示例#1
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)
示例#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
示例#3
0
 def email_at(i):
     for j in range(0, len(g)):
         if g[j][:1] == '(' and g[j][-1:] == ')':
             g[j] = g[j][1:-1]
     rest = ' '.join([
         g[j] for j in range(0, len(g)) if (j != i) and g[j]
     ]).replace(' ,', ',').replace(' ;', ';')
     email, keys = g[i], None
     if '#' in email[email.index('@'):]:
         email, key = email.rsplit('#', 1)
         keys = [{'fingerprint': key}]
     return AddressInfo(email, rest.strip(), keys=keys)
示例#4
0
    def _vcard_addresses(self, cfg, terms):
        addresses = {}
        for vcard in cfg.vcards.find_vcards(terms, kinds='individual'):
            fn = vcard.get('fn')
            for email_vcl in vcard.get_all('email'):
                info = addresses.get(email_vcl.value) or {}
                info.update(AddressInfo(email_vcl.value, fn.value,
                                        vcard=vcard))
                addresses[email_vcl.value] = info
                for term in terms:
                    info['rank'] += self._boost_rank(term, fn.value,
                                                     email_vcl.value)

        return addresses.values()
示例#5
0
    def _vcard_addresses(self, cfg, terms, ignored_count, deadline):
        addresses = {}
        for vcard in cfg.vcards.find_vcards(terms,
                                            kinds=VCardStore.KINDS_PEOPLE):
            fn = vcard.get('fn')
            for email_vcl in vcard.get_all('email'):
                info = addresses.get(email_vcl.value) or {}
                info.update(AddressInfo(email_vcl.value, fn.value,
                                        vcard=vcard))
                addresses[email_vcl.value] = info
                for term in terms:
                    info['rank'] += self._boost_rank(term, fn.value,
                                                     email_vcl.value)
            if len(addresses) and time.time() > deadline:
                break

        return addresses.values()
示例#6
0
def _normalize_key(session, key_info):
    """Make sure expected attributes are on all keys"""
    if not key_info.get("uids"):
        key_info["uids"] = [{"name": "", "email": "", "comment": ""}]
    if key_info.get("vcards") is None:
        key_info["vcards"] = {}
    for uid in key_info["uids"]:
        uid["name"] = uid.get("name", _('Anonymous'))
        uid["email"] = e = uid.get("email", '')
        uid["comment"] = uid.get("comment", '')
        if e and e not in key_info["vcards"]:
            vcard = session.config.vcards.get_vcard(e)
            if vcard:
                ai = AddressInfo(e, uid["name"], vcard=vcard)
                key_info["vcards"][e] = ai
    for key, default in [('on_keychain', False), ('keysize', '0'),
                         ('keytype_name', 'unknown'),
                         ('created', '1970-01-01 00:00:00'),
                         ('fingerprint', 'FINGERPRINT_IS_MISSING'),
                         ('validity', '')]:
        if key not in key_info:
            key_info[key] = default
示例#7
0
def _normalize_key(session, key_info):
    """Make sure expected attributes are on all keys"""
    if not key_info.uids:
        key_info.uids.append(KeyUID())
    for uid in key_info.uids:
        uid.name = uid.name or _('Anonymous')
        e = uid.email
        if e and e not in key_info.vcards:
            vcard = session.config.vcards.get_vcard(e)
            if vcard:
                ai = AddressInfo(e, uid.name, vcard=vcard)
                key_info.vcards[e] = ai
                if vcard.pgp_key == key_info.fingerprint:
                    key_info.is_preferred = True
                    if vcard.pgp_key_pinned:
                        key_info.is_pinned = True
    key_info.origins = list(set(key_info.origins))
    if not key_info.is_pinned:
        key_info.is_pinned = False
    if not key_info.is_preferred:
        key_info.is_preferred = False
    if not key_info.is_autocrypt:
        key_info.is_autocrypt = False
示例#8
0
文件: contacts.py 项目: jean/Mailpile
    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(4, 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'] += boost
            else:
                info = AddressInfo(email, fn)
                info['rank'] = info.get('rank', 0) + boost
                existing[email.lower()] = info
                addresses.append(info)

        return addresses
示例#9
0
    def crypto_policy(cls, session, idx, emails):
        config = session.config
        for i in range(0, len(emails)):
            if '<' in emails[i]:
                emails[i] = (emails[i].split('<', 1)[1]
                                      .split('>', 1)[0]
                                      .rsplit('#', 1)[0].strip())
        policies = [cls._vcard_policy(config, e) for e in set(emails)]
        default = [(v, k, e, p, f) for v, k, e, p, f in policies
                   if k == 'profile']
        default = default[0] if default else (None, None, None,
                                              'best-effort', 'send_keys')
        cpolicy = default[-2]
        cformat = default[-1]

        # Try and merge all the user policies into one. This may lead
        # to conflicts which cannot be resolved.
        policy = cpolicy
        reason = None
        for vc, kind, email, cpol, cfmt in policies:
            if cpol and cpol not in ('default', 'best-effort'):
                if policy in ('default', 'best-effort'):
                    policy = cpol
                elif policy != cpol:
                    reason = _('Recipients have conflicting encryption '
                               'policies.')
                    policy = 'conflict'
        if policy == 'default':
            policy = 'best-effort'
        if not reason:
            reason = _('The encryption policy for these recipients is: %s'
                       ) % policy

        # If we don't have a key ourselves, that limits our options...
        if default[0]:
            if default[0].get_all('KEY'):
                can_sign = True
                can_encrypt = None  # not False and not True
            else:
                can_sign = False
                can_encrypt = False
                if policy in ('sign', 'sign-encrypt', 'encrypt'):
                    reason = _('This account does not have an encryption key.')
                    policy = 'conflict'
                elif policy in ('default', 'best-effort'):
                    reason = _('This account does not have an encryption key.')
                    policy = 'none'
        else:
            can_sign = False
            can_encrypt = False
        if can_encrypt is not False:
            can_encrypt = len([1 for v, k, e, p, f in policies
                               if not v or not v.get_all('KEY')]) == 0
        if not can_encrypt and 'encrypt' in policy:
            policy = 'conflict'
            if 'encrypt' in cpolicy:
                reason = _('Your policy is to always encrypt, '
                           'but we do not have keys for everyone!')
            else:
                reason = _('Some recipients require encryption, '
                           'but we do not have keys for everyone!')

        # If the policy is "best-effort", then we would like to sign and
        # encrypt if possible/safe. The bar for signing is lower.
        if policy == 'best-effort':
            should_encrypt = can_encrypt
            if should_encrypt:
                for v, k, e, p, f in policies:
                    if k and k == 'profile':
                        pass
                    elif cls._encryption_ratio(session, idx, e) < 0.8:
                        should_encrypt = False
                        break
                if should_encrypt:
                    policy = 'sign-encrypt'
                    reason = _('We have keys for everyone!')
            if can_sign and not should_encrypt:
                # FIXME: Should we check if anyone is using a lame MUA?
                policy = 'sign'
                if can_encrypt:
                    reason = _('Will not encrypt because '
                               'historic data is insufficient.')
                else:
                    reason = _('Cannot encrypt because we '
                               'do not have keys for all recipients.')

        if 'send_keys' in cformat:
            send_keys = cls.ShouldAttachKey(
                config,
                vcards=[p[0] for p in policies],
                emails=[p[2] for p in policies if not p[0]])
        else:
            send_keys = False

        return {
          'reason': reason,
          'can-sign': can_sign,
          'can-encrypt': can_encrypt,
          'crypto-policy': policy,
          'crypto-format': cformat,
          'send-keys': send_keys,
          'addresses': dict([(e, AddressInfo(e, vc.fn if vc else e, vcard=vc))
                             for vc, k, e, p, f in policies if vc])
        }
示例#10
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
示例#11
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)
示例#12
0
    def _create_from_to_cc(cls, idx, session, trees):
        config = session.config
        ahp = AddressHeaderParser()
        ref_from, ref_to, ref_cc = [], [], []
        result = {'from': '', 'to': [], 'cc': []}

        def merge_contact(ai):
            vcard = session.config.vcards.get_vcard(ai.address)
            if vcard:
                ai.merge_vcard(vcard)
            return ai

        # Parse the headers, so we know what we're working with. We prune
        # some of the duplicates at this stage.
        for addrs in [t['addresses'] for t in trees]:
            alist = []
            for dst, addresses in ((ref_from, addrs.get('reply-to')
                                    or addrs.get('from', [])),
                                   (ref_to, addrs.get('to', [])),
                                   (ref_cc, addrs.get('cc', []))):
                alist += [d.address for d in dst]
                dst.extend([a for a in addresses if a.address not in alist])

        # 1st, choose a from address. We'll use the system default if
        # nothing is found, but hopefully we'll find an address we
        # recognize in one of the headers.
        from_address = (session.config.prefs.default_email
                        or session.config.profiles[0].email)
        profile_emails = [p.email for p in session.config.profiles if p.email]
        for src in (ref_from, ref_to, ref_cc):
            matches = [s for s in src if s.address in profile_emails]
            if matches:
                from_address = matches[0].address
                break
        result['from'] = ahp.normalized(addresses=[
            AddressInfo(p.email, p.name) for p in session.config.profiles
            if p.email == from_address
        ],
                                        force_name=True)

        def addresses(addrs, exclude=[]):
            alist = [from_address] + [a.address for a in exclude]
            return ahp.normalized_addresses(addresses=[
                merge_contact(a) for a in addrs
                if a.address not in alist and not a.address.startswith(
                    'noreply@') and '@noreply' not in a.address
            ],
                                            with_keys=True,
                                            force_name=True)

        # If only replying to messages sent from chosen from, then this is
        # a follow-up or clarification, so just use the same headers.
        if len([e for e in ref_from
                if e.address == from_address]) == len(ref_from):
            if ref_to:
                result['to'] = addresses(ref_to)
            if ref_cc:
                result['cc'] = addresses(ref_cc)

        # Else, if replying to other people:
        #   - Construct To from the From lines, excluding own from
        #   - Construct Cc from the To and CC lines, except new To/From
        else:
            result['to'] = addresses(ref_from)
            result['cc'] = addresses(ref_to + ref_cc, exclude=ref_from)

        return result