Skip to content

Commit af21856

Browse files
axonxorzepriestley
authored and
epriestley
committedSep 21, 2016
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
1 parent f8c2225 commit af21856

File tree

1 file changed

+62
-40
lines changed

1 file changed

+62
-40
lines changed
 

‎externals/mimemailparser/MimeMailParser.class.php

+62-40
Original file line numberDiff line numberDiff line change
@@ -111,13 +111,13 @@ public function setStream($stream) {
111111
* @param $data String
112112
*/
113113
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+
}
121121

122122
$this->resource = mailparse_msg_create();
123123
// does not parse incrementally, fast memory hog might explode
@@ -203,23 +203,23 @@ public function getMessageBody($type = 'text') {
203203
);
204204
if (in_array($type, array_keys($mime_types))) {
205205
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+
}
212212
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.
218218
$body .= $this->decode(
219219
$this->getPartBody($part),
220220
array_key_exists('content-transfer-encoding', $headers)
221-
? $headers['content-transfer-encoding']
222-
: '');
221+
? $headers['content-transfer-encoding']
222+
: '');
223223
}
224224
}
225225
} else {
@@ -251,20 +251,42 @@ public function getMessageBodyHeaders($type = 'text') {
251251
return $headers;
252252
}
253253

254-
255254
/**
256255
* Returns the attachments contents in order of appearance
257256
* @return Array
258257
* @param $type Object[optional]
259258
*/
260259
public function getAttachments() {
260+
// NOTE: This has been modified for Phabricator. Some mail clients do not
261+
// send attachments with "Content-Disposition" headers.
261262
$attachments = array();
262263
$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) {
264267
$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+
}
266288
$attachments[] = new MimeMailParser_attachment(
267-
$part['disposition-filename'],
289+
$filename,
268290
$this->getPartContentType($part),
269291
$this->getAttachmentStream($part),
270292
$disposition,
@@ -413,7 +435,7 @@ private function getPartBodyFromText(&$part) {
413435
private function getAttachmentStream(&$part) {
414436
$temp_fp = tmpfile();
415437

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 = '';
417439

418440
if ($temp_fp) {
419441
if ($this->stream) {
@@ -445,21 +467,21 @@ private function getAttachmentStream(&$part) {
445467
}
446468

447469

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+
}
463485

464486
}
465487

0 commit comments

Comments
 (0)
Failed to load comments.