lycying 6 years ago
commit
632d9192ef
10 changed files with 412 additions and 0 deletions
  1. 106 0
      .gitignore
  2. 6 0
      cfg.ini
  3. 55 0
      data.py
  4. 28 0
      main.py
  5. 2 0
      requirements.txt
  6. 42 0
      smtpx.py
  7. 124 0
      static/index.html
  8. 12 0
      tests/send_test.py
  9. 3 0
      tests/web_test.py
  10. 34 0
      web.py

+ 106 - 0
.gitignore

@@ -0,0 +1,106 @@
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+#  Usually these files are written by a python script from a template
+#  before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+.hypothesis/
+.pytest_cache/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# pyenv
+.python-version
+
+# celery beat schedule file
+celerybeat-schedule
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.idea
+tests/core/api/cfg.ini

+ 6 - 0
cfg.ini

@@ -0,0 +1,6 @@
+[smtpd]
+host=127.0.0.1
+port=8025
+
+[rest]
+port=14000

+ 55 - 0
data.py

@@ -0,0 +1,55 @@
+import datetime
+import sqlite3
+import json
+
+
+class DataAccess:
+    def __init__(self):
+        self.conn = sqlite3.connect(":memory:", check_same_thread=False);
+        c = self.conn.cursor()
+        c.execute("CREATE TABLE msg(frm TEXT, to0 TEXT, tos TEXT, subject TEXT, content TEXT,createDate timestamp)");
+        c.execute("CREATE INDEX index_frm ON msg (frm);")
+        c.execute("CREATE INDEX index_to0 ON msg (to0);")
+        self.conn.commit()
+
+    def store_msg(self, msg):
+        c = self.conn.cursor()
+        c.execute("insert into msg values(?,?,?,?,?,?)",
+                  (msg['from'], msg['to'][0], json.dumps(msg['to']), msg['subject'], msg['content'],
+                   datetime.datetime.now()))
+        self.conn.commit()
+
+    def read_from(self, frm):
+        c = self.conn.cursor()
+        t = c.execute("select * from msg where frm = ? limit 100", (frm,))
+        rs = t.fetchall()
+        return self.transform(rs)
+
+    def read_to(self, to):
+        c = self.conn.cursor()
+        t = c.execute("select * from msg where to0 = ? limit 100", (to,))
+        rs = t.fetchall()
+        return self.transform(rs)
+
+    def read_all(self):
+        c = self.conn.cursor()
+        t = c.execute("select * from msg limit 100")
+        rs = t.fetchall()
+        return self.transform(rs)
+
+    def transform(self, all):
+        rs = []
+        for item in all:
+            p = {
+                "from": item[0],
+                "to0": item[1],
+                "to": json.loads(item[2]),
+                "subject": item[3],
+                "content": item[4],
+                "time": item[5],
+            }
+            rs.append(p)
+        return rs
+
+
+dataInstance = DataAccess()

+ 28 - 0
main.py

@@ -0,0 +1,28 @@
+from smtpx import CrazySrvHandler
+from web import web_start
+
+from aiosmtpd.controller import Controller
+from aiosmtpd.smtp import SMTP
+import configparser
+
+if __name__ == "__main__":
+    cf = configparser.ConfigParser()
+    cf.read("cfg.ini")
+
+    smtpd_host = cf.get("smtpd", "host")
+    smtpd_port = cf.getint("smtpd", "port")
+
+    rest_host = smtpd_host
+    rest_port = cf.getint("rest", "port")
+
+    handler = CrazySrvHandler()
+    controller = Controller(handler, hostname=smtpd_host, port=smtpd_port)
+    controller.factory = lambda: SMTP(handler, enable_SMTPUTF8=True)
+
+    try:
+        controller.start()
+        web_start(rest_host, rest_port)
+    except KeyboardInterrupt:
+        print("Shutting down")
+    finally:
+        controller.stop()

+ 2 - 0
requirements.txt

@@ -0,0 +1,2 @@
+aiosmtpd
+flask

+ 42 - 0
smtpx.py

@@ -0,0 +1,42 @@
+import email
+from email.message import Message
+from data import dataInstance
+
+
+def message_to_display(message: Message):
+    result = ''
+    if message.is_multipart():
+        for sub_message in message.get_payload():
+            result += message_to_display(sub_message)
+    else:
+        result = f"Content-type: {message.get_content_type()}\n" \
+                 f"{message.get_payload()}\n" + "*" * 76 + '\n'
+    return result
+
+
+class CrazySrvHandler:
+    dao = dataInstance
+
+    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):
+        mail_from = envelope.mail_from
+        rcpt_tos = envelope.rcpt_tos
+        message: Message = email.message_from_bytes(envelope.content)
+        content = message_to_display(message)
+        subject = message['Subject']
+
+        obj = {
+            "from": mail_from,
+            "to": rcpt_tos,
+            "subject": subject,
+            "content": content
+        }
+
+        self.dao.store_msg(obj)
+
+        print(self.dao.read_from(obj['from']))
+
+        return '250 Message accepted for delivery'

+ 124 - 0
static/index.html

@@ -0,0 +1,124 @@
+<html>
+
+    <head>
+        <title>crazy email recv srv</title>
+        <link href="https://cdn.bootcss.com/bootstrap/3.2.0/css/bootstrap.min.css" rel="stylesheet">
+        <link href="https://cdn.bootcss.com/bootstrap-table/1.12.1/bootstrap-table.min.css" rel="stylesheet">
+    </head>
+
+    <body>
+        <div class="container">
+            <div class="jumbotron" style="padding-top:2px;padding-bottom:2px">
+                <h1>Crazy Email Reciver Service</h1>
+                <p class="lead">Use this mock your email server , just for recive . USAGE: such as batch register . This page only show the most recently emails ( about 100 items) 
+                <button id="from_btn" type="submit" class="btn btn-default">github</button> <a target="_blank" href="https://github.com/lycying/crazy-email-recv-srv">https://github.com/lycying/crazy-email-recv-srv</a>
+                </p>
+            </div>
+            <div id="toolbar">
+                <div class="form-inline" role="form">
+                    <div class="form-group">
+                        <input id="from" class="form-control" type="text" placeholder="From">
+                    </div>
+                    <button id="from_btn" type="submit" class="btn btn-success">Search</button>
+
+                    &nbsp;
+                    &nbsp;
+                    &nbsp;
+                    &nbsp;
+                    &nbsp;
+                    &nbsp;
+
+
+                    <div class="form-group">
+                        <input id="to" class="form-control" type="text" placeholder="To">
+                    </div>
+                    <button id="to_btn" type="submit" class="btn btn-success">Search</button>
+                </div>
+
+            </div>
+            <table id="table"
+                   data-toolbar="#toolbar"
+                   data-show-columns="true">
+                <thead>
+                    <tr>
+                        <th data-field="from">From</th>
+                        <th data-field="to0">To[0]</th>
+                        <th data-field="to">To[ALL]</th>
+                        <th data-field="subject">Subject</th>
+                        <th data-field="content">Content</th>
+                        <th data-field="time">Date</th>
+
+                    </tr>
+                </thead>
+            </table>
+
+            <div class="jumbotron">
+                <h2>API</h2>
+                <b>/all </b>  get all emails (limit 100) <br/>
+                <b>/from/{addr}</b>  get emails from addr (limit 100)<br/>
+                <b>/to/{addr}</b>  get emails to addr(limit 100)<br/>
+                <p class="lead">
+                example:
+                <pre>
+ [{
+		"from": "a@example.com",
+		"to0": "b@example.com",
+		"to": ["b@example.com"],
+		"subject": "A test",
+		"content": "xxxxxxxxxxxxxxxxx*
+}]
+                </pre>
+                </p>
+            </div>
+        </div>
+
+        <script src="https://cdn.bootcss.com/jquery/1.11.0/jquery.min.js"></script>
+        <script src="https://cdn.bootcss.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
+        <script src="https://cdn.bootcss.com/bootstrap-table/1.12.1/bootstrap-table.min.js"></script>
+        <script>
+
+            $.getJSON("all",function(data){
+                var $table = $('#table');
+                $table.bootstrapTable({data: data});
+            });
+
+$("#from_btn").click(function(){
+    from=$("#from").val();
+    if(from==""){
+        $.getJSON("all",function(data){
+            var $table = $('#table');
+            $table.bootstrapTable('removeAll');
+            $table.bootstrapTable('append',data);
+        });
+    }else{
+        $.getJSON("from/"+from,function(data){
+            var $table = $('#table');
+            $table.bootstrapTable('removeAll');
+            $table.bootstrapTable('append',data);
+        });
+    }
+});
+$("#to_btn").click(function(){
+    from=$("#to").val();
+    if(from==""){
+        $.getJSON("all",function(data){
+            var $table = $('#table');
+            $table.bootstrapTable('removeAll');
+            $table.bootstrapTable('append',data);
+        });
+    }else{
+        $.getJSON("to/"+from,function(data){
+            var $table = $('#table');
+            $table.bootstrapTable('removeAll');
+            $table.bootstrapTable('append',data);
+        });
+    }
+});
+
+
+
+
+        </script>
+    </body>
+
+</html>

+ 12 - 0
tests/send_test.py

@@ -0,0 +1,12 @@
+from smtplib import SMTP as Client
+
+client = Client("localhost", 8025)
+
+r = client.sendmail('a@example.com', ['b@example.com'], """\
+From: Anne Person <anne@example.com>
+To: Bart Person <bart@example.com>
+Subject: A test
+Message-ID: <ant>
+
+Hi Bart, this is Anne.
+""")

+ 3 - 0
tests/web_test.py

@@ -0,0 +1,3 @@
+from web import web_start
+
+web_start()

+ 34 - 0
web.py

@@ -0,0 +1,34 @@
+import json
+from flask import Flask
+from flask import send_file
+from data import dataInstance
+
+app = Flask(__name__)
+dao = dataInstance
+
+
+def web_start(host, port):
+    app.run(host=host, port=port)
+
+
+@app.route('/')
+def index():
+    return send_file('static/index.html')
+
+
+@app.route('/all')
+def msg_all():
+    rows = dao.read_all()
+    return json.dumps(rows)
+
+
+@app.route('/from/<addr>')
+def msg_from(addr):
+    rows = dao.read_from(addr)
+    return json.dumps(rows)
+
+
+@app.route('/to/<addr>')
+def msg_to(addr):
+    rows = dao.read_to(addr)
+    return json.dumps(rows)