Dealing with MIME-encoded messages

string imap_fetchbody ( resource imap_stream, int msg_number, string part_number [, int options])

Still here? Alright, it is time to get into the real depths of MIME. If you think back, we added an attachment to a HTML and plain text message, but we didn't actually look at what was being sent. So, I have taken the script used for the previous example and edited it so that you can see what's going on inside:

<?php
    include('Mail.php');
    include('Mail\mime.php');

    $message = new Mail_mime();
    $text = file_get_contents("mail_text.txt");
    $html = file_get_contents("mail_html.html");

    $message->setTXTBody($text);
    $message->setHTMLBody($html);
    $message->addAttachment("button.png");
    print $message->get();
?>

I filled mail_text.txt up with some text, and put button.png in the same folder as the script, then ran the script. Here is what it returned on my computer:

--=_d070ce1d1c8b8ebbec5e54a50eb211ef
Content-Type: multipart/alternative;
boundary="=_ecd64c2382d54d7d8b2d1e3ecaafdcb1"

--=_ecd64c2382d54d7d8b2d1e3ecaafdcb1
Content-Type: text/plain; charset="ISO-8859-1"
Content-Transfer-Encoding: 7bit

This is a text message.
Yes it is.
Woohoo!
--=_ecd64c2382d54d7d8b2d1e3ecaafdcb1
Content-Type: text/html; charset="ISO-8859-1"
Content-Transfer-Encoding: quoted-printable

<html>
<body>
This is a <em><strong>HTML</strong></em> message!<br />
<img src=3D"button.png" />
</body>
</html>
--=_ecd64c2382d54d7d8b2d1e3ecaafdcb1--
--=_d070ce1d1c8b8ebbec5e54a50eb211ef
Content-Type: application/octet-stream
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="button.png"
iVBORw0KGgoAAAANSUhEUgAAASwAAAA8CAIAAAEkuSCbAAAACXBIWXMAAAsSAAALEgHS3X78AAAA
CXRFWHRDb21tZW50AACJKo0GAAAJL0lEQVR4nO2da2wc1RXH/zPr2dibJeFlUMCxDWSBD7y0YEUb
...[snip]...
x8kwlcC65mZpB2kKaQ1pEGmTKY9eZs2EHBwccxFsQg4Oj+P/r0YF809KwLwAAAAASUVORK5CYII=
--=_d070ce1d1c8b8ebbec5e54a50eb211ef--

As you can see, the plain text, HTML, and PNG files are all in there buried amongst MIME headers and boundary markings. As our previous script just read the first 100 characters of text from the body, it would read this:

--=_d070ce1d1c8b8ebbec5e54a50eb211ef
Content-Type: multipart/alternative;
boundary="=_ecd64c

Of course, what we would rather it do would be to scan through the plain text in the document and take the first hundred characters of that , but to get to that point we need to break down the MIME type into parts and only retrieve the plain text section.

Fortunately, PHP has a function just for that purpose: imap_fetchbody(). This takes an IMAP stream, a message number, and a part number as its parameters, and returns the text of that part. As a result, if all you want to do is grab the plain text portion of a message, you can just request part 1 and not have your script download anything else.

However, as always, there is a catch. Each MIME-encoded message can be broken down into parts based upon the its contents: each attachment can be considered a part. Most mail readers send the plain text as the first part, although this is not universally true - AOL, for example, often combines the HTML and plain text into one part of two sub-parts.

So, to get around this, the easiest thing to do is use imap_fetchbody() to request sub-part 1 of part 1, and, if it is empty, just request part 1. Use this next code to replace the existing call to imap_body() :

$body = imap_fetchbody($imap, $i, "1.1");

if ($body == "") {
    $body = imap_fetchbody($imap, $i, "1");
}

$body = trim(substr(quoted_printable_decode($body), 0, 100));

When you run the script now, it should be a great deal faster as there is lot less content to download. Furthermore, unless you get some particularly tricky emails through, that should handle everything. Your best bet is to go for this simple option until given reason to write something more complex.

Author's Note: You can pass FT_PEEK as the fourth parameter to imap_fetchbody() to have it not set the read flag to true for the selected message. Without this, any message retrieved using imap_fetchbody() is marked as read.

 

Want to learn PHP 7?

Hacking with PHP has been fully updated for PHP 7, and is now available as a downloadable PDF. Get over 1200 pages of hands-on PHP learning today!

If this was helpful, please take a moment to tell others about Hacking with PHP by tweeting about it!

Next chapter: Mail management >>

Previous chapter: Reading message contents

Jump to:

 

Home: Table of Contents

Copyright ©2015 Paul Hudson. Follow me: @twostraws.