[2] | 1 | #!/usr/bin/env python
|
---|
| 2 | #
|
---|
[405] | 3 | # Rick van der Zwet <rick@vanderzwet.net>
|
---|
[2] | 4 | #
|
---|
[405] | 5 | #
|
---|
| 6 | import argparse
|
---|
[2] | 7 | import mimetypes
|
---|
[405] | 8 | import os
|
---|
| 9 | import random
|
---|
| 10 | import re
|
---|
| 11 | import smtplib
|
---|
| 12 | import socket
|
---|
| 13 | import string
|
---|
| 14 | import sys
|
---|
| 15 | import textwrap
|
---|
[2] | 16 | import time
|
---|
[405] | 17 |
|
---|
[2] | 18 | from time import gmtime,strftime
|
---|
[404] | 19 | from email.mime.multipart import MIMEMultipart
|
---|
| 20 | from email.mime.base import MIMEBase
|
---|
| 21 | from email.mime.text import MIMEText
|
---|
| 22 | from email.utils import COMMASPACE, formatdate
|
---|
| 23 | from email import encoders
|
---|
[2] | 24 |
|
---|
[405] | 25 | __svnversion__ = "$Id: foomail.py 406 2020-06-29 19:26:57Z rick $"
|
---|
[2] | 26 | __author__ = "Rick van der Zwet"
|
---|
| 27 |
|
---|
| 28 |
|
---|
[405] | 29 | default_subject = strftime('Test email send at %a, %d %b %Y %T %z', gmtime())
|
---|
[2] | 30 |
|
---|
[405] | 31 | parser = argparse.ArgumentParser(
|
---|
| 32 | description=textwrap.dedent('''
|
---|
| 33 | Sent test emails for mailserver verification and debugging
|
---|
[2] | 34 |
|
---|
[405] | 35 | Author : %s
|
---|
| 36 | Version: %s
|
---|
| 37 | ''' % (__author__, __svnversion__)),
|
---|
| 38 | formatter_class=argparse.RawDescriptionHelpFormatter)
|
---|
| 39 | parser.add_argument('--from', '-f', dest='sender', required=True)
|
---|
| 40 | parser.add_argument('--recipient', '-r', required=True)
|
---|
| 41 | parser.add_argument('--server', '-s', required=True)
|
---|
[406] | 42 | parser.add_argument('--auth', help="SMTP AUTH username:password")
|
---|
[405] | 43 | parser.add_argument('--cc', help='Carbon Copy header')
|
---|
| 44 | parser.add_argument('--file', action='append', help='Attachments to sent')
|
---|
| 45 | parser.add_argument('--hostname', default=socket.gethostname(), help='EHLO/HELO hostname to present')
|
---|
| 46 | parser.add_argument('--message', help='Alternative message text of body')
|
---|
| 47 | parser.add_argument('--subject', default=default_subject, help='Alternative Subject header')
|
---|
| 48 | parser.add_argument('--spam', action='store_true', help='Include X-Spam testing headers')
|
---|
[406] | 49 | parser.add_argument('--ssl', action='store_true', help='Put SMTP connect in TLS mode')
|
---|
[405] | 50 | parser.add_argument('--port', type=int, default=25, help='SMTP port used for connection')
|
---|
| 51 | parser.add_argument('--reply-to', help='Set Reply-To: header')
|
---|
| 52 | parser.add_argument('--to', help='Alternative To: header')
|
---|
| 53 | parser.add_argument('--silent', action='store_true', help='Do not-follow SMTP flow verbosely')
|
---|
| 54 | parser.add_argument('--virus', action='store_true', help='Include eicar.txt sample virus')
|
---|
| 55 | parser.add_argument('--dry-run', '-d', action='store_true', help='Do not actually sent out the message')
|
---|
| 56 | args = parser.parse_args()
|
---|
[2] | 57 |
|
---|
| 58 |
|
---|
| 59 | email = MIMEMultipart()
|
---|
[405] | 60 | email['From'] = 'Testing sender <' + args.sender + '>'
|
---|
| 61 | email['To'] = args.to or args.recipient
|
---|
| 62 | if args.cc:
|
---|
| 63 | email['Cc'] = args.cc
|
---|
| 64 | email['X-Server'] = args.server
|
---|
| 65 | email['X-Port'] = str(args.port)
|
---|
| 66 | email['X-Hostname'] = args.hostname
|
---|
[404] | 67 | email['Message-ID' ] = '<' +''.join(random.sample(string.ascii_letters +
|
---|
[2] | 68 | string.digits,10)) + '@' + email['X-Hostname'] + '>'
|
---|
| 69 | email['Date'] = strftime('%a, %d %b %Y %T %z',gmtime())
|
---|
[405] | 70 | email['Subject'] = args.subject
|
---|
[2] | 71 | email['X-Version'] = __svnversion__
|
---|
| 72 | email['X-Author'] = __author__
|
---|
| 73 |
|
---|
[405] | 74 | tests = []
|
---|
| 75 | if args.spam:
|
---|
| 76 | tests.append('spam')
|
---|
| 77 | if args.virus:
|
---|
| 78 | test.append('virus')
|
---|
| 79 | email['X-Tests'] = ' '.join(tests)
|
---|
[2] | 80 |
|
---|
[405] | 81 | if args.reply_to:
|
---|
| 82 | email['Reply-To'] = args.reply_to
|
---|
[2] | 83 |
|
---|
[405] | 84 | if args.spam:
|
---|
| 85 | email['X-Spam'] = 'XJS*C4JDBQADN1.NSBN3*2IDNEN*GTUBE-STANDARD-ANTI-UBE-TEST-EMAIL*C.34X'
|
---|
[2] | 86 |
|
---|
[405] | 87 | if args.message:
|
---|
| 88 | f = open(args.message,'r')
|
---|
| 89 | msg = f.read()
|
---|
| 90 | f.close()
|
---|
| 91 | else:
|
---|
| 92 | msg = textwrap.dedent('''
|
---|
| 93 | Hi,
|
---|
| 94 |
|
---|
| 95 | Sorry to bother you...
|
---|
| 96 |
|
---|
| 97 | Please ignore this message as it is used to test
|
---|
| 98 | and verify systems to make sure everything works
|
---|
| 99 | flawless.
|
---|
| 100 |
|
---|
| 101 | %(virus)s
|
---|
| 102 |
|
---|
| 103 | Technical details:\n''' % email)
|
---|
[2] | 104 |
|
---|
[404] | 105 | for k in list(email.keys()):
|
---|
[2] | 106 | v = email[k]
|
---|
| 107 | msg += "* %-15s : %s\n" % (k,v)
|
---|
| 108 |
|
---|
[405] | 109 | msg += textwrap.dedent('''
|
---|
| 110 | Best regards,
|
---|
| 111 | Your system administrator
|
---|
| 112 | --
|
---|
| 113 | BSD Licensed Source Code for this tool at:
|
---|
| 114 | https://rickvanderzwet.nl/svn/personal/foomailer
|
---|
| 115 | ''')
|
---|
[2] | 116 |
|
---|
| 117 |
|
---|
| 118 | email.attach(MIMEText(msg))
|
---|
| 119 |
|
---|
[405] | 120 | if args.virus:
|
---|
[2] | 121 | part = MIMEBase('application', "octet-stream")
|
---|
| 122 | part.set_payload('X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*')
|
---|
[404] | 123 | encoders.encode_base64(part)
|
---|
[2] | 124 | part.add_header('Content-Disposition', 'attachment; filename="eicar.txt"')
|
---|
| 125 | email.attach(part)
|
---|
| 126 |
|
---|
[405] | 127 | if args.file:
|
---|
| 128 | for filename in args.file:
|
---|
| 129 | mimetype = mimetypes.guess_type(filename)[0].split('/')
|
---|
[2] | 130 | part = MIMEBase(mimetype[0],mimetype[1])
|
---|
[405] | 131 | f = open(filename,'r')
|
---|
[2] | 132 | part.set_payload(f.read())
|
---|
| 133 | f.close()
|
---|
[404] | 134 | encoders.encode_base64(part)
|
---|
[405] | 135 | part.add_header('Content-Disposition', 'attachment; filename="%s"' % os.path.basename(filename))
|
---|
[2] | 136 | email.attach(part)
|
---|
| 137 |
|
---|
[404] | 138 | print(email.as_string())
|
---|
[2] | 139 |
|
---|
[405] | 140 | if not args.dry_run:
|
---|
[2] | 141 | mark = time.time()
|
---|
[405] | 142 | server = smtplib.SMTP(args.server, args.port, timeout=600)
|
---|
| 143 | server.set_debuglevel(not args.silent)
|
---|
| 144 | server.helo(args.hostname)
|
---|
| 145 | server.ehlo(args.hostname)
|
---|
[406] | 146 | if args.ssl:
|
---|
| 147 | server.starttls()
|
---|
| 148 | if args.auth:
|
---|
| 149 | user, password = args.auth.split(':', 1)
|
---|
| 150 | server.login(user, password)
|
---|
[405] | 151 | server.sendmail(args.sender, args.recipient, email.as_string())
|
---|
[2] | 152 | server.quit()
|
---|
[404] | 153 | print("Sending took sec: %.2f" % (time.time() - mark))
|
---|