diff --git a/include/class.mailfetch.php b/include/class.mailfetch.php index 7fee6868a50a70cb1681f05ae8e2c7604510234f..82a5182b2b4b9b3ed559e7843994377a7c89f68d 100644 --- a/include/class.mailfetch.php +++ b/include/class.mailfetch.php @@ -423,19 +423,27 @@ class MailFetcher { $newticket=true; $errors=array(); + $seen = false; - if (($thread = ThreadEntry::lookupByEmailHeaders($vars)) + if (($thread = ThreadEntry::lookupByEmailHeaders($vars, $seen)) && ($message = $thread->postEmail($vars))) { if (!$message instanceof ThreadEntry) // Email has been processed previously return $message; $ticket = $message->getTicket(); + } elseif ($seen) { + // Already processed, but for some reason (like rejection), no + // thread item was created. Ignore the email + return true; } elseif (($ticket=Ticket::create($vars, $errors, 'Email'))) { $message = $ticket->getLastMessage(); } else { //Report success if the email was absolutely rejected. - if(isset($errors['errno']) && $errors['errno'] == 403) + if(isset($errors['errno']) && $errors['errno'] == 403) { + // Never process this email again! + ThreadEntry::logEmailInfo(0, $vars['mid']); return true; + } # check if it's a bounce! if($vars['header'] && TicketFilter::isAutoBounce($vars['header'])) { diff --git a/include/class.thread.php b/include/class.thread.php index ba9332ae487d638107b7fdef31d824e5bbb16ef9..2074a6b08756482567d1b237bab04ba1c615c26d 100644 --- a/include/class.thread.php +++ b/include/class.thread.php @@ -579,17 +579,23 @@ Class ThreadEntry { if(!$vars || !$vars['mid']) return 0; - $sql='INSERT INTO '.TICKET_EMAIL_INFO_TABLE - .' SET message_id='.db_input($this->getId()) //TODO: change it to thread_id - .', email_mid='.db_input($vars['mid']); //TODO: change it to mid. - if (isset($vars['header'])) - $sql .= ', headers='.db_input($vars['header']); - $this->ht['email_mid'] = $vars['mid']; - return db_query($sql)?db_insert_id():0; + $header = false; + if (isset($vars['header'])) + $header = $vars['header']; + self::logEmailHeaders($this->getId(), $vars['mid'], $header); } + /* static */ + function logEmailHeaders($id, $mid, $header=false) { + $sql='INSERT INTO '.TICKET_EMAIL_INFO_TABLE + .' SET message_id='.db_input($id) //TODO: change it to thread_id + .', email_mid='.db_input($mid); //TODO: change it to message_id. + if ($header) + $sql .= ', headers='.db_input($header); + return db_query($sql)?db_insert_id():0; + } /* variables */ @@ -640,28 +646,39 @@ Class ThreadEntry { * - "in-reply-to" => Message-Id the email is a direct response to * - "references" => List of Message-Id's the email is in response * - "subject" => Find external ticket number in the subject line + * + * seen (by-ref:bool) a flag that will be set if the message-id was + * positively found, indicating that the message-id has been + * previously seen. This is useful if no thread-id is associated + * with the email (if it was rejected for instance). */ - function lookupByEmailHeaders($mailinfo) { + function lookupByEmailHeaders($mailinfo, &$seen=false) { // Search for messages using the References header, then the // in-reply-to header - $search = 'SELECT message_id FROM '.TICKET_EMAIL_INFO_TABLE + $search = 'SELECT message_id, email_mid FROM '.TICKET_EMAIL_INFO_TABLE . ' WHERE email_mid=%s ORDER BY message_id DESC'; - if ($id = db_result(db_query( - sprintf($search, db_input($mailinfo['mid']))))) + if (list($id, $mid) = db_fetch_row(db_query( + sprintf($search, db_input($mailinfo['mid']))))) { + $seen = true; return ThreadEntry::lookup($id); + } foreach (array('mid', 'in-reply-to', 'references') as $header) { $matches = array(); if (!isset($mailinfo[$header]) || !$mailinfo[$header]) continue; // Header may have multiple entries (usually separated by - // semi-colons (;)) + // spaces ( ) elseif (!preg_match_all('/<[^>@]+@[^>]+>/', $mailinfo[$header], $matches)) continue; - foreach ($matches[0] as $mid) { + // The References header will have the most recent message-id + // (parent) on the far right. + // @see rfc 1036, section 2.2.5 + // @see http://www.jwz.org/doc/threading.html + foreach (array_reverse($matches[0]) as $mid) { $res = db_query(sprintf($search, db_input($mid))); while (list($id) = db_fetch_row($res)) { if ($t = ThreadEntry::lookup($id))