X-Git-Url: https://vcs.fsf.org/?p=squirrelmail.git;a=blobdiff_plain;f=functions%2Fglobal.php;h=0e2dfa0bec063b5aa6aad7861c0ac38c7f1f2f2c;hp=18ee0e2e61f066f41ff55d04a4e0d3eb054f743e;hb=e063ce26545adbebf2076b4dad82ba622e5c883c;hpb=4b5049de2fa934c45599d6e4c74bf2bbee10d34d diff --git a/functions/global.php b/functions/global.php index 18ee0e2e..0e2dfa0b 100644 --- a/functions/global.php +++ b/functions/global.php @@ -7,13 +7,17 @@ * It also has some session register functions that work across various * php versions. * - * @copyright © 1999-2007 The SquirrelMail Project Team + * @copyright 1999-2021 The SquirrelMail Project Team * @license http://opensource.org/licenses/gpl-license.php GNU Public License * @version $Id$ * @package squirrelmail */ /** + * These constants are used in the function sqgetGlobalVar(). See + * sqgetGlobalVar() for a description of what they mean. + * + * @since 1.4.0 */ define('SQ_INORDER',0); define('SQ_GET',1); @@ -83,6 +87,54 @@ function sqstripslashes(&$array) { } } +/** + * Squelch error output to screen (only) for the given function. + * If the SquirrelMail debug mode SM_DEBUG_MODE_ADVANCED is not + * enabled, error output will not go to the log, either. + * + * This provides an alternative to the @ error-suppression + * operator where errors will not be shown in the interface + * but will show up in the server log file (assuming the + * administrator has configured PHP logging). + * + * @since 1.4.12 and 1.5.2 + * + * @param string $function The function to be executed + * @param array $args The arguments to be passed to the function + * (OPTIONAL; default no arguments) + * NOTE: The caller must take extra action if + * the function being called is supposed + * to use any of the parameters by + * reference. In the following example, + * $x is passed by reference and $y is + * passed by value to the "my_func" + * function. + * sq_call_function_suppress_errors('my_func', array(&$x, $y)); + * + * @return mixed The return value, if any, of the function being + * executed will be returned. + * + */ +function sq_call_function_suppress_errors($function, $args=array()) { + global $sm_debug_mode; + + $display_errors = ini_get('display_errors'); + ini_set('display_errors', '0'); + + // if advanced debug mode isn't enabled, don't log the error, either + // + if (!($sm_debug_mode & SM_DEBUG_MODE_ADVANCED)) + $error_reporting = error_reporting(0); + + $ret = call_user_func_array($function, $args); + + if (!($sm_debug_mode & SM_DEBUG_MODE_ADVANCED)) + error_reporting($error_reporting); + + ini_set('display_errors', $display_errors); + return $ret; +} + /** * Add a variable to the session. * @param mixed $var the variable to register @@ -93,9 +145,7 @@ function sqsession_register ($var, $name) { sqsession_is_active(); - $_SESSION["$name"] = $var; - - session_register("$name"); + $_SESSION[$name] = $var; } /** @@ -109,7 +159,9 @@ function sqsession_unregister ($name) { unset($_SESSION[$name]); - session_unregister("$name"); + // starts throwing warnings in PHP 5.3.0 and is + // removed in PHP 6 and is redundant anyway + //session_unregister("$name"); } /** @@ -138,8 +190,8 @@ function sqsession_is_registered ($name) { * input we should retrieve. An example is if we have: * - * - * + * + * * and we want to know which one of the select inputs should be * returned as $startMessage (without the suffix!), this function * decides by looking for either "form_submit_1" or "form_submit_2" @@ -215,19 +267,24 @@ function sqGetGlobalVarMultiple($name, &$value, $indicator_field, /** - * Search for the var $name in $_SESSION, $_POST, $_GET, $_COOKIE, or $_SERVER - * and set it in provided var. + * Search for the variable $name in one or more of the global variables + * $_SESSION, $_POST, $_GET, $_COOKIE, and $_SERVER, and set the value of it in + * the variable $vaule. * - * If $search is not provided, or if it is SQ_INORDER, it will search $_SESSION, - * then $_POST, then $_GET. If $search is SQ_FORM it will search $_POST and - * $_GET. Otherwise, use one of the defined constants to look for a var in one - * place specifically. + * $search must be one of the defined constants below. The default is + * SQ_INORDER. Both SQ_INORDER and SQ_FORM stops on the first match. * - * Note: $search is an int value equal to one of the constants defined above. + * SQ_INORDER searches $_SESSION, then $_POST, and then $_GET. + * SQ_FORM searches $_POST and then $_GET. + * SQ_COOKIE searches $_COOKIE only. + * SQ_GET searches $_GET only. + * SQ_POST searches $_POST only. + * SQ_SERVER searches $_SERVER only. + * SQ_SESSION searches $_SESSION only. * * Example: - * sqgetGlobalVar('username',$username,SQ_SESSION); - * // No quotes around last param, it's a constant - not a string! + * sqgetGlobalVar('username', $username, SQ_SESSION); + * // No quotes around the last parameter, it's a constant - not a string! * * @param string name the name of the var to search * @param mixed value the variable to return @@ -239,69 +296,174 @@ function sqGetGlobalVarMultiple($name, &$value, $indicator_field, * * @return bool whether variable is found. */ -function sqgetGlobalVar($name, &$value, $search = SQ_INORDER, $default = NULL, $typecast = false) { - - $result = false; +function sqgetGlobalVar($name, &$value, $search = SQ_INORDER, $default = NULL, $typecast = FALSE) { + // The return value defaults to FALSE, i.e. the variable wasn't found. + $result = FALSE; + // Search the global variables to find a match. switch ($search) { - /* we want the default case to be first here, - so that if a valid value isn't specified, - all three arrays will be searched. */ - default: - case SQ_INORDER: // check session, post, get - case SQ_SESSION: - if( isset($_SESSION[$name]) ) { - $value = $_SESSION[$name]; - $result = TRUE; - break; - } elseif ( $search == SQ_SESSION ) { - break; - } - case SQ_FORM: // check post, get - case SQ_POST: - if( isset($_POST[$name]) ) { - $value = $_POST[$name]; - $result = TRUE; - break; - } elseif ( $search == SQ_POST ) { - break; - } - case SQ_GET: - if ( isset($_GET[$name]) ) { - $value = $_GET[$name]; - $result = TRUE; + default: + // The default needs to be first here so SQ_INORDER will be used if + // $search isn't a valid constant. + case SQ_INORDER: + // Search $_SESSION, then $_POST, and then $_GET. Stop on the first + // match. + case SQ_SESSION: + if (isset($_SESSION[$name])) { + // If a match is found, set the specified variable to the found + // value, indicate a match, and stop the search. + $value = $_SESSION[$name]; + $result = TRUE; + break; + } elseif ($search == SQ_SESSION) { + // Only stop the search if SQ_SESSION is set. SQ_INORDER will + // continue with the next clause. + break; + } + case SQ_FORM: + // Search $_POST and then $_GET. Stop on the first match. + case SQ_POST: + if (isset($_POST[$name])) { + // If a match is found, set the specified variable to the found + // value, indicate a match, and stop the search. + $value = $_POST[$name]; + $result = TRUE; + break; + } elseif ($search == SQ_POST) { + // Only stop the search if SQ_POST is set. SQ_INORDER and + // SQ_FORM will continue with the next clause. + break; + } + case SQ_GET: + if (isset($_GET[$name])) { + // If a match is found, set the specified variable to the found + // value, indicate a match, and stop the search. + $value = $_GET[$name]; + $result = TRUE; + break; + } + // Stop the search regardless of if SQ_INORDER, SQ_FORM, or SQ_GET + // is set. All three of them ends here. break; - } - /* NO IF HERE. FOR SQ_INORDER CASE, EXIT after GET */ - break; - case SQ_COOKIE: - if ( isset($_COOKIE[$name]) ) { - $value = $_COOKIE[$name]; - $result = TRUE; + case SQ_COOKIE: + if (isset($_COOKIE[$name])) { + // If a match is found, set the specified variable to the found + // value, indicate a match, and stop the search. + $value = $_COOKIE[$name]; + $result = TRUE; + break; + } + // Stop the search. break; - } - break; - case SQ_SERVER: - if ( isset($_SERVER[$name]) ) { - $value = $_SERVER[$name]; - $result = TRUE; + case SQ_SERVER: + if (isset($_SERVER[$name])) { + // If a match is found, set the specified variable to the found + // value, indicate a match, and stop the search. + $value = $_SERVER[$name]; + $result = TRUE; + break; + } + // Stop the search. break; - } - break; } + if ($result && $typecast) { + // Only typecast if it's requested and a match is found. The default is + // not to typecast, which will happen if a valid constant isn't + // specified. switch ($typecast) { - case SQ_TYPE_INT: $value = (int) $value; break; - case SQ_TYPE_STRING: $value = (string) $value; break; - case SQ_TYPE_BOOL: $value = (bool) $value; break; - default: break; + case SQ_TYPE_INT: + // Typecast the value and stop. + $value = (int) $value; + break; + case SQ_TYPE_STRING: + // Typecast the value and stop. + $value = (string) $value; + break; + case SQ_TYPE_BOOL: + // Typecast the value and stop. + $value = (bool) $value; + break; + case SQ_TYPE_BIGINT: + // Typecast the value and stop. + $value = (preg_match('/^[0-9]+$/', $value) ? $value : '0'); + break; + default: + // The default is to do nothing. + break; } } else if (!$result && !is_null($default)) { + // If no match is found and a default value is specified, set it. $value = $default; } + + // Return if a match was found or not. return $result; } +/** + * Get an immutable copy of a configuration variable if SquirrelMail + * is in "secured configuration" mode. This guarantees the caller + * gets a copy of the requested value as it is set in the main + * application configuration (including config_local overrides), and + * not what it might be after possibly having been modified by some + * other code (usually a plugin overriding configuration values for + * one reason or another). + * + * WARNING: Please use this function as little as possible, because + * every time it is called, it forcibly reloads the main configuration + * file(s). + * + * Caller beware that this function will do nothing if SquirrelMail + * is not in "secured configuration" mode per the $secured_config + * setting. + * + * @param string $var_name The name of the desired variable + * + * @return mixed The desired value + * + * @since 1.5.2 + * + */ +function get_secured_config_value($var_name) { + + static $return_values = array(); + + // if we can avoid it, return values that have + // already been retrieved (so we don't have to + // include the config file yet again) + // + if (isset($return_values[$var_name])) { + return $return_values[$var_name]; + } + + + // load site configuration + // + require(SM_PATH . 'config/config.php'); + + // load local configuration overrides + // + if (file_exists(SM_PATH . 'config/config_local.php')) { + require(SM_PATH . 'config/config_local.php'); + } + + // if SM isn't in "secured configuration" mode, + // just return the desired value from the global scope + // + if (!$secured_config) { + global $$var_name; + $return_values[$var_name] = $$var_name; + return $$var_name; + } + + // else we return what we got from the config file + // + $return_values[$var_name] = $$var_name; + return $$var_name; + +} + /** * Deletes an existing session, more advanced than the standard PHP * session_destroy(), it explicitly deletes the cookies and global vars. @@ -326,11 +488,33 @@ function sqsession_destroy() { * merging of sessions. */ - global $base_uri; + global $base_uri, $_COOKIE, $_SESSION; + + if (isset($_COOKIE[session_name()]) && session_name()) { + // sqsetcookie(session_name(), $_COOKIE[session_name()], 1, $base_uri); + sqsetcookie(session_name(), 'SQMTRASH', 1, $base_uri); + + /* + * Make sure to kill /src and /src/ cookies, just in case there are + * some left-over or malicious ones set in user's browser. + * NB: Note that an attacker could try to plant a cookie for one + * of the /plugins/* directories. Such cookies can block + * access to certain plugin pages, but they do not influence + * or fixate the $base_uri cookie, so we don't worry about + * trying to delete all of them here. + */ + // sqsetcookie(session_name(), $_COOKIE[session_name()], 1, $base_uri . 'src'); + // sqsetcookie(session_name(), $_COOKIE[session_name()], 1, $base_uri . 'src/'); + sqsetcookie(session_name(), 'SQMTRASH', 1, $base_uri . 'src'); + sqsetcookie(session_name(), 'SQMTRASH', 1, $base_uri . 'src/'); + } - if (isset($_COOKIE[session_name()]) && session_name()) sqsetcookie(session_name(), '', 0, $base_uri); - if (isset($_COOKIE['username']) && $_COOKIE['username']) sqsetcookie('username','',0,$base_uri); - if (isset($_COOKIE['key']) && $_COOKIE['key']) sqsetcookie('key','',0,$base_uri); + if (isset($_COOKIE['key']) && $_COOKIE['key']) sqsetcookie('key','SQMTRASH',1,$base_uri); + + /* Make sure new session id is generated on subsequent session_start() */ + unset($_COOKIE[session_name()]); + unset($_GET[session_name()]); + unset($_POST[session_name()]); $sessid = session_id(); if (!empty( $sessid )) { @@ -344,84 +528,155 @@ function sqsession_destroy() { * start a session up. php.net doesn't tell you that $_SESSION * (even though autoglobal), is not created unless a session is * started, unlike $_POST, $_GET and such + * Update: (see #1685031) the session ID is left over after the + * session is closed in some PHP setups; this function just becomes + * a passthru to sqsession_start(), but leaving old code in for + * edification. */ function sqsession_is_active() { - $sessid = session_id(); - if ( empty( $sessid ) ) { + //$sessid = session_id(); + //if ( empty( $sessid ) ) { sqsession_start(); - } + //} } /** * Function to start the session and store the cookie with the session_id as * HttpOnly cookie which means that the cookie isn't accessible by javascript * (IE6 only) + * Note that as sqsession_is_active() no longer discriminates as to when + * it calls this function, session_start() has to have E_NOTICE suppression + * (thus the @ sign). */ function sqsession_start() { global $base_uri; - session_start(); + sq_call_function_suppress_errors('session_start'); + // was: @session_start(); $session_id = session_id(); - // session_starts sets the sessionid cookie buth without the httponly var + // session_starts sets the sessionid cookie but without the httponly var // setting the cookie again sets the httponly cookie attribute - sqsetcookie(session_name(),$session_id,false,$base_uri); + // + // need to check if headers have been sent, since sqsession_is_active() + // has become just a passthru to this function, so the sqsetcookie() + // below is called every time, even after headers have already been sent + // + if (!headers_sent()) + sqsetcookie(session_name(),$session_id,false,$base_uri); } + /** * Set a cookie + * * @param string $sName The name of the cookie. * @param string $sValue The value of the cookie. - * @param int $iExpire The time the cookie expires. This is a Unix timestamp so is in number of seconds since the epoch. - * @param string $sPath The path on the server in which the cookie will be available on. + * @param int $iExpire The time the cookie expires. This is a Unix + * timestamp so is in number of seconds since + * the epoch. + * @param string $sPath The path on the server in which the cookie + * will be available on. * @param string $sDomain The domain that the cookie is available. - * @param boolean $bSecure Indicates that the cookie should only be transmitted over a secure HTTPS connection. + * @param boolean $bSecure Indicates that the cookie should only be + * transmitted over a secure HTTPS connection. * @param boolean $bHttpOnly Disallow JS to access the cookie (IE6 only) + * @param boolean $bReplace Replace previous cookies with same name? + * @param string $sSameSite Optional override of the default SameSite + * cookie policy detemined from the global + * configuration item $same_site_cookies + * (which can be set in config/config_local.php) + * (should be NULL to accept the configured global + * default or one of "Lax" "Strict" or "None" + * but "None" will not work if $bSecure is FALSE. + * Can also be set set to an empty string in order + * to NOT specify the SameSite cookie attribute at + * all and accept whatever the browser default is) + * * @return void + * + * @since 1.4.16 and 1.5.1 + * */ -function sqsetcookie($sName,$sValue="deleted",$iExpire=0,$sPath="",$sDomain="",$bSecure=false,$bHttpOnly=true) { +function sqsetcookie($sName, $sValue='deleted', $iExpire=0, $sPath="", $sDomain="", + $bSecure=false, $bHttpOnly=true, $bReplace=false, $sSameSite=NULL) { + + // some environments can get overwhelmed by an excessive + // setting of the same cookie over and over (e.g., many + // calls to this function via sqsession_is_active() result + // in repeated setting of the session cookie when $bReplace + // is FALSE, but something odd happens (during login only) + // if we change that to default TRUE) ... so we keep our own + // naive per-request name/value cache and only set the cookie + // if its value is changing (or never seen before) + static $cookies = array(); + if (isset($cookies[$sName]) && $cookies[$sName] === $sValue) + return; + else + $cookies[$sName] = $sValue; + + // if we have a secure connection then limit the cookies to https only. - if ($sName && isset($_SERVER['HTTPS']) && $_SERVER['HTTPS']) { + global $is_secure_connection; + if ($sName && $is_secure_connection) $bSecure = true; + + // admin config can override the restriction of secure-only cookies + global $only_secure_cookies; + if (!$only_secure_cookies) + $bSecure = false; + + // use global SameSite setting, but allow override + // The global $same_site_cookies (for which an override value + // can be specified in config/config_local.php) defaults to + // "Strict" when it is NULL (when not given in the config file), + // or can be manually set to "Lax" "Strict" or "None" if desired + // or can be set to an empty string in order to not specify + // SameSite at all and use the browser default + if (is_null($sSameSite)) { + global $same_site_cookies; + if (is_null($same_site_cookies)) + $sSameSite = 'Strict'; + else + $sSameSite = $same_site_cookies; } + if (false && check_php_version(5,2)) { // php 5 supports the httponly attribute in setcookie, but because setcookie seems a bit // broken we use the header function for php 5.2 as well. We might change that later. //setcookie($sName,$sValue,(int) $iExpire,$sPath,$sDomain,$bSecure,$bHttpOnly); } else { - if (!empty($Domain)) { + if (!empty($sDomain)) { // Fix the domain to accept domains with and without 'www.'. - if (strtolower(substr($Domain, 0, 4)) == 'www.') $Domain = substr($Domain, 4); - $Domain = '.' . $Domain; + if (strtolower(substr($sDomain, 0, 4)) == 'www.') $sDomain = substr($sDomain, 4); + $sDomain = '.' . $sDomain; // Remove port information. - $Port = strpos($Domain, ':'); - if ($Port !== false) $Domain = substr($Domain, 0, $Port); + $Port = strpos($sDomain, ':'); + if ($Port !== false) $sDomain = substr($sDomain, 0, $Port); } if (!$sValue) $sValue = 'deleted'; header('Set-Cookie: ' . rawurlencode($sName) . '=' . rawurlencode($sValue) - . (empty($iExpires) ? '' : '; expires=' . gmdate('D, d-M-Y H:i:s', $iExpires) . ' GMT') + . (empty($iExpire) ? '' : '; expires=' . gmdate('D, d-M-Y H:i:s', $iExpire) . ' GMT') . (empty($sPath) ? '' : '; path=' . $sPath) . (empty($sDomain) ? '' : '; domain=' . $sDomain) . (!$bSecure ? '' : '; secure') - . (!$bHttpOnly ? '' : '; HttpOnly'), false); + . (!$bHttpOnly ? '' : '; HttpOnly') + . (empty($sSameSite) ? '' : '; SameSite=' . $sSameSite), $bReplace); } } + /** * session_regenerate_id replacement for PHP < 4.3.2 * * This code is borrowed from Gallery, session.php version 1.53.2.1 +FIXME: I saw this code on php.net (in the manual); that's where it comes from originally, but I don't think we need it - it's just redundant to all the hard work we already did seeding the random number generator IMO. I think we can just call to GenerateRandomString() and dump the rest. */ if (!function_exists('session_regenerate_id')) { - function make_seed() { - list($usec, $sec) = explode(' ', microtime()); - return (float)$sec + ((float)$usec * 100000); - } function php_combined_lcg() { - mt_srand(make_seed()); $tv = gettimeofday(); $lcg['s1'] = $tv['sec'] ^ (~$tv['usec']); $lcg['s2'] = mt_rand(); @@ -449,9 +704,7 @@ if (!function_exists('session_regenerate_id')) { $buf = sprintf("%.15s%ld%ld%0.8f", $remote_addr, $tv['sec'], $tv['usec'], php_combined_lcg() * 10); session_id(md5($buf)); if (ini_get('session.use_cookies')) { - // at a later stage we use sqsetcookie. At this point just do - // what session_regenerate_id would do - setcookie(session_name(), session_id(), NULL, $base_uri); + sqsetcookie(session_name(), session_id(), 0, $base_uri); } return TRUE; } @@ -461,137 +714,60 @@ if (!function_exists('session_regenerate_id')) { /** * php_self * - * Creates an URL for the page calling this function, using either the PHP global - * REQUEST_URI, or the PHP global PHP_SELF with QUERY_STRING added. Before 1.5.1 - * function was stored in function/strings.php. + * Attempts to determine the path and filename and any arguments + * for the currently executing script. This is usually found in + * $_SERVER['REQUEST_URI'], but some environments may differ, so + * this function tries to standardize this value. + * + * Note that before SquirrelMail version 1.5.1, this function was + * stored in function/strings.php. * - * @return string the complete url for this page * @since 1.2.3 + * @return string The path, filename and any arguments for the + * current script */ -function php_self () { - if ( sqgetGlobalVar('REQUEST_URI', $req_uri, SQ_SERVER) && !empty($req_uri) ) { - return $req_uri; - } - - if ( sqgetGlobalVar('PHP_SELF', $php_self, SQ_SERVER) && !empty($php_self) ) { - - // need to add query string to end of PHP_SELF to match REQUEST_URI - // - if ( sqgetGlobalVar('QUERY_STRING', $query_string, SQ_SERVER) && !empty($query_string) ) { - $php_self .= '?' . $query_string; - } - - return $php_self; - } - - return ''; -} - - -/** - * Find files and/or directories in a given directory optionally - * limited to only those with the given file extension. If the - * directory is not found or cannot be opened, no error is generated; - * only an empty file list is returned. -FIXME: do we WANT to throw an error or a notice or... or return FALSE? - * - * @param string $directory_path The path (relative or absolute) - * to the desired directory. - * @param string $extension The file extension filter (optional; - * default is to return all files (dirs). - * @param boolean $return_filenames_only When TRUE, only file/dir names - * are returned, otherwise the - * $directory_path string is - * prepended to each file/dir in - * the returned list (optional; - * default is filename/dirname only) - * @param boolean $include_directories When TRUE, directories are - * included (optional; default - * DO include directories). - * @param boolean $directories_only When TRUE, ONLY directories - * are included (optional; default - * is to include files too). - * @param boolean $separate_files_and_directories When TRUE, files and - * directories are returned - * in separate lists, so - * the return value is - * formatted as a two-element - * array with the two keys - * "FILES" and "DIRECTORIES", - * where corresponding values - * are lists of either all - * files or all directories - * (optional; default do not - * split up return array). - * - * - * @return array The requested file/directory list(s). - * - * @since 1.5.2 - * - */ -function list_files($directory_path, $extension='', $return_filenames_only=TRUE, - $include_directories=TRUE, $directories_only=FALSE, - $separate_files_and_directories=FALSE) { - - $files = array(); - $directories = array(); - -//FIXME: do we want to place security restrictions here like only allowing -// directories under SM_PATH? - // validate given directory - // - if (empty($directory_path) - || !is_dir($directory_path) - || !($DIR = opendir($directory_path))) { - return $files; - } - - - if (!empty($extension)) $extension = '.' . trim($extension, '.'); - $directory_path = rtrim($directory_path, '/'); +function php_self($with_query_string=TRUE) { + static $request_uri = ''; + if (!empty($request_uri)) + return ($with_query_string ? $request_uri : (strpos($request_uri, '?') !== FALSE ? substr($request_uri, 0, strpos($request_uri, '?')) : $request_uri)); - // parse through the files + // first try $_SERVER['PHP_SELF'], which seems most reliable + // (albeit it usually won't include the query string) // - while (($file = readdir($DIR)) !== false) { + $request_uri = ''; + if (!sqgetGlobalVar('PHP_SELF', $request_uri, SQ_SERVER) + || empty($request_uri)) { - if ($file == '.' || $file == '..') continue; - - if (!empty($extension) - && strrpos($file, $extension) !== (strlen($file) - strlen($extension))) - continue; - - // only use is_dir() if we really need to (be as efficient as possible) + // well, then let's try $_SERVER['REQUEST_URI'] // - $is_dir = FALSE; - if (!$include_directories || $directories_only - || $separate_files_and_directories) { - if (is_dir($directory_path . '/' . $file)) { - if (!$include_directories) continue; - $is_dir = TRUE; - $directories[] = ($return_filenames_only - ? $file - : $directory_path . '/' . $file); - } - if ($directories_only) continue; - } + $request_uri = ''; + if (!sqgetGlobalVar('REQUEST_URI', $request_uri, SQ_SERVER) + || empty($request_uri)) { - if (!$separate_files_and_directories - || ($separate_files_and_directories && !$is_dir)) { - $files[] = ($return_filenames_only - ? $file - : $directory_path . '/' . $file); + // TODO: anyone have any other ideas? maybe $_SERVER['SCRIPT_NAME']??? + // + return ''; } } - closedir($DIR); + // we may or may not have any query arguments, depending on + // which environment variable was used above, and the PHP + // version, etc., so let's check for it now + // + $query_string = ''; + if (strpos($request_uri, '?') === FALSE + && sqgetGlobalVar('QUERY_STRING', $query_string, SQ_SERVER) + && !empty($query_string)) { - if ($directories_only) return $directories; - if ($separate_files_and_directories) return array('FILES' => $files, - 'DIRECTORIES' => $directories); - return $files; + $request_uri .= '?' . $query_string; + } + + global $php_self_pattern, $php_self_replacement; + if (!empty($php_self_pattern)) + $request_uri = preg_replace($php_self_pattern, $php_self_replacement, $request_uri); + return ($with_query_string ? $request_uri : (strpos($request_uri, '?') !== FALSE ? substr($request_uri, 0, strpos($request_uri, '?')) : $request_uri)); } @@ -636,3 +812,154 @@ function sm_print_r() { } +/** + * Sanitize a value using sm_encode_html_special_chars() or similar, but also + * recursively run sm_encode_html_special_chars() (or similar) on array keys + * and values. + * + * If $value is not a string or an array with strings in it, + * the value is returned as is. + * + * @param mixed $value The value to be sanitized. + * @param mixed $quote_style Either boolean or an integer. If it + * is an integer, it must be the PHP + * constant indicating if/how to escape + * quotes: ENT_QUOTES, ENT_COMPAT, or + * ENT_NOQUOTES. If it is a boolean value, + * it must be TRUE and thus indicates + * that the only sanitizing to be done + * herein is to replace single and double + * quotes with ' and ", no other + * changes are made to $value. If it is + * boolean and FALSE, behavior reverts + * to same as if the value was ENT_QUOTES + * (OPTIONAL; default is ENT_QUOTES). + * + * @return mixed The sanitized value. + * + * @since 1.5.2 + * + **/ +function sq_htmlspecialchars($value, $quote_style=ENT_QUOTES) { + + if ($quote_style === FALSE) $quote_style = ENT_QUOTES; + + // array? go recursive... + // + if (is_array($value)) { + $return_array = array(); + foreach ($value as $key => $val) { + $return_array[sq_htmlspecialchars($key, $quote_style)] + = sq_htmlspecialchars($val, $quote_style); + } + return $return_array; + + // sanitize strings only + // + } else if (is_string($value)) { + if ($quote_style === TRUE) + return str_replace(array('\'', '"'), array(''', '"'), $value); + else + return sm_encode_html_special_chars($value, $quote_style); + } + + // anything else gets returned with no changes + // + return $value; + +} + + +/** + * Detect whether or not we have a SSL secured (HTTPS) connection + * connection to the browser + * + * It is thought to be so if you have 'SSLOptions +StdEnvVars' + * in your Apache configuration, + * OR if you have HTTPS set to a non-empty value (except "off") + * in your HTTP_SERVER_VARS, + * OR if you have HTTP_X_FORWARDED_PROTO=https in your HTTP_SERVER_VARS, + * OR if you are on port 443. + * + * Note: HTTP_X_FORWARDED_PROTO could be sent from the client and + * therefore possibly spoofed/hackable. Thus, SquirrelMail + * ignores such headers by default. The administrator + * can tell SM to use such header values by setting + * $sq_ignore_http_x_forwarded_headers to boolean FALSE + * in config/config.php or by using config/conf.pl. + * + * Note: It is possible to run SSL on a port other than 443, and + * if that is the case, the administrator should set + * $sq_https_port in config/config.php or by using config/conf.pl. + * + * @return boolean TRUE if the current connection is SSL-encrypted; + * FALSE otherwise. + * + * @since 1.4.17 and 1.5.2 + * + */ +function is_ssl_secured_connection() +{ + global $sq_ignore_http_x_forwarded_headers, $sq_https_port; + $https_env_var = getenv('HTTPS'); + if ($sq_ignore_http_x_forwarded_headers + || !sqgetGlobalVar('HTTP_X_FORWARDED_PROTO', $forwarded_proto, SQ_SERVER)) + $forwarded_proto = ''; + if (empty($sq_https_port)) // won't work with port 0 (zero) + $sq_https_port = 443; + if ((isset($https_env_var) && strcasecmp($https_env_var, 'on') === 0) + || (sqgetGlobalVar('HTTPS', $https, SQ_SERVER) && !empty($https) + && strcasecmp($https, 'off') !== 0) + || (strcasecmp($forwarded_proto, 'https') === 0) + || (sqgetGlobalVar('SERVER_PORT', $server_port, SQ_SERVER) + && $server_port == $sq_https_port)) + return TRUE; + return FALSE; +} + + +/** + * Endeavor to detect what user and group PHP is currently + * running as. Probably only works in non-Windows environments. + * + * @return mixed Boolean FALSE is returned if something went wrong, + * otherwise an array is returned with the following + * elements: + * uid The current process' UID (integer) + * euid The current process' effective UID (integer) + * gid The current process' GID (integer) + * egid The current process' effective GID (integer) + * name The current process' name/handle (string) + * ename The current process' effective name/handle (string) + * group The current process' group name (string) + * egroup The current process' effective group name (string) + * Note that some of these elements may have empty + * values, especially if they could not be determined. + * + * @since 1.5.2 + * + */ +function get_process_owner_info() +{ + if (!function_exists('posix_getuid')) + return FALSE; + + $process_info['uid'] = posix_getuid(); + $process_info['euid'] = posix_geteuid(); + $process_info['gid'] = posix_getgid(); + $process_info['egid'] = posix_getegid(); + + $user_info = posix_getpwuid($process_info['uid']); + $euser_info = posix_getpwuid($process_info['euid']); + $group_info = posix_getgrgid($process_info['gid']); + $egroup_info = posix_getgrgid($process_info['egid']); + + $process_info['name'] = $user_info['name']; + $process_info['ename'] = $euser_info['name']; + $process_info['group'] = $user_info['name']; + $process_info['egroup'] = $euser_info['name']; + + return $process_info; +} + +