Home arrow PHP arrow Page 4 - Building A PHP-Based Mail Client (part 2)

Structure And Syntax - PHP

Now that you've got a basic mail reader up and running, it's timeto learn a little more about attachments. This second segment analyzesMIME-encoded attachments, demonstrating how to decode and download them,and then integrates attachment handling features into the primitive mailclient previously demonstrated.

  1. Building A PHP-Based Mail Client (part 2)
  2. A Picture Is Worth A Thousand Words
  3. The Way Things Work
  4. Structure And Syntax
  5. Room With A View
  6. Getting Down
  7. Miles To Go
By: icarus, (c) Melonfire
Rating: starstarstarstarstar / 12
January 11, 2002

print this article


Using this process flow as reference, let's now begin implementing the code for the mail client.

A quick look at the PHP manual reveals that PHP provides a number of useful functions to assist in this process:

1. imap_fetchstructure() - read and return information on message structure

2. imap_header() - return an object containing header elements

3. imap_body() - retrieve the complete message body

4. imap_fetchbody() - retrieve a specific section of the body

Of these, you've already seen the imap_fetchstructure() function in action - using a mailbox handle and message number as arguments, this function reads the message body and returns an object containing information on the message size, message body and MIME parts within it.

This object exposes a "parts" property, which is itself an array; the elements of this array are objects representing the various sections of the message. Therefore, it's possible to identify whether or not a message contains multiple parts, and obtain information on those parts, simply by iterating through this array.

Each of the objects in the "parts" array provides information on the corresponding message section - the encoding, the size, the type and subtype, the filename and so on. A complete list of the properties exposed by each of these objects is available in the PHP manual at http://www.php.net/manual/en/function.imap-fetchstructure.php - you should look at it before proceeding further.

Using this information, it's possible to write a couple of simple functions that build on imap_fetchstructure() to deliver customized information about message sections:

<? // define some constants // message types $type = array("text", "multipart", "message", "application", "audio", "image", "video", "other"); // message encodings $encoding = array("7bit", "8bit", "binary", "base64", "quoted-printable", "other"); // parse message body function parse($structure) { global $type; global $encoding; // create an array to hold message sections $ret = array(); // split structure into parts $parts = $structure->parts; for($x=0; $x<sizeof($parts); $x++) { $ret[$x]["pid"] = ($x+1); $this = $parts[$x]; // default to text if ($this->type == "") { $this->type = 0; } $ret[$x]["type"] = $type[$this->type] . "/" . strtolower($this->subtype); // default to 7bit if ($this->encoding == "") { $this->encoding = 0; } $ret[$x]["encoding"] = $encoding[$this->encoding]; $ret[$x]["size"] = strtolower($this->bytes); $ret[$x]["disposition"] = strtolower($this->disposition); if (strtolower($this->disposition) == "attachment") { $params = $this->dparameters; foreach ($params as $p) { if($p->attribute == "FILENAME") { $ret[$x]["name"] = $p->value; break; } } } } return $ret; } ?>
The parse() function accepts an object, as returned by imap_fetchstructure(), and parses it to produce an array holding some very specific information on each message part.

First, parse() creates an empty array named $ret, which will ultimately contain as many elements as there are message parts.

<? // create an array to hold message sections $ret = array(); ?>
Every element of $ret will itself be an associative array, with keys representing the part number, part type, encoding, disposition, size and filename.

Next, parse() creates an array named $parts, to hold the object array returned by the "parts" property of the imap_fetchstructure() object.

<? // split structure into parts $parts = $structure->parts; ?>
parse() then iterates through $parts, adding each element to $ret and creating keys to represent the characteristics of each part found.

<? for($x=0; $x<sizeof($parts); $x++) { $ret[$x]["pid"] = ($x+1); $ret[$x]["type"] = $type[$this->type] . "/" . strtolower($this->subtype); $ret[$x]["encoding"] = $encoding[$this->encoding]; $ret[$x]["size"] = strtolower($this->bytes); $ret[$x]["disposition"] = strtolower($this->disposition); // some parts snipped for brevity } ?>
The IMAP specification (available at http://www.faqs.org/rfcs/rfc2060.html) defines a number, or part ID, for every part of a multipart message, starting from 1 (which is usually the message text itself); my array defines this part ID via the "pid" key, and I'm recording it at this stage itself because I'm sure to need it when handling messages containing more than one attachment.

The type and encoding of each part are stored as integers by imap_fetchstructure(); these correspond to elements of the $type and $encoding arrays respectively. If you look at the complete function definition above, you'll see that these arrays are defined outside the function, and imported into it with the "global" keyword.

Here's an example of the array returned by parse():

Array ( [0] => Array ( [pid] => 1 [type] => text/plain [encoding] => 7bit [size] => 41 [disposition] => ) [1] => Array ( [pid] => 2 [type] => application/zip [encoding] => base64 [size] => 50634 [disposition] => attachment [name] => photos1.zip ) [2] => Array ( [pid] => 3 [type] => application/zip [encoding] => base64 [size] => 44882 [disposition] => attachment [name] => photos2.zip ) )
As you can see, this is a fairly clear representation of the various sections that make up a MIME message.{mospagebreak title=Getting Attached} I've also written another function to extract the attachments only from the array returned by parse() - take a look at the get_attachments() function:

<? // iterate through object returned by parse() // create a new array holding information only on message attachments function get_attachments($arr) { for($x=0; $x<sizeof($arr); $x++) { if($arr[$x]["disposition"] == "attachment") { $ret[] = $arr[$x]; } } return $ret; } ?>
Let's now backtrack a little and add this code to the message listing, "list.php", where it will be used to identify which messages have attachments (so that an attachment icon can be displayed next to those messages).

Here's the revised version of that script:

<?php // list.php - display message list // includes include("functions.php"); // session check session_start(); if (!session_is_registered("SESSION")) { header("Location: error.php?ec=2"); exit; } // open mailbox $inbox = @imap_open ("{". $SESSION_MAIL_HOST . "/pop3:110}", $SESSION_USER_NAME, $SESSION_USER_PASS) or header("Location: error.php?ec=3"); // get number of messages $total = imap_num_msg($inbox); ?> <html> <head> </head> <body bgcolor="White"> <? // page header ?> <table width="100%" border="0" cellspacing="3" cellpadding="5"> <!-- command buttons - snipped --> </table> <? if ($total > 0) { ?> <table width="100%" border="0" cellspacing="0" cellpadding="5"> <form action="delete.php" method="post"> <!-- message info columns --> <tr> <td width="5%"><font size="-1">&nbsp;</font></td> <td width="5%"><font size="-1">&nbsp;</font></td> <td width="15%"><font face="Verdana" size="-1"><b>Date</b></font></td> <td width="20%"><font face="Verdana" size="-1"><b>From</b></font></td> <td width="45%"><font face="Verdana" size="-1"><b>Subject</b></font></td> <td width="10%"><font face="Verdana" size="-1"><b>Size</b></font></td> </tr> <?php // iterate through messages for($x=$total; $x>0; $x--) { // get header and structure $headers = imap_header($inbox, $x); $structure = imap_fetchstructure($inbox, $x); ?> <tr bgcolor="<?php echo $bgcolor; ?>"> <td align="right" valign="top"> <input type="Checkbox" name="dmsg[]" value="<? echo $x; ?>"> </td> <td valign="top"> <? // attachment handling code goes here // parse structure to see if attachments exist // display icon if so $sections = parse($structure); $attachments = get_attachments($sections); if(is_array($attachments)) { echo "<img alt=\"Message has attachment\" src=images/list.gif width=30 height=30 border=0>"; } else { echo "&nbsp;"; } ?> </td> <td valign="top"> <font face="Verdana" size="-1"><? echo substr($headers->Date, 0, 22); ?></font> </td> <td valign="top"> <font face="Verdana" size="-1"><? echo htmlspecialchars($headers->fromaddress); ?></font> </td> <td valign="top"> <font face="Verdana" size="-1"> <a href="view.php?id=<? echo $x; ?>"> <? // correction for empty subject if ($headers->Subject == "") { echo "No subject"; } else { echo $headers->Subject; } ?> </a> </font> </td> <td valign="top"> <font face="Verdana" size="-1"> <? // display message size echo ceil(($structure->bytes/1024)), " KB"; ?> </font> </td> </tr> <? } // clean up imap_close($inbox); ?> </form> </table> <? } else { echo "<font face=Verdana size=-1>You have no mail at this time</font>"; } ?> </body> </html>
And here's what the listing looks like, after making the addition:

>>> More PHP Articles          >>> More By icarus, (c) Melonfire

blog comments powered by Disqus
escort Bursa Bursa escort Antalya eskort


- Hackers Compromise PHP Sites to Launch Attac...
- Red Hat, Zend Form OpenShift PaaS Alliance
- PHP IDE News
- BCD, Zend Extend PHP Partnership
- PHP FAQ Highlight
- PHP Creator Didn't Set Out to Create a Langu...
- PHP Trends Revealed in Zend Study
- PHP: Best Methods for Running Scheduled Jobs
- PHP Array Functions: array_change_key_case
- PHP array_combine Function
- PHP array_chunk Function
- PHP Closures as View Helpers: Lazy-Loading F...
- Using PHP Closures as View Helpers
- PHP File and Operating System Program Execut...
- PHP: Effects of Wrapping Code in Class Const...

Developer Shed Affiliates


Dev Shed Tutorial Topics: