Some Horde 5 fixes/style.
authorMichael M Slusarz <slusarz@curecanti.org>
Fri, 27 Feb 2009 18:30:17 +0000 (11:30 -0700)
committerMichael M Slusarz <slusarz@curecanti.org>
Fri, 27 Feb 2009 18:50:06 +0000 (11:50 -0700)
25 files changed:
imp/acl.php
imp/ajax.php
imp/compose-dimp.php
imp/folders-mimp.php
imp/folders.php
imp/lib/Block/Foldersummary.php
imp/lib/Block/summary.php
imp/lib/Block/tree_folders.php
imp/lib/DIMP.php
imp/lib/Folder.php
imp/lib/IMAP.php [deleted file]
imp/lib/IMAP/ACL.php [deleted file]
imp/lib/IMAP/Thread.php [deleted file]
imp/lib/IMAP/Tree.php [deleted file]
imp/lib/Imap.php [new file with mode: 0644]
imp/lib/Imap/Acl.php [new file with mode: 0644]
imp/lib/Imap/Thread.php [new file with mode: 0644]
imp/lib/Imap/Tree.php [new file with mode: 0644]
imp/lib/Search.php
imp/lib/Views/ListMessages.php
imp/lib/api.php
imp/lib/base.php
imp/lib/prefs.php
imp/mailbox.php
imp/thread.php

index c6ce06a..299d94b 100644 (file)
@@ -23,7 +23,7 @@ if ($prefs->isLocked('acl') || empty($_SESSION['imp']['acl'])) {
 }
 
 try {
-    $ACLDriver = &IMP_IMAP_ACL::singleton();
+    $ACLDriver = &IMP_Imap_Acl::singleton();
 } catch (Horde_Exception $e) {
     $notification->push($error, _("This server does not support sharing folders."));
     header('Location: ' . $prefs_url);
index 9fb6fb5..9c4b885 100644 (file)
@@ -119,7 +119,7 @@ function _getIdxString($indices)
 
 function _getPollInformation($mbox)
 {
-    $imptree = IMP_IMAP_Tree::singleton();
+    $imptree = IMP_Imap_Tree::singleton();
     $elt = $imptree->get($mbox);
     if ($imptree->isPolled($elt)) {
         $info = $imptree->getElementInfo($mbox);
@@ -190,7 +190,7 @@ case 'CreateFolder':
         break;
     }
 
-    $imptree = IMP_IMAP_Tree::singleton();
+    $imptree = IMP_Imap_Tree::singleton();
     $imptree->eltDiffStart();
 
     $imp_folder = IMP_Folder::singleton();
@@ -213,7 +213,7 @@ case 'DeleteFolder':
         break;
     }
 
-    $imptree = IMP_IMAP_Tree::singleton();
+    $imptree = IMP_Imap_Tree::singleton();
     $imptree->eltDiffStart();
 
     $imp_folder = IMP_Folder::singleton();
@@ -231,7 +231,7 @@ case 'RenameFolder':
         break;
     }
 
-    $imptree = IMP_IMAP_Tree::singleton();
+    $imptree = IMP_Imap_Tree::singleton();
     $imptree->eltDiffStart();
 
     $imp_folder = IMP_Folder::singleton();
@@ -285,8 +285,8 @@ case 'MarkFolderUnseen':
     break;
 
 case 'ListFolders':
-    $imptree = IMP_IMAP_Tree::singleton();
-    $result = DIMP::getFolderResponse($imptree, array('a' => $imptree->folderList(IMP_IMAP_TREE::FLIST_CONTAINER | IMP_IMAP_TREE::FLIST_VFOLDER), 'c' => array(), 'd' => array()));
+    $imptree = IMP_Imap_Tree::singleton();
+    $result = DIMP::getFolderResponse($imptree, array('a' => $imptree->folderList(IMP_Imap_Tree::FLIST_CONTAINER | IMP_Imap_Tree::FLIST_VFOLDER), 'c' => array(), 'd' => array()));
 
     $quota = _getQuota();
     if (!is_null($quota)) {
@@ -297,7 +297,7 @@ case 'ListFolders':
 case 'PollFolders':
     $result = new stdClass;
 
-    $imptree = IMP_IMAP_Tree::singleton();
+    $imptree = IMP_Imap_Tree::singleton();
 
     $result->poll = array();
     foreach ($imptree->getPollList(true) as $val) {
@@ -653,7 +653,7 @@ case 'ModifyPollFolder':
 
     $add = Util::getPost('add');
 
-    $imptree = IMP_IMAP_Tree::singleton();
+    $imptree = IMP_Imap_Tree::singleton();
 
     $result = new stdClass;
     $result->add = (bool) $add;
index ab26c97..80e971a 100644 (file)
@@ -138,7 +138,7 @@ if (count($_POST)) {
 
         /* Use IMP_Tree to determine whether the sent mail folder was
          * created. */
-        $imptree = &IMP_IMAP_Tree::singleton();
+        $imptree = &IMP_Imap_Tree::singleton();
         $imptree->eltDiffStart();
 
         $options = array(
index 6ee44e6..d1366d2 100644 (file)
@@ -29,16 +29,16 @@ if (empty($conf['user']['allow_folders'])) {
 $subscribe = $prefs->getValue('subscribe');
 $showAll = (!$subscribe || $_SESSION['imp']['showunsub']);
 
-/* Initialize the IMP_IMAP_Tree object. */
-$imptree = &IMP_IMAP_Tree::singleton();
-$mask = IMP_IMAP_TREE::NEXT_SHOWCLOSED;
+/* Initialize the IMP_Imap_Tree object. */
+$imptree = &IMP_Imap_Tree::singleton();
+$mask = IMP_Imap_Tree::NEXT_SHOWCLOSED;
 
 /* Toggle subscribed view, if necessary. */
 if ($subscribe && Util::getFormData('ts')) {
     $showAll = !$showAll;
     $_SESSION['imp']['showunsub'] = $showAll;
     $imptree->showUnsubscribed($showAll);
-    $mask |= IMP_IMAP_TREE::NEXT_SHOWSUB;
+    $mask |= IMP_Imap_Tree::NEXT_SHOWSUB;
 }
 
 /* Start iterating through the list of mailboxes, displaying them. */
index 7222e99..70a41fd 100644 (file)
@@ -56,8 +56,8 @@ $folders_url = Horde::selfUrl();
 /* Initialize the IMP_Folder object. */
 $imp_folder = &IMP_Folder::singleton();
 
-/* Initialize the IMP_IMAP_Tree object. */
-$imaptree = &IMP_IMAP_Tree::singleton();
+/* Initialize the IMP_Imap_Tree object. */
+$imaptree = &IMP_Imap_Tree::singleton();
 
 /* $folder_list is already encoded in UTF7-IMAP. */
 $charset = NLS::getCharset();
index 123c8c3..0cbe79b 100644 (file)
@@ -25,7 +25,7 @@ class IMP_Block_Foldersummary extends Horde_Block
         }
 
         /* Get list of mailboxes to poll. */
-        $imptree = &IMP_IMAP_Tree::singleton();
+        $imptree = &IMP_Imap_Tree::singleton();
         $folders = $imptree->getPollList(true, true);
 
         $anyUnseen = false;
index d66c645..861fb3d 100644 (file)
@@ -55,7 +55,7 @@ class Horde_Block_imp_summary extends Horde_Block
         }
 
         /* Get list of mailboxes to poll. */
-        $imaptree = &IMP_IMAP_Tree::singleton();
+        $imaptree = &IMP_Imap_Tree::singleton();
         $folders = $imaptree->getPollList(true, true);
 
         /* Quota info, if available. */
index 1c31e68..eab3545 100644 (file)
@@ -55,10 +55,10 @@ class Horde_Block_imp_tree_folders extends Horde_Block
         $name_url = Util::addParameter(Horde::applicationUrl('mailbox.php'), 'no_newmail_popup', 1);
 
         /* Initialize the IMP_Tree object. */
-        $imaptree = &IMP_IMAP_Tree::singleton();
-        $mask = IMP_IMAP_Tree::NEXT_SHOWCLOSED;
+        $imaptree = &IMP_Imap_Tree::singleton();
+        $mask = IMP_Imap_Tree::NEXT_SHOWCLOSED;
         if ($GLOBALS['prefs']->getValue('subscribe')) {
-            $mask |= IMP_IMAP_Tree::NEXT_SHOWSUB;
+            $mask |= IMP_Imap_Tree::NEXT_SHOWSUB;
         }
 
         $unseen = 0;
index a6e2336..4ae929f 100644 (file)
 class DIMP
 {
     /**
+     * Charset cache.
+     */
+    static protected $_charset;
+
+    /**
      * Output a dimp-style action (menubar) link.
      *
      * @param array $params  A list of parameters.
@@ -27,17 +32,16 @@ class DIMP
      *
      * @return string  An HTML link to $url.
      */
-    public function actionButton($params = array())
+    static public function actionButton($params = array())
     {
         $tooltip = (empty($params['tooltip'])) ? '' : $params['tooltip'];
 
         if (empty($params['title'])) {
-            static $charset;
-            if (!isset($charset)) {
-                $charset = NLS::getCharset();
+            if (!isset(self::$_charset)) {
+                self::$_charset = NLS::getCharset();
             }
             $old_error = error_reporting(0);
-            $tooltip = nl2br(htmlspecialchars($tooltip, ENT_QUOTES, $charset));
+            $tooltip = nl2br(htmlspecialchars($tooltip, ENT_QUOTES, self::$_charset));
             $title = $ak = '';
         } else {
             $title = $params['title'];
@@ -63,7 +67,7 @@ class DIMP
      *                        Each entry contains the three elements necessary
      *                        for a Horde::addScriptFile() call.
      */
-    public function header($title, $scripts = array())
+    static public function header($title, $scripts = array())
     {
         // Don't autoload any javascript files.
         Horde::disableAutoloadHordeJS();
@@ -121,7 +125,7 @@ class DIMP
      *
      * @return string  TODO
      */
-    protected function _includeDIMPJSVars()
+    static protected function _includeDIMPJSVars()
     {
         global $browser, $conf, $prefs, $registry;
 
@@ -269,7 +273,7 @@ class DIMP
     /**
      * Return an appended IMP folder string
      */
-    private function _appendedFolderPref($folder)
+    static private function _appendedFolderPref($folder)
     {
         return IMP::folderPref($folder, true);
     }
@@ -279,7 +283,7 @@ class DIMP
      *
      * @return string  The notification JS code.
      */
-    public function notify()
+    static public function notify()
     {
         $GLOBALS['notification']->notify(array('listeners' => 'status'));
         $msgs = $GLOBALS['imp_notify']->getStack(true);
@@ -304,9 +308,9 @@ class DIMP
      *
      * @return array  The object used by the JS code to update the folder tree.
      */
-    public function getFolderResponse($imptree, $changes = null)
+    static public function getFolderResponse($imptree, $changes = null)
     {
-        if ($changes === null) {
+        if (is_null($changes)) {
             $changes = $imptree->eltDiff();
         }
         if (empty($changes)) {
@@ -327,7 +331,7 @@ class DIMP
             foreach ($changes['c'] as $val) {
                 // Skip the base element, since any change there won't ever be
                 // updated on-screen.
-                if ($val != IMP_IMAP_TREE::BASE_ELT) {
+                if ($val != IMP_Imap_Tree::BASE_ELT) {
                     $result['c'][] = DIMP::_createFolderElt($imptree->element($val));
                 }
             }
@@ -362,7 +366,7 @@ class DIMP
      * 'v' (virtual) = Is this a virtual folder? [boolean] [DEFAULT: no]
      * </pre>
      */
-    private function _createFolderElt($elt)
+    static private function _createFolderElt($elt)
     {
         $ob = new stdClass;
         if ($elt['children']) {
@@ -387,27 +391,27 @@ class DIMP
             }
 
             switch ($elt['special']) {
-            case IMP_IMAP_TREE::SPECIAL_INBOX:
+            case IMP_Imap_Tree::SPECIAL_INBOX:
                 $ob->cl = 'inbox';
                 $ob->s = 1;
                 break;
 
-            case IMP_IMAP_TREE::SPECIAL_TRASH:
+            case IMP_Imap_Tree::SPECIAL_TRASH:
                 $ob->cl = 'trash';
                 $ob->s = 1;
                 break;
 
-            case IMP_IMAP_TREE::SPECIAL_SPAM:
+            case IMP_Imap_Tree::SPECIAL_SPAM:
                 $ob->cl = 'spam';
                 $ob->s = 1;
                 break;
 
-            case IMP_IMAP_TREE::SPECIAL_DRAFT:
+            case IMP_Imap_Tree::SPECIAL_DRAFT:
                 $ob->cl = 'drafts';
                 $ob->s = 1;
                 break;
 
-            case IMP_IMAP_TREE::SPECIAL_SENT:
+            case IMP_Imap_Tree::SPECIAL_SENT:
                 $ob->cl = 'sent';
                 $ob->s = 1;
                 break;
@@ -452,7 +456,7 @@ class DIMP
      * 'size' - The size of the attachment in KB (string)
      * </pre>
      */
-    public function getAttachmentInfo($imp_compose)
+    static public function getAttachmentInfo($imp_compose)
     {
         $fwd_list = array();
 
@@ -477,7 +481,7 @@ class DIMP
      *
      * @return array  The array of menu items.
      */
-    public function menuList()
+    static public function menuList()
     {
         if (isset($GLOBALS['conf']['dimp']['menu']['apps'])) {
             $apps = $GLOBALS['conf']['dimp']['menu']['apps'];
@@ -500,7 +504,7 @@ class DIMP
      *
      * @return string  The link to the message composition screen.
      */
-    public function composeLink($args = array(), $extra = array())
+    static public function composeLink($args = array(), $extra = array())
     {
         // IE 6 & 7 handles window.open() URL param strings differently if
         // triggered via an href or an onclick.  Since we have no hint
index 87f9776..19601e6 100644 (file)
@@ -116,11 +116,11 @@ class IMP_Folder
             return $this->_listCache[$sig];
         }
 
-        $imaptree = IMP_IMAP_Tree::singleton();
+        $imaptree = IMP_Imap_Tree::singleton();
 
-        $list_mask = IMP_IMAP_Tree::FLIST_CONTAINER | IMP_IMAP_Tree::FLIST_OB;
+        $list_mask = IMP_Imap_Tree::FLIST_CONTAINER | IMP_Imap_Tree::FLIST_OB;
         if (!$sub) {
-            $list_mask |= IMP_IMAP_Tree::FLIST_UNSUB;
+            $list_mask |= IMP_Imap_Tree::FLIST_UNSUB;
         }
         $flist = $imaptree->folderList($list_mask);
 
@@ -197,7 +197,7 @@ class IMP_Folder
 
         if (!empty($deleted)) {
             /* Update the IMAP_Tree cache. */
-            $imaptree = IMP_IMAP_Tree::singleton();
+            $imaptree = IMP_Imap_Tree::singleton();
             $imaptree->delete($deleted);
 
             $this->_onDelete($deleted);
@@ -284,7 +284,7 @@ class IMP_Folder
         $this->clearFlistCache();
 
         /* Update the IMAP_Tree object. */
-        $imaptree = IMP_IMAP_Tree::singleton();
+        $imaptree = IMP_Imap_Tree::singleton();
         $imaptree->insert($folder);
 
         /* Recreate Virtual Folders. */
@@ -302,7 +302,7 @@ class IMP_Folder
      */
     public function exists($folder)
     {
-        $imaptree = IMP_IMAP_Tree::singleton();
+        $imaptree = IMP_Imap_Tree::singleton();
         $elt = $imaptree->get($folder);
         if ($elt) {
             return !$imaptree->isContainer($elt);
@@ -343,10 +343,10 @@ class IMP_Folder
         $deleted = array($old);
         $inserted = array($new);
 
-        $imaptree = IMP_IMAP_Tree::singleton();
+        $imaptree = IMP_Imap_Tree::singleton();
 
         /* Get list of any folders that are underneath this one. */
-        $all_folders = array_merge(array($old), $imaptree->folderList(IMP_IMAP_Tree::FLIST_UNSUB, $old));
+        $all_folders = array_merge(array($old), $imaptree->folderList(IMP_Imap_Tree::FLIST_UNSUB, $old));
         $sub_folders = $imaptree->folderList();
 
         try {
@@ -405,7 +405,7 @@ class IMP_Folder
 
         if (!empty($subscribed)) {
             /* Initialize the IMAP_Tree object. */
-            $imaptree = IMP_IMAP_Tree::singleton();
+            $imaptree = IMP_Imap_Tree::singleton();
             $imaptree->subscribe($subscribed);
 
             /* Reset the folder cache. */
@@ -451,7 +451,7 @@ class IMP_Folder
 
         if (!empty($unsubscribed)) {
             /* Initialize the IMAP_Tree object. */
-            $imaptree = IMP_IMAP_Tree::singleton();
+            $imaptree = IMP_Imap_Tree::singleton();
             $imaptree->unsubscribe($unsubscribed);
 
             /* Reset the folder cache. */
diff --git a/imp/lib/IMAP.php b/imp/lib/IMAP.php
deleted file mode 100644 (file)
index b99a0f6..0000000
+++ /dev/null
@@ -1,342 +0,0 @@
-<?php
-/**
- * The IMP_IMAP:: class provides common functions for interaction with the
- * IMAP/POP3 server via the Horde_Imap_Client:: library.
- *
- * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (GPL). If you
- * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
- *
- * @author  Michael Slusarz <slusarz@horde.org>
- * @package IMP
- */
-class IMP_IMAP
-{
-    /**
-     * The Horde_Imap_Client object.
-     *
-     * @var Horde_Imap_Client
-     */
-    public $ob = null;
-
-    /**
-     * The Horde_Imap_Client_Utils object.
-     *
-     * @var Horde_Imap_Client_Utils
-     */
-    public $utils = null;
-
-    /**
-     * Is connection read-only?
-     *
-     * @var array
-     */
-    protected $_readonly = array();
-
-    /**
-     * Namespace cache.
-     *
-     * @var array
-     */
-    protected $_nscache = array();
-
-    /**
-     * Default namespace.
-     *
-     * @var array
-     */
-    protected $_nsdefault;
-
-    /**
-     * Constructor.
-     */
-    function __construct()
-    {
-        /* Register the logging callback. */
-        Horde_Imap_Client_Exception::$logCallback = array($this, 'logException');
-
-        /* Rebuild the Horde_Imap_Client object. */
-        $this->_loadImapObject();
-
-        $this->utils = new Horde_Imap_Client_Utils();
-    }
-
-    /**
-     * Save the Horde_Imap_Client object on session shutdown.
-     */
-    function __destruct()
-    {
-        /* Only need to serialize object once a second. When we do serialize,
-         * make sure we login in order to ensure we have done the necessary
-         * initialization. */
-        if ($this->ob &&
-            isset($_SESSION['imp']) &&
-            !isset($_SESSION['imp']['imap_ob'])) {
-            $this->ob->login();
-            $_SESSION['imp']['imap_ob'] = serialize($this->ob);
-        }
-    }
-
-    /**
-     * Loads the server configuration from servers.php.
-     *
-     * @param string $server  Returns this labeled entry only.
-     *
-     * @return mixed  If $server is set, then return this entry, or return the
-     *                entire servers array. Returns false on error.
-     */
-    static public function loadServerConfig($server = null)
-    {
-        $servers = Horde::loadConfiguration('servers.php', 'servers', 'imp');
-        if (is_a($servers, 'PEAR_Error')) {
-            Horde::logMessage($servers, __FILE__, __LINE__, PEAR_LOG_ERR);
-            return false;
-        }
-
-        if (is_null($server)) {
-            return $servers;
-        }
-
-        /* Check for the existence of the server in the config file. */
-        if (empty($servers[$server]) || !is_array($servers[$server])) {
-            $entry = sprintf('Invalid server key "%s" from client [%s]', $server, $_SERVER['REMOTE_ADDR']);
-            Horde::logMessage($entry, __FILE__, __LINE__, PEAR_LOG_ERR);
-            return false;
-        }
-
-        return $servers[$server];
-    }
-
-    /**
-     * Loads the Horde_Imap_Client object from serialized session data and
-     * loads the object into the global $imp_imap variable.
-     *
-     * @return boolean  True on success, false on error.
-     */
-    protected function _loadImapObject()
-    {
-        if (!is_null($this->ob)) {
-            return true;
-        }
-
-        if (!isset($_SESSION['imp']['imap_ob'])) {
-            return false;
-        }
-
-        Horde_Imap_Client::$encryptKey = IMP::getAuthKey();
-
-        $old_error = error_reporting(0);
-        $this->ob = unserialize($_SESSION['imp']['imap_ob']);
-        error_reporting($old_error);
-
-        if (empty($this->ob)) {
-            // @todo How to handle bad unserialize?
-            // @todo Log message
-            return false;
-        }
-
-        $this->_postcreate($_SESSION['imp']['protocol']);
-
-        return true;
-    }
-
-    /**
-     * Create a new Horde_Imap_Client object.
-     *
-     * @param string $username  The username to authenticate with.
-     * @param string $password  The password to authenticate with.
-     * @param string $key       Create a new object using this server key.
-     * @param boolean $global   If true, treate the created object as the IMP
-     *                          global IMAP object.
-     *
-     * @return boolean  The object on success, false on error.
-     */
-    public function createImapObject($username, $password, $key,
-                                     $global = true)
-    {
-        if ($global && !is_null($this->ob)) {
-            return $GLOBALS['imp_imap'];
-        }
-
-        if (($server = $this->loadServerConfig($key)) === false) {
-            return false;
-        }
-
-        $protocol = isset($server['protocol']) ? strtolower($server['protocol']) : 'imap';
-
-        $imap_config = array(
-            'debug' => isset($server['debug']) ? $server['debug'] : null,
-            'hostspec' => isset($server['hostspec']) ? $server['hostspec'] : null,
-            'password' => $password,
-            'port' => isset($server['port']) ? $server['port'] : null,
-            'secure' => isset($server['secure']) ? $server['secure'] : false,
-            'statuscache' => true,
-            'timeout' => !empty($server['timeout']) ? $server['timeout'] : 10,
-            'username' => $username,
-        );
-
-        /* Initialize caching. */
-        if (!empty($server['cache'])) {
-            $c = $server['cache'];
-            $driver = $GLOBALS['conf']['cache']['driver'];
-            if ($driver != 'none') {
-                $imap_config['cache'] = array(
-                    'comparator' => empty($c['comparator']) ? false : $c['comparator'],
-                    'compress' => empty($c['compress']) ? false : $c['compress'],
-                    'driver' => $driver,
-                    'driver_params' => Horde::getDriverConfig('cache', $driver),
-                    'id' => empty($c['id']) ? false : $c['id'],
-                    'lang' => empty($c['lang']) ? false : $c['lang'],
-                    'lifetime' => empty($c['lifetime']) ? false : $c['lifetime'],
-                    'slicesize' => empty($c['slicesize']) ? false : $c['slicesize'],
-                );
-            }
-        }
-
-        try {
-            $ob = Horde_Imap_Client::getInstance(($protocol == 'imap') ? 'Socket' : 'Cclient_pop3', $imap_config);
-        } catch (Horde_Imap_Client_Exception $e) {
-            return false;
-        }
-
-        if ($global) {
-            $this->ob = $ob;
-            $this->_postcreate($protocol);
-        }
-
-        return $ob;
-    }
-
-    /**
-     * Alter some IMP settings once we load/create the global object.
-     *
-     * @param string $protocol  The protocol used to connect.
-     */
-    protected function _postcreate($protocol)
-    {
-        global $conf, $prefs;
-
-        switch ($protocol) {
-        case 'pop':
-            /* Turn some options off if we are working with POP3. */
-            $conf['user']['allow_folders'] = false;
-            $prefs->setValue('save_sent_mail', false);
-            $prefs->setLocked('save_sent_mail', true);
-            $prefs->setLocked('sent_mail_folder', true);
-            $prefs->setLocked('drafts_folder', true);
-            $prefs->setLocked('trash_folder', true);
-            break;
-        }
-    }
-
-    /**
-     * Is the current IMAP connection read-only?
-     *
-     * @param string $mailbox  The mailbox.
-     *
-     * @return boolean  Is the connection read-only?
-     */
-    public function isReadOnly($mailbox)
-    {
-        if (!isset($this->_readonly[$mailbox])) {
-            $this->_readonly[$mailbox] =
-                !empty($GLOBALS['conf']['hooks']['mbox_readonly']) &&
-                Horde::callHook('_imp_hook_mbox_readonly', array($mailbox), 'imp');
-        }
-
-        return $this->_readonly[$mailbox];
-    }
-
-    /**
-     * Logs an exception from Horde_Imap_Client.
-     *
-     * @param object Horde_Imap_Client_Exception $e  The exception object.
-     */
-    public function logException($e)
-    {
-        Horde::logMessage($e, $e->getFile(), $e->getLine(), PEAR_LOG_ERR);
-    }
-
-    /**
-     * Get the namespace list.
-     *
-     * @return array  An array of namespace information.
-     */
-    public function getNamespaceList()
-    {
-        try {
-            return $GLOBALS['imp_imap']->ob->getNamespaces(!empty($_SESSION['imp']['imap_ext']['namespace']) ? $_SESSION['imp']['imap_ext']['namespace'] : array());
-        } catch (Horde_Imap_Client_Exception $e) {
-            // @todo Error handling
-            return array();
-        }
-    }
-
-    /**
-     * Get namespace info for a full folder path.
-     *
-     * @param string $mailbox  The folder path. If empty, will return info
-     *                         on the default namespace (i.e. the first
-     *                         personal namespace).
-     * @param boolean $empty   If true and no matching namespace is found,
-     *                         return the empty namespace, if it exists.
-     *
-     * @return mixed  The namespace info for the folder path or null if the
-     *                path doesn't exist.
-     */
-    public function getNamespace($mailbox = null, $empty = true)
-    {
-        if ($_SESSION['imp']['protocol'] == 'pop') {
-            return null;
-        }
-
-        $ns = $this->getNamespaceList();
-
-        if ($mailbox === null) {
-            reset($ns);
-            $mailbox = key($ns);
-        }
-
-        $key = (int)$empty;
-        if (isset($this->_nscache[$key][$mailbox])) {
-            return $this->_nscache[$key][$mailbox];
-        }
-
-        foreach ($ns as $key => $val) {
-            $mbx = $mailbox . $val['delimiter'];
-            if (!empty($key) && (strpos($mbx, $key) === 0)) {
-                $this->_nscache[$key][$mailbox] = $val;
-                return $val;
-            }
-        }
-
-        $this->_nscache[$key][$mailbox] = ($empty && isset($ns[''])) ? $ns[''] : null;
-
-        return $this->_nscache[$key][$mailbox];
-    }
-
-    /**
-     * Get the default personal namespace.
-     *
-     * @return mixed  The default personal namespace info.
-     */
-    public function defaultNamespace()
-    {
-        if ($_SESSION['imp']['protocol'] == 'pop') {
-            return null;
-        }
-
-        if (!isset($this->_nsdefault)) {
-            $this->_nsdefault = null;
-            foreach ($this->getNamespaceList() as $val) {
-                if ($val['type'] == 'personal') {
-                    $this->_nsdefault = $val;
-                    break;
-                }
-            }
-        }
-
-        return $this->_nsdefault;
-    }
-}
diff --git a/imp/lib/IMAP/ACL.php b/imp/lib/IMAP/ACL.php
deleted file mode 100644 (file)
index ca800a8..0000000
+++ /dev/null
@@ -1,212 +0,0 @@
-<?php
-/**
- * Contains functions related to managing IMAP Access Control Lists.
- *
- * Copyright 2003-2009 The Horde Project (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
- *
- * @author  Chris Hastie <imp@oak-wood.co.uk>
- * @package IMP
- */
-class IMP_IMAP_ACL
-{
-    /**
-     * Singleton instance.
-     *
-     * @var IMP_IMAP_ACL
-     */
-    static protected $_instance = null;
-
-    /**
-     * Hash containing the list of possible rights and a human readable
-     * description of each.
-     *
-     * @var array
-     */
-    protected $_rightsList;
-
-    /**
-     * Array containing user names that cannot have their access rights
-     * changed.
-     *
-     * @var boolean
-     */
-    protected $_protected;
-
-    /**
-     * Attempts to return a reference to a concrete object instance.
-     * It will only create a new instance if no instance currently exists.
-     *
-     * @return IMP_IMAP_ACL  The created concrete instance.
-     */
-    static public function singleton()
-    {
-        if (!self::$_instance) {
-            self::$_instance = new IMP_IMAP_ACL();
-        }
-
-        return self::$_instance;
-    }
-
-    /**
-     * Constructor.
-     */
-    protected function __construct()
-    {
-        if ($_SESSION['imp']['protocol'] != 'imap') {
-            throw new Horde_Exception(_("ACL requires an IMAP server."));
-        }
-
-        $capability = $GLOBALS['imp_imap']->ob->queryCapability('ACL');
-        if (!$capability) {
-            throw new Horde_Exception(_("IMAP server does not support ACLs."));
-        }
-
-        $rfc4314 = $GLOBALS['imp_imap']->ob->queryCapability('RIGHTS');
-
-        $this->_protected = array($GLOBALS['imp_imap']->ob->getParam('username'));
-
-        $this->_rightsList = array(
-            'l' => array(
-                'desc' => _("List - user can see the folder"),
-                'title' => _("List")
-            ),
-            'r' => array(
-                'desc' => _("Read messages"),
-                'title' => _("Read")
-            ),
-            's' => array(
-                'desc' => _("Mark with Seen/Unseen flags"),
-                'title' => _("Mark (Seen)")
-            ),
-            'w' => array(
-                'desc' => _("Mark with other flags (e.g. Important/Answered)"),
-                'title' => _("Mark (Other)")
-            ),
-            'i' => array(
-                'desc' => _("Insert messages"),
-                'title' => _("Insert")
-            ),
-            'p' => array(
-                'desc' => _("Post to this folder (not enforced by IMAP)"),
-                'title' => _("Post")
-            ),
-            'a' => array(
-                'desc' => _("Administer - set permissions for other users"),
-                'title' => _("Administer")
-            )
-        );
-
-        if ($rfc4314) {
-            // RFC 4314 compliant rights
-            $this->_rightsList = array_merge($this->_rightsList, array(
-                'k' => array(
-                    'desc' => _("Create sub folders"),
-                    'title' => _("Create Folders")
-                ),
-                'x' => array(
-                    'desc' => _("Delete sub folders"),
-                    'title' => _("Delete Folders")
-                ),
-                't' => array(
-                    'desc' => _("Delete messages"),
-                    'title' => _("Delete")
-                ),
-                'e' => array(
-                    'desc' => _("Purge messages"),
-                    'title' => _("Purge")
-                )
-            ));
-        } else {
-            // RFC 2086 compliant rights
-            $this->_rightsList = array_merge($this->_rightsList, array(
-                'c' => array(
-                    'desc' =>_("Create sub folders"),
-                    'title' => _("Create Folder")
-                ),
-                'd' => array(
-                    'desc' => _("Delete and purge messages"),
-                    'title' => _("Delete/Purge")
-                )
-            ));
-        }
-    }
-
-    /**
-     * Attempts to retrieve the existing ACL for a mailbox from the server.
-     *
-     * @param string $mbox  The mailbox to get the ACL for.
-     *
-     * @return array  A hash containing information on the ACL.
-     * @throws Horde_Exception
-     */
-    public function getACL($mbox)
-    {
-        try {
-            return $GLOBALS['imp_imap']->ob->getACL($mbox);
-        } catch (Horde_Imap_Client_Exception $e) {
-            throw new Horde_Exception(_("Could not retrieve ACL"));
-        }
-    }
-
-    /**
-     * Edits an ACL on the server.
-     *
-     * @param string $mbox  The mailbox on which to edit the ACL.
-     * @param string $user  The user to grant rights to.
-     * @param array $acl    The keys of which are the rights to be granted
-     *                      (see RFC 2086).
-     *
-     * @throws Horde_Exception
-     */
-    public function editACL($mbox, $user, $acl)
-    {
-        try {
-            $GLOBALS['imp_imap']->ob->setACL($mbox, $user, array('rights' => $acl));
-        } catch (Horde_Imap_Client_Exception $e) {
-            throw new Horde_Exception(sprintf(_("Couldn't give user \"%s\" the following rights for the folder \"%s\": %s"), $user, $mbox, implode('', $acl)));
-        }
-    }
-
-    /**
-     * Can a user edit the ACL for this mailbox?
-     *
-     * @param string $mbox  The mailbox name.
-     * @param string $user  A user name.
-     *
-     * @return boolean  True if $user has 'a' right
-     */
-    public function canEdit($mbox, $user)
-    {
-        try {
-            $rights = $GLOBALS['imp_imap']->ob->listACLRights($mbox, $user);
-            foreach ($rights as $val) {
-                if (strpos($val, 'a') !== false) {
-                    return true;
-                }
-            }
-            return false;
-        } catch (Horde_Imap_Client_Exception $e) {
-            return false;
-        }
-    }
-
-    /**
-     * TODO
-     */
-    public function getRights()
-    {
-        return $this->_rightsList;
-    }
-
-    /**
-     * TODO
-     */
-    public function getProtected()
-    {
-        return $this->_protected;
-    }
-
-}
diff --git a/imp/lib/IMAP/Thread.php b/imp/lib/IMAP/Thread.php
deleted file mode 100644 (file)
index 99458b2..0000000
+++ /dev/null
@@ -1,137 +0,0 @@
-<?php
-/**
- * The IMP_IMAP_Thread class provides functions to generate thread tree
- * images.
- *
- * Copyright 2005-2009 The Horde Project (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (GPL). If you
- * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
- *
- * @author  Michael Slusarz <slusarz@horde.org>
- * @package IMP
- */
-class IMP_IMAP_Thread
-{
-    /**
-     * The thread data object.
-     *
-     * @var Horde_Imap_Client_Thread
-     */
-    protected $_thread;
-
-    /**
-     * Images used and their internal representations.
-     *
-     * @var array
-     */
-    static protected $_imglist = array(
-        '0' => 'blank.png',
-        '1' => 'line.png',
-        '2' => 'join.png',
-        '3' => 'joinbottom-down.png',
-        '4' => 'joinbottom.png'
-    );
-
-    /**
-     * Constructor.
-     *
-     * @param Horde_Imap_Client_Thread $thread  The thread data object.
-     */
-    function __construct($thread)
-    {
-        $this->_thread = $thread;
-    }
-
-    /**
-     * Generate the thread representation for the given index list in the
-     * internal format (a string with each character representing the graphic
-     * to be displayed from $_imglist).
-     *
-     * @param array $indices    The list of indices to create a tree for.
-     * @param boolean $sortdir  True for newest first, false for oldest first.
-     *
-     * @return array  An array with the index as the key and the internal
-     *                thread representation as the value.
-     */
-    public function getThreadTreeOb($indices, $sortdir)
-    {
-        $container = $last_level = $last_thread = null;
-        $thread_level = $tree = array();
-        $t = &$this->_thread;
-
-        $indices = array_intersect($t->messageList($sortdir), $indices);
-
-        /* If starting in the middle of a thread, the threadLevel tree needs
-         * to be built from the base of the current thread. */
-        $first = reset($indices);
-        foreach ($t->getThread(reset($indices)) as $val) {
-            if ($first == $val) {
-                break;
-            }
-            $thread_level[$t->getThreadIndent($val)] = $t->lastInLevel($val);
-        }
-
-        foreach ($indices as $val) {
-            $tree[$val] = '';
-
-            $indentBase = $t->getThreadBase($val);
-            if (empty($indentBase)) {
-                continue;
-            }
-
-            $lines = '';
-            $indentLevel = $t->getThreadIndent($val);
-            $lastinlevel = $t->lastInLevel($val);
-
-            if ($lastinlevel && ($indentBase == $val)) {
-                continue;
-            }
-
-            if ($lastinlevel) {
-                $join_img = ($sortdir) ? 3 : 4;
-            } elseif (($indentLevel == 1) && ($indentBase == $val)) {
-                $join_img = ($sortdir) ? 4 : 3;
-            } else {
-                $join_img = 2;
-            }
-
-            $thread_level[$indentLevel] = $lastinlevel;
-            $line = '';
-
-            for ($i = 1; $i < $indentLevel; ++$i) {
-                $line .= (!isset($thread_level[$i]) || ($thread_level[$i])) ? 0 : 1;
-            }
-            $tree[$val] = $line . $join_img;
-        }
-
-        return $tree;
-    }
-
-    /**
-     * Generate the thread representation image for the given index list.
-     *
-     * @param array $indices    The list of indices to create a tree for.
-     * @param boolean $sortdir  True for newest first, false for oldest first.
-     *
-     * @return array  An array with the index as the key and the thread image
-     *                representation as the value.
-     */
-    public function getThreadImageTree($indices, $sortdir)
-    {
-        $imgs = $tree = array();
-
-        foreach (self::$_imglist as $key => $val) {
-            $imgs[$key] = Horde::img('tree/' . (($key != 0 && !empty($GLOBALS['nls']['rtl'][$GLOBALS['language']])) ? ('rev-' . $val) : $val), null, null, $GLOBALS['registry']->getImageDir('horde'));
-        }
-
-        foreach ($this->getThreadTreeOb($indices, $sortdir) as $k => $v) {
-            $tree[$k] = '';
-            for ($i = 0, $length = strlen($v); $i < $length; ++$i) {
-                $tree[$k] .= $imgs[$v[$i]];
-            }
-        }
-        return $tree;
-    }
-
-}
diff --git a/imp/lib/IMAP/Tree.php b/imp/lib/IMAP/Tree.php
deleted file mode 100644 (file)
index aace11e..0000000
+++ /dev/null
@@ -1,1990 +0,0 @@
-<?php
-/**
- * The IMP_IMAP_Tree class provides a tree view of the mailboxes in an
- * IMAP/POP3 repository.  It provides access functions to iterate through this
- * tree and query information about individual mailboxes.
- * In IMP, folders = IMAP mailboxes so the two terms are used interchangably.
- *
- * Copyright 2000-2009 The Horde Project (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (GPL). If you
- * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
- *
- * @author  Chuck Hagenbuch <chuck@horde.org>
- * @author  Jon Parise <jon@horde.org>
- * @author  Anil Madhavapeddy <avsm@horde.org>
- * @author  Michael Slusarz <slusarz@horde.org>
- * @package IMP
- */
-class IMP_IMAP_Tree
-{
-    /* Constants for mailboxElt attributes. */
-    const ELT_NOSELECT = 1;
-    const ELT_NAMESPACE = 2;
-    const ELT_IS_OPEN = 4;
-    const ELT_IS_SUBSCRIBED = 8;
-    const ELT_NOSHOW = 16;
-    const ELT_IS_POLLED = 32;
-    const ELT_NEED_SORT = 64;
-    const ELT_VFOLDER = 128;
-    const ELT_NONIMAP = 256;
-    const ELT_INVISIBLE = 512;
-
-    /* The isOpen() expanded mode constants. */
-    const OPEN_NONE = 0;
-    const OPEN_ALL = 1;
-    const OPEN_USER = 2;
-
-    /* The manner to which to traverse the tree when calling next(). */
-    const NEXT_SHOWCLOSED = 1;
-    const NEXT_SHOWSUB = 2;
-
-    /* The string used to indicate the base of the tree. */
-    const BASE_ELT = '%';
-
-    /* Defines used with the output from the build() function. */
-    const SPECIAL_INBOX = 1;
-    const SPECIAL_TRASH = 2;
-    const SPECIAL_DRAFT = 3;
-    const SPECIAL_SPAM = 4;
-    const SPECIAL_SENT = 5;
-
-    /* Defines used with folderList(). */
-    const FLIST_CONTAINER = 1;
-    const FLIST_UNSUB = 2;
-    const FLIST_OB = 4;
-    const FLIST_VFOLDER = 8;
-
-    /* Add a percent to folder key since it allows us to sort by name but
-     * never conflict with an IMAP mailbox of the same name (since '%' is an
-     * invalid character in an IMAP mailbox string). */
-    public $VFOLDER_LABEL;
-    public $VFOLDER_KEY;
-
-    /* Defines used with namespace display. */
-    public $SHARED_LABEL;
-    public $SHARED_KEY;
-    public $OTHER_LABEL;
-    public $OTHER_KEY;
-
-    /**
-     * Array containing the mailbox tree.
-     *
-     * @var array
-     */
-    protected $_tree;
-
-    /**
-     * Location of current element in the tree.
-     *
-     * @var string
-     */
-    protected $_currparent = null;
-
-    /**
-     * Location of current element in the tree.
-     *
-     * @var integer
-     */
-    protected $_currkey = null;
-
-    /**
-     * Location of current element in the tree.
-     *
-     * @var array
-     */
-    protected $_currstack = array();
-
-    /**
-     * Show unsubscribed mailboxes?
-     *
-     * @var boolean
-     */
-    protected $_showunsub = false;
-
-    /**
-     * Parent list.
-     *
-     * @var array
-     */
-    protected $_parent = array();
-
-    /**
-     * The cached list of mailboxes to poll.
-     *
-     * @var array
-     */
-    protected $_poll = null;
-
-    /**
-     * The cached list of expanded folders.
-     *
-     * @var array
-     */
-    protected $_expanded = null;
-
-    /**
-     * Cached list of subscribed mailboxes.
-     *
-     * @var array
-     */
-    protected $_subscribed = null;
-
-    /**
-     * The cached full list of mailboxes on the server.
-     *
-     * @var array
-     */
-    protected $_fulllist = null;
-
-    /**
-     * Tree changed flag.  Set when something in the tree has been altered.
-     *
-     * @var boolean
-     */
-    protected $_changed = false;
-
-    /**
-     * Have we shown unsubscribed folders previously?
-     *
-     * @var boolean
-     */
-    protected $_unsubview = false;
-
-    /**
-     * The string used for the IMAP delimiter.
-     *
-     * @var string
-     */
-    protected $_delimiter = '/';
-
-    /**
-     * The list of namespaces to add to the tree.
-     *
-     * @var array
-     */
-    protected $_namespaces = array();
-
-    /**
-     * Used to determine the list of element changes.
-     *
-     * @var array
-     */
-    protected $_eltdiff = null;
-
-    /**
-     * If set, track element changes.
-     *
-     * @var boolean
-     */
-    protected $_trackdiff = true;
-
-    /**
-     * See $open parameter in build().
-     *
-     * @var boolean
-     */
-    protected $_forceopen = false;
-
-    /**
-     * Attempts to return a reference to a concrete IMP_IMAP_Tree instance.
-     *
-     * If an IMP_IMAP_Tree object is currently stored in the cache, re-create
-     * that object.  Else, create a new instance.  Ensures that only one
-     * instance is available at any time.
-     *
-     * @return IMP_IMAP_Tree  The object or null.
-     */
-    static public function singleton()
-    {
-        static $instance;
-
-        if (!isset($instance)) {
-            if (!empty($_SESSION['imp']['cache']['tree'])) {
-                $imp_cache = IMP::getCache();
-                $instance = unserialize($imp_cache->get($_SESSION['imp']['cache']['tree'], 86400));
-            }
-            if (empty($instance)) {
-                $instance = new IMP_IMAP_Tree();
-            }
-        }
-
-        return $instance;
-    }
-
-    /**
-     * Constructor.
-     */
-    protected function __construct()
-    {
-        if ($_SESSION['imp']['protocol'] == 'imap') {
-            $ns = $GLOBALS['imp_imap']->getNamespaceList();
-            $ptr = reset($ns);
-            $this->_delimiter = $ptr['delimiter'];
-            $this->_namespaces = (empty($GLOBALS['conf']['user']['allow_folders'])) ? array() : $ns;
-        }
-
-        if (!isset($_SESSION['imp']['cache']['tree'])) {
-            $imp_cache = IMP::getCache();
-            $_SESSION['imp']['cache']['tree'] = $imp_cache
-                ? uniqid(mt_rand() . Auth::getAuth())
-                : null;
-        }
-
-        /* Must set these values here because PHP 5 does not allow assignment
-         * of const's to gettext strings. */
-        $this->VFOLDER_LABEL = _("Virtual Folders");
-        $this->VFOLDER_KEY = $this->VFOLDER_LABEL . '%';
-        $this->SHARED_LABEL = _("Shared Folders");
-        $this->SHARED_KEY = $this->SHARED_LABEL . '%';
-        $this->OTHER_LABEL = _("Other Users' Folders");
-        $this->OTHER_KEY = $this->OTHER_LABEL . '%';
-
-        $this->init();
-    }
-
-    /**
-     * Do cleanup prior to serialization and provide a list of variables
-     * to serialize.
-     */
-    function __sleep()
-    {
-        /* Don't store $_expanded and $_poll - these values are handled
-         * by the subclasses.
-         * Don't store $_eltdiff - these needs to be regenerated for each
-         * request.
-         * Don't store $_currkey, $_currparent, and $_currstack since the
-         * user MUST call reset() before cycling through the tree.
-         * Don't store $_subscribed and $_fulllist - this information is
-         * stored in the elements.
-         * Reset the $_changed and $_trackdiff flags. */
-        $this->_currkey = $this->_currparent = $this->_eltdiff = $this->_expanded = $this->_fulllist = $this->_poll = $this->_subscribed = null;
-        $this->_currstack = array();
-        $this->_changed = false;
-        $this->_trackdiff = true;
-
-        return array_keys(get_class_vars(__CLASS__));
-    }
-
-    /**
-     * Store a serialized version of ourself in the current session.
-     */
-    function __destruct()
-    {
-        /* We only need to store the object if using Horde_Cache and the tree
-         * has changed. */
-        if (empty($this->_changed) ||
-            is_null($_SESSION['imp']['cache']['tree'])) {
-            return;
-        }
-
-        $imp_cache = IMP::getCache();
-        $imp_cache->set($_SESSION['imp']['cache']['tree'], serialize($this), 86400);
-    }
-
-    /**
-     * Returns the list of mailboxes on the server.
-     *
-     * @param boolean $showunsub  Show unsubscribed mailboxes?
-     *
-     * @return array  A list of mailbox names.
-     */
-    protected function _getList($showunsub)
-    {
-        if ($showunsub && !is_null($this->_fulllist)) {
-            return $this->_fulllist;
-        } elseif (!$showunsub && !is_null($this->_subscribed)) {
-            return array_keys($this->_subscribed);
-        }
-
-        /* INBOX must always appear. */
-        $names = array('INBOX');
-
-        foreach ($this->_namespaces as $key => $val) {
-            try {
-                $names = array_merge($names, $GLOBALS['imp_imap']->ob->listMailboxes($key . '*', $showunsub ? Horde_Imap_Client::MBOX_ALL : Horde_Imap_Client::MBOX_SUBSCRIBED, array('flat' => true)));
-                if ($showunsub) {
-                    $this->_fulllist = $names;
-                } else {
-                    $this->_subscribed = $names;
-                }
-            } catch (Horde_Imap_Client_Exception $e) {}
-        }
-
-        return $names;
-    }
-
-    /**
-     * Make a single mailbox tree element.
-     * An element consists of the following items (we use single letters here
-     * to save in session storage space):
-     *   'a'  --  Attributes
-     *   'c'  --  Level count
-     *   'l'  --  Label
-     *   'p'  --  Parent node
-     *   'v'  --  Value
-     *
-     * @param string $name         The mailbox name.
-     * @param integer $attributes  The mailbox's attributes.
-     *
-     * @return array  See above format.
-     */
-    protected function _makeElt($name, $attributes = 0)
-    {
-        $elt = array(
-            'a' => $attributes,
-            'c' => 0,
-            'p' => self::BASE_ELT,
-            'v' => $name
-        );
-
-        /* Set subscribed values. We know the folder is subscribed, without
-         * query of the IMAP server, in the following situations:
-         * + Folder is INBOX.
-         * + We are adding while in subscribe-only mode.
-         * + Subscriptions are turned off. */
-        if (!$this->isSubscribed($elt)) {
-            if (!$this->_showunsub ||
-                ($elt['v'] == 'INBOX') ||
-                !$GLOBALS['prefs']->getValue('subscribe')) {
-                $this->_setSubscribed($elt, true);
-            } else {
-                $this->_initSubscribed();
-                $this->_setSubscribed($elt, isset($this->_subscribed[$elt['v']]));
-            }
-        }
-
-        /* Check for polled status. */
-        $this->_initPollList();
-        $this->_setPolled($elt, isset($this->_poll[$elt['v']]));
-
-        /* Check for open status. */
-        switch ($GLOBALS['prefs']->getValue('nav_expanded')) {
-        case self::OPEN_NONE:
-            $open = false;
-            break;
-
-        case self::OPEN_ALL:
-            $open = true;
-            break;
-
-        case self::OPEN_USER:
-            $this->_initExpandedList();
-            $open = !empty($this->_expanded[$elt['v']]);
-            break;
-        }
-        $this->_setOpen($elt, $open);
-
-        /* Computed values. */
-        $ns_info = $this->_getNamespace($elt['v']);
-        $tmp = explode(is_null($ns_info) ? $this->_delimiter : $ns_info['delimiter'], $elt['v']);
-        $elt['c'] = count($tmp) - 1;
-
-        /* Get the mailbox label. */
-        $elt['l'] = IMP::getLabel($tmp[$elt['c']]);
-
-
-        if ($_SESSION['imp']['protocol'] != 'pop') {
-            if (!empty($GLOBALS['conf']['hooks']['display_folder'])) {
-                $this->_setInvisible($elt, !Horde::callHook('_imp_hook_display_folder', array($elt['v']), 'imp'));
-            }
-
-            if ($elt['c'] != 0) {
-                $elt['p'] = implode(is_null($ns_info) ? $this->_delimiter : $ns_info['delimiter'], array_slice($tmp, 0, $elt['c']));
-            }
-
-            if (!is_null($ns_info)) {
-                switch ($ns_info['type']) {
-                case 'personal':
-                    /* Strip personal namespace. */
-                    if (!empty($ns_info['name']) && ($elt['c'] != 0)) {
-                        --$elt['c'];
-                        if (strpos($elt['p'], $ns_info['delimiter']) === false) {
-                            $elt['p'] = self::BASE_ELT;
-                        } elseif (strpos($elt['v'], $ns_info['name'] . 'INBOX' . $ns_info['delimiter']) === 0) {
-                            $elt['p'] = 'INBOX';
-                        }
-                    }
-                    break;
-
-                case 'other':
-                case 'shared':
-                    if (substr($ns_info['name'], 0, -1 * strlen($ns_info['delimiter'])) == $elt['v']) {
-                        $elt['a'] = self::ELT_NOSELECT | self::ELT_NAMESPACE;
-                    }
-
-                    if ($GLOBALS['prefs']->getValue('tree_view')) {
-                        $name = ($ns_info['type'] == 'other') ? $this->OTHER_KEY : $this->SHARED_KEY;
-                        if ($elt['c'] == 0) {
-                            $elt['p'] = $name;
-                            ++$elt['c'];
-                        } elseif ($this->_tree[$name] && self::ELT_NOSHOW) {
-                            if ($elt['c'] == 1) {
-                                $elt['p'] = $name;
-                            }
-                        } else {
-                            ++$elt['c'];
-                        }
-                    }
-                    break;
-                }
-            }
-        }
-
-        return $elt;
-    }
-
-    /**
-     * Initalize the tree.
-     */
-    public function init()
-    {
-        $initmode = (($_SESSION['imp']['protocol'] == 'pop') ||
-                     !$GLOBALS['prefs']->getValue('subscribe') ||
-                     $_SESSION['imp']['showunsub'])
-            ? 'unsub' : 'sub';
-
-        /* Reset class variables to the defaults. */
-        $this->_changed = true;
-        $this->_currkey = $this->_currparent = $this->_subscribed = null;
-        $this->_currstack = $this->_tree = $this->_parent = array();
-        $this->_showunsub = $this->_unsubview = ($initmode == 'unsub');
-
-        /* Create a placeholder element to the base of the tree list so we can
-         * keep track of whether the base level needs to be sorted. */
-        $this->_tree[self::BASE_ELT] = array(
-            'a' => self::ELT_NEED_SORT,
-            'v' => self::BASE_ELT
-        );
-
-        if (empty($GLOBALS['conf']['user']['allow_folders']) ||
-            ($_SESSION['imp']['protocol'] == 'pop')) {
-            $this->_insertElt($this->_makeElt('INBOX', self::ELT_IS_SUBSCRIBED));
-            return;
-        }
-
-        /* Add namespace elements. */
-        foreach ($this->_namespaces as $key => $val) {
-            if ($val['type'] != 'personal' &&
-                $GLOBALS['prefs']->getValue('tree_view')) {
-                $elt = $this->_makeElt(
-                    ($val['type'] == 'other') ? $this->OTHER_KEY : $this->SHARED_KEY,
-                    self::ELT_NOSELECT | self::ELT_NAMESPACE | self::ELT_NONIMAP | self::ELT_NOSHOW
-                );
-                $elt['l'] = ($val['type'] == 'other')
-                    ? $this->OTHER_LABEL : $this->SHARED_LABEL;
-
-                foreach ($this->_namespaces as $val2) {
-                    if (($val2['type'] == $val['type']) &&
-                        ($val2['name'] != $val['name'])) {
-                        $elt['a'] &= ~self::ELT_NOSHOW;
-                        break;
-                    }
-                }
-
-                $this->_insertElt($elt);
-            }
-        }
-
-        /* Create the list (INBOX and all other hierarchies). */
-        $this->insert($this->_getList($this->_showunsub));
-
-        /* Add virtual folders to the tree. */
-        $this->insertVFolders($GLOBALS['imp_search']->listQueries(true));
-    }
-
-    /**
-     * Expand a mail folder.
-     *
-     * @param string $folder      The folder name to expand.
-     * @param boolean $expandall  Expand all folders under this one?
-     */
-    public function expand($folder, $expandall = false)
-    {
-        $folder = $this->_convertName($folder);
-
-        if (!isset($this->_tree[$folder])) {
-            return;
-        }
-        $elt = &$this->_tree[$folder];
-
-        if ($this->hasChildren($elt)) {
-            if (!$this->isOpen($elt)) {
-                $this->_changed = true;
-                $this->_setOpen($elt, true);
-            }
-
-            /* Expand all children beneath this one. */
-            if ($expandall && !empty($this->_parent[$folder])) {
-                foreach ($this->_parent[$folder] as $val) {
-                    $this->expand($this->_tree[$val]['v'], true);
-                }
-            }
-        }
-    }
-
-    /**
-     * Collapse a mail folder.
-     *
-     * @param string $folder  The folder name to collapse.
-     */
-    public function collapse($folder)
-    {
-        $folder = $this->_convertName($folder);
-
-        if (!isset($this->_tree[$folder])) {
-            return;
-        }
-
-        if ($this->isOpen($this->_tree[$folder])) {
-            $this->_changed = true;
-            $this->_setOpen($this->_tree[$folder], false);
-        }
-    }
-
-    /**
-     * Sets the internal array pointer to the next element, and returns the
-     * next object.
-     *
-     * @param integer $mask  A mask with the following elements:
-     * <pre>
-     * IMP_IMAP_Tree::NEXT_SHOWCLOSED - Don't ignore closed elements.
-     * IMP_IMAP_Tree::NEXT_SHOWSUB - Only show subscribed elements.
-     * </pre>
-     *
-     * @return mixed  Returns the next element or false if the element doesn't
-     *                exist.
-     */
-    public function next($mask = 0)
-    {
-        if (is_null($this->_currkey) && is_null($this->_currparent)) {
-            return false;
-        }
-
-        $curr = $this->current();
-
-        $old_showunsub = $this->_showunsub;
-        if ($mask & self::NEXT_SHOWSUB) {
-            $this->_showunsub = false;
-        }
-
-        if ($this->_activeElt($curr) &&
-            (($mask & self::NEXT_SHOWCLOSED) || $this->isOpen($curr)) &&
-            ($this->_currparent != $curr['v'])) {
-            /* If the current element is open, and children exist, move into
-             * it. */
-            $this->_currstack[] = array('k' => $this->_currkey, 'p' => $this->_currparent);
-            $this->_currkey = 0;
-            $this->_currparent = $curr['v'];
-            $this->_sortLevel($curr['v']);
-
-            $curr = $this->current();
-            if ($GLOBALS['prefs']->getValue('tree_view') &&
-                $this->isNamespace($curr) &&
-                !$this->_isNonIMAPElt($curr) &&
-                ($this->_tree[$curr['p']] && self::ELT_NOSHOW)) {
-                $this->next($mask);
-            }
-        } else {
-            /* Else, increment within the current subfolder. */
-            $this->_currkey++;
-        }
-
-        $curr = $this->current();
-        if (!$curr) {
-            if (empty($this->_currstack)) {
-                $this->_currkey = $this->_currparent = null;
-                $this->_showunsub = $old_showunsub;
-                return false;
-            } else {
-                do {
-                    $old = array_pop($this->_currstack);
-                    $this->_currkey = $old['k'] + 1;
-                    $this->_currparent = $old['p'];
-                } while ((($curr = $this->current()) == false) &&
-                         count($this->_currstack));
-            }
-        }
-
-        $res = $this->_activeElt($curr);
-        $this->_showunsub = $old_showunsub;
-        return ($res) ? $curr : $this->next($mask);
-    }
-
-    /**
-     * Set internal pointer to the head of the tree.
-     * This MUST be called before you can traverse the tree with next().
-     *
-     * @return mixed  Returns the element at the head of the tree or false
-     *                if the element doesn't exist.
-     */
-    public function reset()
-    {
-        $this->_currkey = 0;
-        $this->_currparent = self::BASE_ELT;
-        $this->_currstack = array();
-        $this->_sortLevel($this->_currparent);
-        return $this->current();
-    }
-
-    /**
-     * Return the current tree element.
-     *
-     * @return array  The current tree element or false if there is no
-     *                element.
-     */
-    public function current()
-    {
-        return (!isset($this->_parent[$this->_currparent][$this->_currkey]))
-            ? false
-            : $this->_tree[$this->_parent[$this->_currparent][$this->_currkey]];
-    }
-
-    /**
-     * Determines if there are more elements in the current tree level.
-     *
-     * @return boolean  True if there are more elements, false if this is the
-     *                  last element.
-     */
-    public function peek()
-    {
-        for ($i = ($this->_currkey + 1); ; ++$i) {
-            if (!isset($this->_parent[$this->_currparent][$i])) {
-                return false;
-            }
-            if ($this->_activeElt($this->_tree[$this->_parent[$this->_currparent][$i]])) {
-                return true;
-            }
-        }
-    }
-
-    /**
-     * Returns the requested element.
-     *
-     * @param string $name  The name of the tree element.
-     *
-     * @return array  Returns the requested element or false if not found.
-     */
-    public function get($name)
-    {
-        $name = $this->_convertName($name);
-        return (isset($this->_tree[$name])) ? $this->_tree[$name] : false;
-    }
-
-    /**
-     * Insert a folder/mailbox into the tree.
-     *
-     * @param mixed $id  The name of the folder (or a list of folder names)
-     *                   to add.
-     */
-    public function insert($id)
-    {
-        if (is_array($id)) {
-            /* We want to add from the BASE of the tree up for efficiency
-             * sake. */
-            $this->_sortList($id);
-        } else {
-            $id = array($id);
-        }
-
-        foreach ($id as $val) {
-            if (isset($this->_tree[$val]) &&
-                !$this->isContainer($this->_tree[$val])) {
-                continue;
-            }
-
-            $ns_info = $this->_getNamespace($val);
-            if (is_null($ns_info)) {
-                if (strpos($val, $this->VFOLDER_KEY . $this->_delimiter) === 0) {
-                    $elt = $this->_makeElt($this->VFOLDER_KEY, self::ELT_VFOLDER | self::ELT_NOSELECT | self::ELT_NONIMAP);
-                    $elt['l'] = $this->VFOLDER_LABEL;
-                    $this->_insertElt($elt);
-                }
-
-                $elt = $this->_makeElt($val, self::ELT_VFOLDER | self::ELT_IS_SUBSCRIBED);
-                $elt['l'] = $elt['v'] = String::substr($val, String::length($this->VFOLDER_KEY) + String::length($this->_delimiter));
-                $this->_insertElt($elt);
-            } else {
-                /* Break apart the name via the delimiter and go step by
-                 * step through the name to make sure all subfolders exist
-                 * in the tree. */
-                $parts = explode($ns_info['delimiter'], $val);
-                $parts[0] = $this->_convertName($parts[0]);
-                $parts_count = count($parts);
-                for ($i = 0; $i < $parts_count; ++$i) {
-                    $part = implode($ns_info['delimiter'], array_slice($parts, 0, $i + 1));
-
-                    if (isset($this->_tree[$part])) {
-                        if (($part == $val) &&
-                            $this->isContainer($this->_tree[$part])) {
-                            $this->_setContainer($this->_tree[$part], false);
-                        }
-                    } else {
-                        $this->_insertElt(($part == $val) ? $this->_makeElt($part) : $this->_makeElt($part, self::ELT_NOSELECT));
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Insert an element into the tree.
-     *
-     * @param array $elt  The element to insert. The key in the tree is the
-     *                    'v' (value) element of the element.
-     */
-    protected function _insertElt($elt)
-    {
-        if (!strlen($elt['l']) || isset($this->_tree[$elt['v']])) {
-            return;
-        }
-
-        // UW fix - it may return both 'foo' and 'foo/' as folder names.
-        // Only add one of these (without the namespace character) to
-        // the tree.  See Ticket #5764.
-        $ns_info = $this->_getNamespace($elt['v']);
-        if (isset($this->_tree[rtrim($elt['v'], is_null($ns_info) ? $this->_delimiter : $ns_info['delimiter'])])) {
-            return;
-        }
-
-        $this->_changed = true;
-
-        /* Set the parent array to the value in $elt['p']. */
-        if (empty($this->_parent[$elt['p']])) {
-            $this->_parent[$elt['p']] = array();
-            // This is a case where it is possible that the parent element has
-            // changed (it now has children) but we can't catch it via the
-            // bitflag (since hasChildren() is dynamically determined).
-            if ($this->_trackdiff && !is_null($this->_eltdiff)) {
-                $this->_eltdiff['c'][$elt['p']] = 1;
-            }
-        }
-        $this->_parent[$elt['p']][] = $elt['v'];
-        $this->_tree[$elt['v']] = $elt;
-
-        if ($this->_trackdiff && !is_null($this->_eltdiff)) {
-            $this->_eltdiff['a'][$elt['v']] = 1;
-        }
-
-        /* Make sure we are sorted correctly. */
-        if (count($this->_parent[$elt['p']]) > 1) {
-            $this->_setNeedSort($this->_tree[$elt['p']], true);
-        }
-    }
-
-    /**
-     * Delete an element from the tree.
-     *
-     * @param mixed $id  The element name or an array of element names.
-     *
-     * @return boolean  Return true on success, false on error.
-     */
-    public function delete($id)
-    {
-        if (is_array($id)) {
-            /* We want to delete from the TOP of the tree down to ensure that
-             * parents have an accurate view of what children are left. */
-            $this->_sortList($id);
-            $id = array_reverse($id);
-
-            $success = true;
-            foreach ($id as $val) {
-                $currsuccess = $this->delete($val);
-                if (!$currsuccess) {
-                    $success = false;
-                }
-            }
-            return $success;
-        } else {
-            $id = $this->_convertName($id, true);
-        }
-
-        $vfolder_base = ($id == $this->VFOLDER_LABEL);
-        $search_id = $GLOBALS['imp_search']->createSearchID($id);
-
-        if ($vfolder_base ||
-            (isset($this->_tree[$search_id]) &&
-             $this->isVFolder($this->_tree[$search_id]))) {
-            if (!$vfolder_base) {
-                $id = $search_id;
-
-            }
-            $parent = $this->_tree[$id]['p'];
-            unset($this->_tree[$id]);
-
-            /* Delete the entry from the parent tree. */
-            $key = array_search($id, $this->_parent[$parent]);
-            unset($this->_parent[$parent][$key]);
-
-            /* Rebuild the parent tree. */
-            if (!$vfolder_base && empty($this->_parent[$parent])) {
-                $this->delete($parent);
-            } else {
-                $this->_parent[$parent] = array_values($this->_parent[$parent]);
-            }
-            $this->_changed = true;
-
-            return true;
-        }
-
-        $ns_info = $this->_getNamespace($id);
-
-        if (($id == 'INBOX') ||
-            !isset($this->_tree[$id]) ||
-            ($id == $ns_info['name'])) {
-            return false;
-        }
-
-        $this->_changed = true;
-
-        $elt = &$this->_tree[$id];
-
-        /* Do not delete from tree if there are child elements - instead,
-         * convert to a container element. */
-        if ($this->hasChildren($elt)) {
-            $this->_setContainer($elt, true);
-            return true;
-        }
-
-        $parent = $elt['p'];
-
-        /* Delete the tree entry. */
-        unset($this->_tree[$id]);
-
-        /* Delete the entry from the parent tree. */
-        $key = array_search($id, $this->_parent[$parent]);
-        unset($this->_parent[$parent][$key]);
-
-        if (!is_null($this->_eltdiff)) {
-            $this->_eltdiff['d'][$id] = 1;
-        }
-
-        if (empty($this->_parent[$parent])) {
-            /* This folder is now completely empty (no children).  If the
-             * folder is a container only, we should delete the folder from
-             * the tree. */
-            unset($this->_parent[$parent]);
-            if (isset($this->_tree[$parent])) {
-                if ($this->isContainer($this->_tree[$parent]) &&
-                    !$this->isNamespace($this->_tree[$parent])) {
-                    $this->delete($parent);
-                } else {
-                    $this->_modifyExpandedList($parent, 'remove');
-                    $this->_setOpen($this->_tree[$parent], false);
-                    /* This is a case where it is possible that the parent
-                     * element has changed (it no longer has children) but
-                     * we can't catch it via the bitflag (since hasChildren()
-                     * is dynamically determined). */
-                    if (!is_null($this->_eltdiff)) {
-                        $this->_eltdiff['c'][$parent] = 1;
-                    }
-                }
-            }
-        } else {
-            /* Rebuild the parent tree. */
-            $this->_parent[$parent] = array_values($this->_parent[$parent]);
-        }
-
-        /* Remove the mailbox from the expanded folders list. */
-        $this->_modifyExpandedList($id, 'remove');
-
-        /* Remove the mailbox from the nav_poll list. */
-        $this->removePollList($id);
-
-        return true;
-    }
-
-    /**
-     * Subscribe an element to the tree.
-     *
-     * @param mixed $id  The element name or an array of element names.
-     */
-    public function subscribe($id)
-    {
-        if (!is_array($id)) {
-            $id = array($id);
-        }
-
-        foreach ($id as $val) {
-            $val = $this->_convertName($val);
-            if (isset($this->_tree[$val])) {
-                $this->_changed = true;
-                $this->_setSubscribed($this->_tree[$val], true);
-                $this->_setContainer($this->_tree[$val], false);
-            }
-        }
-    }
-
-    /**
-     * Unsubscribe an element from the tree.
-     *
-     * @param mixed $id  The element name or an array of element names.
-     */
-    public function unsubscribe($id)
-    {
-        if (!is_array($id)) {
-            $id = array($id);
-        } else {
-            /* We want to delete from the TOP of the tree down to ensure that
-             * parents have an accurate view of what children are left. */
-            $this->_sortList($id);
-            $id = array_reverse($id);
-        }
-
-        foreach ($id as $val) {
-            $val = $this->_convertName($val);
-
-            /* INBOX can never be unsubscribed to. */
-            if (isset($this->_tree[$val]) && ($val != 'INBOX')) {
-                $this->_changed = $this->_unsubview = true;
-
-                $elt = &$this->_tree[$val];
-
-                /* Do not delete from tree if there are child elements -
-                 * instead, convert to a container element. */
-                if (!$this->_showunsub && $this->hasChildren($elt)) {
-                    $this->_setContainer($elt, true);
-                }
-
-                /* Set as unsubscribed, add to unsubscribed list, and remove
-                 * from subscribed list. */
-                $this->_setSubscribed($elt, false);
-            }
-        }
-    }
-
-    /**
-     * Set an attribute for an element.
-     *
-     * @param array &$elt     The tree element.
-     * @param integer $const  The constant to set/remove from the bitmask.
-     * @param boolean $bool   Should the attribute be set?
-     */
-    protected function _setAttribute(&$elt, $const, $bool)
-    {
-        if ($bool) {
-            $elt['a'] |= $const;
-        } else {
-            $elt['a'] &= ~$const;
-        }
-    }
-
-    /**
-     * Does the element have any active children?
-     *
-     * @param array $elt  A tree element.
-     *
-     * @return boolean  True if the element has active children.
-     */
-    public function hasChildren($elt)
-    {
-        if (isset($this->_parent[$elt['v']])) {
-            if ($this->_showunsub) {
-                return true;
-            }
-
-            foreach ($this->_parent[$elt['v']] as $val) {
-                $child = &$this->_tree[$val];
-                if ($this->isSubscribed($child) ||
-                    $this->hasChildren($this->_tree[$val])) {
-                    return true;
-                }
-            }
-        }
-
-        return false;
-    }
-
-    /**
-     * Is the tree element open?
-     *
-     * @param array $elt  A tree element.
-     *
-     * @return integer  True if the element is open.
-     */
-    public function isOpen($elt)
-    {
-        return (($elt['a'] & self::ELT_IS_OPEN) && $this->hasChildren($elt));
-    }
-
-    /**
-     * Set the open attribute for an element.
-     *
-     * @param array &$elt    A tree element.
-     * @param boolean $bool  The setting.
-     */
-    protected function _setOpen(&$elt, $bool)
-    {
-        $this->_setAttribute($elt, self::ELT_IS_OPEN, $bool);
-        $this->_modifyExpandedList($elt['v'], $bool ? 'add' : 'remove');
-    }
-
-    /**
-     * Is this element a container only, not a mailbox (meaning you can
-     * not open it)?
-     *
-     * @param array $elt  A tree element.
-     *
-     * @return integer  True if the element is a container.
-     */
-    public function isContainer($elt)
-    {
-        return (($elt['a'] & self::ELT_NOSELECT) ||
-                (!$this->_showunsub &&
-                 !$this->isSubscribed($elt) &&
-                 $this->hasChildren($elt)));
-    }
-
-    /**
-     * Set the element as a container?
-     *
-     * @param array &$elt    A tree element.
-     * @param boolean $bool  Is the element a container?
-     */
-    protected function _setContainer(&$elt, $bool)
-    {
-        $this->_setAttribute($elt, self::ELT_NOSELECT, $bool);
-    }
-
-    /**
-     * Is the user subscribed to this element?
-     *
-     * @param array $elt  A tree element.
-     *
-     * @return integer  True if the user is subscribed to the element.
-     */
-    public function isSubscribed($elt)
-    {
-        return $elt['a'] & self::ELT_IS_SUBSCRIBED;
-    }
-
-    /**
-     * Set the subscription status for an element.
-     *
-     * @param array &$elt    A tree element.
-     * @param boolean $bool  Is the element subscribed to?
-     */
-    protected function _setSubscribed(&$elt, $bool)
-    {
-        $this->_setAttribute($elt, self::ELT_IS_SUBSCRIBED, $bool);
-        if (!is_null($this->_subscribed)) {
-            if ($bool) {
-                $this->_subscribed[$elt['v']] = 1;
-            } else {
-                unset($this->_subscribed[$elt['v']]);
-            }
-        }
-    }
-
-    /**
-     * Is the element a namespace container?
-     *
-     * @param array $elt  A tree element.
-     *
-     * @return integer  True if the element is a namespace container.
-     */
-    public function isNamespace($elt)
-    {
-        return $elt['a'] & self::ELT_NAMESPACE;
-    }
-
-    /**
-     * Is the element a non-IMAP element?
-     *
-     * @param array $elt  A tree element.
-     *
-     * @return integer  True if the element is a non-IMAP element.
-     */
-    protected function _isNonIMAPElt($elt)
-    {
-        return $elt['a'] & self::ELT_NONIMAP;
-    }
-
-    /**
-     * Initialize the expanded folder list.
-     */
-    protected function _initExpandedList()
-    {
-        if (is_null($this->_expanded)) {
-            $serialized = $GLOBALS['prefs']->getValue('expanded_folders');
-            $this->_expanded = ($serialized) ? unserialize($serialized) : array();
-        }
-    }
-
-    /**
-     * Add/remove an element to the expanded list.
-     *
-     * @param string $id      The element name to add/remove.
-     * @param string $action  Either 'add' or 'remove';
-     */
-    protected function _modifyExpandedList($id, $action)
-    {
-        $this->_initExpandedList();
-        if ($action == 'add') {
-            $this->_expanded[$id] = true;
-        } else {
-            unset($this->_expanded[$id]);
-        }
-        $GLOBALS['prefs']->setValue('expanded_folders', serialize($this->_expanded));
-    }
-
-    /**
-     * Initializes and returns the list of mailboxes to poll.
-     *
-     * @param boolean $prune  Prune non-existant folders from list?
-     * @param boolean $sort   Sort the directory list?
-     *
-     * @return array  The list of mailboxes to poll.
-     */
-    public function getPollList($prune = false, $sort = false)
-    {
-        $this->_initPollList();
-
-        $plist = ($prune) ? array_values(array_intersect(array_keys($this->_poll), $this->folderList())) : $this->_poll;
-        if ($sort) {
-            $ns_new = $this->_getNamespace(null);
-            Horde_Imap_Client_Sort::sortMailboxes($plist, array('delimiter' => $ns_new['delimiter'], 'inbox' => true));
-        }
-
-        return $plist;
-    }
-
-    /**
-     * Init the poll list.  Called once per session.
-     */
-    protected function _initPollList()
-    {
-        if (is_null($this->_poll)) {
-            /* We ALWAYS poll the INBOX. */
-            $this->_poll = array('INBOX' => 1);
-
-            /* Add the list of polled mailboxes from the prefs. */
-            if ($GLOBALS['prefs']->getValue('nav_poll_all')) {
-                $navPollList = array_flip($this->_getList(true));
-            } else {
-                $old_error = error_reporting(0);
-                $navPollList = unserialize($GLOBALS['prefs']->getValue('nav_poll'));
-                error_reporting($old_error);
-            }
-            if ($navPollList) {
-                $this->_poll += $navPollList;
-            }
-        }
-    }
-
-    /**
-     * Add element to the poll list.
-     *
-     * @param mixed $id  The element name or a list of element names to add.
-     */
-    public function addPollList($id)
-    {
-        if (!is_array($id)) {
-            $id = array($id);
-        }
-
-        if (!empty($id) && !$GLOBALS['prefs']->isLocked('nav_poll')) {
-            $imp_folder = IMP_Folder::singleton();
-            $this->getPollList();
-            foreach ($id as $val) {
-                if (!$this->isSubscribed($this->_tree[$val])) {
-                    $imp_folder->subscribe(array($val));
-                }
-                $this->_poll[$val] = true;
-                $this->_setPolled($this->_tree[$val], true);
-            }
-            $GLOBALS['prefs']->setValue('nav_poll', serialize($this->_poll));
-            $this->_changed = true;
-        }
-    }
-
-    /**
-     * Remove element from the poll list.
-     *
-     * @param string $id  The folder/mailbox or a list of folders/mailboxes
-     *                    to remove.
-     */
-    public function removePollList($id)
-    {
-        if (!is_array($id)) {
-            $id = array($id);
-        }
-
-        $removed = false;
-
-        if (!$GLOBALS['prefs']->isLocked('nav_poll')) {
-            $this->getPollList();
-            foreach ($id as $val) {
-                if ($val != 'INBOX') {
-                    unset($this->_poll[$val]);
-                    if (isset($this->_tree[$val])) {
-                        $this->_setPolled($this->_tree[$val], false);
-                    }
-                    $removed = true;
-                }
-            }
-            if ($removed) {
-                $GLOBALS['prefs']->setValue('nav_poll', serialize($this->_poll));
-                $this->_changed = true;
-            }
-        }
-    }
-
-    /**
-     * Does the user want to poll this mailbox for new/unseen messages?
-     *
-     * @param array $elt  A tree element.
-     *
-     * @return integer  True if the user wants to poll the element.
-     */
-    public function isPolled($elt)
-    {
-        return ($GLOBALS['prefs']->getValue('nav_poll_all')) ? true : ($elt['a'] & self::ELT_IS_POLLED);
-    }
-
-    /**
-     * Set the polled attribute for an element.
-     *
-     * @param array &$elt    A tree element.
-     * @param boolean $bool  The setting.
-     */
-    protected function _setPolled(&$elt, $bool)
-    {
-        $this->_setAttribute($elt, self::ELT_IS_POLLED, $bool);
-    }
-
-    /**
-     * Is the element invisible?
-     *
-     * @param array $elt  A tree element.
-     *
-     * @return integer  True if the element is marked as invisible.
-     */
-    public function isInvisible($elt)
-    {
-        return $elt['a'] & self::ELT_INVISIBLE;
-    }
-
-    /**
-     * Set the invisible attribute for an element.
-     *
-     * @param array &$elt    A tree element.
-     * @param boolean $bool  The setting.
-     */
-    protected function _setInvisible(&$elt, $bool)
-    {
-        $this->_setAttribute($elt, self::ELT_INVISIBLE, $bool);
-    }
-
-    /**
-     * Flag the element as needing its children to be sorted.
-     *
-     * @param array &$elt    A tree element.
-     * @param boolean $bool  The setting.
-     */
-    protected function _setNeedSort(&$elt, $bool)
-    {
-        $this->_setAttribute($elt, self::ELT_NEED_SORT, $bool);
-    }
-
-    /**
-     * Does this element's children need sorting?
-     *
-     * @param array $elt  A tree element.
-     *
-     * @return integer  True if the children need to be sorted.
-     */
-    protected function _needSort($elt)
-    {
-        return (($elt['a'] & self::ELT_NEED_SORT) && (count($this->_parent[$elt['v']]) > 1));
-    }
-
-    /**
-     * Initialize the list of subscribed mailboxes.
-     */
-    protected function _initSubscribed()
-    {
-        if (is_null($this->_subscribed)) {
-            $this->_getList(false);
-        }
-    }
-
-    /**
-     * Should we expand all elements?
-     */
-    public function expandAll()
-    {
-        foreach ($this->_parent[self::BASE_ELT] as $val) {
-            $this->expand($val, true);
-        }
-    }
-
-    /**
-     * Should we collapse all elements?
-     */
-    public function collapseAll()
-    {
-        foreach ($this->_tree as $key => $val) {
-            if ($key !== self::BASE_ELT) {
-                $this->collapse($val['v']);
-            }
-        }
-    }
-
-    /**
-     * Switch subscribed/unsubscribed viewing.
-     *
-     * @param boolean $unsub  Show unsubscribed elements?
-     */
-    public function showUnsubscribed($unsub)
-    {
-        if ((bool)$unsub === $this->_showunsub) {
-            return;
-        }
-
-        $this->_showunsub = $unsub;
-        $this->_changed = true;
-
-        /* If we are switching from unsubscribed to subscribed, no need
-         * to do anything (we just ignore unsubscribed stuff). */
-        if ($unsub === false) {
-            return;
-        }
-
-        /* If we are switching from subscribed to unsubscribed, we need
-         * to add all unsubscribed elements that live in currently
-         * discovered items. */
-        $this->_unsubview = true;
-        $this->_trackdiff = false;
-        $this->insert($this->_getList(true));
-        $this->_trackdiff = true;
-    }
-
-    /**
-     * Get information about new/unseen/total messages for the given element.
-     *
-     * @param string $name  The element name (UTF7-IMAP).
-     *
-     * @return array  Array with the following fields:
-     * <pre>
-     * 'messages' - (integer) Number of total messages.
-     * 'recent' - (integer) Number of new messages.
-     * 'unseen' - (integer) Number of unseen messages.
-     * </pre>
-     */
-    public function getElementInfo($name)
-    {
-        try {
-            return $GLOBALS['imp_imap']->ob->status($name, Horde_Imap_Client::STATUS_MESSAGES | Horde_Imap_Client::STATUS_RECENT | Horde_Imap_Client::STATUS_UNSEEN);
-        } catch (Horde_Imap_Client_Exception $e) {
-            return array();
-        }
-    }
-
-    /**
-     * Sorts a list of mailboxes.
-     *
-     * @param array &$mbox   The list of mailboxes to sort.
-     * @param boolean $base  Are we sorting a list of mailboxes in the base
-     *                       of the tree.
-     */
-    protected function _sortList(&$mbox, $base = false)
-    {
-        $config_array = array(
-            'delimiter' => $this->_delimiter,
-            'inbox' => true
-        );
-
-        if ($base) {
-            $basesort = array();
-            foreach ($mbox as $val) {
-                $basesort[$val] = ($val == 'INBOX') ? 'INBOX' : $this->_tree[$val]['l'];
-            }
-            $config_array['index'] = true;
-            Horde_Imap_Client_Sort::sortMailboxes($basesort, $config_array);
-            $mbox = array_keys($basesort);
-        } else {
-            Horde_Imap_Client_Sort::sortMailboxes($mbox, $config_array);
-        }
-
-        if ($base) {
-            for ($i = 0, $count = count($mbox); $i < $count; ++$i) {
-                if ($this->_isNonIMAPElt($this->_tree[$mbox[$i]])) {
-                    /* Already sorted by name - simply move to the end of
-                     * the array. */
-                    $mbox[] = $mbox[$i];
-                    unset($mbox[$i]);
-                }
-            }
-            $mbox = array_values($mbox);
-        }
-    }
-
-    /**
-     * Is the given element an "active" element (i.e. an element that should
-     * be worked with given the current viewing parameters).
-     *
-     * @param array $elt  A tree element.
-     *
-     * @return boolean  True if it is an active element.
-     */
-    protected function _activeElt($elt)
-    {
-        return (!$this->isInvisible($elt) &&
-                ($this->_showunsub ||
-                 ($this->isSubscribed($elt) && !$this->isContainer($elt)) ||
-                 $this->hasChildren($elt)));
-    }
-
-    /**
-     * Convert a mailbox name to the correct, internal name (i.e. make sure
-     * INBOX is always capitalized for IMAP servers).
-     *
-     * @param string $name  The mailbox name.
-     *
-     * @return string  The converted name.
-     */
-    protected function _convertName($name)
-    {
-        return (strcasecmp($name, 'INBOX') == 0) ? 'INBOX' : $name;
-    }
-
-    /**
-     * Get namespace info for a full folder path.
-     *
-     * @param string $mailbox  The folder path.
-     *
-     * @return mixed  The namespace info for the folder path or null if the
-     *                path doesn't exist.
-     */
-    protected function _getNamespace($mailbox)
-    {
-        if (!in_array($mailbox, array($this->OTHER_KEY, $this->SHARED_KEY, $this->VFOLDER_KEY)) &&
-            (strpos($mailbox, $this->VFOLDER_KEY . $this->_delimiter) !== 0)) {
-            return $GLOBALS['imp_imap']->getNamespace($mailbox);
-        }
-        return null;
-    }
-
-    /**
-     * Set the start point for determining element differences via eltDiff().
-     */
-    public function eltDiffStart()
-    {
-        $this->_eltdiff = array('a' => array(), 'c' => array(), 'd' => array());
-    }
-
-    /**
-     * Return the list of elements that have changed since eltDiffStart()
-     * was last called.
-     *
-     * @return array  Returns false if no changes have occurred, or an array
-     *                with the following keys:
-     * <pre>
-     * 'a' => A list of elements that have been added.
-     * 'c' => A list of elements that have been changed.
-     * 'd' => A list of elements that have been deleted.
-     * </pre>
-     */
-    public function eltDiff()
-    {
-        if (is_null($this->_eltdiff) || !$this->_changed) {
-            return false;
-        }
-
-        $ret = array(
-            'a' => array_keys($this->_eltdiff['a']),
-            'c' => array_keys($this->_eltdiff['c']),
-            'd' => array_keys($this->_eltdiff['d'])
-        );
-
-        $this->_eltdiff = null;
-
-        return $ret;
-    }
-
-    /**
-     * Inserts virtual folders into the tree.
-     *
-     * @param array $id_list  An array with the folder IDs to add as the key
-     *                        and the labels as the value.
-     */
-    public function insertVFolders($id_list)
-    {
-        if (empty($id_list) ||
-            empty($GLOBALS['conf']['user']['allow_folders'])) {
-            return;
-        }
-
-        $adds = $id = array();
-
-        foreach ($id_list as $key => $val) {
-            $id[$GLOBALS['imp_search']->createSearchID($key)] = $val;
-        }
-
-        foreach (array_keys($id) as $key) {
-            $id_key = $this->VFOLDER_KEY . $this->_delimiter . $key;
-            if (!isset($this->_tree[$id_key])) {
-                $adds[] = $id_key;
-            }
-        }
-
-        if (empty($adds)) {
-            return;
-        }
-
-        $this->insert($adds);
-
-        foreach ($id as $key => $val) {
-            $this->_tree[$key]['l'] = $val;
-        }
-
-        /* Sort the Virtual Folder list in the object, if necessary. */
-        if ($this->_needSort($this->_tree[$this->VFOLDER_KEY])) {
-            $vsort = array();
-            foreach ($this->_parent[$this->VFOLDER_KEY] as $val) {
-                $vsort[$val] = $this->_tree[$val]['l'];
-            }
-            natcasesort($vsort);
-            $this->_parent[$this->VFOLDER_KEY] = array_keys($vsort);
-            $this->_setNeedSort($this->_tree[$this->VFOLDER_KEY], false);
-            $this->_changed = true;
-        }
-    }
-
-    /**
-     * Builds a list of folders, suitable to render a folder tree.
-     *
-     * @param integer $mask  The mask to pass to next().
-     * @param boolean $open  If using the base folder icons, display a
-     *                       different icon whether the folder is opened or
-     *                       closed.
-     *
-     * @return array  An array with three elements: the folder list, the total
-     *                number of new messages, and a list with folder names
-     *                suitable for user interaction.
-     *                The folder list array contains the following added
-     *                entries on top of the entries provided by element():
-     * <pre>
-     * 'display' - The mailbox name run through IMP::displayFolder().
-     * 'peek' - See peek().
-     * </pre>
-     */
-    public function build($mask = 0, $open = true)
-    {
-        $displayNames = $newmsgs = $rows = array();
-        $this->_forceopen = $open;
-
-        /* Start iterating through the list of mailboxes, displaying them. */
-        $mailbox = $this->reset();
-        do {
-            $row = $this->element($mailbox['v']);
-
-            $row['display'] = ($this->_isNonIMAPElt($mailbox)) ? $mailbox['l'] : IMP::displayFolder($mailbox['v']);
-            $row['peek'] = $this->peek();
-
-            if (!empty($row['recent'])) {
-                $newmsgs[$row['value']] = $row['recent'];
-            }
-
-            /* Hide folder prefixes from the user. */
-            if ($row['level'] >= 0) {
-                $rows[] = $row;
-                $displayNames[] = addslashes($row['display']);
-            }
-        } while (($mailbox = $this->next($mask)));
-
-        $this->_forceopen = false;
-
-        return array($rows, $newmsgs, $displayNames);
-    }
-
-    /**
-     * Get any custom icon configured for the given element.
-     *
-     * @params array $elt  A tree element.
-     *
-     * @return array  An array with the 'icon', 'icondir', and 'alt'
-     *                information for the element, or false if no icon
-     *                available.
-     */
-    public function getCustomIcon($elt)
-    {
-        static $mbox_icons;
-
-        if (isset($mbox_icons) && !$mbox_icons) {
-            return false;
-        }
-
-        /* Call the mailbox icon hook, if requested. */
-        if (empty($GLOBALS['conf']['hooks']['mbox_icon'])) {
-            $mbox_icons = false;
-            return false;
-        }
-
-        if (!isset($mbox_icons)) {
-            $mbox_icons = Horde::callHook('_imp_hook_mbox_icons', array(), 'imp', false);
-            if (!$mbox_icons) {
-                return false;
-            }
-        }
-
-        if (isset($mbox_icons[$elt['v']])) {
-            return array('icon' => $mbox_icons[$elt['v']]['icon'], 'icondir' => $mbox_icons[$elt['v']]['icondir'], 'alt' => $mbox_icons[$elt['v']]['alt']);
-        }
-
-        return false;
-    }
-
-    /**
-     * Returns whether this element is a virtual folder.
-     *
-     * @param array $elt  A tree element.
-     *
-     * @return integer  True if the element is a virtual folder.
-     */
-    public function isVFolder($elt)
-    {
-        return $elt['a'] & self::ELT_VFOLDER;
-    }
-
-    /**
-     * Rename a current folder.
-     *
-     * @param array $old  The old folder names.
-     * @param array $new  The new folder names.
-     */
-    public function rename($old, $new)
-    {
-        foreach ($old as $key => $val) {
-            $polled = (isset($this->_tree[$val])) ? $this->isPolled($this->_tree[$val]) : false;
-            if ($this->delete($val)) {
-                $this->insert($new[$key]);
-                if ($polled) {
-                    $this->addPollList($new[$key]);
-                }
-            }
-        }
-    }
-
-    /**
-     * Returns a list of all IMAP mailboxes in the tree.
-     *
-     * @param integer $mask  A mask with the following elements:
-     * <pre>
-     * IMP_IMAP_Tree::FLIST_CONTAINER - Show container elements.
-     * IMP_IMAP_Tree::FLIST_UNSUB - Show unsubscribed elements.
-     * IMP_IMAP_Tree::FLIST_OB - Return full tree object.
-     * IMP_IMAP_Tree::FLIST_VFOLDER - Show Virtual Folders.
-     * </pre>
-     * @param string $base  Return all mailboxes below this element.
-     *
-     * @return array  An array of IMAP mailbox names.
-     */
-    public function folderList($mask = 0, $base = null)
-    {
-        $baseindex = null;
-        $ret_array = array();
-
-        $diff_unsub = (($mask & self::FLIST_UNSUB) != $this->_showunsub) ? $this->_showunsub : null;
-        $this->showUnsubscribed($mask & self::FLIST_UNSUB);
-
-        $mailbox = $this->reset();
-
-        // Search to base element.
-        if (!is_null($base)) {
-            while ($mailbox && $mailbox['v'] != $base) {
-                $mailbox = $this->next(self::NEXT_SHOWCLOSED);
-            }
-            if ($mailbox) {
-                $baseindex = count($this->_currstack);
-                $baseparent = $this->_currparent;
-                $basekey = $this->_currkey;
-                $mailbox = $this->next(self::NEXT_SHOWCLOSED);
-            }
-        }
-
-        if ($mailbox) {
-            do {
-                if (!is_null($baseindex) &&
-                    (!isset($this->_currstack[$baseindex]) ||
-                     ($this->_currstack[$baseindex]['k'] != $basekey) ||
-                     ($this->_currstack[$baseindex]['p'] != $baseparent))) {
-                    break;
-                }
-
-                if ((($mask & self::FLIST_CONTAINER) ||
-                     !$this->isContainer($mailbox)) &&
-                    (($mask & self::FLIST_VFOLDER) ||
-                     !$this->isVFolder($mailbox))) {
-                    $ret_array[] = ($mask & self::FLIST_OB) ? $mailbox : $mailbox['v'];
-                }
-            } while (($mailbox = $this->next(self::NEXT_SHOWCLOSED)));
-        }
-
-        if (!is_null($diff_unsub)) {
-            $this->showUnsubscribed($diff_unsub);
-        }
-
-        return $ret_array;
-    }
-
-    /**
-     * Is the mailbox open in the sidebar?
-     *
-     * @param array $mbox  A mailbox name.
-     *
-     * @return integer  True if the mailbox is open in the sidebar.
-     */
-    public function isOpenSidebar($mbox)
-    {
-        switch ($GLOBALS['prefs']->getValue('nav_expanded_sidebar')) {
-        case self::OPEN_USER:
-            $this->_initExpandedList();
-            return !empty($this->_expanded[$mbox]);
-            break;
-
-        case self::OPEN_ALL:
-            return true;
-            break;
-
-        case self::OPEN_NONE:
-        default:
-            return false;
-            break;
-        }
-    }
-
-    /**
-     * Init frequently used element() data.
-     */
-    protected function _initElement()
-    {
-        global $prefs, $registry;
-
-        /* Initialize the user's identities. */
-        require_once 'Horde/Identity.php';
-        $identity = Identity::singleton(array('imp', 'imp'));
-
-        return array(
-            'trash' => IMP::folderPref($prefs->getValue('trash_folder'), true),
-            'draft' => IMP::folderPref($prefs->getValue('drafts_folder'), true),
-            'spam' => IMP::folderPref($prefs->getValue('spam_folder'), true),
-            'sent' => $identity->getAllSentmailFolders(),
-            'image_dir' => $registry->getImageDir(),
-        );
-    }
-
-    /**
-     * Return extended information on an element.
-     *
-     * @param string $name  The name of the tree element.
-     *
-     * @return array  Returns the element with extended information, or false
-     *                if not found.  The information returned is as follows:
-     * <pre>
-     * 'alt' - The alt text for the icon.
-     * 'base_elt' - The return from get().
-     * 'children' - Does the element have children?
-     * 'container' - Is this a container element?
-     * 'editvfolder' - Can this virtual folder be edited?
-     * 'icon' - The name of the icon graphic to use.
-     * 'icondir' - The path of the icon directory.
-     * 'level' - The deepness level of this element.
-     * 'mbox_val' - A html-ized version of 'value'.
-     * 'msgs' - The number of total messages in the element (if polled).
-     * 'name' - A html-ized version of 'label'.
-     * 'parent' - The parent element value.
-     * 'polled' - Show polled information?
-     * 'recent' - The number of new messages in the element (if polled).
-     * 'special' - An integer mask indicating if this is a "special" element.
-     * 'specialvfolder' - Is this a "special" virtual folder?
-     * 'unseen' - The number of unseen messages in the element (if polled).
-     * 'user_icon' - Use a user defined icon?
-     * 'value' - The value of this element (i.e. element id).
-     * 'vfolder' - Is this a virtual folder?
-     * </pre>
-     */
-    public function element($mailbox)
-    {
-        static $elt;
-
-        $mailbox = $this->get($mailbox);
-        if (!$mailbox) {
-            return false;
-        }
-
-        if (!isset($elt)) {
-            $elt = $this->_initElement();
-        }
-
-        $row = array(
-            'base_elt' => $mailbox,
-            'children' => $this->hasChildren($mailbox),
-            'container' => false,
-            'editvfolder' => false,
-            'icondir' => $elt['image_dir'],
-            'iconopen' => null,
-            'level' => $mailbox['c'],
-            'mbox_val' => htmlspecialchars($mailbox['v']),
-            'name' => htmlspecialchars($mailbox['l']),
-            'parent' => $mailbox['p'],
-            'polled' => false,
-            'recent' => 0,
-            'special' => 0,
-            'specialvfolder' => false,
-            'user_icon' => false,
-            'value' => $mailbox['v'],
-            'vfolder' => false,
-        );
-
-        $icon = $this->getCustomIcon($mailbox);
-
-        if (!$this->isContainer($mailbox)) {
-            /* We are dealing with mailboxes here.
-             * Determine if we need to poll this mailbox for new messages. */
-            if ($this->isPolled($mailbox)) {
-                /* If we need message information for this folder, update
-                 * it now. */
-                $msgs_info = $this->getElementInfo($mailbox['v']);
-                if (!empty($msgs_info)) {
-                    $row['polled'] = true;
-                    if (!empty($msgs_info['recent'])) {
-                        $row['recent'] = $msgs_info['recent'];
-                    }
-                    $row['msgs'] = $msgs_info['messages'];
-                    $row['unseen'] = $msgs_info['unseen'];
-                }
-            }
-
-
-            switch ($mailbox['v']) {
-            case 'INBOX':
-                $row['icon'] = 'folders/inbox.png';
-                $row['alt'] = _("Inbox");
-                $row['special'] = self::SPECIAL_INBOX;
-                break;
-
-            case $elt['trash']:
-                if ($GLOBALS['prefs']->getValue('use_vtrash')) {
-                    $row['icon'] = ($this->isOpen($mailbox)) ? 'folders/folder_open.png' : 'folders/folder.png';
-                    $row['alt'] = _("Mailbox");
-                } else {
-                    $row['icon'] = 'folders/trash.png';
-                    $row['alt'] = _("Trash folder");
-                    $row['special'] = self::SPECIAL_TRASH;
-                }
-                break;
-
-            case $elt['draft']:
-                $row['icon'] = 'folders/drafts.png';
-                $row['alt'] = _("Draft folder");
-                $row['special'] = self::SPECIAL_DRAFT;
-                break;
-
-            case $elt['spam']:
-                $row['icon'] = 'folders/spam.png';
-                $row['alt'] = _("Spam folder");
-                $row['special'] = self::SPECIAL_SPAM;
-                break;
-
-            default:
-                if (in_array($mailbox['v'], $elt['sent'])) {
-                    $row['icon'] = 'folders/sent.png';
-                    $row['alt'] = _("Sent mail folder");
-                    $row['special'] = self::SPECIAL_SENT;
-                } else {
-                    $row['icon'] = ($this->isOpen($mailbox)) ? 'folders/folder_open.png' : 'folders/folder.png';
-                    $row['alt'] = _("Mailbox");
-                }
-                break;
-            }
-
-            /* Virtual folders. */
-            if ($this->isVFolder($mailbox)) {
-                $row['vfolder'] = true;
-                $row['editvfolder'] = $GLOBALS['imp_search']->isEditableVFolder($mailbox['v']);
-                if ($GLOBALS['imp_search']->isVTrashFolder($mailbox['v'])) {
-                    $row['specialvfolder'] = true;
-                    $row['icon'] = 'folders/trash.png';
-                    $row['alt'] = _("Virtual Trash Folder");
-                } elseif ($GLOBALS['imp_search']->isVINBOXFolder($mailbox['v'])) {
-                    $row['specialvfolder'] = true;
-                    $row['icon'] = 'folders/inbox.png';
-                    $row['alt'] = _("Virtual INBOX Folder");
-                }
-            }
-        } else {
-            /* We are dealing with folders here. */
-            $row['container'] = true;
-            if ($this->_forceopen && $this->isOpen($mailbox)) {
-                $row['icon'] = 'folders/folder_open.png';
-                $row['alt'] = _("Opened Folder");
-            } else {
-                $row['icon'] = 'folders/folder.png';
-                $row['iconopen'] = 'folders/folder_open.png';
-                $row['alt'] = ($this->_forceopen) ? _("Closed Folder") : _("Folder");
-            }
-            if ($this->isVFolder($mailbox)) {
-                $row['vfolder'] = true;
-            }
-        }
-
-        /* Overwrite the icon information now. */
-        if (!empty($icon)) {
-            $row['icon'] = $icon['icon'];
-            $row['icondir'] = $icon['icondir'];
-            if (!empty($icon['alt'])) {
-                $row['alt'] = $icon['alt'];
-            }
-            $row['iconopen'] = isset($icon['iconopen']) ? $icon['iconopen'] : null;
-            $row['user_icon'] = true;
-        }
-
-        return $row;
-    }
-
-    /**
-     * Sort a level in the tree.
-     *
-     * @param string $id  The parent folder whose children need to be sorted.
-     */
-    protected function _sortLevel($id)
-    {
-        if ($this->_needSort($this->_tree[$id])) {
-            $this->_sortList($this->_parent[$id], ($id === self::BASE_ELT));
-            $this->_setNeedSort($this->_tree[$id], false);
-            $this->_changed = true;
-        }
-    }
-
-    /**
-     * Determines the mailbox name to create given a parent and the new name.
-     *
-     * @param string $parent  The parent name (UTF7-IMAP).
-     * @param string $parent  The new mailbox name (UTF7-IMAP).
-     *
-     * @return string  The full path to the new mailbox.
-     * @throws Horde_Exception
-     */
-    public function createMailboxName($parent, $new)
-    {
-        $ns_info = (empty($parent)) ? $GLOBALS['imp_imap']->defaultNamespace() : $this->_getNamespace($parent);
-        if (is_null($ns_info)) {
-            if ($this->isNamespace($this->_tree[$parent])) {
-                $ns_info = $this->_getNamespace($new);
-                if (in_array($ns_info['type'], array('other', 'shared'))) {
-                    return $new;
-                }
-            }
-            throw new Horde_Exception(_("Cannot directly create mailbox in this folder."), 'horde.error');
-        }
-
-        $mbox = $ns_info['name'];
-        if (!empty($parent)) {
-            $mbox .= substr_replace($parent, '', 0, strlen($ns_info['name']));
-            $mbox = rtrim($mbox, $ns_info['delimiter']) . $ns_info['delimiter'];
-        }
-        return $mbox . $new;
-    }
-}
diff --git a/imp/lib/Imap.php b/imp/lib/Imap.php
new file mode 100644 (file)
index 0000000..7eb8d9d
--- /dev/null
@@ -0,0 +1,342 @@
+<?php
+/**
+ * The IMP_Imap:: class provides common functions for interaction with the
+ * IMAP/POP3 server via the Horde_Imap_Client:: library.
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author  Michael Slusarz <slusarz@horde.org>
+ * @package IMP
+ */
+class IMP_Imap
+{
+    /**
+     * The Horde_Imap_Client object.
+     *
+     * @var Horde_Imap_Client
+     */
+    public $ob = null;
+
+    /**
+     * The Horde_Imap_Client_Utils object.
+     *
+     * @var Horde_Imap_Client_Utils
+     */
+    public $utils = null;
+
+    /**
+     * Is connection read-only?
+     *
+     * @var array
+     */
+    protected $_readonly = array();
+
+    /**
+     * Namespace cache.
+     *
+     * @var array
+     */
+    protected $_nscache = array();
+
+    /**
+     * Default namespace.
+     *
+     * @var array
+     */
+    protected $_nsdefault;
+
+    /**
+     * Constructor.
+     */
+    function __construct()
+    {
+        /* Register the logging callback. */
+        Horde_Imap_Client_Exception::$logCallback = array($this, 'logException');
+
+        /* Rebuild the Horde_Imap_Client object. */
+        $this->_loadImapObject();
+
+        $this->utils = new Horde_Imap_Client_Utils();
+    }
+
+    /**
+     * Save the Horde_Imap_Client object on session shutdown.
+     */
+    function __destruct()
+    {
+        /* Only need to serialize object once a second. When we do serialize,
+         * make sure we login in order to ensure we have done the necessary
+         * initialization. */
+        if ($this->ob &&
+            isset($_SESSION['imp']) &&
+            !isset($_SESSION['imp']['imap_ob'])) {
+            $this->ob->login();
+            $_SESSION['imp']['imap_ob'] = serialize($this->ob);
+        }
+    }
+
+    /**
+     * Loads the server configuration from servers.php.
+     *
+     * @param string $server  Returns this labeled entry only.
+     *
+     * @return mixed  If $server is set, then return this entry, or return the
+     *                entire servers array. Returns false on error.
+     */
+    static public function loadServerConfig($server = null)
+    {
+        $servers = Horde::loadConfiguration('servers.php', 'servers', 'imp');
+        if (is_a($servers, 'PEAR_Error')) {
+            Horde::logMessage($servers, __FILE__, __LINE__, PEAR_LOG_ERR);
+            return false;
+        }
+
+        if (is_null($server)) {
+            return $servers;
+        }
+
+        /* Check for the existence of the server in the config file. */
+        if (empty($servers[$server]) || !is_array($servers[$server])) {
+            $entry = sprintf('Invalid server key "%s" from client [%s]', $server, $_SERVER['REMOTE_ADDR']);
+            Horde::logMessage($entry, __FILE__, __LINE__, PEAR_LOG_ERR);
+            return false;
+        }
+
+        return $servers[$server];
+    }
+
+    /**
+     * Loads the Horde_Imap_Client object from serialized session data and
+     * loads the object into the global $imp_imap variable.
+     *
+     * @return boolean  True on success, false on error.
+     */
+    protected function _loadImapObject()
+    {
+        if (!is_null($this->ob)) {
+            return true;
+        }
+
+        if (!isset($_SESSION['imp']['imap_ob'])) {
+            return false;
+        }
+
+        Horde_Imap_Client::$encryptKey = IMP::getAuthKey();
+
+        $old_error = error_reporting(0);
+        $this->ob = unserialize($_SESSION['imp']['imap_ob']);
+        error_reporting($old_error);
+
+        if (empty($this->ob)) {
+            // @todo How to handle bad unserialize?
+            // @todo Log message
+            return false;
+        }
+
+        $this->_postcreate($_SESSION['imp']['protocol']);
+
+        return true;
+    }
+
+    /**
+     * Create a new Horde_Imap_Client object.
+     *
+     * @param string $username  The username to authenticate with.
+     * @param string $password  The password to authenticate with.
+     * @param string $key       Create a new object using this server key.
+     * @param boolean $global   If true, treate the created object as the IMP
+     *                          global IMAP object.
+     *
+     * @return boolean  The object on success, false on error.
+     */
+    public function createImapObject($username, $password, $key,
+                                     $global = true)
+    {
+        if ($global && !is_null($this->ob)) {
+            return $GLOBALS['imp_imap'];
+        }
+
+        if (($server = $this->loadServerConfig($key)) === false) {
+            return false;
+        }
+
+        $protocol = isset($server['protocol']) ? strtolower($server['protocol']) : 'imap';
+
+        $imap_config = array(
+            'debug' => isset($server['debug']) ? $server['debug'] : null,
+            'hostspec' => isset($server['hostspec']) ? $server['hostspec'] : null,
+            'password' => $password,
+            'port' => isset($server['port']) ? $server['port'] : null,
+            'secure' => isset($server['secure']) ? $server['secure'] : false,
+            'statuscache' => true,
+            'timeout' => !empty($server['timeout']) ? $server['timeout'] : 10,
+            'username' => $username,
+        );
+
+        /* Initialize caching. */
+        if (!empty($server['cache'])) {
+            $c = $server['cache'];
+            $driver = $GLOBALS['conf']['cache']['driver'];
+            if ($driver != 'none') {
+                $imap_config['cache'] = array(
+                    'comparator' => empty($c['comparator']) ? false : $c['comparator'],
+                    'compress' => empty($c['compress']) ? false : $c['compress'],
+                    'driver' => $driver,
+                    'driver_params' => Horde::getDriverConfig('cache', $driver),
+                    'id' => empty($c['id']) ? false : $c['id'],
+                    'lang' => empty($c['lang']) ? false : $c['lang'],
+                    'lifetime' => empty($c['lifetime']) ? false : $c['lifetime'],
+                    'slicesize' => empty($c['slicesize']) ? false : $c['slicesize'],
+                );
+            }
+        }
+
+        try {
+            $ob = Horde_Imap_Client::getInstance(($protocol == 'imap') ? 'Socket' : 'Cclient_pop3', $imap_config);
+        } catch (Horde_Imap_Client_Exception $e) {
+            return false;
+        }
+
+        if ($global) {
+            $this->ob = $ob;
+            $this->_postcreate($protocol);
+        }
+
+        return $ob;
+    }
+
+    /**
+     * Alter some IMP settings once we load/create the global object.
+     *
+     * @param string $protocol  The protocol used to connect.
+     */
+    protected function _postcreate($protocol)
+    {
+        global $conf, $prefs;
+
+        switch ($protocol) {
+        case 'pop':
+            /* Turn some options off if we are working with POP3. */
+            $conf['user']['allow_folders'] = false;
+            $prefs->setValue('save_sent_mail', false);
+            $prefs->setLocked('save_sent_mail', true);
+            $prefs->setLocked('sent_mail_folder', true);
+            $prefs->setLocked('drafts_folder', true);
+            $prefs->setLocked('trash_folder', true);
+            break;
+        }
+    }
+
+    /**
+     * Is the current IMAP connection read-only?
+     *
+     * @param string $mailbox  The mailbox.
+     *
+     * @return boolean  Is the connection read-only?
+     */
+    public function isReadOnly($mailbox)
+    {
+        if (!isset($this->_readonly[$mailbox])) {
+            $this->_readonly[$mailbox] =
+                !empty($GLOBALS['conf']['hooks']['mbox_readonly']) &&
+                Horde::callHook('_imp_hook_mbox_readonly', array($mailbox), 'imp');
+        }
+
+        return $this->_readonly[$mailbox];
+    }
+
+    /**
+     * Logs an exception from Horde_Imap_Client.
+     *
+     * @param object Horde_Imap_Client_Exception $e  The exception object.
+     */
+    public function logException($e)
+    {
+        Horde::logMessage($e, $e->getFile(), $e->getLine(), PEAR_LOG_ERR);
+    }
+
+    /**
+     * Get the namespace list.
+     *
+     * @return array  An array of namespace information.
+     */
+    public function getNamespaceList()
+    {
+        try {
+            return $GLOBALS['imp_imap']->ob->getNamespaces(!empty($_SESSION['imp']['imap_ext']['namespace']) ? $_SESSION['imp']['imap_ext']['namespace'] : array());
+        } catch (Horde_Imap_Client_Exception $e) {
+            // @todo Error handling
+            return array();
+        }
+    }
+
+    /**
+     * Get namespace info for a full folder path.
+     *
+     * @param string $mailbox  The folder path. If empty, will return info
+     *                         on the default namespace (i.e. the first
+     *                         personal namespace).
+     * @param boolean $empty   If true and no matching namespace is found,
+     *                         return the empty namespace, if it exists.
+     *
+     * @return mixed  The namespace info for the folder path or null if the
+     *                path doesn't exist.
+     */
+    public function getNamespace($mailbox = null, $empty = true)
+    {
+        if ($_SESSION['imp']['protocol'] == 'pop') {
+            return null;
+        }
+
+        $ns = $this->getNamespaceList();
+
+        if ($mailbox === null) {
+            reset($ns);
+            $mailbox = key($ns);
+        }
+
+        $key = (int)$empty;
+        if (isset($this->_nscache[$key][$mailbox])) {
+            return $this->_nscache[$key][$mailbox];
+        }
+
+        foreach ($ns as $key => $val) {
+            $mbx = $mailbox . $val['delimiter'];
+            if (!empty($key) && (strpos($mbx, $key) === 0)) {
+                $this->_nscache[$key][$mailbox] = $val;
+                return $val;
+            }
+        }
+
+        $this->_nscache[$key][$mailbox] = ($empty && isset($ns[''])) ? $ns[''] : null;
+
+        return $this->_nscache[$key][$mailbox];
+    }
+
+    /**
+     * Get the default personal namespace.
+     *
+     * @return mixed  The default personal namespace info.
+     */
+    public function defaultNamespace()
+    {
+        if ($_SESSION['imp']['protocol'] == 'pop') {
+            return null;
+        }
+
+        if (!isset($this->_nsdefault)) {
+            $this->_nsdefault = null;
+            foreach ($this->getNamespaceList() as $val) {
+                if ($val['type'] == 'personal') {
+                    $this->_nsdefault = $val;
+                    break;
+                }
+            }
+        }
+
+        return $this->_nsdefault;
+    }
+}
diff --git a/imp/lib/Imap/Acl.php b/imp/lib/Imap/Acl.php
new file mode 100644 (file)
index 0000000..7af5b54
--- /dev/null
@@ -0,0 +1,212 @@
+<?php
+/**
+ * Contains functions related to managing IMAP Access Control Lists.
+ *
+ * Copyright 2003-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @author  Chris Hastie <imp@oak-wood.co.uk>
+ * @package IMP
+ */
+class IMP_Imap_Acl
+{
+    /**
+     * Singleton instance.
+     *
+     * @var IMP_Imap_Acl
+     */
+    static protected $_instance = null;
+
+    /**
+     * Hash containing the list of possible rights and a human readable
+     * description of each.
+     *
+     * @var array
+     */
+    protected $_rightsList;
+
+    /**
+     * Array containing user names that cannot have their access rights
+     * changed.
+     *
+     * @var boolean
+     */
+    protected $_protected;
+
+    /**
+     * Attempts to return a reference to a concrete object instance.
+     * It will only create a new instance if no instance currently exists.
+     *
+     * @return IMP_Imap_Acl  The created concrete instance.
+     */
+    static public function singleton()
+    {
+        if (!self::$_instance) {
+            self::$_instance = new IMP_Imap_Acl();
+        }
+
+        return self::$_instance;
+    }
+
+    /**
+     * Constructor.
+     */
+    protected function __construct()
+    {
+        if ($_SESSION['imp']['protocol'] != 'imap') {
+            throw new Horde_Exception(_("ACL requires an IMAP server."));
+        }
+
+        $capability = $GLOBALS['imp_imap']->ob->queryCapability('ACL');
+        if (!$capability) {
+            throw new Horde_Exception(_("IMAP server does not support ACLs."));
+        }
+
+        $rfc4314 = $GLOBALS['imp_imap']->ob->queryCapability('RIGHTS');
+
+        $this->_protected = array($GLOBALS['imp_imap']->ob->getParam('username'));
+
+        $this->_rightsList = array(
+            'l' => array(
+                'desc' => _("List - user can see the folder"),
+                'title' => _("List")
+            ),
+            'r' => array(
+                'desc' => _("Read messages"),
+                'title' => _("Read")
+            ),
+            's' => array(
+                'desc' => _("Mark with Seen/Unseen flags"),
+                'title' => _("Mark (Seen)")
+            ),
+            'w' => array(
+                'desc' => _("Mark with other flags (e.g. Important/Answered)"),
+                'title' => _("Mark (Other)")
+            ),
+            'i' => array(
+                'desc' => _("Insert messages"),
+                'title' => _("Insert")
+            ),
+            'p' => array(
+                'desc' => _("Post to this folder (not enforced by IMAP)"),
+                'title' => _("Post")
+            ),
+            'a' => array(
+                'desc' => _("Administer - set permissions for other users"),
+                'title' => _("Administer")
+            )
+        );
+
+        if ($rfc4314) {
+            // RFC 4314 compliant rights
+            $this->_rightsList = array_merge($this->_rightsList, array(
+                'k' => array(
+                    'desc' => _("Create sub folders"),
+                    'title' => _("Create Folders")
+                ),
+                'x' => array(
+                    'desc' => _("Delete sub folders"),
+                    'title' => _("Delete Folders")
+                ),
+                't' => array(
+                    'desc' => _("Delete messages"),
+                    'title' => _("Delete")
+                ),
+                'e' => array(
+                    'desc' => _("Purge messages"),
+                    'title' => _("Purge")
+                )
+            ));
+        } else {
+            // RFC 2086 compliant rights
+            $this->_rightsList = array_merge($this->_rightsList, array(
+                'c' => array(
+                    'desc' =>_("Create sub folders"),
+                    'title' => _("Create Folder")
+                ),
+                'd' => array(
+                    'desc' => _("Delete and purge messages"),
+                    'title' => _("Delete/Purge")
+                )
+            ));
+        }
+    }
+
+    /**
+     * Attempts to retrieve the existing ACL for a mailbox from the server.
+     *
+     * @param string $mbox  The mailbox to get the ACL for.
+     *
+     * @return array  A hash containing information on the ACL.
+     * @throws Horde_Exception
+     */
+    public function getACL($mbox)
+    {
+        try {
+            return $GLOBALS['imp_imap']->ob->getACL($mbox);
+        } catch (Horde_Imap_Client_Exception $e) {
+            throw new Horde_Exception(_("Could not retrieve ACL"));
+        }
+    }
+
+    /**
+     * Edits an ACL on the server.
+     *
+     * @param string $mbox  The mailbox on which to edit the ACL.
+     * @param string $user  The user to grant rights to.
+     * @param array $acl    The keys of which are the rights to be granted
+     *                      (see RFC 2086).
+     *
+     * @throws Horde_Exception
+     */
+    public function editACL($mbox, $user, $acl)
+    {
+        try {
+            $GLOBALS['imp_imap']->ob->setACL($mbox, $user, array('rights' => $acl));
+        } catch (Horde_Imap_Client_Exception $e) {
+            throw new Horde_Exception(sprintf(_("Couldn't give user \"%s\" the following rights for the folder \"%s\": %s"), $user, $mbox, implode('', $acl)));
+        }
+    }
+
+    /**
+     * Can a user edit the ACL for this mailbox?
+     *
+     * @param string $mbox  The mailbox name.
+     * @param string $user  A user name.
+     *
+     * @return boolean  True if $user has 'a' right
+     */
+    public function canEdit($mbox, $user)
+    {
+        try {
+            $rights = $GLOBALS['imp_imap']->ob->listACLRights($mbox, $user);
+            foreach ($rights as $val) {
+                if (strpos($val, 'a') !== false) {
+                    return true;
+                }
+            }
+            return false;
+        } catch (Horde_Imap_Client_Exception $e) {
+            return false;
+        }
+    }
+
+    /**
+     * TODO
+     */
+    public function getRights()
+    {
+        return $this->_rightsList;
+    }
+
+    /**
+     * TODO
+     */
+    public function getProtected()
+    {
+        return $this->_protected;
+    }
+
+}
diff --git a/imp/lib/Imap/Thread.php b/imp/lib/Imap/Thread.php
new file mode 100644 (file)
index 0000000..9a409ce
--- /dev/null
@@ -0,0 +1,137 @@
+<?php
+/**
+ * The IMP_Imap_Thread class provides functions to generate thread tree
+ * images.
+ *
+ * Copyright 2005-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author  Michael Slusarz <slusarz@horde.org>
+ * @package IMP
+ */
+class IMP_Imap_Thread
+{
+    /**
+     * The thread data object.
+     *
+     * @var Horde_Imap_Client_Thread
+     */
+    protected $_thread;
+
+    /**
+     * Images used and their internal representations.
+     *
+     * @var array
+     */
+    static protected $_imglist = array(
+        '0' => 'blank.png',
+        '1' => 'line.png',
+        '2' => 'join.png',
+        '3' => 'joinbottom-down.png',
+        '4' => 'joinbottom.png'
+    );
+
+    /**
+     * Constructor.
+     *
+     * @param Horde_Imap_Client_Thread $thread  The thread data object.
+     */
+    public function __construct($thread)
+    {
+        $this->_thread = $thread;
+    }
+
+    /**
+     * Generate the thread representation for the given index list in the
+     * internal format (a string with each character representing the graphic
+     * to be displayed from $_imglist).
+     *
+     * @param array $indices    The list of indices to create a tree for.
+     * @param boolean $sortdir  True for newest first, false for oldest first.
+     *
+     * @return array  An array with the index as the key and the internal
+     *                thread representation as the value.
+     */
+    public function getThreadTreeOb($indices, $sortdir)
+    {
+        $container = $last_level = $last_thread = null;
+        $thread_level = $tree = array();
+        $t = &$this->_thread;
+
+        $indices = array_intersect($t->messageList($sortdir), $indices);
+
+        /* If starting in the middle of a thread, the threadLevel tree needs
+         * to be built from the base of the current thread. */
+        $first = reset($indices);
+        foreach ($t->getThread(reset($indices)) as $val) {
+            if ($first == $val) {
+                break;
+            }
+            $thread_level[$t->getThreadIndent($val)] = $t->lastInLevel($val);
+        }
+
+        foreach ($indices as $val) {
+            $tree[$val] = '';
+
+            $indentBase = $t->getThreadBase($val);
+            if (empty($indentBase)) {
+                continue;
+            }
+
+            $lines = '';
+            $indentLevel = $t->getThreadIndent($val);
+            $lastinlevel = $t->lastInLevel($val);
+
+            if ($lastinlevel && ($indentBase == $val)) {
+                continue;
+            }
+
+            if ($lastinlevel) {
+                $join_img = ($sortdir) ? 3 : 4;
+            } elseif (($indentLevel == 1) && ($indentBase == $val)) {
+                $join_img = ($sortdir) ? 4 : 3;
+            } else {
+                $join_img = 2;
+            }
+
+            $thread_level[$indentLevel] = $lastinlevel;
+            $line = '';
+
+            for ($i = 1; $i < $indentLevel; ++$i) {
+                $line .= (!isset($thread_level[$i]) || ($thread_level[$i])) ? 0 : 1;
+            }
+            $tree[$val] = $line . $join_img;
+        }
+
+        return $tree;
+    }
+
+    /**
+     * Generate the thread representation image for the given index list.
+     *
+     * @param array $indices    The list of indices to create a tree for.
+     * @param boolean $sortdir  True for newest first, false for oldest first.
+     *
+     * @return array  An array with the index as the key and the thread image
+     *                representation as the value.
+     */
+    public function getThreadImageTree($indices, $sortdir)
+    {
+        $imgs = $tree = array();
+
+        foreach (self::$_imglist as $key => $val) {
+            $imgs[$key] = Horde::img('tree/' . (($key != 0 && !empty($GLOBALS['nls']['rtl'][$GLOBALS['language']])) ? ('rev-' . $val) : $val), null, null, $GLOBALS['registry']->getImageDir('horde'));
+        }
+
+        foreach ($this->getThreadTreeOb($indices, $sortdir) as $k => $v) {
+            $tree[$k] = '';
+            for ($i = 0, $length = strlen($v); $i < $length; ++$i) {
+                $tree[$k] .= $imgs[$v[$i]];
+            }
+        }
+        return $tree;
+    }
+
+}
diff --git a/imp/lib/Imap/Tree.php b/imp/lib/Imap/Tree.php
new file mode 100644 (file)
index 0000000..9a19450
--- /dev/null
@@ -0,0 +1,1995 @@
+<?php
+/**
+ * The IMP_Imap_Tree class provides a tree view of the mailboxes in an
+ * IMAP/POP3 repository.  It provides access functions to iterate through this
+ * tree and query information about individual mailboxes.
+ * In IMP, folders = IMAP mailboxes so the two terms are used interchangably.
+ *
+ * Copyright 2000-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author  Chuck Hagenbuch <chuck@horde.org>
+ * @author  Jon Parise <jon@horde.org>
+ * @author  Anil Madhavapeddy <avsm@horde.org>
+ * @author  Michael Slusarz <slusarz@horde.org>
+ * @package IMP
+ */
+class IMP_Imap_Tree
+{
+    /* Constants for mailboxElt attributes. */
+    const ELT_NOSELECT = 1;
+    const ELT_NAMESPACE = 2;
+    const ELT_IS_OPEN = 4;
+    const ELT_IS_SUBSCRIBED = 8;
+    const ELT_NOSHOW = 16;
+    const ELT_IS_POLLED = 32;
+    const ELT_NEED_SORT = 64;
+    const ELT_VFOLDER = 128;
+    const ELT_NONIMAP = 256;
+    const ELT_INVISIBLE = 512;
+
+    /* The isOpen() expanded mode constants. */
+    const OPEN_NONE = 0;
+    const OPEN_ALL = 1;
+    const OPEN_USER = 2;
+
+    /* The manner to which to traverse the tree when calling next(). */
+    const NEXT_SHOWCLOSED = 1;
+    const NEXT_SHOWSUB = 2;
+
+    /* The string used to indicate the base of the tree. */
+    const BASE_ELT = '%';
+
+    /* Defines used with the output from the build() function. */
+    const SPECIAL_INBOX = 1;
+    const SPECIAL_TRASH = 2;
+    const SPECIAL_DRAFT = 3;
+    const SPECIAL_SPAM = 4;
+    const SPECIAL_SENT = 5;
+
+    /* Defines used with folderList(). */
+    const FLIST_CONTAINER = 1;
+    const FLIST_UNSUB = 2;
+    const FLIST_OB = 4;
+    const FLIST_VFOLDER = 8;
+
+    /* Add a percent to folder key since it allows us to sort by name but
+     * never conflict with an IMAP mailbox of the same name (since '%' is an
+     * invalid character in an IMAP mailbox string). */
+    public $VFOLDER_LABEL;
+    public $VFOLDER_KEY;
+
+    /* Defines used with namespace display. */
+    public $SHARED_LABEL;
+    public $SHARED_KEY;
+    public $OTHER_LABEL;
+    public $OTHER_KEY;
+
+    /**
+     * Singleton instance
+     *
+     * @var IMP_Imap_Tree
+     */
+    static protected $_instance = null;
+
+    /**
+     * Array containing the mailbox tree.
+     *
+     * @var array
+     */
+    protected $_tree;
+
+    /**
+     * Location of current element in the tree.
+     *
+     * @var string
+     */
+    protected $_currparent = null;
+
+    /**
+     * Location of current element in the tree.
+     *
+     * @var integer
+     */
+    protected $_currkey = null;
+
+    /**
+     * Location of current element in the tree.
+     *
+     * @var array
+     */
+    protected $_currstack = array();
+
+    /**
+     * Show unsubscribed mailboxes?
+     *
+     * @var boolean
+     */
+    protected $_showunsub = false;
+
+    /**
+     * Parent list.
+     *
+     * @var array
+     */
+    protected $_parent = array();
+
+    /**
+     * The cached list of mailboxes to poll.
+     *
+     * @var array
+     */
+    protected $_poll = null;
+
+    /**
+     * The cached list of expanded folders.
+     *
+     * @var array
+     */
+    protected $_expanded = null;
+
+    /**
+     * Cached list of subscribed mailboxes.
+     *
+     * @var array
+     */
+    protected $_subscribed = null;
+
+    /**
+     * The cached full list of mailboxes on the server.
+     *
+     * @var array
+     */
+    protected $_fulllist = null;
+
+    /**
+     * Tree changed flag.  Set when something in the tree has been altered.
+     *
+     * @var boolean
+     */
+    protected $_changed = false;
+
+    /**
+     * Have we shown unsubscribed folders previously?
+     *
+     * @var boolean
+     */
+    protected $_unsubview = false;
+
+    /**
+     * The string used for the IMAP delimiter.
+     *
+     * @var string
+     */
+    protected $_delimiter = '/';
+
+    /**
+     * The list of namespaces to add to the tree.
+     *
+     * @var array
+     */
+    protected $_namespaces = array();
+
+    /**
+     * Used to determine the list of element changes.
+     *
+     * @var array
+     */
+    protected $_eltdiff = null;
+
+    /**
+     * If set, track element changes.
+     *
+     * @var boolean
+     */
+    protected $_trackdiff = true;
+
+    /**
+     * See $open parameter in build().
+     *
+     * @var boolean
+     */
+    protected $_forceopen = false;
+
+    /**
+     * Attempts to return a reference to a concrete IMP_Imap_Tree instance.
+     *
+     * If an IMP_Imap_Tree object is currently stored in the cache, re-create
+     * that object.  Else, create a new instance.  Ensures that only one
+     * instance is available at any time.
+     *
+     * @return IMP_Imap_Tree  The object or null.
+     */
+    static public function singleton()
+    {
+        if (is_null(self::$_instance)) {
+            if (!empty($_SESSION['imp']['cache']['tree'])) {
+                $imp_cache = IMP::getCache();
+                self::$_instance = unserialize($imp_cache->get($_SESSION['imp']['cache']['tree'], 86400));
+            }
+            if (empty(self::$_instance)) {
+                self::$_instance = new IMP_Imap_Tree();
+            }
+        }
+
+        return self::$_instance;
+    }
+
+    /**
+     * Constructor.
+     */
+    protected function __construct()
+    {
+        if ($_SESSION['imp']['protocol'] == 'imap') {
+            $ns = $GLOBALS['imp_imap']->getNamespaceList();
+            $ptr = reset($ns);
+            $this->_delimiter = $ptr['delimiter'];
+            $this->_namespaces = (empty($GLOBALS['conf']['user']['allow_folders'])) ? array() : $ns;
+        }
+
+        if (!isset($_SESSION['imp']['cache']['tree'])) {
+            $imp_cache = IMP::getCache();
+            $_SESSION['imp']['cache']['tree'] = $imp_cache
+                ? uniqid(mt_rand() . Auth::getAuth())
+                : null;
+        }
+
+        /* Must set these values here because PHP 5 does not allow assignment
+         * of const's to gettext strings. */
+        $this->VFOLDER_LABEL = _("Virtual Folders");
+        $this->VFOLDER_KEY = $this->VFOLDER_LABEL . '%';
+        $this->SHARED_LABEL = _("Shared Folders");
+        $this->SHARED_KEY = $this->SHARED_LABEL . '%';
+        $this->OTHER_LABEL = _("Other Users' Folders");
+        $this->OTHER_KEY = $this->OTHER_LABEL . '%';
+
+        $this->init();
+    }
+
+    /**
+     * Do cleanup prior to serialization and provide a list of variables
+     * to serialize.
+     */
+    function __sleep()
+    {
+        /* Don't store $_expanded and $_poll - these values are handled
+         * by the subclasses.
+         * Don't store $_eltdiff - these needs to be regenerated for each
+         * request.
+         * Don't store $_currkey, $_currparent, and $_currstack since the
+         * user MUST call reset() before cycling through the tree.
+         * Don't store $_subscribed and $_fulllist - this information is
+         * stored in the elements.
+         * Reset the $_changed and $_trackdiff flags. */
+        $this->_currkey = $this->_currparent = $this->_eltdiff = $this->_expanded = $this->_fulllist = $this->_poll = $this->_subscribed = null;
+        $this->_currstack = array();
+        $this->_changed = false;
+        $this->_trackdiff = true;
+
+        return array_keys(get_class_vars(__CLASS__));
+    }
+
+    /**
+     * Store a serialized version of ourself in the current session.
+     */
+    function __destruct()
+    {
+        /* We only need to store the object if using Horde_Cache and the tree
+         * has changed. */
+        if (empty($this->_changed) ||
+            is_null($_SESSION['imp']['cache']['tree'])) {
+            return;
+        }
+
+        $imp_cache = IMP::getCache();
+        $imp_cache->set($_SESSION['imp']['cache']['tree'], serialize($this), 86400);
+    }
+
+    /**
+     * Returns the list of mailboxes on the server.
+     *
+     * @param boolean $showunsub  Show unsubscribed mailboxes?
+     *
+     * @return array  A list of mailbox names.
+     */
+    protected function _getList($showunsub)
+    {
+        if ($showunsub && !is_null($this->_fulllist)) {
+            return $this->_fulllist;
+        } elseif (!$showunsub && !is_null($this->_subscribed)) {
+            return array_keys($this->_subscribed);
+        }
+
+        /* INBOX must always appear. */
+        $names = array('INBOX');
+
+        foreach ($this->_namespaces as $key => $val) {
+            try {
+                $names = array_merge($names, $GLOBALS['imp_imap']->ob->listMailboxes($key . '*', $showunsub ? Horde_Imap_Client::MBOX_ALL : Horde_Imap_Client::MBOX_SUBSCRIBED, array('flat' => true)));
+                if ($showunsub) {
+                    $this->_fulllist = $names;
+                } else {
+                    $this->_subscribed = $names;
+                }
+            } catch (Horde_Imap_Client_Exception $e) {}
+        }
+
+        return $names;
+    }
+
+    /**
+     * Make a single mailbox tree element.
+     * An element consists of the following items (we use single letters here
+     * to save in session storage space):
+     *   'a'  --  Attributes
+     *   'c'  --  Level count
+     *   'l'  --  Label
+     *   'p'  --  Parent node
+     *   'v'  --  Value
+     *
+     * @param string $name         The mailbox name.
+     * @param integer $attributes  The mailbox's attributes.
+     *
+     * @return array  See above format.
+     */
+    protected function _makeElt($name, $attributes = 0)
+    {
+        $elt = array(
+            'a' => $attributes,
+            'c' => 0,
+            'p' => self::BASE_ELT,
+            'v' => $name
+        );
+
+        /* Set subscribed values. We know the folder is subscribed, without
+         * query of the IMAP server, in the following situations:
+         * + Folder is INBOX.
+         * + We are adding while in subscribe-only mode.
+         * + Subscriptions are turned off. */
+        if (!$this->isSubscribed($elt)) {
+            if (!$this->_showunsub ||
+                ($elt['v'] == 'INBOX') ||
+                !$GLOBALS['prefs']->getValue('subscribe')) {
+                $this->_setSubscribed($elt, true);
+            } else {
+                $this->_initSubscribed();
+                $this->_setSubscribed($elt, isset($this->_subscribed[$elt['v']]));
+            }
+        }
+
+        /* Check for polled status. */
+        $this->_initPollList();
+        $this->_setPolled($elt, isset($this->_poll[$elt['v']]));
+
+        /* Check for open status. */
+        switch ($GLOBALS['prefs']->getValue('nav_expanded')) {
+        case self::OPEN_NONE:
+            $open = false;
+            break;
+
+        case self::OPEN_ALL:
+            $open = true;
+            break;
+
+        case self::OPEN_USER:
+            $this->_initExpandedList();
+            $open = !empty($this->_expanded[$elt['v']]);
+            break;
+        }
+        $this->_setOpen($elt, $open);
+
+        /* Computed values. */
+        $ns_info = $this->_getNamespace($elt['v']);
+        $tmp = explode(is_null($ns_info) ? $this->_delimiter : $ns_info['delimiter'], $elt['v']);
+        $elt['c'] = count($tmp) - 1;
+
+        /* Get the mailbox label. */
+        $elt['l'] = IMP::getLabel($tmp[$elt['c']]);
+
+
+        if ($_SESSION['imp']['protocol'] != 'pop') {
+            if (!empty($GLOBALS['conf']['hooks']['display_folder'])) {
+                $this->_setInvisible($elt, !Horde::callHook('_imp_hook_display_folder', array($elt['v']), 'imp'));
+            }
+
+            if ($elt['c'] != 0) {
+                $elt['p'] = implode(is_null($ns_info) ? $this->_delimiter : $ns_info['delimiter'], array_slice($tmp, 0, $elt['c']));
+            }
+
+            if (!is_null($ns_info)) {
+                switch ($ns_info['type']) {
+                case 'personal':
+                    /* Strip personal namespace. */
+                    if (!empty($ns_info['name']) && ($elt['c'] != 0)) {
+                        --$elt['c'];
+                        if (strpos($elt['p'], $ns_info['delimiter']) === false) {
+                            $elt['p'] = self::BASE_ELT;
+                        } elseif (strpos($elt['v'], $ns_info['name'] . 'INBOX' . $ns_info['delimiter']) === 0) {
+                            $elt['p'] = 'INBOX';
+                        }
+                    }
+                    break;
+
+                case 'other':
+                case 'shared':
+                    if (substr($ns_info['name'], 0, -1 * strlen($ns_info['delimiter'])) == $elt['v']) {
+                        $elt['a'] = self::ELT_NOSELECT | self::ELT_NAMESPACE;
+                    }
+
+                    if ($GLOBALS['prefs']->getValue('tree_view')) {
+                        $name = ($ns_info['type'] == 'other') ? $this->OTHER_KEY : $this->SHARED_KEY;
+                        if ($elt['c'] == 0) {
+                            $elt['p'] = $name;
+                            ++$elt['c'];
+                        } elseif ($this->_tree[$name] && self::ELT_NOSHOW) {
+                            if ($elt['c'] == 1) {
+                                $elt['p'] = $name;
+                            }
+                        } else {
+                            ++$elt['c'];
+                        }
+                    }
+                    break;
+                }
+            }
+        }
+
+        return $elt;
+    }
+
+    /**
+     * Initalize the tree.
+     */
+    public function init()
+    {
+        $initmode = (($_SESSION['imp']['protocol'] == 'pop') ||
+                     !$GLOBALS['prefs']->getValue('subscribe') ||
+                     $_SESSION['imp']['showunsub'])
+            ? 'unsub' : 'sub';
+
+        /* Reset class variables to the defaults. */
+        $this->_changed = true;
+        $this->_currkey = $this->_currparent = $this->_subscribed = null;
+        $this->_currstack = $this->_tree = $this->_parent = array();
+        $this->_showunsub = $this->_unsubview = ($initmode == 'unsub');
+
+        /* Create a placeholder element to the base of the tree list so we can
+         * keep track of whether the base level needs to be sorted. */
+        $this->_tree[self::BASE_ELT] = array(
+            'a' => self::ELT_NEED_SORT,
+            'v' => self::BASE_ELT
+        );
+
+        if (empty($GLOBALS['conf']['user']['allow_folders']) ||
+            ($_SESSION['imp']['protocol'] == 'pop')) {
+            $this->_insertElt($this->_makeElt('INBOX', self::ELT_IS_SUBSCRIBED));
+            return;
+        }
+
+        /* Add namespace elements. */
+        foreach ($this->_namespaces as $key => $val) {
+            if ($val['type'] != 'personal' &&
+                $GLOBALS['prefs']->getValue('tree_view')) {
+                $elt = $this->_makeElt(
+                    ($val['type'] == 'other') ? $this->OTHER_KEY : $this->SHARED_KEY,
+                    self::ELT_NOSELECT | self::ELT_NAMESPACE | self::ELT_NONIMAP | self::ELT_NOSHOW
+                );
+                $elt['l'] = ($val['type'] == 'other')
+                    ? $this->OTHER_LABEL : $this->SHARED_LABEL;
+
+                foreach ($this->_namespaces as $val2) {
+                    if (($val2['type'] == $val['type']) &&
+                        ($val2['name'] != $val['name'])) {
+                        $elt['a'] &= ~self::ELT_NOSHOW;
+                        break;
+                    }
+                }
+
+                $this->_insertElt($elt);
+            }
+        }
+
+        /* Create the list (INBOX and all other hierarchies). */
+        $this->insert($this->_getList($this->_showunsub));
+
+        /* Add virtual folders to the tree. */
+        $this->insertVFolders($GLOBALS['imp_search']->listQueries(true));
+    }
+
+    /**
+     * Expand a mail folder.
+     *
+     * @param string $folder      The folder name to expand.
+     * @param boolean $expandall  Expand all folders under this one?
+     */
+    public function expand($folder, $expandall = false)
+    {
+        $folder = $this->_convertName($folder);
+
+        if (!isset($this->_tree[$folder])) {
+            return;
+        }
+        $elt = &$this->_tree[$folder];
+
+        if ($this->hasChildren($elt)) {
+            if (!$this->isOpen($elt)) {
+                $this->_changed = true;
+                $this->_setOpen($elt, true);
+            }
+
+            /* Expand all children beneath this one. */
+            if ($expandall && !empty($this->_parent[$folder])) {
+                foreach ($this->_parent[$folder] as $val) {
+                    $this->expand($this->_tree[$val]['v'], true);
+                }
+            }
+        }
+    }
+
+    /**
+     * Collapse a mail folder.
+     *
+     * @param string $folder  The folder name to collapse.
+     */
+    public function collapse($folder)
+    {
+        $folder = $this->_convertName($folder);
+
+        if (!isset($this->_tree[$folder])) {
+            return;
+        }
+
+        if ($this->isOpen($this->_tree[$folder])) {
+            $this->_changed = true;
+            $this->_setOpen($this->_tree[$folder], false);
+        }
+    }
+
+    /**
+     * Sets the internal array pointer to the next element, and returns the
+     * next object.
+     *
+     * @param integer $mask  A mask with the following elements:
+     * <pre>
+     * IMP_Imap_Tree::NEXT_SHOWCLOSED - Don't ignore closed elements.
+     * IMP_Imap_Tree::NEXT_SHOWSUB - Only show subscribed elements.
+     * </pre>
+     *
+     * @return mixed  Returns the next element or false if the element doesn't
+     *                exist.
+     */
+    public function next($mask = 0)
+    {
+        if (is_null($this->_currkey) && is_null($this->_currparent)) {
+            return false;
+        }
+
+        $curr = $this->current();
+
+        $old_showunsub = $this->_showunsub;
+        if ($mask & self::NEXT_SHOWSUB) {
+            $this->_showunsub = false;
+        }
+
+        if ($this->_activeElt($curr) &&
+            (($mask & self::NEXT_SHOWCLOSED) || $this->isOpen($curr)) &&
+            ($this->_currparent != $curr['v'])) {
+            /* If the current element is open, and children exist, move into
+             * it. */
+            $this->_currstack[] = array('k' => $this->_currkey, 'p' => $this->_currparent);
+            $this->_currkey = 0;
+            $this->_currparent = $curr['v'];
+            $this->_sortLevel($curr['v']);
+
+            $curr = $this->current();
+            if ($GLOBALS['prefs']->getValue('tree_view') &&
+                $this->isNamespace($curr) &&
+                !$this->_isNonIMAPElt($curr) &&
+                ($this->_tree[$curr['p']] && self::ELT_NOSHOW)) {
+                $this->next($mask);
+            }
+        } else {
+            /* Else, increment within the current subfolder. */
+            $this->_currkey++;
+        }
+
+        $curr = $this->current();
+        if (!$curr) {
+            if (empty($this->_currstack)) {
+                $this->_currkey = $this->_currparent = null;
+                $this->_showunsub = $old_showunsub;
+                return false;
+            } else {
+                do {
+                    $old = array_pop($this->_currstack);
+                    $this->_currkey = $old['k'] + 1;
+                    $this->_currparent = $old['p'];
+                } while ((($curr = $this->current()) == false) &&
+                         count($this->_currstack));
+            }
+        }
+
+        $res = $this->_activeElt($curr);
+        $this->_showunsub = $old_showunsub;
+        return ($res) ? $curr : $this->next($mask);
+    }
+
+    /**
+     * Set internal pointer to the head of the tree.
+     * This MUST be called before you can traverse the tree with next().
+     *
+     * @return mixed  Returns the element at the head of the tree or false
+     *                if the element doesn't exist.
+     */
+    public function reset()
+    {
+        $this->_currkey = 0;
+        $this->_currparent = self::BASE_ELT;
+        $this->_currstack = array();
+        $this->_sortLevel($this->_currparent);
+        return $this->current();
+    }
+
+    /**
+     * Return the current tree element.
+     *
+     * @return array  The current tree element or false if there is no
+     *                element.
+     */
+    public function current()
+    {
+        return (!isset($this->_parent[$this->_currparent][$this->_currkey]))
+            ? false
+            : $this->_tree[$this->_parent[$this->_currparent][$this->_currkey]];
+    }
+
+    /**
+     * Determines if there are more elements in the current tree level.
+     *
+     * @return boolean  True if there are more elements, false if this is the
+     *                  last element.
+     */
+    public function peek()
+    {
+        for ($i = ($this->_currkey + 1); ; ++$i) {
+            if (!isset($this->_parent[$this->_currparent][$i])) {
+                return false;
+            }
+            if ($this->_activeElt($this->_tree[$this->_parent[$this->_currparent][$i]])) {
+                return true;
+            }
+        }
+    }
+
+    /**
+     * Returns the requested element.
+     *
+     * @param string $name  The name of the tree element.
+     *
+     * @return array  Returns the requested element or false if not found.
+     */
+    public function get($name)
+    {
+        $name = $this->_convertName($name);
+        return (isset($this->_tree[$name])) ? $this->_tree[$name] : false;
+    }
+
+    /**
+     * Insert a folder/mailbox into the tree.
+     *
+     * @param mixed $id  The name of the folder (or a list of folder names)
+     *                   to add.
+     */
+    public function insert($id)
+    {
+        if (is_array($id)) {
+            /* We want to add from the BASE of the tree up for efficiency
+             * sake. */
+            $this->_sortList($id);
+        } else {
+            $id = array($id);
+        }
+
+        foreach ($id as $val) {
+            if (isset($this->_tree[$val]) &&
+                !$this->isContainer($this->_tree[$val])) {
+                continue;
+            }
+
+            $ns_info = $this->_getNamespace($val);
+            if (is_null($ns_info)) {
+                if (strpos($val, $this->VFOLDER_KEY . $this->_delimiter) === 0) {
+                    $elt = $this->_makeElt($this->VFOLDER_KEY, self::ELT_VFOLDER | self::ELT_NOSELECT | self::ELT_NONIMAP);
+                    $elt['l'] = $this->VFOLDER_LABEL;
+                    $this->_insertElt($elt);
+                }
+
+                $elt = $this->_makeElt($val, self::ELT_VFOLDER | self::ELT_IS_SUBSCRIBED);
+                $elt['l'] = $elt['v'] = String::substr($val, String::length($this->VFOLDER_KEY) + String::length($this->_delimiter));
+                $this->_insertElt($elt);
+            } else {
+                /* Break apart the name via the delimiter and go step by
+                 * step through the name to make sure all subfolders exist
+                 * in the tree. */
+                $parts = explode($ns_info['delimiter'], $val);
+                $parts[0] = $this->_convertName($parts[0]);
+                $parts_count = count($parts);
+                for ($i = 0; $i < $parts_count; ++$i) {
+                    $part = implode($ns_info['delimiter'], array_slice($parts, 0, $i + 1));
+
+                    if (isset($this->_tree[$part])) {
+                        if (($part == $val) &&
+                            $this->isContainer($this->_tree[$part])) {
+                            $this->_setContainer($this->_tree[$part], false);
+                        }
+                    } else {
+                        $this->_insertElt(($part == $val) ? $this->_makeElt($part) : $this->_makeElt($part, self::ELT_NOSELECT));
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Insert an element into the tree.
+     *
+     * @param array $elt  The element to insert. The key in the tree is the
+     *                    'v' (value) element of the element.
+     */
+    protected function _insertElt($elt)
+    {
+        if (!strlen($elt['l']) || isset($this->_tree[$elt['v']])) {
+            return;
+        }
+
+        // UW fix - it may return both 'foo' and 'foo/' as folder names.
+        // Only add one of these (without the namespace character) to
+        // the tree.  See Ticket #5764.
+        $ns_info = $this->_getNamespace($elt['v']);
+        if (isset($this->_tree[rtrim($elt['v'], is_null($ns_info) ? $this->_delimiter : $ns_info['delimiter'])])) {
+            return;
+        }
+
+        $this->_changed = true;
+
+        /* Set the parent array to the value in $elt['p']. */
+        if (empty($this->_parent[$elt['p']])) {
+            $this->_parent[$elt['p']] = array();
+            // This is a case where it is possible that the parent element has
+            // changed (it now has children) but we can't catch it via the
+            // bitflag (since hasChildren() is dynamically determined).
+            if ($this->_trackdiff && !is_null($this->_eltdiff)) {
+                $this->_eltdiff['c'][$elt['p']] = 1;
+            }
+        }
+        $this->_parent[$elt['p']][] = $elt['v'];
+        $this->_tree[$elt['v']] = $elt;
+
+        if ($this->_trackdiff && !is_null($this->_eltdiff)) {
+            $this->_eltdiff['a'][$elt['v']] = 1;
+        }
+
+        /* Make sure we are sorted correctly. */
+        if (count($this->_parent[$elt['p']]) > 1) {
+            $this->_setNeedSort($this->_tree[$elt['p']], true);
+        }
+    }
+
+    /**
+     * Delete an element from the tree.
+     *
+     * @param mixed $id  The element name or an array of element names.
+     *
+     * @return boolean  Return true on success, false on error.
+     */
+    public function delete($id)
+    {
+        if (is_array($id)) {
+            /* We want to delete from the TOP of the tree down to ensure that
+             * parents have an accurate view of what children are left. */
+            $this->_sortList($id);
+            $id = array_reverse($id);
+
+            $success = true;
+            foreach ($id as $val) {
+                $currsuccess = $this->delete($val);
+                if (!$currsuccess) {
+                    $success = false;
+                }
+            }
+            return $success;
+        } else {
+            $id = $this->_convertName($id, true);
+        }
+
+        $vfolder_base = ($id == $this->VFOLDER_LABEL);
+        $search_id = $GLOBALS['imp_search']->createSearchID($id);
+
+        if ($vfolder_base ||
+            (isset($this->_tree[$search_id]) &&
+             $this->isVFolder($this->_tree[$search_id]))) {
+            if (!$vfolder_base) {
+                $id = $search_id;
+
+            }
+            $parent = $this->_tree[$id]['p'];
+            unset($this->_tree[$id]);
+
+            /* Delete the entry from the parent tree. */
+            $key = array_search($id, $this->_parent[$parent]);
+            unset($this->_parent[$parent][$key]);
+
+            /* Rebuild the parent tree. */
+            if (!$vfolder_base && empty($this->_parent[$parent])) {
+                $this->delete($parent);
+            } else {
+                $this->_parent[$parent] = array_values($this->_parent[$parent]);
+            }
+            $this->_changed = true;
+
+            return true;
+        }
+
+        $ns_info = $this->_getNamespace($id);
+
+        if (($id == 'INBOX') ||
+            !isset($this->_tree[$id]) ||
+            ($id == $ns_info['name'])) {
+            return false;
+        }
+
+        $this->_changed = true;
+
+        $elt = &$this->_tree[$id];
+
+        /* Do not delete from tree if there are child elements - instead,
+         * convert to a container element. */
+        if ($this->hasChildren($elt)) {
+            $this->_setContainer($elt, true);
+            return true;
+        }
+
+        $parent = $elt['p'];
+
+        /* Delete the tree entry. */
+        unset($this->_tree[$id]);
+
+        /* Delete the entry from the parent tree. */
+        $key = array_search($id, $this->_parent[$parent]);
+        unset($this->_parent[$parent][$key]);
+
+        if (!is_null($this->_eltdiff)) {
+            $this->_eltdiff['d'][$id] = 1;
+        }
+
+        if (empty($this->_parent[$parent])) {
+            /* This folder is now completely empty (no children).  If the
+             * folder is a container only, we should delete the folder from
+             * the tree. */
+            unset($this->_parent[$parent]);
+            if (isset($this->_tree[$parent])) {
+                if ($this->isContainer($this->_tree[$parent]) &&
+                    !$this->isNamespace($this->_tree[$parent])) {
+                    $this->delete($parent);
+                } else {
+                    $this->_modifyExpandedList($parent, 'remove');
+                    $this->_setOpen($this->_tree[$parent], false);
+                    /* This is a case where it is possible that the parent
+                     * element has changed (it no longer has children) but
+                     * we can't catch it via the bitflag (since hasChildren()
+                     * is dynamically determined). */
+                    if (!is_null($this->_eltdiff)) {
+                        $this->_eltdiff['c'][$parent] = 1;
+                    }
+                }
+            }
+        } else {
+            /* Rebuild the parent tree. */
+            $this->_parent[$parent] = array_values($this->_parent[$parent]);
+        }
+
+        /* Remove the mailbox from the expanded folders list. */
+        $this->_modifyExpandedList($id, 'remove');
+
+        /* Remove the mailbox from the nav_poll list. */
+        $this->removePollList($id);
+
+        return true;
+    }
+
+    /**
+     * Subscribe an element to the tree.
+     *
+     * @param mixed $id  The element name or an array of element names.
+     */
+    public function subscribe($id)
+    {
+        if (!is_array($id)) {
+            $id = array($id);
+        }
+
+        foreach ($id as $val) {
+            $val = $this->_convertName($val);
+            if (isset($this->_tree[$val])) {
+                $this->_changed = true;
+                $this->_setSubscribed($this->_tree[$val], true);
+                $this->_setContainer($this->_tree[$val], false);
+            }
+        }
+    }
+
+    /**
+     * Unsubscribe an element from the tree.
+     *
+     * @param mixed $id  The element name or an array of element names.
+     */
+    public function unsubscribe($id)
+    {
+        if (!is_array($id)) {
+            $id = array($id);
+        } else {
+            /* We want to delete from the TOP of the tree down to ensure that
+             * parents have an accurate view of what children are left. */
+            $this->_sortList($id);
+            $id = array_reverse($id);
+        }
+
+        foreach ($id as $val) {
+            $val = $this->_convertName($val);
+
+            /* INBOX can never be unsubscribed to. */
+            if (isset($this->_tree[$val]) && ($val != 'INBOX')) {
+                $this->_changed = $this->_unsubview = true;
+
+                $elt = &$this->_tree[$val];
+
+                /* Do not delete from tree if there are child elements -
+                 * instead, convert to a container element. */
+                if (!$this->_showunsub && $this->hasChildren($elt)) {
+                    $this->_setContainer($elt, true);
+                }
+
+                /* Set as unsubscribed, add to unsubscribed list, and remove
+                 * from subscribed list. */
+                $this->_setSubscribed($elt, false);
+            }
+        }
+    }
+
+    /**
+     * Set an attribute for an element.
+     *
+     * @param array &$elt     The tree element.
+     * @param integer $const  The constant to set/remove from the bitmask.
+     * @param boolean $bool   Should the attribute be set?
+     */
+    protected function _setAttribute(&$elt, $const, $bool)
+    {
+        if ($bool) {
+            $elt['a'] |= $const;
+        } else {
+            $elt['a'] &= ~$const;
+        }
+    }
+
+    /**
+     * Does the element have any active children?
+     *
+     * @param array $elt  A tree element.
+     *
+     * @return boolean  True if the element has active children.
+     */
+    public function hasChildren($elt)
+    {
+        if (isset($this->_parent[$elt['v']])) {
+            if ($this->_showunsub) {
+                return true;
+            }
+
+            foreach ($this->_parent[$elt['v']] as $val) {
+                $child = &$this->_tree[$val];
+                if ($this->isSubscribed($child) ||
+                    $this->hasChildren($this->_tree[$val])) {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Is the tree element open?
+     *
+     * @param array $elt  A tree element.
+     *
+     * @return integer  True if the element is open.
+     */
+    public function isOpen($elt)
+    {
+        return (($elt['a'] & self::ELT_IS_OPEN) && $this->hasChildren($elt));
+    }
+
+    /**
+     * Set the open attribute for an element.
+     *
+     * @param array &$elt    A tree element.
+     * @param boolean $bool  The setting.
+     */
+    protected function _setOpen(&$elt, $bool)
+    {
+        $this->_setAttribute($elt, self::ELT_IS_OPEN, $bool);
+        $this->_modifyExpandedList($elt['v'], $bool ? 'add' : 'remove');
+    }
+
+    /**
+     * Is this element a container only, not a mailbox (meaning you can
+     * not open it)?
+     *
+     * @param array $elt  A tree element.
+     *
+     * @return integer  True if the element is a container.
+     */
+    public function isContainer($elt)
+    {
+        return (($elt['a'] & self::ELT_NOSELECT) ||
+                (!$this->_showunsub &&
+                 !$this->isSubscribed($elt) &&
+                 $this->hasChildren($elt)));
+    }
+
+    /**
+     * Set the element as a container?
+     *
+     * @param array &$elt    A tree element.
+     * @param boolean $bool  Is the element a container?
+     */
+    protected function _setContainer(&$elt, $bool)
+    {
+        $this->_setAttribute($elt, self::ELT_NOSELECT, $bool);
+    }
+
+    /**
+     * Is the user subscribed to this element?
+     *
+     * @param array $elt  A tree element.
+     *
+     * @return integer  True if the user is subscribed to the element.
+     */
+    public function isSubscribed($elt)
+    {
+        return $elt['a'] & self::ELT_IS_SUBSCRIBED;
+    }
+
+    /**
+     * Set the subscription status for an element.
+     *
+     * @param array &$elt    A tree element.
+     * @param boolean $bool  Is the element subscribed to?
+     */
+    protected function _setSubscribed(&$elt, $bool)
+    {
+        $this->_setAttribute($elt, self::ELT_IS_SUBSCRIBED, $bool);
+        if (!is_null($this->_subscribed)) {
+            if ($bool) {
+                $this->_subscribed[$elt['v']] = 1;
+            } else {
+                unset($this->_subscribed[$elt['v']]);
+            }
+        }
+    }
+
+    /**
+     * Is the element a namespace container?
+     *
+     * @param array $elt  A tree element.
+     *
+     * @return integer  True if the element is a namespace container.
+     */
+    public function isNamespace($elt)
+    {
+        return $elt['a'] & self::ELT_NAMESPACE;
+    }
+
+    /**
+     * Is the element a non-IMAP element?
+     *
+     * @param array $elt  A tree element.
+     *
+     * @return integer  True if the element is a non-IMAP element.
+     */
+    protected function _isNonIMAPElt($elt)
+    {
+        return $elt['a'] & self::ELT_NONIMAP;
+    }
+
+    /**
+     * Initialize the expanded folder list.
+     */
+    protected function _initExpandedList()
+    {
+        if (is_null($this->_expanded)) {
+            $serialized = $GLOBALS['prefs']->getValue('expanded_folders');
+            $this->_expanded = ($serialized) ? unserialize($serialized) : array();
+        }
+    }
+
+    /**
+     * Add/remove an element to the expanded list.
+     *
+     * @param string $id      The element name to add/remove.
+     * @param string $action  Either 'add' or 'remove';
+     */
+    protected function _modifyExpandedList($id, $action)
+    {
+        $this->_initExpandedList();
+        if ($action == 'add') {
+            $this->_expanded[$id] = true;
+        } else {
+            unset($this->_expanded[$id]);
+        }
+        $GLOBALS['prefs']->setValue('expanded_folders', serialize($this->_expanded));
+    }
+
+    /**
+     * Initializes and returns the list of mailboxes to poll.
+     *
+     * @param boolean $prune  Prune non-existant folders from list?
+     * @param boolean $sort   Sort the directory list?
+     *
+     * @return array  The list of mailboxes to poll.
+     */
+    public function getPollList($prune = false, $sort = false)
+    {
+        $this->_initPollList();
+
+        $plist = ($prune) ? array_values(array_intersect(array_keys($this->_poll), $this->folderList())) : $this->_poll;
+        if ($sort) {
+            $ns_new = $this->_getNamespace(null);
+            Horde_Imap_Client_Sort::sortMailboxes($plist, array('delimiter' => $ns_new['delimiter'], 'inbox' => true));
+        }
+
+        return $plist;
+    }
+
+    /**
+     * Init the poll list.  Called once per session.
+     */
+    protected function _initPollList()
+    {
+        if (is_null($this->_poll)) {
+            /* We ALWAYS poll the INBOX. */
+            $this->_poll = array('INBOX' => 1);
+
+            /* Add the list of polled mailboxes from the prefs. */
+            if ($GLOBALS['prefs']->getValue('nav_poll_all')) {
+                $navPollList = array_flip($this->_getList(true));
+            } else {
+                $old_error = error_reporting(0);
+                $navPollList = unserialize($GLOBALS['prefs']->getValue('nav_poll'));
+                error_reporting($old_error);
+            }
+            if ($navPollList) {
+                $this->_poll += $navPollList;
+            }
+        }
+    }
+
+    /**
+     * Add element to the poll list.
+     *
+     * @param mixed $id  The element name or a list of element names to add.
+     */
+    public function addPollList($id)
+    {
+        if (!is_array($id)) {
+            $id = array($id);
+        }
+
+        if (!empty($id) && !$GLOBALS['prefs']->isLocked('nav_poll')) {
+            $imp_folder = IMP_Folder::singleton();
+            $this->getPollList();
+            foreach ($id as $val) {
+                if (!$this->isSubscribed($this->_tree[$val])) {
+                    $imp_folder->subscribe(array($val));
+                }
+                $this->_poll[$val] = true;
+                $this->_setPolled($this->_tree[$val], true);
+            }
+            $GLOBALS['prefs']->setValue('nav_poll', serialize($this->_poll));
+            $this->_changed = true;
+        }
+    }
+
+    /**
+     * Remove element from the poll list.
+     *
+     * @param string $id  The folder/mailbox or a list of folders/mailboxes
+     *                    to remove.
+     */
+    public function removePollList($id)
+    {
+        if (!is_array($id)) {
+            $id = array($id);
+        }
+
+        $removed = false;
+
+        if (!$GLOBALS['prefs']->isLocked('nav_poll')) {
+            $this->getPollList();
+            foreach ($id as $val) {
+                if ($val != 'INBOX') {
+                    unset($this->_poll[$val]);
+                    if (isset($this->_tree[$val])) {
+                        $this->_setPolled($this->_tree[$val], false);
+                    }
+                    $removed = true;
+                }
+            }
+            if ($removed) {
+                $GLOBALS['prefs']->setValue('nav_poll', serialize($this->_poll));
+                $this->_changed = true;
+            }
+        }
+    }
+
+    /**
+     * Does the user want to poll this mailbox for new/unseen messages?
+     *
+     * @param array $elt  A tree element.
+     *
+     * @return integer  True if the user wants to poll the element.
+     */
+    public function isPolled($elt)
+    {
+        return ($GLOBALS['prefs']->getValue('nav_poll_all')) ? true : ($elt['a'] & self::ELT_IS_POLLED);
+    }
+
+    /**
+     * Set the polled attribute for an element.
+     *
+     * @param array &$elt    A tree element.
+     * @param boolean $bool  The setting.
+     */
+    protected function _setPolled(&$elt, $bool)
+    {
+        $this->_setAttribute($elt, self::ELT_IS_POLLED, $bool);
+    }
+
+    /**
+     * Is the element invisible?
+     *
+     * @param array $elt  A tree element.
+     *
+     * @return integer  True if the element is marked as invisible.
+     */
+    public function isInvisible($elt)
+    {
+        return $elt['a'] & self::ELT_INVISIBLE;
+    }
+
+    /**
+     * Set the invisible attribute for an element.
+     *
+     * @param array &$elt    A tree element.
+     * @param boolean $bool  The setting.
+     */
+    protected function _setInvisible(&$elt, $bool)
+    {
+        $this->_setAttribute($elt, self::ELT_INVISIBLE, $bool);
+    }
+
+    /**
+     * Flag the element as needing its children to be sorted.
+     *
+     * @param array &$elt    A tree element.
+     * @param boolean $bool  The setting.
+     */
+    protected function _setNeedSort(&$elt, $bool)
+    {
+        $this->_setAttribute($elt, self::ELT_NEED_SORT, $bool);
+    }
+
+    /**
+     * Does this element's children need sorting?
+     *
+     * @param array $elt  A tree element.
+     *
+     * @return integer  True if the children need to be sorted.
+     */
+    protected function _needSort($elt)
+    {
+        return (($elt['a'] & self::ELT_NEED_SORT) && (count($this->_parent[$elt['v']]) > 1));
+    }
+
+    /**
+     * Initialize the list of subscribed mailboxes.
+     */
+    protected function _initSubscribed()
+    {
+        if (is_null($this->_subscribed)) {
+            $this->_getList(false);
+        }
+    }
+
+    /**
+     * Should we expand all elements?
+     */
+    public function expandAll()
+    {
+        foreach ($this->_parent[self::BASE_ELT] as $val) {
+            $this->expand($val, true);
+        }
+    }
+
+    /**
+     * Should we collapse all elements?
+     */
+    public function collapseAll()
+    {
+        foreach ($this->_tree as $key => $val) {
+            if ($key !== self::BASE_ELT) {
+                $this->collapse($val['v']);
+            }
+        }
+    }
+
+    /**
+     * Switch subscribed/unsubscribed viewing.
+     *
+     * @param boolean $unsub  Show unsubscribed elements?
+     */
+    public function showUnsubscribed($unsub)
+    {
+        if ((bool)$unsub === $this->_showunsub) {
+            return;
+        }
+
+        $this->_showunsub = $unsub;
+        $this->_changed = true;
+
+        /* If we are switching from unsubscribed to subscribed, no need
+         * to do anything (we just ignore unsubscribed stuff). */
+        if ($unsub === false) {
+            return;
+        }
+
+        /* If we are switching from subscribed to unsubscribed, we need
+         * to add all unsubscribed elements that live in currently
+         * discovered items. */
+        $this->_unsubview = true;
+        $this->_trackdiff = false;
+        $this->insert($this->_getList(true));
+        $this->_trackdiff = true;
+    }
+
+    /**
+     * Get information about new/unseen/total messages for the given element.
+     *
+     * @param string $name  The element name (UTF7-IMAP).
+     *
+     * @return array  Array with the following fields:
+     * <pre>
+     * 'messages' - (integer) Number of total messages.
+     * 'recent' - (integer) Number of new messages.
+     * 'unseen' - (integer) Number of unseen messages.
+     * </pre>
+     */
+    public function getElementInfo($name)
+    {
+        try {
+            return $GLOBALS['imp_imap']->ob->status($name, Horde_Imap_Client::STATUS_MESSAGES | Horde_Imap_Client::STATUS_RECENT | Horde_Imap_Client::STATUS_UNSEEN);
+        } catch (Horde_Imap_Client_Exception $e) {
+            return array();
+        }
+    }
+
+    /**
+     * Sorts a list of mailboxes.
+     *
+     * @param array &$mbox   The list of mailboxes to sort.
+     * @param boolean $base  Are we sorting a list of mailboxes in the base
+     *                       of the tree.
+     */
+    protected function _sortList(&$mbox, $base = false)
+    {
+        $config_array = array(
+            'delimiter' => $this->_delimiter,
+            'inbox' => true
+        );
+
+        if ($base) {
+            $basesort = array();
+            foreach ($mbox as $val) {
+                $basesort[$val] = ($val == 'INBOX') ? 'INBOX' : $this->_tree[$val]['l'];
+            }
+            $config_array['index'] = true;
+            Horde_Imap_Client_Sort::sortMailboxes($basesort, $config_array);
+            $mbox = array_keys($basesort);
+        } else {
+            Horde_Imap_Client_Sort::sortMailboxes($mbox, $config_array);
+        }
+
+        if ($base) {
+            for ($i = 0, $count = count($mbox); $i < $count; ++$i) {
+                if ($this->_isNonIMAPElt($this->_tree[$mbox[$i]])) {
+                    /* Already sorted by name - simply move to the end of
+                     * the array. */
+                    $mbox[] = $mbox[$i];
+                    unset($mbox[$i]);
+                }
+            }
+            $mbox = array_values($mbox);
+        }
+    }
+
+    /**
+     * Is the given element an "active" element (i.e. an element that should
+     * be worked with given the current viewing parameters).
+     *
+     * @param array $elt  A tree element.
+     *
+     * @return boolean  True if it is an active element.
+     */
+    protected function _activeElt($elt)
+    {
+        return (!$this->isInvisible($elt) &&
+                ($this->_showunsub ||
+                 ($this->isSubscribed($elt) && !$this->isContainer($elt)) ||
+                 $this->hasChildren($elt)));
+    }
+
+    /**
+     * Convert a mailbox name to the correct, internal name (i.e. make sure
+     * INBOX is always capitalized for IMAP servers).
+     *
+     * @param string $name  The mailbox name.
+     *
+     * @return string  The converted name.
+     */
+    protected function _convertName($name)
+    {
+        return (strcasecmp($name, 'INBOX') == 0) ? 'INBOX' : $name;
+    }
+
+    /**
+     * Get namespace info for a full folder path.
+     *
+     * @param string $mailbox  The folder path.
+     *
+     * @return mixed  The namespace info for the folder path or null if the
+     *                path doesn't exist.
+     */
+    protected function _getNamespace($mailbox)
+    {
+        if (!in_array($mailbox, array($this->OTHER_KEY, $this->SHARED_KEY, $this->VFOLDER_KEY)) &&
+            (strpos($mailbox, $this->VFOLDER_KEY . $this->_delimiter) !== 0)) {
+            return $GLOBALS['imp_imap']->getNamespace($mailbox);
+        }
+        return null;
+    }
+
+    /**
+     * Set the start point for determining element differences via eltDiff().
+     */
+    public function eltDiffStart()
+    {
+        $this->_eltdiff = array('a' => array(), 'c' => array(), 'd' => array());
+    }
+
+    /**
+     * Return the list of elements that have changed since eltDiffStart()
+     * was last called.
+     *
+     * @return array  Returns false if no changes have occurred, or an array
+     *                with the following keys:
+     * <pre>
+     * 'a' => A list of elements that have been added.
+     * 'c' => A list of elements that have been changed.
+     * 'd' => A list of elements that have been deleted.
+     * </pre>
+     */
+    public function eltDiff()
+    {
+        if (is_null($this->_eltdiff) || !$this->_changed) {
+            return false;
+        }
+
+        $ret = array(
+            'a' => array_keys($this->_eltdiff['a']),
+            'c' => array_keys($this->_eltdiff['c']),
+            'd' => array_keys($this->_eltdiff['d'])
+        );
+
+        $this->_eltdiff = null;
+
+        return $ret;
+    }
+
+    /**
+     * Inserts virtual folders into the tree.
+     *
+     * @param array $id_list  An array with the folder IDs to add as the key
+     *                        and the labels as the value.
+     */
+    public function insertVFolders($id_list)
+    {
+        if (empty($id_list) ||
+            empty($GLOBALS['conf']['user']['allow_folders'])) {
+            return;
+        }
+
+        $adds = $id = array();
+
+        foreach ($id_list as $key => $val) {
+            $id[$GLOBALS['imp_search']->createSearchID($key)] = $val;
+        }
+
+        foreach (array_keys($id) as $key) {
+            $id_key = $this->VFOLDER_KEY . $this->_delimiter . $key;
+            if (!isset($this->_tree[$id_key])) {
+                $adds[] = $id_key;
+            }
+        }
+
+        if (empty($adds)) {
+            return;
+        }
+
+        $this->insert($adds);
+
+        foreach ($id as $key => $val) {
+            $this->_tree[$key]['l'] = $val;
+        }
+
+        /* Sort the Virtual Folder list in the object, if necessary. */
+        if ($this->_needSort($this->_tree[$this->VFOLDER_KEY])) {
+            $vsort = array();
+            foreach ($this->_parent[$this->VFOLDER_KEY] as $val) {
+                $vsort[$val] = $this->_tree[$val]['l'];
+            }
+            natcasesort($vsort);
+            $this->_parent[$this->VFOLDER_KEY] = array_keys($vsort);
+            $this->_setNeedSort($this->_tree[$this->VFOLDER_KEY], false);
+            $this->_changed = true;
+        }
+    }
+
+    /**
+     * Builds a list of folders, suitable to render a folder tree.
+     *
+     * @param integer $mask  The mask to pass to next().
+     * @param boolean $open  If using the base folder icons, display a
+     *                       different icon whether the folder is opened or
+     *                       closed.
+     *
+     * @return array  An array with three elements: the folder list, the total
+     *                number of new messages, and a list with folder names
+     *                suitable for user interaction.
+     *                The folder list array contains the following added
+     *                entries on top of the entries provided by element():
+     * <pre>
+     * 'display' - The mailbox name run through IMP::displayFolder().
+     * 'peek' - See peek().
+     * </pre>
+     */
+    public function build($mask = 0, $open = true)
+    {
+        $displayNames = $newmsgs = $rows = array();
+        $this->_forceopen = $open;
+
+        /* Start iterating through the list of mailboxes, displaying them. */
+        $mailbox = $this->reset();
+        do {
+            $row = $this->element($mailbox['v']);
+
+            $row['display'] = ($this->_isNonIMAPElt($mailbox)) ? $mailbox['l'] : IMP::displayFolder($mailbox['v']);
+            $row['peek'] = $this->peek();
+
+            if (!empty($row['recent'])) {
+                $newmsgs[$row['value']] = $row['recent'];
+            }
+
+            /* Hide folder prefixes from the user. */
+            if ($row['level'] >= 0) {
+                $rows[] = $row;
+                $displayNames[] = addslashes($row['display']);
+            }
+        } while (($mailbox = $this->next($mask)));
+
+        $this->_forceopen = false;
+
+        return array($rows, $newmsgs, $displayNames);
+    }
+
+    /**
+     * Get any custom icon configured for the given element.
+     *
+     * @params array $elt  A tree element.
+     *
+     * @return array  An array with the 'icon', 'icondir', and 'alt'
+     *                information for the element, or false if no icon
+     *                available.
+     */
+    public function getCustomIcon($elt)
+    {
+        static $mbox_icons;
+
+        if (isset($mbox_icons) && !$mbox_icons) {
+            return false;
+        }
+
+        /* Call the mailbox icon hook, if requested. */
+        if (empty($GLOBALS['conf']['hooks']['mbox_icon'])) {
+            $mbox_icons = false;
+            return false;
+        }
+
+        if (!isset($mbox_icons)) {
+            $mbox_icons = Horde::callHook('_imp_hook_mbox_icons', array(), 'imp', false);
+            if (!$mbox_icons) {
+                return false;
+            }
+        }
+
+        if (isset($mbox_icons[$elt['v']])) {
+            return array('icon' => $mbox_icons[$elt['v']]['icon'], 'icondir' => $mbox_icons[$elt['v']]['icondir'], 'alt' => $mbox_icons[$elt['v']]['alt']);
+        }
+
+        return false;
+    }
+
+    /**
+     * Returns whether this element is a virtual folder.
+     *
+     * @param array $elt  A tree element.
+     *
+     * @return integer  True if the element is a virtual folder.
+     */
+    public function isVFolder($elt)
+    {
+        return $elt['a'] & self::ELT_VFOLDER;
+    }
+
+    /**
+     * Rename a current folder.
+     *
+     * @param array $old  The old folder names.
+     * @param array $new  The new folder names.
+     */
+    public function rename($old, $new)
+    {
+        foreach ($old as $key => $val) {
+            $polled = (isset($this->_tree[$val])) ? $this->isPolled($this->_tree[$val]) : false;
+            if ($this->delete($val)) {
+                $this->insert($new[$key]);
+                if ($polled) {
+                    $this->addPollList($new[$key]);
+                }
+            }
+        }
+    }
+
+    /**
+     * Returns a list of all IMAP mailboxes in the tree.
+     *
+     * @param integer $mask  A mask with the following elements:
+     * <pre>
+     * IMP_Imap_Tree::FLIST_CONTAINER - Show container elements.
+     * IMP_Imap_Tree::FLIST_UNSUB - Show unsubscribed elements.
+     * IMP_Imap_Tree::FLIST_OB - Return full tree object.
+     * IMP_Imap_Tree::FLIST_VFOLDER - Show Virtual Folders.
+     * </pre>
+     * @param string $base  Return all mailboxes below this element.
+     *
+     * @return array  An array of IMAP mailbox names.
+     */
+    public function folderList($mask = 0, $base = null)
+    {
+        $baseindex = null;
+        $ret_array = array();
+
+        $diff_unsub = (($mask & self::FLIST_UNSUB) != $this->_showunsub) ? $this->_showunsub : null;
+        $this->showUnsubscribed($mask & self::FLIST_UNSUB);
+
+        $mailbox = $this->reset();
+
+        // Search to base element.
+        if (!is_null($base)) {
+            while ($mailbox && $mailbox['v'] != $base) {
+                $mailbox = $this->next(self::NEXT_SHOWCLOSED);
+            }
+            if ($mailbox) {
+                $baseindex = count($this->_currstack);
+                $baseparent = $this->_currparent;
+                $basekey = $this->_currkey;
+                $mailbox = $this->next(self::NEXT_SHOWCLOSED);
+            }
+        }
+
+        if ($mailbox) {
+            do {
+                if (!is_null($baseindex) &&
+                    (!isset($this->_currstack[$baseindex]) ||
+                     ($this->_currstack[$baseindex]['k'] != $basekey) ||
+                     ($this->_currstack[$baseindex]['p'] != $baseparent))) {
+                    break;
+                }
+
+                if ((($mask & self::FLIST_CONTAINER) ||
+                     !$this->isContainer($mailbox)) &&
+                    (($mask & self::FLIST_VFOLDER) ||
+                     !$this->isVFolder($mailbox))) {
+                    $ret_array[] = ($mask & self::FLIST_OB) ? $mailbox : $mailbox['v'];
+                }
+            } while (($mailbox = $this->next(self::NEXT_SHOWCLOSED)));
+        }
+
+        if (!is_null($diff_unsub)) {
+            $this->showUnsubscribed($diff_unsub);
+        }
+
+        return $ret_array;
+    }
+
+    /**
+     * Is the mailbox open in the sidebar?
+     *
+     * @param array $mbox  A mailbox name.
+     *
+     * @return integer  True if the mailbox is open in the sidebar.
+     */
+    public function isOpenSidebar($mbox)
+    {
+        switch ($GLOBALS['prefs']->getValue('nav_expanded_sidebar')) {
+        case self::OPEN_USER:
+            $this->_initExpandedList();
+            return !empty($this->_expanded[$mbox]);
+            break;
+
+        case self::OPEN_ALL:
+            return true;
+            break;
+
+        case self::OPEN_NONE:
+        default:
+            return false;
+            break;
+        }
+    }
+
+    /**
+     * Init frequently used element() data.
+     */
+    protected function _initElement()
+    {
+        global $prefs, $registry;
+
+        /* Initialize the user's identities. */
+        require_once 'Horde/Identity.php';
+        $identity = Identity::singleton(array('imp', 'imp'));
+
+        return array(
+            'trash' => IMP::folderPref($prefs->getValue('trash_folder'), true),
+            'draft' => IMP::folderPref($prefs->getValue('drafts_folder'), true),
+            'spam' => IMP::folderPref($prefs->getValue('spam_folder'), true),
+            'sent' => $identity->getAllSentmailFolders(),
+            'image_dir' => $registry->getImageDir(),
+        );
+    }
+
+    /**
+     * Return extended information on an element.
+     *
+     * @param string $name  The name of the tree element.
+     *
+     * @return array  Returns the element with extended information, or false
+     *                if not found.  The information returned is as follows:
+     * <pre>
+     * 'alt' - The alt text for the icon.
+     * 'base_elt' - The return from get().
+     * 'children' - Does the element have children?
+     * 'container' - Is this a container element?
+     * 'editvfolder' - Can this virtual folder be edited?
+     * 'icon' - The name of the icon graphic to use.
+     * 'icondir' - The path of the icon directory.
+     * 'level' - The deepness level of this element.
+     * 'mbox_val' - A html-ized version of 'value'.
+     * 'msgs' - The number of total messages in the element (if polled).
+     * 'name' - A html-ized version of 'label'.
+     * 'parent' - The parent element value.
+     * 'polled' - Show polled information?
+     * 'recent' - The number of new messages in the element (if polled).
+     * 'special' - An integer mask indicating if this is a "special" element.
+     * 'specialvfolder' - Is this a "special" virtual folder?
+     * 'unseen' - The number of unseen messages in the element (if polled).
+     * 'user_icon' - Use a user defined icon?
+     * 'value' - The value of this element (i.e. element id).
+     * 'vfolder' - Is this a virtual folder?
+     * </pre>
+     */
+    public function element($mailbox)
+    {
+        static $elt;
+
+        $mailbox = $this->get($mailbox);
+        if (!$mailbox) {
+            return false;
+        }
+
+        if (!isset($elt)) {
+            $elt = $this->_initElement();
+        }
+
+        $row = array(
+            'base_elt' => $mailbox,
+            'children' => $this->hasChildren($mailbox),
+            'container' => false,
+            'editvfolder' => false,
+            'icondir' => $elt['image_dir'],
+            'iconopen' => null,
+            'level' => $mailbox['c'],
+            'mbox_val' => htmlspecialchars($mailbox['v']),
+            'name' => htmlspecialchars($mailbox['l']),
+            'parent' => $mailbox['p'],
+            'polled' => false,
+            'recent' => 0,
+            'special' => 0,
+            'specialvfolder' => false,
+            'user_icon' => false,
+            'value' => $mailbox['v'],
+            'vfolder' => false,
+        );
+
+        $icon = $this->getCustomIcon($mailbox);
+
+        if (!$this->isContainer($mailbox)) {
+            /* We are dealing with mailboxes here.
+             * Determine if we need to poll this mailbox for new messages. */
+            if ($this->isPolled($mailbox)) {
+                /* If we need message information for this folder, update
+                 * it now. */
+                $msgs_info = $this->getElementInfo($mailbox['v']);
+                if (!empty($msgs_info)) {
+                    $row['polled'] = true;
+                    if (!empty($msgs_info['recent'])) {
+                        $row['recent'] = $msgs_info['recent'];
+                    }
+                    $row['msgs'] = $msgs_info['messages'];
+                    $row['unseen'] = $msgs_info['unseen'];
+                }
+            }
+
+
+            switch ($mailbox['v']) {
+            case 'INBOX':
+                $row['icon'] = 'folders/inbox.png';
+                $row['alt'] = _("Inbox");
+                $row['special'] = self::SPECIAL_INBOX;
+                break;
+
+            case $elt['trash']:
+                if ($GLOBALS['prefs']->getValue('use_vtrash')) {
+                    $row['icon'] = ($this->isOpen($mailbox)) ? 'folders/folder_open.png' : 'folders/folder.png';
+                    $row['alt'] = _("Mailbox");
+                } else {
+                    $row['icon'] = 'folders/trash.png';
+                    $row['alt'] = _("Trash folder");
+                    $row['special'] = self::SPECIAL_TRASH;
+                }
+                break;
+
+            case $elt['draft']:
+                $row['icon'] = 'folders/drafts.png';
+                $row['alt'] = _("Draft folder");
+                $row['special'] = self::SPECIAL_DRAFT;
+                break;
+
+            case $elt['spam']:
+                $row['icon'] = 'folders/spam.png';
+                $row['alt'] = _("Spam folder");
+                $row['special'] = self::SPECIAL_SPAM;
+                break;
+
+            default:
+                if (in_array($mailbox['v'], $elt['sent'])) {
+                    $row['icon'] = 'folders/sent.png';
+                    $row['alt'] = _("Sent mail folder");
+                    $row['special'] = self::SPECIAL_SENT;
+                } else {
+                    $row['icon'] = ($this->isOpen($mailbox)) ? 'folders/folder_open.png' : 'folders/folder.png';
+                    $row['alt'] = _("Mailbox");
+                }
+                break;
+            }
+
+            /* Virtual folders. */
+            if ($this->isVFolder($mailbox)) {
+                $row['vfolder'] = true;
+                $row['editvfolder'] = $GLOBALS['imp_search']->isEditableVFolder($mailbox['v']);
+                if ($GLOBALS['imp_search']->isVTrashFolder($mailbox['v'])) {
+                    $row['specialvfolder'] = true;
+                    $row['icon'] = 'folders/trash.png';
+                    $row['alt'] = _("Virtual Trash Folder");
+                } elseif ($GLOBALS['imp_search']->isVINBOXFolder($mailbox['v'])) {
+                    $row['specialvfolder'] = true;
+                    $row['icon'] = 'folders/inbox.png';
+                    $row['alt'] = _("Virtual INBOX Folder");
+                }
+            }
+        } else {
+            /* We are dealing with folders here. */
+            $row['container'] = true;
+            if ($this->_forceopen && $this->isOpen($mailbox)) {
+                $row['icon'] = 'folders/folder_open.png';
+                $row['alt'] = _("Opened Folder");
+            } else {
+                $row['icon'] = 'folders/folder.png';
+                $row['iconopen'] = 'folders/folder_open.png';
+                $row['alt'] = ($this->_forceopen) ? _("Closed Folder") : _("Folder");
+            }
+            if ($this->isVFolder($mailbox)) {
+                $row['vfolder'] = true;
+            }
+        }
+
+        /* Overwrite the icon information now. */
+        if (!empty($icon)) {
+            $row['icon'] = $icon['icon'];
+            $row['icondir'] = $icon['icondir'];
+            if (!empty($icon['alt'])) {
+                $row['alt'] = $icon['alt'];
+            }
+            $row['iconopen'] = isset($icon['iconopen']) ? $icon['iconopen'] : null;
+            $row['user_icon'] = true;
+        }
+
+        return $row;
+    }
+
+    /**
+     * Sort a level in the tree.
+     *
+     * @param string $id  The parent folder whose children need to be sorted.
+     */
+    protected function _sortLevel($id)
+    {
+        if ($this->_needSort($this->_tree[$id])) {
+            $this->_sortList($this->_parent[$id], ($id === self::BASE_ELT));
+            $this->_setNeedSort($this->_tree[$id], false);
+            $this->_changed = true;
+        }
+    }
+
+    /**
+     * Determines the mailbox name to create given a parent and the new name.
+     *
+     * @param string $parent  The parent name (UTF7-IMAP).
+     * @param string $parent  The new mailbox name (UTF7-IMAP).
+     *
+     * @return string  The full path to the new mailbox.
+     * @throws Horde_Exception
+     */
+    public function createMailboxName($parent, $new)
+    {
+        $ns_info = (empty($parent)) ? $GLOBALS['imp_imap']->defaultNamespace() : $this->_getNamespace($parent);
+        if (is_null($ns_info)) {
+            if ($this->isNamespace($this->_tree[$parent])) {
+                $ns_info = $this->_getNamespace($new);
+                if (in_array($ns_info['type'], array('other', 'shared'))) {
+                    return $new;
+                }
+            }
+            throw new Horde_Exception(_("Cannot directly create mailbox in this folder."), 'horde.error');
+        }
+
+        $mbox = $ns_info['name'];
+        if (!empty($parent)) {
+            $mbox .= substr_replace($parent, '', 0, strlen($ns_info['name']));
+            $mbox = rtrim($mbox, $ns_info['delimiter']) . $ns_info['delimiter'];
+        }
+        return $mbox . $new;
+    }
+}
index 9b4dfa9..5324e20 100644 (file)
@@ -92,7 +92,7 @@ class IMP_Search
             $_SESSION['imp']['search'] = array('q' => array());
         }
         if (!$no_vf) {
-            $imaptree = &IMP_IMAP_Tree::singleton();
+            $imaptree = &IMP_Imap_Tree::singleton();
             foreach ($this->_getVFolderList() as $key => $val) {
                 if (!empty($val['vfolder']) &&
                     !$this->isVTrashFolder($key) &&
@@ -223,7 +223,7 @@ class IMP_Search
             unset($vfolders[$id]);
             $this->_saveVFolderList($vfolders);
             if (!$no_delete) {
-                $imaptree = &IMP_IMAP_Tree::singleton();
+                $imaptree = &IMP_Imap_Tree::singleton();
                 $imaptree->delete($id);
             }
         }
@@ -330,7 +330,7 @@ class IMP_Search
             $this->_saveVFolderList($vfolders);
         }
 
-        $imaptree = &IMP_IMAP_Tree::singleton();
+        $imaptree = &IMP_Imap_Tree::singleton();
         $imaptree->insertVFolders(array($id => $label));
 
         return $id;
@@ -398,8 +398,8 @@ class IMP_Search
      */
     public function createVINBOXFolder()
     {
-        /* Initialize IMP_IMAP_Tree. */
-        $imaptree = &IMP_IMAP_Tree::singleton();
+        /* Initialize IMP_Imap_Tree. */
+        $imaptree = &IMP_Imap_Tree::singleton();
 
         /* Delete the current Virtual Inbox folder, if it exists. */
         $vinbox_id = $GLOBALS['prefs']->getValue('vinbox_id');
index c0fe260..b470a49 100644 (file)
@@ -55,7 +55,7 @@ class IMP_Views_ListMessages
             /* Create folder search list. */
             switch ($args['searchfolder']) {
             case 'all':
-                $imptree = &IMP_IMAP_Tree::singleton();
+                $imptree = &IMP_Imap_Tree::singleton();
                 $folder_list = $imptree->folderList();
                 break;
 
@@ -232,7 +232,7 @@ class IMP_Views_ListMessages
 
         /* Get unseen/thread information. */
         if (is_null($search_id)) {
-            $imptree = &IMP_IMAP_Tree::singleton();
+            $imptree = &IMP_Imap_Tree::singleton();
             $info = $imptree->getElementInfo($mbox);
             if (!empty($info)) {
                 $md->unseen = $info['unseen'];
@@ -240,7 +240,7 @@ class IMP_Views_ListMessages
 
             if ($sortpref['by'] == Horde_Imap_Client::SORT_THREAD) {
                 $threadob = $imp_mailbox->getThreadOb();
-                $imp_thread = new IMP_IMAP_Thread($threadob);
+                $imp_thread = new IMP_Imap_Thread($threadob);
                 $md->thread = $imp_thread->getThreadTreeOb($msglist, $sortpref['dir']);
             }
         } else {
index 319cb35..6166af1 100644 (file)
@@ -116,7 +116,7 @@ function _imp_authCredentials()
     $app_name = $GLOBALS['registry']->get('name');
 
     require_once dirname(__FILE__) . '/IMAP.php';
-    $servers = IMP_IMAP::loadServerConfig();
+    $servers = IMP_Imap::loadServerConfig();
     $server_list = array();
     foreach ($servers as $key => $val) {
         $server_list[$key] = $val['name'];
@@ -302,7 +302,7 @@ function _imp_changeLanguage()
     if (IMP::checkAuthentication(true)) {
         $imp_folder = &IMP_Folder::singleton();
         $imp_folder->clearFlistCache();
-        $imaptree = &IMP_IMAP_Tree::singleton();
+        $imaptree = &IMP_Imap_Tree::singleton();
         $imaptree->init();
         $imp_search = new IMP_Search();
         $imp_search->sessionSetup(true);
index a22f4d6..729e3a8 100644 (file)
@@ -17,7 +17,7 @@
  *   $session_control - Sets special session control limitations
  *
  * Global variables defined:
- *   $imp_imap    - An IMP_IMAP object
+ *   $imp_imap    - An IMP_Imap object
  *   $imp_mbox    - Current mailbox information
  *   $imp_notify  - A Notification_Listener_Mobile object
  *   $imp_search  - An IMP_Search object
@@ -91,7 +91,7 @@ if (!defined('IMP_TEMPLATES')) {
 
 // Initialize global $imp_imap object.
 if (!isset($GLOBALS['imp_imap'])) {
-    $GLOBALS['imp_imap'] = new IMP_IMAP();
+    $GLOBALS['imp_imap'] = new IMP_Imap();
 }
 
 // Start compression.
index 3a5223c..ac6b4d9 100644 (file)
@@ -179,7 +179,7 @@ function prefs_callback()
     if ($prefs->isDirty('subscribe') || $prefs->isDirty('tree_view')) {
         $imp_folder = &IMP_Folder::singleton();
         $imp_folder->clearFlistCache();
-        $imaptree = &IMP_IMAP_Tree::singleton();
+        $imaptree = &IMP_Imap_Tree::singleton();
         $imaptree->init();
     }
 
index 53d95dd..7d9dda6 100644 (file)
@@ -596,7 +596,7 @@ $messages = $threadlevel = array();
 
 /* Get thread object, if necessary. */
 if ($sortpref['by'] == Horde_Imap_Client::SORT_THREAD) {
-    $imp_thread = new IMP_IMAP_Thread($imp_mailbox->getThreadOb());
+    $imp_thread = new IMP_Imap_Thread($imp_mailbox->getThreadOb());
     $threadtree = $imp_thread->getThreadImageTree(reset($mbox_info['uids']), $sortpref['dir']);
 }
 
index b3fb7dc..2c09920 100644 (file)
@@ -68,7 +68,7 @@ if ($mode == 'thread') {
     $index_array = $imp_mailbox->getIMAPIndex();
     $thread = $threadob->getThread($index_array['index']);
 
-    $imp_thread = new IMP_IMAP_Thread($threadob);
+    $imp_thread = new IMP_Imap_Thread($threadob);
     $threadtree = $imp_thread->getThreadImageTree($thread, false);
     $loop_array = array($imp_mbox['mailbox'] => $thread);
 } else {