1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-18 04:42:40 +01:00

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
This commit is contained in:
Brendan Zerr 2016-09-21 15:02:45 -07:00 committed by epriestley
parent f8c2225268
commit af218564e5

View file

@ -111,13 +111,13 @@ class MimeMailParser {
* @param $data String * @param $data String
*/ */
public function setText($data) { public function setText($data) {
// NOTE: This has been modified for Phabricator. If the input data does not // NOTE: This has been modified for Phabricator. If the input data does not
// end in a newline, Mailparse fails to include the last line in the mail // end in a newline, Mailparse fails to include the last line in the mail
// body. This happens somewhere deep, deep inside the mailparse extension, // body. This happens somewhere deep, deep inside the mailparse extension,
// so adding a newline here seems like the most straightforward fix. // so adding a newline here seems like the most straightforward fix.
if (!preg_match('/\n\z/', $data)) { if (!preg_match('/\n\z/', $data)) {
$data = $data."\n"; $data = $data."\n";
} }
$this->resource = mailparse_msg_create(); $this->resource = mailparse_msg_create();
// does not parse incrementally, fast memory hog might explode // does not parse incrementally, fast memory hog might explode
@ -203,23 +203,23 @@ class MimeMailParser {
); );
if (in_array($type, array_keys($mime_types))) { if (in_array($type, array_keys($mime_types))) {
foreach($this->parts as $part) { foreach($this->parts as $part) {
$disposition = $this->getPartContentDisposition($part); $disposition = $this->getPartContentDisposition($part);
if ($disposition == 'attachment') { if ($disposition == 'attachment') {
// text/plain parts with "Content-Disposition: attachment" are // text/plain parts with "Content-Disposition: attachment" are
// attachments, not part of the text body. // attachments, not part of the text body.
continue; continue;
} }
if ($this->getPartContentType($part) == $mime_types[$type]) { if ($this->getPartContentType($part) == $mime_types[$type]) {
$headers = $this->getPartHeaders($part); $headers = $this->getPartHeaders($part);
// Concatenate all the matching parts into the body text. For example, // Concatenate all the matching parts into the body text. For example,
// if a user sends a message with some text, then an image, and then // if a user sends a message with some text, then an image, and then
// some more text, the text body of the email gets split over several // some more text, the text body of the email gets split over several
// attachments. // attachments.
$body .= $this->decode( $body .= $this->decode(
$this->getPartBody($part), $this->getPartBody($part),
array_key_exists('content-transfer-encoding', $headers) array_key_exists('content-transfer-encoding', $headers)
? $headers['content-transfer-encoding'] ? $headers['content-transfer-encoding']
: ''); : '');
} }
} }
} else { } else {
@ -251,20 +251,42 @@ class MimeMailParser {
return $headers; return $headers;
} }
/** /**
* Returns the attachments contents in order of appearance * Returns the attachments contents in order of appearance
* @return Array * @return Array
* @param $type Object[optional] * @param $type Object[optional]
*/ */
public function getAttachments() { public function getAttachments() {
// NOTE: This has been modified for Phabricator. Some mail clients do not
// send attachments with "Content-Disposition" headers.
$attachments = array(); $attachments = array();
$dispositions = array("attachment","inline"); $dispositions = array("attachment","inline");
foreach($this->parts as $part) { $non_attachment_types = array("text/plain", "text/html");
$nonameIter = 0;
foreach ($this->parts as $part) {
$disposition = $this->getPartContentDisposition($part); $disposition = $this->getPartContentDisposition($part);
if (in_array($disposition, $dispositions)) { $filename = 'noname';
if (isset($part['disposition-filename'])) {
$filename = $part['disposition-filename'];
} elseif (isset($part['content-name'])) {
// if we have no disposition but we have a content-name, it's a valid attachment.
// we simulate the presence of an attachment disposition with a disposition filename
$filename = $part['content-name'];
$disposition = 'attachment';
} elseif (!in_array($part['content-type'], $non_attachment_types, true)
&& substr($part['content-type'], 0, 10) !== 'multipart/'
) {
// if we cannot get it with getMessageBody, we assume it is an attachment
$disposition = 'attachment';
}
if (in_array($disposition, $dispositions) && isset($filename) === true) {
if ($filename == 'noname') {
$nonameIter++;
$filename = 'noname'.$nonameIter;
}
$attachments[] = new MimeMailParser_attachment( $attachments[] = new MimeMailParser_attachment(
$part['disposition-filename'], $filename,
$this->getPartContentType($part), $this->getPartContentType($part),
$this->getAttachmentStream($part), $this->getAttachmentStream($part),
$disposition, $disposition,
@ -413,7 +435,7 @@ class MimeMailParser {
private function getAttachmentStream(&$part) { private function getAttachmentStream(&$part) {
$temp_fp = tmpfile(); $temp_fp = tmpfile();
array_key_exists('content-transfer-encoding', $part['headers']) ? $encoding = $part['headers']['content-transfer-encoding'] : $encoding = ''; array_key_exists('content-transfer-encoding', $part['headers']) ? $encoding = $part['headers']['content-transfer-encoding'] : $encoding = '';
if ($temp_fp) { if ($temp_fp) {
if ($this->stream) { if ($this->stream) {
@ -445,21 +467,21 @@ class MimeMailParser {
} }
/** /**
* Decode the string depending on encoding type. * Decode the string depending on encoding type.
* @return String the decoded string. * @return String the decoded string.
* @param $encodedString The string in its original encoded state. * @param $encodedString The string in its original encoded state.
* @param $encodingType The encoding type from the Content-Transfer-Encoding header of the part. * @param $encodingType The encoding type from the Content-Transfer-Encoding header of the part.
*/ */
private function decode($encodedString, $encodingType) { private function decode($encodedString, $encodingType) {
if (strtolower($encodingType) == 'base64') { if (strtolower($encodingType) == 'base64') {
return base64_decode($encodedString); return base64_decode($encodedString);
} else if (strtolower($encodingType) == 'quoted-printable') { } else if (strtolower($encodingType) == 'quoted-printable') {
return quoted_printable_decode($encodedString); return quoted_printable_decode($encodedString);
} else { } else {
return $encodedString; return $encodedString;
} }
} }
} }