Handle multiple emails in reply-to header (#2365)

This commit is contained in:
Adrià Casajús
2025-01-24 11:20:50 +01:00
committed by GitHub
parent f4b56788d7
commit c14d7e2098
3 changed files with 79 additions and 13 deletions
+25 -13
View File
@@ -590,15 +590,25 @@ def handle_forward(envelope, msg: Message, rcpt_to: str) -> List[Tuple[bool, str
contact.alias
) # In case the Session was closed in the get_or_create we re-fetch the alias
reply_to_contact = None
reply_to_contact = []
if msg[headers.REPLY_TO]:
reply_to = get_header_unicode(msg[headers.REPLY_TO])
LOG.d("Create or get contact for reply_to_header:%s", reply_to)
# ignore when reply-to = alias
if reply_to == alias.email:
LOG.i("Reply-to same as alias %s", alias)
else:
reply_to_contact = get_or_create_reply_to_contact(reply_to, alias, msg)
reply_to_header_contents = get_header_unicode(msg[headers.REPLY_TO])
if reply_to_header_contents:
LOG.d(
"Create or get contact for reply_to_header:%s", reply_to_header_contents
)
for reply_to in [
reply_to.strip()
for reply_to in reply_to_header_contents.split(",")
if reply_to.strip()
]:
reply_to_name, reply_to_email = parse_full_address(reply_to)
if reply_to_email == alias.email:
LOG.i("Reply-to same as alias %s", alias)
else:
reply_to_contact.append(
get_or_create_reply_to_contact(reply_to_email, alias, msg)
)
if alias.user.delete_on is not None:
LOG.d(f"user {user} is pending to be deleted. Do not forward")
@@ -701,7 +711,7 @@ def forward_email_to_mailbox(
envelope,
mailbox,
user,
reply_to_contact: Optional[Contact],
reply_to_contacts: list[Contact],
) -> (bool, str):
LOG.d("Forward %s -> %s -> %s", contact, alias, mailbox)
@@ -884,11 +894,13 @@ def forward_email_to_mailbox(
add_or_replace_header(msg, "From", new_from_header)
LOG.d("From header, new:%s, old:%s", new_from_header, old_from_header)
if reply_to_contact:
reply_to_header = msg[headers.REPLY_TO]
new_reply_to_header = reply_to_contact.new_addr()
if len(reply_to_contacts) > 0:
original_reply_to = get_header_unicode(msg[headers.REPLY_TO])
new_reply_to_header = ", ".join(
[reply_to_contact.new_addr() for reply_to_contact in reply_to_contacts][:5]
)
add_or_replace_header(msg, "Reply-To", new_reply_to_header)
LOG.d("Reply-To header, new:%s, old:%s", new_reply_to_header, reply_to_header)
LOG.d("Reply-To header, new:%s, old:%s", new_reply_to_header, original_reply_to)
# replace CC & To emails by reverse-alias for all emails that are not alias
try:
+21
View File
@@ -0,0 +1,21 @@
X-SimpleLogin-Client-IP: 54.39.200.130
Received-SPF: Softfail (mailfrom) identity=mailfrom; client-ip=34.59.200.130;
helo=relay.somewhere.net; envelope-from=everwaste@gmail.com;
receiver=<UNKNOWN>
Received: from relay.somewhere.net (relay.somewhere.net [34.59.200.130])
(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))
(No client certificate requested)
by mx1.sldev.ovh (Postfix) with ESMTPS id 6D8C13F069
for <wehrman_mannequin@sldev.ovh>; Thu, 17 Mar 2022 16:50:20 +0000 (UTC)
Date: Thu, 17 Mar 2022 16:50:18 +0000
To: {{ alias_email }}
From: somewhere@rainbow.com
Reply-To: 666-Mail Test <a1@mailcstest.com>, 777-Mail Test <a2@mailcstest.com>,
888-Mail Test <a3@mailcstest.com>, - 5 at mailcstest.com" <a4@simplelogin.co>
Subject: test Thu, 17 Mar 2022 16:50:18 +0000
Message-Id: <20220317165018.000191@somewhere-5488dd4b6b-7crp6>
X-Mailer: swaks v20201014.0 jetmore.org/john/code/swaks/
X-Rspamd-Queue-Id: 6D8C13F069
X-Rspamd-Server: staging1
This is a test mailing
+33
View File
@@ -0,0 +1,33 @@
from aiosmtpd.smtp import Envelope
import email_handler
from app.email import status, headers
from app.email_utils import get_header_unicode, parse_full_address
from app.mail_sender import mail_sender
from app.models import Alias, Contact
from tests.utils import create_new_user, load_eml_file
@mail_sender.store_emails_test_decorator
def test_multi_reply_to():
user = create_new_user()
alias = Alias.create_new_random(user)
envelope = Envelope()
envelope.mail_from = "env.somewhere"
envelope.rcpt_tos = [alias.email]
msg = load_eml_file("multi_reply_to.eml", {"alias_email": alias.email})
alias_id = alias.id
result = email_handler.MailHandler()._handle(envelope, msg)
assert result == status.E200
sent_emails = mail_sender.get_stored_emails()
assert 1 == len(sent_emails)
msg = sent_emails[0].msg
reply_to = get_header_unicode(msg[headers.REPLY_TO])
entries = reply_to.split(",")
assert 4 == len(entries)
for entry in entries:
dummy, email = parse_full_address(entry)
contact = Contact.get_by(reply_email=email)
assert contact is not None
assert contact.alias_id == alias_id