thomascube
2012-01-16 c321a955a7b0f6d6b13ffaebf040a6c7091037ae
Merged devel-framework branch (r5746:5779) back into trunk

2 files added
37 files modified
1 files deleted
5663 ■■■■■ changed files
bin/msgexport.sh 19 ●●●●● patch | view | raw | blame | history
index.php 12 ●●●●● patch | view | raw | blame | history
installer/rcube_install.php 5 ●●●●● patch | view | raw | blame | history
program/include/iniset.php patch | view | raw | blame | history
program/include/main.inc 380 ●●●● patch | view | raw | blame | history
program/include/rcmail.php 265 ●●●●● patch | view | raw | blame | history
program/include/rcube_charset.php 745 ●●●●● patch | view | raw | blame | history
program/include/rcube_config.php 6 ●●●● patch | view | raw | blame | history
program/include/rcube_imap.php 2210 ●●●● patch | view | raw | blame | history
program/include/rcube_imap_cache.php 34 ●●●●● patch | view | raw | blame | history
program/include/rcube_message.php 22 ●●●● patch | view | raw | blame | history
program/include/rcube_result_index.php 26 ●●●● patch | view | raw | blame | history
program/include/rcube_result_thread.php 52 ●●●● patch | view | raw | blame | history
program/include/rcube_shared.inc 117 ●●●●● patch | view | raw | blame | history
program/include/rcube_storage.php 1055 ●●●●● patch | view | raw | blame | history
program/include/rcube_template.php 4 ●●●● patch | view | raw | blame | history
program/lib/utf7.inc 249 ●●●●● patch | view | raw | blame | history
program/localization/en_US/messages.inc 2 ●●● patch | view | raw | blame | history
program/steps/mail/check_recent.inc 32 ●●●●● patch | view | raw | blame | history
program/steps/mail/compose.inc 22 ●●●● patch | view | raw | blame | history
program/steps/mail/copy.inc 2 ●●● patch | view | raw | blame | history
program/steps/mail/folders.inc 6 ●●●● patch | view | raw | blame | history
program/steps/mail/func.inc 90 ●●●●● patch | view | raw | blame | history
program/steps/mail/get.inc 8 ●●●● patch | view | raw | blame | history
program/steps/mail/getunread.inc 6 ●●●● patch | view | raw | blame | history
program/steps/mail/headers.inc 2 ●●● patch | view | raw | blame | history
program/steps/mail/list.inc 23 ●●●● patch | view | raw | blame | history
program/steps/mail/mark.inc 45 ●●●●● patch | view | raw | blame | history
program/steps/mail/move_del.inc 43 ●●●●● patch | view | raw | blame | history
program/steps/mail/pagenav.inc 12 ●●●● patch | view | raw | blame | history
program/steps/mail/search.inc 20 ●●●●● patch | view | raw | blame | history
program/steps/mail/sendmail.inc 20 ●●●● patch | view | raw | blame | history
program/steps/mail/show.inc 14 ●●●● patch | view | raw | blame | history
program/steps/mail/viewsource.inc 4 ●●●● patch | view | raw | blame | history
program/steps/settings/edit_folder.inc 17 ●●●● patch | view | raw | blame | history
program/steps/settings/folders.inc 53 ●●●● patch | view | raw | blame | history
program/steps/settings/func.inc 18 ●●●●● patch | view | raw | blame | history
program/steps/settings/save_folder.inc 12 ●●●● patch | view | raw | blame | history
program/steps/settings/save_prefs.inc 8 ●●●● patch | view | raw | blame | history
tests/mailfunc.php 3 ●●●● patch | view | raw | blame | history
bin/msgexport.sh
@@ -31,10 +31,14 @@
{
    global $IMAP;
    $IMAP->set_mailbox($mbox);
    $IMAP->set_folder($mbox);
    $index = $IMAP->index($mbox, null, 'ASC');
    $count = $index->countMessages();
    $index = $index->get();
    vputs("Getting message list of {$mbox}...");
    vputs($IMAP->messagecount()." messages\n");
    vputs("$count messages\n");
    if ($filename)
    {
@@ -48,17 +52,16 @@
    else
        $out = STDOUT;
    for ($count = $IMAP->messagecount(), $i=1; $i <= $count; $i++)
    for ($i = 0; $i < $count; $i++)
    {
        $headers = $IMAP->get_headers($i, null, false);
        $headers = $IMAP->get_message_headers($index[$i]);
        $from = current(rcube_mime::decode_address_list($headers->from, 1, false));
        fwrite($out, sprintf("From %s %s UID %d\n", $from['mailto'], $headers->date, $headers->uid));
        fwrite($out, $IMAP->conn->fetchPartHeader($mbox, $i));
        fwrite($out, $IMAP->conn->handlePartBody($mbox, $i));
        fwrite($out, $IMAP->print_raw_body($headers->uid));
        fwrite($out, "\n\n\n");
        progress_update($i, $count);
        progress_update($i+1, $count);
    }
    vputs("\ncomplete.\n");
@@ -116,7 +119,7 @@
    vputs("IMAP login successful.\n");
    $filename = null;
    $mailboxes = $args['mbox'] == '*' ? $IMAP->list_mailboxes(null) : array($args['mbox']);
    $mailboxes = $args['mbox'] == '*' ? $IMAP->list_folders(null) : array($args['mbox']);
    foreach ($mailboxes as $mbox)
    {
index.php
@@ -48,7 +48,7 @@
}
// check DB connections and exit on failure
if ($err_str = $DB->is_error()) {
if ($err_str = $RCMAIL->db->is_error()) {
  raise_error(array(
    'code' => 603,
    'type' => 'db',
@@ -128,9 +128,9 @@
    $OUTPUT->redirect($redir);
  }
  else {
    $error_code = (isset($RCMAIL->imap) && is_object($RCMAIL->imap)) ? $RCMAIL->imap->get_error_code() : 1;
    $error_code = is_object($RCMAIL->storage) ? $RCMAIL->storage->get_error_code() : 1;
    $OUTPUT->show_message($error_code < -1 ? 'imaperror' : (!$auth['valid'] ? 'invalidrequest' : 'loginfailed'), 'warning');
    $OUTPUT->show_message($error_code < -1 ? 'storageerror' : (!$auth['valid'] ? 'invalidrequest' : 'loginfailed'), 'warning');
    $RCMAIL->plugins->exec_hook('login_failed', array(
      'code' => $error_code, 'host' => $auth['host'], 'user' => $auth['user']));
    $RCMAIL->kill_session();
@@ -139,7 +139,11 @@
// end session (after optional referer check)
else if ($RCMAIL->task == 'logout' && isset($_SESSION['user_id']) && (!$RCMAIL->config->get('referer_check') || rcube_check_referer())) {
  $userdata = array('user' => $_SESSION['username'], 'host' => $_SESSION['imap_host'], 'lang' => $RCMAIL->user->language);
  $userdata = array(
    'user' => $_SESSION['username'],
    'host' => $_SESSION['storage_host'],
    'lang' => $RCMAIL->user->language,
  );
  $OUTPUT->show_message('loggedout');
  $RCMAIL->logout_actions();
  $RCMAIL->kill_session();
installer/rcube_install.php
@@ -41,6 +41,7 @@
    'addrbook_show_images' => 'show_images',
    'imap_root' => 'imap_ns_personal',
    'pagesize' => 'mail_pagesize',
    'default_imap_folders' => 'default_folders',
  );
  // these config options are required for a working system
@@ -179,9 +180,9 @@
      else if ($prop == 'smtp_pass' && !empty($_POST['_smtp_user_u'])) {
        $value = '%p';
      }
      else if ($prop == 'default_imap_folders') {
      else if ($prop == 'default_folders') {
        $value = array();
        foreach ($this->config['default_imap_folders'] as $_folder) {
        foreach ($this->config['default_folders'] as $_folder) {
          switch ($_folder) {
          case 'Drafts': $_folder = $this->config['drafts_mbox']; break;
          case 'Sent':   $_folder = $this->config['sent_mbox']; break;
program/include/iniset.php
old mode 100755 new mode 100644
program/include/main.inc
@@ -26,7 +26,6 @@
 * @author Thomas Bruederli <roundcube@gmail.com>
 */
require_once 'utf7.inc';
require_once INSTALL_PATH . 'program/include/rcube_shared.inc';
// define constannts for input reading
@@ -183,336 +182,40 @@
}
/**
 * Catch an error and throw an exception.
 *
 * @param  int    Level of the error
 * @param  string Error message
 */
function rcube_error_handler($errno, $errstr)
{
  throw new ErrorException($errstr, 0, $errno);
}
/**
 * Convert a string from one charset to another.
 * Uses mbstring and iconv functions if possible
 *
 * @param  string Input string
 * @param  string Suspected charset of the input string
 * @param  string Target charset to convert to; defaults to RCMAIL_CHARSET
 * @return string Converted string
 */
// Deprecated
function rcube_charset_convert($str, $from, $to=NULL)
{
  static $iconv_options = null;
  static $mbstring_loaded = null;
  static $mbstring_list = null;
  static $conv = null;
    return rcube_charset::convert($str, $from, $to);
}
  $error = false;
  $to = empty($to) ? strtoupper(RCMAIL_CHARSET) : rcube_parse_charset($to);
  $from = rcube_parse_charset($from);
// Deprecated
function rc_detect_encoding($string, $failover='')
{
    return rcube_charset::detect($string, $failover);
}
  if ($from == $to || empty($str) || empty($from))
    return $str;
  // convert charset using iconv module
  if (function_exists('iconv') && $from != 'UTF7-IMAP' && $to != 'UTF7-IMAP') {
    if ($iconv_options === null) {
      // ignore characters not available in output charset
      $iconv_options = '//IGNORE';
      if (iconv('', $iconv_options, '') === false) {
        // iconv implementation does not support options
        $iconv_options = '';
      }
    }
    // throw an exception if iconv reports an illegal character in input
    // it means that input string has been truncated
    set_error_handler('rcube_error_handler', E_NOTICE);
    try {
      $_iconv = iconv($from, $to . $iconv_options, $str);
    } catch (ErrorException $e) {
      $_iconv = false;
    }
    restore_error_handler();
    if ($_iconv !== false) {
      return $_iconv;
    }
  }
  if ($mbstring_loaded === null)
    $mbstring_loaded = extension_loaded('mbstring');
  // convert charset using mbstring module
  if ($mbstring_loaded) {
    $aliases['WINDOWS-1257'] = 'ISO-8859-13';
    if ($mbstring_list === null) {
      $mbstring_list = mb_list_encodings();
      $mbstring_list = array_map('strtoupper', $mbstring_list);
    }
    $mb_from = $aliases[$from] ? $aliases[$from] : $from;
    $mb_to = $aliases[$to] ? $aliases[$to] : $to;
    // return if encoding found, string matches encoding and convert succeeded
    if (in_array($mb_from, $mbstring_list) && in_array($mb_to, $mbstring_list)) {
      if (mb_check_encoding($str, $mb_from) && ($out = mb_convert_encoding($str, $mb_to, $mb_from)))
        return $out;
    }
  }
  // convert charset using bundled classes/functions
  if ($to == 'UTF-8') {
    if ($from == 'UTF7-IMAP') {
      if ($_str = utf7_to_utf8($str))
        return $_str;
    }
    else if ($from == 'UTF-7') {
      if ($_str = rcube_utf7_to_utf8($str))
        return $_str;
    }
    else if (($from == 'ISO-8859-1') && function_exists('utf8_encode')) {
      return utf8_encode($str);
    }
    else if (class_exists('utf8')) {
      if (!$conv)
        $conv = new utf8($from);
      else
        $conv->loadCharset($from);
      if($_str = $conv->strToUtf8($str))
        return $_str;
    }
    $error = true;
  }
  // encode string for output
  if ($from == 'UTF-8') {
    // @TODO: we need a function for UTF-7 (RFC2152) conversion
    if ($to == 'UTF7-IMAP' || $to == 'UTF-7') {
      if ($_str = utf8_to_utf7($str))
        return $_str;
    }
    else if ($to == 'ISO-8859-1' && function_exists('utf8_decode')) {
      return utf8_decode($str);
    }
    else if (class_exists('utf8')) {
      if (!$conv)
        $conv = new utf8($to);
      else
        $conv->loadCharset($from);
      if ($_str = $conv->strToUtf8($str))
        return $_str;
    }
    $error = true;
  }
  // return UTF-8 or original string
  return $str;
// Deprecated
function rc_utf8_clean($input)
{
    return rcube_charset::clean($input);
}
/**
 * Parse and validate charset name string (see #1485758).
 * Sometimes charset string is malformed, there are also charset aliases
 * but we need strict names for charset conversion (specially utf8 class)
 * Convert a variable into a javascript object notation
 *
 * @param  string Input charset name
 * @return string The validated charset name
 * @param mixed Input value
 * @return string Serialized JSON string
 */
function rcube_parse_charset($input)
function json_serialize($input)
{
  static $charsets = array();
  $charset = strtoupper($input);
    $input = rcube_charset::clean($input);
  if (isset($charsets[$input]))
    return $charsets[$input];
  $charset = preg_replace(array(
    '/^[^0-9A-Z]+/',    // e.g. _ISO-8859-JP$SIO
    '/\$.*$/',          // e.g. _ISO-8859-JP$SIO
    '/UNICODE-1-1-*/',  // RFC1641/1642
    '/^X-/',            // X- prefix (e.g. X-ROMAN8 => ROMAN8)
    ), '', $charset);
  if ($charset == 'BINARY')
    return $charsets[$input] = null;
  # Aliases: some of them from HTML5 spec.
  $aliases = array(
    'USASCII'       => 'WINDOWS-1252',
    'ANSIX31101983' => 'WINDOWS-1252',
    'ANSIX341968'   => 'WINDOWS-1252',
    'UNKNOWN8BIT'   => 'ISO-8859-15',
    'UNKNOWN'       => 'ISO-8859-15',
    'USERDEFINED'   => 'ISO-8859-15',
    'KSC56011987'   => 'EUC-KR',
    'GB2312'         => 'GBK',
    'GB231280'        => 'GBK',
    'UNICODE'        => 'UTF-8',
    'UTF7IMAP'        => 'UTF7-IMAP',
    'TIS620'        => 'WINDOWS-874',
    'ISO88599'        => 'WINDOWS-1254',
    'ISO885911'        => 'WINDOWS-874',
    'MACROMAN'        => 'MACINTOSH',
    '77'            => 'MAC',
    '128'           => 'SHIFT-JIS',
    '129'           => 'CP949',
    '130'           => 'CP1361',
    '134'           => 'GBK',
    '136'           => 'BIG5',
    '161'           => 'WINDOWS-1253',
    '162'           => 'WINDOWS-1254',
    '163'           => 'WINDOWS-1258',
    '177'           => 'WINDOWS-1255',
    '178'           => 'WINDOWS-1256',
    '186'           => 'WINDOWS-1257',
    '204'           => 'WINDOWS-1251',
    '222'           => 'WINDOWS-874',
    '238'           => 'WINDOWS-1250',
    'MS950'         => 'CP950',
    'WINDOWS949'    => 'UHC',
  );
  // allow A-Z and 0-9 only
  $str = preg_replace('/[^A-Z0-9]/', '', $charset);
  if (isset($aliases[$str]))
    $result = $aliases[$str];
  // UTF
  else if (preg_match('/U[A-Z][A-Z](7|8|16|32)(BE|LE)*/', $str, $m))
    $result = 'UTF-' . $m[1] . $m[2];
  // ISO-8859
  else if (preg_match('/ISO8859([0-9]{0,2})/', $str, $m)) {
    $iso = 'ISO-8859-' . ($m[1] ? $m[1] : 1);
    // some clients sends windows-1252 text as latin1,
    // it is safe to use windows-1252 for all latin1
    $result = $iso == 'ISO-8859-1' ? 'WINDOWS-1252' : $iso;
  }
  // handle broken charset names e.g. WINDOWS-1250HTTP-EQUIVCONTENT-TYPE
  else if (preg_match('/(WIN|WINDOWS)([0-9]+)/', $str, $m)) {
    $result = 'WINDOWS-' . $m[2];
  }
  // LATIN
  else if (preg_match('/LATIN(.*)/', $str, $m)) {
    $aliases = array('2' => 2, '3' => 3, '4' => 4, '5' => 9, '6' => 10,
        '7' => 13, '8' => 14, '9' => 15, '10' => 16,
        'ARABIC' => 6, 'CYRILLIC' => 5, 'GREEK' => 7, 'GREEK1' => 7, 'HEBREW' => 8);
    // some clients sends windows-1252 text as latin1,
    // it is safe to use windows-1252 for all latin1
    if ($m[1] == 1) {
      $result = 'WINDOWS-1252';
    }
    // if iconv is not supported we need ISO labels, it's also safe for iconv
    else if (!empty($aliases[$m[1]])) {
      $result = 'ISO-8859-'.$aliases[$m[1]];
    }
    // iconv requires convertion of e.g. LATIN-1 to LATIN1
    else {
      $result = $str;
    }
  }
  else {
    $result = $charset;
  }
  $charsets[$input] = $result;
  return $result;
}
/**
 * Converts string from standard UTF-7 (RFC 2152) to UTF-8.
 *
 * @param  string  Input string
 * @return string  The converted string
 */
function rcube_utf7_to_utf8($str)
{
  $Index_64 = array(
    0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
    0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
    0,0,0,0, 0,0,0,0, 0,0,0,1, 0,0,0,0,
    1,1,1,1, 1,1,1,1, 1,1,0,0, 0,0,0,0,
    0,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1,
    1,1,1,1, 1,1,1,1, 1,1,1,0, 0,0,0,0,
    0,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1,
    1,1,1,1, 1,1,1,1, 1,1,1,0, 0,0,0,0,
  );
  $u7len = strlen($str);
  $str = strval($str);
  $res = '';
  for ($i=0; $u7len > 0; $i++, $u7len--)
  {
    $u7 = $str[$i];
    if ($u7 == '+')
    {
      $i++;
      $u7len--;
      $ch = '';
      for (; $u7len > 0; $i++, $u7len--)
      {
        $u7 = $str[$i];
        if (!$Index_64[ord($u7)])
          break;
    $ch .= $u7;
      }
      if ($ch == '') {
        if ($u7 == '-')
          $res .= '+';
        continue;
      }
      $res .= rcube_utf16_to_utf8(base64_decode($ch));
    }
    else
    {
      $res .= $u7;
    }
  }
  return $res;
}
/**
 * Converts string from UTF-16 to UTF-8 (helper for utf-7 to utf-8 conversion)
 *
 * @param  string  Input string
 * @return string  The converted string
 */
function rcube_utf16_to_utf8($str)
{
  $len = strlen($str);
  $dec = '';
  for ($i = 0; $i < $len; $i += 2) {
    $c = ord($str[$i]) << 8 | ord($str[$i + 1]);
    if ($c >= 0x0001 && $c <= 0x007F) {
      $dec .= chr($c);
    } else if ($c > 0x07FF) {
      $dec .= chr(0xE0 | (($c >> 12) & 0x0F));
      $dec .= chr(0x80 | (($c >>  6) & 0x3F));
      $dec .= chr(0x80 | (($c >>  0) & 0x3F));
    } else {
      $dec .= chr(0xC0 | (($c >>  6) & 0x1F));
      $dec .= chr(0x80 | (($c >>  0) & 0x3F));
    }
  }
  return $dec;
    // sometimes even using rcube_charset::clean() the input contains invalid UTF-8 sequences
    // that's why we have @ here
    return @json_encode($input);
}
@@ -1193,13 +896,13 @@
    $attrib['folder_name'] = '*';
  // get mailbox list
  $mbox_name = $RCMAIL->imap->get_mailbox_name();
  $mbox_name = $RCMAIL->storage->get_folder();
  // build the folders tree
  if (empty($a_mailboxes)) {
    // get mailbox list
    $a_folders = $RCMAIL->imap->list_mailboxes('', $attrib['folder_name'], $attrib['folder_filter']);
    $delimiter = $RCMAIL->imap->get_hierarchy_delimiter();
    $a_folders = $RCMAIL->storage->list_folders_subscribed('', $attrib['folder_name'], $attrib['folder_filter']);
    $delimiter = $RCMAIL->storage->get_hierarchy_delimiter();
    $a_mailboxes = array();
    foreach ($a_folders as $folder)
@@ -1245,16 +948,18 @@
  $p += array('maxlength' => 100, 'realnames' => false);
  $a_mailboxes = array();
  $storage = $RCMAIL->get_storage();
  if (empty($p['folder_name']))
  if (empty($p['folder_name'])) {
    $p['folder_name'] = '*';
  }
  if ($p['unsubscribed'])
    $list = $RCMAIL->imap->list_unsubscribed('', $p['folder_name'], $p['folder_filter'], $p['folder_rights']);
    $list = $storage->list_folders('', $p['folder_name'], $p['folder_filter'], $p['folder_rights']);
  else
    $list = $RCMAIL->imap->list_mailboxes('', $p['folder_name'], $p['folder_filter'], $p['folder_rights']);
    $list = $storage->list_folders_subscribed('', $p['folder_name'], $p['folder_filter'], $p['folder_rights']);
  $delimiter = $RCMAIL->imap->get_hierarchy_delimiter();
  $delimiter = $storage->get_hierarchy_delimiter();
  foreach ($list as $folder) {
    if (empty($p['exceptions']) || !in_array($folder, $p['exceptions']))
@@ -1285,7 +990,7 @@
  $prefix = '';
  if (!$path) {
    $n_folder = $folder;
    $folder = $RCMAIL->imap->mod_mailbox($folder);
    $folder = $RCMAIL->storage->mod_folder($folder);
    if ($n_folder != $folder) {
      $prefix = substr($n_folder, 0, -strlen($folder));
@@ -1340,7 +1045,7 @@
  $maxlength = intval($attrib['maxlength']);
  $realnames = (bool)$attrib['realnames'];
  $msgcounts = $RCMAIL->imap->get_cache('messagecount');
  $msgcounts = $RCMAIL->storage->get_cache('messagecount');
  $out = '';
  foreach ($arrFolders as $key => $folder) {
@@ -1432,7 +1137,7 @@
    }
    // skip folders in which it isn't possible to create subfolders
    if (!empty($opts['skip_noinferiors']) && ($attrs = $RCMAIL->imap->mailbox_attributes($folder['id']))
    if (!empty($opts['skip_noinferiors']) && ($attrs = $RCMAIL->storage->folder_attributes($folder['id']))
        && in_array('\\Noinferiors', $attrs)
    ) {
      continue;
@@ -1501,8 +1206,8 @@
    global $RCMAIL;
    $protect_folders = $RCMAIL->config->get('protect_default_folders');
    $default_folders = (array) $RCMAIL->config->get('default_imap_folders');
    $delimiter       = $RCMAIL->imap->get_hierarchy_delimiter();
    $default_folders = (array) $RCMAIL->config->get('default_folders');
    $delimiter       = $RCMAIL->storage->get_hierarchy_delimiter();
    $path            = explode($delimiter, $path);
    $result          = array();
@@ -1545,7 +1250,7 @@
{
  global $RCMAIL;
  $quota = $RCMAIL->imap->get_quota();
  $quota = $RCMAIL->storage->get_quota();
  $quota = $RCMAIL->plugins->exec_hook('quota', $quota);
  $quota_result = (array) $quota;
@@ -1591,16 +1296,19 @@
{
    global $RCMAIL;
    $err_code = $RCMAIL->imap->get_error_code();
    $res_code = $RCMAIL->imap->get_response_code();
    $err_code = $RCMAIL->storage->get_error_code();
    $res_code = $RCMAIL->storage->get_response_code();
    if ($res_code == rcube_imap::NOPERM) {
    if ($err_code < 0) {
        $RCMAIL->output->show_message('storageerror', 'error');
    }
    else if ($res_code == rcube_storage::NOPERM) {
        $RCMAIL->output->show_message('errornoperm', 'error');
    }
    else if ($res_code == rcube_imap::READONLY) {
    else if ($res_code == rcube_storage::READONLY) {
        $RCMAIL->output->show_message('errorreadonly', 'error');
    }
    else if ($err_code && ($err_str = $RCMAIL->imap->get_error_str())) {
    else if ($err_code && ($err_str = $RCMAIL->storage->get_error_str())) {
        // try to detect access rights problem and display appropriate message
        if (stripos($err_str, 'Permission denied') !== false)
            $RCMAIL->output->show_message('errornoperm', 'error');
@@ -1968,7 +1676,7 @@
  // %d - domain name without first part, e.g. %n=mail.domain.tld, %d=domain.tld
  $d = preg_replace('/^[^\.]+\./', '', $n);
  // %h - IMAP host
  $h = $_SESSION['imap_host'] ? $_SESSION['imap_host'] : $host;
  $h = $_SESSION['storage_host'] ? $_SESSION['storage_host'] : $host;
  // %z - IMAP domain without first part, e.g. %h=imap.domain.tld, %z=domain.tld
  $z = preg_replace('/^[^\.]+\./', '', $h);
  // %s - domain name after the '@' from e-mail address provided at login screen. Returns FALSE if an invalid email is provided
program/include/rcmail.php
@@ -86,11 +86,11 @@
  public $smtp;
  /**
   * Instance of rcube_imap class.
   * Instance of rcube_storage class.
   *
   * @var rcube_imap
   * @var rcube_storage
   */
  public $imap;
  public $storage;
  /**
   * Instance of rcube_template class.
@@ -172,9 +172,6 @@
    // connect to database
    $this->get_dbh();
    // set global object for backward compatibility
    $GLOBALS['DB'] = $this->db;
    // start session
    $this->session_init();
@@ -241,9 +238,6 @@
  {
    if (is_object($user)) {
      $this->user = $user;
      // set global object for backward compatibility
      $GLOBALS['USER'] = $this->user;
      // overwrite config with user preferences
      $this->config->set_user_prefs((array)$this->user->get_prefs());
@@ -424,7 +418,7 @@
      $contacts = $this->address_books[$id];
    }
    else if ($id && $ldap_config[$id]) {
      $contacts = new rcube_ldap($ldap_config[$id], $this->config->get('ldap_debug'), $this->config->mail_domain($_SESSION['imap_host']));
      $contacts = new rcube_ldap($ldap_config[$id], $this->config->get('ldap_debug'), $this->config->mail_domain($_SESSION['storage_host']));
    }
    else if ($id === '0') {
      $contacts = new rcube_contacts($this->db, $this->user->ID);
@@ -584,89 +578,145 @@
  /**
   * Create global IMAP object and connect to server
   * Initialize and get storage object
   *
   * @param boolean True if connection should be established
   * @return rcube_storage Storage object
   */
  public function imap_init($connect = false)
  public function get_storage()
  {
    // already initialized
    if (is_object($this->imap))
    if (!is_object($this->storage)) {
      $this->storage_init();
    }
    return $this->storage;
  }
  /**
   * Connect to the IMAP server with stored session data.
   *
   * @return bool True on success, False on error
   * @deprecated
   */
  public function imap_connect()
  {
    return $this->storage_connect();
  }
  /**
   * Initialize IMAP object.
   *
   * @deprecated
   */
  public function imap_init()
  {
    $this->storage_init();
  }
  /**
   * Initialize storage object
   */
  public function storage_init()
  {
    // already initialized
    if (is_object($this->storage)) {
      return;
    }
    $this->imap = new rcube_imap();
    $this->imap->skip_deleted = $this->config->get('skip_deleted');
    $driver = $this->config->get('storage_driver', 'imap');
    $driver_class = "rcube_{$driver}";
    // enable caching of imap data
    $imap_cache     = $this->config->get('imap_cache');
    if (!class_exists($driver_class)) {
      raise_error(array(
        'code' => 700, 'type' => 'php',
        'file' => __FILE__, 'line' => __LINE__,
        'message' => "Storage driver class ($driver) not found!"),
        true, true);
    }
    // Initialize storage object
    $this->storage = new $driver_class;
    // for backward compat. (deprecated, will be removed)
    $this->imap = $this->storage;
    // enable caching of mail data
    $storage_cache  = $this->config->get("{$driver}_cache");
    $messages_cache = $this->config->get('messages_cache');
    // for backward compatybility
    if ($imap_cache === null && $messages_cache === null && $this->config->get('enable_caching')) {
        $imap_cache     = 'db';
    if ($storage_cache === null && $messages_cache === null && $this->config->get('enable_caching')) {
        $storage_cache  = 'db';
        $messages_cache = true;
    }
    if ($imap_cache)
        $this->imap->set_caching($imap_cache);
    if ($storage_cache)
        $this->storage->set_caching($storage_cache);
    if ($messages_cache)
        $this->imap->set_messages_caching(true);
        $this->storage->set_messages_caching(true);
    // set pagesize from config
    $pagesize = $this->config->get('mail_pagesize');
    if (!$pagesize) {
        $pagesize = $this->config->get('pagesize', 50);
    }
    $this->imap->set_pagesize($pagesize);
    $this->storage->set_pagesize($pagesize);
    // set connection options
    // set class options
    $options = array(
      'auth_type'   => $this->config->get('imap_auth_type', 'check'),
      'auth_cid'    => $this->config->get('imap_auth_cid'),
      'auth_pw'     => $this->config->get('imap_auth_pw'),
      'debug'       => (bool) $this->config->get('imap_debug', 0),
      'force_caps'  => (bool) $this->config->get('imap_force_caps'),
      'timeout'     => (int) $this->config->get('imap_timeout', 0),
      'auth_type'   => $this->config->get("{$driver}_auth_type", 'check'),
      'auth_cid'    => $this->config->get("{$driver}_auth_cid"),
      'auth_pw'     => $this->config->get("{$driver}_auth_pw"),
      'debug'       => (bool) $this->config->get("{$driver}_debug"),
      'force_caps'  => (bool) $this->config->get("{$driver}_force_caps"),
      'timeout'     => (int) $this->config->get("{$driver}_timeout"),
      'skip_deleted' => (bool) $this->config->get('skip_deleted'),
      'driver'      => $driver,
    );
    $this->imap->set_options($options);
    // set global object for backward compatibility
    $GLOBALS['IMAP'] = $this->imap;
    $hook = $this->plugins->exec_hook('imap_init', array('fetch_headers' => $this->imap->fetch_add_headers));
    if ($hook['fetch_headers'])
      $this->imap->fetch_add_headers = $hook['fetch_headers'];
    // support this parameter for backward compatibility but log warning
    if ($connect) {
      $this->imap_connect();
      raise_error(array(
        'code' => 800, 'type' => 'imap',
        'file' => __FILE__, 'line' => __LINE__,
        'message' => "rcube::imap_init(true) is deprecated, use rcube::imap_connect() instead"),
        true, false);
    if (!empty($_SESSION['storage_host'])) {
      $options['host']     = $_SESSION['storage_host'];
      $options['user']     = $_SESSION['username'];
      $options['port']     = $_SESSION['storage_port'];
      $options['ssl']      = $_SESSION['storage_ssl'];
      $options['password'] = $this->decrypt($_SESSION['password']);
    }
    $options = $this->plugins->exec_hook("storage_init", $options);
    // for backward compat. (deprecated, to be removed)
    $options = $this->plugins->exec_hook("imap_init", $options);
    $this->storage->set_options($options);
    $this->set_storage_prop();
  }
  /**
   * Connect to IMAP server with stored session data
   * Connect to the mail storage server with stored session data
   *
   * @return bool True on success, false on error
   * @return bool True on success, False on error
   */
  public function imap_connect()
  public function storage_connect()
  {
    if (!$this->imap)
      $this->imap_init();
    $storage = $this->get_storage();
    if ($_SESSION['imap_host'] && !$this->imap->conn->connected()) {
      if (!$this->imap->connect($_SESSION['imap_host'], $_SESSION['username'], $this->decrypt($_SESSION['password']), $_SESSION['imap_port'], $_SESSION['imap_ssl'])) {
    if ($_SESSION['storage_host'] && !$storage->is_connected()) {
      $host = $_SESSION['storage_host'];
      $user = $_SESSION['username'];
      $port = $_SESSION['storage_port'];
      $ssl  = $_SESSION['storage_ssl'];
      $pass = $this->decrypt($_SESSION['password']);
      if (!$storage->connect($host, $user, $pass, $port, $ssl)) {
        if ($this->output)
          $this->output->show_message($this->imap->get_error_code() == -1 ? 'imaperror' : 'sessionerror', 'error');
          $this->output->show_message($storage->get_error_code() == -1 ? 'storageerror' : 'sessionerror', 'error');
      }
      else {
        $this->set_imap_prop();
        return $this->imap->conn;
        $this->set_storage_prop();
        return $storage->is_connected();
      }
    }
@@ -744,12 +794,12 @@
  /**
   * Perfom login to the IMAP server and to the webmail service.
   * Perfom login to the mail server and to the webmail service.
   * This will also create a new user entry if auto_create_user is configured.
   *
   * @param string IMAP user name
   * @param string IMAP password
   * @param string IMAP host
   * @param string Mail storage (IMAP) user name
   * @param string Mail storage (IMAP) password
   * @param string Mail storage (IMAP) host
   *
   * @return boolean True on success, False on failure
   */
@@ -785,14 +835,16 @@
    $a_host = parse_url($host);
    if ($a_host['host']) {
      $host = $a_host['host'];
      $imap_ssl = (isset($a_host['scheme']) && in_array($a_host['scheme'], array('ssl','imaps','tls'))) ? $a_host['scheme'] : null;
      $ssl = (isset($a_host['scheme']) && in_array($a_host['scheme'], array('ssl','imaps','tls'))) ? $a_host['scheme'] : null;
      if (!empty($a_host['port']))
        $imap_port = $a_host['port'];
      else if ($imap_ssl && $imap_ssl != 'tls' && (!$config['default_port'] || $config['default_port'] == 143))
        $imap_port = 993;
        $port = $a_host['port'];
      else if ($ssl && $ssl != 'tls' && (!$config['default_port'] || $config['default_port'] == 143))
        $port = 993;
    }
    $imap_port = $imap_port ? $imap_port : $config['default_port'];
    if (!$port) {
        $port = $config['default_port'];
    }
    /* Modify username with domain if required
       Inspired by Marco <P0L0_notspam_binware.org>
@@ -805,7 +857,7 @@
        $username .= '@'.rcube_parse_host($config['username_domain'], $host);
    }
    // Convert username to lowercase. If IMAP backend
    // Convert username to lowercase. If storage backend
    // is case-insensitive we need to store always the same username (#1487113)
    if ($config['login_lc']) {
      $username = mb_strtolower($username);
@@ -830,11 +882,11 @@
    if ($user = rcube_user::query($username, $host))
      $username = $user->data['username'];
    if (!$this->imap)
      $this->imap_init();
    if (!$this->storage)
      $this->storage_init();
    // try IMAP login
    if (!($imap_login = $this->imap->connect($host, $username, $pass, $imap_port, $imap_ssl))) {
    // try to log in
    if (!($login = $this->storage->connect($host, $username, $pass, $port, $ssl))) {
      // try with lowercase
      $username_lc = mb_strtolower($username);
      if ($username_lc != $username) {
@@ -842,14 +894,15 @@
        if (!$user && ($user = rcube_user::query($username_lc, $host)))
          $username_lc = $user->data['username'];
        if ($imap_login = $this->imap->connect($host, $username_lc, $pass, $imap_port, $imap_ssl))
        if ($login = $this->storage->connect($host, $username_lc, $pass, $port, $ssl))
          $username = $username_lc;
      }
    }
    // exit if IMAP login failed
    if (!$imap_login)
    // exit if login failed
    if (!$login) {
      return false;
    }
    // user already registered -> update user's record
    if (is_object($user)) {
@@ -881,7 +934,7 @@
    if (is_object($user) && $user->ID) {
      // Configure environment
      $this->set_user($user);
      $this->set_imap_prop();
      $this->set_storage_prop();
      $this->session_configure();
      // fix some old settings according to namespace prefix
@@ -889,17 +942,17 @@
      // create default folders on first login
      if ($config['create_default_folders'] && (!empty($created) || empty($user->data['last_login']))) {
        $this->imap->create_default_folders();
        $this->storage->create_default_folders();
      }
      // set session vars
      $_SESSION['user_id']   = $user->ID;
      $_SESSION['username']  = $user->data['username'];
      $_SESSION['imap_host'] = $host;
      $_SESSION['imap_port'] = $imap_port;
      $_SESSION['imap_ssl']  = $imap_ssl;
      $_SESSION['password']  = $this->encrypt($pass);
      $_SESSION['login_time'] = mktime();
      $_SESSION['user_id']      = $user->ID;
      $_SESSION['username']     = $user->data['username'];
      $_SESSION['storage_host'] = $host;
      $_SESSION['storage_port'] = $port;
      $_SESSION['storage_ssl']  = $ssl;
      $_SESSION['password']     = $this->encrypt($pass);
      $_SESSION['login_time']   = mktime();
      if (isset($_REQUEST['_timezone']) && $_REQUEST['_timezone'] != '_default_')
        $_SESSION['timezone'] = floatval($_REQUEST['_timezone']);
@@ -907,7 +960,7 @@
        $_SESSION['dst_active'] = intval($_REQUEST['_dstactive']);
      // force reloading complete list of subscribed mailboxes
      $this->imap->clear_cache('mailboxes', true);
      $this->storage->clear_cache('mailboxes', true);
      return true;
    }
@@ -917,21 +970,23 @@
  /**
   * Set root dir and last stored mailbox
   * Set storage parameters.
   * This must be done AFTER connecting to the server!
   */
  public function set_imap_prop()
  private function set_storage_prop()
  {
    $this->imap->set_charset($this->config->get('default_charset', RCMAIL_CHARSET));
    $storage = $this->get_storage();
    if ($default_folders = $this->config->get('default_imap_folders')) {
      $this->imap->set_default_mailboxes($default_folders);
    $storage->set_charset($this->config->get('default_charset', RCMAIL_CHARSET));
    if ($default_folders = $this->config->get('default_folders')) {
      $storage->set_default_folders($default_folders);
    }
    if (isset($_SESSION['mbox'])) {
      $this->imap->set_mailbox($_SESSION['mbox']);
      $storage->set_folder($_SESSION['mbox']);
    }
    if (isset($_SESSION['page'])) {
      $this->imap->set_page($_SESSION['page']);
      $storage->set_page($_SESSION['page']);
    }
  }
@@ -957,9 +1012,9 @@
      // try to select host by mail domain
      list($user, $domain) = explode('@', get_input_value('_user', RCUBE_INPUT_POST));
      if (!empty($domain)) {
        foreach ($default_host as $imap_host => $mail_domains) {
        foreach ($default_host as $storage_host => $mail_domains) {
          if (is_array($mail_domains) && in_array($domain, $mail_domains)) {
            $host = $imap_host;
            $host = $storage_host;
            break;
          }
        }
@@ -1167,15 +1222,15 @@
      if (!$this->session->check_auth())
        return;
      $this->imap_connect();
      $this->storage_connect();
    }
    if ($config['logout_purge'] && !empty($config['trash_mbox'])) {
      $this->imap->clear_mailbox($config['trash_mbox']);
      $this->storage->clear_folder($config['trash_mbox']);
    }
    if ($config['logout_expunge']) {
      $this->imap->expunge('INBOX');
      $this->storage->expunge_folder('INBOX');
    }
    // Try to save unsaved user preferences
@@ -1207,8 +1262,8 @@
            $cache->close();
    }
    if (is_object($this->imap))
      $this->imap->close();
    if (is_object($this->storage))
      $this->storage->close();
    // before closing the database connection, write session data
    if ($_SERVER['REMOTE_ADDR'] && is_object($this->session)) {
@@ -1607,7 +1662,7 @@
   */
  private function fix_namespace_settings($user)
  {
    $prefix     = $this->imap->get_namespace('prefix');
    $prefix     = $this->storage->get_namespace('prefix');
    $prefix_len = strlen($prefix);
    if (!$prefix_len)
@@ -1618,7 +1673,7 @@
      return;
    // Build namespace prefix regexp
    $ns     = $this->imap->get_namespace();
    $ns     = $this->storage->get_namespace();
    $regexp = array();
    foreach ($ns as $entry) {
@@ -1642,10 +1697,10 @@
      }
    }
    if (!empty($prefs['default_imap_folders'])) {
      foreach ($prefs['default_imap_folders'] as $idx => $name) {
    if (!empty($prefs['default_folders'])) {
      foreach ($prefs['default_folders'] as $idx => $name) {
        if ($name != 'INBOX' && !preg_match($regexp, $name)) {
          $prefs['default_imap_folders'][$idx] = $prefix.$name;
          $prefs['default_folders'][$idx] = $prefix.$name;
        }
      }
    }
@@ -1695,7 +1750,7 @@
    // save updated preferences and reset imap settings (default folders)
    $user->save_prefs($prefs);
    $this->set_imap_prop();
    $this->set_storage_prop();
  }
}
program/include/rcube_charset.php
New file
@@ -0,0 +1,745 @@
<?php
/*
 +-----------------------------------------------------------------------+
 | program/include/rcube_charset.php                                     |
 |                                                                       |
 | This file is part of the Roundcube Webmail client                     |
 | Copyright (C) 2005-2012, The Roundcube Dev Team                       |
 | Copyright (C) 2011-2012, Kolab Systems AG                             |
 | Copyright (C) 2000 Edmund Grimley Evans <edmundo@rano.org>            |
 | Licensed under the GNU GPL                                            |
 |                                                                       |
 | PURPOSE:                                                              |
 |   Provide charset conversion functionality                            |
 |                                                                       |
 +-----------------------------------------------------------------------+
 | Author: Thomas Bruederli <roundcube@gmail.com>                        |
 | Author: Aleksander Machniak <alec@alec.pl>                            |
 +-----------------------------------------------------------------------+
 $Id$
*/
/**
 * Character sets conversion functionality
 *
 * @package Core
 * @author Thomas Bruederli <roundcube@gmail.com>
 * @author Aleksander Machniak <alec@alec.pl>
 * @author Edmund Grimley Evans <edmundo@rano.org>
 */
class rcube_charset
{
    // Aliases: some of them from HTML5 spec.
    static public $aliases = array(
        'USASCII'       => 'WINDOWS-1252',
        'ANSIX31101983' => 'WINDOWS-1252',
        'ANSIX341968'   => 'WINDOWS-1252',
        'UNKNOWN8BIT'   => 'ISO-8859-15',
        'UNKNOWN'       => 'ISO-8859-15',
        'USERDEFINED'   => 'ISO-8859-15',
        'KSC56011987'   => 'EUC-KR',
        'GB2312'         => 'GBK',
        'GB231280'        => 'GBK',
        'UNICODE'        => 'UTF-8',
        'UTF7IMAP'        => 'UTF7-IMAP',
        'TIS620'        => 'WINDOWS-874',
        'ISO88599'        => 'WINDOWS-1254',
        'ISO885911'        => 'WINDOWS-874',
        'MACROMAN'        => 'MACINTOSH',
        '77'            => 'MAC',
        '128'           => 'SHIFT-JIS',
        '129'           => 'CP949',
        '130'           => 'CP1361',
        '134'           => 'GBK',
        '136'           => 'BIG5',
        '161'           => 'WINDOWS-1253',
        '162'           => 'WINDOWS-1254',
        '163'           => 'WINDOWS-1258',
        '177'           => 'WINDOWS-1255',
        '178'           => 'WINDOWS-1256',
        '186'           => 'WINDOWS-1257',
        '204'           => 'WINDOWS-1251',
        '222'           => 'WINDOWS-874',
        '238'           => 'WINDOWS-1250',
        'MS950'         => 'CP950',
        'WINDOWS949'    => 'UHC',
    );
    /**
     * Catch an error and throw an exception.
     *
     * @param  int    Level of the error
     * @param  string Error message
     */
    public function error_handler($errno, $errstr)
    {
        throw new ErrorException($errstr, 0, $errno);
    }
    /**
     * Parse and validate charset name string (see #1485758).
     * Sometimes charset string is malformed, there are also charset aliases
     * but we need strict names for charset conversion (specially utf8 class)
     *
     * @param  string Input charset name
     *
     * @return string The validated charset name
     */
    public static function parse($input)
    {
        static $charsets = array();
        $charset = strtoupper($input);
        if (isset($charsets[$input])) {
            return $charsets[$input];
        }
        $charset = preg_replace(array(
            '/^[^0-9A-Z]+/',    // e.g. _ISO-8859-JP$SIO
            '/\$.*$/',          // e.g. _ISO-8859-JP$SIO
            '/UNICODE-1-1-*/',  // RFC1641/1642
            '/^X-/',            // X- prefix (e.g. X-ROMAN8 => ROMAN8)
        ), '', $charset);
        if ($charset == 'BINARY') {
            return $charsets[$input] = null;
        }
        // allow A-Z and 0-9 only
        $str = preg_replace('/[^A-Z0-9]/', '', $charset);
        if (isset(self::$aliases[$str])) {
            $result = self::$aliases[$str];
        }
        // UTF
        else if (preg_match('/U[A-Z][A-Z](7|8|16|32)(BE|LE)*/', $str, $m)) {
            $result = 'UTF-' . $m[1] . $m[2];
        }
        // ISO-8859
        else if (preg_match('/ISO8859([0-9]{0,2})/', $str, $m)) {
            $iso = 'ISO-8859-' . ($m[1] ? $m[1] : 1);
            // some clients sends windows-1252 text as latin1,
            // it is safe to use windows-1252 for all latin1
            $result = $iso == 'ISO-8859-1' ? 'WINDOWS-1252' : $iso;
        }
        // handle broken charset names e.g. WINDOWS-1250HTTP-EQUIVCONTENT-TYPE
        else if (preg_match('/(WIN|WINDOWS)([0-9]+)/', $str, $m)) {
            $result = 'WINDOWS-' . $m[2];
        }
        // LATIN
        else if (preg_match('/LATIN(.*)/', $str, $m)) {
            $aliases = array('2' => 2, '3' => 3, '4' => 4, '5' => 9, '6' => 10,
                '7' => 13, '8' => 14, '9' => 15, '10' => 16,
                'ARABIC' => 6, 'CYRILLIC' => 5, 'GREEK' => 7, 'GREEK1' => 7, 'HEBREW' => 8
            );
            // some clients sends windows-1252 text as latin1,
            // it is safe to use windows-1252 for all latin1
            if ($m[1] == 1) {
                $result = 'WINDOWS-1252';
            }
            // if iconv is not supported we need ISO labels, it's also safe for iconv
            else if (!empty($aliases[$m[1]])) {
                $result = 'ISO-8859-'.$aliases[$m[1]];
            }
            // iconv requires convertion of e.g. LATIN-1 to LATIN1
            else {
                $result = $str;
            }
        }
        else {
            $result = $charset;
        }
        $charsets[$input] = $result;
        return $result;
    }
    /**
     * Convert a string from one charset to another.
     * Uses mbstring and iconv functions if possible
     *
     * @param  string Input string
     * @param  string Suspected charset of the input string
     * @param  string Target charset to convert to; defaults to RCMAIL_CHARSET
     *
     * @return string Converted string
     */
    public static function convert($str, $from, $to = null)
    {
        static $iconv_options   = null;
        static $mbstring_loaded = null;
        static $mbstring_list   = null;
        static $conv            = null;
        $to   = empty($to) ? strtoupper(RCMAIL_CHARSET) : self::parse($to);
        $from = self::parse($from);
        if ($from == $to || empty($str) || empty($from)) {
            return $str;
        }
        // convert charset using iconv module
        if (function_exists('iconv') && $from != 'UTF7-IMAP' && $to != 'UTF7-IMAP') {
            if ($iconv_options === null) {
                // ignore characters not available in output charset
                $iconv_options = '//IGNORE';
                if (iconv('', $iconv_options, '') === false) {
                    // iconv implementation does not support options
                    $iconv_options = '';
                }
            }
            // throw an exception if iconv reports an illegal character in input
            // it means that input string has been truncated
            set_error_handler(array('rcube_charset', 'error_handler'), E_NOTICE);
            try {
                $_iconv = iconv($from, $to . $iconv_options, $str);
            } catch (ErrorException $e) {
                $_iconv = false;
            }
            restore_error_handler();
            if ($_iconv !== false) {
                return $_iconv;
            }
        }
        if ($mbstring_loaded === null) {
            $mbstring_loaded = extension_loaded('mbstring');
        }
        // convert charset using mbstring module
        if ($mbstring_loaded) {
            $aliases['WINDOWS-1257'] = 'ISO-8859-13';
            if ($mbstring_list === null) {
                $mbstring_list = mb_list_encodings();
                $mbstring_list = array_map('strtoupper', $mbstring_list);
            }
            $mb_from = $aliases[$from] ? $aliases[$from] : $from;
            $mb_to   = $aliases[$to] ? $aliases[$to] : $to;
            // return if encoding found, string matches encoding and convert succeeded
            if (in_array($mb_from, $mbstring_list) && in_array($mb_to, $mbstring_list)) {
                if (mb_check_encoding($str, $mb_from) && ($out = mb_convert_encoding($str, $mb_to, $mb_from))) {
                    return $out;
                }
            }
        }
        // convert charset using bundled classes/functions
        if ($to == 'UTF-8') {
            if ($from == 'UTF7-IMAP') {
                if ($_str = self::utf7imap_to_utf8($str)) {
                    return $_str;
                }
            }
            else if ($from == 'UTF-7') {
                if ($_str = self::utf7_to_utf8($str)) {
                    return $_str;
                }
            }
            else if ($from == 'ISO-8859-1' && function_exists('utf8_encode')) {
                return utf8_encode($str);
            }
            else if (class_exists('utf8')) {
                if (!$conv) {
                    $conv = new utf8($from);
                }
                else {
                    $conv->loadCharset($from);
                }
                if ($_str = $conv->strToUtf8($str)) {
                    return $_str;
                }
            }
        }
        // encode string for output
        if ($from == 'UTF-8') {
            // @TODO: we need a function for UTF-7 (RFC2152) conversion
            if ($to == 'UTF7-IMAP' || $to == 'UTF-7') {
                if ($_str = utf8_to_utf7imap($str)) {
                    return $_str;
                }
            }
            else if ($to == 'ISO-8859-1' && function_exists('utf8_decode')) {
                return utf8_decode($str);
            }
            else if (class_exists('utf8')) {
                if (!$conv) {
                    $conv = new utf8($to);
                }
                else {
                    $conv->loadCharset($from);
                }
                if ($_str = $conv->strToUtf8($str)) {
                    return $_str;
                }
            }
        }
        // return original string
        return $str;
    }
    /**
     * Converts string from standard UTF-7 (RFC 2152) to UTF-8.
     *
     * @param  string  Input string (UTF-7)
     *
     * @return string  Converted string (UTF-8)
     */
    public static function utf7_to_utf8($str)
    {
        $Index_64 = array(
            0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
            0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
            0,0,0,0, 0,0,0,0, 0,0,0,1, 0,0,0,0,
            1,1,1,1, 1,1,1,1, 1,1,0,0, 0,0,0,0,
            0,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1,
            1,1,1,1, 1,1,1,1, 1,1,1,0, 0,0,0,0,
            0,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1,
            1,1,1,1, 1,1,1,1, 1,1,1,0, 0,0,0,0,
        );
        $u7len = strlen($str);
        $str   = strval($str);
        $res   = '';
        for ($i=0; $u7len > 0; $i++, $u7len--) {
            $u7 = $str[$i];
            if ($u7 == '+') {
                $i++;
                $u7len--;
                $ch = '';
                for (; $u7len > 0; $i++, $u7len--) {
                    $u7 = $str[$i];
                    if (!$Index_64[ord($u7)]) {
                        break;
                    }
                    $ch .= $u7;
                }
                if ($ch == '') {
                    if ($u7 == '-') {
                        $res .= '+';
                    }
                    continue;
                }
                $res .= self::utf16_to_utf8(base64_decode($ch));
            }
            else {
                $res .= $u7;
            }
        }
        return $res;
    }
    /**
     * Converts string from UTF-16 to UTF-8 (helper for utf-7 to utf-8 conversion)
     *
     * @param  string  Input string
     *
     * @return string  The converted string
     */
    public static function utf16_to_utf8($str)
    {
        $len = strlen($str);
        $dec = '';
        for ($i = 0; $i < $len; $i += 2) {
            $c = ord($str[$i]) << 8 | ord($str[$i + 1]);
            if ($c >= 0x0001 && $c <= 0x007F) {
                $dec .= chr($c);
            }
            else if ($c > 0x07FF) {
                $dec .= chr(0xE0 | (($c >> 12) & 0x0F));
                $dec .= chr(0x80 | (($c >>  6) & 0x3F));
                $dec .= chr(0x80 | (($c >>  0) & 0x3F));
            }
            else {
                $dec .= chr(0xC0 | (($c >>  6) & 0x1F));
                $dec .= chr(0x80 | (($c >>  0) & 0x3F));
            }
        }
        return $dec;
    }
    /**
     * Convert the data ($str) from RFC 2060's UTF-7 to UTF-8.
     * If input data is invalid, return the original input string.
     * RFC 2060 obviously intends the encoding to be unique (see
     * point 5 in section 5.1.3), so we reject any non-canonical
     * form, such as &ACY- (instead of &-) or &AMA-&AMA- (instead
     * of &AMAAwA-).
     *
     * Translated from C to PHP by Thomas Bruederli <roundcube@gmail.com>
     *
     * @param string $str Input string (UTF7-IMAP)
     *
     * @return string Output string (UTF-8)
     */
    public static function utf7imap_to_utf8($str)
    {
        $Index_64 = array(
            -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
            -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
            -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, 63,-1,-1,-1,
            52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1,-1,-1,-1,
            -1, 0, 1, 2,  3, 4, 5, 6,  7, 8, 9,10, 11,12,13,14,
            15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
            -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
            41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
        );
        $u7len = strlen($str);
        $str   = strval($str);
        $p     = '';
        $err   = '';
        for ($i=0; $u7len > 0; $i++, $u7len--) {
            $u7 = $str[$i];
            if ($u7 == '&') {
                $i++;
                $u7len--;
                $u7 = $str[$i];
                if ($u7len && $u7 == '-') {
                    $p .= '&';
                    continue;
                }
                $ch = 0;
                $k = 10;
                for (; $u7len > 0; $i++, $u7len--) {
                    $u7 = $str[$i];
                    if ((ord($u7) & 0x80) || ($b = $Index_64[ord($u7)]) == -1) {
                        break;
                    }
                    if ($k > 0) {
                        $ch |= $b << $k;
                        $k -= 6;
                    }
                    else {
                        $ch |= $b >> (-$k);
                        if ($ch < 0x80) {
                            // Printable US-ASCII
                            if (0x20 <= $ch && $ch < 0x7f) {
                                return $err;
                            }
                            $p .= chr($ch);
                        }
                        else if ($ch < 0x800) {
                            $p .= chr(0xc0 | ($ch >> 6));
                            $p .= chr(0x80 | ($ch & 0x3f));
                        }
                        else {
                            $p .= chr(0xe0 | ($ch >> 12));
                            $p .= chr(0x80 | (($ch >> 6) & 0x3f));
                            $p .= chr(0x80 | ($ch & 0x3f));
                        }
                        $ch = ($b << (16 + $k)) & 0xffff;
                        $k += 10;
                    }
                }
                // Non-zero or too many extra bits
                if ($ch || $k < 6) {
                    return $err;
                }
                // BASE64 not properly terminated
                if (!$u7len || $u7 != '-') {
                    return $err;
                }
                // Adjacent BASE64 sections
                if ($u7len > 2 && $str[$i+1] == '&' && $str[$i+2] != '-') {
                    return $err;
                }
            }
            // Not printable US-ASCII
            else if (ord($u7) < 0x20 || ord($u7) >= 0x7f) {
                return $err;
            }
            else {
                $p .= $u7;
            }
        }
        return $p;
    }
    /**
     * Convert the data ($str) from UTF-8 to RFC 2060's UTF-7.
     * Unicode characters above U+FFFF are replaced by U+FFFE.
     * If input data is invalid, return an empty string.
     *
     * Translated from C to PHP by Thomas Bruederli <roundcube@gmail.com>
     *
     * @param string $str Input string (UTF-8)
     *
     * @return string Output string (UTF7-IMAP)
     */
    public static function utf8_to_utf7imap($str)
    {
        $B64Chars = array(
            'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
            'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
            'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
            't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
            '8', '9', '+', ','
        );
        $u8len  = strlen($str);
        $base64 = 0;
        $i      = 0;
        $p      = '';
        $err    = '';
        while ($u8len) {
            $u8 = $str[$i];
            $c  = ord($u8);
            if ($c < 0x80) {
                $ch = $c;
                $n  = 0;
            }
            else if ($c < 0xc2) {
                return $err;
            }
            else if ($c < 0xe0) {
                $ch = $c & 0x1f;
                $n  = 1;
            }
            else if ($c < 0xf0) {
                $ch = $c & 0x0f;
                $n  = 2;
            }
            else if ($c < 0xf8) {
                $ch = $c & 0x07;
                $n  = 3;
            }
            else if ($c < 0xfc) {
                $ch = $c & 0x03;
                $n  = 4;
            }
            else if ($c < 0xfe) {
                $ch = $c & 0x01;
                $n  = 5;
            }
            else {
                return $err;
            }
            $i++;
            $u8len--;
            if ($n > $u8len) {
                return $err;
            }
            for ($j=0; $j < $n; $j++) {
                $o = ord($str[$i+$j]);
                if (($o & 0xc0) != 0x80) {
                    return $err;
                }
                $ch = ($ch << 6) | ($o & 0x3f);
            }
            if ($n > 1 && !($ch >> ($n * 5 + 1))) {
                return $err;
            }
            $i += $n;
            $u8len -= $n;
            if ($ch < 0x20 || $ch >= 0x7f) {
                if (!$base64) {
                    $p .= '&';
                    $base64 = 1;
                    $b = 0;
                    $k = 10;
                }
                if ($ch & ~0xffff) {
                    $ch = 0xfffe;
                }
                $p .= $B64Chars[($b | $ch >> $k)];
                $k -= 6;
                for (; $k >= 0; $k -= 6) {
                    $p .= $B64Chars[(($ch >> $k) & 0x3f)];
                }
                $b = ($ch << (-$k)) & 0x3f;
                $k += 16;
            }
            else {
                if ($base64) {
                    if ($k > 10) {
                        $p .= $B64Chars[$b];
                    }
                    $p .= '-';
                    $base64 = 0;
                }
                $p .= chr($ch);
                if (chr($ch) == '&') {
                    $p .= '-';
                }
            }
        }
        if ($base64) {
            if ($k > 10) {
                $p .= $B64Chars[$b];
            }
            $p .= '-';
        }
        return $p;
    }
    /**
     * A method to guess character set of a string.
     *
     * @param string $string    String.
     * @param string $failover     Default result for failover.
     *
     * @return string Charset name
     */
    public static function detect($string, $failover='')
    {
        if (!function_exists('mb_detect_encoding')) {
            return $failover;
        }
        // FIXME: the order is important, because sometimes
        // iso string is detected as euc-jp and etc.
        $enc = array(
            'UTF-8', 'SJIS', 'BIG5', 'GB2312',
            'ISO-8859-1', 'ISO-8859-2', 'ISO-8859-3', 'ISO-8859-4',
            'ISO-8859-5', 'ISO-8859-6', 'ISO-8859-7', 'ISO-8859-8', 'ISO-8859-9',
            'ISO-8859-10', 'ISO-8859-13', 'ISO-8859-14', 'ISO-8859-15', 'ISO-8859-16',
            'WINDOWS-1252', 'WINDOWS-1251', 'EUC-JP', 'EUC-TW', 'KOI8-R',
            'ISO-2022-KR', 'ISO-2022-JP'
        );
        $result = mb_detect_encoding($string, join(',', $enc));
        return $result ? $result : $failover;
    }
    /**
     * Removes non-unicode characters from input.
     *
     * @param mixed $input String or array.
     *
     * @return mixed String or array
     */
    public static function clean($input)
    {
        // handle input of type array
        if (is_array($input)) {
            foreach ($input as $idx => $val) {
                $input[$idx] = self::clean($val);
            }
            return $input;
        }
        if (!is_string($input) || $input == '') {
            return $input;
        }
        // iconv/mbstring are much faster (especially with long strings)
        if (function_exists('mb_convert_encoding')) {
            if (($res = mb_convert_encoding($input, 'UTF-8', 'UTF-8')) !== false) {
                return $res;
            }
        }
        if (function_exists('iconv')) {
            if (($res = @iconv('UTF-8', 'UTF-8//IGNORE', $input)) !== false) {
                return $res;
            }
        }
        $seq    = '';
        $out    = '';
        $regexp = '/^('.
//          '[\x00-\x7F]'.                                  // UTF8-1
            '|[\xC2-\xDF][\x80-\xBF]'.                      // UTF8-2
            '|\xE0[\xA0-\xBF][\x80-\xBF]'.                  // UTF8-3
            '|[\xE1-\xEC][\x80-\xBF][\x80-\xBF]'.           // UTF8-3
            '|\xED[\x80-\x9F][\x80-\xBF]'.                  // UTF8-3
            '|[\xEE-\xEF][\x80-\xBF][\x80-\xBF]'.           // UTF8-3
            '|\xF0[\x90-\xBF][\x80-\xBF][\x80-\xBF]'.       // UTF8-4
            '|[\xF1-\xF3][\x80-\xBF][\x80-\xBF][\x80-\xBF]'.// UTF8-4
            '|\xF4[\x80-\x8F][\x80-\xBF][\x80-\xBF]'.       // UTF8-4
            ')$/';
        for ($i = 0, $len = strlen($input); $i < $len; $i++) {
            $chr = $input[$i];
            $ord = ord($chr);
            // 1-byte character
            if ($ord <= 0x7F) {
                if ($seq) {
                    $out .= preg_match($regexp, $seq) ? $seq : '';
                }
                $seq = '';
                $out .= $chr;
            // first (or second) byte of multibyte sequence
            }
            else if ($ord >= 0xC0) {
                if (strlen($seq) > 1) {
                    $out .= preg_match($regexp, $seq) ? $seq : '';
                    $seq = '';
                }
                else if ($seq && ord($seq) < 0xC0) {
                    $seq = '';
                }
                $seq .= $chr;
            // next byte of multibyte sequence
            }
            else if ($seq) {
                $seq .= $chr;
            }
        }
        if ($seq) {
            $out .= preg_match($regexp, $seq) ? $seq : '';
        }
        return $out;
    }
}
program/include/rcube_config.php
@@ -72,9 +72,9 @@
        foreach (array('drafts_mbox', 'junk_mbox', 'sent_mbox', 'trash_mbox') as $folder)
            $this->prop[$folder] = rcube_charset_convert($this->prop[$folder], RCMAIL_CHARSET, 'UTF7-IMAP');
        if (!empty($this->prop['default_imap_folders']))
            foreach ($this->prop['default_imap_folders'] as $n => $folder)
                $this->prop['default_imap_folders'][$n] = rcube_charset_convert($folder, RCMAIL_CHARSET, 'UTF7-IMAP');
        if (!empty($this->prop['default_folders']))
            foreach ($this->prop['default_folders'] as $n => $folder)
                $this->prop['default_folders'][$n] = rcube_charset_convert($folder, RCMAIL_CHARSET, 'UTF7-IMAP');
        // set PHP error logging according to config
        if ($this->prop['debug_level'] & 1) {
program/include/rcube_imap.php
Diff too large
program/include/rcube_imap_cache.php
@@ -135,7 +135,7 @@
            // We've got a valid index
            else if ($sort_field == 'ANY' || $this->icache[$mailbox]['index']['sort_field'] == $sort_field) {
                $result = $this->icache[$mailbox]['index']['object'];
                if ($result->getParameters('ORDER') != $sort_order) {
                if ($result->get_parameters('ORDER') != $sort_order) {
                    $result->revert();
                }
                return $result;
@@ -178,7 +178,7 @@
            if ($is_valid) {
                $data = $index['object'];
                // revert the order if needed
                if ($data->getParameters('ORDER') != $sort_order) {
                if ($data->get_parameters('ORDER') != $sort_order) {
                    $data->revert();
                }
            }
@@ -198,7 +198,7 @@
        // Index not found, not valid or sort field changed, get index from IMAP server
        if ($data === null) {
            // Get mailbox data (UIDVALIDITY, counters, etc.) for status check
            $mbox_data = $this->imap->mailbox_data($mailbox);
            $mbox_data = $this->imap->folder_data($mailbox);
            $data      = $this->get_index_data($mailbox, $sort_field, $sort_order, $mbox_data);
            // insert/update
@@ -259,7 +259,7 @@
        // Index not found or not valid, get index from IMAP server
        if ($index === null) {
            // Get mailbox data (UIDVALIDITY, counters, etc.) for status check
            $mbox_data = $this->imap->mailbox_data($mailbox);
            $mbox_data = $this->imap->folder_data($mailbox);
            if ($mbox_data['EXISTS']) {
                // get all threads (default sort order)
@@ -371,7 +371,7 @@
        // Get the message from IMAP server
        if (empty($message) && $update) {
            $message = $this->imap->get_headers($uid, $mailbox, true);
            $message = $this->imap->get_message_headers($uid, $mailbox, true);
            // cache will be updated in close(), see below
        }
@@ -740,7 +740,7 @@
        $is_thread = is_a($object, 'rcube_result_thread');
        // Get mailbox data (UIDVALIDITY, counters, etc.) for status check
        $mbox_data = $this->imap->mailbox_data($mailbox);
        $mbox_data = $this->imap->folder_data($mailbox);
        // @TODO: Think about skipping validation checks.
        // If we could check only every 10 minutes, we would be able to skip
@@ -757,14 +757,14 @@
        // Folder is empty but cache isn't
        if (empty($mbox_data['EXISTS'])) {
            if (!$object->isEmpty()) {
            if (!$object->is_empty()) {
                $this->clear($mailbox);
                $exists = false;
                return false;
            }
        }
        // Folder is not empty but cache is
        else if ($object->isEmpty()) {
        else if ($object->is_empty()) {
            unset($this->icache[$mailbox][$is_thread ? 'thread' : 'index']);
            return false;
        }
@@ -796,7 +796,7 @@
        // @TODO: find better validity check for threaded index
        if ($is_thread) {
            // check messages number...
            if (!$this->skip_deleted && $mbox_data['EXISTS'] != $object->countMessages()) {
            if (!$this->skip_deleted && $mbox_data['EXISTS'] != $object->count_messages()) {
                return false;
            }
            return true;
@@ -830,7 +830,7 @@
                $ids = $this->imap->search_once($mailbox, 'ALL UNDELETED NOT UID '.
                    rcube_imap_generic::compressMessageSet($object->get()));
                if (!$ids->isEmpty()) {
                if (!$ids->is_empty()) {
                    return false;
                }
            }
@@ -888,6 +888,10 @@
            return;
        }
        if (!$this->imap->check_connection()) {
            return;
        }
        // NOTE: make sure the mailbox isn't selected, before
        // enabling QRESYNC and invoking SELECT
        if ($this->imap->conn->selected !== null) {
@@ -901,7 +905,7 @@
        }
        // Get mailbox data (UIDVALIDITY, HIGHESTMODSEQ, counters, etc.)
        $mbox_data = $this->imap->mailbox_data($mailbox);
        $mbox_data = $this->imap->folder_data($mailbox);
        if (empty($mbox_data)) {
             return;
@@ -986,7 +990,7 @@
        // Get VANISHED
        if ($qresync) {
            $mbox_data = $this->imap->mailbox_data($mailbox);
            $mbox_data = $this->imap->folder_data($mailbox);
            // Removed messages
            if (!empty($mbox_data['VANISHED'])) {
@@ -1004,7 +1008,7 @@
        }
        $sort_field = $index['sort_field'];
        $sort_order = $index['object']->getParameters('ORDER');
        $sort_order = $index['object']->get_parameters('ORDER');
        $exists     = true;
        // Validate index
@@ -1103,12 +1107,12 @@
    private function get_index_data($mailbox, $sort_field, $sort_order, $mbox_data = array())
    {
        if (empty($mbox_data)) {
            $mbox_data = $this->imap->mailbox_data($mailbox);
            $mbox_data = $this->imap->folder_data($mailbox);
        }
        if ($mbox_data['EXISTS']) {
            // fetch sorted sequence numbers
            $index = $this->imap->message_index_direct($mailbox, $sort_field, $sort_order);
            $index = $this->imap->index_direct($mailbox, $sort_field, $sort_order);
        }
        else {
            $index = new rcube_result_index($mailbox, '* SORT');
program/include/rcube_message.php
@@ -76,10 +76,10 @@
    {
        $this->uid  = $uid;
        $this->app  = rcmail::get_instance();
        $this->imap = $this->app->imap;
        $this->imap->get_all_headers = true;
        $this->storage = $this->app->get_storage();
        $this->storage->set_options(array('all_headers' => true));
        $this->headers = $this->imap->get_message($uid);
        $this->headers = $this->storage->get_message($uid);
        if (!$this->headers)
            return;
@@ -94,7 +94,7 @@
            'safe' => $this->is_safe,
            'prefer_html' => $this->app->config->get('prefer_html'),
            'get_url' => rcmail_url('get', array(
                '_mbox' => $this->imap->get_mailbox_name(), '_uid' => $uid))
                '_mbox' => $this->storage->get_folder(), '_uid' => $uid))
        );
        if (!empty($this->headers->structure)) {
@@ -102,7 +102,7 @@
            $this->parse_structure($this->headers->structure);
        }
        else {
            $this->body = $this->imap->get_body($uid);
            $this->body = $this->storage->get_body($uid);
        }
        // notify plugins and let them analyze this structured message object
@@ -176,7 +176,7 @@
                return $fp ? true : $part->body;
            }
            // get from IMAP
            return $this->imap->get_message_part($this->uid, $mime_id, $part, NULL, $fp);
            return $this->storage->get_message_part($this->uid, $mime_id, $part, NULL, $fp);
        } else
            return null;
    }
@@ -211,7 +211,7 @@
        foreach ($this->mime_parts as $mime_id => $part) {
            $mimetype = strtolower($part->ctype_primary . '/' . $part->ctype_secondary);
            if ($mimetype == 'text/html') {
                return $this->imap->get_message_part($this->uid, $mime_id, $part);
                return $this->storage->get_message_part($this->uid, $mime_id, $part);
            }
        }
    }
@@ -234,10 +234,10 @@
            $mimetype = $part->ctype_primary . '/' . $part->ctype_secondary;
            if ($mimetype == 'text/plain') {
                return $this->imap->get_message_part($this->uid, $mime_id, $part);
                return $this->storage->get_message_part($this->uid, $mime_id, $part);
            }
            else if ($mimetype == 'text/html') {
                $out = $this->imap->get_message_part($this->uid, $mime_id, $part);
                $out = $this->storage->get_message_part($this->uid, $mime_id, $part);
                // remove special chars encoding
                $trans = array_flip(get_html_translation_table(HTML_ENTITIES));
@@ -590,7 +590,7 @@
    {
        // @TODO: attachment may be huge, hadle it via file
        if (!isset($part->body))
            $part->body = $this->imap->get_message_part($this->uid, $part->mime_id, $part);
            $part->body = $this->storage->get_message_part($this->uid, $part->mime_id, $part);
        $parts = array();
        $tnef = new tnef_decoder;
@@ -626,7 +626,7 @@
    {
        // @TODO: messages may be huge, hadle body via file
        if (!isset($part->body))
            $part->body = $this->imap->get_message_part($this->uid, $part->mime_id, $part);
            $part->body = $this->storage->get_message_part($this->uid, $part->mime_id, $part);
        $parts = array();
        // FIXME: line length is max.65?
program/include/rcube_result_index.php
@@ -27,11 +27,11 @@
 */
class rcube_result_index
{
    private $raw_data;
    private $mailbox;
    private $meta = array();
    private $params = array();
    private $order = 'ASC';
    protected $raw_data;
    protected $mailbox;
    protected $meta = array();
    protected $params = array();
    protected $order = 'ASC';
    const SEPARATOR_ELEMENT = ' ';
@@ -126,7 +126,7 @@
     *
     * @return bool True if the result is an error, False otherwise
     */
    public function isError()
    public function is_error()
    {
        return $this->raw_data === null ? true : false;
    }
@@ -137,7 +137,7 @@
     *
     * @return bool True if the result is empty, False otherwise
     */
    public function isEmpty()
    public function is_empty()
    {
        return empty($this->raw_data) ? true : false;
    }
@@ -171,7 +171,7 @@
     *
     * @return int Number of elements
     */
    public function countMessages()
    public function count_messages()
    {
        return $this->count();
    }
@@ -305,7 +305,7 @@
                if ($m[0][1]) {
                    $idx = 1 + substr_count($this->raw_data, self::SEPARATOR_ELEMENT, 0, $m[0][1]);
                }
                // cache position of this element, so we can use it in getElement()
                // cache position of this element, so we can use it in get_element()
                $this->meta['pos'][$idx] = (int)$m[0][1];
                return $idx;
@@ -336,7 +336,7 @@
     *
     * @return array List of message IDs
     */
    public function getCompressed()
    public function get_compressed()
    {
        if (empty($this->raw_data)) {
            return '';
@@ -353,7 +353,7 @@
     *
     * @return int Element value
     */
    public function getElement($index)
    public function get_element($index)
    {
        $count = $this->count();
@@ -414,7 +414,7 @@
     *
     * @return array|string Response parameters or parameter value
     */
    public function getParameters($param=null)
    public function get_parameters($param=null)
    {
        $params = $this->params;
        $params['MAILBOX'] = $this->mailbox;
@@ -433,7 +433,7 @@
     *
     * @return int Data length
     */
    private function length()
    protected function length()
    {
        if (!isset($this->meta['length'])) {
            $this->meta['length'] = strlen($this->raw_data);
program/include/rcube_result_thread.php
@@ -27,10 +27,10 @@
 */
class rcube_result_thread
{
    private $raw_data;
    private $mailbox;
    private $meta = array();
    private $order = 'ASC';
    protected $raw_data;
    protected $mailbox;
    protected $meta = array();
    protected $order = 'ASC';
    const SEPARATOR_ELEMENT = ' ';
    const SEPARATOR_ITEM    = '~';
@@ -77,7 +77,7 @@
        $data = preg_replace('/[\r\n]/', '', $data);
        $data = preg_replace('/\s+/', ' ', $data);
        $this->raw_data = $this->parseThread($data);
        $this->raw_data = $this->parse_thread($data);
    }
@@ -86,7 +86,7 @@
     *
     * @return bool True if the result is an error, False otherwise
     */
    public function isError()
    public function is_error()
    {
        return $this->raw_data === null ? true : false;
    }
@@ -97,7 +97,7 @@
     *
     * @return bool True if the result is empty, False otherwise
     */
    public function isEmpty()
    public function is_empty()
    {
        return empty($this->raw_data) ? true : false;
    }
@@ -132,7 +132,7 @@
     *
     * @return int Number of elements
     */
    public function countMessages()
    public function count_messages()
    {
        if ($this->meta['messages'] !== null)
            return $this->meta['messages'];
@@ -300,7 +300,7 @@
                    $idx = substr_count($this->raw_data, self::SEPARATOR_ELEMENT, 0, $m[0][1]+1)
                        + substr_count($this->raw_data, self::SEPARATOR_ITEM, 0, $m[0][1]+1);
                }
                // cache position of this element, so we can use it in getElement()
                // cache position of this element, so we can use it in get_element()
                $this->meta['pos'][$idx] = (int)$m[0][1];
                return $idx;
@@ -336,7 +336,7 @@
     *
     * @return array List of message identifiers
     */
    public function getCompressed()
    public function get_compressed()
    {
        if (empty($this->raw_data)) {
            return '';
@@ -353,7 +353,7 @@
     *
     * @return int Element value
     */
    public function getElement($index)
    public function get_element($index)
    {
        $count = $this->count();
@@ -423,7 +423,7 @@
     *
     * @return array|string Response parameters or parameter value
     */
    public function getParameters($param=null)
    public function get_parameters($param=null)
    {
        $params = $this->params;
        $params['MAILBOX'] = $this->mailbox;
@@ -444,14 +444,14 @@
     */
    public function sort($index)
    {
        $this->sort_order = $index->getParameters('ORDER');
        $this->sort_order = $index->get_parameters('ORDER');
        if (empty($this->raw_data)) {
            return;
        }
        // when sorting search result it's good to make the index smaller
        if ($index->count() != $this->countMessages()) {
        if ($index->count() != $this->count_messages()) {
            $index->intersect($this->get());
        }
@@ -510,7 +510,7 @@
     *
     * @return array Data tree
     */
    public function getTree()
    public function get_tree()
    {
        $datalen = strlen($this->raw_data);
        $result  = array();
@@ -522,7 +522,7 @@
            $len   = $pos - $start;
            $elem  = substr($this->raw_data, $start, $len);
            $items = explode(self::SEPARATOR_ITEM, $elem);
            $result[array_shift($items)] = $this->buildThread($items);
            $result[array_shift($items)] = $this->build_thread($items);
            $start = $pos + 1;
        }
@@ -535,13 +535,13 @@
     *
     * @return array Thread data
     */
    public function getThreadData()
    public function get_thread_data()
    {
        $data     = $this->getTree();
        $data     = $this->get_tree();
        $depth    = array();
        $children = array();
        $this->buildThreadData($data, $depth, $children);
        $this->build_thread_data($data, $depth, $children);
        return array($depth, $children);
    }
@@ -550,14 +550,14 @@
    /**
     * Creates 'depth' and 'children' arrays from stored thread 'tree' data.
     */
    private function buildThreadData($data, &$depth, &$children, $level = 0)
    protected function build_thread_data($data, &$depth, &$children, $level = 0)
    {
        foreach ((array)$data as $key => $val) {
            $empty          = empty($val) || !is_array($val);
            $children[$key] = !$empty;
            $depth[$key]    = $level;
            if (!$empty) {
                $this->buildThreadData($val, $depth, $children, $level + 1);
                $this->build_thread_data($val, $depth, $children, $level + 1);
            }
        }
    }
@@ -566,7 +566,7 @@
    /**
     * Converts part of the raw thread into an array
     */
    private function buildThread($items, $level = 1, &$pos = 0)
    protected function build_thread($items, $level = 1, &$pos = 0)
    {
        $result = array();
@@ -574,7 +574,7 @@
            list($lv, $id) = explode(self::SEPARATOR_LEVEL, $items[$pos]);
            if ($level == $lv) {
                $pos++;
                $result[$id] = $this->buildThread($items, $level+1, $pos);
                $result[$id] = $this->build_thread($items, $level+1, $pos);
            }
            else {
                $pos--;
@@ -589,7 +589,7 @@
    /**
     * IMAP THREAD response parser
     */
    private function parseThread($str, $begin = 0, $end = 0, $depth = 0)
    protected function parse_thread($str, $begin = 0, $end = 0, $depth = 0)
    {
        // Don't be tempted to change $str to pass by reference to speed this up - it will slow it down by about
        // 7 times instead :-) See comments on http://uk2.php.net/references and this article:
@@ -627,7 +627,7 @@
            $node .= ($depth ? self::SEPARATOR_ITEM.$depth.self::SEPARATOR_LEVEL : '').$msg;
            if ($stop + 1 < $end) {
                $node .= $this->parseThread($str, $stop + 1, $end, $depth + 1);
                $node .= $this->parse_thread($str, $stop + 1, $end, $depth + 1);
            }
        } else {
            $off = $begin;
@@ -652,7 +652,7 @@
                    }
                }
                $thread = $this->parseThread($str, $start + 1, $off - 1, $depth);
                $thread = $this->parse_thread($str, $start + 1, $off - 1, $depth);
                if ($thread) {
                    if (!$depth) {
                        if ($node) {
program/include/rcube_shared.inc
@@ -419,123 +419,6 @@
/**
 * A method to guess encoding of a string.
 *
 * @param string $string         String.
 * @param string $failover     Default result for failover.
 *
 * @return string
 */
function rc_detect_encoding($string, $failover='')
{
    if (!function_exists('mb_detect_encoding')) {
        return $failover;
    }
    // FIXME: the order is important, because sometimes
    // iso string is detected as euc-jp and etc.
    $enc = array(
      'UTF-8', 'SJIS', 'BIG5', 'GB2312',
      'ISO-8859-1', 'ISO-8859-2', 'ISO-8859-3', 'ISO-8859-4',
      'ISO-8859-5', 'ISO-8859-6', 'ISO-8859-7', 'ISO-8859-8', 'ISO-8859-9',
      'ISO-8859-10', 'ISO-8859-13', 'ISO-8859-14', 'ISO-8859-15', 'ISO-8859-16',
      'WINDOWS-1252', 'WINDOWS-1251', 'EUC-JP', 'EUC-TW', 'KOI8-R',
      'ISO-2022-KR', 'ISO-2022-JP'
    );
    $result = mb_detect_encoding($string, join(',', $enc));
    return $result ? $result : $failover;
}
/**
 * Removes non-unicode characters from input
 *
 * @param mixed $input String or array.
 * @return string
 */
function rc_utf8_clean($input)
{
  // handle input of type array
  if (is_array($input)) {
    foreach ($input as $idx => $val)
      $input[$idx] = rc_utf8_clean($val);
    return $input;
  }
  if (!is_string($input) || $input == '')
    return $input;
  // iconv/mbstring are much faster (especially with long strings)
  if (function_exists('mb_convert_encoding') && ($res = mb_convert_encoding($input, 'UTF-8', 'UTF-8')) !== false)
    return $res;
  if (function_exists('iconv') && ($res = @iconv('UTF-8', 'UTF-8//IGNORE', $input)) !== false)
    return $res;
  $regexp = '/^('.
//    '[\x00-\x7F]'.                                  // UTF8-1
    '|[\xC2-\xDF][\x80-\xBF]'.                      // UTF8-2
    '|\xE0[\xA0-\xBF][\x80-\xBF]'.                  // UTF8-3
    '|[\xE1-\xEC][\x80-\xBF][\x80-\xBF]'.           // UTF8-3
    '|\xED[\x80-\x9F][\x80-\xBF]'.                  // UTF8-3
    '|[\xEE-\xEF][\x80-\xBF][\x80-\xBF]'.           // UTF8-3
    '|\xF0[\x90-\xBF][\x80-\xBF][\x80-\xBF]'.       // UTF8-4
    '|[\xF1-\xF3][\x80-\xBF][\x80-\xBF][\x80-\xBF]'.// UTF8-4
    '|\xF4[\x80-\x8F][\x80-\xBF][\x80-\xBF]'.       // UTF8-4
    ')$/';
  $seq = '';
  $out = '';
  for ($i = 0, $len = strlen($input); $i < $len; $i++) {
    $chr = $input[$i];
    $ord = ord($chr);
    // 1-byte character
    if ($ord <= 0x7F) {
      if ($seq)
        $out .= preg_match($regexp, $seq) ? $seq : '';
      $seq = '';
      $out .= $chr;
    // first (or second) byte of multibyte sequence
    } else if ($ord >= 0xC0) {
      if (strlen($seq)>1) {
    $out .= preg_match($regexp, $seq) ? $seq : '';
        $seq = '';
      } else if ($seq && ord($seq) < 0xC0) {
        $seq = '';
      }
      $seq .= $chr;
    // next byte of multibyte sequence
    } else if ($seq) {
      $seq .= $chr;
    }
  }
  if ($seq)
    $out .= preg_match($regexp, $seq) ? $seq : '';
  return $out;
}
/**
 * Convert a variable into a javascript object notation
 *
 * @param mixed Input value
 * @return string Serialized JSON string
 */
function json_serialize($input)
{
  $input = rc_utf8_clean($input);
  // sometimes even using rc_utf8_clean() the input contains invalid UTF-8 sequences
  // that's why we have @ here
  return @json_encode($input);
}
/**
 * Explode quoted string
 * 
 * @param string Delimiter expression string for preg_match()
program/include/rcube_storage.php
New file
@@ -0,0 +1,1055 @@
<?php
/*
 +-----------------------------------------------------------------------+
 | program/include/rcube_storage.php                                     |
 |                                                                       |
 | This file is part of the Roundcube Webmail client                     |
 | Copyright (C) 2005-2012, The Roundcube Dev Team                       |
 | Copyright (C) 2012, Kolab Systems AG                                  |
 | Licensed under the GNU GPL                                            |
 |                                                                       |
 | PURPOSE:                                                              |
 |   Mail Storage Engine                                                 |
 |                                                                       |
 +-----------------------------------------------------------------------+
 | Author: Thomas Bruederli <roundcube@gmail.com>                        |
 | Author: Aleksander Machniak <alec@alec.pl>                            |
 +-----------------------------------------------------------------------+
 $Id$
*/
/**
 * Abstract class for accessing mail messages storage server
 *
 * @package Mail
 * @author  Thomas Bruederli <roundcube@gmail.com>
 * @author  Aleksander Machniak <alec@alec.pl>
 * @version 2.0
 */
abstract class rcube_storage
{
    /**
     * Instance of connection object e.g. rcube_imap_generic
     *
     * @var mixed
     */
    public $conn;
    protected $folder = 'INBOX';
    protected $default_charset = 'ISO-8859-1';
    protected $default_folders = array('INBOX');
    protected $search_set;
    protected $options = array('auth_method' => 'check');
    protected $page_size = 10;
    protected $threading = false;
    /**
     * All (additional) headers used (in any way) by Roundcube
     * Not listed here: DATE, FROM, TO, CC, REPLY-TO, SUBJECT, CONTENT-TYPE, LIST-POST
     * (used for messages listing) are hardcoded in rcube_imap_generic::fetchHeaders()
     *
     * @var array
     */
    protected $all_headers = array(
        'IN-REPLY-TO',
        'BCC',
        'MESSAGE-ID',
        'CONTENT-TRANSFER-ENCODING',
        'REFERENCES',
        'X-DRAFT-INFO',
        'MAIL-FOLLOWUP-TO',
        'MAIL-REPLY-TO',
        'RETURN-PATH',
    );
    const UNKNOWN       = 0;
    const NOPERM        = 1;
    const READONLY      = 2;
    const TRYCREATE     = 3;
    const INUSE         = 4;
    const OVERQUOTA     = 5;
    const ALREADYEXISTS = 6;
    const NONEXISTENT   = 7;
    const CONTACTADMIN  = 8;
    /**
     * Connect to the server
     *
     * @param  string   $host    Host to connect
     * @param  string   $user    Username for IMAP account
     * @param  string   $pass    Password for IMAP account
     * @param  integer  $port    Port to connect to
     * @param  string   $use_ssl SSL schema (either ssl or tls) or null if plain connection
     *
     * @return boolean  TRUE on success, FALSE on failure
     */
    abstract function connect($host, $user, $pass, $port = 143, $use_ssl = null);
    /**
     * Close connection. Usually done on script shutdown
     */
    abstract function close();
    /**
     * Checks connection state.
     *
     * @return boolean  TRUE on success, FALSE on failure
     */
    abstract function is_connected();
    /**
     * Returns code of last error
     *
     * @return int Error code
     */
    abstract function get_error_code();
    /**
     * Returns message of last error
     *
     * @return string Error message
     */
    abstract function get_error_str();
    /**
     * Returns code of last command response
     *
     * @return int Response code (class constant)
     */
    abstract function get_response_code();
    /**
     * Set connection and class options
     *
     * @param array $opt Options array
     */
    public function set_options($opt)
    {
        $this->options = array_merge($this->options, (array)$opt);
    }
    /**
     * Activate/deactivate debug mode.
     *
     * @param boolean $dbg True if conversation with the server should be logged
     */
    abstract function set_debug($dbg = true);
    /**
     * Set default message charset.
     *
     * This will be used for message decoding if a charset specification is not available
     *
     * @param  string $cs Charset string
     */
    public function set_charset($cs)
    {
        $this->default_charset = $cs;
    }
    /**
     * This list of folders will be listed above all other folders
     *
     * @param  array $arr Indexed list of folder names
     */
    public function set_default_folders($arr)
    {
        if (is_array($arr)) {
            $this->default_folders = $arr;
            // add inbox if not included
            if (!in_array('INBOX', $this->default_folders)) {
                array_unshift($this->default_folders, 'INBOX');
            }
        }
    }
    /**
     * Set internal folder reference.
     * All operations will be perfomed on this folder.
     *
     * @param  string $folder  Folder name
     */
    public function set_folder($folder)
    {
        if ($this->folder == $folder) {
            return;
        }
        $this->folder = $folder;
    }
    /**
     * Returns the currently used folder name
     *
     * @return string Name of the folder
     */
    public function get_folder()
    {
        return $this->folder;
    }
    /**
     * Set internal list page number.
     *
     * @param int $page Page number to list
     */
    public function set_page($page)
    {
        $this->list_page = (int) $page;
    }
    /**
     * Gets internal list page number.
     *
     * @return int Page number
     */
    public function get_page()
    {
        return $this->list_page;
    }
    /**
     * Set internal page size
     *
     * @param int $size Number of messages to display on one page
     */
    public function set_pagesize($size)
    {
        $this->page_size = (int) $size;
    }
    /**
     * Get internal page size
     *
     * @return int Number of messages to display on one page
     */
    public function get_pagesize()
    {
        return $this->page_size;
    }
    /**
     * Save a search result for future message listing methods.
     *
     * @param  mixed  $set  Search set in driver specific format
     */
    abstract function set_search_set($set);
    /**
     * Return the saved search set.
     *
     * @return array Search set in driver specific format, NULL if search wasn't initialized
     */
    abstract function get_search_set();
    /**
     * Returns the storage server's (IMAP) capability
     *
     * @param   string  $cap Capability name
     *
     * @return  mixed   Capability value or TRUE if supported, FALSE if not
     */
    abstract function get_capability($cap);
    /**
     * Sets threading flag to the best supported THREAD algorithm.
     * Enable/Disable threaded mode.
     *
     * @param  boolean  $enable TRUE to enable and FALSE
     *
     * @return mixed   Threading algorithm or False if THREAD is not supported
     */
    public function set_threading($enable = false)
    {
        $this->threading = false;
        if ($enable && ($caps = $this->get_capability('THREAD'))) {
            $methods = array('REFS', 'REFERENCES', 'ORDEREDSUBJECT');
            $methods = array_intersect($methods, $caps);
            $this->threading = array_shift($methods);
        }
        return $this->threading;
    }
    /**
     * Get current threading flag.
     *
     * @return mixed  Threading algorithm or False if THREAD is not supported or disabled
     */
    public function get_threading()
    {
        return $this->threading;
    }
    /**
     * Checks the PERMANENTFLAGS capability of the current folder
     * and returns true if the given flag is supported by the server.
     *
     * @param   string  $flag Permanentflag name
     *
     * @return  boolean True if this flag is supported
     */
    abstract function check_permflag($flag);
    /**
     * Returns the delimiter that is used by the server
     * for folder hierarchy separation.
     *
     * @return  string  Delimiter string
     */
    abstract function get_hierarchy_delimiter();
    /**
     * Get namespace
     *
     * @param string $name Namespace array index: personal, other, shared, prefix
     *
     * @return  array  Namespace data
     */
    abstract function get_namespace($name = null);
    /**
     * Get messages count for a specific folder.
     *
     * @param  string  $folder  Folder name
     * @param  string  $mode    Mode for count [ALL|THREADS|UNSEEN|RECENT]
     * @param  boolean $force   Force reading from server and update cache
     * @param  boolean $status  Enables storing folder status info (max UID/count),
     *                          required for folder_status()
     *
     * @return int     Number of messages
     */
    abstract function count($folder = null, $mode = 'ALL', $force = false, $status = true);
    /**
     * Public method for listing headers.
     *
     * @param   string   $folder     Folder name
     * @param   int      $page       Current page to list
     * @param   string   $sort_field Header field to sort by
     * @param   string   $sort_order Sort order [ASC|DESC]
     * @param   int      $slice      Number of slice items to extract from result array
     *
     * @return  array    Indexed array with message header objects
     */
    abstract function list_messages($folder = null, $page = null, $sort_field = null, $sort_order = null, $slice = 0);
    /**
     * Return sorted list of message UIDs
     *
     * @param string $folder     Folder to get index from
     * @param string $sort_field Sort column
     * @param string $sort_order Sort order [ASC, DESC]
     *
     * @return rcube_result_index|rcube_result_thread List of messages (UIDs)
     */
    abstract function index($folder = null, $sort_field = null, $sort_order = null);
    /**
     * Invoke search request to the server.
     *
     * @param  string  $folder     Folder name to search in
     * @param  string  $str        Search criteria
     * @param  string  $charset    Search charset
     * @param  string  $sort_field Header field to sort by
     *
     * @todo: Search criteria should be provided in non-IMAP format, eg. array
     */
    abstract function search($folder = null, $str = 'ALL', $charset = null, $sort_field = null);
    /**
     * Direct (real and simple) search request (without result sorting and caching).
     *
     * @param  string  $folder  Folder name to search in
     * @param  string  $str     Search string
     *
     * @return rcube_result_index  Search result (UIDs)
     */
    abstract function search_once($folder = null, $str = 'ALL');
    /**
     * Refresh saved search set
     *
     * @return array Current search set
     */
    abstract function refresh_search();
    /* --------------------------------
     *        messages management
     * --------------------------------*/
    /**
     * Fetch message headers and body structure from the server and build
     * an object structure similar to the one generated by PEAR::Mail_mimeDecode
     *
     * @param int     $uid     Message UID to fetch
     * @param string  $folder  Folder to read from
     *
     * @return object rcube_mail_header Message data
     */
    abstract function get_message($uid, $folder = null);
    /**
     * Return message headers object of a specific message
     *
     * @param int     $id       Message sequence ID or UID
     * @param string  $folder   Folder to read from
     * @param bool    $force    True to skip cache
     *
     * @return rcube_mail_header Message headers
     */
    abstract function get_message_headers($uid, $folder = null, $force = false);
    /**
     * Fetch message body of a specific message from the server
     *
     * @param  int                $uid    Message UID
     * @param  string             $part   Part number
     * @param  rcube_message_part $o_part Part object created by get_structure()
     * @param  mixed              $print  True to print part, ressource to write part contents in
     * @param  resource           $fp     File pointer to save the message part
     * @param  boolean            $skip_charset_conv Disables charset conversion
     *
     * @return string Message/part body if not printed
     */
    abstract function get_message_part($uid, $part = 1, $o_part = null, $print = null, $fp = null, $skip_charset_conv = false);
    /**
     * Fetch message body of a specific message from the server
     *
     * @param  int    $uid  Message UID
     *
     * @return string $part Message/part body
     * @see    rcube_imap::get_message_part()
     */
    public function get_body($uid, $part = 1)
    {
        $headers = $this->get_message_headers($uid);
        return rcube_charset_convert($this->get_message_part($uid, $part, null),
            $headers->charset ? $headers->charset : $this->default_charset);
    }
    /**
     * Returns the whole message source as string (or saves to a file)
     *
     * @param int      $uid Message UID
     * @param resource $fp  File pointer to save the message
     *
     * @return string Message source string
     */
    abstract function get_raw_body($uid, $fp = null);
    /**
     * Returns the message headers as string
     *
     * @param int $uid  Message UID
     *
     * @return string Message headers string
     */
    abstract function get_raw_headers($uid);
    /**
     * Sends the whole message source to stdout
     */
    abstract function print_raw_body($uid);
    /**
     * Set message flag to one or several messages
     *
     * @param mixed   $uids       Message UIDs as array or comma-separated string, or '*'
     * @param string  $flag       Flag to set: SEEN, UNDELETED, DELETED, RECENT, ANSWERED, DRAFT, MDNSENT
     * @param string  $folder     Folder name
     * @param boolean $skip_cache True to skip message cache clean up
     *
     * @return bool  Operation status
     */
    abstract function set_flag($uids, $flag, $folder = null, $skip_cache = false);
    /**
     * Remove message flag for one or several messages
     *
     * @param mixed  $uids    Message UIDs as array or comma-separated string, or '*'
     * @param string $flag    Flag to unset: SEEN, DELETED, RECENT, ANSWERED, DRAFT, MDNSENT
     * @param string $folder  Folder name
     *
     * @return bool   Operation status
     * @see set_flag
     */
    public function unset_flag($uids, $flag, $folder = null)
    {
        return $this->set_flag($uids, 'UN'.$flag, $folder);
    }
    /**
     * Append a mail message (source) to a specific folder.
     *
     * @param string  $folder  Target folder
     * @param string  $message The message source string or filename
     * @param string  $headers Headers string if $message contains only the body
     * @param boolean $is_file True if $message is a filename
     *
     * @return int|bool Appended message UID or True on success, False on error
     */
    abstract function save_message($folder, &$message, $headers = '', $is_file = false);
    /**
     * Move message(s) from one folder to another.
     *
     * @param mixed  $uids  Message UIDs as array or comma-separated string, or '*'
     * @param string $to    Target folder
     * @param string $from  Source folder
     *
     * @return boolean True on success, False on error
     */
    abstract function move_message($uids, $to, $from = null);
    /**
     * Copy message(s) from one mailbox to another.
     *
     * @param mixed  $uids  Message UIDs as array or comma-separated string, or '*'
     * @param string $to    Target folder
     * @param string $from  Source folder
     *
     * @return boolean True on success, False on error
     */
    abstract function copy_message($uids, $to, $from = null);
    /**
     * Mark message(s) as deleted and expunge.
     *
     * @param mixed  $uids    Message UIDs as array or comma-separated string, or '*'
     * @param string $folder  Source folder
     *
     * @return boolean True on success, False on error
     */
    abstract function delete_message($uids, $folder = null);
    /**
     * Expunge message(s) and clear the cache.
     *
     * @param mixed   $uids        Message UIDs as array or comma-separated string, or '*'
     * @param string  $folder      Folder name
     * @param boolean $clear_cache False if cache should not be cleared
     *
     * @return boolean True on success, False on error
     */
    abstract function expunge_message($uids, $folder = null, $clear_cache = true);
    /**
     * Parse message UIDs input
     *
     * @param mixed  $uids  UIDs array or comma-separated list or '*' or '1:*'
     *
     * @return array Two elements array with UIDs converted to list and ALL flag
     */
    protected function parse_uids($uids)
    {
        if ($uids === '*' || $uids === '1:*') {
            if (empty($this->search_set)) {
                $uids = '1:*';
                $all = true;
            }
            // get UIDs from current search set
            else {
                $uids = join(',', $this->search_set->get());
            }
        }
        else {
            if (is_array($uids)) {
                $uids = join(',', $uids);
            }
            if (preg_match('/[^0-9,]/', $uids)) {
                $uids = '';
            }
        }
        return array($uids, (bool) $all);
    }
    /* --------------------------------
     *        folder managment
     * --------------------------------*/
    /**
     * Get a list of subscribed folders.
     *
     * @param   string  $root      Optional root folder
     * @param   string  $name      Optional name pattern
     * @param   string  $filter    Optional filter
     * @param   string  $rights    Optional ACL requirements
     * @param   bool    $skip_sort Enable to return unsorted list (for better performance)
     *
     * @return  array   List of folders
     */
    abstract function list_folders_subscribed($root = '', $name = '*', $filter = null, $rights = null, $skip_sort = false);
    /**
     * Get a list of all folders available on the server.
     *
     * @param string  $root      IMAP root dir
     * @param string  $name      Optional name pattern
     * @param mixed   $filter    Optional filter
     * @param string  $rights    Optional ACL requirements
     * @param bool    $skip_sort Enable to return unsorted list (for better performance)
     *
     * @return array Indexed array with folder names
     */
    abstract function list_folders($root = '', $name = '*', $filter = null, $rights = null, $skip_sort = false);
    /**
     * Subscribe to a specific folder(s)
     *
     * @param array $folders Folder name(s)
     *
     * @return boolean True on success
     */
    abstract function subscribe($folders);
    /**
     * Unsubscribe folder(s)
     *
     * @param array $folders Folder name(s)
     *
     * @return boolean True on success
     */
    abstract function unsubscribe($folders);
    /**
     * Create a new folder on the server.
     *
     * @param string  $folder    New folder name
     * @param boolean $subscribe True if the newvfolder should be subscribed
     *
     * @return boolean True on success, False on error
     */
    abstract function create_folder($folder, $subscribe = false);
    /**
     * Set a new name to an existing folder
     *
     * @param string $folder   Folder to rename
     * @param string $new_name New folder name
     *
     * @return boolean True on success, False on error
     */
    abstract function rename_folder($folder, $new_name);
    /**
     * Remove a folder from the server.
     *
     * @param string $folder Folder name
     *
     * @return boolean True on success, False on error
     */
    abstract function delete_folder($folder);
    /**
     * Send expunge command and clear the cache.
     *
     * @param string  $folder      Folder name
     * @param boolean $clear_cache False if cache should not be cleared
     *
     * @return boolean True on success, False on error
     */
    public function expunge_folder($folder = null, $clear_cache = true)
    {
        return $this->expunge_message('*', $folder, $clear_cache);
    }
    /**
     * Remove all messages in a folder..
     *
     * @param string  $folder  Folder name
     *
     * @return boolean True on success, False on error
     */
    public function clear_folder($folder = null)
    {
        return $this->delete_message('*', $folder);
    }
    /**
     * Checks if folder exists and is subscribed
     *
     * @param string   $folder       Folder name
     * @param boolean  $subscription Enable subscription checking
     *
     * @return boolean True if folder exists, False otherwise
     */
    abstract function folder_exists($folder, $subscription = false);
    /**
     * Get folder size (size of all messages in a folder)
     *
     * @param string $folder Folder name
     *
     * @return int Folder size in bytes, False on error
     */
    abstract function folder_size($folder);
    /**
     * Returns the namespace where the folder is in
     *
     * @param string $folder Folder name
     *
     * @return string One of 'personal', 'other' or 'shared'
     */
    abstract function folder_namespace($folder);
    /**
     * Gets folder attributes (from LIST response, e.g. \Noselect, \Noinferiors).
     *
     * @param string $folder  Folder name
     * @param bool   $force   Set to True if attributes should be refreshed
     *
     * @return array Options list
     */
    abstract function folder_attributes($folder, $force = false);
    /**
     * Gets connection (and current folder) data: UIDVALIDITY, EXISTS, RECENT,
     * PERMANENTFLAGS, UIDNEXT, UNSEEN
     *
     * @param string $folder Folder name
     *
     * @return array Data
     */
    abstract function folder_data($folder);
    /**
     * Returns extended information about the folder.
     *
     * @param string $folder Folder name
     *
     * @return array Data
     */
    abstract function folder_info($folder);
    /**
     * Returns current status of a folder
     *
     * @param string $folder Folder name
     *
     * @return int Folder status
     */
    abstract function folder_status($folder = null);
    /**
     * Synchronizes messages cache.
     *
     * @param string $folder Folder name
     */
    abstract function folder_sync($folder);
    /**
     * Modify folder name according to namespace.
     * For output it removes prefix of the personal namespace if it's possible.
     * For input it adds the prefix. Use it before creating a folder in root
     * of the folders tree.
     *
     * @param string $folder  Folder name
     * @param string $mode    Mode name (out/in)
     *
     * @return string Folder name
     */
    abstract function mod_folder($folder, $mode = 'out');
    /**
     * Create all folders specified as default
     */
    public function create_default_folders()
    {
        // create default folders if they do not exist
        foreach ($this->default_folders as $folder) {
            if (!$this->folder_exists($folder)) {
                $this->create_folder($folder, true);
            }
            else if (!$this->folder_exists($folder, true)) {
                $this->subscribe($folder);
            }
        }
    }
    /**
     * Get mailbox quota information.
     *
     * @return mixed Quota info or False if not supported
     */
    abstract function get_quota();
    /* -----------------------------------------
     *   ACL and METADATA methods
     * ----------------------------------------*/
    /**
     * Changes the ACL on the specified folder (SETACL)
     *
     * @param string $folder  Folder name
     * @param string $user    User name
     * @param string $acl     ACL string
     *
     * @return boolean True on success, False on failure
     */
    abstract function set_acl($folder, $user, $acl);
    /**
     * Removes any <identifier,rights> pair for the
     * specified user from the ACL for the specified
     * folder (DELETEACL).
     *
     * @param string $folder  Folder name
     * @param string $user    User name
     *
     * @return boolean True on success, False on failure
     */
    abstract function delete_acl($folder, $user);
    /**
     * Returns the access control list for a folder (GETACL).
     *
     * @param string $folder Folder name
     *
     * @return array User-rights array on success, NULL on error
     */
    abstract function get_acl($folder);
    /**
     * Returns information about what rights can be granted to the
     * user (identifier) in the ACL for the folder (LISTRIGHTS).
     *
     * @param string $folder  Folder name
     * @param string $user    User name
     *
     * @return array List of user rights
     */
    abstract function list_rights($folder, $user);
    /**
     * Returns the set of rights that the current user has to a folder (MYRIGHTS).
     *
     * @param string $folder Folder name
     *
     * @return array MYRIGHTS response on success, NULL on error
     */
    abstract function my_rights($folder);
    /**
     * Sets metadata/annotations (SETMETADATA/SETANNOTATION)
     *
     * @param string $folder  Folder name (empty for server metadata)
     * @param array  $entries Entry-value array (use NULL value as NIL)
     *
     * @return boolean True on success, False on failure
     */
    abstract function set_metadata($folder, $entries);
    /**
     * Unsets metadata/annotations (SETMETADATA/SETANNOTATION)
     *
     * @param string $folder  Folder name (empty for server metadata)
     * @param array  $entries Entry names array
     *
     * @return boolean True on success, False on failure
     */
    abstract function delete_metadata($folder, $entries);
    /**
     * Returns folder metadata/annotations (GETMETADATA/GETANNOTATION).
     *
     * @param string $folder   Folder name (empty for server metadata)
     * @param array  $entries  Entries
     * @param array  $options  Command options (with MAXSIZE and DEPTH keys)
     *
     * @return array Metadata entry-value hash array on success, NULL on error
     */
    abstract function get_metadata($folder, $entries, $options = array());
    /* -----------------------------------------
     *   Cache related functions
     * ----------------------------------------*/
    /**
     * Clears the cache.
     *
     * @param string  $key         Cache key name or pattern
     * @param boolean $prefix_mode Enable it to clear all keys starting
     *                             with prefix specified in $key
     */
    abstract function clear_cache($key = null, $prefix_mode = false);
    /**
     * Returns cached value
     *
     * @param string $key Cache key
     *
     * @return mixed Cached value
     */
    abstract function get_cache($key);
}  // end class rcube_storage
/**
 * Class representing a message part
 *
 * @package Mail
 */
class rcube_message_part
{
    var $mime_id = '';
    var $ctype_primary = 'text';
    var $ctype_secondary = 'plain';
    var $mimetype = 'text/plain';
    var $disposition = '';
    var $filename = '';
    var $encoding = '8bit';
    var $charset = '';
    var $size = 0;
    var $headers = array();
    var $d_parameters = array();
    var $ctype_parameters = array();
    function __clone()
    {
        if (isset($this->parts)) {
            foreach ($this->parts as $idx => $part) {
                if (is_object($part)) {
                    $this->parts[$idx] = clone $part;
                }
            }
        }
    }
}
/**
 * Class for sorting an array of rcube_mail_header objects in a predetermined order.
 *
 * @package Mail
 * @author Eric Stadtherr
 */
class rcube_header_sorter
{
    private $uids = array();
    /**
     * Set the predetermined sort order.
     *
     * @param array $index  Numerically indexed array of IMAP UIDs
     */
    function set_index($index)
    {
        $index = array_flip($index);
        $this->uids = $index;
    }
    /**
     * Sort the array of header objects
     *
     * @param array $headers Array of rcube_mail_header objects indexed by UID
     */
    function sort_headers(&$headers)
    {
        uksort($headers, array($this, "compare_uids"));
    }
    /**
     * Sort method called by uksort()
     *
     * @param int $a Array key (UID)
     * @param int $b Array key (UID)
     */
    function compare_uids($a, $b)
    {
        // then find each sequence number in my ordered list
        $posa = isset($this->uids[$a]) ? intval($this->uids[$a]) : -1;
        $posb = isset($this->uids[$b]) ? intval($this->uids[$b]) : -1;
        // return the relative position as the comparison value
        return $posa - $posb;
    }
}
program/include/rcube_template.php
old mode 100755 new mode 100644
@@ -804,8 +804,8 @@
                        break;
                    case 'config':
                        $value = $this->config[$name];
                        if (is_array($value) && $value[$_SESSION['imap_host']]) {
                            $value = $value[$_SESSION['imap_host']];
                        if (is_array($value) && $value[$_SESSION['storage_host']]) {
                            $value = $value[$_SESSION['storage_host']];
                        }
                        break;
                    case 'request':
program/lib/utf7.inc
File was deleted
program/localization/en_US/messages.inc
@@ -21,7 +21,7 @@
$messages['loginfailed']  = 'Login failed.';
$messages['cookiesdisabled'] = 'Your browser does not accept cookies.';
$messages['sessionerror'] = 'Your session is invalid or expired.';
$messages['imaperror'] = 'Connection to IMAP server failed.';
$messages['storageerror'] = 'Connection to storage server failed.';
$messages['servererror'] = 'Server Error!';
$messages['servererrormsg'] = 'Server Error: $msg';
$messages['dberror'] = 'Database Error!';
program/steps/mail/check_recent.inc
@@ -19,12 +19,12 @@
*/
$current = $RCMAIL->imap->get_mailbox_name();
$current = $RCMAIL->storage->get_folder();
$check_all = !empty($_GET['_refresh']) || (bool)$RCMAIL->config->get('check_all_folders');
// list of folders to check
if ($check_all) {
    $a_mailboxes = $RCMAIL->imap->list_mailboxes('', '*', 'mail');
    $a_mailboxes = $RCMAIL->storage->list_folders_subscribed('', '*', 'mail');
}
else {
    $a_mailboxes = (array) $current;
@@ -37,11 +37,11 @@
    $is_current = $mbox_name == $current;
    if ($is_current) {
        // Synchronize mailbox cache, handle flag changes
        $RCMAIL->imap->mailbox_sync($mbox_name);
        $RCMAIL->storage->folder_sync($mbox_name);
    }
    // Get mailbox status
    $status = $RCMAIL->imap->mailbox_status($mbox_name);
    $status = $RCMAIL->storage->folder_status($mbox_name);
    if ($status & 1) {
        // trigger plugin hook
@@ -58,7 +58,7 @@
        if ($search_request && isset($_SESSION['search'])
            && $_SESSION['search_request'] == $search_request
        ) {
            $_SESSION['search'] = $RCMAIL->imap->refresh_search();
            $_SESSION['search'] = $RCMAIL->storage->refresh_search();
        }
        if (!empty($_GET['_quota']))
@@ -68,28 +68,32 @@
        if (empty($_GET['_list']))
            continue;
        // get overall message count; allow caching because rcube_imap::mailbox_status() did a refresh
        $all_count = $RCMAIL->imap->messagecount(null, $RCMAIL->imap->threading ? 'THREADS' : 'ALL');
        // get overall message count; allow caching because rcube_storage::folder_status() did a refresh
        $all_count = $RCMAIL->storage->count(null, $RCMAIL->storage->get_threading() ? 'THREADS' : 'ALL');
        $page      = $RCMAIL->storage->get_page();
        $page_size = $RCMAIL->storage->get_pagesize();
        // check current page if we're not on the first page
        if ($all_count && $RCMAIL->imap->list_page > 1) {
            $remaining = $all_count - $RCMAIL->imap->page_size * ($RCMAIL->imap->list_page - 1);
        if ($all_count && $page > 1) {
            $remaining = $all_count - $page_size * ($page - 1);
            if ($remaining <= 0) {
                $RCMAIL->imap->set_page($RCMAIL->imap->list_page-1);
                $_SESSION['page'] = $RCMAIL->imap->list_page;
                $page -= 1;
                $RCMAIL->storage->set_page($page);
                $_SESSION['page'] = $page;
            }
        }
        $OUTPUT->set_env('messagecount', $all_count);
        $OUTPUT->set_env('pagecount', ceil($all_count/$RCMAIL->imap->page_size));
        $OUTPUT->set_env('pagecount', ceil($all_count/$page_size));
        $OUTPUT->command('set_rowcount', rcmail_get_messagecount_text($all_count), $mbox_name);
        $OUTPUT->set_env('current_page', $all_count ? $RCMAIL->imap->list_page : 1);
        $OUTPUT->set_env('current_page', $all_count ? $page : 1);
        // remove old rows (and clear selection if new list is empty)
        $OUTPUT->command('message_list.clear', $all_count ? false : true);
        if ($all_count) {
            $a_headers = $RCMAIL->imap->list_headers($mbox_name, null, $_SESSION['sort_col'], $_SESSION['sort_order']);
            $a_headers = $RCMAIL->storage->list_messages($mbox_name, null, $_SESSION['sort_col'], $_SESSION['sort_order']);
            // add message rows
            rcmail_js_message_list($a_headers, false);
            // remove messages that don't exists from list selection array
program/steps/mail/compose.inc
@@ -56,7 +56,7 @@
  $_SESSION['compose_data_'.$COMPOSE_ID] = array(
    'id'      => $COMPOSE_ID,
    'param'   => request2param(RCUBE_INPUT_GET),
    'mailbox' => $RCMAIL->imap->get_mailbox_name(),
    'mailbox' => $RCMAIL->storage->get_folder(),
  );
  $COMPOSE =& $_SESSION['compose_data_'.$COMPOSE_ID];
@@ -134,7 +134,7 @@
  $OUTPUT->set_env('draft_autosave', $CONFIG['draft_autosave']);
}
// set current mailbox in client environment
$OUTPUT->set_env('mailbox', $RCMAIL->imap->get_mailbox_name());
$OUTPUT->set_env('mailbox', $RCMAIL->storage->get_folder());
$OUTPUT->set_env('sig_above', $RCMAIL->config->get('sig_above', false));
$OUTPUT->set_env('top_posting', $RCMAIL->config->get('top_posting', false));
$OUTPUT->set_env('recipients_separator', trim($RCMAIL->config->get('recipients_separator', ',')));
@@ -147,7 +147,7 @@
// get reference message and set compose mode
if ($msg_uid = $COMPOSE['param']['draft_uid']) {
  $RCMAIL->imap->set_mailbox($CONFIG['drafts_mbox']);
  $RCMAIL->storage->set_folder($CONFIG['drafts_mbox']);
  $compose_mode = RCUBE_COMPOSE_DRAFT;
}
else if ($msg_uid = $COMPOSE['param']['reply_uid'])
@@ -179,10 +179,10 @@
  // make sure message is marked as read
  if ($MESSAGE && $MESSAGE->headers && empty($MESSAGE->headers->flags['SEEN']))
    $RCMAIL->imap->set_flag($msg_uid, 'SEEN');
    $RCMAIL->storage->set_flag($msg_uid, 'SEEN');
  if (!empty($MESSAGE->headers->charset))
    $RCMAIL->imap->set_charset($MESSAGE->headers->charset);
    $RCMAIL->storage->set_charset($MESSAGE->headers->charset);
  if ($compose_mode == RCUBE_COMPOSE_REPLY)
  {
@@ -1051,12 +1051,12 @@
    $temp_dir = unslashify($RCMAIL->config->get('temp_dir'));
    $path = tempnam($temp_dir, 'rcmAttmnt');
    if ($fp = fopen($path, 'w')) {
      $RCMAIL->imap->get_raw_body($message->uid, $fp);
      $RCMAIL->storage->get_raw_body($message->uid, $fp);
      fclose($fp);
    } else
      return false;
  } else {
    $data = $RCMAIL->imap->get_raw_body($message->uid);
    $data = $RCMAIL->storage->get_raw_body($message->uid);
  }
  $attachment = array(
@@ -1428,16 +1428,16 @@
{
  global $RCMAIL;
  if ($RCMAIL->imap->mailbox_exists($folder, true)) {
  if ($RCMAIL->storage->folder_exists($folder, true)) {
    return true;
  }
  // folder may exist but isn't subscribed (#1485241)
  if ($create) {
    if (!$RCMAIL->imap->mailbox_exists($folder))
      return $RCMAIL->imap->create_mailbox($folder, true);
    if (!$RCMAIL->storage->folder_exists($folder))
      return $RCMAIL->storage->create_folder($folder, true);
    else
      return $RCMAIL->imap->subscribe($folder);
      return $RCMAIL->storage->subscribe($folder);
  }
  return false;
program/steps/mail/copy.inc
@@ -29,7 +29,7 @@
    $target = get_input_value('_target_mbox', RCUBE_INPUT_POST, true);
    $mbox = get_input_value('_mbox', RCUBE_INPUT_POST, true);
    $copied = $RCMAIL->imap->copy_message($uids, $target, $mbox);
    $copied = $RCMAIL->storage->copy_message($uids, $target, $mbox);
    if (!$copied) {
        // send error message
program/steps/mail/folders.inc
@@ -27,7 +27,7 @@
// send EXPUNGE command
if ($RCMAIL->action == 'expunge') {
    $success = $RCMAIL->imap->expunge($mbox);
    $success = $RCMAIL->storage->expunge_folder($mbox);
    // reload message list if current mailbox
    if ($success) {
@@ -48,7 +48,7 @@
// clear mailbox
else if ($RCMAIL->action == 'purge')
{
    $delimiter = $RCMAIL->imap->get_hierarchy_delimiter();
    $delimiter = $RCMAIL->storage->get_hierarchy_delimiter();
    $trash_regexp = '/^' . preg_quote($CONFIG['trash_mbox'] . $delimiter, '/') . '/';
    $junk_regexp  = '/^' . preg_quote($CONFIG['junk_mbox'] . $delimiter, '/') . '/';
@@ -56,7 +56,7 @@
    if ($mbox == $CONFIG['trash_mbox'] || $mbox == $CONFIG['junk_mbox']
        || preg_match($trash_regexp, $mbox) || preg_match($junk_regexp, $mbox)
    ) {
        $success = $RCMAIL->imap->clear_mailbox($mbox);
        $success = $RCMAIL->storage->clear_folder($mbox);
        if ($success) {
            $OUTPUT->show_message('folderpurged', 'confirmation');
program/steps/mail/func.inc
@@ -28,31 +28,17 @@
    $DRAFTS_MBOX => array('subject'=>1, 'to'=>1)
);
// actions that do not require imap connection here
$NOIMAP_ACTIONS = array('addcontact', 'autocomplete', 'upload', 'display-attachment', 'remove-attachment', 'get');
// always instantiate imap object (but not yet connect to server)
$RCMAIL->imap_init();
// log in to imap server
if (!in_array($RCMAIL->action, $NOIMAP_ACTIONS) && !$RCMAIL->imap_connect()) {
  $RCMAIL->kill_session();
  if ($OUTPUT->ajax_call)
    $OUTPUT->redirect(array(), 2000);
  $OUTPUT->set_env('task', 'login');
  $OUTPUT->send('login');
}
// always instantiate storage object (but not connect to server yet)
$RCMAIL->storage_init();
// set imap properties and session vars
if (strlen(trim($mbox = get_input_value('_mbox', RCUBE_INPUT_GPC, true))))
  $RCMAIL->imap->set_mailbox(($_SESSION['mbox'] = $mbox));
else if ($RCMAIL->imap)
  $_SESSION['mbox'] = $RCMAIL->imap->get_mailbox_name();
  $RCMAIL->storage->set_folder(($_SESSION['mbox'] = $mbox));
else if ($RCMAIL->storage)
  $_SESSION['mbox'] = $RCMAIL->storage->get_folder();
if (!empty($_GET['_page']))
  $RCMAIL->imap->set_page(($_SESSION['page'] = intval($_GET['_page'])));
  $RCMAIL->storage->set_page(($_SESSION['page'] = intval($_GET['_page'])));
// set default sort col/order to session
if (!isset($_SESSION['sort_col']))
@@ -69,28 +55,28 @@
    unset($a_threading[$_SESSION['mbox']]);
  $RCMAIL->user->save_prefs(array('message_threading' => $a_threading));
}
$RCMAIL->imap->set_threading($a_threading[$_SESSION['mbox']]);
$RCMAIL->storage->set_threading($a_threading[$_SESSION['mbox']]);
// set message set for search result
if (!empty($_REQUEST['_search']) && isset($_SESSION['search'])
    && $_SESSION['search_request'] == $_REQUEST['_search']
) {
  $RCMAIL->imap->set_search_set($_SESSION['search']);
  $RCMAIL->storage->set_search_set($_SESSION['search']);
  $OUTPUT->set_env('search_request', $_REQUEST['_search']);
  $OUTPUT->set_env('search_text', $_SESSION['last_text_search']);
}
// set main env variables, labels and page title
if (empty($RCMAIL->action) || $RCMAIL->action == 'list') {
  $mbox_name = $RCMAIL->imap->get_mailbox_name();
  $mbox_name = $RCMAIL->storage->get_folder();
  if (empty($RCMAIL->action)) {
    // initialize searching result if search_filter is used
    if ($_SESSION['search_filter'] && $_SESSION['search_filter'] != 'ALL') {
      $search_request = md5($mbox_name.$_SESSION['search_filter']);
      $RCMAIL->imap->search($mbox_name, $_SESSION['search_filter'], RCMAIL_CHARSET, $_SESSION['sort_col']);
      $_SESSION['search'] = $RCMAIL->imap->get_search_set();
      $RCMAIL->storage->search($mbox_name, $_SESSION['search_filter'], RCMAIL_CHARSET, $_SESSION['sort_col']);
      $_SESSION['search'] = $RCMAIL->storage->get_search_set();
      $_SESSION['search_request'] = $search_request;
      $OUTPUT->set_env('search_request', $search_request);
    }
@@ -99,13 +85,15 @@
      $OUTPUT->set_env('search_mods', $search_mods);
  }
  $threading = (bool) $RCMAIL->storage->get_threading();
  // set current mailbox and some other vars in client environment
  $OUTPUT->set_env('mailbox', $mbox_name);
  $OUTPUT->set_env('pagesize', $RCMAIL->imap->page_size);
  $OUTPUT->set_env('quota', $RCMAIL->imap->get_capability('QUOTA'));
  $OUTPUT->set_env('delimiter', $RCMAIL->imap->get_hierarchy_delimiter());
  $OUTPUT->set_env('threading', (bool) $RCMAIL->imap->threading);
  $OUTPUT->set_env('threads', $RCMAIL->imap->threading || $RCMAIL->imap->get_capability('THREAD'));
  $OUTPUT->set_env('pagesize', $RCMAIL->storage->get_pagesize());
  $OUTPUT->set_env('quota', $RCMAIL->storage->get_capability('QUOTA'));
  $OUTPUT->set_env('delimiter', $RCMAIL->storage->get_hierarchy_delimiter());
  $OUTPUT->set_env('threading', $threading);
  $OUTPUT->set_env('threads', $threading || $RCMAIL->storage->get_capability('THREAD'));
  $OUTPUT->set_env('preview_pane_mark_read', $RCMAIL->config->get('preview_pane_mark_read', 0));
  if ($CONFIG['flag_for_deletion'])
@@ -130,7 +118,7 @@
      'movingmessage', 'copyingmessage', 'deletingmessage', 'markingmessage',
      'copy', 'move', 'quota');
  $OUTPUT->set_pagetitle(rcmail_localize_foldername($RCMAIL->imap->mod_mailbox($mbox_name)));
  $OUTPUT->set_pagetitle(rcmail_localize_foldername($RCMAIL->storage->mod_folder($mbox_name)));
}
@@ -161,8 +149,8 @@
  // save some variables for use in ajax list
  $_SESSION['list_attrib'] = $attrib;
  $mbox = $RCMAIL->imap->get_mailbox_name();
  $delim = $RCMAIL->imap->get_hierarchy_delimiter();
  $mbox = $RCMAIL->storage->get_folder();
  $delim = $RCMAIL->storage->get_hierarchy_delimiter();
  // show 'to' instead of 'from' in sent/draft messages
  if ((strpos($mbox.$delim, $CONFIG['sent_mbox'].$delim)===0 || strpos($mbox.$delim, $CONFIG['drafts_mbox'].$delim)===0)
@@ -218,8 +206,8 @@
    $head_replace = true;
  }
  $mbox = $RCMAIL->imap->get_mailbox_name();
  $delim = $RCMAIL->imap->get_hierarchy_delimiter();
  $mbox = $RCMAIL->storage->get_folder();
  $delim = $RCMAIL->storage->get_hierarchy_delimiter();
  // make sure 'threads' and 'subject' columns are present
  if (!in_array('subject', $a_show_cols))
@@ -317,7 +305,7 @@
      $insert_top);
  }
  if ($RCMAIL->imap->threading) {
  if ($RCMAIL->storage->get_threading()) {
    $OUTPUT->command('init_threads', (array) $roots, $mbox);
  }
}
@@ -430,22 +418,24 @@
{
  global $RCMAIL;
  if ($page===NULL)
    $page = $RCMAIL->imap->list_page;
  if ($page === NULL) {
    $page = $RCMAIL->storage->get_page();
  }
  $start_msg = ($page-1) * $RCMAIL->imap->page_size + 1;
  $page_size = $RCMAIL->storage->get_pagesize();
  $start_msg = ($page-1) * $page_size + 1;
  if ($count!==NULL)
    $max = $count;
  else if ($RCMAIL->action)
    $max = $RCMAIL->imap->messagecount(NULL, $RCMAIL->imap->threading ? 'THREADS' : 'ALL');
    $max = $RCMAIL->storage->count(NULL, $RCMAIL->storage->get_threading() ? 'THREADS' : 'ALL');
  if ($max==0)
    $out = rcube_label('mailboxempty');
  else
    $out = rcube_label(array('name' => $RCMAIL->imap->threading ? 'threadsfromto' : 'messagesfromto',
    $out = rcube_label(array('name' => $RCMAIL->storage->get_threading() ? 'threadsfromto' : 'messagesfromto',
            'vars' => array('from'  => $start_msg,
            'to'    => min($max, $start_msg + $RCMAIL->imap->page_size - 1),
            'to'    => min($max, $start_msg + $page_size - 1),
            'count' => $max)));
  return Q($out);
@@ -468,7 +458,7 @@
function rcmail_get_mailbox_name_text()
{
  global $RCMAIL;
  return rcmail_localize_foldername($RCMAIL->imap->get_mailbox_name());
  return rcmail_localize_foldername($RCMAIL->storage->get_folder());
}
@@ -479,7 +469,7 @@
  $old_unseen = rcmail_get_unseen_count($mbox_name);
  if ($count === null)
    $unseen = $RCMAIL->imap->messagecount($mbox_name, 'UNSEEN', $force);
    $unseen = $RCMAIL->storage->count($mbox_name, 'UNSEEN', $force);
  else
    $unseen = $count;
@@ -590,7 +580,7 @@
  // fix (unknown/malformed) HTML tags before "wash"
  $html = preg_replace_callback('/(<[\/]*)([^\s>]+)/', 'rcmail_html_tag_callback', $html);
  // charset was converted to UTF-8 in rcube_imap::get_message_part(),
  // charset was converted to UTF-8 in rcube_storage::get_message_part(),
  // change/add charset specification in HTML accordingly,
  // washtml cannot work without that
  $meta = '<meta http-equiv="Content-Type" content="text/html; charset='.RCMAIL_CHARSET.'" />';
@@ -1010,7 +1000,7 @@
        if (!rcmail_mem_check($part->size * 10)) {
          $out .= html::span('part-notice', rcube_label('messagetoobig'). ' '
            . html::a('?_task=mail&_action=get&_download=1&_uid='.$MESSAGE->uid.'&_part='.$part->mime_id
              .'&_mbox='. urlencode($RCMAIL->imap->get_mailbox_name()), rcube_label('download')));
              .'&_mbox='. urlencode($RCMAIL->storage->get_folder()), rcube_label('download')));
          continue;
        }
@@ -1056,7 +1046,7 @@
    if (!rcmail_mem_check(strlen($MESSAGE->body) * 10)) {
      $out .= html::span('part-notice', rcube_label('messagetoobig'). ' '
        . html::a('?_task=mail&_action=get&_download=1&_uid='.$MESSAGE->uid.'&_part=0'
          .'&_mbox='. urlencode($RCMAIL->imap->get_mailbox_name()), rcube_label('download')));
          .'&_mbox='. urlencode($RCMAIL->storage->get_folder()), rcube_label('download')));
    }
    else {
      $plugin = $RCMAIL->plugins->exec_hook('message_body_prefix', array(
@@ -1477,7 +1467,7 @@
    $message = new rcube_message($message);
  if ($message->headers->mdn_to && empty($message->headers->flags['MDNSENT']) &&
    ($RCMAIL->imap->check_permflag('MDNSENT') || $RCMAIL->imap->check_permflag('*')))
    ($RCMAIL->storage->check_permflag('MDNSENT') || $RCMAIL->storage->check_permflag('*')))
  {
    $identity = $RCMAIL->user->get_identity();
    $sender = format_email_recipient($identity['email'], $identity['name']);
@@ -1533,7 +1523,7 @@
    if ($sent)
    {
      $RCMAIL->imap->set_flag($message->uid, 'MDNSENT');
      $RCMAIL->storage->set_flag($message->uid, 'MDNSENT');
      return true;
    }
  }
@@ -1596,7 +1586,7 @@
  // Set env variables for messageerror.html template
  if ($RCMAIL->action == 'show') {
    $mbox_name = $RCMAIL->imap->get_mailbox_name();
    $mbox_name = $RCMAIL->storage->get_folder();
    $RCMAIL->output->set_env('mailbox', $mbox_name);
    $RCMAIL->output->set_env('uid', null);
  }
program/steps/mail/get.inc
@@ -36,7 +36,7 @@
ob_end_clean();
// Now we need IMAP connection
if (!$RCMAIL->imap_connect()) {
if (!$RCMAIL->storage_connect()) {
  // Get action is often executed simultanously.
  // Some servers have MAXPERIP or other limits.
  // To workaround this we'll wait for some time
@@ -117,7 +117,7 @@
      if (!rcmail_mem_check($part->size * 10)) {
        $out = '<body>' . rcube_label('messagetoobig'). ' '
          . html::a('?_task=mail&_action=get&_download=1&_uid='.$MESSAGE->uid.'&_part='.$part->mime_id
            .'&_mbox='. urlencode($RCMAIL->imap->get_mailbox_name()), rcube_label('download')) . '</body></html>';
            .'&_mbox='. urlencode($RCMAIL->storage->get_folder()), rcube_label('download')) . '</body></html>';
      }
      else {
        // get part body if not available
@@ -157,7 +157,7 @@
          $stdout = fopen('php://output', 'w');
          stream_filter_register('rcube_content', 'rcube_content_filter') or die('Failed to register content filter');
          stream_filter_append($stdout, 'rcube_content');
          $RCMAIL->imap->get_message_part($MESSAGE->uid, $part->mime_id, $part, false, $stdout);
          $RCMAIL->storage->get_message_part($MESSAGE->uid, $part->mime_id, $part, false, $stdout);
        }
      }
      else {
@@ -165,7 +165,7 @@
        if ($part->body)
          echo $part->body;
        else if ($part->size)
          $RCMAIL->imap->get_message_part($MESSAGE->uid, $part->mime_id, $part, true);
          $RCMAIL->storage->get_message_part($MESSAGE->uid, $part->mime_id, $part, true);
      }
    }
program/steps/mail/getunread.inc
@@ -19,11 +19,11 @@
*/
$a_folders = $RCMAIL->imap->list_mailboxes('', '*', 'mail');
$a_folders = $RCMAIL->storage->list_folders_subscribed('', '*', 'mail');
if (!empty($a_folders))
{
  $current = $RCMAIL->imap->get_mailbox_name();
  $current = $RCMAIL->storage->get_folder();
  $inbox = ($current == 'INBOX');
  $check_all = (bool)$RCMAIL->config->get('check_all_folders');
@@ -33,7 +33,7 @@
    if (!$check_all && $unseen_old !== null && $mbox_row != $current)
      $unseen = $unseen_old;
    else
      $unseen = $RCMAIL->imap->messagecount($mbox_row, 'UNSEEN', $unseen_old === null);
      $unseen = $RCMAIL->storage->count($mbox_row, 'UNSEEN', $unseen_old === null);
    if ($unseen || $unseen_old === null) {
      $OUTPUT->command('set_unread_count', $mbox_row, $unseen, $inbox && $mbox_row == 'INBOX');
program/steps/mail/headers.inc
@@ -20,7 +20,7 @@
if ($uid = get_input_value('_uid', RCUBE_INPUT_POST))
{
    $source = $RCMAIL->imap->get_raw_headers($uid);
    $source = $RCMAIL->storage->get_raw_headers($uid);
    if ($source !== false) {
        $source = htmlspecialchars(trim($source));
program/steps/mail/list.inc
@@ -51,30 +51,31 @@
if ($save_arr)
  $RCMAIL->user->save_prefs($save_arr);
$mbox_name = $RCMAIL->imap->get_mailbox_name();
$mbox_name = $RCMAIL->storage->get_folder();
$threading = (bool) $RCMAIL->storage->get_threading();
// Synchronize mailbox cache, handle flag changes
$RCMAIL->imap->mailbox_sync($mbox_name);
$RCMAIL->storage->folder_sync($mbox_name);
// initialize searching result if search_filter is used
if ($_SESSION['search_filter'] && $_SESSION['search_filter'] != 'ALL')
{
  $search_request = md5($mbox_name.$_SESSION['search_filter']);
  $RCMAIL->imap->search($mbox_name, $_SESSION['search_filter'], RCMAIL_CHARSET, $sort_col);
  $_SESSION['search'] = $RCMAIL->imap->get_search_set();
  $RCMAIL->storage->search($mbox_name, $_SESSION['search_filter'], RCMAIL_CHARSET, $sort_col);
  $_SESSION['search'] = $RCMAIL->storage->get_search_set();
  $_SESSION['search_request'] = $search_request;
  $OUTPUT->set_env('search_request', $search_request);
}
// fetch message headers
if ($count = $RCMAIL->imap->messagecount($mbox_name, $RCMAIL->imap->threading ? 'THREADS' : 'ALL', !empty($_REQUEST['_refresh'])))
  $a_headers = $RCMAIL->imap->list_headers($mbox_name, NULL, $sort_col, $sort_order);
if ($count = $RCMAIL->storage->count($mbox_name, $threading ? 'THREADS' : 'ALL', !empty($_REQUEST['_refresh'])))
  $a_headers = $RCMAIL->storage->list_messages($mbox_name, NULL, $sort_col, $sort_order);
// update search set (possible change of threading mode)
if (!empty($_REQUEST['_search']) && isset($_SESSION['search'])
    && $_SESSION['search_request'] == $_REQUEST['_search']
) {
  $_SESSION['search'] = $RCMAIL->imap->get_search_set();
  $_SESSION['search'] = $RCMAIL->storage->get_search_set();
}
// remove old search data
else if (empty($_REQUEST['_search']) && isset($_SESSION['search'])) {
@@ -91,11 +92,11 @@
rcmail_send_unread_count($mbox_name, !empty($_REQUEST['_refresh']), $unseen);
// update message count display
$pages = ceil($count/$RCMAIL->imap->page_size);
$pages = ceil($count/$RCMAIL->storage->get_pagesize());
$OUTPUT->set_env('messagecount', $count);
$OUTPUT->set_env('pagecount', $pages);
$OUTPUT->set_env('threading', (bool) $RCMAIL->imap->threading);
$OUTPUT->set_env('current_page', $count ? $RCMAIL->imap->list_page : 1);
$OUTPUT->set_env('threading', $threading);
$OUTPUT->set_env('current_page', $count ? $RCMAIL->storage->get_page() : 1);
$OUTPUT->command('set_rowcount', rcmail_get_messagecount_text($count), $mbox_name);
$OUTPUT->command('set_mailboxname', rcmail_get_mailbox_name_text());
@@ -108,7 +109,7 @@
}
else {
  // handle IMAP errors (e.g. #1486905)
  if ($err_code = $RCMAIL->imap->get_error_code()) {
  if ($err_code = $RCMAIL->storage->get_error_code()) {
    rcmail_display_server_error();
  }
  else if ($search_request)
program/steps/mail/mark.inc
@@ -30,18 +30,20 @@
  'flagged' => 'FLAGGED',
  'unflagged' => 'UNFLAGGED');
$threading = (bool) $RCMAIL->storage->get_threading();
if (($uids = get_input_value('_uid', RCUBE_INPUT_POST)) && ($flag = get_input_value('_flag', RCUBE_INPUT_POST)))
{
  $flag = $a_flags_map[$flag] ? $a_flags_map[$flag] : strtoupper($flag);
  if ($flag == 'DELETED' && $CONFIG['skip_deleted'] && $_POST['_from'] != 'show') {
    // count messages before changing anything
    $old_count = $RCMAIL->imap->messagecount(NULL, $RCMAIL->imap->threading ? 'THREADS' : 'ALL');
    $old_pages = ceil($old_count / $RCMAIL->imap->page_size);
    $old_count = $RCMAIL->storage->count(NULL, $threading ? 'THREADS' : 'ALL');
    $old_pages = ceil($old_count / $RCMAIL->storage->get_pagesize());
    $count = sizeof(explode(',', $uids));
  }
  $marked = $RCMAIL->imap->set_flag($uids, $flag);
  $marked = $RCMAIL->storage->set_flag($uids, $flag);
  if (!$marked) {
    // send error message
@@ -57,14 +59,14 @@
  if ($flag == 'DELETED' && $CONFIG['read_when_deleted'] && !empty($_POST['_ruid'])) {
    $ruids = get_input_value('_ruid', RCUBE_INPUT_POST);
    $read = $RCMAIL->imap->set_flag($ruids, 'SEEN');
    $read = $RCMAIL->storage->set_flag($ruids, 'SEEN');
    if ($read && !$CONFIG['skip_deleted'])
      $OUTPUT->command('flag_deleted_as_read', $ruids);
  }
  if ($flag == 'SEEN' || $flag == 'UNSEEN' || ($flag == 'DELETED' && !$CONFIG['skip_deleted'])) {
    rcmail_send_unread_count($RCMAIL->imap->get_mailbox_name());
    rcmail_send_unread_count($RCMAIL->storage->get_folder());
  }
  else if ($flag == 'DELETED' && $CONFIG['skip_deleted']) {
    if ($_POST['_from'] == 'show') {
@@ -73,31 +75,35 @@
      else
        $OUTPUT->command('command', 'list');
    } else {
      $search_request = get_input_value('_search', RCUBE_INPUT_GPC);
      // refresh saved search set after moving some messages
      if (($search_request = get_input_value('_search', RCUBE_INPUT_GPC)) && $RCMAIL->imap->search_set) {
        $_SESSION['search'] = $RCMAIL->imap->refresh_search();
      if ($search_request && $RCMAIL->storage->get_search_set()) {
        $_SESSION['search'] = $RCMAIL->storage->refresh_search();
      }
      $msg_count      = $RCMAIL->imap->messagecount(NULL, $RCMAIL->imap->threading ? 'THREADS' : 'ALL');
      $pages          = ceil($msg_count / $RCMAIL->imap->page_size);
      $nextpage_count = $old_count - $RCMAIL->imap->page_size * $RCMAIL->imap->list_page;
      $remaining      = $msg_count - $RCMAIL->imap->page_size * ($RCMAIL->imap->list_page - 1);
      $msg_count      = $RCMAIL->storage->count(NULL, $threading ? 'THREADS' : 'ALL');
      $page_size      = $RCMAIL->storage->get_pagesize();
      $page           = $RCMAIL->storage->get_page();
      $pages          = ceil($msg_count / $page_size);
      $nextpage_count = $old_count - $page_size * $page;
      $remaining      = $msg_count - $page_size * ($page - 1);
      // jump back one page (user removed the whole last page)
      if ($RCMAIL->imap->list_page > 1 && $remaining == 0) {
        $RCMAIL->imap->set_page($RCMAIL->imap->list_page-1);
        $_SESSION['page'] = $RCMAIL->imap->list_page;
      if ($page > 1 && $remaining == 0) {
        $page -= 1;
        $RCMAIL->storage->set_page($page);
        $_SESSION['page'] = $page;
        $jump_back = true;
      }
      // update message count display
      $OUTPUT->set_env('messagecount', $msg_count);
      $OUTPUT->set_env('current_page', $RCMAIL->imap->list_page);
      $OUTPUT->set_env('current_page', $page);
      $OUTPUT->set_env('pagecount', $pages);
      // update mailboxlist
      $mbox = $RCMAIL->imap->get_mailbox_name();
      $unseen_count = $msg_count ? $RCMAIL->imap->messagecount($mbox, 'UNSEEN') : 0;
      $mbox = $RCMAIL->storage->get_folder();
      $unseen_count = $msg_count ? $RCMAIL->storage->count($mbox, 'UNSEEN') : 0;
      $old_unseen = rcmail_get_unseen_count($mbox);
      if ($old_unseen != $unseen_count) {
@@ -106,15 +112,16 @@
      }
      $OUTPUT->command('set_rowcount', rcmail_get_messagecount_text($msg_count), $mbox);
      if ($RCMAIL->imap->threading)
      if ($threading) {
        $count = get_input_value('_count', RCUBE_INPUT_POST);
      }
      // add new rows from next page (if any)
      if ($count && $uids != '*' && ($jump_back || $nextpage_count > 0)) {
        $sort_col   = isset($_SESSION['sort_col'])   ? $_SESSION['sort_col']   : $CONFIG['message_sort_col'];
        $sort_order = isset($_SESSION['sort_order']) ? $_SESSION['sort_order'] : $CONFIG['message_sort_order'];
        $a_headers = $RCMAIL->imap->list_headers($mbox, NULL, $sort_col, $sort_order,
        $a_headers = $RCMAIL->storage->list_messages($mbox, NULL, $sort_col, $sort_order,
        $jump_back ? NULL : $count);
        rcmail_js_message_list($a_headers, false);
program/steps/mail/move_del.inc
@@ -24,8 +24,9 @@
  return;
// count messages before changing anything
$old_count = $RCMAIL->imap->messagecount(NULL, $RCMAIL->imap->threading ? 'THREADS' : 'ALL');
$old_pages = ceil($old_count / $RCMAIL->imap->page_size);
$threading = (bool) $RCMAIL->storage->get_threading();
$old_count = $RCMAIL->storage->count(NULL, $threading ? 'THREADS' : 'ALL');
$old_pages = ceil($old_count / $RCMAIL->storage->get_pagesize());
// move messages
if ($RCMAIL->action=='moveto' && !empty($_POST['_uid']) && strlen($_POST['_target_mbox'])) {
@@ -33,7 +34,7 @@
    $target = get_input_value('_target_mbox', RCUBE_INPUT_POST, true);
    $mbox = get_input_value('_mbox', RCUBE_INPUT_POST, true);
    $moved = $RCMAIL->imap->move_message($uids, $target, $mbox);
    $moved = $RCMAIL->storage->move_message($uids, $target, $mbox);
    if (!$moved) {
        // send error message
@@ -54,7 +55,7 @@
    $count = sizeof(explode(',', ($uids = get_input_value('_uid', RCUBE_INPUT_POST))));
    $mbox = get_input_value('_mbox', RCUBE_INPUT_POST, true);
    $del = $RCMAIL->imap->delete_message($uids, $mbox);
    $del = $RCMAIL->storage->delete_message($uids, $mbox);
    if (!$del) {
        // send error message
@@ -75,9 +76,11 @@
    exit;
}
$search_request = get_input_value('_search', RCUBE_INPUT_GPC);
// refresh saved search set after moving some messages
if (($search_request = get_input_value('_search', RCUBE_INPUT_GPC)) && $RCMAIL->imap->search_set) {
    $_SESSION['search'] = $RCMAIL->imap->refresh_search();
if ($search_request && $RCMAIL->storage->get_search_set()) {
    $_SESSION['search'] = $RCMAIL->storage->refresh_search();
}
if ($_POST['_from'] == 'show')
@@ -89,26 +92,29 @@
}
else
{
  $msg_count      = $RCMAIL->imap->messagecount(NULL, $RCMAIL->imap->threading ? 'THREADS' : 'ALL');
  $pages          = ceil($msg_count / $RCMAIL->imap->page_size);
  $nextpage_count = $old_count - $RCMAIL->imap->page_size * $RCMAIL->imap->list_page;
  $remaining      = $msg_count - $RCMAIL->imap->page_size * ($RCMAIL->imap->list_page - 1);
  $msg_count      = $RCMAIL->storage->count(NULL, $threading ? 'THREADS' : 'ALL');
  $page_size      = $RCMAIL->storage->get_pagesize();
  $page           = $RCMAIL->storage->get_page();
  $pages          = ceil($msg_count / $page_size);
  $nextpage_count = $old_count - $page_size * $page;
  $remaining      = $msg_count - $page_size * ($page - 1);
  // jump back one page (user removed the whole last page)
  if ($RCMAIL->imap->list_page > 1 && $remaining == 0) {
    $RCMAIL->imap->set_page($RCMAIL->imap->list_page-1);
    $_SESSION['page'] = $RCMAIL->imap->list_page;
  if ($page > 1 && $remaining == 0) {
    $page -= 1;
    $RCMAIL->storage->set_page($page);
    $_SESSION['page'] = $page;
    $jump_back = true;
  }
  // update message count display
  $OUTPUT->set_env('messagecount', $msg_count);
  $OUTPUT->set_env('current_page', $RCMAIL->imap->list_page);
  $OUTPUT->set_env('current_page', $page);
  $OUTPUT->set_env('pagecount', $pages);
  // update mailboxlist
  $mbox = $RCMAIL->imap->get_mailbox_name();
  $unseen_count = $msg_count ? $RCMAIL->imap->messagecount($mbox, 'UNSEEN') : 0;
  $mbox = $RCMAIL->storage->get_folder();
  $unseen_count = $msg_count ? $RCMAIL->storage->count($mbox, 'UNSEEN') : 0;
  $old_unseen = rcmail_get_unseen_count($mbox);
  if ($old_unseen != $unseen_count) {
@@ -123,15 +129,16 @@
  $OUTPUT->command('set_quota', rcmail_quota_content());
  $OUTPUT->command('set_rowcount', rcmail_get_messagecount_text($msg_count), $mbox);
  if ($RCMAIL->imap->threading)
  if ($threading) {
    $count = get_input_value('_count', RCUBE_INPUT_POST);
  }
  // add new rows from next page (if any)
  if ($addrows && $count && $uids != '*' && ($jump_back || $nextpage_count > 0)) {
    $sort_col   = isset($_SESSION['sort_col'])   ? $_SESSION['sort_col']   : $CONFIG['message_sort_col'];
    $sort_order = isset($_SESSION['sort_order']) ? $_SESSION['sort_order'] : $CONFIG['message_sort_order'];
    $a_headers = $RCMAIL->imap->list_headers($mbox, NULL, $sort_col, $sort_order,
    $a_headers = $RCMAIL->storage->list_messages($mbox, NULL, $sort_col, $sort_order,
      $jump_back ? NULL : $count);
    rcmail_js_message_list($a_headers, false);
program/steps/mail/pagenav.inc
@@ -20,14 +20,14 @@
*/
$uid   = get_input_value('_uid', RCUBE_INPUT_GET);
$index = $RCMAIL->imap->message_index(null, $_SESSION['sort_col'], $_SESSION['sort_order']);
$cnt   = $index->countMessages();
$index = $RCMAIL->storage->index(null, $_SESSION['sort_col'], $_SESSION['sort_order']);
$cnt   = $index->count_messages();
if ($cnt && ($pos = $index->exists($uid, true)) !== false) {
    $prev  = $pos ? $index->getElement($pos-1) : 0;
    $first = $pos ? $index->getElement('FIRST') : 0;
    $next  = $pos < $cnt-1 ? $index->getElement($pos+1) : 0;
    $last  = $pos < $cnt-1 ? $index->getElement('LAST') : 0;
    $prev  = $pos ? $index->get_element($pos-1) : 0;
    $first = $pos ? $index->get_element('FIRST') : 0;
    $next  = $pos < $cnt-1 ? $index->get_element($pos+1) : 0;
    $last  = $pos < $cnt-1 ? $index->get_element('LAST') : 0;
}
// Set UIDs and activate navigation buttons
program/steps/mail/search.inc
@@ -18,8 +18,8 @@
$REMOTE_REQUEST = TRUE;
// reset list_page and old search results
$RCMAIL->imap->set_page(1);
$RCMAIL->imap->set_search_set(NULL);
$RCMAIL->storage->set_page(1);
$RCMAIL->storage->set_search_set(NULL);
$_SESSION['page'] = 1;
// using encodeURI with javascript "should" give us
@@ -107,32 +107,32 @@
// execute IMAP search
if ($search_str)
  $RCMAIL->imap->search($mbox, $search_str, $imap_charset, $_SESSION['sort_col']);
  $RCMAIL->storage->search($mbox, $search_str, $imap_charset, $_SESSION['sort_col']);
// save search results in session
if (!is_array($_SESSION['search']))
  $_SESSION['search'] = array();
if ($search_str) {
  $_SESSION['search'] = $RCMAIL->imap->get_search_set();
  $_SESSION['search'] = $RCMAIL->storage->get_search_set();
  $_SESSION['last_text_search'] = $str;
}
$_SESSION['search_request'] = $search_request;
// Get the headers
$result_h = $RCMAIL->imap->list_headers($mbox, 1, $_SESSION['sort_col'], $_SESSION['sort_order']);
$count = $RCMAIL->imap->messagecount($mbox, $RCMAIL->imap->threading ? 'THREADS' : 'ALL');
$result_h = $RCMAIL->storage->list_messages($mbox, 1, $_SESSION['sort_col'], $_SESSION['sort_order']);
$count = $RCMAIL->storage->count($mbox, $RCMAIL->storage->get_threading() ? 'THREADS' : 'ALL');
// Make sure we got the headers
if (!empty($result_h)) {
  rcmail_js_message_list($result_h);
  if ($search_str)
    $OUTPUT->show_message('searchsuccessful', 'confirmation', array('nr' => $RCMAIL->imap->messagecount(NULL, 'ALL')));
    $OUTPUT->show_message('searchsuccessful', 'confirmation', array('nr' => $RCMAIL->storage->count(NULL, 'ALL')));
}
// handle IMAP errors (e.g. #1486905)
else  if ($err_code = $RCMAIL->imap->get_error_code()) {
else  if ($err_code = $RCMAIL->storage->get_error_code()) {
  rcmail_display_server_error();
}
else {
@@ -142,8 +142,6 @@
// update message count display
$OUTPUT->set_env('search_request', $search_str ? $search_request : '');
$OUTPUT->set_env('messagecount', $count);
$OUTPUT->set_env('pagecount', ceil($count/$RCMAIL->imap->page_size));
$OUTPUT->set_env('pagecount', ceil($count/$RCMAIL->storage->get_pagesize()));
$OUTPUT->command('set_rowcount', rcmail_get_messagecount_text($count, 1), $mbox);
$OUTPUT->send();
program/steps/mail/sendmail.inc
@@ -634,9 +634,9 @@
  // set replied/forwarded flag
  if ($COMPOSE['reply_uid'])
    $RCMAIL->imap->set_flag($COMPOSE['reply_uid'], 'ANSWERED', $COMPOSE['mailbox']);
    $RCMAIL->storage->set_flag($COMPOSE['reply_uid'], 'ANSWERED', $COMPOSE['mailbox']);
  else if ($COMPOSE['forward_uid'])
    $RCMAIL->imap->set_flag($COMPOSE['forward_uid'], 'FORWARDED', $COMPOSE['mailbox']);
    $RCMAIL->storage->set_flag($COMPOSE['forward_uid'], 'FORWARDED', $COMPOSE['mailbox']);
} // End of SMTP Delivery Block
@@ -649,12 +649,12 @@
if ($store_target) {
  // check if folder is subscribed
  if ($RCMAIL->imap->mailbox_exists($store_target, true))
  if ($RCMAIL->storage->folder_exists($store_target, true))
    $store_folder = true;
  // folder may be existing but not subscribed (#1485241)
  else if (!$RCMAIL->imap->mailbox_exists($store_target))
    $store_folder = $RCMAIL->imap->create_mailbox($store_target, true);
  else if ($RCMAIL->imap->subscribe($store_target))
  else if (!$RCMAIL->storage->folder_exists($store_target))
    $store_folder = $RCMAIL->storage->create_folder($store_target, true);
  else if ($RCMAIL->storage->subscribe($store_target))
    $store_folder = true;
  // append message to sent box
@@ -684,7 +684,7 @@
            'message' => "Could not create message: ".$msg->getMessage()),
            TRUE, FALSE);
    else {
      $saved = $RCMAIL->imap->save_message($store_target, $msg, $headers, $mailbody_file ? true : false);
      $saved = $RCMAIL->storage->save_message($store_target, $msg, $headers, $mailbody_file ? true : false);
    }
    if ($mailbody_file) {
@@ -708,11 +708,11 @@
  if ($olddraftmessageid) {
    // delete previous saved draft
    // @TODO: use message UID (remember to check UIDVALIDITY) to skip this SEARCH
    $delete_idx = $RCMAIL->imap->search_once($CONFIG['drafts_mbox'],
    $delete_idx = $RCMAIL->storage->search_once($CONFIG['drafts_mbox'],
        'HEADER Message-ID '.$olddraftmessageid);
    if ($del_uid = $delete_idx->getElement('FIRST')) {
      $deleted = $RCMAIL->imap->delete_message($del_uid, $CONFIG['drafts_mbox']);
      $deleted = $RCMAIL->storage->delete_message($del_uid, $CONFIG['drafts_mbox']);
      // raise error if deletion of old draft failed
      if (!$deleted)
@@ -733,7 +733,7 @@
  // remember new draft-uid ($saved could be an UID or TRUE here)
  if (is_bool($saved)) {
    $draft_idx = $RCMAIL->imap->search_once($CONFIG['drafts_mbox'], 'HEADER Message-ID '.$msgid);
    $draft_idx = $RCMAIL->storage->search_once($CONFIG['drafts_mbox'], 'HEADER Message-ID '.$msgid);
    $saved     = $draft_idx->getElement('FIRST');
  }
  $COMPOSE['param']['draft_uid'] = $saved;
program/steps/mail/show.inc
@@ -30,14 +30,14 @@
    rcmail_message_error($uid);
  }
  $mbox_name = $RCMAIL->imap->get_mailbox_name();
  $mbox_name = $RCMAIL->storage->get_folder();
  // show images?
  rcmail_check_safe($MESSAGE);
  // set message charset as default
  if (!empty($MESSAGE->headers->charset))
    $RCMAIL->imap->set_charset($MESSAGE->headers->charset);
    $RCMAIL->storage->set_charset($MESSAGE->headers->charset);
  $OUTPUT->set_pagetitle(abbreviate_string($MESSAGE->subject, 128, '...', true));
@@ -47,7 +47,7 @@
  $OUTPUT->set_env('safemode', $MESSAGE->is_safe);
  $OUTPUT->set_env('sender', $MESSAGE->sender['string']);
  $OUTPUT->set_env('permaurl', rcmail_url('show', array('_uid' => $MESSAGE->uid, '_mbox' => $mbox_name)));
  $OUTPUT->set_env('delimiter', $RCMAIL->imap->get_hierarchy_delimiter());
  $OUTPUT->set_env('delimiter', $RCMAIL->storage->get_hierarchy_delimiter());
  $OUTPUT->set_env('mailbox', $mbox_name);
  // mimetypes supported by the browser (default settings)
@@ -77,7 +77,7 @@
  if ($MESSAGE->headers->mdn_to
      && empty($MESSAGE->headers->flags['MDNSENT'])
      && empty($MESSAGE->headers->flags['SEEN'])
      && ($RCMAIL->imap->check_permflag('MDNSENT') || $RCMAIL->imap->check_permflag('*'))
      && ($RCMAIL->storage->check_permflag('MDNSENT') || $RCMAIL->storage->check_permflag('*'))
      && $mbox_name != $CONFIG['drafts_mbox']
      && $mbox_name != $CONFIG['sent_mbox']
  ) {
@@ -175,8 +175,8 @@
{
  global $MESSAGE, $RCMAIL, $CONFIG;
  $mbox  = $RCMAIL->imap->get_mailbox_name();
  $delim = $RCMAIL->imap->get_hierarchy_delimiter();
  $mbox  = $RCMAIL->storage->get_folder();
  $delim = $RCMAIL->storage->get_hierarchy_delimiter();
  $dbox  = $CONFIG['drafts_mbox'];
  // the message is not a draft
@@ -248,7 +248,7 @@
if ($MESSAGE && $MESSAGE->headers && empty($MESSAGE->headers->flags['SEEN']) &&
  ($RCMAIL->action == 'show' || ($RCMAIL->action == 'preview' && intval($CONFIG['preview_pane_mark_read']) == 0)))
{
  if ($RCMAIL->imap->set_flag($MESSAGE->uid, 'SEEN')) {
  if ($RCMAIL->storage->set_flag($MESSAGE->uid, 'SEEN')) {
    if ($count = rcmail_get_unseen_count($mbox_name)) {
      rcmail_set_unseen_count($mbox_name, $count - 1);
    }
program/steps/mail/viewsource.inc
@@ -24,7 +24,7 @@
// similar code as in program/steps/mail/get.inc
if ($uid = get_input_value('_uid', RCUBE_INPUT_GET))
{
  $headers = $RCMAIL->imap->get_headers($uid);
  $headers = $RCMAIL->storage->get_message_headers($uid);
  $charset = $headers->charset ? $headers->charset : $CONFIG['default_charset'];
  header("Content-Type: text/plain; charset={$charset}");
@@ -44,7 +44,7 @@
    header("Content-Disposition: attachment; filename=\"$filename\"");
  }
  $RCMAIL->imap->print_raw_body($uid);
  $RCMAIL->storage->print_raw_body($uid);
}
else
{
program/steps/settings/edit_folder.inc
@@ -21,12 +21,11 @@
// WARNING: folder names in UI are encoded with RCMAIL_CHARSET
// init IMAP connection
$RCMAIL->imap_connect();
function rcmail_folder_form($attrib)
{
    global $RCMAIL;
    $storage = $RCMAIL->get_storage();
    // edited folder name (empty in create-folder mode)
    $mbox      = trim(get_input_value('_mbox', RCUBE_INPUT_GPC, true));
@@ -36,13 +35,13 @@
    $parent      = trim(get_input_value('_path', RCUBE_INPUT_GPC, true));
    $parent_imap = rcube_charset_convert($parent, RCMAIL_CHARSET, 'UTF7-IMAP');
    $threading_supported = $RCMAIL->imap->get_capability('THREAD');
    $delimiter = $RCMAIL->imap->get_hierarchy_delimiter();
    $threading_supported = $storage->get_capability('THREAD');
    $delimiter = $storage->get_hierarchy_delimiter();
    // Get mailbox parameters
    if (strlen($mbox)) {
        $options   = rcmail_folder_options($mbox_imap);
        $namespace = $RCMAIL->imap->get_namespace();
        $namespace = $storage->get_namespace();
        $path   = explode($delimiter, $mbox_imap);
        $folder = array_pop($path);
@@ -57,14 +56,14 @@
        // allow creating subfolders of INBOX folder
        if ($path == 'INBOX') {
            $path = $RCMAIL->imap->mod_mailbox($path, 'in');
            $path = $storage->mod_folder($path, 'in');
        }
    }
    // remove personal namespace prefix
    if (strlen($path)) {
        $path_id = $path;
        $path    = $RCMAIL->imap->mod_mailbox($path.$delimiter);
        $path    = $storage->mod_folder($path.$delimiter);
        if ($path[strlen($path)-1] == $delimiter) {
            $path = substr($path, 0, -1);
        }
@@ -193,7 +192,7 @@
        );
        if ((!$options['noselect'] && !$options['is_root']) || $mbox_imap == 'INBOX') {
            $msgcount = $RCMAIL->imap->messagecount($mbox_imap, 'ALL', true, false);
            $msgcount = $storage->count($mbox_imap, 'ALL', true, false);
            // Size
            if ($msgcount) {
program/steps/settings/folders.inc
@@ -23,20 +23,20 @@
// WARNING: folder names in UI are encoded with RCMAIL_CHARSET
// init IMAP connection
$RCMAIL->imap_connect();
$STORAGE = $RCMAIL->get_storage();
// subscribe mailbox
if ($RCMAIL->action == 'subscribe')
{
    $mbox = get_input_value('_mbox', RCUBE_INPUT_POST, true, 'UTF7-IMAP');
    if (strlen($mbox)) {
        $result = $RCMAIL->imap->subscribe(array($mbox));
        $result = $STORAGE->subscribe(array($mbox));
        // Handle virtual (non-existing) folders
        if (!$result && $RCMAIL->imap->get_error_code() == -1 &&
            $RCMAIL->imap->get_response_code() == rcube_imap::TRYCREATE
        if (!$result && $STORAGE->get_error_code() == -1 &&
            $STORAGE->get_response_code() == rcube_storage::TRYCREATE
        ) {
            $result = $RCMAIL->imap->create_mailbox($mbox, true);
            $result = $STORAGE->create_folder($mbox, true);
            if ($result) {
                // @TODO: remove 'virtual' class of folder's row
            }
@@ -45,7 +45,7 @@
        if ($result) {
            // Handle subscription of protected folder (#1487656)
            if ($CONFIG['protect_default_folders'] == true
                && in_array($mbox, $CONFIG['default_imap_folders'])
                && in_array($mbox, $CONFIG['default_folders'])
            ) {
                $OUTPUT->command('disable_subscription', $mbox);
            }
@@ -62,7 +62,7 @@
{
    $mbox = get_input_value('_mbox', RCUBE_INPUT_POST, true, 'UTF7-IMAP');
    if (strlen($mbox)) {
        $result = $RCMAIL->imap->unsubscribe(array($mbox));
        $result = $STORAGE->unsubscribe(array($mbox));
        if ($result)
            $OUTPUT->show_message('folderunsubscribed', 'confirmation');
        else
@@ -80,7 +80,7 @@
        $plugin = $RCMAIL->plugins->exec_hook('folder_delete', array('name' => $mbox));
        if (!$plugin['abort']) {
            $deleted = $RCMAIL->imap->delete_mailbox($plugin['name']);
            $deleted = $STORAGE->delete_folder($plugin['name']);
        }
        else {
            $deleted = $plugin['result'];
@@ -126,19 +126,19 @@
{
    $mbox_utf8 = get_input_value('_mbox', RCUBE_INPUT_POST, true);
    $mbox      = rcube_charset_convert($mbox_utf8, RCMAIL_CHARSET, 'UTF7-IMAP');
    $delimiter = $RCMAIL->imap->get_hierarchy_delimiter();
    $delimiter = $STORAGE->get_hierarchy_delimiter();
    $trash_regexp = '/^' . preg_quote($CONFIG['trash_mbox'] . $delimiter, '/') . '/';
    // we should only be purging trash (or their subfolders)
    if (!strlen($CONFIG['trash_mbox']) || $mbox == $CONFIG['trash_mbox']
        || preg_match($trash_regexp, $mbox)
    ) {
        $success = $RCMAIL->imap->clear_mailbox($mbox);
        $success = $STORAGE->delete_message('*', $mbox);
        $delete = true;
    }
    // copy to Trash
    else {
        $success = $RCMAIL->imap->move_message('1:*', $CONFIG['trash_mbox'], $mbox);
        $success = $STORAGE->move_message('1:*', $CONFIG['trash_mbox'], $mbox);
        $delete = false;
    }
@@ -164,7 +164,7 @@
{
    $name = trim(get_input_value('_mbox', RCUBE_INPUT_POST, true));
    $size = $RCMAIL->imap->get_mailbox_size($name);
    $size = $STORAGE->folder_size($name);
    // @TODO: check quota and show percentage usage of specified mailbox?
@@ -199,13 +199,15 @@
        $table->add_header('subscribed', '');
    }
    // get folders from server
    $RCMAIL->imap->clear_cache('mailboxes', true);
    $STORAGE = $RCMAIL->get_storage();
    $a_unsubscribed = $RCMAIL->imap->list_unsubscribed();
    $a_subscribed   = $RCMAIL->imap->list_mailboxes('', '*', null, null, true); // unsorted
    $delimiter      = $RCMAIL->imap->get_hierarchy_delimiter();
    $namespace      = $RCMAIL->imap->get_namespace();
    // get folders from server
    $STORAGE->clear_cache('mailboxes', true);
    $a_unsubscribed = $STORAGE->list_folders();
    $a_subscribed   = $STORAGE->list_folders_subscribed('', '*', null, null, true); // unsorted
    $delimiter      = $STORAGE->get_hierarchy_delimiter();
    $namespace      = $STORAGE->get_namespace();
    $a_js_folders   = array();
    $seen           = array();
    $list_folders   = array();
@@ -213,7 +215,7 @@
    // pre-process folders list
    foreach ($a_unsubscribed as $i => $folder) {
        $folder_id     = $folder;
        $folder        = $RCMAIL->imap->mod_mailbox($folder);
        $folder        = $STORAGE->mod_folder($folder);
        $foldersplit   = explode($delimiter, $folder);
        $name          = rcube_charset_convert(array_pop($foldersplit), 'UTF7-IMAP');
        $parent_folder = join($delimiter, $foldersplit);
@@ -269,7 +271,7 @@
        $idx        = $i + 1;
        $sub_key    = array_search($folder['id'], $a_subscribed);
        $subscribed = $sub_key !== false;
        $protected  = ($CONFIG['protect_default_folders'] == true && in_array($folder['id'], $CONFIG['default_imap_folders']));
        $protected  = ($CONFIG['protect_default_folders'] == true && in_array($folder['id'], $CONFIG['default_folders']));
        $noselect   = false;
        $classes    = array($i%2 ? 'even' : 'odd');
@@ -283,7 +285,7 @@
        }
        if (!$protected) {
            $attrs = $RCMAIL->imap->mailbox_attributes($folder['id']);
            $attrs = $STORAGE->folder_attributes($folder['id']);
            $noselect = in_array('\\Noselect', $attrs);
        }
@@ -342,7 +344,7 @@
    $OUTPUT->add_gui_object('subscriptionlist', $attrib['id']);
    $OUTPUT->set_env('subscriptionrows', $a_js_folders);
    $OUTPUT->set_env('defaultfolders', $CONFIG['default_imap_folders']);
    $OUTPUT->set_env('defaultfolders', $CONFIG['default_folders']);
    $OUTPUT->set_env('delimiter', $delimiter);
    return $form_start . $table->show($attrib) . $form_end;
@@ -367,13 +369,14 @@
{
    global $RCMAIL;
    $delimiter = $RCMAIL->imap->get_hierarchy_delimiter();
    $storage   = $RCMAIL->get_storage();
    $delimiter = $storage->get_hierarchy_delimiter();
    $plugin = $RCMAIL->plugins->exec_hook('folder_rename', array(
        'oldname' => $oldname, 'newname' => $newname));
    if (!$plugin['abort']) {
        $renamed =  $RCMAIL->imap->rename_mailbox($oldname, $newname);
        $renamed =  $storage->rename_folder($oldname, $newname);
    }
    else {
        $renamed = $plugin['result'];
@@ -405,7 +408,7 @@
$OUTPUT->set_pagetitle(rcube_label('folders'));
$OUTPUT->include_script('list.js');
$OUTPUT->set_env('quota', $RCMAIL->imap->get_capability('QUOTA'));
$OUTPUT->set_env('quota', $STORAGE->get_capability('QUOTA'));
// add some labels to client
$OUTPUT->add_label('deletefolderconfirm', 'purgefolderconfirm', 'folderdeleting',
program/steps/settings/func.inc
@@ -317,8 +317,8 @@
      );
    }
    $RCMAIL->imap_connect();
    $threading_supported = $RCMAIL->imap->get_capability('THREAD');
    $storage             = $RCMAIL->get_storage();
    $threading_supported = $storage->get_capability('THREAD');
    if (!isset($no_override['autoexpand_threads']) && $threading_supported) {
      $field_id = 'rcmfd_autoexpand_threads';
@@ -681,10 +681,7 @@
    );
    // Configure special folders
    if (!isset($no_override['default_imap_folders'])) {
      $RCMAIL->imap_connect();
    if (!isset($no_override['default_folders'])) {
      // load folders list only when needed
      if ($current) {
        $select = rcmail_mailbox_select(array(
@@ -849,7 +846,7 @@
{
    global $RCMAIL;
    $options = $RCMAIL->imap->mailbox_info($mailbox);
    $options = $RCMAIL->get_storage()->folder_info($mailbox);
    $options['protected'] = $options['is_root'] || ($options['special'] && $RCMAIL->config->get('protect_default_folders'));
    return $options;
@@ -867,11 +864,12 @@
{
    global $RCMAIL, $CONFIG, $OUTPUT;
    $delimiter    = $RCMAIL->imap->get_hierarchy_delimiter();
    $storage      = $RCMAIL->get_storage();
    $delimiter    = $storage->get_hierarchy_delimiter();
    $name_utf8    = rcube_charset_convert($name, 'UTF7-IMAP');
    $protected    = ($CONFIG['protect_default_folders'] == true && in_array($name, $CONFIG['default_imap_folders']));
    $protected    = ($CONFIG['protect_default_folders'] == true && in_array($name, $CONFIG['default_folders']));
    $foldersplit  = explode($delimiter, $RCMAIL->imap->mod_mailbox($name));
    $foldersplit  = explode($delimiter, $storage->mod_folder($name));
    $level        = count($foldersplit) - 1;
    $display_name = str_repeat('&nbsp;&nbsp;&nbsp;&nbsp;', $level)
        . Q($protected ? rcmail_localize_foldername($name) : rcube_charset_convert($foldersplit[$level], 'UTF7-IMAP'));
program/steps/settings/save_folder.inc
@@ -22,7 +22,7 @@
// WARNING: folder names in UI are encoded with RCMAIL_CHARSET
// init IMAP connection
$RCMAIL->imap_connect();
$STORAGE = $RCMAIL->get_storage();
$name = trim(get_input_value('_name', RCUBE_INPUT_POST, true));
@@ -33,7 +33,7 @@
$old_imap  = rcube_charset_convert($old, RCMAIL_CHARSET, 'UTF7-IMAP');
// $path is in UTF7-IMAP already
$delimiter = $RCMAIL->imap->get_hierarchy_delimiter();
$delimiter = $STORAGE->get_hierarchy_delimiter();
$options = strlen($old_imap) ? rcmail_folder_options($old_imap) : array();
// Folder name checks
@@ -66,13 +66,13 @@
        $name_imap = $path . $delimiter . $name_imap;
    }
    else {
        $name_imap = $RCMAIL->imap->mod_mailbox($name_imap, 'in');
        $name_imap = $STORAGE->mod_folder($name_imap, 'in');
    }
}
// Check access rights to the parent folder
if (!$error && strlen($path) && (!strlen($old_imap) || $old_imap != $name_imap)) {
    $parent_opts = $RCMAIL->imap->mailbox_info($path);
    $parent_opts = $STORAGE->folder_info($path);
    if ($parent_opts['namespace'] != 'personal'
        && (empty($parent_opts['rights']) || !preg_match('/[ck]/', implode($parent_opts)))
    ) {
@@ -103,7 +103,7 @@
    $folder = $plugin['record'];
    if (!$plugin['abort']) {
        $created = $RCMAIL->imap->create_mailbox($folder['name'], $folder['subscribe']);
        $created = $STORAGE->create_folder($folder['name'], $folder['subscribe']);
    }
    else {
        $created = $plugin['result'];
@@ -143,7 +143,7 @@
    if (!$plugin['abort']) {
        if ($rename) {
            $updated = $RCMAIL->imap->rename_mailbox($folder['oldname'], $folder['name']);
            $updated = $STORAGE->rename_folder($folder['oldname'], $folder['name']);
        }
        else {
            $updated = true;
program/steps/settings/save_prefs.inc
@@ -181,15 +181,15 @@
  break;
  case 'folders':
    // special handling for 'default_imap_folders'
    if (in_array('default_imap_folders', (array)$CONFIG['dont_override'])) {
    // special handling for 'default_folders'
    if (in_array('default_folders', (array)$CONFIG['dont_override'])) {
      foreach (array('drafts_mbox','sent_mbox','junk_mbox','trash_mbox') as $p)
        $a_user_prefs[$p] = $CONFIG[$p];
    } else {
      $a_user_prefs['default_imap_folders'] = array('INBOX');
      $a_user_prefs['default_folders'] = array('INBOX');
      foreach (array('drafts_mbox','sent_mbox','junk_mbox','trash_mbox') as $p) {
        if ($a_user_prefs[$p])
          $a_user_prefs['default_imap_folders'][] = $a_user_prefs[$p];
          $a_user_prefs['default_folders'][] = $a_user_prefs[$p];
      }
    }
tests/mailfunc.php
@@ -16,8 +16,7 @@
    $GLOBALS['RCMAIL'] = $RCMAIL = rcmail::get_instance();
    $GLOBALS['OUTPUT'] = $OUTPUT = $RCMAIL->load_gui();
    $RCMAIL->action = 'autocomplete';
    $RCMAIL->imap_init(false);
    $IMAP = $RCMAIL->imap;
    $RCMAIL->storage_init(false);
    
    require_once INSTALL_PATH . 'program/steps/mail/func.inc';