2019-01-09 12:27:29 +01:00
|
|
|
import email
|
2020-01-14 21:59:46 +01:00
|
|
|
import os
|
2019-01-09 12:27:29 +01:00
|
|
|
from email.message import Message
|
|
|
|
from email.mime.text import MIMEText
|
2020-01-14 21:59:46 +01:00
|
|
|
from typing import Dict, Optional
|
2019-01-09 12:27:29 +01:00
|
|
|
|
2020-01-14 21:59:46 +01:00
|
|
|
import ujson
|
2019-01-09 12:27:29 +01:00
|
|
|
from django.conf import settings
|
|
|
|
from django.core.management.base import CommandParser
|
|
|
|
|
2019-03-16 12:28:18 +01:00
|
|
|
from zerver.lib.email_mirror import mirror_email_message
|
2019-03-21 10:24:56 +01:00
|
|
|
from zerver.lib.email_mirror_helpers import encode_email_address
|
2020-01-14 21:59:46 +01:00
|
|
|
from zerver.lib.management import CommandError, ZulipBaseCommand
|
|
|
|
from zerver.models import Realm, get_realm, get_stream
|
2019-03-16 12:28:18 +01:00
|
|
|
|
2019-01-09 12:27:29 +01:00
|
|
|
# This command loads an email from a specified file and sends it
|
|
|
|
# to the email mirror. Simple emails can be passed in a JSON file,
|
|
|
|
# Look at zerver/tests/fixtures/email/1.json for an example of how
|
|
|
|
# it should look. You can also pass a file which has the raw email,
|
|
|
|
# for example by writing an email.message.Message type object
|
|
|
|
# to a file using as_string() or as_bytes() methods, or copy-pasting
|
|
|
|
# the content of "Show original" on an email in Gmail.
|
|
|
|
# See zerver/tests/fixtures/email/1.txt for a very simple example,
|
|
|
|
# but anything that the message_from_binary_file function
|
|
|
|
# from the email library can parse should work.
|
2020-03-28 01:25:56 +01:00
|
|
|
# Value of the TO: header doesn't matter, as it is overridden
|
2019-01-09 12:27:29 +01:00
|
|
|
# by the command in order for the email to be sent to the correct stream.
|
|
|
|
|
|
|
|
class Command(ZulipBaseCommand):
|
|
|
|
help = """
|
|
|
|
Send specified email from a fixture file to the email mirror
|
|
|
|
Example:
|
|
|
|
./manage.py send_to_email_mirror --fixture=zerver/tests/fixtures/emails/filename
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
def add_arguments(self, parser: CommandParser) -> None:
|
|
|
|
parser.add_argument('-f', '--fixture',
|
|
|
|
dest='fixture',
|
|
|
|
type=str,
|
|
|
|
help='The path to the email message you\'d like to send '
|
|
|
|
'to the email mirror.\n'
|
|
|
|
'Accepted formats: json or raw email file. '
|
|
|
|
'See zerver/tests/fixtures/email/ for examples')
|
|
|
|
parser.add_argument('-s', '--stream',
|
|
|
|
dest='stream',
|
|
|
|
type=str,
|
|
|
|
help='The name of the stream to which you\'d like to send '
|
|
|
|
'the message. Default: Denmark')
|
|
|
|
|
|
|
|
self.add_realm_args(parser, help="Specify which realm to connect to; default is zulip")
|
|
|
|
|
2019-07-29 00:38:14 +02:00
|
|
|
def handle(self, **options: Optional[str]) -> None:
|
2019-01-09 12:27:29 +01:00
|
|
|
if options['fixture'] is None:
|
|
|
|
self.print_help('./manage.py', 'send_to_email_mirror')
|
2019-05-03 23:20:39 +02:00
|
|
|
raise CommandError
|
2019-01-09 12:27:29 +01:00
|
|
|
|
|
|
|
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)
|
|
|
|
|
python: Convert assignment type annotations to Python 3.6 style.
This commit was split by tabbott; this piece covers the vast majority
of files in Zulip, but excludes scripts/, tools/, and puppet/ to help
ensure we at least show the right error messages for Xenial systems.
We can likely further refine the remaining pieces with some testing.
Generated by com2ann, with whitespace fixes and various manual fixes
for runtime issues:
- invoiced_through: Optional[LicenseLedger] = models.ForeignKey(
+ invoiced_through: Optional["LicenseLedger"] = models.ForeignKey(
-_apns_client: Optional[APNsClient] = None
+_apns_client: Optional["APNsClient"] = None
- notifications_stream: Optional[Stream] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
- signup_notifications_stream: Optional[Stream] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
+ notifications_stream: Optional["Stream"] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
+ signup_notifications_stream: Optional["Stream"] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
- author: Optional[UserProfile] = models.ForeignKey('UserProfile', blank=True, null=True, on_delete=CASCADE)
+ author: Optional["UserProfile"] = models.ForeignKey('UserProfile', blank=True, null=True, on_delete=CASCADE)
- bot_owner: Optional[UserProfile] = models.ForeignKey('self', null=True, on_delete=models.SET_NULL)
+ bot_owner: Optional["UserProfile"] = models.ForeignKey('self', null=True, on_delete=models.SET_NULL)
- default_sending_stream: Optional[Stream] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
- default_events_register_stream: Optional[Stream] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
+ default_sending_stream: Optional["Stream"] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
+ default_events_register_stream: Optional["Stream"] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
-descriptors_by_handler_id: Dict[int, ClientDescriptor] = {}
+descriptors_by_handler_id: Dict[int, "ClientDescriptor"] = {}
-worker_classes: Dict[str, Type[QueueProcessingWorker]] = {}
-queues: Dict[str, Dict[str, Type[QueueProcessingWorker]]] = {}
+worker_classes: Dict[str, Type["QueueProcessingWorker"]] = {}
+queues: Dict[str, Dict[str, Type["QueueProcessingWorker"]]] = {}
-AUTH_LDAP_REVERSE_EMAIL_SEARCH: Optional[LDAPSearch] = None
+AUTH_LDAP_REVERSE_EMAIL_SEARCH: Optional["LDAPSearch"] = None
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-22 01:09:50 +02:00
|
|
|
data: Dict[str, str] = {}
|
2019-03-16 12:28:18 +01:00
|
|
|
data['recipient'] = str(message['To']) # Need str() here to avoid mypy throwing an error
|
|
|
|
data['msg_text'] = message.as_string()
|
|
|
|
mirror_email_message(data)
|
2019-01-09 12:27:29 +01:00
|
|
|
|
|
|
|
def _does_fixture_path_exist(self, fixture_path: str) -> bool:
|
|
|
|
return os.path.exists(fixture_path)
|
|
|
|
|
|
|
|
def _parse_email_json_fixture(self, fixture_path: str) -> Message:
|
|
|
|
with open(fixture_path) as fp:
|
|
|
|
json_content = ujson.load(fp)[0]
|
|
|
|
|
|
|
|
message = MIMEText(json_content['body'])
|
|
|
|
message['From'] = json_content['from']
|
|
|
|
message['Subject'] = json_content['subject']
|
|
|
|
return message
|
|
|
|
|
|
|
|
def _parse_email_fixture(self, fixture_path: str) -> Message:
|
|
|
|
if not self._does_fixture_path_exist(fixture_path):
|
2020-06-09 00:25:09 +02:00
|
|
|
raise CommandError(f'Fixture {fixture_path} does not exist')
|
2019-01-09 12:27:29 +01:00
|
|
|
|
|
|
|
if fixture_path.endswith('.json'):
|
|
|
|
message = self._parse_email_json_fixture(fixture_path)
|
|
|
|
else:
|
|
|
|
with open(fixture_path, "rb") as fp:
|
|
|
|
message = email.message_from_binary_file(fp)
|
|
|
|
|
|
|
|
return message
|
|
|
|
|
|
|
|
def _prepare_message(self, message: Message, realm: Realm, stream_name: str) -> None:
|
|
|
|
stream = get_stream(stream_name, realm)
|
|
|
|
|
2020-01-14 15:31:53 +01:00
|
|
|
# The block below ensures that the imported email message doesn't have any recipient-like
|
|
|
|
# headers that are inconsistent with the recipient we want (the stream address).
|
2020-03-20 04:15:24 +01:00
|
|
|
recipient_headers = ["X-Gm-Original-To", "Delivered-To", "Envelope-To",
|
2020-01-14 15:31:53 +01:00
|
|
|
"Resent-To", "Resent-CC", "CC"]
|
2019-01-09 12:27:29 +01:00
|
|
|
for header in recipient_headers:
|
|
|
|
if header in message:
|
|
|
|
del message[header]
|
|
|
|
message[header] = encode_email_address(stream)
|
|
|
|
|
2020-01-14 15:31:53 +01:00
|
|
|
if 'To' in message:
|
|
|
|
del message['To']
|
2019-01-09 12:27:29 +01:00
|
|
|
message['To'] = encode_email_address(stream)
|