Wrox Programmer Forums

Need to download code?

View our list of code downloads.

Go Back   Wrox Programmer Forums > PHP/MySQL > Pro PHP
Password Reminder
Register
Register | FAQ | Members List | Calendar | Search | Today's Posts | Mark Forums Read
Pro PHP Advanced PHP coding discussions. Beginning-level questions will be redirected to the Beginning PHP forum.
Welcome to the p2p.wrox.com Forums.

You are currently viewing the Pro PHP section of the Wrox Programmer to Programmer discussions. This is a community of tens of thousands of software programmers and website developers including Wrox book authors and readers. As a guest, you can read any forum posting. By joining today you can post your own programming questions, respond to other developers’ questions, and eliminate the ads that are displayed to guests. Registration is fast, simple and absolutely free .
DRM-free e-books 300x50
Reply
 
Thread Tools Display Modes
  #1 (permalink)  
Old September 20th, 2004, 07:36 AM
Registered User
 
Join Date: Sep 2004
Location: , , .
Posts: 6
Thanks: 0
Thanked 0 Times in 0 Posts
Default PHP IMAP implementation to process attachments

Somewhat complicated, but there's a big picture.

small picture: have a script that will check an email account, and save all attachments (only zip/jpg/png/gifs allowed) into a specified folder on the same server

big picture: i have an image gallery system. i would like my users to be able to email their photos to an email account. the script will automatically run every so often through cronjobs. the script will verify the email address of the sender with the database's list of email addresses. it will then grab the username, which it uses to determine where to save the images. before saving the images, script will generate thumbnails etc. already have the code which does the above sentence, but the way it works right now is through a form. so it takes the file[] attribute from a form submission. basically i want to replace the file[] variable here with the attachment from an email.

so here's the small picture code i got so far, i need help makign it work!:

<?
 foreach ($_GET as $k => $v)
 {
     $k = $v;
 }
 $login = '$emaillogin';
 $password = '$emailpass';
 $host = '{localhost:110/pop3}';

$id=1;

$mbox = imap_open("$host", "$login", "$password");

$nummessages = imap_num_msg($mbox);

       $strFileName = $att[$file]->parameters[0]->value;
       $strFileType = strrev(substr(strrev($strFileName),0,4));
       $fileContent = imap_fetchbody($mbox,$id,$file+2);


       downloadFile($strFileType,$strFileName,$fileCon tent);
   
   function downloadFile($strFileType,$strFileName,$fileConten t) {


      $ContentType = "application/octet-stream";
   
       if ($strFileType == ".zip")
           $ContentType = "application/zip";
       if ($strFileType == ".gif")
           $ContentType = "image/gif";
       if ($strFileType == ".jpg" || $strFileType == "jpeg")
           $ContentType = "image/jpeg";
  // how do i specify PNG?
       header ("Content-Type: $ContentType");

// if i replace all the below code with just "echo imap_base64($fileContent)"
// it will show just the image on the screen, as a JPG in this case.

       $thefile = imap_base64($fileContent);


$conn_id = ftp_connect("$ftpserver");
$login_result = ftp_login($conn_id, "$ftpuser", "$ftppass");
ftp_pasv($conn_id, true);

$source_file = $thefile;
$destination_file = "testupload/" . $strFileName;

$upload = ftp_put($conn_id, $destination_file, $source_file, FTP_BINARY);

if (!$upload) {
echo "FTP upload has failed!";
} else {
echo "File Upload Successful";
}
ftp_close($conn_id);

}

?>
Reply With Quote
  #2 (permalink)  
Old September 20th, 2004, 08:47 AM
richard.york's Avatar
Wrox Author
Points: 5,506, Level: 31
Points: 5,506, Level: 31 Points: 5,506, Level: 31 Points: 5,506, Level: 31
Activity: 0%
Activity: 0% Activity: 0% Activity: 0%
 
Join Date: Jun 2003
Location: Camby, IN, USA.
Posts: 1,706
Thanks: 0
Thanked 5 Times in 5 Posts
Default

I, of course, recommend that you use my Mail_IMAP package. It's an OOP solution that provides a simplifed interface to PHP's imap functions.

You can get the source code at:
http://www.smilingsouls.net/index.ph...ail_IMAP/index

You need to install this using the PEAR interface for it to work, mostly because the error handling relies on another PEAR package. It isn't too daunting however.

Then once installed you can instantiate the object and iterate through messages looking for specific parts.

If installed successfully via the PEAR interface, all you have to do is include IMAP.php, which lives in the Mail directory, which in turn lives in the PEAR directory. Based on what you said you needed I wrote a quick app.
Code:
<?php

  require_once 'Mail/IMAP.php';

  $msg =& new Mail_IMAP();

  // Open up a mail connection
  if (!$msg->connect('imap://'.urlencode('user@example.com').':password@mail.example.com:143/INBOX')) {
      echo "<span style='font-weight: bold;'>Error:</span> Unable to build a connection.<br/>\n";
      echo $msg->alerts();
      echo $msg->errors();
      // See if ErrorStack has any errors.
      if ($msg->error->hasErrors()) {
          echo "<pre>\n";
          var_dump($msg->error->getErrors(TRUE));
          echo "</pre>\n";
      }
  }

  if ($msg->mailboxInfo['Nmsgs'] >= 0) {
    for ($mid = 0; $mid < $msg->mailboxInfo['Nmsgs']; $mid++) {
        // extractMIME returns an array of message part ids
        // which can in turn be used to retrieve the message part
        // So, $pids will be a multidimensional array
        $pids[$mid]            = $msg->extractMIME($mid, array('application/zip', 'image/gif', 'image/jpeg', 'image/pjpeg', 'image/png', 'image/x-png'));
        $headers[$mid]         = $msg->getHeaders($mid, '0', TRUE);
    }
  }

  //Now you have the information you need to get the message parts themselves
  //You should have an array two deep.

  foreach ($pids as $mid => $parts) {
    foreach ($parts as $i => $pid) {

        // Retrieve the body
        $att = $msg->getBody($mid, $pid);

        $part[$mid][$i]['data'] = $att['body'];
        $part[$mid][$i]['mime'] = $att['ftype'];
    }
  }

  // Now the $part array contains all of the parts that you've specified.
  // And the required top level headers to associate a name with a 
  // those message parts are in the $headers variable.
?>
This is pretty small, Mail_IMAP does all the hard work behind the scenes, it parses message structures that are both simple and complicated, does the decoding and everything else for you. I don't know if this works, cause I haven't tested it. If not it wouldn't take much tweaking to get it there.

One thing to be careful of is filling up your RAM. I'm storing those attachments in variables, where you can put them in the file system or a DB or preform other operations. You can add an unset() in the nested foreach loop after retrieving an attachment and doing something with it to free up memory, in fact you should or you could crash your system if your inbox fills up! Might also monitor the max execution time as well, doing stuff with images and files can be costly.

You can refer to my docs at the above URL for more API info.

HTH!


Regards,
Rich

--
[http://www.smilingsouls.net]
[http://pear.php.net/Mail_IMAP] A PHP/C-Client/PEAR solution for webmail
Reply With Quote
  #3 (permalink)  
Old September 20th, 2004, 08:54 AM
Registered User
 
Join Date: Sep 2004
Location: , , .
Posts: 6
Thanks: 0
Thanked 0 Times in 0 Posts
Default

hey rich.

to be perfectly honest seems like your package would be a little bit of an overshoot... plus i don't have root access on the server.
i'm thinking there's an easier/ more customized way to do this...

Reply With Quote
  #4 (permalink)  
Old September 20th, 2004, 08:54 AM
richard.york's Avatar
Wrox Author
Points: 5,506, Level: 31
Points: 5,506, Level: 31 Points: 5,506, Level: 31 Points: 5,506, Level: 31
Activity: 0%
Activity: 0% Activity: 0% Activity: 0%
 
Join Date: Jun 2003
Location: Camby, IN, USA.
Posts: 1,706
Thanks: 0
Thanked 5 Times in 5 Posts
Default

Again there could be typos, and you could probably change where you access things to make the script more efficient, for instance you could do everything in the first 'for' loop, instead of making extra variables and doing the foreach loops. (Hope that makes sense!) Good luck with your project.

Regards,
Rich

--
[http://www.smilingsouls.net]
[http://pear.php.net/Mail_IMAP] A PHP/C-Client/PEAR solution for webmail
Reply With Quote
  #5 (permalink)  
Old September 20th, 2004, 08:58 AM
Registered User
 
Join Date: Sep 2004
Location: , , .
Posts: 6
Thanks: 0
Thanked 0 Times in 0 Posts
Default

couldn't find any typos... the efficiency thing is VERY big; but at this point i'm just trying brute force just to get the darned thing working, and then go about trimming down the code.

thanks for the luck, if you can help in any way with the method above i'd truly appreciate it!

Reply With Quote
  #6 (permalink)  
Old September 20th, 2004, 09:03 AM
richard.york's Avatar
Wrox Author
Points: 5,506, Level: 31
Points: 5,506, Level: 31 Points: 5,506, Level: 31 Points: 5,506, Level: 31
Activity: 0%
Activity: 0% Activity: 0% Activity: 0%
 
Join Date: Jun 2003
Location: Camby, IN, USA.
Posts: 1,706
Thanks: 0
Thanked 5 Times in 5 Posts
Default

Quote:
quote:
to be perfectly honest seems like your package would be a little bit of an overshoot
With the myriad of MIME types and different message structures, and the inconsistencies in how imap_fetchbody handles different part ids, I doubt you'll be able to do what you need (easily anyway). I've been developing this package for a couple of years. I mean, of course I'm going to support my own work, but I'm speaking from experience here. The imap_* functions are complicated beasts not easily tamed or understood. The first multipart/related or multipart/alternative message that you encounter will cause your whole system to crumble, especially as you have it now.

You're welcome to strip off some of the extra methods, comments, etc used in this package to make it smaller.

You don't have to have root access to use PEAR packages. All you have to do is replicate the directory structure and set the include_path directive of php.ini to whatever directory you want to use for PEAR pacakges used on your server, considering you're using Apache and can do .htaccess files. Other wise, all you have to do is make the required include paths agree.

Regards,
Rich

--
[http://www.smilingsouls.net]
[http://pear.php.net/Mail_IMAP] A PHP/C-Client/PEAR solution for webmail
Reply With Quote
  #7 (permalink)  
Old September 20th, 2004, 10:18 AM
Registered User
 
Join Date: Sep 2004
Location: , , .
Posts: 6
Thanks: 0
Thanked 0 Times in 0 Posts
Default

ok i'm going to give your app a shot.

i looked through the code you typed up in your initial posting. I don't see anywhere in that section that would tell me how i could access the image attachment.

to simplify things- i don't want to get carried away with mime types. i need to handle JPG, PNG, ZIP (i have a function that will unzip images from a zip file and proces each one as needed). oh and GIF files.

so what i'm asking is, using your package, how would i substitute what i have now (the form upload, where ALL form processing is concerned with $_files[] from the input type = file tag in the form.

so for e.g. what would be the equivalent variables that i've got as:

$tmpfile = $_FILES['image']['tmp_name']; (location image/zip file was uploaded to temporarily)
$fullpath = "$users_dir/$user/" . $_FILES['image']['name']; (final resting place for the file)

followed by a number of functions that would say things like if(is_zip_file($tmpfile)) {}

know what i mean?


Reply With Quote
  #8 (permalink)  
Old September 20th, 2004, 10:55 AM
richard.york's Avatar
Wrox Author
Points: 5,506, Level: 31
Points: 5,506, Level: 31 Points: 5,506, Level: 31 Points: 5,506, Level: 31
Activity: 0%
Activity: 0% Activity: 0% Activity: 0%
 
Join Date: Jun 2003
Location: Camby, IN, USA.
Posts: 1,706
Thanks: 0
Thanked 5 Times in 5 Posts
Default

> ok i'm going to give your app a shot.

Right on.

> i looked through the code you typed up in your initial posting. I don't see
> anywhere in that section that would tell me how i could access the image attachment.

It might seem complicated but it isn't really. Each message has an id, called $mid in the source, the $mid is created offset from one and increments for each message in the mailbox. You probably already figured that out. There is a second id, called the $pid or part id. A message can get rather complicated, messages can be attachments of other messages and those messages can have attachments and so on. So to navigate through each part of a message each part receives a unique id. Information from imap_fetchstructure is used to build these ids and imap_fetchbody accepts those ids to retrieve specific parts. There are some not very obvious idiosyncrasies to working with this data that Mail_IMAP handles behind the scenes.

Mail_IMAP parses the entire message and retrieves information about each part, builds these unique ids and uses these unique ids to extract information from messages.

> to simplify things- i don't want to get carried away with mime types. i need to handle
> JPG, PNG, ZIP (i have a function that will unzip images from a zip file and proces each
> one as needed). oh and GIF files.

That's not really possible with email. MIME by design is made to tell you what a file is. You'll only be able to know what a message part is by its MIME type. The filename parameter is often also available but it isn't always set so it can't be relied on. But the MIME however, is always set.

What the code that I provided does is..

extractMIME searches the whole message for the specified MIME types, and it returns an array of part ids if it found the specified MIME types. Those part ids, along with the message id can then be passed to getBody to retrieve the message part. Though getBody can only take one part id at a time. getBody returns the message part decoded as part of an array, the array that it returns contains the message part itself (text file, jpeg image, whatever the part is), the MIME type, the character set (if any) and the orginal file name (if there is one).

One you have extracted a message part you'd have to save it into a file or make an emulator script to use file functions on it (such as fopen or is_zip_file). But you're stuck working with MIME types if you want to use email.

I'd recommend building a multipart process.

1.) Extract message parts, save those parts to the file system. (create filenames /and/or/ a DB schema in such a way that the other processes know what needs done)
2.) Run a separate process that extracts images from zip archives and save those images back to the file system and then removes the zip archives.
3.) In yet another separate process, preform image generation/manipulation.

With this approach you can distribute the load required by each part of the process so you aren't killing the server all at once. You might run 1 first, and five minutes later run 2 on the files extracted by one, and five minutes later run a process that generates thumbnails or does other image manipulation and limit the number of files handled by each so it processes files little by little.

HTH!


Regards,
Rich

--
[http://www.smilingsouls.net]
[http://pear.php.net/Mail_IMAP] A PHP/C-Client/PEAR solution for webmail
Reply With Quote
  #9 (permalink)  
Old September 20th, 2004, 11:31 AM
Registered User
 
Join Date: Sep 2004
Location: , , .
Posts: 6
Thanks: 0
Thanked 0 Times in 0 Posts
Default

i could see joining processes 1 and 2. my guess is that there won't be an enormous server load cause the traffic i'm looking at is probably 10 file uploads a DAY.
plus, if i don't combine it, i'd need the first process to say do not process ZIP files, and the second process to say parse only zip files. too complicated!!

part 3 i figure i can do, thats the easy part in my opinion (i know i'm speaking way to soon for this!!)

but i'm still drowning on how to do the process 1&2 part; how do i use your system to carry out the following algorithm:

<start file>
- check IMAP mailbox for new mail
- if new mail found, start recursive function:
a) retrieve message
b) check mime type
c) save attachment(s) into a folder if its an image (will worry about zip later)
d) delete email message
e) go onto next email, if any.

<end>

i can easily figure out how to pass on the information required to be processed in a DB schema for the other two functions.



Reply With Quote
  #10 (permalink)  
Old September 20th, 2004, 11:53 AM
richard.york's Avatar
Wrox Author
Points: 5,506, Level: 31
Points: 5,506, Level: 31 Points: 5,506, Level: 31 Points: 5,506, Level: 31
Activity: 0%
Activity: 0% Activity: 0% Activity: 0%
 
Join Date: Jun 2003
Location: Camby, IN, USA.
Posts: 1,706
Thanks: 0
Thanked 5 Times in 5 Posts
Default

10's enough to bring your server to its knees, considering you'd be writing 10 files at once to the file system and then manipulating those files. All resource intensive stuff. Might not be a big deal but I'd keep an eye on it.

Here's take two... this example is much clearer than the last one.
Code:
<?php

  require_once 'Mail/IMAP.php';

  $msg =& new Mail_IMAP();

  // Open up a mail connection
  if (!$msg->connect('imap://'.urlencode('user@example.com').':password@mail.example.com:143/INBOX')) {
      echo "<span style='font-weight: bold;'>Error:</span> Unable to build a connection.<br/>\n";
      echo $msg->alerts();
      echo $msg->errors();
      // See if ErrorStack has any errors.
      if ($msg->error->hasErrors()) {
          echo "<pre>\n";
          var_dump($msg->error->getErrors(TRUE));
          echo "</pre>\n";
      }
  }

  // Check to see if there are messages, if there
  // are the count will be higher than 0
  if ($msg->mailboxInfo['Nmsgs'] > 0) {

    for ($mid = 1; $mid <= $msg->mailboxInfo['Nmsgs']; $mid++) {

        // extractMIME returns an array of message part ids
        // which can in turn be used to retrieve the message part
        // So, $pids will be an array
        if (FALSE !== ($pids = $msg->extractMIME($mid, array('application/zip', 'image/gif', 'image/jpeg', 'image/pjpeg', 'image/png', 'image/x-png')))) {

            // Ok, this message contains images or zip files.
            // Now Mail_IMAP can extract those parts.
            // Since extractMIME returns an array, you'll have to loop
            // though these.

            // Get the headers so you can find out who sent the message
            $headers = $msg->getHeaders($mid, '0', TRUE);

            //You'd do your email authentication here.
            //$headers['from'][0] contains the 'from' address.
            //$headers['from_personal'][0] contains any name if one was provided.
            //$headers['from_mailbox'][0] contains the mailbox name
            //$headers['from_host'][0] contains the domain name.

            // Now you can loop through each part id
            // retrieve the message part and save the part to the 
            // file system
            foreach ($pids as $pid) {

                // $body contains the message part
                $body = $msg->getBody($mid, $pid);      

                // This variable contains the binary data
                // $body['message'];      

                // This variable contains the MIME type
                // $body['ftype'];

                // This variable contains the charset, if provided
                // $body['charset'];

                // This variable contains the orginal file name, if there is one
                // $body['fname'];

                // Write the file to the file system
                file_put_contents('The file name', $body['message']);
            }
        }

        // Mark the message for deletion
        $msg->delete($mid);
     }

     // Expunge deleted messages from the mailbox
     $msg->expunge();
  }
?>
HTH!

Regards,
Rich

--
[http://www.smilingsouls.net]
[http://pear.php.net/Mail_IMAP] A PHP/C-Client/PEAR solution for webmail
Reply With Quote
Reply


Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off
Trackbacks are Off
Pingbacks are On
Refbacks are Off

Similar Threads
Thread Thread Starter Forum Replies Last Post
Send mail and attachments with PHP mail function Lofa Beginning PHP 1 June 2nd, 2008 03:24 PM
Newbie help with imap on wamp with joomla MMS blog 123newbie PHP How-To 1 April 28th, 2007 04:06 AM
MIME attachments in PHP vibinkrish Beginning PHP 0 March 9th, 2006 06:45 AM
Imap _open() error vibinkrish PHP How-To 0 March 1st, 2006 04:31 AM
imap streaming error identification... bad_demon Pro PHP 3 December 5th, 2003 02:18 PM



All times are GMT -4. The time now is 08:43 AM.


Powered by vBulletin® Version 3.7.0
Copyright ©2000 - 2014, Jelsoft Enterprises Ltd.
© 2013 John Wiley & Sons, Inc.