From cb2bc809ef29f349d38c89e202d821e67bb4c947 Mon Sep 17 00:00:00 2001 From: thomascube <thomas@roundcube.net> Date: Tue, 21 Sep 2010 14:47:55 -0400 Subject: [PATCH] Fix db_mode check in insert_id() --- program/include/rcube_imap.php | 355 ++++++++++++++++++++++++++++++++--------------------------- 1 files changed, 192 insertions(+), 163 deletions(-) diff --git a/program/include/rcube_imap.php b/program/include/rcube_imap.php index cdf346a..2151f5c 100644 --- a/program/include/rcube_imap.php +++ b/program/include/rcube_imap.php @@ -138,9 +138,10 @@ // write error log else if ($this->conn->error) { $this->error_code = $this->conn->errornum; - raise_error(array('code' => 403, 'type' => 'imap', - 'file' => __FILE__, 'line' => __LINE__, - 'message' => $this->conn->error), true, false); + if ($pass && $user) + raise_error(array('code' => 403, 'type' => 'imap', + 'file' => __FILE__, 'line' => __LINE__, + 'message' => $this->conn->error), true, false); } return false; @@ -155,8 +156,7 @@ */ function close() { - if ($this->conn && $this->conn->connected()) - $this->conn->close(); + $this->conn->close(); $this->write_cache(); } @@ -297,7 +297,9 @@ { if (is_array($str) && $msgs == null) list($str, $msgs, $charset, $sort_field, $threads) = $str; - if ($msgs != null && !is_array($msgs)) + if ($msgs === false) + $msgs = array(); + else if ($msgs != null && !is_array($msgs)) $msgs = explode(',', $msgs); $this->search_string = $str; @@ -472,14 +474,9 @@ // get message count and store in cache if ($mode == 'UNSEEN') $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; @@ -608,7 +605,7 @@ else $msg_index = array(); - if ($slice) + if ($slice && $msg_index) $msg_index = array_slice($msg_index, ($this->sort_order == 'DESC' ? 0 : -$slice), $slice); // fetch reqested headers from server @@ -823,8 +820,11 @@ return $this->_list_thread_header_set($mailbox, $page, $sort_field, $sort_order, $slice); // search set is threaded, we need a new one - if ($this->search_threads) + if ($this->search_threads) { + if (empty($this->search_set['tree'])) + return array(); $this->search('', $this->search_string, $this->search_charset, $sort_field); + } $msgs = $this->search_set; $a_msg_headers = array(); @@ -946,8 +946,15 @@ private function _list_thread_header_set($mailbox, $page=NULL, $sort_field=NULL, $sort_order=NULL, $slice=0) { // update search_set if previous data was fetched with disabled threading - if (!$this->search_threads) + if (!$this->search_threads) { + if (empty($this->search_set)) + return array(); $this->search('', $this->search_string, $this->search_charset, $sort_field); + } + + // empty result + if (empty($this->search_set['tree'])) + return array(); $thread_tree = $this->search_set['tree']; $msg_depth = $this->search_set['depth']; @@ -1188,20 +1195,20 @@ $a_index = range(1, $max); } - if ($this->sort_order == 'DESC') + if ($a_index !== false && $this->sort_order == 'DESC') $a_index = array_reverse($a_index); $this->cache[$key] = $a_index; } // fetch complete message index else if ($this->get_capability('SORT')) { - if ($a_index = $this->conn->sort($mailbox, - $this->sort_field, $this->skip_deleted ? 'UNDELETED' : '')) { - if ($this->sort_order == 'DESC') - $a_index = array_reverse($a_index); + $a_index = $this->conn->sort($mailbox, + $this->sort_field, $this->skip_deleted ? 'UNDELETED' : ''); - $this->cache[$key] = $a_index; - } + if ($a_index !== false && $this->sort_order == 'DESC') + $a_index = array_reverse($a_index); + + $this->cache[$key] = $a_index; } else if ($a_index = $this->conn->fetchHeaderIndex( $mailbox, "1:*", $this->sort_field, $this->skip_deleted)) { @@ -1213,7 +1220,7 @@ $this->cache[$key] = array_keys($a_index); } - return $this->cache[$key]; + return $this->cache[$key] !== false ? $this->cache[$key] : array(); } @@ -1288,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; @@ -1370,32 +1379,6 @@ $results = $this->_search_index($mailbox, $str, $charset, $sort_field); - // try search with US-ASCII charset (should be supported by server) - // only if UTF-8 search is not supported - if (empty($results) && !is_array($results) && !empty($charset) && $charset != 'US-ASCII') - { - // convert strings to US_ASCII - if(preg_match_all('/\{([0-9]+)\}\r\n/', $str, $matches, PREG_OFFSET_CAPTURE)) { - $last = 0; $res = ''; - foreach($matches[1] as $m) - { - $string_offset = $m[1] + strlen($m[0]) + 4; // {}\r\n - $string = substr($str, $string_offset - 1, $m[0]); - $string = rcube_charset_convert($string, $charset, 'US-ASCII'); - if (!$string) - continue; - $res .= sprintf("%s{%d}\r\n%s", substr($str, $last, $m[1] - $last - 1), strlen($string), $string); - $last = $m[0] + $string_offset - 1; - } - if ($last < strlen($str)) - $res .= substr($str, $last, strlen($str)-$last); - } - else // strings for conversion not found - $res = $str; - - $results = $this->search($mbox_name, $res, NULL, $sort_field); - } - $this->set_search_set($str, $results, $charset, $sort_field, (bool)$this->threading); return $results; @@ -1417,21 +1400,32 @@ $criteria = 'UNDELETED '.$criteria; if ($this->threading) { - list ($thread_tree, $msg_depth, $has_children) = $this->conn->thread( - $mailbox, $this->threading, $criteria, $charset); + $a_messages = $this->conn->thread($mailbox, $this->threading, $criteria, $charset); - $a_messages = array( - 'tree' => $thread_tree, - 'depth' => $msg_depth, - 'children' => $has_children - ); + // Error, try with US-ASCII (RFC5256: SORT/THREAD must support US-ASCII and UTF-8, + // but I've seen that Courier doesn't support UTF-8) + if ($a_messages === false && $charset && $charset != 'US-ASCII') + $a_messages = $this->conn->thread($mailbox, $this->threading, + $this->convert_criteria($criteria, $charset), 'US-ASCII'); + + if ($a_messages !== false) { + list ($thread_tree, $msg_depth, $has_children) = $a_messages; + $a_messages = array( + 'tree' => $thread_tree, + 'depth' => $msg_depth, + 'children' => $has_children + ); + } } else if ($sort_field && $this->get_capability('SORT')) { $charset = $charset ? $charset : $this->default_charset; $a_messages = $this->conn->sort($mailbox, $sort_field, $criteria, false, $charset); - if (!$a_messages) - return array(); + // Error, try with US-ASCII (RFC5256: SORT/THREAD must support US-ASCII and UTF-8, + // but I've seen that Courier doesn't support UTF-8) + if ($a_messages === false && $charset && $charset != 'US-ASCII') + $a_messages = $this->conn->sort($mailbox, $sort_field, + $this->convert_criteria($criteria, $charset), false, 'US-ASCII'); } else { if ($orig_criteria == 'ALL') { @@ -1440,14 +1434,16 @@ } else { $a_messages = $this->conn->search($mailbox, - ($charset ? "CHARSET $charset " : '') . $criteria); + ($charset ? "CHARSET $charset " : '') . $criteria); - if (!$a_messages) - return array(); + // Error, try with US-ASCII (some servers may support only US-ASCII) + if ($a_messages === false && $charset && $charset != 'US-ASCII') + $a_messages = $this->conn->search($mailbox, + 'CHARSET US-ASCII ' . $this->convert_criteria($criteria, $charset)); - // I didn't found that SEARCH always returns sorted IDs - if (!$this->sort_field) - sort($a_messages); + // I didn't found that SEARCH should return sorted IDs + if (is_array($a_messages) && !$this->sort_field) + sort($a_messages); } } @@ -1478,6 +1474,39 @@ $mailbox = $mbox_name ? $this->mod_mailbox($mbox_name) : $this->mailbox; return $this->conn->search($mailbox, $str, $ret_uid); + } + + + /** + * Converts charset of search criteria string + * + * @param string Search string + * @param string Original charset + * @param string Destination charset (default US-ASCII) + * @return string Search string + * @access private + */ + private function convert_criteria($str, $charset, $dest_charset='US-ASCII') + { + // convert strings to US_ASCII + if (preg_match_all('/\{([0-9]+)\}\r\n/', $str, $matches, PREG_OFFSET_CAPTURE)) { + $last = 0; $res = ''; + foreach ($matches[1] as $m) { + $string_offset = $m[1] + strlen($m[0]) + 4; // {}\r\n + $string = substr($str, $string_offset - 1, $m[0]); + $string = rcube_charset_convert($string, $charset, $dest_charset); + if (!$string) + continue; + $res .= sprintf("%s{%d}\r\n%s", substr($str, $last, $m[1] - $last - 1), strlen($string), $string); + $last = $m[0] + $string_offset - 1; + } + if ($last < strlen($str)) + $res .= substr($str, $last, strlen($str)-$last); + } + else // strings for conversion not found + $res = $str; + + return $res; } @@ -1675,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); @@ -1709,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"; @@ -1717,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++) { @@ -1729,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; } } } @@ -1753,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]); @@ -1796,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])) @@ -1806,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 @@ -1831,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 @@ -2034,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; @@ -2170,11 +2239,8 @@ // make sure mailbox exists if ($this->mailbox_exists($mbox_name, true)) { - if ($is_file) { - $separator = rcmail::get_instance()->config->header_delimiter(); - $saved = $this->conn->appendFromFile($mailbox, $message, - $headers, $separator.$separator); - } + if ($is_file) + $saved = $this->conn->appendFromFile($mailbox, $message, $headers); else $saved = $this->conn->append($mailbox, $message); } @@ -2560,7 +2626,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'])) { @@ -2591,7 +2657,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'])) { @@ -2601,7 +2667,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(); @@ -3360,7 +3426,7 @@ $name = trim($val['name']); if (preg_match('/^[\'"]/', $name) && preg_match('/[\'"]$/', $name)) - $name = preg_replace(array('/^[\'"]/', '/[\'"]$/'), '', $name); + $name = trim($name, '\'"'); if ($name && $address && $name != $address) $string = sprintf('%s <%s>', preg_match("/$special_chars/", $name) ? '"'.addcslashes($name, '"').'"' : $name, $address); @@ -3383,43 +3449,6 @@ /** - * Decode a Microsoft Outlook TNEF part (winmail.dat) - * - * @param object rcube_message_part Message part to decode - * @param string UID of the message - * @return array List of rcube_message_parts extracted from windmail.dat - */ - function tnef_decode(&$part, $uid) - { - if (!isset($part->body)) - $part->body = $this->get_message_part($uid, $part->mime_id, $part); - - require_once('lib/tnef_decoder.inc'); - - $pid = 0; - $tnef_parts = array(); - $tnef_arr = tnef_decode($part->body); - - foreach ($tnef_arr as $winatt) { - $tpart = new rcube_message_part; - $tpart->filename = trim($winatt['name']); - $tpart->encoding = 'stream'; - $tpart->ctype_primary = trim(strtolower($winatt['type0'])); - $tpart->ctype_secondary = trim(strtolower($winatt['type1'])); - $tpart->mimetype = $tpart->ctype_primary . '/' . $tpart->ctype_secondary; - $tpart->mime_id = "winmail." . $part->mime_id . ".$pid"; - $tpart->size = $winatt['size']; - $tpart->body = $winatt['stream']; - - $tnef_parts[] = $tpart; - $pid++; - } - - return $tnef_parts; - } - - - /** * Decode a message header value * * @param string Header value @@ -3429,7 +3458,7 @@ function decode_header($input, $remove_quotes=false) { $str = rcube_imap::decode_mime_string((string)$input, $this->default_charset); - if ($str{0}=='"' && $remove_quotes) + if ($str[0] == '"' && $remove_quotes) $str = str_replace('"', '', $str); return $str; -- Gitblit v1.9.1