diff --git a/include/class.auth.php b/include/class.auth.php index b5de88a11852e0f4e3636b1921de49dd05b77a99..09708971fe24750cb1c8db3fd69215554f345381 100644 --- a/include/class.auth.php +++ b/include/class.auth.php @@ -560,7 +560,7 @@ class StaffAuthStrikeBackend extends AuthStrikeBackend { } } } -StaffAuthenticationBackend::register(StaffAuthStrikeBackend); +StaffAuthenticationBackend::register('StaffAuthStrikeBackend'); /* * Backend to monitor user's failed login attempts @@ -603,7 +603,7 @@ class UserAuthStrikeBackend extends AuthStrikeBackend { } } -UserAuthenticationBackend::register(UserAuthStrikeBackend); +UserAuthenticationBackend::register('UserAuthStrikeBackend'); class osTicketAuthentication extends StaffAuthenticationBackend { @@ -626,7 +626,7 @@ class osTicketAuthentication extends StaffAuthenticationBackend { } } -StaffAuthenticationBackend::register(osTicketAuthentication); +StaffAuthenticationBackend::register('osTicketAuthentication'); class PasswordResetTokenBackend extends StaffAuthenticationBackend { static $id = "pwreset.staff"; @@ -663,7 +663,7 @@ class PasswordResetTokenBackend extends StaffAuthenticationBackend { return parent::login($staff, $bk); } } -StaffAuthenticationBackend::register(PasswordResetTokenBackend); +StaffAuthenticationBackend::register('PasswordResetTokenBackend'); /* * AuthToken Authentication Backend @@ -749,7 +749,7 @@ class AuthTokenAuthentication extends UserAuthenticationBackend { } } -UserAuthenticationBackend::register(AuthTokenAuthentication); +UserAuthenticationBackend::register('AuthTokenAuthentication'); //Simple ticket lookup backend used to recover ticket access link. // We're using authentication backend so we can guard aganist brute force @@ -784,5 +784,5 @@ class AccessLinkAuthentication extends UserAuthenticationBackend { } } -UserAuthenticationBackend::register(AccessLinkAuthentication); +UserAuthenticationBackend::register('AccessLinkAuthentication'); ?> diff --git a/include/class.mailparse.php b/include/class.mailparse.php index 2aaff18b2e41bf255f19ee1b716c56e7fa8b776b..12d1b24281d6d2debbf950866d4f3d0a808043ed 100644 --- a/include/class.mailparse.php +++ b/include/class.mailparse.php @@ -171,28 +171,31 @@ class Mail_Parse { return Mail_Parse::parseAddressList($header); } + function getDeliveredToAddressList() { + if (!($header = $this->struct->headers['delivered-to'])) + return null; + + return Mail_Parse::parseAddressList($header); + } + function getToAddressList(){ // Delivered-to incase it was a BBC mail. $tolist = array(); if ($header = $this->struct->headers['to']) $tolist = array_merge($tolist, Mail_Parse::parseAddressList($header)); - if ($header = $this->struct->headers['delivered-to']) - $tolist = array_merge($tolist, - Mail_Parse::parseAddressList($header)); - return $tolist ? $tolist : null; } function getCcAddressList(){ - if (!($header = $this->struct->headers['cc'])) + if (!($header = @$this->struct->headers['cc'])) return null; return Mail_Parse::parseAddressList($header); } function getBccAddressList(){ - if (!($header = $this->struct->headers['bcc'])) + if (!($header = @$this->struct->headers['bcc'])) return null; return Mail_Parse::parseAddressList($header); @@ -209,7 +212,7 @@ class Mail_Parse { } function getReplyTo() { - if (!($header = $this->struct->headers['reply-to'])) + if (!($header = @$this->struct->headers['reply-to'])) return null; return Mail_Parse::parseAddressList($header); @@ -252,7 +255,7 @@ class Mail_Parse { function getBody(){ global $cfg; - if ($cfg->isHtmlThreadEnabled()) { + if ($cfg && $cfg->isHtmlThreadEnabled()) { if ($html=$this->getPart($this->struct,'text/html')) $body = new HtmlThreadBody($html); elseif ($text=$this->getPart($this->struct,'text/plain')) @@ -267,7 +270,7 @@ class Mail_Parse { else $body = new TextThreadBody(''); - if ($cfg->stripQuotedReply()) + if ($cfg && $cfg->stripQuotedReply()) $body->stripQuotedReply($cfg->getReplySeparator()); return $body; @@ -275,9 +278,9 @@ class Mail_Parse { function getPart($struct, $ctypepart, $recurse=-1) { - if($struct && !$struct->parts) { + if($struct && !@$struct->parts) { $ctype = @strtolower($struct->ctype_primary.'/'.$struct->ctype_secondary); - if ($struct->disposition + if (@$struct->disposition && (strcasecmp($struct->disposition, 'inline') !== 0)) return ''; if ($ctype && strcasecmp($ctype,$ctypepart)==0) { @@ -292,7 +295,7 @@ class Mail_Parse { } $data=''; - if($struct && $struct->parts && $recurse) { + if($struct && @$struct->parts && $recurse) { foreach($struct->parts as $i=>$part) { if($part && ($text=$this->getPart($part,$ctypepart,$recurse - 1))) $data.=$text; @@ -460,19 +463,30 @@ class EmailDataParser { $data['name'] = $data['email']; } - //TO Address:Try to figure out the email address... associated with the incoming email. + /* Scan through the list of addressees (via To, Cc, and Delivered-To headers), and identify + * how the mail arrived at the system. One of the mails should be in the system email list. + * The recipient list (without the Delivered-To addressees) will be made available to the + * ticket filtering system. However, addresses in the Delivered-To header should never be + * considered for the collaborator list. + */ $data['emailId'] = 0; $data['recipients'] = array(); $tolist = array(); - if(($to = $parser->getToAddressList())) + if (($to = $parser->getToAddressList())) $tolist['to'] = $to; - if(($cc = $parser->getCcAddressList())) + if (($cc = $parser->getCcAddressList())) $tolist['cc'] = $cc; + if (($dt = $parser->getDeliveredToAddressList())) + $tolist['delivered-to'] = $dt; + foreach ($tolist as $source => $list) { foreach($list as $addr) { - if(!($emailId=Email::getIdByEmail(strtolower($addr->mailbox).'@'.$addr->host))) { + if (!($emailId=Email::getIdByEmail(strtolower($addr->mailbox).'@'.$addr->host))) { + //Skip virtual Delivered-To addresses + if ($source == 'delivered-to') continue; + $data['recipients'][] = array( 'source' => "Email ($source)", 'name' => trim(@$addr->personal, '"'), @@ -483,6 +497,22 @@ class EmailDataParser { } } + /* + * In the event that the mail was delivered to the system although none of the system + * mail addresses are in the addressee lists, be careful not to include the addressee + * in the collaborator list. Therefore, the delivered-to addressees should be flagged so they + * are not added to the collaborator list in the ticket creation process. + */ + if ($tolist['delivered-to']) { + foreach ($tolist['delivered-to'] as $addr) { + foreach ($data['recipients'] as $i=>$r) { + if (strcasecmp($r['email'], $addr->mailbox.'@'.$addr->host) === 0) + $data['recipients'][$i]['source'] = 'delivered-to'; + } + } + } + + //maybe we got BCC'ed?? if(!$data['emailId']) { $emailId = 0; @@ -505,8 +535,8 @@ class EmailDataParser { else { // Typical email $data['message'] = $parser->getBody(); - $data['in-reply-to'] = $parser->struct->headers['in-reply-to']; - $data['references'] = $parser->struct->headers['references']; + $data['in-reply-to'] = @$parser->struct->headers['in-reply-to']; + $data['references'] = @$parser->struct->headers['references']; } $data['subject'] = $parser->getSubject(); diff --git a/include/class.ticket.php b/include/class.ticket.php index 827f6d944709e894968e16ae1696a6f7fd76fc37..fd407d922896498450ff72ed93ad78a70895e584 100644 --- a/include/class.ticket.php +++ b/include/class.ticket.php @@ -1531,6 +1531,10 @@ class Ticket { 'isactive' => ($message->getUserId() == $this->getUserId())? 1: 0); $collabs = array(); foreach ($vars['recipients'] as $recipient) { + // Skip virtual delivered-to addresses + if (strcasecmp($recipient['source'], 'delivered-to') === 0) + continue; + if (($user=User::fromVars($recipient))) if ($c=$this->addCollaborator($user, $info, $errors)) $collabs[] = sprintf('%s%s', diff --git a/setup/test/tests/mockdb.php b/setup/test/tests/mockdb.php new file mode 100644 index 0000000000000000000000000000000000000000..b6bc348cf0ddf50be84cd9fe24e4237c74f9ec90 --- /dev/null +++ b/setup/test/tests/mockdb.php @@ -0,0 +1,82 @@ +<?php + +function db_connect($source) { + global $__db; + $__db = $source; +} + +function db_input($what) { + return sprintf("'%8.8s'", md5($what)); +} + +function db_query($sql) { + global $__db; + + return $__db->query($sql); +} + +function db_fetch_row($res) { + return $res->fetch_row(); +} + +function db_fetch_array($res) { + return $res->fetch_array(); +} + +function db_affected_row() { + global $__db; + return $__db->affected_rows; +} +function db_insert_id() { + global $__db; + return $__db->insert_id; +} + +function db_num_rows($res) { + return $res->num_rows(); +} + +class MockDbSource { + var $insert_id = 1; + var $affected_rows = 1; + + var $data; + + function __construct($data=array()) { + $this->data = $data; + } + + function query($sql) { + $hash = md5($sql); + if (!isset($this->data[$sql])) + print ($hash.": No data found:\n".$sql."\n"); + + return new MockDbCursor($this->data[$hash] ?: array()); + } + + function addRecordset($hash, &$data) { + $this->data[$hash] = $data; + } +} + +class MockDbCursor { + var $data; + + function __construct($data) { + $this->data = $data; + } + + function fetch_row() { + list($i, $row) = each($this->data); + return $row; + } + + function fetch_array() { + list($i, $row) = each($this->data); + return $row; + } + + function num_rows() { + return count($this->data); + } +} diff --git a/setup/test/tests/test.mail-parse.php b/setup/test/tests/test.mail-parse.php new file mode 100644 index 0000000000000000000000000000000000000000..d1d80c225bc2ac846e62aa17b71d2a2f93128130 --- /dev/null +++ b/setup/test/tests/test.mail-parse.php @@ -0,0 +1,53 @@ +<?php + +require_once INCLUDE_DIR.'class.validator.php'; +require_once INCLUDE_DIR.'class.auth.php'; +require_once INCLUDE_DIR.'class.staff.php'; +require_once INCLUDE_DIR.'class.email.php'; +require_once INCLUDE_DIR.'class.format.php'; +require_once INCLUDE_DIR.'class.thread.php'; + +require_once 'mockdb.php'; + +class TestMailParsing extends Test { + var $name = "Mail parsing library tests"; + + function testRecipients() { + db_connect(new MockDbSource()); + $email = <<<EOF +Delivered-To: jared@osticket.com +Received: by 10.60.55.168 with SMTP id t8csp161432oep; + Fri, 7 Feb 2014 22:11:19 -0800 (PST) +X-Received: by 10.182.18.9 with SMTP id s9mr16356699obd.15.1391839879167; + Fri, 07 Feb 2014 22:11:19 -0800 (PST) +Return-Path: <mailer@greezybacon.supportsystem.com> +To: jared@osticket.com +Subject: =?utf-8?Q?System_test_email_=C2=AE?= +Content-Type: multipart/alternative; + boundary="=_28022448a1f58a3af7edf57ff2e3af44" +From: "Support" <help@supportsystem.com> +Date: Sat, 08 Feb 2014 01:11:18 -0500 +Message-ID: <Syke6-g24hwuTu77-help@supportsystem.com> +MIME-Version: 1.0 + +--=_28022448a1f58a3af7edf57ff2e3af44 +Content-Transfer-Encoding: base64 +Content-Type: text/plain; charset=utf-8 + +Q2hlZXJzISE= +--=_28022448a1f58a3af7edf57ff2e3af44 +Content-Transfer-Encoding: base64 +Content-Type: text/html; charset=utf-8 + +Q2hlZXJzISE= +--=_28022448a1f58a3af7edf57ff2e3af44-- +EOF; + + $result = EmailDataParser::parse($email); + $this->assert(count($result['recipients']) == 1, 'Expected 1 recipient'); + $this->assert($result['recipients'][0]['source'] == 'delivered-to', + 'Delivered-To header used as a collaborator'); + } +} +return 'TestMailParsing'; +?>