130 lines
4 KiB
Python
130 lines
4 KiB
Python
#!/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()
|