diff options
Diffstat (limited to 'system')
-rw-r--r-- | system/core/Common.php | 18 | ||||
-rw-r--r-- | system/core/Config.php | 2 | ||||
-rw-r--r-- | system/core/Input.php | 48 | ||||
-rw-r--r-- | system/core/Security.php | 2 | ||||
-rw-r--r-- | system/database/drivers/mysqli/mysqli_driver.php | 2 | ||||
-rw-r--r-- | system/helpers/url_helper.php | 21 | ||||
-rw-r--r-- | system/libraries/Email.php | 199 | ||||
-rw-r--r-- | system/libraries/Pagination.php | 4 | ||||
-rwxr-xr-x | system/libraries/Session/drivers/Session_cookie.php | 49 | ||||
-rwxr-xr-x | system/libraries/Session/drivers/Session_native.php | 22 |
10 files changed, 190 insertions, 177 deletions
diff --git a/system/core/Common.php b/system/core/Common.php index 341402c6b..2dd31d3e9 100644 --- a/system/core/Common.php +++ b/system/core/Common.php @@ -330,6 +330,24 @@ if ( ! function_exists('get_mimes')) // ------------------------------------------------------------------------ +if ( ! function_exists('is_https')) +{ + /** + * Is HTTPS? + * + * Determines if the application is accessed via an encrypted + * (HTTPS) connection. + * + * @return bool + */ + function is_https() + { + return ( ! empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) !== 'off'); + } +} + +// ------------------------------------------------------------------------ + if ( ! function_exists('show_error')) { /** diff --git a/system/core/Config.php b/system/core/Config.php index 8e4f998ef..e78128c76 100644 --- a/system/core/Config.php +++ b/system/core/Config.php @@ -75,7 +75,7 @@ class CI_Config { { if (isset($_SERVER['HTTP_HOST'])) { - $base_url = ( ! empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) !== 'off') ? 'https' : 'http'; + $base_url = is_https() ? 'https' : 'http'; $base_url .= '://'.$_SERVER['HTTP_HOST'] .str_replace(basename($_SERVER['SCRIPT_NAME']), '', $_SERVER['SCRIPT_NAME']); } diff --git a/system/core/Input.php b/system/core/Input.php index 82482f2aa..ec935d531 100644 --- a/system/core/Input.php +++ b/system/core/Input.php @@ -390,31 +390,32 @@ class CI_Input { } // Convert the REMOTE_ADDR IP address to binary, if needed - if ( ! isset($ip, $convert_func)) + if ( ! isset($ip, $sprintf)) { if ($separator === ':') { // Make sure we're have the "full" IPv6 format - $ip = str_replace('::', str_repeat(':', 9 - substr_count($this->ip_address, ':')), $this->ip_address); - $convert_func = is_php('5.3') - ? function ($value) - { - return str_pad(base_convert($value, 16, 2), 16, '0', STR_PAD_LEFT); - } - : create_function('$value', 'return str_pad(base_convert($value, 16, 2), 16, "0", STR_PAD_LEFT);'); + $ip = explode(':', + str_replace('::', + str_repeat(':', 9 - substr_count($this->ip_address, ':')), + $this->ip_address + ) + ); + + for ($i = 0; $i < 8; $i++) + { + $ip[$i] = intval($ip[$i], 16); + } + + $sprintf = '%016b%016b%016b%016b%016b%016b%016b%016b'; } else { - $ip = $this->ip_address; - $convert_func = is_php('5.3') - ? function ($value) - { - return str_pad(decbin($value), 8, '0', STR_PAD_LEFT); - } - : create_function('$value', 'return str_pad(decbin($value), 8, "0", STR_PAD_LEFT);'); + $ip = explode('.', $this->ip_address); + $sprintf = '%08b%08b%08b%08b'; } - $ip = implode(array_map($convert_func, explode($separator, $ip))); + $ip = vsprintf($sprintf, $ip); } // Split the netmask length off the network address @@ -423,12 +424,19 @@ class CI_Input { // Again, an IPv6 address is most likely in a compressed form if ($separator === ':') { - $netaddr = str_replace('::', str_repeat(':', 9 - substr_count($netaddr, ':')), $netaddr); + $netaddr = explode(':', str_replace('::', str_repeat(':', 9 - substr_count($netaddr, ':')), $netaddr)); + for ($i = 0; $i < 8; $i++) + { + $netaddr[$i] = intval($netaddr[$i], 16); + } + } + else + { + $netaddr = explode('.', $netaddr); } - // Convert to a binary form and finally compare - $netaddr = implode(array_map($convert_func, explode($separator, $netaddr))); - if (strncmp($ip, $netaddr, $masklen) === 0) + // Convert to binary and finally compare + if (strncmp($ip, vsprintf($sprintf, $netaddr), $masklen) === 0) { $this->ip_address = $spoof; break; diff --git a/system/core/Security.php b/system/core/Security.php index b22d2cf19..2fbc5b34c 100644 --- a/system/core/Security.php +++ b/system/core/Security.php @@ -198,7 +198,7 @@ class CI_Security { $expire = time() + $this->_csrf_expire; $secure_cookie = (bool) config_item('cookie_secure'); - if ($secure_cookie && (empty($_SERVER['HTTPS']) OR strtolower($_SERVER['HTTPS']) === 'off')) + if ($secure_cookie && ! is_https()) { return FALSE; } diff --git a/system/database/drivers/mysqli/mysqli_driver.php b/system/database/drivers/mysqli/mysqli_driver.php index 0a454da6c..91ab13a3c 100644 --- a/system/database/drivers/mysqli/mysqli_driver.php +++ b/system/database/drivers/mysqli/mysqli_driver.php @@ -69,7 +69,7 @@ class CI_DB_mysqli_driver extends CI_DB { ? 'p:'.$this->hostname : $this->hostname; $port = empty($this->port) ? NULL : $this->port; $client_flags = ($this->compress === TRUE) ? MYSQLI_CLIENT_COMPRESS : 0; - $mysqli = new mysqli(); + $mysqli = mysqli_init(); return @$mysqli->real_connect($hostname, $this->username, $this->password, $this->database, $port, NULL, $client_flags) ? $mysqli : FALSE; diff --git a/system/helpers/url_helper.php b/system/helpers/url_helper.php index b1f5eccf1..de5bdec31 100644 --- a/system/helpers/url_helper.php +++ b/system/helpers/url_helper.php @@ -388,40 +388,43 @@ if ( ! function_exists('auto_link')) for ($i = 0, $c = count($matches[0]); $i < $c; $i++) { - if (preg_match('|\.$|', $matches[6][$i])) + if (preg_match('/(\.|\,)$/i', $matches[6][$i], $m)) { - $period = '.'; + $punct = $m[1]; $matches[6][$i] = substr($matches[6][$i], 0, -1); } else { - $period = ''; + $punct = ''; } $str = str_replace($matches[0][$i], $matches[1][$i].'<a href="http'.$matches[4][$i].'://' .$matches[5][$i].$matches[6][$i].'"'.$pop.'>http' .$matches[4][$i].'://'.$matches[5][$i] - .$matches[6][$i].'</a>'.$period, + .$matches[6][$i].'</a>'.$punct, $str); } } - if ($type !== 'url' && preg_match_all('/([a-zA-Z0-9_\.\-\+]+)@([a-zA-Z0-9\-]+)\.([a-zA-Z0-9\-\.]*)/i', $str, $matches)) + if ($type !== 'url' && preg_match_all('/([a-zA-Z0-9_\.\-\+]+)@([a-zA-Z0-9\-]+)\.([a-zA-Z0-9\-\.]+)/i', $str, $matches)) { for ($i = 0, $c = count($matches); $i < $c; $i++) { - if (preg_match('|\.$|', $matches[3][$i])) + if (preg_match('/(\.|\,)$/i', $matches[3][$i], $m)) { - $period = '.'; + $punct = $m[1]; $matches[3][$i] = substr($matches[3][$i], 0, -1); } else { - $period = ''; + $punct = ''; } - $str = str_replace($matches[0][$i], safe_mailto($matches[1][$i].'@'.$matches[2][$i].'.'.$matches[3][$i]).$period, $str); + if (filter_var(($m = $matches[1][$i].'@'.$matches[2][$i].'.'.$matches[3][$i]), FILTER_VALIDATE_EMAIL) !== FALSE) + { + $str = str_replace($matches[0][$i], safe_mailto($m).$punct, $str); + } } } diff --git a/system/libraries/Email.php b/system/libraries/Email.php index fa1d5e9bf..c1130e915 100644 --- a/system/libraries/Email.php +++ b/system/libraries/Email.php @@ -98,7 +98,7 @@ class CI_Email { */ public function __construct($config = array()) { - $this->charset = strtoupper(config_item('charset')); + $this->charset = config_item('charset'); if (count($config) > 0) { @@ -110,6 +110,8 @@ class CI_Email { $this->_safe_mode = (bool) @ini_get('safe_mode'); } + $this->charset = strtoupper($this->charset); + log_message('debug', 'Email Class Initialized'); } @@ -186,11 +188,11 @@ class CI_Email { /** * Set FROM * - * @param string - * @param string + * @param string From + * @param string Return-Path * @return object */ - public function from($from, $name = '', $return_path = '') + public function from($from, $name = '', $return_path = NULL) { if (preg_match('/\<(.*)\>/', $from, $match)) { @@ -217,16 +219,13 @@ class CI_Email { } else { - $name = $this->_prep_q_encoding($name, TRUE); + $name = $this->_prep_q_encoding($name); } } $this->set_header('From', $name.' <'.$from.'>'); - if( ! $return_path) - { - $return_path = $from; - } + isset($return_path) OR $return_path = $from; $this->set_header('Return-Path', '<'.$return_path.'>'); return $this; @@ -292,16 +291,7 @@ class CI_Email { $this->set_header('To', implode(', ', $to)); } - switch ($this->_get_protocol()) - { - case 'smtp': - $this->_recipients = $to; - break; - case 'sendmail': - case 'mail': - $this->_recipients = implode(', ', $to); - break; - } + $this->_recipients = $to; return $this; } @@ -752,8 +742,8 @@ class CI_Email { /** * Build alternative plain text message * - * This public function provides the raw message for use - * in plain-text headers of HTML-formatted emails. + * Provides the raw message for use in plain-text headers of + * HTML-formatted emails. * If the user hasn't specified his own alternative message * it creates one by stripping the HTML * @@ -761,9 +751,11 @@ class CI_Email { */ protected function _get_alt_message() { - if ($this->alt_message !== '') + if ( ! empty($this->alt_message)) { - return $this->word_wrap($this->alt_message, '76'); + return ($this->wordwrap) + ? $this->word_wrap($this->alt_message, 76) + : $this->alt_message; } $body = preg_match('/\<body.*?\>(.*)\<\/body\>/si', $this->_body, $match) ? $match[1] : $this->_body; @@ -774,7 +766,9 @@ class CI_Email { $body = str_replace(str_repeat("\n", $i), "\n\n", $body); } - return $this->word_wrap($body, 76); + return ($this->wordwrap) + ? $this->word_wrap($body, 76) + : $body; } // -------------------------------------------------------------------- @@ -783,15 +777,15 @@ class CI_Email { * Word Wrap * * @param string - * @param int + * @param int line-length limit * @return string */ - public function word_wrap($str, $charlim = '') + public function word_wrap($str, $charlim = NULL) { - // Se the character limit - if ($charlim === '') + // Set the character limit, if not already present + if (empty($charlim)) { - $charlim = ($this->wrapchars === '') ? 76 : $this->wrapchars; + $charlim = empty($this->wrapchars) ? 76 : $this->wrapchars; } // Reduce multiple spaces @@ -1105,6 +1099,10 @@ class CI_Email { */ protected function _prep_quoted_printable($str) { + // We are intentionally wrapping so mail servers will encode characters + // properly and MUAs will behave, so {unwrap} must go! + $str = str_replace(array('{unwrap}', '{/unwrap}'), '', $str); + // RFC 2045 specifies CRLF as "\r\n". // However, many developers choose to override that and violate // the RFC rules due to (apparently) a bug in MS Exchange, @@ -1130,10 +1128,6 @@ class CI_Email { $str = str_replace(array("\r\n", "\r"), "\n", $str); } - // We are intentionally wrapping so mail servers will encode characters - // properly and MUAs will behave, so {unwrap} must go! - $str = str_replace(array('{unwrap}', '{/unwrap}'), '', $str); - $escape = '='; $output = ''; @@ -1186,66 +1180,75 @@ class CI_Email { /** * Prep Q Encoding * - * Performs "Q Encoding" on a string for use in email headers. It's related - * but not identical to quoted-printable, so it has its own method + * Performs "Q Encoding" on a string for use in email headers. + * It's related but not identical to quoted-printable, so it has its + * own method. * * @param string - * @param bool set to TRUE for processing From: headers * @return string */ - protected function _prep_q_encoding($str, $from = FALSE) + protected function _prep_q_encoding($str) { - $str = str_replace(array("\r", "\n"), array('', ''), $str); - - // Line length must not exceed 76 characters, so we adjust for - // a space, 7 extra characters =??Q??=, and the charset that we will add to each line - $limit = 75 - 7 - strlen($this->charset); - - // these special characters must be converted too - $convert = array('_', '=', '?'); + $str = str_replace(array("\r", "\n"), '', $str); - if ($from === TRUE) + if ($this->charset === 'UTF-8') { - $convert[] = ','; - $convert[] = ';'; + if (MB_ENABLED === TRUE) + { + return mb_encode_mimeheader($str, $this->charset, 'Q', $this->crlf); + } + elseif (extension_loaded('iconv')) + { + $output = @iconv_mime_encode('', $str, + array( + 'scheme' => 'Q', + 'line-length' => 76, + 'input-charset' => $this->charset, + 'output-charset' => $this->charset, + 'line-break-chars' => $this->crlf + ) + ); + + // There are reports that iconv_mime_encode() might fail and return FALSE + if ($output !== FALSE) + { + // iconv_mime_encode() will always put a header field name. + // We've passed it an empty one, but it still prepends our + // encoded string with ': ', so we need to strip it. + return substr($output, 2); + } + + $chars = iconv_strlen($str, 'UTF-8'); + } } - $output = ''; - $temp = ''; + // We might already have this set for UTF-8 + isset($chars) OR $chars = strlen($str); - for ($i = 0, $length = strlen($str); $i < $length; $i++) + $output = '=?'.$this->charset.'?Q?'; + for ($i = 0, $length = strlen($output), $iconv = extension_loaded('iconv'); $i < $chars; $i++) { - // Grab the next character - $char = $str[$i]; - $ascii = ord($char); - - // convert ALL non-printable ASCII characters and our specials - if ($ascii < 32 OR $ascii > 126 OR in_array($char, $convert)) - { - $char = '='.dechex($ascii); - } + $chr = ($this->charset === 'UTF-8' && $iconv === TRUE) + ? '='.implode('=', str_split(strtoupper(bin2hex(iconv_substr($str, $i, 1, $this->charset))), 2)) + : '='.strtoupper(bin2hex($str[$i])); - // handle regular spaces a bit more compactly than =20 - if ($ascii === 32) + // RFC 2045 sets a limit of 76 characters per line. + // We'll append ?= to the end of each line though. + if ($length + ($l = strlen($chr)) > 74) { - $char = '_'; + $output .= '?='.$this->crlf // EOL + .' =?'.$this->charset.'?Q?'.$chr; // New line + $length = 6 + strlen($this->charset) + $l; // Reset the length for the new line } - - // If we're at the character limit, add the line to the output, - // reset our temp variable, and keep on chuggin' - if ((strlen($temp) + strlen($char)) >= $limit) + else { - $output .= $temp.$this->crlf; - $temp = ''; + $output .= $chr; + $length += $l; } - - // Add the character to our temporary line - $temp .= $char; } - // wrap each line with the shebang, charset, and transfer encoding - // the preceding space on successive lines is required for header "folding" - return trim(preg_replace('/^(.*?)(\r*)$/m', ' =?'.$this->charset.'?Q?$1?=$2', $output.$temp)); + // End the header + return $output.'?='; } // -------------------------------------------------------------------- @@ -1408,6 +1411,11 @@ class CI_Email { */ protected function _send_with_mail() { + if (is_array($this->_recipients)) + { + $this->_recipients = implode(', ', $this->_recipients); + } + if ($this->_safe_mode === TRUE) { return mail($this->_recipients, $this->_subject, $this->_finalbody, $this->_header_str); @@ -1754,47 +1762,6 @@ class CI_Email { // -------------------------------------------------------------------- /** - * Get IP - * - * @return string - */ - protected function _get_ip() - { - if ($this->_IP !== FALSE) - { - return $this->_IP; - } - - $cip = ( ! empty($_SERVER['HTTP_CLIENT_IP'])) ? $_SERVER['HTTP_CLIENT_IP'] : FALSE; - $rip = ( ! empty($_SERVER['REMOTE_ADDR'])) ? $_SERVER['REMOTE_ADDR'] : FALSE; - if ($cip) $this->_IP = $cip; - elseif ($rip) $this->_IP = $rip; - else - { - $fip = ( ! empty($_SERVER['HTTP_X_FORWARDED_FOR'])) ? $_SERVER['HTTP_X_FORWARDED_FOR'] : FALSE; - if ($fip) - { - $this->_IP = $fip; - } - } - - if (strpos($this->_IP, ',') !== FALSE) - { - $x = explode(',', $this->_IP); - $this->_IP = end($x); - } - - if ( ! preg_match('/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/', $this->_IP)) - { - $this->_IP = '0.0.0.0'; - } - - return $this->_IP; - } - - // -------------------------------------------------------------------- - - /** * Get Debug Message * * @return string diff --git a/system/libraries/Pagination.php b/system/libraries/Pagination.php index e1e729bb0..36b57b332 100644 --- a/system/libraries/Pagination.php +++ b/system/libraries/Pagination.php @@ -157,7 +157,7 @@ class CI_Pagination { // See if we are using a prefix or suffix on links if ($this->prefix !== '' OR $this->suffix !== '') { - $this->cur_page = (int) str_replace(array($this->prefix, $this->suffix), '', $CI->uri->segment($this->uri_segment)); + $this->cur_page = (int) str_replace(array($this->prefix, $this->suffix), '', $CI->uri->rsegment($this->uri_segment)); } if ($CI->config->item('enable_query_strings') === TRUE OR $this->page_query_string === TRUE) @@ -169,7 +169,7 @@ class CI_Pagination { } elseif ( ! $this->cur_page && $CI->uri->segment($this->uri_segment) !== $base_page) { - $this->cur_page = (int) $CI->uri->segment($this->uri_segment); + $this->cur_page = (int) $CI->uri->rsegment($this->uri_segment); } // Set current page to 1 if it's not valid or if using page numbers instead of offset diff --git a/system/libraries/Session/drivers/Session_cookie.php b/system/libraries/Session/drivers/Session_cookie.php index fb62c7ec4..8617aec2d 100755 --- a/system/libraries/Session/drivers/Session_cookie.php +++ b/system/libraries/Session/drivers/Session_cookie.php @@ -308,7 +308,7 @@ class CI_Session_cookie extends CI_Session_driver { } // Kill the cookie - $this->_setcookie($this->sess_cookie_name, addslashes(serialize(array())), ($this->now - 31500000), + $this->_setcookie($this->sess_cookie_name, '', ($this->now - 31500000), $this->cookie_path, $this->cookie_domain, 0); // Kill session data @@ -372,27 +372,31 @@ class CI_Session_cookie extends CI_Session_driver { return FALSE; } + $len = strlen($session) - 40; + + if ($len < 0) + { + log_message('debug', 'The session cookie was not signed.'); + return FALSE; + } + + // Check cookie authentication + $hmac = substr($session, $len); + $session = substr($session, 0, $len); + + if ($hmac !== hash_hmac('sha1', $session, $this->encryption_key)) + { + log_message('error', 'The session cookie data did not match what was expected.'); + $this->sess_destroy(); + return FALSE; + } + // Check for encryption if ($this->sess_encrypt_cookie === TRUE) { // Decrypt the cookie data $session = $this->CI->encrypt->decode($session); } - else - { - // Encryption was not used, so we need to check the md5 hash in the last 32 chars - $len = strlen($session)-32; - $hash = substr($session, $len); - $session = substr($session, 0, $len); - - // Does the md5 hash match? This is to prevent manipulation of session data in userspace - if ($hash !== md5($session.$this->encryption_key)) - { - log_message('error', 'The session cookie data did not match what was expected. This could be a possible hacking attempt.'); - $this->sess_destroy(); - return FALSE; - } - } // Unserialize the session array $session = $this->_unserialize($session); @@ -405,7 +409,7 @@ class CI_Session_cookie extends CI_Session_driver { } // Is the session current? - if (($session['last_activity'] + $this->sess_expiration) < $this->now) + if (($session['last_activity'] + $this->sess_expiration) < $this->now OR $session['last_activity'] > $this->now) { $this->sess_destroy(); return FALSE; @@ -658,10 +662,13 @@ class CI_Session_cookie extends CI_Session_driver { // Serialize the userdata for the cookie $cookie_data = $this->_serialize($cookie_data); - $cookie_data = ($this->sess_encrypt_cookie === TRUE) - ? $this->CI->encrypt->encode($cookie_data) - // if encryption is not used, we provide an md5 hash to prevent userside tampering - : $cookie_data.md5($cookie_data.$this->encryption_key); + if ($this->sess_encrypt_cookie === TRUE) + { + $cookie_data = $this->CI->encrypt->encode($cookie_data); + } + + // Require message authentication + $cookie_data .= hash_hmac('sha1', $cookie_data, $this->encryption_key); $expire = ($this->sess_expire_on_close === TRUE) ? 0 : $this->sess_expiration + time(); diff --git a/system/libraries/Session/drivers/Session_native.php b/system/libraries/Session/drivers/Session_native.php index 8d5e51546..da744f39b 100755 --- a/system/libraries/Session/drivers/Session_native.php +++ b/system/libraries/Session/drivers/Session_native.php @@ -55,7 +55,9 @@ class CI_Session_native extends CI_Session_driver { 'sess_time_to_update', 'cookie_prefix', 'cookie_path', - 'cookie_domain' + 'cookie_domain', + 'cookie_secure', + 'cookie_httponly' ); foreach ($prefs as $key) @@ -82,6 +84,9 @@ class CI_Session_native extends CI_Session_driver { $expire = 7200; $path = '/'; $domain = ''; + $secure = (bool) $config['cookie_secure']; + $http_only = (bool) $config['cookie_httponly']; + if ($config['sess_expiration'] !== FALSE) { // Default to 2 years if expiration is "0" @@ -99,7 +104,8 @@ class CI_Session_native extends CI_Session_driver { // Use specified domain $domain = $config['cookie_domain']; } - session_set_cookie_params($config['sess_expire_on_close'] ? 0 : $expire, $path, $domain); + + session_set_cookie_params($config['sess_expire_on_close'] ? 0 : $expire, $path, $domain, $secure, $http_only); // Start session session_start(); @@ -107,7 +113,7 @@ class CI_Session_native extends CI_Session_driver { // Check session expiration, ip, and agent $now = time(); $destroy = FALSE; - if (isset($_SESSION['last_activity']) && ($_SESSION['last_activity'] + $expire) < $now) + if (isset($_SESSION['last_activity']) && (($_SESSION['last_activity'] + $expire) < $now OR $_SESSION['last_activity'] > $now)) { // Expired - destroy $destroy = TRUE; @@ -137,8 +143,12 @@ class CI_Session_native extends CI_Session_driver { if ($config['sess_time_to_update'] && isset($_SESSION['last_activity']) && ($_SESSION['last_activity'] + $config['sess_time_to_update']) < $now) { - // Regenerate ID, but don't destroy session - $this->sess_regenerate(FALSE); + // Changing the session ID amidst a series of AJAX calls causes problems + if( ! $this->CI->input->is_ajax_request()) + { + // Regenerate ID, but don't destroy session + $this->sess_regenerate(FALSE); + } } // Set activity time @@ -189,7 +199,7 @@ class CI_Session_native extends CI_Session_driver { { // Clear session cookie $params = session_get_cookie_params(); - setcookie($name, '', time() - 42000, $params['path'], $params['domain']); + setcookie($name, '', time() - 42000, $params['path'], $params['domain'], $params['secure'], $params['httponly']); unset($_COOKIE[$name]); } session_destroy(); |