diff --git a/include/class.mailer.php b/include/class.mailer.php index 1b1b8ad555a1a79578d3b3b5e071ebd085e737e0..23270c845956509386b23921aa46cb924d1badb9 100644 --- a/include/class.mailer.php +++ b/include/class.mailer.php @@ -128,7 +128,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) @@ -136,7 +136,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 @@ -245,10 +249,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 @@ -303,6 +313,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 d3703a69a00f88647eedc6c7f31ff68c3f78c409..65487811f43203102fcfd012d57e743ebf4e6c49 100644 --- a/include/class.thread.php +++ b/include/class.thread.php @@ -339,6 +339,11 @@ class ThreadEntry extends VerySimpleModel { return $this->pid; } + function getParent() { + if ($this->getPid()) + return ThreadEntry::lookup($this->getPid()); + } + function getType() { return $this->type; } @@ -407,32 +412,11 @@ class ThreadEntry extends VerySimpleModel { $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', @@ -1016,11 +1000,7 @@ class ThreadEntry extends VerySimpleModel { 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)); - + // Save mail message id, if available $entry->saveEmailInfo($vars); // Inline images (attached to the draft) diff --git a/include/class.ticket.php b/include/class.ticket.php index 8bab52148e33da993abbd9590b937e33548f5f39..0c073e11639790a5c1c8cc7181d8334d13fdc4d1 100644 --- a/include/class.ticket.php +++ b/include/class.ticket.php @@ -1286,7 +1286,7 @@ implements RestrictedAccess, Threadable { 'signature' => ($dept && $dept->isPublic())?$dept->getSignature():'') ); - $email->sendAutoReply($this->getEmail(), $msg['subj'], $msg['body'], + $email->sendAutoReply($this->getOwner(), $msg['subj'], $msg['body'], null, $options); } @@ -1361,7 +1361,7 @@ implements RestrictedAccess, Threadable { $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(); @@ -1423,9 +1423,8 @@ implements RestrictedAccess, Threadable { '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); } @@ -1499,9 +1498,8 @@ implements RestrictedAccess, Threadable { $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); } } @@ -2098,7 +2096,7 @@ implements RestrictedAccess, Threadable { '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); } @@ -3261,7 +3259,7 @@ implements RestrictedAccess, Threadable { 'references' => $references, 'thread' => $message, ); - $email->send($ticket->getEmail(), $msg['subj'], $msg['body'], $attachments, + $email->send($ticket->getOwner(), $msg['subj'], $msg['body'], $attachments, $options); }