summaryrefslogtreecommitdiff
path: root/alot/mail/reply.py
diff options
context:
space:
mode:
Diffstat (limited to 'alot/mail/reply.py')
-rw-r--r--alot/mail/reply.py96
1 files changed, 95 insertions, 1 deletions
diff --git a/alot/mail/reply.py b/alot/mail/reply.py
index 0e7d897e..4a69ecee 100644
--- a/alot/mail/reply.py
+++ b/alot/mail/reply.py
@@ -2,7 +2,8 @@
# For further details see the COPYING file
import email.policy
-from email.utils import getaddresses
+from email.utils import getaddresses, parseaddr
+from enum import Enum, auto
import logging
from . import headers as HDR
@@ -69,6 +70,99 @@ def determine_account(headers, action = ACT_REPLY):
from_value = formataddr((realname, str(address)))
return from_value, account
+class ReplyMode(Enum):
+ AUTHOR = auto()
+ GROUP = auto()
+ LIST = auto()
+
+def _ensure_unique_address(recipients):
+ """
+ clean up a list of name,address pairs so that
+ no address appears multiple times.
+ """
+ res = dict()
+ for name, address in getaddresses(recipients):
+ res[address] = name
+ logging.debug(res)
+ urecipients = [formataddr((n, a)) for a, n in res.items()]
+ return sorted(urecipients)
+
+def _clear_my_address(my_account, value):
+ """return recipient header without the addresses in my_account
+
+ :param my_account: my account
+ :type my_account: :class:`Account`
+ :param value: a list of recipient or sender strings (with or without
+ real names as taken from email headers)
+ :type value: list(str)
+ :returns: a new, potentially shortend list
+ :rtype: list(str)
+ """
+ new_value = []
+ for name, address in getaddresses(value):
+ if not my_account.matches_address(address):
+ new_value.append(formataddr((name, address)))
+ return new_value
+
+def determine_recipients(message, account, mode):
+ # set To
+ sender = (message.headers.get(HDR.REPLY_TO) or
+ message.headers.get(HDR.FROM) or '')
+ sender_address = parseaddr(sender)[1]
+ cc = []
+
+ # check if reply is to self sent message
+ if account.matches_address(sender_address):
+ recipients = message.headers.get_all(HDR.TO)
+ emsg = 'Replying to own message, set recipients to: %s' \
+ % recipients
+ logging.debug(emsg)
+ else:
+ recipients = [sender]
+
+ if mode == ReplyMode.GROUP:
+ # make sure that our own address is not included
+ # if the message was self-sent, then our address is not included
+ MFT = message.headers.get_all(HDR.MAIL_FOLLOWUP_TO)
+ followupto = _clear_my_address(account, MFT)
+ if followupto and settings.get('honor_followup_to'):
+ logging.debug('honor followup to: %s', ', '.join(followupto))
+ recipients = followupto
+ # since Mail-Followup-To was set, ignore the Cc header
+ else:
+ if sender != message.headers.get(HDR.FROM):
+ recipients.append(message.headers.get(HDR.FROM))
+
+ # append To addresses if not replying to self sent message
+ if not account.matches_address(sender_address):
+ cleared = _clear_my_address(account,
+ message.headers.get_all(HDR.TO))
+ recipients.extend(cleared)
+
+ # copy cc for group-replies
+ if HDR.CC in message.headers:
+ cc = _clear_my_address(account, message.headers.get_all(HDR.CC))
+
+ to = ', '.join(_ensure_unique_address(recipients))
+ cc = ', '.join(cc)
+ logging.debug('reply to: %s', to)
+
+ if mode == ReplyMode.LIST:
+ # To choose the target of the reply --list
+ # Reply-To is standart reply target RFC 2822:, RFC 1036: 2.2.1
+ # X-BeenThere is needed by sourceforge ML also winehq
+ # X-Mailing-List is also standart and is used by git-send-mail
+ to = (message.headers.get(HDR.REPLY_TO) or
+ message.headers.get(HDR.X_BEEN_THERE) or
+ message.headers.get(HDR.X_MAILING_LIST))
+
+ # Some mail server (gmail) will not resend you own mail, so you
+ # have to deal with the one in sent
+ if to is None:
+ to = message.headers[HDR.TO]
+ logging.debug('mail list reply to: %s', to)
+
+ return to, cc
def subject(message):
subject = message.headers.get(HDR.SUBJECT) or ''