HomePHP Page 5 - Building A PHP-Based Mail Client (part 1)
Opening Up - PHP
Ever wondered how Web-based mail clients work, or what happens toyour email after you hit the "Send" button? This three-part case studydelves into the wild and wacky world of Web-based email applications, usingPHP's built-in POP3 functions to build an email client suitable forrerieving POP3 email via a Web browser. In this introductory segment -connecting to a POP3 server, logging in and out, retrieving message headersfor display, and deleting messages off the server.
With the session instantiated, the next step is to retrieve and display a list of messages from the user's mailbox on the mail server. This is accomplished via "list.php", a PHP script which opens a connection to the POP3 server, obtains a list of message headers and displays them in a neat table.
Before getting into the details of "list.php", I want to take a minute to explain a little something about the user interface. If you look at the sample screenshots in this article, you'll see that every page generated through this application has some common elements: the logo at the top left corner, the copyright note at the top right corner, and a dividing bar containing the page title below both.
Since these elements will remain constant through the application, I've placed the corresponding HTML code in a separate header file, and simply include()d them on each page. Take a look at "header.php":
Again, by separating common interface elements into separate
files, I've made it easier to customize the look of the application; simply alter these files, and the changes will be reflected on all the pages.
Note that the page title needs to be specified as a variable prior to including this header file in a script - you'll see examples of this over the next few pages. Note also the link to log out, which appears only after the user has logged in (I've used the session_is_registered() function to test for the presence of a valid PHP session).
This probably looks complicated, but it isn't really. Let's
take it from the top:
1. The first few lines of the script are pretty standard - I've included the common function definitions, and tested for the presence of a valid session (and, by implication, the presence of a mail username, password and host).
<?
// includes
// session check
session_start();
if (!session_is_registered("SESSION"))
{
header("Location: error.php?ec=2");
exit;
}
?>
In the event that this test fails, the browser is immediately
redirected to the generic error handler, "error.php", with an error code identifying the problem. You'll see this code in almost every script that follows; it's a standard validation routine I plan to perform throughout the application.
2. Assuming that the session check is passed, the next step is to open a connection to the POP3 server. PHP offers the imap_open() function for this purpose; it accepts three parameters: the POP3 server name, the POP3 user name and the corresponding password (you can also use the imap_open() command to open a connection to an IMAP or NNTP server - look at http://www.php.net/manual/en/ref.imap.php for examples).
<?
// open mailbox
$inbox = @imap_open ("{". $SESSION_MAIL_HOST . "/pop3:110}",
$SESSION_USER_NAME, $SESSION_USER_PASS) or header("Location:
error.php?ec=3");
?>
If you're familiar with the POP3 protocol, this is equivalent
to sending USER and PASS commands to the server.
If the connection is successful, this function returns a handle representing the mailbox, required for all subsequent operations.
In the event that a connection cannot be opened - say, the password is wrong or the mail server is not active - the browser is again redirected to "error.php" with an error code indicating the problem.
3. If a connection is successfully initiated, the imap_num_msg() function, in concert with the handle returned by imap_open(), is used to obtain the total number of messages in the mailbox; this number is then displayed in the page title.
<?
// get number of messages
$total = imap_num_msg($inbox);
// page header
$title = "Message Listing ($total total)";
include("header.php");
?>
Incidentally, don't be fooled by the prefix on all these
function names; as stated previously, though every function starts with "imap_", PHP's IMAP extension can also be used with the POP3 and NNTP protocols.
4. Assuming that there are messages in the mailbox, an HTML table is generated to hold the message headers. In this case, I've decided to display the message date, subject, sender and size, together with a checkbox for message selection and an attachment icon if an attachment exists.
<?
if ($total > 0)
{
?>
<table width="100%" border="0" cellspacing="0" cellpadding="5">
<form action="delete.php" method="post">
<!-- table rows and columns go here -->
</form>
</table>
<?
}
else
{
echo "<font face=Verdana size=-1>You have no mail at this time</font>";
}
?>
Within the table, a "for" loop iterates as many times as
there are messages, retrieving the headers and structure of each message with the imap_header() and imap_fetchstructure() functions respectively. Throughout this loop, the variable $x references the current message number. Note that I'm iterating through the message list in reverse order so that the more recent messages are displayed first.
<?
// iterate through messages
for($x=$total; $x>0; $x--)
{
// get header and structure
$headers = imap_header($inbox, $x);
$structure = imap_fetchstructure($inbox, $x);
// table rows here
}
?>
If you're familiar with the POP3 protocol, this is equivalent
to sending a series of RETR commands to the server.{mospagebreak title=Treating Messages As Objects} 5. Of the headers I've selected for display, the sender, subject and date are fairly easy to obtain - the imap_header() function returns an object, one for each message, exposing these values as properties. All I need to do is access these properties and echo() them to the page. For example, the object property
$obj->fromaddress
would reference the message's From: header, while the
property
$obj->Subject
would reference the message subject.
The imap_header()
function returns an object with the following properties, each corresponding to a different attribute of the mail message:
I also need to link each message to a script, "view.php",
which displays the complete message body. I've decided to do this by attaching a hyperlink to the subject of every message in the message list and passing it the message number via the URL GET method.
If you look at the list above, you'll see that the other two
elements of my proposed message listing - the message size and the attachment status - are not available through imap_header(). So what do I do?
6. The answer, as it turns out, lies in another function: imap_fetchstructure(). Using a mailbox handle and message number as arguments, this function reads the message body and returns another object, this one containing information on the message size, message body and MIME parts within it. In order to obtain the message size, I need to simply access this object's "bytes" property.
For greater readability, I've converted the number into
kilobytes and rounded up to the nearest integer.
At this point, I have absolutely no clue how to find out the attachment status. After a few experiments with the imap_fetchstructure() and imap_body() functions, I was able to obtain the complete body of the message, including the headers for MIME attachments. However, parsing these headers manually turned out to be fairly messy and code-intensive, and my gut tells me there's a better way to do it. So I'm going to leave this aside for now and come back to it after boning up on some MIME theory.
Finally, I need to provide some way for the user to delete
messages from the mailbox. The traditional technique is a checkbox next to each message, which is used to select each message for deletion...and I'm a big fan of tradition.
Note that each checkbox is linked to the message number, and
that the selected message numbers will be added to the $dmsg array. When the form is submitted, the "delete.php" script (discussed next) will use this array to identify and mark messages for deletion from the server.
7. With all (or most of) the information now displayed, the last task is to clean up by closing the POP3 connection.
<?
// clean up
imap_close($inbox);
?>
If you're familiar with the POP3 protocol, this is equivalent
to sending a QUIT command to the server.