aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@linutronix.de>2023-06-09 23:17:26 +0200
committerThomas Gleixner <tglx@linutronix.de>2023-06-20 23:43:23 +0200
commitdc68e97937bf589b31bde1b083c6530c94ec14b4 (patch)
tree55d6157dac314b2142ef4bfc1c494e7bf8b3f45d
parent424944ad36e9925a9d3856b7054ca7fcc26f1ea6 (diff)
downloadremail-dc68e97937bf589b31bde1b083c6530c94ec14b4.tar.gz
remail: Make From header mangling less convoluted
Due to the requirement to reencrypt the incoming mail, remail must rewrite the mail headers and create a new email with the 'From:' header being the list address. So remail implemented a scheme to mangle the original senders name into the reencrypted mails 'From:' header. That fell flat on its nose when there was an incoming mail which had no name part and just consisted of the actual email address. Instead of differentiating between these cases the authors sleep deprived and grump laden brain decided to implement 'From:' mangling in the way it is now. It converts the original sender mail address '[Name] <mailname@sender.domain>' to: 'list-name for mailname_at_sender.domain' <list-name@list.domain> This is hard to read and follow. Use the common format: 'Name via list-name' <list-name@list.domain> Except for the case where the original 'From:' header is a plain email address without a name. This will mangle it to: 'mailname at sender.domain via list-name' <list-name@list.domain> The 'via list-name' add on is technically not required but is useful to visually differentiate the name in auto-completion. Suggested-by: Linus Torvalds <torvalds@linux-foundation.org> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
-rw-r--r--remail/maillist.py55
1 files changed, 44 insertions, 11 deletions
diff --git a/remail/maillist.py b/remail/maillist.py
index b399ff7..c87a986 100644
--- a/remail/maillist.py
+++ b/remail/maillist.py
@@ -14,7 +14,7 @@ from remail.gpg import gpg_crypt, RemailGPGException
from remail.tracking import account_tracking
from remail.config import accounts_config, gpg_config, smime_config
-from email.utils import make_msgid, formatdate, getaddresses
+from email.utils import make_msgid, formatdate, getaddresses, parseaddr
from email.policy import EmailPolicy
from flufl.bounce import all_failures
@@ -22,6 +22,10 @@ from ruamel.yaml import YAML
import mailbox
import os
+import re
+
+mustquote = re.compile(r'[][\\()<>@,:;".]')
+escapedquote = re.compile(r'[\\"]')
class maillist(object):
'''
@@ -228,24 +232,52 @@ class maillist(object):
dest.toadmins = True
dest.accounts = self.config.admins
- def mangle_from(self, msg, mfrom):
+ def mangle_from(self, msg, name, mfrom):
'''
- Build 'From' string so the original 'From' is 'visible':
- From: $LISTNAME for $ORIGINAL_FROM <$LISTADDRESS>
+ If @name is non-empty then replace the 'From:' header with:
+
+ '@name via list-name' <list-name@list.domain>
- If $ORIGINAL_FROM does not contain a name, mangle the email
- address by replacing @ with _at_
+ If @name is empty mangle the email address @mfrom by replacing
+ '@' with at and setting the 'From:' header to:
+
+ '@mfrom via list-name' <list-name@list.domain>
'''
- mfrom = mfrom.replace('@','_at_')
- return '%s for %s <%s>' % (self.config.name, mfrom,
- self.config.listaddrs.post)
+ if not len(name):
+ name = mfrom.replace('@',' at ')
+
+ # Compose the display name
+ name = '%s via %s' %(name, self.config.name)
+
+ # Check whether it must be quoted
+ #
+ # email.utils.formataddr() fails to do so for non-ascii display
+ # names which causes some email-clients to display completely
+ # nonsensical names. Especially for the typical big corporate
+ # "name, surname" patterns as the resulting header entry becomes
+ #
+ # From: =?utf-8?q?NAME?=, =?utf-8?q?SURNAME?= <n@doma.in>
+ # which some clients render as:
+ # NAME@$SOMEMADEUPDOMAIN, SURNAME <n@doma.in>
+ #
+ # instead of
+ # From: =?utf-8?q?NAME=2C_SURNAME?= <n.doma.in>
+ # which they correctly render as:
+ # "NAME, SURNAME" <n@doma.in>
+ #
+ if mustquote.search(name):
+ name = escapedquote.sub(r'\\\g<0>', name)
+ name = '"%s"' %name
+
+ # The result is correcly handled by msg['From'] = result
+ return '%s <%s>' %(name, self.config.listaddrs.post)
def do_process_mail(self, msg, dest):
msgid = msg.get('Message-Id', '<No ID>')
msgto = msg.get('To')
origfrom = msg.get('From')
- msgfrom = get_raw_email_addr(origfrom)
+ name, msgfrom = parseaddr(origfrom)
sinfo = sender_info(msg)
# Archive the incoming mail
@@ -264,7 +296,8 @@ class maillist(object):
self.archive_mail(msg_plain, admin=dest.toadmin)
- mfrom = self.mangle_from(msg, msgfrom)
+ mfrom = self.mangle_from(msg, name, msgfrom)
+
# Save sender information in the outgoing message?
if self.config.attach_sender_info:
# Only do so for non-subscribers