From e70d6ea64e711096af36b1234f8545b870ea5f45 Mon Sep 17 00:00:00 2001
From: thomascube <thomas@roundcube.net>
Date: Sat, 05 Apr 2008 08:49:21 -0400
Subject: [PATCH] Apply changes from trunk to 0.1-stable

---
 program/include/rcube_imap.inc |  193 ++++++++++++++++++++++++++++++++++--------------
 1 files changed, 136 insertions(+), 57 deletions(-)

diff --git a/program/include/rcube_imap.inc b/program/include/rcube_imap.inc
index a525827..9a59485 100644
--- a/program/include/rcube_imap.inc
+++ b/program/include/rcube_imap.inc
@@ -5,7 +5,7 @@
  | program/include/rcube_imap.inc                                        |
  |                                                                       |
  | This file is part of the RoundCube Webmail client                     |
- | Copyright (C) 2005-2006, RoundCube Dev. - Switzerland                 |
+ | Copyright (C) 2005-2007, RoundCube Dev. - Switzerland                 |
  | Licensed under the GNU GPL                                            |
  |                                                                       |
  | PURPOSE:                                                              |
@@ -35,7 +35,7 @@
  *
  * @package    Mail
  * @author     Thomas Bruederli <roundcube@gmail.com>
- * @version    1.39
+ * @version    1.40
  * @link       http://ilohamail.org
  */
 class rcube_imap
@@ -51,6 +51,7 @@
   var $sort_order = 'DESC';
   var $delimiter = NULL;
   var $caching_enabled = FALSE;
+  var $default_charset = 'ISO-8859-1';
   var $default_folders = array('INBOX');
   var $default_folders_lc = array('inbox');
   var $cache = array();
@@ -97,17 +98,17 @@
    * @param  string   Username for IMAP account
    * @param  string   Password for IMAP account
    * @param  number   Port to connect to
-   * @param  boolean  Use SSL connection
+   * @param  string   SSL schema (either ssl or tls) or null if plain connection
    * @return boolean  TRUE on success, FALSE on failure
    * @access public
    */
-  function connect($host, $user, $pass, $port=143, $use_ssl=FALSE)
+  function connect($host, $user, $pass, $port=143, $use_ssl=null)
     {
     global $ICL_SSL, $ICL_PORT, $IMAP_USE_INTERNAL_DATE;
     
     // check for Open-SSL support in PHP build
     if ($use_ssl && in_array('openssl', get_loaded_extensions()))
-      $ICL_SSL = TRUE;
+      $ICL_SSL = $use_ssl == 'imaps' ? 'ssl' : $use_ssl;
     else if ($use_ssl)
       {
       raise_error(array('code' => 403, 'type' => 'imap', 'file' => __FILE__,
@@ -200,6 +201,20 @@
     
     if (empty($this->delimiter))
       $this->get_hierarchy_delimiter();
+    }
+
+
+  /**
+   * Set default message charset
+   *
+   * This will be used for message decoding if a charset specification is not available
+   *
+   * @param  string   Charset string
+   * @access public
+   */
+  function set_charset($cs)
+    {
+    $this->default_charset = $ch;
     }
 
 
@@ -441,7 +456,7 @@
       $mailbox = $this->mailbox;
       
     // count search set
-    if ($this->search_string && $mailbox == $this->mailbox && $mode == 'ALL')
+    if ($this->search_string && $mailbox == $this->mailbox && $mode == 'ALL' && !$force)
       return count((array)$this->search_set);
 
     $a_mailbox_cache = $this->get_cache('messagecount');
@@ -527,10 +542,7 @@
     if ($this->search_string && $mailbox == $this->mailbox)
       return $this->_list_header_set($mailbox, $this->search_set, $page, $sort_field, $sort_order);
 
-    if ($sort_field!=NULL)
-      $this->sort_field = $sort_field;
-    if ($sort_order!=NULL)
-      $this->sort_order = strtoupper($sort_order);
+    $this->_set_sort_order($sort_field, $sort_order);
 
     $max = $this->_messagecount($mailbox);
     $start_msg = ($this->list_page-1) * $this->page_size;
@@ -647,10 +659,7 @@
     if (!strlen($mailbox) || empty($msgs))
       return array();
 
-    if ($sort_field!=NULL)
-      $this->sort_field = $sort_field;
-    if ($sort_order!=NULL)
-      $this->sort_order = strtoupper($sort_order);
+    $this->_set_sort_order($sort_field, $sort_order);
 
     $max = count($msgs);
     $start_msg = ($this->list_page-1) * $this->page_size;
@@ -764,13 +773,20 @@
    */
   function message_index($mbox_name='', $sort_field=NULL, $sort_order=NULL)
     {
-    if ($sort_field!=NULL)
-      $this->sort_field = $sort_field;
-    if ($sort_order!=NULL)
-      $this->sort_order = strtoupper($sort_order);
+    $this->_set_sort_order($sort_field, $sort_order);
 
     $mailbox = $mbox_name ? $this->_mod_mailbox($mbox_name) : $this->mailbox;
-    $key = "$mbox:".$this->sort_field.":".$this->sort_order.".msgi";
+    $key = "{$mailbox}:{$this->sort_field}:{$this->sort_order}:{$this->search_string}.msgi";
+
+    // we have a saved search result. get index from there
+    if (!isset($this->cache[$key]) && $this->search_string && $mailbox == $this->mailbox)
+    {
+      $this->cache[$key] = $a_msg_headers = array();
+      $this->_fetch_headers($mailbox, join(',', $this->search_set), $a_msg_headers, NULL);
+
+      foreach (iil_SortHeaders($a_msg_headers, $this->sort_field, $this->sort_order) as $i => $msg)
+        $this->cache[$key][] = $msg->uid;
+    }
 
     // have stored it in RAM
     if (isset($this->cache[$key]))
@@ -943,6 +959,20 @@
       
     return $this->get_search_set();
     }
+  
+  
+  /**
+   * Check if the given message ID is part of the current search set
+   *
+   * @return boolean True on match or if no search request is stored
+   */
+  function in_searchset($msgid)
+  {
+    if (!empty($this->search_string))
+      return in_array("$msgid", (array)$this->search_set, true);
+    else
+      return true;
+  }
 
 
   /**
@@ -967,8 +997,8 @@
     // write headers cache
     if ($headers)
       {
-      if ($is_uid)
-        $this->uid_id_map[$mbox_name][$uid] = $headers->id;
+      if ($headers->uid && $headers->id)
+        $this->uid_id_map[$mailbox][$headers->uid] = $headers->id;
 
       $this->add_message_cache($mailbox.'.msg', $headers->id, $headers);
       }
@@ -1005,7 +1035,7 @@
     if (!empty($structure))
       {
       $this->_msg_id = $msg_id;
-      $headers = $this->get_headers($msg_id, NULL, FALSE);
+      $headers = $this->get_headers($uid);
       
       $struct = &$this->_structure_part($structure);
       $struct->headers = get_object_vars($headers);
@@ -1130,12 +1160,16 @@
       }
       
     // normalize filename property
-    if (!empty($struct->d_parameters['filename']))
-      $struct->filename = $this->decode_mime_string($struct->d_parameters['filename']);
-    else if (!empty($struct->ctype_parameters['name']))
-      $struct->filename = $this->decode_mime_string($struct->ctype_parameters['name']);
+    if ($filename_mime = $struct->d_parameters['filename'] ? $struct->d_parameters['filename'] : $struct->ctype_parameters['name'])
+      $struct->filename = rcube_imap::decode_mime_string($filename_mime, $this->default_charset);
+    else if ($filename_encoded = $struct->d_parameters['filename*'] ? $struct->d_parameters['filename*'] : $struct->ctype_parameters['name*'])
+    {
+      // decode filename according to RFC 2231, Section 4
+      list($filename_charset,, $filename_urlencoded) = split('\'', $filename_encoded);
+      $struct->filename = rcube_charset_convert(urldecode($filename_urlencoded), $filename_charset);
+    }
     else if (!empty($struct->headers['content-description']))
-      $struct->filename = $this->decode_mime_string($struct->headers['content-description']);
+      $struct->filename = rcube_imap::decode_mime_string($struct->headers['content-description'], $this->default_charset);
       
     return $struct;
     }
@@ -1201,23 +1235,30 @@
 
     if ($print)
       {
-      iil_C_HandlePartBody($this->conn, $this->mailbox, $msg_id, $part, ($o_part->encoding=='base64'?3:2));
-      $body = TRUE;
+      $mode = $o_part->encoding == 'base64' ? 3 : ($o_part->encoding == 'quoted-printable' ? 1 : 2);
+      $body = iil_C_HandlePartBody($this->conn, $this->mailbox, $msg_id, $part, $mode);
+      
+      // we have to decode the part manually before printing
+      if ($mode == 1)
+        {
+        echo $this->mime_decode($body, $o_part->encoding);
+        $body = true;
+        }
       }
     else
       {
       $body = iil_C_HandlePartBody($this->conn, $this->mailbox, $msg_id, $part, 1);
 
       // decode part body
-      if ($o_part->encoding=='base64' || $o_part->encoding=='quoted-printable')
+      if ($o_part->encoding)
         $body = $this->mime_decode($body, $o_part->encoding);
 
       // convert charset (if text or message part)
       if ($o_part->ctype_primary=='text' || $o_part->ctype_primary=='message')
         {
-        // assume ISO-8859-1 if no charset specified
+        // assume default if no charset specified
         if (empty($o_part->charset))
-          $o_part->charset = 'ISO-8859-1';
+          $o_part->charset = $this->default_charset;
 
         $body = rcube_charset_convert($body, $o_part->charset);
         }
@@ -1278,7 +1319,7 @@
    * Set message flag to one or several messages
    *
    * @param mixed  Message UIDs as array or as comma-separated string
-   * @param string Flag to set: SEEN, UNDELETED, DELETED, RECENT, ANSWERED, DRAFT
+   * @param string Flag to set: SEEN, UNDELETED, DELETED, RECENT, ANSWERED, DRAFT, MDNSENT
    * @return boolean True on success, False on failure
    */
   function set_flag($uids, $flag)
@@ -1607,16 +1648,14 @@
   /**
    * Subscribe to a specific mailbox(es)
    *
-   * @param string Mailbox name(s)
+   * @param array Mailbox name(s)
    * @return boolean True on success
    */ 
-  function subscribe($mbox_name)
+  function subscribe($a_mboxes)
     {
-    if (is_array($mbox_name))
-      $a_mboxes = $mbox_name;
-    else if (is_string($mbox_name) && strlen($mbox_name))
-      $a_mboxes = explode(',', $mbox_name);
-    
+    if (!is_array($a_mboxes))
+      $a_mboxes = array($a_mboxes);
+
     // let this common function do the main work
     return $this->_change_subscription($a_mboxes, 'subscribe');
     }
@@ -1625,15 +1664,13 @@
   /**
    * Unsubscribe mailboxes
    *
-   * @param string Mailbox name(s)
+   * @param array Mailbox name(s)
    * @return boolean True on success
    */
-  function unsubscribe($mbox_name)
+  function unsubscribe($a_mboxes)
     {
-    if (is_array($mbox_name))
-      $a_mboxes = $mbox_name;
-    else if (is_string($mbox_name) && strlen($mbox_name))
-      $a_mboxes = explode(',', $mbox_name);
+    if (!is_array($a_mboxes))
+      $a_mboxes = array($a_mboxes);
 
     // let this common function do the main work
     return $this->_change_subscription($a_mboxes, 'unsubscribe');
@@ -1759,13 +1796,17 @@
           $deleted = TRUE;
 
         foreach ($all_mboxes as $c_mbox)
-          if (preg_match('/^'.preg_quote($mailbox.$this->delimiter).'/', $c_mbox))
+          {
+          $regex = preg_quote($mailbox . $this->delimiter, '/');
+          $regex = '/^' . $regex . '/';
+          if (preg_match($regex, $c_mbox))
             {
             iil_C_UnSubscribe($this->conn, $c_mbox);
             $result = iil_C_DeleteFolder($this->conn, $c_mbox);
             if ($result>=0)
               $deleted = TRUE;
             }
+          }
         }
 
     // clear mailboxlist cache
@@ -2059,10 +2100,8 @@
    */
   function &get_cached_message($key, $uid, $struct=false)
     {
-    if (!$this->caching_enabled)
-      return FALSE;
-
     $internal_key = '__single_msg';
+    
     if ($this->caching_enabled && (!isset($this->cache[$internal_key][$uid]) ||
         ($struct && empty($this->cache[$internal_key][$uid]->structure))))
       {
@@ -2123,9 +2162,17 @@
    */
   function add_message_cache($key, $index, $headers, $struct=null)
     {
-    if (!$this->caching_enabled || empty($key) || !is_object($headers) || empty($headers->uid))
+    if (empty($key) || !is_object($headers) || empty($headers->uid))
+        return;
+    
+    // add to internal (fast) cache
+    $this->cache['__single_msg'][$headers->uid] = $headers;
+    $this->cache['__single_msg'][$headers->uid]->structure = $struct;
+    
+    // no further caching
+    if (!$this->caching_enabled)
       return;
-      
+    
     // check for an existing record (probly headers are cached but structure not)
     $sql_result = $this->db->query(
         "SELECT message_id
@@ -2177,6 +2224,9 @@
    */
   function remove_message_cache($key, $index)
     {
+    if (!$this->caching_enabled)
+      return;
+    
     $this->db->query(
       "DELETE FROM ".get_table_name('messages')."
        WHERE  user_id=?
@@ -2192,6 +2242,9 @@
    */
   function clear_message_cache($key, $start_index=1)
     {
+    if (!$this->caching_enabled)
+      return;
+    
     $this->db->query(
       "DELETE FROM ".get_table_name('messages')."
        WHERE  user_id=?
@@ -2221,6 +2274,8 @@
     {
     $a = $this->_parse_address_list($input, $decode);
     $out = array();
+    // Special chars as defined by RFC 822 need to in quoted string (or escaped).
+    $special_chars = '[\(\)\<\>\\\.\[\]@,;:"]';
     
     if (!is_array($a))
       return $out;
@@ -2234,7 +2289,7 @@
       $address = $val['address'];
       $name = preg_replace(array('/^[\'"]/', '/[\'"]$/'), '', trim($val['name']));
       if ($name && $address && $name != $address)
-        $string = sprintf('%s <%s>', strpos($name, ',')!==FALSE ? '"'.$name.'"' : $name, $address);
+        $string = sprintf('%s <%s>', preg_match("/$special_chars/", $name) ? '"'.addcslashes($name, '"').'"' : $name, $address);
       else if ($address)
         $string = $address;
       else if ($name)
@@ -2261,7 +2316,7 @@
    */
   function decode_header($input, $remove_quotes=FALSE)
     {
-    $str = $this->decode_mime_string((string)$input);
+    $str = rcube_imap::decode_mime_string((string)$input, $this->default_charset);
     if ($str{0}=='"' && $remove_quotes)
       $str = str_replace('"', '', $str);
     
@@ -2284,6 +2339,10 @@
     $pos = strpos($input, '=?');
     if ($pos !== false)
       {
+      // rfc: all line breaks or other characters not found in the Base64 Alphabet must be ignored by decoding software
+      // delete all blanks between MIME-lines, differently we can receive unnecessary blanks and broken utf-8 symbols
+      $input = preg_replace("/\?=\s+=\?/", '?==?', $input);
+
       $out = substr($input, 0, $pos);
   
       $end_cs_pos = strpos($input, "?", $pos+2);
@@ -2378,7 +2437,7 @@
       return rcube_charset_convert($body, $ctype_param['charset']);
 
     // defaults to what is specified in the class header
-    return rcube_charset_convert($body,  'ISO-8859-1');
+    return rcube_charset_convert($body,  $this->default_charset);
     }
 
 
@@ -2432,6 +2491,17 @@
     return $mbox_name;
     }
 
+  /**
+   * Validate the given input and save to local properties
+   * @access private
+   */
+  function _set_sort_order($sort_field, $sort_order)
+  {
+    if ($sort_field != null)
+      $this->sort_field = asciiwords($sort_field);
+    if ($sort_order != null)
+      $this->sort_order = strtoupper($sort_order) == 'DESC' ? 'DESC' : 'ASC';
+  }
 
   /**
    * Sort mailboxes first by default folders and then in alphabethical order
@@ -2447,7 +2517,7 @@
       if ($folder{0}=='.')
         continue;
 
-      if (($p = array_search(strtolower($folder), $this->default_folders_lc))!==FALSE)
+      if (($p = array_search(strtolower($folder), $this->default_folders_lc)) !== false && !$a_defaults[$p])
         $a_defaults[$p] = $folder;
       else
         $a_out[] = $folder;
@@ -2481,7 +2551,16 @@
     if (!$mbox_name)
       $mbox_name = $this->mailbox;
       
-    return iil_C_ID2UID($this->conn, $mbox_name, $id);
+    $index = array_flip((array)$this->uid_id_map[$mbox_name]);
+    if (isset($index[$id]))
+      $uid = $index[$id];
+    else
+      {
+      $uid = iil_C_ID2UID($this->conn, $mbox_name, $id);
+      $this->uid_id_map[$mbox_name][$uid] = $id;
+      }
+    
+    return $uid;
     }
 
 

--
Gitblit v1.9.1