Completed
Push — master ( 25c255...d56c20 )
by John
01:13
created

smtp_config_generator_ssl()   A

Complexity

Conditions 3

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
dl 0
loc 11
rs 9.4285
c 0
b 0
f 0
1
#!/usr/bin/env python3
2
"""This module is used for dealing with SMTP email sending."""
3
4
import smtplib  # smtp connection
5
import configparser  # reading config files
6
import getpass  # passwords
7
import os  # paths
8
from email.mime.text import MIMEText  # email formatting
9
from bbarchivist import utilities  # file work
10
11
__author__ = "Thurask"
12
__license__ = "WTFPL v2"
13
__copyright__ = "Copyright 2015-2016 Thurask"
14
15
16
def smtp_config_loader(homepath=None):
17
    """
18
    Read a ConfigParser file to get email preferences.
19
20
    :param homepath: Folder containing ini file. Default is user directory.
21
    :type homepath: str
22
    """
23
    resultdict = {}
24
    config = configparser.ConfigParser()
25
    homepath = smtp_config_homepath(homepath)
26
    conffile = os.path.join(homepath, "bbarchivist.ini")
27
    smtp_config_conffile(conffile)
28
    config.read(conffile)
29
    if not config.has_section('email'):
30
        config['email'] = {}
31
    smtpini = config['email']
32
    resultdict['server'] = smtpini.get('server', fallback=None)
33
    resultdict['port'] = int(smtpini.getint('port', fallback=0))
34
    resultdict['username'] = smtpini.get('username', fallback=None)
35
    resultdict['password'] = smtpini.get('password', fallback=None)
36
    resultdict['is_ssl'] = smtpini.get('is_ssl', fallback=None)
37
    return resultdict
38
39
40
def smtp_config_homepath(homepath):
41
    """
42
    Fix path for ini file.
43
44
    :param homepath: Path to ini file.
45
    :type homepath: str
46
    """
47
    if homepath is None:
48
        homepath = os.path.expanduser("~")
49
    return homepath
50
51
52
def smtp_config_conffile(conffile):
53
    """
54
    Create ini file if it doesn't exist.
55
56
    :param conffile: Path to config ini file.
57
    :type conffile: str
58
    """
59
    if not os.path.exists(conffile):
60
        open(conffile, 'w').close()
61
62
63
def smtp_config_writer_server(kwargs, config):
64
    """
65
    Set server config.
66
67
    :param kwargs: Values. Refer to `:func:smtp_config_writer`.
68
    :type kwargs: dict
69
70
    :param config: Configuration dictionary.
71
    :type config: dict
72
    """
73
    if kwargs['server'] is not None:
74
        config['email']['server'] = kwargs['server']
75
    return config
76
77
78
def smtp_config_writer_port(kwargs, config):
79
    """
80
    Set port config.
81
82
    :param kwargs: Values. Refer to `:func:smtp_config_writer`.
83
    :type kwargs: dict
84
85
    :param config: Configuration dictionary.
86
    :type config: dict
87
    """
88
    if kwargs['port'] is not None:
89
        config['email']['port'] = str(kwargs['port'])
90
    return config
91
92
93
def smtp_config_writer_username(kwargs, config):
94
    """
95
    Set username config.
96
97
    :param kwargs: Values. Refer to `:func:smtp_config_writer`.
98
    :type kwargs: dict
99
100
    :param config: Configuration dictionary.
101
    :type config: dict
102
    """
103
    if kwargs['username'] is not None:
104
        config['email']['username'] = kwargs['username']
105
    return config
106
107
108
def smtp_config_writer_password(kwargs, config):
109
    """
110
    Set password config.
111
112
    :param kwargs: Values. Refer to `:func:smtp_config_writer`.
113
    :type kwargs: dict
114
115
    :param config: Configuration dictionary.
116
    :type config: dict
117
    """
118
    if kwargs['password'] is not None:
119
        config['email']['password'] = kwargs['password']
120
    return config
121
122
123
def smtp_config_writer_ssl(kwargs, config):
124
    """
125
    Set SSL/TLS config.
126
127
    :param kwargs: Values. Refer to `:func:smtp_config_writer`.
128
    :type kwargs: dict
129
130
    :param config: Configuration dictionary.
131
    :type config: dict
132
    """
133
    if kwargs['is_ssl'] is not None:
134
        config['email']['is_ssl'] = str(kwargs['is_ssl']).lower()
135
    return config
136
137
138
def smtp_config_writer(**kwargs):
139
    """
140
    Write a ConfigParser file to store email server details.
141
142
    :param server: SMTP email server.
143
    :type server: str
144
145
    :param port: Port to use.
146
    :type port: int
147
148
    :param username: Email address.
149
    :type username: str
150
151
    :param password: Email password, optional.
152
    :type password: str
153
154
    :param is_ssl: True if server uses SSL, False if TLS only.
155
    :type is_ssl: bool
156
157
    :param homepath: Folder containing ini file. Default is user directory.
158
    :type homepath: str
159
    """
160
    config = configparser.ConfigParser()
161
    kwargs['homepath'] = smtp_config_homepath(kwargs['homepath'])
162
    conffile = os.path.join(kwargs['homepath'], "bbarchivist.ini")
163
    smtp_config_conffile(conffile)
164
    config.read(conffile)
165
    if not config.has_section('email'):
166
        config['email'] = {}
167
    config = smtp_config_writer_server(kwargs, config)
168
    config = smtp_config_writer_port(kwargs, config)
169
    config = smtp_config_writer_username(kwargs, config)
170
    config = smtp_config_writer_password(kwargs, config)
171
    config = smtp_config_writer_ssl(kwargs, config)
172
    with open(conffile, "w") as configfile:
173
        config.write(configfile)
174
175
176
def smtp_config_generator_server(results):
177
    """
178
    Generate server config.
179
180
    :param results: Values. Refer to `:func:smtp_config_writer`.
181
    :type results: dict
182
    """
183
    if results['server'] is None:
184
        results['server'] = input("SMTP SERVER ADDRESS: ")
185
    return results
186
187
188
def smtp_config_generator_port(results):
189
    """
190
    Generate port config.
191
192
    :param results: Values. Refer to `:func:smtp_config_writer`.
193
    :type results: dict
194
    """
195
    if results['port'] == 0:
196
        results['port'] = input("SMTP SERVER PORT: ")
197
    return results
198
199
200
def smtp_config_generator_username(results):
201
    """
202
    Generate username config.
203
204
    :param results: Values. Refer to `:func:smtp_config_writer`.
205
    :type results: dict
206
    """
207
    if results['username'] is None:
208
        results['username'] = input("EMAIL ADDRESS: ")
209
    return results
210
211
212
def smtp_config_generator_password(results):
213
    """
214
    Generate password config.
215
216
    :param results: Values. Refer to `:func:smtp_config_writer`.
217
    :type results: dict
218
    """
219
    if results['password'] is None:
220
        results['password'] = getpass.getpass(prompt="PASSWORD: ")
221
    return results
222
223
224
def smtp_config_generator_ssl(results):
225
    """
226
    Generate SSL/TLS config.
227
228
    :param results: Values. Refer to `:func:smtp_config_writer`.
229
    :type results: dict
230
    """
231
    if results['is_ssl'] is None:
232
        use_ssl = utilities.s2b(input("Y: SSL, N: TLS (Y/N): "))
233
        results['is_ssl'] = "true" if use_ssl else "false"
234
    return results
235
236
237
def smtp_config_generator(results):
238
    """
239
    Take user input to create the SMTP configparser settings.
240
241
    :param results: The results to put in bbarchivist.ini.
242
    :type results: dict
243
    """
244
    results = smtp_config_generator_server(results)
245
    results = smtp_config_generator_port(results)
246
    results = smtp_config_generator_username(results)
247
    results = smtp_config_generator_password(results)
248
    results = smtp_config_generator_ssl(results)
249
    return results
250
251
252
def send_email(kwargs):
253
    """
254
    Wrap email sending based on SSL/TLS.
255
256
    :param server: SMTP email server.
257
    :type server: str
258
259
    :param port: Port to use.
260
    :type port: int
261
262
    :param username: Email address.
263
    :type username: str
264
265
    :param password: Email password, optional.
266
    :type password: str
267
268
    :param is_ssl: True if server uses SSL, False if TLS only.
269
    :type is_ssl: bool
270
271
    :param software: Software release.
272
    :type software: str
273
274
    :param os: OS version.
275
    :type os: str
276
277
    :param body: Email message body.
278
    :type body: str
279
    """
280
    if kwargs['password'] is None:
281
        kwargs['password'] = getpass.getpass(prompt="PASSWORD: ")
282
    server, username, port, password = parse_kwargs(kwargs)
283
    subject = generate_subject(kwargs['software'], kwargs['os'])
284
    message = generate_message(kwargs['body'], username, subject)
285
    payload = {
286
        "server": server,
287
        "port": port,
288
        "username": username,
289
        "password": password,
290
        "message": message,
291
        "is_ssl": kwargs["is_ssl"]
292
    }
293
    send_email_post(payload)
294
295
296
def prep_smtp_instance(kwargs):
297
    """
298
    Prepare a smtplib.SMTP/SMTP_SSL instance.
299
300
    :param is_ssl: True if server uses SSL, False if TLS only.
301
    :type is_ssl: bool
302
303
    :param server: SMTP email server.
304
    :type server: str
305
306
    :param port: Port to use.
307
    :type port: int
308
    """
309
    args = kwargs['server'], kwargs['port']
310
    smt = smtplib.SMTP_SSL(*args) if kwargs['is_ssl'] else smtplib.SMTP(*args)
311
    return smt
312
313
314
def send_email_post(kwargs):
315
    """
316
    Send email through SSL/TLS.
317
318
    :param server: SMTP email server.
319
    :type server: str
320
321
    :param port: Port to use.
322
    :type port: int
323
324
    :param username: Email address.
325
    :type username: str
326
327
    :param password: Email password.
328
    :type password: str
329
330
    :param message: Message to send, with body and subject.
331
    :type message: MIMEText
332
    """
333
    smt = prep_smtp_instance(kwargs)
334
    smt.ehlo()
335
    if not kwargs['is_ssl']:
336
        smt.starttls()
337
    smt.login(kwargs['username'], kwargs['password'])
338
    smt.sendmail(kwargs['username'], kwargs['username'], kwargs['message'].as_string())
339
    smt.quit()
340
341
342
def parse_kwargs(kwargs):
343
    """
344
    Extract variables from kwargs.
345
346
    :param server: SMTP email server.
347
    :type server: str
348
349
    :param port: Port to use.
350
    :type port: int
351
352
    :param username: Email address.
353
    :type username: str
354
355
    :param password: Email password, optional.
356
    :type password: str
357
    """
358
    server = kwargs['server']
359
    username = kwargs['username']
360
    port = kwargs['port']
361
    password = kwargs['password']
362
    return server, username, port, password
363
364
365
def generate_message(body, username, subject):
366
    """
367
    Generate message body/headers.
368
369
    :param body: Body of text.
370
    :type body: str
371
372
    :param username: Address to send to and from.
373
    :type username: str
374
375
    :param subject: Subject of message.
376
    :type subject: str
377
    """
378
    msg = MIMEText(body)
379
    msg['Subject'] = subject
380
    msg['From'] = username
381
    msg['To'] = username
382
    return msg
383
384
385
def generate_subject(softwarerelease, osversion):
386
    """
387
    Generate message subject.
388
389
    :param softwarerelease: Software version.
390
    :type softwarerelease: str
391
392
    :param osversion: OS version.
393
    :type osversion: str
394
    """
395
    return "SW {0} - OS {1} available!".format(softwarerelease, osversion)
396
397
398
def prep_email(osversion, softwarerelease, password=None):
399
    """
400
    Bootstrap the whole process.
401
402
    :param osversion: OS version.
403
    :type osversion: str
404
405
    :param softwarerelease: Software version.
406
    :type softwarerelease: str
407
408
    :param password: Email password. None to prompt later.
409
    :type password: str
410
    """
411
    results = smtp_config_loader()
412
    results['homepath'] = None
413
    smtp_config_writer(**results)
414
    results['software'] = softwarerelease
415
    results['os'] = osversion
416
    results['body'] = utilities.return_and_delete("TEMPFILE.txt")
417
    if password is not None:
418
        results['password'] = password
419
    send_email(results)
420