diff options
Diffstat (limited to 'system/libraries')
-rw-r--r-- | system/libraries/Cache/drivers/Cache_wincache.php | 2 | ||||
-rw-r--r-- | system/libraries/Driver.php | 22 | ||||
-rw-r--r-- | system/libraries/Email.php | 24 | ||||
-rw-r--r-- | system/libraries/Encrypt.php | 4 | ||||
-rw-r--r-- | system/libraries/Form_validation.php | 18 | ||||
-rw-r--r-- | system/libraries/Ftp.php | 2 | ||||
-rw-r--r-- | system/libraries/Image_lib.php | 16 | ||||
-rw-r--r-- | system/libraries/Migration.php | 8 | ||||
-rw-r--r-- | system/libraries/Pagination.php | 57 | ||||
-rwxr-xr-x | system/libraries/Session/Session.php | 689 | ||||
-rwxr-xr-x[-rw-r--r--] | system/libraries/Session/drivers/Session_cookie.php (renamed from system/libraries/Session.php) | 732 | ||||
-rwxr-xr-x | system/libraries/Session/drivers/Session_native.php | 232 | ||||
-rw-r--r-- | system/libraries/Unit_test.php | 5 | ||||
-rwxr-xr-x[-rw-r--r--] | system/libraries/Xmlrpc.php | 33 | ||||
-rw-r--r-- | system/libraries/Xmlrpcs.php | 2 |
15 files changed, 1362 insertions, 484 deletions
diff --git a/system/libraries/Cache/drivers/Cache_wincache.php b/system/libraries/Cache/drivers/Cache_wincache.php index 74048d564..89e9f77c8 100644 --- a/system/libraries/Cache/drivers/Cache_wincache.php +++ b/system/libraries/Cache/drivers/Cache_wincache.php @@ -2,7 +2,7 @@ /** * CodeIgniter * - * An open source application development framework for PHP 5.1.6 or newer + * An open source application development framework for PHP 5.2.4 or newer * * NOTICE OF LICENSE * diff --git a/system/libraries/Driver.php b/system/libraries/Driver.php index d67ee2549..1d084c8e4 100644 --- a/system/libraries/Driver.php +++ b/system/libraries/Driver.php @@ -54,13 +54,29 @@ class CI_Driver_Library { protected $lib_name; /** + * Get magic method + * * The first time a child is used it won't exist, so we instantiate it * subsequents calls will go straight to the proper child. * - * @param mixed $child - * @return mixed + * @param string Child class name + * @return object Child class */ public function __get($child) + { + // Try to load the driver + return $this->load_driver($child); + } + + /** + * Load driver + * + * Separate load_driver call to support explicit driver load by library or user + * + * @param string Child class name + * @return object Child class + */ + public function load_driver($child) { if ( ! isset($this->lib_name)) { @@ -268,4 +284,4 @@ class CI_Driver { } /* End of file Driver.php */ -/* Location: ./system/libraries/Driver.php */
\ No newline at end of file +/* Location: ./system/libraries/Driver.php */ diff --git a/system/libraries/Email.php b/system/libraries/Email.php index 9270d5fca..84ea1654b 100644 --- a/system/libraries/Email.php +++ b/system/libraries/Email.php @@ -715,9 +715,9 @@ class CI_Email { * @param string * @return bool */ - public function valid_email($address) + public function valid_email($email) { - return (bool) preg_match('/^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,6}$/ix', $address); + return (bool) filter_var($email, FILTER_VALIDATE_EMAIL); } // -------------------------------------------------------------------- @@ -1247,7 +1247,7 @@ class CI_Email { * * @return bool */ - public function send() + public function send($auto_clear = TRUE) { if ($this->_replyto_flag === FALSE) { @@ -1266,11 +1266,25 @@ class CI_Email { if ($this->bcc_batch_mode && count($this->_bcc_array) > $this->bcc_batch_size) { - return $this->batch_bcc_send(); + $result = $this->batch_bcc_send(); + + if ($result && $auto_clear) + { + $this->clear(); + } + + return $result; } $this->_build_message(); - return $this->_spool_email(); + $result = $this->_spool_email(); + + if ($result && $auto_clear) + { + $this->clear(); + } + + return $result; } // -------------------------------------------------------------------- diff --git a/system/libraries/Encrypt.php b/system/libraries/Encrypt.php index 8ffd93aea..679609251 100644 --- a/system/libraries/Encrypt.php +++ b/system/libraries/Encrypt.php @@ -484,7 +484,7 @@ class CI_Encrypt { */ public function set_hash($type = 'sha1') { - $this->_hash_type = ($type !== 'sha1' && $type !== 'md5') ? 'sha1' : $type; + $this->_hash_type = in_array($type, hash_algos()) ? $type : 'sha1'; } // -------------------------------------------------------------------- @@ -497,7 +497,7 @@ class CI_Encrypt { */ public function hash($str) { - return ($this->_hash_type === 'sha1') ? sha1($str) : md5($str); + return hash($this->_hash_type, $str); } } diff --git a/system/libraries/Form_validation.php b/system/libraries/Form_validation.php index 484e306b9..b490a34ca 100644 --- a/system/libraries/Form_validation.php +++ b/system/libraries/Form_validation.php @@ -460,6 +460,12 @@ class CI_Form_validation { $this->_field_data[$field]['postdata'] = $validation_array[$field]; } + // Don't try to validate if we have no rules set + if (empty($row['rules'])) + { + continue; + } + $this->_execute($row, explode('|', $row['rules']), $this->_field_data[$field]['postdata']); } @@ -1076,7 +1082,7 @@ class CI_Form_validation { */ public function valid_email($str) { - return (bool) preg_match('/^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,6}$/ix', $str); + return (bool) filter_var($str, FILTER_VALIDATE_EMAIL); } // -------------------------------------------------------------------- @@ -1129,7 +1135,7 @@ class CI_Form_validation { */ public function alpha($str) { - return (bool) preg_match('/^[a-z]+$/i', $str); + return ctype_alpha($str); } // -------------------------------------------------------------------- @@ -1142,7 +1148,7 @@ class CI_Form_validation { */ public function alpha_numeric($str) { - return (bool) preg_match('/^[a-z0-9]+$/i', $str); + return ctype_alnum((string) $str); } // -------------------------------------------------------------------- @@ -1264,7 +1270,7 @@ class CI_Form_validation { */ public function is_natural($str) { - return (bool) preg_match('/^[0-9]+$/', $str); + return ctype_digit((string) $str); } // -------------------------------------------------------------------- @@ -1277,7 +1283,7 @@ class CI_Form_validation { */ public function is_natural_no_zero($str) { - return ($str && preg_match('/^[0-9]+$/', $str)); + return ($str != 0 && ctype_digit((string) $str)); } // -------------------------------------------------------------------- @@ -1360,7 +1366,7 @@ class CI_Form_validation { */ public function strip_image_tags($str) { - return $this->CI->input->strip_image_tags($str); + return $this->CI->security->strip_image_tags($str); } // -------------------------------------------------------------------- diff --git a/system/libraries/Ftp.php b/system/libraries/Ftp.php index 461e884fb..76f5e151a 100644 --- a/system/libraries/Ftp.php +++ b/system/libraries/Ftp.php @@ -445,7 +445,7 @@ class CI_FTP { * Set file permissions * * @param string the file path - * @param string the permissions + * @param int the permissions * @return bool */ public function chmod($path, $perm) diff --git a/system/libraries/Image_lib.php b/system/libraries/Image_lib.php index 4735dfd08..ef4187847 100644 --- a/system/libraries/Image_lib.php +++ b/system/libraries/Image_lib.php @@ -855,7 +855,14 @@ class CI_Image_lib { } else // Resize { - $cmd .= ' -resize '.$this->width.'x'.$this->height.' "'.$this->full_src_path.'" "'.$this->full_dst_path.'" 2>&1'; + if($this->maintain_ratio === TRUE) + { + $cmd .= ' -resize '.$this->width.'x'.$this->height.' "'.$this->full_src_path.'" "'.$this->full_dst_path.'" 2>&1'; + } + else + { + $cmd .= ' -resize '.$this->width.'x'.$this->height.'\! "'.$this->full_src_path.'" "'.$this->full_dst_path.'" 2>&1'; + } } $retval = 1; @@ -1313,6 +1320,13 @@ class CI_Image_lib { imagestring($src_img, $this->wm_font_size, $x_shad, $y_shad, $this->wm_text, $drp_color); imagestring($src_img, $this->wm_font_size, $x_axis, $y_axis, $this->wm_text, $txt_color); } + + // We can preserve transparency for PNG images + if ($this->image_type === 3) + { + imagealphablending($src_img, FALSE); + imagesavealpha($src_img, TRUE); + } } // Output the final image diff --git a/system/libraries/Migration.php b/system/libraries/Migration.php index 3a1e7a0ad..5d637d44a 100644 --- a/system/libraries/Migration.php +++ b/system/libraries/Migration.php @@ -285,14 +285,14 @@ class CI_Migration { if ( ! $migrations = $this->find_migrations()) { $this->_error_string = $this->lang->line('migration_none_found'); - return false; + return FALSE; } $last_migration = basename(end($migrations)); // Calculate the last migration step from existing migration // filenames and procceed to the standard version migration - return $this->version((int) substr($last_migration, 0, 3)); + return $this->version((int) $last_migration); } // -------------------------------------------------------------------- @@ -322,9 +322,9 @@ class CI_Migration { // -------------------------------------------------------------------- /** - * Set's the schema to the latest migration + * Retrieves list of available migration scripts * - * @return mixed true if already latest, false if failed, int if upgraded + * @return array list of migration file paths sorted by version */ protected function find_migrations() { diff --git a/system/libraries/Pagination.php b/system/libraries/Pagination.php index 75745dd48..e1e729bb0 100644 --- a/system/libraries/Pagination.php +++ b/system/libraries/Pagination.php @@ -52,24 +52,25 @@ class CI_Pagination { protected $full_tag_open = ''; protected $full_tag_close = ''; protected $first_tag_open = ''; - protected $first_tag_close = ' '; - protected $last_tag_open = ' '; + protected $first_tag_close = ''; + protected $last_tag_open = ''; protected $last_tag_close = ''; protected $first_url = ''; // Alternative URL for the First Page. - protected $cur_tag_open = ' <strong>'; + protected $cur_tag_open = '<strong>'; protected $cur_tag_close = '</strong>'; - protected $next_tag_open = ' '; - protected $next_tag_close = ' '; - protected $prev_tag_open = ' '; + protected $next_tag_open = ''; + protected $next_tag_close = ''; + protected $prev_tag_open = ''; protected $prev_tag_close = ''; - protected $num_tag_open = ' '; + protected $num_tag_open = ''; protected $num_tag_close = ''; protected $page_query_string = FALSE; - protected $query_string_segment = 'per_page'; + protected $query_string_segment = 'per_page'; protected $display_pages = TRUE; protected $_attributes = ''; protected $_link_types = array(); protected $reuse_query_string = FALSE; + protected $data_page_attr = 'data-ci-pagination-page'; /** * Constructor @@ -202,7 +203,7 @@ class CI_Pagination { if ( ! $this->use_page_numbers) { - $this->cur_page = floor(($this->cur_page/$this->per_page) + 1); + $this->cur_page = (int) floor(($this->cur_page/$this->per_page) + 1); } // Calculate the start and end numbers. These determine @@ -214,7 +215,8 @@ class CI_Pagination { // string. If post, add a trailing slash to the base URL if needed if ($CI->config->item('enable_query_strings') === TRUE OR $this->page_query_string === TRUE) { - $this->base_url = rtrim($this->base_url).'&'.$this->query_string_segment.'='; + $segment = (strpos($this->base_url, '?')) ? '&' : '?'; + $this->base_url = rtrim($this->base_url).$segment.$this->query_string_segment.'='; } else { @@ -230,10 +232,12 @@ class CI_Pagination { if ($this->reuse_query_string === TRUE) { $get = $CI->input->get(); - + // Unset the controll, method, old-school routing options unset($get['c'], $get['m'], $get[$this->query_string_segment]); + if ( ! $get) $get = array(); + // Put everything else onto the end $query_string = (strpos($this->base_url, '&') !== FALSE ? '&' : '?') . http_build_query($get, '', '&'); @@ -245,7 +249,11 @@ class CI_Pagination { if ($this->first_link !== FALSE && $this->cur_page > ($this->num_links + 1)) { $first_url = ($this->first_url === '') ? $this->base_url : $this->first_url; - $output .= $this->first_tag_open.'<a href="'.$first_url.'"'.$this->_attributes.$this->_attr_rel('start').'>' + + // Take the general parameters, and squeeze this pagination-page attr in there for JS fw's + $attributes = sprintf('%s %s="%d"', $this->_attributes, $this->data_page_attr, 1); + + $output .= $this->first_tag_open.'<a href="'.$first_url.'"'.$attributes.$this->_attr_rel('start').'>' .$this->first_link.'</a>'.$this->first_tag_close; } @@ -254,15 +262,18 @@ class CI_Pagination { { $i = ($this->use_page_numbers) ? $uri_page_number - 1 : $uri_page_number - $this->per_page; + // Take the general parameters, and squeeze this pagination-page attr in there for JS fw's + $attributes = sprintf('%s %s="%d"', $this->_attributes, $this->data_page_attr, (int) $i); + if ($i === $base_page && $this->first_url !== '') { - $output .= $this->prev_tag_open.'<a href="'.$this->first_url.$query_string.'"'.$this->_attributes.$this->_attr_rel('prev').'>' + $output .= $this->prev_tag_open.'<a href="'.$this->first_url.$query_string.'"'.$attributes.$this->_attr_rel('prev').'>' .$this->prev_link.'</a>'.$this->prev_tag_close; } else { $append = ($i === $base_page) ? $query_string : $this->prefix.$i.$this->suffix; - $output .= $this->prev_tag_open.'<a href="'.$this->base_url.$append.'"'.$this->_attributes.$this->_attr_rel('prev').'>' + $output .= $this->prev_tag_open.'<a href="'.$this->base_url.$append.'"'.$attributes.$this->_attr_rel('prev').'>' .$this->prev_link.'</a>'.$this->prev_tag_close; } @@ -275,6 +286,10 @@ class CI_Pagination { for ($loop = $start -1; $loop <= $end; $loop++) { $i = ($this->use_page_numbers) ? $loop : ($loop * $this->per_page) - $this->per_page; + + // Take the general parameters, and squeeze this pagination-page attr in there for JS fw's + $attributes = sprintf('%s %s="%d"', $this->_attributes, $this->data_page_attr, (int) $i); + if ($i >= $base_page) { if ($this->cur_page === $loop) @@ -286,13 +301,13 @@ class CI_Pagination { $n = ($i === $base_page) ? '' : $i; if ($n === '' && ! empty($this->first_url)) { - $output .= $this->num_tag_open.'<a href="'.$this->first_url.$query_string.'"'.$this->_attributes.$this->_attr_rel('start').'>' + $output .= $this->num_tag_open.'<a href="'.$this->first_url.$query_string.'"'.$attributes.$this->_attr_rel('start').'>' .$loop.'</a>'.$this->num_tag_close; } else { $append = ($n === '') ? $query_string : $this->prefix.$n.$this->suffix; - $output .= $this->num_tag_open.'<a href="'.$this->base_url.$append.'"'.$this->_attributes.$this->_attr_rel('start').'>' + $output .= $this->num_tag_open.'<a href="'.$this->base_url.$append.'"'.$attributes.$this->_attr_rel('start').'>' .$loop.'</a>'.$this->num_tag_close; } } @@ -305,7 +320,10 @@ class CI_Pagination { { $i = ($this->use_page_numbers) ? $this->cur_page + 1 : $this->cur_page * $this->per_page; - $output .= $this->next_tag_open.'<a href="'.$this->base_url.$this->prefix.$i.$this->suffix.'"'.$this->_attributes + // Take the general parameters, and squeeze this pagination-page attr in there for JS fw's + $attributes = sprintf('%s %s="%d"', $this->_attributes, $this->data_page_attr, (int) $i); + + $output .= $this->next_tag_open.'<a href="'.$this->base_url.$this->prefix.$i.$this->suffix.'"'.$attributes .$this->_attr_rel('next').'>'.$this->next_link.'</a>'.$this->next_tag_close; } @@ -314,7 +332,10 @@ class CI_Pagination { { $i = ($this->use_page_numbers) ? $num_pages : ($num_pages * $this->per_page) - $this->per_page; - $output .= $this->last_tag_open.'<a href="'.$this->base_url.$this->prefix.$i.$this->suffix.'"'.$this->_attributes.'>' + // Take the general parameters, and squeeze this pagination-page attr in there for JS fw's + $attributes = sprintf('%s %s="%d"', $this->_attributes, $this->data_page_attr, (int) $i); + + $output .= $this->last_tag_open.'<a href="'.$this->base_url.$this->prefix.$i.$this->suffix.'"'.$attributes.'>' .$this->last_link.'</a>'.$this->last_tag_close; } diff --git a/system/libraries/Session/Session.php b/system/libraries/Session/Session.php new file mode 100755 index 000000000..e6f6050c0 --- /dev/null +++ b/system/libraries/Session/Session.php @@ -0,0 +1,689 @@ +<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); +/** + * CodeIgniter + * + * An open source application development framework for PHP 5.2.4 or newer + * + * NOTICE OF LICENSE + * + * Licensed under the Open Software License version 3.0 + * + * This source file is subject to the Open Software License (OSL 3.0) that is + * bundled with this package in the files license.txt / license.rst. It is + * also available through the world wide web at this URL: + * http://opensource.org/licenses/OSL-3.0 + * If you did not receive a copy of the license and are unable to obtain it + * through the world wide web, please send an email to + * licensing@ellislab.com so we can send you a copy immediately. + * + * @package CodeIgniter + * @author EllisLab Dev Team + * @copyright Copyright (c) 2006 - 2012 EllisLab, Inc. + * @license http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) + * @link http://codeigniter.com + * @since Version 2.0 + * @filesource + */ + +/** + * CodeIgniter Session Class + * + * The user interface defined by EllisLabs, now with puggable drivers to manage different storage mechanisms. + * By default, the cookie session driver will load, but the 'sess_driver' config/param item (see above) can be + * used to specify the 'native' driver, or any other you might create. + * Once loaded, this driver setup is a drop-in replacement for the former CI_Session library, taking its place as the + * 'session' member of the global controller framework (e.g.: $CI->session or $this->session). + * In keeping with the CI_Driver methodology, multiple drivers may be loaded, although this might be a bit confusing. + * The CI_Session library class keeps track of the most recently loaded driver as "current" to call for driver methods. + * Ideally, one driver is loaded and all calls go directly through the main library interface. However, any methods + * called through the specific driver will switch the "current" driver to itself before invoking the library method + * (which will then call back into the driver for low-level operations). So, alternation between two drivers can be + * achieved by specifying which driver to use for each call (e.g.: $this->session->native->set_userdata('foo', 'bar'); + * $this->session->cookie->userdata('foo'); $this->session->native->unset_userdata('foo');). Notice in the previous + * example that the _native_ userdata value 'foo' would be set to 'bar', which would NOT be returned by the call for + * the _cookie_ userdata 'foo', nor would the _cookie_ value be unset by the call to unset the _native_ 'foo' value. + * + * @package CodeIgniter + * @subpackage Libraries + * @category Sessions + * @author EllisLab Dev Team + * @link http://codeigniter.com/user_guide/libraries/sessions.html + */ +class CI_Session extends CI_Driver_Library { + + public $params = array(); + protected $current = NULL; + protected $userdata = array(); + + const FLASHDATA_KEY = 'flash'; + const FLASHDATA_NEW = ':new:'; + const FLASHDATA_OLD = ':old:'; + const FLASHDATA_EXP = ':exp:'; + const EXPIRATION_KEY = '__expirations'; + const TEMP_EXP_DEF = 300; + + /** + * CI_Session constructor + * + * The constructor loads the configured driver ('sess_driver' in config.php or as a parameter), running + * routines in its constructor, and manages flashdata aging. + * + * @param array Configuration parameters + */ + public function __construct(array $params = array()) + { + log_message('debug', 'CI_Session Class Initialized'); + + // Get valid drivers list + $CI =& get_instance(); + $this->valid_drivers = array( + 'Session_native', + 'Session_cookie' + ); + $key = 'sess_valid_drivers'; + $drivers = isset($params[$key]) ? $params[$key] : $CI->config->item($key); + if ($drivers) + { + is_array($drivers) OR $drivers = array($drivers); + + // Add driver names to valid list + foreach ($drivers as $driver) + { + if ( ! in_array(strtolower($driver), array_map('strtolower', $this->valid_drivers))) + { + $this->valid_drivers[] = $driver; + } + } + } + + // Get driver to load + $key = 'sess_driver'; + $driver = isset($params[$key]) ? $params[$key] : $CI->config->item($key); + if ( ! $driver) + { + $driver = 'cookie'; + } + + if ( ! in_array('session_'.strtolower($driver), array_map('strtolower', $this->valid_drivers))) + { + $this->valid_drivers[] = 'Session_'.$driver; + } + + // Save a copy of parameters in case drivers need access + $this->params = $params; + + // Load driver and get array reference + $this->load_driver($driver); + + // Delete 'old' flashdata (from last request) + $this->_flashdata_sweep(); + + // Mark all new flashdata as old (data will be deleted before next request) + $this->_flashdata_mark(); + + // Delete expired tempdata + $this->_tempdata_sweep(); + + log_message('debug', 'CI_Session routines successfully run'); + } + + // ------------------------------------------------------------------------ + + /** + * Loads session storage driver + * + * @param string Driver classname + * @return object Loaded driver object + */ + public function load_driver($driver) + { + // Save reference to most recently loaded driver as library default and sync userdata + $this->current = parent::load_driver($driver); + $this->userdata =& $this->current->get_userdata(); + return $this->current; + } + + // ------------------------------------------------------------------------ + + /** + * Select default session storage driver + * + * @param string Driver classname + * @return void + */ + public function select_driver($driver) + { + // Validate driver name + $lowername = strtolower(str_replace('CI_', '', $driver)); + if (in_array($lowername, array_map('strtolower', $this->valid_drivers))) + { + // See if driver is loaded + $child = str_replace($this->lib_name.'_', '', $driver); + if (isset($this->$child)) + { + // See if driver is already current + if ($this->$child !== $this->current) + { + // Make driver current and sync userdata + $this->current = $this->$child; + $this->userdata =& $this->current->get_userdata(); + } + } + else + { + // Load new driver + $this->load_driver($child); + } + } + } + + // ------------------------------------------------------------------------ + + /** + * Destroy the current session + * + * @return void + */ + public function sess_destroy() + { + // Just call destroy on driver + $this->current->sess_destroy(); + } + + // ------------------------------------------------------------------------ + + /** + * Regenerate the current session + * + * @param bool Destroy session data flag (default: false) + * @return void + */ + public function sess_regenerate($destroy = FALSE) + { + // Call regenerate on driver and resync userdata + $this->current->sess_regenerate($destroy); + $this->userdata =& $this->current->get_userdata(); + } + + // ------------------------------------------------------------------------ + + /** + * Fetch a specific item from the session array + * + * @param string Item key + * @return string Item value or NULL if not found + */ + public function userdata($item) + { + return isset($this->userdata[$item]) ? $this->userdata[$item] : NULL; + } + + // ------------------------------------------------------------------------ + + /** + * Fetch all session data + * + * @return array User data array + */ + public function all_userdata() + { + return isset($this->userdata) ? $this->userdata : NULL; + } + + // ------------------------------------------------------------------------ + + /** + * Fetch all flashdata + * + * @return array Flash data array + */ + public function all_flashdata() + { + $out = array(); + + // loop through all userdata + foreach ($this->all_userdata() as $key => $val) + { + // if it contains flashdata, add it + if (strpos($key, self::FLASHDATA_KEY.self::FLASHDATA_OLD) !== FALSE) + { + $key = str_replace(self::FLASHDATA_KEY.self::FLASHDATA_OLD, '', $key); + $out[$key] = $val; + } + } + return $out; + } + + // ------------------------------------------------------------------------ + + /** + * Add or change data in the "userdata" array + * + * @param mixed Item name or array of items + * @param string Item value or empty string + * @return void + */ + public function set_userdata($newdata = array(), $newval = '') + { + // Wrap params as array if singular + if (is_string($newdata)) + { + $newdata = array($newdata => $newval); + } + + // Set each name/value pair + if (count($newdata) > 0) + { + foreach ($newdata as $key => $val) + { + $this->userdata[$key] = $val; + } + } + + // Tell driver data changed + $this->current->sess_save(); + } + + // ------------------------------------------------------------------------ + + /** + * Delete a session variable from the "userdata" array + * + * @param mixed Item name or array of item names + * @return void + */ + public function unset_userdata($newdata = array()) + { + // Wrap single name as array + if (is_string($newdata)) + { + $newdata = array($newdata => ''); + } + + // Unset each item name + if (count($newdata) > 0) + { + foreach (array_keys($newdata) as $key) + { + unset($this->userdata[$key]); + } + } + + // Tell driver data changed + $this->current->sess_save(); + } + + // ------------------------------------------------------------------------ + + /** + * Determine if an item exists + * + * @param string Item name + * @return bool + */ + public function has_userdata($item) + { + return isset($this->userdata[$item]); + } + + // ------------------------------------------------------------------------ + + /** + * Add or change flashdata, only available until the next request + * + * @param mixed Item name or array of items + * @param string Item value or empty string + * @return void + */ + public function set_flashdata($newdata = array(), $newval = '') + { + // Wrap item as array if singular + if (is_string($newdata)) + { + $newdata = array($newdata => $newval); + } + + // Prepend each key name and set value + if (count($newdata) > 0) + { + foreach ($newdata as $key => $val) + { + $flashdata_key = self::FLASHDATA_KEY.self::FLASHDATA_NEW.$key; + $this->set_userdata($flashdata_key, $val); + } + } + } + + // ------------------------------------------------------------------------ + + /** + * Keeps existing flashdata available to next request. + * + * @param string Item key + * @return void + */ + public function keep_flashdata($key) + { + // 'old' flashdata gets removed. Here we mark all flashdata as 'new' to preserve it from _flashdata_sweep() + // Note the function will return NULL if the $key provided cannot be found + $old_flashdata_key = self::FLASHDATA_KEY.self::FLASHDATA_OLD.$key; + $value = $this->userdata($old_flashdata_key); + + $new_flashdata_key = self::FLASHDATA_KEY.self::FLASHDATA_NEW.$key; + $this->set_userdata($new_flashdata_key, $value); + } + + // ------------------------------------------------------------------------ + + /** + * Fetch a specific flashdata item from the session array + * + * @param string Item key + * @return string + */ + public function flashdata($key) + { + // Prepend key and retrieve value + $flashdata_key = self::FLASHDATA_KEY.self::FLASHDATA_OLD.$key; + return $this->userdata($flashdata_key); + } + + // ------------------------------------------------------------------------ + + /** + * Add or change tempdata, only available until expiration + * + * @param mixed Item name or array of items + * @param string Item value or empty string + * @param int Item lifetime in seconds or 0 for default + * @return void + */ + public function set_tempdata($newdata = array(), $newval = '', $expire = 0) + { + // Set expiration time + $expire = time() + ($expire ? $expire : self::TEMP_EXP_DEF); + + // Wrap item as array if singular + if (is_string($newdata)) + { + $newdata = array($newdata => $newval); + } + + // Get or create expiration list + $expirations = $this->userdata(self::EXPIRATION_KEY); + if ( ! $expirations) + { + $expirations = array(); + } + + // Prepend each key name and set value + if (count($newdata) > 0) + { + foreach ($newdata as $key => $val) + { + $tempdata_key = self::FLASHDATA_KEY.self::FLASHDATA_EXP.$key; + $expirations[$tempdata_key] = $expire; + $this->set_userdata($tempdata_key, $val); + } + } + + // Update expiration list + $this->set_userdata(self::EXPIRATION_KEY, $expirations); + } + + // ------------------------------------------------------------------------ + + /** + * Delete a temporary session variable from the "userdata" array + * + * @param mixed Item name or array of item names + * @return void + */ + public function unset_tempdata($newdata = array()) + { + // Get expirations list + $expirations = $this->userdata(self::EXPIRATION_KEY); + if (empty($expirations)) + { + // Nothing to do + return; + } + + // Wrap single name as array + if (is_string($newdata)) + { + $newdata = array($newdata => ''); + } + + // Prepend each item name and unset + if (count($newdata) > 0) + { + foreach (array_keys($newdata) as $key) + { + $tempdata_key = self::FLASHDATA_KEY.self::FLASHDATA_EXP.$key; + unset($expirations[$tempdata_key]); + $this->unset_userdata($tempdata_key); + } + } + + // Update expiration list + $this->set_userdata(self::EXPIRATION_KEY, $expirations); + } + + // ------------------------------------------------------------------------ + + /** + * Fetch a specific tempdata item from the session array + * + * @param string Item key + * @return string + */ + public function tempdata($key) + { + // Prepend key and return value + $tempdata_key = self::FLASHDATA_KEY.self::FLASHDATA_EXP.$key; + return $this->userdata($tempdata_key); + } + + // ------------------------------------------------------------------------ + + /** + * Identifies flashdata as 'old' for removal + * when _flashdata_sweep() runs. + * + * @return void + */ + protected function _flashdata_mark() + { + foreach ($this->all_userdata() as $name => $value) + { + $parts = explode(self::FLASHDATA_NEW, $name); + if (is_array($parts) && count($parts) === 2) + { + $new_name = self::FLASHDATA_KEY.self::FLASHDATA_OLD.$parts[1]; + $this->set_userdata($new_name, $value); + $this->unset_userdata($name); + } + } + } + + // ------------------------------------------------------------------------ + + /** + * Removes all flashdata marked as 'old' + * + * @return void + */ + protected function _flashdata_sweep() + { + $userdata = $this->all_userdata(); + foreach (array_keys($userdata) as $key) + { + if (strpos($key, self::FLASHDATA_OLD)) + { + $this->unset_userdata($key); + } + } + } + + // ------------------------------------------------------------------------ + + /** + * Removes all expired tempdata + * + * @return void + */ + protected function _tempdata_sweep() + { + // Get expirations list + $expirations = $this->userdata(self::EXPIRATION_KEY); + if (empty($expirations)) + { + // Nothing to do + return; + } + + // Unset expired elements + $now = time(); + $userdata = $this->all_userdata(); + foreach (array_keys($userdata) as $key) + { + if (strpos($key, self::FLASHDATA_EXP) && $expirations[$key] < $now) + { + unset($expirations[$key]); + $this->unset_userdata($key); + } + } + + // Update expiration list + $this->set_userdata(self::EXPIRATION_KEY, $expirations); + } + +} + +// ------------------------------------------------------------------------ + +/** + * CI_Session_driver Class + * + * Extend this class to make a new CI_Session driver. + * A CI_Session driver basically manages an array of name/value pairs with some sort of storage mechanism. + * To make a new driver, derive from (extend) CI_Session_driver. Overload the initialize method and read or create + * session data. Then implement a save handler to write changed data to storage (sess_save), a destroy handler + * to remove deleted data (sess_destroy), and an access handler to expose the data (get_userdata). + * Put your driver in the libraries/Session/drivers folder anywhere in the loader paths. This includes the + * application directory, the system directory, or any path you add with $CI->load->add_package_path(). + * Your driver must be named CI_Session_<name>, and your filename must be Session_<name>.php, + * preferably also capitalized. (e.g.: CI_Session_foo in libraries/Session/drivers/Session_foo.php) + * Then specify the driver by setting 'sess_driver' in your config file or as a parameter when loading the CI_Session + * object. (e.g.: $config['sess_driver'] = 'foo'; OR $CI->load->driver('session', array('sess_driver' => 'foo')); ) + * Already provided are the Native driver, which manages the native PHP $_SESSION array, and + * the Cookie driver, which manages the data in a browser cookie, with optional extra storage in a database table. + * + * @package CodeIgniter + * @subpackage Libraries + * @category Sessions + * @author EllisLab Dev Team + */ +abstract class CI_Session_driver extends CI_Driver { + + /** + * Decorate + * + * Decorates the child with the parent driver lib's methods and properties + * + * @param object Parent library object + * @return void + */ + public function decorate($parent) + { + // Call base class decorate first + parent::decorate($parent); + + // Call initialize method now that driver has access to $this->_parent + $this->initialize(); + } + + // ------------------------------------------------------------------------ + + /** + * __call magic method + * + * Handles access to the parent driver library's methods + * + * @param string Library method name + * @param array Method arguments (default: none) + * @return mixed + */ + public function __call($method, $args = array()) + { + // Make sure the parent library uses this driver + $this->_parent->select_driver(get_class($this)); + return parent::__call($method, $args); + } + + // ------------------------------------------------------------------------ + + /** + * Initialize driver + * + * @return void + */ + protected function initialize() + { + // Overload this method to implement initialization + } + + // ------------------------------------------------------------------------ + + /** + * Save the session data + * + * Data in the array has changed - perform any storage synchronization + * necessary. The child class MUST implement this abstract method! + * + * @return void + */ + abstract public function sess_save(); + + // ------------------------------------------------------------------------ + + /** + * Destroy the current session + * + * Clean up storage for this session - it has been terminated. + * The child class MUST implement this abstract method! + * + * @return void + */ + abstract public function sess_destroy(); + + // ------------------------------------------------------------------------ + + /** + * Regenerate the current session + * + * Regenerate the session ID. + * The child class MUST implement this abstract method! + * + * @param bool Destroy session data flag (default: false) + * @return void + */ + abstract public function sess_regenerate($destroy = FALSE); + + // ------------------------------------------------------------------------ + + /** + * Get a reference to user data array + * + * Give array access to the main CI_Session object. + * The child class MUST implement this abstract method! + * + * @return array Reference to userdata + */ + abstract public function &get_userdata(); + +} + +/* End of file Session.php */ +/* Location: ./system/libraries/Session/Session.php */
\ No newline at end of file diff --git a/system/libraries/Session.php b/system/libraries/Session/drivers/Session_cookie.php index 72a942b8a..4f415cc0d 100644..100755 --- a/system/libraries/Session.php +++ b/system/libraries/Session/drivers/Session_cookie.php @@ -9,7 +9,7 @@ * Licensed under the Open Software License version 3.0 * * This source file is subject to the Open Software License (OSL 3.0) that is - * bundled with this package in the files license.txt / license.rst. It is + * bundled with this package in the files license.txt / license.rst. It is * also available through the world wide web at this URL: * http://opensource.org/licenses/OSL-3.0 * If you did not receive a copy of the license and are unable to obtain it @@ -26,7 +26,9 @@ */ /** - * Session Class + * Cookie-based session management driver + * + * This is the classic CI_Session functionality, as written by EllisLab, abstracted out to a driver. * * @package CodeIgniter * @subpackage Libraries @@ -34,7 +36,7 @@ * @author EllisLab Dev Team * @link http://codeigniter.com/user_guide/libraries/sessions.html */ -class CI_Session { +class CI_Session_cookie extends CI_Session_driver { /** * Whether to encrypt the session cookie @@ -69,7 +71,7 @@ class CI_Session { * * @var bool */ - public $sess_expire_on_close = FALSE; + public $sess_expire_on_close = FALSE; /** * Whether to match session on ip address @@ -83,7 +85,7 @@ class CI_Session { * * @var bool */ - public $sess_match_useragent = TRUE; + public $sess_match_useragent = TRUE; /** * Name of session cookie @@ -104,7 +106,7 @@ class CI_Session { * * @var string */ - public $cookie_path = ''; + public $cookie_path = ''; /** * Session cookie domain @@ -142,13 +144,6 @@ class CI_Session { public $encryption_key = ''; /** - * String to indicate flash data cookies - * - * @var string - */ - public $flashdata_key = 'flash'; - - /** * Timezone to use for the current time * * @var string @@ -156,18 +151,11 @@ class CI_Session { public $time_reference = 'local'; /** - * Probablity level of garbage collection of old sessions - * - * @var int - */ - public $gc_probability = 5; - - /** * Session data * * @var array */ - public $userdata = array(); + public $userdata = array(); /** * Reference to CodeIgniter instance @@ -184,31 +172,65 @@ class CI_Session { public $now; /** - * Session Constructor + * Default userdata keys * - * The constructor runs the session routines automatically - * whenever the class is instantiated. + * @var array + */ + protected $defaults = array( + 'session_id' => NULL, + 'ip_address' => NULL, + 'user_agent' => NULL, + 'last_activity' => NULL + ); + + /** + * Data needs DB update flag + * + * @var bool + */ + protected $data_dirty = FALSE; + + /** + * Initialize session driver object * - * @param array * @return void */ - public function __construct($params = array()) + protected function initialize() { - log_message('debug', 'Session Class Initialized'); - // Set the super object to a local variable for use throughout the class $this->CI =& get_instance(); // Set all the session preferences, which can either be set - // manually via the $params array above or via the config file - foreach (array('sess_encrypt_cookie', 'sess_use_database', 'sess_table_name', 'sess_expiration', 'sess_expire_on_close', 'sess_match_ip', 'sess_match_useragent', 'sess_cookie_name', 'cookie_path', 'cookie_domain', 'cookie_secure', 'cookie_httponly', 'sess_time_to_update', 'time_reference', 'cookie_prefix', 'encryption_key') as $key) + // manually via the $params array or via the config file + $prefs = array( + 'sess_encrypt_cookie', + 'sess_use_database', + 'sess_table_name', + 'sess_expiration', + 'sess_expire_on_close', + 'sess_match_ip', + 'sess_match_useragent', + 'sess_cookie_name', + 'cookie_path', + 'cookie_domain', + 'cookie_secure', + 'cookie_httponly', + 'sess_time_to_update', + 'time_reference', + 'cookie_prefix', + 'encryption_key' + ); + + foreach ($prefs as $key) { - $this->$key = isset($params[$key]) ? $params[$key] : $this->CI->config->item($key); + $this->$key = isset($this->_parent->params[$key]) + ? $this->_parent->params[$key] + : $this->CI->config->item($key); } if ($this->encryption_key === '') { - show_error('In order to use the Session class you are required to set an encryption key in your config file.'); + show_error('In order to use the Cookie Session driver you are required to set an encryption key in your config file.'); } // Load the string helper so we can use the strip_slashes() function @@ -220,14 +242,18 @@ class CI_Session { $this->CI->load->library('encrypt'); } - // Are we using a database? If so, load it + // Check for database if ($this->sess_use_database === TRUE && $this->sess_table_name !== '') { + // Load database driver $this->CI->load->database(); + + // Register shutdown function + register_shutdown_function(array($this, '_update_db')); } - // Set the "now" time. Can either be GMT or server time, based on the - // config prefs. We use this to set the "last activity" time + // Set the "now" time. Can either be GMT or server time, based on the config prefs. + // We use this to set the "last activity" time $this->now = $this->_get_time(); // Set the session length. If the session expiration is @@ -242,59 +268,135 @@ class CI_Session { // Run the Session routine. If a session doesn't exist we'll // create a new one. If it does, we'll update it. - if ( ! $this->sess_read()) + if ( ! $this->_sess_read()) { - $this->sess_create(); + $this->_sess_create(); } else { - $this->sess_update(); + $this->_sess_update(); } - // Delete 'old' flashdata (from last request) - $this->_flashdata_sweep(); - - // Mark all new flashdata as old (data will be deleted before next request) - $this->_flashdata_mark(); - // Delete expired sessions if necessary $this->_sess_gc(); + } + + // ------------------------------------------------------------------------ + + /** + * Write the session data + * + * @return void + */ + public function sess_save() + { + // Check for database + if ($this->sess_use_database === TRUE) + { + // Mark custom data as dirty so we know to update the DB + $this->data_dirty = TRUE; + } + + // Write the cookie + $this->_set_cookie(); + } + + // ------------------------------------------------------------------------ + + /** + * Destroy the current session + * + * @return void + */ + public function sess_destroy() + { + // Kill the session DB row + if ($this->sess_use_database === TRUE && isset($this->userdata['session_id'])) + { + $this->CI->db->delete($this->sess_table_name, array('session_id' => $this->userdata['session_id'])); + $this->data_dirty = FALSE; + } + + // Kill the cookie + $this->_setcookie($this->sess_cookie_name, addslashes(serialize(array())), ($this->now - 31500000), + $this->cookie_path, $this->cookie_domain, 0); + + // Kill session data + $this->userdata = array(); + } + + // ------------------------------------------------------------------------ + + /** + * Regenerate the current session + * + * Regenerate the session id + * + * @param bool Destroy session data flag (default: false) + * @return void + */ + public function sess_regenerate($destroy = FALSE) + { + // Check destroy flag + if ($destroy) + { + // Destroy old session and create new one + $this->sess_destroy(); + $this->_sess_create(); + } + else + { + // Just force an update to recreate the id + $this->_sess_update(TRUE); + } + } + + // ------------------------------------------------------------------------ - log_message('debug', 'Session routines successfully run'); + /** + * Get a reference to user data array + * + * @return array Reference to userdata + */ + public function &get_userdata() + { + return $this->userdata; } - // -------------------------------------------------------------------- + // ------------------------------------------------------------------------ /** * Fetch the current session data if it exists * * @return bool */ - public function sess_read() + protected function _sess_read() { // Fetch the cookie $session = $this->CI->input->cookie($this->sess_cookie_name); - // No cookie? Goodbye cruel world!... + // No cookie? Goodbye cruel world!... if ($session === NULL) { log_message('debug', 'A session cookie was not found.'); return FALSE; } - // Decrypt the cookie data + // 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 - $hash = substr($session, strlen($session)-32); // get last 32 chars - $session = substr($session, 0, strlen($session)-32); + // 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)) + 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(); @@ -327,7 +429,8 @@ class CI_Session { } // Does the User Agent Match? - if ($this->sess_match_useragent === TRUE && trim($session['user_agent']) !== trim(substr($this->CI->input->user_agent(), 0, 120))) + if ($this->sess_match_useragent === TRUE && + trim($session['user_agent']) !== trim(substr($this->CI->input->user_agent(), 0, 120))) { $this->sess_destroy(); return FALSE; @@ -348,8 +451,19 @@ class CI_Session { $this->CI->db->where('user_agent', $session['user_agent']); } + // Is caching in effect? Turn it off + $db_cache = $this->CI->db->cache_on; + $this->CI->db->cache_off(); + $query = $this->CI->db->limit(1)->get($this->sess_table_name); + // Was caching in effect? + if ($db_cache) + { + // Turn it back on + $this->CI->db->cache_on(); + } + // No result? Kill it! if ($query->num_rows() === 0) { @@ -357,7 +471,7 @@ class CI_Session { return FALSE; } - // Is there custom data? If so, add it to the main session array + // Is there custom data? If so, add it to the main session array $row = $query->row(); if ( ! empty($row->user_data)) { @@ -365,424 +479,164 @@ class CI_Session { if (is_array($custom_data)) { - foreach ($custom_data as $key => $val) - { - $session[$key] = $val; - } + $session = $session + $custom_data; } } } // Session is valid! $this->userdata = $session; - unset($session); - return TRUE; } - // -------------------------------------------------------------------- - - /** - * Write the session data - * - * @return void - */ - public function sess_write() - { - // Are we saving custom data to the DB? If not, all we do is update the cookie - if ($this->sess_use_database === FALSE) - { - $this->_set_cookie(); - return; - } - - // set the custom userdata, the session data we will set in a second - $custom_userdata = $this->userdata; - $cookie_userdata = array(); - - // Before continuing, we need to determine if there is any custom data to deal with. - // Let's determine this by removing the default indexes to see if there's anything left in the array - // and set the session data while we're at it - foreach (array('session_id','ip_address','user_agent','last_activity') as $val) - { - unset($custom_userdata[$val]); - $cookie_userdata[$val] = $this->userdata[$val]; - } - - // Did we find any custom data? If not, we turn the empty array into a string - // since there's no reason to serialize and store an empty array in the DB - if (count($custom_userdata) === 0) - { - $custom_userdata = ''; - } - else - { - // Serialize the custom data array so we can store it - $custom_userdata = $this->_serialize($custom_userdata); - } - - // Run the update query - $this->CI->db->where('session_id', $this->userdata['session_id']); - $this->CI->db->update($this->sess_table_name, array('last_activity' => $this->userdata['last_activity'], 'user_data' => $custom_userdata)); - - // Write the cookie. Notice that we manually pass the cookie data array to the - // _set_cookie() function. Normally that function will store $this->userdata, but - // in this case that array contains custom data, which we do not want in the cookie. - $this->_set_cookie($cookie_userdata); - } - - // -------------------------------------------------------------------- + // ------------------------------------------------------------------------ /** * Create a new session * * @return void */ - public function sess_create() + protected function _sess_create() { - $sessid = ''; - do - { - $sessid .= mt_rand(0, mt_getrandmax()); - } - while (strlen($sessid) < 32); - - // To make the session ID even more secure we'll combine it with the user's IP - $sessid .= $this->CI->input->ip_address(); - + // Initialize userdata $this->userdata = array( - 'session_id' => md5(uniqid($sessid, TRUE)), - 'ip_address' => $this->CI->input->ip_address(), - 'user_agent' => substr($this->CI->input->user_agent(), 0, 120), - 'last_activity' => $this->now, - 'user_data' => '' - ); - - // Save the data to the DB if needed + 'session_id' => $this->_make_sess_id(), + 'ip_address' => $this->CI->input->ip_address(), + 'user_agent' => substr($this->CI->input->user_agent(), 0, 120), + 'last_activity' => $this->now, + ); + + // Check for database if ($this->sess_use_database === TRUE) { - $this->CI->db->query($this->CI->db->insert_string($this->sess_table_name, $this->userdata)); + // Add empty user_data field and save the data to the DB + $this->CI->db->set('user_data', '')->insert($this->sess_table_name, $this->userdata); } // Write the cookie $this->_set_cookie(); } - // -------------------------------------------------------------------- + // ------------------------------------------------------------------------ /** * Update an existing session * + * @param bool Force update flag (default: false) * @return void */ - public function sess_update() + protected function _sess_update($force = FALSE) { - // We only update the session every five minutes by default - if (($this->userdata['last_activity'] + $this->sess_time_to_update) >= $this->now) + // We only update the session every five minutes by default (unless forced) + if ( ! $force && ($this->userdata['last_activity'] + $this->sess_time_to_update) >= $this->now) { return; } - // _set_cookie() will handle this for us if we aren't using database sessions - // by pushing all userdata to the cookie. - $cookie_data = NULL; - - /* Changing the session ID during an AJAX call causes problems, - * so we'll only update our last_activity - */ - if ($this->CI->input->is_ajax_request()) - { - $this->userdata['last_activity'] = $this->now; - - // Update the session ID and last_activity field in the DB if needed - if ($this->sess_use_database === TRUE) - { - // set cookie explicitly to only have our session data - $cookie_data = array(); - foreach (array('session_id','ip_address','user_agent','last_activity') as $val) - { - $cookie_data[$val] = $this->userdata[$val]; - } - - $this->CI->db->query($this->CI->db->update_string($this->sess_table_name, - array('last_activity' => $this->userdata['last_activity']), - array('session_id' => $this->userdata['session_id']))); - } - - return $this->_set_cookie($cookie_data); - } + // Update last activity to now + $this->userdata['last_activity'] = $this->now; - // Save the old session id so we know which record to - // update in the database if we need it + // Save the old session id so we know which DB record to update $old_sessid = $this->userdata['session_id']; - $new_sessid = ''; - do + + // Changing the session ID during an AJAX call causes problems + if ( ! $this->CI->input->is_ajax_request()) { - $new_sessid .= mt_rand(0, mt_getrandmax()); + // Get new id + $this->userdata['session_id'] = $this->_make_sess_id(); } - while (strlen($new_sessid) < 32); - // To make the session ID even more secure we'll combine it with the user's IP - $new_sessid .= $this->CI->input->ip_address(); - - // Turn it into a hash and update the session data array - $this->userdata['session_id'] = $new_sessid = md5(uniqid($new_sessid, TRUE)); - $this->userdata['last_activity'] = $this->now; - - // Update the session ID and last_activity field in the DB if needed + // Check for database if ($this->sess_use_database === TRUE) { - // set cookie explicitly to only have our session data - $cookie_data = array(); - foreach (array('session_id','ip_address','user_agent','last_activity') as $val) - { - $cookie_data[$val] = $this->userdata[$val]; - } - - $this->CI->db->query($this->CI->db->update_string($this->sess_table_name, array('last_activity' => $this->now, 'session_id' => $new_sessid), array('session_id' => $old_sessid))); + // Update the session ID and last_activity field in the DB + $this->CI->db->update($this->sess_table_name, array( + 'last_activity' => $this->now, + 'session_id' => $this->userdata['session_id'] + ), array('session_id' => $old_sessid)); } // Write the cookie - $this->_set_cookie($cookie_data); + $this->_set_cookie(); } - // -------------------------------------------------------------------- + // ------------------------------------------------------------------------ /** - * Destroy the current session + * Update database with current data + * + * This gets called from the shutdown function and also + * registered with PHP to run at the end of the request + * so it's guaranteed to update even when a fatal error + * occurs. The first call makes the update and clears the + * dirty flag so it won't happen twice. * * @return void */ - public function sess_destroy() + public function _update_db() { - // Kill the session DB row - if ($this->sess_use_database === TRUE && isset($this->userdata['session_id'])) - { - $this->CI->db->where('session_id', $this->userdata['session_id']); - $this->CI->db->delete($this->sess_table_name); - } - - // Kill the cookie - setcookie( - $this->sess_cookie_name, - addslashes(serialize(array())), - ($this->now - 31500000), - $this->cookie_path, - $this->cookie_domain, - 0 + // Check for database and dirty flag and unsaved + if ($this->sess_use_database === TRUE && $this->data_dirty === TRUE) + { + // Set up activity and data fields to be set + // If we don't find custom data, user_data will remain an empty string + $set = array( + 'last_activity' => $this->userdata['last_activity'], + 'user_data' => '' ); - // Kill session data - $this->userdata = array(); - } - - // -------------------------------------------------------------------- - - /** - * Fetch a specific item from the session array - * - * @param string - * @return string - */ - public function userdata($item) - { - return isset($this->userdata[$item]) ? $this->userdata[$item] : NULL; - } - - // -------------------------------------------------------------------- - - /** - * Fetch all session data - * - * @return array - */ - public function all_userdata() - { - return $this->userdata; - } - - // -------------------------------------------------------------------------- - - /** - * Fetch all flashdata - * - * @return array - */ - public function all_flashdata() - { - $out = array(); - - // loop through all userdata - foreach ($this->all_userdata() as $key => $val) - { - // if it contains flashdata, add it - if (strpos($key, 'flash:old:') !== FALSE) - { - $out[$key] = $val; - } - } - return $out; - } - - // -------------------------------------------------------------------- + // Get the custom userdata, leaving out the defaults + // (which get stored in the cookie) + $userdata = array_diff_key($this->userdata, $this->defaults); - /** - * Add or change data in the "userdata" array - * - * @param mixed - * @param string - * @return void - */ - public function set_userdata($newdata = array(), $newval = '') - { - if (is_string($newdata)) - { - $newdata = array($newdata => $newval); - } - - if (count($newdata) > 0) - { - foreach ($newdata as $key => $val) + // Did we find any custom data? + if ( ! empty($userdata)) { - $this->userdata[$key] = $val; + // Serialize the custom data array so we can store it + $set['user_data'] = $this->_serialize($userdata); } - } - $this->sess_write(); - } + // Run the update query + // Any time we change the session id, it gets updated immediately, + // so our where clause below is always safe + $this->CI->db->update($this->sess_table_name, $set, array('session_id' => $this->userdata['session_id'])); - // -------------------------------------------------------------------- + // Clear dirty flag to prevent double updates + $this->data_dirty = FALSE; - /** - * Delete a session variable from the "userdata" array - * - * @param array - * @return void - */ - public function unset_userdata($newdata = array()) - { - if (is_string($newdata)) - { - $newdata = array($newdata => ''); + log_message('debug', 'CI_Session Data Saved To DB'); } - - if (count($newdata) > 0) - { - foreach ($newdata as $key => $val) - { - unset($this->userdata[$key]); - } - } - - $this->sess_write(); } // ------------------------------------------------------------------------ /** - * Add or change flashdata, only available - * until the next request + * Generate a new session id * - * @param mixed - * @param string - * @return void + * @return string Hashed session id */ - public function set_flashdata($newdata = array(), $newval = '') + protected function _make_sess_id() { - if (is_string($newdata)) - { - $newdata = array($newdata => $newval); - } - - if (count($newdata) > 0) + $new_sessid = ''; + do { - foreach ($newdata as $key => $val) - { - $this->set_userdata($this->flashdata_key.':new:'.$key, $val); - } + $new_sessid .= mt_rand(0, mt_getrandmax()); } - } - - // ------------------------------------------------------------------------ - - /** - * Keeps existing flashdata available to next request. - * - * @param string - * @return void - */ - public function keep_flashdata($key) - { - // 'old' flashdata gets removed. Here we mark all - // flashdata as 'new' to preserve it from _flashdata_sweep() - // Note the function will return NULL if the $key - // provided cannot be found - $value = $this->userdata($this->flashdata_key.':old:'.$key); - - $this->set_userdata($this->flashdata_key.':new:'.$key, $value); - } - - // ------------------------------------------------------------------------ - - /** - * Fetch a specific flashdata item from the session array - * - * @param string - * @return string - */ - public function flashdata($key) - { - return $this->userdata($this->flashdata_key.':old:'.$key); - } + while (strlen($new_sessid) < 32); - // ------------------------------------------------------------------------ + // To make the session ID even more secure we'll combine it with the user's IP + $new_sessid .= $this->CI->input->ip_address(); - /** - * Identifies flashdata as 'old' for removal - * when _flashdata_sweep() runs. - * - * @return void - */ - protected function _flashdata_mark() - { - $userdata = $this->all_userdata(); - foreach ($userdata as $name => $value) - { - $parts = explode(':new:', $name); - if (is_array($parts) && count($parts) === 2) - { - $this->set_userdata($this->flashdata_key.':old:'.$parts[1], $value); - $this->unset_userdata($name); - } - } + // Turn it into a hash and return + return md5(uniqid($new_sessid, TRUE)); } // ------------------------------------------------------------------------ /** - * Removes all flashdata marked as 'old' - * - * @return void - */ - protected function _flashdata_sweep() - { - $userdata = $this->all_userdata(); - foreach ($userdata as $key => $value) - { - if (strpos($key, ':old:')) - { - $this->unset_userdata($key); - } - } - - } - - // -------------------------------------------------------------------- - - /** * Get the "now" time * - * @return string + * @return int Time */ protected function _get_time() { @@ -797,49 +651,57 @@ class CI_Session { return mktime($hour, $minute, $second, $month, $day, $year); } - // -------------------------------------------------------------------- + // ------------------------------------------------------------------------ /** * Write the session cookie * - * @param mixed * @return void */ - protected function _set_cookie($cookie_data = NULL) + protected function _set_cookie() { - if (is_null($cookie_data)) - { - $cookie_data = $this->userdata; - } + // Get userdata (only defaults if database) + $cookie_data = ($this->sess_use_database === TRUE) + ? array_intersect_key($this->userdata, $this->defaults) + : $this->userdata; // Serialize the userdata for the cookie $cookie_data = $this->_serialize($cookie_data); - if ($this->sess_encrypt_cookie === TRUE) - { - $cookie_data = $this->CI->encrypt->encode($cookie_data); - } - else - { + $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 = $cookie_data.md5($cookie_data.$this->encryption_key); - } + : $cookie_data.md5($cookie_data.$this->encryption_key); $expire = ($this->sess_expire_on_close === TRUE) ? 0 : $this->sess_expiration + time(); // Set the cookie - setcookie( - $this->sess_cookie_name, - $cookie_data, - $expire, - $this->cookie_path, - $this->cookie_domain, - $this->cookie_secure, - $this->cookie_httponly - ); + $this->_setcookie($this->sess_cookie_name, $cookie_data, $expire, $this->cookie_path, $this->cookie_domain, + $this->cookie_secure, $this->cookie_httponly); } - // -------------------------------------------------------------------- + // ------------------------------------------------------------------------ + + /** + * Set a cookie with the system + * + * This abstraction of the setcookie call allows overriding for unit testing + * + * @param string Cookie name + * @param string Cookie value + * @param int Expiration time + * @param string Cookie path + * @param string Cookie domain + * @param bool Secure connection flag + * @param bool HTTP protocol only flag + * @return void + */ + protected function _setcookie($name, $value = '', $expire = 0, $path = '', $domain = '', $secure = FALSE, $httponly = FALSE) + { + setcookie($name, $value, $expire, $path, $domain, $secure, $httponly); + } + + // ------------------------------------------------------------------------ /** * Serialize an array @@ -847,8 +709,8 @@ class CI_Session { * This function first converts any slashes found in the array to a temporary * marker, so when it gets unserialized the slashes will be preserved * - * @param array - * @return string + * @param mixed Data to serialize + * @return string Serialized data */ protected function _serialize($data) { @@ -860,16 +722,19 @@ class CI_Session { { $data = str_replace('\\', '{{slash}}', $data); } + return serialize($data); } + // ------------------------------------------------------------------------ + /** * Escape slashes * * This function converts any slashes found into a temporary marker * - * @param string - * @param string + * @param string Value + * @param string Key * @return void */ protected function _escape_slashes(&$val, $key) @@ -880,7 +745,7 @@ class CI_Session { } } - // -------------------------------------------------------------------- + // ------------------------------------------------------------------------ /** * Unserialize @@ -888,8 +753,8 @@ class CI_Session { * This function unserializes a data string, then converts any * temporary slash markers back to actual slashes * - * @param array - * @return string + * @param mixed Data to unserialize + * @return mixed Unserialized data */ protected function _unserialize($data) { @@ -904,15 +769,15 @@ class CI_Session { return is_string($data) ? str_replace('{{slash}}', '\\', $data) : $data; } - // -------------------------------------------------------------------- + // ------------------------------------------------------------------------ /** * Unescape slashes * * This function converts any slash markers back into actual slashes * - * @param string - * @param string + * @param string Value + * @param string Key * @return void */ protected function _unescape_slashes(&$val, $key) @@ -923,7 +788,7 @@ class CI_Session { } } - // -------------------------------------------------------------------- + // ------------------------------------------------------------------------ /** * Garbage collection @@ -940,13 +805,14 @@ class CI_Session { return; } + $probability = ini_get('session.gc_probability'); + $divisor = ini_get('session.gc_divisor'); + srand(time()); - if ((rand() % 100) < $this->gc_probability) + if ((mt_rand(0, $divisor) / $divisor) < $probability) { $expire = $this->now - $this->sess_expiration; - - $this->CI->db->where('last_activity < '.$expire); - $this->CI->db->delete($this->sess_table_name); + $this->CI->db->delete($this->sess_table_name, 'last_activity < '.$expire); log_message('debug', 'Session garbage collection performed.'); } @@ -954,5 +820,5 @@ class CI_Session { } -/* End of file Session.php */ -/* Location: ./system/libraries/Session.php */
\ No newline at end of file +/* End of file Session_cookie.php */ +/* Location: ./system/libraries/Session/drivers/Session_cookie.php */
\ No newline at end of file diff --git a/system/libraries/Session/drivers/Session_native.php b/system/libraries/Session/drivers/Session_native.php new file mode 100755 index 000000000..c97e15356 --- /dev/null +++ b/system/libraries/Session/drivers/Session_native.php @@ -0,0 +1,232 @@ +<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); +/** + * CodeIgniter + * + * An open source application development framework for PHP 5.2.4 or newer + * + * NOTICE OF LICENSE + * + * Licensed under the Open Software License version 3.0 + * + * This source file is subject to the Open Software License (OSL 3.0) that is + * bundled with this package in the files license.txt / license.rst. It is + * also available through the world wide web at this URL: + * http://opensource.org/licenses/OSL-3.0 + * If you did not receive a copy of the license and are unable to obtain it + * through the world wide web, please send an email to + * licensing@ellislab.com so we can send you a copy immediately. + * + * @package CodeIgniter + * @author EllisLab Dev Team + * @copyright Copyright (c) 2008 - 2012, EllisLab, Inc. (http://ellislab.com/) + * @license http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) + * @link http://codeigniter.com + * @since Version 1.0 + * @filesource + */ + +/** + * Native PHP session management driver + * + * This is the driver that uses the native PHP $_SESSION array through the Session driver library. + * + * @package CodeIgniter + * @subpackage Libraries + * @category Sessions + * @author EllisLab Dev Team + */ +class CI_Session_native extends CI_Session_driver { + + /** + * Initialize session driver object + * + * @return void + */ + protected function initialize() + { + // Get config parameters + $config = array(); + $CI =& get_instance(); + $prefs = array( + 'sess_cookie_name', + 'sess_expire_on_close', + 'sess_expiration', + 'sess_match_ip', + 'sess_match_useragent', + 'sess_time_to_update', + 'cookie_prefix', + 'cookie_path', + 'cookie_domain' + ); + + foreach ($prefs as $key) + { + $config[$key] = isset($this->_parent->params[$key]) + ? $this->_parent->params[$key] + : $CI->config->item($key); + } + + // Set session name, if specified + if ($config['sess_cookie_name']) + { + // Differentiate name from cookie driver with '_id' suffix + $name = $config['sess_cookie_name'].'_id'; + if ($config['cookie_prefix']) + { + // Prepend cookie prefix + $name = $config['cookie_prefix'].$name; + } + session_name($name); + } + + // Set expiration, path, and domain + $expire = 7200; + $path = '/'; + $domain = ''; + if ($config['sess_expiration'] !== FALSE) + { + // Default to 2 years if expiration is "0" + $expire = ($config['sess_expiration'] == 0) ? (60*60*24*365*2) : $config['sess_expiration']; + } + + if ($config['cookie_path']) + { + // Use specified path + $path = $config['cookie_path']; + } + + if ($config['cookie_domain']) + { + // Use specified domain + $domain = $config['cookie_domain']; + } + session_set_cookie_params($config['sess_expire_on_close'] ? 0 : $expire, $path, $domain); + + // Start session + session_start(); + + // Check session expiration, ip, and agent + $now = time(); + $destroy = FALSE; + if (isset($_SESSION['last_activity']) && ($_SESSION['last_activity'] + $expire) < $now) + { + // Expired - destroy + $destroy = TRUE; + } + elseif ($config['sess_match_ip'] === TRUE && isset($_SESSION['ip_address']) + && $_SESSION['ip_address'] !== $CI->input->ip_address()) + { + // IP doesn't match - destroy + $destroy = TRUE; + } + elseif ($config['sess_match_useragent'] === TRUE && isset($_SESSION['user_agent']) + && $_SESSION['user_agent'] !== trim(substr($CI->input->user_agent(), 0, 50))) + { + // Agent doesn't match - destroy + $destroy = TRUE; + } + + // Destroy expired or invalid session + if ($destroy) + { + // Clear old session and start new + $this->sess_destroy(); + session_start(); + } + + // Check for update time + 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); + } + + // Set activity time + $_SESSION['last_activity'] = $now; + + // Set matching values as required + if ($config['sess_match_ip'] === TRUE && ! isset($_SESSION['ip_address'])) + { + // Store user IP address + $_SESSION['ip_address'] = $CI->input->ip_address(); + } + + if ($config['sess_match_useragent'] === TRUE && ! isset($_SESSION['user_agent'])) + { + // Store user agent string + $_SESSION['user_agent'] = trim(substr($CI->input->user_agent(), 0, 50)); + } + + // Make session ID available + $_SESSION['session_id'] = session_id(); + } + + // ------------------------------------------------------------------------ + + /** + * Save the session data + * + * @return void + */ + public function sess_save() + { + // Nothing to do - changes to $_SESSION are automatically saved + } + + // ------------------------------------------------------------------------ + + /** + * Destroy the current session + * + * @return void + */ + public function sess_destroy() + { + // Cleanup session + $_SESSION = array(); + $name = session_name(); + if (isset($_COOKIE[$name])) + { + // Clear session cookie + $params = session_get_cookie_params(); + setcookie($name, '', time() - 42000, $params['path'], $params['domain']); + unset($_COOKIE[$name]); + } + session_destroy(); + } + + // ------------------------------------------------------------------------ + + /** + * Regenerate the current session + * + * Regenerate the session id + * + * @param bool Destroy session data flag (default: FALSE) + * @return void + */ + public function sess_regenerate($destroy = FALSE) + { + // Just regenerate id, passing destroy flag + session_regenerate_id($destroy); + $_SESSION['session_id'] = session_id(); + } + + // ------------------------------------------------------------------------ + + /** + * Get a reference to user data array + * + * @return array Reference to userdata + */ + public function &get_userdata() + { + // Just return reference to $_SESSION + return $_SESSION; + } + +} + +/* End of file Session_native.php */ +/* Location: ./system/libraries/Session/drivers/Session_native.php */
\ No newline at end of file diff --git a/system/libraries/Unit_test.php b/system/libraries/Unit_test.php index 70ad8dc41..c2c01758e 100644 --- a/system/libraries/Unit_test.php +++ b/system/libraries/Unit_test.php @@ -240,6 +240,11 @@ class CI_Unit_test { { foreach ($val as $k => $v) { + if ( ! in_array($k, $this->_test_items_visible)) + { + continue; + } + if (FALSE !== ($line = $CI->lang->line(strtolower('ut_'.$v)))) { $v = $line; diff --git a/system/libraries/Xmlrpc.php b/system/libraries/Xmlrpc.php index eac4ac118..dc5d27f8c 100644..100755 --- a/system/libraries/Xmlrpc.php +++ b/system/libraries/Xmlrpc.php @@ -174,7 +174,7 @@ class CI_Xmlrpc { * @param int port * @return void */ - public function server($url, $port = 80) + public function server($url, $port = 80, $proxy = FALSE, $proxy_port = 8080) { if (strpos($url, 'http') !== 0) { @@ -190,7 +190,7 @@ class CI_Xmlrpc { $path .= '?'.$parts['query']; } - $this->client = new XML_RPC_Client($path, $parts['host'], $port); + $this->client = new XML_RPC_Client($path, $parts['host'], $port, $proxy, $proxy_port); } // -------------------------------------------------------------------- @@ -385,6 +385,8 @@ class XML_RPC_Client extends CI_Xmlrpc public $path = ''; public $server = ''; public $port = 80; + public $proxy = FALSE; + public $proxy_port = 8080; public $errno = ''; public $errstring = ''; public $timeout = 5; @@ -398,13 +400,15 @@ class XML_RPC_Client extends CI_Xmlrpc * @param int * @return void */ - public function __construct($path, $server, $port = 80) + public function __construct($path, $server, $port = 80, $proxy = FALSE, $proxy_port = 8080) { parent::__construct(); $this->port = $port; $this->server = $server; $this->path = $path; + $this->proxy = $proxy; + $this->proxy_port = $proxy_port; } // -------------------------------------------------------------------- @@ -436,7 +440,18 @@ class XML_RPC_Client extends CI_Xmlrpc */ public function sendPayload($msg) { - $fp = @fsockopen($this->server, $this->port,$this->errno, $this->errstring, $this->timeout); + if ($this->proxy === FALSE) + { + $server = $this->server; + $port = $this->port; + } + else + { + $server = $this->proxy; + $port = $this->proxy_port; + } + + $fp = @fsockopen($server, $port, $this->errno, $this->errstring, $this->timeout); if ( ! is_resource($fp)) { @@ -1302,15 +1317,15 @@ class XML_RPC_Values extends CI_Xmlrpc { $type = $type === '' ? 'string' : $type; - if ($this->xmlrpcTypes[$type] === 1) + if ($this->xmlrpcTypes[$type] == 1) { $this->addScalar($val,$type); } - elseif ($this->xmlrpcTypes[$type] === 2) + elseif ($this->xmlrpcTypes[$type] == 2) { $this->addArray($val); } - elseif ($this->xmlrpcTypes[$type] === 3) + elseif ($this->xmlrpcTypes[$type] == 3) { $this->addStruct($val); } @@ -1336,7 +1351,7 @@ class XML_RPC_Values extends CI_Xmlrpc return 0; } - if ($typeof !== 1) + if ($typeof != 1) { echo '<strong>XML_RPC_Values</strong>: not a scalar type (${typeof})<br />'; return 0; @@ -1344,7 +1359,7 @@ class XML_RPC_Values extends CI_Xmlrpc if ($type === $this->xmlrpcBoolean) { - $val = (int) (strcasecmp($val,'true') === 0 OR $val === 1 OR ($val === TRUE && strcasecmp($val, 'false'))); + $val = (int) (strcasecmp($val, 'true') === 0 OR $val === 1 OR ($val === TRUE && strcasecmp($val, 'false'))); } if ($this->mytype === 2) diff --git a/system/libraries/Xmlrpcs.php b/system/libraries/Xmlrpcs.php index e81f2ca9a..5d01d374d 100644 --- a/system/libraries/Xmlrpcs.php +++ b/system/libraries/Xmlrpcs.php @@ -230,7 +230,7 @@ class CI_Xmlrpcs extends CI_Xmlrpc ); xml_set_object($parser, $parser_object); - xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true); + xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, TRUE); xml_set_element_handler($parser, 'open_tag', 'closing_tag'); xml_set_character_data_handler($parser, 'character_data'); //xml_set_default_handler($parser, 'default_handler'); |