2016-04-07 15:03:22 +02:00
|
|
|
#!/usr/bin/env python
|
2013-08-08 16:44:40 +02:00
|
|
|
|
|
|
|
"""
|
2013-10-08 21:02:47 +02:00
|
|
|
Forward messages sent to the configured email gateway to Zulip.
|
2013-08-08 16:44:40 +02:00
|
|
|
|
2015-08-21 18:34:54 +02:00
|
|
|
For zulip.com, messages to that address go to the Inbox of emailgateway@zulip.com.
|
2015-08-21 11:48:43 +02:00
|
|
|
Zulip voyager configurations will differ.
|
2013-08-08 16:44:40 +02:00
|
|
|
|
|
|
|
Messages meant for Zulip have a special recipient form of
|
|
|
|
|
2013-12-17 22:38:32 +01:00
|
|
|
<stream name>+<regenerable stream token>@streams.zulip.com
|
2013-08-08 16:44:40 +02:00
|
|
|
|
2013-12-17 22:38:32 +01:00
|
|
|
This pattern is configurable via the EMAIL_GATEWAY_PATTERN settings.py
|
|
|
|
variable.
|
2013-08-08 16:44:40 +02:00
|
|
|
|
2017-04-18 17:28:55 +02:00
|
|
|
Run this in a cronjob every N minutes if you have configured Zulip to poll
|
|
|
|
an external IMAP mailbox for messages. The script will then connect to
|
|
|
|
your IMAP server and batch-process all messages.
|
2013-12-17 22:38:32 +01:00
|
|
|
|
2017-04-18 17:28:55 +02:00
|
|
|
We extract and validate the target stream from information in the
|
|
|
|
recipient address and retrieve, forward, and archive the message.
|
2013-12-17 22:38:32 +01:00
|
|
|
|
2013-08-08 16:44:40 +02:00
|
|
|
"""
|
|
|
|
|
2013-12-17 22:38:32 +01:00
|
|
|
|
2013-10-10 21:37:26 +02:00
|
|
|
from __future__ import absolute_import
|
2015-11-01 17:11:06 +01:00
|
|
|
from __future__ import print_function
|
2013-10-10 21:37:26 +02:00
|
|
|
|
2016-07-15 20:07:51 +02:00
|
|
|
import six
|
2016-10-11 14:22:37 +02:00
|
|
|
from zerver.lib.str_utils import force_text
|
2016-07-27 17:17:21 +02:00
|
|
|
from typing import Any, List, Generator
|
2016-06-04 16:52:18 +02:00
|
|
|
|
|
|
|
from argparse import ArgumentParser
|
2013-10-28 15:54:32 +01:00
|
|
|
import os
|
2013-08-08 16:44:40 +02:00
|
|
|
import logging
|
|
|
|
import sys
|
2013-12-17 22:38:18 +01:00
|
|
|
import posix
|
2013-08-08 16:44:40 +02:00
|
|
|
|
|
|
|
from django.conf import settings
|
|
|
|
from django.core.management.base import BaseCommand
|
|
|
|
|
2013-12-17 22:38:18 +01:00
|
|
|
from zerver.lib.queue import queue_json_publish
|
2014-07-25 10:40:40 +02:00
|
|
|
from zerver.lib.email_mirror import logger, process_message, \
|
|
|
|
extract_and_validate, ZulipEmailForwardError, \
|
|
|
|
mark_missed_message_address_as_used, is_missed_message_address
|
2013-08-08 16:44:40 +02:00
|
|
|
|
2016-07-27 17:17:21 +02:00
|
|
|
import email
|
|
|
|
from email.message import Message
|
|
|
|
from imaplib import IMAP4_SSL
|
2013-08-08 16:44:40 +02:00
|
|
|
|
|
|
|
## Setup ##
|
|
|
|
|
|
|
|
log_format = "%(asctime)s: %(message)s"
|
|
|
|
logging.basicConfig(format=log_format)
|
|
|
|
|
|
|
|
formatter = logging.Formatter(log_format)
|
2013-11-01 19:31:00 +01:00
|
|
|
file_handler = logging.FileHandler(settings.EMAIL_MIRROR_LOG_PATH)
|
2013-08-08 16:44:40 +02:00
|
|
|
file_handler.setFormatter(formatter)
|
|
|
|
logger.setLevel(logging.DEBUG)
|
|
|
|
logger.addHandler(file_handler)
|
|
|
|
|
2017-04-18 17:28:55 +02:00
|
|
|
|
2016-07-27 17:17:21 +02:00
|
|
|
def get_imap_messages():
|
|
|
|
# type: () -> Generator[Message, None, None]
|
|
|
|
mbox = IMAP4_SSL(settings.EMAIL_GATEWAY_IMAP_SERVER, settings.EMAIL_GATEWAY_IMAP_PORT)
|
|
|
|
mbox.login(settings.EMAIL_GATEWAY_LOGIN, settings.EMAIL_GATEWAY_PASSWORD)
|
|
|
|
try:
|
|
|
|
mbox.select(settings.EMAIL_GATEWAY_IMAP_FOLDER)
|
|
|
|
try:
|
|
|
|
status, num_ids_data = mbox.search(None, 'ALL') # type: bytes, List[bytes]
|
|
|
|
for msgid in num_ids_data[0].split():
|
|
|
|
status, msg_data = mbox.fetch(msgid, '(RFC822)')
|
|
|
|
msg_as_bytes = msg_data[0][1]
|
|
|
|
if six.PY2:
|
|
|
|
message = email.message_from_string(msg_as_bytes)
|
|
|
|
else:
|
|
|
|
message = email.message_from_bytes(msg_as_bytes)
|
|
|
|
yield message
|
|
|
|
mbox.store(msgid, '+FLAGS', '\\Deleted')
|
|
|
|
mbox.expunge()
|
|
|
|
finally:
|
|
|
|
mbox.close()
|
|
|
|
finally:
|
|
|
|
mbox.logout()
|
2013-08-08 16:44:40 +02:00
|
|
|
|
2017-04-18 17:28:55 +02:00
|
|
|
|
2013-08-08 16:44:40 +02:00
|
|
|
class Command(BaseCommand):
|
2013-12-17 22:21:48 +01:00
|
|
|
help = __doc__
|
2013-08-08 16:44:40 +02:00
|
|
|
|
2014-01-30 18:25:24 +01:00
|
|
|
def handle(self, *args, **options):
|
2016-06-04 16:52:18 +02:00
|
|
|
# type: (*Any, **str) -> None
|
2017-04-18 17:28:55 +02:00
|
|
|
# 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)
|