to be displayed.
* The output is wrapped in <> and <
> tags.
*
* @return void
*/
function sm_print_r() {
ob_start(); // Buffer output
foreach(func_get_args() as $var) {
print_r($var);
echo "\n";
}
$buffer = ob_get_contents(); // Grab the print_r output
ob_end_clean(); // Silently discard the output & stop buffering
print '';
print htmlentities($buffer);
print '
';
}
/**
* version of fwrite which checks for failure
*/
function sq_fwrite($fp, $string) {
// write to file
$count = @fwrite($fp,$string);
// the number of bytes written should be the length of the string
if($count != strlen($string)) {
return FALSE;
}
return $count;
}
/**
* Tests if string contains 8bit symbols.
*
* If charset is not set, function defaults to default_charset.
* $default_charset global must be set correctly if $charset is
* not used.
* @param string $string tested string
* @param string $charset charset used in a string
* @return bool true if 8bit symbols are detected
* @since 1.5.1 and 1.4.4
*/
function sq_is8bit($string,$charset='') {
global $default_charset;
if ($charset=='') $charset=$default_charset;
/**
* Don't use \240 in ranges. Sometimes RH 7.2 doesn't like it.
* Don't use \200-\237 for iso-8859-x charsets. This ranges
* stores control symbols in those charsets.
* Use preg_match instead of ereg in order to avoid problems
* with mbstring overloading
*/
if (preg_match("/^iso-8859/i",$charset)) {
$needle='/\240|[\241-\377]/';
} else {
$needle='/[\200-\237]|\240|[\241-\377]/';
}
return preg_match("$needle",$string);
}
/**
* Function returns number of characters in string.
*
* Returned number might be different from number of bytes in string,
* if $charset is multibyte charset. Detection depends on mbstring
* functions. If mbstring does not support tested multibyte charset,
* vanilla string length function is used.
* @param string $str string
* @param string $charset charset
* @since 1.5.1 and 1.4.6
* @return integer number of characters in string
*/
function sq_strlen($string, $charset=NULL){
// NULL charset? Just use strlen()
//
if (is_null($charset))
return strlen($string);
// use current character set?
//
if ($charset == 'auto')
{
//FIXME: this may or may not be better as a session value instead of a global one
global $sq_string_func_auto_charset;
if (!isset($sq_string_func_auto_charset))
{
global $default_charset, $squirrelmail_language;
set_my_charset();
$sq_string_func_auto_charset = $default_charset;
if ($squirrelmail_language == 'ja_JP') $sq_string_func_auto_charset = 'euc-jp';
}
$charset = $sq_string_func_auto_charset;
}
// standardize character set name
//
$charset = strtolower($charset);
/* ===== FIXME: this list is not used in 1.5.x, but if we need it, unless this differs between all our string function wrappers, we should store this info in the session
// only use mbstring with the following character sets
//
$sq_strlen_mb_charsets = array(
'utf-8',
'big5',
'gb2312',
'gb18030',
'euc-jp',
'euc-cn',
'euc-tw',
'euc-kr'
);
// now we can use mb_strlen() if needed
//
if (in_array($charset, $sq_strlen_mb_charsets)
&& in_array($charset, sq_mb_list_encodings()))
===== */
//FIXME: is there any reason why this cannot be a static global array used by all string wrapper functions?
if (in_array($charset, sq_mb_list_encodings()))
return mb_strlen($string, $charset);
// else use normal strlen()
//
return strlen($string);
}
/**
* This is a replacement for PHP's strpos() that is
* multibyte-aware.
*
* @param string $haystack The string to search within
* @param string $needle The substring to search for
* @param int $offset The offset from the beginning of $haystack
* from which to start searching
* (OPTIONAL; default none)
* @param string $charset The charset of the given string. A value of NULL
* here will force the use of PHP's standard strpos().
* (OPTIONAL; default is "auto", which indicates that
* the user's current charset should be used).
*
* @return mixed The integer offset of the next $needle in $haystack,
* if found, or FALSE if not found
*
*/
function sq_strpos($haystack, $needle, $offset=0, $charset='auto')
{
// NULL charset? Just use strpos()
//
if (is_null($charset))
return strpos($haystack, $needle, $offset);
// use current character set?
//
if ($charset == 'auto')
{
//FIXME: this may or may not be better as a session value instead of a global one
global $sq_string_func_auto_charset;
if (!isset($sq_string_func_auto_charset))
{
global $default_charset, $squirrelmail_language;
set_my_charset();
$sq_string_func_auto_charset = $default_charset;
if ($squirrelmail_language == 'ja_JP') $sq_string_func_auto_charset = 'euc-jp';
}
$charset = $sq_string_func_auto_charset;
}
// standardize character set name
//
$charset = strtolower($charset);
/* ===== FIXME: this list is not used in 1.5.x, but if we need it, unless this differs between all our string function wrappers, we should store this info in the session
// only use mbstring with the following character sets
//
$sq_strpos_mb_charsets = array(
'utf-8',
'big5',
'gb2312',
'gb18030',
'euc-jp',
'euc-cn',
'euc-tw',
'euc-kr'
);
// now we can use mb_strpos() if needed
//
if (in_array($charset, $sq_strpos_mb_charsets)
&& in_array($charset, sq_mb_list_encodings()))
===== */
//FIXME: is there any reason why this cannot be a static global array used by all string wrapper functions?
if (in_array($charset, sq_mb_list_encodings()))
return mb_strpos($haystack, $needle, $offset, $charset);
// else use normal strpos()
//
return strpos($haystack, $needle, $offset);
}
/**
* This is a replacement for PHP's substr() that is
* multibyte-aware.
*
* @param string $string The string to operate upon
* @param int $start The offset at which to begin substring extraction
* @param int $length The number of characters after $start to return
* NOTE that if you need to specify a charset but
* want to achieve normal substr() behavior where
* $length is not specified, use NULL (OPTIONAL;
* default from $start to end of string)
* @param string $charset The charset of the given string. A value of NULL
* here will force the use of PHP's standard substr().
* (OPTIONAL; default is "auto", which indicates that
* the user's current charset should be used).
*
* @return string The desired substring
*
* Of course, you can use more advanced (e.g., negative) values
* for $start and $length as needed - see the PHP manual for more
* information: http://www.php.net/manual/function.substr.php
*
*/
function sq_substr($string, $start, $length=NULL, $charset='auto')
{
// if $length is NULL, use the full string length...
// we have to do this to mimick the use of substr()
// where $length is not given
//
if (is_null($length))
$length = sq_strlen($length, $charset);
// NULL charset? Just use substr()
//
if (is_null($charset))
return substr($string, $start, $length);
// use current character set?
//
if ($charset == 'auto')
{
//FIXME: this may or may not be better as a session value instead of a global one
global $sq_string_func_auto_charset;
if (!isset($sq_string_func_auto_charset))
{
global $default_charset, $squirrelmail_language;
set_my_charset();
$sq_string_func_auto_charset = $default_charset;
if ($squirrelmail_language == 'ja_JP') $sq_string_func_auto_charset = 'euc-jp';
}
$charset = $sq_string_func_auto_charset;
}
// standardize character set name
//
$charset = strtolower($charset);
/* ===== FIXME: this list is not used in 1.5.x, but if we need it, unless this differs between all our string function wrappers, we should store this info in the session
// only use mbstring with the following character sets
//
$sq_substr_mb_charsets = array(
'utf-8',
'big5',
'gb2312',
'gb18030',
'euc-jp',
'euc-cn',
'euc-tw',
'euc-kr'
);
// now we can use mb_substr() if needed
//
if (in_array($charset, $sq_substr_mb_charsets)
&& in_array($charset, sq_mb_list_encodings()))
===== */
//FIXME: is there any reason why this cannot be a global array used by all string wrapper functions?
if (in_array($charset, sq_mb_list_encodings()))
return mb_substr($string, $start, $length, $charset);
// else use normal substr()
//
return substr($string, $start, $length);
}
/**
* This is a replacement for PHP's substr_replace() that is
* multibyte-aware.
*
* @param string $string The string to operate upon
* @param string $replacement The string to be inserted
* @param int $start The offset at which to begin substring replacement
* @param int $length The number of characters after $start to remove
* NOTE that if you need to specify a charset but
* want to achieve normal substr_replace() behavior
* where $length is not specified, use NULL (OPTIONAL;
* default from $start to end of string)
* @param string $charset The charset of the given string. A value of NULL
* here will force the use of PHP's standard substr().
* (OPTIONAL; default is "auto", which indicates that
* the user's current charset should be used).
*
* @return string The manipulated string
*
* Of course, you can use more advanced (e.g., negative) values
* for $start and $length as needed - see the PHP manual for more
* information: http://www.php.net/manual/function.substr-replace.php
*
*/
function sq_substr_replace($string, $replacement, $start, $length=NULL,
$charset='auto')
{
// NULL charset? Just use substr_replace()
//
if (is_null($charset))
return is_null($length) ? substr_replace($string, $replacement, $start)
: substr_replace($string, $replacement, $start, $length);
// use current character set?
//
if ($charset == 'auto')
{
//FIXME: this may or may not be better as a session value instead of a global one
$charset = $auto_charset;
global $sq_string_func_auto_charset;
if (!isset($sq_string_func_auto_charset))
{
global $default_charset, $squirrelmail_language;
set_my_charset();
$sq_string_func_auto_charset = $default_charset;
if ($squirrelmail_language == 'ja_JP') $sq_string_func_auto_charset = 'euc-jp';
}
$charset = $sq_string_func_auto_charset;
}
// standardize character set name
//
$charset = strtolower($charset);
/* ===== FIXME: this list is not used in 1.5.x, but if we need it, unless this differs between all our string function wrappers, we should store this info in the session
// only use mbstring with the following character sets
//
$sq_substr_replace_mb_charsets = array(
'utf-8',
'big5',
'gb2312',
'gb18030',
'euc-jp',
'euc-cn',
'euc-tw',
'euc-kr'
);
// now we can use our own implementation using
// mb_substr() and mb_strlen() if needed
//
if (in_array($charset, $sq_substr_replace_mb_charsets)
&& in_array($charset, sq_mb_list_encodings()))
===== */
//FIXME: is there any reason why this cannot be a global array used by all string wrapper functions?
if (in_array($charset, sq_mb_list_encodings()))
{
$string_length = mb_strlen($string, $charset);
if ($start < 0)
$start = max(0, $string_length + $start);
else if ($start > $string_length)
$start = $string_length;
if ($length < 0)
$length = max(0, $string_length - $start + $length);
else if (is_null($length) || $length > $string_length)
$length = $string_length;
if ($start + $length > $string_length)
$length = $string_length - $start;
return mb_substr($string, 0, $start, $charset)
. $replacement
. mb_substr($string,
$start + $length,
$string_length, // FIXME: I can't see why this is needed: - $start - $length,
$charset);
}
// else use normal substr_replace()
//
return is_null($length) ? substr_replace($string, $replacement, $start)
: substr_replace($string, $replacement, $start, $length);
}
/**
* Replacement of mb_list_encodings function
*
* This function provides replacement for function that is available only
* in php 5.x. Function does not test all mbstring encodings. Only the ones
* that might be used in SM translations.
*
* Supported strings are stored in session in order to reduce number of
* mb_internal_encoding function calls.
*
* If mb_list_encodings() function is present, code uses it. Main difference
* from original function behaviour - array values are lowercased in order to
* simplify use of returned array in in_array() checks.
*
* If you want to test all mbstring encodings - fill $list_of_encodings
* array.
* @return array list of encodings supported by php mbstring extension
* @since 1.5.1 and 1.4.6
*/
function sq_mb_list_encodings() {
// if it's already in the session, don't need to regenerate it
if (sqgetGlobalVar('mb_supported_encodings',$mb_supported_encodings,SQ_SESSION)
&& is_array($mb_supported_encodings))
return $mb_supported_encodings;
// check if mbstring extension is present
if (! function_exists('mb_internal_encoding')) {
$supported_encodings = array();
sqsession_register($supported_encodings, 'mb_supported_encodings');
return $supported_encodings;
}
// php 5+ function
if (function_exists('mb_list_encodings')) {
$supported_encodings = mb_list_encodings();
array_walk($supported_encodings, 'sq_lowercase_array_vals');
sqsession_register($supported_encodings, 'mb_supported_encodings');
return $supported_encodings;
}
// save original encoding
$orig_encoding=mb_internal_encoding();
$list_of_encoding=array(
'pass',
'auto',
'ascii',
'jis',
'utf-8',
'sjis',
'euc-jp',
'iso-8859-1',
'iso-8859-2',
'iso-8859-7',
'iso-8859-9',
'iso-8859-15',
'koi8-r',
'koi8-u',
'big5',
'gb2312',
'gb18030',
'windows-1251',
'windows-1255',
'windows-1256',
'tis-620',
'iso-2022-jp',
'euc-cn',
'euc-kr',
'euc-tw',
'uhc',
'utf7-imap');
$supported_encodings=array();
foreach ($list_of_encoding as $encoding) {
// try setting encodings. suppress warning messages
if (@mb_internal_encoding($encoding))
$supported_encodings[]=$encoding;
}
// restore original encoding
mb_internal_encoding($orig_encoding);
// register list in session
sqsession_register($supported_encodings, 'mb_supported_encodings');
return $supported_encodings;
}
/**
* Callback function used to lowercase array values.
* @param string $val array value
* @param mixed $key array key
* @since 1.5.1 and 1.4.6
*/
function sq_lowercase_array_vals(&$val,$key) {
$val = strtolower($val);
}
/**
* Callback function to trim whitespace from a value, to be used in array_walk
* @param string $value value to trim
* @since 1.5.2 and 1.4.7
*/
function sq_trim_value ( &$value ) {
$value = trim($value);
}
/**
* Gathers the list of secuirty tokens currently
* stored in the user's preferences and optionally
* purges old ones from the list.
*
* @param boolean $purge_old Indicates if old tokens
* should be purged from the
* list ("old" is 2 days or
* older unless the administrator
* overrides that value using
* $max_token_age_days in
* config/config_local.php)
* (OPTIONAL; default is to always
* purge old tokens)
*
* @return array The list of tokens
*
* @since 1.4.19 and 1.5.2
*
*/
function sm_get_user_security_tokens($purge_old=TRUE)
{
global $data_dir, $username, $max_token_age_days;
$tokens = getPref($data_dir, $username, 'security_tokens', '');
if (($tokens = unserialize($tokens)) === FALSE || !is_array($tokens))
$tokens = array();
// purge old tokens if necessary
//
if ($purge_old)
{
if (empty($max_token_age_days)) $max_token_age_days = 2;
$now = time();
$discard_token_date = $now - ($max_token_age_days * 86400);
$cleaned_tokens = array();
foreach ($tokens as $token => $timestamp)
if ($timestamp >= $discard_token_date)
$cleaned_tokens[$token] = $timestamp;
$tokens = $cleaned_tokens;
}
return $tokens;
}
/**
* Generates a security token that is then stored in
* the user's preferences with a timestamp for later
* verification/use.
*
* NOTE: The administrator can force SquirrelMail to generate
* a new token every time one is requested (which may increase
* obscurity through token randomness at the cost of some
* performance) by adding the following to
* config/config_local.php: $do_not_use_single_token = TRUE;
* Otherwise, only one token will be generated per user which
* will change only after it expires or is used outside of the
* validity period specified when calling sm_validate_security_token()
*
* 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
* function will not store tokens in the user
* preferences (but it will still generate and return
* a random string).
*
* @param boolean $force_generate_new When TRUE, a new token will
* always be created even if current
* configuration dictates otherwise
* (OPTIONAL; default FALSE)
*
* @return string A security token
*
* @since 1.4.19 and 1.5.2
*
*/
function sm_generate_security_token($force_generate_new=FALSE)
{
global $data_dir, $username, $disable_security_tokens, $do_not_use_single_token;
$max_generation_tries = 1000;
$tokens = sm_get_user_security_tokens();
if (!$force_generate_new && !$do_not_use_single_token && !empty($tokens))
return key($tokens);
$new_token = GenerateRandomString(12, '', 7);
$count = 0;
while (isset($tokens[$new_token]))
{
$new_token = GenerateRandomString(12, '', 7);
if (++$count > $max_generation_tries)
{
logout_error(_("Fatal token generation error; please contact your system administrator or the SquirrelMail Team"));
exit;
}
}
// is the token system enabled? CAREFUL!
//
if (!$disable_security_tokens)
{
$tokens[$new_token] = time();
setPref($data_dir, $username, 'security_tokens', serialize($tokens));
}
return $new_token;
}
/**
* Validates a given security token and optionally remove it
* from the user's preferences if it was valid. If the token
* is too old but otherwise valid, it will still be rejected.
*
* "Too old" is 2 days or older unless the administrator
* overrides that value using $max_token_age_days in
* config/config_local.php
*
* 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
* function will always return TRUE.
*
* @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)
* 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)
* within a more narrow timeframe
* @param boolean $show_error Indicates that if the token is not
* valid, this function should display
* a generic error, log the user out
* and exit - this function will never
* return in that case.
* (OPTIONAL; default FALSE)
*
* @return boolean TRUE if the token validated; FALSE otherwise
*
* @since 1.4.19 and 1.5.2
*
*/
function sm_validate_security_token($token, $validity_period=0, $show_error=FALSE)
{
global $data_dir, $username, $max_token_age_days,
$disable_security_tokens;
// bypass token validation? CAREFUL!
//
if ($disable_security_tokens) return TRUE;
// don't purge old tokens here because we already
// do it when generating tokens
//
$tokens = sm_get_user_security_tokens(FALSE);
// token not found?
//
if (empty($tokens[$token]))
{
if (!$show_error) return FALSE;
logout_error(_("This page request could not be verified and appears to have expired."));
exit;
}
$now = time();
$timestamp = $tokens[$token];
// whether valid or not, we want to remove it from
// user prefs if it's old enough
//
if ($timestamp < $now - $validity_period)
{
unset($tokens[$token]);
setPref($data_dir, $username, 'security_tokens', serialize($tokens));
}
// reject tokens that are too old
//
if (empty($max_token_age_days)) $max_token_age_days = 2;
$old_token_date = $now - ($max_token_age_days * 86400);
if ($timestamp < $old_token_date)
{
if (!$show_error) return FALSE;
logout_error(_("The current page request appears to have originated from an untrusted source."));
exit;
}
// token OK!
//
return TRUE;
}
/**
* 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)
* @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;
}
$string = iconv($encoding, 'UTF-8', $string);
if (check_php_version(5, 2, 3))
$ret = htmlspecialchars($string, $flags, 'UTF-8', $double_encode);
else
$ret = htmlspecialchars($string, $flags, 'UTF-8');
return iconv('UTF-8', $encoding, $ret);
}
$PHP_SELF = php_self();