Completed
Push — master ( 152b55...0bdad9 )
by John
01:22
created

smtp_config_writer()   F

Complexity

Conditions 10

Size

Total Lines 43

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 10
dl 0
loc 43
rs 3.1304
c 0
b 0
f 0

How to fix   Complexity   

Complexity

Complex classes like smtp_config_writer() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

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
    if homepath is None:
26
        homepath = os.path.expanduser("~")
27
    conffile = os.path.join(homepath, "bbarchivist.ini")
28
    if not os.path.exists(conffile):
29
        open(conffile, 'w').close()
30
    config.read(conffile)
31
    if not config.has_section('email'):
32
        config['email'] = {}
33
    smtpini = config['email']
34
    resultdict['server'] = smtpini.get('server', fallback=None)
35
    resultdict['port'] = int(smtpini.getint('port', fallback=0))
36
    resultdict['username'] = smtpini.get('username', fallback=None)
37
    resultdict['password'] = smtpini.get('password', fallback=None)
38
    resultdict['is_ssl'] = smtpini.get('is_ssl', fallback=None)
39
    return resultdict
40
41
42
def smtp_config_writer(**kwargs):
43
    """
44
    Write a ConfigParser file to store email server details.
45
46
    :param server: SMTP email server.
47
    :type server: str
48
49
    :param port: Port to use.
50
    :type port: int
51
52
    :param username: Email address.
53
    :type username: str
54
55
    :param password: Email password, optional.
56
    :type password: str
57
58
    :param is_ssl: True if server uses SSL, False if TLS only.
59
    :type is_ssl: bool
60
61
    :param homepath: Folder containing ini file. Default is user directory.
62
    :type homepath: str
63
    """
64
    config = configparser.ConfigParser()
65
    if kwargs['homepath'] is None:
66
        kwargs['homepath'] = os.path.expanduser("~")
67
    conffile = os.path.join(kwargs['homepath'], "bbarchivist.ini")
68
    if not os.path.exists(conffile):
69
        open(conffile, 'w').close()
70
    config.read(conffile)
71
    if not config.has_section('email'):
72
        config['email'] = {}
73
    if kwargs['server'] is not None:
74
        config['email']['server'] = kwargs['server']
75
    if kwargs['port'] is not None:
76
        config['email']['port'] = str(kwargs['port'])
77
    if kwargs['username'] is not None:
78
        config['email']['username'] = kwargs['username']
79
    if kwargs['password'] is not None:
80
        config['email']['password'] = kwargs['password']
81
    if kwargs['is_ssl'] is not None:
82
        config['email']['is_ssl'] = str(kwargs['is_ssl']).lower()
83
    with open(conffile, "w") as configfile:
84
        config.write(configfile)
85
86
87
def smtp_config_generator(results):
88
    """
89
    Take user input to create the SMTP configparser settings.
90
91
    :param results: The results to put in bbarchivist.ini.
92
    :type results: dict
93
    """
94
    if results['server'] is None:
95
        results['server'] = input("SMTP SERVER ADDRESS: ")
96
    if results['port'] == 0:
97
        results['port'] = input("SMTP SERVER PORT: ")
98
    if results['username'] is None:
99
        results['username'] = input("EMAIL ADDRESS: ")
100
    if results['password'] is None:
101
        results['password'] = getpass.getpass(prompt="PASSWORD: ")
102
    if results['is_ssl'] is None:
103
        use_ssl = utilities.s2b(input("Y: SSL, N: TLS (Y/N): "))
104
        if use_ssl:
105
            results['is_ssl'] = "true"
106
        else:
107
            results['is_ssl'] = "false"
108
    return results
109
110
111
def send_email(kwargs):
112
    """
113
    Wrap email sending based on SSL/TLS.
114
115
    :param server: SMTP email server.
116
    :type server: str
117
118
    :param port: Port to use.
119
    :type port: int
120
121
    :param username: Email address.
122
    :type username: str
123
124
    :param password: Email password, optional.
125
    :type password: str
126
127
    :param is_ssl: True if server uses SSL, False if TLS only.
128
    :type is_ssl: bool
129
130
    :param software: Software release.
131
    :type software: str
132
133
    :param os: OS version.
134
    :type os: str
135
136
    :param body: Email message body.
137
    :type body: str
138
    """
139
    if kwargs['password'] is None:
140
        kwargs['password'] = getpass.getpass(prompt="PASSWORD: ")
141
    server, username, port, password = parse_kwargs(kwargs)
142
    subject = generate_subject(kwargs['software'], kwargs['os'])
143
    message = generate_message(kwargs['body'], username, subject)
144
    payload = {
145
        "server": server,
146
        "port": port,
147
        "username": username,
148
        "password": password,
149
        "message": message
150
    }
151
    if utilities.s2b(kwargs['is_ssl']):
152
        send_email_ssl(payload)
153
    else:
154
        send_email_tls(payload)
155
156
157 View Code Duplication
def send_email_ssl(kwargs):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
158
    """
159
    Send email through SSL.
160
161
    :param server: SMTP email server.
162
    :type server: str
163
164
    :param port: Port to use.
165
    :type port: int
166
167
    :param username: Email address.
168
    :type username: str
169
170
    :param password: Email password.
171
    :type password: str
172
173
    :param message: Message to send, with body and subject.
174
    :type message: MIMEText
175
    """
176
    smt = smtplib.SMTP_SSL(kwargs['server'], kwargs['port'])
177
    smt.ehlo()
178
    smt.login(kwargs['username'], kwargs['password'])
179
    smt.sendmail(kwargs['username'], kwargs['username'], kwargs['message'].as_string())
180
    smt.quit()
181
182
183 View Code Duplication
def send_email_tls(kwargs):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
184
    """
185
    Send email through TLS.
186
187
    :param server: SMTP email server.
188
    :type server: str
189
190
    :param port: Port to use.
191
    :type port: int
192
193
    :param username: Email address.
194
    :type username: str
195
196
    :param password: Email password.
197
    :type password: str
198
199
    :param message: Message to send, with body and subject.
200
    :type message: MIMEText
201
    """
202
    smt = smtplib.SMTP(kwargs['server'], kwargs['port'])
203
    smt.ehlo()
204
    smt.starttls()
205
    smt.login(kwargs['username'], kwargs['password'])
206
    smt.sendmail(kwargs['username'], kwargs['username'], kwargs['message'].as_string())
207
    smt.quit()
208
209
210
def parse_kwargs(kwargs):
211
    """
212
    Extract variables from kwargs.
213
214
    :param server: SMTP email server.
215
    :type server: str
216
217
    :param port: Port to use.
218
    :type port: int
219
220
    :param username: Email address.
221
    :type username: str
222
223
    :param password: Email password, optional.
224
    :type password: str
225
    """
226
    server = kwargs['server']
227
    username = kwargs['username']
228
    port = kwargs['port']
229
    password = kwargs['password']
230
    return server, username, port, password
231
232
233
def generate_message(body, username, subject):
234
    """
235
    Generate message body/headers.
236
237
    :param body: Body of text.
238
    :type body: str
239
240
    :param username: Address to send to and from.
241
    :type username: str
242
243
    :param subject: Subject of message.
244
    :type subject: str
245
    """
246
    msg = MIMEText(body)
247
    msg['Subject'] = subject
248
    msg['From'] = username
249
    msg['To'] = username
250
    return msg
251
252
253
def generate_subject(softwarerelease, osversion):
254
    """
255
    Generate message subject.
256
257
    :param softwarerelease: Software version.
258
    :type softwarerelease: str
259
260
    :param osversion: OS version.
261
    :type osversion: str
262
    """
263
    return "SW {0} - OS {1} available!".format(softwarerelease, osversion)
264
265
266
def prep_email(osversion, softwarerelease, password=None):
267
    """
268
    Bootstrap the whole process.
269
270
    :param osversion: OS version.
271
    :type osversion: str
272
273
    :param softwarerelease: Software version.
274
    :type softwarerelease: str
275
276
    :param password: Email password. None to prompt later.
277
    :type password: str
278
    """
279
    results = smtp_config_loader()
280
    results['homepath'] = None
281
    smtp_config_writer(**results)
1 ignored issue
show
Coding Style introduced by
Usage of * or ** arguments should usually be done with care.

Generally, there is nothing wrong with usage of * or ** arguments. For readability of the code base, we suggest to not over-use these language constructs though.

For more information, we can recommend this blog post from Ned Batchelder including its comments which also touches this aspect.

Loading history...
282
    results['software'] = softwarerelease
283
    results['os'] = osversion
284
    results['body'] = utilities.return_and_delete("TEMPFILE.txt")
285
    if password is not None:
286
        results['password'] = password
287
    send_email(results)
288