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>
This commit is contained in:
Thomas Gleixner 2023-06-09 23:17:26 +02:00
parent 424944ad36
commit dc68e97937

View file

@ -14,7 +14,7 @@ from remail.gpg import gpg_crypt, RemailGPGException
from remail.tracking import account_tracking from remail.tracking import account_tracking
from remail.config import accounts_config, gpg_config, smime_config 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 email.policy import EmailPolicy
from flufl.bounce import all_failures from flufl.bounce import all_failures
@ -22,6 +22,10 @@ from ruamel.yaml import YAML
import mailbox import mailbox
import os import os
import re
mustquote = re.compile(r'[][\\()<>@,:;".]')
escapedquote = re.compile(r'[\\"]')
class maillist(object): class maillist(object):
''' '''
@ -228,24 +232,52 @@ class maillist(object):
dest.toadmins = True dest.toadmins = True
dest.accounts = self.config.admins 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': If @name is non-empty then replace the 'From:' header with:
From: $LISTNAME for $ORIGINAL_FROM <$LISTADDRESS>
If $ORIGINAL_FROM does not contain a name, mangle the email '@name via list-name' <list-name@list.domain>
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_') if not len(name):
return '%s for %s <%s>' % (self.config.name, mfrom, name = mfrom.replace('@',' at ')
self.config.listaddrs.post)
# 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): def do_process_mail(self, msg, dest):
msgid = msg.get('Message-Id', '<No ID>') msgid = msg.get('Message-Id', '<No ID>')
msgto = msg.get('To') msgto = msg.get('To')
origfrom = msg.get('From') origfrom = msg.get('From')
msgfrom = get_raw_email_addr(origfrom) name, msgfrom = parseaddr(origfrom)
sinfo = sender_info(msg) sinfo = sender_info(msg)
# Archive the incoming mail # Archive the incoming mail
@ -264,7 +296,8 @@ class maillist(object):
self.archive_mail(msg_plain, admin=dest.toadmin) 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? # Save sender information in the outgoing message?
if self.config.attach_sender_info: if self.config.attach_sender_info:
# Only do so for non-subscribers # Only do so for non-subscribers