Building A PHP-Based Mail Client (part 1) - Opening Up
(Page 5 of 7 )
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":
<table width="100%" border="0" cellspacing="0" cellpadding="5">
<tr>
<td><img src="images/logo.jpg" width=67 height=55 alt="" border="0"></td>
<td valign="bottom" align="right"><font size="-2"
face="Verdana">Everything here is © <a
href="http://www.melonfire.com/">Melonfire</a>, 2001.<br>All rights
reserved.</font></td>
</tr>
<tr>
<td bgcolor="#C70D11" align="left"><font size="-1" color="white"
face="Verdana"><b><? echo $title; ?></b></font></td>
<td bgcolor="#C70D11" align="right"><?
if(session_is_registered("SESSION")) { ?><font size="-1" color="white"
face="Verdana"><b><a style="color: white" href="logout.php">Log
Out</a></b></font><? } ?> </td>
</tr>
</table>
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).
Okay, back to "list.php". Here's the 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
$title = "Message Listing ($total total)";
include("header.php");
?>
<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"> </font></td>
<td width="5%"><font size="-1"> </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
?>
</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>
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:
$obj->remail;
$obj->date,
$obj->Date,
$obj->subject,
$obj->Subject,
$obj->in_reply_to,
$obj->message_id,
$obj->newsgroups,
$obj->references
$obj->toaddress
$obj->fromaddress
$obj->ccaddress
$obj->bccaddress
$obj->reply_toaddress
$obj->senderaddress
$obj->udate
For a complete list, take a look at the PHP manual page for
this function at http://www.php.net/manual/en/function.imap-header.php
Here's the code to print the message date and sender:
<!-- snip -->
<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>
<!-- snip -->
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.
<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>
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.
<td valign="top">
<font face="Verdana" size="-1">
<?
// display message size
echo ceil(($structure->bytes/1024)), " KB";
?>
</font>
</td>
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.
<td valign="top">
<font face="Verdana" size="-1">
<?
// attachment handling code goes here
?>
</font>
</td>
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.
<td align="right" valign="top">
<input type="Checkbox" name="dmsg[]" value="<? echo $x; ?>">
</td>
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.
Here's what it all looks like:

Next: Calling The Exterminator >>
More PHP Articles
More By icarus, (c) Melonfire