eZ components “Mail” to save the day


A few days ago I was asked to develop a simple script for a solidarity campaign. The idea is that people send their photos as attachements to some email. The script would download all images attached and insert a record for that in the database.

I got introduced to eZ components during my last visit to Norway to attend the eZ systems conference. I decided to give it a shot, and oh boy it’s just amazing, probably the cleanest and simplest API ever.

I managed to find the package “Mail”:

The components allows you construct Mail messages conforming to the RFCs. It has support for attachments, multipart messages and HTML mail. It also interfaces with SMTP to send the e-mail. Reading and parsing mail messages comes in version 1.1.

The package is well documented and includes many examples. Here’s the script I ended up writing (edited version):

  1. < ?php
  2. define( 'PHOTOS_DIR', 'email_photos/' );
  3. define( 'FILE_PARSED_MAILS', '.fpm' );
  4.  
  5. require_once 'Base/src/base.php';
  6. function __autoload( $className )
  7. {
  8. ezcBase::autoload( $className );
  9. }
  10.  
  11. //get the list of emails that we alread processed
  12. $parsedEmailsIds = array();
  13. $parsedEmailsIds = @unserialize( file_get_contents(FILE_PARSED_MAILS) );
  14.  
  15. if( 'array' != gettype($parsedEmailsIds) ){
  16. $parsedEmailsIds = array();
  17. }
  18.  
  19. $pop3 = new ezcMailPop3Transport( 'mail.example.org' );
  20. $pop3->authenticate( 'username', 'password' );
  21.  
  22. //get the list of emails from the server
  23. $severEmailsIds = $pop3->listUniqueIdentifiers();
  24. $emailsToFetch = array_diff($severEmailsIds, $parsedEmailsIds);
  25. if( !count($emailsToFetch) ){
  26. //no emails to get!
  27. exit;
  28. }
  29.  
  30. $set = $pop3->fetchAll(false, $emailsToFetch );
  31. $parser = new ezcMailParser();
  32. $parser->setTmpDir( 'tmp/' );
  33. $mails = $parser->parseMail( $set );
  34.  
  35. foreach ( $mails as $mail )
  36. {
  37.  
  38. //Get email parts
  39. foreach( $mail->body->getParts() as $part ){
  40. if( get_class($part) == 'ezcMailFile' ){
  41.  
  42. //get the destination file name after some cleanups
  43. $fileName = nameToSafe( basename($part->fileName) );
  44.  
  45. //the new destintation filename, this function makes sure we get a unique file name everytime
  46. $destFileName = PHOTOS_DIR . nameToSeq(PHOTOS_DIR,$fileName);
  47.  
  48. rename( $part->fileName, $destFileName );
  49.  
  50. }
  51.  
  52. //after we're done, we need to update the file that stores the list of parsed emails
  53. file_put_contents( FILE_PARSED_MAILS, serialize( $severEmailsIds ) );
  54.  
  55. ?>

I’m sure there’s a better way to get the new emails only, probably with var_export()

Note: The only problem I faced, which is VERY strange, is the ability to fetch a single email or list of emails. Surprisingly, the API didn’t provide such feature. I did an ugly hack to “Mail/transports/pop3/pop3_transport.php” to function fetchAll(), I should have created another function, but I was in a hurry. Here’s the modified version:

  1. /**
  2. * @throws ezcMailTransportException if the mail could not be retrieved.
  3. * @param bool $deleteFromServer
  4. * @param array $messages
  5. * @return ezcMailParserSet
  6. */
  7. public function fetchAll( $deleteFromServer = false, $messages = null )
  8. {
  9. if( !isset($messages) ){
  10. $message = $this->listMessages();
  11. }
  12. return new ezcMailPop3Set( $this->connection, array_keys( $messages ), $deleteFromServer );
  13. }

Tags: , , , , , , , , , , , , , , , , , , ,