Exemple #1
0
    def handle(self, *args, **options):
        # type: (*Any, **str) -> None
        if six.PY3:
            print(py3_warning)
            return
        rcpt_to = 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.
            message = 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": message,
                "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)
            reactor.callLater(0, main)
            reactor.run()
    def test_get_missed_message_token(self) -> None:
        with self.settings(EMAIL_GATEWAY_PATTERN="*****@*****.**"):
            address = 'mm' + ('x' * 32) + '@example.com'
            self.assertTrue(is_missed_message_address(address))
            token = get_missed_message_token_from_address(address)
            self.assertEqual(token, 'x' * 32)

            # This next section was a bug at one point--we'd treat ordinary
            # user addresses that happened to begin with "mm" as being
            # the special mm+32chars tokens.
            address = '*****@*****.**'
            self.assertFalse(is_missed_message_address(address))
            with self.assertRaises(ZulipEmailForwardError):
                get_missed_message_token_from_address(address)

            # Now test the case where we our address does not match the
            # EMAIL_GATEWAY_PATTERN.
            # This used to crash in an ugly way; we want to throw a proper
            # exception.
            address = '*****@*****.**'
            self.assertFalse(is_missed_message_address(address))
            with self.assertRaises(ZulipEmailForwardError):
                get_missed_message_token_from_address(address)
Exemple #3
0
    def handle(self, *args, **options):
        # type: (*Any, **str) -> None
        if six.PY3:
            print(py3_warning)
            return
        rcpt_to = 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.
            message = 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": message,
                        "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)
            reactor.callLater(0, main)
            reactor.run()
Exemple #4
0
    def consume(self, event: Mapping[str, Any]) -> None:
        rcpt_to = event['rcpt_to']
        if not is_missed_message_address(rcpt_to):
            # Missed message addresses are one-time use, so we don't need
            # to worry about emails to them resulting in message spam.
            recipient_realm = decode_stream_email_address(rcpt_to)[0].realm
            try:
                rate_limit_mirror_by_realm(recipient_realm)
            except RateLimited:
                msg = email.message_from_string(event["message"])
                logger.warning("MirrorWorker: Rejecting an email from: %s "
                               "to realm: %s - rate limited." %
                               (msg['From'], recipient_realm.name))
                return

        mirror_email(email.message_from_string(event["message"]),
                     rcpt_to=rcpt_to)
Exemple #5
0
    def consume(self, event: Mapping[str, Any]) -> None:
        rcpt_to = event['rcpt_to']
        msg = email.message_from_bytes(
            base64.b64decode(event["msg_base64"]),
            policy=email.policy.default,
        )
        assert isinstance(msg, EmailMessage)  # https://github.com/python/typeshed/issues/2417
        if not is_missed_message_address(rcpt_to):
            # Missed message addresses are one-time use, so we don't need
            # to worry about emails to them resulting in message spam.
            recipient_realm = decode_stream_email_address(rcpt_to)[0].realm
            try:
                rate_limit_mirror_by_realm(recipient_realm)
            except RateLimited:
                logger.warning("MirrorWorker: Rejecting an email from: %s "
                               "to realm: %s - rate limited.",
                               msg['From'], recipient_realm.name)
                return

        mirror_email(msg, rcpt_to=rcpt_to)