python-3.xsocketsemailmimepython-sockets

How could I separate email using Python MIME multipart?


Sending an email using sockets. I want to separate plain text and attachment. I use MIME multipart/mixed; boundary.

cSockSSL.send("MIME-Version: 1.0\r\n".encode())
cSockSSL.send("Content-Type: multipart/mixed; boundary=gg4g5gg\r\n".encode())
cSockSSL.send("--gg4g5gg".encode('ascii'))

cSockSSL.send("Content-Type: text/plain\r\n".encode())
cSockSSL.send("Some text".encode())
cSockSSL.send("--gg4g5gg".encode())

cSockSSL.send("Content-Type: text/plain\r\n".encode())
cSockSSL.send("Content-Disposition: attachment; filename = gg.txt\r\n".encode())
cSockSSL.send(txt_file)
cSockSSL.send("--gg4g5gg--".encode())
cSockSSL.send("\r\n.\r\n".encode())

In this case, I get an empty email with the header. If I delete first boundary I'll get this:

Some text--gg4g5ggContent-Type: text/plain Content-Disposition: attachment; filename = gg.txt Hey! I'm txt file!--gg4g5gg--

How to correctly split content-type?


Solution

  • Your email data is malformed, as you are missing several required line breaks.

    Per RFC 2822, you need to separate the email's headers from the email's body using \r\n\r\n instead of \r\n.

    And per RFC 2045, you need to separate MIME headers from MIME bodies using \r\n\r\n instead of \r\n. And, you are missing \r\n in front of each MIME boundary that follows a text body, and after each MIME boundary.

    So, essentially, you are sending an email that looks like this, all squished together:

    MIME-Version: 1.0
    Content-Type: multipart/mixed; boundary=gg4g5gg
    --gg4g5ggContent-Type: text/plain
    Some text--gg4g5ggContent-Type: text/plain
    Content-Disposition: attachment; filename = gg.txt
    <txt_file>--gg4g5gg--
    .
    

    But it needs to look more like this instead:

    MIME-Version: 1.0
    Content-Type: multipart/mixed; boundary=gg4g5gg
    
    --gg4g5gg
    Content-Type: text/plain
    
    Some text
    --gg4g5gg
    Content-Type: text/plain
    Content-Disposition: attachment; filename = gg.txt
    
    <txt_file>
    --gg4g5gg--
    .
    

    So, try this instead:

    cSockSSL.send("DATA\r\n".encode())
    # verify response is 354...
    
    cSockSSL.send("MIME-Version: 1.0\r\n".encode())
    cSockSSL.send("Content-Type: multipart/mixed; boundary=gg4g5gg\r\n".encode())
    cSockSSL.send("\r\n".encode()) # add a line break
    
    cSockSSL.send("--gg4g5gg\r\n".encode('ascii')) # add a line break
    cSockSSL.send("Content-Type: text/plain\r\n".encode())
    cSockSSL.send("\r\n".encode()) # add a line break
    cSockSSL.send("Some text".encode())
    
    cSockSSL.send("\r\n--gg4g5gg\r\n".encode()) # add line breaks
    cSockSSL.send("Content-Type: text/plain\r\n".encode())
    cSockSSL.send("Content-Disposition: attachment; filename=gg.txt\r\n".encode())
    cSockSSL.send("\r\n".encode()) # add a line break
    cSockSSL.send(txt_file)
    cSockSSL.send("\r\n--gg4g5gg--\r\n".encode()) # add line breaks
    
    cSockSSL.send(".\r\n".encode())
    

    Something else to be aware of - because the DATA command terminator is .\r\n, if "Some text" or txt_file contain any lines that begin with the . character, you MUST escape each leading . as .., per RFC 2821 Section 4.5.2.

    I suggest you study the RFCs mentioned above more carefully.