def get_vcards(self): if not self.config.active: return [] gnupg = GnuPG() keys = gnupg.list_keys() results = [] vcards = {} for key in keys.values(): vcls = [VCardLine(name="KEY", value=self.VCL_KEY_FMT % key)] card = None emails = [] for uid in key["uids"]: if "email" in uid and uid["email"]: vcls.append(VCardLine(name="email", value=uid["email"])) card = card or vcards.get(uid['email']) emails.append(uid["email"]) if "name" in uid and uid["name"]: name = uid["name"] vcls.append(VCardLine(name="fn", value=name)) if card: card.add(*vcls) else: # This is us taking care to only create one card for each # set of e-mail addresses. card = SimpleVCard(*vcls) for email in emails: vcards[email] = card results.append(card) return results
def UnwrapMimeCrypto(part, si=None, ei=None): """ This method will replace encrypted and signed parts with their contents and set part attributes describing the security properties instead. """ part.signature_info = si or SignatureInfo() part.encryption_info = ei or EncryptionInfo() mimetype = part.get_content_type() if part.is_multipart(): # FIXME: Check the protocol. PGP? Something else? # FIXME: This is where we add hooks for other MIME encryption # schemes, so route to callbacks by protocol. if mimetype == "multipart/signed": gpg = GnuPG() boundary = part.get_boundary() payload, signature = part.get_payload() # The Python get_payload() method likes to rewrite headers, # which breaks signature verification. So we manually parse # out the raw payload here. head, raw_payload, junk = part.as_string().replace("\r\n", "\n").split("\n--%s\n" % boundary, 2) part.signature_info = gpg.verify(gpg.pgpmime_normalize(raw_payload), signature.get_payload()) # Reparent the contents up, removing the signature wrapper part.set_payload(payload.get_payload()) for h in payload.keys(): del part[h] for h, v in payload.items(): part.add_header(h, v) elif mimetype == "multipart/encrypted": gpg = GnuPG() preamble, payload = part.get_payload() (part.signature_info, part.encryption_info, decrypted) = gpg.decrypt(payload.as_string()) if part.encryption_info["status"] == "decrypted": newpart = email.parser.Parser().parse(StringIO.StringIO(decrypted)) # Reparent the contents up, removing the encryption wrapper part.set_payload(newpart.get_payload()) for h in newpart.keys(): del part[h] for h, v in newpart.items(): part.add_header(h, v) # If we are still multipart after the above shenanigans, recurse # into our subparts and unwrap them too. if part.is_multipart(): for subpart in part.get_payload(): UnwrapMimeCrypto(subpart, si=part.signature_info, ei=part.encryption_info) else: # FIXME: This is where we would handle cryptoschemes that don't # appear as multipart/... pass
def evaluate_pgp(self, tree, check_sigs=True, decrypt=False): if 'text_parts' not in tree: return tree pgpdata = [] for part in tree['text_parts']: if 'crypto' not in part: part['crypto'] = {} ei = si = None if check_sigs: if part['type'] == 'pgpbeginsigned': pgpdata = [part] elif part['type'] == 'pgpsignedtext': pgpdata.append(part) elif part['type'] == 'pgpsignature': pgpdata.append(part) try: gpg = GnuPG() message = ''.join([p['data'].encode(p['charset']) for p in pgpdata]) si = pgpdata[1]['crypto']['signature' ] = gpg.verify(message) pgpdata[0]['data'] = '' pgpdata[2]['data'] = '' except Exception, e: print e if decrypt: if part['type'] in ('pgpbegin', 'pgptext'): pgpdata.append(part) elif part['type'] == 'pgpend': pgpdata.append(part) gpg = GnuPG() (signature_info, encryption_info, text ) = gpg.decrypt(''.join([p['data'] for p in pgpdata])) ei = pgpdata[1]['crypto']['encryption'] = encryption_info si = pgpdata[1]['crypto']['signature'] = signature_info if encryption_info["status"] == "decrypted": pgpdata[1]['data'] = text pgpdata[0]['data'] = "" pgpdata[2]['data'] = "" # Bubbling up! if (si or ei) and 'crypto' not in tree: tree['crypto'] = {'signature': SignatureInfo(), 'encryption': EncryptionInfo()} if si: tree['crypto']['signature'].mix(si) if ei: tree['crypto']['encryption'].mix(ei)
def evaluate_pgp(self, tree, check_sigs=True, decrypt=False): if 'text_parts' not in tree: return tree pgpdata = [] for part in tree['text_parts']: # Handle signed messages if 'signature_info' not in part: part['signature_info'] = SignatureInfo() if check_sigs: if part['type'] == 'pgpbeginsigned': pgpdata = [part] elif part['type'] == 'pgpsignedtext': pgpdata.append(part) elif part['type'] == 'pgpsignature': pgpdata.append(part) try: gpg = GnuPG() message = ''.join([p['data'].encode(p['charset']) for p in pgpdata]) pgpdata[1]['signature_info'] = gpg.verify(message) pgpdata[0]['data'] = '' pgpdata[2]['data'] = '' except Exception, e: print e if "encryption_info" not in part: part['encryption_info'] = EncryptionInfo() if decrypt: if part['type'] in ('pgpbegin', 'pgptext'): pgpdata.append(part) elif part['type'] == 'pgpend': pgpdata.append(part) message = ''.join([p['data'] for p in pgpdata]) gpg = GnuPG() encryption_info, text = gpg.decrypt(message) pgpdata[1]['encryption_info'] = encryption_info if encryption_info["status"] == "decrypted": pgpdata[1]['data'] = text pgpdata[0]['data'] = "" pgpdata[2]['data'] = ""
def evaluate_pgp(self, tree, check_sigs=True, decrypt=False): if "text_parts" not in tree: return tree pgpdata = [] for part in tree["text_parts"]: # Handle signed messages if "signature_info" not in part: part["signature_info"] = SignatureInfo() if check_sigs: if part["type"] == "pgpbeginsigned": pgpdata = [part] elif part["type"] == "pgpsignedtext": pgpdata.append(part) elif part["type"] == "pgpsignature": pgpdata.append(part) try: gpg = GnuPG() message = "".join([p["data"].encode(p["charset"]) for p in pgpdata]) pgpdata[1]["signature_info"] = gpg.verify(message) pgpdata[0]["data"] = "" pgpdata[2]["data"] = "" except Exception, e: print e if "encryption_info" not in part: part["encryption_info"] = EncryptionInfo() if decrypt: if part["type"] in ("pgpbegin", "pgptext"): pgpdata.append(part) elif part["type"] == "pgpend": pgpdata.append(part) message = "".join([p["data"] for p in pgpdata]) gpg = GnuPG() si, encryption_info, text = gpg.decrypt(message) pgpdata[1]["encryption_info"] = encryption_info if encryption_info["status"] == "decrypted": pgpdata[1]["data"] = text pgpdata[0]["data"] = "" pgpdata[2]["data"] = ""
def PrepareMail(config, mailobj, sender=None, rcpts=None): if not sender or not rcpts: tree = mailobj.get_message_tree() sender = sender or tree['headers_lc']['from'] if not rcpts: rcpts = ExtractEmails(tree['headers_lc'].get('to', '')) rcpts += ExtractEmails(tree['headers_lc'].get('cc', '')) rcpts += ExtractEmails(tree['headers_lc'].get('bcc', '')) if not rcpts: raise NoRecipientError() rcpts += [sender] # Cleanup... sender = ExtractEmails(sender)[0] sender_keyid = None if config.prefs.openpgp_header: try: gnupg = GnuPG() seckeys = dict([(x["email"], y["fingerprint"]) for y in gnupg.list_secret_keys().values() for x in y["uids"]]) sender_keyid = seckeys[sender] except: pass rcpts, rr = [sender], rcpts for r in rr: for e in ExtractEmails(r): if e not in rcpts: rcpts.append(e) msg = copy.deepcopy(mailobj.get_msg()) # Remove headers we don't want to expose for bcc in ('bcc', 'Bcc', 'BCc', 'BCC'): if bcc in msg: del msg[bcc] if 'date' not in msg: msg['Date'] = email.utils.formatdate() if sender_keyid and config.prefs.openpgp_header: msg["OpenPGP"] = "id=%s; preference=%s" % (sender_keyid, config.prefs.openpgp_header) # Sign and encrypt signatureopt = bool(int(tree['headers_lc'].get('do_sign', 0))) encryptopt = bool(int(tree['headers_lc'].get('do_encrypt', 0))) gnupg = GnuPG() if signatureopt: signingstring = MessageAsString(msg) signature = gnupg.sign(signingstring, fromkey=sender, armor=True) # FIXME: Create attachment, attach signature. if signature[0] == 0: # sigblock = MIMEMultipart(_subtype="signed", # protocol="application/pgp-signature") # sigblock.attach(msg) msg.set_type("multipart/signed") msg.set_param("micalg", "pgp-sha1") # need to find this! msg.set_param("protocol", "application/pgp-signature") sigblock = MIMEText(str(signature[1]), _charset=None) sigblock.set_type("application/pgp-signature") sigblock.set_param("name", "signature.asc") sigblock.add_header("Content-Description", "OpenPGP digital signature") sigblock.add_header("Content-Disposition", "attachment; filename=\"signature.asc\"") msg.attach(sigblock) else: # Raise stink about signing having failed. pass #print signature #if encryptopt: # encrypt_to = tree['headers_lc'].get('encrypt_to') # newmsg = gnupg.encrypt(msg.as_string(), encrypt_to) # # TODO: Replace unencrypted message # When a mail has been signed or encrypted, it should be saved as such. del(msg["do_sign"]) del(msg["do_encrypt"]) del(msg["encrypt_to"]) return (sender, set(rcpts), msg)
def command(self): session = self.session # Create local mailboxes session.config.open_local_mailbox(session) # Create standard tags and filters created = [] for t in self.TAGS: if not session.config.get_tag_id(t): AddTag(session, arg=[t]).run() created.append(t) session.config.get_tag(t).update(self.TAGS[t]) if 'New' in created: Filter(session, arg=['new', '+Inbox', '+New', 'New Mail filter']).run() session.ui.notify(_('Created default tags')) # Import all the basic plugins for plugin in PLUGINS: if plugin not in session.config.sys.plugins: session.config.sys.plugins.append(plugin) try: # If spambayes is not installed, this will fail import mailpile.plugins.autotag_sb if 'autotag_sb' not in session.config.sys.plugins: session.config.sys.plugins.append('autotag_sb') session.ui.notify(_('Enabling spambayes autotagger')) except ImportError: session.ui.warning(_('Please install spambayes ' 'for super awesome spam filtering')) session.config.save() session.config.load(session) vcard_importers = session.config.prefs.vcard.importers if not vcard_importers.gravatar: vcard_importers.gravatar.append({'active': True}) session.ui.notify(_('Enabling gravatar image importer')) gpg_home = os.path.expanduser('~/.gnupg') if os.path.exists(gpg_home) and not vcard_importers.gpg: vcard_importers.gpg.append({'active': True, 'gpg_home': gpg_home}) session.ui.notify(_('Importing contacts from GPG keyring')) if ('autotag_sb' in session.config.sys.plugins and len(session.config.prefs.autotag) == 0): session.config.prefs.autotag.append({ 'match_tag': 'spam', 'unsure_tag': 'maybespam', 'tagger': 'spambayes', 'trainer': 'spambayes' }) session.config.prefs.autotag[0].exclude_tags[0] = 'ham' # Assumption: If you already have secret keys, you want to # use the associated addresses for your e-mail. # If you don't already have secret keys, you should have # one made for you, if GnuPG is available. # If GnuPG is not available, you should be warned. gnupg = GnuPG() if gnupg.is_available(): keys = gnupg.list_secret_keys() if len(keys) == 0: # FIXME: Start background process generating a key once a user # has supplied a name and e-mail address. pass else: for key, details in keys.iteritems(): for uid in details["uids"]: if "email" not in uid or uid["email"] == "": continue if uid["email"] in [x["email"] for x in session.config.profiles]: # Don't set up the same e-mail address twice. continue # FIXME: Add route discovery mechanism. profile = { "email": uid["email"], "name": uid["name"], } session.config.profiles.append(profile) if not session.config.prefs.gpg_recipient: session.config.prefs.gpg_recipient = key session.ui.notify(_('Encrypting config to %s') % key) else: session.ui.warning(_('Oh no, PGP/GPG support is unavailable!')) if (session.config.prefs.gpg_recipient and not (self._idx() and self._idx().INDEX) and not session.config.prefs.obfuscate_index): randcrap = sha512b64(open('/dev/urandom').read(1024), session.config.prefs.gpg_recipient, '%s' % time.time()) session.config.prefs.obfuscate_index = randcrap session.config.prefs.index_encrypted = True session.ui.notify(_('Obfuscating search index and enabling ' 'indexing of encrypted e-mail. ')) session.config.save() return True
def command(self): session = self.session # Create local mailboxes session.config.open_local_mailbox(session) # Create standard tags and filters created = [] for t in self.TAGS: if not session.config.get_tag_id(t): AddTag(session, arg=[t]).run() created.append(t) session.config.get_tag(t).update(self.TAGS[t]) if 'New' in created: Filter(session, arg=['new', '+Inbox', '+New', 'New Mail filter']).run() # Import all the basic plugins for plugin in PLUGINS: if plugin not in session.config.sys.plugins: session.config.sys.plugins.append(plugin) try: # If spambayes is not installed, this will fail import mailpile.plugins.autotag_sb if 'autotag_sb' not in session.config.sys.plugins: session.config.sys.plugins.append('autotag_sb') except ImportError: session.ui.warning(_('Please install spambayes ' 'for super awesome spam filtering')) session.config.save() session.config.load(session) vcard_importers = session.config.prefs.vcard.importers if not vcard_importers.gravatar: vcard_importers.gravatar.append({'active': True}) gpg_home = os.path.expanduser('~/.gnupg') if os.path.exists(gpg_home) and not vcard_importers.gpg: vcard_importers.gpg.append({'active': True, 'gpg_home': gpg_home}) # Assumption: If you already have secret keys, you want to # use the associated addresses for your e-mail. # If you don't already have secret keys, you should have # one made for you, if GnuPG is available. # If GnuPG is not available, you should be warned. gnupg = GnuPG() if gnupg.is_available(): keys = gnupg.list_secret_keys() if len(keys) == 0: # FIXME: Start background process generating a key once a user # has supplied a name and e-mail address. pass else: for key, details in keys.iteritems(): for uid in details["uids"]: if "email" not in uid or uid["email"] == "": continue if uid["email"] in [x["email"] for x in session.config.profiles]: # Don't set up the same e-mail address twice. continue # FIXME: Add route discovery mechanism. profile = { "email": uid["email"], "name": uid["name"], } session.config.profiles.append(profile) else: # FIXME: Alert the user to the fact that PGP was not discovered pass if ('autotag_sb' in session.config.sys.plugins and len(session.config.prefs.autotag) == 0): session.config.prefs.autotag.append({ 'match_tag': 'spam', 'unsure_tag': 'maybespam', 'tagger': 'spambayes', 'trainer': 'spambayes' }) session.config.prefs.autotag[0].exclude_tags[0] = 'ham' session.config.save() return True
def command(self): session = self.session # Create local mailboxes session.config.open_local_mailbox(session) # Create standard tags and filters created = [] for t in ('New', 'Inbox', 'Outbox', 'Spam', 'Drafts', 'Blank', 'Sent', 'Trash'): if not session.config.get_tag_id(t): AddTag(session, arg=[t]).run() created.append(t) session.config.get_tag('New').update({ 'type': 'unread', 'label': False, 'display': 'invisible' }) session.config.get_tag('Blank').update({ 'type': 'blank', 'flag_editable': True, 'display': 'invisible' }) session.config.get_tag('Drafts').update({ 'type': 'drafts', 'flag_editable': True, 'display': 'priority', 'display_order': 1, }) session.config.get_tag('Inbox').update({ 'display': 'priority', 'display_order': 2, }) session.config.get_tag('Outbox').update({ 'type': 'outbox', 'display': 'priority', 'display_order': 3, }) session.config.get_tag('Sent').update({ 'type': 'sent', 'display': 'priority', 'display_order': 4, }) session.config.get_tag('Spam').update({ 'type': 'spam', 'flag_hides': True, 'display': 'priority', 'display_order': 5, }) session.config.get_tag('Trash').update({ 'type': 'trash', 'flag_hides': True, 'display': 'priority', 'display_order': 6, }) if 'New' in created: Filter(session, arg=['new', '+Inbox', '+New', 'New Mail filter']).run() Filter(session, arg=['read', '-New', 'Read Mail filter']).run() for old in ('invisible_tags', 'writable_tags'): if old in session.config.sys: del session.config.sys[old] vcard_importers = session.config.prefs.vcard.importers if not vcard_importers.gravatar: vcard_importers.gravatar.append({'active': True}) gpg_home = os.path.expanduser('~/.gnupg') if os.path.exists(gpg_home) and not vcard_importers.gpg: vcard_importers.gpg.append({'active': True, 'gpg_home': gpg_home}) # Assumption: If you already have secret keys, you want to # use the associated addresses for your e-mail. # If you don't already have secret keys, you should have # one made for you, if GnuPG is available. # If GnuPG is not available, you should be warned. gnupg = GnuPG() if gnupg.is_available(): keys = gnupg.list_secret_keys() if len(keys) == 0: # FIXME: Start background process generating a key once a user # has supplied a name and e-mail address. pass else: for key, details in keys.iteritems(): for uid in details["uids"]: if "email" not in uid or uid["email"] == "": continue if uid["email"] in [x["email"] for x in session.config.profiles]: # Don't set up the same e-mail address twice. continue # FIXME: Add route discovery mechanism. profile = { "email": uid["email"], "name": uid["name"], } session.config.profiles.append(profile) else: # FIXME: Alert the user to the fact that PGP was not discovered pass session.config.save() return True
def PrepareMail(mailobj, sender=None, rcpts=None): if not sender or not rcpts: tree = mailobj.get_message_tree() sender = sender or tree['headers_lc']['from'] if not rcpts: rcpts = ExtractEmails(tree['headers_lc'].get('to', '')) rcpts += ExtractEmails(tree['headers_lc'].get('cc', '')) rcpts += ExtractEmails(tree['headers_lc'].get('bcc', '')) if not rcpts: raise NoRecipientError() rcpts += [sender] # Cleanup... sender = ExtractEmails(sender)[0] rcpts, rr = [sender], rcpts for r in rr: for e in ExtractEmails(r): if e not in rcpts: rcpts.append(e) msg = copy.deepcopy(mailobj.get_msg()) # Remove headers we don't want to expose for bcc in ('bcc', 'Bcc', 'BCc', 'BCC'): if bcc in msg: del msg[bcc] if 'date' not in msg: msg['Date'] = email.utils.formatdate() # Sign and encrypt signatureopt = bool(int(tree['headers_lc'].get('do_sign', 0))) encryptopt = bool(int(tree['headers_lc'].get('do_encrypt', 0))) gnupg = GnuPG() if signatureopt: signingstring = re.sub("[\r]{1}[\n]{0}", "\r\n", msg.get_payload()[0].as_string()) # print ">>>%s<<<" % signingstring.replace("\r", "<CR>").replace("\n", "<LF>") signature = gnupg.sign(signingstring, fromkey=sender, armor=True) # TODO: Create attachment, attach signature. if signature[0] == 0: # sigblock = MIMEMultipart(_subtype="signed", protocol="application/pgp-signature") # sigblock.attach(msg) msg.set_type("multipart/signed") msg.set_param("micalg", "pgp-sha1") # need to find this! msg.set_param("protocol", "application/pgp-signature") sigblock = MIMEText(str(signature[1]), _charset=None) sigblock.set_type("application/pgp-signature") sigblock.set_param("name", "signature.asc") sigblock.add_header("Content-Description", "OpenPGP digital signature") sigblock.add_header("Content-Disposition", "attachment; filename=\"signature.asc\"") msg.attach(sigblock) else: # Raise stink about signing having failed. pass #print signature #if encryptopt: # encrypt_to = tree['headers_lc'].get('encrypt_to') # newmsg = gnupg.encrypt(msg.as_string(), encrypt_to) # # TODO: Replace unencrypted message # When a mail has been signed or encrypted, it should be saved as such. del(msg["do_sign"]) del(msg["do_encrypt"]) del(msg["encrypt_to"]) return (sender, set(rcpts), msg)