Hi,
I have found script on internet that grab received emails from inbox and store some basic info (like subject, message, author of email...) into MySQL. That part works great, but I have problem with getting doubled message. So when I receive email, script grab message but somehow she do it twice. I spent last few days to figure out what could be a problem but I cant find it. Here is complete code that grab emails and store them into DB.
#!/usr/local/bin/php -q
<?php
class MIMEDECODE
{
var $_input;
var $_header;
var $_body;
var $_error;
var $_include_bodies;
var $_decode_bodies;
var $_decode_headers;
var $mailMimeDecode;
function MIMEDECODE($input)
{
list($header, $body) = $this->_split_body_header($input);
$this->_input = $input;
$this->_header = $header;
$this->_body = $body;
$this->_decode_bodies = true;
$this->_include_bodies = true;
$this->mailMimeDecode = true;
}
function decode($params = null)
{
// Have we been called statically? If so, create an object and pass details to that.
if (!isset($this->mailMimeDecode) AND isset($params['input'])) {
$obj = new MIMEDECODE($params['input']);
$structure = $obj->decode($params);
// Called statically but no input
} elseif (!isset($this->mailMimeDecode)) {
return $this->_error='Called statically and no input given';
// Called via an object
} else {
//$this->_include_bodies = isset($params['include_bodies']) ? $params['include_bodies'] : false;
//$this->_decode_bodies = isset($params['decode_bodies']) ? $params['decode_bodies'] : false;
//$this->_decode_headers = isset($params['decode_headers']) ? $params['decode_headers'] : false;
$structure = $this->_decode($this->_header, $this->_body);
if ($structure === false) {
$structure =$this->_error;
}
}
return $structure;
}
function _decode($headers, $body, $default_ctype = 'text/plain')
{
$return = new stdClass;
$headers = $this->_parseHeaders($headers);
foreach ($headers as $value) {
if (isset($return->headers[strtolower($value['name'])]) AND !is_array($return->headers[strtolower($value['name'])])) {
$return->headers[strtolower($value['name'])] = array($return->headers[strtolower($value['name'])]);
$return->headers[strtolower($value['name'])][] = $value['value'];
} elseif (isset($return->headers[strtolower($value['name'])])) {
$return->headers[strtolower($value['name'])][] = $value['value'];
} else {
$return->headers[strtolower($value['name'])] = $value['value'];
}
}
reset($headers);
while (list($key, $value) = each($headers)) {
$headers[$key]['name'] = strtolower($headers[$key]['name']);
switch ($headers[$key]['name']) {
case 'content-type':
$content_type = $this->_parseHeaderValue($headers[$key]['value']);
if (preg_match('/([0-9a-z+.-]+)\/([0-9a-z+.-]+)/i', $content_type['value'], $regs)) {
$return->ctype_primary = $regs[1];
$return->ctype_secondary = $regs[2];
}
if (isset($content_type['other'])) {
while (list($p_name, $p_value) = each($content_type['other'])) {
$return->ctype_parameters[$p_name] = $p_value;
}
}
break;
case 'content-disposition';
$content_disposition = $this->_parseHeaderValue($headers[$key]['value']);
$return->disposition = $content_disposition['value'];
if (isset($content_disposition['other'])) {
while (list($p_name, $p_value) = each($content_disposition['other'])) {
$return->d_parameters[$p_name] = $p_value;
}
}
break;
case 'content-transfer-encoding':
$content_transfer_encoding = $this->_parseHeaderValue($headers[$key]['value']);
break;
}
}
if (isset($content_type)) {
switch (trim(strtolower($content_type['value']))) {
case 'text/plain':
$encoding = isset($content_transfer_encoding) ? $content_transfer_encoding['value'] : '7bit';
$this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $encoding) : $body) : null;
break;
case 'text/html':
$encoding = isset($content_transfer_encoding) ? $content_transfer_encoding['value'] : '7bit';
$this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $encoding) : $body) : null;
break;
case 'multipart/parallel':
case 'multipart/report': // RFC1892
case 'multipart/signed': // PGP
case 'multipart/digest':
case 'multipart/alternative':
case 'multipart/related':
case 'multipart/mixed':
if(!isset($content_type['other']['boundary'])){
$this->_error = 'No boundary found for ' . $content_type['value'] . ' part';
return false;
}
$default_ctype = (strtolower($content_type['value']) === 'multipart/digest') ? 'message/rfc822' : 'text/plain';
$parts = $this->_boundarySplit($body, $content_type['other']['boundary']);
for ($i = 0; $i < count($parts); $i++) {
list($part_header, $part_body) = $this->_split_body_header($parts[$i]);
$part = $this->_decode($part_header, $part_body, $default_ctype);
if($part === false)
$part = $this->raiseError($this->_error);
$return->parts[] = $part;
}
break;
case 'message/rfc822':
$obj = &new MIMEDECODE($body);
$return->parts[] = $obj->decode(array('include_bodies' => $this->_include_bodies));
unset($obj);
break;
default:
if(!isset($content_transfer_encoding['value']))
$content_transfer_encoding['value'] = '7bit';
$this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $content_transfer_encoding['value']) : $body) : null;
break;
}
} else {
$ctype = explode('/', $default_ctype);
$return->ctype_primary = $ctype[0];
$return->ctype_secondary = $ctype[1];
$this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body) : $body) : null;
}
return $return;
}
/**
* Given the output of the above function, this will return an
* array of references to the parts, indexed by mime number.
*
* @param object $structure The structure to go through
* @param string $mime_number Internal use only.
* @return array Mime numbers
*/
function &getMimeNumbers(&$structure, $no_refs = false, $mime_number = '', $prepend = '')
{
$return = array();
if (!empty($structure->parts)) {
if ($mime_number != '') {
$structure->mime_id = $prepend . $mime_number;
$return[$prepend . $mime_number] = &$structure;
}
for ($i = 0; $i < count($structure->parts); $i++) {
if (!empty($structure->headers['content-type']) AND substr(strtolower($structure->headers['content-type']), 0, 8) == 'message/') {
$prepend = $prepend . $mime_number . '.';
$_mime_number = '';
} else {
$_mime_number = ($mime_number == '' ? $i + 1 : sprintf('%s.%s', $mime_number, $i + 1));
}
$arr = &MIMEDECODE::getMimeNumbers($structure->parts[$i], $no_refs, $_mime_number, $prepend);
foreach ($arr as $key => $val) {
$no_refs ? $return[$key] = '' : $return[$key] = &$arr[$key];
}
}
} else {
if ($mime_number == '') {
$mime_number = '1';
}
$structure->mime_id = $prepend . $mime_number;
$no_refs ? $return[$prepend . $mime_number] = '' : $return[$prepend . $mime_number] = &$structure;
}
return $return;
}
/**
* Given a string containing a header and body
* section, this function will split them (at the first
* blank line) and return them.
*
* @param string Input to split apart
* @return array Contains header and body section
* @access private
*/
function _split_body_header($input)
{
if (preg_match("/^(.*?)\r?\n\r?\n(.*)/s", $input, $match)) {
return array($match[1], $match[2]);
}
$this->_error = 'Could not split header and body';
return false;
}
/**
* Parse headers given in $input and return
* as assoc array.
*
* @param string Headers to parse
* @return array Contains parsed headers
* @access private
*/
function _parseHeaders($input)
{
if ($input !== '') {
// Unfold the input
$input = preg_replace("/\r?\n/", "\r\n", $input);
$input = preg_replace("/\r\n(\t| )+/", ' ', $input);
$headers = explode("\r\n", trim($input));
foreach ($headers as $value) {
$hdr_name = substr($value, 0, $pos = strpos($value, ':'));
$hdr_value = substr($value, $pos+1);
if($hdr_value[0] == ' ')
$hdr_value = substr($hdr_value, 1);
$return[] = array(
'name' => $hdr_name,
'value' => $this->_decode_headers ? $this->_decodeHeader($hdr_value) : $hdr_value
);
}
} else {
$return = array();
}
return $return;
}
/**
* Function to parse a header value,
* extract first part, and any secondary
* parts (after ;) This function is not as
* robust as it could be. Eg. header comments
* in the wrong place will probably break it.
*
* @param string Header value to parse
* @return array Contains parsed result
* @access private
*/
function _parseHeaderValue($input)
{
if (($pos = strpos($input, ';')) !== false) {
$return['value'] = trim(substr($input, 0, $pos));
$input = trim(substr($input, $pos+1));
if (strlen($input) > 0) {
// This splits on a semi-colon, if there's no preceeding backslash
// Can't handle if it's in double quotes however. (Of course anyone
// sending that needs a good slap).
$parameters = preg_split('/\s*(?<!\\\\);\s*/i', $input);
for ($i = 0; $i < count($parameters); $i++) {
$param_name = substr($parameters[$i], 0, $pos = strpos($parameters[$i], '='));
$param_value = substr($parameters[$i], $pos + 1);
if ($param_value[0] == '"') {
$param_value = substr($param_value, 1, -1);
}
$return['other'][$param_name] = $param_value;
$return['other'][strtolower($param_name)] = $param_value;
}
}
} else {
$return['value'] = trim($input);
}
return $return;
}
/**
* This function splits the input based
* on the given boundary
*
* @param string Input to parse
* @return array Contains array of resulting mime parts
* @access private
*/
function _boundarySplit($input, $boundary)
{
$boundary=trim($boundary);
if(substr($boundary,-1)=='"')
$boundary=substr_replace($boundary,"" ,-1 );
if(substr($boundary,0,1)=='"')
$boundary=substr_replace($boundary,"" ,0,1);
$boundary=trim($boundary);
$tmp = explode('--'.$boundary,$input);//boundary
for ($i=1; $i<count($tmp)-1; $i++) {
$parts[] = $tmp[$i];
}
return $parts;
}
/**
* Given a header, this function will decode it
* according to RFC2047. Probably not *exactly*
* conformant, but it does pass all the given
* examples (in RFC2047).
*
* @param string Input header value to decode
* @return string Decoded header value
* @access private
*/
function _decodeHeader($input)
{
// Remove white space between encoded-words
$input = preg_replace('/(=\?[^?]+\?(q|b)\?[^?]*\?=)(\s)+=\?/i', '\1=?', $input);
// For each encoded-word...
while (preg_match('/(=\?([^?]+)\?(q|b)\?([^?]*)\?=)/i', $input, $matches)) {
$encoded = $matches[1];
$charset = $matches[2];
$encoding = $matches[3];
$text = $matches[4];
switch (strtolower($encoding)) {
case 'b':
$text = base64_decode($text);
break;
case 'q':
$text = str_replace('_', ' ', $text);
preg_match_all('/=([a-f0-9]{2})/i', $text, $matches);
foreach($matches[1] as $value)
$text = str_replace('='.$value, chr(hexdec($value)), $text);
break;
}
$input = str_replace($encoded, $text, $input);
}
return $input;
}
/**
* Given a body string and an encoding type,
* this function will decode and return it.
*
* @param string Input body to decode
* @param string Encoding type to use.
* @return string Decoded body
* @access private
*/
function _decodeBody($input, $encoding = '7bit')
{
switch ($encoding) {
case '7bit':
return $input;
break;
case 'quoted-printable':
return $this->_quotedPrintableDecode($input);
break;
case 'base64':
return base64_decode($input);
break;
default:
return $input;
}
}
/**
* Given a quoted-printable string, this
* function will decode and return it.
*
* @param string Input body to decode
* @return string Decoded body
* @access private
*/
function _quotedPrintableDecode($input)
{
// Remove soft line breaks
$input = preg_replace("/=\r?\n/", '', $input);
// Replace encoded characters
$input = preg_replace('/=([a-f0-9]{2})/ie', "chr(hexdec('\\1'))", $input);
return $input;
}
////this function is used to get the parsed message or you shoud say fineal message
/////arguments [in]$object = object returned by get_message function defined above
/////arguments [in]$msg = a pointer to messgae
///// this function returns message string
function get_parsed_message()
{
$object=$this->decode();
/*$msg.="<b>To : </b>".$object->headers[to]."<br>";
$msg.="<b>From : </b>".$object->headers[from]."<br>";
$msg.="<b>Subject : </b>".$object->headers[subject]."<br>";
$msg.="<b>Date : </b>".$object->headers[date]."<br><br>";*/
$main_content_type=trim($object->ctype_primary)."/".trim($object->ctype_secondary);
//trim(strtok($object->headers['content-type'],";"));
$msg.=$this->walk(&$object,"",$main_content_type);
return $msg;
}
function walk($object,$msg="",$main_content_type="")
{
if(!isset($object->parts))
{
//$ctype=trim(strtok($object->headers['content-type'],";"));
$ctype=trim($object->ctype_primary)."/".trim($object->ctype_secondary);
switch($ctype)
{
case "text/html":
$msg.=$object->body;
break;
case "text/plain":
$enc=$object->headers['content-transfer-encoding'];
if($enc!="quoted-printable")
$msg.=nl2br($object->body);
break;
case "image/jpeg":
case "image/gif":
$name=trim($object->headers['name']);
$cid=trim($object->headers['content-id']);
$cid=str_replace("<","",$cid);
$cid=str_replace(">","",$cid);
if(empty($name))
trim(strtok($object->headers['content-type'],"="));
$name=trim(strtok("=\""));
$temp="pop3_temp/";
@mkdir($temp,777);
$tmpfile=$temp.$name;
//$tmpfile=realpath($tmpfile);
$fp=fopen($tmpfile,"w");
fwrite($fp,$object->body);
fclose($fp);
if($main_content_type=="multipart/mixed")
{
$msg.="<hr>";
$msg.="<a href='$tmpfile' target='_blank'>$name</a>";
$msg.="<hr>";
$msg.="<img src='$tmpfile'>";
}
$msg=str_replace("cid:$cid",$tmpfile,$msg);
//@unlink($tmpfile);
break;
default:
$name=trim($object->headers['name']);
if(empty($name))
trim(strtok($object->headers['content-type'],"="));
$name=trim(strtok("=\""));
$temp="pop3_temp/";
@mkdir($temp,777);
$tmpfile=$temp.$name;
//$tmpfile=realpath($tmpfile);
$fp=fopen($tmpfile,"w");
fwrite($fp,$object->body);
fclose($fp);
$msg.="<hr>";
$msg.="<a href='$tmpfile' target='_blank'>$name</a>";
break;
}
}
else
foreach($object->parts as $obj)
$this->walk($obj,&$msg,$main_content_type);
return $msg;
}
}
// read from stdin
$fd = fopen("php://stdin", "r");
$email = "";
while (!feof($fd)) {
$email .= fread($fd, 1024);
}
fclose($fd);
// handle email
$lines = explode("\n", $email);
// empty vars
$from = "";
$subject = "";
$headers = "";
$message = "";
$splittingheaders = true;
for ($i=0; $i < count($lines); $i++) {
if ($splittingheaders) {
// this is a header
$headers .= $lines[$i]."\n";
// look out for special headers
if (preg_match("/^Subject: (.*)/", $lines[$i], $matches)) {
$subject = $matches[1];
}
if (preg_match("/^From: (.*)/", $lines[$i], $matches)) {
$from = $matches[1];
}
} else {
// not a header, but message
$message .= $lines[$i]."\n";
}
if (trim($lines[$i])=="") {
// empty line, header section has ended
$splittingheaders = false;
}
}
//*** make a connection to the database: ***
$handle = mysql_connect("localhost","***********","***********") or die(mysql_error());
mysql_select_db("**********") or die(mysql_error());
//**********************************
$when = date("Y-m-d G:i:s");
$data = explode('@',$from);
$username = $data[0];
$mime = new MIMEDECODE($email);
$emessage = $mime->get_parsed_message();
mysql_query("INSERT INTO `support_email` (`subject`,`date`,`content`,`author`) VALUES ('".$subject."', '".$when."', '".$emessage."', '".$from."')") or die(mysql_error());
mysql_close($handle);
return NULL;
?>
Thank you for any help