Python Forum
download base64 email attachment - Printable Version

+- Python Forum (https://python-forum.io)
+-- Forum: Python Coding (https://python-forum.io/forum-7.html)
+--- Forum: General Coding Help (https://python-forum.io/forum-8.html)
+--- Thread: download base64 email attachment (/thread-24624.html)



download base64 email attachment - cferguson - Feb-23-2020

Hey,

New to coding. New to python. I have been using home assistant to do some automation stuff at my place. But my lorex NVR is not natively supported. It does send a email notification on motion detection. I have been able to decode the email body with a python script ran inside the home assistant api (gmail is supported in home assistant.) But now i would like to download the attached email picture. I have been able to piece together a bit of code that can download attachments from non-base64 emails. But I haven't found anything that works for the encoded ones.

Here is what I have so far.

import imaplib
import email
import os

svdir = 'c:/save/temp'

mail = imaplib.IMAP4_SSL('imap.gmail.com', 993)
mail.login("hidden","hidden")
mail.select("Inbox")

typ, data = mail.search(None, 'SEEN')
for num in data[0].split():
   mail.store(num, '+FLAGS', '\\deleted')

mail.expunge()

typ, msgs = mail.search(None, 'ALL')
msgs = msgs[0].split()

for emailid in msgs:
    resp, data = mail.fetch(emailid, "(RFC822)")
    email_body = data[0][1]
    m = email.message_from_bytes(email_body)

    if m.get_content_maintype() != 'multipart': continue

    for part in m.walk():
        if part.get_content_maintype() == 'multipart': continue
        if part.get('Content-Disposition') is None: continue

        filename = part.get_filename()
        if filename is not None:
            sv_path = os.path.join(svdir, filename)
            if not os.path.isfile(sv_path):
                print(sv_path)
                fp = open(sv_path, 'wb')
                fp.write(part.get_payload(decode=True))
                fp.close()

mail.close()
mail.logout()
It also only leaves one email in the account by design. Can anyone help me so I can get this to download any and all email attachments regardless of the encoding?

the svdir in the above code is what I am using in spyder for testing. But the version I run in home assistant works the same the correctly defined dir.

Thanks.

I have been able to run this code and identify the attachment.

import imaplib
mail = imaplib.IMAP4_SSL('imap.gmail.com', 993)
mail.login("hidden","hidden")
mail.select('inbox')
result, data = mail.uid('search', None, 'ALL')
uids=data[0].split()
result, data = mail.uid('fetch', uids[-1], 'BODYSTRUCTURE')
print(data)
it returns
Output:
[b'1 (UID 5424 BODYSTRUCTURE (("TEXT" "PLAIN" ("CHARSET" "utf-8") NIL NIL "BASE64" 128 3 NIL NIL NIL)("IMAGE" "JPG" ("NAME" "Motion_CAM01_20200223_094803.jpg") NIL NIL "BASE64" 198074 NIL NIL NIL) "MIXED" ("BOUNDARY" "====my_split_tag====") NIL NIL))']



RE: download base64 email attachment - cferguson - Feb-23-2020

Woo I got a result! not I just gotta figure out how to decode it and turn it into a proper picture!

import imaplib
import email
import base64
import os

svdir = 'c:/save/temp'

mail = imaplib.IMAP4_SSL('imap.gmail.com', 993)
mail.login("","")
mail.select("Inbox")

#typ, data = mail.search(None, 'SEEN')
#for num in data[0].split():
#   mail.store(num, '+FLAGS', '\\deleted')

#mail.expunge()

typ, msgs = mail.search(None, 'ALL')
msgs = msgs[0].split()

for emailid in msgs:
    resp, data = mail.fetch(emailid, "(RFC822)")
    email_body = data[0][1]
    m = email.message_from_bytes(email_body)
    print((data)[0][1]) #added this and the output was a raw base64 string I 
                        #was able to repair and decode on a website. Returned 
                        #the actual image! Now I just need to decode it and 
                        #save it as a file I guess.
    if m.get_content_maintype() != 'multipart': continue

    for part in m.walk():
        if part.get_content_maintype() == 'multipart': continue
        if part.get('Content-Disposition') is None: continue

        filename = part.get_filename()
        if filename is not None:
            sv_path = os.path.join(svdir, filename)
            if not os.path.isfile(sv_path):
                print(sv_path)
                fp = open(sv_path, 'wb')
                fp.write(part.get_payload(decode=True))
                fp.close()

mail.close()
mail.logout()



RE: download base64 email attachment - cferguson - Feb-24-2020

still struggling with this.

    for part in m.walk():
        if part.get_content_maintype() == 'multipart': continue
        if part.get('Content-Transfer-Encoding') is None: continue

        filename = part.get_filename()
        filename = str(filename.strip('=?utf-8?b?)')
        filename = base64.b64decode(filename).decode('utf-8')
I tried this but I can't seem to figure out how to decode the output

(Feb-24-2020, 05:40 PM)cferguson Wrote: still struggling with this.

    for part in m.walk():
        if part.get_content_maintype() == 'multipart': continue
        if part.get('Content-Transfer-Encoding') is None: continue

        filename = part.get_filename()
        filename = str(filename.strip('=?utf-8?b?'))
        filename = base64.b64decode(filename).decode('utf-8')
I tried this but I can't seem to figure out how to decode the output

*correction


RE: download base64 email attachment - cferguson - Feb-25-2020

I DID IT. WOO.

import imaplib
import email
import base64
import os

svdir = 'c:/save/temp'

def sender_decode(sender):
    parsed_string = sender.split("?")
    decoded = base64.b64decode(parsed_string[3]).decode(parsed_string[1], "ignore")
    return decoded

mail = imaplib.IMAP4_SSL('imap.gmail.com', 993)
mail.login("HIDDEN","HIDDEN")
mail.select("Inbox")

typ, data = mail.search(None, 'SEEN')
for num in data[0].split():
   mail.store(num, '+FLAGS', '\\deleted')

mail.expunge()

typ, msgs = mail.search(None, 'UNSEEN')
msgs = msgs[0].split()

for emailid in msgs:
    resp, data = mail.fetch(emailid, "(RFC822)")
    email_body = data[0][1]
    m = email.message_from_bytes(email_body)
    if m.get_content_maintype() != 'multipart': continue

    for part in m.walk():
        if part.get_content_maintype() == 'multipart': continue
        if part.get('Content-Transfer-Encoding') is None: continue

        filename: str = part.get_filename()
        if filename is None: continue
        try:
            filename = sender_decode(filename)
        except IndexError:
            filename = part.get_filename()
        if filename is not None:
            sv_path = os.path.join(svdir, filename)
            if not os.path.isfile(sv_path):
                fp = open(sv_path, 'wb')
                fp.write(part.get_payload(decode=True))
                fp.close()

mail.close()
mail.logout()