Archive for mobile phone

PHP Script for parsing inbound email and saving attachments to a web host

Posted in Social Networking, Web Programming with tags , , , , , , on July 18, 2008 by webzealot

Ever wondered how BrightKite, TwitPic, and some of these other Web 2.0 websites have managed to reverse engineer email?  It used to be that websites would send YOU email, but that’s not all that exciting anymore.  Newsletters, notices, and alerts generated by web scripts are pretty common in even the most mundane of websites.

The trend recently has been to flip email on it’s head.  Now people can email (or send SMS text) to a specific address, and the contents of that message can be instantly parsed and posted on a website.  Particularly intriguing to me was the ability of a site like BrightKite or Flikr to save an attached image, more or less instantly, and have it post to the web.

That got me to wondering… How are they doing that?

As a long time ASP coder, I could see lots of trouble ahead.  In the world of Windows hosting, there are some very wide chasms between web services, and email services.  I asked around, looked high and low, and tinkered.  To make a long story short, I don’t think there is any way, or at least any good way, this functionality can be implemented through ASP.  I could be wrong about that, but I sure couldn’t find even a hint of a way to do it.  If I’m wrong, I would love if somebody could please respond and set the record straight.

What I did find is that this is NOT the case in the world of Unix and PHP!  In fact, PHP has many very nice tools to bridge the gap between email and web hosting… Many of these nifty toys can be found at the PHP.net website.  So, I dove in and started playing!

What I came up with was the following script.  To give proper credit, the main function (called “parsepart”) was originally written by some fellow in the UK named John… That’s all I know about him, and his script is posted on the PHP.net site along with others.  I did modify his script in a few spots, specifically in decoding the file name of any attachments.  But this script is a foundation for doing what TwitPic, Flikr, FaceBook, and others are doing.  I looked and played long and hard to come up with this… I hope somebody else can benefit from it as well…  Also, this doesn’t handle SMS, just email.  SMS is a task for another day!

<?php
//script will fetch an email identified by $msgid, and parse its parts into an
//array $partsarray
//structure of array:
//$partsarray[<name of part>][<attachment/text>]
//if attachment- subarray is [filename][binary data]
//if text- subarray is [type of text(HTML/PLAIN)][text string]

//i.e.
//$partsarray[1][attachment][filename]=filename of attachment in part 3.1
//$partsarray[1][attachment][binary]=binary data of attachment in part 3.1
//$partsarray[2][text][type]=type of text in part 2
//$partsarray[2][text][string]=decoded text string in part 2
//$partsarray[not multipart][text][string]=decoded text string in message that isn’t multipart

function parsepart($p,$i){
global $mbox,$msgid,$partsarray;
//where to write file attachments to:
$filestore = ‘[full/path/to/attachment/store/(chmod777)]’;

//fetch part
$part=imap_fetchbody($mbox,$msgid,$i);
//if type is not text
if ($p->type!=0){
//DECODE PART
//decode if base64
if ($p->encoding==3)$part=base64_decode($part);
//decode if quoted printable
if ($p->encoding==4)$part=quoted_printable_decode($part);
//no need to decode binary or 8bit!

//get filename of attachment if present
$filename=”;
// if there are any dparameters present in this part
if (count($p->dparameters)>0){
foreach ($p->dparameters as $dparam){
if ((strtoupper($dparam->attribute)==’NAME’) ||(strtoupper($dparam->attribute)==’FILENAME’)) $filename=$dparam->value;
}
}
//if no filename found
if ($filename==”){
// if there are any parameters present in this part
if (count($p->parameters)>0){
foreach ($p->parameters as $param){
if ((strtoupper($param->attribute)==’NAME’) ||(strtoupper($param->attribute)==’FILENAME’)) $filename=$param->value;
}
}
}
//write to disk and set partsarray variable
if ($filename!=”){
$tempfilename = imap_mime_header_decode($filename);

}
$partsarray[$i][attachment] = array(‘filename’=>$filename,’binary’=>$part);
$fp=fopen($filestore.$filename,”w+”);
fwrite($fp,$part);
fclose($fp);
}
//end if type!=0
}

//if part is text
else if($p->type==0){
//decode text
//if QUOTED-PRINTABLE
if ($p->encoding==4) $part=quoted_printable_decode($part);
//if base 64
if ($p->encoding==3) $part=base64_decode($part);

//OPTIONAL PROCESSING e.g. nl2br for plain text
//if plain text

if (strtoupper($p->subtype)==’PLAIN’)1;
//if HTML
else if (strtoupper($p->subtype)==’HTML’)1;
$partsarray[$i][text] = array(‘type’=>$p->subtype,’string’=>$part);
}

//if subparts… recurse into function and parse them too!
if (count($p->parts)>0){
foreach ($p->parts as $pno=>$parr){
parsepart($parr,($i.’.’.($pno+1)));
}
}
return;
}

//Open the connection to IMAP server
$mbox = imap_open(“{your.emailserver.com}”, “your@address.com”, “password”)
or die(“can’t connect: ” . imap_last_error());

$status = @imap_status($mbox, “{your.emailserver.com}INBOX”, SA_ALL);

//$message_to_read = imap_uid($mbox, $status->uidnext – 1);
$msgid = $status->messages;

if ($msgid == 0) {
die(“No messages in inbox.”);
}

//fetch structure of message
$s=imap_fetchstructure($mbox,$msgid);

//see if there are any parts
if (count($s->parts)>0){
foreach ($s->parts as $partno=>$partarr){
//parse parts of email
parsepart($partarr,$partno+1);
}
}

//for not multipart messages
else{
//get body of message
$text=imap_body($mbox,$msgid);
//decode if quoted-printable
if ($s->encoding==4) $text=quoted_printable_decode($text);
//OPTIONAL PROCESSING
if (strtoupper($s->subtype)==’PLAIN’) $text=$text;
if (strtoupper($s->subtype)==’HTML’) $text=$text;

$partsarray[‘not multipart’][text]=array(‘type’=>$s->subtype,’string’=>$text);
}

$header = imap_fetchheader($mbox,$msgid);
$obj = imap_rfc822_parse_headers($header);

//And heres the header info… Store it in a database, display it, whatever you like…

echo “Subject: ” . $obj->subject . “<br>”;
echo “From: ” . $obj->reply_toaddress . “<br>”;
echo “Plain Text: ” . $partsarray[‘not multipart’][‘text’][‘string’] . “<br>”;
echo “Text: ” . $partsarray[1][‘text’][‘string’] . “<br>”;
echo “Attachment file name: ” . $partsarray[1][‘attachment’][‘filename’] . “<br>”;

//here you can respond back with a confirmation email to the sender…
$headers = “From: fromaddress@email.com”;
imap_mail ($obj->reply_toaddress,”Thanks for posting!”,”Your message was received!”,$headers);

//here we delete the message once parsed, clean out the mailbox, and close it all up…
imap_delete($mbox, $msgid);
imap_expunge($mbox);
imap_close($mbox);
?>

I’ll be the first to admit I’m a bit of a PHP hack, and there may be better ways of doing this… But this script, in fewer than 150 lines, seems to do the trick!