Advertisement
johnmahugu

python - sending an email using python tutorial and library

Jul 8th, 2015
598
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 21.39 KB | None | 0 0
  1. generate and send mail with python: tutorial
  2.  
  3. The code and tips here, are all included in the new pyzmail library. More samples and tips can be found in the API documentation.
  4. This article follows two other articles (1, 2) about how to parse emails in Python.
  5. These articles describe very well mail usages and rules and can be helpful for non Python developer too.
  6.  
  7. The goal here is to generate a valid email including internationalized content, attachments and even inline images, and send it to a SMTP server.
  8.  
  9. The procedure can be achieved in 3 steps :
  10.  
  11. * compose the body of the email.
  12. * compose the header of the email.
  13. * send the email to a SMTP server
  14.  
  15. At the end you will find the sources.
  16.  
  17. The first rule to keep in mind all the time, is that emails are 7bits only, they can contains us-ascii characters only ! A lot of RFCs define rules to encode non us-ascii characters when they are required: RFC2047 to encode headers, RFC2045 for encoding body using the MIME format or RFC2231 for MIME parameters like the attachment filename.
  18. The mail header
  19.  
  20. The header is composed of lines having a name and a value. RFC2047 let you use quoted or binary encoding to encode non us-ascii characters in it.
  21. For example:
  22.  
  23. Subject: Courrier électronique en Français
  24.  
  25. become using the quoted format
  26.  
  27. Subject: =?iso-8859-1?q?Courrier_=E8lectronique_en_Fran=E7ais?=
  28.  
  29. or using the binary one.
  30.  
  31. Subject: =?iso-8859-1?b?Q291cnJpZXIg6GxlY3Ryb25pcXVlIGVuIEZyYW7nYWlz?=
  32.  
  33. Here the quoted format is readable and shorter. Python email.header.Header object generates user friendly header by choosing the quoted format when the encoding shares characters with us-ascii, and the binary one for encoding like the Chinese big5 that requires the encoding of all characters.
  34.  
  35. >>> str(Header(u'Courrier \xe8lectronique en Fran\xe7ais', 'iso-8859-1'))
  36. '=?iso-8859-1?q?Courrier_=E8lectronique_en_Fran=E7ais?='
  37.  
  38. Header values have different types (text, date, address, ...) that all require different encoding rules. For example, in an address like :
  39.  
  40. Sender: Alain Spineux <alain.spineux@gmail.com>
  41.  
  42. The email part <alain.spineux@gmail.com> has to be in us-ascii and cannot be encoded. The name part cannot contains some special characters without quoting : []\()<>@,:;".
  43. We can easily understand why "<>" are in the list, others have all their own story.
  44.  
  45. For example, the use of the "Dr." prefix requires to quote the name because of the '.':
  46.  
  47. Sender: "Dr. Alain Spineux" <alain.spineux@gmail.com>
  48.  
  49. For a name with non us-ascii characters like (look at the "ï" in Alaïn), the name must be encoded.
  50.  
  51. Sender: Dr. Alïan Spineux <alain.spineux@gmail.com>
  52.  
  53. must be written :
  54.  
  55. Sender: =?iso-8859-1?q?Dr=2E_Ala=EFn_Spineux?= <alain.spineux@gmail.com>
  56.  
  57. Notice that the '.' in the prefix is replaced by "=2E", because Header preventively encode all non alpha or digit characters to match the most restrictive header rules.
  58.  
  59. The Python function email.utils.formataddr() quotes the special characters but don't encode non us-ascii characters. On the other hand, email.header.Header can encode non us-ascii characters but ignore all specials rule about address encoding.
  60. Let see how both work:
  61.  
  62. >>> email.Utils.formataddr(('Alain Spineux', 'alain.spineux@gmail.com'))
  63. 'Alain Spineux <alain.spineux@gmail.com>'
  64.  
  65. This is a valid header value for a To: field
  66.  
  67. >>> str(Header('Dr. Alain Spineux <alain.spineux@gmail.com>'))
  68. 'Dr. Alain Spineux <alain.spineux@gmail.com>'
  69.  
  70. Here the '.' should be escaped like these 13 characters: []\()<>@,:;".
  71.  
  72. >>> email.Utils.formataddr(('"Alain" Spineux', 'alain.spineux@gmail.com'))
  73. '"\\"Alain\\" Spineux" <alain.spineux@gmail.com>'
  74.  
  75. Here '"' is escaped using '\', this is fine.
  76.  
  77. >>> email.Utils.formataddr((u'Ala\xefn Spineux', 'alain.spineux@gmail.com'))
  78. u'Ala\xefn Spineux <alain.spineux@gmail.com>'
  79.  
  80. formataddr() don't handle non us-ascii string, this must be done by Header object
  81.  
  82. >>> str(Header(email.Utils.formataddr((u'Ala\xefn Spineux', 'alain.spineux@gmail.com'))))
  83. '=?utf-8?q?Ala=C3=AFn_Spineux_=3Calain=2Espineux=40gmail=2Ecom=3E?='
  84.  
  85. This is not valid because the address is also encoded and an old or some recent MUA will't handle this. The good form here is :
  86.  
  87. =?utf-8?q?Ala=C3=AFn_Spineux?= <alain.spineux@gmail.com>'
  88.  
  89. Function format_addresses(addresses, header_name=None, charset=None) handle carefully the encoding of the addresses.
  90.  
  91. >>> str(format_addresses([ (u'Ala\xefn Spineux', 'alain.spineux@gmail.com'), ('John', 'John@smith.com'), ], 'to', 'iso-8859-1'))
  92. '=?iso-8859-1?q?Ala=EFn_Spineux?= <alain.spineux@gmail.com> ,\n John <John@smith.com>'
  93.  
  94. Bytes and unicode string can be mixed. Addresses must always be us-ascii. Byte string must be encoded using charset or be us-ascii. Unicode strings that cannot be encoded using charset will be encoded using utf-8 automatically by Header.
  95.  
  96. For dates, use the email.utils.formatdate() this way.
  97.  
  98. >>> email.utils.formatdate(time.time(), localtime=True)
  99. 'Wed, 10 Aug 2011 16:46:30 +0200'
  100.  
  101. The mail body
  102.  
  103. Depending of your need :
  104.  
  105. * text and/or html version of the message
  106. * related inline images
  107. * attached files
  108.  
  109. the structure of the MIME email may vary, but the general one is as follow:
  110.  
  111. multipart/mixed
  112. |
  113. +-- multipart/related
  114. | |
  115. | +-- multipart/alternative
  116. | | |
  117. | | +-- text/plain
  118. | | +-- text/html
  119. | |
  120. | +-- image/gif
  121. |
  122. +-- application/msword
  123.  
  124. Un-needed parts will be removed by function gen_mail(text, html=None, attachments=[], relateds=[]) regarding your parameters.
  125.  
  126. >>> print gen_mail(text=(u'Bonne journ\xe8e', 'iso-8859-1'), \
  127. html=None, \
  128. attachments=[ (u'Text attach\xe8'.encode('iso-8859-1'), 'text', 'plain', 'filename.txt', 'iso-8859-1'), ] )
  129. From nobody Thu Aug 11 08:05:14 2011
  130. Content-Type: multipart/mixed; boundary="===============0992984520=="
  131. MIME-Version: 1.0
  132.  
  133. --===============0992984520==
  134. Content-Type: text/plain; charset="iso-8859-1"
  135. MIME-Version: 1.0
  136. Content-Transfer-Encoding: quoted-printable
  137.  
  138. Bonne journ=E8e
  139. --===============0992984520==
  140. Content-Type: text/plain; charset="iso-8859-1"
  141. MIME-Version: 1.0
  142. Content-Transfer-Encoding: quoted-printable
  143. Content-Disposition: attachment; filename="filename.txt"
  144.  
  145. Text attach=E8
  146. --===============0992984520==--
  147.  
  148. text and html are tuple of the form (content, encoding).
  149. Items of the attachments list can be tuples of the form (content, maintype, subtype, filename, charset) or can be MIME object inheriting from MIMEBase. If maintype is 'text', charset must match the encoding of the content, or if content is unicode, charset will be used to encode it. For other value of maintype, charset is not used.
  150. relateds is similar to attachments but content is related to the message in HTML to allow embedding of images or other contents. filename is replaced by content_id that must match the cid: references inside the HTML message.
  151.  
  152. Attachments can have non us-ascii filename, but this is badly supported by some MUA and then discouraged. Anyway, if you want to use non us-ascii filename, RFC2231 need the encoding but also the language. Replace filename, by a tuple of the form (encoding, language, filename), for example, use ('iso-8859-1', 'fr', u'r\xe9pertoir.png'.encode('iso-8859-1')) instead of 'filename.txt'.
  153.  
  154. The attached source code provide more samples. Look carefully in it how I encode or not the unicode string and content before to use them.
  155. Send your email
  156.  
  157. The Python smtplib library handles SSL, STARTLS, and authentication, all you need to connect to any SMTP server. The library also reports all errors carefully.
  158.  
  159. The send_mail(smtp_host, sender, recipients, subject, msg, default_charset, cc=[], bcc=[], smtp_port=25, smtp_mode='normal', smtp_login=None, smtp_password=None, message_id_string=None) function first fill in the header about sender and recipients using format_addresses() function, and send the email to the SMTP using the protocols and credentials you have choose with smtp_* variables.
  160. sender is a tuples and recipients, cc, bcc are list of tuple of the form [ (name, address), .. ] like expected by format_addresses().
  161. default_charset will be used as default encoding when generating the email (to encode unicode string), and as default encoding for byte string.
  162. subject is the subject of the email.
  163. msg is a MIME object like returned by gen_mail().
  164. smtp_mode can be 'normal', 'ssl' or 'tls'.
  165.  
  166. For example if you want to use your GMAIL account to send your emails, use this setup:
  167.  
  168. smtp_host='smtp.gmail.com'
  169. smtp_port=587
  170. smtp_mode='tls'
  171. smtp_login='your.address@gmail.com'
  172. smtp_password='yourpassword'
  173. sender=('Your Name', smtp_login)
  174.  
  175. Most of the time you will just need to specify smtp_host
  176. Source
  177.  
  178. #!/bin/env/python
  179. #
  180. # sendmail.py
  181. # (c) Alain Spineux <alain.spineux@gmail.com>
  182. # http://blog.magiksys.net/sending-email-using-python-tutorial
  183. # Released under GPL
  184. #
  185. import os, sys
  186. import time
  187. import base64
  188. import smtplib
  189. import email
  190. import email.header
  191. import email.utils
  192. import email.mime
  193. import email.mime.base
  194. import email.mime.text
  195. import email.mime.image
  196. import email.mime.multipart
  197.  
  198. def format_addresses(addresses, header_name=None, charset=None):
  199. """This is an extension of email.utils.formataddr.
  200. Function expect a list of addresses [ ('name', 'name@domain'), ...].
  201. The len(header_name) is used to limit first line length.
  202. The function mix the use Header(), formataddr() and check for 'us-ascii'
  203. string to have valid and friendly 'address' header.
  204. If one 'name' is not unicode string, then it must encoded using 'charset',
  205. Header will use 'charset' to decode it.
  206. Unicode string will be encoded following the "Header" rules : (
  207. try first using ascii, then 'charset', then 'uft8')
  208. 'name@address' is supposed to be pure us-ascii, it can be unicode
  209. string or not (but cannot contains non us-ascii)
  210.  
  211. In short Header() ignore syntax rules about 'address' field,
  212. and formataddr() ignore encoding of non us-ascci chars.
  213. """
  214. header=email.header.Header(charset=charset, header_name=header_name)
  215. for i, (name, addr) in enumerate(addresses):
  216. if i!=0:
  217. # add separator between addresses
  218. header.append(',', charset='us-ascii')
  219. # check if address name is a unicode or byte string in "pure" us-ascii
  220. try:
  221. if isinstance(name, unicode):
  222. # convert name in byte string
  223. name=name.encode('us-ascii')
  224. else:
  225. # check id byte string contains only us-ascii chars
  226. name.decode('us-ascii')
  227. except UnicodeError:
  228. # Header will use "RFC2047" to encode the address name
  229. # if name is byte string, charset will be used to decode it first
  230. header.append(name)
  231. # here us-ascii must be used and not default 'charset'
  232. header.append('<%s>' % (addr,), charset='us-ascii')
  233. else:
  234. # name is a us-ascii byte string, i can use formataddr
  235. formated_addr=email.utils.formataddr((name, addr))
  236. # us-ascii must be used and not default 'charset'
  237. header.append(formated_addr, charset='us-ascii')
  238.  
  239. return header
  240.  
  241.  
  242. def gen_mail(text, html=None, attachments=[], relateds=[]):
  243. """generate the core of the email message.
  244. text=(encoded_content, encoding)
  245. html=(encoded_content, encoding)
  246. attachments=[(data, maintype, subtype, filename, charset), ..]
  247. if maintype is 'text', data lmust be encoded using charset
  248. filename can be us-ascii or (charset, lang, encoded_filename)
  249. where encoded_filename is encoded into charset, lang can be empty but
  250. usually the language related to the charset.
  251. relateds=[(data, maintype, subtype, content_id, charset), ..]
  252. idem attachment above, but content_id is related to the "CID" reference
  253. in the html version of the message.
  254. """
  255.  
  256. main=text_part=html_part=None
  257. if text:
  258. content, charset=text
  259. main=text_part=email.mime.text.MIMEText(content, 'plain', charset)
  260.  
  261. if html:
  262. content, charset=html
  263. main=html_part=email.mime.text.MIMEText(content, 'html', charset)
  264.  
  265. if not text_part and not html_part:
  266. main=text_part=email.mime.text.MIMEText('', 'plain', 'us-ascii')
  267. elif text_part and html_part:
  268. # need to create a multipart/alternative to include text and html version
  269. main=email.mime.multipart.MIMEMultipart('alternative', None, [text_part, html_part])
  270.  
  271. if relateds:
  272. related=email.mime.multipart.MIMEMultipart('related')
  273. related.attach(main)
  274. for part in relateds:
  275. if not isinstance(part, email.mime.base.MIMEBase):
  276. data, maintype, subtype, content_id, charset=part
  277. if (maintype=='text'):
  278. part=email.mime.text.MIMEText(data, subtype, charset)
  279. else:
  280. part=email.mime.base.MIMEBase(maintype, subtype)
  281. part.set_payload(data)
  282. email.Encoders.encode_base64(part)
  283. part.add_header('Content-ID', '<'+content_id+'>')
  284. part.add_header('Content-Disposition', 'inline')
  285. related.attach(part)
  286. main=related
  287.  
  288. if attachments:
  289. mixed=email.mime.multipart.MIMEMultipart('mixed')
  290. mixed.attach(main)
  291. for part in attachments:
  292. if not isinstance(part, email.mime.base.MIMEBase):
  293. data, maintype, subtype, filename, charset=part
  294. if (maintype=='text'):
  295. part=email.mime.text.MIMEText(data, subtype, charset)
  296. else:
  297. part=email.mime.base.MIMEBase(maintype, subtype)
  298. part.set_payload(data)
  299. email.Encoders.encode_base64(part)
  300. part.add_header('Content-Disposition', 'attachment', filename=filename)
  301. mixed.attach(part)
  302. main=mixed
  303.  
  304. return main
  305.  
  306. def send_mail(smtp_host, sender, recipients, subject, msg, default_charset, cc=[], bcc=[], smtp_port=25, smtp_mode='normal', smtp_login=None, smtp_password=None, message_id_string=None):
  307. """
  308.  
  309. """
  310.  
  311. mail_from=sender[1]
  312. rcpt_to=map(lambda x:x[1], recipients)
  313. rcpt_to.extend(map(lambda x:x[1], cc))
  314. rcpt_to.extend(map(lambda x:x[1], bcc))
  315.  
  316. msg['From'] = format_addresses([ sender, ], header_name='from', charset=default_charset)
  317. msg['To'] = format_addresses(recipients, header_name='to', charset=default_charset)
  318. msg['Cc'] = format_addresses(cc, header_name='cc', charset=default_charset)
  319. msg['Subject'] = email.header.Header(subject, default_charset)
  320. utc_from_epoch=time.time()
  321. msg['Date'] = email.utils.formatdate(utc_from_epoch, localtime=True)
  322. msg['Messsage-Id'] =email.utils.make_msgid(message_id_string)
  323.  
  324. # Send the message
  325. errmsg=''
  326. failed_addresses=[]
  327. try:
  328. if smtp_mode=='ssl':
  329. smtp=smtplib.SMTP_SSL(smtp_host, smtp_port)
  330. else:
  331. smtp=smtplib.SMTP(smtp_host, smtp_port)
  332. if smtp_mode=='tls':
  333. smtp.starttls()
  334.  
  335. if smtp_login and smtp_password:
  336. # login and password must be encoded
  337. # because HMAC used in CRAM_MD5 require non unicode string
  338. smtp.login(smtp_login.encode('utf-8'), smtp_password.encode('utf-8'))
  339.  
  340. ret=smtp.sendmail(mail_from, rcpt_to, msg.as_string())
  341. smtp.quit()
  342. except (socket.error, ), e:
  343. errmsg='server %s:%s not responding: %s' % (smtp_host, smtp_port, e)
  344. except smtplib.SMTPAuthenticationError, e:
  345. errmsg='authentication error: %s' % (e, )
  346. except smtplib.SMTPRecipientsRefused, e:
  347. # code, errmsg=e.recipients[recipient_addr]
  348. errmsg='recipients refused: '+', '.join(e.recipients.keys())
  349. except smtplib.SMTPSenderRefused, e:
  350. # e.sender, e.smtp_code, e.smtp_error
  351. errmsg='sender refused: %s' % (e.sender, )
  352. except smtplib.SMTPDataError, e:
  353. errmsg='SMTP protocol mismatch: %s' % (e, )
  354. except smtplib.SMTPHeloError, e:
  355. errmsg="server didn't reply properly to the HELO greeting: %s" % (e, )
  356. except smtplib.SMTPException, e:
  357. errmsg='SMTP error: %s' % (e, )
  358. except Exception, e:
  359. errmsg=str(e)
  360. else:
  361. if ret:
  362. failed_addresses=ret.keys()
  363. errmsg='recipients refused: '+', '.join(failed_addresses)
  364.  
  365. return msg, errmsg, failed_addresses
  366.  
  367. And how to use it :
  368.  
  369. smtp_host='max'
  370. smtp_port=25
  371.  
  372. if False:
  373. smtp_host='smtp.gmail.com'
  374. smtp_port='587'
  375. smtp_login='your.addresse@gmail.com'
  376. smtp_passwd='your.password'
  377.  
  378. sender=(u'Ala\xefn Spineux', 'alain.spineux@gmail.com')
  379. sender=(u'Alain Spineux', u'alain.spineux@gmail.com')
  380.  
  381. root_addr='root@max.asxnet.loc'
  382. recipients=[ ('Alain Spineux', root_addr),
  383. # (u'Alain Spineux', root_addr),
  384. # ('Dr. Alain Sp<i>neux', root_addr),
  385. # (u'Dr. Alain Sp<i>neux', root_addr),
  386. # (u'Dr. Ala\xefn Spineux', root_addr),
  387. # (u'Dr. Ala\xefn Spineux', root_addr),
  388. # ('us_ascii_name_with_a_space_some_where_in_the_middle to_allow_python_Header._split()_to_split_according_RFC2822', root_addr),
  389. # (u'This-is-a-very-long-unicode-name-with-one-non-ascii-char-\xf4-to-force-Header()-to-use-RFC2047-encoding-and-split-in-multi-line', root_addr),
  390. # ('this_line_is_too_long_and_dont_have_any_white_space_to_allow_Header._split()_to_split_according_RFC2822', root_addr),
  391. # ('Alain Spineux', root_addr),
  392. ]
  393.  
  394. smile_png=base64.b64decode(
  395. """iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOBAMAAADtZjDiAAAAMFBMVEUQEAhaUjlaWlp7e3uMezGU
  396. hDGcnJy1lCnGvVretTnn5+/3pSn33mP355T39+//75SdwkyMAAAACXBIWXMAAA7EAAAOxAGVKw4b
  397. AAAAB3RJTUUH2wcJDxEjgefAiQAAAAd0RVh0QXV0aG9yAKmuzEgAAAAMdEVYdERlc2NyaXB0aW9u
  398. ABMJISMAAAAKdEVYdENvcHlyaWdodACsD8w6AAAADnRFWHRDcmVhdGlvbiB0aW1lADX3DwkAAAAJ
  399. dEVYdFNvZnR3YXJlAF1w/zoAAAALdEVYdERpc2NsYWltZXIAt8C0jwAAAAh0RVh0V2FybmluZwDA
  400. G+aHAAAAB3RFWHRTb3VyY2UA9f+D6wAAAAh0RVh0Q29tbWVudAD2zJa/AAAABnRFWHRUaXRsZQCo
  401. 7tInAAAAaElEQVR4nGNYsXv3zt27TzHcPup6XDBmDsOeBvYzLTynGfacuHfm/x8gfS7tbtobEM3w
  402. n2E9kP5n9N/oPZA+//7PP5D8GSCYA6RPzjlzEkSfmTlz+xkgffbkzDlAuvsMWAHDmt0g0AUAmyNE
  403. wLAIvcgAAAAASUVORK5CYII=
  404. """)
  405. angry_gif=base64.b64decode(
  406. """R0lGODlhDgAOALMAAAwMCYAAAACAAKaCIwAAgIAAgACAgPbTfoR/YP8AAAD/AAAA//rMUf8A/wD/
  407. //Tw5CH5BAAAAAAALAAAAAAOAA4AgwwMCYAAAACAAKaCIwAAgIAAgACAgPbTfoR/YP8AAAD/AAAA
  408. //rMUf8A/wD///Tw5AQ28B1Gqz3S6jop2sxnAYNGaghAHirQUZh6sEDGPQgy5/b9UI+eZkAkghhG
  409. ZPLIbMKcDMwLhIkAADs=
  410. """)
  411. pingu_png=base64.b64decode(
  412. """iVBORw0KGgoAAAANSUhEUgAAABoAAAATBAMAAAB8awA1AAAAMFBMVEUQGCE5OUJKa3tSUlJSrdZj
  413. xu9rWjl7e4SljDGlnHutnFK9vbXGxsbWrTHW1tbv7+88a/HUAAAACXBIWXMAAA7EAAAOxAGVKw4b
  414. AAAAB3RJTUUH2wgJDw8mp5ycCAAAAAd0RVh0QXV0aG9yAKmuzEgAAAAMdEVYdERlc2NyaXB0aW9u
  415. ABMJISMAAAAKdEVYdENvcHlyaWdodACsD8w6AAAADnRFWHRDcmVhdGlvbiB0aW1lADX3DwkAAAAJ
  416. dEVYdFNvZnR3YXJlAF1w/zoAAAALdEVYdERpc2NsYWltZXIAt8C0jwAAAAh0RVh0V2FybmluZwDA
  417. G+aHAAAAB3RFWHRTb3VyY2UA9f+D6wAAAAh0RVh0Q29tbWVudAD2zJa/AAAABnRFWHRUaXRsZQCo
  418. 7tInAAAA0klEQVR4nE2OsYrCUBBFJ42woOhuq43f4IfYmD5fsO2yWNhbCFZ21ovgFyQYG1FISCxt
  419. Xi8k3KnFx8xOTON0Z86d4ZKKiNowM5QEUD6FU9uCSWwpYThrSQuj2fjLso2DqB9OBqqvJFiVllHa
  420. usJedty1NFe2brRbs7ny5aIP8dSXukmyUABQ0CR9AU9d1IOO1EZgjg7n+XEBpOQP0E/rUz2Rlw09
  421. Amte7bdVSs/s7py5i1vFRsnFuW+gdysSu4vzv9vm57faJuY0ywFhAFmupO/zDzDcxlhVE/gbAAAA
  422. AElFTkSuQmCC
  423. """)
  424.  
  425. text_utf8="""This is the the text part.
  426. With a related picture: cid:smile.png
  427. and related document: cid:related.txt
  428.  
  429. Bonne journ\xc3\xa9ee.
  430. """
  431. utext=u"""This is the the text part.
  432. With a related picture: cid:smile.png
  433. and related document: cid:related.txt
  434. Bonne journ\xe9e.
  435. """
  436. data_text=u'Text en Fran\xe7ais'
  437. related_text=u'Document relatif en Fran\xe7ais'
  438.  
  439. html="""<html><body>
  440. This is the html part with a related picture: <img src="cid:smile.png" />
  441. and related document: <a href="cid:related.txt">here</a><br>
  442. Bonne journ&eacute;e.
  443. </body></html>
  444. """
  445. relateds=[ (smile_png, 'image', 'png', 'smile.png', None),
  446. (related_text.encode('iso-8859-1'), 'text', 'plain', 'related.txt', 'iso-8859-1'),
  447. ]
  448.  
  449. pingu_att=email.mime.image.MIMEImage(pingu_png, 'png')
  450. pingu_att.add_header('Content-Disposition', 'attachment', filename=('iso-8859-1', 'fr', u'ping\xfc.png'.encode('iso-8859-1')))
  451.  
  452. pingu_att2=email.mime.image.MIMEImage(pingu_png, 'png')
  453. pingu_att2.add_header('Content-Disposition', 'attachment', filename='pingu.png')
  454.  
  455. attachments=[ (angry_gif, 'image', 'gif', ('iso-8859-1', 'fr', u'\xe4ngry.gif'.encode('iso-8859-1')), None),
  456. (angry_gif, 'image', 'gif', 'angry.gif', None),
  457.  
  458. (data_text.encode('iso-8859-1'), 'text', 'plain', 'document.txt', 'iso-8859-1'),
  459. pingu_att,
  460. pingu_att2]
  461.  
  462. mail=gen_mail((utext.encode('iso-8859-1'), 'iso-8859-1'), (html, 'us-ascii'), attachments, relateds)
  463.  
  464. msg, errmsg, failed_addresses=send_mail(smtp_host, \
  465. sender, \
  466. recipients, \
  467. u'My Subject', \
  468. mail, \
  469. default_charset='iso-8859-1',
  470. cc=[('Gama', root_addr), ],
  471. bcc=[('Colombus', root_addr), ],
  472. smtp_port=smtp_port,
  473. )
  474.  
  475. print msg
  476. print errmsg
  477. print failed_addresses
  478.  
  479.  
  480. Tags:
  481.  
  482. * it
  483. * python
  484. * mail
  485. * tutorial
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement