pythonemaileml

How to add pdf attachment in an existing eml file using Python?


Currently I am working on eml files and I am newly working on those kind of files:

I had to do change the sender name and send the same eml file to those sender but I want to add .eml file to my existing

I have got successfully changed the changed the sender by using email.parser library using replace header command.

For example:

from email.parser import Parser
f = open('C:\\Users\\Downloads\\Message.eml', 'r+')
header = Parser().parse(f)

headers.replace_header(headername, headervalue)
headers.replace_header('to', 'name@gmail.com') 

But now I am stuck how to add pdf attachment in email body of that same .eml file. Below is an example of an .eml file. In this I want to add an attachment.

Can you guys please help?

.eml file template:

Delivered-To: ***@gmail.com
Received: by 2002:ac9:1e03:0:0:0:0:0 with SMTP id r3csp1380999oci;
        Thu, 6 May 2021 03:24:14 -0700 (PDT)
X-Google-Smtp-Source: ABdhPJxznI2eQK4UcAUk1vJbeKdYMovRwYMwxz4trgXWy7O+V1jklccpi92jvFWplswmqnBJfdpV
X-Received: by 2002:a17:2:94:b9:ec:7fd5:193e with SMTP id w4-20020a1709029a84b02900ec7fd5193emr3638953plp.62.1620296654743;
        Thu, 06 May 2021 03:24:14 -0700 (PDT)
ARC-Seal: i=1; a=rsa-sha256; t=1620296654; cv=none;
        d=google.com; s=arc-20160816;
        b=EH7EGZH8A3o9/LvvqIgO3KaZPU82Jn0iX0/kGV5W/tawujBF7y3qV3Er4lpFtX
         rm1jiy+cH3CPEHEiAyyd3XSuBZFA+AoE8xpoZxXaTxmqB6vBQXVWigVUUTKcsl71CSVs
         xLG7NHWsFABWEdemJY/cnibY85tpk1NpVISzDihAd4IShMKOGlYqoOlyWf06pdyIc2y6
         DZVYrlo/oWsnD2VT5nYiVqMeOwjUKIVg9ACyZIIRpmMQT/2/lutcsrLPMBBJbLK1vpgU
         jpZHu3s++EFPjmuTijNbyvv/5d5RrcsOwvLpWqk
         8U1A==
ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816;
        h=from:subject:sender:message-id:to:mime-version:date:dkim-signature;
        bh=oz3nVaiXvPhENQytolVf3ACAgfI2p8aslAq1BN/w55M=;
        b=fUeNvuOk3JjseXNpa+wFWtdmRjgG/Le5G62cV0ZMbelccGKi1H7GWx
         Exred4q9phvSSGV7ZuE+U5MXpwL1tXmPYZhHO+fj5uPEt6dY2x
         Yqg2/1IxDhcd/3NLH8CB19AolyRgAA8Qn+ThyBgpHs8mCVQ0f5XzxZvP/rKf
         WXxyQwA/1CcOPEcDlaOPAZNngacjvxeecjWWLrHUK1eH
         bETcDxabCPKXagRnP4xDXwTSqzj4Gtjsbc7 +v
         WGfw==
ARC-Authentication-Results: i=1; mx.google.com;
       dkim=pass header.i=@mail.com header.s=mail header.b=LRDuMb9q;
       spf=pass (google.com: domain of ***@mail.com designates ****.137.**.*** as permitted sender) smtp.mailfrom=***@mail.com;
       dmarc=pass (p=QUARANTINE sp=REJECT dis=NONE) header.from=mail.com
Return-Path: <***@mail.com>
Received: from mail.com (f4mail-235-203. mail.com. [***.137.**.***])
        by mx.google.com with ESMTPS id l10si2328115pgb.331.2021.05.06.03.24.13
        for <xxxxx@gmail.com>
        (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128);
        Thu, 06 May 2021 03:24:14 -0700 (PDT)
Received-SPF: pass (google.com: domain of ***@mail.com designates ***.137.**.*** as permitted sender) client-ip=***.137.**.***;
Authentication-Results: mx.google.com;
       dkim=pass header.i=@mail.com header.s=mail header.b=LRDuMb9q;
       spf=pass (google.com: domain of ***@mail.com designates ***.137.**.*** as permitted sender) smtp.mailfrom=***@mail.com;
       dmarc=pass (p=QUARANTINE sp=REJECT dis=NONE) header.from=mail.com
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=mail.com;
    s=mail; t=1620296652;
    bh=oz3nVaiXvPhENQytolVf3ACAgfI2p8aslAq1BN/w55M=;
    h=MIME-Version:From:Date:Message-ID:Subject:To:Content-Type;
    b=LRDuMb9qOWYq/u397M6T9zLkk1kInTolxD538xl5crHBsb3PL8eR5GiE0Deg7fTNe
     T8+whLVLTServKQLpxrEE3ob/6c5gr11SFYP8dIyzYU+qhbtxp6OJcAnBuxkJSRgRD
     JFQ/6oaHO49Jhz/2qkQ82USjrCi1fiAZe/mBKUGY=
Received: (qmail 20965 invoked by uid 510); 6 May 2021 10:24:12 -0000
x-m-msg: asd54ad564ad7aa6sd5as6d5; a6da7d6asas6dasd77; 5dad65ad5sd;
X-OUT-VDRT-SpamState: 0\LEGIT
X-OUT-VDRT-SpamScore: 0
X-OUT-VDRT-SpamCause: gggruggvucftvghtrhhoucdtuddrgeduledrvdegtddgvdejucetufdoteggodetrfdotffvucfrrhhofhhilhgvmecufdftgfffkffhhfdpqfgfvfdfnecuuegrihhlohhuthemuceftddtnecunecujfgurhepffggvffkshfuhfgtsegrtderredttdejnecuhfhrohhmpedfrfhrihihrghnkhgrucffvghsrghifdcuoehpihihrgguvghsrghiudduuddusehrvgguihhffhhmrghilhdrtghomheqnecuggftrfgrthhtvghrnheptdefkeehkeduhfeljeelleehgefgffeutdeljedtiedtgeeigfdtjeettedvkedtnecukfhppedurddukeeirdduvdegrdduheeinecuvehluhhsthgvrhfuihiivgeptdenucfrrghrrghmpehmohguvgepshhmthhpohhuth
X-Remote-IP: ***.137.**.***
X-REDF-OSEN: ***@mail.com
Date: 6 May 2021 10:24:12 -0000
MIME-Version: 1.0
To: "***" <***@gmail.com>
Received: from unknown ***.137.**.*** by mail.com via HTTP; 06 May 2021 10:24:12 -0000
X-Senderscore: D=0&S=0
Message-ID: <1620296512.S.1386.3658.f4mail-***-13*.mail.com.1620296652.20941@webmail.mail.com>
Sender: ***@mail.com
Subject: =?utf-8?B?UmVxdWVzdGVkIGRvY3VtZW50cw==?=
From: "Fisrtname Lastname" <***@mail.com>
Content-Type: multipart/alternative;
    boundary="=_f619e79a5c2c1319e417d1bc96f343f8"

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



Hey Name,

I hope you are fine and staying safe.
&nbsp;Please find the attached document of some details for the whole process for this program.&nbsp; Once you go through it if you find interest to know then let&#39;s have a discussion.

Regards,
Name LastName
&nbsp;
--=_f619e79a5c2c1319e417d1bc96f343f8
Content-Transfer-Encoding: quoted-printable
Content-Type: text/html; charset="UTF-8"

<br /><br />Hey Name,<br /><br />I hope you are fine and staying safe.<br /=
>&nbsp;<div>Please find the attached document of some details for the whole=
 process for this program.&nbsp; Once you go through it if you find interes=
t to know then let&#39;s have a discussion.<br /><br />Regards,<br />Name L=
astName<br />&nbsp;</div><br>
--=_f619e79a5c2c1319e417d1bc96f343f8--

Solution

  • You are using the legacy email.message.Message API which does not easily let you add attachments to a message you parsed. But it's easy and overall a good idea to switch to the modern Python 3.6+ API which makes this a breeze.

    Somewhat obscurely, the way to make the email.parser module produce a modern EmailMessage object instead of a legacy Message one is to pass in a policy parameter.

    As an aside, you should read the file as bytes and use the BytesParser to avoid having weird encoding errors if the email message is not pure UTF-8 on disk. Many real-world messages contain Latin-1 or various Asian character sets; but the proper solution is to not even try to guess - just read it in as a binary blob and let the email parser figure it out.

    from email.parser import BytesParser
    from email.policy import default
    
    headername = 'sender'
    headervalue = 'you need to show us your variables <failure@example.net>'
    
    with open(r'C:\Users\Sun\Download\Message.eml', 'rb') as mess:
        message = BytesParser(policy=default).parse(mess)
    
    message.replace_header(headername, headervalue)
    message.replace_header('to', 'name@gmail.com')
    
    message.add_attachment(b'\x00\x00\x00', 'attachment', 'pdf', filename='poop.pdf')
    

    The add_attachment method is also somewhat poorly documented; its arguments are the attachment body data, the main type, and the subtype (though there are additional parameters you can add, like a disposition, to give the recipient a file name for it). See e.g. Addng attachment to an EmailMessage raises TypeError: set_text_content() got an unexpected keyword argument 'maintype'

    Normally, you'll probably want to read the PDF from a file, too:

    from pathlib import Path
    
    pdf_file = Path(r'C:\windows\horrible\atrocious\nasty\file.pdf')
    with pdf_file.open('rb') as pdf:
        message.add_attachment(
            pdf.read(), 'attachment', 'pdf', filename=pdf_file.name)
    

    Notice how again we require binary input, and how the first argument to message.add_attachment is the bytes object containing the payload.

    The pathlib diversion is not strictly necessary here, but shows how to type the file name only once, and pass it to the filename keyword parameter of add_attachment to use the same file name there, without the directory part.

    If you want to write the modified message to the same file, just close the file handle (or better, use with open() ...: when initially reading it, so you don't have to remember to explicitly close(), like I do above) and open for reading, then write out the new content. Again, remember to use binary mode, and write out bytes.

    with open(filename, 'wb') as destination:
        destination.write(message.as_bytes())
    

    Vaguely adapted demo: https://ideone.com/NBMOs1

    As a aside, your SMTP server doesn't care what's in the To: header; if you sendmail me@example.org <Message.eml it will be delivered to me, not to whoever is in the To: header. (sendmail is not commonly installed on Windows, but the same principle holds.) Maybe you don't actually want or need to replace the To: header if that's your concern.