From c321a955a7b0f6d6b13ffaebf040a6c7091037ae Mon Sep 17 00:00:00 2001
From: thomascube <thomas@roundcube.net>
Date: Mon, 16 Jan 2012 10:14:41 -0500
Subject: [PATCH] Merged devel-framework branch (r5746:5779) back into trunk

---
 program/steps/mail/pagenav.inc          |   12 
 program/include/main.inc                |  380 ---
 program/steps/mail/compose.inc          |   22 
 program/steps/mail/show.inc             |   14 
 tests/mailfunc.php                      |    3 
 program/include/iniset.php              |    0 
 program/include/rcube_charset.php       |  745 +++++++
 index.php                               |   12 
 program/include/rcube_shared.inc        |  117 -
 program/steps/settings/edit_folder.inc  |   17 
 installer/rcube_install.php             |    5 
 program/steps/mail/folders.inc          |    6 
 program/steps/mail/check_recent.inc     |   32 
 program/include/rcube_imap_cache.php    |   34 
 program/steps/mail/func.inc             |   90 
 program/steps/settings/func.inc         |   18 
 program/include/rcube_storage.php       | 1055 +++++++++++
 program/steps/mail/mark.inc             |   45 
 program/include/rcube_result_thread.php |   52 
 program/steps/settings/folders.inc      |   53 
 program/steps/mail/viewsource.inc       |    4 
 program/include/rcube_message.php       |   22 
 program/include/rcube_result_index.php  |   26 
 program/steps/mail/sendmail.inc         |   20 
 program/include/rcube_config.php        |    6 
 program/steps/mail/headers.inc          |    2 
 program/steps/settings/save_prefs.inc   |    8 
 program/steps/mail/copy.inc             |    2 
 program/steps/mail/move_del.inc         |   43 
 /dev/null                               |  249 --
 program/steps/mail/list.inc             |   23 
 program/steps/mail/search.inc           |   20 
 program/include/rcube_imap.php          | 2210 +++++++++++-----------
 program/include/rcmail.php              |  265 +-
 program/localization/en_US/messages.inc |    2 
 program/steps/settings/save_folder.inc  |   12 
 program/steps/mail/getunread.inc        |    6 
 program/steps/mail/get.inc              |    8 
 program/include/rcube_template.php      |    4 
 bin/msgexport.sh                        |   19 
 40 files changed, 3,414 insertions(+), 2,249 deletions(-)

diff --git a/bin/msgexport.sh b/bin/msgexport.sh
index 5f90619..c876f5f 100755
--- a/bin/msgexport.sh
+++ b/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)
 	{
diff --git a/index.php b/index.php
index a2d012c..9abd0b4 100644
--- a/index.php
+++ b/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();
diff --git a/installer/rcube_install.php b/installer/rcube_install.php
index cc946ec..2e1a473 100644
--- a/installer/rcube_install.php
+++ b/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;
diff --git a/program/include/iniset.php b/program/include/iniset.php
old mode 100755
new mode 100644
diff --git a/program/include/main.inc b/program/include/main.inc
index 9f154e4..29f6de7 100644
--- a/program/include/main.inc
+++ b/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
diff --git a/program/include/rcmail.php b/program/include/rcmail.php
index fbf691a..7c01edf 100644
--- a/program/include/rcmail.php
+++ b/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();
   }
 
 }
diff --git a/program/include/rcube_charset.php b/program/include/rcube_charset.php
new file mode 100644
index 0000000..29de426
--- /dev/null
+++ b/program/include/rcube_charset.php
@@ -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;
+    }
+
+}
diff --git a/program/include/rcube_config.php b/program/include/rcube_config.php
index 0383364..6ccf69b 100644
--- a/program/include/rcube_config.php
+++ b/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) {
diff --git a/program/include/rcube_imap.php b/program/include/rcube_imap.php
index 6542f3c..8d5acfb 100644
--- a/program/include/rcube_imap.php
+++ b/program/include/rcube_imap.php
@@ -5,12 +5,12 @@
  | program/include/rcube_imap.php                                        |
  |                                                                       |
  | This file is part of the Roundcube Webmail client                     |
- | Copyright (C) 2005-2011, The Roundcube Dev Team                       |
- | Copyright (C) 2011, Kolab Systems AG                                  |
+ | Copyright (C) 2005-2012, The Roundcube Dev Team                       |
+ | Copyright (C) 2011-2012, Kolab Systems AG                             |
  | Licensed under the GNU GPL                                            |
  |                                                                       |
  | PURPOSE:                                                              |
- |   IMAP Engine                                                         |
+ |   IMAP Storage Engine                                                 |
  |                                                                       |
  +-----------------------------------------------------------------------+
  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
@@ -30,15 +30,8 @@
  * @author     Aleksander Machniak <alec@alec.pl>
  * @version    2.0
  */
-class rcube_imap
+class rcube_imap extends rcube_storage
 {
-    public $skip_deleted = false;
-    public $page_size = 10;
-    public $list_page = 1;
-    public $threading = false;
-    public $fetch_add_headers = '';
-    public $get_all_headers = false;
-
     /**
      * Instance of rcube_imap_generic
      *
@@ -51,87 +44,70 @@
      *
      * @var rcube_imap_cache
      */
-    private $mcache;
+    protected $mcache;
 
     /**
      * Instance of rcube_cache
      *
      * @var rcube_cache
      */
-    private $cache;
+    protected $cache;
 
     /**
      * Internal (in-memory) cache
      *
      * @var array
      */
-    private $icache = array();
+    protected $icache = array();
 
-    private $mailbox = 'INBOX';
-    private $delimiter = NULL;
-    private $namespace = NULL;
-    private $sort_field = '';
-    private $sort_order = 'DESC';
-    private $default_charset = 'ISO-8859-1';
-    private $struct_charset = NULL;
-    private $default_folders = array('INBOX');
-    private $uid_id_map = array();
-    private $msg_headers = array();
-    public  $search_set = NULL;
-    public  $search_string = '';
-    private $search_charset = '';
-    private $search_sort_field = '';
-    private $search_threads = false;
-    private $search_sorted = false;
-    private $options = array('auth_method' => 'check');
-    private $host, $user, $pass, $port, $ssl;
-    private $caching = false;
-    private $messages_caching = 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
-     * @see rcube_imap::fetch_add_headers
-     */
-    private $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;
+    protected $list_page = 1;
+    protected $delimiter;
+    protected $namespace;
+    protected $sort_field = '';
+    protected $sort_order = 'DESC';
+    protected $struct_charset;
+    protected $uid_id_map = array();
+    protected $msg_headers = array();
+    protected $search_set;
+    protected $search_string = '';
+    protected $search_charset = '';
+    protected $search_sort_field = '';
+    protected $search_threads = false;
+    protected $search_sorted = false;
+    protected $options = array('auth_method' => 'check');
+    protected $caching = false;
+    protected $messages_caching = false;
+    protected $threading = false;
 
 
     /**
      * Object constructor.
      */
-    function __construct()
+    public function __construct()
     {
         $this->conn = new rcube_imap_generic();
 
         // Set namespace and delimiter from session,
         // so some methods would work before connection
-        if (isset($_SESSION['imap_namespace']))
+        if (isset($_SESSION['imap_namespace'])) {
             $this->namespace = $_SESSION['imap_namespace'];
-        if (isset($_SESSION['imap_delimiter']))
+        }
+        if (isset($_SESSION['imap_delimiter'])) {
             $this->delimiter = $_SESSION['imap_delimiter'];
+        }
+    }
+
+
+    /**
+     * Magic getter for backward compat.
+     *
+     * @deprecated.
+     */
+    public function __get($name)
+    {
+        if (isset($this->{$name})) {
+            return $this->{$name};
+        }
     }
 
 
@@ -143,14 +119,15 @@
      * @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
-     * @access public
      */
-    function connect($host, $user, $pass, $port=143, $use_ssl=null)
+    public function connect($host, $user, $pass, $port=143, $use_ssl=null)
     {
         // check for OpenSSL support in PHP build
-        if ($use_ssl && extension_loaded('openssl'))
+        if ($use_ssl && extension_loaded('openssl')) {
             $this->options['ssl_mode'] = $use_ssl == 'imaps' ? 'ssl' : $use_ssl;
+        }
         else if ($use_ssl) {
             raise_error(array('code' => 403, 'type' => 'imap',
                 'file' => __FILE__, 'line' => __LINE__,
@@ -161,7 +138,7 @@
         $this->options['port'] = $port;
 
         if ($this->options['debug']) {
-            $this->conn->setDebug(true, array($this, 'debug_handler'));
+            $this->set_debug(true);
 
             $this->options['ident'] = array(
                 'name' => 'Roundcube Webmail',
@@ -178,17 +155,23 @@
                 array_merge($this->options, array('host' => $host, 'user' => $user,
                     'attempt' => ++$attempt)));
 
-            if (!empty($data['pass']))
+            if (!empty($data['pass'])) {
                 $pass = $data['pass'];
+            }
 
             $this->conn->connect($data['host'], $data['user'], $pass, $data);
         } while(!$this->conn->connected() && $data['retry']);
 
-        $this->host = $data['host'];
-        $this->user = $data['user'];
-        $this->pass = $pass;
-        $this->port = $port;
-        $this->ssl  = $use_ssl;
+        $config = array(
+            'host'     => $data['host'],
+            'user'     => $data['user'],
+            'password' => $pass,
+            'port'     => $port,
+            'ssl'      => $use_ssl,
+        );
+
+        $this->options      = array_merge($this->options, $config);
+        $this->connect_done = true;
 
         if ($this->conn->connected()) {
             // get namespace and delimiter
@@ -212,33 +195,46 @@
 
 
     /**
-     * Close IMAP connection
+     * Close IMAP connection.
      * Usually done on script shutdown
-     *
-     * @access public
      */
-    function close()
+    public function close()
     {
         $this->conn->closeConnection();
-        if ($this->mcache)
+        if ($this->mcache) {
             $this->mcache->close();
+        }
     }
 
 
     /**
-     * Close IMAP connection and re-connect
-     * This is used to avoid some strange socket errors when talking to Courier IMAP
-     *
-     * @access public
+     * Check connection state, connect if not connected.
      */
-    function reconnect()
+    public function check_connection()
     {
-        $this->conn->closeConnection();
-        $connected = $this->connect($this->host, $this->user, $this->pass, $this->port, $this->ssl);
+        // Establish connection if it wasn't done yet
+        if (!$this->connect_done && !empty($this->options['user'])) {
+            return $this->connect(
+                $this->options['host'],
+                $this->options['user'],
+                $this->options['password'],
+                $this->options['port'],
+                $this->options['ssl']
+            );
+        }
 
-        // issue SELECT command to restore connection status
-        if ($connected && strlen($this->mailbox))
-            $this->conn->select($this->mailbox);
+        return $this->is_connected();
+    }
+
+
+    /**
+     * Checks IMAP connection.
+     *
+     * @return boolean  TRUE on success, FALSE on failure
+     */
+    public function is_connected()
+    {
+        return $this->conn->connected();
     }
 
 
@@ -247,18 +243,18 @@
      *
      * @return int Error code
      */
-    function get_error_code()
+    public function get_error_code()
     {
         return $this->conn->errornum;
     }
 
 
     /**
-     * Returns message of last error
+     * Returns text of last error
      *
-     * @return string Error message
+     * @return string Error string
      */
-    function get_error_str()
+    public function get_error_str()
     {
         return $this->conn->error;
     }
@@ -269,7 +265,7 @@
      *
      * @return int Response code
      */
-    function get_response_code()
+    public function get_response_code()
     {
         switch ($this->conn->resultcode) {
             case 'NOPERM':
@@ -295,34 +291,11 @@
 
 
     /**
-     * Returns last command response
-     *
-     * @return string Response
-     */
-    function get_response_str()
-    {
-        return $this->conn->result;
-    }
-
-
-    /**
-     * Set options to be used in rcube_imap_generic::connect()
-     *
-     * @param array $opt Options array
-     */
-    function set_options($opt)
-    {
-        $this->options = array_merge($this->options, (array)$opt);
-    }
-
-
-    /**
      * Activate/deactivate debug mode
      *
      * @param boolean $dbg True if IMAP conversation should be logged
-     * @access public
      */
-    function set_debug($dbg = true)
+    public function set_debug($dbg = true)
     {
         $this->options['debug'] = $dbg;
         $this->conn->setDebug($dbg, array($this, 'debug_handler'));
@@ -330,78 +303,21 @@
 
 
     /**
-     * Set default message charset
+     * Set internal folder reference.
+     * All operations will be perfomed on this folder.
      *
-     * This will be used for message decoding if a charset specification is not available
-     *
-     * @param  string $cs Charset string
-     * @access public
+     * @param  string $folder Folder name
      */
-    function set_charset($cs)
+    public function set_folder($folder)
     {
-        $this->default_charset = $cs;
-    }
-
-
-    /**
-     * This list of folders will be listed above all other folders
-     *
-     * @param  array $arr Indexed list of folder names
-     * @access public
-     */
-    function set_default_mailboxes($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 mailbox reference.
-     *
-     * All operations will be perfomed on this mailbox/folder
-     *
-     * @param  string $mailbox Mailbox/Folder name
-     * @access public
-     */
-    function set_mailbox($mailbox)
-    {
-        if ($this->mailbox == $mailbox)
+        if ($this->folder == $folder) {
             return;
+        }
 
-        $this->mailbox = $mailbox;
+        $this->folder = $folder;
 
-        // clear messagecount cache for this mailbox
-        $this->_clear_messagecount($mailbox);
-    }
-
-
-    /**
-     * Set internal list page
-     *
-     * @param  number $page Page number to list
-     * @access public
-     */
-    function set_page($page)
-    {
-        $this->list_page = (int)$page;
-    }
-
-
-    /**
-     * Set internal page size
-     *
-     * @param  number $size Number of messages to display on one page
-     * @access public
-     */
-    function set_pagesize($size)
-    {
-        $this->page_size = (int)$size;
+        // clear messagecount cache for this folder
+        $this->clear_messagecount($folder);
     }
 
 
@@ -415,7 +331,7 @@
      *                      3 - sorting field, string
      *                      4 - true if sorted, bool
      */
-    function set_search_set($set)
+    public function set_search_set($set)
     {
         $set = (array)$set;
 
@@ -433,8 +349,12 @@
      *
      * @return array Search set
      */
-    function get_search_set()
+    public function get_search_set()
     {
+        if (empty($this->search_set)) {
+            return null;
+        }
+
         return array(
             $this->search_string,
 	        $this->search_set,
@@ -446,66 +366,47 @@
 
 
     /**
-     * Returns the currently used mailbox name
-     *
-     * @return  string Name of the mailbox/folder
-     * @access  public
-     */
-    function get_mailbox_name()
-    {
-        return $this->mailbox;
-    }
-
-
-    /**
-     * Returns the IMAP server's capability
+     * Returns the IMAP server's capability.
      *
      * @param   string  $cap Capability name
-     * @return  mixed   Capability value or TRUE if supported, FALSE if not
-     * @access  public
-     */
-    function get_capability($cap)
-    {
-        return $this->conn->getCapability(strtoupper($cap));
-    }
-
-
-    /**
-     * Sets threading flag to the best supported THREAD algorithm
      *
-     * @param  boolean  $enable TRUE to enable and FALSE
-     * @return string   Algorithm or false if THREAD is not supported
-     * @access public
+     * @return  mixed   Capability value or TRUE if supported, FALSE if not
      */
-    function set_threading($enable=false)
+    public function get_capability($cap)
     {
-        $this->threading = false;
+        $cap      = strtoupper($cap);
+        $sess_key = "STORAGE_$cap";
 
-        if ($enable && ($caps = $this->get_capability('THREAD'))) {
-            if (in_array('REFS', $caps))
-                $this->threading = 'REFS';
-            else if (in_array('REFERENCES', $caps))
-                $this->threading = 'REFERENCES';
-            else if (in_array('ORDEREDSUBJECT', $caps))
-                $this->threading = 'ORDEREDSUBJECT';
+        if (!isset($_SESSION[$sess_key])) {
+            if (!$this->check_connection()) {
+                return false;
+            }
+
+            $_SESSION[$sess_key] = $this->conn->getCapability($cap);
         }
 
-        return $this->threading;
+        return $_SESSION[$sess_key];
     }
 
 
     /**
-     * Checks the PERMANENTFLAGS capability of the current mailbox
+     * Checks the PERMANENTFLAGS capability of the current folder
      * and returns true if the given flag is supported by the IMAP server
      *
      * @param   string  $flag Permanentflag name
+     *
      * @return  boolean True if this flag is supported
-     * @access  public
      */
-    function check_permflag($flag)
+    public function check_permflag($flag)
     {
         $flag = strtoupper($flag);
         $imap_flag = $this->conn->flags[$flag];
+
+        if ($this->folder !== null) {
+            $this->check_connection();
+        }
+        // @TODO: cache permanent flags (?)
+
         return (in_array_nocase($imap_flag, $this->conn->data['PERMANENTFLAGS']));
     }
 
@@ -516,7 +417,7 @@
      * @return  string  Delimiter string
      * @access  public
      */
-    function get_hierarchy_delimiter()
+    public function get_hierarchy_delimiter()
     {
         return $this->delimiter;
     }
@@ -528,9 +429,8 @@
      * @param string $name Namespace array index: personal, other, shared, prefix
      *
      * @return  array  Namespace data
-     * @access  public
      */
-    function get_namespace($name=null)
+    public function get_namespace($name = null)
     {
         $ns = $this->namespace;
 
@@ -545,10 +445,8 @@
 
     /**
      * Sets delimiter and namespaces
-     *
-     * @access private
      */
-    private function set_env()
+    protected function set_env()
     {
         if ($this->delimiter !== null && $this->namespace !== null) {
             return;
@@ -560,8 +458,9 @@
         $imap_shared    = $config->get('imap_ns_shared');
         $imap_delimiter = $config->get('imap_delimiter');
 
-        if (!$this->conn->connected())
+        if (!$this->check_connection()) {
             return;
+        }
 
         $ns = $this->conn->getNamespace();
 
@@ -614,7 +513,7 @@
             }
         }
 
-        // Find personal namespace prefix for mod_mailbox()
+        // Find personal namespace prefix for mod_folder()
         // Prefix can be removed when there is only one personal namespace
         if (is_array($this->namespace['personal']) && count($this->namespace['personal']) == 1) {
             $this->namespace['prefix'] = $this->namespace['personal'][0][0];
@@ -626,75 +525,83 @@
 
 
     /**
-     * Get message count for a specific mailbox
+     * Get message count for a specific folder
      *
-     * @param  string  $mailbox Mailbox/folder name
+     * @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 mailbox_status()
+     *                          required for folder_status()
+     *
      * @return int     Number of messages
-     * @access public
      */
-    function messagecount($mailbox='', $mode='ALL', $force=false, $status=true)
+    public function count($folder='', $mode='ALL', $force=false, $status=true)
     {
-        if (!strlen($mailbox)) {
-            $mailbox = $this->mailbox;
+        if (!strlen($folder)) {
+            $folder = $this->folder;
         }
 
-        return $this->_messagecount($mailbox, $mode, $force, $status);
+        return $this->messagecount($folder, $mode, $force, $status);
     }
 
 
     /**
-     * Private method for getting nr of messages
+     * protected method for getting nr of messages
      *
-     * @param string  $mailbox Mailbox name
+     * @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 mailbox_status()
+     *                         required for folder_status()
+     *
      * @return int Number of messages
-     * @access  private
-     * @see     rcube_imap::messagecount()
+     * @see rcube_imap::count()
      */
-    private function _messagecount($mailbox, $mode='ALL', $force=false, $status=true)
+    protected function messagecount($folder, $mode='ALL', $force=false, $status=true)
     {
         $mode = strtoupper($mode);
 
         // count search set
-        if ($this->search_string && $mailbox == $this->mailbox && ($mode == 'ALL' || $mode == 'THREADS') && !$force) {
-            if ($mode == 'ALL')
-                return $this->search_set->countMessages();
-            else
+        if ($this->search_string && $folder == $this->folder && ($mode == 'ALL' || $mode == 'THREADS') && !$force) {
+            if ($mode == 'ALL') {
+                return $this->search_set->count_messages();
+            }
+            else {
                 return $this->search_set->count();
+            }
         }
 
-        $a_mailbox_cache = $this->get_cache('messagecount');
+        $a_folder_cache = $this->get_cache('messagecount');
 
         // return cached value
-        if (!$force && is_array($a_mailbox_cache[$mailbox]) && isset($a_mailbox_cache[$mailbox][$mode]))
-            return $a_mailbox_cache[$mailbox][$mode];
+        if (!$force && is_array($a_folder_cache[$folder]) && isset($a_folder_cache[$folder][$mode])) {
+            return $a_folder_cache[$folder][$mode];
+        }
 
-        if (!is_array($a_mailbox_cache[$mailbox]))
-            $a_mailbox_cache[$mailbox] = array();
+        if (!is_array($a_folder_cache[$folder])) {
+            $a_folder_cache[$folder] = array();
+        }
 
         if ($mode == 'THREADS') {
-            $res   = $this->fetch_threads($mailbox, $force);
+            $res   = $this->fetch_threads($folder, $force);
             $count = $res->count();
 
             if ($status) {
-                $msg_count = $res->countMessages();
-                $this->set_folder_stats($mailbox, 'cnt', $msg_count);
-                $this->set_folder_stats($mailbox, 'maxuid', $msg_count ? $this->id2uid($msg_count, $mailbox) : 0);
+                $msg_count = $res->count_messages();
+                $this->set_folder_stats($folder, 'cnt', $msg_count);
+                $this->set_folder_stats($folder, 'maxuid', $msg_count ? $this->id2uid($msg_count, $folder) : 0);
             }
+        }
+        // Need connection here
+        else if (!$this->check_connection()) {
+            return 0;
         }
         // RECENT count is fetched a bit different
         else if ($mode == 'RECENT') {
-            $count = $this->conn->countRecent($mailbox);
+            $count = $this->conn->countRecent($folder);
         }
         // use SEARCH for message counting
-        else if ($this->skip_deleted) {
+        else if (!empty($this->options['skip_deleted'])) {
             $search_str = "ALL UNDELETED";
             $keys       = array('COUNT');
 
@@ -714,35 +621,36 @@
 
             // get message count using (E)SEARCH
             // not very performant but more precise (using UNDELETED)
-            $index = $this->conn->search($mailbox, $search_str, true, $keys);
+            $index = $this->conn->search($folder, $search_str, true, $keys);
             $count = $index->count();
 
             if ($mode == 'ALL') {
-                // Cache index data, will be used in message_index_direct()
+                // Cache index data, will be used in index_direct()
                 $this->icache['undeleted_idx'] = $index;
 
                 if ($status) {
-                    $this->set_folder_stats($mailbox, 'cnt', $count);
-                    $this->set_folder_stats($mailbox, 'maxuid', $index->max());
+                    $this->set_folder_stats($folder, 'cnt', $count);
+                    $this->set_folder_stats($folder, 'maxuid', $index->max());
                 }
             }
         }
         else {
-            if ($mode == 'UNSEEN')
-                $count = $this->conn->countUnseen($mailbox);
+            if ($mode == 'UNSEEN') {
+                $count = $this->conn->countUnseen($folder);
+            }
             else {
-                $count = $this->conn->countMessages($mailbox);
+                $count = $this->conn->countMessages($folder);
                 if ($status) {
-                    $this->set_folder_stats($mailbox,'cnt', $count);
-                    $this->set_folder_stats($mailbox, 'maxuid', $count ? $this->id2uid($count, $mailbox) : 0);
+                    $this->set_folder_stats($folder,'cnt', $count);
+                    $this->set_folder_stats($folder, 'maxuid', $count ? $this->id2uid($count, $folder) : 0);
                 }
             }
         }
 
-        $a_mailbox_cache[$mailbox][$mode] = (int)$count;
+        $a_folder_cache[$folder][$mode] = (int)$count;
 
         // write back to cache
-        $this->update_cache('messagecount', $a_mailbox_cache);
+        $this->update_cache('messagecount', $a_folder_cache);
 
         return (int)$count;
     }
@@ -750,42 +658,40 @@
 
     /**
      * Public method for listing headers
-     * convert mailbox name with root dir first
      *
-     * @param   string   $mailbox    Mailbox/folder name
+     * @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
-     * @access  public
      */
-    public function list_headers($mailbox='', $page=NULL, $sort_field=NULL, $sort_order=NULL, $slice=0)
+    public function list_messages($folder='', $page=NULL, $sort_field=NULL, $sort_order=NULL, $slice=0)
     {
-        if (!strlen($mailbox)) {
-            $mailbox = $this->mailbox;
+        if (!strlen($folder)) {
+            $folder = $this->folder;
         }
 
-        return $this->_list_headers($mailbox, $page, $sort_field, $sort_order, $slice);
+        return $this->_list_messages($folder, $page, $sort_field, $sort_order, $slice);
     }
 
 
     /**
-     * Private method for listing message headers
+     * protected method for listing message headers
      *
-     * @param   string   $mailbox    Mailbox name
+     * @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
-     * @see     rcube_imap::list_headers
+     * @see     rcube_imap::list_messages
      */
-    private function _list_headers($mailbox='', $page=NULL, $sort_field=NULL, $sort_order=NULL, $slice=0)
+    protected function _list_messages($folder='', $page=NULL, $sort_field=NULL, $sort_order=NULL, $slice=0)
     {
-        if (!strlen($mailbox)) {
+        if (!strlen($folder)) {
             return array();
         }
 
@@ -793,18 +699,18 @@
         $page = $page ? $page : $this->list_page;
 
         // use saved message set
-        if ($this->search_string && $mailbox == $this->mailbox) {
-            return $this->_list_header_set($mailbox, $page, $slice);
+        if ($this->search_string && $folder == $this->folder) {
+            return $this->list_search_messages($folder, $page, $slice);
         }
 
         if ($this->threading) {
-            return $this->_list_thread_headers($mailbox, $page, $slice);
+            return $this->list_thread_messages($folder, $page, $slice);
         }
 
         // get UIDs of all messages in the folder, sorted
-        $index = $this->message_index($mailbox, $this->sort_field, $this->sort_order);
+        $index = $this->index($folder, $this->sort_field, $this->sort_order);
 
-        if ($index->isEmpty()) {
+        if ($index->is_empty()) {
             return array();
         }
 
@@ -813,58 +719,64 @@
 
         $index->slice($from, $to - $from);
 
-        if ($slice)
+        if ($slice) {
             $index->slice(-$slice, $slice);
+        }
 
         // fetch reqested messages headers
         $a_index = $index->get();
-        $a_msg_headers = $this->fetch_headers($mailbox, $a_index);
+        $a_msg_headers = $this->fetch_headers($folder, $a_index);
 
         return array_values($a_msg_headers);
     }
 
 
     /**
-     * Private method for listing message headers using threads
+     * protected method for listing message headers using threads
      *
-     * @param   string   $mailbox    Mailbox/folder name
+     * @param   string   $folder     Folder name
      * @param   int      $page       Current page to list
      * @param   int      $slice      Number of slice items to extract from result array
      *
      * @return  array    Indexed array with message header objects
-     * @see     rcube_imap::list_headers
+     * @see     rcube_imap::list_messages
      */
-    private function _list_thread_headers($mailbox, $page, $slice=0)
+    protected function list_thread_messages($folder, $page, $slice=0)
     {
         // get all threads (not sorted)
-        if ($mcache = $this->get_mcache_engine())
-            $threads = $mcache->get_thread($mailbox);
-        else
-            $threads = $this->fetch_threads($mailbox);
+        if ($mcache = $this->get_mcache_engine()) {
+            $threads = $mcache->get_thread($folder);
+        }
+        else {
+            $threads = $this->fetch_threads($folder);
+        }
 
-        return $this->_fetch_thread_headers($mailbox, $threads, $page, $slice);
+        return $this->fetch_thread_headers($folder, $threads, $page, $slice);
     }
-
 
     /**
      * Method for fetching threads data
      *
-     * @param  string $mailbox  Folder name
-     * @param  bool   $force    Use IMAP server, no cache
+     * @param  string $folder  Folder name
+     * @param  bool   $force   Use IMAP server, no cache
      *
      * @return rcube_imap_thread Thread data object
      */
-    function fetch_threads($mailbox, $force = false)
+    function fetch_threads($folder, $force = false)
     {
         if (!$force && ($mcache = $this->get_mcache_engine())) {
             // don't store in self's internal cache, cache has it's own internal cache
-            return $mcache->get_thread($mailbox);
+            return $mcache->get_thread($folder);
         }
 
         if (empty($this->icache['threads'])) {
+            if (!$this->check_connection()) {
+                return new rcube_result_thread();
+            }
+
             // get all threads
-            $result = $this->conn->thread($mailbox, $this->threading,
-                $this->skip_deleted ? 'UNDELETED' : '', true);
+            $result = $this->conn->thread($folder, $this->threading,
+                $this->options['skip_deleted'] ? 'UNDELETED' : '', true);
 
             // add to internal (fast) cache
             $this->icache['threads'] = $result;
@@ -875,17 +787,16 @@
 
 
     /**
-     * Private method for fetching threaded messages headers
+     * protected method for fetching threaded messages headers
      *
-     * @param string              $mailbox    Mailbox name
+     * @param string              $folder     Folder name
      * @param rcube_result_thread $threads    Threads data object
      * @param int                 $page       List page number
      * @param int                 $slice      Number of threads to slice
      *
      * @return array  Messages headers
-     * @access  private
      */
-    private function _fetch_thread_headers($mailbox, $threads, $page, $slice=0)
+    protected function fetch_thread_headers($folder, $threads, $page, $slice=0)
     {
         // Sort thread structure
         $this->sort_threads($threads);
@@ -895,39 +806,39 @@
 
         $threads->slice($from, $to - $from);
 
-        if ($slice)
+        if ($slice) {
             $threads->slice(-$slice, $slice);
+        }
 
         // Get UIDs of all messages in all threads
         $a_index = $threads->get();
 
         // fetch reqested headers from server
-        $a_msg_headers = $this->fetch_headers($mailbox, $a_index);
+        $a_msg_headers = $this->fetch_headers($folder, $a_index);
 
         unset($a_index);
 
         // Set depth, has_children and unread_children fields in headers
-        $this->_set_thread_flags($a_msg_headers, $threads);
+        $this->set_thread_flags($a_msg_headers, $threads);
 
         return array_values($a_msg_headers);
     }
 
 
     /**
-     * Private method for setting threaded messages flags:
+     * protected method for setting threaded messages flags:
      * depth, has_children and unread_children
      *
      * @param  array             $headers Reference to headers array indexed by message UID
      * @param  rcube_imap_result $threads Threads data object
      *
      * @return array Message headers array indexed by message UID
-     * @access private
      */
-    private function _set_thread_flags(&$headers, $threads)
+    protected function set_thread_flags(&$headers, $threads)
     {
         $parents = array();
 
-        list ($msg_depth, $msg_children) = $threads->getThreadData();
+        list ($msg_depth, $msg_children) = $threads->get_thread_data();
 
         foreach ($headers as $uid => $header) {
             $depth = $msg_depth[$uid];
@@ -947,24 +858,23 @@
 
 
     /**
-     * Private method for listing a set of message headers (search results)
+     * protected method for listing a set of message headers (search results)
      *
-     * @param   string   $mailbox  Mailbox/folder name
+     * @param   string   $folder   Folder name
      * @param   int      $page     Current page to list
      * @param   int      $slice    Number of slice items to extract from result array
      *
      * @return  array    Indexed array with message header objects
-     * @access  private
      */
-    private function _list_header_set($mailbox, $page, $slice=0)
+    protected function list_search_messages($folder, $page, $slice=0)
     {
-        if (!strlen($mailbox) || empty($this->search_set) || $this->search_set->isEmpty()) {
+        if (!strlen($folder) || empty($this->search_set) || $this->search_set->is_empty()) {
             return array();
         }
 
         // use saved messages from searching
         if ($this->threading) {
-            return $this->_list_thread_header_set($mailbox, $page, $slice);
+            return $this->list_search_thread_messages($folder, $page, $slice);
         }
 
         // search set is threaded, we need a new one
@@ -977,8 +887,9 @@
         $to    = $from + $this->page_size;
 
         // return empty array if no messages found
-        if ($index->isEmpty())
+        if ($index->is_empty()) {
             return array();
+        }
 
         // quickest method (default sorting)
         if (!$this->search_sort_field && !$this->sort_field) {
@@ -994,25 +905,27 @@
                 $index = clone $this->search_set;
 
                 // return empty array if no messages found
-                if ($index->isEmpty())
+                if ($index->is_empty()) {
                     return array();
+                }
             }
         }
 
         if ($got_index) {
-            if ($this->sort_order != $index->getParameters('ORDER')) {
+            if ($this->sort_order != $index->get_parameters('ORDER')) {
                 $index->revert();
             }
 
             // get messages uids for one page
             $index->slice($from, $to-$from);
 
-            if ($slice)
+            if ($slice) {
                 $index->slice(-$slice, $slice);
+            }
 
             // fetch headers
             $a_index       = $index->get();
-            $a_msg_headers = $this->fetch_headers($mailbox, $a_index);
+            $a_msg_headers = $this->fetch_headers($folder, $a_index);
 
             return array_values($a_msg_headers);
         }
@@ -1023,27 +936,33 @@
         // 300: experimantal value for best result
         if (($cnt > 300 && $cnt > $this->page_size) || !$this->sort_field) {
             // use memory less expensive (and quick) method for big result set
-            $index = clone $this->message_index('', $this->sort_field, $this->sort_order);
+            $index = clone $this->index('', $this->sort_field, $this->sort_order);
             // get messages uids for one page...
             $index->slice($start_msg, min($cnt-$from, $this->page_size));
 
-            if ($slice)
+            if ($slice) {
                 $index->slice(-$slice, $slice);
+            }
 
             // ...and fetch headers
             $a_index       = $index->get();
-            $a_msg_headers = $this->fetch_headers($mailbox, $a_index);
+            $a_msg_headers = $this->fetch_headers($folder, $a_index);
 
             return array_values($a_msg_headers);
         }
         else {
             // for small result set we can fetch all messages headers
             $a_index       = $index->get();
-            $a_msg_headers = $this->fetch_headers($mailbox, $a_index, false);
+            $a_msg_headers = $this->fetch_headers($folder, $a_index, false);
 
             // return empty array if no messages found
-            if (!is_array($a_msg_headers) || empty($a_msg_headers))
+            if (!is_array($a_msg_headers) || empty($a_msg_headers)) {
                 return array();
+            }
+
+            if (!$this->check_connection()) {
+                return array();
+            }
 
             // if not already sorted
             $a_msg_headers = $this->conn->sortHeaders(
@@ -1053,8 +972,9 @@
             $a_msg_headers = array_slice(array_values($a_msg_headers),
                 $from, min($cnt-$to, $this->page_size));
 
-            if ($slice)
+            if ($slice) {
                 $a_msg_headers = array_slice($a_msg_headers, -$slice, $slice);
+            }
 
             return $a_msg_headers;
         }
@@ -1062,56 +982,60 @@
 
 
     /**
-     * Private method for listing a set of threaded message headers (search results)
+     * protected method for listing a set of threaded message headers (search results)
      *
-     * @param   string   $mailbox    Mailbox/folder name
+     * @param   string   $folder     Folder name
      * @param   int      $page       Current page to list
      * @param   int      $slice      Number of slice items to extract from result array
      *
      * @return  array    Indexed array with message header objects
-     * @access  private
-     * @see     rcube_imap::list_header_set()
+     * @see rcube_imap::list_search_messages()
      */
-    private function _list_thread_header_set($mailbox, $page, $slice=0)
+    protected function list_search_thread_messages($folder, $page, $slice=0)
     {
         // update search_set if previous data was fetched with disabled threading
         if (!$this->search_threads) {
-            if ($this->search_set->isEmpty())
+            if ($this->search_set->is_empty()) {
                 return array();
+            }
             $this->search('', $this->search_string, $this->search_charset, $this->sort_field);
         }
 
-        return $this->_fetch_thread_headers($mailbox, clone $this->search_set, $page, $slice);
+        return $this->fetch_thread_headers($folder, clone $this->search_set, $page, $slice);
     }
 
 
     /**
      * Fetches messages headers (by UID)
      *
-     * @param  string  $mailbox  Mailbox name
+     * @param  string  $folder   Folder name
      * @param  array   $msgs     Message UIDs
      * @param  bool    $sort     Enables result sorting by $msgs
      * @param  bool    $force    Disables cache use
      *
      * @return array Messages headers indexed by UID
-     * @access private
      */
-    function fetch_headers($mailbox, $msgs, $sort = true, $force = false)
+    function fetch_headers($folder, $msgs, $sort = true, $force = false)
     {
-        if (empty($msgs))
+        if (empty($msgs)) {
             return array();
+        }
 
         if (!$force && ($mcache = $this->get_mcache_engine())) {
-            $headers = $mcache->get_messages($mailbox, $msgs);
+            $headers = $mcache->get_messages($folder, $msgs);
+        }
+        else if (!$this->check_connection()) {
+            return array();
         }
         else {
             // fetch reqested headers from server
             $headers = $this->conn->fetchHeaders(
-                $mailbox, $msgs, true, false, $this->get_fetch_headers());
+                $folder, $msgs, true, false, $this->get_fetch_headers());
         }
 
-        if (empty($headers))
+        if (empty($headers)) {
             return array();
+        }
 
         foreach ($headers as $h) {
             $a_msg_headers[$h->uid] = $h;
@@ -1129,23 +1053,24 @@
 
 
     /**
-     * Returns current status of mailbox
+     * Returns current status of folder
      *
      * We compare the maximum UID to determine the number of
      * new messages because the RECENT flag is not reliable.
      *
-     * @param string $mailbox Mailbox/folder name
+     * @param string $folder Folder name
+     *
      * @return int   Folder status
      */
-    public function mailbox_status($mailbox = null)
+    public function folder_status($folder = null)
     {
-        if (!strlen($mailbox)) {
-            $mailbox = $this->mailbox;
+        if (!strlen($folder)) {
+            $folder = $this->folder;
         }
-        $old = $this->get_folder_stats($mailbox);
+        $old = $this->get_folder_stats($folder);
 
         // refresh message count -> will update
-        $this->_messagecount($mailbox, 'ALL', true);
+        $this->messagecount($folder, 'ALL', true);
 
         $result = 0;
 
@@ -1153,14 +1078,16 @@
             return $result;
         }
 
-        $new = $this->get_folder_stats($mailbox);
+        $new = $this->get_folder_stats($folder);
 
         // got new messages
-        if ($new['maxuid'] > $old['maxuid'])
+        if ($new['maxuid'] > $old['maxuid']) {
             $result += 1;
+        }
         // some messages has been deleted
-        if ($new['cnt'] < $old['cnt'])
+        if ($new['cnt'] < $old['cnt']) {
             $result += 2;
+        }
 
         // @TODO: optional checking for messages flags changes (?)
         // @TODO: UIDVALIDITY checking
@@ -1173,71 +1100,76 @@
      * Stores folder statistic data in session
      * @TODO: move to separate DB table (cache?)
      *
-     * @param string $mailbox Mailbox name
+     * @param string $folder  Folder name
      * @param string $name    Data name
      * @param mixed  $data    Data value
      */
-    private function set_folder_stats($mailbox, $name, $data)
+    protected function set_folder_stats($folder, $name, $data)
     {
-        $_SESSION['folders'][$mailbox][$name] = $data;
+        $_SESSION['folders'][$folder][$name] = $data;
     }
 
 
     /**
      * Gets folder statistic data
      *
-     * @param string $mailbox Mailbox name
+     * @param string $folder Folder name
      *
      * @return array Stats data
      */
-    private function get_folder_stats($mailbox)
+    protected function get_folder_stats($folder)
     {
-        if ($_SESSION['folders'][$mailbox])
-            return (array) $_SESSION['folders'][$mailbox];
-        else
-            return array();
+        if ($_SESSION['folders'][$folder]) {
+            return (array) $_SESSION['folders'][$folder];
+        }
+
+        return array();
     }
 
 
     /**
      * Return sorted list of message UIDs
      *
-     * @param string $mailbox    Mailbox to get index from
+     * @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)
      */
-    public function message_index($mailbox='', $sort_field=NULL, $sort_order=NULL)
+    public function index($folder = '', $sort_field = NULL, $sort_order = NULL)
     {
-        if ($this->threading)
-            return $this->thread_index($mailbox, $sort_field, $sort_order);
+        if ($this->threading) {
+            return $this->thread_index($folder, $sort_field, $sort_order);
+        }
 
         $this->set_sort_order($sort_field, $sort_order);
 
-        if (!strlen($mailbox)) {
-            $mailbox = $this->mailbox;
+        if (!strlen($folder)) {
+            $folder = $this->folder;
         }
 
         // we have a saved search result, get index from there
         if ($this->search_string) {
             if ($this->search_threads) {
-                $this->search($mailbox, $this->search_string, $this->search_charset, $this->sort_field);
+                $this->search($folder, $this->search_string, $this->search_charset, $this->sort_field);
             }
 
             // use message index sort as default sorting
             if (!$this->sort_field || $this->search_sorted) {
                 if ($this->sort_field && $this->search_sort_field != $this->sort_field) {
-                    $this->search($mailbox, $this->search_string, $this->search_charset, $this->sort_field);
+                    $this->search($folder, $this->search_string, $this->search_charset, $this->sort_field);
                 }
                 $index = $this->search_set;
             }
+            else if (!$this->check_connection()) {
+                return new rcube_result_index();
+            }
             else {
-                $index = $this->conn->index($mailbox, $this->search_set->get(),
-                    $this->sort_field, $this->skip_deleted, true, true);
+                $index = $this->conn->index($folder, $this->search_set->get(),
+                    $this->sort_field, $this->options['skip_deleted'], true, true);
             }
 
-            if ($this->sort_order != $index->getParameters('ORDER')) {
+            if ($this->sort_order != $index->get_parameters('ORDER')) {
                 $index->revert();
             }
 
@@ -1246,12 +1178,12 @@
 
         // check local cache
         if ($mcache = $this->get_mcache_engine()) {
-            $index = $mcache->get_index($mailbox, $this->sort_field, $this->sort_order);
+            $index = $mcache->get_index($folder, $this->sort_field, $this->sort_order);
         }
         // fetch from IMAP server
         else {
-            $index = $this->message_index_direct(
-                $mailbox, $this->sort_field, $this->sort_order);
+            $index = $this->index_direct(
+                $folder, $this->sort_field, $this->sort_order);
         }
 
         return $index;
@@ -1262,44 +1194,50 @@
      * Return sorted list of message UIDs ignoring current search settings.
      * Doesn't uses cache by default.
      *
-     * @param string $mailbox    Mailbox to get index from
+     * @param string $folder     Folder to get index from
      * @param string $sort_field Sort column
      * @param string $sort_order Sort order [ASC, DESC]
      * @param bool   $skip_cache Disables cache usage
      *
      * @return rcube_result_index Sorted list of message UIDs
      */
-    public function message_index_direct($mailbox, $sort_field = null, $sort_order = null, $skip_cache = true)
+    public function index_direct($folder, $sort_field = null, $sort_order = null, $skip_cache = true)
     {
         if (!$skip_cache && ($mcache = $this->get_mcache_engine())) {
-            $index = $mcache->get_index($mailbox, $sort_field, $sort_order);
+            $index = $mcache->get_index($folder, $sort_field, $sort_order);
         }
         // use message index sort as default sorting
         else if (!$sort_field) {
-            if ($this->skip_deleted && !empty($this->icache['undeleted_idx'])
-                && $this->icache['undeleted_idx']->getParameters('MAILBOX') == $mailbox
+            if ($this->options['skip_deleted'] && !empty($this->icache['undeleted_idx'])
+                && $this->icache['undeleted_idx']->get_parameters('MAILBOX') == $folder
             ) {
                 $index = $this->icache['undeleted_idx'];
             }
-            else {
-                $index = $this->conn->search($mailbox,
-                    'ALL' .($this->skip_deleted ? ' UNDELETED' : ''), true);
+            else if (!$this->check_connection()) {
+                return new rcube_result_index();
             }
+            else {
+                $index = $this->conn->search($folder,
+                    'ALL' .($this->options['skip_deleted'] ? ' UNDELETED' : ''), true);
+            }
+        }
+        else if (!$this->check_connection()) {
+            return new rcube_result_index();
         }
         // fetch complete message index
         else {
             if ($this->get_capability('SORT')) {
-                $index = $this->conn->sort($mailbox, $sort_field,
-                    $this->skip_deleted ? 'UNDELETED' : '', true);
+                $index = $this->conn->sort($folder, $sort_field,
+                    $this->options['skip_deleted'] ? 'UNDELETED' : '', true);
             }
 
-            if (empty($index) || $index->isError()) {
-                $index = $this->conn->index($mailbox, "1:*", $sort_field,
-                    $this->skip_deleted, false, true);
+            if (empty($index) || $index->is_error()) {
+                $index = $this->conn->index($folder, "1:*", $sort_field,
+                    $this->options['skip_deleted'], false, true);
             }
         }
 
-        if ($sort_order != $index->getParameters('ORDER')) {
+        if ($sort_order != $index->get_parameters('ORDER')) {
             $index->revert();
         }
 
@@ -1310,25 +1248,25 @@
     /**
      * Return index of threaded message UIDs
      *
-     * @param string $mailbox    Mailbox to get index from
+     * @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_thread Message UIDs
      */
-    function thread_index($mailbox='', $sort_field=NULL, $sort_order=NULL)
+    public function thread_index($folder='', $sort_field=NULL, $sort_order=NULL)
     {
-        if (!strlen($mailbox)) {
-            $mailbox = $this->mailbox;
+        if (!strlen($folder)) {
+            $folder = $this->folder;
         }
 
         // we have a saved search result, get index from there
-        if ($this->search_string && $this->search_threads && $mailbox == $this->mailbox) {
+        if ($this->search_string && $this->search_threads && $folder == $this->folder) {
             $threads = $this->search_set;
         }
         else {
             // get all threads (default sort order)
-            $threads = $this->fetch_threads($mailbox);
+            $threads = $this->fetch_threads($folder);
         }
 
         $this->set_sort_order($sort_field, $sort_order);
@@ -1343,9 +1281,9 @@
      *
      * @param rcube_result_thread $threads  Threads result set
      */
-    private function sort_threads($threads)
+    protected function sort_threads($threads)
     {
-        if ($threads->isEmpty()) {
+        if ($threads->is_empty()) {
             return;
         }
 
@@ -1354,14 +1292,14 @@
         // THREAD=REFS:           sorting by the most recent date in each thread
 
         if ($this->sort_field && ($this->sort_field != 'date' || $this->get_capability('THREAD') != 'REFS')) {
-            $index = $this->message_index_direct($this->mailbox, $this->sort_field, $this->sort_order, false);
+            $index = $this->index_direct($this->folder, $this->sort_field, $this->sort_order, false);
 
-            if (!$index->isEmpty()) {
+            if (!$index->is_empty()) {
                 $threads->sort($index);
             }
         }
         else {
-            if ($this->sort_order != $threads->getParameters('ORDER')) {
+            if ($this->sort_order != $threads->get_parameters('ORDER')) {
                 $threads->revert();
             }
         }
@@ -1371,23 +1309,24 @@
     /**
      * Invoke search request to IMAP server
      *
-     * @param  string  $mailbox    Mailbox name to search in
+     * @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
-     * @access public
+     *
      * @todo: Search criteria should be provided in non-IMAP format, eg. array
      */
-    function search($mailbox='', $str='ALL', $charset=NULL, $sort_field=NULL)
+    public function search($folder='', $str='ALL', $charset=NULL, $sort_field=NULL)
     {
-        if (!$str)
+        if (!$str) {
             $str = 'ALL';
-
-        if (!strlen($mailbox)) {
-            $mailbox = $this->mailbox;
         }
 
-        $results = $this->_search_index($mailbox, $str, $charset, $sort_field);
+        if (!strlen($folder)) {
+            $folder = $this->folder;
+        }
+
+        $results = $this->search_index($folder, $str, $charset, $sort_field);
 
         $this->set_search_set(array($str, $results, $charset, $sort_field,
             $this->threading || $this->search_sorted ? true : false));
@@ -1395,82 +1334,25 @@
 
 
     /**
-     * Private search method
-     *
-     * @param string $mailbox    Mailbox name
-     * @param string $criteria   Search criteria
-     * @param string $charset    Charset
-     * @param string $sort_field Sorting field
-     *
-     * @return rcube_result_index|rcube_result_thread  Search results (UIDs)
-     * @see rcube_imap::search()
-     */
-    private function _search_index($mailbox, $criteria='ALL', $charset=NULL, $sort_field=NULL)
-    {
-        $orig_criteria = $criteria;
-
-        if ($this->skip_deleted && !preg_match('/UNDELETED/', $criteria))
-            $criteria = 'UNDELETED '.$criteria;
-
-        if ($this->threading) {
-            $threads = $this->conn->thread($mailbox, $this->threading, $criteria, true, $charset);
-
-            // Error, try with US-ASCII (RFC5256: SORT/THREAD must support US-ASCII and UTF-8,
-            // but I've seen that Courier doesn't support UTF-8)
-            if ($threads->isError() && $charset && $charset != 'US-ASCII')
-                $threads = $this->conn->thread($mailbox, $this->threading,
-                    $this->convert_criteria($criteria, $charset), true, 'US-ASCII');
-
-            return $threads;
-        }
-
-        if ($sort_field && $this->get_capability('SORT')) {
-            $charset  = $charset ? $charset : $this->default_charset;
-            $messages = $this->conn->sort($mailbox, $sort_field, $criteria, true, $charset);
-
-            // Error, try with US-ASCII (RFC5256: SORT/THREAD must support US-ASCII and UTF-8,
-            // but I've seen Courier with disabled UTF-8 support)
-            if ($messages->isError() && $charset && $charset != 'US-ASCII')
-                $messages = $this->conn->sort($mailbox, $sort_field,
-                    $this->convert_criteria($criteria, $charset), true, 'US-ASCII');
-
-            if (!$messages->isError()) {
-                $this->search_sorted = true;
-                return $messages;
-            }
-        }
-
-        $messages = $this->conn->search($mailbox,
-            ($charset ? "CHARSET $charset " : '') . $criteria, true);
-
-        // Error, try with US-ASCII (some servers may support only US-ASCII)
-        if ($messages->isError() && $charset && $charset != 'US-ASCII')
-            $messages = $this->conn->search($mailbox,
-                $this->convert_criteria($criteria, $charset), true);
-
-        $this->search_sorted = false;
-
-        return $messages;
-    }
-
-
-    /**
-     * Direct (real and simple) SEARCH request to IMAP server,
-     * without result sorting and caching
+     * Direct (real and simple) SEARCH request (without result sorting and caching).
      *
      * @param  string  $mailbox Mailbox name to search in
      * @param  string  $str     Search string
-     * @param  boolean $ret_uid True if UIDs should be returned
      *
      * @return rcube_result_index  Search result (UIDs)
      */
-    function search_once($mailbox='', $str='ALL')
+    public function search_once($mailbox = null, $str = 'ALL')
     {
-        if (!$str)
+        if (!$str) {
             return 'ALL';
+        }
 
         if (!strlen($mailbox)) {
             $mailbox = $this->mailbox;
+        }
+
+        if (!$this->check_connection()) {
+            return new rcube_result_index();
         }
 
         $index = $this->conn->search($mailbox, $str, true);
@@ -1480,15 +1362,88 @@
 
 
     /**
+     * protected search method
+     *
+     * @param string $folder     Folder name
+     * @param string $criteria   Search criteria
+     * @param string $charset    Charset
+     * @param string $sort_field Sorting field
+     *
+     * @return rcube_result_index|rcube_result_thread  Search results (UIDs)
+     * @see rcube_imap::search()
+     */
+    protected function search_index($folder, $criteria='ALL', $charset=NULL, $sort_field=NULL)
+    {
+        $orig_criteria = $criteria;
+
+        if (!$this->check_connection()) {
+            if ($this->threading) {
+                return new rcube_result_thread();
+            }
+            else {
+                return new rcube_result_index();
+            }
+        }
+
+        if ($this->options['skip_deleted'] && !preg_match('/UNDELETED/', $criteria)) {
+            $criteria = 'UNDELETED '.$criteria;
+        }
+
+        if ($this->threading) {
+            $threads = $this->conn->thread($folder, $this->threading, $criteria, true, $charset);
+
+            // Error, try with US-ASCII (RFC5256: SORT/THREAD must support US-ASCII and UTF-8,
+            // but I've seen that Courier doesn't support UTF-8)
+            if ($threads->is_error() && $charset && $charset != 'US-ASCII') {
+                $threads = $this->conn->thread($folder, $this->threading,
+                    $this->convert_criteria($criteria, $charset), true, 'US-ASCII');
+            }
+
+            return $threads;
+        }
+
+        if ($sort_field && $this->get_capability('SORT')) {
+            $charset  = $charset ? $charset : $this->default_charset;
+            $messages = $this->conn->sort($folder, $sort_field, $criteria, true, $charset);
+
+            // Error, try with US-ASCII (RFC5256: SORT/THREAD must support US-ASCII and UTF-8,
+            // but I've seen Courier with disabled UTF-8 support)
+            if ($messages->is_error() && $charset && $charset != 'US-ASCII') {
+                $messages = $this->conn->sort($folder, $sort_field,
+                    $this->convert_criteria($criteria, $charset), true, 'US-ASCII');
+            }
+
+            if (!$messages->is_error()) {
+                $this->search_sorted = true;
+                return $messages;
+            }
+        }
+
+        $messages = $this->conn->search($folder,
+            ($charset ? "CHARSET $charset " : '') . $criteria, true);
+
+        // Error, try with US-ASCII (some servers may support only US-ASCII)
+        if ($messages->is_error() && $charset && $charset != 'US-ASCII') {
+            $messages = $this->conn->search($folder,
+                $this->convert_criteria($criteria, $charset), true);
+        }
+
+        $this->search_sorted = false;
+
+        return $messages;
+    }
+
+
+    /**
      * Converts charset of search criteria string
      *
      * @param  string  $str          Search string
      * @param  string  $charset      Original charset
      * @param  string  $dest_charset Destination charset (default US-ASCII)
+     *
      * @return string  Search string
-     * @access private
      */
-    private function convert_criteria($str, $charset, $dest_charset='US-ASCII')
+    protected function convert_criteria($str, $charset, $dest_charset='US-ASCII')
     {
         // convert strings to US_ASCII
         if (preg_match_all('/\{([0-9]+)\}\r\n/', $str, $matches, PREG_OFFSET_CAPTURE)) {
@@ -1497,16 +1452,20 @@
                 $string_offset = $m[1] + strlen($m[0]) + 4; // {}\r\n
                 $string = substr($str, $string_offset - 1, $m[0]);
                 $string = rcube_charset_convert($string, $charset, $dest_charset);
-                if ($string === false)
+                if ($string === false) {
                     continue;
+                }
                 $res .= substr($str, $last, $m[1] - $last - 1) . rcube_imap_generic::escape($string);
                 $last = $m[0] + $string_offset - 1;
             }
-            if ($last < strlen($str))
+            if ($last < strlen($str)) {
                 $res .= substr($str, $last, strlen($str)-$last);
+            }
         }
-        else // strings for conversion not found
+        // strings for conversion not found
+        else {
             $res = $str;
+        }
 
         return $res;
     }
@@ -1517,7 +1476,7 @@
      *
      * @return array Current search set
      */
-    function refresh_search()
+    public function refresh_search()
     {
         if (!empty($this->search_string)) {
             $this->search('', $this->search_string, $this->search_charset, $this->search_sort_field);
@@ -1530,25 +1489,28 @@
     /**
      * Return message headers object of a specific message
      *
-     * @param int     $id       Message sequence ID or UID
-     * @param string  $mailbox  Mailbox to read from
+     * @param int     $id       Message UID
+     * @param string  $folder   Folder to read from
      * @param bool    $force    True to skip cache
      *
      * @return rcube_mail_header Message headers
      */
-    function get_headers($uid, $mailbox = null, $force = false)
+    public function get_message_headers($uid, $folder = null, $force = false)
     {
-        if (!strlen($mailbox)) {
-            $mailbox = $this->mailbox;
+        if (!strlen($folder)) {
+            $folder = $this->folder;
         }
 
         // get cached headers
         if (!$force && $uid && ($mcache = $this->get_mcache_engine())) {
-            $headers = $mcache->get_message($mailbox, $uid);
+            $headers = $mcache->get_message($folder, $uid);
+        }
+        else if (!$this->check_connection()) {
+            $headers = false;
         }
         else {
             $headers = $this->conn->fetchHeader(
-                $mailbox, $uid, true, true, $this->get_fetch_headers());
+                $folder, $uid, true, true, $this->get_fetch_headers());
         }
 
         return $headers;
@@ -1560,14 +1522,14 @@
      * an object structure similar to the one generated by PEAR::Mail_mimeDecode
      *
      * @param int     $uid      Message UID to fetch
-     * @param string  $mailbox  Mailbox to read from
+     * @param string  $folder   Folder to read from
      *
      * @return object rcube_mail_header Message data
      */
-    function get_message($uid, $mailbox = null)
+    public function get_message($uid, $folder = null)
     {
-        if (!strlen($mailbox)) {
-            $mailbox = $this->mailbox;
+        if (!strlen($folder)) {
+            $folder = $this->folder;
         }
 
         // Check internal cache
@@ -1577,32 +1539,41 @@
             }
         }
 
-        $headers = $this->get_headers($uid, $mailbox);
+        $headers = $this->get_message_headers($uid, $folder);
 
         // message doesn't exist?
-        if (empty($headers))
+        if (empty($headers)) {
             return null;
+        }
 
         // structure might be cached
-        if (!empty($headers->structure))
+        if (!empty($headers->structure)) {
             return $headers;
+        }
 
-        $this->_msg_uid = $uid;
+        $this->msg_uid = $uid;
+
+        if (!$this->check_connection()) {
+            return $headers;
+        }
 
         if (empty($headers->bodystructure)) {
-            $headers->bodystructure = $this->conn->getStructure($mailbox, $uid, true);
+            $headers->bodystructure = $this->conn->getStructure($folder, $uid, true);
         }
 
         $structure = $headers->bodystructure;
 
-        if (empty($structure))
+        if (empty($structure)) {
             return $headers;
+        }
 
         // set message charset from message headers
-        if ($headers->charset)
+        if ($headers->charset) {
             $this->struct_charset = $headers->charset;
-        else
-            $this->struct_charset = $this->_structure_charset($structure);
+        }
+        else {
+            $this->struct_charset = $this->structure_charset($structure);
+        }
 
         $headers->ctype = strtolower($headers->ctype);
 
@@ -1618,11 +1589,12 @@
                 $structure[0] = $m[1];
                 $structure[1] = $m[2];
             }
-            else
+            else {
                 return $headers;
+            }
         }
 
-        $struct = $this->_structure_part($structure, 0, '', $headers);
+        $struct = $this->structure_part($structure, 0, '', $headers);
 
         // don't trust given content-type
         if (empty($struct->parts) && !empty($headers->ctype)) {
@@ -1643,9 +1615,8 @@
      * @param array  $part
      * @param int    $count
      * @param string $parent
-     * @access private
      */
-    private function _structure_part($part, $count=0, $parent='', $mime_headers=null)
+    protected function structure_part($part, $count=0, $parent='', $mime_headers=null)
     {
         $struct = new rcube_message_part;
         $struct->mime_id = empty($parent) ? (string)$count : "$parent.$count";
@@ -1678,8 +1649,9 @@
 
             // build parts list for headers pre-fetching
             for ($i=0; $i<count($part); $i++) {
-                if (!is_array($part[$i]))
+                if (!is_array($part[$i])) {
                     break;
+                }
                 // fetch message headers if message/rfc822
                 // or named part (could contain Content-Location header)
                 if (!is_array($part[$i][0])) {
@@ -1697,16 +1669,17 @@
             // @TODO: we could do this before _structure_part() call, to fetch
             // headers for parts on all levels
             if ($mime_part_headers) {
-                $mime_part_headers = $this->conn->fetchMIMEHeaders($this->mailbox,
-                    $this->_msg_uid, $mime_part_headers);
+                $mime_part_headers = $this->conn->fetchMIMEHeaders($this->folder,
+                    $this->msg_uid, $mime_part_headers);
             }
 
             $struct->parts = array();
             for ($i=0, $count=0; $i<count($part); $i++) {
-                if (!is_array($part[$i]))
+                if (!is_array($part[$i])) {
                     break;
+                }
                 $tmp_part_id = $struct->mime_id ? $struct->mime_id.'.'.($i+1) : $i+1;
-                $struct->parts[] = $this->_structure_part($part[$i], ++$count, $struct->mime_id,
+                $struct->parts[] = $this->structure_part($part[$i], ++$count, $struct->mime_id,
                     $mime_part_headers[$tmp_part_id]);
             }
 
@@ -1742,11 +1715,13 @@
         // read content type parameters
         if (is_array($part[2])) {
             $struct->ctype_parameters = array();
-            for ($i=0; $i<count($part[2]); $i+=2)
+            for ($i=0; $i<count($part[2]); $i+=2) {
                 $struct->ctype_parameters[strtolower($part[2][$i])] = $part[2][$i+1];
+            }
 
-            if (isset($struct->ctype_parameters['charset']))
+            if (isset($struct->ctype_parameters['charset'])) {
                 $struct->charset = $struct->ctype_parameters['charset'];
+            }
         }
 
         // #1487700: workaround for lack of charset in malformed structure
@@ -1761,29 +1736,37 @@
         }
 
         // get part size
-        if (!empty($part[6]))
+        if (!empty($part[6])) {
             $struct->size = intval($part[6]);
+        }
 
         // read part disposition
         $di = 8;
-        if ($struct->ctype_primary == 'text') $di += 1;
-        else if ($struct->mimetype == 'message/rfc822') $di += 3;
+        if ($struct->ctype_primary == 'text') {
+            $di += 1;
+        }
+        else if ($struct->mimetype == 'message/rfc822') {
+            $di += 3;
+        }
 
         if (is_array($part[$di]) && count($part[$di]) == 2) {
             $struct->disposition = strtolower($part[$di][0]);
 
-            if (is_array($part[$di][1]))
-                for ($n=0; $n<count($part[$di][1]); $n+=2)
+            if (is_array($part[$di][1])) {
+                for ($n=0; $n<count($part[$di][1]); $n+=2) {
                     $struct->d_parameters[strtolower($part[$di][1][$n])] = $part[$di][1][$n+1];
+                }
+            }
         }
 
         // get message/rfc822's child-parts
         if (is_array($part[8]) && $di != 8) {
             $struct->parts = array();
             for ($i=0, $count=0; $i<count($part[8]); $i++) {
-                if (!is_array($part[8][$i]))
+                if (!is_array($part[8][$i])) {
                     break;
-                $struct->parts[] = $this->_structure_part($part[8][$i], ++$count, $struct->mime_id);
+                }
+                $struct->parts[] = $this->structure_part($part[8][$i], ++$count, $struct->mime_id);
             }
         }
 
@@ -1792,44 +1775,51 @@
             $struct->content_id = $part[3];
             $struct->headers['content-id'] = $part[3];
 
-            if (empty($struct->disposition))
+            if (empty($struct->disposition)) {
                 $struct->disposition = 'inline';
+            }
         }
 
         // fetch message headers if message/rfc822 or named part (could contain Content-Location header)
         if ($struct->ctype_primary == 'message' || ($struct->ctype_parameters['name'] && !$struct->content_id)) {
             if (empty($mime_headers)) {
                 $mime_headers = $this->conn->fetchPartHeader(
-                    $this->mailbox, $this->_msg_uid, true, $struct->mime_id);
+                    $this->folder, $this->msg_uid, true, $struct->mime_id);
             }
 
-            if (is_string($mime_headers))
+            if (is_string($mime_headers)) {
                 $struct->headers = rcube_mime::parse_headers($mime_headers) + $struct->headers;
-            else if (is_object($mime_headers))
+            }
+            else if (is_object($mime_headers)) {
                 $struct->headers = get_object_vars($mime_headers) + $struct->headers;
+            }
 
             // get real content-type of message/rfc822
             if ($struct->mimetype == 'message/rfc822') {
                 // single-part
-                if (!is_array($part[8][0]))
+                if (!is_array($part[8][0])) {
                     $struct->real_mimetype = strtolower($part[8][0] . '/' . $part[8][1]);
+                }
                 // multi-part
                 else {
-                    for ($n=0; $n<count($part[8]); $n++)
-                        if (!is_array($part[8][$n]))
+                    for ($n=0; $n<count($part[8]); $n++) {
+                        if (!is_array($part[8][$n])) {
                             break;
+                        }
+                    }
                     $struct->real_mimetype = 'multipart/' . strtolower($part[8][$n]);
                 }
             }
 
             if ($struct->ctype_primary == 'message' && empty($struct->parts)) {
-                if (is_array($part[8]) && $di != 8)
-                    $struct->parts[] = $this->_structure_part($part[8], ++$count, $struct->mime_id);
+                if (is_array($part[8]) && $di != 8) {
+                    $struct->parts[] = $this->structure_part($part[8], ++$count, $struct->mime_id);
+                }
             }
         }
 
         // normalize filename property
-        $this->_set_part_filename($struct, $mime_headers);
+        $this->set_part_filename($struct, $mime_headers);
 
         return $struct;
     }
@@ -1840,16 +1830,18 @@
      *
      * @param  rcube_message_part $part    Part object
      * @param  string             $headers Part's raw headers
-     * @access private
      */
-    private function _set_part_filename(&$part, $headers=null)
+    protected function set_part_filename(&$part, $headers=null)
     {
-        if (!empty($part->d_parameters['filename']))
+        if (!empty($part->d_parameters['filename'])) {
             $filename_mime = $part->d_parameters['filename'];
-        else if (!empty($part->d_parameters['filename*']))
+        }
+        else if (!empty($part->d_parameters['filename*'])) {
             $filename_encoded = $part->d_parameters['filename*'];
-        else if (!empty($part->ctype_parameters['name*']))
+        }
+        else if (!empty($part->ctype_parameters['name*'])) {
             $filename_encoded = $part->ctype_parameters['name*'];
+        }
         // RFC2231 value continuations
         // TODO: this should be rewrited to support RFC2231 4.1 combinations
         else if (!empty($part->d_parameters['filename*0'])) {
@@ -1863,7 +1855,7 @@
             if ($i<2) {
                 if (!$headers) {
                     $headers = $this->conn->fetchPartHeader(
-                        $this->mailbox, $this->_msg_uid, true, $part->mime_id);
+                        $this->folder, $this->msg_uid, true, $part->mime_id);
                 }
                 $filename_mime = '';
                 $i = 0;
@@ -1882,7 +1874,7 @@
             if ($i<2) {
                 if (!$headers) {
                     $headers = $this->conn->fetchPartHeader(
-                            $this->mailbox, $this->_msg_uid, true, $part->mime_id);
+                            $this->folder, $this->msg_uid, true, $part->mime_id);
                 }
                 $filename_encoded = '';
                 $i = 0; $matches = array();
@@ -1901,7 +1893,7 @@
             if ($i<2) {
                 if (!$headers) {
                     $headers = $this->conn->fetchPartHeader(
-                        $this->mailbox, $this->_msg_uid, true, $part->mime_id);
+                        $this->folder, $this->msg_uid, true, $part->mime_id);
                 }
                 $filename_mime = '';
                 $i = 0; $matches = array();
@@ -1920,7 +1912,7 @@
             if ($i<2) {
                 if (!$headers) {
                     $headers = $this->conn->fetchPartHeader(
-                        $this->mailbox, $this->_msg_uid, true, $part->mime_id);
+                        $this->folder, $this->msg_uid, true, $part->mime_id);
                 }
                 $filename_encoded = '';
                 $i = 0; $matches = array();
@@ -1931,22 +1923,28 @@
             }
         }
         // read 'name' after rfc2231 parameters as it may contains truncated filename (from Thunderbird)
-        else if (!empty($part->ctype_parameters['name']))
+        else if (!empty($part->ctype_parameters['name'])) {
             $filename_mime = $part->ctype_parameters['name'];
+        }
         // Content-Disposition
-        else if (!empty($part->headers['content-description']))
+        else if (!empty($part->headers['content-description'])) {
             $filename_mime = $part->headers['content-description'];
-        else
+        }
+        else {
             return;
+        }
 
         // decode filename
         if (!empty($filename_mime)) {
-            if (!empty($part->charset))
+            if (!empty($part->charset)) {
                 $charset = $part->charset;
-            else if (!empty($this->struct_charset))
+            }
+            else if (!empty($this->struct_charset)) {
                 $charset = $this->struct_charset;
-            else
+            }
+            else {
                 $charset = rc_detect_encoding($filename_mime, $this->default_charset);
+            }
 
             $part->filename = rcube_mime::decode_mime_string($filename_mime, $charset);
         }
@@ -1966,14 +1964,15 @@
      * Get charset name from message structure (first part)
      *
      * @param  array $structure Message structure
+     *
      * @return string Charset name
-     * @access private
      */
-    private function _structure_charset($structure)
+    protected function structure_charset($structure)
     {
         while (is_array($structure)) {
-            if (is_array($structure[2]) && $structure[2][0] == 'charset')
+            if (is_array($structure[2]) && $structure[2][0] == 'charset') {
                 return $structure[2][1];
+            }
             $structure = $structure[0];
         }
     }
@@ -1991,11 +1990,15 @@
      *
      * @return string Message/part body if not printed
      */
-    function get_message_part($uid, $part=1, $o_part=NULL, $print=NULL, $fp=NULL, $skip_charset_conv=false)
+    public function get_message_part($uid, $part=1, $o_part=NULL, $print=NULL, $fp=NULL, $skip_charset_conv=false)
     {
+        if (!$this->check_connection()) {
+            return null;
+        }
+
         // get part data if not provided
         if (!is_object($o_part)) {
-            $structure = $this->conn->getStructure($this->mailbox, $uid, true);
+            $structure = $this->conn->getStructure($this->folder, $uid, true);
             $part_data = rcube_imap_generic::getStructurePartData($structure, $part);
 
             $o_part = new rcube_message_part;
@@ -2006,7 +2009,7 @@
         }
 
         if ($o_part && $o_part->size) {
-            $body = $this->conn->handlePartBody($this->mailbox, $uid, true,
+            $body = $this->conn->handlePartBody($this->folder, $uid, true,
                 $part ? $part : 'TEXT', $o_part->encoding, $print, $fp);
         }
 
@@ -2024,31 +2027,18 @@
             if (!$skip_charset_conv) {
                 if (!$o_part->charset || strtoupper($o_part->charset) == 'US-ASCII') {
                     // try to extract charset information from HTML meta tag (#1488125)
-                    if ($o_part->ctype_secondary == 'html' && preg_match('/<meta[^>]+charset=([a-z0-9-_]+)/i', $body, $m))
+                    if ($o_part->ctype_secondary == 'html' && preg_match('/<meta[^>]+charset=([a-z0-9-_]+)/i', $body, $m)) {
                         $o_part->charset = strtoupper($m[1]);
-                    else
+                    }
+                    else {
                         $o_part->charset = $this->default_charset;
+                    }
                 }
                 $body = rcube_charset_convert($body, $o_part->charset);
             }
         }
 
         return $body;
-    }
-
-
-    /**
-     * 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()
-     */
-    function get_body($uid, $part=1)
-    {
-        $headers = $this->get_headers($uid);
-        return rcube_charset_convert($this->get_message_part($uid, $part, NULL),
-            $headers->charset ? $headers->charset : $this->default_charset);
     }
 
 
@@ -2060,9 +2050,13 @@
      *
      * @return string Message source string
      */
-    function get_raw_body($uid, $fp=null)
+    public function get_raw_body($uid, $fp=null)
     {
-        return $this->conn->handlePartBody($this->mailbox, $uid,
+        if (!$this->check_connection()) {
+            return null;
+        }
+
+        return $this->conn->handlePartBody($this->folder, $uid,
             true, null, null, false, $fp);
     }
 
@@ -2071,22 +2065,29 @@
      * Returns the message headers as string
      *
      * @param int $uid  Message UID
+     *
      * @return string Message headers string
      */
-    function get_raw_headers($uid)
+    public function get_raw_headers($uid)
     {
-        return $this->conn->fetchPartHeader($this->mailbox, $uid, true);
+        if (!$this->check_connection()) {
+            return null;
+        }
+
+        return $this->conn->fetchPartHeader($this->folder, $uid, true);
     }
 
 
     /**
      * Sends the whole message source to stdout
-     *
-     * @param int $uid Message UID
      */
-    function print_raw_body($uid)
+    public function print_raw_body($uid)
     {
-        $this->conn->handlePartBody($this->mailbox, $uid, true, NULL, NULL, true);
+        if (!$this->check_connection()) {
+            return;
+        }
+
+        $this->conn->handlePartBody($this->folder, $uid, true, NULL, NULL, true);
     }
 
 
@@ -2095,24 +2096,30 @@
      *
      * @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  $mailbox    Folder name
+     * @param string  $folder    Folder name
      * @param boolean $skip_cache True to skip message cache clean up
      *
      * @return boolean  Operation status
      */
-    function set_flag($uids, $flag, $mailbox=null, $skip_cache=false)
+    public function set_flag($uids, $flag, $folder=null, $skip_cache=false)
     {
-        if (!strlen($mailbox)) {
-            $mailbox = $this->mailbox;
+        if (!strlen($folder)) {
+            $folder = $this->folder;
+        }
+
+        if (!$this->check_connection()) {
+            return false;
         }
 
         $flag = strtoupper($flag);
-        list($uids, $all_mode) = $this->_parse_uids($uids, $mailbox);
+        list($uids, $all_mode) = $this->parse_uids($uids);
 
-        if (strpos($flag, 'UN') === 0)
-            $result = $this->conn->unflag($mailbox, $uids, substr($flag, 2));
-        else
-            $result = $this->conn->flag($mailbox, $uids, $flag);
+        if (strpos($flag, 'UN') === 0) {
+            $result = $this->conn->unflag($folder, $uids, substr($flag, 2));
+        }
+        else {
+            $result = $this->conn->flag($folder, $uids, $flag);
+        }
 
         if ($result) {
             // reload message headers if cached
@@ -2120,17 +2127,17 @@
             if (!$skip_cache && ($mcache = $this->get_mcache_engine())) {
                 $status = strpos($flag, 'UN') !== 0;
                 $mflag  = preg_replace('/^UN/', '', $flag);
-                $mcache->change_flag($mailbox, $all_mode ? null : explode(',', $uids),
+                $mcache->change_flag($folder, $all_mode ? null : explode(',', $uids),
                     $mflag, $status);
             }
 
             // clear cached counters
             if ($flag == 'SEEN' || $flag == 'UNSEEN') {
-                $this->_clear_messagecount($mailbox, 'SEEN');
-                $this->_clear_messagecount($mailbox, 'UNSEEN');
+                $this->clear_messagecount($folder, 'SEEN');
+                $this->clear_messagecount($folder, 'UNSEEN');
             }
             else if ($flag == 'DELETED') {
-                $this->_clear_messagecount($mailbox, 'DELETED');
+                $this->clear_messagecount($folder, 'DELETED');
             }
         }
 
@@ -2139,48 +2146,34 @@
 
 
     /**
-     * Remove message flag for one or several messages
+     * Append a mail message (source) to a specific folder
      *
-     * @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 $mailbox Folder name
-     *
-     * @return int   Number of flagged messages, -1 on failure
-     * @see set_flag
-     */
-    function unset_flag($uids, $flag, $mailbox=null)
-    {
-        return $this->set_flag($uids, 'UN'.$flag, $mailbox);
-    }
-
-
-    /**
-     * Append a mail message (source) to a specific mailbox
-     *
-     * @param string  $mailbox Target mailbox
+     * @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
      */
-    function save_message($mailbox, &$message, $headers='', $is_file=false)
+    public function save_message($folder, &$message, $headers='', $is_file=false)
     {
-        if (!strlen($mailbox)) {
-            $mailbox = $this->mailbox;
+        if (!strlen($folder)) {
+            $folder = $this->folder;
         }
 
-        // make sure mailbox exists
-        if ($this->mailbox_exists($mailbox)) {
-            if ($is_file)
-                $saved = $this->conn->appendFromFile($mailbox, $message, $headers);
-            else
-                $saved = $this->conn->append($mailbox, $message);
+        // make sure folder exists
+        if ($this->folder_exists($folder)) {
+            if ($is_file) {
+                $saved = $this->conn->appendFromFile($folder, $message, $headers);
+            }
+            else {
+                $saved = $this->conn->append($folder, $message);
+            }
         }
 
         if ($saved) {
-            // increase messagecount of the target mailbox
-            $this->_set_messagecount($mailbox, 'ALL', 1);
+            // increase messagecount of the target folder
+            $this->set_messagecount($folder, 'ALL', 1);
         }
 
         return $saved;
@@ -2188,33 +2181,39 @@
 
 
     /**
-     * Move a message from one mailbox to another
+     * Move a message from one folder to another
      *
      * @param mixed  $uids      Message UIDs as array or comma-separated string, or '*'
-     * @param string $to_mbox   Target mailbox
-     * @param string $from_mbox Source mailbox
+     * @param string $to_mbox   Target folder
+     * @param string $from_mbox Source folder
+     *
      * @return boolean True on success, False on error
      */
-    function move_message($uids, $to_mbox, $from_mbox='')
+    public function move_message($uids, $to_mbox, $from_mbox='')
     {
         if (!strlen($from_mbox)) {
-            $from_mbox = $this->mailbox;
+            $from_mbox = $this->folder;
         }
 
         if ($to_mbox === $from_mbox) {
             return false;
         }
 
-        list($uids, $all_mode) = $this->_parse_uids($uids, $from_mbox);
+        list($uids, $all_mode) = $this->parse_uids($uids);
 
         // exit if no message uids are specified
-        if (empty($uids))
+        if (empty($uids)) {
             return false;
+        }
 
-        // make sure mailbox exists
-        if ($to_mbox != 'INBOX' && !$this->mailbox_exists($to_mbox)) {
+        if (!$this->check_connection()) {
+            return false;
+        }
+
+        // make sure folder exists
+        if ($to_mbox != 'INBOX' && !$this->folder_exists($to_mbox)) {
             if (in_array($to_mbox, $this->default_folders)) {
-                if (!$this->create_mailbox($to_mbox, true)) {
+                if (!$this->create_folder($to_mbox, true)) {
                     return false;
                 }
             }
@@ -2236,11 +2235,11 @@
         $moved = $this->conn->move($uids, $from_mbox, $to_mbox);
 
         // send expunge command in order to have the moved message
-        // really deleted from the source mailbox
+        // really deleted from the source folder
         if ($moved) {
-            $this->_expunge($from_mbox, false, $uids);
-            $this->_clear_messagecount($from_mbox);
-            $this->_clear_messagecount($to_mbox);
+            $this->expunge_message($uids, $from_mbox, false);
+            $this->clear_messagecount($from_mbox);
+            $this->clear_messagecount($to_mbox);
         }
         // moving failed
         else if ($to_trash && $config->get('delete_always', false)) {
@@ -2252,12 +2251,14 @@
             unset($this->icache['threads']);
 
             // remove message ids from search set
-            if ($this->search_set && $from_mbox == $this->mailbox) {
+            if ($this->search_set && $from_mbox == $this->folder) {
                 // threads are too complicated to just remove messages from set
-                if ($this->search_threads || $all_mode)
+                if ($this->search_threads || $all_mode) {
                     $this->refresh_search();
-                else
+                }
+                else {
                     $this->search_set->filter(explode(',', $uids));
+                }
             }
 
             // remove cached messages
@@ -2270,30 +2271,35 @@
 
 
     /**
-     * Copy a message from one mailbox to another
+     * Copy a message from one folder to another
      *
      * @param mixed  $uids      Message UIDs as array or comma-separated string, or '*'
-     * @param string $to_mbox   Target mailbox
-     * @param string $from_mbox Source mailbox
+     * @param string $to_mbox   Target folder
+     * @param string $from_mbox Source folder
+     *
      * @return boolean True on success, False on error
      */
-    function copy_message($uids, $to_mbox, $from_mbox='')
+    public function copy_message($uids, $to_mbox, $from_mbox='')
     {
         if (!strlen($from_mbox)) {
-            $from_mbox = $this->mailbox;
+            $from_mbox = $this->folder;
         }
 
-        list($uids, $all_mode) = $this->_parse_uids($uids, $from_mbox);
+        list($uids, $all_mode) = $this->parse_uids($uids);
 
         // exit if no message uids are specified
         if (empty($uids)) {
             return false;
         }
 
-        // make sure mailbox exists
-        if ($to_mbox != 'INBOX' && !$this->mailbox_exists($to_mbox)) {
+        if (!$this->check_connection()) {
+            return false;
+        }
+
+        // make sure folder exists
+        if ($to_mbox != 'INBOX' && !$this->folder_exists($to_mbox)) {
             if (in_array($to_mbox, $this->default_folders)) {
-                if (!$this->create_mailbox($to_mbox, true)) {
+                if (!$this->create_folder($to_mbox, true)) {
                     return false;
                 }
             }
@@ -2306,7 +2312,7 @@
         $copied = $this->conn->copy($uids, $from_mbox, $to_mbox);
 
         if ($copied) {
-            $this->_clear_messagecount($to_mbox);
+            $this->clear_messagecount($to_mbox);
         }
 
         return $copied;
@@ -2314,48 +2320,55 @@
 
 
     /**
-     * Mark messages as deleted and expunge mailbox
+     * Mark messages as deleted and expunge them
      *
      * @param mixed  $uids    Message UIDs as array or comma-separated string, or '*'
-     * @param string $mailbox Source mailbox
+     * @param string $folder  Source folder
      *
      * @return boolean True on success, False on error
      */
-    function delete_message($uids, $mailbox='')
+    public function delete_message($uids, $folder='')
     {
-        if (!strlen($mailbox)) {
-            $mailbox = $this->mailbox;
+        if (!strlen($folder)) {
+            $folder = $this->folder;
         }
 
-        list($uids, $all_mode) = $this->_parse_uids($uids, $mailbox);
+        list($uids, $all_mode) = $this->parse_uids($uids);
 
         // exit if no message uids are specified
-        if (empty($uids))
+        if (empty($uids)) {
             return false;
+        }
 
-        $deleted = $this->conn->flag($mailbox, $uids, 'DELETED');
+        if (!$this->check_connection()) {
+            return false;
+        }
+
+        $deleted = $this->conn->flag($folder, $uids, 'DELETED');
 
         if ($deleted) {
             // send expunge command in order to have the deleted message
-            // really deleted from the mailbox
-            $this->_expunge($mailbox, false, $uids);
-            $this->_clear_messagecount($mailbox);
-            unset($this->uid_id_map[$mailbox]);
+            // really deleted from the folder
+            $this->expunge_message($uids, $folder, false);
+            $this->clear_messagecount($folder);
+            unset($this->uid_id_map[$folder]);
 
             // unset threads internal cache
             unset($this->icache['threads']);
 
             // remove message ids from search set
-            if ($this->search_set && $mailbox == $this->mailbox) {
+            if ($this->search_set && $folder == $this->folder) {
                 // threads are too complicated to just remove messages from set
-                if ($this->search_threads || $all_mode)
+                if ($this->search_threads || $all_mode) {
                     $this->refresh_search();
-                else
+                }
+                else {
                     $this->search_set->filter(explode(',', $uids));
+                }
             }
 
             // remove cached messages
-            $this->clear_message_cache($mailbox, $all_mode ? null : explode(',', $uids));
+            $this->clear_message_cache($folder, $all_mode ? null : explode(',', $uids));
         }
 
         return $deleted;
@@ -2363,126 +2376,58 @@
 
 
     /**
-     * Clear all messages in a specific mailbox
-     *
-     * @param string $mailbox Mailbox name
-     *
-     * @return int Above 0 on success
-     */
-    function clear_mailbox($mailbox=null)
-    {
-        if (!strlen($mailbox)) {
-            $mailbox = $this->mailbox;
-        }
-
-        // SELECT will set messages count for clearFolder()
-        if ($this->conn->select($mailbox)) {
-            $cleared = $this->conn->clearFolder($mailbox);
-        }
-
-        // make sure the cache is cleared as well
-        if ($cleared) {
-            $this->clear_message_cache($mailbox);
-            $a_mailbox_cache = $this->get_cache('messagecount');
-            unset($a_mailbox_cache[$mailbox]);
-            $this->update_cache('messagecount', $a_mailbox_cache);
-        }
-
-        return $cleared;
-    }
-
-
-    /**
      * Send IMAP expunge command and clear cache
      *
-     * @param string  $mailbox     Mailbox name
-     * @param boolean $clear_cache False if cache should not be cleared
-     *
-     * @return boolean True on success
-     */
-    function expunge($mailbox='', $clear_cache=true)
-    {
-        if (!strlen($mailbox)) {
-            $mailbox = $this->mailbox;
-        }
-
-        return $this->_expunge($mailbox, $clear_cache);
-    }
-
-
-    /**
-     * Send IMAP expunge command and clear cache
-     *
-     * @param string  $mailbox     Mailbox name
-     * @param boolean $clear_cache False if cache should not be cleared
      * @param mixed   $uids        Message UIDs as array or comma-separated string, or '*'
-     * @return boolean True on success
-     * @access private
-     * @see rcube_imap::expunge()
+     * @param string  $folder      Folder name
+     * @param boolean $clear_cache False if cache should not be cleared
+     *
+     * @return boolean True on success, False on failure
      */
-    private function _expunge($mailbox, $clear_cache=true, $uids=NULL)
+    public function expunge_message($uids, $folder = null, $clear_cache = true)
     {
-        if ($uids && $this->get_capability('UIDPLUS'))
-            list($uids, $all_mode) = $this->_parse_uids($uids, $mailbox);
-        else
+        if ($uids && $this->get_capability('UIDPLUS')) {
+            list($uids, $all_mode) = $this->parse_uids($uids);
+        }
+        else {
             $uids = null;
+        }
 
-        // force mailbox selection and check if mailbox is writeable
+        if (!strlen($folder)) {
+            $folder = $this->folder;
+        }
+
+        if (!$this->check_connection()) {
+            return false;
+        }
+
+        // force folder selection and check if folder is writeable
         // to prevent a situation when CLOSE is executed on closed
-        // or EXPUNGE on read-only mailbox
-        $result = $this->conn->select($mailbox);
+        // or EXPUNGE on read-only folder
+        $result = $this->conn->select($folder);
         if (!$result) {
             return false;
         }
+
         if (!$this->conn->data['READ-WRITE']) {
-            $this->conn->setError(rcube_imap_generic::ERROR_READONLY, "Mailbox is read-only");
+            $this->conn->setError(rcube_imap_generic::ERROR_READONLY, "Folder is read-only");
             return false;
         }
 
         // CLOSE(+SELECT) should be faster than EXPUNGE
-        if (empty($uids) || $all_mode)
+        if (empty($uids) || $all_mode) {
             $result = $this->conn->close();
-        else
-            $result = $this->conn->expunge($mailbox, $uids);
+        }
+        else {
+            $result = $this->conn->expunge($folder, $uids);
+        }
 
         if ($result && $clear_cache) {
-            $this->clear_message_cache($mailbox, $all_mode ? null : explode(',', $uids));
-            $this->_clear_messagecount($mailbox);
+            $this->clear_message_cache($folder, $all_mode ? null : explode(',', $uids));
+            $this->clear_messagecount($folder);
         }
 
         return $result;
-    }
-
-
-    /**
-     * Parse message UIDs input
-     *
-     * @param mixed  $uids    UIDs array or comma-separated list or '*' or '1:*'
-     * @param string $mailbox Mailbox name
-     * @return array Two elements array with UIDs converted to list and ALL flag
-     * @access private
-     */
-    private function _parse_uids($uids, $mailbox)
-    {
-        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);
     }
 
 
@@ -2491,7 +2436,7 @@
      * --------------------------------*/
 
     /**
-     * Public method for listing subscribed folders
+     * Public method for listing subscribed folders.
      *
      * @param   string  $root      Optional root folder
      * @param   string  $name      Optional name pattern
@@ -2500,9 +2445,8 @@
      * @param   bool    $skip_sort Enable to return unsorted list (for better performance)
      *
      * @return  array   List of folders
-     * @access  public
      */
-    function list_mailboxes($root='', $name='*', $filter=null, $rights=null, $skip_sort=false)
+    public function list_folders_subscribed($root='', $name='*', $filter=null, $rights=null, $skip_sort=false)
     {
         $cache_key = $root.':'.$name;
         if (!empty($filter)) {
@@ -2517,7 +2461,7 @@
             return $a_mboxes;
         }
 
-        $a_mboxes = $this->_list_mailboxes($root, $name, $filter, $rights);
+        $a_mboxes = $this->_list_folders_subscribed($root, $name, $filter, $rights);
 
         if (!is_array($a_mboxes)) {
             return array();
@@ -2533,12 +2477,12 @@
             array_unshift($a_mboxes, 'INBOX');
         }
 
-        // sort mailboxes (always sort for cache)
+        // sort folders (always sort for cache)
         if (!$skip_sort || $this->cache) {
-            $a_mboxes = $this->_sort_mailbox_list($a_mboxes);
+            $a_mboxes = $this->sort_folder_list($a_mboxes);
         }
 
-        // write mailboxlist to cache
+        // write folders list to cache
         $this->update_cache($cache_key, $a_mboxes);
 
         return $a_mboxes;
@@ -2546,7 +2490,7 @@
 
 
     /**
-     * Private method for mailbox listing (LSUB)
+     * protected method for folders listing (LSUB)
      *
      * @param   string  $root   Optional root folder
      * @param   string  $name   Optional name pattern
@@ -2554,21 +2498,20 @@
      * @param   string  $rights Optional ACL requirements
      *
      * @return  array   List of subscribed folders
-     * @see     rcube_imap::list_mailboxes()
-     * @access  private
+     * @see     rcube_imap::list_folders_subscribed()
      */
-    private function _list_mailboxes($root='', $name='*', $filter=null, $rights=null)
+    protected function _list_folders_subscribed($root='', $name='*', $filter=null, $rights=null)
     {
         $a_defaults = $a_out = array();
 
-        // Give plugins a chance to provide a list of mailboxes
-        $data = rcmail::get_instance()->plugins->exec_hook('mailboxes_list',
+        // Give plugins a chance to provide a list of folders
+        $data = rcmail::get_instance()->plugins->exec_hook('folders_list',
             array('root' => $root, 'name' => $name, 'filter' => $filter, 'mode' => 'LSUB'));
 
         if (isset($data['folders'])) {
             $a_folders = $data['folders'];
         }
-        else if (!$this->conn->connected()) {
+        else if (!$this->check_connection()) {
            return null;
         }
         else {
@@ -2576,7 +2519,7 @@
             $config = rcmail::get_instance()->config;
             // #1486225: Some dovecot versions returns wrong result using LIST-EXTENDED
             if (!$config->get('imap_force_lsub') && $this->get_capability('LIST-EXTENDED')) {
-                // This will also set mailbox options, LSUB doesn't do that
+                // This will also set folder options, LSUB doesn't do that
                 $a_folders = $this->conn->listMailboxes($root, $name,
                     NULL, array('SUBSCRIBED'));
 
@@ -2603,7 +2546,7 @@
                             && in_array('\\Noselect', $opts)
                         ) {
                             // Some servers returns \Noselect for existing folders
-                            if (!$this->mailbox_exists($folder)) {
+                            if (!$this->folder_exists($folder)) {
                                 $this->conn->unsubscribe($folder);
                                 unset($a_folders[$idx]);
                             }
@@ -2622,7 +2565,7 @@
 
 
     /**
-     * Get a list of all folders available on the IMAP server
+     * Get a list of all folders available on the server
      *
      * @param string  $root      IMAP root dir
      * @param string  $name      Optional name pattern
@@ -2632,7 +2575,7 @@
      *
      * @return array Indexed array with folder names
      */
-    function list_unsubscribed($root='', $name='*', $filter=null, $rights=null, $skip_sort=false)
+    public function list_folders($root='', $name='*', $filter=null, $rights=null, $skip_sort=false)
     {
         $cache_key = $root.':'.$name;
         if (!empty($filter)) {
@@ -2647,8 +2590,8 @@
             return $a_mboxes;
         }
 
-        // Give plugins a chance to provide a list of mailboxes
-        $data = rcmail::get_instance()->plugins->exec_hook('mailboxes_list',
+        // Give plugins a chance to provide a list of folders
+        $data = rcmail::get_instance()->plugins->exec_hook('folders_list',
             array('root' => $root, 'name' => $name, 'filter' => $filter, 'mode' => 'LIST'));
 
         if (isset($data['folders'])) {
@@ -2656,7 +2599,7 @@
         }
         else {
             // retrieve list of folders from IMAP server
-            $a_mboxes = $this->_list_unsubscribed($root, $name);
+            $a_mboxes = $this->_list_folders($root, $name);
         }
 
         if (!is_array($a_mboxes)) {
@@ -2669,7 +2612,7 @@
         }
 
         // cache folder attributes
-        if ($root == '' && $name == '*' && empty($filter)) {
+        if ($root == '' && $name == '*' && empty($filter) && !empty($this->conn->data)) {
             $this->update_cache('mailboxes.attributes', $this->conn->data['LIST']);
         }
 
@@ -2680,10 +2623,10 @@
 
         // filter folders and sort them
         if (!$skip_sort) {
-            $a_mboxes = $this->_sort_mailbox_list($a_mboxes);
+            $a_mboxes = $this->sort_folder_list($a_mboxes);
         }
 
-        // write mailboxlist to cache
+        // write folders list to cache
         $this->update_cache($cache_key, $a_mboxes);
 
         return $a_mboxes;
@@ -2691,16 +2634,20 @@
 
 
     /**
-     * Private method for mailbox listing (LIST)
+     * protected method for folders listing (LIST)
      *
      * @param   string  $root   Optional root folder
      * @param   string  $name   Optional name pattern
      *
      * @return  array   List of folders
-     * @see     rcube_imap::list_unsubscribed()
+     * @see     rcube_imap::list_folders()
      */
-    private function _list_unsubscribed($root='', $name='*')
+    protected function _list_folders($root='', $name='*')
     {
+        if (!$this->check_connection()) {
+            return null;
+        }
+
         $result = $this->conn->listMailboxes($root, $name);
 
         if (!is_array($result)) {
@@ -2757,13 +2704,14 @@
     /**
      * Filter the given list of folders according to access rights
      */
-    private function filter_rights($a_folders, $rights)
+    protected function filter_rights($a_folders, $rights)
     {
         $regex = '/('.$rights.')/';
         foreach ($a_folders as $idx => $folder) {
             $myrights = join('', (array)$this->my_rights($folder));
-            if ($myrights !== null && !preg_match($regex, $myrights))
+            if ($myrights !== null && !preg_match($regex, $myrights)) {
                 unset($a_folders[$idx]);
+            }
         }
 
         return $a_folders;
@@ -2776,85 +2724,92 @@
      *
      * @return mixed Quota info or False if not supported
      */
-    function get_quota()
+    public function get_quota()
     {
-        if ($this->get_capability('QUOTA'))
+        if ($this->get_capability('QUOTA')) {
             return $this->conn->getQuota();
+        }
 
         return false;
     }
 
 
     /**
-     * Get mailbox size (size of all messages in a mailbox)
+     * Get folder size (size of all messages in a folder)
      *
-     * @param string $mailbox Mailbox name
+     * @param string $folder Folder name
      *
-     * @return int Mailbox size in bytes, False on error
+     * @return int Folder size in bytes, False on error
      */
-    function get_mailbox_size($mailbox)
+    public function folder_size($folder)
     {
-        // @TODO: could we try to use QUOTA here?
-        $result = $this->conn->fetchHeaderIndex($mailbox, '1:*', 'SIZE', false);
+        if (!$this->check_connection()) {
+            return 0;
+        }
 
-        if (is_array($result))
+        // @TODO: could we try to use QUOTA here?
+        $result = $this->conn->fetchHeaderIndex($folder, '1:*', 'SIZE', false);
+
+        if (is_array($result)) {
             $result = array_sum($result);
+        }
 
         return $result;
     }
 
 
     /**
-     * Subscribe to a specific mailbox(es)
+     * Subscribe to a specific folder(s)
      *
-     * @param array $a_mboxes Mailbox name(s)
+     * @param array $folders Folder name(s)
+     *
      * @return boolean True on success
      */
-    function subscribe($a_mboxes)
+    public function subscribe($folders)
     {
-        if (!is_array($a_mboxes))
-            $a_mboxes = array($a_mboxes);
-
         // let this common function do the main work
-        return $this->_change_subscription($a_mboxes, 'subscribe');
+        return $this->change_subscription($folders, 'subscribe');
     }
 
 
     /**
-     * Unsubscribe mailboxes
+     * Unsubscribe folder(s)
      *
-     * @param array $a_mboxes Mailbox name(s)
+     * @param array $a_mboxes Folder name(s)
+     *
      * @return boolean True on success
      */
-    function unsubscribe($a_mboxes)
+    public function unsubscribe($folders)
     {
-        if (!is_array($a_mboxes))
-            $a_mboxes = array($a_mboxes);
-
         // let this common function do the main work
-        return $this->_change_subscription($a_mboxes, 'unsubscribe');
+        return $this->change_subscription($folders, 'unsubscribe');
     }
 
 
     /**
-     * Create a new mailbox on the server and register it in local cache
+     * Create a new folder on the server and register it in local cache
      *
-     * @param string  $mailbox   New mailbox name
-     * @param boolean $subscribe True if the new mailbox should be subscribed
+     * @param string  $folder    New folder name
+     * @param boolean $subscribe True if the new folder should be subscribed
      *
      * @return boolean True on success
      */
-    function create_mailbox($mailbox, $subscribe=false)
+    public function create_folder($folder, $subscribe=false)
     {
-        $result = $this->conn->createFolder($mailbox);
+        if (!$this->check_connection()) {
+            return false;
+        }
+
+        $result = $this->conn->createFolder($folder);
 
         // try to subscribe it
         if ($result) {
             // clear cache
             $this->clear_cache('mailboxes', true);
 
-            if ($subscribe)
-                $this->subscribe($mailbox);
+            if ($subscribe) {
+                $this->subscribe($folder);
+            }
         }
 
         return $result;
@@ -2862,45 +2817,49 @@
 
 
     /**
-     * Set a new name to an existing mailbox
+     * Set a new name to an existing folder
      *
-     * @param string $mailbox  Mailbox to rename
-     * @param string $new_name New mailbox name
+     * @param string $folder   Folder to rename
+     * @param string $new_name New folder name
      *
      * @return boolean True on success
      */
-    function rename_mailbox($mailbox, $new_name)
+    public function rename_folder($folder, $new_name)
     {
         if (!strlen($new_name)) {
+            return false;
+        }
+
+        if (!$this->check_connection()) {
             return false;
         }
 
         $delm = $this->get_hierarchy_delimiter();
 
         // get list of subscribed folders
-        if ((strpos($mailbox, '%') === false) && (strpos($mailbox, '*') === false)) {
-            $a_subscribed = $this->_list_mailboxes('', $mailbox . $delm . '*');
-            $subscribed   = $this->mailbox_exists($mailbox, true);
+        if ((strpos($folder, '%') === false) && (strpos($folder, '*') === false)) {
+            $a_subscribed = $this->_list_folders_subscribed('', $folder . $delm . '*');
+            $subscribed   = $this->folder_exists($folder, true);
         }
         else {
-            $a_subscribed = $this->_list_mailboxes();
-            $subscribed   = in_array($mailbox, $a_subscribed);
+            $a_subscribed = $this->_list_folders_subscribed();
+            $subscribed   = in_array($folder, $a_subscribed);
         }
 
-        $result = $this->conn->renameFolder($mailbox, $new_name);
+        $result = $this->conn->renameFolder($folder, $new_name);
 
         if ($result) {
             // unsubscribe the old folder, subscribe the new one
             if ($subscribed) {
-                $this->conn->unsubscribe($mailbox);
+                $this->conn->unsubscribe($folder);
                 $this->conn->subscribe($new_name);
             }
 
-            // check if mailbox children are subscribed
+            // check if folder children are subscribed
             foreach ($a_subscribed as $c_subscribed) {
-                if (strpos($c_subscribed, $mailbox.$delm) === 0) {
+                if (strpos($c_subscribed, $folder.$delm) === 0) {
                     $this->conn->unsubscribe($c_subscribed);
-                    $this->conn->subscribe(preg_replace('/^'.preg_quote($mailbox, '/').'/',
+                    $this->conn->subscribe(preg_replace('/^'.preg_quote($folder, '/').'/',
                         $new_name, $c_subscribed));
 
                     // clear cache
@@ -2909,7 +2868,7 @@
             }
 
             // clear cache
-            $this->clear_message_cache($mailbox);
+            $this->clear_message_cache($folder);
             $this->clear_cache('mailboxes', true);
         }
 
@@ -2918,31 +2877,37 @@
 
 
     /**
-     * Remove mailbox from server
+     * Remove folder from server
      *
-     * @param string $mailbox Mailbox name
+     * @param string $folder Folder name
      *
      * @return boolean True on success
      */
-    function delete_mailbox($mailbox)
+    function delete_folder($folder)
     {
         $delm = $this->get_hierarchy_delimiter();
 
+        if (!$this->check_connection()) {
+            return false;
+        }
+
         // get list of folders
-        if ((strpos($mailbox, '%') === false) && (strpos($mailbox, '*') === false))
-            $sub_mboxes = $this->list_unsubscribed('', $mailbox . $delm . '*');
-        else
+        if ((strpos($folder, '%') === false) && (strpos($folder, '*') === false)) {
+            $sub_mboxes = $this->list_unsubscribed('', $folder . $delm . '*');
+        }
+        else {
             $sub_mboxes = $this->list_unsubscribed();
+        }
 
         // send delete command to server
-        $result = $this->conn->deleteFolder($mailbox);
+        $result = $this->conn->deleteFolder($folder);
 
         if ($result) {
-            // unsubscribe mailbox
-            $this->conn->unsubscribe($mailbox);
+            // unsubscribe folder
+            $this->conn->unsubscribe($folder);
 
             foreach ($sub_mboxes as $c_mbox) {
-                if (strpos($c_mbox, $mailbox.$delm) === 0) {
+                if (strpos($c_mbox, $folder.$delm) === 0) {
                     $this->conn->unsubscribe($c_mbox);
                     if ($this->conn->deleteFolder($c_mbox)) {
 	                    $this->clear_message_cache($c_mbox);
@@ -2950,8 +2915,8 @@
                 }
             }
 
-            // clear mailbox-related cache
-            $this->clear_message_cache($mailbox);
+            // clear folder-related cache
+            $this->clear_message_cache($folder);
             $this->clear_cache('mailboxes', true);
         }
 
@@ -2962,14 +2927,16 @@
     /**
      * Create all folders specified as default
      */
-    function create_default_folders()
+    public function create_default_folders()
     {
         // create default folders if they do not exist
         foreach ($this->default_folders as $folder) {
-            if (!$this->mailbox_exists($folder))
-                $this->create_mailbox($folder, true);
-            else if (!$this->mailbox_exists($folder, true))
+            if (!$this->folder_exists($folder)) {
+                $this->create_folder($folder, true);
+            }
+            else if (!$this->folder_exists($folder, true)) {
                 $this->subscribe($folder);
+            }
         }
     }
 
@@ -2977,31 +2944,36 @@
     /**
      * Checks if folder exists and is subscribed
      *
-     * @param string   $mailbox      Folder name
+     * @param string   $folder       Folder name
      * @param boolean  $subscription Enable subscription checking
      *
      * @return boolean TRUE or FALSE
      */
-    function mailbox_exists($mailbox, $subscription=false)
+    public function folder_exists($folder, $subscription=false)
     {
-        if ($mailbox == 'INBOX') {
+        if ($folder == 'INBOX') {
             return true;
         }
 
         $key  = $subscription ? 'subscribed' : 'existing';
 
-        if (is_array($this->icache[$key]) && in_array($mailbox, $this->icache[$key]))
+        if (is_array($this->icache[$key]) && in_array($folder, $this->icache[$key])) {
             return true;
+        }
+
+        if (!$this->check_connection()) {
+            return false;
+        }
 
         if ($subscription) {
-            $a_folders = $this->conn->listSubscribed('', $mailbox);
+            $a_folders = $this->conn->listSubscribed('', $folder);
         }
         else {
-            $a_folders = $this->conn->listMailboxes('', $mailbox);
+            $a_folders = $this->conn->listMailboxes('', $folder);
         }
 
-        if (is_array($a_folders) && in_array($mailbox, $a_folders)) {
-            $this->icache[$key][] = $mailbox;
+        if (is_array($a_folders) && in_array($folder, $a_folders)) {
+            $this->icache[$key][] = $folder;
             return true;
         }
 
@@ -3012,14 +2984,13 @@
     /**
      * Returns the namespace where the folder is in
      *
-     * @param string $mailbox Folder name
+     * @param string $folder Folder name
      *
      * @return string One of 'personal', 'other' or 'shared'
-     * @access public
      */
-    function mailbox_namespace($mailbox)
+    public function folder_namespace($folder)
     {
-        if ($mailbox == 'INBOX') {
+        if ($folder == 'INBOX') {
             return 'personal';
         }
 
@@ -3027,8 +2998,8 @@
             if (is_array($namespace)) {
                 foreach ($namespace as $ns) {
                     if ($len = strlen($ns[0])) {
-                        if (($len > 1 && $mailbox == substr($ns[0], 0, -1))
-                            || strpos($mailbox, $ns[0]) === 0
+                        if (($len > 1 && $folder == substr($ns[0], 0, -1))
+                            || strpos($folder, $ns[0]) === 0
                         ) {
                             return $type;
                         }
@@ -3047,62 +3018,66 @@
      * For input it adds the prefix. Use it before creating a folder in root
      * of the folders tree.
      *
-     * @param string $mailbox Folder name
+     * @param string $folder Folder name
      * @param string $mode    Mode name (out/in)
      *
      * @return string Folder name
      */
-    function mod_mailbox($mailbox, $mode = 'out')
+    public function mod_folder($folder, $mode = 'out')
     {
-        if (!strlen($mailbox)) {
-            return $mailbox;
+        if (!strlen($folder)) {
+            return $folder;
         }
 
         $prefix     = $this->namespace['prefix']; // see set_env()
         $prefix_len = strlen($prefix);
 
         if (!$prefix_len) {
-            return $mailbox;
+            return $folder;
         }
 
         // remove prefix for output
         if ($mode == 'out') {
-            if (substr($mailbox, 0, $prefix_len) === $prefix) {
-                return substr($mailbox, $prefix_len);
+            if (substr($folder, 0, $prefix_len) === $prefix) {
+                return substr($folder, $prefix_len);
             }
         }
         // add prefix for input (e.g. folder creation)
         else {
-            return $prefix . $mailbox;
+            return $prefix . $folder;
         }
 
-        return $mailbox;
+        return $folder;
     }
 
 
     /**
      * Gets folder attributes from LIST response, e.g. \Noselect, \Noinferiors
      *
-     * @param string $mailbox Folder name
+     * @param string $folder Folder name
      * @param bool   $force   Set to True if attributes should be refreshed
      *
      * @return array Options list
      */
-    function mailbox_attributes($mailbox, $force=false)
+    public function folder_attributes($folder, $force=false)
     {
         // get attributes directly from LIST command
-        if (!empty($this->conn->data['LIST']) && is_array($this->conn->data['LIST'][$mailbox])) {
-            $opts = $this->conn->data['LIST'][$mailbox];
+        if (!empty($this->conn->data['LIST']) && is_array($this->conn->data['LIST'][$folder])) {
+            $opts = $this->conn->data['LIST'][$folder];
         }
         // get cached folder attributes
         else if (!$force) {
             $opts = $this->get_cache('mailboxes.attributes');
-            $opts = $opts[$mailbox];
+            $opts = $opts[$folder];
         }
 
         if (!is_array($opts)) {
-            $this->conn->listMailboxes('', $mailbox);
-            $opts = $this->conn->data['LIST'][$mailbox];
+            if (!$this->check_connection()) {
+                return array();
+            }
+
+            $this->conn->listMailboxes('', $folder);
+            $opts = $this->conn->data['LIST'][$folder];
         }
 
         return is_array($opts) ? $opts : array();
@@ -3110,30 +3085,37 @@
 
 
     /**
-     * Gets connection (and current mailbox) data: UIDVALIDITY, EXISTS, RECENT,
+     * Gets connection (and current folder) data: UIDVALIDITY, EXISTS, RECENT,
      * PERMANENTFLAGS, UIDNEXT, UNSEEN
      *
-     * @param string $mailbox Folder name
+     * @param string $folder Folder name
      *
      * @return array Data
      */
-    function mailbox_data($mailbox)
+    public function folder_data($folder)
     {
-        if (!strlen($mailbox))
-            $mailbox = $this->mailbox !== null ? $this->mailbox : 'INBOX';
+        if (!strlen($folder)) {
+            $folder = $this->folder !== null ? $this->folder : 'INBOX';
+        }
 
-        if ($this->conn->selected != $mailbox) {
-            if ($this->conn->select($mailbox))
-                $this->mailbox = $mailbox;
-            else
+        if ($this->conn->selected != $folder) {
+            if (!$this->check_connection()) {
+                return array();
+            }
+
+            if ($this->conn->select($folder)) {
+                $this->folder = $folder;
+            }
+            else {
                 return null;
+            }
         }
 
         $data = $this->conn->data;
 
         // add (E)SEARCH result for ALL UNDELETED query
         if (!empty($this->icache['undeleted_idx'])
-            && $this->icache['undeleted_idx']->getParameters('MAILBOX') == $mailbox
+            && $this->icache['undeleted_idx']->get_parameters('MAILBOX') == $folder
         ) {
             $data['UNDELETED'] = $this->icache['undeleted_idx'];
         }
@@ -3145,13 +3127,13 @@
     /**
      * Returns extended information about the folder
      *
-     * @param string $mailbox Folder name
+     * @param string $folder Folder name
      *
      * @return array Data
      */
-    function mailbox_info($mailbox)
+    public function folder_info($folder)
     {
-        if ($this->icache['options'] && $this->icache['options']['name'] == $mailbox) {
+        if ($this->icache['options'] && $this->icache['options']['name'] == $folder) {
             return $this->icache['options'];
         }
 
@@ -3161,7 +3143,7 @@
 
         // check if the folder is a namespace prefix
         if (!empty($namespace)) {
-            $mbox = $mailbox . $this->delimiter;
+            $mbox = $folder . $this->delimiter;
             foreach ($namespace as $ns) {
                 if (!empty($ns)) {
                     foreach ($ns as $item) {
@@ -3175,7 +3157,7 @@
         }
         // check if the folder is other user virtual-root
         if (!$options['is_root'] && !empty($namespace) && !empty($namespace['other'])) {
-            $parts = explode($this->delimiter, $mailbox);
+            $parts = explode($this->delimiter, $folder);
             if (count($parts) == 2) {
                 $mbox = $parts[0] . $this->delimiter;
                 foreach ($namespace['other'] as $item) {
@@ -3187,11 +3169,11 @@
             }
         }
 
-        $options['name']       = $mailbox;
-        $options['attributes'] = $this->mailbox_attributes($mailbox, true);
-        $options['namespace']  = $this->mailbox_namespace($mailbox);
-        $options['rights']     = $acl && !$options['is_root'] ? (array)$this->my_rights($mailbox) : array();
-        $options['special']    = in_array($mailbox, $this->default_folders);
+        $options['name']       = $folder;
+        $options['attributes'] = $this->folder_attributes($folder, true);
+        $options['namespace']  = $this->folder_namespace($folder);
+        $options['rights']     = $acl && !$options['is_root'] ? (array)$this->my_rights($folder) : array();
+        $options['special']    = in_array($folder, $this->default_folders);
 
         // Set 'noselect' and 'norename' flags
         if (is_array($options['attributes'])) {
@@ -3226,12 +3208,12 @@
     /**
      * Synchronizes messages cache.
      *
-     * @param string $mailbox Folder name
+     * @param string $folder Folder name
      */
-    public function mailbox_sync($mailbox)
+    public function folder_sync($folder)
     {
         if ($mcache = $this->get_mcache_engine()) {
-            $mcache->synchronize($mailbox);
+            $mcache->synchronize($folder);
         }
     }
 
@@ -3241,13 +3223,19 @@
      *
      * @return string Space-separated list of header names
      */
-    private function get_fetch_headers()
+    protected function get_fetch_headers()
     {
-        $headers = explode(' ', $this->fetch_add_headers);
-        $headers = array_map('strtoupper', $headers);
+        if (!empty($this->options['fetch_headers'])) {
+            $headers = explode(' ', $this->options['fetch_headers']);
+            $headers = array_map('strtoupper', $headers);
+        }
+        else {
+            $headers = array();
+        }
 
-        if ($this->messages_caching || $this->get_all_headers)
+        if ($this->messages_caching || $this->options['all_headers']) {
             $headers = array_merge($headers, $this->all_headers);
+        }
 
         return implode(' ', array_unique($headers));
     }
@@ -3258,128 +3246,149 @@
      * ----------------------------------------*/
 
     /**
-     * Changes the ACL on the specified mailbox (SETACL)
+     * Changes the ACL on the specified folder (SETACL)
      *
-     * @param string $mailbox Mailbox name
+     * @param string $folder  Folder name
      * @param string $user    User name
      * @param string $acl     ACL string
      *
      * @return boolean True on success, False on failure
-     *
-     * @access public
      * @since 0.5-beta
      */
-    function set_acl($mailbox, $user, $acl)
+    public function set_acl($folder, $user, $acl)
     {
-        if ($this->get_capability('ACL'))
-            return $this->conn->setACL($mailbox, $user, $acl);
+        if (!$this->get_capability('ACL')) {
+            return false;
+        }
 
-        return false;
+        if (!$this->check_connection()) {
+            return false;
+        }
+
+        return $this->conn->setACL($folder, $user, $acl);
     }
 
 
     /**
      * Removes any <identifier,rights> pair for the
      * specified user from the ACL for the specified
-     * mailbox (DELETEACL)
+     * folder (DELETEACL)
      *
-     * @param string $mailbox Mailbox name
+     * @param string $folder  Folder name
      * @param string $user    User name
      *
      * @return boolean True on success, False on failure
-     *
-     * @access public
      * @since 0.5-beta
      */
-    function delete_acl($mailbox, $user)
+    public function delete_acl($folder, $user)
     {
-        if ($this->get_capability('ACL'))
-            return $this->conn->deleteACL($mailbox, $user);
+        if (!$this->get_capability('ACL')) {
+            return false;
+        }
 
-        return false;
+        if (!$this->check_connection()) {
+            return false;
+        }
+
+        return $this->conn->deleteACL($folder, $user);
     }
 
 
     /**
-     * Returns the access control list for mailbox (GETACL)
+     * Returns the access control list for folder (GETACL)
      *
-     * @param string $mailbox Mailbox name
+     * @param string $folder Folder name
      *
      * @return array User-rights array on success, NULL on error
-     * @access public
      * @since 0.5-beta
      */
-    function get_acl($mailbox)
+    public function get_acl($folder)
     {
-        if ($this->get_capability('ACL'))
-            return $this->conn->getACL($mailbox);
+        if (!$this->get_capability('ACL')) {
+            return null;
+        }
 
-        return NULL;
+        if (!$this->check_connection()) {
+            return null;
+        }
+
+        return $this->conn->getACL($folder);
     }
 
 
     /**
      * Returns information about what rights can be granted to the
-     * user (identifier) in the ACL for the mailbox (LISTRIGHTS)
+     * user (identifier) in the ACL for the folder (LISTRIGHTS)
      *
-     * @param string $mailbox Mailbox name
+     * @param string $folder  Folder name
      * @param string $user    User name
      *
      * @return array List of user rights
-     * @access public
      * @since 0.5-beta
      */
-    function list_rights($mailbox, $user)
+    public function list_rights($folder, $user)
     {
-        if ($this->get_capability('ACL'))
-            return $this->conn->listRights($mailbox, $user);
+        if (!$this->get_capability('ACL')) {
+            return null;
+        }
 
-        return NULL;
+        if (!$this->check_connection()) {
+            return null;
+        }
+
+        return $this->conn->listRights($folder, $user);
     }
 
 
     /**
      * Returns the set of rights that the current user has to
-     * mailbox (MYRIGHTS)
+     * folder (MYRIGHTS)
      *
-     * @param string $mailbox Mailbox name
+     * @param string $folder Folder name
      *
      * @return array MYRIGHTS response on success, NULL on error
-     * @access public
      * @since 0.5-beta
      */
-    function my_rights($mailbox)
+    public function my_rights($folder)
     {
-        if ($this->get_capability('ACL'))
-            return $this->conn->myRights($mailbox);
+        if (!$this->get_capability('ACL')) {
+            return null;
+        }
 
-        return NULL;
+        if (!$this->check_connection()) {
+            return null;
+        }
+
+        return $this->conn->myRights($folder);
     }
 
 
     /**
      * Sets IMAP metadata/annotations (SETMETADATA/SETANNOTATION)
      *
-     * @param string $mailbox Mailbox name (empty for server metadata)
+     * @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
-     * @access public
      * @since 0.5-beta
      */
-    function set_metadata($mailbox, $entries)
+    public function set_metadata($folder, $entries)
     {
+        if (!$this->check_connection()) {
+            return false;
+        }
+
         if ($this->get_capability('METADATA') ||
-            (!strlen($mailbox) && $this->get_capability('METADATA-SERVER'))
+            (!strlen($folder) && $this->get_capability('METADATA-SERVER'))
         ) {
-            return $this->conn->setMetadata($mailbox, $entries);
+            return $this->conn->setMetadata($folder, $entries);
         }
         else if ($this->get_capability('ANNOTATEMORE') || $this->get_capability('ANNOTATEMORE2')) {
             foreach ((array)$entries as $entry => $value) {
                 list($ent, $attr) = $this->md2annotate($entry);
                 $entries[$entry] = array($ent, $attr, $value);
             }
-            return $this->conn->setAnnotation($mailbox, $entries);
+            return $this->conn->setAnnotation($folder, $entries);
         }
 
         return false;
@@ -3389,27 +3398,29 @@
     /**
      * Unsets IMAP metadata/annotations (SETMETADATA/SETANNOTATION)
      *
-     * @param string $mailbox Mailbox name (empty for server metadata)
+     * @param string $folder  Folder name (empty for server metadata)
      * @param array  $entries Entry names array
      *
      * @return boolean True on success, False on failure
-     *
-     * @access public
      * @since 0.5-beta
      */
-    function delete_metadata($mailbox, $entries)
+    public function delete_metadata($folder, $entries)
     {
+        if (!$this->check_connection()) {
+            return false;
+        }
+
         if ($this->get_capability('METADATA') || 
-            (!strlen($mailbox) && $this->get_capability('METADATA-SERVER'))
+            (!strlen($folder) && $this->get_capability('METADATA-SERVER'))
         ) {
-            return $this->conn->deleteMetadata($mailbox, $entries);
+            return $this->conn->deleteMetadata($folder, $entries);
         }
         else if ($this->get_capability('ANNOTATEMORE') || $this->get_capability('ANNOTATEMORE2')) {
             foreach ((array)$entries as $idx => $entry) {
                 list($ent, $attr) = $this->md2annotate($entry);
                 $entries[$idx] = array($ent, $attr, NULL);
             }
-            return $this->conn->setAnnotation($mailbox, $entries);
+            return $this->conn->setAnnotation($folder, $entries);
         }
 
         return false;
@@ -3419,21 +3430,23 @@
     /**
      * Returns IMAP metadata/annotations (GETMETADATA/GETANNOTATION)
      *
-     * @param string $mailbox Mailbox name (empty for server metadata)
+     * @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
-     *
-     * @access public
      * @since 0.5-beta
      */
-    function get_metadata($mailbox, $entries, $options=array())
+    public function get_metadata($folder, $entries, $options=array())
     {
-        if ($this->get_capability('METADATA') || 
-            (!strlen($mailbox) && $this->get_capability('METADATA-SERVER'))
+        if (!$this->check_connection()) {
+            return null;
+        }
+
+        if ($this->get_capability('METADATA') ||
+            (!strlen($folder) && $this->get_capability('METADATA-SERVER'))
         ) {
-            return $this->conn->getMetadata($mailbox, $entries, $options);
+            return $this->conn->getMetadata($folder, $entries, $options);
         }
         else if ($this->get_capability('ANNOTATEMORE') || $this->get_capability('ANNOTATEMORE2')) {
             $queries = array();
@@ -3446,14 +3459,16 @@
             }
 
             // @TODO: Honor MAXSIZE and DEPTH options
-            foreach ($queries as $attrib => $entry)
-                if ($result = $this->conn->getAnnotation($mailbox, $entry, $attrib))
+            foreach ($queries as $attrib => $entry) {
+                if ($result = $this->conn->getAnnotation($folder, $entry, $attrib)) {
                     $res = array_merge_recursive($res, $result);
+                }
+            }
 
             return $res;
         }
 
-        return NULL;
+        return null;
     }
 
 
@@ -3465,17 +3480,17 @@
      *
      * @return array Entry-attribute list, NULL if not supported (?)
      */
-    private function md2annotate($entry)
+    protected function md2annotate($entry)
     {
         if (substr($entry, 0, 7) == '/shared') {
             return array(substr($entry, 7), 'value.shared');
         }
-        else if (substr($entry, 0, 8) == '/private') {
+        else if (substr($entry, 0, 8) == '/protected') {
             return array(substr($entry, 8), 'value.priv');
         }
 
         // @TODO: log error
-        return NULL;
+        return null;
     }
 
 
@@ -3487,16 +3502,16 @@
      * Enable or disable indexes caching
      *
      * @param string $type Cache type (@see rcmail::get_cache)
-     * @access public
      */
-    function set_caching($type)
+    public function set_caching($type)
     {
         if ($type) {
             $this->caching = $type;
         }
         else {
-            if ($this->cache)
+            if ($this->cache) {
                 $this->cache->close();
+            }
             $this->cache   = null;
             $this->caching = false;
         }
@@ -3505,7 +3520,7 @@
     /**
      * Getter for IMAP cache object
      */
-    private function get_cache_engine()
+    protected function get_cache_engine()
     {
         if ($this->caching && !$this->cache) {
             $rcmail = rcmail::get_instance();
@@ -3519,10 +3534,10 @@
      * Returns cached value
      *
      * @param string $key Cache key
+     *
      * @return mixed
-     * @access public
      */
-    function get_cache($key)
+    public function get_cache($key)
     {
         if ($cache = $this->get_cache_engine()) {
             return $cache->get($key);
@@ -3534,9 +3549,8 @@
      *
      * @param string $key  Cache key
      * @param mixed  $data Data
-     * @access public
      */
-    function update_cache($key, $data)
+    protected function update_cache($key, $data)
     {
         if ($cache = $this->get_cache_engine()) {
             $cache->set($key, $data);
@@ -3549,9 +3563,8 @@
      * @param string  $key         Cache key name or pattern
      * @param boolean $prefix_mode Enable it to clear all keys starting
      *                             with prefix specified in $key
-     * @access public
      */
-    function clear_cache($key=null, $prefix_mode=false)
+    public function clear_cache($key = null, $prefix_mode = false)
     {
         if ($cache = $this->get_cache_engine()) {
             $cache->remove($key, $prefix_mode);
@@ -3568,14 +3581,15 @@
      *
      * @param boolean $set Flag
      */
-    function set_messages_caching($set)
+    public function set_messages_caching($set)
     {
         if ($set) {
             $this->messages_caching = true;
         }
         else {
-            if ($this->mcache)
+            if ($this->mcache) {
                 $this->mcache->close();
+            }
             $this->mcache = null;
             $this->messages_caching = false;
         }
@@ -3585,13 +3599,13 @@
     /**
      * Getter for messages cache object
      */
-    private function get_mcache_engine()
+    protected function get_mcache_engine()
     {
         if ($this->messages_caching && !$this->mcache) {
             $rcmail = rcmail::get_instance();
             if ($dbh = $rcmail->get_dbh()) {
                 $this->mcache = new rcube_imap_cache(
-                    $dbh, $this, $rcmail->user->ID, $this->skip_deleted);
+                    $dbh, $this, $rcmail->user->ID, $this->options['skip_deleted']);
             }
         }
 
@@ -3602,19 +3616,19 @@
     /**
      * Clears the messages cache.
      *
-     * @param string $mailbox Folder name
+     * @param string $folder Folder name
      * @param array  $uids    Optional message UIDs to remove from cache
      */
-    function clear_message_cache($mailbox = null, $uids = null)
+    protected function clear_message_cache($folder = null, $uids = null)
     {
         if ($mcache = $this->get_mcache_engine()) {
-            $mcache->clear($mailbox, $uids);
+            $mcache->clear($folder, $uids);
         }
     }
 
 
     /* --------------------------------
-     *         private methods
+     *         protected methods
      * --------------------------------*/
 
     /**
@@ -3622,24 +3636,24 @@
      *
      * @param string $sort_field Sort column
      * @param string $sort_order Sort order
-     * @access private
      */
-    private function set_sort_order($sort_field, $sort_order)
+    protected function set_sort_order($sort_field, $sort_order)
     {
-        if ($sort_field != null)
+        if ($sort_field != null) {
             $this->sort_field = asciiwords($sort_field);
-        if ($sort_order != null)
+        }
+        if ($sort_order != null) {
             $this->sort_order = strtoupper($sort_order) == 'DESC' ? 'DESC' : 'ASC';
+        }
     }
 
 
     /**
-     * Sort mailboxes first by default folders and then in alphabethical order
+     * Sort folders first by default folders and then in alphabethical order
      *
-     * @param array $a_folders Mailboxes list
-     * @access private
+     * @param array $a_folders Folders list
      */
-    private function _sort_mailbox_list($a_folders)
+    protected function sort_folder_list($a_folders)
     {
         $a_out = $a_defaults = $folders = array();
 
@@ -3647,13 +3661,16 @@
 
         // find default folders and skip folders starting with '.'
         foreach ($a_folders as $i => $folder) {
-            if ($folder[0] == '.')
+            if ($folder[0] == '.') {
                 continue;
+            }
 
-            if (($p = array_search($folder, $this->default_folders)) !== false && !$a_defaults[$p])
+            if (($p = array_search($folder, $this->default_folders)) !== false && !$a_defaults[$p]) {
                 $a_defaults[$p] = $folder;
-            else
+            }
+            else {
                 $folders[$folder] = rcube_charset_convert($folder, 'UTF7-IMAP');
+            }
         }
 
         // sort folders and place defaults on the top
@@ -3669,7 +3686,7 @@
             // set the type of folder name variable (#1485527)
             $a_out[] = (string) $folder;
             unset($folders[$key]);
-            $this->_rsort($folder, $delimiter, $folders, $a_out);
+            $this->rsort($folder, $delimiter, $folders, $a_out);
         }
 
         return $a_out;
@@ -3677,16 +3694,16 @@
 
 
     /**
-     * @access private
+     * Recursive method for sorting folders
      */
-    private function _rsort($folder, $delimiter, &$list, &$out)
+    protected function rsort($folder, $delimiter, &$list, &$out)
     {
         while (list($key, $name) = each($list)) {
 	        if (strpos($name, $folder.$delimiter) === 0) {
 	            // set the type of folder name variable (#1485527)
     	        $out[] = (string) $name;
 	            unset($list[$key]);
-	            $this->_rsort($name, $delimiter, $list, $out);
+	            $this->rsort($name, $delimiter, $list, $out);
 	        }
         }
         reset($list);
@@ -3697,47 +3714,57 @@
      * Find UID of the specified message sequence ID
      *
      * @param int    $id       Message (sequence) ID
-     * @param string $mailbox  Mailbox name
+     * @param string $folder   Folder name
      *
      * @return int Message UID
      */
-    public function id2uid($id, $mailbox = null)
+    public function id2uid($id, $folder = null)
     {
-        if (!strlen($mailbox)) {
-            $mailbox = $this->mailbox;
+        if (!strlen($folder)) {
+            $folder = $this->folder;
         }
 
-        if ($uid = array_search($id, (array)$this->uid_id_map[$mailbox])) {
+        if ($uid = array_search($id, (array)$this->uid_id_map[$folder])) {
             return $uid;
         }
 
-        $uid = $this->conn->ID2UID($mailbox, $id);
+        if (!$this->check_connection()) {
+            return null;
+        }
 
-        $this->uid_id_map[$mailbox][$uid] = $id;
+        $uid = $this->conn->ID2UID($folder, $id);
+
+        $this->uid_id_map[$folder][$uid] = $id;
 
         return $uid;
     }
 
 
     /**
-     * Subscribe/unsubscribe a list of mailboxes and update local cache
-     * @access private
+     * Subscribe/unsubscribe a list of folders and update local cache
      */
-    private function _change_subscription($a_mboxes, $mode)
+    protected function change_subscription($folders, $mode)
     {
         $updated = false;
 
-        if (is_array($a_mboxes))
-            foreach ($a_mboxes as $i => $mailbox) {
-                $a_mboxes[$i] = $mailbox;
-
-                if ($mode == 'subscribe')
-                    $updated = $this->conn->subscribe($mailbox);
-                else if ($mode == 'unsubscribe')
-                    $updated = $this->conn->unsubscribe($mailbox);
+        if (!empty($folders)) {
+            if (!$this->check_connection()) {
+                return false;
             }
 
-        // clear cached mailbox list(s)
+            foreach ((array)$folders as $i => $folder) {
+                $folders[$i] = $folder;
+
+                if ($mode == 'subscribe') {
+                    $updated = $this->conn->subscribe($folder);
+                }
+                else if ($mode == 'unsubscribe') {
+                    $updated = $this->conn->unsubscribe($folder);
+                }
+            }
+        }
+
+        // clear cached folders list(s)
         if ($updated) {
             $this->clear_cache('mailboxes', true);
         }
@@ -3747,47 +3774,51 @@
 
 
     /**
-     * Increde/decrese messagecount for a specific mailbox
-     * @access private
+     * Increde/decrese messagecount for a specific folder
      */
-    private function _set_messagecount($mailbox, $mode, $increment)
+    protected function set_messagecount($folder, $mode, $increment)
     {
-        $mode = strtoupper($mode);
-        $a_mailbox_cache = $this->get_cache('messagecount');
-
-        if (!is_array($a_mailbox_cache[$mailbox]) || !isset($a_mailbox_cache[$mailbox][$mode]) || !is_numeric($increment))
+        if (!is_numeric($increment)) {
             return false;
+        }
+
+        $mode = strtoupper($mode);
+        $a_folder_cache = $this->get_cache('messagecount');
+
+        if (!is_array($a_folder_cache[$folder]) || !isset($a_folder_cache[$folder][$mode])) {
+            return false;
+        }
 
         // add incremental value to messagecount
-        $a_mailbox_cache[$mailbox][$mode] += $increment;
+        $a_folder_cache[$folder][$mode] += $increment;
 
         // there's something wrong, delete from cache
-        if ($a_mailbox_cache[$mailbox][$mode] < 0)
-            unset($a_mailbox_cache[$mailbox][$mode]);
+        if ($a_folder_cache[$folder][$mode] < 0) {
+            unset($a_folder_cache[$folder][$mode]);
+        }
 
         // write back to cache
-        $this->update_cache('messagecount', $a_mailbox_cache);
+        $this->update_cache('messagecount', $a_folder_cache);
 
         return true;
     }
 
 
     /**
-     * Remove messagecount of a specific mailbox from cache
-     * @access private
+     * Remove messagecount of a specific folder from cache
      */
-    private function _clear_messagecount($mailbox, $mode=null)
+    protected function clear_messagecount($folder, $mode=null)
     {
-        $a_mailbox_cache = $this->get_cache('messagecount');
+        $a_folder_cache = $this->get_cache('messagecount');
 
-        if (is_array($a_mailbox_cache[$mailbox])) {
+        if (is_array($a_folder_cache[$folder])) {
             if ($mode) {
-                unset($a_mailbox_cache[$mailbox][$mode]);
+                unset($a_folder_cache[$folder][$mode]);
             }
             else {
-                unset($a_mailbox_cache[$mailbox]);
+                unset($a_folder_cache[$folder]);
             }
-            $this->update_cache('messagecount', $a_mailbox_cache);
+            $this->update_cache('messagecount', $a_folder_cache);
         }
     }
 
@@ -3802,84 +3833,3 @@
     }
 
 }  // end class rcube_imap
-
-
-/**
- * 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;
-    }
-}
diff --git a/program/include/rcube_imap_cache.php b/program/include/rcube_imap_cache.php
index 5eca539..1b6a165 100644
--- a/program/include/rcube_imap_cache.php
+++ b/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');
diff --git a/program/include/rcube_message.php b/program/include/rcube_message.php
index d05b57a..8c61a4d 100644
--- a/program/include/rcube_message.php
+++ b/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?
diff --git a/program/include/rcube_result_index.php b/program/include/rcube_result_index.php
index 38a7528..0138f9f 100644
--- a/program/include/rcube_result_index.php
+++ b/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);
diff --git a/program/include/rcube_result_thread.php b/program/include/rcube_result_thread.php
index 0cfd383..e93423e 100644
--- a/program/include/rcube_result_thread.php
+++ b/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) {
diff --git a/program/include/rcube_shared.inc b/program/include/rcube_shared.inc
index 6767c93..936e895 100644
--- a/program/include/rcube_shared.inc
+++ b/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()
diff --git a/program/include/rcube_storage.php b/program/include/rcube_storage.php
new file mode 100644
index 0000000..124d148
--- /dev/null
+++ b/program/include/rcube_storage.php
@@ -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;
+    }
+}
diff --git a/program/include/rcube_template.php b/program/include/rcube_template.php
old mode 100755
new mode 100644
index 95f92a5..db14b3c
--- a/program/include/rcube_template.php
+++ b/program/include/rcube_template.php
@@ -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':
diff --git a/program/lib/utf7.inc b/program/lib/utf7.inc
deleted file mode 100644
index d2068be..0000000
--- a/program/lib/utf7.inc
+++ /dev/null
@@ -1,249 +0,0 @@
-<?php
-
-/*
- *  Copyright (C) 2000 Edmund Grimley Evans <edmundo@rano.org>
- * 
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- * 
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  Translated from C to PHP by Thomas Bruederli <roundcube@gmail.com>
- */ 
-
-
-/**
- * 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-).
- */
-function utf7_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.
- */
-function utf8_to_utf7($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 = $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;
-}
-
-?>
diff --git a/program/localization/en_US/messages.inc b/program/localization/en_US/messages.inc
index 525df95..9b51146 100644
--- a/program/localization/en_US/messages.inc
+++ b/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!';
diff --git a/program/steps/mail/check_recent.inc b/program/steps/mail/check_recent.inc
index 498bf0c..6673d0c 100644
--- a/program/steps/mail/check_recent.inc
+++ b/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
diff --git a/program/steps/mail/compose.inc b/program/steps/mail/compose.inc
index 274983a..6878498 100644
--- a/program/steps/mail/compose.inc
+++ b/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;
diff --git a/program/steps/mail/copy.inc b/program/steps/mail/copy.inc
index edb6af3..e553adc 100644
--- a/program/steps/mail/copy.inc
+++ b/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
diff --git a/program/steps/mail/folders.inc b/program/steps/mail/folders.inc
index 6e687b0..2aa83ee 100644
--- a/program/steps/mail/folders.inc
+++ b/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');
diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc
index 5d0b30a..0ffffc5 100644
--- a/program/steps/mail/func.inc
+++ b/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);
   }
diff --git a/program/steps/mail/get.inc b/program/steps/mail/get.inc
index d114e7c..7da8f13 100644
--- a/program/steps/mail/get.inc
+++ b/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);
       }
     }
 
diff --git a/program/steps/mail/getunread.inc b/program/steps/mail/getunread.inc
index 39880b9..64e7a6a 100644
--- a/program/steps/mail/getunread.inc
+++ b/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');
diff --git a/program/steps/mail/headers.inc b/program/steps/mail/headers.inc
index 5eee4bd..0cce5a5 100644
--- a/program/steps/mail/headers.inc
+++ b/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));
diff --git a/program/steps/mail/list.inc b/program/steps/mail/list.inc
index c2a4748..8bfb4c8 100644
--- a/program/steps/mail/list.inc
+++ b/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)
diff --git a/program/steps/mail/mark.inc b/program/steps/mail/mark.inc
index 3b52dfe..ab64deb 100644
--- a/program/steps/mail/mark.inc
+++ b/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);
diff --git a/program/steps/mail/move_del.inc b/program/steps/mail/move_del.inc
index 95a456e..63f2744 100644
--- a/program/steps/mail/move_del.inc
+++ b/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);
diff --git a/program/steps/mail/pagenav.inc b/program/steps/mail/pagenav.inc
index f15ac38..e74298b 100644
--- a/program/steps/mail/pagenav.inc
+++ b/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
diff --git a/program/steps/mail/search.inc b/program/steps/mail/search.inc
index 42bdf97..8835730 100644
--- a/program/steps/mail/search.inc
+++ b/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();
-
-
diff --git a/program/steps/mail/sendmail.inc b/program/steps/mail/sendmail.inc
index 953c752..ea673fd 100644
--- a/program/steps/mail/sendmail.inc
+++ b/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;
diff --git a/program/steps/mail/show.inc b/program/steps/mail/show.inc
index aee563d..7a3d3cd 100644
--- a/program/steps/mail/show.inc
+++ b/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);
     }
diff --git a/program/steps/mail/viewsource.inc b/program/steps/mail/viewsource.inc
index e2b4e1b..c9aeac4 100644
--- a/program/steps/mail/viewsource.inc
+++ b/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
 {
diff --git a/program/steps/settings/edit_folder.inc b/program/steps/settings/edit_folder.inc
index 9860c2f..b99764c 100644
--- a/program/steps/settings/edit_folder.inc
+++ b/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) {
diff --git a/program/steps/settings/folders.inc b/program/steps/settings/folders.inc
index 7364c6e..94f4462 100644
--- a/program/steps/settings/folders.inc
+++ b/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',
diff --git a/program/steps/settings/func.inc b/program/steps/settings/func.inc
index 44d3610..f91f2cf 100644
--- a/program/steps/settings/func.inc
+++ b/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'));
diff --git a/program/steps/settings/save_folder.inc b/program/steps/settings/save_folder.inc
index 0fc090e..b912c09 100644
--- a/program/steps/settings/save_folder.inc
+++ b/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;
diff --git a/program/steps/settings/save_prefs.inc b/program/steps/settings/save_prefs.inc
index 18ef5e7..ccb851b 100644
--- a/program/steps/settings/save_prefs.inc
+++ b/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];
       }
     }
 
diff --git a/tests/mailfunc.php b/tests/mailfunc.php
index 2e55d33..ed63788 100644
--- a/tests/mailfunc.php
+++ b/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';
     

--
Gitblit v1.9.1