0

I'm trying to open an email with an attachment using python code and then send the unzipped file in a post request to a php script.

The attachment is always a zip file with a single csv file inside it.

import requests
import imaplib
import email
import os
from zipfile import ZipFile

# Open zip file and return it with a different name.
def extract_zip(input_zip):
    input_zip=ZipFile(input_zip)
    return {'file': ('report.csv', input_zip.open(input_zip.namelist()[0], 'r'))}

# Connect to gmail server.
mail=imaplib.IMAP4_SSL('imap.gmail.com')
mail.login("z@z","z")
mail.list()
mail.select("inbox")

typ, msgs = mail.search(None, '(SUBJECT "Report")')
msgs = msgs[0].split()
data_file = '';

for emailid in msgs:
    resp, data = mail.fetch(emailid, "(RFC822)")
    email_body = data[0][1] 
    m = email.message_from_string(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:
            data_file = extract_zip(filename) # Error Here

# Send the file to a data.php on a localhost,
# Text output should be file content.
http_post_request = requests.post("http://localhost/data.php", files=data_file)
if(http_post_request.status_code != 200):
    print 'Error sending POST request'
print http_post_request.text

The problem is that the code expects the zip file name to be a string of the location / name of the file. Which I have no idea how to do when I want to open an attachment I got in an email.

If I pass the filename it will simply give me a file-not-found error.

How can I get the path to the attachment without saving it to disk?

Potato
  • 24
  • 5
  • does this help? https://stackoverflow.com/questions/10908877/extracting-a-zipfile-to-memory – Nullman May 29 '17 at 10:15
  • the answer by Robᵩ [here](https://stackoverflow.com/questions/23569659/extract-zip-to-memory-parse-contents) seems to be what you are looking for – Nullman May 29 '17 at 10:18
  • @Nullman The part of extracting into memory works fine, I just can't find a way to give it a path to the attachment which only returns data and filename (not path, which would probably require me to write to disk). – Potato May 29 '17 at 10:20
  • ohh, i think you are looking for the `get_payload` method of email https://stackoverflow.com/questions/4067937/getting-mail-attachment-to-python-file-object – Nullman May 29 '17 at 10:22
  • @Nullman Thank you very much, this method combined with io.BytesIO provided a solution. – Potato May 29 '17 at 11:01

1 Answers1

0

The solution I came up with thanks to @Nullman :

I took the payload of the attachment, decoded it and used io.BytesIO to create a file stream (file like object).

import requests
import imaplib
import email
import os
import io
from zipfile import ZipFile

# Open zip file and return it with a different name.
def extract_zip(input_zip):
    input_zip=ZipFile(input_zip)
    return {'file': ('report.csv', input_zip.open(input_zip.namelist()[0], 'r'))}

# Connect to gmail server.
mail=imaplib.IMAP4_SSL('imap.gmail.com')
mail.login("z@z","z")
mail.list()
mail.select("inbox")

typ, msgs = mail.search(None, '(SUBJECT "Report")')
msgs = msgs[0].split()
data_file = '';

for emailid in msgs:
    resp, data = mail.fetch(emailid, "(RFC822)")
    email_body = data[0][1] 
    m = email.message_from_string(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:
            # Get the payload bytes into a file like object:
            file_bytes = io.BytesIO(part.get_payload(decode=True))
            data_file = extract_zip(file_bytes)

# Send the file to a data.php on a localhost,
# Text output should be file content.
http_post_request = requests.post("http://localhost/data.php", files=data_file)
if(http_post_request.status_code != 200):
    print 'Error sending POST request'
print http_post_request.text
Potato
  • 24
  • 5