Beispiel #1
0
    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))
Beispiel #2
0
    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
Beispiel #3
0
    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
Beispiel #4
0
    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
Beispiel #5
0
    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
Beispiel #6
0
 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)
Beispiel #7
0
 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
Beispiel #8
0
 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]
Beispiel #9
0
 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))
Beispiel #10
0
 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))
Beispiel #11
0
 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)
Beispiel #12
0
    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))
Beispiel #13
0
    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
Beispiel #14
0
    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
Beispiel #15
0
 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
Beispiel #16
0
    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
Beispiel #17
0
 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)
Beispiel #18
0
    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
Beispiel #19
0
    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
Beispiel #20
0
    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
Beispiel #21
0
    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
Beispiel #22
0
        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
Beispiel #23
0
    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