mirror of https://github.com/zulip/zulip.git
email_mirror: Add email address parsing.
When trying to find the email gateway address, use the `email.util.getaddresses` function to deal with cases where multiple recipients are included in the email header or the stream address appears as an angle-addr with a name given (e.g. if someone added it to their address book). Added some other headers where the required address may appear: "Resent" headers are sometimes used for forwarding, and streams may also be found in CC. There is no way to find the address if the email was recieved as a BCC.
This commit is contained in:
parent
e3c8e8a839
commit
f2e06128c6
|
@ -4,6 +4,7 @@ import logging
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from email.header import decode_header, Header
|
from email.header import decode_header, Header
|
||||||
|
from email.utils import getaddresses
|
||||||
import email.message as message
|
import email.message as message
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
@ -287,19 +288,19 @@ def find_emailgateway_recipient(message: message.Message) -> str:
|
||||||
# We can't use Delivered-To; if there is a X-Gm-Original-To
|
# We can't use Delivered-To; if there is a X-Gm-Original-To
|
||||||
# it is more accurate, so try to find the most-accurate
|
# it is more accurate, so try to find the most-accurate
|
||||||
# recipient list in descending priority order
|
# recipient list in descending priority order
|
||||||
recipient_headers = ["X-Gm-Original-To", "Delivered-To", "To"]
|
recipient_headers = ["X-Gm-Original-To", "Delivered-To",
|
||||||
recipients = [] # type: List[Union[str, Header]]
|
"Resent-To", "Resent-CC", "To", "CC"]
|
||||||
for recipient_header in recipient_headers:
|
|
||||||
r = message.get_all(recipient_header, None)
|
|
||||||
if r:
|
|
||||||
recipients = r
|
|
||||||
break
|
|
||||||
|
|
||||||
pattern_parts = [re.escape(part) for part in settings.EMAIL_GATEWAY_PATTERN.split('%s')]
|
pattern_parts = [re.escape(part) for part in settings.EMAIL_GATEWAY_PATTERN.split('%s')]
|
||||||
match_email_re = re.compile(".*?".join(pattern_parts))
|
match_email_re = re.compile(".*?".join(pattern_parts))
|
||||||
for recipient_email in [str(recipient) for recipient in recipients]:
|
|
||||||
if match_email_re.match(recipient_email):
|
header_addresses = [str(addr)
|
||||||
return recipient_email
|
for recipient_header in recipient_headers
|
||||||
|
for addr in message.get_all(recipient_header, [])]
|
||||||
|
|
||||||
|
for addr_tuple in getaddresses(header_addresses):
|
||||||
|
if match_email_re.match(addr_tuple[1]):
|
||||||
|
return addr_tuple[1]
|
||||||
|
|
||||||
raise ZulipEmailForwardError("Missing recipient in mirror email")
|
raise ZulipEmailForwardError("Missing recipient in mirror email")
|
||||||
|
|
||||||
|
|
|
@ -185,6 +185,33 @@ class TestStreamEmailMessagesSuccess(ZulipTestCase):
|
||||||
self.assertEqual(get_display_recipient(message.recipient), stream.name)
|
self.assertEqual(get_display_recipient(message.recipient), stream.name)
|
||||||
self.assertEqual(message.topic_name(), incoming_valid_message['Subject'])
|
self.assertEqual(message.topic_name(), incoming_valid_message['Subject'])
|
||||||
|
|
||||||
|
def test_receive_stream_email_multiple_recipient_success(self) -> None:
|
||||||
|
user_profile = self.example_user('hamlet')
|
||||||
|
self.login(user_profile.email)
|
||||||
|
self.subscribe(user_profile, "Denmark")
|
||||||
|
stream = get_stream("Denmark", user_profile.realm)
|
||||||
|
|
||||||
|
# stream address is angle-addr within multiple addresses
|
||||||
|
stream_to_addresses = ["A.N. Other <another@example.org>",
|
||||||
|
"Denmark <{}>".format(encode_email_address(stream))]
|
||||||
|
|
||||||
|
incoming_valid_message = MIMEText('TestStreamEmailMessages Body') # type: Any # https://github.com/python/typeshed/issues/275
|
||||||
|
|
||||||
|
incoming_valid_message['Subject'] = 'TestStreamEmailMessages Subject'
|
||||||
|
incoming_valid_message['From'] = self.example_email('hamlet')
|
||||||
|
incoming_valid_message['To'] = ", ".join(stream_to_addresses)
|
||||||
|
incoming_valid_message['Reply-to'] = self.example_email('othello')
|
||||||
|
|
||||||
|
process_message(incoming_valid_message)
|
||||||
|
|
||||||
|
# Hamlet is subscribed to this stream so should see the email message from Othello.
|
||||||
|
message = most_recent_message(user_profile)
|
||||||
|
|
||||||
|
self.assertEqual(message.content, "TestStreamEmailMessages Body")
|
||||||
|
self.assertEqual(get_display_recipient(message.recipient), stream.name)
|
||||||
|
self.assertEqual(message.topic_name(), incoming_valid_message['Subject'])
|
||||||
|
|
||||||
|
|
||||||
class TestStreamEmailMessagesEmptyBody(ZulipTestCase):
|
class TestStreamEmailMessagesEmptyBody(ZulipTestCase):
|
||||||
def test_receive_stream_email_messages_empty_body(self) -> None:
|
def test_receive_stream_email_messages_empty_body(self) -> None:
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue