From 1a2f8375ded7563964ea24c44c7874a92e6f7b77 Mon Sep 17 00:00:00 2001
From: alecpl <alec@alec.pl>
Date: Thu, 12 Aug 2010 03:11:28 -0400
Subject: [PATCH] - add message_part_structure hook also for text parts of mixed messages

---
 program/include/rcube_imap.php |  158 ++++++++++++++++++++++++++++++++--------------------
 1 files changed, 98 insertions(+), 60 deletions(-)

diff --git a/program/include/rcube_imap.php b/program/include/rcube_imap.php
index 09ea4df..3aa2132 100644
--- a/program/include/rcube_imap.php
+++ b/program/include/rcube_imap.php
@@ -476,11 +476,7 @@
                 $search_str .= " UNSEEN";
             // get message count using SEARCH
             // not very performant but more precise (using UNDELETED)
-            // disable THREADS for this request
-            $threads = $this->threading;
-            $this->threading = false;
-            $index = $this->_search_index($mailbox, $search_str);
-            $this->threading = $threads;
+            $index = $this->conn->search($mailbox, $search_str);
 
             $count = is_array($index) ? count($index) : 0;
 
@@ -1299,8 +1295,10 @@
         $all_ids = array();
         foreach($msg_index as $root) {
             $all_ids[] = $root;
-            if (!empty($thread_tree[$root]))
-                $all_ids = array_merge($all_ids, array_keys_recursive($thread_tree[$root]));
+            if (!empty($thread_tree[$root])) {
+                foreach (array_keys_recursive($thread_tree[$root]) as $val)
+                    $all_ids[] = $val;
+            }
         }
 
         return $all_ids;
@@ -1706,14 +1704,22 @@
         else
             $this->struct_charset = $this->_structure_charset($structure);
 
+        $headers->ctype = strtolower($headers->ctype);
+
         // Here we can recognize malformed BODYSTRUCTURE and
         // 1. [@TODO] parse the message in other way to create our own message structure
         // 2. or just show the raw message body.
         // Example of structure for malformed MIME message:
-        // ("text" "plain" ("charset" "us-ascii") NIL NIL "7bit" 2154 70 NIL NIL NIL)
-        if ($headers->ctype && $headers->ctype != 'text/plain'
-            && $structure[0] == 'text' && $structure[1] == 'plain') {
-            return false;
+        // ("text" "plain" NIL NIL NIL "7bit" 2154 70 NIL NIL NIL)
+        if ($headers->ctype && !is_array($structure[0]) && $headers->ctype != 'text/plain'
+            && strtolower($structure[0].'/'.$structure[1]) == 'text/plain') {
+            // we can handle single-part messages, by simple fix in structure (#1486898)
+            if (preg_match('/^(text|application)\/(.*)/', $headers->ctype, $m)) {
+                $structure[0] = $m[1];
+                $structure[1] = $m[2];
+            }
+            else
+                return false;
         }
 
         $struct = &$this->_structure_part($structure);
@@ -1740,7 +1746,7 @@
      *
      * @access private
      */
-    function &_structure_part($part, $count=0, $parent='', $mime_headers=null, $raw_headers=null)
+    function &_structure_part($part, $count=0, $parent='', $mime_headers=null)
     {
         $struct = new rcube_message_part;
         $struct->mime_id = empty($parent) ? (string)$count : "$parent.$count";
@@ -1748,6 +1754,18 @@
         // multipart
         if (is_array($part[0])) {
             $struct->ctype_primary = 'multipart';
+
+        /* RFC3501: BODYSTRUCTURE fields of multipart part
+            part1 array
+            part2 array
+            part3 array
+            ....
+            1. subtype
+            2. parameters (optional)
+            3. description (optional)
+            4. language (optional)
+            5. location (optional)
+        */
 
             // find first non-array entry
             for ($i=1; $i<count($part); $i++) {
@@ -1760,19 +1778,18 @@
             $struct->mimetype = 'multipart/'.$struct->ctype_secondary;
 
             // build parts list for headers pre-fetching
-            for ($i=0, $count=0; $i<count($part); $i++) {
-                if (is_array($part[$i]) && count($part[$i]) > 3) {
-                    // fetch message headers if message/rfc822
-                    // or named part (could contain Content-Location header)
-                    if (!is_array($part[$i][0])) {
-                        $tmp_part_id = $struct->mime_id ? $struct->mime_id.'.'.($i+1) : $i+1;
-                        if (strtolower($part[$i][0]) == 'message' && strtolower($part[$i][1]) == 'rfc822') {
-                            $raw_part_headers[] = $tmp_part_id;
-                            $mime_part_headers[] = $tmp_part_id;
-                        }
-                        else if (in_array('name', (array)$part[$i][2]) && (empty($part[$i][3]) || $part[$i][3]=='NIL')) {
-                            $mime_part_headers[] = $tmp_part_id;
-                        }
+            for ($i=0; $i<count($part); $i++) {
+                if (!is_array($part[$i]))
+                    break;
+                // fetch message headers if message/rfc822
+                // or named part (could contain Content-Location header)
+                if (!is_array($part[$i][0])) {
+                    $tmp_part_id = $struct->mime_id ? $struct->mime_id.'.'.($i+1) : $i+1;
+                    if (strtolower($part[$i][0]) == 'message' && strtolower($part[$i][1]) == 'rfc822') {
+                        $mime_part_headers[] = $tmp_part_id;
+                    }
+                    else if (in_array('name', (array)$part[$i][2]) && (empty($part[$i][3]) || $part[$i][3]=='NIL')) {
+                        $mime_part_headers[] = $tmp_part_id;
                     }
                 }
             }
@@ -1784,22 +1801,39 @@
                 $mime_part_headers = $this->conn->fetchMIMEHeaders($this->mailbox,
                     $this->_msg_id, $mime_part_headers);
             }
-            // we'll need a real content-type of message/rfc822 part
-            if ($raw_part_headers) {
-                $raw_part_headers = $this->conn->fetchMIMEHeaders($this->mailbox,
-                    $this->_msg_id, $raw_part_headers, false);
-            }
+
             $struct->parts = array();
             for ($i=0, $count=0; $i<count($part); $i++) {
-                if (is_array($part[$i]) && count($part[$i]) > 3) {
-                    $tmp_part_id = $struct->mime_id ? $struct->mime_id.'.'.($i+1) : $i+1;
-                    $struct->parts[] = $this->_structure_part($part[$i], ++$count, $struct->mime_id,
-                    $mime_part_headers[$tmp_part_id], $raw_part_headers[$tmp_part_id]);
-                }
+                if (!is_array($part[$i]))
+                    break;
+                $tmp_part_id = $struct->mime_id ? $struct->mime_id.'.'.($i+1) : $i+1;
+                $struct->parts[] = $this->_structure_part($part[$i], ++$count, $struct->mime_id,
+                    $mime_part_headers[$tmp_part_id]);
             }
 
             return $struct;
         }
+
+        /* RFC3501: BODYSTRUCTURE fields of non-multipart part
+            0. type
+            1. subtype
+            2. parameters
+            3. id
+            4. description
+            5. encoding
+            6. size
+          -- text
+            7. lines
+          -- message/rfc822
+            7. envelope structure
+            8. body structure
+            9. lines
+          --
+            x. md5 (optional)
+            x. disposition (optional)
+            x. language (optional)
+            x. location (optional)
+        */
 
         // regular part
         $struct->ctype_primary = strtolower($part[0]);
@@ -1827,9 +1861,11 @@
             $struct->size = intval($part[6]);
 
         // read part disposition
-        $di = count($part) - 2;
-        if ((is_array($part[$di]) && count($part[$di]) == 2 && is_array($part[$di][1])) ||
-            (is_array($part[--$di]) && count($part[$di]) == 2)) {
+        $di = 8;
+        if ($struct->ctype_primary == 'text') $di += 1;
+        else if ($struct->mimetype == 'message/rfc822') $di += 3;
+
+        if (is_array($part[$di]) && count($part[$di]) == 2) {
             $struct->disposition = strtolower($part[$di][0]);
 
             if (is_array($part[$di][1]))
@@ -1837,12 +1873,14 @@
                     $struct->d_parameters[strtolower($part[$di][1][$n])] = $part[$di][1][$n+1];
         }
 
-        // get child parts
+        // get message/rfc822's child-parts
         if (is_array($part[8]) && $di != 8) {
             $struct->parts = array();
-            for ($i=0, $count=0; $i<count($part[8]); $i++)
-                if (is_array($part[8][$i]) && count($part[8][$i]) > 5)
-                    $struct->parts[] = $this->_structure_part($part[8][$i], ++$count, $struct->mime_id);
+            for ($i=0, $count=0; $i<count($part[8]); $i++) {
+                if (!is_array($part[8][$i]))
+                    break;
+                $struct->parts[] = $this->_structure_part($part[8][$i], ++$count, $struct->mime_id);
+            }
         }
 
         // get part ID
@@ -1862,24 +1900,24 @@
             }
             $struct->headers = $this->_parse_headers($mime_headers) + $struct->headers;
 
-            // get real headers for message of type 'message/rfc822'
+            // get real content-type of message/rfc822
             if ($struct->mimetype == 'message/rfc822') {
-                if (empty($raw_headers)) {
-                    $raw_headers = $this->conn->fetchMIMEHeaders(
-                        $this->mailbox, $this->_msg_id, (array)$struct->mime_id, false);
-                }
-                $struct->real_headers = $this->_parse_headers($raw_headers);
-
-                // get real content-type of message/rfc822
-                if (preg_match('/^([a-z0-9_\/-]+)/i', $struct->real_headers['content-type'], $matches)) {
-                    $struct->real_mimetype = strtolower($matches[1]);
+                // single-part
+                if (!is_array($part[8][0]))
+                    $struct->real_mimetype = strtolower($part[8][0] . '/' . $part[8][1]);
+                // multi-part
+                else {
+                    for ($n=0; $n<count($part[8]); $n++)
+                        if (!is_array($part[8][$n]))
+                            break;
+                    $struct->real_mimetype = 'multipart/' . strtolower($part[8][$n]);
                 }
             }
-        }
 
-        if ($struct->ctype_primary=='message') {
-            if (is_array($part[8]) && $di != 8 && empty($struct->parts))
-                $struct->parts[] = $this->_structure_part($part[8], ++$count, $struct->mime_id);
+            if ($struct->ctype_primary == 'message' && empty($struct->parts)) {
+                if (is_array($part[8]) && $di != 8)
+                    $struct->parts[] = $this->_structure_part($part[8], ++$count, $struct->mime_id);
+            }
         }
 
         // normalize filename property
@@ -2065,7 +2103,7 @@
             return true;
 
         // convert charset (if text or message part)
-        if ($o_part->ctype_primary == 'text' || $o_part->ctype_primary == 'message') {
+        if ($body && ($o_part->ctype_primary == 'text' || $o_part->ctype_primary == 'message')) {
             // assume default if no charset specified
             if (empty($o_part->charset) || strtolower($o_part->charset) == 'us-ascii')
                 $o_part->charset = $this->default_charset;
@@ -2591,7 +2629,7 @@
         $a_defaults = $a_out = array();
 
         // Give plugins a chance to provide a list of mailboxes
-        $data = rcmail::get_instance()->plugins->exec_hook('list_mailboxes',
+        $data = rcmail::get_instance()->plugins->exec_hook('mailboxes_list',
             array('root' => $root, 'filter' => $filter, 'mode' => 'LSUB'));
 
         if (isset($data['folders'])) {
@@ -2622,7 +2660,7 @@
     function list_unsubscribed($root='', $filter='*')
     {
         // Give plugins a chance to provide a list of mailboxes
-        $data = rcmail::get_instance()->plugins->exec_hook('list_mailboxes',
+        $data = rcmail::get_instance()->plugins->exec_hook('mailboxes_list',
             array('root' => $root, 'filter' => $filter, 'mode' => 'LIST'));
 
         if (isset($data['folders'])) {
@@ -2632,7 +2670,7 @@
             // retrieve list of folders from IMAP server
             $a_mboxes = $this->conn->listMailboxes($this->mod_mailbox($root), $filter);
         }
-        
+
         $a_folders = array();
         if (!is_array($a_mboxes))
             $a_mboxes = array();

--
Gitblit v1.9.1