#!/usr/bin/env python import os import sys import checks from config import config, nyms, nymList, printNymList, configSection, writeConfig, delSection import traceback import re from stats import parse_stats, stats_m, uptime_sort_m, format_stats from utils import pressKey, validateChoice, chooseList, askPassphrase, askSomething, askYesNo from utils import chooseSubjType, selectServer, getDomain, validateEmail, isGpg, isAscii, X_is_running, editMessage import gpgfuncts import easygui import string import subprocess import optparse class bcolors: """ Define colors for outputs """ GREEN = '\033[92m' YELLOW = '\033[93m' RED = '\033[91m' ENDC = '\033[0m' def disable(self): self.GREEN = '' self.YELLOW = '' self.RED = '' self.ENDC = '' def menu(): """ Print menu and wait for user choice """ os.system(['clear', 'cls'][os.name == 'nt']) print bcolors.GREEN + "----- AUTONYM - for zax type nyms -----" + bcolors.ENDC if anym is False: print bcolors.RED + "active nym: NONE" else: print bcolors.GREEN + "active nym: %s " % anym.name if re.match("OK", config['last_message']): print bcolors.YELLOW + "\ndebug: %s\n" % config['last_message'] + bcolors.ENDC else: print bcolors.RED + "\ndebug: %s\n" % config['last_message'] + bcolors.ENDC selection = raw_input("Enter\n\ 1 to choose/select active nym\n\ 2 to write a message (as active nym)\n\ 3 to create a nym\n\ 4 to update a nym (i.e. set new subject...)\n\ 5 to send a request of deletion for a nym\n\ 6 to create a new secret key\n\ 7 to delete a nym secret key and local config\n\ 8 to save nym secret passphrase in config file (NOT SECURE)\n\ q to quit\n\n\n") # perform actions based on selection above if selection == "1": askNym() menu() elif selection == "2": writeMessage() menu() elif selection == "3": createNym() menu() elif selection == "4": updateNym() menu() elif selection == "5": updateNym(delete=True) menu() elif selection == "6": print "\nsecret keys in your keyring:" gpgfuncts.listSecKeys() print "\n" gpgfuncts.createKey() print "key generated, secret keys in your keyring: " gpgfuncts.listSecKeys() pressKey() menu() elif selection == "7": deleteNymKeysConfig() menu() elif selection == "8": savePassword() menu() elif selection == "q": sys.exit("bye bye") else: print "wrong selection" pressKey() menu() def askNym(): """ set active nym """ nymlist = nymList(nyms) setActiveNym(chooseList(nymlist)) return def createNym(): name = askSomething('Enter the name of the nym (without domain): ') server = selectServer(config) email = name + "@" + server dest = "config@" + server if not gpgfuncts.selectKey(dest): config['last_message'] = "Server %s key not available, abort" % server menu() if not gpgfuncts.selectKey(email): print "no key available, creating a new one for %s" % email gpgfuncts.createKey(email, name) subj_type = chooseSubjType(config) subject = askSomething('Enter the subject you want to use with this nym: ') configSection(email, subject, subj_type) writeConfig() setActiveNym(email) msg = prepareSetupMsg(gpgfuncts.selectSecKey(email), subj_type, subject) emsg = signCryptMsg(msg, dest, sign=False, passphrase="") if not emsg: config['last_message'] = "Something went wrong while preparing the message, recipient key missing ???" menu() sendMixMsg(dest, emsg) def updateNym(delete=False): print "Choose nym to update/delete:" askNym() dest = "config@" + anym.domain if not gpgfuncts.selectKey(dest): config['last_message'] = "Server %s key not available, abort" % dest menu() if not gpgfuncts.selectKey(anym.name): print "no key available for %s" % anym.name menu() if not anym['passphrase']: pwd = askPassphrase() else: pwd = anym['passphrase'] if not delete: subj_type = chooseSubjType(config) while True: subject = askSomething('Enter the subject you want to use with this nym: ') if isAscii(subject): break else: print "ERROR: subject contains not ascii characters, try again" pressKey() continue configSection(anym.name, subject, subj_type) writeConfig() setActiveNym(anym.name) else: subj_type = "delete" subject = "yes" msg = prepareSetupMsg(gpgfuncts.selectSecKey(anym.name), subj_type, subject) emsg = signCryptMsg(msg, dest, sign=anym.fp, passphrase=pwd) if not emsg: config['last_message'] = "Something went wrong while preparing the message, recipient key missing ???" menu() sendMixMsg(dest, emsg) def savePassword(): askNym() pwd = askPassphrase() configSection(anym.name, passphrase=pwd) writeConfig() config['last_message'] = "Password for %s saved in config.ini file (not secure, use just for testing)" def deleteNymKeysConfig(): askNym() if not askYesNo('Are you sure you want to delete local config file and %s secret key ?: ' % anym.name): menu() gpgfuncts.deleteKey(anym.fp) msg = "key and config of %s deleted" % anym.name delSection(anym.name) writeConfig() config['last_message'] = msg def prepareSetupMsg(keyid, subj_type, subject): key = gpgfuncts.exportPubKey(keyid) try: msg = "\n%s: %s\n\n" % (subj_type, subject) + key return msg except UnicodeDecodeError, e: print 'Unicode Error: ', e.reason print 'Please use only plain ascii characters' pressKey() menu() def signCryptMsg(msg, dest, sign, passphrase): msg = gpgfuncts.stripVersion(str(gpgfuncts.encrypt(msg, dest, sign=sign, passphrase=passphrase))) if msg == "ERROR": menu() return msg def setActiveNym(nym): global anym anym = config['nym'][nym] anym.domain = getDomain(anym.name) anym.fp = gpgfuncts.selectSecKey(anym.name) class MixError(Exception): pass def sendMixMsg(dest, msg, subj="subject", test=False, chain=""): """ send msg through a local mixmaster client """ args = [config['options']['mixmaster'], '--mail', '--to=' + dest, '--subject=' + subj, '--copies=1'] chooseChain(chain=chain) args.append('--chain=' + ','.join(config['remailer_chain'])) try: mix = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = mix.communicate(msg) if err.find('Error') >= 0: raise MixError('Mixmaster process returned the following error: ' + str(err) + '. Sending failed.') config['last_message'] = "OK, succesfully sent email" except OSError: # usually means that mixmaster could not be executed raise MixError('Could not find mixmaster binary.') def writeMessage(subject="", recipient="", text="", test=0): """ Create a message to be sent from nym The message has to be sent through a remailer chain to send@nym-server-address """ if anym.fp is False: config['last_message'] = "ERROR - select your nym, actual selection HAS NOT a secret key" menu() if subject == "": while True: subject = askSomething('Insert message subject: ') if isAscii(subject): break else: print "ERROR: subject contains not ascii characters, try again" pressKey() continue if recipient == "": recipient = raw_input("Insert recipient email address or newsgroup name: ") if text == "": if X_is_running(): text = easygui.textbox(msg='Write your message....', title='Input Box', text='', codebox=0) else: text = editMessage() text = filter(lambda x: x in string.printable, text) if isGpg(text): print "message already encrypted" pressKey() elif gpgfuncts.encrypt("test", recipient, test=True).status == "encryption ok": # really bad fix to solve a ridiculous problem, first line gets removed... to be investigated text = "Message:\n" + text text = gpgfuncts.encrypt(text, recipient) print "message has been encrypted with %s public key" % recipient pressKey() else: print "msg has not been encrypted, public key for %s not found" % recipient pressKey() msg = "From: %s\n" % anym.name if validateEmail(recipient): msg = msg + "To: %s\n" % recipient else: msg = msg + "To: %s\n" % [config['options']['mail-to-news']] msg = msg + "Newsgroups: %s\n" % recipient msg = msg + "Subject: %s\n\n" % subject if not anym['passphrase']: pwd = askPassphrase() else: pwd = anym['passphrase'] msg = msg + text dest = 'send@' + anym.domain emsg = signCryptMsg(msg, dest, sign=anym.fp, passphrase=pwd) if test == 1: sendMixMsg(dest, emsg, chain="paranoia") else: sendMixMsg(dest, emsg) def modifyConfig(): pass def chooseChain(chain=""): """ Print remailer list and prompt for chain choice """ if chain != "": config['remailer_chain'] = [chain] return print "\nChoose remailer chain used to send the message to the nymserver\n" for remailer in uptime_sort_m(): print format_stats(remailer) chain = raw_input('Choose remailer to be chained (by num), separated by commas, or write "r" for a random chain (3 remailers): ') if chain == "r": config['remailer_chain'] = ['*','*','*'] return else: validateChain(chain) def validateChain(chain): """ Validate remailer chain, check the availability of public key of remailers and that last remailer of chain is not middleman """ remailer_chain = [] comma = re.compile(',') if len(chain.split(',')) > 1: if not comma.search(chain): print bcolors.RED + "ERROR - remailer chain selections have to be separated by commas" + bcolors.ENDC pressKey() return a = chain.split(',') for n in a: if not n.isdigit(): print bcolors.RED + "ERROR - remailer chain selection cannot contains non-numeric chars" + bcolors.ENDC return n = int(n) n -= 1 remailer_chain.append(uptime_sort_m()[n]) # check if last remailer is middleman if checkMiddle(remailer_chain[len(remailer_chain)-1]): print bcolors.RED + "ERROR - remailer %s is middleman, it cannot be last remailer in chain" % remailer_chain[len(remailer_chain)-1] + bcolors.ENDC pressKey() chooseChain() if checkBrokenChains(remailer_chain): print bcolors.RED + "ERROR - broken chain selected, check the breaks in stats last column" + bcolors.ENDC pressKey() chooseChain() config['last_message'] = "OK, remailer chain choosed: %s" % remailer_chain config['remailer_chain'] = remailer_chain def checkMiddle(remailer): """ check if a remailer is middleman """ if stats_m[remailer].middleman: return True else: return False def checkBrokenChains(remailer_chain): """ check for broken remailer chains """ for i in range(0,len(remailer_chain)-1): cur = remailer_chain[i] next = remailer_chain[i+1] if next in stats_m[cur].broken: return True else: return False def main(): if checks.OptionsCheck(config) == True: config['last_message'] = "OK, initial check passed" if not nyms.sections: print "No nyms configured, going to create it..." createNym() else: setActiveNym(nyms.sections[0]) parser = optparse.OptionParser() parser.add_option('--test', action='store_true', dest='test') opts, args = parser.parse_args() try: if opts.test: if config['options']['testnym'] == "": print bcolors.RED + "\nTestnym not configured in config.ini, exiting..." + bcolors.ENDC sys.exit() else: setActiveNym(config['options']['testnym']) parse_stats() writeMessage(subject="prova nymphet", recipient=config['options']['testnym_recipient'], text="asdadsfj", test=1) print bcolors.GREEN + "\nTest message sent from nym %s to %s" % (config['options']['testnym'], config['options']['testnym_recipient']) else: parse_stats() menu() except KeyboardInterrupt: print "program terminated" except SystemExit: print "program terminated, bye" except: print bcolors.RED + "\nAn unhandled exception occured, here's the traceback!\n" + bcolors.ENDC traceback.print_exc() print bcolors.RED + "\nReport this to putro@autistici.org" + bcolors.ENDC sys.exit() if __name__ == '__main__': main()