Process a MIME message with Python

Posted on Tue, 12 June 2018 in development

Picture of a hard-disk drive

Today I needed to process a MIME message, to quickly verify the content. Here’s how I did it.

Problem

I needed to verify, if the MIME message stored in a file was intact, or if it had some issues. The message was supposed to have two parts:

  1. An inline XML message
  2. A base64-encoded PDF attachment

Solution

Python to the rescue! I already knew, how to process email messages, so this was pretty straight forward. The comments on the code below should explain, how my tiny app works.

I decided to use werkzeug’s secure_filename function to make sure, that the filenames are handled correctly when saving them to disk. Werkzeug is used e.g. by Flask, my Python web framework of choice. The same functionality could be archieved with the standard library only, should you not want to install any external packages.

For privacy reasons I cannot include the sample data used. Basically any MIME message should work.

'''
Read and extract MIME parts from a MIME message.

Run:
python process-mime-message.py file_name_to_read.txt

'''

import sys
import mimetypes
import email
from werkzeug.utils import secure_filename

''' Read message from a file. '''
msg = email.message_from_file(open(sys.argv[1]))

''' Split the input filename to parts, which will be needed later. '''
(name, extension) = sys.argv[1].split('.')

''' The MIME message consists of parts, let's iterate over them. '''
for i, part in enumerate(msg.walk(), 1):
    ''' Check the main type, and skip the one starting the whole message. '''
    if part.get_content_maintype() == 'multipart':
        continue

    ''' For each part, let's print the content type. '''
    print("New MIME-part:")
    ct = part.get_content_type()
    print(f"\tcontent-type: {ct}")

    ''' The filename bound to MIME part is either given, or not (in case of inline data). '''
    filename = part.get_filename()
    if filename is None:
        ext = mimetypes.guess_extension(part.get_content_type())
        filename = 'part-%03d%s' % (i, ext)
    filename = f"{name}_{filename}"
    print(f"\tfilename: {filename}")

    ''' ... And finally let's store the data to files on disk. '''
    output_filename = secure_filename(filename)
    with open(output_filename, 'wb') as fp:
        fp.write(part.get_payload(decode=True))
        print(f"\tSaving MIME-part to file {output_filename}")

It really is that easy. I got my answer, the data indeed was as it was supposed to be. I hope this little snippet will help you, if you are struggling with MIME messages.