def test_email_subject_stripping(self) -> None: subject_list = ujson.loads( self.fixture_data('subjects.json', type='email')) for subject in subject_list: user_profile = self.example_user('hamlet') self.login(user_profile.email) self.subscribe(user_profile, "Denmark") stream = get_stream("Denmark", user_profile.realm) stream_to_address = encode_email_address(stream) incoming_valid_message = MIMEText('TestStreamEmailMessages Body') incoming_valid_message['Subject'] = subject['original_subject'] incoming_valid_message['From'] = self.example_email('hamlet') incoming_valid_message['To'] = stream_to_address incoming_valid_message['Reply-to'] = self.example_email('othello') process_message(incoming_valid_message) message = most_recent_message(user_profile) if subject['stripped_subject'] != "": expected_topic = subject['stripped_subject'] else: expected_topic = "(no topic)" self.assertEqual(expected_topic, message.topic_name())
def test_receive_stream_email_messages_success(self): # build dummy messages for stream # test valid incoming stream message is processed properly self.login("*****@*****.**") user_profile = get_user_profile_by_email("*****@*****.**") self.subscribe_to_stream(user_profile.email, "Denmark") stream = get_stream("Denmark", user_profile.realm) stream_to_address = encode_email_address(stream) incoming_valid_message = MIMEText('TestStreamEmailMessages Body') incoming_valid_message['Subject'] = 'TestStreamEmailMessages Subject' incoming_valid_message['From'] = "*****@*****.**" incoming_valid_message['To'] = stream_to_address incoming_valid_message['Reply-to'] = "*****@*****.**" 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.subject, incoming_valid_message['Subject'])
def test_receive_stream_email_messages_success(self): # type: () -> None # build dummy messages for stream # test valid incoming stream message is processed properly user_profile = self.example_user('hamlet') self.login(user_profile.email) self.subscribe(user_profile, "Denmark") stream = get_stream("Denmark", user_profile.realm) stream_to_address = 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'] = stream_to_address 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'])
def test_reply_is_extracted_from_plain(self): # type: () -> None # build dummy messages for stream # test valid incoming stream message is processed properly email = self.example_email('hamlet') self.login(email) user_profile = self.example_user('hamlet') self.subscribe(user_profile, "Denmark") stream = get_stream("Denmark", user_profile.realm) stream_to_address = encode_email_address(stream) text = """Reply -----Original Message----- Quote""" incoming_valid_message = MIMEText(text) # 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'] = stream_to_address 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, "Reply")
def test_receive_stream_email_messages_success(self) -> None: # build dummy messages for stream # test valid incoming stream message is processed properly user_profile = self.example_user('hamlet') self.login(user_profile.email) self.subscribe(user_profile, "Denmark") stream = get_stream("Denmark", user_profile.realm) stream_to_address = 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'] = stream_to_address 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'])
def test_reply_is_extracted_from_plain(self) -> None: # build dummy messages for stream # test valid incoming stream message is processed properly email = self.example_email('hamlet') self.login(email) user_profile = self.example_user('hamlet') self.subscribe(user_profile, "Denmark") stream = get_stream("Denmark", user_profile.realm) stream_to_address = encode_email_address(stream) text = """Reply -----Original Message----- Quote""" incoming_valid_message = MIMEText(text) # 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'] = stream_to_address 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, "Reply")
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 <*****@*****.**>", "Denmark <{}>".format(encode_email_address(stream)) ] incoming_valid_message = MIMEText('TestStreamEmailMessages Body') 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'])
def test_receive_private_stream_email_messages_success(self) -> None: user_profile = self.example_user('hamlet') self.login(user_profile.email) self.make_stream("private_stream", invite_only=True) self.subscribe(user_profile, "private_stream") stream = get_stream("private_stream", user_profile.realm) stream_to_address = encode_email_address(stream) incoming_valid_message = MIMEText('TestStreamEmailMessages Body') incoming_valid_message['Subject'] = 'TestStreamEmailMessages Subject' incoming_valid_message['From'] = self.example_email('hamlet') incoming_valid_message['To'] = stream_to_address 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'])
def test_receive_stream_email_show_sender_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_to_address = encode_email_address(stream) parts = stream_to_address.split('@') parts[0] += "+show-sender" stream_to_address = '@'.join(parts) incoming_valid_message = MIMEText('TestStreamEmailMessages Body') incoming_valid_message['Subject'] = 'TestStreamEmailMessages Subject' incoming_valid_message['From'] = self.example_email('hamlet') incoming_valid_message['To'] = stream_to_address incoming_valid_message['Reply-to'] = self.example_email('othello') process_message(incoming_valid_message) message = most_recent_message(user_profile) self.assertEqual( message.content, "From: %s\n%s" % (self.example_email('hamlet'), "TestStreamEmailMessages Body")) self.assertEqual(get_display_recipient(message.recipient), stream.name) 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 <*****@*****.**>", "Denmark <{}>".format(encode_email_address(stream))] incoming_valid_message = MIMEText('TestStreamEmailMessages Body') 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'])
def test_message_with_valid_attachment(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_to_address = encode_email_address(stream) incoming_valid_message = MIMEMultipart() text_msg = MIMEText("Test body") incoming_valid_message.attach(text_msg) with open(os.path.join(settings.DEPLOY_ROOT, "static/images/default-avatar.png"), 'rb') as f: image_bytes = f.read() attachment_msg = MIMEImage(image_bytes) attachment_msg.add_header('Content-Disposition', 'attachment', filename="image.png") incoming_valid_message.attach(attachment_msg) incoming_valid_message['Subject'] = 'TestStreamEmailMessages Subject' incoming_valid_message['From'] = self.example_email('hamlet') incoming_valid_message['To'] = stream_to_address incoming_valid_message['Reply-to'] = self.example_email('othello') with mock.patch('zerver.lib.email_mirror.upload_message_file', return_value='https://test_url') as upload_message_file: process_message(incoming_valid_message) upload_message_file.assert_called_with('image.png', len(image_bytes), 'image/png', image_bytes, get_system_bot(settings.EMAIL_GATEWAY_BOT), target_realm=user_profile.realm) message = most_recent_message(user_profile) self.assertEqual(message.content, "Test body[image.png](https://test_url)")
def test_receive_missed_huddle_message_email_messages(self) -> None: # build dummy messages for missed messages email reply # have Othello send Iago and Cordelia a PM. Cordelia will reply via email # Iago and Othello will receive the message. email = self.example_email('othello') self.login(email) result = self.client_post( "/json/messages", { "type": "private", "content": "test_receive_missed_message_email_messages", "client": "test suite", "to": ujson.dumps([ self.example_email('cordelia'), self.example_email('iago') ]) }) self.assert_json_success(result) user_profile = self.example_user('cordelia') usermessage = most_recent_usermessage(user_profile) # we don't want to send actual emails but we do need to create and store the # token for looking up who did reply. mm_address = create_missed_message_address(user_profile, usermessage.message) incoming_valid_message = MIMEText( 'TestMissedHuddleMessageEmailMessages Body' ) # type: Any # https://github.com/python/typeshed/issues/275 incoming_valid_message[ 'Subject'] = 'TestMissedHuddleMessageEmailMessages Subject' incoming_valid_message['From'] = self.example_email('cordelia') incoming_valid_message['To'] = mm_address incoming_valid_message['Reply-to'] = self.example_email('cordelia') process_message(incoming_valid_message) # Confirm Iago received the message. user_profile = self.example_user('iago') message = most_recent_message(user_profile) self.assertEqual(message.content, "TestMissedHuddleMessageEmailMessages Body") self.assertEqual(message.sender, self.example_user('cordelia')) self.assertEqual(message.recipient.type, Recipient.HUDDLE) # Confirm Othello received the message. user_profile = self.example_user('othello') message = most_recent_message(user_profile) self.assertEqual(message.content, "TestMissedHuddleMessageEmailMessages Body") self.assertEqual(message.sender, self.example_user('cordelia')) self.assertEqual(message.recipient.type, Recipient.HUDDLE)
def handle(self, *args: Any, **options: str) -> None: # We're probably running from cron, try to batch-process mail if (not settings.EMAIL_GATEWAY_BOT or not settings.EMAIL_GATEWAY_LOGIN or not settings.EMAIL_GATEWAY_PASSWORD or not settings.EMAIL_GATEWAY_IMAP_SERVER or not settings.EMAIL_GATEWAY_IMAP_PORT or not settings.EMAIL_GATEWAY_IMAP_FOLDER): raise CommandError("Please configure the Email Mirror Gateway in /etc/zulip/, " "or specify $ORIGINAL_RECIPIENT if piping a single mail.") for message in get_imap_messages(): process_message(message)
def handle(self, *args: Any, **options: str) -> None: # We're probably running from cron, try to batch-process mail if (not settings.EMAIL_GATEWAY_BOT or not settings.EMAIL_GATEWAY_LOGIN or not settings.EMAIL_GATEWAY_PASSWORD or not settings.EMAIL_GATEWAY_IMAP_SERVER or not settings.EMAIL_GATEWAY_IMAP_PORT or not settings.EMAIL_GATEWAY_IMAP_FOLDER): print("Please configure the Email Mirror Gateway in /etc/zulip/, " "or specify $ORIGINAL_RECIPIENT if piping a single mail.") exit(1) for message in get_imap_messages(): process_message(message)
def handle(self, *args, **options): # type: (*Any, **str) -> None rcpt_to = force_text( os.environ.get("ORIGINAL_RECIPIENT", options['recipient'])) if rcpt_to is not None: if is_missed_message_address(rcpt_to): try: mark_missed_message_address_as_used(rcpt_to) except ZulipEmailForwardError: print( "5.1.1 Bad destination mailbox address: Bad or expired missed message address." ) exit( posix.EX_NOUSER ) # type: ignore # There are no stubs for posix in python 3 else: try: extract_and_validate(rcpt_to) except ZulipEmailForwardError: print( "5.1.1 Bad destination mailbox address: Please use the address specified " "in your Streams page.") exit( posix.EX_NOUSER ) # type: ignore # There are no stubs for posix in python 3 # Read in the message, at most 25MiB. This is the limit enforced by # Gmail, which we use here as a decent metric. msg_text = sys.stdin.read(25 * 1024 * 1024) if len(sys.stdin.read(1)) != 0: # We're not at EOF, reject large mail. print("5.3.4 Message too big for system: Max size is 25MiB") exit( posix.EX_DATAERR ) # type: ignore # There are no stubs for posix in python 3 queue_json_publish("email_mirror", { "message": msg_text, "rcpt_to": rcpt_to }, lambda x: None) else: # We're probably running from cron, try to batch-process mail if (not settings.EMAIL_GATEWAY_BOT or not settings.EMAIL_GATEWAY_LOGIN or not settings.EMAIL_GATEWAY_PASSWORD or not settings.EMAIL_GATEWAY_IMAP_SERVER or not settings.EMAIL_GATEWAY_IMAP_PORT or not settings.EMAIL_GATEWAY_IMAP_FOLDER): print( "Please configure the Email Mirror Gateway in /etc/zulip/, " "or specify $ORIGINAL_RECIPIENT if piping a single mail.") exit(1) for message in get_imap_messages(): process_message(message)
def test_process_message_no_valid_recipient(self) -> None: incoming_valid_message = MIMEText('Test Body') incoming_valid_message['Subject'] = "Test Subject" incoming_valid_message['From'] = self.example_email('hamlet') incoming_valid_message['To'] = "address@wrongdomain, address@notzulip" incoming_valid_message['Reply-to'] = self.example_email('othello') with mock.patch("zerver.lib.email_mirror.log_and_report") as mock_log_and_report: process_message(incoming_valid_message) mock_log_and_report.assert_called_with(incoming_valid_message, "Missing recipient in mirror email", {})
def test_receive_missed_huddle_message_email_messages(self): # build dummy messages for missed messages email reply # have Othello send Iago and Cordelia a PM. Cordelia will reply via email # Iago and Othello will receive the message. self.login("*****@*****.**") result = self.client.post( "/json/messages", { "type": "private", "content": "test_receive_missed_message_email_messages", "client": "test suite", "to": ujson.dumps(["*****@*****.**", "*****@*****.**"]) }) self.assert_json_success(result) user_profile = get_user_profile_by_email("*****@*****.**") usermessage = most_recent_usermessage(user_profile) # we don't want to send actual emails but we do need to create and store the # token for looking up who did reply. mm_address = create_missed_message_address(user_profile, usermessage.message) incoming_valid_message = MIMEText( 'TestMissedHuddleMessageEmailMessages Body') incoming_valid_message[ 'Subject'] = 'TestMissedHuddleMessageEmailMessages Subject' incoming_valid_message['From'] = "*****@*****.**" incoming_valid_message['To'] = mm_address incoming_valid_message['Reply-to'] = "*****@*****.**" process_message(incoming_valid_message) # Confirm Iago received the message. user_profile = get_user_profile_by_email("*****@*****.**") message = most_recent_message(user_profile) self.assertEqual(message.content, "TestMissedHuddleMessageEmailMessages Body") self.assertEqual(message.sender, get_user_profile_by_email("*****@*****.**")) self.assertEqual(message.recipient.type, Recipient.HUDDLE) # Confirm Othello received the message. user_profile = get_user_profile_by_email("*****@*****.**") message = most_recent_message(user_profile) self.assertEqual(message.content, "TestMissedHuddleMessageEmailMessages Body") self.assertEqual(message.sender, get_user_profile_by_email("*****@*****.**")) self.assertEqual(message.recipient.type, Recipient.HUDDLE)
def test_reply_is_extracted_from_html(self) -> None: # build dummy messages for stream # test valid incoming stream message is processed properly email = self.example_email('hamlet') self.login(email) user_profile = self.example_user('hamlet') self.subscribe(user_profile, "Denmark") stream = get_stream("Denmark", user_profile.realm) stream_to_address = encode_email_address(stream) html = """ <html> <body> <p>Reply</p> <blockquote> <div> On 11-Apr-2011, at 6:54 PM, Bob <[email protected]> wrote: </div> <div> Quote </div> </blockquote> </body> </html> """ incoming_valid_message = MIMEText(html, 'html') incoming_valid_message['Subject'] = 'TestStreamEmailMessages Subject' incoming_valid_message['From'] = self.example_email('hamlet') incoming_valid_message['To'] = stream_to_address 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, 'Reply') # Don't extract if Subject indicates the email has been forwarded into the mirror: del incoming_valid_message['Subject'] incoming_valid_message[ 'Subject'] = 'FWD: TestStreamEmailMessages Subject' process_message(incoming_valid_message) message = most_recent_message(user_profile) self.assertEqual(message.content, convert_html_to_markdown(html))
def test_reply_is_extracted_from_html(self) -> None: # build dummy messages for stream # test valid incoming stream message is processed properly email = self.example_email('hamlet') self.login(email) user_profile = self.example_user('hamlet') self.subscribe(user_profile, "Denmark") stream = get_stream("Denmark", user_profile.realm) stream_to_address = encode_email_address(stream) html = """ <html> <body> <p>Reply</p> <blockquote> <div> On 11-Apr-2011, at 6:54 PM, Bob <[email protected]> wrote: </div> <div> Quote </div> </blockquote> </body> </html> """ incoming_valid_message = MIMEText(html, 'html') incoming_valid_message['Subject'] = 'TestStreamEmailMessages Subject' incoming_valid_message['From'] = self.example_email('hamlet') incoming_valid_message['To'] = stream_to_address 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, 'Reply') # Don't extract if Subject indicates the email has been forwarded into the mirror: del incoming_valid_message['Subject'] incoming_valid_message['Subject'] = 'FWD: TestStreamEmailMessages Subject' process_message(incoming_valid_message) message = most_recent_message(user_profile) self.assertEqual(message.content, convert_html_to_markdown(html))
def fetch(result, proto, mailboxes): if not result: return proto.logout() # Make sure we forward the messages in time-order. message_uids = sorted(result.keys()) for uid in message_uids: message = email.message_from_string(result[uid]["RFC822"]) process_message(message) # Delete the processed messages from the Inbox. message_set = ",".join([result[key]["UID"] for key in message_uids]) d = proto.addFlags(message_set, ["\\Deleted"], uid=True, silent=False) d.addCallback(delete, proto) return d
def test_receive_missed_personal_message_email_messages(self): # type: () -> None # build dummy messages for missed messages email reply # have Hamlet send Othello a PM. Othello will reply via email # Hamlet will receive the message. email = self.example_email('hamlet') self.login(email) result = self.client_post( "/json/messages", { "type": "private", "content": "test_receive_missed_message_email_messages", "client": "test suite", "to": self.example_email('othello') }) self.assert_json_success(result) user_profile = self.example_user('othello') usermessage = most_recent_usermessage(user_profile) # we don't want to send actual emails but we do need to create and store the # token for looking up who did reply. mm_address = create_missed_message_address(user_profile, usermessage.message) incoming_valid_message = MIMEText( 'TestMissedMessageEmailMessages Body' ) # type: Any # https://github.com/python/typeshed/issues/275 incoming_valid_message[ 'Subject'] = 'TestMissedMessageEmailMessages Subject' incoming_valid_message['From'] = self.example_email('othello') incoming_valid_message['To'] = mm_address incoming_valid_message['Reply-to'] = self.example_email('othello') process_message(incoming_valid_message) # self.login(self.example_email("hamlet")) # confirm that Hamlet got the message user_profile = self.example_user('hamlet') message = most_recent_message(user_profile) self.assertEqual(message.content, "TestMissedMessageEmailMessages Body") self.assertEqual(message.sender, self.example_user('othello')) self.assertEqual(message.recipient.id, user_profile.id) self.assertEqual(message.recipient.type, Recipient.PERSONAL)
def test_receive_missed_huddle_message_email_messages(self): # type: () -> None # build dummy messages for missed messages email reply # have Othello send Iago and Cordelia a PM. Cordelia will reply via email # Iago and Othello will receive the message. email = self.example_email('othello') self.login(email) result = self.client_post("/json/messages", {"type": "private", "content": "test_receive_missed_message_email_messages", "client": "test suite", "to": ujson.dumps([self.example_email('cordelia'), self.example_email('iago')])}) self.assert_json_success(result) user_profile = self.example_user('cordelia') usermessage = most_recent_usermessage(user_profile) # we don't want to send actual emails but we do need to create and store the # token for looking up who did reply. mm_address = create_missed_message_address(user_profile, usermessage.message) incoming_valid_message = MIMEText('TestMissedHuddleMessageEmailMessages Body') # type: Any # https://github.com/python/typeshed/issues/275 incoming_valid_message['Subject'] = 'TestMissedHuddleMessageEmailMessages Subject' incoming_valid_message['From'] = self.example_email('cordelia') incoming_valid_message['To'] = mm_address incoming_valid_message['Reply-to'] = self.example_email('cordelia') process_message(incoming_valid_message) # Confirm Iago received the message. user_profile = self.example_user('iago') message = most_recent_message(user_profile) self.assertEqual(message.content, "TestMissedHuddleMessageEmailMessages Body") self.assertEqual(message.sender, self.example_user('cordelia')) self.assertEqual(message.recipient.type, Recipient.HUDDLE) # Confirm Othello received the message. user_profile = self.example_user('othello') message = most_recent_message(user_profile) self.assertEqual(message.content, "TestMissedHuddleMessageEmailMessages Body") self.assertEqual(message.sender, self.example_user('cordelia')) self.assertEqual(message.recipient.type, Recipient.HUDDLE)
def test_reply_is_extracted_from_html(self): # type: () -> None # build dummy messages for stream # test valid incoming stream message is processed properly email = self.example_email('hamlet') self.login(email) user_profile = self.example_user('hamlet') self.subscribe(user_profile, "Denmark") stream = get_stream("Denmark", user_profile.realm) stream_to_address = encode_email_address(stream) html = """ <html> <body> <p>Reply</p> <blockquote> <div> On 11-Apr-2011, at 6:54 PM, Bob <[email protected]> wrote: </div> <div> Quote </div> </blockquote> </body> </html> """ incoming_valid_message = MIMEText( html, 'html' ) # 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'] = stream_to_address 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, 'Reply')
def handle(self, *args, **options): # type: (*Any, **str) -> None rcpt_to = force_text(os.environ.get("ORIGINAL_RECIPIENT", options['recipient'])) if rcpt_to is not None: if is_missed_message_address(rcpt_to): try: mark_missed_message_address_as_used(rcpt_to) except ZulipEmailForwardError: print("5.1.1 Bad destination mailbox address: Bad or expired missed message address.") exit(posix.EX_NOUSER) # type: ignore # There are no stubs for posix in python 3 else: try: extract_and_validate(rcpt_to) except ZulipEmailForwardError: print("5.1.1 Bad destination mailbox address: Please use the address specified " "in your Streams page.") exit(posix.EX_NOUSER) # type: ignore # There are no stubs for posix in python 3 # Read in the message, at most 25MiB. This is the limit enforced by # Gmail, which we use here as a decent metric. msg_text = sys.stdin.read(25*1024*1024) if len(sys.stdin.read(1)) != 0: # We're not at EOF, reject large mail. print("5.3.4 Message too big for system: Max size is 25MiB") exit(posix.EX_DATAERR) # type: ignore # There are no stubs for posix in python 3 queue_json_publish( "email_mirror", { "message": msg_text, "rcpt_to": rcpt_to }, lambda x: None ) else: # We're probably running from cron, try to batch-process mail if (not settings.EMAIL_GATEWAY_BOT or not settings.EMAIL_GATEWAY_LOGIN or not settings.EMAIL_GATEWAY_PASSWORD or not settings.EMAIL_GATEWAY_IMAP_SERVER or not settings.EMAIL_GATEWAY_IMAP_PORT or not settings.EMAIL_GATEWAY_IMAP_FOLDER): print("Please configure the Email Mirror Gateway in /etc/zulip/, " "or specify $ORIGINAL_RECIPIENT if piping a single mail.") exit(1) for message in get_imap_messages(): process_message(message)
def test_receive_missed_personal_message_email_messages(self): # build dummy messages for missed messages email reply # have Hamlet send Othello a PM. Othello will reply via email # Hamlet will receive the message. self.login("*****@*****.**") result = self.client.post( "/json/messages", { "type": "private", "content": "test_receive_missed_message_email_messages", "client": "test suite", "to": "*****@*****.**" }) self.assert_json_success(result) user_profile = get_user_profile_by_email("*****@*****.**") usermessage = most_recent_usermessage(user_profile) # we don't want to send actual emails but we do need to create and store the # token for looking up who did reply. mm_address = create_missed_message_address(user_profile, usermessage.message) incoming_valid_message = MIMEText( 'TestMissedMessageEmailMessages Body') incoming_valid_message[ 'Subject'] = 'TestMissedMessageEmailMessages Subject' incoming_valid_message['From'] = "*****@*****.**" incoming_valid_message['To'] = mm_address incoming_valid_message['Reply-to'] = "*****@*****.**" process_message(incoming_valid_message) # self.login("*****@*****.**") # confirm that Hamlet got the message user_profile = get_user_profile_by_email("*****@*****.**") message = most_recent_message(user_profile) self.assertEqual(message.content, "TestMissedMessageEmailMessages Body") self.assertEqual(message.sender, get_user_profile_by_email("*****@*****.**")) self.assertEqual(message.recipient.id, user_profile.id) self.assertEqual(message.recipient.type, Recipient.PERSONAL)
def test_charset_not_specified(self) -> None: message_as_string = self.fixture_data('1.txt', type='email') message_as_string = message_as_string.replace("Content-Type: text/plain; charset=\"us-ascii\"", "Content-Type: text/plain") incoming_message = message_from_string(message_as_string) user_profile = self.example_user('hamlet') self.login(user_profile.email) self.subscribe(user_profile, "Denmark") stream = get_stream("Denmark", user_profile.realm) stream_to_address = encode_email_address(stream) del incoming_message['To'] incoming_message['To'] = stream_to_address process_message(incoming_message) message = most_recent_message(user_profile) self.assertEqual(message.content, "Email fixture 1.txt body")
def test_reply_is_extracted_from_html(self): # type: () -> None # build dummy messages for stream # test valid incoming stream message is processed properly email = self.example_email('hamlet') self.login(email) user_profile = self.example_user('hamlet') self.subscribe(user_profile, "Denmark") stream = get_stream("Denmark", user_profile.realm) stream_to_address = encode_email_address(stream) html = """ <html> <body> <p>Reply</p> <blockquote> <div> On 11-Apr-2011, at 6:54 PM, Bob <[email protected]> wrote: </div> <div> Quote </div> </blockquote> </body> </html> """ incoming_valid_message = MIMEText(html, 'html') # 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'] = stream_to_address 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, 'Reply')
def test_receive_missed_huddle_message_email_messages(self): # build dummy messages for missed messages email reply # have Othello send Iago and Cordelia a PM. Cordelia will reply via email # Iago and Othello will receive the message. self.login("*****@*****.**") result = self.client.post("/json/send_message", {"type": "private", "content": "test_receive_missed_message_email_messages", "client": "test suite", "to": ujson.dumps(["*****@*****.**", "*****@*****.**"])}) self.assert_json_success(result) user_profile = get_user_profile_by_email("*****@*****.**") usermessage = most_recent_usermessage(user_profile) # we don't want to send actual emails but we do need to create and store the # token for looking up who did reply. mm_address = create_missed_message_address(user_profile, usermessage.message) incoming_valid_message = MIMEText('TestMissedHuddleMessageEmailMessages Body') incoming_valid_message['Subject'] = 'TestMissedHuddleMessageEmailMessages Subject' incoming_valid_message['From'] = "*****@*****.**" incoming_valid_message['To'] = mm_address incoming_valid_message['Reply-to'] = "*****@*****.**" process_message(incoming_valid_message) # Confirm Iago received the message. user_profile = get_user_profile_by_email("*****@*****.**") message = most_recent_message(user_profile) self.assertEqual(message.content, "TestMissedHuddleMessageEmailMessages Body") self.assertEqual(message.sender, get_user_profile_by_email("*****@*****.**")) self.assertEqual(message.recipient.type, Recipient.HUDDLE) # Confirm Othello received the message. user_profile = get_user_profile_by_email("*****@*****.**") message = most_recent_message(user_profile) self.assertEqual(message.content, "TestMissedHuddleMessageEmailMessages Body") self.assertEqual(message.sender, get_user_profile_by_email("*****@*****.**")) self.assertEqual(message.recipient.type, Recipient.HUDDLE)
def test_receive_stream_email_messages_no_textual_body(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_to_address = encode_email_address(stream) # No textual body incoming_valid_message = MIMEMultipart() with open(os.path.join(settings.DEPLOY_ROOT, "static/images/default-avatar.png"), 'rb') as f: incoming_valid_message.attach(MIMEImage(f.read())) incoming_valid_message['Subject'] = 'TestStreamEmailMessages Subject' incoming_valid_message['From'] = self.example_email('hamlet') incoming_valid_message['To'] = stream_to_address incoming_valid_message['Reply-to'] = self.example_email('othello') with mock.patch('zerver.lib.email_mirror.logging.warning') as mock_warn: process_message(incoming_valid_message) mock_warn.assert_called_with("Unable to find plaintext or HTML message body")
def test_receive_stream_email_messages_empty_body(self) -> None: # build dummy messages for stream # test message with empty body is not sent user_profile = self.example_user('hamlet') self.login(user_profile.email) self.subscribe(user_profile, "Denmark") stream = get_stream("Denmark", user_profile.realm) stream_to_address = encode_email_address(stream) # empty body incoming_valid_message = MIMEText('') incoming_valid_message['Subject'] = 'TestStreamEmailMessages Subject' incoming_valid_message['From'] = self.example_email('hamlet') incoming_valid_message['To'] = stream_to_address incoming_valid_message['Reply-to'] = self.example_email('othello') with mock.patch('zerver.lib.email_mirror.logging.warning') as mock_warn: process_message(incoming_valid_message) mock_warn.assert_called_with("Email has no nonempty body sections; ignoring.")
def handle(self, **options: str) -> None: if options['fixture'] is None: self.print_help('./manage.py', 'send_to_email_mirror') exit(1) if options['stream'] is None: stream = "Denmark" else: stream = options['stream'] realm = self.get_realm(options) if realm is None: realm = get_realm("zulip") full_fixture_path = os.path.join(settings.DEPLOY_ROOT, options['fixture']) # parse the input email into Message type and prepare to process_message() it message = self._parse_email_fixture(full_fixture_path) self._prepare_message(message, realm, stream) process_message(message)
def test_process_message_strips_subject(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_to_address = encode_email_address(stream) incoming_valid_message = MIMEText('TestStreamEmailMessages Body') incoming_valid_message['Subject'] = "Re: Fwd: Re: Test" incoming_valid_message['From'] = self.example_email('hamlet') incoming_valid_message['To'] = stream_to_address incoming_valid_message['Reply-to'] = self.example_email('othello') process_message(incoming_valid_message) message = most_recent_message(user_profile) self.assertEqual("Test", message.topic_name()) # If after stripping we get an empty subject, it should get set to (no topic) del incoming_valid_message['Subject'] incoming_valid_message['Subject'] = "Re: Fwd: Re: " process_message(incoming_valid_message) message = most_recent_message(user_profile) self.assertEqual("(no topic)", message.topic_name())
def test_receive_missed_personal_message_email_messages(self): # type: () -> None # build dummy messages for missed messages email reply # have Hamlet send Othello a PM. Othello will reply via email # Hamlet will receive the message. email = self.example_email('hamlet') self.login(email) result = self.client_post("/json/messages", {"type": "private", "content": "test_receive_missed_message_email_messages", "client": "test suite", "to": self.example_email('othello')}) self.assert_json_success(result) user_profile = self.example_user('othello') usermessage = most_recent_usermessage(user_profile) # we don't want to send actual emails but we do need to create and store the # token for looking up who did reply. mm_address = create_missed_message_address(user_profile, usermessage.message) incoming_valid_message = MIMEText('TestMissedMessageEmailMessages Body') # type: Any # https://github.com/python/typeshed/issues/275 incoming_valid_message['Subject'] = 'TestMissedMessageEmailMessages Subject' incoming_valid_message['From'] = self.example_email('othello') incoming_valid_message['To'] = mm_address incoming_valid_message['Reply-to'] = self.example_email('othello') process_message(incoming_valid_message) # self.login(self.example_email("hamlet")) # confirm that Hamlet got the message user_profile = self.example_user('hamlet') message = most_recent_message(user_profile) self.assertEqual(message.content, "TestMissedMessageEmailMessages Body") self.assertEqual(message.sender, self.example_user('othello')) self.assertEqual(message.recipient.id, user_profile.id) self.assertEqual(message.recipient.type, Recipient.PERSONAL)
def test_receive_stream_email_messages_empty_body_after_stripping(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_to_address = encode_email_address(stream) headers = {} headers['Reply-To'] = self.example_email('othello') # empty body incoming_valid_message = MIMEText('-- \nFooter') incoming_valid_message['Subject'] = 'TestStreamEmailMessages Subject' incoming_valid_message['From'] = self.example_email('hamlet') incoming_valid_message['To'] = stream_to_address incoming_valid_message['Reply-to'] = self.example_email('othello') process_message(incoming_valid_message) message = most_recent_message(user_profile) self.assertEqual(message.content, "(No email body)")
def test_receive_missed_stream_message_email_messages(self) -> None: # build dummy messages for missed messages email reply # have Hamlet send a message to stream Denmark, that Othello # will receive a missed message email about. # Othello will reply via email. # Hamlet will see the message in the stream. self.subscribe(self.example_user("hamlet"), "Denmark") self.subscribe(self.example_user("othello"), "Denmark") email = self.example_email('hamlet') self.login(email) result = self.client_post("/json/messages", {"type": "stream", "topic": "test topic", "content": "test_receive_missed_stream_message_email_messages", "client": "test suite", "to": "Denmark"}) self.assert_json_success(result) user_profile = self.example_user('othello') usermessage = most_recent_usermessage(user_profile) mm_address = create_missed_message_address(user_profile, usermessage.message) incoming_valid_message = MIMEText('TestMissedMessageEmailMessages Body') incoming_valid_message['Subject'] = 'TestMissedMessageEmailMessages Subject' incoming_valid_message['From'] = self.example_email('othello') incoming_valid_message['To'] = mm_address incoming_valid_message['Reply-to'] = self.example_email('othello') process_message(incoming_valid_message) # confirm that Hamlet got the message user_profile = self.example_user('hamlet') message = most_recent_message(user_profile) self.assertEqual(message.content, "TestMissedMessageEmailMessages Body") self.assertEqual(message.sender, self.example_user('othello')) self.assertEqual(message.recipient.type, Recipient.STREAM) self.assertEqual(message.recipient.id, usermessage.message.recipient.id)
def test_reply_is_extracted_from_plain(self) -> None: # build dummy messages for stream # test valid incoming stream message is processed properly email = self.example_email('hamlet') self.login(email) user_profile = self.example_user('hamlet') self.subscribe(user_profile, "Denmark") stream = get_stream("Denmark", user_profile.realm) stream_to_address = encode_email_address(stream) text = """Reply -----Original Message----- Quote""" incoming_valid_message = MIMEText(text) incoming_valid_message['Subject'] = 'TestStreamEmailMessages Subject' incoming_valid_message['From'] = self.example_email('hamlet') incoming_valid_message['To'] = stream_to_address 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, "Reply") # Don't extract if Subject indicates the email has been forwarded into the mirror: del incoming_valid_message['Subject'] incoming_valid_message[ 'Subject'] = 'FWD: TestStreamEmailMessages Subject' process_message(incoming_valid_message) message = most_recent_message(user_profile) self.assertEqual(message.content, text)
def test_receive_stream_email_messages_blank_subject_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_to_address = encode_email_address(stream) incoming_valid_message = MIMEText('TestStreamEmailMessages Body') incoming_valid_message['Subject'] = '' incoming_valid_message['From'] = self.example_email('hamlet') incoming_valid_message['To'] = stream_to_address 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(), "(no topic)")
def test_receive_missed_personal_message_email_messages(self): # build dummy messages for missed messages email reply # have Hamlet send Othello a PM. Othello will reply via email # Hamlet will receive the message. self.login("*****@*****.**") result = self.client.post("/json/send_message", {"type": "private", "content": "test_receive_missed_message_email_messages", "client": "test suite", "to": "*****@*****.**"}) self.assert_json_success(result) user_profile = get_user_profile_by_email("*****@*****.**") usermessage = most_recent_usermessage(user_profile) # we don't want to send actual emails but we do need to create and store the # token for looking up who did reply. mm_address = create_missed_message_address(user_profile, usermessage.message) incoming_valid_message = MIMEText('TestMissedMessageEmailMessages Body') incoming_valid_message['Subject'] = 'TestMissedMessageEmailMessages Subject' incoming_valid_message['From'] = "*****@*****.**" incoming_valid_message['To'] = mm_address incoming_valid_message['Reply-to'] = "*****@*****.**" process_message(incoming_valid_message) # self.login("*****@*****.**") # confirm that Hamlet got the message user_profile = get_user_profile_by_email("*****@*****.**") message = most_recent_message(user_profile) self.assertEqual(message.content, "TestMissedMessageEmailMessages Body") self.assertEqual(message.sender, get_user_profile_by_email("*****@*****.**")) self.assertEqual(message.recipient.id, user_profile.id) self.assertEqual(message.recipient.type, Recipient.PERSONAL)
def test_reply_is_extracted_from_plain(self) -> None: # build dummy messages for stream # test valid incoming stream message is processed properly email = self.example_email('hamlet') self.login(email) user_profile = self.example_user('hamlet') self.subscribe(user_profile, "Denmark") stream = get_stream("Denmark", user_profile.realm) stream_to_address = encode_email_address(stream) text = """Reply -----Original Message----- Quote""" incoming_valid_message = MIMEText(text) incoming_valid_message['Subject'] = 'TestStreamEmailMessages Subject' incoming_valid_message['From'] = self.example_email('hamlet') incoming_valid_message['To'] = stream_to_address 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, "Reply") # Don't extract if Subject indicates the email has been forwarded into the mirror: del incoming_valid_message['Subject'] incoming_valid_message['Subject'] = 'FWD: TestStreamEmailMessages Subject' process_message(incoming_valid_message) message = most_recent_message(user_profile) self.assertEqual(message.content, text)
def test_message_with_invalid_attachment(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_to_address = encode_email_address(stream) incoming_valid_message = MIMEMultipart() text_msg = MIMEText("Test body") incoming_valid_message.attach(text_msg) # Create an invalid attachment: attachment_msg = MIMEMultipart() attachment_msg.add_header('Content-Disposition', 'attachment', filename="some_attachment") incoming_valid_message.attach(attachment_msg) incoming_valid_message['Subject'] = 'TestStreamEmailMessages Subject' incoming_valid_message['From'] = self.example_email('hamlet') incoming_valid_message['To'] = stream_to_address incoming_valid_message['Reply-to'] = self.example_email('othello') with mock.patch('zerver.lib.email_mirror.logger.warning') as mock_warn: process_message(incoming_valid_message) mock_warn.assert_called_with("Payload is not bytes (invalid attachment %s in message from %s)." % ('some_attachment', self.example_email('hamlet')))
def test_receive_stream_email_show_sender_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_to_address = encode_email_address(stream) parts = stream_to_address.split('@') parts[0] += "+show-sender" stream_to_address = '@'.join(parts) incoming_valid_message = MIMEText('TestStreamEmailMessages Body') incoming_valid_message['Subject'] = 'TestStreamEmailMessages Subject' incoming_valid_message['From'] = self.example_email('hamlet') incoming_valid_message['To'] = stream_to_address incoming_valid_message['Reply-to'] = self.example_email('othello') process_message(incoming_valid_message) message = most_recent_message(user_profile) self.assertEqual(message.content, "From: %s\n%s" % (self.example_email('hamlet'), "TestStreamEmailMessages Body")) self.assertEqual(get_display_recipient(message.recipient), stream.name) self.assertEqual(message.topic_name(), incoming_valid_message['Subject'])
def test_email_subject_stripping(self) -> None: subject_list = ujson.loads(self.fixture_data('subjects.json', type='email')) for subject in subject_list: user_profile = self.example_user('hamlet') self.login(user_profile.email) self.subscribe(user_profile, "Denmark") stream = get_stream("Denmark", user_profile.realm) stream_to_address = encode_email_address(stream) incoming_valid_message = MIMEText('TestStreamEmailMessages Body') incoming_valid_message['Subject'] = subject['original_subject'] incoming_valid_message['From'] = self.example_email('hamlet') incoming_valid_message['To'] = stream_to_address incoming_valid_message['Reply-to'] = self.example_email('othello') process_message(incoming_valid_message) message = most_recent_message(user_profile) if subject['stripped_subject'] != "": expected_topic = subject['stripped_subject'] else: expected_topic = "(no topic)" self.assertEqual(expected_topic, message.topic_name())
def handle(self, *args: Any, **options: str) -> None: for message in get_imap_messages(): process_message(message)