In my first tutorial I created a simple Webmail application that uses the IMAP protocol to access a user’s mailbox. In somewhere around 300 lines of code we were able to implement most of what you need to access an IMAP server to read, send, and delete email.
You might have asked yourself what on earth you would want to do with such an application when there are perfectly adequate mail programs around, and indeed, a handful of people did e-mail me to ask that very question.
Apart from the clear practicality as an example in an educational context, a Webmail application can be very useful in Real Life as well. For example, it might be a good alternative way of accessing your e-mail while on the road: you just need to pop into some Internet café, and all you need is a browser to access your mail.
NOTE: See page 6 for the code you’ll need, and page 7 for the index page code.
More features!
The first version of the Webmail interface had a glaring omission: support for mail folders. Only people who hardly get any mail at all or who delete all their mail after they’ve read it would not be interested in having the option of moving messages to other folders.
First off, we’ve added another page that shows the user the mail a_folders she has created. This page is displayed when the user clicks the “folders” button that appears at the bottom left group of buttons whenever the user is viewing the contents of a folder.
The folders page is quite minimalist. You can select a folder to browse from the list or you can create a new folder by typing in a folder name and pressing the “new folder” button.
While viewing the contents of a folder you can now move messages into other folders as well. Just mark the checkboxes in front of the messages you wish to move into a particular folder (just like you would do if you wanted to delete them), then select a folder from the folder pulldown menu in the lower right corner and click the “move to” button. The selected mail messages will then be moved to the target folder.
Folders and files
To make the files in which the folders reside easier to identify, and therefore easier to list in the Webmail interface, I’ve added some code to massage the folder names a bit.
When you want to enter or create a folder the folder name that you typed (or clicked on) is filtered through the function
m_mailbox_name |
function m_mailbox_name ($mbx)
{
global $M_MBOX, $M_PREFIX;
/* no mailbox specified means we map it to the inbox */
if ($mbx == “”) {
return $M_MBOX . “INBOX”;
}
/* replace some special chars */
$mbx = ereg_replace(“[^[:alnum:]]”, “_”, $mbx);
return $M_MBOX . $M_PREFIX . $mbx;
}
If the mailbox name was blank, it will assume that we want the default folder. In the IMAP protocol this folder is called
INBOX |
$M_MBOX |
$M_PREFIX |
The variable
To this we then add the string contained in
Why do you want to do that?The prefix
The
The third argument to the PHP3 function
In short: folder names on disk have prefixes so we can identify them. When displayed to the user everything but the folder name itself is stripped away.
Keeping trackNow that we have several folders, we have to keep track of which folder we’re in. Actually, most of the code to do this was already added in version 1.0. The
To remember what folder we were in while browsing a message, when moving or deleting messages some pages have a hidden input field (well, “input field” is obviously a bad name if you’re not supposed to input anything into it or even see it, but nevertheless, ’tis an input field). This is sort of a sneaky way to have the application open the correct folder. We could also have used cookies to keep track of the current folder. Setting cookies is fairly simple in PHP3 and reading them is even simpler. (Yes, you guessed it; cookies are turned into variables just like forms fields are). Note: See page 5 for a rundown of what’s new in this tutorial. What’s next?Our little Webmail interface is still under 500 lines of code. Considering what it would take to write an equivalent application with a GUI, that is not bad at all. Of course there is a lot left to be desired. For one, the folders should be hierarchical and they should be displayed as such. Some tasks could be automated, such as the creation of monthly archive folders. E-mail could be moved from the default folder into such archive folders when a message reaches a certain age. Also, we haven’t begun to dive into MIME and multipart MIME messages. Tune in next month and find out where we go next!ø
Related resources 1. Web mail in PHP Part I of this workshop.
|
New functions
- m_move_mail()
Move one or more mail messages from the current mailbox to some other mailbox. - m_list_mailboxes()
Return an array containing the names of the mailfolders the IMAP server can find. The names returned in the array have been stripped of their prefixes. - m_folder_form()
Display the folder list and provide a data entry field where the user can type in a new folder name. - m_create_mailbox()
Creates a new mail-folder. - m_strip_mbox_name()
Takes the mailbox name as returned by the internal IMAP functions and strips off all the ugly parts the user doesn’t want to know about.
These functions were changed
- m_login()
Minor bugfix plus added some code to incorporate the new features in the interface. - m_mailbox_name()
Added prefix to all mailbox names except the default mailbox called “INBOX”.
Also, the index.php3 file was changed a bit to reflect the new functionality available and to perhaps be a bit more readable.
imapfuncs.phl
n”;
print “n”;
n”;print “
“; n”;
print “Folder name “;
print “$i = 1;
$bgcolor = $M_COLOR_EVEN;
print “n”; n”;
print ”Default inbox n”;
print “if ($mailboxes) {
while (list($dummy, $box) = each($mailboxes)) {
$bgcolor = ($i%2 == 0)?$M_COLOR_EVEN:$M_COLOR_ODD;
print “n”; n”;
print ”$box n”;
print “
$i++;
}
}print “
print ““;
print “n”;
print “n”;
}function m_create_mailbox ($new = ”)
{
global $MBOX, $M_REALM;
global $PHP_AUTH_USER, $PHP_AUTH_PW;if (! $MBOX) {
m_login(“INBOX”);
}if ($new == “”) {
return false;
}$name = m_mailbox_name($new);
$result = imap_createmailbox($MBOX, $name);return $result;
}function m_login ($mailbox = ”)
{
global $MBOX, $M_REALM;
global $PHP_AUTH_USER, $PHP_AUTH_PW;if ($MBOX) {
return true;
}if (! $PHP_AUTH_USER) {
m_reject($M_REALM);
}$MBOX = @imap_open(m_mailbox_name($mailbox), $PHP_AUTH_USER, $PHP_AUTH_PW);
if (! $MBOX) {
m_reject($M_REALM);
}return true;
}function m_list($mailbox = ”)
{
global $MBOX, $PHP_SELF;
global $M_COLOR_ODD, $M_COLOR_EVEN, $M_COLOR_HEAD, $M_COLOR_BG;/* if not logged into server, do so */
if (! $MBOX) {
if (! m_login($mailbox)) {
return false;
}
}$num = imap_num_msg($MBOX);
print “
n”;return true;
}function m_display($msgno, $mailbox = ”)
{
global $MBOX, $M_COLOR_HEAD, $M_COLOR_BG;
global $PHP_SELF;if (! $MBOX) {
if (! m_login($mailbox)) {
return false;
}
}$struc = imap_fetchstructure($MBOX, $msgno);
if (! $struc) {
return false;
}$head = imap_header($MBOX, $msgno, 50, 50, 0);
$from = $head->fromaddress;
$subj = $head->subject;
$date = $head->date;
$body = htmlentities(imap_body($MBOX, $msgno));print “
n”; n”;
print “n”;
n”;
print “n”; Message #$msgno: $from / $subj
print “n”; n”;
print “From: $fromn”;
print “Subject: $subjn”;
print “Date: $daten”;
print “
n”;
print “$bodyn”;
print “
print “n”; “; print “
print “
print “return true;
}function m_delete ($msgno, $mailbox=”)
{
global $MBOX;if (is_array($msgno)) {
while (list($dummy, $num) = each($msgno)) {
imap_delete($MBOX, $num);
}
imap_expunge($MBOX);
} else {
return false;
}return true;
}function m_compose ($msgno=”, $mailbox=”)
{
global $MBOX, $M_COLOR_HEAD, $M_COLOR_BG;
global $PHP_SELF, $PHP_AUTH_USER, $M_MAILSERVER;if ($msgno != ”) {
$head = imap_header($MBOX, $msgno, 150, 150, 0);
$to = $head->fromaddress;
$subject = “Re: ” . $head->subject;
$body = “$to wrote:n”;
$body .= ereg_replace(“n”,”n>”, “n” . imap_body($MBOX, $msgno));
} else {
$to = “”;
$subject = “”;
$body = “”;
}print “
n”; n”;
print “n”;
print “
}function m_send ($to, $subject, $body)
{
global $PHP_AUTH_USER, $M_MAILSERVER, $M_SYSNAME;if ($PHP_AUTH_USER && $M_MAILSERVER && $to && $body) {
$headers = “From: $PHP_AUTH_USER@$M_MAILSERVERn”;
$headers .= “Reply-to: $PHP_AUTH_USER@$M_MAILSERVERn”;
$headers .= “Content-Type: text/plain; charset=iso-8859-1n”;
$headers .= “Content-Transfer-Encoding: 8bitn”;
$headers .= “X-Mailer: $M_SYSNAME/” . phpversion() . “n”;return mail($to, $subject, $body, $headers);
}return false;
}function m_date_format($datestr)
{if (ereg(“([[:digit:]]{1,2})[[:space:]]+([[:alpha:]]{3})[[:space:]]+([[:digit:]]{4})”,
$datestr, $regs)) {
return $regs[0];
}return $datestr;
}function m_mailbox_name ($mbx)
{
global $M_MBOX, $M_PREFIX;/* no mailbox specified means we map it to the inbox */
if ($mbx == “”) {
return $M_MBOX . “INBOX”;
}/* replace some special chars */
$mbx = ereg_replace(“[^[:alnum:]]”, “_”, $mbx);return $M_MBOX . $M_PREFIX . $mbx;
}function m_strip_mbox_name ($mbx)
{
global $M_PREFIX;ereg(“$M_PREFIX(.*)$”, $mbx, $regs);
return $regs[1];
}function m_reject($dom)
{
Header(“HTTP/1.0 401 Unauthorized”);
Header(“WWW-authenticate: basic realm=”$dom””);
print “Access deniedn”;
exit;
}/* make sure there is NO trailing space here!!! */
?>
index.php3
Folder: INBOX,
User: $PHP_AUTH_USER“;
} else {
print “Folder: $m,
“;
User: $PHP_AUTH_USER
}print “
n”;
if ($cmd == “delete”) {
m_delete($marked, $m);
m_list($m);
}elseif ($cmd == “display”) {
m_display($n, $m);
}elseif ($cmd == “folders”) {
m_folder_form();
}elseif ($cmd == “new folder” || $new_folder != “”) {
m_create_mailbox($new_folder);
m_folder_form();
}elseif ($cmd == “move to”) {
m_move_mail($marked, $tm);
m_list($m);
}elseif ($cmd == “compose” || $cmd == “reply”) {
m_compose($n, $m);
}elseif ($cmd == “send”) {
m_send($to, $subject, $body);
m_list($m);
}else {
m_list($m);
}?>