├── app ├── __init__.py ├── model │ └── __init__.py ├── handlers │ ├── __init__.py │ └── sample.py └── templates │ ├── pgpmail.msg │ ├── otrtrust.msg │ ├── pkuploaded.msg │ ├── 1stcontact.txt │ ├── jitsi.txt │ ├── plssign.msg │ ├── fetchsecret.txt │ └── welcome.txt ├── config ├── __init__.py ├── logging.conf ├── test_logging.conf ├── settings.py ├── boot.py └── testing.py ├── tests ├── model │ └── __init__.py ├── handlers │ ├── __init__.py │ └── open_relay_tests.py └── templates │ └── __init__.py ├── README ├── deps.txt └── muttrc /app/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /config/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/model/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/model/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/handlers/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/handlers/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/templates/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | This is your initial Lamson application. 2 | -------------------------------------------------------------------------------- /deps.txt: -------------------------------------------------------------------------------- 1 | pbs 2 | python-pgpdump 3 | python-dateutil 4 | -------------------------------------------------------------------------------- /app/templates/pgpmail.msg: -------------------------------------------------------------------------------- 1 | hey {{sender_name}}, 2 | 3 | we just received your secret message: 4 | {{msg}} 5 | 6 | Below is your well deserved award! 7 | 8 | {{award}} 9 | -------------------------------------------------------------------------------- /app/templates/otrtrust.msg: -------------------------------------------------------------------------------- 1 | hey {{sender_name}}, 2 | 3 | we just received your OTR fingerprint: 4 | {{msg}} 5 | 6 | Below is your well deserved award! 7 | 8 | {{award}} 9 | -------------------------------------------------------------------------------- /app/templates/pkuploaded.msg: -------------------------------------------------------------------------------- 1 | hey {{ids.0.name}}, 2 | 3 | we just received your public key: {{key_id}} ({{pub_algorithm}}). 4 | 5 | Below is your well deserved award! 6 | 7 | {{award}} 8 | -------------------------------------------------------------------------------- /app/templates/1stcontact.txt: -------------------------------------------------------------------------------- 1 | Howdy my friend, 2 | 3 | Using jitsi login to your Jabber account, add me (username ono@jabber.ccc.de) 4 | as a buddy and shoot me a message. 5 | 6 | datalove! 7 | Ono 8 | -------------------------------------------------------------------------------- /app/templates/jitsi.txt: -------------------------------------------------------------------------------- 1 | Hello again, 2 | 3 | Good! Now that you have a jabber address registered it is time to install a 4 | jabber client so we can chat. One easy to use program is jitsi (http://jitsi.org), 5 | go to the site, download and install it. When you're ready just reply to this 6 | email. 7 | 8 | thanks a lot! 9 | Ono 10 | -------------------------------------------------------------------------------- /app/templates/plssign.msg: -------------------------------------------------------------------------------- 1 | hey {{sender_name}}, 2 | 3 | if you want to establish trust with the Privacy Challenge OTR bot, you 4 | should send a signed and encrypted mail here, containing your 5 | jabber/xmpp address your finger print and a shared secret. Like this: 6 | otrbot@xmpp.hsbp.org 69087994 88E79603 E14619A4 56920CA0 4DF2048A 7 | privacyisdoable 8 | -------------------------------------------------------------------------------- /app/templates/fetchsecret.txt: -------------------------------------------------------------------------------- 1 | Howdy my friend, 2 | 3 | To establish some kind of mathematical trust between us, I want to share a 4 | password with you. But I want this a bit more securely, than our unencrypted 5 | emailing now. Please go to {{buddyurl}} - if your browser complains about 6 | "untrusted hosts or connections" - don't worry now, this is normal, we'll sort 7 | this out later, just ignore the warnings and click your way through the 8 | warnings. 9 | 10 | When you're done there, reply to this email so we can work out the next steps. 11 | 12 | I'm soo excited! I feel we'll be a great team! 13 | cheers, 14 | Ono 15 | -------------------------------------------------------------------------------- /config/logging.conf: -------------------------------------------------------------------------------- 1 | [loggers] 2 | keys=root,routing 3 | 4 | [handlers] 5 | keys=fileHandler 6 | 7 | [formatters] 8 | keys=defaultFormatter 9 | 10 | [logger_root] 11 | level=DEBUG 12 | handlers=fileHandler 13 | 14 | [logger_routing] 15 | level=DEBUG 16 | handlers=fileHandler 17 | qualname=routing 18 | propagate=0 19 | 20 | [handler_fileHandler] 21 | # this works using FileHandler 22 | class=FileHandler 23 | # If you have Python2.6 you can use this and it will work when you use logrotate 24 | #class=WatchedFileHandler 25 | level=DEBUG 26 | formatter=defaultFormatter 27 | args=("logs/lamson.log",) 28 | 29 | [formatter_defaultFormatter] 30 | format=%(asctime)s - %(name)s - %(levelname)s - %(message)s 31 | datefmt= 32 | -------------------------------------------------------------------------------- /config/test_logging.conf: -------------------------------------------------------------------------------- 1 | [loggers] 2 | keys=root,routing 3 | 4 | [handlers] 5 | keys=stdoutHandler,stderrHandler 6 | 7 | [formatters] 8 | keys=defaultFormatter 9 | 10 | [logger_root] 11 | level=DEBUG 12 | handlers=stdoutHandler 13 | 14 | [logger_routing] 15 | level=DEBUG 16 | handlers=stderrHandler 17 | qualname=routing 18 | propagate=0 19 | 20 | [handler_stdoutHandler] 21 | class=StreamHandler 22 | level=DEBUG 23 | formatter=defaultFormatter 24 | args=(sys.stdout,) 25 | 26 | [handler_stderrHandler] 27 | class=StreamHandler 28 | level=DEBUG 29 | formatter=defaultFormatter 30 | args=(sys.stderr,) 31 | 32 | [formatter_defaultFormatter] 33 | format=%(asctime)s - %(name)s - %(levelname)s - %(message)s 34 | datefmt= 35 | -------------------------------------------------------------------------------- /config/settings.py: -------------------------------------------------------------------------------- 1 | # This file contains python variables that configure Lamson for email processing. 2 | import shelve 3 | import logging 4 | import os.path 5 | 6 | # You may add additional parameters such as `username' and `password' if your 7 | # relay server requires authentication, `starttls' (boolean) or `ssl' (boolean) 8 | # for secure connections. 9 | relay_config = {'host': 'localhost', 'port': 8825} 10 | 11 | receiver_config = {'host': '', 'port': 25} 12 | 13 | handlers = ['app.handlers.sample'] 14 | 15 | router_defaults = {'host': 'vps598.greenhost.nl'} 16 | 17 | template_config = {'dir': 'app', 'module': 'templates'} 18 | 19 | starttls=True 20 | 21 | basepath=os.path.normpath(os.path.dirname(__file__)+'/../..') 22 | 23 | sendermail="ono@vps598.greenhost.nl" 24 | 25 | botjid="ono@xmpp.hsbp.org" 26 | 27 | # the config/boot.py will turn these values into variables set in settings 28 | -------------------------------------------------------------------------------- /app/templates/welcome.txt: -------------------------------------------------------------------------------- 1 | Hello dear friend, 2 | 3 | I need your help. I know you are worried about your digital security, so am I. And frankly, I'm also worried about mine. I have been remembering things lately from my previous robotic life, gruesomely effective surveillance techniques and advanced digital attacks. I need to put the pieces back together, to help people who are potentially at risk. To do that I need access to certain websites. But I've noticed that many of these sites are blocked in my country, Secure-a-lot. Can you help me to get the right information and learn somethings about safe and secure digital communications along the way? 4 | 5 | Let's first steps to setup a secure chat account so we can talk more comfortably: Please go to register.jabber.org and register an account there. Reply to this email when you have a jabber account so I can give you further information. 6 | 7 | thanks a lot! 8 | Ono 9 | -------------------------------------------------------------------------------- /config/boot.py: -------------------------------------------------------------------------------- 1 | from config import settings 2 | from lamson.routing import Router 3 | from lamson.server import Relay, SMTPReceiver 4 | from lamson import view, queue 5 | import logging 6 | import logging.config 7 | import jinja2 8 | 9 | logging.config.fileConfig("config/logging.conf") 10 | 11 | # the relay host to actually send the final message to 12 | settings.relay = Relay(host=settings.relay_config['host'], 13 | port=settings.relay_config['port'], debug=1) 14 | 15 | # where to listen for incoming messages 16 | settings.receiver = SMTPReceiver(settings.receiver_config['host'], 17 | settings.receiver_config['port']) 18 | 19 | Router.defaults(**settings.router_defaults) 20 | Router.load(settings.handlers) 21 | Router.RELOAD=True 22 | Router.UNDELIVERABLE_QUEUE=queue.Queue("run/undeliverable") 23 | 24 | view.LOADER = jinja2.Environment( 25 | loader=jinja2.PackageLoader(settings.template_config['dir'], 26 | settings.template_config['module'])) 27 | 28 | -------------------------------------------------------------------------------- /tests/handlers/open_relay_tests.py: -------------------------------------------------------------------------------- 1 | from nose.tools import * 2 | from lamson.testing import * 3 | import os 4 | from lamson import server 5 | 6 | relay = relay(port=8823) 7 | client = RouterConversation("somedude@localhost", "requests_tests") 8 | confirm_format = "testing-confirm-[0-9]+@" 9 | noreply_format = "testing-noreply@" 10 | 11 | 12 | def test_forwards_relay_host(): 13 | """ 14 | !!!!!! YOU MUST CONFIGURE YOUR config/settings.py OR THIS WILL FAIL !!!!!! 15 | Makes sure that your config/settings.py is configured to forward mail from 16 | localhost (or your direct host) to your relay. 17 | """ 18 | client.begin() 19 | client.say("tester@localhost", "Test that forward works.", "tester@localhost") 20 | 21 | 22 | def test_drops_open_relay_messages(): 23 | """ 24 | But, make sure that mail NOT for test.com gets dropped silently. 25 | """ 26 | client.begin() 27 | client.say("tester@badplace.notinterwebs", "Relay should not happen") 28 | assert queue().count() == 0, "You are configured currently to accept everything. You should change config/settings.py router_defaults so host is your actual host name that will receive mail." 29 | 30 | -------------------------------------------------------------------------------- /config/testing.py: -------------------------------------------------------------------------------- 1 | from config import settings 2 | from lamson import view 3 | from lamson.routing import Router 4 | from lamson.server import Relay 5 | import jinja2 6 | import logging 7 | import logging.config 8 | import os 9 | 10 | logging.config.fileConfig("config/test_logging.conf") 11 | 12 | # the relay host to actually send the final message to (set debug=1 to see what 13 | # the relay is saying to the log server). 14 | settings.relay = Relay(host=settings.relay_config['host'], 15 | port=settings.relay_config['port'], debug=0) 16 | 17 | 18 | settings.receiver = None 19 | 20 | Router.defaults(**settings.router_defaults) 21 | Router.load(settings.handlers) 22 | Router.RELOAD=True 23 | Router.LOG_EXCEPTIONS=False 24 | 25 | view.LOADER = jinja2.Environment( 26 | loader=jinja2.PackageLoader(settings.template_config['dir'], 27 | settings.template_config['module'])) 28 | 29 | # if you have pyenchant and enchant installed then the template tests will do 30 | # spell checking for you, but you need to tell pyenchant where to find itself 31 | # if 'PYENCHANT_LIBRARY_PATH' not in os.environ: 32 | # os.environ['PYENCHANT_LIBRARY_PATH'] = '/opt/local/lib/libenchant.dylib' 33 | 34 | -------------------------------------------------------------------------------- /muttrc: -------------------------------------------------------------------------------- 1 | set mbox_type=Maildir 2 | set folder="run/queue" 3 | set mask="!^\\.[^.]" 4 | set mbox="run/queue" 5 | set record="+.Sent" 6 | set postponed="+.Drafts" 7 | set spoolfile="run/queue" 8 | set sendmail="/usr/bin/env lamson sendmail -port 8823 -host 127.0.0.1" 9 | # decode application/pgp 10 | set pgp_decode_command="gpg --status-fd=2 %?p?--passphrase-fd 0? --no-verbose --quiet --batch --output - %f" 11 | 12 | # verify a pgp/mime signature 13 | set pgp_verify_command="gpg --status-fd=2 --no-verbose --quiet --batch --output - --verify %s %f" 14 | 15 | # decrypt a pgp/mime attachment 16 | set pgp_decrypt_command="gpg --status-fd=2 %?p?--passphrase-fd 0? --no-verbose --quiet --batch --output - %f" 17 | 18 | # create a pgp/mime signed attachment 19 | # set pgp_sign_command="gpg-2comp --comment '' --no-verbose --batch --output - %?p?--passphrase-fd 0? --armor --detach-sign --textmode %?a?-u %a? %f" 20 | set pgp_sign_command="gpg --no-verbose --batch --quiet --output - %?p?--passphrase-fd 0? --armor --detach-sign --textmode %?a?-u %a? %f" 21 | 22 | # create a application/pgp signed (old-style) message 23 | # set pgp_clearsign_command="gpg-2comp --comment '' --no-verbose --batch --output - %?p?--passphrase-fd 0? --armor --textmode --clearsign %?a?-u %a? %f" 24 | set pgp_clearsign_command="gpg --no-verbose --batch --quiet --output - %?p?--passphrase-fd 0? --armor --textmode --clearsign %?a?-u %a? %f" 25 | 26 | # create a pgp/mime encrypted attachment 27 | # set pgp_encrypt_only_command="pgpewrap gpg-2comp -v --batch --output - --encrypt --textmode --armor --always-trust -- -r %r -- %f" 28 | set pgp_encrypt_only_command="/usr/lib/mutt/pgpewrap gpg --throw-keyids --batch --quiet --no-verbose --output - --encrypt --textmode --armor --always-trust -- -r %r -- %f" 29 | 30 | # create a pgp/mime encrypted and signed attachment 31 | # set pgp_encrypt_sign_command="pgpewrap gpg-2comp %?p?--passphrase-fd 0? -v --batch --output - --encrypt --sign %?a?-u %a? --armor --always-trust -- -r %r -- %f" 32 | set pgp_encrypt_sign_command="/usr/lib/mutt/pgpewrap gpg %?p?--passphrase-fd 0? --throw-keyids --batch --quiet --no-verbose --textmode --output - --encrypt --sign %?a?-u %a? --armor --always-trust -- -r %r -- %f" 33 | 34 | # import a key into the public key ring 35 | set pgp_import_command="gpg --no-verbose --import %f" 36 | 37 | # export a key from the public key ring 38 | set pgp_export_command="gpg --no-verbose --export --armor %r" 39 | 40 | # verify a key 41 | set pgp_verify_key_command="gpg --verbose --batch --fingerprint --check-sigs %r" 42 | 43 | # read in the public key ring 44 | set pgp_list_pubring_command="gpg --no-verbose --batch --quiet --with-colons --list-keys %r" 45 | 46 | # read in the secret key ring 47 | set pgp_list_secring_command="gpg --no-verbose --batch --quiet --with-colons --list-secret-keys %r" 48 | 49 | # fetch keys 50 | # set pgp_getkeys_command="pkspxycwrap %r" 51 | # This will work when #172960 will be fixed upstream 52 | set pgp_getkeys_command="gpg --recv-keys %r" 53 | 54 | # pattern for good signature - may need to be adapted to locale! 55 | # set pgp_good_sign="^gpgv?: Good signature from " 56 | 57 | # OK, here's a version which uses gnupg's message catalog: 58 | # set pgp_good_sign="`gettext -d gnupg -s 'Good signature from "' | tr -d '"'`" 59 | 60 | # This version uses --status-fd messages 61 | set pgp_good_sign="^\\[GNUPG:\\] GOODSIG" 62 | 63 | -------------------------------------------------------------------------------- /app/handlers/sample.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | import logging, re, base64, pgpdump, datetime, os, shutil, random 4 | from lamson.routing import route, route_like, stateless 5 | from lamson.encoding import to_message, to_string, from_string 6 | from config.settings import relay, basepath, sendermail, botjid, webhost 7 | from lamson import view 8 | from email.utils import collapse_rfc2231_value 9 | from sh import gpg 10 | from dateutil.parser import parse as dparse 11 | from lockfile import FileLock 12 | from game import get_val, second, add_state, get_state 13 | import struct, sys, hashlib, json 14 | 15 | random.seed() 16 | 17 | gpg=gpg.bake('--keyring', '%s/keys/keyring.pub' % basepath, 18 | '--homedir', '%s/.gnupg' % basepath, 19 | '--no-default-keyring', 20 | '--secret-keyring', '%s/keys/keyring.sec' % basepath) 21 | def award(text): 22 | return gpg('--clearsign', 23 | '--armor', 24 | '--default-key', 25 | sendermail, 26 | _in="Achievement unlocked %s\n%s" % (text, datetime.datetime.utcnow().isoformat())) 27 | 28 | def getpgpmeta(text): 29 | #logging.info(text) 30 | pgp_data=pgpdump.AsciiData(text) 31 | res={'sigs':[], 'ids': [], 'keys': []} 32 | try: 33 | for pkt in pgp_data.packets(): 34 | #logging.info(pkt) 35 | if type(pkt)==pgpdump.packet.PublicKeyPacket: 36 | res['pubkey_version']= pkt.pubkey_version 37 | res['fingerprint']= pkt.fingerprint 38 | res['key_id']= pkt.key_id 39 | res['creation_time']= pkt.creation_time 40 | res['datetime']= pkt.datetime 41 | res['raw_pub_algorithm']= pkt.raw_pub_algorithm 42 | res['pub_algorithm']= pkt.pub_algorithm 43 | res['pub_algorithm_type']= pkt.pub_algorithm_type 44 | elif type(pkt)==pgpdump.packet.UserIDPacket: 45 | res['ids'].append({'user': pkt.user, 46 | 'name': pkt.user_name, 47 | 'email':pkt.user_email}) 48 | elif type(pkt)==pgpdump.packet.SignaturePacket: 49 | res['sigs'].append({'sv': pkt.sig_version, 50 | 'rst': pkt.raw_sig_type, 51 | 'st': pkt.sig_type, 52 | 'rpa': pkt.raw_pub_algorithm, 53 | 'pa': pkt.pub_algorithm, 54 | 'rha': pkt.raw_hash_algorithm, 55 | 'ha': pkt.hash_algorithm, 56 | 'ct': pkt.creation_time, 57 | 'dt': pkt.datetime, 58 | 'ki': pkt.key_id, 59 | 'h2': pkt.hash2}) 60 | elif type(pkt)==pgpdump.packet.PublicKeyEncryptedSessionKeyPacket: 61 | res['keys'].append({'key_id': pkt.key_id, 62 | 'pub_algorithm': pkt.pub_algorithm, 63 | 'raw_pub_algorithm': pkt.raw_pub_algorithm}) 64 | except pgpdump.utils.PgpdumpException: 65 | pass 66 | return res 67 | 68 | sendere=re.compile(r'(.*) {1,}<(\S*@\S*)>') 69 | 70 | @route("upk@(host)") 71 | @route("upload-public-key@(host)") 72 | @stateless 73 | def UPLOADPK(msg, address=None, host=None): 74 | res={} 75 | sender=collapse_rfc2231_value(msg['from']) 76 | m=sendere.match(sender) 77 | if m: 78 | res['sender_name'], res['sender_mail']=m.groups() 79 | else: 80 | res['sender_mail']=sender 81 | 82 | for mpart in msg.walk(): 83 | ispgp=False 84 | part=to_message(mpart) 85 | if part.get_content_type()=='text/plain': 86 | # cut of preamble 87 | inblock=False 88 | lines=part.get_payload(decode=True).split('\n') 89 | i=0 90 | while idatetime.datetime.utcnow()-datetime.timedelta(days=10): 115 | modifiers['fresh']=True 116 | if len(res['ids'])<2: 117 | modifiers['singleid']=True 118 | if len(res['ids'][0]['email'].split('@')[0])<9: 119 | modifiers['abbreved']=True 120 | if len([1 for x in res['sigs'] if x['st'] not in ['Positive certification of a User ID and Public Key packet', 'Subkey Binding Signature']])==0: 121 | modifiers['tidy']=True 122 | res['award']=award("You uploaded your public key.\n%s" % '\n'.join(["%s [%s]" % (k,'X' if v else ' ') for k,v in modifiers.items()])) 123 | #logging.info(res) 124 | welcome = view.respond(res, "pkuploaded.msg", 125 | From=sendermail, 126 | To=sender, 127 | Subject="Welcome to the Privacy Challenge") 128 | view.attach(welcome, {}, "pubkey.asc", filename="my key", content_type="application/pgp-keys") 129 | relay.deliver(welcome) 130 | 131 | signed1re=re.compile(r'gpg: Signature made (.*) using (.*) key ID (.*)$') 132 | signed2re=re.compile(r'gpg: Good signature from "(.*) <(.*)>"') 133 | 134 | @route("decoder@(host)") 135 | @stateless 136 | def DECODER(msg, address=None, host=None): 137 | sender=collapse_rfc2231_value(msg['from']) 138 | m=sendere.match(sender) 139 | res={} 140 | if m: 141 | res['sender_name'], res['sender_mail']=m.groups() 142 | else: 143 | res['sender_mail']=sender 144 | 145 | for mpart in msg.walk(): 146 | part=to_message(mpart) 147 | # cut of preamble 148 | inblock=False 149 | lines=part.get_payload(decode=True).split('\n') 150 | i=0 151 | #logging.info(lines) 152 | while i %s" % x for x in ret.stdout.split('\n')]) 170 | # extra points, 171 | # - no named recipient 172 | # - signed 173 | modifiers={'sekrit': False, 'signed': False} 174 | #logging.info(res['keys']) 175 | if len([x for x in res['keys'] if x['key_id']!="0000000000000000"])==0: 176 | modifiers['sekrit']=True 177 | signed={} 178 | for line in ret.stderr.split('\n'): 179 | if line.startswith('gpg: Signature made '): 180 | # gpg: Signature made Fri 11 May 2012 04:43:04 PM CEST using RSA key ID XXXXXX 181 | m=signed1re.match(line) 182 | if m: 183 | #logging.info(m.groups()) 184 | signed['date']=dparse(str(m.group(1))) 185 | signed['algo']=m.group(2) 186 | signed['key_id']=m.group(3) 187 | elif line.startswith('gpg: Good signature from '): 188 | # gpg: Good signature from "name " 189 | m=signed2re.match(line) 190 | if m: 191 | #logging.info(m.groups()) 192 | signed['name']=m.group(1) 193 | signed['mail']=m.group(2) 194 | modifiers['signed']=True 195 | if signed: res['signed']=signed 196 | res['award']=award("You sent an encrypted mail.\n%s" % '\n'.join(["%s [%s]" % (k,'X' if v else ' ') for k,v in modifiers.items()])) 197 | #logging.info(res) 198 | welcome = view.respond(res, "pgpmail.msg", 199 | From=sendermail, 200 | To=sender, 201 | Subject="Encrypted mail received") 202 | relay.deliver(welcome) 203 | 204 | otrfpre=re.compile(r'(\S*@\S*)\s\s*([0-9a-fA-F]{8} [0-9a-fA-F]{8} [0-9a-fA-F]{8} [0-9a-fA-F]{8} [0-9a-fA-F]{8})$') 205 | 206 | @route("otrfp@(host)") 207 | @stateless 208 | def otrfp(msg, address=None, host=None): 209 | sender=collapse_rfc2231_value(msg['from']) 210 | m=sendere.match(sender) 211 | res={} 212 | if m: 213 | res['sender_name'], res['sender_mail']=m.groups() 214 | else: 215 | res['sender_mail']=sender 216 | 217 | for mpart in msg.walk(): 218 | part=to_message(mpart) 219 | # cut of preamble 220 | inblock=False 221 | lines=part.get_payload(decode=True).split('\n') 222 | i=0 223 | #logging.info(lines) 224 | while i %s" % x for x in ret.stdout.split('\n')]) 242 | # extra points, 243 | # - no named recipient 244 | # - signed 245 | #logging.info(res['keys']) 246 | modifiers={'sekrit': False, 'signed': False} 247 | if len([x for x in res['keys'] if x['key_id']!="0000000000000000"])==0: 248 | modifiers['sekrit']=True 249 | else: 250 | logging.warn([x for x in res['keys'] if x['key_id']!="0000000000000000"]) 251 | signed={} 252 | for line in ret.stderr.split('\n'): 253 | if line.startswith('gpg: Signature made '): 254 | # gpg: Signature made Fri 11 May 2012 04:43:04 PM CEST using RSA key ID XXXXXX 255 | m=signed1re.match(line) 256 | if m: 257 | #logging.info(m.groups()) 258 | signed['date']=dparse(str(m.group(1))) 259 | signed['algo']=m.group(2) 260 | signed['key_id']=m.group(3) 261 | elif line.startswith('gpg: Good signature from '): 262 | # gpg: Good signature from "name " 263 | m=signed2re.match(line) 264 | if m: 265 | #logging.info(m.groups()) 266 | signed['name']=m.group(1) 267 | signed['mail']=m.group(2) 268 | modifiers['signed']=True 269 | if not signed: 270 | plssign = view.respond(res, "plssign.msg", 271 | From=sendermail, 272 | To=sender, 273 | Subject="OTR fingerprint help") 274 | relay.deliver(plssign) 275 | continue 276 | res['signed']=signed 277 | res['award']=award("you bootstrapped OTR trust using PGP.\n%s" % '\n'.join(["%s [%s]" % (k,'X' if v else ' ') for k,v in modifiers.items()])) 278 | #logging.info(res) 279 | jid=None 280 | fp=None 281 | secret=None 282 | for line in to_message(from_string(ret.stdout)).get_payload(decode=True).split('\n'): 283 | if not line.strip(): continue 284 | if line=='-- ': break 285 | if jid and fp: 286 | secret=line 287 | break 288 | #logging.info("line "+line) 289 | m=otrfpre.match(line) 290 | if m: 291 | #logging.info(m.groups()) 292 | jid, fp = m.group(1), m.group(2) 293 | if jid and fp: 294 | with FileLock('%s/otr/otr/%s.fpr' % (basepath, botjid)): 295 | fr=open('%s/otr/otr/%s.fpr' % (basepath, botjid), 'r') 296 | fw=open('%s/otr/otr/%s.fpr.new' % (basepath, botjid), 'w') 297 | for line in fr: 298 | #logging.info(line) 299 | #logging.info("%s\t%s\tjabber\t%s" % (jid, 300 | # botjid, 301 | # fp.lower().replace(' ',''))) 302 | if line.startswith("%s\t%s\tjabber\t%s" % (jid, 303 | botjid, 304 | fp.lower().replace(' ',''))): 305 | fw.write("%s\t%s\tjabber\t%s\ttrust\n" % (jid, 306 | botjid, 307 | fp.lower().replace(' ',''))) 308 | else: 309 | fw.write(line) 310 | fw.close() 311 | fr.close() 312 | os.unlink('%s/otr/otr/%s.fpr' % (basepath, botjid)) 313 | shutil.move('%s/otr/otr/%s.fpr.new' % (basepath, botjid), 314 | '%s/otr/otr/%s.fpr' % (basepath, botjid)) 315 | if secret: 316 | fs=open('%s/otr/otr/%s.s' % (basepath, jid), 'w') 317 | fs.write("%s %s" % (signed['key_id'], secret)) 318 | fs.close() 319 | welcome = view.respond(res, "otrtrust.msg", 320 | From=sendermail, 321 | To=sender, 322 | Subject="OTR fingerprint received") 323 | relay.deliver(welcome) 324 | 325 | #@spam_filter(SPAM['db'], SPAM['rc'], SPAM['queue'], next_state=SPAMMING) 326 | @route("ono@(host)") 327 | def START(msg, host=None): 328 | sender=collapse_rfc2231_value(msg['from']) 329 | #subj=collapse_rfc2231_value(msg['subject']) 330 | resp = view.respond({}, "welcome.txt", 331 | From=sendermail, 332 | To=sender, 333 | #Subject="Re: %s" % subj) 334 | Subject="thanks! let's chat") 335 | relay.deliver(resp) 336 | return JITSI 337 | 338 | @route("ono@(host)") 339 | def JITSI(msg, host=None): 340 | sender=collapse_rfc2231_value(msg['from']) 341 | resp = view.respond({}, "jitsi.txt", 342 | From=sendermail, 343 | To=sender, 344 | Subject="chatting continued") 345 | relay.deliver(resp) 346 | return SECRET 347 | 348 | @route("ono@(host)") 349 | def SECRET(msg, host=None): 350 | sender=collapse_rfc2231_value(msg['from']) 351 | resp = view.respond({'buddyurl': 'https://%s/buddy' % webhost}, 352 | "fetchsecret.txt", 353 | From=sendermail, 354 | To=sender, 355 | Subject="getting serious") 356 | relay.deliver(resp) 357 | return XMPP 358 | 359 | @route("ono@(host)") 360 | def XMPP(msg, host=None): 361 | sender=collapse_rfc2231_value(msg['from']) 362 | resp = view.respond({}, "1stcontact.txt", 363 | From=sendermail, 364 | To=sender, 365 | Subject="start chatting") 366 | relay.deliver(resp) 367 | return XMPP 368 | 369 | @route("ono-dox-(mailid)@(host)") 370 | @stateless 371 | def doxer(msg, mailid=None, host=None): 372 | try: 373 | keyid=get_val('dox-mailid',":%s" % mailid, second)[:-(len(mailid)+1)] 374 | except TypeError: 375 | #print >>sys.stderr, 'nomailid' 376 | return # no such mailid 377 | pwd=get_state(keyid, 'prod.pdf.pass') 378 | #logging.info("pwd "+pwd) 379 | if not pwd: 380 | add_state(keyid, 'prod.pdf.err',"I got a mail, but i was not aware of the password at that time. try to resend it after telling me the password please.") 381 | return 382 | err = None 383 | for mpart in msg.walk(): 384 | part=to_message(mpart) 385 | #if part.get_content_maintype() == 'multipart' or len(part.get_payload(decode=True)) < 268125: 386 | if part.get_content_maintype() == 'multipart': 387 | #print >>sys.stderr, 'skip', len(part.get_payload(decode=True) or ''), part.get_content_maintype() 388 | continue 389 | size = len(part.get_payload(decode=True)) 390 | if (size < 200000 or size > 310000): 391 | continue 392 | hash=hashlib.sha256() 393 | def hupdate(data): # workaround for http://bugs.python.org/issue17481 394 | hash.update(data) 395 | ret=gpg('-d', 396 | '--passphrase', pwd, 397 | _ok_code=[0,2], 398 | _in=part.get_payload(decode=True), 399 | _out=hupdate) 400 | if ret.exit_code!=0: 401 | add_state(keyid, 'prod.pdf.err',"got a mail, but gpg had problems, try fixing the problem and resend the mail. gpg said this\n"+err) 402 | break 403 | #logging.info('ret '+str(ret)) 404 | #logging.info('stderr '+ret.stderr) 405 | err=str(ret.stderr) # for flushing the process? 406 | #print >>sys.stderr, 'err', err 407 | if hash.hexdigest() == '658be96015645fe1d646fd167c1ac3bd372360530191d574ace5870c5aeb132f': 408 | add_state(keyid, 'prod.pdf.done','1') 409 | break 410 | else: 411 | add_state(keyid, 'prod.pdf.err',"got a mail, but it wasn't quite what i expected, so i dropped it.") 412 | break 413 | else: 414 | add_state(keyid, 'prod.pdf.err',"got a mail, but there was nothing found that looked like a reasonably sized pgp payload") 415 | 416 | @route_like(START) 417 | #@route(".+") 418 | #@stateless 419 | def FORWARD(message, address=None, host=None): 420 | relay.deliver(message) 421 | 422 | 423 | --------------------------------------------------------------------------------