import logging import aiosmtplib def make_reply(message, to, subject): from email.message import EmailMessage from core.settings import MAIL_DOMAIN reply = EmailMessage() reply["From"] = "noreply@" + MAIL_DOMAIN reply["To"] = to reply["Subject"] = subject reply.set_content(message) return reply async def send_smtp(message, log): log.info('Sending message to %s' % message['To']) await aiosmtplib.send(message, hostname="127.0.0.1", port=25, use_tls=False, start_tls=False) class LMTPHandler: async def handle_RCPT(self, server, session, envelope, address, rcpt_options): from core.settings import MAIL_DOMAIN if not address.endswith('@' + MAIL_DOMAIN): return '550 not relaying to that domain' envelope.rcpt_tos.append(address) return '250 OK' async def handle_DATA(self, server, session, envelope): import email log = logging.getLogger('mail.log') log.info('Message from %s' % envelope.mail_from) log.info('Message for %s' % envelope.rcpt_tos) log.info('Message data:\n') try: parsed = email.message_from_bytes(envelope.content) body = "" if parsed.is_multipart(): for part in parsed.walk(): ctype = part.get_content_type() cdispo = str(part.get('Content-Disposition')) # skip any text/plain (txt) attachments if ctype == 'text/plain' and 'attachment' not in cdispo: body = part.get_payload(decode=True) else: log.info("Attachment", ctype, cdispo) else: body = parsed.get_payload(decode=True) log.info(body) header_from = parsed.get('From') header_to = parsed.get('To') if header_from != envelope.mail_from: log.warning("Header from does not match envelope from") if header_to != envelope.rcpt_tos[0]: log.warning("Header to does not match envelope to") await send_smtp(make_reply("Thank you for your message.", envelope.mail_from, 'Message received'), log) log.info("Sent reply") return '250 Message accepted for delivery' except Exception as e: log.error(e) return '550 Message rejected'