Add ability to test email from launcher
[discourse_docker.git] / scripts / mailtest
1 #!/usr/bin/env python3
2 # vim:sw=4 ts=4 et:
3
4 import email
5 import smtplib
6 import ssl
7 import socket
8 import sys
9 import os
10
11 try:
12 import yaml
13 except ImportError:
14 print("ERROR: python yaml module not installed - run the following and try again:", file=sys.stderr)
15 print("sudo apt-get install python3-yaml", file=sys.stderr)
16 sys.exit(1)
17
18 def do_tls(conn, sslv):
19 # Possible values of smtp_sslv: none|peer|client_once|fail_if_no_peer_cert
20 try:
21 # Creating a context with the purpose of server authentication implies verifying the certificate
22 sslcontext=ssl.create_default_context(purpose=ssl.Purpose.SERVER_AUTH)
23 # The None below looks like might be a typo but it's not - it represents the ActiveRecord default (to verify)
24 if sslv in (None, 'peer', 'client_once', 'fail_if_no_peer_cert'):
25 # defaults are good
26 conn.starttls(context=sslcontext)
27 elif sslv in ('none',):
28 # disable cert checking
29 sslcontext.check_hostname = False
30 sslcontext.verify_mode = ssl.CERT_NONE
31 conn.starttls(context=sslcontext)
32 else:
33 raise ValueError('invalid value for DISCOURSE_SMTP_OPENSSL_VERIFY_MODE: {}'.format(sslv))
34 except smtplib.SMTPException as e:
35 if (sslv is None) and ('STARTTLS extension not supported by server' in e.args[0]):
36 print("unable to establish TLS, continuing: {}".format(e.args[0]))
37 else:
38 raise
39
40 ### Start of execution ###
41 cfgfile = sys.argv[1]
42 try:
43 destemail = sys.argv[2]
44 except IndexError:
45 destemail = input('Enter your email address: ')
46 srcemail = 'nobody+launcher-mailtest@discourse.org'
47
48 # Read in the container yaml and grab the env section
49 cfgdata = yaml.safe_load(open(cfgfile).read())
50 envdata = cfgdata['env']
51
52 # Here are the variables we'll test
53 smtp_addr = envdata.get('DISCOURSE_SMTP_ADDRESS')
54 smtp_port = envdata.get('DISCOURSE_SMTP_PORT')
55 smtp_user = envdata.get('DISCOURSE_SMTP_USER_NAME')
56 smtp_pass = envdata.get('DISCOURSE_SMTP_PASSWORD')
57 smtp_sslv = envdata.get('DISCOURSE_SMTP_OPENSSL_VERIFY_MODE')
58
59 # Yoink out the settings from the file - we'll print them and put them in the email
60 testinfo = 'DISCOURSE_SMTP_ settings:\n'
61 for k,v in filter(lambda x: x[0].startswith('DISCOURSE_SMTP_'), envdata.items()):
62 if 'PASSWORD' in k:
63 v = '(hidden)'
64 testinfo += ' {} = {}\n'.format(k,v)
65 print(testinfo)
66
67 # Ensure at least smtp-addr is specified - everything else is optional
68 if smtp_addr is None:
69 print("ERROR: DISCOURSE_SMTP_ADDRESS not specified", file=sys.stderr)
70 sys.exit(1)
71
72 if (smtp_user is None and smtp_pass is not None) or (smtp_user is not None and smtp_pass is None):
73 print("ERROR: both username and password must be specified for auth", file=sys.stderr)
74 sys.exit(1)
75
76 # Try and ensure the test is valid
77 if destemail.split('@',1)[1] in smtp_addr:
78 print('WARNING: {} may be allowed to relay mail to {}, this may not be a valid test!'.format(smtp_addr, destemail))
79
80 msg = email.message.Message()
81 msg.add_header('From', 'nobody+launcher-mailtest@discourse.org')
82 msg.add_header('To', destemail)
83 msg.add_header('Subject', 'discourse launcher mailtest for {}'.format(os.path.basename(cfgfile)))
84 msg.set_payload(testinfo)
85
86 try:
87 smtp = smtplib.SMTP(smtp_addr, smtp_port, timeout=5)
88 #smtp.debuglevel=1
89 do_tls(smtp,smtp_sslv)
90 if smtp_user:
91 smtp.login(smtp_user, smtp_pass)
92 result = smtp.sendmail('nobody+launcher-mailtest@discourse.org', destemail, msg.as_string())
93 except socket.gaierror as e:
94 print("ERROR: {}".format(e.args[-1]), file=sys.stderr)
95 print(" Ensure that the host '{}' exists".format(smtp_addr), file=sys.stderr)
96 sys.exit(1)
97 except socket.timeout as e:
98 print("ERROR: {}".format(e.args[-1]), file=sys.stderr)
99 print(" Ensure that the host '{}' is up and reachable".format(smtp_addr), file=sys.stderr)
100 sys.exit(1)
101 except smtplib.SMTPConnectError as e:
102 print("ERROR: {}".format(e.args[-1]), file=sys.stderr)
103 print(" Ensure that the host '{}' is up and reachable", file=sys.stderr)
104 sys.exit(1)
105 except ssl.SSLError as e:
106 print("ERROR: unable to establish TLS: {}".format(e.args[-1]), file=sys.stderr)
107 if 'certificate verify failed' in e.args[-1]:
108 print(" Fix the host certificate or disable validation".format(smtp_addr), file=sys.stderr)
109 sys.exit(1)
110 except smtplib.SMTPRecipientsRefused as e:
111 print("ERROR: {}".format(e.args[-1].popitem()[1][1].decode()), file=sys.stderr)
112 print(" You must provide a username/password to send to this host", file=sys.stderr)
113 sys.exit(1)
114 except smtplib.SMTPAuthenticationError as e:
115 print("ERROR: {}".format(e.args[-1].decode()), file=sys.stderr)
116 print(" Check to ensure your username and password are correct", file=sys.stderr)
117 sys.exit(1)
118 except smtplib.SMTPException as e:
119 print("ERROR: {}".format(e.args[-1]), file=sys.stderr)
120 if 'SMTP AUTH extension not supported by server' in e.args[0]:
121 print(" Authorization is not available - you may need to use TLS", file=sys.stderr)
122 sys.exit(1)
123 except ValueError:
124 print("ERROR: {}".format(e.args[-1]), file=sys.stderr)
125 sys.exit(1)
126
127 print("Success!")