Home > Blockchain >  smtplib.SMTP.send_message replaces "From" with ">From" in text content
smtplib.SMTP.send_message replaces "From" with ">From" in text content

Time:03-25

Consider the following example to send an email via smtplib and email libraries in Python 3:

import smtplib
from email.message import EmailMessage

# Make the email:
msg = EmailMessage()
msg.set_content("""
Hi,
From foo
from bar
 From baz
""")

msg['Subject'] = 'Example'
msg['From'] = "[email protected]"
msg['To'] = "[email protected]"

# Send the message via our own SMTP server.
s = smtplib.SMTP('localhost')
s.send_message(msg)
s.quit()

This will produce an email that looks like this:

Message-ID: <...>
Date: Thu, 17 Mar 2022 12:01:47 -0700 (PDT)
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 7bit
MIME-Version: 1.0
Subject: Example
From: [email protected]
To: [email protected]


Hi,
>From foo
from bar
 From baz

Note how From was turned to >From in the body of the email if it is in the beginning of a line. I would like to not put the > in there if there is From used in the email text. I suspect this is to prevent the from being injected in the body if it was not defined in the headers but in my case it is defined.

Interestingly, this is not in the EmailMessage itself:

>>> print(msg)
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 7bit
MIME-Version: 1.0
Subject: Example
From: [email protected]
To: [email protected]


Hi,
From foo
from bar
 From baz

It seems the \nFrom in the body is turned to \n>From in the method SMTP(...).send_message and it is not caused by the email policy in the message itself.

So the question is how to stop replacing the \nFrom with \n>From in the text body when sending emails.

CodePudding user response:

I found the answer and decided to document it if someone else stumbles on the same issue. In short, the answer is that there is not much you can do.

Basically this is what happens in smtplib.SMTP.send_message:

def send_message(self, msg, from_addr=None, to_addrs=None,
                 mail_options=(), rcpt_options=()):
        ...
    else:
        g = email.generator.BytesGenerator(bytesmsg)
    g.flatten(msg_copy, linesep='\r\n')

Note how the generator is created by just passing the bytesmsg as an argument.

Then the initiation of the generator:

def __init__(self, outfp, mangle_from_=None, maxheaderlen=None, *,
             policy=None):
    ...
    if mangle_from_ is None:
        mangle_from_ = True if policy is None else policy.mangle_from_
    self._fp = outfp
    self._mangle_from_ = mangle_from_
    self.maxheaderlen = maxheaderlen
    self.policy = policy

Only the outfp is passed (the bytesmsg). The policy is None thus the mangle_from_ is always True in the smtplib's send_message method.

After a couple of method calls, a method is called that executes this:

...
if self._mangle_from_:
    payload = fcre.sub('>From ', payload)
self._write_lines(payload)
...

This adds the > in front of n\From in the text content of an email.

Therefore, it seems one cannot get rid of replacing From to >From in the text body. You may subclass the method, copy the code and fix the policy yourself but that won't be clean.

  • Related