# 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