alecpl
2008-11-12 0803fb007eae33c43446460e25fdc40f6c550423
program/include/rcube_imap.php
@@ -64,6 +64,7 @@
  var $search_subject = '';
  var $search_string = '';
  var $search_charset = '';
  var $search_sort_field = '';
  var $debug_level = 1;
  var $error_code = 0;
@@ -285,10 +286,10 @@
   * @param  string Search string
   * @param  array  List of message ids or NULL if empty
   */
  function set_search_set($subject, $str=null, $msgs=null, $charset=null)
  function set_search_set($subject, $str=null, $msgs=null, $charset=null, $sort_field=null)
    {
    if (is_array($subject) && $str == null && $msgs == null)
      list($subject, $str, $msgs, $charset) = $subject;
      list($subject, $str, $msgs, $charset, $sort_field) = $subject;
    if ($msgs != null && !is_array($msgs))
      $msgs = split(',', $msgs);
      
@@ -296,6 +297,7 @@
    $this->search_string = $str;
    $this->search_set = (array)$msgs;
    $this->search_charset = $charset;
    $this->search_sort_field = $sort_field;
    }
@@ -305,7 +307,7 @@
   */
  function get_search_set()
    {
    return array($this->search_subject, $this->search_string, $this->search_set, $this->search_charset);
    return array($this->search_subject, $this->search_string, $this->search_set, $this->search_charset, $this->search_sort_field);
    }
@@ -544,7 +546,7 @@
    // use saved message set
    if ($this->search_string && $mailbox == $this->mailbox)
      return $this->_list_header_set($mailbox, $this->search_set, $page, $sort_field, $sort_order);
      return $this->_list_header_set($mailbox, $page, $sort_field, $sort_order);
    $this->_set_sort_order($sort_field, $sort_order);
@@ -626,62 +628,74 @@
    }
  /**
   * Public method for listing a specific set of headers
   * convert mailbox name with root dir first
   * Private method for listing a set of message headers (search results)
   *
   * @param   string   Mailbox/folder name
   * @param   array    List of message ids to list
   * @param   int      Current page to list
   * @param   string   Header field to sort by
   * @param   string   Sort order [ASC|DESC]
   * @return  array    Indexed array with message header objects
   * @access  public
   */
  function list_header_set($mbox_name='', $msgs, $page=NULL, $sort_field=NULL, $sort_order=NULL)
    {
    $mailbox = $mbox_name ? $this->_mod_mailbox($mbox_name) : $this->mailbox;
    return $this->_list_header_set($mailbox, $msgs, $page, $sort_field, $sort_order);
    }
  /**
   * Private method for listing a set of message headers
   *
   * @access  private
   * @see     rcube_imap::list_header_set()
   */
  function _list_header_set($mailbox, $msgs, $page=NULL, $sort_field=NULL, $sort_order=NULL)
  function _list_header_set($mailbox, $page=NULL, $sort_field=NULL, $sort_order=NULL)
    {
    if (!strlen($mailbox) || empty($msgs))
    if (!strlen($mailbox) || empty($this->search_set))
      return array();
    // also accept a comma-separated list of message ids
    if (is_array ($msgs)) {
      $max = count ($msgs);
      $msgs = join (',', $msgs);
    } else {
      $max = count(split(',', $msgs));
    }
    $msgs = $this->search_set;
    $a_msg_headers = array();
    $start_msg = ($this->list_page-1) * $this->page_size;
    $this->_set_sort_order($sort_field, $sort_order);
    $start_msg = ($this->list_page-1) * $this->page_size;
    // sorted messages, so we can first slice array and then fetch only wanted headers
    if ($this->get_capability('sort')) // SORT searching result
      {
      // reset search set if sorting field has been changed
      if ($sort_field && $this->search_sort_field != $sort_field)
        {
        $msgs = $this->search('', $this->search_subject, $this->search_string, $this->search_charset, $sort_field);
        }
    // fetch reuested headers from server
    $a_msg_headers = array();
    $this->_fetch_headers($mailbox, $msgs, $a_msg_headers, NULL);
      // return empty array if no messages found
      if (empty($msgs))
        return array();
    // return empty array if no messages found
    if (!is_array($a_msg_headers) || empty($a_msg_headers))
      return array();
      if ($sort_order == 'DESC')
        $msgs = array_reverse($msgs);
    // if not already sorted
    $a_msg_headers = iil_SortHeaders($a_msg_headers, $this->sort_field, $this->sort_order);
      // get messages uids for one page
      $msgs = array_slice(array_values($msgs), $start_msg, min(count($msgs)-$start_msg, $this->page_size));
    // only return the requested part of the set
    return array_slice(array_values($a_msg_headers), $start_msg, min($max-$start_msg, $this->page_size));
      // fetch headers
      $this->_fetch_headers($mailbox, join(',',$msgs), $a_msg_headers, NULL);
      $sorter = new rcube_header_sorter();
      $sorter->set_sequence_numbers($msgs);
      $sorter->sort_headers($a_msg_headers);
      return array_values($a_msg_headers);
      }
    else { // SEARCH searching result
      // not sorted, so we must fetch headers for all messages
      // TODO: to minimize big memory consumption on servers without SORT
      // capability, we should fetch only headers used for sorting, and then
      // fetch all headers needed for displaying one page of messages list.
      // Of course it has sense only for big results if count($msgs) > $this->pagesize
      $this->_fetch_headers($mailbox, join(',', $msgs), $a_msg_headers, NULL);
      // return empty array if no messages found
      if (!is_array($a_msg_headers) || empty($a_msg_headers))
        return array();
      // if not already sorted
      $a_msg_headers = iil_SortHeaders($a_msg_headers, $this->sort_field, $this->sort_order);
      // only return the requested part of the set
      return array_slice(array_values($a_msg_headers), $start_msg, min(count($msgs)-$start_msg, $this->page_size));
      }
    }
@@ -894,39 +908,36 @@
   * @param  string  mailbox name to search in
   * @param  string  search criteria (ALL, TO, FROM, SUBJECT, etc)
   * @param  string  search string
   * @param  string  search string charset
   * @param  string  header field to sort by
   * @return array   search results as list of message ids
   * @access public
   */
  function search($mbox_name='', $criteria='ALL', $str=NULL, $charset=NULL)
  function search($mbox_name='', $criteria='ALL', $str=NULL, $charset=NULL, $sort_field=NULL)
    {
    $mailbox = $mbox_name ? $this->_mod_mailbox($mbox_name) : $this->mailbox;
    $search = '';
    // have an array of criterias => execute multiple searches
    if (is_array($criteria) && $str)
      {
      $results = array();
      foreach ($criteria as $crit)
        if ($search_result = $this->search($mbox_name, $crit, $str, $charset))
          $results = array_merge($results, $search_result);
      $results = array_unique($results);
      $this->set_search_set($criteria, $str, $results, $charset);
      return $results;
      }
    else if ($str && $criteria)
      {
      $search = (!empty($charset) ? "CHARSET $charset " : '') . sprintf("%s {%d}\r\n%s", $criteria, strlen($str), $str);
      $results = $this->_search_index($mailbox, $search);
    // have an array of criterias => create search string
    if (is_array($criteria) && count($criteria) > 1)
      $search .= 'OR';
      // try search with ISO charset (should be supported by server)
      if (empty($results) && !empty($charset) && $charset!='ISO-8859-1')
        $results = $this->search($mbox_name, $criteria, rcube_charset_convert($str, $charset, 'ISO-8859-1'), 'ISO-8859-1');
      $this->set_search_set($criteria, $str, $results, $charset);
      return $results;
      }
    else
      return $this->_search_index($mailbox, $criteria);
    $criteria = (array) $criteria;
    foreach($criteria as $idx => $crit)
      if ($str)
        $search .= sprintf(" (%s {%d}\r\n%s)", $crit, strlen($str), $str);
      else
        $search .= '('. $crit .')';
    $results = $this->_search_index($mailbox, $search, $charset, $sort_field);
    // try search with ISO charset (should be supported by server)
    if (empty($results) && !empty($charset) && $charset!='ISO-8859-1')
      $results = $this->search($mbox_name, $criteria, rcube_charset_convert($str, $charset, 'ISO-8859-1'), 'ISO-8859-1', $sort_field);
    $this->set_search_set($criteria, $str, $results, $charset, $sort_field);
    return $results;
    }    
@@ -937,9 +948,13 @@
   * @access private
   * @see rcube_imap::search()
   */
  function _search_index($mailbox, $criteria='ALL')
  function _search_index($mailbox, $criteria='ALL', $charset='', $sort_field='')
    {
    $a_messages = iil_C_Search($this->conn, $mailbox, $criteria);
    if ($this->get_capability('sort'))
      $a_messages = iil_C_Sort($this->conn, $mailbox, $sort_field, $criteria, FALSE, $charset);
    else
      $a_messages = iil_C_Search($this->conn, $mailbox, ($charset ? "CHARSET $charset " : '') . $criteria);
    // clean message list (there might be some empty entries)
    if (is_array($a_messages))
      {
@@ -960,7 +975,7 @@
  function refresh_search()
    {
    if (!empty($this->search_subject) && !empty($this->search_string))
      $this->search_set = $this->search('', $this->search_subject, $this->search_string, $this->search_charset);
      $this->search_set = $this->search('', $this->search_subject, $this->search_string, $this->search_charset, $this->search_sort_field);
      
    return $this->get_search_set();
    }
@@ -1025,14 +1040,16 @@
    $headers = &$this->get_cached_message($cache_key, $uid, true);
    // return cached message structure
    if (is_object($headers) && is_object($headers->structure))
    if (is_object($headers) && is_object($headers->structure)) {
      return $headers->structure;
    // resolve message sequence number
    if (!($msg_id = $this->_uid2id($uid)))
      return FALSE;
    }
    $structure_str = iil_C_FetchStructureString($this->conn, $this->mailbox, $msg_id);
    // resolve message sequence number
    if (!($msg_id = $this->_uid2id($uid))) {
      return FALSE;
    }
    $structure_str = iil_C_FetchStructureString($this->conn, $this->mailbox, $msg_id);
    $structure = iml_GetRawStructureArray($structure_str);
    $struct = false;
@@ -1057,7 +1074,7 @@
      if ($this->caching_enabled)
        $this->add_message_cache($cache_key, $msg_id, $headers, $struct);
      }
    return $struct;
    }
@@ -1182,8 +1199,6 @@
    {
    if (!empty($part->d_parameters['filename']))
      $filename_mime = $part->d_parameters['filename'];
    else if (!empty($part->ctype_parameters['name']))
      $filename_mime = $part->ctype_parameters['name'];
    else if (!empty($part->d_parameters['filename*']))
      $filename_encoded = $part->d_parameters['filename*'];
    else if (!empty($part->ctype_parameters['name*']))
@@ -1193,9 +1208,9 @@
    else if (!empty($part->d_parameters['filename*0'])) {
      $i = 0;
      while (isset($part->d_parameters['filename*'.$i])) {
        $i++;
        $filename_mime .= $part->d_parameters['filename*'.$i];
   }
        $i++;
      }
      // some servers (eg. dovecot-1.x) have no support for parameter value continuations
      // we must fetch and parse headers "manually"
      //TODO: fetching headers for a second time is not effecient, this code should be moved somewhere earlier --tensor
@@ -1213,8 +1228,8 @@
    else if (!empty($part->d_parameters['filename*0*'])) {
      $i = 0;
      while (isset($part->d_parameters['filename*'.$i.'*'])) {
        $i++;
        $filename_encoded .= $part->d_parameters['filename*'.$i.'*'];
        $i++;
      }
      if ($i<2) {
        $headers = iil_C_FetchPartHeader($this->conn, $this->mailbox, $this->_msg_id, $part->mime_id);
@@ -1229,8 +1244,8 @@
    else if (!empty($part->ctype_parameters['name*0'])) {
      $i = 0;
      while (isset($part->ctype_parameters['name*'.$i])) {
        $i++;
        $filename_mime .= $part->ctype_parameters['name*'.$i];
        $i++;
      }
      if ($i<2) {
        $headers = iil_C_FetchPartHeader($this->conn, $this->mailbox, $this->_msg_id, $part->mime_id);
@@ -1245,8 +1260,8 @@
    else if (!empty($part->ctype_parameters['name*0*'])) {
      $i = 0;
      while (isset($part->ctype_parameters['name*'.$i.'*'])) {
        $i++;
        $filename_encoded .= $part->ctype_parameters['name*'.$i.'*'];
        $i++;
      }
      if ($i<2) {
        $headers = iil_C_FetchPartHeader($this->conn, $this->mailbox, $this->_msg_id, $part->mime_id);
@@ -1258,6 +1273,9 @@
        }
      }
    }
    // read 'name' after rfc2231 parameters as it may contains truncated filename (from Thunderbird)
    else if (!empty($part->ctype_parameters['name']))
      $filename_mime = $part->ctype_parameters['name'];
    // Content-Disposition
    else if (!empty($part->headers['content-description']))
      $filename_mime = $part->headers['content-description'];
@@ -1271,8 +1289,11 @@
      } 
    else if (!empty($filename_encoded)) {
      // decode filename according to RFC 2231, Section 4
      list($filename_charset,, $filename_urlencoded) = split('\'', $filename_encoded);
      $part->filename = rcube_charset_convert(urldecode($filename_urlencoded), $filename_charset);
      if (preg_match("/^([^']*)'[^']*'(.*)$/", $filename_encoded, $fmatches)) {
        $filename_charset = $fmatches[1];
        $filename_encoded = $fmatches[2];
        }
      $part->filename = rcube_charset_convert(urldecode($filename_encoded), $filename_charset);
      }
    }
     
@@ -1490,7 +1511,7 @@
    $mailbox = $this->_mod_mailbox($mbox_name);
    // make sure mailbox exists
    if (in_array($mailbox, $this->_list_mailboxes()))
    if (($mailbox == 'INBOX') || in_array($mailbox, $this->_list_mailboxes()))
      $saved = iil_C_Append($this->conn, $mailbox, $message);
    if ($saved)
@@ -2244,7 +2265,7 @@
  /**
   * @access private
   */  
  function get_message_cache_index($key, $force=FALSE, $sort_col='idx', $sort_order='ASC')
  function get_message_cache_index($key, $force=FALSE, $sort_field='idx', $sort_order='ASC')
    {
    static $sa_message_index = array();
    
@@ -2261,7 +2282,7 @@
       FROM ".get_table_name('messages')."
       WHERE  user_id=?
       AND    cache_key=?
       ORDER BY ".$this->db->quote_identifier($sort_col)." ".$sort_order,
       ORDER BY ".$this->db->quote_identifier($sort_field)." ".$sort_order,
      $_SESSION['user_id'],
      $key);
@@ -2663,13 +2684,15 @@
    // finally we must rebuild the list to move 
    // subfolders of default folders to their place...
    // ...also do this for the rest of folders because
    // asort() is not properly sorting case sensitive names
    // asort() is not properly sorting case sensitive names
    // set the type of folder name variable (#1485527)
    while (list($key, $folder) = each($folders)) {
      $a_out[] = $folder;
      $a_out[] = (string) $folder;
      unset($folders[$key]);
      foreach ($folders as $idx => $f) {
   if (strpos($f, $folder.$delimiter) === 0) {
         $a_out[] = $f;
         $a_out[] = (string) $f;
     unset($folders[$idx]);
     }
        }