def command(self): signingkey = None keyid = None args = list(self.args) try: keyid = args.pop(0) except: keyid = self.data.get("keyid", None) try: signingkey = args.pop(0) except: signingkey = self.data.get("signingkey", None) print keyid if not keyid: return self._error("You must supply a keyid", None) g = GnuPG() return g.sign_key(keyid, signingkey)
def VerifyAndStorePassphrase(config, passphrase=None, sps=None, key=None): if passphrase and not sps: from mailpile.config import SecurePassphraseStorage sps = SecurePassphraseStorage(passphrase) passphrase = 'this probably does not really overwrite :-( ' assert (sps is not None) # Note: Must use GnuPG without a config, otherwise bad things happen. gpg = GnuPG(None, use_agent=False, debug=('gnupg' in config.sys.debug)) if gpg.is_available(): gpg.passphrase = sps.get_reader() gpgr = config.prefs.gpg_recipient gpgr = key or (gpgr if (gpgr not in (None, '', '!CREATE')) else None) assert (gpg.sign('Sign This!', fromkey=gpgr)[0] == 0) # Fun side effect: changing the passphrase invalidates the message cache import mailpile.mailutils mailpile.mailutils.ClearParseCache(full=True) return sps
def command(self): if self.session.config.sys.lockdown: return self._error(_('In lockdown, doing nothing.')) signingkey = None keyid = None args = list(self.args) try: keyid = args.pop(0) except: keyid = self.data.get("keyid", None) try: signingkey = args.pop(0) except: signingkey = self.data.get("signingkey", None) print keyid if not keyid: return self._error("You must supply a keyid", None) g = GnuPG() return g.sign_key(keyid, signingkey)
def get_vcards(self, selectors=None, public=True, secret=True, vcards=None): if not self.config.active: return [] # Generate all the nice new cards! new_cards = self.gnupg_keys_as_vcards(GnuPG(self.session.config), selectors=selectors, public=public, secret=secret) # Generate tombstones for keys which are gone from the keyring. if vcards: deleted = set() deleted_names = {} search = ';%s/' % self.config.guid for cardid, vcard in (vcards or {}).iteritems(): for vcl in vcard.get_all('clientpidmap'): if search in vcl.value: key_id = vcl.value.split(';')[1] deleted.add(key_id) deleted_names[key_id] = vcard.fn if selectors: deleted = set([ guid for guid in deleted if guid.split('/')[-1] in selectors ]) deleted -= set([self.get_guid(card) for card in new_cards]) for guid in deleted: mrgid = guid.split('/')[-1] fn = deleted_names[guid] new_cards.append( MailpileVCard(VCardLine(name='fn', value=fn), VCardLine(name='x-gpg-mrgid', value=mrgid))) return new_cards
def command(self): session, config, idx = self.session, self.session.config, self._idx() args = list(self.args) if args and args[-1][0] == "#": attid = args.pop() else: attid = self.data.get("att", 'application/pgp-keys') args.extend(["=%s" % x for x in self.data.get("mid", [])]) eids = self._choose_messages(args) if len(eids) < 0: return self._error("No messages selected", None) elif len(eids) > 1: return self._error("One message at a time, please", None) email = Email(idx, list(eids)[0]) fn, attr = email.extract_attachment(session, attid, mode='inline') if attr and attr["data"]: g = GnuPG() res = g.import_keys(attr["data"]) return self._success("Imported key", res) return self._error("No results found", None)
def get_vcards(self): if not self.config.active: return [] gnupg = GnuPG(self.session.config) keys = gnupg.list_keys() results = [] vcards = {} for key_id, key in keys.iteritems(): if (key.get("disabled") or key.get("revoked") or not key["capabilities_map"].get("encrypt") or not key["capabilities_map"].get("sign")): continue vcls = [VCardLine(name='KEY', value=self.VCL_KEY_FMT % key_id)] card = None emails = [] for uid in key.get('uids', []): if uid.get('email'): vcls.append(VCardLine(name='email', value=uid['email'])) card = card or vcards.get(uid['email']) emails.append(uid['email']) if uid.get('name'): name = uid['name'] vcls.append(VCardLine(name='fn', value=name)) if card and emails: card.add(*vcls) elif emails: # This is us taking care to only create one card for each # set of e-mail addresses. card = MailpileVCard(*vcls) for email in emails: vcards[email] = card results.append(card) return results
def PrepareMessage(config, msg, sender=None, rcpts=None, events=None): msg = copy.deepcopy(msg) # Short circuit if this message has already been prepared. if 'x-mp-internal-sender' in msg and 'x-mp-internal-rcpts' in msg: return (sender or msg['x-mp-internal-sender'], rcpts or [r.strip() for r in msg['x-mp-internal-rcpts'].split(',')], msg, events) crypto_policy = config.prefs.crypto_policy.lower() rcpts = rcpts or [] # Iterate through headers to figure out what we want to do... need_rcpts = not rcpts for hdr, val in msg.items(): lhdr = hdr.lower() if lhdr == 'from': sender = sender or val elif lhdr == 'encryption': crypto_policy = val.lower() elif need_rcpts and lhdr in ('to', 'cc', 'bcc'): rcpts += ExtractEmails(val, strip_keys=False) # Are we sane? if not sender: raise NoFromAddressError() if not rcpts: raise NoRecipientError() # Are we encrypting? Signing? if crypto_policy == 'default': crypto_policy = config.prefs.crypto_policy # This is the BCC hack that Brennan hates! rcpts += [sender] sender = ExtractEmails(sender, strip_keys=False)[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, strip_keys=False): if e not in rcpts: rcpts.append(e) # Add headers we require 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) if 'openpgp' in crypto_policy: # FIXME: Make a more efficient sign+encrypt wrapper cleaner = lambda m: CleanMessage(config, m) if 'sign' in crypto_policy: msg = OpenPGPMimeSigningWrapper(config, sender=sender, cleaner=cleaner, recipients=rcpts).wrap(msg) if 'encrypt' in crypto_policy: msg = OpenPGPMimeEncryptingWrapper(config, sender=sender, cleaner=cleaner, recipients=rcpts).wrap(msg) rcpts = set([r.rsplit('#', 1)[0] for r in rcpts]) msg['x-mp-internal-readonly'] = str(int(time.time())) msg['x-mp-internal-sender'] = sender msg['x-mp-internal-rcpts'] = ', '.join(rcpts) return (sender, rcpts, msg, events)
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])) # FIXME: If the data is binary, we should provide some # sort of download link or maybe leave the PGP # blob entirely intact, undecoded. text, charset = self.decode_text(text, binary=False) 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 setup_command(self, session, do_gpg_stuff=False): do_gpg_stuff = do_gpg_stuff or ('do_gpg_stuff' in self.args) # Stop the workers... want_daemons = session.config.cron_worker is not None session.config.stop_workers() # Perform any required migrations Migrate(session).run(before_setup=True, after_setup=False) # Basic app config, tags, plugins, etc. self.basic_app_config(session, save_and_update_workers=False, want_daemons=want_daemons) # 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. if do_gpg_stuff: gnupg = GnuPG(None) accepted_keys = [] if gnupg.is_available(): keys = gnupg.list_secret_keys() for key, details in keys.iteritems(): # Ignore revoked/expired keys. if ("revocation-date" in details and details["revocation-date"] <= date.today().strftime("%Y-%m-%d")): continue accepted_keys.append(key) 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 (session.config.prefs.gpg_recipient in (None, '', '!CREATE') and details["capabilities_map"][0]["encrypt"]): session.config.prefs.gpg_recipient = key session.ui.notify(_('Encrypting config to %s') % key) if session.config.prefs.crypto_policy == 'none': session.config.prefs.crypto_policy = 'openpgp-sign' if len(accepted_keys) == 0: # FIXME: Start background process generating a key once a user # has supplied a name and e-mail address. pass else: session.ui.warning(_('Oh no, PGP/GPG support is unavailable!')) # If we have a GPG key, but no master key, create it self.make_master_key() # Perform any required migrations Migrate(session).run(before_setup=False, after_setup=True) session.config.save() session.config.prepare_workers(session, daemons=want_daemons) return self._success(_('Performed initial Mailpile setup'))
def _gnupg(self): return GnuPG(self.session and self.session.config or None)
def _gnupg(self): return GnuPG(self.session.config, event=self.event)
def command(self): keyid = self.data.get("keyid", self.args[0]) g = GnuPG() return g.recv_key(keyid)
def lookup_crypto_keys(session, address, event=None, strict_email_match=False, allowremote=True, origins=None, get=None): known_keys_list = GnuPG(session and session.config or None).list_keys() found_keys = {} ordered_keys = [] if origins: handlers = [ h for h in KEY_LOOKUP_HANDLERS if (h.NAME in origins) or (h.NAME.lower() in origins) ] else: handlers = KEY_LOOKUP_HANDLERS ungotten = get and get[:] or [] progress = [] for handler in handlers: if get and not ungotten: # We have all the keys! break try: h = handler(session, known_keys_list) if not allowremote and not h.LOCAL: continue if found_keys and (not h.PRIVACY_FRIENDLY) and (not origins): # We only try the privacy-hostile methods if we haven't # found any keys (unless origins were specified). if not ungotten: continue progress.append(h.NAME) if event: ordered_keys.sort(key=lambda k: -k["score"]) event.message = _('Searching for encryption keys in: %s') % _( h.NAME) event.private_data = { "result": ordered_keys, "progress": progress, "runningsearch": h.NAME } session.config.event_log.log_event(event) # We allow for more time when importing keys timeout = h.TIMEOUT if ungotten: timeout *= 4 # h.lookup will remove found keys from the wanted list, # but we have to watch out for the effects of timeouts. wanted = ungotten[:] results = RunTimed(timeout, h.lookup, address, strict_email_match=strict_email_match, get=(wanted if (get is not None) else None)) ungotten[:] = wanted except KeyboardInterrupt: raise except: if session.config.sys.debug: traceback.print_exc() results = {} for key_id, key_info in results.iteritems(): if key_id in found_keys: old_scores = found_keys[key_id].get('scores', {}) old_uids = found_keys[key_id].get('uids', [])[:] found_keys[key_id].update(key_info) if 'scores' in found_keys[key_id]: found_keys[key_id]['scores'].update(old_scores) # No need for an else, as old_scores will be empty # Merge in the old UIDs uid_emails = [u['email'] for u in key_info.get('uids', [])] if 'uids' not in found_keys[key_id]: found_keys[key_id]['uids'] = [] for uid in old_uids: email = uid.get('email') if email and email not in uid_emails: found_keys[key_id]['uids'].append(uid) else: found_keys[key_id] = key_info found_keys[key_id]["origins"] = [] found_keys[key_id]["origins"].append(h.NAME) _normalize_key(session, found_keys[key_id]) _update_scores(session, key_id, found_keys[key_id], known_keys_list) # If the key_id was listed in get and is in found_keys then the key # has been successfully imported to the keychain. if key_id in known_keys_list or get and key_id in get: found_keys[key_id]['on_keychain'] = True # Check if key is already in the VCard for the specified address. if address in found_keys[key_id]['vcards']: vcard = found_keys[key_id]['vcards'][address] if 'keys' in vcard: for k in vcard['keys']: if k['fingerprint'] == key_id: found_keys[key_id]['in_vcards'] = True # This updates and sorts ordered_keys in place. This will magically # also update the data on the viewable event, because Python. ordered_keys[:] = found_keys.values() ordered_keys.sort(key=lambda k: -k["score"]) if event: event.private_data = {"result": ordered_keys, "runningsearch": False} session.config.event_log.log_event(event) return ordered_keys
def _gnupg(self, **kwargs): return GnuPG(self.session.config, event=self.event, **kwargs)
def TransformOutgoing(self, sender, rcpts, msg, **kwargs): matched = False gnupg = None sender_keyid = None if self.config.prefs.openpgp_header: try: gnupg = gnupg or GnuPG(self.config) seckeys = dict([ (uid["email"], fp) for fp, key in gnupg.list_secret_keys().iteritems() if key["capabilities_map"].get("encrypt") and key["capabilities_map"].get("sign") for uid in key["uids"] ]) sender_keyid = seckeys.get(sender) except (KeyError, TypeError, IndexError, ValueError): traceback.print_exc() if sender_keyid and self.config.prefs.openpgp_header: msg["OpenPGP"] = ("id=%s; preference=%s" % (sender_keyid, self.config.prefs.openpgp_header)) if ('attach-pgp-pubkey' in msg and msg['attach-pgp-pubkey'][:3].lower() in ('yes', 'tru')): # FIXME: Check attach_pgp_pubkey for instructions on which key(s) # to attach. Attaching all of them may be a bit lame. gnupg = gnupg or GnuPG(self.config) keys = gnupg.address_to_keys(ExtractEmails(sender)[0]) key_count = 0 for fp, key in keys.iteritems(): if not any(key["capabilities_map"].values()): continue # We should never really hit this more than once. But if we # do, should still be fine. keyid = key["keyid"] data = gnupg.get_pubkey(keyid) try: from_name = key["uids"][0]["name"] filename = _('Encryption key for %s.asc') % from_name except: filename = _('My encryption key.asc') att = MIMEBase('application', 'pgp-keys') att.set_payload(data) encoders.encode_base64(att) del att['MIME-Version'] att.add_header('Content-Id', MakeContentID()) att.add_header('Content-Disposition', 'attachment', filename=filename) att.signature_info = SignatureInfo(parent=msg.signature_info) att.encryption_info = EncryptionInfo( parent=msg.encryption_info) msg.attach(att) key_count += 1 if key_count > 0: msg['x-mp-internal-pubkeys-attached'] = "Yes" return sender, rcpts, msg, matched, True
def _lookup(self, address): g = GnuPG() return g.search_key(address)
def TransformOutgoing(self, sender, rcpts, msg, **kwargs): matched = False gnupg = None sender_keyid = None # Prefer to just get everything from the profile VCard, in the # common case... profile = self.config.vcards.get_vcard(sender) if profile: sender_keyid = profile.pgp_key crypto_format = profile.crypto_format or 'none' else: crypto_format = 'none' # Parse the openpgp_header data from the crypto_format openpgp_header = [ p.split(':')[-1] for p in crypto_format.split('+') if p.startswith('openpgp_header:') ] if not openpgp_header: openpgp_header = self.config.prefs.openpgp_header and ['CFG'] if openpgp_header[0] != 'N' and not sender_keyid: # This is a fallback: this shouldn't happen much in normal use try: gnupg = gnupg or GnuPG(self.config, event=GetThreadEvent()) seckeys = dict([ (uid["email"], fp) for fp, key in gnupg.list_secret_keys().iteritems() if key["capabilities_map"].get("encrypt") and key["capabilities_map"].get("sign") for uid in key["uids"] ]) sender_keyid = seckeys.get(sender) except (KeyError, TypeError, IndexError, ValueError): traceback.print_exc() if sender_keyid and openpgp_header: preference = { 'ES': 'signencrypt', 'SE': 'signencrypt', 'E': 'encrypt', 'S': 'sign', 'N': 'unprotected', 'CFG': self.config.prefs.openpgp_header }[openpgp_header[0].upper()] msg["OpenPGP"] = ("id=%s; preference=%s" % (sender_keyid, preference)) if ('attach-pgp-pubkey' in msg and msg['attach-pgp-pubkey'][:3].lower() in ('yes', 'tru')): gnupg = gnupg or GnuPG(self.config, event=GetThreadEvent()) if sender_keyid: keys = gnupg.list_keys(selectors=[sender_keyid]) else: keys = gnupg.address_to_keys(ExtractEmails(sender)[0]) key_count = 0 for fp, key in keys.iteritems(): if not any(key["capabilities_map"].values()): continue # We should never really hit this more than once. But if we # do, should still be fine. keyid = key["keyid"] data = gnupg.get_pubkey(keyid) try: from_name = key["uids"][0]["name"] filename = _('Encryption key for %s.asc') % from_name except: filename = _('My encryption key.asc') att = MIMEBase('application', 'pgp-keys') att.set_payload(data) encoders.encode_base64(att) del att['MIME-Version'] att.add_header('Content-Id', MakeContentID()) att.add_header('Content-Disposition', 'attachment', filename=filename) att.signature_info = SignatureInfo(parent=msg.signature_info) att.encryption_info = EncryptionInfo( parent=msg.encryption_info) msg.attach(att) key_count += 1 if key_count > 0: msg['x-mp-internal-pubkeys-attached'] = "Yes" return sender, rcpts, msg, matched, True
def _GnuPG(session): gpg = GnuPG() if session and session.config: gpg.passphrase = session.config.gnupg_passphrase.get_reader() return gpg
def lookup_crypto_keys(session, address, event=None, strict_email_match=False, allowremote=True, origins=None, get=None): known_keys_list = GnuPG(session and session.config or None).list_keys() found_keys = {} ordered_keys = [] if origins: handlers = [h for h in KEY_LOOKUP_HANDLERS if (h.NAME in origins) or (h.NAME.lower() in origins)] else: handlers = KEY_LOOKUP_HANDLERS ungotten = get and get[:] or [] for handler in handlers: if get and not ungotten: # We have all the keys! break try: h = handler(session, known_keys_list) if not allowremote and not h.LOCAL: continue if event: ordered_keys.sort(key=lambda k: -k["score"]) event.message = _('Searching for encryption keys in: %s' ) % _(h.NAME) event.private_data = {"result": ordered_keys, "runningsearch": h.NAME} session.config.event_log.log_event(event) # We allow for more time when importing keys timeout = h.TIMEOUT if ungotten: timeout *= 4 # h.lookup will remove found keys from the wanted list, # but we have to watch out for the effects of timeouts. wanted = ungotten[:] results = RunTimed(timeout, h.lookup, address, strict_email_match=strict_email_match, get=(wanted if (get is not None) else None)) ungotten[:] = wanted except (TimedOut, IOError, KeyError, ValueError, TypeError, AttributeError): if session.config.sys.debug: traceback.print_exc() results = {} for key_id, key_info in results.iteritems(): if key_id in found_keys: old_scores = found_keys[key_id].get('scores', {}) old_uids = found_keys[key_id].get('uids', [])[:] found_keys[key_id].update(key_info) if 'scores' in found_keys[key_id]: found_keys[key_id]['scores'].update(old_scores) # No need for an else, as old_scores will be empty # Merge in the old UIDs uid_emails = [u['email'] for u in key_info.get('uids', [])] if 'uids' not in found_keys[key_id]: found_keys[key_id]['uids'] = [] for uid in old_uids: email = uid.get('email') if email and email not in uid_emails: found_keys[key_id]['uids'].append(uid) else: found_keys[key_id] = key_info found_keys[key_id]["origins"] = [] found_keys[key_id]["origins"].append(h.NAME) _update_scores(session, key_id, found_keys[key_id], known_keys_list) _normalize_key(session, found_keys[key_id]) # This updates and sorts ordered_keys in place. This will magically # also update the data on the viewable event, because Python. ordered_keys[:] = found_keys.values() ordered_keys.sort(key=lambda k: -k["score"]) if event: event.private_data = {"result": ordered_keys, "runningsearch": False} session.config.event_log.log_event(event) return ordered_keys
def command(self): g = GnuPG() res = g.list_secret_keys() return self._success("Searched for secret keys", res)
def command(self): session = self.session if session.config.sys.lockdown: return self._error(_('In lockdown, doing nothing.')) # Perform any required migrations Migrate(session).run(before_setup=True, after_setup=False) # 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(save=False) created.append(t) session.config.get_tag(t).update(self.TAGS[t]) for stype, statuses in (('sig', SignatureInfo.STATUSES), ('enc', EncryptionInfo.STATUSES)): for status in statuses: tagname = 'mp_%s-%s' % (stype, status) if not session.config.get_tag_id(tagname): AddTag(session, arg=[tagname]).run(save=False) created.append(tagname) session.config.get_tag(tagname).update({ 'type': 'attribute', 'display': 'invisible', 'label': False, }) if 'New' in created: 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() accepted_keys = [] if gnupg.is_available(): keys = gnupg.list_secret_keys() for key, details in keys.iteritems(): # Ignore revoked/expired keys. if ("revocation-date" in details and details["revocation-date"] <= date.today().strftime("%Y-%m-%d")): continue accepted_keys.append(key) 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 and details["capabilities_map"][0]["encrypt"]): session.config.prefs.gpg_recipient = key session.ui.notify(_('Encrypting config to %s') % key) if session.config.prefs.crypto_policy == 'none': session.config.prefs.crypto_policy = 'openpgp-sign' if len(accepted_keys) == 0: # FIXME: Start background process generating a key once a user # has supplied a name and e-mail address. pass 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. ')) # Perform any required migrations Migrate(session).run(before_setup=False, after_setup=True) session.config.save() return self._success(_('Performed initial Mailpile setup'))
def lookup_crypto_keys(session, address, event=None, strict_email_match=False, allowremote=True, origins=None, get=None, vcard=None, only_good=False, pin_key=False, known_keys_list=None): config = (session and session.config) found_keys = {} ordered_keys = [] known_keys_list = known_keys_list or _mailpile_key_list( GnuPG(config or None).list_keys()) if origins: handlers = [ h for h in KEY_LOOKUP_HANDLERS if (h.NAME in origins) or ( h.NAME.lower() in origins) or (h.SHORTNAME in origins) ] else: handlers = KEY_LOOKUP_HANDLERS ungotten = get and get[:] or [] progress = [] # If the user has disabled "web content", only allow local requests if config and allowremote and config.prefs.web_content == 'off': if ('keylookup' in config.sys.debug or 'keytofu' in config.sys.debug): session.ui.debug( "Remote key lookups disabled by prefs.web_content.") allowremote = False for handler in handlers: if get and not ungotten: # We have all the keys! break try: h = handler(session, known_keys_list) if not allowremote and not h.LOCAL: continue if allowremote and config and config.prefs.web_content == 'anon': if (config.sys.proxy.protocol not in ('tor', 'tor-risky') or not h.PRIVACY_FRIENDLY): if ('keylookup' in config.sys.debug or 'keytofu' in config.sys.debug): session.ui.debug( "Origin %s disabled by prefs.web_content" % h.NAME) continue if found_keys and (not h.PRIVACY_FRIENDLY) and (not origins): # We only try the privacy-hostile methods if we haven't # found any keys (unless origins were specified). if not ungotten: continue progress.append(h.NAME) if event and config: ordered_keys.sort(key=lambda k: -k["score"]) event.message = _('Searching for encryption keys in: %s') % _( h.NAME) event.private_data = { "result": ordered_keys, "progress": progress, "runningsearch": h.NAME } config.event_log.log_event(event) # We allow for more time when importing keys timeout = h.TIMEOUT if ungotten: timeout *= 4 # h.lookup will remove found keys from the wanted list, # but we have to watch out for the effects of timeouts. wanted = ungotten[:] results = RunTimed(timeout, h.lookup, address, strict_email_match=strict_email_match, get=(wanted if (get is not None) else None)) ungotten[:] = wanted except KeyboardInterrupt: raise except: if config and config.sys.debug: traceback.print_exc() results = {} # FIXME: This merging of info about keys is probably misguided. for key_id, key_info in results.iteritems(): if key_id in found_keys: old_scores = found_keys[key_id].scores old_uids = found_keys[key_id].uids found_keys[key_id].update(key_info) found_keys[key_id].scores.update(old_scores) # Merge in the old UIDs uid_emails = [u.email for u in key_info.uids] for uid in old_uids: email = uid.email if email and email not in uid_emails: found_keys[key_id].uids.append(uid) else: found_keys[key_id] = key_info found_keys[key_id].origins.append(h.NAME) for key_id in found_keys.keys(): _normalize_key(session, found_keys[key_id]) _update_scores(session, key_id, found_keys[key_id], known_keys_list) # This updates and sorts ordered_keys in place. This will magically # also update the data on the viewable event, because Python. ordered_keys[:] = found_keys.values() ordered_keys.sort(key=lambda k: -k.score) if only_good: ordered_keys = [k for k in ordered_keys if k.score > 0] if get and vcard and ordered_keys: with vcard: vcard.pgp_key = ordered_keys[0].fingerprint vcard.pgp_key_pinned = 'true' if pin_key else 'false' vcard.save() ordered_keys[0].is_preferred = True ordered_keys[0].is_pinned = pin_key for k in ordered_keys[1:]: k.is_preferred = k.is_pinned = False if event and config: event.private_data = {"result": ordered_keys, "runningsearch": False} config.event_log.log_event(event) return ordered_keys