def _msg_addresses(self, msg_info=None, addresses=[], no_from=False, no_to=False, no_cc=False): cids = set() for ai in addresses: eid = self.idx.EMAIL_IDS.get(ai.address.lower()) cids.add(b36(self.idx._add_email(ai.address, name=ai.fn, eid=eid))) if msg_info: if not no_to: to = [t for t in msg_info[MailIndex.MSG_TO].split(',') if t] cids |= set(to) if not no_cc: cc = [t for t in msg_info[MailIndex.MSG_CC].split(',') if t] cids |= set(cc) if not no_from: fe, fn = ExtractEmailAndName(msg_info[MailIndex.MSG_FROM]) if fe: eid = self.idx.EMAIL_IDS.get(fe.lower()) cids.add(b36(self.idx._add_email(fe, name=fn, eid=eid))) return sorted(list(cids))
def _index_addresses(self, cfg, terms, vcard_addresses): existing = dict([(k['address'].lower(), k) for k in vcard_addresses]) index = self._idx() # Figure out which tags are invisible so we can skip messages marked # with those tags. invisible = set([t._key for t in cfg.get_tags(flag_hides=True)]) # 1st, go through the last 1000 or so messages in the index and search # for matching senders or recipients, give medium priority. matches = {} addresses = [] for msg_idx in xrange(max(0, len(index.INDEX) - 2500), len(index.INDEX)): msg_info = index.get_msg_at_idx_pos(msg_idx) tags = set(msg_info[index.MSG_TAGS].split(',')) frm = msg_info[index.MSG_FROM] match = not (tags & invisible) if match: for term in terms: if term not in frm.lower(): match = False if match: matches[frm] = matches.get(frm, 0) + 1 if len(matches) > 1000: break # FIXME: 2nd, search the social graph for matches, give low priority. for frm in index.EMAILS: match = True for term in terms: if term not in frm.lower(): match = False if match: matches[frm] = matches.get(frm, 0) + 1 # Assign info & scores! for frm in matches: email, fn = ExtractEmailAndName(frm) boost = min(10, matches[frm]) for term in terms: boost += self._boost_rank(term, fn, email) if not email or '@' not in email: # FIXME: This may not be the right thing for alternate # message transports. pass elif email.lower() in existing: existing[email.lower()]['rank'] += min(20, boost) else: info = AddressInfo(email, fn) existing[email.lower()] = info addresses.append(info) return addresses
def _metadata(self, msg_info): import mailpile.urlmap nz = lambda l: [v for v in l if v] msg_ts = long(msg_info[MailIndex.MSG_DATE], 36) msg_date = datetime.datetime.fromtimestamp(msg_ts) um = mailpile.urlmap.UrlMap(self.session) fe, fn = ExtractEmailAndName(msg_info[MailIndex.MSG_FROM]) expl = { 'mid': msg_info[MailIndex.MSG_MID], 'id': msg_info[MailIndex.MSG_ID], 'from': { 'name': fn, 'email': fe, }, 'urls': { 'thread': um.url_thread(msg_info[MailIndex.MSG_MID]), }, 'timestamp': msg_ts, 'tag_tids': self._msg_tags(msg_info), 'to_eids': self._msg_people(msg_info, no_from=True), 'thread_mid': msg_info[MailIndex.MSG_CONV_MID], 'subject': msg_info[MailIndex.MSG_SUBJECT], 'body': { 'snippet': msg_info[MailIndex.MSG_SNIPPET], }, 'flags': { } } # Support rich snippets if expl['body']['snippet'].startswith('{'): try: expl['body'] = json.loads(expl['body']['snippet']) except ValueError: pass # Misc flags if [e for e in self.idx.config.profiles if e.email.lower() == fe.lower()]: expl['flags']['from_me'] = True; tag_types = [self.idx.config.get_tag(t).type for t in expl['tag_tids']] for t in self.TAG_TYPE_FLAG_MAP: if t in tag_types: expl['flags'][self.TAG_TYPE_FLAG_MAP[t]] = True; # FIXME: Is message signed or encrypted? # Extra behavior for editable messages if 'draft' in expl['flags']: if self.idx.config.is_editable_message(msg_info): expl['urls']['editing'] = um.url_edit(expl['mid']) else: del expl['flags']['draft'] return expl
def _index_addresses(self, cfg, terms, vcard_addresses): existing = dict([(k['address'].lower(), k) for k in vcard_addresses]) index = self._idx() # Figure out which tags are invisible so we can skip messages marked # with those tags. invisible = set([t._key for t in cfg.get_tags(flag_hides=True)]) # 1st, go through the last 1000 or so messages in the index and search # for matching senders or recipients, give medium priority. matches = {} addresses = [] for msg_idx in xrange(max(0, len(index.INDEX)-2500), len(index.INDEX)): msg_info = index.get_msg_at_idx_pos(msg_idx) tags = set(msg_info[index.MSG_TAGS].split(',')) frm = msg_info[index.MSG_FROM] match = not (tags & invisible) if match: for term in terms: if term not in frm.lower(): match = False if match: matches[frm] = matches.get(frm, 0) + 1 if len(matches) > 1000: break # FIXME: 2nd, search the social graph for matches, give low priority. for frm in index.EMAILS: match = True for term in terms: if term not in frm.lower(): match = False if match: matches[frm] = matches.get(frm, 0) + 1 # Assign info & scores! for frm in matches: email, fn = ExtractEmailAndName(frm) boost = min(10, matches[frm]) for term in terms: boost += self._boost_rank(term, fn, email) if not email or '@' not in email: # FIXME: This may not be the right thing for alternate # message transports. pass elif email.lower() in existing: existing[email.lower()]['rank'] += min(20, boost) else: info = AddressInfo(email, fn) existing[email.lower()] = info addresses.append(info) return addresses
def _metadata(self, msg_info): import mailpile.urlmap nz = lambda l: [v for v in l if v] msg_ts = long(msg_info[MailIndex.MSG_DATE], 36) msg_date = datetime.datetime.fromtimestamp(msg_ts) um = mailpile.urlmap.UrlMap(self.session) fe, fn = ExtractEmailAndName(msg_info[MailIndex.MSG_FROM]) fvcard = self.session.config.vcards.get_vcard(fe) expl = { "mid": msg_info[MailIndex.MSG_MID], "id": msg_info[MailIndex.MSG_ID], "from": AddressInfo(fe, fn, vcard=fvcard), "urls": {"thread": um.url_thread(msg_info[MailIndex.MSG_MID])}, "timestamp": msg_ts, "tag_tids": self._msg_tags(msg_info), "to_eids": self._msg_people(msg_info, no_from=True), "thread_mid": msg_info[MailIndex.MSG_CONV_MID], "subject": msg_info[MailIndex.MSG_SUBJECT], "body": {"snippet": msg_info[MailIndex.MSG_SNIPPET]}, "flags": {}, } # Support rich snippets if expl["body"]["snippet"].startswith("{"): try: expl["body"] = json.loads(expl["body"]["snippet"]) except ValueError: pass # Misc flags if [e for e in self.idx.config.profiles if e.email.lower() == fe.lower()]: expl["flags"]["from_me"] = True tag_types = [self.idx.config.get_tag(t).type for t in expl["tag_tids"]] for t in self.TAG_TYPE_FLAG_MAP: if t in tag_types: expl["flags"][self.TAG_TYPE_FLAG_MAP[t]] = True # FIXME: Is message signed or encrypted? # Extra behavior for editable messages if "draft" in expl["flags"]: if self.idx.config.is_editable_message(msg_info): expl["urls"]["editing"] = um.url_edit(expl["mid"]) else: del expl["flags"]["draft"] return expl
def _address(self, cid=None, e=None, n=None): if cid and not (e and n): e, n = ExtractEmailAndName(self.idx.EMAILS[int(cid, 36)]) vcard = self.session.config.vcards.get_vcard(e) if vcard and '@' in n: n = vcard.fn return AddressInfo(e, n, vcard=vcard)
def add_new_msg(self, msg_ptr, msg_id, msg_ts, msg_from, msg_to, msg_cc, msg_bytes, msg_subject, msg_snippet, tags): msg_idx_pos = len(self.INDEX) msg_mid = b36(msg_idx_pos) # FIXME: Refactor this to use edit_msg_info. msg_info = [ msg_mid, # Index ID msg_ptr, # Location on disk msg_id, # Message ID b36(msg_ts), # Date as UTC timstamp msg_from, # From: self.compact_to_list(msg_to or []), # To: self.compact_to_list(msg_cc or []), # Cc: b36(msg_bytes // 1024), # KB msg_subject, # Subject: msg_snippet, # Snippet ','.join(tags), # Initial tags '', # No replies for now msg_mid # Conversation ID ] email, fn = ExtractEmailAndName(msg_from) if email and fn: self.update_email(email, name=fn) self.set_msg_at_idx_pos(msg_idx_pos, msg_info) return msg_idx_pos, msg_info
def _add_from_messages(self, args, add_recipients): pairs, idx = [], self._idx() for email in [Email(idx, i) for i in self._choose_messages(args)]: msg_info = email.get_msg_info() pairs.append(ExtractEmailAndName(msg_info[idx.MSG_FROM])) if add_recipients: people = (idx.expand_to_list(msg_info) + idx.expand_to_list(msg_info, field=idx.MSG_CC)) for e in people: pair = ExtractEmailAndName(e) domain = pair[0].split('@')[-1] if (pair[0] not in self.IGNORED_EMAILS_AND_DOMAINS and domain not in self.IGNORED_EMAILS_AND_DOMAINS and 'noreply' not in pair[0]): pairs.append(pair) return [(p1, p2, '') for p1, p2 in pairs]
def _msg_addresses(self, msg_info, no_from=False, no_to=False, no_cc=False): if no_to: cids = set() else: to = [t for t in msg_info[MailIndex.MSG_TO].split(",") if t] cids = set(to) if not no_cc: cc = [t for t in msg_info[MailIndex.MSG_CC].split(",") if t] cids |= set(cc) if not no_from: fe, fn = ExtractEmailAndName(msg_info[MailIndex.MSG_FROM]) if fe: try: cids.add(b36(self.idx.EMAIL_IDS[fe.lower()])) except KeyError: cids.add(b36(self.idx._add_email(fe, name=fn))) return sorted(list(cids))
def _msg_addresses(self, msg_info, no_from=False, no_to=False, no_cc=False): if no_to: cids = set() else: to = [t for t in msg_info[MailIndex.MSG_TO].split(',') if t] cids = set(to) if not no_cc: cc = [t for t in msg_info[MailIndex.MSG_CC].split(',') if t] cids |= set(cc) if not no_from: fe, fn = ExtractEmailAndName(msg_info[MailIndex.MSG_FROM]) if fe: try: cids.add(b36(self.idx.EMAIL_IDS[fe.lower()])) except KeyError: cids.add(b36(self.idx._add_email(fe, name=fn))) return sorted(list(cids))
def _add_gpg_key(cls, idx, session, addr): fe, fn = ExtractEmailAndName(addr) vcard = session.config.vcards.get_vcard(fe) if vcard: keys = vcard.get_all('KEY') if keys: mime, fp = keys[0].value.split('data:')[1].split(',', 1) return "%s <%s#%s>" % (fn, fe, fp) return "%s <%s>" % (fn, fe)
def _msg_addresses(self, msg_info=None, addresses=[], no_from=False, no_to=False, no_cc=False): cids = set() for ai in addresses: eid = self.idx.EMAIL_IDS.get(ai.address.lower()) cids.add(b36(self.idx._add_email(ai.address, name=ai.fn, eid=eid))) if msg_info: if not no_to: to = [t for t in msg_info[MailIndex.MSG_TO].split(",") if t] cids |= set(to) if not no_cc: cc = [t for t in msg_info[MailIndex.MSG_CC].split(",") if t] cids |= set(cc) if not no_from: fe, fn = ExtractEmailAndName(msg_info[MailIndex.MSG_FROM]) if fe: eid = self.idx.EMAIL_IDS.get(fe.lower()) cids.add(b36(self.idx._add_email(fe, name=fn, eid=eid))) return sorted(list(cids))
def _index_addresses(self, cfg, terms, vcard_addresses, count, deadline): existing = dict([(k['address'].lower(), k) for k in vcard_addresses]) index = self._idx() # Figure out which tags are invisible so we can skip messages marked # with those tags. invisible = set([t._key for t in cfg.get_tags(flag_hides=True)]) matches = {} addresses = [] # 1st, search the social graph for matches, give low priority. for frm in index.EMAILS: frm_lower = frm.lower() match = True for term in terms: if term not in frm_lower: match = False break if match: matches[frm] = matches.get(frm, 0) + 3 if len(matches) > (count * 10): break elif len(matches) and time.time() > deadline: break # 2nd, go through at most the last 5000 messages in the index and # search for matching senders or recipients, give medium priority. # Note: This is more CPU intensive, so we do this last. if len(matches) < (count * 5): for msg_idx in xrange(max(0, len(index.INDEX)-5000), len(index.INDEX)): msg_info = index.get_msg_at_idx_pos(msg_idx) tags = set(msg_info[index.MSG_TAGS].split(',')) match = not (tags & invisible) if match: frm = msg_info[index.MSG_FROM] search = (frm + ' ' + msg_info[index.MSG_SUBJECT]).lower() for term in terms: if term not in search: match = False break if match: matches[frm] = matches.get(frm, 0) + 1 if len(matches) > (count * 5): break if len(matches) and time.time() > deadline: break # Assign info & scores! for frm in matches: email, fn = ExtractEmailAndName(frm) boost = min(10, matches[frm]) for term in terms: boost += self._boost_rank(term, fn, email) if not email or '@' not in email: # FIXME: This may not be the right thing for alternate # message transports. pass elif email.lower() in existing: existing[email.lower()]['rank'] += min(20, boost) else: info = AddressInfo(email, fn) existing[email.lower()] = info addresses.append(info) return addresses
def _metadata(self, msg_info): import mailpile.urlmap nz = lambda l: [v for v in l if v] msg_ts = long(msg_info[MailIndex.MSG_DATE], 36) msg_date = datetime.datetime.fromtimestamp(msg_ts) um = mailpile.urlmap.UrlMap(self.session) fe, fn = ExtractEmailAndName(msg_info[MailIndex.MSG_FROM]) expl = { 'mid': msg_info[MailIndex.MSG_MID], 'id': msg_info[MailIndex.MSG_ID], 'timestamp': msg_ts, 'from': { 'email': fe, 'fn': fn, 'aid': (self._msg_addresses(msg_info, no_to=True, no_cc=True) or [''])[0], }, 'to_aids': self._msg_addresses(msg_info, no_from=True, no_cc=True), 'cc_aids': self._msg_addresses(msg_info, no_from=True, no_to=True), 'msg_kb': int(msg_info[MailIndex.MSG_KB], 36), 'tag_tids': self._msg_tags(msg_info), 'thread_mid': msg_info[MailIndex.MSG_THREAD_MID], 'subject': msg_info[MailIndex.MSG_SUBJECT], 'body': { 'snippet': msg_info[MailIndex.MSG_BODY], }, 'flags': {}, 'crypto': {} } # Ephemeral messages do not have URLs if ':' not in msg_info[MailIndex.MSG_MID]: expl['urls'] = { 'thread': um.url_thread(msg_info[MailIndex.MSG_MID]), 'source': um.url_source(msg_info[MailIndex.MSG_MID]), } else: expl['flags']['ephemeral'] = True # Support rich snippets if expl['body']['snippet'].startswith('{'): try: expl['body'] = json.loads(expl['body']['snippet']) except ValueError: pass # Misc flags if [ e for e in self.idx.config.profiles if (e.email.lower() == fe.lower()) ]: expl['flags']['from_me'] = True tag_types = [self.idx.config.get_tag(t).type for t in expl['tag_tids']] for t in self.TAG_TYPE_FLAG_MAP: if t in tag_types: expl['flags'][self.TAG_TYPE_FLAG_MAP[t]] = True # Check tags for signs of encryption or signatures tag_slugs = [self.idx.config.get_tag(t).slug for t in expl['tag_tids']] for t in tag_slugs: if t.startswith('mp_sig'): expl['crypto']['signature'] = t[7:] elif t.startswith('mp_enc'): expl['crypto']['encryption'] = t[7:] # Extra behavior for editable messages if 'draft' in expl['flags']: if self.idx.config.is_editable_message(msg_info): expl['urls']['editing'] = um.url_edit(expl['mid']) else: del expl['flags']['draft'] return expl
def _add_from_messages(self, args): pairs, idx = [], self._idx() for email in [Email(idx, i) for i in self._choose_messages(args)]: pairs.append(ExtractEmailAndName(email.get_msg_info(idx.MSG_FROM))) return pairs
def _index_addresses(self, cfg, terms, vcard_addresses, count, deadline): existing = dict([(k['address'].lower(), k) for k in vcard_addresses]) index = self._idx() # Figure out which tags are invisible so we can skip messages marked # with those tags. invisible = set([t._key for t in cfg.get_tags(flag_hides=True)]) matches = {} addresses = [] # 1st, search the social graph for matches, give low priority. for frm in index.EMAILS: frm_lower = frm.lower() match = True for term in terms: if term not in frm_lower: match = False break if match: matches[frm] = matches.get(frm, 0) + 3 if len(matches) > (count * 10): break elif len(matches) and time.time() > deadline: break # 2nd, go through at most the last 5000 messages in the index and # search for matching senders or recipients, give medium priority. # Note: This is more CPU intensive, so we do this last. if len(matches) < (count * 5): for msg_idx in xrange(max(0, len(index.INDEX) - 5000), len(index.INDEX)): msg_info = index.get_msg_at_idx_pos(msg_idx) tags = set(msg_info[index.MSG_TAGS].split(',')) match = not (tags & invisible) if match: frm = msg_info[index.MSG_FROM] search = (frm + ' ' + msg_info[index.MSG_SUBJECT]).lower() for term in terms: if term not in search: match = False break if match: matches[frm] = matches.get(frm, 0) + 1 if len(matches) > (count * 5): break if len(matches) and time.time() > deadline: break # Assign info & scores! for frm in matches: email, fn = ExtractEmailAndName(frm) boost = min(10, matches[frm]) for term in terms: boost += self._boost_rank(term, fn, email) if not email or '@' not in email: # FIXME: This may not be the right thing for alternate # message transports. pass elif email.lower() in existing: existing[email.lower()]['rank'] += min(20, boost) else: info = AddressInfo(email, fn) existing[email.lower()] = info addresses.append(info) return addresses
def _address(self, cid): e, n = ExtractEmailAndName(self.idx.EMAILS[int(cid, 36)]) vcard = self.session.config.vcards.get_vcard(e) return AddressInfo(e, n, vcard=vcard)
def _metadata(self, msg_info): msg_mid = msg_info[MailIndex.MSG_MID] if '-' in msg_mid: # Ephemeral... msg_idx = None else: msg_idx = int(msg_mid, 36) cache = self.idx.CACHE.get(msg_idx, {}) if 'metadata' in cache: return cache['metadata'] nz = lambda l: [v for v in l if v] msg_ts = long(msg_info[MailIndex.MSG_DATE], 36) msg_date = datetime.datetime.fromtimestamp(msg_ts) fe, fn = ExtractEmailAndName(msg_info[MailIndex.MSG_FROM]) f_info = self._address(e=fe, n=fn) f_info['aid'] = (self._msg_addresses(msg_info, no_to=True, no_cc=True) or [''])[0] thread_mid = parent_mid = msg_info[MailIndex.MSG_THREAD_MID] if '/' in thread_mid: thread_mid, parent_mid = thread_mid.split('/') expl = { 'mid': msg_mid, 'id': msg_info[MailIndex.MSG_ID], 'timestamp': msg_ts, 'from': f_info, 'to_aids': self._msg_addresses(msg_info, no_from=True, no_cc=True), 'cc_aids': self._msg_addresses(msg_info, no_from=True, no_to=True), 'msg_kb': int(msg_info[MailIndex.MSG_KB], 36), 'tag_tids': sorted(self._msg_tags(msg_info)), 'thread_mid': thread_mid, 'parent_mid': parent_mid, 'subject': msg_info[MailIndex.MSG_SUBJECT], 'body': MailIndex.get_body(msg_info), 'flags': { }, 'crypto': { } } # Ephemeral messages do not have URLs if '-' in msg_info[MailIndex.MSG_MID]: expl['flags'].update({ 'ephemeral': True, 'draft': True, }) else: expl['urls'] = { 'thread': self.urlmap.url_thread(msg_info[MailIndex.MSG_MID]), 'source': self.urlmap.url_source(msg_info[MailIndex.MSG_MID]), } # Support rich snippets if expl['body']['snippet'].startswith('{'): try: expl['body'] = json.loads(expl['body']['snippet']) except ValueError: pass # Misc flags sender_vcard = self.idx.config.vcards.get_vcard(fe.lower()) if sender_vcard: if sender_vcard.kind == 'profile': expl['flags']['from_me'] = True tag_types = [self.idx.config.get_tag(t).type for t in expl['tag_tids']] for t in self.TAG_TYPE_FLAG_MAP: if t in tag_types: expl['flags'][self.TAG_TYPE_FLAG_MAP[t]] = True # Check tags for signs of encryption or signatures tag_slugs = [self.idx.config.get_tag(t).slug for t in expl['tag_tids']] for t in tag_slugs: if t.startswith('mp_sig'): expl['crypto']['signature'] = t[7:] elif t.startswith('mp_enc'): expl['crypto']['encryption'] = t[7:] # Extra behavior for editable messages if 'draft' in expl['flags']: if 'ephemeral' in expl['flags']: pass elif self.idx.config.is_editable_message(msg_info): expl['urls']['editing'] = self.urlmap.url_edit(expl['mid']) else: del expl['flags']['draft'] if msg_idx is not None: cache['metadata'] = expl self.idx.CACHE[msg_idx] = cache return expl
def _metadata(self, msg_info): msg_mid = msg_info[MailIndex.MSG_MID] if '-' in msg_mid: # Ephemeral... msg_idx = None else: msg_idx = int(msg_mid, 36) cache = self.idx.CACHE.get(msg_idx, {}) if 'metadata' in cache: return cache['metadata'] nz = lambda l: [v for v in l if v] msg_ts = long(msg_info[MailIndex.MSG_DATE], 36) msg_date = datetime.datetime.fromtimestamp(msg_ts) fe, fn = ExtractEmailAndName(msg_info[MailIndex.MSG_FROM]) f_info = self._address(e=fe, n=fn) f_info['aid'] = (self._msg_addresses(msg_info, no_to=True, no_cc=True) or [''])[0] thread_mid = parent_mid = msg_info[MailIndex.MSG_THREAD_MID] if '/' in thread_mid: thread_mid, parent_mid = thread_mid.split('/') expl = { 'mid': msg_mid, 'id': msg_info[MailIndex.MSG_ID], 'timestamp': msg_ts, 'from': f_info, 'to_aids': self._msg_addresses(msg_info, no_from=True, no_cc=True), 'cc_aids': self._msg_addresses(msg_info, no_from=True, no_to=True), 'msg_kb': int(msg_info[MailIndex.MSG_KB], 36), 'tag_tids': sorted(self._msg_tags(msg_info)), 'thread_mid': thread_mid, 'parent_mid': parent_mid, 'subject': msg_info[MailIndex.MSG_SUBJECT], 'body': MailIndex.get_body(msg_info), 'flags': {}, 'crypto': {} } # Ephemeral messages do not have URLs if '-' in msg_info[MailIndex.MSG_MID]: expl['flags'].update({ 'ephemeral': True, 'draft': True, }) else: expl['urls'] = { 'thread': self.urlmap.url_thread(msg_info[MailIndex.MSG_MID]), 'source': self.urlmap.url_source(msg_info[MailIndex.MSG_MID]), } # Support rich snippets if expl['body']['snippet'].startswith('{'): try: expl['body'] = json.loads(expl['body']['snippet']) except ValueError: pass # Misc flags sender_vcard = self.idx.config.vcards.get_vcard(fe.lower()) if sender_vcard: if sender_vcard.kind == 'profile': expl['flags']['from_me'] = True tag_types = [self.idx.config.get_tag(t).type for t in expl['tag_tids']] for t in self.TAG_TYPE_FLAG_MAP: if t in tag_types: expl['flags'][self.TAG_TYPE_FLAG_MAP[t]] = True # Check tags for signs of encryption or signatures tag_slugs = [self.idx.config.get_tag(t).slug for t in expl['tag_tids']] for t in tag_slugs: if t.startswith('mp_sig'): expl['crypto']['signature'] = t[7:] elif t.startswith('mp_enc'): expl['crypto']['encryption'] = t[7:] # Extra behavior for editable messages if 'draft' in expl['flags']: if 'ephemeral' in expl['flags']: pass elif self.idx.config.is_editable_message(msg_info): expl['urls']['editing'] = self.urlmap.url_edit(expl['mid']) else: del expl['flags']['draft'] if msg_idx is not None: cache['metadata'] = expl self.idx.CACHE[msg_idx] = cache return expl
def _metadata(self, msg_info): import mailpile.urlmap nz = lambda l: [v for v in l if v] msg_ts = long(msg_info[MailIndex.MSG_DATE], 36) msg_date = datetime.datetime.fromtimestamp(msg_ts) um = mailpile.urlmap.UrlMap(self.session) fe, fn = ExtractEmailAndName(msg_info[MailIndex.MSG_FROM]) expl = { 'mid': msg_info[MailIndex.MSG_MID], 'id': msg_info[MailIndex.MSG_ID], 'timestamp': msg_ts, 'from': { 'email': fe, 'fn': fn, 'aid': (self._msg_addresses(msg_info, no_to=True, no_cc=True) or [''])[0], }, 'to_aids': self._msg_addresses(msg_info, no_from=True, no_cc=True), 'cc_aids': self._msg_addresses(msg_info, no_from=True, no_to=True), 'msg_kb': int(msg_info[MailIndex.MSG_KB], 36), 'tag_tids': self._msg_tags(msg_info), 'thread_mid': msg_info[MailIndex.MSG_THREAD_MID], 'subject': msg_info[MailIndex.MSG_SUBJECT], 'body': { 'snippet': msg_info[MailIndex.MSG_BODY], }, 'flags': { }, 'crypto': { } } # Ephemeral messages do not have URLs if ':' not in msg_info[MailIndex.MSG_MID]: expl['urls'] = { 'thread': um.url_thread(msg_info[MailIndex.MSG_MID]), 'source': um.url_source(msg_info[MailIndex.MSG_MID]), } else: expl['flags']['ephemeral'] = True # Support rich snippets if expl['body']['snippet'].startswith('{'): try: expl['body'] = json.loads(expl['body']['snippet']) except ValueError: pass # Misc flags if [e for e in self.idx.config.profiles if (e.email.lower() == fe.lower())]: expl['flags']['from_me'] = True tag_types = [self.idx.config.get_tag(t).type for t in expl['tag_tids']] for t in self.TAG_TYPE_FLAG_MAP: if t in tag_types: expl['flags'][self.TAG_TYPE_FLAG_MAP[t]] = True # Check tags for signs of encryption or signatures tag_slugs = [self.idx.config.get_tag(t).slug for t in expl['tag_tids']] for t in tag_slugs: if t.startswith('mp_sig'): expl['crypto']['signature'] = t[7:] elif t.startswith('mp_enc'): expl['crypto']['encryption'] = t[7:] # Extra behavior for editable messages if 'draft' in expl['flags']: if self.idx.config.is_editable_message(msg_info): expl['urls']['editing'] = um.url_edit(expl['mid']) else: del expl['flags']['draft'] return expl
def _metadata(self, msg_info): msg_mid = msg_info[MailIndex.MSG_MID] if "-" in msg_mid: # Ephemeral... msg_idx = None else: msg_idx = int(msg_mid, 36) cache = self.idx.CACHE.get(msg_idx, {}) if "metadata" in cache: return cache["metadata"] nz = lambda l: [v for v in l if v] msg_ts = long(msg_info[MailIndex.MSG_DATE], 36) msg_date = datetime.datetime.fromtimestamp(msg_ts) fe, fn = ExtractEmailAndName(msg_info[MailIndex.MSG_FROM]) f_info = self._address(e=fe, n=fn) f_info["aid"] = (self._msg_addresses(msg_info, no_to=True, no_cc=True) or [""])[0] thread_mid = parent_mid = msg_info[MailIndex.MSG_THREAD_MID] if "/" in thread_mid: thread_mid, parent_mid = thread_mid.split("/") expl = { "mid": msg_mid, "id": msg_info[MailIndex.MSG_ID], "timestamp": msg_ts, "from": f_info, "to_aids": self._msg_addresses(msg_info, no_from=True, no_cc=True), "cc_aids": self._msg_addresses(msg_info, no_from=True, no_to=True), "msg_kb": int(msg_info[MailIndex.MSG_KB], 36), "tag_tids": sorted(self._msg_tags(msg_info)), "thread_mid": thread_mid, "parent_mid": parent_mid, "subject": msg_info[MailIndex.MSG_SUBJECT], "body": MailIndex.get_body(msg_info), "flags": {}, "crypto": {}, } # Ephemeral messages do not have URLs if "-" in msg_info[MailIndex.MSG_MID]: expl["flags"].update({"ephemeral": True, "draft": True}) else: expl["urls"] = { "thread": self.urlmap.url_thread(msg_info[MailIndex.MSG_MID]), "source": self.urlmap.url_source(msg_info[MailIndex.MSG_MID]), } # Support rich snippets if expl["body"]["snippet"].startswith("{"): try: expl["body"] = json.loads(expl["body"]["snippet"]) except ValueError: pass # Misc flags sender_vcard = self.idx.config.vcards.get_vcard(fe.lower()) if sender_vcard: if sender_vcard.kind == "profile": expl["flags"]["from_me"] = True tag_types = [self.idx.config.get_tag(t).type for t in expl["tag_tids"]] for t in self.TAG_TYPE_FLAG_MAP: if t in tag_types: expl["flags"][self.TAG_TYPE_FLAG_MAP[t]] = True # Check tags for signs of encryption or signatures tag_slugs = [self.idx.config.get_tag(t).slug for t in expl["tag_tids"]] for t in tag_slugs: if t.startswith("mp_sig"): expl["crypto"]["signature"] = t[7:] elif t.startswith("mp_enc"): expl["crypto"]["encryption"] = t[7:] # Extra behavior for editable messages if "draft" in expl["flags"]: if "ephemeral" in expl["flags"]: pass elif self.idx.config.is_editable_message(msg_info): expl["urls"]["editing"] = self.urlmap.url_edit(expl["mid"]) else: del expl["flags"]["draft"] if msg_idx is not None: cache["metadata"] = expl self.idx.CACHE[msg_idx] = cache return expl
def parse(rp, retvals): signature_info = rp.signature_info encryption_info = rp.encryption_info from mailpile.mailutils import ExtractEmailAndName # First pass, set some initial state. for data in retvals[1]["status"]: keyword = data[0] if keyword == "DECRYPTION_FAILED": missing = [ x[1] for x in retvals[1]["status"] if x[0] == "NO_SECKEY" ] if missing: encryption_info["status"] = "missingkey" encryption_info["missing_keys"] = missing else: encryption_info["status"] = "error" elif keyword == "DECRYPTION_OKAY": encryption_info["status"] = "decrypted" rp.plaintext = retvals[1]["stdout"][0] elif keyword == "ENC_TO": keylist = encryption_info.get("have_keys", []) if data[0] not in keylist: keylist.append(data[1]) encryption_info["have_keys"] = keylist elif signature_info["status"] == "none": # Only one of these will ever be emitted per key, use # this to set initial state. We may end up revising # the status depending on more info later. if keyword in ("GOODSIG", "BADSIG"): email, fn = ExtractEmailAndName(" ".join( data[2:]).decode('utf-8')) signature_info["name"] = fn signature_info["email"] = email signature_info["status"] = ((keyword == "GOODSIG") and "unverified" or "invalid") elif keyword == "ERRSIG": signature_info["status"] = "error" signature_info["keyinfo"] = data[1] signature_info["timestamp"] = int(data[5]) # Second pass, this may update/mutate the state set above for data in retvals[1]["status"]: keyword = data[0] if keyword == "NO_SECKEY": if "missing_keys" not in encryption_info: encryption_info["missing_keys"] = [data[1]] else: encryption_info["missing_keys"].append(data[1]) try: encryption_info["have_keys"].remove(data[1]) except (KeyError, ValueError): pass elif keyword == "VALIDSIG": # FIXME: Determine trust level, between new, unverified, # verified, untrusted. signature_info["keyinfo"] = data[1] signature_info["timestamp"] = int(data[3]) elif keyword in ("EXPKEYSIG", "REVKEYSIG"): email, fn = ExtractEmailAndName(" ".join( data[2:]).decode('utf-8')) signature_info["name"] = fn signature_info["email"] = email signature_info["status"] = ((keyword == "EXPKEYSIG") and "expired" or "revoked") # FIXME: This appears to be spammy. Is my key borked, or # is GnuPG being stupid? # # elif keyword == "KEYEXPIRED": # Ignoring: SIGEXPIRED # signature_info["status"] = "expired" elif keyword == "KEYREVOKED": signature_info["status"] = "revoked" elif keyword == "NO_PUBKEY": signature_info["status"] = "unknown" elif keyword in ["TRUST_ULTIMATE", "TRUST_FULLY"]: if signature_info["status"] == "unverified": signature_info["status"] = "verified" return rp
def _metadata(self, msg_info): import mailpile.urlmap nz = lambda l: [v for v in l if v] msg_ts = long(msg_info[MailIndex.MSG_DATE], 36) msg_date = datetime.datetime.fromtimestamp(msg_ts) fe, fn = ExtractEmailAndName(msg_info[MailIndex.MSG_FROM]) expl = { "mid": msg_info[MailIndex.MSG_MID], "id": msg_info[MailIndex.MSG_ID], "timestamp": msg_ts, "from": {"email": fe, "fn": fn, "aid": (self._msg_addresses(msg_info, no_to=True, no_cc=True) or [""])[0]}, "to_aids": self._msg_addresses(msg_info, no_from=True, no_cc=True), "cc_aids": self._msg_addresses(msg_info, no_from=True, no_to=True), "msg_kb": int(msg_info[MailIndex.MSG_KB], 36), "tag_tids": self._msg_tags(msg_info), "thread_mid": msg_info[MailIndex.MSG_THREAD_MID], "subject": msg_info[MailIndex.MSG_SUBJECT], "body": {"snippet": msg_info[MailIndex.MSG_BODY]}, "flags": {}, "crypto": {}, } # Ephemeral messages do not have URLs if ":" not in msg_info[MailIndex.MSG_MID]: expl["urls"] = { "thread": self.urlmap.url_thread(msg_info[MailIndex.MSG_MID]), "source": self.urlmap.url_source(msg_info[MailIndex.MSG_MID]), } else: expl["flags"]["ephemeral"] = True # Support rich snippets if expl["body"]["snippet"].startswith("{"): try: expl["body"] = json.loads(expl["body"]["snippet"]) except ValueError: pass # Misc flags if [e for e in self.idx.config.profiles if (e.email.lower() == fe.lower())]: expl["flags"]["from_me"] = True tag_types = [self.idx.config.get_tag(t).type for t in expl["tag_tids"]] for t in self.TAG_TYPE_FLAG_MAP: if t in tag_types: expl["flags"][self.TAG_TYPE_FLAG_MAP[t]] = True # Check tags for signs of encryption or signatures tag_slugs = [self.idx.config.get_tag(t).slug for t in expl["tag_tids"]] for t in tag_slugs: if t.startswith("mp_sig"): expl["crypto"]["signature"] = t[7:] elif t.startswith("mp_enc"): expl["crypto"]["encryption"] = t[7:] # Extra behavior for editable messages if "draft" in expl["flags"]: if self.idx.config.is_editable_message(msg_info): expl["urls"]["editing"] = self.urlmap.url_edit(expl["mid"]) else: del expl["flags"]["draft"] return expl