root/trunk/simplemail.py

Revision 25, 18.0 kB (checked in by gerold, 3 years ago)

- rollback to iso-8859-1

Line 
1 #!/usr/bin/env python
2 # -*- coding: iso-8859-1 -*-
3 """
4 ******************************************************************************
5 * SimpleMail sends emails with and without attachments
6 *
7 * Filename: simplemail.py
8 * Created: 2004-10-06 Gerold - http://gerold.bcom.at/
9 * License: LGPL: http://www.opensource.org/licenses/lgpl-license.php
10 * Requirements: Python >= 2.3: http://www.python.org/
11 * History: http://gelb.bcom.at/svn/pub/simplemail/trunk/history.rst
12 * Trac: http://gelb.bcom.at/trac/simplemail/
13 *
14 * Simple Example:
15 *
16 *   from simplemail import Email
17 *   Email(
18 *       from_address = "sender@domain.at",
19 *       to_address = "recipient@domain.at",
20 *       subject = "This is the subject",
21 *       message = "This is the short message body."
22 *   ).send()
23 *
24 ******************************************************************************
25 """
26
27 import os.path
28 import sys
29 import time
30 import smtplib
31 import mimetypes
32 from email import Encoders
33 from email.Header import Header
34 from email.MIMEText import MIMEText
35 from email.MIMEMultipart import MIMEMultipart
36 from email.Utils import formataddr
37 from email.Utils import formatdate
38 from email.Message import Message
39 from email.MIMEAudio import MIMEAudio
40 from email.MIMEBase import MIMEBase
41 from email.MIMEImage import MIMEImage
42
43
44 # Exceptions
45 #----------------------------------------------------------------------
46 class SimpleMailException(Exception):
47     """SimpleMail Base-Exception"""
48     def __str__(self):
49         return self.__doc__
50
51 class NoFromAddressException(SimpleMailException):
52     """No sender address"""
53     pass
54
55 class NoToAddressException(SimpleMailException):
56     """No recipient address"""
57     pass
58
59 class NoSubjectException(SimpleMailException):
60     """No subject"""
61     pass
62
63 class AttachmentNotFoundException(SimpleMailException):
64     """Attachment not found"""
65     def __init__(self, filename = None):
66         if filename:
67             self.__doc__ = 'Attachment not found: "%s"' % filename
68
69 # for backward compatibility
70 SimpleMail_Exception = SimpleMailException
71 NoFromAddress_Exception = NoFromAddressException
72 NoToAddress_Exception = NoToAddressException
73 NoSubject_Exception = NoSubjectException
74 AttachmentNotFound_Exception = AttachmentNotFoundException
75 #----------------------------------------------------------------------
76
77
78 class Attachments(object):
79     """Email attachments"""
80
81     def __init__(self):
82         self._attachments = []
83
84
85     def add_filename(self, filename = ''):
86         """Adds a new attachment"""
87        
88         self._attachments.append(filename)
89
90
91     def count(self):
92         """Returns the number of attachments"""
93        
94         return len(self._attachments)
95
96
97     def get_list(self):
98         """Returns the attachments, as list"""
99        
100         return self._attachments
101
102
103 class Recipients(object):
104     """Email recipients"""
105
106     def __init__(self):
107         self._recipients = []
108
109
110     def add(self, address, caption = ''):
111         """
112         Adds a new address to the list of recipients
113         
114         :param address: email address of the recipient
115         :param caption: caption (name) of the recipient
116         """
117        
118         self._recipients.append(formataddr((caption, address)))
119
120
121     def count(self):
122         """Returns the quantity of recipients"""
123        
124         return len(self._recipients)
125
126
127     def __repr__(self):
128         """Returns the list of recipients, as string"""
129        
130         return str(self._recipients)
131
132
133     def get_list(self):
134         """Returns the list of recipients, as list"""
135        
136         return self._recipients
137
138
139 class CCRecipients(Recipients):
140     """Carbon copy recipients"""
141     pass
142
143
144 class BCCRecipients(Recipients):
145     """Blind carbon copy recipients"""
146     pass
147
148
149 class Email(object):
150     """One email, which can sent with the 'send'-method"""
151
152     def __init__(
153         self,
154         from_address = "",
155         from_caption = "",
156         to_address = "",
157         to_caption = "",
158         subject = "",
159         message = "",
160         smtp_server = "localhost",
161         smtp_user = "",
162         smtp_password = "",
163         attachment_file = "",
164         user_agent = "",
165         reply_to_address = "",
166         reply_to_caption = "",
167         use_tls = False,
168         header = {},
169     ):
170         """
171         Initializes the email object
172         
173         :param from_address: the email address of the sender
174         :param from_caption: the caption (name) of the sender
175         :param to_address: the email address of the recipient
176         :param to_caption: the caption (name) of the recipient
177         :param subject: the subject of the email message
178         :param message: the body text of the email message
179         :param smtp_server: the ip-address or the name of the SMTP-server
180         :param smtp_user: (optional) Login name for the SMTP-Server
181         :param smtp_passwort: (optional) Password for the SMTP-Server
182         :param user_agent: (optional) program identification
183         :param reply_to_address: (optional) Reply-to email address
184         :param reply_to_caption: (optional) Reply-to caption (name)
185         :param use_tls: (optional) True, if the connection should use TLS to encrypt.
186         :param header: (optional) Additional header fields as dictionary.
187             You can use this parameter to add additional header fields.
188             Allready (internal) used header fields are: "From", "Reply-To", "To",
189             "Cc", "Date", "User-Agent" and "Subject". (case sensitive)
190             The items of this dictionary replace internal used header fields.
191         """
192        
193         self.from_address = from_address
194         self.from_caption = from_caption
195         self.recipients = Recipients()
196         self.cc_recipients = CCRecipients()
197         self.bcc_recipients = BCCRecipients()
198         if to_address:
199             self.recipients.add(to_address, to_caption)
200         self.subject = subject
201         self.message = message
202         self.smtp_server = smtp_server
203         self.smtp_user = smtp_user
204         self.smtp_password = smtp_password
205         self.attachments = Attachments()
206         if attachment_file:
207             self.attachments.add_filename(attachment_file)
208         self.content_subtype = "plain"
209         self.content_charset = "iso-8859-1"
210         self.header_charset = "us-ascii"
211         self.statusdict = None
212         if user_agent:
213             self.user_agent = user_agent
214         else:
215             self.user_agent = (
216                 "SimpleMail Python/%s (http://www.python-forum.de/post-18144.html)"
217             ) % sys.version.split()[0]
218         self.reply_to_address = reply_to_address
219         self.reply_to_caption = reply_to_caption
220         self.use_tls = use_tls
221         self.header_fields = header
222
223
224     def send(self):
225         """
226         de: Sendet die Email an den Empfaenger.
227             Wird das Email nur an einen Empfaenger gesendet, dann wird bei
228             Erfolg <True> zurueck gegeben. Wird das Email an mehrere Empfaenger
229             gesendet und wurde an mindestens einen der Empfaenger erfolgreich
230             ausgeliefert, dann wird ebenfalls <True> zurueck gegeben.
231             
232             Wird das Email nur an einen Empfaenger gesendet, dann wird bei
233             Misserfolg <False> zurueck gegeben. Wird das Email an mehrere
234             Empfaenger gesendet und wurde an keinen der Empfaenger erfolgreich
235             ausgeliefert, dann wird <False> zurueck gegeben.
236         """
237        
238         #
239         # pruefen ob alle notwendigen Informationen angegeben wurden
240         #
241         if len(self.from_address.strip()) == 0:
242             raise NoFromAddressException()
243         if self.recipients.count() == 0:
244             if (
245                 (self.cc_recipients.count() == 0) and
246                 (self.bcc_recipients.count() == 0)
247             ):
248                 raise NoToAddressException()
249         if len(self.subject.strip()) == 0:
250             raise NoSubjectException()
251        
252         #
253         # Email zusammensetzen
254         #
255         if self.attachments.count() == 0:
256             # Nur Text
257             msg = MIMEText(
258                 _text = self.message,
259                 _subtype = self.content_subtype,
260                 _charset = self.content_charset
261             )
262         else:
263             # Multipart
264             msg = MIMEMultipart()
265             if self.message:
266                 att = MIMEText(
267                     _text = self.message,
268                     _subtype = self.content_subtype,
269                     _charset = self.content_charset
270                 )
271                 msg.attach(att)
272        
273         # Empfänger, CC, BCC, Absender, User-Agent, Antwort-an
274         # und Betreff hinzufügen
275         from_str = formataddr((self.from_caption, self.from_address))
276         msg["From"] = from_str
277         if self.reply_to_address:
278             reply_to_str = formataddr((self.reply_to_caption, self.reply_to_address))
279             msg["Reply-To"] = reply_to_str
280         if self.recipients.count() > 0:
281             msg["To"] = ", ".join(self.recipients.get_list())
282         if self.cc_recipients.count() > 0:
283             msg["Cc"] = ", ".join(self.cc_recipients.get_list())
284         msg["Date"] = formatdate(time.time())
285         msg["User-Agent"] = self.user_agent
286         try:
287             msg["Subject"] = Header(
288                 self.subject, self.header_charset
289             )
290         except(UnicodeDecodeError):
291             msg["Subject"] = Header(
292                 self.subject, self.content_charset
293             )
294         # User defined header_fields
295         if self.header_fields:
296             for key, value in self.header_fields.items():
297                 msg[key] = value
298        
299         msg.preamble = "You will not see this in a MIME-aware mail reader.\n"
300         msg.epilogue = ""
301        
302         # Falls MULTIPART --> zusammensetzen
303         if self.attachments.count() > 0:
304             for filename in self.attachments.get_list():
305                 # Pruefen ob Datei existiert
306                 if not os.path.isfile(filename):
307                     raise AttachmentNotFoundException(filename = filename)
308                 # Datentyp herausfinden
309                 ctype, encoding = mimetypes.guess_type(filename)
310                 if ctype is None or encoding is not None:
311                     ctype = 'application/octet-stream'
312                 maintype, subtype = ctype.split('/', 1)
313                 if maintype == 'text':
314                     fp = file(filename)
315                     # Note: we should handle calculating the charset
316                     att = MIMEText(fp.read(), _subtype=subtype)
317                     fp.close()
318                 elif maintype == 'image':
319                     fp = file(filename, 'rb')
320                     att = MIMEImage(fp.read(), _subtype=subtype)
321                     fp.close()
322                 elif maintype == 'audio':
323                     fp = file(filename, 'rb')
324                     att = MIMEAudio(fp.read(), _subtype=subtype)
325                     fp.close()
326                 else:
327                     fp = file(filename, 'rb')
328                     att = MIMEBase(maintype, subtype)
329                     att.set_payload(fp.read())
330                     fp.close()
331                     # Encode the payload using Base64
332                     Encoders.encode_base64(att)
333                 # Set the filename parameter
334                 att.add_header(
335                     'Content-Disposition',
336                     'attachment',
337                     filename = os.path.split(filename)[1].strip()
338                 )
339                 msg.attach(att)
340        
341         #
342         # Am SMTP-Server anmelden
343         #
344         smtp = smtplib.SMTP()
345         if self.smtp_server:
346             smtp.connect(self.smtp_server)
347         else:
348             smtp.connect()
349        
350         # TLS-Verschlüsselung
351         if self.use_tls:
352             smtp.ehlo()
353             smtp.starttls()
354             smtp.ehlo()
355        
356         # authentifizieren
357         if self.smtp_user:
358             smtp.login(user = self.smtp_user, password = self.smtp_password)
359        
360         #
361         # Email versenden
362         #
363         self.statusdict = smtp.sendmail(
364             from_str,
365             (
366                 self.recipients.get_list() +
367                 self.cc_recipients.get_list() +
368                 self.bcc_recipients.get_list()
369             ),
370             msg.as_string()
371         )
372         smtp.close()
373        
374         # Rueckmeldung
375         return True
376
377
378 def test():
379
380     # Einfaches Beispiel
381     from simplemail import Email
382    
383     print "Einfaches Beispiel"
384     print "------------------"
385     Email(
386         from_address = "server@gps.gp",
387         smtp_server = "gps.gp",
388         to_address = "gerold@gps.gp",
389         subject = "Einfaches Beispiel (öäüß)",
390         message = "Das ist der Nachrichtentext mit Umlauten (öäüß)",
391     ).send()
392     print "Fertig"
393     print
394    
395     # Einfaches Beispiel nur mit CC-Adresse
396     from simplemail import Email
397    
398     print "Einfaches Beispiel mit CC-Adresse"
399     print "---------------------------------"
400     email = Email(
401         from_address = "server@gps.gp",
402         smtp_server = "gps.gp",
403         subject = "Einfaches Beispiel mit CC-Adresse (öäüß)",
404         message = "Das ist der Nachrichtentext mit Umlauten (öäüß)"
405     )
406     email.cc_recipients.add("gerold@gps.gp", "Gerold")
407     email.send()
408     print "Fertig"
409     print
410
411     # Einfaches Beispiel nur mit BCC-Adresse
412     from simplemail import Email
413    
414     print "Einfaches Beispiel mit BCC-Adresse"
415     print "---------------------------------"
416     email = Email(
417         from_address = "server@gps.gp",
418         smtp_server = "gps.gp",
419         subject = "Einfaches Beispiel mit BCC-Adresse (öäüß)",
420         message = "Das ist der Nachrichtentext mit Umlauten (öäüß)"
421     )
422     email.bcc_recipients.add("gerold@gps.gp", "Gerold Penz")
423     email.send()
424     print "Fertig"
425     print
426
427     # Komplexeres Beispiel mit Umlauten und Anhaengen
428     from simplemail import Email
429    
430     print "Komplexeres Beispiel mit Umlauten und Anhaengen"
431     print "-----------------------------------------------"
432     email = Email(
433         smtp_server = "gps.gp"
434     )
435    
436     # Absender
437     email.from_address = "server@gps.gp"
438     email.from_caption = "Gerolds Server"
439
440     # Antwort an
441     email.reply_to_address = "gerold@gps.gp"
442     email.reply_to_caption = "Gerold Penz (Antwortadresse)"
443
444     # Empfaenger
445     email.recipients.add("gerold@gps.gp", "Gerold Penz (lokal)")
446     # Zum Testen wird hier eine unbekannte Adresse eingeschoben.
447     email.recipients.add("unbekannte-adresse@gps.gp", "UNBEKANNT")
448    
449     # Betreff
450     email.subject = "Komplexeres Beispiel"
451    
452     # Nachricht
453     email.message = (
454         "Das ist ein etwas komplexeres Beispiel\n"
455         "\n"
456         "Hier steht normaler Text mit Umlauten (öäüß).\n"
457         "Groß kann man sie auch schreiben -- ÖÄÜ.\n"
458         "\n"
459         "mfg\n"
460         "Gerold\n"
461         ":-)"
462     )
463
464     # Anhaenge (die Pfade sind an meine Testsysteme angepasst)
465     if sys.platform.startswith("win"):
466         filename1 = r"H:\GEROLD\Bilder und Videos\Blumencorso Seefeld 2006\000013.JPG"
467         filename2 = r"H:\GEROLD\Bilder und Videos\Blumencorso Seefeld 2006\000018.JPG"
468     else:
469         filename1 = "/home/gerold/GEROLD/Bilder und Videos/Blumencorso Seefeld 2006/000013.JPG"
470         filename2 = "/home/gerold/GEROLD/Bilder und Videos/Blumencorso Seefeld 2006/000018.JPG"
471     if os.path.isfile(filename1):
472         email.attachments.add_filename(filename1)
473         email.attachments.add_filename(filename2)
474    
475     # Senden und Statusmeldungen anzeigen
476     if email.send():
477         if email.recipients.count() == 1:
478             print "Die Nachricht wurde erfolgreich an den Empfaenger versendet."
479         else:
480             if email.statusdict:
481                 print \
482                     "Die Nachricht wurde mindestens an einen der Empfaenger " + \
483                     "versendet.\nEs sind aber auch Fehler aufgetreten:"
484                 for item in email.statusdict:
485                     print "  Adresse:", item, "Fehler:", email.statusdict[item]
486             else:
487                 print \
488                     "Die Nachricht wurde an alle Empfaenger " + \
489                     "erfolgreich versendet."
490     else:
491         print "Die Nachricht wurde nicht versendet."
492    
493     print "Fertig"
494     print
495
496     # HTML-Email
497     from simplemail import Email
498    
499     print "HTML-Email"
500     print "----------"
501     email = Email(
502         from_address = "server@gps.gp",
503         smtp_server = "gps.gp",
504         to_address = "gerold@gps.gp",
505         header = {"Reply-To": "gerold@gps.gp"},
506     )
507     email.subject = "Das ist ein HTML-Email"
508     email.content_subtype = "html"
509     email.message = \
510         "<h1>Das ist die Überschrift</h1>\n" + \
511         "<p>\n" + \
512         "  Das ist ein <b>kleiner</b><br />\n" + \
513         "  Absatz.\n" + \
514         "</p>\n" + \
515         "<p>\n" + \
516         "  Das ist noch ein <i>Absatz</i>.\n" + \
517         "</p>\n" + \
518         "<p>\n" + \
519         "  mfg<br />\n" + \
520         "  Gerold<br />\n" + \
521         "  :-)\n" + \
522         "</p>"
523     if email.send():
524         print "Die Nachricht wurde gesendet."
525     else:
526         print "Die Nachricht wurde nicht versendet."
527
528     print "Fertig"
529     print
530    
531     ## Googlemail-Email-Beispielcode
532     #from simplemail import Email
533     #
534     #Email(
535     #    from_address = "EMAILNAME@gmail.com",
536     #    to_address = "EMPFAENGER@domain.xx",
537     #    subject = "Googlemail Test",
538     #    message = "Das ist ein Googlemail Test.",
539     #    smtp_server = "smtp.googlemail.com:587",
540     #    smtp_user = "EMAILNAME", # Emailadresse ohne "@gmail.com"
541     #    smtp_password = "PASSWORT",
542     #    use_tls = True # Muss auf True gesetzt sein
543     #).send()
544     
545
546 if __name__ == "__main__":
547     # Wenn dieses Modul mit dem Parameter "test" aufgerufen wird,
548     # dann werden Test-Emails gesendet.
549     if "test" in sys.argv[1:]:
550         test()
551
Note: See TracBrowser for help on using the browser.