diff --git a/include/class.filter.php b/include/class.filter.php index 8854ea89e03095d003f2cb3bea407c8b5e062a91..993b7971213aaaa509b577e7b49633e9aba1e20e 100644 --- a/include/class.filter.php +++ b/include/class.filter.php @@ -832,19 +832,22 @@ class TicketFilter { * http://msdn.microsoft.com/en-us/library/ee219609(v=exchg.80).aspx */ /* static */ - function isAutoResponse($headers) { + function isAutoReply($headers) { if($headers && !is_array($headers)) $headers = Mail_Parse::splitHeaders($headers); $auto_headers = array( - 'Auto-Submitted' => 'AUTO-REPLIED', + 'Auto-Submitted' => array('AUTO-REPLIED', 'AUTO-GENERATED'), 'Precedence' => array('AUTO_REPLY', 'BULK', 'JUNK', 'LIST'), - 'Subject' => array('OUT OF OFFICE', 'AUTO-REPLY:', 'AUTORESPONSE'), + 'X-Precedence' => array('AUTO_REPLY', 'BULK', 'JUNK', 'LIST'), 'X-Autoreply' => 'YES', 'X-Auto-Response-Suppress' => array('ALL', 'DR', 'RN', 'NRN', 'OOF', 'AutoReply'), - 'X-Autoresponse' => '', - 'X-Auto-Reply-From' => '' + 'X-Autoresponse' => '*', + 'X-AutoReply-From' => '*', + 'X-Autorespond' => '*', + 'X-Mail-Autoreply' => '*', + 'X-Autogenerated' => 'REPLY', ); foreach ($auto_headers as $header=>$find) { @@ -860,39 +863,44 @@ class TicketFilter { foreach ($find as $f) if (strpos($value, $f) === 0) return true; + } elseif ($find === '*') { + return true; } elseif (strpos($value, $find) === 0) { return true; } } - # Bounces also counts as auto-responses. - if(self::isAutoBounce($headers)) - return true; - return false; } - function isAutoBounce($headers) { + static function isBounce($headers) { if($headers && !is_array($headers)) $headers = Mail_Parse::splitHeaders($headers); $bounce_headers = array( - 'From' => array('<MAILER-DAEMON@MAILER-DAEMON>', 'MAILER-DAEMON', '<>'), - 'Subject' => array('DELIVERY FAILURE', 'DELIVERY STATUS', 'UNDELIVERABLE:'), + 'From' => array('stripos', + array('MAILER-DAEMON', '<>'), null, false), + 'Subject' => array('stripos', + array('DELIVERY FAILURE', 'DELIVERY STATUS', + 'UNDELIVERABLE:', 'Undelivered Mail Returned'), 0), + 'Return-Path' => array('strcmp', array('<>'), 0), + 'Content-Type' => array('stripos', array('report-type=delivery-status'), null, false), + 'X-Failed-Recipients' => array('strpos', array('@'), null, false) ); foreach ($bounce_headers as $header => $find) { if(!isset($headers[$header])) continue; - $value = strtoupper($headers[$header]); + @list($func, $searches, $pos, $neg) = $find; - if (is_array($find)) { - foreach ($find as $f) - if (strpos($value, $f) === 0) - return true; - } elseif (strpos($value, $find) === 0) { - return true; + if(!($value = $headers[$header]) || !is_array($searches)) + continue; + + foreach ($searches as $f) { + $result = call_user_func($func, $value, $f); + if (($pos === null && $result !== $neg) or ($result === $pos)) + return true; } } diff --git a/include/class.mailfetch.php b/include/class.mailfetch.php index 882cd91a9d1f7cdbdaa0d0795739ed7a3b825e57..f1ce7fdc75bf590efa9c46eaff30045e78e6795a 100644 --- a/include/class.mailfetch.php +++ b/include/class.mailfetch.php @@ -641,12 +641,6 @@ class MailFetcher { return true; } - # check if it's a bounce! - if($vars['header'] && TicketFilter::isAutoBounce($vars['header'])) { - $ost->logWarning('Bounced email', $vars['message'], false); - return true; - } - //TODO: Log error.. return null; } diff --git a/include/class.mailparse.php b/include/class.mailparse.php index cd66f4e69262442eff83ccf9391051a89acf2d81..eacd62d2a24b44db7a6f66c38411ca9bf5a885f5 100644 --- a/include/class.mailparse.php +++ b/include/class.mailparse.php @@ -107,7 +107,7 @@ class Mail_Parse { foreach ($headers as $hdr) { list($name, $val) = explode(": ", $hdr, 2); # Create list of values if header is specified more than once - if ($array[$name] && $as_array) { + if (isset($array[$name]) && $as_array) { if (is_array($array[$name])) $array[$name][] = $val; else $array[$name] = array($array[$name], $val); } else { diff --git a/include/class.thread.php b/include/class.thread.php index a05dcedb9446ab36f8241e9e61d846e802f5c467..b0748f69e7522f3ac4113591efab38e0bbea56a9 100644 --- a/include/class.thread.php +++ b/include/class.thread.php @@ -415,8 +415,26 @@ Class ThreadEntry { return $this->ht['headers']; } - function isAutoResponse() { - return $this->getEmailHeader()?TicketFilter::isAutoResponse($this->getEmailHeader()):false; + function isAutoReply() { + + if (!isset($this->is_autoreply)) + $this->is_autoreply = $this->getEmailHeader() + ? TicketFilter::isAutoReply($this->getEmailHeader()) : false; + + return $this->is_autoreply; + } + + function isBounce() { + + if (!isset($this->is_bounce)) + $this->is_bounce = $this->getEmailHeader() + ? TicketFilter::isBounce($this->getEmailHeader()) : false; + + return $this->is_bounce; + } + + function isBounceOrAutoReply() { + return ($this->isAutoReply() || $this->isBounce()); } //Web uploads - caller is expected to format, validate and set any errors. diff --git a/include/class.ticket.php b/include/class.ticket.php index e8235b49ea6312d0e7cc7bc2263a0c8e4fa8b1d1..7bfcdc7a662c7227878da332c9c6659e1122403a 100644 --- a/include/class.ticket.php +++ b/include/class.ticket.php @@ -904,6 +904,10 @@ class Ticket { $msg = $this->replaceVars($msg->asArray(), array('message' => $message)); $recipients=$sentlist=array(); + //Exclude the auto responding email just incase it's from staff member. + if ($message->isAutoReply()) + $sentlist[] = $this->getEmail(); + //Alert admin?? if($cfg->alertAdminONNewTicket()) { $alert = $this->replaceVars($msg, array('recipient' => 'Admin')); @@ -1543,7 +1547,7 @@ class Ticket { if(!$alerts) return $message; //Our work is done... $autorespond = true; - if ($autorespond && $message->isAutoResponse()) + if ($autorespond && $message->isBounceOrAutoReply()) $autorespond=false; $this->onMessage($message, $autorespond); //must be called b4 sending alerts to staff. @@ -2314,15 +2318,8 @@ class Ticket { # Messages that are clearly auto-responses from email systems should # not have a return 'ping' message - if ($autorespond && $message && $message->isAutoResponse()) - $autorespond=false; - - //Don't auto respond to mailer daemons. - if( $autorespond && - (strpos(strtolower($vars['email']),'mailer-daemon@')!==false - || strpos(strtolower($vars['email']),'postmaster@')!==false)) { - $autorespond=false; - } + if ($autorespond && $message->isAutoReply()) + $autorespond = false; //post canned auto-response IF any (disables new ticket auto-response). if ($vars['cannedResponseId'] @@ -2336,6 +2333,11 @@ class Ticket { if($autorespond && $dept && !$dept->autoRespONNewTicket()) $autorespond=false; + //Don't send alerts to staff when the message is a bounce + // this is necessary to avoid possible loop (especially on new ticket) + if ($alertstaff && $message->isBounce()) + $alertstaff = false; + /***** See if we need to send some alerts ****/ $ticket->onNewTicket($message, $autorespond, $alertstaff); diff --git a/include/mysqli.php b/include/mysqli.php index 3b535919e88f18fec8465067c7f7b55d7b4e54f2..d8ce115ccfc454369f8a8a89094dd5ce124c1016 100644 --- a/include/mysqli.php +++ b/include/mysqli.php @@ -125,7 +125,13 @@ function db_create_database($database, $charset='utf8', function db_query($query, $logError=true) { global $ost, $__db; - $res = $__db->query($query); + $tries = 3; + do { + $res = $__db->query($query); + // Retry the query due to deadlock error (#1213) + // TODO: Consider retry on #1205 (lock wait timeout exceeded) + // TODO: Log warning + } while (!$res && --$tries && $__db->errno == 1213); if(!$res && $logError && $ost) { //error reporting $msg='['.$query.']'."\n\n".db_error();