commit 032536ccc90af6558c776a62b19e6f3824b324ff Author: itismadness Date: Tue Feb 20 16:57:50 2018 +1200 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..28221e0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +__pycache__/ +messages/* +!messages/.gitkeep +.idea/ \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..ba04192 --- /dev/null +++ b/README.md @@ -0,0 +1,29 @@ +PostBox +======= + +Smtpd server (via [aiosmtpd](https://github.com/aio-libs/aiosmtpd)) with a [falcon](https://falconframework.org/) +frontend. Incoming messages are saved as serialized JSON objects in the `messages/` folder. + +Requirements +------------ +* Python 3.5+ +* aiosmtpd +* falcon + +Installation +------------ +``` +pip3 install -r requirements.txt +``` + +Usage +----- +To start the smtpd server: +``` +./smtpd.py +``` + +To start the web API: +``` +gunicorn -b '127.0.0.1:8000' server:application +``` \ No newline at end of file diff --git a/generate_mail.py b/generate_mail.py new file mode 100755 index 0000000..417d01d --- /dev/null +++ b/generate_mail.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python3 + +from datetime import datetime +import json +import uuid +import os + + +class DateTimeEncoder(json.JSONEncoder): + def default(self, obj): + if isinstance(obj, datetime): + return obj.isoformat() + return json.JSONEncoder.default(self, obj) + + +with open(os.path.join('messages', str(uuid.uuid4().hex)), 'w') as open_file: + json.dump({'mail_from': 'test@example.com', + 'rcpt_tos': 'person@example.com', + 'message': 'This is an email message!', + 'timestamp': datetime.utcnow().isoformat()}, + open_file, + cls=DateTimeEncoder) diff --git a/messages/.gitkeep b/messages/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..0bf30ea --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +falcon +aiosmtpd +gunicorn \ No newline at end of file diff --git a/server.py b/server.py new file mode 100644 index 0000000..f7b90b7 --- /dev/null +++ b/server.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 + +import json +import os +import falcon + + +DATA_PATH = 'messages' + + +class Resource(object): + # noinspection PyMethodMayBeStatic + def on_get(self, _, resp): + response = [] + for entry in os.scandir(DATA_PATH): + if entry.name == '.gitkeep': + continue + with open(entry.path) as open_file: + response.append(json.load(open_file)) + os.remove(entry.path) + resp.body = str.encode(json.dumps(response)) + resp.status = falcon.HTTP_200 + + +application = falcon.API() +application.add_route('/', Resource()) diff --git a/smtpd.py b/smtpd.py new file mode 100755 index 0000000..3075a41 --- /dev/null +++ b/smtpd.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 + +from datetime import datetime +import json +import os +import uuid + +from aiosmtpd.controller import Controller + +HOST = '127.0.0.1' +PORT = 10025 +DATA_PATH = 'messages' + + +class Handler(object): + # noinspection PyPep8Naming,PyMethodMayBeStatic + async def handle_DATA(self, _, session, envelope): + # noinspection PyBroadException + try: + with open(os.path.join('messages', str(uuid.uuid4().hex)), 'w') as open_file: + json.dump({'mail_from': envelope.mail_from, + 'rcpt_tos': envelope.rcpt_tos, + 'message': envelope.content.decode('utf-8'), + 'timestamp': datetime.utcnow().isoformat()}, + open_file) + return '250 OK' + except Exception: + return '500 Could not process your message' + + +if __name__ == '__main__': + handler = Handler() + controller = Controller(handler, hostname='127.0.0.1', port=10025) + # noinspection PyBroadException + try: + controller.start() + input('Running smtpd server on {}:{}'.format(HOST, PORT)) + except Exception: + controller.stop()