<?php

require_once 'Horde/IMAP/Tree.php';

/** Identify an element as a virtual folder. */
define('IMPTREE_ELT_VFOLDER', 8192);

/**
 * The IMP_tree class provides a tree view of the folders in an IMAP/POP3
 * repository.  It provides access functions to iterate through this tree and
 * query information about individual mailboxes.
 *
 * $Horde: imp/lib/IMAP/Tree.php,v 1.25.2.15 2005/08/12 03:43:55 slusarz Exp $
 *
 * Copyright 2000-2005 Chuck Hagenbuch <chuck@horde.org>
 * Copyright 2000-2005 Jon Parise <jon@horde.org>
 * Copyright 2000-2005 Anil Madhavapeddy <avsm@horde.org>
 * Copyright 2003-2005 Michael Slusarz <slusarz@bigworm.colorado.edu>
 *
 * See the enclosed file COPYING for license information (GPL). If you did not
 * receive this file, see http://www.fsf.org/copyleft/gpl.html.
 *
 * @author  Chuck Hagenbuch <chuck@horde.org>
 * @author  Jon Parise <jon@horde.org>
 * @author  Anil Madhavapeddy <avsm@horde.org>
 * @author  Michael Slusarz <slusarz@bigworm.colorado.edu>
 * @since   IMP 2.3
 * @package IMP
 */
class IMP_Tree extends IMAP_Tree {

    /**
     * Mapping for virtual folders to their label.
     *
     * @var array
     */
    var $_vfolders = array();

    /**
     * The default IMP initialization mask to use.
     *
     * @var integer
     */
    var $_initmask;

    /**
     * Have we already added additional hierarchy subscription info?
     *
     * @var boolean
     */
    var $_hsub = false;

    /**
     * Singleton method.
     */
    function &singleton()
    {
        return IMAP_Tree::singleton('imp', 'IMP_Tree', true);
    }

    /**
     * Constructor.
     *
     * @see IMAP_Tree::IMAP_Tree()
     */
    function IMP_Tree($init = IMAPTREE_INIT_SUB, $cachename = null)
    {
        global $imp;

        $this->_app = 'imp';
        if ($imp['base_protocol'] != 'pop3') {
            $this->_delimiter = $imp['delimiter'];
        }
        $this->_dotfiles = $imp['dotfiles'];
        $this->_mode = IMAPTREE_MODE_MAIL;
        $this->_prefix = $imp['folders'];
        $this->_namespace = $imp['namespace'];
        $this->_server = IMP::serverString();

        /* We just override the init parameter with the following. */
        $this->_initmask = ($imp['base_protocol'] == 'pop3' ||
                            !$GLOBALS['prefs']->getValue('subscribe') ||
                            $imp['showunsub'])
            ? IMAPTREE_INIT_UNSUB : IMAPTREE_INIT_SUB;
        if ($GLOBALS['prefs']->getValue('show_sidebar')) {
            $this->_initmask |= IMAPTREE_INIT_FETCHALL;
        }

        parent::IMAP_Tree($this->_initmask);
    }

    /**
     * Initalizes the list at the top level of the hierarchy.
     *
     * @see IMAP_Tree::_init()
     */
    function init($init = null)
    {
        if (is_null($init)) {
            $init = $this->_initmask;
        }
        parent::init($init);

        /* Convert 'INBOX' to localized name. */
        $this->_tree['INBOX']['l'] = _("Inbox");

        /* Add virtual folders to the tree. */
        if (!empty($_SESSION['imp']['vfolder'])) {
            $this->_vfolders = array();
            $this->insertVFolders(IMP_VFolder::listVFolders());
        }
    }

    /**
     * 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.
     */
    function insertVFolders($id_list)
    {
        $id = array();
        foreach ($id_list as $key => $val) {
            $id[IMP_VFOLDER_PREFIX . $key] = $val;
        }

        if (empty($this->_vfolders)) {
            $this->_vfolders = $id;
            $id = array_merge(array(_("Virtual Folders")), array_keys($id));
        } else {
            $this->_vfolders = array_merge($this->_vfolders, $id);
            $id = array_keys($id);
        }

        $this->_nonimapelt = true;
        $this->_initmode = IMAPTREE_INIT_SUB;
        $this->insert($id);
        $this->_initmode = 0;
        $this->_nonimapelt = false;

        /* Sort the Virtual Folder list in the object, if necessary. */
        if ($this->_needSort($this->_tree[_("Virtual Folders")])) {
            $vsort = array();
            foreach ($this->_parent[_("Virtual Folders")] as $val) {
                $vsort[$val] = $this->_tree[$val]['l'];
            }
            asort($vsort);
            $this->_parent[_("Virtual Folders")] = array_keys($vsort);
            $this->_setNeedSort($this->_tree[_("Virtual Folders")], false);
            $this->_changed = true;
        }
    }

    /**
     * Subclass specific initialization tasks.
     *
     * @see IMAP_Tree::_init()
     */
    function _init()
    {
        $boxes = array();

        if (empty($this->_prefix)) {
            $boxes = $this->_getList('%');
        }

        if (!isset($boxes['INBOX'])) {
            $boxes['INBOX'] = $this->_getMailbox('INBOX');
        }

        /* Do a sort to make sure that 'INBOX' always appears as the first
         * element. */
        require_once HORDE_BASE . '/lib/version.php';
        if (version_compare(HORDE_VERSION, '3.0.4') == -1) {
            $this->_sortList($boxes, true);
        }

        /* If we have any additional hierarchies, such as UW's '#shared/', add
         * them in. */
        foreach ($GLOBALS['imp']['hierarchies'] as $val) {
            $tmp = $this->_getMailbox($val);
            if ($tmp) {
                $tmp->name = $this->noTrailingDelimiter($tmp->name);
                $boxes[$tmp->name] = $tmp;
            }
        }

        return $boxes;
    }

    /**
     * Adds aliases to a tree element and returns the resulting array.
     *
     * @access protected
     *
     * @param array $elt  A tree element.
     *
     * @return array  A tree element with the aliases added.
     */
    function _addAliases($elt)
    {
        $elt = parent::_addAliases($elt);
        if ($elt['label'] == 'INBOX') {
            $elt['label'] = _("Inbox");
        }

        return $elt;
    }

    /**
     * Returns a reference to a currently open IMAP stream.
     *
     * @see IMAP_Tree::_getStream()
     */
    function &_getStream()
    {
        return $GLOBALS['imp']['stream'];
    }

    /**
     * Initializes the expanded folder list.
     *
     * @see IMAP_Tree::_initExpandedList()
     */
    function _initExpandedList()
    {
        if (is_null($this->_expanded)) {
            $serialized = $GLOBALS['prefs']->getValue('expanded_folders');
            $this->_expanded = ($serialized) ? unserialize($serialized) : array();
        }
    }

    /**
     * Adds an element to the expanded list.
     *
     * @see IMAP_Tree::_addExpandedList()
     */
    function _addExpandedList($id)
    {
        $this->_initExpandedList();
        $this->_expanded[$id] = true;
        $GLOBALS['prefs']->setValue('expanded_folders', serialize($this->_expanded));
    }

    /**
     * Removes an element from the expanded list.
     *
     * @see IMAP_Tree::_removeExpandedList()
     */
    function _removeExpandedList($id)
    {
        $this->_initExpandedList();
        unset($this->_expanded[$id]);
        $GLOBALS['prefs']->setValue('expanded_folders', serialize($this->_expanded));
    }

    /**
     * Initializes and returns the list of mailboxes to poll.  This list is
     * returned sorted via IMAP_Sort::.
     *
     * @see IMAP_Tree::getPollList()
     */
    function getPollList()
    {
        if (is_null($this->_poll)) {
            /* We ALWAYS poll the INBOX. */
            $this->_poll = array('INBOX' => 1);

            /* Add the user's list of polled mailboxes, if not locked. */
            if (!$GLOBALS['prefs']->isLocked('nav_poll')) {
                $navPollList = @unserialize($GLOBALS['prefs']->getValue('nav_poll'));
                if ($navPollList) {
                    $this->_poll += $navPollList;
                }
            }
        }

        return $this->_poll;
    }

    /**
     * Adds element(s) to the poll list.
     *
     * @see IMAP_Tree::addPollList()
     */
    function addPollList($id)
    {
        if (!is_array($id)) {
            $id = array($id);
        }

        if (!empty($id) && !$GLOBALS['prefs']->isLocked('nav_poll')) {
            $this->getPollList();
            foreach ($id as $val) {
                $this->_poll[$val] = true;
                $this->_setPolled($this->_tree[$val], true);
            }
            $this->_sortList($this->_poll, true);
            $GLOBALS['prefs']->setValue('nav_poll', serialize($this->_poll));
            $this->_changed = true;
        }
    }

    /**
     * Removes element(s) from the poll list.
     *
     * @see IMAP_Tree::removePollList()
     */
    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;
            }
        }
    }

    /**
     * Returns the currently selected initialization expanded mode.
     *
     * @see IMAP_Tree::_getInitExpandedMode()
     */
    function _getInitExpandedMode()
    {
        return $GLOBALS['prefs']->getValue('nav_expanded');
    }

    /**
     * Creates the virtual folder container.
     *
     * @access private
     *
     * @return array  A mailbox element.
     */
    function _createVFolderContainer()
    {
        $base = _("Virtual Folders");

        $ob = &new stdClass;
        $ob->delimiter = $this->_delimiter;
        $ob->attributes = LATT_NOSELECT | LATT_HASCHILDREN | IMAPTREE_ELT_HAS_CHILDREN | IMAPTREE_ELT_IS_DISCOVERED | IMAPTREE_ELT_IS_SUBSCRIBED | IMPTREE_ELT_VFOLDER;
        $ob->fullServerPath = $ob->name = $base;

        $elt = $this->_makeMailboxTreeElt($ob);
        $elt['l'] = $elt['v'] = $base;

        return $elt;
    }

    /**
     * Creates a virtual folder element.
     *
     * @access private
     *
     * @param string $vfolder  Virtual folder ID.
     *
     * @return array  A mailbox element.
     */
    function _createVFolderElt($vfolder)
    {
        $base = _("Virtual Folders");

        $ob = &new stdClass;
        $ob->delimiter = $this->_delimiter;
        $ob->attributes = LATT_HASNOCHILDREN | IMAPTREE_ELT_IS_DISCOVERED | IMAPTREE_ELT_IS_SUBSCRIBED | IMPTREE_ELT_VFOLDER;
        $ob->name = $base . $this->_delimiter . $vfolder;
        $ob->fullServerPath = $ob->name;

        $elt = $this->_makeMailboxTreeElt($ob);
        $elt['l'] = $this->_vfolders[$vfolder];
        $elt['v'] = $vfolder;

        return $elt;
    }

    /**
     * Returns whether this element is a virtual folder.
     *
     * @param array $elt  A tree element.
     *
     * @return integer  True if the element is a virtual folder.
     */
    function isVFolder($elt)
    {
        return $elt['a'] & IMPTREE_ELT_VFOLDER;
    }

    /**
     * Returns a non-IMAP mailbox element given an element identifier.
     *
     * @abstract
     *
     * @access private
     *
     * @param string $id  The element identifier.
     *
     * @return array  A mailbox element.
     */
    function _getNonIMAPElt($id)
    {
        if ($id == _("Virtual Folders")) {
            return $this->_createVfolderContainer();
        } else {
            return $this->_createVfolderElt($id);
        }
    }

    /**
     * Deletes an element from the tree.
     *
     * @see IMAP_Tree::delete()
     */
    function delete($id)
    {
        if (!is_array($id) && (strpos($id, IMP_VFOLDER_PREFIX) === 0)) {
            unset($this->_vfolders[$id]);
        }
        return parent::delete($id);
    }

    /**
     * Initializes the list of subscribed mailboxes.
     *
     * @see IMAP_Tree::delete()
     */
    function _initSubscribed()
    {
        parent::_initSubscribed();

        /* Add in other hierarchy subscription information. */
        if (!$this->_hsub) {
            foreach ($GLOBALS['imp']['hierarchies'] as $val) {
                $sublist = @imap_lsub($this->_getStream(), $this->_server, $this->_prefix . $val . '*');
                if (!empty($sublist)) {
                    foreach ($sublist as $val2) {
                        $this->_subscribed[substr($val2, strpos($val2, '}') + 1)] = 1;
                    }
                }
            }
            $this->_hsub = true;
        }
     }

}
