Archive for April 2009

Accessing Email through IMAP using PHP

For a concise overview of some of the more common functions and a few tips read PHP IMAP Notes.

Previously I wrote about Intercepting Email with PHP and so far, that has been the most popular post on this blog. That being the case, I decided to write about how you can use PHP’s IMAP functions to access email.

The main difference between the two method is that when accessing email through IMAP you are accessing a mailbox that already exists. If you use piping to intercept an email you bypass the mailbox altogether. One advantage of using PHP’s IMAP functions to access Email is that you can avoid working with raw data, which would be the case with piping. With the functions provided by PHP, there would be nothing to stop you from building a webmail application with just as many capabilities as any of the major applications out there.

Before you can retrieve your email you must establish a connection to the mailbox. In order to do this you will need the IP and port of the mail server, along with a valid username and password. To connect to a mailbox on your local host use this code:

$host = 'localhost:143';
$user = 'user@domain.com';
$password = 'drowssap';
$mailbox = "{$host}INBOX";

$mbx = imap_open($mailbox , $user , $password);

Note that port 143 is the default, but is not always the correct port. If you don’t know the port you can use just the IP (or localhost) by itself, though it is recommended that you find the correct port. Also note that you can use this code to open a POP stream, as well as many other protocols. I’ll show you how to open a POP stream here, but for other protocols (IMAP over SSL, NNTP, etc.) you’ll have to read the documentation for imap_open.

$pophost = 'localhost:110/pop3';
$user = 'user@domain.com';
$password = 'drowssap';
$mailbox = "{$host}INBOX";

$mbx = imap_open($mailbox , $user , $password);

In this case as well, port 110 is the default but might not be the case for you.

Now that we’ve opened our IMAP (or other) stream we can start using PHP’s various IMAP functions to work with our mailbox. Note that if you’re stream is not an IMAP stream, not all of these functions will work as intended. Here I will show you to retrieve all of the emails in your inbox and how to view one of those emails, but I will only be using a small portion of the functions available to you, so you may want to take a look at the functions.

<?php
$mbx = imap_open($mailbox , $user , $password);

/*imap_check returns information about the mailbox
including mailbox name and number of messages*/
$check = imap_check($mbx);

/*imap_fetch_overview returns an overview for a message.
An overview contains information such as message subject,
sender, date, and if it has been seen. Note that it does
not contain the body of the message. Setting the second
parameter to "1:n" will cause it to return a sequence of messages*/
$overviews = imap_fetch_overview($mbox,"1:{$check->Nmsgs}");
?>
<table>
     <tr>
          <td>From</td>
          <td>Date</td>
          <td>Subject</td>
     </tr>

<?php
foreach($overviews as $overview)
{
     ?>
     <tr>
          <td><?php echo $overview->from; ?></td>
          <td><?php echo $overview->date; ?></td>
          <td><a href="open.php?id=<?php echo $overview->uid; ?>"><?php echo $overview->subject; ?></a></td>
     </tr>
     <?php
}
?>
</table>

The code above will generate a list of emails in your inbox, similar to what you would see if you logged into your email account. Each subject line is a link to open.php which is an arbitrary script that will open the message with the corresponding uid. The code below is an example of what open.php might look like:

<?php
$uid = $_GET['id']; //I won't validate this input, but you should

$mbx = imap_open($mailbox , $user , $password);
$overview = imap_fetch_overview($mbx, $uid, FT_UID);
$body = imap_body($mbx, $uid, FT_UID);
?>
<table>
     <tr>
          <td>From:</td>
          <td><?php echo $overview->from; ?></td>
     </tr>
     <tr>
          <td>Date:</td>
          <td><?php echo $overview->date; ?></td>
     </tr>
     <tr>
          <td>Subject:</td>
          <td><?php echo $overview->subject; ?></td>
     </tr>
     <tr>
          <td colspan="2"><?php echo $body; ?></td>
     </tr>
</table>

Notice that I set the FT_UID parameter in order to use a UID rather than a message_id. A UID will always remain the same, while a message_id may change as the content of the mailbox changes. The script would most likely work with message ids, but UIDs are guaranteed to work. If you are using a POP stream, you might find that in some cases you have to use message_ids in place of UIDs due to compatibility issues.

The imap_body function returns the body of the message with the corresponding id, but it also sets the message to “seen”. If you do not want to mark the message as seen, pass it the FT_PEEK option.

There is a great deal that you can do with these functions; far too much for me to detail all of it here. If there is anything in particular you would like to see, feel free to post a comment detailing your question.

Core Dumps

Today I noticed that my disk space usage on my web host was much higher than what it should have been. As soon as I opened my ftp program to browse through my files I found the problem. In my home directory there were tons of core.##### files (where # is some number). Each one was roughly 45MB and in total they were eating up about 2GB of space. I don’t know why they were generated, but I do know how. Core dumps occur when some program crashes. The core files are more or less an image of what was in memory at the time of the crash. These files are generated mostly for debugging purposes and (should be) safe to delete. If you are unsure of whether or not you can delete them, try renaming them and wait to see if anything explodes.

If these files are continuously being generated, its most likely a signal that something is crashing on a regular basis, but otherwise it may just be a rare occurrence. I’ve read that these files can be caused by WordPress and Drupal so if it becomes a problem you may want to look into that.

PDO… Use It

The majority of PHP code I see, whether it be posted by a beginner on a forum, or built into a large application, seems to suggest that the current standard when accessing a database is to make use of whatever database specific commands PHP provides for your particular database (ie mysql_query, mssql_query, etc). Often times, calls to these function are coupled with various attempts to prevent sql injections (such as calls to mysql_real_escape_string). Altogether, however, to me it seems clumsy and insecure.

PHP5 provies a built in (partial) database abstraction layer that can not only simplify the process of querying your database not only protecting against sql injections, but optimize your queries and make your application more portable: PDO (PHP Data Objects). A typical database transaction with PHP might look something like this:

mysql_connect('localhost', 'user', 'password');
mysql_select_db('myDB');

$data = mysql_real_escape_string($_POST['data']);
$query = 'SELECT column FROM table WHERE data = \'' . $data . '\'';

$result = mysql_query($query);
while($row = mysql_fetch_array($result, FETCH_NUM))
{
     echo $row[0];
}

Now with PDO

$dsn = 'mysql:dbname=myDB;host=127.0.0.1';
try {
     $db = new PDO($dsn , 'user' , 'password');
}
catch(PDOException $e) {
     echo $e->getMessage();
}

$query = 'SELECT column FROM table WHERE data = ?';
$statement = $db->prepare($query);
$statement->bindParam(1 , $_POST['data']);
$statement->execute();

$rows = $statement->fetchAll(PDO::FETCH_NUM);

foreach($rows as $row)
{
     echo $row[0];
}

Outwardly, the second example might not seem like an improvement on the first. What is important, however, is what is going on underneath. From PHP.net:

Calling PDO::prepare() and PDOStatement::execute() for statements that will be issued multiple times with different parameter values optimizes the performance of your application by allowing the driver to negotiate client and/or server side caching of the query plan and meta information, and helps to prevent SQL injection attacks by eliminating the need to manually quote the parameters.

In addition to this, it simplifies the process of making your application compatible with another database. If you were to use database specific function throughout your application, you would need to change each to allow it to work with another database.

PDO, however, is not a complete database abstraction layer. While it does aid you in making your application portable, it will not emulate features of a particular database. If your queries are incompatible with your database PDO will not help you. Rarely, however, will it be the case that your queries are not compatible with most sql databases.

There are actually a few cases where where PDO will not be able to execute your queries (example: http://dev.mysql.com/doc/refman/5.0/en/c-api-prepared-statement-problems.html) but these cases are few and far between. Really it is rarely the case that you should NOT be using PDO.

Using PDO does not guarantee that user data is safe. You should always validate and sanitize user input, but PDO provides a decent extra line of defense.

http://us.php.net/pdo

Getting started with C++

I am by no means a C++ expert, but I did for, for a summer, teach introduction to programming in C++ (I would have preferred teaching in Java, but the job called for C++). This being the case, I’m aware of many of the issues that people who are just picking up the language have. I’ll address a few of the common questions and issues here. Continue reading ‘Getting started with C++’ »

WordPress XHTML Validation

Almost immediately after I started my blog I began having difficulties ensuring that my XHTML would validate. Most of the time I could fix it by modifying the code in a plugin that carelessly preventing my XHTML from validating. Almost always it was something trivial (ie using single quotes instead of double quotes or using CAPS in attributes), however one problem I had persisted for quite a while.

These were the errors I was getting:

  • end tag for element “div” which is not open
  • XML Parsing Error: Opening and ending tag mismatch: body
  • XML Parsing Error: Opening and ending tag mismatch: html

After some effort I realized that this error did not appear on the main page or any of the pages. It only appeared when viewing individual posts. After a while I determined that the cause of this was an extra div closing tag. I also realized that the error only occurred when comments were open, which pointed to comments.php as the source of this problem.

After looking through the code my best guess was that these lines were the source of the error:

<?php endif; // If registration required and not logged in ?>
</div>
<?php endif; // if you delete this the sky will fall on your head ?>

Removing the div from the code above seemed to fix the problem in all but one case. Apparently if a page (not a post) has comments enabled, the extra closing tag was needed and not only did validation fail, but it caused the side bar to fall below page. Making the following change seemed to fix all of the errors (as far as I’m aware):

<?php endif; // If registration required and not logged in ?>
<?php if(is_page()) { echo '</div>'; } ?>
<?php endif; // if you delete this the sky will fall on your head ?>

Keeping up with XHTML validation can be tedious, especially when the people who wrote the plugins you are using might not care about validation. Being able to validate your entire site rather than just a page at one time might ease your suffering and it just so happens that such a validator exists: http://htmlhelp.com/tools/validator/

Organic

I take no issue with cage free eggs or vegetables grown without exposure to pesticides, but I do take issue with the word organic when used to describe food.

“I can’t eat that cheeseburger because it is not organic.”

“Really!? Its not relating to or derived from or having properties characteristic of living organisms? To think that I went my whole life thinking beef was from cows.”