@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Backport fix from php-mime-mail-parser to fix attachment parsing

Summary:
- Allow proper parsing of attachments with missing Content-Disposition
header

Test Plan:
- Create application email for Maniphest.
- Send example broken email from Outlook 2007 to that address {F1842816}

Reviewers: #blessed_reviewers, epriestley

Reviewed By: #blessed_reviewers, epriestley

Subscribers: epriestley

Differential Revision: https://secure.phabricator.com/D16584

authored by

Brendan Zerr and committed by
epriestley
af218564 f8c22252

+62 -40
+62 -40
externals/mimemailparser/MimeMailParser.class.php
··· 111 111 * @param $data String 112 112 */ 113 113 public function setText($data) { 114 - // NOTE: This has been modified for Phabricator. If the input data does not 115 - // end in a newline, Mailparse fails to include the last line in the mail 116 - // body. This happens somewhere deep, deep inside the mailparse extension, 117 - // so adding a newline here seems like the most straightforward fix. 118 - if (!preg_match('/\n\z/', $data)) { 119 - $data = $data."\n"; 120 - } 114 + // NOTE: This has been modified for Phabricator. If the input data does not 115 + // end in a newline, Mailparse fails to include the last line in the mail 116 + // body. This happens somewhere deep, deep inside the mailparse extension, 117 + // so adding a newline here seems like the most straightforward fix. 118 + if (!preg_match('/\n\z/', $data)) { 119 + $data = $data."\n"; 120 + } 121 121 122 122 $this->resource = mailparse_msg_create(); 123 123 // does not parse incrementally, fast memory hog might explode ··· 203 203 ); 204 204 if (in_array($type, array_keys($mime_types))) { 205 205 foreach($this->parts as $part) { 206 - $disposition = $this->getPartContentDisposition($part); 207 - if ($disposition == 'attachment') { 208 - // text/plain parts with "Content-Disposition: attachment" are 209 - // attachments, not part of the text body. 210 - continue; 211 - } 206 + $disposition = $this->getPartContentDisposition($part); 207 + if ($disposition == 'attachment') { 208 + // text/plain parts with "Content-Disposition: attachment" are 209 + // attachments, not part of the text body. 210 + continue; 211 + } 212 212 if ($this->getPartContentType($part) == $mime_types[$type]) { 213 - $headers = $this->getPartHeaders($part); 214 - // Concatenate all the matching parts into the body text. For example, 215 - // if a user sends a message with some text, then an image, and then 216 - // some more text, the text body of the email gets split over several 217 - // attachments. 213 + $headers = $this->getPartHeaders($part); 214 + // Concatenate all the matching parts into the body text. For example, 215 + // if a user sends a message with some text, then an image, and then 216 + // some more text, the text body of the email gets split over several 217 + // attachments. 218 218 $body .= $this->decode( 219 219 $this->getPartBody($part), 220 220 array_key_exists('content-transfer-encoding', $headers) 221 - ? $headers['content-transfer-encoding'] 222 - : ''); 221 + ? $headers['content-transfer-encoding'] 222 + : ''); 223 223 } 224 224 } 225 225 } else { ··· 251 251 return $headers; 252 252 } 253 253 254 - 255 254 /** 256 255 * Returns the attachments contents in order of appearance 257 256 * @return Array 258 257 * @param $type Object[optional] 259 258 */ 260 259 public function getAttachments() { 260 + // NOTE: This has been modified for Phabricator. Some mail clients do not 261 + // send attachments with "Content-Disposition" headers. 261 262 $attachments = array(); 262 263 $dispositions = array("attachment","inline"); 263 - foreach($this->parts as $part) { 264 + $non_attachment_types = array("text/plain", "text/html"); 265 + $nonameIter = 0; 266 + foreach ($this->parts as $part) { 264 267 $disposition = $this->getPartContentDisposition($part); 265 - if (in_array($disposition, $dispositions)) { 268 + $filename = 'noname'; 269 + if (isset($part['disposition-filename'])) { 270 + $filename = $part['disposition-filename']; 271 + } elseif (isset($part['content-name'])) { 272 + // if we have no disposition but we have a content-name, it's a valid attachment. 273 + // we simulate the presence of an attachment disposition with a disposition filename 274 + $filename = $part['content-name']; 275 + $disposition = 'attachment'; 276 + } elseif (!in_array($part['content-type'], $non_attachment_types, true) 277 + && substr($part['content-type'], 0, 10) !== 'multipart/' 278 + ) { 279 + // if we cannot get it with getMessageBody, we assume it is an attachment 280 + $disposition = 'attachment'; 281 + } 282 + 283 + if (in_array($disposition, $dispositions) && isset($filename) === true) { 284 + if ($filename == 'noname') { 285 + $nonameIter++; 286 + $filename = 'noname'.$nonameIter; 287 + } 266 288 $attachments[] = new MimeMailParser_attachment( 267 - $part['disposition-filename'], 289 + $filename, 268 290 $this->getPartContentType($part), 269 291 $this->getAttachmentStream($part), 270 292 $disposition, ··· 413 435 private function getAttachmentStream(&$part) { 414 436 $temp_fp = tmpfile(); 415 437 416 - array_key_exists('content-transfer-encoding', $part['headers']) ? $encoding = $part['headers']['content-transfer-encoding'] : $encoding = ''; 438 + array_key_exists('content-transfer-encoding', $part['headers']) ? $encoding = $part['headers']['content-transfer-encoding'] : $encoding = ''; 417 439 418 440 if ($temp_fp) { 419 441 if ($this->stream) { ··· 445 467 } 446 468 447 469 448 - /** 449 - * Decode the string depending on encoding type. 450 - * @return String the decoded string. 451 - * @param $encodedString The string in its original encoded state. 452 - * @param $encodingType The encoding type from the Content-Transfer-Encoding header of the part. 453 - */ 454 - private function decode($encodedString, $encodingType) { 455 - if (strtolower($encodingType) == 'base64') { 456 - return base64_decode($encodedString); 457 - } else if (strtolower($encodingType) == 'quoted-printable') { 458 - return quoted_printable_decode($encodedString); 459 - } else { 460 - return $encodedString; 461 - } 462 - } 470 + /** 471 + * Decode the string depending on encoding type. 472 + * @return String the decoded string. 473 + * @param $encodedString The string in its original encoded state. 474 + * @param $encodingType The encoding type from the Content-Transfer-Encoding header of the part. 475 + */ 476 + private function decode($encodedString, $encodingType) { 477 + if (strtolower($encodingType) == 'base64') { 478 + return base64_decode($encodedString); 479 + } else if (strtolower($encodingType) == 'quoted-printable') { 480 + return quoted_printable_decode($encodedString); 481 + } else { 482 + return $encodedString; 483 + } 484 + } 463 485 464 486 } 465 487