PHP Code

Use Email Piping to Save Email Attachments

Back to the world of email piping. In my third instalment we’re going to look at saving attachments in emails sent to the piping script. If you need to get caught up, read through my and .

I thought this was going to be hard at first but it actually turned out to be straight forward.

Decode the Email

#!/usr/bin/php
ignore_syntax_errors = 1;
$parameters=array(
 'Data'=>$email,
);
 
$mime->Decode($parameters, $decoded);

You should be familiar with this from . We take the email and send it through the decoding class.

Find and Save the Attachments

//------------------------ ATTACHMENTS ------------------------------------//

//loop through email parts
foreach($decoded[0]['Parts'] as $part){
	
	//check for attachments
	if($part['FileDisposition'] == 'attachment'){
		
		//format file name (change spaces to underscore then remove anything that isn't a letter, number or underscore)
		$filename = preg_replace('/[^0-9,a-z,\.,_]*/i','',str_replace(' ','_', $part['FileName']));
		
		//write the data to the file
		$fp = fopen('save_dir/' . $filename, 'w');
		$written = fwrite($fp,$part['Body']);
		fclose($fp);
		
		//add file to attachments array
		$attachments[] = $part['FileName'];

	}

}

//print out the attachments for debug
print_r($attachments);

Here we loop through the parts array and look for any attachments. When we find one, we rewrite the filename to remove any bad characters, create a new file and write the data to the file. It’s pretty simple. Originally I thought I would have to decode the data as attachments are usually base64 encoded in emails, but for some reason the data is already decoded. I haven’t check but the decoding class may decode it already. If you look at all the fields in the $part array, you will see what the encode type is.

All my tests so far have worked. Let me know if you run into any issues. Next up, dealing with bounce emails.

Continue Reading

  • Ben

    Thank you, this is awesome, you just saved my day! Used this and the piepe tuts and got a nice little app working, keep up the good work

    • Skye

      Glad it helped you. Thanks for the comment.

  • Jeff

    Great work.

    I can get your part 2 script to work as long as I don’t have attachments. How do I ignore the attachments in that case? $body in that example returns exactly what I am looking for as long as I don’t have the attachements (it seems to fail in that case). that being said though, I still want to process the attachments too. I am having trouble getting the body from part 2 and for the attachments from part 3 to be processed into a clean set of variables at the same time.

    Do you have a single, merged script example that does a complete job of surfacing everything into a set of variables? headers, body, attachments etc.

    Thanks!

  • Jack

    Thank you very much for posting this! One thing that I’d like to share is that the save_dir path has to be the absolute path to the directory you want to save in (ie /var/www/save_dir). I was beating my head against the wall for a few minutes trying to figure out why it wouldn’t save to the path I specified.

    • Skye

      Yes. Absolute works best as you should place this script outside your web directory but you can always reference the directory relative to where the script is too.

  • Bill

    I’m unable to save the attachment(s) to a directory for further processing. Is that not what’s intended here? I’ve already chmod to 777 when I create it so it doesn’t seem to be a permission problem. I should note that I’m calling this script from an email pipe.

  • Skye

    Make sure the directory path for the save location is correct. For piping scripts I always use absolute path but the script is usually called from the home directory of the email user.

    Is your piping script outside of httpdocs?
    Is your save directory inside of httpdocs?

  • Bill

    I’m running them both in /var/www for testing and sudo just to make sure it’s not a permission issue.

    sudo /usr/bin/php /var/www/test.php < /var/mail/testemail.eml

  • This has been very helpful. I’m using it for an email marketing application.
    You should note that “FileDisposition” for attachments emailed from an iphone is “inline”

    • Same goes for attachment from Mac OS X Mail.

  • I found the (easy) way to read both images embedded into the body (not as attachments) and those attached as attachments.

  • Mike Thrussell

    Thanks for the very useful scripts, works great – however, I am experiencing an issue with larger file sizes on attachements – anything over ~10k doesn’t get uploaded. Any insights as to why would be much appreciated!

  • Mr. Dobalina

    Hey Great stuff,

    This is exactly what I’ve been looking for.

    As noted by Rocco above it states that “FileDisposition” for attachments emailed from an iphone is “inline”

    I imagine it’s the same or similar from a Mac. I see Content-Disposition: inline; when sending from the desktop.

    is this why I am seeing this?

    PHP_Notice:__Undefined_index:__FileDisposition_in_/var/www/dir/script.php_on_line_33

  • anaa

    Hi,
    Great tutorial but I’m unable to store attachements . I’ve already chmod to 777. Please help i really need to get it done asap !!!

    Thanks in advance …

    • Skye

      Do you get any errors? Turn on error reporting and you should get something to work with.

  • dev

    this is simply awesome code and explain very well

  • Mike Ross

    Hi,

    I’ve been using your script for a few years to handle emails and attachments. Howver, we’ve noticed that attachments sent from an iPhone/iPad don’t get parsed, they simply lost/ignored by the script. Why is this?

    Thank you!

  • Mike Ross

    Hi Guys,

    I was able to solve the issue with receiving iPhone/iPad attachments. Here’s my contribution to anyone who may be having the same problem. Under the attachments section, change the following:

    //check for attachments
    if($part[‘FileDisposition’] == ‘attachment’){

    to this:

    //check for attachments
    if($part[‘FileDisposition’] == ‘inline’ || $part[‘FileDisposition’] == ‘attachment’){

    That should do it. I’ve tested it and it seems to be working now.

  • evident

    You should use to prevent notivce. it may have several arrays and some without FileDisposition key.

    if(isset($part[‘FileDisposition’]) && $part[‘FileDisposition’] == ‘attachment’){

  • Mobin Mirza

    Hi,
    Hope you are doing well, I am not able to save the attachment when I am in bcc field.