12 from xtermcolor
import colorize
13 COLOR_ERROR
= 0xff0000
16 def print_error(val
="", *args
, **kwargs
):
17 return print(colorize(val
, COLOR_ERROR
), *args
, **kwargs
)
18 def print_warn(val
="", *args
, **kwargs
):
19 return print(colorize(val
, COLOR_WARN
), *args
, **kwargs
)
20 def print_good(val
="", *args
, **kwargs
):
21 return print(colorize(val
, COLOR_GOOD
), *args
, **kwargs
)
23 print("Install the python3-xtermcolor package for coloured output")
31 print_error("ERROR: python yaml module not installed - run the following and try again:", file=sys
.stderr
)
32 print("sudo apt-get install python3-yaml", file=sys
.stderr
)
35 def do_tls(conn
, sslv
):
36 # Possible values of smtp_sslv: none|peer|client_once|fail_if_no_peer_cert
38 # Creating a context with the purpose of server authentication implies verifying the certificate
39 sslcontext
=ssl
.create_default_context(purpose
=ssl
.Purpose
.SERVER_AUTH
)
40 # The None below looks like might be a typo but it's not - it represents the ActiveRecord default (to verify)
41 if sslv
in (None, 'peer', 'client_once', 'fail_if_no_peer_cert'):
43 conn
.starttls(context
=sslcontext
)
44 elif sslv
in ('none',):
45 # disable cert checking
46 sslcontext
.check_hostname
= False
47 sslcontext
.verify_mode
= ssl
.CERT_NONE
48 conn
.starttls(context
=sslcontext
)
50 raise ValueError('invalid value for DISCOURSE_SMTP_OPENSSL_VERIFY_MODE: {}'.format(sslv
))
51 except smtplib
.SMTPException
as e
:
52 if (sslv
is None) and ('STARTTLS extension not supported by server' in e
.args
[0]):
53 print_warn("unable to establish TLS, continuing: {}".format(e
.args
[0]))
57 ### Start of execution ###
60 destemail
= sys
.argv
[2]
62 destemail
= input('Enter your email address: ')
63 srcemail
= 'nobody+launcher-mailtest@discourse.org'
65 # Read in the container yaml and grab the env section
66 cfgdata
= yaml
.safe_load(open(cfgfile
).read())
67 envdata
= cfgdata
['env']
69 # Here are the variables we'll test
70 smtp_addr
= envdata
.get('DISCOURSE_SMTP_ADDRESS')
71 smtp_port
= envdata
.get('DISCOURSE_SMTP_PORT')
72 smtp_user
= envdata
.get('DISCOURSE_SMTP_USER_NAME')
73 smtp_pass
= envdata
.get('DISCOURSE_SMTP_PASSWORD')
74 smtp_sslv
= envdata
.get('DISCOURSE_SMTP_OPENSSL_VERIFY_MODE')
76 # Yoink out the settings from the file - we'll print them and put them in the email
77 testinfo
= 'DISCOURSE_SMTP_ settings:\n'
78 for k
,v
in filter(lambda x
: x
[0].startswith('DISCOURSE_SMTP_'), envdata
.items()):
81 testinfo
+= ' {} = {}\n'.format(k
,v
)
84 # Ensure at least smtp-addr is specified - everything else is optional
86 print_error("ERROR: DISCOURSE_SMTP_ADDRESS not specified", file=sys
.stderr
)
89 if (smtp_user
is None and smtp_pass
is not None) or (smtp_user
is not None and smtp_pass
is None):
90 print_error("ERROR: both username and password must be specified for auth", file=sys
.stderr
)
93 # Do we have a known good set of parameters?
94 known_good_settings
= {
95 ('smtp.mandrillapp.com', 587): 'Mandrill',
98 print_good('You are correctly configured to use: {}'.format(known_good_settings
[smtp_addr
,smtp_port
]))
102 # Try and ensure the test is valid
103 if destemail
.split('@',1)[1] in smtp_addr
:
104 print_warn('WARNING: {} may be allowed to relay mail to {}, this may not be a valid test!'.format(smtp_addr
, destemail
))
106 # Outbound port smtp?
107 if smtp_port
== 25 or smtp_port
is None:
108 print_warn('WARNING: many networks block outbound port 25 - consider an alternative (587?)')
110 # Outbound port submission?
112 if smtp_user
is None:
113 print_warn('WARNING: trying to use the submission (587) port without authenticating will probably fail')
115 # Build the message and send!
116 msg
= email
.message
.Message()
117 msg
.add_header('From', 'nobody+launcher-mailtest@discourse.org')
118 msg
.add_header('To', destemail
)
119 msg
.add_header('Subject', 'discourse launcher mailtest for {}'.format(os
.path
.basename(cfgfile
)))
120 msg
.set_payload(testinfo
)
123 smtp
= smtplib
.SMTP(smtp_addr
, smtp_port
, timeout
=5)
125 do_tls(smtp
,smtp_sslv
)
127 smtp
.login(smtp_user
, smtp_pass
)
128 result
= smtp
.sendmail('nobody+launcher-mailtest@discourse.org', destemail
, msg
.as_string())
129 except socket
.gaierror
as e
:
130 print_error("ERROR: {}".format(e
.args
[-1]), file=sys
.stderr
)
131 print(" Ensure that the host '{}' exists".format(smtp_addr
), file=sys
.stderr
)
133 except socket
.timeout
as e
:
134 print_error("ERROR: {}".format(e
.args
[-1]), file=sys
.stderr
)
135 print(" Ensure that the host '{}' is up and port {} is reachable".format(smtp_addr
, smtp_port
), file=sys
.stderr
)
136 print(" If your settings are known-good, ensure outbound port {} is not blocked".format(smtp_port
), file=sys
.stderr
)
138 except smtplib
.SMTPConnectError
as e
:
139 print_error("ERROR: {}".format(e
.args
[-1]), file=sys
.stderr
)
140 print(" Ensure that the host '{}' is up and port {} is reachable".format(smtp_addr
, smtp_port
), file=sys
.stderr
)
142 except ssl
.SSLError
as e
:
143 print_error("ERROR: unable to establish TLS: {}".format(e
.args
[-1]), file=sys
.stderr
)
144 if 'certificate verify failed' in e
.args
[-1]:
145 print(" Fix the host certificate or disable validation".format(smtp_addr
), file=sys
.stderr
)
147 except smtplib
.SMTPRecipientsRefused
as e
:
148 print_error("ERROR: {}".format(e
.args
[-1].popitem()[1][1].decode()), file=sys
.stderr
)
149 print(" You must provide a username/password to send to this host", file=sys
.stderr
)
151 except smtplib
.SMTPAuthenticationError
as e
:
152 print_error("ERROR: {}".format(e
.args
[-1].decode()), file=sys
.stderr
)
153 print(" Check to ensure your username and password are correct", file=sys
.stderr
)
155 except smtplib
.SMTPException
as e
:
156 print_error("ERROR: {}".format(e
.args
[-1]), file=sys
.stderr
)
157 if 'SMTP AUTH extension not supported by server' in e
.args
[0]:
158 print(" Authorization is not available - you may need to use TLS", file=sys
.stderr
)
161 print_error("ERROR: {}".format(e
.args
[-1]), file=sys
.stderr
)
164 print_good("Success!")