From: Michael M Slusarz Date: Fri, 27 Feb 2009 18:30:17 +0000 (-0700) Subject: Some Horde 5 fixes/style. X-Git-Url: https://git.internetallee.de/?a=commitdiff_plain;h=b50b38c1449905a0455120a86d954990612406d9;p=horde.git Some Horde 5 fixes/style. --- diff --git a/imp/acl.php b/imp/acl.php index c6ce06ace..299d94bbb 100644 --- a/imp/acl.php +++ b/imp/acl.php @@ -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); diff --git a/imp/ajax.php b/imp/ajax.php index 9fb6fb59a..9c4b8857e 100644 --- a/imp/ajax.php +++ b/imp/ajax.php @@ -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; diff --git a/imp/compose-dimp.php b/imp/compose-dimp.php index ab26c9761..80e971ac3 100644 --- a/imp/compose-dimp.php +++ b/imp/compose-dimp.php @@ -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( diff --git a/imp/folders-mimp.php b/imp/folders-mimp.php index 6ee44e62b..d1366d204 100644 --- a/imp/folders-mimp.php +++ b/imp/folders-mimp.php @@ -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. */ diff --git a/imp/folders.php b/imp/folders.php index 7222e9925..70a41fd4b 100644 --- a/imp/folders.php +++ b/imp/folders.php @@ -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(); diff --git a/imp/lib/Block/Foldersummary.php b/imp/lib/Block/Foldersummary.php index 123c8c3c1..0cbe79baa 100644 --- a/imp/lib/Block/Foldersummary.php +++ b/imp/lib/Block/Foldersummary.php @@ -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; diff --git a/imp/lib/Block/summary.php b/imp/lib/Block/summary.php index d66c645c6..861fb3df3 100644 --- a/imp/lib/Block/summary.php +++ b/imp/lib/Block/summary.php @@ -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. */ diff --git a/imp/lib/Block/tree_folders.php b/imp/lib/Block/tree_folders.php index 1c31e68bb..eab354554 100644 --- a/imp/lib/Block/tree_folders.php +++ b/imp/lib/Block/tree_folders.php @@ -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; diff --git a/imp/lib/DIMP.php b/imp/lib/DIMP.php index a6e23365e..4ae929f86 100644 --- a/imp/lib/DIMP.php +++ b/imp/lib/DIMP.php @@ -13,6 +13,11 @@ 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] * */ - 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) * */ - 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 diff --git a/imp/lib/Folder.php b/imp/lib/Folder.php index 87f9776cb..19601e699 100644 --- a/imp/lib/Folder.php +++ b/imp/lib/Folder.php @@ -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 index b99a0f687..000000000 --- a/imp/lib/IMAP.php +++ /dev/null @@ -1,342 +0,0 @@ - - * @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 index ca800a878..000000000 --- a/imp/lib/IMAP/ACL.php +++ /dev/null @@ -1,212 +0,0 @@ - - * @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 index 99458b2bc..000000000 --- a/imp/lib/IMAP/Thread.php +++ /dev/null @@ -1,137 +0,0 @@ - - * @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 index aace11e1e..000000000 --- a/imp/lib/IMAP/Tree.php +++ /dev/null @@ -1,1990 +0,0 @@ - - * @author Jon Parise - * @author Anil Madhavapeddy - * @author Michael Slusarz - * @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: - *
-     * IMP_IMAP_Tree::NEXT_SHOWCLOSED - Don't ignore closed elements.
-     * IMP_IMAP_Tree::NEXT_SHOWSUB - Only show subscribed elements.
-     * 
- * - * @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: - *
-     * 'messages' - (integer) Number of total messages.
-     * 'recent' - (integer) Number of new messages.
-     * 'unseen' - (integer) Number of unseen messages.
-     * 
- */ - 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: - *
-     * '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.
-     * 
- */ - 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(): - *
-     * 'display' - The mailbox name run through IMP::displayFolder().
-     * 'peek' - See peek().
-     * 
- */ - 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: - *
-     * 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.
-     * 
- * @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: - *
-     * '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?
-     * 
- */ - 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 index 000000000..7eb8d9d0b --- /dev/null +++ b/imp/lib/Imap.php @@ -0,0 +1,342 @@ + + * @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 index 000000000..7af5b5460 --- /dev/null +++ b/imp/lib/Imap/Acl.php @@ -0,0 +1,212 @@ + + * @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 index 000000000..9a409ce90 --- /dev/null +++ b/imp/lib/Imap/Thread.php @@ -0,0 +1,137 @@ + + * @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 index 000000000..9a1945006 --- /dev/null +++ b/imp/lib/Imap/Tree.php @@ -0,0 +1,1995 @@ + + * @author Jon Parise + * @author Anil Madhavapeddy + * @author Michael Slusarz + * @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: + *
+     * IMP_Imap_Tree::NEXT_SHOWCLOSED - Don't ignore closed elements.
+     * IMP_Imap_Tree::NEXT_SHOWSUB - Only show subscribed elements.
+     * 
+ * + * @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: + *
+     * 'messages' - (integer) Number of total messages.
+     * 'recent' - (integer) Number of new messages.
+     * 'unseen' - (integer) Number of unseen messages.
+     * 
+ */ + 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: + *
+     * '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.
+     * 
+ */ + 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(): + *
+     * 'display' - The mailbox name run through IMP::displayFolder().
+     * 'peek' - See peek().
+     * 
+ */ + 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: + *
+     * 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.
+     * 
+ * @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: + *
+     * '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?
+     * 
+ */ + 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/Search.php b/imp/lib/Search.php index 9b4dfa916..5324e20fc 100644 --- a/imp/lib/Search.php +++ b/imp/lib/Search.php @@ -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'); diff --git a/imp/lib/Views/ListMessages.php b/imp/lib/Views/ListMessages.php index c0fe260ba..b470a4998 100644 --- a/imp/lib/Views/ListMessages.php +++ b/imp/lib/Views/ListMessages.php @@ -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 { diff --git a/imp/lib/api.php b/imp/lib/api.php index 319cb3578..6166af1de 100644 --- a/imp/lib/api.php +++ b/imp/lib/api.php @@ -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); diff --git a/imp/lib/base.php b/imp/lib/base.php index a22f4d67c..729e3a886 100644 --- a/imp/lib/base.php +++ b/imp/lib/base.php @@ -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. diff --git a/imp/lib/prefs.php b/imp/lib/prefs.php index 3a5223c26..ac6b4d91a 100644 --- a/imp/lib/prefs.php +++ b/imp/lib/prefs.php @@ -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(); } diff --git a/imp/mailbox.php b/imp/mailbox.php index 53d95dd0f..7d9dda68f 100644 --- a/imp/mailbox.php +++ b/imp/mailbox.php @@ -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']); } diff --git a/imp/thread.php b/imp/thread.php index b3fb7dc23..2c0992049 100644 --- a/imp/thread.php +++ b/imp/thread.php @@ -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 {