1

BACKGROUND

Regarding the following articles:

All the problems and solutions refer to PHP issue, but I have run into this problem in Python.

If I send the emails directly to recipients, all is well, no exclamation marks appear, and the message displays properly.

However, utilizing our "Sympa" (https://www.sympa.org/) system that the University uses for it "mailing list" solution, emails from this system have the exclamation marks and line breaks inserted in the message and HTML breaks causing display issues.

The problem stems from line length. Any line longer than a magical 998 character length line gets this exclamation marks and line breaks inserted.

NOW THE QUESTION

One of the solutions they mention is encoding the body of a message in base64, which apparently is immune to the line length issue. However, I can not figure out how to properly form a message in Python and have the proper headers and encoding happen so the message will display properly in an email client.

Right now, I have only succeed in sending emails with base64 encode bodies as attached files. Bleck!

I need to send HTML encoded emails (tables and some formatting). I create one very long concatenated string of all the html squished together. It is ugly but will display properly.

HELP?!

NOTE: If anyone else has had this problem and has a solution that will allow me to send emails that are not plagued by line length issue, I am all ears!

Source Code as Requested

# Add support for emailing reports
import smtplib
# from email.mime.text import MIMEText
from email.mime.message import MIMEMessage
from email.encoders import encode_base64
from email.message import Message

... ...

headerData = {"rDate": datetime.datetime.now().strftime('%Y-%m-%d')}
msg_body = msg_header.format(**headerData) + contact_table + spacer + svc_table

theMsg = Message()
theMsg.set_payload(msg_body)
encode_base64(theMsg)
theMsg.add_header('Content-Transfer-Encoding', 'base64')
envelope = MIMEMessage(theMsg, 'html')

theSubject = "Audit for: "+aService['description']
envelope['Subject'] = theSubject

from_addr = "xxx@xxx"
envelope['From'] = from_addr

to_addrs = "xxx@xxxx"
# to_addrs = aService['contact_email']
envelope['To'] = to_addrs

# Send the message via our own SMTP server.
s = smtplib.SMTP('x.x.x.x')
s.sendmail(from_addr, to_addrs, envelope.as_string())
s.quit()

SOLUTION, thank you @Serge Ballesta Going back to MIMEText, and specifying a character set seemed to do the trick:

envelope = MIMEText(msg_body, 'html', _charset='utf-8') 
assert envelope['Content-Transfer-Encoding'] == 'base64'

envelope['Subject'] = "Audit for: "+aService['description']

from_addr = "f5-admins@utlists.utexas.edu"
envelope['From'] = from_addr

to_addrs = "xx-test@xx.xx.edu"
envelope['To'] = to_addrs

# Send the message via our own SMTP server.
s = smtplib.SMTP('xx.xx.xx.edu')
s.sendmail(from_addr, to_addrs, envelope.as_string())
s.quit()

Apparently I was just stabbing around and did not account for character set. Using MIMEText and not MIMEMessage.

jewettg
  • 1,098
  • 11
  • 20
  • There are ways to force the encoding of the message, but it may depend on how you compose it. If at any moment you use `setContent(content)` it may be enough to use `setContent(content, cte='base64')`. I could be more detailed if you show your code. – Serge Ballesta Mar 05 '19 at 22:37
  • Source code posted, anonymized. Thank you @SergeBallesta! :) – jewettg Mar 05 '19 at 23:50
  • `encode_base64` is the correct way to encode an `email.message.Message`, so this part should work. What worries me is that you have `msg_body = msg_header.format(...)+...` which let think that `msg_body` contains headers when it should not. Could you show a simplified but reproducible example? – Serge Ballesta Mar 06 '19 at 07:21
  • Sorry .. bad use of variable name there. No headers in there, I should say "message_top", it is the content of the message before the dynamic content (done with loops, API calls, etc..) to generated the "contact_table" and "svc_table". The only headers are done in the "envelope" which is an instance of "MIMEMessage". – jewettg Mar 06 '19 at 14:54

1 Answers1

2

Normally, email.mime.MIMEText automatically sets the Content-Transfert-Encoding to base64 if the body is not declared to be plain ASCII. So, assuming that body contains the HTML text of the body of the message (no mail headers there), declaring it as utf-8 should be enough:

msg = email.mime.text.MIMEText(body, 'html', _charset='utf-8')

# assert the cte:
assert msg['Content-Transfer-Encoding'] == 'base64'

theSubject = "Audit for: "+aService['description']
msg['Subject'] = theSubject

from_addr = "xxx@xxx"
msg['From'] = from_addr

to_addrs = "xxx@xxxx"
# to_addrs = aService['contact_email']
msg['To'] = to_addrs

# optionally set other headers
# msg['Date']=datetime.datetime.now().isoformat()

# Send the message
s = smtplib.SMTP('x.x.x.x')
s.sendmail(from_addr, to_addrs, msg.as_bytes())
s.quit()
Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
  • I tried your suggestion, received the error: AttributeError: MIMEText instance has no attribute 'as_bytes'. Should it be MIMEText or MIMEMessage? Digging... – jewettg Mar 06 '19 at 15:10
  • I tried your suggestion, received the error: AttributeError: MIMEText instance has no attribute 'as_bytes'. I left it as: as_string() and it worked! All my html emails are NO LONGER corrupted (no "!" and linebreaks added). THANK YOU! – jewettg Mar 06 '19 at 15:27
  • 1
    If you don't have as_bytes, you should upgrade to recent Python 3 (3.4 or better). – Max Mar 06 '19 at 15:34