From 18117d047eb85cb54c426970d311deb4f416b0c0 Mon Sep 17 00:00:00 2001 From: Jared Hancock <jared@osticket.com> Date: Fri, 23 Jan 2015 17:05:29 -0600 Subject: [PATCH] mail: Implement the message id tracking for everyone Also try harder to send a relevant In-Reply-To and References header back to the client with the email message. --- include/class.mailer.php | 42 ++++++++++++++++++++++++++++++++++------ include/class.thread.php | 35 ++++++++------------------------- include/class.ticket.php | 14 ++++++-------- 3 files changed, 50 insertions(+), 41 deletions(-) diff --git a/include/class.mailer.php b/include/class.mailer.php index 3db5e0a93..1063ffcba 100644 --- a/include/class.mailer.php +++ b/include/class.mailer.php @@ -118,7 +118,7 @@ class Mailer { * A: Predictable random code — used for loop detection * B: Random data for unique identifier * Version Code: A (at char position 10) - * C: TAG: Base64(Pack(userid, ticketid, type)), = chars discarded + * C: TAG: Base64(Pack(userid, entryId, type)), = chars discarded * D: Signature: * '@' + Signed Tag value, last 10 chars from * HMAC(sha1, tag+rand, SECRET_SALT) @@ -126,7 +126,11 @@ class Mailer { */ function getMessageId($recipient, $options=array(), $version='A') { $tag = ''; - $rand = str_replace('-','_', Misc::randCode(9)); + $rand = Misc::randCode(9, + // RFC822 specifies the LHS of the addr-spec can have any char + // except the specials — ()<>@,;:\".[], dash is reserved as the + // section separator, and + is reserved for historical reasons + 'abcdefghiklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_='); $sig = $this->getEmail()?$this->getEmail()->getEmail():'@osTicketMailer'; if ($recipient instanceof EmailContact) { // Create a tag for the outbound email @@ -235,10 +239,16 @@ class Mailer { $messageId = $this->getMessageId($to, $options); - if ($to instanceof EmailContact - || (is_object($to) && is_callable(array($to, 'getEmail'))) - ) { - $to = $to->getEmail(); + if (is_object($to) && is_callable(array($to, 'getEmail'))) { + // Add personal name if available + if (is_callable(array($to, 'getName'))) { + $to = sprintf('"%s" <%s>', + $to->getName()->getOriginal(), $to->getEmail() + ); + } + else { + $to = $to->getEmail(); + } } //do some cleanup @@ -293,6 +303,26 @@ class Mailer { } } + // Make the best effort to add In-Reply-To and References headers + if (isset($options['thread']) + && $options['thread'] instanceof ThreadEntry + ) { + $headers += array('References' => $options['thread']->getEmailReferences()); + if ($irt = $options['thread']->getEmailMessageId()) { + // This is an response from an email, like and autoresponse. + // Web posts will not have a email message-id + $headers += array('In-Reply-To' => $irt); + } + elseif ($parent = $options['thread']->getParent()) { + // Use the parent item as the email information source. This + // will apply for staff replies + $headers += array( + 'In-Reply-To' => $parent->getEmailMessageId(), + 'References' => $parent->getEmailReferences(), + ); + } + } + // Use Mail_mime default initially $eol = null; diff --git a/include/class.thread.php b/include/class.thread.php index eb7af30b4..a68636a66 100644 --- a/include/class.thread.php +++ b/include/class.thread.php @@ -291,6 +291,11 @@ Class ThreadEntry { return $this->ht['pid']; } + function getParent() { + if ($this->getPid()) + return ThreadEntry::lookup($this->getPid()); + } + function getType() { return $this->ht['thread_type']; } @@ -362,32 +367,11 @@ Class ThreadEntry { $headers = self::getEmailHeaderArray(); if (isset($headers['References']) && $headers['References']) $references = $headers['References']." "; - if ($include_mid) - $references .= $this->getEmailMessageId(); + if ($include_mid && ($mid = $this->getEmailMessageId())) + $references .= $mid; return $references; } - function getTaggedEmailReferences($prefix, $refId) { - - $ref = "+$prefix".Base32::encode(pack('VV', $this->getId(), $refId)); - - $mid = substr_replace($this->getEmailMessageId(), - $ref, strpos($this->getEmailMessageId(), '@'), 0); - - return sprintf('%s %s', $this->getEmailReferences(false), $mid); - } - - function getEmailReferencesForUser($user) { - return $this->getTaggedEmailReferences('u', - ($user instanceof Collaborator) - ? $user->getUserId() - : $user->getId()); - } - - function getEmailReferencesForStaff($staff) { - return $this->getTaggedEmailReferences('s', $staff->getId()); - } - function getUIDFromEmailReference($ref) { $info = unpack('Vtid/Vuid', @@ -1123,10 +1107,7 @@ Class ThreadEntry { return false; } - // Email message id (required for all thread posts) - if (!isset($vars['mid'])) - $vars['mid'] = sprintf('<%s@%s>', Misc::randCode(24), - substr(md5($cfg->getUrl()), -10)); + // Email message id $entry->saveEmailInfo($vars); // Inline images (attached to the draft) diff --git a/include/class.ticket.php b/include/class.ticket.php index 97f6c25b4..4524a893e 100644 --- a/include/class.ticket.php +++ b/include/class.ticket.php @@ -986,7 +986,7 @@ class Ticket { 'signature' => ($dept && $dept->isPublic())?$dept->getSignature():'') ); - $email->sendAutoReply($this->getEmail(), $msg['subj'], $msg['body'], + $email->sendAutoReply($this->getOwner(), $msg['subj'], $msg['body'], null, $options); } @@ -1061,7 +1061,7 @@ class Ticket { $msg = $this->replaceVars($msg->asArray(), array('signature' => ($dept && $dept->isPublic())?$dept->getSignature():'')); - $email->sendAutoReply($this->getEmail(), $msg['subj'], $msg['body']); + $email->sendAutoReply($this->getOwner(), $msg['subj'], $msg['body']); } $user = $this->getOwner(); @@ -1123,9 +1123,8 @@ class Ticket { 'thread' => $entry); foreach ($recipients as $recipient) { if ($uid == $recipient->getUserId()) continue; - $options['references'] = $entry->getEmailReferencesForUser($recipient); $notice = $this->replaceVars($msg, array('recipient' => $recipient)); - $email->send($recipient->getEmail(), $notice['subj'], $notice['body'], $attachments, + $email->send($recipient, $notice['subj'], $notice['body'], $attachments, $options); } @@ -1188,9 +1187,8 @@ class Ticket { $options = array( 'inreplyto'=>$message->getEmailMessageId(), - 'references' => $message->getEmailReferencesForUser($user), 'thread'=>$message); - $email->sendAutoReply($user->getEmail(), $msg['subj'], $msg['body'], + $email->sendAutoReply($user, $msg['subj'], $msg['body'], null, $options); } } @@ -1797,7 +1795,7 @@ class Ticket { 'inreplyto'=>$response->getEmailMessageId(), 'references'=>$response->getEmailReferences(), 'thread'=>$response); - $email->sendAutoReply($this->getEmail(), $msg['subj'], $msg['body'], $attachments, + $email->sendAutoReply($this, $msg['subj'], $msg['body'], $attachments, $options); } @@ -2864,7 +2862,7 @@ class Ticket { 'references' => $references, 'thread' => $ticket->getLastMessage() ); - $email->send($ticket->getEmail(), $msg['subj'], $msg['body'], $attachments, + $email->send($ticket->getOwner(), $msg['subj'], $msg['body'], $attachments, $options); } -- GitLab