Commit 344295d4 authored by putro's avatar putro

first commit

parents
#!/usr/bin/env python
import os
import sys
from utils import download
import subprocess
class MixError(Exception):
pass
def process_args():
""" Process command line arguments """
from optparse import OptionParser
use = "nymhelper.py [options] \nexcept for -C, all other options are ju" \
"st used to send a message from command line\nfor normal use do " \
"not use any options"
parser = OptionParser(usage=use)
parser.add_option("-C", "--config", action="store", dest="config",
default="config.ini",
help="config file to use nym to send the message from.")
parser.add_option("-n", "--nym", action="store", dest="nym", type="int",
help="nym to send the message from.")
parser.add_option("-s", "--subject", action="store", dest="subject",
help="subject for the message to be sent.")
parser.add_option("-r", "--recipient", action="store", dest="recipient",
help="recipient of the message to be sent.")
parser.add_option("-t", "--text", action="store", dest="text",
help="text of the message to be sent.")
parser.add_option("-c", "--chain", action="store", dest="chain",
help="remailer chain to send the message through \
(example: -c \"remailer1,remailer2\").")
return parser.parse_args()
def checkAscii(file):
""" Check if the a file is pure ascii """
import codecs
bad = 0
f = codecs.open(file, encoding='ascii')
lines = open(file).readlines()
for i in range(0, len(lines)):
try:
l = f.readline()
except:
num = i + 1
print "config file problem (%s): line %d contains non-ascii " \
"character, fix it" % (file, num)
bad = 1
break
f.close()
return bad
def InitialCheck():
global configfile
global opts, args
(opts, args) = process_args()
if opts.config:
configfile = os.path.dirname(sys.argv[0]) + "/" + opts.config
else:
configfile = os.path.dirname(sys.argv[0]) + "/config.ini"
def OptionsCheck(config):
""" performs some checks """
try:
f = open(os.path.dirname(sys.argv[0]) + "/check", "w")
except:
print "Error: you cannot write %s, fix permission or launch this script from another directory" % options['path'] + "/" + options['stats-file']
sys.exit()
else:
f.close()
os.remove(os.path.dirname(sys.argv[0]) + "/check")
# try:
# f = open(os.path.dirname(sys.argv[0]) + "/" + config['stats']['stats_mlist'], "r")
# except:
# download(config['stats']['stats_mlist'], config)
# else:
# f.close()
try:
download(config['stats']['stats_mlist'], config)
except:
print "Error: cannot download fresh remailer stats"
try:
mix = subprocess.Popen(config['options']['mixmaster'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
except OSError: # usally means that mixmaster could not be executed
raise MixError('Could not find mixmaster binary.')
#import gpgfuncts
#gpgfuncts.selectSecKey(options.nym_email)
return True
import os
import sys
import checks
from configobj import ConfigObj, ConfigObjError
class ConfigError(Exception):
pass
configfile = os.path.dirname(sys.argv[0]) + "/config.ini"
try:
config = ConfigObj(configfile, file_error=True)
except (ConfigObjError, IOError), e:
print 'Could not read "%s": %s' % (configfile, e)
sys.exit(-1)
nyms = config['nym']
def writeConfig():
config.write()
def checkSection(section, config=config):
if section in listSection(config):
return True
def listSection(config):
return config.keys()
def configSection(nym, subject, subj_type, passphrase=""):
config['nym'][nym] = {}
config['nym'][nym]['subject'] = subject
config['nym'][nym]['subj_type'] = subj_type
config['nym'][nym]['passphrase'] = passphrase
def checkNymConfig(nym):
return nyms.has_key(nym)
def nymList(nyms):
nymlist = []
for n in nyms.keys():
nymlist.append(n)
return nymlist
def printNymList():
nymlist = nymList()
counter = 0
for n in nymlist:
print counter, "-", n
counter += 1
return counter
This diff is collapsed.
Copyright (c) 2008-2013 by Vinay Sajip.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* The name(s) of the copyright holder(s) may not be used to endorse or
promote products derived from this software without specific prior
written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) "AS IS" AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
EVENT SHALL THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
This diff is collapsed.
import sys
import re
import gnupg
import getpass
from config import config
from utils import pressKey
try:
global gpg
gpg = gnupg.GPG()
except:
print "Error, problems with your keyring directory, exiting"
sys.exit(0)
def createKey(email, name, comment="", key_type="RSA", key_length=4096):
""" Create a gpg key """
while True:
pwd = getpass.getpass("insert the password: ")
pwd2 = getpass.getpass("type again the password: ")
if pwd == pwd2:
break
print "wait, I'm generating the key, move the mouse, press keys, it could take some times......\n"
input_data = gpg.gen_key_input(name_real=name, name_email=email, name_comment="", passphrase=pwd, key_type=key_type, key_length=key_length, )
key = gpg.gen_key(input_data)
def selectKey(email, secret=False):
""" Select the key fingerprint of an email address"""
keys = gpg.list_keys(secret=secret)
key = False
for k in keys:
counter = 0
# if keys have multiple uids, scan through all uids
for u in k["uids"]:
# stop at first match
e = k["uids"][counter]
counter += 1
expr = re.compile("%s" % email)
m = expr.search(e)
""" if match found, select key """
if m:
"""check if key is valid"""
if encrypt("test", k["fingerprint"], test=True).status == "encryption ok":
key = True
config['last_message'] = "OK - key choosed: keyID=%s" % getKeyID(k["fingerprint"])
break
else:
config['last_message'] = "ERROR - invalid key for recipient: %s" % u
key = False
if key:
break
if not key:
config['last_message'] = "ERROR, public key for %s NOT found" % email
print "ERROR, public key for %s NOT found" % email
pressKey()
return False
return k["fingerprint"]
def listSecKeys():
""" Print list of secret keys in keyring"""
seckeys = gpg.list_keys(secret=True)
counter = 0
for k in seckeys:
counter += 1
print "%s - %s - %s" % (counter, k["uids"][0], k["keyid"])
print [x for x in k["uids"]]
return seckeys
def selectSecKey(email):
""" Select the key fingerprint of an email address"""
global last_message
seckeys = gpg.list_keys(True)
key = False
for k in seckeys:
counter = 0
"""if keys have multiple uids, scan through all uids"""
for u in k["uids"]:
"""stop at first match"""
e = k["uids"][counter]
counter += 1
expr = re.compile("%s" % email)
m = expr.search(e)
""" if match found, select key """
if m:
#print "key fingerprint for %s: %s" % (email, k["fingerprint"])
config['nym_fp'] = k["fingerprint"]
config['nym_keyid'] = getKeyID(k["fingerprint"])
key = True
last_message = "OK - secret key choosed: keyID=%s" % config['nym_keyid']
break
if key:
break
if not key:
config['last_message'] = "ERROR, secret key for %s NOT found" % email
return False
return k["fingerprint"]
def getKeyID(fp):
""" Get keyid from fingeprint """
# works only with v4 keys
return fp[-8:]
def exportPubKey(id):
""" Export public key
id can be the fingerprint """
ascii_armored_public_keys = stripVersion(str(gpg.export_keys(id)))
return ascii_armored_public_keys
def defineSecKey():
""" Select which secret key to use """
ls = listSecKeys()
numkeys = int(len(ls))
print "there are %s keys" % numkeys
if numkeys == 0:
print "going to generate a new key..."
createKey()
listSecKeys()
while True:
a = raw_input("enter key number (and press enter): ")
if a.isdigit():
a = int(a)
if a < 1:
print "you can't enter a key number lower than 1"
continue
elif a > numkeys:
print "there are only %d keys, select key number again" % numkeys
continue
break
a -= 1
return ls[a]
def deleteKey(fp):
""" Delete secret and public key """
global last_message
seckey = gpg.list_keys(secret=True)
str(gpg.delete_keys(fp, True))
str(gpg.delete_keys(fp))
last_message = "OK - key with fingerprint %s deleted" % fp
def stripVersion(pgptext):
"""Version strings in PGP blocks can weaken anonymity by partitioning users
into subsets. This function replaces the Version string with 'N/A'."""
newtext = re.sub('(?m)^Version: .*', 'Version: N/A', pgptext)
return newtext
def encrypt(message, recipient, sign=False, passphrase=False, test=False):
""" Encrypt a message for a recipient """
encrypted_ascii_data = gpg.encrypt(message, recipient, always_trust=True,
sign=sign, passphrase=passphrase)
if not test:
if encrypted_ascii_data.status != "encryption ok":
config['last_message'] = "ERROR " + encrypted_ascii_data.status + " (recipient: %s)" % recipient
return False
else:
return stripVersion(str(encrypted_ascii_data))
else:
return encrypted_ascii_data
This diff is collapsed.
###############################################################################
# This file is part of Pyano. #
# #
# Pyano is a web interface for the mixmaster remailer, written for mod_python #
# Copyright (C) 2010 Sean Whitbeck <sean@neush.net> #
# #
# Pyano is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# Pyano is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
###############################################################################
import re
from config import config
class Remailer():
def __init__(self, name):
self.name = name
class RemailerStats(Remailer):
def __init__(self,name,latency,uptime):
self.name = name
self.latency = latency
self.uptime = uptime
self.broken = []
self.allow_from = False # by default no remailers accept from headers
self.middleman = False
self.post = False
stats_m = {}
bad_mail2news = {}
def parse_stats():
try:
_read_mlist()
try:
_read_allow_from()
except IOError: # Error reading from.html
pass
except IOError: # Could not read mlist2.txt
stats.clear()
def _read_mlist():
completely_broken = set([])
with open(config['stats']['stats_mlist'], 'r') as f:
n = 0
in_stats_block = False
in_broken_block = False
for line in f:
n += 1
if n == 4:
in_stats_block = True
elif in_stats_block:
elems = line.split()
if len(elems) == 0: # blank line, we are out of the stats block
in_stats_block = False
else:
name = elems[0]
latency = elems[2]
uptime = float(elems[4].strip('%'))
stats_m[name] = RemailerStats(name,latency,uptime)
elif "Broken type-II remailer chains" in line:
in_broken_block = True
elif in_broken_block:
elems = line.strip().strip('()').split()
if len(elems) == 0: # blank line, we are out of the broken chains block
in_broken_block = False
else:
rem_from = elems[0]
rem_to = elems[1]
if rem_from == '*': # This remailer doesn't accept messages
completely_broken.add(rem_to)
elif rem_to == '*': # This remailer doesn't send messages to anyone
completely_broken.add(rem_from)
else:
stats_m[rem_from].broken.append(rem_to)
elif '=' in line: # we are now looking for the remailer capabilities block
name = line[11:].split('"',1)[0]
rem_stats = stats_m[name]
rem_stats.middleman = ("middle" in line)
rem_stats.post = ("post" in line)
rem_stats.address = line[25:].split('>',1)[0]
# cleanup up completely useless remailers
for remailer in completely_broken:
del stats_m[remailer]
def _read_allow_from():
with open(config['stats']['allow_from'],'r') as f: # find out those that do accept from headers
in_from_block = False
m = re.compile("<td>(\w+)</td>")
for line in f:
if line.find("User Supplied From") >= 0:
in_from_block = True
if in_from_block:
ok = m.search(line)
if ok:
name = ok.group(1)
if name in stats_m:
stats_m[name].allow_from = True
def format_stats(name):
rem = stats_m[name]
s = rem.number.ljust(3)+rem.name.ljust(12)+rem.latency.rjust(5)+(str(rem.uptime)+"%").rjust(7)
if rem.middleman:
s += " M"
else:
s += " "
if rem.allow_from:
s += "F"
else:
s += " "
if rem.post:
s += "P"
else:
s += " "
if rem.broken:
s += " (breaks: "
for remailer in rem.broken:
s += remailer+","
s = s.rstrip(',')
s += ')'
return s
def uptime_sort_m():
remailers = stats_m.keys()
remailers.sort(cmp=lambda x,y: cmp(stats_m[y].uptime,stats_m[x].uptime))
n = 1
for r in remailers:
stats_m[r].number = str(n)
n += 1
return remailers
import os
import sys
import re
import getpass
class InputError(Exception):
pass
def download(file, config, pgp=False):
""" Download remailer stats file """
print "downloading %s....... please wait" % file
from urllib2 import Request, urlopen, URLError, HTTPError
url = config['stats']['stats_url'] + file
req = Request(url)
try:
response = urlopen(req)
except HTTPError, e:
print 'Error - The server couldn\'t fulfill the request.'
print 'Error code: ', e.code
pressKey()
except URLError, e:
print 'Error - We failed to reach a server.'
print 'Reason: ', e.reason
pressKey()
else:
the_page = response.read()
w = open(os.path.dirname(sys.argv[0]) + "/" + file, "w")
w.write(the_page)
w.close()
pass
if pgp:
key_data = open(options.path + "/" +
options.keys_file, "r").read()
import_keys = gpg.import_keys(key_data)
last_message = "OK - remailer keys downloaded - %s keys imported" % \
import_keys.count
def pressKey():
""" wait user to press RETURN """
raw_input("press RETURN to continue\n")
def validateEmail(email):
""" check if the recipient is a valid email address"""
if re.match(r'[\w\-][\w\-\.]*@[\w\-][\w\-\.]+[a-zA-Z]{1,4}', email):
return True
else:
return False
def validateChoice(choice, list):
try:
i = (int(choice))
except ValueError:
print "Invalid input, enter a number...."
return False
if i < 1:
print "enter a number greater than 1...."
return False
if i > len(list):
print "error, there are only %s choices" % len(list)
return False
else:
return True
def askPassphrase():
""" Ask passphrase for nym secret key if not set in config"""
pwd = getpass.getpass("insert passphrase for nym secret key: ")
return pwd
def askSomething(question):
usr_input = raw_input(question)
return usr_input
def askYesNo(question, default="yes"):
"""Ask a yes/no question via raw_input() and return their answer.
"question" is a string that is presented to the user.
"default" is the presumed answer if the user just hits <Enter>.
It must be "yes" (the default), "no" or None (meaning
an answer is required of the user).
The "answer" return value is one of "yes" or "no".
"""
valid = {"yes":True, "y":True, "ye":True,
"no":False, "n":False}
if default == None:
prompt = " [y/n] "
elif default == "yes":
prompt = " [Y/n] "
elif default == "no":
prompt = " [y/N] "
else:
raise ValueError("invalid default answer: '%s'" % default)
while True:
sys.stdout.write(question + prompt)
choice = raw_input().lower()
if default is not None and choice == '':
return valid[default]
elif choice in valid:
return valid[choice]
else:
sys.stdout.write("Please respond with 'yes' or 'no' "\
"(or 'y' or 'n').\n")
def printList(list):
counter = 1
for i in list:
print counter, "-", i
counter += 1
def chooseList(list):
printList(list)
usr_input = ''
while usr_input not in range(1, len(list) + 1):
try:
usr_input = input("Enter your choice (1-%s): " % len(list))
except:
print "\nError - you have to enter a number !!!!"
return list[usr_input - 1]
def selectServer(config):
servers = config['options']['nymservers']
for idx, val in enumerate(servers):
print idx+1, val
while True:
choice = raw_input("Enter the nymserver you want to use: ")
if validateChoice(choice, servers):
break
server = servers[int(choice) - 1]
return server
def chooseSubjType(config):
subject_types = config['options']['subj_types']
for idx, val in enumerate(subject_types):
print idx+1, "-", val
while True:
choice = raw_input("Enter what kind of subject you want: ")
if validateChoice(choice, subject_types):
break
subj_type = subject_types[int(choice) - 1]
return subj_type
def getDomain(email):
domain = email.split("@")[1]
return domain
def isGpg(message):
m = re.compile('^-----BEGIN PGP MESSAGE-----')
line = message.split("\n", 1)[0]
print line
pressKey()
if m.match(line):
return True
else:
return False
###############################################################################
# This file is part of Pyano. #
# #
# Pyano is a web interface for the mixmaster remailer, written for mod_python #
# Copyright (C) 2010 Sean Whitbeck <sean@neush.net> #
# #
# Pyano is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# Pyano is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
###############################################################################
import re
import random
from stats import stats, bad_mail2news
from config import conf, NONE, RANDOM, POST
class InputError(Exception):
pass
def val_email_local(local):
ac = "[a-zA-Z0-9_!#\$%&'*+\-=?\^`{|}~]"
m = re.compile( '^('+ac+'+\.)*'+ac+'+$')
if not m.match(local):
raise InputError()
def val_dot_seq(server):
ac = '[a-zA-Z0-9+\-_]'
m = re.compile( '^('+ac+'+\.)+'+ac+'+$')
if not m.match(server):
raise InputError()
def val_email(addr):
m = re.compile( '^("?[\w ]*"? *<)?([^>]+)>?$' )
ok = m.match(addr)
try:
if not ok:
raise InputError()
email = ok.group(2)
parts = email.split('@')
if len(parts) != 2:
raise InputError()
val_email_local(parts[0])
val_dot_seq(parts[1])
except InputError:
if addr:
raise InputError(addr+' is not a valid email address.')
raise InputError('Please enter an email address.')
def val_newsgroups(newsgroups):
groups = newsgroups.split(',')
for group in groups:
val_dot_seq(group)
def val_references(refs):
m = re.compile( '^<(.*)>$' )
try:
for ref in refs.split(' '):
ok = m.match(ref)