This repository has been archived by the owner on Jun 23, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy path__init__.py
417 lines (345 loc) · 13.8 KB
/
__init__.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
#!/usr/bin/env python
# coding: utf-8
"""
Python-Simplemail sends emails with and without attachments
By Gerold - http://halvar.at/
Example::
from simplemail import Email
Email(
from_address = "[email protected]",
to_address = "[email protected]",
subject = "This is the subject",
message = "This is the short message body."
).send()
"""
import os.path
import sys
import time
import smtplib
import mimetypes
import email
import email.header
from email.message import Message
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.audio import MIMEAudio
from email.mime.base import MIMEBase
from email.mime.image import MIMEImage
# Exceptions
class SimpleMailException(Exception):
"""SimpleMail Base-Exception"""
def __str__(self):
return self.__doc__
class NoFromAddressException(SimpleMailException):
"""No sender address"""
pass
class NoToAddressException(SimpleMailException):
"""No recipient address"""
pass
class NoSubjectException(SimpleMailException):
"""No subject"""
pass
class AttachmentNotFoundException(SimpleMailException):
"""Attachment not found"""
def __init__(self, filename = None):
if filename:
self.__doc__ = 'Attachment not found: "%s"' % filename
# UTF-8 encoded emails should not be Base64-coded. Quoted-printable is a better choice.
email.charset.add_charset("utf-8", email.charset.QP, email.charset.QP, "utf-8")
class Attachments(object):
"""Email attachments"""
def __init__(self):
self._attachments = []
def add_filename(self, filename = ''):
"""Adds a new attachment"""
self._attachments.append(filename)
def count(self):
"""Returns the number of attachments"""
return len(self._attachments)
def get_list(self):
"""Returns the attachments, as list"""
return self._attachments
class Recipients(object):
"""Email recipients"""
def __init__(self):
self._recipients = []
def add(self, address, caption = u""):
"""
Adds a new address to the list of recipients
:param address: email address of the recipient
:param caption: caption (name) of the recipient
"""
# ToDo: Die Umwandlung sollte später mal, nicht mehr hier, sondern erst
# beim Verwenden des Empfängers umgewandelt werden. Dann weiß man
# das gewünschte Encoding. Das Encoding muss ich hier leider fest
# im Quellcode verankern. :-(
if isinstance(caption, unicode):
caption = str(email.header.Header(caption, charset = "utf-8"))
self._recipients.append(email.utils.formataddr((caption, address)))
def count(self):
"""Returns the quantity of recipients"""
return len(self._recipients)
def __repr__(self):
"""Returns the list of recipients, as string"""
return str(self._recipients)
def get_list(self):
"""Returns the list of recipients, as list"""
return self._recipients
class CCRecipients(Recipients):
"""Carbon copy recipients"""
pass
class BCCRecipients(Recipients):
"""Blind carbon copy recipients"""
pass
class Email(object):
"""One email, which can sent with the 'send'-method"""
def __init__(
self,
from_address = "",
from_caption = "",
to_address = "",
to_caption = "",
subject = "",
message = "",
smtp_server = "localhost",
smtp_user = "",
smtp_password = "",
attachment_file = "",
user_agent = "",
reply_to_address = "",
reply_to_caption = "",
use_tls = False,
header = None,
use_ssl = False,
keyfile = None,
certfile = None
):
"""
Initializes the email object
:param from_address: the email address of the sender
:param from_caption: the caption (name) of the sender
:param to_address: the email address of the recipient
:param to_caption: the caption (name) of the recipient
:param subject: the subject of the email message
:param message: the body text of the email message
:param smtp_server: The ip-address or the name of the SMTP-server.
The default is to connect to the local host at the standard
SMTP port (25). If the hostname ends with a colon (':') followed
by a number, that suffix will be stripped off and the number
interpreted as the port number to use.
:param smtp_user: (optional) Login name for the SMTP-Server
:param smtp_password: (optional) Password for the SMTP-Server
:param user_agent: (optional) program identification
:param reply_to_address: (optional) Reply-to email address
:param reply_to_caption: (optional) Reply-to caption (name)
:param use_tls: (optional) True, if the connection should use TLS to encrypt.
:param header: (optional) Additional header fields as dictionary.
You can use this parameter to add additional header fields.
Allready (internal) used header fields are: "From", "Reply-To", "To",
"Cc", "Date", "User-Agent" and "Subject". (case sensitive)
The items of this dictionary replace internal used header fields.
:param use_ssl: (optional) True, if the connection should use SSL to encrypt.
:param keyfile: (optional) Keyfile for SSL or TLS.
:param certfile: (optional) Certfile for SSL or TLS.
"""
self.from_address = from_address
if isinstance(from_caption, unicode):
from_caption = str(email.header.Header(from_caption, charset = "utf-8"))
self.from_caption = from_caption
self.recipients = Recipients()
self.cc_recipients = CCRecipients()
self.bcc_recipients = BCCRecipients()
if to_address:
self.recipients.add(to_address, to_caption)
self.subject = subject
self.message = message
self.smtp_server = smtp_server
self.smtp_user = smtp_user
self.smtp_password = smtp_password
self.attachments = Attachments()
if attachment_file:
self.attachments.add_filename(attachment_file)
self.content_subtype = "plain"
self.content_charset = "utf-8"
self.header_charset = "us-ascii"
self.statusdict = None
if user_agent:
self.user_agent = user_agent
else:
self.user_agent = (
"SimpleMail Python/%s (https://github.com/gerold-penz/python-simplemail)"
) % sys.version.split()[0]
self.reply_to_address = reply_to_address
if isinstance(reply_to_caption, unicode):
reply_to_caption = str(
email.header.Header(reply_to_caption, charset = "utf-8")
)
self.reply_to_caption = reply_to_caption
self.use_tls = use_tls
self.header_fields = header or {}
self.use_ssl = use_ssl
self.keyfile = keyfile
self.certfile = certfile
def send(self):
"""
en: Sends the email to the recipient.
If the email is only sent to one recipient, if only one recipient is
specfied, <True> will be returned. If the email is send to several
recipient and was succesfully delivered to at least one recipient,
<True> will be returned either way.
If the email was only sent to one recipient and sending failed, then
<False> will be returned. If the email was sent to several recipients
and failed to be sent to any recipient, then <False> will be returned.
de: Sendet die Email an den Empfaenger.
Wird das Email nur an einen Empfaenger gesendet, dann wird bei
Erfolg <True> zurueck gegeben. Wird das Email an mehrere Empfaenger
gesendet und wurde an mindestens einen der Empfaenger erfolgreich
ausgeliefert, dann wird ebenfalls <True> zurueck gegeben.
Wird das Email nur an einen Empfaenger gesendet, dann wird bei
Misserfolg <False> zurueck gegeben. Wird das Email an mehrere
Empfaenger gesendet und wurde an keinen der Empfaenger erfolgreich
ausgeliefert, dann wird <False> zurueck gegeben.
"""
#
# pruefen ob alle notwendigen Informationen angegeben wurden
#
if len(self.from_address.strip()) == 0:
raise NoFromAddressException()
if self.recipients.count() == 0:
if (
(self.cc_recipients.count() == 0) and
(self.bcc_recipients.count() == 0)
):
raise NoToAddressException()
if len(self.subject.strip()) == 0:
raise NoSubjectException()
#
# Wenn die Nachricht oder Subject UNICODE sind,
# dann nach self.content_charset umwandeln
#
if isinstance(self.subject, unicode):
if self.header_charset.lower() != "us-ascii":
self.subject = self.subject.encode(self.header_charset)
if isinstance(self.message, unicode):
self.message = self.message.encode(self.content_charset)
#
# Email zusammensetzen
#
if self.attachments.count() == 0:
# Nur Text
msg = MIMEText(
_text = self.message,
_subtype = self.content_subtype,
_charset = self.content_charset
)
else:
# Multipart
msg = MIMEMultipart()
if self.message:
att = MIMEText(
_text = self.message,
_subtype = self.content_subtype,
_charset = self.content_charset
)
msg.attach(att)
assert isinstance(msg, Message)
# Empfänger, CC, BCC, Absender, User-Agent, Antwort-an
# und Betreff hinzufügen
from_str = email.utils.formataddr((self.from_caption, self.from_address))
msg["From"] = from_str
if self.reply_to_address:
reply_to_str = email.utils.formataddr(
(self.reply_to_caption, self.reply_to_address)
)
msg["Reply-To"] = reply_to_str
if self.recipients.count() > 0:
msg["To"] = ", ".join(self.recipients.get_list())
if self.cc_recipients.count() > 0:
msg["Cc"] = ", ".join(self.cc_recipients.get_list())
msg["Date"] = email.utils.formatdate(time.time())
msg["User-Agent"] = self.user_agent
try:
msg["Subject"] = email.header.Header(
self.subject, self.header_charset
)
except UnicodeDecodeError:
msg["Subject"] = email.header.Header(
self.subject, self.content_charset
)
# User defined header_fields
if self.header_fields:
for key, value in self.header_fields.items():
msg[key] = value
msg.preamble = "You will not see this in a MIME-aware mail reader.\n"
msg.epilogue = ""
# Falls MULTIPART --> zusammensetzen
if self.attachments.count() > 0:
for filename in self.attachments.get_list():
# Pruefen ob Datei existiert
if not os.path.isfile(filename):
raise AttachmentNotFoundException(filename = filename)
# Datentyp herausfinden
ctype, encoding = mimetypes.guess_type(filename)
if ctype is None or encoding is not None:
ctype = 'application/octet-stream'
maintype, subtype = ctype.split('/', 1)
if maintype == 'text':
fp = file(filename)
# Note: we should handle calculating the charset
att = MIMEText(fp.read(), _subtype=subtype)
fp.close()
elif maintype == 'image':
fp = file(filename, 'rb')
att = MIMEImage(fp.read(), _subtype=subtype)
fp.close()
elif maintype == 'audio':
fp = file(filename, 'rb')
att = MIMEAudio(fp.read(), _subtype=subtype)
fp.close()
else:
fp = file(filename, 'rb')
att = MIMEBase(maintype, subtype)
att.set_payload(fp.read())
fp.close()
# Encode the payload using Base64
email.Encoders.encode_base64(att)
# Set the filename parameter
att.add_header(
"Content-Disposition",
"attachment",
filename = os.path.split(filename)[1].strip()
)
msg.attach(att)
#
# Connect to SMTP-server
#
if self.use_ssl:
smtp = smtplib.SMTP_SSL(
keyfile = self.keyfile, certfile = self.certfile
)
else:
smtp = smtplib.SMTP()
smtp.connect(host = self.smtp_server or "localhost")
# TLS
if self.use_tls:
smtp.ehlo()
smtp.starttls(keyfile = self.keyfile, certfile = self.certfile)
smtp.ehlo()
# authenticate
if self.smtp_user:
smtp.login(user = self.smtp_user, password = self.smtp_password)
#
# Send email
#
self.statusdict = smtp.sendmail(
from_str,
(
self.recipients.get_list() +
self.cc_recipients.get_list() +
self.bcc_recipients.get_list()
),
msg.as_string()
)
smtp.quit()
# Rueckmeldung
return True