From 424944ad36e9925a9d3856b7054ca7fcc26f1ea6 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 9 Jun 2023 23:12:58 +0200 Subject: [PATCH] remail: Add X-Original-From header The 'From:' header mangling makes it unnecessarily hard to figure out the name and email address of the person who posted to a list. Save the original sender in the 'X-Original-From:' header. Suggested-by: Linus Torvalds Suggested-by: Konstantin Ryabitsev Signed-off-by: Thomas Gleixner Link: https://wiki.ietf.org/group/dmarc/XOriginalFrom --- remail/mail.py | 17 +++++++++++++++-- remail/maillist.py | 10 ++++++---- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/remail/mail.py b/remail/mail.py index 1dd6860..83b55cc 100644 --- a/remail/mail.py +++ b/remail/mail.py @@ -11,6 +11,7 @@ from email.generator import Generator from email.message import Message, EmailMessage from email.policy import EmailPolicy from email.policy import default as DefaultPolicy +from email.headerregistry import UniqueSingleAddressHeader import smtplib import mailbox @@ -75,13 +76,21 @@ def msg_copy_headers(msgout, msg, headers): if val: msgout[key] = val -def send_mail(msg, account, mfrom, sender, listheaders, use_smtp, logger): +def send_mail(msg, account, mfrom, sender, listheaders, use_smtp, logger, + origfrom=None): ''' Send mail to the account. Make sure that the message is correct and all required headers and only necessary headers are in the outgoing mail. ''' - # Convert to EmailMessage with default policy (utf-8=false) so both + # Clone the default policy (utf-8=false) and map the X-Original-From + # header to UniqueSingleAddressHeader. Otherwise this is treated as + # unspecified header. + policy = DefaultPolicy.clone() + policy.header_factory.map_to_type('X-Original-From', + UniqueSingleAddressHeader) + + # Convert to EmailMessage with the modified default policy so both # smptlib.send_message() and the stdout dump keep the headers properly # encoded. parser = BytesFeedParser(policy=DefaultPolicy) @@ -109,6 +118,10 @@ def send_mail(msg, account, mfrom, sender, listheaders, use_smtp, logger): msg_copy_headers(msgout, msg, ['Content-Type', 'Content-Disposition', 'Content-Transfer-Encoding', 'Content-Language']) + + if origfrom: + msgout['X-Original-From'] = origfrom + msgout['Envelope-To'] = get_raw_email_addr(account.addr) # Set unixfrom with the current date/time diff --git a/remail/maillist.py b/remail/maillist.py index 2bd9dd8..b399ff7 100644 --- a/remail/maillist.py +++ b/remail/maillist.py @@ -86,11 +86,12 @@ class maillist(object): self.gpg.encrypt(msg, account) return msg - def send_encrypted_mail(self, msg_plain, account, mfrom): + def send_encrypted_mail(self, msg_plain, account, mfrom, origfrom=None): try: msg_out = self.encrypt(msg_plain, account) send_mail(msg_out, account, mfrom, self.config.listaddrs.bounce, - self.config.listheaders, self.use_smtp, self.logger) + self.config.listheaders, self.use_smtp, self.logger, + origfrom) except (RemailGPGException, RemailSmimeException) as ex: ''' @@ -243,7 +244,8 @@ class maillist(object): msgid = msg.get('Message-Id', '') msgto = msg.get('To') - msgfrom = get_raw_email_addr(msg.get('From')) + origfrom = msg.get('From') + msgfrom = get_raw_email_addr(origfrom) sinfo = sender_info(msg) # Archive the incoming mail @@ -272,7 +274,7 @@ class maillist(object): for account in dest.accounts.values(): if not account.enabled: continue - self.send_encrypted_mail(msg_plain, account, mfrom) + self.send_encrypted_mail(msg_plain, account, mfrom, origfrom) return True def handle_log(self):