EML file as attachment is not downloading using IMAP in Python? - python

I am using IMAP library in python to read an email inbox which is working file and i am downloading all my attachment successfully but when any .eml file is coming as attachment i got an error, pease help me how to download an eml file coming as attachment.

In-order to download an attachment such as .png from an email, the payload needs to be decoded using: part.get_payload(decode=True).decode(). However, from the documentation:
If the message is a multipart and the decode flag is True, then None is returned.
The error you are seeing is caused because a .eml file is a multipart message. The parts consist of message/rfc822 at the top level which holds all the email's details. Beneath will be single part messages such as text/html which holds the email's text etc...
To download this text into an .html or .txt file you need to .walk() through the parts of the .eml file - like you are doing on the original email to download the .eml attachment.
Here is a snippet of my code:
if msg.is_multipart():
for part in msg.walk():
# extract content type of email
content_type = part.get_content_type()
content_disposition = str(part.get("Content-Disposition"))
if "attachment" in content_disposition:
if content_type == "message/rfc822":
# walk through the .eml attachment parts:
for eml_part in part.walk():
# find the content type of each part:
content_type = eml_part.get_content_type()
if content_type == "text/html": # this type is not multipart
body = eml_part.get_payload(decode=True).decode() # get_payload() can be decoded
# can do what you need with the decoded body.
# in this case extract text and save to .txt or .html
else: .....

Maybe you need to use EML Parser?
You can find the manual for eml-parser here.

You can use it:
def _read(self):
"""Reads all emails and get attachments.
Returns:
Attachments.
"""
self.mail.list()
self.mail.select(self.select)
self.mail.uid('search', None, 'ALL')
self.uids = self.data[0].split()
self.content_length = len(self.uids)
self.attachments = []
for uid in self.uids:
self.result, self.email_data = self.mail.uid(
'fetch', uid, '(RFC822)')
self.raw_email = self.email_data[0][1]
self.raw_email_string = self.raw_email.decode('utf-8')
self.parsed_email = email.message_from_bytes(self.raw_email)
for part in self.parsed_email.walk():
if part.get_content_maintype() == 'multipart':
continue
if part.get_content_type() not in ['text/html', 'text/plain']:
self.attachments.append({
'name':
part.get_filename(),
'content_type':
part.get_content_type(),
'bytes':
part.get_payload(decode=True)
})
self.result = {'attachments': self.attachments}
return self.result

Try to use my high level imap lib:
https://github.com/ikvk/imap_tools
from imap_tools import MailBox, MailMessage
# get .eml files attached to email messages from INBOX
with MailBox('imap.mail.com').login('test#mail.com', 'password', 'INBOX') as mailbox:
for message in mailbox.fetch():
for att in message.attachments:
if '.eml' in att.filename:
print(att.filename, len(att.payload))
Also you can parse .eml in place - see lib examples:
https://github.com/ikvk/imap_tools/blob/master/examples/parse_eml_attachments.py

Related

How to get name of all email attachments of a particular mail using imaplib, python?

I am trying to fetch all the attachments of email messages and make a list of those attachments for that particular mail and save that list in a JSON file.
I have been instructed to use imaplib only.
This is the function that I am using to extract the mails data but the part.getfilename() is only returning one attachment even if I have sent multiple attachments.
The output I want is the list of attachments like [attach1.xlss, attach2.xml, attch.csv].
Again, I can only use imaplib library.
I also don't want to have to download any attachment, so please don't share that code. I tried several websites but couldn't find anything that I could use.
def get_body_and_attachments(msg):
email_body = None
filename = None
html_part = None
# if the email message is multipart
if msg.is_multipart():
# iterate over email parts
for part in msg.walk():
# extract content type of email
content_type = part.get_content_type()
content_disposition = str(part.get("Content-Disposition"))
try:
# get the email body
body = part.get_payload(decode=True).decode()
except:
pass
if content_type == "text/plain" and "attachment" not in content_disposition:
# print text/plain emails and skip attachments
email_body = body
elif "attachment" in content_disposition:
# download attachment
print(part.get_filename(), "helloooo")
filename = part.get_filename()
filename = filename
else:
# extract content type of email
content_type = msg.get_content_type()
# get the email body
body = msg.get_payload(decode=True).decode()
if content_type == "text/plain":
email_body = body
if content_type == "text/html":
html_part = body
return email_body, filename, html_part
It was easy; I just had to do this.
import re
# getting filenames
filenames = mailbox.uid('fetch', num, '(BODYSTRUCTURE)')[1][0]
filenames = re.findall('\("name".*?\)', str(filenames))
filenames = [filenames[i].split('" "')[1][:-2] for i in range(len(filenames))]
Explanation: mailbox.uid will fetch the message (or mail) of a particular uid (num) and will return a byte string with all the data relating to that message.
Now I use re.findall to find all the attachment names and then I clean that return value and save it as a list.

How to send a complete email using smtplib python

I am trying to send an email using Python smtplib.
My objective is to include the below info in email
Attachment file #works fine
Paste the contents of a table in message body #works fine
Write a few lines about the table (as text) in message body # not works. instead stores as an attachment
So, I tried the below code
message = MIMEMultipart()
message['Subject'] = 'For your review - files'
message['From'] = 'user2#org.com'
message['To'] = 'user1#org.com'
# code to paste table contents in outlook message window - works fine
body_content = output # this has the pretty table - html table
message.attach(MIMEText(body_content, "html"))
# code to paste the written text in outlook message window - not works. instead of writing the text in outlook body,it stores as an attachment
written_text = """\
Hi,
How are you?"""
message.attach(MIMEText(written_text, "plain"))
# code to attach an csv file to a outlook email - works fine
with open(filename, "rb") as attachment:
part = MIMEBase("application", "octet-stream")
part.set_payload(attachment.read())
encoders.encode_base64(part)
part.add_header(
"Content-Disposition",
f"attachment; filename= {filename}",
)
message.attach(part)
msg_body = message.as_string()
server = SMTP('internal.org.com', 2089)
server.sendmail(message['From'], message['To'], msg_body)
print("mail sent successfully")
server.quit()
The problem in my code is that it creates a text file (containing the message "Hi, How are you") and sends as an attachment?
But I want "Hi, How are you" as a text message in the main Outlook message window.
The immediate problem is that many email clients assume that text body parts after the first are attachments. You can experiment with adding an explicit Content-Disposition: inline to the part(s) you want rendered as part of the main message, but is there a reason these need to be separate body parts in the first place? Combining the text fragments into a single body part would perhaps make more sense here.
More fundamentally, your email code was written for an older Python version. The email module in the standard library was overhauled in Python 3.6 to be more logical, versatile, and succinct; new code should target the (no longer very) new EmailMessage API. Probably throw away this code and start over with modern code from the Python email examples documentation.
from email.message import EmailMessage
message = EmailMessage()
message['Subject'] = 'For your review - files'
message['From'] = 'user2#org.com'
message['To'] = 'user1#org.com'
message.set_content(output, subtype="html")
written_text = """\
Hi,
How are you?"""
message.add_attachment(
written_text, subtype="plain",
disposition="inline")
with open(filename, "rb") as attachment:
message.add_attachment(
attachment.read(),
maintype="application", subtype="octet-stream",
filename=filename)
with SMTP('internal.org.com', 2089) as server:
server.send_message(message)
print("mail sent successfully")
server.quit()
If the final attachment is really a CSV file, specifying it as application/octet-stream is a bit misleading; the proper MIME type would be text/csv (see also What MIME type should I use for CSV?)

How to download the complete html of emails with python using imap

Goal:
Getting the Html of an email looking exactly as in the mailbox saved
as.html
Explanation: I am using Python and IMAP to download email and get the HTML content with .get_content_payload(text/HTML) but when I save it and open it, it doesn't look like the mail content as shown in the mailbox.
I tried writing an HTML file with mail_html but, it doesn't work out quite well as the CSS is missing the whole HTML email looks terrible. I want it to download along with other page assets. And here I need your help.
mail = imaplib.IMAP4_SSL(SERVER)
mail.login(EMAIL, PASSWORD)
mail.select(f'{TARGET}')
status, data = mail.search(None, 'ALL')
mail_ids = []
for block in data:
mail_ids += block.split()
for mail_id in mail_ids:
status, data = mail.fetch(mail_id, '(RFC822)')
print("BODY ENDS")
for response_part in data:
if isinstance(response_part, tuple):
message = email.message_from_bytes(response_part[1])
mail_from = message['from']
mail_subject = message['subject']
# if it is multi-part message separate first
if message.is_multipart():
mail_content = ''
mail_html = ' '
for part in message.get_payload():
# Get all parts of the message
if part.get_content_type() == 'text/plain':
mail_content += part.get_payload()
if message.get_content_type() == "text/html":
mail_html + part.get_payload()
Try my lib: https://github.com/ikvk/imap_tools
from imap_tools import MailBox
# get list of email bodies from INBOX folder
with MailBox('imap.mail.com').login('test#mail.com', 'pwd', 'INBOX') as mailbox:
bodies = [msg.html or msg.text for msg in mailbox.fetch()]

How to get email attachment size in python imap

How to get email attachment size in python imap
# DOWNLOAD ATTACHMENTS
for part in msg.walk():
# this part comes from the snipped I don't understand yet...
if part.get_content_maintype() == 'multipart':
continue
if part.get('Content-Disposition') is None:
continue
fileName = part.get_filename()
return HttpResponse(fileSize)
if bool(fileName):
filePath = os.path.join('C:/Users/bali/attachments/', fileName)
if not os.path.isfile(filePath) :
fp = open(filePath, 'wb')
fp.write(part.get_payload(decode=True))
fp.close()
Is there any function to get size of the attachment just like "get_filename()" to get the name of the file.
Information about the attachment size is not available in the header of the MIME message (you can check this by sending an attachment to yourself and seeing the original email to see if there is any information about attachment size) but you can get the size of the attachment without "creating a file", which I consider is equivalent to "downloading the attachment".
You can do so by getting the payload of the part with attachment and then returning the length of the payload:
payload = part.get_payload(decode=True)
file_size = len(payload) # in bytes
Also, instead of checking for part.get_filename(), as you did in your sample code, I recommend using is_attachment() check on the message part OR instead of walk() using iter_attachments() to get all the message parts with attachments. You can see how the attachments are handled in this python emails document examples.
Well, you have the filename, so you can use os.path.getsize
import os
os.path.getsize(part.get_filename())
You may try external lib: https://github.com/ikvk/imap_tools
from imap_tools import MailBox, A
with MailBox('imap.mail.com').login('test#mail.com', 'pwd', 'INBOX') as mailbox:
for msg in mailbox.fetch(A(all=True)):
print(msg.subject, msg.date)
for att in msg.attachments:
print(att.filename, att.size)

Sending a PDF attachment with a HTML+plain_text email in Python

I'm trying to send a PDF attachment with an email body that summarises the contents of the PDF file. The email body is in both HTML and plaintext.
I'm using the following code to build the email email message object:
#Part A
logging.debug(" Building standard email with HTML and Plain Text")
msg = MIMEMultipart("alternative")
msg.attach(MIMEText(email_obj.attachments["plain_text"], "plain", _charset="utf-8"))
msg.attach(MIMEText(email_obj.attachments["html_text"], "html", _charset="utf-8"))
#Part B
logging.debug(" Adding PDF report")
pdf_part = MIMEApplication(base64.decodestring(email_obj.attachments["pdf_report"]), "pdf")
pdf_part.add_header('Content-Disposition', 'attachment', filename="pdf_report.pdf")
logging.debug(" Attaching PDF report")
msg.attach(pdf_part)
My problem is that my email body disappears if I attach the PDF. If I comment out the code that attaches the PDF (Part B), the email body appears.
Unless I'm mistaken, it looks as though my PDF attachment is overwriting the email body.
Such a message should have a more complex structure. The message itself contains two "top-level" MIME parts and has content-type "multipart/mixed". The first of these MIME part has type of "multipart/alternative" with two subparts, one for plain text and another for HTML. The second of the main parts is PDF attachment
pdfAttachment = MIMEApplication(pdf, _subtype = "pdf")
pdfAttachment.add_header('content-disposition', 'attachment', filename = ('utf-8', '', 'payment.pdf'))
text = MIMEMultipart('alternative')
text.attach(MIMEText("Some plain text", "plain", _charset="utf-8"))
text.attach(MIMEText("<html><head>Some HTML text</head><body><h1>Some HTML Text</h1> Another line of text</body></html>", "html", _charset="utf-8"))
message = MIMEMultipart('mixed')
message.attach(text)
message.attach(pdfAttachment)
message['Subject'] = 'Test multipart message'
f = open("message.msg", "wb")
f.write(bytes(message.as_string(), 'utf-8'))
f.close()
You may try to open a "source view" of a message in your favorite mail program (mail user agent) and see yourself (Ctrl-U in Thunderbird)

Categories

Resources