* This code provides various string manipulation functions that are
* used by the rest of the SquirrelMail code.
*
- * @copyright 1999-2012 The SquirrelMail Project Team
+ * @copyright 1999-2019 The SquirrelMail Project Team
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
* @version $Id$
* @package squirrelmail
* stores control symbols in those charsets.
* Use preg_match instead of ereg in order to avoid problems
* with mbstring overloading
-NOTES:
-NOTES:
- 37 (base 8) = 31 (base 10) (LAST CONTROL CHAR)
- 40 (base 8) = 32 (base 10) (FIRST PRINTABLE ASCII)
- 176 (base 8) = 126 (base 10) (LAST PRINTABLE ASCII)
- 177 (base 8) = 127 (base 10) (DEL)
- 200 (base 8) = 128 (base 10)
- 237 (base 8) = 159 (base 10)
- 240 (base 8) = 160 (base 10) (FIRST EXTRA PRINTABLE)
- 377 (base 8) = 255 (base 10) (LAST EXTRA PRINTABLE)
- 400 (base 8) = 256 (base 10)
-
- Traditional ASCII 0 - 127 (octal 0 - 177)
- Printable ASCII 32 - 126 (octal 40 - 176)
- Additional Printables for ISO Latin 1 160 - 255 (octal 240 - 377)
-
- 240 (160) is the first character of the extra 7-bit printable characer
- range, sometimes used as the no-break space, but the regular expression
- ranges are broken up at 240 only because RH 7.2 PHP seemed to have
- problems otherwise - this is a PHP/preg issue, NOT a charset issue
-
- So supposedly printable chars in an 8859 charset are 32-126 (octal 40-176)
- and 160/161-255 (octal 240/241-377))
-
- So checking for the range between the two makes sense (128-159 or octal 200-237)
- (wait, no, to skip DEL too, it's 127-159 (octal 177-237))
-
- But why not for 0-31 (octal 0-37) and DEL (127 or 177 octal)????
- (or do we need a new fxn that detects *printable* 7-bit chars?)
- (if we do, note that some control characters are "printable",
- notably the CR, LF and TAB characters)
-
- And why is 241-377 octal considered 8-bit for iso 8859??? Isn't it
- the opposite for iso 8859???? aren't these 7 bit characters?
- see http://www.cs.tut.fi/~jkorpela/chars.html#latin1
- Uh, well, anything more than 127 (octal 177) takes 8 bits to represent
- but errrrr, these are simple non-multibyte characters, right? but
- maybe this "is 8-bit" business is NOT the same as "is multibyte"????
-
- That begs the question how this fxn is actually used - what's its purpose?
- (is it being misused in some places?)
-
*/
if (preg_match("/^iso-8859/i",$charset)) {
$needle='/\240|[\241-\377]/';
function sm_get_user_security_tokens($purge_old=TRUE)
{
- global $data_dir, $username, $max_token_age_days;
+ global $data_dir, $username, $max_token_age_days,
+ $use_expiring_security_tokens;
$tokens = getPref($data_dir, $username, 'security_tokens', '');
if (($tokens = unserialize($tokens)) === FALSE || !is_array($tokens))
/**
* Generates a security token that is then stored in
* the user's preferences with a timestamp for later
- * verification/use.
+ * verification/use (although session-based tokens
+ * are not stored in user preferences).
+ *
+ * NOTE: By default SquirrelMail will use a single session-based
+ * token, but if desired, user tokens can have expiration
+ * dates associated with them and become invalid even during
+ * the same login session. When in that mode, the note
+ * immediately below applies, otherwise it is irrelevant.
+ * To enable that mode, the administrator must add the
+ * following to config/config_local.php:
+ * $use_expiring_security_tokens = TRUE;
*
* NOTE: The administrator can force SquirrelMail to generate
* a new token every time one is requested (which may increase
function sm_generate_security_token($force_generate_new=FALSE)
{
- global $data_dir, $username, $disable_security_tokens, $do_not_use_single_token;
+ global $data_dir, $username, $disable_security_tokens, $do_not_use_single_token,
+ $use_expiring_security_tokens;
$max_generation_tries = 1000;
+ // if we're using session-based tokens, just return
+ // the same one every time (generate it if it's not there)
+ //
+ if (!$use_expiring_security_tokens)
+ {
+ if (sqgetGlobalVar('sm_security_token', $token, SQ_SESSION))
+ return $token;
+
+ // create new one since there was none in session
+ $token = GenerateRandomString(12, '', 7);
+ sqsession_register($token, 'sm_security_token');
+ return $token;
+ }
+
$tokens = sm_get_user_security_tokens();
if (!$force_generate_new && !$do_not_use_single_token && !empty($tokens))
* overrides that value using $max_token_age_days in
* config/config_local.php
*
+ * Session-based tokens of course are always reused and are
+ * valid for the lifetime of the login session.
+ *
* WARNING: If the administrator has turned the token system
* off by setting $disable_security_tokens to TRUE in
* config/config.php or the configuration tool, this
* @param string $token The token to validate
* @param int $validity_period The number of seconds tokens are valid
* for (set to zero to remove valid tokens
- * after only one use; use 3600 to allow
- * tokens to be reused for an hour)
- * (OPTIONAL; default is to only allow tokens
- * to be used once)
+ * after only one use; set to -1 to allow
+ * indefinite re-use (but still subject to
+ * $max_token_age_days - see elsewhere);
+ * use 3600 to allow tokens to be reused for
+ * an hour) (OPTIONAL; default is to only
+ * allow tokens to be used once)
* NOTE this is unrelated to $max_token_age_days
* or rather is an additional time constraint on
* tokens that allows them to be re-used (or not)
{
global $data_dir, $username, $max_token_age_days,
+ $use_expiring_security_tokens,
$disable_security_tokens;
// bypass token validation? CAREFUL!
//
if ($disable_security_tokens) return TRUE;
+ // if we're using session-based tokens, just compare
+ // the same one every time
+ //
+ if (!$use_expiring_security_tokens)
+ {
+ if (!sqgetGlobalVar('sm_security_token', $session_token, SQ_SESSION))
+ {
+ if (!$show_error) return FALSE;
+ logout_error(_("Fatal security token error; please log in again"));
+ exit;
+ }
+ if ($token !== $session_token)
+ {
+ if (!$show_error) return FALSE;
+ logout_error(_("The current page request appears to have originated from an untrusted source."));
+ exit;
+ }
+ return TRUE;
+ }
+
// don't purge old tokens here because we already
// do it when generating tokens
//
$timestamp = $tokens[$token];
// whether valid or not, we want to remove it from
- // user prefs if it's old enough
+ // user prefs if it's old enough (unless requested to
+ // bypass this (in which case $validity_period is -1))
//
- if ($timestamp < $now - $validity_period)
+ if ($validity_period >= 0
+ && $timestamp < $now - $validity_period)
{
unset($tokens[$token]);
setPref($data_dir, $username, 'security_tokens', serialize($tokens));
}
+/**
+ * Wrapper for PHP's htmlspecialchars() that
+ * attempts to add the correct character encoding
+ *
+ * @param string $string The string to be converted
+ * @param int $flags A bitmask that controls the behavior of htmlspecialchars()
+ * (See http://php.net/manual/function.htmlspecialchars.php )
+ * (OPTIONAL; default ENT_COMPAT, ENT_COMPAT | ENT_SUBSTITUTE for PHP >=5.4)
+ * @param string $encoding The character encoding to use in the conversion
+ * (OPTIONAL; default automatic detection)
+ * @param boolean $double_encode Whether or not to convert entities that are
+ * already in the string (only supported in
+ * PHP 5.2.3+) (OPTIONAL; default TRUE)
+ *
+ * @return string The converted text
+ *
+ */
+function sm_encode_html_special_chars($string, $flags=ENT_COMPAT,
+ $encoding=NULL, $double_encode=TRUE)
+{
+ if (!$encoding)
+ {
+ global $default_charset;
+ if ($default_charset == 'iso-2022-jp')
+ $default_charset = 'EUC-JP';
+ $encoding = $default_charset;
+ }
+
+ if (check_php_version(5, 2, 3)) {
+ // Replace invalid characters with a symbol instead of returning
+ // empty string for the entire to be encoded string.
+ if (check_php_version(5, 4, 0) && $flags == ENT_COMPAT) {
+ $flags = $flags | ENT_SUBSTITUTE;
+ }
+ return htmlspecialchars($string, $flags, $encoding, $double_encode);
+ }
+
+ return htmlspecialchars($string, $flags, $encoding);
+}
+