<?php
/**
 * Functions required to start an IMP session.
 *
 * $Horde: imp/lib/Session.php,v 1.74.2.3 2005/02/02 17:11:48 slusarz Exp $
 *
 * Copyright 1999-2005 Chuck Hagenbuch <chuck@horde.org>
 * Copyright 1999-2005 Jon Parise <jon@horde.org>
 *
 * See the enclosed file COPYING for license information (GPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
 *
 * @author  Chuck Hagenbuch <chuck@horde.org>
 * @author  Jon Parise <jon@horde.org>
 * @version $Revision: 1.74.2.3 $
 * @since   IMP 4.0
 * @package IMP
 */
class IMP_Session {

    /**
     * Take information posted from a login attempt and try setting up
     * an initial IMP session. Handle Horde authentication, if
     * required, and only do enough work to see if the user can log
     * in. This function should only be called once, when the user
     * first logs in.
     *
     * Creates the $imp session variable with the following entries:
     * '_login'        -- Have the login tasks been completed?
     * 'acl'           -- See config/servers.php.
     * 'admin'         -- See config/servers.php.
     * 'base_protocol' -- Either 'imap' or 'pop3'.
     * 'cache'         -- Various IMP libraries can use this variable to cache
     *                    data.
     * 'delimiter'     -- The folder delimiter to use for the IMAP server.
     * 'dotfiles'      -- See config/servers.php.
     * 'file_upload'   -- If file uploads are allowed, the max size.
     * 'filteravail'   -- Can we apply filters manually?
     * 'folders'       -- See config/servers.php.
     * 'hierarchies'   -- See config/servers.php.
     * 'mailbox'       -- The current viewable mailbox.
     * 'maildomain'    -- See config/servers.php.
     * 'namespace'     -- See config/servers.php.
     * 'pass'          -- The encrypted password.
     * 'port'          -- See config/servers.php.
     * 'protocol'      -- See config/servers.php.
     * 'quota'         -- See config/servers.php.
     * 'search'        -- Mailbox/Folder search parameters.
     * 'server'        -- The name of the server entry in config/servers.php.
     * 'smime'         -- Settings related to the S/MIME viewer.
     * 'smtphost'      -- The SMTP host to use instead of the Horde default.
     * 'smtpport'      -- The SMTP port to use instead of the Horde default.
     * 'showunsub'     -- Show unsusubscribed mailboxes on the folders screen.
     * 'tasklistavail' -- Is listing of tasklists available?
     * 'thismailbox'   -- The mailbox containing the current viewable message.
     * 'uniquser'      -- The unique user name.
     * 'user'          -- The IMAP username.
     *
     * @access public
     *
     * @param string $imapuser      The username of the user.
     * @param string $password      The password of the user.
     * @param string $server        The server to use (see
     *                              config/servers.php).
     * @param optional array $args  The necessary server information.
     *
     * @return boolean  True on success, false on failure.
     */
    function createSession($imapuser, $password, $server, $args = array())
    {
        global $conf, $registry;

        /* We need both a username and password. */
        if (empty($imapuser) || empty($password)) {
            return false;
        }

        /* Make sure all necessary parameters are set. */
        $default_args = array(
            'realm'      => '',
            'port'       => '',
            'protocol'   => '',
            'maildomain' => '',
            'namespace'  => '',
            'folders'    => ''
        );

        /* Merge with the passed-in parameters. */
        $args = array_merge($default_args, $args);

        /* Create the $imp session variable. */
        $_SESSION['imp'] = array();
        $_SESSION['imp']['cache'] = array();
        $_SESSION['imp']['pass'] = Secret::write(Secret::getKey('imp'), $password);

        /* Set the login flag. */
        IMP_Session::loginFlag(true);

        /* Run the username through virtualhost expansion functions if
         * necessary. */
        $_SESSION['imp']['user'] = $imapuser;
        if (!empty($conf['hooks']['vinfo'])) {
            require_once HORDE_BASE . '/config/hooks.php';
            if (function_exists('_imp_hook_vinfo')) {
                $_SESSION['imp']['user'] = call_user_func('_imp_hook_vinfo');
            }
        }

        /* We might need to override some of the defaults with
         * environment-wide settings. Do NOT use the global $servers
         * variable as it may not exist. */
        require IMP_BASE . '/config/servers.php';

        /* Determine the unique user name. */
        if (Auth::isAuthenticated()) {
            $_SESSION['imp']['uniquser'] = Auth::getAuth();
        } else {
            $_SESSION['imp']['uniquser'] = $_SESSION['imp']['user'];

            if (($conf['server']['server_list'] != 'none') &&
                !empty($servers[$server]['realm'])) {
                $_SESSION['imp']['uniquser'] .= '@' . $servers[$server]['realm'];
            } elseif (!empty($args['realm'])) {
                $_SESSION['imp']['uniquser'] .= '@' . $args['realm'];
            }
        }

        if (($conf['server']['server_list'] != 'none') &&
            !empty($servers[$server]) &&
            is_array($servers[$server])) {
            $fields = array('server', 'protocol', 'port', 'folders',
                            'namespace','maildomain', 'quota', 'acl',
                            'dotfiles');
            $fields_array = array('hierarchies', 'admin');
            $ptr = &$servers[$server];

            foreach ($fields as $val) {
                $_SESSION['imp'][$val] = isset($ptr[$val]) ? $ptr[$val] : null;
                /* 'admin' and 'quota' have password entries - encrypt these
                 * entries in the session if they exist. */
                if (isset($ptr[$val]['params']['password'])) {
                    $_SESSION['imp'][$val]['params']['password'] = Secret::write(Secret::getKey('imp'), $ptr[$val]['params']['password']);
                }
            }
            foreach ($fields_array as $val) {
                $_SESSION['imp'][$val] = isset($ptr[$val]) ? $ptr[$val] : array();
            }

            if ($conf['mailer']['type'] == 'smtp') {
                if (!empty($ptr['smtphost'])) {
                    $_SESSION['imp']['smtphost'] = $ptr['smtphost'];
                }
                if (!empty($ptr['smtpport'])) {
                    $_SESSION['imp']['smtpport'] = $ptr['smtpport'];
                }
            }
        } else {
            $server_key = null;
            foreach ($servers as $key => $val) {
                if (is_null($server_key) && (substr($key, 0, 1) != '_')) {
                    $server_key = $key;
                }
                if (IMP::isPreferredServer($val, $key)) {
                    $server_key = $key;
                    break;
                }
            }
            $ptr = &$servers[$server_key];

            if ($conf['server']['change_server']) {
                $_SESSION['imp']['server'] = $server;
            } else {
                $_SESSION['imp']['server'] = $ptr['server'];

                foreach (array('acl', 'admin', 'quota') as $val) {
                    if (isset($ptr[$val])) {
                        $_SESSION['imp'][$val] = $ptr[$val];
                        /* 'admin' and 'quota' have password entries - encrypt
                         * these entries in the session if they exist. */
                        if (isset($ptr[$val]['params']['password'])) {
                            $_SESSION['imp'][$val]['params']['password'] = Secret::write(Secret::getKey('imp'), $ptr[$val]['params']['password']);
                        }
                    } else {
                        $_SESSION['imp'][$val] = false;
                    }
                }
            }

            if ($conf['server']['change_port']) {
                $_SESSION['imp']['port'] = $args['port'];
            } else {
                $_SESSION['imp']['port'] = $ptr['port'];
            }

            if ($conf['server']['change_protocol']) {
                $_SESSION['imp']['protocol'] = $args['protocol'];
            } else {
                $_SESSION['imp']['protocol'] = $ptr['protocol'];
            }

            if ($conf['server']['change_folders']) {
                $_SESSION['imp']['folders'] = $args['folders'];
            } else {
                $_SESSION['imp']['folders'] = $ptr['folders'];
            }

            $_SESSION['imp']['maildomain'] = $args['maildomain'];
            $_SESSION['imp']['namespace'] = $args['namespace'];
            $_SESSION['imp']['dotfiles'] = !empty($ptr['dotfiles']);
            $_SESSION['imp']['hierarchies'] = !empty($ptr['hierarchies']) ? $ptr['hierarchies'] : array();
        }

        /* Determine the base protocol. */
        if (($pos = strpos($_SESSION['imp']['protocol'], '/'))) {
            $_SESSION['imp']['base_protocol'] = substr($_SESSION['imp']['protocol'], 0, $pos);
        } else {
            $_SESSION['imp']['base_protocol'] = $_SESSION['imp']['protocol'];
        }

        /* Set the initial mailbox to blank. */
        $_SESSION['imp']['mailbox'] = '';

        /* Try to authenticate with the given information. */
        $auth_imp = &Auth::singleton(array('imp', 'imp'));
        $auth_imp->authenticateOptions(array('flags' => OP_HALFOPEN));
        if ($auth_imp->authenticate(null, null, true) !== true) {
            return false;
        }

        /* Set the session variables. These are cached. */
        $_SESSION['imp']['mailbox'] = $GLOBALS['prefs']->getValue('mailbox');

        /* Does the server allow file uploads? If yes, store the
         * value, in bytes, of the maximum file size. */
        $_SESSION['imp']['file_upload'] = Browser::allowFileUploads();

        /* Is the 'mail/canApplyFilters' API call available? */
        if ($registry->hasMethod('mail/canApplyFilters')) {
            $_SESSION['imp']['filteravail'] = $registry->call('mail/canApplyFilters');
        } else {
            $_SESSION['imp']['filteravail'] = false;
        }

        /* Is the 'tasks/listTasklists' call available? */
        $_SESSION['imp']['tasklistavail'] = ($conf['tasklist']['use_tasklist'] && $registry->hasMethod('tasks/listTasklists'));

        /* IMAP specific variables. */
        if ($_SESSION['imp']['base_protocol'] != 'pop3') {
            /* Get the default delimiter value (per RFC 3501 [6.3.8]). */
            $box = @imap_getmailboxes($_SESSION['imp']['stream'], IMP::serverString(), '');
            if (empty($box)) {
                $box = @imap_getmailboxes($_SESSION['imp']['stream'], IMP::serverString(), 'INBOX');
            }
            $_SESSION['imp']['delimiter'] = !empty($box[0]->delimiter) ? $box[0]->delimiter : '/';

            /* Initialize the 'showunsub' value. */
            $_SESSION['imp']['showunsub'] = false;

            /* Are there any virtual folders the user has? */
            require_once IMP_BASE . '/lib/VFolder.php';
            $_SESSION['imp']['vfolder'] = IMP_VFolder::getVFolderList();
        }

        /* Store the $imp variable globally. */
        $GLOBALS['imp'] = &$_SESSION['imp'];

        return true;
    }

    /**
     * Perform IMP login tasks.
     *
     * @access public
     */
    function loginTasks()
    {
        if (IMP_Session::loginFlag()) {
            if (empty($_SESSION['imp']['stream'])) {
                IMP::checkAuthentication(OP_HALFOPEN, true);
            }

            /* Do maintenance operations. */
            if ($GLOBALS['prefs']->getValue('do_maintenance')) {
                require_once 'Horde/Maintenance.php';
                $maint = &Maintenance::factory('imp', array('last_maintenance' => $GLOBALS['prefs']->getValue('last_maintenance')));
                if (!$maint) {
                    $GLOBALS['notification']->push(_("Could not execute maintenance operations."), 'horde.warning');
                } else {
                    $maint->runMaintenance();
                }
            }

            /* If the user wants to run filters on login, make sure they get
               run. */
            if ($GLOBALS['prefs']->getValue('filter_on_login')) {
                require_once IMP_BASE . '/lib/IMAP.php';
                $imp_imap = &IMP_IMAP::singleton();

                /* Open the INBOX read-write. */
                $imp_imap->changeMbox('INBOX');

                /* Run filters. */
                require_once IMP_BASE . '/lib/Filter.php';
                IMP_Filter::filter('INBOX');

                /* Return to read-only. */
                $imp_imap->changeMbox('', OP_HALFOPEN);
            }

            IMP_Session::loginFlag(false);
        }
    }

    /**
     * Either sets or checks the value of the login flag.
     *
     * @access public
     *
     * @param optional boolean $set  The value of the flag.
     *
     * @return boolean  The value of the flag.
     */
    function loginFlag($set = null)
    {
        if (!is_null($set)) {
            if ($set) {
                $_SESSION['imp']['_login'] = true;
            } else {
                unset($_SESSION['imp']['_login']);
            }
        }

        return isset($_SESSION['imp']['_login']);
    }

}
