#!/usr/bin/env python3 """ Forward messages sent to the configured email gateway to Zulip. For zulip.com, messages to that address go to the Inbox of emailgateway@zulip.com. Zulip voyager configurations will differ. Messages meant for Zulip have a special recipient form of +@streams.zulip.com This pattern is configurable via the EMAIL_GATEWAY_PATTERN settings.py variable. 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. We extract and validate the target stream from information in the recipient address and retrieve, forward, and archive the message. """ from __future__ import absolute_import from __future__ import print_function import six from typing import Any, List, Generator import logging from django.conf import settings from django.core.management.base import BaseCommand from zerver.lib.queue import queue_json_publish from zerver.lib.email_mirror import logger, process_message, \ extract_and_validate, ZulipEmailForwardError, \ mark_missed_message_address_as_used, is_missed_message_address import email from email.message import Message from imaplib import IMAP4_SSL ## Setup ## log_format = "%(asctime)s: %(message)s" logging.basicConfig(format=log_format) formatter = logging.Formatter(log_format) file_handler = logging.FileHandler(settings.EMAIL_MIRROR_LOG_PATH) file_handler.setFormatter(formatter) logger.setLevel(logging.DEBUG) logger.addHandler(file_handler) 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() class Command(BaseCommand): help = __doc__ def handle(self, *args, **options): # type: (*Any, **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)