#!/usr/bin/env python3 import asyncio import logging import uvicorn from aiosmtpd.controller import Controller, UnixSocketController from aiosmtpd.lmtp import LMTP async def handle_echo(reader, writer): # session = Session() data = await reader.read(100) message = data.decode() addr = writer.get_extra_info('peername') print(f"Received {message!r} from {addr!r}") print(f"Send: {message!r}") writer.write(data) await writer.drain() print("Close the connection") writer.close() await writer.wait_closed() class ExampleHandler: 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): print('Message from %s' % envelope.mail_from) print('Message for %s' % envelope.rcpt_tos) print('Message data:\n') for ln in envelope.content.decode('utf8', errors='replace').splitlines(): print(f'> {ln}'.strip()) print() print('End of message') return '250 Message accepted for delivery' class LTMPController(Controller): def factory(self): return LMTP(self.handler) class UnixSocketLMTPController(UnixSocketController): def factory(self): return LMTP(self.handler) class UvicornServer(uvicorn.Server): def install_signal_handlers(self): pass async def web(): log_config = uvicorn.config.LOGGING_CONFIG log_config["handlers"]["default"] = {"class": "logging.FileHandler", "filename": "web.log", "formatter": "default"} log_config["handlers"]["access"] = {"class": "logging.FileHandler", "filename": "web-access.log", "formatter": "access"} config = uvicorn.Config("core.asgi:application", uds="web.sock", log_config=log_config) server = UvicornServer(config=config) await server.serve() async def tcp(): log = logging.getLogger('test') log.info("Starting TCP server") server = await asyncio.start_unix_server(handle_echo, path='test.sock') addrs = ', '.join(str(sock.getsockname()) for sock in server.sockets) log.info(f'Serving on {addrs}') async with server: await server.serve_forever() log.info("TCP done") async def lmtp(): log = logging.getLogger('lmtp') log.info("Starting LMTP server") cont = UnixSocketLMTPController(ExampleHandler(), unix_socket='lmtp.sock') cont.start() log.info("LMTP done") async def shutdown(sig, loop): log = logging.getLogger() log.info(f"Received exit signal {sig.name}...") tasks = [t for t in asyncio.all_tasks() if t is not asyncio.current_task()] [task.cancel() for task in tasks] log.info(f"Cancelling {len(tasks)} outstanding tasks") await asyncio.wait_for(loop.shutdown_asyncgens(), timeout=10) loop.stop() log.info("Shutdown complete.") def main(): import sdnotify import signal import setproctitle setproctitle.setproctitle("c3lf-sys3") logging.basicConfig(filename='server.log', level=logging.DEBUG, encoding='utf-8') logging.basicConfig(filename='test.log', level=logging.DEBUG, encoding='utf-8') logging.basicConfig(filename='lmtp.log', level=logging.DEBUG, encoding='utf-8') log = logging.getLogger() log.info("Starting server") loop = asyncio.get_event_loop() loop.add_signal_handler(signal.SIGTERM, lambda: asyncio.create_task(shutdown(signal.SIGTERM, loop))) loop.add_signal_handler(signal.SIGINT, lambda: asyncio.create_task(shutdown(signal.SIGINT, loop))) loop.create_task(web()) loop.create_task(tcp()) loop.create_task(lmtp()) n = sdnotify.SystemdNotifier() n.notify("READY=1") log.info("Server ready") try: loop.run_forever() finally: loop.close() logging.info("Server stopped") if __name__ == '__main__': main()