only for RuBoard - do not distribute or recompile Previous Section Next Section

Reading Mail

After the user has set up some accounts, we can move on to the main game: connecting to these accounts and reading mail.

Selecting an Account

We need to select one of the user's accounts to read mail from. The currently selected account is stored in the $selected_account session variable.

If the user has a single account registered in the system, it will be automatically selected when he logs in, as follows:

						
if(number_of_accounts($auth_user)==1)
{
  $accounts = get_account_list($auth_user);
  $selected_account = $accounts[0];
  session_register("selected_account");
}

					

The number_of_accounts() function, from mail_fns.php, is used to work out whether the user has more than one account. The get_account_list() function retrieves an array of the names of the user's accounts. In this case there is exactly one, so we can access it as the array's 0 value.

The number_of_accounts() function is shown in Listing 27.7.

Listing 27.7 number_of_accounts() Function from mail_fns.php—Function to Work Out How Many Accounts a User Has Registered
function number_of_accounts($auth_user)
{
  // get the number of accounts that belong to this user
  $query = "select count(*) from accounts where username = '$auth_user'";

  if(db_connect())
  {
    $result = mysql_query($query);
      if($result)
        return mysql_result($result, 0, 0);
  }
  return 0;
}

The get_account_list() function is similar to the get_accounts() function we looked at before except that it only retrieves the account names.

If a user has multiple accounts registered, he will need to select one to use. In this case, the headers will contain a SELECT that lists the available mailboxes. Choosing the appropriate one will automatically display the mailbox for that account. You can see this in Figure 27.5.

Figure 27.5. After the localhost account is selected from the SELECT box, the mail from that account is downloaded and displayed.
graphics/27fig05.gif

This SELECT option is generated in the do_html_header() function from output_fns.php, as shown in the following code fragment:

						
<?
  // include the account select box only if the user has more than one account
  if(number_of_accounts($auth_user)>1)
  {
    echo "<form target='index.php?action=open-mailbox'method=post>";
    echo '<td bgcolor = "#ff6600" align = right valign = middle>';
    display_account_select($auth_user, $selected_account);
    echo '</td>';
    echo "</form>";
  }
?>

					

We have generally avoided discussing the HTML used in the examples in this book, but the HTML generated by the function display_account_select() bears a visit.

Depending on the accounts the current user has, display_account_select() will generate HTML like this:

						
<select onchange=window.location=this.options[selectedIndex].value name=account>
  <option value = 0 selected>
    Choose Account</a>
  <option value = 'index.php?action=select-account&account=10'>
    mail.domain.com
  </option>
  <option value = 'index.php?action=select-account&account=11'>
    mail.server.com
  </option>
  <option value = 'index.php?action=select-account&account=9'>
    localhost
  </option>
</select>

					

Most of this code is just an HTML select element, but it also includes a little JavaScript. In the same way that PHP can generate HTML, it can also be used to generate client-side scripts.

Whenever a change event happens to this element, JavaScript will set window.location to the value of the option. If your user selects the first option in the select, window.location will be set to 'index.php?action=select-account&account=10'. This will result in this URL being loaded. Obviously, if the user has a browser that does not support JavaScript or has JavaScript disabled, this code will have no effect.

The display_account_select() function, from output_fns.php, is used to get the available account list and display the SELECT. It also uses the get_account_list() function we discussed previously.

Choosing one of the options in the SELECT activates the select_account event. If you look at the URL in Figure 27.5, you can see this appended to the end of the URL, along with the account ID of the chosen account.

This has two effects. First, in the preprocessing stage of index.php, the chosen account will be stored in the session variable $selected_account, as follows:

						
case 'select-account' :
{
// if have chosen a valid account, store it as a session variable
if($account&&account_exists($auth_user, $account))
{
  $selected_account = $account;
  session_register('selected_account');
}
}

					

Second, when the body stage of the script is executed, the following code will be executed:

						
case 'select-account' :
case 'view-mailbox' :
{
  // if mailbox just chosen, or view mailbox chosen, show mailbox
  display_list($auth_user, $selected_account);
  break;
}

					

As you can see, we take the same action here as if the user had chosen the View Mailbox option. We'll look at that next.

Viewing Mailbox Contents

Mailbox contents can be viewed with the display_list() function. This displays a list of all the messages in the mailbox. The code for this function is shown in Listing 27.8.

Listing 27.8 display_list() Function from output_fns.php—Function to Display All Mailbox Messages
function display_list($auth_user, $accountid)
{
  // show the list of messages in this mailbox

  global $table_width;

  if(!$accountid)
  {
    echo "No mailbox selected<br><br><br><br><br><br>.";
  }
  else
  {

    $imap = open_mailbox($auth_user, $accountid);

    if($imap)
    {
      echo "<table width = $table_width cellspacing = 0
                   cellpadding = 6  border = 0>";

      $headers = imap_headers($imap);
      // we could reformat this data, or get other details using
      // imap_fetchheaders, but this is not a bad summary so we just echo each

      $messages = sizeof($headers);
      for($i = 0; $i<$messages; $i++)
      {
        echo "<tr><td bgcolor = '";
        if($i%2)
          echo "#ffffff";
        else
          echo "#ffffcc";
        echo "'><a href ='index.php?action=view-
           message&messageid=".($i+1)."'>";
        echo $headers[$i];
        echo "</a></td></tr>\n";
      }
      echo "</table>";

    }
    else
    {
      $account = get_account_settings($auth_user, $accountid);
      echo "could not open mail box ".$account['server'].".<br><br><br><br>";
    }
  }
}

In this function, we actually begin to use PHP's IMAP functions. The two key parts of this function are opening the mailbox and reading the message headers.

We open the mailbox for a user account with a call to the open_mailbox() function that we have written in mail_fns.php. This function is shown in Listing 27.9.

Listing 27.9 open_mailbox() Function from mail_fns.php—This Function Connects to a User Mailbox
function open_mailbox($auth_user, $accountid)
{
  // select mailbox if there is only one
  if(number_of_accounts($auth_user)==1)
  {
    $accounts = get_account_list($auth_user);
    $selected_account = $accounts[0];
    session_register("selected_account");
    $accountid = $selected_account;
  }

  // connect to the POP3 or IMAP server the user has selected

  $settings = get_account_settings($auth_user, $accountid);
  if(!sizeof($settings)) return 0;
  $mailbox = "{ ".$settings[server];
  if($settings[type]=='POP3')
    $mailbox .= '/pop3';
  $mailbox .= ":".$settings[port]."} INBOX";

// suppress warning, remember to check return value
@  $imap = imap_open($mailbox, $settings[remoteuser],
                $settings[remotepassword]);

  return  $imap;

}

We actually open the mailbox with the imap_open() function. This function has the following prototype:

						
int imap_open (string mailbox, string username, string password [, int flags])

					

The parameters you need to pass to it are as follows:

You can also pass it optional flags to specify options such as "open mailbox in read-only mode".

One thing to note is that we have constructed the mailbox string piece by piece with the concatenation operator before passing it to imap_open(). You need to be careful how you construct this string because strings containing {$ cause problems in PHP 4.

This function call returns an IMAP stream if the mailbox can be opened, and false if it cannot.

When you are finished with an IMAP stream, you can close it using imap_close(imap_stream).

In our function, the IMAP stream is passed back to the main program. We then use the imap_headers() function to get the email headers for display:

						
$headers = imap_headers($imap);

					

This function returns header information for all mail messages in the mailbox we have connected to. The information is returned as an array, one line per message. We haven't formatted this. It just outputs one line per message, so you can see from looking at Figure 27.5 what the output looks like.

You can get more information about email headers using the confusing, similarly named imap_header(). In this case, the imap_headers() function gives us enough detail for our purpose.

Reading a Mail Message

We have set up each of the messages in the previous display_list() function to link to specific email messages.

Each link is of the form

						
index.php?action=view-message&messageid=6

					

The messageid is the sequence number used in the headers we retrieved earlier. Note that IMAP messages are numbered from 1, not 0.

If the user clicks one of these links, he will see output like that shown in Figure 27.6.

Figure 27.6. Using the view-message action shows us a particular message—in this case, it's a piece of spam.
graphics/27fig06.gif

When we enter these parameters into the index.php script, we execute the following code:

						
case 'show-headers' :
case 'hide-headers' :
case 'view-message' :
{
  // if we have just picked a message from the list, or were looking at
  // a message and chose to hide or view headers, load a message
  $fullheaders = ($action=='show-headers');
  display_message($auth_user, $selected_account, $messageid,
     $fullheaders)
  break;
}

					

You'll notice that we're checking the value of the $action being equal to 'show-headers'. In this case, it will be false, and $fullheaders will be set equal to false. We'll look at the 'show-headers' action in a moment.

The line

						
$fullheaders = ($action=='show-headers');

					

could have been more verbosely—and perhaps more clearly—written as

						
if($action=='show-headers')
  $fullheaders = true;
else
  $fullheaders = false;

					

Next, we call the display_message() function. Most of this function outputs plain HTML, so we will not go through it here. It calls the retrieve_message() function to get the appropriate message from the mailbox:

						
$message = retrieve_message($auth_user, $accountid, $messageid, $fullheaders);

					

The retrieve_message() function is in the mail_fns.php library. You can see the code for it in Listing 27.10.

Listing 27.10 retrieve_message() Function from mail_fns.php—This Function Retrieves One Specific Message from a Mailbox
function retrieve_message($auth_user, $accountid, $messageid, $fullheaders)
{
  $message = array();

  if(!($auth_user && $messageid && $accountid))
    return false;

  $imap = open_mailbox($auth_user, $accountid);
  if(!$imap)
    return false;

  $header = imap_header($imap, $messageid);

  if(!$header)
    return false;

  $message['body'] = imap_body($imap, $messageid);
  if(!$message['body'])
    $message['body'] = '[This message has no body]\n\n\n\n\n\n';

  if($fullheaders)
    $message['fullheaders'] = imap_fetchheader($imap, $messageid);
  else
    $message['fullheaders'] = '';
  $message['subject'] = $header->subject;
  $message['fromaddress'] =   $header->fromaddress;
  $message['toaddress'] =   $header->toaddress;
  $message['ccaddress'] =   $header->ccaddress;
  $message['date'] =   $header->date;

  // note we can get more detailed information by using from and to
  // rather than fromaddress and toaddress, but these are easier

  imap_close($imap);
  return $message;
}

Again we have used open_mailbox() to open the user's mailbox.

This time, however, we are after a specific message. Using this function library, we download the message headers and message body separately.

The three IMAP functions we use here are imap_header(), imap_fetchheader(), and imap_body(). Note that the two header functions are distinct from imap_headers,() the one we used previously. They are somewhat confusingly named. In summary,

In this case we use imap_header() to fill out specific header fields and imap_fetchheader() to show the user the full headers if requested. (We'll come back to this.)

We use imap_header() and imap_body() to build an array containing all the elements of a message that we are interested in.

We call imap_header() as follows:

						
$header = imap_header($imap, $messageid);

					

We can then extract each of the fields we require from the object:

						
$message['subject'] = $header->subject;

					

We call imap_body() to add the message body to our array as follows:

						
$message['body'] = imap_body($imap, $messageid);

					

Finally we close the mailbox with imap_close() and return the array we have built. The display_message() function can then display the message's fields in the form you can see in Figure 27.6.

Viewing Message Headers

As you can see in Figure 27.6, there is a Show Headers button. This activates the show-headers option, which adds the full message headers to the message display. If the user clicks this button, he will see output similar to that shown in Figure 27.7.

Figure 27.7. Using show-headers to see the full headers for this message will help a user to track down the source of the spam.
graphics/27fig07.gif

As you probably noticed, the event handling for view-message covers show-headers (and its counterpart hide-headers) too. If this option is selected, we do the same things as before. But in retrieve_message(), we also grab the full text of the headers, as follows:

						
if($fullheaders)
    $message['fullheaders'] = imap_fetchheader($imap, $messageid);

					

We can then display these headers for the user.

Deleting Mail

If a user clicks the Delete button on a particular email, he will activate the "delete" action. This will execute the following code from index.php:

						
case 'delete' :
{
  delete_message($auth_user, $selected_account, $messageid);
  //note deliberately no 'break'- we will continue to the next case
}
case 'select-account' :
case 'view-mailbox' :
{
  // if mailbox just chosen, or view mailbox chosen, show mailbox
  display_list($auth_user, $selected_account);
  break;
}

					

As you can see, the message is deleted using the delete_message() function, and then the resulting mailbox is displayed as discussed previously.

The code for the delete_message() function is shown in Listing 27.11.

Listing 27.11 delete_message() Function from mail_fns.php—This Function Deletes One Specific Message from a Mailbox
function delete_message($auth_user, $accountid, $message_id)
{
  // delete a single message from the server

  $imap = open_mailbox($auth_user, $accountid);
  if($imap)
  {
    imap_delete($imap, $message_id);
    imap_expunge($imap);
    imap_close($imap);
    return true;
  }
  return false;
}

As you can see, this function uses a number of the IMAP functions. The new ones are imap_delete() and imap_expunge(). Note that imap_delete() only marks messages for deletion. You can mark as many messages as you like. The call to imap_expunge() actually deletes the messages.

only for RuBoard - do not distribute or recompile Previous Section Next Section