1# -*- coding: utf-8 -*-
2import asyncore
3import sys
4from logging import getLogger
5from smtpd import SMTPServer
6
7from django.core.management.base import BaseCommand, CommandError
8
9from django_extensions.management.utils import setup_logger, signalcommand
10
11logger = getLogger(__name__)
12
13
14class ExtensionDebuggingServer(SMTPServer):
15    """Duplication of smtpd.DebuggingServer, but using logging instead of print."""
16
17    # Do something with the gathered message
18    def process_message(self, peer, mailfrom, rcpttos, data, **kwargs):
19        """Output will be sent to the module logger at INFO level."""
20        inheaders = 1
21        lines = data.split('\n')
22        logger.info('---------- MESSAGE FOLLOWS ----------')
23        for line in lines:
24            # headers first
25            if inheaders and not line:
26                logger.info('X-Peer: %s' % peer[0])
27                inheaders = 0
28            logger.info(line)
29        logger.info('------------ END MESSAGE ------------')
30
31
32class Command(BaseCommand):
33    help = "Starts a test mail server for development."
34    args = '[optional port number or ippaddr:port]'
35
36    requires_system_checks = False
37
38    def add_arguments(self, parser):
39        super().add_arguments(parser)
40        parser.add_argument(
41            '--output', dest='output_file', default=None,
42            help='Specifies an output file to send a copy of all messages (not flushed immediately).'
43        )
44        parser.add_argument(
45            '--use-settings', dest='use_settings',
46            action='store_true', default=False,
47            help='Uses EMAIL_HOST and HOST_PORT from Django settings.'
48        )
49
50    @signalcommand
51    def handle(self, addrport='', *args, **options):
52        if not addrport:
53            if options['use_settings']:
54                from django.conf import settings
55                addr = getattr(settings, 'EMAIL_HOST', '')
56                port = str(getattr(settings, 'EMAIL_PORT', '1025'))
57            else:
58                addr = ''
59                port = '1025'
60        else:
61            try:
62                addr, port = addrport.split(':')
63            except ValueError:
64                addr, port = '', addrport
65        if not addr:
66            addr = '127.0.0.1'
67
68        if not port.isdigit():
69            raise CommandError("%r is not a valid port number." % port)
70        else:
71            port = int(port)
72
73        # Add console handler
74        setup_logger(logger, stream=self.stdout, filename=options['output_file'])
75
76        def inner_run():
77            quit_command = (sys.platform == 'win32') and 'CTRL-BREAK' or 'CONTROL-C'
78            print("Now accepting mail at %s:%s -- use %s to quit" % (addr, port, quit_command))
79            ExtensionDebuggingServer((addr, port), None, decode_data=True)
80            asyncore.loop()
81
82        try:
83            inner_run()
84        except KeyboardInterrupt:
85            pass
86