* Copyright 1999-2003 Jon Parise * * 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 * @author Jon Parise * @version $Revision: 1.198.2.60 $ * @since IMP 2.3.5 * @package imp */ class IMP { /** * 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. * * @access public * * @return mixed True on success, string with the failure reason on failure. */ function createSession() { global $conf, $registry; if (!isset($_POST['imapuser']) || !isset($_POST['pass']) || !isset($_POST['server'])) { return 'failed'; } $_SESSION['imp'] = array(); $_SESSION['imp']['user'] = trim(Horde::getFormData('imapuser')); /* Define these now so they're available to the vinfo hooks. */ $server = Horde::getFormData('server', ''); $realm = Horde::getFormData('realm', ''); /* Run the username through virtualhost expansion functions if necessary. */ if (!empty($conf['hooks']['vinfo']) && function_exists($conf['hooks']['vinfo'])) { $_SESSION['imp']['user'] = call_user_func($conf['hooks']['vinfo']); } $_SESSION['imp']['pass'] = Secret::write(Secret::getKey('imp'), Horde::getFormData('pass')); /* We might need to override some of the defaults with environmental settings. */ include_once IMP_BASE . '/config/servers.php'; if (Auth::getAuth()) { $_SESSION['imp']['uniquser'] = Auth::getAuth(); } elseif (($conf['server']['server_list'] != 'none') && !empty($servers[$server]['realm'])) { $_SESSION['imp']['uniquser'] = $_SESSION['imp']['user'] . '@' . $servers[$server]['realm']; } elseif (!empty($realm)) { $_SESSION['imp']['uniquser'] = $_SESSION['imp']['user'] . '@' . $realm; } else { $_SESSION['imp']['uniquser'] = $_SESSION['imp']['user']; } if ($conf['server']['server_list'] != 'none' && isset($servers[$server]) && is_array($servers[$server])) { $_SESSION['imp']['server'] = $servers[$server]['server']; $_SESSION['imp']['port'] = $servers[$server]['port']; $_SESSION['imp']['protocol'] = $servers[$server]['protocol']; $_SESSION['imp']['maildomain'] = $servers[$server]['maildomain']; $_SESSION['imp']['namespace'] = $servers[$server]['namespace']; $_SESSION['imp']['folders'] = $servers[$server]['folders']; if (($conf['mailer']['type'] == 'smtp') && !empty($servers[$server]['smtphost'])) { $_SESSION['imp']['smtphost'] = $servers[$server]['smtphost']; } } else { foreach ($servers as $key => $curServer) { if (!isset($server_key) && substr($key, 0, 1) != '_') { $server_key = $key; } if (IMP::isPreferredServer($curServer, $key)) { $server_key = $key; break; } } if ($conf['server']['change_server']) { $_SESSION['imp']['server'] = $server; } else { $_SESSION['imp']['server'] = $servers[$server_key]['server']; } if ($conf['server']['change_port']) { $_SESSION['imp']['port'] = Horde::getFormData('port', ''); } else { $_SESSION['imp']['port'] = $servers[$server_key]['port']; } if ($conf['server']['change_protocol']) { $_SESSION['imp']['protocol'] = Horde::getFormData('protocol', ''); } else { $_SESSION['imp']['protocol'] = $servers[$server_key]['protocol']; } if ($conf['server']['change_folders']) { $_SESSION['imp']['folders'] = Horde::getFormData('folders'); } else { $_SESSION['imp']['folders'] = $servers[$server_key]['folders']; } $_SESSION['imp']['maildomain'] = Horde::getFormData('maildomain', ''); $_SESSION['imp']['namespace'] = Horde::getFormData('namespace', ''); } $protocols = explode('/', $_SESSION['imp']['protocol']); if (is_array($protocols)) { $_SESSION['imp']['base_protocol'] = $protocols[0]; } else { $_SESSION['imp']['base_protocol'] = $_SESSION['imp']['protocol']; } if (Horde::getFormData('url')) { $_SESSION['imp']['direct_access'] = false; } else { $_SESSION['imp']['direct_access'] = true; } $_SESSION['imp']['mailbox'] = ''; if (IMP::authenticate(OP_HALFOPEN) === true) { if ($registry->getMethod('auth/login') == 'imp') { Auth::setAuth($_SESSION['imp']['uniquser'], array('password' => Horde::dispelMagicQuotes($_POST['pass']))); $registry->loadPrefs(); } global $prefs; /* Set the session variables. These are cached. */ $_SESSION['imp']['mailbox'] = $prefs->getValue('mailbox'); IMP::setLabel($_SESSION['imp']); $GLOBALS['imp'] = &$_SESSION['imp']; return true; } else { return 'failed'; } } /** * Set up the session for use in the page. Retrieve preferences, * and make sure to update the mailbox, the sorting information, * etc, if any of them have been changed. Also does * protocol-specific tasks, such as automatically disabling * folders if pop3 is being used. * * @access public * * @return mixed True on success, string containing the failure reason on failure. */ function setupSession() { global $conf, $prefs, $registry; if (!isset($_SESSION['imp']) || !is_array($_SESSION['imp']) || !isset($_SESSION['imp']['user'])) { if (isset($prefs)) { $prefs->cleanup(true); } return 'session'; } elseif (!isset($GLOBALS['imp'])) { $GLOBALS['imp'] = &$_SESSION['imp']; } if (isset($_GET['sortby'])) { $prefs->setValue('sortby', $_GET['sortby']); } if (isset($_GET['sortdir'])) { $prefs->setValue('sortdir', $_GET['sortdir']); } $mailbox = Horde::getFormData('mailbox'); if ($mailbox != null) { $GLOBALS['imp']['mailbox'] = $mailbox; } elseif (!isset($GLOBALS['imp']['mailbox'])) { $GLOBALS['imp']['mailbox'] = 'INBOX'; } IMP::setLabel($GLOBALS['imp']); switch ($GLOBALS['imp']['base_protocol']) { case 'pop3': $conf['user']['allow_folders'] = false; $prefs->setValue('save_sent_mail', false); $prefs->setLocked('save_sent_mail', true); $prefs->setLocked('sent_mail_folder', true); $prefs->setLocked('drafts_folder', true); $prefs->setLocked('trash_folder', true); break; } return true; } /** * Attempt to open a connection to the IMAP server using the * information in the $imp session variable. * * @access public * * @param integer $flags (optional) Flags to pass to IMAP_OPEN. Valid values are: *
     *     OP_READONLY  : Open the mailbox read-only.
     *     OP_ANONYMOUS : (NNTP only) Don't use or update a .newrc file.
     *     OP_HALFOPEN  : (IMAP and NNTP only) Open a connection, but not a specific mailbox.
     *     CL_EXPUNGE   : Expunge the mailbox automatically when the stream is closed.
     * 
* @param boolean $setup (optional) Run IMP::setupSession() first. * * @return mixed True on success, string containing the failure reason on failure. */ function authenticate($flags = 0, $setup = false) { if ($setup) { $retval = IMP::setupSession(); if ($retval !== true) { return $retval; } } global $conf; if (!(isset($_SESSION['imp']) && is_array($_SESSION['imp']))) { if (isset($GLOBALS['prefs'])) { $GLOBALS['prefs']->cleanup(true); } return 'session'; } switch ($_SESSION['imp']['base_protocol']) { case 'pop3': $connstr = IMP::serverString($_SESSION['imp']['protocol']); $flags &= ~OP_ANONYMOUS; $flags &= ~OP_HALFOPEN; $_SESSION['imp']['thismailbox'] = ''; break; default: $flags &= ~OP_ANONYMOUS; $_SESSION['imp']['thismailbox'] = Horde::getFormData('thismailbox', $_SESSION['imp']['mailbox']); if ($_SESSION['imp']['thismailbox'] == '**search') { $aindex = Horde::getFormData('array_index'); if (($aindex !== null) && isset($_SESSION['imp']['searchfolders'])) { $tmp = explode(':', $_SESSION['imp']['messagefolders']); $connstr = IMP::serverString($_SESSION['imp']['protocol']) . $tmp[$aindex]; } else { $connstr = IMP::serverString($_SESSION['imp']['protocol']); $flags |= OP_HALFOPEN; } } else { $connstr = IMP::serverString($_SESSION['imp']['protocol']) . $_SESSION['imp']['thismailbox']; } break; } if ($flags == 0) { $_SESSION['imp']['stream'] = @imap_open($connstr, $_SESSION['imp']['user'], Secret::read(Secret::getKey('imp'), $_SESSION['imp']['pass'])); } else { $_SESSION['imp']['stream'] = @imap_open($connstr, $_SESSION['imp']['user'], Secret::read(Secret::getKey('imp'), $_SESSION['imp']['pass']), $flags); } if (!$_SESSION['imp']['stream']) { if (!empty($_SESSION['imp']['server']) && !empty($_SESSION['imp']['port']) && !empty($_SESSION['imp']['protocol']) && !empty($_SESSION['imp']['user'])) { if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { $entry = sprintf('FAILED LOGIN %s (forwarded for [%s]) to %s:%s[%s] as %s', $_SERVER['REMOTE_ADDR'], $_SERVER['HTTP_X_FORWARDED_FOR'], $_SESSION['imp']['server'], $_SESSION['imp']['port'], $_SESSION['imp']['protocol'], $_SESSION['imp']['user']); } else { $entry = sprintf('FAILED LOGIN %s to %s:%s[%s] as %s', $_SERVER['REMOTE_ADDR'], $_SESSION['imp']['server'], $_SESSION['imp']['port'], $_SESSION['imp']['protocol'], $_SESSION['imp']['user']); } Horde::logMessage($entry, __FILE__, __LINE__, LOG_ERR); } $_SESSION['imp'] = null; session_unregister('imp'); if (isset($GLOBALS['prefs'])) { $GLOBALS['prefs']->cleanup(true); } return 'failed'; } return true; } /** * Make sure the user has been authenticated to view the page. * * @access public * * @param option integer $flags Any flags to pass to imap_open(). * See IMP::authenticate(). */ function checkAuthentication($flags = null) { if (($reason = IMP::authenticate($flags, true)) !== true) { if (Horde::getFormData('popup')) { echo ''; } else { $url = Horde::applicationUrl(IMP::logoutUrl('login.php', $reason), true); $url = IMP::addParameter($url, 'url=' . urlencode(Horde::selfUrl())); header('Location: ' . $url); } exit; } } function loginTasks() { global $actionID, $hordeMessageStack, $prefs; if ($actionID == IMP_LOGIN || !empty($_SESSION['imp']['_login'])) { /* Get UNIX timestamp of the last time the user logged in. */ $last_login = $prefs->getValue('last_login'); /* Do maintenance operations. */ if ($prefs->getValue('do_maintenance')) { include_once IMP_BASE . '/lib/Maintenance/imp.php'; $maint = new Maintenance_IMP(); $maint->runMaintenance(); } /* Display user's last login time if requested. */ /* Optional UNIX date style: "D M j H:i:s T Y" */ if ($prefs->getValue('show_last_login')) { $language = $prefs->getValue('language'); if (empty($language)) { $language = Lang::select(); } Lang::setLang($language); Lang::setDomain('imp', IMP_BASE . '/locale', Lang::getCharset()); if ($prefs->getValue('timezone') != '') { putenv('TZ=' . $prefs->getValue('timezone')); } if (empty($last_login) || !(int)$last_login) { Horde::raiseMessage(_("Last login: Never"), HORDE_MESSAGE); } else { Horde::raiseMessage(sprintf(_("Last login: %s"), strftime('%c', $last_login)), HORDE_MESSAGE); } } /* If the user wants to run filters on login, make sure they get run. */ if ($prefs->getValue('filter_on_login') && $_SESSION['imp']['mailbox'] == 'INBOX') { $actionID = FILTER; } /* Store the current login timestamp now. */ $prefs->setValue('last_login', time()); $prefs->store(); /* Store any notification messages in a session variable so it will be displayed on the initial login screen. */ $_SESSION['logintasks'] = $hordeMessageStack; $_SESSION['imp']['_login'] = null; } } /** * 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 $server A complete server entry from the $servers hash. * * @return boolean True if this entry is "preferred". */ function isPreferredServer($server, $key = null) { static $urlServer; if (!isset($urlServer)) $urlServer = Horde::getFormData('server'); if (!empty($urlServer)) { return $key == $urlServer; } if (!empty($server['preferred'])) { if (is_array($server['preferred'])) { foreach ($server['preferred'] as $preferred) { if ($preferred == $_SERVER['SERVER_NAME'] || $preferred == $_SERVER['HTTP_HOST']) { return true; } } } elseif ($server['preferred'] == $_SERVER['SERVER_NAME'] || $server['preferred'] == $_SERVER['HTTP_HOST']) { return true; } } return false; } /** * Tack on any prefixes that need to be put at the beginning of * the folder path. * * @return string The prefix, if any. */ function preambleString() { return $GLOBALS['imp']['folders'] . $GLOBALS['imp']['namespace']; } /** * Tack 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; } /** * Remove any prefixes from a full folder path. * * @param string $mailbox The folder path to strip the prefix from * * @return The folder path without prefixes. */ function stripPreambleString($mailbox) { if (substr($mailbox, 0, strlen(IMP::preambleString())) == IMP::preambleString()) { $mailbox = substr($mailbox, strlen(IMP::preambleString())); } return $mailbox; } /** * Generate a full c-client server specification string. * * @param string $protocol (optional) Override the protocol currently being used. * @return string The full spec string. */ function serverString($protocol = null) { if (!isset($protocol)) { $protocol = $_SESSION['imp']['protocol']; } return '{' . $_SESSION['imp']['server'] . ':' . $_SESSION['imp']['port'] . '/' . $protocol . '}'; } /** * Set the plain-text label that is displayed for the current * mailbox, replacing '**search' with an appropriate string and * removing namespace and folder prefix information from what is * shown to the user. * * @access private * * @param array &$imp The hash to pull mailbox information from and put * the label in. */ function setLabel(&$imp) { global $conf; if ($imp['mailbox'] == '**search') { $imp['label'] = _("Search Results"); } else { $imp['label'] = IMP::utf7Decode($imp['mailbox']); if (strcmp($imp['folders'], substr($imp['label'], 0, strlen($imp['folders']))) == 0) { $imp['label'] = substr($imp['label'], strlen($imp['folders'])); } if (strcmp($imp['namespace'], substr($imp['label'], 0, strlen($imp['namespace']))) == 0) { $imp['label'] = substr($imp['label'], strlen($imp['namespace'])); } } } /** * 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 IMP::utf7Decoded. * * @return string The folder, with any prefix gone. */ function displayFolder($folder, $decode = true) { $prefix = IMP::serverString($_SESSION['imp']['protocol']); if (substr($folder, 0, strlen($prefix)) == $prefix) { $folder = substr($folder, strlen($prefix)); } $prefix = IMP::preambleString(); if (substr($folder, 0, strlen($prefix)) == $prefix) { $folder = substr($folder, strlen($prefix)); } return $decode ? IMP::utf7Decode($folder) : $folder; } /** * Takes an address object, as returned by imap_header(), and * formats it as a string. * * Object Format * For the address: John Doe * The object fields are: *
     *  $object->personal = Personal name ("John Doe")
     *  $object->mailbox  = The user's mailbox ("john_doe")
     *  $object->host     = The host the mailbox is on ("example.com")
     * 
* * @access public * * @param Object $ob The address object to be turned into a string * @param mixed $filter (optional) A user@example.com style bare address * to ignore. Either single string or an array of * strings. If the address matches $filter, * an empty string will be returned. * * @return string $address The formatted address (Example: John Doe * ) */ function addrObject2String($ob, $filter = '') { include_once HORDE_BASE . '/lib/MIME.php'; /* If the personal name is set, decode it. */ $ob->personal = isset($ob->personal) ? MIME::decode($ob->personal) : ''; /* * If both the mailbox and the host are empty, return an empty * string. If we just let this case fall through, the call to * IMP::rfc822WriteAddress will end up return just a '@', which * is undesirable. */ if (empty($ob->mailbox) && empty($ob->host)) { return ''; } /* Make sure these two variables have some sort of value. */ if (!isset($ob->mailbox)) $ob->mailbox = ''; if (!isset($ob->host)) $ob->host = ''; if ($ob->mailbox == 'undisclosed-recipients') { return; } /* Filter out unwanted addresses based on the $filter string. */ if (!is_array($filter)) { $filter = array($filter); } foreach ($filter as $f) { if (strcasecmp($f, $ob->mailbox . '@' . $ob->host) == 0) { return ''; } } /* Return the trimmed, formatted email address. */ return Mime::trimEmailAddress(IMP::rfc822WriteAddress($ob->mailbox, $ob->host, $ob->personal)); } /** * Takes an array of address objects, as returned by imap_headerinfo(), * and passes each of them through IMP::addrObject2String(). * * @access public * * @param array $addresses The array of address objects. * @param optional mixed $filter A user@example.com style bare address * to ignore. If any address matches * $filter, it will not be included in the * final string. * * @return string All of the addresses in a comma-delimited string. */ function addrArray2String($addresses, $filter = '') { $addrList = array(); if (!is_array($addresses)) { return ''; } foreach ($addresses as $addr) { $val = IMP::addrObject2String($addr, $filter); if (!empty($val)) { $bareAddr = strtolower(IMP::bareAddress($val)); if (!array_key_exists($bareAddr, $addrList)) { $addrList[$bareAddr] = $val; } } } if (empty($addrList)) { return ''; } else { return implode(', ', $addrList); } } /** * Returns the bare address. * * @access public * * @param string $address The address string. * @param optional boolean $multiple Should we return multiple results? * * @return mixed If $multiple is false, returns the mailbox@host e-mail * address. If $multiple is true, returns an array of * these addresses. */ function bareAddress($address, $multiple = false) { $addressList = array(); $from = imap_rfc822_parse_adrlist($address, $_SESSION['imp']['maildomain']); foreach ($from as $entry) { if (isset($entry->mailbox)) { if (isset($entry->host)) { $addressList[] = $entry->mailbox . '@' . $entry->host; } else { $addressList[] = $entry->mailbox; } } } if ($multiple) { return $addressList; } else { return array_pop($addressList); } } /** * Return an array of folders. This is a wrapper around the * IMP_Folder::flist() function which reduces the number of * arguments needed if we can assume that IMP's full environment * is present. * * @access public * * @param array $filter (optional) An array of mailboxes to ignore. * * @return array $mailboxes The array of mailboxes returned by * IMP_Folder::flist(). */ function flist($filter = array()) { require_once IMP_BASE . '/lib/Folder.php'; global $conf, $prefs; return IMP_Folder::flist($_SESSION['imp']['stream'], IMP::serverString(), $_SESSION['imp']['folders'], $prefs->getValue('subscribe'), $filter, $conf['server']['hierarchies'], $conf['server']['show_dotfiles'], $_SESSION['imp']['namespace']); } /** * Wrapper around IMP::flist() which generates the body of a * <select> form input from the generated folder list. The * <select> and </select> tags are NOT included in the output of * this function. * * @access public * * @param string $heading (optional) An optional string to use as * the label for an empty-value option at * the top * of the list. Defaults to none. * @param boolean $abbrev (optional) If true, abbreviate long mailbox names * by replacing the middle of the name with * '...'. Defaults to true. * @param array $filter (optional) An array of mailboxes to ignore. * @param string $selected (optional) The mailbox to have selected by * default. Defaults to the first option in the * list. * * @return string $options A string containg \n"; } if ($new_folder_header) { $options .= '' . "\n"; $options .= '\n"; $options .= '' . "\n"; } foreach ($mailboxes as $mbox) { $sel = ($mbox['val'] && $mbox['val'] === $selected) ? ' selected="selected"' : ''; $val = htmlspecialchars($mbox['val']); if ($abbrev) { $label = $mbox['abbrev']; } else { $label = $mbox['label']; } $options .= sprintf('%s', $val, $sel, str_replace(' ', ' ', htmlspecialchars($label)), "\n"); } return $options; } /** * Return an array of unsubscribed folders. This is a wrapper * around the IMP_Folder::flistUnsubscribed() function which * reduces the number of arguments needed if we can assume that * IMP's full environment is present. * * @access public * * @param array $filter (optional) An array of mailboxes to ignore. * * @return array $mailboxes The array of mailboxes returned by * IMP_Folder::flistUnsubscribed(). */ function flistUnsubscribed($filter = array()) { require_once IMP_BASE . '/lib/Folder.php'; global $conf; return IMP_Folder::flistUnsubscribed($_SESSION['imp']['stream'], IMP::serverString(), $_SESSION['imp']['folders'], $filter, $conf['server']['hierarchies'], $conf['server']['show_dotfiles'], $_SESSION['imp']['namespace']); } function getThisMailbox() { global $array_index; if ($_SESSION['imp']['mailbox'] == '**search' && isset($array_index) && array_key_exists('messagefolders', $_SESSION['imp'])) { $tmp = explode(':', $_SESSION['imp']['messagefolders']); if (array_key_exists($array_index, $tmp)) { return $tmp[$array_index]; } } return $_SESSION['imp']['mailbox']; } /** * Check 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 * array with any compose arguments present. */ function getComposeArgs($format = 'uri') { $args = array(); $fields = array('to', 'cc', 'bcc', 'msg', 'subject'); foreach ($fields as $val) { if (($$val = Horde::getFormData($val))) { $args[$val] = $$val; } } /* decode mailto: url */ if (array_key_exists('to', $args) && strpos($args['to'], 'mailto:') === 0) { $mailto = @parse_url($args['to']); if (is_array($mailto)) { $args['to'] = $mailto['path']; parse_str($mailto['query'], $vals); foreach ($fields as $val) { if (array_key_exists($val, $vals)) { $args[$val] = $vals[$val]; } } } } if (stristr($format, 'array')) { return $args; } else { $url = ''; foreach ($args as $key => $val) { $url = IMP::addParameter($url, $key . '=' . urlencode($val)); } return $url; } } /** * Returns the appropriate link to call the message composition screen. * * @access public * * @param mixed $args (optional) Hash 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 (optional) Hash of extra, non-standard arguments to * pass to compose.php. * * @return string $link The link to the message composition screen. */ function composeLink($args = array(), $extra = array()) { global $prefs, $registry, $browser; /* Make sure the compose window always knows which mailbox it's in, for replying, forwarding, marking as answered, etc. */ $extra['thismailbox'] = IMP::getThisMailbox(); 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; } } if ($prefs->getValue('compose_popup')) { /* %23 and %26 (for IE) and %27 need to be escaped or else IE's jscript will interpret it as a single quote, pound sign, or ampersand and refuse to work. */ if ($browser->isBrowser('msie') || ($browser->isBrowser('mozilla') && $browser->getMajor() >= 5)) { $from = array('%23', '%26', '%2B'); $to = array(urlencode('%23'), urlencode('%26'), urlencode('%2B')); } $from[] = '%27'; $to[] = '\%27'; return str_replace($from, $to, $registry->link('mail/compose', $args, $extra)); } else { /* Merge the two argument arrays. */ $args = array_merge($args, $extra); /* Convert the $args hash into proper URL parameters. */ $params = '?'; foreach ($args as $key => $val) { if (is_int($val) || !empty($val)) { $key = urlencode($key); $val = urlencode($val); $params .= "$key=$val&"; } } /* Check for page or message number information to let the compose window return to the right place in the mailbox. */ global $msgindex, $page; if (isset($msgindex)) { $params .= 'start=' . urlencode(($msgindex)) . '&'; } if (isset($page)) { $params .= 'page=' . urlencode($page) . '&'; } return Horde::url('compose.php' . substr($params, 0, -1)); } } /** * Generate 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. * * @param string $uri The page that will process the logout. * @param string $reason (optional) The reason for the logout. * * @return string The $uri with parameters added. */ function logoutUrl($uri, $reason = null) { global $conf; if (!strstr($uri, '?')) { $uri .= '?1=1'; } if (!empty($_SESSION['imp']['user'])) { $uri .= '&imapuser=' . urlencode($_SESSION['imp']['user']); } elseif (Horde::getFormData('imapuser')) { $uri .= '&imapuser=' . urlencode(Horde::getFormData('imapuser')); } if (Horde::getFormData('server')) { $uri .= '&server=' . urlencode(Horde::getFormData('server')); } elseif (!empty($_SESSION['imp']['server'])) { $uri .= '&server=' . urlencode($_SESSION['imp']['server']); } if (!empty($_SESSION['imp']['port'])) { $uri .= '&port=' . urlencode($_SESSION['imp']['port']); } if (!empty($_SESSION['imp']['protocol'])) { $uri .= '&protocol=' . urlencode($_SESSION['imp']['protocol']); } if (!empty($_SESSION['imp']['folders'])) { $uri .= '&folders=' . urlencode($_SESSION['imp']['folders']); } if (!empty($_SESSION['imp']['language'])) { $uri .= '&language=' . urlencode($_SESSION['imp']['language']); } if (!empty($reason)) { $uri .= '&reason=' . urlencode($reason); } return $uri; } /** * Fetch a part of a MIME message. * * @param object MIME_Part $mime The MIME_Part object describing the part to fetch. * * @return string The raw MIME part asked for. */ function getPart($mime) { return imap_fetchbody($_SESSION['imp']['stream'], $mime->index, $mime->imap_id, FT_UID); } /** * Fetch part of a MIME message and decode it, if it is base_64 or * qprint encoded. * * @param object MIME_Part $mime The MIME_Part object describing the part to fetch. * * @return string The raw MIME part asked for. */ function getDecodedPart($mime) { global $registry; if ($mime->encoding == ENCBASE64) { $msg = imap_base64(imap_fetchbody($_SESSION['imp']['stream'], $mime->index, $mime->imap_id, FT_UID)); } elseif ($mime->encoding == ENCQUOTEDPRINTABLE) { $raw = imap_fetchbody($_SESSION['imp']['stream'], $mime->index, $mime->imap_id, FT_UID); $data = imap_qprint($raw); if (empty($data)) { $data = $raw; } $msg = $data; } else { $msg = imap_fetchbody($_SESSION['imp']['stream'], $mime->index, $mime->imap_id, FT_UID); } /* Convert Cyrillic character sets. */ $charset = Horde::getFormData('charset'); if (stristr($registry->getCharset(), 'windows-1251')) { if ($charset == 'koi') { $msg = convert_cyr_string($msg, 'k', 'w'); } else if ($charset == 'iso') { $msg = convert_cyr_string($msg, 'i', 'w'); } else if ($charset == 'mac') { $msg = convert_cyr_string($msg, 'm', 'w'); } else if ($charset == 'dos') { $msg = convert_cyr_string($msg, 'a', 'w'); // } else if (stristr($mime->charset, 'koi8-r')) { } else if ((stristr($mime->charset, 'koi8-r')) or (stristr($mime->charset, 'koi8r')) or (stristr($mime->charset, 'koi8-ru')) or (stristr($mime->charset, 'koi8'))) { $msg = convert_cyr_string($msg, 'k', 'w'); } else if (stristr($mime->charset, 'iso-8859-5')) { $msg = convert_cyr_string($msg, 'i', 'w'); } else if ((stristr($mime->charset, 'x-cp866')) or (stristr($mime->charset, 'csibm866')) or (stristr($mime->charset, 'ibm866'))) { $msg = convert_cyr_string($msg, 'd', 'w'); } else if ((stristr($mime->charset, 'x-mac-cyrillic')) or (stristr($mime->charset, 'maccyrillic')) or (stristr($mime->charset, 'mac-cyrillic'))) { $msg = convert_cyr_string($msg, 'm', 'w'); } elseif ((stristr($mime->charset, 'utf-8')) or (stristr($mime->charset, 'utf8'))) { if (function_exists('mb_convert_encoding')) { $decoded_tmp = @mb_convert_encoding($msg, 'WINDOWS-1251', 'UTF8'); if ($decoded_tmp) { $msg = $decoded_tmp; } else { $msg = convert_cyr_string(@mb_convert_encoding($msg, 'ISO-8859-5', 'UTF8'), 'i', 'w'); } } } } if (stristr($registry->getCharset(), 'koi8-r')) { if ($charset == 'win') { $msg = convert_cyr_string($msg, 'w', 'k'); } else if ($charset == 'iso') { $msg = convert_cyr_string($msg, 'i', 'k'); } else if ($charset == 'mac') { $msg = convert_cyr_string($msg, 'm', 'k'); } else if ($charset == 'dos') { $msg = convert_cyr_string($msg, 'a', 'k'); // } else if (stristr($mime->charset, 'windows-1251')) { } else if ((stristr($mime->charset, 'windows-1251')) or (stristr($mime->charset, 'cp1251'))) { $msg = convert_cyr_string($msg, 'w', 'k'); } else if (stristr($mime->charset, 'iso-8859-5')) { $msg = convert_cyr_string($msg, 'i', 'k'); } else if ((stristr($mime->charset, 'x-cp866')) or (stristr($mime->charset, 'csibm866')) or (stristr($mime->charset, 'ibm866'))) { $msg = convert_cyr_string($msg, 'd', 'k'); } else if ((stristr($mime->charset, 'x-mac-cyrillic')) or (stristr($mime->charset, 'maccyrillic')) or (stristr($mime->charset, 'mac-cyrillic'))) { $msg = convert_cyr_string($msg, 'm', 'k'); } elseif ((stristr($mime->charset, 'utf-8')) or (stristr($mime->charset, 'utf8'))) { if (function_exists('mb_convert_encoding')) { $decoded_tmp = @mb_convert_encoding($msg, 'KOI8-R', 'UTF8'); if ($decoded_tmp) { $msg = $decoded_tmp; } else { $msg = convert_cyr_string(@mb_convert_encoding($msg, 'ISO-8859-5', 'UTF8'), 'i', 'k'); } } } } return $msg; } /** * Accepts an IMP mime variable and attached a MIME_Part and a MIME_Viewer * object onto it. The actual contents of the part are not filled in here. * @param mime Reference to the IMP $mime variable */ function resolveMimeViewer(&$mime) { global $mime_drivers, $mime_drivers_map; if (!isset($mime->part) || isset($mime->viewer)) { /* Spawn the MIME_Viewer driver */ $mime->part = new MIME_Part( $mime->TYPE . '/' . $mime->subtype ); $mime->viewer = &MIME_Viewer::factory($mime->part); } } /** * Returns an html table row summarizing a part of a MIME message. * * @access public * @param array $mime The MIME object to summarize. * @param int $index The index of the message these parts belong to. * * @return string $row The html table row summary. */ function partSummary(&$mime, $index) { if (!($mime->size > 0)) { // don't display zero-size attachments return ''; } global $mime_drivers, $mime_mapping, $conf; include_once HORDE_BASE . '/lib/MIME.php'; include_once HORDE_BASE . '/lib/SessionCache.php'; IMP::resolveMimeViewer($mime); $mime_type = $mime->TYPE . '/' . $mime->subtype; $icon = MIME_Viewer::getIcon($mime_type); // icon column $row = ''; // number column $row .= '' . (!empty($mime->imap_id) ? $mime->imap_id : ' ') . ''; // name/text part column $row .= ''; if (isset($mime->viewer->conf['no_view']) && $mime->viewer->conf['no_view']) { $row .= htmlspecialchars(MIME::decode($mime->description)); } else { $row .= Horde::link('', sprintf(_("View %s"), MIME::decode($mime->description)), null, null, "view('" . VIEW_ATTACH . "', '" . $mime->imap_id . "', '" . SessionCache::putObject($mime) . "'); return false;") . htmlspecialchars(MIME::decode($mime->description)) . ''; $row .= ''; } $row .= '' . $mime->TYPE . '/' . $mime->subtype . ''; $row .= '' . $mime->size . ' KB'; // download column if (!empty($mime->viewer->conf['no_download'])) { $row .= ' '; } else { require_once IMP_BASE . '/lib/Browser.php'; $params = array(); $params['thismailbox'] = urlencode(IMP::getThisMailbox()); $params['index'] = $index; $params['id'] = $mime->imap_id; $params['actionID'] = DOWNLOAD_ATTACH; $params['mime'] = SessionCache::putObject($mime); $row .= ''; $row .= Horde::link(IMP_Browser::downloadUrl(MIME::decode($mime->name), $params), sprintf(_("Download %s"), MIME::decode($mime->description))); $row .= Horde::img('download.gif', 'alt="' . _("Download") . '"'); $row .= ''; } $row .= "\n"; return $row; } /** * Add any site-specific headers defined in config/header.txt to * an array of headers. * * @param array &$headers The array to add headers to. */ function addSiteHeaders(&$headers) { global $conf; // Tack on any site-specific headers. if ($conf['msg']['prepend_header'] && @is_readable(IMP_BASE . '/config/header.txt')) { include_once HORDE_BASE . '/lib/Text.php'; $lines = @file(IMP_BASE . '/config/header.txt'); foreach ($lines as $line) { $line = Text::expandEnvironment($line); if (!empty($line)) { list($key, $val) = explode(':', $line, 2); $val = strtr($val, ':', '_'); $headers[trim($key)] = trim($val); } } } return $headers; } /** * Prepares the parameter array for the mailer connection based on the * current runtime environment. * * @return array Mailer connection parameters */ function prepareMailerParams() { global $conf; /* * We don't actually want to alter the contents of the $conf['mailer'] * array, so we make a copy of the current settings. We will apply * our modifications (if any) to the copy, instead. */ $params = $conf['mailer']['params']; /* * Force the SMTP host value to the current SMTP server if one has been * selected for this connection. */ if (!empty($_SESSION['imp']['smtphost'])) { $params['host'] = $_SESSION['imp']['smtphost']; } /* * If SMTP authentication has been requested, populate the username and * password fields based on the current values for the user. * * Note that we assume that the username and password values from the * current IMAP / POP3 connection are valid for SMTP authentication as * well. */ if (!empty($params['auth'])) { $params['username'] = $_SESSION['imp']['user']; $params['password'] = Secret::read(Secret::getKey('imp'), $_SESSION['imp']['pass']); } return $params; } /** * Add a name=value pair to an URL, taking care of whether there * are existing parameters and whether to use ? or & as the glue. * * @access public * * @param string $url The URL to modify * @param string $parameter The name=value pair to add. * * @return string The modified URL. * * @since IMP 3.2 */ function addParameter($url, $parameter) { if (!empty($parameter) && strstr($url, $parameter) === false) { if (substr($parameter, 0, 1) == '?') { $parameter = substr($parameter, 1); } $pos = strpos($url, '?'); if ($pos !== false) { $url = substr_replace($url, $parameter . ini_get('arg_separator.output'), $pos + 1, 0); } else { $url .= '?' . $parameter; } } return $url; } /** * Perform filtering on the current mailbox. * * @access public */ function filterMailbox() { global $prefs; $filters = @unserialize($prefs->getValue('filters')); if (is_array($filters) && count($filters) && !strstr($_SESSION['imp']['protocol'], 'pop3')) { include_once IMP_BASE . '/lib/Folder.php'; $baseQuery = 'UNDELETED '; $applied = array(); for ($i = 0; $i < count($filters); $i++) { if (!isset($filters[$i])) { break; } for ($j = 0; $j < count($filters[$i]['fields']); $j++) { $query = $baseQuery . strtoupper($filters[$i]['fields'][$j]) . ' "' . $filters[$i]['text'] . '"'; $indices = @imap_search($_SESSION['imp']['stream'], $query, SE_UID); if (isset($indices) && is_array($indices)) { $indices = array_diff($indices, $applied); if (count($indices)) { if ($filters[$i]['action'] == 'delete') { if ($prefs->getValue('show_filter_msg')) { $overview = @imap_fetch_overview($_SESSION['imp']['stream'], implode(',', $indices), FT_UID); foreach ($overview as $message) { if ($prefs->getValue('use_trash')) { Horde::raiseMessage(sprintf(_("Filter Activity: The message \"%s\" from \"%s\" has been moved to your Trash folder."), isset($message->subject) ? MIME::decode($message->subject) : _("[No Subject]"), MIME::decode($message->from)), HORDE_MESSAGE); } else { Horde::raiseMessage(sprintf(_("Filter Activity: The message \"%s\" from \"%s\" has been deleted."), isset($message->subject) ? MIME::decode($message->subject) : _("[No Subject]"), MIME::decode($message->from)), HORDE_MESSAGE); } } } IMP_Message::delete($indices); $applied = array_merge($applied, $indices); } elseif ($filters[$i]['folder'] != $_SESSION['imp']['mailbox']) { if (IMP_Folder::exists($_SESSION['imp']['stream'], $filters[$i]['folder'])) { if ($prefs->getValue('show_filter_msg')) { $overview = @imap_fetch_overview($_SESSION['imp']['stream'], implode(',', $indices), FT_UID); foreach ($overview as $message) { Horde::raiseMessage(sprintf(_("Filter Activity: The message \"%s\" from \"%s\" has been moved to the folder \"%s\"."), isset($message->subject) ? MIME::decode($message->subject) : _("[No Subject]"), MIME::decode($message->from), $filters[$i]['folder']), HORDE_MESSAGE); } } IMP_Message::copy($filters[$i]['folder'], $indices, MOVE_MESSAGES); $applied = array_merge($applied, $indices); } else { Horde::raiseMessage(sprintf(_("Problem with filter rule %d: folder does not exist: %s"), $i + 1, IMP::displayFolder($filters[$i]['folder'])), HORDE_WARNING); } } else { $applied = array_merge($applied, $indices); } } } } } } } /** * Converts a string to the modified UTF-7 charset as per * RFC 2060. * * @access public * * @param string $input The string to be converted. * * @return string The converted string. * * @since IMP 3.2 */ function utf7Encode($input) { global $nls; if (!empty($nls['charsets'][$GLOBALS['language']])) { $charset = $nls['charsets'][$GLOBALS['language']]; } else { $charset = $nls['defaults']['charset']; } $output = false; /* First try iconv with transliteration. */ if (extension_loaded('iconv')) { ini_set('track_errors', 1); $output = @iconv($charset, 'UTF7-IMAP', $input); if (isset($php_errormsg)) { $output = false; } ini_restore('track_errors'); } /* Next try mbstring. */ if (!$output && extension_loaded('mbstring')) { $output = @mb_convert_encoding($input, 'UTF7-IMAP', $charset); } /* At last try imap_utf7_encode. */ if (!$output) { return @imap_utf7_encode($input); } if (!$output) { $output = $input; } return $output; } /** * Converts a string from the modified UTF-7 charset as per * RFC 2060. * * @access public * * @param string $input The string to be converted. * * @return string The converted string. * * @since IMP 3.2 */ function utf7Decode($input) { global $nls; if (!empty($nls['charsets'][$GLOBALS['language']])) { $charset = $nls['charsets'][$GLOBALS['language']]; } else { $charset = $nls['defaults']['charset']; } $output = false; /* First try iconv with transliteration. */ if (extension_loaded('iconv')) { ini_set('track_errors', 1); $output = @iconv('UTF7-IMAP', $charset, $input); if (isset($php_errormsg)) { $output = false; } ini_restore('track_errors'); } /* Next try mbstring. */ if (!$output && extension_loaded('mbstring')) { $output = @mb_convert_encoding($input, $charset, 'UTF7-IMAP'); } /* At last try imap_utf7_[en|de]code. */ if (!$output) { return @imap_utf7_decode($input); } if (!$output) { $output = $input; } return $output; } /** * Quotes and escapes the given string if necessary. * * @access private * * @param string $str The string to be quoted and escaped. * @param optional string $filter A list of characters that make it * necessary to quote the string if they * occur. * * @return string The correctly quoted and escaped string. * * @since IMP 3.2.2 */ function _rfc822Encode($str, $filter = '') { $filter .= "()<>@,;:\\\"[]\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37\177"; if (strcspn($str, $filter) != strlen($str)) { return '"' . str_replace('"', '\\"', str_replace('\\', '\\\\', $str)) . '"'; } else { return $str; } } /** * Builds an RFC 822 compliant email address. * * @access public * * @param string $mailbox Mailbox name. * @param optional string $host Domain name of mailbox's host. * @param optional string $personal Personal name phrase. * * @return string The correctly escaped and quoted * "$personal <$mailbox@$host>" string. * * @since IMP 3.2.2 */ function rfc822WriteAddress($mailbox, $host = null, $personal = '') { $address = ''; if (!empty($personal)) { $address .= IMP::_rfc822Encode($personal, '.'); $address .= ' <'; } if (!is_null($host)) { $address .= IMP::_rfc822Encode($mailbox); if (substr($host, 0, 1) != '@') { $address .= '@' . $host; } } if (!empty($personal)) { $address .= '>'; } return $address; } }