summaryrefslogtreecommitdiff
path: root/alot/mail/reply.py
blob: 0e7d897ee5a3ee87e5e7573163d2481e78d25fd5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# This file is released under the GNU GPL, version 3 or a later revision.
# For further details see the COPYING file

import email.policy
from   email.utils import getaddresses
import logging

from .                      import headers as HDR

from ..helper               import formataddr
from ..settings.const       import settings

ACT_REPLY  = 'reply'
ACT_BOUNCE = 'bounce'
ACT_FWD    = 'forward'

def determine_account(headers, action = ACT_REPLY):
    """
    Determine the local account to use for acting on a message with the given
    headers.

    :param headers: headers of the email to inspect
    :type headers: `db.message._MessageHeaders`
    :param action: intended use case: one of the ACT_* constants
    :type action: str
    :returns: 2-tuple of (from address, account)
    """
    # get accounts
    my_accounts = settings.get_accounts()
    assert my_accounts, 'no accounts set!'

    # extract list of addresses to check for my address
    # X-Envelope-To and Envelope-To are used to store the recipient address
    # if not included in other fields
    # Process the headers in order of importance: if a mail was sent with
    # account X, with account Y in e.g. CC or delivered-to, make sure that
    # account X is the one selected and not account Y.
    for candidate_header in settings.get("reply_account_header_priority"):
        candidate_addresses = getaddresses(headers.get_all(candidate_header))

        logging.debug('candidate addresses: %s', candidate_addresses)
        # pick the most important account that has an address in candidates
        # and use that account's realname and the address found here
        for account in my_accounts:
            for seen_name, seen_address in candidate_addresses:
                if account.matches_address(seen_address):
                    if settings.get(action + '_force_realname'):
                        realname = account.realname
                    else:
                        realname = seen_name
                    if settings.get(action + '_force_address'):
                        address = str(account.address)
                    else:
                        address = seen_address

                    logging.debug('using realname: "%s"', realname)
                    logging.debug('using address: %s', address)

                    from_value = formataddr((realname, address))
                    return from_value, account

    # revert to default account if nothing found
    account = my_accounts[0]
    realname = account.realname
    address = account.address
    logging.debug('using realname: "%s"', realname)
    logging.debug('using address: %s', address)

    from_value = formataddr((realname, str(address)))
    return from_value, account


def subject(message):
    subject = message.headers.get(HDR.SUBJECT) or ''
    reply_subject_hook = settings.get_hook('reply_subject')
    if reply_subject_hook:
        subject = reply_subject_hook(subject)
    else:
        rsp = settings.get('reply_subject_prefix')
        if not subject.lower().startswith(('re:', rsp.lower())):
            subject = rsp + subject

    return subject

def body_text(message, ui):
    name, address = message.get_author()
    timestamp     = message.date
    qf = settings.get_hook('reply_prefix')
    if qf:
        mail = email.message_from_bytes(message.as_bytes(),
                                        policy = email.policy.SMTP)
        quotestring = qf(name, address, timestamp,
                         message=mail, ui=ui, dbm=ui.dbman)
    else:
        quotestring = 'Quoting %s (%s)\n' % (name or address, timestamp)
    mailcontent = quotestring
    quotehook = settings.get_hook('text_quote')
    if quotehook:
        mailcontent += quotehook(message.get_body_text())
    else:
        quote_prefix = settings.get('quote_prefix')
        for line in message.get_body_text().splitlines():
            mailcontent += quote_prefix + line + '\n'

    return mailcontent

def references(message):
    old_references = message.headers.get(HDR.REFERENCES)
    references     = []

    if old_references:
        references = old_references.split()
        # limit to 16 references, including the one we add
        del references[1:-14]

    references.append('<%s>' % message.id)
    return ' '.join(references)

def mail_followup_to(recipients):
    if not settings.get('followup_to'):
        return None

    lists = settings.get('mailinglists')

    # check if any recipient address matches a known mailing list
    if any(addr in lists for n, addr in getaddresses(recipients)):
        followupto = ', '.join(recipients)
        logging.debug('mail followup to: %s', followupto)
        return followupto