<?php
// Compose encryption options
/**
 * Send Message w/no encryption.
 */
define('IMP_ENCRYPT_NONE', 1);

/**
 * Send Message - PGP Encrypt.
 */
define('IMP_PGP_ENCRYPT', 2);

/**
 * Send Message - PGP Sign.
 */
define('IMP_PGP_SIGN', 3);

/**
 * Send Message - PGP Sign/Encrypt.
 */
define('IMP_PGP_SIGNENC', 4);

/**
 * Send Message - S/MIME Encrypt.
 */
define('IMP_SMIME_ENCRYPT', 5);

/**
 * Send Message - S/MIME Sign.
 */
define('IMP_SMIME_SIGN', 6);

/**
 * Send Message - S/MIME Sign/Encrypt.
 */
define('IMP_SMIME_SIGNENC', 7);

// IMAP Flags
/**
 * Match all IMAP flags.
 */
define('IMP_ALL', 0);

/**
 * \\UNSEEN flag
.*/
define('IMP_UNSEEN', 1);

/**
 * \\DELETED flag
.*/
define('IMP_DELETED', 2);

/**
 * \\ANSWERED flag.
 */
define('IMP_ANSWERED', 4);

/**
 * \\FLAGGED flag.
 */
define('IMP_FLAGGED', 8);

/**
 * \\DRAFT flag.
 */
define('IMP_DRAFT', 16);

/**
 * An email is personal.
 */
define('IMP_PERSONAL', 32);

// IMAP Sorting Constant
/**
 * Sort By Thread.
 */
@define('SORTTHREAD', 161);

// IMP Mailbox view constants
/**
 * Start on the page with the first unseen message.
 */
define('IMP_MAILBOXSTART_FIRSTUNSEEN', 1);

/**
 * Start on the page with the last unseen message.
 */
define('IMP_MAILBOXSTART_LASTUNSEEN', 2);

/**
 * Start on the first page.
 */
define('IMP_MAILBOXSTART_FIRSTPAGE', 3);

/**
 * Start on the last page.
 */
define('IMP_MAILBOXSTART_LASTPAGE', 4);

// IMP mailbox labels
/**
 * The mailbox name to use for search results.
 */
define('IMP_SEARCH_MBOX', '**search');

/**
 * The mailbox prefix to use for virtual folder selection.
 */
define('IMP_VFOLDER_PREFIX', '**vfolder_');

// IMP internal indexing strings
/**
 * String used to separate messages.
 */
define('IMP_MSG_SEP', "\0");

/**
 * String used to separate indexes.
 */
define('IMP_IDX_SEP', "\1");

/**
 * IMP Base Class.
 *
 * $Horde: imp/lib/IMP.php,v 1.449 2004/12/02 16:27:53 chuck Exp $
 *
 * Copyright 1999-2004 Chuck Hagenbuch <chuck@horde.org>
 * Copyright 1999-2004 Jon Parise <jon@horde.org>
 * Copyright 2002-2004 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  Michael Slusarz <slusarz@bigworm.colorado.edu>
 * @version $Revision: 1.449 $
 * @since   IMP 2.3.5
 * @package IMP
 */
class IMP {

    /**
     * Returns the AutoLogin server key.
     *
     * @access public
     *
     * @param optional boolean $first  Return the first value?
     *
     * @return string  The server key.
     */
    function getAutoLoginServer($first = false)
    {
        require IMP_BASE . '/config/servers.php';
        foreach ($servers as $key => $curServer) {
            if (empty($server_key) && substr($key, 0, 1) != '_') {
                $server_key = $key;
            }
            if (IMP::isPreferredServer($curServer, ($first) ? $key : null)) {
                $server_key = $key;
                if ($first) {
                    break;
                }
            }
        }

        return $server_key;
    }

    /**
     * Returns whether we can log in without a login screen for $server_key.
     *
     * @param string $server_key  The server to check. Defaults to
     *                            IMP::getCurrentServer().
     * @param boolean $force      If true, check $server_key even if there is
     *                            more than one server available.
     *
     * @return boolean  True or false.
     */
    function canAutoLogin($server_key = null, $force = false)
    {
        require IMP_BASE . '/config/servers.php';

        $auto_server = IMP::getAutoLoginServer();
        if (is_null($server_key)) {
            $server_key = $auto_server;
        }

        return (((count($auto_server) == 1) || $force) &&
                Auth::getAuth() &&
                !empty($servers[$server_key]['hordeauth']));
    }

    /**
     * Makes sure the user has been authenticated to view the page.
     *
     * @access public
     *
     * @param mixed $flags     Any flags to pass to imap_open(). See
     *                         Auth_imp::authenticate(). However, if this is
     *                         the string 'horde', we just check for Horde auth
     *                         and don't bother the IMAP server.
     * @param boolean $return  If this is true, return false instead of
     *                         exiting/redirecting if authentication fails.
     *
     * @return boolean  True on success, false on error.
     */
    function checkAuthentication($flags = 0, $return = false)
    {
        if ($flags === 'horde') {
            $reason = Auth::isAuthenticated();
        } else {
            $auth_imp = &Auth::singleton(array('imp', 'imp'));
            $auth_imp->authenticateOptions(array('flags' => $flags));
            $reason = $auth_imp->authenticate();
        }

        if ($reason !== true) {
            if ($return) {
                return false;
            }

            if (Util::getFormData('popup')) {
                Util::closeWindowJS();
            } else {
                $url = Auth::addLogoutParameters(IMP::logoutUrl());
                $url = Util::addParameter($url, 'url', Horde::selfUrl(true));
                header('Location: ' . $url);
            }
            exit;
        }

        return true;
    }

    /**
     * Determines if the given mail server is the "preferred" mail server for
     * this web server.  This decision is based on the global 'SERVER_NAME'
     * and 'HTTP_HOST' server variables and the contents of the 'preferred'
     * either field in the server's definition.  The 'preferred' field may
     * take a single value or an array of multiple values.
     *
     * @param string $server  A complete server entry from the $servers hash.
     * @param TODO $key       TODO
     *
     * @return boolean  True if this entry is "preferred".
     */
    function isPreferredServer($server, $key = null)
    {
        static $urlServer;

        if (!isset($urlServer)) {
            $urlServer = Util::getFormData('server');
        }

        if (!empty($urlServer)) {
            return ($key == $urlServer);
        }

        if (!empty($server['preferred'])) {
            if (is_array($server['preferred'])) {
                if (in_array($_SERVER['SERVER_NAME'], $server['preferred']) ||
                    in_array($_SERVER['HTTP_HOST'], $server['preferred'])) {
                    return true;
                }
            } elseif (($server['preferred'] == $_SERVER['SERVER_NAME']) ||
                      ($server['preferred'] == $_SERVER['HTTP_HOST'])) {
                return true;
            }
        }

        return false;
    }

    /**
     * Returns any prefixes that need to be put at the beginning of the folder
     * path.
     *
     * @return string  The prefix, if any.
     */
    function preambleString()
    {
        return $_SESSION['imp']['folders'] . $_SESSION['imp']['namespace'];
    }

    /**
     * Tacks on any prefixes that need to be put at the beginning of the
     * folder path, but don't add it to INBOX, and return the full mailbox
     * name.
     *
     * @param string $mailbox   The folder path that needs the prefixes to be
     *                          prepended.
     *
     * @return string  The full folder path with prefixes if needed.
     */
    function addPreambleString($mailbox)
    {
        if (empty($mailbox) || ($mailbox == 'INBOX')) {
            return $mailbox;
        }
        return IMP::preambleString() . $mailbox;
    }

    /**
     * Removes any prefixes from a full folder path.
     *
     * @param string $mailbox  The folder path to strip the prefix from
     *
     * @return string  The folder path without prefixes.
     */
    function stripPreambleString($mailbox)
    {
        $preamble = IMP::preambleString();
        if (substr($mailbox, 0, strlen($preamble)) == $preamble) {
            $mailbox = substr($mailbox, strlen($preamble));
        }
        return $mailbox;
    }

    /**
     * Generates a full c-client server specification string.
     *
     * @access public
     *
     * @param string $mbox      The mailbox to append to end of the server
     *                          string.
     * @param string $protocol  Override the protocol currently being used.
     *
     * @return string  The full spec string.
     */
    function serverString($mbox = null, $protocol = null)
    {
        $srvstr = '{' . $_SESSION['imp']['server'];

        /* If port is not specified, don't include it in the string. */
        if (!empty($_SESSION['imp']['port'])) {
            $srvstr .= ':' . $_SESSION['imp']['port'];
        }

        if (is_null($protocol)) {
            $protocol = $_SESSION['imp']['protocol'];
        }

        /* If protocol is not specified, don't include it in the string. */
        if (!empty($protocol)) {
            $srvstr .= '/' . $protocol;
        }

        return $srvstr . '}' . $mbox;
    }

    /**
     * Returns the plain text label that is displayed for the current mailbox,
     * replacing IMP_SEARCH_MBOX with an appropriate string and removing
     * namespace and folder prefix information from what is shown to the user.
     *
     * @access public
     *
     * @return string  The plain text label.
     */
    function getLabel()
    {
        global $imp;

        $label = '';

        if ($imp['mailbox'] == 'INBOX') {
            $label = _("Inbox");
        } elseif ($imp['mailbox'] == IMP_SEARCH_MBOX) {
            require_once IMP_BASE . '/lib/Search.php';
            $label = IMP_Search::searchLabel();
        } else {
            $label = String::convertCharset($imp['mailbox'], 'UTF7-IMAP');
            if (strcmp($imp['folders'], String::substr($label, 0, String::length($imp['folders']))) == 0) {
                $label = String::substr($label, String::length($imp['folders']));
            }
            if (strcmp($imp['namespace'], String::substr($label, 0, String::length($imp['namespace']))) == 0) {
                $label = String::substr($label, String::length($imp['namespace']));
            }
        }

        return $label;
    }

    /**
     * Returns the bare address.
     *
     * @access public
     *
     * @param string $address    The address string.
     * @param boolean $multiple  Should we return multiple results?
     *
     * @return mixed  See {@link MIME::bareAddress}.
     */
    function bareAddress($address, $multiple = false)
    {
        global $imp;

        require_once 'Horde/MIME.php';
        return MIME::bareAddress($address, $imp['maildomain'], $multiple);
    }

    /**
     * Uses the Registry to expand names and returning error information for
     * any address that is either not valid or fails to expand.
     *
     * @param string $addrString      The name(s) or address(es) to expand.
     * @param optional boolean $full  If true generate a full, rfc822-valid
     *                                address list.
     *
     * @return mixed   Either a string containing all expanded addresses or an
     *                 array containing all matching address or an error
     *                 object.
     */
    function expandAddresses($addrString, $full = false)
    {
        if (!preg_match('|[^\s]|', $addrString)) {
            return '';
        }

        global $prefs;

        require_once 'Mail/RFC822.php';
        require_once 'Horde/MIME.php';

        $parser = &new Mail_RFC822(null, '@INVALID');
        $search_fields = array();

        $src = explode("\t", $prefs->getValue('search_sources'));
        if ((count($src) == 1) && empty($src[0])) {
            $src = array();
        }

        if (($val = $prefs->getValue('search_fields'))) {
            $field_arr = explode("\n", $val);
            foreach ($field_arr as $field) {
                $field = trim($field);
                if (!empty($field)) {
                    $tmp = explode("\t", $field);
                    if (count($tmp) > 1) {
                        $source = array_splice($tmp, 0, 1);
                        $search_fields[$source[0]] = $tmp;
                    }
                }
            }
        }

        $arr = MIME::rfc822Explode($addrString, ',');
        $arr = array_map('trim', $arr);

        $results = $GLOBALS['registry']->call('contacts/search', array($arr, $src, $search_fields));
        if (is_a($results, 'PEAR_Error')) {
            return $results;
        }

        $ambiguous = false;
        $error = false;
        $i = 0;
        $missing = array();

        foreach ($results as $res) {
            $tmp = $arr[$i];
            if ($parser->validateMailbox(MIME::encodeAddress($tmp, null, ''))) {
                // noop
            } elseif (count($res) == 1) {
                if ($full) {
                    if (strpos($res[0]['email'], ',') !== false) {
                        $arr[$i] = $res[0]['name'] . ': ' . $res[0]['email'] . ';';
                    } else {
                        list($mbox, $host) = explode('@', $res[0]['email']);
                        $arr[$i] = MIME::rfc822WriteAddress($mbox, $host, $res[0]['name']);
                    }
                } else {
                    $arr[$i] = $res[0]['name'];
                }
            } elseif (count($res) > 1) {
                /* Handle the multiple case - we return an array with all found
                   addresses. */
                $arr[$i] = array($arr[$i]);
                foreach ($res as $one_res) {
                    if ($full) {
                        if (strpos($one_res['email'], ',') !== false) {
                            $arr[$i][] = $one_res['name'] . ': ' . $one_res['email'] . ';';
                        } else {
                            $mbox_host = explode('@', $one_res['email']);
                            if (isset($mbox_host[1])) {
                                $arr[$i][] = MIME::rfc822WriteAddress($mbox_host[0], $mbox_host[1], $one_res['name']);
                            }
                        }
                    } else {
                        $arr[$i][] = $one_res['name'];
                    }
                }
                $ambiguous = true;
            } else {
                /* Handle the missing/invalid case - we should return error info
                   on each address that couldn't be expanded/validated. */
                $error = true;
                if (!$ambiguous) {
                    $arr[$i] = PEAR::raiseError(null, null, null, null, $arr[$i]);
                    $missing[$i] = $arr[$i];
                }
            }
            $i++;
        }

        if ($ambiguous) {
            foreach ($missing as $i => $addr) {
                $arr[$i] = $addr->getUserInfo();
            }
            return $arr;
        } elseif ($error) {
            return PEAR::raiseError(_("Please resolve ambiguous or invalid addresses."), null, null, null, $arr);
        } else {
            return implode(', ', $arr);
        }
    }

    /**
     * Wrapper around IMP_Folder::flist() which generates the body of a
     * &lt;select&gt; form input from the generated folder list. The
     * &lt;select&gt; and &lt;/select&gt; tags are NOT included in the output
     * of this function.
     *
     * @access public
     *
     * @param string $heading      An optional string to use as the label for
     *                             an empty-value option at the top of the list
     * @param bool $abbrev         If true, abbreviate long mailbox names by
     *                             replacing the middle of the name with '...'.
     * @param array $filter        An array of mailboxes to ignore.
     * @param string $selected     The mailbox to have selected by default.
     * @param bool $new_folder     If true, display an option to create a new
     *                             folder.
     * @param bool $inc_tasklists  Should the user's editable tasklists be
     *                             included in the list?
     * @param bool $inc_vfolder    Should the user's virtual folders be
     *                             included in the list?
     *
     * @return string  A string containg <option> elements for each mailbox in
     *                 the list.
     */
    function flistSelect($heading = '', $abbrev = true, $filter = array(),
                         $selected = null, $new_folder = false,
                         $inc_tasklists = false, $inc_vfolder = false)
    {
        global $registry;

        require_once 'Horde/Text.php';
        require_once IMP_BASE . '/lib/Folder.php';

        $imp_folder = &IMP_Folder::singleton();

        /* Don't filter here - since we are going to parse through every
         * member of the folder list below anyway, we can filter at that time.
         * This allows us the have a single cached value for the folder list
         * rather than a cached value for each different mailbox we may
         * visit. */
        $mailboxes = $imp_folder->flist_IMP();
        $text = '';

        if (strlen($heading) > 0) {
            $text .= '<option value="">' . $heading . "</option>\n";
        }

        if ($new_folder) {
            $text .= '<option value="">----</option>' . "\n";
            $text .= '<option value="*new*">' . _("New Folder") . "</option>\n";
            $text .= '<option value="">----</option>' . "\n";
        }

        /* Add the list of mailboxes to the lists. */
        $filter = array_flip($filter);
        foreach ($mailboxes as $mbox) {
            if (isset($filter[$mbox['val']])) {
                continue;
            }

            $sel = ($mbox['val'] && ($mbox['val'] === $selected)) ? ' selected="selected"' : '';
            $val = htmlspecialchars($mbox['val']);
            $label = ($abbrev) ? $mbox['abbrev'] : $mbox['label'];
            $text .= sprintf('<option value="%s"%s>%s</option>%s', $val, $sel, Text::htmlSpaces($label), "\n");
        }

        /* Add the list of virtual folders to the list. */
        if ($inc_vfolder && !empty($_SESSION['imp']['vfolder'])) {
            $vfolders = IMP_VFolder::listVFolders();
            if (!empty($vfolders)) {
                $text .= '<option value="">----</option>' . "\n";
                foreach ($vfolders as $id => $val) {
                    $sel = (($GLOBALS['imp']['mailbox'] == IMP_SEARCH_MBOX) && ($id == $_SESSION['imp']['search']['vfolder_id'])) ? ' selected="selected"' : '';
                    $text .= sprintf('<option value="%s"%s>%s</option>%s', IMP_VFOLDER_PREFIX . $id, $sel, Text::htmlSpaces($val), "\n");
                }
            }
        }

        /* Add the list of editable tasklists to the list. */
        if ($inc_tasklists && $_SESSION['imp']['tasklistavail']) {
            $tasklists = $registry->call('tasks/listTasklists',
                                         array(false, PERMS_EDIT));

            if (!is_a($tasklists, 'PEAR_Error') && count($tasklists)) {
                $text .= '<option value="">----</option>' . "\n";

                foreach ($tasklists as $id => $tasklist) {
                    $text .= sprintf('<option value="%s">%s</option>%s',
                                     '_tasklist_' . $id,
                                     Text::htmlSpaces($tasklist->get('name')),
                                     "\n");
                }
            }
        }

        return $text;
    }

    /**
     * Returns the current mailbox.
     *
     * @access public
     *
     * @param integer $array_index  The array index of the current message.
     *
     * @return string  The current mailbox name.
     */
    function getThisMailbox($array_index = null)
    {
        global $imp;

        if (!is_null($array_index) && ($imp['mailbox'] == IMP_SEARCH_MBOX)) {
            $msgs = explode(IMP_MSG_SEP, $imp['msgl']);
            if (isset($msgs[$array_index])) {
                return substr($msgs[$array_index], strpos($msgs[$array_index], IMP_IDX_SEP) + 1);
            }
        }

        return $imp['thismailbox'];
    }

    /**
     * Checks for To:, Subject:, Cc:, and other compose window arguments and
     * pass back either a URI fragment or an associative array with any of
     * them which are present.
     *
     * @access public
     *
     * @param string $format  Either 'uri' or 'array'.
     *
     * @return string  A URI fragment or an associative array with any compose
     *                 arguments present.
     */
    function getComposeArgs()
    {
        $args = array();
        $fields = array('to', 'cc', 'bcc', 'msg', 'subject');

        foreach ($fields as $val) {
            if (($$val = Util::getFormData($val))) {
                $args[$val] = $$val;
            }
        }

        /* Decode mailto: URLs. */
        if (isset($args['to']) && (strpos($args['to'], 'mailto:') === 0)) {
            $mailto = @parse_url($args['to']);
            if (is_array($mailto)) {
                $args['to'] = $mailto['path'];
                if (!empty($mailto['query'])) {
                    parse_str($mailto['query'], $vals);
                    foreach ($fields as $val) {
                        if (isset($vals[$val])) {
                            $args[$val] = $vals[$val];
                        }
                    }
                }
            }
        }

        return $args;
    }

    /**
     * Returns the initial URL.
     *
     * @access public
     *
     * @param string $actionID  The action ID to perform on the initial page.
     * @param boolean $encode   If true the argument separator gets encoded.
     *
     * @return string  The initial URL.
     */
    function getInitialUrl($actionID = null, $encode = true)
    {
        global $prefs;

        $init_url = $prefs->getValue('initial_page');
        if ($init_url == 'folders.php') {
            $url = Horde::applicationUrl($init_url, !$encode);
            $url = Util::addParameter($url, IMP::getComposeArgs(), null, $encode);
        } else {
            $url = Horde::applicationUrl('mailbox.php', !$encode);
            $url = Util::addParameter($url, 'mailbox', IMP::addPreambleString($init_url), $encode);
            $url = Util::addParameter($url, IMP::getComposeArgs(), null, $encode);
        }

        if (!empty($actionID)) {
            $url = Util::addParameter($url, 'actionID', $actionID, $encode);
        }

        return $url;
    }

    /**
     * Open a compose window.
     */
    function openComposeWin($options = array())
    {
        global $prefs;

        if ($prefs->getValue('compose_popup')) {
            return true;
        } else {
            $options += IMP::getComposeArgs();
            $url = Util::addParameter(Horde::applicationUrl('compose.php', true),
                                      $options, null, false);
            header('Location: ' . $url);
            return false;
        }
    }

    /**
     * Returns the appropriate link to call the message composition screen.
     *
     * @access public
     *
     * @param mixed $args   List of arguments to pass to compose.php. If this
     *                      is passed in as a string, it will be parsed as a
     *                      toaddress?subject=foo&cc=ccaddress (mailto-style)
     *                      string.
     * @param array $extra  Hash of extra, non-standard arguments to pass to
     *                      compose.php.
     *
     * @return string  The link to the message composition screen.
     */
    function composeLink($args = array(), $extra = array())
    {
        global $prefs, $browser;

        /* Make sure the compose window always knows which mailbox it's in,
           for replying, forwarding, marking as answered, etc. */
        if (!isset($extra['thismailbox'])) {
            $extra['thismailbox'] = IMP::getThisMailbox((isset($extra['array_index'])) ? $extra['array_index'] : null);
        }

        if (is_string($args)) {
            $string = $args;
            $args = array();
            if (($pos = strpos($string, '?')) !== false) {
                parse_str(substr($string, $pos + 1), $args);
                $args['to'] = substr($string, 0, $pos);
            } else {
                $args['to'] = $string;
            }
        }

        /* Merge the two argument arrays. */
        $args = array_merge($args, $extra);

        /* Convert the $args hash into proper URL parameters. */
        $url = '';
        foreach ($args as $key => $val) {
            if (!empty($val) || is_int($val)) {
                $url = Util::addParameter($url, $key, $val);
            }
        }

        if ($prefs->getValue('compose_popup') &&
            $browser->hasFeature('javascript')) {
            Horde::addScriptFile('open_compose_win.js');
            return 'javascript:open_compose_win(\'' . $browser->escapeJSCode(substr($url, 1)) . '\');';
        } else {
            return Horde::applicationUrl('compose.php' . $url);
        }
    }

    /**
     * Generates an URL to the logout screen that includes any known
     * information, such as username, server, etc., that can be filled in on
     * the login form.
     *
     * @return string  Logout URL with logout parameters added.
     */
    function logoutUrl()
    {
        $params = array(
            'imapuser' => isset($_SESSION['imp']['user']) ? $_SESSION['imp']['user'] :
                                                            Util::getFormData('imapuser'),
            'server'   => isset($_SESSION['imp']['server']) ? $_SESSION['imp']['server'] :
                                                              Util::getFormData('server'),
            'port'     => isset($_SESSION['imp']['port']) ? $_SESSION['imp']['port'] :
                                                            Util::getFormData('port'),
            'protocol' => isset($_SESSION['imp']['protocol']) ? $_SESSION['imp']['protocol'] :
                                                                Util::getFormData('protocol'),
            'folders'  => isset($_SESSION['imp']['folders']) ? $_SESSION['imp']['folders'] :
                                                               Util::getFormData('folders'),
            'language' => isset($_SESSION['imp']['language']) ? $_SESSION['imp']['language'] :
                                                                Util::getFormData('language')
        );

        $url = 'login.php';

        foreach ($params as $key => $val) {
            if (!empty($val)) {
                $url = Util::addParameter($url, $key, $val, false);
            }
        }

        return Horde::applicationUrl($url, true);
    }

    /**
     * If there is information available to tell us about a prefix in front of
     * mailbox names that shouldn't be displayed to the user, then use it to
     * strip that prefix out.
     *
     * @param string $folder   The folder name to display.
     * @param boolean $decode  Whether or not the folder needs to be decoded
     *                         from UTF7-IMAP.
     *
     * @return string  The folder, with any prefix gone.
     */
    function displayFolder($folder, $decode = true)
    {
        $prefix = IMP::preambleString();
        if (substr($folder, 0, ($prefixLen = strlen($prefix))) == $prefix) {
            $folder = substr($folder, $prefixLen);
        }

        return $decode ? String::convertCharset($folder, 'UTF7-IMAP') : $folder;
    }

    /**
     * Filters a string, if requested.
     *
     * @access public
     *
     * @param string $text  The text to filter.
     *
     * @return string  The filtered text (if requested).
     */
    function filterText($text)
    {
        global $conf, $prefs;

        if ($prefs->getValue('filtering')) {
            require_once 'Horde/Text/Filter.php';
            $text = Text_Filter::filter($text, 'words', array('words_file' => $conf['msgsettings']['filtering']['words'], 'replacement' => $conf['msgsettings']['filtering']['replacement']));
        }

        return $text;
    }

    /**
     * Build IMP's list of menu items.
     *
     * @access public
     */
    function getMenu($returnType = 'object')
    {
        global $imp, $conf, $prefs, $registry;

        require_once 'Horde/Menu.php';

        $menu_search_url = Horde::applicationUrl('search.php');
        $menu_mailbox_url = Horde::applicationUrl('mailbox.php');
        $menu_allow_folders = ($conf['user']['allow_folders'] && isset($menu_folders));

        $menu = &new Menu(HORDE_MENU_MASK_ALL & ~HORDE_MENU_MASK_LOGIN);

        $menu->add(Util::addParameter($menu_mailbox_url, 'mailbox', 'INBOX'), _("_Inbox"), 'folders/inbox.png');
        if (($imp['base_protocol'] != 'pop3') && $prefs->getValue('use_trash') && ($prefs->getValue('trash_folder') !== null) && $prefs->getValue('empty_trash_menu')) {
            $menu_trash_url = Util::addParameter($menu_mailbox_url, 'thismailbox', IMP::preambleString() . $prefs->getValue('trash_folder'));
            $menu_trash_url = Util::addParameter($menu_trash_url, 'actionID', 'empty_mailbox');
            $menu_trash_url = Util::addParameter($menu_trash_url, 'return_url', IMP::getInitialUrl());
            $menu->add($menu_trash_url, _("Empty _Trash"), 'empty_trash.png', null, null, "d = new Date(); if (d.getTime() - menu_trash_confirmed > 100) { menu_trash_cvalue = window.confirm('" . addslashes(_("Are you sure you wish to empty your trash folder?")) . "'); d = new Date(); menu_trash_confirmed = d.getTime(); } return menu_trash_cvalue;", '__noselection');
        }
        $menu->add(IMP::composeLink(), _("_Compose"), 'compose.png');
        if ($conf['user']['allow_folders']) {
            $menu->add(Util::nocacheUrl(Horde::applicationUrl('folders.php')), _("_Folders"), 'folders/folder.png');
        }
        $menu->add($menu_search_url, _("_Search"), 'search.png', $registry->getImageDir('horde'));
        if ($prefs->getValue('fetchmail_menu')) {
            if ($prefs->getValue('fetchmail_popup')) {
                $menu->add('#', _("Acco_unts"), 'fetchmail.png', null, 'fetchmail', 'window.open(\'' . Horde::applicationUrl('fetchmail.php'). '\', \'fetchmail\', \'toolbar=no,location=no,status=yes,scrollbars=yes,resizable=yes,width=300,height=450,left=100,top=100\'); return false;');
            } else {
                $menu->add(Horde::applicationUrl('fetchmail.php'), _("Acco_unts"), 'fetchmail.png');
            }
        }
        if ($prefs->getValue('filter_menuitem')) {
            $menu->add(Horde::applicationUrl('filterprefs.php'), _("Fi_lters"), 'filters.png');
        }

        /* Logout. If IMP can auto login or IMP is providing
         * authentication, then we only show the logout link if the
         * sidebar isn't shown or if the configuration says to always
         * show the current user a logout link. */
        $impAuth = Auth::getProvider() == 'imp';
        $impAutoLogin = IMP::canAutoLogin();
        if (!($impAuth || $impAutoLogin) ||
            !$prefs->getValue('show_sidebar') ||
            Horde::showService('logout')) {

            /* If IMP provides authentication and the sidebar isn't
             * always on, target the main frame for logout to hide the
             * sidebar while logged out. */
            $logout_target = null;
            if ($impAuth || $impAutoLogin) {
                $logout_target = '_parent';
            }

            /* If IMP doesn't provide Horde authentication then we
             * need to use IMP's logout screen since logging out
             * should *not* end a Horde session. */
            if ($impAuth || $impAutoLogin) {
                $logout_url = Horde::getServiceLink('logout', 'horde', true);
            } else {
                $logout_url = Auth::addLogoutParameters(Horde::applicationUrl('login.php'), AUTH_REASON_LOGOUT);
            }

            $id = $menu->add($logout_url, _("_Log out"), 'logout.png', $registry->getImageDir('horde'), $logout_target);
            $menu->setPosition($id, HORDE_MENU_POS_LAST);
        }

        if ($returnType == 'object') {
            return $menu;
        } else {
            return $menu->render();
        }
    }

    /**
     * Outputs IMP's status/notification bar.
     *
     * @access public
     */
    function status()
    {
        global $imp, $notification;

        if (isset($imp['stream'])) {
            $alerts = imap_alerts();
            if (is_array($alerts)) {
                foreach ($alerts as $alert) {
                    $notification->push($alert, 'horde.message');
                }
            }
        }

        $notification->notify(array('listeners' => 'status'));
    }

    /**
     * Returns the javascript for a new message notification popup.
     *
     * @param array $newmsgs  Associative array with mailbox names as the keys
     *                        and the message count as the values
     *
     * @return string  The javascript for the popup message
     */
    function getNewMessagePopup($newmsgs)
    {
        $_alert = '';
        $count = 0;
        foreach ($newmsgs as $mb => $nm) {
            $count++;
            $_mailbox_message = $mb;
            $_alert .= IMP::displayFolder($_mailbox_message) .
                ($nm > 1 ? _(" - $nm new messages") : _(" - $nm new message")) . '\n';
        }
        if (!empty($_alert)) {
            if ($count == 1) {
                $mailboxOpenUrl = Horde::applicationUrl('mailbox.php', true);
                $mailboxOpenUrl = Util::addParameter($mailboxOpenUrl, 'no_newmail_popup' , 1);
                $mailboxOpenUrl = Util::addParameter($mailboxOpenUrl, 'mailbox', $_mailbox_message);

                return "if (confirm('" . addslashes(_("You have new mail in the following folder:")) . '\n' .
                    $_alert . addslashes(_("Do you want to open that folder?")) .
                    "')) { window.location.href = '" . str_replace('&amp;', '&', $mailboxOpenUrl) .
                    "'; window.focus(); }";
            } else {
                return "alert('" . addslashes(_("You have new mail in the following folders:")) .
                    '\n' . $_alert ."');";
            }
        }
    }

    /**
     * Generates the URL to the prefs page.
     *
     * @access public
     *
     * @param boolean $full  Generate full URL?
     *
     * @return string  The URL to the IMP prefs page.
     */
    function prefsURL($full = false)
    {
        return Horde::url($GLOBALS['registry']->get('webroot', 'horde') . '/services/prefs.php?app=imp', $full);
    }

}
