From 67127c690f5cb90f2e516aecf9efc5611575d6b1 Mon Sep 17 00:00:00 2001 From: Jared Hancock <jared@osticket.com> Date: Thu, 23 Apr 2015 14:50:14 -0500 Subject: [PATCH] email: Properly detect bounce message with alternative content This correctly handles a bounce message with the following layout: multipart/report; report-type=delivery-status multipart/alternative; differences=Content-Type text/plain; charset="us-ascii" text/html; charset="us-ascii" message/delivery-status message/rfc822 --- include/class.mailfetch.php | 32 ++++++++++++++++++---------- include/class.mailparse.php | 42 ++++++++++++++++++++++++++++--------- 2 files changed, 53 insertions(+), 21 deletions(-) diff --git a/include/class.mailfetch.php b/include/class.mailfetch.php index 1c3bf4fd6..c20c426fc 100644 --- a/include/class.mailfetch.php +++ b/include/class.mailfetch.php @@ -360,7 +360,7 @@ class MailFetcher { } //search for specific mime type parts....encoding is the desired encoding. - function getPart($mid, $mimeType, $encoding=false, $struct=null, $partNumber=false, $recurse=-1) { + function getPart($mid, $mimeType, $encoding=false, $struct=null, $partNumber=false, $recurse=-1, $recurseIntoRfc822=true) { if(!$struct && $mid) $struct=@imap_fetchstructure($this->mbox, $mid); @@ -396,15 +396,21 @@ class MailFetcher { && ($content = $this->tnef->getBody('text/html', $encoding))) return $content; - //Do recursive search - $text=''; - if($struct && $struct->parts && $recurse) { + // Do recursive search + $text = ''; + $ctype = $this->getMimeType($struct); + if ($struct && $struct->parts && $recurse + // Do not recurse into email (rfc822) attachments unless requested + && (strtolower($ctype) !== 'message/rfc822' || $recurseIntoRfc822) + ) { while(list($i, $substruct) = each($struct->parts)) { - if($partNumber) + if ($partNumber) $prefix = $partNumber . '.'; - if (($result=$this->getPart($mid, $mimeType, $encoding, - $substruct, $prefix.($i+1), $recurse-1))) - $text.=$result; + if ($result = $this->getPart($mid, $mimeType, $encoding, + $substruct, $prefix.($i+1), $recurse-1, $recurseIntoRfc822) + ) { + $text .= $result; + } } } @@ -519,9 +525,13 @@ class MailFetcher { if (strtolower($ctype) == 'multipart/report') { foreach ($struct->parameters as $p) { if (strtolower($p->attribute) == 'report-type' - && $p->value == 'delivery-status') { - return new TextThreadBody( $this->getPart( - $mid, 'text/plain', $this->charset, $struct, false, 1)); + && $p->value == 'delivery-status' + ) { + if ($body = $this->getPart( + $mid, 'text/plain', $this->charset, $struct, false, 3, false + )) { + return new TextThreadBody($body); + } } } } diff --git a/include/class.mailparse.php b/include/class.mailparse.php index f8209f395..3361cf2c1 100644 --- a/include/class.mailparse.php +++ b/include/class.mailparse.php @@ -278,9 +278,8 @@ class Mail_Parse { && isset($this->struct->ctype_parameters['report-type']) && $this->struct->ctype_parameters['report-type'] == 'delivery-status' ) { - return new TextThreadBody( - $this->getPart($this->struct, 'text/plain', 1) - ); + if ($body = $this->getPart($this->struct, 'text/plain', 3, false)) + return new TextThreadBody($body); } return false; } @@ -326,10 +325,27 @@ class Mail_Parse { return $body; } - function getPart($struct, $ctypepart, $recurse=-1) { + /** + * Fetch all the parts of the message for a specific MIME type. The + * parts are automatically transcoded to UTF-8 and concatenated together + * in the event more than one body of the requested type exists. + * + * Parameters: + * $struct - (<Mail_mime>) decoded message + * $ctypepart - (string) 'text/plain' or 'text/html', message body + * format to retrieve from the mail + * $recurse - (int:-1) levels acceptable to recurse into. Default is to + * recurse as needed. + * $recurseIntoRfc822 - (bool:true) proceed to recurse into + * message/rfc822 bodies to look for the message body format + * requested. For something like a bounce notice, where another + * email might be attached to the email, set this to false to avoid + * finding the wrong body. + */ + function getPart($struct, $ctypepart, $recurse=-1, $recurseIntoRfc822=true) { - if($struct && !@$struct->parts) { - $ctype = @strtolower($struct->ctype_primary.'/'.$struct->ctype_secondary); + $ctype = @strtolower($struct->ctype_primary.'/'.$struct->ctype_secondary); + if ($struct && !@$struct->parts) { if (@$struct->disposition && (strcasecmp($struct->disposition, 'inline') !== 0)) return ''; @@ -349,10 +365,16 @@ class Mail_Parse { return $content; $data=''; - if($struct && @$struct->parts && $recurse) { - foreach($struct->parts as $i=>$part) { - if($part && ($text=$this->getPart($part,$ctypepart,$recurse - 1))) - $data.=$text; + if ($struct && @$struct->parts && $recurse + // Do not recurse into email (rfc822) attachments unless requested + && ($ctype !== 'message/rfc822' || $recurseIntoRfc822) + ) { + foreach ($struct->parts as $i=>$part) { + if ($part && ($text=$this->getPart($part, $ctypepart, + $recurse-1, $recursIntoRfc822)) + ) { + $data .= $text; + } } } return $data; -- GitLab