From 5579ef662197029afbf90d7bc2bfb5ba594475ac Mon Sep 17 00:00:00 2001
From: Aleksander Machniak <alec@alec.pl>
Date: Thu, 17 Dec 2015 03:29:30 -0500
Subject: [PATCH] Fix handling of message/rfc822 attachments on replies and forwards (#1490607)

---
 CHANGELOG                               |    1 
 program/steps/mail/compose.inc          |   50 ++++++++--------
 program/steps/mail/func.inc             |   26 +-------
 program/lib/Roundcube/rcube_message.php |   50 +++++-----------
 4 files changed, 47 insertions(+), 80 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG
index 6053494..545d339 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -4,6 +4,7 @@
 - Add workaround for https://bugs.php.net/bug.php?id=70757 (#1490582)
 - Fix HTML sanitizer to skip <!-- node type X --> in output (#1490583)
 - Fix charset encoding of message/rfc822 part bodies (#1490606)
+- Fix handling of message/rfc822 attachments on replies and forwards (#1490607)
 
 RELEASE 1.0.7
 -------------
diff --git a/program/lib/Roundcube/rcube_message.php b/program/lib/Roundcube/rcube_message.php
index 30f6b56..6268228 100644
--- a/program/lib/Roundcube/rcube_message.php
+++ b/program/lib/Roundcube/rcube_message.php
@@ -525,24 +525,16 @@
                 $mail_part      = &$structure->parts[$i];
                 $primary_type   = $mail_part->ctype_primary;
                 $secondary_type = $mail_part->ctype_secondary;
+                $part_mimetype  = $mail_part->mimetype;
 
-                // real content-type of message/rfc822
-                if ($mail_part->real_mimetype) {
-                    $part_orig_mimetype = $mail_part->mimetype;
-                    $part_mimetype = $mail_part->real_mimetype;
-                    list($primary_type, $secondary_type) = explode('/', $part_mimetype);
-                }
-                else {
-                    $part_mimetype = $part_orig_mimetype = $mail_part->mimetype;
-                  }
-
-                // multipart/alternative
-                if ($primary_type == 'multipart') {
+                // multipart/alternative or message/rfc822
+                if ($primary_type == 'multipart' || $part_mimetype == 'message/rfc822') {
                     $this->parse_structure($mail_part, true);
 
                     // list message/rfc822 as attachment as well (mostly .eml)
-                    if ($part_orig_mimetype == 'message/rfc822' && !empty($mail_part->filename))
+                    if ($primary_type == 'message' && !empty($mail_part->filename)) {
                         $this->attachments[] = $mail_part;
+                    }
                 }
                 // part text/[plain|html] or delivery status
                 else if ((($part_mimetype == 'text/plain' || $part_mimetype == 'text/html') && $mail_part->disposition != 'attachment') ||
@@ -553,8 +545,9 @@
                         array('object' => $this, 'structure' => $mail_part,
                             'mimetype' => $part_mimetype, 'recursive' => true));
 
-                    if ($plugin['abort'])
+                    if ($plugin['abort']) {
                         continue;
+                    }
 
                     if ($part_mimetype == 'text/html' && $mail_part->size) {
                         $got_html_part = true;
@@ -576,14 +569,6 @@
                     if (!empty($mail_part->filename)) {
                         $this->attachments[] = $mail_part;
                     }
-                }
-                // part message/*
-                else if ($primary_type == 'message') {
-                    $this->parse_structure($mail_part, true);
-
-                    // list as attachment as well (mostly .eml)
-                    if (!empty($mail_part->filename))
-                        $this->attachments[] = $mail_part;
                 }
                 // ignore "virtual" protocol parts
                 else if ($primary_type == 'protocol') {
@@ -608,21 +593,14 @@
 
                     // part belongs to a related message and is linked
                     if (preg_match('/^multipart\/(related|relative)/', $mimetype)
-                        && ($mail_part->headers['content-id'] || $mail_part->headers['content-location'])) {
+                        && ($mail_part->headers['content-id'] || $mail_part->headers['content-location'])
+                    ) {
                         if ($mail_part->headers['content-id'])
                             $mail_part->content_id = preg_replace(array('/^</', '/>$/'), '', $mail_part->headers['content-id']);
                         if ($mail_part->headers['content-location'])
                             $mail_part->content_location = $mail_part->headers['content-base'] . $mail_part->headers['content-location'];
 
                         $this->inline_parts[] = $mail_part;
-                    }
-                    // attachment encapsulated within message/rfc822 part needs further decoding (#1486743)
-                    else if ($part_orig_mimetype == 'message/rfc822') {
-                        $this->parse_structure($mail_part, true);
-
-                        // list as attachment as well (mostly .eml)
-                        if (!empty($mail_part->filename))
-                            $this->attachments[] = $mail_part;
                     }
                     // regular attachment with valid content type
                     // (content-type name regexp according to RFC4288.4.2)
@@ -639,9 +617,13 @@
                         $this->attachments[] = $mail_part;
                     }
                 }
-                // attachment part as message/rfc822 (#1488026)
-                else if ($mail_part->mimetype == 'message/rfc822') {
-                    $this->parse_structure($mail_part);
+                // calendar part not marked as attachment (#1490325)
+                else if ($part_mimetype == 'text/calendar') {
+                    if (!$mail_part->filename) {
+                        $mail_part->filename = 'calendar.ics';
+                    }
+
+                    $this->attachments[] = $mail_part;
                 }
             }
 
diff --git a/program/steps/mail/compose.inc b/program/steps/mail/compose.inc
index bd0b11a..0dab641 100644
--- a/program/steps/mail/compose.inc
+++ b/program/steps/mail/compose.inc
@@ -716,11 +716,9 @@
 
         if (!empty($MESSAGE->parts)) {
             // collect IDs of message/rfc822 parts
-            if ($compose_mode == RCUBE_COMPOSE_EDIT || $compose_mode == RCUBE_COMPOSE_DRAFT) {
-                foreach ($MESSAGE->attachments as $part) {
-                    if ($part->mimetype == 'message/rfc822') {
-                        $messages[] = $part->mime_id;
-                    }
+            foreach ($MESSAGE->mime_parts as $part) {
+                if ($part->mimetype == 'message/rfc822') {
+                    $messages[] = $part->mime_id;
                 }
             }
 
@@ -730,7 +728,7 @@
                     continue;
                 }
 
-                // skip all content parts inside the message/rfc822 part in DRAFT/EDIT mode
+                // skip all content parts inside the message/rfc822 part
                 foreach ($messages as $mimeid) {
                     if (strpos($part->mime_id, $mimeid . '.') === 0) {
                         continue 2;
@@ -1163,6 +1161,10 @@
     $messages = array();
 
     foreach ((array)$message->mime_parts as $pid => $part) {
+        if ($part->mimetype == 'message/rfc822') {
+            $messages[] = $part->mime_id;
+        }
+
         if ($part->disposition == 'attachment' || ($part->disposition == 'inline' && $bodyIsHtml) || $part->filename) {
             // skip parts that aren't valid attachments
             if ($part->ctype_primary == 'multipart' || $part->mimetype == 'application/ms-tnef') {
@@ -1179,23 +1181,10 @@
                 continue;
             }
 
-            // skip message/rfc822 attachments on forwards (#1489214)
-            // Thunderbird when forwarding in inline mode displays such attachments
-            // and skips any attachments from inside of such part, this however
-            // skipped e.g. images used in HTML body or other attachments. So,
-            // better to skip .eml attachments but not their content (included files).
-            if ($part->mimetype == 'message/rfc822') {
-                if ($compose_mode == RCUBE_COMPOSE_FORWARD) {
-                    continue;
-                }
-                $messages[] = $part->mime_id;
-            }
-            else if ($compose_mode != RCUBE_COMPOSE_FORWARD) {
-                // skip attachments included in message/rfc822 attachment (#1486487)
-                foreach ($messages as $mimeid) {
-                    if (strpos($part->mime_id, $mimeid . '.') === 0) {
-                        continue 2;
-                    }
+            // skip attachments included in message/rfc822 attachment (#1486487, #1490607)
+            foreach ($messages as $mimeid) {
+                if (strpos($part->mime_id, $mimeid . '.') === 0) {
+                    continue 2;
                 }
             }
 
@@ -1227,9 +1216,22 @@
 {
     global $RCMAIL, $COMPOSE;
 
-    $cid_map = array();
+    $cid_map  = array();
+    $messages = array();
+
     foreach ((array)$message->mime_parts as $pid => $part) {
+        if ($part->mimetype == 'message/rfc822') {
+            $messages[] = $part->mime_id;
+        }
+
         if (($part->content_id || $part->content_location) && $part->filename) {
+            // skip attachments included in message/rfc822 attachment (#1486487, #1490607)
+            foreach ($messages as $mimeid) {
+                if (strpos($part->mime_id, $mimeid . '.') === 0) {
+                    continue 2;
+                }
+            }
+
             if ($attachment = rcmail_save_attachment($message, $pid)) {
                 $COMPOSE['attachments'][$attachment['id']] = $attachment;
                 $url = sprintf('%s&_id=%s&_action=display-attachment&_file=rcmfile%s',
diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc
index 7a96356..3eee82d 100644
--- a/program/steps/mail/func.inc
+++ b/program/steps/mail/func.inc
@@ -789,20 +789,15 @@
  *
  * @param rcube_message_part Message part
  * @param array              Display parameters array
- * @param string             Part body
  * @return string Formatted HTML string
  */
-function rcmail_print_body($part, $p = array(), $body = null)
+function rcmail_print_body($part, $p = array())
 {
     global $RCMAIL;
 
-    if ($body === null) {
-        $body = $part->body;
-    }
-
     // trigger plugin hook
     $data = $RCMAIL->plugins->exec_hook('message_part_before',
-        array('type' => $part->ctype_secondary, 'body' => $body, 'id' => $part->mime_id)
+        array('type' => $part->ctype_secondary, 'body' => $part->body, 'id' => $part->mime_id)
             + $p + array('safe' => false, 'plain' => false, 'inline_html' => true));
 
     // convert html to text/plain
@@ -1205,28 +1200,15 @@
                     $part->body = $MESSAGE->get_part_content($part->mime_id);
                 }
 
-                $body = $part->body;
-
-                // extract headers from message/rfc822 parts
-                if ($part->mimetype == 'message/rfc822') {
-                    $msgpart = rcube_mime::parse_message($part->body);
-                    $body    = rcube_charset::convert($msgpart->body, $msgpart->charset);
-
-                    if (!empty($msgpart->headers)) {
-                        $part = $msgpart;
-                        $out .= html::div('message-partheaders', rcmail_message_headers(sizeof($header_attrib) ? $header_attrib : null, $part->headers));
-                    }
-                }
-
                 // message is cached but not exists (#1485443), or other error
-                if ($body === false) {
+                if ($part->body === false) {
                     rcmail_message_error($MESSAGE->uid);
                 }
 
                 $plugin = $RCMAIL->plugins->exec_hook('message_body_prefix',
                     array('part' => $part, 'prefix' => ''));
 
-                $body = rcmail_print_body($part, array('safe' => $safe_mode, 'plain' => !$RCMAIL->config->get('prefer_html')), $body);
+                $body = rcmail_print_body($part, array('safe' => $safe_mode, 'plain' => !$RCMAIL->config->get('prefer_html')));
 
                 if ($part->ctype_secondary == 'html') {
                     $body     = rcmail_html4inline($body, $attrib['id'], 'rcmBody', $attrs, $safe_mode);

--
Gitblit v1.9.1