[issue34025] SMTP EmailPolicy not setting max_line_length as expected. RCF 2045 states 76 char, Default policy uses 78

Douglas Thor report at bugs.python.org
Mon Jul 2 12:18:14 EDT 2018


New submission from Douglas Thor <doug.thor at gmail.com>:

It appears that the SMTP EmailPolicy object does not correctly set max_line_length.

RFC 2045 (https://www.ietf.org/rfc/rfc2045.txt) requires a max_line_length of 76 characters, while email._policybase.Policy sets it to 78 (which typically is correct).

This causes email attachments to be truncated/corrupted.

While the workaround is quite trivial, debugging the root cause was not as easy. Thus I think this should be fixed in at the std lib level, or at the very least mentioned in the docs.

This was tested on Python 3.5.2. I did not test on 3.7, but I did check the source code and it appears that the bug still exists.



To reproduce:

    import smtplib
    from email.message import EmailMessage
    from email.policy import SMTP

    msg = EmailMessage()
    msg['Subject'] = "Test message"
    msg['To'] = "to at valid_address.com"
    msg['From'] = "from at valid_address.com"
    content = "hello world, I should have an attachment"
    msg.set_content(content)

    maintype = 'image'
    subtype = 'png'
    test_file = './aaa.png'

    with open(test_file, 'rb') as fp:
        data = fp.read()
    msg.add_attachment(data,
                       maintype=maintype,
                       subtype=subtype,
                       filename='aaa.png')

    with smtplib.SMTP_SSL('smtp.address.com', port=465) as s:
        s.ehlo()
        user = 'smtp_user at valid_address.com'
        pw = 'smtp_password'
        s.login(user, pw)
        s.send_message(msg)



Check the raw email message received. You'll see that lines have a length of 78 and are padded with "==" which makes the total length 80 chars:

    ...
    MIME-Version: 1.0
    Content-Type: multipart/mixed; boundary="===============1585876390557366757=="

    --===============1585876390557366757==
    Content-Type: text/plain; charset="utf-8"
    Content-Transfer-Encoding: 7bit

    hello world, I should have an attachment

    --===============1585876390557366757==
    Content-Type: image/png
    Content-Transfer-Encoding: base64
    Content-Disposition: attachment; filename="aaa.png"
    MIME-Version: 1.0

    iVBORw0KGgoAAAANSUhEUgAABE0AAALFCAYAAAA2rB2uAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjw==
    C/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAD/jklEQVR4Xg==
    7P1rsHVJWecBvl4RFFAuIpcCEYQuLkUDBcgBoQsULLnJXaoaEApe5FLSTVFUC4JACQ1NiQdsQoFBAQ==
    iW4aQSWEt4cQwwB6xqC+CPYHqX5jQno+jHyYCCMmZsKJmYjZs3/rnOe8uXNnrpW5LrlzrfVfVSfOeQ==
    986Vl/+T678y//nkk9+3OXdu+78uISAEhIAQEAJCQAgIASEgBISAEBACQkAICIEdBBBN9CMM1AfUBw==
    ...



Next, modify the code with:

    from email.policy import SMTP
    smtp_pol = SMTP.clone(max_line_length=76)
    msg = EmailMessage(policy=smtp_pol)
    msg['Subject'] = ...



And check the raw email again. This time, lines are 76 chars long and have no padding. Data is the same, just wrapped differently as expected.

    ...
    MIME-Version: 1.0
    Content-Type: multipart/mixed;
     boundary="===============4874323426669347622=="

    --===============4874323426669347622==
    Content-Type: text/plain; charset="utf-8"
    Content-Transfer-Encoding: 7bit

    hello world, I should have an attachment

    --===============4874323426669347622==
    Content-Type: image/png
    Content-Transfer-Encoding: base64
    Content-Disposition: attachment; filename="aaa.png"
    MIME-Version: 1.0

    iVBORw0KGgoAAAANSUhEUgAABE0AAALFCAYAAAA2rB2uAAAAAXNSR0IArs4c6QAAAARnQU1BAACx
    jwv8YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAA/45JREFU
    eF7s/WuwdUlZ5wG+XhEUUC4ilwIRhC4uRQMFyAGhCxQsucldqhoQCl7kUtJNUVQLgkAJDU2JB2xC
    gUEBiW4aQSWEt4cQwwB6xqC+CPYHqX5jQno+jHyYCCMmZsKJmYjZs3/rnOe8uXNnrpW5LrlzrfVf
    ...



This fix *should* be extremely easy:

    diff --git "a/policy.py" "b/policy.py"
    index 6ac64a5..046b788 100644
    --- "a/policy.py"
    +++ "b/policy.py"
    @@ -209,6 +209,6 @@ default = EmailPolicy()
     # Make the default policy use the class default header_factory
     del default.header_factory
     strict = default.clone(raise_on_defect=True)
    -SMTP = default.clone(linesep='\r\n')
    +SMTP = default.clone(linesep='\r\n', max_line_length=76)
     HTTP = default.clone(linesep='\r\n', max_line_length=None)
     SMTPUTF8 = SMTP.clone(utf8=True)

----------
components: email
messages: 320898
nosy: Douglas Thor, barry, r.david.murray
priority: normal
severity: normal
status: open
title: SMTP EmailPolicy not setting max_line_length as expected. RCF 2045 states 76 char, Default policy uses 78
type: behavior
versions: Python 3.5

_______________________________________
Python tracker <report at bugs.python.org>
<https://bugs.python.org/issue34025>
_______________________________________


More information about the Python-bugs-list mailing list