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 | 148 +++++++++++++++++++++++++++++++----------------- 1 files changed, 95 insertions(+), 53 deletions(-) diff --git a/program/include/rcube_imap.php b/program/include/rcube_imap.php index 6f3b402..3aa2132 100644 --- a/program/include/rcube_imap.php +++ b/program/include/rcube_imap.php @@ -1295,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; @@ -1702,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); @@ -1736,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"; @@ -1744,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++) { @@ -1756,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]) > 4) { - // 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; } } } @@ -1780,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]) > 4) { - $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]); @@ -1823,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])) @@ -1833,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 @@ -1858,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 @@ -2587,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'])) { @@ -2618,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'])) { -- Gitblit v1.9.1