docker-nagios-4/sendmail.py

213 lines
9.9 KiB
Python
Executable File

#!/usr/bin/env python3
##############################################
# SendmailViaSMTP is a kit for sending mail
# under console throught exist SMTP server.
#
# Author: dave@davepedu.com
#
# History:
# 2020-01-04:
# * ported to python3 (dave@davepedu.com)
# 2013-04-23:
# * fixed a bug while adding an attachment(thanks http://weibo.com/u/1738440993).
# 2012-10-29:
# * fixed problem under python 2.3.x.
# 2012-10-09:
# * fixed bug under crontab.
# * adjust priority to --file, --content and piped mode because of the bug above.
# 2012-09-30:
# + add --log option for debugging errors.
# 2012-09-24:
# * restruct the codes.
# * update the version number to 1.2.
# 2012-08-20:
# * fixed homepage link broken in README.rst(thanks http://weibo.com/royshan ).
# 2012-08-15:
# + support attachments(-a or --attach option).
# 2012-03-13:
# * Fixed sending mail under python 2.4x(thanks yong.yang).
# 2011-12-28:
# + add README.rst.
# + add pipe mode -- accept data as mail body which transfered through pipe.
# + implement file mode.
# * adjust the file mode has higher priority than option mode.
# 2011-12-23:
# * Fixed auth not supported issue under 2.5.x(thanks doitmy).
# 2010-09-28:
# * fixed --tls as turn on/off option.
# * optimize help message.
# 2010-09-27:
# + add support for Gmail(smtp.gmail.com).
# * fixed bugs for multi recipients.
# + first release.
##############################################
import sys
import os
import os.path
import fileinput
import smtplib
import mimetypes
from email import encoders
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
__author__ = "dave@davepedu.com"
__date__ = "$2020-01-04 14:52:27$"
__usage__ = """python %prog [--host=smtp.yourdomain.com] <--port=110> [--user=smtpaccount] [--password=smtppass] <--subject=subject> [--file=filename]|[--content=mailbody] [--from=sender] [--to=reciver].
example:
1. echo "blablabla" | python %prog --host="mail.domain.com" --from="myname@yourdomain.com" --to="friends1@domain1.com;friends2@domain.com" --user="myname@yourdomain.com" --password="p4word" --subject="mail title"
2. python %prog --host="mail.domain.com" --from="myname@yourdomain.com" --to="friends1@domain1.com;friends2@domain.com" --user="myname@yourdomain.com" --password="p4word" --subject="mail title" --file=/path/of/file
3. python %prog --host="mail.yourdomain.com" --from="myname@yourdomain.com" --to="friends1@domain1.com;friends2@domain2.com;friends3@domain3.com" --user="myname@yourdomain.com" --password="p4word" -s "Hello from MailViaSMTP" -c "This is a mail just for testing."
The priority of three content inputing method is: piped-data, --file, --content."""
__version__ = "%prog 3.0"
__desc__ = """This is a command line kit for sending mail via smtp server which can use in multiple platforms like linux, BSD, Windows etc.
This little kit was written by %s using python.
The minimum version of python required was 2.3.""" % (__author__)
class Mail:
"""docstring for Mail"""
def __init__(self, subject="", content="", m_from="", m_to="", m_cc=""):
self.subject = subject
self.content = MIMEText(content, "html", "utf-8")
self.m_from = m_from
self.m_to = m_to
self.m_cc = m_cc
self.body = MIMEMultipart("related")
self.body["Subject"] = self.subject
self.body["From"] = self.m_from
self.body["To"] = self.m_to
self.body.preamble = "This is a multi-part message in MIME format."
self.alternative = MIMEMultipart("alternative")
self.body.attach(self.alternative)
self.alternative.attach(self.content)
def attach(self, attachments):
if attachments:
for attachment in attachments:
if not os.path.isfile(attachment):
print("WARNING: Unable to attach %s because it is not a file." % attachment)
continue
ctype, encoding = mimetypes.guess_type(attachment)
if ctype is None or encoding is not None:
ctype = "application/octet-stream"
maintype, subtype = ctype.split("/", 1)
fp = open(attachment, "rb")
attachment_mime = MIMEBase("application", "octet-stream")
attachment_mime.set_payload(fp.read())
fp.close()
encoders.encode_base64(attachment_mime)
attachment_mime.add_header("Content-Disposition", "attachment", filename=attachment)
self.body.attach(attachment_mime)
class SMTPServer:
"""docstring for SMTPServer"""
def __init__(self, host="localhost", user="", password="", port=25, tls=False):
self.port = port
self.host = host
self.user = user
self.password = password
self.is_gmail = False
if self.host == "smtp.gmail.com":
self.is_gmail = True
self.port = 587
self.tls = tls
def sendmail(self, mail):
smtp = smtplib.SMTP(host=self.host, port=self.port)
if self.tls or self.is_gmail:
smtp.starttls()
smtp.ehlo()
smtp.esmtp_features["auth"] = "LOGIN DIGEST-MD5 PLAIN"
if self.user:
smtp.login(self.user, self.password)
smtp.sendmail(mail.m_from, mail.m_to.split(";"), mail.body.as_string())
smtp.quit()
if __name__ == "__main__":
import optparse
import logging
parser = optparse.OptionParser(usage=__usage__, version=__version__, description=__desc__)
parser.add_option("-a", "--attach", dest="attach", default=[], action="append", help="Specifies a file as attachment to be attached. Can be specified more than once.")
parser.add_option("-s", "--subject", dest="subject", help="The subject of the mail.")
parser.add_option("-c", "--content", dest="content", help="option mode. Mail body should be passed through this option. Note: this option should be ignored while working with piped-data or --file option.")
parser.add_option("-f", "--from", dest="address_from", metavar="my@domain.com", help="Set envelope from address. If --user option is not empty, commonly this option should be equaled with --user options. Otherwize, the authoration of the smtp server should be failed.")
parser.add_option("-t", "--to", dest="address_to", metavar="friend@domain2.com", help="Set recipient address. Use semicolon to seperate multi recipient, for example: 'a@a.com;b@b.com.'")
parser.add_option("-F", "--file", dest="file", help="File mode. Read mail body from file. NOTE: this option should be ignored while working with piped-data.")
parser.add_option("--host", dest="host", metavar="smtp.domain.com", help="SMTP server host name or ip. Like 'smtp.gmail.com' through GMail(tm) or '192.168.0.99' through your own smtp server.")
parser.add_option("-P", "--port", dest="port", type="int", default=25, help="SMTP server port number. Default is %default.")
parser.add_option("-u", "--user", dest="user", metavar="my@domain.com", help="The username for SMTP server authorcation. Left this option empty for non-auth smtp server.")
parser.add_option("-p", "--password", dest="password", help="The password for SMTP server authorcation. NOTE: if --user option is empty, this option will be ignored.")
parser.add_option("--tls", dest="tls", action="store_true", help="Using tls to communicate with SMTP server. Default is false. NOTE: if --host option equals 'smtp.gmail.com', this option becomes defaults true.")
parser.add_option("--log", dest="log", default="critical", help="specify --log=DEBUG or --log=debug, more info see document for logging module.")
opts, args = parser.parse_args()
numeric_level = getattr(logging, opts.log.upper(), None)
if not isinstance(numeric_level, int):
raise ValueError("Invalid log level: %s" % opts.log)
if sys.version_info < (3, 0, 0):
raise "Python runtime MUST greater than 3"
else:
logging.basicConfig(level=numeric_level, format="%(asctime)s %(message)s")
if opts.host is None or opts.address_from is None or opts.address_to is None:
msg = """ERROR: All parameters followed were required: --host, --from and --to.
Use -h to get more help."""
logging.critical(msg)
sys.exit(msg)
content = None
filename = None
if opts.content:
logging.debug("[param mode] %s" % opts.content)
content = opts.content # content mode, mail content should read from --content option.
if opts.file:
logging.debug("[file mode] %s" % opts.file)
filename = opts.file # file mode, mail content should read from file.
if content is None and filename is None and not sys.stdin.isatty(0):
logging.debug("[pip mode]")
filename = "-" # pipe mode - mail content should read from stdin.
if filename:
try:
fi = fileinput.FileInput(filename)
logging.debug("[filename] %s" % filename)
content = "<br />".join(fi)
except:
logging.critical("can not open %s." % filename)
logging.debug("[content]%s" % content)
if content:
try:
logging.info("preparing mail...")
mail = Mail(opts.subject, content, opts.address_from, opts.address_to)
logging.info("preparing attachments...")
mail.attach(opts.attach)
logging.info("preparing SMTP server...")
smtp = SMTPServer(opts.host, opts.user, opts.password, opts.port, opts.tls)
logging.info("sending mail...")
smtp.sendmail(mail)
logging.info("all done.")
except Exception as e:
logging.critical("[Exception] %s" % e)
else:
msg = """ERROR: Mail content is EMPTY! Please specify one option of listed: piped-data, --file or --content.
Use -h to get more help."""
logging.critical(msg)
sys.exit(msg)