<?php

include_once 'Log.php';

/**
 * The Horde:: class provides the functionality shared by all Horde
 * applications.
 *
 * $Horde: horde/lib/Horde.php,v 1.323 2003/08/21 00:13:02 jan Exp $
 *
 * Copyright 1999-2003 Chuck Hagenbuch <chuck@horde.org>
 * Copyright 1999-2003 Jon Parise <jon@horde.org>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Chuck Hagenbuch <chuck@horde.org>
 * @author  Jon Parise <jon@horde.org>
 * @version $Revision: 1.323 $
 * @since   Horde 1.3
 * @package horde
 */
class Horde {

    /**
     * Log a message to the global Horde log backend.
     *
     * @access public
     *
     * @param mixed $message              Either a string or a PEAR_Error
     *                                    object.
     * @param string $file                What file was the log function called
     *                                    from (e.g. __FILE__) ?
     * @param integer $line               What line was the log function called
     *                                    from (e.g. __LINE__) ?
     * @param optional integer $priority  The priority of the message. One of:
     * <pre>
     * PEAR_LOG_EMERG
     * PEAR_LOG_ALERT
     * PEAR_LOG_CRIT
     * PEAR_LOG_ERR
     * PEAR_LOG_WARNING
     * PEAR_LOG_NOTICE
     * PEAR_LOG_INFO
     * PEAR_LOG_DEBUG
     * </pre>
     */
    function logMessage($message, $file, $line, $priority = PEAR_LOG_INFO)
    {
        static $logcheck;
        global $conf;

        if (!$conf['log']['enabled']) {
            return;
        }

        if ($priority > $conf['log']['priority']) {
            return;
        }

        if (!isset($logcheck)) {
            // Try to make sure that we can log messages somehow.
            if (empty($conf['log']) ||
                empty($conf['log']['type']) ||
                empty($conf['log']['name']) ||
                empty($conf['log']['ident']) ||
                !isset($conf['log']['params'])) {
                Horde::fatal(PEAR::raiseError('Horde is not correctly configured to log error messages. You must configure at least a text file log in horde/config/conf.php.'), __FILE__, __LINE__, false);
            }
            $logcheck = true;
        }

        $logger = &Log::singleton($conf['log']['type'], $conf['log']['name'],
                                  $conf['log']['ident'], $conf['log']['params']);

        if (!$logger) {
            Horde::fatal(PEAR::raiseError('An error has occurred. Furthermore, Horde encountered an error attempting to log this error. Please check your Horde logging configuration in horde/config/conf.php.'), __FILE__, __LINE__, false);
        }

        if (is_a($message, 'PEAR_Error')) {
            $userinfo = $message->getUserInfo();
            $message = $message->getMessage();
            if (!empty($userinfo)) {
                if (is_array($userinfo)) {
                    $userinfo = implode(', ', $userinfo);
                }
                $message .= ': ' . $userinfo;
            }
        }

        $app = isset($GLOBALS['registry']) ? $GLOBALS['registry']->getApp() : 'horde';
        $message = '[' . $app . '] ' . $message . ' [on line ' . $line . ' of "' . $file . '"]';

        /* Make sure to log in the system's locale. */
        $locale = setlocale(LC_TIME, 0);
        setlocale(LC_TIME, 'C');

        $logger->log($message, $priority);

        /* Restore original locale. */
        setlocale(LC_TIME, $locale);

        return true;
    }

    /**
     * Destroy any existing session on login and make sure to use a new session
     * ID, to avoid session fixation issues. Should be called before checking a
     * login.
     */
    function getCleanSession()
    {
        Auth::clearAuth();
        @session_destroy();

        // Make sure to force a completely new session ID.
        if (version_compare(phpversion(), '4.3.3') !== -1) {
            session_regenerate_id();
        } else {
            if (Horde::extensionExists('posix')) {
                $new_session_id = md5(microtime() . posix_getpid());
            } else {
                $new_session_id = md5(uniqid(mt_rand(), true));
            }
            session_id($new_session_id);
        }

        // Restart the session, including setting up the session
        // handler.
        Horde::setupSessionHandler();
        @session_start();
    }

    /**
     * Abort with a fatal error, displaying debug information to the user.
     *
     * @access public
     *
     * @param mixed $error           A PEAR_Error object with debug information
     *                               or an error message.
     * @param integer $file          The file in which the error occured.
     * @param integer $line          The line on which the error occured.
     * @param optional boolean $log  Log this message via Horde::logMesage()?
     */
    function fatal($error, $file, $line, $log = true)
    {
        @include_once HORDE_BASE . '/lib/Text.php';
        @include_once HORDE_BASE . '/lib/Auth.php';

        $admin = class_exists('Auth') && Auth::isAdmin();

        $errortext = _("<b>A fatal error has occurred:</b>") . '<br />';
        if (is_object($error) && method_exists($error, 'getMessage')) {
            $errortext .= $error->getMessage();
        } elseif (is_string($error)) {
            $errortext .= $error;
        }
        $errortext .= '<br /><br />';

        if ($admin) {
            $errortext .= sprintf(_("[line %s of %s]"), $line, $file) . '<br /><br />';
            if (is_object($error)) {
                $errortext .= _("Details (also in Horde's logfile):") . '<br /><br />';
                $errortext .= '<pre>' . Horde::bufferOutput('var_dump', $error) . '</pre><br /><br />';
            }
        } elseif ($log) {
            $errortext .= _("Details have been logged for the administrator.") . '<br /><br />';
        }

        // Log the fatal error via Horde::logMessage() if requested.
        if ($log) {
            Horde::logMessage($error, $file, $line, PEAR_LOG_EMERG);
        }

        // Hardcode a small stylesheet so that this doesn't depend on
        // anything else.
        echo <<< HTML
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "DTD/xhtml1-transitional.dtd">
<html>
<head>
<title>Horde :: Fatal Error</title>
<style type="text/css">
<!--
body { font-family: Geneva,Arial,Helvetica,sans-serif; font-size: 12px; background-color: #222244; color: #ffffff; }
.header { color: #ccccee; background-color: #444466; font-family: Verdana,Helvetica,sans-serif; font-size: 12px; }
-->
</style>
</head>
<body>
<table border="0" align="center" width="500" cellpadding="2" cellspacing="0">
<tr><td class="header">$errortext</td></tr>
</table>
</body>
</html>
HTML;

        exit;
    }

    /**
     * Buffer the output from a function call, like readfile() or
     * highlight_string(), that prints the output directly, so that
     * instead it can be returned as a string and used.
     *
     * @access public
     *
     * @param string $function        The function to run.
     * @param optional mixed $arg1    First argument to $function().
     * @param optional mixed $arg2    Second argument to $function().
     * @param optional mixed $arg...  ...
     * @param optional mixed $argN    Nth argument to $function().
     *
     * @return string  The output of the function.
     */
    function bufferOutput()
    {
        if (func_num_args() == 0) {
            return false;
        }

        $eval = false;
        $args = func_get_args();
        $function = array_shift($args);
        if (is_array($function)) {
            if (!method_exists($function[0], $function[1])) {
                return false;
            }
        } elseif (($function == 'include') ||
                  ($function == 'include_once') ||
                  ($function == 'require') ||
                  ($function == 'require_once')) {
            $eval = true;
        } elseif (!function_exists($function) &&
                  ($function != 'eval')) {
            return false;
        }

        ob_start();
        if ($eval) {
            eval($function . " '" . implode(',', $args) . "';");
        } elseif ($function == 'eval') {
            eval($args[0]);
        } else {
            call_user_func_array($function, $args);
        }
        $output = ob_get_contents();
        ob_end_clean();

        return $output;
    }

    /**
     * Adds the javascript code to the output (if output has already started)
     * or to the list of script files to include via includeScriptFiles().
     *
     * @access public
     *
     * @param string $file          The full javascript file name.
     * @param optional string $app  The application name.
     */
    function addScriptFile($file, $app = null)
    {
        static $included;
        if (is_null($included)) {
            $included = array();
        }

        global $registry;

        if (is_null($app)) {
            $app = $registry->getApp();
        }

        // Don't include scripts multiple times.
        if (array_key_exists($app, $included) &&
            array_key_exists($file, $included[$app])) {
            return;
        }
        $included[$app][$file] = true;

        if (ob_get_length() || headers_sent()) {
            $url = Horde::addParameter($registry->getParam('webroot', 'horde') . '/javascript.php', 'file', $file);
            $url = Horde::addParameter($url, 'app', $app);
            echo '<script language="JavaScript" type="text/javascript" src="' . Horde::url($url) . "\"></script>\n";
        } else {
            global $_horde_script_files;
            $_horde_script_files[$app][] = $file;
        }
    }

    /**
     * Include javascript files that were needed before any headers were sent.
     *
     * @access public
     */
    function includeScriptFiles()
    {
        global $_horde_script_files, $registry;

        if (!empty($_horde_script_files)) {
            foreach ($_horde_script_files as $app => $files) {
                foreach ($files as $file) {
                    $url = Horde::addParameter($registry->getParam('webroot', 'horde') . '/javascript.php', 'file', $file);
                    $url = Horde::addParameter($url, 'app', $app);
                    echo '<script language="JavaScript" type="text/javascript" src="' . Horde::url($url) . "\"></script>\n";
                }
            }
        }
    }

    /**
     * Return the driver parameters for the specified backend.
     *
     * @param string $backend        The backend system - prefs, categories,
     *                               contacts - being used.
     * @param optional string $type  The type of driver. Defaults to 'sql'.
     *
     * @return array  The connection parameters.
     */
    function getDriverConfig($backend, $type = 'sql')
    {
        global $conf;

        if (isset($conf[$backend]) &&
            isset($conf[$backend]['params'])) {
            if (isset($conf[$type])) {
                return array_merge($conf[$type], $conf[$backend]['params']);
            } else {
                return $conf[$backend]['params'];
            }
        }

        return isset($conf[$type]) ? $conf[$type] : array();
    }

    /**
     * Check to see if a value has been set by the script and not by GET, POST,
     * or cookie input. The value being checked MUST be in the global scope.
     *
     * @param string $varname  The variable name to check.
     *
     * @return mixed  Null if the var is in user input, the variable value otherwise.
     */
    function nonInputVar($varname)
    {
        if (isset($_GET[$varname]) ||
            isset($_POST[$varname]) ||
            isset($_COOKIE[$varname])) {
            return null;
        } else {
            return isset($GLOBALS[$varname]) ? $GLOBALS[$varname] : null;
        }
    }

    /**
     * Return a session-id-ified version of $uri.
     *
     * @access public
     *
     * @param string $uri                       The URI to be modified.
     * @param optional boolean $full            Generate a full
     *                                          (http://server/path/) URL.
     * @param optional integer $append_session  0 = only if needed, 1 = always,
     *                                          -1 = never.
     *
     * @return string  The URL with the session id appended (if needed).
     */
    function url($uri, $full = false, $append_session = 0)
    {
        if ($full) {
            global $conf, $registry;

            /* Store connection parameters in local variables. */
            $server_name = $conf['server']['name'];
            $server_port = $conf['server']['port'];

            $protocol = 'http';
            if ($conf['use_ssl'] == 1) {
                $protocol = 'https';
            } elseif ($conf['use_ssl'] == 2) {
                require_once HORDE_BASE . '/lib/Server.php';
                if (Server::usingSSLConnection()) {
                    $protocol = 'https';
                }
            }

            /* If using non-standard ports, add the port to the URL. */
            if (!empty($server_port) &&
                (($protocol == 'http') && ($server_port != 80)) ||
                (($protocol == 'https') && ($server_port != 443))) {
                $server_name .= ':' . $server_port;
            }

            /* Store the webroot in a local variable. */
            $webroot = $registry->getParam('webroot');

            $url = $protocol . '://' . $server_name;
            if (substr($uri, 0, 1) != '/') {
                if (substr($webroot, -1) == '/') {
                    $url .= $webroot . $uri;
                } else {
                    $url .= $webroot . '/' . $uri;
                }
            } else {
                $url .= $uri;
            }
        } else {
            $url = $uri;
        }

        if (($append_session == 1) ||
            (($append_session == 0) &&
             !array_key_exists(session_name(), $_COOKIE))) {
            $url = Horde::addParameter($url, session_name(), session_id());
        }

        return ($full ? $url : htmlentities($url));
    }

    /**
     * Add a name=value pair to the end of an URL, taking care of whether there
     * are existing parameters and whether to use ? or & as the glue. All data
     * will be urlencoded.
     *
     * @access public
     *
     * @param string $url             The URL to modify
     * @param string $parameter       The name=value pair to add.
     * @param optional string $value  If specified, the value part ($parameter
     *                                is assumed to just be the parameter
     *                                name).
     *
     * @return string  The modified URL.
     *
     * @since Horde 2.1
     */
    function addParameter($url, $parameter, $value = null)
    {
        if (empty($parameter)) {
            return $url;
        }

        if (!is_array($parameter)) {
            if (is_null($value)) {
                @list($parameter, $value) = explode('=', $parameter, 2);
            }
            $add = array($parameter => $value);
        } else {
            $add = $parameter;
        }

        if (($pos = strpos($url, '?')) === false) {
            $url .= '?';
        } else {
            parse_str(substr($url, $pos + 1), $params);
            $url .= ini_get('arg_separator.output');
        }

        $url_params = array();
        foreach ($add as $parameter => $value) {
            if (!isset($params[$parameter])) {
                $url_params[] = urlencode($parameter) . '=' . urlencode($value);
            }
        }

        return $url . join(ini_get('arg_separator.output'), $url_params);
    }

    /**
     * Removes name=value pairs from a URL.
     *
     * @access public
     *
     * @param string $url    The URL to modify.
     * @param array $remove  The array of parameters to remove.
     *
     * @return string  The modified URL.
     *
     * @since Horde 2.2
     */
    function removeParameter($url, $remove = array())
    {
        if (!is_array($remove)) {
            $remove = array($remove);
        }

        /* Return immediately if there are no parameters to remove. */
        if (($pos = strpos($url, '?')) === false) {
            return $url;
        }

        $arg = ini_get('arg_separator.output');
        $entities = false;
        list($url, $query) = explode('?', $url, 2);

        /* Check if the argument separator has been already htmlentities-ized
           in the URL. */
        if (preg_match('/=.*?' . htmlentities($arg) . '.*?=/', $query)) {
            $entities = true;
            $query = strtr($query, array_flip(get_html_translation_table(HTML_ENTITIES)));
        }

        /* Get the list of parameters. */
        parse_str($query, $params);

        /* Remove the parameters. */
        foreach ($remove as $val) {
            unset($params[$val]);
        }

        if (empty($params)) {
            return $url;
        }

        $query = implode($arg, array_map(create_function('$a,$b', 'return urlencode($a) . "=" . $b;'), array_keys($params), $params));
        if ($entities) {
            $query = htmlentities($query);
        }

        return $url . '?' . $query;
    }

    /**
     * Return a session-id-ified version of $uri, using the current
     * application's webroot setting.
     *
     * @access public
     *
     * @param string $uri                       The URI to be modified.
     * @param optional boolean $full            Generate a full
     *                                          (http://server/path/) URL.
     * @param optional integer $append_session  0 = only if needed, 1 = always,
     *                                          -1 = never.
     *
     * @return string  The url with the session id appended
     */
    function applicationUrl($uri, $full = false, $append_session = 0)
    {
        global $registry;

        /* Store the webroot in a local variable. */
        $webroot = $registry->getParam('webroot');

        if ($full) {
            return Horde::url($uri, $full, $append_session);
        } elseif (substr($webroot, -1) == '/') {
            return Horde::url($webroot . $uri, $full, $append_session);
        } else {
            return Horde::url($webroot . '/' . $uri, $full, $append_session);
        }
    }

    /**
     * Return an anchor tag with the relevant parameters
     *
     * @access public
     *
     * @param string $url                 The full URL to be linked to
     * @param optional string $status     The JavaScript mouse-over string
     * @param optional string $class      The CSS class of the link
     * @param optional string $target     The window target to point to.
     * @param optional string $onclick    JavaScript action for the 'onclick'
     *                                    event.
     * @param optional string $title      The link title (tooltip).
     * @param optional string $accesskey  The access key to use.
     *
     * @return string  The full <a href> tag.
     */
    function link($url, $status = '', $class = '', $target = '', $onclick = '',
                  $title = '', $accesskey = '')
    {
        $ret = "<a href=\"$url\"";
        if (!empty($onclick)) {
            $ret .= " onclick=\"$onclick\"";
        }
        if (!empty($status)) {
            $ret .= ' onmouseout="window.status=\'\';" onmouseover="window.status=\'' . @htmlspecialchars(addslashes($status), ENT_QUOTES, NLS::getCharset()) . '\'; return true;"';
        }
        if (!empty($class)) {
            $ret .= " class=\"$class\"";
        }
        if (!empty($target)) {
            $ret .= " target=\"$target\"";
        }
        if (!empty($title)) {
            $ret .= ' title="' . @htmlspecialchars($title, ENT_QUOTES, NLS::getCharset()) . '"';
        }
        if (!empty($accesskey)) {
            $ret .= ' accesskey="' . htmlspecialchars($accesskey) . '"';
        }

        return "$ret>";
    }

    /**
     * Print an anchor tag with the relevant parameters
     *
     * @access public
     *
     * @param string $url               The full URL to be linked to
     * @param optional string $status   The JavaScript mouse-over string
     * @param optional string $class    The CSS class of the link
     * @param optional string $target   The window target to point to.
     * @param optional string $onclick  JavaScript action for the 'onclick'
     *                                  event.
     * @param optional string $title    The link title (tooltip)
     */
    function plink($url, $status = '', $class = '', $target = '',
                   $onclick = '', $title = '')
    {
        echo Horde::link($url, $status, $class, $target, $onclick, $title);
    }

    /**
     * Return an anchor sequence with the relevant parameters for a widget with
     * accesskey and text.
     *
     * @access public
     *
     * @param string $url               The full URL to be linked to
     * @param optional string $status   The JavaScript mouse-over string
     * @param optional string $class    The CSS class of the link
     * @param optional string $target   The window target to point to.
     * @param optional string $onclick  JavaScript action for the 'onclick'
     *                                  event.
     * @param optional string $title    The link title (tooltip).
     * @param optional integer $akey    If the same widget has already been
     *                                  used,
     *                                    0 => don't make another accesskey
     *                                    1 => make a unique accesskey
     *                                    2 => use the same accesskey
     *                                    3 => don't use accesskey at all
     *
     * @return string  The full <a href>Title</a> sequence.
     */
    function widget($url, $status = '', $class = 'widget', $target = '',
                    $onclick = '', $title = '', $akey = 0)
    {
        global $prefs;

        /* The access keys already defined as wigets */
        static $_used;
        if (is_null($_used)) {
            $_used = array();
        }

        $use_ak = true;

        if (isset($prefs)) {
            $use_ak = $prefs->getValue('widget_accesskey');
        }

        if ($use_ak && ($akey != 3)) {
            if (array_key_exists($title, $_used)) {
                switch ($akey) {
                    case 0:
                        $ak = '';
                        break;
                    case 1:
                        $ak = Horde::getAccessKey($title);
                        break;
                    case 2:
                        $ak = $_used[$title];
                }
            } else {
                $ak = Horde::getAccessKey($title);
                $_used[$title] = $ak;
            }
        } else {
            $ak = '';
        }

        return Horde::link($url, $status, $class, $target, $onclick, $title, $ak) . Horde::highlightAccessKey($title, $ak) . '</a>';
    }

    /**
     * Print a session-id-ified version of the URI.
     *
     * @access public
     *
     * @param string $uri                       The URI to be modified.
     * @param boolean $full                     Generate a full
     *                                          (http://server/path/) URL.
     * @param optional boolean $append_session  0 = only if needed, 1 = always,
     *                                          -1 = never.
     */
    function purl($uri, $full = false, $append_session = 0)
    {
        echo Horde::url($uri, $full, $append_session);
    }

    /**
     * Return a session-id-ified version of $PHP_SELF.
     *
     * @access public
     *
     * @param optional boolean $query_string  Include any QUERY_STRING?
     * @param optional boolean $nocache       Include a nocache parameter in
     *                                        the URL?
     * @param optional boolean $full          Return a full URL?
     *
     * @return string  The requested URI.
     */
    function selfURL($query_string = false, $nocache = true, $full = false)
    {
        $url = $_SERVER['PHP_SELF'];

        if ($query_string && !empty($_SERVER['QUERY_STRING'])) {
            $url .= '?' . $_SERVER['QUERY_STRING'];
        }

        $url = Horde::url($url, $full);

        if ($nocache) {
            return Horde::nocacheUrl($url);
        } else {
            return $url;
        }
    }

    /**
     * Print a session-id-ified version of $PHP_SELF.
     *
     * @access public
     *
     * @param optional boolean $query_string  Include any QUERY_STRING?
     *                                        Defaults to no.
     */
    function pselfURL($query_string = false)
    {
        echo Horde::selfURL($query_string);
    }

    /**
     * Returns a url with the 'nocache' parameter added, if the browser is buggy
     * and caches old URLs.
     *
     * @access public
     *
     * @param string $url  The URL to modify.
     *
     * @return string  The requested URI.
     */
    function nocacheUrl($url)
    {
        static $rand_num;

        require_once HORDE_BASE . '/lib/Browser.php';
        $browser = &Browser::singleton();

        if (!isset($rand_num)) {
            $rand_num = md5(mt_rand());
        }

        /* We may need to set a dummy parameter 'nocache' since some browsers
           do not always honor the 'no-cache' header. */
        if ($browser->hasQuirk('cache_same_url')) {
            return Horde::addParameter($url, 'nocache', $rand_num);
        } else {
            return $url;
        }
    }

    /**
     * Return a hidden form input containing the session name and id.
     *
     * @access public
     *
     * @param optional boolean $append_session  0 = only if needed, 1 = always.
     *
     * @return string  The hidden form input, if needed/requested.
     */
    function formInput($append_session = 0)
    {
        if (($append_session == 1) ||
            !array_key_exists(session_name(), $_COOKIE)) {
            return '<input type="hidden" name="' . session_name() . '" value="' . session_id() . "\" />\n";
        } else {
            return '';
        }
    }

    /**
     * Print a hidden form input containing the session name and id.
     *
     * @access public
     *
     * @param optional boolean $append_session  0 = only if needed, 1 = always.
     *
     * @return string  The hidden form input, if needed/requested.
     */
    function pformInput($append_session = 0)
    {
        echo Horde::formInput($append_session);
    }

    /**
     * Construct a correctly-pathed link to an image
     *
     * @access public
     *
     * @param string $src            The image file.
     * @param optional string $alt   Text describing the image.
     * @param optional string $attr  Any additional attributes for the image
     *                               tag.
     * @param optional string $dir   The root graphics directory.
     *
     * @return string  The full image tag.
     */
    function img($src, $alt = '', $attr = '', $dir = null)
    {
        require_once HORDE_BASE . '/lib/Browser.php';
        $browser = &Browser::singleton();

        /* If browser does not support images, simply return the ALT text. */
        if (!$browser->hasFeature('images')) {
            return $alt;
        }

        /* If no directory has been specified, get it from the registry. */
        if ($dir === null) {
            global $registry;
            $dir = $registry->getParam('graphics');
        }

        /* If a directory has been provided, prepend it to the image source. */
        if (!empty($dir)) {
            $src = $dir . '/' . $src;
        }

        /* Build the image tag. */
        $img = "<img src=\"$src\" border=\"0\" alt=\"$alt\" title=\"$alt\"";

        /* Add any additional attributes. Then, close the tag. */
        $img .= (!empty($attr)) ? " $attr />" : ' />';

        return $img;
    }

    /**
     * Construct a correctly-pathed link to an image
     *
     * @access public
     *
     * @param string $src            The image file.
     * @param optional string $alt   Text describing the image.
     * @param optional string $attr  Any additional attributes for the image
     *                               tag.
     * @param optional string $dir   The root graphics directory.
     */
    function pimg($src, $alt = '', $attr = null, $dir = null)
    {
        echo Horde::img($src, $alt, $attr, $dir);
    }

    /**
     * If magic_quotes_gpc is in use, run stripslashes() on $var.
     *
     * @access public
     *
     * @param string &$var  The string to un-quote, if necessary.
     *
     * @return string  $var, minus any magic quotes.
     */
    function dispelMagicQuotes(&$var)
    {
        static $magic_quotes;

        if (!isset($magic_quotes)) {
            $magic_quotes = get_magic_quotes_gpc();
        }

        if ($magic_quotes) {
            if (!is_array($var)) {
                $var = stripslashes($var);
            } else {
                array_walk($var, array('Horde', 'dispelMagicQuotes'));
            }
        }

        return $var;
    }

    /**
     * Get a form variable from GET or POST data, stripped of magic quotes if
     * necessary. If the variable is somehow set in both the GET data and the
     * POST data, the value from the POST data will be returned and the GET
     * value will be ignored.
     *
     * @access public
     *
     * @param string $var               The name of the form variable to look
     *                                  for.
     * @param optional string $default  The value to return if the variable is
     *                                  not there.
     *
     * @return string  The cleaned form variable, or $default.
     */
    function getFormData($var, $default = null)
    {
        return (($val = Horde::getPost($var)) !== null)
            ? $val : Horde::getGet($var, $default);
    }

    /**
     * Get a form variable from GET data, stripped of magic quotes if necessary.
     * This function will NOT return a POST variable.
     *
     * @access public
     *
     * @param string $var               The name of the form variable to look
     *                                  for.
     * @param optional string $default  The value to return if the variable is
     *                                  not there.
     *
     * @return string  The cleaned form variable, or $default.
     *
     * @since Horde 2.2
     */
    function getGet($var, $default = null)
    {
        return (isset($_GET[$var]))
            ? Horde::dispelMagicQuotes($_GET[$var])
            : $default;
    }

    /**
     * Get a form variable from POST data, stripped of magic quotes if
     * necessary. This function will NOT return a GET variable.
     *
     * @access public
     *
     * @param string $var               The name of the form variable to look
     *                                  for.
     * @param optional string $default  The value to return if the variable is
     *                                  not there.
     *
     * @return string  The cleaned form variable, or $default.
     *
     * @since Horde 2.2
     */
    function getPost($var, $default = null)
    {
        return (isset($_POST[$var]))
            ? Horde::dispelMagicQuotes($_POST[$var])
            : $default;
    }

    /**
     * Determine the location of the system temporary directory. If a specific
     * setting cannot be found, it defaults to /tmp.
     *
     * @access public
     *
     * @return string  A directory name which can be used for temp files.
     *                 Returns false if one could not be found.
     */
    function getTempDir()
    {
        global $conf;

        $tmp_locations = array('/tmp', '/var/tmp', 'c:\WUTemp', 'c:\temp', 'c:\windows\temp', 'c:\winnt\temp');

        /* If one has been specifically set, then use that */
        if (!empty($conf['tmpdir'])) {
            $tmp = $conf['tmpdir'];
        }

        /* Next, try PHP's upload_tmp_dir directive. */
        if (empty($tmp)) {
            $tmp = ini_get('upload_tmp_dir');
        }

        /* Otherwise, try to determine the TMPDIR environment
           variable. */
        if (empty($tmp)) {
            $tmp = getenv('TMPDIR');
        }

        /* If we still cannot determine a value, then cycle through a
         * list of preset possibilities. */
        while (empty($tmp) && sizeof($tmp_locations)) {
            $tmp_check = array_shift($tmp_locations);
            if (@is_dir($tmp_check)) {
                $tmp = $tmp_check;
            }
        }

        /* If it is still empty, we have failed, so return false;
         * otherwise return the directory determined. */
        return empty($tmp) ? false : $tmp;
    }

    /**
     * Create a temporary filename for the lifetime of the script, and
     * (optionally) register it to be deleted at request shutdown.
     *
     * @access public
     *
     * @param string $prefix            Prefix to make the temporary name more
     *                                  recognizable.
     * @param optional boolean $delete  Delete the file at the end of the
     *                                  request?
     * @param optional string $dir      Directory to create the temporary file
     *                                  in.
     * @param optional boolean $secure  If deleting file, should we securely
     *                                  delete the file?
     *
     * @return string   Returns the full path-name to the temporary file.
     *                  Returns false if a temp file could not be created.
     */
    function getTempFile($prefix = 'Horde', $delete = true, $dir = '',
                         $secure = false)
    {
        if (empty($dir) || !is_dir($dir)) {
            $tmp_dir = Horde::getTempDir();
        } else {
            $tmp_dir = $dir;
        }

        if (empty($tmp_dir)) {
            return false;
        }

        $tmp_file = tempnam($tmp_dir, $prefix);

        /* If the file was created, then register it for deletion and return */
        if (empty($tmp_file)) {
            return false;
        } else {
            if ($delete) {
                Horde::deleteAtShutdown($tmp_file, true, $secure);
            }
            return $tmp_file;
        }
    }

    /**
     * Create a temporary directory in the system's temporary directory.
     *
     * @access public
     *
     * @param optional boolean $delete  Delete the temporary directory at the
     *                                  end of the request?
     *
     * @return string       The pathname to the new temporary directory.
     *                      Returns false if directory not created.
     *
     * @since Horde 2.2
     */
    function createTempDir($delete = true)
    {
        $temp_dir = Horde::getTempDir();
        if (empty($temp_dir)) return false;

        /* Get the first 8 characters of a random string to use as a temporary
           directory name. */
        do {
            $temp_dir .= '/' . substr(md5(mt_rand()), 0, 8);
        } while (file_exists($temp_dir));

        $old_umask = umask(0000);
        if (!mkdir($temp_dir, 0700)) {
            $temp_dir = false;
        } else {
            if ($delete) {
                Horde::deleteAtShutdown($temp_dir);
            }
        }
        umask($old_umask);

        return $temp_dir;
    }

    /**
     * Start output compression, if requested.
     *
     * @access public
     *
     * @since Horde 2.2
     */
    function compressOutput()
    {
        static $started;

        if (isset($started)) {
            return;
        }

        require_once HORDE_BASE . '/lib/Server.php';

        /* Compress output if requested and possible. */
        global $conf;
        if ($conf['compress_pages'] &&
            Server::allowOutputCompression()) {
            if (ob_get_level()) {
                ob_end_clean();
            }
            ob_start('ob_gzhandler');
        }

        $started = true;
    }

    /**
     * Get the theme configuration for a particular application. This is
     * necessary for things like application specific mouseovers to work.
     *
     * @access public
     *
     * @param optional string $base  The base path for the application.
     *                               If given, will attempt to load application
     *                               specific CSS theme information.
     *
     * @return string  Requested theme or empty if no theme selected.
     */
    function getThemeConfig($base = null)
    {
        /* We need $registry because the theme files may use it. */
        global $prefs, $css, $registry;

        require_once HORDE_BASE . '/lib/Browser.php';
        $browser = &Browser::singleton();

        $theme = '';

        include_once HORDE_BASE . '/config/html.php';
        if (!is_null($base)) {
            @include_once $base . '/config/html.php';
        }
        if (isset($prefs)) {
            $theme = $prefs->getValue('theme');
            if (!empty($theme)) {
                @include_once HORDE_BASE . '/config/themes/html-' . $theme . '.php';
                if (!is_null($base)) {
                    @include_once $base . '/config/themes/html-' . $theme . '.php';
                }
            }
        }

        return $theme;
    }

    /**
     * Returns the <link> tag for the CSS stylesheet.
     *
     * @access public
     *
     * @param optional string  $theme    The theme to use.
     * @param optional string  $app      The Horde application.
     * @param optional boolean $inherit  Inherit Horde-wide CSS?
     *
     * @return string  A <link> tag for a CSS stylesheet.
     */
    function stylesheetLink($theme = null, $app = null, $inherit = true)
    {
        global $registry;

        $css_link = Horde::url($registry->getParam('webroot', 'horde') . '/css.php', false, -1);
        if (!is_null($app)) {
            if (substr($app, 0, 3) != 'app') {
                $app = 'app=' . $app;
            }
            $css_link = $css_link . '?' . $app;
        }
        if ($inherit === false) {
            $css_link = Horde::addParameter($css_link, 'inherit', 'no');
        }
        if (!is_null($theme) && !empty($theme)) {
            $css_link = Horde::addParameter($css_link, 'theme', htmlspecialchars($theme));
        }

        return '<link href="' . $css_link . '" rel="stylesheet" type="text/css" />';
    }

    /**
     * If there is a custom session handler, set it up now.
     *
     * @access public
     */
    function setupSessionHandler()
    {
        global $conf;

        ini_set('session.use_trans_sid', 0);
        session_set_cookie_params($conf['session']['timeout'], $conf['cookie']['path'], $conf['cookie']['domain']);
        session_cache_limiter($conf['session']['cache_limiter']);
        session_name(urlencode($conf['session']['name']));

        $type = !empty($conf['sessionhandler']['type']) ? $conf['sessionhandler']['type'] : 'none';

        if ($type == 'external') {
            $calls = $conf['sessionhandler']['params'];
            session_set_save_handler($calls['open'],
                                     $calls['close'],
                                     $calls['read'],
                                     $calls['write'],
                                     $calls['destroy'],
                                     $calls['gc']);
        } elseif ($type != 'none') {
            global $_session_handler;
            require_once HORDE_BASE . '/lib/SessionHandler.php';
            $_session_handler = &SessionHandler::singleton($conf['sessionhandler']['type']);
            if (!empty($_session_handler) &&
                !is_a($_session_handler, 'PEAR_Error')) {
                ini_set('session.save_handler', 'user');
                session_set_save_handler(array($_session_handler, 'open'),
                                         array($_session_handler, 'close'),
                                         array($_session_handler, 'read'),
                                         array($_session_handler, 'write'),
                                         array($_session_handler, 'destroy'),
                                         array($_session_handler, 'gc'));
            } else {
                Horde::fatal(PEAR::raiseError('Horde is unable to correctly start the custom session handler.'), __FILE__, __LINE__, false);
            }
        }
    }

    /**
     * Removes given elements at request shutdown.
     *
     * If called with a filename will delete that file at request shutdown; if
     * called with a directory will remove that directory and all files in that
     * directory at request shutdown.
     *
     * If called with no arguments, return all elements to be deleted (this
     * should only be done by Horde::_deleteAtShutdown).
     *
     * The first time it is called, it initializes the array and registers
     * Horde::_deleteAtShutdown() as a shutdown function - no need to do so
     * manually.
     *
     * The second parameter allows the unregistering of previously registered
     * elements.
     *
     * @access public
     *
     * @param optional string $filename   The filename to be deleted at the end
     *                                    of the request.
     * @param optional boolean $register  If true, then register the element for
     *                                    deletion, otherwise, unregister it.
     * @param optional boolean $secure    If deleting file, should we securely
     *                                    delete the file?
     */
    function deleteAtShutdown($filename = false, $register = true,
                              $secure = false)
    {
        static $dirs, $files, $securedel;

        /* Initialization of variables and shutdown functions. */
        if (is_null($dirs)){
            $dirs = array();
            $files = array();
            $securedel = array();
            register_shutdown_function(array('Horde', '_deleteAtShutdown'));
        }

        if ($filename) {
            if ($register) {
                if (@is_dir($filename)) {
                    $dirs[$filename] = true;
                } else {
                    $files[$filename] = true;
                }
                if ($secure) {
                    $securedel[$filename] = true;
                }
            } else {
                unset($dirs[$filename]);
                unset($files[$filename]);
                unset($securedel[$filename]);
            }
        } else {
            return array($dirs, $files, $securedel);
        }
    }

    /**
     * Delete registered files at request shutdown.
     *
     * This function should never be called manually; it is registered as a
     * shutdown function by Horde::deleteAtShutdown() and called automatically
     * at the end of the request. It will retrieve the list of folders and files
     * to delete from Horde::deleteAtShutdown()'s static array, and then iterate
     * through, deleting folders recursively.
     *
     * Contains code from gpg_functions.php.
     * Copyright (c) 2002-2003 Braverock Ventures
     *
     * @access private
     */
    function _deleteAtShutdown()
    {
        $registered = Horde::deleteAtShutdown();
        $dirs = $registered[0];
        $files = $registered[1];
        $secure = $registered[2];

        foreach ($files as $file => $val) {
            /* Delete files */
            if ($val && @file_exists($file)) {
                /* Should we securely delete the file by overwriting the
                   data with a random string? */
                if (isset($secure[$file])) {
                    $random_str = '';
                    for ($i = 0; $i < filesize($file); $i++) {
                        $random_str .= chr(mt_rand(0, 255));
                    }
                    $fp = fopen($file, 'r+');
                    fwrite($fp, $random_str);
                    fclose($fp);
                }
                @unlink($file);
            }
        }

        foreach ($dirs as $dir => $val) {
            /* Delete directories */
            if ($val && @file_exists($dir)) {
                /* Make sure directory is empty. */
                $dir_class = dir($dir);
                while (false !== ($entry = $dir_class->read())) {
                    if ($entry != '.' && $entry != '..') {
                        @unlink($dir . '/' . $entry);
                    }
                }
                $dir_class->close();
                @rmdir($dir);
            }
        }
    }

    /**
     * Returns an un-used access key from the label given.
     *
     * @access public
     *
     * @param string $label The label to choose an access key from.
     *
     * @return string  A single lower case character access key or empty
     *                 string if none can be found
     */
    function getAccessKey($label)
    {
        require_once HORDE_BASE . '/lib/String.php';

        /* The access keys already used in this page */
        static $_used;

        if (is_null($_used)) {
            $_used = array();
        }

        $letters = array();
        $label = strip_tags($label);

        /* Try the upper case characters first */
        $iMax = strlen($label);
        for ($i = 0; $i < $iMax; $i++) {
            $c = substr($label, $i, 1);
            if (preg_match('/[A-Z]/', $c)) {
                $lower = String::lower($c, true);
                if (!isset($_used[$lower])) {
                    $_used[$lower] = 1;
                    return $c;
                }
            } else {
                $letters[] = $c;
            }
        }

        /* Try the lower case characters next */
        foreach ($letters as $c) {
            if (!isset($_used[$c]) && preg_match('/[a-z]/', $c)) {
                $_used[$c] = 1;
                return $c;
            }
        }

        /* Didn't find one so return an empty string */
        return '';
    }

    /**
     * Highlight an access key in a label.
     *
     * @access public
     *
     * @param string $label      The label to to highlight the access key in.
     * @param string $accessKey  The access key to highlight.
     *
     * @return string  The HTML version of the label with the access key
     *                 highlighted.
     */
    function highlightAccessKey($label, $accessKey)
    {
        include_once HORDE_BASE . '/config/nls.php';
        global $nls;

        $html = '';

        if (empty($accessKey)) {
            return $label;
        }

        if (isset($nls['multibyte'][NLS::getCharset(true)])) {
            $html = $label . '(' . '<span class="accessKey">'
                           . strtoupper($accessKey) . '</span>' . ')';
        } else {
            $pos = String::pos($label, $accessKey);
            if ($pos === false) {
               return $label;
            }

            if ($pos > 0) {
                $html .= String::substr($label, 0, $pos);
            }
            $html .= '<span class="accessKey">' . String::substr($label, $pos, 1) . '</span>';
            if ($pos < String::length($label) - 1) {
                $html .= String::substr($label, $pos + 1);
            }
        }

        return $html;
    }

    /**
     * Returns a label element including an access key for usage
     * in conjuction with a form field. User preferences regarding
     * access keys are respected.
     *
     * @param string $for    The form field's id attribute.
     * @param string $label  The label text.
     * @param string $ak     The access key to use. If null a new access key
     *                       will be generated.
     *
     * @return string  The html code for the label element.
     */
    function label($for, $label, $ak = null)
    {
        global $prefs;

        if (isset($prefs) && $prefs->getValue('widget_accesskey')) {
            if (is_null($ak)) {
                $ak = Horde::getAccesskey($label);
            }
            $label = Horde::highlightAccessKey($label, $ak);
        }

        return sprintf('<label for="%s"%s>%s</label>',
                     $for,
                     !empty($ak) ? ' accesskey="' . $ak . '"' : '',
                     $label);
    }

    /**
     * Output javascript code to close the current window.
     *
     * @access public
     *
     * @param string $code  Any addtional javascript code to run before
     *                      closing the window.
     */
    function closeWindowJS($code = null)
    {
        echo '<script language="JavaScript" type="text/javascript">' . $code . 'window.close();</script>';
    }

    /**
     * Redirect to the main Horde login page on authentication failure.
     *
     * @access public
     */
    function authenticationFailureRedirect()
    {
        global $registry;

        $url = $registry->getParam('webroot', 'horde') . '/login.php';
        $url = Horde::addParameter($url, 'url', Horde::selfURL(true));
        header('Location: ' . Horde::url($url, true));
        exit;
    }

    /**
     * Caches the result of extension_loaded() calls.
     *
     * @access private
     *
     * @param string $ext  The extension name.
     *
     * @return boolean  Is the extension loaded?
     */
    function extensionExists($ext)
    {
        static $cache;

        if (!isset($cache)) {
            $cache = array();
        }
        if (!isset($cache[$ext])) {
            $cache[$ext] = extension_loaded($ext);
        }

        return $cache[$ext];
    }

    /**
     * Use DOM Tooltips (via javascript) to display the 'title' attribute
     * for Horde::link() calls.
     *
     * If using this function, the following function must be called:
     *   Horde::addScriptFile('tooltip.js', 'horde');
     * Additionally, this line must appear in the body of the page:
     *   <div id="tooltip" style="position:absolute;visibility:hidden;"></div>
     *
     * @access public
     *
     * @param string $url                 The full URL to be linked to
     * @param optional string $status     The JavaScript mouse-over string
     * @param optional string $class      The CSS class of the link
     * @param optional string $target     The window target to point to.
     * @param optional string $onclick    JavaScript action for the 'onclick'
     *                                    event.
     * @param optional string $title      The link title (tooltip).
     * @param optional string $accesskey  The access key to use.
     *
     * @return string  The full <a href> tag.
     */
    function linkTooltip($url, $status = '', $class = '', $target = '',
                         $onclick = '', $title = '', $accesskey = '')
    {
        $url = substr(Horde::link($url, null, $class, $target, $onclick, null, $accesskey), 0, -1);

        if (empty($status)) {
            $status = _("Preview Text");
        } else {
            $status = @htmlspecialchars(addslashes($status), ENT_QUOTES, NLS::getCharset());
        }

        $url .= ' onmouseover="tooltipLink(\'' . @htmlspecialchars('<pre>' . strtr($title, array("\r" => '', "\n" => '<br />')) . '</pre>', ENT_QUOTES, NLS::getCharset()) . '\', \'' . $status . '\'); return true;" onmouseout="tooltipClose();">';

        return $url;
    }

    /**
     * Provides a standardised function to call a Horde hook, checking whether
     * a hook config file exists and whether the function itself exists. If
     * these two conditions are not satisfied it will return a PEAR error to
     * differentiate from a hook which returns false or empty.
     *
     * @access public
     *
     * @param string $hook          The function to call.
     * @param optional array  $app  An array of any arguments to pass to the
     *                              hook function.
     * @param optional string $app  If specified look for hooks in the config
     *                              directory of this app.
     *
     * @return mixed  Either the results of the hook or PEAR error on failure.
     */
    function callHook($hook, $args = array(), $app = 'horde')
    {
        global $registry;

        if (file_exists($registry->getParam('fileroot', $app) . '/config/hooks.php')) {
            require_once $registry->getParam('fileroot', $app) . '/config/hooks.php';
            if (function_exists($hook)) {
                return call_user_func_array($hook, $args);
            }
        }

        $error = PEAR::raiseError(sprintf(_("Could not call function %s in application %s."), $hook, $app));
        Horde::logMessage($error, __FILE__, __LINE__, PEAR_LOG_DEBUG);

        return $error;
    }

}
