def test_hostname_empty(self): # WARNING: This test _always_ succeeds in Windows. cont = Controller(Sink(), hostname="") try: cont.start() finally: cont.stop()
def main(keywords, antivirus, in_port, server, port): in_port = in_port if in_port else IN_PORT server = server if server else MAIL_SERVER port = port if port else OUT_PORT filters = [] # add keyword filters if specified if keywords: keywords = keywords.split(",") for k in keywords: if not isfile(abspath(k)): print("%s: keywords file does not exist" % k) else: filters.append(KeywordFilter(k)) # add antivirus filter if specified if antivirus: filters.append(AntivirusFilter()) # create a gateway object containing the filters, start the server gateway = Gateway(server, port, filters) controller = Controller(MailHandler(gateway), hostname='localhost', port=in_port) try: controller.start() while True: pass finally: controller.stop()
def test_real_mail_aiosmtpd(self): """ Test sending messages with a real-world SMTPD server """ if aiosmtpd is None: self.skipTest('aiosmtpd not available') # Start an smtp server mail_handler = StashingHandler() controller = Controller(mail_handler, loop=None, hostname='localhost', port=self.smtpd_port) controller.start() # Give it time to settle sleep(0.5) # Initialize a Postman postman = Postman('*****@*****.**', NoLoginSMTP('localhost', self.smtpd_port, None, None)) # Send messages with postman.connect() as c: # Send plaintext message msg = Message(['*****@*****.**'], 'Subject', 'HTML message') c.sendmail(msg) # Send unicode message msg = Message(['*****@*****.**'], u'Håkon', u'Håkon') c.sendmail(msg) # Done controller.stop() # Test self.assertEqual(len(mail_handler.mail), 2)
def test_reuse_loop(self, temp_event_loop): cont = Controller(Sink(), loop=temp_event_loop) assert cont.loop is temp_event_loop try: cont.start() assert cont.smtpd.loop is temp_event_loop finally: cont.stop()
def test_normal_situation(self): cont = Controller(Sink()) try: cont.start() assert cont.smtpd is not None assert cont._thread_exception is None finally: cont.stop()
def test_socket_error_dupe(self, plain_controller, client): contr2 = Controller(Sink(), hostname=Global.SrvAddr.host, port=Global.SrvAddr.port) try: with pytest.raises(socket.error): contr2.start() finally: contr2.stop()
def test_server_attribute(self): controller = Controller(Sink()) self.assertIsNone(controller.server) try: controller.start() self.assertIsNotNone(controller.server) finally: controller.stop() self.assertIsNone(controller.server)
def test_testconn_raises(self, mocker: MockFixture): mocker.patch("socket.socket.recv", side_effect=RuntimeError("MockError")) cont = Controller(Sink(), hostname="") try: with pytest.raises(RuntimeError, match="MockError"): cont.start() finally: cont.stop()
def test_server_attribute(self): controller = Controller(Sink()) assert controller.server is None try: controller.start() assert controller.server is not None finally: controller.stop() assert controller.server is None
async def amain(): handler = RelayHandler() cont = Controller(handler, hostname='', port=8025, authenticator=Authenticator(DB_AUTH)) try: cont.start() finally: cont.stop()
def test_socket_error_dupe(self, plain_controller, client): contr2 = Controller(Sink(), hostname=Global.SrvAddr.host, port=Global.SrvAddr.port) expectedre = r"error while attempting to bind on address" try: with pytest.raises(socket.error, match=expectedre): contr2.start() finally: contr2.stop()
def test_unknown_args_direct(self, silence_event_loop_closed): unknown = "this_is_an_unknown_kwarg" cont = Controller(Sink(), ready_timeout=0.3, **{unknown: True}) expectedre = r"__init__.. got an unexpected keyword argument '" + unknown + r"'" try: with pytest.raises(TypeError, match=expectedre): cont.start() assert cont.smtpd is None assert isinstance(cont._thread_exception, TypeError) finally: cont.stop()
def test_socket_error_default(self): contr1 = Controller(Sink()) contr2 = Controller(Sink()) expectedre = r"error while attempting to bind on address" try: with pytest.raises(socket.error, match=expectedre): contr1.start() contr2.start() finally: contr2.stop() contr1.stop()
def smtp_server(): ''' Provides an asynchronous SMTP server to test mail delivering functionalities. Returns a controller that stores the received messages. ''' handler = TestSMTPHandler() controller = Controller(handler) controller.start() yield handler controller.stop()
def test_unknown_args_inkwargs(self, silence_event_loop_closed: bool): unknown = "this_is_an_unknown_kwarg" cont = Controller(Sink(), ready_timeout=0.3, server_kwargs={unknown: True}) expectedre = r"__init__.. got an unexpected keyword argument '" + unknown + r"'" try: with pytest.raises(TypeError, match=expectedre): cont.start() assert cont.smtpd is None finally: cont.stop()
def test_factory_none(self, mocker: MockFixture, silence_event_loop_closed): # Hypothetical situation where factory() did not raise an Exception # but returned None instead mocker.patch("aiosmtpd.controller.SMTP", return_value=None) cont = Controller(Sink(), ready_timeout=0.3) expectedre = r"factory\(\) returned None" try: with pytest.raises(RuntimeError, match=expectedre): cont.start() assert cont.smtpd is None finally: cont.stop()
def serve(self, port=None, address=None, log_level=logbook.INFO): """Serves the SMTP server on the given port and address.""" port = port or self.port address = address or self.address controller = Controller(InboxServerHandler(self.collator), hostname=address, port=port) try: controller.start() controller._thread.join() except KeyboardInterrupt: finally: controller.stop()
class DummyMailServer(ContextManager['DummyMailServer']): """dummy mail server for testing """ def authenticator(self, _unused_server: SMTP, _unused_session: Session, _unused_envelope: Envelope, mechanism: str, login_data: Tuple[bytes, bytes]) -> AuthResult: if mechanism not in ('LOGIN', 'PLAIN'): return AuthResult(success=False, handled=False) if not isinstance(login_data, LoginPassword): # type: ignore return AuthResult(success=False, handled=False) username = login_data.login.decode() # type: ignore password = login_data.password.decode() # type: ignore if username != self._user or password != self._password: return AuthResult(success=False, handled=False) return AuthResult(success=True) def __enter__(self) -> 'DummyMailServer': # skipcq: PYL-W0201 self._controller = Controller( handler=Debugging(), tls_context=self._tsl_context, # otherwise mail-client have to decode data manually (split by \r\n) decode_data=True, # require starttls from client for authentication require_starttls=True, port=self._port, authenticator=self.authenticator) self._controller.start() return self def __exit__(self, _unused_exc_type: Optional[Type[BaseException]], _unused_exc_value: Optional[BaseException], _unused_traceback: Optional[TracebackType]) -> Optional[bool]: if not self._controller: return None self._controller.stop() # type: ignore return False def __init__(self, user: str, password: str, port: int) -> None: self._user = user self._password = password self._port = port self._tsl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) self._tsl_context.check_hostname = False self._tsl_context.load_cert_chain(certfile=MAIL_CERT, keyfile=MAIL_KEY)
class CapturingAiosmtpdServer: """An async SMTP server / context manager for testing RPC effects.""" def __init__(self): self.messages = [] self.handler = CapturingAiosmtpdHandler(context=self) self.controller = Controller(handler=self.handler, hostname="localhost", port=10025) def __enter__(self): self.controller.start() return self def __exit__(self, *exc): self.controller.stop()
class CapturingAiosmtpdServer: """An async SMTP server / context manager for testing RPC effects.""" def __init__(self): self.messages = [] self.handler = CapturingAiosmtpdHandler(context=self) self.controller = Controller( handler=self.handler, hostname="localhost", port=10025) def __enter__(self): self.controller.start() return self def __exit__(self, *exc): self.controller.stop()
def handle(self, *args, **kwargs): controller = Controller( IncomingMailHandler(), hostname=settings.INCOMING_TASK_MAIL_HOSTNAME, port=settings.INCOMING_TASK_MAIL_PORT, ) self.stdout.write( f"Listening for incoming mail on {controller.hostname}:{controller.port}" ) try: controller.start() while True: time.sleep(0.1) except KeyboardInterrupt: controller.stop()
def serve(self, port=None, address=None, log_level=logbook.INFO): """Serves the SMTP server on the given port and address.""" logbook.StreamHandler(sys.stdout, level=log_level).push_application() port = port or self.port address = address or self.address log.info('Starting SMTP server at {0}:{1}'.format(address, port)) controller = Controller(InboxServerHandler(self.collator), hostname=address, port=port) try: controller.start() controller._thread.join() except KeyboardInterrupt: log.info('Cleaning up') finally: controller.stop()
async def server(*, config: Config) -> t.AsyncIterator[Queue[Message]]: """Manage the LMTP server's lifetime. Args: config: application configuration. Yields: Queue of incoming mail. """ msg_queue: Queue[Message] = Queue() controller = Controller( handler=LmtpHandler(config=config, queue=msg_queue), hostname=config.lmtp_host, port=config.lmtp_port, ) controller.start() logger.info(f"listening on {config.lmtp_host}:{config.lmtp_port}") yield msg_queue controller.stop()
def main(smtp_host, smtp_port, web_host, web_port, develop, debug, maildir): if debug: logging.basicConfig(level=logging.DEBUG) else: logging.basicConfig(level=logging.INFO) logger.info("SMTP server is running on %s:%s", smtp_host, smtp_port) logger.info("Web server is running on %s:%s", web_host, web_port) if develop: logger.info("Running in developer mode") dir_context = TemporaryDirectory if maildir is None else lambda: nullcontext( maildir) with dir_context() as maildir_path: maildir_path = pathlib.Path(maildir_path) maildir_path.mkdir(parents=True, exist_ok=True) logger.info("Mail directory: %s", maildir_path) config = Configuration( smtp_host=smtp_host, smtp_port=smtp_port, web_host=web_host, web_port=web_port, develop=develop, debug=debug, ) maildir = Maildir(maildir_path / "maildir") mailbox = MailboxHandler(maildir_path / "maildir") controller = Controller(mailbox, hostname=config.smtp_host, port=config.smtp_port) web_server = WebServer(config, maildir) mailbox.register_message_observer(web_server) controller.start() web_server.start() controller.stop()
def email_catcher(): class TestHandler: received_mail = [] async def handle_RCPT(self, server, session, envelope, address, rcpt_options): if not address.endswith("@example.com"): return "550 not relaying to that domain" envelope.rcpt_tos.append(address) return "250 OK" async def handle_DATA(self, server, session, envelope): self.received_mail.append(envelope) return "250 Message accepted for delivery" controller = Controller(TestHandler(), hostname="localhost", port=8025) controller.start() try: yield controller finally: controller.stop()
def start(reload: 'Make server autoreload (Dev only)' = False, ): """ Start byemail """ settings.init_settings() controller = Controller(smtpserver.MSGHandler(), **settings.SMTP_CONF) controller.start() asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) loop = asyncio.get_event_loop() app = httpserver.get_app() server = app.create_server(**settings.HTTP_CONF) asyncio.ensure_future(server) try: print("Server started on %s:%d" % (controller.hostname, controller.port)) loop.run_forever() except KeyboardInterrupt: print("Stopping") controller.stop()
def test_noexc_smtpd_missing(self, mocker, silence_event_loop_closed): # Hypothetical situation where factory() failed but no # Exception was generated. cont = Controller(Sink()) def hijacker(*args, **kwargs): cont._thread_exception = None # Must still return an (unmocked) _FakeServer to prevent a whole bunch # of messy exceptions, although they doesn't affect the test at all. return _FakeServer(cont.loop) mocker.patch("aiosmtpd.controller._FakeServer", side_effect=hijacker) mocker.patch("aiosmtpd.controller.SMTP", side_effect=RuntimeError("Simulated Failure")) expectedre = r"Unknown Error, failed to init SMTP server" try: with pytest.raises(RuntimeError, match=expectedre): cont.start() assert cont.smtpd is None assert cont._thread_exception is None finally: cont.stop()
def smtp_messages(): message_q: Queue = Queue() ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) ssl_context.load_cert_chain(THIS_DIR / "test_cert.pem", THIS_DIR / "test_key.pem") class SMTPHandler: async def handle_DATA(self, _server, session, envelope): peer = session.peer mail_from = envelope.mail_from rcpt_tos = envelope.rcpt_tos data = envelope.content # type: bytes msg = SMTPMessage(peer, mail_from, rcpt_tos, data) message_q.put(msg) return "250 OK" def smtp_authenticator(_server, _session, _envelope, mechanism, auth_data): auth_failed = smtp.AuthResult(success=False, handled=False) if mechanism != "LOGIN": return auth_failed username = auth_data.login.decode("utf-8") password = auth_data.password.decode("utf-8") if username != config.smtp_user or password != config.smtp_pass: return auth_failed return smtp.AuthResult(success=True) controller = SMTPController(SMTPHandler(), hostname=SMTP_TEST_SERVER_HOST, port=SMTP_TEST_SERVER_PORT, auth_required=True, authenticator=smtp_authenticator, tls_context=ssl_context) controller.start() try: yield queue_consumer(message_q) finally: controller.stop()
def test_real_mail_aiosmtpd(self): """ Test sending messages with a real-world SMTPD server """ if aiosmtpd is None: self.skipTest('aiosmtpd not available') # Start an smtp server mail_handler = StashingHandler() controller = Controller(mail_handler, loop=None, hostname='localhost', port=self.smtpd_port) controller.start() # Give it time to settle sleep(0.5) # Initialize a Postman postman = Postman( '*****@*****.**', NoLoginSMTP('localhost', self.smtpd_port, None, None)) # Send messages with postman.connect() as c: # Send plaintext message msg = Message(['*****@*****.**'], 'Subject', 'HTML message') c.sendmail(msg) # Send unicode message msg = Message(['*****@*****.**'], u'Håkon', u'Håkon') c.sendmail(msg) # Done controller.stop() # Test self.assertEqual(len(mail_handler.mail), 2)
log.info("Quitting...") self.quit = True if __name__ == "__main__": log.debug(", ".join([f"{k}={v}" for k, v in config.items()])) # If there's a dir called log - set up a filehandler if os.path.exists("log"): log.info("Setting up a filehandler") fh = logging.FileHandler("log/emqtt.log") fh.setFormatter(formatter) log.addHandler(fh) loop = asyncio.get_event_loop() c = Controller( handler=EMQTTHandler(loop), loop=loop, hostname="0.0.0.0", port=config["SMTP_PORT"], ) c.start() log.info("Running") try: while not c.handler.quit: time.sleep(0.5) c.stop() except: c.stop() raise
class AiosmtpdServerManager: def __init__(self, handler: AIOHandler, auth: bool = False) -> None: self.handler = handler self.auth = auth def __enter__(self) -> Controller: auth_kwargs = {} if self.auth: auth_kwargs = { "auth_required": True, "authenticator": authenticator, } if EmailSettings.connection_security in [ ConnectionSecurity.STARTTLS, ConnectionSecurity.SSLTLS, ]: ssl_context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH) ssl_context.load_default_certs(purpose=ssl.Purpose.SERVER_AUTH) ssl_context.load_cert_chain("cert.pem", "key.pem") ssl_context.check_hostname = False ssl_context.verify_mode = ssl.VerifyMode.CERT_NONE if EmailSettings.connection_security == ConnectionSecurity.SSLTLS: # This is a hack: The aiosmtpd library does not issue AUTH in EHLO, if not starttls is used. # For other methods (SSL/TLS and NONE) setting auth_require_tls allows AUTH. The intention is to # allow AUTH before TLS (which is ok for NONE), but a hack for SSL/TLS since we have an # encrypted connection if self.auth: auth_kwargs["auth_require_tls"] = False self.controller = Controller( self.handler, EmailSettings.host, EmailSettings.port, server_hostname="127.0.0.1", ssl_context=ssl_context, **auth_kwargs, # type: ignore ) else: self.controller = Controller( self.handler, EmailSettings.host, EmailSettings.port, server_hostname="127.0.0.1", require_starttls=True, tls_context=ssl_context, **auth_kwargs, # type: ignore ) else: if self.auth: auth_kwargs["auth_require_tls"] = False self.controller = Controller( self.handler, EmailSettings.host, EmailSettings.port, **auth_kwargs # type: ignore ) self.controller.start() return self.controller def __exit__( self, exc_type: Optional[Type[BaseException]], exc_value: Optional[BaseException], exc_traceback: Optional[TracebackType], ) -> None: if self.controller.loop.is_running(): self.controller.stop(no_assert=True)
class CustomHandler: async def handle_RCPT(self, server, session, envelope, address, rcpt_options): envelope.rcpt_tos.append(address) return '250 OK' async def handle_DATA(self, server, session, envelope): print('Message from', envelope.mail_from) print('Message for', envelope.rcpt_tos) print('Message data:\n') print(envelope.content.decode('utf8', errors='replace')) return '250 Message accepted for delivery' controller = Controller(CustomHandler(), None, '0.0.0.0', 8025) ts = time.time() st = datetime.datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S') print('Running SMTP Server ', st) controller.start() loop = asyncio.get_event_loop() try: loop.run_forever() finally: loop.run_until_complete(loop.shutdown_asyncgens()) controller.stop() loop.close()