diff --git a/Make_statuscodes.php b/Make_statuscodes.php index ef16be8..b3ac8c9 100644 --- a/Make_statuscodes.php +++ b/Make_statuscodes.php @@ -1,34 +1,61 @@ 1, + CURLOPT_URL => $csv_url_1, + CURLOPT_USERAGENT => 'PHP-Bounce-Handler (Make_statuscodes.php)' +)); +$csv_raw_content = curl_exec($ch); +curl_close($ch); -$fh = fopen("http://www.iana.org/assignments/smtp-enhanced-status-codes/smtp-enhanced-status-codes-1.csv", "r"); +$file1 = fopen($file1_name,'w'); +fwrite($file1,$csv_raw_content); +fclose($file1); + +$fh = fopen($file1_name, 'r'); if ($fh !== FALSE) { $a = fgets($fh); # 1st line is titles while (!feof($fh)) { $a = fgetcsv($fh, 0, ',', '"'); - $a[0] = preg_replace('/\..*/', '', $a[0]); # 5.x.x -> 5 - $a[1] = preg_replace('/\n\s*/', ' ', $a[1]); #remove line breaks - $a[1] = preg_replace('/\s\s+/', ' ', $a[1]); #remove double spaces - $a[1] = preg_replace('/"/', '\\"', $a[1]); #requote quotes - $a[2] = preg_replace('/\n\s*/', ' ', $a[2]); #remove line breaks - $a[2] = preg_replace('/\s\s+/', ' ', $a[2]); #remove double spaces - $a[2] = preg_replace('/"/', '\\"', $a[2]); #requote quotes - print "\$status_code_classes['$a[0]']['title'] = \"". $a[1]. "\"; # $a[3]\n"; - print "\$status_code_classes['$a[0]']['descr'] = \"". $a[2]. "\";\n"; + if ($a[0]) { + $a[0] = preg_replace('/\..*/', '', $a[0]); # 5.x.x -> 5 + $a[1] = preg_replace('/\n\s*/', ' ', $a[1]); #remove line breaks + $a[1] = preg_replace('/\s\s+/', ' ', $a[1]); #remove double spaces + $a[1] = preg_replace('/"/', '\\"', $a[1]); #requote quotes + $a[2] = preg_replace('/\n\s*/', ' ', $a[2]); #remove line breaks + $a[2] = preg_replace('/\s\s+/', ' ', $a[2]); #remove double spaces + $a[2] = preg_replace('/"/', '\\"', $a[2]); #requote quotes + print "\$status_code_classes['$a[0]']['title'] = \"". $a[1]. "\"; # $a[3]\n"; + print "\$status_code_classes['$a[0]']['descr'] = \"". $a[2]. "\";\n"; + } } fclose ($fh); } print "\n"; - #X.7.17,Mailbox owner has changed,5XX,"This status code is returned when a message is #received with a Require-Recipient-Valid-Since #field or RRVS extension and the receiving @@ -36,24 +63,43 @@ #recipient mailbox has not been under #continuous ownership since the specified date.",[RFC-ietf-appsawg-rrvs-header-field-10] (Standards Track),M. Kucherawy,IESG +$ch = curl_init(); +curl_setopt_array($ch, array( + CURLOPT_RETURNTRANSFER => 1, + CURLOPT_URL => $csv_url_2, + CURLOPT_USERAGENT => 'PHP-Bounce-Handler (Make_statuscodes.php)' +)); +$csv_raw_content = curl_exec($ch); +curl_close($ch); +$file2 = fopen($file2_name,'w'); +fwrite($file2,$csv_raw_content); +fclose($file2); -$fh = fopen("http://www.iana.org/assignments/smtp-enhanced-status-codes/smtp-enhanced-status-codes-3.csv", "r"); +$fh = fopen($file2_name, 'r'); if ($fh !== FALSE) { $a = fgets($fh); # 1st line is titles while (!feof($fh)) { $a = fgetcsv($fh, 0, ',', '"'); - $a[0] = preg_replace('/^X./i', '', $a[0]); #X.5.0 -> 5.0 - $a[1] = preg_replace('/\n\s*/', ' ', $a[1]); #remove line breaks - $a[1] = preg_replace('/\s\s+/', ' ', $a[1]); #remove double spaces - $a[1] = preg_replace('/"/', '\\"', $a[1]); #requote quotes - $a[3] = preg_replace('/\n\s*/', ' ', $a[3]); #remove line breaks - $a[3] = preg_replace('/\s\s+/', ' ', $a[3]); #remove double spaces - $a[3] = preg_replace('/"/', '\\"', $a[3]); #requote quotes - print "\$status_code_subclasses['$a[0]']['title'] = \"". $a[1]. "\"; # $a[4]\n"; - print "\$status_code_subclasses['$a[0]']['descr'] = \"". $a[3]. "\";\n"; + if ($a[0]) { + $a[0] = preg_replace('/^X./i', '', $a[0]); #X.5.0 -> 5.0 + $a[1] = preg_replace('/\n\s*/', ' ', $a[1]); #remove line breaks + $a[1] = preg_replace('/\s\s+/', ' ', $a[1]); #remove double spaces + $a[1] = preg_replace('/"/', '\\"', $a[1]); #requote quotes + $a[3] = preg_replace('/\n\s*/', ' ', $a[3]); #remove line breaks + $a[3] = preg_replace('/\s\s+/', ' ', $a[3]); #remove double spaces + $a[3] = preg_replace('/"/', '\\"', $a[3]); #requote quotes + print "\$status_code_subclasses['$a[0]']['title'] = \"". $a[1]. "\"; # $a[4]\n"; + print "\$status_code_subclasses['$a[0]']['descr'] = \"". $a[3]. "\";\n"; + } } fclose ($fh); } print "\n\n?>"; +# Unless -k (keep flap is passed) +if (!array_key_exists('k',$options)) { + unlink($file1_name); + unlink($file2_name); +} + ?> diff --git a/README.txt b/README.txt index 1cf6991..c14dda0 100644 --- a/README.txt +++ b/README.txt @@ -3,17 +3,41 @@ PHP Bounce Handler INSTALL ------- Upload to a website, and open testdriver1.php in a web browser -for normal operation only - bounce_driver_class.php and bounce_statuscodes.php are required RELEASE HISTORY --------------- -v7.4 SB June 19, 2014 +v7.5.1 WP Jan 14, 2025 + - PHP7/8 php moderizations + +v7.5.0 WP May 18, 2019 + - Switch to curl from fopen. + - Add -k (keep flag) for optionally keeping the original csv files. + +v7.4.3 SB Dec 2014 + - add a softlink for rfc1893.error.codes.php -> bounce_statuscodes.php + - fix a fatal error with format_status_code returning the wrong datatype + - remove zip from distro (github will do that automagically) + - more autoresponse catchers + - handler broken MTAs better + +v7.4.2 SB Oct 24, 2014 + - slightly less agressive bounce detection; only calls last chance if we + see specific from/return-path + - added a couple more autoreponder subject line checks + - allows it to find email addresses new TLDs that are more than 4 chararters + - more supression for php notifications on undefined data + +v7.4.1 SB Oct 22, 2014 +- fix autoresponder detection. A lot of bounces were incorrectly being + caught as autoresponders +- added vacation/autoresponder examples +- minor fix in command line test tool +v7.4 SB June 19, 2014 - make auto-responder identification table driven -- make bounce_statuscodes.php (prev rfc1893_status_codes.php) generated from IANA list +- make bounce_statuscodes.php (prev rfc1893_status_codes.php) generated from IANA list php Make_statuscodes.php >bounce_statuscodes.php - allow for rfc status codes with 2 digits in the 3rd paramater - more supression for php notifications on undefined data @@ -26,7 +50,7 @@ v7.4 SB June 19, 2014 v7.3 CF July 4, 2013 - Replaced deprecated split() function. -- Added auto-responder identification filter. +- Added auto-responder identification filter. - Suppressed php Notice errors. diff --git a/bounce_driver.class.php b/bounce_driver.class.php index d9a140b..0273d12 100644 --- a/bounce_driver.class.php +++ b/bounce_driver.class.php @@ -1,7 +1,7 @@ @@ -64,7 +64,7 @@ class BounceHandler{ public $feedback_type = ""; public $x_header_beacon_1 = ""; public $x_header_beacon_2 = ""; - + // these accessors are useful only for FBL's // or if the output array has only one index public $action = ""; @@ -75,18 +75,15 @@ class BounceHandler{ // the raw data set, a multiArray public $output = array(); - + /**** INSTANTIATION *******************************************************/ public function __construct(){ $this->output[0]['action'] = ""; $this->output[0]['status'] = ""; $this->output[0]['recipient'] = ""; - require('bounce_responses.php'); - $this->bouncelist = $bouncelist; - $this->autorespondlist = $autorespondlist; - $this->bouncesubj = $bouncesubj; + require('bounce_responses.php'); # populates bouncelist, autorespondlist, bouncesubj } - + /**** METHODS *************************************************************/ // this is the most commonly used public method @@ -98,7 +95,7 @@ public function parse_email($eml){ public function get_the_facts($eml){ // fluff up the email $bounce = $this->init_bouncehandler($eml); - if (strpos($bounce, "\r\n\r\n") !== FALSE) + if (strpos($bounce, "\r\n\r\n") !== FALSE) list($head, $body) = preg_split("/\r\n\r\n/", $bounce, 2); else list($head, $body) = array($bounce, ''); @@ -107,27 +104,17 @@ public function get_the_facts($eml){ // parse the email into data structures $boundary = isset($this->head_hash['Content-type']['boundary']) ? $this->head_hash['Content-type']['boundary'] : ''; $mime_sections = $this->parse_body_into_mime_sections($body, $boundary); - $this->body_hash = split("\r\n", $body); + $this->body_hash = explode("\r\n", $body); $this->first_body_hash = isset($mime_sections['first_body_part']) ? $this->parse_head($mime_sections['first_body_part']) : array(); - - $this->looks_like_a_bounce = $this->is_a_bounce(); - $this->looks_like_an_FBL = $this->is_an_ARF(); - $this->looks_like_an_autoresponse = !$this->looks_like_a_bounce && !$this->looks_like_an_FBL && $this->is_an_autoresponse(); - - /* If you are trying to save processing power, and don't care much - * about accuracy then uncomment this statement in order to skip the - * heroic text parsing below. - */ - //if(!$this->looks_like_a_bounce && !$this->looks_like_an_FBL && !$this->looks_like_an_autoresponse){ - // return "unknown"; - //} + $this->looks_like_a_bounce = $this->is_RFC1892_multipart_report() || $this->is_a_bounce(); + $this->looks_like_an_autoresponse = $this->is_an_autoresponse(); /*** now we try all our weird text parsing methods (E-mail is weird!) ******************************/ - // is it a Feedback Loop, in Abuse Feedback Reporting Format (ARF)? // http://en.wikipedia.org/wiki/Abuse_Reporting_Format#Abuse_Feedback_Reporting_Format_.28ARF.29 - if($this->looks_like_an_FBL){ + if($this->is_an_ARF()) { + $this->looks_like_an_FBL = TRUE; $this->output[0]['action'] = 'failed'; $this->output[0]['status'] = "5.7.1"; $this->subject = trim(str_ireplace("Fw:", "", $this->head_hash['Subject'])); @@ -142,7 +129,7 @@ public function get_the_facts($eml){ $this->fbl_hash['User-agent'] = 'Hotmail FBL'; if (isset($this->first_body_hash['Date'])) $this->fbl_hash['Received-date'] = $this->first_body_hash['Date']; - if (isset($this->head_hash['Subject']) && preg_match('/complaint about message from ([0-9.]+)/', $this->head_hash['Subject'], $matches)) + if (isset($this->head_hash['Subject']) && preg_match('/complaint about message from ([0-9.]+)/', $this->head_hash['Subject'], $matches)) $this->fbl_hash['Source-ip'] = $matches[1]; if (!empty($this->recipient)) $this->fbl_hash['Original-rcpt-to'] = $this->recipient; @@ -180,29 +167,22 @@ public function get_the_facts($eml){ $this->fbl_hash['Original-mail-from'] = $this->strip_angle_brackets(@$this->fbl_hash['Original-mail-from']); $this->fbl_hash['Original-rcpt-to'] = $this->strip_angle_brackets(@$this->fbl_hash['Original-rcpt-to']); $this->output[0]['recipient'] = $this->fbl_hash['Original-rcpt-to']; - } + } // is FBL -#??? else if (preg_match("/auto.{0,20}reply|vacation|(out|away|on holiday).*office/i", $this->head_hash['Subject'])){ -# // looks like a vacation autoreply, ignoring -# $this->output[0]['action'] = 'autoreply'; -# } - - // is this an autoresponse ? elseif ($this->looks_like_an_autoresponse) { - $this->output[0]['action'] = 'autoresponse'; #??? 'transient' 'autoreply' ?? - $this->output[0]['autoresponse'] = $this->autoresponse; #??? 4.3.2 + $this->output[0]['action'] = 'autoresponse'; + $this->output[0]['autoresponse'] = $this->autoresponse; // grab the first recipient and break $this->output[0]['recipient'] = isset($this->head_hash['Return-path']) ? $this->strip_angle_brackets($this->head_hash['Return-path']) : ''; if(empty($this->output[0]['recipient'])){ $arrFailed = $this->find_email_addresses($body); for($j=0; $joutput[$j]['recipient'] = trim($arrFailed[$j]); - break; + break; } } } - - else if ($this->is_RFC1892_multipart_report() === TRUE){ + elseif ($this->is_RFC1892_multipart_report() === TRUE){ $rpt_hash = $this->parse_machine_parsable_body_part($mime_sections['machine_parsable_body_part']); if (isset($rpt_hash['per_recipient'])) { for($i=0; $ioutput[$j]['status'] = $this->get_status_code_from_text($this->output[$j]['recipient'],0); $this->output[$j]['action'] = $this->get_action_from_status_code($this->output[$j]['status']); } - } + } } - - else if(isset($this->head_hash['X-failed-recipients'])) { + elseif (isset($this->head_hash['X-failed-recipients'])) { // Busted Exim MTA // Up to 50 email addresses can be listed on each header. // There can be multiple X-Failed-Recipients: headers. - (not supported) - $arrFailed = split(',', $this->head_hash['X-failed-recipients']); + $arrFailed = explode(',', $this->head_hash['X-failed-recipients']); for($j=0; $joutput[$j]['recipient'] = trim($arrFailed[$j]); $this->output[$j]['status'] = $this->get_status_code_from_text($this->output[$j]['recipient'],0); $this->output[$j]['action'] = $this->get_action_from_status_code($this->output[$j]['status']); + $this->looks_like_a_bounce = TRUE; } } - - else if(!empty($boundary) && $this->looks_like_a_bounce){ + elseif ($this->looks_like_a_bounce) { // if it had signs of being a bounce + if (!empty($boundary)) { // oh god it could be anything, but at least it has mime parts, so let's try anyway - $arrFailed = $this->find_email_addresses($mime_sections['first_body_part']); - for($j=0; $joutput[$j]['recipient'] = trim($arrFailed[$j]); - $this->output[$j]['status'] = $this->get_status_code_from_text($this->output[$j]['recipient'],0); - $this->output[$j]['action'] = $this->get_action_from_status_code($this->output[$j]['status']); + $arrFailed = $this->find_email_addresses($mime_sections['first_body_part']); + for($j=0; $joutput[$j]['recipient'] = trim($arrFailed[$j]); + $this->output[$j]['status'] = $this->get_status_code_from_text($this->output[$j]['recipient'],0); + $this->output[$j]['action'] = $this->get_action_from_status_code($this->output[$j]['status']); + $this->looks_like_a_bounce = TRUE; + } } - } - - else if($this->looks_like_a_bounce){ - // last ditch attempt - // could possibly produce erroneous output, or be very resource consuming, - // so be careful. You should comment out this section if you are very concerned - // about 100% accuracy or if you want very fast performance. - // Leave it turned on if you know that all messages to be analyzed are bounces. - $arrFailed = $this->find_email_addresses($body); - for($j=0; $joutput[$j]['recipient'] = trim($arrFailed[$j]); - $this->output[$j]['status'] = $this->get_status_code_from_text($this->output[$j]['recipient'],0); - $this->output[$j]['action'] = $this->get_action_from_status_code($this->output[$j]['status']); + else { + // last ditch attempt + // could possibly produce erroneous output, or be very resource consuming, + // so be careful. You should comment out this section if you are very concerned + // about 100% accuracy or if you want very fast performance. + // Leave it turned on if you know that all messages to be analyzed are bounces. + $arrFailed = $this->find_email_addresses($body); + for ($j=0; $joutput[$j]['recipient'] = trim($arrFailed[$j]); + $this->output[$j]['status'] = $this->get_status_code_from_text($this->output[$j]['recipient'],0); + $this->output[$j]['action'] = $this->get_action_from_status_code($this->output[$j]['status']); + $this->looks_like_a_bounce = TRUE; + } } } + // else if()..... add a parser for your busted-ass MTA here - + + // remove empty array indices $tmp = array(); foreach($this->output as $arr){ @@ -278,7 +262,6 @@ public function get_the_facts($eml){ $this->subject = ($this->subject) ? $this->subject : $this->head_hash['Subject']; $this->recipient = isset($this->output[0]['recipient']) ? $this->output[0]['recipient'] : ''; $this->feedback_type = (isset($this->fbl_hash['Feedback-type'])) ? $this->fbl_hash['Feedback-type'] : ""; - // sniff out any web beacons if($this->web_beacon_preg_1) $this->web_beacon_1 = $this->find_web_beacon($body, $this->web_beacon_preg_1); @@ -288,16 +271,15 @@ public function get_the_facts($eml){ $this->x_header_beacon_1 = $this->find_x_header ($this->x_header_search_1); if($this->x_header_search_2) $this->x_header_beacon_2 = $this->find_x_header ($this->x_header_search_2); - return $this->output; } - + function init_bouncehandler($blob, $format='string'){ $this->head_hash = array(); $this->fbl_hash = array(); - $this->body_hash = array(); + $this->body_hash = array(); $this->looks_like_a_bounce = false; $this->looks_like_an_FBL = false; $this->is_hotmail_fbl = false; @@ -330,18 +312,6 @@ function init_bouncehandler($blob, $format='string'){ $strEmail = str_replace("\r\n", "\n", $blob); // line returns 1 $strEmail = str_replace("\n", "\r\n", $strEmail);// line returns 2 -# $strEmail = str_replace("=\r\n", "", $strEmail); // remove MIME line breaks (would never exist as #1 above would have dealt with) -# $strEmail = str_replace("=3D", "=", $strEmail); // equals sign - dealt with in the MIME decode section now -# $strEmail = str_replace("=09", " ", $strEmail); // tabs - - //} - //else if($format=='array'){ - // $strEmail = ""; - // for($i=0; $i<$blob; $i++){ - // $strEmail .= rtrim($blob[$i]) . "\r\n"; - // } - //} - return $strEmail; } @@ -354,7 +324,7 @@ function get_status_code_from_text($recipient, $index){ //skip Message-ID lines if (stripos($line, 'Message-ID') !== FALSE) continue; - + /******** recurse into the email if you find the recipient ********/ if(stristr($line, $recipient)!==FALSE){ // the status code MIGHT be in the next few lines after the recipient line, @@ -377,7 +347,7 @@ function get_status_code_from_text($recipient, $index){ && strstr($line, 'FROM:<') === FALSE) { // Kanon added this line because Hotmail puts the e-mail address too soon and there actually is error message stuff after it. break; } - + //******** pattern matching ********/ foreach ($this->bouncelist as $bouncetext => $bouncecode) { if (preg_match("/$bouncetext/i", $line, $matches)) @@ -394,7 +364,7 @@ function get_status_code_from_text($recipient, $index){ } // search for RFC2821 return code - // thanks to mark.tolman@gmail.com + // thanks to mark.tolman@gmail.com // Maybe at some point it should have it's own place within the main parsing scheme (at line 88) if (preg_match('/\]?: ([45][01257][012345]) /', $line, $matches) || preg_match('/^([45][01257][012345]) (?:.*?)(?:denied|inactive|deactivated|rejected|disabled|unknown|no such|not (?:our|activated|a valid))+/i', $line, $matches)) @@ -418,16 +388,16 @@ function get_status_code_from_text($recipient, $index){ function is_RFC1892_multipart_report(){ return @$this->head_hash['Content-type']['type']=='multipart/report' && @$this->head_hash['Content-type']['report-type']=='delivery-status' - && @$this->head_hash['Content-type'][boundary]!==''; + && @$this->head_hash['Content-type']['boundary']!==''; } function parse_head($headers){ - if(!is_array($headers)) + if(!is_array($headers)) $headers = explode("\r\n", $headers); $hash = $this->standard_parser($headers); if(isset($hash['Content-type'])) {//preg_match('/Multipart\/Report/i', $hash['Content-type'])){ $multipart_report = explode (';', $hash['Content-type']); - $hash['Content-type']=''; + $hash['Content-type']=[]; $hash['Content-type']['type'] = strtolower($multipart_report[0]); foreach($multipart_report as $mr){ if(preg_match('/([^=.]*?)=(.*)/i', $mr, $matches)){ @@ -455,7 +425,14 @@ function contenttype_decode ($mimepart) { $line = substr($line, 0, -1); else $line .= "\r\n"; - $decoded .= preg_replace("/=([0-9A-F][0-9A-F])/e", 'chr(hexdec("$1"))', $line); + ## $decoded .= preg_replace("/=([0-9A-F][0-9A-F])/e", 'chr(hexdec("$1"))', $line); + $decoded .= preg_replace_callback( + "/=([0-9A-F][0-9A-F])/", + function($matches){ + chr(hexdec($matches[0])); + }, + $line + ); } case 'base64': { $decoded .= base64_decode($line); @@ -470,7 +447,7 @@ function contenttype_decode ($mimepart) { function parse_body_into_mime_sections($body, $boundary){ if(!$boundary) return array(); - if (is_array($body)) + if (is_array($body)) $body = implode("\r\n", $body); $body = explode($boundary, $body); $mime_sections['first_body_part'] = isset($body[1]) ? $this->contenttype_decode($body[1]) : ''; #proper MIME decode @@ -502,16 +479,16 @@ function standard_parser($content){ // associative array orstr } } } - elseif (isset($line) && isset($entity) && preg_match('/^\s+(.+)\s*/', $line) && $entity) { + elseif (isset($line) && isset($entity) && preg_match('/^\s+(.+)\s*/', $line, $array) && $entity) { $line = trim($line); - if (strpos($array[2], '=?') !== FALSE) - $line = iconv_mime_decode($array[2], ICONV_MIME_DECODE_CONTINUE_ON_ERROR, "UTF-8"); + if (strpos($array[1], '=?') !== FALSE) + $line = iconv_mime_decode($array[1], ICONV_MIME_DECODE_CONTINUE_ON_ERROR, "UTF-8"); $hash[$entity] .= ' '. $line; } } // special formatting $hash['Received']= @explode('|', $hash['Received']); - $hash['Subject'] = isset($hash['Subject']) ? : ''; + $hash['Subject'] = isset($hash['Subject']) ? $hash['Subject'] : ''; return $hash; } @@ -522,13 +499,13 @@ function parse_machine_parsable_body_part($str){ $hash['per_message'] = isset($hash['per_message']) ? $this->standard_parser($hash['per_message']) : array(); if(isset($hash['per_message']['X-postfix-sender'])){ $arr = explode (';', $hash['per_message']['X-postfix-sender']); - $hash['per_message']['X-postfix-sender']=''; + $hash['per_message']['X-postfix-sender']=[]; $hash['per_message']['X-postfix-sender']['type'] = @trim($arr[0]); $hash['per_message']['X-postfix-sender']['addr'] = @trim($arr[1]); } if(isset($hash['per_message']['Reporting-mta'])){ $arr = explode (';', $hash['per_message']['Reporting-mta']); - $hash['per_message']['Reporting-mta']=''; + $hash['per_message']['Reporting-mta']=[]; $hash['per_message']['Reporting-mta']['type'] = @trim($arr[0]); $hash['per_message']['Reporting-mta']['addr'] = @trim($arr[1]); } @@ -595,12 +572,12 @@ function find_recipient($per_rcpt){ } function find_type(){ - if($this->looks_like_a_bounce) - return "bounce"; + if ($this->looks_like_an_autoresponse) + return "autoresponse"; elseif ($this->looks_like_an_FBL) return "fbl"; - elseif ($this->looks_like_an_autoresponse) - return "autoresponse"; + elseif($this->looks_like_a_bounce) + return "bounce"; else return false; } @@ -631,7 +608,7 @@ function parse_dsn_fields($dsn_fields){ // Take a line like "4.2.12 This is an error" and return "4.2.12" and "This is an error" function format_status_code($code) { - $ret = ""; + $ret = []; if(preg_match('/([245]\.[01234567]\.\d{1,2})\s*(.*)/', $code, $matches)) { $ret['code'] = $matches[1]; $ret['text'] = $matches[2]; @@ -652,7 +629,7 @@ function fetch_status_messages($code){ } function get_action_from_status_code($code){ - if($code=='') + if($code=='') return ''; $ret = $this->format_status_code($code); switch (isset($ret['code']) ? $ret['code'][0] : '') { @@ -680,20 +657,20 @@ function decode_diagnostic_code($dcode){ } } - function is_a_bounce(){ + function is_a_bounce() { + if (isset($this->head_hash['From']) && preg_match("/^(postmaster|mailer-daemon)\@?/i", $this->head_hash['From'])) + return true; foreach ($this->bouncesubj as $s) if (preg_match("/^$s/i", $this->head_hash['Subject'])) return true; - #if(@preg_match('/auto_reply/',$this->head_hash['Precedence'])) return true; # autoresponse, not bounce - if (isset($this->head_hash['From']) && - preg_match("/^(postmaster|mailer-daemon)\@?/i", $this->head_hash['From'])) - return true; + #if (isset($this->head_hash['Return-path']) && $this->head_hash['Return-path'] === '<>') + # return true; return false; } - + function find_email_addresses($first_body_part){ // not finished yet. This finds only one address. - if (preg_match("/\b([A-Z0-9._%-]+@[A-Z0-9.-]+\.[A-Z]{2,4})\b/i", $first_body_part, $matches)){ + if (preg_match("/\b([A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4})\b/i", $first_body_part, $matches)){ return array($matches[1]); } else @@ -703,9 +680,9 @@ function find_email_addresses($first_body_part){ // these functions are for feedback loops function is_an_ARF(){ - if(isset($this->head_hash['Content-type']['report-type']) && preg_match('/feedback-report/',$this->head_hash['Content-type']['report-type'])) + if(isset($this->head_hash['Content-type']['report-type']) && preg_match('/feedback-report/',$this->head_hash['Content-type']['report-type'])) return true; - if(isset($this->head_hash['X-loop']) && preg_match('/scomp/',$this->head_hash['X-loop'])) + if(isset($this->head_hash['X-loop']) && preg_match('/scomp/',$this->head_hash['X-loop'])) return true; if(isset($this->head_hash['X-hmxmroriginalrecipient'])) { $this->is_hotmail_fbl = TRUE; @@ -719,34 +696,32 @@ function is_an_ARF(){ } return false; } - + // look for common auto-responders function is_an_autoresponse() { - foreach (array ('Auto-submitted', 'X-autorespond') as $a) { - if (isset($this->head_hash[$a])) { - $this->autoresponse = "$a: ". $this->head_hash[$a]; + if (isset($this->head_hash['Auto-submitted'])) { + if (preg_match('/auto-notified|vacation|away/i', $this->head_hash['Auto-submitted'])) { + $this->autoresponse = "Auto-submitted: ". $this->head_hash['Auto-submitted']; return TRUE; } } - foreach (array ('Precedence', 'X-precedence') as $a) { - if (isset($this->head_hash[$a]) && preg_match('/^(auto|junk)/i', $this->head_hash[$a])) { - $this->autoresponse = "$a: ". $this->head_hash[$a]; - return TRUE; - } + if (isset($this->head_hash['Precedence']) && preg_match('/^auto_reply/i', $this->head_hash['Precedence'])) { + $this->autoresponse = "Precedence: ". $this->head_hash['Precedence']; + return TRUE; } - - $subj = isset($this->head_hash['Subject']) ? $this->head_hash['Subject'] : ''; - foreach ($this->autorespondlist as $a) { - if (preg_match("/$a/i", $subj)) { - $this->autoresponse = $this->head_hash['Subject']; - return TRUE; + if (isset($this->head_hash['Subject'])) { + foreach ($this->autorespondlist as $a) { + if (preg_match("/$a/i", ($this->head_hash['Subject']))) { + $this->autoresponse = "Subject: ". $this->head_hash['Subject']; + return TRUE; + } } } return FALSE; } - - - + + + // use a perl regular expression to find the web beacon public function find_web_beacon($body, $preg){ if(!isset($preg) || !$preg) @@ -754,7 +729,7 @@ public function find_web_beacon($body, $preg){ if(preg_match($preg, $body, $matches)) return $matches[1]; } - + public function find_x_header($xheader){ $xheader = ucfirst(strtolower($xheader)); // check the header @@ -768,7 +743,7 @@ public function find_x_header($xheader){ } return ""; } - + private function find_fbl_recipients($fbl){ if(isset($fbl['Original-rcpt-to'])){ return $fbl['Original-rcpt-to']; diff --git a/bounce_responses.php b/bounce_responses.php index 37b1bf9..63b3122 100644 --- a/bounce_responses.php +++ b/bounce_responses.php @@ -5,7 +5,7 @@ # # text in messages from which to figure out what kind of bounce -$bouncelist = array( +$this->bouncelist = array( '[45]\d\d[- ]#?([45]\.\d\.\d{1,2})' => 'x', # use the code from the regex 'Diagnostic[- ][Cc]ode: smtp; ?\d\d\ ([45]\.\d\.\d{1,2})' => 'x', # use the code from the regex 'Status: ([45]\.\d\.\d{1,2})' => 'x', # use the code from the regex @@ -222,19 +222,23 @@ ); # triggers for autoresponders -$autorespondlist = array( - '^\[?auto.{0,20}reply\]?', - '^auto[ -]?response', - '^Yahoo! auto response', - '^Thank you for your email\.', - '^Vacation.{0,20}(reply|respon)', +$this->autorespondlist = array( + 'auto:', + '^away from.{0,15}office', + '^.?auto(mated|matic)?.{0,5}(reply|response)', + 'Autom.{0,10} Antwort:', #auto response + 'Abwesenheitsnotiz', #vacation '^out.?of (the )?office', - '^(I am|I\'m).{0,20}\s(away|on vacation|on leave|out of office|out of the office)', + '^(I am|I\'m|I will).{0,15}\s(away|on vacation|on leave|out of office|out of the office)', + '^Thank you for your e-?mail', + '^This is an automated', + '^Vacation.{0,10}(alert|reply|response)', + '^Yahoo! auto response', "\350\207\252\345\212\250\345\233\236\345\244\215" #sino.com, 163.com UTF8 encoded ); # trigger subject lines for bounces -$bouncesubj = array( +$this->bouncesubj = array( 'deletver reports about your e?mail', 'delivery errors', 'delivery failure', @@ -264,6 +268,7 @@ 'undeliverable', 'undelivered mail', 'warning: message', + 'Warning. Still could not deliver email.', ); # @@ -276,4 +281,4 @@ # } # } #} -?> \ No newline at end of file +?> diff --git a/bounce_statuscodes.php b/bounce_statuscodes.php index e5251f5..234c229 100644 --- a/bounce_statuscodes.php +++ b/bounce_statuscodes.php @@ -1,8 +1,8 @@ \ No newline at end of file +?> diff --git a/cmdlinetest.php b/cmdlinetest.php index 1a6459d..de63342 100644 --- a/cmdlinetest.php +++ b/cmdlinetest.php @@ -7,23 +7,23 @@ function checkmail($email) { global $total; $bh = new Bouncehandler(); $bounceinfo = $bh->get_the_facts($email); -# var_dump($bounceinfo); -# var_dump($bh); - print "TYPE ". @$bh->type. "\n"; + # var_export($bounceinfo); + # var_export($bh); + print " TYPE ". @$bh->type. "\n"; if ($bh->type == 'bounce') { - print "ACTION ". $bounceinfo[0]['action']. "\n"; - print "STATUS ". $bounceinfo[0]['status']. "\n"; - print "RECIPIENT ". $bounceinfo[0]['recipient']. "\n"; + print " ACTION ". $bounceinfo[0]['action']. "\n"; + print " STATUS ". $bounceinfo[0]['status']. "\n"; + print " RECIPIENT ". $bounceinfo[0]['recipient']. "\n"; } if ($bh->type == 'fbl') { - print "ENV FROM ". @$bh->fbl_hash['Original-mail-from']. "\n"; - print "AGENT ". @$bh->fbl_hash['User-agent']. "\n"; - print "IP ". @$bh->fbl_hash['Source-ip']. "\n"; + print " ENV FROM ". @$bh->fbl_hash['Original-mail-from']. "\n"; + print " AGENT ". @$bh->fbl_hash['User-agent']. "\n"; + print " IP ". @$bh->fbl_hash['Source-ip']. "\n"; } if ($bh->type == 'autoresponse') { - print "AUTO ". $bounceinfo[0]['autoresponse']. "\n"; + print " AUTO ". $bounceinfo[0]['autoresponse']. "\n"; } - if ($bh->type) + if ($bh->type) @$total[$bh->type]++; else @$total['unknown']++; @@ -37,24 +37,24 @@ function checkmail($email) { for ($i = 1; $i < count($argv); $i++) { if (is_dir($argv[$i])) { $dh = opendir($argv[$i]); - while ($fn = readdir($dh)) { - if (substr($fn,0,1) !== '.') { - $email = file_get_contents($argv[$i].'/'.$fn); - print $argv[1]."/$fn\n"; - checkmail($email); - } + $fns = array(); + while ($fn = readdir($dh)) + if ($fn[0] !== '.' && !is_dir($argv[1]. '/'. $fn)) + $fns[] = $argv[1]. '/'. $fn; + closedir($dh); + foreach (sort($fns) as $fn) { + $email = file_get_contents($fn); + print "$fn\n"; + checkmail($email); } } else { $email = file_get_contents($argv[$i]); - print $argv[1]."\n"; + print $argv[$i]."\n"; checkmail($email); } } - foreach ($total as $k => $v) { - print "$k = $v\n"; - } - + } else { $handle = fopen("php://stdin","r"); @@ -62,6 +62,8 @@ function checkmail($email) { fclose($handle); checkmail($email); } + + # and the results foreach ($total as $t => $v) { printf("%-15s %6d\n", $t, $v); } diff --git a/eml/hotmailbounce.txt b/eml/hotmailbounce.txt index ec46715..8383a7c 100644 --- a/eml/hotmailbounce.txt +++ b/eml/hotmailbounce.txt @@ -11,7 +11,7 @@ Received: from BAY0-JMR-DB02 ([65.54.190.125]) by bay0-omc2-s22.bay0.hotmail.com Wed, 30 Jun 2010 10:38:06 -0700 Date: Wed, 30 Jun 10 10:38:06 -0700 From: staff@hotmail.com -Subject: complaint about message from 74.200.17.xxx +Subject: complaint about message from 100.200.17.xxx To: fbl-abuse@blahblah.com MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="F484BCB3-CE4F-4E2D-9A8E-B18786277C43" diff --git a/eml/vac1 b/eml/vac1 new file mode 100644 index 0000000..643cc58 --- /dev/null +++ b/eml/vac1 @@ -0,0 +1,38 @@ +Return-Path: <> +Delivered-To: support@example-sender.com +Received: (qmail 25576 invoked from network); 16 Sep 2014 14:24:16 -0000 +Received: from unknown (HELO mx.example-mailserver.com) (10.1.111.3) + by inboundmx01.local with SMTP; 16 Sep 2014 14:24:16 -0000 +Received: from gate.forward.smtp.dfw1a.emailsrvr.com ([98.129.184.12]) + by mx.example-mailserver.com with ESMTP/TLS/DHE-RSA-AES256-SHA; 16 Sep 2014 14:24:16 +0000 +Return-Path: <> +X-Virus-Scanned: OK +X-MessageSniffer-Scan-Result: 0 +X-MessageSniffer-Rules: 0-0-0-2193-c +Received: from [172.20.10.52] ([172.20.10.52:59452] helo=dfw136.emailsrvr.com) + by smtp36.gate.dfw1a.rsapps.net (envelope-from <>) + (ecelerity 2.2.3.49 r(42060/42061)) with ESMTPS (cipher=AES256-SHA) + id 04/9B-29943-E0848145; Tue, 16 Sep 2014 10:24:14 -0400 +Received: by store52a.mail.dfw1a (SMTP Server, from userid 5000) + id 48DC43B00EC; Tue, 16 Sep 2014 10:24:14 -0400 (EDT) +X-Sieve: Pigeonhole Sieve 0.4.2 +Message-ID: +Date: Tue, 16 Sep 2014 10:24:14 -0400 +From: +To: +Subject: Re: Tell us your your opinions +In-Reply-To: +References: +Auto-Submitted: auto-replied (vacation) +Precedence: bulk +MIME-Version: 1.0 +Content-Type: text/plain; charset=utf-8 +Content-Transfer-Encoding: 8bit + +Hello! + +Thank you for contacting us in West Oakville! +We have received your inquiry and will be contacting you within 24 hours. Hopefully today! + +Thanks! +Jane diff --git a/eml/vac2 b/eml/vac2 new file mode 100644 index 0000000..2ee753b --- /dev/null +++ b/eml/vac2 @@ -0,0 +1,26 @@ +Return-Path: <> +Delivered-To: assistance@example-sender.com +Received: (qmail 28064 invoked from network); 16 Sep 2014 14:52:45 -0000 +Received: from unknown (HELO mx.example.com) (10.1.111.3) + by inboundmx01.lau.local with SMTP; 16 Sep 2014 14:52:45 -0000 +Received: from 79.128.17.93.rev.sfr.net (HELO smtp1.services.sfr.fr) ([93.17.128.79]) + by mx.example.com with ESMTP; 16 Sep 2014 14:52:44 +0000 +Received: from msfrb0603.sfr.fr (msfrb0603 [10.18.35.17]) + by msfrf0101.sfr.fr (SMTP Server) with ESMTP id 2C9FC1800009E + for ; Tue, 16 Sep 2014 16:52:42 +0200 (CEST) +X-SFR-UUID: 20140916145242182.2C9FC1800009E@msfrf0101.sfr.fr +Received: by msfrb0603.sfr.fr (SMTP Server, from userid 1001) + id 2B0391C00401; Tue, 16 Sep 2014 16:52:42 +0200 (CEST) +Message-ID: +Date: Tue, 16 Sep 2014 16:52:42 +0200 +X-Sieve: CMU Sieve 2.3 +From: +To: +Subject: Auto: Original Subject +In-Reply-To: +Auto-Submitted: auto-replied (vacation) +MIME-Version: 1.0 +Content-Type: text/plain; charset=utf-8 +Content-Transfer-Encoding: 8bit + +A bientôt.... diff --git a/eml/vac3 b/eml/vac3 new file mode 100644 index 0000000..2de033f --- /dev/null +++ b/eml/vac3 @@ -0,0 +1,63 @@ +Return-Path: +Delivered-To: support@example-sender.com +Received: (qmail 32281 invoked from network); 16 Sep 2014 15:20:53 -0000 +Received: from unknown (HELO mx.example-sender.com) (10.1.111.3) + by inboundmx01.local with SMTP; 16 Sep 2014 15:20:53 -0000 +X-IronPort-Anti-Spam-Filtered: true +X-IronPort-Anti-Spam-Result: Aq8BAOpUGFQgYbaKm2dsb2JhbABghDfSagGBFBYBARABAQEBAQYLCwkUKoQEAQU6A0w/ElcZiEG7TxiOegEBVhaENQWpMo0WUIEPgTsBAQE +X-IronPort-AV: E=Sophos;i="5.04,534,1406592000"; + d="scan'208";a="3707773" +Received: from e8.ny.us.ibm.com ([32.97.182.138]) + by mx.example-sender.com with ESMTP/TLS/DHE-RSA-AES256-SHA; 16 Sep 2014 15:20:53 +0000 +Received: from /spool/local + by e8.ny.us.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted + for from ; + Tue, 16 Sep 2014 11:20:49 -0400 +Received: from d01dlp02.pok.ibm.com (9.56.250.167) + by e8.ny.us.ibm.com (192.168.1.108) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; + Tue, 16 Sep 2014 11:20:47 -0400 +Received: from b01cxnp23033.gho.pok.ibm.com (b01cxnp23033.gho.pok.ibm.com [9.57.198.28]) + by d01dlp02.pok.ibm.com (Postfix) with ESMTP id 1CCBE6E804A + for ; Tue, 16 Sep 2014 11:20:34 -0400 (EDT) +Received: from d01av05.pok.ibm.com (d01av05.pok.ibm.com [9.56.224.195]) + by b01cxnp23033.gho.pok.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id s8GFKcVT56033388 + for ; Tue, 16 Sep 2014 15:20:46 GMT +Received: from d01av05.pok.ibm.com (localhost [127.0.0.1]) + by d01av05.pok.ibm.com (8.14.4/8.14.4/NCO v10.0 AVout) with ESMTP id s8GFKC45028691 + for ; Tue, 16 Sep 2014 11:20:12 -0400 +Received: from d01ml394.pok.ibm.com (d01ml394.pok.ibm.com [9.63.8.16]) + by d01av05.pok.ibm.com (8.14.4/8.14.4/NCO v10.0 AVin) with ESMTP id s8GFKCOb028070 + for ; Tue, 16 Sep 2014 11:20:12 -0400 +Date: Tue, 16 Sep 2014 11:19:53 -0400 +From: Example User +To: support@example-sender.com +Subject: AUTO: The User is out of the office (returning 09/18/2014) +Auto-Submitted: auto-replied +In-Reply-To: +References: +Message-ID: +X-MIMETrack: Serialize by Router on D01ML394/01/M/IBM(Release 9.0.1FP1|April 03, 2014) at + 09/16/2014 11:20:12 +MIME-Version: 1.0 +Content-type: text/plain; charset=US-ASCII +X-TM-AS-MML: disable +X-Content-Scanned: Fidelis XPS MAILER +x-cbid: 14091615-0320-0000-0000-0000007FE4B0 + + + +I am out of the office until 09/18/2014. + +I am away on vacation from Sep 10 afternoon & back on Sep 18 in the +afternoon - +taking my laptop with me & will respond to mail periodically + + in case you have an urgent issue pls contact the following people + + + +Note: This is an automated response to your message "Original Subject" +sent on 09/16/2014 11:17:36. + +This is the only notification you will receive while this person is away. + diff --git a/eml/vac4 b/eml/vac4 new file mode 100644 index 0000000..75b6656 --- /dev/null +++ b/eml/vac4 @@ -0,0 +1,26 @@ +Return-Path: <> +Delivered-To: support@example.com +Received: (qmail 9714 invoked from network); 16 Sep 2014 16:31:50 -0000 +Received: from unknown (HELO mx.example.com) (10.1.111.4) + by inboundmx01.lau.local with SMTP; 16 Sep 2014 16:31:50 -0000 +X-IronPort-Anti-Spam-Filtered: true +X-IronPort-Anti-Spam-Result: ApUEAKFkGFTU4w8WlGdsb2JhbABgvDyDLpdjAXoWAQEQAQEBAQkJCwkSDh6ECjQGgRASDkeIW5ZBpVCOaREBbYQ1BZJQkXyPSIIaO4E+gTsBAQE +X-IronPort-AV: E=Sophos;i="5.04,535,1406592000"; + d="scan'208";a="3715381" +Received: from mout-bounce.user.de ([212.227.15.22]) + by mx.example.com with ESMTP/TLS/DHE-RSA-AES256-SHA; 16 Sep 2014 16:31:50 +0000 +From: Claire +To: support@example.com +Subject: Out of Office +Date: Tue, 16 Sep 2014 18:31:47 +0200 +Message-ID: <0MgCpx-1XiRi23hun-00NQy6@server.de> +In-Reply-To: +MIME-Version: 1.0 +Auto-Submitted: auto-replied (vacation) +Content-Type: text/plain; charset="us-ascii" +Content-Transfer-Encoding: base64 +X-UI-Out-Filterresults: unknown:0; + +SSBhbSBvdXQgb2YgdGhlIG9mZmljZSBGcmlkYXkgMTJ0aCBTZXB0ZW1iZXIgcmV0dXJuaW5nIG9u +IE1vbmRheSAxNXRoIFNlcHRlbWJlciB3aGVuIEkgd2lsbCBhbnN3ZXIgeW91ciBlbWFpbC4gCgpL +aW5kIFJlZ2FyZHMKCkNsYWlyZQ== diff --git a/eml/vac5 b/eml/vac5 new file mode 100644 index 0000000..8e10fd0 --- /dev/null +++ b/eml/vac5 @@ -0,0 +1,32 @@ +Return-Path: +Delivered-To: support@example.com +Received: (qmail 27206 invoked from network); 16 Sep 2014 20:03:10 -0000 +Received: from unknown (HELO mx.example.com) (10.1.111.4) + by inboundmx01.local with SMTP; 16 Sep 2014 20:03:10 -0000 +Received: from cp995.x.ca ([64.24.94.5]) + by mx.example.com with ESMTP/TLS/DHE-RSA-AES256-SHA; 16 Sep 2014 20:03:10 +0000 +Received: from mfiltd by something.ca with local (Exim 4.82) + (envelope-from ) + id 1XTyxt-0034KP-50 + for support@example.com; Tue, 16 Sep 2014 14:03:05 -0600 +To: "support" +X-Autorespond: Update your Info +MIME-Version: 1.0 +X-Loop: "support" +Precedence: auto_reply +X-Precedence: auto_reply +From: "scott@user.com" +Content-type: text/plain; charset=utf-8 +Subject: Out of Office +Message-Id: +Date: Tue, 16 Sep 2014 14:03:05 -0600 + +To our valued customers, + + +I will be on vacation from Monday September 8th, returning Tuesday September 23rd. Please contact if you require immediate assistance. If not, I will reply upon my return. Thank you! + +Scott + + + diff --git a/eml/vac6 b/eml/vac6 new file mode 100644 index 0000000..2a217a9 --- /dev/null +++ b/eml/vac6 @@ -0,0 +1,50 @@ +Return-Path: +Delivered-To: support@example.com +Received: (qmail 27211 invoked from network); 16 Sep 2014 20:03:11 -0000 +Received: from unknown (HELO mx.example.com) (10.1.111.3) + by inboundmx.local with SMTP; 16 Sep 2014 20:03:11 -0000 +Received: from mail-lb0-f169.google.com ([209.85.217.169]) + by mx.example.com with ESMTP/TLS/RC4-SHA; 16 Sep 2014 20:03:09 +0000 +Received: by mail-lb0-f169.google.com with SMTP id p9so517399lbv.14 + for ; Tue, 16 Sep 2014 13:03:08 -0700 (PDT) +DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; + d=thesoundresearch.com; s=google; + h=to:from:date:message-id:subject:mime-version:precedence + :auto-submitted:content-type:content-transfer-encoding + :content-disposition; + bh=uTsfxq0kCxhrNoyj95FRcTfCIq0ZYYk7yyba2BrpzPk=; + b=eFYvQFIETJPBrMl+B0BE21sguiicBN6VPQcz3SbcnWnyg55BN9tVmQc/7dZaBx1uc0 + HwgW6fXfJjf2o2NQL1o3/LadkslhI4biCHWTeyygOoCrSqKJt4M7i6webucMMypB/5y0 + Mf051Etq3uT1tLl+Q80DSjWEPMFdZKpFGalt0= +X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; + d=1e100.net; s=20130820; + h=x-gm-message-state:to:from:date:message-id:subject:mime-version + :precedence:auto-submitted:content-type:content-transfer-encoding + :content-disposition; + bh=uTsfxq0kCxhrNoyj95FRcTfCIq0ZYYk7yyba2BrpzPk=; + b=RCf/yY+JMAna8w13Zjl7hto1XMH8lOwQBw01i+cxBOvzvUNLnd0x/Ip0YVCUswp3jO + RrBkNC1/ejtDfnDA2pgTOLA8xHkhHCTuGWo6kz6NGRUAqoeTivJjTl6rlLNPia632ZXB + L2q2OOIXSU5pXSitAkBcQEvg41EBIRsNdFL/XpTaZvuWgxdx/DzgIdm11KEwwxzg58Hj + Z50vUH8F7/OyUL23nG9NnbvhiXwfxBv2ZVLJ3aW2am2mwPu6N0V4C+anJF+RCREuWHGj + 7OYacjO+ePaTecUf/XGM7Zh5H4Dl+rT1Pj1xiNjPdYwOFYX/5UM56E7uxcJwQKwDcHzt + CF/Q== +X-Received: by 10.112.72.10 with SMTP id z10mr13321654lbu.87.1410897788000; + Tue, 16 Sep 2014 13:03:08 -0700 (PDT) +To: support@example.com +From: "Carol" +Date: Tue, 16 Sep 2014 13:03:07 -0700 +Message-ID: +Subject: Vacation Alert! Re: Update your profile +MIME-Version: 1.0 +Precedence: bulk +X-Autoreply: yes +Auto-Submitted: auto-replied +Content-Type: text/html; charset=UTF-8 +Content-Transfer-Encoding: quoted-printable +Content-Disposition: inline + +
Hello!

Thanks for your message. I will = +be on vacation from Friday September 5th until Sunday, September 21st, back= + in the office on Monday, September 22nd. During this time I will have very= + limited access to email and voicemail. Please do not hesitate to reach out= + to
diff --git a/eml/vac7 b/eml/vac7 new file mode 100644 index 0000000..06ab35d --- /dev/null +++ b/eml/vac7 @@ -0,0 +1,25 @@ +Return-Path: <> +Delivered-To: support@example.com +Received: (qmail 31309 invoked from network); 16 Sep 2014 21:04:44 -0000 +Received: from unknown (HELO mx.example.com) (10.1.111.4) + by inboundmx01.lau.local with SMTP; 16 Sep 2014 21:04:44 -0000 +Received: from mail.user.ca ([137.21.136.1]) + by mx.example.com with ESMTP; 16 Sep 2014 21:04:44 +0000 +Received: by mail.user.ca (Postfix, from userid 1047) + id 550C8B982810; Tue, 16 Sep 2014 14:04:43 -0700 (PDT) +Message-ID: +Date: Tue, 16 Sep 2014 14:04:43 -0700 +X-Sieve: CMU Sieve 2.3 +From: tanya@user.ca +To: +Subject: I'm away from my mail +In-Reply-To: +Auto-Submitted: auto-replied (vacation) +Precedence: bulk +MIME-Version: 1.0 +Content-Type: text/plain; charset=utf-8 +Content-Transfer-Encoding: 8bit + +I am currently on vacation. + +Thank you and enjoy the rest of the Summer. diff --git a/php.bouncehandler.v7.4.zip b/php.bouncehandler.v7.4.zip deleted file mode 100644 index 9417828..0000000 Binary files a/php.bouncehandler.v7.4.zip and /dev/null differ diff --git a/rfc1893.error.codes.php b/rfc1893.error.codes.php new file mode 120000 index 0000000..6000d2d --- /dev/null +++ b/rfc1893.error.codes.php @@ -0,0 +1 @@ +bounce_statuscodes.php \ No newline at end of file diff --git a/testdriver1.php b/testdriver1.php index 12419dc..b8f84c4 100644 --- a/testdriver1.php +++ b/testdriver1.php @@ -31,48 +31,12 @@ } ?> -

bounce_driver.class.php -- Version 7.4

+

bounce_driver.class.php -- Version version; ?>

- Chris Fortune ~ http://cfortune.kics.bc.ca + Chris Fortune ~ http://cfortune.kics.bc.ca
+ Bounce Handler on GitHub

-

June 19, 2014

-
    -
  • make auto-responder identification table driven
  • -
  • make bounce_statuscodes.php (prev rfc1893_status_codes.php) generated from IANA list - php Make_statuscodes.php >bounce_statuscodes.php
  • -
  • allow for rfc status codes with 2 digits in the 3rd paramater
  • -
  • more supression for php notifications on undefined data
  • -
  • better detection and field definition for FBL handling
  • -
  • remove spaces in joined header lines
  • -
  • remove invalid/redundant implode operations
  • -
  • add two new sample emails 61,62
  • -
  • add command line test tool (cmdline_test.php)
  • - -

    -July 4, 2013 -

    -

    -Replaced deprecated split() function. -Added auto-responder identification filter. -Suppressed php Notice errors. -

    -


    - -Download source code -
    - - -

    -Feb 3, 2011 -

    -

    -Hey! Class is no longer static, it is rewritten in dynamic $this-> notation. It's much easier to customize now. If you are upgrading from a previous version, you will need to rewrite your method invocation code. -

    -


    - -Download source code -

    This bounce handler Attempts to parse Multipart reports for hard bounces, according to RFC1892 (RFC 1892 - The Multipart/Report Content Type for the Reporting of Mail System Administrative Messages) and RFC1894 (RFC 1894 - An Extensible Message Format for Delivery Status Notifications). We can reuse this for any well-formed bounces.