| | |
| | | <?php |
| | | |
| | | /* |
| | | /** |
| | | +-----------------------------------------------------------------------+ |
| | | | This file is part of the Roundcube Webmail client | |
| | | | Copyright (C) 2008-2012, The Roundcube Dev Team | |
| | |
| | | return false; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Validates IPv4 or IPv6 address |
| | | * |
| | |
| | | */ |
| | | public static function check_ip($ip) |
| | | { |
| | | // IPv6, but there's no build-in IPv6 support |
| | | if (strpos($ip, ':') !== false && !defined('AF_INET6')) { |
| | | $parts = explode(':', $ip); |
| | | $count = count($parts); |
| | | |
| | | if ($count > 8 || $count < 2) { |
| | | return false; |
| | | } |
| | | |
| | | foreach ($parts as $idx => $part) { |
| | | $length = strlen($part); |
| | | if (!$length) { |
| | | // there can be only one :: |
| | | if ($found_empty) { |
| | | return false; |
| | | } |
| | | $found_empty = true; |
| | | } |
| | | // last part can be an IPv4 address |
| | | else if ($idx == $count - 1) { |
| | | if (!preg_match('/^[0-9a-f]{1,4}$/i', $part)) { |
| | | return @inet_pton($part) !== false; |
| | | } |
| | | } |
| | | else if (!preg_match('/^[0-9a-f]{1,4}$/i', $part)) { |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | return true; |
| | | } |
| | | |
| | | return @inet_pton($ip) !== false; |
| | | return filter_var($ip, FILTER_VALIDATE_IP) !== false; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Check whether the HTTP referer matches the current request |
| | |
| | | */ |
| | | public static function check_referer() |
| | | { |
| | | $uri = parse_url($_SERVER['REQUEST_URI']); |
| | | $uri = parse_url($_SERVER['REQUEST_URI']); |
| | | $referer = parse_url(self::request_header('Referer')); |
| | | |
| | | return $referer['host'] == self::request_header('Host') && $referer['path'] == $uri['path']; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Replacing specials characters to a specific encoding type |
| | | * |
| | | * @param string Input string |
| | | * @param string Encoding type: text|html|xml|js|url |
| | | * @param string Replace mode for tags: show|replace|remove |
| | | * @param boolean Convert newlines |
| | | * @param string Input string |
| | | * @param string Encoding type: text|html|xml|js|url |
| | | * @param string Replace mode for tags: show|remove|strict |
| | | * @param boolean Convert newlines |
| | | * |
| | | * @return string The quoted string |
| | | * @return string The quoted string |
| | | */ |
| | | public static function rep_specialchars_output($str, $enctype = '', $mode = '', $newlines = true) |
| | | { |
| | | static $html_encode_arr = false; |
| | | static $js_rep_table = false; |
| | | static $xml_rep_table = false; |
| | | static $js_rep_table = false; |
| | | static $xml_rep_table = false; |
| | | |
| | | if (!is_string($str)) { |
| | | $str = strval($str); |
| | |
| | | |
| | | $encode_arr = $html_encode_arr; |
| | | |
| | | // don't replace quotes and html tags |
| | | if ($mode == 'show' || $mode == '') { |
| | | if ($mode == 'remove') { |
| | | $str = strip_tags($str); |
| | | } |
| | | else if ($mode != 'strict') { |
| | | // don't replace quotes and html tags |
| | | $ltpos = strpos($str, '<'); |
| | | if ($ltpos !== false && strpos($str, '>', $ltpos) !== false) { |
| | | unset($encode_arr['"']); |
| | |
| | | unset($encode_arr['>']); |
| | | unset($encode_arr['&']); |
| | | } |
| | | } |
| | | else if ($mode == 'remove') { |
| | | $str = strip_tags($str); |
| | | } |
| | | |
| | | $out = strtr($str, $encode_arr); |
| | |
| | | $js_rep_table["'"] = "\\'"; |
| | | $js_rep_table["\\"] = "\\\\"; |
| | | // Unicode line and paragraph separators (#1486310) |
| | | $js_rep_table[chr(hexdec(E2)).chr(hexdec(80)).chr(hexdec(A8))] = '
'; |
| | | $js_rep_table[chr(hexdec(E2)).chr(hexdec(80)).chr(hexdec(A9))] = '
'; |
| | | $js_rep_table[chr(hexdec('E2')).chr(hexdec('80')).chr(hexdec('A8'))] = '
'; |
| | | $js_rep_table[chr(hexdec('E2')).chr(hexdec('80')).chr(hexdec('A9'))] = '
'; |
| | | } |
| | | |
| | | // encode for javascript use |
| | |
| | | |
| | | // encode for plaintext |
| | | if ($enctype == 'text') { |
| | | return str_replace("\r\n", "\n", $mode=='remove' ? strip_tags($str) : $str); |
| | | return str_replace("\r\n", "\n", $mode == 'remove' ? strip_tags($str) : $str); |
| | | } |
| | | |
| | | if ($enctype == 'url') { |
| | |
| | | return $str; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Read input value and convert it for internal use |
| | | * Performs stripslashes() and charset conversion if necessary |
| | | * |
| | | * @param string Field name to read |
| | | * @param int Source to get value from (GPC) |
| | | * @param boolean Allow HTML tags in field value |
| | | * @param string Charset to convert into |
| | | * @param string Field name to read |
| | | * @param int Source to get value from (GPC) |
| | | * @param boolean Allow HTML tags in field value |
| | | * @param string Charset to convert into |
| | | * |
| | | * @return string Field value or NULL if not available |
| | | * @return string Field value or NULL if not available |
| | | */ |
| | | public static function get_input_value($fname, $source, $allow_html=FALSE, $charset=NULL) |
| | | public static function get_input_value($fname, $source, $allow_html = false, $charset = null) |
| | | { |
| | | $value = NULL; |
| | | $value = null; |
| | | |
| | | if ($source == self::INPUT_GET) { |
| | | if (isset($_GET[$fname])) { |
| | |
| | | return self::parse_input_value($value, $allow_html, $charset); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Parse/validate input value. See self::get_input_value() |
| | | * Performs stripslashes() and charset conversion if necessary |
| | | * |
| | | * @param string Input value |
| | | * @param boolean Allow HTML tags in field value |
| | | * @param string Charset to convert into |
| | | * @param string Input value |
| | | * @param boolean Allow HTML tags in field value |
| | | * @param string Charset to convert into |
| | | * |
| | | * @return string Parsed value |
| | | * @return string Parsed value |
| | | */ |
| | | public static function parse_input_value($value, $allow_html=FALSE, $charset=NULL) |
| | | public static function parse_input_value($value, $allow_html = false, $charset = null) |
| | | { |
| | | global $OUTPUT; |
| | | |
| | |
| | | return $value; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Convert array of request parameters (prefixed with _) |
| | | * to a regular array with non-prefixed keys. |
| | |
| | | return $out; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Convert the given string into a valid HTML identifier |
| | | * Same functionality as done in app.js with rcube_webmail.html_identifier() |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Replace all css definitions with #container [def] |
| | | * and remove css-inlined scripting |
| | |
| | | * |
| | | * @return string Modified CSS source |
| | | */ |
| | | public static function mod_css_styles($source, $container_id, $allow_remote=false) |
| | | public static function mod_css_styles($source, $container_id, $allow_remote = false) |
| | | { |
| | | $last_pos = 0; |
| | | $last_pos = 0; |
| | | $replacements = new rcube_string_replacer; |
| | | |
| | | // ignore the whole block if evil styles are detected |
| | |
| | | return $source; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Generate CSS classes from mimetype and filename extension |
| | | * |
| | | * @param string $mimetype Mimetype |
| | | * @param string $filename Filename |
| | | * @param string $mimetype Mimetype |
| | | * @param string $filename Filename |
| | | * |
| | | * @return string CSS classes separated by space |
| | | */ |
| | |
| | | return join(" ", $classes); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Decode escaped entities used by known XSS exploits. |
| | | * See http://downloads.securityfocus.com/vulnerabilities/exploits/26800.eml for examples |
| | |
| | | return $out; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * preg_replace_callback callback for xss_entity_decode |
| | | * |
| | |
| | | { |
| | | return chr(hexdec($matches[1])); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Check if we can process not exceeding memory_limit |
| | |
| | | return $mem_limit > 0 && $memory + $need > $mem_limit ? false : true; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Check if working in SSL mode |
| | | * |
| | |
| | | } |
| | | if (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) |
| | | && strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) == 'https' |
| | | && in_array($_SERVER['REMOTE_ADDR'], rcube::get_instance()->config->get('proxy_whitelist', array()))) { |
| | | && in_array($_SERVER['REMOTE_ADDR'], rcube::get_instance()->config->get('proxy_whitelist', array())) |
| | | ) { |
| | | return true; |
| | | } |
| | | if ($port && $_SERVER['SERVER_PORT'] == $port) { |
| | |
| | | |
| | | return false; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Replaces hostname variables. |
| | |
| | | $h = $_SESSION['storage_host'] ? $_SESSION['storage_host'] : $host; |
| | | // %z - IMAP domain without first part, e.g. %h=imap.domain.tld, %z=domain.tld |
| | | $z = preg_replace('/^[^\.]+\./', '', $h); |
| | | // %s - domain name after the '@' from e-mail address provided at login screen. Returns FALSE if an invalid email is provided |
| | | // %s - domain name after the '@' from e-mail address provided at login screen. |
| | | // Returns FALSE if an invalid email is provided |
| | | if (strpos($name, '%s') !== false) { |
| | | $user_email = self::get_input_value('_user', self::INPUT_POST); |
| | | $user_email = self::idn_convert($user_email, true); |
| | |
| | | |
| | | return str_replace(array('%n', '%t', '%d', '%h', '%z', '%s'), array($n, $t, $d, $h, $z, $s[2]), $name); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Returns remote IP address and forwarded addresses if found |
| | |
| | | if (!empty($_SERVER['HTTP_X_REAL_IP'])) { |
| | | $remote_ip[] = 'X-Real-IP: ' . $_SERVER['HTTP_X_REAL_IP']; |
| | | } |
| | | |
| | | // append the X-Forwarded-For header, if set |
| | | if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { |
| | | $remote_ip[] = 'X-Forwarded-For: ' . $_SERVER['HTTP_X_FORWARDED_FOR']; |
| | |
| | | |
| | | return $address; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Returns the real remote IP address |
| | |
| | | /** |
| | | * Read a specific HTTP request header. |
| | | * |
| | | * @param string $name Header name |
| | | * @param string $name Header name |
| | | * |
| | | * @return mixed Header value or null if not available |
| | | * @return mixed Header value or null if not available |
| | | */ |
| | | public static function request_header($name) |
| | | { |
| | |
| | | return $result; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Improved equivalent to strtotime() |
| | | * |
| | | * @param string $date Date string |
| | | * @param string $date Date string |
| | | * @param DateTimeZone $timezone Timezone to use for DateTime object |
| | | * |
| | | * @return int Unix timestamp |
| | | */ |
| | | public static function strtotime($date) |
| | | public static function strtotime($date, $timezone = null) |
| | | { |
| | | $date = self::clean_datestr($date); |
| | | $date = self::clean_datestr($date); |
| | | $tzname = $timezone ? ' ' . $timezone->getName() : ''; |
| | | |
| | | // unix timestamp |
| | | if (is_numeric($date)) { |
| | |
| | | |
| | | // if date parsing fails, we have a date in non-rfc format. |
| | | // remove token from the end and try again |
| | | while ((($ts = @strtotime($date)) === false) || ($ts < 0)) { |
| | | while ((($ts = @strtotime($date . $tzname)) === false) || ($ts < 0)) { |
| | | $d = explode(' ', $date); |
| | | array_pop($d); |
| | | if (!$d) { |
| | |
| | | /** |
| | | * Date parsing function that turns the given value into a DateTime object |
| | | * |
| | | * @param string $date Date string |
| | | * @param string $date Date string |
| | | * @param DateTimeZone $timezone Timezone to use for DateTime object |
| | | * |
| | | * @return object DateTime instance or false on failure |
| | | * @return DateTime instance or false on failure |
| | | */ |
| | | public static function anytodatetime($date, $timezone = null) |
| | | { |
| | | if (is_object($date) && is_a($date, 'DateTime')) { |
| | | if ($date instanceof DateTime) { |
| | | return $date; |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | // try our advanced strtotime() method |
| | | if (!$dt && ($timestamp = self::strtotime($date))) { |
| | | if (!$dt && ($timestamp = self::strtotime($date, $timezone))) { |
| | | try { |
| | | $dt = new DateTime("@".$timestamp); |
| | | if ($timezone) { |
| | | $dt->setTimezone($timezone); |
| | | } |
| | | } |
| | | catch (Exception $e) { |
| | | // ignore |
| | |
| | | return self::idn_convert($str, true); |
| | | } |
| | | |
| | | |
| | | /* |
| | | * Idn_to_ascii wrapper. |
| | | * Intl/Idn modules version of this function doesn't work with e-mail address |
| | |
| | | return self::idn_convert($str, false); |
| | | } |
| | | |
| | | |
| | | public static function idn_convert($input, $is_utf=false) |
| | | public static function idn_convert($input, $is_utf = false) |
| | | { |
| | | if ($at = strpos($input, '@')) { |
| | | $user = substr($input, 0, $at); |
| | |
| | | * Split the given string into word tokens |
| | | * |
| | | * @param string Input to tokenize |
| | | * @param integer Minimum length of a single token |
| | | * @return array List of tokens |
| | | */ |
| | | public static function tokenize_string($str) |
| | | public static function tokenize_string($str, $minlen = 2) |
| | | { |
| | | return explode(" ", preg_replace( |
| | | array('/[\s;\/+-]+/i', '/(\d)[-.\s]+(\d)/', '/\s\w{1,3}\s/u'), |
| | | array(' ', '\\1\\2', ' '), |
| | | $str)); |
| | | $expr = array('/[\s;,"\'\/+-]+/ui', '/(\d)[-.\s]+(\d)/u'); |
| | | $repl = array(' ', '\\1\\2'); |
| | | |
| | | if ($minlen > 1) { |
| | | $minlen--; |
| | | $expr[] = "/(^|\s+)\w{1,$minlen}(\s+|$)/u"; |
| | | $repl[] = ' '; |
| | | } |
| | | |
| | | return array_filter(explode(" ", preg_replace($expr, $repl, $str))); |
| | | } |
| | | |
| | | /** |
| | |
| | | * |
| | | * @param string Input string (UTF-8) |
| | | * @param boolean True to return list of words as array |
| | | * @param integer Minimum length of tokens |
| | | * |
| | | * @return mixed Normalized string or a list of normalized tokens |
| | | * @return mixed Normalized string or a list of normalized tokens |
| | | */ |
| | | public static function normalize_string($str, $as_array = false) |
| | | public static function normalize_string($str, $as_array = false, $minlen = 2) |
| | | { |
| | | // replace 4-byte unicode characters with '?' character, |
| | | // these are not supported in default utf-8 charset on mysql, |
| | |
| | | . ')/', '?', $str); |
| | | |
| | | // split by words |
| | | $arr = self::tokenize_string($str); |
| | | $arr = self::tokenize_string($str, $minlen); |
| | | |
| | | // detect character set |
| | | if (utf8_encode(utf8_decode($str)) == $str) { |
| | |
| | | } |
| | | |
| | | return $as_array ? $arr : join(" ", $arr); |
| | | } |
| | | |
| | | /** |
| | | * Compare two strings for matching words (order not relevant) |
| | | * |
| | | * @param string Haystack |
| | | * @param string Needle |
| | | * |
| | | * @return boolean True if match, False otherwise |
| | | */ |
| | | public static function words_match($haystack, $needle) |
| | | { |
| | | $a_needle = self::tokenize_string($needle, 1); |
| | | $haystack = join(" ", self::tokenize_string($haystack, 1)); |
| | | |
| | | $hits = 0; |
| | | foreach ($a_needle as $w) { |
| | | if (stripos($haystack, $w) !== false) { |
| | | $hits++; |
| | | } |
| | | } |
| | | |
| | | return $hits >= count($a_needle); |
| | | } |
| | | |
| | | /** |
| | |
| | | |
| | | return $url; |
| | | } |
| | | |
| | | /** |
| | | * Generate a random string |
| | | * |
| | | * @param int $length String length |
| | | * @param bool $raw Return RAW data instead of ascii |
| | | * |
| | | * @return string The generated random string |
| | | */ |
| | | public static function random_bytes($length, $raw = false) |
| | | { |
| | | // Use PHP7 true random generator |
| | | if (function_exists('random_bytes')) { |
| | | // random_bytes() can throw an Error/TypeError/Exception in some cases |
| | | try { |
| | | $random = random_bytes($length); |
| | | } |
| | | catch (Throwable $e) {} |
| | | } |
| | | |
| | | if (!$random) { |
| | | $random = openssl_random_pseudo_bytes($length); |
| | | } |
| | | |
| | | if ($raw) { |
| | | return $random; |
| | | } |
| | | |
| | | $random = self::bin2ascii($random); |
| | | |
| | | // truncate to the specified size... |
| | | if ($length < strlen($random)) { |
| | | $random = substr($random, 0, $length); |
| | | } |
| | | |
| | | return $random; |
| | | } |
| | | |
| | | /** |
| | | * Convert binary data into readable form (containing a-zA-Z0-9 characters) |
| | | * |
| | | * @param string $input Binary input |
| | | * |
| | | * @return string Readable output |
| | | */ |
| | | public static function bin2ascii($input) |
| | | { |
| | | // Above method returns "hexits". |
| | | // Based on bin_to_readable() function in ext/session/session.c. |
| | | // Note: removed ",-" characters from hextab |
| | | $hextab = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; |
| | | $nbits = 6; // can be 4, 5 or 6 |
| | | $length = strlen($input); |
| | | $result = ''; |
| | | $char = 0; |
| | | $i = 0; |
| | | $have = 0; |
| | | $mask = (1 << $nbits) - 1; |
| | | |
| | | while (true) { |
| | | if ($have < $nbits) { |
| | | if ($i < $length) { |
| | | $char |= ord($input[$i++]) << $have; |
| | | $have += 8; |
| | | } |
| | | else if (!$have) { |
| | | break; |
| | | } |
| | | else { |
| | | $have = $nbits; |
| | | } |
| | | } |
| | | |
| | | // consume nbits |
| | | $result .= $hextab[$char & $mask]; |
| | | $char >>= $nbits; |
| | | $have -= $nbits; |
| | | } |
| | | |
| | | return $result; |
| | | } |
| | | |
| | | /** |
| | | * Format current date according to specified format. |
| | | * This method supports microseconds (u). |
| | | * |
| | | * @param string $format Date format (default: 'd-M-Y H:i:s O') |
| | | * |
| | | * @return string Formatted date |
| | | */ |
| | | public static function date_format($format = null) |
| | | { |
| | | if (empty($format)) { |
| | | $format = 'd-M-Y H:i:s O'; |
| | | } |
| | | |
| | | if (strpos($format, 'u') !== false |
| | | && ($date = date_create_from_format('U.u.e', microtime(true) . '.' . date_default_timezone_get())) |
| | | ) { |
| | | return $date->format($format); |
| | | } |
| | | |
| | | return date($format); |
| | | } |
| | | } |