influx-smtp-gateway/main.py

155 lines
4.8 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
"""
SMTP webhook server
"""
import os
import json
import smtplib
import sys
import time
from email.header import Header
from email.mime.text import MIMEText
import cherrypy
import logging
import threading
logging.basicConfig(stream=sys.stdout,
format='%(name)s - %(levelname)s - %(message)s')
def soft_return(msg):
time.sleep(0.2)
return(msg)
class SMTPWebhookApp:
"""
SMTP webhook server
"""
def create_new_client(self, response):
try:
mythread.client = smtplib.SMTP(host=SMTP_SERVER_HOST,
port=SMTP_SERVER_PORT)
mythread.client.starttls()
return True
except (smtplib.SMTPException, TimeoutError) as e:
soft_return("can't connect")
response = {"status": 500, # noqa:F841
"message": "some error: {}".format(e)}
return False
def smtp_login(self, response, msg):
try:
mythread.client.login(msg["From"], FROM_MAIL_PASSWORD)
return True
except smtplib.SMTPException as e:
soft_return(e)
response = {"status": 500, # noqa:F841
"message": "some error: {}".format(e)}
return False
def _send_mail(self):
if not cherrypy.request.headers.get('Content-Length'):
logging.error("error: Invalid content length.")
return {"status": 400, "message": "Invalid content length."}
cl = cherrypy.request.headers['Content-Length']
rawbody = cherrypy.request.body.read(int(cl))
req_body = json.loads(rawbody)
if not req_body.get('subject') or req_body.get('subject') == '':
logging.error("error: body field is required.")
return {"status": 400, "message": "subject field is required."}
if not req_body.get('body') or req_body.get('body') == '':
logging.error("error: body field is required.")
return {"status": 400, "message": "body field is required."}
if not req_body.get('to_mail') or req_body.get('to_mail') == '':
logging.error("error: to_mail field is required.")
return {"status": 400, "message": "to_mail field is required."}
subject = req_body['subject']
body = req_body['body']
to_mail = req_body['to_mail']
msg = MIMEText(body, 'plain', 'utf-8')
msg['Subject'] = Header(subject, 'utf-8')
msg['From'] = FROM_MAIL
msg['To'] = to_mail
c = 0
response = {}
while (c < 2):
try:
mythread.client.sendmail(msg['From'],
msg['To'], msg.as_string())
response = {"status": 200, "message": "mail sent successfully"}
except (AttributeError, ValueError,
smtplib.SMTPServerDisconnected):
if (c == 1):
soft_return("can't connect")
if not self.create_new_client(response):
break
if not self.smtp_login(response, msg):
break
except smtplib.SMTPException as e:
soft_return("can't send for some reason")
response = {"status": 500,
"message": "some error: {}".format(e)}
finally:
c += 1
return response
@cherrypy.expose
def send_mail(self):
"""
api endpoint for send mail
"""
data = self._send_mail()
cherrypy.response.status = data["status"]
cherrypy.response.headers["Content-Type"] = "application/json"
return json.dumps(data["status"]).encode()
ROOT = SMTPWebhookApp()
SMTP_SERVER_PORT = 587
CHERRYPY_SERVER_HOST = "0.0.0.0"
CHERRYPY_SERVER_PORT = 8081
if __name__ == "__main__":
try:
SMTP_SERVER_HOST = os.environ["SMTP_SERVER_HOST"]
FROM_MAIL = os.environ["FROM_MAIL"]
FROM_MAIL_PASSWORD = os.environ["FROM_MAIL_PASSWORD"]
except KeyError:
raise KeyError("Please check missing environment variables: "
"SMTP_SERVER_HOST, FROM_MAIL, "
"FROM_MAIL_PASSWORD")
cherrypy.server.socket_host = CHERRYPY_SERVER_HOST
cherrypy.server.socket_port = CHERRYPY_SERVER_PORT
ENGINE = cherrypy.engine
cherrypy.tree.mount(ROOT, config={})
mythread = threading.local()
mythread.client = None
if hasattr(ENGINE, "signal_handler"):
ENGINE.signal_handler.subscribe()
if hasattr(ENGINE, "console_control_handler"):
ENGINE.console_control_handler.subscribe()
try:
ENGINE.start()
except Exception: # pylint: disable=broad-except
sys.exit(1)
else:
ENGINE.block()