From 4d970783e3ab3063b19f04164249c4e77e6080c7 Mon Sep 17 00:00:00 2001 From: Phil Sturgeon Date: Sat, 26 Mar 2011 14:15:13 +0000 Subject: [PATCH 001/651] Added $arguments to the remap so people can use them in their controller methods. Use at your own risk as it breaks key/value pairing, but whatever, people seem to want it. --- application/libraries/REST_Controller.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index ca74ae1c..04e6910a 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -144,7 +144,7 @@ public function __construct() * Requests are not made to methods directly The request will be for an "object". * this simply maps the object and method to the correct Controller method. */ - public function _remap($object_called) + public function _remap($object_called, $arguments) { $pattern = '/^(.*)\.(' . implode('|', array_keys($this->_supported_formats)) . ')$/'; if (preg_match($pattern, $object_called, $matches)) @@ -204,7 +204,7 @@ public function _remap($object_called) } // And...... GO! - $this->$controller_method(); + call_user_func_array(array($this, $controller_method), $arguments); } /* From 5f2156e52e4d9ea490b525a8a92f3a034b3b8fbd Mon Sep 17 00:00:00 2001 From: Phil Sturgeon Date: Tue, 5 Apr 2011 15:26:23 +0100 Subject: [PATCH 002/651] Fixed PUT argumewnts. Once again just sending a body query string works. http://d.pr/cY0b --- application/controllers/api/example.php | 6 ++++++ application/libraries/REST_Controller.php | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/application/controllers/api/example.php b/application/controllers/api/example.php index ae98ae5c..c5caefdb 100644 --- a/application/controllers/api/example.php +++ b/application/controllers/api/example.php @@ -86,4 +86,10 @@ public function send_post() { var_dump($this->request->body); } + + + public function send_put() + { + var_dump($this->put('foo')); + } } \ No newline at end of file diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 04e6910a..74cf6290 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -77,7 +77,7 @@ public function __construct() // If no file type is provided, this is probably just arguments else { - parse_str($this->request->body, $this->_put_args); + parse_str(file_get_contents('php://input'), $this->_put_args); } break; From 5284df58f1e75ad453168a44661777c66c64520d Mon Sep 17 00:00:00 2001 From: Phil Sturgeon Date: Thu, 14 Apr 2011 00:27:22 +0100 Subject: [PATCH 003/651] Added Format::to_php(). --- application/libraries/Format.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/application/libraries/Format.php b/application/libraries/Format.php index 9c0918ef..1ad7686d 100644 --- a/application/libraries/Format.php +++ b/application/libraries/Format.php @@ -209,12 +209,17 @@ public function to_serialized() { return serialize($this->_data); } - + + // Output as a string representing the PHP structure + public function to_php() + { + return var_export($this->_data, TRUE); + } // Format XML for output protected function _from_xml($string) { - return (array) simplexml_load_string($string, 'SimpleXMLElement', LIBXML_NOCDATA); + return $string ? (array) simplexml_load_string($string, 'SimpleXMLElement', LIBXML_NOCDATA) : array(); } // Format HTML for output From 21ec0ff9895ce26de35f44495fc424ce2ab760ff Mon Sep 17 00:00:00 2001 From: Phil Sturgeon Date: Thu, 14 Apr 2011 01:01:44 +0100 Subject: [PATCH 004/651] Instead of just seeing item, item, item, the singular version of the basenode will be used if possible. [Example](http://d.pr/RS46) --- README.md | 5 ++++ application/controllers/api/example.php | 4 +-- application/libraries/Format.php | 33 +++++++++++++------------ 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 21209c8a..04a019ee 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,11 @@ but you can read my NetTuts article which covers it's usage along with the REST ## Change Log +### 2.5 + +* Instead of just seeing item, item, item, the singular version of the basenode will be used if possible. [Example](http://d.pr/RS46) +* Re-factored to use the Format library, soon to be merged with CodeIgniter. + ### 2.4 * Added support for UTF-8 characters in XML. diff --git a/application/controllers/api/example.php b/application/controllers/api/example.php index c5caefdb..edfd1e32 100644 --- a/application/controllers/api/example.php +++ b/application/controllers/api/example.php @@ -29,7 +29,7 @@ function user_get() $users = array( 1 => array('id' => 1, 'name' => 'Some Guy', 'email' => 'example1@example.com', 'fact' => 'Loves swimming'), 2 => array('id' => 2, 'name' => 'Person Face', 'email' => 'example2@example.com', 'fact' => 'Has a huge face'), - 3 => array('id' => 3, 'name' => 'Scotty', 'email' => 'example3@example.com', 'fact' => 'Is a Scott!'), + 3 => array('id' => 3, 'name' => 'Scotty', 'email' => 'example3@example.com', 'fact' => 'Is a Scott!', array('hobbies' => array('fartings', 'bikes'))), ); $user = @$users[$this->get('id')]; @@ -67,7 +67,7 @@ function users_get() $users = array( array('id' => 1, 'name' => 'Some Guy', 'email' => 'example1@example.com'), array('id' => 2, 'name' => 'Person Face', 'email' => 'example2@example.com'), - array('id' => 3, 'name' => 'Scotty', 'email' => 'example3@example.com'), + 3 => array('id' => 3, 'name' => 'Scotty', 'email' => 'example3@example.com', 'fact' => array('hobbies' => array('fartings', 'bikes'))), ); if($users) diff --git a/application/libraries/Format.php b/application/libraries/Format.php index 1ad7686d..79d3c4e5 100644 --- a/application/libraries/Format.php +++ b/application/libraries/Format.php @@ -36,6 +36,8 @@ public function factory($data, $from_type = null) */ public function __construct($data = null, $from_type = null) { + get_instance()->load->helper('inflector'); + // If the provided data is already formatted we should probably convert it to an array if ($from_type !== null) { @@ -110,27 +112,26 @@ public function to_xml($data = null, $structure = null, $basenode = 'xml') { // no numeric keys in our xml please! if (is_numeric($key)) - { - // make string key... - //$key = "item_". (string) $key; - $key = "item"; - } + { + // make string key... + $key = (singular($basenode) != $basenode) ? singular($basenode) : 'item'; + } // replace anything not alpha numeric $key = preg_replace('/[^a-z_\-0-9]/i', '', $key); - // if there is another array found recrusively call this function - if (is_array($value) OR is_object($value)) - { - $node = $structure->addChild($key); - $this->to_xml($value, $node, $basenode); - } - else - { - // Actual boolean values need to be converted to numbers - is_bool($value) AND $value = (int) $value; + // if there is another array found recrusively call this function + if (is_array($value) || is_object($value)) + { + $node = $structure->addChild($key); + + // recrusive call. + $this->to_xml($value, $node, $key); + } - // add single node. + else + { + // add single node. $value = htmlspecialchars(html_entity_decode($value, ENT_QUOTES, 'UTF-8'), ENT_QUOTES, "UTF-8"); $structure->addChild($key, $value); From b25ef86cef40c57bfa06b5d85598f90be79f4403 Mon Sep 17 00:00:00 2001 From: Phil Sturgeon Date: Sat, 30 Apr 2011 14:21:35 +0100 Subject: [PATCH 005/651] MIME's with character encodings on the end will now work. --- application/libraries/REST_Controller.php | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 74cf6290..3ff6a3cf 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -269,7 +269,12 @@ private function _detect_input_format() // Check all formats against the HTTP_ACCEPT header foreach ($this->_supported_formats as $format => $mime) { - if ($this->input->server('CONTENT_TYPE') == $mime) + if (strpos($match = $this->input->server('CONTENT_TYPE'), ';')) + { + $match = current(explode(';', $match)); + } + + if ($match == $mime) { return $format; } @@ -405,8 +410,9 @@ private function _detect_api_key() } $this->rest->key = $row->key; - $this->rest->level = $row->level; - $this->rest->ignore_limits = $row->ignore_limits; + + isset($row->level) AND $this->rest->level = $row->level; + isset($row->ignore_limits) AND $this->rest->ignore_limits = $row->ignore_limits; return TRUE; } From 001852a7c71e06a2f22d6ef1551d94dd1c265195 Mon Sep 17 00:00:00 2001 From: Phil Sturgeon Date: Tue, 3 May 2011 16:52:37 +0100 Subject: [PATCH 006/651] Skip file extension checking on arrayed GET values. --- application/libraries/REST_Controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 3ff6a3cf..019f3a47 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -300,7 +300,7 @@ private function _detect_output_format() } // Check if a file extension is used - elseif ($this->_get_args AND preg_match($pattern, end($this->_get_args), $matches)) + elseif ($this->_get_args AND ! is_array(end($this->_get_args)) AND preg_match($pattern, end($this->_get_args), $matches)) { // The key of the last argument $last_key = end(array_keys($this->_get_args)); From 7eca8df4e992cf6a3f6a7a88850baa10df1ec239 Mon Sep 17 00:00:00 2001 From: absfarah Date: Sun, 22 May 2011 13:52:33 -0700 Subject: [PATCH 007/651] Need to reference the api key variable name set in the rest config file and use that for the if condition for comparison. --- application/libraries/REST_Controller.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 019f3a47..2e451cda 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -394,15 +394,19 @@ private function _detect_method() private function _detect_api_key() { + + // Get the api key name variable set in the rest config file + $api_key_variable = config_item('rest_key_name'); + // Work out the name of the SERVER entry based on config - $key_name = 'HTTP_' . strtoupper(str_replace('-', '_', config_item('rest_key_name'))); + $key_name = 'HTTP_' . strtoupper(str_replace('-', '_', $api_key_variable)); $this->rest->key = NULL; $this->rest->level = NULL; $this->rest->ignore_limits = FALSE; // Find the key from server or arguments - if ($key = isset($this->_args['API-Key']) ? $this->_args['API-Key'] : $this->input->server($key_name)) + if ($key = isset($this->_args[$api_key_variable]) ? $this->_args[$api_key_variable] : $this->input->server($key_name)) { if ( ! $row = $this->rest->db->where('key', $key)->get(config_item('rest_keys_table'))->row()) { From 628ab834b29849845ca7c24e1f192c76eb7ee474 Mon Sep 17 00:00:00 2001 From: brickattack Date: Thu, 26 May 2011 13:18:25 -0400 Subject: [PATCH 008/651] Fixed some errors to get keys example working --- application/controllers/api/key.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/application/controllers/api/key.php b/application/controllers/api/key.php index 1b03a6ae..16266bfb 100644 --- a/application/controllers/api/key.php +++ b/application/controllers/api/key.php @@ -198,7 +198,7 @@ private function _generate_key() do { - $salt = dohash(time().mt_rand()); + $salt = do_hash(time().mt_rand()); $new_key = substr($salt, 0, config_item('rest_key_length')); } @@ -214,14 +214,14 @@ private function _generate_key() private function _get_key($key) { - return $this->rest->db->where('key', $key)->get(config_item('rest_keys_table'))->row(); + return $this->db->where('key', $key)->get(config_item('rest_keys_table'))->row(); } // -------------------------------------------------------------------- private function _key_exists($key) { - return $this->rest->db->where('key', $key)->count_all_results(config_item('rest_keys_table')) > 0; + return $this->db->where('key', $key)->count_all_results(config_item('rest_keys_table')) > 0; } // -------------------------------------------------------------------- @@ -232,20 +232,20 @@ private function _insert_key($key, $data) $data['key'] = $key; $data['date_created'] = function_exists('now') ? now() : time(); - return $this->rest->db->set($data)->insert(config_item('rest_keys_table')); + return $this->db->set($data)->insert(config_item('rest_keys_table')); } // -------------------------------------------------------------------- private function _update_key($key, $data) { - return $this->rest->db->where('key', $key)->update(config_item('rest_keys_table'), $data); + return $this->db->where('key', $key)->update(config_item('rest_keys_table'), $data); } // -------------------------------------------------------------------- private function _delete_key($key) { - return $this->rest->db->where('key', $key)->delete(config_item('rest_keys_table')); + return $this->db->where('key', $key)->delete(config_item('rest_keys_table')); } } From 4b7bf25a1bfadcc6b8c03a29875729560e5c4406 Mon Sep 17 00:00:00 2001 From: rhbecker Date: Tue, 28 Jun 2011 08:11:56 -0700 Subject: [PATCH 009/651] Changed all visibility 'private' to 'protected'. --- application/libraries/REST_Controller.php | 44 +++++++++++------------ 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 019f3a47..7e0d9dce 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -7,15 +7,15 @@ class REST_Controller extends CI_Controller { protected $request = NULL; // Stores accept, language, body, headers, etc protected $response = NULL; // What is gonna happen in output? protected $rest = NULL; // Stores DB, keys, key level, etc - private $_get_args = array(); - private $_post_args = array(); - private $_put_args = array(); - private $_delete_args = array(); - private $_args = array(); - private $_allow = TRUE; + protected $_get_args = array(); + protected $_post_args = array(); + protected $_put_args = array(); + protected $_delete_args = array(); + protected $_args = array(); + protected $_allow = TRUE; // List all supported methods, the first will be the default format - private $_supported_formats = array( + protected $_supported_formats = array( 'xml' => 'application/xml', 'rawxml' => 'application/xml', 'json' => 'application/json', @@ -262,7 +262,7 @@ public function response($data = array(), $http_code = null) * * Detect which format the HTTP Body is provided in */ - private function _detect_input_format() + protected function _detect_input_format() { if ($this->input->server('CONTENT_TYPE')) { @@ -289,7 +289,7 @@ private function _detect_input_format() * * Detect which format should be used to output the data */ - private function _detect_output_format() + protected function _detect_output_format() { $pattern = '/\.(' . implode('|', array_keys($this->_supported_formats)) . ')$/'; @@ -369,7 +369,7 @@ private function _detect_output_format() * Detect which method (POST, PUT, GET, DELETE) is being used */ - private function _detect_method() + protected function _detect_method() { $method = strtolower($this->input->server('REQUEST_METHOD')); @@ -392,7 +392,7 @@ private function _detect_method() * See if the user has provided an API key */ - private function _detect_api_key() + protected function _detect_api_key() { // Work out the name of the SERVER entry based on config $key_name = 'HTTP_' . strtoupper(str_replace('-', '_', config_item('rest_key_name'))); @@ -427,7 +427,7 @@ private function _detect_api_key() * What language do they want it in? */ - private function _detect_lang() + protected function _detect_lang() { if ( ! $lang = $this->input->server('HTTP_ACCEPT_LANGUAGE')) { @@ -461,7 +461,7 @@ private function _detect_lang() * Record the entry for awesomeness purposes */ - private function _log_request($authorized = FALSE) + protected function _log_request($authorized = FALSE) { return $this->rest->db->insert(config_item('rest_logs_table'), array( 'uri' => $this->uri->uri_string(), @@ -480,7 +480,7 @@ private function _log_request($authorized = FALSE) * Record the entry for awesomeness purposes */ - private function _check_limit($controller_method) + protected function _check_limit($controller_method) { // They are special, or it might not even have a limit if (!empty($this->rest->ignore_limits) OR !isset($this->methods[$controller_method]['limit'])) @@ -535,7 +535,7 @@ private function _check_limit($controller_method) * Check if there is a specific auth type set for the current class/method being called */ - private function _auth_override_check() + protected function _auth_override_check() { // Assign the class/method auth type override array from the config @@ -620,7 +620,7 @@ public function delete($key = NULL, $xss_clean = TRUE) return array_key_exists($key, $this->_delete_args) ? $this->_xss_clean($this->_delete_args[$key], $xss_clean) : FALSE; } - private function _xss_clean($val, $bool) + protected function _xss_clean($val, $bool) { if (CI_VERSION < 2) { @@ -641,7 +641,7 @@ public function validation_errors() // SECURITY FUNCTIONS --------------------------------------------------------- - private function _check_login($username = '', $password = NULL) + protected function _check_login($username = '', $password = NULL) { if (empty($username)) { @@ -664,7 +664,7 @@ private function _check_login($username = '', $password = NULL) return TRUE; } - private function _prepare_basic_auth() + protected function _prepare_basic_auth() { $username = NULL; $password = NULL; @@ -691,7 +691,7 @@ private function _prepare_basic_auth() } } - private function _prepare_digest_auth() + protected function _prepare_digest_auth() { $uniqid = uniqid(""); // Empty argument for backward compatibility // We need to test which server authentication variable to use @@ -742,7 +742,7 @@ private function _prepare_digest_auth() } } - private function _force_login($nonce = '') + protected function _force_login($nonce = '') { if ($this->config->item('rest_auth') == 'basic') { @@ -757,7 +757,7 @@ private function _force_login($nonce = '') } // Force it into an array - private function _force_loopable($data) + protected function _force_loopable($data) { // Force it to be something useful if ( ! is_array($data) AND ! is_object($data)) @@ -773,7 +773,7 @@ private function _force_loopable($data) // Many of these have been moved to the Format class for better separation, but these methods will be checked too // Encode as JSONP - private function _format_jsonp($data = array()) + protected function _format_jsonp($data = array()) { return $this->get('callback') . '(' . json_encode($data) . ')'; } From fd1ce2c83f9878bd47c5a7b2442fbd96cf1ef2be Mon Sep 17 00:00:00 2001 From: Fabio Borraccetti Date: Fri, 26 Aug 2011 16:19:01 +0200 Subject: [PATCH 010/651] Updated CodeIgniter files to 2.0.3 (Aug 20, 2011 release) Signed-off-by: Fabio Borraccetti --- index.php | 63 +- system/core/CodeIgniter.php | 62 +- system/core/Common.php | 115 ++- system/core/Config.php | 95 ++- system/core/Controller.php | 7 +- system/core/Exceptions.php | 4 +- system/core/Hooks.php | 10 +- system/core/Input.php | 70 +- system/core/Lang.php | 17 +- system/core/Loader.php | 295 ++++--- system/core/Output.php | 77 +- system/core/Router.php | 18 +- system/core/URI.php | 69 +- system/core/Utf8.php | 2 +- system/database/DB.php | 19 +- system/database/DB_active_rec.php | 314 ++++---- system/database/DB_driver.php | 18 +- system/database/DB_forge.php | 2 +- system/database/DB_result.php | 97 +-- .../database/drivers/mssql/mssql_driver.php | 12 +- .../database/drivers/mysql/mysql_driver.php | 37 +- system/database/drivers/mysql/mysql_forge.php | 8 +- .../database/drivers/mysqli/mysqli_driver.php | 27 +- .../database/drivers/mysqli/mysqli_forge.php | 8 +- system/database/drivers/oci8/oci8_driver.php | 6 +- system/database/drivers/odbc/odbc_driver.php | 4 +- system/database/drivers/odbc/odbc_result.php | 4 +- .../drivers/postgre/postgre_driver.php | 32 +- .../database/drivers/sqlite/sqlite_driver.php | 4 +- system/fonts/texb.ttf | Bin 152992 -> 143830 bytes system/helpers/array_helper.php | 1 + system/helpers/captcha_helper.php | 2 +- system/helpers/cookie_helper.php | 4 +- system/helpers/download_helper.php | 9 +- system/helpers/file_helper.php | 13 +- system/helpers/form_helper.php | 39 +- system/helpers/html_helper.php | 20 +- system/helpers/inflector_helper.php | 111 +-- system/helpers/language_helper.php | 2 +- system/helpers/number_helper.php | 2 +- system/helpers/security_helper.php | 34 +- system/helpers/smiley_helper.php | 22 +- system/helpers/text_helper.php | 12 +- system/helpers/typography_helper.php | 5 +- system/helpers/url_helper.php | 11 +- .../language/english/form_validation_lang.php | 3 + system/language/english/ftp_lang.php | 14 +- system/language/english/profiler_lang.php | 3 + system/libraries/Cache/Cache.php | 6 +- system/libraries/Cache/drivers/Cache_apc.php | 4 +- .../libraries/Cache/drivers/Cache_dummy.php | 26 +- system/libraries/Cache/drivers/Cache_file.php | 4 +- .../Cache/drivers/Cache_memcached.php | 4 +- system/libraries/Calendar.php | 2 +- system/libraries/Cart.php | 4 +- system/libraries/Driver.php | 34 +- system/libraries/Email.php | 58 +- system/libraries/Encrypt.php | 2 +- system/libraries/Form_validation.php | 83 +- system/libraries/Image_lib.php | 2 +- system/libraries/Javascript.php | 2 +- system/libraries/Log.php | 2 +- system/libraries/Pagination.php | 2 +- system/libraries/Profiler.php | 82 +- system/libraries/Security.php | 737 ------------------ system/libraries/Session.php | 13 +- system/libraries/Sha1.php | 4 +- system/libraries/Table.php | 11 +- system/libraries/Trackback.php | 4 +- system/libraries/Upload.php | 27 +- system/libraries/User_agent.php | 16 +- system/libraries/Xmlrpc.php | 66 +- system/libraries/Xmlrpcs.php | 178 +++-- system/libraries/Zip.php | 2 +- 74 files changed, 1533 insertions(+), 1645 deletions(-) delete mode 100755 system/libraries/Security.php diff --git a/index.php b/index.php index 5bb53d2f..f4ac11a7 100755 --- a/index.php +++ b/index.php @@ -2,15 +2,49 @@ /* *--------------------------------------------------------------- - * PHP ERROR REPORTING LEVEL + * APPLICATION ENVIRONMENT *--------------------------------------------------------------- * - * By default CI runs with error reporting set to ALL. For security - * reasons you are encouraged to change this to 0 when your site goes live. - * For more info visit: http://www.php.net/error_reporting + * You can load different configurations depending on your + * current environment. Setting the environment also influences + * things like logging and error reporting. + * + * This can be set to anything, but default usage is: + * + * development + * testing + * production + * + * NOTE: If you change these, also change the error_reporting() code below * */ - error_reporting(E_ALL); + define('ENVIRONMENT', 'development'); +/* + *--------------------------------------------------------------- + * ERROR REPORTING + *--------------------------------------------------------------- + * + * Different environments will require different levels of error reporting. + * By default development will show errors but testing and live will hide them. + */ + +if (defined('ENVIRONMENT')) +{ + switch (ENVIRONMENT) + { + case 'development': + error_reporting(E_ALL); + break; + + case 'testing': + case 'production': + error_reporting(0); + break; + + default: + exit('The application environment is not set correctly.'); + } +} /* *--------------------------------------------------------------- @@ -22,7 +56,7 @@ * as this file. * */ - $system_path = "system"; + $system_path = 'system'; /* *--------------------------------------------------------------- @@ -38,7 +72,7 @@ * NO TRAILING SLASH! * */ - $application_folder = "application"; + $application_folder = 'application'; /* * -------------------------------------------------------------------- @@ -64,7 +98,7 @@ // if your controller is not in a sub-folder within the "controllers" folder // $routing['directory'] = ''; - // The controller class file name. Example: Mycontroller.php + // The controller class file name. Example: Mycontroller // $routing['controller'] = ''; // The controller function you wish to be called. @@ -94,14 +128,18 @@ // END OF USER CONFIGURABLE SETTINGS. DO NOT EDIT BELOW THIS LINE // -------------------------------------------------------------------- - - - /* * --------------------------------------------------------------- * Resolve the system path for increased reliability * --------------------------------------------------------------- */ + + // Set the current directory correctly for CLI requests + if (defined('STDIN')) + { + chdir(dirname(__FILE__)); + } + if (realpath($system_path) !== FALSE) { $system_path = realpath($system_path).'/'; @@ -125,6 +163,7 @@ define('SELF', pathinfo(__FILE__, PATHINFO_BASENAME)); // The PHP file extension + // this global constant is deprecated. define('EXT', '.php'); // Path to the system folder @@ -160,7 +199,7 @@ * And away we go... * */ -require_once BASEPATH.'core/CodeIgniter'.EXT; +require_once BASEPATH.'core/CodeIgniter.php'; /* End of file index.php */ /* Location: ./index.php */ \ No newline at end of file diff --git a/system/core/CodeIgniter.php b/system/core/CodeIgniter.php index 2d3f2495..94fecb5c 100755 --- a/system/core/CodeIgniter.php +++ b/system/core/CodeIgniter.php @@ -32,21 +32,35 @@ * Define the CodeIgniter Version * ------------------------------------------------------ */ - define('CI_VERSION', '2.0'); + define('CI_VERSION', '2.0.3'); + +/* + * ------------------------------------------------------ + * Define the CodeIgniter Branch (Core = TRUE, Reactor = FALSE) + * ------------------------------------------------------ + */ + define('CI_CORE', FALSE); /* * ------------------------------------------------------ * Load the global functions * ------------------------------------------------------ */ - require(BASEPATH.'core/Common'.EXT); + require(BASEPATH.'core/Common.php'); /* * ------------------------------------------------------ * Load the framework constants * ------------------------------------------------------ */ - require(APPPATH.'config/constants'.EXT); + if (defined('ENVIRONMENT') AND file_exists(APPPATH.'config/'.ENVIRONMENT.'/constants.php')) + { + require(APPPATH.'config/'.ENVIRONMENT.'/constants.php'); + } + else + { + require(APPPATH.'config/constants.php'); + } /* * ------------------------------------------------------ @@ -80,7 +94,7 @@ { get_config(array('subclass_prefix' => $assign_to_config['subclass_prefix'])); } - + /* * ------------------------------------------------------ * Set a liberal script execution time limit @@ -182,6 +196,13 @@ } } +/* + * ----------------------------------------------------- + * Load the security class for xss and csrf support + * ----------------------------------------------------- + */ + $SEC =& load_class('Security', 'core'); + /* * ------------------------------------------------------ * Load the Input class and sanitize globals @@ -203,7 +224,7 @@ * */ // Load the base controller class - require BASEPATH.'core/Controller'.EXT; + require BASEPATH.'core/Controller.php'; function &get_instance() { @@ -211,20 +232,20 @@ function &get_instance() } - if (file_exists(APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller'.EXT)) + if (file_exists(APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php')) { - require APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller'.EXT; + require APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php'; } // Load the local application controller // Note: The Router class automatically validates the controller path using the router->_validate_request(). // If this include fails it means that the default controller in the Routes.php file is not resolving to something valid. - if ( ! file_exists(APPPATH.'controllers/'.$RTR->fetch_directory().$RTR->fetch_class().EXT)) + if ( ! file_exists(APPPATH.'controllers/'.$RTR->fetch_directory().$RTR->fetch_class().'.php')) { show_error('Unable to load your default controller. Please make sure the controller specified in your Routes.php file is valid.'); } - include(APPPATH.'controllers/'.$RTR->fetch_directory().$RTR->fetch_class().EXT); + include(APPPATH.'controllers/'.$RTR->fetch_directory().$RTR->fetch_class().'.php'); // Set a mark point for benchmarking $BM->mark('loading_time:_base_classes_end'); @@ -289,7 +310,28 @@ function &get_instance() // methods, so we'll use this workaround for consistent behavior if ( ! in_array(strtolower($method), array_map('strtolower', get_class_methods($CI)))) { - show_404("{$class}/{$method}"); + // Check and see if we are using a 404 override and use it. + if ( ! empty($RTR->routes['404_override'])) + { + $x = explode('/', $RTR->routes['404_override']); + $class = $x[0]; + $method = (isset($x[1]) ? $x[1] : 'index'); + if ( ! class_exists($class)) + { + if ( ! file_exists(APPPATH.'controllers/'.$class.'.php')) + { + show_404("{$class}/{$method}"); + } + + include_once(APPPATH.'controllers/'.$class.'.php'); + unset($CI); + $CI = new $class(); + } + } + else + { + show_404("{$class}/{$method}"); + } } // Call the requested method. diff --git a/system/core/Common.php b/system/core/Common.php index b5adfacb..db9fbeb9 100755 --- a/system/core/Common.php +++ b/system/core/Common.php @@ -39,6 +39,8 @@ * @param string * @return bool TRUE if the current version is $version or higher */ +if ( ! function_exists('is_php')) +{ function is_php($version = '5.0.0') { static $_is_php; @@ -51,6 +53,7 @@ function is_php($version = '5.0.0') return $_is_php[$version]; } +} // ------------------------------------------------------------------------ @@ -64,6 +67,8 @@ function is_php($version = '5.0.0') * @access private * @return void */ +if ( ! function_exists('is_really_writable')) +{ function is_really_writable($file) { // If we're on a Unix server with safe_mode off we call is_writable @@ -88,7 +93,7 @@ function is_really_writable($file) @unlink($file); return TRUE; } - elseif (($fp = @fopen($file, FOPEN_WRITE_CREATE)) === FALSE) + elseif ( ! is_file($file) OR ($fp = @fopen($file, FOPEN_WRITE_CREATE)) === FALSE) { return FALSE; } @@ -96,6 +101,7 @@ function is_really_writable($file) fclose($fp); return TRUE; } +} // ------------------------------------------------------------------------ @@ -112,6 +118,8 @@ function is_really_writable($file) * @param string the class name prefix * @return object */ +if ( ! function_exists('load_class')) +{ function &load_class($class, $directory = 'libraries', $prefix = 'CI_') { static $_classes = array(); @@ -128,13 +136,13 @@ function &load_class($class, $directory = 'libraries', $prefix = 'CI_') // thenin the local application/libraries folder foreach (array(BASEPATH, APPPATH) as $path) { - if (file_exists($path.$directory.'/'.$class.EXT)) + if (file_exists($path.$directory.'/'.$class.'.php')) { $name = $prefix.$class; if (class_exists($name) === FALSE) { - require($path.$directory.'/'.$class.EXT); + require($path.$directory.'/'.$class.'.php'); } break; @@ -142,13 +150,13 @@ function &load_class($class, $directory = 'libraries', $prefix = 'CI_') } // Is the request a class extension? If so we load it too - if (file_exists(APPPATH.$directory.'/'.config_item('subclass_prefix').$class.EXT)) + if (file_exists(APPPATH.$directory.'/'.config_item('subclass_prefix').$class.'.php')) { $name = config_item('subclass_prefix').$class; if (class_exists($name) === FALSE) { - require(APPPATH.$directory.'/'.config_item('subclass_prefix').$class.EXT); + require(APPPATH.$directory.'/'.config_item('subclass_prefix').$class.'.php'); } } @@ -157,7 +165,7 @@ function &load_class($class, $directory = 'libraries', $prefix = 'CI_') { // Note: We use exit() rather then show_error() in order to avoid a // self-referencing loop with the Excptions class - exit('Unable to locate the specified class: '.$class.EXT); + exit('Unable to locate the specified class: '.$class.'.php'); } // Keep track of what we just loaded @@ -166,6 +174,7 @@ function &load_class($class, $directory = 'libraries', $prefix = 'CI_') $_classes[$class] = new $name(); return $_classes[$class]; } +} // -------------------------------------------------------------------- @@ -176,6 +185,8 @@ function &load_class($class, $directory = 'libraries', $prefix = 'CI_') * @access public * @return array */ +if ( ! function_exists('is_loaded')) +{ function is_loaded($class = '') { static $_is_loaded = array(); @@ -187,6 +198,7 @@ function is_loaded($class = '') return $_is_loaded; } +} // ------------------------------------------------------------------------ @@ -199,6 +211,8 @@ function is_loaded($class = '') * @access private * @return array */ +if ( ! function_exists('get_config')) +{ function &get_config($replace = array()) { static $_config; @@ -208,16 +222,20 @@ function &get_config($replace = array()) return $_config[0]; } - // Fetch the config file - if ( ! file_exists(APPPATH.'config/config'.EXT)) + // Is the config file in the environment folder? + if ( ! defined('ENVIRONMENT') OR ! file_exists($file_path = APPPATH.'config/'.ENVIRONMENT.'/config.php')) { - exit('The configuration file does not exist.'); + $file_path = APPPATH.'config/config.php'; } - else + + // Fetch the config file + if ( ! file_exists($file_path)) { - require(APPPATH.'config/config'.EXT); + exit('The configuration file does not exist.'); } + require($file_path); + // Does the $config array exist in the file? if ( ! isset($config) OR ! is_array($config)) { @@ -238,6 +256,7 @@ function &get_config($replace = array()) return $_config[0] =& $config; } +} // ------------------------------------------------------------------------ @@ -247,6 +266,8 @@ function &get_config($replace = array()) * @access public * @return mixed */ +if ( ! function_exists('config_item')) +{ function config_item($item) { static $_config_item = array(); @@ -264,6 +285,7 @@ function config_item($item) return $_config_item[$item]; } +} // ------------------------------------------------------------------------ @@ -279,12 +301,15 @@ function config_item($item) * @access public * @return void */ +if ( ! function_exists('show_error')) +{ function show_error($message, $status_code = 500, $heading = 'An Error Was Encountered') { $_error =& load_class('Exceptions', 'core'); echo $_error->show_error($heading, $message, 'error_general', $status_code); exit; } +} // ------------------------------------------------------------------------ @@ -298,12 +323,15 @@ function show_error($message, $status_code = 500, $heading = 'An Error Was Encou * @access public * @return void */ +if ( ! function_exists('show_404')) +{ function show_404($page = '', $log_error = TRUE) { $_error =& load_class('Exceptions', 'core'); $_error->show_404($page, $log_error); exit; } +} // ------------------------------------------------------------------------ @@ -316,6 +344,8 @@ function show_404($page = '', $log_error = TRUE) * @access public * @return void */ +if ( ! function_exists('log_message')) +{ function log_message($level = 'error', $message, $php_error = FALSE) { static $_log; @@ -328,6 +358,7 @@ function log_message($level = 'error', $message, $php_error = FALSE) $_log =& load_class('Log'); $_log->write_log($level, $message, $php_error); } +} // ------------------------------------------------------------------------ @@ -339,6 +370,8 @@ function log_message($level = 'error', $message, $php_error = FALSE) * @param string * @return void */ +if ( ! function_exists('set_status_header')) +{ function set_status_header($code = 200, $text = '') { $stati = array( @@ -413,6 +446,7 @@ function set_status_header($code = 200, $text = '') header("HTTP/1.1 {$code} {$text}", TRUE, $code); } } +} // -------------------------------------------------------------------- @@ -430,6 +464,8 @@ function set_status_header($code = 200, $text = '') * @access private * @return void */ +if ( ! function_exists('_exception_handler')) +{ function _exception_handler($severity, $message, $filepath, $line) { // We don't bother with "strict" notices since they tend to fill up @@ -459,45 +495,46 @@ function _exception_handler($severity, $message, $filepath, $line) $_error->log_exception($severity, $message, $filepath, $line); } +} - // -------------------------------------------------------------------- - - /** - * Remove Invisible Characters - * - * This prevents sandwiching null characters - * between ascii characters, like Java\0script. - * - * @access public - * @param string - * @return string - */ - function remove_invisible_characters($str) - { - static $non_displayables; +// -------------------------------------------------------------------- - if ( ! isset($non_displayables)) +/** + * Remove Invisible Characters + * + * This prevents sandwiching null characters + * between ascii characters, like Java\0script. + * + * @access public + * @param string + * @return string + */ +if ( ! function_exists('remove_invisible_characters')) +{ + function remove_invisible_characters($str, $url_encoded = TRUE) + { + $non_displayables = array(); + + // every control character except newline (dec 10) + // carriage return (dec 13), and horizontal tab (dec 09) + + if ($url_encoded) { - // every control character except newline (dec 10), carriage return (dec 13), and horizontal tab (dec 09), - $non_displayables = array( - '/%0[0-8bcef]/', // url encoded 00-08, 11, 12, 14, 15 - '/%1[0-9a-f]/', // url encoded 16-31 - '/[\x00-\x08]/', // 00-08 - '/\x0b/', '/\x0c/', // 11, 12 - '/[\x0e-\x1f]/' // 14-31 - ); + $non_displayables[] = '/%0[0-8bcef]/'; // url encoded 00-08, 11, 12, 14, 15 + $non_displayables[] = '/%1[0-9a-f]/'; // url encoded 16-31 } + + $non_displayables[] = '/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+/S'; // 00-08, 11, 12, 14-31, 127 do { - $cleaned = $str; - $str = preg_replace($non_displayables, '', $str); + $str = preg_replace($non_displayables, '', $str, -1, $count); } - while ($cleaned != $str); + while ($count); return $str; } - +} /* End of file Common.php */ /* Location: ./system/core/Common.php */ \ No newline at end of file diff --git a/system/core/Config.php b/system/core/Config.php index bfb60fa4..0e6f10e0 100755 --- a/system/core/Config.php +++ b/system/core/Config.php @@ -51,7 +51,7 @@ function __construct() // Set the base_url automatically if none was provided if ($this->config['base_url'] == '') { - if(isset($_SERVER['HTTP_HOST'])) + if (isset($_SERVER['HTTP_HOST'])) { $base_url = isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) !== 'off' ? 'https' : 'http'; $base_url .= '://'. $_SERVER['HTTP_HOST']; @@ -74,24 +74,40 @@ function __construct() * * @access public * @param string the config file name + * @param boolean if configuration values should be loaded into their own section + * @param boolean true if errors should just return false, false if an error message should be displayed * @return boolean if the file was loaded correctly */ function load($file = '', $use_sections = FALSE, $fail_gracefully = FALSE) { - $file = ($file == '') ? 'config' : str_replace(EXT, '', $file); + $file = ($file == '') ? 'config' : str_replace('.php', '', $file); + $found = FALSE; $loaded = FALSE; - foreach($this->_config_paths as $path) + foreach ($this->_config_paths as $path) { - $file_path = $path.'config/'.$file.EXT; + $check_locations = defined('ENVIRONMENT') + ? array(ENVIRONMENT.'/'.$file, $file) + : array($file); - if (in_array($file_path, $this->is_loaded, TRUE)) + foreach ($check_locations as $location) { - $loaded = TRUE; - continue; + $file_path = $path.'config/'.$location.'.php'; + + if (in_array($file_path, $this->is_loaded, TRUE)) + { + $loaded = TRUE; + continue 2; + } + + if (file_exists($file_path)) + { + $found = TRUE; + break; + } } - if ( ! file_exists($path.'config/'.$file.EXT)) + if ($found === FALSE) { continue; } @@ -128,6 +144,7 @@ function load($file = '', $use_sections = FALSE, $fail_gracefully = FALSE) $loaded = TRUE; log_message('debug', 'Config file loaded: '.$file_path); + break; } if ($loaded === FALSE) @@ -136,7 +153,7 @@ function load($file = '', $use_sections = FALSE, $fail_gracefully = FALSE) { return FALSE; } - show_error('The configuration file '.$file.EXT.' does not exist.'); + show_error('The configuration file '.$file.'.php'.' does not exist.'); } return TRUE; @@ -186,10 +203,7 @@ function item($item, $index = '') // -------------------------------------------------------------------- /** - * Fetch a config file item - adds slash after item - * - * The second parameter allows a slash to be added to the end of - * the item, in the case of a path. + * Fetch a config file item - adds slash after item (if item is not empty) * * @access public * @param string the config item name @@ -202,6 +216,10 @@ function slash_item($item) { return FALSE; } + if( trim($this->config[$item]) == '') + { + return ''; + } return rtrim($this->config[$item], '/').'/'; } @@ -210,6 +228,7 @@ function slash_item($item) /** * Site URL + * Returns base_url . index_page [. uri_string] * * @access public * @param string the URI string @@ -222,16 +241,50 @@ function site_url(http://github.com/$uri%20=%20'') return $this->slash_item('base_url').$this->item('index_page'); } + if ($this->item('enable_query_strings') == FALSE) + { + $suffix = ($this->item('url_suffix') == FALSE) ? '' : $this->item('url_suffix'); + return $this->slash_item('base_url').$this->slash_item('index_page').$this->_uri_string($uri).$suffix; + } + else + { + return $this->slash_item('base_url').$this->item('index_page').'?'.$this->_uri_string($uri); + } + } + + // ------------------------------------------------------------- + + /** + * Base URL + * Returns base_url [. uri_string] + * + * @access public + * @param string $uri + * @return string + */ + function base_url(http://github.com/$uri%20=%20'') + { + return $this->slash_item('base_url').ltrim($this->_uri_string($uri),'/'); + } + + // ------------------------------------------------------------- + + /** + * Build URI string for use in Config::site_url() and Config::base_url() + * + * @access protected + * @param $uri + * @return string + */ + protected function _uri_string($uri) + { if ($this->item('enable_query_strings') == FALSE) { if (is_array($uri)) { $uri = implode('/', $uri); } - - $index = $this->item('index_page') == '' ? '' : $this->slash_item('index_page'); - $suffix = ($this->item('url_suffix') == FALSE) ? '' : $this->item('url_suffix'); - return $this->slash_item('base_url').$index.trim($uri, '/').$suffix; + $uri = trim($uri, '/'); } else { @@ -245,16 +298,14 @@ function site_url(http://github.com/$uri%20=%20'') $str .= $prefix.$key.'='.$val; $i++; } - $uri = $str; } - - return $this->slash_item('base_url').$this->item('index_page').'?'.$uri; } + return $uri; } // -------------------------------------------------------------------- - + /** * System URL * @@ -310,4 +361,4 @@ function _assign_to_config($items = array()) // END CI_Config class /* End of file Config.php */ -/* Location: ./system/core/Config.php */ \ No newline at end of file +/* Location: ./system/core/Config.php */ diff --git a/system/core/Controller.php b/system/core/Controller.php index 469663f0..ec86b792 100755 --- a/system/core/Controller.php +++ b/system/core/Controller.php @@ -48,12 +48,9 @@ public function __construct() $this->load =& load_class('Loader', 'core'); - $this->load->_base_classes =& is_loaded(); - - $this->load->_ci_autoloader(); - + $this->load->set_base_classes()->ci_autoloader(); + log_message('debug', "Controller Class Initialized"); - } public static function &get_instance() diff --git a/system/core/Exceptions.php b/system/core/Exceptions.php index f5659561..bff86a92 100755 --- a/system/core/Exceptions.php +++ b/system/core/Exceptions.php @@ -128,7 +128,7 @@ function show_error($heading, $message, $template = 'error_general', $status_cod ob_end_flush(); } ob_start(); - include(APPPATH.'errors/'.$template.EXT); + include(APPPATH.'errors/'.$template.'.php'); $buffer = ob_get_contents(); ob_end_clean(); return $buffer; @@ -164,7 +164,7 @@ function show_php_error($severity, $message, $filepath, $line) ob_end_flush(); } ob_start(); - include(APPPATH.'errors/error_php'.EXT); + include(APPPATH.'errors/error_php.php'); $buffer = ob_get_contents(); ob_end_clean(); echo $buffer; diff --git a/system/core/Hooks.php b/system/core/Hooks.php index 75fd811b..fd6380f0 100755 --- a/system/core/Hooks.php +++ b/system/core/Hooks.php @@ -65,7 +65,15 @@ function _initialize() // Grab the "hooks" definition file. // If there are no hooks, we're done. - @include(APPPATH.'config/hooks'.EXT); + if (defined('ENVIRONMENT') AND is_file(APPPATH.'config/'.ENVIRONMENT.'/hooks.php')) + { + include(APPPATH.'config/'.ENVIRONMENT.'/hooks.php'); + } + elseif (is_file(APPPATH.'config/hooks.php')) + { + include(APPPATH.'config/hooks.php'); + } + if ( ! isset($hook) OR ! is_array($hook)) { diff --git a/system/core/Input.php b/system/core/Input.php index 3e82874f..cfbef942 100755 --- a/system/core/Input.php +++ b/system/core/Input.php @@ -53,11 +53,8 @@ public function __construct() $this->_enable_xss = (config_item('global_xss_filtering') === TRUE); $this->_enable_csrf = (config_item('csrf_protection') === TRUE); - // Do we need to load the security class? - if ($this->_enable_xss == TRUE OR $this->_enable_csrf == TRUE) - { - $this->security =& load_class('Security'); - } + global $SEC; + $this->security =& $SEC; // Do we need the UTF-8 class? if (UTF8_ENABLED === TRUE) @@ -92,8 +89,7 @@ function _fetch_from_array(&$array, $index = '', $xss_clean = FALSE) if ($xss_clean === TRUE) { - $_security =& load_class('Security'); - return $_security->xss_clean($array[$index]); + return $this->security->xss_clean($array[$index]); } return $array[$index]; @@ -109,8 +105,21 @@ function _fetch_from_array(&$array, $index = '', $xss_clean = FALSE) * @param bool * @return string */ - function get($index = '', $xss_clean = FALSE) + function get($index = NULL, $xss_clean = FALSE) { + // Check if a field has been provided + if ($index === NULL AND ! empty($_GET)) + { + $get = array(); + + // loop through the full _GET array + foreach (array_keys($_GET) as $key) + { + $get[$key] = $this->_fetch_from_array($_GET, $key, $xss_clean); + } + return $get; + } + return $this->_fetch_from_array($_GET, $index, $xss_clean); } @@ -124,8 +133,21 @@ function get($index = '', $xss_clean = FALSE) * @param bool * @return string */ - function post($index = '', $xss_clean = FALSE) + function post($index = NULL, $xss_clean = FALSE) { + // Check if a field has been provided + if ($index === NULL AND ! empty($_POST)) + { + $post = array(); + + // Loop through the full _POST array and return it + foreach (array_keys($_POST) as $key) + { + $post[$key] = $this->_fetch_from_array($_POST, $key, $xss_clean); + } + return $post; + } + return $this->_fetch_from_array($_POST, $index, $xss_clean); } @@ -182,13 +204,15 @@ function cookie($index = '', $xss_clean = FALSE) * @param string the cookie domain. Usually: .yourdomain.com * @param string the cookie path * @param string the cookie prefix + * @param bool true makes the cookie secure * @return void */ - function set_cookie($name = '', $value = '', $expire = '', $domain = '', $path = '/', $prefix = '') + function set_cookie($name = '', $value = '', $expire = '', $domain = '', $path = '/', $prefix = '', $secure = FALSE) { if (is_array($name)) { - foreach (array('value', 'expire', 'domain', 'path', 'prefix', 'name') as $item) + // always leave 'name' in last place, as the loop will break otherwise, due to $$item + foreach (array('value', 'expire', 'domain', 'path', 'prefix', 'secure', 'name') as $item) { if (isset($name[$item])) { @@ -209,6 +233,10 @@ function set_cookie($name = '', $value = '', $expire = '', $domain = '', $path = { $path = config_item('cookie_path'); } + if ($secure == FALSE AND config_item('cookie_secure') != FALSE) + { + $secure = config_item('cookie_secure'); + } if ( ! is_numeric($expire)) { @@ -219,7 +247,7 @@ function set_cookie($name = '', $value = '', $expire = '', $domain = '', $path = $expire = ($expire > 0) ? time() + $expire : 0; } - setcookie($prefix.$name, $value, $expire, $path, $domain, 0); + setcookie($prefix.$name, $value, $expire, $path, $domain, $secure); } // -------------------------------------------------------------------- @@ -413,7 +441,7 @@ function _sanitize_globals() { if (is_array($_GET) AND count($_GET) > 0) { - foreach($_GET as $key => $val) + foreach ($_GET as $key => $val) { $_GET[$this->_clean_input_keys($key)] = $this->_clean_input_data($val); } @@ -423,7 +451,7 @@ function _sanitize_globals() // Clean $_POST Data if (is_array($_POST) AND count($_POST) > 0) { - foreach($_POST as $key => $val) + foreach ($_POST as $key => $val) { $_POST[$this->_clean_input_keys($key)] = $this->_clean_input_data($val); } @@ -441,7 +469,7 @@ function _sanitize_globals() unset($_COOKIE['$Path']); unset($_COOKIE['$Domain']); - foreach($_COOKIE as $key => $val) + foreach ($_COOKIE as $key => $val) { $_COOKIE[$this->_clean_input_keys($key)] = $this->_clean_input_data($val); } @@ -495,6 +523,9 @@ function _clean_input_data($str) { $str = $this->uni->clean_string($str); } + + // Remove control characters + $str = remove_invisible_characters($str); // Should we filter the input data? if ($this->_enable_xss === TRUE) @@ -507,7 +538,7 @@ function _clean_input_data($str) { if (strpos($str, "\r") !== FALSE) { - $str = str_replace(array("\r\n", "\r"), PHP_EOL, $str); + $str = str_replace(array("\r\n", "\r", "\r\n\n"), PHP_EOL, $str); } } @@ -610,8 +641,7 @@ public function get_request_header($index, $xss_clean = FALSE) if ($xss_clean === TRUE) { - $_security =& load_class('Security'); - return $_security->xss_clean($this->headers[$index]); + return $this->security->xss_clean($this->headers[$index]); } return $this->headers[$index]; @@ -642,11 +672,11 @@ public function is_ajax_request() */ public function is_cli_request() { - return (bool) defined('STDIN'); + return (php_sapi_name() == 'cli') or defined('STDIN'); } } // END Input class /* End of file Input.php */ -/* Location: ./system/core/Input.php */ \ No newline at end of file +/* Location: ./system/core/Input.php */ diff --git a/system/core/Lang.php b/system/core/Lang.php index fb177902..170e6c72 100755 --- a/system/core/Lang.php +++ b/system/core/Lang.php @@ -51,14 +51,14 @@ function __construct() */ function load($langfile = '', $idiom = '', $return = FALSE, $add_suffix = TRUE, $alt_path = '') { - $langfile = str_replace(EXT, '', $langfile); + $langfile = str_replace('.php', '', $langfile); if ($add_suffix == TRUE) { $langfile = str_replace('_lang.', '', $langfile).'_lang'; } - $langfile .= EXT; + $langfile .= '.php'; if (in_array($langfile, $this->is_loaded, TRUE)) { @@ -129,12 +129,19 @@ function load($langfile = '', $idiom = '', $return = FALSE, $add_suffix = TRUE, */ function line($line = '') { - $line = ($line == '' OR ! isset($this->language[$line])) ? FALSE : $this->language[$line]; - return $line; + $value = ($line == '' OR ! isset($this->language[$line])) ? FALSE : $this->language[$line]; + + // Because killer robots like unicorns! + if ($value === FALSE) + { + log_message('error', 'Could not find the language line "'.$line.'"'); + } + + return $value; } } // END Language Class /* End of file Lang.php */ -/* Location: ./system/core/Lang.php */ \ No newline at end of file +/* Location: ./system/core/Lang.php */ diff --git a/system/core/Loader.php b/system/core/Loader.php index 07166188..7c8b298a 100755 --- a/system/core/Loader.php +++ b/system/core/Loader.php @@ -29,38 +29,77 @@ class CI_Loader { // All these are set automatically. Don't mess with them. - var $_ci_ob_level; - var $_ci_view_path = ''; - var $_ci_library_paths = array(); - var $_ci_model_paths = array(); - var $_ci_helper_paths = array(); - var $_base_classes = array(); // Set by the controller class - var $_ci_cached_vars = array(); - var $_ci_classes = array(); - var $_ci_loaded_files = array(); - var $_ci_models = array(); - var $_ci_helpers = array(); - var $_ci_varmap = array('unit_test' => 'unit', 'user_agent' => 'agent'); - + protected $_ci_ob_level; + protected $_ci_view_paths = array(); + protected $_ci_library_paths = array(); + protected $_ci_model_paths = array(); + protected $_ci_helper_paths = array(); + protected $_base_classes = array(); // Set by the controller class + protected $_ci_cached_vars = array(); + protected $_ci_classes = array(); + protected $_ci_loaded_files = array(); + protected $_ci_models = array(); + protected $_ci_helpers = array(); + protected $_ci_varmap = array('unit_test' => 'unit', + 'user_agent' => 'agent'); /** * Constructor * * Sets the path to the view files and gets the initial output buffering level - * - * @access public */ - function __construct() + public function __construct() { - $this->_ci_view_path = APPPATH.'views/'; $this->_ci_ob_level = ob_get_level(); $this->_ci_library_paths = array(APPPATH, BASEPATH); $this->_ci_helper_paths = array(APPPATH, BASEPATH); $this->_ci_model_paths = array(APPPATH); - + $this->_ci_view_paths = array(APPPATH.'views/' => TRUE); + log_message('debug', "Loader Class Initialized"); } + // -------------------------------------------------------------------- + + /** + * Set _base_classes variable + * + * This method is called once in CI_Controller. + * + * @param array + * @return object + */ + public function set_base_classes() + { + $this->_base_classes =& is_loaded(); + + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Is Loaded + * + * A utility function to test if a class is in the self::$_ci_classes array. + * This function returns the object name if the class tested for is loaded, + * and returns FALSE if it isn't. + * + * It is mainly used in the form_helper -> _get_validation_object() + * + * @param string class being checked for + * @return mixed class object name on the CI SuperObject or FALSE + */ + public function is_loaded($class) + { + if (isset($this->_ci_classes[$class])) + { + return $this->_ci_classes[$class]; + } + + return FALSE; + } + // -------------------------------------------------------------------- /** @@ -69,19 +108,18 @@ function __construct() * This function lets users load and instantiate classes. * It is designed to be called from a user's app controllers. * - * @access public * @param string the name of the class * @param mixed the optional parameters * @param string an optional object name * @return void */ - function library($library = '', $params = NULL, $object_name = NULL) + public function library($library = '', $params = NULL, $object_name = NULL) { if (is_array($library)) { - foreach($library as $read) + foreach ($library as $class) { - $this->library($read); + $this->library($class, $params); } return; @@ -97,17 +135,7 @@ function library($library = '', $params = NULL, $object_name = NULL) $params = NULL; } - if (is_array($library)) - { - foreach ($library as $class) - { - $this->_ci_load_class($class, $params, $object_name); - } - } - else - { - $this->_ci_load_class($library, $params, $object_name); - } + $this->_ci_load_class($library, $params, $object_name); } // -------------------------------------------------------------------- @@ -117,17 +145,16 @@ function library($library = '', $params = NULL, $object_name = NULL) * * This function lets users load and instantiate models. * - * @access public * @param string the name of the class * @param string name for the model * @param bool database connection * @return void */ - function model($model, $name = '', $db_conn = FALSE) + public function model($model, $name = '', $db_conn = FALSE) { if (is_array($model)) { - foreach($model as $babe) + foreach ($model as $babe) { $this->model($babe); } @@ -171,7 +198,7 @@ function model($model, $name = '', $db_conn = FALSE) foreach ($this->_ci_model_paths as $mod_path) { - if ( ! file_exists($mod_path.'models/'.$path.$model.EXT)) + if ( ! file_exists($mod_path.'models/'.$path.$model.'.php')) { continue; } @@ -191,7 +218,7 @@ function model($model, $name = '', $db_conn = FALSE) load_class('Model', 'core'); } - require_once($mod_path.'models/'.$path.$model.EXT); + require_once($mod_path.'models/'.$path.$model.'.php'); $model = ucfirst($model); @@ -210,13 +237,12 @@ function model($model, $name = '', $db_conn = FALSE) /** * Database Loader * - * @access public * @param string the DB credentials * @param bool whether to return the DB object * @param bool whether to enable active record (this allows us to override the config setting) * @return object */ - function database($params = '', $return = FALSE, $active_record = NULL) + public function database($params = '', $return = FALSE, $active_record = NULL) { // Grab the super object $CI =& get_instance(); @@ -227,7 +253,7 @@ function database($params = '', $return = FALSE, $active_record = NULL) return FALSE; } - require_once(BASEPATH.'database/DB'.EXT); + require_once(BASEPATH.'database/DB.php'); if ($return === TRUE) { @@ -247,10 +273,9 @@ function database($params = '', $return = FALSE, $active_record = NULL) /** * Load the Utilities Class * - * @access public * @return string */ - function dbutil() + public function dbutil() { if ( ! class_exists('CI_DB')) { @@ -263,8 +288,8 @@ function dbutil() // this use is deprecated and strongly discouraged $CI->load->dbforge(); - require_once(BASEPATH.'database/DB_utility'.EXT); - require_once(BASEPATH.'database/drivers/'.$CI->db->dbdriver.'/'.$CI->db->dbdriver.'_utility'.EXT); + require_once(BASEPATH.'database/DB_utility.php'); + require_once(BASEPATH.'database/drivers/'.$CI->db->dbdriver.'/'.$CI->db->dbdriver.'_utility.php'); $class = 'CI_DB_'.$CI->db->dbdriver.'_utility'; $CI->dbutil = new $class(); @@ -275,10 +300,9 @@ function dbutil() /** * Load the Database Forge Class * - * @access public * @return string */ - function dbforge() + public function dbforge() { if ( ! class_exists('CI_DB')) { @@ -287,8 +311,8 @@ function dbforge() $CI =& get_instance(); - require_once(BASEPATH.'database/DB_forge'.EXT); - require_once(BASEPATH.'database/drivers/'.$CI->db->dbdriver.'/'.$CI->db->dbdriver.'_forge'.EXT); + require_once(BASEPATH.'database/DB_forge.php'); + require_once(BASEPATH.'database/drivers/'.$CI->db->dbdriver.'/'.$CI->db->dbdriver.'_forge.php'); $class = 'CI_DB_'.$CI->db->dbdriver.'_forge'; $CI->dbforge = new $class(); @@ -307,13 +331,12 @@ function dbforge() * some cases it's advantageous to be able to return data so that * a developer can process it in some way. * - * @access public * @param string * @param array * @param bool * @return void */ - function view($view, $vars = array(), $return = FALSE) + public function view($view, $vars = array(), $return = FALSE) { return $this->_ci_load(array('_ci_view' => $view, '_ci_vars' => $this->_ci_object_to_array($vars), '_ci_return' => $return)); } @@ -325,12 +348,11 @@ function view($view, $vars = array(), $return = FALSE) * * This is a generic file loader * - * @access public * @param string * @param bool * @return string */ - function file($path, $return = FALSE) + public function file($path, $return = FALSE) { return $this->_ci_load(array('_ci_path' => $path, '_ci_return' => $return)); } @@ -343,11 +365,10 @@ function file($path, $return = FALSE) * Once variables are set they become available within * the controller class and its "view" files. * - * @access public * @param array * @return void */ - function vars($vars = array(), $val = '') + public function vars($vars = array(), $val = '') { if ($val != '' AND is_string($vars)) { @@ -367,16 +388,30 @@ function vars($vars = array(), $val = '') // -------------------------------------------------------------------- + /** + * Get Variable + * + * Check if a variable is set and retrieve it. + * + * @param array + * @return void + */ + public function get_var($key) + { + return isset($this->_ci_cached_vars[$key]) ? $this->_ci_cached_vars[$key] : NULL; + } + + // -------------------------------------------------------------------- + /** * Load Helper * * This function loads the specified helper file. * - * @access public * @param mixed * @return void */ - function helper($helpers = array()) + public function helper($helpers = array()) { foreach ($this->_ci_prep_filename($helpers, '_helper') as $helper) { @@ -385,16 +420,16 @@ function helper($helpers = array()) continue; } - $ext_helper = APPPATH.'helpers/'.config_item('subclass_prefix').$helper.EXT; + $ext_helper = APPPATH.'helpers/'.config_item('subclass_prefix').$helper.'.php'; // Is this a helper extension request? if (file_exists($ext_helper)) { - $base_helper = BASEPATH.'helpers/'.$helper.EXT; + $base_helper = BASEPATH.'helpers/'.$helper.'.php'; if ( ! file_exists($base_helper)) { - show_error('Unable to load the requested file: helpers/'.$helper.EXT); + show_error('Unable to load the requested file: helpers/'.$helper.'.php'); } include_once($ext_helper); @@ -408,9 +443,9 @@ function helper($helpers = array()) // Try to load the helper foreach ($this->_ci_helper_paths as $path) { - if (file_exists($path.'helpers/'.$helper.EXT)) + if (file_exists($path.'helpers/'.$helper.'.php')) { - include_once($path.'helpers/'.$helper.EXT); + include_once($path.'helpers/'.$helper.'.php'); $this->_ci_helpers[$helper] = TRUE; log_message('debug', 'Helper loaded: '.$helper); @@ -421,7 +456,7 @@ function helper($helpers = array()) // unable to load the helper if ( ! isset($this->_ci_helpers[$helper])) { - show_error('Unable to load the requested file: helpers/'.$helper.EXT); + show_error('Unable to load the requested file: helpers/'.$helper.'.php'); } } } @@ -434,11 +469,10 @@ function helper($helpers = array()) * This is simply an alias to the above function in case the * user has written the plural form of this function. * - * @access public * @param array * @return void */ - function helpers($helpers = array()) + public function helpers($helpers = array()) { $this->helper($helpers); } @@ -448,12 +482,11 @@ function helpers($helpers = array()) /** * Loads a language file * - * @access public * @param array * @param string * @return void */ - function language($file = array(), $lang = '') + public function language($file = array(), $lang = '') { $CI =& get_instance(); @@ -473,11 +506,10 @@ function language($file = array(), $lang = '') /** * Loads a config file * - * @access public * @param string * @return void */ - function config($file = '', $use_sections = FALSE, $fail_gracefully = FALSE) + public function config($file = '', $use_sections = FALSE, $fail_gracefully = FALSE) { $CI =& get_instance(); $CI->config->load($file, $use_sections, $fail_gracefully); @@ -495,12 +527,12 @@ function config($file = '', $use_sections = FALSE, $fail_gracefully = FALSE) * @param string an optional object name * @return void */ - function driver($library = '', $params = NULL, $object_name = NULL) + public function driver($library = '', $params = NULL, $object_name = NULL) { if ( ! class_exists('CI_Driver_Library')) { // we aren't instantiating an object here, that'll be done by the Library itself - require BASEPATH.'libraries/Driver'.EXT; + require BASEPATH.'libraries/Driver.php'; } // We can save the loader some time since Drivers will *always* be in a subfolder, @@ -520,18 +552,20 @@ function driver($library = '', $params = NULL, $object_name = NULL) * * Prepends a parent path to the library, model, helper, and config path arrays * - * @access public * @param string + * @param boolean * @return void */ - function add_package_path($path) + public function add_package_path($path, $view_cascade=TRUE) { $path = rtrim($path, '/').'/'; - + array_unshift($this->_ci_library_paths, $path); array_unshift($this->_ci_model_paths, $path); array_unshift($this->_ci_helper_paths, $path); + $this->_ci_view_paths = array($path.'views/' => $view_cascade) + $this->_ci_view_paths; + // Add config file path $config =& $this->_ci_get_component('config'); array_unshift($config->_config_paths, $path); @@ -544,11 +578,10 @@ function add_package_path($path) * * Return a list of all package paths, by default it will ignore BASEPATH. * - * @access public * @param string * @return void */ - function get_package_paths($include_base = FALSE) + public function get_package_paths($include_base = FALSE) { return $include_base === TRUE ? $this->_ci_library_paths : $this->_ci_model_paths; } @@ -561,11 +594,10 @@ function get_package_paths($include_base = FALSE) * Remove a path from the library, model, and helper path arrays if it exists * If no path is provided, the most recently added path is removed. * - * @access public * @param type * @return type */ - function remove_package_path($path = '', $remove_config_path = TRUE) + public function remove_package_path($path = '', $remove_config_path = TRUE) { $config =& $this->_ci_get_component('config'); @@ -574,12 +606,12 @@ function remove_package_path($path = '', $remove_config_path = TRUE) $void = array_shift($this->_ci_library_paths); $void = array_shift($this->_ci_model_paths); $void = array_shift($this->_ci_helper_paths); + $void = array_shift($this->_ci_view_paths); $void = array_shift($config->_config_paths); } else { $path = rtrim($path, '/').'/'; - foreach (array('_ci_library_paths', '_ci_model_paths', '_ci_helper_paths') as $var) { if (($key = array_search($path, $this->{$var})) !== FALSE) @@ -587,6 +619,11 @@ function remove_package_path($path = '', $remove_config_path = TRUE) unset($this->{$var}[$key]); } } + + if (isset($this->_ci_view_paths[$path.'views/'])) + { + unset($this->_ci_view_paths[$path.'views/']); + } if (($key = array_search($path, $config->_config_paths)) !== FALSE) { @@ -598,6 +635,7 @@ function remove_package_path($path = '', $remove_config_path = TRUE) $this->_ci_library_paths = array_unique(array_merge($this->_ci_library_paths, array(APPPATH, BASEPATH))); $this->_ci_helper_paths = array_unique(array_merge($this->_ci_helper_paths, array(APPPATH, BASEPATH))); $this->_ci_model_paths = array_unique(array_merge($this->_ci_model_paths, array(APPPATH))); + $this->_ci_view_paths = array_merge($this->_ci_view_paths, array(APPPATH.'views/' => TRUE)); $config->_config_paths = array_unique(array_merge($config->_config_paths, array(APPPATH))); } @@ -610,32 +648,47 @@ function remove_package_path($path = '', $remove_config_path = TRUE) * Variables are prefixed with _ci_ to avoid symbol collision with * variables made available to view files * - * @access private * @param array * @return void */ - function _ci_load($_ci_data) + protected function _ci_load($_ci_data) { // Set the default data variables foreach (array('_ci_view', '_ci_vars', '_ci_path', '_ci_return') as $_ci_val) { $$_ci_val = ( ! isset($_ci_data[$_ci_val])) ? FALSE : $_ci_data[$_ci_val]; } + + $file_exists = FALSE; // Set the path to the requested file - if ($_ci_path == '') + if ($_ci_path != '') { - $_ci_ext = pathinfo($_ci_view, PATHINFO_EXTENSION); - $_ci_file = ($_ci_ext == '') ? $_ci_view.EXT : $_ci_view; - $_ci_path = $this->_ci_view_path.$_ci_file; + $_ci_x = explode('/', $_ci_path); + $_ci_file = end($_ci_x); } else { - $_ci_x = explode('/', $_ci_path); - $_ci_file = end($_ci_x); + $_ci_ext = pathinfo($_ci_view, PATHINFO_EXTENSION); + $_ci_file = ($_ci_ext == '') ? $_ci_view.'.php' : $_ci_view; + + foreach ($this->_ci_view_paths as $view_file => $cascade) + { + if (file_exists($view_file.$_ci_file)) + { + $_ci_path = $view_file.$_ci_file; + $file_exists = TRUE; + break; + } + + if ( ! $cascade) + { + break; + } + } } - if ( ! file_exists($_ci_path)) + if ( ! $file_exists && ! file_exists($_ci_path)) { show_error('Unable to load the requested file: '.$_ci_file); } @@ -731,18 +784,17 @@ function _ci_load($_ci_data) * * This function loads the requested class. * - * @access private * @param string the item that is being loaded * @param mixed any additional parameters * @param string an optional object name * @return void */ - function _ci_load_class($class, $params = NULL, $object_name = NULL) + protected function _ci_load_class($class, $params = NULL, $object_name = NULL) { // Get the class name, and while we're at it trim any slashes. // The directory path can be included as part of the class name, // but we don't want a leading slash - $class = str_replace(EXT, '', trim($class, '/')); + $class = str_replace('.php', '', trim($class, '/')); // Was the path included with the class name? // We look for a slash to determine this @@ -759,12 +811,12 @@ function _ci_load_class($class, $params = NULL, $object_name = NULL) // We'll test for both lowercase and capitalized versions of the file name foreach (array(ucfirst($class), strtolower($class)) as $class) { - $subclass = APPPATH.'libraries/'.$subdir.config_item('subclass_prefix').$class.EXT; + $subclass = APPPATH.'libraries/'.$subdir.config_item('subclass_prefix').$class.'.php'; // Is this a class extension request? if (file_exists($subclass)) { - $baseclass = BASEPATH.'libraries/'.ucfirst($class).EXT; + $baseclass = BASEPATH.'libraries/'.ucfirst($class).'.php'; if ( ! file_exists($baseclass)) { @@ -803,7 +855,7 @@ function _ci_load_class($class, $params = NULL, $object_name = NULL) $is_duplicate = FALSE; foreach ($this->_ci_library_paths as $path) { - $filepath = $path.'libraries/'.$subdir.$class.EXT; + $filepath = $path.'libraries/'.$subdir.$class.'.php'; // Does the file exist? No? Bummer... if ( ! file_exists($filepath)) @@ -859,13 +911,12 @@ function _ci_load_class($class, $params = NULL, $object_name = NULL) /** * Instantiates a class * - * @access private * @param string * @param string * @param string an optional object name * @return null */ - function _ci_init_class($class, $prefix = '', $config = FALSE, $object_name = NULL) + protected function _ci_init_class($class, $prefix = '', $config = FALSE, $object_name = NULL) { // Is there an associated config file for this class? Note: these should always be lowercase if ($config === NULL) @@ -880,15 +931,26 @@ function _ci_init_class($class, $prefix = '', $config = FALSE, $object_name = NU foreach ($config_component->_config_paths as $path) { // We test for both uppercase and lowercase, for servers that - // are case-sensitive with regard to file names - if (file_exists($path .'config/'.strtolower($class).EXT)) + // are case-sensitive with regard to file names. Check for environment + // first, global next + if (defined('ENVIRONMENT') AND file_exists($path .'config/'.ENVIRONMENT.'/'.strtolower($class).'.php')) { - include_once($path .'config/'.strtolower($class).EXT); + include_once($path .'config/'.ENVIRONMENT.'/'.strtolower($class).'.php'); break; } - elseif (file_exists($path .'config/'.ucfirst(strtolower($class)).EXT)) + elseif (defined('ENVIRONMENT') AND file_exists($path .'config/'.ENVIRONMENT.'/'.ucfirst(strtolower($class)).'.php')) { - include_once($path .'config/'.ucfirst(strtolower($class)).EXT); + include_once($path .'config/'.ENVIRONMENT.'/'.ucfirst(strtolower($class)).'.php'); + break; + } + elseif (file_exists($path .'config/'.strtolower($class).'.php')) + { + include_once($path .'config/'.strtolower($class).'.php'); + break; + } + elseif (file_exists($path .'config/'.ucfirst(strtolower($class)).'.php')) + { + include_once($path .'config/'.ucfirst(strtolower($class)).'.php'); break; } } @@ -958,13 +1020,23 @@ function _ci_init_class($class, $prefix = '', $config = FALSE, $object_name = NU * The config/autoload.php file contains an array that permits sub-systems, * libraries, and helpers to be loaded automatically. * - * @access private + * This function is public, as it's used in the CI_Controller class. + * However, there is no reason you should ever needs to use it. + * * @param array * @return void */ - function _ci_autoloader() + public function ci_autoloader() { - include_once(APPPATH.'config/autoload'.EXT); + if (defined('ENVIRONMENT') AND file_exists(APPPATH.'config/'.ENVIRONMENT.'/autoload.php')) + { + include_once(APPPATH.'config/'.ENVIRONMENT.'/autoload.php'); + } + else + { + include_once(APPPATH.'config/autoload.php'); + } + if ( ! isset($autoload)) { @@ -1037,11 +1109,10 @@ function _ci_autoloader() * * Takes an object as input and converts the class variables to array key/vals * - * @access private * @param object * @return array */ - function _ci_object_to_array($object) + protected function _ci_object_to_array($object) { return (is_object($object)) ? get_object_vars($object) : $object; } @@ -1051,10 +1122,9 @@ function _ci_object_to_array($object) /** * Get a reference to a specific library or model * - * @access private * @return bool */ - function &_ci_get_component($component) + protected function &_ci_get_component($component) { $CI =& get_instance(); return $CI->$component; @@ -1067,28 +1137,25 @@ function &_ci_get_component($component) * * This function preps the name of various items to make loading them more reliable. * - * @access private * @param mixed * @return array */ - function _ci_prep_filename($filename, $extension) + protected function _ci_prep_filename($filename, $extension) { if ( ! is_array($filename)) { - return array(strtolower(str_replace(EXT, '', str_replace($extension, '', $filename)).$extension)); + return array(strtolower(str_replace('.php', '', str_replace($extension, '', $filename)).$extension)); } else { foreach ($filename as $key => $val) { - $filename[$key] = strtolower(str_replace(EXT, '', str_replace($extension, '', $val)).$extension); + $filename[$key] = strtolower(str_replace('.php', '', str_replace($extension, '', $val)).$extension); } return $filename; } } - - } /* End of file Loader.php */ diff --git a/system/core/Output.php b/system/core/Output.php index 7fb9f791..05ace919 100755 --- a/system/core/Output.php +++ b/system/core/Output.php @@ -28,19 +28,32 @@ */ class CI_Output { - var $final_output; - var $cache_expiration = 0; - var $headers = array(); - var $enable_profiler = FALSE; - var $parse_exec_vars = TRUE; // whether or not to parse variables like {elapsed_time} and {memory_usage} - - var $_zlib_oc = FALSE; - var $_profiler_sections = array(); + protected $final_output; + protected $cache_expiration = 0; + protected $headers = array(); + protected $mime_types = array(); + protected $enable_profiler = FALSE; + protected $_zlib_oc = FALSE; + protected $_profiler_sections = array(); + protected $parse_exec_vars = TRUE; // whether or not to parse variables like {elapsed_time} and {memory_usage} function __construct() { $this->_zlib_oc = @ini_get('zlib.output_compression'); + // Get mime types for later + if (defined('ENVIRONMENT') AND file_exists(APPPATH.'config/'.ENVIRONMENT.'/mimes.php')) + { + include APPPATH.'config/'.ENVIRONMENT.'/mimes.php'; + } + else + { + include APPPATH.'config/mimes.php'; + } + + + $this->mime_types = $mimes; + log_message('debug', "Output Class Initialized"); } @@ -73,6 +86,8 @@ function get_output() function set_output($output) { $this->final_output = $output; + + return $this; } // -------------------------------------------------------------------- @@ -96,6 +111,8 @@ function append_output($output) { $this->final_output .= $output; } + + return $this; } // -------------------------------------------------------------------- @@ -125,6 +142,42 @@ function set_header($header, $replace = TRUE) } $this->headers[] = array($header, $replace); + + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Set Content Type Header + * + * @access public + * @param string extension of the file we're outputting + * @return void + */ + function set_content_type($mime_type) + { + if (strpos($mime_type, '/') === FALSE) + { + $extension = ltrim($mime_type, '.'); + + // Is this extension supported? + if (isset($this->mime_types[$extension])) + { + $mime_type =& $this->mime_types[$extension]; + + if (is_array($mime_type)) + { + $mime_type = current($mime_type); + } + } + } + + $header = 'Content-Type: '.$mime_type; + + $this->headers[] = array($header, TRUE); + + return $this; } // -------------------------------------------------------------------- @@ -141,6 +194,8 @@ function set_header($header, $replace = TRUE) function set_status_header($code = 200, $text = '') { set_status_header($code, $text); + + return $this; } // -------------------------------------------------------------------- @@ -155,6 +210,8 @@ function set_status_header($code = 200, $text = '') function enable_profiler($val = TRUE) { $this->enable_profiler = (is_bool($val)) ? $val : TRUE; + + return $this; } // -------------------------------------------------------------------- @@ -174,6 +231,8 @@ function set_profiler_sections($sections) { $this->_profiler_sections[$section] = ($enable !== FALSE) ? TRUE : FALSE; } + + return $this; } // -------------------------------------------------------------------- @@ -188,6 +247,8 @@ function set_profiler_sections($sections) function cache($time) { $this->cache_expiration = ( ! is_numeric($time)) ? 0 : $time; + + return $this; } // -------------------------------------------------------------------- diff --git a/system/core/Router.php b/system/core/Router.php index 7be508fe..5e92a04b 100755 --- a/system/core/Router.php +++ b/system/core/Router.php @@ -87,7 +87,15 @@ function _set_routing() } // Load the routes.php file. - @include(APPPATH.'config/routes'.EXT); + if (defined('ENVIRONMENT') AND is_file(APPPATH.'config/'.ENVIRONMENT.'/routes.php')) + { + include(APPPATH.'config/'.ENVIRONMENT.'/routes.php'); + } + elseif (is_file(APPPATH.'config/routes.php')) + { + include(APPPATH.'config/routes.php'); + } + $this->routes = ( ! isset($route) OR ! is_array($route)) ? array() : $route; unset($route); @@ -219,7 +227,7 @@ function _validate_request($segments) } // Does the requested controller exist in the root folder? - if (file_exists(APPPATH.'controllers/'.$segments[0].EXT)) + if (file_exists(APPPATH.'controllers/'.$segments[0].'.php')) { return $segments; } @@ -234,7 +242,7 @@ function _validate_request($segments) if (count($segments) > 0) { // Does the requested controller exist in the sub-folder? - if ( ! file_exists(APPPATH.'controllers/'.$this->fetch_directory().$segments[0].EXT)) + if ( ! file_exists(APPPATH.'controllers/'.$this->fetch_directory().$segments[0].'.php')) { show_404($this->fetch_directory().$segments[0]); } @@ -256,7 +264,7 @@ function _validate_request($segments) } // Does the default controller exist in the sub-folder? - if ( ! file_exists(APPPATH.'controllers/'.$this->fetch_directory().$this->default_controller.EXT)) + if ( ! file_exists(APPPATH.'controllers/'.$this->fetch_directory().$this->default_controller.'.php')) { $this->directory = ''; return array(); @@ -270,7 +278,7 @@ function _validate_request($segments) // If we've gotten this far it means that the URI does not correlate to a valid // controller class. We will now see if there is an override - if (!empty($this->routes['404_override'])) + if ( ! empty($this->routes['404_override'])) { $x = explode('/', $this->routes['404_override']); diff --git a/system/core/URI.php b/system/core/URI.php index 99901594..20f0f006 100755 --- a/system/core/URI.php +++ b/system/core/URI.php @@ -61,17 +61,17 @@ function _fetch_uri_string() { if (strtoupper($this->config->item('uri_protocol')) == 'AUTO') { - // Arguments exist, it must be a command line request - if ( ! empty($_SERVER['argv'])) + // Is the request coming from the command line? + if (php_sapi_name() == 'cli' or defined('STDIN')) { - $this->uri_string = $this->_parse_cli_args(); + $this->_set_uri_string($this->_parse_cli_args()); return; } // Let's try the REQUEST_URI first, this will work in most situations if ($uri = $this->_detect_uri()) { - $this->uri_string = $uri; + $this->_set_uri_string($uri); return; } @@ -80,7 +80,7 @@ function _fetch_uri_string() $path = (isset($_SERVER['PATH_INFO'])) ? $_SERVER['PATH_INFO'] : @getenv('PATH_INFO'); if (trim($path, '/') != '' && $path != "/".SELF) { - $this->uri_string = $path; + $this->_set_uri_string($path); return; } @@ -88,43 +88,54 @@ function _fetch_uri_string() $path = (isset($_SERVER['QUERY_STRING'])) ? $_SERVER['QUERY_STRING'] : @getenv('QUERY_STRING'); if (trim($path, '/') != '') { - $this->uri_string = $path; + $this->_set_uri_string($path); return; } // As a last ditch effort lets try using the $_GET array if (is_array($_GET) && count($_GET) == 1 && trim(key($_GET), '/') != '') { - $this->uri_string = key($_GET); + $this->_set_uri_string(key($_GET)); return; } // We've exhausted all our options... $this->uri_string = ''; + return; } - else - { - $uri = strtoupper($this->config->item('uri_protocol')); - if ($uri == 'REQUEST_URI') - { - $this->uri_string = $this->_detect_uri(); - return; - } - elseif ($uri == 'CLI') - { - $this->uri_string = $this->_parse_cli_args(); - return; - } + $uri = strtoupper($this->config->item('uri_protocol')); - $this->uri_string = (isset($_SERVER[$uri])) ? $_SERVER[$uri] : @getenv($uri); + if ($uri == 'REQUEST_URI') + { + $this->_set_uri_string($this->_detect_uri()); + return; } - - // If the URI contains only a slash we'll kill it - if ($this->uri_string == '/') + elseif ($uri == 'CLI') { - $this->uri_string = ''; + $this->_set_uri_string($this->_parse_cli_args()); + return; } + + $path = (isset($_SERVER[$uri])) ? $_SERVER[$uri] : @getenv($uri); + $this->_set_uri_string($path); + } + + // -------------------------------------------------------------------- + + /** + * Set the URI String + * + * @access public + * @return string + */ + function _set_uri_string($str) + { + // Filter out control characters + $str = remove_invisible_characters($str, FALSE); + + // If the URI contains only a slash we'll kill it + $this->uri_string = ($str == '/') ? '' : $str; } // -------------------------------------------------------------------- @@ -140,7 +151,7 @@ function _fetch_uri_string() */ private function _detect_uri() { - if ( ! isset($_SERVER['REQUEST_URI'])) + if ( ! isset($_SERVER['REQUEST_URI']) OR ! isset($_SERVER['SCRIPT_NAME'])) { return ''; } @@ -173,6 +184,12 @@ private function _detect_uri() $_SERVER['QUERY_STRING'] = ''; $_GET = array(); } + + if ($uri == '/' || empty($uri)) + { + return '/'; + } + $uri = parse_url(http://github.com/$uri,%20PHP_URL_PATH); // Do some final cleaning of the URI and return it diff --git a/system/core/Utf8.php b/system/core/Utf8.php index 5d5a7ef7..2a27d1f3 100755 --- a/system/core/Utf8.php +++ b/system/core/Utf8.php @@ -107,7 +107,7 @@ function clean_string($str) */ function safe_ascii_for_xml($str) { - return preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+/S', '', $str); + return remove_invisible_characters($str, FALSE); } // -------------------------------------------------------------------- diff --git a/system/database/DB.php b/system/database/DB.php index fb0516ba..33207d88 100755 --- a/system/database/DB.php +++ b/system/database/DB.php @@ -27,7 +27,16 @@ function &DB($params = '', $active_record_override = NULL) // Load the DB config file if a DSN string wasn't passed if (is_string($params) AND strpos($params, '://') === FALSE) { - include(APPPATH.'config/database'.EXT); + // Is the config file in the environment folder? + if ( ! defined('ENVIRONMENT') OR ! file_exists($file_path = APPPATH.'config/'.ENVIRONMENT.'/database.php')) + { + if ( ! file_exists($file_path = APPPATH.'config/database.php')) + { + show_error('The configuration file database.php does not exist.'); + } + } + + include($file_path); if ( ! isset($db) OR count($db) == 0) { @@ -74,7 +83,7 @@ function &DB($params = '', $active_record_override = NULL) { parse_str($dns['query'], $extra); - foreach($extra as $key => $val) + foreach ($extra as $key => $val) { // booleans please if (strtoupper($val) == "TRUE") @@ -107,11 +116,11 @@ function &DB($params = '', $active_record_override = NULL) $active_record = $active_record_override; } - require_once(BASEPATH.'database/DB_driver'.EXT); + require_once(BASEPATH.'database/DB_driver.php'); if ( ! isset($active_record) OR $active_record == TRUE) { - require_once(BASEPATH.'database/DB_active_rec'.EXT); + require_once(BASEPATH.'database/DB_active_rec.php'); if ( ! class_exists('CI_DB')) { @@ -126,7 +135,7 @@ function &DB($params = '', $active_record_override = NULL) } } - require_once(BASEPATH.'database/drivers/'.$params['dbdriver'].'/'.$params['dbdriver'].'_driver'.EXT); + require_once(BASEPATH.'database/drivers/'.$params['dbdriver'].'/'.$params['dbdriver'].'_driver.php'); // Instantiate the DB adapter $driver = 'CI_DB_'.$params['dbdriver'].'_driver'; diff --git a/system/database/DB_active_rec.php b/system/database/DB_active_rec.php index ce9d1c1a..2af3553e 100755 --- a/system/database/DB_active_rec.php +++ b/system/database/DB_active_rec.php @@ -58,7 +58,9 @@ class CI_DB_active_record extends CI_DB_driver { var $ar_cache_having = array(); var $ar_cache_orderby = array(); var $ar_cache_set = array(); - + + var $ar_no_escape = array(); + var $ar_cache_no_escape = array(); // -------------------------------------------------------------------- @@ -67,18 +69,11 @@ class CI_DB_active_record extends CI_DB_driver { * * Generates the SELECT portion of the query * - * @access public * @param string * @return object */ - function select($select = '*', $escape = NULL) + public function select($select = '*', $escape = NULL) { - // Set the global value if this was sepecified - if (is_bool($escape)) - { - $this->_protect_identifiers = $escape; - } - if (is_string($select)) { $select = explode(',', $select); @@ -91,11 +86,13 @@ function select($select = '*', $escape = NULL) if ($val != '') { $this->ar_select[] = $val; + $this->ar_no_escape[] = $escape; if ($this->ar_caching === TRUE) { $this->ar_cache_select[] = $val; $this->ar_cache_exists[] = 'select'; + $this->ar_cache_no_escape[] = $escape; } } } @@ -109,12 +106,11 @@ function select($select = '*', $escape = NULL) * * Generates a SELECT MAX(field) portion of a query * - * @access public * @param string the field * @param string an alias * @return object */ - function select_max($select = '', $alias = '') + public function select_max($select = '', $alias = '') { return $this->_max_min_avg_sum($select, $alias, 'MAX'); } @@ -126,12 +122,11 @@ function select_max($select = '', $alias = '') * * Generates a SELECT MIN(field) portion of a query * - * @access public * @param string the field * @param string an alias * @return object */ - function select_min($select = '', $alias = '') + public function select_min($select = '', $alias = '') { return $this->_max_min_avg_sum($select, $alias, 'MIN'); } @@ -143,12 +138,11 @@ function select_min($select = '', $alias = '') * * Generates a SELECT AVG(field) portion of a query * - * @access public * @param string the field * @param string an alias * @return object */ - function select_avg($select = '', $alias = '') + public function select_avg($select = '', $alias = '') { return $this->_max_min_avg_sum($select, $alias, 'AVG'); } @@ -160,12 +154,11 @@ function select_avg($select = '', $alias = '') * * Generates a SELECT SUM(field) portion of a query * - * @access public * @param string the field * @param string an alias * @return object */ - function select_sum($select = '', $alias = '') + public function select_sum($select = '', $alias = '') { return $this->_max_min_avg_sum($select, $alias, 'SUM'); } @@ -180,12 +173,11 @@ function select_sum($select = '', $alias = '') * select_avg() * select_sum() * - * @access public * @param string the field * @param string an alias * @return object */ - function _max_min_avg_sum($select = '', $alias = '', $type = 'MAX') + protected function _max_min_avg_sum($select = '', $alias = '', $type = 'MAX') { if ( ! is_string($select) OR $select == '') { @@ -222,11 +214,10 @@ function _max_min_avg_sum($select = '', $alias = '', $type = 'MAX') /** * Determines the alias name based on the table * - * @access private * @param string * @return string */ - function _create_alias_from_table($item) + protected function _create_alias_from_table($item) { if (strpos($item, '.') !== FALSE) { @@ -243,11 +234,10 @@ function _create_alias_from_table($item) * * Sets a flag which tells the query string compiler to add DISTINCT * - * @access public * @param bool * @return object */ - function distinct($val = TRUE) + public function distinct($val = TRUE) { $this->ar_distinct = (is_bool($val)) ? $val : TRUE; return $this; @@ -260,11 +250,10 @@ function distinct($val = TRUE) * * Generates the FROM portion of the query * - * @access public * @param mixed can be a string or array * @return object */ - function from($from) + public function from($from) { foreach ((array)$from as $val) { @@ -313,13 +302,12 @@ function from($from) * * Generates the JOIN portion of the query * - * @access public * @param string * @param string the join condition * @param string the type of join * @return object */ - function join($table, $cond, $type = '') + public function join($table, $cond, $type = '') { if ($type != '') { @@ -369,12 +357,11 @@ function join($table, $cond, $type = '') * Generates the WHERE portion of the query. Separates * multiple calls with AND * - * @access public * @param mixed * @param mixed * @return object */ - function where($key, $value = NULL, $escape = TRUE) + public function where($key, $value = NULL, $escape = TRUE) { return $this->_where($key, $value, 'AND ', $escape); } @@ -387,12 +374,11 @@ function where($key, $value = NULL, $escape = TRUE) * Generates the WHERE portion of the query. Separates * multiple calls with OR * - * @access public * @param mixed * @param mixed * @return object */ - function or_where($key, $value = NULL, $escape = TRUE) + public function or_where($key, $value = NULL, $escape = TRUE) { return $this->_where($key, $value, 'OR ', $escape); } @@ -402,15 +388,14 @@ function or_where($key, $value = NULL, $escape = TRUE) /** * Where * - * Called by where() or orwhere() + * Called by where() or or_where() * - * @access private * @param mixed * @param mixed * @param string * @return object */ - function _where($key, $value = NULL, $type = 'AND ', $escape = NULL) + protected function _where($key, $value = NULL, $type = 'AND ', $escape = NULL) { if ( ! is_array($key)) { @@ -441,10 +426,10 @@ function _where($key, $value = NULL, $type = 'AND ', $escape = NULL) $v = ' '.$this->escape($v); } - + if ( ! $this->_has_operator($k)) { - $k .= ' ='; + $k .= ' = '; } } else @@ -473,12 +458,11 @@ function _where($key, $value = NULL, $type = 'AND ', $escape = NULL) * Generates a WHERE field IN ('item', 'item') SQL query joined with * AND if appropriate * - * @access public * @param string The field to search * @param array The values searched on * @return object */ - function where_in($key = NULL, $values = NULL) + public function where_in($key = NULL, $values = NULL) { return $this->_where_in($key, $values); } @@ -491,12 +475,11 @@ function where_in($key = NULL, $values = NULL) * Generates a WHERE field IN ('item', 'item') SQL query joined with * OR if appropriate * - * @access public * @param string The field to search * @param array The values searched on * @return object */ - function or_where_in($key = NULL, $values = NULL) + public function or_where_in($key = NULL, $values = NULL) { return $this->_where_in($key, $values, FALSE, 'OR '); } @@ -509,12 +492,11 @@ function or_where_in($key = NULL, $values = NULL) * Generates a WHERE field NOT IN ('item', 'item') SQL query joined * with AND if appropriate * - * @access public * @param string The field to search * @param array The values searched on * @return object */ - function where_not_in($key = NULL, $values = NULL) + public function where_not_in($key = NULL, $values = NULL) { return $this->_where_in($key, $values, TRUE); } @@ -527,12 +509,11 @@ function where_not_in($key = NULL, $values = NULL) * Generates a WHERE field NOT IN ('item', 'item') SQL query joined * with OR if appropriate * - * @access public * @param string The field to search * @param array The values searched on * @return object */ - function or_where_not_in($key = NULL, $values = NULL) + public function or_where_not_in($key = NULL, $values = NULL) { return $this->_where_in($key, $values, TRUE, 'OR '); } @@ -544,14 +525,13 @@ function or_where_not_in($key = NULL, $values = NULL) * * Called by where_in, where_in_or, where_not_in, where_not_in_or * - * @access public * @param string The field to search * @param array The values searched on * @param boolean If the statement would be IN or NOT IN * @param string * @return object */ - function _where_in($key = NULL, $values = NULL, $not = FALSE, $type = 'AND ') + protected function _where_in($key = NULL, $values = NULL, $not = FALSE, $type = 'AND ') { if ($key === NULL OR $values === NULL) { @@ -594,12 +574,11 @@ function _where_in($key = NULL, $values = NULL, $not = FALSE, $type = 'AND ') * Generates a %LIKE% portion of the query. Separates * multiple calls with AND * - * @access public * @param mixed * @param mixed * @return object */ - function like($field, $match = '', $side = 'both') + public function like($field, $match = '', $side = 'both') { return $this->_like($field, $match, 'AND ', $side); } @@ -612,12 +591,11 @@ function like($field, $match = '', $side = 'both') * Generates a NOT LIKE portion of the query. Separates * multiple calls with AND * - * @access public * @param mixed * @param mixed * @return object */ - function not_like($field, $match = '', $side = 'both') + public function not_like($field, $match = '', $side = 'both') { return $this->_like($field, $match, 'AND ', $side, 'NOT'); } @@ -630,12 +608,11 @@ function not_like($field, $match = '', $side = 'both') * Generates a %LIKE% portion of the query. Separates * multiple calls with OR * - * @access public * @param mixed * @param mixed * @return object */ - function or_like($field, $match = '', $side = 'both') + public function or_like($field, $match = '', $side = 'both') { return $this->_like($field, $match, 'OR ', $side); } @@ -648,12 +625,11 @@ function or_like($field, $match = '', $side = 'both') * Generates a NOT LIKE portion of the query. Separates * multiple calls with OR * - * @access public * @param mixed * @param mixed * @return object */ - function or_not_like($field, $match = '', $side = 'both') + public function or_not_like($field, $match = '', $side = 'both') { return $this->_like($field, $match, 'OR ', $side, 'NOT'); } @@ -665,13 +641,12 @@ function or_not_like($field, $match = '', $side = 'both') * * Called by like() or orlike() * - * @access private * @param mixed * @param mixed * @param string * @return object */ - function _like($field, $match = '', $type = 'AND ', $side = 'both', $not = '') + protected function _like($field, $match = '', $type = 'AND ', $side = 'both', $not = '') { if ( ! is_array($field)) { @@ -721,11 +696,10 @@ function _like($field, $match = '', $type = 'AND ', $side = 'both', $not = '') /** * GROUP BY * - * @access public * @param string * @return object */ - function group_by($by) + public function group_by($by) { if (is_string($by)) { @@ -757,12 +731,11 @@ function group_by($by) * * Separates multiple calls with AND * - * @access public * @param string * @param string * @return object */ - function having($key, $value = '', $escape = TRUE) + public function having($key, $value = '', $escape = TRUE) { return $this->_having($key, $value, 'AND ', $escape); } @@ -774,12 +747,11 @@ function having($key, $value = '', $escape = TRUE) * * Separates multiple calls with OR * - * @access public * @param string * @param string * @return object */ - function or_having($key, $value = '', $escape = TRUE) + public function or_having($key, $value = '', $escape = TRUE) { return $this->_having($key, $value, 'OR ', $escape); } @@ -791,12 +763,11 @@ function or_having($key, $value = '', $escape = TRUE) * * Called by having() or or_having() * - * @access private * @param string * @param string * @return object */ - function _having($key, $value = '', $type = 'AND ', $escape = TRUE) + protected function _having($key, $value = '', $type = 'AND ', $escape = TRUE) { if ( ! is_array($key)) { @@ -819,7 +790,7 @@ function _having($key, $value = '', $type = 'AND ', $escape = TRUE) if ($v != '') { - $v = ' '.$this->escape_str($v); + $v = ' '.$this->escape($v); } $this->ar_having[] = $prefix.$k.$v; @@ -838,12 +809,11 @@ function _having($key, $value = '', $type = 'AND ', $escape = TRUE) /** * Sets the ORDER BY value * - * @access public * @param string * @param string direction: asc or desc * @return object */ - function order_by($orderby, $direction = '') + public function order_by($orderby, $direction = '') { if (strtolower($direction) == 'random') { @@ -894,12 +864,11 @@ function order_by($orderby, $direction = '') /** * Sets the LIMIT value * - * @access public * @param integer the limit value * @param integer the offset value * @return object */ - function limit($value, $offset = '') + public function limit($value, $offset = '') { $this->ar_limit = $value; @@ -916,11 +885,10 @@ function limit($value, $offset = '') /** * Sets the OFFSET value * - * @access public * @param integer the offset value * @return object */ - function offset($offset) + public function offset($offset) { $this->ar_offset = $offset; return $this; @@ -931,13 +899,12 @@ function offset($offset) /** * The "set" function. Allows key/value pairs to be set for inserting or updating * - * @access public * @param mixed * @param string * @param boolean * @return object */ - function set($key, $value = '', $escape = TRUE) + public function set($key, $value = '', $escape = TRUE) { $key = $this->_object_to_array($key); @@ -954,7 +921,7 @@ function set($key, $value = '', $escape = TRUE) } else { - $this->ar_set[$this->_protect_identifiers($k)] = $this->escape($v); + $this->ar_set[$this->_protect_identifiers($k, FALSE, TRUE)] = $this->escape($v); } } @@ -969,13 +936,12 @@ function set($key, $value = '', $escape = TRUE) * Compiles the select statement based on the other functions called * and runs the query * - * @access public * @param string the table * @param string the limit clause * @param string the offset clause * @return object */ - function get($table = '', $limit = null, $offset = null) + public function get($table = '', $limit = null, $offset = null) { if ($table != '') { @@ -1001,11 +967,10 @@ function get($table = '', $limit = null, $offset = null) * Generates a platform-specific query string that counts all records * returned by an Active Record query. * - * @access public * @param string * @return string */ - function count_all_results($table = '') + public function count_all_results($table = '') { if ($table != '') { @@ -1020,11 +985,11 @@ function count_all_results($table = '') if ($query->num_rows() == 0) { - return '0'; + return 0; } $row = $query->row(); - return $row->numrows; + return (int) $row->numrows; } // -------------------------------------------------------------------- @@ -1034,13 +999,12 @@ function count_all_results($table = '') * * Allows the where clause, limit and offset to be added directly * - * @access public * @param string the where clause * @param string the limit clause * @param string the offset clause * @return object */ - function get_where($table = '', $where = null, $limit = null, $offset = null) + public function get_where($table = '', $where = null, $limit = null, $offset = null) { if ($table != '') { @@ -1071,12 +1035,11 @@ function get_where($table = '', $where = null, $limit = null, $offset = null) * * Compiles batch insert strings and runs the queries * - * @access public * @param string the table to retrieve the results from * @param array an associative array of insert values * @return object */ - function insert_batch($table = '', $set = NULL) + public function insert_batch($table = '', $set = NULL) { if ( ! is_null($set)) { @@ -1129,14 +1092,12 @@ function insert_batch($table = '', $set = NULL) /** * The "set_insert_batch" function. Allows key/value pairs to be set for batch inserts * - * @access public * @param mixed * @param string * @param boolean * @return object */ - - function set_insert_batch($key, $value = '', $escape = TRUE) + public function set_insert_batch($key, $value = '', $escape = TRUE) { $key = $this->_object_to_array_batch($key); @@ -1156,7 +1117,7 @@ function set_insert_batch($key, $value = '', $escape = TRUE) $this->ar_set[] = array(); return; } - + ksort($row); // puts $row in the same order as our keys if ($escape === FALSE) @@ -1167,7 +1128,7 @@ function set_insert_batch($key, $value = '', $escape = TRUE) { $clean = array(); - foreach($row as $value) + foreach ($row as $value) { $clean[] = $this->escape($value); } @@ -1191,8 +1152,7 @@ function set_insert_batch($key, $value = '', $escape = TRUE) * * Compiles an insert string and runs the query * - * @access public - * @param string the table to retrieve the results from + * @param string the table to insert data into * @param array an associative array of insert values * @return object */ @@ -1232,7 +1192,18 @@ function insert($table = '', $set = NULL) return $this->query($sql); } - function replace($table = '', $set = NULL) + // -------------------------------------------------------------------- + + /** + * Replace + * + * Compiles an replace into string and runs the query + * + * @param string the table to replace data into + * @param array an associative array of insert values + * @return object + */ + public function replace($table = '', $set = NULL) { if ( ! is_null($set)) { @@ -1275,13 +1246,12 @@ function replace($table = '', $set = NULL) * * Compiles an update string and runs the query * - * @access public * @param string the table to retrieve the results from * @param array an associative array of update values * @param mixed the where clause * @return object */ - function update($table = '', $set = NULL, $where = NULL, $limit = NULL) + public function update($table = '', $set = NULL, $where = NULL, $limit = NULL) { // Combine any cached components with the current statements $this->_merge_cache(); @@ -1338,13 +1308,12 @@ function update($table = '', $set = NULL, $where = NULL, $limit = NULL) * * Compiles an update string and runs the query * - * @access public * @param string the table to retrieve the results from * @param array an associative array of update values * @param string the where key * @return object */ - function update_batch($table = '', $set = NULL, $index = NULL) + public function update_batch($table = '', $set = NULL, $index = NULL) { // Combine any cached components with the current statements $this->_merge_cache(); @@ -1404,14 +1373,12 @@ function update_batch($table = '', $set = NULL, $index = NULL) /** * The "set_update_batch" function. Allows key/value pairs to be set for batch updating * - * @access public * @param array * @param string * @param boolean * @return object */ - - function set_update_batch($key, $index = '', $escape = TRUE) + public function set_update_batch($key, $index = '', $escape = TRUE) { $key = $this->_object_to_array_batch($key); @@ -1425,7 +1392,7 @@ function set_update_batch($key, $index = '', $escape = TRUE) $index_set = FALSE; $clean = array(); - foreach($v as $k2 => $v2) + foreach ($v as $k2 => $v2) { if ($k2 == $index) { @@ -1464,11 +1431,10 @@ function set_update_batch($key, $index = '', $escape = TRUE) * * Compiles a delete string and runs "DELETE FROM table" * - * @access public * @param string the table to empty * @return object */ - function empty_table($table = '') + public function empty_table($table = '') { if ($table == '') { @@ -1504,11 +1470,10 @@ function empty_table($table = '') * If the database does not support the truncate() command * This function maps to "DELETE FROM table" * - * @access public * @param string the table to truncate * @return object */ - function truncate($table = '') + public function truncate($table = '') { if ($table == '') { @@ -1542,14 +1507,13 @@ function truncate($table = '') * * Compiles a delete string and runs the query * - * @access public * @param mixed the table(s) to delete from. String or array * @param mixed the where clause * @param mixed the limit clause * @param boolean * @return object */ - function delete($table = '', $where = '', $limit = NULL, $reset_data = TRUE) + public function delete($table = '', $where = '', $limit = NULL, $reset_data = TRUE) { // Combine any cached components with the current statements $this->_merge_cache(); @@ -1569,7 +1533,7 @@ function delete($table = '', $where = '', $limit = NULL, $reset_data = TRUE) } elseif (is_array($table)) { - foreach($table as $single_table) + foreach ($table as $single_table) { $this->delete($single_table, $where, $limit, FALSE); } @@ -1619,11 +1583,10 @@ function delete($table = '', $where = '', $limit = NULL, $reset_data = TRUE) * * Prepends a database prefix if one exists in configuration * - * @access public * @param string the table * @return string */ - function dbprefix($table = '') + public function dbprefix($table = '') { if ($table == '') { @@ -1635,16 +1598,30 @@ function dbprefix($table = '') // -------------------------------------------------------------------- + /** + * Set DB Prefix + * + * Set's the DB Prefix to something new without needing to reconnect + * + * @param string the prefix + * @return string + */ + public function set_dbprefix($prefix = '') + { + return $this->dbprefix = $prefix; + } + + // -------------------------------------------------------------------- + /** * Track Aliases * * Used to track SQL statements written with aliased tables. * - * @access private * @param string The table to inspect * @return string */ - function _track_aliases($table) + protected function _track_aliases($table) { if (is_array($table)) { @@ -1687,10 +1664,9 @@ function _track_aliases($table) * Generates a query string based on which functions were used. * Should not be called directly. The get() function calls it. * - * @access private * @return string */ - function _compile_select($select_override = FALSE) + protected function _compile_select($select_override = FALSE) { // Combine any cached components with the current statements $this->_merge_cache(); @@ -1718,7 +1694,8 @@ function _compile_select($select_override = FALSE) // is because until the user calls the from() function we don't know if there are aliases foreach ($this->ar_select as $key => $val) { - $this->ar_select[$key] = $this->_protect_identifiers($val); + $no_escape = isset($this->ar_no_escape[$key]) ? $this->ar_no_escape[$key] : NULL; + $this->ar_select[$key] = $this->_protect_identifiers($val, FALSE, $no_escape); } $sql .= implode(', ', $this->ar_select); @@ -1753,9 +1730,7 @@ function _compile_select($select_override = FALSE) if (count($this->ar_where) > 0 OR count($this->ar_like) > 0) { - $sql .= "\n"; - - $sql .= "WHERE "; + $sql .= "\nWHERE "; } $sql .= implode("\n", $this->ar_where); @@ -1830,11 +1805,10 @@ function _compile_select($select_override = FALSE) * * Takes an object as input and converts the class variables to array key/vals * - * @access public * @param object * @return array */ - function _object_to_array($object) + public function _object_to_array($object) { if ( ! is_object($object)) { @@ -1861,11 +1835,10 @@ function _object_to_array($object) * * Takes an object as input and converts the class variables to array key/vals * - * @access public * @param object * @return array */ - function _object_to_array_batch($object) + public function _object_to_array_batch($object) { if ( ! is_object($object)) { @@ -1901,10 +1874,9 @@ function _object_to_array_batch($object) * * Starts AR caching * - * @access public * @return void */ - function start_cache() + public function start_cache() { $this->ar_caching = TRUE; } @@ -1916,10 +1888,9 @@ function start_cache() * * Stops AR caching * - * @access public * @return void */ - function stop_cache() + public function stop_cache() { $this->ar_caching = FALSE; } @@ -1934,22 +1905,21 @@ function stop_cache() * @access public * @return void */ - function flush_cache() + public function flush_cache() { - $this->_reset_run( - array( - 'ar_cache_select' => array(), - 'ar_cache_from' => array(), - 'ar_cache_join' => array(), - 'ar_cache_where' => array(), - 'ar_cache_like' => array(), - 'ar_cache_groupby' => array(), - 'ar_cache_having' => array(), - 'ar_cache_orderby' => array(), - 'ar_cache_set' => array(), - 'ar_cache_exists' => array() - ) - ); + $this->_reset_run(array( + 'ar_cache_select' => array(), + 'ar_cache_from' => array(), + 'ar_cache_join' => array(), + 'ar_cache_where' => array(), + 'ar_cache_like' => array(), + 'ar_cache_groupby' => array(), + 'ar_cache_having' => array(), + 'ar_cache_orderby' => array(), + 'ar_cache_set' => array(), + 'ar_cache_exists' => array(), + 'ar_cache_no_escape' => array() + )); } // -------------------------------------------------------------------- @@ -1960,10 +1930,9 @@ function flush_cache() * When called, this function merges any cached AR arrays with * locally called ones. * - * @access private * @return void */ - function _merge_cache() + protected function _merge_cache() { if (count($this->ar_cache_exists) == 0) { @@ -1989,6 +1958,8 @@ function _merge_cache() { $this->_track_aliases($this->ar_from); } + + $this->ar_no_escape = $this->ar_cache_no_escape; } // -------------------------------------------------------------------- @@ -1996,11 +1967,10 @@ function _merge_cache() /** * Resets the active record values. Called by the get() function * - * @access private * @param array An array of fields to reset * @return void */ - function _reset_run($ar_reset_items) + protected function _reset_run($ar_reset_items) { foreach ($ar_reset_items as $item => $default_value) { @@ -2016,27 +1986,27 @@ function _reset_run($ar_reset_items) /** * Resets the active record values. Called by the get() function * - * @access private * @return void */ - function _reset_select() + protected function _reset_select() { $ar_reset_items = array( - 'ar_select' => array(), - 'ar_from' => array(), - 'ar_join' => array(), - 'ar_where' => array(), - 'ar_like' => array(), - 'ar_groupby' => array(), - 'ar_having' => array(), - 'ar_orderby' => array(), - 'ar_wherein' => array(), - 'ar_aliased_tables' => array(), - 'ar_distinct' => FALSE, - 'ar_limit' => FALSE, - 'ar_offset' => FALSE, - 'ar_order' => FALSE, - ); + 'ar_select' => array(), + 'ar_from' => array(), + 'ar_join' => array(), + 'ar_where' => array(), + 'ar_like' => array(), + 'ar_groupby' => array(), + 'ar_having' => array(), + 'ar_orderby' => array(), + 'ar_wherein' => array(), + 'ar_aliased_tables' => array(), + 'ar_no_escape' => array(), + 'ar_distinct' => FALSE, + 'ar_limit' => FALSE, + 'ar_offset' => FALSE, + 'ar_order' => FALSE, + ); $this->_reset_run($ar_reset_items); } @@ -2048,25 +2018,23 @@ function _reset_select() * * Called by the insert() update() insert_batch() update_batch() and delete() functions * - * @access private * @return void */ - function _reset_write() + protected function _reset_write() { $ar_reset_items = array( - 'ar_set' => array(), - 'ar_from' => array(), - 'ar_where' => array(), - 'ar_like' => array(), - 'ar_orderby' => array(), - 'ar_keys' => array(), - 'ar_limit' => FALSE, - 'ar_order' => FALSE - ); + 'ar_set' => array(), + 'ar_from' => array(), + 'ar_where' => array(), + 'ar_like' => array(), + 'ar_orderby' => array(), + 'ar_keys' => array(), + 'ar_limit' => FALSE, + 'ar_order' => FALSE + ); $this->_reset_run($ar_reset_items); } - } /* End of file DB_active_rec.php */ diff --git a/system/database/DB_driver.php b/system/database/DB_driver.php index 2d8f592e..10e8ed0c 100755 --- a/system/database/DB_driver.php +++ b/system/database/DB_driver.php @@ -424,8 +424,8 @@ function load_rdriver() if ( ! class_exists($driver)) { - include_once(BASEPATH.'database/DB_result'.EXT); - include_once(BASEPATH.'database/drivers/'.$this->dbdriver.'/'.$this->dbdriver.'_result'.EXT); + include_once(BASEPATH.'database/DB_result.php'); + include_once(BASEPATH.'database/drivers/'.$this->dbdriver.'/'.$this->dbdriver.'_result.php'); } return $driver; @@ -767,7 +767,7 @@ function list_tables($constrain_by_prefix = FALSE) if ($query->num_rows() > 0) { - foreach($query->result_array() as $row) + foreach ($query->result_array() as $row) { if (isset($row['TABLE_NAME'])) { @@ -834,7 +834,7 @@ function list_fields($table = '') $query = $this->query($sql); $retval = array(); - foreach($query->result_array() as $row) + foreach ($query->result_array() as $row) { if (isset($row['COLUMN_NAME'])) { @@ -904,7 +904,7 @@ function insert_string($table, $data) $fields = array(); $values = array(); - foreach($data as $key => $val) + foreach ($data as $key => $val) { $fields[] = $this->_escape_identifiers($key); $values[] = $this->escape($val); @@ -932,7 +932,7 @@ function update_string($table, $data, $where) } $fields = array(); - foreach($data as $key => $val) + foreach ($data as $key => $val) { $fields[$this->_protect_identifiers($key)] = $this->escape($val); } @@ -1115,7 +1115,7 @@ function _cache_init() if ( ! class_exists('CI_DB_Cache')) { - if ( ! @include(BASEPATH.'database/DB_cache'.EXT)) + if ( ! @include(BASEPATH.'database/DB_cache.php')) { return $this->cache_off(); } @@ -1175,7 +1175,7 @@ function display_error($error = '', $swap = '', $native = FALSE) $trace = debug_backtrace(); - foreach($trace as $call) + foreach ($trace as $call) { if (isset($call['file']) && strpos($call['file'], BASEPATH.'database') === FALSE) { @@ -1248,7 +1248,7 @@ function _protect_identifiers($item, $prefix_single = FALSE, $protect_identifier { $escaped_array = array(); - foreach($item as $k => $v) + foreach ($item as $k => $v) { $escaped_array[$this->_protect_identifiers($k)] = $this->_protect_identifiers($v); } diff --git a/system/database/DB_forge.php b/system/database/DB_forge.php index 27f2c372..a71fca78 100755 --- a/system/database/DB_forge.php +++ b/system/database/DB_forge.php @@ -99,7 +99,7 @@ function add_key($key = '', $primary = FALSE) { if (is_array($key)) { - foreach($key as $one) + foreach ($key as $one) { $this->add_key($one, $primary); } diff --git a/system/database/DB_result.php b/system/database/DB_result.php index fb4268c2..0c4e7810 100755 --- a/system/database/DB_result.php +++ b/system/database/DB_result.php @@ -28,14 +28,14 @@ */ class CI_DB_result { - var $conn_id = NULL; - var $result_id = NULL; - var $result_array = array(); - var $result_object = array(); - var $custom_result_object = array(); - var $current_row = 0; - var $num_rows = 0; - var $row_data = NULL; + var $conn_id = NULL; + var $result_id = NULL; + var $result_array = array(); + var $result_object = array(); + var $custom_result_object = array(); + var $current_row = 0; + var $num_rows = 0; + var $row_data = NULL; /** @@ -47,47 +47,52 @@ class CI_DB_result { */ function result($type = 'object') { - if ($type == 'array') return $this->result_array(); - else if ($type == 'object') return $this->result_object(); - else return $this->custom_result_object($type); + if ($type == 'array') return $this->result_array(); + else if ($type == 'object') return $this->result_object(); + else return $this->custom_result_object($type); } // -------------------------------------------------------------------- - /** - * Custom query result. - * - * @param class_name A string that represents the type of object you want back - * @return array of objects - */ - function custom_result_object($class_name) - { - if (array_key_exists($class_name, $this->custom_result_object)) - { - return $this->custom_result_object[$class_name]; - } - - if ($this->result_id === FALSE OR $this->num_rows() == 0) - { - return array(); - } - - // add the data to the object - $this->_data_seek(0); - $result_object = array(); + /** + * Custom query result. + * + * @param class_name A string that represents the type of object you want back + * @return array of objects + */ + function custom_result_object($class_name) + { + if (array_key_exists($class_name, $this->custom_result_object)) + { + return $this->custom_result_object[$class_name]; + } + + if ($this->result_id === FALSE OR $this->num_rows() == 0) + { + return array(); + } + + // add the data to the object + $this->_data_seek(0); + $result_object = array(); + while ($row = $this->_fetch_object()) - { - $object = new $class_name(); - foreach($row as $key => $value) - { - $object->$key = $value; - } + { + $object = new $class_name(); + + foreach ($row as $key => $value) + { + $object->$key = $value; + } + $result_object[] = $object; } - // return the array - return $this->custom_result_object[$class_name] = $result_object; - } + // return the array + return $this->custom_result_object[$class_name] = $result_object; + } + + // -------------------------------------------------------------------- /** * Query result. "object" version. @@ -180,9 +185,9 @@ function row($n = 0, $type = 'object') $n = 0; } - if ($type == 'object') return $this->row_object($n); - else if ($type == 'array') return $this->row_array($n); - else return $this->custom_row_object($n, $type); + if ($type == 'object') return $this->row_object($n); + else if ($type == 'array') return $this->row_array($n); + else return $this->custom_row_object($n, $type); } // -------------------------------------------------------------------- @@ -219,7 +224,7 @@ function set_row($key, $value = NULL) // -------------------------------------------------------------------- - /** + /** * Returns a single result row - custom object version * * @access public @@ -242,7 +247,7 @@ function custom_row_object($n, $type) return $result[$this->current_row]; } - /** + /** * Returns a single result row - object version * * @access public diff --git a/system/database/drivers/mssql/mssql_driver.php b/system/database/drivers/mssql/mssql_driver.php index 5a69132c..65397ed8 100755 --- a/system/database/drivers/mssql/mssql_driver.php +++ b/system/database/drivers/mssql/mssql_driver.php @@ -253,7 +253,7 @@ function escape_str($str, $like = FALSE) { if (is_array($str)) { - foreach($str as $key => $val) + foreach ($str as $key => $val) { $str[$key] = $this->escape_str($val, $like); } @@ -267,9 +267,11 @@ function escape_str($str, $like = FALSE) // escape LIKE condition wildcards if ($like === TRUE) { - $str = str_replace( array('%', '_', $this->_like_escape_chr), - array($this->_like_escape_chr.'%', $this->_like_escape_chr.'_', $this->_like_escape_chr.$this->_like_escape_chr), - $str); + $str = str_replace( + array($this->_like_escape_chr, '%', '_'), + array($this->_like_escape_chr.$this->_like_escape_chr, $this->_like_escape_chr.'%', $this->_like_escape_chr.'_'), + $str + ); } return $str; @@ -551,7 +553,7 @@ function _insert($table, $keys, $values) */ function _update($table, $values, $where, $orderby = array(), $limit = FALSE) { - foreach($values as $key => $val) + foreach ($values as $key => $val) { $valstr[] = $key." = ".$val; } diff --git a/system/database/drivers/mysql/mysql_driver.php b/system/database/drivers/mysql/mysql_driver.php index df18c912..b7d547cc 100755 --- a/system/database/drivers/mysql/mysql_driver.php +++ b/system/database/drivers/mysql/mysql_driver.php @@ -132,7 +132,22 @@ function db_select() */ function db_set_charset($charset, $collation) { - return @mysql_query("SET NAMES '".$this->escape_str($charset)."' COLLATE '".$this->escape_str($collation)."'", $this->conn_id); + static $use_set_names; + + if ( ! isset($use_set_names)) + { + // mysql_set_charset() requires PHP >= 5.2.3 and MySQL >= 5.0.7, use SET NAMES as fallback + $use_set_names = (version_compare(PHP_VERSION, '5.2.3', '>=') && version_compare(mysql_get_server_info(), '5.0.7', '>=')) ? FALSE : TRUE; + } + + if ($use_set_names) + { + return @mysql_query("SET NAMES '".$this->escape_str($charset)."' COLLATE '".$this->escape_str($collation)."'", $this->conn_id); + } + else + { + return @mysql_set_charset($charset, $this->conn_id); + } } // -------------------------------------------------------------------- @@ -286,13 +301,13 @@ function escape_str($str, $like = FALSE) { if (is_array($str)) { - foreach($str as $key => $val) - { + foreach ($str as $key => $val) + { $str[$key] = $this->escape_str($val, $like); - } + } - return $str; - } + return $str; + } if (function_exists('mysql_real_escape_string') AND is_resource($this->conn_id)) { @@ -590,9 +605,9 @@ function _insert_batch($table, $keys, $values) */ function _update($table, $values, $where, $orderby = array(), $limit = FALSE) { - foreach($values as $key => $val) + foreach ($values as $key => $val) { - $valstr[] = $key." = ".$val; + $valstr[] = $key . ' = ' . $val; } $limit = ( ! $limit) ? '' : ' LIMIT '.$limit; @@ -627,11 +642,11 @@ function _update_batch($table, $values, $index, $where = NULL) $ids = array(); $where = ($where != '' AND count($where) >=1) ? implode(" ", $where).' AND ' : ''; - foreach($values as $key => $val) + foreach ($values as $key => $val) { $ids[] = $val[$index]; - foreach(array_keys($val) as $field) + foreach (array_keys($val) as $field) { if ($field != $index) { @@ -643,7 +658,7 @@ function _update_batch($table, $values, $index, $where = NULL) $sql = "UPDATE ".$table." SET "; $cases = ''; - foreach($final as $k => $v) + foreach ($final as $k => $v) { $cases .= $k.' = CASE '."\n"; foreach ($v as $row) diff --git a/system/database/drivers/mysql/mysql_forge.php b/system/database/drivers/mysql/mysql_forge.php index 529ec980..c1cae136 100755 --- a/system/database/drivers/mysql/mysql_forge.php +++ b/system/database/drivers/mysql/mysql_forge.php @@ -119,9 +119,13 @@ function _process_fields($fields) $sql .= ' DEFAULT \''.$attributes['DEFAULT'].'\''; } - if (array_key_exists('NULL', $attributes)) + if (array_key_exists('NULL', $attributes) && $attributes['NULL'] === TRUE) { - $sql .= ($attributes['NULL'] === TRUE) ? ' NULL' : ' NOT NULL'; + $sql .= ' NULL'; + } + else + { + $sql .= ' NOT NULL'; } if (array_key_exists('AUTO_INCREMENT', $attributes) && $attributes['AUTO_INCREMENT'] === TRUE) diff --git a/system/database/drivers/mysqli/mysqli_driver.php b/system/database/drivers/mysqli/mysqli_driver.php index 8942100d..b8586c21 100755 --- a/system/database/drivers/mysqli/mysqli_driver.php +++ b/system/database/drivers/mysqli/mysqli_driver.php @@ -132,7 +132,22 @@ function db_select() */ function _db_set_charset($charset, $collation) { - return @mysqli_query($this->conn_id, "SET NAMES '".$this->escape_str($charset)."' COLLATE '".$this->escape_str($collation)."'"); + static $use_set_names; + + if ( ! isset($use_set_names)) + { + // mysqli_set_charset() requires MySQL >= 5.0.7, use SET NAMES as fallback + $use_set_names = (version_compare(mysqli_get_server_info($this->conn_id), '5.0.7', '>=')) ? FALSE : TRUE; + } + + if ($use_set_names) + { + return @mysqli_query($this->conn_id, "SET NAMES '".$this->escape_str($charset)."' COLLATE '".$this->escape_str($collation)."'"); + } + else + { + return @mysqli_set_charset($this->conn_id, $charset); + } } // -------------------------------------------------------------------- @@ -287,7 +302,7 @@ function escape_str($str, $like = FALSE) { if (is_array($str)) { - foreach($str as $key => $val) + foreach ($str as $key => $val) { $str[$key] = $this->escape_str($val, $like); } @@ -571,7 +586,7 @@ function _insert_batch($table, $keys, $values) */ function _update($table, $values, $where, $orderby = array(), $limit = FALSE) { - foreach($values as $key => $val) + foreach ($values as $key => $val) { $valstr[] = $key." = ".$val; } @@ -607,11 +622,11 @@ function _update_batch($table, $values, $index, $where = NULL) $ids = array(); $where = ($where != '' AND count($where) >=1) ? implode(" ", $where).' AND ' : ''; - foreach($values as $key => $val) + foreach ($values as $key => $val) { $ids[] = $val[$index]; - foreach(array_keys($val) as $field) + foreach (array_keys($val) as $field) { if ($field != $index) { @@ -623,7 +638,7 @@ function _update_batch($table, $values, $index, $where = NULL) $sql = "UPDATE ".$table." SET "; $cases = ''; - foreach($final as $k => $v) + foreach ($final as $k => $v) { $cases .= $k.' = CASE '."\n"; foreach ($v as $row) diff --git a/system/database/drivers/mysqli/mysqli_forge.php b/system/database/drivers/mysqli/mysqli_forge.php index d5097335..26054945 100755 --- a/system/database/drivers/mysqli/mysqli_forge.php +++ b/system/database/drivers/mysqli/mysqli_forge.php @@ -104,9 +104,13 @@ function _process_fields($fields) $sql .= ' DEFAULT \''.$attributes['DEFAULT'].'\''; } - if (array_key_exists('NULL', $attributes)) + if (array_key_exists('NULL', $attributes) && $attributes['NULL'] === TRUE) { - $sql .= ($attributes['NULL'] === TRUE) ? ' NULL' : ' NOT NULL'; + $sql .= ' NULL'; + } + else + { + $sql .= ' NOT NULL'; } if (array_key_exists('AUTO_INCREMENT', $attributes) && $attributes['AUTO_INCREMENT'] === TRUE) diff --git a/system/database/drivers/oci8/oci8_driver.php b/system/database/drivers/oci8/oci8_driver.php index 64f53cc3..14df104f 100755 --- a/system/database/drivers/oci8/oci8_driver.php +++ b/system/database/drivers/oci8/oci8_driver.php @@ -253,7 +253,7 @@ function stored_procedure($package, $procedure, $params) $sql = "begin $package.$procedure("; $have_cursor = FALSE; - foreach($params as $param) + foreach ($params as $param) { $sql .= $param['name'] . ","; @@ -395,7 +395,7 @@ function escape_str($str, $like = FALSE) { if (is_array($str)) { - foreach($str as $key => $val) + foreach ($str as $key => $val) { $str[$key] = $this->escape_str($val, $like); } @@ -655,7 +655,7 @@ function _insert($table, $keys, $values) */ function _update($table, $values, $where, $orderby = array(), $limit = FALSE) { - foreach($values as $key => $val) + foreach ($values as $key => $val) { $valstr[] = $key." = ".$val; } diff --git a/system/database/drivers/odbc/odbc_driver.php b/system/database/drivers/odbc/odbc_driver.php index c8e03c35..81e0d7cf 100755 --- a/system/database/drivers/odbc/odbc_driver.php +++ b/system/database/drivers/odbc/odbc_driver.php @@ -264,7 +264,7 @@ function escape_str($str, $like = FALSE) { if (is_array($str)) { - foreach($str as $key => $val) + foreach ($str as $key => $val) { $str[$key] = $this->escape_str($val, $like); } @@ -523,7 +523,7 @@ function _insert($table, $keys, $values) */ function _update($table, $values, $where, $orderby = array(), $limit = FALSE) { - foreach($values as $key => $val) + foreach ($values as $key => $val) { $valstr[] = $key." = ".$val; } diff --git a/system/database/drivers/odbc/odbc_result.php b/system/database/drivers/odbc/odbc_result.php index a81a2b8b..5d64a464 100755 --- a/system/database/drivers/odbc/odbc_result.php +++ b/system/database/drivers/odbc/odbc_result.php @@ -188,7 +188,7 @@ function _fetch_object() */ function _odbc_fetch_object(& $odbc_result) { $rs = array(); - $rs_obj = false; + $rs_obj = FALSE; if (odbc_fetch_into($odbc_result, $rs)) { foreach ($rs as $k=>$v) { $field_name= odbc_field_name($odbc_result, $k+1); @@ -210,7 +210,7 @@ function _odbc_fetch_object(& $odbc_result) { */ function _odbc_fetch_array(& $odbc_result) { $rs = array(); - $rs_assoc = false; + $rs_assoc = FALSE; if (odbc_fetch_into($odbc_result, $rs)) { $rs_assoc=array(); foreach ($rs as $k=>$v) { diff --git a/system/database/drivers/postgre/postgre_driver.php b/system/database/drivers/postgre/postgre_driver.php index 0bb7974d..14039688 100755 --- a/system/database/drivers/postgre/postgre_driver.php +++ b/system/database/drivers/postgre/postgre_driver.php @@ -283,7 +283,7 @@ function escape_str($str, $like = FALSE) { if (is_array($str)) { - foreach($str as $key => $val) + foreach ($str as $key => $val) { $str[$key] = $this->escape_str($val, $like); } @@ -330,21 +330,21 @@ function insert_id() $v = $this->_version(); $v = $v['server']; - $table = func_num_args() > 0 ? func_get_arg(0) : null; - $column = func_num_args() > 1 ? func_get_arg(1) : null; + $table = func_num_args() > 0 ? func_get_arg(0) : NULL; + $column = func_num_args() > 1 ? func_get_arg(1) : NULL; - if ($table == null && $v >= '8.1') + if ($table == NULL && $v >= '8.1') { $sql='SELECT LASTVAL() as ins_id'; } - elseif ($table != null && $column != null && $v >= '8.0') + elseif ($table != NULL && $column != NULL && $v >= '8.0') { $sql = sprintf("SELECT pg_get_serial_sequence('%s','%s') as seq", $table, $column); $query = $this->query($sql); $row = $query->row(); $sql = sprintf("SELECT CURRVAL('%s') as ins_id", $row->seq); } - elseif ($table != null) + elseif ($table != NULL) { // seq_name passed in table parameter $sql = sprintf("SELECT CURRVAL('%s') as ins_id", $table); @@ -553,6 +553,24 @@ function _insert($table, $keys, $values) // -------------------------------------------------------------------- + /** + * Insert_batch statement + * + * Generates a platform-specific insert string from the supplied data + * + * @access public + * @param string the table name + * @param array the insert keys + * @param array the insert values + * @return string + */ + function _insert_batch($table, $keys, $values) + { + return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES ".implode(', ', $values); + } + + // -------------------------------------------------------------------- + /** * Update statement * @@ -568,7 +586,7 @@ function _insert($table, $keys, $values) */ function _update($table, $values, $where, $orderby = array(), $limit = FALSE) { - foreach($values as $key => $val) + foreach ($values as $key => $val) { $valstr[] = $key." = ".$val; } diff --git a/system/database/drivers/sqlite/sqlite_driver.php b/system/database/drivers/sqlite/sqlite_driver.php index 5bfc1f55..eb4e585b 100755 --- a/system/database/drivers/sqlite/sqlite_driver.php +++ b/system/database/drivers/sqlite/sqlite_driver.php @@ -280,7 +280,7 @@ function escape_str($str, $like = FALSE) { if (is_array($str)) { - foreach($str as $key => $val) + foreach ($str as $key => $val) { $str[$key] = $this->escape_str($val, $like); } @@ -537,7 +537,7 @@ function _insert($table, $keys, $values) */ function _update($table, $values, $where, $orderby = array(), $limit = FALSE) { - foreach($values as $key => $val) + foreach ($values as $key => $val) { $valstr[] = $key." = ".$val; } diff --git a/system/fonts/texb.ttf b/system/fonts/texb.ttf index 6792342a33277106ecd28a130476b2aeb0983af2..383c88b86b7c17e2e284732af48b2bfc359647ae 100755 GIT binary patch delta 59402 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 E%A)ovwIx96Fph>n4U`fq29X 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 delta 68545 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 Hbw>R!N diff --git a/system/helpers/array_helper.php b/system/helpers/array_helper.php index 6b2415df..075a31fd 100755 --- a/system/helpers/array_helper.php +++ b/system/helpers/array_helper.php @@ -69,6 +69,7 @@ function random_element($array) { return $array; } + return $array[array_rand($array)]; } } diff --git a/system/helpers/captcha_helper.php b/system/helpers/captcha_helper.php index c0e3798f..19ec0c77 100755 --- a/system/helpers/captcha_helper.php +++ b/system/helpers/captcha_helper.php @@ -87,7 +87,7 @@ function create_captcha($data = '', $img_path = '', $img_url = '', $font_path = $current_dir = @opendir($img_path); - while($filename = @readdir($current_dir)) + while ($filename = @readdir($current_dir)) { if ($filename != "." and $filename != ".." and $filename != "index.html") { diff --git a/system/helpers/cookie_helper.php b/system/helpers/cookie_helper.php index 7701d503..7cee0282 100755 --- a/system/helpers/cookie_helper.php +++ b/system/helpers/cookie_helper.php @@ -44,11 +44,11 @@ */ if ( ! function_exists('set_cookie')) { - function set_cookie($name = '', $value = '', $expire = '', $domain = '', $path = '/', $prefix = '') + function set_cookie($name = '', $value = '', $expire = '', $domain = '', $path = '/', $prefix = '', $secure = FALSE) { // Set the config file options $CI =& get_instance(); - $CI->input->set_cookie($name, $value, $expire, $domain, $path, $prefix); + $CI->input->set_cookie($name, $value, $expire, $domain, $path, $prefix, $secure); } } diff --git a/system/helpers/download_helper.php b/system/helpers/download_helper.php index 6cecd0d1..1145688a 100755 --- a/system/helpers/download_helper.php +++ b/system/helpers/download_helper.php @@ -58,7 +58,14 @@ function force_download($filename = '', $data = '') $extension = end($x); // Load the mime types - @include(APPPATH.'config/mimes'.EXT); + if (defined('ENVIRONMENT') AND is_file(APPPATH.'config/'.ENVIRONMENT.'/mimes.php')) + { + include(APPPATH.'config/'.ENVIRONMENT.'/mimes.php'); + } + elseif (is_file(APPPATH.'config/mimes.php')) + { + include(APPPATH.'config/mimes.php'); + } // Set a default mime if we can't find it if ( ! isset($mimes[$extension])) diff --git a/system/helpers/file_helper.php b/system/helpers/file_helper.php index 334eef87..3931667f 100755 --- a/system/helpers/file_helper.php +++ b/system/helpers/file_helper.php @@ -128,7 +128,7 @@ function delete_files($path, $del_dir = FALSE, $level = 0) return FALSE; } - while(FALSE !== ($filename = @readdir($current_dir))) + while (FALSE !== ($filename = @readdir($current_dir))) { if ($filename != "." and $filename != "..") { @@ -352,7 +352,16 @@ function get_mime_by_extension($file) if ( ! is_array($mimes)) { - if ( ! require_once(APPPATH.'config/mimes.php')) + if (defined('ENVIRONMENT') AND is_file(APPPATH.'config/'.ENVIRONMENT.'/mimes.php')) + { + include(APPPATH.'config/'.ENVIRONMENT.'/mimes.php'); + } + elseif (is_file(APPPATH.'config/mimes.php')) + { + include(APPPATH.'config/mimes.php'); + } + + if ( ! is_array($mimes)) { return FALSE; } diff --git a/system/helpers/form_helper.php b/system/helpers/form_helper.php index 758056b5..2925d3c7 100755 --- a/system/helpers/form_helper.php +++ b/system/helpers/form_helper.php @@ -49,7 +49,14 @@ function form_open($action = '', $attributes = '', $hidden = array()) $attributes = 'method="post"'; } - $action = ( strpos($action, '://') === FALSE) ? $CI->config->site_url(http://github.com/$action) : $action; + // If an action is not a full URL then turn it into one + if ($action && strpos($action, '://') === FALSE) + { + $action = $CI->config->site_url(http://github.com/$action); + } + + // If no action is provided then set to the current url + $action OR $action = $CI->config->site_url(http://github.com/$CI-%3Euri-%3Euri_string()); $form = '
config->item('csrf_protection') === TRUE) { - $hidden[$CI->security->csrf_token_name] = $CI->security->csrf_hash; + $hidden[$CI->security->get_csrf_token_name()] = $CI->security->get_csrf_hash(); } if (is_array($hidden) AND count($hidden) > 0) { - $form .= sprintf("\n
%s
", form_hidden($hidden)); + $form .= sprintf("
%s
", form_hidden($hidden)); } return $form; @@ -1025,25 +1032,23 @@ function &_get_validation_object() { $CI =& get_instance(); - // We set this as a variable since we're returning by reference + // We set this as a variable since we're returning by reference. $return = FALSE; - - if ( ! isset($CI->load->_ci_classes) OR ! isset($CI->load->_ci_classes['form_validation'])) + + if (FALSE !== ($object = $CI->load->is_loaded('form_validation'))) { - return $return; - } - - $object = $CI->load->_ci_classes['form_validation']; - - if ( ! isset($CI->$object) OR ! is_object($CI->$object)) - { - return $return; + if ( ! isset($CI->$object) OR ! is_object($CI->$object)) + { + return $return; + } + + return $CI->$object; } - - return $CI->$object; + + return $return; } } /* End of file form_helper.php */ -/* Location: ./system/helpers/form_helper.php */ \ No newline at end of file +/* Location: ./system/helpers/form_helper.php */ diff --git a/system/helpers/html_helper.php b/system/helpers/html_helper.php index 68c6f590..b64b6065 100755 --- a/system/helpers/html_helper.php +++ b/system/helpers/html_helper.php @@ -40,9 +40,10 @@ */ if ( ! function_exists('heading')) { - function heading($data = '', $h = '1') + function heading($data = '', $h = '1', $attributes = '') { - return "".$data.""; + $attributes = ($attributes != '') ? ' '.$attributes : $attributes; + return "".$data.""; } } @@ -123,6 +124,10 @@ function _list($type = 'ul', $list, $attributes = '', $depth = 0) } $attributes = $atts; } + elseif (is_string($attributes) AND strlen($attributes) > 0) + { + $attributes = ' '. $attributes; + } // Write the opening list tag $out .= "<".$type.$attributes.">\n"; @@ -258,7 +263,16 @@ function doctype($type = 'xhtml1-strict') if ( ! is_array($_doctypes)) { - if ( ! require_once(APPPATH.'config/doctypes.php')) + if (defined('ENVIRONMENT') AND is_file(APPPATH.'config/'.ENVIRONMENT.'/doctypes.php')) + { + include(APPPATH.'config/'.ENVIRONMENT.'/doctypes.php'); + } + elseif (is_file(APPPATH.'config/doctypes.php')) + { + include(APPPATH.'config/doctypes.php'); + } + + if ( ! is_array($_doctypes)) { return FALSE; } diff --git a/system/helpers/inflector_helper.php b/system/helpers/inflector_helper.php index 4cd7486b..7b99bc5b 100755 --- a/system/helpers/inflector_helper.php +++ b/system/helpers/inflector_helper.php @@ -41,28 +41,48 @@ { function singular($str) { - $str = strtolower(trim($str)); - $end = substr($str, -3); - - if ($end == 'ies') - { - $str = substr($str, 0, strlen($str)-3).'y'; - } - elseif ($end == 'ses') - { - $str = substr($str, 0, strlen($str)-2); - } - else + $result = strval($str); + + $singular_rules = array( + '/(matr)ices$/' => '\1ix', + '/(vert|ind)ices$/' => '\1ex', + '/^(ox)en/' => '\1', + '/(alias)es$/' => '\1', + '/([octop|vir])i$/' => '\1us', + '/(cris|ax|test)es$/' => '\1is', + '/(shoe)s$/' => '\1', + '/(o)es$/' => '\1', + '/(bus|campus)es$/' => '\1', + '/([m|l])ice$/' => '\1ouse', + '/(x|ch|ss|sh)es$/' => '\1', + '/(m)ovies$/' => '\1\2ovie', + '/(s)eries$/' => '\1\2eries', + '/([^aeiouy]|qu)ies$/' => '\1y', + '/([lr])ves$/' => '\1f', + '/(tive)s$/' => '\1', + '/(hive)s$/' => '\1', + '/([^f])ves$/' => '\1fe', + '/(^analy)ses$/' => '\1sis', + '/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/' => '\1\2sis', + '/([ti])a$/' => '\1um', + '/(p)eople$/' => '\1\2erson', + '/(m)en$/' => '\1an', + '/(s)tatuses$/' => '\1\2tatus', + '/(c)hildren$/' => '\1\2hild', + '/(n)ews$/' => '\1\2ews', + '/([^u])s$/' => '\1', + ); + + foreach ($singular_rules as $rule => $replacement) { - $end = substr($str, -1); - - if ($end == 's') + if (preg_match($rule, $result)) { - $str = substr($str, 0, strlen($str)-1); + $result = preg_replace($rule, $replacement, $result); + break; } } - return $str; + return $result; } } @@ -82,39 +102,40 @@ function singular($str) { function plural($str, $force = FALSE) { - $str = strtolower(trim($str)); - $end = substr($str, -1); - - if ($end == 'y') - { - // Y preceded by vowel => regular plural - $vowels = array('a', 'e', 'i', 'o', 'u'); - $str = in_array(substr($str, -2, 1), $vowels) ? $str.'s' : substr($str, 0, -1).'ies'; - } - elseif ($end == 'h') + $result = strval($str); + + $plural_rules = array( + '/^(ox)$/' => '\1\2en', // ox + '/([m|l])ouse$/' => '\1ice', // mouse, louse + '/(matr|vert|ind)ix|ex$/' => '\1ices', // matrix, vertex, index + '/(x|ch|ss|sh)$/' => '\1es', // search, switch, fix, box, process, address + '/([^aeiouy]|qu)y$/' => '\1ies', // query, ability, agency + '/(hive)$/' => '\1s', // archive, hive + '/(?:([^f])fe|([lr])f)$/' => '\1\2ves', // half, safe, wife + '/sis$/' => 'ses', // basis, diagnosis + '/([ti])um$/' => '\1a', // datum, medium + '/(p)erson$/' => '\1eople', // person, salesperson + '/(m)an$/' => '\1en', // man, woman, spokesman + '/(c)hild$/' => '\1hildren', // child + '/(buffal|tomat)o$/' => '\1\2oes', // buffalo, tomato + '/(bu|campu)s$/' => '\1\2ses', // bus, campus + '/(alias|status|virus)/' => '\1es', // alias + '/(octop)us$/' => '\1i', // octopus + '/(ax|cris|test)is$/' => '\1es', // axis, crisis + '/s$/' => 's', // no change (compatibility) + '/$/' => 's', + ); + + foreach ($plural_rules as $rule => $replacement) { - if (substr($str, -2) == 'ch' OR substr($str, -2) == 'sh') + if (preg_match($rule, $result)) { - $str .= 'es'; + $result = preg_replace($rule, $replacement, $result); + break; } - else - { - $str .= 's'; - } - } - elseif ($end == 's') - { - if ($force == TRUE) - { - $str .= 'es'; - } - } - else - { - $str .= 's'; } - return $str; + return $result; } } diff --git a/system/helpers/language_helper.php b/system/helpers/language_helper.php index 68c1a1fc..ac0d69da 100755 --- a/system/helpers/language_helper.php +++ b/system/helpers/language_helper.php @@ -1,4 +1,4 @@ -generate($str); - } - else - { - return bin2hex(mhash(MHASH_SHA1, $str)); - } - } - else - { - return sha1($str); - } + return sha1($str); } else { diff --git a/system/helpers/smiley_helper.php b/system/helpers/smiley_helper.php index 80a8d79a..6d888935 100755 --- a/system/helpers/smiley_helper.php +++ b/system/helpers/smiley_helper.php @@ -59,7 +59,7 @@ function smiley_js($alias = '', $field_id = '', $inline = TRUE) if (is_array($alias)) { - foreach($alias as $name => $id) + foreach ($alias as $name => $id) { $m[] = '"'.$name.'" : "'.$id.'"'; } @@ -101,7 +101,7 @@ function insert_smiley(smiley, field_id) { { if (is_array($alias)) { - foreach($alias as $name => $id) + foreach ($alias as $name => $id) { $r .= 'smiley_map["'.$name.'"] = "'.$id.'";'."\n"; } @@ -229,19 +229,21 @@ function parse_smileys($str = '', $image_url = '', $smileys = NULL) { function _get_smiley_array() { - if ( ! file_exists(APPPATH.'config/smileys'.EXT)) + if (defined('ENVIRONMENT') AND file_exists(APPPATH.'config/'.ENVIRONMENT.'/smileys.php')) { - return FALSE; + include(APPPATH.'config/'.ENVIRONMENT.'/smileys.php'); } - - include(APPPATH.'config/smileys'.EXT); - - if ( ! isset($smileys) OR ! is_array($smileys)) + elseif (file_exists(APPPATH.'config/smileys.php')) + { + include(APPPATH.'config/smileys.php'); + } + + if (isset($smileys) AND is_array($smileys)) { - return FALSE; + return $smileys; } - return $smileys; + return FALSE; } } diff --git a/system/helpers/text_helper.php b/system/helpers/text_helper.php index 47e6ccc9..33d7fa2f 100755 --- a/system/helpers/text_helper.php +++ b/system/helpers/text_helper.php @@ -366,12 +366,14 @@ function highlight_phrase($str, $phrase, $tag_open = '', $tag_close = '< { function convert_accented_characters($str) { - if ( ! file_exists(APPPATH.'config/foreign_chars'.EXT)) + if (defined('ENVIRONMENT') AND is_file(APPPATH.'config/'.ENVIRONMENT.'/foreign_chars.php')) { - return $str; + include(APPPATH.'config/'.ENVIRONMENT.'/foreign_chars.php'); + } + elseif (is_file(APPPATH.'config/foreign_chars.php')) + { + include(APPPATH.'config/foreign_chars.php'); } - - include APPPATH.'config/foreign_chars'.EXT; if ( ! isset($foreign_characters)) { @@ -443,7 +445,7 @@ function word_wrap($str, $charlim = '76') } $temp = ''; - while((strlen($line)) > $charlim) + while ((strlen($line)) > $charlim) { // If the over-length word is a URL we won't wrap it if (preg_match("!\[url.+\]|://|wwww.!", $line)) diff --git a/system/helpers/typography_helper.php b/system/helpers/typography_helper.php index 302bf45c..19b4eec0 100755 --- a/system/helpers/typography_helper.php +++ b/system/helpers/typography_helper.php @@ -84,9 +84,8 @@ function auto_typography($str, $strip_js_event_handlers = TRUE, $reduce_linebrea { function entity_decode($str, $charset='UTF-8') { - $CI =& get_instance(); - $CI->load->library('security'); - return $CI->security->entity_decode($str, $charset); + global $SEC; + return $SEC->entity_decode($str, $charset); } } diff --git a/system/helpers/url_helper.php b/system/helpers/url_helper.php index d0516cee..9f4b8524 100755 --- a/system/helpers/url_helper.php +++ b/system/helpers/url_helper.php @@ -50,18 +50,21 @@ function site_url(http://github.com/$uri%20=%20'') /** * Base URL - * - * Returns the "base_url" item from your config file + * + * Create a local URL based on your basepath. + * Segments can be passed in as a string or an array, same as site_url + * or a URL to a file can be passed in, e.g. to an image file. * * @access public + * @param string * @return string */ if ( ! function_exists('base_url')) { - function base_url() + function base_url(http://github.com/$uri%20=%20'') { $CI =& get_instance(); - return $CI->config->slash_item('base_url'); + return $CI->config->base_url(http://github.com/$uri); } } diff --git a/system/language/english/form_validation_lang.php b/system/language/english/form_validation_lang.php index b0188509..3f240900 100755 --- a/system/language/english/form_validation_lang.php +++ b/system/language/english/form_validation_lang.php @@ -19,6 +19,9 @@ $lang['matches'] = "The %s field does not match the %s field."; $lang['is_natural'] = "The %s field must contain only positive numbers."; $lang['is_natural_no_zero'] = "The %s field must contain a number greater than zero."; +$lang['decimal'] = "The %s field must contain a decimal number."; +$lang['less_than'] = "The %s field must contain a number less than %s."; +$lang['greater_than'] = "The %s field must contain a number greater than %s."; /* End of file form_validation_lang.php */ diff --git a/system/language/english/ftp_lang.php b/system/language/english/ftp_lang.php index 392b2b27..1e5168cf 100755 --- a/system/language/english/ftp_lang.php +++ b/system/language/english/ftp_lang.php @@ -1,17 +1,17 @@ $time + $ttl, diff --git a/system/libraries/Cache/drivers/Cache_dummy.php b/system/libraries/Cache/drivers/Cache_dummy.php index 74f68924..f96a68e2 100755 --- a/system/libraries/Cache/drivers/Cache_dummy.php +++ b/system/libraries/Cache/drivers/Cache_dummy.php @@ -10,29 +10,29 @@ * @license http://codeigniter.com/user_guide/license.html * @link http://codeigniter.com * @since Version 2.0 - * @filesource + * @filesource */ // ------------------------------------------------------------------------ /** - * CodeIgniter Dummy Caching Class + * CodeIgniter Dummy Caching Class * * @package CodeIgniter * @subpackage Libraries * @category Core * @author ExpressionEngine Dev Team - * @link + * @link */ -class Cache_dummy extends CI_Driver { +class CI_Cache_dummy extends CI_Driver { /** - * Get + * Get * * Since this is the dummy class, it's always going to return FALSE. * - * @param string + * @param string * @return Boolean FALSE */ public function get($id) @@ -40,8 +40,8 @@ public function get($id) return FALSE; } - // ------------------------------------------------------------------------ - + // ------------------------------------------------------------------------ + /** * Cache Save * @@ -55,7 +55,7 @@ public function save($id, $data, $ttl = 60) { return TRUE; } - + // ------------------------------------------------------------------------ /** @@ -112,7 +112,7 @@ public function get_metadata($id) /** * Is this caching driver supported on the system? * Of course this one is. - * + * * @return TRUE; */ public function is_supported() @@ -121,9 +121,9 @@ public function is_supported() } // ------------------------------------------------------------------------ - + } // End Class -/* End of file Cache_apc.php */ -/* Location: ./system/libraries/Cache/drivers/Cache_apc.php */ \ No newline at end of file +/* End of file Cache_dummy.php */ +/* Location: ./system/libraries/Cache/drivers/Cache_dummy.php */ \ No newline at end of file diff --git a/system/libraries/Cache/drivers/Cache_file.php b/system/libraries/Cache/drivers/Cache_file.php index 3ed357f2..13e2d1af 100755 --- a/system/libraries/Cache/drivers/Cache_file.php +++ b/system/libraries/Cache/drivers/Cache_file.php @@ -25,7 +25,7 @@ * @link */ -class Cache_file extends CI_Driver { +class CI_Cache_file extends CI_Driver { protected $_cache_path; @@ -39,7 +39,7 @@ public function __construct() $path = $CI->config->item('cache_path'); - $this->_cache_path = ($path == '') ? BASEPATH.'cache/' : $path; + $this->_cache_path = ($path == '') ? APPPATH.'cache/' : $path; } // ------------------------------------------------------------------------ diff --git a/system/libraries/Cache/drivers/Cache_memcached.php b/system/libraries/Cache/drivers/Cache_memcached.php index a7efdc5d..ec2fd216 100755 --- a/system/libraries/Cache/drivers/Cache_memcached.php +++ b/system/libraries/Cache/drivers/Cache_memcached.php @@ -25,7 +25,7 @@ * @link */ -class Cache_memcached extends CI_Driver { +class CI_Cache_memcached extends CI_Driver { private $_memcached; // Holds the memcached object @@ -122,7 +122,7 @@ public function get_metadata($id) return FALSE; } - list($value, $time, $ttl) = $stored; + list($data, $time, $ttl) = $stored; return array( 'expire' => $time + $ttl, diff --git a/system/libraries/Calendar.php b/system/libraries/Calendar.php index 72d228e7..df0fd6ee 100755 --- a/system/libraries/Calendar.php +++ b/system/libraries/Calendar.php @@ -47,7 +47,7 @@ public function __construct($config = array()) { $this->CI =& get_instance(); - if ( ! in_array('calendar_lang'.EXT, $this->CI->lang->is_loaded, TRUE)) + if ( ! in_array('calendar_lang.php', $this->CI->lang->is_loaded, TRUE)) { $this->CI->lang->load('calendar'); } diff --git a/system/libraries/Cart.php b/system/libraries/Cart.php index 7f65b48b..b2eaa9ad 100755 --- a/system/libraries/Cart.php +++ b/system/libraries/Cart.php @@ -374,6 +374,7 @@ function _save_cart() // Lets add up the individual prices and set the cart sub-total $total = 0; + $items = 0; foreach ($this->_cart_contents as $key => $val) { // We make sure the array contains the proper indexes @@ -383,13 +384,14 @@ function _save_cart() } $total += ($val['price'] * $val['qty']); + $items += $val['qty']; // Set the subtotal $this->_cart_contents[$key]['subtotal'] = ($this->_cart_contents[$key]['price'] * $this->_cart_contents[$key]['qty']); } // Set the cart total and total items. - $this->_cart_contents['total_items'] = count($this->_cart_contents); + $this->_cart_contents['total_items'] = $items; $this->_cart_contents['cart_total'] = $total; // Is our cart empty? If so we delete it from the session diff --git a/system/libraries/Driver.php b/system/libraries/Driver.php index 15fc3da2..c32f2c1e 100755 --- a/system/libraries/Driver.php +++ b/system/libraries/Driver.php @@ -1,4 +1,4 @@ -lib_name)) + if ( ! isset($this->lib_name)) { $this->lib_name = get_class($this); } // The class will be prefixed with the parent lib $child_class = $this->lib_name.'_'.$child; - - if (in_array(strtolower($child_class), array_map('strtolower', $this->valid_drivers))) + + // Remove the CI_ prefix and lowercase + $lib_name = ucfirst(strtolower(str_replace('CI_', '', $this->lib_name))); + $driver_name = strtolower(str_replace('CI_', '', $child_class)); + + if (in_array($driver_name, array_map('strtolower', $this->valid_drivers))) { // check and see if the driver is in a separate file if ( ! class_exists($child_class)) @@ -52,19 +56,15 @@ function __get($child) // check application path first foreach (array(APPPATH, BASEPATH) as $path) { - // and check for case sensitivity of both the parent and child libs - foreach (array(ucfirst($this->lib_name), strtolower($this->lib_name)) as $lib) + // loves me some nesting! + foreach (array(ucfirst($driver_name), $driver_name) as $class) { - // loves me some nesting! - foreach (array(ucfirst($child_class), strtolower($child_class)) as $class) - { - $filepath = $path.'libraries/'.$this->lib_name.'/drivers/'.$child_class.EXT; + $filepath = $path.'libraries/'.$lib_name.'/drivers/'.$class.'.php'; - if (file_exists($filepath)) - { - include_once $filepath; - break; - } + if (file_exists($filepath)) + { + include_once $filepath; + break; } } } @@ -143,7 +143,7 @@ public function decorate($parent) } } - foreach($r->getProperties() as $prop) + foreach ($r->getProperties() as $prop) { if ($prop->isPublic()) { @@ -226,4 +226,4 @@ public function __set($var, $val) // END CI_Driver CLASS /* 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 e5af38f4..03eccea0 100755 --- a/system/libraries/Email.php +++ b/system/libraries/Email.php @@ -395,7 +395,7 @@ public function message($body) public function attach($filename, $disposition = 'attachment') { $this->_attach_name[] = $filename; - $this->_attach_type[] = $this->_mime_types(next(explode('.', basename($filename)))); + $this->_attach_type[] = $this->_mime_types(pathinfo($filename, PATHINFO_EXTENSION)); $this->_attach_disp[] = $disposition; // Can also be 'inline' Not sure if it matters return $this; } @@ -722,7 +722,7 @@ public function validate_email($email) { if ( ! is_array($email)) { - $this->_set_error_message('email_must_be_array'); + $this->_set_error_message('lang:email_must_be_array'); return FALSE; } @@ -730,7 +730,7 @@ public function validate_email($email) { if ( ! $this->valid_email($val)) { - $this->_set_error_message('email_invalid_address', $val); + $this->_set_error_message('lang:email_invalid_address', $val); return FALSE; } } @@ -897,7 +897,7 @@ public function word_wrap($str, $charlim = '') } $temp = ''; - while((strlen($line)) > $charlim) + while ((strlen($line)) > $charlim) { // If the over-length word is a URL we won't wrap it if (preg_match("!\[url.+\]|://|wwww.!", $line)) @@ -973,7 +973,7 @@ private function _write_headers() reset($this->_headers); $this->_header_str = ""; - foreach($this->_headers as $key => $val) + foreach ($this->_headers as $key => $val) { $val = trim($val); @@ -1131,7 +1131,7 @@ private function _build_message() if ( ! file_exists($filename)) { - $this->_set_error_message('email_attachment_missing', $filename); + $this->_set_error_message('lang:email_attachment_missing', $filename); return FALSE; } @@ -1146,7 +1146,7 @@ private function _build_message() if ( ! $fp = fopen($filename, FOPEN_READ)) { - $this->_set_error_message('email_attachment_unreadable', $filename); + $this->_set_error_message('lang:email_attachment_unreadable', $filename); return FALSE; } @@ -1353,7 +1353,7 @@ public function send() ( ! isset($this->_bcc_array) AND ! isset($this->_headers['Bcc'])) AND ( ! isset($this->_headers['Cc']))) { - $this->_set_error_message('email_no_recipients'); + $this->_set_error_message('lang:email_no_recipients'); return FALSE; } @@ -1484,7 +1484,7 @@ private function _spool_email() if ( ! $this->_send_with_mail()) { - $this->_set_error_message('email_send_failure_phpmail'); + $this->_set_error_message('lang:email_send_failure_phpmail'); return FALSE; } break; @@ -1492,7 +1492,7 @@ private function _spool_email() if ( ! $this->_send_with_sendmail()) { - $this->_set_error_message('email_send_failure_sendmail'); + $this->_set_error_message('lang:email_send_failure_sendmail'); return FALSE; } break; @@ -1500,14 +1500,14 @@ private function _spool_email() if ( ! $this->_send_with_smtp()) { - $this->_set_error_message('email_send_failure_smtp'); + $this->_set_error_message('lang:email_send_failure_smtp'); return FALSE; } break; } - $this->_set_error_message('email_sent', $this->_get_protocol()); + $this->_set_error_message('lang:email_sent', $this->_get_protocol()); return TRUE; } @@ -1578,8 +1578,8 @@ private function _send_with_sendmail() if ($status != 0) { - $this->_set_error_message('email_exit_status', $status); - $this->_set_error_message('email_no_socket'); + $this->_set_error_message('lang:email_exit_status', $status); + $this->_set_error_message('lang:email_no_socket'); return FALSE; } @@ -1598,7 +1598,7 @@ private function _send_with_smtp() { if ($this->smtp_host == '') { - $this->_set_error_message('email_no_hostname'); + $this->_set_error_message('lang:email_no_hostname'); return FALSE; } @@ -1607,14 +1607,14 @@ private function _send_with_smtp() $this->_send_command('from', $this->clean_email($this->_headers['From'])); - foreach($this->_recipients as $val) + foreach ($this->_recipients as $val) { $this->_send_command('to', $val); } if (count($this->_cc_array) > 0) { - foreach($this->_cc_array as $val) + foreach ($this->_cc_array as $val) { if ($val != "") { @@ -1625,7 +1625,7 @@ private function _send_with_smtp() if (count($this->_bcc_array) > 0) { - foreach($this->_bcc_array as $val) + foreach ($this->_bcc_array as $val) { if ($val != "") { @@ -1647,7 +1647,7 @@ private function _send_with_smtp() if (strncmp($reply, '250', 3) != 0) { - $this->_set_error_message('email_smtp_error', $reply); + $this->_set_error_message('lang:email_smtp_error', $reply); return FALSE; } @@ -1672,9 +1672,9 @@ private function _smtp_connect() $errstr, $this->smtp_timeout); - if( ! is_resource($this->_smtp_connect)) + if ( ! is_resource($this->_smtp_connect)) { - $this->_set_error_message('email_smtp_error', $errno." ".$errstr); + $this->_set_error_message('lang:email_smtp_error', $errno." ".$errstr); return FALSE; } @@ -1737,7 +1737,7 @@ private function _send_command($cmd, $data = '') if (substr($reply, 0, 3) != $resp) { - $this->_set_error_message('email_smtp_error', $reply); + $this->_set_error_message('lang:email_smtp_error', $reply); return FALSE; } @@ -1766,7 +1766,7 @@ private function _smtp_authenticate() if ($this->smtp_user == "" AND $this->smtp_pass == "") { - $this->_set_error_message('email_no_smtp_unpw'); + $this->_set_error_message('lang:email_no_smtp_unpw'); return FALSE; } @@ -1776,7 +1776,7 @@ private function _smtp_authenticate() if (strncmp($reply, '334', 3) != 0) { - $this->_set_error_message('email_failed_smtp_login', $reply); + $this->_set_error_message('lang:email_failed_smtp_login', $reply); return FALSE; } @@ -1786,7 +1786,7 @@ private function _smtp_authenticate() if (strncmp($reply, '334', 3) != 0) { - $this->_set_error_message('email_smtp_auth_un', $reply); + $this->_set_error_message('lang:email_smtp_auth_un', $reply); return FALSE; } @@ -1796,7 +1796,7 @@ private function _smtp_authenticate() if (strncmp($reply, '235', 3) != 0) { - $this->_set_error_message('email_smtp_auth_pw', $reply); + $this->_set_error_message('lang:email_smtp_auth_pw', $reply); return FALSE; } @@ -1815,7 +1815,7 @@ private function _send_data($data) { if ( ! fwrite($this->_smtp_connect, $data . $this->newline)) { - $this->_set_error_message('email_smtp_data_failure', $data); + $this->_set_error_message('lang:email_smtp_data_failure', $data); return FALSE; } else @@ -1942,7 +1942,7 @@ private function _set_error_message($msg, $val = '') $CI =& get_instance(); $CI->lang->load('email'); - if (FALSE === ($line = $CI->lang->line($msg))) + if (substr($msg, 0, 5) != 'lang:' || FALSE === ($line = $CI->lang->line(substr($msg, 5)))) { $this->_debug_msg[] = str_replace('%s', $val, $msg)."
"; } @@ -2059,4 +2059,4 @@ private function _mime_types($ext = "") // END CI_Email class /* End of file Email.php */ -/* Location: ./system/libraries/Email.php */ \ No newline at end of file +/* Location: ./system/libraries/Email.php */ diff --git a/system/libraries/Encrypt.php b/system/libraries/Encrypt.php index e5f65878..b30a8cf0 100755 --- a/system/libraries/Encrypt.php +++ b/system/libraries/Encrypt.php @@ -524,7 +524,7 @@ function sha1($str) { if ( ! function_exists('mhash')) { - require_once(BASEPATH.'libraries/Sha1'.EXT); + require_once(BASEPATH.'libraries/Sha1.php'); $SH = new CI_SHA; return $SH->generate($str); } diff --git a/system/libraries/Form_validation.php b/system/libraries/Form_validation.php index fc5b82ee..6f79a554 100755 --- a/system/libraries/Form_validation.php +++ b/system/libraries/Form_validation.php @@ -138,14 +138,14 @@ function set_rules($field, $label = '', $rules = '') // Build our master array $this->_field_data[$field] = array( - 'field' => $field, - 'label' => $label, - 'rules' => $rules, - 'is_array' => $is_array, - 'keys' => $indexes, - 'postdata' => NULL, - 'error' => '' - ); + 'field' => $field, + 'label' => $label, + 'rules' => $rules, + 'is_array' => $is_array, + 'keys' => $indexes, + 'postdata' => NULL, + 'error' => '' + ); return $this; } @@ -628,6 +628,10 @@ function _execute($row, $rules, $postdata = NULL, $cycles = 0) $this->_field_data[$row['field']]['postdata'] = (is_bool($result)) ? $postdata : $result; } } + else + { + log_message('debug', "Unable to find validation rule: ".$rule); + } continue; } @@ -1040,7 +1044,7 @@ function valid_emails($str) return $this->valid_email(trim($str)); } - foreach(explode(',', $str) as $email) + foreach (explode(',', $str) as $email) { if (trim($email) != '' && $this->valid_email(trim($email)) === FALSE) { @@ -1147,7 +1151,57 @@ function is_numeric($str) */ function integer($str) { - return (bool)preg_match( '/^[\-+]?[0-9]+$/', $str); + return (bool) preg_match('/^[\-+]?[0-9]+$/', $str); + } + + // -------------------------------------------------------------------- + + /** + * Decimal number + * + * @access public + * @param string + * @return bool + */ + function decimal($str) + { + return (bool) preg_match('/^[\-+]?[0-9]+\.[0-9]+$/', $str); + } + + // -------------------------------------------------------------------- + + /** + * Greather than + * + * @access public + * @param string + * @return bool + */ + function greater_than($str, $min) + { + if ( ! is_numeric($str)) + { + return FALSE; + } + return $str > $min; + } + + // -------------------------------------------------------------------- + + /** + * Less than + * + * @access public + * @param string + * @return bool + */ + function less_than($str, $max) + { + if ( ! is_numeric($str)) + { + return FALSE; + } + return $str < $max; } // -------------------------------------------------------------------- @@ -1161,7 +1215,7 @@ function integer($str) */ function is_natural($str) { - return (bool)preg_match( '/^[0-9]+$/', $str); + return (bool) preg_match( '/^[0-9]+$/', $str); } // -------------------------------------------------------------------- @@ -1286,11 +1340,6 @@ function strip_image_tags($str) */ function xss_clean($str) { - if ( ! isset($this->CI->security)) - { - $this->CI->load->library('security'); - } - return $this->CI->security->xss_clean($str); } @@ -1312,4 +1361,4 @@ function encode_php_tags($str) // END Form Validation Class /* End of file Form_validation.php */ -/* Location: ./system/libraries/Form_validation.php */ \ No newline at end of file +/* Location: ./system/libraries/Form_validation.php */ diff --git a/system/libraries/Image_lib.php b/system/libraries/Image_lib.php index 41f9ad39..8902f524 100755 --- a/system/libraries/Image_lib.php +++ b/system/libraries/Image_lib.php @@ -749,7 +749,7 @@ function image_rotate_gd() @chmod($this->full_dst_path, FILE_WRITE_MODE); - return true; + return TRUE; } // -------------------------------------------------------------------- diff --git a/system/libraries/Javascript.php b/system/libraries/Javascript.php index 167859ab..34e0d700 100755 --- a/system/libraries/Javascript.php +++ b/system/libraries/Javascript.php @@ -855,7 +855,7 @@ function _prep_args($result, $is_key = FALSE) } elseif (is_string($result) OR $is_key) { - return '"'.str_replace(array('\\', "\t", "\n", "\r", '"'), array('\\\\', '\\t', '\\n', "\\r", '\"'), $result).'"'; + return '"'.str_replace(array('\\', "\t", "\n", "\r", '"', '/'), array('\\\\', '\\t', '\\n', "\\r", '\"', '\/'), $result).'"'; } elseif (is_scalar($result)) { diff --git a/system/libraries/Log.php b/system/libraries/Log.php index fb2c5a49..9f1db76b 100755 --- a/system/libraries/Log.php +++ b/system/libraries/Log.php @@ -83,7 +83,7 @@ public function write_log($level = 'error', $msg, $php_error = FALSE) return FALSE; } - $filepath = $this->_log_path.'log-'.date('Y-m-d').EXT; + $filepath = $this->_log_path.'log-'.date('Y-m-d').'.php'; $message = ''; if ( ! file_exists($filepath)) diff --git a/system/libraries/Pagination.php b/system/libraries/Pagination.php index 1113f862..cc62e660 100755 --- a/system/libraries/Pagination.php +++ b/system/libraries/Pagination.php @@ -30,7 +30,7 @@ class CI_Pagination { var $prefix = ''; // A custom prefix added to the path. var $suffix = ''; // A custom suffix added to the path. - var $total_rows = ''; // Total number of items (database results) + var $total_rows = 0; // Total number of items (database results) var $per_page = 10; // Max number of items you want shown per page var $num_links = 2; // Number of "digit" links to show before/after the currently viewed page var $cur_page = 0; // The current page being viewed diff --git a/system/libraries/Profiler.php b/system/libraries/Profiler.php index 6587eae0..082a5ee1 100755 --- a/system/libraries/Profiler.php +++ b/system/libraries/Profiler.php @@ -32,8 +32,6 @@ */ class CI_Profiler { - var $CI; - protected $_available_sections = array( 'benchmarks', 'get', @@ -43,14 +41,27 @@ class CI_Profiler { 'controller_info', 'queries', 'http_headers', + 'session_data', 'config' ); + protected $_query_toggle_count = 25; + + protected $CI; + + // -------------------------------------------------------------------- + public function __construct($config = array()) { $this->CI =& get_instance(); $this->CI->load->language('profiler'); + if (isset($config['query_toggle_count'])) + { + $this->_query_toggle_count = (int) $config['query_toggle_count']; + unset($config['query_toggle_count']); + } + // default all sections to display foreach ($this->_available_sections as $section) { @@ -162,7 +173,7 @@ protected function _compile_queries() $output .= "\n"; $output .= '  '.$this->CI->lang->line('profiler_queries').'  '; $output .= "\n"; - $output .= "\n\n\n"; + $output .= "\n\n
\n"; $output .="\n"; $output .= "
".$this->CI->lang->line('profiler_no_db')."
\n"; $output .= ""; @@ -178,13 +189,26 @@ protected function _compile_queries() $output = "\n\n"; + $count = 0; + foreach ($dbs as $db) { + $count++; + + $hide_queries = (count($db->queries) > $this->_query_toggle_count) ? ' display:none' : ''; + + $show_hide_js = '('.$this->CI->lang->line('profiler_section_hide').')'; + + if ($hide_queries != '') + { + $show_hide_js = '('.$this->CI->lang->line('profiler_section_show').')'; + } + $output .= '
'; $output .= "\n"; - $output .= '  '.$this->CI->lang->line('profiler_database').':  '.$db->database.'   '.$this->CI->lang->line('profiler_queries').': '.count($db->queries).'   '; + $output .= '  '.$this->CI->lang->line('profiler_database').':  '.$db->database.'   '.$this->CI->lang->line('profiler_queries').': '.count($db->queries).'  '.$show_hide_js.''; $output .= "\n"; - $output .= "\n\n\n"; + $output .= "\n\n
\n"; if (count($db->queries) == 0) { @@ -388,7 +412,7 @@ protected function _compile_memory_usage() } else { - $output .= "
".$this->CI->lang->line('profiler_no_memory_usage')."
"; + $output .= "
".$this->CI->lang->line('profiler_no_memory')."
"; } $output .= ""; @@ -410,12 +434,12 @@ protected function _compile_http_headers() $output = "\n\n"; $output .= '
'; $output .= "\n"; - $output .= '  '.$this->CI->lang->line('profiler_headers').'  '; + $output .= '  '.$this->CI->lang->line('profiler_headers').'  ('.$this->CI->lang->line('profiler_section_show').')'; $output .= "\n"; - $output .= "\n\n
\n"; + $output .= "\n\n
\n"; - foreach(array('HTTP_ACCEPT', 'HTTP_USER_AGENT', 'HTTP_CONNECTION', 'SERVER_PORT', 'SERVER_NAME', 'REMOTE_ADDR', 'SERVER_SOFTWARE', 'HTTP_ACCEPT_LANGUAGE', 'SCRIPT_NAME', 'REQUEST_METHOD',' HTTP_HOST', 'REMOTE_HOST', 'CONTENT_TYPE', 'SERVER_PROTOCOL', 'QUERY_STRING', 'HTTP_ACCEPT_ENCODING', 'HTTP_X_FORWARDED_FOR') as $header) + foreach (array('HTTP_ACCEPT', 'HTTP_USER_AGENT', 'HTTP_CONNECTION', 'SERVER_PORT', 'SERVER_NAME', 'REMOTE_ADDR', 'SERVER_SOFTWARE', 'HTTP_ACCEPT_LANGUAGE', 'SCRIPT_NAME', 'REQUEST_METHOD',' HTTP_HOST', 'REMOTE_HOST', 'CONTENT_TYPE', 'SERVER_PROTOCOL', 'QUERY_STRING', 'HTTP_ACCEPT_ENCODING', 'HTTP_X_FORWARDED_FOR') as $header) { $val = (isset($_SERVER[$header])) ? $_SERVER[$header] : ''; $output .= "\n"; @@ -441,12 +465,12 @@ protected function _compile_config() $output = "\n\n"; $output .= '
'; $output .= "\n"; - $output .= '  '.$this->CI->lang->line('profiler_config').'  '; + $output .= '  '.$this->CI->lang->line('profiler_config').'  ('.$this->CI->lang->line('profiler_section_show').')'; $output .= "\n"; - $output .= "\n\n
\n"; + $output .= "\n\n
\n"; - foreach($this->CI->config->config as $config=>$val) + foreach ($this->CI->config->config as $config=>$val) { if (is_array($val)) { @@ -464,6 +488,39 @@ protected function _compile_config() // -------------------------------------------------------------------- + /** + * Compile session userdata + * + * @return string + */ + private function _compile_session_data() + { + if ( ! isset($this->CI->session)) + { + return; + } + + $output = '
'; + $output .= '  '.$this->CI->lang->line('profiler_session_data').'  ('.$this->CI->lang->line('profiler_section_show').')'; + $output .= "
"; + + foreach ($this->CI->session->all_userdata() as $key => $val) + { + if (is_array($val)) + { + $val = print_r($val, TRUE); + } + + $output .= "\n"; + } + + $output .= ''; + $output .= "
"; + return $output; + } + + // -------------------------------------------------------------------- + /** * Run the Profiler * @@ -493,7 +550,6 @@ public function run() return $output; } - } // END CI_Profiler class diff --git a/system/libraries/Security.php b/system/libraries/Security.php deleted file mode 100755 index ba64c732..00000000 --- a/system/libraries/Security.php +++ /dev/null @@ -1,737 +0,0 @@ - '[removed]', - 'document.write' => '[removed]', - '.parentNode' => '[removed]', - '.innerHTML' => '[removed]', - 'window.location' => '[removed]', - '-moz-binding' => '[removed]', - '' => '-->', - ' '<![CDATA[' - ); - /* never allowed, regex replacement */ - public $never_allowed_regex = array( - "javascript\s*:" => '[removed]', - "expression\s*(\(|&\#40;)" => '[removed]', // CSS and IE - "vbscript\s*:" => '[removed]', // IE, surprise! - "Redirect\s+302" => '[removed]' - ); - - public function __construct() - { - $this->csrf_token_name = (config_item('csrf_token_name')) ? config_item('csrf_token_name') : 'csrf_token_name'; - $this->csrf_cookie_name = (config_item('csrf_cookie_name')) ? config_item('csrf_cookie_name') : 'csrf_cookie_name'; - $this->csrf_expire = (config_item('csrf_expire')) ? config_item('csrf_expire') : 7200; - - // Append application specific cookie prefix to token name - $this->csrf_cookie_name = (config_item('cookie_prefix')) ? config_item('cookie_prefix').$this->csrf_token_name : $this->csrf_token_name; - - // Set the CSRF hash - $this->_csrf_set_hash(); - - log_message('debug', "Security Class Initialized"); - } - - // -------------------------------------------------------------------- - - /** - * Verify Cross Site Request Forgery Protection - * - * @access public - * @return null - */ - public function csrf_verify() - { - // If no POST data exists we will set the CSRF cookie - if (count($_POST) == 0) - { - return $this->csrf_set_cookie(); - } - - // Do the tokens exist in both the _POST and _COOKIE arrays? - if ( ! isset($_POST[$this->csrf_token_name]) OR ! isset($_COOKIE[$this->csrf_cookie_name])) - { - $this->csrf_show_error(); - } - - // Do the tokens match? - if ($_POST[$this->csrf_token_name] != $_COOKIE[$this->csrf_cookie_name]) - { - $this->csrf_show_error(); - } - - // We kill this since we're done and we don't want to polute the _POST array - unset($_POST[$this->csrf_token_name]); - - // Nothing should last forever - unset($_COOKIE[$this->csrf_cookie_name]); - $this->_csrf_set_hash(); - $this->csrf_set_cookie(); - - log_message('debug', "CSRF token verified "); - } - - // -------------------------------------------------------------------- - - /** - * Set Cross Site Request Forgery Protection Cookie - * - * @access public - * @return null - */ - public function csrf_set_cookie() - { - $expire = time() + $this->csrf_expire; - - setcookie($this->csrf_cookie_name, $this->csrf_hash, $expire, config_item('cookie_path'), config_item('cookie_domain'), 0); - - log_message('debug', "CRSF cookie Set"); - } - - // -------------------------------------------------------------------- - - /** - * Set Cross Site Request Forgery Protection Cookie - * - * @access private - * @return null - */ - private function _csrf_set_hash() - { - if ($this->csrf_hash == '') - { - // If the cookie exists we will use it's value. We don't necessarily want to regenerate it with - // each page load since a page could contain embedded sub-pages causing this feature to fail - if (isset($_COOKIE[$this->csrf_cookie_name]) AND $_COOKIE[$this->csrf_cookie_name] != '') - { - $this->csrf_hash = $_COOKIE[$this->csrf_cookie_name]; - } - else - { - $this->csrf_hash = md5(uniqid(rand(), TRUE)); - } - } - - return $this->csrf_hash; - } - - // -------------------------------------------------------------------- - - /** - * Show CSRF Error - * - * @access public - * @return null - */ - public function csrf_show_error() - { - show_error('The action you have requested is not allowed.'); - } - - // -------------------------------------------------------------------- - - /** - * XSS Clean - * - * Sanitizes data so that Cross Site Scripting Hacks can be - * prevented. This function does a fair amount of work but - * it is extremely thorough, designed to prevent even the - * most obscure XSS attempts. Nothing is ever 100% foolproof, - * of course, but I haven't been able to get anything passed - * the filter. - * - * Note: This function should only be used to deal with data - * upon submission. It's not something that should - * be used for general runtime processing. - * - * This function was based in part on some code and ideas I - * got from Bitflux: http://channel.bitflux.ch/wiki/XSS_Prevention - * - * To help develop this script I used this great list of - * vulnerabilities along with a few other hacks I've - * harvested from examining vulnerabilities in other programs: - * http://ha.ckers.org/xss.html - * - * @access public - * @param mixed string or array - * @return string - */ - public function xss_clean($str, $is_image = FALSE) - { - /* - * Is the string an array? - * - */ - if (is_array($str)) - { - while (list($key) = each($str)) - { - $str[$key] = $this->xss_clean($str[$key]); - } - - return $str; - } - - /* - * Remove Invisible Characters - */ - $str = remove_invisible_characters($str); - - /* - * Protect GET variables in URLs - */ - - // 901119URL5918AMP18930PROTECT8198 - - $str = preg_replace('|\&([a-z\_0-9\-]+)\=([a-z\_0-9\-]+)|i', $this->xss_hash()."\\1=\\2", $str); - - /* - * Validate standard character entities - * - * Add a semicolon if missing. We do this to enable - * the conversion of entities to ASCII later. - * - */ - $str = preg_replace('#(&\#?[0-9a-z]{2,})([\x00-\x20])*;?#i', "\\1;\\2", $str); - - /* - * Validate UTF16 two byte encoding (x00) - * - * Just as above, adds a semicolon if missing. - * - */ - $str = preg_replace('#(&\#x?)([0-9A-F]+);?#i',"\\1\\2;",$str); - - /* - * Un-Protect GET variables in URLs - */ - $str = str_replace($this->xss_hash(), '&', $str); - - /* - * URL Decode - * - * Just in case stuff like this is submitted: - * - * Google - * - * Note: Use rawurldecode() so it does not remove plus signs - * - */ - $str = rawurldecode($str); - - /* - * Convert character entities to ASCII - * - * This permits our tests below to work reliably. - * We only convert entities that are within tags since - * these are the ones that will pose security problems. - * - */ - - $str = preg_replace_callback("/[a-z]+=([\'\"]).*?\\1/si", array($this, '_convert_attribute'), $str); - - $str = preg_replace_callback("/<\w+.*?(?=>|<|$)/si", array($this, '_decode_entity'), $str); - - /* - * Remove Invisible Characters Again! - */ - $str = remove_invisible_characters($str); - - /* - * Convert all tabs to spaces - * - * This prevents strings like this: ja vascript - * NOTE: we deal with spaces between characters later. - * NOTE: preg_replace was found to be amazingly slow here on large blocks of data, - * so we use str_replace. - * - */ - - if (strpos($str, "\t") !== FALSE) - { - $str = str_replace("\t", ' ', $str); - } - - /* - * Capture converted string for later comparison - */ - $converted_string = $str; - - /* - * Not Allowed Under Any Conditions - */ - - foreach ($this->never_allowed_str as $key => $val) - { - $str = str_replace($key, $val, $str); - } - - foreach ($this->never_allowed_regex as $key => $val) - { - $str = preg_replace("#".$key."#i", $val, $str); - } - - /* - * Makes PHP tags safe - * - * Note: XML tags are inadvertently replaced too: - * - * '), array('<?', '?>'), $str); - } - - /* - * Compact any exploded words - * - * This corrects words like: j a v a s c r i p t - * These words are compacted back to their correct state. - * - */ - $words = array('javascript', 'expression', 'vbscript', 'script', 'applet', 'alert', 'document', 'write', 'cookie', 'window'); - foreach ($words as $word) - { - $temp = ''; - - for ($i = 0, $wordlen = strlen($word); $i < $wordlen; $i++) - { - $temp .= substr($word, $i, 1)."\s*"; - } - - // We only want to do this when it is followed by a non-word character - // That way valid stuff like "dealer to" does not become "dealerto" - $str = preg_replace_callback('#('.substr($temp, 0, -3).')(\W)#is', array($this, '_compact_exploded_words'), $str); - } - - /* - * Remove disallowed Javascript in links or img tags - * We used to do some version comparisons and use of stripos for PHP5, but it is dog slow compared - * to these simplified non-capturing preg_match(), especially if the pattern exists in the string - */ - do - { - $original = $str; - - if (preg_match("/]*?)(>|$)#si", array($this, '_js_link_removal'), $str); - } - - if (preg_match("/]*?)(\s?/?>|$)#si", array($this, '_js_img_removal'), $str); - } - - if (preg_match("/script/i", $str) OR preg_match("/xss/i", $str)) - { - $str = preg_replace("#<(/*)(script|xss)(.*?)\>#si", '[removed]', $str); - } - } - while($original != $str); - - unset($original); - - /* - * Remove JavaScript Event Handlers - * - * Note: This code is a little blunt. It removes - * the event handler and anything up to the closing >, - * but it's unlikely to be a problem. - * - */ - $event_handlers = array('[^a-z_\-]on\w*','xmlns'); - - if ($is_image === TRUE) - { - /* - * Adobe Photoshop puts XML metadata into JFIF images, including namespacing, - * so we have to allow this for images. -Paul - */ - unset($event_handlers[array_search('xmlns', $event_handlers)]); - } - - $str = preg_replace("#<([^><]+?)(".implode('|', $event_handlers).")(\s*=\s*[^><]*)([><]*)#i", "<\\1\\4", $str); - - /* - * Sanitize naughty HTML elements - * - * If a tag containing any of the words in the list - * below is found, the tag gets converted to entities. - * - * So this: - * Becomes: <blink> - * - */ - $naughty = 'alert|applet|audio|basefont|base|behavior|bgsound|blink|body|embed|expression|form|frameset|frame|head|html|ilayer|iframe|input|isindex|layer|link|meta|object|plaintext|style|script|textarea|title|video|xml|xss'; - $str = preg_replace_callback('#<(/*\s*)('.$naughty.')([^><]*)([><]*)#is', array($this, '_sanitize_naughty_html'), $str); - - /* - * Sanitize naughty scripting elements - * - * Similar to above, only instead of looking for - * tags it looks for PHP and JavaScript commands - * that are disallowed. Rather than removing the - * code, it simply converts the parenthesis to entities - * rendering the code un-executable. - * - * For example: eval('some code') - * Becomes: eval('some code') - * - */ - $str = preg_replace('#(alert|cmd|passthru|eval|exec|expression|system|fopen|fsockopen|file|file_get_contents|readfile|unlink)(\s*)\((.*?)\)#si', "\\1\\2(\\3)", $str); - - /* - * Final clean up - * - * This adds a bit of extra precaution in case - * something got through the above filters - * - */ - foreach ($this->never_allowed_str as $key => $val) - { - $str = str_replace($key, $val, $str); - } - - foreach ($this->never_allowed_regex as $key => $val) - { - $str = preg_replace("#".$key."#i", $val, $str); - } - - /* - * Images are Handled in a Special Way - * - Essentially, we want to know that after all of the character conversion is done whether - * any unwanted, likely XSS, code was found. If not, we return TRUE, as the image is clean. - * However, if the string post-conversion does not matched the string post-removal of XSS, - * then it fails, as there was unwanted XSS code found and removed/changed during processing. - */ - - if ($is_image === TRUE) - { - if ($str == $converted_string) - { - return TRUE; - } - else - { - return FALSE; - } - } - - log_message('debug', "XSS Filtering completed"); - return $str; - } - - // -------------------------------------------------------------------- - - /** - * Random Hash for protecting URLs - * - * @access public - * @return string - */ - public function xss_hash() - { - if ($this->xss_hash == '') - { - if (phpversion() >= 4.2) - mt_srand(); - else - mt_srand(hexdec(substr(md5(microtime()), -8)) & 0x7fffffff); - - $this->xss_hash = md5(time() + mt_rand(0, 1999999999)); - } - - return $this->xss_hash; - } - - // -------------------------------------------------------------------- - - /** - * Compact Exploded Words - * - * Callback function for xss_clean() to remove whitespace from - * things like j a v a s c r i p t - * - * @access private - * @param type - * @return type - */ - private function _compact_exploded_words($matches) - { - return preg_replace('/\s+/s', '', $matches[1]).$matches[2]; - } - - // -------------------------------------------------------------------- - - /** - * Sanitize Naughty HTML - * - * Callback function for xss_clean() to remove naughty HTML elements - * - * @access private - * @param array - * @return string - */ - private function _sanitize_naughty_html($matches) - { - // encode opening brace - $str = '<'.$matches[1].$matches[2].$matches[3]; - - // encode captured opening or closing brace to prevent recursive vectors - $str .= str_replace(array('>', '<'), array('>', '<'), $matches[4]); - - return $str; - } - - // -------------------------------------------------------------------- - - /** - * JS Link Removal - * - * Callback function for xss_clean() to sanitize links - * This limits the PCRE backtracks, making it more performance friendly - * and prevents PREG_BACKTRACK_LIMIT_ERROR from being triggered in - * PHP 5.2+ on link-heavy strings - * - * @access private - * @param array - * @return string - */ - private function _js_link_removal($match) - { - $attributes = $this->_filter_attributes(str_replace(array('<', '>'), '', $match[1])); - return str_replace($match[1], preg_replace("#href=.*?(alert\(|alert&\#40;|javascript\:|charset\=|window\.|document\.|\.cookie|_filter_attributes(str_replace(array('<', '>'), '', $match[1])); - return str_replace($match[1], preg_replace("#src=.*?(alert\(|alert&\#40;|javascript\:|charset\=|window\.|document\.|\.cookie|', '<', '\\'), array('>', '<', '\\\\'), $match[0]); - } - - // -------------------------------------------------------------------- - - /** - * Filter Attributes - * - * Filters tag attributes for consistency and safety - * - * @access private - * @param string - * @return string - */ - private function _filter_attributes($str) - { - $out = ''; - - if (preg_match_all('#\s*[a-z\-]+\s*=\s*(\042|\047)([^\\1]*?)\\1#is', $str, $matches)) - { - foreach ($matches[0] as $match) - { - $out .= preg_replace("#/\*.*?\*/#s", '', $match); - } - } - - return $out; - } - - // -------------------------------------------------------------------- - - /** - * HTML Entity Decode Callback - * - * Used as a callback for XSS Clean - * - * @access private - * @param array - * @return string - */ - private function _decode_entity($match) - { - return $this->entity_decode($match[0], strtoupper(config_item('charset'))); - } - - // -------------------------------------------------------------------- - - /** - * HTML Entities Decode - * - * This function is a replacement for html_entity_decode() - * - * In some versions of PHP the native function does not work - * when UTF-8 is the specified character set, so this gives us - * a work-around. More info here: - * http://bugs.php.net/bug.php?id=25670 - * - * NOTE: html_entity_decode() has a bug in some PHP versions when UTF-8 is the - * character set, and the PHP developers said they were not back porting the - * fix to versions other than PHP 5.x. - * - * @access public - * @param string - * @param string - * @return string - */ - public function entity_decode($str, $charset='UTF-8') - { - if (stristr($str, '&') === FALSE) return $str; - - // The reason we are not using html_entity_decode() by itself is because - // while it is not technically correct to leave out the semicolon - // at the end of an entity most browsers will still interpret the entity - // correctly. html_entity_decode() does not convert entities without - // semicolons, so we are left with our own little solution here. Bummer. - - if (function_exists('html_entity_decode') && (strtolower($charset) != 'utf-8' OR is_php('5.0.0'))) - { - $str = html_entity_decode($str, ENT_COMPAT, $charset); - $str = preg_replace('~&#x(0*[0-9a-f]{2,5})~ei', 'chr(hexdec("\\1"))', $str); - return preg_replace('~&#([0-9]{2,4})~e', 'chr(\\1)', $str); - } - - // Numeric Entities - $str = preg_replace('~&#x(0*[0-9a-f]{2,5});{0,1}~ei', 'chr(hexdec("\\1"))', $str); - $str = preg_replace('~&#([0-9]{2,4});{0,1}~e', 'chr(\\1)', $str); - - // Literal Entities - Slightly slow so we do another check - if (stristr($str, '&') === FALSE) - { - $str = strtr($str, array_flip(get_html_translation_table(HTML_ENTITIES))); - } - - return $str; - } - - // -------------------------------------------------------------------- - - /** - * Filename Security - * - * @access public - * @param string - * @return string - */ - public function sanitize_filename($str, $relative_path = FALSE) - { - $bad = array( - "../", - "", - "<", - ">", - "'", - '"', - '&', - '$', - '#', - '{', - '}', - '[', - ']', - '=', - ';', - '?', - "%20", - "%22", - "%3c", // < - "%253c", // < - "%3e", // > - "%0e", // > - "%28", // ( - "%29", // ) - "%2528", // ( - "%26", // & - "%24", // $ - "%3f", // ? - "%3b", // ; - "%3d" // = - ); - - if ( ! $relative_path) - { - $bad[] = './'; - $bad[] = '/'; - } - - return stripslashes(str_replace($bad, '', $str)); - } - -} -// END Security Class - -/* End of file Security.php */ -/* Location: ./system/libraries/Security.php */ \ No newline at end of file diff --git a/system/libraries/Session.php b/system/libraries/Session.php index 53ff4f5d..2c8a8016 100755 --- a/system/libraries/Session.php +++ b/system/libraries/Session.php @@ -37,6 +37,7 @@ class CI_Session { var $cookie_prefix = ''; var $cookie_path = ''; var $cookie_domain = ''; + var $cookie_secure = FALSE; var $sess_time_to_update = 300; var $encryption_key = ''; var $flashdata_key = 'flash'; @@ -61,7 +62,7 @@ public function __construct($params = array()) // 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', 'sess_time_to_update', 'time_reference', 'cookie_prefix', 'encryption_key') as $key) + 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', 'sess_time_to_update', 'time_reference', 'cookie_prefix', 'encryption_key') as $key) { $this->$key = (isset($params[$key])) ? $params[$key] : $this->CI->config->item($key); } @@ -188,7 +189,7 @@ function sess_read() } // Does the User Agent Match? - if ($this->sess_match_useragent == TRUE AND trim($session['user_agent']) != trim(substr($this->CI->input->user_agent(), 0, 50))) + if ($this->sess_match_useragent == TRUE AND trim($session['user_agent']) != trim(substr($this->CI->input->user_agent(), 0, 120))) { $this->sess_destroy(); return FALSE; @@ -315,7 +316,7 @@ function sess_create() $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, 50), + 'user_agent' => substr($this->CI->input->user_agent(), 0, 120), 'last_activity' => $this->now ); @@ -434,11 +435,11 @@ function userdata($item) * Fetch all session data * * @access public - * @return mixed + * @return array */ function all_userdata() { - return ( ! isset($this->userdata)) ? FALSE : $this->userdata; + return $this->userdata; } // -------------------------------------------------------------------- @@ -666,7 +667,7 @@ function _set_cookie($cookie_data = NULL) $expire, $this->cookie_path, $this->cookie_domain, - 0 + $this->cookie_secure ); } diff --git a/system/libraries/Sha1.php b/system/libraries/Sha1.php index ad747a00..1a657572 100755 --- a/system/libraries/Sha1.php +++ b/system/libraries/Sha1.php @@ -42,7 +42,7 @@ * @author ExpressionEngine Dev Team * @link http://codeigniter.com/user_guide/general/encryption.html */ -class CI_SHA { +class CI_SHA1 { public function __construct() { @@ -88,7 +88,7 @@ function generate($str) $oldd = $d; $olde = $e; - for($j = 0; $j < 80; $j++) + for ($j = 0; $j < 80; $j++) { if ($j < 16) { diff --git a/system/libraries/Table.php b/system/libraries/Table.php index 48554163..def69677 100755 --- a/system/libraries/Table.php +++ b/system/libraries/Table.php @@ -108,7 +108,7 @@ function make_columns($array = array(), $col_limit = 0) } $new = array(); - while(count($array) > 0) + while (count($array) > 0) { $temp = array_splice($array, 0, $col_limit); @@ -280,7 +280,7 @@ function generate($table_data = NULL) $out .= $this->template['heading_row_start']; $out .= $this->newline; - foreach($this->heading as $heading) + foreach ($this->heading as $heading) { $temp = $this->template['heading_cell_start']; @@ -310,7 +310,7 @@ function generate($table_data = NULL) $out .= $this->newline; $i = 1; - foreach($this->rows as $row) + foreach ($this->rows as $row) { if ( ! is_array($row)) { @@ -323,7 +323,7 @@ function generate($table_data = NULL) $out .= $this->template['row_'.$name.'start']; $out .= $this->newline; - foreach($row as $cell) + foreach ($row as $cell) { $temp = $this->template['cell_'.$name.'start']; @@ -367,6 +367,9 @@ function generate($table_data = NULL) $out .= $this->template['table_close']; + // Clear table class properties before generating the table + $this->clear(); + return $out; } diff --git a/system/libraries/Trackback.php b/system/libraries/Trackback.php index e29b35c7..b0a76782 100755 --- a/system/libraries/Trackback.php +++ b/system/libraries/Trackback.php @@ -94,7 +94,7 @@ function send($tb_data) { $$item = $this->convert_ascii($$item); } - elseif($item == 'blog_name') + elseif ($item == 'blog_name') { $$item = $this->convert_ascii($$item); } @@ -261,7 +261,7 @@ function process($url, $data) // Was it successful? $this->response = ""; - while( ! feof($fp)) + while ( ! feof($fp)) { $this->response .= fgets($fp, 128); } diff --git a/system/libraries/Upload.php b/system/libraries/Upload.php index 8f84ffd7..3177424c 100755 --- a/system/libraries/Upload.php +++ b/system/libraries/Upload.php @@ -142,7 +142,8 @@ public function initialize($config = array()) */ public function do_upload($field = 'userfile') { - // Is $_FILES[$field] set? If not, no reason to continue. + + // Is $_FILES[$field] set? If not, no reason to continue. if ( ! isset($_FILES[$field])) { $this->set_error('upload_no_file_selected'); @@ -214,7 +215,7 @@ public function do_upload($field = 'userfile') $this->file_name = $this->_prep_filename($this->_file_name_override); // If no extension was provided in the file_name config item, use the uploaded one - if(strpos($this->_file_name_override, '.') === FALSE) + if (strpos($this->_file_name_override, '.') === FALSE) { $this->file_name .= $this->file_ext; } @@ -874,12 +875,6 @@ public function do_xss_clean() } $CI =& get_instance(); - - if ( ! isset($CI->security)) - { - $CI->load->library('security'); - } - return $CI->security->xss_clean($data, TRUE); } @@ -950,11 +945,21 @@ public function mimes_types($mime) if (count($this->mimes) == 0) { - if (@require_once(APPPATH.'config/mimes'.EXT)) + if (defined('ENVIRONMENT') AND is_file(APPPATH.'config/'.ENVIRONMENT.'/mimes.php')) + { + include(APPPATH.'config/'.ENVIRONMENT.'/mimes.php'); + } + elseif (is_file(APPPATH.'config/mimes.php')) + { + include(APPPATH.'config//mimes.php'); + } + else { - $this->mimes = $mimes; - unset($mimes); + return FALSE; } + + $this->mimes = $mimes; + unset($mimes); } return ( ! isset($this->mimes[$mime])) ? FALSE : $this->mimes[$mime]; diff --git a/system/libraries/User_agent.php b/system/libraries/User_agent.php index c6217483..016102a2 100755 --- a/system/libraries/User_agent.php +++ b/system/libraries/User_agent.php @@ -84,7 +84,15 @@ public function __construct() */ private function _load_agent_file() { - if ( ! @include(APPPATH.'config/user_agents'.EXT)) + if (defined('ENVIRONMENT') AND is_file(APPPATH.'config/'.ENVIRONMENT.'/user_agents.php')) + { + include(APPPATH.'config/'.ENVIRONMENT.'/user_agents.php'); + } + elseif (is_file(APPPATH.'config/user_agents.php')) + { + include(APPPATH.'config/user_agents.php'); + } + else { return FALSE; } @@ -375,7 +383,11 @@ public function is_mobile($key = NULL) */ public function is_referral() { - return ( ! isset($_SERVER['HTTP_REFERER']) OR $_SERVER['HTTP_REFERER'] == ''); + if ( ! isset($_SERVER['HTTP_REFERER']) OR $_SERVER['HTTP_REFERER'] == '') + { + return FALSE; + } + return TRUE; } // -------------------------------------------------------------------- diff --git a/system/libraries/Xmlrpc.php b/system/libraries/Xmlrpc.php index 9cf307cc..5da6ea6a 100755 --- a/system/libraries/Xmlrpc.php +++ b/system/libraries/Xmlrpc.php @@ -207,7 +207,7 @@ function request($incoming) $this->data = array(); - foreach($incoming as $key => $value) + foreach ($incoming as $key => $value) { $this->data[$key] = $this->values_parsing($value); } @@ -232,7 +232,7 @@ function values_parsing($value, $return = FALSE) { if (is_array($value) && array_key_exists(0, $value)) { - if ( ! isset($value['1']) OR (! isset($this->xmlrpcTypes[$value['1']]))) + if ( ! isset($value['1']) OR ( ! isset($this->xmlrpcTypes[$value['1']]))) { if (is_array($value[0])) { @@ -243,7 +243,7 @@ function values_parsing($value, $return = FALSE) $temp = new XML_RPC_Values($value['0'], 'string'); } } - elseif(is_array($value['0']) && ($value['1'] == 'struct' OR $value['1'] == 'array')) + elseif (is_array($value['0']) && ($value['1'] == 'struct' OR $value['1'] == 'array')) { while (list($k) = each($value['0'])) { @@ -281,7 +281,7 @@ function send_request() $this->error = $this->result->errstr; return FALSE; } - elseif( ! is_object($this->result->val)) + elseif ( ! is_object($this->result->val)) { $this->error = $this->result->errstr; return FALSE; @@ -358,7 +358,7 @@ class XML_RPC_Client extends CI_Xmlrpc var $errno = ''; var $errstring = ''; var $timeout = 5; - var $no_multicall = false; + var $no_multicall = FALSE; public function __construct($path, $server, $port=80) { @@ -392,7 +392,7 @@ function sendPayload($msg) return $r; } - if(empty($msg->payload)) + if (empty($msg->payload)) { // $msg = XML_RPC_Messages $msg->createPayload(); @@ -504,12 +504,7 @@ function prepare_response() function decode($array=FALSE) { $CI =& get_instance(); - - if ($this->xss_clean && ! isset($CI->security)) - { - $CI->load->library('security'); - } - + if ($array !== FALSE && is_array($array)) { while (list($key) = each($array)) @@ -553,11 +548,11 @@ function xmlrpc_decoder($xmlrpc_val) { $kind = $xmlrpc_val->kindOf(); - if($kind == 'scalar') + if ($kind == 'scalar') { return $xmlrpc_val->scalarval(); } - elseif($kind == 'array') + elseif ($kind == 'array') { reset($xmlrpc_val->me); list($a,$b) = each($xmlrpc_val->me); @@ -565,18 +560,18 @@ function xmlrpc_decoder($xmlrpc_val) $arr = array(); - for($i = 0; $i < $size; $i++) + for ($i = 0; $i < $size; $i++) { $arr[] = $this->xmlrpc_decoder($xmlrpc_val->me['array'][$i]); } return $arr; } - elseif($kind == 'struct') + elseif ($kind == 'struct') { reset($xmlrpc_val->me['struct']); $arr = array(); - while(list($key,$value) = each($xmlrpc_val->me['struct'])) + while (list($key,$value) = each($xmlrpc_val->me['struct'])) { $arr[$key] = $this->xmlrpc_decoder($value); } @@ -595,10 +590,8 @@ function iso8601_decode($time, $utc=0) $t = 0; if (preg_match('/([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})/', $time, $regs)) { - if ($utc == 1) - $t = gmmktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]); - else - $t = mktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]); + $fnc = ($utc == 1) ? 'gmmktime' : 'mktime'; + $t = $fnc($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]); } return $t; } @@ -628,7 +621,7 @@ public function __construct($method, $pars=0) $this->method_name = $method; if (is_array($pars) && count($pars) > 0) { - for($i=0; $iparams[] = $pars[$i]; @@ -646,7 +639,7 @@ function createPayload() $this->payload .= '' . $this->method_name . "\r\n"; $this->payload .= "\r\n"; - for($i=0; $iparams); $i++) + for ($i=0; $iparams); $i++) { // $p = XML_RPC_Values $p = $this->params[$i]; @@ -664,7 +657,7 @@ function parseResponse($fp) { $data = ''; - while($datum = fread($fp, 4096)) + while ($datum = fread($fp, 4096)) { $data .= $datum; } @@ -684,7 +677,7 @@ function parseResponse($fp) // Check for data //------------------------------------- - if($data == "") + if ($data == "") { error_log($this->xmlrpcstr['no_data']); $r = new XML_RPC_Response(0, $this->xmlrpcerr['no_data'], $this->xmlrpcstr['no_data']); @@ -896,7 +889,7 @@ function open_tag($the_parser, $name, $attrs) $this->xh[$the_parser]['isf'] = 1; break; case 'PARAM': - $this->xh[$the_parser]['value'] = null; + $this->xh[$the_parser]['value'] = NULL; break; case 'VALUE': $this->xh[$the_parser]['vt'] = 'value'; @@ -925,7 +918,7 @@ function open_tag($the_parser, $name, $attrs) $this->xh[$the_parser]['valuestack'][0]['name'] = ''; // Set NULL value to check to see if value passed for this param/member - $this->xh[$the_parser]['value'] = null; + $this->xh[$the_parser]['value'] = NULL; break; case 'DATA': case 'METHODCALL': @@ -1108,7 +1101,7 @@ function character_data($the_parser, $data) $this->xh[$the_parser]['lv'] = 2; // Found a value } - if( ! @isset($this->xh[$the_parser]['ac'])) + if ( ! @isset($this->xh[$the_parser]['ac'])) { $this->xh[$the_parser]['ac'] = ''; } @@ -1123,12 +1116,7 @@ function addParam($par) { $this->params[]=$par; } function output_parameters($array=FALSE) { $CI =& get_instance(); - - if ($this->xss_clean && ! isset($CI->security)) - { - $CI->load->library('security'); - } - + if ($array !== FALSE && is_array($array)) { while (list($key) = each($array)) @@ -1174,11 +1162,11 @@ function decode_message($param) { $kind = $param->kindOf(); - if($kind == 'scalar') + if ($kind == 'scalar') { return $param->scalarval(); } - elseif($kind == 'array') + elseif ($kind == 'array') { reset($param->me); list($a,$b) = each($param->me); @@ -1192,13 +1180,13 @@ function decode_message($param) return $arr; } - elseif($kind == 'struct') + elseif ($kind == 'struct') { reset($param->me['struct']); $arr = array(); - while(list($key,$value) = each($param->me['struct'])) + while (list($key,$value) = each($param->me['struct'])) { $arr[$key] = $this->decode_message($value); } @@ -1343,7 +1331,7 @@ function serializedata($typ, $val) // struct $rs .= "\n"; reset($val); - while(list($key2, $val2) = each($val)) + while (list($key2, $val2) = each($val)) { $rs .= "\n{$key2}\n"; $rs .= $this->serializeval($val2); diff --git a/system/libraries/Xmlrpcs.php b/system/libraries/Xmlrpcs.php index 6bedfe32..9cd33214 100755 --- a/system/libraries/Xmlrpcs.php +++ b/system/libraries/Xmlrpcs.php @@ -59,10 +59,15 @@ public function __construct($config=array()) log_message('debug', "XML-RPC Server Class Initialized"); } - //------------------------------------- - // Initialize Prefs and Serve - //------------------------------------- + // -------------------------------------------------------------------- + /** + * Initialize Prefs and Serve + * + * @access public + * @param mixed + * @return void + */ function initialize($config=array()) { if (isset($config['functions']) && is_array($config['functions'])) @@ -86,11 +91,15 @@ function initialize($config=array()) } } - //------------------------------------- - // Setting of System Methods - //------------------------------------- + // -------------------------------------------------------------------- - function set_system_methods () + /** + * Setting of System Methods + * + * @access public + * @return void + */ + function set_system_methods() { $this->methods = array( 'system.listMethods' => array( @@ -112,11 +121,14 @@ function set_system_methods () ); } + // -------------------------------------------------------------------- - //------------------------------------- - // Main Server Function - //------------------------------------- - + /** + * Main Server Function + * + * @access public + * @return void + */ function serve() { $r = $this->parseRequest(); @@ -129,11 +141,19 @@ function serve() exit($payload); } - //------------------------------------- - // Add Method to Class - //------------------------------------- + // -------------------------------------------------------------------- - function add_to_map($methodname,$function,$sig,$doc) + /** + * Add Method to Class + * + * @access public + * @param string method name + * @param string function + * @param string signature + * @param string docstring + * @return void + */ + function add_to_map($methodname, $function, $sig, $doc) { $this->methods[$methodname] = array( 'function' => $function, @@ -142,11 +162,15 @@ function add_to_map($methodname,$function,$sig,$doc) ); } + // -------------------------------------------------------------------- - //------------------------------------- - // Parse Server Request - //------------------------------------- - + /** + * Parse Server Request + * + * @access public + * @param string data + * @return object xmlrpc response + */ function parseRequest($data='') { global $HTTP_RAW_POST_DATA; @@ -196,7 +220,7 @@ function parseRequest($data='') xml_get_current_line_number($parser))); xml_parser_free($parser); } - elseif($parser_object->xh[$parser]['isf']) + elseif ($parser_object->xh[$parser]['isf']) { return new XML_RPC_Response(0, $this->xmlrpcerr['invalid_return'], $this->xmlrpcstr['invalid_return']); } @@ -207,7 +231,7 @@ function parseRequest($data='') $m = new XML_RPC_Message($parser_object->xh[$parser]['method']); $plist=''; - for($i=0; $i < count($parser_object->xh[$parser]['params']); $i++) + for ($i=0; $i < count($parser_object->xh[$parser]['params']); $i++) { if ($this->debug === TRUE) { @@ -239,10 +263,15 @@ function parseRequest($data='') return $r; } - //------------------------------------- - // Executes the Method - //------------------------------------- + // -------------------------------------------------------------------- + /** + * Executes the Method + * + * @access protected + * @param object + * @return mixed + */ function _execute($m) { $methName = $m->method_name; @@ -297,13 +326,13 @@ function _execute($m) if (isset($this->methods[$methName]['signature'])) { $sig = $this->methods[$methName]['signature']; - for($i=0; $iparams)+1) { - for($n=0; $n < count($m->params); $n++) + for ($n=0; $n < count($m->params); $n++) { $p = $m->params[$n]; $pt = ($p->kindOf() == 'scalar') ? $p->scalarval() : $p->kindOf(); @@ -352,23 +381,27 @@ function _execute($m) return call_user_func($this->methods[$methName]['function'], $m); } } + + // -------------------------------------------------------------------- - - //------------------------------------- - // Server Function: List Methods - //------------------------------------- - + /** + * Server Function: List Methods + * + * @access public + * @param mixed + * @return object + */ function listMethods($m) { $v = new XML_RPC_Values(); $output = array(); - foreach($this->methods as $key => $value) + foreach ($this->methods as $key => $value) { $output[] = new XML_RPC_Values($key, 'string'); } - foreach($this->system_methods as $key => $value) + foreach ($this->system_methods as $key => $value) { $output[]= new XML_RPC_Values($key, 'string'); } @@ -376,11 +409,16 @@ function listMethods($m) $v->addArray($output); return new XML_RPC_Response($v); } + + // -------------------------------------------------------------------- - //------------------------------------- - // Server Function: Return Signature for Method - //------------------------------------- - + /** + * Server Function: Return Signature for Method + * + * @access public + * @param mixed + * @return object + */ function methodSignature($m) { $parameters = $m->output_parameters(); @@ -393,11 +431,11 @@ function methodSignature($m) $sigs = array(); $signature = $this->methods[$method_name]['signature']; - for($i=0; $i < count($signature); $i++) + for ($i=0; $i < count($signature); $i++) { $cursig = array(); $inSig = $signature[$i]; - for($j=0; $joutput_parameters(); @@ -437,11 +480,16 @@ function methodHelp($m) return new XML_RPC_Response(0, $this->xmlrpcerr['introspect_unknown'], $this->xmlrpcstr['introspect_unknown']); } } + + // -------------------------------------------------------------------- - //------------------------------------- - // Server Function: Multi-call - //------------------------------------- - + /** + * Server Function: Multi-call + * + * @access public + * @param mixed + * @return object + */ function multicall($m) { // Disabled @@ -459,7 +507,7 @@ function multicall($m) $m = new XML_RPC_Message($value[0]); $plist=''; - for($i=0; $i < count($value[1]); $i++) + for ($i=0; $i < count($value[1]); $i++) { $m->addParam(new XML_RPC_Values($value[1][$i], 'string')); } @@ -477,11 +525,15 @@ function multicall($m) return new XML_RPC_Response(new XML_RPC_Values($result, 'array')); } + // -------------------------------------------------------------------- - //------------------------------------- - // Multi-call Function: Error Handling - //------------------------------------- - + /** + * Multi-call Function: Error Handling + * + * @access public + * @param mixed + * @return object + */ function multicall_error($err) { $str = is_string($err) ? $this->xmlrpcstr["multicall_${err}"] : $err->faultString(); @@ -493,29 +545,45 @@ function multicall_error($err) return new XML_RPC_Values($struct, 'struct'); } + // -------------------------------------------------------------------- - //------------------------------------- - // Multi-call Function: Processes method - //------------------------------------- - + /** + * Multi-call Function: Processes method + * + * @access public + * @param mixed + * @return object + */ function do_multicall($call) { if ($call->kindOf() != 'struct') + { return $this->multicall_error('notstruct'); + } elseif ( ! $methName = $call->me['struct']['methodName']) + { return $this->multicall_error('nomethod'); + } list($scalar_type,$scalar_value)=each($methName->me); $scalar_type = $scalar_type == $this->xmlrpcI4 ? $this->xmlrpcInt : $scalar_type; if ($methName->kindOf() != 'scalar' OR $scalar_type != 'string') + { return $this->multicall_error('notstring'); + } elseif ($scalar_value == 'system.multicall') + { return $this->multicall_error('recursion'); + } elseif ( ! $params = $call->me['struct']['params']) + { return $this->multicall_error('noparams'); + } elseif ($params->kindOf() != 'array') + { return $this->multicall_error('notarray'); + } list($a,$b)=each($params->me); $numParams = count($b); diff --git a/system/libraries/Zip.php b/system/libraries/Zip.php index 80633c70..666327d5 100755 --- a/system/libraries/Zip.php +++ b/system/libraries/Zip.php @@ -289,7 +289,7 @@ function read_dir($path, $preserve_filepath = TRUE, $root_path = NULL) while (FALSE !== ($file = readdir($fp))) { - if(substr($file, 0, 1) == '.') + if (substr($file, 0, 1) == '.') { continue; } From f7be426c90eb56cd7d92182e34cbf893dfb83152 Mon Sep 17 00:00:00 2001 From: Fabio Borraccetti Date: Fri, 26 Aug 2011 16:36:38 +0200 Subject: [PATCH 011/651] remove load library security no more needed with ci 2.0.3 --- application/controllers/api/key.php | 2 +- application/libraries/REST_Controller.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/application/controllers/api/key.php b/application/controllers/api/key.php index 16266bfb..e4468862 100644 --- a/application/controllers/api/key.php +++ b/application/controllers/api/key.php @@ -194,7 +194,7 @@ public function regenerate_post() private function _generate_key() { - $this->load->helper('security'); + //$this->load->helper('security'); do { diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 7e0d9dce..1ed424bc 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -40,7 +40,7 @@ public function __construct() // Set up our GET variables $this->_get_args = array_merge($this->_get_args, $this->uri->ruri_to_assoc()); - $this->load->library('security'); + //$this->load->library('security'); // This library is bundled with REST_Controller 2.5+, but will eventually be part of CodeIgniter itself $this->load->library('format'); From ed38c9749992a58553b68fdc325784c998d6ebea Mon Sep 17 00:00:00 2001 From: Rene Brokholm Date: Fri, 9 Sep 2011 14:30:02 +0200 Subject: [PATCH 012/651] Added missing codeigniter file system/core/Security.php --- system/core/Security.php | 832 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 832 insertions(+) create mode 100755 system/core/Security.php diff --git a/system/core/Security.php b/system/core/Security.php new file mode 100755 index 00000000..3617cadc --- /dev/null +++ b/system/core/Security.php @@ -0,0 +1,832 @@ + '[removed]', + 'document.write' => '[removed]', + '.parentNode' => '[removed]', + '.innerHTML' => '[removed]', + 'window.location' => '[removed]', + '-moz-binding' => '[removed]', + '' => '-->', + ' '<![CDATA[' + ); + + /* never allowed, regex replacement */ + protected $_never_allowed_regex = array( + "javascript\s*:" => '[removed]', + "expression\s*(\(|&\#40;)" => '[removed]', // CSS and IE + "vbscript\s*:" => '[removed]', // IE, surprise! + "Redirect\s+302" => '[removed]' + ); + + /** + * Constructor + */ + public function __construct() + { + // CSRF config + foreach(array('csrf_expire', 'csrf_token_name', 'csrf_cookie_name') as $key) + { + if (FALSE !== ($val = config_item($key))) + { + $this->{'_'.$key} = $val; + } + } + + // Append application specific cookie prefix + if (config_item('cookie_prefix')) + { + $this->_csrf_cookie_name = config_item('cookie_prefix').$this->_csrf_cookie_name; + } + + // Set the CSRF hash + $this->_csrf_set_hash(); + + log_message('debug', "Security Class Initialized"); + } + + // -------------------------------------------------------------------- + + /** + * Verify Cross Site Request Forgery Protection + * + * @return object + */ + public function csrf_verify() + { + // If no POST data exists we will set the CSRF cookie + if (count($_POST) == 0) + { + return $this->csrf_set_cookie(); + } + + // Do the tokens exist in both the _POST and _COOKIE arrays? + if ( ! isset($_POST[$this->_csrf_token_name]) OR + ! isset($_COOKIE[$this->_csrf_cookie_name])) + { + $this->csrf_show_error(); + } + + // Do the tokens match? + if ($_POST[$this->_csrf_token_name] != $_COOKIE[$this->_csrf_cookie_name]) + { + $this->csrf_show_error(); + } + + // We kill this since we're done and we don't want to + // polute the _POST array + unset($_POST[$this->_csrf_token_name]); + + // Nothing should last forever + unset($_COOKIE[$this->_csrf_cookie_name]); + $this->_csrf_set_hash(); + $this->csrf_set_cookie(); + + log_message('debug', "CSRF token verified "); + + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Set Cross Site Request Forgery Protection Cookie + * + * @return object + */ + public function csrf_set_cookie() + { + $expire = time() + $this->_csrf_expire; + $secure_cookie = (config_item('cookie_secure') === TRUE) ? 1 : 0; + + if ($secure_cookie) + { + $req = isset($_SERVER['HTTPS']) ? $_SERVER['HTTPS'] : FALSE; + + if ( ! $req OR $req == 'off') + { + return FALSE; + } + } + + setcookie($this->_csrf_cookie_name, $this->_csrf_hash, $expire, config_item('cookie_path'), config_item('cookie_domain'), $secure_cookie); + + log_message('debug', "CRSF cookie Set"); + + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Show CSRF Error + * + * @return void + */ + public function csrf_show_error() + { + show_error('The action you have requested is not allowed.'); + } + + // -------------------------------------------------------------------- + + /** + * Get CSRF Hash + * + * Getter Method + * + * @return string self::_csrf_hash + */ + public function get_csrf_hash() + { + return $this->_csrf_hash; + } + + // -------------------------------------------------------------------- + + /** + * Get CSRF Token Name + * + * Getter Method + * + * @return string self::csrf_token_name + */ + public function get_csrf_token_name() + { + return $this->_csrf_token_name; + } + + // -------------------------------------------------------------------- + + /** + * XSS Clean + * + * Sanitizes data so that Cross Site Scripting Hacks can be + * prevented. This function does a fair amount of work but + * it is extremely thorough, designed to prevent even the + * most obscure XSS attempts. Nothing is ever 100% foolproof, + * of course, but I haven't been able to get anything passed + * the filter. + * + * Note: This function should only be used to deal with data + * upon submission. It's not something that should + * be used for general runtime processing. + * + * This function was based in part on some code and ideas I + * got from Bitflux: http://channel.bitflux.ch/wiki/XSS_Prevention + * + * To help develop this script I used this great list of + * vulnerabilities along with a few other hacks I've + * harvested from examining vulnerabilities in other programs: + * http://ha.ckers.org/xss.html + * + * @param mixed string or array + * @return string + */ + public function xss_clean($str, $is_image = FALSE) + { + /* + * Is the string an array? + * + */ + if (is_array($str)) + { + while (list($key) = each($str)) + { + $str[$key] = $this->xss_clean($str[$key]); + } + + return $str; + } + + /* + * Remove Invisible Characters + */ + $str = remove_invisible_characters($str); + + // Validate Entities in URLs + $str = $this->_validate_entities($str); + + /* + * URL Decode + * + * Just in case stuff like this is submitted: + * + * Google + * + * Note: Use rawurldecode() so it does not remove plus signs + * + */ + $str = rawurldecode($str); + + /* + * Convert character entities to ASCII + * + * This permits our tests below to work reliably. + * We only convert entities that are within tags since + * these are the ones that will pose security problems. + * + */ + + $str = preg_replace_callback("/[a-z]+=([\'\"]).*?\\1/si", array($this, '_convert_attribute'), $str); + + $str = preg_replace_callback("/<\w+.*?(?=>|<|$)/si", array($this, '_decode_entity'), $str); + + /* + * Remove Invisible Characters Again! + */ + $str = remove_invisible_characters($str); + + /* + * Convert all tabs to spaces + * + * This prevents strings like this: ja vascript + * NOTE: we deal with spaces between characters later. + * NOTE: preg_replace was found to be amazingly slow here on + * large blocks of data, so we use str_replace. + */ + + if (strpos($str, "\t") !== FALSE) + { + $str = str_replace("\t", ' ', $str); + } + + /* + * Capture converted string for later comparison + */ + $converted_string = $str; + + // Remove Strings that are never allowed + $str = $this->_do_never_allowed($str); + + /* + * Makes PHP tags safe + * + * Note: XML tags are inadvertently replaced too: + * + * '), array('<?', '?>'), $str); + } + + /* + * Compact any exploded words + * + * This corrects words like: j a v a s c r i p t + * These words are compacted back to their correct state. + */ + $words = array( + 'javascript', 'expression', 'vbscript', 'script', + 'applet', 'alert', 'document', 'write', 'cookie', 'window' + ); + + foreach ($words as $word) + { + $temp = ''; + + for ($i = 0, $wordlen = strlen($word); $i < $wordlen; $i++) + { + $temp .= substr($word, $i, 1)."\s*"; + } + + // We only want to do this when it is followed by a non-word character + // That way valid stuff like "dealer to" does not become "dealerto" + $str = preg_replace_callback('#('.substr($temp, 0, -3).')(\W)#is', array($this, '_compact_exploded_words'), $str); + } + + /* + * Remove disallowed Javascript in links or img tags + * We used to do some version comparisons and use of stripos for PHP5, + * but it is dog slow compared to these simplified non-capturing + * preg_match(), especially if the pattern exists in the string + */ + do + { + $original = $str; + + if (preg_match("/]*?)(>|$)#si", array($this, '_js_link_removal'), $str); + } + + if (preg_match("/]*?)(\s?/?>|$)#si", array($this, '_js_img_removal'), $str); + } + + if (preg_match("/script/i", $str) OR preg_match("/xss/i", $str)) + { + $str = preg_replace("#<(/*)(script|xss)(.*?)\>#si", '[removed]', $str); + } + } + while($original != $str); + + unset($original); + + // Remove evil attributes such as style, onclick and xmlns + $str = $this->_remove_evil_attributes($str, $is_image); + + /* + * Sanitize naughty HTML elements + * + * If a tag containing any of the words in the list + * below is found, the tag gets converted to entities. + * + * So this: + * Becomes: <blink> + */ + $naughty = 'alert|applet|audio|basefont|base|behavior|bgsound|blink|body|embed|expression|form|frameset|frame|head|html|ilayer|iframe|input|isindex|layer|link|meta|object|plaintext|style|script|textarea|title|video|xml|xss'; + $str = preg_replace_callback('#<(/*\s*)('.$naughty.')([^><]*)([><]*)#is', array($this, '_sanitize_naughty_html'), $str); + + /* + * Sanitize naughty scripting elements + * + * Similar to above, only instead of looking for + * tags it looks for PHP and JavaScript commands + * that are disallowed. Rather than removing the + * code, it simply converts the parenthesis to entities + * rendering the code un-executable. + * + * For example: eval('some code') + * Becomes: eval('some code') + */ + $str = preg_replace('#(alert|cmd|passthru|eval|exec|expression|system|fopen|fsockopen|file|file_get_contents|readfile|unlink)(\s*)\((.*?)\)#si', "\\1\\2(\\3)", $str); + + + // Final clean up + // This adds a bit of extra precaution in case + // something got through the above filters + $str = $this->_do_never_allowed($str); + + /* + * Images are Handled in a Special Way + * - Essentially, we want to know that after all of the character + * conversion is done whether any unwanted, likely XSS, code was found. + * If not, we return TRUE, as the image is clean. + * However, if the string post-conversion does not matched the + * string post-removal of XSS, then it fails, as there was unwanted XSS + * code found and removed/changed during processing. + */ + + if ($is_image === TRUE) + { + return ($str == $converted_string) ? TRUE: FALSE; + } + + log_message('debug', "XSS Filtering completed"); + return $str; + } + + // -------------------------------------------------------------------- + + /** + * Random Hash for protecting URLs + * + * @return string + */ + public function xss_hash() + { + if ($this->_xss_hash == '') + { + if (phpversion() >= 4.2) + { + mt_srand(); + } + else + { + mt_srand(hexdec(substr(md5(microtime()), -8)) & 0x7fffffff); + } + + $this->_xss_hash = md5(time() + mt_rand(0, 1999999999)); + } + + return $this->_xss_hash; + } + + // -------------------------------------------------------------------- + + /** + * HTML Entities Decode + * + * This function is a replacement for html_entity_decode() + * + * In some versions of PHP the native function does not work + * when UTF-8 is the specified character set, so this gives us + * a work-around. More info here: + * http://bugs.php.net/bug.php?id=25670 + * + * NOTE: html_entity_decode() has a bug in some PHP versions when UTF-8 is the + * character set, and the PHP developers said they were not back porting the + * fix to versions other than PHP 5.x. + * + * @param string + * @param string + * @return string + */ + public function entity_decode($str, $charset='UTF-8') + { + if (stristr($str, '&') === FALSE) return $str; + + // The reason we are not using html_entity_decode() by itself is because + // while it is not technically correct to leave out the semicolon + // at the end of an entity most browsers will still interpret the entity + // correctly. html_entity_decode() does not convert entities without + // semicolons, so we are left with our own little solution here. Bummer. + + if (function_exists('html_entity_decode') && + (strtolower($charset) != 'utf-8')) + { + $str = html_entity_decode($str, ENT_COMPAT, $charset); + $str = preg_replace('~&#x(0*[0-9a-f]{2,5})~ei', 'chr(hexdec("\\1"))', $str); + return preg_replace('~&#([0-9]{2,4})~e', 'chr(\\1)', $str); + } + + // Numeric Entities + $str = preg_replace('~&#x(0*[0-9a-f]{2,5});{0,1}~ei', 'chr(hexdec("\\1"))', $str); + $str = preg_replace('~&#([0-9]{2,4});{0,1}~e', 'chr(\\1)', $str); + + // Literal Entities - Slightly slow so we do another check + if (stristr($str, '&') === FALSE) + { + $str = strtr($str, array_flip(get_html_translation_table(HTML_ENTITIES))); + } + + return $str; + } + + // -------------------------------------------------------------------- + + /** + * Filename Security + * + * @param string + * @return string + */ + public function sanitize_filename($str, $relative_path = FALSE) + { + $bad = array( + "../", + "", + "<", + ">", + "'", + '"', + '&', + '$', + '#', + '{', + '}', + '[', + ']', + '=', + ';', + '?', + "%20", + "%22", + "%3c", // < + "%253c", // < + "%3e", // > + "%0e", // > + "%28", // ( + "%29", // ) + "%2528", // ( + "%26", // & + "%24", // $ + "%3f", // ? + "%3b", // ; + "%3d" // = + ); + + if ( ! $relative_path) + { + $bad[] = './'; + $bad[] = '/'; + } + + $str = remove_invisible_characters($str, FALSE); + return stripslashes(str_replace($bad, '', $str)); + } + + // ---------------------------------------------------------------- + + /** + * Compact Exploded Words + * + * Callback function for xss_clean() to remove whitespace from + * things like j a v a s c r i p t + * + * @param type + * @return type + */ + protected function _compact_exploded_words($matches) + { + return preg_replace('/\s+/s', '', $matches[1]).$matches[2]; + } + + // -------------------------------------------------------------------- + + /* + * Remove Evil HTML Attributes (like evenhandlers and style) + * + * It removes the evil attribute and either: + * - Everything up until a space + * For example, everything between the pipes: + * + * - Everything inside the quotes + * For example, everything between the pipes: + * + * + * @param string $str The string to check + * @param boolean $is_image TRUE if this is an image + * @return string The string with the evil attributes removed + */ + protected function _remove_evil_attributes($str, $is_image) + { + // All javascript event handlers (e.g. onload, onclick, onmouseover), style, and xmlns + $evil_attributes = array('on\w*', 'style', 'xmlns'); + + if ($is_image === TRUE) + { + /* + * Adobe Photoshop puts XML metadata into JFIF images, + * including namespacing, so we have to allow this for images. + */ + unset($evil_attributes[array_search('xmlns', $evil_attributes)]); + } + + do { + $str = preg_replace( + "#<(/?[^><]+?)([^A-Za-z\-])(".implode('|', $evil_attributes).")(\s*=\s*)([\"][^>]*?[\"]|[\'][^>]*?[\']|[^>]*?)([\s><])([><]*)#i", + "<$1$6", + $str, -1, $count + ); + } while ($count); + + return $str; + } + + // -------------------------------------------------------------------- + + /** + * Sanitize Naughty HTML + * + * Callback function for xss_clean() to remove naughty HTML elements + * + * @param array + * @return string + */ + protected function _sanitize_naughty_html($matches) + { + // encode opening brace + $str = '<'.$matches[1].$matches[2].$matches[3]; + + // encode captured opening or closing brace to prevent recursive vectors + $str .= str_replace(array('>', '<'), array('>', '<'), + $matches[4]); + + return $str; + } + + // -------------------------------------------------------------------- + + /** + * JS Link Removal + * + * Callback function for xss_clean() to sanitize links + * This limits the PCRE backtracks, making it more performance friendly + * and prevents PREG_BACKTRACK_LIMIT_ERROR from being triggered in + * PHP 5.2+ on link-heavy strings + * + * @param array + * @return string + */ + protected function _js_link_removal($match) + { + $attributes = $this->_filter_attributes(str_replace(array('<', '>'), '', $match[1])); + + return str_replace($match[1], preg_replace("#href=.*?(alert\(|alert&\#40;|javascript\:|livescript\:|mocha\:|charset\=|window\.|document\.|\.cookie|_filter_attributes(str_replace(array('<', '>'), '', $match[1])); + + return str_replace($match[1], preg_replace("#src=.*?(alert\(|alert&\#40;|javascript\:|livescript\:|mocha\:|charset\=|window\.|document\.|\.cookie|', '<', '\\'), array('>', '<', '\\\\'), $match[0]); + } + + // -------------------------------------------------------------------- + + /** + * Filter Attributes + * + * Filters tag attributes for consistency and safety + * + * @param string + * @return string + */ + protected function _filter_attributes($str) + { + $out = ''; + + if (preg_match_all('#\s*[a-z\-]+\s*=\s*(\042|\047)([^\\1]*?)\\1#is', $str, $matches)) + { + foreach ($matches[0] as $match) + { + $out .= preg_replace("#/\*.*?\*/#s", '', $match); + } + } + + return $out; + } + + // -------------------------------------------------------------------- + + /** + * HTML Entity Decode Callback + * + * Used as a callback for XSS Clean + * + * @param array + * @return string + */ + protected function _decode_entity($match) + { + return $this->entity_decode($match[0], strtoupper(config_item('charset'))); + } + + // -------------------------------------------------------------------- + + /** + * Validate URL entities + * + * Called by xss_clean() + * + * @param string + * @return string + */ + protected function _validate_entities($str) + { + /* + * Protect GET variables in URLs + */ + + // 901119URL5918AMP18930PROTECT8198 + + $str = preg_replace('|\&([a-z\_0-9\-]+)\=([a-z\_0-9\-]+)|i', $this->xss_hash()."\\1=\\2", $str); + + /* + * Validate standard character entities + * + * Add a semicolon if missing. We do this to enable + * the conversion of entities to ASCII later. + * + */ + $str = preg_replace('#(&\#?[0-9a-z]{2,})([\x00-\x20])*;?#i', "\\1;\\2", $str); + + /* + * Validate UTF16 two byte encoding (x00) + * + * Just as above, adds a semicolon if missing. + * + */ + $str = preg_replace('#(&\#x?)([0-9A-F]+);?#i',"\\1\\2;",$str); + + /* + * Un-Protect GET variables in URLs + */ + $str = str_replace($this->xss_hash(), '&', $str); + + return $str; + } + + // ---------------------------------------------------------------------- + + /** + * Do Never Allowed + * + * A utility function for xss_clean() + * + * @param string + * @return string + */ + protected function _do_never_allowed($str) + { + foreach ($this->_never_allowed_str as $key => $val) + { + $str = str_replace($key, $val, $str); + } + + foreach ($this->_never_allowed_regex as $key => $val) + { + $str = preg_replace("#".$key."#i", $val, $str); + } + + return $str; + } + + // -------------------------------------------------------------------- + + /** + * Set Cross Site Request Forgery Protection Cookie + * + * @return string + */ + protected function _csrf_set_hash() + { + if ($this->_csrf_hash == '') + { + // If the cookie exists we will use it's value. + // We don't necessarily want to regenerate it with + // each page load since a page could contain embedded + // sub-pages causing this feature to fail + if (isset($_COOKIE[$this->_csrf_cookie_name]) && + $_COOKIE[$this->_csrf_cookie_name] != '') + { + return $this->_csrf_hash = $_COOKIE[$this->_csrf_cookie_name]; + } + + return $this->_csrf_hash = md5(uniqid(rand(), TRUE)); + } + + return $this->_csrf_hash; + } + +} +// END Security Class + +/* End of file Security.php */ +/* Location: ./system/libraries/Security.php */ From 69beba39703dc195361cf99180ba4d74ba2becd0 Mon Sep 17 00:00:00 2001 From: diegorivera Date: Wed, 21 Sep 2011 12:15:58 -0300 Subject: [PATCH 013/651] Edited application/libraries/REST_Controller.php via GitHub --- application/libraries/REST_Controller.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 64640702..6a10de95 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -223,6 +223,18 @@ public function response($data = array(), $http_code = null) // Otherwise (if no data but 200 provided) or some data, carry on camping! else { + // all the compression settings must be done before sending any headers + // if php is not handling the compression by itself + if (@ini_get('zlib.output_compression') == FALSE) { + // ob_gzhandler depends on zlib + if (extension_loaded('zlib')) { + // if the client supports GZIP compression + if (isset($_SERVER['HTTP_ACCEPT_ENCODING']) AND strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE) { + ob_start('ob_gzhandler'); + } + } + } + is_numeric($http_code) OR $http_code = 200; // If the format method exists, call and return the output in that format From 5ced01f681af4dbc7362465104575c854d5e4399 Mon Sep 17 00:00:00 2001 From: Philo01 Date: Tue, 18 Oct 2011 17:59:18 +0300 Subject: [PATCH 014/651] Bug fix when using default format serialized: $config['rest_default_format'] = 'serialized'; Or when trying to access http://domain.com/users/format/serialized. --- application/libraries/REST_Controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 64640702..d676f2c6 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -20,7 +20,7 @@ class REST_Controller extends CI_Controller { 'rawxml' => 'application/xml', 'json' => 'application/json', 'jsonp' => 'application/javascript', - 'serialize' => 'application/vnd.php.serialized', + 'serialized' => 'application/vnd.php.serialized', 'php' => 'text/plain', 'html' => 'text/html', 'csv' => 'application/csv' From 5e05076c0a6d39c6cdf491e61ab48e4f4e3f71da Mon Sep 17 00:00:00 2001 From: juantorres Date: Thu, 20 Oct 2011 16:35:45 -0700 Subject: [PATCH 015/651] If log is enabled, all request with an invalid API key will be logged. --- application/libraries/REST_Controller.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 64640702..75903e04 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -1,5 +1,12 @@ _allow === FALSE) { + if (config_item('rest_enable_logging') AND $log_method) + { + $this->_log_request(); + } + $this->response(array('status' => false, 'error' => 'Invalid API Key.'), 403); } From 7b0164ac0037237fb9be47929adfd01549f7632f Mon Sep 17 00:00:00 2001 From: juantorres Date: Thu, 20 Oct 2011 16:37:34 -0700 Subject: [PATCH 016/651] Limits where allowing an extra call. This fixes that issue. --- application/libraries/REST_Controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 75903e04..10091d96 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -531,7 +531,7 @@ protected function _check_limit($controller_method) else { // Your luck is out, you've called too many times! - if ($result->count > $limit) + if ($result->count >= $limit) { return FALSE; } From bc9df8ec22acad21ada0b5ebb1604b1aa27ee818 Mon Sep 17 00:00:00 2001 From: juantorres Date: Fri, 21 Oct 2011 14:39:39 -0700 Subject: [PATCH 017/651] Serialized data transfer was not working due to an spelling mistake in the original code. https://github.com/philsturgeon/codeigniter-restserver/pull/61 --- application/libraries/REST_Controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 10091d96..c10cd5c5 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -27,7 +27,7 @@ class REST_Controller extends CI_Controller { 'rawxml' => 'application/xml', 'json' => 'application/json', 'jsonp' => 'application/javascript', - 'serialize' => 'application/vnd.php.serialized', + 'serialized' => 'application/vnd.php.serialized', 'php' => 'text/plain', 'html' => 'text/html', 'csv' => 'application/csv' From db8de8fbebefcf68d5ab2fd70ae9fa3514dd1b7e Mon Sep 17 00:00:00 2001 From: Phil Sturgeon Date: Sun, 23 Oct 2011 12:16:48 +0100 Subject: [PATCH 018/651] Updated README with Changelog. --- README.md | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 04a019ee..d5345d5f 100644 --- a/README.md +++ b/README.md @@ -7,8 +7,8 @@ config file and one controller. ## Requirements -1. PHP 5.1+ -2. CodeIgniter Reactor 2.0 (for 1.7.x support download v2.2 from Downloads tab) +1. PHP 5.2+ +2. CodeIgniter 2.0.3 to 2.1-dev (for 1.7.x support download v2.2 from Downloads tab) ## Usage @@ -24,8 +24,17 @@ but you can read my NetTuts article which covers it's usage along with the REST ### 2.5 -* Instead of just seeing item, item, item, the singular version of the basenode will be used if possible. [Example](http://d.pr/RS46) -* Re-factored to use the Format library, soon to be merged with CodeIgniter. +* Instead of just seeing item, item, item, the singular version of the basenode will be used if possible. [Example](http://d.pr/RS46). +* Re-factored to use the Format library, which will soon be merged with CodeIgniter. +* Fixed Limit bug (limit of 5 would allow 6 requests). +* Added logging for invalid API key requests. +* Changed serialize to serialized. +* Changed all visibility 'private' to 'protected'. +* MIME's with character encodings on the end will now work. +* Fixed PUT arguments. Once again just sending a body query string works. [Example](http://d.pr/cY0b) +* Fixed up all .foo extensions to work when no get arguments provided, and moved .html to Format library. +* Updated key.php example to use config_item('rest_keys_table') instead of hardcoded 'keys' table name. +* Updated REST_Controller to use config_item('rest_limits_table') instead of hardcoded 'limits'. ### 2.4 From ceb7641e838d47546f44463f4735313673dc8994 Mon Sep 17 00:00:00 2001 From: jerel Date: Sun, 6 Nov 2011 14:43:32 -0600 Subject: [PATCH 019/651] Added an IP address whitelist auth option. --- application/config/rest.php | 14 +++++++++++- application/libraries/REST_Controller.php | 27 +++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/application/config/rest.php b/application/config/rest.php index 956c192f..c86bcbd8 100644 --- a/application/config/rest.php +++ b/application/config/rest.php @@ -44,7 +44,8 @@ | | Is login required and if so, which type of login? | -| '' = no login required, 'basic' = unsecure login, 'digest' = more secure login +| '' = no login required, 'basic' = unsecure login, 'digest' = more secure login, +| 'whitelist' = restrict clients to a list of IPs | */ $config['rest_auth'] = false; @@ -84,6 +85,17 @@ */ $config['rest_valid_logins'] = array('admin' => '1234'); +/* +|-------------------------------------------------------------------------- +| REST IP Whitelist +|-------------------------------------------------------------------------- +| +| Limit connections to your REST server to a comma separated +| list of IP addresses +| +*/ +$config['rest_ip_whitelist'] = '127.0.0.1, 0.0.0.0'; + /* |-------------------------------------------------------------------------- | REST Database Group diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index c10cd5c5..967d9aba 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -124,6 +124,10 @@ public function __construct() { $this->_prepare_digest_auth(); } + elseif ($this->config->item('rest_auth') == 'whitelist') + { + $this->_check_whitelist_auth(); + } } // Load DB if its enabled @@ -589,6 +593,13 @@ protected function _auth_override_check() return true; } + // Whitelist auth override found, check client's ip against config whitelist + if ($this->overrides_array[$this->router->class][$this->router->method] == 'whitelist') + { + $this->_check_whitelist_auth(); + return true; + } + // Return false when there is an override value set but it doesn't match 'basic', 'digest', or 'none'. (the value was misspelled) return false; } @@ -757,6 +768,22 @@ protected function _prepare_digest_auth() exit; } } + + // Check if the client's ip is in the 'rest_ip_whitelist' config + public function _check_whitelist_auth() + { + $whitelist = explode(',', $this->config->item('rest_ip_whitelist')); + + foreach ($whitelist AS &$ip) + { + $ip = trim($ip); + } + + if ( ! in_array($this->input->ip_address(), $whitelist)) + { + $this->response(array('status' => false, 'error' => 'Not authorized'), 401); + } + } protected function _force_login($nonce = '') { From 9c998148f0b88b89753ac756abf269f0f32a3bbb Mon Sep 17 00:00:00 2001 From: jerel Date: Sun, 6 Nov 2011 14:50:37 -0600 Subject: [PATCH 020/651] Changed visibility of _check_whitelist_auth() to protected. --- application/libraries/REST_Controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 967d9aba..6ba9bc17 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -770,7 +770,7 @@ protected function _prepare_digest_auth() } // Check if the client's ip is in the 'rest_ip_whitelist' config - public function _check_whitelist_auth() + protected function _check_whitelist_auth() { $whitelist = explode(',', $this->config->item('rest_ip_whitelist')); From 730b5cb7de4a1f4c12e3f2a2ff3ca460e764a25e Mon Sep 17 00:00:00 2001 From: ggoforth Date: Mon, 7 Nov 2011 01:32:50 -0800 Subject: [PATCH 021/651] fixed php error caused by not setting $output in the case of $this->response(array()) --- application/libraries/REST_Controller.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index c10cd5c5..27b91e04 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -230,6 +230,9 @@ public function response($data = array(), $http_code = null) if (empty($data) && $http_code === null) { $http_code = 404; + + //create the output variable here in the case of $this->response(array()); + $output = $data; } // Otherwise (if no data but 200 provided) or some data, carry on camping! From 4ecde70e0d6ae02755c7e7a2a7fe9409f5c3bf0e Mon Sep 17 00:00:00 2001 From: jerel Date: Tue, 8 Nov 2011 01:05:23 -0600 Subject: [PATCH 022/651] Tweaked IP whitelisting a bit to make it more flexible. It can now be used in addition to other auth or as a replacement. --- application/config/rest.php | 26 ++++++++++++++++++++--- application/libraries/REST_Controller.php | 18 ++++++++++++++-- 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/application/config/rest.php b/application/config/rest.php index c86bcbd8..88c2b456 100644 --- a/application/config/rest.php +++ b/application/config/rest.php @@ -44,8 +44,7 @@ | | Is login required and if so, which type of login? | -| '' = no login required, 'basic' = unsecure login, 'digest' = more secure login, -| 'whitelist' = restrict clients to a list of IPs +| '' = no login required, 'basic' = unsecure login, 'digest' = more secure login | */ $config['rest_auth'] = false; @@ -85,6 +84,23 @@ */ $config['rest_valid_logins'] = array('admin' => '1234'); +/* +|-------------------------------------------------------------------------- +| Global IP Whitelisting +|-------------------------------------------------------------------------- +| +| Limit connections to your REST server to whitelisted IP addresses. +| +| Usage: +| 1. Set to true *and* select an auth option for extreme security (client's IP +| address must be in whitelist and they must also log in) +| 2. Set to true with auth set to false to allow whitelisted IPs access with no login. +| 3. Set to false here but set 'auth_override_class_method' to 'whitelist' to +| restrict certain methods to IPs in your whitelist +| +*/ +$config['rest_ip_whitelist_enabled'] = false; + /* |-------------------------------------------------------------------------- | REST IP Whitelist @@ -93,8 +109,12 @@ | Limit connections to your REST server to a comma separated | list of IP addresses | +| Example: $config['rest_ip_whitelist'] = '123.456.789.0, 987.654.32.1'; +| +| 127.0.0.1 and 0.0.0.0 are allowed by default. +| */ -$config['rest_ip_whitelist'] = '127.0.0.1, 0.0.0.0'; +$config['rest_ip_whitelist'] = ''; /* |-------------------------------------------------------------------------- diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 6ba9bc17..af0ff387 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -124,7 +124,7 @@ public function __construct() { $this->_prepare_digest_auth(); } - elseif ($this->config->item('rest_auth') == 'whitelist') + elseif ($this->config->item('rest_ip_whitelist_enabled')) { $this->_check_whitelist_auth(); } @@ -693,6 +693,12 @@ protected function _check_login($username = '', $password = NULL) protected function _prepare_basic_auth() { + // If whitelist is enabled it has the first chance to kick them out + if (config_item('rest_ip_whitelist_enabled')) + { + $this->_check_whitelist_auth(); + } + $username = NULL; $password = NULL; @@ -720,6 +726,12 @@ protected function _prepare_basic_auth() protected function _prepare_digest_auth() { + // If whitelist is enabled it has the first chance to kick them out + if (config_item('rest_ip_whitelist_enabled')) + { + $this->_check_whitelist_auth(); + } + $uniqid = uniqid(""); // Empty argument for backward compatibility // We need to test which server authentication variable to use // because the PHP ISAPI module in IIS acts different from CGI @@ -772,8 +784,10 @@ protected function _prepare_digest_auth() // Check if the client's ip is in the 'rest_ip_whitelist' config protected function _check_whitelist_auth() { - $whitelist = explode(',', $this->config->item('rest_ip_whitelist')); + $whitelist = explode(',', config_item('rest_ip_whitelist')); + array_push($whitelist, '127.0.0.1', '0.0.0.0'); + foreach ($whitelist AS &$ip) { $ip = trim($ip); From c83d87fd9d77bc760e751e88621f1ec1b7da2175 Mon Sep 17 00:00:00 2001 From: Phil Sturgeon Date: Mon, 21 Nov 2011 21:30:01 +0000 Subject: [PATCH 023/651] Separate each piece of the WWW-Authenticate header for digest requests with a comma. --- application/libraries/REST_Controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index bb3195c3..4bdd01a9 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -810,7 +810,7 @@ protected function _force_login($nonce = '') } elseif ($this->config->item('rest_auth') == 'digest') { - header('WWW-Authenticate: Digest realm="' . $this->config->item('rest_realm') . '" qop="auth" nonce="' . $nonce . '" opaque="' . md5($this->config->item('rest_realm')) . '"'); + header('WWW-Authenticate: Digest realm="' . $this->config->item('rest_realm') . '", qop="auth", nonce="' . $nonce . '", opaque="' . md5($this->config->item('rest_realm')) . '"'); } $this->response(array('status' => false, 'error' => 'Not authorized'), 401); From b967f9a49e2b1830cfaa4fd4cd3a5554dc4e190a Mon Sep 17 00:00:00 2001 From: oozman Date: Fri, 16 Dec 2011 17:25:12 +0800 Subject: [PATCH 024/651] fix still empty when having a false value in XML output format. --- application/libraries/Format.php | 263 ------------------------------- 1 file changed, 263 deletions(-) diff --git a/application/libraries/Format.php b/application/libraries/Format.php index 79d3c4e5..e69de29b 100644 --- a/application/libraries/Format.php +++ b/application/libraries/Format.php @@ -1,263 +0,0 @@ -format->factory(array('foo' => 'bar'))->to_xml(); - * - * @param mixed general date to be converted - * @param string data format the file was provided in - * @return Factory - */ - public function factory($data, $from_type = null) - { - // Stupid stuff to emulate the "new static()" stuff in this libraries PHP 5.3 equivilent - $class = __CLASS__; - return new $class($data, $from_type); - } - - /** - * Do not use this directly, call factory() - */ - public function __construct($data = null, $from_type = null) - { - get_instance()->load->helper('inflector'); - - // If the provided data is already formatted we should probably convert it to an array - if ($from_type !== null) - { - if (method_exists($this, '_from_' . $from_type)) - { - $data = call_user_func(array($this, '_from_' . $from_type), $data); - } - - else - { - throw new Exception('Format class does not support conversion from "' . $from_type . '".'); - } - } - - $this->_data = $data; - } - - // FORMATING OUTPUT --------------------------------------------------------- - - public function to_array($data = null) - { - // If not just null, but nopthing is provided - if ($data === null and ! func_num_args()) - { - $data = $this->_data; - } - - $array = array(); - - foreach ((array) $data as $key => $value) - { - if (is_object($value) or is_array($value)) - { - $array[$key] = $this->to_array($value); - } - - else - { - $array[$key] = $value; - } - } - - return $array; - } - - // Format XML for output - public function to_xml($data = null, $structure = null, $basenode = 'xml') - { - if ($data === null and ! func_num_args()) - { - $data = $this->_data; - } - - // turn off compatibility mode as simple xml throws a wobbly if you don't. - if (ini_get('zend.ze1_compatibility_mode') == 1) - { - ini_set('zend.ze1_compatibility_mode', 0); - } - - if ($structure === null) - { - $structure = simplexml_load_string("<$basenode />"); - } - - // Force it to be something useful - if ( ! is_array($data) AND ! is_object($data)) - { - $data = (array) $data; - } - - foreach ($data as $key => $value) - { - // no numeric keys in our xml please! - if (is_numeric($key)) - { - // make string key... - $key = (singular($basenode) != $basenode) ? singular($basenode) : 'item'; - } - - // replace anything not alpha numeric - $key = preg_replace('/[^a-z_\-0-9]/i', '', $key); - - // if there is another array found recrusively call this function - if (is_array($value) || is_object($value)) - { - $node = $structure->addChild($key); - - // recrusive call. - $this->to_xml($value, $node, $key); - } - - else - { - // add single node. - $value = htmlspecialchars(html_entity_decode($value, ENT_QUOTES, 'UTF-8'), ENT_QUOTES, "UTF-8"); - - $structure->addChild($key, $value); - } - } - - return $structure->asXML(); - } - - // Format HTML for output - public function to_html() - { - $data = $this->_data; - - // Multi-dimentional array - if (isset($data[0])) - { - $headings = array_keys($data[0]); - } - - // Single array - else - { - $headings = array_keys($data); - $data = array($data); - } - - $ci = get_instance(); - $ci->load->library('table'); - - $ci->table->set_heading($headings); - - foreach ($data as &$row) - { - $ci->table->add_row($row); - } - - return $ci->table->generate(); - } - - // Format HTML for output - public function to_csv() - { - $data = $this->_data; - - // Multi-dimentional array - if (isset($data[0])) - { - $headings = array_keys($data[0]); - } - - // Single array - else - { - $headings = array_keys($data); - $data = array($data); - } - - $output = implode(',', $headings).PHP_EOL; - foreach ($data as &$row) - { - $output .= '"'.implode('","', $row).'"'.PHP_EOL; - } - - return $output; - } - - // Encode as JSON - public function to_json() - { - return json_encode($this->_data); - } - - // Encode as Serialized array - public function to_serialized() - { - return serialize($this->_data); - } - - // Output as a string representing the PHP structure - public function to_php() - { - return var_export($this->_data, TRUE); - } - - // Format XML for output - protected function _from_xml($string) - { - return $string ? (array) simplexml_load_string($string, 'SimpleXMLElement', LIBXML_NOCDATA) : array(); - } - - // Format HTML for output - // This function is DODGY! Not perfect CSV support but works with my REST_Controller - protected function _from_csv($string) - { - $data = array(); - - // Splits - $rows = explode("\n", trim($string)); - $headings = explode(',', array_shift($rows)); - foreach ($rows as $row) - { - // The substr removes " from start and end - $data_fields = explode('","', trim(substr($row, 1, -1))); - - if (count($data_fields) == count($headings)) - { - $data[] = array_combine($headings, $data_fields); - } - } - - return $data; - } - - // Encode as JSON - private function _from_json($string) - { - return json_decode(trim($string)); - } - - // Encode as Serialized array - private function _from_serialize($string) - { - return unserialize(trim($string)); - } - -} - -/* End of file format.php */ \ No newline at end of file From 03afe6faed8732ede7f55ff5ee383d8a607e9181 Mon Sep 17 00:00:00 2001 From: oozman Date: Fri, 16 Dec 2011 18:38:13 +0800 Subject: [PATCH 025/651] restored back to version 2.5.0 --- application/libraries/Format.php | 263 +++++++++++++++++++++++++++++++ 1 file changed, 263 insertions(+) diff --git a/application/libraries/Format.php b/application/libraries/Format.php index e69de29b..3ac3958e 100644 --- a/application/libraries/Format.php +++ b/application/libraries/Format.php @@ -0,0 +1,263 @@ +format->factory(array('foo' => 'bar'))->to_xml(); + * + * @param mixed general date to be converted + * @param string data format the file was provided in + * @return Factory + */ + public function factory($data, $from_type = null) + { + // Stupid stuff to emulate the "new static()" stuff in this libraries PHP 5.3 equivilent + $class = __CLASS__; + return new $class($data, $from_type); + } + + /** + * Do not use this directly, call factory() + */ + public function __construct($data = null, $from_type = null) + { + get_instance()->load->helper('inflector'); + + // If the provided data is already formatted we should probably convert it to an array + if ($from_type !== null) + { + if (method_exists($this, '_from_' . $from_type)) + { + $data = call_user_func(array($this, '_from_' . $from_type), $data); + } + + else + { + throw new Exception('Format class does not support conversion from "' . $from_type . '".'); + } + } + + $this->_data = $data; + } + + // FORMATING OUTPUT --------------------------------------------------------- + + public function to_array($data = null) + { + // If not just null, but nopthing is provided + if ($data === null and ! func_num_args()) + { + $data = $this->_data; + } + + $array = array(); + + foreach ((array) $data as $key => $value) + { + if (is_object($value) or is_array($value)) + { + $array[$key] = $this->to_array($value); + } + + else + { + $array[$key] = $value; + } + } + + return $array; + } + + // Format XML for output + public function to_xml($data = null, $structure = null, $basenode = 'xml') + { + if ($data === null and ! func_num_args()) + { + $data = $this->_data; + } + + // turn off compatibility mode as simple xml throws a wobbly if you don't. + if (ini_get('zend.ze1_compatibility_mode') == 1) + { + ini_set('zend.ze1_compatibility_mode', 0); + } + + if ($structure === null) + { + $structure = simplexml_load_string("<$basenode />"); + } + + // Force it to be something useful + if ( ! is_array($data) AND ! is_object($data)) + { + $data = (array) $data; + } + + foreach ($data as $key => $value) + { + // no numeric keys in our xml please! + if (is_numeric($key)) + { + // make string key... + $key = (singular($basenode) != $basenode) ? singular($basenode) : 'item'; + } + + // replace anything not alpha numeric + $key = preg_replace('/[^a-z_\-0-9]/i', '', $key); + + // if there is another array found recrusively call this function + if (is_array($value) || is_object($value)) + { + $node = $structure->addChild($key); + + // recrusive call. + $this->to_xml($value, $node, $key); + } + + else + { + // add single node. + $value = htmlspecialchars(html_entity_decode($value, ENT_QUOTES, 'UTF-8'), ENT_QUOTES, "UTF-8"); + + $structure->addChild($key, $value); + } + } + + return $structure->asXML(); + } + + // Format HTML for output + public function to_html() + { + $data = $this->_data; + + // Multi-dimentional array + if (isset($data[0])) + { + $headings = array_keys($data[0]); + } + + // Single array + else + { + $headings = array_keys($data); + $data = array($data); + } + + $ci = get_instance(); + $ci->load->library('table'); + + $ci->table->set_heading($headings); + + foreach ($data as &$row) + { + $ci->table->add_row($row); + } + + return $ci->table->generate(); + } + + // Format HTML for output + public function to_csv() + { + $data = $this->_data; + + // Multi-dimentional array + if (isset($data[0])) + { + $headings = array_keys($data[0]); + } + + // Single array + else + { + $headings = array_keys($data); + $data = array($data); + } + + $output = implode(',', $headings).PHP_EOL; + foreach ($data as &$row) + { + $output .= '"'.implode('","', $row).'"'.PHP_EOL; + } + + return $output; + } + + // Encode as JSON + public function to_json() + { + return json_encode($this->_data); + } + + // Encode as Serialized array + public function to_serialized() + { + return serialize($this->_data); + } + + // Output as a string representing the PHP structure + public function to_php() + { + return var_export($this->_data, TRUE); + } + + // Format XML for output + protected function _from_xml($string) + { + return $string ? (array) simplexml_load_string($string, 'SimpleXMLElement', LIBXML_NOCDATA) : array(); + } + + // Format HTML for output + // This function is DODGY! Not perfect CSV support but works with my REST_Controller + protected function _from_csv($string) + { + $data = array(); + + // Splits + $rows = explode("\n", trim($string)); + $headings = explode(',', array_shift($rows)); + foreach ($rows as $row) + { + // The substr removes " from start and end + $data_fields = explode('","', trim(substr($row, 1, -1))); + + if (count($data_fields) == count($headings)) + { + $data[] = array_combine($headings, $data_fields); + } + } + + return $data; + } + + // Encode as JSON + private function _from_json($string) + { + return json_decode(trim($string)); + } + + // Encode as Serialized array + private function _from_serialize($string) + { + return unserialize(trim($string)); + } + +} + +/* End of file format.php */ \ No newline at end of file From d6c0c6bfc57663035d484ae22f2e62c53739f532 Mon Sep 17 00:00:00 2001 From: oozman Date: Fri, 16 Dec 2011 18:40:15 +0800 Subject: [PATCH 026/651] fix empty node on XML output format if value is equal to "false". --- application/libraries/Format.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/application/libraries/Format.php b/application/libraries/Format.php index 3ac3958e..bd25dc79 100644 --- a/application/libraries/Format.php +++ b/application/libraries/Format.php @@ -134,6 +134,12 @@ public function to_xml($data = null, $structure = null, $basenode = 'xml') // add single node. $value = htmlspecialchars(html_entity_decode($value, ENT_QUOTES, 'UTF-8'), ENT_QUOTES, "UTF-8"); + //change false to 0 + if($value == false) + { + $value = 0; + } + $structure->addChild($key, $value); } } From 48d6311106856f3e88cd184dbf774717fe4c922f Mon Sep 17 00:00:00 2001 From: oozman Date: Fri, 16 Dec 2011 19:12:21 +0800 Subject: [PATCH 027/651] re-implement fix for empty node on XML output format, for false = 0, true = 1. --- application/libraries/Format.php | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/application/libraries/Format.php b/application/libraries/Format.php index bd25dc79..7b0e4116 100644 --- a/application/libraries/Format.php +++ b/application/libraries/Format.php @@ -110,6 +110,13 @@ public function to_xml($data = null, $structure = null, $basenode = 'xml') foreach ($data as $key => $value) { + + //change false/true to 0/1 + if(is_bool($value)) + { + $value = (int) $value; + } + // no numeric keys in our xml please! if (is_numeric($key)) { @@ -133,12 +140,6 @@ public function to_xml($data = null, $structure = null, $basenode = 'xml') { // add single node. $value = htmlspecialchars(html_entity_decode($value, ENT_QUOTES, 'UTF-8'), ENT_QUOTES, "UTF-8"); - - //change false to 0 - if($value == false) - { - $value = 0; - } $structure->addChild($key, $value); } From 6f906992681f67e8403c017d1ffb5548289ea481 Mon Sep 17 00:00:00 2001 From: Phil Sturgeon Date: Fri, 16 Dec 2011 16:39:08 +0000 Subject: [PATCH 028/651] Random space fixes. Whoop. --- application/libraries/REST_Controller.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 4bdd01a9..d87266c5 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -506,7 +506,7 @@ protected function _log_request($authorized = FALSE) protected function _check_limit($controller_method) { // They are special, or it might not even have a limit - if (!empty($this->rest->ignore_limits) OR !isset($this->methods[$controller_method]['limit'])) + if ( ! empty($this->rest->ignore_limits) OR !isset($this->methods[$controller_method]['limit'])) { // On your way sonny-jim. return TRUE; @@ -523,7 +523,7 @@ protected function _check_limit($controller_method) ->row(); // No calls yet, or been an hour since they called - if (!$result OR $result->hour_started < time() - (60 * 60)) + if ( ! $result OR $result->hour_started < time() - (60 * 60)) { // Right, set one up from scratch $this->rest->db->insert(config_item('rest_limits_table'), array( From 656d35f619022a8f8327b8d28b14580730c5111b Mon Sep 17 00:00:00 2001 From: David Freerksen Date: Fri, 16 Dec 2011 13:12:00 -0800 Subject: [PATCH 029/651] Combine both URI segment and GET params instead of using one or the other --- application/libraries/REST_Controller.php | 26 +++++++++++------------ 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index d87266c5..a88c52bf 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -2,9 +2,9 @@ /** * REST_controller V 2.5.x - * + * * @see https://github.com/philsturgeon/codeigniter-restserver - * + * */ class REST_Controller extends CI_Controller { @@ -64,8 +64,8 @@ public function __construct() // Grab proper GET variables parse_str(parse_url(http://github.com/$_SERVER['REQUEST_URI'],%20PHP_URL_QUERY), $get); - // If there are any, populate $this->_get_args - empty($get) OR $this->_get_args = $get; + // Merge both the URI segements and GET params + $this->_get_args = array_merge($this->_get_args, $get); break; case 'post': @@ -86,7 +86,7 @@ public function __construct() { parse_str(file_get_contents('php://input'), $this->_put_args); } - + break; case 'delete': @@ -178,7 +178,7 @@ public function _remap($object_called, $arguments) { $this->_log_request(); } - + $this->response(array('status' => false, 'error' => 'Invalid API Key.'), 403); } @@ -234,7 +234,7 @@ public function response($data = array(), $http_code = null) if (empty($data) && $http_code === null) { $http_code = 404; - + //create the output variable here in the case of $this->response(array()); $output = $data; } @@ -317,7 +317,7 @@ protected function _detect_output_format() { return $matches[1]; } - + // Check if a file extension is used elseif ($this->_get_args AND ! is_array(end($this->_get_args)) AND preg_match($pattern, end($this->_get_args), $matches)) { @@ -371,7 +371,7 @@ protected function _detect_output_format() } } } // End HTTP_ACCEPT checking - + // Well, none of that has worked! Let's see if the controller has a default if ( ! empty($this->rest_format)) { @@ -433,7 +433,7 @@ protected function _detect_api_key() } $this->rest->key = $row->key; - + isset($row->level) AND $this->rest->level = $row->level; isset($row->ignore_limits) AND $this->rest->ignore_limits = $row->ignore_limits; @@ -783,12 +783,12 @@ protected function _prepare_digest_auth() exit; } } - + // Check if the client's ip is in the 'rest_ip_whitelist' config protected function _check_whitelist_auth() { $whitelist = explode(',', config_item('rest_ip_whitelist')); - + array_push($whitelist, '127.0.0.1', '0.0.0.0'); foreach ($whitelist AS &$ip) @@ -831,7 +831,7 @@ protected function _force_loopable($data) // FORMATING FUNCTIONS --------------------------------------------------------- // Many of these have been moved to the Format class for better separation, but these methods will be checked too - + // Encode as JSONP protected function _format_jsonp($data = array()) { From 59f1d077bf8559cab26bcab8c0513806ff9595c1 Mon Sep 17 00:00:00 2001 From: cnanney Date: Fri, 13 Jan 2012 00:17:39 -0600 Subject: [PATCH 030/651] When using Backbone.js and Backbone.emulateHTTP = true, the _method variable is all caps so must be lower-cased to work here. --- application/libraries/REST_Controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 9fb31fcc..baa06fdf 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -406,7 +406,7 @@ protected function _detect_method() if ($this->config->item('enable_emulate_request') && $this->input->post('_method')) { - $method = $this->input->post('_method'); + $method = strtolower($this->input->post('_method')); } if (in_array($method, array('get', 'delete', 'post', 'put'))) From cbb35573da01220f7991419df07de7fb5c821a24 Mon Sep 17 00:00:00 2001 From: Steven Farley Date: Wed, 18 Jan 2012 14:44:15 -0500 Subject: [PATCH 031/651] Updated CodeIgniter files to 2.1.0 (Nov 14, 2011 release) --- application/config/user_agents.php | 0 index.php | 0 system/.htaccess | 0 system/core/Benchmark.php | 5 + system/core/CodeIgniter.php | 40 +- system/core/Common.php | 30 +- system/core/Config.php | 29 +- system/core/Controller.php | 2 +- system/core/Exceptions.php | 17 +- system/core/Hooks.php | 15 + system/core/Input.php | 75 +- system/core/Lang.php | 13 + system/core/Loader.php | 136 ++- system/core/Model.php | 1 + system/core/Output.php | 61 +- system/core/Router.php | 59 +- system/core/Security.php | 208 +++-- system/core/URI.php | 31 + system/core/Utf8.php | 0 system/core/index.html | 0 system/database/DB.php | 4 +- system/database/DB_active_rec.php | 6 +- system/database/DB_cache.php | 2 +- system/database/DB_driver.php | 6 +- system/database/DB_forge.php | 4 +- system/database/DB_result.php | 46 +- system/database/DB_utility.php | 2 +- .../database/drivers/cubrid/cubrid_driver.php | 792 +++++++++++++++++ .../database/drivers/cubrid/cubrid_forge.php | 288 +++++++ .../database/drivers/cubrid/cubrid_result.php | 202 +++++ .../drivers/cubrid/cubrid_utility.php | 108 +++ system/database/drivers/cubrid/index.html | 10 + system/database/drivers/index.html | 0 system/database/drivers/mssql/index.html | 0 .../database/drivers/mssql/mssql_driver.php | 1 + system/database/drivers/mssql/mssql_forge.php | 0 .../database/drivers/mssql/mssql_result.php | 0 .../database/drivers/mssql/mssql_utility.php | 0 system/database/drivers/mysql/index.html | 0 .../database/drivers/mysql/mysql_driver.php | 14 +- system/database/drivers/mysql/mysql_forge.php | 0 .../database/drivers/mysql/mysql_result.php | 17 +- .../database/drivers/mysql/mysql_utility.php | 2 +- system/database/drivers/mysqli/index.html | 0 .../database/drivers/mysqli/mysqli_driver.php | 33 +- .../database/drivers/mysqli/mysqli_forge.php | 0 .../database/drivers/mysqli/mysqli_result.php | 19 +- .../drivers/mysqli/mysqli_utility.php | 0 system/database/drivers/oci8/index.html | 0 system/database/drivers/oci8/oci8_driver.php | 161 ++-- system/database/drivers/oci8/oci8_forge.php | 0 system/database/drivers/oci8/oci8_result.php | 98 +-- system/database/drivers/oci8/oci8_utility.php | 0 system/database/drivers/odbc/index.html | 0 system/database/drivers/odbc/odbc_driver.php | 5 +- system/database/drivers/odbc/odbc_forge.php | 0 system/database/drivers/odbc/odbc_result.php | 0 system/database/drivers/odbc/odbc_utility.php | 0 system/database/drivers/pdo/index.html | 10 + system/database/drivers/pdo/pdo_driver.php | 803 ++++++++++++++++++ system/database/drivers/pdo/pdo_forge.php | 266 ++++++ system/database/drivers/pdo/pdo_result.php | 171 ++++ system/database/drivers/pdo/pdo_utility.php | 103 +++ system/database/drivers/postgre/index.html | 0 .../drivers/postgre/postgre_driver.php | 1 + .../drivers/postgre/postgre_forge.php | 0 .../drivers/postgre/postgre_result.php | 0 .../drivers/postgre/postgre_utility.php | 0 system/database/drivers/sqlite/index.html | 0 .../database/drivers/sqlite/sqlite_driver.php | 1 + .../database/drivers/sqlite/sqlite_forge.php | 0 .../database/drivers/sqlite/sqlite_result.php | 0 .../drivers/sqlite/sqlite_utility.php | 0 system/database/drivers/sqlsrv/index.html | 10 + .../database/drivers/sqlsrv/sqlsrv_driver.php | 599 +++++++++++++ .../database/drivers/sqlsrv/sqlsrv_forge.php | 248 ++++++ .../database/drivers/sqlsrv/sqlsrv_result.php | 169 ++++ .../drivers/sqlsrv/sqlsrv_utility.php | 88 ++ system/database/index.html | 0 system/fonts/index.html | 0 system/fonts/texb.ttf | Bin system/helpers/array_helper.php | 0 system/helpers/captcha_helper.php | 0 system/helpers/cookie_helper.php | 0 system/helpers/date_helper.php | 2 +- system/helpers/directory_helper.php | 0 system/helpers/download_helper.php | 0 system/helpers/email_helper.php | 0 system/helpers/file_helper.php | 0 system/helpers/form_helper.php | 8 +- system/helpers/html_helper.php | 0 system/helpers/index.html | 0 system/helpers/inflector_helper.php | 0 system/helpers/language_helper.php | 0 system/helpers/number_helper.php | 0 system/helpers/path_helper.php | 0 system/helpers/security_helper.php | 0 system/helpers/smiley_helper.php | 0 system/helpers/string_helper.php | 17 + system/helpers/text_helper.php | 0 system/helpers/typography_helper.php | 0 system/helpers/url_helper.php | 0 system/helpers/xml_helper.php | 0 system/index.html | 0 system/language/english/calendar_lang.php | 0 system/language/english/date_lang.php | 0 system/language/english/db_lang.php | 0 system/language/english/email_lang.php | 0 .../language/english/form_validation_lang.php | 1 + system/language/english/ftp_lang.php | 0 system/language/english/imglib_lang.php | 0 system/language/english/index.html | 0 system/language/english/migration_lang.php | 13 + system/language/english/number_lang.php | 0 system/language/english/profiler_lang.php | 0 system/language/english/unit_test_lang.php | 0 system/language/english/upload_lang.php | 0 system/language/index.html | 0 system/libraries/Cache/Cache.php | 0 system/libraries/Cache/drivers/Cache_apc.php | 4 +- .../libraries/Cache/drivers/Cache_dummy.php | 0 system/libraries/Cache/drivers/Cache_file.php | 0 .../Cache/drivers/Cache_memcached.php | 11 +- system/libraries/Calendar.php | 0 system/libraries/Cart.php | 8 +- system/libraries/Driver.php | 4 +- system/libraries/Email.php | 154 ++-- system/libraries/Encrypt.php | 0 system/libraries/Form_validation.php | 119 +-- system/libraries/Ftp.php | 0 system/libraries/Image_lib.php | 0 system/libraries/Javascript.php | 0 system/libraries/Log.php | 0 system/libraries/Migration.php | 338 ++++++++ system/libraries/Pagination.php | 85 +- system/libraries/Parser.php | 0 system/libraries/Profiler.php | 0 system/libraries/Session.php | 3 +- system/libraries/Sha1.php | 0 system/libraries/Table.php | 0 system/libraries/Trackback.php | 0 system/libraries/Typography.php | 0 system/libraries/Unit_test.php | 0 system/libraries/Upload.php | 66 +- system/libraries/User_agent.php | 2 +- system/libraries/Xmlrpc.php | 6 +- system/libraries/Xmlrpcs.php | 0 system/libraries/Zip.php | 0 system/libraries/index.html | 0 system/libraries/javascript/Jquery.php | 0 150 files changed, 5360 insertions(+), 492 deletions(-) mode change 100755 => 100644 application/config/user_agents.php mode change 100755 => 100644 index.php mode change 100755 => 100644 system/.htaccess mode change 100755 => 100644 system/core/Common.php mode change 100755 => 100644 system/core/Controller.php mode change 100755 => 100644 system/core/Loader.php mode change 100755 => 100644 system/core/Utf8.php mode change 100755 => 100644 system/core/index.html mode change 100755 => 100644 system/database/DB_active_rec.php mode change 100755 => 100644 system/database/DB_cache.php mode change 100755 => 100644 system/database/DB_driver.php mode change 100755 => 100644 system/database/DB_forge.php mode change 100755 => 100644 system/database/DB_result.php mode change 100755 => 100644 system/database/DB_utility.php create mode 100644 system/database/drivers/cubrid/cubrid_driver.php create mode 100644 system/database/drivers/cubrid/cubrid_forge.php create mode 100644 system/database/drivers/cubrid/cubrid_result.php create mode 100644 system/database/drivers/cubrid/cubrid_utility.php create mode 100644 system/database/drivers/cubrid/index.html mode change 100755 => 100644 system/database/drivers/index.html mode change 100755 => 100644 system/database/drivers/mssql/index.html mode change 100755 => 100644 system/database/drivers/mssql/mssql_driver.php mode change 100755 => 100644 system/database/drivers/mssql/mssql_forge.php mode change 100755 => 100644 system/database/drivers/mssql/mssql_result.php mode change 100755 => 100644 system/database/drivers/mssql/mssql_utility.php mode change 100755 => 100644 system/database/drivers/mysql/index.html mode change 100755 => 100644 system/database/drivers/mysql/mysql_driver.php mode change 100755 => 100644 system/database/drivers/mysql/mysql_forge.php mode change 100755 => 100644 system/database/drivers/mysql/mysql_result.php mode change 100755 => 100644 system/database/drivers/mysql/mysql_utility.php mode change 100755 => 100644 system/database/drivers/mysqli/index.html mode change 100755 => 100644 system/database/drivers/mysqli/mysqli_driver.php mode change 100755 => 100644 system/database/drivers/mysqli/mysqli_forge.php mode change 100755 => 100644 system/database/drivers/mysqli/mysqli_result.php mode change 100755 => 100644 system/database/drivers/mysqli/mysqli_utility.php mode change 100755 => 100644 system/database/drivers/oci8/index.html mode change 100755 => 100644 system/database/drivers/oci8/oci8_driver.php mode change 100755 => 100644 system/database/drivers/oci8/oci8_forge.php mode change 100755 => 100644 system/database/drivers/oci8/oci8_result.php mode change 100755 => 100644 system/database/drivers/oci8/oci8_utility.php mode change 100755 => 100644 system/database/drivers/odbc/index.html mode change 100755 => 100644 system/database/drivers/odbc/odbc_driver.php mode change 100755 => 100644 system/database/drivers/odbc/odbc_forge.php mode change 100755 => 100644 system/database/drivers/odbc/odbc_result.php mode change 100755 => 100644 system/database/drivers/odbc/odbc_utility.php create mode 100644 system/database/drivers/pdo/index.html create mode 100644 system/database/drivers/pdo/pdo_driver.php create mode 100644 system/database/drivers/pdo/pdo_forge.php create mode 100644 system/database/drivers/pdo/pdo_result.php create mode 100644 system/database/drivers/pdo/pdo_utility.php mode change 100755 => 100644 system/database/drivers/postgre/index.html mode change 100755 => 100644 system/database/drivers/postgre/postgre_driver.php mode change 100755 => 100644 system/database/drivers/postgre/postgre_forge.php mode change 100755 => 100644 system/database/drivers/postgre/postgre_result.php mode change 100755 => 100644 system/database/drivers/postgre/postgre_utility.php mode change 100755 => 100644 system/database/drivers/sqlite/index.html mode change 100755 => 100644 system/database/drivers/sqlite/sqlite_driver.php mode change 100755 => 100644 system/database/drivers/sqlite/sqlite_forge.php mode change 100755 => 100644 system/database/drivers/sqlite/sqlite_result.php mode change 100755 => 100644 system/database/drivers/sqlite/sqlite_utility.php create mode 100644 system/database/drivers/sqlsrv/index.html create mode 100644 system/database/drivers/sqlsrv/sqlsrv_driver.php create mode 100644 system/database/drivers/sqlsrv/sqlsrv_forge.php create mode 100644 system/database/drivers/sqlsrv/sqlsrv_result.php create mode 100644 system/database/drivers/sqlsrv/sqlsrv_utility.php mode change 100755 => 100644 system/database/index.html mode change 100755 => 100644 system/fonts/index.html mode change 100755 => 100644 system/fonts/texb.ttf mode change 100755 => 100644 system/helpers/array_helper.php mode change 100755 => 100644 system/helpers/captcha_helper.php mode change 100755 => 100644 system/helpers/cookie_helper.php mode change 100755 => 100644 system/helpers/date_helper.php mode change 100755 => 100644 system/helpers/directory_helper.php mode change 100755 => 100644 system/helpers/download_helper.php mode change 100755 => 100644 system/helpers/email_helper.php mode change 100755 => 100644 system/helpers/file_helper.php mode change 100755 => 100644 system/helpers/form_helper.php mode change 100755 => 100644 system/helpers/html_helper.php mode change 100755 => 100644 system/helpers/index.html mode change 100755 => 100644 system/helpers/inflector_helper.php mode change 100755 => 100644 system/helpers/language_helper.php mode change 100755 => 100644 system/helpers/number_helper.php mode change 100755 => 100644 system/helpers/path_helper.php mode change 100755 => 100644 system/helpers/security_helper.php mode change 100755 => 100644 system/helpers/smiley_helper.php mode change 100755 => 100644 system/helpers/string_helper.php mode change 100755 => 100644 system/helpers/text_helper.php mode change 100755 => 100644 system/helpers/typography_helper.php mode change 100755 => 100644 system/helpers/url_helper.php mode change 100755 => 100644 system/helpers/xml_helper.php mode change 100755 => 100644 system/index.html mode change 100755 => 100644 system/language/english/calendar_lang.php mode change 100755 => 100644 system/language/english/date_lang.php mode change 100755 => 100644 system/language/english/db_lang.php mode change 100755 => 100644 system/language/english/email_lang.php mode change 100755 => 100644 system/language/english/form_validation_lang.php mode change 100755 => 100644 system/language/english/ftp_lang.php mode change 100755 => 100644 system/language/english/imglib_lang.php mode change 100755 => 100644 system/language/english/index.html create mode 100644 system/language/english/migration_lang.php mode change 100755 => 100644 system/language/english/number_lang.php mode change 100755 => 100644 system/language/english/profiler_lang.php mode change 100755 => 100644 system/language/english/unit_test_lang.php mode change 100755 => 100644 system/language/english/upload_lang.php mode change 100755 => 100644 system/language/index.html mode change 100755 => 100644 system/libraries/Cache/Cache.php mode change 100755 => 100644 system/libraries/Cache/drivers/Cache_apc.php mode change 100755 => 100644 system/libraries/Cache/drivers/Cache_dummy.php mode change 100755 => 100644 system/libraries/Cache/drivers/Cache_file.php mode change 100755 => 100644 system/libraries/Cache/drivers/Cache_memcached.php mode change 100755 => 100644 system/libraries/Calendar.php mode change 100755 => 100644 system/libraries/Cart.php mode change 100755 => 100644 system/libraries/Driver.php mode change 100755 => 100644 system/libraries/Email.php mode change 100755 => 100644 system/libraries/Encrypt.php mode change 100755 => 100644 system/libraries/Form_validation.php mode change 100755 => 100644 system/libraries/Ftp.php mode change 100755 => 100644 system/libraries/Image_lib.php mode change 100755 => 100644 system/libraries/Javascript.php mode change 100755 => 100644 system/libraries/Log.php create mode 100644 system/libraries/Migration.php mode change 100755 => 100644 system/libraries/Pagination.php mode change 100755 => 100644 system/libraries/Parser.php mode change 100755 => 100644 system/libraries/Profiler.php mode change 100755 => 100644 system/libraries/Session.php mode change 100755 => 100644 system/libraries/Sha1.php mode change 100755 => 100644 system/libraries/Table.php mode change 100755 => 100644 system/libraries/Trackback.php mode change 100755 => 100644 system/libraries/Typography.php mode change 100755 => 100644 system/libraries/Unit_test.php mode change 100755 => 100644 system/libraries/Upload.php mode change 100755 => 100644 system/libraries/User_agent.php mode change 100755 => 100644 system/libraries/Xmlrpc.php mode change 100755 => 100644 system/libraries/Xmlrpcs.php mode change 100755 => 100644 system/libraries/Zip.php mode change 100755 => 100644 system/libraries/index.html mode change 100755 => 100644 system/libraries/javascript/Jquery.php diff --git a/application/config/user_agents.php b/application/config/user_agents.php old mode 100755 new mode 100644 diff --git a/index.php b/index.php old mode 100755 new mode 100644 diff --git a/system/.htaccess b/system/.htaccess old mode 100755 new mode 100644 diff --git a/system/core/Benchmark.php b/system/core/Benchmark.php index 515550e9..a200727a 100755 --- a/system/core/Benchmark.php +++ b/system/core/Benchmark.php @@ -29,6 +29,11 @@ */ class CI_Benchmark { + /** + * List of all benchmark markers and when they were added + * + * @var array + */ var $marker = array(); // -------------------------------------------------------------------- diff --git a/system/core/CodeIgniter.php b/system/core/CodeIgniter.php index 94fecb5c..db1aee57 100755 --- a/system/core/CodeIgniter.php +++ b/system/core/CodeIgniter.php @@ -27,17 +27,19 @@ * @link http://codeigniter.com/user_guide/ */ -/* - * ------------------------------------------------------ - * Define the CodeIgniter Version - * ------------------------------------------------------ +/** + * CodeIgniter Version + * + * @var string + * */ - define('CI_VERSION', '2.0.3'); + define('CI_VERSION', '2.1.0'); -/* - * ------------------------------------------------------ - * Define the CodeIgniter Branch (Core = TRUE, Reactor = FALSE) - * ------------------------------------------------------ +/** + * CodeIgniter Branch (Core = TRUE, Reactor = FALSE) + * + * @var boolean + * */ define('CI_CORE', FALSE); @@ -267,7 +269,25 @@ function &get_instance() OR in_array(strtolower($method), array_map('strtolower', get_class_methods('CI_Controller'))) ) { - show_404("{$class}/{$method}"); + if ( ! empty($RTR->routes['404_override'])) + { + $x = explode('/', $RTR->routes['404_override']); + $class = $x[0]; + $method = (isset($x[1]) ? $x[1] : 'index'); + if ( ! class_exists($class)) + { + if ( ! file_exists(APPPATH.'controllers/'.$class.'.php')) + { + show_404("{$class}/{$method}"); + } + + include_once(APPPATH.'controllers/'.$class.'.php'); + } + } + else + { + show_404("{$class}/{$method}"); + } } /* diff --git a/system/core/Common.php b/system/core/Common.php old mode 100755 new mode 100644 index db9fbeb9..d7937547 --- a/system/core/Common.php +++ b/system/core/Common.php @@ -132,9 +132,9 @@ function &load_class($class, $directory = 'libraries', $prefix = 'CI_') $name = FALSE; - // Look for the class first in the native system/libraries folder - // thenin the local application/libraries folder - foreach (array(BASEPATH, APPPATH) as $path) + // Look for the class first in the local application/libraries folder + // then in the native system/libraries folder + foreach (array(APPPATH, BASEPATH) as $path) { if (file_exists($path.$directory.'/'.$class.'.php')) { @@ -536,5 +536,29 @@ function remove_invisible_characters($str, $url_encoded = TRUE) } } +// ------------------------------------------------------------------------ + +/** +* Returns HTML escaped variable +* +* @access public +* @param mixed +* @return mixed +*/ +if ( ! function_exists('html_escape')) +{ + function html_escape($var) + { + if (is_array($var)) + { + return array_map('html_escape', $var); + } + else + { + return htmlspecialchars($var, ENT_QUOTES, config_item('charset')); + } + } +} + /* End of file Common.php */ /* Location: ./system/core/Common.php */ \ No newline at end of file diff --git a/system/core/Config.php b/system/core/Config.php index 0e6f10e0..714c4667 100755 --- a/system/core/Config.php +++ b/system/core/Config.php @@ -28,8 +28,23 @@ */ class CI_Config { + /** + * List of all loaded config values + * + * @var array + */ var $config = array(); + /** + * List of all loaded config files + * + * @var array + */ var $is_loaded = array(); + /** + * List of paths to search when trying to load a config file + * + * @var array + */ var $_config_paths = array(APPPATH); /** @@ -251,13 +266,13 @@ function site_url(http://github.com/$uri%20=%20'') return $this->slash_item('base_url').$this->item('index_page').'?'.$this->_uri_string($uri); } } - + // ------------------------------------------------------------- - + /** * Base URL * Returns base_url [. uri_string] - * + * * @access public * @param string $uri * @return string @@ -266,12 +281,12 @@ function base_url(http://github.com/$uri%20=%20'') { return $this->slash_item('base_url').ltrim($this->_uri_string($uri),'/'); } - + // ------------------------------------------------------------- - + /** * Build URI string for use in Config::site_url() and Config::base_url() - * + * * @access protected * @param $uri * @return string @@ -305,7 +320,7 @@ protected function _uri_string($uri) } // -------------------------------------------------------------------- - + /** * System URL * diff --git a/system/core/Controller.php b/system/core/Controller.php old mode 100755 new mode 100644 index ec86b792..fddb81e1 --- a/system/core/Controller.php +++ b/system/core/Controller.php @@ -48,7 +48,7 @@ public function __construct() $this->load =& load_class('Loader', 'core'); - $this->load->set_base_classes()->ci_autoloader(); + $this->load->initialize(); log_message('debug', "Controller Class Initialized"); } diff --git a/system/core/Exceptions.php b/system/core/Exceptions.php index bff86a92..869739a5 100755 --- a/system/core/Exceptions.php +++ b/system/core/Exceptions.php @@ -30,8 +30,21 @@ class CI_Exceptions { var $message; var $filename; var $line; + + /** + * Nesting level of the output buffering mechanism + * + * @var int + * @access public + */ var $ob_level; + /** + * List if available error levels + * + * @var array + * @access public + */ var $levels = array( E_ERROR => 'Error', E_WARNING => 'Warning', @@ -84,7 +97,8 @@ function log_exception($severity, $message, $filepath, $line) * 404 Page Not Found Handler * * @access private - * @param string + * @param string the page + * @param bool log error yes/no * @return string */ function show_404($page = '', $log_error = TRUE) @@ -115,6 +129,7 @@ function show_404($page = '', $log_error = TRUE) * @param string the heading * @param string the message * @param string the template name + * @param int the status code * @return string */ function show_error($heading, $message, $template = 'error_general', $status_code = 500) diff --git a/system/core/Hooks.php b/system/core/Hooks.php index fd6380f0..33f1c034 100755 --- a/system/core/Hooks.php +++ b/system/core/Hooks.php @@ -28,8 +28,23 @@ */ class CI_Hooks { + /** + * Determines wether hooks are enabled + * + * @var bool + */ var $enabled = FALSE; + /** + * List of all hooks set in config/hooks.php + * + * @var array + */ var $hooks = array(); + /** + * Determines wether hook is in progress, used to prevent infinte loops + * + * @var bool + */ var $in_progress = FALSE; /** diff --git a/system/core/Input.php b/system/core/Input.php index cfbef942..9bfb5f1f 100755 --- a/system/core/Input.php +++ b/system/core/Input.php @@ -28,15 +28,51 @@ */ class CI_Input { + /** + * IP address of the current user + * + * @var string + */ var $ip_address = FALSE; + /** + * user agent (web browser) being used by the current user + * + * @var string + */ var $user_agent = FALSE; + /** + * If FALSE, then $_GET will be set to an empty array + * + * @var bool + */ var $_allow_get_array = TRUE; + /** + * If TRUE, then newlines are standardized + * + * @var bool + */ var $_standardize_newlines = TRUE; - var $_enable_xss = FALSE; // Set automatically based on config setting - var $_enable_csrf = FALSE; // Set automatically based on config setting - + /** + * Determines whether the XSS filter is always active when GET, POST or COOKIE data is encountered + * Set automatically based on config setting + * + * @var bool + */ + var $_enable_xss = FALSE; + /** + * Enables a CSRF cookie token to be set. + * Set automatically based on config setting + * + * @var bool + */ + var $_enable_csrf = FALSE; + /** + * List of all HTTP request headers + * + * @var array + */ protected $headers = array(); - + /** * Constructor @@ -147,7 +183,7 @@ function post($index = NULL, $xss_clean = FALSE) } return $post; } - + return $this->_fetch_from_array($_POST, $index, $xss_clean); } @@ -402,9 +438,9 @@ function user_agent() function _sanitize_globals() { // It would be "wrong" to unset any of these GLOBALS. - $protected = array('_SERVER', '_GET', '_POST', '_FILES', '_REQUEST', + $protected = array('_SERVER', '_GET', '_POST', '_FILES', '_REQUEST', '_SESSION', '_ENV', 'GLOBALS', 'HTTP_RAW_POST_DATA', - 'system_folder', 'application_folder', 'BM', 'EXT', + 'system_folder', 'application_folder', 'BM', 'EXT', 'CFG', 'URI', 'RTR', 'OUT', 'IN'); // Unset globals for securiy. @@ -512,8 +548,12 @@ function _clean_input_data($str) return $new_array; } - // We strip slashes if magic quotes is on to keep things consistent - if (function_exists('get_magic_quotes_gpc') AND get_magic_quotes_gpc()) + /* We strip slashes if magic quotes is on to keep things consistent + + NOTE: In PHP 5.4 get_magic_quotes_gpc() will always return 0 and + it will probably not exist in future versions at all. + */ + if ( ! is_php('5.4') && get_magic_quotes_gpc()) { $str = stripslashes($str); } @@ -523,7 +563,7 @@ function _clean_input_data($str) { $str = $this->uni->clean_string($str); } - + // Remove control characters $str = remove_invisible_characters($str); @@ -579,9 +619,11 @@ function _clean_input_keys($str) /** * Request Headers * - * In Apache, you can simply call apache_request_headers(), however for + * In Apache, you can simply call apache_request_headers(), however for * people running other webservers the function is undefined. * + * @param bool XSS cleaning + * * @return array */ public function request_headers($xss_clean = FALSE) @@ -609,10 +651,10 @@ public function request_headers($xss_clean = FALSE) { $key = str_replace('_', ' ', strtolower($key)); $key = str_replace(' ', '-', ucwords($key)); - + $this->headers[$key] = $val; } - + return $this->headers; } @@ -633,7 +675,7 @@ public function get_request_header($index, $xss_clean = FALSE) { $this->request_headers(); } - + if ( ! isset($this->headers[$index])) { return FALSE; @@ -644,7 +686,7 @@ public function get_request_header($index, $xss_clean = FALSE) return $this->security->xss_clean($this->headers[$index]); } - return $this->headers[$index]; + return $this->headers[$index]; } // -------------------------------------------------------------------- @@ -676,7 +718,6 @@ public function is_cli_request() } } -// END Input class /* End of file Input.php */ -/* Location: ./system/core/Input.php */ +/* Location: ./system/core/Input.php */ \ No newline at end of file diff --git a/system/core/Lang.php b/system/core/Lang.php index 170e6c72..5ac67183 100755 --- a/system/core/Lang.php +++ b/system/core/Lang.php @@ -26,7 +26,17 @@ */ class CI_Lang { + /** + * List of translations + * + * @var array + */ var $language = array(); + /** + * List of loaded language files + * + * @var array + */ var $is_loaded = array(); /** @@ -47,6 +57,9 @@ function __construct() * @access public * @param mixed the name of the language file to be loaded. Can be an array * @param string the language (english, etc.) + * @param bool return loaded array of translations + * @param bool add suffix to $langfile + * @param string alternative path to look for language file * @return mixed */ function load($langfile = '', $idiom = '', $return = FALSE, $add_suffix = TRUE, $alt_path = '') diff --git a/system/core/Loader.php b/system/core/Loader.php old mode 100755 new mode 100644 index 7c8b298a..6b7ee0c2 --- a/system/core/Loader.php +++ b/system/core/Loader.php @@ -29,18 +29,91 @@ class CI_Loader { // All these are set automatically. Don't mess with them. + /** + * Nesting level of the output buffering mechanism + * + * @var int + * @access protected + */ protected $_ci_ob_level; + /** + * List of paths to load views from + * + * @var array + * @access protected + */ protected $_ci_view_paths = array(); + /** + * List of paths to load libraries from + * + * @var array + * @access protected + */ protected $_ci_library_paths = array(); + /** + * List of paths to load models from + * + * @var array + * @access protected + */ protected $_ci_model_paths = array(); + /** + * List of paths to load helpers from + * + * @var array + * @access protected + */ protected $_ci_helper_paths = array(); + /** + * List of loaded base classes + * Set by the controller class + * + * @var array + * @access protected + */ protected $_base_classes = array(); // Set by the controller class + /** + * List of cached variables + * + * @var array + * @access protected + */ protected $_ci_cached_vars = array(); + /** + * List of loaded classes + * + * @var array + * @access protected + */ protected $_ci_classes = array(); + /** + * List of loaded files + * + * @var array + * @access protected + */ protected $_ci_loaded_files = array(); + /** + * List of loaded models + * + * @var array + * @access protected + */ protected $_ci_models = array(); + /** + * List of loaded helpers + * + * @var array + * @access protected + */ protected $_ci_helpers = array(); - protected $_ci_varmap = array('unit_test' => 'unit', + /** + * List of class name mappings + * + * @var array + * @access protected + */ + protected $_ci_varmap = array('unit_test' => 'unit', 'user_agent' => 'agent'); /** @@ -55,24 +128,29 @@ public function __construct() $this->_ci_helper_paths = array(APPPATH, BASEPATH); $this->_ci_model_paths = array(APPPATH); $this->_ci_view_paths = array(APPPATH.'views/' => TRUE); - + log_message('debug', "Loader Class Initialized"); } // -------------------------------------------------------------------- - + /** - * Set _base_classes variable + * Initialize the Loader * * This method is called once in CI_Controller. * - * @param array + * @param array * @return object */ - public function set_base_classes() + public function initialize() { + $this->_ci_classes = array(); + $this->_ci_loaded_files = array(); + $this->_ci_models = array(); $this->_base_classes =& is_loaded(); - + + $this->_ci_autoloader(); + return $this; } @@ -96,7 +174,7 @@ public function is_loaded($class) { return $this->_ci_classes[$class]; } - + return FALSE; } @@ -366,6 +444,7 @@ public function file($path, $return = FALSE) * the controller class and its "view" files. * * @param array + * @param string * @return void */ public function vars($vars = array(), $val = '') @@ -507,6 +586,8 @@ public function language($file = array(), $lang = '') * Loads a config file * * @param string + * @param bool + * @param bool * @return void */ public function config($file = '', $use_sections = FALSE, $fail_gracefully = FALSE) @@ -535,6 +616,11 @@ public function driver($library = '', $params = NULL, $object_name = NULL) require BASEPATH.'libraries/Driver.php'; } + if ($library == '') + { + return FALSE; + } + // We can save the loader some time since Drivers will *always* be in a subfolder, // and typically identically named to the library if ( ! strpos($library, '/')) @@ -553,13 +639,13 @@ public function driver($library = '', $params = NULL, $object_name = NULL) * Prepends a parent path to the library, model, helper, and config path arrays * * @param string - * @param boolean + * @param boolean * @return void */ public function add_package_path($path, $view_cascade=TRUE) { $path = rtrim($path, '/').'/'; - + array_unshift($this->_ci_library_paths, $path); array_unshift($this->_ci_model_paths, $path); array_unshift($this->_ci_helper_paths, $path); @@ -595,6 +681,7 @@ public function get_package_paths($include_base = FALSE) * If no path is provided, the most recently added path is removed. * * @param type + * @param bool * @return type */ public function remove_package_path($path = '', $remove_config_path = TRUE) @@ -619,7 +706,7 @@ public function remove_package_path($path = '', $remove_config_path = TRUE) unset($this->{$var}[$key]); } } - + if (isset($this->_ci_view_paths[$path.'views/'])) { unset($this->_ci_view_paths[$path.'views/']); @@ -658,7 +745,7 @@ protected function _ci_load($_ci_data) { $$_ci_val = ( ! isset($_ci_data[$_ci_val])) ? FALSE : $_ci_data[$_ci_val]; } - + $file_exists = FALSE; // Set the path to the requested file @@ -680,11 +767,11 @@ protected function _ci_load($_ci_data) $file_exists = TRUE; break; } - + if ( ! $cascade) { break; - } + } } } @@ -913,6 +1000,7 @@ protected function _ci_load_class($class, $params = NULL, $object_name = NULL) * * @param string * @param string + * @param bool * @param string an optional object name * @return null */ @@ -935,22 +1023,22 @@ protected function _ci_init_class($class, $prefix = '', $config = FALSE, $object // first, global next if (defined('ENVIRONMENT') AND file_exists($path .'config/'.ENVIRONMENT.'/'.strtolower($class).'.php')) { - include_once($path .'config/'.ENVIRONMENT.'/'.strtolower($class).'.php'); + include($path .'config/'.ENVIRONMENT.'/'.strtolower($class).'.php'); break; } elseif (defined('ENVIRONMENT') AND file_exists($path .'config/'.ENVIRONMENT.'/'.ucfirst(strtolower($class)).'.php')) { - include_once($path .'config/'.ENVIRONMENT.'/'.ucfirst(strtolower($class)).'.php'); + include($path .'config/'.ENVIRONMENT.'/'.ucfirst(strtolower($class)).'.php'); break; } elseif (file_exists($path .'config/'.strtolower($class).'.php')) { - include_once($path .'config/'.strtolower($class).'.php'); + include($path .'config/'.strtolower($class).'.php'); break; } elseif (file_exists($path .'config/'.ucfirst(strtolower($class)).'.php')) { - include_once($path .'config/'.ucfirst(strtolower($class)).'.php'); + include($path .'config/'.ucfirst(strtolower($class)).'.php'); break; } } @@ -1020,23 +1108,19 @@ protected function _ci_init_class($class, $prefix = '', $config = FALSE, $object * The config/autoload.php file contains an array that permits sub-systems, * libraries, and helpers to be loaded automatically. * - * This function is public, as it's used in the CI_Controller class. - * However, there is no reason you should ever needs to use it. - * * @param array * @return void */ - public function ci_autoloader() + private function _ci_autoloader() { if (defined('ENVIRONMENT') AND file_exists(APPPATH.'config/'.ENVIRONMENT.'/autoload.php')) { - include_once(APPPATH.'config/'.ENVIRONMENT.'/autoload.php'); + include(APPPATH.'config/'.ENVIRONMENT.'/autoload.php'); } else { - include_once(APPPATH.'config/autoload.php'); + include(APPPATH.'config/autoload.php'); } - if ( ! isset($autoload)) { @@ -1122,6 +1206,7 @@ protected function _ci_object_to_array($object) /** * Get a reference to a specific library or model * + * @param string * @return bool */ protected function &_ci_get_component($component) @@ -1138,6 +1223,7 @@ protected function &_ci_get_component($component) * This function preps the name of various items to make loading them more reliable. * * @param mixed + * @param string * @return array */ protected function _ci_prep_filename($filename, $extension) diff --git a/system/core/Model.php b/system/core/Model.php index 8566a0b6..e15ffbeb 100755 --- a/system/core/Model.php +++ b/system/core/Model.php @@ -42,6 +42,7 @@ function __construct() * Allows models to access CI's loaded classes using the same * syntax as controllers. * + * @param string * @access private */ function __get($key) diff --git a/system/core/Output.php b/system/core/Output.php index 05ace919..ccecafd2 100755 --- a/system/core/Output.php +++ b/system/core/Output.php @@ -28,15 +28,67 @@ */ class CI_Output { + /** + * Current output string + * + * @var string + * @access protected + */ protected $final_output; + /** + * Cache expiration time + * + * @var int + * @access protected + */ protected $cache_expiration = 0; + /** + * List of server headers + * + * @var array + * @access protected + */ protected $headers = array(); - protected $mime_types = array(); + /** + * List of mime types + * + * @var array + * @access protected + */ + protected $mime_types = array(); + /** + * Determines wether profiler is enabled + * + * @var book + * @access protected + */ protected $enable_profiler = FALSE; + /** + * Determines if output compression is enabled + * + * @var bool + * @access protected + */ protected $_zlib_oc = FALSE; + /** + * List of profiler sections + * + * @var array + * @access protected + */ protected $_profiler_sections = array(); - protected $parse_exec_vars = TRUE; // whether or not to parse variables like {elapsed_time} and {memory_usage} + /** + * Whether or not to parse variables like {elapsed_time} and {memory_usage} + * + * @var bool + * @access protected + */ + protected $parse_exec_vars = TRUE; + /** + * Constructor + * + */ function __construct() { $this->_zlib_oc = @ini_get('zlib.output_compression'); @@ -127,6 +179,7 @@ function append_output($output) * * @access public * @param string + * @param bool * @return void */ function set_header($header, $replace = TRUE) @@ -265,6 +318,7 @@ function cache($time) * benchmark timer so the page rendering speed and memory usage can be shown. * * @access public + * @param string * @return mixed */ function _display($output = '') @@ -401,6 +455,7 @@ function _display($output = '') * Write a Cache File * * @access public + * @param string * @return void */ function _write_cache($output) @@ -452,6 +507,8 @@ function _write_cache($output) * Update/serve a cached file * * @access public + * @param object config class + * @param object uri class * @return void */ function _display_cache(&$CFG, &$URI) diff --git a/system/core/Router.php b/system/core/Router.php index 5e92a04b..6da66747 100755 --- a/system/core/Router.php +++ b/system/core/Router.php @@ -28,12 +28,54 @@ */ class CI_Router { + /** + * Config class + * + * @var object + * @access public + */ var $config; + /** + * List of routes + * + * @var array + * @access public + */ var $routes = array(); + /** + * List of error routes + * + * @var array + * @access public + */ var $error_routes = array(); + /** + * Current class name + * + * @var string + * @access public + */ var $class = ''; + /** + * Current method name + * + * @var string + * @access public + */ var $method = 'index'; + /** + * Sub-directory that contains the requested controller class + * + * @var string + * @access public + */ var $directory = ''; + /** + * Default controller (and method if specific) + * + * @var string + * @access public + */ var $default_controller; /** @@ -95,7 +137,7 @@ function _set_routing() { include(APPPATH.'config/routes.php'); } - + $this->routes = ( ! isset($route) OR ! is_array($route)) ? array() : $route; unset($route); @@ -244,7 +286,20 @@ function _validate_request($segments) // Does the requested controller exist in the sub-folder? if ( ! file_exists(APPPATH.'controllers/'.$this->fetch_directory().$segments[0].'.php')) { - show_404($this->fetch_directory().$segments[0]); + if ( ! empty($this->routes['404_override'])) + { + $x = explode('/', $this->routes['404_override']); + + $this->set_directory(''); + $this->set_class($x[0]); + $this->set_method(isset($x[1]) ? $x[1] : 'index'); + + return $x; + } + else + { + show_404($this->fetch_directory().$segments[0]); + } } } else diff --git a/system/core/Security.php b/system/core/Security.php index 3617cadc..a3e22743 100755 --- a/system/core/Security.php +++ b/system/core/Security.php @@ -25,14 +25,49 @@ * @link http://codeigniter.com/user_guide/libraries/security.html */ class CI_Security { - + + /** + * Random Hash for protecting URLs + * + * @var string + * @access protected + */ protected $_xss_hash = ''; + /** + * Random Hash for Cross Site Request Forgery Protection Cookie + * + * @var string + * @access protected + */ protected $_csrf_hash = ''; - protected $_csrf_expire = 7200; // Two hours (in seconds) + /** + * Expiration time for Cross Site Request Forgery Protection Cookie + * Defaults to two hours (in seconds) + * + * @var int + * @access protected + */ + protected $_csrf_expire = 7200; + /** + * Token name for Cross Site Request Forgery Protection Cookie + * + * @var string + * @access protected + */ protected $_csrf_token_name = 'ci_csrf_token'; + /** + * Cookie name for Cross Site Request Forgery Protection Cookie + * + * @var string + * @access protected + */ protected $_csrf_cookie_name = 'ci_csrf_token'; - - /* never allowed, string replacement */ + /** + * List of never allowed strings + * + * @var array + * @access protected + */ protected $_never_allowed_str = array( 'document.cookie' => '[removed]', 'document.write' => '[removed]', @@ -42,17 +77,24 @@ class CI_Security { '-moz-binding' => '[removed]', '' => '-->', - ' '<![CDATA[' + ' '<![CDATA[', + '' => '<comment>' ); /* never allowed, regex replacement */ + /** + * List of never allowed regex replacement + * + * @var array + * @access protected + */ protected $_never_allowed_regex = array( "javascript\s*:" => '[removed]', "expression\s*(\(|&\#40;)" => '[removed]', // CSS and IE "vbscript\s*:" => '[removed]', // IE, surprise! "Redirect\s+302" => '[removed]' ); - + /** * Constructor */ @@ -95,7 +137,7 @@ public function csrf_verify() } // Do the tokens exist in both the _POST and _COOKIE arrays? - if ( ! isset($_POST[$this->_csrf_token_name]) OR + if ( ! isset($_POST[$this->_csrf_token_name]) OR ! isset($_COOKIE[$this->_csrf_cookie_name])) { $this->csrf_show_error(); @@ -107,7 +149,7 @@ public function csrf_verify() $this->csrf_show_error(); } - // We kill this since we're done and we don't want to + // We kill this since we're done and we don't want to // polute the _POST array unset($_POST[$this->_csrf_token_name]); @@ -117,7 +159,7 @@ public function csrf_verify() $this->csrf_set_cookie(); log_message('debug', "CSRF token verified "); - + return $this; } @@ -146,7 +188,7 @@ public function csrf_set_cookie() setcookie($this->_csrf_cookie_name, $this->_csrf_hash, $expire, config_item('cookie_path'), config_item('cookie_domain'), $secure_cookie); log_message('debug', "CRSF cookie Set"); - + return $this; } @@ -165,9 +207,9 @@ public function csrf_show_error() // -------------------------------------------------------------------- /** - * Get CSRF Hash + * Get CSRF Hash * - * Getter Method + * Getter Method * * @return string self::_csrf_hash */ @@ -215,6 +257,7 @@ public function get_csrf_token_name() * http://ha.ckers.org/xss.html * * @param mixed string or array + * @param bool * @return string */ public function xss_clean($str, $is_image = FALSE) @@ -263,7 +306,7 @@ public function xss_clean($str, $is_image = FALSE) */ $str = preg_replace_callback("/[a-z]+=([\'\"]).*?\\1/si", array($this, '_convert_attribute'), $str); - + $str = preg_replace_callback("/<\w+.*?(?=>|<|$)/si", array($this, '_decode_entity'), $str); /* @@ -276,7 +319,7 @@ public function xss_clean($str, $is_image = FALSE) * * This prevents strings like this: ja vascript * NOTE: we deal with spaces between characters later. - * NOTE: preg_replace was found to be amazingly slow here on + * NOTE: preg_replace was found to be amazingly slow here on * large blocks of data, so we use str_replace. */ @@ -304,8 +347,8 @@ public function xss_clean($str, $is_image = FALSE) */ if ($is_image === TRUE) { - // Images have a tendency to have the PHP short opening and - // closing tags every so often so we skip those and only + // Images have a tendency to have the PHP short opening and + // closing tags every so often so we skip those and only // do the long opening tags. $str = preg_replace('/<\?(php)/i', "<?\\1", $str); } @@ -321,10 +364,10 @@ public function xss_clean($str, $is_image = FALSE) * These words are compacted back to their correct state. */ $words = array( - 'javascript', 'expression', 'vbscript', 'script', + 'javascript', 'expression', 'vbscript', 'script', 'applet', 'alert', 'document', 'write', 'cookie', 'window' ); - + foreach ($words as $word) { $temp = ''; @@ -341,8 +384,8 @@ public function xss_clean($str, $is_image = FALSE) /* * Remove disallowed Javascript in links or img tags - * We used to do some version comparisons and use of stripos for PHP5, - * but it is dog slow compared to these simplified non-capturing + * We used to do some version comparisons and use of stripos for PHP5, + * but it is dog slow compared to these simplified non-capturing * preg_match(), especially if the pattern exists in the string */ do @@ -405,11 +448,11 @@ public function xss_clean($str, $is_image = FALSE) /* * Images are Handled in a Special Way - * - Essentially, we want to know that after all of the character - * conversion is done whether any unwanted, likely XSS, code was found. + * - Essentially, we want to know that after all of the character + * conversion is done whether any unwanted, likely XSS, code was found. * If not, we return TRUE, as the image is clean. - * However, if the string post-conversion does not matched the - * string post-removal of XSS, then it fails, as there was unwanted XSS + * However, if the string post-conversion does not matched the + * string post-removal of XSS, then it fails, as there was unwanted XSS * code found and removed/changed during processing. */ @@ -433,15 +476,7 @@ public function xss_hash() { if ($this->_xss_hash == '') { - if (phpversion() >= 4.2) - { - mt_srand(); - } - else - { - mt_srand(hexdec(substr(md5(microtime()), -8)) & 0x7fffffff); - } - + mt_srand(); $this->_xss_hash = md5(time() + mt_rand(0, 1999999999)); } @@ -455,14 +490,11 @@ public function xss_hash() * * This function is a replacement for html_entity_decode() * - * In some versions of PHP the native function does not work - * when UTF-8 is the specified character set, so this gives us - * a work-around. More info here: - * http://bugs.php.net/bug.php?id=25670 - * - * NOTE: html_entity_decode() has a bug in some PHP versions when UTF-8 is the - * character set, and the PHP developers said they were not back porting the - * fix to versions other than PHP 5.x. + * The reason we are not using html_entity_decode() by itself is because + * while it is not technically correct to leave out the semicolon + * at the end of an entity most browsers will still interpret the entity + * correctly. html_entity_decode() does not convert entities without + * semicolons, so we are left with our own little solution here. Bummer. * * @param string * @param string @@ -470,33 +502,14 @@ public function xss_hash() */ public function entity_decode($str, $charset='UTF-8') { - if (stristr($str, '&') === FALSE) return $str; - - // The reason we are not using html_entity_decode() by itself is because - // while it is not technically correct to leave out the semicolon - // at the end of an entity most browsers will still interpret the entity - // correctly. html_entity_decode() does not convert entities without - // semicolons, so we are left with our own little solution here. Bummer. - - if (function_exists('html_entity_decode') && - (strtolower($charset) != 'utf-8')) - { - $str = html_entity_decode($str, ENT_COMPAT, $charset); - $str = preg_replace('~&#x(0*[0-9a-f]{2,5})~ei', 'chr(hexdec("\\1"))', $str); - return preg_replace('~&#([0-9]{2,4})~e', 'chr(\\1)', $str); - } - - // Numeric Entities - $str = preg_replace('~&#x(0*[0-9a-f]{2,5});{0,1}~ei', 'chr(hexdec("\\1"))', $str); - $str = preg_replace('~&#([0-9]{2,4});{0,1}~e', 'chr(\\1)', $str); - - // Literal Entities - Slightly slow so we do another check if (stristr($str, '&') === FALSE) { - $str = strtr($str, array_flip(get_html_translation_table(HTML_ENTITIES))); + return $str; } - return $str; + $str = html_entity_decode($str, ENT_COMPAT, $charset); + $str = preg_replace('~&#x(0*[0-9a-f]{2,5})~ei', 'chr(hexdec("\\1"))', $str); + return preg_replace('~&#([0-9]{2,4})~e', 'chr(\\1)', $str); } // -------------------------------------------------------------------- @@ -505,6 +518,7 @@ public function entity_decode($str, $charset='UTF-8') * Filename Security * * @param string + * @param bool * @return string */ public function sanitize_filename($str, $relative_path = FALSE) @@ -542,7 +556,7 @@ public function sanitize_filename($str, $relative_path = FALSE) "%3b", // ; "%3d" // = ); - + if ( ! $relative_path) { $bad[] = './'; @@ -570,7 +584,7 @@ protected function _compact_exploded_words($matches) } // -------------------------------------------------------------------- - + /* * Remove Evil HTML Attributes (like evenhandlers and style) * @@ -578,7 +592,7 @@ protected function _compact_exploded_words($matches) * - Everything up until a space * For example, everything between the pipes: * - * - Everything inside the quotes + * - Everything inside the quotes * For example, everything between the pipes: * * @@ -589,7 +603,7 @@ protected function _compact_exploded_words($matches) protected function _remove_evil_attributes($str, $is_image) { // All javascript event handlers (e.g. onload, onclick, onmouseover), style, and xmlns - $evil_attributes = array('on\w*', 'style', 'xmlns'); + $evil_attributes = array('on\w*', 'style', 'xmlns', 'formaction'); if ($is_image === TRUE) { @@ -601,16 +615,36 @@ protected function _remove_evil_attributes($str, $is_image) } do { - $str = preg_replace( - "#<(/?[^><]+?)([^A-Za-z\-])(".implode('|', $evil_attributes).")(\s*=\s*)([\"][^>]*?[\"]|[\'][^>]*?[\']|[^>]*?)([\s><])([><]*)#i", - "<$1$6", - $str, -1, $count - ); + $count = 0; + $attribs = array(); + + // find occurrences of illegal attribute strings without quotes + preg_match_all("/(".implode('|', $evil_attributes).")\s*=\s*([^\s]*)/is", $str, $matches, PREG_SET_ORDER); + + foreach ($matches as $attr) + { + $attribs[] = preg_quote($attr[0], '/'); + } + + // find occurrences of illegal attribute strings with quotes (042 and 047 are octal quotes) + preg_match_all("/(".implode('|', $evil_attributes).")\s*=\s*(\042|\047)([^\\2]*?)(\\2)/is", $str, $matches, PREG_SET_ORDER); + + foreach ($matches as $attr) + { + $attribs[] = preg_quote($attr[0], '/'); + } + + // replace illegal attribute strings that are inside an html tag + if (count($attribs) > 0) + { + $str = preg_replace("/<(\/?[^><]+?)([^A-Za-z\-])(".implode('|', $attribs).")([\s><])([><]*)/i", '<$1$2$4$5', $str, -1, $count); + } + } while ($count); return $str; } - + // -------------------------------------------------------------------- /** @@ -627,7 +661,7 @@ protected function _sanitize_naughty_html($matches) $str = '<'.$matches[1].$matches[2].$matches[3]; // encode captured opening or closing brace to prevent recursive vectors - $str .= str_replace(array('>', '<'), array('>', '<'), + $str .= str_replace(array('>', '<'), array('>', '<'), $matches[4]); return $str; @@ -649,7 +683,7 @@ protected function _sanitize_naughty_html($matches) protected function _js_link_removal($match) { $attributes = $this->_filter_attributes(str_replace(array('<', '>'), '', $match[1])); - + return str_replace($match[1], preg_replace("#href=.*?(alert\(|alert&\#40;|javascript\:|livescript\:|mocha\:|charset\=|window\.|document\.|\.cookie|_filter_attributes(str_replace(array('<', '>'), '', $match[1])); - + return str_replace($match[1], preg_replace("#src=.*?(alert\(|alert&\#40;|javascript\:|livescript\:|mocha\:|charset\=|window\.|document\.|\.cookie|xss_hash()."\\1=\\2", $str); /* @@ -769,7 +803,7 @@ protected function _validate_entities($str) * Un-Protect GET variables in URLs */ $str = str_replace($this->xss_hash(), '&', $str); - + return $str; } @@ -794,7 +828,7 @@ protected function _do_never_allowed($str) { $str = preg_replace("#".$key."#i", $val, $str); } - + return $str; } @@ -809,16 +843,16 @@ protected function _csrf_set_hash() { if ($this->_csrf_hash == '') { - // If the cookie exists we will use it's value. + // If the cookie exists we will use it's value. // We don't necessarily want to regenerate it with - // each page load since a page could contain embedded + // each page load since a page could contain embedded // sub-pages causing this feature to fail - if (isset($_COOKIE[$this->_csrf_cookie_name]) && + if (isset($_COOKIE[$this->_csrf_cookie_name]) && $_COOKIE[$this->_csrf_cookie_name] != '') { return $this->_csrf_hash = $_COOKIE[$this->_csrf_cookie_name]; } - + return $this->_csrf_hash = md5(uniqid(rand(), TRUE)); } diff --git a/system/core/URI.php b/system/core/URI.php index 20f0f006..a3ae20cc 100755 --- a/system/core/URI.php +++ b/system/core/URI.php @@ -28,9 +28,34 @@ */ class CI_URI { + /** + * List of cached uri segments + * + * @var array + * @access public + */ var $keyval = array(); + /** + * Current uri string + * + * @var string + * @access public + */ var $uri_string; + /** + * List of uri segments + * + * @var array + * @access public + */ var $segments = array(); + /** + * Re-indexed list of uri segments + * Starts at 1 instead of 0 + * + * @var array + * @access public + */ var $rsegments = array(); /** @@ -127,6 +152,7 @@ function _fetch_uri_string() * Set the URI String * * @access public + * @param string * @return string */ function _set_uri_string($str) @@ -366,6 +392,11 @@ function uri_to_assoc($n = 3, $default = array()) /** * Identical to above only it uses the re-routed segment array * + * @access public + * @param integer the starting segment number + * @param array an array of default values + * @return array + * */ function ruri_to_assoc($n = 3, $default = array()) { diff --git a/system/core/Utf8.php b/system/core/Utf8.php old mode 100755 new mode 100644 diff --git a/system/core/index.html b/system/core/index.html old mode 100755 new mode 100644 diff --git a/system/database/DB.php b/system/database/DB.php index 33207d88..8314d3b9 100755 --- a/system/database/DB.php +++ b/system/database/DB.php @@ -21,6 +21,8 @@ * @category Database * @author ExpressionEngine Dev Team * @link http://codeigniter.com/user_guide/database/ + * @param string + * @param bool Determines if active record should be used or not */ function &DB($params = '', $active_record_override = NULL) { @@ -35,7 +37,7 @@ function &DB($params = '', $active_record_override = NULL) show_error('The configuration file database.php does not exist.'); } } - + include($file_path); if ( ! isset($db) OR count($db) == 0) diff --git a/system/database/DB_active_rec.php b/system/database/DB_active_rec.php old mode 100755 new mode 100644 index 2af3553e..7bab729f --- a/system/database/DB_active_rec.php +++ b/system/database/DB_active_rec.php @@ -870,11 +870,11 @@ public function order_by($orderby, $direction = '') */ public function limit($value, $offset = '') { - $this->ar_limit = $value; + $this->ar_limit = (int) $value; if ($offset != '') { - $this->ar_offset = $offset; + $this->ar_offset = (int) $offset; } return $this; @@ -1322,7 +1322,7 @@ public function update_batch($table = '', $set = NULL, $index = NULL) { if ($this->db_debug) { - return $this->display_error('db_myst_use_index'); + return $this->display_error('db_must_use_index'); } return FALSE; diff --git a/system/database/DB_cache.php b/system/database/DB_cache.php old mode 100755 new mode 100644 index 3bf065ca..ad1c28d7 --- a/system/database/DB_cache.php +++ b/system/database/DB_cache.php @@ -33,7 +33,7 @@ class CI_DB_Cache { * Grabs the CI super object instance so we can access it. * */ - function CI_DB_Cache(&$db) + function __construct(&$db) { // Assign the main CI object to $this->CI // and load the file helper since we use it a lot diff --git a/system/database/DB_driver.php b/system/database/DB_driver.php old mode 100755 new mode 100644 index 10e8ed0c..3680b85c --- a/system/database/DB_driver.php +++ b/system/database/DB_driver.php @@ -78,7 +78,7 @@ class CI_DB_driver { * * @param array */ - function CI_DB_driver($params) + function __construct($params) { if (is_array($params)) { @@ -218,7 +218,7 @@ function version() // Some DBs have functions that return the version, and don't run special // SQL queries per se. In these instances, just return the result. - $driver_version_exceptions = array('oci8', 'sqlite'); + $driver_version_exceptions = array('oci8', 'sqlite', 'cubrid'); if (in_array($this->dbdriver, $driver_version_exceptions)) { @@ -1387,4 +1387,4 @@ function _protect_identifiers($item, $prefix_single = FALSE, $protect_identifier /* End of file DB_driver.php */ -/* Location: ./system/database/DB_driver.php */ \ No newline at end of file +/* Location: ./system/database/DB_driver.php */ diff --git a/system/database/DB_forge.php b/system/database/DB_forge.php old mode 100755 new mode 100644 index a71fca78..6bc40411 --- a/system/database/DB_forge.php +++ b/system/database/DB_forge.php @@ -35,7 +35,7 @@ class CI_DB_forge { * Grabs the CI super object instance so we can access it. * */ - function CI_DB_forge() + function __construct() { // Assign the main database object to $this->db $CI =& get_instance(); @@ -234,7 +234,7 @@ function rename_table($table_name, $new_table_name) show_error('A table name is required for that operation.'); } - $sql = $this->_rename_table($table_name, $new_table_name); + $sql = $this->_rename_table($this->db->dbprefix.$table_name, $this->db->dbprefix.$new_table_name); return $this->db->query($sql); } diff --git a/system/database/DB_result.php b/system/database/DB_result.php old mode 100755 new mode 100644 index 0c4e7810..48d66c8e --- a/system/database/DB_result.php +++ b/system/database/DB_result.php @@ -45,7 +45,7 @@ class CI_DB_result { * @param string can be "object" or "array" * @return mixed either a result object or array */ - function result($type = 'object') + public function result($type = 'object') { if ($type == 'array') return $this->result_array(); else if ($type == 'object') return $this->result_object(); @@ -60,7 +60,7 @@ function result($type = 'object') * @param class_name A string that represents the type of object you want back * @return array of objects */ - function custom_result_object($class_name) + public function custom_result_object($class_name) { if (array_key_exists($class_name, $this->custom_result_object)) { @@ -79,12 +79,12 @@ function custom_result_object($class_name) while ($row = $this->_fetch_object()) { $object = new $class_name(); - + foreach ($row as $key => $value) { $object->$key = $value; } - + $result_object[] = $object; } @@ -100,7 +100,7 @@ function custom_result_object($class_name) * @access public * @return object */ - function result_object() + public function result_object() { if (count($this->result_object) > 0) { @@ -132,7 +132,7 @@ function result_object() * @access public * @return array */ - function result_array() + public function result_array() { if (count($this->result_array) > 0) { @@ -166,7 +166,7 @@ function result_array() * @param string can be "object" or "array" * @return mixed either a result object or array */ - function row($n = 0, $type = 'object') + public function row($n = 0, $type = 'object') { if ( ! is_numeric($n)) { @@ -198,7 +198,7 @@ function row($n = 0, $type = 'object') * @access public * @return object */ - function set_row($key, $value = NULL) + public function set_row($key, $value = NULL) { // We cache the row data for subsequent uses if ( ! is_array($this->row_data)) @@ -230,7 +230,7 @@ function set_row($key, $value = NULL) * @access public * @return object */ - function custom_row_object($n, $type) + public function custom_row_object($n, $type) { $result = $this->custom_result_object($type); @@ -253,7 +253,7 @@ function custom_row_object($n, $type) * @access public * @return object */ - function row_object($n = 0) + public function row_object($n = 0) { $result = $this->result_object(); @@ -278,7 +278,7 @@ function row_object($n = 0) * @access public * @return array */ - function row_array($n = 0) + public function row_array($n = 0) { $result = $this->result_array(); @@ -304,7 +304,7 @@ function row_array($n = 0) * @access public * @return object */ - function first_row($type = 'object') + public function first_row($type = 'object') { $result = $this->result($type); @@ -323,7 +323,7 @@ function first_row($type = 'object') * @access public * @return object */ - function last_row($type = 'object') + public function last_row($type = 'object') { $result = $this->result($type); @@ -342,7 +342,7 @@ function last_row($type = 'object') * @access public * @return object */ - function next_row($type = 'object') + public function next_row($type = 'object') { $result = $this->result($type); @@ -367,7 +367,7 @@ function next_row($type = 'object') * @access public * @return object */ - function previous_row($type = 'object') + public function previous_row($type = 'object') { $result = $this->result($type); @@ -394,14 +394,14 @@ function previous_row($type = 'object') * operational due to the unavailability of the database resource IDs with * cached results. */ - function num_rows() { return $this->num_rows; } - function num_fields() { return 0; } - function list_fields() { return array(); } - function field_data() { return array(); } - function free_result() { return TRUE; } - function _data_seek() { return TRUE; } - function _fetch_assoc() { return array(); } - function _fetch_object() { return array(); } + public function num_rows() { return $this->num_rows; } + public function num_fields() { return 0; } + public function list_fields() { return array(); } + public function field_data() { return array(); } + public function free_result() { return TRUE; } + protected function _data_seek() { return TRUE; } + protected function _fetch_assoc() { return array(); } + protected function _fetch_object() { return array(); } } // END DB_result class diff --git a/system/database/DB_utility.php b/system/database/DB_utility.php old mode 100755 new mode 100644 index a5f174f0..52196b7c --- a/system/database/DB_utility.php +++ b/system/database/DB_utility.php @@ -33,7 +33,7 @@ class CI_DB_utility extends CI_DB_forge { * Grabs the CI super object instance so we can access it. * */ - function CI_DB_utility() + function __construct() { // Assign the main database object to $this->db $CI =& get_instance(); diff --git a/system/database/drivers/cubrid/cubrid_driver.php b/system/database/drivers/cubrid/cubrid_driver.php new file mode 100644 index 00000000..d0114041 --- /dev/null +++ b/system/database/drivers/cubrid/cubrid_driver.php @@ -0,0 +1,792 @@ +port == '') + { + $this->port = self::DEFAULT_PORT; + } + + $conn = cubrid_connect($this->hostname, $this->port, $this->database, $this->username, $this->password); + + if ($conn) + { + // Check if a user wants to run queries in dry, i.e. run the + // queries but not commit them. + if (isset($this->auto_commit) && ! $this->auto_commit) + { + cubrid_set_autocommit($conn, CUBRID_AUTOCOMMIT_FALSE); + } + else + { + cubrid_set_autocommit($conn, CUBRID_AUTOCOMMIT_TRUE); + $this->auto_commit = TRUE; + } + } + + return $conn; + } + + // -------------------------------------------------------------------- + + /** + * Persistent database connection + * In CUBRID persistent DB connection is supported natively in CUBRID + * engine which can be configured in the CUBRID Broker configuration + * file by setting the CCI_PCONNECT parameter to ON. In that case, all + * connections established between the client application and the + * server will become persistent. This is calling the same + * @cubrid_connect function will establish persisten connection + * considering that the CCI_PCONNECT is ON. + * + * @access private called by the base class + * @return resource + */ + function db_pconnect() + { + return $this->db_connect(); + } + + // -------------------------------------------------------------------- + + /** + * Reconnect + * + * Keep / reestablish the db connection if no queries have been + * sent for a length of time exceeding the server's idle timeout + * + * @access public + * @return void + */ + function reconnect() + { + if (cubrid_ping($this->conn_id) === FALSE) + { + $this->conn_id = FALSE; + } + } + + // -------------------------------------------------------------------- + + /** + * Select the database + * + * @access private called by the base class + * @return resource + */ + function db_select() + { + // In CUBRID there is no need to select a database as the database + // is chosen at the connection time. + // So, to determine if the database is "selected", all we have to + // do is ping the server and return that value. + return cubrid_ping($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * Set client character set + * + * @access public + * @param string + * @param string + * @return resource + */ + function db_set_charset($charset, $collation) + { + // In CUBRID, there is no need to set charset or collation. + // This is why returning true will allow the application continue + // its normal process. + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Version number query string + * + * @access public + * @return string + */ + function _version() + { + // To obtain the CUBRID Server version, no need to run the SQL query. + // CUBRID PHP API provides a function to determin this value. + // This is why we also need to add 'cubrid' value to the list of + // $driver_version_exceptions array in DB_driver class in + // version() function. + return cubrid_get_server_info($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * Execute the query + * + * @access private called by the base class + * @param string an SQL query + * @return resource + */ + function _execute($sql) + { + $sql = $this->_prep_query($sql); + return @cubrid_query($sql, $this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * Prep the query + * + * If needed, each database adapter can prep the query string + * + * @access private called by execute() + * @param string an SQL query + * @return string + */ + function _prep_query($sql) + { + // No need to prepare + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Begin Transaction + * + * @access public + * @return bool + */ + function trans_begin($test_mode = FALSE) + { + if ( ! $this->trans_enabled) + { + return TRUE; + } + + // When transactions are nested we only begin/commit/rollback the outermost ones + if ($this->_trans_depth > 0) + { + return TRUE; + } + + // Reset the transaction failure flag. + // If the $test_mode flag is set to TRUE transactions will be rolled back + // even if the queries produce a successful result. + $this->_trans_failure = ($test_mode === TRUE) ? TRUE : FALSE; + + if (cubrid_get_autocommit($this->conn_id)) + { + cubrid_set_autocommit($this->conn_id, CUBRID_AUTOCOMMIT_FALSE); + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Commit Transaction + * + * @access public + * @return bool + */ + function trans_commit() + { + if ( ! $this->trans_enabled) + { + return TRUE; + } + + // When transactions are nested we only begin/commit/rollback the outermost ones + if ($this->_trans_depth > 0) + { + return TRUE; + } + + cubrid_commit($this->conn_id); + + if ($this->auto_commit && ! cubrid_get_autocommit($this->conn_id)) + { + cubrid_set_autocommit($this->conn_id, CUBRID_AUTOCOMMIT_TRUE); + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Rollback Transaction + * + * @access public + * @return bool + */ + function trans_rollback() + { + if ( ! $this->trans_enabled) + { + return TRUE; + } + + // When transactions are nested we only begin/commit/rollback the outermost ones + if ($this->_trans_depth > 0) + { + return TRUE; + } + + cubrid_rollback($this->conn_id); + + if ($this->auto_commit && ! cubrid_get_autocommit($this->conn_id)) + { + cubrid_set_autocommit($this->conn_id, CUBRID_AUTOCOMMIT_TRUE); + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Escape String + * + * @access public + * @param string + * @param bool whether or not the string will be used in a LIKE condition + * @return string + */ + function escape_str($str, $like = FALSE) + { + if (is_array($str)) + { + foreach ($str as $key => $val) + { + $str[$key] = $this->escape_str($val, $like); + } + + return $str; + } + + if (function_exists('cubrid_real_escape_string') AND is_resource($this->conn_id)) + { + $str = cubrid_real_escape_string($str, $this->conn_id); + } + else + { + $str = addslashes($str); + } + + // escape LIKE condition wildcards + if ($like === TRUE) + { + $str = str_replace(array('%', '_'), array('\\%', '\\_'), $str); + } + + return $str; + } + + // -------------------------------------------------------------------- + + /** + * Affected Rows + * + * @access public + * @return integer + */ + function affected_rows() + { + return @cubrid_affected_rows($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * Insert ID + * + * @access public + * @return integer + */ + function insert_id() + { + return @cubrid_insert_id($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * "Count All" query + * + * Generates a platform-specific query string that counts all records in + * the specified table + * + * @access public + * @param string + * @return string + */ + function count_all($table = '') + { + if ($table == '') + { + return 0; + } + + $query = $this->query($this->_count_string . $this->_protect_identifiers('numrows') . " FROM " . $this->_protect_identifiers($table, TRUE, NULL, FALSE)); + + if ($query->num_rows() == 0) + { + return 0; + } + + $row = $query->row(); + $this->_reset_select(); + return (int) $row->numrows; + } + + // -------------------------------------------------------------------- + + /** + * List table query + * + * Generates a platform-specific query string so that the table names can be fetched + * + * @access private + * @param boolean + * @return string + */ + function _list_tables($prefix_limit = FALSE) + { + $sql = "SHOW TABLES"; + + if ($prefix_limit !== FALSE AND $this->dbprefix != '') + { + $sql .= " LIKE '".$this->escape_like_str($this->dbprefix)."%'"; + } + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Show column query + * + * Generates a platform-specific query string so that the column names can be fetched + * + * @access public + * @param string the table name + * @return string + */ + function _list_columns($table = '') + { + return "SHOW COLUMNS FROM ".$this->_protect_identifiers($table, TRUE, NULL, FALSE); + } + + // -------------------------------------------------------------------- + + /** + * Field data query + * + * Generates a platform-specific query so that the column data can be retrieved + * + * @access public + * @param string the table name + * @return object + */ + function _field_data($table) + { + return "SELECT * FROM ".$table." LIMIT 1"; + } + + // -------------------------------------------------------------------- + + /** + * The error message string + * + * @access private + * @return string + */ + function _error_message() + { + return cubrid_error($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * The error message number + * + * @access private + * @return integer + */ + function _error_number() + { + return cubrid_errno($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * Escape the SQL Identifiers + * + * This function escapes column and table names + * + * @access private + * @param string + * @return string + */ + function _escape_identifiers($item) + { + if ($this->_escape_char == '') + { + return $item; + } + + foreach ($this->_reserved_identifiers as $id) + { + if (strpos($item, '.'.$id) !== FALSE) + { + $str = $this->_escape_char. str_replace('.', $this->_escape_char.'.', $item); + + // remove duplicates if the user already included the escape + return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str); + } + } + + if (strpos($item, '.') !== FALSE) + { + $str = $this->_escape_char.str_replace('.', $this->_escape_char.'.'.$this->_escape_char, $item).$this->_escape_char; + } + else + { + $str = $this->_escape_char.$item.$this->_escape_char; + } + + // remove duplicates if the user already included the escape + return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str); + } + + // -------------------------------------------------------------------- + + /** + * From Tables + * + * This function implicitly groups FROM tables so there is no confusion + * about operator precedence in harmony with SQL standards + * + * @access public + * @param type + * @return type + */ + function _from_tables($tables) + { + if ( ! is_array($tables)) + { + $tables = array($tables); + } + + return '('.implode(', ', $tables).')'; + } + + // -------------------------------------------------------------------- + + /** + * Insert statement + * + * Generates a platform-specific insert string from the supplied data + * + * @access public + * @param string the table name + * @param array the insert keys + * @param array the insert values + * @return string + */ + function _insert($table, $keys, $values) + { + return "INSERT INTO ".$table." (\"".implode('", "', $keys)."\") VALUES (".implode(', ', $values).")"; + } + + // -------------------------------------------------------------------- + + + /** + * Replace statement + * + * Generates a platform-specific replace string from the supplied data + * + * @access public + * @param string the table name + * @param array the insert keys + * @param array the insert values + * @return string + */ + function _replace($table, $keys, $values) + { + return "REPLACE INTO ".$table." (\"".implode('", "', $keys)."\") VALUES (".implode(', ', $values).")"; + } + + // -------------------------------------------------------------------- + + /** + * Insert_batch statement + * + * Generates a platform-specific insert string from the supplied data + * + * @access public + * @param string the table name + * @param array the insert keys + * @param array the insert values + * @return string + */ + function _insert_batch($table, $keys, $values) + { + return "INSERT INTO ".$table." (\"".implode('", "', $keys)."\") VALUES ".implode(', ', $values); + } + + // -------------------------------------------------------------------- + + + /** + * Update statement + * + * Generates a platform-specific update string from the supplied data + * + * @access public + * @param string the table name + * @param array the update data + * @param array the where clause + * @param array the orderby clause + * @param array the limit clause + * @return string + */ + function _update($table, $values, $where, $orderby = array(), $limit = FALSE) + { + foreach ($values as $key => $val) + { + $valstr[] = sprintf('"%s" = %s', $key, $val); + } + + $limit = ( ! $limit) ? '' : ' LIMIT '.$limit; + + $orderby = (count($orderby) >= 1)?' ORDER BY '.implode(", ", $orderby):''; + + $sql = "UPDATE ".$table." SET ".implode(', ', $valstr); + + $sql .= ($where != '' AND count($where) >=1) ? " WHERE ".implode(" ", $where) : ''; + + $sql .= $orderby.$limit; + + return $sql; + } + + // -------------------------------------------------------------------- + + + /** + * Update_Batch statement + * + * Generates a platform-specific batch update string from the supplied data + * + * @access public + * @param string the table name + * @param array the update data + * @param array the where clause + * @return string + */ + function _update_batch($table, $values, $index, $where = NULL) + { + $ids = array(); + $where = ($where != '' AND count($where) >=1) ? implode(" ", $where).' AND ' : ''; + + foreach ($values as $key => $val) + { + $ids[] = $val[$index]; + + foreach (array_keys($val) as $field) + { + if ($field != $index) + { + $final[$field][] = 'WHEN '.$index.' = '.$val[$index].' THEN '.$val[$field]; + } + } + } + + $sql = "UPDATE ".$table." SET "; + $cases = ''; + + foreach ($final as $k => $v) + { + $cases .= $k.' = CASE '."\n"; + foreach ($v as $row) + { + $cases .= $row."\n"; + } + + $cases .= 'ELSE '.$k.' END, '; + } + + $sql .= substr($cases, 0, -2); + + $sql .= ' WHERE '.$where.$index.' IN ('.implode(',', $ids).')'; + + return $sql; + } + + // -------------------------------------------------------------------- + + + /** + * Truncate statement + * + * Generates a platform-specific truncate string from the supplied data + * If the database does not support the truncate() command + * This function maps to "DELETE FROM table" + * + * @access public + * @param string the table name + * @return string + */ + function _truncate($table) + { + return "TRUNCATE ".$table; + } + + // -------------------------------------------------------------------- + + /** + * Delete statement + * + * Generates a platform-specific delete string from the supplied data + * + * @access public + * @param string the table name + * @param array the where clause + * @param string the limit clause + * @return string + */ + function _delete($table, $where = array(), $like = array(), $limit = FALSE) + { + $conditions = ''; + + if (count($where) > 0 OR count($like) > 0) + { + $conditions = "\nWHERE "; + $conditions .= implode("\n", $this->ar_where); + + if (count($where) > 0 && count($like) > 0) + { + $conditions .= " AND "; + } + $conditions .= implode("\n", $like); + } + + $limit = ( ! $limit) ? '' : ' LIMIT '.$limit; + + return "DELETE FROM ".$table.$conditions.$limit; + } + + // -------------------------------------------------------------------- + + /** + * Limit string + * + * Generates a platform-specific LIMIT clause + * + * @access public + * @param string the sql query string + * @param integer the number of rows to limit the query to + * @param integer the offset value + * @return string + */ + function _limit($sql, $limit, $offset) + { + if ($offset == 0) + { + $offset = ''; + } + else + { + $offset .= ", "; + } + + return $sql."LIMIT ".$offset.$limit; + } + + // -------------------------------------------------------------------- + + /** + * Close DB Connection + * + * @access public + * @param resource + * @return void + */ + function _close($conn_id) + { + @cubrid_close($conn_id); + } + +} + + +/* End of file cubrid_driver.php */ +/* Location: ./system/database/drivers/cubrid/cubrid_driver.php */ \ No newline at end of file diff --git a/system/database/drivers/cubrid/cubrid_forge.php b/system/database/drivers/cubrid/cubrid_forge.php new file mode 100644 index 00000000..bab03f74 --- /dev/null +++ b/system/database/drivers/cubrid/cubrid_forge.php @@ -0,0 +1,288 @@ +$attributes) + { + // Numeric field names aren't allowed in databases, so if the key is + // numeric, we know it was assigned by PHP and the developer manually + // entered the field information, so we'll simply add it to the list + if (is_numeric($field)) + { + $sql .= "\n\t$attributes"; + } + else + { + $attributes = array_change_key_case($attributes, CASE_UPPER); + + $sql .= "\n\t\"" . $this->db->_protect_identifiers($field) . "\""; + + if (array_key_exists('NAME', $attributes)) + { + $sql .= ' '.$this->db->_protect_identifiers($attributes['NAME']).' '; + } + + if (array_key_exists('TYPE', $attributes)) + { + $sql .= ' '.$attributes['TYPE']; + + if (array_key_exists('CONSTRAINT', $attributes)) + { + switch ($attributes['TYPE']) + { + case 'decimal': + case 'float': + case 'numeric': + $sql .= '('.implode(',', $attributes['CONSTRAINT']).')'; + break; + case 'enum': // As of version 8.4.0 CUBRID does not support + // enum data type. + break; + case 'set': + $sql .= '("'.implode('","', $attributes['CONSTRAINT']).'")'; + break; + default: + $sql .= '('.$attributes['CONSTRAINT'].')'; + } + } + } + + if (array_key_exists('UNSIGNED', $attributes) && $attributes['UNSIGNED'] === TRUE) + { + //$sql .= ' UNSIGNED'; + // As of version 8.4.0 CUBRID does not support UNSIGNED INTEGER data type. + // Will be supported in the next release as a part of MySQL Compatibility. + } + + if (array_key_exists('DEFAULT', $attributes)) + { + $sql .= ' DEFAULT \''.$attributes['DEFAULT'].'\''; + } + + if (array_key_exists('NULL', $attributes) && $attributes['NULL'] === TRUE) + { + $sql .= ' NULL'; + } + else + { + $sql .= ' NOT NULL'; + } + + if (array_key_exists('AUTO_INCREMENT', $attributes) && $attributes['AUTO_INCREMENT'] === TRUE) + { + $sql .= ' AUTO_INCREMENT'; + } + + if (array_key_exists('UNIQUE', $attributes) && $attributes['UNIQUE'] === TRUE) + { + $sql .= ' UNIQUE'; + } + } + + // don't add a comma on the end of the last field + if (++$current_field_count < count($fields)) + { + $sql .= ','; + } + } + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Create Table + * + * @access private + * @param string the table name + * @param mixed the fields + * @param mixed primary key(s) + * @param mixed key(s) + * @param boolean should 'IF NOT EXISTS' be added to the SQL + * @return bool + */ + function _create_table($table, $fields, $primary_keys, $keys, $if_not_exists) + { + $sql = 'CREATE TABLE '; + + if ($if_not_exists === TRUE) + { + //$sql .= 'IF NOT EXISTS '; + // As of version 8.4.0 CUBRID does not support this SQL syntax. + } + + $sql .= $this->db->_escape_identifiers($table)." ("; + + $sql .= $this->_process_fields($fields); + + // If there is a PK defined + if (count($primary_keys) > 0) + { + $key_name = "pk_" . $table . "_" . + $this->db->_protect_identifiers(implode('_', $primary_keys)); + + $primary_keys = $this->db->_protect_identifiers($primary_keys); + $sql .= ",\n\tCONSTRAINT " . $key_name . " PRIMARY KEY(" . implode(', ', $primary_keys) . ")"; + } + + if (is_array($keys) && count($keys) > 0) + { + foreach ($keys as $key) + { + if (is_array($key)) + { + $key_name = $this->db->_protect_identifiers(implode('_', $key)); + $key = $this->db->_protect_identifiers($key); + } + else + { + $key_name = $this->db->_protect_identifiers($key); + $key = array($key_name); + } + + $sql .= ",\n\tKEY \"{$key_name}\" (" . implode(', ', $key) . ")"; + } + } + + $sql .= "\n);"; + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Drop Table + * + * @access private + * @return string + */ + function _drop_table($table) + { + return "DROP TABLE IF EXISTS ".$this->db->_escape_identifiers($table); + } + + // -------------------------------------------------------------------- + + /** + * Alter table query + * + * Generates a platform-specific query so that a table can be altered + * Called by add_column(), drop_column(), and column_alter(), + * + * @access private + * @param string the ALTER type (ADD, DROP, CHANGE) + * @param string the column name + * @param array fields + * @param string the field after which we should add the new field + * @return object + */ + function _alter_table($alter_type, $table, $fields, $after_field = '') + { + $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table)." $alter_type "; + + // DROP has everything it needs now. + if ($alter_type == 'DROP') + { + return $sql.$this->db->_protect_identifiers($fields); + } + + $sql .= $this->_process_fields($fields); + + if ($after_field != '') + { + $sql .= ' AFTER ' . $this->db->_protect_identifiers($after_field); + } + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Rename a table + * + * Generates a platform-specific query so that a table can be renamed + * + * @access private + * @param string the old table name + * @param string the new table name + * @return string + */ + function _rename_table($table_name, $new_table_name) + { + $sql = 'RENAME TABLE '.$this->db->_protect_identifiers($table_name)." AS ".$this->db->_protect_identifiers($new_table_name); + return $sql; + } + +} + +/* End of file cubrid_forge.php */ +/* Location: ./system/database/drivers/cubrid/cubrid_forge.php */ \ No newline at end of file diff --git a/system/database/drivers/cubrid/cubrid_result.php b/system/database/drivers/cubrid/cubrid_result.php new file mode 100644 index 00000000..6f0c2b5f --- /dev/null +++ b/system/database/drivers/cubrid/cubrid_result.php @@ -0,0 +1,202 @@ +result_id); + } + + // -------------------------------------------------------------------- + + /** + * Number of fields in the result set + * + * @access public + * @return integer + */ + function num_fields() + { + return @cubrid_num_fields($this->result_id); + } + + // -------------------------------------------------------------------- + + /** + * Fetch Field Names + * + * Generates an array of column names + * + * @access public + * @return array + */ + function list_fields() + { + return cubrid_column_names($this->result_id); + } + + // -------------------------------------------------------------------- + + /** + * Field data + * + * Generates an array of objects containing field meta-data + * + * @access public + * @return array + */ + function field_data() + { + $retval = array(); + + $tablePrimaryKeys = array(); + + while ($field = cubrid_fetch_field($this->result_id)) + { + $F = new stdClass(); + $F->name = $field->name; + $F->type = $field->type; + $F->default = $field->def; + $F->max_length = $field->max_length; + + // At this moment primary_key property is not returned when + // cubrid_fetch_field is called. The following code will + // provide a patch for it. primary_key property will be added + // in the next release. + + // TODO: later version of CUBRID will provide primary_key + // property. + // When PK is defined in CUBRID, an index is automatically + // created in the db_index system table in the form of + // pk_tblname_fieldname. So the following will count how many + // columns are there which satisfy this format. + // The query will search for exact single columns, thus + // compound PK is not supported. + $res = cubrid_query($this->conn_id, + "SELECT COUNT(*) FROM db_index WHERE class_name = '" . $field->table . + "' AND is_primary_key = 'YES' AND index_name = 'pk_" . + $field->table . "_" . $field->name . "'" + ); + + if ($res) + { + $row = cubrid_fetch_array($res, CUBRID_NUM); + $F->primary_key = ($row[0] > 0 ? 1 : null); + } + else + { + $F->primary_key = null; + } + + if (is_resource($res)) + { + cubrid_close_request($res); + $this->result_id = FALSE; + } + + $retval[] = $F; + } + + return $retval; + } + + // -------------------------------------------------------------------- + + /** + * Free the result + * + * @return null + */ + function free_result() + { + if(is_resource($this->result_id) || + get_resource_type($this->result_id) == "Unknown" && + preg_match('/Resource id #/', strval($this->result_id))) + { + cubrid_close_request($this->result_id); + $this->result_id = FALSE; + } + } + + // -------------------------------------------------------------------- + + /** + * Data Seek + * + * Moves the internal pointer to the desired offset. We call + * this internally before fetching results to make sure the + * result set starts at zero + * + * @access private + * @return array + */ + function _data_seek($n = 0) + { + return cubrid_data_seek($this->result_id, $n); + } + + // -------------------------------------------------------------------- + + /** + * Result - associative array + * + * Returns the result set as an array + * + * @access private + * @return array + */ + function _fetch_assoc() + { + return cubrid_fetch_assoc($this->result_id); + } + + // -------------------------------------------------------------------- + + /** + * Result - object + * + * Returns the result set as an object + * + * @access private + * @return object + */ + function _fetch_object() + { + return cubrid_fetch_object($this->result_id); + } + +} + + +/* End of file cubrid_result.php */ +/* Location: ./system/database/drivers/cubrid/cubrid_result.php */ \ No newline at end of file diff --git a/system/database/drivers/cubrid/cubrid_utility.php b/system/database/drivers/cubrid/cubrid_utility.php new file mode 100644 index 00000000..cd16d1e1 --- /dev/null +++ b/system/database/drivers/cubrid/cubrid_utility.php @@ -0,0 +1,108 @@ +conn_id) + { + return "SELECT '" . $this->database . "'"; + } + else + { + return FALSE; + } + } + + // -------------------------------------------------------------------- + + /** + * Optimize table query + * + * Generates a platform-specific query so that a table can be optimized + * + * @access private + * @param string the table name + * @return object + * @link http://www.cubrid.org/manual/840/en/Optimize%20Database + */ + function _optimize_table($table) + { + // No SQL based support in CUBRID as of version 8.4.0. Database or + // table optimization can be performed using CUBRID Manager + // database administration tool. See the link above for more info. + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Repair table query + * + * Generates a platform-specific query so that a table can be repaired + * + * @access private + * @param string the table name + * @return object + * @link http://www.cubrid.org/manual/840/en/Checking%20Database%20Consistency + */ + function _repair_table($table) + { + // Not supported in CUBRID as of version 8.4.0. Database or + // table consistency can be checked using CUBRID Manager + // database administration tool. See the link above for more info. + return FALSE; + } + + // -------------------------------------------------------------------- + /** + * CUBRID Export + * + * @access private + * @param array Preferences + * @return mixed + */ + function _backup($params = array()) + { + // No SQL based support in CUBRID as of version 8.4.0. Database or + // table backup can be performed using CUBRID Manager + // database administration tool. + return $this->db->display_error('db_unsuported_feature'); + } +} + +/* End of file cubrid_utility.php */ +/* Location: ./system/database/drivers/cubrid/cubrid_utility.php */ \ No newline at end of file diff --git a/system/database/drivers/cubrid/index.html b/system/database/drivers/cubrid/index.html new file mode 100644 index 00000000..c942a79c --- /dev/null +++ b/system/database/drivers/cubrid/index.html @@ -0,0 +1,10 @@ + + + 403 Forbidden + + + +

Directory access is forbidden.

+ + + \ No newline at end of file diff --git a/system/database/drivers/index.html b/system/database/drivers/index.html old mode 100755 new mode 100644 diff --git a/system/database/drivers/mssql/index.html b/system/database/drivers/mssql/index.html old mode 100755 new mode 100644 diff --git a/system/database/drivers/mssql/mssql_driver.php b/system/database/drivers/mssql/mssql_driver.php old mode 100755 new mode 100644 index 65397ed8..b39bd936 --- a/system/database/drivers/mssql/mssql_driver.php +++ b/system/database/drivers/mssql/mssql_driver.php @@ -367,6 +367,7 @@ function count_all($table = '') } $row = $query->row(); + $this->_reset_select(); return (int) $row->numrows; } diff --git a/system/database/drivers/mssql/mssql_forge.php b/system/database/drivers/mssql/mssql_forge.php old mode 100755 new mode 100644 diff --git a/system/database/drivers/mssql/mssql_result.php b/system/database/drivers/mssql/mssql_result.php old mode 100755 new mode 100644 diff --git a/system/database/drivers/mssql/mssql_utility.php b/system/database/drivers/mssql/mssql_utility.php old mode 100755 new mode 100644 diff --git a/system/database/drivers/mysql/index.html b/system/database/drivers/mysql/index.html old mode 100755 new mode 100644 diff --git a/system/database/drivers/mysql/mysql_driver.php b/system/database/drivers/mysql/mysql_driver.php old mode 100755 new mode 100644 index b7d547cc..f87cfea4 --- a/system/database/drivers/mysql/mysql_driver.php +++ b/system/database/drivers/mysql/mysql_driver.php @@ -54,6 +54,9 @@ class CI_DB_mysql_driver extends CI_DB { var $_count_string = 'SELECT COUNT(*) AS '; var $_random_keyword = ' RAND()'; // database specific random keyword + // whether SET NAMES must be used to set the character set + var $use_set_names; + /** * Non-persistent database connection * @@ -132,15 +135,13 @@ function db_select() */ function db_set_charset($charset, $collation) { - static $use_set_names; - - if ( ! isset($use_set_names)) + if ( ! isset($this->use_set_names)) { // mysql_set_charset() requires PHP >= 5.2.3 and MySQL >= 5.0.7, use SET NAMES as fallback - $use_set_names = (version_compare(PHP_VERSION, '5.2.3', '>=') && version_compare(mysql_get_server_info(), '5.0.7', '>=')) ? FALSE : TRUE; + $this->use_set_names = (version_compare(PHP_VERSION, '5.2.3', '>=') && version_compare(mysql_get_server_info(), '5.0.7', '>=')) ? FALSE : TRUE; } - if ($use_set_names) + if ($this->use_set_names === TRUE) { return @mysql_query("SET NAMES '".$this->escape_str($charset)."' COLLATE '".$this->escape_str($collation)."'", $this->conn_id); } @@ -384,6 +385,7 @@ function count_all($table = '') } $row = $query->row(); + $this->_reset_select(); return (int) $row->numrows; } @@ -439,7 +441,7 @@ function _list_columns($table = '') */ function _field_data($table) { - return "SELECT * FROM ".$table." LIMIT 1"; + return "DESCRIBE ".$table; } // -------------------------------------------------------------------- diff --git a/system/database/drivers/mysql/mysql_forge.php b/system/database/drivers/mysql/mysql_forge.php old mode 100755 new mode 100644 diff --git a/system/database/drivers/mysql/mysql_result.php b/system/database/drivers/mysql/mysql_result.php old mode 100755 new mode 100644 index 50738960..e1a6e93c --- a/system/database/drivers/mysql/mysql_result.php +++ b/system/database/drivers/mysql/mysql_result.php @@ -84,14 +84,19 @@ function list_fields() function field_data() { $retval = array(); - while ($field = mysql_fetch_field($this->result_id)) + while ($field = mysql_fetch_object($this->result_id)) { + preg_match('/([a-zA-Z]+)(\(\d+\))?/', $field->Type, $matches); + + $type = (array_key_exists(1, $matches)) ? $matches[1] : NULL; + $length = (array_key_exists(2, $matches)) ? preg_replace('/[^\d]/', '', $matches[2]) : NULL; + $F = new stdClass(); - $F->name = $field->name; - $F->type = $field->type; - $F->default = $field->def; - $F->max_length = $field->max_length; - $F->primary_key = $field->primary_key; + $F->name = $field->Field; + $F->type = $type; + $F->default = $field->Default; + $F->max_length = $length; + $F->primary_key = ( $field->Key == 'PRI' ? 1 : 0 ); $retval[] = $F; } diff --git a/system/database/drivers/mysql/mysql_utility.php b/system/database/drivers/mysql/mysql_utility.php old mode 100755 new mode 100644 index e9747c54..48c4d631 --- a/system/database/drivers/mysql/mysql_utility.php +++ b/system/database/drivers/mysql/mysql_utility.php @@ -96,7 +96,7 @@ function _backup($params = array()) } // Get the table schema - $query = $this->db->query("SHOW CREATE TABLE `".$this->db->database.'`.'.$table); + $query = $this->db->query("SHOW CREATE TABLE `".$this->db->database.'`.`'.$table.'`'); // No result means the table name was invalid if ($query === FALSE) diff --git a/system/database/drivers/mysqli/index.html b/system/database/drivers/mysqli/index.html old mode 100755 new mode 100644 diff --git a/system/database/drivers/mysqli/mysqli_driver.php b/system/database/drivers/mysqli/mysqli_driver.php old mode 100755 new mode 100644 index b8586c21..d3200f32 --- a/system/database/drivers/mysqli/mysqli_driver.php +++ b/system/database/drivers/mysqli/mysqli_driver.php @@ -54,6 +54,9 @@ class CI_DB_mysqli_driver extends CI_DB { */ var $delete_hack = TRUE; + // whether SET NAMES must be used to set the character set + var $use_set_names; + // -------------------------------------------------------------------- /** @@ -132,15 +135,13 @@ function db_select() */ function _db_set_charset($charset, $collation) { - static $use_set_names; - - if ( ! isset($use_set_names)) + if ( ! isset($this->use_set_names)) { // mysqli_set_charset() requires MySQL >= 5.0.7, use SET NAMES as fallback - $use_set_names = (version_compare(mysqli_get_server_info($this->conn_id), '5.0.7', '>=')) ? FALSE : TRUE; + $this->use_set_names = (version_compare(mysqli_get_server_info($this->conn_id), '5.0.7', '>=')) ? FALSE : TRUE; } - if ($use_set_names) + if ($this->use_set_names === TRUE) { return @mysqli_query($this->conn_id, "SET NAMES '".$this->escape_str($charset)."' COLLATE '".$this->escape_str($collation)."'"); } @@ -385,6 +386,7 @@ function count_all($table = '') } $row = $query->row(); + $this->_reset_select(); return (int) $row->numrows; } @@ -440,7 +442,7 @@ function _list_columns($table = '') */ function _field_data($table) { - return "SELECT * FROM ".$table." LIMIT 1"; + return "DESCRIBE ".$table; } // -------------------------------------------------------------------- @@ -568,6 +570,25 @@ function _insert_batch($table, $keys, $values) { return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES ".implode(', ', $values); } + + // -------------------------------------------------------------------- + + + /** + * Replace statement + * + * Generates a platform-specific replace string from the supplied data + * + * @access public + * @param string the table name + * @param array the insert keys + * @param array the insert values + * @return string + */ + function _replace($table, $keys, $values) + { + return "REPLACE INTO ".$table." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")"; + } // -------------------------------------------------------------------- diff --git a/system/database/drivers/mysqli/mysqli_forge.php b/system/database/drivers/mysqli/mysqli_forge.php old mode 100755 new mode 100644 diff --git a/system/database/drivers/mysqli/mysqli_result.php b/system/database/drivers/mysqli/mysqli_result.php old mode 100755 new mode 100644 index c4d8f5d5..124d4e59 --- a/system/database/drivers/mysqli/mysqli_result.php +++ b/system/database/drivers/mysqli/mysqli_result.php @@ -84,21 +84,26 @@ function list_fields() function field_data() { $retval = array(); - while ($field = mysqli_fetch_field($this->result_id)) + while ($field = mysqli_fetch_object($this->result_id)) { + preg_match('/([a-zA-Z]+)(\(\d+\))?/', $field->Type, $matches); + + $type = (array_key_exists(1, $matches)) ? $matches[1] : NULL; + $length = (array_key_exists(2, $matches)) ? preg_replace('/[^\d]/', '', $matches[2]) : NULL; + $F = new stdClass(); - $F->name = $field->name; - $F->type = $field->type; - $F->default = $field->def; - $F->max_length = $field->max_length; - $F->primary_key = ($field->flags & MYSQLI_PRI_KEY_FLAG) ? 1 : 0; + $F->name = $field->Field; + $F->type = $type; + $F->default = $field->Default; + $F->max_length = $length; + $F->primary_key = ( $field->Key == 'PRI' ? 1 : 0 ); $retval[] = $F; } return $retval; } - + // -------------------------------------------------------------------- /** diff --git a/system/database/drivers/mysqli/mysqli_utility.php b/system/database/drivers/mysqli/mysqli_utility.php old mode 100755 new mode 100644 diff --git a/system/database/drivers/oci8/index.html b/system/database/drivers/oci8/index.html old mode 100755 new mode 100644 diff --git a/system/database/drivers/oci8/oci8_driver.php b/system/database/drivers/oci8/oci8_driver.php old mode 100755 new mode 100644 index 14df104f..930177e6 --- a/system/database/drivers/oci8/oci8_driver.php +++ b/system/database/drivers/oci8/oci8_driver.php @@ -35,8 +35,6 @@ * This is a modification of the DB_driver class to * permit access to oracle databases * - * NOTE: this uses the PHP 4 oci methods - * * @author Kelly McArdle * */ @@ -77,9 +75,9 @@ class CI_DB_oci8_driver extends CI_DB { * @access private called by the base class * @return resource */ - function db_connect() + public function db_connect() { - return @ocilogon($this->username, $this->password, $this->hostname); + return @oci_connect($this->username, $this->password, $this->hostname, $this->char_set); } // -------------------------------------------------------------------- @@ -90,9 +88,9 @@ function db_connect() * @access private called by the base class * @return resource */ - function db_pconnect() + public function db_pconnect() { - return @ociplogon($this->username, $this->password, $this->hostname); + return @oci_pconnect($this->username, $this->password, $this->hostname, $this->char_set); } // -------------------------------------------------------------------- @@ -106,9 +104,10 @@ function db_pconnect() * @access public * @return void */ - function reconnect() + public function reconnect() { // not implemented in oracle + return; } // -------------------------------------------------------------------- @@ -119,8 +118,9 @@ function reconnect() * @access private called by the base class * @return resource */ - function db_select() + public function db_select() { + // Not in Oracle - schemas are actually usernames return TRUE; } @@ -134,7 +134,7 @@ function db_select() * @param string * @return resource */ - function db_set_charset($charset, $collation) + public function db_set_charset($charset, $collation) { // @todo - add support if needed return TRUE; @@ -145,12 +145,12 @@ function db_set_charset($charset, $collation) /** * Version number query string * - * @access public + * @access protected * @return string */ - function _version() + protected function _version() { - return ociserverversion($this->conn_id); + return oci_server_version($this->conn_id); } // -------------------------------------------------------------------- @@ -158,18 +158,18 @@ function _version() /** * Execute the query * - * @access private called by the base class + * @access protected called by the base class * @param string an SQL query * @return resource */ - function _execute($sql) + protected function _execute($sql) { // oracle must parse the query before it is run. All of the actions with // the query are based on the statement id returned by ociparse $this->stmt_id = FALSE; $this->_set_stmt_id($sql); - ocisetprefetch($this->stmt_id, 1000); - return @ociexecute($this->stmt_id, $this->_commit); + oci_set_prefetch($this->stmt_id, 1000); + return @oci_execute($this->stmt_id, $this->_commit); } /** @@ -179,11 +179,11 @@ function _execute($sql) * @param string an SQL query * @return none */ - function _set_stmt_id($sql) + private function _set_stmt_id($sql) { if ( ! is_resource($this->stmt_id)) { - $this->stmt_id = ociparse($this->conn_id, $this->_prep_query($sql)); + $this->stmt_id = oci_parse($this->conn_id, $this->_prep_query($sql)); } } @@ -198,7 +198,7 @@ function _set_stmt_id($sql) * @param string an SQL query * @return string */ - function _prep_query($sql) + private function _prep_query($sql) { return $sql; } @@ -211,9 +211,9 @@ function _prep_query($sql) * @access public * @return cursor id */ - function get_cursor() + public function get_cursor() { - $this->curs_id = ocinewcursor($this->conn_id); + $this->curs_id = oci_new_cursor($this->conn_id); return $this->curs_id; } @@ -237,7 +237,7 @@ function get_cursor() * type yes the type of the parameter * length yes the max size of the parameter */ - function stored_procedure($package, $procedure, $params) + public function stored_procedure($package, $procedure, $params) { if ($package == '' OR $procedure == '' OR ! is_array($params)) { @@ -257,7 +257,7 @@ function stored_procedure($package, $procedure, $params) { $sql .= $param['name'] . ","; - if (array_key_exists('type', $param) && ($param['type'] == OCI_B_CURSOR)) + if (array_key_exists('type', $param) && ($param['type'] === OCI_B_CURSOR)) { $have_cursor = TRUE; } @@ -278,7 +278,7 @@ function stored_procedure($package, $procedure, $params) * @access private * @return none */ - function _bind_params($params) + private function _bind_params($params) { if ( ! is_array($params) OR ! is_resource($this->stmt_id)) { @@ -295,7 +295,7 @@ function _bind_params($params) } } - ocibindbyname($this->stmt_id, $param['name'], $param['value'], $param['length'], $param['type']); + oci_bind_by_name($this->stmt_id, $param['name'], $param['value'], $param['length'], $param['type']); } } @@ -307,7 +307,7 @@ function _bind_params($params) * @access public * @return bool */ - function trans_begin($test_mode = FALSE) + public function trans_begin($test_mode = FALSE) { if ( ! $this->trans_enabled) { @@ -337,7 +337,7 @@ function trans_begin($test_mode = FALSE) * @access public * @return bool */ - function trans_commit() + public function trans_commit() { if ( ! $this->trans_enabled) { @@ -350,7 +350,7 @@ function trans_commit() return TRUE; } - $ret = OCIcommit($this->conn_id); + $ret = oci_commit($this->conn_id); $this->_commit = OCI_COMMIT_ON_SUCCESS; return $ret; } @@ -363,7 +363,7 @@ function trans_commit() * @access public * @return bool */ - function trans_rollback() + public function trans_rollback() { if ( ! $this->trans_enabled) { @@ -376,7 +376,7 @@ function trans_rollback() return TRUE; } - $ret = OCIrollback($this->conn_id); + $ret = oci_rollback($this->conn_id); $this->_commit = OCI_COMMIT_ON_SUCCESS; return $ret; } @@ -391,7 +391,7 @@ function trans_rollback() * @param bool whether or not the string will be used in a LIKE condition * @return string */ - function escape_str($str, $like = FALSE) + public function escape_str($str, $like = FALSE) { if (is_array($str)) { @@ -424,9 +424,9 @@ function escape_str($str, $like = FALSE) * @access public * @return integer */ - function affected_rows() + public function affected_rows() { - return @ocirowcount($this->stmt_id); + return @oci_num_rows($this->stmt_id); } // -------------------------------------------------------------------- @@ -437,7 +437,7 @@ function affected_rows() * @access public * @return integer */ - function insert_id() + public function insert_id() { // not supported in oracle return $this->display_error('db_unsupported_function'); @@ -455,7 +455,7 @@ function insert_id() * @param string * @return string */ - function count_all($table = '') + public function count_all($table = '') { if ($table == '') { @@ -470,6 +470,7 @@ function count_all($table = '') } $row = $query->row(); + $this->_reset_select(); return (int) $row->numrows; } @@ -480,11 +481,11 @@ function count_all($table = '') * * Generates a platform-specific query string so that the table names can be fetched * - * @access private + * @access protected * @param boolean - * @return string + * @return string */ - function _list_tables($prefix_limit = FALSE) + protected function _list_tables($prefix_limit = FALSE) { $sql = "SELECT TABLE_NAME FROM ALL_TABLES"; @@ -503,11 +504,11 @@ function _list_tables($prefix_limit = FALSE) * * Generates a platform-specific query string so that the column names can be fetched * - * @access public + * @access protected * @param string the table name * @return string */ - function _list_columns($table = '') + protected function _list_columns($table = '') { return "SELECT COLUMN_NAME FROM all_tab_columns WHERE table_name = '$table'"; } @@ -523,7 +524,7 @@ function _list_columns($table = '') * @param string the table name * @return object */ - function _field_data($table) + protected function _field_data($table) { return "SELECT * FROM ".$table." where rownum = 1"; } @@ -533,12 +534,13 @@ function _field_data($table) /** * The error message string * - * @access private + * @access protected * @return string */ - function _error_message() + protected function _error_message() { - $error = ocierror($this->conn_id); + // If the error was during connection, no conn_id should be passed + $error = is_resource($this->conn_id) ? oci_error($this->conn_id) : oci_error(); return $error['message']; } @@ -547,12 +549,13 @@ function _error_message() /** * The error message number * - * @access private + * @access protected * @return integer */ - function _error_number() + protected function _error_number() { - $error = ocierror($this->conn_id); + // Same as _error_message() + $error = is_resource($this->conn_id) ? oci_error($this->conn_id) : oci_error(); return $error['code']; } @@ -563,11 +566,11 @@ function _error_number() * * This function escapes column and table names * - * @access private + * @access protected * @param string * @return string */ - function _escape_identifiers($item) + protected function _escape_identifiers($item) { if ($this->_escape_char == '') { @@ -606,11 +609,11 @@ function _escape_identifiers($item) * This function implicitly groups FROM tables so there is no confusion * about operator precedence in harmony with SQL standards * - * @access public + * @access protected * @param type * @return type */ - function _from_tables($tables) + protected function _from_tables($tables) { if ( ! is_array($tables)) { @@ -633,9 +636,37 @@ function _from_tables($tables) * @param array the insert values * @return string */ - function _insert($table, $keys, $values) + protected function _insert($table, $keys, $values) { - return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")"; + return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")"; + } + + // -------------------------------------------------------------------- + + /** + * Insert_batch statement + * + * Generates a platform-specific insert string from the supplied data + * + * @access protected + * @param string the table name + * @param array the insert keys + * @param array the insert values + * @return string + */ + protected function _insert_batch($table, $keys, $values) + { + $keys = implode(', ', $keys); + $sql = "INSERT ALL\n"; + + for ($i = 0, $c = count($values); $i < $c; $i++) + { + $sql .= ' INTO ' . $table . ' (' . $keys . ') VALUES ' . $values[$i] . "\n"; + } + + $sql .= 'SELECT * FROM dual'; + + return $sql; } // -------------------------------------------------------------------- @@ -645,7 +676,7 @@ function _insert($table, $keys, $values) * * Generates a platform-specific update string from the supplied data * - * @access public + * @access protected * @param string the table name * @param array the update data * @param array the where clause @@ -653,7 +684,7 @@ function _insert($table, $keys, $values) * @param array the limit clause * @return string */ - function _update($table, $values, $where, $orderby = array(), $limit = FALSE) + protected function _update($table, $values, $where, $orderby = array(), $limit = FALSE) { foreach ($values as $key => $val) { @@ -682,11 +713,11 @@ function _update($table, $values, $where, $orderby = array(), $limit = FALSE) * If the database does not support the truncate() command * This function maps to "DELETE FROM table" * - * @access public + * @access protected * @param string the table name * @return string */ - function _truncate($table) + protected function _truncate($table) { return "TRUNCATE TABLE ".$table; } @@ -698,13 +729,13 @@ function _truncate($table) * * Generates a platform-specific delete string from the supplied data * - * @access public + * @access protected * @param string the table name * @param array the where clause * @param string the limit clause * @return string */ - function _delete($table, $where = array(), $like = array(), $limit = FALSE) + protected function _delete($table, $where = array(), $like = array(), $limit = FALSE) { $conditions = ''; @@ -732,13 +763,13 @@ function _delete($table, $where = array(), $like = array(), $limit = FALSE) * * Generates a platform-specific LIMIT clause * - * @access public + * @access protected * @param string the sql query string * @param integer the number of rows to limit the query to * @param integer the offset value * @return string */ - function _limit($sql, $limit, $offset) + protected function _limit($sql, $limit, $offset) { $limit = $offset + $limit; $newsql = "SELECT * FROM (select inner_query.*, rownum rnum FROM ($sql) inner_query WHERE rownum < $limit)"; @@ -759,13 +790,13 @@ function _limit($sql, $limit, $offset) /** * Close DB Connection * - * @access public + * @access protected * @param resource * @return void */ - function _close($conn_id) + protected function _close($conn_id) { - @ocilogoff($conn_id); + @oci_close($conn_id); } @@ -774,4 +805,4 @@ function _close($conn_id) /* End of file oci8_driver.php */ -/* Location: ./system/database/drivers/oci8/oci8_driver.php */ \ No newline at end of file +/* Location: ./system/database/drivers/oci8/oci8_driver.php */ diff --git a/system/database/drivers/oci8/oci8_forge.php b/system/database/drivers/oci8/oci8_forge.php old mode 100755 new mode 100644 diff --git a/system/database/drivers/oci8/oci8_result.php b/system/database/drivers/oci8/oci8_result.php old mode 100755 new mode 100644 index 88531b43..ae133d7b --- a/system/database/drivers/oci8/oci8_result.php +++ b/system/database/drivers/oci8/oci8_result.php @@ -40,14 +40,17 @@ class CI_DB_oci8_result extends CI_DB_result { * @access public * @return integer */ - function num_rows() + public function num_rows() { - $rowcount = count($this->result_array()); - @ociexecute($this->stmt_id); - - if ($this->curs_id) + if ($this->num_rows === 0 && count($this->result_array()) > 0) { - @ociexecute($this->curs_id); + $this->num_rows = count($this->result_array()); + @oci_execute($this->stmt_id); + + if ($this->curs_id) + { + @oci_execute($this->curs_id); + } } return $rowcount; @@ -61,9 +64,9 @@ function num_rows() * @access public * @return integer */ - function num_fields() + public function num_fields() { - $count = @ocinumcols($this->stmt_id); + $count = @oci_num_fields($this->stmt_id); // if we used a limit we subtract it if ($this->limit_used) @@ -84,13 +87,12 @@ function num_fields() * @access public * @return array */ - function list_fields() + public function list_fields() { $field_names = array(); - $fieldCount = $this->num_fields(); - for ($c = 1; $c <= $fieldCount; $c++) + for ($c = 1, $fieldCount = $this->num_fields(); $c <= $fieldCount; $c++) { - $field_names[] = ocicolumnname($this->stmt_id, $c); + $field_names[] = oci_field_name($this->stmt_id, $c); } return $field_names; } @@ -105,16 +107,15 @@ function list_fields() * @access public * @return array */ - function field_data() + public function field_data() { $retval = array(); - $fieldCount = $this->num_fields(); - for ($c = 1; $c <= $fieldCount; $c++) + for ($c = 1, $fieldCount = $this->num_fields(); $c <= $fieldCount; $c++) { - $F = new stdClass(); - $F->name = ocicolumnname($this->stmt_id, $c); - $F->type = ocicolumntype($this->stmt_id, $c); - $F->max_length = ocicolumnsize($this->stmt_id, $c); + $F = new stdClass(); + $F->name = oci_field_name($this->stmt_id, $c); + $F->type = oci_field_type($this->stmt_id, $c); + $F->max_length = oci_field_size($this->stmt_id, $c); $retval[] = $F; } @@ -129,11 +130,11 @@ function field_data() * * @return null */ - function free_result() + public function free_result() { if (is_resource($this->result_id)) { - ocifreestatement($this->result_id); + oci_free_statement($this->result_id); $this->result_id = FALSE; } } @@ -145,14 +146,13 @@ function free_result() * * Returns the result set as an array * - * @access private + * @access protected * @return array */ - function _fetch_assoc(&$row) + protected function _fetch_assoc() { $id = ($this->curs_id) ? $this->curs_id : $this->stmt_id; - - return ocifetchinto($id, $row, OCI_ASSOC + OCI_RETURN_NULLS); + return oci_fetch_assoc($id); } // -------------------------------------------------------------------- @@ -162,41 +162,13 @@ function _fetch_assoc(&$row) * * Returns the result set as an object * - * @access private + * @access protected * @return object */ - function _fetch_object() + protected function _fetch_object() { - $result = array(); - - // If PHP 5 is being used we can fetch an result object - if (function_exists('oci_fetch_object')) - { - $id = ($this->curs_id) ? $this->curs_id : $this->stmt_id; - - return @oci_fetch_object($id); - } - - // If PHP 4 is being used we have to build our own result - foreach ($this->result_array() as $key => $val) - { - $obj = new stdClass(); - if (is_array($val)) - { - foreach ($val as $k => $v) - { - $obj->$k = $v; - } - } - else - { - $obj->$key = $val; - } - - $result[] = $obj; - } - - return $result; + $id = ($this->curs_id) ? $this->curs_id : $this->stmt_id; + return @oci_fetch_object($id); } // -------------------------------------------------------------------- @@ -207,17 +179,15 @@ function _fetch_object() * @access public * @return array */ - function result_array() + public function result_array() { if (count($this->result_array) > 0) { return $this->result_array; } - // oracle's fetch functions do not return arrays. - // The information is returned in reference parameters $row = NULL; - while ($this->_fetch_assoc($row)) + while ($row = $this->_fetch_assoc()) { $this->result_array[] = $row; } @@ -234,10 +204,10 @@ function result_array() * this internally before fetching results to make sure the * result set starts at zero * - * @access private + * @access protected * @return array */ - function _data_seek($n = 0) + protected function _data_seek($n = 0) { return FALSE; // Not needed } @@ -246,4 +216,4 @@ function _data_seek($n = 0) /* End of file oci8_result.php */ -/* Location: ./system/database/drivers/oci8/oci8_result.php */ \ No newline at end of file +/* Location: ./system/database/drivers/oci8/oci8_result.php */ diff --git a/system/database/drivers/oci8/oci8_utility.php b/system/database/drivers/oci8/oci8_utility.php old mode 100755 new mode 100644 diff --git a/system/database/drivers/odbc/index.html b/system/database/drivers/odbc/index.html old mode 100755 new mode 100644 diff --git a/system/database/drivers/odbc/odbc_driver.php b/system/database/drivers/odbc/odbc_driver.php old mode 100755 new mode 100644 index 81e0d7cf..bcd7937d --- a/system/database/drivers/odbc/odbc_driver.php +++ b/system/database/drivers/odbc/odbc_driver.php @@ -48,9 +48,9 @@ class CI_DB_odbc_driver extends CI_DB { var $_random_keyword; - function CI_DB_odbc_driver($params) + function __construct($params) { - parent::CI_DB($params); + parent::__construct($params); $this->_random_keyword = ' RND('.time().')'; // database specific random keyword } @@ -339,6 +339,7 @@ function count_all($table = '') } $row = $query->row(); + $this->_reset_select(); return (int) $row->numrows; } diff --git a/system/database/drivers/odbc/odbc_forge.php b/system/database/drivers/odbc/odbc_forge.php old mode 100755 new mode 100644 diff --git a/system/database/drivers/odbc/odbc_result.php b/system/database/drivers/odbc/odbc_result.php old mode 100755 new mode 100644 diff --git a/system/database/drivers/odbc/odbc_utility.php b/system/database/drivers/odbc/odbc_utility.php old mode 100755 new mode 100644 diff --git a/system/database/drivers/pdo/index.html b/system/database/drivers/pdo/index.html new file mode 100644 index 00000000..c942a79c --- /dev/null +++ b/system/database/drivers/pdo/index.html @@ -0,0 +1,10 @@ + + + 403 Forbidden + + + +

Directory access is forbidden.

+ + + \ No newline at end of file diff --git a/system/database/drivers/pdo/pdo_driver.php b/system/database/drivers/pdo/pdo_driver.php new file mode 100644 index 00000000..5de2079b --- /dev/null +++ b/system/database/drivers/pdo/pdo_driver.php @@ -0,0 +1,803 @@ +hostname, 'mysql') !== FALSE) + { + $this->_like_escape_str = ''; + $this->_like_escape_chr = ''; + + //Prior to this version, the charset can't be set in the dsn + if(is_php('5.3.6')) + { + $this->hostname .= ";charset={$this->char_set}"; + } + + //Set the charset with the connection options + $this->options['PDO::MYSQL_ATTR_INIT_COMMAND'] = "SET NAMES {$this->char_set}"; + } + else if (strpos($this->hostname, 'odbc') !== FALSE) + { + $this->_like_escape_str = " {escape '%s'} "; + $this->_like_escape_chr = '!'; + } + else + { + $this->_like_escape_str = " ESCAPE '%s' "; + $this->_like_escape_chr = '!'; + } + + $this->hostname .= ";dbname=".$this->database; + + $this->trans_enabled = FALSE; + + $this->_random_keyword = ' RND('.time().')'; // database specific random keyword + } + + /** + * Non-persistent database connection + * + * @access private called by the base class + * @return resource + */ + function db_connect() + { + $this->options['PDO::ATTR_ERRMODE'] = PDO::ERRMODE_SILENT; + + return new PDO($this->hostname, $this->username, $this->password, $this->options); + } + + // -------------------------------------------------------------------- + + /** + * Persistent database connection + * + * @access private called by the base class + * @return resource + */ + function db_pconnect() + { + $this->options['PDO::ATTR_ERRMODE'] = PDO::ERRMODE_SILENT; + $this->options['PDO::ATTR_PERSISTENT'] = TRUE; + + return new PDO($this->hostname, $this->username, $this->password, $this->options); + } + + // -------------------------------------------------------------------- + + /** + * Reconnect + * + * Keep / reestablish the db connection if no queries have been + * sent for a length of time exceeding the server's idle timeout + * + * @access public + * @return void + */ + function reconnect() + { + if ($this->db->db_debug) + { + return $this->db->display_error('db_unsuported_feature'); + } + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Select the database + * + * @access private called by the base class + * @return resource + */ + function db_select() + { + // Not needed for PDO + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Set client character set + * + * @access public + * @param string + * @param string + * @return resource + */ + function db_set_charset($charset, $collation) + { + // @todo - add support if needed + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Version number query string + * + * @access public + * @return string + */ + function _version() + { + return $this->conn_id->getAttribute(PDO::ATTR_CLIENT_VERSION); + } + + // -------------------------------------------------------------------- + + /** + * Execute the query + * + * @access private called by the base class + * @param string an SQL query + * @return object + */ + function _execute($sql) + { + $sql = $this->_prep_query($sql); + $result_id = $this->conn_id->query($sql); + + if (is_object($result_id)) + { + $this->affect_rows = $result_id->rowCount(); + } + else + { + $this->affect_rows = 0; + } + + return $result_id; + } + + // -------------------------------------------------------------------- + + /** + * Prep the query + * + * If needed, each database adapter can prep the query string + * + * @access private called by execute() + * @param string an SQL query + * @return string + */ + function _prep_query($sql) + { + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Begin Transaction + * + * @access public + * @return bool + */ + function trans_begin($test_mode = FALSE) + { + if ( ! $this->trans_enabled) + { + return TRUE; + } + + // When transactions are nested we only begin/commit/rollback the outermost ones + if ($this->_trans_depth > 0) + { + return TRUE; + } + + // Reset the transaction failure flag. + // If the $test_mode flag is set to TRUE transactions will be rolled back + // even if the queries produce a successful result. + $this->_trans_failure = (bool) ($test_mode === TRUE); + + return $this->conn_id->beginTransaction(); + } + + // -------------------------------------------------------------------- + + /** + * Commit Transaction + * + * @access public + * @return bool + */ + function trans_commit() + { + if ( ! $this->trans_enabled) + { + return TRUE; + } + + // When transactions are nested we only begin/commit/rollback the outermost ones + if ($this->_trans_depth > 0) + { + return TRUE; + } + + $ret = $this->conn->commit(); + return $ret; + } + + // -------------------------------------------------------------------- + + /** + * Rollback Transaction + * + * @access public + * @return bool + */ + function trans_rollback() + { + if ( ! $this->trans_enabled) + { + return TRUE; + } + + // When transactions are nested we only begin/commit/rollback the outermost ones + if ($this->_trans_depth > 0) + { + return TRUE; + } + + $ret = $this->conn_id->rollBack(); + return $ret; + } + + // -------------------------------------------------------------------- + + /** + * Escape String + * + * @access public + * @param string + * @param bool whether or not the string will be used in a LIKE condition + * @return string + */ + function escape_str($str, $like = FALSE) + { + if (is_array($str)) + { + foreach ($str as $key => $val) + { + $str[$key] = $this->escape_str($val, $like); + } + + return $str; + } + + //Escape the string + $str = $this->conn_id->quote($str); + + //If there are duplicated quotes, trim them away + if (strpos($str, "'") === 0) + { + $str = substr($str, 1, -1); + } + + // escape LIKE condition wildcards + if ($like === TRUE) + { + $str = str_replace( array('%', '_', $this->_like_escape_chr), + array($this->_like_escape_chr.'%', $this->_like_escape_chr.'_', $this->_like_escape_chr.$this->_like_escape_chr), + $str); + } + + return $str; + } + + // -------------------------------------------------------------------- + + /** + * Affected Rows + * + * @access public + * @return integer + */ + function affected_rows() + { + return $this->affect_rows; + } + + // -------------------------------------------------------------------- + + /** + * Insert ID + * + * @access public + * @return integer + */ + function insert_id($name=NULL) + { + //Convenience method for postgres insertid + if (strpos($this->hostname, 'pgsql') !== FALSE) + { + $v = $this->_version(); + + $table = func_num_args() > 0 ? func_get_arg(0) : NULL; + + if ($table == NULL && $v >= '8.1') + { + $sql='SELECT LASTVAL() as ins_id'; + } + $query = $this->query($sql); + $row = $query->row(); + return $row->ins_id; + } + else + { + return $this->conn_id->lastInsertId($name); + } + } + + // -------------------------------------------------------------------- + + /** + * "Count All" query + * + * Generates a platform-specific query string that counts all records in + * the specified database + * + * @access public + * @param string + * @return string + */ + function count_all($table = '') + { + if ($table == '') + { + return 0; + } + + $query = $this->query($this->_count_string . $this->_protect_identifiers('numrows') . " FROM " . $this->_protect_identifiers($table, TRUE, NULL, FALSE)); + + if ($query->num_rows() == 0) + { + return 0; + } + + $row = $query->row(); + $this->_reset_select(); + return (int) $row->numrows; + } + + // -------------------------------------------------------------------- + + /** + * Show table query + * + * Generates a platform-specific query string so that the table names can be fetched + * + * @access private + * @param boolean + * @return string + */ + function _list_tables($prefix_limit = FALSE) + { + $sql = "SHOW TABLES FROM `".$this->database."`"; + + if ($prefix_limit !== FALSE AND $this->dbprefix != '') + { + //$sql .= " LIKE '".$this->escape_like_str($this->dbprefix)."%' ".sprintf($this->_like_escape_str, $this->_like_escape_chr); + return FALSE; // not currently supported + } + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Show column query + * + * Generates a platform-specific query string so that the column names can be fetched + * + * @access public + * @param string the table name + * @return string + */ + function _list_columns($table = '') + { + return "SHOW COLUMNS FROM ".$table; + } + + // -------------------------------------------------------------------- + + /** + * Field data query + * + * Generates a platform-specific query so that the column data can be retrieved + * + * @access public + * @param string the table name + * @return object + */ + function _field_data($table) + { + return "SELECT TOP 1 FROM ".$table; + } + + // -------------------------------------------------------------------- + + /** + * The error message string + * + * @access private + * @return string + */ + function _error_message() + { + $error_array = $this->conn_id->errorInfo(); + return $error_array[2]; + } + + // -------------------------------------------------------------------- + + /** + * The error message number + * + * @access private + * @return integer + */ + function _error_number() + { + return $this->conn_id->errorCode(); + } + + // -------------------------------------------------------------------- + + /** + * Escape the SQL Identifiers + * + * This function escapes column and table names + * + * @access private + * @param string + * @return string + */ + function _escape_identifiers($item) + { + if ($this->_escape_char == '') + { + return $item; + } + + foreach ($this->_reserved_identifiers as $id) + { + if (strpos($item, '.'.$id) !== FALSE) + { + $str = $this->_escape_char. str_replace('.', $this->_escape_char.'.', $item); + + // remove duplicates if the user already included the escape + return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str); + } + } + + if (strpos($item, '.') !== FALSE) + { + $str = $this->_escape_char.str_replace('.', $this->_escape_char.'.'.$this->_escape_char, $item).$this->_escape_char; + + } + else + { + $str = $this->_escape_char.$item.$this->_escape_char; + } + + // remove duplicates if the user already included the escape + return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str); + } + + // -------------------------------------------------------------------- + + /** + * From Tables + * + * This function implicitly groups FROM tables so there is no confusion + * about operator precedence in harmony with SQL standards + * + * @access public + * @param type + * @return type + */ + function _from_tables($tables) + { + if ( ! is_array($tables)) + { + $tables = array($tables); + } + + return (count($tables) == 1) ? $tables[0] : '('.implode(', ', $tables).')'; + } + + // -------------------------------------------------------------------- + + /** + * Insert statement + * + * Generates a platform-specific insert string from the supplied data + * + * @access public + * @param string the table name + * @param array the insert keys + * @param array the insert values + * @return string + */ + function _insert($table, $keys, $values) + { + return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")"; + } + + // -------------------------------------------------------------------- + + /** + * Insert_batch statement + * + * Generates a platform-specific insert string from the supplied data + * + * @access public + * @param string the table name + * @param array the insert keys + * @param array the insert values + * @return string + */ + function _insert_batch($table, $keys, $values) + { + return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES ".implode(', ', $values); + } + + // -------------------------------------------------------------------- + + /** + * Update statement + * + * Generates a platform-specific update string from the supplied data + * + * @access public + * @param string the table name + * @param array the update data + * @param array the where clause + * @param array the orderby clause + * @param array the limit clause + * @return string + */ + function _update($table, $values, $where, $orderby = array(), $limit = FALSE) + { + foreach ($values as $key => $val) + { + $valstr[] = $key." = ".$val; + } + + $limit = ( ! $limit) ? '' : ' LIMIT '.$limit; + + $orderby = (count($orderby) >= 1)?' ORDER BY '.implode(", ", $orderby):''; + + $sql = "UPDATE ".$table." SET ".implode(', ', $valstr); + + $sql .= ($where != '' AND count($where) >=1) ? " WHERE ".implode(" ", $where) : ''; + + $sql .= $orderby.$limit; + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Update_Batch statement + * + * Generates a platform-specific batch update string from the supplied data + * + * @access public + * @param string the table name + * @param array the update data + * @param array the where clause + * @return string + */ + function _update_batch($table, $values, $index, $where = NULL) + { + $ids = array(); + $where = ($where != '' AND count($where) >=1) ? implode(" ", $where).' AND ' : ''; + + foreach ($values as $key => $val) + { + $ids[] = $val[$index]; + + foreach (array_keys($val) as $field) + { + if ($field != $index) + { + $final[$field][] = 'WHEN '.$index.' = '.$val[$index].' THEN '.$val[$field]; + } + } + } + + $sql = "UPDATE ".$table." SET "; + $cases = ''; + + foreach ($final as $k => $v) + { + $cases .= $k.' = CASE '."\n"; + foreach ($v as $row) + { + $cases .= $row."\n"; + } + + $cases .= 'ELSE '.$k.' END, '; + } + + $sql .= substr($cases, 0, -2); + + $sql .= ' WHERE '.$where.$index.' IN ('.implode(',', $ids).')'; + + return $sql; + } + + + // -------------------------------------------------------------------- + + /** + * Truncate statement + * + * Generates a platform-specific truncate string from the supplied data + * If the database does not support the truncate() command + * This function maps to "DELETE FROM table" + * + * @access public + * @param string the table name + * @return string + */ + function _truncate($table) + { + return $this->_delete($table); + } + + // -------------------------------------------------------------------- + + /** + * Delete statement + * + * Generates a platform-specific delete string from the supplied data + * + * @access public + * @param string the table name + * @param array the where clause + * @param string the limit clause + * @return string + */ + function _delete($table, $where = array(), $like = array(), $limit = FALSE) + { + $conditions = ''; + + if (count($where) > 0 OR count($like) > 0) + { + $conditions = "\nWHERE "; + $conditions .= implode("\n", $this->ar_where); + + if (count($where) > 0 && count($like) > 0) + { + $conditions .= " AND "; + } + $conditions .= implode("\n", $like); + } + + $limit = ( ! $limit) ? '' : ' LIMIT '.$limit; + + return "DELETE FROM ".$table.$conditions.$limit; + } + + // -------------------------------------------------------------------- + + /** + * Limit string + * + * Generates a platform-specific LIMIT clause + * + * @access public + * @param string the sql query string + * @param integer the number of rows to limit the query to + * @param integer the offset value + * @return string + */ + function _limit($sql, $limit, $offset) + { + if (strpos($this->hostname, 'cubrid') !== FALSE || strpos($this->hostname, 'sqlite') !== FALSE) + { + if ($offset == 0) + { + $offset = ''; + } + else + { + $offset .= ", "; + } + + return $sql."LIMIT ".$offset.$limit; + } + else + { + $sql .= "LIMIT ".$limit; + + if ($offset > 0) + { + $sql .= " OFFSET ".$offset; + } + + return $sql; + } + } + + // -------------------------------------------------------------------- + + /** + * Close DB Connection + * + * @access public + * @param resource + * @return void + */ + function _close($conn_id) + { + $this->conn_id = null; + } + + +} + + + +/* End of file pdo_driver.php */ +/* Location: ./system/database/drivers/pdo/pdo_driver.php */ \ No newline at end of file diff --git a/system/database/drivers/pdo/pdo_forge.php b/system/database/drivers/pdo/pdo_forge.php new file mode 100644 index 00000000..1462e8c2 --- /dev/null +++ b/system/database/drivers/pdo/pdo_forge.php @@ -0,0 +1,266 @@ +db->db_debug) + { + return $this->db->display_error('db_unsuported_feature'); + } + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Drop database + * + * @access private + * @param string the database name + * @return bool + */ + function _drop_database($name) + { + // PDO has no "drop database" command since it's + // designed to connect to an existing database + if ($this->db->db_debug) + { + return $this->db->display_error('db_unsuported_feature'); + } + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Create Table + * + * @access private + * @param string the table name + * @param array the fields + * @param mixed primary key(s) + * @param mixed key(s) + * @param boolean should 'IF NOT EXISTS' be added to the SQL + * @return bool + */ + function _create_table($table, $fields, $primary_keys, $keys, $if_not_exists) + { + $sql = 'CREATE TABLE '; + + if ($if_not_exists === TRUE) + { + $sql .= 'IF NOT EXISTS '; + } + + $sql .= $this->db->_escape_identifiers($table)." ("; + $current_field_count = 0; + + foreach ($fields as $field=>$attributes) + { + // Numeric field names aren't allowed in databases, so if the key is + // numeric, we know it was assigned by PHP and the developer manually + // entered the field information, so we'll simply add it to the list + if (is_numeric($field)) + { + $sql .= "\n\t$attributes"; + } + else + { + $attributes = array_change_key_case($attributes, CASE_UPPER); + + $sql .= "\n\t".$this->db->_protect_identifiers($field); + + $sql .= ' '.$attributes['TYPE']; + + if (array_key_exists('CONSTRAINT', $attributes)) + { + $sql .= '('.$attributes['CONSTRAINT'].')'; + } + + if (array_key_exists('UNSIGNED', $attributes) && $attributes['UNSIGNED'] === TRUE) + { + $sql .= ' UNSIGNED'; + } + + if (array_key_exists('DEFAULT', $attributes)) + { + $sql .= ' DEFAULT \''.$attributes['DEFAULT'].'\''; + } + + if (array_key_exists('NULL', $attributes) && $attributes['NULL'] === TRUE) + { + $sql .= ' NULL'; + } + else + { + $sql .= ' NOT NULL'; + } + + if (array_key_exists('AUTO_INCREMENT', $attributes) && $attributes['AUTO_INCREMENT'] === TRUE) + { + $sql .= ' AUTO_INCREMENT'; + } + } + + // don't add a comma on the end of the last field + if (++$current_field_count < count($fields)) + { + $sql .= ','; + } + } + + if (count($primary_keys) > 0) + { + $primary_keys = $this->db->_protect_identifiers($primary_keys); + $sql .= ",\n\tPRIMARY KEY (" . implode(', ', $primary_keys) . ")"; + } + + if (is_array($keys) && count($keys) > 0) + { + foreach ($keys as $key) + { + if (is_array($key)) + { + $key = $this->db->_protect_identifiers($key); + } + else + { + $key = array($this->db->_protect_identifiers($key)); + } + + $sql .= ",\n\tFOREIGN KEY (" . implode(', ', $key) . ")"; + } + } + + $sql .= "\n)"; + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Drop Table + * + * @access private + * @return bool + */ + function _drop_table($table) + { + // Not a supported PDO feature + if ($this->db->db_debug) + { + return $this->db->display_error('db_unsuported_feature'); + } + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Alter table query + * + * Generates a platform-specific query so that a table can be altered + * Called by add_column(), drop_column(), and column_alter(), + * + * @access private + * @param string the ALTER type (ADD, DROP, CHANGE) + * @param string the column name + * @param string the table name + * @param string the column definition + * @param string the default value + * @param boolean should 'NOT NULL' be added + * @param string the field after which we should add the new field + * @return object + */ + function _alter_table($alter_type, $table, $column_name, $column_definition = '', $default_value = '', $null = '', $after_field = '') + { + $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table)." $alter_type ".$this->db->_protect_identifiers($column_name); + + // DROP has everything it needs now. + if ($alter_type == 'DROP') + { + return $sql; + } + + $sql .= " $column_definition"; + + if ($default_value != '') + { + $sql .= " DEFAULT \"$default_value\""; + } + + if ($null === NULL) + { + $sql .= ' NULL'; + } + else + { + $sql .= ' NOT NULL'; + } + + if ($after_field != '') + { + $sql .= ' AFTER ' . $this->db->_protect_identifiers($after_field); + } + + return $sql; + + } + + + // -------------------------------------------------------------------- + + /** + * Rename a table + * + * Generates a platform-specific query so that a table can be renamed + * + * @access private + * @param string the old table name + * @param string the new table name + * @return string + */ + function _rename_table($table_name, $new_table_name) + { + $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table_name)." RENAME TO ".$this->db->_protect_identifiers($new_table_name); + return $sql; + } + + +} + +/* End of file pdo_forge.php */ +/* Location: ./system/database/drivers/pdo/pdo_forge.php */ \ No newline at end of file diff --git a/system/database/drivers/pdo/pdo_result.php b/system/database/drivers/pdo/pdo_result.php new file mode 100644 index 00000000..7f3058ff --- /dev/null +++ b/system/database/drivers/pdo/pdo_result.php @@ -0,0 +1,171 @@ +result_id->rowCount(); + } + + // -------------------------------------------------------------------- + + /** + * Number of fields in the result set + * + * @access public + * @return integer + */ + function num_fields() + { + return $this->result_id->columnCount(); + } + + // -------------------------------------------------------------------- + + /** + * Fetch Field Names + * + * Generates an array of column names + * + * @access public + * @return array + */ + function list_fields() + { + if ($this->db->db_debug) + { + return $this->db->display_error('db_unsuported_feature'); + } + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Field data + * + * Generates an array of objects containing field meta-data + * + * @access public + * @return array + */ + function field_data() + { + $data = array(); + + try + { + for($i = 0; $i < $this->num_fields(); $i++) + { + $data[] = $this->result_id->getColumnMeta($i); + } + + return $data; + } + catch (Exception $e) + { + if ($this->db->db_debug) + { + return $this->db->display_error('db_unsuported_feature'); + } + return FALSE; + } + } + + // -------------------------------------------------------------------- + + /** + * Free the result + * + * @return null + */ + function free_result() + { + if (is_object($this->result_id)) + { + $this->result_id = FALSE; + } + } + + // -------------------------------------------------------------------- + + /** + * Data Seek + * + * Moves the internal pointer to the desired offset. We call + * this internally before fetching results to make sure the + * result set starts at zero + * + * @access private + * @return array + */ + function _data_seek($n = 0) + { + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Result - associative array + * + * Returns the result set as an array + * + * @access private + * @return array + */ + function _fetch_assoc() + { + return $this->result_id->fetch(PDO::FETCH_ASSOC); + } + + // -------------------------------------------------------------------- + + /** + * Result - object + * + * Returns the result set as an object + * + * @access private + * @return object + */ + function _fetch_object() + { + return $this->result_id->fetchObject(); + } + +} + + +/* End of file pdo_result.php */ +/* Location: ./system/database/drivers/pdo/pdo_result.php */ \ No newline at end of file diff --git a/system/database/drivers/pdo/pdo_utility.php b/system/database/drivers/pdo/pdo_utility.php new file mode 100644 index 00000000..29aefca8 --- /dev/null +++ b/system/database/drivers/pdo/pdo_utility.php @@ -0,0 +1,103 @@ +db->db_debug) + { + return $this->db->display_error('db_unsuported_feature'); + } + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Optimize table query + * + * Generates a platform-specific query so that a table can be optimized + * + * @access private + * @param string the table name + * @return object + */ + function _optimize_table($table) + { + // Not a supported PDO feature + if ($this->db->db_debug) + { + return $this->db->display_error('db_unsuported_feature'); + } + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Repair table query + * + * Generates a platform-specific query so that a table can be repaired + * + * @access private + * @param string the table name + * @return object + */ + function _repair_table($table) + { + // Not a supported PDO feature + if ($this->db->db_debug) + { + return $this->db->display_error('db_unsuported_feature'); + } + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * PDO Export + * + * @access private + * @param array Preferences + * @return mixed + */ + function _backup($params = array()) + { + // Currently unsupported + return $this->db->display_error('db_unsuported_feature'); + } + +} + +/* End of file pdo_utility.php */ +/* Location: ./system/database/drivers/pdo/pdo_utility.php */ \ No newline at end of file diff --git a/system/database/drivers/postgre/index.html b/system/database/drivers/postgre/index.html old mode 100755 new mode 100644 diff --git a/system/database/drivers/postgre/postgre_driver.php b/system/database/drivers/postgre/postgre_driver.php old mode 100755 new mode 100644 index 14039688..5367f975 --- a/system/database/drivers/postgre/postgre_driver.php +++ b/system/database/drivers/postgre/postgre_driver.php @@ -385,6 +385,7 @@ function count_all($table = '') } $row = $query->row(); + $this->_reset_select(); return (int) $row->numrows; } diff --git a/system/database/drivers/postgre/postgre_forge.php b/system/database/drivers/postgre/postgre_forge.php old mode 100755 new mode 100644 diff --git a/system/database/drivers/postgre/postgre_result.php b/system/database/drivers/postgre/postgre_result.php old mode 100755 new mode 100644 diff --git a/system/database/drivers/postgre/postgre_utility.php b/system/database/drivers/postgre/postgre_utility.php old mode 100755 new mode 100644 diff --git a/system/database/drivers/sqlite/index.html b/system/database/drivers/sqlite/index.html old mode 100755 new mode 100644 diff --git a/system/database/drivers/sqlite/sqlite_driver.php b/system/database/drivers/sqlite/sqlite_driver.php old mode 100755 new mode 100644 index eb4e585b..0cc898b3 --- a/system/database/drivers/sqlite/sqlite_driver.php +++ b/system/database/drivers/sqlite/sqlite_driver.php @@ -354,6 +354,7 @@ function count_all($table = '') } $row = $query->row(); + $this->_reset_select(); return (int) $row->numrows; } diff --git a/system/database/drivers/sqlite/sqlite_forge.php b/system/database/drivers/sqlite/sqlite_forge.php old mode 100755 new mode 100644 diff --git a/system/database/drivers/sqlite/sqlite_result.php b/system/database/drivers/sqlite/sqlite_result.php old mode 100755 new mode 100644 diff --git a/system/database/drivers/sqlite/sqlite_utility.php b/system/database/drivers/sqlite/sqlite_utility.php old mode 100755 new mode 100644 diff --git a/system/database/drivers/sqlsrv/index.html b/system/database/drivers/sqlsrv/index.html new file mode 100644 index 00000000..c942a79c --- /dev/null +++ b/system/database/drivers/sqlsrv/index.html @@ -0,0 +1,10 @@ + + + 403 Forbidden + + + +

Directory access is forbidden.

+ + + \ No newline at end of file diff --git a/system/database/drivers/sqlsrv/sqlsrv_driver.php b/system/database/drivers/sqlsrv/sqlsrv_driver.php new file mode 100644 index 00000000..400fd31c --- /dev/null +++ b/system/database/drivers/sqlsrv/sqlsrv_driver.php @@ -0,0 +1,599 @@ +char_set)) ? 'UTF-8' : $this->char_set; + + $connection = array( + 'UID' => empty($this->username) ? '' : $this->username, + 'PWD' => empty($this->password) ? '' : $this->password, + 'Database' => $this->database, + 'ConnectionPooling' => $pooling ? 1 : 0, + 'CharacterSet' => $character_set, + 'ReturnDatesAsStrings' => 1 + ); + + // If the username and password are both empty, assume this is a + // 'Windows Authentication Mode' connection. + if(empty($connection['UID']) && empty($connection['PWD'])) { + unset($connection['UID'], $connection['PWD']); + } + + return sqlsrv_connect($this->hostname, $connection); + } + + // -------------------------------------------------------------------- + + /** + * Persistent database connection + * + * @access private called by the base class + * @return resource + */ + function db_pconnect() + { + $this->db_connect(TRUE); + } + + // -------------------------------------------------------------------- + + /** + * Reconnect + * + * Keep / reestablish the db connection if no queries have been + * sent for a length of time exceeding the server's idle timeout + * + * @access public + * @return void + */ + function reconnect() + { + // not implemented in MSSQL + } + + // -------------------------------------------------------------------- + + /** + * Select the database + * + * @access private called by the base class + * @return resource + */ + function db_select() + { + return $this->_execute('USE ' . $this->database); + } + + // -------------------------------------------------------------------- + + /** + * Set client character set + * + * @access public + * @param string + * @param string + * @return resource + */ + function db_set_charset($charset, $collation) + { + // @todo - add support if needed + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Execute the query + * + * @access private called by the base class + * @param string an SQL query + * @return resource + */ + function _execute($sql) + { + $sql = $this->_prep_query($sql); + return sqlsrv_query($this->conn_id, $sql, null, array( + 'Scrollable' => SQLSRV_CURSOR_STATIC, + 'SendStreamParamsAtExec' => true + )); + } + + // -------------------------------------------------------------------- + + /** + * Prep the query + * + * If needed, each database adapter can prep the query string + * + * @access private called by execute() + * @param string an SQL query + * @return string + */ + function _prep_query($sql) + { + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Begin Transaction + * + * @access public + * @return bool + */ + function trans_begin($test_mode = FALSE) + { + if ( ! $this->trans_enabled) + { + return TRUE; + } + + // When transactions are nested we only begin/commit/rollback the outermost ones + if ($this->_trans_depth > 0) + { + return TRUE; + } + + // Reset the transaction failure flag. + // If the $test_mode flag is set to TRUE transactions will be rolled back + // even if the queries produce a successful result. + $this->_trans_failure = ($test_mode === TRUE) ? TRUE : FALSE; + + return sqlsrv_begin_transaction($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * Commit Transaction + * + * @access public + * @return bool + */ + function trans_commit() + { + if ( ! $this->trans_enabled) + { + return TRUE; + } + + // When transactions are nested we only begin/commit/rollback the outermost ones + if ($this->_trans_depth > 0) + { + return TRUE; + } + + return sqlsrv_commit($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * Rollback Transaction + * + * @access public + * @return bool + */ + function trans_rollback() + { + if ( ! $this->trans_enabled) + { + return TRUE; + } + + // When transactions are nested we only begin/commit/rollback the outermost ones + if ($this->_trans_depth > 0) + { + return TRUE; + } + + return sqlsrv_rollback($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * Escape String + * + * @access public + * @param string + * @param bool whether or not the string will be used in a LIKE condition + * @return string + */ + function escape_str($str, $like = FALSE) + { + // Escape single quotes + return str_replace("'", "''", $str); + } + + // -------------------------------------------------------------------- + + /** + * Affected Rows + * + * @access public + * @return integer + */ + function affected_rows() + { + return @sqlrv_rows_affected($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * Insert ID + * + * Returns the last id created in the Identity column. + * + * @access public + * @return integer + */ + function insert_id() + { + return $this->query('select @@IDENTITY as insert_id')->row('insert_id'); + } + + // -------------------------------------------------------------------- + + /** + * Parse major version + * + * Grabs the major version number from the + * database server version string passed in. + * + * @access private + * @param string $version + * @return int16 major version number + */ + function _parse_major_version($version) + { + preg_match('/([0-9]+)\.([0-9]+)\.([0-9]+)/', $version, $ver_info); + return $ver_info[1]; // return the major version b/c that's all we're interested in. + } + + // -------------------------------------------------------------------- + + /** + * Version number query string + * + * @access public + * @return string + */ + function _version() + { + $info = sqlsrv_server_info($this->conn_id); + return sprintf("select '%s' as ver", $info['SQLServerVersion']); + } + + // -------------------------------------------------------------------- + + /** + * "Count All" query + * + * Generates a platform-specific query string that counts all records in + * the specified database + * + * @access public + * @param string + * @return string + */ + function count_all($table = '') + { + if ($table == '') + return '0'; + + $query = $this->query("SELECT COUNT(*) AS numrows FROM " . $this->dbprefix . $table); + + if ($query->num_rows() == 0) + return '0'; + + $row = $query->row(); + $this->_reset_select(); + return $row->numrows; + } + + // -------------------------------------------------------------------- + + /** + * List table query + * + * Generates a platform-specific query string so that the table names can be fetched + * + * @access private + * @param boolean + * @return string + */ + function _list_tables($prefix_limit = FALSE) + { + return "SELECT name FROM sysobjects WHERE type = 'U' ORDER BY name"; + } + + // -------------------------------------------------------------------- + + /** + * List column query + * + * Generates a platform-specific query string so that the column names can be fetched + * + * @access private + * @param string the table name + * @return string + */ + function _list_columns($table = '') + { + return "SELECT * FROM INFORMATION_SCHEMA.Columns WHERE TABLE_NAME = '".$this->_escape_table($table)."'"; + } + + // -------------------------------------------------------------------- + + /** + * Field data query + * + * Generates a platform-specific query so that the column data can be retrieved + * + * @access public + * @param string the table name + * @return object + */ + function _field_data($table) + { + return "SELECT TOP 1 * FROM " . $this->_escape_table($table); + } + + // -------------------------------------------------------------------- + + /** + * The error message string + * + * @access private + * @return string + */ + function _error_message() + { + $error = array_shift(sqlsrv_errors()); + return !empty($error['message']) ? $error['message'] : null; + } + + // -------------------------------------------------------------------- + + /** + * The error message number + * + * @access private + * @return integer + */ + function _error_number() + { + $error = array_shift(sqlsrv_errors()); + return isset($error['SQLSTATE']) ? $error['SQLSTATE'] : null; + } + + // -------------------------------------------------------------------- + + /** + * Escape Table Name + * + * This function adds backticks if the table name has a period + * in it. Some DBs will get cranky unless periods are escaped + * + * @access private + * @param string the table name + * @return string + */ + function _escape_table($table) + { + return $table; + } + + + /** + * Escape the SQL Identifiers + * + * This function escapes column and table names + * + * @access private + * @param string + * @return string + */ + function _escape_identifiers($item) + { + return $item; + } + + // -------------------------------------------------------------------- + + /** + * From Tables + * + * This function implicitly groups FROM tables so there is no confusion + * about operator precedence in harmony with SQL standards + * + * @access public + * @param type + * @return type + */ + function _from_tables($tables) + { + if ( ! is_array($tables)) + { + $tables = array($tables); + } + + return implode(', ', $tables); + } + + // -------------------------------------------------------------------- + + /** + * Insert statement + * + * Generates a platform-specific insert string from the supplied data + * + * @access public + * @param string the table name + * @param array the insert keys + * @param array the insert values + * @return string + */ + function _insert($table, $keys, $values) + { + return "INSERT INTO ".$this->_escape_table($table)." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")"; + } + + // -------------------------------------------------------------------- + + /** + * Update statement + * + * Generates a platform-specific update string from the supplied data + * + * @access public + * @param string the table name + * @param array the update data + * @param array the where clause + * @param array the orderby clause + * @param array the limit clause + * @return string + */ + function _update($table, $values, $where) + { + foreach($values as $key => $val) + { + $valstr[] = $key." = ".$val; + } + + return "UPDATE ".$this->_escape_table($table)." SET ".implode(', ', $valstr)." WHERE ".implode(" ", $where); + } + + // -------------------------------------------------------------------- + + /** + * Truncate statement + * + * Generates a platform-specific truncate string from the supplied data + * If the database does not support the truncate() command + * This function maps to "DELETE FROM table" + * + * @access public + * @param string the table name + * @return string + */ + function _truncate($table) + { + return "TRUNCATE ".$table; + } + + // -------------------------------------------------------------------- + + /** + * Delete statement + * + * Generates a platform-specific delete string from the supplied data + * + * @access public + * @param string the table name + * @param array the where clause + * @param string the limit clause + * @return string + */ + function _delete($table, $where) + { + return "DELETE FROM ".$this->_escape_table($table)." WHERE ".implode(" ", $where); + } + + // -------------------------------------------------------------------- + + /** + * Limit string + * + * Generates a platform-specific LIMIT clause + * + * @access public + * @param string the sql query string + * @param integer the number of rows to limit the query to + * @param integer the offset value + * @return string + */ + function _limit($sql, $limit, $offset) + { + $i = $limit + $offset; + + return preg_replace('/(^\SELECT (DISTINCT)?)/i','\\1 TOP '.$i.' ', $sql); + } + + // -------------------------------------------------------------------- + + /** + * Close DB Connection + * + * @access public + * @param resource + * @return void + */ + function _close($conn_id) + { + @sqlsrv_close($conn_id); + } + +} + + + +/* End of file mssql_driver.php */ +/* Location: ./system/database/drivers/mssql/mssql_driver.php */ \ No newline at end of file diff --git a/system/database/drivers/sqlsrv/sqlsrv_forge.php b/system/database/drivers/sqlsrv/sqlsrv_forge.php new file mode 100644 index 00000000..cc88ec5c --- /dev/null +++ b/system/database/drivers/sqlsrv/sqlsrv_forge.php @@ -0,0 +1,248 @@ +db->_escape_identifiers($table); + } + + // -------------------------------------------------------------------- + + /** + * Create Table + * + * @access private + * @param string the table name + * @param array the fields + * @param mixed primary key(s) + * @param mixed key(s) + * @param boolean should 'IF NOT EXISTS' be added to the SQL + * @return bool + */ + function _create_table($table, $fields, $primary_keys, $keys, $if_not_exists) + { + $sql = 'CREATE TABLE '; + + if ($if_not_exists === TRUE) + { + $sql .= 'IF NOT EXISTS '; + } + + $sql .= $this->db->_escape_identifiers($table)." ("; + $current_field_count = 0; + + foreach ($fields as $field=>$attributes) + { + // Numeric field names aren't allowed in databases, so if the key is + // numeric, we know it was assigned by PHP and the developer manually + // entered the field information, so we'll simply add it to the list + if (is_numeric($field)) + { + $sql .= "\n\t$attributes"; + } + else + { + $attributes = array_change_key_case($attributes, CASE_UPPER); + + $sql .= "\n\t".$this->db->_protect_identifiers($field); + + $sql .= ' '.$attributes['TYPE']; + + if (array_key_exists('CONSTRAINT', $attributes)) + { + $sql .= '('.$attributes['CONSTRAINT'].')'; + } + + if (array_key_exists('UNSIGNED', $attributes) && $attributes['UNSIGNED'] === TRUE) + { + $sql .= ' UNSIGNED'; + } + + if (array_key_exists('DEFAULT', $attributes)) + { + $sql .= ' DEFAULT \''.$attributes['DEFAULT'].'\''; + } + + if (array_key_exists('NULL', $attributes) && $attributes['NULL'] === TRUE) + { + $sql .= ' NULL'; + } + else + { + $sql .= ' NOT NULL'; + } + + if (array_key_exists('AUTO_INCREMENT', $attributes) && $attributes['AUTO_INCREMENT'] === TRUE) + { + $sql .= ' AUTO_INCREMENT'; + } + } + + // don't add a comma on the end of the last field + if (++$current_field_count < count($fields)) + { + $sql .= ','; + } + } + + if (count($primary_keys) > 0) + { + $primary_keys = $this->db->_protect_identifiers($primary_keys); + $sql .= ",\n\tPRIMARY KEY (" . implode(', ', $primary_keys) . ")"; + } + + if (is_array($keys) && count($keys) > 0) + { + foreach ($keys as $key) + { + if (is_array($key)) + { + $key = $this->db->_protect_identifiers($key); + } + else + { + $key = array($this->db->_protect_identifiers($key)); + } + + $sql .= ",\n\tFOREIGN KEY (" . implode(', ', $key) . ")"; + } + } + + $sql .= "\n)"; + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Alter table query + * + * Generates a platform-specific query so that a table can be altered + * Called by add_column(), drop_column(), and column_alter(), + * + * @access private + * @param string the ALTER type (ADD, DROP, CHANGE) + * @param string the column name + * @param string the table name + * @param string the column definition + * @param string the default value + * @param boolean should 'NOT NULL' be added + * @param string the field after which we should add the new field + * @return object + */ + function _alter_table($alter_type, $table, $column_name, $column_definition = '', $default_value = '', $null = '', $after_field = '') + { + $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table)." $alter_type ".$this->db->_protect_identifiers($column_name); + + // DROP has everything it needs now. + if ($alter_type == 'DROP') + { + return $sql; + } + + $sql .= " $column_definition"; + + if ($default_value != '') + { + $sql .= " DEFAULT \"$default_value\""; + } + + if ($null === NULL) + { + $sql .= ' NULL'; + } + else + { + $sql .= ' NOT NULL'; + } + + if ($after_field != '') + { + $sql .= ' AFTER ' . $this->db->_protect_identifiers($after_field); + } + + return $sql; + + } + + // -------------------------------------------------------------------- + + /** + * Rename a table + * + * Generates a platform-specific query so that a table can be renamed + * + * @access private + * @param string the old table name + * @param string the new table name + * @return string + */ + function _rename_table($table_name, $new_table_name) + { + // I think this syntax will work, but can find little documentation on renaming tables in MSSQL + $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table_name)." RENAME TO ".$this->db->_protect_identifiers($new_table_name); + return $sql; + } + +} + +/* End of file mssql_forge.php */ +/* Location: ./system/database/drivers/mssql/mssql_forge.php */ \ No newline at end of file diff --git a/system/database/drivers/sqlsrv/sqlsrv_result.php b/system/database/drivers/sqlsrv/sqlsrv_result.php new file mode 100644 index 00000000..bf0abd1c --- /dev/null +++ b/system/database/drivers/sqlsrv/sqlsrv_result.php @@ -0,0 +1,169 @@ +result_id); + } + + // -------------------------------------------------------------------- + + /** + * Number of fields in the result set + * + * @access public + * @return integer + */ + function num_fields() + { + return @sqlsrv_num_fields($this->result_id); + } + + // -------------------------------------------------------------------- + + /** + * Fetch Field Names + * + * Generates an array of column names + * + * @access public + * @return array + */ + function list_fields() + { + $field_names = array(); + foreach(sqlsrv_field_metadata($this->result_id) as $offset => $field) + { + $field_names[] = $field['Name']; + } + + return $field_names; + } + + // -------------------------------------------------------------------- + + /** + * Field data + * + * Generates an array of objects containing field meta-data + * + * @access public + * @return array + */ + function field_data() + { + $retval = array(); + foreach(sqlsrv_field_metadata($this->result_id) as $offset => $field) + { + $F = new stdClass(); + $F->name = $field['Name']; + $F->type = $field['Type']; + $F->max_length = $field['Size']; + $F->primary_key = 0; + $F->default = ''; + + $retval[] = $F; + } + + return $retval; + } + + // -------------------------------------------------------------------- + + /** + * Free the result + * + * @return null + */ + function free_result() + { + if (is_resource($this->result_id)) + { + sqlsrv_free_stmt($this->result_id); + $this->result_id = FALSE; + } + } + + // -------------------------------------------------------------------- + + /** + * Data Seek + * + * Moves the internal pointer to the desired offset. We call + * this internally before fetching results to make sure the + * result set starts at zero + * + * @access private + * @return array + */ + function _data_seek($n = 0) + { + // Not implemented + } + + // -------------------------------------------------------------------- + + /** + * Result - associative array + * + * Returns the result set as an array + * + * @access private + * @return array + */ + function _fetch_assoc() + { + return sqlsrv_fetch_array($this->result_id, SQLSRV_FETCH_ASSOC); + } + + // -------------------------------------------------------------------- + + /** + * Result - object + * + * Returns the result set as an object + * + * @access private + * @return object + */ + function _fetch_object() + { + return sqlsrv_fetch_object($this->result_id); + } + +} + + +/* End of file mssql_result.php */ +/* Location: ./system/database/drivers/mssql/mssql_result.php */ \ No newline at end of file diff --git a/system/database/drivers/sqlsrv/sqlsrv_utility.php b/system/database/drivers/sqlsrv/sqlsrv_utility.php new file mode 100644 index 00000000..13a1850c --- /dev/null +++ b/system/database/drivers/sqlsrv/sqlsrv_utility.php @@ -0,0 +1,88 @@ +db->display_error('db_unsuported_feature'); + } + +} + +/* End of file mssql_utility.php */ +/* Location: ./system/database/drivers/mssql/mssql_utility.php */ \ No newline at end of file diff --git a/system/database/index.html b/system/database/index.html old mode 100755 new mode 100644 diff --git a/system/fonts/index.html b/system/fonts/index.html old mode 100755 new mode 100644 diff --git a/system/fonts/texb.ttf b/system/fonts/texb.ttf old mode 100755 new mode 100644 diff --git a/system/helpers/array_helper.php b/system/helpers/array_helper.php old mode 100755 new mode 100644 diff --git a/system/helpers/captcha_helper.php b/system/helpers/captcha_helper.php old mode 100755 new mode 100644 diff --git a/system/helpers/cookie_helper.php b/system/helpers/cookie_helper.php old mode 100755 new mode 100644 diff --git a/system/helpers/date_helper.php b/system/helpers/date_helper.php old mode 100755 new mode 100644 index 553e8d7e..0aeb7faf --- a/system/helpers/date_helper.php +++ b/system/helpers/date_helper.php @@ -116,7 +116,7 @@ function standard_date($fmt = 'DATE_RFC822', $time = '') 'DATE_COOKIE' => '%l, %d-%M-%y %H:%i:%s UTC', 'DATE_ISO8601' => '%Y-%m-%dT%H:%i:%s%Q', 'DATE_RFC822' => '%D, %d %M %y %H:%i:%s %O', - 'DATE_RFC850' => '%l, %d-%M-%y %H:%m:%i UTC', + 'DATE_RFC850' => '%l, %d-%M-%y %H:%i:%s UTC', 'DATE_RFC1036' => '%D, %d %M %y %H:%i:%s %O', 'DATE_RFC1123' => '%D, %d %M %Y %H:%i:%s %O', 'DATE_RSS' => '%D, %d %M %Y %H:%i:%s %O', diff --git a/system/helpers/directory_helper.php b/system/helpers/directory_helper.php old mode 100755 new mode 100644 diff --git a/system/helpers/download_helper.php b/system/helpers/download_helper.php old mode 100755 new mode 100644 diff --git a/system/helpers/email_helper.php b/system/helpers/email_helper.php old mode 100755 new mode 100644 diff --git a/system/helpers/file_helper.php b/system/helpers/file_helper.php old mode 100755 new mode 100644 diff --git a/system/helpers/form_helper.php b/system/helpers/form_helper.php old mode 100755 new mode 100644 index 2925d3c7..d9305c00 --- a/system/helpers/form_helper.php +++ b/system/helpers/form_helper.php @@ -64,8 +64,8 @@ function form_open($action = '', $attributes = '', $hidden = array()) $form .= '>'; - // CSRF - if ($CI->config->item('csrf_protection') === TRUE) + // Add CSRF field if enabled, but leave it out for GET requests and requests to external websites + if ($CI->config->item('csrf_protection') === TRUE AND ! (strpos($action, $CI->config->site_url()) === FALSE OR strpos($form, 'method="get"'))) { $hidden[$CI->security->get_csrf_token_name()] = $CI->security->get_csrf_hash(); } @@ -94,7 +94,7 @@ function form_open($action = '', $attributes = '', $hidden = array()) */ if ( ! function_exists('form_open_multipart')) { - function form_open_multipart($action, $attributes = array(), $hidden = array()) + function form_open_multipart($action = '', $attributes = array(), $hidden = array()) { if (is_string($attributes)) { @@ -249,7 +249,7 @@ function form_upload($data = '', $value = '', $extra = '') { function form_textarea($data = '', $value = '', $extra = '') { - $defaults = array('name' => (( ! is_array($data)) ? $data : ''), 'cols' => '90', 'rows' => '12'); + $defaults = array('name' => (( ! is_array($data)) ? $data : ''), 'cols' => '40', 'rows' => '10'); if ( ! is_array($data) OR ! isset($data['value'])) { diff --git a/system/helpers/html_helper.php b/system/helpers/html_helper.php old mode 100755 new mode 100644 diff --git a/system/helpers/index.html b/system/helpers/index.html old mode 100755 new mode 100644 diff --git a/system/helpers/inflector_helper.php b/system/helpers/inflector_helper.php old mode 100755 new mode 100644 diff --git a/system/helpers/language_helper.php b/system/helpers/language_helper.php old mode 100755 new mode 100644 diff --git a/system/helpers/number_helper.php b/system/helpers/number_helper.php old mode 100755 new mode 100644 diff --git a/system/helpers/path_helper.php b/system/helpers/path_helper.php old mode 100755 new mode 100644 diff --git a/system/helpers/security_helper.php b/system/helpers/security_helper.php old mode 100755 new mode 100644 diff --git a/system/helpers/smiley_helper.php b/system/helpers/smiley_helper.php old mode 100755 new mode 100644 diff --git a/system/helpers/string_helper.php b/system/helpers/string_helper.php old mode 100755 new mode 100644 index 7765bba3..9fa69f46 --- a/system/helpers/string_helper.php +++ b/system/helpers/string_helper.php @@ -242,6 +242,23 @@ function random_string($type = 'alnum', $len = 8) // ------------------------------------------------------------------------ +/** + * Add's _1 to a string or increment the ending number to allow _2, _3, etc + * + * @param string $str required + * @param string $separator What should the duplicate number be appended with + * @param string $first Which number should be used for the first dupe increment + * @return string + */ +function increment_string($str, $separator = '_', $first = 1) +{ + preg_match('/(.+)'.$separator.'([0-9]+)$/', $str, $match); + + return isset($match[2]) ? $match[1].$separator.($match[2] + 1) : $str.$separator.$first; +} + +// ------------------------------------------------------------------------ + /** * Alternator * diff --git a/system/helpers/text_helper.php b/system/helpers/text_helper.php old mode 100755 new mode 100644 diff --git a/system/helpers/typography_helper.php b/system/helpers/typography_helper.php old mode 100755 new mode 100644 diff --git a/system/helpers/url_helper.php b/system/helpers/url_helper.php old mode 100755 new mode 100644 diff --git a/system/helpers/xml_helper.php b/system/helpers/xml_helper.php old mode 100755 new mode 100644 diff --git a/system/index.html b/system/index.html old mode 100755 new mode 100644 diff --git a/system/language/english/calendar_lang.php b/system/language/english/calendar_lang.php old mode 100755 new mode 100644 diff --git a/system/language/english/date_lang.php b/system/language/english/date_lang.php old mode 100755 new mode 100644 diff --git a/system/language/english/db_lang.php b/system/language/english/db_lang.php old mode 100755 new mode 100644 diff --git a/system/language/english/email_lang.php b/system/language/english/email_lang.php old mode 100755 new mode 100644 diff --git a/system/language/english/form_validation_lang.php b/system/language/english/form_validation_lang.php old mode 100755 new mode 100644 index 3f240900..3418f29a --- a/system/language/english/form_validation_lang.php +++ b/system/language/english/form_validation_lang.php @@ -17,6 +17,7 @@ $lang['integer'] = "The %s field must contain an integer."; $lang['regex_match'] = "The %s field is not in the correct format."; $lang['matches'] = "The %s field does not match the %s field."; +$lang['is_unique'] = "The %s field must contain a unique value."; $lang['is_natural'] = "The %s field must contain only positive numbers."; $lang['is_natural_no_zero'] = "The %s field must contain a number greater than zero."; $lang['decimal'] = "The %s field must contain a decimal number."; diff --git a/system/language/english/ftp_lang.php b/system/language/english/ftp_lang.php old mode 100755 new mode 100644 diff --git a/system/language/english/imglib_lang.php b/system/language/english/imglib_lang.php old mode 100755 new mode 100644 diff --git a/system/language/english/index.html b/system/language/english/index.html old mode 100755 new mode 100644 diff --git a/system/language/english/migration_lang.php b/system/language/english/migration_lang.php new file mode 100644 index 00000000..4763ca24 --- /dev/null +++ b/system/language/english/migration_lang.php @@ -0,0 +1,13 @@ +_memcached->add($id, array($data, time(), $ttl), $ttl); + if (get_class($this->_memcached) == 'Memcached') + { + return $this->_memcached->set($id, array($data, time(), $ttl), $ttl); + } + else if (get_class($this->_memcached) == 'Memcache') + { + return $this->_memcached->set($id, array($data, time(), $ttl), 0, $ttl); + } + + return FALSE; } // ------------------------------------------------------------------------ diff --git a/system/libraries/Calendar.php b/system/libraries/Calendar.php old mode 100755 new mode 100644 diff --git a/system/libraries/Cart.php b/system/libraries/Cart.php old mode 100755 new mode 100644 index b2eaa9ad..ab5a70c9 --- a/system/libraries/Cart.php +++ b/system/libraries/Cart.php @@ -99,7 +99,7 @@ function insert($items = array()) $save_cart = FALSE; if (isset($items['id'])) { - if ($this->_insert($items) == TRUE) + if (($rowid = $this->_insert($items))) { $save_cart = TRUE; } @@ -110,7 +110,7 @@ function insert($items = array()) { if (is_array($val) AND isset($val['id'])) { - if ($this->_insert($val) == TRUE) + if ($this->_insert($val)) { $save_cart = TRUE; } @@ -122,7 +122,7 @@ function insert($items = array()) if ($save_cart == TRUE) { $this->_save_cart(); - return TRUE; + return isset($rowid) ? $rowid : TRUE; } return FALSE; @@ -244,7 +244,7 @@ function _insert($items = array()) } // Woot! - return TRUE; + return $rowid; } // -------------------------------------------------------------------- diff --git a/system/libraries/Driver.php b/system/libraries/Driver.php old mode 100755 new mode 100644 index c32f2c1e..9881c1ee --- a/system/libraries/Driver.php +++ b/system/libraries/Driver.php @@ -54,7 +54,7 @@ function __get($child) if ( ! class_exists($child_class)) { // check application path first - foreach (array(APPPATH, BASEPATH) as $path) + foreach (get_instance()->load->get_package_paths(TRUE) as $path) { // loves me some nesting! foreach (array(ucfirst($driver_name), $driver_name) as $class) @@ -226,4 +226,4 @@ public function __set($var, $val) // END CI_Driver CLASS /* End of file Driver.php */ -/* Location: ./system/libraries/Driver.php */ +/* Location: ./system/libraries/Driver.php */ \ No newline at end of file diff --git a/system/libraries/Email.php b/system/libraries/Email.php old mode 100755 new mode 100644 index 03eccea0..9ec40af9 --- a/system/libraries/Email.php +++ b/system/libraries/Email.php @@ -36,6 +36,7 @@ class CI_Email { var $smtp_pass = ""; // SMTP Password var $smtp_port = "25"; // SMTP Port var $smtp_timeout = 5; // SMTP Timeout in seconds + var $smtp_crypto = ""; // SMTP Encryption. Can be null, tls or ssl. var $wordwrap = TRUE; // TRUE/FALSE Turns word-wrap on/off var $wrapchars = "76"; // Number of characters to wrap at. var $mailtype = "text"; // text/html Defines email formatting @@ -379,7 +380,19 @@ public function subject($subject) */ public function message($body) { - $this->_body = stripslashes(rtrim(str_replace("\r", "", $body))); + $this->_body = rtrim(str_replace("\r", "", $body)); + + /* strip slashes only if magic quotes is ON + if we do it with magic quotes OFF, it strips real, user-inputted chars. + + NOTE: In PHP 5.4 get_magic_quotes_gpc() will always return 0 and + it will probably not exist in future versions at all. + */ + if ( ! is_php('5.4') && get_magic_quotes_gpc()) + { + $this->_body = stripslashes($this->_body); + } + return $this; } @@ -405,12 +418,12 @@ public function attach($filename, $disposition = 'attachment') /** * Add a Header Item * - * @access private + * @access protected * @param string * @param string * @return void */ - private function _set_header($header, $value) + protected function _set_header($header, $value) { $this->_headers[$header] = $value; } @@ -420,11 +433,11 @@ private function _set_header($header, $value) /** * Convert a String to an Array * - * @access private + * @access protected * @param string * @return array */ - private function _str_to_array($email) + protected function _str_to_array($email) { if ( ! is_array($email)) { @@ -452,7 +465,7 @@ private function _str_to_array($email) */ public function set_alt_message($str = '') { - $this->alt_message = ($str == '') ? '' : $str; + $this->alt_message = $str; return $this; } @@ -577,10 +590,10 @@ public function set_crlf($crlf = "\n") /** * Set Message Boundary * - * @access private + * @access protected * @return void */ - private function _set_boundaries() + protected function _set_boundaries() { $this->_alt_boundary = "B_ALT_".uniqid(''); // multipart/alternative $this->_atc_boundary = "B_ATC_".uniqid(''); // attachment boundary @@ -591,10 +604,10 @@ private function _set_boundaries() /** * Get the Message ID * - * @access private + * @access protected * @return string */ - private function _get_message_id() + protected function _get_message_id() { $from = $this->_headers['Return-Path']; $from = str_replace(">", "", $from); @@ -608,11 +621,11 @@ private function _get_message_id() /** * Get Mail Protocol * - * @access private + * @access protected * @param bool * @return string */ - private function _get_protocol($return = TRUE) + protected function _get_protocol($return = TRUE) { $this->protocol = strtolower($this->protocol); $this->protocol = ( ! in_array($this->protocol, $this->_protocols, TRUE)) ? 'mail' : $this->protocol; @@ -628,11 +641,11 @@ private function _get_protocol($return = TRUE) /** * Get Mail Encoding * - * @access private + * @access protected * @param bool * @return string */ - private function _get_encoding($return = TRUE) + protected function _get_encoding($return = TRUE) { $this->_encoding = ( ! in_array($this->_encoding, $this->_bit_depths)) ? '8bit' : $this->_encoding; @@ -655,10 +668,10 @@ private function _get_encoding($return = TRUE) /** * Get content type (text/html/attachment) * - * @access private + * @access protected * @return string */ - private function _get_content_type() + protected function _get_content_type() { if ($this->mailtype == 'html' && count($this->_attach_name) == 0) { @@ -683,10 +696,10 @@ private function _get_content_type() /** * Set RFC 822 Date * - * @access private + * @access protected * @return string */ - private function _set_date() + protected function _set_date() { $timezone = date("Z"); $operator = (strncmp($timezone, '-', 1) == 0) ? '-' : '+'; @@ -701,10 +714,10 @@ private function _set_date() /** * Mime message * - * @access private + * @access protected * @return string */ - private function _get_mime_message() + protected function _get_mime_message() { return "This is a multi-part message in MIME format.".$this->newline."Your email application may not support this format."; } @@ -802,10 +815,10 @@ public function clean_email($email) * If the user hasn't specified his own alternative message * it creates one by stripping the HTML * - * @access private + * @access protected * @return string */ - private function _get_alt_message() + protected function _get_alt_message() { if ($this->alt_message != "") { @@ -941,11 +954,11 @@ public function word_wrap($str, $charlim = '') /** * Build final headers * - * @access private + * @access protected * @param string * @return string */ - private function _build_headers() + protected function _build_headers() { $this->_set_header('X-Sender', $this->clean_email($this->_headers['From'])); $this->_set_header('X-Mailer', $this->useragent); @@ -959,10 +972,10 @@ private function _build_headers() /** * Write Headers as a string * - * @access private + * @access protected * @return void */ - private function _write_headers() + protected function _write_headers() { if ($this->protocol == 'mail') { @@ -994,10 +1007,10 @@ private function _write_headers() /** * Build Final Body and attachments * - * @access private + * @access protected * @return void */ - private function _build_message() + protected function _build_message() { if ($this->wordwrap === TRUE AND $this->mailtype != 'html') { @@ -1177,12 +1190,12 @@ private function _build_message() * Prepares string for Quoted-Printable Content-Transfer-Encoding * Refer to RFC 2045 http://www.ietf.org/rfc/rfc2045.txt * - * @access private + * @access protected * @param string * @param integer * @return string */ - private function _prep_quoted_printable($str, $charlim = '') + protected function _prep_quoted_printable($str, $charlim = '') { // Set the character limit // Don't allow over 76, as that will make servers and MUAs barf @@ -1275,7 +1288,7 @@ private function _prep_quoted_printable($str, $charlim = '') * @param bool // set to TRUE for processing From: headers * @return str */ - private function _prep_q_encoding($str, $from = FALSE) + protected function _prep_q_encoding($str, $from = FALSE) { $str = str_replace(array("\r", "\n"), array('', ''), $str); @@ -1440,10 +1453,10 @@ public function batch_bcc_send() /** * Unwrap special elements * - * @access private + * @access protected * @return void */ - private function _unwrap_specials() + protected function _unwrap_specials() { $this->_finalbody = preg_replace_callback("/\{unwrap\}(.*?)\{\/unwrap\}/si", array($this, '_remove_nl_callback'), $this->_finalbody); } @@ -1453,10 +1466,10 @@ private function _unwrap_specials() /** * Strip line-breaks via callback * - * @access private + * @access protected * @return string */ - private function _remove_nl_callback($matches) + protected function _remove_nl_callback($matches) { if (strpos($matches[1], "\r") !== FALSE OR strpos($matches[1], "\n") !== FALSE) { @@ -1471,10 +1484,10 @@ private function _remove_nl_callback($matches) /** * Spool mail to the mail server * - * @access private + * @access protected * @return bool */ - private function _spool_email() + protected function _spool_email() { $this->_unwrap_specials(); @@ -1516,10 +1529,10 @@ private function _spool_email() /** * Send using mail() * - * @access private + * @access protected * @return bool */ - private function _send_with_mail() + protected function _send_with_mail() { if ($this->_safe_mode == TRUE) { @@ -1553,10 +1566,10 @@ private function _send_with_mail() /** * Send using Sendmail * - * @access private + * @access protected * @return bool */ - private function _send_with_sendmail() + protected function _send_with_sendmail() { $fp = @popen($this->mailpath . " -oi -f ".$this->clean_email($this->_headers['From'])." -t", 'w'); @@ -1591,10 +1604,10 @@ private function _send_with_sendmail() /** * Send using SMTP * - * @access private + * @access protected * @return bool */ - private function _send_with_smtp() + protected function _send_with_smtp() { if ($this->smtp_host == '') { @@ -1660,13 +1673,16 @@ private function _send_with_smtp() /** * SMTP Connect * - * @access private + * @access protected * @param string * @return string */ - private function _smtp_connect() + protected function _smtp_connect() { - $this->_smtp_connect = fsockopen($this->smtp_host, + $ssl = NULL; + if ($this->smtp_crypto == 'ssl') + $ssl = 'ssl://'; + $this->_smtp_connect = fsockopen($ssl.$this->smtp_host, $this->smtp_port, $errno, $errstr, @@ -1679,6 +1695,14 @@ private function _smtp_connect() } $this->_set_error_message($this->_get_smtp_data()); + + if ($this->smtp_crypto == 'tls') + { + $this->_send_command('hello'); + $this->_send_command('starttls'); + stream_socket_enable_crypto($this->_smtp_connect, TRUE, STREAM_CRYPTO_METHOD_TLS_CLIENT); + } + return $this->_send_command('hello'); } @@ -1687,12 +1711,12 @@ private function _smtp_connect() /** * Send SMTP command * - * @access private + * @access protected * @param string * @param string * @return string */ - private function _send_command($cmd, $data = '') + protected function _send_command($cmd, $data = '') { switch ($cmd) { @@ -1705,6 +1729,12 @@ private function _send_command($cmd, $data = '') $resp = 250; break; + case 'starttls' : + + $this->_send_data('STARTTLS'); + + $resp = 220; + break; case 'from' : $this->_send_data('MAIL FROM:<'.$data.'>'); @@ -1754,10 +1784,10 @@ private function _send_command($cmd, $data = '') /** * SMTP Authenticate * - * @access private + * @access protected * @return bool */ - private function _smtp_authenticate() + protected function _smtp_authenticate() { if ( ! $this->_smtp_auth) { @@ -1808,10 +1838,10 @@ private function _smtp_authenticate() /** * Send SMTP data * - * @access private + * @access protected * @return bool */ - private function _send_data($data) + protected function _send_data($data) { if ( ! fwrite($this->_smtp_connect, $data . $this->newline)) { @@ -1829,10 +1859,10 @@ private function _send_data($data) /** * Get SMTP data * - * @access private + * @access protected * @return string */ - private function _get_smtp_data() + protected function _get_smtp_data() { $data = ""; @@ -1854,10 +1884,10 @@ private function _get_smtp_data() /** * Get Hostname * - * @access private + * @access protected * @return string */ - private function _get_hostname() + protected function _get_hostname() { return (isset($_SERVER['SERVER_NAME'])) ? $_SERVER['SERVER_NAME'] : 'localhost.localdomain'; } @@ -1867,10 +1897,10 @@ private function _get_hostname() /** * Get IP * - * @access private + * @access protected * @return string */ - private function _get_ip() + protected function _get_ip() { if ($this->_IP !== FALSE) { @@ -1933,11 +1963,11 @@ public function print_debugger() /** * Set Message * - * @access private + * @access protected * @param string * @return string */ - private function _set_error_message($msg, $val = '') + protected function _set_error_message($msg, $val = '') { $CI =& get_instance(); $CI->lang->load('email'); @@ -1957,11 +1987,11 @@ private function _set_error_message($msg, $val = '') /** * Mime Types * - * @access private + * @access protected * @param string * @return string */ - private function _mime_types($ext = "") + protected function _mime_types($ext = "") { $mimes = array( 'hqx' => 'application/mac-binhex40', 'cpt' => 'application/mac-compactpro', diff --git a/system/libraries/Encrypt.php b/system/libraries/Encrypt.php old mode 100755 new mode 100644 diff --git a/system/libraries/Form_validation.php b/system/libraries/Form_validation.php old mode 100755 new mode 100644 index 6f79a554..a34809e0 --- a/system/libraries/Form_validation.php +++ b/system/libraries/Form_validation.php @@ -26,16 +26,15 @@ */ class CI_Form_validation { - var $CI; - var $_field_data = array(); - var $_config_rules = array(); - var $_error_array = array(); - var $_error_messages = array(); - var $_error_prefix = '

'; - var $_error_suffix = '

'; - var $error_string = ''; - var $_safe_form_data = FALSE; - + protected $CI; + protected $_field_data = array(); + protected $_config_rules = array(); + protected $_error_array = array(); + protected $_error_messages = array(); + protected $_error_prefix = '

'; + protected $_error_suffix = '

'; + protected $error_string = ''; + protected $_safe_form_data = FALSE; /** * Constructor @@ -72,7 +71,7 @@ public function __construct($rules = array()) * @param string * @return void */ - function set_rules($field, $label = '', $rules = '') + public function set_rules($field, $label = '', $rules = '') { // No reason to set rules if we have no POST data if (count($_POST) == 0) @@ -163,7 +162,7 @@ function set_rules($field, $label = '', $rules = '') * @param string * @return string */ - function set_message($lang, $val = '') + public function set_message($lang, $val = '') { if ( ! is_array($lang)) { @@ -187,7 +186,7 @@ function set_message($lang, $val = '') * @param string * @return void */ - function set_error_delimiters($prefix = '

', $suffix = '

') + public function set_error_delimiters($prefix = '

', $suffix = '

') { $this->_error_prefix = $prefix; $this->_error_suffix = $suffix; @@ -206,7 +205,7 @@ function set_error_delimiters($prefix = '

', $suffix = '

') * @param string the field name * @return void */ - function error($field = '', $prefix = '', $suffix = '') + public function error($field = '', $prefix = '', $suffix = '') { if ( ! isset($this->_field_data[$field]['error']) OR $this->_field_data[$field]['error'] == '') { @@ -238,7 +237,7 @@ function error($field = '', $prefix = '', $suffix = '') * @param string * @return str */ - function error_string($prefix = '', $suffix = '') + public function error_string($prefix = '', $suffix = '') { // No errrors, validation passes! if (count($this->_error_array) === 0) @@ -279,7 +278,7 @@ function error_string($prefix = '', $suffix = '') * @access public * @return bool */ - function run($group = '') + public function run($group = '') { // Do we even have any data to process? Mm? if (count($_POST) == 0) @@ -374,7 +373,7 @@ function run($group = '') * @param integer * @return mixed */ - function _reduce_array($array, $keys, $i = 0) + protected function _reduce_array($array, $keys, $i = 0) { if (is_array($array)) { @@ -406,7 +405,7 @@ function _reduce_array($array, $keys, $i = 0) * @access private * @return null */ - function _reset_post_array() + protected function _reset_post_array() { foreach ($this->_field_data as $field => $row) { @@ -468,7 +467,7 @@ function _reset_post_array() * @param integer * @return mixed */ - function _execute($row, $rules, $postdata = NULL, $cycles = 0) + protected function _execute($row, $rules, $postdata = NULL, $cycles = 0) { // If the $_POST data is an array we will run a recursive call if (is_array($postdata)) @@ -489,7 +488,7 @@ function _execute($row, $rules, $postdata = NULL, $cycles = 0) if ( ! in_array('required', $rules) AND is_null($postdata)) { // Before we bail out, does the rule contain a callback? - if (preg_match("/(callback_\w+)/", implode(' ', $rules), $match)) + if (preg_match("/(callback_\w+(\[.*?\])?)/", implode(' ', $rules), $match)) { $callback = TRUE; $rules = (array('1' => $match[1])); @@ -695,7 +694,7 @@ function _execute($row, $rules, $postdata = NULL, $cycles = 0) * @param string the field name * @return string */ - function _translate_fieldname($fieldname) + protected function _translate_fieldname($fieldname) { // Do we need to translate the field name? // We look for the prefix lang: to determine this @@ -727,7 +726,7 @@ function _translate_fieldname($fieldname) * @param string * @return void */ - function set_value($field = '', $default = '') + public function set_value($field = '', $default = '') { if ( ! isset($this->_field_data[$field])) { @@ -757,7 +756,7 @@ function set_value($field = '', $default = '') * @param string * @return string */ - function set_select($field = '', $value = '', $default = FALSE) + public function set_select($field = '', $value = '', $default = FALSE) { if ( ! isset($this->_field_data[$field]) OR ! isset($this->_field_data[$field]['postdata'])) { @@ -801,7 +800,7 @@ function set_select($field = '', $value = '', $default = FALSE) * @param string * @return string */ - function set_radio($field = '', $value = '', $default = FALSE) + public function set_radio($field = '', $value = '', $default = FALSE) { if ( ! isset($this->_field_data[$field]) OR ! isset($this->_field_data[$field]['postdata'])) { @@ -845,7 +844,7 @@ function set_radio($field = '', $value = '', $default = FALSE) * @param string * @return string */ - function set_checkbox($field = '', $value = '', $default = FALSE) + public function set_checkbox($field = '', $value = '', $default = FALSE) { if ( ! isset($this->_field_data[$field]) OR ! isset($this->_field_data[$field]['postdata'])) { @@ -885,7 +884,7 @@ function set_checkbox($field = '', $value = '', $default = FALSE) * @param string * @return bool */ - function required($str) + public function required($str) { if ( ! is_array($str)) { @@ -907,7 +906,7 @@ function required($str) * @param regex * @return bool */ - function regex_match($str, $regex) + public function regex_match($str, $regex) { if ( ! preg_match($regex, $str)) { @@ -927,7 +926,7 @@ function regex_match($str, $regex) * @param field * @return bool */ - function matches($str, $field) + public function matches($str, $field) { if ( ! isset($_POST[$field])) { @@ -938,6 +937,24 @@ function matches($str, $field) return ($str !== $field) ? FALSE : TRUE; } + + // -------------------------------------------------------------------- + + /** + * Match one field to another + * + * @access public + * @param string + * @param field + * @return bool + */ + public function is_unique($str, $field) + { + list($table, $field)=explode('.', $field); + $query = $this->CI->db->limit(1)->get_where($table, array($field => $str)); + + return $query->num_rows() === 0; + } // -------------------------------------------------------------------- @@ -949,7 +966,7 @@ function matches($str, $field) * @param value * @return bool */ - function min_length($str, $val) + public function min_length($str, $val) { if (preg_match("/[^0-9]/", $val)) { @@ -974,7 +991,7 @@ function min_length($str, $val) * @param value * @return bool */ - function max_length($str, $val) + public function max_length($str, $val) { if (preg_match("/[^0-9]/", $val)) { @@ -999,7 +1016,7 @@ function max_length($str, $val) * @param value * @return bool */ - function exact_length($str, $val) + public function exact_length($str, $val) { if (preg_match("/[^0-9]/", $val)) { @@ -1023,7 +1040,7 @@ function exact_length($str, $val) * @param string * @return bool */ - function valid_email($str) + public function valid_email($str) { return ( ! preg_match("/^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,6}$/ix", $str)) ? FALSE : TRUE; } @@ -1037,7 +1054,7 @@ function valid_email($str) * @param string * @return bool */ - function valid_emails($str) + public function valid_emails($str) { if (strpos($str, ',') === FALSE) { @@ -1064,7 +1081,7 @@ function valid_emails($str) * @param string * @return string */ - function valid_ip($ip) + public function valid_ip($ip) { return $this->CI->input->valid_ip($ip); } @@ -1078,7 +1095,7 @@ function valid_ip($ip) * @param string * @return bool */ - function alpha($str) + public function alpha($str) { return ( ! preg_match("/^([a-z])+$/i", $str)) ? FALSE : TRUE; } @@ -1092,7 +1109,7 @@ function alpha($str) * @param string * @return bool */ - function alpha_numeric($str) + public function alpha_numeric($str) { return ( ! preg_match("/^([a-z0-9])+$/i", $str)) ? FALSE : TRUE; } @@ -1106,7 +1123,7 @@ function alpha_numeric($str) * @param string * @return bool */ - function alpha_dash($str) + public function alpha_dash($str) { return ( ! preg_match("/^([-a-z0-9_-])+$/i", $str)) ? FALSE : TRUE; } @@ -1120,7 +1137,7 @@ function alpha_dash($str) * @param string * @return bool */ - function numeric($str) + public function numeric($str) { return (bool)preg_match( '/^[\-+]?[0-9]*\.?[0-9]+$/', $str); @@ -1135,7 +1152,7 @@ function numeric($str) * @param string * @return bool */ - function is_numeric($str) + public function is_numeric($str) { return ( ! is_numeric($str)) ? FALSE : TRUE; } @@ -1149,7 +1166,7 @@ function is_numeric($str) * @param string * @return bool */ - function integer($str) + public function integer($str) { return (bool) preg_match('/^[\-+]?[0-9]+$/', $str); } @@ -1163,7 +1180,7 @@ function integer($str) * @param string * @return bool */ - function decimal($str) + public function decimal($str) { return (bool) preg_match('/^[\-+]?[0-9]+\.[0-9]+$/', $str); } @@ -1177,7 +1194,7 @@ function decimal($str) * @param string * @return bool */ - function greater_than($str, $min) + public function greater_than($str, $min) { if ( ! is_numeric($str)) { @@ -1195,7 +1212,7 @@ function greater_than($str, $min) * @param string * @return bool */ - function less_than($str, $max) + public function less_than($str, $max) { if ( ! is_numeric($str)) { @@ -1213,7 +1230,7 @@ function less_than($str, $max) * @param string * @return bool */ - function is_natural($str) + public function is_natural($str) { return (bool) preg_match( '/^[0-9]+$/', $str); } @@ -1227,7 +1244,7 @@ function is_natural($str) * @param string * @return bool */ - function is_natural_no_zero($str) + public function is_natural_no_zero($str) { if ( ! preg_match( '/^[0-9]+$/', $str)) { @@ -1254,7 +1271,7 @@ function is_natural_no_zero($str) * @param string * @return bool */ - function valid_base64($str) + public function valid_base64($str) { return (bool) ! preg_match('/[^a-zA-Z0-9\/\+=]/', $str); } @@ -1271,7 +1288,7 @@ function valid_base64($str) * @param string * @return string */ - function prep_for_form($data = '') + public function prep_for_form($data = '') { if (is_array($data)) { @@ -1300,7 +1317,7 @@ function prep_for_form($data = '') * @param string * @return string */ - function prep_url(http://github.com/$str%20=%20'') + public function prep_url(http://github.com/$str%20=%20'') { if ($str == 'http://' OR $str == '') { @@ -1324,7 +1341,7 @@ function prep_url(http://github.com/$str%20=%20'') * @param string * @return string */ - function strip_image_tags($str) + public function strip_image_tags($str) { return $this->CI->input->strip_image_tags($str); } @@ -1338,7 +1355,7 @@ function strip_image_tags($str) * @param string * @return string */ - function xss_clean($str) + public function xss_clean($str) { return $this->CI->security->xss_clean($str); } @@ -1352,7 +1369,7 @@ function xss_clean($str) * @param string * @return string */ - function encode_php_tags($str) + public function encode_php_tags($str) { return str_replace(array(''), array('<?php', '<?PHP', '<?', '?>'), $str); } diff --git a/system/libraries/Ftp.php b/system/libraries/Ftp.php old mode 100755 new mode 100644 diff --git a/system/libraries/Image_lib.php b/system/libraries/Image_lib.php old mode 100755 new mode 100644 diff --git a/system/libraries/Javascript.php b/system/libraries/Javascript.php old mode 100755 new mode 100644 diff --git a/system/libraries/Log.php b/system/libraries/Log.php old mode 100755 new mode 100644 diff --git a/system/libraries/Migration.php b/system/libraries/Migration.php new file mode 100644 index 00000000..3943ec13 --- /dev/null +++ b/system/libraries/Migration.php @@ -0,0 +1,338 @@ + $val) + { + $this->{'_' . $key} = $val; + } + + log_message('debug', 'Migrations class initialized'); + + // Are they trying to use migrations while it is disabled? + if ($this->_migration_enabled !== TRUE) + { + show_error('Migrations has been loaded but is disabled or set up incorrectly.'); + } + + // If not set, set it + $this->_migration_path == '' OR $this->_migration_path = APPPATH . 'migrations/'; + + // Add trailing slash if not set + $this->_migration_path = rtrim($this->_migration_path, '/').'/'; + + // Load migration language + $this->lang->load('migration'); + + // They'll probably be using dbforge + $this->load->dbforge(); + + // If the migrations table is missing, make it + if ( ! $this->db->table_exists('migrations')) + { + $this->dbforge->add_field(array( + 'version' => array('type' => 'INT', 'constraint' => 3), + )); + + $this->dbforge->create_table('migrations', TRUE); + + $this->db->insert('migrations', array('version' => 0)); + } + } + + // -------------------------------------------------------------------- + + /** + * Migrate to a schema version + * + * Calls each migration step required to get to the schema version of + * choice + * + * @access public + * @param $version integer Target schema version + * @return mixed TRUE if already latest, FALSE if failed, int if upgraded + */ + public function version($target_version) + { + $start = $current_version = $this->_get_version(); + $stop = $target_version; + + if ($target_version > $current_version) + { + // Moving Up + ++$start; + ++$stop; + $step = 1; + } + + else + { + // Moving Down + $step = -1; + } + + $method = $step === 1 ? 'up' : 'down'; + $migrations = array(); + + // We now prepare to actually DO the migrations + // But first let's make sure that everything is the way it should be + for ($i = $start; $i != $stop; $i += $step) + { + $f = glob(sprintf($this->_migration_path . '%03d_*.php', $i)); + + // Only one migration per step is permitted + if (count($f) > 1) + { + $this->_error_string = sprintf($this->lang->line('migration_multiple_version'), $i); + return FALSE; + } + + // Migration step not found + if (count($f) == 0) + { + // If trying to migrate up to a version greater than the last + // existing one, migrate to the last one. + if ($step == 1) + { + break; + } + + // If trying to migrate down but we're missing a step, + // something must definitely be wrong. + $this->_error_string = sprintf($this->lang->line('migration_not_found'), $i); + return FALSE; + } + + $file = basename($f[0]); + $name = basename($f[0], '.php'); + + // Filename validations + if (preg_match('/^\d{3}_(\w+)$/', $name, $match)) + { + $match[1] = strtolower($match[1]); + + // Cannot repeat a migration at different steps + if (in_array($match[1], $migrations)) + { + $this->_error_string = sprintf($this->lang->line('migration_multiple_version'), $match[1]); + return FALSE; + } + + include $f[0]; + $class = 'Migration_' . ucfirst($match[1]); + + if ( ! class_exists($class)) + { + $this->_error_string = sprintf($this->lang->line('migration_class_doesnt_exist'), $class); + return FALSE; + } + + if ( ! is_callable(array($class, $method))) + { + $this->_error_string = sprintf($this->lang->line('migration_missing_'.$method.'_method'), $class); + return FALSE; + } + + $migrations[] = $match[1]; + } + else + { + $this->_error_string = sprintf($this->lang->line('migration_invalid_filename'), $file); + return FALSE; + } + } + + log_message('debug', 'Current migration: ' . $current_version); + + $version = $i + ($step == 1 ? -1 : 0); + + // If there is nothing to do so quit + if ($migrations === array()) + { + return TRUE; + } + + log_message('debug', 'Migrating from ' . $method . ' to version ' . $version); + + // Loop through the migrations + foreach ($migrations AS $migration) + { + // Run the migration class + $class = 'Migration_' . ucfirst(strtolower($migration)); + call_user_func(array(new $class, $method)); + + $current_version += $step; + $this->_update_version($current_version); + } + + log_message('debug', 'Finished migrating to '.$current_version); + + return $current_version; + } + + // -------------------------------------------------------------------- + + /** + * Set's the schema to the latest migration + * + * @access public + * @return mixed true if already latest, false if failed, int if upgraded + */ + public function latest() + { + if ( ! $migrations = $this->find_migrations()) + { + $this->_error_string = $this->line->lang('migration_none_found'); + 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)); + } + + // -------------------------------------------------------------------- + + /** + * Set's the schema to the migration version set in config + * + * @access public + * @return mixed true if already current, false if failed, int if upgraded + */ + public function current() + { + return $this->version($this->_migration_version); + } + + // -------------------------------------------------------------------- + + /** + * Error string + * + * @access public + * @return string Error message returned as a string + */ + public function error_string() + { + return $this->_error_string; + } + + // -------------------------------------------------------------------- + + /** + * Set's the schema to the latest migration + * + * @access protected + * @return mixed true if already latest, false if failed, int if upgraded + */ + protected function find_migrations() + { + // Load all *_*.php files in the migrations path + $files = glob($this->_migration_path . '*_*.php'); + $file_count = count($files); + + for ($i = 0; $i < $file_count; $i++) + { + // Mark wrongly formatted files as false for later filtering + $name = basename($files[$i], '.php'); + if ( ! preg_match('/^\d{3}_(\w+)$/', $name)) + { + $files[$i] = FALSE; + } + } + + sort($files); + + return $files; + } + + // -------------------------------------------------------------------- + + /** + * Retrieves current schema version + * + * @access protected + * @return integer Current Migration + */ + protected function _get_version() + { + $row = $this->db->get('migrations')->row(); + return $row ? $row->version : 0; + } + + // -------------------------------------------------------------------- + + /** + * Stores the current schema version + * + * @access protected + * @param $migrations integer Migration reached + * @return void Outputs a report of the migration + */ + protected function _update_version($migrations) + { + return $this->db->update('migrations', array( + 'version' => $migrations + )); + } + + // -------------------------------------------------------------------- + + /** + * Enable the use of CI super-global + * + * @access public + * @param $var + * @return mixed + */ + public function __get($var) + { + return get_instance()->$var; + } +} + +/* End of file Migration.php */ +/* Location: ./system/libraries/Migration.php */ \ No newline at end of file diff --git a/system/libraries/Pagination.php b/system/libraries/Pagination.php old mode 100755 new mode 100644 index cc62e660..cdaacf2d --- a/system/libraries/Pagination.php +++ b/system/libraries/Pagination.php @@ -34,6 +34,7 @@ class CI_Pagination { var $per_page = 10; // Max number of items you want shown per page var $num_links = 2; // Number of "digit" links to show before/after the currently viewed page var $cur_page = 0; // The current page being viewed + var $use_page_numbers = FALSE; // Use page number for segment instead of offset var $first_link = '‹ First'; var $next_link = '>'; var $prev_link = '<'; @@ -128,12 +129,22 @@ function create_links() return ''; } + // Set the base page index for starting page number + if ($this->use_page_numbers) + { + $base_page = 1; + } + else + { + $base_page = 0; + } + // Determine the current page number. $CI =& get_instance(); if ($CI->config->item('enable_query_strings') === TRUE OR $this->page_query_string === TRUE) { - if ($CI->input->get($this->query_string_segment) != 0) + if ($CI->input->get($this->query_string_segment) != $base_page) { $this->cur_page = $CI->input->get($this->query_string_segment); @@ -143,7 +154,7 @@ function create_links() } else { - if ($CI->uri->segment($this->uri_segment) != 0) + if ($CI->uri->segment($this->uri_segment) != $base_page) { $this->cur_page = $CI->uri->segment($this->uri_segment); @@ -151,6 +162,12 @@ function create_links() $this->cur_page = (int) $this->cur_page; } } + + // Set current page to 1 if using page numbers instead of offset + if ($this->use_page_numbers AND $this->cur_page == 0) + { + $this->cur_page = $base_page; + } $this->num_links = (int)$this->num_links; @@ -161,18 +178,32 @@ function create_links() if ( ! is_numeric($this->cur_page)) { - $this->cur_page = 0; + $this->cur_page = $base_page; } // Is the page number beyond the result range? // If so we show the last page - if ($this->cur_page > $this->total_rows) + if ($this->use_page_numbers) { - $this->cur_page = ($num_pages - 1) * $this->per_page; + if ($this->cur_page > $num_pages) + { + $this->cur_page = $num_pages; + } + } + else + { + if ($this->cur_page > $this->total_rows) + { + $this->cur_page = ($num_pages - 1) * $this->per_page; + } } $uri_page_number = $this->cur_page; - $this->cur_page = floor(($this->cur_page/$this->per_page) + 1); + + if ( ! $this->use_page_numbers) + { + $this->cur_page = floor(($this->cur_page/$this->per_page) + 1); + } // Calculate the start and end numbers. These determine // which number to start and end the digit links with @@ -203,7 +234,14 @@ function create_links() // Render the "previous" link if ($this->prev_link !== FALSE AND $this->cur_page != 1) { - $i = $uri_page_number - $this->per_page; + if ($this->use_page_numbers) + { + $i = $uri_page_number - 1; + } + else + { + $i = $uri_page_number - $this->per_page; + } if ($i == 0 && $this->first_url != '') { @@ -223,9 +261,16 @@ function create_links() // Write the digit links for ($loop = $start -1; $loop <= $end; $loop++) { - $i = ($loop * $this->per_page) - $this->per_page; + if ($this->use_page_numbers) + { + $i = $loop; + } + else + { + $i = ($loop * $this->per_page) - $this->per_page; + } - if ($i >= 0) + if ($i >= $base_page) { if ($this->cur_page == $loop) { @@ -233,7 +278,7 @@ function create_links() } else { - $n = ($i == 0) ? '' : $i; + $n = ($i == $base_page) ? '' : $i; if ($n == '' && $this->first_url != '') { @@ -253,13 +298,29 @@ function create_links() // Render the "next" link if ($this->next_link !== FALSE AND $this->cur_page < $num_pages) { - $output .= $this->next_tag_open.'
anchor_class.'href="'.$this->base_url.$this->prefix.($this->cur_page * $this->per_page).$this->suffix.'">'.$this->next_link.''.$this->next_tag_close; + if ($this->use_page_numbers) + { + $i = $this->cur_page + 1; + } + else + { + $i = ($this->cur_page * $this->per_page); + } + + $output .= $this->next_tag_open.'anchor_class.'href="'.$this->base_url.$this->prefix.$i.$this->suffix.'">'.$this->next_link.''.$this->next_tag_close; } // Render the "Last" link if ($this->last_link !== FALSE AND ($this->cur_page + $this->num_links) < $num_pages) { - $i = (($num_pages * $this->per_page) - $this->per_page); + if ($this->use_page_numbers) + { + $i = $num_pages; + } + else + { + $i = (($num_pages * $this->per_page) - $this->per_page); + } $output .= $this->last_tag_open.'anchor_class.'href="'.$this->base_url.$this->prefix.$i.$this->suffix.'">'.$this->last_link.''.$this->last_tag_close; } diff --git a/system/libraries/Parser.php b/system/libraries/Parser.php old mode 100755 new mode 100644 diff --git a/system/libraries/Profiler.php b/system/libraries/Profiler.php old mode 100755 new mode 100644 diff --git a/system/libraries/Session.php b/system/libraries/Session.php old mode 100755 new mode 100644 index 2c8a8016..8ee08c5b --- a/system/libraries/Session.php +++ b/system/libraries/Session.php @@ -317,7 +317,8 @@ function sess_create() '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 + 'last_activity' => $this->now, + 'user_data' => '' ); diff --git a/system/libraries/Sha1.php b/system/libraries/Sha1.php old mode 100755 new mode 100644 diff --git a/system/libraries/Table.php b/system/libraries/Table.php old mode 100755 new mode 100644 diff --git a/system/libraries/Trackback.php b/system/libraries/Trackback.php old mode 100755 new mode 100644 diff --git a/system/libraries/Typography.php b/system/libraries/Typography.php old mode 100755 new mode 100644 diff --git a/system/libraries/Unit_test.php b/system/libraries/Unit_test.php old mode 100755 new mode 100644 diff --git a/system/libraries/Upload.php b/system/libraries/Upload.php old mode 100755 new mode 100644 index 3177424c..05511b5d --- a/system/libraries/Upload.php +++ b/system/libraries/Upload.php @@ -196,7 +196,8 @@ public function do_upload($field = 'userfile') // Set the uploaded data as class variables $this->file_temp = $_FILES[$field]['tmp_name']; $this->file_size = $_FILES[$field]['size']; - $this->file_type = preg_replace("/^(.+?);.*$/", "\\1", $_FILES[$field]['type']); + $this->_file_mime_type($_FILES[$field]); + $this->file_type = preg_replace("/^(.+?);.*$/", "\\1", $this->file_type); $this->file_type = strtolower(trim(stripslashes($this->file_type), '"')); $this->file_name = $this->_prep_filename($_FILES[$field]['name']); $this->file_ext = $this->get_extension($this->file_name); @@ -1006,8 +1007,69 @@ protected function _prep_filename($filename) // -------------------------------------------------------------------- + /** + * File MIME type + * + * Detects the (actual) MIME type of the uploaded file, if possible. + * The input array is expected to be $_FILES[$field] + * + * @param array + * @return void + */ + protected function _file_mime_type($file) + { + // Use if the Fileinfo extension, if available (only versions above 5.3 support the FILEINFO_MIME_TYPE flag) + if ( (float) substr(phpversion(), 0, 3) >= 5.3 && function_exists('finfo_file')) + { + $finfo = new finfo(FILEINFO_MIME_TYPE); + if ($finfo !== FALSE) // This is possible, if there is no magic MIME database file found on the system + { + $file_type = $finfo->file($file['tmp_name']); + + /* According to the comments section of the PHP manual page, + * it is possible that this function returns an empty string + * for some files (e.g. if they don't exist in the magic MIME database) + */ + if (strlen($file_type) > 1) + { + $this->file_type = $file_type; + return; + } + } + } + + // Fall back to the deprecated mime_content_type(), if available + if (function_exists('mime_content_type')) + { + $this->file_type = @mime_content_type($file['tmp_name']); + return; + } + + /* This is an ugly hack, but UNIX-type systems provide a native way to detect the file type, + * which is still more secure than depending on the value of $_FILES[$field]['type']. + * + * Notes: + * - a 'W' in the substr() expression bellow, would mean that we're using Windows + * - many system admins would disable the exec() function due to security concerns, hence the function_exists() check + */ + if (DIRECTORY_SEPARATOR !== '\\' && function_exists('exec')) + { + $output = array(); + @exec('file --brief --mime-type ' . escapeshellarg($file['tmp_path']), $output, $return_code); + if ($return_code === 0 && strlen($output[0]) > 0) // A return status code != 0 would mean failed execution + { + $this->file_type = rtrim($output[0]); + return; + } + } + + $this->file_type = $file['type']; + } + + // -------------------------------------------------------------------- + } // END Upload Class /* End of file Upload.php */ -/* Location: ./system/libraries/Upload.php */ \ No newline at end of file +/* Location: ./system/libraries/Upload.php */ diff --git a/system/libraries/User_agent.php b/system/libraries/User_agent.php old mode 100755 new mode 100644 index 016102a2..0b77a7d4 --- a/system/libraries/User_agent.php +++ b/system/libraries/User_agent.php @@ -142,7 +142,7 @@ private function _compile_data() { $this->_set_platform(); - foreach (array('_set_browser', '_set_robot', '_set_mobile') as $function) + foreach (array('_set_robot', '_set_browser', '_set_mobile') as $function) { if ($this->$function() === TRUE) { diff --git a/system/libraries/Xmlrpc.php b/system/libraries/Xmlrpc.php old mode 100755 new mode 100644 index 5da6ea6a..d702e902 --- a/system/libraries/Xmlrpc.php +++ b/system/libraries/Xmlrpc.php @@ -1404,14 +1404,14 @@ function iso8601_encode($time, $utc=0) { if ($utc == 1) { - $t = strftime("%Y%m%dT%H:%M:%S", $time); + $t = strftime("%Y%m%dT%H:%i:%s", $time); } else { if (function_exists('gmstrftime')) - $t = gmstrftime("%Y%m%dT%H:%M:%S", $time); + $t = gmstrftime("%Y%m%dT%H:%i:%s", $time); else - $t = strftime("%Y%m%dT%H:%M:%S", $time - date('Z')); + $t = strftime("%Y%m%dT%H:%i:%s", $time - date('Z')); } return $t; } diff --git a/system/libraries/Xmlrpcs.php b/system/libraries/Xmlrpcs.php old mode 100755 new mode 100644 diff --git a/system/libraries/Zip.php b/system/libraries/Zip.php old mode 100755 new mode 100644 diff --git a/system/libraries/index.html b/system/libraries/index.html old mode 100755 new mode 100644 diff --git a/system/libraries/javascript/Jquery.php b/system/libraries/javascript/Jquery.php old mode 100755 new mode 100644 From 6be49b5583eb679d64bae6e82180acaefdda398d Mon Sep 17 00:00:00 2001 From: Chris Nanney Date: Thu, 19 Jan 2012 23:02:57 -0600 Subject: [PATCH 032/651] Allows method emulation to be determined via X-HTTP-Method-Override request header. --- application/libraries/REST_Controller.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index baa06fdf..4cd1455d 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -403,10 +403,18 @@ protected function _detect_output_format() protected function _detect_method() { $method = strtolower($this->input->server('REQUEST_METHOD')); + $request_headers = apache_request_headers(); - if ($this->config->item('enable_emulate_request') && $this->input->post('_method')) + if ($this->config->item('enable_emulate_request')) { - $method = strtolower($this->input->post('_method')); + if ($this->input->post('_method')) + { + $method = strtolower($this->input->post('_method')); + } + else if (isset($request_headers['X-HTTP-Method-Override'])) + { + $method = strtolower($request_headers['X-HTTP-Method-Override']); + } } if (in_array($method, array('get', 'delete', 'post', 'put'))) From 6b174b5bf87f1f7f706e6c0a214762c86ef3016c Mon Sep 17 00:00:00 2001 From: Chris Nanney Date: Thu, 19 Jan 2012 23:21:01 -0600 Subject: [PATCH 033/651] Assign request arguments to proper method container, minor formatting --- application/libraries/REST_Controller.php | 24 ++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 4cd1455d..3e160f68 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -99,6 +99,8 @@ public function __construct() if ($this->request->format and $this->request->body) { $this->request->body = $this->format->factory($this->request->body, $this->request->format)->to_array(); + // Assign payload arguments to proper method container + $this->{'_'.$this->request->method.'_args'} = $this->request->body; } // Merge both for one mega-args variable @@ -244,15 +246,15 @@ public function response($data = array(), $http_code = null) { // all the compression settings must be done before sending any headers // if php is not handling the compression by itself - if (@ini_get('zlib.output_compression') == FALSE) { - // ob_gzhandler depends on zlib - if (extension_loaded('zlib')) { - // if the client supports GZIP compression - if (isset($_SERVER['HTTP_ACCEPT_ENCODING']) AND strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE) { - ob_start('ob_gzhandler'); - } - } - } + if (@ini_get('zlib.output_compression') == FALSE) { + // ob_gzhandler depends on zlib + if (extension_loaded('zlib')) { + // if the client supports GZIP compression + if (isset($_SERVER['HTTP_ACCEPT_ENCODING']) AND strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE) { + ob_start('ob_gzhandler'); + } + } + } is_numeric($http_code) OR $http_code = 200; @@ -434,8 +436,8 @@ protected function _detect_method() protected function _detect_api_key() { - // Get the api key name variable set in the rest config file - $api_key_variable = config_item('rest_key_name'); + // Get the api key name variable set in the rest config file + $api_key_variable = config_item('rest_key_name'); // Work out the name of the SERVER entry based on config $key_name = 'HTTP_' . strtoupper(str_replace('-', '_', $api_key_variable)); From bc4b8fbafa5d1ad69dce2c3475f667b216fb0949 Mon Sep 17 00:00:00 2001 From: Mhd Zaher Ghaibeh Date: Tue, 31 Jan 2012 15:20:01 +0200 Subject: [PATCH 034/651] fix the apache_request_header function with CGI --- application/libraries/REST_Controller.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 3e160f68..93cddb94 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -405,7 +405,13 @@ protected function _detect_output_format() protected function _detect_method() { $method = strtolower($this->input->server('REQUEST_METHOD')); - $request_headers = apache_request_headers(); + + if(function_exists('apache_request_headers')) { + $request_headers = apache_request_headers(); + }else{ + $request_headers['X-HTTP-Method-Override'] = getenv('X-HTTP-Method-Override'); + } + if ($this->config->item('enable_emulate_request')) { From c9b632bcd19c0baca3e8ba376538caa02a8fbde8 Mon Sep 17 00:00:00 2001 From: Mhd Zaher Ghaibeh Date: Sat, 4 Feb 2012 06:48:54 +0200 Subject: [PATCH 035/651] fixing the _detect_method with more general way --- application/libraries/REST_Controller.php | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 93cddb94..82b82857 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -406,23 +406,16 @@ protected function _detect_method() { $method = strtolower($this->input->server('REQUEST_METHOD')); - if(function_exists('apache_request_headers')) { - $request_headers = apache_request_headers(); - }else{ - $request_headers['X-HTTP-Method-Override'] = getenv('X-HTTP-Method-Override'); - } - - if ($this->config->item('enable_emulate_request')) { if ($this->input->post('_method')) { $method = strtolower($this->input->post('_method')); } - else if (isset($request_headers['X-HTTP-Method-Override'])) - { - $method = strtolower($request_headers['X-HTTP-Method-Override']); - } + else if ($this->input->server('HTTP_X_HTTP_METHOD_OVERRIDE')) + { + $method = strtolower($this->input->server('HTTP_X_HTTP_METHOD_OVERRIDE')); + } } if (in_array($method, array('get', 'delete', 'post', 'put'))) From fe9f985646e00ef257f7bc16dbeafac9d2b73f87 Mon Sep 17 00:00:00 2001 From: mhd zaher ghaibeh Date: Sat, 4 Feb 2012 06:53:15 +0200 Subject: [PATCH 036/651] Update application/libraries/REST_Controller.php --- application/libraries/REST_Controller.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 82b82857..8fb7ca52 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -412,10 +412,10 @@ protected function _detect_method() { $method = strtolower($this->input->post('_method')); } - else if ($this->input->server('HTTP_X_HTTP_METHOD_OVERRIDE')) - { - $method = strtolower($this->input->server('HTTP_X_HTTP_METHOD_OVERRIDE')); - } + else if ($this->input->server('HTTP_X_HTTP_METHOD_OVERRIDE')) + { + $method = strtolower($this->input->server('HTTP_X_HTTP_METHOD_OVERRIDE')); + } } if (in_array($method, array('get', 'delete', 'post', 'put'))) From 29d2090f65989338e5f56a13901bf2ceee05cea6 Mon Sep 17 00:00:00 2001 From: swas Date: Mon, 13 Feb 2012 21:54:32 +0100 Subject: [PATCH 037/651] Fixed up correctly .foo extensions to work when get arguments provided --- application/libraries/REST_Controller.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 7e0d9dce..62901be7 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -293,8 +293,8 @@ protected function _detect_output_format() { $pattern = '/\.(' . implode('|', array_keys($this->_supported_formats)) . ')$/'; - // Check if a file extension is used - if (preg_match($pattern, $this->uri->uri_string(), $matches)) + // Check if a file extension is used when no get arguments provided + if (!$this->_get_args AND preg_match($pattern, $this->uri->uri_string(), $matches)) { return $matches[1]; } From f255b66216b4ddc83ca48d0b6a5080476aa75a2b Mon Sep 17 00:00:00 2001 From: Shane Pearson Date: Fri, 9 Mar 2012 02:51:08 -0600 Subject: [PATCH 038/651] Use the compress_output config item when calling ob_gzhandler and do not send content-length header when zlib.output_compression is enabled. --- application/libraries/REST_Controller.php | 45 +++++++++++++++-------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 95bb842e..4b737aac 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -20,6 +20,7 @@ class REST_Controller extends CI_Controller { protected $_delete_args = array(); protected $_args = array(); protected $_allow = TRUE; + protected $_zlib_oc = FALSE; // Determines if output compression is enabled // List all supported methods, the first will be the default format protected $_supported_formats = array( @@ -38,6 +39,8 @@ public function __construct() { parent::__construct(); + $this->_zlib_oc = @ini_get('zlib.output_compression'); + // Lets grab the config and get ready to party $this->load->config('rest'); @@ -232,28 +235,30 @@ public function _remap($object_called, $arguments) */ public function response($data = array(), $http_code = null) { + global $CFG; + // If data is empty and not code provide, error and bail if (empty($data) && $http_code === null) - { - $http_code = 404; + { + $http_code = 404; - //create the output variable here in the case of $this->response(array()); - $output = $data; - } + //create the output variable here in the case of $this->response(array()); + $output = $data; + } // Otherwise (if no data but 200 provided) or some data, carry on camping! else { - // all the compression settings must be done before sending any headers - // if php is not handling the compression by itself - if (@ini_get('zlib.output_compression') == FALSE) { - // ob_gzhandler depends on zlib - if (extension_loaded('zlib')) { - // if the client supports GZIP compression - if (isset($_SERVER['HTTP_ACCEPT_ENCODING']) AND strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE) { - ob_start('ob_gzhandler'); - } - } + // Is compression requested? + if ($CFG->item('compress_output') === TRUE && $this->_zlib_oc == FALSE) + { + if (extension_loaded('zlib')) + { + if (isset($_SERVER['HTTP_ACCEPT_ENCODING']) AND strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE) + { + ob_start('ob_gzhandler'); + } + } } is_numeric($http_code) OR $http_code = 200; @@ -285,7 +290,15 @@ public function response($data = array(), $http_code = null) header('HTTP/1.1: ' . $http_code); header('Status: ' . $http_code); - header('Content-Length: ' . strlen($output)); + + // If zlib.output_compression is enabled it will compress the output, + // but it will not modify the content-length header to compensate for + // the reduction, causing the browser to hang waiting for more data. + // We'll just skip content-length in those cases. + if ( ! $this->_zlib_oc) + { + header('Content-Length: ' . strlen($output)); + } exit($output); } From b6b9c1173583945731fdc392757cb710f6a36998 Mon Sep 17 00:00:00 2001 From: Shane Pearson Date: Fri, 9 Mar 2012 02:58:23 -0600 Subject: [PATCH 039/651] Only send content length header if there is no output compression. --- application/libraries/REST_Controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 4b737aac..49f69d3d 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -295,7 +295,7 @@ public function response($data = array(), $http_code = null) // but it will not modify the content-length header to compensate for // the reduction, causing the browser to hang waiting for more data. // We'll just skip content-length in those cases. - if ( ! $this->_zlib_oc) + if ( ! $this->_zlib_oc && ! $CFG->item('compress_output')) { header('Content-Length: ' . strlen($output)); } From d7dabe595f57b4d732a6b07ebafcc1aa43236276 Mon Sep 17 00:00:00 2001 From: Shane Pearson Date: Wed, 14 Mar 2012 10:03:50 -0500 Subject: [PATCH 040/651] Fixed a typo. --- application/libraries/REST_Controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 49f69d3d..f3f9e800 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -112,7 +112,7 @@ public function __construct() // Which format should the data be returned in? $this->response->format = $this->_detect_output_format(); - // Which format should the data be returned in? + // Which language should the data be returned in? $this->response->lang = $this->_detect_lang(); // Check if there is a specific auth type for the current class/method From 6c9b492889ee0bbbcd66914a936fe35e25ce7d33 Mon Sep 17 00:00:00 2001 From: Givemeup Date: Thu, 22 Mar 2012 09:17:45 +0100 Subject: [PATCH 041/651] Declares an instance of stdClass Severity: Warning Message: Creating default object from empty value on PHP 5.4 --- application/libraries/REST_Controller.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index f3f9e800..b6b024dc 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -45,6 +45,7 @@ public function __construct() $this->load->config('rest'); // How is this request being made? POST, DELETE, GET, PUT? + $this->request = new stdClass(); $this->request->method = $this->_detect_method(); // Set up our GET variables @@ -110,6 +111,7 @@ public function __construct() $this->_args = array_merge($this->_get_args, $this->_put_args, $this->_post_args, $this->_delete_args); // Which format should the data be returned in? + $this->response = new stdClass(); $this->response->format = $this->_detect_output_format(); // Which language should the data be returned in? From 6df5602d358296292d969fc4e24c6fa6fb7aad35 Mon Sep 17 00:00:00 2001 From: Nick Jackson Date: Thu, 12 Apr 2012 15:50:14 +0200 Subject: [PATCH 042/651] Response where method doesn't exist is now "HTTP 405 Method Not Allowed", not "HTTP 404 Not Found". --- application/libraries/REST_Controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index b6b024dc..9c77cc4f 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -192,7 +192,7 @@ public function _remap($object_called, $arguments) // Sure it exists, but can they do anything with it? if ( ! method_exists($this, $controller_method)) { - $this->response(array('status' => false, 'error' => 'Unknown method.'), 404); + $this->response(array('status' => false, 'error' => 'Unknown method.'), 405); } // Doing key related stuff? Can only do it if they have a key right? From cb08579c22e15ef5b765a91018582ae19bdf9367 Mon Sep 17 00:00:00 2001 From: Phil Sturgeon Date: Thu, 19 Apr 2012 12:33:10 +0100 Subject: [PATCH 043/651] Use InnoDB by default. --- application/config/rest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/config/rest.php b/application/config/rest.php index 956c192f..baef8e04 100644 --- a/application/config/rest.php +++ b/application/config/rest.php @@ -126,7 +126,7 @@ `ignore_limits` tinyint(1) NOT NULL DEFAULT '0', `date_created` int(11) NOT NULL, PRIMARY KEY (`id`) - ) ENGINE=MyISAM DEFAULT CHARSET=utf8; + ) ENGINE=InnoDB DEFAULT CHARSET=utf8; | */ $config['rest_enable_keys'] = FALSE; From ab9c836973a11d3015a03a7055d80532ad33ef2d Mon Sep 17 00:00:00 2001 From: Phil Sturgeon Date: Thu, 19 Apr 2012 12:33:16 +0100 Subject: [PATCH 044/651] Fixed some padding. --- application/libraries/REST_Controller.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index c10cd5c5..6c07fc12 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -170,7 +170,7 @@ public function _remap($object_called, $arguments) // Get that useless shitty key out of here if (config_item('rest_enable_keys') AND $use_key AND $this->_allow === FALSE) { - if (config_item('rest_enable_logging') AND $log_method) + if (config_item('rest_enable_logging') AND $log_method) { $this->_log_request(); } @@ -406,9 +406,8 @@ protected function _detect_method() protected function _detect_api_key() { - - // Get the api key name variable set in the rest config file - $api_key_variable = config_item('rest_key_name'); + // Get the api key name variable set in the rest config file + $api_key_variable = config_item('rest_key_name'); // Work out the name of the SERVER entry based on config $key_name = 'HTTP_' . strtoupper(str_replace('-', '_', $api_key_variable)); From 37a837fc1e750f52486f6b31b8a599daf7320739 Mon Sep 17 00:00:00 2001 From: Phil Sturgeon Date: Thu, 19 Apr 2012 12:58:07 +0100 Subject: [PATCH 045/651] Updated README for 2.6. --- README.md | 20 +- application/libraries/REST_Controller.php | 372 ++++++++++++++++------ 2 files changed, 298 insertions(+), 94 deletions(-) diff --git a/README.md b/README.md index d5345d5f..7b052b45 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,9 @@ config file and one controller. ## Requirements 1. PHP 5.2+ -2. CodeIgniter 2.0.3 to 2.1-dev (for 1.7.x support download v2.2 from Downloads tab) +2. CodeIgniter 2.1.0 to 3.0-dev + +Note: for 1.7.x support download v2.2 from Downloads tab ## Usage @@ -22,6 +24,22 @@ but you can read my NetTuts article which covers it's usage along with the REST ## Change Log +### 2.6 + +* Added loads of PHPDoc comments. +* Response where method doesn't exist is now "HTTP 405 Method Not Allowed", not "HTTP 404 Not Found". +* Compatable with PHP 5.4. +* Added support for gzip compression. +* Fix the apache\_request\_header function with CGI. +* Fixed up correctly .foo extensions to work when get arguments provided. +* Allows method emulation via X-HTTP-Method-Override +* Support for Backbone.emulateHTTP improved. +* Combine both URI segment and GET params instead of using one or the other +* Separate each piece of the WWW-Authenticate header for digest requests with a comma. +* Added IP whitelist option. + + + ### 2.5 * Instead of just seeing item, item, item, the singular version of the basenode will be used if possible. [Example](http://d.pr/RS46). diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 877fcc36..e8e7b625 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -1,28 +1,106 @@ 'application/xml', 'rawxml' => 'application/xml', @@ -34,7 +112,18 @@ class REST_Controller extends CI_Controller { 'csv' => 'application/csv' ); - // Constructor function + /** + * Developers can extend this class and add a check in here. + */ + protected function early_checks() + { + + } + + /** + * Constructor function + * @todo Document more please. + */ public function __construct() { parent::__construct(); @@ -114,14 +203,17 @@ public function __construct() $this->response = new stdClass(); $this->response->format = $this->_detect_output_format(); - // Which language should the data be returned in? + // Which format should the data be returned in? $this->response->lang = $this->_detect_lang(); + // Developers can extend this class and add a check in here + $this->early_checks(); + // Check if there is a specific auth type for the current class/method $this->auth_override = $this->_auth_override_check(); // When there is no specific override for the current class/method, use the default auth value set in the config - if ( $this->auth_override !== TRUE ) + if ($this->auth_override !== TRUE) { if ($this->config->item('rest_auth') == 'basic') { @@ -143,6 +235,12 @@ public function __construct() $this->rest->db = $this->load->database(config_item('rest_database_group'), TRUE); } + // Use whatever database is in use (isset returns false) + elseif (@$this->db) + { + $this->rest->db = $this->db; + } + // Checking for keys? GET TO WORK! if (config_item('rest_enable_keys')) { @@ -150,38 +248,42 @@ public function __construct() } // only allow ajax requests - if ( ! $this->input->is_ajax_request() AND config_item('rest_ajax_only') ) + if (!$this->input->is_ajax_request() AND config_item('rest_ajax_only')) { - $this->response( array('status' => false, 'error' => 'Only AJAX requests are accepted.'), 505 ); + $this->response(array('status' => false, 'error' => 'Only AJAX requests are accepted.'), 505); } } - /* + /** * Remap * - * Requests are not made to methods directly The request will be for an "object". - * this simply maps the object and method to the correct Controller method. + * Requests are not made to methods directly, the request will be for + * an "object". This simply maps the object and method to the correct + * Controller method. + * + * @param string $object_called + * @param array $arguments The arguments passed to the controller method. */ public function _remap($object_called, $arguments) { - $pattern = '/^(.*)\.(' . implode('|', array_keys($this->_supported_formats)) . ')$/'; + $pattern = '/^(.*)\.('.implode('|', array_keys($this->_supported_formats)).')$/'; if (preg_match($pattern, $object_called, $matches)) { $object_called = $matches[1]; } - $controller_method = $object_called . '_' . $this->request->method; + $controller_method = $object_called.'_'.$this->request->method; // Do we want to log this method (if allowed by config)? - $log_method = ! (isset($this->methods[$controller_method]['log']) AND $this->methods[$controller_method]['log'] == FALSE); + $log_method = !(isset($this->methods[$controller_method]['log']) AND $this->methods[$controller_method]['log'] == FALSE); // Use keys for this method? - $use_key = ! (isset($this->methods[$controller_method]['key']) AND $this->methods[$controller_method]['key'] == FALSE); + $use_key = !(isset($this->methods[$controller_method]['key']) AND $this->methods[$controller_method]['key'] == FALSE); // Get that useless shitty key out of here if (config_item('rest_enable_keys') AND $use_key AND $this->_allow === FALSE) { - if (config_item('rest_enable_logging') AND $log_method) + if (config_item('rest_enable_logging') AND $log_method) { $this->_log_request(); } @@ -190,16 +292,16 @@ public function _remap($object_called, $arguments) } // Sure it exists, but can they do anything with it? - if ( ! method_exists($this, $controller_method)) + if (!method_exists($this, $controller_method)) { - $this->response(array('status' => false, 'error' => 'Unknown method.'), 405); + $this->response(array('status' => false, 'error' => 'Unknown method.'), 404); } // Doing key related stuff? Can only do it if they have a key right? - if (config_item('rest_enable_keys') AND ! empty($this->rest->key)) + if (config_item('rest_enable_keys') AND !empty($this->rest->key)) { // Check the limit - if (config_item('rest_enable_limits') AND ! $this->_check_limit($controller_method)) + if (config_item('rest_enable_limits') AND !$this->_check_limit($controller_method)) { $this->response(array('status' => false, 'error' => 'This API key has reached the hourly limit for this method.'), 401); } @@ -230,10 +332,13 @@ public function _remap($object_called, $arguments) call_user_func_array(array($this, $controller_method), $arguments); } - /* - * response + /** + * Response + * + * Takes pure data and optionally a status code, then creates the response. * - * Takes pure data and optionally a status code, then creates the response + * @param array $data + * @param null|int $http_code */ public function response($data = array(), $http_code = null) { @@ -332,23 +437,25 @@ protected function _detect_input_format() return NULL; } - /* + /** * Detect format * - * Detect which format should be used to output the data + * Detect which format should be used to output the data. + * + * @return string The output format. */ protected function _detect_output_format() { - $pattern = '/\.(' . implode('|', array_keys($this->_supported_formats)) . ')$/'; + $pattern = '/\.('.implode('|', array_keys($this->_supported_formats)).')$/'; - // Check if a file extension is used when no get arguments provided - if (!$this->_get_args AND preg_match($pattern, $this->uri->uri_string(), $matches)) + // Check if a file extension is used + if (preg_match($pattern, $this->uri->uri_string(), $matches)) { return $matches[1]; } // Check if a file extension is used - elseif ($this->_get_args AND ! is_array(end($this->_get_args)) AND preg_match($pattern, end($this->_get_args), $matches)) + elseif ($this->_get_args AND !is_array(end($this->_get_args)) AND preg_match($pattern, end($this->_get_args), $matches)) { // The key of the last argument $last_key = end(array_keys($this->_get_args)); @@ -411,12 +518,13 @@ protected function _detect_output_format() return config_item('rest_default_format'); } - /* + /** * Detect method * * Detect which method (POST, PUT, GET, DELETE) is being used + * + * @return string */ - protected function _detect_method() { $method = strtolower($this->input->server('REQUEST_METHOD')); @@ -441,34 +549,37 @@ protected function _detect_method() return 'get'; } - /* + /** * Detect API Key * * See if the user has provided an API key + * + * @return boolean */ - protected function _detect_api_key() { + // Get the api key name variable set in the rest config file $api_key_variable = config_item('rest_key_name'); // Work out the name of the SERVER entry based on config - $key_name = 'HTTP_' . strtoupper(str_replace('-', '_', $api_key_variable)); + $key_name = 'HTTP_'.strtoupper(str_replace('-', '_', $api_key_variable)); $this->rest->key = NULL; $this->rest->level = NULL; $this->rest->ignore_limits = FALSE; // Find the key from server or arguments - if ($key = isset($this->_args[$api_key_variable]) ? $this->_args[$api_key_variable] : $this->input->server($key_name)) + if (($key = isset($this->_args[$api_key_variable]) ? $this->_args[$api_key_variable] : $this->input->server($key_name))) { - if ( ! $row = $this->rest->db->where('key', $key)->get(config_item('rest_keys_table'))->row()) + if (!($row = $this->rest->db->where('key', $key)->get(config_item('rest_keys_table'))->row())) { return FALSE; } $this->rest->key = $row->key; + isset($row->user_id) AND $this->rest->user_id = $row->user_id; isset($row->level) AND $this->rest->level = $row->level; isset($row->ignore_limits) AND $this->rest->ignore_limits = $row->ignore_limits; @@ -479,15 +590,16 @@ protected function _detect_api_key() return FALSE; } - /* + /** * Detect language(s) * * What language do they want it in? + * + * @return null|string The language code. */ - protected function _detect_lang() { - if ( ! $lang = $this->input->server('HTTP_ACCEPT_LANGUAGE')) + if (!$lang = $this->input->server('HTTP_ACCEPT_LANGUAGE')) { return NULL; } @@ -513,35 +625,39 @@ protected function _detect_lang() return $lang; } - /* + /** * Log request * * Record the entry for awesomeness purposes + * + * @param boolean $authorized + * @return object */ - protected function _log_request($authorized = FALSE) { return $this->rest->db->insert(config_item('rest_logs_table'), array( - 'uri' => $this->uri->uri_string(), - 'method' => $this->request->method, - 'params' => serialize($this->_args), - 'api_key' => isset($this->rest->key) ? $this->rest->key : '', - 'ip_address' => $this->input->ip_address(), - 'time' => function_exists('now') ? now() : time(), - 'authorized' => $authorized - )); + 'uri' => $this->uri->uri_string(), + 'method' => $this->request->method, + 'params' => $this->_args ? serialize($this->_args) : null, + 'api_key' => isset($this->rest->key) ? $this->rest->key : '', + 'ip_address' => $this->input->ip_address(), + 'time' => function_exists('now') ? now() : time(), + 'authorized' => $authorized + )); } - /* - * Log request + /** + * Limiting requests * - * Record the entry for awesomeness purposes + * Check if the requests are coming in a tad too fast. + * + * @param string $controller_method The method deing called. + * @return boolean */ - protected function _check_limit($controller_method) { // They are special, or it might not even have a limit - if ( ! empty($this->rest->ignore_limits) OR !isset($this->methods[$controller_method]['limit'])) + if (!empty($this->rest->ignore_limits) OR !isset($this->methods[$controller_method]['limit'])) { // On your way sonny-jim. return TRUE; @@ -552,13 +668,13 @@ protected function _check_limit($controller_method) // Get data on a keys usage $result = $this->rest->db - ->where('uri', $this->uri->uri_string()) - ->where('api_key', $this->rest->key) - ->get(config_item('rest_limits_table')) - ->row(); + ->where('uri', $this->uri->uri_string()) + ->where('api_key', $this->rest->key) + ->get(config_item('rest_limits_table')) + ->row(); // No calls yet, or been an hour since they called - if ( ! $result OR $result->hour_started < time() - (60 * 60)) + if (!$result OR $result->hour_started < time() - (60 * 60)) { // Right, set one up from scratch $this->rest->db->insert(config_item('rest_limits_table'), array( @@ -587,12 +703,15 @@ protected function _check_limit($controller_method) return TRUE; } - /* + + /** * Auth override check * - * Check if there is a specific auth type set for the current class/method being called + * Check if there is a specific auth type set for the current class/method + * being called. + * + * @return boolean */ - protected function _auth_override_check() { @@ -600,13 +719,13 @@ protected function _auth_override_check() $this->overrides_array = $this->config->item('auth_override_class_method'); // Check to see if the override array is even populated, otherwise return false - if ( empty($this->overrides_array) ) + if (empty($this->overrides_array)) { return false; } // Check to see if there's an override value set for the current class/method being called - if ( empty($this->overrides_array[$this->router->class][$this->router->method]) ) + if (empty($this->overrides_array[$this->router->class][$this->router->method])) { return false; } @@ -638,13 +757,20 @@ protected function _auth_override_check() return true; } - // Return false when there is an override value set but it doesn't match 'basic', 'digest', or 'none'. (the value was misspelled) + // Return false when there is an override value set but it does not match + // 'basic', 'digest', or 'none'. (the value was misspelled) return false; } - // INPUT FUNCTION -------------------------------------------------------------- + /** + * Retrieve a value from the GET request arguments. + * + * @param string $key The key for the GET request argument to retrieve + * @param boolean $xss_clean Whether the value should be XSS cleaned or not. + * @return string The GET argument value. + */ public function get($key = NULL, $xss_clean = TRUE) { if ($key === NULL) @@ -655,6 +781,13 @@ public function get($key = NULL, $xss_clean = TRUE) return array_key_exists($key, $this->_get_args) ? $this->_xss_clean($this->_get_args[$key], $xss_clean) : FALSE; } + /** + * Retrieve a value from the POST request arguments. + * + * @param string $key The key for the POST request argument to retrieve + * @param boolean $xss_clean Whether the value should be XSS cleaned or not. + * @return string The POST argument value. + */ public function post($key = NULL, $xss_clean = TRUE) { if ($key === NULL) @@ -665,6 +798,13 @@ public function post($key = NULL, $xss_clean = TRUE) return $this->input->post($key, $xss_clean); } + /** + * Retrieve a value from the PUT request arguments. + * + * @param string $key The key for the PUT request argument to retrieve + * @param boolean $xss_clean Whether the value should be XSS cleaned or not. + * @return string The PUT argument value. + */ public function put($key = NULL, $xss_clean = TRUE) { if ($key === NULL) @@ -675,6 +815,13 @@ public function put($key = NULL, $xss_clean = TRUE) return array_key_exists($key, $this->_put_args) ? $this->_xss_clean($this->_put_args[$key], $xss_clean) : FALSE; } + /** + * Retrieve a value from the DELETE request arguments. + * + * @param string $key The key for the DELETE request argument to retrieve + * @param boolean $xss_clean Whether the value should be XSS cleaned or not. + * @return string The DELETE argument value. + */ public function delete($key = NULL, $xss_clean = TRUE) { if ($key === NULL) @@ -685,18 +832,28 @@ public function delete($key = NULL, $xss_clean = TRUE) return array_key_exists($key, $this->_delete_args) ? $this->_xss_clean($this->_delete_args[$key], $xss_clean) : FALSE; } - protected function _xss_clean($val, $bool) + /** + * Process to protect from XSS attacks. + * + * @param string $val The input. + * @param boolean $process Do clean or note the input. + * @return string + */ + protected function _xss_clean($val, $process) { if (CI_VERSION < 2) { - return $bool ? $this->input->xss_clean($val) : $val; - } - else - { - return $bool ? $this->security->xss_clean($val) : $val; + return $process ? $this->input->xss_clean($val) : $val; } + + return $process ? $this->security->xss_clean($val) : $val; } + /** + * Retrieve the validation errors. + * + * @return array + */ public function validation_errors() { $string = strip_tags($this->form_validation->error_string()); @@ -706,6 +863,13 @@ public function validation_errors() // SECURITY FUNCTIONS --------------------------------------------------------- + /** + * Check if the user is logged in. + * + * @param string $username The user's name + * @param string $password The user's password + * @return boolean + */ protected function _check_login($username = '', $password = NULL) { if (empty($username)) @@ -729,6 +893,9 @@ protected function _check_login($username = '', $password = NULL) return TRUE; } + /** + * @todo document this. + */ protected function _prepare_basic_auth() { // If whitelist is enabled it has the first chance to kick them out @@ -762,6 +929,9 @@ protected function _prepare_basic_auth() } } + /** + * @todo Document this. + */ protected function _prepare_digest_auth() { // If whitelist is enabled it has the first chance to kick them out @@ -786,9 +956,8 @@ protected function _prepare_digest_auth() $digest_string = ""; } - /* The $_SESSION['error_prompted'] variabile is used to ask - the password again if none given or if the user enters - a wrong auth. informations. */ + // The $_SESSION['error_prompted'] variable is used to ask the password + // again if none given or if the user enters wrong auth information. if (empty($digest_string)) { $this->_force_login($uniqid); @@ -807,9 +976,9 @@ protected function _prepare_digest_auth() $valid_pass = $valid_logins[$digest['username']]; // This is the valid response expected - $A1 = md5($digest['username'] . ':' . $this->config->item('rest_realm') . ':' . $valid_pass); - $A2 = md5(strtoupper($this->request->method) . ':' . $digest['uri']); - $valid_response = md5($A1 . ':' . $digest['nonce'] . ':' . $digest['nc'] . ':' . $digest['cnonce'] . ':' . $digest['qop'] . ':' . $A2); + $A1 = md5($digest['username'].':'.$this->config->item('rest_realm').':'.$valid_pass); + $A2 = md5(strtoupper($this->request->method).':'.$digest['uri']); + $valid_response = md5($A1.':'.$digest['nonce'].':'.$digest['nc'].':'.$digest['cnonce'].':'.$digest['qop'].':'.$A2); if ($digest['response'] != $valid_response) { @@ -819,7 +988,9 @@ protected function _prepare_digest_auth() } } - // Check if the client's ip is in the 'rest_ip_whitelist' config + /** + * Check if the client's ip is in the 'rest_ip_whitelist' config + */ protected function _check_whitelist_auth() { $whitelist = explode(',', config_item('rest_ip_whitelist')); @@ -831,31 +1002,41 @@ protected function _check_whitelist_auth() $ip = trim($ip); } - if ( ! in_array($this->input->ip_address(), $whitelist)) + if (!in_array($this->input->ip_address(), $whitelist)) { $this->response(array('status' => false, 'error' => 'Not authorized'), 401); } } + /** + * @todo Document this. + * + * @param string $nonce + */ protected function _force_login($nonce = '') { if ($this->config->item('rest_auth') == 'basic') { - header('WWW-Authenticate: Basic realm="' . $this->config->item('rest_realm') . '"'); + header('WWW-Authenticate: Basic realm="'.$this->config->item('rest_realm').'"'); } elseif ($this->config->item('rest_auth') == 'digest') { - header('WWW-Authenticate: Digest realm="' . $this->config->item('rest_realm') . '", qop="auth", nonce="' . $nonce . '", opaque="' . md5($this->config->item('rest_realm')) . '"'); + header('WWW-Authenticate: Digest realm="'.$this->config->item('rest_realm').'", qop="auth", nonce="'.$nonce.'", opaque="'.md5($this->config->item('rest_realm')).'"'); } $this->response(array('status' => false, 'error' => 'Not authorized'), 401); } - // Force it into an array + /** + * Force it into an array + * + * @param object|array $data + * @return array + */ protected function _force_loopable($data) { // Force it to be something useful - if ( ! is_array($data) AND ! is_object($data)) + if (!is_array($data) AND !is_object($data)) { $data = (array) $data; } @@ -864,12 +1045,17 @@ protected function _force_loopable($data) } // FORMATING FUNCTIONS --------------------------------------------------------- - // Many of these have been moved to the Format class for better separation, but these methods will be checked too - // Encode as JSONP + /** + * Encode as JSONP + * + * @param array $data The input data. + * @return string The JSONP data string (loadable from Javascript). + */ protected function _format_jsonp($data = array()) { - return $this->get('callback') . '(' . json_encode($data) . ')'; + return $this->get('callback').'('.json_encode($data).')'; } + } \ No newline at end of file From 361cb7e0822a3bc57d6c2dc36dfcfe56fa3cd43a Mon Sep 17 00:00:00 2001 From: Phil Sturgeon Date: Thu, 19 Apr 2012 13:57:43 +0100 Subject: [PATCH 046/651] Style, docs and abstract. --- application/libraries/REST_Controller.php | 37 +++++++++++++---------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index e8e7b625..7bdcc61b 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -11,9 +11,9 @@ * @author Phil Sturgeon * @license http://philsturgeon.co.uk/code/dbad-license * @link https://github.com/philsturgeon/codeigniter-restserver - * @version 2.6 + * @version 2.6.0 */ -class REST_Controller extends MY_Controller +abstract class REST_Controller extends CI_Controller { /** * This defines the rest format. @@ -94,7 +94,13 @@ class REST_Controller extends MY_Controller * @var boolean */ protected $_allow = TRUE; - protected $_zlib_oc = FALSE; // Determines if output compression is enabled + + /** + * Determines if output compression is enabled + * + * @var boolean + */ + protected $_zlib_oc = FALSE; /** * List all supported methods, the first will be the default format @@ -103,7 +109,6 @@ class REST_Controller extends MY_Controller */ protected $_supported_formats = array( 'xml' => 'application/xml', - 'rawxml' => 'application/xml', 'json' => 'application/json', 'jsonp' => 'application/javascript', 'serialized' => 'application/vnd.php.serialized', @@ -248,7 +253,7 @@ public function __construct() } // only allow ajax requests - if (!$this->input->is_ajax_request() AND config_item('rest_ajax_only')) + if ( ! $this->input->is_ajax_request() AND config_item('rest_ajax_only')) { $this->response(array('status' => false, 'error' => 'Only AJAX requests are accepted.'), 505); } @@ -292,7 +297,7 @@ public function _remap($object_called, $arguments) } // Sure it exists, but can they do anything with it? - if (!method_exists($this, $controller_method)) + if ( ! method_exists($this, $controller_method)) { $this->response(array('status' => false, 'error' => 'Unknown method.'), 404); } @@ -558,7 +563,6 @@ protected function _detect_method() */ protected function _detect_api_key() { - // Get the api key name variable set in the rest config file $api_key_variable = config_item('rest_key_name'); @@ -567,12 +571,13 @@ protected function _detect_api_key() $this->rest->key = NULL; $this->rest->level = NULL; + $this->rest->user_id = NULL; $this->rest->ignore_limits = FALSE; // Find the key from server or arguments if (($key = isset($this->_args[$api_key_variable]) ? $this->_args[$api_key_variable] : $this->input->server($key_name))) { - if (!($row = $this->rest->db->where('key', $key)->get(config_item('rest_keys_table'))->row())) + if ( ! ($row = $this->rest->db->where('key', $key)->get(config_item('rest_keys_table'))->row())) { return FALSE; } @@ -599,7 +604,7 @@ protected function _detect_api_key() */ protected function _detect_lang() { - if (!$lang = $this->input->server('HTTP_ACCEPT_LANGUAGE')) + if ( ! $lang = $this->input->server('HTTP_ACCEPT_LANGUAGE')) { return NULL; } @@ -657,7 +662,7 @@ protected function _log_request($authorized = FALSE) protected function _check_limit($controller_method) { // They are special, or it might not even have a limit - if (!empty($this->rest->ignore_limits) OR !isset($this->methods[$controller_method]['limit'])) + if ( ! empty($this->rest->ignore_limits) OR !isset($this->methods[$controller_method]['limit'])) { // On your way sonny-jim. return TRUE; @@ -674,7 +679,7 @@ protected function _check_limit($controller_method) ->row(); // No calls yet, or been an hour since they called - if (!$result OR $result->hour_started < time() - (60 * 60)) + if ( ! $result OR $result->hour_started < time() - (60 * 60)) { // Right, set one up from scratch $this->rest->db->insert(config_item('rest_limits_table'), array( @@ -879,7 +884,7 @@ protected function _check_login($username = '', $password = NULL) $valid_logins = & $this->config->item('rest_valid_logins'); - if (!array_key_exists($username, $valid_logins)) + if ( ! array_key_exists($username, $valid_logins)) { return FALSE; } @@ -923,7 +928,7 @@ protected function _prepare_basic_auth() } } - if (!$this->_check_login($username, $password)) + if ( ! $this->_check_login($username, $password)) { $this->_force_login(); } @@ -967,7 +972,7 @@ protected function _prepare_digest_auth() preg_match_all('@(username|nonce|uri|nc|cnonce|qop|response)=[\'"]?([^\'",]+)@', $digest_string, $matches); $digest = array_combine($matches[1], $matches[2]); - if (!array_key_exists('username', $digest) OR !$this->_check_login($digest['username'])) + if ( ! array_key_exists('username', $digest) OR !$this->_check_login($digest['username'])) { $this->_force_login($uniqid); } @@ -1002,7 +1007,7 @@ protected function _check_whitelist_auth() $ip = trim($ip); } - if (!in_array($this->input->ip_address(), $whitelist)) + if ( ! in_array($this->input->ip_address(), $whitelist)) { $this->response(array('status' => false, 'error' => 'Not authorized'), 401); } @@ -1036,7 +1041,7 @@ protected function _force_login($nonce = '') protected function _force_loopable($data) { // Force it to be something useful - if (!is_array($data) AND !is_object($data)) + if ( ! is_array($data) AND !is_object($data)) { $data = (array) $data; } From 52219d8d090335e2933b32ee531417f815628724 Mon Sep 17 00:00:00 2001 From: Phil Sturgeon Date: Thu, 19 Apr 2012 13:58:35 +0100 Subject: [PATCH 047/651] Tagged 2.6.0. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7b052b45..f62ccf9c 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# CodeIgniter-RestServer +# CodeIgniter Rest Server A fully RESTful server implementation for CodeIgniter using one library, one config file and one controller. @@ -24,7 +24,7 @@ but you can read my NetTuts article which covers it's usage along with the REST ## Change Log -### 2.6 +### 2.6.0 * Added loads of PHPDoc comments. * Response where method doesn't exist is now "HTTP 405 Method Not Allowed", not "HTTP 404 Not Found". From c78babcf7d864c4145392f12b32bb6d032a69ad5 Mon Sep 17 00:00:00 2001 From: Kevin Wood-Friend Date: Thu, 7 Jun 2012 10:53:42 -0400 Subject: [PATCH 048/651] Fix for bug in Format.php's to_html() which failed to detect if $data was really a multidimensional array. Prior, array('foo', 'bar', 'baz') would be considered a multidimensional array and spit out a PHP warning. --- application/libraries/Format.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/libraries/Format.php b/application/libraries/Format.php index 79d3c4e5..6d8a7d2e 100644 --- a/application/libraries/Format.php +++ b/application/libraries/Format.php @@ -146,8 +146,8 @@ public function to_html() { $data = $this->_data; - // Multi-dimentional array - if (isset($data[0])) + // Multi-dimensional array + if (isset($data[0]) && is_array($data[0])) { $headings = array_keys($data[0]); } From eab7e35a4ca396d1c31347e87a2bcb7677564a00 Mon Sep 17 00:00:00 2001 From: Aaron Yang Date: Mon, 18 Jun 2012 17:43:11 -0700 Subject: [PATCH 049/651] Declares an instance of stdClass --- application/libraries/REST_Controller.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 7bdcc61b..8eb8d055 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -234,6 +234,7 @@ public function __construct() } } + $this->rest = new StdClass(); // Load DB if its enabled if (config_item('rest_database_group') AND (config_item('rest_enable_keys') OR config_item('rest_enable_logging'))) { @@ -1063,4 +1064,4 @@ protected function _format_jsonp($data = array()) return $this->get('callback').'('.json_encode($data).')'; } -} \ No newline at end of file +} From 4c5eabe1e4d4169bde78b34b993dd0ed9c305ff1 Mon Sep 17 00:00:00 2001 From: Kinjal Dixit Date: Tue, 19 Jun 2012 10:47:58 +0530 Subject: [PATCH 050/651] corrects post implementation --- application/libraries/REST_Controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 8eb8d055..fcac78f3 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -801,7 +801,7 @@ public function post($key = NULL, $xss_clean = TRUE) return $this->_post_args; } - return $this->input->post($key, $xss_clean); + return array_key_exists($key, $this->_post_args) ? $this->_xss_clean($this->_post_args[$key], $xss_clean) : FALSE; } /** From 7a27e3142a239f6a8ca63efc093e0fe9dcf4917f Mon Sep 17 00:00:00 2001 From: Jason Horwitz Date: Fri, 22 Jun 2012 03:01:46 -0400 Subject: [PATCH 051/651] Patched rest config SQL log table to support null param REST calls & IPv6 IP Addresses as they are now supported in CodeIgniter v2.1.1 --- application/config/rest.php | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/application/config/rest.php b/application/config/rest.php index b26504f1..c170bfd3 100644 --- a/application/config/rest.php +++ b/application/config/rest.php @@ -58,14 +58,14 @@ | | Set as many config entries as needed. Any methods not set will use the default 'rest_auth' config value. | -| example: -| +| example: +| | $config['auth_override_class_method']['deals']['view'] = 'none'; | $config['auth_override_class_method']['deals']['insert'] = 'digest'; -| $config['auth_override_class_method']['accounts']['user'] = 'basic'; +| $config['auth_override_class_method']['accounts']['user'] = 'basic'; | | Here 'deals' and 'accounts' are controller names, 'view', 'insert' and 'user' are methods within. (NOTE: leave off the '_get' or '_post' from the end of the method name) -| Acceptable values are; 'none', 'digest' and 'basic'. +| Acceptable values are; 'none', 'digest' and 'basic'. | */ // $config['auth_override_class_method']['deals']['view'] = 'none'; @@ -215,9 +215,9 @@ `id` int(11) NOT NULL AUTO_INCREMENT, `uri` varchar(255) NOT NULL, `method` varchar(6) NOT NULL, - `params` text NOT NULL, + `params` text DEFAULT NULL, `api_key` varchar(40) NOT NULL, - `ip_address` varchar(15) NOT NULL, + `ip_address` varchar(45) NOT NULL, `time` int(11) NOT NULL, `authorized` tinyint(1) NOT NULL, PRIMARY KEY (`id`) @@ -279,10 +279,10 @@ | REST AJAX Only |-------------------------------------------------------------------------- | -| Set to TRUE to only allow AJAX requests. If TRUE and the request is not -| coming from AJAX, a 505 response with the error message "Only AJAX -| requests are accepted." will be returned. This is good for production -| environments. Set to FALSE to also accept HTTP requests. +| Set to TRUE to only allow AJAX requests. If TRUE and the request is not +| coming from AJAX, a 505 response with the error message "Only AJAX +| requests are accepted." will be returned. This is good for production +| environments. Set to FALSE to also accept HTTP requests. | | FALSE | From 391512658dcbb8d6fe605d17f0690518ab6d8884 Mon Sep 17 00:00:00 2001 From: Jamie Rumbelow Date: Mon, 2 Jul 2012 15:50:20 +0100 Subject: [PATCH 052/651] The beginnings of documentation --- README.md | 82 ++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 72 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index f62ccf9c..22a9214d 100644 --- a/README.md +++ b/README.md @@ -10,17 +10,82 @@ config file and one controller. 1. PHP 5.2+ 2. CodeIgniter 2.1.0 to 3.0-dev -Note: for 1.7.x support download v2.2 from Downloads tab +_Note: for 1.7.x support download v2.2 from Downloads tab_ -## Usage +## Installation -Coming soon. Take a look at application/controllers/api/example.php for -hints until the default controller demo is built and ready. +Drag and drop the **application/libraries/Format.php** and **application/libraries/REST_Controller.php** files into your application's directories. Either autoload the `REST_Controller` class or `require_once` it at the top of your controllers to load it into the scope. -I haven't got around to writing any documentation specifically for this project -but you can read my NetTuts article which covers it's usage along with the REST Client lib. +## Handling Requests -[NetTuts: Working with RESTful Services in CodeIgniter](http://net.tutsplus.com/tutorials/php/working-with-restful-services-in-codeigniter-2/) +When your controller extends from `REST_Controller`, the method names will be appended with the HTTP method used to access the request. If you're making an HTTP `GET` call to `/books`, for instance, it would call a `Books#index_get()` method. + +This allows you to implement a RESTful interface easily: + + class Books extends REST_Controller + { + public function index_get() + { + // Display all books + } + + public function index_post() + { + // Create a new book + } + } + +`REST_Controller` also supports `PUT` and `DELETE` methods, allowing you to support a truly RESTful interface. + +Accessing parameters is also easy. Simply use the name of the HTTP verb as a method: + + $this->get('blah'); // GET param + $this->post('blah'); // POST param + $this->put('blah'); // PUT param + $this->delete('blah'); // DELETE param + +## Content Types + +`REST_Controller` supports a bunch of different request/response formats, including XML, JSON and serialised PHP. By default, the class will check the URL and look for a format either as an extension or as a separate segment. + +This means your URLs can look like this: + + http://example.com/books.json + http://example.com/books?format=json + +Alternatively (and recommend) is using the HTTP `Accept` header, which is built for this purpose: + + $ curl -H "Accept: application/json" http://example.com + +Any responses you make from the class (see [responses](#responses) for more on this) will be serialised in the designated format. + +## Responses + +The class provides a `response()` method that allows you to return data in the user's requested response format. + +Returning any object / array / string / whatever is easy: + + public function index_get() + { + $this->response($this->db->get('books')->result()); + } + +This will automatically return an `HTTP 200 OK` response. You can specify the status code in the second parameter: + + public function index_post() + { + // ...create new book + + $this->response($book, 201); // Send an HTTP 201 Created + } + +If you don't specify a response code, and the data you respond with `== FALSE` (an empty array or string, for instance), the response code will automatically be set to `404 Not Found`: + + $this->response(array()); // HTTP 404 Not Found + +## Other Documentation / Tutorials + +* [NetTuts: Working with RESTful Services in CodeIgniter](http://net.tutsplus.com/tutorials/php/working-with-restful-services-in-codeigniter-2/) ## Change Log @@ -38,8 +103,6 @@ but you can read my NetTuts article which covers it's usage along with the REST * Separate each piece of the WWW-Authenticate header for digest requests with a comma. * Added IP whitelist option. - - ### 2.5 * Instead of just seeing item, item, item, the singular version of the basenode will be used if possible. [Example](http://d.pr/RS46). @@ -76,7 +139,6 @@ but you can read my NetTuts article which covers it's usage along with the REST * key => FALSE can now be used to override the keys_enabled option for a specific method, and level is now optional. If no level is set it will assume the method has a level of 0. * Fixed issue where calls to ->get('foo') would error is foo was not set. Reported by Paul Barto. - ## Donations If my REST Server has helped you out, or you'd like me to do some custom work on it, [please sponsor me](http://pledgie.com/campaigns/8328) From 7c9a90b160a8cb1773c0757079b92e405cc267f8 Mon Sep 17 00:00:00 2001 From: Jamie Rumbelow Date: Mon, 2 Jul 2012 16:08:11 +0100 Subject: [PATCH 053/651] Adding some information on authentication and multilingual support --- README.md | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 22a9214d..8ef6de49 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ _Note: for 1.7.x support download v2.2 from Downloads tab_ ## Installation -Drag and drop the **application/libraries/Format.php** and **application/libraries/REST_Controller.php** files into your application's directories. Either autoload the `REST_Controller` class or `require_once` it at the top of your controllers to load it into the scope. +Drag and drop the **application/libraries/Format.php** and **application/libraries/REST_Controller.php** files into your application's directories. Either autoload the `REST_Controller` class or `require_once` it at the top of your controllers to load it into the scope. Additionally, copy the **rest.php** file from **application/config** in your application's configuration directory. ## Handling Requests @@ -83,6 +83,42 @@ If you don't specify a response code, and the data you respond with `== FALSE` ( $this->response(array()); // HTTP 404 Not Found +## Multilingual Support + +If your application uses language files to support multiple locales, `REST_Controller` will automatically parse the HTTP `Accept-Language` header and provide the language(s) in your actions. This information can be found in the `$this->request->lang` object: + + public function __construct() + { + parent::__construct(); + + if (is_array($this->request->lang)) + { + $this->load->language('application', $this->request->lang[0]); + } + else + { + $this->load->language('application', $this->request->lang); + } + } + +## Authentication + +This class also provides rudimentary support for HTTP basic authentication and/or the securer HTTP digest access authentication. + +You can enable basic authentication by setting the `$config['rest_auth']` to `'basic'`. The `$config['rest_valid_logins']` directive can then be used to set the usernames and passwords able to log in to your system. The class will automatically send all the correct headers to trigger the authentication dialogue: + + $config['rest_valid_logins'] = array( 'username' => 'password', 'other_person' => 'secure123' ); + +Enabling digest auth is similarly easy. Configure your desired logins in the config file like above, and set `$config['rest_auth']` to `'digest'`. The class will automatically send out the headers to enable digest auth. + +Both methods of authentication can be secured further by using an IP whitelist. If you enable `$config['rest_ip_whitelist_enabled']` in your config file, you can then set a list of allowed IPs. + +Any client connecting to your API will be checked against the whitelisted IP array. If they're on the list, they'll be allowed access. If not, sorry, no can do hombre. The whitelist is a comma-separated string: + + $config['rest_ip_whitelist'] = '123.456.789.0, 987.654.32.1'; + +Your localhost IPs (`127.0.0.1` and `0.0.0.0`) are allowed by default. + ## Other Documentation / Tutorials * [NetTuts: Working with RESTful Services in CodeIgniter](http://net.tutsplus.com/tutorials/php/working-with-restful-services-in-codeigniter-2/) From 7d816b068019126ca8712dfd558a4451f3533cc1 Mon Sep 17 00:00:00 2001 From: Jamie Rumbelow Date: Mon, 2 Jul 2012 16:16:39 +0100 Subject: [PATCH 054/651] Documentation on API keys --- README.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/README.md b/README.md index 8ef6de49..8fd4c440 100644 --- a/README.md +++ b/README.md @@ -119,6 +119,29 @@ Any client connecting to your API will be checked against the whitelisted IP arr Your localhost IPs (`127.0.0.1` and `0.0.0.0`) are allowed by default. +## API Keys + +In addition to the authentication methods above, the `REST_Controller` class also supports the use of API keys. Enabling API keys is easy. Turn it on in your **config/rest.php** file: + + $config['rest_enable_keys'] = TRUE; + +You'll need to create a new database table to store and access the keys. `REST_Controller` will automatically assume you have a table that looks like this: + + CREATE TABLE `keys` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `key` varchar(40) NOT NULL, + `level` int(2) NOT NULL, + `ignore_limits` tinyint(1) NOT NULL DEFAULT '0', + `date_created` int(11) NOT NULL, + PRIMARY KEY (`id`) + ) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +The class will look for an HTTP header with the API key on each request. An invalid or missing API key will result in an `HTTP 403 Forbidden`. + +By default, the HTTP will be `X-API-KEY`. This can be configured in **config/rest.php**. + + $ curl -X POST -H "X-API-KEY: some_key_here" http://example.com/books + ## Other Documentation / Tutorials * [NetTuts: Working with RESTful Services in CodeIgniter](http://net.tutsplus.com/tutorials/php/working-with-restful-services-in-codeigniter-2/) From f06971ff4678a2d839a189e80dfb0bffb37cf3c5 Mon Sep 17 00:00:00 2001 From: Kevin Wood-Friend Date: Tue, 3 Jul 2012 21:11:19 -0400 Subject: [PATCH 055/651] Removed some extra whitespace --- application/libraries/Format.php | 14 ++-- application/libraries/REST_Controller.php | 78 +++++++++++------------ 2 files changed, 46 insertions(+), 46 deletions(-) diff --git a/application/libraries/Format.php b/application/libraries/Format.php index 955f3a8d..42e73727 100644 --- a/application/libraries/Format.php +++ b/application/libraries/Format.php @@ -37,7 +37,7 @@ public function factory($data, $from_type = null) public function __construct($data = null, $from_type = null) { get_instance()->load->helper('inflector'); - + // If the provided data is already formatted we should probably convert it to an array if ($from_type !== null) { @@ -110,17 +110,17 @@ public function to_xml($data = null, $structure = null, $basenode = 'xml') foreach ($data as $key => $value) { - + //change false/true to 0/1 if(is_bool($value)) { $value = (int) $value; } - + // no numeric keys in our xml please! if (is_numeric($key)) { - // make string key... + // make string key... $key = (singular($basenode) != $basenode) ? singular($basenode) : 'item'; } @@ -140,7 +140,7 @@ public function to_xml($data = null, $structure = null, $basenode = 'xml') { // add single node. $value = htmlspecialchars(html_entity_decode($value, ENT_QUOTES, 'UTF-8'), ENT_QUOTES, "UTF-8"); - + $structure->addChild($key, $value); } } @@ -152,7 +152,7 @@ public function to_xml($data = null, $structure = null, $basenode = 'xml') public function to_html() { $data = $this->_data; - + // Multi-dimensional array if (isset($data[0]) && is_array($data[0])) { @@ -217,7 +217,7 @@ public function to_serialized() { return serialize($this->_data); } - + // Output as a string representing the PHP structure public function to_php() { diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index fcac78f3..3a621c79 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -17,7 +17,7 @@ abstract class REST_Controller extends CI_Controller { /** * This defines the rest format. - * + * * Must be overriden it in a controller so that it is set. * * @var string|null @@ -27,7 +27,7 @@ abstract class REST_Controller extends CI_Controller /** * Defines the list of method properties such as limit, log and level * - * @var array + * @var array */ protected $methods = array(); @@ -35,34 +35,34 @@ abstract class REST_Controller extends CI_Controller * General request data and information. * Stores accept, language, body, headers, etc. * - * @var object + * @var object */ protected $request = NULL; /** * What is gonna happen in output? - * - * @var object + * + * @var object */ protected $response = NULL; /** * Stores DB, keys, key level, etc * - * @var object + * @var object */ protected $rest = NULL; /** * The arguments for the GET request method * - * @var array + * @var array */ protected $_get_args = array(); /** * The arguments for the POST request method - * + * * @var array */ protected $_post_args = array(); @@ -90,14 +90,14 @@ abstract class REST_Controller extends CI_Controller /** * If the request is allowed based on the API key provided. - * + * * @var boolean */ protected $_allow = TRUE; /** * Determines if output compression is enabled - * + * * @var boolean */ protected $_zlib_oc = FALSE; @@ -122,7 +122,7 @@ abstract class REST_Controller extends CI_Controller */ protected function early_checks() { - + } /** @@ -263,8 +263,8 @@ public function __construct() /** * Remap * - * Requests are not made to methods directly, the request will be for - * an "object". This simply maps the object and method to the correct + * Requests are not made to methods directly, the request will be for + * an "object". This simply maps the object and method to the correct * Controller method. * * @param string $object_called @@ -344,7 +344,7 @@ public function _remap($object_called, $arguments) * Takes pure data and optionally a status code, then creates the response. * * @param array $data - * @param null|int $http_code + * @param null|int $http_code */ public function response($data = array(), $http_code = null) { @@ -373,7 +373,7 @@ public function response($data = array(), $http_code = null) } } } - + is_numeric($http_code) OR $http_code = 200; // If the format method exists, call and return the output in that format @@ -447,8 +447,8 @@ protected function _detect_input_format() * Detect format * * Detect which format should be used to output the data. - * - * @return string The output format. + * + * @return string The output format. */ protected function _detect_output_format() { @@ -528,13 +528,13 @@ protected function _detect_output_format() * Detect method * * Detect which method (POST, PUT, GET, DELETE) is being used - * - * @return string + * + * @return string */ protected function _detect_method() { $method = strtolower($this->input->server('REQUEST_METHOD')); - + if ($this->config->item('enable_emulate_request')) { if ($this->input->post('_method')) @@ -544,7 +544,7 @@ protected function _detect_method() else if ($this->input->server('HTTP_X_HTTP_METHOD_OVERRIDE')) { $method = strtolower($this->input->server('HTTP_X_HTTP_METHOD_OVERRIDE')); - } + } } if (in_array($method, array('get', 'delete', 'post', 'put'))) @@ -559,8 +559,8 @@ protected function _detect_method() * Detect API Key * * See if the user has provided an API key - * - * @return boolean + * + * @return boolean */ protected function _detect_api_key() { @@ -600,7 +600,7 @@ protected function _detect_api_key() * Detect language(s) * * What language do they want it in? - * + * * @return null|string The language code. */ protected function _detect_lang() @@ -635,9 +635,9 @@ protected function _detect_lang() * Log request * * Record the entry for awesomeness purposes - * + * * @param boolean $authorized - * @return object + * @return object */ protected function _log_request($authorized = FALSE) { @@ -658,7 +658,7 @@ protected function _log_request($authorized = FALSE) * Check if the requests are coming in a tad too fast. * * @param string $controller_method The method deing called. - * @return boolean + * @return boolean */ protected function _check_limit($controller_method) { @@ -713,10 +713,10 @@ protected function _check_limit($controller_method) /** * Auth override check * - * Check if there is a specific auth type set for the current class/method + * Check if there is a specific auth type set for the current class/method * being called. * - * @return boolean + * @return boolean */ protected function _auth_override_check() { @@ -763,7 +763,7 @@ protected function _auth_override_check() return true; } - // Return false when there is an override value set but it does not match + // Return false when there is an override value set but it does not match // 'basic', 'digest', or 'none'. (the value was misspelled) return false; } @@ -858,7 +858,7 @@ protected function _xss_clean($val, $process) /** * Retrieve the validation errors. * - * @return array + * @return array */ public function validation_errors() { @@ -874,7 +874,7 @@ public function validation_errors() * * @param string $username The user's name * @param string $password The user's password - * @return boolean + * @return boolean */ protected function _check_login($username = '', $password = NULL) { @@ -900,7 +900,7 @@ protected function _check_login($username = '', $password = NULL) } /** - * @todo document this. + * @todo document this. */ protected function _prepare_basic_auth() { @@ -936,7 +936,7 @@ protected function _prepare_basic_auth() } /** - * @todo Document this. + * @todo Document this. */ protected function _prepare_digest_auth() { @@ -962,7 +962,7 @@ protected function _prepare_digest_auth() $digest_string = ""; } - // The $_SESSION['error_prompted'] variable is used to ask the password + // The $_SESSION['error_prompted'] variable is used to ask the password // again if none given or if the user enters wrong auth information. if (empty($digest_string)) { @@ -1017,7 +1017,7 @@ protected function _check_whitelist_auth() /** * @todo Document this. * - * @param string $nonce + * @param string $nonce */ protected function _force_login($nonce = '') { @@ -1037,7 +1037,7 @@ protected function _force_login($nonce = '') * Force it into an array * * @param object|array $data - * @return array + * @return array */ protected function _force_loopable($data) { @@ -1055,9 +1055,9 @@ protected function _force_loopable($data) /** * Encode as JSONP - * + * * @param array $data The input data. - * @return string The JSONP data string (loadable from Javascript). + * @return string The JSONP data string (loadable from Javascript). */ protected function _format_jsonp($data = array()) { From a278fa87bc48725431fd1ad8a2a7564bd4249052 Mon Sep 17 00:00:00 2001 From: Kevin Wood-Friend Date: Tue, 3 Jul 2012 21:20:03 -0400 Subject: [PATCH 056/651] Flipped on Spell Check and corrected a few typos --- application/libraries/Format.php | 10 +++++----- application/libraries/REST_Controller.php | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/application/libraries/Format.php b/application/libraries/Format.php index 42e73727..74f7b0d4 100644 --- a/application/libraries/Format.php +++ b/application/libraries/Format.php @@ -26,7 +26,7 @@ class Format { */ public function factory($data, $from_type = null) { - // Stupid stuff to emulate the "new static()" stuff in this libraries PHP 5.3 equivilent + // Stupid stuff to emulate the "new static()" stuff in this libraries PHP 5.3 equivalent $class = __CLASS__; return new $class($data, $from_type); } @@ -59,7 +59,7 @@ public function __construct($data = null, $from_type = null) public function to_array($data = null) { - // If not just null, but nopthing is provided + // If not just null, but nothing is provided if ($data === null and ! func_num_args()) { $data = $this->_data; @@ -127,12 +127,12 @@ public function to_xml($data = null, $structure = null, $basenode = 'xml') // replace anything not alpha numeric $key = preg_replace('/[^a-z_\-0-9]/i', '', $key); - // if there is another array found recrusively call this function + // if there is another array found recursively call this function if (is_array($value) || is_object($value)) { $node = $structure->addChild($key); - // recrusive call. + // recursive call. $this->to_xml($value, $node, $key); } @@ -184,7 +184,7 @@ public function to_csv() { $data = $this->_data; - // Multi-dimentional array + // Multi-dimensional array if (isset($data[0])) { $headings = array_keys($data[0]); diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 3a621c79..9d74270f 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -18,7 +18,7 @@ abstract class REST_Controller extends CI_Controller /** * This defines the rest format. * - * Must be overriden it in a controller so that it is set. + * Must be overridden it in a controller so that it is set. * * @var string|null */ @@ -162,7 +162,7 @@ public function __construct() // Grab proper GET variables parse_str(parse_url(http://github.com/$_SERVER['REQUEST_URI'],%20PHP_URL_QUERY), $get); - // Merge both the URI segements and GET params + // Merge both the URI segments and GET params $this->_get_args = array_merge($this->_get_args, $get); break; @@ -498,13 +498,13 @@ protected function _detect_output_format() // HTML or XML have shown up as a match else { - // If it is truely HTML, it wont want any XML + // If it is truly HTML, it wont want any XML if ($format == 'html' AND strpos($this->input->server('HTTP_ACCEPT'), 'xml') === FALSE) { return $format; } - // If it is truely XML, it wont want any HTML + // If it is truly XML, it wont want any HTML elseif ($format == 'xml' AND strpos($this->input->server('HTTP_ACCEPT'), 'html') === FALSE) { return $format; @@ -657,7 +657,7 @@ protected function _log_request($authorized = FALSE) * * Check if the requests are coming in a tad too fast. * - * @param string $controller_method The method deing called. + * @param string $controller_method The method being called. * @return boolean */ protected function _check_limit($controller_method) From c08b4fe76214c920e717ab3d76b36baeb5f6a61d Mon Sep 17 00:00:00 2001 From: Kevin Wood-Friend Date: Tue, 3 Jul 2012 21:20:52 -0400 Subject: [PATCH 057/651] Fixed a couple CSV related comments --- application/libraries/Format.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/libraries/Format.php b/application/libraries/Format.php index 74f7b0d4..1cfee672 100644 --- a/application/libraries/Format.php +++ b/application/libraries/Format.php @@ -179,7 +179,7 @@ public function to_html() return $ci->table->generate(); } - // Format HTML for output + // Format CSV for output public function to_csv() { $data = $this->_data; @@ -230,7 +230,7 @@ protected function _from_xml($string) return $string ? (array) simplexml_load_string($string, 'SimpleXMLElement', LIBXML_NOCDATA) : array(); } - // Format HTML for output + // Format CSV for output // This function is DODGY! Not perfect CSV support but works with my REST_Controller protected function _from_csv($string) { From 0e60f9d878c2258e0f8781d6f049af1e8164630d Mon Sep 17 00:00:00 2001 From: Kevin Wood-Friend Date: Tue, 3 Jul 2012 22:24:57 -0400 Subject: [PATCH 058/651] Converted space indentation to tabs --- application/libraries/Format.php | 30 +++++++++++------------ application/libraries/REST_Controller.php | 8 +++--- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/application/libraries/Format.php b/application/libraries/Format.php index 1cfee672..2c862c4a 100644 --- a/application/libraries/Format.php +++ b/application/libraries/Format.php @@ -119,26 +119,26 @@ public function to_xml($data = null, $structure = null, $basenode = 'xml') // no numeric keys in our xml please! if (is_numeric($key)) - { - // make string key... - $key = (singular($basenode) != $basenode) ? singular($basenode) : 'item'; - } + { + // make string key... + $key = (singular($basenode) != $basenode) ? singular($basenode) : 'item'; + } // replace anything not alpha numeric $key = preg_replace('/[^a-z_\-0-9]/i', '', $key); - // if there is another array found recursively call this function - if (is_array($value) || is_object($value)) - { - $node = $structure->addChild($key); + // if there is another array found recursively call this function + if (is_array($value) || is_object($value)) + { + $node = $structure->addChild($key); - // recursive call. - $this->to_xml($value, $node, $key); - } + // recursive call. + $this->to_xml($value, $node, $key); + } - else - { - // add single node. + else + { + // add single node. $value = htmlspecialchars(html_entity_decode($value, ENT_QUOTES, 'UTF-8'), ENT_QUOTES, "UTF-8"); $structure->addChild($key, $value); @@ -221,7 +221,7 @@ public function to_serialized() // Output as a string representing the PHP structure public function to_php() { - return var_export($this->_data, TRUE); + return var_export($this->_data, TRUE); } // Format XML for output diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 9d74270f..7c258218 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -541,10 +541,10 @@ protected function _detect_method() { $method = strtolower($this->input->post('_method')); } - else if ($this->input->server('HTTP_X_HTTP_METHOD_OVERRIDE')) - { - $method = strtolower($this->input->server('HTTP_X_HTTP_METHOD_OVERRIDE')); - } + elseif ($this->input->server('HTTP_X_HTTP_METHOD_OVERRIDE')) + { + $method = strtolower($this->input->server('HTTP_X_HTTP_METHOD_OVERRIDE')); + } } if (in_array($method, array('get', 'delete', 'post', 'put'))) From ae7b2d429e845f673696e8e8326f2310e2e7a5eb Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Thu, 5 Jul 2012 13:07:17 +0200 Subject: [PATCH 059/651] Made sure response() wont't try and pass an empty array to strlen (line 413), which is generating an E_WARNING in PHP 5.3. --- application/libraries/REST_Controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 7c258218..afbb8177 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -356,7 +356,7 @@ public function response($data = array(), $http_code = null) $http_code = 404; //create the output variable here in the case of $this->response(array()); - $output = $data; + $output = NULL; } // Otherwise (if no data but 200 provided) or some data, carry on camping! From bf37537724e60ebc643d79a423c776b67f4820b8 Mon Sep 17 00:00:00 2001 From: Kevin Wood-Friend Date: Thu, 5 Jul 2012 14:15:30 -0400 Subject: [PATCH 060/651] Moved GET, POST, PUT, and DELETE parsing to separate methods, allowing them to be overridden as needed --- application/libraries/REST_Controller.php | 86 +++++++++++++---------- 1 file changed, 50 insertions(+), 36 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index afbb8177..1b4bd431 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -156,42 +156,7 @@ public function __construct() // Some Methods cant have a body $this->request->body = NULL; - switch ($this->request->method) - { - case 'get': - // Grab proper GET variables - parse_str(parse_url(http://github.com/$_SERVER['REQUEST_URI'],%20PHP_URL_QUERY), $get); - - // Merge both the URI segments and GET params - $this->_get_args = array_merge($this->_get_args, $get); - break; - - case 'post': - $this->_post_args = $_POST; - - $this->request->format and $this->request->body = file_get_contents('php://input'); - break; - - case 'put': - // It might be a HTTP body - if ($this->request->format) - { - $this->request->body = file_get_contents('php://input'); - } - - // If no file type is provided, this is probably just arguments - else - { - parse_str(file_get_contents('php://input'), $this->_put_args); - } - - break; - - case 'delete': - // Set up out DELETE variables (which shouldn't really exist, but sssh!) - parse_str(file_get_contents('php://input'), $this->_delete_args); - break; - } + $this->{'_parse_' . $this->request->method}(); // Now we know all about our request, let's try and parse the body if it exists if ($this->request->format and $this->request->body) @@ -768,6 +733,55 @@ protected function _auth_override_check() return false; } + /** + * Parse GET + */ + protected function _parse_get() + { + // Grab proper GET variables + parse_str(parse_url(http://github.com/$_SERVER['REQUEST_URI'],%20PHP_URL_QUERY), $get); + + // Merge both the URI segments and GET params + $this->_get_args = array_merge($this->_get_args, $get); + } + + /** + * Parse POST + */ + protected function _parse_post() + { + $this->_post_args = $_POST; + + $this->request->format and $this->request->body = file_get_contents('php://input'); + } + + /** + * Parse PUT + */ + protected function _parse_put() + { + // It might be a HTTP body + if ($this->request->format) + { + $this->request->body = file_get_contents('php://input'); + } + + // If no file type is provided, this is probably just arguments + else + { + parse_str(file_get_contents('php://input'), $this->_put_args); + } + } + + /** + * Parse DELETE + */ + protected function _parse_delete() + { + // Set up out DELETE variables (which shouldn't really exist, but sssh!) + parse_str(file_get_contents('php://input'), $this->_delete_args); + } + // INPUT FUNCTION -------------------------------------------------------------- /** From 420c1ff347c0eaef3f4edd09af64317620a88fa7 Mon Sep 17 00:00:00 2001 From: Kevin Wood-Friend Date: Thu, 5 Jul 2012 14:17:12 -0400 Subject: [PATCH 061/651] Added support for allowing alternate HTTP methods --- application/libraries/REST_Controller.php | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 1b4bd431..fa8e86f2 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -31,6 +31,13 @@ abstract class REST_Controller extends CI_Controller */ protected $methods = array(); + /** + * List of allowed HTTP methods + * + * @var array + */ + protected $allowed_http_methods = array('get', 'delete', 'post', 'put'); + /** * General request data and information. * Stores accept, language, body, headers, etc. @@ -320,7 +327,7 @@ public function response($data = array(), $http_code = null) { $http_code = 404; - //create the output variable here in the case of $this->response(array()); + // create the output variable here in the case of $this->response(array()); $output = NULL; } @@ -492,7 +499,7 @@ protected function _detect_output_format() /** * Detect method * - * Detect which method (POST, PUT, GET, DELETE) is being used + * Detect which HTTP method is being used * * @return string */ @@ -512,7 +519,7 @@ protected function _detect_method() } } - if (in_array($method, array('get', 'delete', 'post', 'put'))) + if (in_array($method, $this->allowed_http_methods) && method_exists($this, '_parse_' . $method)) { return $method; } From 4cd56e4af9fd5ae9fd8c7b124c5d8bb9beeb3fd2 Mon Sep 17 00:00:00 2001 From: Kevin Wood-Friend Date: Fri, 6 Jul 2012 09:42:50 -0400 Subject: [PATCH 062/651] Improved support for alternate HTTP methods --- application/libraries/REST_Controller.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index fa8e86f2..79a0d99a 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -149,6 +149,12 @@ public function __construct() $this->request = new stdClass(); $this->request->method = $this->_detect_method(); + // Create argument container, if nonexistent + if ( ! isset($this->{'_'.$this->request->method.'_args'})) + { + $this->{'_'.$this->request->method.'_args'} = array(); + } + // Set up our GET variables $this->_get_args = array_merge($this->_get_args, $this->uri->ruri_to_assoc()); @@ -174,7 +180,7 @@ public function __construct() } // Merge both for one mega-args variable - $this->_args = array_merge($this->_get_args, $this->_put_args, $this->_post_args, $this->_delete_args); + $this->_args = array_merge($this->_get_args, $this->_put_args, $this->_post_args, $this->_delete_args, $this->{'_'.$this->request->method.'_args'}); // Which format should the data be returned in? $this->response = new stdClass(); From a37300529bca14d125fef9c8a5a2a4e15c4a1ee3 Mon Sep 17 00:00:00 2001 From: Kevin Wood-Friend Date: Fri, 6 Jul 2012 09:56:13 -0400 Subject: [PATCH 063/651] Abstract out the processes of firing a controller method within _remap() to an separate method --- application/libraries/REST_Controller.php | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 79a0d99a..b0fdfddc 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -313,7 +313,20 @@ public function _remap($object_called, $arguments) } // And...... GO! - call_user_func_array(array($this, $controller_method), $arguments); + $this->_fire_method(array($this, $controller_method), $arguments); + } + + /** + * Fire Method + * + * Fires the designated controller method with the given arguments. + * + * @param array $method The controller method to fire + * @param array $args The arguments to pass to the controller method + */ + protected function _fire_method($method, $args) + { + call_user_func_array($method, $args); } /** From 37a44e04ab70f4e781b199afde950ac03505d6e6 Mon Sep 17 00:00:00 2001 From: Kevin Wood-Friend Date: Fri, 6 Jul 2012 10:59:22 -0400 Subject: [PATCH 064/651] Fix to to_csv() for determining if data is a multi-dimensional array --- application/libraries/Format.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/libraries/Format.php b/application/libraries/Format.php index 2c862c4a..acb13699 100644 --- a/application/libraries/Format.php +++ b/application/libraries/Format.php @@ -185,7 +185,7 @@ public function to_csv() $data = $this->_data; // Multi-dimensional array - if (isset($data[0])) + if (isset($data[0]) && is_array($data[0])) { $headings = array_keys($data[0]); } From 03a3d31c4ae1d2f49e822b68852ca066b4069b5d Mon Sep 17 00:00:00 2001 From: Jason Horwitz Date: Sat, 7 Jul 2012 21:25:27 -0400 Subject: [PATCH 065/651] Update CodeIgniter files to 2.1.2 (Jun,29th, 2012 release) --- application/config/mimes.php | 17 +- system/core/CodeIgniter.php | 2 +- system/core/Input.php | 135 +++++++++++++++- system/core/Security.php | 153 ++++++++++-------- system/database/DB_active_rec.php | 12 +- system/database/DB_driver.php | 44 +++-- system/database/drivers/pdo/pdo_driver.php | 31 ++-- system/database/drivers/pdo/pdo_forge.php | 2 +- system/database/drivers/pdo/pdo_result.php | 15 +- system/database/drivers/pdo/pdo_utility.php | 2 +- system/helpers/form_helper.php | 2 +- system/helpers/url_helper.php | 36 ++--- system/language/english/migration_lang.php | 2 +- system/libraries/Cache/Cache.php | 2 +- system/libraries/Cache/drivers/Cache_apc.php | 2 +- .../libraries/Cache/drivers/Cache_dummy.php | 2 +- system/libraries/Cache/drivers/Cache_file.php | 2 +- .../Cache/drivers/Cache_memcached.php | 2 +- system/libraries/Cart.php | 2 +- system/libraries/Driver.php | 4 +- system/libraries/Form_validation.php | 5 +- system/libraries/Image_lib.php | 4 +- system/libraries/Migration.php | 2 +- system/libraries/Upload.php | 107 +++++++++--- 24 files changed, 414 insertions(+), 173 deletions(-) mode change 100755 => 100644 application/config/mimes.php diff --git a/application/config/mimes.php b/application/config/mimes.php old mode 100755 new mode 100644 index de923c45..100f7d44 --- a/application/config/mimes.php +++ b/application/config/mimes.php @@ -10,7 +10,7 @@ $mimes = array( 'hqx' => 'application/mac-binhex40', 'cpt' => 'application/mac-compactpro', - 'csv' => array('text/x-comma-separated-values', 'text/comma-separated-values', 'application/octet-stream', 'application/vnd.ms-excel', 'text/x-csv', 'text/csv', 'application/csv', 'application/excel', 'application/vnd.msexcel'), + 'csv' => array('text/x-comma-separated-values', 'text/comma-separated-values', 'application/octet-stream', 'application/vnd.ms-excel', 'application/x-csv', 'text/x-csv', 'text/csv', 'application/csv', 'application/excel', 'application/vnd.msexcel'), 'bin' => 'application/macbinary', 'dms' => 'application/octet-stream', 'lha' => 'application/octet-stream', @@ -56,7 +56,7 @@ 'midi' => 'audio/midi', 'mpga' => 'audio/mpeg', 'mp2' => 'audio/mpeg', - 'mp3' => array('audio/mpeg', 'audio/mpg', 'audio/mpeg3'), + 'mp3' => array('audio/mpeg', 'audio/mpg', 'audio/mpeg3', 'audio/mp3'), 'aif' => 'audio/x-aiff', 'aiff' => 'audio/x-aiff', 'aifc' => 'audio/x-aiff', @@ -65,8 +65,8 @@ 'rpm' => 'audio/x-pn-realaudio-plugin', 'ra' => 'audio/x-realaudio', 'rv' => 'video/vnd.rn-realvideo', - 'wav' => 'audio/x-wav', - 'bmp' => 'image/bmp', + 'wav' => array('audio/x-wav', 'audio/wave', 'audio/wav'), + 'bmp' => array('image/bmp', 'image/x-windows-bmp'), 'gif' => 'image/gif', 'jpeg' => array('image/jpeg', 'image/pjpeg'), 'jpg' => array('image/jpeg', 'image/pjpeg'), @@ -93,13 +93,14 @@ 'avi' => 'video/x-msvideo', 'movie' => 'video/x-sgi-movie', 'doc' => 'application/msword', - 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', - 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'docx' => array('application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/zip'), + 'xlsx' => array('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/zip'), 'word' => array('application/msword', 'application/octet-stream'), 'xl' => 'application/excel', - 'eml' => 'message/rfc822' + 'eml' => 'message/rfc822', + 'json' => array('application/json', 'text/json') ); /* End of file mimes.php */ -/* Location: ./application/config/mimes.php */ \ No newline at end of file +/* Location: ./application/config/mimes.php */ diff --git a/system/core/CodeIgniter.php b/system/core/CodeIgniter.php index db1aee57..cd333333 100755 --- a/system/core/CodeIgniter.php +++ b/system/core/CodeIgniter.php @@ -33,7 +33,7 @@ * @var string * */ - define('CI_VERSION', '2.1.0'); + define('CI_VERSION', '2.1.2'); /** * CodeIgniter Branch (Core = TRUE, Reactor = FALSE) diff --git a/system/core/Input.php b/system/core/Input.php index 9bfb5f1f..3559d860 100755 --- a/system/core/Input.php +++ b/system/core/Input.php @@ -365,18 +365,70 @@ function ip_address() /** * Validate IP Address * + * @access public + * @param string + * @param string ipv4 or ipv6 + * @return bool + */ + public function valid_ip($ip, $which = '') + { + $which = strtolower($which); + + // First check if filter_var is available + if (is_callable('filter_var')) + { + switch ($which) { + case 'ipv4': + $flag = FILTER_FLAG_IPV4; + break; + case 'ipv6': + $flag = FILTER_FLAG_IPV6; + break; + default: + $flag = ''; + break; + } + + return (bool) filter_var($ip, FILTER_VALIDATE_IP, $flag); + } + + if ($which !== 'ipv6' && $which !== 'ipv4') + { + if (strpos($ip, ':') !== FALSE) + { + $which = 'ipv6'; + } + elseif (strpos($ip, '.') !== FALSE) + { + $which = 'ipv4'; + } + else + { + return FALSE; + } + } + + $func = '_valid_'.$which; + return $this->$func($ip); + } + + // -------------------------------------------------------------------- + + /** + * Validate IPv4 Address + * * Updated version suggested by Geert De Deckere * - * @access public + * @access protected * @param string - * @return string + * @return bool */ - function valid_ip($ip) + protected function _valid_ipv4($ip) { $ip_segments = explode('.', $ip); // Always 4 segments needed - if (count($ip_segments) != 4) + if (count($ip_segments) !== 4) { return FALSE; } @@ -385,6 +437,7 @@ function valid_ip($ip) { return FALSE; } + // Check each segment foreach ($ip_segments as $segment) { @@ -401,6 +454,80 @@ function valid_ip($ip) // -------------------------------------------------------------------- + /** + * Validate IPv6 Address + * + * @access protected + * @param string + * @return bool + */ + protected function _valid_ipv6($str) + { + // 8 groups, separated by : + // 0-ffff per group + // one set of consecutive 0 groups can be collapsed to :: + + $groups = 8; + $collapsed = FALSE; + + $chunks = array_filter( + preg_split('/(:{1,2})/', $str, NULL, PREG_SPLIT_DELIM_CAPTURE) + ); + + // Rule out easy nonsense + if (current($chunks) == ':' OR end($chunks) == ':') + { + return FALSE; + } + + // PHP supports IPv4-mapped IPv6 addresses, so we'll expect those as well + if (strpos(end($chunks), '.') !== FALSE) + { + $ipv4 = array_pop($chunks); + + if ( ! $this->_valid_ipv4($ipv4)) + { + return FALSE; + } + + $groups--; + } + + while ($seg = array_pop($chunks)) + { + if ($seg[0] == ':') + { + if (--$groups == 0) + { + return FALSE; // too many groups + } + + if (strlen($seg) > 2) + { + return FALSE; // long separator + } + + if ($seg == '::') + { + if ($collapsed) + { + return FALSE; // multiple collapsed + } + + $collapsed = TRUE; + } + } + elseif (preg_match("/[^0-9a-f]/i", $seg) OR strlen($seg) > 4) + { + return FALSE; // invalid segment + } + } + + return $collapsed OR $groups == 1; + } + + // -------------------------------------------------------------------- + /** * User Agent * diff --git a/system/core/Security.php b/system/core/Security.php index a3e22743..7af240de 100755 --- a/system/core/Security.php +++ b/system/core/Security.php @@ -1,4 +1,4 @@ - '[removed]', - 'document.write' => '[removed]', - '.parentNode' => '[removed]', - '.innerHTML' => '[removed]', - 'window.location' => '[removed]', - '-moz-binding' => '[removed]', - '' => '-->', - ' '<![CDATA[', - '' => '<comment>' + 'document.cookie' => '[removed]', + 'document.write' => '[removed]', + '.parentNode' => '[removed]', + '.innerHTML' => '[removed]', + 'window.location' => '[removed]', + '-moz-binding' => '[removed]', + '' => '-->', + ' '<![CDATA[', + '' => '<comment>' ); /* never allowed, regex replacement */ @@ -89,10 +89,11 @@ class CI_Security { * @access protected */ protected $_never_allowed_regex = array( - "javascript\s*:" => '[removed]', - "expression\s*(\(|&\#40;)" => '[removed]', // CSS and IE - "vbscript\s*:" => '[removed]', // IE, surprise! - "Redirect\s+302" => '[removed]' + 'javascript\s*:', + 'expression\s*(\(|&\#40;)', // CSS and IE + 'vbscript\s*:', // IE, surprise! + 'Redirect\s+302', + "([\"'])?data\s*:[^\\1]*?base64[^\\1]*?,[^\\1]*?\\1?" ); /** @@ -364,9 +365,9 @@ public function xss_clean($str, $is_image = FALSE) * These words are compacted back to their correct state. */ $words = array( - 'javascript', 'expression', 'vbscript', 'script', - 'applet', 'alert', 'document', 'write', 'cookie', 'window' - ); + 'javascript', 'expression', 'vbscript', 'script', 'base64', + 'applet', 'alert', 'document', 'write', 'cookie', 'window' + ); foreach ($words as $word) { @@ -524,38 +525,38 @@ public function entity_decode($str, $charset='UTF-8') public function sanitize_filename($str, $relative_path = FALSE) { $bad = array( - "../", - "", - "<", - ">", - "'", - '"', - '&', - '$', - '#', - '{', - '}', - '[', - ']', - '=', - ';', - '?', - "%20", - "%22", - "%3c", // < - "%253c", // < - "%3e", // > - "%0e", // > - "%28", // ( - "%29", // ) - "%2528", // ( - "%26", // & - "%24", // $ - "%3f", // ? - "%3b", // ; - "%3d" // = - ); + "../", + "", + "<", + ">", + "'", + '"', + '&', + '$', + '#', + '{', + '}', + '[', + ']', + '=', + ';', + '?', + "%20", + "%22", + "%3c", // < + "%253c", // < + "%3e", // > + "%0e", // > + "%28", // ( + "%29", // ) + "%2528", // ( + "%26", // & + "%24", // $ + "%3f", // ? + "%3b", // ; + "%3d" // = + ); if ( ! $relative_path) { @@ -613,19 +614,20 @@ protected function _remove_evil_attributes($str, $is_image) */ unset($evil_attributes[array_search('xmlns', $evil_attributes)]); } - + do { $count = 0; $attribs = array(); - + // find occurrences of illegal attribute strings without quotes - preg_match_all("/(".implode('|', $evil_attributes).")\s*=\s*([^\s]*)/is", $str, $matches, PREG_SET_ORDER); - + preg_match_all('/('.implode('|', $evil_attributes).')\s*=\s*([^\s>]*)/is', $str, $matches, PREG_SET_ORDER); + foreach ($matches as $attr) { + $attribs[] = preg_quote($attr[0], '/'); } - + // find occurrences of illegal attribute strings with quotes (042 and 047 are octal quotes) preg_match_all("/(".implode('|', $evil_attributes).")\s*=\s*(\042|\047)([^\\2]*?)(\\2)/is", $str, $matches, PREG_SET_ORDER); @@ -637,11 +639,11 @@ protected function _remove_evil_attributes($str, $is_image) // replace illegal attribute strings that are inside an html tag if (count($attribs) > 0) { - $str = preg_replace("/<(\/?[^><]+?)([^A-Za-z\-])(".implode('|', $attribs).")([\s><])([><]*)/i", '<$1$2$4$5', $str, -1, $count); + $str = preg_replace("/<(\/?[^><]+?)([^A-Za-z<>\-])(.*?)(".implode('|', $attribs).")(.*?)([\s><])([><]*)/i", '<$1 $3$5$6$7', $str, -1, $count); } - + } while ($count); - + return $str; } @@ -682,9 +684,15 @@ protected function _sanitize_naughty_html($matches) */ protected function _js_link_removal($match) { - $attributes = $this->_filter_attributes(str_replace(array('<', '>'), '', $match[1])); - - return str_replace($match[1], preg_replace("#href=.*?(alert\(|alert&\#40;|javascript\:|livescript\:|mocha\:|charset\=|window\.|document\.|\.cookie|_filter_attributes(str_replace(array('<', '>'), '', $match[1])) + ), + $match[0] + ); } // -------------------------------------------------------------------- @@ -702,9 +710,15 @@ protected function _js_link_removal($match) */ protected function _js_img_removal($match) { - $attributes = $this->_filter_attributes(str_replace(array('<', '>'), '', $match[1])); - - return str_replace($match[1], preg_replace("#src=.*?(alert\(|alert&\#40;|javascript\:|livescript\:|mocha\:|charset\=|window\.|document\.|\.cookie|_filter_attributes(str_replace(array('<', '>'), '', $match[1])) + ), + $match[0] + ); } // -------------------------------------------------------------------- @@ -819,14 +833,11 @@ protected function _validate_entities($str) */ protected function _do_never_allowed($str) { - foreach ($this->_never_allowed_str as $key => $val) - { - $str = str_replace($key, $val, $str); - } + $str = str_replace(array_keys($this->_never_allowed_str), $this->_never_allowed_str, $str); - foreach ($this->_never_allowed_regex as $key => $val) + foreach ($this->_never_allowed_regex as $regex) { - $str = preg_replace("#".$key."#i", $val, $str); + $str = preg_replace('#'.$regex.'#is', '[removed]', $str); } return $str; @@ -848,7 +859,7 @@ protected function _csrf_set_hash() // each page load since a page could contain embedded // sub-pages causing this feature to fail if (isset($_COOKIE[$this->_csrf_cookie_name]) && - $_COOKIE[$this->_csrf_cookie_name] != '') + preg_match('#^[0-9a-f]{32}$#iS', $_COOKIE[$this->_csrf_cookie_name]) === 1) { return $this->_csrf_hash = $_COOKIE[$this->_csrf_cookie_name]; } diff --git a/system/database/DB_active_rec.php b/system/database/DB_active_rec.php index 7bab729f..10febb1f 100644 --- a/system/database/DB_active_rec.php +++ b/system/database/DB_active_rec.php @@ -255,7 +255,7 @@ public function distinct($val = TRUE) */ public function from($from) { - foreach ((array)$from as $val) + foreach ((array) $from as $val) { if (strpos($val, ',') !== FALSE) { @@ -660,8 +660,12 @@ protected function _like($field, $match = '', $type = 'AND ', $side = 'both', $n $prefix = (count($this->ar_like) == 0) ? '' : $type; $v = $this->escape_like_str($v); - - if ($side == 'before') + + if ($side == 'none') + { + $like_statement = $prefix." $k $not LIKE '{$v}'"; + } + elseif ($side == 'before') { $like_statement = $prefix." $k $not LIKE '%{$v}'"; } @@ -1643,7 +1647,7 @@ protected function _track_aliases($table) if (strpos($table, " ") !== FALSE) { // if the alias is written with the AS keyword, remove it - $table = preg_replace('/ AS /i', ' ', $table); + $table = preg_replace('/\s+AS\s+/i', ' ', $table); // Grab the alias $table = trim(strrchr($table, " ")); diff --git a/system/database/DB_driver.php b/system/database/DB_driver.php index 3680b85c..775fd335 100644 --- a/system/database/DB_driver.php +++ b/system/database/DB_driver.php @@ -265,6 +265,12 @@ function query($sql, $binds = FALSE, $return_object = TRUE) $sql = preg_replace("/(\W)".$this->swap_pre."(\S+?)/", "\\1".$this->dbprefix."\\2", $sql); } + // Compile binds if needed + if ($binds !== FALSE) + { + $sql = $this->compile_binds($sql, $binds); + } + // Is query caching enabled? If the query is a "read type" // we will load the caching class and return the previously // cached query if it exists @@ -280,12 +286,6 @@ function query($sql, $binds = FALSE, $return_object = TRUE) } } - // Compile binds if needed - if ($binds !== FALSE) - { - $sql = $this->compile_binds($sql, $binds); - } - // Save the query for debugging if ($this->save_queries == TRUE) { @@ -1015,8 +1015,14 @@ function call_function($function) else { $args = (func_num_args() > 1) ? array_splice(func_get_args(), 1) : null; - - return call_user_func_array($function, $args); + if (is_null($args)) + { + return call_user_func($function); + } + else + { + return call_user_func_array($function, $args); + } } } @@ -1261,12 +1267,15 @@ function _protect_identifiers($item, $prefix_single = FALSE, $protect_identifier // If the item has an alias declaration we remove it and set it aside. // Basically we remove everything to the right of the first space - $alias = ''; if (strpos($item, ' ') !== FALSE) { - $alias = strstr($item, " "); + $alias = strstr($item, ' '); $item = substr($item, 0, - strlen($alias)); } + else + { + $alias = ''; + } // This is basically a bug fix for queries that use MAX, MIN, etc. // If a parenthesis is found we know that we do not need to @@ -1382,9 +1391,20 @@ function _protect_identifiers($item, $prefix_single = FALSE, $protect_identifier return $item.$alias; } + // -------------------------------------------------------------------- -} + /** + * Dummy method that allows Active Record class to be disabled + * + * This function is used extensively by every db driver. + * + * @return void + */ + protected function _reset_select() + { + } +} /* End of file DB_driver.php */ -/* Location: ./system/database/DB_driver.php */ +/* Location: ./system/database/DB_driver.php */ \ No newline at end of file diff --git a/system/database/drivers/pdo/pdo_driver.php b/system/database/drivers/pdo/pdo_driver.php index 5de2079b..1ecc90a8 100644 --- a/system/database/drivers/pdo/pdo_driver.php +++ b/system/database/drivers/pdo/pdo_driver.php @@ -9,7 +9,7 @@ * @license http://codeigniter.com/user_guide/license.html * @author EllisLab Dev Team * @link http://codeigniter.com - * @since Version 2.1.0 + * @since Version 2.1.2 * @filesource */ @@ -51,23 +51,23 @@ class CI_DB_pdo_driver extends CI_DB { function __construct($params) { parent::__construct($params); - + // clause and character used for LIKE escape sequences if (strpos($this->hostname, 'mysql') !== FALSE) { $this->_like_escape_str = ''; $this->_like_escape_chr = ''; - + //Prior to this version, the charset can't be set in the dsn if(is_php('5.3.6')) { $this->hostname .= ";charset={$this->char_set}"; } - + //Set the charset with the connection options $this->options['PDO::MYSQL_ATTR_INIT_COMMAND'] = "SET NAMES {$this->char_set}"; } - else if (strpos($this->hostname, 'odbc') !== FALSE) + elseif (strpos($this->hostname, 'odbc') !== FALSE) { $this->_like_escape_str = " {escape '%s'} "; $this->_like_escape_chr = '!'; @@ -77,9 +77,9 @@ function __construct($params) $this->_like_escape_str = " ESCAPE '%s' "; $this->_like_escape_chr = '!'; } - - $this->hostname .= ";dbname=".$this->database; - + + empty($this->database) OR $this->hostname .= ';dbname='.$this->database; + $this->trans_enabled = FALSE; $this->_random_keyword = ' RND('.time().')'; // database specific random keyword @@ -94,7 +94,7 @@ function __construct($params) function db_connect() { $this->options['PDO::ATTR_ERRMODE'] = PDO::ERRMODE_SILENT; - + return new PDO($this->hostname, $this->username, $this->password, $this->options); } @@ -189,11 +189,20 @@ function _version() function _execute($sql) { $sql = $this->_prep_query($sql); - $result_id = $this->conn_id->query($sql); + $result_id = $this->conn_id->prepare($sql); + $result_id->execute(); if (is_object($result_id)) { - $this->affect_rows = $result_id->rowCount(); + if (is_numeric(stripos($sql, 'SELECT'))) + { + $this->affect_rows = count($result_id->fetchAll()); + $result_id->execute(); + } + else + { + $this->affect_rows = $result_id->rowCount(); + } } else { diff --git a/system/database/drivers/pdo/pdo_forge.php b/system/database/drivers/pdo/pdo_forge.php index 1462e8c2..9a782208 100644 --- a/system/database/drivers/pdo/pdo_forge.php +++ b/system/database/drivers/pdo/pdo_forge.php @@ -9,7 +9,7 @@ * @license http://codeigniter.com/user_guide/license.html * @author EllisLab Dev Team * @link http://codeigniter.com - * @since Version 2.1.0 + * @since Version 2.1.2 * @filesource */ diff --git a/system/database/drivers/pdo/pdo_result.php b/system/database/drivers/pdo/pdo_result.php index 7f3058ff..a366a5f1 100644 --- a/system/database/drivers/pdo/pdo_result.php +++ b/system/database/drivers/pdo/pdo_result.php @@ -9,7 +9,7 @@ * @license http://codeigniter.com/user_guide/license.html * @author EllisLab Dev Team * @link http://codeigniter.com - * @since Version 2.1.0 + * @since Version 2.1.2 * @filesource */ @@ -34,7 +34,18 @@ class CI_DB_pdo_result extends CI_DB_result { */ function num_rows() { - return $this->result_id->rowCount(); + if (is_numeric(stripos($this->result_id->queryString, 'SELECT'))) + { + $dbh = $this->conn_id; + $query = $dbh->query($this->result_id->queryString); + $result = $query->fetchAll(); + unset($dbh, $query); + return count($result); + } + else + { + return $this->result_id->rowCount(); + } } // -------------------------------------------------------------------- diff --git a/system/database/drivers/pdo/pdo_utility.php b/system/database/drivers/pdo/pdo_utility.php index 29aefca8..88ce033a 100644 --- a/system/database/drivers/pdo/pdo_utility.php +++ b/system/database/drivers/pdo/pdo_utility.php @@ -9,7 +9,7 @@ * @license http://codeigniter.com/user_guide/license.html * @author EllisLab Dev Team * @link http://codeigniter.com - * @since Version 2.1.0 + * @since Version 2.1.2 * @filesource */ diff --git a/system/helpers/form_helper.php b/system/helpers/form_helper.php index d9305c00..8733ae05 100644 --- a/system/helpers/form_helper.php +++ b/system/helpers/form_helper.php @@ -65,7 +65,7 @@ function form_open($action = '', $attributes = '', $hidden = array()) $form .= '>'; // Add CSRF field if enabled, but leave it out for GET requests and requests to external websites - if ($CI->config->item('csrf_protection') === TRUE AND ! (strpos($action, $CI->config->site_url()) === FALSE OR strpos($form, 'method="get"'))) + if ($CI->config->item('csrf_protection') === TRUE AND ! (strpos($action, $CI->config->base_url()) === FALSE OR strpos($form, 'method="get"'))) { $hidden[$CI->security->get_csrf_token_name()] = $CI->security->get_csrf_hash(); } diff --git a/system/helpers/url_helper.php b/system/helpers/url_helper.php index 9f4b8524..f1e8c6ac 100644 --- a/system/helpers/url_helper.php +++ b/system/helpers/url_helper.php @@ -466,39 +466,35 @@ function prep_url(http://github.com/$str%20=%20'') * Create URL Title * * Takes a "title" string as input and creates a - * human-friendly URL string with either a dash - * or an underscore as the word separator. + * human-friendly URL string with a "separator" string + * as the word separator. * * @access public * @param string the string - * @param string the separator: dash, or underscore + * @param string the separator * @return string */ if ( ! function_exists('url_title')) { - function url_title($str, $separator = 'dash', $lowercase = FALSE) + function url_title($str, $separator = '-', $lowercase = FALSE) { - if ($separator == 'dash') + if ($separator == 'dash') { - $search = '_'; - $replace = '-'; + $separator = '-'; } - else + else if ($separator == 'underscore') { - $search = '-'; - $replace = '_'; + $separator = '_'; } + + $q_separator = preg_quote($separator); $trans = array( - '&\#\d+?;' => '', - '&\S+?;' => '', - '\s+' => $replace, - '[^a-z0-9\-\._]' => '', - $replace.'+' => $replace, - $replace.'$' => $replace, - '^'.$replace => $replace, - '\.+$' => '' - ); + '&.+?;' => '', + '[^a-z0-9 _-]' => '', + '\s+' => $separator, + '('.$q_separator.')+' => $separator + ); $str = strip_tags($str); @@ -512,7 +508,7 @@ function url_title($str, $separator = 'dash', $lowercase = FALSE) $str = strtolower($str); } - return trim(stripslashes($str)); + return trim($str, $separator); } } diff --git a/system/language/english/migration_lang.php b/system/language/english/migration_lang.php index 4763ca24..f17530f0 100644 --- a/system/language/english/migration_lang.php +++ b/system/language/english/migration_lang.php @@ -5,7 +5,7 @@ $lang['migration_multiple_version'] = "This are multiple migrations with the same version number: %d."; $lang['migration_class_doesnt_exist'] = "The migration class \"%s\" could not be found."; $lang['migration_missing_up_method'] = "The migration class \"%s\" is missing an 'up' method."; -$lang['migration_missing_down_method'] = "The migration class \"%s\" is missing an 'up' method."; +$lang['migration_missing_down_method'] = "The migration class \"%s\" is missing an 'down' method."; $lang['migration_invalid_filename'] = "Migration \"%s\" has an invalid filename."; diff --git a/system/libraries/Cache/Cache.php b/system/libraries/Cache/Cache.php index 61e7aa76..261fc367 100644 --- a/system/libraries/Cache/Cache.php +++ b/system/libraries/Cache/Cache.php @@ -6,7 +6,7 @@ * * @package CodeIgniter * @author ExpressionEngine Dev Team - * @copyright Copyright (c) 2006 - 2011 EllisLab, Inc. + * @copyright Copyright (c) 2006 - 2012 EllisLab, Inc. * @license http://codeigniter.com/user_guide/license.html * @link http://codeigniter.com * @since Version 2.0 diff --git a/system/libraries/Cache/drivers/Cache_apc.php b/system/libraries/Cache/drivers/Cache_apc.php index 79d91b32..f750e6cb 100644 --- a/system/libraries/Cache/drivers/Cache_apc.php +++ b/system/libraries/Cache/drivers/Cache_apc.php @@ -6,7 +6,7 @@ * * @package CodeIgniter * @author ExpressionEngine Dev Team - * @copyright Copyright (c) 2006 - 2011 EllisLab, Inc. + * @copyright Copyright (c) 2006 - 2012 EllisLab, Inc. * @license http://codeigniter.com/user_guide/license.html * @link http://codeigniter.com * @since Version 2.0 diff --git a/system/libraries/Cache/drivers/Cache_dummy.php b/system/libraries/Cache/drivers/Cache_dummy.php index f96a68e2..b11b5b8f 100644 --- a/system/libraries/Cache/drivers/Cache_dummy.php +++ b/system/libraries/Cache/drivers/Cache_dummy.php @@ -6,7 +6,7 @@ * * @package CodeIgniter * @author ExpressionEngine Dev Team - * @copyright Copyright (c) 2006 - 2011 EllisLab, Inc. + * @copyright Copyright (c) 2006 - 2012 EllisLab, Inc. * @license http://codeigniter.com/user_guide/license.html * @link http://codeigniter.com * @since Version 2.0 diff --git a/system/libraries/Cache/drivers/Cache_file.php b/system/libraries/Cache/drivers/Cache_file.php index 13e2d1af..c5004366 100644 --- a/system/libraries/Cache/drivers/Cache_file.php +++ b/system/libraries/Cache/drivers/Cache_file.php @@ -6,7 +6,7 @@ * * @package CodeIgniter * @author ExpressionEngine Dev Team - * @copyright Copyright (c) 2006 - 2011 EllisLab, Inc. + * @copyright Copyright (c) 2006 - 2012 EllisLab, Inc. * @license http://codeigniter.com/user_guide/license.html * @link http://codeigniter.com * @since Version 2.0 diff --git a/system/libraries/Cache/drivers/Cache_memcached.php b/system/libraries/Cache/drivers/Cache_memcached.php index fc586e02..74784209 100644 --- a/system/libraries/Cache/drivers/Cache_memcached.php +++ b/system/libraries/Cache/drivers/Cache_memcached.php @@ -6,7 +6,7 @@ * * @package CodeIgniter * @author ExpressionEngine Dev Team - * @copyright Copyright (c) 2006 - 2011 EllisLab, Inc. + * @copyright Copyright (c) 2006 - 2012 EllisLab, Inc. * @license http://codeigniter.com/user_guide/license.html * @link http://codeigniter.com * @since Version 2.0 diff --git a/system/libraries/Cart.php b/system/libraries/Cart.php index ab5a70c9..da47b5a1 100644 --- a/system/libraries/Cart.php +++ b/system/libraries/Cart.php @@ -6,7 +6,7 @@ * * @package CodeIgniter * @author ExpressionEngine Dev Team - * @copyright Copyright (c) 2006 - 2011, EllisLab, Inc. + * @copyright Copyright (c) 2006 - 2012, EllisLab, Inc. * @license http://codeigniter.com/user_guide/license.html * @link http://codeigniter.com * @since Version 1.0 diff --git a/system/libraries/Driver.php b/system/libraries/Driver.php index 9881c1ee..4912c4aa 100644 --- a/system/libraries/Driver.php +++ b/system/libraries/Driver.php @@ -6,7 +6,7 @@ * * @package CodeIgniter * @author EllisLab Dev Team - * @copyright Copyright (c) 2006 - 2011, EllisLab, Inc. + * @copyright Copyright (c) 2006 - 2012, EllisLab, Inc. * @license http://codeigniter.com/user_guide/license.html * @link http://codeigniter.com * @since Version 1.0 @@ -30,7 +30,7 @@ class CI_Driver_Library { protected $valid_drivers = array(); - protected static $lib_name; + protected $lib_name; // The first time a child is used it won't exist, so we instantiate it // subsequents calls will go straight to the proper child. diff --git a/system/libraries/Form_validation.php b/system/libraries/Form_validation.php index a34809e0..9aab5da4 100644 --- a/system/libraries/Form_validation.php +++ b/system/libraries/Form_validation.php @@ -1079,11 +1079,12 @@ public function valid_emails($str) * * @access public * @param string + * @param string "ipv4" or "ipv6" to validate a specific ip format * @return string */ - public function valid_ip($ip) + public function valid_ip($ip, $which = '') { - return $this->CI->input->valid_ip($ip); + return $this->CI->input->valid_ip($ip, $which); } // -------------------------------------------------------------------- diff --git a/system/libraries/Image_lib.php b/system/libraries/Image_lib.php index 8902f524..21ec2cb4 100644 --- a/system/libraries/Image_lib.php +++ b/system/libraries/Image_lib.php @@ -104,7 +104,7 @@ public function __construct($props = array()) */ function clear() { - $props = array('source_folder', 'dest_folder', 'source_image', 'full_src_path', 'full_dst_path', 'new_image', 'image_type', 'size_str', 'quality', 'orig_width', 'orig_height', 'rotation_angle', 'x_axis', 'y_axis', 'create_fnc', 'copy_fnc', 'wm_overlay_path', 'wm_use_truetype', 'dynamic_output', 'wm_font_size', 'wm_text', 'wm_vrt_alignment', 'wm_hor_alignment', 'wm_padding', 'wm_hor_offset', 'wm_vrt_offset', 'wm_font_color', 'wm_use_drop_shadow', 'wm_shadow_color', 'wm_shadow_distance', 'wm_opacity'); + $props = array('source_folder', 'dest_folder', 'source_image', 'full_src_path', 'full_dst_path', 'new_image', 'image_type', 'size_str', 'quality', 'orig_width', 'orig_height', 'width', 'height', 'rotation_angle', 'x_axis', 'y_axis', 'create_fnc', 'copy_fnc', 'wm_overlay_path', 'wm_use_truetype', 'dynamic_output', 'wm_font_size', 'wm_text', 'wm_vrt_alignment', 'wm_hor_alignment', 'wm_padding', 'wm_hor_offset', 'wm_vrt_offset', 'wm_font_color', 'wm_use_drop_shadow', 'wm_shadow_color', 'wm_shadow_distance', 'wm_opacity'); foreach ($props as $val) { @@ -208,7 +208,7 @@ function initialize($props = array()) } else { - if (strpos($this->new_image, '/') === FALSE) + if (strpos($this->new_image, '/') === FALSE AND strpos($this->new_image, '\\') === FALSE) { $this->dest_folder = $this->source_folder; $this->dest_image = $this->new_image; diff --git a/system/libraries/Migration.php b/system/libraries/Migration.php index 3943ec13..5a41377e 100644 --- a/system/libraries/Migration.php +++ b/system/libraries/Migration.php @@ -6,7 +6,7 @@ * * @package CodeIgniter * @author EllisLab Dev Team - * @copyright Copyright (c) 2006 - 2011, EllisLab, Inc. + * @copyright Copyright (c) 2006 - 2012, EllisLab, Inc. * @license http://codeigniter.com/user_guide/license.html * @link http://codeigniter.com * @since Version 1.0 diff --git a/system/libraries/Upload.php b/system/libraries/Upload.php index 05511b5d..0e5d73b1 100644 --- a/system/libraries/Upload.php +++ b/system/libraries/Upload.php @@ -868,6 +868,10 @@ public function do_xss_clean() { return TRUE; // its an image, no "triggers" detected in the first 256 bytes, we're good } + else + { + return FALSE; + } } if (($data = @file_get_contents($file)) === FALSE) @@ -1018,47 +1022,104 @@ protected function _prep_filename($filename) */ protected function _file_mime_type($file) { - // Use if the Fileinfo extension, if available (only versions above 5.3 support the FILEINFO_MIME_TYPE flag) - if ( (float) substr(phpversion(), 0, 3) >= 5.3 && function_exists('finfo_file')) + // We'll need this to validate the MIME info string (e.g. text/plain; charset=us-ascii) + $regexp = '/^([a-z\-]+\/[a-z0-9\-\.\+]+)(;\s.+)?$/'; + + /* Fileinfo extension - most reliable method + * + * Unfortunately, prior to PHP 5.3 - it's only available as a PECL extension and the + * more convenient FILEINFO_MIME_TYPE flag doesn't exist. + */ + if (function_exists('finfo_file')) { - $finfo = new finfo(FILEINFO_MIME_TYPE); - if ($finfo !== FALSE) // This is possible, if there is no magic MIME database file found on the system + $finfo = finfo_open(FILEINFO_MIME); + if (is_resource($finfo)) // It is possible that a FALSE value is returned, if there is no magic MIME database file found on the system { - $file_type = $finfo->file($file['tmp_name']); + $mime = @finfo_file($finfo, $file['tmp_name']); + finfo_close($finfo); /* According to the comments section of the PHP manual page, * it is possible that this function returns an empty string * for some files (e.g. if they don't exist in the magic MIME database) */ - if (strlen($file_type) > 1) + if (is_string($mime) && preg_match($regexp, $mime, $matches)) { - $this->file_type = $file_type; + $this->file_type = $matches[1]; return; } } } - // Fall back to the deprecated mime_content_type(), if available - if (function_exists('mime_content_type')) - { - $this->file_type = @mime_content_type($file['tmp_name']); - return; - } - - /* This is an ugly hack, but UNIX-type systems provide a native way to detect the file type, - * which is still more secure than depending on the value of $_FILES[$field]['type']. + /* This is an ugly hack, but UNIX-type systems provide a "native" way to detect the file type, + * which is still more secure than depending on the value of $_FILES[$field]['type'], and as it + * was reported in issue #750 (https://github.com/EllisLab/CodeIgniter/issues/750) - it's better + * than mime_content_type() as well, hence the attempts to try calling the command line with + * three different functions. * * Notes: - * - a 'W' in the substr() expression bellow, would mean that we're using Windows - * - many system admins would disable the exec() function due to security concerns, hence the function_exists() check + * - the DIRECTORY_SEPARATOR comparison ensures that we're not on a Windows system + * - many system admins would disable the exec(), shell_exec(), popen() and similar functions + * due to security concerns, hence the function_exists() checks */ - if (DIRECTORY_SEPARATOR !== '\\' && function_exists('exec')) + if (DIRECTORY_SEPARATOR !== '\\') { - $output = array(); - @exec('file --brief --mime-type ' . escapeshellarg($file['tmp_path']), $output, $return_code); - if ($return_code === 0 && strlen($output[0]) > 0) // A return status code != 0 would mean failed execution + $cmd = 'file --brief --mime ' . escapeshellarg($file['tmp_name']) . ' 2>&1'; + + if (function_exists('exec')) + { + /* This might look confusing, as $mime is being populated with all of the output when set in the second parameter. + * However, we only neeed the last line, which is the actual return value of exec(), and as such - it overwrites + * anything that could already be set for $mime previously. This effectively makes the second parameter a dummy + * value, which is only put to allow us to get the return status code. + */ + $mime = @exec($cmd, $mime, $return_status); + if ($return_status === 0 && is_string($mime) && preg_match($regexp, $mime, $matches)) + { + $this->file_type = $matches[1]; + return; + } + } + + if ( (bool) @ini_get('safe_mode') === FALSE && function_exists('shell_exec')) + { + $mime = @shell_exec($cmd); + if (strlen($mime) > 0) + { + $mime = explode("\n", trim($mime)); + if (preg_match($regexp, $mime[(count($mime) - 1)], $matches)) + { + $this->file_type = $matches[1]; + return; + } + } + } + + if (function_exists('popen')) + { + $proc = @popen($cmd, 'r'); + if (is_resource($proc)) + { + $mime = @fread($proc, 512); + @pclose($proc); + if ($mime !== FALSE) + { + $mime = explode("\n", trim($mime)); + if (preg_match($regexp, $mime[(count($mime) - 1)], $matches)) + { + $this->file_type = $matches[1]; + return; + } + } + } + } + } + + // Fall back to the deprecated mime_content_type(), if available (still better than $_FILES[$field]['type']) + if (function_exists('mime_content_type')) + { + $this->file_type = @mime_content_type($file['tmp_name']); + if (strlen($this->file_type) > 0) // It's possible that mime_content_type() returns FALSE or an empty string { - $this->file_type = rtrim($output[0]); return; } } From b0a502eeaffad32de2dd25028661bad5dd795b28 Mon Sep 17 00:00:00 2001 From: juantorres Date: Fri, 13 Jul 2012 00:03:51 -0700 Subject: [PATCH 066/651] Added two new fields to the keys table: 1) is_private_key and 2) ip_addresses. These two fields work together. If the key is private a list of IP addresses can be associated with the API key, If the API key is private and no valid address if found, the request is rejected. IP addresses need to be separated by a line break n. This is an alternative to the ip-whitelist, but it is tight to the API key. --- application/config/rest.php | 2 ++ application/libraries/REST_Controller.php | 27 +++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/application/config/rest.php b/application/config/rest.php index c170bfd3..c671ed33 100644 --- a/application/config/rest.php +++ b/application/config/rest.php @@ -156,6 +156,8 @@ `key` varchar(40) NOT NULL, `level` int(2) NOT NULL, `ignore_limits` tinyint(1) NOT NULL DEFAULT '0', + `is_private_key` tinyint(1) NOT NULL DEFAULT '0', + `ip_addresses` TEXT NULL DEFAULT NULL, `date_created` int(11) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index b0fdfddc..7c53f2ce 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -579,6 +579,33 @@ protected function _detect_api_key() isset($row->user_id) AND $this->rest->user_id = $row->user_id; isset($row->level) AND $this->rest->level = $row->level; isset($row->ignore_limits) AND $this->rest->ignore_limits = $row->ignore_limits; + + if(!empty($row->is_private_key)) + { + + // Check for a list of valid ip addresses + if(isset($row->ip_addresses)) + { + $list_ip_addresses = explode("\n", $row->ip_addresses); + $found_address = FALSE; + + foreach($list_ip_addresses as $ip_address) + { + if($this->input->ip_address() == $ip_address) + { + $found_address = TRUE; + break; + } + } + + return $found_address; + } + else + { + // There should be at least one IP address for this private key. + return FALSE; + } + } return TRUE; } From 2dd4aad0df83db4b04d34deb651aaf2410834ea1 Mon Sep 17 00:00:00 2001 From: David Stanley Date: Sun, 19 Aug 2012 22:49:14 -0500 Subject: [PATCH 067/651] added options to force ssl and to specify api key table data --- application/config/rest.php | 20 ++++++++-- application/libraries/REST_Controller.php | 46 ++++++++++++++++++++--- 2 files changed, 58 insertions(+), 8 deletions(-) diff --git a/application/config/rest.php b/application/config/rest.php index c170bfd3..7349e870 100644 --- a/application/config/rest.php +++ b/application/config/rest.php @@ -1,5 +1,17 @@ load->config('rest'); - // How is this request being made? POST, DELETE, GET, PUT? + // let's learn about the request $this->request = new stdClass(); + + // Is it over SSL? + $this->request->ssl = $this->_detect_ssl(); + + // How is this request being made? POST, DELETE, GET, PUT? $this->request->method = $this->_detect_method(); // Create argument container, if nonexistent @@ -250,6 +262,12 @@ public function __construct() */ public function _remap($object_called, $arguments) { + // Should we answer if not over SSL? + if (config_item('force_https') AND !$this->_detect_ssl()) + { + $this->response(array('status' => false, 'error' => 'Unsupported protocol'), 403); + } + $pattern = '/^(.*)\.('.implode('|', array_keys($this->_supported_formats)).')$/'; if (preg_match($pattern, $object_called, $matches)) { @@ -407,6 +425,22 @@ public function response($data = array(), $http_code = null) exit($output); } + /* + * Detect SSL use + * + * Detect whether SSL is being used or not + */ + protected function _detect_ssl() + { + if( isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == "on") + { + return TRUE; + } + + return FALSE; + } + + /* * Detect input format * @@ -569,18 +603,20 @@ protected function _detect_api_key() // Find the key from server or arguments if (($key = isset($this->_args[$api_key_variable]) ? $this->_args[$api_key_variable] : $this->input->server($key_name))) { - if ( ! ($row = $this->rest->db->where('key', $key)->get(config_item('rest_keys_table'))->row())) + if ( ! ($this->client = $this->rest->db->where(config_item('rest_key_column'), $key)->get(config_item('rest_keys_table'))->row())) { return FALSE; } - $this->rest->key = $row->key; + $this->rest->key = $this->client->{config_item('rest_key_column')}; + /* isset($row->user_id) AND $this->rest->user_id = $row->user_id; isset($row->level) AND $this->rest->level = $row->level; isset($row->ignore_limits) AND $this->rest->ignore_limits = $row->ignore_limits; + */ - return TRUE; + return $this->client; } // No key has been sent @@ -1104,4 +1140,4 @@ protected function _format_jsonp($data = array()) return $this->get('callback').'('.json_encode($data).')'; } -} +} \ No newline at end of file From 0e6c6aa8e4a8b0c2aaf22373b657ffc6681f1b1f Mon Sep 17 00:00:00 2001 From: David Stanley Date: Wed, 22 Aug 2012 13:54:27 -0500 Subject: [PATCH 068/651] Updated comment on rest_key_column name --- application/config/rest.php | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/application/config/rest.php b/application/config/rest.php index 7349e870..c8fb9ea2 100644 --- a/application/config/rest.php +++ b/application/config/rest.php @@ -151,7 +151,7 @@ | 'keys' | */ -$config['rest_keys_table'] = 'system_user'; +$config['rest_keys_table'] = 'keys'; /* |-------------------------------------------------------------------------- @@ -174,7 +174,18 @@ | */ $config['rest_enable_keys'] = TRUE; -$config['rest_key_column'] = 'username'; + +/* +|-------------------------------------------------------------------------- +| REST Table Key Column Name +|-------------------------------------------------------------------------- +| +| If you are not using the default table schema as shown above, what is the +| name of the db column that holds the api key value? +| +*/ +$config['rest_key_column'] = 'key'; + /* |-------------------------------------------------------------------------- | REST Key Length From 0872a6f30d75ef1e1628ffc82c70abf2bb7b540f Mon Sep 17 00:00:00 2001 From: juantorres Date: Tue, 11 Sep 2012 16:39:47 -0700 Subject: [PATCH 069/651] IP addresses are no longer separated by a line brake '\n' rather all ip addresses are coma separated. --- application/libraries/REST_Controller.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 7c53f2ce..54dee3ce 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -586,12 +586,12 @@ protected function _detect_api_key() // Check for a list of valid ip addresses if(isset($row->ip_addresses)) { - $list_ip_addresses = explode("\n", $row->ip_addresses); + $list_ip_addresses = explode(",", $row->ip_addresses); $found_address = FALSE; foreach($list_ip_addresses as $ip_address) { - if($this->input->ip_address() == $ip_address) + if($this->input->ip_address() == trim($ip_address)) { $found_address = TRUE; break; From b13a810f6c7e45c4359e0f3be05def7a76682397 Mon Sep 17 00:00:00 2001 From: Phil Sturgeon Date: Wed, 12 Sep 2012 10:49:43 +0100 Subject: [PATCH 070/651] Added changelog. --- README.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8fd4c440..3329ad1d 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ This means your URLs can look like this: http://example.com/books.json http://example.com/books?format=json -Alternatively (and recommend) is using the HTTP `Accept` header, which is built for this purpose: +This can be flaky with URI segments, so the recommend approach is using the HTTP `Accept` header: $ curl -H "Accept: application/json" http://example.com @@ -148,6 +148,17 @@ By default, the HTTP will be `X-API-KEY`. This can be configured in **config/res ## Change Log +### 2.6.1 + +* Update CodeIgniter files to 2.1.2 +* Log Table support for IPv6 & NULL parameters +* Abstract out the processes of firing a controller method within _remap() to an separate method +* Moved GET, POST, PUT, and DELETE parsing to separate methods, allowing them to be overridden as needed +* Small bugfix for a PHP 5.3 strlen error +* Fixed some PHP 5.4 warnings +* Fix for bug in Format.php's to_html() which failed to detect if $data was really a multidimensional array. +* Fix for empty node on XML output format, for false = 0, true = 1. + ### 2.6.0 * Added loads of PHPDoc comments. From 806f8db1a0bc050918d7ca846431c30ec8791871 Mon Sep 17 00:00:00 2001 From: David Stanley Date: Wed, 12 Sep 2012 08:22:30 -0500 Subject: [PATCH 071/651] simplified _detect_ssl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit thanks, Phil.  That was unnecessary.  --- application/libraries/REST_Controller.php | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 710f95a0..107a182d 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -432,12 +432,7 @@ public function response($data = array(), $http_code = null) */ protected function _detect_ssl() { - if( isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == "on") - { - return TRUE; - } - - return FALSE; + return (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == "on")); } From da1fc96c0258dd7f8ea4392a3a9907036bae1974 Mon Sep 17 00:00:00 2001 From: David Stanley Date: Wed, 12 Sep 2012 08:24:15 -0500 Subject: [PATCH 072/651] fixed some stuff MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit changed default on rest keys.  also removed two columns from key table until I can figure out how i want to handle the request status. --- application/config/rest.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/application/config/rest.php b/application/config/rest.php index c8fb9ea2..ae841f45 100644 --- a/application/config/rest.php +++ b/application/config/rest.php @@ -173,7 +173,7 @@ ) ENGINE=InnoDB DEFAULT CHARSET=utf8; | */ -$config['rest_enable_keys'] = TRUE; +$config['rest_enable_keys'] = FALSE; /* |-------------------------------------------------------------------------- @@ -242,9 +242,7 @@ `api_key` varchar(40) NOT NULL, `ip_address` varchar(45) NOT NULL, `time` int(11) NOT NULL, - `authorized` tinyint(1) NOT NULL, - 'status' int(3) NOT NULL, - 'payload' TEXT DEFAULT NULL, + `authorized` tinyint(1) NOT NULL PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; | From fe13c4a3492f6f4f36d12007fe39ae738d91c7f1 Mon Sep 17 00:00:00 2001 From: Phil Sturgeon Date: Mon, 17 Sep 2012 15:30:43 +0200 Subject: [PATCH 073/651] Take down donations from README I don't want money, just pull requests. --- README.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 3329ad1d..fe75afe0 100644 --- a/README.md +++ b/README.md @@ -209,7 +209,12 @@ By default, the HTTP will be `X-API-KEY`. This can be configured in **config/res * key => FALSE can now be used to override the keys_enabled option for a specific method, and level is now optional. If no level is set it will assume the method has a level of 0. * Fixed issue where calls to ->get('foo') would error is foo was not set. Reported by Paul Barto. -## Donations +## Contributions -If my REST Server has helped you out, or you'd like me to do some custom work on it, [please sponsor me](http://pledgie.com/campaigns/8328) -so I can keep working on this and other CodeIgniter projects for you all. \ No newline at end of file +This project has been funded and made possible through my clients kindly allowing me to +open-source the functionality as I build it into their projects. I am no longer actively developing +features for this as I no longer require it, but I will continue to maintain pull requests and try to +fix issues as and when they are reported (within a week or two). + +Pull Requests are the best way to fix bugs or add features. I know loads of you use this, so please +contribute if you have improvements to be made and I'll keep releasing versions over time. \ No newline at end of file From bab618587ba81b3eda0b4598aac87c9707d0f610 Mon Sep 17 00:00:00 2001 From: Phil Sturgeon Date: Mon, 17 Sep 2012 15:31:17 +0200 Subject: [PATCH 074/651] Update README.md --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index fe75afe0..b244c9d2 100644 --- a/README.md +++ b/README.md @@ -3,11 +3,9 @@ A fully RESTful server implementation for CodeIgniter using one library, one config file and one controller. -## Sponsored by: Coding Futures - ## Requirements -1. PHP 5.2+ +1. PHP 5.2 or greater 2. CodeIgniter 2.1.0 to 3.0-dev _Note: for 1.7.x support download v2.2 from Downloads tab_ From f0ea8c5ba414c829edeaacf544918c46c351c54a Mon Sep 17 00:00:00 2001 From: Jonathan Creasy Date: Thu, 20 Sep 2012 22:38:33 -0700 Subject: [PATCH 075/651] adding LDAP authentication support --- application/config/rest.php | 9 +- application/libraries/REST_Controller.php | 103 ++++++++++++++++++++++ 2 files changed, 108 insertions(+), 4 deletions(-) diff --git a/application/config/rest.php b/application/config/rest.php index c170bfd3..be454733 100644 --- a/application/config/rest.php +++ b/application/config/rest.php @@ -44,7 +44,7 @@ | | Is login required and if so, which type of login? | -| '' = no login required, 'basic' = unsecure login, 'digest' = more secure login +| '' = no login required, 'basic' = unsecure login, 'digest' = more secure login, 'ldap' = digest with ldap | */ $config['rest_auth'] = false; @@ -65,19 +65,20 @@ | $config['auth_override_class_method']['accounts']['user'] = 'basic'; | | Here 'deals' and 'accounts' are controller names, 'view', 'insert' and 'user' are methods within. (NOTE: leave off the '_get' or '_post' from the end of the method name) -| Acceptable values are; 'none', 'digest' and 'basic'. +| Acceptable values are; 'none', 'digest', 'ldap', and 'basic' | */ // $config['auth_override_class_method']['deals']['view'] = 'none'; // $config['auth_override_class_method']['deals']['insert'] = 'digest'; // $config['auth_override_class_method']['accounts']['user'] = 'basic'; +// $config['auth_override_class_method']['accounts']['create'] = 'ldap'; /* |-------------------------------------------------------------------------- | REST Login usernames |-------------------------------------------------------------------------- | -| Array of usernames and passwords for login +| Array of usernames and passwords for login, if ldap is configured this is ignored | | array('admin' => '1234') | @@ -290,4 +291,4 @@ $config['rest_ajax_only'] = FALSE; /* End of file config.php */ -/* Location: ./system/application/config/rest.php */ \ No newline at end of file +/* Location: ./system/application/config/rest.php */ diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index b0fdfddc..80157df4 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -109,6 +109,13 @@ abstract class REST_Controller extends CI_Controller */ protected $_zlib_oc = FALSE; + /** + * The LDAP Distinguished Name of the User post authentication + * + * @var string + */ + protected $_user_ldap_dn = '' + /** * List all supported methods, the first will be the default format * @@ -909,6 +916,94 @@ public function validation_errors() // SECURITY FUNCTIONS --------------------------------------------------------- + /** + * Perform LDAP Authentication + * + * @param string $username The username to validate + * @param string $password The password to validate + * @return boolean + */ + protected function _perform_ldap_auth($username = '', $password = NULL) + { + if (empty($username)) + { + log_message('debug', 'LDAP Auth: failure, empty username'); + return false; + } + + log_message('debug', 'LDAP Auth: Loading Config'); + + $this->config->load('ldap.php', true); + + $ldaptimeout = $this->config->item('timeout', 'ldap'); + $ldaphost = $this->config->item('server', 'ldap'); + $ldapport = $this->config->item('port', 'ldap'); + $ldaprdn = $this->config->item('binduser', 'ldap'); + $ldappass = $this->config->item('bindpw', 'ldap'); + $ldapbasedn = $this->config->item('basedn', 'ldap'); + + log_message('debug', 'LDAP Auth: Connect to ' . $ldaphost); + + $ldapconfig['authrealm'] = $this->config->item('domain', 'ldap'); + + // connect to ldap server + $ldapconn = ldap_connect($ldaphost, $ldapport); + + if ($ldapconn) { + + log_message('debug', 'Setting timeout to ' . $ldaptimeout . ' seconds'); + + ldap_set_option($ldapconn, LDAP_OPT_NETWORK_TIMEOUT, $ldaptimeout); + + log_message('debug', 'LDAP Auth: Binding to ' . $ldaphost . ' with dn ' . $ldaprdn); + + // binding to ldap server + $ldapbind = ldap_bind($ldapconn, $ldaprdn, $ldappass); + + // verify binding + if ($ldapbind) { + log_message('debug', 'LDAP Auth: bind successful'); + } else { + log_message('error', 'LDAP Auth: bind unsuccessful'); + return false; + } + + } + + // search for user + if (($res_id = ldap_search( $ldapconn, $ldapbasedn, "uid=$username")) == false) { + log_message('error', 'LDAP Auth: User ' . $username . ' not found in search'); + return false; + } + + if (ldap_count_entries($ldapconn, $res_id) != 1) { + log_message('error', 'LDAP Auth: failure, username ' . $username . 'found more than once'); + return false; + } + + if (( $entry_id = ldap_first_entry($ldapconn, $res_id))== false) { + log_message('error', 'LDAP Auth: failure, entry of searchresult could not be fetched'); + return false; + } + + if (( $user_dn = ldap_get_dn($ldapconn, $entry_id)) == false) { + log_message('error', 'LDAP Auth: failure, user-dn could not be fetched'); + return false; + } + + // User found, could not authenticate as user + if (($link_id = ldap_bind($ldapconn, $user_dn, $password)) == false) { + log_message('error', 'LDAP Auth: failure, username/password did not match: ' . $user_dn); + return false; + } + + log_message('debug', 'LDAP Auth: Success ' . $user_dn . ' authenticated successfully'); + + $this->_user_ldap_dn = $user_dn; + ldap_close($ldapconn); + return true; + } + /** * Check if the user is logged in. * @@ -923,6 +1018,14 @@ protected function _check_login($username = '', $password = NULL) return FALSE; } + $auth_source = strtolower($this-config->item('auth_source')); + + if ($auth_source == 'ldap') + { + log_message('debug', 'performing LDAP authentication for $username'); + return $this->_perform_ldap_auth($username, $password); + } + $valid_logins = & $this->config->item('rest_valid_logins'); if ( ! array_key_exists($username, $valid_logins)) From bcacdfde352d667557244315dd31ce8cf8b3e176 Mon Sep 17 00:00:00 2001 From: Jonathan Creasy Date: Thu, 20 Sep 2012 22:45:42 -0700 Subject: [PATCH 076/651] adding LDAP authentication support --- application/config/ldap.php | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100755 application/config/ldap.php diff --git a/application/config/ldap.php b/application/config/ldap.php new file mode 100755 index 00000000..2293c132 --- /dev/null +++ b/application/config/ldap.php @@ -0,0 +1,21 @@ + From 8957763012be5a3470305e45833c8b1a7f48a1da Mon Sep 17 00:00:00 2001 From: Jonathan Creasy Date: Thu, 20 Sep 2012 23:03:14 -0700 Subject: [PATCH 077/651] adding LDAP authentication support --- application/config/rest.php | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/application/config/rest.php b/application/config/rest.php index be454733..10efde4e 100644 --- a/application/config/rest.php +++ b/application/config/rest.php @@ -44,11 +44,23 @@ | | Is login required and if so, which type of login? | -| '' = no login required, 'basic' = unsecure login, 'digest' = more secure login, 'ldap' = digest with ldap +| '' = no login required, 'basic' = unsecure login, 'digest' = more secure login | */ $config['rest_auth'] = false; +/* +|-------------------------------------------------------------------------- +| REST Login +|-------------------------------------------------------------------------- +| +| Is login required and if so, which user store do we use? +| +| '' = use config based users, 'ldap' = use LDAP authencation +| +*/ +$config['auth_source'] = 'ldap'; + /* |-------------------------------------------------------------------------- | Override auth types for specific class/method @@ -65,13 +77,12 @@ | $config['auth_override_class_method']['accounts']['user'] = 'basic'; | | Here 'deals' and 'accounts' are controller names, 'view', 'insert' and 'user' are methods within. (NOTE: leave off the '_get' or '_post' from the end of the method name) -| Acceptable values are; 'none', 'digest', 'ldap', and 'basic' +| Acceptable values are; 'none', 'digest', and 'basic' | */ // $config['auth_override_class_method']['deals']['view'] = 'none'; // $config['auth_override_class_method']['deals']['insert'] = 'digest'; // $config['auth_override_class_method']['accounts']['user'] = 'basic'; -// $config['auth_override_class_method']['accounts']['create'] = 'ldap'; /* |-------------------------------------------------------------------------- From 9876980cb774a984664fb046c7398c5d4ab7011a Mon Sep 17 00:00:00 2001 From: Jonathan Creasy Date: Thu, 20 Sep 2012 23:04:04 -0700 Subject: [PATCH 078/651] adding LDAP authentication support --- application/config/rest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/config/rest.php b/application/config/rest.php index 10efde4e..36207f94 100644 --- a/application/config/rest.php +++ b/application/config/rest.php @@ -77,7 +77,7 @@ | $config['auth_override_class_method']['accounts']['user'] = 'basic'; | | Here 'deals' and 'accounts' are controller names, 'view', 'insert' and 'user' are methods within. (NOTE: leave off the '_get' or '_post' from the end of the method name) -| Acceptable values are; 'none', 'digest', and 'basic' +| Acceptable values are; 'none', 'digest' and 'basic' | */ // $config['auth_override_class_method']['deals']['view'] = 'none'; From 44f5959bef6c015e555b181016f8566ca24f2d73 Mon Sep 17 00:00:00 2001 From: Jonathan Creasy Date: Thu, 20 Sep 2012 23:05:49 -0700 Subject: [PATCH 079/651] adding LDAP authentication support --- application/config/rest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/config/rest.php b/application/config/rest.php index 36207f94..144ecfac 100644 --- a/application/config/rest.php +++ b/application/config/rest.php @@ -77,7 +77,7 @@ | $config['auth_override_class_method']['accounts']['user'] = 'basic'; | | Here 'deals' and 'accounts' are controller names, 'view', 'insert' and 'user' are methods within. (NOTE: leave off the '_get' or '_post' from the end of the method name) -| Acceptable values are; 'none', 'digest' and 'basic' +| Acceptable values are; 'none', 'digest' and 'basic'. | */ // $config['auth_override_class_method']['deals']['view'] = 'none'; From 09b8f888aaf2f180cca814ba14f62fe78f64096e Mon Sep 17 00:00:00 2001 From: Butch Ewing Date: Tue, 23 Oct 2012 17:49:18 -0500 Subject: [PATCH 080/651] Corrected version number Corrected version number from 2.6.0 to 2.6.1 --- application/libraries/REST_Controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index b0fdfddc..1af06691 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -11,7 +11,7 @@ * @author Phil Sturgeon * @license http://philsturgeon.co.uk/code/dbad-license * @link https://github.com/philsturgeon/codeigniter-restserver - * @version 2.6.0 + * @version 2.6.1 */ abstract class REST_Controller extends CI_Controller { From ea8260d6b025f9d3a223d019a7d8e0436f1ecb63 Mon Sep 17 00:00:00 2001 From: onema Date: Mon, 19 Nov 2012 12:11:40 -0500 Subject: [PATCH 081/651] added some comments --- application/libraries/REST_Controller.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 73fa759d..b942341b 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -611,12 +611,16 @@ protected function _detect_api_key() isset($row->ignore_limits) AND $this->rest->ignore_limits = $row->ignore_limits; */ + /* + * If "is private key" is enabled, compare the ip address with the list + * of valid ip addresses stored in the database. + */ if(!empty($row->is_private_key)) { - // Check for a list of valid ip addresses if(isset($row->ip_addresses)) { + // multiple ip addresses must be separated using a comma, explode and loop $list_ip_addresses = explode(",", $row->ip_addresses); $found_address = FALSE; @@ -624,6 +628,7 @@ protected function _detect_api_key() { if($this->input->ip_address() == trim($ip_address)) { + // there is a match, set the the value to true and break out of the loop $found_address = TRUE; break; } From a38b87178cf28fba5548e60f872308ce05ae403e Mon Sep 17 00:00:00 2001 From: Phil Sturgeon Date: Wed, 28 Nov 2012 22:12:25 -0500 Subject: [PATCH 082/651] Removed commented out code I'm sure that was commented out for a reason but that should definitely be in there. --- application/libraries/REST_Controller.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index d8b072a0..c4ddda39 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -65,7 +65,7 @@ abstract class REST_Controller extends CI_Controller * * @var object */ - protected $client = NULL; + protected $client = NULL; /** * The arguments for the GET request method @@ -265,7 +265,7 @@ public function _remap($object_called, $arguments) // Should we answer if not over SSL? if (config_item('force_https') AND !$this->_detect_ssl()) { - $this->response(array('status' => false, 'error' => 'Unsupported protocol'), 403); + $this->response(array('status' => false, 'error' => 'Unsupported protocol'), 403); } $pattern = '/^(.*)\.('.implode('|', array_keys($this->_supported_formats)).')$/'; @@ -280,7 +280,7 @@ public function _remap($object_called, $arguments) $log_method = !(isset($this->methods[$controller_method]['log']) AND $this->methods[$controller_method]['log'] == FALSE); // Use keys for this method? - $use_key = !(isset($this->methods[$controller_method]['key']) AND $this->methods[$controller_method]['key'] == FALSE); + $use_key = ! (isset($this->methods[$controller_method]['key']) AND $this->methods[$controller_method]['key'] == FALSE); // Get that useless shitty key out of here if (config_item('rest_enable_keys') AND $use_key AND $this->_allow === FALSE) @@ -605,11 +605,9 @@ protected function _detect_api_key() $this->rest->key = $this->client->{config_item('rest_key_column')}; - /* isset($row->user_id) AND $this->rest->user_id = $row->user_id; isset($row->level) AND $this->rest->level = $row->level; isset($row->ignore_limits) AND $this->rest->ignore_limits = $row->ignore_limits; - */ return $this->client; } From 7e021e4b6067dc8ca8d14e78f63af80eef873b9a Mon Sep 17 00:00:00 2001 From: Rogerio Prado de Jesus Date: Fri, 7 Dec 2012 13:27:49 -0200 Subject: [PATCH 083/651] Fix #166: Typo on README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b244c9d2..b3d351ab 100644 --- a/README.md +++ b/README.md @@ -161,7 +161,7 @@ By default, the HTTP will be `X-API-KEY`. This can be configured in **config/res * Added loads of PHPDoc comments. * Response where method doesn't exist is now "HTTP 405 Method Not Allowed", not "HTTP 404 Not Found". -* Compatable with PHP 5.4. +* Compatible with PHP 5.4. * Added support for gzip compression. * Fix the apache\_request\_header function with CGI. * Fixed up correctly .foo extensions to work when get arguments provided. From 52a78e9de86bc2e58bff05fd448307f494380b6e Mon Sep 17 00:00:00 2001 From: Stolz Date: Sat, 8 Dec 2012 16:19:19 +0100 Subject: [PATCH 084/651] Removed unnecessary bracket --- application/libraries/REST_Controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index c4ddda39..aed872f3 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -432,7 +432,7 @@ public function response($data = array(), $http_code = null) */ protected function _detect_ssl() { - return (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == "on")); + return (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == "on"); } From 0d1a4c843f205678020c1075fe54657731d00cb8 Mon Sep 17 00:00:00 2001 From: onema Date: Mon, 10 Dec 2012 12:50:18 -0800 Subject: [PATCH 085/651] removed extra parenthesis in detect_ssl method --- application/libraries/REST_Controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 652a5def..6a5e9972 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -432,7 +432,7 @@ public function response($data = array(), $http_code = null) */ protected function _detect_ssl() { - return (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == "on")); + return (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == "on"); } From bbf0d903942a4cd9e99170e55bfb5ed8f764545e Mon Sep 17 00:00:00 2001 From: Repox Date: Wed, 19 Dec 2012 08:46:51 +0100 Subject: [PATCH 086/651] Update CodeIgniter files to 2.1.3 --- system/core/CodeIgniter.php | 2 +- system/core/Common.php | 2 +- system/core/Config.php | 12 ++-- system/core/Input.php | 71 +++++++++---------- system/core/Security.php | 51 +++++++------ system/database/drivers/oci8/oci8_result.php | 10 ++- system/database/drivers/pdo/pdo_result.php | 23 +++--- system/libraries/Cache/drivers/Cache_file.php | 11 ++- system/libraries/Migration.php | 32 +++------ system/libraries/Profiler.php | 2 +- system/libraries/Session.php | 7 +- 11 files changed, 106 insertions(+), 117 deletions(-) diff --git a/system/core/CodeIgniter.php b/system/core/CodeIgniter.php index cd333333..c16c79c0 100755 --- a/system/core/CodeIgniter.php +++ b/system/core/CodeIgniter.php @@ -33,7 +33,7 @@ * @var string * */ - define('CI_VERSION', '2.1.2'); + define('CI_VERSION', '2.1.3'); /** * CodeIgniter Branch (Core = TRUE, Reactor = FALSE) diff --git a/system/core/Common.php b/system/core/Common.php index d7937547..07534c51 100644 --- a/system/core/Common.php +++ b/system/core/Common.php @@ -187,7 +187,7 @@ function &load_class($class, $directory = 'libraries', $prefix = 'CI_') */ if ( ! function_exists('is_loaded')) { - function is_loaded($class = '') + function &is_loaded($class = '') { static $_is_loaded = array(); diff --git a/system/core/Config.php b/system/core/Config.php index 714c4667..5dffbf3f 100755 --- a/system/core/Config.php +++ b/system/core/Config.php @@ -99,12 +99,12 @@ function load($file = '', $use_sections = FALSE, $fail_gracefully = FALSE) $found = FALSE; $loaded = FALSE; + $check_locations = defined('ENVIRONMENT') + ? array(ENVIRONMENT.'/'.$file, $file) + : array($file); + foreach ($this->_config_paths as $path) { - $check_locations = defined('ENVIRONMENT') - ? array(ENVIRONMENT.'/'.$file, $file) - : array($file); - foreach ($check_locations as $location) { $file_path = $path.'config/'.$location.'.php'; @@ -168,7 +168,7 @@ function load($file = '', $use_sections = FALSE, $fail_gracefully = FALSE) { return FALSE; } - show_error('The configuration file '.$file.'.php'.' does not exist.'); + show_error('The configuration file '.$file.'.php does not exist.'); } return TRUE; @@ -279,7 +279,7 @@ function site_url(http://github.com/$uri%20=%20'') */ function base_url(http://github.com/$uri%20=%20'') { - return $this->slash_item('base_url').ltrim($this->_uri_string($uri),'/'); + return $this->slash_item('base_url').ltrim($this->_uri_string($uri), '/'); } // ------------------------------------------------------------- diff --git a/system/core/Input.php b/system/core/Input.php index 3559d860..0c1f2b08 100755 --- a/system/core/Input.php +++ b/system/core/Input.php @@ -73,13 +73,13 @@ class CI_Input { */ protected $headers = array(); - /** * Constructor * * Sets whether to globally enable the XSS processing * and whether to allow the $_GET array * + * @return void */ public function __construct() { @@ -306,50 +306,49 @@ function server($index = '', $xss_clean = FALSE) /** * Fetch the IP Address * - * @access public * @return string */ - function ip_address() + public function ip_address() { if ($this->ip_address !== FALSE) { return $this->ip_address; } - if (config_item('proxy_ips') != '' && $this->server('HTTP_X_FORWARDED_FOR') && $this->server('REMOTE_ADDR')) + $proxy_ips = config_item('proxy_ips'); + if ( ! empty($proxy_ips)) { - $proxies = preg_split('/[\s,]/', config_item('proxy_ips'), -1, PREG_SPLIT_NO_EMPTY); - $proxies = is_array($proxies) ? $proxies : array($proxies); + $proxy_ips = explode(',', str_replace(' ', '', $proxy_ips)); + foreach (array('HTTP_X_FORWARDED_FOR', 'HTTP_CLIENT_IP', 'HTTP_X_CLIENT_IP', 'HTTP_X_CLUSTER_CLIENT_IP') as $header) + { + if (($spoof = $this->server($header)) !== FALSE) + { + // Some proxies typically list the whole chain of IP + // addresses through which the client has reached us. + // e.g. client_ip, proxy_ip1, proxy_ip2, etc. + if (strpos($spoof, ',') !== FALSE) + { + $spoof = explode(',', $spoof, 2); + $spoof = $spoof[0]; + } - $this->ip_address = in_array($_SERVER['REMOTE_ADDR'], $proxies) ? $_SERVER['HTTP_X_FORWARDED_FOR'] : $_SERVER['REMOTE_ADDR']; - } - elseif ($this->server('REMOTE_ADDR') AND $this->server('HTTP_CLIENT_IP')) - { - $this->ip_address = $_SERVER['HTTP_CLIENT_IP']; - } - elseif ($this->server('REMOTE_ADDR')) - { - $this->ip_address = $_SERVER['REMOTE_ADDR']; - } - elseif ($this->server('HTTP_CLIENT_IP')) - { - $this->ip_address = $_SERVER['HTTP_CLIENT_IP']; - } - elseif ($this->server('HTTP_X_FORWARDED_FOR')) - { - $this->ip_address = $_SERVER['HTTP_X_FORWARDED_FOR']; - } + if ( ! $this->valid_ip($spoof)) + { + $spoof = FALSE; + } + else + { + break; + } + } + } - if ($this->ip_address === FALSE) - { - $this->ip_address = '0.0.0.0'; - return $this->ip_address; + $this->ip_address = ($spoof !== FALSE && in_array($_SERVER['REMOTE_ADDR'], $proxy_ips, TRUE)) + ? $spoof : $_SERVER['REMOTE_ADDR']; } - - if (strpos($this->ip_address, ',') !== FALSE) + else { - $x = explode(',', $this->ip_address); - $this->ip_address = trim(end($x)); + $this->ip_address = $_SERVER['REMOTE_ADDR']; } if ( ! $this->valid_ip($this->ip_address)) @@ -642,8 +641,8 @@ function _sanitize_globals() $_SERVER['PHP_SELF'] = strip_tags($_SERVER['PHP_SELF']); - // CSRF Protection check - if ($this->_enable_csrf == TRUE) + // CSRF Protection check on HTTP requests + if ($this->_enable_csrf == TRUE && ! $this->is_cli_request()) { $this->security->csrf_verify(); } @@ -837,11 +836,11 @@ public function is_ajax_request() * * Test to see if a request was made from the command line * - * @return boolean + * @return bool */ public function is_cli_request() { - return (php_sapi_name() == 'cli') or defined('STDIN'); + return (php_sapi_name() === 'cli' OR defined('STDIN')); } } diff --git a/system/core/Security.php b/system/core/Security.php index 7af240de..00089d76 100755 --- a/system/core/Security.php +++ b/system/core/Security.php @@ -98,26 +98,32 @@ class CI_Security { /** * Constructor + * + * @return void */ public function __construct() { - // CSRF config - foreach(array('csrf_expire', 'csrf_token_name', 'csrf_cookie_name') as $key) + // Is CSRF protection enabled? + if (config_item('csrf_protection') === TRUE) { - if (FALSE !== ($val = config_item($key))) + // CSRF config + foreach (array('csrf_expire', 'csrf_token_name', 'csrf_cookie_name') as $key) { - $this->{'_'.$key} = $val; + if (FALSE !== ($val = config_item($key))) + { + $this->{'_'.$key} = $val; + } } - } - // Append application specific cookie prefix - if (config_item('cookie_prefix')) - { - $this->_csrf_cookie_name = config_item('cookie_prefix').$this->_csrf_cookie_name; - } + // Append application specific cookie prefix + if (config_item('cookie_prefix')) + { + $this->_csrf_cookie_name = config_item('cookie_prefix').$this->_csrf_cookie_name; + } - // Set the CSRF hash - $this->_csrf_set_hash(); + // Set the CSRF hash + $this->_csrf_set_hash(); + } log_message('debug', "Security Class Initialized"); } @@ -131,15 +137,14 @@ public function __construct() */ public function csrf_verify() { - // If no POST data exists we will set the CSRF cookie - if (count($_POST) == 0) + // If it's not a POST request we will set the CSRF cookie + if (strtoupper($_SERVER['REQUEST_METHOD']) !== 'POST') { return $this->csrf_set_cookie(); } // Do the tokens exist in both the _POST and _COOKIE arrays? - if ( ! isset($_POST[$this->_csrf_token_name]) OR - ! isset($_COOKIE[$this->_csrf_cookie_name])) + if ( ! isset($_POST[$this->_csrf_token_name], $_COOKIE[$this->_csrf_cookie_name])) { $this->csrf_show_error(); } @@ -159,7 +164,7 @@ public function csrf_verify() $this->_csrf_set_hash(); $this->csrf_set_cookie(); - log_message('debug', "CSRF token verified "); + log_message('debug', 'CSRF token verified'); return $this; } @@ -176,14 +181,9 @@ public function csrf_set_cookie() $expire = time() + $this->_csrf_expire; $secure_cookie = (config_item('cookie_secure') === TRUE) ? 1 : 0; - if ($secure_cookie) + if ($secure_cookie && (empty($_SERVER['HTTPS']) OR strtolower($_SERVER['HTTPS']) === 'off')) { - $req = isset($_SERVER['HTTPS']) ? $_SERVER['HTTPS'] : FALSE; - - if ( ! $req OR $req == 'off') - { - return FALSE; - } + return FALSE; } setcookie($this->_csrf_cookie_name, $this->_csrf_hash, $expire, config_item('cookie_path'), config_item('cookie_domain'), $secure_cookie); @@ -871,7 +871,6 @@ protected function _csrf_set_hash() } } -// END Security Class /* End of file Security.php */ -/* Location: ./system/libraries/Security.php */ +/* Location: ./system/libraries/Security.php */ \ No newline at end of file diff --git a/system/database/drivers/oci8/oci8_result.php b/system/database/drivers/oci8/oci8_result.php index ae133d7b..3421278a 100644 --- a/system/database/drivers/oci8/oci8_result.php +++ b/system/database/drivers/oci8/oci8_result.php @@ -26,9 +26,9 @@ */ class CI_DB_oci8_result extends CI_DB_result { - var $stmt_id; - var $curs_id; - var $limit_used; + public $stmt_id; + public $curs_id; + public $limit_used; /** * Number of rows in the result set. @@ -36,8 +36,6 @@ class CI_DB_oci8_result extends CI_DB_result { * Oracle doesn't have a graceful way to retun the number of rows * so we have to use what amounts to a hack. * - * - * @access public * @return integer */ public function num_rows() @@ -53,7 +51,7 @@ public function num_rows() } } - return $rowcount; + return $this->num_rows; } // -------------------------------------------------------------------- diff --git a/system/database/drivers/pdo/pdo_result.php b/system/database/drivers/pdo/pdo_result.php index a366a5f1..44fdd6dc 100644 --- a/system/database/drivers/pdo/pdo_result.php +++ b/system/database/drivers/pdo/pdo_result.php @@ -26,26 +26,27 @@ */ class CI_DB_pdo_result extends CI_DB_result { + public $num_rows; + /** * Number of rows in the result set * - * @access public - * @return integer + * @return int */ - function num_rows() + public function num_rows() { - if (is_numeric(stripos($this->result_id->queryString, 'SELECT'))) + if (is_int($this->num_rows)) { - $dbh = $this->conn_id; - $query = $dbh->query($this->result_id->queryString); - $result = $query->fetchAll(); - unset($dbh, $query); - return count($result); + return $this->num_rows; } - else + elseif (($this->num_rows = $this->result_id->rowCount()) > 0) { - return $this->result_id->rowCount(); + return $this->num_rows; } + + $this->num_rows = count($this->result_id->fetchAll()); + $this->result_id->execute(); + return $this->num_rows; } // -------------------------------------------------------------------- diff --git a/system/libraries/Cache/drivers/Cache_file.php b/system/libraries/Cache/drivers/Cache_file.php index c5004366..e515eebf 100644 --- a/system/libraries/Cache/drivers/Cache_file.php +++ b/system/libraries/Cache/drivers/Cache_file.php @@ -151,13 +151,12 @@ public function get_metadata($id) { return FALSE; } - - $data = read_file($this->_cache_path.$id); + + $data = read_file($this->_cache_path.$id); $data = unserialize($data); - + if (is_array($data)) { - $data = $data['data']; $mtime = filemtime($this->_cache_path.$id); if ( ! isset($data['ttl'])) @@ -166,11 +165,11 @@ public function get_metadata($id) } return array( - 'expire' => $mtime + $data['ttl'], + 'expire' => $mtime + $data['ttl'], 'mtime' => $mtime ); } - + return FALSE; } diff --git a/system/libraries/Migration.php b/system/libraries/Migration.php index 5a41377e..df2dd7ce 100644 --- a/system/libraries/Migration.php +++ b/system/libraries/Migration.php @@ -57,7 +57,7 @@ public function __construct($config = array()) } // If not set, set it - $this->_migration_path == '' OR $this->_migration_path = APPPATH . 'migrations/'; + $this->_migration_path == '' AND $this->_migration_path = APPPATH . 'migrations/'; // Add trailing slash if not set $this->_migration_path = rtrim($this->_migration_path, '/').'/'; @@ -89,8 +89,7 @@ public function __construct($config = array()) * Calls each migration step required to get to the schema version of * choice * - * @access public - * @param $version integer Target schema version + * @param int Target schema version * @return mixed TRUE if already latest, FALSE if failed, int if upgraded */ public function version($target_version) @@ -105,14 +104,13 @@ public function version($target_version) ++$stop; $step = 1; } - else { // Moving Down $step = -1; } - - $method = $step === 1 ? 'up' : 'down'; + + $method = ($step === 1) ? 'up' : 'down'; $migrations = array(); // We now prepare to actually DO the migrations @@ -216,7 +214,6 @@ public function version($target_version) /** * Set's the schema to the latest migration * - * @access public * @return mixed true if already latest, false if failed, int if upgraded */ public function latest() @@ -228,7 +225,7 @@ public function latest() } $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)); @@ -239,7 +236,6 @@ public function latest() /** * Set's the schema to the migration version set in config * - * @access public * @return mixed true if already current, false if failed, int if upgraded */ public function current() @@ -252,7 +248,6 @@ public function current() /** * Error string * - * @access public * @return string Error message returned as a string */ public function error_string() @@ -265,7 +260,6 @@ public function error_string() /** * Set's the schema to the latest migration * - * @access protected * @return mixed true if already latest, false if failed, int if upgraded */ protected function find_migrations() @@ -273,7 +267,7 @@ protected function find_migrations() // Load all *_*.php files in the migrations path $files = glob($this->_migration_path . '*_*.php'); $file_count = count($files); - + for ($i = 0; $i < $file_count; $i++) { // Mark wrongly formatted files as false for later filtering @@ -283,9 +277,8 @@ protected function find_migrations() $files[$i] = FALSE; } } - - sort($files); + sort($files); return $files; } @@ -294,8 +287,7 @@ protected function find_migrations() /** * Retrieves current schema version * - * @access protected - * @return integer Current Migration + * @return int Current Migration */ protected function _get_version() { @@ -308,9 +300,8 @@ protected function _get_version() /** * Stores the current schema version * - * @access protected - * @param $migrations integer Migration reached - * @return void Outputs a report of the migration + * @param int Migration reached + * @return bool */ protected function _update_version($migrations) { @@ -324,8 +315,7 @@ protected function _update_version($migrations) /** * Enable the use of CI super-global * - * @access public - * @param $var + * @param mixed $var * @return mixed */ public function __get($var) diff --git a/system/libraries/Profiler.php b/system/libraries/Profiler.php index 082a5ee1..882a82c1 100644 --- a/system/libraries/Profiler.php +++ b/system/libraries/Profiler.php @@ -506,7 +506,7 @@ private function _compile_session_data() foreach ($this->CI->session->all_userdata() as $key => $val) { - if (is_array($val)) + if (is_array($val) OR is_object($val)) { $val = print_r($val, TRUE); } diff --git a/system/libraries/Session.php b/system/libraries/Session.php index 8ee08c5b..891fdd36 100644 --- a/system/libraries/Session.php +++ b/system/libraries/Session.php @@ -97,7 +97,7 @@ public function __construct($params = array()) { $this->sess_expiration = (60*60*24*365*2); } - + // Set the cookie name $this->sess_cookie_name = $this->cookie_prefix.$this->sess_cookie_name; @@ -399,7 +399,7 @@ function sess_update() function sess_destroy() { // Kill the session DB row - if ($this->sess_use_database === TRUE AND isset($this->userdata['session_id'])) + 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); @@ -414,6 +414,9 @@ function sess_destroy() $this->cookie_domain, 0 ); + + // Kill session data + $this->userdata = array(); } // -------------------------------------------------------------------- From 33e86b1695c4f32608f3f55fffd92ef7dcc21017 Mon Sep 17 00:00:00 2001 From: Repox Date: Wed, 19 Dec 2012 08:51:32 +0100 Subject: [PATCH 087/651] Fixes issue #165 --- application/libraries/REST_Controller.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 6a5e9972..cc54a9fc 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -598,12 +598,12 @@ protected function _detect_api_key() // Find the key from server or arguments if (($key = isset($this->_args[$api_key_variable]) ? $this->_args[$api_key_variable] : $this->input->server($key_name))) { - if ( ! ($this->client = $this->rest->db->where(config_item('rest_key_column'), $key)->get(config_item('rest_keys_table'))->row())) + if ( ! ($row = $this->rest->db->where(config_item('rest_key_column'), $key)->get(config_item('rest_keys_table'))->row())) { return FALSE; } - $this->rest->key = $this->client->{config_item('rest_key_column')}; + $this->rest->key = $row->{config_item('rest_key_column')}; isset($row->user_id) AND $this->rest->user_id = $row->user_id; isset($row->level) AND $this->rest->level = $row->level; From 63f09cf34af7cfc5f5792df42b2aec6cd6bbdf6d Mon Sep 17 00:00:00 2001 From: Repox Date: Wed, 19 Dec 2012 13:22:20 +0100 Subject: [PATCH 088/651] Consistency edit Setting the class member $client for compability purposes. --- application/libraries/REST_Controller.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index cc54a9fc..103b2116 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -640,7 +640,9 @@ protected function _detect_api_key() return FALSE; } } - + + // Set the class member for compatility purposes + $this->client = $row; return $this->client; } From 573149a38026c9e5d4ecaceeec9be92a67f5ca27 Mon Sep 17 00:00:00 2001 From: Repox Date: Wed, 19 Dec 2012 13:30:21 +0100 Subject: [PATCH 089/651] Updated change log --- README.md | 5 +++++ application/libraries/REST_Controller.php | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b3d351ab..e835745d 100644 --- a/README.md +++ b/README.md @@ -146,6 +146,11 @@ By default, the HTTP will be `X-API-KEY`. This can be configured in **config/res ## Change Log +### 2.6.2 + +* Update CodeIgniter files to 2.1.3 +* Fixed issue #165 + ### 2.6.1 * Update CodeIgniter files to 2.1.2 diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 103b2116..4c57a4e9 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -11,7 +11,7 @@ * @author Phil Sturgeon * @license http://philsturgeon.co.uk/code/dbad-license * @link https://github.com/philsturgeon/codeigniter-restserver - * @version 2.6.1 + * @version 2.6.2 */ abstract class REST_Controller extends CI_Controller { From bf060af161f62b1f8f1d978fd999f21c384cfb16 Mon Sep 17 00:00:00 2001 From: Repox Date: Mon, 7 Jan 2013 15:09:58 +0100 Subject: [PATCH 090/651] Removed references to the class member `client` --- application/libraries/REST_Controller.php | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 4c57a4e9..567670fd 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -60,13 +60,6 @@ abstract class REST_Controller extends CI_Controller */ protected $rest = NULL; - /** - * Object to store data about the client sending the request - * - * @var object - */ - protected $client = NULL; - /** * The arguments for the GET request method * @@ -641,9 +634,7 @@ protected function _detect_api_key() } } - // Set the class member for compatility purposes - $this->client = $row; - return $this->client; + return $row; } // No key has been sent From 8191ab82689ef90734562ae053867206e52b5b62 Mon Sep 17 00:00:00 2001 From: jrmadsen67 Date: Tue, 8 Jan 2013 17:37:08 +0900 Subject: [PATCH 091/651] two typos in REST_Controller ldap implementation causes whole thing to bork --- application/libraries/REST_Controller.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 0e5fddfb..c421f405 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -114,7 +114,7 @@ abstract class REST_Controller extends CI_Controller * * @var string */ - protected $_user_ldap_dn = '' + protected $_user_ldap_dn = ''; /** * List all supported methods, the first will be the default format @@ -1072,7 +1072,7 @@ protected function _check_login($username = '', $password = NULL) return FALSE; } - $auth_source = strtolower($this-config->item('auth_source')); + $auth_source = strtolower($this->config->item('auth_source')); if ($auth_source == 'ldap') { From af239adbe017378b897d139a3f2722f024c5f29e Mon Sep 17 00:00:00 2001 From: Phil Sturgeon Date: Wed, 9 Jan 2013 22:53:44 -0500 Subject: [PATCH 092/651] Enabled Security --- application/libraries/REST_Controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index c421f405..552a0789 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -170,7 +170,7 @@ public function __construct() // Set up our GET variables $this->_get_args = array_merge($this->_get_args, $this->uri->ruri_to_assoc()); - //$this->load->library('security'); + $this->load->library('security'); // This library is bundled with REST_Controller 2.5+, but will eventually be part of CodeIgniter itself $this->load->library('format'); From 5b017052623359a523e991927e40ee8158dfc426 Mon Sep 17 00:00:00 2001 From: Burak Erdem Date: Tue, 22 Jan 2013 00:45:59 +0200 Subject: [PATCH 093/651] Fixed SQL to create `logs` table. --- application/config/rest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/config/rest.php b/application/config/rest.php index c68909ad..7c47794d 100644 --- a/application/config/rest.php +++ b/application/config/rest.php @@ -256,7 +256,7 @@ `api_key` varchar(40) NOT NULL, `ip_address` varchar(45) NOT NULL, `time` int(11) NOT NULL, - `authorized` tinyint(1) NOT NULL + `authorized` tinyint(1) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; | From ba40e833ef834dac21bce4d6d34e04f8dd7493e8 Mon Sep 17 00:00:00 2001 From: Burak Erdem Date: Tue, 22 Jan 2013 00:53:00 +0200 Subject: [PATCH 094/651] Fixed sample code at multilingual section. --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index e835745d..676c7f10 100644 --- a/README.md +++ b/README.md @@ -83,19 +83,19 @@ If you don't specify a response code, and the data you respond with `== FALSE` ( ## Multilingual Support -If your application uses language files to support multiple locales, `REST_Controller` will automatically parse the HTTP `Accept-Language` header and provide the language(s) in your actions. This information can be found in the `$this->request->lang` object: +If your application uses language files to support multiple locales, `REST_Controller` will automatically parse the HTTP `Accept-Language` header and provide the language(s) in your actions. This information can be found in the `$this->response->lang` object: public function __construct() { parent::__construct(); - if (is_array($this->request->lang)) + if (is_array($this->response->lang)) { - $this->load->language('application', $this->request->lang[0]); + $this->load->language('application', $this->response->lang[0]); } else { - $this->load->language('application', $this->request->lang); + $this->load->language('application', $this->response->lang); } } @@ -220,4 +220,4 @@ features for this as I no longer require it, but I will continue to maintain pul fix issues as and when they are reported (within a week or two). Pull Requests are the best way to fix bugs or add features. I know loads of you use this, so please -contribute if you have improvements to be made and I'll keep releasing versions over time. \ No newline at end of file +contribute if you have improvements to be made and I'll keep releasing versions over time. From 96e54e259ae90205e1a79a8242e45d913c2a69d1 Mon Sep 17 00:00:00 2001 From: Jason Horwitz Date: Wed, 30 Jan 2013 01:41:12 -0500 Subject: [PATCH 095/651] Add a rest config option to log api params as JSON instead of serialized. --- application/config/rest.php | 13 ++++++++++++- application/libraries/REST_Controller.php | 22 +++++++++++----------- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/application/config/rest.php b/application/config/rest.php index 7c47794d..b7363674 100644 --- a/application/config/rest.php +++ b/application/config/rest.php @@ -194,7 +194,7 @@ | REST Table Key Column Name |-------------------------------------------------------------------------- | -| If you are not using the default table schema as shown above, what is the +| If you are not using the default table schema as shown above, what is the | name of the db column that holds the api key value? | */ @@ -263,6 +263,17 @@ */ $config['rest_enable_logging'] = FALSE; +/* +|-------------------------------------------------------------------------- +| REST API Param Log Format +|-------------------------------------------------------------------------- +| +| When set to true API log params will be stored in the database as JSON, +| when false they will be php serialized. +| +*/ +$config['rest_logs_json_params'] = FALSE; + /* |-------------------------------------------------------------------------- | REST API Limits Table Name diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 552a0789..49e52f8b 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -154,10 +154,10 @@ public function __construct() // let's learn about the request $this->request = new stdClass(); - + // Is it over SSL? $this->request->ssl = $this->_detect_ssl(); - + // How is this request being made? POST, DELETE, GET, PUT? $this->request->method = $this->_detect_method(); @@ -265,9 +265,9 @@ public function _remap($object_called, $arguments) // Should we answer if not over SSL? if (config_item('force_https') AND !$this->_detect_ssl()) { - $this->response(array('status' => false, 'error' => 'Unsupported protocol'), 403); + $this->response(array('status' => false, 'error' => 'Unsupported protocol'), 403); } - + $pattern = '/^(.*)\.('.implode('|', array_keys($this->_supported_formats)).')$/'; if (preg_match($pattern, $object_called, $matches)) { @@ -434,8 +434,8 @@ protected function _detect_ssl() { return (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == "on"); } - - + + /* * Detect input format * @@ -608,7 +608,7 @@ protected function _detect_api_key() isset($row->user_id) AND $this->rest->user_id = $row->user_id; isset($row->level) AND $this->rest->level = $row->level; isset($row->ignore_limits) AND $this->rest->ignore_limits = $row->ignore_limits; - + /* * If "is private key" is enabled, compare the ip address with the list * of valid ip addresses stored in the database. @@ -621,7 +621,7 @@ protected function _detect_api_key() // multiple ip addresses must be separated using a comma, explode and loop $list_ip_addresses = explode(",", $row->ip_addresses); $found_address = FALSE; - + foreach($list_ip_addresses as $ip_address) { if($this->input->ip_address() == trim($ip_address)) @@ -631,7 +631,7 @@ protected function _detect_api_key() break; } } - + return $found_address; } else @@ -640,7 +640,7 @@ protected function _detect_api_key() return FALSE; } } - + return $row; } @@ -696,7 +696,7 @@ protected function _log_request($authorized = FALSE) return $this->rest->db->insert(config_item('rest_logs_table'), array( 'uri' => $this->uri->uri_string(), 'method' => $this->request->method, - 'params' => $this->_args ? serialize($this->_args) : null, + 'params' => $this->_args ? (config_item('rest_logs_json_params') ? json_encode($this->_args) : serialize($this->_args)) : null, 'api_key' => isset($this->rest->key) ? $this->rest->key : '', 'ip_address' => $this->input->ip_address(), 'time' => function_exists('now') ? now() : time(), From 29975a23b458e73453481367097d81307869b153 Mon Sep 17 00:00:00 2001 From: Caio Iglesias Date: Fri, 1 Feb 2013 16:45:42 -0200 Subject: [PATCH 096/651] http code 204 was causing an error 500 When trying to send back $this->response(null, 204) the code would crash when handling the empty data. --- application/libraries/REST_Controller.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 552a0789..b89836b2 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -368,6 +368,12 @@ public function response($data = array(), $http_code = null) $output = NULL; } + // If data is empty but http code provided, keep the output empty + else if (empty($data) && is_numeric($http_code)) + { + $output = NULL; + } + // Otherwise (if no data but 200 provided) or some data, carry on camping! else { From c9c1d5f6a87fa6feee89db5cc5ef2b86093fe358 Mon Sep 17 00:00:00 2001 From: Bo Thomsen Date: Mon, 25 Feb 2013 12:55:14 +0100 Subject: [PATCH 097/651] Added PATCH, HEAD and OPTIONS --- application/libraries/REST_Controller.php | 155 ++++++++++++++++++---- 1 file changed, 130 insertions(+), 25 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 15e4a483..533cb4d6 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -13,7 +13,7 @@ * @link https://github.com/philsturgeon/codeigniter-restserver * @version 2.6.2 */ -abstract class REST_Controller extends CI_Controller +class REST_Controller extends CI_Controller { /** * This defines the rest format. @@ -36,7 +36,7 @@ abstract class REST_Controller extends CI_Controller * * @var array */ - protected $allowed_http_methods = array('get', 'delete', 'post', 'put'); + protected $allowed_http_methods = array('get', 'delete', 'post', 'put', 'options', 'patch', 'head'); /** * General request data and information. @@ -88,6 +88,27 @@ abstract class REST_Controller extends CI_Controller */ protected $_delete_args = array(); + /** + * The arguments for the PATCH request method + * + * @var array + */ + protected $_patch_args = array(); + + /** + * The arguments for the HEAD request method + * + * @var array + */ + protected $_head_args = array(); + + /** + * The arguments for the OPTIONS request method + * + * @var array + */ + protected $_options_args = array(); + /** * The arguments from GET, POST, PUT, DELETE request methods combined. * @@ -154,10 +175,10 @@ public function __construct() // let's learn about the request $this->request = new stdClass(); - + // Is it over SSL? $this->request->ssl = $this->_detect_ssl(); - + // How is this request being made? POST, DELETE, GET, PUT? $this->request->method = $this->_detect_method(); @@ -170,7 +191,7 @@ public function __construct() // Set up our GET variables $this->_get_args = array_merge($this->_get_args, $this->uri->ruri_to_assoc()); - $this->load->library('security'); + //$this->load->library('security'); // This library is bundled with REST_Controller 2.5+, but will eventually be part of CodeIgniter itself $this->load->library('format'); @@ -192,7 +213,7 @@ public function __construct() } // Merge both for one mega-args variable - $this->_args = array_merge($this->_get_args, $this->_put_args, $this->_post_args, $this->_delete_args, $this->{'_'.$this->request->method.'_args'}); + $this->_args = array_merge($this->_get_args, $this->_options_args, $this->_patch_args, $this->_head_args , $this->_put_args, $this->_post_args, $this->_delete_args, $this->{'_'.$this->request->method.'_args'}); // Which format should the data be returned in? $this->response = new stdClass(); @@ -265,9 +286,9 @@ public function _remap($object_called, $arguments) // Should we answer if not over SSL? if (config_item('force_https') AND !$this->_detect_ssl()) { - $this->response(array('status' => false, 'error' => 'Unsupported protocol'), 403); + $this->response(array('status' => false, 'error' => 'Unsupported protocol'), 403); } - + $pattern = '/^(.*)\.('.implode('|', array_keys($this->_supported_formats)).')$/'; if (preg_match($pattern, $object_called, $matches)) { @@ -368,16 +389,9 @@ public function response($data = array(), $http_code = null) $output = NULL; } - // If data is empty but http code provided, keep the output empty - else if (empty($data) && is_numeric($http_code)) - { - $output = NULL; - } - // Otherwise (if no data but 200 provided) or some data, carry on camping! - else - { - // Is compression requested? + else { + // Is compression requested? if ($CFG->item('compress_output') === TRUE && $this->_zlib_oc == FALSE) { if (extension_loaded('zlib')) @@ -427,7 +441,7 @@ public function response($data = array(), $http_code = null) { header('Content-Length: ' . strlen($output)); } - + exit($output); } @@ -440,8 +454,8 @@ protected function _detect_ssl() { return (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == "on"); } - - + + /* * Detect input format * @@ -614,7 +628,7 @@ protected function _detect_api_key() isset($row->user_id) AND $this->rest->user_id = $row->user_id; isset($row->level) AND $this->rest->level = $row->level; isset($row->ignore_limits) AND $this->rest->ignore_limits = $row->ignore_limits; - + /* * If "is private key" is enabled, compare the ip address with the list * of valid ip addresses stored in the database. @@ -627,7 +641,7 @@ protected function _detect_api_key() // multiple ip addresses must be separated using a comma, explode and loop $list_ip_addresses = explode(",", $row->ip_addresses); $found_address = FALSE; - + foreach($list_ip_addresses as $ip_address) { if($this->input->ip_address() == trim($ip_address)) @@ -637,7 +651,7 @@ protected function _detect_api_key() break; } } - + return $found_address; } else @@ -646,7 +660,7 @@ protected function _detect_api_key() return FALSE; } } - + return $row; } @@ -702,7 +716,7 @@ protected function _log_request($authorized = FALSE) return $this->rest->db->insert(config_item('rest_logs_table'), array( 'uri' => $this->uri->uri_string(), 'method' => $this->request->method, - 'params' => $this->_args ? (config_item('rest_logs_json_params') ? json_encode($this->_args) : serialize($this->_args)) : null, + 'params' => $this->_args ? serialize($this->_args) : null, 'api_key' => isset($this->rest->key) ? $this->rest->key : '', 'ip_address' => $this->input->ip_address(), 'time' => function_exists('now') ? now() : time(), @@ -848,6 +862,48 @@ protected function _parse_post() $this->request->format and $this->request->body = file_get_contents('php://input'); } + /** + * Parse HEAD + */ + protected function _parse_head() + { + // Grab proper HEAD variables + parse_str(parse_url(http://github.com/$_SERVER['REQUEST_URI'],%20PHP_URL_QUERY), $head); + + // Merge both the URI segments and HEAD params + $this->_head_args = array_merge($this->_head_args, $head); + } + + /** + * Parse OPTIONS + */ + protected function _parse_options() + { + // Grab proper OPTIONS variables + parse_str(parse_url(http://github.com/$_SERVER['REQUEST_URI'],%20PHP_URL_QUERY), $options); + + // Merge both the URI segments and OPTIONS params + $this->_options_args = array_merge($this->_options_args, $options); + } + + /** + * Parse PATCH + */ + protected function _parse_patch() + { + // It might be a HTTP body + if ($this->request->format) + { + $this->request->body = file_get_contents('php://input'); + } + + // If no file type is provided, this is probably just arguments + else + { + parse_str(file_get_contents('php://input'), $this->_patch_args); + } + } + /** * Parse PUT */ @@ -894,6 +950,38 @@ public function get($key = NULL, $xss_clean = TRUE) return array_key_exists($key, $this->_get_args) ? $this->_xss_clean($this->_get_args[$key], $xss_clean) : FALSE; } + /** + * This function retrieves a values from the OPTIONS request arguments + * + * @param string $key The OPTIONS/GET argument key + * @param boolean $xss_clean Whether the value should be XSS cleaned or not + * @return string The OPTIONS/GET argument value + */ + public function options($key = NULL, $xss_clean = TRUE) + { + if ($key === NULL) { + return $this->_options_args; + } + + return array_key_exists($key, $this->_options_args) ? $this->_xss_clean($this->_options_args[$key], $xss_clean) : FALSE; + } + + /** + * This function retrieves a values from the HEAD request arguments + * + * @param string $key The HEAD/GET argument key + * @param boolean $xss_clean Whether the value should be XSS cleaned or not + * @return string The HEAD/GET argument value + */ + public function head($key = NULL, $xss_clean = TRUE) + { + if ($key === NULL) { + return $this->head_args; + } + + return array_key_exists($key, $this->head_args) ? $this->_xss_clean($this->head_args[$key], $xss_clean) : FALSE; + } + /** * Retrieve a value from the POST request arguments. * @@ -928,6 +1016,23 @@ public function put($key = NULL, $xss_clean = TRUE) return array_key_exists($key, $this->_put_args) ? $this->_xss_clean($this->_put_args[$key], $xss_clean) : FALSE; } + /** + * Retrieve a value from the PATCH request arguments. + * + * @param string $key The key for the PATCH request argument to retrieve + * @param boolean $xss_clean Whether the value should be XSS cleaned or not. + * @return string The PATCH argument value. + */ + public function patch($key = NULL, $xss_clean = TRUE) + { + if ($key === NULL) + { + return $this->_patch_args; + } + + return array_key_exists($key, $this->_patch_args) ? $this->_xss_clean($this->_patch_args[$key], $xss_clean) : FALSE; + } + /** * Retrieve a value from the DELETE request arguments. * From 27a0c8b85a94e37a0aab75a9373cdae686cecb1d Mon Sep 17 00:00:00 2001 From: Bo Thomsen Date: Mon, 25 Feb 2013 12:57:46 +0100 Subject: [PATCH 098/651] Revert "Added PATCH, HEAD and OPTIONS" This reverts commit c9c1d5f6a87fa6feee89db5cc5ef2b86093fe358. --- application/libraries/REST_Controller.php | 155 ++++------------------ 1 file changed, 25 insertions(+), 130 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 533cb4d6..15e4a483 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -13,7 +13,7 @@ * @link https://github.com/philsturgeon/codeigniter-restserver * @version 2.6.2 */ -class REST_Controller extends CI_Controller +abstract class REST_Controller extends CI_Controller { /** * This defines the rest format. @@ -36,7 +36,7 @@ class REST_Controller extends CI_Controller * * @var array */ - protected $allowed_http_methods = array('get', 'delete', 'post', 'put', 'options', 'patch', 'head'); + protected $allowed_http_methods = array('get', 'delete', 'post', 'put'); /** * General request data and information. @@ -88,27 +88,6 @@ class REST_Controller extends CI_Controller */ protected $_delete_args = array(); - /** - * The arguments for the PATCH request method - * - * @var array - */ - protected $_patch_args = array(); - - /** - * The arguments for the HEAD request method - * - * @var array - */ - protected $_head_args = array(); - - /** - * The arguments for the OPTIONS request method - * - * @var array - */ - protected $_options_args = array(); - /** * The arguments from GET, POST, PUT, DELETE request methods combined. * @@ -175,10 +154,10 @@ public function __construct() // let's learn about the request $this->request = new stdClass(); - + // Is it over SSL? $this->request->ssl = $this->_detect_ssl(); - + // How is this request being made? POST, DELETE, GET, PUT? $this->request->method = $this->_detect_method(); @@ -191,7 +170,7 @@ public function __construct() // Set up our GET variables $this->_get_args = array_merge($this->_get_args, $this->uri->ruri_to_assoc()); - //$this->load->library('security'); + $this->load->library('security'); // This library is bundled with REST_Controller 2.5+, but will eventually be part of CodeIgniter itself $this->load->library('format'); @@ -213,7 +192,7 @@ public function __construct() } // Merge both for one mega-args variable - $this->_args = array_merge($this->_get_args, $this->_options_args, $this->_patch_args, $this->_head_args , $this->_put_args, $this->_post_args, $this->_delete_args, $this->{'_'.$this->request->method.'_args'}); + $this->_args = array_merge($this->_get_args, $this->_put_args, $this->_post_args, $this->_delete_args, $this->{'_'.$this->request->method.'_args'}); // Which format should the data be returned in? $this->response = new stdClass(); @@ -286,9 +265,9 @@ public function _remap($object_called, $arguments) // Should we answer if not over SSL? if (config_item('force_https') AND !$this->_detect_ssl()) { - $this->response(array('status' => false, 'error' => 'Unsupported protocol'), 403); + $this->response(array('status' => false, 'error' => 'Unsupported protocol'), 403); } - + $pattern = '/^(.*)\.('.implode('|', array_keys($this->_supported_formats)).')$/'; if (preg_match($pattern, $object_called, $matches)) { @@ -389,9 +368,16 @@ public function response($data = array(), $http_code = null) $output = NULL; } + // If data is empty but http code provided, keep the output empty + else if (empty($data) && is_numeric($http_code)) + { + $output = NULL; + } + // Otherwise (if no data but 200 provided) or some data, carry on camping! - else { - // Is compression requested? + else + { + // Is compression requested? if ($CFG->item('compress_output') === TRUE && $this->_zlib_oc == FALSE) { if (extension_loaded('zlib')) @@ -441,7 +427,7 @@ public function response($data = array(), $http_code = null) { header('Content-Length: ' . strlen($output)); } - + exit($output); } @@ -454,8 +440,8 @@ protected function _detect_ssl() { return (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == "on"); } - - + + /* * Detect input format * @@ -628,7 +614,7 @@ protected function _detect_api_key() isset($row->user_id) AND $this->rest->user_id = $row->user_id; isset($row->level) AND $this->rest->level = $row->level; isset($row->ignore_limits) AND $this->rest->ignore_limits = $row->ignore_limits; - + /* * If "is private key" is enabled, compare the ip address with the list * of valid ip addresses stored in the database. @@ -641,7 +627,7 @@ protected function _detect_api_key() // multiple ip addresses must be separated using a comma, explode and loop $list_ip_addresses = explode(",", $row->ip_addresses); $found_address = FALSE; - + foreach($list_ip_addresses as $ip_address) { if($this->input->ip_address() == trim($ip_address)) @@ -651,7 +637,7 @@ protected function _detect_api_key() break; } } - + return $found_address; } else @@ -660,7 +646,7 @@ protected function _detect_api_key() return FALSE; } } - + return $row; } @@ -716,7 +702,7 @@ protected function _log_request($authorized = FALSE) return $this->rest->db->insert(config_item('rest_logs_table'), array( 'uri' => $this->uri->uri_string(), 'method' => $this->request->method, - 'params' => $this->_args ? serialize($this->_args) : null, + 'params' => $this->_args ? (config_item('rest_logs_json_params') ? json_encode($this->_args) : serialize($this->_args)) : null, 'api_key' => isset($this->rest->key) ? $this->rest->key : '', 'ip_address' => $this->input->ip_address(), 'time' => function_exists('now') ? now() : time(), @@ -862,48 +848,6 @@ protected function _parse_post() $this->request->format and $this->request->body = file_get_contents('php://input'); } - /** - * Parse HEAD - */ - protected function _parse_head() - { - // Grab proper HEAD variables - parse_str(parse_url(http://github.com/$_SERVER['REQUEST_URI'],%20PHP_URL_QUERY), $head); - - // Merge both the URI segments and HEAD params - $this->_head_args = array_merge($this->_head_args, $head); - } - - /** - * Parse OPTIONS - */ - protected function _parse_options() - { - // Grab proper OPTIONS variables - parse_str(parse_url(http://github.com/$_SERVER['REQUEST_URI'],%20PHP_URL_QUERY), $options); - - // Merge both the URI segments and OPTIONS params - $this->_options_args = array_merge($this->_options_args, $options); - } - - /** - * Parse PATCH - */ - protected function _parse_patch() - { - // It might be a HTTP body - if ($this->request->format) - { - $this->request->body = file_get_contents('php://input'); - } - - // If no file type is provided, this is probably just arguments - else - { - parse_str(file_get_contents('php://input'), $this->_patch_args); - } - } - /** * Parse PUT */ @@ -950,38 +894,6 @@ public function get($key = NULL, $xss_clean = TRUE) return array_key_exists($key, $this->_get_args) ? $this->_xss_clean($this->_get_args[$key], $xss_clean) : FALSE; } - /** - * This function retrieves a values from the OPTIONS request arguments - * - * @param string $key The OPTIONS/GET argument key - * @param boolean $xss_clean Whether the value should be XSS cleaned or not - * @return string The OPTIONS/GET argument value - */ - public function options($key = NULL, $xss_clean = TRUE) - { - if ($key === NULL) { - return $this->_options_args; - } - - return array_key_exists($key, $this->_options_args) ? $this->_xss_clean($this->_options_args[$key], $xss_clean) : FALSE; - } - - /** - * This function retrieves a values from the HEAD request arguments - * - * @param string $key The HEAD/GET argument key - * @param boolean $xss_clean Whether the value should be XSS cleaned or not - * @return string The HEAD/GET argument value - */ - public function head($key = NULL, $xss_clean = TRUE) - { - if ($key === NULL) { - return $this->head_args; - } - - return array_key_exists($key, $this->head_args) ? $this->_xss_clean($this->head_args[$key], $xss_clean) : FALSE; - } - /** * Retrieve a value from the POST request arguments. * @@ -1016,23 +928,6 @@ public function put($key = NULL, $xss_clean = TRUE) return array_key_exists($key, $this->_put_args) ? $this->_xss_clean($this->_put_args[$key], $xss_clean) : FALSE; } - /** - * Retrieve a value from the PATCH request arguments. - * - * @param string $key The key for the PATCH request argument to retrieve - * @param boolean $xss_clean Whether the value should be XSS cleaned or not. - * @return string The PATCH argument value. - */ - public function patch($key = NULL, $xss_clean = TRUE) - { - if ($key === NULL) - { - return $this->_patch_args; - } - - return array_key_exists($key, $this->_patch_args) ? $this->_xss_clean($this->_patch_args[$key], $xss_clean) : FALSE; - } - /** * Retrieve a value from the DELETE request arguments. * From 429083dffca4311fe4b180f285a7f2a6b173d278 Mon Sep 17 00:00:00 2001 From: Bo Thomsen Date: Mon, 25 Feb 2013 13:04:21 +0100 Subject: [PATCH 099/651] Added PATCH, HEAD and OPTIONS --- application/libraries/REST_Controller.php | 116 +++++++++++++++++++++- 1 file changed, 114 insertions(+), 2 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 15e4a483..448be4f3 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -36,7 +36,7 @@ abstract class REST_Controller extends CI_Controller * * @var array */ - protected $allowed_http_methods = array('get', 'delete', 'post', 'put'); + protected $allowed_http_methods = array('get', 'delete', 'post', 'put', 'options', 'patch', 'head'); /** * General request data and information. @@ -88,6 +88,27 @@ abstract class REST_Controller extends CI_Controller */ protected $_delete_args = array(); + /** + * The arguments for the PATCH request method + * + * @var array + */ + protected $_patch_args = array(); + + /** + * The arguments for the HEAD request method + * + * @var array + */ + protected $_head_args = array(); + + /** + * The arguments for the OPTIONS request method + * + * @var array + */ + protected $_options_args = array(); + /** * The arguments from GET, POST, PUT, DELETE request methods combined. * @@ -192,7 +213,7 @@ public function __construct() } // Merge both for one mega-args variable - $this->_args = array_merge($this->_get_args, $this->_put_args, $this->_post_args, $this->_delete_args, $this->{'_'.$this->request->method.'_args'}); + $this->_args = array_merge($this->_get_args, $this->_options_args, $this->_patch_args, $this->_head_args , $this->_put_args, $this->_post_args, $this->_delete_args, $this->{'_'.$this->request->method.'_args'}); // Which format should the data be returned in? $this->response = new stdClass(); @@ -866,6 +887,48 @@ protected function _parse_put() } } + /** + * Parse HEAD + */ + protected function _parse_head() + { + // Grab proper HEAD variables + parse_str(parse_url(http://github.com/$_SERVER['REQUEST_URI'],%20PHP_URL_QUERY), $head); + + // Merge both the URI segments and HEAD params + $this->_head_args = array_merge($this->_head_args, $head); + } + + /** + * Parse OPTIONS + */ + protected function _parse_options() + { + // Grab proper OPTIONS variables + parse_str(parse_url(http://github.com/$_SERVER['REQUEST_URI'],%20PHP_URL_QUERY), $options); + + // Merge both the URI segments and OPTIONS params + $this->_options_args = array_merge($this->_options_args, $options); + } + + /** + * Parse PATCH + */ + protected function _parse_patch() + { + // It might be a HTTP body + if ($this->request->format) + { + $this->request->body = file_get_contents('php://input'); + } + + // If no file type is provided, this is probably just arguments + else + { + parse_str(file_get_contents('php://input'), $this->_patch_args); + } + } + /** * Parse DELETE */ @@ -894,6 +957,38 @@ public function get($key = NULL, $xss_clean = TRUE) return array_key_exists($key, $this->_get_args) ? $this->_xss_clean($this->_get_args[$key], $xss_clean) : FALSE; } + /** + * This function retrieves a values from the OPTIONS request arguments + * + * @param string $key The OPTIONS/GET argument key + * @param boolean $xss_clean Whether the value should be XSS cleaned or not + * @return string The OPTIONS/GET argument value + */ + public function options($key = NULL, $xss_clean = TRUE) + { + if ($key === NULL) { + return $this->_options_args; + } + + return array_key_exists($key, $this->_options_args) ? $this->_xss_clean($this->_options_args[$key], $xss_clean) : FALSE; + } + + /** + * This function retrieves a values from the HEAD request arguments + * + * @param string $key The HEAD/GET argument key + * @param boolean $xss_clean Whether the value should be XSS cleaned or not + * @return string The HEAD/GET argument value + */ + public function head($key = NULL, $xss_clean = TRUE) + { + if ($key === NULL) { + return $this->head_args; + } + + return array_key_exists($key, $this->head_args) ? $this->_xss_clean($this->head_args[$key], $xss_clean) : FALSE; + } + /** * Retrieve a value from the POST request arguments. * @@ -945,6 +1040,23 @@ public function delete($key = NULL, $xss_clean = TRUE) return array_key_exists($key, $this->_delete_args) ? $this->_xss_clean($this->_delete_args[$key], $xss_clean) : FALSE; } + /** + * Retrieve a value from the PATCH request arguments. + * + * @param string $key The key for the PATCH request argument to retrieve + * @param boolean $xss_clean Whether the value should be XSS cleaned or not. + * @return string The PATCH argument value. + */ + public function patch($key = NULL, $xss_clean = TRUE) + { + if ($key === NULL) + { + return $this->_patch_args; + } + + return array_key_exists($key, $this->_patch_args) ? $this->_xss_clean($this->_patch_args[$key], $xss_clean) : FALSE; + } + /** * Process to protect from XSS attacks. * From ec61867fb52bfe7324cea5f7893a45d2f57e07e6 Mon Sep 17 00:00:00 2001 From: m4ikel Date: Thu, 28 Mar 2013 01:06:09 +0100 Subject: [PATCH 100/651] Passing-by-reference deprecated Pass-by-reference has been deprecated as of PHP 5.3.x causing a notice. As of PHP 5.4.x pass-by-reference is being removed. Function definitions alone are enough to correctly pass the argument by reference. --- application/libraries/REST_Controller.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 15e4a483..8737e7e8 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -1086,7 +1086,7 @@ protected function _check_login($username = '', $password = NULL) return $this->_perform_ldap_auth($username, $password); } - $valid_logins = & $this->config->item('rest_valid_logins'); + $valid_logins = $this->config->item('rest_valid_logins'); if ( ! array_key_exists($username, $valid_logins)) { @@ -1181,7 +1181,7 @@ protected function _prepare_digest_auth() $this->_force_login($uniqid); } - $valid_logins = & $this->config->item('rest_valid_logins'); + $valid_logins = $this->config->item('rest_valid_logins'); $valid_pass = $valid_logins[$digest['username']]; // This is the valid response expected From 0c7bb496604d1afb302fd57aaa614593f3357f61 Mon Sep 17 00:00:00 2001 From: Dimitri De Franciscis Date: Wed, 8 May 2013 17:54:16 +0200 Subject: [PATCH 101/651] Fixed issue #117 - Undefined property $this->db --- application/libraries/REST_Controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 8737e7e8..d30baddc 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -232,7 +232,7 @@ public function __construct() } // Use whatever database is in use (isset returns false) - elseif (@$this->db) + elseif (property_exists($this, "db")) { $this->rest->db = $this->db; } From b6a22cc764d730227d3bc36c98d86daf47ccb772 Mon Sep 17 00:00:00 2001 From: mohab Date: Mon, 27 May 2013 23:08:12 +0200 Subject: [PATCH 102/651] Escaping CSV Headings to fix the case of a heading including a comma --- application/libraries/Format.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/libraries/Format.php b/application/libraries/Format.php index acb13699..1bcb4aa4 100644 --- a/application/libraries/Format.php +++ b/application/libraries/Format.php @@ -197,7 +197,7 @@ public function to_csv() $data = array($data); } - $output = implode(',', $headings).PHP_EOL; + $output = '"'.implode('","', $headings).'"'.PHP_EOL; foreach ($data as &$row) { $output .= '"'.implode('","', $row).'"'.PHP_EOL; From 8c1ac7e2ec737953a904aa83d33e51f891cb1c72 Mon Sep 17 00:00:00 2001 From: Paul Ryan Date: Thu, 30 May 2013 09:47:18 +0100 Subject: [PATCH 103/651] changed readme to explain use of DELETE --- README.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 676c7f10..7fe759c5 100644 --- a/README.md +++ b/README.md @@ -35,12 +35,21 @@ This allows you to implement a RESTful interface easily: `REST_Controller` also supports `PUT` and `DELETE` methods, allowing you to support a truly RESTful interface. + Accessing parameters is also easy. Simply use the name of the HTTP verb as a method: $this->get('blah'); // GET param $this->post('blah'); // POST param $this->put('blah'); // PUT param - $this->delete('blah'); // DELETE param + +The HTTP spec for DELETE requests precludes the use of parameters. For delete requests, you can add items to the URL + + public function index_delete($id) + { + $this->response(array( + 'returned from delete:' => $id, + )); + } ## Content Types From 8d7d17582a527e871b53d718f291592d8cca8a4f Mon Sep 17 00:00:00 2001 From: KennyDs Date: Mon, 3 Jun 2013 15:41:37 +0200 Subject: [PATCH 104/651] Added extra variable that holds the information about the API user --- application/libraries/REST_Controller.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 8737e7e8..24aacf6c 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -130,6 +130,13 @@ abstract class REST_Controller extends CI_Controller 'html' => 'text/html', 'csv' => 'application/csv' ); + + /** + * Information about the current API user + * + * @var object + */ + protected $_apiuser; /** * Developers can extend this class and add a check in here. @@ -615,6 +622,8 @@ protected function _detect_api_key() isset($row->level) AND $this->rest->level = $row->level; isset($row->ignore_limits) AND $this->rest->ignore_limits = $row->ignore_limits; + $this->_apiuser = $row; + /* * If "is private key" is enabled, compare the ip address with the list * of valid ip addresses stored in the database. From c3fefb2f0ff08a552528710042a7a9a0e9e8437f Mon Sep 17 00:00:00 2001 From: Shiro Amada Date: Tue, 11 Jun 2013 16:15:59 +0800 Subject: [PATCH 105/651] 'key' column value should get from the config variable replace 'key' to config_item('rest_key_column') --- application/controllers/api/key.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/application/controllers/api/key.php b/application/controllers/api/key.php index e4468862..33d8049a 100644 --- a/application/controllers/api/key.php +++ b/application/controllers/api/key.php @@ -214,14 +214,14 @@ private function _generate_key() private function _get_key($key) { - return $this->db->where('key', $key)->get(config_item('rest_keys_table'))->row(); + return $this->db->where(config_item('rest_key_column'), $key)->get(config_item('rest_keys_table'))->row(); } // -------------------------------------------------------------------- private function _key_exists($key) { - return $this->db->where('key', $key)->count_all_results(config_item('rest_keys_table')) > 0; + return $this->db->where(config_item('rest_key_column'), $key)->count_all_results(config_item('rest_keys_table')) > 0; } // -------------------------------------------------------------------- @@ -229,7 +229,7 @@ private function _key_exists($key) private function _insert_key($key, $data) { - $data['key'] = $key; + $data[config_item('rest_key_column')] = $key; $data['date_created'] = function_exists('now') ? now() : time(); return $this->db->set($data)->insert(config_item('rest_keys_table')); @@ -239,13 +239,13 @@ private function _insert_key($key, $data) private function _update_key($key, $data) { - return $this->db->where('key', $key)->update(config_item('rest_keys_table'), $data); + return $this->db->where(config_item('rest_key_column'), $key)->update(config_item('rest_keys_table'), $data); } // -------------------------------------------------------------------- private function _delete_key($key) { - return $this->db->where('key', $key)->delete(config_item('rest_keys_table')); + return $this->db->where(config_item('rest_key_column'), $key)->delete(config_item('rest_keys_table')); } } From 5dd96cef1830dfc31b79d4bcb4c18742d064e647 Mon Sep 17 00:00:00 2001 From: Mark Oberemk Date: Wed, 19 Jun 2013 16:22:55 -0300 Subject: [PATCH 106/651] Update Format.php Added a way of formatting the JSON to include the numeric check, but only if the available PHP version supports it (IE v5.3.3 or higher). --- application/libraries/Format.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/application/libraries/Format.php b/application/libraries/Format.php index acb13699..c0453c15 100644 --- a/application/libraries/Format.php +++ b/application/libraries/Format.php @@ -209,7 +209,11 @@ public function to_csv() // Encode as JSON public function to_json() { - return json_encode($this->_data); + if (strnatcmp(phpversion(),'5.3.3') >= 0) { + return json_encode($this->_data, JSON_NUMERIC_CHECK); + } else { + return json_encode($this->_data); + } } // Encode as Serialized array @@ -267,4 +271,4 @@ private function _from_serialize($string) } -/* End of file format.php */ \ No newline at end of file +/* End of file format.php */ From ea862c8a0cacf557ac0f645516902f566d098010 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Klomp?= Date: Fri, 5 Jul 2013 14:37:08 +0200 Subject: [PATCH 107/651] Allow login via arbitrary library --- application/config/rest.php | 16 ++++++++- application/libraries/REST_Controller.php | 40 +++++++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/application/config/rest.php b/application/config/rest.php index b7363674..cd632c8a 100644 --- a/application/config/rest.php +++ b/application/config/rest.php @@ -68,11 +68,25 @@ | | Is login required and if so, which user store do we use? | -| '' = use config based users, 'ldap' = use LDAP authencation +| '' = use config based users, 'ldap' = use LDAP authencation, 'library' = use a authentication library | */ $config['auth_source'] = 'ldap'; +/* +|-------------------------------------------------------------------------- +| REST Login +|-------------------------------------------------------------------------- +| +| If library authentication is used define the class and function name here +| +| The function should accept two parameters: class->function($username, $password) +| In other cases override the function _perform_library_auth in your controller +| +*/ +$config['auth_library_class'] = ''; +$config['auth_library_function'] = ''; + /* |-------------------------------------------------------------------------- | Override auth types for specific class/method diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 8737e7e8..720ac590 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -1064,6 +1064,40 @@ protected function _perform_ldap_auth($username = '', $password = NULL) return true; } + /** + * Perform Library Authentication - Override this function to change the way the library is called + * + * @param string $username The username to validate + * @param string $password The password to validate + * @return boolean + */ + protected function _perform_library_auth($username = '', $password = NULL) + { + if (empty($username)) + { + log_message('debug', 'Library Auth: failure, empty username'); + return false; + } + + $auth_library_class = strtolower($this->config->item('auth_library_class')); + $auth_library_function = strtolower($this->config->item('auth_library_function')); + + if (empty($auth_library_class)) + { + log_message('debug', 'Library Auth: failure, empty auth_library_class'); + return false; + } + + if (empty($auth_library_function)) + { + log_message('debug', 'Library Auth: failure, empty auth_library_function'); + return false; + } + + $this->load->library($auth_library_class); + return $this->$auth_library_class->$auth_library_function($username, $password); + } + /** * Check if the user is logged in. * @@ -1086,6 +1120,12 @@ protected function _check_login($username = '', $password = NULL) return $this->_perform_ldap_auth($username, $password); } + if ($auth_source == 'library') + { + log_message('debug', 'performing Library authentication for $username'); + return $this->_perform_library_auth($username, $password); + } + $valid_logins = $this->config->item('rest_valid_logins'); if ( ! array_key_exists($username, $valid_logins)) From 51ce7fb635ae6ddf3ad40a380873490794f3de02 Mon Sep 17 00:00:00 2001 From: Joe Auty Date: Sun, 14 Jul 2013 01:45:36 -0400 Subject: [PATCH 108/651] init request/response/rest to suppress PHP warnings --- application/libraries/REST_Controller.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 7e0d9dce..7df373cc 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -30,6 +30,11 @@ class REST_Controller extends CI_Controller { public function __construct() { parent::__construct(); + + // init objects + $this->request = new stdClass(); + $this->response = new stdClass(); + $this->rest = new stdClass(); // Lets grab the config and get ready to party $this->load->config('rest'); From b0cf0b4027793b4dcb65a8742dd5937eafdfd360 Mon Sep 17 00:00:00 2001 From: Justin Ethier Date: Wed, 31 Jul 2013 15:54:28 -0400 Subject: [PATCH 109/651] Escape double quotes per RFC 4180 According to this RFC: 7. If double-quotes are used to enclose fields, then a double-quote appearing inside a field must be escaped by preceding it with another double quote. --- application/libraries/Format.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/application/libraries/Format.php b/application/libraries/Format.php index acb13699..e6ae7341 100644 --- a/application/libraries/Format.php +++ b/application/libraries/Format.php @@ -200,6 +200,7 @@ public function to_csv() $output = implode(',', $headings).PHP_EOL; foreach ($data as &$row) { + $row = str_replace('"', '""', $row); // Escape dbl quotes per RFC 4180 $output .= '"'.implode('","', $row).'"'.PHP_EOL; } @@ -267,4 +268,4 @@ private function _from_serialize($string) } -/* End of file format.php */ \ No newline at end of file +/* End of file format.php */ From 922ef7ac51da4410d6f6c8349320a502cbe36801 Mon Sep 17 00:00:00 2001 From: KennyDs Date: Fri, 16 Aug 2013 10:08:46 +0200 Subject: [PATCH 110/651] Added possibility to add attributes to XML elements --- application/libraries/Format.php | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/application/libraries/Format.php b/application/libraries/Format.php index acb13699..cb674454 100644 --- a/application/libraries/Format.php +++ b/application/libraries/Format.php @@ -127,15 +127,24 @@ public function to_xml($data = null, $structure = null, $basenode = 'xml') // replace anything not alpha numeric $key = preg_replace('/[^a-z_\-0-9]/i', '', $key); + if ($key === '_attributes' && (is_array($value) || is_object($value))) + { + $attributes = $value; + if (is_object($attributes)) $attributes = get_object_vars($attributes); + + foreach ($attributes as $attributeName => $attributeValue) + { + $structure->addAttribute($attributeName, $attributeValue); + } + } // if there is another array found recursively call this function - if (is_array($value) || is_object($value)) + else if (is_array($value) || is_object($value)) { $node = $structure->addChild($key); // recursive call. $this->to_xml($value, $node, $key); } - else { // add single node. @@ -267,4 +276,4 @@ private function _from_serialize($string) } -/* End of file format.php */ \ No newline at end of file +/* End of file format.php */ From 3cfa863c59483d100f5c38d0673046eedbcb38ec Mon Sep 17 00:00:00 2001 From: SMaker Date: Sat, 28 Sep 2013 18:05:11 +0900 Subject: [PATCH 111/651] Unable to load the requested class: Security --- application/libraries/REST_Controller.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 4c799740..5dfc6dee 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -175,8 +175,6 @@ public function __construct() // Set up our GET variables $this->_get_args = array_merge($this->_get_args, $this->uri->ruri_to_assoc()); - $this->load->library('security'); - // This library is bundled with REST_Controller 2.5+, but will eventually be part of CodeIgniter itself $this->load->library('format'); From 58955e514a41c48cce92ad38658eb344d9a8d25d Mon Sep 17 00:00:00 2001 From: Jarrett Chisholm Date: Mon, 30 Sep 2013 11:48:42 -0400 Subject: [PATCH 112/651] - fixed issue where json response 'integer' strings turned into integers (issue #244) --- application/libraries/Format.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/application/libraries/Format.php b/application/libraries/Format.php index 0a97e29c..3d4be776 100644 --- a/application/libraries/Format.php +++ b/application/libraries/Format.php @@ -210,11 +210,7 @@ public function to_csv() // Encode as JSON public function to_json() { - if (strnatcmp(phpversion(),'5.3.3') >= 0) { - return json_encode($this->_data, JSON_NUMERIC_CHECK); - } else { - return json_encode($this->_data); - } + return json_encode($this->_data); } // Encode as Serialized array From d6853e6b4187f33f76ea907724e680bda938ad02 Mon Sep 17 00:00:00 2001 From: Felix Date: Mon, 18 Nov 2013 15:32:47 +0100 Subject: [PATCH 113/651] do NOT return 404 when an empty array is sent to response instead DO return it --- application/libraries/REST_Controller.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index a17d4706..df5b8e13 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -358,12 +358,12 @@ protected function _fire_method($method, $args) * @param array $data * @param null|int $http_code */ - public function response($data = array(), $http_code = null) + public function response($data = null, $http_code = null) { global $CFG; - // If data is empty and not code provide, error and bail - if (empty($data) && $http_code === null) + // If data is NULL and not code provide, error and bail + if ($data === NULL && $http_code === null) { $http_code = 404; @@ -371,8 +371,8 @@ public function response($data = array(), $http_code = null) $output = NULL; } - // If data is empty but http code provided, keep the output empty - else if (empty($data) && is_numeric($http_code)) + // If data is NULL but http code provided, keep the output empty + else if ($data === NULL && is_numeric($http_code)) { $output = NULL; } From 1ef844acd1f6532b4edd6452f32345dfaa573443 Mon Sep 17 00:00:00 2001 From: James Rainaldi Date: Mon, 25 Nov 2013 07:05:35 -0600 Subject: [PATCH 114/651] Rest Auth Config Change I updated the 'rest_auth' variable by setting it to lower case to prevent any auth errors if you use capitalized or uppercase letters in that config item. Reason for change was due to documentation and unexpected errors. --- application/libraries/REST_Controller.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index a17d4706..31705090 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -213,11 +213,11 @@ public function __construct() // When there is no specific override for the current class/method, use the default auth value set in the config if ($this->auth_override !== TRUE) { - if ($this->config->item('rest_auth') == 'basic') + if (strtolower( $this->config->item('rest_auth') ) == 'basic') { $this->_prepare_basic_auth(); } - elseif ($this->config->item('rest_auth') == 'digest') + elseif (strtolower( $this->config->item('rest_auth') ) == 'digest') { $this->_prepare_digest_auth(); } @@ -1227,11 +1227,11 @@ protected function _check_whitelist_auth() */ protected function _force_login($nonce = '') { - if ($this->config->item('rest_auth') == 'basic') + if (strtolower( $this->config->item('rest_auth') ) == 'basic') { header('WWW-Authenticate: Basic realm="'.$this->config->item('rest_realm').'"'); } - elseif ($this->config->item('rest_auth') == 'digest') + elseif (strtolower( $this->config->item('rest_auth') ) == 'digest') { header('WWW-Authenticate: Digest realm="'.$this->config->item('rest_realm').'", qop="auth", nonce="'.$nonce.'", opaque="'.md5($this->config->item('rest_realm')).'"'); } From 492c3cdf96ab0ec850beedc1d0b88f7f861b1f8e Mon Sep 17 00:00:00 2001 From: Chris Kacerguis Date: Wed, 27 Nov 2013 08:43:16 -0600 Subject: [PATCH 115/651] adds request time logging to the database. resolved #258 --- application/config/rest.php | 1 + application/libraries/REST_Controller.php | 51 ++++++++++++++++++++++- 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/application/config/rest.php b/application/config/rest.php index b7363674..f3ca3b38 100644 --- a/application/config/rest.php +++ b/application/config/rest.php @@ -256,6 +256,7 @@ `api_key` varchar(40) NOT NULL, `ip_address` varchar(45) NOT NULL, `time` int(11) NOT NULL, + `rtime` float DEFAULT NULL, `authorized` tinyint(1) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index a17d4706..61c7262b 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -116,6 +116,20 @@ abstract class REST_Controller extends CI_Controller */ protected $_user_ldap_dn = ''; + /** + * The start of the response time from the server + * + * @var string + */ + protected $_start_rtime = ''; + + /** + * The end of the response time from the server + * + * @var string + */ + protected $_end_rtime = ''; + /** * List all supported methods, the first will be the default format * @@ -146,7 +160,10 @@ protected function early_checks() public function __construct() { parent::__construct(); - + + // Start the timer for how long the request takes + $this->_start_rtime = microtime(TRUE); + // init objects $this->request = new stdClass(); $this->response = new stdClass(); @@ -253,6 +270,23 @@ public function __construct() } } + /** + * Destructor function + * @author Chris Kacerguis + */ + public function __destruct() + { + // Record the "stop" time of the request + $this->_end_rtime = microtime(TRUE); + // CK: if, we are logging, log the access time here, as we are done! + if (config_item('rest_enable_logging')) + { + $this->_log_access_time(); + } + + + } + /** * Remap * @@ -1256,6 +1290,21 @@ protected function _force_loopable($data) return $data; } + /** + * updates the log with the access time + * + * @author Chris Kacerguis + * @return boolean + */ + + protected function _log_access_time() + { + $payload['rtime'] = $this->_end_rtime - $this->_start_rtime; + return $this->rest->db->update(config_item('rest_logs_table'), $payload, array('id' => $this->_insert_id)); + } + + + // FORMATING FUNCTIONS --------------------------------------------------------- // Many of these have been moved to the Format class for better separation, but these methods will be checked too From 5e02f0af198d91ad4a37c544de2b927bf7edc378 Mon Sep 17 00:00:00 2001 From: Chris Kacerguis Date: Wed, 27 Nov 2013 08:55:04 -0600 Subject: [PATCH 116/651] Added controller based access controls. --- application/config/rest.php | 37 +++++++++++++++++++++++ application/libraries/REST_Controller.php | 34 +++++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/application/config/rest.php b/application/config/rest.php index f3ca3b38..16a1f5fa 100644 --- a/application/config/rest.php +++ b/application/config/rest.php @@ -264,6 +264,43 @@ */ $config['rest_enable_logging'] = FALSE; + +/* +|-------------------------------------------------------------------------- +| REST API Access Table Name +|-------------------------------------------------------------------------- +| +| The table name in your database that stores the access controls. +| +| 'access' +| +*/ +$config['rest_access_table'] = 'access'; + +/* +|-------------------------------------------------------------------------- +| REST Method Access Control +|-------------------------------------------------------------------------- +| +| When set to true REST_Controller will check the access table to see if +| the API KEY can access that controller. rest_enable_keys *must* be enabled +| to use this. +| +| FALSE +| +CREATE TABLE `access` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `key` varchar(40) NOT NULL DEFAULT '', + `controller` varchar(50) NOT NULL DEFAULT '', + `date_created` datetime DEFAULT NULL, + `date_modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +| +*/ +$config['rest_enable_access'] = FALSE; + + /* |-------------------------------------------------------------------------- | REST API Param Log Format diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 61c7262b..813f9d1f 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -322,6 +322,12 @@ public function _remap($object_called, $arguments) // Get that useless shitty key out of here if (config_item('rest_enable_keys') AND $use_key AND $this->_allow === FALSE) { + // Check to see if they can access the controller + if (!$this->_check_access()) + { + $this->response(array('status' => false, 'error' => 'This API key does not have access to the requested controller.'), 401); + } + if (config_item('rest_enable_logging') AND $log_method) { $this->_log_request(); @@ -1303,6 +1309,34 @@ protected function _log_access_time() return $this->rest->db->update(config_item('rest_logs_table'), $payload, array('id' => $this->_insert_id)); } + /** + * Check to see if the API key has access to the controller and methods + * + * @return boolean + */ + protected function _check_access() + { + // if we don't want to check acccess, just return TRUE + if (config_item('rest_enable_access') === FALSE) + { + return TRUE; + } + + $controller = explode('/', $this->uri->uri_string()); + + $this->rest->db->select(); + $this->rest->db->where('key', $this->rest->key); + $this->rest->db->where('controller', $controller[0]); + + $query = $this->rest->db->get(config_item('rest_access_table')); + + if ($query->num_rows > 0) + { + return TRUE; + } + + return FALSE; + } // FORMATING FUNCTIONS --------------------------------------------------------- From 3aa56604568a5d3c8f78267b053db57a6e101383 Mon Sep 17 00:00:00 2001 From: Chris Kacerguis Date: Wed, 27 Nov 2013 10:22:51 -0600 Subject: [PATCH 117/651] fixed #258 --- application/libraries/REST_Controller.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 813f9d1f..f6fba5cd 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -74,6 +74,13 @@ abstract class REST_Controller extends CI_Controller */ protected $_post_args = array(); + /** + * The insert_id of the log entry (if we have one) + * + * @var string + */ + protected $_insert_id = ''; + /** * The arguments for the PUT request method * @@ -742,7 +749,7 @@ protected function _detect_lang() */ protected function _log_request($authorized = FALSE) { - return $this->rest->db->insert(config_item('rest_logs_table'), array( + $status = $this->rest->db->insert(config_item('rest_logs_table'), array( 'uri' => $this->uri->uri_string(), 'method' => $this->request->method, 'params' => $this->_args ? (config_item('rest_logs_json_params') ? json_encode($this->_args) : serialize($this->_args)) : null, @@ -751,6 +758,9 @@ protected function _log_request($authorized = FALSE) 'time' => function_exists('now') ? now() : time(), 'authorized' => $authorized )); + + $this->_insert_id = $this->rest->db->insert_id(); + return $statusl } /** From 48153bcf3d5bdf08aff2930651eeb0f135b14128 Mon Sep 17 00:00:00 2001 From: Chris Kacerguis Date: Wed, 27 Nov 2013 11:15:38 -0600 Subject: [PATCH 118/651] fixed #258 --- application/libraries/REST_Controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index f6fba5cd..f4c7c71a 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -760,7 +760,7 @@ protected function _log_request($authorized = FALSE) )); $this->_insert_id = $this->rest->db->insert_id(); - return $statusl + return $status; } /** From 660b3a3e30c1d4604105cbdfe8cdbac4b3242c02 Mon Sep 17 00:00:00 2001 From: Chris Kacerguis Date: Wed, 27 Nov 2013 15:04:52 -0600 Subject: [PATCH 119/651] Closes #248 --- application/libraries/REST_Controller.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index f4c7c71a..8e8a8e87 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -884,6 +884,13 @@ protected function _auth_override_check() */ protected function _parse_get() { + // Fix for Issue #247 + if ($this->input->is_cli_request()) { + $args = $_SERVER['argv']; + unset($args[0]); + $_SERVER['QUERY_STRING'] = $_SERVER['PATH_INFO'] = $_SERVER['REQUEST_URI'] = '/' . implode('/', $args) . '/'; + } + // Grab proper GET variables parse_str(parse_url(http://github.com/$_SERVER['REQUEST_URI'],%20PHP_URL_QUERY), $get); From 0adaec19691cd70f682b6324fbd9fc49d8265e2c Mon Sep 17 00:00:00 2001 From: Chris Kacerguis Date: Wed, 27 Nov 2013 15:22:50 -0600 Subject: [PATCH 120/651] updated readme to reflect Phil's current status, and the new dev --- README.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 7fe759c5..2e21b924 100644 --- a/README.md +++ b/README.md @@ -218,15 +218,14 @@ By default, the HTTP will be `X-API-KEY`. This can be configured in **config/res * Added config options to set table names for keys, limits and logs. * FALSE values were coming out as empty strings in xml or rawxml mode, now they will be 0/1. -* key => FALSE can now be used to override the keys_enabled option for a specific method, and level is now optional. If no level is set it will assume the method has a level of 0. +* key => FALSE can now be used to override the keys_enabled option for a specific method, and level +is now optional. If no level is set it will assume the method has a level of 0. * Fixed issue where calls to ->get('foo') would error is foo was not set. Reported by Paul Barto. ## Contributions -This project has been funded and made possible through my clients kindly allowing me to -open-source the functionality as I build it into their projects. I am no longer actively developing -features for this as I no longer require it, but I will continue to maintain pull requests and try to -fix issues as and when they are reported (within a week or two). +This project was originally written by the awesome Phil Sturgeon, however his involvment has shifted +as he is no longer using it. As of 11/20/2013 further developement and support will be done by Chris Kacerguis. Pull Requests are the best way to fix bugs or add features. I know loads of you use this, so please contribute if you have improvements to be made and I'll keep releasing versions over time. From 34077091f94bb9ca8c51ee4cd85081ce8727d04b Mon Sep 17 00:00:00 2001 From: Chris Kacerguis Date: Wed, 27 Nov 2013 19:57:52 -0600 Subject: [PATCH 121/651] fixes #193 --- application/libraries/REST_Controller.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 81861cd1..de01087f 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -8,7 +8,7 @@ * @package CodeIgniter * @subpackage Libraries * @category Libraries - * @author Phil Sturgeon + * @author Phil Sturgeon, Chris Kacerguis * @license http://philsturgeon.co.uk/code/dbad-license * @link https://github.com/philsturgeon/codeigniter-restserver * @version 2.6.2 @@ -1346,7 +1346,7 @@ protected function _prepare_digest_auth() // We need to retrieve authentication informations from the $auth_data variable preg_match_all('@(username|nonce|uri|nc|cnonce|qop|response)=[\'"]?([^\'",]+)@', $digest_string, $matches); - $digest = array_combine($matches[1], $matches[2]); + $digest = (empty($matches[1]) || empty($matches[2])) ? array() : array_combine($matches[1], $matches[2]); if ( ! array_key_exists('username', $digest) OR !$this->_check_login($digest['username'])) { From 7e262f48bb96f04200d3f31c74423d262ad8d25e Mon Sep 17 00:00:00 2001 From: CJ Date: Thu, 21 Mar 2013 01:18:46 +0800 Subject: [PATCH 122/651] Added automated jsonp support with validation based on the presence of the 'callback' GET param; Also works should: * the requesting Content-Type is application/json instead of application/javascript; * the format GET param is: json * file extension: .json --- application/libraries/Format.php | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/application/libraries/Format.php b/application/libraries/Format.php index b157fdbf..5656669d 100644 --- a/application/libraries/Format.php +++ b/application/libraries/Format.php @@ -210,7 +210,32 @@ public function to_csv() // Encode as JSON public function to_json() { - return json_encode($this->_data); + $_numeric = strnatcmp(PHP_VERSION, '5.3.3') >= 0; + $callback = isset($_GET['callback']) ? $_GET['callback'] : ''; + + // we only honour jsonp callback which are valid javascript identifiers + if ($callback === '') + { + return $_numeric + ? json_encode($this->_data, JSON_NUMERIC_CHECK); + : json_encode($this->_data); + } + else if (preg_match('/^[a-z_\$][a-z0-9\$_]*(\.[a-z_\$][a-z0-9\$_]*)*$/i', $callback)) + { + // this is a jsonp request, the content-type must be updated to be text/javascript + header("Content-Type: application/javascript"); + return $_numeric + ? $callback . "(" . json_encode($this->_data, JSON_NUMERIC_CHECK) . ");"; + : $callback . "(" . json_encode($this->_data) . ");"; + } + else + { + // we have an invalid jsonp callback identifier, we'll return plain json with a warning field + $this->_data['warning'] = "invalid jsonp callback provided: ".$callback; + return $_numeric + ? json_encode($this->_data, JSON_NUMERIC_CHECK); + : json_encode($this->_data); + } } // Encode as Serialized array From 9689f0645684d1146e217358a8fb1ef95538747c Mon Sep 17 00:00:00 2001 From: CJ Date: Thu, 21 Mar 2013 01:21:48 +0800 Subject: [PATCH 123/651] Removed _format_jsonp which depends on file extension .jsonp or 'format' query param; --- application/libraries/REST_Controller.php | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index be6f6541..256239d8 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -1467,19 +1467,4 @@ protected function _check_access() return FALSE; } - - // FORMATING FUNCTIONS --------------------------------------------------------- - // Many of these have been moved to the Format class for better separation, but these methods will be checked too - - /** - * Encode as JSONP - * - * @param array $data The input data. - * @return string The JSONP data string (loadable from Javascript). - */ - protected function _format_jsonp($data = array()) - { - return $this->get('callback').'('.json_encode($data).')'; - } - } From ec5fb9c1b6a11f70914cc505e9e8f9de63c69139 Mon Sep 17 00:00:00 2001 From: CJ Date: Thu, 28 Nov 2013 04:19:32 +0000 Subject: [PATCH 124/651] See #202: Consider removing the _format_ factory --- application/libraries/REST_Controller.php | 1 + 1 file changed, 1 insertion(+) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 256239d8..00dc761b 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -462,6 +462,7 @@ public function response($data = array(), $http_code = null) is_numeric($http_code) OR $http_code = 200; + // @deprecated the following statement can be deleted. // If the format method exists, call and return the output in that format if (method_exists($this, '_format_'.$this->response->format)) { From 15c3b855cb3e609daf6fac93738d522c70d2dde6 Mon Sep 17 00:00:00 2001 From: Chris Kacerguis Date: Thu, 28 Nov 2013 15:35:43 -0600 Subject: [PATCH 125/651] Added option to blacklist an IP address --- application/config/rest.php | 26 +++++++++++++++++++++++ application/libraries/REST_Controller.php | 24 +++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/application/config/rest.php b/application/config/rest.php index 16a1f5fa..db89f955 100644 --- a/application/config/rest.php +++ b/application/config/rest.php @@ -140,6 +140,32 @@ */ $config['rest_ip_whitelist'] = ''; +/* +|-------------------------------------------------------------------------- +| Global IP Blacklisting +|-------------------------------------------------------------------------- +| +| Prevent connections to your REST server from blacklisted IP addresses. +| +| Usage: +| 1. Set to true *and* add any IP address to "rest_ip_blacklist" option +| +*/ +$config['rest_ip_blacklist_enabled'] = false; + +/* +|-------------------------------------------------------------------------- +| REST IP Blacklist +|-------------------------------------------------------------------------- +| +| Block connections from these IP addresses. +| +| Example: $config['rest_ip_blacklist'] = '123.456.789.0, 987.654.32.1'; +| +| +*/ +$config['rest_ip_blacklist'] = ''; + /* |-------------------------------------------------------------------------- | REST Database Group diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index d20905da..95a44af7 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -205,6 +205,12 @@ public function __construct() // let's learn about the request $this->request = new stdClass(); + // Check to see if this IP is Blacklisted + if ($this->config->item('rest_ip_blacklist_enabled')) + { + $this->_check_blacklist_auth(); + } + // Is it over SSL? $this->request->ssl = $this->_detect_ssl(); @@ -1369,6 +1375,24 @@ protected function _prepare_digest_auth() } } + /** + * Check if the client's ip is in the 'rest_ip_blacklist' config + */ + protected function _check_blacklist_auth() + { + $blacklist = explode(',', config_item('rest_ip_blacklist')); + + foreach ($blacklist AS &$ip) + { + $ip = trim($ip); + } + + if (!in_array($this->input->ip_address(), $blacklist)) + { + $this->response(array('status' => false, 'error' => 'IP Denied'), 401); + } + } + /** * Check if the client's ip is in the 'rest_ip_whitelist' config */ From 172a0725dbbf50452498c65c2c6ce85d3d140a3d Mon Sep 17 00:00:00 2001 From: Chris Kacerguis Date: Thu, 28 Nov 2013 15:45:25 -0600 Subject: [PATCH 126/651] Updated DB Engine to use InnoDB (MySQL default) --- application/config/rest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/config/rest.php b/application/config/rest.php index db89f955..2cdbf809 100644 --- a/application/config/rest.php +++ b/application/config/rest.php @@ -285,7 +285,7 @@ `rtime` float DEFAULT NULL, `authorized` tinyint(1) NOT NULL, PRIMARY KEY (`id`) - ) ENGINE=MyISAM DEFAULT CHARSET=utf8; + ) ENGINE=InnoDB DEFAULT CHARSET=utf8; | */ $config['rest_enable_logging'] = FALSE; @@ -368,7 +368,7 @@ `hour_started` int(11) NOT NULL, `api_key` varchar(40) NOT NULL, PRIMARY KEY (`id`) - ) ENGINE=MyISAM DEFAULT CHARSET=utf8; + ) ENGINE=InnoDB DEFAULT CHARSET=utf8; | */ $config['rest_enable_limits'] = FALSE; From 7852a16dd13a4047323c57e01adc264c23264e8f Mon Sep 17 00:00:00 2001 From: Chris Kacerguis Date: Thu, 28 Nov 2013 15:45:41 -0600 Subject: [PATCH 127/651] Updating change log in the readme --- README.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2e21b924..25f1a298 100644 --- a/README.md +++ b/README.md @@ -141,7 +141,7 @@ You'll need to create a new database table to store and access the keys. `REST_C `ignore_limits` tinyint(1) NOT NULL DEFAULT '0', `date_created` int(11) NOT NULL, PRIMARY KEY (`id`) - ) ENGINE=MyISAM DEFAULT CHARSET=utf8; + ) ENGINE=InnoDB DEFAULT CHARSET=utf8; The class will look for an HTTP header with the API key on each request. An invalid or missing API key will result in an `HTTP 403 Forbidden`. @@ -155,6 +155,15 @@ By default, the HTTP will be `X-API-KEY`. This can be configured in **config/res ## Change Log +### 3.0.0 (NOT YET RELEASED) + +* Added Blacklist IP option +* Added controller based access controls +* Added support for OPTIONS, PATCH, and HEAD (from boh1996) +* Added logging of the time it takes for a request (rtime column in DB) +* Changed DB schemas to use InnoDB, not MyISAM +* Updated Readme to reflect new developer (Chris Kacerguis) + ### 2.6.2 * Update CodeIgniter files to 2.1.3 From 9f345f6046a7ac39924e7a0eb1957a1a8be27bad Mon Sep 17 00:00:00 2001 From: CJ Date: Fri, 29 Nov 2013 23:00:39 +0800 Subject: [PATCH 128/651] Removed 5dd96ce (#202) following 58955e5 (fix #244); --- application/libraries/Format.php | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/application/libraries/Format.php b/application/libraries/Format.php index 5656669d..f712c387 100644 --- a/application/libraries/Format.php +++ b/application/libraries/Format.php @@ -210,31 +210,23 @@ public function to_csv() // Encode as JSON public function to_json() { - $_numeric = strnatcmp(PHP_VERSION, '5.3.3') >= 0; $callback = isset($_GET['callback']) ? $_GET['callback'] : ''; - - // we only honour jsonp callback which are valid javascript identifiers if ($callback === '') { - return $_numeric - ? json_encode($this->_data, JSON_NUMERIC_CHECK); - : json_encode($this->_data); + return json_encode($this->_data); } + // we only honour jsonp callback which are valid javascript identifiers else if (preg_match('/^[a-z_\$][a-z0-9\$_]*(\.[a-z_\$][a-z0-9\$_]*)*$/i', $callback)) { // this is a jsonp request, the content-type must be updated to be text/javascript header("Content-Type: application/javascript"); - return $_numeric - ? $callback . "(" . json_encode($this->_data, JSON_NUMERIC_CHECK) . ");"; - : $callback . "(" . json_encode($this->_data) . ");"; + return $callback . "(" . json_encode($this->_data) . ");"; } else { // we have an invalid jsonp callback identifier, we'll return plain json with a warning field $this->_data['warning'] = "invalid jsonp callback provided: ".$callback; - return $_numeric - ? json_encode($this->_data, JSON_NUMERIC_CHECK); - : json_encode($this->_data); + return json_encode($this->_data); } } From 76947107ccba1c595e5c318a0e9b2e801ac9828c Mon Sep 17 00:00:00 2001 From: avdata99 Date: Mon, 2 Dec 2013 16:39:57 -0300 Subject: [PATCH 129/651] Fixin csv and html outputs --- application/libraries/Format.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/libraries/Format.php b/application/libraries/Format.php index b157fdbf..3c5c25c3 100644 --- a/application/libraries/Format.php +++ b/application/libraries/Format.php @@ -151,7 +151,7 @@ public function to_xml($data = null, $structure = null, $basenode = 'xml') // Format HTML for output public function to_html() { - $data = $this->_data; + $data = (array)$this->_data; // Multi-dimensional array if (isset($data[0]) && is_array($data[0])) @@ -182,7 +182,7 @@ public function to_html() // Format CSV for output public function to_csv() { - $data = $this->_data; + $data = (array)$this->_data; // Multi-dimensional array if (isset($data[0]) && is_array($data[0])) From 4be8c7893cdedf79adfd3f651cec5b728fc39000 Mon Sep 17 00:00:00 2001 From: Oscar Morante Date: Sun, 15 Dec 2013 19:55:59 +0100 Subject: [PATCH 130/651] fix hardcoded HTTP version on some responses This was causing trouble with certain combinations of proxies and webservers. --- application/libraries/REST_Controller.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 38473f39..eb123cb9 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -493,8 +493,7 @@ public function response($data = null, $http_code = null) } } - header('HTTP/1.1: ' . $http_code); - header('Status: ' . $http_code); + set_status_header($http_code); // If zlib.output_compression is enabled it will compress the output, // but it will not modify the content-length header to compensate for @@ -1409,8 +1408,7 @@ protected function _prepare_digest_auth() if ($digest['response'] != $valid_response) { - header('HTTP/1.0 401 Unauthorized'); - header('HTTP/1.1 401 Unauthorized'); + set_status_header(401); exit; } } From 4d767cbec80ed56ebd072b6b57e71f5971809bbf Mon Sep 17 00:00:00 2001 From: Chris Kacerguis Date: Sun, 12 Jan 2014 08:48:32 -0600 Subject: [PATCH 131/651] Closes issue 269 --- application/libraries/REST_Controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index eb123cb9..db002a3c 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -11,7 +11,7 @@ * @author Phil Sturgeon, Chris Kacerguis * @license http://philsturgeon.co.uk/code/dbad-license * @link https://github.com/philsturgeon/codeigniter-restserver - * @version 2.6.2 + * @version 3.0.0-pre */ abstract class REST_Controller extends CI_Controller { From b7fe4ab7aa0c571ec398c70f9783cc3dfde5ffa8 Mon Sep 17 00:00:00 2001 From: Chris Kacerguis Date: Sun, 12 Jan 2014 09:00:34 -0600 Subject: [PATCH 132/651] resolves #235 --- application/libraries/Format.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/application/libraries/Format.php b/application/libraries/Format.php index 3c5c25c3..0551c05f 100644 --- a/application/libraries/Format.php +++ b/application/libraries/Format.php @@ -200,8 +200,13 @@ public function to_csv() $output = '"'.implode('","', $headings).'"'.PHP_EOL; foreach ($data as &$row) { - $row = str_replace('"', '""', $row); // Escape dbl quotes per RFC 4180 - $output .= '"'.implode('","', $row).'"'.PHP_EOL; + if (is_array($row)) { + throw new Exception('Format class does not support multi-dimensional arrays'); + } else { + $row = str_replace('"', '""', $row); // Escape dbl quotes per RFC 4180 + $output .= '"'.implode('","', $row).'"'.PHP_EOL; + } + } return $output; From 91b7ad26fb05b146b72f326c04f7007019d9e9bc Mon Sep 17 00:00:00 2001 From: Chris Kacerguis Date: Wed, 22 Jan 2014 19:21:36 -0600 Subject: [PATCH 133/651] updated version # --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 25f1a298..cd6cc6a4 100644 --- a/README.md +++ b/README.md @@ -155,7 +155,7 @@ By default, the HTTP will be `X-API-KEY`. This can be configured in **config/res ## Change Log -### 3.0.0 (NOT YET RELEASED) +### 2.7.0 * Added Blacklist IP option * Added controller based access controls From b1c5bbde833b1acbde46b362d69e33b7e30de8a2 Mon Sep 17 00:00:00 2001 From: Chris Kacerguis Date: Wed, 22 Jan 2014 19:21:43 -0600 Subject: [PATCH 134/651] whitespace fix --- application/libraries/REST_Controller.php | 1 + 1 file changed, 1 insertion(+) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 66c1b182..3cdea75b 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -960,6 +960,7 @@ protected function _parse_put() { parse_str(file_get_contents('php://input'), $this->_put_args); } + } /** From 2df31393a26066dad538f03c095db492742a0537 Mon Sep 17 00:00:00 2001 From: swas Date: Mon, 3 Feb 2014 19:31:11 +0100 Subject: [PATCH 135/651] Fixed .foo extensions to work when get arguments provided It was fixed 2 years ago in the pull request #94 (https://github.com/philsturgeon/codeigniter-restserver/pull/94) There was an error in the function _detect_output_format() With api/example/user/id/1.json URL's, enter in the first condition, but leaves without cleaning the arguments, which is done in the second condition. Now, only enter in the first condition if the URL does not have arguments. --- application/libraries/REST_Controller.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 3cdea75b..1a6de61b 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -564,8 +564,8 @@ protected function _detect_output_format() { $pattern = '/\.('.implode('|', array_keys($this->_supported_formats)).')$/'; - // Check if a file extension is used - if (preg_match($pattern, $this->uri->uri_string(), $matches)) + // Check if a file extension is used when no get arguments provided + if (!$this->_get_args AND preg_match($pattern, $this->uri->uri_string(), $matches)) { return $matches[1]; } From 65ad918423814768b5dd702e53eea859cdb9022e Mon Sep 17 00:00:00 2001 From: Chris Kacerguis Date: Wed, 12 Feb 2014 07:26:02 -0600 Subject: [PATCH 136/651] Delete license.txt --- license.txt | 51 --------------------------------------------------- 1 file changed, 51 deletions(-) delete mode 100755 license.txt diff --git a/license.txt b/license.txt deleted file mode 100755 index 061cdb9d..00000000 --- a/license.txt +++ /dev/null @@ -1,51 +0,0 @@ -Copyright (c) 2008 - 2011, EllisLab, Inc. -All rights reserved. - -This license is a legal agreement between you and EllisLab Inc. for the use -of CodeIgniter Software (the "Software"). By obtaining the Software you -agree to comply with the terms and conditions of this license. - -PERMITTED USE -You are permitted to use, copy, modify, and distribute the Software and its -documentation, with or without modification, for any purpose, provided that -the following conditions are met: - -1. A copy of this license agreement must be included with the distribution. - -2. Redistributions of source code must retain the above copyright notice in - all source code files. - -3. Redistributions in binary form must reproduce the above copyright notice - in the documentation and/or other materials provided with the distribution. - -4. Any files that have been modified must carry notices stating the nature - of the change and the names of those who changed them. - -5. Products derived from the Software must include an acknowledgment that - they are derived from CodeIgniter in their documentation and/or other - materials provided with the distribution. - -6. Products derived from the Software may not be called "CodeIgniter", - nor may "CodeIgniter" appear in their name, without prior written - permission from EllisLab, Inc. - -INDEMNITY -You agree to indemnify and hold harmless the authors of the Software and -any contributors for any direct, indirect, incidental, or consequential -third-party claims, actions or suits, as well as any related expenses, -liabilities, damages, settlements or fees arising from your use or misuse -of the Software, or a violation of any terms of this license. - -DISCLAIMER OF WARRANTY -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESSED OR -IMPLIED, INCLUDING, BUT NOT LIMITED TO, WARRANTIES OF QUALITY, PERFORMANCE, -NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. - -LIMITATIONS OF LIABILITY -YOU ASSUME ALL RISK ASSOCIATED WITH THE INSTALLATION AND USE OF THE SOFTWARE. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS OF THE SOFTWARE BE LIABLE -FOR CLAIMS, DAMAGES OR OTHER LIABILITY ARISING FROM, OUT OF, OR IN CONNECTION -WITH THE SOFTWARE. LICENSE HOLDERS ARE SOLELY RESPONSIBLE FOR DETERMINING THE -APPROPRIATENESS OF USE AND ASSUME ALL RISKS ASSOCIATED WITH ITS USE, INCLUDING -BUT NOT LIMITED TO THE RISKS OF PROGRAM ERRORS, DAMAGE TO EQUIPMENT, LOSS OF -DATA OR SOFTWARE PROGRAMS, OR UNAVAILABILITY OR INTERRUPTION OF OPERATIONS. From 383d639deccaf6a8be2d2540b3b87109f88cc6fa Mon Sep 17 00:00:00 2001 From: Chris Kacerguis Date: Wed, 12 Feb 2014 07:29:27 -0600 Subject: [PATCH 137/651] License update --- LICENSE.txt | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 LICENSE.txt diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 00000000..77a4a2ef --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,22 @@ +The MIT License + +Copyright (c) 2012 - 2014 Phil Sturgeon, Chris Kacerguis + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + From 2aa6fec9a05a04d530c58ce77716bba2ba4f46ca Mon Sep 17 00:00:00 2001 From: Chris Kacerguis Date: Mon, 10 Mar 2014 21:11:56 -0500 Subject: [PATCH 138/651] fixed #282 --- application/libraries/REST_Controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 1a6de61b..c921f194 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -1436,7 +1436,7 @@ protected function _check_blacklist_auth() $ip = trim($ip); } - if (!in_array($this->input->ip_address(), $blacklist)) + if (in_array($this->input->ip_address(), $blacklist)) { $this->response(array('status' => false, 'error' => 'IP Denied'), 401); } From 2c6724bfeadb046052defcc7e4641f803997ced5 Mon Sep 17 00:00:00 2001 From: Dionisis Mouroutis Date: Wed, 9 Apr 2014 16:32:58 +0300 Subject: [PATCH 139/651] Basic oauth breaks because of _check_login function error #296 --- application/libraries/REST_Controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index c921f194..5175556e 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -1322,7 +1322,7 @@ protected function _check_login($username = '', $password = NULL) } // If actually NULL (not empty string) then do not check it - if ($password !== NULL AND $valid_logins[$username] != $password) + if ($password === NULL AND $valid_logins[$username] != $password) { return FALSE; } From e2a4aee40403376994e4e609eabeee2321191cf7 Mon Sep 17 00:00:00 2001 From: David Newhall Date: Tue, 13 May 2014 14:48:25 -0700 Subject: [PATCH 140/651] allow user authentication AND the use of API keys for the same resource --- application/libraries/REST_Controller.php | 51 +++++++++++++---------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 5175556e..e91fb09c 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -268,23 +268,6 @@ public function __construct() // Check if there is a specific auth type for the current class/method $this->auth_override = $this->_auth_override_check(); - // When there is no specific override for the current class/method, use the default auth value set in the config - if ($this->auth_override !== TRUE) - { - if (strtolower( $this->config->item('rest_auth') ) == 'basic') - { - $this->_prepare_basic_auth(); - } - elseif (strtolower( $this->config->item('rest_auth') ) == 'digest') - { - $this->_prepare_digest_auth(); - } - elseif ($this->config->item('rest_ip_whitelist_enabled')) - { - $this->_check_whitelist_auth(); - } - } - $this->rest = new StdClass(); // Load DB if its enabled if (config_item('rest_database_group') AND (config_item('rest_enable_keys') OR config_item('rest_enable_logging'))) @@ -309,6 +292,25 @@ public function __construct() { $this->response(array('status' => false, 'error' => 'Only AJAX requests are accepted.'), 505); } + + // When there is no specific override for the current class/method, use the default auth value set in the config + if ($this->auth_override !== TRUE && $this->_allow === FALSE) + { + if (strtolower( $this->config->item('rest_auth') ) == 'basic') + { + $this->_prepare_basic_auth(); + } + elseif (strtolower( $this->config->item('rest_auth') ) == 'digest') + { + $this->_prepare_digest_auth(); + } + elseif ($this->config->item('rest_ip_whitelist_enabled')) + { + $this->_check_whitelist_auth(); + } + // If we made it this far we either authenticated or were in the white list. + $this->_allow = TRUE; + } } /** @@ -360,21 +362,26 @@ public function _remap($object_called, $arguments) // Use keys for this method? $use_key = ! (isset($this->methods[$controller_method]['key']) AND $this->methods[$controller_method]['key'] == FALSE); - // Get that useless shitty key out of here + // They provided a key, but it wasn't valid, so get them out of here. if (config_item('rest_enable_keys') AND $use_key AND $this->_allow === FALSE) { - // Check to see if they can access the controller - if (!$this->_check_access()) + if (config_item('rest_enable_logging') AND $log_method) { - $this->response(array('status' => false, 'error' => 'This API key does not have access to the requested controller.'), 401); + $this->_log_request(); } + $this->response(array('status' => false, 'error' => 'Invalid API Key.'), 403); + } + + // Check to see if this key has access to the requested controller. + if (config_item('rest_enable_keys') AND $use_key AND !empty($this->rest->key) AND !$this->_check_access()) + { if (config_item('rest_enable_logging') AND $log_method) { $this->_log_request(); } - $this->response(array('status' => false, 'error' => 'Invalid API Key.'), 403); + $this->response(array('status' => false, 'error' => 'This API key does not have access to the requested controller.'), 401); } // Sure it exists, but can they do anything with it? From bd97216829a079c5cf2e4a01d8b29748ce5955f8 Mon Sep 17 00:00:00 2001 From: David Newhall Date: Tue, 13 May 2014 14:50:58 -0700 Subject: [PATCH 141/651] add php session support --- application/config/rest.php | 6 ++++-- application/libraries/REST_Controller.php | 16 ++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/application/config/rest.php b/application/config/rest.php index a636ca28..45d83900 100644 --- a/application/config/rest.php +++ b/application/config/rest.php @@ -56,7 +56,8 @@ | | Is login required and if so, which type of login? | -| '' = no login required, 'basic' = unsecure login, 'digest' = more secure login +| '' = no login required, 'basic' = unsecure login, 'digest' = more secure login, +| 'session' = check for PHP session variable. Set variable name below. | */ $config['rest_auth'] = false; @@ -68,7 +69,8 @@ | | Is login required and if so, which user store do we use? | -| '' = use config based users, 'ldap' = use LDAP authencation, 'library' = use a authentication library +| '' = use config based users, 'ldap' = use LDAP authencation, 'library' = use a authentication library +| If 'rest_auth' is 'session' then set 'auth_source' to the name of the session variable to check for. | */ $config['auth_source'] = 'ldap'; diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index e91fb09c..34e8d6f2 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -304,6 +304,10 @@ public function __construct() { $this->_prepare_digest_auth(); } + elseif (strtolower( $this->config->item('rest_auth') ) == 'session') + { + $this->_check_php_session(); + } elseif ($this->config->item('rest_ip_whitelist_enabled')) { $this->_check_whitelist_auth(); @@ -1337,6 +1341,18 @@ protected function _check_login($username = '', $password = NULL) return TRUE; } + /** + * Check to see if the user is logged into the web app with a php session key. + */ + protected function _check_php_session() + { + $key = $this->config->item('auth_source'); + if (!$this->session->userdata($key)) + { + $this->response(array('status' => false, 'error' => 'Not Authorized'), 401); + } + } + /** * @todo document this. */ From a61ff412c258aab82e56a42d5aeffedc759fa767 Mon Sep 17 00:00:00 2001 From: David Newhall Date: Tue, 13 May 2014 15:23:40 -0700 Subject: [PATCH 142/651] update readme --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index cd6cc6a4..86fd3010 100644 --- a/README.md +++ b/README.md @@ -118,7 +118,9 @@ You can enable basic authentication by setting the `$config['rest_auth']` to `'b Enabling digest auth is similarly easy. Configure your desired logins in the config file like above, and set `$config['rest_auth']` to `'digest'`. The class will automatically send out the headers to enable digest auth. -Both methods of authentication can be secured further by using an IP whitelist. If you enable `$config['rest_ip_whitelist_enabled']` in your config file, you can then set a list of allowed IPs. +If you're tying this library into an AJAX endpoint where clients authenticate using PHP sessions then you may not like either of the digest nor basic authentication methods. In that case, you can tell the REST Library what PHP session variable to check for. If the variable exists, then the user is authorized. It will be up to your application to set that variable. You can define the variable in ``$config['auth_source']``. Then tell the library to use a php session variable by setting ``$config['rest_auth']`` to ``session``. + +All three methods of authentication can be secured further by using an IP whitelist. If you enable `$config['rest_ip_whitelist_enabled']` in your config file, you can then set a list of allowed IPs. Any client connecting to your API will be checked against the whitelisted IP array. If they're on the list, they'll be allowed access. If not, sorry, no can do hombre. The whitelist is a comma-separated string: From 398f39f954efaddf9ab900344c9d8df6a640a0a0 Mon Sep 17 00:00:00 2001 From: Chris Kacerguis Date: Thu, 22 May 2014 07:10:51 -0500 Subject: [PATCH 143/651] converted to PSR coding standards --- application/libraries/REST_Controller.php | 2989 ++++++++++----------- 1 file changed, 1442 insertions(+), 1547 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 34e8d6f2..93332301 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -15,1552 +15,1447 @@ */ abstract class REST_Controller extends CI_Controller { - /** - * This defines the rest format. - * - * Must be overridden it in a controller so that it is set. - * - * @var string|null - */ - protected $rest_format = NULL; - - /** - * Defines the list of method properties such as limit, log and level - * - * @var array - */ - protected $methods = array(); - - /** - * List of allowed HTTP methods - * - * @var array - */ - protected $allowed_http_methods = array('get', 'delete', 'post', 'put', 'options', 'patch', 'head'); - - /** - * General request data and information. - * Stores accept, language, body, headers, etc. - * - * @var object - */ - protected $request = NULL; - - /** - * What is gonna happen in output? - * - * @var object - */ - protected $response = NULL; - - /** - * Stores DB, keys, key level, etc - * - * @var object - */ - protected $rest = NULL; - - /** - * The arguments for the GET request method - * - * @var array - */ - protected $_get_args = array(); - - /** - * The arguments for the POST request method - * - * @var array - */ - protected $_post_args = array(); - - /** - * The insert_id of the log entry (if we have one) - * - * @var string - */ - protected $_insert_id = ''; - - /** - * The arguments for the PUT request method - * - * @var array - */ - protected $_put_args = array(); - - /** - * The arguments for the DELETE request method - * - * @var array - */ - protected $_delete_args = array(); - - /** - * The arguments for the PATCH request method - * - * @var array - */ - protected $_patch_args = array(); - - /** - * The arguments for the HEAD request method - * - * @var array - */ - protected $_head_args = array(); - - /** - * The arguments for the OPTIONS request method - * - * @var array - */ - protected $_options_args = array(); - - /** - * The arguments from GET, POST, PUT, DELETE request methods combined. - * - * @var array - */ - protected $_args = array(); - - /** - * If the request is allowed based on the API key provided. - * - * @var boolean - */ - protected $_allow = TRUE; - - /** - * Determines if output compression is enabled - * - * @var boolean - */ - protected $_zlib_oc = FALSE; - - /** - * The LDAP Distinguished Name of the User post authentication - * - * @var string - */ - protected $_user_ldap_dn = ''; - - /** - * The start of the response time from the server - * - * @var string - */ - protected $_start_rtime = ''; - - /** - * The end of the response time from the server - * - * @var string - */ - protected $_end_rtime = ''; - - /** - * List all supported methods, the first will be the default format - * - * @var array - */ - protected $_supported_formats = array( - 'xml' => 'application/xml', - 'json' => 'application/json', - 'jsonp' => 'application/javascript', - 'serialized' => 'application/vnd.php.serialized', - 'php' => 'text/plain', - 'html' => 'text/html', - 'csv' => 'application/csv' - ); - - /** - * Information about the current API user - * - * @var object - */ - protected $_apiuser; - - /** - * Developers can extend this class and add a check in here. - */ - protected function early_checks() - { - - } - - /** - * Constructor function - * @todo Document more please. - */ - public function __construct() - { - parent::__construct(); - - // Start the timer for how long the request takes - $this->_start_rtime = microtime(TRUE); - - // init objects - $this->request = new stdClass(); - $this->response = new stdClass(); - $this->rest = new stdClass(); - - $this->_zlib_oc = @ini_get('zlib.output_compression'); - - // Lets grab the config and get ready to party - $this->load->config('rest'); - - // let's learn about the request - $this->request = new stdClass(); - - // Check to see if this IP is Blacklisted - if ($this->config->item('rest_ip_blacklist_enabled')) - { - $this->_check_blacklist_auth(); - } - - // Is it over SSL? - $this->request->ssl = $this->_detect_ssl(); - - // How is this request being made? POST, DELETE, GET, PUT? - $this->request->method = $this->_detect_method(); - - // Create argument container, if nonexistent - if ( ! isset($this->{'_'.$this->request->method.'_args'})) - { - $this->{'_'.$this->request->method.'_args'} = array(); - } - - // Set up our GET variables - $this->_get_args = array_merge($this->_get_args, $this->uri->ruri_to_assoc()); - - // This library is bundled with REST_Controller 2.5+, but will eventually be part of CodeIgniter itself - $this->load->library('format'); - - // Try to find a format for the request (means we have a request body) - $this->request->format = $this->_detect_input_format(); - - // Some Methods cant have a body - $this->request->body = NULL; - - $this->{'_parse_' . $this->request->method}(); - - // Now we know all about our request, let's try and parse the body if it exists - if ($this->request->format and $this->request->body) - { - $this->request->body = $this->format->factory($this->request->body, $this->request->format)->to_array(); - // Assign payload arguments to proper method container - $this->{'_'.$this->request->method.'_args'} = $this->request->body; - } - - // Merge both for one mega-args variable - $this->_args = array_merge($this->_get_args, $this->_options_args, $this->_patch_args, $this->_head_args , $this->_put_args, $this->_post_args, $this->_delete_args, $this->{'_'.$this->request->method.'_args'}); - - // Which format should the data be returned in? - $this->response = new stdClass(); - $this->response->format = $this->_detect_output_format(); - - // Which format should the data be returned in? - $this->response->lang = $this->_detect_lang(); - - // Developers can extend this class and add a check in here - $this->early_checks(); - - // Check if there is a specific auth type for the current class/method - $this->auth_override = $this->_auth_override_check(); - - $this->rest = new StdClass(); - // Load DB if its enabled - if (config_item('rest_database_group') AND (config_item('rest_enable_keys') OR config_item('rest_enable_logging'))) - { - $this->rest->db = $this->load->database(config_item('rest_database_group'), TRUE); - } - - // Use whatever database is in use (isset returns false) - elseif (property_exists($this, "db")) - { - $this->rest->db = $this->db; - } - - // Checking for keys? GET TO WORK! - if (config_item('rest_enable_keys')) - { - $this->_allow = $this->_detect_api_key(); - } - - // only allow ajax requests - if ( ! $this->input->is_ajax_request() AND config_item('rest_ajax_only')) - { - $this->response(array('status' => false, 'error' => 'Only AJAX requests are accepted.'), 505); - } - - // When there is no specific override for the current class/method, use the default auth value set in the config - if ($this->auth_override !== TRUE && $this->_allow === FALSE) - { - if (strtolower( $this->config->item('rest_auth') ) == 'basic') - { - $this->_prepare_basic_auth(); - } - elseif (strtolower( $this->config->item('rest_auth') ) == 'digest') - { - $this->_prepare_digest_auth(); - } - elseif (strtolower( $this->config->item('rest_auth') ) == 'session') - { - $this->_check_php_session(); - } - elseif ($this->config->item('rest_ip_whitelist_enabled')) - { - $this->_check_whitelist_auth(); - } - // If we made it this far we either authenticated or were in the white list. - $this->_allow = TRUE; - } - } - - /** - * Destructor function - * @author Chris Kacerguis - */ - public function __destruct() - { - // Record the "stop" time of the request - $this->_end_rtime = microtime(TRUE); - // CK: if, we are logging, log the access time here, as we are done! - if (config_item('rest_enable_logging')) - { - $this->_log_access_time(); - } - - - } - - /** - * Remap - * - * Requests are not made to methods directly, the request will be for - * an "object". This simply maps the object and method to the correct - * Controller method. - * - * @param string $object_called - * @param array $arguments The arguments passed to the controller method. - */ - public function _remap($object_called, $arguments) - { - // Should we answer if not over SSL? - if (config_item('force_https') AND !$this->_detect_ssl()) - { - $this->response(array('status' => false, 'error' => 'Unsupported protocol'), 403); - } - - $pattern = '/^(.*)\.('.implode('|', array_keys($this->_supported_formats)).')$/'; - if (preg_match($pattern, $object_called, $matches)) - { - $object_called = $matches[1]; - } - - $controller_method = $object_called.'_'.$this->request->method; - - // Do we want to log this method (if allowed by config)? - $log_method = !(isset($this->methods[$controller_method]['log']) AND $this->methods[$controller_method]['log'] == FALSE); - - // Use keys for this method? - $use_key = ! (isset($this->methods[$controller_method]['key']) AND $this->methods[$controller_method]['key'] == FALSE); - - // They provided a key, but it wasn't valid, so get them out of here. - if (config_item('rest_enable_keys') AND $use_key AND $this->_allow === FALSE) - { - if (config_item('rest_enable_logging') AND $log_method) - { - $this->_log_request(); - } - - $this->response(array('status' => false, 'error' => 'Invalid API Key.'), 403); - } - - // Check to see if this key has access to the requested controller. - if (config_item('rest_enable_keys') AND $use_key AND !empty($this->rest->key) AND !$this->_check_access()) - { - if (config_item('rest_enable_logging') AND $log_method) - { - $this->_log_request(); - } - - $this->response(array('status' => false, 'error' => 'This API key does not have access to the requested controller.'), 401); - } - - // Sure it exists, but can they do anything with it? - if ( ! method_exists($this, $controller_method)) - { - $this->response(array('status' => false, 'error' => 'Unknown method.'), 404); - } - - // Doing key related stuff? Can only do it if they have a key right? - if (config_item('rest_enable_keys') AND !empty($this->rest->key)) - { - // Check the limit - if (config_item('rest_enable_limits') AND !$this->_check_limit($controller_method)) - { - $this->response(array('status' => false, 'error' => 'This API key has reached the hourly limit for this method.'), 401); - } - - // If no level is set use 0, they probably aren't using permissions - $level = isset($this->methods[$controller_method]['level']) ? $this->methods[$controller_method]['level'] : 0; - - // If no level is set, or it is lower than/equal to the key's level - $authorized = $level <= $this->rest->level; - - // IM TELLIN! - if (config_item('rest_enable_logging') AND $log_method) - { - $this->_log_request($authorized); - } - - // They don't have good enough perms - $authorized OR $this->response(array('status' => false, 'error' => 'This API key does not have enough permissions.'), 401); - } - - // No key stuff, but record that stuff is happening - else if (config_item('rest_enable_logging') AND $log_method) - { - $this->_log_request($authorized = TRUE); - } - - // And...... GO! - $this->_fire_method(array($this, $controller_method), $arguments); - } - - /** - * Fire Method - * - * Fires the designated controller method with the given arguments. - * - * @param array $method The controller method to fire - * @param array $args The arguments to pass to the controller method - */ - protected function _fire_method($method, $args) - { - call_user_func_array($method, $args); - } - - /** - * Response - * - * Takes pure data and optionally a status code, then creates the response. - * - * @param array $data - * @param null|int $http_code - */ - public function response($data = null, $http_code = null) - { - global $CFG; - - // If data is NULL and not code provide, error and bail - if ($data === NULL && $http_code === null) - { - $http_code = 404; - - // create the output variable here in the case of $this->response(array()); - $output = NULL; - } - - // If data is NULL but http code provided, keep the output empty - else if ($data === NULL && is_numeric($http_code)) - { - $output = NULL; - } - - // Otherwise (if no data but 200 provided) or some data, carry on camping! - else - { - // Is compression requested? - if ($CFG->item('compress_output') === TRUE && $this->_zlib_oc == FALSE) - { - if (extension_loaded('zlib')) - { - if (isset($_SERVER['HTTP_ACCEPT_ENCODING']) AND strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE) - { - ob_start('ob_gzhandler'); - } - } - } - - is_numeric($http_code) OR $http_code = 200; - - // @deprecated the following statement can be deleted. - // If the format method exists, call and return the output in that format - if (method_exists($this, '_format_'.$this->response->format)) - { - // Set the correct format header - header('Content-Type: '.$this->_supported_formats[$this->response->format]); - - $output = $this->{'_format_'.$this->response->format}($data); - } - - // If the format method exists, call and return the output in that format - elseif (method_exists($this->format, 'to_'.$this->response->format)) - { - // Set the correct format header - header('Content-Type: '.$this->_supported_formats[$this->response->format]); - - $output = $this->format->factory($data)->{'to_'.$this->response->format}(); - } - - // Format not supported, output directly - else - { - $output = $data; - } - } - - set_status_header($http_code); - - // If zlib.output_compression is enabled it will compress the output, - // but it will not modify the content-length header to compensate for - // the reduction, causing the browser to hang waiting for more data. - // We'll just skip content-length in those cases. - if ( ! $this->_zlib_oc && ! $CFG->item('compress_output')) - { - header('Content-Length: ' . strlen($output)); - } - - exit($output); - } - - /* - * Detect SSL use - * - * Detect whether SSL is being used or not - */ - protected function _detect_ssl() - { - return (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == "on"); - } - - - /* - * Detect input format - * - * Detect which format the HTTP Body is provided in - */ - protected function _detect_input_format() - { - if ($this->input->server('CONTENT_TYPE')) - { - // Check all formats against the HTTP_ACCEPT header - foreach ($this->_supported_formats as $format => $mime) - { - if (strpos($match = $this->input->server('CONTENT_TYPE'), ';')) - { - $match = current(explode(';', $match)); - } - - if ($match == $mime) - { - return $format; - } - } - } - - return NULL; - } - - /** - * Detect format - * - * Detect which format should be used to output the data. - * - * @return string The output format. - */ - protected function _detect_output_format() - { - $pattern = '/\.('.implode('|', array_keys($this->_supported_formats)).')$/'; - - // Check if a file extension is used when no get arguments provided - if (!$this->_get_args AND preg_match($pattern, $this->uri->uri_string(), $matches)) - { - return $matches[1]; - } - - // Check if a file extension is used - elseif ($this->_get_args AND !is_array(end($this->_get_args)) AND preg_match($pattern, end($this->_get_args), $matches)) - { - // The key of the last argument - $last_key = end(array_keys($this->_get_args)); - - // Remove the extension from arguments too - $this->_get_args[$last_key] = preg_replace($pattern, '', $this->_get_args[$last_key]); - $this->_args[$last_key] = preg_replace($pattern, '', $this->_args[$last_key]); - - return $matches[1]; - } - - // A format has been passed as an argument in the URL and it is supported - if (isset($this->_get_args['format']) AND array_key_exists($this->_get_args['format'], $this->_supported_formats)) - { - return $this->_get_args['format']; - } - - // Otherwise, check the HTTP_ACCEPT (if it exists and we are allowed) - if ($this->config->item('rest_ignore_http_accept') === FALSE AND $this->input->server('HTTP_ACCEPT')) - { - // Check all formats against the HTTP_ACCEPT header - foreach (array_keys($this->_supported_formats) as $format) - { - // Has this format been requested? - if (strpos($this->input->server('HTTP_ACCEPT'), $format) !== FALSE) - { - // If not HTML or XML assume its right and send it on its way - if ($format != 'html' AND $format != 'xml') - { - - return $format; - } - - // HTML or XML have shown up as a match - else - { - // If it is truly HTML, it wont want any XML - if ($format == 'html' AND strpos($this->input->server('HTTP_ACCEPT'), 'xml') === FALSE) - { - return $format; - } - - // If it is truly XML, it wont want any HTML - elseif ($format == 'xml' AND strpos($this->input->server('HTTP_ACCEPT'), 'html') === FALSE) - { - return $format; - } - } - } - } - } // End HTTP_ACCEPT checking - - // Well, none of that has worked! Let's see if the controller has a default - if ( ! empty($this->rest_format)) - { - return $this->rest_format; - } - - // Just use the default format - return config_item('rest_default_format'); - } - - /** - * Detect method - * - * Detect which HTTP method is being used - * - * @return string - */ - protected function _detect_method() - { - $method = strtolower($this->input->server('REQUEST_METHOD')); - - if ($this->config->item('enable_emulate_request')) - { - if ($this->input->post('_method')) - { - $method = strtolower($this->input->post('_method')); - } - elseif ($this->input->server('HTTP_X_HTTP_METHOD_OVERRIDE')) - { - $method = strtolower($this->input->server('HTTP_X_HTTP_METHOD_OVERRIDE')); - } - } - - if (in_array($method, $this->allowed_http_methods) && method_exists($this, '_parse_' . $method)) - { - return $method; - } - - return 'get'; - } - - /** - * Detect API Key - * - * See if the user has provided an API key - * - * @return boolean - */ - protected function _detect_api_key() - { - // Get the api key name variable set in the rest config file - $api_key_variable = config_item('rest_key_name'); - - // Work out the name of the SERVER entry based on config - $key_name = 'HTTP_'.strtoupper(str_replace('-', '_', $api_key_variable)); - - $this->rest->key = NULL; - $this->rest->level = NULL; - $this->rest->user_id = NULL; - $this->rest->ignore_limits = FALSE; - - // Find the key from server or arguments - if (($key = isset($this->_args[$api_key_variable]) ? $this->_args[$api_key_variable] : $this->input->server($key_name))) - { - if ( ! ($row = $this->rest->db->where(config_item('rest_key_column'), $key)->get(config_item('rest_keys_table'))->row())) - { - return FALSE; - } - - $this->rest->key = $row->{config_item('rest_key_column')}; - - isset($row->user_id) AND $this->rest->user_id = $row->user_id; - isset($row->level) AND $this->rest->level = $row->level; - isset($row->ignore_limits) AND $this->rest->ignore_limits = $row->ignore_limits; - - $this->_apiuser = $row; - - /* - * If "is private key" is enabled, compare the ip address with the list - * of valid ip addresses stored in the database. - */ - if(!empty($row->is_private_key)) - { - // Check for a list of valid ip addresses - if(isset($row->ip_addresses)) - { - // multiple ip addresses must be separated using a comma, explode and loop - $list_ip_addresses = explode(",", $row->ip_addresses); - $found_address = FALSE; - - foreach($list_ip_addresses as $ip_address) - { - if($this->input->ip_address() == trim($ip_address)) - { - // there is a match, set the the value to true and break out of the loop - $found_address = TRUE; - break; - } - } - - return $found_address; - } - else - { - // There should be at least one IP address for this private key. - return FALSE; - } - } - - return $row; - } - - // No key has been sent - return FALSE; - } - - /** - * Detect language(s) - * - * What language do they want it in? - * - * @return null|string The language code. - */ - protected function _detect_lang() - { - if ( ! $lang = $this->input->server('HTTP_ACCEPT_LANGUAGE')) - { - return NULL; - } - - // They might have sent a few, make it an array - if (strpos($lang, ',') !== FALSE) - { - $langs = explode(',', $lang); - - $return_langs = array(); - $i = 1; - foreach ($langs as $lang) - { - // Remove weight and strip space - list($lang) = explode(';', $lang); - $return_langs[] = trim($lang); - } - - return $return_langs; - } - - // Nope, just return the string - return $lang; - } - - /** - * Log request - * - * Record the entry for awesomeness purposes - * - * @param boolean $authorized - * @return object - */ - protected function _log_request($authorized = FALSE) - { - $status = $this->rest->db->insert(config_item('rest_logs_table'), array( - 'uri' => $this->uri->uri_string(), - 'method' => $this->request->method, - 'params' => $this->_args ? (config_item('rest_logs_json_params') ? json_encode($this->_args) : serialize($this->_args)) : null, - 'api_key' => isset($this->rest->key) ? $this->rest->key : '', - 'ip_address' => $this->input->ip_address(), - 'time' => function_exists('now') ? now() : time(), - 'authorized' => $authorized - )); - - $this->_insert_id = $this->rest->db->insert_id(); - return $status; - } - - /** - * Limiting requests - * - * Check if the requests are coming in a tad too fast. - * - * @param string $controller_method The method being called. - * @return boolean - */ - protected function _check_limit($controller_method) - { - // They are special, or it might not even have a limit - if ( ! empty($this->rest->ignore_limits) OR !isset($this->methods[$controller_method]['limit'])) - { - // On your way sonny-jim. - return TRUE; - } - - // How many times can you get to this method an hour? - $limit = $this->methods[$controller_method]['limit']; - - // Get data on a keys usage - $result = $this->rest->db - ->where('uri', $this->uri->uri_string()) - ->where('api_key', $this->rest->key) - ->get(config_item('rest_limits_table')) - ->row(); - - // No calls yet, or been an hour since they called - if ( ! $result OR $result->hour_started < time() - (60 * 60)) - { - // Right, set one up from scratch - $this->rest->db->insert(config_item('rest_limits_table'), array( - 'uri' => $this->uri->uri_string(), - 'api_key' => isset($this->rest->key) ? $this->rest->key : '', - 'count' => 1, - 'hour_started' => time() - )); - } - - // They have called within the hour, so lets update - else - { - // Your luck is out, you've called too many times! - if ($result->count >= $limit) - { - return FALSE; - } - - $this->rest->db - ->where('uri', $this->uri->uri_string()) - ->where('api_key', $this->rest->key) - ->set('count', 'count + 1', FALSE) - ->update(config_item('rest_limits_table')); - } - - return TRUE; - } - - /** - * Auth override check - * - * Check if there is a specific auth type set for the current class/method - * being called. - * - * @return boolean - */ - protected function _auth_override_check() - { - - // Assign the class/method auth type override array from the config - $this->overrides_array = $this->config->item('auth_override_class_method'); - - // Check to see if the override array is even populated, otherwise return false - if (empty($this->overrides_array)) - { - return false; - } - - // Check to see if there's an override value set for the current class/method being called - if (empty($this->overrides_array[$this->router->class][$this->router->method])) - { - return false; - } - - // None auth override found, prepare nothing but send back a true override flag - if ($this->overrides_array[$this->router->class][$this->router->method] == 'none') - { - return true; - } - - // Basic auth override found, prepare basic - if ($this->overrides_array[$this->router->class][$this->router->method] == 'basic') - { - $this->_prepare_basic_auth(); - return true; - } - - // Digest auth override found, prepare digest - if ($this->overrides_array[$this->router->class][$this->router->method] == 'digest') - { - $this->_prepare_digest_auth(); - return true; - } - - // Whitelist auth override found, check client's ip against config whitelist - if ($this->overrides_array[$this->router->class][$this->router->method] == 'whitelist') - { - $this->_check_whitelist_auth(); - return true; - } - - // Return false when there is an override value set but it does not match - // 'basic', 'digest', or 'none'. (the value was misspelled) - return false; - } - - /** - * Parse GET - */ - protected function _parse_get() - { - // Fix for Issue #247 - if ($this->input->is_cli_request()) { - $args = $_SERVER['argv']; - unset($args[0]); - $_SERVER['QUERY_STRING'] = $_SERVER['PATH_INFO'] = $_SERVER['REQUEST_URI'] = '/' . implode('/', $args) . '/'; - } - - // Grab proper GET variables - parse_str(parse_url(http://github.com/$_SERVER['REQUEST_URI'],%20PHP_URL_QUERY), $get); - - // Merge both the URI segments and GET params - $this->_get_args = array_merge($this->_get_args, $get); - } - - /** - * Parse POST - */ - protected function _parse_post() - { - $this->_post_args = $_POST; - - $this->request->format and $this->request->body = file_get_contents('php://input'); - } - - /** - * Parse PUT - */ - protected function _parse_put() - { - // It might be a HTTP body - if ($this->request->format) - { - $this->request->body = file_get_contents('php://input'); - } - - // If no file type is provided, this is probably just arguments - else - { - parse_str(file_get_contents('php://input'), $this->_put_args); - } - - } - - /** - * Parse HEAD - */ - protected function _parse_head() - { - // Grab proper HEAD variables - parse_str(parse_url(http://github.com/$_SERVER['REQUEST_URI'],%20PHP_URL_QUERY), $head); - - // Merge both the URI segments and HEAD params - $this->_head_args = array_merge($this->_head_args, $head); - } - - /** - * Parse OPTIONS - */ - protected function _parse_options() - { - // Grab proper OPTIONS variables - parse_str(parse_url(http://github.com/$_SERVER['REQUEST_URI'],%20PHP_URL_QUERY), $options); - - // Merge both the URI segments and OPTIONS params - $this->_options_args = array_merge($this->_options_args, $options); - } - - /** - * Parse PATCH - */ - protected function _parse_patch() - { - // It might be a HTTP body - if ($this->request->format) - { - $this->request->body = file_get_contents('php://input'); - } - - // If no file type is provided, this is probably just arguments - else - { - parse_str(file_get_contents('php://input'), $this->_patch_args); - } - } - - /** - * Parse DELETE - */ - protected function _parse_delete() - { - // Set up out DELETE variables (which shouldn't really exist, but sssh!) - parse_str(file_get_contents('php://input'), $this->_delete_args); - } - - // INPUT FUNCTION -------------------------------------------------------------- - - /** - * Retrieve a value from the GET request arguments. - * - * @param string $key The key for the GET request argument to retrieve - * @param boolean $xss_clean Whether the value should be XSS cleaned or not. - * @return string The GET argument value. - */ - public function get($key = NULL, $xss_clean = TRUE) - { - if ($key === NULL) - { - return $this->_get_args; - } - - return array_key_exists($key, $this->_get_args) ? $this->_xss_clean($this->_get_args[$key], $xss_clean) : FALSE; - } - - /** - * This function retrieves a values from the OPTIONS request arguments - * - * @param string $key The OPTIONS/GET argument key - * @param boolean $xss_clean Whether the value should be XSS cleaned or not - * @return string The OPTIONS/GET argument value - */ - public function options($key = NULL, $xss_clean = TRUE) - { - if ($key === NULL) { - return $this->_options_args; - } - - return array_key_exists($key, $this->_options_args) ? $this->_xss_clean($this->_options_args[$key], $xss_clean) : FALSE; - } - - /** - * This function retrieves a values from the HEAD request arguments - * - * @param string $key The HEAD/GET argument key - * @param boolean $xss_clean Whether the value should be XSS cleaned or not - * @return string The HEAD/GET argument value - */ - public function head($key = NULL, $xss_clean = TRUE) - { - if ($key === NULL) { - return $this->head_args; - } - - return array_key_exists($key, $this->head_args) ? $this->_xss_clean($this->head_args[$key], $xss_clean) : FALSE; - } - - /** - * Retrieve a value from the POST request arguments. - * - * @param string $key The key for the POST request argument to retrieve - * @param boolean $xss_clean Whether the value should be XSS cleaned or not. - * @return string The POST argument value. - */ - public function post($key = NULL, $xss_clean = TRUE) - { - if ($key === NULL) - { - return $this->_post_args; - } - - return array_key_exists($key, $this->_post_args) ? $this->_xss_clean($this->_post_args[$key], $xss_clean) : FALSE; - } - - /** - * Retrieve a value from the PUT request arguments. - * - * @param string $key The key for the PUT request argument to retrieve - * @param boolean $xss_clean Whether the value should be XSS cleaned or not. - * @return string The PUT argument value. - */ - public function put($key = NULL, $xss_clean = TRUE) - { - if ($key === NULL) - { - return $this->_put_args; - } - - return array_key_exists($key, $this->_put_args) ? $this->_xss_clean($this->_put_args[$key], $xss_clean) : FALSE; - } - - /** - * Retrieve a value from the DELETE request arguments. - * - * @param string $key The key for the DELETE request argument to retrieve - * @param boolean $xss_clean Whether the value should be XSS cleaned or not. - * @return string The DELETE argument value. - */ - public function delete($key = NULL, $xss_clean = TRUE) - { - if ($key === NULL) - { - return $this->_delete_args; - } - - return array_key_exists($key, $this->_delete_args) ? $this->_xss_clean($this->_delete_args[$key], $xss_clean) : FALSE; - } - - /** - * Retrieve a value from the PATCH request arguments. - * - * @param string $key The key for the PATCH request argument to retrieve - * @param boolean $xss_clean Whether the value should be XSS cleaned or not. - * @return string The PATCH argument value. - */ - public function patch($key = NULL, $xss_clean = TRUE) - { - if ($key === NULL) - { - return $this->_patch_args; - } - - return array_key_exists($key, $this->_patch_args) ? $this->_xss_clean($this->_patch_args[$key], $xss_clean) : FALSE; - } - - /** - * Process to protect from XSS attacks. - * - * @param string $val The input. - * @param boolean $process Do clean or note the input. - * @return string - */ - protected function _xss_clean($val, $process) - { - if (CI_VERSION < 2) - { - return $process ? $this->input->xss_clean($val) : $val; - } - - return $process ? $this->security->xss_clean($val) : $val; - } - - /** - * Retrieve the validation errors. - * - * @return array - */ - public function validation_errors() - { - $string = strip_tags($this->form_validation->error_string()); - - return explode("\n", trim($string, "\n")); - } - - // SECURITY FUNCTIONS --------------------------------------------------------- - - /** - * Perform LDAP Authentication - * - * @param string $username The username to validate - * @param string $password The password to validate - * @return boolean - */ - protected function _perform_ldap_auth($username = '', $password = NULL) - { - if (empty($username)) - { - log_message('debug', 'LDAP Auth: failure, empty username'); - return false; - } - - log_message('debug', 'LDAP Auth: Loading Config'); - - $this->config->load('ldap.php', true); - - $ldaptimeout = $this->config->item('timeout', 'ldap'); - $ldaphost = $this->config->item('server', 'ldap'); - $ldapport = $this->config->item('port', 'ldap'); - $ldaprdn = $this->config->item('binduser', 'ldap'); - $ldappass = $this->config->item('bindpw', 'ldap'); - $ldapbasedn = $this->config->item('basedn', 'ldap'); - - log_message('debug', 'LDAP Auth: Connect to ' . $ldaphost); - - $ldapconfig['authrealm'] = $this->config->item('domain', 'ldap'); - - // connect to ldap server - $ldapconn = ldap_connect($ldaphost, $ldapport); - - if ($ldapconn) { - - log_message('debug', 'Setting timeout to ' . $ldaptimeout . ' seconds'); - - ldap_set_option($ldapconn, LDAP_OPT_NETWORK_TIMEOUT, $ldaptimeout); - - log_message('debug', 'LDAP Auth: Binding to ' . $ldaphost . ' with dn ' . $ldaprdn); - - // binding to ldap server - $ldapbind = ldap_bind($ldapconn, $ldaprdn, $ldappass); - - // verify binding - if ($ldapbind) { - log_message('debug', 'LDAP Auth: bind successful'); - } else { - log_message('error', 'LDAP Auth: bind unsuccessful'); - return false; - } - - } - - // search for user - if (($res_id = ldap_search( $ldapconn, $ldapbasedn, "uid=$username")) == false) { - log_message('error', 'LDAP Auth: User ' . $username . ' not found in search'); - return false; - } - - if (ldap_count_entries($ldapconn, $res_id) != 1) { - log_message('error', 'LDAP Auth: failure, username ' . $username . 'found more than once'); - return false; - } - - if (( $entry_id = ldap_first_entry($ldapconn, $res_id))== false) { - log_message('error', 'LDAP Auth: failure, entry of searchresult could not be fetched'); - return false; - } - - if (( $user_dn = ldap_get_dn($ldapconn, $entry_id)) == false) { - log_message('error', 'LDAP Auth: failure, user-dn could not be fetched'); - return false; - } - - // User found, could not authenticate as user - if (($link_id = ldap_bind($ldapconn, $user_dn, $password)) == false) { - log_message('error', 'LDAP Auth: failure, username/password did not match: ' . $user_dn); - return false; - } - - log_message('debug', 'LDAP Auth: Success ' . $user_dn . ' authenticated successfully'); - - $this->_user_ldap_dn = $user_dn; - ldap_close($ldapconn); - return true; - } - - /** - * Perform Library Authentication - Override this function to change the way the library is called - * - * @param string $username The username to validate - * @param string $password The password to validate - * @return boolean - */ - protected function _perform_library_auth($username = '', $password = NULL) - { - if (empty($username)) - { - log_message('debug', 'Library Auth: failure, empty username'); - return false; - } - - $auth_library_class = strtolower($this->config->item('auth_library_class')); - $auth_library_function = strtolower($this->config->item('auth_library_function')); - - if (empty($auth_library_class)) - { - log_message('debug', 'Library Auth: failure, empty auth_library_class'); - return false; - } - - if (empty($auth_library_function)) - { - log_message('debug', 'Library Auth: failure, empty auth_library_function'); - return false; - } - - $this->load->library($auth_library_class); - return $this->$auth_library_class->$auth_library_function($username, $password); - } - - /** - * Check if the user is logged in. - * - * @param string $username The user's name - * @param string $password The user's password - * @return boolean - */ - protected function _check_login($username = '', $password = NULL) - { - if (empty($username)) - { - return FALSE; - } - - $auth_source = strtolower($this->config->item('auth_source')); - - if ($auth_source == 'ldap') - { - log_message('debug', 'performing LDAP authentication for $username'); - return $this->_perform_ldap_auth($username, $password); - } - - if ($auth_source == 'library') - { - log_message('debug', 'performing Library authentication for $username'); - return $this->_perform_library_auth($username, $password); - } - - $valid_logins = $this->config->item('rest_valid_logins'); - - if ( ! array_key_exists($username, $valid_logins)) - { - return FALSE; - } - - // If actually NULL (not empty string) then do not check it - if ($password === NULL AND $valid_logins[$username] != $password) - { - return FALSE; - } - - return TRUE; - } - - /** - * Check to see if the user is logged into the web app with a php session key. - */ - protected function _check_php_session() - { - $key = $this->config->item('auth_source'); - if (!$this->session->userdata($key)) - { - $this->response(array('status' => false, 'error' => 'Not Authorized'), 401); - } - } - - /** - * @todo document this. - */ - protected function _prepare_basic_auth() - { - // If whitelist is enabled it has the first chance to kick them out - if (config_item('rest_ip_whitelist_enabled')) - { - $this->_check_whitelist_auth(); - } - - $username = NULL; - $password = NULL; - - // mod_php - if ($this->input->server('PHP_AUTH_USER')) - { - $username = $this->input->server('PHP_AUTH_USER'); - $password = $this->input->server('PHP_AUTH_PW'); - } - - // most other servers - elseif ($this->input->server('HTTP_AUTHENTICATION')) - { - if (strpos(strtolower($this->input->server('HTTP_AUTHENTICATION')), 'basic') === 0) - { - list($username, $password) = explode(':', base64_decode(substr($this->input->server('HTTP_AUTHORIZATION'), 6))); - } - } - - if ( ! $this->_check_login($username, $password)) - { - $this->_force_login(); - } - } - - /** - * @todo Document this. - */ - protected function _prepare_digest_auth() - { - // If whitelist is enabled it has the first chance to kick them out - if (config_item('rest_ip_whitelist_enabled')) - { - $this->_check_whitelist_auth(); - } - - $uniqid = uniqid(""); // Empty argument for backward compatibility - // We need to test which server authentication variable to use - // because the PHP ISAPI module in IIS acts different from CGI - if ($this->input->server('PHP_AUTH_DIGEST')) - { - $digest_string = $this->input->server('PHP_AUTH_DIGEST'); - } - elseif ($this->input->server('HTTP_AUTHORIZATION')) - { - $digest_string = $this->input->server('HTTP_AUTHORIZATION'); - } - else - { - $digest_string = ""; - } - - // The $_SESSION['error_prompted'] variable is used to ask the password - // again if none given or if the user enters wrong auth information. - if (empty($digest_string)) - { - $this->_force_login($uniqid); - } - - // We need to retrieve authentication informations from the $auth_data variable - preg_match_all('@(username|nonce|uri|nc|cnonce|qop|response)=[\'"]?([^\'",]+)@', $digest_string, $matches); - $digest = (empty($matches[1]) || empty($matches[2])) ? array() : array_combine($matches[1], $matches[2]); - - if ( ! array_key_exists('username', $digest) OR !$this->_check_login($digest['username'])) - { - $this->_force_login($uniqid); - } - - $valid_logins = $this->config->item('rest_valid_logins'); - $valid_pass = $valid_logins[$digest['username']]; - - // This is the valid response expected - $A1 = md5($digest['username'].':'.$this->config->item('rest_realm').':'.$valid_pass); - $A2 = md5(strtoupper($this->request->method).':'.$digest['uri']); - $valid_response = md5($A1.':'.$digest['nonce'].':'.$digest['nc'].':'.$digest['cnonce'].':'.$digest['qop'].':'.$A2); - - if ($digest['response'] != $valid_response) - { - set_status_header(401); - exit; - } - } - - /** - * Check if the client's ip is in the 'rest_ip_blacklist' config - */ - protected function _check_blacklist_auth() - { - $blacklist = explode(',', config_item('rest_ip_blacklist')); - - foreach ($blacklist AS &$ip) - { - $ip = trim($ip); - } - - if (in_array($this->input->ip_address(), $blacklist)) - { - $this->response(array('status' => false, 'error' => 'IP Denied'), 401); - } - } - - /** - * Check if the client's ip is in the 'rest_ip_whitelist' config - */ - protected function _check_whitelist_auth() - { - $whitelist = explode(',', config_item('rest_ip_whitelist')); - - array_push($whitelist, '127.0.0.1', '0.0.0.0'); - - foreach ($whitelist AS &$ip) - { - $ip = trim($ip); - } - - if ( ! in_array($this->input->ip_address(), $whitelist)) - { - $this->response(array('status' => false, 'error' => 'Not authorized'), 401); - } - } - - /** - * @todo Document this. - * - * @param string $nonce - */ - protected function _force_login($nonce = '') - { - if (strtolower( $this->config->item('rest_auth') ) == 'basic') - { - header('WWW-Authenticate: Basic realm="'.$this->config->item('rest_realm').'"'); - } - elseif (strtolower( $this->config->item('rest_auth') ) == 'digest') - { - header('WWW-Authenticate: Digest realm="'.$this->config->item('rest_realm').'", qop="auth", nonce="'.$nonce.'", opaque="'.md5($this->config->item('rest_realm')).'"'); - } - - $this->response(array('status' => false, 'error' => 'Not authorized'), 401); - } - - /** - * Force it into an array - * - * @param object|array $data - * @return array - */ - protected function _force_loopable($data) - { - // Force it to be something useful - if ( ! is_array($data) AND !is_object($data)) - { - $data = (array) $data; - } - - return $data; - } - - /** - * updates the log with the access time - * - * @author Chris Kacerguis - * @return boolean - */ - - protected function _log_access_time() - { - $payload['rtime'] = $this->_end_rtime - $this->_start_rtime; - return $this->rest->db->update(config_item('rest_logs_table'), $payload, array('id' => $this->_insert_id)); - } - - /** - * Check to see if the API key has access to the controller and methods - * - * @return boolean - */ - protected function _check_access() - { - // if we don't want to check acccess, just return TRUE - if (config_item('rest_enable_access') === FALSE) - { - return TRUE; - } - - $controller = explode('/', $this->uri->uri_string()); - - $this->rest->db->select(); - $this->rest->db->where('key', $this->rest->key); - $this->rest->db->where('controller', $controller[0]); - - $query = $this->rest->db->get(config_item('rest_access_table')); - - if ($query->num_rows > 0) - { - return TRUE; - } - - return FALSE; - } + /** + * This defines the rest format. + * + * Must be overridden it in a controller so that it is set. + * + * @var string|null + */ + protected $rest_format = NULL; + + /** + * Defines the list of method properties such as limit, log and level + * + * @var array + */ + protected $methods = array(); + + /** + * List of allowed HTTP methods + * + * @var array + */ + protected $allowed_http_methods = array('get', 'delete', 'post', 'put', 'options', 'patch', 'head'); + + /** + * General request data and information. + * Stores accept, language, body, headers, etc. + * + * @var object + */ + protected $request = NULL; + + /** + * What is gonna happen in output? + * + * @var object + */ + protected $response = NULL; + + /** + * Stores DB, keys, key level, etc + * + * @var object + */ + protected $rest = NULL; + + /** + * The arguments for the GET request method + * + * @var array + */ + protected $_get_args = array(); + + /** + * The arguments for the POST request method + * + * @var array + */ + protected $_post_args = array(); + + /** + * The insert_id of the log entry (if we have one) + * + * @var string + */ + protected $_insert_id = ''; + + /** + * The arguments for the PUT request method + * + * @var array + */ + protected $_put_args = array(); + + /** + * The arguments for the DELETE request method + * + * @var array + */ + protected $_delete_args = array(); + + /** + * The arguments for the PATCH request method + * + * @var array + */ + protected $_patch_args = array(); + + /** + * The arguments for the HEAD request method + * + * @var array + */ + protected $_head_args = array(); + + /** + * The arguments for the OPTIONS request method + * + * @var array + */ + protected $_options_args = array(); + + /** + * The arguments from GET, POST, PUT, DELETE request methods combined. + * + * @var array + */ + protected $_args = array(); + + /** + * If the request is allowed based on the API key provided. + * + * @var boolean + */ + protected $_allow = TRUE; + + /** + * Determines if output compression is enabled + * + * @var boolean + */ + protected $_zlib_oc = FALSE; + + /** + * The LDAP Distinguished Name of the User post authentication + * + * @var string + */ + protected $_user_ldap_dn = ''; + + /** + * The start of the response time from the server + * + * @var string + */ + protected $_start_rtime = ''; + + /** + * The end of the response time from the server + * + * @var string + */ + protected $_end_rtime = ''; + + /** + * List all supported methods, the first will be the default format + * + * @var array + */ + protected $_supported_formats = array( + 'xml' => 'application/xml', + 'json' => 'application/json', + 'jsonp' => 'application/javascript', + 'serialized' => 'application/vnd.php.serialized', + 'php' => 'text/plain', + 'html' => 'text/html', + 'csv' => 'application/csv' + ); + + /** + * Information about the current API user + * + * @var object + */ + protected $_apiuser; + + /** + * Developers can extend this class and add a check in here. + */ + protected function early_checks() + { + + } + + /** + * Constructor function + * @todo Document more please. + */ + public function __construct() + { + parent::__construct(); + + // Start the timer for how long the request takes + $this->_start_rtime = microtime(TRUE); + + // init objects + $this->request = new stdClass(); + $this->response = new stdClass(); + $this->rest = new stdClass(); + + $this->_zlib_oc = @ini_get('zlib.output_compression'); + + // Lets grab the config and get ready to party + $this->load->config('rest'); + + // let's learn about the request + $this->request = new stdClass(); + + // Check to see if this IP is Blacklisted + if ($this->config->item('rest_ip_blacklist_enabled')) { + $this->_check_blacklist_auth(); + } + + // Is it over SSL? + $this->request->ssl = $this->_detect_ssl(); + + // How is this request being made? POST, DELETE, GET, PUT? + $this->request->method = $this->_detect_method(); + + // Create argument container, if nonexistent + if ( ! isset($this->{'_'.$this->request->method.'_args'})) { + $this->{'_'.$this->request->method.'_args'} = array(); + } + + // Set up our GET variables + $this->_get_args = array_merge($this->_get_args, $this->uri->ruri_to_assoc()); + + // This library is bundled with REST_Controller 2.5+, but will eventually be part of CodeIgniter itself + $this->load->library('format'); + + // Try to find a format for the request (means we have a request body) + $this->request->format = $this->_detect_input_format(); + + // Some Methods cant have a body + $this->request->body = NULL; + + $this->{'_parse_' . $this->request->method}(); + + // Now we know all about our request, let's try and parse the body if it exists + if ($this->request->format and $this->request->body) { + $this->request->body = $this->format->factory($this->request->body, $this->request->format)->to_array(); + // Assign payload arguments to proper method container + $this->{'_'.$this->request->method.'_args'} = $this->request->body; + } + + // Merge both for one mega-args variable + $this->_args = array_merge($this->_get_args, $this->_options_args, $this->_patch_args, $this->_head_args , $this->_put_args, $this->_post_args, $this->_delete_args, $this->{'_'.$this->request->method.'_args'}); + + // Which format should the data be returned in? + $this->response = new stdClass(); + $this->response->format = $this->_detect_output_format(); + + // Which format should the data be returned in? + $this->response->lang = $this->_detect_lang(); + + // Developers can extend this class and add a check in here + $this->early_checks(); + + // Check if there is a specific auth type for the current class/method + $this->auth_override = $this->_auth_override_check(); + + $this->rest = new StdClass(); + // Load DB if its enabled + if (config_item('rest_database_group') AND (config_item('rest_enable_keys') OR config_item('rest_enable_logging'))) { + $this->rest->db = $this->load->database(config_item('rest_database_group'), TRUE); + } + + // Use whatever database is in use (isset returns false) + elseif (property_exists($this, "db")) { + $this->rest->db = $this->db; + } + + // Checking for keys? GET TO WORK! + if (config_item('rest_enable_keys')) { + $this->_allow = $this->_detect_api_key(); + } + + // only allow ajax requests + if ( ! $this->input->is_ajax_request() AND config_item('rest_ajax_only')) { + $this->response(array('status' => false, 'error' => 'Only AJAX requests are accepted.'), 505); + } + + // When there is no specific override for the current class/method, use the default auth value set in the config + if ($this->auth_override !== TRUE && $this->_allow === FALSE) { + if (strtolower( $this->config->item('rest_auth') ) == 'basic') { + $this->_prepare_basic_auth(); + } elseif (strtolower( $this->config->item('rest_auth') ) == 'digest') { + $this->_prepare_digest_auth(); + } elseif (strtolower( $this->config->item('rest_auth') ) == 'session') { + $this->_check_php_session(); + } elseif ($this->config->item('rest_ip_whitelist_enabled')) { + $this->_check_whitelist_auth(); + } + // If we made it this far we either authenticated or were in the white list. + $this->_allow = TRUE; + } + } + + /** + * Destructor function + * @author Chris Kacerguis + */ + public function __destruct() + { + // Record the "stop" time of the request + $this->_end_rtime = microtime(TRUE); + // CK: if, we are logging, log the access time here, as we are done! + if (config_item('rest_enable_logging')) { + $this->_log_access_time(); + } + + } + + /** + * Remap + * + * Requests are not made to methods directly, the request will be for + * an "object". This simply maps the object and method to the correct + * Controller method. + * + * @param string $object_called + * @param array $arguments The arguments passed to the controller method. + */ + public function _remap($object_called, $arguments) + { + // Should we answer if not over SSL? + if (config_item('force_https') AND !$this->_detect_ssl()) { + $this->response(array('status' => false, 'error' => 'Unsupported protocol'), 403); + } + + $pattern = '/^(.*)\.('.implode('|', array_keys($this->_supported_formats)).')$/'; + if (preg_match($pattern, $object_called, $matches)) { + $object_called = $matches[1]; + } + + $controller_method = $object_called.'_'.$this->request->method; + + // Do we want to log this method (if allowed by config)? + $log_method = !(isset($this->methods[$controller_method]['log']) AND $this->methods[$controller_method]['log'] == FALSE); + + // Use keys for this method? + $use_key = ! (isset($this->methods[$controller_method]['key']) AND $this->methods[$controller_method]['key'] == FALSE); + + // They provided a key, but it wasn't valid, so get them out of here. + if (config_item('rest_enable_keys') AND $use_key AND $this->_allow === FALSE) { + if (config_item('rest_enable_logging') AND $log_method) { + $this->_log_request(); + } + + $this->response(array('status' => false, 'error' => 'Invalid API Key.'), 403); + } + + // Check to see if this key has access to the requested controller. + if (config_item('rest_enable_keys') AND $use_key AND !empty($this->rest->key) AND !$this->_check_access()) { + if (config_item('rest_enable_logging') AND $log_method) { + $this->_log_request(); + } + + $this->response(array('status' => false, 'error' => 'This API key does not have access to the requested controller.'), 401); + } + + // Sure it exists, but can they do anything with it? + if ( ! method_exists($this, $controller_method)) { + $this->response(array('status' => false, 'error' => 'Unknown method.'), 404); + } + + // Doing key related stuff? Can only do it if they have a key right? + if (config_item('rest_enable_keys') AND !empty($this->rest->key)) { + // Check the limit + if (config_item('rest_enable_limits') AND !$this->_check_limit($controller_method)) { + $this->response(array('status' => false, 'error' => 'This API key has reached the hourly limit for this method.'), 401); + } + + // If no level is set use 0, they probably aren't using permissions + $level = isset($this->methods[$controller_method]['level']) ? $this->methods[$controller_method]['level'] : 0; + + // If no level is set, or it is lower than/equal to the key's level + $authorized = $level <= $this->rest->level; + + // IM TELLIN! + if (config_item('rest_enable_logging') AND $log_method) { + $this->_log_request($authorized); + } + + // They don't have good enough perms + $authorized OR $this->response(array('status' => false, 'error' => 'This API key does not have enough permissions.'), 401); + } + + // No key stuff, but record that stuff is happening + else if (config_item('rest_enable_logging') AND $log_method) { + $this->_log_request($authorized = TRUE); + } + + // And...... GO! + $this->_fire_method(array($this, $controller_method), $arguments); + } + + /** + * Fire Method + * + * Fires the designated controller method with the given arguments. + * + * @param array $method The controller method to fire + * @param array $args The arguments to pass to the controller method + */ + protected function _fire_method($method, $args) + { + call_user_func_array($method, $args); + } + + /** + * Response + * + * Takes pure data and optionally a status code, then creates the response. + * + * @param array $data + * @param null|int $http_code + */ + public function response($data = null, $http_code = null) + { + global $CFG; + + // If data is NULL and not code provide, error and bail + if ($data === NULL && $http_code === null) { + $http_code = 404; + + // create the output variable here in the case of $this->response(array()); + $output = NULL; + } + + // If data is NULL but http code provided, keep the output empty + else if ($data === NULL && is_numeric($http_code)) { + $output = NULL; + } + + // Otherwise (if no data but 200 provided) or some data, carry on camping! + else { + // Is compression requested? + if ($CFG->item('compress_output') === TRUE && $this->_zlib_oc == FALSE) { + if (extension_loaded('zlib')) { + if (isset($_SERVER['HTTP_ACCEPT_ENCODING']) AND strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE) { + ob_start('ob_gzhandler'); + } + } + } + + is_numeric($http_code) OR $http_code = 200; + + // @deprecated the following statement can be deleted. + // If the format method exists, call and return the output in that format + if (method_exists($this, '_format_'.$this->response->format)) { + // Set the correct format header + header('Content-Type: '.$this->_supported_formats[$this->response->format]); + + $output = $this->{'_format_'.$this->response->format}($data); + } + + // If the format method exists, call and return the output in that format + elseif (method_exists($this->format, 'to_'.$this->response->format)) { + // Set the correct format header + header('Content-Type: '.$this->_supported_formats[$this->response->format]); + + $output = $this->format->factory($data)->{'to_'.$this->response->format}(); + } + + // Format not supported, output directly + else { + $output = $data; + } + } + + set_status_header($http_code); + + // If zlib.output_compression is enabled it will compress the output, + // but it will not modify the content-length header to compensate for + // the reduction, causing the browser to hang waiting for more data. + // We'll just skip content-length in those cases. + if ( ! $this->_zlib_oc && ! $CFG->item('compress_output')) { + header('Content-Length: ' . strlen($output)); + } + + exit($output); + } + + /* + * Detect SSL use + * + * Detect whether SSL is being used or not + */ + protected function _detect_ssl() + { + return (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == "on"); + } + + + /* + * Detect input format + * + * Detect which format the HTTP Body is provided in + */ + protected function _detect_input_format() + { + if ($this->input->server('CONTENT_TYPE')) { + // Check all formats against the HTTP_ACCEPT header + foreach ($this->_supported_formats as $format => $mime) { + if (strpos($match = $this->input->server('CONTENT_TYPE'), ';')) { + $match = current(explode(';', $match)); + } + + if ($match == $mime) { + return $format; + } + } + } + + return NULL; + } + + /** + * Detect format + * + * Detect which format should be used to output the data. + * + * @return string The output format. + */ + protected function _detect_output_format() + { + $pattern = '/\.('.implode('|', array_keys($this->_supported_formats)).')$/'; + + // Check if a file extension is used when no get arguments provided + if (!$this->_get_args AND preg_match($pattern, $this->uri->uri_string(), $matches)) { + return $matches[1]; + } + + // Check if a file extension is used + elseif ($this->_get_args AND !is_array(end($this->_get_args)) AND preg_match($pattern, end($this->_get_args), $matches)) { + // The key of the last argument + $last_key = end(array_keys($this->_get_args)); + + // Remove the extension from arguments too + $this->_get_args[$last_key] = preg_replace($pattern, '', $this->_get_args[$last_key]); + $this->_args[$last_key] = preg_replace($pattern, '', $this->_args[$last_key]); + + return $matches[1]; + } + + // A format has been passed as an argument in the URL and it is supported + if (isset($this->_get_args['format']) AND array_key_exists($this->_get_args['format'], $this->_supported_formats)) { + return $this->_get_args['format']; + } + + // Otherwise, check the HTTP_ACCEPT (if it exists and we are allowed) + if ($this->config->item('rest_ignore_http_accept') === FALSE AND $this->input->server('HTTP_ACCEPT')) { + // Check all formats against the HTTP_ACCEPT header + foreach (array_keys($this->_supported_formats) as $format) { + // Has this format been requested? + if (strpos($this->input->server('HTTP_ACCEPT'), $format) !== FALSE) { + // If not HTML or XML assume its right and send it on its way + if ($format != 'html' AND $format != 'xml') { + return $format; + } + + // HTML or XML have shown up as a match + else { + // If it is truly HTML, it wont want any XML + if ($format == 'html' AND strpos($this->input->server('HTTP_ACCEPT'), 'xml') === FALSE) { + return $format; + } + + // If it is truly XML, it wont want any HTML + elseif ($format == 'xml' AND strpos($this->input->server('HTTP_ACCEPT'), 'html') === FALSE) { + return $format; + } + } + } + } + } // End HTTP_ACCEPT checking + + // Well, none of that has worked! Let's see if the controller has a default + if ( ! empty($this->rest_format)) { + return $this->rest_format; + } + + // Just use the default format + return config_item('rest_default_format'); + } + + /** + * Detect method + * + * Detect which HTTP method is being used + * + * @return string + */ + protected function _detect_method() + { + $method = strtolower($this->input->server('REQUEST_METHOD')); + + if ($this->config->item('enable_emulate_request')) { + if ($this->input->post('_method')) { + $method = strtolower($this->input->post('_method')); + } elseif ($this->input->server('HTTP_X_HTTP_METHOD_OVERRIDE')) { + $method = strtolower($this->input->server('HTTP_X_HTTP_METHOD_OVERRIDE')); + } + } + + if (in_array($method, $this->allowed_http_methods) && method_exists($this, '_parse_' . $method)) { + return $method; + } + + return 'get'; + } + + /** + * Detect API Key + * + * See if the user has provided an API key + * + * @return boolean + */ + protected function _detect_api_key() + { + // Get the api key name variable set in the rest config file + $api_key_variable = config_item('rest_key_name'); + + // Work out the name of the SERVER entry based on config + $key_name = 'HTTP_'.strtoupper(str_replace('-', '_', $api_key_variable)); + + $this->rest->key = NULL; + $this->rest->level = NULL; + $this->rest->user_id = NULL; + $this->rest->ignore_limits = FALSE; + + // Find the key from server or arguments + if (($key = isset($this->_args[$api_key_variable]) ? $this->_args[$api_key_variable] : $this->input->server($key_name))) { + if ( ! ($row = $this->rest->db->where(config_item('rest_key_column'), $key)->get(config_item('rest_keys_table'))->row())) { + return FALSE; + } + + $this->rest->key = $row->{config_item('rest_key_column')}; + + isset($row->user_id) AND $this->rest->user_id = $row->user_id; + isset($row->level) AND $this->rest->level = $row->level; + isset($row->ignore_limits) AND $this->rest->ignore_limits = $row->ignore_limits; + + $this->_apiuser = $row; + + /* + * If "is private key" is enabled, compare the ip address with the list + * of valid ip addresses stored in the database. + */ + if (!empty($row->is_private_key)) { + // Check for a list of valid ip addresses + if (isset($row->ip_addresses)) { + // multiple ip addresses must be separated using a comma, explode and loop + $list_ip_addresses = explode(",", $row->ip_addresses); + $found_address = FALSE; + + foreach ($list_ip_addresses as $ip_address) { + if ($this->input->ip_address() == trim($ip_address)) { + // there is a match, set the the value to true and break out of the loop + $found_address = TRUE; + break; + } + } + + return $found_address; + } else { + // There should be at least one IP address for this private key. + return FALSE; + } + } + + return $row; + } + + // No key has been sent + return FALSE; + } + + /** + * Detect language(s) + * + * What language do they want it in? + * + * @return null|string The language code. + */ + protected function _detect_lang() + { + if ( ! $lang = $this->input->server('HTTP_ACCEPT_LANGUAGE')) { + return NULL; + } + + // They might have sent a few, make it an array + if (strpos($lang, ',') !== FALSE) { + $langs = explode(',', $lang); + + $return_langs = array(); + $i = 1; + foreach ($langs as $lang) { + // Remove weight and strip space + list($lang) = explode(';', $lang); + $return_langs[] = trim($lang); + } + + return $return_langs; + } + + // Nope, just return the string + return $lang; + } + + /** + * Log request + * + * Record the entry for awesomeness purposes + * + * @param boolean $authorized + * @return object + */ + protected function _log_request($authorized = FALSE) + { + $status = $this->rest->db->insert(config_item('rest_logs_table'), array( + 'uri' => $this->uri->uri_string(), + 'method' => $this->request->method, + 'params' => $this->_args ? (config_item('rest_logs_json_params') ? json_encode($this->_args) : serialize($this->_args)) : null, + 'api_key' => isset($this->rest->key) ? $this->rest->key : '', + 'ip_address' => $this->input->ip_address(), + 'time' => function_exists('now') ? now() : time(), + 'authorized' => $authorized + )); + + $this->_insert_id = $this->rest->db->insert_id(); + + return $status; + } + + /** + * Limiting requests + * + * Check if the requests are coming in a tad too fast. + * + * @param string $controller_method The method being called. + * @return boolean + */ + protected function _check_limit($controller_method) + { + // They are special, or it might not even have a limit + if ( ! empty($this->rest->ignore_limits) OR !isset($this->methods[$controller_method]['limit'])) { + // On your way sonny-jim. + return TRUE; + } + + // How many times can you get to this method an hour? + $limit = $this->methods[$controller_method]['limit']; + + // Get data on a keys usage + $result = $this->rest->db + ->where('uri', $this->uri->uri_string()) + ->where('api_key', $this->rest->key) + ->get(config_item('rest_limits_table')) + ->row(); + + // No calls yet, or been an hour since they called + if ( ! $result OR $result->hour_started < time() - (60 * 60)) { + // Right, set one up from scratch + $this->rest->db->insert(config_item('rest_limits_table'), array( + 'uri' => $this->uri->uri_string(), + 'api_key' => isset($this->rest->key) ? $this->rest->key : '', + 'count' => 1, + 'hour_started' => time() + )); + } + + // They have called within the hour, so lets update + else { + // Your luck is out, you've called too many times! + if ($result->count >= $limit) { + return FALSE; + } + + $this->rest->db + ->where('uri', $this->uri->uri_string()) + ->where('api_key', $this->rest->key) + ->set('count', 'count + 1', FALSE) + ->update(config_item('rest_limits_table')); + } + + return TRUE; + } + + /** + * Auth override check + * + * Check if there is a specific auth type set for the current class/method + * being called. + * + * @return boolean + */ + protected function _auth_override_check() + { + + // Assign the class/method auth type override array from the config + $this->overrides_array = $this->config->item('auth_override_class_method'); + + // Check to see if the override array is even populated, otherwise return false + if (empty($this->overrides_array)) { + return false; + } + + // Check to see if there's an override value set for the current class/method being called + if (empty($this->overrides_array[$this->router->class][$this->router->method])) { + return false; + } + + // None auth override found, prepare nothing but send back a true override flag + if ($this->overrides_array[$this->router->class][$this->router->method] == 'none') { + return true; + } + + // Basic auth override found, prepare basic + if ($this->overrides_array[$this->router->class][$this->router->method] == 'basic') { + $this->_prepare_basic_auth(); + + return true; + } + + // Digest auth override found, prepare digest + if ($this->overrides_array[$this->router->class][$this->router->method] == 'digest') { + $this->_prepare_digest_auth(); + + return true; + } + + // Whitelist auth override found, check client's ip against config whitelist + if ($this->overrides_array[$this->router->class][$this->router->method] == 'whitelist') { + $this->_check_whitelist_auth(); + + return true; + } + + // Return false when there is an override value set but it does not match + // 'basic', 'digest', or 'none'. (the value was misspelled) + return false; + } + + /** + * Parse GET + */ + protected function _parse_get() + { + // Fix for Issue #247 + if ($this->input->is_cli_request()) { + $args = $_SERVER['argv']; + unset($args[0]); + $_SERVER['QUERY_STRING'] = $_SERVER['PATH_INFO'] = $_SERVER['REQUEST_URI'] = '/' . implode('/', $args) . '/'; + } + + // Grab proper GET variables + parse_str(parse_url(http://github.com/$_SERVER['REQUEST_URI'],%20PHP_URL_QUERY), $get); + + // Merge both the URI segments and GET params + $this->_get_args = array_merge($this->_get_args, $get); + } + + /** + * Parse POST + */ + protected function _parse_post() + { + $this->_post_args = $_POST; + + $this->request->format and $this->request->body = file_get_contents('php://input'); + } + + /** + * Parse PUT + */ + protected function _parse_put() + { + // It might be a HTTP body + if ($this->request->format) { + $this->request->body = file_get_contents('php://input'); + } + + // If no file type is provided, this is probably just arguments + else { + parse_str(file_get_contents('php://input'), $this->_put_args); + } + + } + + /** + * Parse HEAD + */ + protected function _parse_head() + { + // Grab proper HEAD variables + parse_str(parse_url(http://github.com/$_SERVER['REQUEST_URI'],%20PHP_URL_QUERY), $head); + + // Merge both the URI segments and HEAD params + $this->_head_args = array_merge($this->_head_args, $head); + } + + /** + * Parse OPTIONS + */ + protected function _parse_options() + { + // Grab proper OPTIONS variables + parse_str(parse_url(http://github.com/$_SERVER['REQUEST_URI'],%20PHP_URL_QUERY), $options); + + // Merge both the URI segments and OPTIONS params + $this->_options_args = array_merge($this->_options_args, $options); + } + + /** + * Parse PATCH + */ + protected function _parse_patch() + { + // It might be a HTTP body + if ($this->request->format) { + $this->request->body = file_get_contents('php://input'); + } + + // If no file type is provided, this is probably just arguments + else { + parse_str(file_get_contents('php://input'), $this->_patch_args); + } + } + + /** + * Parse DELETE + */ + protected function _parse_delete() + { + // Set up out DELETE variables (which shouldn't really exist, but sssh!) + parse_str(file_get_contents('php://input'), $this->_delete_args); + } + + // INPUT FUNCTION -------------------------------------------------------------- + + /** + * Retrieve a value from the GET request arguments. + * + * @param string $key The key for the GET request argument to retrieve + * @param boolean $xss_clean Whether the value should be XSS cleaned or not. + * @return string The GET argument value. + */ + public function get($key = NULL, $xss_clean = TRUE) + { + if ($key === NULL) { + return $this->_get_args; + } + + return array_key_exists($key, $this->_get_args) ? $this->_xss_clean($this->_get_args[$key], $xss_clean) : FALSE; + } + + /** + * This function retrieves a values from the OPTIONS request arguments + * + * @param string $key The OPTIONS/GET argument key + * @param boolean $xss_clean Whether the value should be XSS cleaned or not + * @return string The OPTIONS/GET argument value + */ + public function options($key = NULL, $xss_clean = TRUE) + { + if ($key === NULL) { + return $this->_options_args; + } + + return array_key_exists($key, $this->_options_args) ? $this->_xss_clean($this->_options_args[$key], $xss_clean) : FALSE; + } + + /** + * This function retrieves a values from the HEAD request arguments + * + * @param string $key The HEAD/GET argument key + * @param boolean $xss_clean Whether the value should be XSS cleaned or not + * @return string The HEAD/GET argument value + */ + public function head($key = NULL, $xss_clean = TRUE) + { + if ($key === NULL) { + return $this->head_args; + } + + return array_key_exists($key, $this->head_args) ? $this->_xss_clean($this->head_args[$key], $xss_clean) : FALSE; + } + + /** + * Retrieve a value from the POST request arguments. + * + * @param string $key The key for the POST request argument to retrieve + * @param boolean $xss_clean Whether the value should be XSS cleaned or not. + * @return string The POST argument value. + */ + public function post($key = NULL, $xss_clean = TRUE) + { + if ($key === NULL) { + return $this->_post_args; + } + + return array_key_exists($key, $this->_post_args) ? $this->_xss_clean($this->_post_args[$key], $xss_clean) : FALSE; + } + + /** + * Retrieve a value from the PUT request arguments. + * + * @param string $key The key for the PUT request argument to retrieve + * @param boolean $xss_clean Whether the value should be XSS cleaned or not. + * @return string The PUT argument value. + */ + public function put($key = NULL, $xss_clean = TRUE) + { + if ($key === NULL) { + return $this->_put_args; + } + + return array_key_exists($key, $this->_put_args) ? $this->_xss_clean($this->_put_args[$key], $xss_clean) : FALSE; + } + + /** + * Retrieve a value from the DELETE request arguments. + * + * @param string $key The key for the DELETE request argument to retrieve + * @param boolean $xss_clean Whether the value should be XSS cleaned or not. + * @return string The DELETE argument value. + */ + public function delete($key = NULL, $xss_clean = TRUE) + { + if ($key === NULL) { + return $this->_delete_args; + } + + return array_key_exists($key, $this->_delete_args) ? $this->_xss_clean($this->_delete_args[$key], $xss_clean) : FALSE; + } + + /** + * Retrieve a value from the PATCH request arguments. + * + * @param string $key The key for the PATCH request argument to retrieve + * @param boolean $xss_clean Whether the value should be XSS cleaned or not. + * @return string The PATCH argument value. + */ + public function patch($key = NULL, $xss_clean = TRUE) + { + if ($key === NULL) { + return $this->_patch_args; + } + + return array_key_exists($key, $this->_patch_args) ? $this->_xss_clean($this->_patch_args[$key], $xss_clean) : FALSE; + } + + /** + * Process to protect from XSS attacks. + * + * @param string $val The input. + * @param boolean $process Do clean or note the input. + * @return string + */ + protected function _xss_clean($val, $process) + { + if (CI_VERSION < 2) { + return $process ? $this->input->xss_clean($val) : $val; + } + + return $process ? $this->security->xss_clean($val) : $val; + } + + /** + * Retrieve the validation errors. + * + * @return array + */ + public function validation_errors() + { + $string = strip_tags($this->form_validation->error_string()); + + return explode("\n", trim($string, "\n")); + } + + // SECURITY FUNCTIONS --------------------------------------------------------- + + /** + * Perform LDAP Authentication + * + * @param string $username The username to validate + * @param string $password The password to validate + * @return boolean + */ + protected function _perform_ldap_auth($username = '', $password = NULL) + { + if (empty($username)) { + log_message('debug', 'LDAP Auth: failure, empty username'); + + return false; + } + + log_message('debug', 'LDAP Auth: Loading Config'); + + $this->config->load('ldap.php', true); + + $ldaptimeout = $this->config->item('timeout', 'ldap'); + $ldaphost = $this->config->item('server', 'ldap'); + $ldapport = $this->config->item('port', 'ldap'); + $ldaprdn = $this->config->item('binduser', 'ldap'); + $ldappass = $this->config->item('bindpw', 'ldap'); + $ldapbasedn = $this->config->item('basedn', 'ldap'); + + log_message('debug', 'LDAP Auth: Connect to ' . $ldaphost); + + $ldapconfig['authrealm'] = $this->config->item('domain', 'ldap'); + + // connect to ldap server + $ldapconn = ldap_connect($ldaphost, $ldapport); + + if ($ldapconn) { + + log_message('debug', 'Setting timeout to ' . $ldaptimeout . ' seconds'); + + ldap_set_option($ldapconn, LDAP_OPT_NETWORK_TIMEOUT, $ldaptimeout); + + log_message('debug', 'LDAP Auth: Binding to ' . $ldaphost . ' with dn ' . $ldaprdn); + + // binding to ldap server + $ldapbind = ldap_bind($ldapconn, $ldaprdn, $ldappass); + + // verify binding + if ($ldapbind) { + log_message('debug', 'LDAP Auth: bind successful'); + } else { + log_message('error', 'LDAP Auth: bind unsuccessful'); + + return false; + } + + } + + // search for user + if (($res_id = ldap_search( $ldapconn, $ldapbasedn, "uid=$username")) == false) { + log_message('error', 'LDAP Auth: User ' . $username . ' not found in search'); + + return false; + } + + if (ldap_count_entries($ldapconn, $res_id) != 1) { + log_message('error', 'LDAP Auth: failure, username ' . $username . 'found more than once'); + + return false; + } + + if (( $entry_id = ldap_first_entry($ldapconn, $res_id))== false) { + log_message('error', 'LDAP Auth: failure, entry of searchresult could not be fetched'); + + return false; + } + + if (( $user_dn = ldap_get_dn($ldapconn, $entry_id)) == false) { + log_message('error', 'LDAP Auth: failure, user-dn could not be fetched'); + + return false; + } + + // User found, could not authenticate as user + if (($link_id = ldap_bind($ldapconn, $user_dn, $password)) == false) { + log_message('error', 'LDAP Auth: failure, username/password did not match: ' . $user_dn); + + return false; + } + + log_message('debug', 'LDAP Auth: Success ' . $user_dn . ' authenticated successfully'); + + $this->_user_ldap_dn = $user_dn; + ldap_close($ldapconn); + + return true; + } + + /** + * Perform Library Authentication - Override this function to change the way the library is called + * + * @param string $username The username to validate + * @param string $password The password to validate + * @return boolean + */ + protected function _perform_library_auth($username = '', $password = NULL) + { + if (empty($username)) { + log_message('debug', 'Library Auth: failure, empty username'); + + return false; + } + + $auth_library_class = strtolower($this->config->item('auth_library_class')); + $auth_library_function = strtolower($this->config->item('auth_library_function')); + + if (empty($auth_library_class)) { + log_message('debug', 'Library Auth: failure, empty auth_library_class'); + + return false; + } + + if (empty($auth_library_function)) { + log_message('debug', 'Library Auth: failure, empty auth_library_function'); + + return false; + } + + $this->load->library($auth_library_class); + + return $this->$auth_library_class->$auth_library_function($username, $password); + } + + /** + * Check if the user is logged in. + * + * @param string $username The user's name + * @param string $password The user's password + * @return boolean + */ + protected function _check_login($username = '', $password = NULL) + { + if (empty($username)) { + return FALSE; + } + + $auth_source = strtolower($this->config->item('auth_source')); + + if ($auth_source == 'ldap') { + log_message('debug', 'performing LDAP authentication for $username'); + + return $this->_perform_ldap_auth($username, $password); + } + + if ($auth_source == 'library') { + log_message('debug', 'performing Library authentication for $username'); + + return $this->_perform_library_auth($username, $password); + } + + $valid_logins = $this->config->item('rest_valid_logins'); + + if ( ! array_key_exists($username, $valid_logins)) { + return FALSE; + } + + // If actually NULL (not empty string) then do not check it + if ($password === NULL AND $valid_logins[$username] != $password) { + return FALSE; + } + + return TRUE; + } + + /** + * Check to see if the user is logged into the web app with a php session key. + */ + protected function _check_php_session() + { + $key = $this->config->item('auth_source'); + if (!$this->session->userdata($key)) { + $this->response(array('status' => false, 'error' => 'Not Authorized'), 401); + } + } + + /** + * @todo document this. + */ + protected function _prepare_basic_auth() + { + // If whitelist is enabled it has the first chance to kick them out + if (config_item('rest_ip_whitelist_enabled')) { + $this->_check_whitelist_auth(); + } + + $username = NULL; + $password = NULL; + + // mod_php + if ($this->input->server('PHP_AUTH_USER')) { + $username = $this->input->server('PHP_AUTH_USER'); + $password = $this->input->server('PHP_AUTH_PW'); + } + + // most other servers + elseif ($this->input->server('HTTP_AUTHENTICATION')) { + if (strpos(strtolower($this->input->server('HTTP_AUTHENTICATION')), 'basic') === 0) { + list($username, $password) = explode(':', base64_decode(substr($this->input->server('HTTP_AUTHORIZATION'), 6))); + } + } + + if ( ! $this->_check_login($username, $password)) { + $this->_force_login(); + } + } + + /** + * @todo Document this. + */ + protected function _prepare_digest_auth() + { + // If whitelist is enabled it has the first chance to kick them out + if (config_item('rest_ip_whitelist_enabled')) { + $this->_check_whitelist_auth(); + } + + $uniqid = uniqid(""); // Empty argument for backward compatibility + // We need to test which server authentication variable to use + // because the PHP ISAPI module in IIS acts different from CGI + if ($this->input->server('PHP_AUTH_DIGEST')) { + $digest_string = $this->input->server('PHP_AUTH_DIGEST'); + } elseif ($this->input->server('HTTP_AUTHORIZATION')) { + $digest_string = $this->input->server('HTTP_AUTHORIZATION'); + } else { + $digest_string = ""; + } + + // The $_SESSION['error_prompted'] variable is used to ask the password + // again if none given or if the user enters wrong auth information. + if (empty($digest_string)) { + $this->_force_login($uniqid); + } + + // We need to retrieve authentication informations from the $auth_data variable + preg_match_all('@(username|nonce|uri|nc|cnonce|qop|response)=[\'"]?([^\'",]+)@', $digest_string, $matches); + $digest = (empty($matches[1]) || empty($matches[2])) ? array() : array_combine($matches[1], $matches[2]); + + if ( ! array_key_exists('username', $digest) OR !$this->_check_login($digest['username'])) { + $this->_force_login($uniqid); + } + + $valid_logins = $this->config->item('rest_valid_logins'); + $valid_pass = $valid_logins[$digest['username']]; + + // This is the valid response expected + $A1 = md5($digest['username'].':'.$this->config->item('rest_realm').':'.$valid_pass); + $A2 = md5(strtoupper($this->request->method).':'.$digest['uri']); + $valid_response = md5($A1.':'.$digest['nonce'].':'.$digest['nc'].':'.$digest['cnonce'].':'.$digest['qop'].':'.$A2); + + if ($digest['response'] != $valid_response) { + set_status_header(401); + exit; + } + } + + /** + * Check if the client's ip is in the 'rest_ip_blacklist' config + */ + protected function _check_blacklist_auth() + { + $blacklist = explode(',', config_item('rest_ip_blacklist')); + + foreach ($blacklist AS &$ip) { + $ip = trim($ip); + } + + if (in_array($this->input->ip_address(), $blacklist)) { + $this->response(array('status' => false, 'error' => 'IP Denied'), 401); + } + } + + /** + * Check if the client's ip is in the 'rest_ip_whitelist' config + */ + protected function _check_whitelist_auth() + { + $whitelist = explode(',', config_item('rest_ip_whitelist')); + + array_push($whitelist, '127.0.0.1', '0.0.0.0'); + + foreach ($whitelist AS &$ip) { + $ip = trim($ip); + } + + if ( ! in_array($this->input->ip_address(), $whitelist)) { + $this->response(array('status' => false, 'error' => 'Not authorized'), 401); + } + } + + /** + * @todo Document this. + * + * @param string $nonce + */ + protected function _force_login($nonce = '') + { + if (strtolower( $this->config->item('rest_auth') ) == 'basic') { + header('WWW-Authenticate: Basic realm="'.$this->config->item('rest_realm').'"'); + } elseif (strtolower( $this->config->item('rest_auth') ) == 'digest') { + header('WWW-Authenticate: Digest realm="'.$this->config->item('rest_realm').'", qop="auth", nonce="'.$nonce.'", opaque="'.md5($this->config->item('rest_realm')).'"'); + } + + $this->response(array('status' => false, 'error' => 'Not authorized'), 401); + } + + /** + * Force it into an array + * + * @param object|array $data + * @return array + */ + protected function _force_loopable($data) + { + // Force it to be something useful + if ( ! is_array($data) AND !is_object($data)) { + $data = (array) $data; + } + + return $data; + } + + /** + * updates the log with the access time + * + * @author Chris Kacerguis + * @return boolean + */ + + protected function _log_access_time() + { + $payload['rtime'] = $this->_end_rtime - $this->_start_rtime; + + return $this->rest->db->update(config_item('rest_logs_table'), $payload, array('id' => $this->_insert_id)); + } + + /** + * Check to see if the API key has access to the controller and methods + * + * @return boolean + */ + protected function _check_access() + { + // if we don't want to check acccess, just return TRUE + if (config_item('rest_enable_access') === FALSE) { + return TRUE; + } + + $controller = explode('/', $this->uri->uri_string()); + + $this->rest->db->select(); + $this->rest->db->where('key', $this->rest->key); + $this->rest->db->where('controller', $controller[0]); + + $query = $this->rest->db->get(config_item('rest_access_table')); + + if ($query->num_rows > 0) { + return TRUE; + } + + return FALSE; + } } From 5735369f9772c747e892270fe019e9c3bf9788d6 Mon Sep 17 00:00:00 2001 From: Chris Kacerguis Date: Thu, 22 May 2014 07:13:02 -0500 Subject: [PATCH 144/651] fixes #305 --- application/libraries/REST_Controller.php | 24 +++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 93332301..d8c2b285 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -287,18 +287,22 @@ public function __construct() } // When there is no specific override for the current class/method, use the default auth value set in the config - if ($this->auth_override !== TRUE && $this->_allow === FALSE) { - if (strtolower( $this->config->item('rest_auth') ) == 'basic') { - $this->_prepare_basic_auth(); - } elseif (strtolower( $this->config->item('rest_auth') ) == 'digest') { - $this->_prepare_digest_auth(); - } elseif (strtolower( $this->config->item('rest_auth') ) == 'session') { - $this->_check_php_session(); - } elseif ($this->config->item('rest_ip_whitelist_enabled')) { + if ($this->auth_override !== true && $this->_allow === false) { + $rest_auth = strtolower($this->config->item('rest_auth')); + switch ($rest_auth) { + case 'basic': + $this->_prepare_basic_auth(); + break; + case 'digest': + $this->_prepare_digest_auth(); + break; + case 'session': + $this->_check_php_session(); + break; + } + if ($this->config->item('rest_ip_whitelist_enabled')) { $this->_check_whitelist_auth(); } - // If we made it this far we either authenticated or were in the white list. - $this->_allow = TRUE; } } From 0323cf33da4421d630abc3e452072d1c7dde5c0c Mon Sep 17 00:00:00 2001 From: Chris Kacerguis Date: Thu, 22 May 2014 07:15:54 -0500 Subject: [PATCH 145/651] more code conversion --- application/libraries/REST_Controller.php | 162 +++++++++++----------- 1 file changed, 81 insertions(+), 81 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index d8c2b285..d911dbe8 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -22,7 +22,7 @@ abstract class REST_Controller extends CI_Controller * * @var string|null */ - protected $rest_format = NULL; + protected $rest_format = null; /** * Defines the list of method properties such as limit, log and level @@ -44,21 +44,21 @@ abstract class REST_Controller extends CI_Controller * * @var object */ - protected $request = NULL; + protected $request = null; /** * What is gonna happen in output? * * @var object */ - protected $response = NULL; + protected $response = null; /** * Stores DB, keys, key level, etc * * @var object */ - protected $rest = NULL; + protected $rest = null; /** * The arguments for the GET request method @@ -128,14 +128,14 @@ abstract class REST_Controller extends CI_Controller * * @var boolean */ - protected $_allow = TRUE; + protected $_allow = true; /** * Determines if output compression is enabled * * @var boolean */ - protected $_zlib_oc = FALSE; + protected $_zlib_oc = false; /** * The LDAP Distinguished Name of the User post authentication @@ -197,7 +197,7 @@ public function __construct() parent::__construct(); // Start the timer for how long the request takes - $this->_start_rtime = microtime(TRUE); + $this->_start_rtime = microtime(true); // init objects $this->request = new stdClass(); @@ -238,7 +238,7 @@ public function __construct() $this->request->format = $this->_detect_input_format(); // Some Methods cant have a body - $this->request->body = NULL; + $this->request->body = null; $this->{'_parse_' . $this->request->method}(); @@ -268,7 +268,7 @@ public function __construct() $this->rest = new StdClass(); // Load DB if its enabled if (config_item('rest_database_group') AND (config_item('rest_enable_keys') OR config_item('rest_enable_logging'))) { - $this->rest->db = $this->load->database(config_item('rest_database_group'), TRUE); + $this->rest->db = $this->load->database(config_item('rest_database_group'), true); } // Use whatever database is in use (isset returns false) @@ -313,7 +313,7 @@ public function __construct() public function __destruct() { // Record the "stop" time of the request - $this->_end_rtime = microtime(TRUE); + $this->_end_rtime = microtime(true); // CK: if, we are logging, log the access time here, as we are done! if (config_item('rest_enable_logging')) { $this->_log_access_time(); @@ -346,13 +346,13 @@ public function _remap($object_called, $arguments) $controller_method = $object_called.'_'.$this->request->method; // Do we want to log this method (if allowed by config)? - $log_method = !(isset($this->methods[$controller_method]['log']) AND $this->methods[$controller_method]['log'] == FALSE); + $log_method = !(isset($this->methods[$controller_method]['log']) AND $this->methods[$controller_method]['log'] == false); // Use keys for this method? - $use_key = ! (isset($this->methods[$controller_method]['key']) AND $this->methods[$controller_method]['key'] == FALSE); + $use_key = ! (isset($this->methods[$controller_method]['key']) AND $this->methods[$controller_method]['key'] == false); // They provided a key, but it wasn't valid, so get them out of here. - if (config_item('rest_enable_keys') AND $use_key AND $this->_allow === FALSE) { + if (config_item('rest_enable_keys') AND $use_key AND $this->_allow === false) { if (config_item('rest_enable_logging') AND $log_method) { $this->_log_request(); } @@ -398,7 +398,7 @@ public function _remap($object_called, $arguments) // No key stuff, but record that stuff is happening else if (config_item('rest_enable_logging') AND $log_method) { - $this->_log_request($authorized = TRUE); + $this->_log_request($authorized = true); } // And...... GO! @@ -430,25 +430,25 @@ public function response($data = null, $http_code = null) { global $CFG; - // If data is NULL and not code provide, error and bail - if ($data === NULL && $http_code === null) { + // If data is null and not code provide, error and bail + if ($data === null && $http_code === null) { $http_code = 404; // create the output variable here in the case of $this->response(array()); - $output = NULL; + $output = null; } - // If data is NULL but http code provided, keep the output empty - else if ($data === NULL && is_numeric($http_code)) { - $output = NULL; + // If data is null but http code provided, keep the output empty + else if ($data === null && is_numeric($http_code)) { + $output = null; } // Otherwise (if no data but 200 provided) or some data, carry on camping! else { // Is compression requested? - if ($CFG->item('compress_output') === TRUE && $this->_zlib_oc == FALSE) { + if ($CFG->item('compress_output') === true && $this->_zlib_oc == false) { if (extension_loaded('zlib')) { - if (isset($_SERVER['HTTP_ACCEPT_ENCODING']) AND strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE) { + if (isset($_SERVER['HTTP_ACCEPT_ENCODING']) AND strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== false) { ob_start('ob_gzhandler'); } } @@ -523,7 +523,7 @@ protected function _detect_input_format() } } - return NULL; + return null; } /** @@ -549,7 +549,7 @@ protected function _detect_output_format() // Remove the extension from arguments too $this->_get_args[$last_key] = preg_replace($pattern, '', $this->_get_args[$last_key]); - $this->_args[$last_key] = preg_replace($pattern, '', $this->_args[$last_key]); + $this->_args[$last_key] = preg_replace($pattern, '', $this->_args[$last_key]); return $matches[1]; } @@ -560,11 +560,11 @@ protected function _detect_output_format() } // Otherwise, check the HTTP_ACCEPT (if it exists and we are allowed) - if ($this->config->item('rest_ignore_http_accept') === FALSE AND $this->input->server('HTTP_ACCEPT')) { + if ($this->config->item('rest_ignore_http_accept') === false AND $this->input->server('HTTP_ACCEPT')) { // Check all formats against the HTTP_ACCEPT header foreach (array_keys($this->_supported_formats) as $format) { // Has this format been requested? - if (strpos($this->input->server('HTTP_ACCEPT'), $format) !== FALSE) { + if (strpos($this->input->server('HTTP_ACCEPT'), $format) !== false) { // If not HTML or XML assume its right and send it on its way if ($format != 'html' AND $format != 'xml') { return $format; @@ -573,12 +573,12 @@ protected function _detect_output_format() // HTML or XML have shown up as a match else { // If it is truly HTML, it wont want any XML - if ($format == 'html' AND strpos($this->input->server('HTTP_ACCEPT'), 'xml') === FALSE) { + if ($format == 'html' AND strpos($this->input->server('HTTP_ACCEPT'), 'xml') === false) { return $format; } // If it is truly XML, it wont want any HTML - elseif ($format == 'xml' AND strpos($this->input->server('HTTP_ACCEPT'), 'html') === FALSE) { + elseif ($format == 'xml' AND strpos($this->input->server('HTTP_ACCEPT'), 'html') === false) { return $format; } } @@ -636,15 +636,15 @@ protected function _detect_api_key() // Work out the name of the SERVER entry based on config $key_name = 'HTTP_'.strtoupper(str_replace('-', '_', $api_key_variable)); - $this->rest->key = NULL; - $this->rest->level = NULL; - $this->rest->user_id = NULL; - $this->rest->ignore_limits = FALSE; + $this->rest->key = null; + $this->rest->level = null; + $this->rest->user_id = null; + $this->rest->ignore_limits = false; // Find the key from server or arguments if (($key = isset($this->_args[$api_key_variable]) ? $this->_args[$api_key_variable] : $this->input->server($key_name))) { if ( ! ($row = $this->rest->db->where(config_item('rest_key_column'), $key)->get(config_item('rest_keys_table'))->row())) { - return FALSE; + return false; } $this->rest->key = $row->{config_item('rest_key_column')}; @@ -664,12 +664,12 @@ protected function _detect_api_key() if (isset($row->ip_addresses)) { // multiple ip addresses must be separated using a comma, explode and loop $list_ip_addresses = explode(",", $row->ip_addresses); - $found_address = FALSE; + $found_address = false; foreach ($list_ip_addresses as $ip_address) { if ($this->input->ip_address() == trim($ip_address)) { // there is a match, set the the value to true and break out of the loop - $found_address = TRUE; + $found_address = true; break; } } @@ -677,7 +677,7 @@ protected function _detect_api_key() return $found_address; } else { // There should be at least one IP address for this private key. - return FALSE; + return false; } } @@ -685,7 +685,7 @@ protected function _detect_api_key() } // No key has been sent - return FALSE; + return false; } /** @@ -698,11 +698,11 @@ protected function _detect_api_key() protected function _detect_lang() { if ( ! $lang = $this->input->server('HTTP_ACCEPT_LANGUAGE')) { - return NULL; + return null; } // They might have sent a few, make it an array - if (strpos($lang, ',') !== FALSE) { + if (strpos($lang, ',') !== false) { $langs = explode(',', $lang); $return_langs = array(); @@ -728,7 +728,7 @@ protected function _detect_lang() * @param boolean $authorized * @return object */ - protected function _log_request($authorized = FALSE) + protected function _log_request($authorized = false) { $status = $this->rest->db->insert(config_item('rest_logs_table'), array( 'uri' => $this->uri->uri_string(), @@ -758,7 +758,7 @@ protected function _check_limit($controller_method) // They are special, or it might not even have a limit if ( ! empty($this->rest->ignore_limits) OR !isset($this->methods[$controller_method]['limit'])) { // On your way sonny-jim. - return TRUE; + return true; } // How many times can you get to this method an hour? @@ -786,17 +786,17 @@ protected function _check_limit($controller_method) else { // Your luck is out, you've called too many times! if ($result->count >= $limit) { - return FALSE; + return false; } $this->rest->db ->where('uri', $this->uri->uri_string()) ->where('api_key', $this->rest->key) - ->set('count', 'count + 1', FALSE) + ->set('count', 'count + 1', false) ->update(config_item('rest_limits_table')); } - return TRUE; + return true; } /** @@ -958,13 +958,13 @@ protected function _parse_delete() * @param boolean $xss_clean Whether the value should be XSS cleaned or not. * @return string The GET argument value. */ - public function get($key = NULL, $xss_clean = TRUE) + public function get($key = null, $xss_clean = true) { - if ($key === NULL) { + if ($key === null) { return $this->_get_args; } - return array_key_exists($key, $this->_get_args) ? $this->_xss_clean($this->_get_args[$key], $xss_clean) : FALSE; + return array_key_exists($key, $this->_get_args) ? $this->_xss_clean($this->_get_args[$key], $xss_clean) : false; } /** @@ -974,13 +974,13 @@ public function get($key = NULL, $xss_clean = TRUE) * @param boolean $xss_clean Whether the value should be XSS cleaned or not * @return string The OPTIONS/GET argument value */ - public function options($key = NULL, $xss_clean = TRUE) + public function options($key = null, $xss_clean = true) { - if ($key === NULL) { + if ($key === null) { return $this->_options_args; } - return array_key_exists($key, $this->_options_args) ? $this->_xss_clean($this->_options_args[$key], $xss_clean) : FALSE; + return array_key_exists($key, $this->_options_args) ? $this->_xss_clean($this->_options_args[$key], $xss_clean) : false; } /** @@ -990,13 +990,13 @@ public function options($key = NULL, $xss_clean = TRUE) * @param boolean $xss_clean Whether the value should be XSS cleaned or not * @return string The HEAD/GET argument value */ - public function head($key = NULL, $xss_clean = TRUE) + public function head($key = null, $xss_clean = true) { - if ($key === NULL) { + if ($key === null) { return $this->head_args; } - return array_key_exists($key, $this->head_args) ? $this->_xss_clean($this->head_args[$key], $xss_clean) : FALSE; + return array_key_exists($key, $this->head_args) ? $this->_xss_clean($this->head_args[$key], $xss_clean) : false; } /** @@ -1006,13 +1006,13 @@ public function head($key = NULL, $xss_clean = TRUE) * @param boolean $xss_clean Whether the value should be XSS cleaned or not. * @return string The POST argument value. */ - public function post($key = NULL, $xss_clean = TRUE) + public function post($key = null, $xss_clean = true) { - if ($key === NULL) { + if ($key === null) { return $this->_post_args; } - return array_key_exists($key, $this->_post_args) ? $this->_xss_clean($this->_post_args[$key], $xss_clean) : FALSE; + return array_key_exists($key, $this->_post_args) ? $this->_xss_clean($this->_post_args[$key], $xss_clean) : false; } /** @@ -1022,13 +1022,13 @@ public function post($key = NULL, $xss_clean = TRUE) * @param boolean $xss_clean Whether the value should be XSS cleaned or not. * @return string The PUT argument value. */ - public function put($key = NULL, $xss_clean = TRUE) + public function put($key = null, $xss_clean = true) { - if ($key === NULL) { + if ($key === null) { return $this->_put_args; } - return array_key_exists($key, $this->_put_args) ? $this->_xss_clean($this->_put_args[$key], $xss_clean) : FALSE; + return array_key_exists($key, $this->_put_args) ? $this->_xss_clean($this->_put_args[$key], $xss_clean) : false; } /** @@ -1038,13 +1038,13 @@ public function put($key = NULL, $xss_clean = TRUE) * @param boolean $xss_clean Whether the value should be XSS cleaned or not. * @return string The DELETE argument value. */ - public function delete($key = NULL, $xss_clean = TRUE) + public function delete($key = null, $xss_clean = true) { - if ($key === NULL) { + if ($key === null) { return $this->_delete_args; } - return array_key_exists($key, $this->_delete_args) ? $this->_xss_clean($this->_delete_args[$key], $xss_clean) : FALSE; + return array_key_exists($key, $this->_delete_args) ? $this->_xss_clean($this->_delete_args[$key], $xss_clean) : false; } /** @@ -1054,13 +1054,13 @@ public function delete($key = NULL, $xss_clean = TRUE) * @param boolean $xss_clean Whether the value should be XSS cleaned or not. * @return string The PATCH argument value. */ - public function patch($key = NULL, $xss_clean = TRUE) + public function patch($key = null, $xss_clean = true) { - if ($key === NULL) { + if ($key === null) { return $this->_patch_args; } - return array_key_exists($key, $this->_patch_args) ? $this->_xss_clean($this->_patch_args[$key], $xss_clean) : FALSE; + return array_key_exists($key, $this->_patch_args) ? $this->_xss_clean($this->_patch_args[$key], $xss_clean) : false; } /** @@ -1100,7 +1100,7 @@ public function validation_errors() * @param string $password The password to validate * @return boolean */ - protected function _perform_ldap_auth($username = '', $password = NULL) + protected function _perform_ldap_auth($username = '', $password = null) { if (empty($username)) { log_message('debug', 'LDAP Auth: failure, empty username'); @@ -1195,7 +1195,7 @@ protected function _perform_ldap_auth($username = '', $password = NULL) * @param string $password The password to validate * @return boolean */ - protected function _perform_library_auth($username = '', $password = NULL) + protected function _perform_library_auth($username = '', $password = null) { if (empty($username)) { log_message('debug', 'Library Auth: failure, empty username'); @@ -1230,10 +1230,10 @@ protected function _perform_library_auth($username = '', $password = NULL) * @param string $password The user's password * @return boolean */ - protected function _check_login($username = '', $password = NULL) + protected function _check_login($username = '', $password = null) { if (empty($username)) { - return FALSE; + return false; } $auth_source = strtolower($this->config->item('auth_source')); @@ -1253,15 +1253,15 @@ protected function _check_login($username = '', $password = NULL) $valid_logins = $this->config->item('rest_valid_logins'); if ( ! array_key_exists($username, $valid_logins)) { - return FALSE; + return false; } - // If actually NULL (not empty string) then do not check it - if ($password === NULL AND $valid_logins[$username] != $password) { - return FALSE; + // If actually null (not empty string) then do not check it + if ($password === null AND $valid_logins[$username] != $password) { + return false; } - return TRUE; + return true; } /** @@ -1285,8 +1285,8 @@ protected function _prepare_basic_auth() $this->_check_whitelist_auth(); } - $username = NULL; - $password = NULL; + $username = null; + $password = null; // mod_php if ($this->input->server('PHP_AUTH_USER')) { @@ -1442,9 +1442,9 @@ protected function _log_access_time() */ protected function _check_access() { - // if we don't want to check acccess, just return TRUE - if (config_item('rest_enable_access') === FALSE) { - return TRUE; + // if we don't want to check acccess, just return true + if (config_item('rest_enable_access') === false) { + return true; } $controller = explode('/', $this->uri->uri_string()); @@ -1456,10 +1456,10 @@ protected function _check_access() $query = $this->rest->db->get(config_item('rest_access_table')); if ($query->num_rows > 0) { - return TRUE; + return true; } - return FALSE; + return false; } } From 5457bec09169786e40b472f95481b14dd3318ff9 Mon Sep 17 00:00:00 2001 From: Chris Kacerguis Date: Thu, 22 May 2014 07:22:01 -0500 Subject: [PATCH 146/651] code pretty-ing --- application/libraries/REST_Controller.php | 191 ++++++++++++---------- 1 file changed, 101 insertions(+), 90 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index d911dbe8..a26681f3 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -1,4 +1,4 @@ - 'application/xml', - 'json' => 'application/json', - 'jsonp' => 'application/javascript', - 'serialized' => 'application/vnd.php.serialized', - 'php' => 'text/plain', - 'html' => 'text/html', - 'csv' => 'application/csv' + protected $_supported_formats = array( + 'xml' => 'application/xml', + 'json' => 'application/json', + 'jsonp' => 'application/javascript', + 'serialized' => 'application/vnd.php.serialized', + 'php' => 'text/plain', + 'html' => 'text/html', + 'csv' => 'application/csv' ); /** @@ -199,18 +199,21 @@ public function __construct() // Start the timer for how long the request takes $this->_start_rtime = microtime(true); - // init objects - $this->request = new stdClass(); - $this->response = new stdClass(); - $this->rest = new stdClass(); - - $this->_zlib_oc = @ini_get('zlib.output_compression'); - // Lets grab the config and get ready to party $this->load->config('rest'); + // This library is bundled with REST_Controller 2.5+, but will eventually be part of CodeIgniter itself + $this->load->library('format'); + + // init objects + $this->request = new stdClass(); + $this->response = new stdClass(); + $this->rest = new stdClass(); + + $this->_zlib_oc = @ini_get('zlib.output_compression'); + // let's learn about the request - $this->request = new stdClass(); + $this->request = new stdClass(); // Check to see if this IP is Blacklisted if ($this->config->item('rest_ip_blacklist_enabled')) { @@ -218,27 +221,24 @@ public function __construct() } // Is it over SSL? - $this->request->ssl = $this->_detect_ssl(); + $this->request->ssl = $this->_detect_ssl(); // How is this request being made? POST, DELETE, GET, PUT? - $this->request->method = $this->_detect_method(); + $this->request->method = $this->_detect_method(); // Create argument container, if nonexistent - if ( ! isset($this->{'_'.$this->request->method.'_args'})) { + if (!isset($this->{'_'.$this->request->method.'_args'})) { $this->{'_'.$this->request->method.'_args'} = array(); } // Set up our GET variables - $this->_get_args = array_merge($this->_get_args, $this->uri->ruri_to_assoc()); - - // This library is bundled with REST_Controller 2.5+, but will eventually be part of CodeIgniter itself - $this->load->library('format'); + $this->_get_args = array_merge($this->_get_args, $this->uri->ruri_to_assoc()); // Try to find a format for the request (means we have a request body) - $this->request->format = $this->_detect_input_format(); + $this->request->format = $this->_detect_input_format(); // Some Methods cant have a body - $this->request->body = null; + $this->request->body = null; $this->{'_parse_' . $this->request->method}(); @@ -250,40 +250,50 @@ public function __construct() } // Merge both for one mega-args variable - $this->_args = array_merge($this->_get_args, $this->_options_args, $this->_patch_args, $this->_head_args , $this->_put_args, $this->_post_args, $this->_delete_args, $this->{'_'.$this->request->method.'_args'}); + $this->_args = array_merge($this->_get_args, + $this->_options_args, + $this->_patch_args, + $this->_head_args , + $this->_put_args, + $this->_post_args, + $this->_delete_args, + $this->{'_'.$this->request->method.'_args'} + ); // Which format should the data be returned in? - $this->response = new stdClass(); + $this->response = new stdClass(); $this->response->format = $this->_detect_output_format(); // Which format should the data be returned in? - $this->response->lang = $this->_detect_lang(); + $this->response->lang = $this->_detect_lang(); // Developers can extend this class and add a check in here $this->early_checks(); // Check if there is a specific auth type for the current class/method - $this->auth_override = $this->_auth_override_check(); + $this->auth_override = $this->_auth_override_check(); + + $this->rest = new StdClass(); - $this->rest = new StdClass(); // Load DB if its enabled - if (config_item('rest_database_group') AND (config_item('rest_enable_keys') OR config_item('rest_enable_logging'))) { - $this->rest->db = $this->load->database(config_item('rest_database_group'), true); + if (config_item('rest_database_group') and (config_item('rest_enable_keys') or config_item('rest_enable_logging'))) { + $this->rest->db = $this->load->database(config_item('rest_database_group'), true); } // Use whatever database is in use (isset returns false) elseif (property_exists($this, "db")) { - $this->rest->db = $this->db; + $this->rest->db = $this->db; } - // Checking for keys? GET TO WORK! + // Checking for keys? GET TO WorK! if (config_item('rest_enable_keys')) { $this->_allow = $this->_detect_api_key(); } // only allow ajax requests - if ( ! $this->input->is_ajax_request() AND config_item('rest_ajax_only')) { - $this->response(array('status' => false, 'error' => 'Only AJAX requests are accepted.'), 505); + if (!$this->input->is_ajax_request() and config_item('rest_ajax_only')) { + $response = array('status' => false, 'error' => 'Only AJAX requests are accepted.') + $this->response($response, 505); } // When there is no specific override for the current class/method, use the default auth value set in the config @@ -334,11 +344,12 @@ public function __destruct() public function _remap($object_called, $arguments) { // Should we answer if not over SSL? - if (config_item('force_https') AND !$this->_detect_ssl()) { + if (config_item('force_https') and !$this->_detect_ssl()) { $this->response(array('status' => false, 'error' => 'Unsupported protocol'), 403); } $pattern = '/^(.*)\.('.implode('|', array_keys($this->_supported_formats)).')$/'; + if (preg_match($pattern, $object_called, $matches)) { $object_called = $matches[1]; } @@ -346,14 +357,14 @@ public function _remap($object_called, $arguments) $controller_method = $object_called.'_'.$this->request->method; // Do we want to log this method (if allowed by config)? - $log_method = !(isset($this->methods[$controller_method]['log']) AND $this->methods[$controller_method]['log'] == false); + $log_method = !(isset($this->methods[$controller_method]['log']) and $this->methods[$controller_method]['log'] == false); // Use keys for this method? - $use_key = ! (isset($this->methods[$controller_method]['key']) AND $this->methods[$controller_method]['key'] == false); + $use_key = ! (isset($this->methods[$controller_method]['key']) and $this->methods[$controller_method]['key'] == false); // They provided a key, but it wasn't valid, so get them out of here. - if (config_item('rest_enable_keys') AND $use_key AND $this->_allow === false) { - if (config_item('rest_enable_logging') AND $log_method) { + if (config_item('rest_enable_keys') and $use_key and $this->_allow === false) { + if (config_item('rest_enable_logging') and $log_method) { $this->_log_request(); } @@ -361,8 +372,8 @@ public function _remap($object_called, $arguments) } // Check to see if this key has access to the requested controller. - if (config_item('rest_enable_keys') AND $use_key AND !empty($this->rest->key) AND !$this->_check_access()) { - if (config_item('rest_enable_logging') AND $log_method) { + if (config_item('rest_enable_keys') and $use_key and !empty($this->rest->key) and !$this->_check_access()) { + if (config_item('rest_enable_logging') and $log_method) { $this->_log_request(); } @@ -375,9 +386,9 @@ public function _remap($object_called, $arguments) } // Doing key related stuff? Can only do it if they have a key right? - if (config_item('rest_enable_keys') AND !empty($this->rest->key)) { + if (config_item('rest_enable_keys') and !empty($this->rest->key)) { // Check the limit - if (config_item('rest_enable_limits') AND !$this->_check_limit($controller_method)) { + if (config_item('rest_enable_limits') and !$this->_check_limit($controller_method)) { $this->response(array('status' => false, 'error' => 'This API key has reached the hourly limit for this method.'), 401); } @@ -388,20 +399,20 @@ public function _remap($object_called, $arguments) $authorized = $level <= $this->rest->level; // IM TELLIN! - if (config_item('rest_enable_logging') AND $log_method) { + if (config_item('rest_enable_logging') and $log_method) { $this->_log_request($authorized); } // They don't have good enough perms - $authorized OR $this->response(array('status' => false, 'error' => 'This API key does not have enough permissions.'), 401); + $authorized or $this->response(array('status' => false, 'error' => 'This API key does not have enough permissions.'), 401); } // No key stuff, but record that stuff is happening - else if (config_item('rest_enable_logging') AND $log_method) { + else if (config_item('rest_enable_logging') and $log_method) { $this->_log_request($authorized = true); } - // And...... GO! + // and...... GO! $this->_fire_method(array($this, $controller_method), $arguments); } @@ -448,13 +459,13 @@ public function response($data = null, $http_code = null) // Is compression requested? if ($CFG->item('compress_output') === true && $this->_zlib_oc == false) { if (extension_loaded('zlib')) { - if (isset($_SERVER['HTTP_ACCEPT_ENCODING']) AND strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== false) { + if (isset($_SERVER['HTTP_ACCEPT_ENCODING']) and strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== false) { ob_start('ob_gzhandler'); } } } - is_numeric($http_code) OR $http_code = 200; + is_numeric($http_code) or $http_code = 200; // @deprecated the following statement can be deleted. // If the format method exists, call and return the output in that format @@ -538,12 +549,12 @@ protected function _detect_output_format() $pattern = '/\.('.implode('|', array_keys($this->_supported_formats)).')$/'; // Check if a file extension is used when no get arguments provided - if (!$this->_get_args AND preg_match($pattern, $this->uri->uri_string(), $matches)) { + if (!$this->_get_args and preg_match($pattern, $this->uri->uri_string(), $matches)) { return $matches[1]; } // Check if a file extension is used - elseif ($this->_get_args AND !is_array(end($this->_get_args)) AND preg_match($pattern, end($this->_get_args), $matches)) { + elseif ($this->_get_args and !is_array(end($this->_get_args)) and preg_match($pattern, end($this->_get_args), $matches)) { // The key of the last argument $last_key = end(array_keys($this->_get_args)); @@ -555,30 +566,30 @@ protected function _detect_output_format() } // A format has been passed as an argument in the URL and it is supported - if (isset($this->_get_args['format']) AND array_key_exists($this->_get_args['format'], $this->_supported_formats)) { + if (isset($this->_get_args['format']) and array_key_exists($this->_get_args['format'], $this->_supported_formats)) { return $this->_get_args['format']; } // Otherwise, check the HTTP_ACCEPT (if it exists and we are allowed) - if ($this->config->item('rest_ignore_http_accept') === false AND $this->input->server('HTTP_ACCEPT')) { + if ($this->config->item('rest_ignore_http_accept') === false and $this->input->server('HTTP_ACCEPT')) { // Check all formats against the HTTP_ACCEPT header foreach (array_keys($this->_supported_formats) as $format) { // Has this format been requested? if (strpos($this->input->server('HTTP_ACCEPT'), $format) !== false) { // If not HTML or XML assume its right and send it on its way - if ($format != 'html' AND $format != 'xml') { + if ($format != 'html' and $format != 'xml') { return $format; } // HTML or XML have shown up as a match else { // If it is truly HTML, it wont want any XML - if ($format == 'html' AND strpos($this->input->server('HTTP_ACCEPT'), 'xml') === false) { + if ($format == 'html' and strpos($this->input->server('HTTP_ACCEPT'), 'xml') === false) { return $format; } // If it is truly XML, it wont want any HTML - elseif ($format == 'xml' AND strpos($this->input->server('HTTP_ACCEPT'), 'html') === false) { + elseif ($format == 'xml' and strpos($this->input->server('HTTP_ACCEPT'), 'html') === false) { return $format; } } @@ -649,9 +660,9 @@ protected function _detect_api_key() $this->rest->key = $row->{config_item('rest_key_column')}; - isset($row->user_id) AND $this->rest->user_id = $row->user_id; - isset($row->level) AND $this->rest->level = $row->level; - isset($row->ignore_limits) AND $this->rest->ignore_limits = $row->ignore_limits; + isset($row->user_id) and $this->rest->user_id = $row->user_id; + isset($row->level) and $this->rest->level = $row->level; + isset($row->ignore_limits) and $this->rest->ignore_limits = $row->ignore_limits; $this->_apiuser = $row; @@ -756,7 +767,7 @@ protected function _log_request($authorized = false) protected function _check_limit($controller_method) { // They are special, or it might not even have a limit - if ( ! empty($this->rest->ignore_limits) OR !isset($this->methods[$controller_method]['limit'])) { + if ( ! empty($this->rest->ignore_limits) or !isset($this->methods[$controller_method]['limit'])) { // On your way sonny-jim. return true; } @@ -772,7 +783,7 @@ protected function _check_limit($controller_method) ->row(); // No calls yet, or been an hour since they called - if ( ! $result OR $result->hour_started < time() - (60 * 60)) { + if ( ! $result or $result->hour_started < time() - (60 * 60)) { // Right, set one up from scratch $this->rest->db->insert(config_item('rest_limits_table'), array( 'uri' => $this->uri->uri_string(), @@ -1130,7 +1141,7 @@ protected function _perform_ldap_auth($username = '', $password = null) log_message('debug', 'Setting timeout to ' . $ldaptimeout . ' seconds'); - ldap_set_option($ldapconn, LDAP_OPT_NETWORK_TIMEOUT, $ldaptimeout); + ldap_set_option($ldapconn, LDAP_OPT_NETWorK_TIMEOUT, $ldaptimeout); log_message('debug', 'LDAP Auth: Binding to ' . $ldaphost . ' with dn ' . $ldaprdn); @@ -1257,7 +1268,7 @@ protected function _check_login($username = '', $password = null) } // If actually null (not empty string) then do not check it - if ($password === null AND $valid_logins[$username] != $password) { + if ($password === null and $valid_logins[$username] != $password) { return false; } @@ -1297,7 +1308,7 @@ protected function _prepare_basic_auth() // most other servers elseif ($this->input->server('HTTP_AUTHENTICATION')) { if (strpos(strtolower($this->input->server('HTTP_AUTHENTICATION')), 'basic') === 0) { - list($username, $password) = explode(':', base64_decode(substr($this->input->server('HTTP_AUTHORIZATION'), 6))); + list($username, $password) = explode(':', base64_decode(substr($this->input->server('HTTP_AUTHorIZATION'), 6))); } } @@ -1321,8 +1332,8 @@ protected function _prepare_digest_auth() // because the PHP ISAPI module in IIS acts different from CGI if ($this->input->server('PHP_AUTH_DIGEST')) { $digest_string = $this->input->server('PHP_AUTH_DIGEST'); - } elseif ($this->input->server('HTTP_AUTHORIZATION')) { - $digest_string = $this->input->server('HTTP_AUTHORIZATION'); + } elseif ($this->input->server('HTTP_AUTHorIZATION')) { + $digest_string = $this->input->server('HTTP_AUTHorIZATION'); } else { $digest_string = ""; } @@ -1337,7 +1348,7 @@ protected function _prepare_digest_auth() preg_match_all('@(username|nonce|uri|nc|cnonce|qop|response)=[\'"]?([^\'",]+)@', $digest_string, $matches); $digest = (empty($matches[1]) || empty($matches[2])) ? array() : array_combine($matches[1], $matches[2]); - if ( ! array_key_exists('username', $digest) OR !$this->_check_login($digest['username'])) { + if ( ! array_key_exists('username', $digest) or !$this->_check_login($digest['username'])) { $this->_force_login($uniqid); } @@ -1414,7 +1425,7 @@ protected function _force_login($nonce = '') protected function _force_loopable($data) { // Force it to be something useful - if ( ! is_array($data) AND !is_object($data)) { + if ( ! is_array($data) and !is_object($data)) { $data = (array) $data; } From 34b3fdc3fd19fd22c84e6951df931ea48ebbc513 Mon Sep 17 00:00:00 2001 From: Chris Kacerguis Date: Thu, 22 May 2014 07:23:15 -0500 Subject: [PATCH 147/651] set correct response code --- application/libraries/REST_Controller.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index a26681f3..2dfbc7eb 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -293,7 +293,7 @@ public function __construct() // only allow ajax requests if (!$this->input->is_ajax_request() and config_item('rest_ajax_only')) { $response = array('status' => false, 'error' => 'Only AJAX requests are accepted.') - $this->response($response, 505); + $this->response($response, 406); // Set status to 406 NOT ACCEPTABLE } // When there is no specific override for the current class/method, use the default auth value set in the config @@ -349,7 +349,7 @@ public function _remap($object_called, $arguments) } $pattern = '/^(.*)\.('.implode('|', array_keys($this->_supported_formats)).')$/'; - + if (preg_match($pattern, $object_called, $matches)) { $object_called = $matches[1]; } From 1d5bb6b8e78cf633e2fe5afbd9780206106f4201 Mon Sep 17 00:00:00 2001 From: Chris Kacerguis Date: Thu, 22 May 2014 07:26:27 -0500 Subject: [PATCH 148/651] code cleanup --- application/libraries/REST_Controller.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 2dfbc7eb..0c97dec4 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -360,7 +360,7 @@ public function _remap($object_called, $arguments) $log_method = !(isset($this->methods[$controller_method]['log']) and $this->methods[$controller_method]['log'] == false); // Use keys for this method? - $use_key = ! (isset($this->methods[$controller_method]['key']) and $this->methods[$controller_method]['key'] == false); + $use_key = !(isset($this->methods[$controller_method]['key']) and $this->methods[$controller_method]['key'] == false); // They provided a key, but it wasn't valid, so get them out of here. if (config_item('rest_enable_keys') and $use_key and $this->_allow === false) { @@ -389,7 +389,8 @@ public function _remap($object_called, $arguments) if (config_item('rest_enable_keys') and !empty($this->rest->key)) { // Check the limit if (config_item('rest_enable_limits') and !$this->_check_limit($controller_method)) { - $this->response(array('status' => false, 'error' => 'This API key has reached the hourly limit for this method.'), 401); + $response = array('status' => false, 'error' => 'This API key has reached the hourly limit for this method.'); + $this->response($response, 401); } // If no level is set use 0, they probably aren't using permissions @@ -404,7 +405,8 @@ public function _remap($object_called, $arguments) } // They don't have good enough perms - $authorized or $this->response(array('status' => false, 'error' => 'This API key does not have enough permissions.'), 401); + $response = array('status' => false, 'error' => 'This API key does not have enough permissions.'); + $authorized or $this->response($response, 401); } // No key stuff, but record that stuff is happening From c84c79ebfa4e22c43dd6bffa1fa0d0b74d27cae1 Mon Sep 17 00:00:00 2001 From: Rajan Balana Date: Fri, 23 May 2014 01:19:27 +0530 Subject: [PATCH 149/651] Added semicolon on line 296 Added semicolon on line 296 --- application/libraries/REST_Controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 0c97dec4..e75bd80f 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -292,7 +292,7 @@ public function __construct() // only allow ajax requests if (!$this->input->is_ajax_request() and config_item('rest_ajax_only')) { - $response = array('status' => false, 'error' => 'Only AJAX requests are accepted.') + $response = array('status' => false, 'error' => 'Only AJAX requests are accepted.'); $this->response($response, 406); // Set status to 406 NOT ACCEPTABLE } From c5d1b3fd4df7b0bb050637a8424e1cceea524350 Mon Sep 17 00:00:00 2001 From: Phil Douglas Date: Fri, 23 May 2014 10:48:43 +1000 Subject: [PATCH 150/651] added continue option to response for post return processes --- application/libraries/REST_Controller.php | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index e75bd80f..c1cee5ba 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -435,11 +435,13 @@ protected function _fire_method($method, $args) * Response * * Takes pure data and optionally a status code, then creates the response. + * Set $continue to true to flush the response to the client and continue running the script. * * @param array $data * @param null|int $http_code + * @param bool $continue */ - public function response($data = null, $http_code = null) + public function response($data = null, $http_code = null, $continue = false) { global $CFG; @@ -502,7 +504,14 @@ public function response($data = null, $http_code = null) header('Content-Length: ' . strlen($output)); } - exit($output); + if($continue){ + echo($output); + ob_end_flush(); + ob_flush(); + flush(); + }else{ + exit($output); + } } /* From e510522d04ea938f966e959196df9547b9566653 Mon Sep 17 00:00:00 2001 From: Allen Taylor Date: Thu, 29 May 2014 17:08:57 -0500 Subject: [PATCH 151/651] Rebased and added wildcard feature. This allows authentication rules to be defined at the controller level as well as the method level. --- application/config/rest.php | 9 ++- .../unit_tests/wildcard_test_cases.php | 37 ++++++++++ .../unit_tests/wildcard_test_harness.php | 67 +++++++++++++++++++ application/libraries/REST_Controller.php | 30 +++++++++ 4 files changed, 142 insertions(+), 1 deletion(-) create mode 100644 application/controllers/unit_tests/wildcard_test_cases.php create mode 100644 application/controllers/unit_tests/wildcard_test_harness.php diff --git a/application/config/rest.php b/application/config/rest.php index 45d83900..ba2a1c29 100644 --- a/application/config/rest.php +++ b/application/config/rest.php @@ -73,6 +73,8 @@ | If 'rest_auth' is 'session' then set 'auth_source' to the name of the session variable to check for. | */ + +//change this to '' for wildcard unit test $config['auth_source'] = 'ldap'; /* @@ -103,15 +105,20 @@ | $config['auth_override_class_method']['deals']['view'] = 'none'; | $config['auth_override_class_method']['deals']['insert'] = 'digest'; | $config['auth_override_class_method']['accounts']['user'] = 'basic'; +| $config['auth_override_class_method']['dashboard']['*'] = 'none|digest|basic'; | -| Here 'deals' and 'accounts' are controller names, 'view', 'insert' and 'user' are methods within. (NOTE: leave off the '_get' or '_post' from the end of the method name) +| Here 'deals', 'accounts' and 'dashboard' are controller names, 'view', 'insert' and 'user' are methods within. An asterisk may also be used to specify an authentication method for an entire classes methods. Ex: $config['auth_override_class_method']['dashboard']['*'] = 'basic'; (NOTE: leave off the '_get' or '_post' from the end of the method name) | Acceptable values are; 'none', 'digest' and 'basic'. | */ // $config['auth_override_class_method']['deals']['view'] = 'none'; // $config['auth_override_class_method']['deals']['insert'] = 'digest'; // $config['auth_override_class_method']['accounts']['user'] = 'basic'; +// $config['auth_override_class_method']['dashboard']['*'] = 'basic'; + +//---Uncomment list line for the wildard unit test +//$config['auth_override_class_method']['wildcard_test_cases']['*'] = 'basic'; /* |-------------------------------------------------------------------------- | REST Login usernames diff --git a/application/controllers/unit_tests/wildcard_test_cases.php b/application/controllers/unit_tests/wildcard_test_cases.php new file mode 100644 index 00000000..86a6497c --- /dev/null +++ b/application/controllers/unit_tests/wildcard_test_cases.php @@ -0,0 +1,37 @@ +config->load('rest'); + $this->config->set_item('rest_auth', 'none');//turn on rest auth + $this->config->set_item('auth_source', '');//use config array for authentication + $this->config->set_item('auth_override_class_method', array('wildcard_test_cases' => array('*' => 'basic'))); + $this->load->helper('url'); + } + + + function digest_get(){ + $this->response("welcome", 200); + } +} +?> \ No newline at end of file diff --git a/application/controllers/unit_tests/wildcard_test_harness.php b/application/controllers/unit_tests/wildcard_test_harness.php new file mode 100644 index 00000000..d21dd578 --- /dev/null +++ b/application/controllers/unit_tests/wildcard_test_harness.php @@ -0,0 +1,67 @@ +load->library('unit_test'); + $this->load->helper('url'); + } + + //curl interface functions + private function makeRequest($url, $cred = '', $curlopts = array()){ + $ch = curl_init($url); + $items = array( + CURLOPT_URL => $url, + CURLOPT_USERPWD => $cred + ); + foreach($curlopts as $opt => $value) + $items[$opt] = $value; + curl_setopt_array($ch, $items); + ob_start(); + $response = curl_exec($ch); + $contents = ob_get_contents(); + ob_end_clean(); + $info = curl_getinfo($ch); + + $errno = curl_errno($ch); + $error = curl_error($ch); + curl_close($ch); + return array('response' => $response, 'contents' => $contents, 'errno' => $errno, 'error' => $error, 'info' => $info);//return + } + + /* + These two test cases will test if the authentication is working for the wildcard method. The curl requests may not work if you do not have an .htaccess file with mod rewrite in the same directory as your index.php file. If you don't have that file you can add it or change the url below to the one that includes index.php. + */ + function index(){ + + //not authorized + //no htaccess: $test = $this->makeRequest(base_url() . 'index.php/unit_tests/wildcard_test_cases/digest', ''); + $test = $this->makeRequest(base_url() . 'unit_tests/wildcard_test_cases/digest', ''); + // print_r($test); + $this->unit->run($test['info']['http_code'], '401', 'Not Authorized test (No credentials provided)'); + //no htaccess: $test = $this->makeRequest(base_url() . 'index.php/unit_tests/wildcard_test_cases/digest', 'admin:1234'); + $test = $this->makeRequest(base_url() . 'unit_tests/wildcard_test_cases/digest', 'admin:1234'); + //print_r($test); + $this->unit->run($test['info']['http_code'], '200', 'Authorized, credentials given'); + echo $this->unit->report(); + } +} +?> \ No newline at end of file diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index c1cee5ba..a9ca9c8b 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -840,6 +840,36 @@ protected function _auth_override_check() return false; } + // check for wildcard flag for rules for classes + if(!empty($this->overrides_array[$this->router->class]['*'])){//check for class overides + // None auth override found, prepare nothing but send back a true override flag + if ($this->overrides_array[$this->router->class]['*'] == 'none') + { + return true; + } + + // Basic auth override found, prepare basic + if ($this->overrides_array[$this->router->class]['*'] == 'basic') + { + $this->_prepare_basic_auth(); + return true; + } + + // Digest auth override found, prepare digest + if ($this->overrides_array[$this->router->class]['*'] == 'digest') + { + $this->_prepare_digest_auth(); + return true; + } + + // Whitelist auth override found, check client's ip against config whitelist + if ($this->overrides_array[$this->router->class]['*'] == 'whitelist') + { + $this->_check_whitelist_auth(); + return true; + } + } + // Check to see if there's an override value set for the current class/method being called if (empty($this->overrides_array[$this->router->class][$this->router->method])) { return false; From 95dd903e587c946266eb1c2e9cc595be7d524261 Mon Sep 17 00:00:00 2001 From: c4xp Date: Mon, 2 Jun 2014 12:53:48 +0300 Subject: [PATCH 152/651] Added response field names and small fixes --- application/config/rest.php | 24 ++++++++++++++ application/libraries/REST_Controller.php | 40 ++++++++++++----------- 2 files changed, 45 insertions(+), 19 deletions(-) diff --git a/application/config/rest.php b/application/config/rest.php index 45d83900..e606202b 100644 --- a/application/config/rest.php +++ b/application/config/rest.php @@ -24,6 +24,30 @@ */ $config['rest_default_format'] = 'xml'; +/* +|-------------------------------------------------------------------------- +| REST Status field name +|-------------------------------------------------------------------------- +| +| The field name for the status of the response +| +| 'status' +| +*/ +$config['rest_status_field_name'] = 'status'; + +/* +|-------------------------------------------------------------------------- +| REST message field name +|-------------------------------------------------------------------------- +| +| The field name for the message inside the response +| +| 'error' +| +*/ +$config['rest_message_field_name'] = 'error'; + /* |-------------------------------------------------------------------------- | Enable emulate request diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index c1cee5ba..fe0f1a9d 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -270,9 +270,6 @@ public function __construct() // Developers can extend this class and add a check in here $this->early_checks(); - // Check if there is a specific auth type for the current class/method - $this->auth_override = $this->_auth_override_check(); - $this->rest = new StdClass(); // Load DB if its enabled @@ -285,6 +282,10 @@ public function __construct() $this->rest->db = $this->db; } + // Check if there is a specific auth type for the current class/method + // _auth_override_check could exit so we need $this->rest->db initialized before + $this->auth_override = $this->_auth_override_check(); + // Checking for keys? GET TO WorK! if (config_item('rest_enable_keys')) { $this->_allow = $this->_detect_api_key(); @@ -292,7 +293,7 @@ public function __construct() // only allow ajax requests if (!$this->input->is_ajax_request() and config_item('rest_ajax_only')) { - $response = array('status' => false, 'error' => 'Only AJAX requests are accepted.'); + $response = array(config_item('rest_status_field_name') => false, config_item('rest_message_field_name') => 'Only AJAX requests are accepted.'); $this->response($response, 406); // Set status to 406 NOT ACCEPTABLE } @@ -345,11 +346,11 @@ public function _remap($object_called, $arguments) { // Should we answer if not over SSL? if (config_item('force_https') and !$this->_detect_ssl()) { - $this->response(array('status' => false, 'error' => 'Unsupported protocol'), 403); + $this->response(array(config_item('rest_status_field_name') => false, config_item('rest_message_field_name') => 'Unsupported protocol'), 403); } $pattern = '/^(.*)\.('.implode('|', array_keys($this->_supported_formats)).')$/'; - + $matches = array(); if (preg_match($pattern, $object_called, $matches)) { $object_called = $matches[1]; } @@ -367,8 +368,8 @@ public function _remap($object_called, $arguments) if (config_item('rest_enable_logging') and $log_method) { $this->_log_request(); } - - $this->response(array('status' => false, 'error' => 'Invalid API Key.'), 403); + + $this->response(array(config_item('rest_status_field_name') => false, config_item('rest_message_field_name') => 'Invalid API Key '.$this->rest->key), 403); } // Check to see if this key has access to the requested controller. @@ -377,19 +378,19 @@ public function _remap($object_called, $arguments) $this->_log_request(); } - $this->response(array('status' => false, 'error' => 'This API key does not have access to the requested controller.'), 401); + $this->response(array(config_item('rest_status_field_name') => false, config_item('rest_message_field_name') => 'This API key does not have access to the requested controller.'), 401); } // Sure it exists, but can they do anything with it? if ( ! method_exists($this, $controller_method)) { - $this->response(array('status' => false, 'error' => 'Unknown method.'), 404); + $this->response(array(config_item('rest_status_field_name') => false, config_item('rest_message_field_name') => 'Unknown method.'), 404); } // Doing key related stuff? Can only do it if they have a key right? if (config_item('rest_enable_keys') and !empty($this->rest->key)) { // Check the limit if (config_item('rest_enable_limits') and !$this->_check_limit($controller_method)) { - $response = array('status' => false, 'error' => 'This API key has reached the hourly limit for this method.'); + $response = array(config_item('rest_status_field_name') => false, config_item('rest_message_field_name') => 'This API key has reached the hourly limit for this method.'); $this->response($response, 401); } @@ -405,7 +406,7 @@ public function _remap($object_called, $arguments) } // They don't have good enough perms - $response = array('status' => false, 'error' => 'This API key does not have enough permissions.'); + $response = array(config_item('rest_status_field_name') => false, config_item('rest_message_field_name') => 'This API key does not have enough permissions.'); $authorized or $this->response($response, 401); } @@ -560,6 +561,7 @@ protected function _detect_output_format() $pattern = '/\.('.implode('|', array_keys($this->_supported_formats)).')$/'; // Check if a file extension is used when no get arguments provided + $matches = array(); if (!$this->_get_args and preg_match($pattern, $this->uri->uri_string(), $matches)) { return $matches[1]; } @@ -728,7 +730,6 @@ protected function _detect_lang() $langs = explode(',', $lang); $return_langs = array(); - $i = 1; foreach ($langs as $lang) { // Remove weight and strip space list($lang) = explode(';', $lang); @@ -1267,7 +1268,7 @@ protected function _check_login($username = '', $password = null) } if ($auth_source == 'library') { - log_message('debug', 'performing Library authentication for $username'); + log_message('debug', 'performing Library authentication for '.$username); return $this->_perform_library_auth($username, $password); } @@ -1319,7 +1320,7 @@ protected function _prepare_basic_auth() // most other servers elseif ($this->input->server('HTTP_AUTHENTICATION')) { if (strpos(strtolower($this->input->server('HTTP_AUTHENTICATION')), 'basic') === 0) { - list($username, $password) = explode(':', base64_decode(substr($this->input->server('HTTP_AUTHorIZATION'), 6))); + list($username, $password) = explode(':', base64_decode(substr($this->input->server('HTTP_AUTHORIZATION'), 6))); } } @@ -1343,8 +1344,8 @@ protected function _prepare_digest_auth() // because the PHP ISAPI module in IIS acts different from CGI if ($this->input->server('PHP_AUTH_DIGEST')) { $digest_string = $this->input->server('PHP_AUTH_DIGEST'); - } elseif ($this->input->server('HTTP_AUTHorIZATION')) { - $digest_string = $this->input->server('HTTP_AUTHorIZATION'); + } elseif ($this->input->server('HTTP_AUTHORIZATION')) { + $digest_string = $this->input->server('HTTP_AUTHORIZATION'); } else { $digest_string = ""; } @@ -1356,6 +1357,7 @@ protected function _prepare_digest_auth() } // We need to retrieve authentication informations from the $auth_data variable + $matches = array(); preg_match_all('@(username|nonce|uri|nc|cnonce|qop|response)=[\'"]?([^\'",]+)@', $digest_string, $matches); $digest = (empty($matches[1]) || empty($matches[2])) ? array() : array_combine($matches[1], $matches[2]); @@ -1407,7 +1409,7 @@ protected function _check_whitelist_auth() } if ( ! in_array($this->input->ip_address(), $whitelist)) { - $this->response(array('status' => false, 'error' => 'Not authorized'), 401); + $this->response(array(config_item('rest_status_field_name') => false, config_item('rest_message_field_name') => 'IP not authorized'), 401); } } @@ -1424,7 +1426,7 @@ protected function _force_login($nonce = '') header('WWW-Authenticate: Digest realm="'.$this->config->item('rest_realm').'", qop="auth", nonce="'.$nonce.'", opaque="'.md5($this->config->item('rest_realm')).'"'); } - $this->response(array('status' => false, 'error' => 'Not authorized'), 401); + $this->response(array(config_item('rest_status_field_name') => false, config_item('rest_message_field_name') => 'Not authorized'), 401); } /** From 959c9be480d5f5baa3ead9682f672987f73aea58 Mon Sep 17 00:00:00 2001 From: 2 <2> Date: Wed, 4 Jun 2014 17:55:26 -0300 Subject: [PATCH 153/651] Check to include calls to models --- application/libraries/REST_Controller.php | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index c1cee5ba..6631e119 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -250,13 +250,13 @@ public function __construct() } // Merge both for one mega-args variable - $this->_args = array_merge($this->_get_args, - $this->_options_args, - $this->_patch_args, - $this->_head_args , - $this->_put_args, - $this->_post_args, - $this->_delete_args, + $this->_args = array_merge($this->_get_args, + $this->_options_args, + $this->_patch_args, + $this->_head_args , + $this->_put_args, + $this->_post_args, + $this->_delete_args, $this->{'_'.$this->request->method.'_args'} ); @@ -508,7 +508,7 @@ public function response($data = null, $http_code = null, $continue = false) echo($output); ob_end_flush(); ob_flush(); - flush(); + flush(); }else{ exit($output); } @@ -1240,7 +1240,11 @@ protected function _perform_library_auth($username = '', $password = null) return false; } - $this->load->library($auth_library_class); + if (!is_callable(array($this->$auth_library_class, $auth_library_function))) { + + $this->load->library($auth_library_class); + + } return $this->$auth_library_class->$auth_library_function($username, $password); } From 403f5cc576609edb99fed2ade4e7ccab5bec0981 Mon Sep 17 00:00:00 2001 From: Krzysztof Tomasz Zembrowski Date: Tue, 10 Jun 2014 00:12:40 +0200 Subject: [PATCH 154/651] Charset in Content-Type header --- application/libraries/REST_Controller.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index fe0f1a9d..eb2bb6fe 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -476,7 +476,7 @@ public function response($data = null, $http_code = null, $continue = false) // If the format method exists, call and return the output in that format if (method_exists($this, '_format_'.$this->response->format)) { // Set the correct format header - header('Content-Type: '.$this->_supported_formats[$this->response->format]); + header('Content-Type: '.$this->_supported_formats[$this->response->format] . '; charset=' . strtolower($this->config->item('charset'))); $output = $this->{'_format_'.$this->response->format}($data); } @@ -484,7 +484,7 @@ public function response($data = null, $http_code = null, $continue = false) // If the format method exists, call and return the output in that format elseif (method_exists($this->format, 'to_'.$this->response->format)) { // Set the correct format header - header('Content-Type: '.$this->_supported_formats[$this->response->format]); + header('Content-Type: '.$this->_supported_formats[$this->response->format] . '; charset=' . strtolower($this->config->item('charset'))); $output = $this->format->factory($data)->{'to_'.$this->response->format}(); } From 6f39f2644709fa92af9bfb1aa498fe4a42077501 Mon Sep 17 00:00:00 2001 From: c4xp Date: Tue, 10 Jun 2014 11:05:04 +0300 Subject: [PATCH 155/651] Fix for issue #283 --- application/config/rest.php | 3 +++ application/libraries/REST_Controller.php | 27 ++++++++++++++++++----- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/application/config/rest.php b/application/config/rest.php index e606202b..b4803193 100644 --- a/application/config/rest.php +++ b/application/config/rest.php @@ -109,6 +109,9 @@ | The function should accept two parameters: class->function($username, $password) | In other cases override the function _perform_library_auth in your controller | +| For digest authentication the library function should return already stored md5(username:restrealm:password) for that username +| E.g: md5('admin:REST API:1234') = '1e957ebc35631ab22d5bd6526bd14ea2' +| */ $config['auth_library_class'] = ''; $config['auth_library_function'] = ''; diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index fe0f1a9d..58dd01e5 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -805,6 +805,17 @@ protected function _check_limit($controller_method) )); } + // Been an hour since they called + else if ($result->hour_started < time() - (60 * 60)) { + // Reset the started period + $this->rest->db + ->where('uri', $this->uri->uri_string()) + ->where('api_key_id', $this->rest->key_id) + ->set('hour_started', time()) + ->set('count', 1) + ->update(config_item('rest_limits_table')); + } + // They have called within the hour, so lets update else { // Your luck is out, you've called too many times! @@ -1361,20 +1372,24 @@ protected function _prepare_digest_auth() preg_match_all('@(username|nonce|uri|nc|cnonce|qop|response)=[\'"]?([^\'",]+)@', $digest_string, $matches); $digest = (empty($matches[1]) || empty($matches[2])) ? array() : array_combine($matches[1], $matches[2]); - if ( ! array_key_exists('username', $digest) or !$this->_check_login($digest['username'])) { + // For digest authentication the library function should return already stored md5(username:restrealm:password) for that username @see rest.php::auth_library_function config + if ( ! array_key_exists('username', $digest) or ! ($A1 = $this->_check_login($digest['username'])) ) { $this->_force_login($uniqid); } - $valid_logins = $this->config->item('rest_valid_logins'); - $valid_pass = $valid_logins[$digest['username']]; + // If the auth_source is not selected use the rest.php configuration valid logins + if ( ! $this->config->item('auth_source') ) { + $valid_logins = $this->config->item('rest_valid_logins'); + $valid_pass = $valid_logins[$digest['username']]; - // This is the valid response expected - $A1 = md5($digest['username'].':'.$this->config->item('rest_realm').':'.$valid_pass); + // This is the valid response expected + $A1 = md5($digest['username'].':'.$this->config->item('rest_realm').':'.$valid_pass); + } $A2 = md5(strtoupper($this->request->method).':'.$digest['uri']); $valid_response = md5($A1.':'.$digest['nonce'].':'.$digest['nc'].':'.$digest['cnonce'].':'.$digest['qop'].':'.$A2); if ($digest['response'] != $valid_response) { - set_status_header(401); + $this->response(array(config_item('rest_status_field_name') => 0, config_item('rest_message_field_name') => 'Invalid credentials'), 401); exit; } } From 93dc5fbb3b4c0b0b6562b820fe75025915e63357 Mon Sep 17 00:00:00 2001 From: hprakash Date: Wed, 11 Jun 2014 12:07:42 +0530 Subject: [PATCH 156/651] auth_override_class_method bug fix Fixed: Those methods set to 'none' in auth_override_class_method, still checked for rest api key. --- application/libraries/REST_Controller.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index eb2bb6fe..03975b66 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -285,9 +285,10 @@ public function __construct() // Check if there is a specific auth type for the current class/method // _auth_override_check could exit so we need $this->rest->db initialized before $this->auth_override = $this->_auth_override_check(); - + // Checking for keys? GET TO WorK! - if (config_item('rest_enable_keys')) { + // Skip keys test for $config['auth_override_class_method']['class'['method'] = 'none' + if (config_item('rest_enable_keys') and $this->auth_override !== true) { $this->_allow = $this->_detect_api_key(); } From 4c1213ad91f64c162c9666c8b1dab62afa77a288 Mon Sep 17 00:00:00 2001 From: Chris Kacerguis Date: Sun, 22 Jun 2014 14:40:35 -0500 Subject: [PATCH 157/651] closes #323 --- application/libraries/Format.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/application/libraries/Format.php b/application/libraries/Format.php index 8d0d437d..9cec802c 100644 --- a/application/libraries/Format.php +++ b/application/libraries/Format.php @@ -227,8 +227,18 @@ public function to_json() $callback = isset($_GET['callback']) ? $_GET['callback'] : ''; if ($callback === '') { - return json_encode($this->_data); + $str = $this->_data; + array_walk_recursive($str, function(&$item, $key) + { + if(!mb_detect_encoding($item, 'utf-8', true)) + { + $item = utf8_encode($item); + } + }); + + return json_encode($str); } + // we only honour jsonp callback which are valid javascript identifiers else if (preg_match('/^[a-z_\$][a-z0-9\$_]*(\.[a-z_\$][a-z0-9\$_]*)*$/i', $callback)) { From 1ce33e0036823abadcaba7a386adf6e7182d2cdd Mon Sep 17 00:00:00 2001 From: Chris Kacerguis Date: Tue, 24 Jun 2014 08:05:59 -0500 Subject: [PATCH 158/651] fixes 326 --- application/libraries/REST_Controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index caa2ed4d..13602238 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -299,7 +299,7 @@ public function __construct() } // When there is no specific override for the current class/method, use the default auth value set in the config - if ($this->auth_override !== true && $this->_allow === false) { + if ($this->auth_override !== true && !(config_item('rest_enable_keys') && $this->_allow === true)) { $rest_auth = strtolower($this->config->item('rest_auth')); switch ($rest_auth) { case 'basic': From e5c781ab35a12ad634469012efe222185f22e5a5 Mon Sep 17 00:00:00 2001 From: Chris Kacerguis Date: Tue, 1 Jul 2014 17:19:46 -0500 Subject: [PATCH 159/651] fixed constant. --- application/libraries/REST_Controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 13602238..5cc4d396 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -1154,7 +1154,7 @@ protected function _perform_ldap_auth($username = '', $password = null) log_message('debug', 'Setting timeout to ' . $ldaptimeout . ' seconds'); - ldap_set_option($ldapconn, LDAP_OPT_NETWorK_TIMEOUT, $ldaptimeout); + ldap_set_option($ldapconn, LDAP_OPT_NETWORK_TIMEOUT, $ldaptimeout); log_message('debug', 'LDAP Auth: Binding to ' . $ldaphost . ' with dn ' . $ldaprdn); From 0e16eb6a684537f7c3b58ea902db5bec7591d3e5 Mon Sep 17 00:00:00 2001 From: Chris Kacerguis Date: Fri, 4 Jul 2014 19:26:02 -0500 Subject: [PATCH 160/651] =?UTF-8?q?Undo=20the=20changes=20from=20#323=20(I?= =?UTF-8?q?t=20didn=E2=80=99t=20work=20on=20Objects,=20only=20Arrays)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- application/libraries/Format.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/application/libraries/Format.php b/application/libraries/Format.php index 9cec802c..5cd812d4 100644 --- a/application/libraries/Format.php +++ b/application/libraries/Format.php @@ -227,6 +227,9 @@ public function to_json() $callback = isset($_GET['callback']) ? $_GET['callback'] : ''; if ($callback === '') { + return json_encode($this->_data); + + /* Had to take out this code, it doesn't work on Objects. $str = $this->_data; array_walk_recursive($str, function(&$item, $key) { @@ -237,6 +240,7 @@ public function to_json() }); return json_encode($str); + */ } // we only honour jsonp callback which are valid javascript identifiers From 1476160848ab6c4e02c6794a40bc49482361d693 Mon Sep 17 00:00:00 2001 From: Abhishek Dilliwal Date: Sat, 26 Jul 2014 21:26:06 +0200 Subject: [PATCH 161/651] $this->request being already assigned after loadeing zlib.output_compression we are already assigning this var. --- application/libraries/REST_Controller.php | 1 - 1 file changed, 1 deletion(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 5cc4d396..34d349ba 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -206,7 +206,6 @@ public function __construct() $this->load->library('format'); // init objects - $this->request = new stdClass(); $this->response = new stdClass(); $this->rest = new stdClass(); From 34d78ae2d53f14d25314116ed654e4d12f143d16 Mon Sep 17 00:00:00 2001 From: Chris Kacerguis Date: Sat, 26 Jul 2014 14:50:18 -0500 Subject: [PATCH 162/651] closes #332 --- application/libraries/REST_Controller.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 34d349ba..09c8e253 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -1257,12 +1257,16 @@ protected function _perform_library_auth($username = '', $password = null) * @param string $password The user's password * @return boolean */ - protected function _check_login($username = '', $password = null) + protected function _check_login($username = '', $password = false) { if (empty($username)) { return false; } + if ($password === false) { + return false; + } + $auth_source = strtolower($this->config->item('auth_source')); if ($auth_source == 'ldap') { @@ -1279,12 +1283,11 @@ protected function _check_login($username = '', $password = null) $valid_logins = $this->config->item('rest_valid_logins'); - if ( ! array_key_exists($username, $valid_logins)) { + if (!array_key_exists($username, $valid_logins)) { return false; } - // If actually null (not empty string) then do not check it - if ($password === null and $valid_logins[$username] != $password) { + if ($valid_logins[$username] != $password) { return false; } From f5eba0b1f73e5e248d9c8abd83e020d243a9cd81 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 31 Jul 2014 15:00:43 +0300 Subject: [PATCH 163/651] delegated _check_login (child to ) to do digest hashing --- application/libraries/REST_Controller.php | 37 ++++++++++------------- 1 file changed, 16 insertions(+), 21 deletions(-) mode change 100644 => 100755 application/libraries/REST_Controller.php diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php old mode 100644 new mode 100755 index bdecef0a..9f2e9782 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -286,7 +286,7 @@ public function __construct() $this->auth_override = $this->_auth_override_check(); // Checking for keys? GET TO WorK! - // Skip keys test for $config['auth_override_class_method']['class'['method'] = 'none' + // Skip keys test for $config['auth_override_class_method']['class'['method'] = 'none' if (config_item('rest_enable_keys') and $this->auth_override !== true) { $this->_allow = $this->_detect_api_key(); } @@ -444,8 +444,6 @@ protected function _fire_method($method, $args) */ public function response($data = null, $http_code = null, $continue = false) { - global $CFG; - // If data is null and not code provide, error and bail if ($data === null && $http_code === null) { $http_code = 404; @@ -462,7 +460,7 @@ public function response($data = null, $http_code = null, $continue = false) // Otherwise (if no data but 200 provided) or some data, carry on camping! else { // Is compression requested? - if ($CFG->item('compress_output') === true && $this->_zlib_oc == false) { + if ($this->config->item('compress_output') === true && $this->_zlib_oc == false) { if (extension_loaded('zlib')) { if (isset($_SERVER['HTTP_ACCEPT_ENCODING']) and strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== false) { ob_start('ob_gzhandler'); @@ -501,7 +499,7 @@ public function response($data = null, $http_code = null, $continue = false) // but it will not modify the content-length header to compensate for // the reduction, causing the browser to hang waiting for more data. // We'll just skip content-length in those cases. - if ( ! $this->_zlib_oc && ! $CFG->item('compress_output')) { + if ( ! $this->_zlib_oc && ! $this->config->item('compress_output')) { header('Content-Length: ' . strlen($output)); } @@ -794,8 +792,8 @@ protected function _check_limit($controller_method) ->get(config_item('rest_limits_table')) ->row(); - // No calls yet, or been an hour since they called - if ( ! $result or $result->hour_started < time() - (60 * 60)) { + // No calls yet for this key + if ( ! $result ) { // Right, set one up from scratch $this->rest->db->insert(config_item('rest_limits_table'), array( 'uri' => $this->uri->uri_string(), @@ -810,7 +808,7 @@ protected function _check_limit($controller_method) // Reset the started period $this->rest->db ->where('uri', $this->uri->uri_string()) - ->where('api_key_id', $this->rest->key_id) + ->where('api_key', isset($this->rest->key) ? $this->rest->key : '') ->set('hour_started', time()) ->set('count', 1) ->update(config_item('rest_limits_table')); @@ -1303,13 +1301,19 @@ protected function _check_login($username = '', $password = false) if (empty($username)) { return false; } + + $auth_source = strtolower($this->config->item('auth_source')); + $rest_auth = strtolower($this->config->item('rest_auth')); + $valid_logins = $this->config->item('rest_valid_logins'); + + if (!$this->config->item('auth_source') && $rest_auth == 'digest') { // for digest we do not have a password passed as argument + return md5($username.':'.$this->config->item('rest_realm').':'.(isset($valid_logins[$username])?$valid_logins[$username]:'')); + } if ($password === false) { return false; } - $auth_source = strtolower($this->config->item('auth_source')); - if ($auth_source == 'ldap') { log_message('debug', 'performing LDAP authentication for $username'); @@ -1322,8 +1326,6 @@ protected function _check_login($username = '', $password = false) return $this->_perform_library_auth($username, $password); } - $valid_logins = $this->config->item('rest_valid_logins'); - if (!array_key_exists($username, $valid_logins)) { return false; } @@ -1410,18 +1412,11 @@ protected function _prepare_digest_auth() $digest = (empty($matches[1]) || empty($matches[2])) ? array() : array_combine($matches[1], $matches[2]); // For digest authentication the library function should return already stored md5(username:restrealm:password) for that username @see rest.php::auth_library_function config - if ( ! array_key_exists('username', $digest) or ! ($A1 = $this->_check_login($digest['username'])) ) { + $A1 = $this->_check_login($digest['username']); + if ( ! array_key_exists('username', $digest) or ! $A1 ) { $this->_force_login($uniqid); } - // If the auth_source is not selected use the rest.php configuration valid logins - if ( ! $this->config->item('auth_source') ) { - $valid_logins = $this->config->item('rest_valid_logins'); - $valid_pass = $valid_logins[$digest['username']]; - - // This is the valid response expected - $A1 = md5($digest['username'].':'.$this->config->item('rest_realm').':'.$valid_pass); - } $A2 = md5(strtoupper($this->request->method).':'.$digest['uri']); $valid_response = md5($A1.':'.$digest['nonce'].':'.$digest['nc'].':'.$digest['cnonce'].':'.$digest['qop'].':'.$A2); From 9469bf898d9a1b19b569eaa10cc3cf8bbe2916df Mon Sep 17 00:00:00 2001 From: root Date: Thu, 31 Jul 2014 15:17:44 +0300 Subject: [PATCH 164/651] delegated _check_login (child to ) to do digest hashing --- application/libraries/REST_Controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 9f2e9782..323a9cdc 100755 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -1412,7 +1412,7 @@ protected function _prepare_digest_auth() $digest = (empty($matches[1]) || empty($matches[2])) ? array() : array_combine($matches[1], $matches[2]); // For digest authentication the library function should return already stored md5(username:restrealm:password) for that username @see rest.php::auth_library_function config - $A1 = $this->_check_login($digest['username']); + $A1 = $this->_check_login($digest['username'], true); if ( ! array_key_exists('username', $digest) or ! $A1 ) { $this->_force_login($uniqid); } From b5c8da4e9f8c478292acae07fb9ba3fec287e253 Mon Sep 17 00:00:00 2001 From: Chris Kacerguis Date: Thu, 31 Jul 2014 19:13:19 -0500 Subject: [PATCH 165/651] updated license and docs --- application/libraries/REST_Controller.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 323a9cdc..980a4e3a 100755 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -9,8 +9,8 @@ * @subpackage Libraries * @category Libraries * @author Phil Sturgeon, Chris Kacerguis - * @license http://philsturgeon.co.uk/code/dbad-license - * @link https://github.com/philsturgeon/codeigniter-restserver + * @license MIT + * @link https://github.com/chriskacerguis/codeigniter-restserver * @version 3.0.0-pre */ abstract class REST_Controller extends CI_Controller From 7e2c0beb5d595943b2d5f4ef7aee7bd5c4a2a8e1 Mon Sep 17 00:00:00 2001 From: Chris Kacerguis Date: Thu, 31 Jul 2014 19:15:08 -0500 Subject: [PATCH 166/651] closes #336 --- application/libraries/REST_Controller.php | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 980a4e3a..7d7c8a8a 100755 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -1260,33 +1260,28 @@ protected function _perform_ldap_auth($username = '', $password = null) protected function _perform_library_auth($username = '', $password = null) { if (empty($username)) { - log_message('debug', 'Library Auth: failure, empty username'); - + log_message('error', 'Library Auth: failure, empty username'); return false; } - $auth_library_class = strtolower($this->config->item('auth_library_class')); - $auth_library_function = strtolower($this->config->item('auth_library_function')); + $auth_library_class = strtolower($this->config->item('auth_library_class')); + $auth_library_function = strtolower($this->config->item('auth_library_function')); if (empty($auth_library_class)) { log_message('debug', 'Library Auth: failure, empty auth_library_class'); - return false; } if (empty($auth_library_function)) { log_message('debug', 'Library Auth: failure, empty auth_library_function'); - return false; } - if (!is_callable(array($this->$auth_library_class, $auth_library_function))) { - + if (!is_callable(array($auth_library_class, $auth_library_function))) { $this->load->library($auth_library_class); - } - return $this->$auth_library_class->$auth_library_function($username, $password); + return $auth_library_class->$auth_library_function($username, $password); } /** From 3e3c83eb2712d5f5a361cb7f808d18cead8d17fd Mon Sep 17 00:00:00 2001 From: Phil Sturgeon Date: Fri, 1 Aug 2014 11:06:01 -0400 Subject: [PATCH 167/651] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 86fd3010..f9981abf 100644 --- a/README.md +++ b/README.md @@ -235,7 +235,7 @@ is now optional. If no level is set it will assume the method has a level of 0. ## Contributions -This project was originally written by the awesome Phil Sturgeon, however his involvment has shifted +This project was originally written by Phil Sturgeon, however his involvment has shifted as he is no longer using it. As of 11/20/2013 further developement and support will be done by Chris Kacerguis. Pull Requests are the best way to fix bugs or add features. I know loads of you use this, so please From e4c0b1262f303221eb9e588a96c495f7007f1335 Mon Sep 17 00:00:00 2001 From: Chris Kacerguis Date: Fri, 29 Aug 2014 11:47:48 -0500 Subject: [PATCH 168/651] fixes #344 --- application/libraries/REST_Controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 7d7c8a8a..cd9ae4bb 100755 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -1281,7 +1281,7 @@ protected function _perform_library_auth($username = '', $password = null) $this->load->library($auth_library_class); } - return $auth_library_class->$auth_library_function($username, $password); + return $this->{$auth_library_class}->$auth_library_function($username, $password); } /** From 2414bee42dcb9248b9f3642ad4fc2abef033148e Mon Sep 17 00:00:00 2001 From: Gary Date: Mon, 8 Sep 2014 15:00:56 +0100 Subject: [PATCH 169/651] Updated rest.php Changed the documentation for 'limits table' to show correct table name --- application/config/rest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/config/rest.php b/application/config/rest.php index 6a306238..91793155 100644 --- a/application/config/rest.php +++ b/application/config/rest.php @@ -395,7 +395,7 @@ | | The table name in your database that stores limits. | -| 'logs' +| 'limits' | */ $config['rest_limits_table'] = 'limits'; From 42591e2745e32ae0bb4cf0a6ec2a049962ca45e9 Mon Sep 17 00:00:00 2001 From: Chris Kacerguis Date: Sun, 14 Sep 2014 20:40:53 -0500 Subject: [PATCH 170/651] updated docs to reflect that the REST_Library is not to be autoloaded. Fixes #348 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f9981abf..252f6750 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ _Note: for 1.7.x support download v2.2 from Downloads tab_ ## Installation -Drag and drop the **application/libraries/Format.php** and **application/libraries/REST_Controller.php** files into your application's directories. Either autoload the `REST_Controller` class or `require_once` it at the top of your controllers to load it into the scope. Additionally, copy the **rest.php** file from **application/config** in your application's configuration directory. +Drag and drop the **application/libraries/Format.php** and **application/libraries/REST_Controller.php** files into your application's directories. To use 'require_once` it at the top of your controllers to load it into the scope. Additionally, copy the **rest.php** file from **application/config** in your application's configuration directory. ## Handling Requests From 6e4dcc0dd27c2906afa6c688713f8b8e90c45133 Mon Sep 17 00:00:00 2001 From: Adriano Rosa Date: Wed, 24 Sep 2014 14:17:10 -0300 Subject: [PATCH 171/651] correct readme markdown --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 252f6750..c4b9496c 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ _Note: for 1.7.x support download v2.2 from Downloads tab_ ## Installation -Drag and drop the **application/libraries/Format.php** and **application/libraries/REST_Controller.php** files into your application's directories. To use 'require_once` it at the top of your controllers to load it into the scope. Additionally, copy the **rest.php** file from **application/config** in your application's configuration directory. +Drag and drop the **application/libraries/Format.php** and **application/libraries/REST_Controller.php** files into your application's directories. To use `require_once` it at the top of your controllers to load it into the scope. Additionally, copy the **rest.php** file from **application/config** in your application's configuration directory. ## Handling Requests From fecf6d978ff0f26a596d2cc716ceb55aad6264bd Mon Sep 17 00:00:00 2001 From: admin36 Date: Fri, 24 Oct 2014 11:51:16 -0400 Subject: [PATCH 172/651] Adding controller method limits documentation and examples Added a note on how to utilize controller method limits in application/config/rest.php; Added example controller method limits in application/controllers/api/example.php --- application/config/rest.php | 6 ++++++ application/controllers/api/example.php | 15 ++++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/application/config/rest.php b/application/config/rest.php index 91793155..e09d72ce 100644 --- a/application/config/rest.php +++ b/application/config/rest.php @@ -420,6 +420,12 @@ PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; | +| To specify limits, within your Controller __construct() method add per-method +| limits with: + + $this->method['METHOD_NAME']['limit'] = [NUM_REQUESTS_PER_HOUR]; + +| See application/controllers/api/example.php for examples. */ $config['rest_enable_limits'] = FALSE; diff --git a/application/controllers/api/example.php b/application/controllers/api/example.php index edfd1e32..f2a00f48 100644 --- a/application/controllers/api/example.php +++ b/application/controllers/api/example.php @@ -18,7 +18,20 @@ class Example extends REST_Controller { - function user_get() + function __construct() + { + // Construct our parent class + parent::__construct(); + + // Configure limits on our controller methods. Ensure + // you have created the 'limits' table and enabled 'limits' + // within application/config/rest.php + $this->methods['user_get']['limit'] = 500; //500 requests per hour per user/key + $this->methods['user_post']['limit'] = 100; //100 requests per hour per user/key + $this->methods['user_delete']['limit'] = 50; //100 requests per hour per user/key + } + + function user_get() { if(!$this->get('id')) { From 26fb60742651353bd9efa465a9363f96b2326408 Mon Sep 17 00:00:00 2001 From: admin36 Date: Fri, 24 Oct 2014 11:51:45 -0400 Subject: [PATCH 173/651] Fix example --- application/controllers/api/example.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/controllers/api/example.php b/application/controllers/api/example.php index f2a00f48..c483ca83 100644 --- a/application/controllers/api/example.php +++ b/application/controllers/api/example.php @@ -28,7 +28,7 @@ function __construct() // within application/config/rest.php $this->methods['user_get']['limit'] = 500; //500 requests per hour per user/key $this->methods['user_post']['limit'] = 100; //100 requests per hour per user/key - $this->methods['user_delete']['limit'] = 50; //100 requests per hour per user/key + $this->methods['user_delete']['limit'] = 50; //50 requests per hour per user/key } function user_get() From d218d7e6a770124e4a80842eeab709bf548444c6 Mon Sep 17 00:00:00 2001 From: admin36 Date: Fri, 31 Oct 2014 11:30:11 -0400 Subject: [PATCH 174/651] Add Controller Sub-Folder Access Support Enables codeigniter-restserver to restrict access to controllers that are in sub-folder paths. Useful for custom routers that allow arbitrary controller sub folder organization PLUS compatability with Codeigniter 3 controller sub-folders. --- application/libraries/REST_Controller.php | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index cd9ae4bb..86abd8b1 100755 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -1513,12 +1513,17 @@ protected function _check_access() return true; } - $controller = explode('/', $this->uri->uri_string()); + // Fetch controller based on path and controller name + $controller = implode( '/', array($this->router->fetch_directory(), $this->router->fetch_class()) ); + + // Remove any double slashes for safety + $controller = str_replace('//', '/', $controller); + // Build access table query $this->rest->db->select(); $this->rest->db->where('key', $this->rest->key); - $this->rest->db->where('controller', $controller[0]); - + $this->rest->db->where('controller', $controller); + $query = $this->rest->db->get(config_item('rest_access_table')); if ($query->num_rows > 0) { From ac033c423175a62f5768e9951021b91bf6a55b80 Mon Sep 17 00:00:00 2001 From: Rikard Hassel Date: Wed, 3 Dec 2014 20:27:50 +0100 Subject: [PATCH 175/651] User defined configuration file. Make it possible to specify which configuration file to use when extending this class, in my example we have a pretty complex system where we have two different interfaces, where different groups of end users will use different authentication methods, one will use session based and another will use basic authentication, with this change you could enable that behaviour. --- application/libraries/REST_Controller.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 86abd8b1..06732bf1 100755 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -192,7 +192,7 @@ protected function early_checks() * Constructor function * @todo Document more please. */ - public function __construct() + public function __construct($config = 'rest') { parent::__construct(); @@ -200,7 +200,7 @@ public function __construct() $this->_start_rtime = microtime(true); // Lets grab the config and get ready to party - $this->load->config('rest'); + $this->load->config($config); // This library is bundled with REST_Controller 2.5+, but will eventually be part of CodeIgniter itself $this->load->library('format'); From 280f7919be530f66ecc44a567ee763d9131f12e9 Mon Sep 17 00:00:00 2001 From: agung_wete Date: Tue, 9 Dec 2014 21:49:42 +0700 Subject: [PATCH 176/651] Change default "Enable emulate request" comment to TRUE in application/config/rest.php --- application/config/rest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/config/rest.php b/application/config/rest.php index e09d72ce..4dbe3557 100644 --- a/application/config/rest.php +++ b/application/config/rest.php @@ -55,7 +55,7 @@ | | Should we enable emulation of the request (e.g. used in Mootools request)? | -| Default: false +| Default: TRUE | */ $config['enable_emulate_request'] = TRUE; From 58a474d7ccca4b4d5803df72c510a811b80ab738 Mon Sep 17 00:00:00 2001 From: Chris Kacerguis Date: Fri, 12 Dec 2014 20:46:31 -0600 Subject: [PATCH 177/651] fixed #371 --- application/libraries/Format.php | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/application/libraries/Format.php b/application/libraries/Format.php index 5cd812d4..aca0b1e2 100644 --- a/application/libraries/Format.php +++ b/application/libraries/Format.php @@ -131,7 +131,7 @@ public function to_xml($data = null, $structure = null, $basenode = 'xml') { $attributes = $value; if (is_object($attributes)) $attributes = get_object_vars($attributes); - + foreach ($attributes as $attributeName => $attributeValue) { $structure->addAttribute($attributeName, $attributeValue); @@ -213,7 +213,7 @@ public function to_csv() throw new Exception('Format class does not support multi-dimensional arrays'); } else { $row = str_replace('"', '""', $row); // Escape dbl quotes per RFC 4180 - $output .= '"'.implode('","', $row).'"'.PHP_EOL; + $output .= '"'.implode('","', $row).'"'.PHP_EOL; } } @@ -228,12 +228,12 @@ public function to_json() if ($callback === '') { return json_encode($this->_data); - + /* Had to take out this code, it doesn't work on Objects. $str = $this->_data; - array_walk_recursive($str, function(&$item, $key) + array_walk_recursive($str, function(&$item, $key) { - if(!mb_detect_encoding($item, 'utf-8', true)) + if(!mb_detect_encoding($item, 'utf-8', true)) { $item = utf8_encode($item); } @@ -311,6 +311,13 @@ private function _from_serialize($string) return unserialize(trim($string)); } + // If you provide text/plain value on the Content-type header on a request + // just return the string + private function _from_php($string) + { + return trim($string) + } + } /* End of file format.php */ From db7389eb3f243bb92d001467328b7980800f7ab0 Mon Sep 17 00:00:00 2001 From: fripig Date: Sun, 14 Dec 2014 21:26:31 +0800 Subject: [PATCH 178/651] Update Format.php miss ; --- application/libraries/Format.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/libraries/Format.php b/application/libraries/Format.php index aca0b1e2..bf3d2fa1 100644 --- a/application/libraries/Format.php +++ b/application/libraries/Format.php @@ -315,7 +315,7 @@ private function _from_serialize($string) // just return the string private function _from_php($string) { - return trim($string) + return trim($string); } } From a1f96644eab251c6098d3cc2ef2ab8290d8d6c24 Mon Sep 17 00:00:00 2001 From: Mahmoud Said Date: Mon, 22 Dec 2014 19:19:12 +0200 Subject: [PATCH 179/651] Fix the check if a file extension is used --- application/libraries/REST_Controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 06732bf1..a39521dd 100755 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -565,7 +565,7 @@ protected function _detect_output_format() } // Check if a file extension is used - elseif ($this->_get_args and !is_array(end($this->_get_args)) and preg_match($pattern, end($this->_get_args), $matches)) { + elseif ($this->_get_args and !is_array(end($this->_get_args)) and preg_match($pattern, end(array_keys($this->_get_args)), $matches)) { // The key of the last argument $last_key = end(array_keys($this->_get_args)); From d865783f673cd1776fc2866728a9f000faa67387 Mon Sep 17 00:00:00 2001 From: Chris Kacerguis Date: Thu, 15 Jan 2015 20:37:49 -0600 Subject: [PATCH 180/651] removed CI files (now it is just the REST files) --- application/cache/.htaccess | 1 - application/cache/index.html | 10 - application/config/autoload.php | 116 - application/config/config.php | 360 --- application/config/constants.php | 41 - application/config/database.php | 62 - application/config/doctypes.php | 15 - application/config/foreign_chars.php | 64 - application/config/hooks.php | 16 - application/config/index.html | 10 - application/config/ldap.php | 21 - application/config/mimes.php | 106 - application/config/profiler.php | 17 - application/config/routes.php | 46 - application/config/smileys.php | 66 - application/config/user_agents.php | 178 -- .../unit_tests/wildcard_test_cases.php | 37 - .../unit_tests/wildcard_test_harness.php | 67 - application/controllers/welcome.php | 18 - application/errors/error_404.php | 34 - application/errors/error_db.php | 34 - application/errors/error_general.php | 34 - application/errors/error_php.php | 10 - application/errors/index.html | 10 - application/helpers/index.html | 10 - application/hooks/index.html | 10 - application/language/english/index.html | 10 - application/logs/index.html | 10 - application/models/index.html | 10 - application/third_party/index.html | 10 - application/views/index.html | 10 - application/views/welcome_message.php | 89 - index.php | 205 -- system/.htaccess | 1 - system/core/Benchmark.php | 118 - system/core/CodeIgniter.php | 402 ---- system/core/Common.php | 564 ----- system/core/Config.php | 379 --- system/core/Controller.php | 64 - system/core/Exceptions.php | 193 -- system/core/Hooks.php | 248 -- system/core/Input.php | 849 ------- system/core/Lang.php | 160 -- system/core/Loader.php | 1248 ---------- system/core/Model.php | 57 - system/core/Output.php | 574 ----- system/core/Router.php | 522 ---- system/core/Security.php | 876 ------- system/core/URI.php | 654 ------ system/core/Utf8.php | 165 -- system/core/index.html | 10 - system/database/DB.php | 162 -- system/database/DB_active_rec.php | 2045 ---------------- system/database/DB_cache.php | 195 -- system/database/DB_driver.php | 1410 ----------- system/database/DB_forge.php | 382 --- system/database/DB_result.php | 410 ---- system/database/DB_utility.php | 414 ---- .../database/drivers/cubrid/cubrid_driver.php | 792 ------- .../database/drivers/cubrid/cubrid_forge.php | 288 --- .../database/drivers/cubrid/cubrid_result.php | 202 -- .../drivers/cubrid/cubrid_utility.php | 108 - system/database/drivers/cubrid/index.html | 10 - system/database/drivers/index.html | 10 - system/database/drivers/mssql/index.html | 10 - .../database/drivers/mssql/mssql_driver.php | 667 ------ system/database/drivers/mssql/mssql_forge.php | 248 -- .../database/drivers/mssql/mssql_result.php | 169 -- .../database/drivers/mssql/mssql_utility.php | 88 - system/database/drivers/mysql/index.html | 10 - .../database/drivers/mysql/mysql_driver.php | 779 ------ system/database/drivers/mysql/mysql_forge.php | 273 --- .../database/drivers/mysql/mysql_result.php | 174 -- .../database/drivers/mysql/mysql_utility.php | 210 -- system/database/drivers/mysqli/index.html | 10 - .../database/drivers/mysqli/mysqli_driver.php | 776 ------ .../database/drivers/mysqli/mysqli_forge.php | 258 -- .../database/drivers/mysqli/mysqli_result.php | 174 -- .../drivers/mysqli/mysqli_utility.php | 87 - system/database/drivers/oci8/index.html | 10 - system/database/drivers/oci8/oci8_driver.php | 808 ------- system/database/drivers/oci8/oci8_forge.php | 248 -- system/database/drivers/oci8/oci8_result.php | 217 -- system/database/drivers/oci8/oci8_utility.php | 87 - system/database/drivers/odbc/index.html | 10 - system/database/drivers/odbc/odbc_driver.php | 637 ----- system/database/drivers/odbc/odbc_forge.php | 266 --- system/database/drivers/odbc/odbc_result.php | 228 -- system/database/drivers/odbc/odbc_utility.php | 103 - system/database/drivers/pdo/index.html | 10 - system/database/drivers/pdo/pdo_driver.php | 812 ------- system/database/drivers/pdo/pdo_forge.php | 266 --- system/database/drivers/pdo/pdo_result.php | 183 -- system/database/drivers/pdo/pdo_utility.php | 103 - system/database/drivers/postgre/index.html | 10 - .../drivers/postgre/postgre_driver.php | 703 ------ .../drivers/postgre/postgre_forge.php | 299 --- .../drivers/postgre/postgre_result.php | 169 -- .../drivers/postgre/postgre_utility.php | 88 - system/database/drivers/sqlite/index.html | 10 - .../database/drivers/sqlite/sqlite_driver.php | 658 ------ .../database/drivers/sqlite/sqlite_forge.php | 265 --- .../database/drivers/sqlite/sqlite_result.php | 179 -- .../drivers/sqlite/sqlite_utility.php | 96 - system/database/drivers/sqlsrv/index.html | 10 - .../database/drivers/sqlsrv/sqlsrv_driver.php | 599 ----- .../database/drivers/sqlsrv/sqlsrv_forge.php | 248 -- .../database/drivers/sqlsrv/sqlsrv_result.php | 169 -- .../drivers/sqlsrv/sqlsrv_utility.php | 88 - system/database/index.html | 10 - system/fonts/index.html | 10 - system/fonts/texb.ttf | Bin 143830 -> 0 bytes system/helpers/array_helper.php | 119 - system/helpers/captcha_helper.php | 246 -- system/helpers/cookie_helper.php | 103 - system/helpers/date_helper.php | 611 ----- system/helpers/directory_helper.php | 80 - system/helpers/download_helper.php | 107 - system/helpers/email_helper.php | 62 - system/helpers/file_helper.php | 479 ---- system/helpers/form_helper.php | 1054 --------- system/helpers/html_helper.php | 436 ---- system/helpers/index.html | 10 - system/helpers/inflector_helper.php | 203 -- system/helpers/language_helper.php | 58 - system/helpers/number_helper.php | 76 - system/helpers/path_helper.php | 72 - system/helpers/security_helper.php | 128 - system/helpers/smiley_helper.php | 281 --- system/helpers/string_helper.php | 307 --- system/helpers/text_helper.php | 535 ----- system/helpers/typography_helper.php | 93 - system/helpers/url_helper.php | 594 ----- system/helpers/xml_helper.php | 71 - system/index.html | 10 - system/language/english/calendar_lang.php | 51 - system/language/english/date_lang.php | 61 - system/language/english/db_lang.php | 29 - system/language/english/email_lang.php | 24 - .../language/english/form_validation_lang.php | 29 - system/language/english/ftp_lang.php | 18 - system/language/english/imglib_lang.php | 24 - system/language/english/index.html | 10 - system/language/english/migration_lang.php | 13 - system/language/english/number_lang.php | 10 - system/language/english/profiler_lang.php | 25 - system/language/english/unit_test_lang.php | 25 - system/language/english/upload_lang.php | 22 - system/language/index.html | 10 - system/libraries/Cache/Cache.php | 216 -- system/libraries/Cache/drivers/Cache_apc.php | 151 -- .../libraries/Cache/drivers/Cache_dummy.php | 129 - system/libraries/Cache/drivers/Cache_file.php | 195 -- .../Cache/drivers/Cache_memcached.php | 218 -- system/libraries/Calendar.php | 475 ---- system/libraries/Cart.php | 552 ----- system/libraries/Driver.php | 229 -- system/libraries/Email.php | 2092 ----------------- system/libraries/Encrypt.php | 547 ----- system/libraries/Form_validation.php | 1382 ----------- system/libraries/Ftp.php | 660 ------ system/libraries/Image_lib.php | 1537 ------------ system/libraries/Javascript.php | 871 ------- system/libraries/Log.php | 114 - system/libraries/Migration.php | 328 --- system/libraries/Pagination.php | 340 --- system/libraries/Parser.php | 212 -- system/libraries/Profiler.php | 558 ----- system/libraries/Session.php | 780 ------ system/libraries/Sha1.php | 251 -- system/libraries/Table.php | 531 ----- system/libraries/Trackback.php | 548 ----- system/libraries/Typography.php | 410 ---- system/libraries/Unit_test.php | 383 --- system/libraries/Upload.php | 1136 --------- system/libraries/User_agent.php | 549 ----- system/libraries/Xmlrpc.php | 1423 ----------- system/libraries/Xmlrpcs.php | 612 ----- system/libraries/Zip.php | 423 ---- system/libraries/index.html | 10 - system/libraries/javascript/Jquery.php | 1071 --------- 181 files changed, 51800 deletions(-) delete mode 100755 application/cache/.htaccess delete mode 100755 application/cache/index.html delete mode 100755 application/config/autoload.php delete mode 100755 application/config/config.php delete mode 100755 application/config/constants.php delete mode 100755 application/config/database.php delete mode 100755 application/config/doctypes.php delete mode 100755 application/config/foreign_chars.php delete mode 100755 application/config/hooks.php delete mode 100755 application/config/index.html delete mode 100755 application/config/ldap.php delete mode 100644 application/config/mimes.php delete mode 100755 application/config/profiler.php delete mode 100755 application/config/routes.php delete mode 100755 application/config/smileys.php delete mode 100644 application/config/user_agents.php delete mode 100644 application/controllers/unit_tests/wildcard_test_cases.php delete mode 100644 application/controllers/unit_tests/wildcard_test_harness.php delete mode 100644 application/controllers/welcome.php delete mode 100755 application/errors/error_404.php delete mode 100755 application/errors/error_db.php delete mode 100755 application/errors/error_general.php delete mode 100755 application/errors/error_php.php delete mode 100755 application/errors/index.html delete mode 100755 application/helpers/index.html delete mode 100755 application/hooks/index.html delete mode 100755 application/language/english/index.html delete mode 100755 application/logs/index.html delete mode 100755 application/models/index.html delete mode 100755 application/third_party/index.html delete mode 100755 application/views/index.html delete mode 100755 application/views/welcome_message.php delete mode 100644 index.php delete mode 100644 system/.htaccess delete mode 100755 system/core/Benchmark.php delete mode 100755 system/core/CodeIgniter.php delete mode 100644 system/core/Common.php delete mode 100755 system/core/Config.php delete mode 100644 system/core/Controller.php delete mode 100755 system/core/Exceptions.php delete mode 100755 system/core/Hooks.php delete mode 100755 system/core/Input.php delete mode 100755 system/core/Lang.php delete mode 100644 system/core/Loader.php delete mode 100755 system/core/Model.php delete mode 100755 system/core/Output.php delete mode 100755 system/core/Router.php delete mode 100755 system/core/Security.php delete mode 100755 system/core/URI.php delete mode 100644 system/core/Utf8.php delete mode 100644 system/core/index.html delete mode 100755 system/database/DB.php delete mode 100644 system/database/DB_active_rec.php delete mode 100644 system/database/DB_cache.php delete mode 100644 system/database/DB_driver.php delete mode 100644 system/database/DB_forge.php delete mode 100644 system/database/DB_result.php delete mode 100644 system/database/DB_utility.php delete mode 100644 system/database/drivers/cubrid/cubrid_driver.php delete mode 100644 system/database/drivers/cubrid/cubrid_forge.php delete mode 100644 system/database/drivers/cubrid/cubrid_result.php delete mode 100644 system/database/drivers/cubrid/cubrid_utility.php delete mode 100644 system/database/drivers/cubrid/index.html delete mode 100644 system/database/drivers/index.html delete mode 100644 system/database/drivers/mssql/index.html delete mode 100644 system/database/drivers/mssql/mssql_driver.php delete mode 100644 system/database/drivers/mssql/mssql_forge.php delete mode 100644 system/database/drivers/mssql/mssql_result.php delete mode 100644 system/database/drivers/mssql/mssql_utility.php delete mode 100644 system/database/drivers/mysql/index.html delete mode 100644 system/database/drivers/mysql/mysql_driver.php delete mode 100644 system/database/drivers/mysql/mysql_forge.php delete mode 100644 system/database/drivers/mysql/mysql_result.php delete mode 100644 system/database/drivers/mysql/mysql_utility.php delete mode 100644 system/database/drivers/mysqli/index.html delete mode 100644 system/database/drivers/mysqli/mysqli_driver.php delete mode 100644 system/database/drivers/mysqli/mysqli_forge.php delete mode 100644 system/database/drivers/mysqli/mysqli_result.php delete mode 100644 system/database/drivers/mysqli/mysqli_utility.php delete mode 100644 system/database/drivers/oci8/index.html delete mode 100644 system/database/drivers/oci8/oci8_driver.php delete mode 100644 system/database/drivers/oci8/oci8_forge.php delete mode 100644 system/database/drivers/oci8/oci8_result.php delete mode 100644 system/database/drivers/oci8/oci8_utility.php delete mode 100644 system/database/drivers/odbc/index.html delete mode 100644 system/database/drivers/odbc/odbc_driver.php delete mode 100644 system/database/drivers/odbc/odbc_forge.php delete mode 100644 system/database/drivers/odbc/odbc_result.php delete mode 100644 system/database/drivers/odbc/odbc_utility.php delete mode 100644 system/database/drivers/pdo/index.html delete mode 100644 system/database/drivers/pdo/pdo_driver.php delete mode 100644 system/database/drivers/pdo/pdo_forge.php delete mode 100644 system/database/drivers/pdo/pdo_result.php delete mode 100644 system/database/drivers/pdo/pdo_utility.php delete mode 100644 system/database/drivers/postgre/index.html delete mode 100644 system/database/drivers/postgre/postgre_driver.php delete mode 100644 system/database/drivers/postgre/postgre_forge.php delete mode 100644 system/database/drivers/postgre/postgre_result.php delete mode 100644 system/database/drivers/postgre/postgre_utility.php delete mode 100644 system/database/drivers/sqlite/index.html delete mode 100644 system/database/drivers/sqlite/sqlite_driver.php delete mode 100644 system/database/drivers/sqlite/sqlite_forge.php delete mode 100644 system/database/drivers/sqlite/sqlite_result.php delete mode 100644 system/database/drivers/sqlite/sqlite_utility.php delete mode 100644 system/database/drivers/sqlsrv/index.html delete mode 100644 system/database/drivers/sqlsrv/sqlsrv_driver.php delete mode 100644 system/database/drivers/sqlsrv/sqlsrv_forge.php delete mode 100644 system/database/drivers/sqlsrv/sqlsrv_result.php delete mode 100644 system/database/drivers/sqlsrv/sqlsrv_utility.php delete mode 100644 system/database/index.html delete mode 100644 system/fonts/index.html delete mode 100644 system/fonts/texb.ttf delete mode 100644 system/helpers/array_helper.php delete mode 100644 system/helpers/captcha_helper.php delete mode 100644 system/helpers/cookie_helper.php delete mode 100644 system/helpers/date_helper.php delete mode 100644 system/helpers/directory_helper.php delete mode 100644 system/helpers/download_helper.php delete mode 100644 system/helpers/email_helper.php delete mode 100644 system/helpers/file_helper.php delete mode 100644 system/helpers/form_helper.php delete mode 100644 system/helpers/html_helper.php delete mode 100644 system/helpers/index.html delete mode 100644 system/helpers/inflector_helper.php delete mode 100644 system/helpers/language_helper.php delete mode 100644 system/helpers/number_helper.php delete mode 100644 system/helpers/path_helper.php delete mode 100644 system/helpers/security_helper.php delete mode 100644 system/helpers/smiley_helper.php delete mode 100644 system/helpers/string_helper.php delete mode 100644 system/helpers/text_helper.php delete mode 100644 system/helpers/typography_helper.php delete mode 100644 system/helpers/url_helper.php delete mode 100644 system/helpers/xml_helper.php delete mode 100644 system/index.html delete mode 100644 system/language/english/calendar_lang.php delete mode 100644 system/language/english/date_lang.php delete mode 100644 system/language/english/db_lang.php delete mode 100644 system/language/english/email_lang.php delete mode 100644 system/language/english/form_validation_lang.php delete mode 100644 system/language/english/ftp_lang.php delete mode 100644 system/language/english/imglib_lang.php delete mode 100644 system/language/english/index.html delete mode 100644 system/language/english/migration_lang.php delete mode 100644 system/language/english/number_lang.php delete mode 100644 system/language/english/profiler_lang.php delete mode 100644 system/language/english/unit_test_lang.php delete mode 100644 system/language/english/upload_lang.php delete mode 100644 system/language/index.html delete mode 100644 system/libraries/Cache/Cache.php delete mode 100644 system/libraries/Cache/drivers/Cache_apc.php delete mode 100644 system/libraries/Cache/drivers/Cache_dummy.php delete mode 100644 system/libraries/Cache/drivers/Cache_file.php delete mode 100644 system/libraries/Cache/drivers/Cache_memcached.php delete mode 100644 system/libraries/Calendar.php delete mode 100644 system/libraries/Cart.php delete mode 100644 system/libraries/Driver.php delete mode 100644 system/libraries/Email.php delete mode 100644 system/libraries/Encrypt.php delete mode 100644 system/libraries/Form_validation.php delete mode 100644 system/libraries/Ftp.php delete mode 100644 system/libraries/Image_lib.php delete mode 100644 system/libraries/Javascript.php delete mode 100644 system/libraries/Log.php delete mode 100644 system/libraries/Migration.php delete mode 100644 system/libraries/Pagination.php delete mode 100644 system/libraries/Parser.php delete mode 100644 system/libraries/Profiler.php delete mode 100644 system/libraries/Session.php delete mode 100644 system/libraries/Sha1.php delete mode 100644 system/libraries/Table.php delete mode 100644 system/libraries/Trackback.php delete mode 100644 system/libraries/Typography.php delete mode 100644 system/libraries/Unit_test.php delete mode 100644 system/libraries/Upload.php delete mode 100644 system/libraries/User_agent.php delete mode 100644 system/libraries/Xmlrpc.php delete mode 100644 system/libraries/Xmlrpcs.php delete mode 100644 system/libraries/Zip.php delete mode 100644 system/libraries/index.html delete mode 100644 system/libraries/javascript/Jquery.php diff --git a/application/cache/.htaccess b/application/cache/.htaccess deleted file mode 100755 index 3418e55a..00000000 --- a/application/cache/.htaccess +++ /dev/null @@ -1 +0,0 @@ -deny from all \ No newline at end of file diff --git a/application/cache/index.html b/application/cache/index.html deleted file mode 100755 index c942a79c..00000000 --- a/application/cache/index.html +++ /dev/null @@ -1,10 +0,0 @@ - - - 403 Forbidden - - - -

Directory access is forbidden.

- - - \ No newline at end of file diff --git a/application/config/autoload.php b/application/config/autoload.php deleted file mode 100755 index 90b1a808..00000000 --- a/application/config/autoload.php +++ /dev/null @@ -1,116 +0,0 @@ - '', - 'xhtml1-strict' => '', - 'xhtml1-trans' => '', - 'xhtml1-frame' => '', - 'html5' => '', - 'html4-strict' => '', - 'html4-trans' => '', - 'html4-frame' => '' - ); - -/* End of file doctypes.php */ -/* Location: ./application/config/doctypes.php */ \ No newline at end of file diff --git a/application/config/foreign_chars.php b/application/config/foreign_chars.php deleted file mode 100755 index 14b0d737..00000000 --- a/application/config/foreign_chars.php +++ /dev/null @@ -1,64 +0,0 @@ - 'ae', - '/ö|œ/' => 'oe', - '/ü/' => 'ue', - '/Ä/' => 'Ae', - '/Ü/' => 'Ue', - '/Ö/' => 'Oe', - '/À|Á|Â|Ã|Ä|Å|Ǻ|Ā|Ă|Ą|Ǎ/' => 'A', - '/à|á|â|ã|å|ǻ|ā|ă|ą|ǎ|ª/' => 'a', - '/Ç|Ć|Ĉ|Ċ|Č/' => 'C', - '/ç|ć|ĉ|ċ|č/' => 'c', - '/Ð|Ď|Đ/' => 'D', - '/ð|ď|đ/' => 'd', - '/È|É|Ê|Ë|Ē|Ĕ|Ė|Ę|Ě/' => 'E', - '/è|é|ê|ë|ē|ĕ|ė|ę|ě/' => 'e', - '/Ĝ|Ğ|Ġ|Ģ/' => 'G', - '/ĝ|ğ|ġ|ģ/' => 'g', - '/Ĥ|Ħ/' => 'H', - '/ĥ|ħ/' => 'h', - '/Ì|Í|Î|Ï|Ĩ|Ī|Ĭ|Ǐ|Į|İ/' => 'I', - '/ì|í|î|ï|ĩ|ī|ĭ|ǐ|į|ı/' => 'i', - '/Ĵ/' => 'J', - '/ĵ/' => 'j', - '/Ķ/' => 'K', - '/ķ/' => 'k', - '/Ĺ|Ļ|Ľ|Ŀ|Ł/' => 'L', - '/ĺ|ļ|ľ|ŀ|ł/' => 'l', - '/Ñ|Ń|Ņ|Ň/' => 'N', - '/ñ|ń|ņ|ň|ʼn/' => 'n', - '/Ò|Ó|Ô|Õ|Ō|Ŏ|Ǒ|Ő|Ơ|Ø|Ǿ/' => 'O', - '/ò|ó|ô|õ|ō|ŏ|ǒ|ő|ơ|ø|ǿ|º/' => 'o', - '/Ŕ|Ŗ|Ř/' => 'R', - '/ŕ|ŗ|ř/' => 'r', - '/Ś|Ŝ|Ş|Š/' => 'S', - '/ś|ŝ|ş|š|ſ/' => 's', - '/Ţ|Ť|Ŧ/' => 'T', - '/ţ|ť|ŧ/' => 't', - '/Ù|Ú|Û|Ũ|Ū|Ŭ|Ů|Ű|Ų|Ư|Ǔ|Ǖ|Ǘ|Ǚ|Ǜ/' => 'U', - '/ù|ú|û|ũ|ū|ŭ|ů|ű|ų|ư|ǔ|ǖ|ǘ|ǚ|ǜ/' => 'u', - '/Ý|Ÿ|Ŷ/' => 'Y', - '/ý|ÿ|ŷ/' => 'y', - '/Ŵ/' => 'W', - '/ŵ/' => 'w', - '/Ź|Ż|Ž/' => 'Z', - '/ź|ż|ž/' => 'z', - '/Æ|Ǽ/' => 'AE', - '/ß/'=> 'ss', - '/IJ/' => 'IJ', - '/ij/' => 'ij', - '/Œ/' => 'OE', - '/ƒ/' => 'f' -); - -/* End of file foreign_chars.php */ -/* Location: ./application/config/foreign_chars.php */ \ No newline at end of file diff --git a/application/config/hooks.php b/application/config/hooks.php deleted file mode 100755 index a4ad2be6..00000000 --- a/application/config/hooks.php +++ /dev/null @@ -1,16 +0,0 @@ - - - 403 Forbidden - - - -

Directory access is forbidden.

- - - \ No newline at end of file diff --git a/application/config/ldap.php b/application/config/ldap.php deleted file mode 100755 index 2293c132..00000000 --- a/application/config/ldap.php +++ /dev/null @@ -1,21 +0,0 @@ - diff --git a/application/config/mimes.php b/application/config/mimes.php deleted file mode 100644 index 100f7d44..00000000 --- a/application/config/mimes.php +++ /dev/null @@ -1,106 +0,0 @@ - 'application/mac-binhex40', - 'cpt' => 'application/mac-compactpro', - 'csv' => array('text/x-comma-separated-values', 'text/comma-separated-values', 'application/octet-stream', 'application/vnd.ms-excel', 'application/x-csv', 'text/x-csv', 'text/csv', 'application/csv', 'application/excel', 'application/vnd.msexcel'), - 'bin' => 'application/macbinary', - 'dms' => 'application/octet-stream', - 'lha' => 'application/octet-stream', - 'lzh' => 'application/octet-stream', - 'exe' => array('application/octet-stream', 'application/x-msdownload'), - 'class' => 'application/octet-stream', - 'psd' => 'application/x-photoshop', - 'so' => 'application/octet-stream', - 'sea' => 'application/octet-stream', - 'dll' => 'application/octet-stream', - 'oda' => 'application/oda', - 'pdf' => array('application/pdf', 'application/x-download'), - 'ai' => 'application/postscript', - 'eps' => 'application/postscript', - 'ps' => 'application/postscript', - 'smi' => 'application/smil', - 'smil' => 'application/smil', - 'mif' => 'application/vnd.mif', - 'xls' => array('application/excel', 'application/vnd.ms-excel', 'application/msexcel'), - 'ppt' => array('application/powerpoint', 'application/vnd.ms-powerpoint'), - 'wbxml' => 'application/wbxml', - 'wmlc' => 'application/wmlc', - 'dcr' => 'application/x-director', - 'dir' => 'application/x-director', - 'dxr' => 'application/x-director', - 'dvi' => 'application/x-dvi', - 'gtar' => 'application/x-gtar', - 'gz' => 'application/x-gzip', - 'php' => 'application/x-httpd-php', - 'php4' => 'application/x-httpd-php', - 'php3' => 'application/x-httpd-php', - 'phtml' => 'application/x-httpd-php', - 'phps' => 'application/x-httpd-php-source', - 'js' => 'application/x-javascript', - 'swf' => 'application/x-shockwave-flash', - 'sit' => 'application/x-stuffit', - 'tar' => 'application/x-tar', - 'tgz' => array('application/x-tar', 'application/x-gzip-compressed'), - 'xhtml' => 'application/xhtml+xml', - 'xht' => 'application/xhtml+xml', - 'zip' => array('application/x-zip', 'application/zip', 'application/x-zip-compressed'), - 'mid' => 'audio/midi', - 'midi' => 'audio/midi', - 'mpga' => 'audio/mpeg', - 'mp2' => 'audio/mpeg', - 'mp3' => array('audio/mpeg', 'audio/mpg', 'audio/mpeg3', 'audio/mp3'), - 'aif' => 'audio/x-aiff', - 'aiff' => 'audio/x-aiff', - 'aifc' => 'audio/x-aiff', - 'ram' => 'audio/x-pn-realaudio', - 'rm' => 'audio/x-pn-realaudio', - 'rpm' => 'audio/x-pn-realaudio-plugin', - 'ra' => 'audio/x-realaudio', - 'rv' => 'video/vnd.rn-realvideo', - 'wav' => array('audio/x-wav', 'audio/wave', 'audio/wav'), - 'bmp' => array('image/bmp', 'image/x-windows-bmp'), - 'gif' => 'image/gif', - 'jpeg' => array('image/jpeg', 'image/pjpeg'), - 'jpg' => array('image/jpeg', 'image/pjpeg'), - 'jpe' => array('image/jpeg', 'image/pjpeg'), - 'png' => array('image/png', 'image/x-png'), - 'tiff' => 'image/tiff', - 'tif' => 'image/tiff', - 'css' => 'text/css', - 'html' => 'text/html', - 'htm' => 'text/html', - 'shtml' => 'text/html', - 'txt' => 'text/plain', - 'text' => 'text/plain', - 'log' => array('text/plain', 'text/x-log'), - 'rtx' => 'text/richtext', - 'rtf' => 'text/rtf', - 'xml' => 'text/xml', - 'xsl' => 'text/xml', - 'mpeg' => 'video/mpeg', - 'mpg' => 'video/mpeg', - 'mpe' => 'video/mpeg', - 'qt' => 'video/quicktime', - 'mov' => 'video/quicktime', - 'avi' => 'video/x-msvideo', - 'movie' => 'video/x-sgi-movie', - 'doc' => 'application/msword', - 'docx' => array('application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/zip'), - 'xlsx' => array('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/zip'), - 'word' => array('application/msword', 'application/octet-stream'), - 'xl' => 'application/excel', - 'eml' => 'message/rfc822', - 'json' => array('application/json', 'text/json') - ); - - -/* End of file mimes.php */ -/* Location: ./application/config/mimes.php */ diff --git a/application/config/profiler.php b/application/config/profiler.php deleted file mode 100755 index f8a5b1a1..00000000 --- a/application/config/profiler.php +++ /dev/null @@ -1,17 +0,0 @@ - array('grin.gif', '19', '19', 'grin'), - ':lol:' => array('lol.gif', '19', '19', 'LOL'), - ':cheese:' => array('cheese.gif', '19', '19', 'cheese'), - ':)' => array('smile.gif', '19', '19', 'smile'), - ';-)' => array('wink.gif', '19', '19', 'wink'), - ';)' => array('wink.gif', '19', '19', 'wink'), - ':smirk:' => array('smirk.gif', '19', '19', 'smirk'), - ':roll:' => array('rolleyes.gif', '19', '19', 'rolleyes'), - ':-S' => array('confused.gif', '19', '19', 'confused'), - ':wow:' => array('surprise.gif', '19', '19', 'surprised'), - ':bug:' => array('bigsurprise.gif', '19', '19', 'big surprise'), - ':-P' => array('tongue_laugh.gif', '19', '19', 'tongue laugh'), - '%-P' => array('tongue_rolleye.gif', '19', '19', 'tongue rolleye'), - ';-P' => array('tongue_wink.gif', '19', '19', 'tongue wink'), - ':P' => array('raspberry.gif', '19', '19', 'raspberry'), - ':blank:' => array('blank.gif', '19', '19', 'blank stare'), - ':long:' => array('longface.gif', '19', '19', 'long face'), - ':ohh:' => array('ohh.gif', '19', '19', 'ohh'), - ':grrr:' => array('grrr.gif', '19', '19', 'grrr'), - ':gulp:' => array('gulp.gif', '19', '19', 'gulp'), - '8-/' => array('ohoh.gif', '19', '19', 'oh oh'), - ':down:' => array('downer.gif', '19', '19', 'downer'), - ':red:' => array('embarrassed.gif', '19', '19', 'red face'), - ':sick:' => array('sick.gif', '19', '19', 'sick'), - ':shut:' => array('shuteye.gif', '19', '19', 'shut eye'), - ':-/' => array('hmm.gif', '19', '19', 'hmmm'), - '>:(' => array('mad.gif', '19', '19', 'mad'), - ':mad:' => array('mad.gif', '19', '19', 'mad'), - '>:-(' => array('angry.gif', '19', '19', 'angry'), - ':angry:' => array('angry.gif', '19', '19', 'angry'), - ':zip:' => array('zip.gif', '19', '19', 'zipper'), - ':kiss:' => array('kiss.gif', '19', '19', 'kiss'), - ':ahhh:' => array('shock.gif', '19', '19', 'shock'), - ':coolsmile:' => array('shade_smile.gif', '19', '19', 'cool smile'), - ':coolsmirk:' => array('shade_smirk.gif', '19', '19', 'cool smirk'), - ':coolgrin:' => array('shade_grin.gif', '19', '19', 'cool grin'), - ':coolhmm:' => array('shade_hmm.gif', '19', '19', 'cool hmm'), - ':coolmad:' => array('shade_mad.gif', '19', '19', 'cool mad'), - ':coolcheese:' => array('shade_cheese.gif', '19', '19', 'cool cheese'), - ':vampire:' => array('vampire.gif', '19', '19', 'vampire'), - ':snake:' => array('snake.gif', '19', '19', 'snake'), - ':exclaim:' => array('exclaim.gif', '19', '19', 'excaim'), - ':question:' => array('question.gif', '19', '19', 'question') // no comma after last item - - ); - -/* End of file smileys.php */ -/* Location: ./application/config/smileys.php */ \ No newline at end of file diff --git a/application/config/user_agents.php b/application/config/user_agents.php deleted file mode 100644 index e2d3c3af..00000000 --- a/application/config/user_agents.php +++ /dev/null @@ -1,178 +0,0 @@ - 'Windows Longhorn', - 'windows nt 5.2' => 'Windows 2003', - 'windows nt 5.0' => 'Windows 2000', - 'windows nt 5.1' => 'Windows XP', - 'windows nt 4.0' => 'Windows NT 4.0', - 'winnt4.0' => 'Windows NT 4.0', - 'winnt 4.0' => 'Windows NT', - 'winnt' => 'Windows NT', - 'windows 98' => 'Windows 98', - 'win98' => 'Windows 98', - 'windows 95' => 'Windows 95', - 'win95' => 'Windows 95', - 'windows' => 'Unknown Windows OS', - 'os x' => 'Mac OS X', - 'ppc mac' => 'Power PC Mac', - 'freebsd' => 'FreeBSD', - 'ppc' => 'Macintosh', - 'linux' => 'Linux', - 'debian' => 'Debian', - 'sunos' => 'Sun Solaris', - 'beos' => 'BeOS', - 'apachebench' => 'ApacheBench', - 'aix' => 'AIX', - 'irix' => 'Irix', - 'osf' => 'DEC OSF', - 'hp-ux' => 'HP-UX', - 'netbsd' => 'NetBSD', - 'bsdi' => 'BSDi', - 'openbsd' => 'OpenBSD', - 'gnu' => 'GNU/Linux', - 'unix' => 'Unknown Unix OS' - ); - - -// The order of this array should NOT be changed. Many browsers return -// multiple browser types so we want to identify the sub-type first. -$browsers = array( - 'Flock' => 'Flock', - 'Chrome' => 'Chrome', - 'Opera' => 'Opera', - 'MSIE' => 'Internet Explorer', - 'Internet Explorer' => 'Internet Explorer', - 'Shiira' => 'Shiira', - 'Firefox' => 'Firefox', - 'Chimera' => 'Chimera', - 'Phoenix' => 'Phoenix', - 'Firebird' => 'Firebird', - 'Camino' => 'Camino', - 'Netscape' => 'Netscape', - 'OmniWeb' => 'OmniWeb', - 'Safari' => 'Safari', - 'Mozilla' => 'Mozilla', - 'Konqueror' => 'Konqueror', - 'icab' => 'iCab', - 'Lynx' => 'Lynx', - 'Links' => 'Links', - 'hotjava' => 'HotJava', - 'amaya' => 'Amaya', - 'IBrowse' => 'IBrowse' - ); - -$mobiles = array( - // legacy array, old values commented out - 'mobileexplorer' => 'Mobile Explorer', -// 'openwave' => 'Open Wave', -// 'opera mini' => 'Opera Mini', -// 'operamini' => 'Opera Mini', -// 'elaine' => 'Palm', - 'palmsource' => 'Palm', -// 'digital paths' => 'Palm', -// 'avantgo' => 'Avantgo', -// 'xiino' => 'Xiino', - 'palmscape' => 'Palmscape', -// 'nokia' => 'Nokia', -// 'ericsson' => 'Ericsson', -// 'blackberry' => 'BlackBerry', -// 'motorola' => 'Motorola' - - // Phones and Manufacturers - 'motorola' => "Motorola", - 'nokia' => "Nokia", - 'palm' => "Palm", - 'iphone' => "Apple iPhone", - 'ipad' => "iPad", - 'ipod' => "Apple iPod Touch", - 'sony' => "Sony Ericsson", - 'ericsson' => "Sony Ericsson", - 'blackberry' => "BlackBerry", - 'cocoon' => "O2 Cocoon", - 'blazer' => "Treo", - 'lg' => "LG", - 'amoi' => "Amoi", - 'xda' => "XDA", - 'mda' => "MDA", - 'vario' => "Vario", - 'htc' => "HTC", - 'samsung' => "Samsung", - 'sharp' => "Sharp", - 'sie-' => "Siemens", - 'alcatel' => "Alcatel", - 'benq' => "BenQ", - 'ipaq' => "HP iPaq", - 'mot-' => "Motorola", - 'playstation portable' => "PlayStation Portable", - 'hiptop' => "Danger Hiptop", - 'nec-' => "NEC", - 'panasonic' => "Panasonic", - 'philips' => "Philips", - 'sagem' => "Sagem", - 'sanyo' => "Sanyo", - 'spv' => "SPV", - 'zte' => "ZTE", - 'sendo' => "Sendo", - - // Operating Systems - 'symbian' => "Symbian", - 'SymbianOS' => "SymbianOS", - 'elaine' => "Palm", - 'palm' => "Palm", - 'series60' => "Symbian S60", - 'windows ce' => "Windows CE", - - // Browsers - 'obigo' => "Obigo", - 'netfront' => "Netfront Browser", - 'openwave' => "Openwave Browser", - 'mobilexplorer' => "Mobile Explorer", - 'operamini' => "Opera Mini", - 'opera mini' => "Opera Mini", - - // Other - 'digital paths' => "Digital Paths", - 'avantgo' => "AvantGo", - 'xiino' => "Xiino", - 'novarra' => "Novarra Transcoder", - 'vodafone' => "Vodafone", - 'docomo' => "NTT DoCoMo", - 'o2' => "O2", - - // Fallback - 'mobile' => "Generic Mobile", - 'wireless' => "Generic Mobile", - 'j2me' => "Generic Mobile", - 'midp' => "Generic Mobile", - 'cldc' => "Generic Mobile", - 'up.link' => "Generic Mobile", - 'up.browser' => "Generic Mobile", - 'smartphone' => "Generic Mobile", - 'cellphone' => "Generic Mobile" - ); - -// There are hundreds of bots but these are the most common. -$robots = array( - 'googlebot' => 'Googlebot', - 'msnbot' => 'MSNBot', - 'slurp' => 'Inktomi Slurp', - 'yahoo' => 'Yahoo', - 'askjeeves' => 'AskJeeves', - 'fastcrawler' => 'FastCrawler', - 'infoseek' => 'InfoSeek Robot 1.0', - 'lycos' => 'Lycos' - ); - -/* End of file user_agents.php */ -/* Location: ./application/config/user_agents.php */ \ No newline at end of file diff --git a/application/controllers/unit_tests/wildcard_test_cases.php b/application/controllers/unit_tests/wildcard_test_cases.php deleted file mode 100644 index 86a6497c..00000000 --- a/application/controllers/unit_tests/wildcard_test_cases.php +++ /dev/null @@ -1,37 +0,0 @@ -config->load('rest'); - $this->config->set_item('rest_auth', 'none');//turn on rest auth - $this->config->set_item('auth_source', '');//use config array for authentication - $this->config->set_item('auth_override_class_method', array('wildcard_test_cases' => array('*' => 'basic'))); - $this->load->helper('url'); - } - - - function digest_get(){ - $this->response("welcome", 200); - } -} -?> \ No newline at end of file diff --git a/application/controllers/unit_tests/wildcard_test_harness.php b/application/controllers/unit_tests/wildcard_test_harness.php deleted file mode 100644 index d21dd578..00000000 --- a/application/controllers/unit_tests/wildcard_test_harness.php +++ /dev/null @@ -1,67 +0,0 @@ -load->library('unit_test'); - $this->load->helper('url'); - } - - //curl interface functions - private function makeRequest($url, $cred = '', $curlopts = array()){ - $ch = curl_init($url); - $items = array( - CURLOPT_URL => $url, - CURLOPT_USERPWD => $cred - ); - foreach($curlopts as $opt => $value) - $items[$opt] = $value; - curl_setopt_array($ch, $items); - ob_start(); - $response = curl_exec($ch); - $contents = ob_get_contents(); - ob_end_clean(); - $info = curl_getinfo($ch); - - $errno = curl_errno($ch); - $error = curl_error($ch); - curl_close($ch); - return array('response' => $response, 'contents' => $contents, 'errno' => $errno, 'error' => $error, 'info' => $info);//return - } - - /* - These two test cases will test if the authentication is working for the wildcard method. The curl requests may not work if you do not have an .htaccess file with mod rewrite in the same directory as your index.php file. If you don't have that file you can add it or change the url below to the one that includes index.php. - */ - function index(){ - - //not authorized - //no htaccess: $test = $this->makeRequest(base_url() . 'index.php/unit_tests/wildcard_test_cases/digest', ''); - $test = $this->makeRequest(base_url() . 'unit_tests/wildcard_test_cases/digest', ''); - // print_r($test); - $this->unit->run($test['info']['http_code'], '401', 'Not Authorized test (No credentials provided)'); - //no htaccess: $test = $this->makeRequest(base_url() . 'index.php/unit_tests/wildcard_test_cases/digest', 'admin:1234'); - $test = $this->makeRequest(base_url() . 'unit_tests/wildcard_test_cases/digest', 'admin:1234'); - //print_r($test); - $this->unit->run($test['info']['http_code'], '200', 'Authorized, credentials given'); - echo $this->unit->report(); - } -} -?> \ No newline at end of file diff --git a/application/controllers/welcome.php b/application/controllers/welcome.php deleted file mode 100644 index 57151c6e..00000000 --- a/application/controllers/welcome.php +++ /dev/null @@ -1,18 +0,0 @@ -load->helper('url'); - $this->load->view('welcome_message'); - } -} - -/* End of file welcome.php */ -/* Location: ./system/application/controllers/welcome.php */ \ No newline at end of file diff --git a/application/errors/error_404.php b/application/errors/error_404.php deleted file mode 100755 index a304f456..00000000 --- a/application/errors/error_404.php +++ /dev/null @@ -1,34 +0,0 @@ - - -404 Page Not Found - - - -
-

- -
- - \ No newline at end of file diff --git a/application/errors/error_db.php b/application/errors/error_db.php deleted file mode 100755 index 181de960..00000000 --- a/application/errors/error_db.php +++ /dev/null @@ -1,34 +0,0 @@ - - -Database Error - - - -
-

- -
- - \ No newline at end of file diff --git a/application/errors/error_general.php b/application/errors/error_general.php deleted file mode 100755 index 7734d34c..00000000 --- a/application/errors/error_general.php +++ /dev/null @@ -1,34 +0,0 @@ - - -Error - - - -
-

- -
- - \ No newline at end of file diff --git a/application/errors/error_php.php b/application/errors/error_php.php deleted file mode 100755 index f085c203..00000000 --- a/application/errors/error_php.php +++ /dev/null @@ -1,10 +0,0 @@ -
- -

A PHP Error was encountered

- -

Severity:

-

Message:

-

Filename:

-

Line Number:

- -
\ No newline at end of file diff --git a/application/errors/index.html b/application/errors/index.html deleted file mode 100755 index c942a79c..00000000 --- a/application/errors/index.html +++ /dev/null @@ -1,10 +0,0 @@ - - - 403 Forbidden - - - -

Directory access is forbidden.

- - - \ No newline at end of file diff --git a/application/helpers/index.html b/application/helpers/index.html deleted file mode 100755 index c942a79c..00000000 --- a/application/helpers/index.html +++ /dev/null @@ -1,10 +0,0 @@ - - - 403 Forbidden - - - -

Directory access is forbidden.

- - - \ No newline at end of file diff --git a/application/hooks/index.html b/application/hooks/index.html deleted file mode 100755 index c942a79c..00000000 --- a/application/hooks/index.html +++ /dev/null @@ -1,10 +0,0 @@ - - - 403 Forbidden - - - -

Directory access is forbidden.

- - - \ No newline at end of file diff --git a/application/language/english/index.html b/application/language/english/index.html deleted file mode 100755 index c942a79c..00000000 --- a/application/language/english/index.html +++ /dev/null @@ -1,10 +0,0 @@ - - - 403 Forbidden - - - -

Directory access is forbidden.

- - - \ No newline at end of file diff --git a/application/logs/index.html b/application/logs/index.html deleted file mode 100755 index c942a79c..00000000 --- a/application/logs/index.html +++ /dev/null @@ -1,10 +0,0 @@ - - - 403 Forbidden - - - -

Directory access is forbidden.

- - - \ No newline at end of file diff --git a/application/models/index.html b/application/models/index.html deleted file mode 100755 index c942a79c..00000000 --- a/application/models/index.html +++ /dev/null @@ -1,10 +0,0 @@ - - - 403 Forbidden - - - -

Directory access is forbidden.

- - - \ No newline at end of file diff --git a/application/third_party/index.html b/application/third_party/index.html deleted file mode 100755 index c942a79c..00000000 --- a/application/third_party/index.html +++ /dev/null @@ -1,10 +0,0 @@ - - - 403 Forbidden - - - -

Directory access is forbidden.

- - - \ No newline at end of file diff --git a/application/views/index.html b/application/views/index.html deleted file mode 100755 index c942a79c..00000000 --- a/application/views/index.html +++ /dev/null @@ -1,10 +0,0 @@ - - - 403 Forbidden - - - -

Directory access is forbidden.

- - - \ No newline at end of file diff --git a/application/views/welcome_message.php b/application/views/welcome_message.php deleted file mode 100755 index 2c8f9e29..00000000 --- a/application/views/welcome_message.php +++ /dev/null @@ -1,89 +0,0 @@ - - - - - Welcome to CodeIgniter - - - - - -

Welcome to CodeIgniter!

- -

The page you are looking at is being generated dynamically by CodeIgniter.

- -
    -
  • Users - defaulting to XML
  • -
  • Users - get it in CSV
  • -
  • User #1 - defaulting to XML
  • -
  • User #1 - get it in JSON
  • -
  • Users - get it in JSON (AJAX request)
  • -
- -

If you are exploring CodeIgniter for the very first time, you should start by reading the User Guide.

- -


Page rendered in {elapsed_time} seconds

- - - - - - \ No newline at end of file diff --git a/index.php b/index.php deleted file mode 100644 index f4ac11a7..00000000 --- a/index.php +++ /dev/null @@ -1,205 +0,0 @@ -marker[$name] = microtime(); - } - - // -------------------------------------------------------------------- - - /** - * Calculates the time difference between two marked points. - * - * If the first parameter is empty this function instead returns the - * {elapsed_time} pseudo-variable. This permits the full system - * execution time to be shown in a template. The output class will - * swap the real value for this variable. - * - * @access public - * @param string a particular marked point - * @param string a particular marked point - * @param integer the number of decimal places - * @return mixed - */ - function elapsed_time($point1 = '', $point2 = '', $decimals = 4) - { - if ($point1 == '') - { - return '{elapsed_time}'; - } - - if ( ! isset($this->marker[$point1])) - { - return ''; - } - - if ( ! isset($this->marker[$point2])) - { - $this->marker[$point2] = microtime(); - } - - list($sm, $ss) = explode(' ', $this->marker[$point1]); - list($em, $es) = explode(' ', $this->marker[$point2]); - - return number_format(($em + $es) - ($sm + $ss), $decimals); - } - - // -------------------------------------------------------------------- - - /** - * Memory Usage - * - * This function returns the {memory_usage} pseudo-variable. - * This permits it to be put it anywhere in a template - * without the memory being calculated until the end. - * The output class will swap the real value for this variable. - * - * @access public - * @return string - */ - function memory_usage() - { - return '{memory_usage}'; - } - -} - -// END CI_Benchmark class - -/* End of file Benchmark.php */ -/* Location: ./system/core/Benchmark.php */ \ No newline at end of file diff --git a/system/core/CodeIgniter.php b/system/core/CodeIgniter.php deleted file mode 100755 index c16c79c0..00000000 --- a/system/core/CodeIgniter.php +++ /dev/null @@ -1,402 +0,0 @@ - $assign_to_config['subclass_prefix'])); - } - -/* - * ------------------------------------------------------ - * Set a liberal script execution time limit - * ------------------------------------------------------ - */ - if (function_exists("set_time_limit") == TRUE AND @ini_get("safe_mode") == 0) - { - @set_time_limit(300); - } - -/* - * ------------------------------------------------------ - * Start the timer... tick tock tick tock... - * ------------------------------------------------------ - */ - $BM =& load_class('Benchmark', 'core'); - $BM->mark('total_execution_time_start'); - $BM->mark('loading_time:_base_classes_start'); - -/* - * ------------------------------------------------------ - * Instantiate the hooks class - * ------------------------------------------------------ - */ - $EXT =& load_class('Hooks', 'core'); - -/* - * ------------------------------------------------------ - * Is there a "pre_system" hook? - * ------------------------------------------------------ - */ - $EXT->_call_hook('pre_system'); - -/* - * ------------------------------------------------------ - * Instantiate the config class - * ------------------------------------------------------ - */ - $CFG =& load_class('Config', 'core'); - - // Do we have any manually set config items in the index.php file? - if (isset($assign_to_config)) - { - $CFG->_assign_to_config($assign_to_config); - } - -/* - * ------------------------------------------------------ - * Instantiate the UTF-8 class - * ------------------------------------------------------ - * - * Note: Order here is rather important as the UTF-8 - * class needs to be used very early on, but it cannot - * properly determine if UTf-8 can be supported until - * after the Config class is instantiated. - * - */ - - $UNI =& load_class('Utf8', 'core'); - -/* - * ------------------------------------------------------ - * Instantiate the URI class - * ------------------------------------------------------ - */ - $URI =& load_class('URI', 'core'); - -/* - * ------------------------------------------------------ - * Instantiate the routing class and set the routing - * ------------------------------------------------------ - */ - $RTR =& load_class('Router', 'core'); - $RTR->_set_routing(); - - // Set any routing overrides that may exist in the main index file - if (isset($routing)) - { - $RTR->_set_overrides($routing); - } - -/* - * ------------------------------------------------------ - * Instantiate the output class - * ------------------------------------------------------ - */ - $OUT =& load_class('Output', 'core'); - -/* - * ------------------------------------------------------ - * Is there a valid cache file? If so, we're done... - * ------------------------------------------------------ - */ - if ($EXT->_call_hook('cache_override') === FALSE) - { - if ($OUT->_display_cache($CFG, $URI) == TRUE) - { - exit; - } - } - -/* - * ----------------------------------------------------- - * Load the security class for xss and csrf support - * ----------------------------------------------------- - */ - $SEC =& load_class('Security', 'core'); - -/* - * ------------------------------------------------------ - * Load the Input class and sanitize globals - * ------------------------------------------------------ - */ - $IN =& load_class('Input', 'core'); - -/* - * ------------------------------------------------------ - * Load the Language class - * ------------------------------------------------------ - */ - $LANG =& load_class('Lang', 'core'); - -/* - * ------------------------------------------------------ - * Load the app controller and local controller - * ------------------------------------------------------ - * - */ - // Load the base controller class - require BASEPATH.'core/Controller.php'; - - function &get_instance() - { - return CI_Controller::get_instance(); - } - - - if (file_exists(APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php')) - { - require APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php'; - } - - // Load the local application controller - // Note: The Router class automatically validates the controller path using the router->_validate_request(). - // If this include fails it means that the default controller in the Routes.php file is not resolving to something valid. - if ( ! file_exists(APPPATH.'controllers/'.$RTR->fetch_directory().$RTR->fetch_class().'.php')) - { - show_error('Unable to load your default controller. Please make sure the controller specified in your Routes.php file is valid.'); - } - - include(APPPATH.'controllers/'.$RTR->fetch_directory().$RTR->fetch_class().'.php'); - - // Set a mark point for benchmarking - $BM->mark('loading_time:_base_classes_end'); - -/* - * ------------------------------------------------------ - * Security check - * ------------------------------------------------------ - * - * None of the functions in the app controller or the - * loader class can be called via the URI, nor can - * controller functions that begin with an underscore - */ - $class = $RTR->fetch_class(); - $method = $RTR->fetch_method(); - - if ( ! class_exists($class) - OR strncmp($method, '_', 1) == 0 - OR in_array(strtolower($method), array_map('strtolower', get_class_methods('CI_Controller'))) - ) - { - if ( ! empty($RTR->routes['404_override'])) - { - $x = explode('/', $RTR->routes['404_override']); - $class = $x[0]; - $method = (isset($x[1]) ? $x[1] : 'index'); - if ( ! class_exists($class)) - { - if ( ! file_exists(APPPATH.'controllers/'.$class.'.php')) - { - show_404("{$class}/{$method}"); - } - - include_once(APPPATH.'controllers/'.$class.'.php'); - } - } - else - { - show_404("{$class}/{$method}"); - } - } - -/* - * ------------------------------------------------------ - * Is there a "pre_controller" hook? - * ------------------------------------------------------ - */ - $EXT->_call_hook('pre_controller'); - -/* - * ------------------------------------------------------ - * Instantiate the requested controller - * ------------------------------------------------------ - */ - // Mark a start point so we can benchmark the controller - $BM->mark('controller_execution_time_( '.$class.' / '.$method.' )_start'); - - $CI = new $class(); - -/* - * ------------------------------------------------------ - * Is there a "post_controller_constructor" hook? - * ------------------------------------------------------ - */ - $EXT->_call_hook('post_controller_constructor'); - -/* - * ------------------------------------------------------ - * Call the requested method - * ------------------------------------------------------ - */ - // Is there a "remap" function? If so, we call it instead - if (method_exists($CI, '_remap')) - { - $CI->_remap($method, array_slice($URI->rsegments, 2)); - } - else - { - // is_callable() returns TRUE on some versions of PHP 5 for private and protected - // methods, so we'll use this workaround for consistent behavior - if ( ! in_array(strtolower($method), array_map('strtolower', get_class_methods($CI)))) - { - // Check and see if we are using a 404 override and use it. - if ( ! empty($RTR->routes['404_override'])) - { - $x = explode('/', $RTR->routes['404_override']); - $class = $x[0]; - $method = (isset($x[1]) ? $x[1] : 'index'); - if ( ! class_exists($class)) - { - if ( ! file_exists(APPPATH.'controllers/'.$class.'.php')) - { - show_404("{$class}/{$method}"); - } - - include_once(APPPATH.'controllers/'.$class.'.php'); - unset($CI); - $CI = new $class(); - } - } - else - { - show_404("{$class}/{$method}"); - } - } - - // Call the requested method. - // Any URI segments present (besides the class/function) will be passed to the method for convenience - call_user_func_array(array(&$CI, $method), array_slice($URI->rsegments, 2)); - } - - - // Mark a benchmark end point - $BM->mark('controller_execution_time_( '.$class.' / '.$method.' )_end'); - -/* - * ------------------------------------------------------ - * Is there a "post_controller" hook? - * ------------------------------------------------------ - */ - $EXT->_call_hook('post_controller'); - -/* - * ------------------------------------------------------ - * Send the final rendered output to the browser - * ------------------------------------------------------ - */ - if ($EXT->_call_hook('display_override') === FALSE) - { - $OUT->_display(); - } - -/* - * ------------------------------------------------------ - * Is there a "post_system" hook? - * ------------------------------------------------------ - */ - $EXT->_call_hook('post_system'); - -/* - * ------------------------------------------------------ - * Close the DB connection if one exists - * ------------------------------------------------------ - */ - if (class_exists('CI_DB') AND isset($CI->db)) - { - $CI->db->close(); - } - - -/* End of file CodeIgniter.php */ -/* Location: ./system/core/CodeIgniter.php */ \ No newline at end of file diff --git a/system/core/Common.php b/system/core/Common.php deleted file mode 100644 index 07534c51..00000000 --- a/system/core/Common.php +++ /dev/null @@ -1,564 +0,0 @@ - 5 -* we'll set a static variable. -* -* @access public -* @param string -* @return bool TRUE if the current version is $version or higher -*/ -if ( ! function_exists('is_php')) -{ - function is_php($version = '5.0.0') - { - static $_is_php; - $version = (string)$version; - - if ( ! isset($_is_php[$version])) - { - $_is_php[$version] = (version_compare(PHP_VERSION, $version) < 0) ? FALSE : TRUE; - } - - return $_is_php[$version]; - } -} - -// ------------------------------------------------------------------------ - -/** - * Tests for file writability - * - * is_writable() returns TRUE on Windows servers when you really can't write to - * the file, based on the read-only attribute. is_writable() is also unreliable - * on Unix servers if safe_mode is on. - * - * @access private - * @return void - */ -if ( ! function_exists('is_really_writable')) -{ - function is_really_writable($file) - { - // If we're on a Unix server with safe_mode off we call is_writable - if (DIRECTORY_SEPARATOR == '/' AND @ini_get("safe_mode") == FALSE) - { - return is_writable($file); - } - - // For windows servers and safe_mode "on" installations we'll actually - // write a file then read it. Bah... - if (is_dir($file)) - { - $file = rtrim($file, '/').'/'.md5(mt_rand(1,100).mt_rand(1,100)); - - if (($fp = @fopen($file, FOPEN_WRITE_CREATE)) === FALSE) - { - return FALSE; - } - - fclose($fp); - @chmod($file, DIR_WRITE_MODE); - @unlink($file); - return TRUE; - } - elseif ( ! is_file($file) OR ($fp = @fopen($file, FOPEN_WRITE_CREATE)) === FALSE) - { - return FALSE; - } - - fclose($fp); - return TRUE; - } -} - -// ------------------------------------------------------------------------ - -/** -* Class registry -* -* This function acts as a singleton. If the requested class does not -* exist it is instantiated and set to a static variable. If it has -* previously been instantiated the variable is returned. -* -* @access public -* @param string the class name being requested -* @param string the directory where the class should be found -* @param string the class name prefix -* @return object -*/ -if ( ! function_exists('load_class')) -{ - function &load_class($class, $directory = 'libraries', $prefix = 'CI_') - { - static $_classes = array(); - - // Does the class exist? If so, we're done... - if (isset($_classes[$class])) - { - return $_classes[$class]; - } - - $name = FALSE; - - // Look for the class first in the local application/libraries folder - // then in the native system/libraries folder - foreach (array(APPPATH, BASEPATH) as $path) - { - if (file_exists($path.$directory.'/'.$class.'.php')) - { - $name = $prefix.$class; - - if (class_exists($name) === FALSE) - { - require($path.$directory.'/'.$class.'.php'); - } - - break; - } - } - - // Is the request a class extension? If so we load it too - if (file_exists(APPPATH.$directory.'/'.config_item('subclass_prefix').$class.'.php')) - { - $name = config_item('subclass_prefix').$class; - - if (class_exists($name) === FALSE) - { - require(APPPATH.$directory.'/'.config_item('subclass_prefix').$class.'.php'); - } - } - - // Did we find the class? - if ($name === FALSE) - { - // Note: We use exit() rather then show_error() in order to avoid a - // self-referencing loop with the Excptions class - exit('Unable to locate the specified class: '.$class.'.php'); - } - - // Keep track of what we just loaded - is_loaded($class); - - $_classes[$class] = new $name(); - return $_classes[$class]; - } -} - -// -------------------------------------------------------------------- - -/** -* Keeps track of which libraries have been loaded. This function is -* called by the load_class() function above -* -* @access public -* @return array -*/ -if ( ! function_exists('is_loaded')) -{ - function &is_loaded($class = '') - { - static $_is_loaded = array(); - - if ($class != '') - { - $_is_loaded[strtolower($class)] = $class; - } - - return $_is_loaded; - } -} - -// ------------------------------------------------------------------------ - -/** -* Loads the main config.php file -* -* This function lets us grab the config file even if the Config class -* hasn't been instantiated yet -* -* @access private -* @return array -*/ -if ( ! function_exists('get_config')) -{ - function &get_config($replace = array()) - { - static $_config; - - if (isset($_config)) - { - return $_config[0]; - } - - // Is the config file in the environment folder? - if ( ! defined('ENVIRONMENT') OR ! file_exists($file_path = APPPATH.'config/'.ENVIRONMENT.'/config.php')) - { - $file_path = APPPATH.'config/config.php'; - } - - // Fetch the config file - if ( ! file_exists($file_path)) - { - exit('The configuration file does not exist.'); - } - - require($file_path); - - // Does the $config array exist in the file? - if ( ! isset($config) OR ! is_array($config)) - { - exit('Your config file does not appear to be formatted correctly.'); - } - - // Are any values being dynamically replaced? - if (count($replace) > 0) - { - foreach ($replace as $key => $val) - { - if (isset($config[$key])) - { - $config[$key] = $val; - } - } - } - - return $_config[0] =& $config; - } -} - -// ------------------------------------------------------------------------ - -/** -* Returns the specified config item -* -* @access public -* @return mixed -*/ -if ( ! function_exists('config_item')) -{ - function config_item($item) - { - static $_config_item = array(); - - if ( ! isset($_config_item[$item])) - { - $config =& get_config(); - - if ( ! isset($config[$item])) - { - return FALSE; - } - $_config_item[$item] = $config[$item]; - } - - return $_config_item[$item]; - } -} - -// ------------------------------------------------------------------------ - -/** -* Error Handler -* -* This function lets us invoke the exception class and -* display errors using the standard error template located -* in application/errors/errors.php -* This function will send the error page directly to the -* browser and exit. -* -* @access public -* @return void -*/ -if ( ! function_exists('show_error')) -{ - function show_error($message, $status_code = 500, $heading = 'An Error Was Encountered') - { - $_error =& load_class('Exceptions', 'core'); - echo $_error->show_error($heading, $message, 'error_general', $status_code); - exit; - } -} - -// ------------------------------------------------------------------------ - -/** -* 404 Page Handler -* -* This function is similar to the show_error() function above -* However, instead of the standard error template it displays -* 404 errors. -* -* @access public -* @return void -*/ -if ( ! function_exists('show_404')) -{ - function show_404($page = '', $log_error = TRUE) - { - $_error =& load_class('Exceptions', 'core'); - $_error->show_404($page, $log_error); - exit; - } -} - -// ------------------------------------------------------------------------ - -/** -* Error Logging Interface -* -* We use this as a simple mechanism to access the logging -* class and send messages to be logged. -* -* @access public -* @return void -*/ -if ( ! function_exists('log_message')) -{ - function log_message($level = 'error', $message, $php_error = FALSE) - { - static $_log; - - if (config_item('log_threshold') == 0) - { - return; - } - - $_log =& load_class('Log'); - $_log->write_log($level, $message, $php_error); - } -} - -// ------------------------------------------------------------------------ - -/** - * Set HTTP Status Header - * - * @access public - * @param int the status code - * @param string - * @return void - */ -if ( ! function_exists('set_status_header')) -{ - function set_status_header($code = 200, $text = '') - { - $stati = array( - 200 => 'OK', - 201 => 'Created', - 202 => 'Accepted', - 203 => 'Non-Authoritative Information', - 204 => 'No Content', - 205 => 'Reset Content', - 206 => 'Partial Content', - - 300 => 'Multiple Choices', - 301 => 'Moved Permanently', - 302 => 'Found', - 304 => 'Not Modified', - 305 => 'Use Proxy', - 307 => 'Temporary Redirect', - - 400 => 'Bad Request', - 401 => 'Unauthorized', - 403 => 'Forbidden', - 404 => 'Not Found', - 405 => 'Method Not Allowed', - 406 => 'Not Acceptable', - 407 => 'Proxy Authentication Required', - 408 => 'Request Timeout', - 409 => 'Conflict', - 410 => 'Gone', - 411 => 'Length Required', - 412 => 'Precondition Failed', - 413 => 'Request Entity Too Large', - 414 => 'Request-URI Too Long', - 415 => 'Unsupported Media Type', - 416 => 'Requested Range Not Satisfiable', - 417 => 'Expectation Failed', - - 500 => 'Internal Server Error', - 501 => 'Not Implemented', - 502 => 'Bad Gateway', - 503 => 'Service Unavailable', - 504 => 'Gateway Timeout', - 505 => 'HTTP Version Not Supported' - ); - - if ($code == '' OR ! is_numeric($code)) - { - show_error('Status codes must be numeric', 500); - } - - if (isset($stati[$code]) AND $text == '') - { - $text = $stati[$code]; - } - - if ($text == '') - { - show_error('No status text available. Please check your status code number or supply your own message text.', 500); - } - - $server_protocol = (isset($_SERVER['SERVER_PROTOCOL'])) ? $_SERVER['SERVER_PROTOCOL'] : FALSE; - - if (substr(php_sapi_name(), 0, 3) == 'cgi') - { - header("Status: {$code} {$text}", TRUE); - } - elseif ($server_protocol == 'HTTP/1.1' OR $server_protocol == 'HTTP/1.0') - { - header($server_protocol." {$code} {$text}", TRUE, $code); - } - else - { - header("HTTP/1.1 {$code} {$text}", TRUE, $code); - } - } -} - -// -------------------------------------------------------------------- - -/** -* Exception Handler -* -* This is the custom exception handler that is declaired at the top -* of Codeigniter.php. The main reason we use this is to permit -* PHP errors to be logged in our own log files since the user may -* not have access to server logs. Since this function -* effectively intercepts PHP errors, however, we also need -* to display errors based on the current error_reporting level. -* We do that with the use of a PHP error template. -* -* @access private -* @return void -*/ -if ( ! function_exists('_exception_handler')) -{ - function _exception_handler($severity, $message, $filepath, $line) - { - // We don't bother with "strict" notices since they tend to fill up - // the log file with excess information that isn't normally very helpful. - // For example, if you are running PHP 5 and you use version 4 style - // class functions (without prefixes like "public", "private", etc.) - // you'll get notices telling you that these have been deprecated. - if ($severity == E_STRICT) - { - return; - } - - $_error =& load_class('Exceptions', 'core'); - - // Should we display the error? We'll get the current error_reporting - // level and add its bits with the severity bits to find out. - if (($severity & error_reporting()) == $severity) - { - $_error->show_php_error($severity, $message, $filepath, $line); - } - - // Should we log the error? No? We're done... - if (config_item('log_threshold') == 0) - { - return; - } - - $_error->log_exception($severity, $message, $filepath, $line); - } -} - -// -------------------------------------------------------------------- - -/** - * Remove Invisible Characters - * - * This prevents sandwiching null characters - * between ascii characters, like Java\0script. - * - * @access public - * @param string - * @return string - */ -if ( ! function_exists('remove_invisible_characters')) -{ - function remove_invisible_characters($str, $url_encoded = TRUE) - { - $non_displayables = array(); - - // every control character except newline (dec 10) - // carriage return (dec 13), and horizontal tab (dec 09) - - if ($url_encoded) - { - $non_displayables[] = '/%0[0-8bcef]/'; // url encoded 00-08, 11, 12, 14, 15 - $non_displayables[] = '/%1[0-9a-f]/'; // url encoded 16-31 - } - - $non_displayables[] = '/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+/S'; // 00-08, 11, 12, 14-31, 127 - - do - { - $str = preg_replace($non_displayables, '', $str, -1, $count); - } - while ($count); - - return $str; - } -} - -// ------------------------------------------------------------------------ - -/** -* Returns HTML escaped variable -* -* @access public -* @param mixed -* @return mixed -*/ -if ( ! function_exists('html_escape')) -{ - function html_escape($var) - { - if (is_array($var)) - { - return array_map('html_escape', $var); - } - else - { - return htmlspecialchars($var, ENT_QUOTES, config_item('charset')); - } - } -} - -/* End of file Common.php */ -/* Location: ./system/core/Common.php */ \ No newline at end of file diff --git a/system/core/Config.php b/system/core/Config.php deleted file mode 100755 index 5dffbf3f..00000000 --- a/system/core/Config.php +++ /dev/null @@ -1,379 +0,0 @@ -config =& get_config(); - log_message('debug', "Config Class Initialized"); - - // Set the base_url automatically if none was provided - if ($this->config['base_url'] == '') - { - if (isset($_SERVER['HTTP_HOST'])) - { - $base_url = isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) !== 'off' ? 'https' : 'http'; - $base_url .= '://'. $_SERVER['HTTP_HOST']; - $base_url .= str_replace(basename($_SERVER['SCRIPT_NAME']), '', $_SERVER['SCRIPT_NAME']); - } - - else - { - $base_url = 'http://localhost/'; - } - - $this->set_item('base_url', $base_url); - } - } - - // -------------------------------------------------------------------- - - /** - * Load Config File - * - * @access public - * @param string the config file name - * @param boolean if configuration values should be loaded into their own section - * @param boolean true if errors should just return false, false if an error message should be displayed - * @return boolean if the file was loaded correctly - */ - function load($file = '', $use_sections = FALSE, $fail_gracefully = FALSE) - { - $file = ($file == '') ? 'config' : str_replace('.php', '', $file); - $found = FALSE; - $loaded = FALSE; - - $check_locations = defined('ENVIRONMENT') - ? array(ENVIRONMENT.'/'.$file, $file) - : array($file); - - foreach ($this->_config_paths as $path) - { - foreach ($check_locations as $location) - { - $file_path = $path.'config/'.$location.'.php'; - - if (in_array($file_path, $this->is_loaded, TRUE)) - { - $loaded = TRUE; - continue 2; - } - - if (file_exists($file_path)) - { - $found = TRUE; - break; - } - } - - if ($found === FALSE) - { - continue; - } - - include($file_path); - - if ( ! isset($config) OR ! is_array($config)) - { - if ($fail_gracefully === TRUE) - { - return FALSE; - } - show_error('Your '.$file_path.' file does not appear to contain a valid configuration array.'); - } - - if ($use_sections === TRUE) - { - if (isset($this->config[$file])) - { - $this->config[$file] = array_merge($this->config[$file], $config); - } - else - { - $this->config[$file] = $config; - } - } - else - { - $this->config = array_merge($this->config, $config); - } - - $this->is_loaded[] = $file_path; - unset($config); - - $loaded = TRUE; - log_message('debug', 'Config file loaded: '.$file_path); - break; - } - - if ($loaded === FALSE) - { - if ($fail_gracefully === TRUE) - { - return FALSE; - } - show_error('The configuration file '.$file.'.php does not exist.'); - } - - return TRUE; - } - - // -------------------------------------------------------------------- - - /** - * Fetch a config file item - * - * - * @access public - * @param string the config item name - * @param string the index name - * @param bool - * @return string - */ - function item($item, $index = '') - { - if ($index == '') - { - if ( ! isset($this->config[$item])) - { - return FALSE; - } - - $pref = $this->config[$item]; - } - else - { - if ( ! isset($this->config[$index])) - { - return FALSE; - } - - if ( ! isset($this->config[$index][$item])) - { - return FALSE; - } - - $pref = $this->config[$index][$item]; - } - - return $pref; - } - - // -------------------------------------------------------------------- - - /** - * Fetch a config file item - adds slash after item (if item is not empty) - * - * @access public - * @param string the config item name - * @param bool - * @return string - */ - function slash_item($item) - { - if ( ! isset($this->config[$item])) - { - return FALSE; - } - if( trim($this->config[$item]) == '') - { - return ''; - } - - return rtrim($this->config[$item], '/').'/'; - } - - // -------------------------------------------------------------------- - - /** - * Site URL - * Returns base_url . index_page [. uri_string] - * - * @access public - * @param string the URI string - * @return string - */ - function site_url(http://github.com/$uri%20=%20'') - { - if ($uri == '') - { - return $this->slash_item('base_url').$this->item('index_page'); - } - - if ($this->item('enable_query_strings') == FALSE) - { - $suffix = ($this->item('url_suffix') == FALSE) ? '' : $this->item('url_suffix'); - return $this->slash_item('base_url').$this->slash_item('index_page').$this->_uri_string($uri).$suffix; - } - else - { - return $this->slash_item('base_url').$this->item('index_page').'?'.$this->_uri_string($uri); - } - } - - // ------------------------------------------------------------- - - /** - * Base URL - * Returns base_url [. uri_string] - * - * @access public - * @param string $uri - * @return string - */ - function base_url(http://github.com/$uri%20=%20'') - { - return $this->slash_item('base_url').ltrim($this->_uri_string($uri), '/'); - } - - // ------------------------------------------------------------- - - /** - * Build URI string for use in Config::site_url() and Config::base_url() - * - * @access protected - * @param $uri - * @return string - */ - protected function _uri_string($uri) - { - if ($this->item('enable_query_strings') == FALSE) - { - if (is_array($uri)) - { - $uri = implode('/', $uri); - } - $uri = trim($uri, '/'); - } - else - { - if (is_array($uri)) - { - $i = 0; - $str = ''; - foreach ($uri as $key => $val) - { - $prefix = ($i == 0) ? '' : '&'; - $str .= $prefix.$key.'='.$val; - $i++; - } - $uri = $str; - } - } - return $uri; - } - - // -------------------------------------------------------------------- - - /** - * System URL - * - * @access public - * @return string - */ - function system_url() - { - $x = explode("/", preg_replace("|/*(.+?)/*$|", "\\1", BASEPATH)); - return $this->slash_item('base_url').end($x).'/'; - } - - // -------------------------------------------------------------------- - - /** - * Set a config file item - * - * @access public - * @param string the config item key - * @param string the config item value - * @return void - */ - function set_item($item, $value) - { - $this->config[$item] = $value; - } - - // -------------------------------------------------------------------- - - /** - * Assign to Config - * - * This function is called by the front controller (CodeIgniter.php) - * after the Config class is instantiated. It permits config items - * to be assigned or overriden by variables contained in the index.php file - * - * @access private - * @param array - * @return void - */ - function _assign_to_config($items = array()) - { - if (is_array($items)) - { - foreach ($items as $key => $val) - { - $this->set_item($key, $val); - } - } - } -} - -// END CI_Config class - -/* End of file Config.php */ -/* Location: ./system/core/Config.php */ diff --git a/system/core/Controller.php b/system/core/Controller.php deleted file mode 100644 index fddb81e1..00000000 --- a/system/core/Controller.php +++ /dev/null @@ -1,64 +0,0 @@ - $class) - { - $this->$var =& load_class($class); - } - - $this->load =& load_class('Loader', 'core'); - - $this->load->initialize(); - - log_message('debug', "Controller Class Initialized"); - } - - public static function &get_instance() - { - return self::$instance; - } -} -// END Controller class - -/* End of file Controller.php */ -/* Location: ./system/core/Controller.php */ \ No newline at end of file diff --git a/system/core/Exceptions.php b/system/core/Exceptions.php deleted file mode 100755 index 869739a5..00000000 --- a/system/core/Exceptions.php +++ /dev/null @@ -1,193 +0,0 @@ - 'Error', - E_WARNING => 'Warning', - E_PARSE => 'Parsing Error', - E_NOTICE => 'Notice', - E_CORE_ERROR => 'Core Error', - E_CORE_WARNING => 'Core Warning', - E_COMPILE_ERROR => 'Compile Error', - E_COMPILE_WARNING => 'Compile Warning', - E_USER_ERROR => 'User Error', - E_USER_WARNING => 'User Warning', - E_USER_NOTICE => 'User Notice', - E_STRICT => 'Runtime Notice' - ); - - - /** - * Constructor - */ - public function __construct() - { - $this->ob_level = ob_get_level(); - // Note: Do not log messages from this constructor. - } - - // -------------------------------------------------------------------- - - /** - * Exception Logger - * - * This function logs PHP generated error messages - * - * @access private - * @param string the error severity - * @param string the error string - * @param string the error filepath - * @param string the error line number - * @return string - */ - function log_exception($severity, $message, $filepath, $line) - { - $severity = ( ! isset($this->levels[$severity])) ? $severity : $this->levels[$severity]; - - log_message('error', 'Severity: '.$severity.' --> '.$message. ' '.$filepath.' '.$line, TRUE); - } - - // -------------------------------------------------------------------- - - /** - * 404 Page Not Found Handler - * - * @access private - * @param string the page - * @param bool log error yes/no - * @return string - */ - function show_404($page = '', $log_error = TRUE) - { - $heading = "404 Page Not Found"; - $message = "The page you requested was not found."; - - // By default we log this, but allow a dev to skip it - if ($log_error) - { - log_message('error', '404 Page Not Found --> '.$page); - } - - echo $this->show_error($heading, $message, 'error_404', 404); - exit; - } - - // -------------------------------------------------------------------- - - /** - * General Error Page - * - * This function takes an error message as input - * (either as a string or an array) and displays - * it using the specified template. - * - * @access private - * @param string the heading - * @param string the message - * @param string the template name - * @param int the status code - * @return string - */ - function show_error($heading, $message, $template = 'error_general', $status_code = 500) - { - set_status_header($status_code); - - $message = '

'.implode('

', ( ! is_array($message)) ? array($message) : $message).'

'; - - if (ob_get_level() > $this->ob_level + 1) - { - ob_end_flush(); - } - ob_start(); - include(APPPATH.'errors/'.$template.'.php'); - $buffer = ob_get_contents(); - ob_end_clean(); - return $buffer; - } - - // -------------------------------------------------------------------- - - /** - * Native PHP error handler - * - * @access private - * @param string the error severity - * @param string the error string - * @param string the error filepath - * @param string the error line number - * @return string - */ - function show_php_error($severity, $message, $filepath, $line) - { - $severity = ( ! isset($this->levels[$severity])) ? $severity : $this->levels[$severity]; - - $filepath = str_replace("\\", "/", $filepath); - - // For safety reasons we do not show the full file path - if (FALSE !== strpos($filepath, '/')) - { - $x = explode('/', $filepath); - $filepath = $x[count($x)-2].'/'.end($x); - } - - if (ob_get_level() > $this->ob_level + 1) - { - ob_end_flush(); - } - ob_start(); - include(APPPATH.'errors/error_php.php'); - $buffer = ob_get_contents(); - ob_end_clean(); - echo $buffer; - } - - -} -// END Exceptions Class - -/* End of file Exceptions.php */ -/* Location: ./system/core/Exceptions.php */ \ No newline at end of file diff --git a/system/core/Hooks.php b/system/core/Hooks.php deleted file mode 100755 index 33f1c034..00000000 --- a/system/core/Hooks.php +++ /dev/null @@ -1,248 +0,0 @@ -_initialize(); - log_message('debug', "Hooks Class Initialized"); - } - - // -------------------------------------------------------------------- - - /** - * Initialize the Hooks Preferences - * - * @access private - * @return void - */ - function _initialize() - { - $CFG =& load_class('Config', 'core'); - - // If hooks are not enabled in the config file - // there is nothing else to do - - if ($CFG->item('enable_hooks') == FALSE) - { - return; - } - - // Grab the "hooks" definition file. - // If there are no hooks, we're done. - - if (defined('ENVIRONMENT') AND is_file(APPPATH.'config/'.ENVIRONMENT.'/hooks.php')) - { - include(APPPATH.'config/'.ENVIRONMENT.'/hooks.php'); - } - elseif (is_file(APPPATH.'config/hooks.php')) - { - include(APPPATH.'config/hooks.php'); - } - - - if ( ! isset($hook) OR ! is_array($hook)) - { - return; - } - - $this->hooks =& $hook; - $this->enabled = TRUE; - } - - // -------------------------------------------------------------------- - - /** - * Call Hook - * - * Calls a particular hook - * - * @access private - * @param string the hook name - * @return mixed - */ - function _call_hook($which = '') - { - if ( ! $this->enabled OR ! isset($this->hooks[$which])) - { - return FALSE; - } - - if (isset($this->hooks[$which][0]) AND is_array($this->hooks[$which][0])) - { - foreach ($this->hooks[$which] as $val) - { - $this->_run_hook($val); - } - } - else - { - $this->_run_hook($this->hooks[$which]); - } - - return TRUE; - } - - // -------------------------------------------------------------------- - - /** - * Run Hook - * - * Runs a particular hook - * - * @access private - * @param array the hook details - * @return bool - */ - function _run_hook($data) - { - if ( ! is_array($data)) - { - return FALSE; - } - - // ----------------------------------- - // Safety - Prevents run-away loops - // ----------------------------------- - - // If the script being called happens to have the same - // hook call within it a loop can happen - - if ($this->in_progress == TRUE) - { - return; - } - - // ----------------------------------- - // Set file path - // ----------------------------------- - - if ( ! isset($data['filepath']) OR ! isset($data['filename'])) - { - return FALSE; - } - - $filepath = APPPATH.$data['filepath'].'/'.$data['filename']; - - if ( ! file_exists($filepath)) - { - return FALSE; - } - - // ----------------------------------- - // Set class/function name - // ----------------------------------- - - $class = FALSE; - $function = FALSE; - $params = ''; - - if (isset($data['class']) AND $data['class'] != '') - { - $class = $data['class']; - } - - if (isset($data['function'])) - { - $function = $data['function']; - } - - if (isset($data['params'])) - { - $params = $data['params']; - } - - if ($class === FALSE AND $function === FALSE) - { - return FALSE; - } - - // ----------------------------------- - // Set the in_progress flag - // ----------------------------------- - - $this->in_progress = TRUE; - - // ----------------------------------- - // Call the requested class and/or function - // ----------------------------------- - - if ($class !== FALSE) - { - if ( ! class_exists($class)) - { - require($filepath); - } - - $HOOK = new $class; - $HOOK->$function($params); - } - else - { - if ( ! function_exists($function)) - { - require($filepath); - } - - $function($params); - } - - $this->in_progress = FALSE; - return TRUE; - } - -} - -// END CI_Hooks class - -/* End of file Hooks.php */ -/* Location: ./system/core/Hooks.php */ \ No newline at end of file diff --git a/system/core/Input.php b/system/core/Input.php deleted file mode 100755 index 0c1f2b08..00000000 --- a/system/core/Input.php +++ /dev/null @@ -1,849 +0,0 @@ -_allow_get_array = (config_item('allow_get_array') === TRUE); - $this->_enable_xss = (config_item('global_xss_filtering') === TRUE); - $this->_enable_csrf = (config_item('csrf_protection') === TRUE); - - global $SEC; - $this->security =& $SEC; - - // Do we need the UTF-8 class? - if (UTF8_ENABLED === TRUE) - { - global $UNI; - $this->uni =& $UNI; - } - - // Sanitize global arrays - $this->_sanitize_globals(); - } - - // -------------------------------------------------------------------- - - /** - * Fetch from array - * - * This is a helper function to retrieve values from global arrays - * - * @access private - * @param array - * @param string - * @param bool - * @return string - */ - function _fetch_from_array(&$array, $index = '', $xss_clean = FALSE) - { - if ( ! isset($array[$index])) - { - return FALSE; - } - - if ($xss_clean === TRUE) - { - return $this->security->xss_clean($array[$index]); - } - - return $array[$index]; - } - - // -------------------------------------------------------------------- - - /** - * Fetch an item from the GET array - * - * @access public - * @param string - * @param bool - * @return string - */ - function get($index = NULL, $xss_clean = FALSE) - { - // Check if a field has been provided - if ($index === NULL AND ! empty($_GET)) - { - $get = array(); - - // loop through the full _GET array - foreach (array_keys($_GET) as $key) - { - $get[$key] = $this->_fetch_from_array($_GET, $key, $xss_clean); - } - return $get; - } - - return $this->_fetch_from_array($_GET, $index, $xss_clean); - } - - // -------------------------------------------------------------------- - - /** - * Fetch an item from the POST array - * - * @access public - * @param string - * @param bool - * @return string - */ - function post($index = NULL, $xss_clean = FALSE) - { - // Check if a field has been provided - if ($index === NULL AND ! empty($_POST)) - { - $post = array(); - - // Loop through the full _POST array and return it - foreach (array_keys($_POST) as $key) - { - $post[$key] = $this->_fetch_from_array($_POST, $key, $xss_clean); - } - return $post; - } - - return $this->_fetch_from_array($_POST, $index, $xss_clean); - } - - - // -------------------------------------------------------------------- - - /** - * Fetch an item from either the GET array or the POST - * - * @access public - * @param string The index key - * @param bool XSS cleaning - * @return string - */ - function get_post($index = '', $xss_clean = FALSE) - { - if ( ! isset($_POST[$index]) ) - { - return $this->get($index, $xss_clean); - } - else - { - return $this->post($index, $xss_clean); - } - } - - // -------------------------------------------------------------------- - - /** - * Fetch an item from the COOKIE array - * - * @access public - * @param string - * @param bool - * @return string - */ - function cookie($index = '', $xss_clean = FALSE) - { - return $this->_fetch_from_array($_COOKIE, $index, $xss_clean); - } - - // ------------------------------------------------------------------------ - - /** - * Set cookie - * - * Accepts six parameter, or you can submit an associative - * array in the first parameter containing all the values. - * - * @access public - * @param mixed - * @param string the value of the cookie - * @param string the number of seconds until expiration - * @param string the cookie domain. Usually: .yourdomain.com - * @param string the cookie path - * @param string the cookie prefix - * @param bool true makes the cookie secure - * @return void - */ - function set_cookie($name = '', $value = '', $expire = '', $domain = '', $path = '/', $prefix = '', $secure = FALSE) - { - if (is_array($name)) - { - // always leave 'name' in last place, as the loop will break otherwise, due to $$item - foreach (array('value', 'expire', 'domain', 'path', 'prefix', 'secure', 'name') as $item) - { - if (isset($name[$item])) - { - $$item = $name[$item]; - } - } - } - - if ($prefix == '' AND config_item('cookie_prefix') != '') - { - $prefix = config_item('cookie_prefix'); - } - if ($domain == '' AND config_item('cookie_domain') != '') - { - $domain = config_item('cookie_domain'); - } - if ($path == '/' AND config_item('cookie_path') != '/') - { - $path = config_item('cookie_path'); - } - if ($secure == FALSE AND config_item('cookie_secure') != FALSE) - { - $secure = config_item('cookie_secure'); - } - - if ( ! is_numeric($expire)) - { - $expire = time() - 86500; - } - else - { - $expire = ($expire > 0) ? time() + $expire : 0; - } - - setcookie($prefix.$name, $value, $expire, $path, $domain, $secure); - } - - // -------------------------------------------------------------------- - - /** - * Fetch an item from the SERVER array - * - * @access public - * @param string - * @param bool - * @return string - */ - function server($index = '', $xss_clean = FALSE) - { - return $this->_fetch_from_array($_SERVER, $index, $xss_clean); - } - - // -------------------------------------------------------------------- - - /** - * Fetch the IP Address - * - * @return string - */ - public function ip_address() - { - if ($this->ip_address !== FALSE) - { - return $this->ip_address; - } - - $proxy_ips = config_item('proxy_ips'); - if ( ! empty($proxy_ips)) - { - $proxy_ips = explode(',', str_replace(' ', '', $proxy_ips)); - foreach (array('HTTP_X_FORWARDED_FOR', 'HTTP_CLIENT_IP', 'HTTP_X_CLIENT_IP', 'HTTP_X_CLUSTER_CLIENT_IP') as $header) - { - if (($spoof = $this->server($header)) !== FALSE) - { - // Some proxies typically list the whole chain of IP - // addresses through which the client has reached us. - // e.g. client_ip, proxy_ip1, proxy_ip2, etc. - if (strpos($spoof, ',') !== FALSE) - { - $spoof = explode(',', $spoof, 2); - $spoof = $spoof[0]; - } - - if ( ! $this->valid_ip($spoof)) - { - $spoof = FALSE; - } - else - { - break; - } - } - } - - $this->ip_address = ($spoof !== FALSE && in_array($_SERVER['REMOTE_ADDR'], $proxy_ips, TRUE)) - ? $spoof : $_SERVER['REMOTE_ADDR']; - } - else - { - $this->ip_address = $_SERVER['REMOTE_ADDR']; - } - - if ( ! $this->valid_ip($this->ip_address)) - { - $this->ip_address = '0.0.0.0'; - } - - return $this->ip_address; - } - - // -------------------------------------------------------------------- - - /** - * Validate IP Address - * - * @access public - * @param string - * @param string ipv4 or ipv6 - * @return bool - */ - public function valid_ip($ip, $which = '') - { - $which = strtolower($which); - - // First check if filter_var is available - if (is_callable('filter_var')) - { - switch ($which) { - case 'ipv4': - $flag = FILTER_FLAG_IPV4; - break; - case 'ipv6': - $flag = FILTER_FLAG_IPV6; - break; - default: - $flag = ''; - break; - } - - return (bool) filter_var($ip, FILTER_VALIDATE_IP, $flag); - } - - if ($which !== 'ipv6' && $which !== 'ipv4') - { - if (strpos($ip, ':') !== FALSE) - { - $which = 'ipv6'; - } - elseif (strpos($ip, '.') !== FALSE) - { - $which = 'ipv4'; - } - else - { - return FALSE; - } - } - - $func = '_valid_'.$which; - return $this->$func($ip); - } - - // -------------------------------------------------------------------- - - /** - * Validate IPv4 Address - * - * Updated version suggested by Geert De Deckere - * - * @access protected - * @param string - * @return bool - */ - protected function _valid_ipv4($ip) - { - $ip_segments = explode('.', $ip); - - // Always 4 segments needed - if (count($ip_segments) !== 4) - { - return FALSE; - } - // IP can not start with 0 - if ($ip_segments[0][0] == '0') - { - return FALSE; - } - - // Check each segment - foreach ($ip_segments as $segment) - { - // IP segments must be digits and can not be - // longer than 3 digits or greater then 255 - if ($segment == '' OR preg_match("/[^0-9]/", $segment) OR $segment > 255 OR strlen($segment) > 3) - { - return FALSE; - } - } - - return TRUE; - } - - // -------------------------------------------------------------------- - - /** - * Validate IPv6 Address - * - * @access protected - * @param string - * @return bool - */ - protected function _valid_ipv6($str) - { - // 8 groups, separated by : - // 0-ffff per group - // one set of consecutive 0 groups can be collapsed to :: - - $groups = 8; - $collapsed = FALSE; - - $chunks = array_filter( - preg_split('/(:{1,2})/', $str, NULL, PREG_SPLIT_DELIM_CAPTURE) - ); - - // Rule out easy nonsense - if (current($chunks) == ':' OR end($chunks) == ':') - { - return FALSE; - } - - // PHP supports IPv4-mapped IPv6 addresses, so we'll expect those as well - if (strpos(end($chunks), '.') !== FALSE) - { - $ipv4 = array_pop($chunks); - - if ( ! $this->_valid_ipv4($ipv4)) - { - return FALSE; - } - - $groups--; - } - - while ($seg = array_pop($chunks)) - { - if ($seg[0] == ':') - { - if (--$groups == 0) - { - return FALSE; // too many groups - } - - if (strlen($seg) > 2) - { - return FALSE; // long separator - } - - if ($seg == '::') - { - if ($collapsed) - { - return FALSE; // multiple collapsed - } - - $collapsed = TRUE; - } - } - elseif (preg_match("/[^0-9a-f]/i", $seg) OR strlen($seg) > 4) - { - return FALSE; // invalid segment - } - } - - return $collapsed OR $groups == 1; - } - - // -------------------------------------------------------------------- - - /** - * User Agent - * - * @access public - * @return string - */ - function user_agent() - { - if ($this->user_agent !== FALSE) - { - return $this->user_agent; - } - - $this->user_agent = ( ! isset($_SERVER['HTTP_USER_AGENT'])) ? FALSE : $_SERVER['HTTP_USER_AGENT']; - - return $this->user_agent; - } - - // -------------------------------------------------------------------- - - /** - * Sanitize Globals - * - * This function does the following: - * - * Unsets $_GET data (if query strings are not enabled) - * - * Unsets all globals if register_globals is enabled - * - * Standardizes newline characters to \n - * - * @access private - * @return void - */ - function _sanitize_globals() - { - // It would be "wrong" to unset any of these GLOBALS. - $protected = array('_SERVER', '_GET', '_POST', '_FILES', '_REQUEST', - '_SESSION', '_ENV', 'GLOBALS', 'HTTP_RAW_POST_DATA', - 'system_folder', 'application_folder', 'BM', 'EXT', - 'CFG', 'URI', 'RTR', 'OUT', 'IN'); - - // Unset globals for securiy. - // This is effectively the same as register_globals = off - foreach (array($_GET, $_POST, $_COOKIE) as $global) - { - if ( ! is_array($global)) - { - if ( ! in_array($global, $protected)) - { - global $$global; - $$global = NULL; - } - } - else - { - foreach ($global as $key => $val) - { - if ( ! in_array($key, $protected)) - { - global $$key; - $$key = NULL; - } - } - } - } - - // Is $_GET data allowed? If not we'll set the $_GET to an empty array - if ($this->_allow_get_array == FALSE) - { - $_GET = array(); - } - else - { - if (is_array($_GET) AND count($_GET) > 0) - { - foreach ($_GET as $key => $val) - { - $_GET[$this->_clean_input_keys($key)] = $this->_clean_input_data($val); - } - } - } - - // Clean $_POST Data - if (is_array($_POST) AND count($_POST) > 0) - { - foreach ($_POST as $key => $val) - { - $_POST[$this->_clean_input_keys($key)] = $this->_clean_input_data($val); - } - } - - // Clean $_COOKIE Data - if (is_array($_COOKIE) AND count($_COOKIE) > 0) - { - // Also get rid of specially treated cookies that might be set by a server - // or silly application, that are of no use to a CI application anyway - // but that when present will trip our 'Disallowed Key Characters' alarm - // http://www.ietf.org/rfc/rfc2109.txt - // note that the key names below are single quoted strings, and are not PHP variables - unset($_COOKIE['$Version']); - unset($_COOKIE['$Path']); - unset($_COOKIE['$Domain']); - - foreach ($_COOKIE as $key => $val) - { - $_COOKIE[$this->_clean_input_keys($key)] = $this->_clean_input_data($val); - } - } - - // Sanitize PHP_SELF - $_SERVER['PHP_SELF'] = strip_tags($_SERVER['PHP_SELF']); - - - // CSRF Protection check on HTTP requests - if ($this->_enable_csrf == TRUE && ! $this->is_cli_request()) - { - $this->security->csrf_verify(); - } - - log_message('debug', "Global POST and COOKIE data sanitized"); - } - - // -------------------------------------------------------------------- - - /** - * Clean Input Data - * - * This is a helper function. It escapes data and - * standardizes newline characters to \n - * - * @access private - * @param string - * @return string - */ - function _clean_input_data($str) - { - if (is_array($str)) - { - $new_array = array(); - foreach ($str as $key => $val) - { - $new_array[$this->_clean_input_keys($key)] = $this->_clean_input_data($val); - } - return $new_array; - } - - /* We strip slashes if magic quotes is on to keep things consistent - - NOTE: In PHP 5.4 get_magic_quotes_gpc() will always return 0 and - it will probably not exist in future versions at all. - */ - if ( ! is_php('5.4') && get_magic_quotes_gpc()) - { - $str = stripslashes($str); - } - - // Clean UTF-8 if supported - if (UTF8_ENABLED === TRUE) - { - $str = $this->uni->clean_string($str); - } - - // Remove control characters - $str = remove_invisible_characters($str); - - // Should we filter the input data? - if ($this->_enable_xss === TRUE) - { - $str = $this->security->xss_clean($str); - } - - // Standardize newlines if needed - if ($this->_standardize_newlines == TRUE) - { - if (strpos($str, "\r") !== FALSE) - { - $str = str_replace(array("\r\n", "\r", "\r\n\n"), PHP_EOL, $str); - } - } - - return $str; - } - - // -------------------------------------------------------------------- - - /** - * Clean Keys - * - * This is a helper function. To prevent malicious users - * from trying to exploit keys we make sure that keys are - * only named with alpha-numeric text and a few other items. - * - * @access private - * @param string - * @return string - */ - function _clean_input_keys($str) - { - if ( ! preg_match("/^[a-z0-9:_\/-]+$/i", $str)) - { - exit('Disallowed Key Characters.'); - } - - // Clean UTF-8 if supported - if (UTF8_ENABLED === TRUE) - { - $str = $this->uni->clean_string($str); - } - - return $str; - } - - // -------------------------------------------------------------------- - - /** - * Request Headers - * - * In Apache, you can simply call apache_request_headers(), however for - * people running other webservers the function is undefined. - * - * @param bool XSS cleaning - * - * @return array - */ - public function request_headers($xss_clean = FALSE) - { - // Look at Apache go! - if (function_exists('apache_request_headers')) - { - $headers = apache_request_headers(); - } - else - { - $headers['Content-Type'] = (isset($_SERVER['CONTENT_TYPE'])) ? $_SERVER['CONTENT_TYPE'] : @getenv('CONTENT_TYPE'); - - foreach ($_SERVER as $key => $val) - { - if (strncmp($key, 'HTTP_', 5) === 0) - { - $headers[substr($key, 5)] = $this->_fetch_from_array($_SERVER, $key, $xss_clean); - } - } - } - - // take SOME_HEADER and turn it into Some-Header - foreach ($headers as $key => $val) - { - $key = str_replace('_', ' ', strtolower($key)); - $key = str_replace(' ', '-', ucwords($key)); - - $this->headers[$key] = $val; - } - - return $this->headers; - } - - // -------------------------------------------------------------------- - - /** - * Get Request Header - * - * Returns the value of a single member of the headers class member - * - * @param string array key for $this->headers - * @param boolean XSS Clean or not - * @return mixed FALSE on failure, string on success - */ - public function get_request_header($index, $xss_clean = FALSE) - { - if (empty($this->headers)) - { - $this->request_headers(); - } - - if ( ! isset($this->headers[$index])) - { - return FALSE; - } - - if ($xss_clean === TRUE) - { - return $this->security->xss_clean($this->headers[$index]); - } - - return $this->headers[$index]; - } - - // -------------------------------------------------------------------- - - /** - * Is ajax Request? - * - * Test to see if a request contains the HTTP_X_REQUESTED_WITH header - * - * @return boolean - */ - public function is_ajax_request() - { - return ($this->server('HTTP_X_REQUESTED_WITH') === 'XMLHttpRequest'); - } - - // -------------------------------------------------------------------- - - /** - * Is cli Request? - * - * Test to see if a request was made from the command line - * - * @return bool - */ - public function is_cli_request() - { - return (php_sapi_name() === 'cli' OR defined('STDIN')); - } - -} - -/* End of file Input.php */ -/* Location: ./system/core/Input.php */ \ No newline at end of file diff --git a/system/core/Lang.php b/system/core/Lang.php deleted file mode 100755 index 5ac67183..00000000 --- a/system/core/Lang.php +++ /dev/null @@ -1,160 +0,0 @@ -is_loaded, TRUE)) - { - return; - } - - $config =& get_config(); - - if ($idiom == '') - { - $deft_lang = ( ! isset($config['language'])) ? 'english' : $config['language']; - $idiom = ($deft_lang == '') ? 'english' : $deft_lang; - } - - // Determine where the language file is and load it - if ($alt_path != '' && file_exists($alt_path.'language/'.$idiom.'/'.$langfile)) - { - include($alt_path.'language/'.$idiom.'/'.$langfile); - } - else - { - $found = FALSE; - - foreach (get_instance()->load->get_package_paths(TRUE) as $package_path) - { - if (file_exists($package_path.'language/'.$idiom.'/'.$langfile)) - { - include($package_path.'language/'.$idiom.'/'.$langfile); - $found = TRUE; - break; - } - } - - if ($found !== TRUE) - { - show_error('Unable to load the requested language file: language/'.$idiom.'/'.$langfile); - } - } - - - if ( ! isset($lang)) - { - log_message('error', 'Language file contains no data: language/'.$idiom.'/'.$langfile); - return; - } - - if ($return == TRUE) - { - return $lang; - } - - $this->is_loaded[] = $langfile; - $this->language = array_merge($this->language, $lang); - unset($lang); - - log_message('debug', 'Language file loaded: language/'.$idiom.'/'.$langfile); - return TRUE; - } - - // -------------------------------------------------------------------- - - /** - * Fetch a single line of text from the language array - * - * @access public - * @param string $line the language line - * @return string - */ - function line($line = '') - { - $value = ($line == '' OR ! isset($this->language[$line])) ? FALSE : $this->language[$line]; - - // Because killer robots like unicorns! - if ($value === FALSE) - { - log_message('error', 'Could not find the language line "'.$line.'"'); - } - - return $value; - } - -} -// END Language Class - -/* End of file Lang.php */ -/* Location: ./system/core/Lang.php */ diff --git a/system/core/Loader.php b/system/core/Loader.php deleted file mode 100644 index 6b7ee0c2..00000000 --- a/system/core/Loader.php +++ /dev/null @@ -1,1248 +0,0 @@ - 'unit', - 'user_agent' => 'agent'); - - /** - * Constructor - * - * Sets the path to the view files and gets the initial output buffering level - */ - public function __construct() - { - $this->_ci_ob_level = ob_get_level(); - $this->_ci_library_paths = array(APPPATH, BASEPATH); - $this->_ci_helper_paths = array(APPPATH, BASEPATH); - $this->_ci_model_paths = array(APPPATH); - $this->_ci_view_paths = array(APPPATH.'views/' => TRUE); - - log_message('debug', "Loader Class Initialized"); - } - - // -------------------------------------------------------------------- - - /** - * Initialize the Loader - * - * This method is called once in CI_Controller. - * - * @param array - * @return object - */ - public function initialize() - { - $this->_ci_classes = array(); - $this->_ci_loaded_files = array(); - $this->_ci_models = array(); - $this->_base_classes =& is_loaded(); - - $this->_ci_autoloader(); - - return $this; - } - - // -------------------------------------------------------------------- - - /** - * Is Loaded - * - * A utility function to test if a class is in the self::$_ci_classes array. - * This function returns the object name if the class tested for is loaded, - * and returns FALSE if it isn't. - * - * It is mainly used in the form_helper -> _get_validation_object() - * - * @param string class being checked for - * @return mixed class object name on the CI SuperObject or FALSE - */ - public function is_loaded($class) - { - if (isset($this->_ci_classes[$class])) - { - return $this->_ci_classes[$class]; - } - - return FALSE; - } - - // -------------------------------------------------------------------- - - /** - * Class Loader - * - * This function lets users load and instantiate classes. - * It is designed to be called from a user's app controllers. - * - * @param string the name of the class - * @param mixed the optional parameters - * @param string an optional object name - * @return void - */ - public function library($library = '', $params = NULL, $object_name = NULL) - { - if (is_array($library)) - { - foreach ($library as $class) - { - $this->library($class, $params); - } - - return; - } - - if ($library == '' OR isset($this->_base_classes[$library])) - { - return FALSE; - } - - if ( ! is_null($params) && ! is_array($params)) - { - $params = NULL; - } - - $this->_ci_load_class($library, $params, $object_name); - } - - // -------------------------------------------------------------------- - - /** - * Model Loader - * - * This function lets users load and instantiate models. - * - * @param string the name of the class - * @param string name for the model - * @param bool database connection - * @return void - */ - public function model($model, $name = '', $db_conn = FALSE) - { - if (is_array($model)) - { - foreach ($model as $babe) - { - $this->model($babe); - } - return; - } - - if ($model == '') - { - return; - } - - $path = ''; - - // Is the model in a sub-folder? If so, parse out the filename and path. - if (($last_slash = strrpos($model, '/')) !== FALSE) - { - // The path is in front of the last slash - $path = substr($model, 0, $last_slash + 1); - - // And the model name behind it - $model = substr($model, $last_slash + 1); - } - - if ($name == '') - { - $name = $model; - } - - if (in_array($name, $this->_ci_models, TRUE)) - { - return; - } - - $CI =& get_instance(); - if (isset($CI->$name)) - { - show_error('The model name you are loading is the name of a resource that is already being used: '.$name); - } - - $model = strtolower($model); - - foreach ($this->_ci_model_paths as $mod_path) - { - if ( ! file_exists($mod_path.'models/'.$path.$model.'.php')) - { - continue; - } - - if ($db_conn !== FALSE AND ! class_exists('CI_DB')) - { - if ($db_conn === TRUE) - { - $db_conn = ''; - } - - $CI->load->database($db_conn, FALSE, TRUE); - } - - if ( ! class_exists('CI_Model')) - { - load_class('Model', 'core'); - } - - require_once($mod_path.'models/'.$path.$model.'.php'); - - $model = ucfirst($model); - - $CI->$name = new $model(); - - $this->_ci_models[] = $name; - return; - } - - // couldn't find the model - show_error('Unable to locate the model you have specified: '.$model); - } - - // -------------------------------------------------------------------- - - /** - * Database Loader - * - * @param string the DB credentials - * @param bool whether to return the DB object - * @param bool whether to enable active record (this allows us to override the config setting) - * @return object - */ - public function database($params = '', $return = FALSE, $active_record = NULL) - { - // Grab the super object - $CI =& get_instance(); - - // Do we even need to load the database class? - if (class_exists('CI_DB') AND $return == FALSE AND $active_record == NULL AND isset($CI->db) AND is_object($CI->db)) - { - return FALSE; - } - - require_once(BASEPATH.'database/DB.php'); - - if ($return === TRUE) - { - return DB($params, $active_record); - } - - // Initialize the db variable. Needed to prevent - // reference errors with some configurations - $CI->db = ''; - - // Load the DB class - $CI->db =& DB($params, $active_record); - } - - // -------------------------------------------------------------------- - - /** - * Load the Utilities Class - * - * @return string - */ - public function dbutil() - { - if ( ! class_exists('CI_DB')) - { - $this->database(); - } - - $CI =& get_instance(); - - // for backwards compatibility, load dbforge so we can extend dbutils off it - // this use is deprecated and strongly discouraged - $CI->load->dbforge(); - - require_once(BASEPATH.'database/DB_utility.php'); - require_once(BASEPATH.'database/drivers/'.$CI->db->dbdriver.'/'.$CI->db->dbdriver.'_utility.php'); - $class = 'CI_DB_'.$CI->db->dbdriver.'_utility'; - - $CI->dbutil = new $class(); - } - - // -------------------------------------------------------------------- - - /** - * Load the Database Forge Class - * - * @return string - */ - public function dbforge() - { - if ( ! class_exists('CI_DB')) - { - $this->database(); - } - - $CI =& get_instance(); - - require_once(BASEPATH.'database/DB_forge.php'); - require_once(BASEPATH.'database/drivers/'.$CI->db->dbdriver.'/'.$CI->db->dbdriver.'_forge.php'); - $class = 'CI_DB_'.$CI->db->dbdriver.'_forge'; - - $CI->dbforge = new $class(); - } - - // -------------------------------------------------------------------- - - /** - * Load View - * - * This function is used to load a "view" file. It has three parameters: - * - * 1. The name of the "view" file to be included. - * 2. An associative array of data to be extracted for use in the view. - * 3. TRUE/FALSE - whether to return the data or load it. In - * some cases it's advantageous to be able to return data so that - * a developer can process it in some way. - * - * @param string - * @param array - * @param bool - * @return void - */ - public function view($view, $vars = array(), $return = FALSE) - { - return $this->_ci_load(array('_ci_view' => $view, '_ci_vars' => $this->_ci_object_to_array($vars), '_ci_return' => $return)); - } - - // -------------------------------------------------------------------- - - /** - * Load File - * - * This is a generic file loader - * - * @param string - * @param bool - * @return string - */ - public function file($path, $return = FALSE) - { - return $this->_ci_load(array('_ci_path' => $path, '_ci_return' => $return)); - } - - // -------------------------------------------------------------------- - - /** - * Set Variables - * - * Once variables are set they become available within - * the controller class and its "view" files. - * - * @param array - * @param string - * @return void - */ - public function vars($vars = array(), $val = '') - { - if ($val != '' AND is_string($vars)) - { - $vars = array($vars => $val); - } - - $vars = $this->_ci_object_to_array($vars); - - if (is_array($vars) AND count($vars) > 0) - { - foreach ($vars as $key => $val) - { - $this->_ci_cached_vars[$key] = $val; - } - } - } - - // -------------------------------------------------------------------- - - /** - * Get Variable - * - * Check if a variable is set and retrieve it. - * - * @param array - * @return void - */ - public function get_var($key) - { - return isset($this->_ci_cached_vars[$key]) ? $this->_ci_cached_vars[$key] : NULL; - } - - // -------------------------------------------------------------------- - - /** - * Load Helper - * - * This function loads the specified helper file. - * - * @param mixed - * @return void - */ - public function helper($helpers = array()) - { - foreach ($this->_ci_prep_filename($helpers, '_helper') as $helper) - { - if (isset($this->_ci_helpers[$helper])) - { - continue; - } - - $ext_helper = APPPATH.'helpers/'.config_item('subclass_prefix').$helper.'.php'; - - // Is this a helper extension request? - if (file_exists($ext_helper)) - { - $base_helper = BASEPATH.'helpers/'.$helper.'.php'; - - if ( ! file_exists($base_helper)) - { - show_error('Unable to load the requested file: helpers/'.$helper.'.php'); - } - - include_once($ext_helper); - include_once($base_helper); - - $this->_ci_helpers[$helper] = TRUE; - log_message('debug', 'Helper loaded: '.$helper); - continue; - } - - // Try to load the helper - foreach ($this->_ci_helper_paths as $path) - { - if (file_exists($path.'helpers/'.$helper.'.php')) - { - include_once($path.'helpers/'.$helper.'.php'); - - $this->_ci_helpers[$helper] = TRUE; - log_message('debug', 'Helper loaded: '.$helper); - break; - } - } - - // unable to load the helper - if ( ! isset($this->_ci_helpers[$helper])) - { - show_error('Unable to load the requested file: helpers/'.$helper.'.php'); - } - } - } - - // -------------------------------------------------------------------- - - /** - * Load Helpers - * - * This is simply an alias to the above function in case the - * user has written the plural form of this function. - * - * @param array - * @return void - */ - public function helpers($helpers = array()) - { - $this->helper($helpers); - } - - // -------------------------------------------------------------------- - - /** - * Loads a language file - * - * @param array - * @param string - * @return void - */ - public function language($file = array(), $lang = '') - { - $CI =& get_instance(); - - if ( ! is_array($file)) - { - $file = array($file); - } - - foreach ($file as $langfile) - { - $CI->lang->load($langfile, $lang); - } - } - - // -------------------------------------------------------------------- - - /** - * Loads a config file - * - * @param string - * @param bool - * @param bool - * @return void - */ - public function config($file = '', $use_sections = FALSE, $fail_gracefully = FALSE) - { - $CI =& get_instance(); - $CI->config->load($file, $use_sections, $fail_gracefully); - } - - // -------------------------------------------------------------------- - - /** - * Driver - * - * Loads a driver library - * - * @param string the name of the class - * @param mixed the optional parameters - * @param string an optional object name - * @return void - */ - public function driver($library = '', $params = NULL, $object_name = NULL) - { - if ( ! class_exists('CI_Driver_Library')) - { - // we aren't instantiating an object here, that'll be done by the Library itself - require BASEPATH.'libraries/Driver.php'; - } - - if ($library == '') - { - return FALSE; - } - - // We can save the loader some time since Drivers will *always* be in a subfolder, - // and typically identically named to the library - if ( ! strpos($library, '/')) - { - $library = ucfirst($library).'/'.$library; - } - - return $this->library($library, $params, $object_name); - } - - // -------------------------------------------------------------------- - - /** - * Add Package Path - * - * Prepends a parent path to the library, model, helper, and config path arrays - * - * @param string - * @param boolean - * @return void - */ - public function add_package_path($path, $view_cascade=TRUE) - { - $path = rtrim($path, '/').'/'; - - array_unshift($this->_ci_library_paths, $path); - array_unshift($this->_ci_model_paths, $path); - array_unshift($this->_ci_helper_paths, $path); - - $this->_ci_view_paths = array($path.'views/' => $view_cascade) + $this->_ci_view_paths; - - // Add config file path - $config =& $this->_ci_get_component('config'); - array_unshift($config->_config_paths, $path); - } - - // -------------------------------------------------------------------- - - /** - * Get Package Paths - * - * Return a list of all package paths, by default it will ignore BASEPATH. - * - * @param string - * @return void - */ - public function get_package_paths($include_base = FALSE) - { - return $include_base === TRUE ? $this->_ci_library_paths : $this->_ci_model_paths; - } - - // -------------------------------------------------------------------- - - /** - * Remove Package Path - * - * Remove a path from the library, model, and helper path arrays if it exists - * If no path is provided, the most recently added path is removed. - * - * @param type - * @param bool - * @return type - */ - public function remove_package_path($path = '', $remove_config_path = TRUE) - { - $config =& $this->_ci_get_component('config'); - - if ($path == '') - { - $void = array_shift($this->_ci_library_paths); - $void = array_shift($this->_ci_model_paths); - $void = array_shift($this->_ci_helper_paths); - $void = array_shift($this->_ci_view_paths); - $void = array_shift($config->_config_paths); - } - else - { - $path = rtrim($path, '/').'/'; - foreach (array('_ci_library_paths', '_ci_model_paths', '_ci_helper_paths') as $var) - { - if (($key = array_search($path, $this->{$var})) !== FALSE) - { - unset($this->{$var}[$key]); - } - } - - if (isset($this->_ci_view_paths[$path.'views/'])) - { - unset($this->_ci_view_paths[$path.'views/']); - } - - if (($key = array_search($path, $config->_config_paths)) !== FALSE) - { - unset($config->_config_paths[$key]); - } - } - - // make sure the application default paths are still in the array - $this->_ci_library_paths = array_unique(array_merge($this->_ci_library_paths, array(APPPATH, BASEPATH))); - $this->_ci_helper_paths = array_unique(array_merge($this->_ci_helper_paths, array(APPPATH, BASEPATH))); - $this->_ci_model_paths = array_unique(array_merge($this->_ci_model_paths, array(APPPATH))); - $this->_ci_view_paths = array_merge($this->_ci_view_paths, array(APPPATH.'views/' => TRUE)); - $config->_config_paths = array_unique(array_merge($config->_config_paths, array(APPPATH))); - } - - // -------------------------------------------------------------------- - - /** - * Loader - * - * This function is used to load views and files. - * Variables are prefixed with _ci_ to avoid symbol collision with - * variables made available to view files - * - * @param array - * @return void - */ - protected function _ci_load($_ci_data) - { - // Set the default data variables - foreach (array('_ci_view', '_ci_vars', '_ci_path', '_ci_return') as $_ci_val) - { - $$_ci_val = ( ! isset($_ci_data[$_ci_val])) ? FALSE : $_ci_data[$_ci_val]; - } - - $file_exists = FALSE; - - // Set the path to the requested file - if ($_ci_path != '') - { - $_ci_x = explode('/', $_ci_path); - $_ci_file = end($_ci_x); - } - else - { - $_ci_ext = pathinfo($_ci_view, PATHINFO_EXTENSION); - $_ci_file = ($_ci_ext == '') ? $_ci_view.'.php' : $_ci_view; - - foreach ($this->_ci_view_paths as $view_file => $cascade) - { - if (file_exists($view_file.$_ci_file)) - { - $_ci_path = $view_file.$_ci_file; - $file_exists = TRUE; - break; - } - - if ( ! $cascade) - { - break; - } - } - } - - if ( ! $file_exists && ! file_exists($_ci_path)) - { - show_error('Unable to load the requested file: '.$_ci_file); - } - - // This allows anything loaded using $this->load (views, files, etc.) - // to become accessible from within the Controller and Model functions. - - $_ci_CI =& get_instance(); - foreach (get_object_vars($_ci_CI) as $_ci_key => $_ci_var) - { - if ( ! isset($this->$_ci_key)) - { - $this->$_ci_key =& $_ci_CI->$_ci_key; - } - } - - /* - * Extract and cache variables - * - * You can either set variables using the dedicated $this->load_vars() - * function or via the second parameter of this function. We'll merge - * the two types and cache them so that views that are embedded within - * other views can have access to these variables. - */ - if (is_array($_ci_vars)) - { - $this->_ci_cached_vars = array_merge($this->_ci_cached_vars, $_ci_vars); - } - extract($this->_ci_cached_vars); - - /* - * Buffer the output - * - * We buffer the output for two reasons: - * 1. Speed. You get a significant speed boost. - * 2. So that the final rendered template can be - * post-processed by the output class. Why do we - * need post processing? For one thing, in order to - * show the elapsed page load time. Unless we - * can intercept the content right before it's sent to - * the browser and then stop the timer it won't be accurate. - */ - ob_start(); - - // If the PHP installation does not support short tags we'll - // do a little string replacement, changing the short tags - // to standard PHP echo statements. - - if ((bool) @ini_get('short_open_tag') === FALSE AND config_item('rewrite_short_tags') == TRUE) - { - echo eval('?>'.preg_replace("/;*\s*\?>/", "; ?>", str_replace(' $this->_ci_ob_level + 1) - { - ob_end_flush(); - } - else - { - $_ci_CI->output->append_output(ob_get_contents()); - @ob_end_clean(); - } - } - - // -------------------------------------------------------------------- - - /** - * Load class - * - * This function loads the requested class. - * - * @param string the item that is being loaded - * @param mixed any additional parameters - * @param string an optional object name - * @return void - */ - protected function _ci_load_class($class, $params = NULL, $object_name = NULL) - { - // Get the class name, and while we're at it trim any slashes. - // The directory path can be included as part of the class name, - // but we don't want a leading slash - $class = str_replace('.php', '', trim($class, '/')); - - // Was the path included with the class name? - // We look for a slash to determine this - $subdir = ''; - if (($last_slash = strrpos($class, '/')) !== FALSE) - { - // Extract the path - $subdir = substr($class, 0, $last_slash + 1); - - // Get the filename from the path - $class = substr($class, $last_slash + 1); - } - - // We'll test for both lowercase and capitalized versions of the file name - foreach (array(ucfirst($class), strtolower($class)) as $class) - { - $subclass = APPPATH.'libraries/'.$subdir.config_item('subclass_prefix').$class.'.php'; - - // Is this a class extension request? - if (file_exists($subclass)) - { - $baseclass = BASEPATH.'libraries/'.ucfirst($class).'.php'; - - if ( ! file_exists($baseclass)) - { - log_message('error', "Unable to load the requested class: ".$class); - show_error("Unable to load the requested class: ".$class); - } - - // Safety: Was the class already loaded by a previous call? - if (in_array($subclass, $this->_ci_loaded_files)) - { - // Before we deem this to be a duplicate request, let's see - // if a custom object name is being supplied. If so, we'll - // return a new instance of the object - if ( ! is_null($object_name)) - { - $CI =& get_instance(); - if ( ! isset($CI->$object_name)) - { - return $this->_ci_init_class($class, config_item('subclass_prefix'), $params, $object_name); - } - } - - $is_duplicate = TRUE; - log_message('debug', $class." class already loaded. Second attempt ignored."); - return; - } - - include_once($baseclass); - include_once($subclass); - $this->_ci_loaded_files[] = $subclass; - - return $this->_ci_init_class($class, config_item('subclass_prefix'), $params, $object_name); - } - - // Lets search for the requested library file and load it. - $is_duplicate = FALSE; - foreach ($this->_ci_library_paths as $path) - { - $filepath = $path.'libraries/'.$subdir.$class.'.php'; - - // Does the file exist? No? Bummer... - if ( ! file_exists($filepath)) - { - continue; - } - - // Safety: Was the class already loaded by a previous call? - if (in_array($filepath, $this->_ci_loaded_files)) - { - // Before we deem this to be a duplicate request, let's see - // if a custom object name is being supplied. If so, we'll - // return a new instance of the object - if ( ! is_null($object_name)) - { - $CI =& get_instance(); - if ( ! isset($CI->$object_name)) - { - return $this->_ci_init_class($class, '', $params, $object_name); - } - } - - $is_duplicate = TRUE; - log_message('debug', $class." class already loaded. Second attempt ignored."); - return; - } - - include_once($filepath); - $this->_ci_loaded_files[] = $filepath; - return $this->_ci_init_class($class, '', $params, $object_name); - } - - } // END FOREACH - - // One last attempt. Maybe the library is in a subdirectory, but it wasn't specified? - if ($subdir == '') - { - $path = strtolower($class).'/'.$class; - return $this->_ci_load_class($path, $params); - } - - // If we got this far we were unable to find the requested class. - // We do not issue errors if the load call failed due to a duplicate request - if ($is_duplicate == FALSE) - { - log_message('error', "Unable to load the requested class: ".$class); - show_error("Unable to load the requested class: ".$class); - } - } - - // -------------------------------------------------------------------- - - /** - * Instantiates a class - * - * @param string - * @param string - * @param bool - * @param string an optional object name - * @return null - */ - protected function _ci_init_class($class, $prefix = '', $config = FALSE, $object_name = NULL) - { - // Is there an associated config file for this class? Note: these should always be lowercase - if ($config === NULL) - { - // Fetch the config paths containing any package paths - $config_component = $this->_ci_get_component('config'); - - if (is_array($config_component->_config_paths)) - { - // Break on the first found file, thus package files - // are not overridden by default paths - foreach ($config_component->_config_paths as $path) - { - // We test for both uppercase and lowercase, for servers that - // are case-sensitive with regard to file names. Check for environment - // first, global next - if (defined('ENVIRONMENT') AND file_exists($path .'config/'.ENVIRONMENT.'/'.strtolower($class).'.php')) - { - include($path .'config/'.ENVIRONMENT.'/'.strtolower($class).'.php'); - break; - } - elseif (defined('ENVIRONMENT') AND file_exists($path .'config/'.ENVIRONMENT.'/'.ucfirst(strtolower($class)).'.php')) - { - include($path .'config/'.ENVIRONMENT.'/'.ucfirst(strtolower($class)).'.php'); - break; - } - elseif (file_exists($path .'config/'.strtolower($class).'.php')) - { - include($path .'config/'.strtolower($class).'.php'); - break; - } - elseif (file_exists($path .'config/'.ucfirst(strtolower($class)).'.php')) - { - include($path .'config/'.ucfirst(strtolower($class)).'.php'); - break; - } - } - } - } - - if ($prefix == '') - { - if (class_exists('CI_'.$class)) - { - $name = 'CI_'.$class; - } - elseif (class_exists(config_item('subclass_prefix').$class)) - { - $name = config_item('subclass_prefix').$class; - } - else - { - $name = $class; - } - } - else - { - $name = $prefix.$class; - } - - // Is the class name valid? - if ( ! class_exists($name)) - { - log_message('error', "Non-existent class: ".$name); - show_error("Non-existent class: ".$class); - } - - // Set the variable name we will assign the class to - // Was a custom class name supplied? If so we'll use it - $class = strtolower($class); - - if (is_null($object_name)) - { - $classvar = ( ! isset($this->_ci_varmap[$class])) ? $class : $this->_ci_varmap[$class]; - } - else - { - $classvar = $object_name; - } - - // Save the class name and object name - $this->_ci_classes[$class] = $classvar; - - // Instantiate the class - $CI =& get_instance(); - if ($config !== NULL) - { - $CI->$classvar = new $name($config); - } - else - { - $CI->$classvar = new $name; - } - } - - // -------------------------------------------------------------------- - - /** - * Autoloader - * - * The config/autoload.php file contains an array that permits sub-systems, - * libraries, and helpers to be loaded automatically. - * - * @param array - * @return void - */ - private function _ci_autoloader() - { - if (defined('ENVIRONMENT') AND file_exists(APPPATH.'config/'.ENVIRONMENT.'/autoload.php')) - { - include(APPPATH.'config/'.ENVIRONMENT.'/autoload.php'); - } - else - { - include(APPPATH.'config/autoload.php'); - } - - if ( ! isset($autoload)) - { - return FALSE; - } - - // Autoload packages - if (isset($autoload['packages'])) - { - foreach ($autoload['packages'] as $package_path) - { - $this->add_package_path($package_path); - } - } - - // Load any custom config file - if (count($autoload['config']) > 0) - { - $CI =& get_instance(); - foreach ($autoload['config'] as $key => $val) - { - $CI->config->load($val); - } - } - - // Autoload helpers and languages - foreach (array('helper', 'language') as $type) - { - if (isset($autoload[$type]) AND count($autoload[$type]) > 0) - { - $this->$type($autoload[$type]); - } - } - - // A little tweak to remain backward compatible - // The $autoload['core'] item was deprecated - if ( ! isset($autoload['libraries']) AND isset($autoload['core'])) - { - $autoload['libraries'] = $autoload['core']; - } - - // Load libraries - if (isset($autoload['libraries']) AND count($autoload['libraries']) > 0) - { - // Load the database driver. - if (in_array('database', $autoload['libraries'])) - { - $this->database(); - $autoload['libraries'] = array_diff($autoload['libraries'], array('database')); - } - - // Load all other libraries - foreach ($autoload['libraries'] as $item) - { - $this->library($item); - } - } - - // Autoload models - if (isset($autoload['model'])) - { - $this->model($autoload['model']); - } - } - - // -------------------------------------------------------------------- - - /** - * Object to Array - * - * Takes an object as input and converts the class variables to array key/vals - * - * @param object - * @return array - */ - protected function _ci_object_to_array($object) - { - return (is_object($object)) ? get_object_vars($object) : $object; - } - - // -------------------------------------------------------------------- - - /** - * Get a reference to a specific library or model - * - * @param string - * @return bool - */ - protected function &_ci_get_component($component) - { - $CI =& get_instance(); - return $CI->$component; - } - - // -------------------------------------------------------------------- - - /** - * Prep filename - * - * This function preps the name of various items to make loading them more reliable. - * - * @param mixed - * @param string - * @return array - */ - protected function _ci_prep_filename($filename, $extension) - { - if ( ! is_array($filename)) - { - return array(strtolower(str_replace('.php', '', str_replace($extension, '', $filename)).$extension)); - } - else - { - foreach ($filename as $key => $val) - { - $filename[$key] = strtolower(str_replace('.php', '', str_replace($extension, '', $val)).$extension); - } - - return $filename; - } - } -} - -/* End of file Loader.php */ -/* Location: ./system/core/Loader.php */ \ No newline at end of file diff --git a/system/core/Model.php b/system/core/Model.php deleted file mode 100755 index e15ffbeb..00000000 --- a/system/core/Model.php +++ /dev/null @@ -1,57 +0,0 @@ -$key; - } -} -// END Model Class - -/* End of file Model.php */ -/* Location: ./system/core/Model.php */ \ No newline at end of file diff --git a/system/core/Output.php b/system/core/Output.php deleted file mode 100755 index ccecafd2..00000000 --- a/system/core/Output.php +++ /dev/null @@ -1,574 +0,0 @@ -_zlib_oc = @ini_get('zlib.output_compression'); - - // Get mime types for later - if (defined('ENVIRONMENT') AND file_exists(APPPATH.'config/'.ENVIRONMENT.'/mimes.php')) - { - include APPPATH.'config/'.ENVIRONMENT.'/mimes.php'; - } - else - { - include APPPATH.'config/mimes.php'; - } - - - $this->mime_types = $mimes; - - log_message('debug', "Output Class Initialized"); - } - - // -------------------------------------------------------------------- - - /** - * Get Output - * - * Returns the current output string - * - * @access public - * @return string - */ - function get_output() - { - return $this->final_output; - } - - // -------------------------------------------------------------------- - - /** - * Set Output - * - * Sets the output string - * - * @access public - * @param string - * @return void - */ - function set_output($output) - { - $this->final_output = $output; - - return $this; - } - - // -------------------------------------------------------------------- - - /** - * Append Output - * - * Appends data onto the output string - * - * @access public - * @param string - * @return void - */ - function append_output($output) - { - if ($this->final_output == '') - { - $this->final_output = $output; - } - else - { - $this->final_output .= $output; - } - - return $this; - } - - // -------------------------------------------------------------------- - - /** - * Set Header - * - * Lets you set a server header which will be outputted with the final display. - * - * Note: If a file is cached, headers will not be sent. We need to figure out - * how to permit header data to be saved with the cache data... - * - * @access public - * @param string - * @param bool - * @return void - */ - function set_header($header, $replace = TRUE) - { - // If zlib.output_compression is enabled it will compress the output, - // but it will not modify the content-length header to compensate for - // the reduction, causing the browser to hang waiting for more data. - // We'll just skip content-length in those cases. - - if ($this->_zlib_oc && strncasecmp($header, 'content-length', 14) == 0) - { - return; - } - - $this->headers[] = array($header, $replace); - - return $this; - } - - // -------------------------------------------------------------------- - - /** - * Set Content Type Header - * - * @access public - * @param string extension of the file we're outputting - * @return void - */ - function set_content_type($mime_type) - { - if (strpos($mime_type, '/') === FALSE) - { - $extension = ltrim($mime_type, '.'); - - // Is this extension supported? - if (isset($this->mime_types[$extension])) - { - $mime_type =& $this->mime_types[$extension]; - - if (is_array($mime_type)) - { - $mime_type = current($mime_type); - } - } - } - - $header = 'Content-Type: '.$mime_type; - - $this->headers[] = array($header, TRUE); - - return $this; - } - - // -------------------------------------------------------------------- - - /** - * Set HTTP Status Header - * moved to Common procedural functions in 1.7.2 - * - * @access public - * @param int the status code - * @param string - * @return void - */ - function set_status_header($code = 200, $text = '') - { - set_status_header($code, $text); - - return $this; - } - - // -------------------------------------------------------------------- - - /** - * Enable/disable Profiler - * - * @access public - * @param bool - * @return void - */ - function enable_profiler($val = TRUE) - { - $this->enable_profiler = (is_bool($val)) ? $val : TRUE; - - return $this; - } - - // -------------------------------------------------------------------- - - /** - * Set Profiler Sections - * - * Allows override of default / config settings for Profiler section display - * - * @access public - * @param array - * @return void - */ - function set_profiler_sections($sections) - { - foreach ($sections as $section => $enable) - { - $this->_profiler_sections[$section] = ($enable !== FALSE) ? TRUE : FALSE; - } - - return $this; - } - - // -------------------------------------------------------------------- - - /** - * Set Cache - * - * @access public - * @param integer - * @return void - */ - function cache($time) - { - $this->cache_expiration = ( ! is_numeric($time)) ? 0 : $time; - - return $this; - } - - // -------------------------------------------------------------------- - - /** - * Display Output - * - * All "view" data is automatically put into this variable by the controller class: - * - * $this->final_output - * - * This function sends the finalized output data to the browser along - * with any server headers and profile data. It also stops the - * benchmark timer so the page rendering speed and memory usage can be shown. - * - * @access public - * @param string - * @return mixed - */ - function _display($output = '') - { - // Note: We use globals because we can't use $CI =& get_instance() - // since this function is sometimes called by the caching mechanism, - // which happens before the CI super object is available. - global $BM, $CFG; - - // Grab the super object if we can. - if (class_exists('CI_Controller')) - { - $CI =& get_instance(); - } - - // -------------------------------------------------------------------- - - // Set the output data - if ($output == '') - { - $output =& $this->final_output; - } - - // -------------------------------------------------------------------- - - // Do we need to write a cache file? Only if the controller does not have its - // own _output() method and we are not dealing with a cache file, which we - // can determine by the existence of the $CI object above - if ($this->cache_expiration > 0 && isset($CI) && ! method_exists($CI, '_output')) - { - $this->_write_cache($output); - } - - // -------------------------------------------------------------------- - - // Parse out the elapsed time and memory usage, - // then swap the pseudo-variables with the data - - $elapsed = $BM->elapsed_time('total_execution_time_start', 'total_execution_time_end'); - - if ($this->parse_exec_vars === TRUE) - { - $memory = ( ! function_exists('memory_get_usage')) ? '0' : round(memory_get_usage()/1024/1024, 2).'MB'; - - $output = str_replace('{elapsed_time}', $elapsed, $output); - $output = str_replace('{memory_usage}', $memory, $output); - } - - // -------------------------------------------------------------------- - - // Is compression requested? - if ($CFG->item('compress_output') === TRUE && $this->_zlib_oc == FALSE) - { - if (extension_loaded('zlib')) - { - if (isset($_SERVER['HTTP_ACCEPT_ENCODING']) AND strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE) - { - ob_start('ob_gzhandler'); - } - } - } - - // -------------------------------------------------------------------- - - // Are there any server headers to send? - if (count($this->headers) > 0) - { - foreach ($this->headers as $header) - { - @header($header[0], $header[1]); - } - } - - // -------------------------------------------------------------------- - - // Does the $CI object exist? - // If not we know we are dealing with a cache file so we'll - // simply echo out the data and exit. - if ( ! isset($CI)) - { - echo $output; - log_message('debug', "Final output sent to browser"); - log_message('debug', "Total execution time: ".$elapsed); - return TRUE; - } - - // -------------------------------------------------------------------- - - // Do we need to generate profile data? - // If so, load the Profile class and run it. - if ($this->enable_profiler == TRUE) - { - $CI->load->library('profiler'); - - if ( ! empty($this->_profiler_sections)) - { - $CI->profiler->set_sections($this->_profiler_sections); - } - - // If the output data contains closing and tags - // we will remove them and add them back after we insert the profile data - if (preg_match("|.*?|is", $output)) - { - $output = preg_replace("|.*?|is", '', $output); - $output .= $CI->profiler->run(); - $output .= ''; - } - else - { - $output .= $CI->profiler->run(); - } - } - - // -------------------------------------------------------------------- - - // Does the controller contain a function named _output()? - // If so send the output there. Otherwise, echo it. - if (method_exists($CI, '_output')) - { - $CI->_output($output); - } - else - { - echo $output; // Send it to the browser! - } - - log_message('debug', "Final output sent to browser"); - log_message('debug', "Total execution time: ".$elapsed); - } - - // -------------------------------------------------------------------- - - /** - * Write a Cache File - * - * @access public - * @param string - * @return void - */ - function _write_cache($output) - { - $CI =& get_instance(); - $path = $CI->config->item('cache_path'); - - $cache_path = ($path == '') ? APPPATH.'cache/' : $path; - - if ( ! is_dir($cache_path) OR ! is_really_writable($cache_path)) - { - log_message('error', "Unable to write cache file: ".$cache_path); - return; - } - - $uri = $CI->config->item('base_url'). - $CI->config->item('index_page'). - $CI->uri->uri_string(); - - $cache_path .= md5($uri); - - if ( ! $fp = @fopen($cache_path, FOPEN_WRITE_CREATE_DESTRUCTIVE)) - { - log_message('error', "Unable to write cache file: ".$cache_path); - return; - } - - $expire = time() + ($this->cache_expiration * 60); - - if (flock($fp, LOCK_EX)) - { - fwrite($fp, $expire.'TS--->'.$output); - flock($fp, LOCK_UN); - } - else - { - log_message('error', "Unable to secure a file lock for file at: ".$cache_path); - return; - } - fclose($fp); - @chmod($cache_path, FILE_WRITE_MODE); - - log_message('debug', "Cache file written: ".$cache_path); - } - - // -------------------------------------------------------------------- - - /** - * Update/serve a cached file - * - * @access public - * @param object config class - * @param object uri class - * @return void - */ - function _display_cache(&$CFG, &$URI) - { - $cache_path = ($CFG->item('cache_path') == '') ? APPPATH.'cache/' : $CFG->item('cache_path'); - - // Build the file path. The file name is an MD5 hash of the full URI - $uri = $CFG->item('base_url'). - $CFG->item('index_page'). - $URI->uri_string; - - $filepath = $cache_path.md5($uri); - - if ( ! @file_exists($filepath)) - { - return FALSE; - } - - if ( ! $fp = @fopen($filepath, FOPEN_READ)) - { - return FALSE; - } - - flock($fp, LOCK_SH); - - $cache = ''; - if (filesize($filepath) > 0) - { - $cache = fread($fp, filesize($filepath)); - } - - flock($fp, LOCK_UN); - fclose($fp); - - // Strip out the embedded timestamp - if ( ! preg_match("/(\d+TS--->)/", $cache, $match)) - { - return FALSE; - } - - // Has the file expired? If so we'll delete it. - if (time() >= trim(str_replace('TS--->', '', $match['1']))) - { - if (is_really_writable($cache_path)) - { - @unlink($filepath); - log_message('debug', "Cache file has expired. File deleted"); - return FALSE; - } - } - - // Display the cache - $this->_display(str_replace($match['0'], '', $cache)); - log_message('debug', "Cache file is current. Sending it to browser."); - return TRUE; - } - - -} -// END Output Class - -/* End of file Output.php */ -/* Location: ./system/core/Output.php */ \ No newline at end of file diff --git a/system/core/Router.php b/system/core/Router.php deleted file mode 100755 index 6da66747..00000000 --- a/system/core/Router.php +++ /dev/null @@ -1,522 +0,0 @@ -config =& load_class('Config', 'core'); - $this->uri =& load_class('URI', 'core'); - log_message('debug', "Router Class Initialized"); - } - - // -------------------------------------------------------------------- - - /** - * Set the route mapping - * - * This function determines what should be served based on the URI request, - * as well as any "routes" that have been set in the routing config file. - * - * @access private - * @return void - */ - function _set_routing() - { - // Are query strings enabled in the config file? Normally CI doesn't utilize query strings - // since URI segments are more search-engine friendly, but they can optionally be used. - // If this feature is enabled, we will gather the directory/class/method a little differently - $segments = array(); - if ($this->config->item('enable_query_strings') === TRUE AND isset($_GET[$this->config->item('controller_trigger')])) - { - if (isset($_GET[$this->config->item('directory_trigger')])) - { - $this->set_directory(trim($this->uri->_filter_uri($_GET[$this->config->item('directory_trigger')]))); - $segments[] = $this->fetch_directory(); - } - - if (isset($_GET[$this->config->item('controller_trigger')])) - { - $this->set_class(trim($this->uri->_filter_uri($_GET[$this->config->item('controller_trigger')]))); - $segments[] = $this->fetch_class(); - } - - if (isset($_GET[$this->config->item('function_trigger')])) - { - $this->set_method(trim($this->uri->_filter_uri($_GET[$this->config->item('function_trigger')]))); - $segments[] = $this->fetch_method(); - } - } - - // Load the routes.php file. - if (defined('ENVIRONMENT') AND is_file(APPPATH.'config/'.ENVIRONMENT.'/routes.php')) - { - include(APPPATH.'config/'.ENVIRONMENT.'/routes.php'); - } - elseif (is_file(APPPATH.'config/routes.php')) - { - include(APPPATH.'config/routes.php'); - } - - $this->routes = ( ! isset($route) OR ! is_array($route)) ? array() : $route; - unset($route); - - // Set the default controller so we can display it in the event - // the URI doesn't correlated to a valid controller. - $this->default_controller = ( ! isset($this->routes['default_controller']) OR $this->routes['default_controller'] == '') ? FALSE : strtolower($this->routes['default_controller']); - - // Were there any query string segments? If so, we'll validate them and bail out since we're done. - if (count($segments) > 0) - { - return $this->_validate_request($segments); - } - - // Fetch the complete URI string - $this->uri->_fetch_uri_string(); - - // Is there a URI string? If not, the default controller specified in the "routes" file will be shown. - if ($this->uri->uri_string == '') - { - return $this->_set_default_controller(); - } - - // Do we need to remove the URL suffix? - $this->uri->_remove_url_suffix(); - - // Compile the segments into an array - $this->uri->_explode_segments(); - - // Parse any custom routing that may exist - $this->_parse_routes(); - - // Re-index the segment array so that it starts with 1 rather than 0 - $this->uri->_reindex_segments(); - } - - // -------------------------------------------------------------------- - - /** - * Set the default controller - * - * @access private - * @return void - */ - function _set_default_controller() - { - if ($this->default_controller === FALSE) - { - show_error("Unable to determine what should be displayed. A default route has not been specified in the routing file."); - } - // Is the method being specified? - if (strpos($this->default_controller, '/') !== FALSE) - { - $x = explode('/', $this->default_controller); - - $this->set_class($x[0]); - $this->set_method($x[1]); - $this->_set_request($x); - } - else - { - $this->set_class($this->default_controller); - $this->set_method('index'); - $this->_set_request(array($this->default_controller, 'index')); - } - - // re-index the routed segments array so it starts with 1 rather than 0 - $this->uri->_reindex_segments(); - - log_message('debug', "No URI present. Default controller set."); - } - - // -------------------------------------------------------------------- - - /** - * Set the Route - * - * This function takes an array of URI segments as - * input, and sets the current class/method - * - * @access private - * @param array - * @param bool - * @return void - */ - function _set_request($segments = array()) - { - $segments = $this->_validate_request($segments); - - if (count($segments) == 0) - { - return $this->_set_default_controller(); - } - - $this->set_class($segments[0]); - - if (isset($segments[1])) - { - // A standard method request - $this->set_method($segments[1]); - } - else - { - // This lets the "routed" segment array identify that the default - // index method is being used. - $segments[1] = 'index'; - } - - // Update our "routed" segment array to contain the segments. - // Note: If there is no custom routing, this array will be - // identical to $this->uri->segments - $this->uri->rsegments = $segments; - } - - // -------------------------------------------------------------------- - - /** - * Validates the supplied segments. Attempts to determine the path to - * the controller. - * - * @access private - * @param array - * @return array - */ - function _validate_request($segments) - { - if (count($segments) == 0) - { - return $segments; - } - - // Does the requested controller exist in the root folder? - if (file_exists(APPPATH.'controllers/'.$segments[0].'.php')) - { - return $segments; - } - - // Is the controller in a sub-folder? - if (is_dir(APPPATH.'controllers/'.$segments[0])) - { - // Set the directory and remove it from the segment array - $this->set_directory($segments[0]); - $segments = array_slice($segments, 1); - - if (count($segments) > 0) - { - // Does the requested controller exist in the sub-folder? - if ( ! file_exists(APPPATH.'controllers/'.$this->fetch_directory().$segments[0].'.php')) - { - if ( ! empty($this->routes['404_override'])) - { - $x = explode('/', $this->routes['404_override']); - - $this->set_directory(''); - $this->set_class($x[0]); - $this->set_method(isset($x[1]) ? $x[1] : 'index'); - - return $x; - } - else - { - show_404($this->fetch_directory().$segments[0]); - } - } - } - else - { - // Is the method being specified in the route? - if (strpos($this->default_controller, '/') !== FALSE) - { - $x = explode('/', $this->default_controller); - - $this->set_class($x[0]); - $this->set_method($x[1]); - } - else - { - $this->set_class($this->default_controller); - $this->set_method('index'); - } - - // Does the default controller exist in the sub-folder? - if ( ! file_exists(APPPATH.'controllers/'.$this->fetch_directory().$this->default_controller.'.php')) - { - $this->directory = ''; - return array(); - } - - } - - return $segments; - } - - - // If we've gotten this far it means that the URI does not correlate to a valid - // controller class. We will now see if there is an override - if ( ! empty($this->routes['404_override'])) - { - $x = explode('/', $this->routes['404_override']); - - $this->set_class($x[0]); - $this->set_method(isset($x[1]) ? $x[1] : 'index'); - - return $x; - } - - - // Nothing else to do at this point but show a 404 - show_404($segments[0]); - } - - // -------------------------------------------------------------------- - - /** - * Parse Routes - * - * This function matches any routes that may exist in - * the config/routes.php file against the URI to - * determine if the class/method need to be remapped. - * - * @access private - * @return void - */ - function _parse_routes() - { - // Turn the segment array into a URI string - $uri = implode('/', $this->uri->segments); - - // Is there a literal match? If so we're done - if (isset($this->routes[$uri])) - { - return $this->_set_request(explode('/', $this->routes[$uri])); - } - - // Loop through the route array looking for wild-cards - foreach ($this->routes as $key => $val) - { - // Convert wild-cards to RegEx - $key = str_replace(':any', '.+', str_replace(':num', '[0-9]+', $key)); - - // Does the RegEx match? - if (preg_match('#^'.$key.'$#', $uri)) - { - // Do we have a back-reference? - if (strpos($val, '$') !== FALSE AND strpos($key, '(') !== FALSE) - { - $val = preg_replace('#^'.$key.'$#', $val, $uri); - } - - return $this->_set_request(explode('/', $val)); - } - } - - // If we got this far it means we didn't encounter a - // matching route so we'll set the site default route - $this->_set_request($this->uri->segments); - } - - // -------------------------------------------------------------------- - - /** - * Set the class name - * - * @access public - * @param string - * @return void - */ - function set_class($class) - { - $this->class = str_replace(array('/', '.'), '', $class); - } - - // -------------------------------------------------------------------- - - /** - * Fetch the current class - * - * @access public - * @return string - */ - function fetch_class() - { - return $this->class; - } - - // -------------------------------------------------------------------- - - /** - * Set the method name - * - * @access public - * @param string - * @return void - */ - function set_method($method) - { - $this->method = $method; - } - - // -------------------------------------------------------------------- - - /** - * Fetch the current method - * - * @access public - * @return string - */ - function fetch_method() - { - if ($this->method == $this->fetch_class()) - { - return 'index'; - } - - return $this->method; - } - - // -------------------------------------------------------------------- - - /** - * Set the directory name - * - * @access public - * @param string - * @return void - */ - function set_directory($dir) - { - $this->directory = str_replace(array('/', '.'), '', $dir).'/'; - } - - // -------------------------------------------------------------------- - - /** - * Fetch the sub-directory (if any) that contains the requested controller class - * - * @access public - * @return string - */ - function fetch_directory() - { - return $this->directory; - } - - // -------------------------------------------------------------------- - - /** - * Set the controller overrides - * - * @access public - * @param array - * @return null - */ - function _set_overrides($routing) - { - if ( ! is_array($routing)) - { - return; - } - - if (isset($routing['directory'])) - { - $this->set_directory($routing['directory']); - } - - if (isset($routing['controller']) AND $routing['controller'] != '') - { - $this->set_class($routing['controller']); - } - - if (isset($routing['function'])) - { - $routing['function'] = ($routing['function'] == '') ? 'index' : $routing['function']; - $this->set_method($routing['function']); - } - } - - -} -// END Router Class - -/* End of file Router.php */ -/* Location: ./system/core/Router.php */ \ No newline at end of file diff --git a/system/core/Security.php b/system/core/Security.php deleted file mode 100755 index 00089d76..00000000 --- a/system/core/Security.php +++ /dev/null @@ -1,876 +0,0 @@ - '[removed]', - 'document.write' => '[removed]', - '.parentNode' => '[removed]', - '.innerHTML' => '[removed]', - 'window.location' => '[removed]', - '-moz-binding' => '[removed]', - '' => '-->', - ' '<![CDATA[', - '' => '<comment>' - ); - - /* never allowed, regex replacement */ - /** - * List of never allowed regex replacement - * - * @var array - * @access protected - */ - protected $_never_allowed_regex = array( - 'javascript\s*:', - 'expression\s*(\(|&\#40;)', // CSS and IE - 'vbscript\s*:', // IE, surprise! - 'Redirect\s+302', - "([\"'])?data\s*:[^\\1]*?base64[^\\1]*?,[^\\1]*?\\1?" - ); - - /** - * Constructor - * - * @return void - */ - public function __construct() - { - // Is CSRF protection enabled? - if (config_item('csrf_protection') === TRUE) - { - // CSRF config - foreach (array('csrf_expire', 'csrf_token_name', 'csrf_cookie_name') as $key) - { - if (FALSE !== ($val = config_item($key))) - { - $this->{'_'.$key} = $val; - } - } - - // Append application specific cookie prefix - if (config_item('cookie_prefix')) - { - $this->_csrf_cookie_name = config_item('cookie_prefix').$this->_csrf_cookie_name; - } - - // Set the CSRF hash - $this->_csrf_set_hash(); - } - - log_message('debug', "Security Class Initialized"); - } - - // -------------------------------------------------------------------- - - /** - * Verify Cross Site Request Forgery Protection - * - * @return object - */ - public function csrf_verify() - { - // If it's not a POST request we will set the CSRF cookie - if (strtoupper($_SERVER['REQUEST_METHOD']) !== 'POST') - { - return $this->csrf_set_cookie(); - } - - // Do the tokens exist in both the _POST and _COOKIE arrays? - if ( ! isset($_POST[$this->_csrf_token_name], $_COOKIE[$this->_csrf_cookie_name])) - { - $this->csrf_show_error(); - } - - // Do the tokens match? - if ($_POST[$this->_csrf_token_name] != $_COOKIE[$this->_csrf_cookie_name]) - { - $this->csrf_show_error(); - } - - // We kill this since we're done and we don't want to - // polute the _POST array - unset($_POST[$this->_csrf_token_name]); - - // Nothing should last forever - unset($_COOKIE[$this->_csrf_cookie_name]); - $this->_csrf_set_hash(); - $this->csrf_set_cookie(); - - log_message('debug', 'CSRF token verified'); - - return $this; - } - - // -------------------------------------------------------------------- - - /** - * Set Cross Site Request Forgery Protection Cookie - * - * @return object - */ - public function csrf_set_cookie() - { - $expire = time() + $this->_csrf_expire; - $secure_cookie = (config_item('cookie_secure') === TRUE) ? 1 : 0; - - if ($secure_cookie && (empty($_SERVER['HTTPS']) OR strtolower($_SERVER['HTTPS']) === 'off')) - { - return FALSE; - } - - setcookie($this->_csrf_cookie_name, $this->_csrf_hash, $expire, config_item('cookie_path'), config_item('cookie_domain'), $secure_cookie); - - log_message('debug', "CRSF cookie Set"); - - return $this; - } - - // -------------------------------------------------------------------- - - /** - * Show CSRF Error - * - * @return void - */ - public function csrf_show_error() - { - show_error('The action you have requested is not allowed.'); - } - - // -------------------------------------------------------------------- - - /** - * Get CSRF Hash - * - * Getter Method - * - * @return string self::_csrf_hash - */ - public function get_csrf_hash() - { - return $this->_csrf_hash; - } - - // -------------------------------------------------------------------- - - /** - * Get CSRF Token Name - * - * Getter Method - * - * @return string self::csrf_token_name - */ - public function get_csrf_token_name() - { - return $this->_csrf_token_name; - } - - // -------------------------------------------------------------------- - - /** - * XSS Clean - * - * Sanitizes data so that Cross Site Scripting Hacks can be - * prevented. This function does a fair amount of work but - * it is extremely thorough, designed to prevent even the - * most obscure XSS attempts. Nothing is ever 100% foolproof, - * of course, but I haven't been able to get anything passed - * the filter. - * - * Note: This function should only be used to deal with data - * upon submission. It's not something that should - * be used for general runtime processing. - * - * This function was based in part on some code and ideas I - * got from Bitflux: http://channel.bitflux.ch/wiki/XSS_Prevention - * - * To help develop this script I used this great list of - * vulnerabilities along with a few other hacks I've - * harvested from examining vulnerabilities in other programs: - * http://ha.ckers.org/xss.html - * - * @param mixed string or array - * @param bool - * @return string - */ - public function xss_clean($str, $is_image = FALSE) - { - /* - * Is the string an array? - * - */ - if (is_array($str)) - { - while (list($key) = each($str)) - { - $str[$key] = $this->xss_clean($str[$key]); - } - - return $str; - } - - /* - * Remove Invisible Characters - */ - $str = remove_invisible_characters($str); - - // Validate Entities in URLs - $str = $this->_validate_entities($str); - - /* - * URL Decode - * - * Just in case stuff like this is submitted: - * - * Google - * - * Note: Use rawurldecode() so it does not remove plus signs - * - */ - $str = rawurldecode($str); - - /* - * Convert character entities to ASCII - * - * This permits our tests below to work reliably. - * We only convert entities that are within tags since - * these are the ones that will pose security problems. - * - */ - - $str = preg_replace_callback("/[a-z]+=([\'\"]).*?\\1/si", array($this, '_convert_attribute'), $str); - - $str = preg_replace_callback("/<\w+.*?(?=>|<|$)/si", array($this, '_decode_entity'), $str); - - /* - * Remove Invisible Characters Again! - */ - $str = remove_invisible_characters($str); - - /* - * Convert all tabs to spaces - * - * This prevents strings like this: ja vascript - * NOTE: we deal with spaces between characters later. - * NOTE: preg_replace was found to be amazingly slow here on - * large blocks of data, so we use str_replace. - */ - - if (strpos($str, "\t") !== FALSE) - { - $str = str_replace("\t", ' ', $str); - } - - /* - * Capture converted string for later comparison - */ - $converted_string = $str; - - // Remove Strings that are never allowed - $str = $this->_do_never_allowed($str); - - /* - * Makes PHP tags safe - * - * Note: XML tags are inadvertently replaced too: - * - * '), array('<?', '?>'), $str); - } - - /* - * Compact any exploded words - * - * This corrects words like: j a v a s c r i p t - * These words are compacted back to their correct state. - */ - $words = array( - 'javascript', 'expression', 'vbscript', 'script', 'base64', - 'applet', 'alert', 'document', 'write', 'cookie', 'window' - ); - - foreach ($words as $word) - { - $temp = ''; - - for ($i = 0, $wordlen = strlen($word); $i < $wordlen; $i++) - { - $temp .= substr($word, $i, 1)."\s*"; - } - - // We only want to do this when it is followed by a non-word character - // That way valid stuff like "dealer to" does not become "dealerto" - $str = preg_replace_callback('#('.substr($temp, 0, -3).')(\W)#is', array($this, '_compact_exploded_words'), $str); - } - - /* - * Remove disallowed Javascript in links or img tags - * We used to do some version comparisons and use of stripos for PHP5, - * but it is dog slow compared to these simplified non-capturing - * preg_match(), especially if the pattern exists in the string - */ - do - { - $original = $str; - - if (preg_match("/]*?)(>|$)#si", array($this, '_js_link_removal'), $str); - } - - if (preg_match("/]*?)(\s?/?>|$)#si", array($this, '_js_img_removal'), $str); - } - - if (preg_match("/script/i", $str) OR preg_match("/xss/i", $str)) - { - $str = preg_replace("#<(/*)(script|xss)(.*?)\>#si", '[removed]', $str); - } - } - while($original != $str); - - unset($original); - - // Remove evil attributes such as style, onclick and xmlns - $str = $this->_remove_evil_attributes($str, $is_image); - - /* - * Sanitize naughty HTML elements - * - * If a tag containing any of the words in the list - * below is found, the tag gets converted to entities. - * - * So this: - * Becomes: <blink> - */ - $naughty = 'alert|applet|audio|basefont|base|behavior|bgsound|blink|body|embed|expression|form|frameset|frame|head|html|ilayer|iframe|input|isindex|layer|link|meta|object|plaintext|style|script|textarea|title|video|xml|xss'; - $str = preg_replace_callback('#<(/*\s*)('.$naughty.')([^><]*)([><]*)#is', array($this, '_sanitize_naughty_html'), $str); - - /* - * Sanitize naughty scripting elements - * - * Similar to above, only instead of looking for - * tags it looks for PHP and JavaScript commands - * that are disallowed. Rather than removing the - * code, it simply converts the parenthesis to entities - * rendering the code un-executable. - * - * For example: eval('some code') - * Becomes: eval('some code') - */ - $str = preg_replace('#(alert|cmd|passthru|eval|exec|expression|system|fopen|fsockopen|file|file_get_contents|readfile|unlink)(\s*)\((.*?)\)#si', "\\1\\2(\\3)", $str); - - - // Final clean up - // This adds a bit of extra precaution in case - // something got through the above filters - $str = $this->_do_never_allowed($str); - - /* - * Images are Handled in a Special Way - * - Essentially, we want to know that after all of the character - * conversion is done whether any unwanted, likely XSS, code was found. - * If not, we return TRUE, as the image is clean. - * However, if the string post-conversion does not matched the - * string post-removal of XSS, then it fails, as there was unwanted XSS - * code found and removed/changed during processing. - */ - - if ($is_image === TRUE) - { - return ($str == $converted_string) ? TRUE: FALSE; - } - - log_message('debug', "XSS Filtering completed"); - return $str; - } - - // -------------------------------------------------------------------- - - /** - * Random Hash for protecting URLs - * - * @return string - */ - public function xss_hash() - { - if ($this->_xss_hash == '') - { - mt_srand(); - $this->_xss_hash = md5(time() + mt_rand(0, 1999999999)); - } - - return $this->_xss_hash; - } - - // -------------------------------------------------------------------- - - /** - * HTML Entities Decode - * - * This function is a replacement for html_entity_decode() - * - * The reason we are not using html_entity_decode() by itself is because - * while it is not technically correct to leave out the semicolon - * at the end of an entity most browsers will still interpret the entity - * correctly. html_entity_decode() does not convert entities without - * semicolons, so we are left with our own little solution here. Bummer. - * - * @param string - * @param string - * @return string - */ - public function entity_decode($str, $charset='UTF-8') - { - if (stristr($str, '&') === FALSE) - { - return $str; - } - - $str = html_entity_decode($str, ENT_COMPAT, $charset); - $str = preg_replace('~&#x(0*[0-9a-f]{2,5})~ei', 'chr(hexdec("\\1"))', $str); - return preg_replace('~&#([0-9]{2,4})~e', 'chr(\\1)', $str); - } - - // -------------------------------------------------------------------- - - /** - * Filename Security - * - * @param string - * @param bool - * @return string - */ - public function sanitize_filename($str, $relative_path = FALSE) - { - $bad = array( - "../", - "", - "<", - ">", - "'", - '"', - '&', - '$', - '#', - '{', - '}', - '[', - ']', - '=', - ';', - '?', - "%20", - "%22", - "%3c", // < - "%253c", // < - "%3e", // > - "%0e", // > - "%28", // ( - "%29", // ) - "%2528", // ( - "%26", // & - "%24", // $ - "%3f", // ? - "%3b", // ; - "%3d" // = - ); - - if ( ! $relative_path) - { - $bad[] = './'; - $bad[] = '/'; - } - - $str = remove_invisible_characters($str, FALSE); - return stripslashes(str_replace($bad, '', $str)); - } - - // ---------------------------------------------------------------- - - /** - * Compact Exploded Words - * - * Callback function for xss_clean() to remove whitespace from - * things like j a v a s c r i p t - * - * @param type - * @return type - */ - protected function _compact_exploded_words($matches) - { - return preg_replace('/\s+/s', '', $matches[1]).$matches[2]; - } - - // -------------------------------------------------------------------- - - /* - * Remove Evil HTML Attributes (like evenhandlers and style) - * - * It removes the evil attribute and either: - * - Everything up until a space - * For example, everything between the pipes: - * - * - Everything inside the quotes - * For example, everything between the pipes: - * - * - * @param string $str The string to check - * @param boolean $is_image TRUE if this is an image - * @return string The string with the evil attributes removed - */ - protected function _remove_evil_attributes($str, $is_image) - { - // All javascript event handlers (e.g. onload, onclick, onmouseover), style, and xmlns - $evil_attributes = array('on\w*', 'style', 'xmlns', 'formaction'); - - if ($is_image === TRUE) - { - /* - * Adobe Photoshop puts XML metadata into JFIF images, - * including namespacing, so we have to allow this for images. - */ - unset($evil_attributes[array_search('xmlns', $evil_attributes)]); - } - - do { - $count = 0; - $attribs = array(); - - // find occurrences of illegal attribute strings without quotes - preg_match_all('/('.implode('|', $evil_attributes).')\s*=\s*([^\s>]*)/is', $str, $matches, PREG_SET_ORDER); - - foreach ($matches as $attr) - { - - $attribs[] = preg_quote($attr[0], '/'); - } - - // find occurrences of illegal attribute strings with quotes (042 and 047 are octal quotes) - preg_match_all("/(".implode('|', $evil_attributes).")\s*=\s*(\042|\047)([^\\2]*?)(\\2)/is", $str, $matches, PREG_SET_ORDER); - - foreach ($matches as $attr) - { - $attribs[] = preg_quote($attr[0], '/'); - } - - // replace illegal attribute strings that are inside an html tag - if (count($attribs) > 0) - { - $str = preg_replace("/<(\/?[^><]+?)([^A-Za-z<>\-])(.*?)(".implode('|', $attribs).")(.*?)([\s><])([><]*)/i", '<$1 $3$5$6$7', $str, -1, $count); - } - - } while ($count); - - return $str; - } - - // -------------------------------------------------------------------- - - /** - * Sanitize Naughty HTML - * - * Callback function for xss_clean() to remove naughty HTML elements - * - * @param array - * @return string - */ - protected function _sanitize_naughty_html($matches) - { - // encode opening brace - $str = '<'.$matches[1].$matches[2].$matches[3]; - - // encode captured opening or closing brace to prevent recursive vectors - $str .= str_replace(array('>', '<'), array('>', '<'), - $matches[4]); - - return $str; - } - - // -------------------------------------------------------------------- - - /** - * JS Link Removal - * - * Callback function for xss_clean() to sanitize links - * This limits the PCRE backtracks, making it more performance friendly - * and prevents PREG_BACKTRACK_LIMIT_ERROR from being triggered in - * PHP 5.2+ on link-heavy strings - * - * @param array - * @return string - */ - protected function _js_link_removal($match) - { - return str_replace( - $match[1], - preg_replace( - '#href=.*?(alert\(|alert&\#40;|javascript\:|livescript\:|mocha\:|charset\=|window\.|document\.|\.cookie|_filter_attributes(str_replace(array('<', '>'), '', $match[1])) - ), - $match[0] - ); - } - - // -------------------------------------------------------------------- - - /** - * JS Image Removal - * - * Callback function for xss_clean() to sanitize image tags - * This limits the PCRE backtracks, making it more performance friendly - * and prevents PREG_BACKTRACK_LIMIT_ERROR from being triggered in - * PHP 5.2+ on image tag heavy strings - * - * @param array - * @return string - */ - protected function _js_img_removal($match) - { - return str_replace( - $match[1], - preg_replace( - '#src=.*?(alert\(|alert&\#40;|javascript\:|livescript\:|mocha\:|charset\=|window\.|document\.|\.cookie|_filter_attributes(str_replace(array('<', '>'), '', $match[1])) - ), - $match[0] - ); - } - - // -------------------------------------------------------------------- - - /** - * Attribute Conversion - * - * Used as a callback for XSS Clean - * - * @param array - * @return string - */ - protected function _convert_attribute($match) - { - return str_replace(array('>', '<', '\\'), array('>', '<', '\\\\'), $match[0]); - } - - // -------------------------------------------------------------------- - - /** - * Filter Attributes - * - * Filters tag attributes for consistency and safety - * - * @param string - * @return string - */ - protected function _filter_attributes($str) - { - $out = ''; - - if (preg_match_all('#\s*[a-z\-]+\s*=\s*(\042|\047)([^\\1]*?)\\1#is', $str, $matches)) - { - foreach ($matches[0] as $match) - { - $out .= preg_replace("#/\*.*?\*/#s", '', $match); - } - } - - return $out; - } - - // -------------------------------------------------------------------- - - /** - * HTML Entity Decode Callback - * - * Used as a callback for XSS Clean - * - * @param array - * @return string - */ - protected function _decode_entity($match) - { - return $this->entity_decode($match[0], strtoupper(config_item('charset'))); - } - - // -------------------------------------------------------------------- - - /** - * Validate URL entities - * - * Called by xss_clean() - * - * @param string - * @return string - */ - protected function _validate_entities($str) - { - /* - * Protect GET variables in URLs - */ - - // 901119URL5918AMP18930PROTECT8198 - - $str = preg_replace('|\&([a-z\_0-9\-]+)\=([a-z\_0-9\-]+)|i', $this->xss_hash()."\\1=\\2", $str); - - /* - * Validate standard character entities - * - * Add a semicolon if missing. We do this to enable - * the conversion of entities to ASCII later. - * - */ - $str = preg_replace('#(&\#?[0-9a-z]{2,})([\x00-\x20])*;?#i', "\\1;\\2", $str); - - /* - * Validate UTF16 two byte encoding (x00) - * - * Just as above, adds a semicolon if missing. - * - */ - $str = preg_replace('#(&\#x?)([0-9A-F]+);?#i',"\\1\\2;",$str); - - /* - * Un-Protect GET variables in URLs - */ - $str = str_replace($this->xss_hash(), '&', $str); - - return $str; - } - - // ---------------------------------------------------------------------- - - /** - * Do Never Allowed - * - * A utility function for xss_clean() - * - * @param string - * @return string - */ - protected function _do_never_allowed($str) - { - $str = str_replace(array_keys($this->_never_allowed_str), $this->_never_allowed_str, $str); - - foreach ($this->_never_allowed_regex as $regex) - { - $str = preg_replace('#'.$regex.'#is', '[removed]', $str); - } - - return $str; - } - - // -------------------------------------------------------------------- - - /** - * Set Cross Site Request Forgery Protection Cookie - * - * @return string - */ - protected function _csrf_set_hash() - { - if ($this->_csrf_hash == '') - { - // If the cookie exists we will use it's value. - // We don't necessarily want to regenerate it with - // each page load since a page could contain embedded - // sub-pages causing this feature to fail - if (isset($_COOKIE[$this->_csrf_cookie_name]) && - preg_match('#^[0-9a-f]{32}$#iS', $_COOKIE[$this->_csrf_cookie_name]) === 1) - { - return $this->_csrf_hash = $_COOKIE[$this->_csrf_cookie_name]; - } - - return $this->_csrf_hash = md5(uniqid(rand(), TRUE)); - } - - return $this->_csrf_hash; - } - -} - -/* End of file Security.php */ -/* Location: ./system/libraries/Security.php */ \ No newline at end of file diff --git a/system/core/URI.php b/system/core/URI.php deleted file mode 100755 index a3ae20cc..00000000 --- a/system/core/URI.php +++ /dev/null @@ -1,654 +0,0 @@ -config =& load_class('Config', 'core'); - log_message('debug', "URI Class Initialized"); - } - - - // -------------------------------------------------------------------- - - /** - * Get the URI String - * - * @access private - * @return string - */ - function _fetch_uri_string() - { - if (strtoupper($this->config->item('uri_protocol')) == 'AUTO') - { - // Is the request coming from the command line? - if (php_sapi_name() == 'cli' or defined('STDIN')) - { - $this->_set_uri_string($this->_parse_cli_args()); - return; - } - - // Let's try the REQUEST_URI first, this will work in most situations - if ($uri = $this->_detect_uri()) - { - $this->_set_uri_string($uri); - return; - } - - // Is there a PATH_INFO variable? - // Note: some servers seem to have trouble with getenv() so we'll test it two ways - $path = (isset($_SERVER['PATH_INFO'])) ? $_SERVER['PATH_INFO'] : @getenv('PATH_INFO'); - if (trim($path, '/') != '' && $path != "/".SELF) - { - $this->_set_uri_string($path); - return; - } - - // No PATH_INFO?... What about QUERY_STRING? - $path = (isset($_SERVER['QUERY_STRING'])) ? $_SERVER['QUERY_STRING'] : @getenv('QUERY_STRING'); - if (trim($path, '/') != '') - { - $this->_set_uri_string($path); - return; - } - - // As a last ditch effort lets try using the $_GET array - if (is_array($_GET) && count($_GET) == 1 && trim(key($_GET), '/') != '') - { - $this->_set_uri_string(key($_GET)); - return; - } - - // We've exhausted all our options... - $this->uri_string = ''; - return; - } - - $uri = strtoupper($this->config->item('uri_protocol')); - - if ($uri == 'REQUEST_URI') - { - $this->_set_uri_string($this->_detect_uri()); - return; - } - elseif ($uri == 'CLI') - { - $this->_set_uri_string($this->_parse_cli_args()); - return; - } - - $path = (isset($_SERVER[$uri])) ? $_SERVER[$uri] : @getenv($uri); - $this->_set_uri_string($path); - } - - // -------------------------------------------------------------------- - - /** - * Set the URI String - * - * @access public - * @param string - * @return string - */ - function _set_uri_string($str) - { - // Filter out control characters - $str = remove_invisible_characters($str, FALSE); - - // If the URI contains only a slash we'll kill it - $this->uri_string = ($str == '/') ? '' : $str; - } - - // -------------------------------------------------------------------- - - /** - * Detects the URI - * - * This function will detect the URI automatically and fix the query string - * if necessary. - * - * @access private - * @return string - */ - private function _detect_uri() - { - if ( ! isset($_SERVER['REQUEST_URI']) OR ! isset($_SERVER['SCRIPT_NAME'])) - { - return ''; - } - - $uri = $_SERVER['REQUEST_URI']; - if (strpos($uri, $_SERVER['SCRIPT_NAME']) === 0) - { - $uri = substr($uri, strlen($_SERVER['SCRIPT_NAME'])); - } - elseif (strpos($uri, dirname($_SERVER['SCRIPT_NAME'])) === 0) - { - $uri = substr($uri, strlen(dirname($_SERVER['SCRIPT_NAME']))); - } - - // This section ensures that even on servers that require the URI to be in the query string (Nginx) a correct - // URI is found, and also fixes the QUERY_STRING server var and $_GET array. - if (strncmp($uri, '?/', 2) === 0) - { - $uri = substr($uri, 2); - } - $parts = preg_split('#\?#i', $uri, 2); - $uri = $parts[0]; - if (isset($parts[1])) - { - $_SERVER['QUERY_STRING'] = $parts[1]; - parse_str($_SERVER['QUERY_STRING'], $_GET); - } - else - { - $_SERVER['QUERY_STRING'] = ''; - $_GET = array(); - } - - if ($uri == '/' || empty($uri)) - { - return '/'; - } - - $uri = parse_url(http://github.com/$uri,%20PHP_URL_PATH); - - // Do some final cleaning of the URI and return it - return str_replace(array('//', '../'), '/', trim($uri, '/')); - } - - // -------------------------------------------------------------------- - - /** - * Parse cli arguments - * - * Take each command line argument and assume it is a URI segment. - * - * @access private - * @return string - */ - private function _parse_cli_args() - { - $args = array_slice($_SERVER['argv'], 1); - - return $args ? '/' . implode('/', $args) : ''; - } - - // -------------------------------------------------------------------- - - /** - * Filter segments for malicious characters - * - * @access private - * @param string - * @return string - */ - function _filter_uri($str) - { - if ($str != '' && $this->config->item('permitted_uri_chars') != '' && $this->config->item('enable_query_strings') == FALSE) - { - // preg_quote() in PHP 5.3 escapes -, so the str_replace() and addition of - to preg_quote() is to maintain backwards - // compatibility as many are unaware of how characters in the permitted_uri_chars will be parsed as a regex pattern - if ( ! preg_match("|^[".str_replace(array('\\-', '\-'), '-', preg_quote($this->config->item('permitted_uri_chars'), '-'))."]+$|i", $str)) - { - show_error('The URI you submitted has disallowed characters.', 400); - } - } - - // Convert programatic characters to entities - $bad = array('$', '(', ')', '%28', '%29'); - $good = array('$', '(', ')', '(', ')'); - - return str_replace($bad, $good, $str); - } - - // -------------------------------------------------------------------- - - /** - * Remove the suffix from the URL if needed - * - * @access private - * @return void - */ - function _remove_url_suffix() - { - if ($this->config->item('url_suffix') != "") - { - $this->uri_string = preg_replace("|".preg_quote($this->config->item('url_suffix'))."$|", "", $this->uri_string); - } - } - - // -------------------------------------------------------------------- - - /** - * Explode the URI Segments. The individual segments will - * be stored in the $this->segments array. - * - * @access private - * @return void - */ - function _explode_segments() - { - foreach (explode("/", preg_replace("|/*(.+?)/*$|", "\\1", $this->uri_string)) as $val) - { - // Filter segments for security - $val = trim($this->_filter_uri($val)); - - if ($val != '') - { - $this->segments[] = $val; - } - } - } - - // -------------------------------------------------------------------- - /** - * Re-index Segments - * - * This function re-indexes the $this->segment array so that it - * starts at 1 rather than 0. Doing so makes it simpler to - * use functions like $this->uri->segment(n) since there is - * a 1:1 relationship between the segment array and the actual segments. - * - * @access private - * @return void - */ - function _reindex_segments() - { - array_unshift($this->segments, NULL); - array_unshift($this->rsegments, NULL); - unset($this->segments[0]); - unset($this->rsegments[0]); - } - - // -------------------------------------------------------------------- - - /** - * Fetch a URI Segment - * - * This function returns the URI segment based on the number provided. - * - * @access public - * @param integer - * @param bool - * @return string - */ - function segment($n, $no_result = FALSE) - { - return ( ! isset($this->segments[$n])) ? $no_result : $this->segments[$n]; - } - - // -------------------------------------------------------------------- - - /** - * Fetch a URI "routed" Segment - * - * This function returns the re-routed URI segment (assuming routing rules are used) - * based on the number provided. If there is no routing this function returns the - * same result as $this->segment() - * - * @access public - * @param integer - * @param bool - * @return string - */ - function rsegment($n, $no_result = FALSE) - { - return ( ! isset($this->rsegments[$n])) ? $no_result : $this->rsegments[$n]; - } - - // -------------------------------------------------------------------- - - /** - * Generate a key value pair from the URI string - * - * This function generates and associative array of URI data starting - * at the supplied segment. For example, if this is your URI: - * - * example.com/user/search/name/joe/location/UK/gender/male - * - * You can use this function to generate an array with this prototype: - * - * array ( - * name => joe - * location => UK - * gender => male - * ) - * - * @access public - * @param integer the starting segment number - * @param array an array of default values - * @return array - */ - function uri_to_assoc($n = 3, $default = array()) - { - return $this->_uri_to_assoc($n, $default, 'segment'); - } - /** - * Identical to above only it uses the re-routed segment array - * - * @access public - * @param integer the starting segment number - * @param array an array of default values - * @return array - * - */ - function ruri_to_assoc($n = 3, $default = array()) - { - return $this->_uri_to_assoc($n, $default, 'rsegment'); - } - - // -------------------------------------------------------------------- - - /** - * Generate a key value pair from the URI string or Re-routed URI string - * - * @access private - * @param integer the starting segment number - * @param array an array of default values - * @param string which array we should use - * @return array - */ - function _uri_to_assoc($n = 3, $default = array(), $which = 'segment') - { - if ($which == 'segment') - { - $total_segments = 'total_segments'; - $segment_array = 'segment_array'; - } - else - { - $total_segments = 'total_rsegments'; - $segment_array = 'rsegment_array'; - } - - if ( ! is_numeric($n)) - { - return $default; - } - - if (isset($this->keyval[$n])) - { - return $this->keyval[$n]; - } - - if ($this->$total_segments() < $n) - { - if (count($default) == 0) - { - return array(); - } - - $retval = array(); - foreach ($default as $val) - { - $retval[$val] = FALSE; - } - return $retval; - } - - $segments = array_slice($this->$segment_array(), ($n - 1)); - - $i = 0; - $lastval = ''; - $retval = array(); - foreach ($segments as $seg) - { - if ($i % 2) - { - $retval[$lastval] = $seg; - } - else - { - $retval[$seg] = FALSE; - $lastval = $seg; - } - - $i++; - } - - if (count($default) > 0) - { - foreach ($default as $val) - { - if ( ! array_key_exists($val, $retval)) - { - $retval[$val] = FALSE; - } - } - } - - // Cache the array for reuse - $this->keyval[$n] = $retval; - return $retval; - } - - // -------------------------------------------------------------------- - - /** - * Generate a URI string from an associative array - * - * - * @access public - * @param array an associative array of key/values - * @return array - */ - function assoc_to_uri($array) - { - $temp = array(); - foreach ((array)$array as $key => $val) - { - $temp[] = $key; - $temp[] = $val; - } - - return implode('/', $temp); - } - - // -------------------------------------------------------------------- - - /** - * Fetch a URI Segment and add a trailing slash - * - * @access public - * @param integer - * @param string - * @return string - */ - function slash_segment($n, $where = 'trailing') - { - return $this->_slash_segment($n, $where, 'segment'); - } - - // -------------------------------------------------------------------- - - /** - * Fetch a URI Segment and add a trailing slash - * - * @access public - * @param integer - * @param string - * @return string - */ - function slash_rsegment($n, $where = 'trailing') - { - return $this->_slash_segment($n, $where, 'rsegment'); - } - - // -------------------------------------------------------------------- - - /** - * Fetch a URI Segment and add a trailing slash - helper function - * - * @access private - * @param integer - * @param string - * @param string - * @return string - */ - function _slash_segment($n, $where = 'trailing', $which = 'segment') - { - $leading = '/'; - $trailing = '/'; - - if ($where == 'trailing') - { - $leading = ''; - } - elseif ($where == 'leading') - { - $trailing = ''; - } - - return $leading.$this->$which($n).$trailing; - } - - // -------------------------------------------------------------------- - - /** - * Segment Array - * - * @access public - * @return array - */ - function segment_array() - { - return $this->segments; - } - - // -------------------------------------------------------------------- - - /** - * Routed Segment Array - * - * @access public - * @return array - */ - function rsegment_array() - { - return $this->rsegments; - } - - // -------------------------------------------------------------------- - - /** - * Total number of segments - * - * @access public - * @return integer - */ - function total_segments() - { - return count($this->segments); - } - - // -------------------------------------------------------------------- - - /** - * Total number of routed segments - * - * @access public - * @return integer - */ - function total_rsegments() - { - return count($this->rsegments); - } - - // -------------------------------------------------------------------- - - /** - * Fetch the entire URI string - * - * @access public - * @return string - */ - function uri_string() - { - return $this->uri_string; - } - - - // -------------------------------------------------------------------- - - /** - * Fetch the entire Re-routed URI string - * - * @access public - * @return string - */ - function ruri_string() - { - return '/'.implode('/', $this->rsegment_array()); - } - -} -// END URI Class - -/* End of file URI.php */ -/* Location: ./system/core/URI.php */ \ No newline at end of file diff --git a/system/core/Utf8.php b/system/core/Utf8.php deleted file mode 100644 index 2a27d1f3..00000000 --- a/system/core/Utf8.php +++ /dev/null @@ -1,165 +0,0 @@ -item('charset') == 'UTF-8' // Application charset must be UTF-8 - ) - { - log_message('debug', "UTF-8 Support Enabled"); - - define('UTF8_ENABLED', TRUE); - - // set internal encoding for multibyte string functions if necessary - // and set a flag so we don't have to repeatedly use extension_loaded() - // or function_exists() - if (extension_loaded('mbstring')) - { - define('MB_ENABLED', TRUE); - mb_internal_encoding('UTF-8'); - } - else - { - define('MB_ENABLED', FALSE); - } - } - else - { - log_message('debug', "UTF-8 Support Disabled"); - define('UTF8_ENABLED', FALSE); - } - } - - // -------------------------------------------------------------------- - - /** - * Clean UTF-8 strings - * - * Ensures strings are UTF-8 - * - * @access public - * @param string - * @return string - */ - function clean_string($str) - { - if ($this->_is_ascii($str) === FALSE) - { - $str = @iconv('UTF-8', 'UTF-8//IGNORE', $str); - } - - return $str; - } - - // -------------------------------------------------------------------- - - /** - * Remove ASCII control characters - * - * Removes all ASCII control characters except horizontal tabs, - * line feeds, and carriage returns, as all others can cause - * problems in XML - * - * @access public - * @param string - * @return string - */ - function safe_ascii_for_xml($str) - { - return remove_invisible_characters($str, FALSE); - } - - // -------------------------------------------------------------------- - - /** - * Convert to UTF-8 - * - * Attempts to convert a string to UTF-8 - * - * @access public - * @param string - * @param string - input encoding - * @return string - */ - function convert_to_utf8($str, $encoding) - { - if (function_exists('iconv')) - { - $str = @iconv($encoding, 'UTF-8', $str); - } - elseif (function_exists('mb_convert_encoding')) - { - $str = @mb_convert_encoding($str, 'UTF-8', $encoding); - } - else - { - return FALSE; - } - - return $str; - } - - // -------------------------------------------------------------------- - - /** - * Is ASCII? - * - * Tests if a string is standard 7-bit ASCII or not - * - * @access public - * @param string - * @return bool - */ - function _is_ascii($str) - { - return (preg_match('/[^\x00-\x7F]/S', $str) == 0); - } - - // -------------------------------------------------------------------- - -} -// End Utf8 Class - -/* End of file Utf8.php */ -/* Location: ./system/core/Utf8.php */ \ No newline at end of file diff --git a/system/core/index.html b/system/core/index.html deleted file mode 100644 index c942a79c..00000000 --- a/system/core/index.html +++ /dev/null @@ -1,10 +0,0 @@ - - - 403 Forbidden - - - -

Directory access is forbidden.

- - - \ No newline at end of file diff --git a/system/database/DB.php b/system/database/DB.php deleted file mode 100755 index 8314d3b9..00000000 --- a/system/database/DB.php +++ /dev/null @@ -1,162 +0,0 @@ - $dns['scheme'], - 'hostname' => (isset($dns['host'])) ? rawurldecode($dns['host']) : '', - 'username' => (isset($dns['user'])) ? rawurldecode($dns['user']) : '', - 'password' => (isset($dns['pass'])) ? rawurldecode($dns['pass']) : '', - 'database' => (isset($dns['path'])) ? rawurldecode(substr($dns['path'], 1)) : '' - ); - - // were additional config items set? - if (isset($dns['query'])) - { - parse_str($dns['query'], $extra); - - foreach ($extra as $key => $val) - { - // booleans please - if (strtoupper($val) == "TRUE") - { - $val = TRUE; - } - elseif (strtoupper($val) == "FALSE") - { - $val = FALSE; - } - - $params[$key] = $val; - } - } - } - - // No DB specified yet? Beat them senseless... - if ( ! isset($params['dbdriver']) OR $params['dbdriver'] == '') - { - show_error('You have not selected a database type to connect to.'); - } - - // Load the DB classes. Note: Since the active record class is optional - // we need to dynamically create a class that extends proper parent class - // based on whether we're using the active record class or not. - // Kudos to Paul for discovering this clever use of eval() - - if ($active_record_override !== NULL) - { - $active_record = $active_record_override; - } - - require_once(BASEPATH.'database/DB_driver.php'); - - if ( ! isset($active_record) OR $active_record == TRUE) - { - require_once(BASEPATH.'database/DB_active_rec.php'); - - if ( ! class_exists('CI_DB')) - { - eval('class CI_DB extends CI_DB_active_record { }'); - } - } - else - { - if ( ! class_exists('CI_DB')) - { - eval('class CI_DB extends CI_DB_driver { }'); - } - } - - require_once(BASEPATH.'database/drivers/'.$params['dbdriver'].'/'.$params['dbdriver'].'_driver.php'); - - // Instantiate the DB adapter - $driver = 'CI_DB_'.$params['dbdriver'].'_driver'; - $DB = new $driver($params); - - if ($DB->autoinit == TRUE) - { - $DB->initialize(); - } - - if (isset($params['stricton']) && $params['stricton'] == TRUE) - { - $DB->query('SET SESSION sql_mode="STRICT_ALL_TABLES"'); - } - - return $DB; -} - - - -/* End of file DB.php */ -/* Location: ./system/database/DB.php */ \ No newline at end of file diff --git a/system/database/DB_active_rec.php b/system/database/DB_active_rec.php deleted file mode 100644 index 10febb1f..00000000 --- a/system/database/DB_active_rec.php +++ /dev/null @@ -1,2045 +0,0 @@ -ar_select[] = $val; - $this->ar_no_escape[] = $escape; - - if ($this->ar_caching === TRUE) - { - $this->ar_cache_select[] = $val; - $this->ar_cache_exists[] = 'select'; - $this->ar_cache_no_escape[] = $escape; - } - } - } - return $this; - } - - // -------------------------------------------------------------------- - - /** - * Select Max - * - * Generates a SELECT MAX(field) portion of a query - * - * @param string the field - * @param string an alias - * @return object - */ - public function select_max($select = '', $alias = '') - { - return $this->_max_min_avg_sum($select, $alias, 'MAX'); - } - - // -------------------------------------------------------------------- - - /** - * Select Min - * - * Generates a SELECT MIN(field) portion of a query - * - * @param string the field - * @param string an alias - * @return object - */ - public function select_min($select = '', $alias = '') - { - return $this->_max_min_avg_sum($select, $alias, 'MIN'); - } - - // -------------------------------------------------------------------- - - /** - * Select Average - * - * Generates a SELECT AVG(field) portion of a query - * - * @param string the field - * @param string an alias - * @return object - */ - public function select_avg($select = '', $alias = '') - { - return $this->_max_min_avg_sum($select, $alias, 'AVG'); - } - - // -------------------------------------------------------------------- - - /** - * Select Sum - * - * Generates a SELECT SUM(field) portion of a query - * - * @param string the field - * @param string an alias - * @return object - */ - public function select_sum($select = '', $alias = '') - { - return $this->_max_min_avg_sum($select, $alias, 'SUM'); - } - - // -------------------------------------------------------------------- - - /** - * Processing Function for the four functions above: - * - * select_max() - * select_min() - * select_avg() - * select_sum() - * - * @param string the field - * @param string an alias - * @return object - */ - protected function _max_min_avg_sum($select = '', $alias = '', $type = 'MAX') - { - if ( ! is_string($select) OR $select == '') - { - $this->display_error('db_invalid_query'); - } - - $type = strtoupper($type); - - if ( ! in_array($type, array('MAX', 'MIN', 'AVG', 'SUM'))) - { - show_error('Invalid function type: '.$type); - } - - if ($alias == '') - { - $alias = $this->_create_alias_from_table(trim($select)); - } - - $sql = $type.'('.$this->_protect_identifiers(trim($select)).') AS '.$alias; - - $this->ar_select[] = $sql; - - if ($this->ar_caching === TRUE) - { - $this->ar_cache_select[] = $sql; - $this->ar_cache_exists[] = 'select'; - } - - return $this; - } - - // -------------------------------------------------------------------- - - /** - * Determines the alias name based on the table - * - * @param string - * @return string - */ - protected function _create_alias_from_table($item) - { - if (strpos($item, '.') !== FALSE) - { - return end(explode('.', $item)); - } - - return $item; - } - - // -------------------------------------------------------------------- - - /** - * DISTINCT - * - * Sets a flag which tells the query string compiler to add DISTINCT - * - * @param bool - * @return object - */ - public function distinct($val = TRUE) - { - $this->ar_distinct = (is_bool($val)) ? $val : TRUE; - return $this; - } - - // -------------------------------------------------------------------- - - /** - * From - * - * Generates the FROM portion of the query - * - * @param mixed can be a string or array - * @return object - */ - public function from($from) - { - foreach ((array) $from as $val) - { - if (strpos($val, ',') !== FALSE) - { - foreach (explode(',', $val) as $v) - { - $v = trim($v); - $this->_track_aliases($v); - - $this->ar_from[] = $this->_protect_identifiers($v, TRUE, NULL, FALSE); - - if ($this->ar_caching === TRUE) - { - $this->ar_cache_from[] = $this->_protect_identifiers($v, TRUE, NULL, FALSE); - $this->ar_cache_exists[] = 'from'; - } - } - - } - else - { - $val = trim($val); - - // Extract any aliases that might exist. We use this information - // in the _protect_identifiers to know whether to add a table prefix - $this->_track_aliases($val); - - $this->ar_from[] = $this->_protect_identifiers($val, TRUE, NULL, FALSE); - - if ($this->ar_caching === TRUE) - { - $this->ar_cache_from[] = $this->_protect_identifiers($val, TRUE, NULL, FALSE); - $this->ar_cache_exists[] = 'from'; - } - } - } - - return $this; - } - - // -------------------------------------------------------------------- - - /** - * Join - * - * Generates the JOIN portion of the query - * - * @param string - * @param string the join condition - * @param string the type of join - * @return object - */ - public function join($table, $cond, $type = '') - { - if ($type != '') - { - $type = strtoupper(trim($type)); - - if ( ! in_array($type, array('LEFT', 'RIGHT', 'OUTER', 'INNER', 'LEFT OUTER', 'RIGHT OUTER'))) - { - $type = ''; - } - else - { - $type .= ' '; - } - } - - // Extract any aliases that might exist. We use this information - // in the _protect_identifiers to know whether to add a table prefix - $this->_track_aliases($table); - - // Strip apart the condition and protect the identifiers - if (preg_match('/([\w\.]+)([\W\s]+)(.+)/', $cond, $match)) - { - $match[1] = $this->_protect_identifiers($match[1]); - $match[3] = $this->_protect_identifiers($match[3]); - - $cond = $match[1].$match[2].$match[3]; - } - - // Assemble the JOIN statement - $join = $type.'JOIN '.$this->_protect_identifiers($table, TRUE, NULL, FALSE).' ON '.$cond; - - $this->ar_join[] = $join; - if ($this->ar_caching === TRUE) - { - $this->ar_cache_join[] = $join; - $this->ar_cache_exists[] = 'join'; - } - - return $this; - } - - // -------------------------------------------------------------------- - - /** - * Where - * - * Generates the WHERE portion of the query. Separates - * multiple calls with AND - * - * @param mixed - * @param mixed - * @return object - */ - public function where($key, $value = NULL, $escape = TRUE) - { - return $this->_where($key, $value, 'AND ', $escape); - } - - // -------------------------------------------------------------------- - - /** - * OR Where - * - * Generates the WHERE portion of the query. Separates - * multiple calls with OR - * - * @param mixed - * @param mixed - * @return object - */ - public function or_where($key, $value = NULL, $escape = TRUE) - { - return $this->_where($key, $value, 'OR ', $escape); - } - - // -------------------------------------------------------------------- - - /** - * Where - * - * Called by where() or or_where() - * - * @param mixed - * @param mixed - * @param string - * @return object - */ - protected function _where($key, $value = NULL, $type = 'AND ', $escape = NULL) - { - if ( ! is_array($key)) - { - $key = array($key => $value); - } - - // If the escape value was not set will will base it on the global setting - if ( ! is_bool($escape)) - { - $escape = $this->_protect_identifiers; - } - - foreach ($key as $k => $v) - { - $prefix = (count($this->ar_where) == 0 AND count($this->ar_cache_where) == 0) ? '' : $type; - - if (is_null($v) && ! $this->_has_operator($k)) - { - // value appears not to have been set, assign the test to IS NULL - $k .= ' IS NULL'; - } - - if ( ! is_null($v)) - { - if ($escape === TRUE) - { - $k = $this->_protect_identifiers($k, FALSE, $escape); - - $v = ' '.$this->escape($v); - } - - if ( ! $this->_has_operator($k)) - { - $k .= ' = '; - } - } - else - { - $k = $this->_protect_identifiers($k, FALSE, $escape); - } - - $this->ar_where[] = $prefix.$k.$v; - - if ($this->ar_caching === TRUE) - { - $this->ar_cache_where[] = $prefix.$k.$v; - $this->ar_cache_exists[] = 'where'; - } - - } - - return $this; - } - - // -------------------------------------------------------------------- - - /** - * Where_in - * - * Generates a WHERE field IN ('item', 'item') SQL query joined with - * AND if appropriate - * - * @param string The field to search - * @param array The values searched on - * @return object - */ - public function where_in($key = NULL, $values = NULL) - { - return $this->_where_in($key, $values); - } - - // -------------------------------------------------------------------- - - /** - * Where_in_or - * - * Generates a WHERE field IN ('item', 'item') SQL query joined with - * OR if appropriate - * - * @param string The field to search - * @param array The values searched on - * @return object - */ - public function or_where_in($key = NULL, $values = NULL) - { - return $this->_where_in($key, $values, FALSE, 'OR '); - } - - // -------------------------------------------------------------------- - - /** - * Where_not_in - * - * Generates a WHERE field NOT IN ('item', 'item') SQL query joined - * with AND if appropriate - * - * @param string The field to search - * @param array The values searched on - * @return object - */ - public function where_not_in($key = NULL, $values = NULL) - { - return $this->_where_in($key, $values, TRUE); - } - - // -------------------------------------------------------------------- - - /** - * Where_not_in_or - * - * Generates a WHERE field NOT IN ('item', 'item') SQL query joined - * with OR if appropriate - * - * @param string The field to search - * @param array The values searched on - * @return object - */ - public function or_where_not_in($key = NULL, $values = NULL) - { - return $this->_where_in($key, $values, TRUE, 'OR '); - } - - // -------------------------------------------------------------------- - - /** - * Where_in - * - * Called by where_in, where_in_or, where_not_in, where_not_in_or - * - * @param string The field to search - * @param array The values searched on - * @param boolean If the statement would be IN or NOT IN - * @param string - * @return object - */ - protected function _where_in($key = NULL, $values = NULL, $not = FALSE, $type = 'AND ') - { - if ($key === NULL OR $values === NULL) - { - return; - } - - if ( ! is_array($values)) - { - $values = array($values); - } - - $not = ($not) ? ' NOT' : ''; - - foreach ($values as $value) - { - $this->ar_wherein[] = $this->escape($value); - } - - $prefix = (count($this->ar_where) == 0) ? '' : $type; - - $where_in = $prefix . $this->_protect_identifiers($key) . $not . " IN (" . implode(", ", $this->ar_wherein) . ") "; - - $this->ar_where[] = $where_in; - if ($this->ar_caching === TRUE) - { - $this->ar_cache_where[] = $where_in; - $this->ar_cache_exists[] = 'where'; - } - - // reset the array for multiple calls - $this->ar_wherein = array(); - return $this; - } - - // -------------------------------------------------------------------- - - /** - * Like - * - * Generates a %LIKE% portion of the query. Separates - * multiple calls with AND - * - * @param mixed - * @param mixed - * @return object - */ - public function like($field, $match = '', $side = 'both') - { - return $this->_like($field, $match, 'AND ', $side); - } - - // -------------------------------------------------------------------- - - /** - * Not Like - * - * Generates a NOT LIKE portion of the query. Separates - * multiple calls with AND - * - * @param mixed - * @param mixed - * @return object - */ - public function not_like($field, $match = '', $side = 'both') - { - return $this->_like($field, $match, 'AND ', $side, 'NOT'); - } - - // -------------------------------------------------------------------- - - /** - * OR Like - * - * Generates a %LIKE% portion of the query. Separates - * multiple calls with OR - * - * @param mixed - * @param mixed - * @return object - */ - public function or_like($field, $match = '', $side = 'both') - { - return $this->_like($field, $match, 'OR ', $side); - } - - // -------------------------------------------------------------------- - - /** - * OR Not Like - * - * Generates a NOT LIKE portion of the query. Separates - * multiple calls with OR - * - * @param mixed - * @param mixed - * @return object - */ - public function or_not_like($field, $match = '', $side = 'both') - { - return $this->_like($field, $match, 'OR ', $side, 'NOT'); - } - - // -------------------------------------------------------------------- - - /** - * Like - * - * Called by like() or orlike() - * - * @param mixed - * @param mixed - * @param string - * @return object - */ - protected function _like($field, $match = '', $type = 'AND ', $side = 'both', $not = '') - { - if ( ! is_array($field)) - { - $field = array($field => $match); - } - - foreach ($field as $k => $v) - { - $k = $this->_protect_identifiers($k); - - $prefix = (count($this->ar_like) == 0) ? '' : $type; - - $v = $this->escape_like_str($v); - - if ($side == 'none') - { - $like_statement = $prefix." $k $not LIKE '{$v}'"; - } - elseif ($side == 'before') - { - $like_statement = $prefix." $k $not LIKE '%{$v}'"; - } - elseif ($side == 'after') - { - $like_statement = $prefix." $k $not LIKE '{$v}%'"; - } - else - { - $like_statement = $prefix." $k $not LIKE '%{$v}%'"; - } - - // some platforms require an escape sequence definition for LIKE wildcards - if ($this->_like_escape_str != '') - { - $like_statement = $like_statement.sprintf($this->_like_escape_str, $this->_like_escape_chr); - } - - $this->ar_like[] = $like_statement; - if ($this->ar_caching === TRUE) - { - $this->ar_cache_like[] = $like_statement; - $this->ar_cache_exists[] = 'like'; - } - - } - return $this; - } - - // -------------------------------------------------------------------- - - /** - * GROUP BY - * - * @param string - * @return object - */ - public function group_by($by) - { - if (is_string($by)) - { - $by = explode(',', $by); - } - - foreach ($by as $val) - { - $val = trim($val); - - if ($val != '') - { - $this->ar_groupby[] = $this->_protect_identifiers($val); - - if ($this->ar_caching === TRUE) - { - $this->ar_cache_groupby[] = $this->_protect_identifiers($val); - $this->ar_cache_exists[] = 'groupby'; - } - } - } - return $this; - } - - // -------------------------------------------------------------------- - - /** - * Sets the HAVING value - * - * Separates multiple calls with AND - * - * @param string - * @param string - * @return object - */ - public function having($key, $value = '', $escape = TRUE) - { - return $this->_having($key, $value, 'AND ', $escape); - } - - // -------------------------------------------------------------------- - - /** - * Sets the OR HAVING value - * - * Separates multiple calls with OR - * - * @param string - * @param string - * @return object - */ - public function or_having($key, $value = '', $escape = TRUE) - { - return $this->_having($key, $value, 'OR ', $escape); - } - - // -------------------------------------------------------------------- - - /** - * Sets the HAVING values - * - * Called by having() or or_having() - * - * @param string - * @param string - * @return object - */ - protected function _having($key, $value = '', $type = 'AND ', $escape = TRUE) - { - if ( ! is_array($key)) - { - $key = array($key => $value); - } - - foreach ($key as $k => $v) - { - $prefix = (count($this->ar_having) == 0) ? '' : $type; - - if ($escape === TRUE) - { - $k = $this->_protect_identifiers($k); - } - - if ( ! $this->_has_operator($k)) - { - $k .= ' = '; - } - - if ($v != '') - { - $v = ' '.$this->escape($v); - } - - $this->ar_having[] = $prefix.$k.$v; - if ($this->ar_caching === TRUE) - { - $this->ar_cache_having[] = $prefix.$k.$v; - $this->ar_cache_exists[] = 'having'; - } - } - - return $this; - } - - // -------------------------------------------------------------------- - - /** - * Sets the ORDER BY value - * - * @param string - * @param string direction: asc or desc - * @return object - */ - public function order_by($orderby, $direction = '') - { - if (strtolower($direction) == 'random') - { - $orderby = ''; // Random results want or don't need a field name - $direction = $this->_random_keyword; - } - elseif (trim($direction) != '') - { - $direction = (in_array(strtoupper(trim($direction)), array('ASC', 'DESC'), TRUE)) ? ' '.$direction : ' ASC'; - } - - - if (strpos($orderby, ',') !== FALSE) - { - $temp = array(); - foreach (explode(',', $orderby) as $part) - { - $part = trim($part); - if ( ! in_array($part, $this->ar_aliased_tables)) - { - $part = $this->_protect_identifiers(trim($part)); - } - - $temp[] = $part; - } - - $orderby = implode(', ', $temp); - } - else if ($direction != $this->_random_keyword) - { - $orderby = $this->_protect_identifiers($orderby); - } - - $orderby_statement = $orderby.$direction; - - $this->ar_orderby[] = $orderby_statement; - if ($this->ar_caching === TRUE) - { - $this->ar_cache_orderby[] = $orderby_statement; - $this->ar_cache_exists[] = 'orderby'; - } - - return $this; - } - - // -------------------------------------------------------------------- - - /** - * Sets the LIMIT value - * - * @param integer the limit value - * @param integer the offset value - * @return object - */ - public function limit($value, $offset = '') - { - $this->ar_limit = (int) $value; - - if ($offset != '') - { - $this->ar_offset = (int) $offset; - } - - return $this; - } - - // -------------------------------------------------------------------- - - /** - * Sets the OFFSET value - * - * @param integer the offset value - * @return object - */ - public function offset($offset) - { - $this->ar_offset = $offset; - return $this; - } - - // -------------------------------------------------------------------- - - /** - * The "set" function. Allows key/value pairs to be set for inserting or updating - * - * @param mixed - * @param string - * @param boolean - * @return object - */ - public function set($key, $value = '', $escape = TRUE) - { - $key = $this->_object_to_array($key); - - if ( ! is_array($key)) - { - $key = array($key => $value); - } - - foreach ($key as $k => $v) - { - if ($escape === FALSE) - { - $this->ar_set[$this->_protect_identifiers($k)] = $v; - } - else - { - $this->ar_set[$this->_protect_identifiers($k, FALSE, TRUE)] = $this->escape($v); - } - } - - return $this; - } - - // -------------------------------------------------------------------- - - /** - * Get - * - * Compiles the select statement based on the other functions called - * and runs the query - * - * @param string the table - * @param string the limit clause - * @param string the offset clause - * @return object - */ - public function get($table = '', $limit = null, $offset = null) - { - if ($table != '') - { - $this->_track_aliases($table); - $this->from($table); - } - - if ( ! is_null($limit)) - { - $this->limit($limit, $offset); - } - - $sql = $this->_compile_select(); - - $result = $this->query($sql); - $this->_reset_select(); - return $result; - } - - /** - * "Count All Results" query - * - * Generates a platform-specific query string that counts all records - * returned by an Active Record query. - * - * @param string - * @return string - */ - public function count_all_results($table = '') - { - if ($table != '') - { - $this->_track_aliases($table); - $this->from($table); - } - - $sql = $this->_compile_select($this->_count_string . $this->_protect_identifiers('numrows')); - - $query = $this->query($sql); - $this->_reset_select(); - - if ($query->num_rows() == 0) - { - return 0; - } - - $row = $query->row(); - return (int) $row->numrows; - } - - // -------------------------------------------------------------------- - - /** - * Get_Where - * - * Allows the where clause, limit and offset to be added directly - * - * @param string the where clause - * @param string the limit clause - * @param string the offset clause - * @return object - */ - public function get_where($table = '', $where = null, $limit = null, $offset = null) - { - if ($table != '') - { - $this->from($table); - } - - if ( ! is_null($where)) - { - $this->where($where); - } - - if ( ! is_null($limit)) - { - $this->limit($limit, $offset); - } - - $sql = $this->_compile_select(); - - $result = $this->query($sql); - $this->_reset_select(); - return $result; - } - - // -------------------------------------------------------------------- - - /** - * Insert_Batch - * - * Compiles batch insert strings and runs the queries - * - * @param string the table to retrieve the results from - * @param array an associative array of insert values - * @return object - */ - public function insert_batch($table = '', $set = NULL) - { - if ( ! is_null($set)) - { - $this->set_insert_batch($set); - } - - if (count($this->ar_set) == 0) - { - if ($this->db_debug) - { - //No valid data array. Folds in cases where keys and values did not match up - return $this->display_error('db_must_use_set'); - } - return FALSE; - } - - if ($table == '') - { - if ( ! isset($this->ar_from[0])) - { - if ($this->db_debug) - { - return $this->display_error('db_must_set_table'); - } - return FALSE; - } - - $table = $this->ar_from[0]; - } - - // Batch this baby - for ($i = 0, $total = count($this->ar_set); $i < $total; $i = $i + 100) - { - - $sql = $this->_insert_batch($this->_protect_identifiers($table, TRUE, NULL, FALSE), $this->ar_keys, array_slice($this->ar_set, $i, 100)); - - //echo $sql; - - $this->query($sql); - } - - $this->_reset_write(); - - - return TRUE; - } - - // -------------------------------------------------------------------- - - /** - * The "set_insert_batch" function. Allows key/value pairs to be set for batch inserts - * - * @param mixed - * @param string - * @param boolean - * @return object - */ - public function set_insert_batch($key, $value = '', $escape = TRUE) - { - $key = $this->_object_to_array_batch($key); - - if ( ! is_array($key)) - { - $key = array($key => $value); - } - - $keys = array_keys(current($key)); - sort($keys); - - foreach ($key as $row) - { - if (count(array_diff($keys, array_keys($row))) > 0 OR count(array_diff(array_keys($row), $keys)) > 0) - { - // batch function above returns an error on an empty array - $this->ar_set[] = array(); - return; - } - - ksort($row); // puts $row in the same order as our keys - - if ($escape === FALSE) - { - $this->ar_set[] = '('.implode(',', $row).')'; - } - else - { - $clean = array(); - - foreach ($row as $value) - { - $clean[] = $this->escape($value); - } - - $this->ar_set[] = '('.implode(',', $clean).')'; - } - } - - foreach ($keys as $k) - { - $this->ar_keys[] = $this->_protect_identifiers($k); - } - - return $this; - } - - // -------------------------------------------------------------------- - - /** - * Insert - * - * Compiles an insert string and runs the query - * - * @param string the table to insert data into - * @param array an associative array of insert values - * @return object - */ - function insert($table = '', $set = NULL) - { - if ( ! is_null($set)) - { - $this->set($set); - } - - if (count($this->ar_set) == 0) - { - if ($this->db_debug) - { - return $this->display_error('db_must_use_set'); - } - return FALSE; - } - - if ($table == '') - { - if ( ! isset($this->ar_from[0])) - { - if ($this->db_debug) - { - return $this->display_error('db_must_set_table'); - } - return FALSE; - } - - $table = $this->ar_from[0]; - } - - $sql = $this->_insert($this->_protect_identifiers($table, TRUE, NULL, FALSE), array_keys($this->ar_set), array_values($this->ar_set)); - - $this->_reset_write(); - return $this->query($sql); - } - - // -------------------------------------------------------------------- - - /** - * Replace - * - * Compiles an replace into string and runs the query - * - * @param string the table to replace data into - * @param array an associative array of insert values - * @return object - */ - public function replace($table = '', $set = NULL) - { - if ( ! is_null($set)) - { - $this->set($set); - } - - if (count($this->ar_set) == 0) - { - if ($this->db_debug) - { - return $this->display_error('db_must_use_set'); - } - return FALSE; - } - - if ($table == '') - { - if ( ! isset($this->ar_from[0])) - { - if ($this->db_debug) - { - return $this->display_error('db_must_set_table'); - } - return FALSE; - } - - $table = $this->ar_from[0]; - } - - $sql = $this->_replace($this->_protect_identifiers($table, TRUE, NULL, FALSE), array_keys($this->ar_set), array_values($this->ar_set)); - - $this->_reset_write(); - return $this->query($sql); - } - - // -------------------------------------------------------------------- - - /** - * Update - * - * Compiles an update string and runs the query - * - * @param string the table to retrieve the results from - * @param array an associative array of update values - * @param mixed the where clause - * @return object - */ - public function update($table = '', $set = NULL, $where = NULL, $limit = NULL) - { - // Combine any cached components with the current statements - $this->_merge_cache(); - - if ( ! is_null($set)) - { - $this->set($set); - } - - if (count($this->ar_set) == 0) - { - if ($this->db_debug) - { - return $this->display_error('db_must_use_set'); - } - return FALSE; - } - - if ($table == '') - { - if ( ! isset($this->ar_from[0])) - { - if ($this->db_debug) - { - return $this->display_error('db_must_set_table'); - } - return FALSE; - } - - $table = $this->ar_from[0]; - } - - if ($where != NULL) - { - $this->where($where); - } - - if ($limit != NULL) - { - $this->limit($limit); - } - - $sql = $this->_update($this->_protect_identifiers($table, TRUE, NULL, FALSE), $this->ar_set, $this->ar_where, $this->ar_orderby, $this->ar_limit); - - $this->_reset_write(); - return $this->query($sql); - } - - - // -------------------------------------------------------------------- - - /** - * Update_Batch - * - * Compiles an update string and runs the query - * - * @param string the table to retrieve the results from - * @param array an associative array of update values - * @param string the where key - * @return object - */ - public function update_batch($table = '', $set = NULL, $index = NULL) - { - // Combine any cached components with the current statements - $this->_merge_cache(); - - if (is_null($index)) - { - if ($this->db_debug) - { - return $this->display_error('db_must_use_index'); - } - - return FALSE; - } - - if ( ! is_null($set)) - { - $this->set_update_batch($set, $index); - } - - if (count($this->ar_set) == 0) - { - if ($this->db_debug) - { - return $this->display_error('db_must_use_set'); - } - - return FALSE; - } - - if ($table == '') - { - if ( ! isset($this->ar_from[0])) - { - if ($this->db_debug) - { - return $this->display_error('db_must_set_table'); - } - return FALSE; - } - - $table = $this->ar_from[0]; - } - - // Batch this baby - for ($i = 0, $total = count($this->ar_set); $i < $total; $i = $i + 100) - { - $sql = $this->_update_batch($this->_protect_identifiers($table, TRUE, NULL, FALSE), array_slice($this->ar_set, $i, 100), $this->_protect_identifiers($index), $this->ar_where); - - $this->query($sql); - } - - $this->_reset_write(); - } - - // -------------------------------------------------------------------- - - /** - * The "set_update_batch" function. Allows key/value pairs to be set for batch updating - * - * @param array - * @param string - * @param boolean - * @return object - */ - public function set_update_batch($key, $index = '', $escape = TRUE) - { - $key = $this->_object_to_array_batch($key); - - if ( ! is_array($key)) - { - // @todo error - } - - foreach ($key as $k => $v) - { - $index_set = FALSE; - $clean = array(); - - foreach ($v as $k2 => $v2) - { - if ($k2 == $index) - { - $index_set = TRUE; - } - else - { - $not[] = $k.'-'.$v; - } - - if ($escape === FALSE) - { - $clean[$this->_protect_identifiers($k2)] = $v2; - } - else - { - $clean[$this->_protect_identifiers($k2)] = $this->escape($v2); - } - } - - if ($index_set == FALSE) - { - return $this->display_error('db_batch_missing_index'); - } - - $this->ar_set[] = $clean; - } - - return $this; - } - - // -------------------------------------------------------------------- - - /** - * Empty Table - * - * Compiles a delete string and runs "DELETE FROM table" - * - * @param string the table to empty - * @return object - */ - public function empty_table($table = '') - { - if ($table == '') - { - if ( ! isset($this->ar_from[0])) - { - if ($this->db_debug) - { - return $this->display_error('db_must_set_table'); - } - return FALSE; - } - - $table = $this->ar_from[0]; - } - else - { - $table = $this->_protect_identifiers($table, TRUE, NULL, FALSE); - } - - $sql = $this->_delete($table); - - $this->_reset_write(); - - return $this->query($sql); - } - - // -------------------------------------------------------------------- - - /** - * Truncate - * - * Compiles a truncate string and runs the query - * If the database does not support the truncate() command - * This function maps to "DELETE FROM table" - * - * @param string the table to truncate - * @return object - */ - public function truncate($table = '') - { - if ($table == '') - { - if ( ! isset($this->ar_from[0])) - { - if ($this->db_debug) - { - return $this->display_error('db_must_set_table'); - } - return FALSE; - } - - $table = $this->ar_from[0]; - } - else - { - $table = $this->_protect_identifiers($table, TRUE, NULL, FALSE); - } - - $sql = $this->_truncate($table); - - $this->_reset_write(); - - return $this->query($sql); - } - - // -------------------------------------------------------------------- - - /** - * Delete - * - * Compiles a delete string and runs the query - * - * @param mixed the table(s) to delete from. String or array - * @param mixed the where clause - * @param mixed the limit clause - * @param boolean - * @return object - */ - public function delete($table = '', $where = '', $limit = NULL, $reset_data = TRUE) - { - // Combine any cached components with the current statements - $this->_merge_cache(); - - if ($table == '') - { - if ( ! isset($this->ar_from[0])) - { - if ($this->db_debug) - { - return $this->display_error('db_must_set_table'); - } - return FALSE; - } - - $table = $this->ar_from[0]; - } - elseif (is_array($table)) - { - foreach ($table as $single_table) - { - $this->delete($single_table, $where, $limit, FALSE); - } - - $this->_reset_write(); - return; - } - else - { - $table = $this->_protect_identifiers($table, TRUE, NULL, FALSE); - } - - if ($where != '') - { - $this->where($where); - } - - if ($limit != NULL) - { - $this->limit($limit); - } - - if (count($this->ar_where) == 0 && count($this->ar_wherein) == 0 && count($this->ar_like) == 0) - { - if ($this->db_debug) - { - return $this->display_error('db_del_must_use_where'); - } - - return FALSE; - } - - $sql = $this->_delete($table, $this->ar_where, $this->ar_like, $this->ar_limit); - - if ($reset_data) - { - $this->_reset_write(); - } - - return $this->query($sql); - } - - // -------------------------------------------------------------------- - - /** - * DB Prefix - * - * Prepends a database prefix if one exists in configuration - * - * @param string the table - * @return string - */ - public function dbprefix($table = '') - { - if ($table == '') - { - $this->display_error('db_table_name_required'); - } - - return $this->dbprefix.$table; - } - - // -------------------------------------------------------------------- - - /** - * Set DB Prefix - * - * Set's the DB Prefix to something new without needing to reconnect - * - * @param string the prefix - * @return string - */ - public function set_dbprefix($prefix = '') - { - return $this->dbprefix = $prefix; - } - - // -------------------------------------------------------------------- - - /** - * Track Aliases - * - * Used to track SQL statements written with aliased tables. - * - * @param string The table to inspect - * @return string - */ - protected function _track_aliases($table) - { - if (is_array($table)) - { - foreach ($table as $t) - { - $this->_track_aliases($t); - } - return; - } - - // Does the string contain a comma? If so, we need to separate - // the string into discreet statements - if (strpos($table, ',') !== FALSE) - { - return $this->_track_aliases(explode(',', $table)); - } - - // if a table alias is used we can recognize it by a space - if (strpos($table, " ") !== FALSE) - { - // if the alias is written with the AS keyword, remove it - $table = preg_replace('/\s+AS\s+/i', ' ', $table); - - // Grab the alias - $table = trim(strrchr($table, " ")); - - // Store the alias, if it doesn't already exist - if ( ! in_array($table, $this->ar_aliased_tables)) - { - $this->ar_aliased_tables[] = $table; - } - } - } - - // -------------------------------------------------------------------- - - /** - * Compile the SELECT statement - * - * Generates a query string based on which functions were used. - * Should not be called directly. The get() function calls it. - * - * @return string - */ - protected function _compile_select($select_override = FALSE) - { - // Combine any cached components with the current statements - $this->_merge_cache(); - - // ---------------------------------------------------------------- - - // Write the "select" portion of the query - - if ($select_override !== FALSE) - { - $sql = $select_override; - } - else - { - $sql = ( ! $this->ar_distinct) ? 'SELECT ' : 'SELECT DISTINCT '; - - if (count($this->ar_select) == 0) - { - $sql .= '*'; - } - else - { - // Cycle through the "select" portion of the query and prep each column name. - // The reason we protect identifiers here rather then in the select() function - // is because until the user calls the from() function we don't know if there are aliases - foreach ($this->ar_select as $key => $val) - { - $no_escape = isset($this->ar_no_escape[$key]) ? $this->ar_no_escape[$key] : NULL; - $this->ar_select[$key] = $this->_protect_identifiers($val, FALSE, $no_escape); - } - - $sql .= implode(', ', $this->ar_select); - } - } - - // ---------------------------------------------------------------- - - // Write the "FROM" portion of the query - - if (count($this->ar_from) > 0) - { - $sql .= "\nFROM "; - - $sql .= $this->_from_tables($this->ar_from); - } - - // ---------------------------------------------------------------- - - // Write the "JOIN" portion of the query - - if (count($this->ar_join) > 0) - { - $sql .= "\n"; - - $sql .= implode("\n", $this->ar_join); - } - - // ---------------------------------------------------------------- - - // Write the "WHERE" portion of the query - - if (count($this->ar_where) > 0 OR count($this->ar_like) > 0) - { - $sql .= "\nWHERE "; - } - - $sql .= implode("\n", $this->ar_where); - - // ---------------------------------------------------------------- - - // Write the "LIKE" portion of the query - - if (count($this->ar_like) > 0) - { - if (count($this->ar_where) > 0) - { - $sql .= "\nAND "; - } - - $sql .= implode("\n", $this->ar_like); - } - - // ---------------------------------------------------------------- - - // Write the "GROUP BY" portion of the query - - if (count($this->ar_groupby) > 0) - { - $sql .= "\nGROUP BY "; - - $sql .= implode(', ', $this->ar_groupby); - } - - // ---------------------------------------------------------------- - - // Write the "HAVING" portion of the query - - if (count($this->ar_having) > 0) - { - $sql .= "\nHAVING "; - $sql .= implode("\n", $this->ar_having); - } - - // ---------------------------------------------------------------- - - // Write the "ORDER BY" portion of the query - - if (count($this->ar_orderby) > 0) - { - $sql .= "\nORDER BY "; - $sql .= implode(', ', $this->ar_orderby); - - if ($this->ar_order !== FALSE) - { - $sql .= ($this->ar_order == 'desc') ? ' DESC' : ' ASC'; - } - } - - // ---------------------------------------------------------------- - - // Write the "LIMIT" portion of the query - - if (is_numeric($this->ar_limit)) - { - $sql .= "\n"; - $sql = $this->_limit($sql, $this->ar_limit, $this->ar_offset); - } - - return $sql; - } - - // -------------------------------------------------------------------- - - /** - * Object to Array - * - * Takes an object as input and converts the class variables to array key/vals - * - * @param object - * @return array - */ - public function _object_to_array($object) - { - if ( ! is_object($object)) - { - return $object; - } - - $array = array(); - foreach (get_object_vars($object) as $key => $val) - { - // There are some built in keys we need to ignore for this conversion - if ( ! is_object($val) && ! is_array($val) && $key != '_parent_name') - { - $array[$key] = $val; - } - } - - return $array; - } - - // -------------------------------------------------------------------- - - /** - * Object to Array - * - * Takes an object as input and converts the class variables to array key/vals - * - * @param object - * @return array - */ - public function _object_to_array_batch($object) - { - if ( ! is_object($object)) - { - return $object; - } - - $array = array(); - $out = get_object_vars($object); - $fields = array_keys($out); - - foreach ($fields as $val) - { - // There are some built in keys we need to ignore for this conversion - if ($val != '_parent_name') - { - - $i = 0; - foreach ($out[$val] as $data) - { - $array[$i][$val] = $data; - $i++; - } - } - } - - return $array; - } - - // -------------------------------------------------------------------- - - /** - * Start Cache - * - * Starts AR caching - * - * @return void - */ - public function start_cache() - { - $this->ar_caching = TRUE; - } - - // -------------------------------------------------------------------- - - /** - * Stop Cache - * - * Stops AR caching - * - * @return void - */ - public function stop_cache() - { - $this->ar_caching = FALSE; - } - - // -------------------------------------------------------------------- - - /** - * Flush Cache - * - * Empties the AR cache - * - * @access public - * @return void - */ - public function flush_cache() - { - $this->_reset_run(array( - 'ar_cache_select' => array(), - 'ar_cache_from' => array(), - 'ar_cache_join' => array(), - 'ar_cache_where' => array(), - 'ar_cache_like' => array(), - 'ar_cache_groupby' => array(), - 'ar_cache_having' => array(), - 'ar_cache_orderby' => array(), - 'ar_cache_set' => array(), - 'ar_cache_exists' => array(), - 'ar_cache_no_escape' => array() - )); - } - - // -------------------------------------------------------------------- - - /** - * Merge Cache - * - * When called, this function merges any cached AR arrays with - * locally called ones. - * - * @return void - */ - protected function _merge_cache() - { - if (count($this->ar_cache_exists) == 0) - { - return; - } - - foreach ($this->ar_cache_exists as $val) - { - $ar_variable = 'ar_'.$val; - $ar_cache_var = 'ar_cache_'.$val; - - if (count($this->$ar_cache_var) == 0) - { - continue; - } - - $this->$ar_variable = array_unique(array_merge($this->$ar_cache_var, $this->$ar_variable)); - } - - // If we are "protecting identifiers" we need to examine the "from" - // portion of the query to determine if there are any aliases - if ($this->_protect_identifiers === TRUE AND count($this->ar_cache_from) > 0) - { - $this->_track_aliases($this->ar_from); - } - - $this->ar_no_escape = $this->ar_cache_no_escape; - } - - // -------------------------------------------------------------------- - - /** - * Resets the active record values. Called by the get() function - * - * @param array An array of fields to reset - * @return void - */ - protected function _reset_run($ar_reset_items) - { - foreach ($ar_reset_items as $item => $default_value) - { - if ( ! in_array($item, $this->ar_store_array)) - { - $this->$item = $default_value; - } - } - } - - // -------------------------------------------------------------------- - - /** - * Resets the active record values. Called by the get() function - * - * @return void - */ - protected function _reset_select() - { - $ar_reset_items = array( - 'ar_select' => array(), - 'ar_from' => array(), - 'ar_join' => array(), - 'ar_where' => array(), - 'ar_like' => array(), - 'ar_groupby' => array(), - 'ar_having' => array(), - 'ar_orderby' => array(), - 'ar_wherein' => array(), - 'ar_aliased_tables' => array(), - 'ar_no_escape' => array(), - 'ar_distinct' => FALSE, - 'ar_limit' => FALSE, - 'ar_offset' => FALSE, - 'ar_order' => FALSE, - ); - - $this->_reset_run($ar_reset_items); - } - - // -------------------------------------------------------------------- - - /** - * Resets the active record "write" values. - * - * Called by the insert() update() insert_batch() update_batch() and delete() functions - * - * @return void - */ - protected function _reset_write() - { - $ar_reset_items = array( - 'ar_set' => array(), - 'ar_from' => array(), - 'ar_where' => array(), - 'ar_like' => array(), - 'ar_orderby' => array(), - 'ar_keys' => array(), - 'ar_limit' => FALSE, - 'ar_order' => FALSE - ); - - $this->_reset_run($ar_reset_items); - } -} - -/* End of file DB_active_rec.php */ -/* Location: ./system/database/DB_active_rec.php */ \ No newline at end of file diff --git a/system/database/DB_cache.php b/system/database/DB_cache.php deleted file mode 100644 index ad1c28d7..00000000 --- a/system/database/DB_cache.php +++ /dev/null @@ -1,195 +0,0 @@ -CI - // and load the file helper since we use it a lot - $this->CI =& get_instance(); - $this->db =& $db; - $this->CI->load->helper('file'); - } - - // -------------------------------------------------------------------- - - /** - * Set Cache Directory Path - * - * @access public - * @param string the path to the cache directory - * @return bool - */ - function check_path($path = '') - { - if ($path == '') - { - if ($this->db->cachedir == '') - { - return $this->db->cache_off(); - } - - $path = $this->db->cachedir; - } - - // Add a trailing slash to the path if needed - $path = preg_replace("/(.+?)\/*$/", "\\1/", $path); - - if ( ! is_dir($path) OR ! is_really_writable($path)) - { - // If the path is wrong we'll turn off caching - return $this->db->cache_off(); - } - - $this->db->cachedir = $path; - return TRUE; - } - - // -------------------------------------------------------------------- - - /** - * Retrieve a cached query - * - * The URI being requested will become the name of the cache sub-folder. - * An MD5 hash of the SQL statement will become the cache file name - * - * @access public - * @return string - */ - function read($sql) - { - if ( ! $this->check_path()) - { - return $this->db->cache_off(); - } - - $segment_one = ($this->CI->uri->segment(1) == FALSE) ? 'default' : $this->CI->uri->segment(1); - - $segment_two = ($this->CI->uri->segment(2) == FALSE) ? 'index' : $this->CI->uri->segment(2); - - $filepath = $this->db->cachedir.$segment_one.'+'.$segment_two.'/'.md5($sql); - - if (FALSE === ($cachedata = read_file($filepath))) - { - return FALSE; - } - - return unserialize($cachedata); - } - - // -------------------------------------------------------------------- - - /** - * Write a query to a cache file - * - * @access public - * @return bool - */ - function write($sql, $object) - { - if ( ! $this->check_path()) - { - return $this->db->cache_off(); - } - - $segment_one = ($this->CI->uri->segment(1) == FALSE) ? 'default' : $this->CI->uri->segment(1); - - $segment_two = ($this->CI->uri->segment(2) == FALSE) ? 'index' : $this->CI->uri->segment(2); - - $dir_path = $this->db->cachedir.$segment_one.'+'.$segment_two.'/'; - - $filename = md5($sql); - - if ( ! @is_dir($dir_path)) - { - if ( ! @mkdir($dir_path, DIR_WRITE_MODE)) - { - return FALSE; - } - - @chmod($dir_path, DIR_WRITE_MODE); - } - - if (write_file($dir_path.$filename, serialize($object)) === FALSE) - { - return FALSE; - } - - @chmod($dir_path.$filename, FILE_WRITE_MODE); - return TRUE; - } - - // -------------------------------------------------------------------- - - /** - * Delete cache files within a particular directory - * - * @access public - * @return bool - */ - function delete($segment_one = '', $segment_two = '') - { - if ($segment_one == '') - { - $segment_one = ($this->CI->uri->segment(1) == FALSE) ? 'default' : $this->CI->uri->segment(1); - } - - if ($segment_two == '') - { - $segment_two = ($this->CI->uri->segment(2) == FALSE) ? 'index' : $this->CI->uri->segment(2); - } - - $dir_path = $this->db->cachedir.$segment_one.'+'.$segment_two.'/'; - - delete_files($dir_path, TRUE); - } - - // -------------------------------------------------------------------- - - /** - * Delete all existing cache files - * - * @access public - * @return bool - */ - function delete_all() - { - delete_files($this->db->cachedir, TRUE); - } - -} - - -/* End of file DB_cache.php */ -/* Location: ./system/database/DB_cache.php */ \ No newline at end of file diff --git a/system/database/DB_driver.php b/system/database/DB_driver.php deleted file mode 100644 index 775fd335..00000000 --- a/system/database/DB_driver.php +++ /dev/null @@ -1,1410 +0,0 @@ - $val) - { - $this->$key = $val; - } - } - - log_message('debug', 'Database Driver Class Initialized'); - } - - // -------------------------------------------------------------------- - - /** - * Initialize Database Settings - * - * @access private Called by the constructor - * @param mixed - * @return void - */ - function initialize() - { - // If an existing connection resource is available - // there is no need to connect and select the database - if (is_resource($this->conn_id) OR is_object($this->conn_id)) - { - return TRUE; - } - - // ---------------------------------------------------------------- - - // Connect to the database and set the connection ID - $this->conn_id = ($this->pconnect == FALSE) ? $this->db_connect() : $this->db_pconnect(); - - // No connection resource? Throw an error - if ( ! $this->conn_id) - { - log_message('error', 'Unable to connect to the database'); - - if ($this->db_debug) - { - $this->display_error('db_unable_to_connect'); - } - return FALSE; - } - - // ---------------------------------------------------------------- - - // Select the DB... assuming a database name is specified in the config file - if ($this->database != '') - { - if ( ! $this->db_select()) - { - log_message('error', 'Unable to select database: '.$this->database); - - if ($this->db_debug) - { - $this->display_error('db_unable_to_select', $this->database); - } - return FALSE; - } - else - { - // We've selected the DB. Now we set the character set - if ( ! $this->db_set_charset($this->char_set, $this->dbcollat)) - { - return FALSE; - } - - return TRUE; - } - } - - return TRUE; - } - - // -------------------------------------------------------------------- - - /** - * Set client character set - * - * @access public - * @param string - * @param string - * @return resource - */ - function db_set_charset($charset, $collation) - { - if ( ! $this->_db_set_charset($this->char_set, $this->dbcollat)) - { - log_message('error', 'Unable to set database connection charset: '.$this->char_set); - - if ($this->db_debug) - { - $this->display_error('db_unable_to_set_charset', $this->char_set); - } - - return FALSE; - } - - return TRUE; - } - - // -------------------------------------------------------------------- - - /** - * The name of the platform in use (mysql, mssql, etc...) - * - * @access public - * @return string - */ - function platform() - { - return $this->dbdriver; - } - - // -------------------------------------------------------------------- - - /** - * Database Version Number. Returns a string containing the - * version of the database being used - * - * @access public - * @return string - */ - function version() - { - if (FALSE === ($sql = $this->_version())) - { - if ($this->db_debug) - { - return $this->display_error('db_unsupported_function'); - } - return FALSE; - } - - // Some DBs have functions that return the version, and don't run special - // SQL queries per se. In these instances, just return the result. - $driver_version_exceptions = array('oci8', 'sqlite', 'cubrid'); - - if (in_array($this->dbdriver, $driver_version_exceptions)) - { - return $sql; - } - else - { - $query = $this->query($sql); - return $query->row('ver'); - } - } - - // -------------------------------------------------------------------- - - /** - * Execute the query - * - * Accepts an SQL string as input and returns a result object upon - * successful execution of a "read" type query. Returns boolean TRUE - * upon successful execution of a "write" type query. Returns boolean - * FALSE upon failure, and if the $db_debug variable is set to TRUE - * will raise an error. - * - * @access public - * @param string An SQL query string - * @param array An array of binding data - * @return mixed - */ - function query($sql, $binds = FALSE, $return_object = TRUE) - { - if ($sql == '') - { - if ($this->db_debug) - { - log_message('error', 'Invalid query: '.$sql); - return $this->display_error('db_invalid_query'); - } - return FALSE; - } - - // Verify table prefix and replace if necessary - if ( ($this->dbprefix != '' AND $this->swap_pre != '') AND ($this->dbprefix != $this->swap_pre) ) - { - $sql = preg_replace("/(\W)".$this->swap_pre."(\S+?)/", "\\1".$this->dbprefix."\\2", $sql); - } - - // Compile binds if needed - if ($binds !== FALSE) - { - $sql = $this->compile_binds($sql, $binds); - } - - // Is query caching enabled? If the query is a "read type" - // we will load the caching class and return the previously - // cached query if it exists - if ($this->cache_on == TRUE AND stristr($sql, 'SELECT')) - { - if ($this->_cache_init()) - { - $this->load_rdriver(); - if (FALSE !== ($cache = $this->CACHE->read($sql))) - { - return $cache; - } - } - } - - // Save the query for debugging - if ($this->save_queries == TRUE) - { - $this->queries[] = $sql; - } - - // Start the Query Timer - $time_start = list($sm, $ss) = explode(' ', microtime()); - - // Run the Query - if (FALSE === ($this->result_id = $this->simple_query($sql))) - { - if ($this->save_queries == TRUE) - { - $this->query_times[] = 0; - } - - // This will trigger a rollback if transactions are being used - $this->_trans_status = FALSE; - - if ($this->db_debug) - { - // grab the error number and message now, as we might run some - // additional queries before displaying the error - $error_no = $this->_error_number(); - $error_msg = $this->_error_message(); - - // We call this function in order to roll-back queries - // if transactions are enabled. If we don't call this here - // the error message will trigger an exit, causing the - // transactions to remain in limbo. - $this->trans_complete(); - - // Log and display errors - log_message('error', 'Query error: '.$error_msg); - return $this->display_error( - array( - 'Error Number: '.$error_no, - $error_msg, - $sql - ) - ); - } - - return FALSE; - } - - // Stop and aggregate the query time results - $time_end = list($em, $es) = explode(' ', microtime()); - $this->benchmark += ($em + $es) - ($sm + $ss); - - if ($this->save_queries == TRUE) - { - $this->query_times[] = ($em + $es) - ($sm + $ss); - } - - // Increment the query counter - $this->query_count++; - - // Was the query a "write" type? - // If so we'll simply return true - if ($this->is_write_type($sql) === TRUE) - { - // If caching is enabled we'll auto-cleanup any - // existing files related to this particular URI - if ($this->cache_on == TRUE AND $this->cache_autodel == TRUE AND $this->_cache_init()) - { - $this->CACHE->delete(); - } - - return TRUE; - } - - // Return TRUE if we don't need to create a result object - // Currently only the Oracle driver uses this when stored - // procedures are used - if ($return_object !== TRUE) - { - return TRUE; - } - - // Load and instantiate the result driver - - $driver = $this->load_rdriver(); - $RES = new $driver(); - $RES->conn_id = $this->conn_id; - $RES->result_id = $this->result_id; - - if ($this->dbdriver == 'oci8') - { - $RES->stmt_id = $this->stmt_id; - $RES->curs_id = NULL; - $RES->limit_used = $this->limit_used; - $this->stmt_id = FALSE; - } - - // oci8 vars must be set before calling this - $RES->num_rows = $RES->num_rows(); - - // Is query caching enabled? If so, we'll serialize the - // result object and save it to a cache file. - if ($this->cache_on == TRUE AND $this->_cache_init()) - { - // We'll create a new instance of the result object - // only without the platform specific driver since - // we can't use it with cached data (the query result - // resource ID won't be any good once we've cached the - // result object, so we'll have to compile the data - // and save it) - $CR = new CI_DB_result(); - $CR->num_rows = $RES->num_rows(); - $CR->result_object = $RES->result_object(); - $CR->result_array = $RES->result_array(); - - // Reset these since cached objects can not utilize resource IDs. - $CR->conn_id = NULL; - $CR->result_id = NULL; - - $this->CACHE->write($sql, $CR); - } - - return $RES; - } - - // -------------------------------------------------------------------- - - /** - * Load the result drivers - * - * @access public - * @return string the name of the result class - */ - function load_rdriver() - { - $driver = 'CI_DB_'.$this->dbdriver.'_result'; - - if ( ! class_exists($driver)) - { - include_once(BASEPATH.'database/DB_result.php'); - include_once(BASEPATH.'database/drivers/'.$this->dbdriver.'/'.$this->dbdriver.'_result.php'); - } - - return $driver; - } - - // -------------------------------------------------------------------- - - /** - * Simple Query - * This is a simplified version of the query() function. Internally - * we only use it when running transaction commands since they do - * not require all the features of the main query() function. - * - * @access public - * @param string the sql query - * @return mixed - */ - function simple_query($sql) - { - if ( ! $this->conn_id) - { - $this->initialize(); - } - - return $this->_execute($sql); - } - - // -------------------------------------------------------------------- - - /** - * Disable Transactions - * This permits transactions to be disabled at run-time. - * - * @access public - * @return void - */ - function trans_off() - { - $this->trans_enabled = FALSE; - } - - // -------------------------------------------------------------------- - - /** - * Enable/disable Transaction Strict Mode - * When strict mode is enabled, if you are running multiple groups of - * transactions, if one group fails all groups will be rolled back. - * If strict mode is disabled, each group is treated autonomously, meaning - * a failure of one group will not affect any others - * - * @access public - * @return void - */ - function trans_strict($mode = TRUE) - { - $this->trans_strict = is_bool($mode) ? $mode : TRUE; - } - - // -------------------------------------------------------------------- - - /** - * Start Transaction - * - * @access public - * @return void - */ - function trans_start($test_mode = FALSE) - { - if ( ! $this->trans_enabled) - { - return FALSE; - } - - // When transactions are nested we only begin/commit/rollback the outermost ones - if ($this->_trans_depth > 0) - { - $this->_trans_depth += 1; - return; - } - - $this->trans_begin($test_mode); - } - - // -------------------------------------------------------------------- - - /** - * Complete Transaction - * - * @access public - * @return bool - */ - function trans_complete() - { - if ( ! $this->trans_enabled) - { - return FALSE; - } - - // When transactions are nested we only begin/commit/rollback the outermost ones - if ($this->_trans_depth > 1) - { - $this->_trans_depth -= 1; - return TRUE; - } - - // The query() function will set this flag to FALSE in the event that a query failed - if ($this->_trans_status === FALSE) - { - $this->trans_rollback(); - - // If we are NOT running in strict mode, we will reset - // the _trans_status flag so that subsequent groups of transactions - // will be permitted. - if ($this->trans_strict === FALSE) - { - $this->_trans_status = TRUE; - } - - log_message('debug', 'DB Transaction Failure'); - return FALSE; - } - - $this->trans_commit(); - return TRUE; - } - - // -------------------------------------------------------------------- - - /** - * Lets you retrieve the transaction flag to determine if it has failed - * - * @access public - * @return bool - */ - function trans_status() - { - return $this->_trans_status; - } - - // -------------------------------------------------------------------- - - /** - * Compile Bindings - * - * @access public - * @param string the sql statement - * @param array an array of bind data - * @return string - */ - function compile_binds($sql, $binds) - { - if (strpos($sql, $this->bind_marker) === FALSE) - { - return $sql; - } - - if ( ! is_array($binds)) - { - $binds = array($binds); - } - - // Get the sql segments around the bind markers - $segments = explode($this->bind_marker, $sql); - - // The count of bind should be 1 less then the count of segments - // If there are more bind arguments trim it down - if (count($binds) >= count($segments)) { - $binds = array_slice($binds, 0, count($segments)-1); - } - - // Construct the binded query - $result = $segments[0]; - $i = 0; - foreach ($binds as $bind) - { - $result .= $this->escape($bind); - $result .= $segments[++$i]; - } - - return $result; - } - - // -------------------------------------------------------------------- - - /** - * Determines if a query is a "write" type. - * - * @access public - * @param string An SQL query string - * @return boolean - */ - function is_write_type($sql) - { - if ( ! preg_match('/^\s*"?(SET|INSERT|UPDATE|DELETE|REPLACE|CREATE|DROP|TRUNCATE|LOAD DATA|COPY|ALTER|GRANT|REVOKE|LOCK|UNLOCK)\s+/i', $sql)) - { - return FALSE; - } - return TRUE; - } - - // -------------------------------------------------------------------- - - /** - * Calculate the aggregate query elapsed time - * - * @access public - * @param integer The number of decimal places - * @return integer - */ - function elapsed_time($decimals = 6) - { - return number_format($this->benchmark, $decimals); - } - - // -------------------------------------------------------------------- - - /** - * Returns the total number of queries - * - * @access public - * @return integer - */ - function total_queries() - { - return $this->query_count; - } - - // -------------------------------------------------------------------- - - /** - * Returns the last query that was executed - * - * @access public - * @return void - */ - function last_query() - { - return end($this->queries); - } - - // -------------------------------------------------------------------- - - /** - * "Smart" Escape String - * - * Escapes data based on type - * Sets boolean and null types - * - * @access public - * @param string - * @return mixed - */ - function escape($str) - { - if (is_string($str)) - { - $str = "'".$this->escape_str($str)."'"; - } - elseif (is_bool($str)) - { - $str = ($str === FALSE) ? 0 : 1; - } - elseif (is_null($str)) - { - $str = 'NULL'; - } - - return $str; - } - - // -------------------------------------------------------------------- - - /** - * Escape LIKE String - * - * Calls the individual driver for platform - * specific escaping for LIKE conditions - * - * @access public - * @param string - * @return mixed - */ - function escape_like_str($str) - { - return $this->escape_str($str, TRUE); - } - - // -------------------------------------------------------------------- - - /** - * Primary - * - * Retrieves the primary key. It assumes that the row in the first - * position is the primary key - * - * @access public - * @param string the table name - * @return string - */ - function primary($table = '') - { - $fields = $this->list_fields($table); - - if ( ! is_array($fields)) - { - return FALSE; - } - - return current($fields); - } - - // -------------------------------------------------------------------- - - /** - * Returns an array of table names - * - * @access public - * @return array - */ - function list_tables($constrain_by_prefix = FALSE) - { - // Is there a cached result? - if (isset($this->data_cache['table_names'])) - { - return $this->data_cache['table_names']; - } - - if (FALSE === ($sql = $this->_list_tables($constrain_by_prefix))) - { - if ($this->db_debug) - { - return $this->display_error('db_unsupported_function'); - } - return FALSE; - } - - $retval = array(); - $query = $this->query($sql); - - if ($query->num_rows() > 0) - { - foreach ($query->result_array() as $row) - { - if (isset($row['TABLE_NAME'])) - { - $retval[] = $row['TABLE_NAME']; - } - else - { - $retval[] = array_shift($row); - } - } - } - - $this->data_cache['table_names'] = $retval; - return $this->data_cache['table_names']; - } - - // -------------------------------------------------------------------- - - /** - * Determine if a particular table exists - * @access public - * @return boolean - */ - function table_exists($table_name) - { - return ( ! in_array($this->_protect_identifiers($table_name, TRUE, FALSE, FALSE), $this->list_tables())) ? FALSE : TRUE; - } - - // -------------------------------------------------------------------- - - /** - * Fetch MySQL Field Names - * - * @access public - * @param string the table name - * @return array - */ - function list_fields($table = '') - { - // Is there a cached result? - if (isset($this->data_cache['field_names'][$table])) - { - return $this->data_cache['field_names'][$table]; - } - - if ($table == '') - { - if ($this->db_debug) - { - return $this->display_error('db_field_param_missing'); - } - return FALSE; - } - - if (FALSE === ($sql = $this->_list_columns($table))) - { - if ($this->db_debug) - { - return $this->display_error('db_unsupported_function'); - } - return FALSE; - } - - $query = $this->query($sql); - - $retval = array(); - foreach ($query->result_array() as $row) - { - if (isset($row['COLUMN_NAME'])) - { - $retval[] = $row['COLUMN_NAME']; - } - else - { - $retval[] = current($row); - } - } - - $this->data_cache['field_names'][$table] = $retval; - return $this->data_cache['field_names'][$table]; - } - - // -------------------------------------------------------------------- - - /** - * Determine if a particular field exists - * @access public - * @param string - * @param string - * @return boolean - */ - function field_exists($field_name, $table_name) - { - return ( ! in_array($field_name, $this->list_fields($table_name))) ? FALSE : TRUE; - } - - // -------------------------------------------------------------------- - - /** - * Returns an object with field data - * - * @access public - * @param string the table name - * @return object - */ - function field_data($table = '') - { - if ($table == '') - { - if ($this->db_debug) - { - return $this->display_error('db_field_param_missing'); - } - return FALSE; - } - - $query = $this->query($this->_field_data($this->_protect_identifiers($table, TRUE, NULL, FALSE))); - - return $query->field_data(); - } - - // -------------------------------------------------------------------- - - /** - * Generate an insert string - * - * @access public - * @param string the table upon which the query will be performed - * @param array an associative array data of key/values - * @return string - */ - function insert_string($table, $data) - { - $fields = array(); - $values = array(); - - foreach ($data as $key => $val) - { - $fields[] = $this->_escape_identifiers($key); - $values[] = $this->escape($val); - } - - return $this->_insert($this->_protect_identifiers($table, TRUE, NULL, FALSE), $fields, $values); - } - - // -------------------------------------------------------------------- - - /** - * Generate an update string - * - * @access public - * @param string the table upon which the query will be performed - * @param array an associative array data of key/values - * @param mixed the "where" statement - * @return string - */ - function update_string($table, $data, $where) - { - if ($where == '') - { - return false; - } - - $fields = array(); - foreach ($data as $key => $val) - { - $fields[$this->_protect_identifiers($key)] = $this->escape($val); - } - - if ( ! is_array($where)) - { - $dest = array($where); - } - else - { - $dest = array(); - foreach ($where as $key => $val) - { - $prefix = (count($dest) == 0) ? '' : ' AND '; - - if ($val !== '') - { - if ( ! $this->_has_operator($key)) - { - $key .= ' ='; - } - - $val = ' '.$this->escape($val); - } - - $dest[] = $prefix.$key.$val; - } - } - - return $this->_update($this->_protect_identifiers($table, TRUE, NULL, FALSE), $fields, $dest); - } - - // -------------------------------------------------------------------- - - /** - * Tests whether the string has an SQL operator - * - * @access private - * @param string - * @return bool - */ - function _has_operator($str) - { - $str = trim($str); - if ( ! preg_match("/(\s|<|>|!|=|is null|is not null)/i", $str)) - { - return FALSE; - } - - return TRUE; - } - - // -------------------------------------------------------------------- - - /** - * Enables a native PHP function to be run, using a platform agnostic wrapper. - * - * @access public - * @param string the function name - * @param mixed any parameters needed by the function - * @return mixed - */ - function call_function($function) - { - $driver = ($this->dbdriver == 'postgre') ? 'pg_' : $this->dbdriver.'_'; - - if (FALSE === strpos($driver, $function)) - { - $function = $driver.$function; - } - - if ( ! function_exists($function)) - { - if ($this->db_debug) - { - return $this->display_error('db_unsupported_function'); - } - return FALSE; - } - else - { - $args = (func_num_args() > 1) ? array_splice(func_get_args(), 1) : null; - if (is_null($args)) - { - return call_user_func($function); - } - else - { - return call_user_func_array($function, $args); - } - } - } - - // -------------------------------------------------------------------- - - /** - * Set Cache Directory Path - * - * @access public - * @param string the path to the cache directory - * @return void - */ - function cache_set_path($path = '') - { - $this->cachedir = $path; - } - - // -------------------------------------------------------------------- - - /** - * Enable Query Caching - * - * @access public - * @return void - */ - function cache_on() - { - $this->cache_on = TRUE; - return TRUE; - } - - // -------------------------------------------------------------------- - - /** - * Disable Query Caching - * - * @access public - * @return void - */ - function cache_off() - { - $this->cache_on = FALSE; - return FALSE; - } - - - // -------------------------------------------------------------------- - - /** - * Delete the cache files associated with a particular URI - * - * @access public - * @return void - */ - function cache_delete($segment_one = '', $segment_two = '') - { - if ( ! $this->_cache_init()) - { - return FALSE; - } - return $this->CACHE->delete($segment_one, $segment_two); - } - - // -------------------------------------------------------------------- - - /** - * Delete All cache files - * - * @access public - * @return void - */ - function cache_delete_all() - { - if ( ! $this->_cache_init()) - { - return FALSE; - } - - return $this->CACHE->delete_all(); - } - - // -------------------------------------------------------------------- - - /** - * Initialize the Cache Class - * - * @access private - * @return void - */ - function _cache_init() - { - if (is_object($this->CACHE) AND class_exists('CI_DB_Cache')) - { - return TRUE; - } - - if ( ! class_exists('CI_DB_Cache')) - { - if ( ! @include(BASEPATH.'database/DB_cache.php')) - { - return $this->cache_off(); - } - } - - $this->CACHE = new CI_DB_Cache($this); // pass db object to support multiple db connections and returned db objects - return TRUE; - } - - // -------------------------------------------------------------------- - - /** - * Close DB Connection - * - * @access public - * @return void - */ - function close() - { - if (is_resource($this->conn_id) OR is_object($this->conn_id)) - { - $this->_close($this->conn_id); - } - $this->conn_id = FALSE; - } - - // -------------------------------------------------------------------- - - /** - * Display an error message - * - * @access public - * @param string the error message - * @param string any "swap" values - * @param boolean whether to localize the message - * @return string sends the application/error_db.php template - */ - function display_error($error = '', $swap = '', $native = FALSE) - { - $LANG =& load_class('Lang', 'core'); - $LANG->load('db'); - - $heading = $LANG->line('db_error_heading'); - - if ($native == TRUE) - { - $message = $error; - } - else - { - $message = ( ! is_array($error)) ? array(str_replace('%s', $swap, $LANG->line($error))) : $error; - } - - // Find the most likely culprit of the error by going through - // the backtrace until the source file is no longer in the - // database folder. - - $trace = debug_backtrace(); - - foreach ($trace as $call) - { - if (isset($call['file']) && strpos($call['file'], BASEPATH.'database') === FALSE) - { - // Found it - use a relative path for safety - $message[] = 'Filename: '.str_replace(array(BASEPATH, APPPATH), '', $call['file']); - $message[] = 'Line Number: '.$call['line']; - - break; - } - } - - $error =& load_class('Exceptions', 'core'); - echo $error->show_error($heading, $message, 'error_db'); - exit; - } - - // -------------------------------------------------------------------- - - /** - * Protect Identifiers - * - * This function adds backticks if appropriate based on db type - * - * @access private - * @param mixed the item to escape - * @return mixed the item with backticks - */ - function protect_identifiers($item, $prefix_single = FALSE) - { - return $this->_protect_identifiers($item, $prefix_single); - } - - // -------------------------------------------------------------------- - - /** - * Protect Identifiers - * - * This function is used extensively by the Active Record class, and by - * a couple functions in this class. - * It takes a column or table name (optionally with an alias) and inserts - * the table prefix onto it. Some logic is necessary in order to deal with - * column names that include the path. Consider a query like this: - * - * SELECT * FROM hostname.database.table.column AS c FROM hostname.database.table - * - * Or a query with aliasing: - * - * SELECT m.member_id, m.member_name FROM members AS m - * - * Since the column name can include up to four segments (host, DB, table, column) - * or also have an alias prefix, we need to do a bit of work to figure this out and - * insert the table prefix (if it exists) in the proper position, and escape only - * the correct identifiers. - * - * @access private - * @param string - * @param bool - * @param mixed - * @param bool - * @return string - */ - function _protect_identifiers($item, $prefix_single = FALSE, $protect_identifiers = NULL, $field_exists = TRUE) - { - if ( ! is_bool($protect_identifiers)) - { - $protect_identifiers = $this->_protect_identifiers; - } - - if (is_array($item)) - { - $escaped_array = array(); - - foreach ($item as $k => $v) - { - $escaped_array[$this->_protect_identifiers($k)] = $this->_protect_identifiers($v); - } - - return $escaped_array; - } - - // Convert tabs or multiple spaces into single spaces - $item = preg_replace('/[\t ]+/', ' ', $item); - - // If the item has an alias declaration we remove it and set it aside. - // Basically we remove everything to the right of the first space - if (strpos($item, ' ') !== FALSE) - { - $alias = strstr($item, ' '); - $item = substr($item, 0, - strlen($alias)); - } - else - { - $alias = ''; - } - - // This is basically a bug fix for queries that use MAX, MIN, etc. - // If a parenthesis is found we know that we do not need to - // escape the data or add a prefix. There's probably a more graceful - // way to deal with this, but I'm not thinking of it -- Rick - if (strpos($item, '(') !== FALSE) - { - return $item.$alias; - } - - // Break the string apart if it contains periods, then insert the table prefix - // in the correct location, assuming the period doesn't indicate that we're dealing - // with an alias. While we're at it, we will escape the components - if (strpos($item, '.') !== FALSE) - { - $parts = explode('.', $item); - - // Does the first segment of the exploded item match - // one of the aliases previously identified? If so, - // we have nothing more to do other than escape the item - if (in_array($parts[0], $this->ar_aliased_tables)) - { - if ($protect_identifiers === TRUE) - { - foreach ($parts as $key => $val) - { - if ( ! in_array($val, $this->_reserved_identifiers)) - { - $parts[$key] = $this->_escape_identifiers($val); - } - } - - $item = implode('.', $parts); - } - return $item.$alias; - } - - // Is there a table prefix defined in the config file? If not, no need to do anything - if ($this->dbprefix != '') - { - // We now add the table prefix based on some logic. - // Do we have 4 segments (hostname.database.table.column)? - // If so, we add the table prefix to the column name in the 3rd segment. - if (isset($parts[3])) - { - $i = 2; - } - // Do we have 3 segments (database.table.column)? - // If so, we add the table prefix to the column name in 2nd position - elseif (isset($parts[2])) - { - $i = 1; - } - // Do we have 2 segments (table.column)? - // If so, we add the table prefix to the column name in 1st segment - else - { - $i = 0; - } - - // This flag is set when the supplied $item does not contain a field name. - // This can happen when this function is being called from a JOIN. - if ($field_exists == FALSE) - { - $i++; - } - - // Verify table prefix and replace if necessary - if ($this->swap_pre != '' && strncmp($parts[$i], $this->swap_pre, strlen($this->swap_pre)) === 0) - { - $parts[$i] = preg_replace("/^".$this->swap_pre."(\S+?)/", $this->dbprefix."\\1", $parts[$i]); - } - - // We only add the table prefix if it does not already exist - if (substr($parts[$i], 0, strlen($this->dbprefix)) != $this->dbprefix) - { - $parts[$i] = $this->dbprefix.$parts[$i]; - } - - // Put the parts back together - $item = implode('.', $parts); - } - - if ($protect_identifiers === TRUE) - { - $item = $this->_escape_identifiers($item); - } - - return $item.$alias; - } - - // Is there a table prefix? If not, no need to insert it - if ($this->dbprefix != '') - { - // Verify table prefix and replace if necessary - if ($this->swap_pre != '' && strncmp($item, $this->swap_pre, strlen($this->swap_pre)) === 0) - { - $item = preg_replace("/^".$this->swap_pre."(\S+?)/", $this->dbprefix."\\1", $item); - } - - // Do we prefix an item with no segments? - if ($prefix_single == TRUE AND substr($item, 0, strlen($this->dbprefix)) != $this->dbprefix) - { - $item = $this->dbprefix.$item; - } - } - - if ($protect_identifiers === TRUE AND ! in_array($item, $this->_reserved_identifiers)) - { - $item = $this->_escape_identifiers($item); - } - - return $item.$alias; - } - - // -------------------------------------------------------------------- - - /** - * Dummy method that allows Active Record class to be disabled - * - * This function is used extensively by every db driver. - * - * @return void - */ - protected function _reset_select() - { - } - -} - -/* End of file DB_driver.php */ -/* Location: ./system/database/DB_driver.php */ \ No newline at end of file diff --git a/system/database/DB_forge.php b/system/database/DB_forge.php deleted file mode 100644 index 6bc40411..00000000 --- a/system/database/DB_forge.php +++ /dev/null @@ -1,382 +0,0 @@ -db - $CI =& get_instance(); - $this->db =& $CI->db; - log_message('debug', "Database Forge Class Initialized"); - } - - // -------------------------------------------------------------------- - - /** - * Create database - * - * @access public - * @param string the database name - * @return bool - */ - function create_database($db_name) - { - $sql = $this->_create_database($db_name); - - if (is_bool($sql)) - { - return $sql; - } - - return $this->db->query($sql); - } - - // -------------------------------------------------------------------- - - /** - * Drop database - * - * @access public - * @param string the database name - * @return bool - */ - function drop_database($db_name) - { - $sql = $this->_drop_database($db_name); - - if (is_bool($sql)) - { - return $sql; - } - - return $this->db->query($sql); - } - - // -------------------------------------------------------------------- - - /** - * Add Key - * - * @access public - * @param string key - * @param string type - * @return void - */ - function add_key($key = '', $primary = FALSE) - { - if (is_array($key)) - { - foreach ($key as $one) - { - $this->add_key($one, $primary); - } - - return; - } - - if ($key == '') - { - show_error('Key information is required for that operation.'); - } - - if ($primary === TRUE) - { - $this->primary_keys[] = $key; - } - else - { - $this->keys[] = $key; - } - } - - // -------------------------------------------------------------------- - - /** - * Add Field - * - * @access public - * @param string collation - * @return void - */ - function add_field($field = '') - { - if ($field == '') - { - show_error('Field information is required.'); - } - - if (is_string($field)) - { - if ($field == 'id') - { - $this->add_field(array( - 'id' => array( - 'type' => 'INT', - 'constraint' => 9, - 'auto_increment' => TRUE - ) - )); - $this->add_key('id', TRUE); - } - else - { - if (strpos($field, ' ') === FALSE) - { - show_error('Field information is required for that operation.'); - } - - $this->fields[] = $field; - } - } - - if (is_array($field)) - { - $this->fields = array_merge($this->fields, $field); - } - - } - - // -------------------------------------------------------------------- - - /** - * Create Table - * - * @access public - * @param string the table name - * @return bool - */ - function create_table($table = '', $if_not_exists = FALSE) - { - if ($table == '') - { - show_error('A table name is required for that operation.'); - } - - if (count($this->fields) == 0) - { - show_error('Field information is required.'); - } - - $sql = $this->_create_table($this->db->dbprefix.$table, $this->fields, $this->primary_keys, $this->keys, $if_not_exists); - - $this->_reset(); - return $this->db->query($sql); - } - - // -------------------------------------------------------------------- - - /** - * Drop Table - * - * @access public - * @param string the table name - * @return bool - */ - function drop_table($table_name) - { - $sql = $this->_drop_table($this->db->dbprefix.$table_name); - - if (is_bool($sql)) - { - return $sql; - } - - return $this->db->query($sql); - } - - // -------------------------------------------------------------------- - - /** - * Rename Table - * - * @access public - * @param string the old table name - * @param string the new table name - * @return bool - */ - function rename_table($table_name, $new_table_name) - { - if ($table_name == '' OR $new_table_name == '') - { - show_error('A table name is required for that operation.'); - } - - $sql = $this->_rename_table($this->db->dbprefix.$table_name, $this->db->dbprefix.$new_table_name); - return $this->db->query($sql); - } - - // -------------------------------------------------------------------- - - /** - * Column Add - * - * @access public - * @param string the table name - * @param string the column name - * @param string the column definition - * @return bool - */ - function add_column($table = '', $field = array(), $after_field = '') - { - if ($table == '') - { - show_error('A table name is required for that operation.'); - } - - // add field info into field array, but we can only do one at a time - // so we cycle through - - foreach ($field as $k => $v) - { - $this->add_field(array($k => $field[$k])); - - if (count($this->fields) == 0) - { - show_error('Field information is required.'); - } - - $sql = $this->_alter_table('ADD', $this->db->dbprefix.$table, $this->fields, $after_field); - - $this->_reset(); - - if ($this->db->query($sql) === FALSE) - { - return FALSE; - } - } - - return TRUE; - - } - - // -------------------------------------------------------------------- - - /** - * Column Drop - * - * @access public - * @param string the table name - * @param string the column name - * @return bool - */ - function drop_column($table = '', $column_name = '') - { - - if ($table == '') - { - show_error('A table name is required for that operation.'); - } - - if ($column_name == '') - { - show_error('A column name is required for that operation.'); - } - - $sql = $this->_alter_table('DROP', $this->db->dbprefix.$table, $column_name); - - return $this->db->query($sql); - } - - // -------------------------------------------------------------------- - - /** - * Column Modify - * - * @access public - * @param string the table name - * @param string the column name - * @param string the column definition - * @return bool - */ - function modify_column($table = '', $field = array()) - { - if ($table == '') - { - show_error('A table name is required for that operation.'); - } - - // add field info into field array, but we can only do one at a time - // so we cycle through - - foreach ($field as $k => $v) - { - // If no name provided, use the current name - if ( ! isset($field[$k]['name'])) - { - $field[$k]['name'] = $k; - } - - $this->add_field(array($k => $field[$k])); - - if (count($this->fields) == 0) - { - show_error('Field information is required.'); - } - - $sql = $this->_alter_table('CHANGE', $this->db->dbprefix.$table, $this->fields); - - $this->_reset(); - - if ($this->db->query($sql) === FALSE) - { - return FALSE; - } - } - - return TRUE; - } - - // -------------------------------------------------------------------- - - /** - * Reset - * - * Resets table creation vars - * - * @access private - * @return void - */ - function _reset() - { - $this->fields = array(); - $this->keys = array(); - $this->primary_keys = array(); - } - -} - -/* End of file DB_forge.php */ -/* Location: ./system/database/DB_forge.php */ \ No newline at end of file diff --git a/system/database/DB_result.php b/system/database/DB_result.php deleted file mode 100644 index 48d66c8e..00000000 --- a/system/database/DB_result.php +++ /dev/null @@ -1,410 +0,0 @@ -result_array(); - else if ($type == 'object') return $this->result_object(); - else return $this->custom_result_object($type); - } - - // -------------------------------------------------------------------- - - /** - * Custom query result. - * - * @param class_name A string that represents the type of object you want back - * @return array of objects - */ - public function custom_result_object($class_name) - { - if (array_key_exists($class_name, $this->custom_result_object)) - { - return $this->custom_result_object[$class_name]; - } - - if ($this->result_id === FALSE OR $this->num_rows() == 0) - { - return array(); - } - - // add the data to the object - $this->_data_seek(0); - $result_object = array(); - - while ($row = $this->_fetch_object()) - { - $object = new $class_name(); - - foreach ($row as $key => $value) - { - $object->$key = $value; - } - - $result_object[] = $object; - } - - // return the array - return $this->custom_result_object[$class_name] = $result_object; - } - - // -------------------------------------------------------------------- - - /** - * Query result. "object" version. - * - * @access public - * @return object - */ - public function result_object() - { - if (count($this->result_object) > 0) - { - return $this->result_object; - } - - // In the event that query caching is on the result_id variable - // will return FALSE since there isn't a valid SQL resource so - // we'll simply return an empty array. - if ($this->result_id === FALSE OR $this->num_rows() == 0) - { - return array(); - } - - $this->_data_seek(0); - while ($row = $this->_fetch_object()) - { - $this->result_object[] = $row; - } - - return $this->result_object; - } - - // -------------------------------------------------------------------- - - /** - * Query result. "array" version. - * - * @access public - * @return array - */ - public function result_array() - { - if (count($this->result_array) > 0) - { - return $this->result_array; - } - - // In the event that query caching is on the result_id variable - // will return FALSE since there isn't a valid SQL resource so - // we'll simply return an empty array. - if ($this->result_id === FALSE OR $this->num_rows() == 0) - { - return array(); - } - - $this->_data_seek(0); - while ($row = $this->_fetch_assoc()) - { - $this->result_array[] = $row; - } - - return $this->result_array; - } - - // -------------------------------------------------------------------- - - /** - * Query result. Acts as a wrapper function for the following functions. - * - * @access public - * @param string - * @param string can be "object" or "array" - * @return mixed either a result object or array - */ - public function row($n = 0, $type = 'object') - { - if ( ! is_numeric($n)) - { - // We cache the row data for subsequent uses - if ( ! is_array($this->row_data)) - { - $this->row_data = $this->row_array(0); - } - - // array_key_exists() instead of isset() to allow for MySQL NULL values - if (array_key_exists($n, $this->row_data)) - { - return $this->row_data[$n]; - } - // reset the $n variable if the result was not achieved - $n = 0; - } - - if ($type == 'object') return $this->row_object($n); - else if ($type == 'array') return $this->row_array($n); - else return $this->custom_row_object($n, $type); - } - - // -------------------------------------------------------------------- - - /** - * Assigns an item into a particular column slot - * - * @access public - * @return object - */ - public function set_row($key, $value = NULL) - { - // We cache the row data for subsequent uses - if ( ! is_array($this->row_data)) - { - $this->row_data = $this->row_array(0); - } - - if (is_array($key)) - { - foreach ($key as $k => $v) - { - $this->row_data[$k] = $v; - } - - return; - } - - if ($key != '' AND ! is_null($value)) - { - $this->row_data[$key] = $value; - } - } - - // -------------------------------------------------------------------- - - /** - * Returns a single result row - custom object version - * - * @access public - * @return object - */ - public function custom_row_object($n, $type) - { - $result = $this->custom_result_object($type); - - if (count($result) == 0) - { - return $result; - } - - if ($n != $this->current_row AND isset($result[$n])) - { - $this->current_row = $n; - } - - return $result[$this->current_row]; - } - - /** - * Returns a single result row - object version - * - * @access public - * @return object - */ - public function row_object($n = 0) - { - $result = $this->result_object(); - - if (count($result) == 0) - { - return $result; - } - - if ($n != $this->current_row AND isset($result[$n])) - { - $this->current_row = $n; - } - - return $result[$this->current_row]; - } - - // -------------------------------------------------------------------- - - /** - * Returns a single result row - array version - * - * @access public - * @return array - */ - public function row_array($n = 0) - { - $result = $this->result_array(); - - if (count($result) == 0) - { - return $result; - } - - if ($n != $this->current_row AND isset($result[$n])) - { - $this->current_row = $n; - } - - return $result[$this->current_row]; - } - - - // -------------------------------------------------------------------- - - /** - * Returns the "first" row - * - * @access public - * @return object - */ - public function first_row($type = 'object') - { - $result = $this->result($type); - - if (count($result) == 0) - { - return $result; - } - return $result[0]; - } - - // -------------------------------------------------------------------- - - /** - * Returns the "last" row - * - * @access public - * @return object - */ - public function last_row($type = 'object') - { - $result = $this->result($type); - - if (count($result) == 0) - { - return $result; - } - return $result[count($result) -1]; - } - - // -------------------------------------------------------------------- - - /** - * Returns the "next" row - * - * @access public - * @return object - */ - public function next_row($type = 'object') - { - $result = $this->result($type); - - if (count($result) == 0) - { - return $result; - } - - if (isset($result[$this->current_row + 1])) - { - ++$this->current_row; - } - - return $result[$this->current_row]; - } - - // -------------------------------------------------------------------- - - /** - * Returns the "previous" row - * - * @access public - * @return object - */ - public function previous_row($type = 'object') - { - $result = $this->result($type); - - if (count($result) == 0) - { - return $result; - } - - if (isset($result[$this->current_row - 1])) - { - --$this->current_row; - } - return $result[$this->current_row]; - } - - // -------------------------------------------------------------------- - - /** - * The following functions are normally overloaded by the identically named - * methods in the platform-specific driver -- except when query caching - * is used. When caching is enabled we do not load the other driver. - * These functions are primarily here to prevent undefined function errors - * when a cached result object is in use. They are not otherwise fully - * operational due to the unavailability of the database resource IDs with - * cached results. - */ - public function num_rows() { return $this->num_rows; } - public function num_fields() { return 0; } - public function list_fields() { return array(); } - public function field_data() { return array(); } - public function free_result() { return TRUE; } - protected function _data_seek() { return TRUE; } - protected function _fetch_assoc() { return array(); } - protected function _fetch_object() { return array(); } - -} -// END DB_result class - -/* End of file DB_result.php */ -/* Location: ./system/database/DB_result.php */ diff --git a/system/database/DB_utility.php b/system/database/DB_utility.php deleted file mode 100644 index 52196b7c..00000000 --- a/system/database/DB_utility.php +++ /dev/null @@ -1,414 +0,0 @@ -db - $CI =& get_instance(); - $this->db =& $CI->db; - - log_message('debug', "Database Utility Class Initialized"); - } - - // -------------------------------------------------------------------- - - /** - * List databases - * - * @access public - * @return bool - */ - function list_databases() - { - // Is there a cached result? - if (isset($this->data_cache['db_names'])) - { - return $this->data_cache['db_names']; - } - - $query = $this->db->query($this->_list_databases()); - $dbs = array(); - if ($query->num_rows() > 0) - { - foreach ($query->result_array() as $row) - { - $dbs[] = current($row); - } - } - - $this->data_cache['db_names'] = $dbs; - return $this->data_cache['db_names']; - } - - // -------------------------------------------------------------------- - - /** - * Determine if a particular database exists - * - * @access public - * @param string - * @return boolean - */ - function database_exists($database_name) - { - // Some databases won't have access to the list_databases() function, so - // this is intended to allow them to override with their own functions as - // defined in $driver_utility.php - if (method_exists($this, '_database_exists')) - { - return $this->_database_exists($database_name); - } - else - { - return ( ! in_array($database_name, $this->list_databases())) ? FALSE : TRUE; - } - } - - - // -------------------------------------------------------------------- - - /** - * Optimize Table - * - * @access public - * @param string the table name - * @return bool - */ - function optimize_table($table_name) - { - $sql = $this->_optimize_table($table_name); - - if (is_bool($sql)) - { - show_error('db_must_use_set'); - } - - $query = $this->db->query($sql); - $res = $query->result_array(); - - // Note: Due to a bug in current() that affects some versions - // of PHP we can not pass function call directly into it - return current($res); - } - - // -------------------------------------------------------------------- - - /** - * Optimize Database - * - * @access public - * @return array - */ - function optimize_database() - { - $result = array(); - foreach ($this->db->list_tables() as $table_name) - { - $sql = $this->_optimize_table($table_name); - - if (is_bool($sql)) - { - return $sql; - } - - $query = $this->db->query($sql); - - // Build the result array... - // Note: Due to a bug in current() that affects some versions - // of PHP we can not pass function call directly into it - $res = $query->result_array(); - $res = current($res); - $key = str_replace($this->db->database.'.', '', current($res)); - $keys = array_keys($res); - unset($res[$keys[0]]); - - $result[$key] = $res; - } - - return $result; - } - - // -------------------------------------------------------------------- - - /** - * Repair Table - * - * @access public - * @param string the table name - * @return bool - */ - function repair_table($table_name) - { - $sql = $this->_repair_table($table_name); - - if (is_bool($sql)) - { - return $sql; - } - - $query = $this->db->query($sql); - - // Note: Due to a bug in current() that affects some versions - // of PHP we can not pass function call directly into it - $res = $query->result_array(); - return current($res); - } - - // -------------------------------------------------------------------- - - /** - * Generate CSV from a query result object - * - * @access public - * @param object The query result object - * @param string The delimiter - comma by default - * @param string The newline character - \n by default - * @param string The enclosure - double quote by default - * @return string - */ - function csv_from_result($query, $delim = ",", $newline = "\n", $enclosure = '"') - { - if ( ! is_object($query) OR ! method_exists($query, 'list_fields')) - { - show_error('You must submit a valid result object'); - } - - $out = ''; - - // First generate the headings from the table column names - foreach ($query->list_fields() as $name) - { - $out .= $enclosure.str_replace($enclosure, $enclosure.$enclosure, $name).$enclosure.$delim; - } - - $out = rtrim($out); - $out .= $newline; - - // Next blast through the result array and build out the rows - foreach ($query->result_array() as $row) - { - foreach ($row as $item) - { - $out .= $enclosure.str_replace($enclosure, $enclosure.$enclosure, $item).$enclosure.$delim; - } - $out = rtrim($out); - $out .= $newline; - } - - return $out; - } - - // -------------------------------------------------------------------- - - /** - * Generate XML data from a query result object - * - * @access public - * @param object The query result object - * @param array Any preferences - * @return string - */ - function xml_from_result($query, $params = array()) - { - if ( ! is_object($query) OR ! method_exists($query, 'list_fields')) - { - show_error('You must submit a valid result object'); - } - - // Set our default values - foreach (array('root' => 'root', 'element' => 'element', 'newline' => "\n", 'tab' => "\t") as $key => $val) - { - if ( ! isset($params[$key])) - { - $params[$key] = $val; - } - } - - // Create variables for convenience - extract($params); - - // Load the xml helper - $CI =& get_instance(); - $CI->load->helper('xml'); - - // Generate the result - $xml = "<{$root}>".$newline; - foreach ($query->result_array() as $row) - { - $xml .= $tab."<{$element}>".$newline; - - foreach ($row as $key => $val) - { - $xml .= $tab.$tab."<{$key}>".xml_convert($val)."".$newline; - } - $xml .= $tab."".$newline; - } - $xml .= "".$newline; - - return $xml; - } - - // -------------------------------------------------------------------- - - /** - * Database Backup - * - * @access public - * @return void - */ - function backup($params = array()) - { - // If the parameters have not been submitted as an - // array then we know that it is simply the table - // name, which is a valid short cut. - if (is_string($params)) - { - $params = array('tables' => $params); - } - - // ------------------------------------------------------ - - // Set up our default preferences - $prefs = array( - 'tables' => array(), - 'ignore' => array(), - 'filename' => '', - 'format' => 'gzip', // gzip, zip, txt - 'add_drop' => TRUE, - 'add_insert' => TRUE, - 'newline' => "\n" - ); - - // Did the user submit any preferences? If so set them.... - if (count($params) > 0) - { - foreach ($prefs as $key => $val) - { - if (isset($params[$key])) - { - $prefs[$key] = $params[$key]; - } - } - } - - // ------------------------------------------------------ - - // Are we backing up a complete database or individual tables? - // If no table names were submitted we'll fetch the entire table list - if (count($prefs['tables']) == 0) - { - $prefs['tables'] = $this->db->list_tables(); - } - - // ------------------------------------------------------ - - // Validate the format - if ( ! in_array($prefs['format'], array('gzip', 'zip', 'txt'), TRUE)) - { - $prefs['format'] = 'txt'; - } - - // ------------------------------------------------------ - - // Is the encoder supported? If not, we'll either issue an - // error or use plain text depending on the debug settings - if (($prefs['format'] == 'gzip' AND ! @function_exists('gzencode')) - OR ($prefs['format'] == 'zip' AND ! @function_exists('gzcompress'))) - { - if ($this->db->db_debug) - { - return $this->db->display_error('db_unsuported_compression'); - } - - $prefs['format'] = 'txt'; - } - - // ------------------------------------------------------ - - // Set the filename if not provided - Only needed with Zip files - if ($prefs['filename'] == '' AND $prefs['format'] == 'zip') - { - $prefs['filename'] = (count($prefs['tables']) == 1) ? $prefs['tables'] : $this->db->database; - $prefs['filename'] .= '_'.date('Y-m-d_H-i', time()); - } - - // ------------------------------------------------------ - - // Was a Gzip file requested? - if ($prefs['format'] == 'gzip') - { - return gzencode($this->_backup($prefs)); - } - - // ------------------------------------------------------ - - // Was a text file requested? - if ($prefs['format'] == 'txt') - { - return $this->_backup($prefs); - } - - // ------------------------------------------------------ - - // Was a Zip file requested? - if ($prefs['format'] == 'zip') - { - // If they included the .zip file extension we'll remove it - if (preg_match("|.+?\.zip$|", $prefs['filename'])) - { - $prefs['filename'] = str_replace('.zip', '', $prefs['filename']); - } - - // Tack on the ".sql" file extension if needed - if ( ! preg_match("|.+?\.sql$|", $prefs['filename'])) - { - $prefs['filename'] .= '.sql'; - } - - // Load the Zip class and output it - - $CI =& get_instance(); - $CI->load->library('zip'); - $CI->zip->add_data($prefs['filename'], $this->_backup($prefs)); - return $CI->zip->get_zip(); - } - - } - -} - - -/* End of file DB_utility.php */ -/* Location: ./system/database/DB_utility.php */ \ No newline at end of file diff --git a/system/database/drivers/cubrid/cubrid_driver.php b/system/database/drivers/cubrid/cubrid_driver.php deleted file mode 100644 index d0114041..00000000 --- a/system/database/drivers/cubrid/cubrid_driver.php +++ /dev/null @@ -1,792 +0,0 @@ -port == '') - { - $this->port = self::DEFAULT_PORT; - } - - $conn = cubrid_connect($this->hostname, $this->port, $this->database, $this->username, $this->password); - - if ($conn) - { - // Check if a user wants to run queries in dry, i.e. run the - // queries but not commit them. - if (isset($this->auto_commit) && ! $this->auto_commit) - { - cubrid_set_autocommit($conn, CUBRID_AUTOCOMMIT_FALSE); - } - else - { - cubrid_set_autocommit($conn, CUBRID_AUTOCOMMIT_TRUE); - $this->auto_commit = TRUE; - } - } - - return $conn; - } - - // -------------------------------------------------------------------- - - /** - * Persistent database connection - * In CUBRID persistent DB connection is supported natively in CUBRID - * engine which can be configured in the CUBRID Broker configuration - * file by setting the CCI_PCONNECT parameter to ON. In that case, all - * connections established between the client application and the - * server will become persistent. This is calling the same - * @cubrid_connect function will establish persisten connection - * considering that the CCI_PCONNECT is ON. - * - * @access private called by the base class - * @return resource - */ - function db_pconnect() - { - return $this->db_connect(); - } - - // -------------------------------------------------------------------- - - /** - * Reconnect - * - * Keep / reestablish the db connection if no queries have been - * sent for a length of time exceeding the server's idle timeout - * - * @access public - * @return void - */ - function reconnect() - { - if (cubrid_ping($this->conn_id) === FALSE) - { - $this->conn_id = FALSE; - } - } - - // -------------------------------------------------------------------- - - /** - * Select the database - * - * @access private called by the base class - * @return resource - */ - function db_select() - { - // In CUBRID there is no need to select a database as the database - // is chosen at the connection time. - // So, to determine if the database is "selected", all we have to - // do is ping the server and return that value. - return cubrid_ping($this->conn_id); - } - - // -------------------------------------------------------------------- - - /** - * Set client character set - * - * @access public - * @param string - * @param string - * @return resource - */ - function db_set_charset($charset, $collation) - { - // In CUBRID, there is no need to set charset or collation. - // This is why returning true will allow the application continue - // its normal process. - return TRUE; - } - - // -------------------------------------------------------------------- - - /** - * Version number query string - * - * @access public - * @return string - */ - function _version() - { - // To obtain the CUBRID Server version, no need to run the SQL query. - // CUBRID PHP API provides a function to determin this value. - // This is why we also need to add 'cubrid' value to the list of - // $driver_version_exceptions array in DB_driver class in - // version() function. - return cubrid_get_server_info($this->conn_id); - } - - // -------------------------------------------------------------------- - - /** - * Execute the query - * - * @access private called by the base class - * @param string an SQL query - * @return resource - */ - function _execute($sql) - { - $sql = $this->_prep_query($sql); - return @cubrid_query($sql, $this->conn_id); - } - - // -------------------------------------------------------------------- - - /** - * Prep the query - * - * If needed, each database adapter can prep the query string - * - * @access private called by execute() - * @param string an SQL query - * @return string - */ - function _prep_query($sql) - { - // No need to prepare - return $sql; - } - - // -------------------------------------------------------------------- - - /** - * Begin Transaction - * - * @access public - * @return bool - */ - function trans_begin($test_mode = FALSE) - { - if ( ! $this->trans_enabled) - { - return TRUE; - } - - // When transactions are nested we only begin/commit/rollback the outermost ones - if ($this->_trans_depth > 0) - { - return TRUE; - } - - // Reset the transaction failure flag. - // If the $test_mode flag is set to TRUE transactions will be rolled back - // even if the queries produce a successful result. - $this->_trans_failure = ($test_mode === TRUE) ? TRUE : FALSE; - - if (cubrid_get_autocommit($this->conn_id)) - { - cubrid_set_autocommit($this->conn_id, CUBRID_AUTOCOMMIT_FALSE); - } - - return TRUE; - } - - // -------------------------------------------------------------------- - - /** - * Commit Transaction - * - * @access public - * @return bool - */ - function trans_commit() - { - if ( ! $this->trans_enabled) - { - return TRUE; - } - - // When transactions are nested we only begin/commit/rollback the outermost ones - if ($this->_trans_depth > 0) - { - return TRUE; - } - - cubrid_commit($this->conn_id); - - if ($this->auto_commit && ! cubrid_get_autocommit($this->conn_id)) - { - cubrid_set_autocommit($this->conn_id, CUBRID_AUTOCOMMIT_TRUE); - } - - return TRUE; - } - - // -------------------------------------------------------------------- - - /** - * Rollback Transaction - * - * @access public - * @return bool - */ - function trans_rollback() - { - if ( ! $this->trans_enabled) - { - return TRUE; - } - - // When transactions are nested we only begin/commit/rollback the outermost ones - if ($this->_trans_depth > 0) - { - return TRUE; - } - - cubrid_rollback($this->conn_id); - - if ($this->auto_commit && ! cubrid_get_autocommit($this->conn_id)) - { - cubrid_set_autocommit($this->conn_id, CUBRID_AUTOCOMMIT_TRUE); - } - - return TRUE; - } - - // -------------------------------------------------------------------- - - /** - * Escape String - * - * @access public - * @param string - * @param bool whether or not the string will be used in a LIKE condition - * @return string - */ - function escape_str($str, $like = FALSE) - { - if (is_array($str)) - { - foreach ($str as $key => $val) - { - $str[$key] = $this->escape_str($val, $like); - } - - return $str; - } - - if (function_exists('cubrid_real_escape_string') AND is_resource($this->conn_id)) - { - $str = cubrid_real_escape_string($str, $this->conn_id); - } - else - { - $str = addslashes($str); - } - - // escape LIKE condition wildcards - if ($like === TRUE) - { - $str = str_replace(array('%', '_'), array('\\%', '\\_'), $str); - } - - return $str; - } - - // -------------------------------------------------------------------- - - /** - * Affected Rows - * - * @access public - * @return integer - */ - function affected_rows() - { - return @cubrid_affected_rows($this->conn_id); - } - - // -------------------------------------------------------------------- - - /** - * Insert ID - * - * @access public - * @return integer - */ - function insert_id() - { - return @cubrid_insert_id($this->conn_id); - } - - // -------------------------------------------------------------------- - - /** - * "Count All" query - * - * Generates a platform-specific query string that counts all records in - * the specified table - * - * @access public - * @param string - * @return string - */ - function count_all($table = '') - { - if ($table == '') - { - return 0; - } - - $query = $this->query($this->_count_string . $this->_protect_identifiers('numrows') . " FROM " . $this->_protect_identifiers($table, TRUE, NULL, FALSE)); - - if ($query->num_rows() == 0) - { - return 0; - } - - $row = $query->row(); - $this->_reset_select(); - return (int) $row->numrows; - } - - // -------------------------------------------------------------------- - - /** - * List table query - * - * Generates a platform-specific query string so that the table names can be fetched - * - * @access private - * @param boolean - * @return string - */ - function _list_tables($prefix_limit = FALSE) - { - $sql = "SHOW TABLES"; - - if ($prefix_limit !== FALSE AND $this->dbprefix != '') - { - $sql .= " LIKE '".$this->escape_like_str($this->dbprefix)."%'"; - } - - return $sql; - } - - // -------------------------------------------------------------------- - - /** - * Show column query - * - * Generates a platform-specific query string so that the column names can be fetched - * - * @access public - * @param string the table name - * @return string - */ - function _list_columns($table = '') - { - return "SHOW COLUMNS FROM ".$this->_protect_identifiers($table, TRUE, NULL, FALSE); - } - - // -------------------------------------------------------------------- - - /** - * Field data query - * - * Generates a platform-specific query so that the column data can be retrieved - * - * @access public - * @param string the table name - * @return object - */ - function _field_data($table) - { - return "SELECT * FROM ".$table." LIMIT 1"; - } - - // -------------------------------------------------------------------- - - /** - * The error message string - * - * @access private - * @return string - */ - function _error_message() - { - return cubrid_error($this->conn_id); - } - - // -------------------------------------------------------------------- - - /** - * The error message number - * - * @access private - * @return integer - */ - function _error_number() - { - return cubrid_errno($this->conn_id); - } - - // -------------------------------------------------------------------- - - /** - * Escape the SQL Identifiers - * - * This function escapes column and table names - * - * @access private - * @param string - * @return string - */ - function _escape_identifiers($item) - { - if ($this->_escape_char == '') - { - return $item; - } - - foreach ($this->_reserved_identifiers as $id) - { - if (strpos($item, '.'.$id) !== FALSE) - { - $str = $this->_escape_char. str_replace('.', $this->_escape_char.'.', $item); - - // remove duplicates if the user already included the escape - return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str); - } - } - - if (strpos($item, '.') !== FALSE) - { - $str = $this->_escape_char.str_replace('.', $this->_escape_char.'.'.$this->_escape_char, $item).$this->_escape_char; - } - else - { - $str = $this->_escape_char.$item.$this->_escape_char; - } - - // remove duplicates if the user already included the escape - return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str); - } - - // -------------------------------------------------------------------- - - /** - * From Tables - * - * This function implicitly groups FROM tables so there is no confusion - * about operator precedence in harmony with SQL standards - * - * @access public - * @param type - * @return type - */ - function _from_tables($tables) - { - if ( ! is_array($tables)) - { - $tables = array($tables); - } - - return '('.implode(', ', $tables).')'; - } - - // -------------------------------------------------------------------- - - /** - * Insert statement - * - * Generates a platform-specific insert string from the supplied data - * - * @access public - * @param string the table name - * @param array the insert keys - * @param array the insert values - * @return string - */ - function _insert($table, $keys, $values) - { - return "INSERT INTO ".$table." (\"".implode('", "', $keys)."\") VALUES (".implode(', ', $values).")"; - } - - // -------------------------------------------------------------------- - - - /** - * Replace statement - * - * Generates a platform-specific replace string from the supplied data - * - * @access public - * @param string the table name - * @param array the insert keys - * @param array the insert values - * @return string - */ - function _replace($table, $keys, $values) - { - return "REPLACE INTO ".$table." (\"".implode('", "', $keys)."\") VALUES (".implode(', ', $values).")"; - } - - // -------------------------------------------------------------------- - - /** - * Insert_batch statement - * - * Generates a platform-specific insert string from the supplied data - * - * @access public - * @param string the table name - * @param array the insert keys - * @param array the insert values - * @return string - */ - function _insert_batch($table, $keys, $values) - { - return "INSERT INTO ".$table." (\"".implode('", "', $keys)."\") VALUES ".implode(', ', $values); - } - - // -------------------------------------------------------------------- - - - /** - * Update statement - * - * Generates a platform-specific update string from the supplied data - * - * @access public - * @param string the table name - * @param array the update data - * @param array the where clause - * @param array the orderby clause - * @param array the limit clause - * @return string - */ - function _update($table, $values, $where, $orderby = array(), $limit = FALSE) - { - foreach ($values as $key => $val) - { - $valstr[] = sprintf('"%s" = %s', $key, $val); - } - - $limit = ( ! $limit) ? '' : ' LIMIT '.$limit; - - $orderby = (count($orderby) >= 1)?' ORDER BY '.implode(", ", $orderby):''; - - $sql = "UPDATE ".$table." SET ".implode(', ', $valstr); - - $sql .= ($where != '' AND count($where) >=1) ? " WHERE ".implode(" ", $where) : ''; - - $sql .= $orderby.$limit; - - return $sql; - } - - // -------------------------------------------------------------------- - - - /** - * Update_Batch statement - * - * Generates a platform-specific batch update string from the supplied data - * - * @access public - * @param string the table name - * @param array the update data - * @param array the where clause - * @return string - */ - function _update_batch($table, $values, $index, $where = NULL) - { - $ids = array(); - $where = ($where != '' AND count($where) >=1) ? implode(" ", $where).' AND ' : ''; - - foreach ($values as $key => $val) - { - $ids[] = $val[$index]; - - foreach (array_keys($val) as $field) - { - if ($field != $index) - { - $final[$field][] = 'WHEN '.$index.' = '.$val[$index].' THEN '.$val[$field]; - } - } - } - - $sql = "UPDATE ".$table." SET "; - $cases = ''; - - foreach ($final as $k => $v) - { - $cases .= $k.' = CASE '."\n"; - foreach ($v as $row) - { - $cases .= $row."\n"; - } - - $cases .= 'ELSE '.$k.' END, '; - } - - $sql .= substr($cases, 0, -2); - - $sql .= ' WHERE '.$where.$index.' IN ('.implode(',', $ids).')'; - - return $sql; - } - - // -------------------------------------------------------------------- - - - /** - * Truncate statement - * - * Generates a platform-specific truncate string from the supplied data - * If the database does not support the truncate() command - * This function maps to "DELETE FROM table" - * - * @access public - * @param string the table name - * @return string - */ - function _truncate($table) - { - return "TRUNCATE ".$table; - } - - // -------------------------------------------------------------------- - - /** - * Delete statement - * - * Generates a platform-specific delete string from the supplied data - * - * @access public - * @param string the table name - * @param array the where clause - * @param string the limit clause - * @return string - */ - function _delete($table, $where = array(), $like = array(), $limit = FALSE) - { - $conditions = ''; - - if (count($where) > 0 OR count($like) > 0) - { - $conditions = "\nWHERE "; - $conditions .= implode("\n", $this->ar_where); - - if (count($where) > 0 && count($like) > 0) - { - $conditions .= " AND "; - } - $conditions .= implode("\n", $like); - } - - $limit = ( ! $limit) ? '' : ' LIMIT '.$limit; - - return "DELETE FROM ".$table.$conditions.$limit; - } - - // -------------------------------------------------------------------- - - /** - * Limit string - * - * Generates a platform-specific LIMIT clause - * - * @access public - * @param string the sql query string - * @param integer the number of rows to limit the query to - * @param integer the offset value - * @return string - */ - function _limit($sql, $limit, $offset) - { - if ($offset == 0) - { - $offset = ''; - } - else - { - $offset .= ", "; - } - - return $sql."LIMIT ".$offset.$limit; - } - - // -------------------------------------------------------------------- - - /** - * Close DB Connection - * - * @access public - * @param resource - * @return void - */ - function _close($conn_id) - { - @cubrid_close($conn_id); - } - -} - - -/* End of file cubrid_driver.php */ -/* Location: ./system/database/drivers/cubrid/cubrid_driver.php */ \ No newline at end of file diff --git a/system/database/drivers/cubrid/cubrid_forge.php b/system/database/drivers/cubrid/cubrid_forge.php deleted file mode 100644 index bab03f74..00000000 --- a/system/database/drivers/cubrid/cubrid_forge.php +++ /dev/null @@ -1,288 +0,0 @@ -$attributes) - { - // Numeric field names aren't allowed in databases, so if the key is - // numeric, we know it was assigned by PHP and the developer manually - // entered the field information, so we'll simply add it to the list - if (is_numeric($field)) - { - $sql .= "\n\t$attributes"; - } - else - { - $attributes = array_change_key_case($attributes, CASE_UPPER); - - $sql .= "\n\t\"" . $this->db->_protect_identifiers($field) . "\""; - - if (array_key_exists('NAME', $attributes)) - { - $sql .= ' '.$this->db->_protect_identifiers($attributes['NAME']).' '; - } - - if (array_key_exists('TYPE', $attributes)) - { - $sql .= ' '.$attributes['TYPE']; - - if (array_key_exists('CONSTRAINT', $attributes)) - { - switch ($attributes['TYPE']) - { - case 'decimal': - case 'float': - case 'numeric': - $sql .= '('.implode(',', $attributes['CONSTRAINT']).')'; - break; - case 'enum': // As of version 8.4.0 CUBRID does not support - // enum data type. - break; - case 'set': - $sql .= '("'.implode('","', $attributes['CONSTRAINT']).'")'; - break; - default: - $sql .= '('.$attributes['CONSTRAINT'].')'; - } - } - } - - if (array_key_exists('UNSIGNED', $attributes) && $attributes['UNSIGNED'] === TRUE) - { - //$sql .= ' UNSIGNED'; - // As of version 8.4.0 CUBRID does not support UNSIGNED INTEGER data type. - // Will be supported in the next release as a part of MySQL Compatibility. - } - - if (array_key_exists('DEFAULT', $attributes)) - { - $sql .= ' DEFAULT \''.$attributes['DEFAULT'].'\''; - } - - if (array_key_exists('NULL', $attributes) && $attributes['NULL'] === TRUE) - { - $sql .= ' NULL'; - } - else - { - $sql .= ' NOT NULL'; - } - - if (array_key_exists('AUTO_INCREMENT', $attributes) && $attributes['AUTO_INCREMENT'] === TRUE) - { - $sql .= ' AUTO_INCREMENT'; - } - - if (array_key_exists('UNIQUE', $attributes) && $attributes['UNIQUE'] === TRUE) - { - $sql .= ' UNIQUE'; - } - } - - // don't add a comma on the end of the last field - if (++$current_field_count < count($fields)) - { - $sql .= ','; - } - } - - return $sql; - } - - // -------------------------------------------------------------------- - - /** - * Create Table - * - * @access private - * @param string the table name - * @param mixed the fields - * @param mixed primary key(s) - * @param mixed key(s) - * @param boolean should 'IF NOT EXISTS' be added to the SQL - * @return bool - */ - function _create_table($table, $fields, $primary_keys, $keys, $if_not_exists) - { - $sql = 'CREATE TABLE '; - - if ($if_not_exists === TRUE) - { - //$sql .= 'IF NOT EXISTS '; - // As of version 8.4.0 CUBRID does not support this SQL syntax. - } - - $sql .= $this->db->_escape_identifiers($table)." ("; - - $sql .= $this->_process_fields($fields); - - // If there is a PK defined - if (count($primary_keys) > 0) - { - $key_name = "pk_" . $table . "_" . - $this->db->_protect_identifiers(implode('_', $primary_keys)); - - $primary_keys = $this->db->_protect_identifiers($primary_keys); - $sql .= ",\n\tCONSTRAINT " . $key_name . " PRIMARY KEY(" . implode(', ', $primary_keys) . ")"; - } - - if (is_array($keys) && count($keys) > 0) - { - foreach ($keys as $key) - { - if (is_array($key)) - { - $key_name = $this->db->_protect_identifiers(implode('_', $key)); - $key = $this->db->_protect_identifiers($key); - } - else - { - $key_name = $this->db->_protect_identifiers($key); - $key = array($key_name); - } - - $sql .= ",\n\tKEY \"{$key_name}\" (" . implode(', ', $key) . ")"; - } - } - - $sql .= "\n);"; - - return $sql; - } - - // -------------------------------------------------------------------- - - /** - * Drop Table - * - * @access private - * @return string - */ - function _drop_table($table) - { - return "DROP TABLE IF EXISTS ".$this->db->_escape_identifiers($table); - } - - // -------------------------------------------------------------------- - - /** - * Alter table query - * - * Generates a platform-specific query so that a table can be altered - * Called by add_column(), drop_column(), and column_alter(), - * - * @access private - * @param string the ALTER type (ADD, DROP, CHANGE) - * @param string the column name - * @param array fields - * @param string the field after which we should add the new field - * @return object - */ - function _alter_table($alter_type, $table, $fields, $after_field = '') - { - $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table)." $alter_type "; - - // DROP has everything it needs now. - if ($alter_type == 'DROP') - { - return $sql.$this->db->_protect_identifiers($fields); - } - - $sql .= $this->_process_fields($fields); - - if ($after_field != '') - { - $sql .= ' AFTER ' . $this->db->_protect_identifiers($after_field); - } - - return $sql; - } - - // -------------------------------------------------------------------- - - /** - * Rename a table - * - * Generates a platform-specific query so that a table can be renamed - * - * @access private - * @param string the old table name - * @param string the new table name - * @return string - */ - function _rename_table($table_name, $new_table_name) - { - $sql = 'RENAME TABLE '.$this->db->_protect_identifiers($table_name)." AS ".$this->db->_protect_identifiers($new_table_name); - return $sql; - } - -} - -/* End of file cubrid_forge.php */ -/* Location: ./system/database/drivers/cubrid/cubrid_forge.php */ \ No newline at end of file diff --git a/system/database/drivers/cubrid/cubrid_result.php b/system/database/drivers/cubrid/cubrid_result.php deleted file mode 100644 index 6f0c2b5f..00000000 --- a/system/database/drivers/cubrid/cubrid_result.php +++ /dev/null @@ -1,202 +0,0 @@ -result_id); - } - - // -------------------------------------------------------------------- - - /** - * Number of fields in the result set - * - * @access public - * @return integer - */ - function num_fields() - { - return @cubrid_num_fields($this->result_id); - } - - // -------------------------------------------------------------------- - - /** - * Fetch Field Names - * - * Generates an array of column names - * - * @access public - * @return array - */ - function list_fields() - { - return cubrid_column_names($this->result_id); - } - - // -------------------------------------------------------------------- - - /** - * Field data - * - * Generates an array of objects containing field meta-data - * - * @access public - * @return array - */ - function field_data() - { - $retval = array(); - - $tablePrimaryKeys = array(); - - while ($field = cubrid_fetch_field($this->result_id)) - { - $F = new stdClass(); - $F->name = $field->name; - $F->type = $field->type; - $F->default = $field->def; - $F->max_length = $field->max_length; - - // At this moment primary_key property is not returned when - // cubrid_fetch_field is called. The following code will - // provide a patch for it. primary_key property will be added - // in the next release. - - // TODO: later version of CUBRID will provide primary_key - // property. - // When PK is defined in CUBRID, an index is automatically - // created in the db_index system table in the form of - // pk_tblname_fieldname. So the following will count how many - // columns are there which satisfy this format. - // The query will search for exact single columns, thus - // compound PK is not supported. - $res = cubrid_query($this->conn_id, - "SELECT COUNT(*) FROM db_index WHERE class_name = '" . $field->table . - "' AND is_primary_key = 'YES' AND index_name = 'pk_" . - $field->table . "_" . $field->name . "'" - ); - - if ($res) - { - $row = cubrid_fetch_array($res, CUBRID_NUM); - $F->primary_key = ($row[0] > 0 ? 1 : null); - } - else - { - $F->primary_key = null; - } - - if (is_resource($res)) - { - cubrid_close_request($res); - $this->result_id = FALSE; - } - - $retval[] = $F; - } - - return $retval; - } - - // -------------------------------------------------------------------- - - /** - * Free the result - * - * @return null - */ - function free_result() - { - if(is_resource($this->result_id) || - get_resource_type($this->result_id) == "Unknown" && - preg_match('/Resource id #/', strval($this->result_id))) - { - cubrid_close_request($this->result_id); - $this->result_id = FALSE; - } - } - - // -------------------------------------------------------------------- - - /** - * Data Seek - * - * Moves the internal pointer to the desired offset. We call - * this internally before fetching results to make sure the - * result set starts at zero - * - * @access private - * @return array - */ - function _data_seek($n = 0) - { - return cubrid_data_seek($this->result_id, $n); - } - - // -------------------------------------------------------------------- - - /** - * Result - associative array - * - * Returns the result set as an array - * - * @access private - * @return array - */ - function _fetch_assoc() - { - return cubrid_fetch_assoc($this->result_id); - } - - // -------------------------------------------------------------------- - - /** - * Result - object - * - * Returns the result set as an object - * - * @access private - * @return object - */ - function _fetch_object() - { - return cubrid_fetch_object($this->result_id); - } - -} - - -/* End of file cubrid_result.php */ -/* Location: ./system/database/drivers/cubrid/cubrid_result.php */ \ No newline at end of file diff --git a/system/database/drivers/cubrid/cubrid_utility.php b/system/database/drivers/cubrid/cubrid_utility.php deleted file mode 100644 index cd16d1e1..00000000 --- a/system/database/drivers/cubrid/cubrid_utility.php +++ /dev/null @@ -1,108 +0,0 @@ -conn_id) - { - return "SELECT '" . $this->database . "'"; - } - else - { - return FALSE; - } - } - - // -------------------------------------------------------------------- - - /** - * Optimize table query - * - * Generates a platform-specific query so that a table can be optimized - * - * @access private - * @param string the table name - * @return object - * @link http://www.cubrid.org/manual/840/en/Optimize%20Database - */ - function _optimize_table($table) - { - // No SQL based support in CUBRID as of version 8.4.0. Database or - // table optimization can be performed using CUBRID Manager - // database administration tool. See the link above for more info. - return FALSE; - } - - // -------------------------------------------------------------------- - - /** - * Repair table query - * - * Generates a platform-specific query so that a table can be repaired - * - * @access private - * @param string the table name - * @return object - * @link http://www.cubrid.org/manual/840/en/Checking%20Database%20Consistency - */ - function _repair_table($table) - { - // Not supported in CUBRID as of version 8.4.0. Database or - // table consistency can be checked using CUBRID Manager - // database administration tool. See the link above for more info. - return FALSE; - } - - // -------------------------------------------------------------------- - /** - * CUBRID Export - * - * @access private - * @param array Preferences - * @return mixed - */ - function _backup($params = array()) - { - // No SQL based support in CUBRID as of version 8.4.0. Database or - // table backup can be performed using CUBRID Manager - // database administration tool. - return $this->db->display_error('db_unsuported_feature'); - } -} - -/* End of file cubrid_utility.php */ -/* Location: ./system/database/drivers/cubrid/cubrid_utility.php */ \ No newline at end of file diff --git a/system/database/drivers/cubrid/index.html b/system/database/drivers/cubrid/index.html deleted file mode 100644 index c942a79c..00000000 --- a/system/database/drivers/cubrid/index.html +++ /dev/null @@ -1,10 +0,0 @@ - - - 403 Forbidden - - - -

Directory access is forbidden.

- - - \ No newline at end of file diff --git a/system/database/drivers/index.html b/system/database/drivers/index.html deleted file mode 100644 index c942a79c..00000000 --- a/system/database/drivers/index.html +++ /dev/null @@ -1,10 +0,0 @@ - - - 403 Forbidden - - - -

Directory access is forbidden.

- - - \ No newline at end of file diff --git a/system/database/drivers/mssql/index.html b/system/database/drivers/mssql/index.html deleted file mode 100644 index c942a79c..00000000 --- a/system/database/drivers/mssql/index.html +++ /dev/null @@ -1,10 +0,0 @@ - - - 403 Forbidden - - - -

Directory access is forbidden.

- - - \ No newline at end of file diff --git a/system/database/drivers/mssql/mssql_driver.php b/system/database/drivers/mssql/mssql_driver.php deleted file mode 100644 index b39bd936..00000000 --- a/system/database/drivers/mssql/mssql_driver.php +++ /dev/null @@ -1,667 +0,0 @@ -port != '') - { - $this->hostname .= ','.$this->port; - } - - return @mssql_connect($this->hostname, $this->username, $this->password); - } - - // -------------------------------------------------------------------- - - /** - * Persistent database connection - * - * @access private called by the base class - * @return resource - */ - function db_pconnect() - { - if ($this->port != '') - { - $this->hostname .= ','.$this->port; - } - - return @mssql_pconnect($this->hostname, $this->username, $this->password); - } - - // -------------------------------------------------------------------- - - /** - * Reconnect - * - * Keep / reestablish the db connection if no queries have been - * sent for a length of time exceeding the server's idle timeout - * - * @access public - * @return void - */ - function reconnect() - { - // not implemented in MSSQL - } - - // -------------------------------------------------------------------- - - /** - * Select the database - * - * @access private called by the base class - * @return resource - */ - function db_select() - { - // Note: The brackets are required in the event that the DB name - // contains reserved characters - return @mssql_select_db('['.$this->database.']', $this->conn_id); - } - - // -------------------------------------------------------------------- - - /** - * Set client character set - * - * @access public - * @param string - * @param string - * @return resource - */ - function db_set_charset($charset, $collation) - { - // @todo - add support if needed - return TRUE; - } - - // -------------------------------------------------------------------- - - /** - * Execute the query - * - * @access private called by the base class - * @param string an SQL query - * @return resource - */ - function _execute($sql) - { - $sql = $this->_prep_query($sql); - return @mssql_query($sql, $this->conn_id); - } - - // -------------------------------------------------------------------- - - /** - * Prep the query - * - * If needed, each database adapter can prep the query string - * - * @access private called by execute() - * @param string an SQL query - * @return string - */ - function _prep_query($sql) - { - return $sql; - } - - // -------------------------------------------------------------------- - - /** - * Begin Transaction - * - * @access public - * @return bool - */ - function trans_begin($test_mode = FALSE) - { - if ( ! $this->trans_enabled) - { - return TRUE; - } - - // When transactions are nested we only begin/commit/rollback the outermost ones - if ($this->_trans_depth > 0) - { - return TRUE; - } - - // Reset the transaction failure flag. - // If the $test_mode flag is set to TRUE transactions will be rolled back - // even if the queries produce a successful result. - $this->_trans_failure = ($test_mode === TRUE) ? TRUE : FALSE; - - $this->simple_query('BEGIN TRAN'); - return TRUE; - } - - // -------------------------------------------------------------------- - - /** - * Commit Transaction - * - * @access public - * @return bool - */ - function trans_commit() - { - if ( ! $this->trans_enabled) - { - return TRUE; - } - - // When transactions are nested we only begin/commit/rollback the outermost ones - if ($this->_trans_depth > 0) - { - return TRUE; - } - - $this->simple_query('COMMIT TRAN'); - return TRUE; - } - - // -------------------------------------------------------------------- - - /** - * Rollback Transaction - * - * @access public - * @return bool - */ - function trans_rollback() - { - if ( ! $this->trans_enabled) - { - return TRUE; - } - - // When transactions are nested we only begin/commit/rollback the outermost ones - if ($this->_trans_depth > 0) - { - return TRUE; - } - - $this->simple_query('ROLLBACK TRAN'); - return TRUE; - } - - // -------------------------------------------------------------------- - - /** - * Escape String - * - * @access public - * @param string - * @param bool whether or not the string will be used in a LIKE condition - * @return string - */ - function escape_str($str, $like = FALSE) - { - if (is_array($str)) - { - foreach ($str as $key => $val) - { - $str[$key] = $this->escape_str($val, $like); - } - - return $str; - } - - // Escape single quotes - $str = str_replace("'", "''", remove_invisible_characters($str)); - - // escape LIKE condition wildcards - if ($like === TRUE) - { - $str = str_replace( - array($this->_like_escape_chr, '%', '_'), - array($this->_like_escape_chr.$this->_like_escape_chr, $this->_like_escape_chr.'%', $this->_like_escape_chr.'_'), - $str - ); - } - - return $str; - } - - // -------------------------------------------------------------------- - - /** - * Affected Rows - * - * @access public - * @return integer - */ - function affected_rows() - { - return @mssql_rows_affected($this->conn_id); - } - - // -------------------------------------------------------------------- - - /** - * Insert ID - * - * Returns the last id created in the Identity column. - * - * @access public - * @return integer - */ - function insert_id() - { - $ver = self::_parse_major_version($this->version()); - $sql = ($ver >= 8 ? "SELECT SCOPE_IDENTITY() AS last_id" : "SELECT @@IDENTITY AS last_id"); - $query = $this->query($sql); - $row = $query->row(); - return $row->last_id; - } - - // -------------------------------------------------------------------- - - /** - * Parse major version - * - * Grabs the major version number from the - * database server version string passed in. - * - * @access private - * @param string $version - * @return int16 major version number - */ - function _parse_major_version($version) - { - preg_match('/([0-9]+)\.([0-9]+)\.([0-9]+)/', $version, $ver_info); - return $ver_info[1]; // return the major version b/c that's all we're interested in. - } - - // -------------------------------------------------------------------- - - /** - * Version number query string - * - * @access public - * @return string - */ - function _version() - { - return "SELECT @@VERSION AS ver"; - } - - // -------------------------------------------------------------------- - - /** - * "Count All" query - * - * Generates a platform-specific query string that counts all records in - * the specified database - * - * @access public - * @param string - * @return string - */ - function count_all($table = '') - { - if ($table == '') - { - return 0; - } - - $query = $this->query($this->_count_string . $this->_protect_identifiers('numrows') . " FROM " . $this->_protect_identifiers($table, TRUE, NULL, FALSE)); - - if ($query->num_rows() == 0) - { - return 0; - } - - $row = $query->row(); - $this->_reset_select(); - return (int) $row->numrows; - } - - // -------------------------------------------------------------------- - - /** - * List table query - * - * Generates a platform-specific query string so that the table names can be fetched - * - * @access private - * @param boolean - * @return string - */ - function _list_tables($prefix_limit = FALSE) - { - $sql = "SELECT name FROM sysobjects WHERE type = 'U' ORDER BY name"; - - // for future compatibility - if ($prefix_limit !== FALSE AND $this->dbprefix != '') - { - //$sql .= " LIKE '".$this->escape_like_str($this->dbprefix)."%' ".sprintf($this->_like_escape_str, $this->_like_escape_chr); - return FALSE; // not currently supported - } - - return $sql; - } - - // -------------------------------------------------------------------- - - /** - * List column query - * - * Generates a platform-specific query string so that the column names can be fetched - * - * @access private - * @param string the table name - * @return string - */ - function _list_columns($table = '') - { - return "SELECT * FROM INFORMATION_SCHEMA.Columns WHERE TABLE_NAME = '".$table."'"; - } - - // -------------------------------------------------------------------- - - /** - * Field data query - * - * Generates a platform-specific query so that the column data can be retrieved - * - * @access public - * @param string the table name - * @return object - */ - function _field_data($table) - { - return "SELECT TOP 1 * FROM ".$table; - } - - // -------------------------------------------------------------------- - - /** - * The error message string - * - * @access private - * @return string - */ - function _error_message() - { - return mssql_get_last_message(); - } - - // -------------------------------------------------------------------- - - /** - * The error message number - * - * @access private - * @return integer - */ - function _error_number() - { - // Are error numbers supported? - return ''; - } - - // -------------------------------------------------------------------- - - /** - * Escape the SQL Identifiers - * - * This function escapes column and table names - * - * @access private - * @param string - * @return string - */ - function _escape_identifiers($item) - { - if ($this->_escape_char == '') - { - return $item; - } - - foreach ($this->_reserved_identifiers as $id) - { - if (strpos($item, '.'.$id) !== FALSE) - { - $str = $this->_escape_char. str_replace('.', $this->_escape_char.'.', $item); - - // remove duplicates if the user already included the escape - return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str); - } - } - - if (strpos($item, '.') !== FALSE) - { - $str = $this->_escape_char.str_replace('.', $this->_escape_char.'.'.$this->_escape_char, $item).$this->_escape_char; - } - else - { - $str = $this->_escape_char.$item.$this->_escape_char; - } - - // remove duplicates if the user already included the escape - return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str); - } - - // -------------------------------------------------------------------- - - /** - * From Tables - * - * This function implicitly groups FROM tables so there is no confusion - * about operator precedence in harmony with SQL standards - * - * @access public - * @param type - * @return type - */ - function _from_tables($tables) - { - if ( ! is_array($tables)) - { - $tables = array($tables); - } - - return implode(', ', $tables); - } - - // -------------------------------------------------------------------- - - /** - * Insert statement - * - * Generates a platform-specific insert string from the supplied data - * - * @access public - * @param string the table name - * @param array the insert keys - * @param array the insert values - * @return string - */ - function _insert($table, $keys, $values) - { - return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")"; - } - - // -------------------------------------------------------------------- - - /** - * Update statement - * - * Generates a platform-specific update string from the supplied data - * - * @access public - * @param string the table name - * @param array the update data - * @param array the where clause - * @param array the orderby clause - * @param array the limit clause - * @return string - */ - function _update($table, $values, $where, $orderby = array(), $limit = FALSE) - { - foreach ($values as $key => $val) - { - $valstr[] = $key." = ".$val; - } - - $limit = ( ! $limit) ? '' : ' LIMIT '.$limit; - - $orderby = (count($orderby) >= 1)?' ORDER BY '.implode(", ", $orderby):''; - - $sql = "UPDATE ".$table." SET ".implode(', ', $valstr); - - $sql .= ($where != '' AND count($where) >=1) ? " WHERE ".implode(" ", $where) : ''; - - $sql .= $orderby.$limit; - - return $sql; - } - - - // -------------------------------------------------------------------- - - /** - * Truncate statement - * - * Generates a platform-specific truncate string from the supplied data - * If the database does not support the truncate() command - * This function maps to "DELETE FROM table" - * - * @access public - * @param string the table name - * @return string - */ - function _truncate($table) - { - return "TRUNCATE ".$table; - } - - // -------------------------------------------------------------------- - - /** - * Delete statement - * - * Generates a platform-specific delete string from the supplied data - * - * @access public - * @param string the table name - * @param array the where clause - * @param string the limit clause - * @return string - */ - function _delete($table, $where = array(), $like = array(), $limit = FALSE) - { - $conditions = ''; - - if (count($where) > 0 OR count($like) > 0) - { - $conditions = "\nWHERE "; - $conditions .= implode("\n", $this->ar_where); - - if (count($where) > 0 && count($like) > 0) - { - $conditions .= " AND "; - } - $conditions .= implode("\n", $like); - } - - $limit = ( ! $limit) ? '' : ' LIMIT '.$limit; - - return "DELETE FROM ".$table.$conditions.$limit; - } - - // -------------------------------------------------------------------- - - /** - * Limit string - * - * Generates a platform-specific LIMIT clause - * - * @access public - * @param string the sql query string - * @param integer the number of rows to limit the query to - * @param integer the offset value - * @return string - */ - function _limit($sql, $limit, $offset) - { - $i = $limit + $offset; - - return preg_replace('/(^\SELECT (DISTINCT)?)/i','\\1 TOP '.$i.' ', $sql); - } - - // -------------------------------------------------------------------- - - /** - * Close DB Connection - * - * @access public - * @param resource - * @return void - */ - function _close($conn_id) - { - @mssql_close($conn_id); - } - -} - - - -/* End of file mssql_driver.php */ -/* Location: ./system/database/drivers/mssql/mssql_driver.php */ \ No newline at end of file diff --git a/system/database/drivers/mssql/mssql_forge.php b/system/database/drivers/mssql/mssql_forge.php deleted file mode 100644 index 70b20ecf..00000000 --- a/system/database/drivers/mssql/mssql_forge.php +++ /dev/null @@ -1,248 +0,0 @@ -db->_escape_identifiers($table); - } - - // -------------------------------------------------------------------- - - /** - * Create Table - * - * @access private - * @param string the table name - * @param array the fields - * @param mixed primary key(s) - * @param mixed key(s) - * @param boolean should 'IF NOT EXISTS' be added to the SQL - * @return bool - */ - function _create_table($table, $fields, $primary_keys, $keys, $if_not_exists) - { - $sql = 'CREATE TABLE '; - - if ($if_not_exists === TRUE) - { - $sql .= 'IF NOT EXISTS '; - } - - $sql .= $this->db->_escape_identifiers($table)." ("; - $current_field_count = 0; - - foreach ($fields as $field=>$attributes) - { - // Numeric field names aren't allowed in databases, so if the key is - // numeric, we know it was assigned by PHP and the developer manually - // entered the field information, so we'll simply add it to the list - if (is_numeric($field)) - { - $sql .= "\n\t$attributes"; - } - else - { - $attributes = array_change_key_case($attributes, CASE_UPPER); - - $sql .= "\n\t".$this->db->_protect_identifiers($field); - - $sql .= ' '.$attributes['TYPE']; - - if (array_key_exists('CONSTRAINT', $attributes)) - { - $sql .= '('.$attributes['CONSTRAINT'].')'; - } - - if (array_key_exists('UNSIGNED', $attributes) && $attributes['UNSIGNED'] === TRUE) - { - $sql .= ' UNSIGNED'; - } - - if (array_key_exists('DEFAULT', $attributes)) - { - $sql .= ' DEFAULT \''.$attributes['DEFAULT'].'\''; - } - - if (array_key_exists('NULL', $attributes) && $attributes['NULL'] === TRUE) - { - $sql .= ' NULL'; - } - else - { - $sql .= ' NOT NULL'; - } - - if (array_key_exists('AUTO_INCREMENT', $attributes) && $attributes['AUTO_INCREMENT'] === TRUE) - { - $sql .= ' AUTO_INCREMENT'; - } - } - - // don't add a comma on the end of the last field - if (++$current_field_count < count($fields)) - { - $sql .= ','; - } - } - - if (count($primary_keys) > 0) - { - $primary_keys = $this->db->_protect_identifiers($primary_keys); - $sql .= ",\n\tPRIMARY KEY (" . implode(', ', $primary_keys) . ")"; - } - - if (is_array($keys) && count($keys) > 0) - { - foreach ($keys as $key) - { - if (is_array($key)) - { - $key = $this->db->_protect_identifiers($key); - } - else - { - $key = array($this->db->_protect_identifiers($key)); - } - - $sql .= ",\n\tFOREIGN KEY (" . implode(', ', $key) . ")"; - } - } - - $sql .= "\n)"; - - return $sql; - } - - // -------------------------------------------------------------------- - - /** - * Alter table query - * - * Generates a platform-specific query so that a table can be altered - * Called by add_column(), drop_column(), and column_alter(), - * - * @access private - * @param string the ALTER type (ADD, DROP, CHANGE) - * @param string the column name - * @param string the table name - * @param string the column definition - * @param string the default value - * @param boolean should 'NOT NULL' be added - * @param string the field after which we should add the new field - * @return object - */ - function _alter_table($alter_type, $table, $column_name, $column_definition = '', $default_value = '', $null = '', $after_field = '') - { - $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table)." $alter_type ".$this->db->_protect_identifiers($column_name); - - // DROP has everything it needs now. - if ($alter_type == 'DROP') - { - return $sql; - } - - $sql .= " $column_definition"; - - if ($default_value != '') - { - $sql .= " DEFAULT \"$default_value\""; - } - - if ($null === NULL) - { - $sql .= ' NULL'; - } - else - { - $sql .= ' NOT NULL'; - } - - if ($after_field != '') - { - $sql .= ' AFTER ' . $this->db->_protect_identifiers($after_field); - } - - return $sql; - - } - - // -------------------------------------------------------------------- - - /** - * Rename a table - * - * Generates a platform-specific query so that a table can be renamed - * - * @access private - * @param string the old table name - * @param string the new table name - * @return string - */ - function _rename_table($table_name, $new_table_name) - { - // I think this syntax will work, but can find little documentation on renaming tables in MSSQL - $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table_name)." RENAME TO ".$this->db->_protect_identifiers($new_table_name); - return $sql; - } - -} - -/* End of file mssql_forge.php */ -/* Location: ./system/database/drivers/mssql/mssql_forge.php */ \ No newline at end of file diff --git a/system/database/drivers/mssql/mssql_result.php b/system/database/drivers/mssql/mssql_result.php deleted file mode 100644 index 2897ca5a..00000000 --- a/system/database/drivers/mssql/mssql_result.php +++ /dev/null @@ -1,169 +0,0 @@ -result_id); - } - - // -------------------------------------------------------------------- - - /** - * Number of fields in the result set - * - * @access public - * @return integer - */ - function num_fields() - { - return @mssql_num_fields($this->result_id); - } - - // -------------------------------------------------------------------- - - /** - * Fetch Field Names - * - * Generates an array of column names - * - * @access public - * @return array - */ - function list_fields() - { - $field_names = array(); - while ($field = mssql_fetch_field($this->result_id)) - { - $field_names[] = $field->name; - } - - return $field_names; - } - - // -------------------------------------------------------------------- - - /** - * Field data - * - * Generates an array of objects containing field meta-data - * - * @access public - * @return array - */ - function field_data() - { - $retval = array(); - while ($field = mssql_fetch_field($this->result_id)) - { - $F = new stdClass(); - $F->name = $field->name; - $F->type = $field->type; - $F->max_length = $field->max_length; - $F->primary_key = 0; - $F->default = ''; - - $retval[] = $F; - } - - return $retval; - } - - // -------------------------------------------------------------------- - - /** - * Free the result - * - * @return null - */ - function free_result() - { - if (is_resource($this->result_id)) - { - mssql_free_result($this->result_id); - $this->result_id = FALSE; - } - } - - // -------------------------------------------------------------------- - - /** - * Data Seek - * - * Moves the internal pointer to the desired offset. We call - * this internally before fetching results to make sure the - * result set starts at zero - * - * @access private - * @return array - */ - function _data_seek($n = 0) - { - return mssql_data_seek($this->result_id, $n); - } - - // -------------------------------------------------------------------- - - /** - * Result - associative array - * - * Returns the result set as an array - * - * @access private - * @return array - */ - function _fetch_assoc() - { - return mssql_fetch_assoc($this->result_id); - } - - // -------------------------------------------------------------------- - - /** - * Result - object - * - * Returns the result set as an object - * - * @access private - * @return object - */ - function _fetch_object() - { - return mssql_fetch_object($this->result_id); - } - -} - - -/* End of file mssql_result.php */ -/* Location: ./system/database/drivers/mssql/mssql_result.php */ \ No newline at end of file diff --git a/system/database/drivers/mssql/mssql_utility.php b/system/database/drivers/mssql/mssql_utility.php deleted file mode 100644 index 48ecbc72..00000000 --- a/system/database/drivers/mssql/mssql_utility.php +++ /dev/null @@ -1,88 +0,0 @@ -db->display_error('db_unsuported_feature'); - } - -} - -/* End of file mssql_utility.php */ -/* Location: ./system/database/drivers/mssql/mssql_utility.php */ \ No newline at end of file diff --git a/system/database/drivers/mysql/index.html b/system/database/drivers/mysql/index.html deleted file mode 100644 index c942a79c..00000000 --- a/system/database/drivers/mysql/index.html +++ /dev/null @@ -1,10 +0,0 @@ - - - 403 Forbidden - - - -

Directory access is forbidden.

- - - \ No newline at end of file diff --git a/system/database/drivers/mysql/mysql_driver.php b/system/database/drivers/mysql/mysql_driver.php deleted file mode 100644 index f87cfea4..00000000 --- a/system/database/drivers/mysql/mysql_driver.php +++ /dev/null @@ -1,779 +0,0 @@ -port != '') - { - $this->hostname .= ':'.$this->port; - } - - return @mysql_connect($this->hostname, $this->username, $this->password, TRUE); - } - - // -------------------------------------------------------------------- - - /** - * Persistent database connection - * - * @access private called by the base class - * @return resource - */ - function db_pconnect() - { - if ($this->port != '') - { - $this->hostname .= ':'.$this->port; - } - - return @mysql_pconnect($this->hostname, $this->username, $this->password); - } - - // -------------------------------------------------------------------- - - /** - * Reconnect - * - * Keep / reestablish the db connection if no queries have been - * sent for a length of time exceeding the server's idle timeout - * - * @access public - * @return void - */ - function reconnect() - { - if (mysql_ping($this->conn_id) === FALSE) - { - $this->conn_id = FALSE; - } - } - - // -------------------------------------------------------------------- - - /** - * Select the database - * - * @access private called by the base class - * @return resource - */ - function db_select() - { - return @mysql_select_db($this->database, $this->conn_id); - } - - // -------------------------------------------------------------------- - - /** - * Set client character set - * - * @access public - * @param string - * @param string - * @return resource - */ - function db_set_charset($charset, $collation) - { - if ( ! isset($this->use_set_names)) - { - // mysql_set_charset() requires PHP >= 5.2.3 and MySQL >= 5.0.7, use SET NAMES as fallback - $this->use_set_names = (version_compare(PHP_VERSION, '5.2.3', '>=') && version_compare(mysql_get_server_info(), '5.0.7', '>=')) ? FALSE : TRUE; - } - - if ($this->use_set_names === TRUE) - { - return @mysql_query("SET NAMES '".$this->escape_str($charset)."' COLLATE '".$this->escape_str($collation)."'", $this->conn_id); - } - else - { - return @mysql_set_charset($charset, $this->conn_id); - } - } - - // -------------------------------------------------------------------- - - /** - * Version number query string - * - * @access public - * @return string - */ - function _version() - { - return "SELECT version() AS ver"; - } - - // -------------------------------------------------------------------- - - /** - * Execute the query - * - * @access private called by the base class - * @param string an SQL query - * @return resource - */ - function _execute($sql) - { - $sql = $this->_prep_query($sql); - return @mysql_query($sql, $this->conn_id); - } - - // -------------------------------------------------------------------- - - /** - * Prep the query - * - * If needed, each database adapter can prep the query string - * - * @access private called by execute() - * @param string an SQL query - * @return string - */ - function _prep_query($sql) - { - // "DELETE FROM TABLE" returns 0 affected rows This hack modifies - // the query so that it returns the number of affected rows - if ($this->delete_hack === TRUE) - { - if (preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $sql)) - { - $sql = preg_replace("/^\s*DELETE\s+FROM\s+(\S+)\s*$/", "DELETE FROM \\1 WHERE 1=1", $sql); - } - } - - return $sql; - } - - // -------------------------------------------------------------------- - - /** - * Begin Transaction - * - * @access public - * @return bool - */ - function trans_begin($test_mode = FALSE) - { - if ( ! $this->trans_enabled) - { - return TRUE; - } - - // When transactions are nested we only begin/commit/rollback the outermost ones - if ($this->_trans_depth > 0) - { - return TRUE; - } - - // Reset the transaction failure flag. - // If the $test_mode flag is set to TRUE transactions will be rolled back - // even if the queries produce a successful result. - $this->_trans_failure = ($test_mode === TRUE) ? TRUE : FALSE; - - $this->simple_query('SET AUTOCOMMIT=0'); - $this->simple_query('START TRANSACTION'); // can also be BEGIN or BEGIN WORK - return TRUE; - } - - // -------------------------------------------------------------------- - - /** - * Commit Transaction - * - * @access public - * @return bool - */ - function trans_commit() - { - if ( ! $this->trans_enabled) - { - return TRUE; - } - - // When transactions are nested we only begin/commit/rollback the outermost ones - if ($this->_trans_depth > 0) - { - return TRUE; - } - - $this->simple_query('COMMIT'); - $this->simple_query('SET AUTOCOMMIT=1'); - return TRUE; - } - - // -------------------------------------------------------------------- - - /** - * Rollback Transaction - * - * @access public - * @return bool - */ - function trans_rollback() - { - if ( ! $this->trans_enabled) - { - return TRUE; - } - - // When transactions are nested we only begin/commit/rollback the outermost ones - if ($this->_trans_depth > 0) - { - return TRUE; - } - - $this->simple_query('ROLLBACK'); - $this->simple_query('SET AUTOCOMMIT=1'); - return TRUE; - } - - // -------------------------------------------------------------------- - - /** - * Escape String - * - * @access public - * @param string - * @param bool whether or not the string will be used in a LIKE condition - * @return string - */ - function escape_str($str, $like = FALSE) - { - if (is_array($str)) - { - foreach ($str as $key => $val) - { - $str[$key] = $this->escape_str($val, $like); - } - - return $str; - } - - if (function_exists('mysql_real_escape_string') AND is_resource($this->conn_id)) - { - $str = mysql_real_escape_string($str, $this->conn_id); - } - elseif (function_exists('mysql_escape_string')) - { - $str = mysql_escape_string($str); - } - else - { - $str = addslashes($str); - } - - // escape LIKE condition wildcards - if ($like === TRUE) - { - $str = str_replace(array('%', '_'), array('\\%', '\\_'), $str); - } - - return $str; - } - - // -------------------------------------------------------------------- - - /** - * Affected Rows - * - * @access public - * @return integer - */ - function affected_rows() - { - return @mysql_affected_rows($this->conn_id); - } - - // -------------------------------------------------------------------- - - /** - * Insert ID - * - * @access public - * @return integer - */ - function insert_id() - { - return @mysql_insert_id($this->conn_id); - } - - // -------------------------------------------------------------------- - - /** - * "Count All" query - * - * Generates a platform-specific query string that counts all records in - * the specified database - * - * @access public - * @param string - * @return string - */ - function count_all($table = '') - { - if ($table == '') - { - return 0; - } - - $query = $this->query($this->_count_string . $this->_protect_identifiers('numrows') . " FROM " . $this->_protect_identifiers($table, TRUE, NULL, FALSE)); - - if ($query->num_rows() == 0) - { - return 0; - } - - $row = $query->row(); - $this->_reset_select(); - return (int) $row->numrows; - } - - // -------------------------------------------------------------------- - - /** - * List table query - * - * Generates a platform-specific query string so that the table names can be fetched - * - * @access private - * @param boolean - * @return string - */ - function _list_tables($prefix_limit = FALSE) - { - $sql = "SHOW TABLES FROM ".$this->_escape_char.$this->database.$this->_escape_char; - - if ($prefix_limit !== FALSE AND $this->dbprefix != '') - { - $sql .= " LIKE '".$this->escape_like_str($this->dbprefix)."%'"; - } - - return $sql; - } - - // -------------------------------------------------------------------- - - /** - * Show column query - * - * Generates a platform-specific query string so that the column names can be fetched - * - * @access public - * @param string the table name - * @return string - */ - function _list_columns($table = '') - { - return "SHOW COLUMNS FROM ".$this->_protect_identifiers($table, TRUE, NULL, FALSE); - } - - // -------------------------------------------------------------------- - - /** - * Field data query - * - * Generates a platform-specific query so that the column data can be retrieved - * - * @access public - * @param string the table name - * @return object - */ - function _field_data($table) - { - return "DESCRIBE ".$table; - } - - // -------------------------------------------------------------------- - - /** - * The error message string - * - * @access private - * @return string - */ - function _error_message() - { - return mysql_error($this->conn_id); - } - - // -------------------------------------------------------------------- - - /** - * The error message number - * - * @access private - * @return integer - */ - function _error_number() - { - return mysql_errno($this->conn_id); - } - - // -------------------------------------------------------------------- - - /** - * Escape the SQL Identifiers - * - * This function escapes column and table names - * - * @access private - * @param string - * @return string - */ - function _escape_identifiers($item) - { - if ($this->_escape_char == '') - { - return $item; - } - - foreach ($this->_reserved_identifiers as $id) - { - if (strpos($item, '.'.$id) !== FALSE) - { - $str = $this->_escape_char. str_replace('.', $this->_escape_char.'.', $item); - - // remove duplicates if the user already included the escape - return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str); - } - } - - if (strpos($item, '.') !== FALSE) - { - $str = $this->_escape_char.str_replace('.', $this->_escape_char.'.'.$this->_escape_char, $item).$this->_escape_char; - } - else - { - $str = $this->_escape_char.$item.$this->_escape_char; - } - - // remove duplicates if the user already included the escape - return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str); - } - - // -------------------------------------------------------------------- - - /** - * From Tables - * - * This function implicitly groups FROM tables so there is no confusion - * about operator precedence in harmony with SQL standards - * - * @access public - * @param type - * @return type - */ - function _from_tables($tables) - { - if ( ! is_array($tables)) - { - $tables = array($tables); - } - - return '('.implode(', ', $tables).')'; - } - - // -------------------------------------------------------------------- - - /** - * Insert statement - * - * Generates a platform-specific insert string from the supplied data - * - * @access public - * @param string the table name - * @param array the insert keys - * @param array the insert values - * @return string - */ - function _insert($table, $keys, $values) - { - return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")"; - } - - // -------------------------------------------------------------------- - - - /** - * Replace statement - * - * Generates a platform-specific replace string from the supplied data - * - * @access public - * @param string the table name - * @param array the insert keys - * @param array the insert values - * @return string - */ - function _replace($table, $keys, $values) - { - return "REPLACE INTO ".$table." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")"; - } - - // -------------------------------------------------------------------- - - /** - * Insert_batch statement - * - * Generates a platform-specific insert string from the supplied data - * - * @access public - * @param string the table name - * @param array the insert keys - * @param array the insert values - * @return string - */ - function _insert_batch($table, $keys, $values) - { - return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES ".implode(', ', $values); - } - - // -------------------------------------------------------------------- - - - /** - * Update statement - * - * Generates a platform-specific update string from the supplied data - * - * @access public - * @param string the table name - * @param array the update data - * @param array the where clause - * @param array the orderby clause - * @param array the limit clause - * @return string - */ - function _update($table, $values, $where, $orderby = array(), $limit = FALSE) - { - foreach ($values as $key => $val) - { - $valstr[] = $key . ' = ' . $val; - } - - $limit = ( ! $limit) ? '' : ' LIMIT '.$limit; - - $orderby = (count($orderby) >= 1)?' ORDER BY '.implode(", ", $orderby):''; - - $sql = "UPDATE ".$table." SET ".implode(', ', $valstr); - - $sql .= ($where != '' AND count($where) >=1) ? " WHERE ".implode(" ", $where) : ''; - - $sql .= $orderby.$limit; - - return $sql; - } - - // -------------------------------------------------------------------- - - - /** - * Update_Batch statement - * - * Generates a platform-specific batch update string from the supplied data - * - * @access public - * @param string the table name - * @param array the update data - * @param array the where clause - * @return string - */ - function _update_batch($table, $values, $index, $where = NULL) - { - $ids = array(); - $where = ($where != '' AND count($where) >=1) ? implode(" ", $where).' AND ' : ''; - - foreach ($values as $key => $val) - { - $ids[] = $val[$index]; - - foreach (array_keys($val) as $field) - { - if ($field != $index) - { - $final[$field][] = 'WHEN '.$index.' = '.$val[$index].' THEN '.$val[$field]; - } - } - } - - $sql = "UPDATE ".$table." SET "; - $cases = ''; - - foreach ($final as $k => $v) - { - $cases .= $k.' = CASE '."\n"; - foreach ($v as $row) - { - $cases .= $row."\n"; - } - - $cases .= 'ELSE '.$k.' END, '; - } - - $sql .= substr($cases, 0, -2); - - $sql .= ' WHERE '.$where.$index.' IN ('.implode(',', $ids).')'; - - return $sql; - } - - // -------------------------------------------------------------------- - - - /** - * Truncate statement - * - * Generates a platform-specific truncate string from the supplied data - * If the database does not support the truncate() command - * This function maps to "DELETE FROM table" - * - * @access public - * @param string the table name - * @return string - */ - function _truncate($table) - { - return "TRUNCATE ".$table; - } - - // -------------------------------------------------------------------- - - /** - * Delete statement - * - * Generates a platform-specific delete string from the supplied data - * - * @access public - * @param string the table name - * @param array the where clause - * @param string the limit clause - * @return string - */ - function _delete($table, $where = array(), $like = array(), $limit = FALSE) - { - $conditions = ''; - - if (count($where) > 0 OR count($like) > 0) - { - $conditions = "\nWHERE "; - $conditions .= implode("\n", $this->ar_where); - - if (count($where) > 0 && count($like) > 0) - { - $conditions .= " AND "; - } - $conditions .= implode("\n", $like); - } - - $limit = ( ! $limit) ? '' : ' LIMIT '.$limit; - - return "DELETE FROM ".$table.$conditions.$limit; - } - - // -------------------------------------------------------------------- - - /** - * Limit string - * - * Generates a platform-specific LIMIT clause - * - * @access public - * @param string the sql query string - * @param integer the number of rows to limit the query to - * @param integer the offset value - * @return string - */ - function _limit($sql, $limit, $offset) - { - if ($offset == 0) - { - $offset = ''; - } - else - { - $offset .= ", "; - } - - return $sql."LIMIT ".$offset.$limit; - } - - // -------------------------------------------------------------------- - - /** - * Close DB Connection - * - * @access public - * @param resource - * @return void - */ - function _close($conn_id) - { - @mysql_close($conn_id); - } - -} - - -/* End of file mysql_driver.php */ -/* Location: ./system/database/drivers/mysql/mysql_driver.php */ \ No newline at end of file diff --git a/system/database/drivers/mysql/mysql_forge.php b/system/database/drivers/mysql/mysql_forge.php deleted file mode 100644 index c1cae136..00000000 --- a/system/database/drivers/mysql/mysql_forge.php +++ /dev/null @@ -1,273 +0,0 @@ -$attributes) - { - // Numeric field names aren't allowed in databases, so if the key is - // numeric, we know it was assigned by PHP and the developer manually - // entered the field information, so we'll simply add it to the list - if (is_numeric($field)) - { - $sql .= "\n\t$attributes"; - } - else - { - $attributes = array_change_key_case($attributes, CASE_UPPER); - - $sql .= "\n\t".$this->db->_protect_identifiers($field); - - if (array_key_exists('NAME', $attributes)) - { - $sql .= ' '.$this->db->_protect_identifiers($attributes['NAME']).' '; - } - - if (array_key_exists('TYPE', $attributes)) - { - $sql .= ' '.$attributes['TYPE']; - - if (array_key_exists('CONSTRAINT', $attributes)) - { - switch ($attributes['TYPE']) - { - case 'decimal': - case 'float': - case 'numeric': - $sql .= '('.implode(',', $attributes['CONSTRAINT']).')'; - break; - - case 'enum': - case 'set': - $sql .= '("'.implode('","', $attributes['CONSTRAINT']).'")'; - break; - - default: - $sql .= '('.$attributes['CONSTRAINT'].')'; - } - } - } - - if (array_key_exists('UNSIGNED', $attributes) && $attributes['UNSIGNED'] === TRUE) - { - $sql .= ' UNSIGNED'; - } - - if (array_key_exists('DEFAULT', $attributes)) - { - $sql .= ' DEFAULT \''.$attributes['DEFAULT'].'\''; - } - - if (array_key_exists('NULL', $attributes) && $attributes['NULL'] === TRUE) - { - $sql .= ' NULL'; - } - else - { - $sql .= ' NOT NULL'; - } - - if (array_key_exists('AUTO_INCREMENT', $attributes) && $attributes['AUTO_INCREMENT'] === TRUE) - { - $sql .= ' AUTO_INCREMENT'; - } - } - - // don't add a comma on the end of the last field - if (++$current_field_count < count($fields)) - { - $sql .= ','; - } - } - - return $sql; - } - - // -------------------------------------------------------------------- - - /** - * Create Table - * - * @access private - * @param string the table name - * @param mixed the fields - * @param mixed primary key(s) - * @param mixed key(s) - * @param boolean should 'IF NOT EXISTS' be added to the SQL - * @return bool - */ - function _create_table($table, $fields, $primary_keys, $keys, $if_not_exists) - { - $sql = 'CREATE TABLE '; - - if ($if_not_exists === TRUE) - { - $sql .= 'IF NOT EXISTS '; - } - - $sql .= $this->db->_escape_identifiers($table)." ("; - - $sql .= $this->_process_fields($fields); - - if (count($primary_keys) > 0) - { - $key_name = $this->db->_protect_identifiers(implode('_', $primary_keys)); - $primary_keys = $this->db->_protect_identifiers($primary_keys); - $sql .= ",\n\tPRIMARY KEY ".$key_name." (" . implode(', ', $primary_keys) . ")"; - } - - if (is_array($keys) && count($keys) > 0) - { - foreach ($keys as $key) - { - if (is_array($key)) - { - $key_name = $this->db->_protect_identifiers(implode('_', $key)); - $key = $this->db->_protect_identifiers($key); - } - else - { - $key_name = $this->db->_protect_identifiers($key); - $key = array($key_name); - } - - $sql .= ",\n\tKEY {$key_name} (" . implode(', ', $key) . ")"; - } - } - - $sql .= "\n) DEFAULT CHARACTER SET {$this->db->char_set} COLLATE {$this->db->dbcollat};"; - - return $sql; - } - - // -------------------------------------------------------------------- - - /** - * Drop Table - * - * @access private - * @return string - */ - function _drop_table($table) - { - return "DROP TABLE IF EXISTS ".$this->db->_escape_identifiers($table); - } - - // -------------------------------------------------------------------- - - /** - * Alter table query - * - * Generates a platform-specific query so that a table can be altered - * Called by add_column(), drop_column(), and column_alter(), - * - * @access private - * @param string the ALTER type (ADD, DROP, CHANGE) - * @param string the column name - * @param array fields - * @param string the field after which we should add the new field - * @return object - */ - function _alter_table($alter_type, $table, $fields, $after_field = '') - { - $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table)." $alter_type "; - - // DROP has everything it needs now. - if ($alter_type == 'DROP') - { - return $sql.$this->db->_protect_identifiers($fields); - } - - $sql .= $this->_process_fields($fields); - - if ($after_field != '') - { - $sql .= ' AFTER ' . $this->db->_protect_identifiers($after_field); - } - - return $sql; - } - - // -------------------------------------------------------------------- - - /** - * Rename a table - * - * Generates a platform-specific query so that a table can be renamed - * - * @access private - * @param string the old table name - * @param string the new table name - * @return string - */ - function _rename_table($table_name, $new_table_name) - { - $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table_name)." RENAME TO ".$this->db->_protect_identifiers($new_table_name); - return $sql; - } - -} - -/* End of file mysql_forge.php */ -/* Location: ./system/database/drivers/mysql/mysql_forge.php */ \ No newline at end of file diff --git a/system/database/drivers/mysql/mysql_result.php b/system/database/drivers/mysql/mysql_result.php deleted file mode 100644 index e1a6e93c..00000000 --- a/system/database/drivers/mysql/mysql_result.php +++ /dev/null @@ -1,174 +0,0 @@ -result_id); - } - - // -------------------------------------------------------------------- - - /** - * Number of fields in the result set - * - * @access public - * @return integer - */ - function num_fields() - { - return @mysql_num_fields($this->result_id); - } - - // -------------------------------------------------------------------- - - /** - * Fetch Field Names - * - * Generates an array of column names - * - * @access public - * @return array - */ - function list_fields() - { - $field_names = array(); - while ($field = mysql_fetch_field($this->result_id)) - { - $field_names[] = $field->name; - } - - return $field_names; - } - - // -------------------------------------------------------------------- - - /** - * Field data - * - * Generates an array of objects containing field meta-data - * - * @access public - * @return array - */ - function field_data() - { - $retval = array(); - while ($field = mysql_fetch_object($this->result_id)) - { - preg_match('/([a-zA-Z]+)(\(\d+\))?/', $field->Type, $matches); - - $type = (array_key_exists(1, $matches)) ? $matches[1] : NULL; - $length = (array_key_exists(2, $matches)) ? preg_replace('/[^\d]/', '', $matches[2]) : NULL; - - $F = new stdClass(); - $F->name = $field->Field; - $F->type = $type; - $F->default = $field->Default; - $F->max_length = $length; - $F->primary_key = ( $field->Key == 'PRI' ? 1 : 0 ); - - $retval[] = $F; - } - - return $retval; - } - - // -------------------------------------------------------------------- - - /** - * Free the result - * - * @return null - */ - function free_result() - { - if (is_resource($this->result_id)) - { - mysql_free_result($this->result_id); - $this->result_id = FALSE; - } - } - - // -------------------------------------------------------------------- - - /** - * Data Seek - * - * Moves the internal pointer to the desired offset. We call - * this internally before fetching results to make sure the - * result set starts at zero - * - * @access private - * @return array - */ - function _data_seek($n = 0) - { - return mysql_data_seek($this->result_id, $n); - } - - // -------------------------------------------------------------------- - - /** - * Result - associative array - * - * Returns the result set as an array - * - * @access private - * @return array - */ - function _fetch_assoc() - { - return mysql_fetch_assoc($this->result_id); - } - - // -------------------------------------------------------------------- - - /** - * Result - object - * - * Returns the result set as an object - * - * @access private - * @return object - */ - function _fetch_object() - { - return mysql_fetch_object($this->result_id); - } - -} - - -/* End of file mysql_result.php */ -/* Location: ./system/database/drivers/mysql/mysql_result.php */ \ No newline at end of file diff --git a/system/database/drivers/mysql/mysql_utility.php b/system/database/drivers/mysql/mysql_utility.php deleted file mode 100644 index 48c4d631..00000000 --- a/system/database/drivers/mysql/mysql_utility.php +++ /dev/null @@ -1,210 +0,0 @@ -db->_escape_identifiers($table); - } - - // -------------------------------------------------------------------- - - /** - * Repair table query - * - * Generates a platform-specific query so that a table can be repaired - * - * @access private - * @param string the table name - * @return object - */ - function _repair_table($table) - { - return "REPAIR TABLE ".$this->db->_escape_identifiers($table); - } - - // -------------------------------------------------------------------- - /** - * MySQL Export - * - * @access private - * @param array Preferences - * @return mixed - */ - function _backup($params = array()) - { - if (count($params) == 0) - { - return FALSE; - } - - // Extract the prefs for simplicity - extract($params); - - // Build the output - $output = ''; - foreach ((array)$tables as $table) - { - // Is the table in the "ignore" list? - if (in_array($table, (array)$ignore, TRUE)) - { - continue; - } - - // Get the table schema - $query = $this->db->query("SHOW CREATE TABLE `".$this->db->database.'`.`'.$table.'`'); - - // No result means the table name was invalid - if ($query === FALSE) - { - continue; - } - - // Write out the table schema - $output .= '#'.$newline.'# TABLE STRUCTURE FOR: '.$table.$newline.'#'.$newline.$newline; - - if ($add_drop == TRUE) - { - $output .= 'DROP TABLE IF EXISTS '.$table.';'.$newline.$newline; - } - - $i = 0; - $result = $query->result_array(); - foreach ($result[0] as $val) - { - if ($i++ % 2) - { - $output .= $val.';'.$newline.$newline; - } - } - - // If inserts are not needed we're done... - if ($add_insert == FALSE) - { - continue; - } - - // Grab all the data from the current table - $query = $this->db->query("SELECT * FROM $table"); - - if ($query->num_rows() == 0) - { - continue; - } - - // Fetch the field names and determine if the field is an - // integer type. We use this info to decide whether to - // surround the data with quotes or not - - $i = 0; - $field_str = ''; - $is_int = array(); - while ($field = mysql_fetch_field($query->result_id)) - { - // Most versions of MySQL store timestamp as a string - $is_int[$i] = (in_array( - strtolower(mysql_field_type($query->result_id, $i)), - array('tinyint', 'smallint', 'mediumint', 'int', 'bigint'), //, 'timestamp'), - TRUE) - ) ? TRUE : FALSE; - - // Create a string of field names - $field_str .= '`'.$field->name.'`, '; - $i++; - } - - // Trim off the end comma - $field_str = preg_replace( "/, $/" , "" , $field_str); - - - // Build the insert string - foreach ($query->result_array() as $row) - { - $val_str = ''; - - $i = 0; - foreach ($row as $v) - { - // Is the value NULL? - if ($v === NULL) - { - $val_str .= 'NULL'; - } - else - { - // Escape the data if it's not an integer - if ($is_int[$i] == FALSE) - { - $val_str .= $this->db->escape($v); - } - else - { - $val_str .= $v; - } - } - - // Append a comma - $val_str .= ', '; - $i++; - } - - // Remove the comma at the end of the string - $val_str = preg_replace( "/, $/" , "" , $val_str); - - // Build the INSERT string - $output .= 'INSERT INTO '.$table.' ('.$field_str.') VALUES ('.$val_str.');'.$newline; - } - - $output .= $newline.$newline; - } - - return $output; - } -} - -/* End of file mysql_utility.php */ -/* Location: ./system/database/drivers/mysql/mysql_utility.php */ \ No newline at end of file diff --git a/system/database/drivers/mysqli/index.html b/system/database/drivers/mysqli/index.html deleted file mode 100644 index c942a79c..00000000 --- a/system/database/drivers/mysqli/index.html +++ /dev/null @@ -1,10 +0,0 @@ - - - 403 Forbidden - - - -

Directory access is forbidden.

- - - \ No newline at end of file diff --git a/system/database/drivers/mysqli/mysqli_driver.php b/system/database/drivers/mysqli/mysqli_driver.php deleted file mode 100644 index d3200f32..00000000 --- a/system/database/drivers/mysqli/mysqli_driver.php +++ /dev/null @@ -1,776 +0,0 @@ -port != '') - { - return @mysqli_connect($this->hostname, $this->username, $this->password, $this->database, $this->port); - } - else - { - return @mysqli_connect($this->hostname, $this->username, $this->password, $this->database); - } - - } - - // -------------------------------------------------------------------- - - /** - * Persistent database connection - * - * @access private called by the base class - * @return resource - */ - function db_pconnect() - { - return $this->db_connect(); - } - - // -------------------------------------------------------------------- - - /** - * Reconnect - * - * Keep / reestablish the db connection if no queries have been - * sent for a length of time exceeding the server's idle timeout - * - * @access public - * @return void - */ - function reconnect() - { - if (mysqli_ping($this->conn_id) === FALSE) - { - $this->conn_id = FALSE; - } - } - - // -------------------------------------------------------------------- - - /** - * Select the database - * - * @access private called by the base class - * @return resource - */ - function db_select() - { - return @mysqli_select_db($this->conn_id, $this->database); - } - - // -------------------------------------------------------------------- - - /** - * Set client character set - * - * @access private - * @param string - * @param string - * @return resource - */ - function _db_set_charset($charset, $collation) - { - if ( ! isset($this->use_set_names)) - { - // mysqli_set_charset() requires MySQL >= 5.0.7, use SET NAMES as fallback - $this->use_set_names = (version_compare(mysqli_get_server_info($this->conn_id), '5.0.7', '>=')) ? FALSE : TRUE; - } - - if ($this->use_set_names === TRUE) - { - return @mysqli_query($this->conn_id, "SET NAMES '".$this->escape_str($charset)."' COLLATE '".$this->escape_str($collation)."'"); - } - else - { - return @mysqli_set_charset($this->conn_id, $charset); - } - } - - // -------------------------------------------------------------------- - - /** - * Version number query string - * - * @access public - * @return string - */ - function _version() - { - return "SELECT version() AS ver"; - } - - // -------------------------------------------------------------------- - - /** - * Execute the query - * - * @access private called by the base class - * @param string an SQL query - * @return resource - */ - function _execute($sql) - { - $sql = $this->_prep_query($sql); - $result = @mysqli_query($this->conn_id, $sql); - return $result; - } - - // -------------------------------------------------------------------- - - /** - * Prep the query - * - * If needed, each database adapter can prep the query string - * - * @access private called by execute() - * @param string an SQL query - * @return string - */ - function _prep_query($sql) - { - // "DELETE FROM TABLE" returns 0 affected rows This hack modifies - // the query so that it returns the number of affected rows - if ($this->delete_hack === TRUE) - { - if (preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $sql)) - { - $sql = preg_replace("/^\s*DELETE\s+FROM\s+(\S+)\s*$/", "DELETE FROM \\1 WHERE 1=1", $sql); - } - } - - return $sql; - } - - // -------------------------------------------------------------------- - - /** - * Begin Transaction - * - * @access public - * @return bool - */ - function trans_begin($test_mode = FALSE) - { - if ( ! $this->trans_enabled) - { - return TRUE; - } - - // When transactions are nested we only begin/commit/rollback the outermost ones - if ($this->_trans_depth > 0) - { - return TRUE; - } - - // Reset the transaction failure flag. - // If the $test_mode flag is set to TRUE transactions will be rolled back - // even if the queries produce a successful result. - $this->_trans_failure = ($test_mode === TRUE) ? TRUE : FALSE; - - $this->simple_query('SET AUTOCOMMIT=0'); - $this->simple_query('START TRANSACTION'); // can also be BEGIN or BEGIN WORK - return TRUE; - } - - // -------------------------------------------------------------------- - - /** - * Commit Transaction - * - * @access public - * @return bool - */ - function trans_commit() - { - if ( ! $this->trans_enabled) - { - return TRUE; - } - - // When transactions are nested we only begin/commit/rollback the outermost ones - if ($this->_trans_depth > 0) - { - return TRUE; - } - - $this->simple_query('COMMIT'); - $this->simple_query('SET AUTOCOMMIT=1'); - return TRUE; - } - - // -------------------------------------------------------------------- - - /** - * Rollback Transaction - * - * @access public - * @return bool - */ - function trans_rollback() - { - if ( ! $this->trans_enabled) - { - return TRUE; - } - - // When transactions are nested we only begin/commit/rollback the outermost ones - if ($this->_trans_depth > 0) - { - return TRUE; - } - - $this->simple_query('ROLLBACK'); - $this->simple_query('SET AUTOCOMMIT=1'); - return TRUE; - } - - // -------------------------------------------------------------------- - - /** - * Escape String - * - * @access public - * @param string - * @param bool whether or not the string will be used in a LIKE condition - * @return string - */ - function escape_str($str, $like = FALSE) - { - if (is_array($str)) - { - foreach ($str as $key => $val) - { - $str[$key] = $this->escape_str($val, $like); - } - - return $str; - } - - if (function_exists('mysqli_real_escape_string') AND is_object($this->conn_id)) - { - $str = mysqli_real_escape_string($this->conn_id, $str); - } - elseif (function_exists('mysql_escape_string')) - { - $str = mysql_escape_string($str); - } - else - { - $str = addslashes($str); - } - - // escape LIKE condition wildcards - if ($like === TRUE) - { - $str = str_replace(array('%', '_'), array('\\%', '\\_'), $str); - } - - return $str; - } - - // -------------------------------------------------------------------- - - /** - * Affected Rows - * - * @access public - * @return integer - */ - function affected_rows() - { - return @mysqli_affected_rows($this->conn_id); - } - - // -------------------------------------------------------------------- - - /** - * Insert ID - * - * @access public - * @return integer - */ - function insert_id() - { - return @mysqli_insert_id($this->conn_id); - } - - // -------------------------------------------------------------------- - - /** - * "Count All" query - * - * Generates a platform-specific query string that counts all records in - * the specified database - * - * @access public - * @param string - * @return string - */ - function count_all($table = '') - { - if ($table == '') - { - return 0; - } - - $query = $this->query($this->_count_string . $this->_protect_identifiers('numrows') . " FROM " . $this->_protect_identifiers($table, TRUE, NULL, FALSE)); - - if ($query->num_rows() == 0) - { - return 0; - } - - $row = $query->row(); - $this->_reset_select(); - return (int) $row->numrows; - } - - // -------------------------------------------------------------------- - - /** - * List table query - * - * Generates a platform-specific query string so that the table names can be fetched - * - * @access private - * @param boolean - * @return string - */ - function _list_tables($prefix_limit = FALSE) - { - $sql = "SHOW TABLES FROM ".$this->_escape_char.$this->database.$this->_escape_char; - - if ($prefix_limit !== FALSE AND $this->dbprefix != '') - { - $sql .= " LIKE '".$this->escape_like_str($this->dbprefix)."%'"; - } - - return $sql; - } - - // -------------------------------------------------------------------- - - /** - * Show column query - * - * Generates a platform-specific query string so that the column names can be fetched - * - * @access public - * @param string the table name - * @return string - */ - function _list_columns($table = '') - { - return "SHOW COLUMNS FROM ".$this->_protect_identifiers($table, TRUE, NULL, FALSE); - } - - // -------------------------------------------------------------------- - - /** - * Field data query - * - * Generates a platform-specific query so that the column data can be retrieved - * - * @access public - * @param string the table name - * @return object - */ - function _field_data($table) - { - return "DESCRIBE ".$table; - } - - // -------------------------------------------------------------------- - - /** - * The error message string - * - * @access private - * @return string - */ - function _error_message() - { - return mysqli_error($this->conn_id); - } - - // -------------------------------------------------------------------- - - /** - * The error message number - * - * @access private - * @return integer - */ - function _error_number() - { - return mysqli_errno($this->conn_id); - } - - // -------------------------------------------------------------------- - - /** - * Escape the SQL Identifiers - * - * This function escapes column and table names - * - * @access private - * @param string - * @return string - */ - function _escape_identifiers($item) - { - if ($this->_escape_char == '') - { - return $item; - } - - foreach ($this->_reserved_identifiers as $id) - { - if (strpos($item, '.'.$id) !== FALSE) - { - $str = $this->_escape_char. str_replace('.', $this->_escape_char.'.', $item); - - // remove duplicates if the user already included the escape - return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str); - } - } - - if (strpos($item, '.') !== FALSE) - { - $str = $this->_escape_char.str_replace('.', $this->_escape_char.'.'.$this->_escape_char, $item).$this->_escape_char; - } - else - { - $str = $this->_escape_char.$item.$this->_escape_char; - } - - // remove duplicates if the user already included the escape - return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str); - } - - // -------------------------------------------------------------------- - - /** - * From Tables - * - * This function implicitly groups FROM tables so there is no confusion - * about operator precedence in harmony with SQL standards - * - * @access public - * @param type - * @return type - */ - function _from_tables($tables) - { - if ( ! is_array($tables)) - { - $tables = array($tables); - } - - return '('.implode(', ', $tables).')'; - } - - // -------------------------------------------------------------------- - - /** - * Insert statement - * - * Generates a platform-specific insert string from the supplied data - * - * @access public - * @param string the table name - * @param array the insert keys - * @param array the insert values - * @return string - */ - function _insert($table, $keys, $values) - { - return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")"; - } - - // -------------------------------------------------------------------- - - /** - * Insert_batch statement - * - * Generates a platform-specific insert string from the supplied data - * - * @access public - * @param string the table name - * @param array the insert keys - * @param array the insert values - * @return string - */ - function _insert_batch($table, $keys, $values) - { - return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES ".implode(', ', $values); - } - - // -------------------------------------------------------------------- - - - /** - * Replace statement - * - * Generates a platform-specific replace string from the supplied data - * - * @access public - * @param string the table name - * @param array the insert keys - * @param array the insert values - * @return string - */ - function _replace($table, $keys, $values) - { - return "REPLACE INTO ".$table." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")"; - } - - // -------------------------------------------------------------------- - - /** - * Update statement - * - * Generates a platform-specific update string from the supplied data - * - * @access public - * @param string the table name - * @param array the update data - * @param array the where clause - * @param array the orderby clause - * @param array the limit clause - * @return string - */ - function _update($table, $values, $where, $orderby = array(), $limit = FALSE) - { - foreach ($values as $key => $val) - { - $valstr[] = $key." = ".$val; - } - - $limit = ( ! $limit) ? '' : ' LIMIT '.$limit; - - $orderby = (count($orderby) >= 1)?' ORDER BY '.implode(", ", $orderby):''; - - $sql = "UPDATE ".$table." SET ".implode(', ', $valstr); - - $sql .= ($where != '' AND count($where) >=1) ? " WHERE ".implode(" ", $where) : ''; - - $sql .= $orderby.$limit; - - return $sql; - } - - // -------------------------------------------------------------------- - - /** - * Update_Batch statement - * - * Generates a platform-specific batch update string from the supplied data - * - * @access public - * @param string the table name - * @param array the update data - * @param array the where clause - * @return string - */ - function _update_batch($table, $values, $index, $where = NULL) - { - $ids = array(); - $where = ($where != '' AND count($where) >=1) ? implode(" ", $where).' AND ' : ''; - - foreach ($values as $key => $val) - { - $ids[] = $val[$index]; - - foreach (array_keys($val) as $field) - { - if ($field != $index) - { - $final[$field][] = 'WHEN '.$index.' = '.$val[$index].' THEN '.$val[$field]; - } - } - } - - $sql = "UPDATE ".$table." SET "; - $cases = ''; - - foreach ($final as $k => $v) - { - $cases .= $k.' = CASE '."\n"; - foreach ($v as $row) - { - $cases .= $row."\n"; - } - - $cases .= 'ELSE '.$k.' END, '; - } - - $sql .= substr($cases, 0, -2); - - $sql .= ' WHERE '.$where.$index.' IN ('.implode(',', $ids).')'; - - return $sql; - } - - // -------------------------------------------------------------------- - - /** - * Truncate statement - * - * Generates a platform-specific truncate string from the supplied data - * If the database does not support the truncate() command - * This function maps to "DELETE FROM table" - * - * @access public - * @param string the table name - * @return string - */ - function _truncate($table) - { - return "TRUNCATE ".$table; - } - - // -------------------------------------------------------------------- - - /** - * Delete statement - * - * Generates a platform-specific delete string from the supplied data - * - * @access public - * @param string the table name - * @param array the where clause - * @param string the limit clause - * @return string - */ - function _delete($table, $where = array(), $like = array(), $limit = FALSE) - { - $conditions = ''; - - if (count($where) > 0 OR count($like) > 0) - { - $conditions = "\nWHERE "; - $conditions .= implode("\n", $this->ar_where); - - if (count($where) > 0 && count($like) > 0) - { - $conditions .= " AND "; - } - $conditions .= implode("\n", $like); - } - - $limit = ( ! $limit) ? '' : ' LIMIT '.$limit; - - return "DELETE FROM ".$table.$conditions.$limit; - } - - // -------------------------------------------------------------------- - - /** - * Limit string - * - * Generates a platform-specific LIMIT clause - * - * @access public - * @param string the sql query string - * @param integer the number of rows to limit the query to - * @param integer the offset value - * @return string - */ - function _limit($sql, $limit, $offset) - { - $sql .= "LIMIT ".$limit; - - if ($offset > 0) - { - $sql .= " OFFSET ".$offset; - } - - return $sql; - } - - // -------------------------------------------------------------------- - - /** - * Close DB Connection - * - * @access public - * @param resource - * @return void - */ - function _close($conn_id) - { - @mysqli_close($conn_id); - } - - -} - - -/* End of file mysqli_driver.php */ -/* Location: ./system/database/drivers/mysqli/mysqli_driver.php */ \ No newline at end of file diff --git a/system/database/drivers/mysqli/mysqli_forge.php b/system/database/drivers/mysqli/mysqli_forge.php deleted file mode 100644 index 26054945..00000000 --- a/system/database/drivers/mysqli/mysqli_forge.php +++ /dev/null @@ -1,258 +0,0 @@ -$attributes) - { - // Numeric field names aren't allowed in databases, so if the key is - // numeric, we know it was assigned by PHP and the developer manually - // entered the field information, so we'll simply add it to the list - if (is_numeric($field)) - { - $sql .= "\n\t$attributes"; - } - else - { - $attributes = array_change_key_case($attributes, CASE_UPPER); - - $sql .= "\n\t".$this->db->_protect_identifiers($field); - - if (array_key_exists('NAME', $attributes)) - { - $sql .= ' '.$this->db->_protect_identifiers($attributes['NAME']).' '; - } - - if (array_key_exists('TYPE', $attributes)) - { - $sql .= ' '.$attributes['TYPE']; - } - - if (array_key_exists('CONSTRAINT', $attributes)) - { - $sql .= '('.$attributes['CONSTRAINT'].')'; - } - - if (array_key_exists('UNSIGNED', $attributes) && $attributes['UNSIGNED'] === TRUE) - { - $sql .= ' UNSIGNED'; - } - - if (array_key_exists('DEFAULT', $attributes)) - { - $sql .= ' DEFAULT \''.$attributes['DEFAULT'].'\''; - } - - if (array_key_exists('NULL', $attributes) && $attributes['NULL'] === TRUE) - { - $sql .= ' NULL'; - } - else - { - $sql .= ' NOT NULL'; - } - - if (array_key_exists('AUTO_INCREMENT', $attributes) && $attributes['AUTO_INCREMENT'] === TRUE) - { - $sql .= ' AUTO_INCREMENT'; - } - } - - // don't add a comma on the end of the last field - if (++$current_field_count < count($fields)) - { - $sql .= ','; - } - } - - return $sql; - } - - // -------------------------------------------------------------------- - - /** - * Create Table - * - * @access private - * @param string the table name - * @param mixed the fields - * @param mixed primary key(s) - * @param mixed key(s) - * @param boolean should 'IF NOT EXISTS' be added to the SQL - * @return bool - */ - function _create_table($table, $fields, $primary_keys, $keys, $if_not_exists) - { - $sql = 'CREATE TABLE '; - - if ($if_not_exists === TRUE) - { - $sql .= 'IF NOT EXISTS '; - } - - $sql .= $this->db->_escape_identifiers($table)." ("; - - $sql .= $this->_process_fields($fields); - - if (count($primary_keys) > 0) - { - $key_name = $this->db->_protect_identifiers(implode('_', $primary_keys)); - $primary_keys = $this->db->_protect_identifiers($primary_keys); - $sql .= ",\n\tPRIMARY KEY ".$key_name." (" . implode(', ', $primary_keys) . ")"; - } - - if (is_array($keys) && count($keys) > 0) - { - foreach ($keys as $key) - { - if (is_array($key)) - { - $key_name = $this->db->_protect_identifiers(implode('_', $key)); - $key = $this->db->_protect_identifiers($key); - } - else - { - $key_name = $this->db->_protect_identifiers($key); - $key = array($key_name); - } - - $sql .= ",\n\tKEY {$key_name} (" . implode(', ', $key) . ")"; - } - } - - $sql .= "\n) DEFAULT CHARACTER SET {$this->db->char_set} COLLATE {$this->db->dbcollat};"; - - return $sql; - } - - // -------------------------------------------------------------------- - - /** - * Drop Table - * - * @access private - * @return string - */ - function _drop_table($table) - { - return "DROP TABLE IF EXISTS ".$this->db->_escape_identifiers($table); - } - - // -------------------------------------------------------------------- - - /** - * Alter table query - * - * Generates a platform-specific query so that a table can be altered - * Called by add_column(), drop_column(), and column_alter(), - * - * @access private - * @param string the ALTER type (ADD, DROP, CHANGE) - * @param string the column name - * @param array fields - * @param string the field after which we should add the new field - * @return object - */ - function _alter_table($alter_type, $table, $fields, $after_field = '') - { - $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table)." $alter_type "; - - // DROP has everything it needs now. - if ($alter_type == 'DROP') - { - return $sql.$this->db->_protect_identifiers($fields); - } - - $sql .= $this->_process_fields($fields); - - if ($after_field != '') - { - $sql .= ' AFTER ' . $this->db->_protect_identifiers($after_field); - } - - return $sql; - } - - // -------------------------------------------------------------------- - - /** - * Rename a table - * - * Generates a platform-specific query so that a table can be renamed - * - * @access private - * @param string the old table name - * @param string the new table name - * @return string - */ - function _rename_table($table_name, $new_table_name) - { - $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table_name)." RENAME TO ".$this->db->_protect_identifiers($new_table_name); - return $sql; - } - -} - -/* End of file mysqli_forge.php */ -/* Location: ./system/database/drivers/mysqli/mysqli_forge.php */ \ No newline at end of file diff --git a/system/database/drivers/mysqli/mysqli_result.php b/system/database/drivers/mysqli/mysqli_result.php deleted file mode 100644 index 124d4e59..00000000 --- a/system/database/drivers/mysqli/mysqli_result.php +++ /dev/null @@ -1,174 +0,0 @@ -result_id); - } - - // -------------------------------------------------------------------- - - /** - * Number of fields in the result set - * - * @access public - * @return integer - */ - function num_fields() - { - return @mysqli_num_fields($this->result_id); - } - - // -------------------------------------------------------------------- - - /** - * Fetch Field Names - * - * Generates an array of column names - * - * @access public - * @return array - */ - function list_fields() - { - $field_names = array(); - while ($field = mysqli_fetch_field($this->result_id)) - { - $field_names[] = $field->name; - } - - return $field_names; - } - - // -------------------------------------------------------------------- - - /** - * Field data - * - * Generates an array of objects containing field meta-data - * - * @access public - * @return array - */ - function field_data() - { - $retval = array(); - while ($field = mysqli_fetch_object($this->result_id)) - { - preg_match('/([a-zA-Z]+)(\(\d+\))?/', $field->Type, $matches); - - $type = (array_key_exists(1, $matches)) ? $matches[1] : NULL; - $length = (array_key_exists(2, $matches)) ? preg_replace('/[^\d]/', '', $matches[2]) : NULL; - - $F = new stdClass(); - $F->name = $field->Field; - $F->type = $type; - $F->default = $field->Default; - $F->max_length = $length; - $F->primary_key = ( $field->Key == 'PRI' ? 1 : 0 ); - - $retval[] = $F; - } - - return $retval; - } - - // -------------------------------------------------------------------- - - /** - * Free the result - * - * @return null - */ - function free_result() - { - if (is_object($this->result_id)) - { - mysqli_free_result($this->result_id); - $this->result_id = FALSE; - } - } - - // -------------------------------------------------------------------- - - /** - * Data Seek - * - * Moves the internal pointer to the desired offset. We call - * this internally before fetching results to make sure the - * result set starts at zero - * - * @access private - * @return array - */ - function _data_seek($n = 0) - { - return mysqli_data_seek($this->result_id, $n); - } - - // -------------------------------------------------------------------- - - /** - * Result - associative array - * - * Returns the result set as an array - * - * @access private - * @return array - */ - function _fetch_assoc() - { - return mysqli_fetch_assoc($this->result_id); - } - - // -------------------------------------------------------------------- - - /** - * Result - object - * - * Returns the result set as an object - * - * @access private - * @return object - */ - function _fetch_object() - { - return mysqli_fetch_object($this->result_id); - } - -} - - -/* End of file mysqli_result.php */ -/* Location: ./system/database/drivers/mysqli/mysqli_result.php */ \ No newline at end of file diff --git a/system/database/drivers/mysqli/mysqli_utility.php b/system/database/drivers/mysqli/mysqli_utility.php deleted file mode 100644 index e17889b8..00000000 --- a/system/database/drivers/mysqli/mysqli_utility.php +++ /dev/null @@ -1,87 +0,0 @@ -db->_escape_identifiers($table); - } - - // -------------------------------------------------------------------- - - /** - * Repair table query - * - * Generates a platform-specific query so that a table can be repaired - * - * @access private - * @param string the table name - * @return object - */ - function _repair_table($table) - { - return "REPAIR TABLE ".$this->db->_escape_identifiers($table); - } - - // -------------------------------------------------------------------- - - /** - * MySQLi Export - * - * @access private - * @param array Preferences - * @return mixed - */ - function _backup($params = array()) - { - // Currently unsupported - return $this->db->display_error('db_unsuported_feature'); - } -} - -/* End of file mysqli_utility.php */ -/* Location: ./system/database/drivers/mysqli/mysqli_utility.php */ \ No newline at end of file diff --git a/system/database/drivers/oci8/index.html b/system/database/drivers/oci8/index.html deleted file mode 100644 index c942a79c..00000000 --- a/system/database/drivers/oci8/index.html +++ /dev/null @@ -1,10 +0,0 @@ - - - 403 Forbidden - - - -

Directory access is forbidden.

- - - \ No newline at end of file diff --git a/system/database/drivers/oci8/oci8_driver.php b/system/database/drivers/oci8/oci8_driver.php deleted file mode 100644 index 930177e6..00000000 --- a/system/database/drivers/oci8/oci8_driver.php +++ /dev/null @@ -1,808 +0,0 @@ -username, $this->password, $this->hostname, $this->char_set); - } - - // -------------------------------------------------------------------- - - /** - * Persistent database connection - * - * @access private called by the base class - * @return resource - */ - public function db_pconnect() - { - return @oci_pconnect($this->username, $this->password, $this->hostname, $this->char_set); - } - - // -------------------------------------------------------------------- - - /** - * Reconnect - * - * Keep / reestablish the db connection if no queries have been - * sent for a length of time exceeding the server's idle timeout - * - * @access public - * @return void - */ - public function reconnect() - { - // not implemented in oracle - return; - } - - // -------------------------------------------------------------------- - - /** - * Select the database - * - * @access private called by the base class - * @return resource - */ - public function db_select() - { - // Not in Oracle - schemas are actually usernames - return TRUE; - } - - // -------------------------------------------------------------------- - - /** - * Set client character set - * - * @access public - * @param string - * @param string - * @return resource - */ - public function db_set_charset($charset, $collation) - { - // @todo - add support if needed - return TRUE; - } - - // -------------------------------------------------------------------- - - /** - * Version number query string - * - * @access protected - * @return string - */ - protected function _version() - { - return oci_server_version($this->conn_id); - } - - // -------------------------------------------------------------------- - - /** - * Execute the query - * - * @access protected called by the base class - * @param string an SQL query - * @return resource - */ - protected function _execute($sql) - { - // oracle must parse the query before it is run. All of the actions with - // the query are based on the statement id returned by ociparse - $this->stmt_id = FALSE; - $this->_set_stmt_id($sql); - oci_set_prefetch($this->stmt_id, 1000); - return @oci_execute($this->stmt_id, $this->_commit); - } - - /** - * Generate a statement ID - * - * @access private - * @param string an SQL query - * @return none - */ - private function _set_stmt_id($sql) - { - if ( ! is_resource($this->stmt_id)) - { - $this->stmt_id = oci_parse($this->conn_id, $this->_prep_query($sql)); - } - } - - // -------------------------------------------------------------------- - - /** - * Prep the query - * - * If needed, each database adapter can prep the query string - * - * @access private called by execute() - * @param string an SQL query - * @return string - */ - private function _prep_query($sql) - { - return $sql; - } - - // -------------------------------------------------------------------- - - /** - * getCursor. Returns a cursor from the datbase - * - * @access public - * @return cursor id - */ - public function get_cursor() - { - $this->curs_id = oci_new_cursor($this->conn_id); - return $this->curs_id; - } - - // -------------------------------------------------------------------- - - /** - * Stored Procedure. Executes a stored procedure - * - * @access public - * @param package package stored procedure is in - * @param procedure stored procedure to execute - * @param params array of parameters - * @return array - * - * params array keys - * - * KEY OPTIONAL NOTES - * name no the name of the parameter should be in : format - * value no the value of the parameter. If this is an OUT or IN OUT parameter, - * this should be a reference to a variable - * type yes the type of the parameter - * length yes the max size of the parameter - */ - public function stored_procedure($package, $procedure, $params) - { - if ($package == '' OR $procedure == '' OR ! is_array($params)) - { - if ($this->db_debug) - { - log_message('error', 'Invalid query: '.$package.'.'.$procedure); - return $this->display_error('db_invalid_query'); - } - return FALSE; - } - - // build the query string - $sql = "begin $package.$procedure("; - - $have_cursor = FALSE; - foreach ($params as $param) - { - $sql .= $param['name'] . ","; - - if (array_key_exists('type', $param) && ($param['type'] === OCI_B_CURSOR)) - { - $have_cursor = TRUE; - } - } - $sql = trim($sql, ",") . "); end;"; - - $this->stmt_id = FALSE; - $this->_set_stmt_id($sql); - $this->_bind_params($params); - $this->query($sql, FALSE, $have_cursor); - } - - // -------------------------------------------------------------------- - - /** - * Bind parameters - * - * @access private - * @return none - */ - private function _bind_params($params) - { - if ( ! is_array($params) OR ! is_resource($this->stmt_id)) - { - return; - } - - foreach ($params as $param) - { - foreach (array('name', 'value', 'type', 'length') as $val) - { - if ( ! isset($param[$val])) - { - $param[$val] = ''; - } - } - - oci_bind_by_name($this->stmt_id, $param['name'], $param['value'], $param['length'], $param['type']); - } - } - - // -------------------------------------------------------------------- - - /** - * Begin Transaction - * - * @access public - * @return bool - */ - public function trans_begin($test_mode = FALSE) - { - if ( ! $this->trans_enabled) - { - return TRUE; - } - - // When transactions are nested we only begin/commit/rollback the outermost ones - if ($this->_trans_depth > 0) - { - return TRUE; - } - - // Reset the transaction failure flag. - // If the $test_mode flag is set to TRUE transactions will be rolled back - // even if the queries produce a successful result. - $this->_trans_failure = ($test_mode === TRUE) ? TRUE : FALSE; - - $this->_commit = OCI_DEFAULT; - return TRUE; - } - - // -------------------------------------------------------------------- - - /** - * Commit Transaction - * - * @access public - * @return bool - */ - public function trans_commit() - { - if ( ! $this->trans_enabled) - { - return TRUE; - } - - // When transactions are nested we only begin/commit/rollback the outermost ones - if ($this->_trans_depth > 0) - { - return TRUE; - } - - $ret = oci_commit($this->conn_id); - $this->_commit = OCI_COMMIT_ON_SUCCESS; - return $ret; - } - - // -------------------------------------------------------------------- - - /** - * Rollback Transaction - * - * @access public - * @return bool - */ - public function trans_rollback() - { - if ( ! $this->trans_enabled) - { - return TRUE; - } - - // When transactions are nested we only begin/commit/rollback the outermost ones - if ($this->_trans_depth > 0) - { - return TRUE; - } - - $ret = oci_rollback($this->conn_id); - $this->_commit = OCI_COMMIT_ON_SUCCESS; - return $ret; - } - - // -------------------------------------------------------------------- - - /** - * Escape String - * - * @access public - * @param string - * @param bool whether or not the string will be used in a LIKE condition - * @return string - */ - public function escape_str($str, $like = FALSE) - { - if (is_array($str)) - { - foreach ($str as $key => $val) - { - $str[$key] = $this->escape_str($val, $like); - } - - return $str; - } - - $str = remove_invisible_characters($str); - - // escape LIKE condition wildcards - if ($like === TRUE) - { - $str = str_replace( array('%', '_', $this->_like_escape_chr), - array($this->_like_escape_chr.'%', $this->_like_escape_chr.'_', $this->_like_escape_chr.$this->_like_escape_chr), - $str); - } - - return $str; - } - - // -------------------------------------------------------------------- - - /** - * Affected Rows - * - * @access public - * @return integer - */ - public function affected_rows() - { - return @oci_num_rows($this->stmt_id); - } - - // -------------------------------------------------------------------- - - /** - * Insert ID - * - * @access public - * @return integer - */ - public function insert_id() - { - // not supported in oracle - return $this->display_error('db_unsupported_function'); - } - - // -------------------------------------------------------------------- - - /** - * "Count All" query - * - * Generates a platform-specific query string that counts all records in - * the specified database - * - * @access public - * @param string - * @return string - */ - public function count_all($table = '') - { - if ($table == '') - { - return 0; - } - - $query = $this->query($this->_count_string . $this->_protect_identifiers('numrows') . " FROM " . $this->_protect_identifiers($table, TRUE, NULL, FALSE)); - - if ($query == FALSE) - { - return 0; - } - - $row = $query->row(); - $this->_reset_select(); - return (int) $row->numrows; - } - - // -------------------------------------------------------------------- - - /** - * Show table query - * - * Generates a platform-specific query string so that the table names can be fetched - * - * @access protected - * @param boolean - * @return string - */ - protected function _list_tables($prefix_limit = FALSE) - { - $sql = "SELECT TABLE_NAME FROM ALL_TABLES"; - - if ($prefix_limit !== FALSE AND $this->dbprefix != '') - { - $sql .= " WHERE TABLE_NAME LIKE '".$this->escape_like_str($this->dbprefix)."%' ".sprintf($this->_like_escape_str, $this->_like_escape_chr); - } - - return $sql; - } - - // -------------------------------------------------------------------- - - /** - * Show column query - * - * Generates a platform-specific query string so that the column names can be fetched - * - * @access protected - * @param string the table name - * @return string - */ - protected function _list_columns($table = '') - { - return "SELECT COLUMN_NAME FROM all_tab_columns WHERE table_name = '$table'"; - } - - // -------------------------------------------------------------------- - - /** - * Field data query - * - * Generates a platform-specific query so that the column data can be retrieved - * - * @access public - * @param string the table name - * @return object - */ - protected function _field_data($table) - { - return "SELECT * FROM ".$table." where rownum = 1"; - } - - // -------------------------------------------------------------------- - - /** - * The error message string - * - * @access protected - * @return string - */ - protected function _error_message() - { - // If the error was during connection, no conn_id should be passed - $error = is_resource($this->conn_id) ? oci_error($this->conn_id) : oci_error(); - return $error['message']; - } - - // -------------------------------------------------------------------- - - /** - * The error message number - * - * @access protected - * @return integer - */ - protected function _error_number() - { - // Same as _error_message() - $error = is_resource($this->conn_id) ? oci_error($this->conn_id) : oci_error(); - return $error['code']; - } - - // -------------------------------------------------------------------- - - /** - * Escape the SQL Identifiers - * - * This function escapes column and table names - * - * @access protected - * @param string - * @return string - */ - protected function _escape_identifiers($item) - { - if ($this->_escape_char == '') - { - return $item; - } - - foreach ($this->_reserved_identifiers as $id) - { - if (strpos($item, '.'.$id) !== FALSE) - { - $str = $this->_escape_char. str_replace('.', $this->_escape_char.'.', $item); - - // remove duplicates if the user already included the escape - return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str); - } - } - - if (strpos($item, '.') !== FALSE) - { - $str = $this->_escape_char.str_replace('.', $this->_escape_char.'.'.$this->_escape_char, $item).$this->_escape_char; - } - else - { - $str = $this->_escape_char.$item.$this->_escape_char; - } - - // remove duplicates if the user already included the escape - return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str); - } - - // -------------------------------------------------------------------- - - /** - * From Tables - * - * This function implicitly groups FROM tables so there is no confusion - * about operator precedence in harmony with SQL standards - * - * @access protected - * @param type - * @return type - */ - protected function _from_tables($tables) - { - if ( ! is_array($tables)) - { - $tables = array($tables); - } - - return implode(', ', $tables); - } - - // -------------------------------------------------------------------- - - /** - * Insert statement - * - * Generates a platform-specific insert string from the supplied data - * - * @access public - * @param string the table name - * @param array the insert keys - * @param array the insert values - * @return string - */ - protected function _insert($table, $keys, $values) - { - return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")"; - } - - // -------------------------------------------------------------------- - - /** - * Insert_batch statement - * - * Generates a platform-specific insert string from the supplied data - * - * @access protected - * @param string the table name - * @param array the insert keys - * @param array the insert values - * @return string - */ - protected function _insert_batch($table, $keys, $values) - { - $keys = implode(', ', $keys); - $sql = "INSERT ALL\n"; - - for ($i = 0, $c = count($values); $i < $c; $i++) - { - $sql .= ' INTO ' . $table . ' (' . $keys . ') VALUES ' . $values[$i] . "\n"; - } - - $sql .= 'SELECT * FROM dual'; - - return $sql; - } - - // -------------------------------------------------------------------- - - /** - * Update statement - * - * Generates a platform-specific update string from the supplied data - * - * @access protected - * @param string the table name - * @param array the update data - * @param array the where clause - * @param array the orderby clause - * @param array the limit clause - * @return string - */ - protected function _update($table, $values, $where, $orderby = array(), $limit = FALSE) - { - foreach ($values as $key => $val) - { - $valstr[] = $key." = ".$val; - } - - $limit = ( ! $limit) ? '' : ' LIMIT '.$limit; - - $orderby = (count($orderby) >= 1)?' ORDER BY '.implode(", ", $orderby):''; - - $sql = "UPDATE ".$table." SET ".implode(', ', $valstr); - - $sql .= ($where != '' AND count($where) >=1) ? " WHERE ".implode(" ", $where) : ''; - - $sql .= $orderby.$limit; - - return $sql; - } - - // -------------------------------------------------------------------- - - /** - * Truncate statement - * - * Generates a platform-specific truncate string from the supplied data - * If the database does not support the truncate() command - * This function maps to "DELETE FROM table" - * - * @access protected - * @param string the table name - * @return string - */ - protected function _truncate($table) - { - return "TRUNCATE TABLE ".$table; - } - - // -------------------------------------------------------------------- - - /** - * Delete statement - * - * Generates a platform-specific delete string from the supplied data - * - * @access protected - * @param string the table name - * @param array the where clause - * @param string the limit clause - * @return string - */ - protected function _delete($table, $where = array(), $like = array(), $limit = FALSE) - { - $conditions = ''; - - if (count($where) > 0 OR count($like) > 0) - { - $conditions = "\nWHERE "; - $conditions .= implode("\n", $this->ar_where); - - if (count($where) > 0 && count($like) > 0) - { - $conditions .= " AND "; - } - $conditions .= implode("\n", $like); - } - - $limit = ( ! $limit) ? '' : ' LIMIT '.$limit; - - return "DELETE FROM ".$table.$conditions.$limit; - } - - // -------------------------------------------------------------------- - - /** - * Limit string - * - * Generates a platform-specific LIMIT clause - * - * @access protected - * @param string the sql query string - * @param integer the number of rows to limit the query to - * @param integer the offset value - * @return string - */ - protected function _limit($sql, $limit, $offset) - { - $limit = $offset + $limit; - $newsql = "SELECT * FROM (select inner_query.*, rownum rnum FROM ($sql) inner_query WHERE rownum < $limit)"; - - if ($offset != 0) - { - $newsql .= " WHERE rnum >= $offset"; - } - - // remember that we used limits - $this->limit_used = TRUE; - - return $newsql; - } - - // -------------------------------------------------------------------- - - /** - * Close DB Connection - * - * @access protected - * @param resource - * @return void - */ - protected function _close($conn_id) - { - @oci_close($conn_id); - } - - -} - - - -/* End of file oci8_driver.php */ -/* Location: ./system/database/drivers/oci8/oci8_driver.php */ diff --git a/system/database/drivers/oci8/oci8_forge.php b/system/database/drivers/oci8/oci8_forge.php deleted file mode 100644 index 3cd17585..00000000 --- a/system/database/drivers/oci8/oci8_forge.php +++ /dev/null @@ -1,248 +0,0 @@ -db->_escape_identifiers($table)." ("; - $current_field_count = 0; - - foreach ($fields as $field=>$attributes) - { - // Numeric field names aren't allowed in databases, so if the key is - // numeric, we know it was assigned by PHP and the developer manually - // entered the field information, so we'll simply add it to the list - if (is_numeric($field)) - { - $sql .= "\n\t$attributes"; - } - else - { - $attributes = array_change_key_case($attributes, CASE_UPPER); - - $sql .= "\n\t".$this->db->_protect_identifiers($field); - - $sql .= ' '.$attributes['TYPE']; - - if (array_key_exists('CONSTRAINT', $attributes)) - { - $sql .= '('.$attributes['CONSTRAINT'].')'; - } - - if (array_key_exists('UNSIGNED', $attributes) && $attributes['UNSIGNED'] === TRUE) - { - $sql .= ' UNSIGNED'; - } - - if (array_key_exists('DEFAULT', $attributes)) - { - $sql .= ' DEFAULT \''.$attributes['DEFAULT'].'\''; - } - - if (array_key_exists('NULL', $attributes) && $attributes['NULL'] === TRUE) - { - $sql .= ' NULL'; - } - else - { - $sql .= ' NOT NULL'; - } - - if (array_key_exists('AUTO_INCREMENT', $attributes) && $attributes['AUTO_INCREMENT'] === TRUE) - { - $sql .= ' AUTO_INCREMENT'; - } - } - - // don't add a comma on the end of the last field - if (++$current_field_count < count($fields)) - { - $sql .= ','; - } - } - - if (count($primary_keys) > 0) - { - $primary_keys = $this->db->_protect_identifiers($primary_keys); - $sql .= ",\n\tPRIMARY KEY (" . implode(', ', $primary_keys) . ")"; - } - - if (is_array($keys) && count($keys) > 0) - { - foreach ($keys as $key) - { - if (is_array($key)) - { - $key = $this->db->_protect_identifiers($key); - } - else - { - $key = array($this->db->_protect_identifiers($key)); - } - - $sql .= ",\n\tUNIQUE COLUMNS (" . implode(', ', $key) . ")"; - } - } - - $sql .= "\n)"; - - return $sql; - } - - // -------------------------------------------------------------------- - - /** - * Drop Table - * - * @access private - * @return bool - */ - function _drop_table($table) - { - return FALSE; - } - - // -------------------------------------------------------------------- - - /** - * Alter table query - * - * Generates a platform-specific query so that a table can be altered - * Called by add_column(), drop_column(), and column_alter(), - * - * @access private - * @param string the ALTER type (ADD, DROP, CHANGE) - * @param string the column name - * @param string the table name - * @param string the column definition - * @param string the default value - * @param boolean should 'NOT NULL' be added - * @param string the field after which we should add the new field - * @return object - */ - function _alter_table($alter_type, $table, $column_name, $column_definition = '', $default_value = '', $null = '', $after_field = '') - { - $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table)." $alter_type ".$this->db->_protect_identifiers($column_name); - - // DROP has everything it needs now. - if ($alter_type == 'DROP') - { - return $sql; - } - - $sql .= " $column_definition"; - - if ($default_value != '') - { - $sql .= " DEFAULT \"$default_value\""; - } - - if ($null === NULL) - { - $sql .= ' NULL'; - } - else - { - $sql .= ' NOT NULL'; - } - - if ($after_field != '') - { - $sql .= ' AFTER ' . $this->db->_protect_identifiers($after_field); - } - - return $sql; - - } - - // -------------------------------------------------------------------- - - /** - * Rename a table - * - * Generates a platform-specific query so that a table can be renamed - * - * @access private - * @param string the old table name - * @param string the new table name - * @return string - */ - function _rename_table($table_name, $new_table_name) - { - $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table_name)." RENAME TO ".$this->db->_protect_identifiers($new_table_name); - return $sql; - } - - -} - -/* End of file oci8_forge.php */ -/* Location: ./system/database/drivers/oci8/oci8_forge.php */ \ No newline at end of file diff --git a/system/database/drivers/oci8/oci8_result.php b/system/database/drivers/oci8/oci8_result.php deleted file mode 100644 index 3421278a..00000000 --- a/system/database/drivers/oci8/oci8_result.php +++ /dev/null @@ -1,217 +0,0 @@ -num_rows === 0 && count($this->result_array()) > 0) - { - $this->num_rows = count($this->result_array()); - @oci_execute($this->stmt_id); - - if ($this->curs_id) - { - @oci_execute($this->curs_id); - } - } - - return $this->num_rows; - } - - // -------------------------------------------------------------------- - - /** - * Number of fields in the result set - * - * @access public - * @return integer - */ - public function num_fields() - { - $count = @oci_num_fields($this->stmt_id); - - // if we used a limit we subtract it - if ($this->limit_used) - { - $count = $count - 1; - } - - return $count; - } - - // -------------------------------------------------------------------- - - /** - * Fetch Field Names - * - * Generates an array of column names - * - * @access public - * @return array - */ - public function list_fields() - { - $field_names = array(); - for ($c = 1, $fieldCount = $this->num_fields(); $c <= $fieldCount; $c++) - { - $field_names[] = oci_field_name($this->stmt_id, $c); - } - return $field_names; - } - - // -------------------------------------------------------------------- - - /** - * Field data - * - * Generates an array of objects containing field meta-data - * - * @access public - * @return array - */ - public function field_data() - { - $retval = array(); - for ($c = 1, $fieldCount = $this->num_fields(); $c <= $fieldCount; $c++) - { - $F = new stdClass(); - $F->name = oci_field_name($this->stmt_id, $c); - $F->type = oci_field_type($this->stmt_id, $c); - $F->max_length = oci_field_size($this->stmt_id, $c); - - $retval[] = $F; - } - - return $retval; - } - - // -------------------------------------------------------------------- - - /** - * Free the result - * - * @return null - */ - public function free_result() - { - if (is_resource($this->result_id)) - { - oci_free_statement($this->result_id); - $this->result_id = FALSE; - } - } - - // -------------------------------------------------------------------- - - /** - * Result - associative array - * - * Returns the result set as an array - * - * @access protected - * @return array - */ - protected function _fetch_assoc() - { - $id = ($this->curs_id) ? $this->curs_id : $this->stmt_id; - return oci_fetch_assoc($id); - } - - // -------------------------------------------------------------------- - - /** - * Result - object - * - * Returns the result set as an object - * - * @access protected - * @return object - */ - protected function _fetch_object() - { - $id = ($this->curs_id) ? $this->curs_id : $this->stmt_id; - return @oci_fetch_object($id); - } - - // -------------------------------------------------------------------- - - /** - * Query result. "array" version. - * - * @access public - * @return array - */ - public function result_array() - { - if (count($this->result_array) > 0) - { - return $this->result_array; - } - - $row = NULL; - while ($row = $this->_fetch_assoc()) - { - $this->result_array[] = $row; - } - - return $this->result_array; - } - - // -------------------------------------------------------------------- - - /** - * Data Seek - * - * Moves the internal pointer to the desired offset. We call - * this internally before fetching results to make sure the - * result set starts at zero - * - * @access protected - * @return array - */ - protected function _data_seek($n = 0) - { - return FALSE; // Not needed - } - -} - - -/* End of file oci8_result.php */ -/* Location: ./system/database/drivers/oci8/oci8_result.php */ diff --git a/system/database/drivers/oci8/oci8_utility.php b/system/database/drivers/oci8/oci8_utility.php deleted file mode 100644 index 854b467e..00000000 --- a/system/database/drivers/oci8/oci8_utility.php +++ /dev/null @@ -1,87 +0,0 @@ -db->display_error('db_unsuported_feature'); - } -} - -/* End of file oci8_utility.php */ -/* Location: ./system/database/drivers/oci8/oci8_utility.php */ \ No newline at end of file diff --git a/system/database/drivers/odbc/index.html b/system/database/drivers/odbc/index.html deleted file mode 100644 index c942a79c..00000000 --- a/system/database/drivers/odbc/index.html +++ /dev/null @@ -1,10 +0,0 @@ - - - 403 Forbidden - - - -

Directory access is forbidden.

- - - \ No newline at end of file diff --git a/system/database/drivers/odbc/odbc_driver.php b/system/database/drivers/odbc/odbc_driver.php deleted file mode 100644 index bcd7937d..00000000 --- a/system/database/drivers/odbc/odbc_driver.php +++ /dev/null @@ -1,637 +0,0 @@ -_random_keyword = ' RND('.time().')'; // database specific random keyword - } - - /** - * Non-persistent database connection - * - * @access private called by the base class - * @return resource - */ - function db_connect() - { - return @odbc_connect($this->hostname, $this->username, $this->password); - } - - // -------------------------------------------------------------------- - - /** - * Persistent database connection - * - * @access private called by the base class - * @return resource - */ - function db_pconnect() - { - return @odbc_pconnect($this->hostname, $this->username, $this->password); - } - - // -------------------------------------------------------------------- - - /** - * Reconnect - * - * Keep / reestablish the db connection if no queries have been - * sent for a length of time exceeding the server's idle timeout - * - * @access public - * @return void - */ - function reconnect() - { - // not implemented in odbc - } - - // -------------------------------------------------------------------- - - /** - * Select the database - * - * @access private called by the base class - * @return resource - */ - function db_select() - { - // Not needed for ODBC - return TRUE; - } - - // -------------------------------------------------------------------- - - /** - * Set client character set - * - * @access public - * @param string - * @param string - * @return resource - */ - function db_set_charset($charset, $collation) - { - // @todo - add support if needed - return TRUE; - } - - // -------------------------------------------------------------------- - - /** - * Version number query string - * - * @access public - * @return string - */ - function _version() - { - return "SELECT version() AS ver"; - } - - // -------------------------------------------------------------------- - - /** - * Execute the query - * - * @access private called by the base class - * @param string an SQL query - * @return resource - */ - function _execute($sql) - { - $sql = $this->_prep_query($sql); - return @odbc_exec($this->conn_id, $sql); - } - - // -------------------------------------------------------------------- - - /** - * Prep the query - * - * If needed, each database adapter can prep the query string - * - * @access private called by execute() - * @param string an SQL query - * @return string - */ - function _prep_query($sql) - { - return $sql; - } - - // -------------------------------------------------------------------- - - /** - * Begin Transaction - * - * @access public - * @return bool - */ - function trans_begin($test_mode = FALSE) - { - if ( ! $this->trans_enabled) - { - return TRUE; - } - - // When transactions are nested we only begin/commit/rollback the outermost ones - if ($this->_trans_depth > 0) - { - return TRUE; - } - - // Reset the transaction failure flag. - // If the $test_mode flag is set to TRUE transactions will be rolled back - // even if the queries produce a successful result. - $this->_trans_failure = ($test_mode === TRUE) ? TRUE : FALSE; - - return odbc_autocommit($this->conn_id, FALSE); - } - - // -------------------------------------------------------------------- - - /** - * Commit Transaction - * - * @access public - * @return bool - */ - function trans_commit() - { - if ( ! $this->trans_enabled) - { - return TRUE; - } - - // When transactions are nested we only begin/commit/rollback the outermost ones - if ($this->_trans_depth > 0) - { - return TRUE; - } - - $ret = odbc_commit($this->conn_id); - odbc_autocommit($this->conn_id, TRUE); - return $ret; - } - - // -------------------------------------------------------------------- - - /** - * Rollback Transaction - * - * @access public - * @return bool - */ - function trans_rollback() - { - if ( ! $this->trans_enabled) - { - return TRUE; - } - - // When transactions are nested we only begin/commit/rollback the outermost ones - if ($this->_trans_depth > 0) - { - return TRUE; - } - - $ret = odbc_rollback($this->conn_id); - odbc_autocommit($this->conn_id, TRUE); - return $ret; - } - - // -------------------------------------------------------------------- - - /** - * Escape String - * - * @access public - * @param string - * @param bool whether or not the string will be used in a LIKE condition - * @return string - */ - function escape_str($str, $like = FALSE) - { - if (is_array($str)) - { - foreach ($str as $key => $val) - { - $str[$key] = $this->escape_str($val, $like); - } - - return $str; - } - - // ODBC doesn't require escaping - $str = remove_invisible_characters($str); - - // escape LIKE condition wildcards - if ($like === TRUE) - { - $str = str_replace( array('%', '_', $this->_like_escape_chr), - array($this->_like_escape_chr.'%', $this->_like_escape_chr.'_', $this->_like_escape_chr.$this->_like_escape_chr), - $str); - } - - return $str; - } - - // -------------------------------------------------------------------- - - /** - * Affected Rows - * - * @access public - * @return integer - */ - function affected_rows() - { - return @odbc_num_rows($this->conn_id); - } - - // -------------------------------------------------------------------- - - /** - * Insert ID - * - * @access public - * @return integer - */ - function insert_id() - { - return @odbc_insert_id($this->conn_id); - } - - // -------------------------------------------------------------------- - - /** - * "Count All" query - * - * Generates a platform-specific query string that counts all records in - * the specified database - * - * @access public - * @param string - * @return string - */ - function count_all($table = '') - { - if ($table == '') - { - return 0; - } - - $query = $this->query($this->_count_string . $this->_protect_identifiers('numrows') . " FROM " . $this->_protect_identifiers($table, TRUE, NULL, FALSE)); - - if ($query->num_rows() == 0) - { - return 0; - } - - $row = $query->row(); - $this->_reset_select(); - return (int) $row->numrows; - } - - // -------------------------------------------------------------------- - - /** - * Show table query - * - * Generates a platform-specific query string so that the table names can be fetched - * - * @access private - * @param boolean - * @return string - */ - function _list_tables($prefix_limit = FALSE) - { - $sql = "SHOW TABLES FROM `".$this->database."`"; - - if ($prefix_limit !== FALSE AND $this->dbprefix != '') - { - //$sql .= " LIKE '".$this->escape_like_str($this->dbprefix)."%' ".sprintf($this->_like_escape_str, $this->_like_escape_chr); - return FALSE; // not currently supported - } - - return $sql; - } - - // -------------------------------------------------------------------- - - /** - * Show column query - * - * Generates a platform-specific query string so that the column names can be fetched - * - * @access public - * @param string the table name - * @return string - */ - function _list_columns($table = '') - { - return "SHOW COLUMNS FROM ".$table; - } - - // -------------------------------------------------------------------- - - /** - * Field data query - * - * Generates a platform-specific query so that the column data can be retrieved - * - * @access public - * @param string the table name - * @return object - */ - function _field_data($table) - { - return "SELECT TOP 1 FROM ".$table; - } - - // -------------------------------------------------------------------- - - /** - * The error message string - * - * @access private - * @return string - */ - function _error_message() - { - return odbc_errormsg($this->conn_id); - } - - // -------------------------------------------------------------------- - - /** - * The error message number - * - * @access private - * @return integer - */ - function _error_number() - { - return odbc_error($this->conn_id); - } - - // -------------------------------------------------------------------- - - /** - * Escape the SQL Identifiers - * - * This function escapes column and table names - * - * @access private - * @param string - * @return string - */ - function _escape_identifiers($item) - { - if ($this->_escape_char == '') - { - return $item; - } - - foreach ($this->_reserved_identifiers as $id) - { - if (strpos($item, '.'.$id) !== FALSE) - { - $str = $this->_escape_char. str_replace('.', $this->_escape_char.'.', $item); - - // remove duplicates if the user already included the escape - return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str); - } - } - - if (strpos($item, '.') !== FALSE) - { - $str = $this->_escape_char.str_replace('.', $this->_escape_char.'.'.$this->_escape_char, $item).$this->_escape_char; - } - else - { - $str = $this->_escape_char.$item.$this->_escape_char; - } - - // remove duplicates if the user already included the escape - return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str); - } - - // -------------------------------------------------------------------- - - /** - * From Tables - * - * This function implicitly groups FROM tables so there is no confusion - * about operator precedence in harmony with SQL standards - * - * @access public - * @param type - * @return type - */ - function _from_tables($tables) - { - if ( ! is_array($tables)) - { - $tables = array($tables); - } - - return '('.implode(', ', $tables).')'; - } - - // -------------------------------------------------------------------- - - /** - * Insert statement - * - * Generates a platform-specific insert string from the supplied data - * - * @access public - * @param string the table name - * @param array the insert keys - * @param array the insert values - * @return string - */ - function _insert($table, $keys, $values) - { - return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")"; - } - - // -------------------------------------------------------------------- - - /** - * Update statement - * - * Generates a platform-specific update string from the supplied data - * - * @access public - * @param string the table name - * @param array the update data - * @param array the where clause - * @param array the orderby clause - * @param array the limit clause - * @return string - */ - function _update($table, $values, $where, $orderby = array(), $limit = FALSE) - { - foreach ($values as $key => $val) - { - $valstr[] = $key." = ".$val; - } - - $limit = ( ! $limit) ? '' : ' LIMIT '.$limit; - - $orderby = (count($orderby) >= 1)?' ORDER BY '.implode(", ", $orderby):''; - - $sql = "UPDATE ".$table." SET ".implode(', ', $valstr); - - $sql .= ($where != '' AND count($where) >=1) ? " WHERE ".implode(" ", $where) : ''; - - $sql .= $orderby.$limit; - - return $sql; - } - - - // -------------------------------------------------------------------- - - /** - * Truncate statement - * - * Generates a platform-specific truncate string from the supplied data - * If the database does not support the truncate() command - * This function maps to "DELETE FROM table" - * - * @access public - * @param string the table name - * @return string - */ - function _truncate($table) - { - return $this->_delete($table); - } - - // -------------------------------------------------------------------- - - /** - * Delete statement - * - * Generates a platform-specific delete string from the supplied data - * - * @access public - * @param string the table name - * @param array the where clause - * @param string the limit clause - * @return string - */ - function _delete($table, $where = array(), $like = array(), $limit = FALSE) - { - $conditions = ''; - - if (count($where) > 0 OR count($like) > 0) - { - $conditions = "\nWHERE "; - $conditions .= implode("\n", $this->ar_where); - - if (count($where) > 0 && count($like) > 0) - { - $conditions .= " AND "; - } - $conditions .= implode("\n", $like); - } - - $limit = ( ! $limit) ? '' : ' LIMIT '.$limit; - - return "DELETE FROM ".$table.$conditions.$limit; - } - - // -------------------------------------------------------------------- - - /** - * Limit string - * - * Generates a platform-specific LIMIT clause - * - * @access public - * @param string the sql query string - * @param integer the number of rows to limit the query to - * @param integer the offset value - * @return string - */ - function _limit($sql, $limit, $offset) - { - // Does ODBC doesn't use the LIMIT clause? - return $sql; - } - - // -------------------------------------------------------------------- - - /** - * Close DB Connection - * - * @access public - * @param resource - * @return void - */ - function _close($conn_id) - { - @odbc_close($conn_id); - } - - -} - - - -/* End of file odbc_driver.php */ -/* Location: ./system/database/drivers/odbc/odbc_driver.php */ \ No newline at end of file diff --git a/system/database/drivers/odbc/odbc_forge.php b/system/database/drivers/odbc/odbc_forge.php deleted file mode 100644 index 3ec86b4e..00000000 --- a/system/database/drivers/odbc/odbc_forge.php +++ /dev/null @@ -1,266 +0,0 @@ -db->db_debug) - { - return $this->db->display_error('db_unsuported_feature'); - } - return FALSE; - } - - // -------------------------------------------------------------------- - - /** - * Drop database - * - * @access private - * @param string the database name - * @return bool - */ - function _drop_database($name) - { - // ODBC has no "drop database" command since it's - // designed to connect to an existing database - if ($this->db->db_debug) - { - return $this->db->display_error('db_unsuported_feature'); - } - return FALSE; - } - - // -------------------------------------------------------------------- - - /** - * Create Table - * - * @access private - * @param string the table name - * @param array the fields - * @param mixed primary key(s) - * @param mixed key(s) - * @param boolean should 'IF NOT EXISTS' be added to the SQL - * @return bool - */ - function _create_table($table, $fields, $primary_keys, $keys, $if_not_exists) - { - $sql = 'CREATE TABLE '; - - if ($if_not_exists === TRUE) - { - $sql .= 'IF NOT EXISTS '; - } - - $sql .= $this->db->_escape_identifiers($table)." ("; - $current_field_count = 0; - - foreach ($fields as $field=>$attributes) - { - // Numeric field names aren't allowed in databases, so if the key is - // numeric, we know it was assigned by PHP and the developer manually - // entered the field information, so we'll simply add it to the list - if (is_numeric($field)) - { - $sql .= "\n\t$attributes"; - } - else - { - $attributes = array_change_key_case($attributes, CASE_UPPER); - - $sql .= "\n\t".$this->db->_protect_identifiers($field); - - $sql .= ' '.$attributes['TYPE']; - - if (array_key_exists('CONSTRAINT', $attributes)) - { - $sql .= '('.$attributes['CONSTRAINT'].')'; - } - - if (array_key_exists('UNSIGNED', $attributes) && $attributes['UNSIGNED'] === TRUE) - { - $sql .= ' UNSIGNED'; - } - - if (array_key_exists('DEFAULT', $attributes)) - { - $sql .= ' DEFAULT \''.$attributes['DEFAULT'].'\''; - } - - if (array_key_exists('NULL', $attributes) && $attributes['NULL'] === TRUE) - { - $sql .= ' NULL'; - } - else - { - $sql .= ' NOT NULL'; - } - - if (array_key_exists('AUTO_INCREMENT', $attributes) && $attributes['AUTO_INCREMENT'] === TRUE) - { - $sql .= ' AUTO_INCREMENT'; - } - } - - // don't add a comma on the end of the last field - if (++$current_field_count < count($fields)) - { - $sql .= ','; - } - } - - if (count($primary_keys) > 0) - { - $primary_keys = $this->db->_protect_identifiers($primary_keys); - $sql .= ",\n\tPRIMARY KEY (" . implode(', ', $primary_keys) . ")"; - } - - if (is_array($keys) && count($keys) > 0) - { - foreach ($keys as $key) - { - if (is_array($key)) - { - $key = $this->db->_protect_identifiers($key); - } - else - { - $key = array($this->db->_protect_identifiers($key)); - } - - $sql .= ",\n\tFOREIGN KEY (" . implode(', ', $key) . ")"; - } - } - - $sql .= "\n)"; - - return $sql; - } - - // -------------------------------------------------------------------- - - /** - * Drop Table - * - * @access private - * @return bool - */ - function _drop_table($table) - { - // Not a supported ODBC feature - if ($this->db->db_debug) - { - return $this->db->display_error('db_unsuported_feature'); - } - return FALSE; - } - - // -------------------------------------------------------------------- - - /** - * Alter table query - * - * Generates a platform-specific query so that a table can be altered - * Called by add_column(), drop_column(), and column_alter(), - * - * @access private - * @param string the ALTER type (ADD, DROP, CHANGE) - * @param string the column name - * @param string the table name - * @param string the column definition - * @param string the default value - * @param boolean should 'NOT NULL' be added - * @param string the field after which we should add the new field - * @return object - */ - function _alter_table($alter_type, $table, $column_name, $column_definition = '', $default_value = '', $null = '', $after_field = '') - { - $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table)." $alter_type ".$this->db->_protect_identifiers($column_name); - - // DROP has everything it needs now. - if ($alter_type == 'DROP') - { - return $sql; - } - - $sql .= " $column_definition"; - - if ($default_value != '') - { - $sql .= " DEFAULT \"$default_value\""; - } - - if ($null === NULL) - { - $sql .= ' NULL'; - } - else - { - $sql .= ' NOT NULL'; - } - - if ($after_field != '') - { - $sql .= ' AFTER ' . $this->db->_protect_identifiers($after_field); - } - - return $sql; - - } - - - // -------------------------------------------------------------------- - - /** - * Rename a table - * - * Generates a platform-specific query so that a table can be renamed - * - * @access private - * @param string the old table name - * @param string the new table name - * @return string - */ - function _rename_table($table_name, $new_table_name) - { - $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table_name)." RENAME TO ".$this->db->_protect_identifiers($new_table_name); - return $sql; - } - - -} - -/* End of file odbc_forge.php */ -/* Location: ./system/database/drivers/odbc/odbc_forge.php */ \ No newline at end of file diff --git a/system/database/drivers/odbc/odbc_result.php b/system/database/drivers/odbc/odbc_result.php deleted file mode 100644 index 5d64a464..00000000 --- a/system/database/drivers/odbc/odbc_result.php +++ /dev/null @@ -1,228 +0,0 @@ -result_id); - } - - // -------------------------------------------------------------------- - - /** - * Number of fields in the result set - * - * @access public - * @return integer - */ - function num_fields() - { - return @odbc_num_fields($this->result_id); - } - - // -------------------------------------------------------------------- - - /** - * Fetch Field Names - * - * Generates an array of column names - * - * @access public - * @return array - */ - function list_fields() - { - $field_names = array(); - for ($i = 0; $i < $this->num_fields(); $i++) - { - $field_names[] = odbc_field_name($this->result_id, $i); - } - - return $field_names; - } - - // -------------------------------------------------------------------- - - /** - * Field data - * - * Generates an array of objects containing field meta-data - * - * @access public - * @return array - */ - function field_data() - { - $retval = array(); - for ($i = 0; $i < $this->num_fields(); $i++) - { - $F = new stdClass(); - $F->name = odbc_field_name($this->result_id, $i); - $F->type = odbc_field_type($this->result_id, $i); - $F->max_length = odbc_field_len($this->result_id, $i); - $F->primary_key = 0; - $F->default = ''; - - $retval[] = $F; - } - - return $retval; - } - - // -------------------------------------------------------------------- - - /** - * Free the result - * - * @return null - */ - function free_result() - { - if (is_resource($this->result_id)) - { - odbc_free_result($this->result_id); - $this->result_id = FALSE; - } - } - - // -------------------------------------------------------------------- - - /** - * Data Seek - * - * Moves the internal pointer to the desired offset. We call - * this internally before fetching results to make sure the - * result set starts at zero - * - * @access private - * @return array - */ - function _data_seek($n = 0) - { - return FALSE; - } - - // -------------------------------------------------------------------- - - /** - * Result - associative array - * - * Returns the result set as an array - * - * @access private - * @return array - */ - function _fetch_assoc() - { - if (function_exists('odbc_fetch_object')) - { - return odbc_fetch_array($this->result_id); - } - else - { - return $this->_odbc_fetch_array($this->result_id); - } - } - - // -------------------------------------------------------------------- - - /** - * Result - object - * - * Returns the result set as an object - * - * @access private - * @return object - */ - function _fetch_object() - { - if (function_exists('odbc_fetch_object')) - { - return odbc_fetch_object($this->result_id); - } - else - { - return $this->_odbc_fetch_object($this->result_id); - } - } - - - /** - * Result - object - * - * subsititutes the odbc_fetch_object function when - * not available (odbc_fetch_object requires unixODBC) - * - * @access private - * @return object - */ - function _odbc_fetch_object(& $odbc_result) { - $rs = array(); - $rs_obj = FALSE; - if (odbc_fetch_into($odbc_result, $rs)) { - foreach ($rs as $k=>$v) { - $field_name= odbc_field_name($odbc_result, $k+1); - $rs_obj->$field_name = $v; - } - } - return $rs_obj; - } - - - /** - * Result - array - * - * subsititutes the odbc_fetch_array function when - * not available (odbc_fetch_array requires unixODBC) - * - * @access private - * @return array - */ - function _odbc_fetch_array(& $odbc_result) { - $rs = array(); - $rs_assoc = FALSE; - if (odbc_fetch_into($odbc_result, $rs)) { - $rs_assoc=array(); - foreach ($rs as $k=>$v) { - $field_name= odbc_field_name($odbc_result, $k+1); - $rs_assoc[$field_name] = $v; - } - } - return $rs_assoc; - } - -} - - -/* End of file odbc_result.php */ -/* Location: ./system/database/drivers/odbc/odbc_result.php */ \ No newline at end of file diff --git a/system/database/drivers/odbc/odbc_utility.php b/system/database/drivers/odbc/odbc_utility.php deleted file mode 100644 index d335bed9..00000000 --- a/system/database/drivers/odbc/odbc_utility.php +++ /dev/null @@ -1,103 +0,0 @@ -db->db_debug) - { - return $this->db->display_error('db_unsuported_feature'); - } - return FALSE; - } - - // -------------------------------------------------------------------- - - /** - * Optimize table query - * - * Generates a platform-specific query so that a table can be optimized - * - * @access private - * @param string the table name - * @return object - */ - function _optimize_table($table) - { - // Not a supported ODBC feature - if ($this->db->db_debug) - { - return $this->db->display_error('db_unsuported_feature'); - } - return FALSE; - } - - // -------------------------------------------------------------------- - - /** - * Repair table query - * - * Generates a platform-specific query so that a table can be repaired - * - * @access private - * @param string the table name - * @return object - */ - function _repair_table($table) - { - // Not a supported ODBC feature - if ($this->db->db_debug) - { - return $this->db->display_error('db_unsuported_feature'); - } - return FALSE; - } - - // -------------------------------------------------------------------- - - /** - * ODBC Export - * - * @access private - * @param array Preferences - * @return mixed - */ - function _backup($params = array()) - { - // Currently unsupported - return $this->db->display_error('db_unsuported_feature'); - } - -} - -/* End of file odbc_utility.php */ -/* Location: ./system/database/drivers/odbc/odbc_utility.php */ \ No newline at end of file diff --git a/system/database/drivers/pdo/index.html b/system/database/drivers/pdo/index.html deleted file mode 100644 index c942a79c..00000000 --- a/system/database/drivers/pdo/index.html +++ /dev/null @@ -1,10 +0,0 @@ - - - 403 Forbidden - - - -

Directory access is forbidden.

- - - \ No newline at end of file diff --git a/system/database/drivers/pdo/pdo_driver.php b/system/database/drivers/pdo/pdo_driver.php deleted file mode 100644 index 1ecc90a8..00000000 --- a/system/database/drivers/pdo/pdo_driver.php +++ /dev/null @@ -1,812 +0,0 @@ -hostname, 'mysql') !== FALSE) - { - $this->_like_escape_str = ''; - $this->_like_escape_chr = ''; - - //Prior to this version, the charset can't be set in the dsn - if(is_php('5.3.6')) - { - $this->hostname .= ";charset={$this->char_set}"; - } - - //Set the charset with the connection options - $this->options['PDO::MYSQL_ATTR_INIT_COMMAND'] = "SET NAMES {$this->char_set}"; - } - elseif (strpos($this->hostname, 'odbc') !== FALSE) - { - $this->_like_escape_str = " {escape '%s'} "; - $this->_like_escape_chr = '!'; - } - else - { - $this->_like_escape_str = " ESCAPE '%s' "; - $this->_like_escape_chr = '!'; - } - - empty($this->database) OR $this->hostname .= ';dbname='.$this->database; - - $this->trans_enabled = FALSE; - - $this->_random_keyword = ' RND('.time().')'; // database specific random keyword - } - - /** - * Non-persistent database connection - * - * @access private called by the base class - * @return resource - */ - function db_connect() - { - $this->options['PDO::ATTR_ERRMODE'] = PDO::ERRMODE_SILENT; - - return new PDO($this->hostname, $this->username, $this->password, $this->options); - } - - // -------------------------------------------------------------------- - - /** - * Persistent database connection - * - * @access private called by the base class - * @return resource - */ - function db_pconnect() - { - $this->options['PDO::ATTR_ERRMODE'] = PDO::ERRMODE_SILENT; - $this->options['PDO::ATTR_PERSISTENT'] = TRUE; - - return new PDO($this->hostname, $this->username, $this->password, $this->options); - } - - // -------------------------------------------------------------------- - - /** - * Reconnect - * - * Keep / reestablish the db connection if no queries have been - * sent for a length of time exceeding the server's idle timeout - * - * @access public - * @return void - */ - function reconnect() - { - if ($this->db->db_debug) - { - return $this->db->display_error('db_unsuported_feature'); - } - return FALSE; - } - - // -------------------------------------------------------------------- - - /** - * Select the database - * - * @access private called by the base class - * @return resource - */ - function db_select() - { - // Not needed for PDO - return TRUE; - } - - // -------------------------------------------------------------------- - - /** - * Set client character set - * - * @access public - * @param string - * @param string - * @return resource - */ - function db_set_charset($charset, $collation) - { - // @todo - add support if needed - return TRUE; - } - - // -------------------------------------------------------------------- - - /** - * Version number query string - * - * @access public - * @return string - */ - function _version() - { - return $this->conn_id->getAttribute(PDO::ATTR_CLIENT_VERSION); - } - - // -------------------------------------------------------------------- - - /** - * Execute the query - * - * @access private called by the base class - * @param string an SQL query - * @return object - */ - function _execute($sql) - { - $sql = $this->_prep_query($sql); - $result_id = $this->conn_id->prepare($sql); - $result_id->execute(); - - if (is_object($result_id)) - { - if (is_numeric(stripos($sql, 'SELECT'))) - { - $this->affect_rows = count($result_id->fetchAll()); - $result_id->execute(); - } - else - { - $this->affect_rows = $result_id->rowCount(); - } - } - else - { - $this->affect_rows = 0; - } - - return $result_id; - } - - // -------------------------------------------------------------------- - - /** - * Prep the query - * - * If needed, each database adapter can prep the query string - * - * @access private called by execute() - * @param string an SQL query - * @return string - */ - function _prep_query($sql) - { - return $sql; - } - - // -------------------------------------------------------------------- - - /** - * Begin Transaction - * - * @access public - * @return bool - */ - function trans_begin($test_mode = FALSE) - { - if ( ! $this->trans_enabled) - { - return TRUE; - } - - // When transactions are nested we only begin/commit/rollback the outermost ones - if ($this->_trans_depth > 0) - { - return TRUE; - } - - // Reset the transaction failure flag. - // If the $test_mode flag is set to TRUE transactions will be rolled back - // even if the queries produce a successful result. - $this->_trans_failure = (bool) ($test_mode === TRUE); - - return $this->conn_id->beginTransaction(); - } - - // -------------------------------------------------------------------- - - /** - * Commit Transaction - * - * @access public - * @return bool - */ - function trans_commit() - { - if ( ! $this->trans_enabled) - { - return TRUE; - } - - // When transactions are nested we only begin/commit/rollback the outermost ones - if ($this->_trans_depth > 0) - { - return TRUE; - } - - $ret = $this->conn->commit(); - return $ret; - } - - // -------------------------------------------------------------------- - - /** - * Rollback Transaction - * - * @access public - * @return bool - */ - function trans_rollback() - { - if ( ! $this->trans_enabled) - { - return TRUE; - } - - // When transactions are nested we only begin/commit/rollback the outermost ones - if ($this->_trans_depth > 0) - { - return TRUE; - } - - $ret = $this->conn_id->rollBack(); - return $ret; - } - - // -------------------------------------------------------------------- - - /** - * Escape String - * - * @access public - * @param string - * @param bool whether or not the string will be used in a LIKE condition - * @return string - */ - function escape_str($str, $like = FALSE) - { - if (is_array($str)) - { - foreach ($str as $key => $val) - { - $str[$key] = $this->escape_str($val, $like); - } - - return $str; - } - - //Escape the string - $str = $this->conn_id->quote($str); - - //If there are duplicated quotes, trim them away - if (strpos($str, "'") === 0) - { - $str = substr($str, 1, -1); - } - - // escape LIKE condition wildcards - if ($like === TRUE) - { - $str = str_replace( array('%', '_', $this->_like_escape_chr), - array($this->_like_escape_chr.'%', $this->_like_escape_chr.'_', $this->_like_escape_chr.$this->_like_escape_chr), - $str); - } - - return $str; - } - - // -------------------------------------------------------------------- - - /** - * Affected Rows - * - * @access public - * @return integer - */ - function affected_rows() - { - return $this->affect_rows; - } - - // -------------------------------------------------------------------- - - /** - * Insert ID - * - * @access public - * @return integer - */ - function insert_id($name=NULL) - { - //Convenience method for postgres insertid - if (strpos($this->hostname, 'pgsql') !== FALSE) - { - $v = $this->_version(); - - $table = func_num_args() > 0 ? func_get_arg(0) : NULL; - - if ($table == NULL && $v >= '8.1') - { - $sql='SELECT LASTVAL() as ins_id'; - } - $query = $this->query($sql); - $row = $query->row(); - return $row->ins_id; - } - else - { - return $this->conn_id->lastInsertId($name); - } - } - - // -------------------------------------------------------------------- - - /** - * "Count All" query - * - * Generates a platform-specific query string that counts all records in - * the specified database - * - * @access public - * @param string - * @return string - */ - function count_all($table = '') - { - if ($table == '') - { - return 0; - } - - $query = $this->query($this->_count_string . $this->_protect_identifiers('numrows') . " FROM " . $this->_protect_identifiers($table, TRUE, NULL, FALSE)); - - if ($query->num_rows() == 0) - { - return 0; - } - - $row = $query->row(); - $this->_reset_select(); - return (int) $row->numrows; - } - - // -------------------------------------------------------------------- - - /** - * Show table query - * - * Generates a platform-specific query string so that the table names can be fetched - * - * @access private - * @param boolean - * @return string - */ - function _list_tables($prefix_limit = FALSE) - { - $sql = "SHOW TABLES FROM `".$this->database."`"; - - if ($prefix_limit !== FALSE AND $this->dbprefix != '') - { - //$sql .= " LIKE '".$this->escape_like_str($this->dbprefix)."%' ".sprintf($this->_like_escape_str, $this->_like_escape_chr); - return FALSE; // not currently supported - } - - return $sql; - } - - // -------------------------------------------------------------------- - - /** - * Show column query - * - * Generates a platform-specific query string so that the column names can be fetched - * - * @access public - * @param string the table name - * @return string - */ - function _list_columns($table = '') - { - return "SHOW COLUMNS FROM ".$table; - } - - // -------------------------------------------------------------------- - - /** - * Field data query - * - * Generates a platform-specific query so that the column data can be retrieved - * - * @access public - * @param string the table name - * @return object - */ - function _field_data($table) - { - return "SELECT TOP 1 FROM ".$table; - } - - // -------------------------------------------------------------------- - - /** - * The error message string - * - * @access private - * @return string - */ - function _error_message() - { - $error_array = $this->conn_id->errorInfo(); - return $error_array[2]; - } - - // -------------------------------------------------------------------- - - /** - * The error message number - * - * @access private - * @return integer - */ - function _error_number() - { - return $this->conn_id->errorCode(); - } - - // -------------------------------------------------------------------- - - /** - * Escape the SQL Identifiers - * - * This function escapes column and table names - * - * @access private - * @param string - * @return string - */ - function _escape_identifiers($item) - { - if ($this->_escape_char == '') - { - return $item; - } - - foreach ($this->_reserved_identifiers as $id) - { - if (strpos($item, '.'.$id) !== FALSE) - { - $str = $this->_escape_char. str_replace('.', $this->_escape_char.'.', $item); - - // remove duplicates if the user already included the escape - return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str); - } - } - - if (strpos($item, '.') !== FALSE) - { - $str = $this->_escape_char.str_replace('.', $this->_escape_char.'.'.$this->_escape_char, $item).$this->_escape_char; - - } - else - { - $str = $this->_escape_char.$item.$this->_escape_char; - } - - // remove duplicates if the user already included the escape - return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str); - } - - // -------------------------------------------------------------------- - - /** - * From Tables - * - * This function implicitly groups FROM tables so there is no confusion - * about operator precedence in harmony with SQL standards - * - * @access public - * @param type - * @return type - */ - function _from_tables($tables) - { - if ( ! is_array($tables)) - { - $tables = array($tables); - } - - return (count($tables) == 1) ? $tables[0] : '('.implode(', ', $tables).')'; - } - - // -------------------------------------------------------------------- - - /** - * Insert statement - * - * Generates a platform-specific insert string from the supplied data - * - * @access public - * @param string the table name - * @param array the insert keys - * @param array the insert values - * @return string - */ - function _insert($table, $keys, $values) - { - return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")"; - } - - // -------------------------------------------------------------------- - - /** - * Insert_batch statement - * - * Generates a platform-specific insert string from the supplied data - * - * @access public - * @param string the table name - * @param array the insert keys - * @param array the insert values - * @return string - */ - function _insert_batch($table, $keys, $values) - { - return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES ".implode(', ', $values); - } - - // -------------------------------------------------------------------- - - /** - * Update statement - * - * Generates a platform-specific update string from the supplied data - * - * @access public - * @param string the table name - * @param array the update data - * @param array the where clause - * @param array the orderby clause - * @param array the limit clause - * @return string - */ - function _update($table, $values, $where, $orderby = array(), $limit = FALSE) - { - foreach ($values as $key => $val) - { - $valstr[] = $key." = ".$val; - } - - $limit = ( ! $limit) ? '' : ' LIMIT '.$limit; - - $orderby = (count($orderby) >= 1)?' ORDER BY '.implode(", ", $orderby):''; - - $sql = "UPDATE ".$table." SET ".implode(', ', $valstr); - - $sql .= ($where != '' AND count($where) >=1) ? " WHERE ".implode(" ", $where) : ''; - - $sql .= $orderby.$limit; - - return $sql; - } - - // -------------------------------------------------------------------- - - /** - * Update_Batch statement - * - * Generates a platform-specific batch update string from the supplied data - * - * @access public - * @param string the table name - * @param array the update data - * @param array the where clause - * @return string - */ - function _update_batch($table, $values, $index, $where = NULL) - { - $ids = array(); - $where = ($where != '' AND count($where) >=1) ? implode(" ", $where).' AND ' : ''; - - foreach ($values as $key => $val) - { - $ids[] = $val[$index]; - - foreach (array_keys($val) as $field) - { - if ($field != $index) - { - $final[$field][] = 'WHEN '.$index.' = '.$val[$index].' THEN '.$val[$field]; - } - } - } - - $sql = "UPDATE ".$table." SET "; - $cases = ''; - - foreach ($final as $k => $v) - { - $cases .= $k.' = CASE '."\n"; - foreach ($v as $row) - { - $cases .= $row."\n"; - } - - $cases .= 'ELSE '.$k.' END, '; - } - - $sql .= substr($cases, 0, -2); - - $sql .= ' WHERE '.$where.$index.' IN ('.implode(',', $ids).')'; - - return $sql; - } - - - // -------------------------------------------------------------------- - - /** - * Truncate statement - * - * Generates a platform-specific truncate string from the supplied data - * If the database does not support the truncate() command - * This function maps to "DELETE FROM table" - * - * @access public - * @param string the table name - * @return string - */ - function _truncate($table) - { - return $this->_delete($table); - } - - // -------------------------------------------------------------------- - - /** - * Delete statement - * - * Generates a platform-specific delete string from the supplied data - * - * @access public - * @param string the table name - * @param array the where clause - * @param string the limit clause - * @return string - */ - function _delete($table, $where = array(), $like = array(), $limit = FALSE) - { - $conditions = ''; - - if (count($where) > 0 OR count($like) > 0) - { - $conditions = "\nWHERE "; - $conditions .= implode("\n", $this->ar_where); - - if (count($where) > 0 && count($like) > 0) - { - $conditions .= " AND "; - } - $conditions .= implode("\n", $like); - } - - $limit = ( ! $limit) ? '' : ' LIMIT '.$limit; - - return "DELETE FROM ".$table.$conditions.$limit; - } - - // -------------------------------------------------------------------- - - /** - * Limit string - * - * Generates a platform-specific LIMIT clause - * - * @access public - * @param string the sql query string - * @param integer the number of rows to limit the query to - * @param integer the offset value - * @return string - */ - function _limit($sql, $limit, $offset) - { - if (strpos($this->hostname, 'cubrid') !== FALSE || strpos($this->hostname, 'sqlite') !== FALSE) - { - if ($offset == 0) - { - $offset = ''; - } - else - { - $offset .= ", "; - } - - return $sql."LIMIT ".$offset.$limit; - } - else - { - $sql .= "LIMIT ".$limit; - - if ($offset > 0) - { - $sql .= " OFFSET ".$offset; - } - - return $sql; - } - } - - // -------------------------------------------------------------------- - - /** - * Close DB Connection - * - * @access public - * @param resource - * @return void - */ - function _close($conn_id) - { - $this->conn_id = null; - } - - -} - - - -/* End of file pdo_driver.php */ -/* Location: ./system/database/drivers/pdo/pdo_driver.php */ \ No newline at end of file diff --git a/system/database/drivers/pdo/pdo_forge.php b/system/database/drivers/pdo/pdo_forge.php deleted file mode 100644 index 9a782208..00000000 --- a/system/database/drivers/pdo/pdo_forge.php +++ /dev/null @@ -1,266 +0,0 @@ -db->db_debug) - { - return $this->db->display_error('db_unsuported_feature'); - } - return FALSE; - } - - // -------------------------------------------------------------------- - - /** - * Drop database - * - * @access private - * @param string the database name - * @return bool - */ - function _drop_database($name) - { - // PDO has no "drop database" command since it's - // designed to connect to an existing database - if ($this->db->db_debug) - { - return $this->db->display_error('db_unsuported_feature'); - } - return FALSE; - } - - // -------------------------------------------------------------------- - - /** - * Create Table - * - * @access private - * @param string the table name - * @param array the fields - * @param mixed primary key(s) - * @param mixed key(s) - * @param boolean should 'IF NOT EXISTS' be added to the SQL - * @return bool - */ - function _create_table($table, $fields, $primary_keys, $keys, $if_not_exists) - { - $sql = 'CREATE TABLE '; - - if ($if_not_exists === TRUE) - { - $sql .= 'IF NOT EXISTS '; - } - - $sql .= $this->db->_escape_identifiers($table)." ("; - $current_field_count = 0; - - foreach ($fields as $field=>$attributes) - { - // Numeric field names aren't allowed in databases, so if the key is - // numeric, we know it was assigned by PHP and the developer manually - // entered the field information, so we'll simply add it to the list - if (is_numeric($field)) - { - $sql .= "\n\t$attributes"; - } - else - { - $attributes = array_change_key_case($attributes, CASE_UPPER); - - $sql .= "\n\t".$this->db->_protect_identifiers($field); - - $sql .= ' '.$attributes['TYPE']; - - if (array_key_exists('CONSTRAINT', $attributes)) - { - $sql .= '('.$attributes['CONSTRAINT'].')'; - } - - if (array_key_exists('UNSIGNED', $attributes) && $attributes['UNSIGNED'] === TRUE) - { - $sql .= ' UNSIGNED'; - } - - if (array_key_exists('DEFAULT', $attributes)) - { - $sql .= ' DEFAULT \''.$attributes['DEFAULT'].'\''; - } - - if (array_key_exists('NULL', $attributes) && $attributes['NULL'] === TRUE) - { - $sql .= ' NULL'; - } - else - { - $sql .= ' NOT NULL'; - } - - if (array_key_exists('AUTO_INCREMENT', $attributes) && $attributes['AUTO_INCREMENT'] === TRUE) - { - $sql .= ' AUTO_INCREMENT'; - } - } - - // don't add a comma on the end of the last field - if (++$current_field_count < count($fields)) - { - $sql .= ','; - } - } - - if (count($primary_keys) > 0) - { - $primary_keys = $this->db->_protect_identifiers($primary_keys); - $sql .= ",\n\tPRIMARY KEY (" . implode(', ', $primary_keys) . ")"; - } - - if (is_array($keys) && count($keys) > 0) - { - foreach ($keys as $key) - { - if (is_array($key)) - { - $key = $this->db->_protect_identifiers($key); - } - else - { - $key = array($this->db->_protect_identifiers($key)); - } - - $sql .= ",\n\tFOREIGN KEY (" . implode(', ', $key) . ")"; - } - } - - $sql .= "\n)"; - - return $sql; - } - - // -------------------------------------------------------------------- - - /** - * Drop Table - * - * @access private - * @return bool - */ - function _drop_table($table) - { - // Not a supported PDO feature - if ($this->db->db_debug) - { - return $this->db->display_error('db_unsuported_feature'); - } - return FALSE; - } - - // -------------------------------------------------------------------- - - /** - * Alter table query - * - * Generates a platform-specific query so that a table can be altered - * Called by add_column(), drop_column(), and column_alter(), - * - * @access private - * @param string the ALTER type (ADD, DROP, CHANGE) - * @param string the column name - * @param string the table name - * @param string the column definition - * @param string the default value - * @param boolean should 'NOT NULL' be added - * @param string the field after which we should add the new field - * @return object - */ - function _alter_table($alter_type, $table, $column_name, $column_definition = '', $default_value = '', $null = '', $after_field = '') - { - $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table)." $alter_type ".$this->db->_protect_identifiers($column_name); - - // DROP has everything it needs now. - if ($alter_type == 'DROP') - { - return $sql; - } - - $sql .= " $column_definition"; - - if ($default_value != '') - { - $sql .= " DEFAULT \"$default_value\""; - } - - if ($null === NULL) - { - $sql .= ' NULL'; - } - else - { - $sql .= ' NOT NULL'; - } - - if ($after_field != '') - { - $sql .= ' AFTER ' . $this->db->_protect_identifiers($after_field); - } - - return $sql; - - } - - - // -------------------------------------------------------------------- - - /** - * Rename a table - * - * Generates a platform-specific query so that a table can be renamed - * - * @access private - * @param string the old table name - * @param string the new table name - * @return string - */ - function _rename_table($table_name, $new_table_name) - { - $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table_name)." RENAME TO ".$this->db->_protect_identifiers($new_table_name); - return $sql; - } - - -} - -/* End of file pdo_forge.php */ -/* Location: ./system/database/drivers/pdo/pdo_forge.php */ \ No newline at end of file diff --git a/system/database/drivers/pdo/pdo_result.php b/system/database/drivers/pdo/pdo_result.php deleted file mode 100644 index 44fdd6dc..00000000 --- a/system/database/drivers/pdo/pdo_result.php +++ /dev/null @@ -1,183 +0,0 @@ -num_rows)) - { - return $this->num_rows; - } - elseif (($this->num_rows = $this->result_id->rowCount()) > 0) - { - return $this->num_rows; - } - - $this->num_rows = count($this->result_id->fetchAll()); - $this->result_id->execute(); - return $this->num_rows; - } - - // -------------------------------------------------------------------- - - /** - * Number of fields in the result set - * - * @access public - * @return integer - */ - function num_fields() - { - return $this->result_id->columnCount(); - } - - // -------------------------------------------------------------------- - - /** - * Fetch Field Names - * - * Generates an array of column names - * - * @access public - * @return array - */ - function list_fields() - { - if ($this->db->db_debug) - { - return $this->db->display_error('db_unsuported_feature'); - } - return FALSE; - } - - // -------------------------------------------------------------------- - - /** - * Field data - * - * Generates an array of objects containing field meta-data - * - * @access public - * @return array - */ - function field_data() - { - $data = array(); - - try - { - for($i = 0; $i < $this->num_fields(); $i++) - { - $data[] = $this->result_id->getColumnMeta($i); - } - - return $data; - } - catch (Exception $e) - { - if ($this->db->db_debug) - { - return $this->db->display_error('db_unsuported_feature'); - } - return FALSE; - } - } - - // -------------------------------------------------------------------- - - /** - * Free the result - * - * @return null - */ - function free_result() - { - if (is_object($this->result_id)) - { - $this->result_id = FALSE; - } - } - - // -------------------------------------------------------------------- - - /** - * Data Seek - * - * Moves the internal pointer to the desired offset. We call - * this internally before fetching results to make sure the - * result set starts at zero - * - * @access private - * @return array - */ - function _data_seek($n = 0) - { - return FALSE; - } - - // -------------------------------------------------------------------- - - /** - * Result - associative array - * - * Returns the result set as an array - * - * @access private - * @return array - */ - function _fetch_assoc() - { - return $this->result_id->fetch(PDO::FETCH_ASSOC); - } - - // -------------------------------------------------------------------- - - /** - * Result - object - * - * Returns the result set as an object - * - * @access private - * @return object - */ - function _fetch_object() - { - return $this->result_id->fetchObject(); - } - -} - - -/* End of file pdo_result.php */ -/* Location: ./system/database/drivers/pdo/pdo_result.php */ \ No newline at end of file diff --git a/system/database/drivers/pdo/pdo_utility.php b/system/database/drivers/pdo/pdo_utility.php deleted file mode 100644 index 88ce033a..00000000 --- a/system/database/drivers/pdo/pdo_utility.php +++ /dev/null @@ -1,103 +0,0 @@ -db->db_debug) - { - return $this->db->display_error('db_unsuported_feature'); - } - return FALSE; - } - - // -------------------------------------------------------------------- - - /** - * Optimize table query - * - * Generates a platform-specific query so that a table can be optimized - * - * @access private - * @param string the table name - * @return object - */ - function _optimize_table($table) - { - // Not a supported PDO feature - if ($this->db->db_debug) - { - return $this->db->display_error('db_unsuported_feature'); - } - return FALSE; - } - - // -------------------------------------------------------------------- - - /** - * Repair table query - * - * Generates a platform-specific query so that a table can be repaired - * - * @access private - * @param string the table name - * @return object - */ - function _repair_table($table) - { - // Not a supported PDO feature - if ($this->db->db_debug) - { - return $this->db->display_error('db_unsuported_feature'); - } - return FALSE; - } - - // -------------------------------------------------------------------- - - /** - * PDO Export - * - * @access private - * @param array Preferences - * @return mixed - */ - function _backup($params = array()) - { - // Currently unsupported - return $this->db->display_error('db_unsuported_feature'); - } - -} - -/* End of file pdo_utility.php */ -/* Location: ./system/database/drivers/pdo/pdo_utility.php */ \ No newline at end of file diff --git a/system/database/drivers/postgre/index.html b/system/database/drivers/postgre/index.html deleted file mode 100644 index c942a79c..00000000 --- a/system/database/drivers/postgre/index.html +++ /dev/null @@ -1,10 +0,0 @@ - - - 403 Forbidden - - - -

Directory access is forbidden.

- - - \ No newline at end of file diff --git a/system/database/drivers/postgre/postgre_driver.php b/system/database/drivers/postgre/postgre_driver.php deleted file mode 100644 index 5367f975..00000000 --- a/system/database/drivers/postgre/postgre_driver.php +++ /dev/null @@ -1,703 +0,0 @@ - 'host', - 'port' => 'port', - 'database' => 'dbname', - 'username' => 'user', - 'password' => 'password' - ); - - $connect_string = ""; - foreach ($components as $key => $val) - { - if (isset($this->$key) && $this->$key != '') - { - $connect_string .= " $val=".$this->$key; - } - } - return trim($connect_string); - } - - // -------------------------------------------------------------------- - - /** - * Non-persistent database connection - * - * @access private called by the base class - * @return resource - */ - function db_connect() - { - return @pg_connect($this->_connect_string()); - } - - // -------------------------------------------------------------------- - - /** - * Persistent database connection - * - * @access private called by the base class - * @return resource - */ - function db_pconnect() - { - return @pg_pconnect($this->_connect_string()); - } - - // -------------------------------------------------------------------- - - /** - * Reconnect - * - * Keep / reestablish the db connection if no queries have been - * sent for a length of time exceeding the server's idle timeout - * - * @access public - * @return void - */ - function reconnect() - { - if (pg_ping($this->conn_id) === FALSE) - { - $this->conn_id = FALSE; - } - } - - // -------------------------------------------------------------------- - - /** - * Select the database - * - * @access private called by the base class - * @return resource - */ - function db_select() - { - // Not needed for Postgre so we'll return TRUE - return TRUE; - } - - // -------------------------------------------------------------------- - - /** - * Set client character set - * - * @access public - * @param string - * @param string - * @return resource - */ - function db_set_charset($charset, $collation) - { - // @todo - add support if needed - return TRUE; - } - - // -------------------------------------------------------------------- - - /** - * Version number query string - * - * @access public - * @return string - */ - function _version() - { - return "SELECT version() AS ver"; - } - - // -------------------------------------------------------------------- - - /** - * Execute the query - * - * @access private called by the base class - * @param string an SQL query - * @return resource - */ - function _execute($sql) - { - $sql = $this->_prep_query($sql); - return @pg_query($this->conn_id, $sql); - } - - // -------------------------------------------------------------------- - - /** - * Prep the query - * - * If needed, each database adapter can prep the query string - * - * @access private called by execute() - * @param string an SQL query - * @return string - */ - function _prep_query($sql) - { - return $sql; - } - - // -------------------------------------------------------------------- - - /** - * Begin Transaction - * - * @access public - * @return bool - */ - function trans_begin($test_mode = FALSE) - { - if ( ! $this->trans_enabled) - { - return TRUE; - } - - // When transactions are nested we only begin/commit/rollback the outermost ones - if ($this->_trans_depth > 0) - { - return TRUE; - } - - // Reset the transaction failure flag. - // If the $test_mode flag is set to TRUE transactions will be rolled back - // even if the queries produce a successful result. - $this->_trans_failure = ($test_mode === TRUE) ? TRUE : FALSE; - - return @pg_exec($this->conn_id, "begin"); - } - - // -------------------------------------------------------------------- - - /** - * Commit Transaction - * - * @access public - * @return bool - */ - function trans_commit() - { - if ( ! $this->trans_enabled) - { - return TRUE; - } - - // When transactions are nested we only begin/commit/rollback the outermost ones - if ($this->_trans_depth > 0) - { - return TRUE; - } - - return @pg_exec($this->conn_id, "commit"); - } - - // -------------------------------------------------------------------- - - /** - * Rollback Transaction - * - * @access public - * @return bool - */ - function trans_rollback() - { - if ( ! $this->trans_enabled) - { - return TRUE; - } - - // When transactions are nested we only begin/commit/rollback the outermost ones - if ($this->_trans_depth > 0) - { - return TRUE; - } - - return @pg_exec($this->conn_id, "rollback"); - } - - // -------------------------------------------------------------------- - - /** - * Escape String - * - * @access public - * @param string - * @param bool whether or not the string will be used in a LIKE condition - * @return string - */ - function escape_str($str, $like = FALSE) - { - if (is_array($str)) - { - foreach ($str as $key => $val) - { - $str[$key] = $this->escape_str($val, $like); - } - - return $str; - } - - $str = pg_escape_string($str); - - // escape LIKE condition wildcards - if ($like === TRUE) - { - $str = str_replace( array('%', '_', $this->_like_escape_chr), - array($this->_like_escape_chr.'%', $this->_like_escape_chr.'_', $this->_like_escape_chr.$this->_like_escape_chr), - $str); - } - - return $str; - } - - // -------------------------------------------------------------------- - - /** - * Affected Rows - * - * @access public - * @return integer - */ - function affected_rows() - { - return @pg_affected_rows($this->result_id); - } - - // -------------------------------------------------------------------- - - /** - * Insert ID - * - * @access public - * @return integer - */ - function insert_id() - { - $v = $this->_version(); - $v = $v['server']; - - $table = func_num_args() > 0 ? func_get_arg(0) : NULL; - $column = func_num_args() > 1 ? func_get_arg(1) : NULL; - - if ($table == NULL && $v >= '8.1') - { - $sql='SELECT LASTVAL() as ins_id'; - } - elseif ($table != NULL && $column != NULL && $v >= '8.0') - { - $sql = sprintf("SELECT pg_get_serial_sequence('%s','%s') as seq", $table, $column); - $query = $this->query($sql); - $row = $query->row(); - $sql = sprintf("SELECT CURRVAL('%s') as ins_id", $row->seq); - } - elseif ($table != NULL) - { - // seq_name passed in table parameter - $sql = sprintf("SELECT CURRVAL('%s') as ins_id", $table); - } - else - { - return pg_last_oid($this->result_id); - } - $query = $this->query($sql); - $row = $query->row(); - return $row->ins_id; - } - - // -------------------------------------------------------------------- - - /** - * "Count All" query - * - * Generates a platform-specific query string that counts all records in - * the specified database - * - * @access public - * @param string - * @return string - */ - function count_all($table = '') - { - if ($table == '') - { - return 0; - } - - $query = $this->query($this->_count_string . $this->_protect_identifiers('numrows') . " FROM " . $this->_protect_identifiers($table, TRUE, NULL, FALSE)); - - if ($query->num_rows() == 0) - { - return 0; - } - - $row = $query->row(); - $this->_reset_select(); - return (int) $row->numrows; - } - - // -------------------------------------------------------------------- - - /** - * Show table query - * - * Generates a platform-specific query string so that the table names can be fetched - * - * @access private - * @param boolean - * @return string - */ - function _list_tables($prefix_limit = FALSE) - { - $sql = "SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'"; - - if ($prefix_limit !== FALSE AND $this->dbprefix != '') - { - $sql .= " AND table_name LIKE '".$this->escape_like_str($this->dbprefix)."%' ".sprintf($this->_like_escape_str, $this->_like_escape_chr); - } - - return $sql; - } - - // -------------------------------------------------------------------- - - /** - * Show column query - * - * Generates a platform-specific query string so that the column names can be fetched - * - * @access public - * @param string the table name - * @return string - */ - function _list_columns($table = '') - { - return "SELECT column_name FROM information_schema.columns WHERE table_name ='".$table."'"; - } - - // -------------------------------------------------------------------- - - /** - * Field data query - * - * Generates a platform-specific query so that the column data can be retrieved - * - * @access public - * @param string the table name - * @return object - */ - function _field_data($table) - { - return "SELECT * FROM ".$table." LIMIT 1"; - } - - // -------------------------------------------------------------------- - - /** - * The error message string - * - * @access private - * @return string - */ - function _error_message() - { - return pg_last_error($this->conn_id); - } - - // -------------------------------------------------------------------- - - /** - * The error message number - * - * @access private - * @return integer - */ - function _error_number() - { - return ''; - } - - // -------------------------------------------------------------------- - - /** - * Escape the SQL Identifiers - * - * This function escapes column and table names - * - * @access private - * @param string - * @return string - */ - function _escape_identifiers($item) - { - if ($this->_escape_char == '') - { - return $item; - } - - foreach ($this->_reserved_identifiers as $id) - { - if (strpos($item, '.'.$id) !== FALSE) - { - $str = $this->_escape_char. str_replace('.', $this->_escape_char.'.', $item); - - // remove duplicates if the user already included the escape - return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str); - } - } - - if (strpos($item, '.') !== FALSE) - { - $str = $this->_escape_char.str_replace('.', $this->_escape_char.'.'.$this->_escape_char, $item).$this->_escape_char; - } - else - { - $str = $this->_escape_char.$item.$this->_escape_char; - } - - // remove duplicates if the user already included the escape - return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str); - } - - // -------------------------------------------------------------------- - - /** - * From Tables - * - * This function implicitly groups FROM tables so there is no confusion - * about operator precedence in harmony with SQL standards - * - * @access public - * @param type - * @return type - */ - function _from_tables($tables) - { - if ( ! is_array($tables)) - { - $tables = array($tables); - } - - return implode(', ', $tables); - } - - // -------------------------------------------------------------------- - - /** - * Insert statement - * - * Generates a platform-specific insert string from the supplied data - * - * @access public - * @param string the table name - * @param array the insert keys - * @param array the insert values - * @return string - */ - function _insert($table, $keys, $values) - { - return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")"; - } - - // -------------------------------------------------------------------- - - /** - * Insert_batch statement - * - * Generates a platform-specific insert string from the supplied data - * - * @access public - * @param string the table name - * @param array the insert keys - * @param array the insert values - * @return string - */ - function _insert_batch($table, $keys, $values) - { - return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES ".implode(', ', $values); - } - - // -------------------------------------------------------------------- - - /** - * Update statement - * - * Generates a platform-specific update string from the supplied data - * - * @access public - * @param string the table name - * @param array the update data - * @param array the where clause - * @param array the orderby clause - * @param array the limit clause - * @return string - */ - function _update($table, $values, $where, $orderby = array(), $limit = FALSE) - { - foreach ($values as $key => $val) - { - $valstr[] = $key." = ".$val; - } - - $limit = ( ! $limit) ? '' : ' LIMIT '.$limit; - - $orderby = (count($orderby) >= 1)?' ORDER BY '.implode(", ", $orderby):''; - - $sql = "UPDATE ".$table." SET ".implode(', ', $valstr); - - $sql .= ($where != '' AND count($where) >=1) ? " WHERE ".implode(" ", $where) : ''; - - $sql .= $orderby.$limit; - - return $sql; - } - - // -------------------------------------------------------------------- - - /** - * Truncate statement - * - * Generates a platform-specific truncate string from the supplied data - * If the database does not support the truncate() command - * This function maps to "DELETE FROM table" - * - * @access public - * @param string the table name - * @return string - */ - function _truncate($table) - { - return "TRUNCATE ".$table; - } - - // -------------------------------------------------------------------- - - /** - * Delete statement - * - * Generates a platform-specific delete string from the supplied data - * - * @access public - * @param string the table name - * @param array the where clause - * @param string the limit clause - * @return string - */ - function _delete($table, $where = array(), $like = array(), $limit = FALSE) - { - $conditions = ''; - - if (count($where) > 0 OR count($like) > 0) - { - $conditions = "\nWHERE "; - $conditions .= implode("\n", $this->ar_where); - - if (count($where) > 0 && count($like) > 0) - { - $conditions .= " AND "; - } - $conditions .= implode("\n", $like); - } - - $limit = ( ! $limit) ? '' : ' LIMIT '.$limit; - - return "DELETE FROM ".$table.$conditions.$limit; - } - - // -------------------------------------------------------------------- - /** - * Limit string - * - * Generates a platform-specific LIMIT clause - * - * @access public - * @param string the sql query string - * @param integer the number of rows to limit the query to - * @param integer the offset value - * @return string - */ - function _limit($sql, $limit, $offset) - { - $sql .= "LIMIT ".$limit; - - if ($offset > 0) - { - $sql .= " OFFSET ".$offset; - } - - return $sql; - } - - // -------------------------------------------------------------------- - - /** - * Close DB Connection - * - * @access public - * @param resource - * @return void - */ - function _close($conn_id) - { - @pg_close($conn_id); - } - - -} - - -/* End of file postgre_driver.php */ -/* Location: ./system/database/drivers/postgre/postgre_driver.php */ \ No newline at end of file diff --git a/system/database/drivers/postgre/postgre_forge.php b/system/database/drivers/postgre/postgre_forge.php deleted file mode 100644 index 91a1c686..00000000 --- a/system/database/drivers/postgre/postgre_forge.php +++ /dev/null @@ -1,299 +0,0 @@ -db->table_exists($table)) - { - return "SELECT * FROM $table"; // Needs to return innocous but valid SQL statement - } - } - - $sql .= $this->db->_escape_identifiers($table)." ("; - $current_field_count = 0; - - foreach ($fields as $field=>$attributes) - { - // Numeric field names aren't allowed in databases, so if the key is - // numeric, we know it was assigned by PHP and the developer manually - // entered the field information, so we'll simply add it to the list - if (is_numeric($field)) - { - $sql .= "\n\t$attributes"; - } - else - { - $attributes = array_change_key_case($attributes, CASE_UPPER); - - $sql .= "\n\t".$this->db->_protect_identifiers($field); - - $is_unsigned = (array_key_exists('UNSIGNED', $attributes) && $attributes['UNSIGNED'] === TRUE); - - // Convert datatypes to be PostgreSQL-compatible - switch (strtoupper($attributes['TYPE'])) - { - case 'TINYINT': - $attributes['TYPE'] = 'SMALLINT'; - break; - case 'SMALLINT': - $attributes['TYPE'] = ($is_unsigned) ? 'INTEGER' : 'SMALLINT'; - break; - case 'MEDIUMINT': - $attributes['TYPE'] = 'INTEGER'; - break; - case 'INT': - $attributes['TYPE'] = ($is_unsigned) ? 'BIGINT' : 'INTEGER'; - break; - case 'BIGINT': - $attributes['TYPE'] = ($is_unsigned) ? 'NUMERIC' : 'BIGINT'; - break; - case 'DOUBLE': - $attributes['TYPE'] = 'DOUBLE PRECISION'; - break; - case 'DATETIME': - $attributes['TYPE'] = 'TIMESTAMP'; - break; - case 'LONGTEXT': - $attributes['TYPE'] = 'TEXT'; - break; - case 'BLOB': - $attributes['TYPE'] = 'BYTEA'; - break; - } - - // If this is an auto-incrementing primary key, use the serial data type instead - if (in_array($field, $primary_keys) && array_key_exists('AUTO_INCREMENT', $attributes) - && $attributes['AUTO_INCREMENT'] === TRUE) - { - $sql .= ' SERIAL'; - } - else - { - $sql .= ' '.$attributes['TYPE']; - } - - // Modified to prevent constraints with integer data types - if (array_key_exists('CONSTRAINT', $attributes) && strpos($attributes['TYPE'], 'INT') === false) - { - $sql .= '('.$attributes['CONSTRAINT'].')'; - } - - if (array_key_exists('DEFAULT', $attributes)) - { - $sql .= ' DEFAULT \''.$attributes['DEFAULT'].'\''; - } - - if (array_key_exists('NULL', $attributes) && $attributes['NULL'] === TRUE) - { - $sql .= ' NULL'; - } - else - { - $sql .= ' NOT NULL'; - } - - // Added new attribute to create unqite fields. Also works with MySQL - if (array_key_exists('UNIQUE', $attributes) && $attributes['UNIQUE'] === TRUE) - { - $sql .= ' UNIQUE'; - } - } - - // don't add a comma on the end of the last field - if (++$current_field_count < count($fields)) - { - $sql .= ','; - } - } - - if (count($primary_keys) > 0) - { - // Something seems to break when passing an array to _protect_identifiers() - foreach ($primary_keys as $index => $key) - { - $primary_keys[$index] = $this->db->_protect_identifiers($key); - } - - $sql .= ",\n\tPRIMARY KEY (" . implode(', ', $primary_keys) . ")"; - } - - $sql .= "\n);"; - - if (is_array($keys) && count($keys) > 0) - { - foreach ($keys as $key) - { - if (is_array($key)) - { - $key = $this->db->_protect_identifiers($key); - } - else - { - $key = array($this->db->_protect_identifiers($key)); - } - - foreach ($key as $field) - { - $sql .= "CREATE INDEX " . $table . "_" . str_replace(array('"', "'"), '', $field) . "_index ON $table ($field); "; - } - } - } - - return $sql; - } - - // -------------------------------------------------------------------- - - /** - * Drop Table - * - * @access private - * @return bool - */ - function _drop_table($table) - { - return "DROP TABLE IF EXISTS ".$this->db->_escape_identifiers($table)." CASCADE"; - } - - // -------------------------------------------------------------------- - - /** - * Alter table query - * - * Generates a platform-specific query so that a table can be altered - * Called by add_column(), drop_column(), and column_alter(), - * - * @access private - * @param string the ALTER type (ADD, DROP, CHANGE) - * @param string the column name - * @param string the table name - * @param string the column definition - * @param string the default value - * @param boolean should 'NOT NULL' be added - * @param string the field after which we should add the new field - * @return object - */ - function _alter_table($alter_type, $table, $column_name, $column_definition = '', $default_value = '', $null = '', $after_field = '') - { - $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table)." $alter_type ".$this->db->_protect_identifiers($column_name); - - // DROP has everything it needs now. - if ($alter_type == 'DROP') - { - return $sql; - } - - $sql .= " $column_definition"; - - if ($default_value != '') - { - $sql .= " DEFAULT \"$default_value\""; - } - - if ($null === NULL) - { - $sql .= ' NULL'; - } - else - { - $sql .= ' NOT NULL'; - } - - if ($after_field != '') - { - $sql .= ' AFTER ' . $this->db->_protect_identifiers($after_field); - } - - return $sql; - - } - - // -------------------------------------------------------------------- - - /** - * Rename a table - * - * Generates a platform-specific query so that a table can be renamed - * - * @access private - * @param string the old table name - * @param string the new table name - * @return string - */ - function _rename_table($table_name, $new_table_name) - { - $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table_name)." RENAME TO ".$this->db->_protect_identifiers($new_table_name); - return $sql; - } - - -} - -/* End of file postgre_forge.php */ -/* Location: ./system/database/drivers/postgre/postgre_forge.php */ \ No newline at end of file diff --git a/system/database/drivers/postgre/postgre_result.php b/system/database/drivers/postgre/postgre_result.php deleted file mode 100644 index e9a1d160..00000000 --- a/system/database/drivers/postgre/postgre_result.php +++ /dev/null @@ -1,169 +0,0 @@ -result_id); - } - - // -------------------------------------------------------------------- - - /** - * Number of fields in the result set - * - * @access public - * @return integer - */ - function num_fields() - { - return @pg_num_fields($this->result_id); - } - - // -------------------------------------------------------------------- - - /** - * Fetch Field Names - * - * Generates an array of column names - * - * @access public - * @return array - */ - function list_fields() - { - $field_names = array(); - for ($i = 0; $i < $this->num_fields(); $i++) - { - $field_names[] = pg_field_name($this->result_id, $i); - } - - return $field_names; - } - - // -------------------------------------------------------------------- - - /** - * Field data - * - * Generates an array of objects containing field meta-data - * - * @access public - * @return array - */ - function field_data() - { - $retval = array(); - for ($i = 0; $i < $this->num_fields(); $i++) - { - $F = new stdClass(); - $F->name = pg_field_name($this->result_id, $i); - $F->type = pg_field_type($this->result_id, $i); - $F->max_length = pg_field_size($this->result_id, $i); - $F->primary_key = 0; - $F->default = ''; - - $retval[] = $F; - } - - return $retval; - } - - // -------------------------------------------------------------------- - - /** - * Free the result - * - * @return null - */ - function free_result() - { - if (is_resource($this->result_id)) - { - pg_free_result($this->result_id); - $this->result_id = FALSE; - } - } - - // -------------------------------------------------------------------- - - /** - * Data Seek - * - * Moves the internal pointer to the desired offset. We call - * this internally before fetching results to make sure the - * result set starts at zero - * - * @access private - * @return array - */ - function _data_seek($n = 0) - { - return pg_result_seek($this->result_id, $n); - } - - // -------------------------------------------------------------------- - - /** - * Result - associative array - * - * Returns the result set as an array - * - * @access private - * @return array - */ - function _fetch_assoc() - { - return pg_fetch_assoc($this->result_id); - } - - // -------------------------------------------------------------------- - - /** - * Result - object - * - * Returns the result set as an object - * - * @access private - * @return object - */ - function _fetch_object() - { - return pg_fetch_object($this->result_id); - } - -} - - -/* End of file postgre_result.php */ -/* Location: ./system/database/drivers/postgre/postgre_result.php */ \ No newline at end of file diff --git a/system/database/drivers/postgre/postgre_utility.php b/system/database/drivers/postgre/postgre_utility.php deleted file mode 100644 index 741c52ea..00000000 --- a/system/database/drivers/postgre/postgre_utility.php +++ /dev/null @@ -1,88 +0,0 @@ -db->display_error('db_unsuported_feature'); - } -} - - -/* End of file postgre_utility.php */ -/* Location: ./system/database/drivers/postgre/postgre_utility.php */ \ No newline at end of file diff --git a/system/database/drivers/sqlite/index.html b/system/database/drivers/sqlite/index.html deleted file mode 100644 index c942a79c..00000000 --- a/system/database/drivers/sqlite/index.html +++ /dev/null @@ -1,10 +0,0 @@ - - - 403 Forbidden - - - -

Directory access is forbidden.

- - - \ No newline at end of file diff --git a/system/database/drivers/sqlite/sqlite_driver.php b/system/database/drivers/sqlite/sqlite_driver.php deleted file mode 100644 index 0cc898b3..00000000 --- a/system/database/drivers/sqlite/sqlite_driver.php +++ /dev/null @@ -1,658 +0,0 @@ -database, FILE_WRITE_MODE, $error)) - { - log_message('error', $error); - - if ($this->db_debug) - { - $this->display_error($error, '', TRUE); - } - - return FALSE; - } - - return $conn_id; - } - - // -------------------------------------------------------------------- - - /** - * Persistent database connection - * - * @access private called by the base class - * @return resource - */ - function db_pconnect() - { - if ( ! $conn_id = @sqlite_popen($this->database, FILE_WRITE_MODE, $error)) - { - log_message('error', $error); - - if ($this->db_debug) - { - $this->display_error($error, '', TRUE); - } - - return FALSE; - } - - return $conn_id; - } - - // -------------------------------------------------------------------- - - /** - * Reconnect - * - * Keep / reestablish the db connection if no queries have been - * sent for a length of time exceeding the server's idle timeout - * - * @access public - * @return void - */ - function reconnect() - { - // not implemented in SQLite - } - - // -------------------------------------------------------------------- - - /** - * Select the database - * - * @access private called by the base class - * @return resource - */ - function db_select() - { - return TRUE; - } - - // -------------------------------------------------------------------- - - /** - * Set client character set - * - * @access public - * @param string - * @param string - * @return resource - */ - function db_set_charset($charset, $collation) - { - // @todo - add support if needed - return TRUE; - } - - // -------------------------------------------------------------------- - - /** - * Version number query string - * - * @access public - * @return string - */ - function _version() - { - return sqlite_libversion(); - } - - // -------------------------------------------------------------------- - - /** - * Execute the query - * - * @access private called by the base class - * @param string an SQL query - * @return resource - */ - function _execute($sql) - { - $sql = $this->_prep_query($sql); - return @sqlite_query($this->conn_id, $sql); - } - - // -------------------------------------------------------------------- - - /** - * Prep the query - * - * If needed, each database adapter can prep the query string - * - * @access private called by execute() - * @param string an SQL query - * @return string - */ - function _prep_query($sql) - { - return $sql; - } - - // -------------------------------------------------------------------- - - /** - * Begin Transaction - * - * @access public - * @return bool - */ - function trans_begin($test_mode = FALSE) - { - if ( ! $this->trans_enabled) - { - return TRUE; - } - - // When transactions are nested we only begin/commit/rollback the outermost ones - if ($this->_trans_depth > 0) - { - return TRUE; - } - - // Reset the transaction failure flag. - // If the $test_mode flag is set to TRUE transactions will be rolled back - // even if the queries produce a successful result. - $this->_trans_failure = ($test_mode === TRUE) ? TRUE : FALSE; - - $this->simple_query('BEGIN TRANSACTION'); - return TRUE; - } - - // -------------------------------------------------------------------- - - /** - * Commit Transaction - * - * @access public - * @return bool - */ - function trans_commit() - { - if ( ! $this->trans_enabled) - { - return TRUE; - } - - // When transactions are nested we only begin/commit/rollback the outermost ones - if ($this->_trans_depth > 0) - { - return TRUE; - } - - $this->simple_query('COMMIT'); - return TRUE; - } - - // -------------------------------------------------------------------- - - /** - * Rollback Transaction - * - * @access public - * @return bool - */ - function trans_rollback() - { - if ( ! $this->trans_enabled) - { - return TRUE; - } - - // When transactions are nested we only begin/commit/rollback the outermost ones - if ($this->_trans_depth > 0) - { - return TRUE; - } - - $this->simple_query('ROLLBACK'); - return TRUE; - } - - // -------------------------------------------------------------------- - - /** - * Escape String - * - * @access public - * @param string - * @param bool whether or not the string will be used in a LIKE condition - * @return string - */ - function escape_str($str, $like = FALSE) - { - if (is_array($str)) - { - foreach ($str as $key => $val) - { - $str[$key] = $this->escape_str($val, $like); - } - - return $str; - } - - $str = sqlite_escape_string($str); - - // escape LIKE condition wildcards - if ($like === TRUE) - { - $str = str_replace( array('%', '_', $this->_like_escape_chr), - array($this->_like_escape_chr.'%', $this->_like_escape_chr.'_', $this->_like_escape_chr.$this->_like_escape_chr), - $str); - } - - return $str; - } - - // -------------------------------------------------------------------- - - /** - * Affected Rows - * - * @access public - * @return integer - */ - function affected_rows() - { - return sqlite_changes($this->conn_id); - } - - // -------------------------------------------------------------------- - - /** - * Insert ID - * - * @access public - * @return integer - */ - function insert_id() - { - return @sqlite_last_insert_rowid($this->conn_id); - } - - // -------------------------------------------------------------------- - - /** - * "Count All" query - * - * Generates a platform-specific query string that counts all records in - * the specified database - * - * @access public - * @param string - * @return string - */ - function count_all($table = '') - { - if ($table == '') - { - return 0; - } - - $query = $this->query($this->_count_string . $this->_protect_identifiers('numrows') . " FROM " . $this->_protect_identifiers($table, TRUE, NULL, FALSE)); - - if ($query->num_rows() == 0) - { - return 0; - } - - $row = $query->row(); - $this->_reset_select(); - return (int) $row->numrows; - } - - // -------------------------------------------------------------------- - - /** - * List table query - * - * Generates a platform-specific query string so that the table names can be fetched - * - * @access private - * @param boolean - * @return string - */ - function _list_tables($prefix_limit = FALSE) - { - $sql = "SELECT name from sqlite_master WHERE type='table'"; - - if ($prefix_limit !== FALSE AND $this->dbprefix != '') - { - $sql .= " AND 'name' LIKE '".$this->escape_like_str($this->dbprefix)."%' ".sprintf($this->_like_escape_str, $this->_like_escape_chr); - } - return $sql; - } - - // -------------------------------------------------------------------- - - /** - * Show column query - * - * Generates a platform-specific query string so that the column names can be fetched - * - * @access public - * @param string the table name - * @return string - */ - function _list_columns($table = '') - { - // Not supported - return FALSE; - } - - // -------------------------------------------------------------------- - - /** - * Field data query - * - * Generates a platform-specific query so that the column data can be retrieved - * - * @access public - * @param string the table name - * @return object - */ - function _field_data($table) - { - return "SELECT * FROM ".$table." LIMIT 1"; - } - - // -------------------------------------------------------------------- - - /** - * The error message string - * - * @access private - * @return string - */ - function _error_message() - { - return sqlite_error_string(sqlite_last_error($this->conn_id)); - } - - // -------------------------------------------------------------------- - - /** - * The error message number - * - * @access private - * @return integer - */ - function _error_number() - { - return sqlite_last_error($this->conn_id); - } - - // -------------------------------------------------------------------- - - /** - * Escape the SQL Identifiers - * - * This function escapes column and table names - * - * @access private - * @param string - * @return string - */ - function _escape_identifiers($item) - { - if ($this->_escape_char == '') - { - return $item; - } - - foreach ($this->_reserved_identifiers as $id) - { - if (strpos($item, '.'.$id) !== FALSE) - { - $str = $this->_escape_char. str_replace('.', $this->_escape_char.'.', $item); - - // remove duplicates if the user already included the escape - return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str); - } - } - - if (strpos($item, '.') !== FALSE) - { - $str = $this->_escape_char.str_replace('.', $this->_escape_char.'.'.$this->_escape_char, $item).$this->_escape_char; - } - else - { - $str = $this->_escape_char.$item.$this->_escape_char; - } - - // remove duplicates if the user already included the escape - return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str); - } - - // -------------------------------------------------------------------- - - /** - * From Tables - * - * This function implicitly groups FROM tables so there is no confusion - * about operator precedence in harmony with SQL standards - * - * @access public - * @param type - * @return type - */ - function _from_tables($tables) - { - if ( ! is_array($tables)) - { - $tables = array($tables); - } - - return '('.implode(', ', $tables).')'; - } - - // -------------------------------------------------------------------- - - /** - * Insert statement - * - * Generates a platform-specific insert string from the supplied data - * - * @access public - * @param string the table name - * @param array the insert keys - * @param array the insert values - * @return string - */ - function _insert($table, $keys, $values) - { - return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")"; - } - - // -------------------------------------------------------------------- - - /** - * Update statement - * - * Generates a platform-specific update string from the supplied data - * - * @access public - * @param string the table name - * @param array the update data - * @param array the where clause - * @param array the orderby clause - * @param array the limit clause - * @return string - */ - function _update($table, $values, $where, $orderby = array(), $limit = FALSE) - { - foreach ($values as $key => $val) - { - $valstr[] = $key." = ".$val; - } - - $limit = ( ! $limit) ? '' : ' LIMIT '.$limit; - - $orderby = (count($orderby) >= 1)?' ORDER BY '.implode(", ", $orderby):''; - - $sql = "UPDATE ".$table." SET ".implode(', ', $valstr); - - $sql .= ($where != '' AND count($where) >=1) ? " WHERE ".implode(" ", $where) : ''; - - $sql .= $orderby.$limit; - - return $sql; - } - - - // -------------------------------------------------------------------- - - /** - * Truncate statement - * - * Generates a platform-specific truncate string from the supplied data - * If the database does not support the truncate() command - * This function maps to "DELETE FROM table" - * - * @access public - * @param string the table name - * @return string - */ - function _truncate($table) - { - return $this->_delete($table); - } - - // -------------------------------------------------------------------- - - /** - * Delete statement - * - * Generates a platform-specific delete string from the supplied data - * - * @access public - * @param string the table name - * @param array the where clause - * @param string the limit clause - * @return string - */ - function _delete($table, $where = array(), $like = array(), $limit = FALSE) - { - $conditions = ''; - - if (count($where) > 0 OR count($like) > 0) - { - $conditions = "\nWHERE "; - $conditions .= implode("\n", $this->ar_where); - - if (count($where) > 0 && count($like) > 0) - { - $conditions .= " AND "; - } - $conditions .= implode("\n", $like); - } - - $limit = ( ! $limit) ? '' : ' LIMIT '.$limit; - - return "DELETE FROM ".$table.$conditions.$limit; - } - - // -------------------------------------------------------------------- - - /** - * Limit string - * - * Generates a platform-specific LIMIT clause - * - * @access public - * @param string the sql query string - * @param integer the number of rows to limit the query to - * @param integer the offset value - * @return string - */ - function _limit($sql, $limit, $offset) - { - if ($offset == 0) - { - $offset = ''; - } - else - { - $offset .= ", "; - } - - return $sql."LIMIT ".$offset.$limit; - } - - // -------------------------------------------------------------------- - - /** - * Close DB Connection - * - * @access public - * @param resource - * @return void - */ - function _close($conn_id) - { - @sqlite_close($conn_id); - } - - -} - - -/* End of file sqlite_driver.php */ -/* Location: ./system/database/drivers/sqlite/sqlite_driver.php */ \ No newline at end of file diff --git a/system/database/drivers/sqlite/sqlite_forge.php b/system/database/drivers/sqlite/sqlite_forge.php deleted file mode 100644 index 56904082..00000000 --- a/system/database/drivers/sqlite/sqlite_forge.php +++ /dev/null @@ -1,265 +0,0 @@ -db->database) OR ! @unlink($this->db->database)) - { - if ($this->db->db_debug) - { - return $this->db->display_error('db_unable_to_drop'); - } - return FALSE; - } - return TRUE; - } - // -------------------------------------------------------------------- - - /** - * Create Table - * - * @access private - * @param string the table name - * @param array the fields - * @param mixed primary key(s) - * @param mixed key(s) - * @param boolean should 'IF NOT EXISTS' be added to the SQL - * @return bool - */ - function _create_table($table, $fields, $primary_keys, $keys, $if_not_exists) - { - $sql = 'CREATE TABLE '; - - // IF NOT EXISTS added to SQLite in 3.3.0 - if ($if_not_exists === TRUE && version_compare($this->db->_version(), '3.3.0', '>=') === TRUE) - { - $sql .= 'IF NOT EXISTS '; - } - - $sql .= $this->db->_escape_identifiers($table)."("; - $current_field_count = 0; - - foreach ($fields as $field=>$attributes) - { - // Numeric field names aren't allowed in databases, so if the key is - // numeric, we know it was assigned by PHP and the developer manually - // entered the field information, so we'll simply add it to the list - if (is_numeric($field)) - { - $sql .= "\n\t$attributes"; - } - else - { - $attributes = array_change_key_case($attributes, CASE_UPPER); - - $sql .= "\n\t".$this->db->_protect_identifiers($field); - - $sql .= ' '.$attributes['TYPE']; - - if (array_key_exists('CONSTRAINT', $attributes)) - { - $sql .= '('.$attributes['CONSTRAINT'].')'; - } - - if (array_key_exists('UNSIGNED', $attributes) && $attributes['UNSIGNED'] === TRUE) - { - $sql .= ' UNSIGNED'; - } - - if (array_key_exists('DEFAULT', $attributes)) - { - $sql .= ' DEFAULT \''.$attributes['DEFAULT'].'\''; - } - - if (array_key_exists('NULL', $attributes) && $attributes['NULL'] === TRUE) - { - $sql .= ' NULL'; - } - else - { - $sql .= ' NOT NULL'; - } - - if (array_key_exists('AUTO_INCREMENT', $attributes) && $attributes['AUTO_INCREMENT'] === TRUE) - { - $sql .= ' AUTO_INCREMENT'; - } - } - - // don't add a comma on the end of the last field - if (++$current_field_count < count($fields)) - { - $sql .= ','; - } - } - - if (count($primary_keys) > 0) - { - $primary_keys = $this->db->_protect_identifiers($primary_keys); - $sql .= ",\n\tPRIMARY KEY (" . implode(', ', $primary_keys) . ")"; - } - - if (is_array($keys) && count($keys) > 0) - { - foreach ($keys as $key) - { - if (is_array($key)) - { - $key = $this->db->_protect_identifiers($key); - } - else - { - $key = array($this->db->_protect_identifiers($key)); - } - - $sql .= ",\n\tUNIQUE (" . implode(', ', $key) . ")"; - } - } - - $sql .= "\n)"; - - return $sql; - } - - // -------------------------------------------------------------------- - - /** - * Drop Table - * - * Unsupported feature in SQLite - * - * @access private - * @return bool - */ - function _drop_table($table) - { - if ($this->db->db_debug) - { - return $this->db->display_error('db_unsuported_feature'); - } - return array(); - } - - // -------------------------------------------------------------------- - - /** - * Alter table query - * - * Generates a platform-specific query so that a table can be altered - * Called by add_column(), drop_column(), and column_alter(), - * - * @access private - * @param string the ALTER type (ADD, DROP, CHANGE) - * @param string the column name - * @param string the table name - * @param string the column definition - * @param string the default value - * @param boolean should 'NOT NULL' be added - * @param string the field after which we should add the new field - * @return object - */ - function _alter_table($alter_type, $table, $column_name, $column_definition = '', $default_value = '', $null = '', $after_field = '') - { - $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table)." $alter_type ".$this->db->_protect_identifiers($column_name); - - // DROP has everything it needs now. - if ($alter_type == 'DROP') - { - // SQLite does not support dropping columns - // http://www.sqlite.org/omitted.html - // http://www.sqlite.org/faq.html#q11 - return FALSE; - } - - $sql .= " $column_definition"; - - if ($default_value != '') - { - $sql .= " DEFAULT \"$default_value\""; - } - - if ($null === NULL) - { - $sql .= ' NULL'; - } - else - { - $sql .= ' NOT NULL'; - } - - if ($after_field != '') - { - $sql .= ' AFTER ' . $this->db->_protect_identifiers($after_field); - } - - return $sql; - - } - - // -------------------------------------------------------------------- - - /** - * Rename a table - * - * Generates a platform-specific query so that a table can be renamed - * - * @access private - * @param string the old table name - * @param string the new table name - * @return string - */ - function _rename_table($table_name, $new_table_name) - { - $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table_name)." RENAME TO ".$this->db->_protect_identifiers($new_table_name); - return $sql; - } -} - -/* End of file sqlite_forge.php */ -/* Location: ./system/database/drivers/sqlite/sqlite_forge.php */ \ No newline at end of file diff --git a/system/database/drivers/sqlite/sqlite_result.php b/system/database/drivers/sqlite/sqlite_result.php deleted file mode 100644 index 7bd30db7..00000000 --- a/system/database/drivers/sqlite/sqlite_result.php +++ /dev/null @@ -1,179 +0,0 @@ -result_id); - } - - // -------------------------------------------------------------------- - - /** - * Number of fields in the result set - * - * @access public - * @return integer - */ - function num_fields() - { - return @sqlite_num_fields($this->result_id); - } - - // -------------------------------------------------------------------- - - /** - * Fetch Field Names - * - * Generates an array of column names - * - * @access public - * @return array - */ - function list_fields() - { - $field_names = array(); - for ($i = 0; $i < $this->num_fields(); $i++) - { - $field_names[] = sqlite_field_name($this->result_id, $i); - } - - return $field_names; - } - - // -------------------------------------------------------------------- - - /** - * Field data - * - * Generates an array of objects containing field meta-data - * - * @access public - * @return array - */ - function field_data() - { - $retval = array(); - for ($i = 0; $i < $this->num_fields(); $i++) - { - $F = new stdClass(); - $F->name = sqlite_field_name($this->result_id, $i); - $F->type = 'varchar'; - $F->max_length = 0; - $F->primary_key = 0; - $F->default = ''; - - $retval[] = $F; - } - - return $retval; - } - - // -------------------------------------------------------------------- - - /** - * Free the result - * - * @return null - */ - function free_result() - { - // Not implemented in SQLite - } - - // -------------------------------------------------------------------- - - /** - * Data Seek - * - * Moves the internal pointer to the desired offset. We call - * this internally before fetching results to make sure the - * result set starts at zero - * - * @access private - * @return array - */ - function _data_seek($n = 0) - { - return sqlite_seek($this->result_id, $n); - } - - // -------------------------------------------------------------------- - - /** - * Result - associative array - * - * Returns the result set as an array - * - * @access private - * @return array - */ - function _fetch_assoc() - { - return sqlite_fetch_array($this->result_id); - } - - // -------------------------------------------------------------------- - - /** - * Result - object - * - * Returns the result set as an object - * - * @access private - * @return object - */ - function _fetch_object() - { - if (function_exists('sqlite_fetch_object')) - { - return sqlite_fetch_object($this->result_id); - } - else - { - $arr = sqlite_fetch_array($this->result_id, SQLITE_ASSOC); - if (is_array($arr)) - { - $obj = (object) $arr; - return $obj; - } else { - return NULL; - } - } - } - -} - - -/* End of file sqlite_result.php */ -/* Location: ./system/database/drivers/sqlite/sqlite_result.php */ \ No newline at end of file diff --git a/system/database/drivers/sqlite/sqlite_utility.php b/system/database/drivers/sqlite/sqlite_utility.php deleted file mode 100644 index 508023e2..00000000 --- a/system/database/drivers/sqlite/sqlite_utility.php +++ /dev/null @@ -1,96 +0,0 @@ -db_debug) - { - return $this->db->display_error('db_unsuported_feature'); - } - return array(); - } - - // -------------------------------------------------------------------- - - /** - * Optimize table query - * - * Is optimization even supported in SQLite? - * - * @access private - * @param string the table name - * @return object - */ - function _optimize_table($table) - { - return FALSE; - } - - // -------------------------------------------------------------------- - - /** - * Repair table query - * - * Are table repairs even supported in SQLite? - * - * @access private - * @param string the table name - * @return object - */ - function _repair_table($table) - { - return FALSE; - } - - // -------------------------------------------------------------------- - - /** - * SQLite Export - * - * @access private - * @param array Preferences - * @return mixed - */ - function _backup($params = array()) - { - // Currently unsupported - return $this->db->display_error('db_unsuported_feature'); - } -} - -/* End of file sqlite_utility.php */ -/* Location: ./system/database/drivers/sqlite/sqlite_utility.php */ \ No newline at end of file diff --git a/system/database/drivers/sqlsrv/index.html b/system/database/drivers/sqlsrv/index.html deleted file mode 100644 index c942a79c..00000000 --- a/system/database/drivers/sqlsrv/index.html +++ /dev/null @@ -1,10 +0,0 @@ - - - 403 Forbidden - - - -

Directory access is forbidden.

- - - \ No newline at end of file diff --git a/system/database/drivers/sqlsrv/sqlsrv_driver.php b/system/database/drivers/sqlsrv/sqlsrv_driver.php deleted file mode 100644 index 400fd31c..00000000 --- a/system/database/drivers/sqlsrv/sqlsrv_driver.php +++ /dev/null @@ -1,599 +0,0 @@ -char_set)) ? 'UTF-8' : $this->char_set; - - $connection = array( - 'UID' => empty($this->username) ? '' : $this->username, - 'PWD' => empty($this->password) ? '' : $this->password, - 'Database' => $this->database, - 'ConnectionPooling' => $pooling ? 1 : 0, - 'CharacterSet' => $character_set, - 'ReturnDatesAsStrings' => 1 - ); - - // If the username and password are both empty, assume this is a - // 'Windows Authentication Mode' connection. - if(empty($connection['UID']) && empty($connection['PWD'])) { - unset($connection['UID'], $connection['PWD']); - } - - return sqlsrv_connect($this->hostname, $connection); - } - - // -------------------------------------------------------------------- - - /** - * Persistent database connection - * - * @access private called by the base class - * @return resource - */ - function db_pconnect() - { - $this->db_connect(TRUE); - } - - // -------------------------------------------------------------------- - - /** - * Reconnect - * - * Keep / reestablish the db connection if no queries have been - * sent for a length of time exceeding the server's idle timeout - * - * @access public - * @return void - */ - function reconnect() - { - // not implemented in MSSQL - } - - // -------------------------------------------------------------------- - - /** - * Select the database - * - * @access private called by the base class - * @return resource - */ - function db_select() - { - return $this->_execute('USE ' . $this->database); - } - - // -------------------------------------------------------------------- - - /** - * Set client character set - * - * @access public - * @param string - * @param string - * @return resource - */ - function db_set_charset($charset, $collation) - { - // @todo - add support if needed - return TRUE; - } - - // -------------------------------------------------------------------- - - /** - * Execute the query - * - * @access private called by the base class - * @param string an SQL query - * @return resource - */ - function _execute($sql) - { - $sql = $this->_prep_query($sql); - return sqlsrv_query($this->conn_id, $sql, null, array( - 'Scrollable' => SQLSRV_CURSOR_STATIC, - 'SendStreamParamsAtExec' => true - )); - } - - // -------------------------------------------------------------------- - - /** - * Prep the query - * - * If needed, each database adapter can prep the query string - * - * @access private called by execute() - * @param string an SQL query - * @return string - */ - function _prep_query($sql) - { - return $sql; - } - - // -------------------------------------------------------------------- - - /** - * Begin Transaction - * - * @access public - * @return bool - */ - function trans_begin($test_mode = FALSE) - { - if ( ! $this->trans_enabled) - { - return TRUE; - } - - // When transactions are nested we only begin/commit/rollback the outermost ones - if ($this->_trans_depth > 0) - { - return TRUE; - } - - // Reset the transaction failure flag. - // If the $test_mode flag is set to TRUE transactions will be rolled back - // even if the queries produce a successful result. - $this->_trans_failure = ($test_mode === TRUE) ? TRUE : FALSE; - - return sqlsrv_begin_transaction($this->conn_id); - } - - // -------------------------------------------------------------------- - - /** - * Commit Transaction - * - * @access public - * @return bool - */ - function trans_commit() - { - if ( ! $this->trans_enabled) - { - return TRUE; - } - - // When transactions are nested we only begin/commit/rollback the outermost ones - if ($this->_trans_depth > 0) - { - return TRUE; - } - - return sqlsrv_commit($this->conn_id); - } - - // -------------------------------------------------------------------- - - /** - * Rollback Transaction - * - * @access public - * @return bool - */ - function trans_rollback() - { - if ( ! $this->trans_enabled) - { - return TRUE; - } - - // When transactions are nested we only begin/commit/rollback the outermost ones - if ($this->_trans_depth > 0) - { - return TRUE; - } - - return sqlsrv_rollback($this->conn_id); - } - - // -------------------------------------------------------------------- - - /** - * Escape String - * - * @access public - * @param string - * @param bool whether or not the string will be used in a LIKE condition - * @return string - */ - function escape_str($str, $like = FALSE) - { - // Escape single quotes - return str_replace("'", "''", $str); - } - - // -------------------------------------------------------------------- - - /** - * Affected Rows - * - * @access public - * @return integer - */ - function affected_rows() - { - return @sqlrv_rows_affected($this->conn_id); - } - - // -------------------------------------------------------------------- - - /** - * Insert ID - * - * Returns the last id created in the Identity column. - * - * @access public - * @return integer - */ - function insert_id() - { - return $this->query('select @@IDENTITY as insert_id')->row('insert_id'); - } - - // -------------------------------------------------------------------- - - /** - * Parse major version - * - * Grabs the major version number from the - * database server version string passed in. - * - * @access private - * @param string $version - * @return int16 major version number - */ - function _parse_major_version($version) - { - preg_match('/([0-9]+)\.([0-9]+)\.([0-9]+)/', $version, $ver_info); - return $ver_info[1]; // return the major version b/c that's all we're interested in. - } - - // -------------------------------------------------------------------- - - /** - * Version number query string - * - * @access public - * @return string - */ - function _version() - { - $info = sqlsrv_server_info($this->conn_id); - return sprintf("select '%s' as ver", $info['SQLServerVersion']); - } - - // -------------------------------------------------------------------- - - /** - * "Count All" query - * - * Generates a platform-specific query string that counts all records in - * the specified database - * - * @access public - * @param string - * @return string - */ - function count_all($table = '') - { - if ($table == '') - return '0'; - - $query = $this->query("SELECT COUNT(*) AS numrows FROM " . $this->dbprefix . $table); - - if ($query->num_rows() == 0) - return '0'; - - $row = $query->row(); - $this->_reset_select(); - return $row->numrows; - } - - // -------------------------------------------------------------------- - - /** - * List table query - * - * Generates a platform-specific query string so that the table names can be fetched - * - * @access private - * @param boolean - * @return string - */ - function _list_tables($prefix_limit = FALSE) - { - return "SELECT name FROM sysobjects WHERE type = 'U' ORDER BY name"; - } - - // -------------------------------------------------------------------- - - /** - * List column query - * - * Generates a platform-specific query string so that the column names can be fetched - * - * @access private - * @param string the table name - * @return string - */ - function _list_columns($table = '') - { - return "SELECT * FROM INFORMATION_SCHEMA.Columns WHERE TABLE_NAME = '".$this->_escape_table($table)."'"; - } - - // -------------------------------------------------------------------- - - /** - * Field data query - * - * Generates a platform-specific query so that the column data can be retrieved - * - * @access public - * @param string the table name - * @return object - */ - function _field_data($table) - { - return "SELECT TOP 1 * FROM " . $this->_escape_table($table); - } - - // -------------------------------------------------------------------- - - /** - * The error message string - * - * @access private - * @return string - */ - function _error_message() - { - $error = array_shift(sqlsrv_errors()); - return !empty($error['message']) ? $error['message'] : null; - } - - // -------------------------------------------------------------------- - - /** - * The error message number - * - * @access private - * @return integer - */ - function _error_number() - { - $error = array_shift(sqlsrv_errors()); - return isset($error['SQLSTATE']) ? $error['SQLSTATE'] : null; - } - - // -------------------------------------------------------------------- - - /** - * Escape Table Name - * - * This function adds backticks if the table name has a period - * in it. Some DBs will get cranky unless periods are escaped - * - * @access private - * @param string the table name - * @return string - */ - function _escape_table($table) - { - return $table; - } - - - /** - * Escape the SQL Identifiers - * - * This function escapes column and table names - * - * @access private - * @param string - * @return string - */ - function _escape_identifiers($item) - { - return $item; - } - - // -------------------------------------------------------------------- - - /** - * From Tables - * - * This function implicitly groups FROM tables so there is no confusion - * about operator precedence in harmony with SQL standards - * - * @access public - * @param type - * @return type - */ - function _from_tables($tables) - { - if ( ! is_array($tables)) - { - $tables = array($tables); - } - - return implode(', ', $tables); - } - - // -------------------------------------------------------------------- - - /** - * Insert statement - * - * Generates a platform-specific insert string from the supplied data - * - * @access public - * @param string the table name - * @param array the insert keys - * @param array the insert values - * @return string - */ - function _insert($table, $keys, $values) - { - return "INSERT INTO ".$this->_escape_table($table)." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")"; - } - - // -------------------------------------------------------------------- - - /** - * Update statement - * - * Generates a platform-specific update string from the supplied data - * - * @access public - * @param string the table name - * @param array the update data - * @param array the where clause - * @param array the orderby clause - * @param array the limit clause - * @return string - */ - function _update($table, $values, $where) - { - foreach($values as $key => $val) - { - $valstr[] = $key." = ".$val; - } - - return "UPDATE ".$this->_escape_table($table)." SET ".implode(', ', $valstr)." WHERE ".implode(" ", $where); - } - - // -------------------------------------------------------------------- - - /** - * Truncate statement - * - * Generates a platform-specific truncate string from the supplied data - * If the database does not support the truncate() command - * This function maps to "DELETE FROM table" - * - * @access public - * @param string the table name - * @return string - */ - function _truncate($table) - { - return "TRUNCATE ".$table; - } - - // -------------------------------------------------------------------- - - /** - * Delete statement - * - * Generates a platform-specific delete string from the supplied data - * - * @access public - * @param string the table name - * @param array the where clause - * @param string the limit clause - * @return string - */ - function _delete($table, $where) - { - return "DELETE FROM ".$this->_escape_table($table)." WHERE ".implode(" ", $where); - } - - // -------------------------------------------------------------------- - - /** - * Limit string - * - * Generates a platform-specific LIMIT clause - * - * @access public - * @param string the sql query string - * @param integer the number of rows to limit the query to - * @param integer the offset value - * @return string - */ - function _limit($sql, $limit, $offset) - { - $i = $limit + $offset; - - return preg_replace('/(^\SELECT (DISTINCT)?)/i','\\1 TOP '.$i.' ', $sql); - } - - // -------------------------------------------------------------------- - - /** - * Close DB Connection - * - * @access public - * @param resource - * @return void - */ - function _close($conn_id) - { - @sqlsrv_close($conn_id); - } - -} - - - -/* End of file mssql_driver.php */ -/* Location: ./system/database/drivers/mssql/mssql_driver.php */ \ No newline at end of file diff --git a/system/database/drivers/sqlsrv/sqlsrv_forge.php b/system/database/drivers/sqlsrv/sqlsrv_forge.php deleted file mode 100644 index cc88ec5c..00000000 --- a/system/database/drivers/sqlsrv/sqlsrv_forge.php +++ /dev/null @@ -1,248 +0,0 @@ -db->_escape_identifiers($table); - } - - // -------------------------------------------------------------------- - - /** - * Create Table - * - * @access private - * @param string the table name - * @param array the fields - * @param mixed primary key(s) - * @param mixed key(s) - * @param boolean should 'IF NOT EXISTS' be added to the SQL - * @return bool - */ - function _create_table($table, $fields, $primary_keys, $keys, $if_not_exists) - { - $sql = 'CREATE TABLE '; - - if ($if_not_exists === TRUE) - { - $sql .= 'IF NOT EXISTS '; - } - - $sql .= $this->db->_escape_identifiers($table)." ("; - $current_field_count = 0; - - foreach ($fields as $field=>$attributes) - { - // Numeric field names aren't allowed in databases, so if the key is - // numeric, we know it was assigned by PHP and the developer manually - // entered the field information, so we'll simply add it to the list - if (is_numeric($field)) - { - $sql .= "\n\t$attributes"; - } - else - { - $attributes = array_change_key_case($attributes, CASE_UPPER); - - $sql .= "\n\t".$this->db->_protect_identifiers($field); - - $sql .= ' '.$attributes['TYPE']; - - if (array_key_exists('CONSTRAINT', $attributes)) - { - $sql .= '('.$attributes['CONSTRAINT'].')'; - } - - if (array_key_exists('UNSIGNED', $attributes) && $attributes['UNSIGNED'] === TRUE) - { - $sql .= ' UNSIGNED'; - } - - if (array_key_exists('DEFAULT', $attributes)) - { - $sql .= ' DEFAULT \''.$attributes['DEFAULT'].'\''; - } - - if (array_key_exists('NULL', $attributes) && $attributes['NULL'] === TRUE) - { - $sql .= ' NULL'; - } - else - { - $sql .= ' NOT NULL'; - } - - if (array_key_exists('AUTO_INCREMENT', $attributes) && $attributes['AUTO_INCREMENT'] === TRUE) - { - $sql .= ' AUTO_INCREMENT'; - } - } - - // don't add a comma on the end of the last field - if (++$current_field_count < count($fields)) - { - $sql .= ','; - } - } - - if (count($primary_keys) > 0) - { - $primary_keys = $this->db->_protect_identifiers($primary_keys); - $sql .= ",\n\tPRIMARY KEY (" . implode(', ', $primary_keys) . ")"; - } - - if (is_array($keys) && count($keys) > 0) - { - foreach ($keys as $key) - { - if (is_array($key)) - { - $key = $this->db->_protect_identifiers($key); - } - else - { - $key = array($this->db->_protect_identifiers($key)); - } - - $sql .= ",\n\tFOREIGN KEY (" . implode(', ', $key) . ")"; - } - } - - $sql .= "\n)"; - - return $sql; - } - - // -------------------------------------------------------------------- - - /** - * Alter table query - * - * Generates a platform-specific query so that a table can be altered - * Called by add_column(), drop_column(), and column_alter(), - * - * @access private - * @param string the ALTER type (ADD, DROP, CHANGE) - * @param string the column name - * @param string the table name - * @param string the column definition - * @param string the default value - * @param boolean should 'NOT NULL' be added - * @param string the field after which we should add the new field - * @return object - */ - function _alter_table($alter_type, $table, $column_name, $column_definition = '', $default_value = '', $null = '', $after_field = '') - { - $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table)." $alter_type ".$this->db->_protect_identifiers($column_name); - - // DROP has everything it needs now. - if ($alter_type == 'DROP') - { - return $sql; - } - - $sql .= " $column_definition"; - - if ($default_value != '') - { - $sql .= " DEFAULT \"$default_value\""; - } - - if ($null === NULL) - { - $sql .= ' NULL'; - } - else - { - $sql .= ' NOT NULL'; - } - - if ($after_field != '') - { - $sql .= ' AFTER ' . $this->db->_protect_identifiers($after_field); - } - - return $sql; - - } - - // -------------------------------------------------------------------- - - /** - * Rename a table - * - * Generates a platform-specific query so that a table can be renamed - * - * @access private - * @param string the old table name - * @param string the new table name - * @return string - */ - function _rename_table($table_name, $new_table_name) - { - // I think this syntax will work, but can find little documentation on renaming tables in MSSQL - $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table_name)." RENAME TO ".$this->db->_protect_identifiers($new_table_name); - return $sql; - } - -} - -/* End of file mssql_forge.php */ -/* Location: ./system/database/drivers/mssql/mssql_forge.php */ \ No newline at end of file diff --git a/system/database/drivers/sqlsrv/sqlsrv_result.php b/system/database/drivers/sqlsrv/sqlsrv_result.php deleted file mode 100644 index bf0abd1c..00000000 --- a/system/database/drivers/sqlsrv/sqlsrv_result.php +++ /dev/null @@ -1,169 +0,0 @@ -result_id); - } - - // -------------------------------------------------------------------- - - /** - * Number of fields in the result set - * - * @access public - * @return integer - */ - function num_fields() - { - return @sqlsrv_num_fields($this->result_id); - } - - // -------------------------------------------------------------------- - - /** - * Fetch Field Names - * - * Generates an array of column names - * - * @access public - * @return array - */ - function list_fields() - { - $field_names = array(); - foreach(sqlsrv_field_metadata($this->result_id) as $offset => $field) - { - $field_names[] = $field['Name']; - } - - return $field_names; - } - - // -------------------------------------------------------------------- - - /** - * Field data - * - * Generates an array of objects containing field meta-data - * - * @access public - * @return array - */ - function field_data() - { - $retval = array(); - foreach(sqlsrv_field_metadata($this->result_id) as $offset => $field) - { - $F = new stdClass(); - $F->name = $field['Name']; - $F->type = $field['Type']; - $F->max_length = $field['Size']; - $F->primary_key = 0; - $F->default = ''; - - $retval[] = $F; - } - - return $retval; - } - - // -------------------------------------------------------------------- - - /** - * Free the result - * - * @return null - */ - function free_result() - { - if (is_resource($this->result_id)) - { - sqlsrv_free_stmt($this->result_id); - $this->result_id = FALSE; - } - } - - // -------------------------------------------------------------------- - - /** - * Data Seek - * - * Moves the internal pointer to the desired offset. We call - * this internally before fetching results to make sure the - * result set starts at zero - * - * @access private - * @return array - */ - function _data_seek($n = 0) - { - // Not implemented - } - - // -------------------------------------------------------------------- - - /** - * Result - associative array - * - * Returns the result set as an array - * - * @access private - * @return array - */ - function _fetch_assoc() - { - return sqlsrv_fetch_array($this->result_id, SQLSRV_FETCH_ASSOC); - } - - // -------------------------------------------------------------------- - - /** - * Result - object - * - * Returns the result set as an object - * - * @access private - * @return object - */ - function _fetch_object() - { - return sqlsrv_fetch_object($this->result_id); - } - -} - - -/* End of file mssql_result.php */ -/* Location: ./system/database/drivers/mssql/mssql_result.php */ \ No newline at end of file diff --git a/system/database/drivers/sqlsrv/sqlsrv_utility.php b/system/database/drivers/sqlsrv/sqlsrv_utility.php deleted file mode 100644 index 13a1850c..00000000 --- a/system/database/drivers/sqlsrv/sqlsrv_utility.php +++ /dev/null @@ -1,88 +0,0 @@ -db->display_error('db_unsuported_feature'); - } - -} - -/* End of file mssql_utility.php */ -/* Location: ./system/database/drivers/mssql/mssql_utility.php */ \ No newline at end of file diff --git a/system/database/index.html b/system/database/index.html deleted file mode 100644 index c942a79c..00000000 --- a/system/database/index.html +++ /dev/null @@ -1,10 +0,0 @@ - - - 403 Forbidden - - - -

Directory access is forbidden.

- - - \ No newline at end of file diff --git a/system/fonts/index.html b/system/fonts/index.html deleted file mode 100644 index c942a79c..00000000 --- a/system/fonts/index.html +++ /dev/null @@ -1,10 +0,0 @@ - - - 403 Forbidden - - - -

Directory access is forbidden.

- - - \ No newline at end of file diff --git a/system/fonts/texb.ttf b/system/fonts/texb.ttf deleted file mode 100644 index 383c88b86b7c17e2e284732af48b2bfc359647ae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 143830 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 z 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 n`i '', 'img_path' => '', 'img_url' => '', 'img_width' => '150', 'img_height' => '30', 'font_path' => '', 'expiration' => 7200); - - foreach ($defaults as $key => $val) - { - if ( ! is_array($data)) - { - if ( ! isset($$key) OR $$key == '') - { - $$key = $val; - } - } - else - { - $$key = ( ! isset($data[$key])) ? $val : $data[$key]; - } - } - - if ($img_path == '' OR $img_url == '') - { - return FALSE; - } - - if ( ! @is_dir($img_path)) - { - return FALSE; - } - - if ( ! is_writable($img_path)) - { - return FALSE; - } - - if ( ! extension_loaded('gd')) - { - return FALSE; - } - - // ----------------------------------- - // Remove old images - // ----------------------------------- - - list($usec, $sec) = explode(" ", microtime()); - $now = ((float)$usec + (float)$sec); - - $current_dir = @opendir($img_path); - - while ($filename = @readdir($current_dir)) - { - if ($filename != "." and $filename != ".." and $filename != "index.html") - { - $name = str_replace(".jpg", "", $filename); - - if (($name + $expiration) < $now) - { - @unlink($img_path.$filename); - } - } - } - - @closedir($current_dir); - - // ----------------------------------- - // Do we have a "word" yet? - // ----------------------------------- - - if ($word == '') - { - $pool = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; - - $str = ''; - for ($i = 0; $i < 8; $i++) - { - $str .= substr($pool, mt_rand(0, strlen($pool) -1), 1); - } - - $word = $str; - } - - // ----------------------------------- - // Determine angle and position - // ----------------------------------- - - $length = strlen($word); - $angle = ($length >= 6) ? rand(-($length-6), ($length-6)) : 0; - $x_axis = rand(6, (360/$length)-16); - $y_axis = ($angle >= 0 ) ? rand($img_height, $img_width) : rand(6, $img_height); - - // ----------------------------------- - // Create image - // ----------------------------------- - - // PHP.net recommends imagecreatetruecolor(), but it isn't always available - if (function_exists('imagecreatetruecolor')) - { - $im = imagecreatetruecolor($img_width, $img_height); - } - else - { - $im = imagecreate($img_width, $img_height); - } - - // ----------------------------------- - // Assign colors - // ----------------------------------- - - $bg_color = imagecolorallocate ($im, 255, 255, 255); - $border_color = imagecolorallocate ($im, 153, 102, 102); - $text_color = imagecolorallocate ($im, 204, 153, 153); - $grid_color = imagecolorallocate($im, 255, 182, 182); - $shadow_color = imagecolorallocate($im, 255, 240, 240); - - // ----------------------------------- - // Create the rectangle - // ----------------------------------- - - ImageFilledRectangle($im, 0, 0, $img_width, $img_height, $bg_color); - - // ----------------------------------- - // Create the spiral pattern - // ----------------------------------- - - $theta = 1; - $thetac = 7; - $radius = 16; - $circles = 20; - $points = 32; - - for ($i = 0; $i < ($circles * $points) - 1; $i++) - { - $theta = $theta + $thetac; - $rad = $radius * ($i / $points ); - $x = ($rad * cos($theta)) + $x_axis; - $y = ($rad * sin($theta)) + $y_axis; - $theta = $theta + $thetac; - $rad1 = $radius * (($i + 1) / $points); - $x1 = ($rad1 * cos($theta)) + $x_axis; - $y1 = ($rad1 * sin($theta )) + $y_axis; - imageline($im, $x, $y, $x1, $y1, $grid_color); - $theta = $theta - $thetac; - } - - // ----------------------------------- - // Write the text - // ----------------------------------- - - $use_font = ($font_path != '' AND file_exists($font_path) AND function_exists('imagettftext')) ? TRUE : FALSE; - - if ($use_font == FALSE) - { - $font_size = 5; - $x = rand(0, $img_width/($length/3)); - $y = 0; - } - else - { - $font_size = 16; - $x = rand(0, $img_width/($length/1.5)); - $y = $font_size+2; - } - - for ($i = 0; $i < strlen($word); $i++) - { - if ($use_font == FALSE) - { - $y = rand(0 , $img_height/2); - imagestring($im, $font_size, $x, $y, substr($word, $i, 1), $text_color); - $x += ($font_size*2); - } - else - { - $y = rand($img_height/2, $img_height-3); - imagettftext($im, $font_size, $angle, $x, $y, $text_color, $font_path, substr($word, $i, 1)); - $x += $font_size; - } - } - - - // ----------------------------------- - // Create the border - // ----------------------------------- - - imagerectangle($im, 0, 0, $img_width-1, $img_height-1, $border_color); - - // ----------------------------------- - // Generate the image - // ----------------------------------- - - $img_name = $now.'.jpg'; - - ImageJPEG($im, $img_path.$img_name); - - $img = "\""; - - ImageDestroy($im); - - return array('word' => $word, 'time' => $now, 'image' => $img); - } -} - -// ------------------------------------------------------------------------ - -/* End of file captcha_helper.php */ -/* Location: ./system/heleprs/captcha_helper.php */ \ No newline at end of file diff --git a/system/helpers/cookie_helper.php b/system/helpers/cookie_helper.php deleted file mode 100644 index 7cee0282..00000000 --- a/system/helpers/cookie_helper.php +++ /dev/null @@ -1,103 +0,0 @@ -input->set_cookie($name, $value, $expire, $domain, $path, $prefix, $secure); - } -} - -// -------------------------------------------------------------------- - -/** - * Fetch an item from the COOKIE array - * - * @access public - * @param string - * @param bool - * @return mixed - */ -if ( ! function_exists('get_cookie')) -{ - function get_cookie($index = '', $xss_clean = FALSE) - { - $CI =& get_instance(); - - $prefix = ''; - - if ( ! isset($_COOKIE[$index]) && config_item('cookie_prefix') != '') - { - $prefix = config_item('cookie_prefix'); - } - - return $CI->input->cookie($prefix.$index, $xss_clean); - } -} - -// -------------------------------------------------------------------- - -/** - * Delete a COOKIE - * - * @param mixed - * @param string the cookie domain. Usually: .yourdomain.com - * @param string the cookie path - * @param string the cookie prefix - * @return void - */ -if ( ! function_exists('delete_cookie')) -{ - function delete_cookie($name = '', $domain = '', $path = '/', $prefix = '') - { - set_cookie($name, '', '', $domain, $path, $prefix); - } -} - - -/* End of file cookie_helper.php */ -/* Location: ./system/helpers/cookie_helper.php */ \ No newline at end of file diff --git a/system/helpers/date_helper.php b/system/helpers/date_helper.php deleted file mode 100644 index 0aeb7faf..00000000 --- a/system/helpers/date_helper.php +++ /dev/null @@ -1,611 +0,0 @@ -config->item('time_reference')) == 'gmt') - { - $now = time(); - $system_time = mktime(gmdate("H", $now), gmdate("i", $now), gmdate("s", $now), gmdate("m", $now), gmdate("d", $now), gmdate("Y", $now)); - - if (strlen($system_time) < 10) - { - $system_time = time(); - log_message('error', 'The Date class could not set a proper GMT timestamp so the local time() value was used.'); - } - - return $system_time; - } - else - { - return time(); - } - } -} - -// ------------------------------------------------------------------------ - -/** - * Convert MySQL Style Datecodes - * - * This function is identical to PHPs date() function, - * except that it allows date codes to be formatted using - * the MySQL style, where each code letter is preceded - * with a percent sign: %Y %m %d etc... - * - * The benefit of doing dates this way is that you don't - * have to worry about escaping your text letters that - * match the date codes. - * - * @access public - * @param string - * @param integer - * @return integer - */ -if ( ! function_exists('mdate')) -{ - function mdate($datestr = '', $time = '') - { - if ($datestr == '') - return ''; - - if ($time == '') - $time = now(); - - $datestr = str_replace('%\\', '', preg_replace("/([a-z]+?){1}/i", "\\\\\\1", $datestr)); - return date($datestr, $time); - } -} - -// ------------------------------------------------------------------------ - -/** - * Standard Date - * - * Returns a date formatted according to the submitted standard. - * - * @access public - * @param string the chosen format - * @param integer Unix timestamp - * @return string - */ -if ( ! function_exists('standard_date')) -{ - function standard_date($fmt = 'DATE_RFC822', $time = '') - { - $formats = array( - 'DATE_ATOM' => '%Y-%m-%dT%H:%i:%s%Q', - 'DATE_COOKIE' => '%l, %d-%M-%y %H:%i:%s UTC', - 'DATE_ISO8601' => '%Y-%m-%dT%H:%i:%s%Q', - 'DATE_RFC822' => '%D, %d %M %y %H:%i:%s %O', - 'DATE_RFC850' => '%l, %d-%M-%y %H:%i:%s UTC', - 'DATE_RFC1036' => '%D, %d %M %y %H:%i:%s %O', - 'DATE_RFC1123' => '%D, %d %M %Y %H:%i:%s %O', - 'DATE_RSS' => '%D, %d %M %Y %H:%i:%s %O', - 'DATE_W3C' => '%Y-%m-%dT%H:%i:%s%Q' - ); - - if ( ! isset($formats[$fmt])) - { - return FALSE; - } - - return mdate($formats[$fmt], $time); - } -} - -// ------------------------------------------------------------------------ - -/** - * Timespan - * - * Returns a span of seconds in this format: - * 10 days 14 hours 36 minutes 47 seconds - * - * @access public - * @param integer a number of seconds - * @param integer Unix timestamp - * @return integer - */ -if ( ! function_exists('timespan')) -{ - function timespan($seconds = 1, $time = '') - { - $CI =& get_instance(); - $CI->lang->load('date'); - - if ( ! is_numeric($seconds)) - { - $seconds = 1; - } - - if ( ! is_numeric($time)) - { - $time = time(); - } - - if ($time <= $seconds) - { - $seconds = 1; - } - else - { - $seconds = $time - $seconds; - } - - $str = ''; - $years = floor($seconds / 31536000); - - if ($years > 0) - { - $str .= $years.' '.$CI->lang->line((($years > 1) ? 'date_years' : 'date_year')).', '; - } - - $seconds -= $years * 31536000; - $months = floor($seconds / 2628000); - - if ($years > 0 OR $months > 0) - { - if ($months > 0) - { - $str .= $months.' '.$CI->lang->line((($months > 1) ? 'date_months' : 'date_month')).', '; - } - - $seconds -= $months * 2628000; - } - - $weeks = floor($seconds / 604800); - - if ($years > 0 OR $months > 0 OR $weeks > 0) - { - if ($weeks > 0) - { - $str .= $weeks.' '.$CI->lang->line((($weeks > 1) ? 'date_weeks' : 'date_week')).', '; - } - - $seconds -= $weeks * 604800; - } - - $days = floor($seconds / 86400); - - if ($months > 0 OR $weeks > 0 OR $days > 0) - { - if ($days > 0) - { - $str .= $days.' '.$CI->lang->line((($days > 1) ? 'date_days' : 'date_day')).', '; - } - - $seconds -= $days * 86400; - } - - $hours = floor($seconds / 3600); - - if ($days > 0 OR $hours > 0) - { - if ($hours > 0) - { - $str .= $hours.' '.$CI->lang->line((($hours > 1) ? 'date_hours' : 'date_hour')).', '; - } - - $seconds -= $hours * 3600; - } - - $minutes = floor($seconds / 60); - - if ($days > 0 OR $hours > 0 OR $minutes > 0) - { - if ($minutes > 0) - { - $str .= $minutes.' '.$CI->lang->line((($minutes > 1) ? 'date_minutes' : 'date_minute')).', '; - } - - $seconds -= $minutes * 60; - } - - if ($str == '') - { - $str .= $seconds.' '.$CI->lang->line((($seconds > 1) ? 'date_seconds' : 'date_second')).', '; - } - - return substr(trim($str), 0, -1); - } -} - -// ------------------------------------------------------------------------ - -/** - * Number of days in a month - * - * Takes a month/year as input and returns the number of days - * for the given month/year. Takes leap years into consideration. - * - * @access public - * @param integer a numeric month - * @param integer a numeric year - * @return integer - */ -if ( ! function_exists('days_in_month')) -{ - function days_in_month($month = 0, $year = '') - { - if ($month < 1 OR $month > 12) - { - return 0; - } - - if ( ! is_numeric($year) OR strlen($year) != 4) - { - $year = date('Y'); - } - - if ($month == 2) - { - if ($year % 400 == 0 OR ($year % 4 == 0 AND $year % 100 != 0)) - { - return 29; - } - } - - $days_in_month = array(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31); - return $days_in_month[$month - 1]; - } -} - -// ------------------------------------------------------------------------ - -/** - * Converts a local Unix timestamp to GMT - * - * @access public - * @param integer Unix timestamp - * @return integer - */ -if ( ! function_exists('local_to_gmt')) -{ - function local_to_gmt($time = '') - { - if ($time == '') - $time = time(); - - return mktime( gmdate("H", $time), gmdate("i", $time), gmdate("s", $time), gmdate("m", $time), gmdate("d", $time), gmdate("Y", $time)); - } -} - -// ------------------------------------------------------------------------ - -/** - * Converts GMT time to a localized value - * - * Takes a Unix timestamp (in GMT) as input, and returns - * at the local value based on the timezone and DST setting - * submitted - * - * @access public - * @param integer Unix timestamp - * @param string timezone - * @param bool whether DST is active - * @return integer - */ -if ( ! function_exists('gmt_to_local')) -{ - function gmt_to_local($time = '', $timezone = 'UTC', $dst = FALSE) - { - if ($time == '') - { - return now(); - } - - $time += timezones($timezone) * 3600; - - if ($dst == TRUE) - { - $time += 3600; - } - - return $time; - } -} - -// ------------------------------------------------------------------------ - -/** - * Converts a MySQL Timestamp to Unix - * - * @access public - * @param integer Unix timestamp - * @return integer - */ -if ( ! function_exists('mysql_to_unix')) -{ - function mysql_to_unix($time = '') - { - // We'll remove certain characters for backward compatibility - // since the formatting changed with MySQL 4.1 - // YYYY-MM-DD HH:MM:SS - - $time = str_replace('-', '', $time); - $time = str_replace(':', '', $time); - $time = str_replace(' ', '', $time); - - // YYYYMMDDHHMMSS - return mktime( - substr($time, 8, 2), - substr($time, 10, 2), - substr($time, 12, 2), - substr($time, 4, 2), - substr($time, 6, 2), - substr($time, 0, 4) - ); - } -} - -// ------------------------------------------------------------------------ - -/** - * Unix to "Human" - * - * Formats Unix timestamp to the following prototype: 2006-08-21 11:35 PM - * - * @access public - * @param integer Unix timestamp - * @param bool whether to show seconds - * @param string format: us or euro - * @return string - */ -if ( ! function_exists('unix_to_human')) -{ - function unix_to_human($time = '', $seconds = FALSE, $fmt = 'us') - { - $r = date('Y', $time).'-'.date('m', $time).'-'.date('d', $time).' '; - - if ($fmt == 'us') - { - $r .= date('h', $time).':'.date('i', $time); - } - else - { - $r .= date('H', $time).':'.date('i', $time); - } - - if ($seconds) - { - $r .= ':'.date('s', $time); - } - - if ($fmt == 'us') - { - $r .= ' '.date('A', $time); - } - - return $r; - } -} - -// ------------------------------------------------------------------------ - -/** - * Convert "human" date to GMT - * - * Reverses the above process - * - * @access public - * @param string format: us or euro - * @return integer - */ -if ( ! function_exists('human_to_unix')) -{ - function human_to_unix($datestr = '') - { - if ($datestr == '') - { - return FALSE; - } - - $datestr = trim($datestr); - $datestr = preg_replace("/\040+/", ' ', $datestr); - - if ( ! preg_match('/^[0-9]{2,4}\-[0-9]{1,2}\-[0-9]{1,2}\s[0-9]{1,2}:[0-9]{1,2}(?::[0-9]{1,2})?(?:\s[AP]M)?$/i', $datestr)) - { - return FALSE; - } - - $split = explode(' ', $datestr); - - $ex = explode("-", $split['0']); - - $year = (strlen($ex['0']) == 2) ? '20'.$ex['0'] : $ex['0']; - $month = (strlen($ex['1']) == 1) ? '0'.$ex['1'] : $ex['1']; - $day = (strlen($ex['2']) == 1) ? '0'.$ex['2'] : $ex['2']; - - $ex = explode(":", $split['1']); - - $hour = (strlen($ex['0']) == 1) ? '0'.$ex['0'] : $ex['0']; - $min = (strlen($ex['1']) == 1) ? '0'.$ex['1'] : $ex['1']; - - if (isset($ex['2']) && preg_match('/[0-9]{1,2}/', $ex['2'])) - { - $sec = (strlen($ex['2']) == 1) ? '0'.$ex['2'] : $ex['2']; - } - else - { - // Unless specified, seconds get set to zero. - $sec = '00'; - } - - if (isset($split['2'])) - { - $ampm = strtolower($split['2']); - - if (substr($ampm, 0, 1) == 'p' AND $hour < 12) - $hour = $hour + 12; - - if (substr($ampm, 0, 1) == 'a' AND $hour == 12) - $hour = '00'; - - if (strlen($hour) == 1) - $hour = '0'.$hour; - } - - return mktime($hour, $min, $sec, $month, $day, $year); - } -} - -// ------------------------------------------------------------------------ - -/** - * Timezone Menu - * - * Generates a drop-down menu of timezones. - * - * @access public - * @param string timezone - * @param string classname - * @param string menu name - * @return string - */ -if ( ! function_exists('timezone_menu')) -{ - function timezone_menu($default = 'UTC', $class = "", $name = 'timezones') - { - $CI =& get_instance(); - $CI->lang->load('date'); - - if ($default == 'GMT') - $default = 'UTC'; - - $menu = '"; - - return $menu; - } -} - -// ------------------------------------------------------------------------ - -/** - * Timezones - * - * Returns an array of timezones. This is a helper function - * for various other ones in this library - * - * @access public - * @param string timezone - * @return string - */ -if ( ! function_exists('timezones')) -{ - function timezones($tz = '') - { - // Note: Don't change the order of these even though - // some items appear to be in the wrong order - - $zones = array( - 'UM12' => -12, - 'UM11' => -11, - 'UM10' => -10, - 'UM95' => -9.5, - 'UM9' => -9, - 'UM8' => -8, - 'UM7' => -7, - 'UM6' => -6, - 'UM5' => -5, - 'UM45' => -4.5, - 'UM4' => -4, - 'UM35' => -3.5, - 'UM3' => -3, - 'UM2' => -2, - 'UM1' => -1, - 'UTC' => 0, - 'UP1' => +1, - 'UP2' => +2, - 'UP3' => +3, - 'UP35' => +3.5, - 'UP4' => +4, - 'UP45' => +4.5, - 'UP5' => +5, - 'UP55' => +5.5, - 'UP575' => +5.75, - 'UP6' => +6, - 'UP65' => +6.5, - 'UP7' => +7, - 'UP8' => +8, - 'UP875' => +8.75, - 'UP9' => +9, - 'UP95' => +9.5, - 'UP10' => +10, - 'UP105' => +10.5, - 'UP11' => +11, - 'UP115' => +11.5, - 'UP12' => +12, - 'UP1275' => +12.75, - 'UP13' => +13, - 'UP14' => +14 - ); - - if ($tz == '') - { - return $zones; - } - - if ($tz == 'GMT') - $tz = 'UTC'; - - return ( ! isset($zones[$tz])) ? 0 : $zones[$tz]; - } -} - - -/* End of file date_helper.php */ -/* Location: ./system/helpers/date_helper.php */ \ No newline at end of file diff --git a/system/helpers/directory_helper.php b/system/helpers/directory_helper.php deleted file mode 100644 index 38347fa6..00000000 --- a/system/helpers/directory_helper.php +++ /dev/null @@ -1,80 +0,0 @@ - 0) && @is_dir($source_dir.$file)) - { - $filedata[$file] = directory_map($source_dir.$file.DIRECTORY_SEPARATOR, $new_depth, $hidden); - } - else - { - $filedata[] = $file; - } - } - - closedir($fp); - return $filedata; - } - - return FALSE; - } -} - - -/* End of file directory_helper.php */ -/* Location: ./system/helpers/directory_helper.php */ \ No newline at end of file diff --git a/system/helpers/download_helper.php b/system/helpers/download_helper.php deleted file mode 100644 index 1145688a..00000000 --- a/system/helpers/download_helper.php +++ /dev/null @@ -1,107 +0,0 @@ - 0) - { - $data =& fread($fp, filesize($file)); - } - - flock($fp, LOCK_UN); - fclose($fp); - - return $data; - } -} - -// ------------------------------------------------------------------------ - -/** - * Write File - * - * Writes data to the file specified in the path. - * Creates a new file if non-existent. - * - * @access public - * @param string path to file - * @param string file data - * @return bool - */ -if ( ! function_exists('write_file')) -{ - function write_file($path, $data, $mode = FOPEN_WRITE_CREATE_DESTRUCTIVE) - { - if ( ! $fp = @fopen($path, $mode)) - { - return FALSE; - } - - flock($fp, LOCK_EX); - fwrite($fp, $data); - flock($fp, LOCK_UN); - fclose($fp); - - return TRUE; - } -} - -// ------------------------------------------------------------------------ - -/** - * Delete Files - * - * Deletes all files contained in the supplied directory path. - * Files must be writable or owned by the system in order to be deleted. - * If the second parameter is set to TRUE, any directories contained - * within the supplied base directory will be nuked as well. - * - * @access public - * @param string path to file - * @param bool whether to delete any directories found in the path - * @return bool - */ -if ( ! function_exists('delete_files')) -{ - function delete_files($path, $del_dir = FALSE, $level = 0) - { - // Trim the trailing slash - $path = rtrim($path, DIRECTORY_SEPARATOR); - - if ( ! $current_dir = @opendir($path)) - { - return FALSE; - } - - while (FALSE !== ($filename = @readdir($current_dir))) - { - if ($filename != "." and $filename != "..") - { - if (is_dir($path.DIRECTORY_SEPARATOR.$filename)) - { - // Ignore empty folders - if (substr($filename, 0, 1) != '.') - { - delete_files($path.DIRECTORY_SEPARATOR.$filename, $del_dir, $level + 1); - } - } - else - { - unlink($path.DIRECTORY_SEPARATOR.$filename); - } - } - } - @closedir($current_dir); - - if ($del_dir == TRUE AND $level > 0) - { - return @rmdir($path); - } - - return TRUE; - } -} - -// ------------------------------------------------------------------------ - -/** - * Get Filenames - * - * Reads the specified directory and builds an array containing the filenames. - * Any sub-folders contained within the specified path are read as well. - * - * @access public - * @param string path to source - * @param bool whether to include the path as part of the filename - * @param bool internal variable to determine recursion status - do not use in calls - * @return array - */ -if ( ! function_exists('get_filenames')) -{ - function get_filenames($source_dir, $include_path = FALSE, $_recursion = FALSE) - { - static $_filedata = array(); - - if ($fp = @opendir($source_dir)) - { - // reset the array and make sure $source_dir has a trailing slash on the initial call - if ($_recursion === FALSE) - { - $_filedata = array(); - $source_dir = rtrim(realpath($source_dir), DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR; - } - - while (FALSE !== ($file = readdir($fp))) - { - if (@is_dir($source_dir.$file) && strncmp($file, '.', 1) !== 0) - { - get_filenames($source_dir.$file.DIRECTORY_SEPARATOR, $include_path, TRUE); - } - elseif (strncmp($file, '.', 1) !== 0) - { - $_filedata[] = ($include_path == TRUE) ? $source_dir.$file : $file; - } - } - return $_filedata; - } - else - { - return FALSE; - } - } -} - -// -------------------------------------------------------------------- - -/** - * Get Directory File Information - * - * Reads the specified directory and builds an array containing the filenames, - * filesize, dates, and permissions - * - * Any sub-folders contained within the specified path are read as well. - * - * @access public - * @param string path to source - * @param bool Look only at the top level directory specified? - * @param bool internal variable to determine recursion status - do not use in calls - * @return array - */ -if ( ! function_exists('get_dir_file_info')) -{ - function get_dir_file_info($source_dir, $top_level_only = TRUE, $_recursion = FALSE) - { - static $_filedata = array(); - $relative_path = $source_dir; - - if ($fp = @opendir($source_dir)) - { - // reset the array and make sure $source_dir has a trailing slash on the initial call - if ($_recursion === FALSE) - { - $_filedata = array(); - $source_dir = rtrim(realpath($source_dir), DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR; - } - - // foreach (scandir($source_dir, 1) as $file) // In addition to being PHP5+, scandir() is simply not as fast - while (FALSE !== ($file = readdir($fp))) - { - if (@is_dir($source_dir.$file) AND strncmp($file, '.', 1) !== 0 AND $top_level_only === FALSE) - { - get_dir_file_info($source_dir.$file.DIRECTORY_SEPARATOR, $top_level_only, TRUE); - } - elseif (strncmp($file, '.', 1) !== 0) - { - $_filedata[$file] = get_file_info($source_dir.$file); - $_filedata[$file]['relative_path'] = $relative_path; - } - } - - return $_filedata; - } - else - { - return FALSE; - } - } -} - -// -------------------------------------------------------------------- - -/** -* Get File Info -* -* Given a file and path, returns the name, path, size, date modified -* Second parameter allows you to explicitly declare what information you want returned -* Options are: name, server_path, size, date, readable, writable, executable, fileperms -* Returns FALSE if the file cannot be found. -* -* @access public -* @param string path to file -* @param mixed array or comma separated string of information returned -* @return array -*/ -if ( ! function_exists('get_file_info')) -{ - function get_file_info($file, $returned_values = array('name', 'server_path', 'size', 'date')) - { - - if ( ! file_exists($file)) - { - return FALSE; - } - - if (is_string($returned_values)) - { - $returned_values = explode(',', $returned_values); - } - - foreach ($returned_values as $key) - { - switch ($key) - { - case 'name': - $fileinfo['name'] = substr(strrchr($file, DIRECTORY_SEPARATOR), 1); - break; - case 'server_path': - $fileinfo['server_path'] = $file; - break; - case 'size': - $fileinfo['size'] = filesize($file); - break; - case 'date': - $fileinfo['date'] = filemtime($file); - break; - case 'readable': - $fileinfo['readable'] = is_readable($file); - break; - case 'writable': - // There are known problems using is_weritable on IIS. It may not be reliable - consider fileperms() - $fileinfo['writable'] = is_writable($file); - break; - case 'executable': - $fileinfo['executable'] = is_executable($file); - break; - case 'fileperms': - $fileinfo['fileperms'] = fileperms($file); - break; - } - } - - return $fileinfo; - } -} - -// -------------------------------------------------------------------- - -/** - * Get Mime by Extension - * - * Translates a file extension into a mime type based on config/mimes.php. - * Returns FALSE if it can't determine the type, or open the mime config file - * - * Note: this is NOT an accurate way of determining file mime types, and is here strictly as a convenience - * It should NOT be trusted, and should certainly NOT be used for security - * - * @access public - * @param string path to file - * @return mixed - */ -if ( ! function_exists('get_mime_by_extension')) -{ - function get_mime_by_extension($file) - { - $extension = strtolower(substr(strrchr($file, '.'), 1)); - - global $mimes; - - if ( ! is_array($mimes)) - { - if (defined('ENVIRONMENT') AND is_file(APPPATH.'config/'.ENVIRONMENT.'/mimes.php')) - { - include(APPPATH.'config/'.ENVIRONMENT.'/mimes.php'); - } - elseif (is_file(APPPATH.'config/mimes.php')) - { - include(APPPATH.'config/mimes.php'); - } - - if ( ! is_array($mimes)) - { - return FALSE; - } - } - - if (array_key_exists($extension, $mimes)) - { - if (is_array($mimes[$extension])) - { - // Multiple mime types, just give the first one - return current($mimes[$extension]); - } - else - { - return $mimes[$extension]; - } - } - else - { - return FALSE; - } - } -} - -// -------------------------------------------------------------------- - -/** - * Symbolic Permissions - * - * Takes a numeric value representing a file's permissions and returns - * standard symbolic notation representing that value - * - * @access public - * @param int - * @return string - */ -if ( ! function_exists('symbolic_permissions')) -{ - function symbolic_permissions($perms) - { - if (($perms & 0xC000) == 0xC000) - { - $symbolic = 's'; // Socket - } - elseif (($perms & 0xA000) == 0xA000) - { - $symbolic = 'l'; // Symbolic Link - } - elseif (($perms & 0x8000) == 0x8000) - { - $symbolic = '-'; // Regular - } - elseif (($perms & 0x6000) == 0x6000) - { - $symbolic = 'b'; // Block special - } - elseif (($perms & 0x4000) == 0x4000) - { - $symbolic = 'd'; // Directory - } - elseif (($perms & 0x2000) == 0x2000) - { - $symbolic = 'c'; // Character special - } - elseif (($perms & 0x1000) == 0x1000) - { - $symbolic = 'p'; // FIFO pipe - } - else - { - $symbolic = 'u'; // Unknown - } - - // Owner - $symbolic .= (($perms & 0x0100) ? 'r' : '-'); - $symbolic .= (($perms & 0x0080) ? 'w' : '-'); - $symbolic .= (($perms & 0x0040) ? (($perms & 0x0800) ? 's' : 'x' ) : (($perms & 0x0800) ? 'S' : '-')); - - // Group - $symbolic .= (($perms & 0x0020) ? 'r' : '-'); - $symbolic .= (($perms & 0x0010) ? 'w' : '-'); - $symbolic .= (($perms & 0x0008) ? (($perms & 0x0400) ? 's' : 'x' ) : (($perms & 0x0400) ? 'S' : '-')); - - // World - $symbolic .= (($perms & 0x0004) ? 'r' : '-'); - $symbolic .= (($perms & 0x0002) ? 'w' : '-'); - $symbolic .= (($perms & 0x0001) ? (($perms & 0x0200) ? 't' : 'x' ) : (($perms & 0x0200) ? 'T' : '-')); - - return $symbolic; - } -} - -// -------------------------------------------------------------------- - -/** - * Octal Permissions - * - * Takes a numeric value representing a file's permissions and returns - * a three character string representing the file's octal permissions - * - * @access public - * @param int - * @return string - */ -if ( ! function_exists('octal_permissions')) -{ - function octal_permissions($perms) - { - return substr(sprintf('%o', $perms), -3); - } -} - - -/* End of file file_helper.php */ -/* Location: ./system/helpers/file_helper.php */ \ No newline at end of file diff --git a/system/helpers/form_helper.php b/system/helpers/form_helper.php deleted file mode 100644 index 8733ae05..00000000 --- a/system/helpers/form_helper.php +++ /dev/null @@ -1,1054 +0,0 @@ -config->site_url(http://github.com/$action); - } - - // If no action is provided then set to the current url - $action OR $action = $CI->config->site_url(http://github.com/$CI-%3Euri-%3Euri_string()); - - $form = 'config->item('csrf_protection') === TRUE AND ! (strpos($action, $CI->config->base_url()) === FALSE OR strpos($form, 'method="get"'))) - { - $hidden[$CI->security->get_csrf_token_name()] = $CI->security->get_csrf_hash(); - } - - if (is_array($hidden) AND count($hidden) > 0) - { - $form .= sprintf("
%s
", form_hidden($hidden)); - } - - return $form; - } -} - -// ------------------------------------------------------------------------ - -/** - * Form Declaration - Multipart type - * - * Creates the opening portion of the form, but with "multipart/form-data". - * - * @access public - * @param string the URI segments of the form destination - * @param array a key/value pair of attributes - * @param array a key/value pair hidden data - * @return string - */ -if ( ! function_exists('form_open_multipart')) -{ - function form_open_multipart($action = '', $attributes = array(), $hidden = array()) - { - if (is_string($attributes)) - { - $attributes .= ' enctype="multipart/form-data"'; - } - else - { - $attributes['enctype'] = 'multipart/form-data'; - } - - return form_open($action, $attributes, $hidden); - } -} - -// ------------------------------------------------------------------------ - -/** - * Hidden Input Field - * - * Generates hidden fields. You can pass a simple key/value string or an associative - * array with multiple values. - * - * @access public - * @param mixed - * @param string - * @return string - */ -if ( ! function_exists('form_hidden')) -{ - function form_hidden($name, $value = '', $recursing = FALSE) - { - static $form; - - if ($recursing === FALSE) - { - $form = "\n"; - } - - if (is_array($name)) - { - foreach ($name as $key => $val) - { - form_hidden($key, $val, TRUE); - } - return $form; - } - - if ( ! is_array($value)) - { - $form .= ''."\n"; - } - else - { - foreach ($value as $k => $v) - { - $k = (is_int($k)) ? '' : $k; - form_hidden($name.'['.$k.']', $v, TRUE); - } - } - - return $form; - } -} - -// ------------------------------------------------------------------------ - -/** - * Text Input Field - * - * @access public - * @param mixed - * @param string - * @param string - * @return string - */ -if ( ! function_exists('form_input')) -{ - function form_input($data = '', $value = '', $extra = '') - { - $defaults = array('type' => 'text', 'name' => (( ! is_array($data)) ? $data : ''), 'value' => $value); - - return ""; - } -} - -// ------------------------------------------------------------------------ - -/** - * Password Field - * - * Identical to the input function but adds the "password" type - * - * @access public - * @param mixed - * @param string - * @param string - * @return string - */ -if ( ! function_exists('form_password')) -{ - function form_password($data = '', $value = '', $extra = '') - { - if ( ! is_array($data)) - { - $data = array('name' => $data); - } - - $data['type'] = 'password'; - return form_input($data, $value, $extra); - } -} - -// ------------------------------------------------------------------------ - -/** - * Upload Field - * - * Identical to the input function but adds the "file" type - * - * @access public - * @param mixed - * @param string - * @param string - * @return string - */ -if ( ! function_exists('form_upload')) -{ - function form_upload($data = '', $value = '', $extra = '') - { - if ( ! is_array($data)) - { - $data = array('name' => $data); - } - - $data['type'] = 'file'; - return form_input($data, $value, $extra); - } -} - -// ------------------------------------------------------------------------ - -/** - * Textarea field - * - * @access public - * @param mixed - * @param string - * @param string - * @return string - */ -if ( ! function_exists('form_textarea')) -{ - function form_textarea($data = '', $value = '', $extra = '') - { - $defaults = array('name' => (( ! is_array($data)) ? $data : ''), 'cols' => '40', 'rows' => '10'); - - if ( ! is_array($data) OR ! isset($data['value'])) - { - $val = $value; - } - else - { - $val = $data['value']; - unset($data['value']); // textareas don't use the value attribute - } - - $name = (is_array($data)) ? $data['name'] : $data; - return ""; - } -} - -// ------------------------------------------------------------------------ - -/** - * Multi-select menu - * - * @access public - * @param string - * @param array - * @param mixed - * @param string - * @return type - */ -if ( ! function_exists('form_multiselect')) -{ - function form_multiselect($name = '', $options = array(), $selected = array(), $extra = '') - { - if ( ! strpos($extra, 'multiple')) - { - $extra .= ' multiple="multiple"'; - } - - return form_dropdown($name, $options, $selected, $extra); - } -} - -// -------------------------------------------------------------------- - -/** - * Drop-down Menu - * - * @access public - * @param string - * @param array - * @param string - * @param string - * @return string - */ -if ( ! function_exists('form_dropdown')) -{ - function form_dropdown($name = '', $options = array(), $selected = array(), $extra = '') - { - if ( ! is_array($selected)) - { - $selected = array($selected); - } - - // If no selected state was submitted we will attempt to set it automatically - if (count($selected) === 0) - { - // If the form name appears in the $_POST array we have a winner! - if (isset($_POST[$name])) - { - $selected = array($_POST[$name]); - } - } - - if ($extra != '') $extra = ' '.$extra; - - $multiple = (count($selected) > 1 && strpos($extra, 'multiple') === FALSE) ? ' multiple="multiple"' : ''; - - $form = ''; - - return $form; - } -} - -// ------------------------------------------------------------------------ - -/** - * Checkbox Field - * - * @access public - * @param mixed - * @param string - * @param bool - * @param string - * @return string - */ -if ( ! function_exists('form_checkbox')) -{ - function form_checkbox($data = '', $value = '', $checked = FALSE, $extra = '') - { - $defaults = array('type' => 'checkbox', 'name' => (( ! is_array($data)) ? $data : ''), 'value' => $value); - - if (is_array($data) AND array_key_exists('checked', $data)) - { - $checked = $data['checked']; - - if ($checked == FALSE) - { - unset($data['checked']); - } - else - { - $data['checked'] = 'checked'; - } - } - - if ($checked == TRUE) - { - $defaults['checked'] = 'checked'; - } - else - { - unset($defaults['checked']); - } - - return ""; - } -} - -// ------------------------------------------------------------------------ - -/** - * Radio Button - * - * @access public - * @param mixed - * @param string - * @param bool - * @param string - * @return string - */ -if ( ! function_exists('form_radio')) -{ - function form_radio($data = '', $value = '', $checked = FALSE, $extra = '') - { - if ( ! is_array($data)) - { - $data = array('name' => $data); - } - - $data['type'] = 'radio'; - return form_checkbox($data, $value, $checked, $extra); - } -} - -// ------------------------------------------------------------------------ - -/** - * Submit Button - * - * @access public - * @param mixed - * @param string - * @param string - * @return string - */ -if ( ! function_exists('form_submit')) -{ - function form_submit($data = '', $value = '', $extra = '') - { - $defaults = array('type' => 'submit', 'name' => (( ! is_array($data)) ? $data : ''), 'value' => $value); - - return ""; - } -} - -// ------------------------------------------------------------------------ - -/** - * Reset Button - * - * @access public - * @param mixed - * @param string - * @param string - * @return string - */ -if ( ! function_exists('form_reset')) -{ - function form_reset($data = '', $value = '', $extra = '') - { - $defaults = array('type' => 'reset', 'name' => (( ! is_array($data)) ? $data : ''), 'value' => $value); - - return ""; - } -} - -// ------------------------------------------------------------------------ - -/** - * Form Button - * - * @access public - * @param mixed - * @param string - * @param string - * @return string - */ -if ( ! function_exists('form_button')) -{ - function form_button($data = '', $content = '', $extra = '') - { - $defaults = array('name' => (( ! is_array($data)) ? $data : ''), 'type' => 'button'); - - if ( is_array($data) AND isset($data['content'])) - { - $content = $data['content']; - unset($data['content']); // content is not an attribute - } - - return ""; - } -} - -// ------------------------------------------------------------------------ - -/** - * Form Label Tag - * - * @access public - * @param string The text to appear onscreen - * @param string The id the label applies to - * @param string Additional attributes - * @return string - */ -if ( ! function_exists('form_label')) -{ - function form_label($label_text = '', $id = '', $attributes = array()) - { - - $label = ' 0) - { - foreach ($attributes as $key => $val) - { - $label .= ' '.$key.'="'.$val.'"'; - } - } - - $label .= ">$label_text"; - - return $label; - } -} - -// ------------------------------------------------------------------------ -/** - * Fieldset Tag - * - * Used to produce
text. To close fieldset - * use form_fieldset_close() - * - * @access public - * @param string The legend text - * @param string Additional attributes - * @return string - */ -if ( ! function_exists('form_fieldset')) -{ - function form_fieldset($legend_text = '', $attributes = array()) - { - $fieldset = "".$extra; - } -} - -// ------------------------------------------------------------------------ - -/** - * Form Close Tag - * - * @access public - * @param string - * @return string - */ -if ( ! function_exists('form_close')) -{ - function form_close($extra = '') - { - return "".$extra; - } -} - -// ------------------------------------------------------------------------ - -/** - * Form Prep - * - * Formats text so that it can be safely placed in a form field in the event it has HTML tags. - * - * @access public - * @param string - * @return string - */ -if ( ! function_exists('form_prep')) -{ - function form_prep($str = '', $field_name = '') - { - static $prepped_fields = array(); - - // if the field name is an array we do this recursively - if (is_array($str)) - { - foreach ($str as $key => $val) - { - $str[$key] = form_prep($val); - } - - return $str; - } - - if ($str === '') - { - return ''; - } - - // we've already prepped a field with this name - // @todo need to figure out a way to namespace this so - // that we know the *exact* field and not just one with - // the same name - if (isset($prepped_fields[$field_name])) - { - return $str; - } - - $str = htmlspecialchars($str); - - // In case htmlspecialchars misses these. - $str = str_replace(array("'", '"'), array("'", """), $str); - - if ($field_name != '') - { - $prepped_fields[$field_name] = $field_name; - } - - return $str; - } -} - -// ------------------------------------------------------------------------ - -/** - * Form Value - * - * Grabs a value from the POST array for the specified field so you can - * re-populate an input field or textarea. If Form Validation - * is active it retrieves the info from the validation class - * - * @access public - * @param string - * @return mixed - */ -if ( ! function_exists('set_value')) -{ - function set_value($field = '', $default = '') - { - if (FALSE === ($OBJ =& _get_validation_object())) - { - if ( ! isset($_POST[$field])) - { - return $default; - } - - return form_prep($_POST[$field], $field); - } - - return form_prep($OBJ->set_value($field, $default), $field); - } -} - -// ------------------------------------------------------------------------ - -/** - * Set Select - * - * Let's you set the selected value of a + + + + + + +
+

Page not found

+

The requested page could not be found.

+

You have probably clicked on a link that is outdated and points to a page that does not exist any more or you have made an typing error in the address.

+

To continue please try to find requested page in the menu, take a look at the tree view of the whole project or use search field on the top.

+
+ + + + + + + + diff --git a/documentation/class-Example.html b/documentation/class-Example.html new file mode 100644 index 00000000..5d10d511 --- /dev/null +++ b/documentation/class-Example.html @@ -0,0 +1,477 @@ + + + + + + Class Example + + + + + + +
+ +
+ +
+ + + + + + diff --git a/documentation/class-Format.html b/documentation/class-Format.html new file mode 100644 index 00000000..762585cd --- /dev/null +++ b/documentation/class-Format.html @@ -0,0 +1,555 @@ + + + + + + Class Format + + + + + + +
+ +
+ +
+ + + + + + diff --git a/documentation/class-Key.html b/documentation/class-Key.html new file mode 100644 index 00000000..ae46fc61 --- /dev/null +++ b/documentation/class-Key.html @@ -0,0 +1,454 @@ + + + + + + Class Key + + + + + + +
+ +
+ +
+ + + + + + diff --git a/documentation/class-REST_Controller.html b/documentation/class-REST_Controller.html new file mode 100644 index 00000000..74d8de5b --- /dev/null +++ b/documentation/class-REST_Controller.html @@ -0,0 +1,2292 @@ + + + + + + Class REST_Controller + + + + + + +
+ +
+ +
+ + + + + + diff --git a/documentation/elementlist.js b/documentation/elementlist.js new file mode 100644 index 00000000..7941e8a2 --- /dev/null +++ b/documentation/elementlist.js @@ -0,0 +1,3 @@ + +var ApiGen = ApiGen || {}; +ApiGen.elements = [["f","_perform_ldap_auth()"],["c","Example"],["c","Format"],["c","Key"],["c","REST_Controller"]]; diff --git a/documentation/function-_perform_ldap_auth.html b/documentation/function-_perform_ldap_auth.html new file mode 100644 index 00000000..af6e32cc --- /dev/null +++ b/documentation/function-_perform_ldap_auth.html @@ -0,0 +1,135 @@ + + + + + + Function _perform_ldap_auth + + + + + + +
+ +
+ +
+ + + + + + diff --git a/documentation/index.html b/documentation/index.html new file mode 100644 index 00000000..7790a6fe --- /dev/null +++ b/documentation/index.html @@ -0,0 +1,127 @@ + + + + + + Overview + + + + + + +
+ +
+ +
+ + + + + + diff --git a/documentation/package-CodeIgniter.Libraries.html b/documentation/package-CodeIgniter.Libraries.html new file mode 100644 index 00000000..c79d4217 --- /dev/null +++ b/documentation/package-CodeIgniter.Libraries.html @@ -0,0 +1,128 @@ + + + + + + Package CodeIgniter\Libraries + + + + + + +
+ +
+ +
+ + + + + + diff --git a/documentation/package-CodeIgniter.html b/documentation/package-CodeIgniter.html new file mode 100644 index 00000000..0b48c862 --- /dev/null +++ b/documentation/package-CodeIgniter.html @@ -0,0 +1,112 @@ + + + + + + Package CodeIgniter + + + + + + +
+ +
+ +
+ + + + + + diff --git a/documentation/package-None.html b/documentation/package-None.html new file mode 100644 index 00000000..9909f104 --- /dev/null +++ b/documentation/package-None.html @@ -0,0 +1,129 @@ + + + + + + No package + + + + + + +
+ +
+ +
+ + + + + + diff --git a/documentation/resources/collapsed.png b/documentation/resources/collapsed.png new file mode 100644 index 0000000000000000000000000000000000000000..56e7323931a3ca5774e2e85ba622c6282c122f5f GIT binary patch literal 238 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`Gjk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5XCV09yhE&{2`t$$4z4PH~`Tr$57mdKI;Vst0QmG<2><{9 literal 0 HcmV?d00001 diff --git a/documentation/resources/combined.js b/documentation/resources/combined.js new file mode 100644 index 00000000..45cd2fe1 --- /dev/null +++ b/documentation/resources/combined.js @@ -0,0 +1,1315 @@ + +var ApiGen = ApiGen || {}; +ApiGen.config = {"options":{"elementDetailsCollapsed":true,"elementsOrder":"natural"},"name":"ApiGen theme","templatesPath":"phar:\/\/\/usr\/local\/bin\/apigen\/src\/templates\/default","resources":{"phar:\/\/\/usr\/local\/bin\/apigen\/src\/templates\/default\/resources":"resources"},"templates":{"overview":{"filename":"index.html","template":"phar:\/\/\/usr\/local\/bin\/apigen\/src\/templates\/default\/overview.latte"},"combined":{"filename":"resources\/combined.js","template":"phar:\/\/\/usr\/local\/bin\/apigen\/src\/templates\/default\/combined.js.latte"},"elementlist":{"filename":"elementlist.js","template":"phar:\/\/\/usr\/local\/bin\/apigen\/src\/templates\/default\/elementlist.js.latte"},"404":{"filename":"404.html","template":"phar:\/\/\/usr\/local\/bin\/apigen\/src\/templates\/default\/404.latte"},"package":{"filename":"package-%s.html","template":"phar:\/\/\/usr\/local\/bin\/apigen\/src\/templates\/default\/package.latte"},"namespace":{"filename":"namespace-%s.html","template":"phar:\/\/\/usr\/local\/bin\/apigen\/src\/templates\/default\/namespace.latte"},"class":{"filename":"class-%s.html","template":"phar:\/\/\/usr\/local\/bin\/apigen\/src\/templates\/default\/class.latte"},"constant":{"filename":"constant-%s.html","template":"phar:\/\/\/usr\/local\/bin\/apigen\/src\/templates\/default\/constant.latte"},"function":{"filename":"function-%s.html","template":"phar:\/\/\/usr\/local\/bin\/apigen\/src\/templates\/default\/function.latte"},"source":{"filename":"source-%s.html","template":"phar:\/\/\/usr\/local\/bin\/apigen\/src\/templates\/default\/source.latte"},"tree":{"filename":"tree.html","template":"phar:\/\/\/usr\/local\/bin\/apigen\/src\/templates\/default\/tree.latte"},"deprecated":{"filename":"deprecated.html","template":"phar:\/\/\/usr\/local\/bin\/apigen\/src\/templates\/default\/deprecated.latte"},"todo":{"filename":"todo.html","template":"phar:\/\/\/usr\/local\/bin\/apigen\/src\/templates\/default\/todo.latte"},"sitemap":{"filename":"sitemap.xml","template":"phar:\/\/\/usr\/local\/bin\/apigen\/src\/templates\/default\/sitemap.xml.latte"},"opensearch":{"filename":"opensearch.xml","template":"phar:\/\/\/usr\/local\/bin\/apigen\/src\/templates\/default\/opensearch.xml.latte"},"robots":{"filename":"robots.txt","template":"phar:\/\/\/usr\/local\/bin\/apigen\/src\/templates\/default\/robots.txt.latte"}}}; + + + /*! jQuery v1.10.2 | (c) 2005, 2013 jQuery Foundation, Inc. | jquery.org/license +*/ +(function(e,t){var n,r,i=typeof t,o=e.location,a=e.document,s=a.documentElement,l=e.jQuery,u=e.$,c={},p=[],f="1.10.2",d=p.concat,h=p.push,g=p.slice,m=p.indexOf,y=c.toString,v=c.hasOwnProperty,b=f.trim,x=function(e,t){return new x.fn.init(e,t,r)},w=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,T=/\S+/g,C=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,N=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,k=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,E=/^[\],:{}\s]*$/,S=/(?:^|:|,)(?:\s*\[)+/g,A=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,j=/"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g,D=/^-ms-/,L=/-([\da-z])/gi,H=function(e,t){return t.toUpperCase()},q=function(e){(a.addEventListener||"load"===e.type||"complete"===a.readyState)&&(_(),x.ready())},_=function(){a.addEventListener?(a.removeEventListener("DOMContentLoaded",q,!1),e.removeEventListener("load",q,!1)):(a.detachEvent("onreadystatechange",q),e.detachEvent("onload",q))};x.fn=x.prototype={jquery:f,constructor:x,init:function(e,n,r){var i,o;if(!e)return this;if("string"==typeof e){if(i="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:N.exec(e),!i||!i[1]&&n)return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e);if(i[1]){if(n=n instanceof x?n[0]:n,x.merge(this,x.parseHTML(i[1],n&&n.nodeType?n.ownerDocument||n:a,!0)),k.test(i[1])&&x.isPlainObject(n))for(i in n)x.isFunction(this[i])?this[i](n[i]):this.attr(i,n[i]);return this}if(o=a.getElementById(i[2]),o&&o.parentNode){if(o.id!==i[2])return r.find(e);this.length=1,this[0]=o}return this.context=a,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):x.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),x.makeArray(e,this))},selector:"",length:0,toArray:function(){return g.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e){var t=x.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return x.each(this,e,t)},ready:function(e){return x.ready.promise().done(e),this},slice:function(){return this.pushStack(g.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},map:function(e){return this.pushStack(x.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:h,sort:[].sort,splice:[].splice},x.fn.init.prototype=x.fn,x.extend=x.fn.extend=function(){var e,n,r,i,o,a,s=arguments[0]||{},l=1,u=arguments.length,c=!1;for("boolean"==typeof s&&(c=s,s=arguments[1]||{},l=2),"object"==typeof s||x.isFunction(s)||(s={}),u===l&&(s=this,--l);u>l;l++)if(null!=(o=arguments[l]))for(i in o)e=s[i],r=o[i],s!==r&&(c&&r&&(x.isPlainObject(r)||(n=x.isArray(r)))?(n?(n=!1,a=e&&x.isArray(e)?e:[]):a=e&&x.isPlainObject(e)?e:{},s[i]=x.extend(c,a,r)):r!==t&&(s[i]=r));return s},x.extend({expando:"jQuery"+(f+Math.random()).replace(/\D/g,""),noConflict:function(t){return e.$===x&&(e.$=u),t&&e.jQuery===x&&(e.jQuery=l),x},isReady:!1,readyWait:1,holdReady:function(e){e?x.readyWait++:x.ready(!0)},ready:function(e){if(e===!0?!--x.readyWait:!x.isReady){if(!a.body)return setTimeout(x.ready);x.isReady=!0,e!==!0&&--x.readyWait>0||(n.resolveWith(a,[x]),x.fn.trigger&&x(a).trigger("ready").off("ready"))}},isFunction:function(e){return"function"===x.type(e)},isArray:Array.isArray||function(e){return"array"===x.type(e)},isWindow:function(e){return null!=e&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?c[y.call(e)]||"object":typeof e},isPlainObject:function(e){var n;if(!e||"object"!==x.type(e)||e.nodeType||x.isWindow(e))return!1;try{if(e.constructor&&!v.call(e,"constructor")&&!v.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(r){return!1}if(x.support.ownLast)for(n in e)return v.call(e,n);for(n in e);return n===t||v.call(e,n)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw Error(e)},parseHTML:function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||a;var r=k.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=x.buildFragment([e],t,i),i&&x(i).remove(),x.merge([],r.childNodes))},parseJSON:function(n){return e.JSON&&e.JSON.parse?e.JSON.parse(n):null===n?n:"string"==typeof n&&(n=x.trim(n),n&&E.test(n.replace(A,"@").replace(j,"]").replace(S,"")))?Function("return "+n)():(x.error("Invalid JSON: "+n),t)},parseXML:function(n){var r,i;if(!n||"string"!=typeof n)return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(o){r=t}return r&&r.documentElement&&!r.getElementsByTagName("parsererror").length||x.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&x.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(D,"ms-").replace(L,H)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,n){var r,i=0,o=e.length,a=M(e);if(n){if(a){for(;o>i;i++)if(r=t.apply(e[i],n),r===!1)break}else for(i in e)if(r=t.apply(e[i],n),r===!1)break}else if(a){for(;o>i;i++)if(r=t.call(e[i],i,e[i]),r===!1)break}else for(i in e)if(r=t.call(e[i],i,e[i]),r===!1)break;return e},trim:b&&!b.call("\ufeff\u00a0")?function(e){return null==e?"":b.call(e)}:function(e){return null==e?"":(e+"").replace(C,"")},makeArray:function(e,t){var n=t||[];return null!=e&&(M(Object(e))?x.merge(n,"string"==typeof e?[e]:e):h.call(n,e)),n},inArray:function(e,t,n){var r;if(t){if(m)return m.call(t,e,n);for(r=t.length,n=n?0>n?Math.max(0,r+n):n:0;r>n;n++)if(n in t&&t[n]===e)return n}return-1},merge:function(e,n){var r=n.length,i=e.length,o=0;if("number"==typeof r)for(;r>o;o++)e[i++]=n[o];else while(n[o]!==t)e[i++]=n[o++];return e.length=i,e},grep:function(e,t,n){var r,i=[],o=0,a=e.length;for(n=!!n;a>o;o++)r=!!t(e[o],o),n!==r&&i.push(e[o]);return i},map:function(e,t,n){var r,i=0,o=e.length,a=M(e),s=[];if(a)for(;o>i;i++)r=t(e[i],i,n),null!=r&&(s[s.length]=r);else for(i in e)r=t(e[i],i,n),null!=r&&(s[s.length]=r);return d.apply([],s)},guid:1,proxy:function(e,n){var r,i,o;return"string"==typeof n&&(o=e[n],n=e,e=o),x.isFunction(e)?(r=g.call(arguments,2),i=function(){return e.apply(n||this,r.concat(g.call(arguments)))},i.guid=e.guid=e.guid||x.guid++,i):t},access:function(e,n,r,i,o,a,s){var l=0,u=e.length,c=null==r;if("object"===x.type(r)){o=!0;for(l in r)x.access(e,n,l,r[l],!0,a,s)}else if(i!==t&&(o=!0,x.isFunction(i)||(s=!0),c&&(s?(n.call(e,i),n=null):(c=n,n=function(e,t,n){return c.call(x(e),n)})),n))for(;u>l;l++)n(e[l],r,s?i:i.call(e[l],l,n(e[l],r)));return o?e:c?n.call(e):u?n(e[0],r):a},now:function(){return(new Date).getTime()},swap:function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=a[o];return i}}),x.ready.promise=function(t){if(!n)if(n=x.Deferred(),"complete"===a.readyState)setTimeout(x.ready);else if(a.addEventListener)a.addEventListener("DOMContentLoaded",q,!1),e.addEventListener("load",q,!1);else{a.attachEvent("onreadystatechange",q),e.attachEvent("onload",q);var r=!1;try{r=null==e.frameElement&&a.documentElement}catch(i){}r&&r.doScroll&&function o(){if(!x.isReady){try{r.doScroll("left")}catch(e){return setTimeout(o,50)}_(),x.ready()}}()}return n.promise(t)},x.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(e,t){c["[object "+t+"]"]=t.toLowerCase()});function M(e){var t=e.length,n=x.type(e);return x.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||"function"!==n&&(0===t||"number"==typeof t&&t>0&&t-1 in e)}r=x(a),function(e,t){var n,r,i,o,a,s,l,u,c,p,f,d,h,g,m,y,v,b="sizzle"+-new Date,w=e.document,T=0,C=0,N=st(),k=st(),E=st(),S=!1,A=function(e,t){return e===t?(S=!0,0):0},j=typeof t,D=1<<31,L={}.hasOwnProperty,H=[],q=H.pop,_=H.push,M=H.push,O=H.slice,F=H.indexOf||function(e){var t=0,n=this.length;for(;n>t;t++)if(this[t]===e)return t;return-1},B="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",P="[\\x20\\t\\r\\n\\f]",R="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",W=R.replace("w","w#"),$="\\["+P+"*("+R+")"+P+"*(?:([*^$|!~]?=)"+P+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+W+")|)|)"+P+"*\\]",I=":("+R+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+$.replace(3,8)+")*)|.*)\\)|)",z=RegExp("^"+P+"+|((?:^|[^\\\\])(?:\\\\.)*)"+P+"+$","g"),X=RegExp("^"+P+"*,"+P+"*"),U=RegExp("^"+P+"*([>+~]|"+P+")"+P+"*"),V=RegExp(P+"*[+~]"),Y=RegExp("="+P+"*([^\\]'\"]*)"+P+"*\\]","g"),J=RegExp(I),G=RegExp("^"+W+"$"),Q={ID:RegExp("^#("+R+")"),CLASS:RegExp("^\\.("+R+")"),TAG:RegExp("^("+R.replace("w","w*")+")"),ATTR:RegExp("^"+$),PSEUDO:RegExp("^"+I),CHILD:RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+P+"*(even|odd|(([+-]|)(\\d*)n|)"+P+"*(?:([+-]|)"+P+"*(\\d+)|))"+P+"*\\)|)","i"),bool:RegExp("^(?:"+B+")$","i"),needsContext:RegExp("^"+P+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+P+"*((?:-\\d)?\\d*)"+P+"*\\)|)(?=[^-]|$)","i")},K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,et=/^(?:input|select|textarea|button)$/i,tt=/^h\d$/i,nt=/'|\\/g,rt=RegExp("\\\\([\\da-f]{1,6}"+P+"?|("+P+")|.)","ig"),it=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:0>r?String.fromCharCode(r+65536):String.fromCharCode(55296|r>>10,56320|1023&r)};try{M.apply(H=O.call(w.childNodes),w.childNodes),H[w.childNodes.length].nodeType}catch(ot){M={apply:H.length?function(e,t){_.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function at(e,t,n,i){var o,a,s,l,u,c,d,m,y,x;if((t?t.ownerDocument||t:w)!==f&&p(t),t=t||f,n=n||[],!e||"string"!=typeof e)return n;if(1!==(l=t.nodeType)&&9!==l)return[];if(h&&!i){if(o=Z.exec(e))if(s=o[1]){if(9===l){if(a=t.getElementById(s),!a||!a.parentNode)return n;if(a.id===s)return n.push(a),n}else if(t.ownerDocument&&(a=t.ownerDocument.getElementById(s))&&v(t,a)&&a.id===s)return n.push(a),n}else{if(o[2])return M.apply(n,t.getElementsByTagName(e)),n;if((s=o[3])&&r.getElementsByClassName&&t.getElementsByClassName)return M.apply(n,t.getElementsByClassName(s)),n}if(r.qsa&&(!g||!g.test(e))){if(m=d=b,y=t,x=9===l&&e,1===l&&"object"!==t.nodeName.toLowerCase()){c=mt(e),(d=t.getAttribute("id"))?m=d.replace(nt,"\\$&"):t.setAttribute("id",m),m="[id='"+m+"'] ",u=c.length;while(u--)c[u]=m+yt(c[u]);y=V.test(e)&&t.parentNode||t,x=c.join(",")}if(x)try{return M.apply(n,y.querySelectorAll(x)),n}catch(T){}finally{d||t.removeAttribute("id")}}}return kt(e.replace(z,"$1"),t,n,i)}function st(){var e=[];function t(n,r){return e.push(n+=" ")>o.cacheLength&&delete t[e.shift()],t[n]=r}return t}function lt(e){return e[b]=!0,e}function ut(e){var t=f.createElement("div");try{return!!e(t)}catch(n){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function ct(e,t){var n=e.split("|"),r=e.length;while(r--)o.attrHandle[n[r]]=t}function pt(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&(~t.sourceIndex||D)-(~e.sourceIndex||D);if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function ft(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function dt(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function ht(e){return lt(function(t){return t=+t,lt(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}s=at.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},r=at.support={},p=at.setDocument=function(e){var n=e?e.ownerDocument||e:w,i=n.defaultView;return n!==f&&9===n.nodeType&&n.documentElement?(f=n,d=n.documentElement,h=!s(n),i&&i.attachEvent&&i!==i.top&&i.attachEvent("onbeforeunload",function(){p()}),r.attributes=ut(function(e){return e.className="i",!e.getAttribute("className")}),r.getElementsByTagName=ut(function(e){return e.appendChild(n.createComment("")),!e.getElementsByTagName("*").length}),r.getElementsByClassName=ut(function(e){return e.innerHTML="
",e.firstChild.className="i",2===e.getElementsByClassName("i").length}),r.getById=ut(function(e){return d.appendChild(e).id=b,!n.getElementsByName||!n.getElementsByName(b).length}),r.getById?(o.find.ID=function(e,t){if(typeof t.getElementById!==j&&h){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},o.filter.ID=function(e){var t=e.replace(rt,it);return function(e){return e.getAttribute("id")===t}}):(delete o.find.ID,o.filter.ID=function(e){var t=e.replace(rt,it);return function(e){var n=typeof e.getAttributeNode!==j&&e.getAttributeNode("id");return n&&n.value===t}}),o.find.TAG=r.getElementsByTagName?function(e,n){return typeof n.getElementsByTagName!==j?n.getElementsByTagName(e):t}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},o.find.CLASS=r.getElementsByClassName&&function(e,n){return typeof n.getElementsByClassName!==j&&h?n.getElementsByClassName(e):t},m=[],g=[],(r.qsa=K.test(n.querySelectorAll))&&(ut(function(e){e.innerHTML="",e.querySelectorAll("[selected]").length||g.push("\\["+P+"*(?:value|"+B+")"),e.querySelectorAll(":checked").length||g.push(":checked")}),ut(function(e){var t=n.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("t",""),e.querySelectorAll("[t^='']").length&&g.push("[*^$]="+P+"*(?:''|\"\")"),e.querySelectorAll(":enabled").length||g.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),g.push(",.*:")})),(r.matchesSelector=K.test(y=d.webkitMatchesSelector||d.mozMatchesSelector||d.oMatchesSelector||d.msMatchesSelector))&&ut(function(e){r.disconnectedMatch=y.call(e,"div"),y.call(e,"[s!='']:x"),m.push("!=",I)}),g=g.length&&RegExp(g.join("|")),m=m.length&&RegExp(m.join("|")),v=K.test(d.contains)||d.compareDocumentPosition?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},A=d.compareDocumentPosition?function(e,t){if(e===t)return S=!0,0;var i=t.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(t);return i?1&i||!r.sortDetached&&t.compareDocumentPosition(e)===i?e===n||v(w,e)?-1:t===n||v(w,t)?1:c?F.call(c,e)-F.call(c,t):0:4&i?-1:1:e.compareDocumentPosition?-1:1}:function(e,t){var r,i=0,o=e.parentNode,a=t.parentNode,s=[e],l=[t];if(e===t)return S=!0,0;if(!o||!a)return e===n?-1:t===n?1:o?-1:a?1:c?F.call(c,e)-F.call(c,t):0;if(o===a)return pt(e,t);r=e;while(r=r.parentNode)s.unshift(r);r=t;while(r=r.parentNode)l.unshift(r);while(s[i]===l[i])i++;return i?pt(s[i],l[i]):s[i]===w?-1:l[i]===w?1:0},n):f},at.matches=function(e,t){return at(e,null,null,t)},at.matchesSelector=function(e,t){if((e.ownerDocument||e)!==f&&p(e),t=t.replace(Y,"='$1']"),!(!r.matchesSelector||!h||m&&m.test(t)||g&&g.test(t)))try{var n=y.call(e,t);if(n||r.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(i){}return at(t,f,null,[e]).length>0},at.contains=function(e,t){return(e.ownerDocument||e)!==f&&p(e),v(e,t)},at.attr=function(e,n){(e.ownerDocument||e)!==f&&p(e);var i=o.attrHandle[n.toLowerCase()],a=i&&L.call(o.attrHandle,n.toLowerCase())?i(e,n,!h):t;return a===t?r.attributes||!h?e.getAttribute(n):(a=e.getAttributeNode(n))&&a.specified?a.value:null:a},at.error=function(e){throw Error("Syntax error, unrecognized expression: "+e)},at.uniqueSort=function(e){var t,n=[],i=0,o=0;if(S=!r.detectDuplicates,c=!r.sortStable&&e.slice(0),e.sort(A),S){while(t=e[o++])t===e[o]&&(i=n.push(o));while(i--)e.splice(n[i],1)}return e},a=at.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=a(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r];r++)n+=a(t);return n},o=at.selectors={cacheLength:50,createPseudo:lt,match:Q,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(rt,it),e[3]=(e[4]||e[5]||"").replace(rt,it),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||at.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&at.error(e[0]),e},PSEUDO:function(e){var n,r=!e[5]&&e[2];return Q.CHILD.test(e[0])?null:(e[3]&&e[4]!==t?e[2]=e[4]:r&&J.test(r)&&(n=mt(r,!0))&&(n=r.indexOf(")",r.length-n)-r.length)&&(e[0]=e[0].slice(0,n),e[2]=r.slice(0,n)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(rt,it).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=N[e+" "];return t||(t=RegExp("(^|"+P+")"+e+"("+P+"|$)"))&&N(e,function(e){return t.test("string"==typeof e.className&&e.className||typeof e.getAttribute!==j&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=at.attr(r,e);return null==i?"!="===t:t?(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i+" ").indexOf(n)>-1:"|="===t?i===n||i.slice(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,l){var u,c,p,f,d,h,g=o!==a?"nextSibling":"previousSibling",m=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!l&&!s;if(m){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===y:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?m.firstChild:m.lastChild],a&&v){c=m[b]||(m[b]={}),u=c[e]||[],d=u[0]===T&&u[1],f=u[0]===T&&u[2],p=d&&m.childNodes[d];while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if(1===p.nodeType&&++f&&p===t){c[e]=[T,d,f];break}}else if(v&&(u=(t[b]||(t[b]={}))[e])&&u[0]===T)f=u[1];else while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===y:1===p.nodeType)&&++f&&(v&&((p[b]||(p[b]={}))[e]=[T,f]),p===t))break;return f-=i,f===r||0===f%r&&f/r>=0}}},PSEUDO:function(e,t){var n,r=o.pseudos[e]||o.setFilters[e.toLowerCase()]||at.error("unsupported pseudo: "+e);return r[b]?r(t):r.length>1?(n=[e,e,"",t],o.setFilters.hasOwnProperty(e.toLowerCase())?lt(function(e,n){var i,o=r(e,t),a=o.length;while(a--)i=F.call(e,o[a]),e[i]=!(n[i]=o[a])}):function(e){return r(e,0,n)}):r}},pseudos:{not:lt(function(e){var t=[],n=[],r=l(e.replace(z,"$1"));return r[b]?lt(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),!n.pop()}}),has:lt(function(e){return function(t){return at(e,t).length>0}}),contains:lt(function(e){return function(t){return(t.textContent||t.innerText||a(t)).indexOf(e)>-1}}),lang:lt(function(e){return G.test(e||"")||at.error("unsupported lang: "+e),e=e.replace(rt,it).toLowerCase(),function(t){var n;do if(n=h?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===d},focus:function(e){return e===f.activeElement&&(!f.hasFocus||f.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeName>"@"||3===e.nodeType||4===e.nodeType)return!1;return!0},parent:function(e){return!o.pseudos.empty(e)},header:function(e){return tt.test(e.nodeName)},input:function(e){return et.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||t.toLowerCase()===e.type)},first:ht(function(){return[0]}),last:ht(function(e,t){return[t-1]}),eq:ht(function(e,t,n){return[0>n?n+t:n]}),even:ht(function(e,t){var n=0;for(;t>n;n+=2)e.push(n);return e}),odd:ht(function(e,t){var n=1;for(;t>n;n+=2)e.push(n);return e}),lt:ht(function(e,t,n){var r=0>n?n+t:n;for(;--r>=0;)e.push(r);return e}),gt:ht(function(e,t,n){var r=0>n?n+t:n;for(;t>++r;)e.push(r);return e})}},o.pseudos.nth=o.pseudos.eq;for(n in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})o.pseudos[n]=ft(n);for(n in{submit:!0,reset:!0})o.pseudos[n]=dt(n);function gt(){}gt.prototype=o.filters=o.pseudos,o.setFilters=new gt;function mt(e,t){var n,r,i,a,s,l,u,c=k[e+" "];if(c)return t?0:c.slice(0);s=e,l=[],u=o.preFilter;while(s){(!n||(r=X.exec(s)))&&(r&&(s=s.slice(r[0].length)||s),l.push(i=[])),n=!1,(r=U.exec(s))&&(n=r.shift(),i.push({value:n,type:r[0].replace(z," ")}),s=s.slice(n.length));for(a in o.filter)!(r=Q[a].exec(s))||u[a]&&!(r=u[a](r))||(n=r.shift(),i.push({value:n,type:a,matches:r}),s=s.slice(n.length));if(!n)break}return t?s.length:s?at.error(e):k(e,l).slice(0)}function yt(e){var t=0,n=e.length,r="";for(;n>t;t++)r+=e[t].value;return r}function vt(e,t,n){var r=t.dir,o=n&&"parentNode"===r,a=C++;return t.first?function(t,n,i){while(t=t[r])if(1===t.nodeType||o)return e(t,n,i)}:function(t,n,s){var l,u,c,p=T+" "+a;if(s){while(t=t[r])if((1===t.nodeType||o)&&e(t,n,s))return!0}else while(t=t[r])if(1===t.nodeType||o)if(c=t[b]||(t[b]={}),(u=c[r])&&u[0]===p){if((l=u[1])===!0||l===i)return l===!0}else if(u=c[r]=[p],u[1]=e(t,n,s)||i,u[1]===!0)return!0}}function bt(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function xt(e,t,n,r,i){var o,a=[],s=0,l=e.length,u=null!=t;for(;l>s;s++)(o=e[s])&&(!n||n(o,r,i))&&(a.push(o),u&&t.push(s));return a}function wt(e,t,n,r,i,o){return r&&!r[b]&&(r=wt(r)),i&&!i[b]&&(i=wt(i,o)),lt(function(o,a,s,l){var u,c,p,f=[],d=[],h=a.length,g=o||Nt(t||"*",s.nodeType?[s]:s,[]),m=!e||!o&&t?g:xt(g,f,e,s,l),y=n?i||(o?e:h||r)?[]:a:m;if(n&&n(m,y,s,l),r){u=xt(y,d),r(u,[],s,l),c=u.length;while(c--)(p=u[c])&&(y[d[c]]=!(m[d[c]]=p))}if(o){if(i||e){if(i){u=[],c=y.length;while(c--)(p=y[c])&&u.push(m[c]=p);i(null,y=[],u,l)}c=y.length;while(c--)(p=y[c])&&(u=i?F.call(o,p):f[c])>-1&&(o[u]=!(a[u]=p))}}else y=xt(y===a?y.splice(h,y.length):y),i?i(null,a,y,l):M.apply(a,y)})}function Tt(e){var t,n,r,i=e.length,a=o.relative[e[0].type],s=a||o.relative[" "],l=a?1:0,c=vt(function(e){return e===t},s,!0),p=vt(function(e){return F.call(t,e)>-1},s,!0),f=[function(e,n,r){return!a&&(r||n!==u)||((t=n).nodeType?c(e,n,r):p(e,n,r))}];for(;i>l;l++)if(n=o.relative[e[l].type])f=[vt(bt(f),n)];else{if(n=o.filter[e[l].type].apply(null,e[l].matches),n[b]){for(r=++l;i>r;r++)if(o.relative[e[r].type])break;return wt(l>1&&bt(f),l>1&&yt(e.slice(0,l-1).concat({value:" "===e[l-2].type?"*":""})).replace(z,"$1"),n,r>l&&Tt(e.slice(l,r)),i>r&&Tt(e=e.slice(r)),i>r&&yt(e))}f.push(n)}return bt(f)}function Ct(e,t){var n=0,r=t.length>0,a=e.length>0,s=function(s,l,c,p,d){var h,g,m,y=[],v=0,b="0",x=s&&[],w=null!=d,C=u,N=s||a&&o.find.TAG("*",d&&l.parentNode||l),k=T+=null==C?1:Math.random()||.1;for(w&&(u=l!==f&&l,i=n);null!=(h=N[b]);b++){if(a&&h){g=0;while(m=e[g++])if(m(h,l,c)){p.push(h);break}w&&(T=k,i=++n)}r&&((h=!m&&h)&&v--,s&&x.push(h))}if(v+=b,r&&b!==v){g=0;while(m=t[g++])m(x,y,l,c);if(s){if(v>0)while(b--)x[b]||y[b]||(y[b]=q.call(p));y=xt(y)}M.apply(p,y),w&&!s&&y.length>0&&v+t.length>1&&at.uniqueSort(p)}return w&&(T=k,u=C),x};return r?lt(s):s}l=at.compile=function(e,t){var n,r=[],i=[],o=E[e+" "];if(!o){t||(t=mt(e)),n=t.length;while(n--)o=Tt(t[n]),o[b]?r.push(o):i.push(o);o=E(e,Ct(i,r))}return o};function Nt(e,t,n){var r=0,i=t.length;for(;i>r;r++)at(e,t[r],n);return n}function kt(e,t,n,i){var a,s,u,c,p,f=mt(e);if(!i&&1===f.length){if(s=f[0]=f[0].slice(0),s.length>2&&"ID"===(u=s[0]).type&&r.getById&&9===t.nodeType&&h&&o.relative[s[1].type]){if(t=(o.find.ID(u.matches[0].replace(rt,it),t)||[])[0],!t)return n;e=e.slice(s.shift().value.length)}a=Q.needsContext.test(e)?0:s.length;while(a--){if(u=s[a],o.relative[c=u.type])break;if((p=o.find[c])&&(i=p(u.matches[0].replace(rt,it),V.test(s[0].type)&&t.parentNode||t))){if(s.splice(a,1),e=i.length&&yt(s),!e)return M.apply(n,i),n;break}}}return l(e,f)(i,t,!h,n,V.test(e)),n}r.sortStable=b.split("").sort(A).join("")===b,r.detectDuplicates=S,p(),r.sortDetached=ut(function(e){return 1&e.compareDocumentPosition(f.createElement("div"))}),ut(function(e){return e.innerHTML="","#"===e.firstChild.getAttribute("href")})||ct("type|href|height|width",function(e,n,r){return r?t:e.getAttribute(n,"type"===n.toLowerCase()?1:2)}),r.attributes&&ut(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||ct("value",function(e,n,r){return r||"input"!==e.nodeName.toLowerCase()?t:e.defaultValue}),ut(function(e){return null==e.getAttribute("disabled")})||ct(B,function(e,n,r){var i;return r?t:(i=e.getAttributeNode(n))&&i.specified?i.value:e[n]===!0?n.toLowerCase():null}),x.find=at,x.expr=at.selectors,x.expr[":"]=x.expr.pseudos,x.unique=at.uniqueSort,x.text=at.getText,x.isXMLDoc=at.isXML,x.contains=at.contains}(e);var O={};function F(e){var t=O[e]={};return x.each(e.match(T)||[],function(e,n){t[n]=!0}),t}x.Callbacks=function(e){e="string"==typeof e?O[e]||F(e):x.extend({},e);var n,r,i,o,a,s,l=[],u=!e.once&&[],c=function(t){for(r=e.memory&&t,i=!0,a=s||0,s=0,o=l.length,n=!0;l&&o>a;a++)if(l[a].apply(t[0],t[1])===!1&&e.stopOnFalse){r=!1;break}n=!1,l&&(u?u.length&&c(u.shift()):r?l=[]:p.disable())},p={add:function(){if(l){var t=l.length;(function i(t){x.each(t,function(t,n){var r=x.type(n);"function"===r?e.unique&&p.has(n)||l.push(n):n&&n.length&&"string"!==r&&i(n)})})(arguments),n?o=l.length:r&&(s=t,c(r))}return this},remove:function(){return l&&x.each(arguments,function(e,t){var r;while((r=x.inArray(t,l,r))>-1)l.splice(r,1),n&&(o>=r&&o--,a>=r&&a--)}),this},has:function(e){return e?x.inArray(e,l)>-1:!(!l||!l.length)},empty:function(){return l=[],o=0,this},disable:function(){return l=u=r=t,this},disabled:function(){return!l},lock:function(){return u=t,r||p.disable(),this},locked:function(){return!u},fireWith:function(e,t){return!l||i&&!u||(t=t||[],t=[e,t.slice?t.slice():t],n?u.push(t):c(t)),this},fire:function(){return p.fireWith(this,arguments),this},fired:function(){return!!i}};return p},x.extend({Deferred:function(e){var t=[["resolve","done",x.Callbacks("once memory"),"resolved"],["reject","fail",x.Callbacks("once memory"),"rejected"],["notify","progress",x.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return x.Deferred(function(n){x.each(t,function(t,o){var a=o[0],s=x.isFunction(e[t])&&e[t];i[o[1]](function(){var e=s&&s.apply(this,arguments);e&&x.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[a+"With"](this===r?n.promise():this,s?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?x.extend(e,r):r}},i={};return r.pipe=r.then,x.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=g.call(arguments),r=n.length,i=1!==r||e&&x.isFunction(e.promise)?r:0,o=1===i?e:x.Deferred(),a=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?g.call(arguments):r,n===s?o.notifyWith(t,n):--i||o.resolveWith(t,n)}},s,l,u;if(r>1)for(s=Array(r),l=Array(r),u=Array(r);r>t;t++)n[t]&&x.isFunction(n[t].promise)?n[t].promise().done(a(t,u,n)).fail(o.reject).progress(a(t,l,s)):--i;return i||o.resolveWith(u,n),o.promise()}}),x.support=function(t){var n,r,o,s,l,u,c,p,f,d=a.createElement("div");if(d.setAttribute("className","t"),d.innerHTML="
a",n=d.getElementsByTagName("*")||[],r=d.getElementsByTagName("a")[0],!r||!r.style||!n.length)return t;s=a.createElement("select"),u=s.appendChild(a.createElement("option")),o=d.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t.getSetAttribute="t"!==d.className,t.leadingWhitespace=3===d.firstChild.nodeType,t.tbody=!d.getElementsByTagName("tbody").length,t.htmlSerialize=!!d.getElementsByTagName("link").length,t.style=/top/.test(r.getAttribute("style")),t.hrefNormalized="/a"===r.getAttribute("href"),t.opacity=/^0.5/.test(r.style.opacity),t.cssFloat=!!r.style.cssFloat,t.checkOn=!!o.value,t.optSelected=u.selected,t.enctype=!!a.createElement("form").enctype,t.html5Clone="<:nav>"!==a.createElement("nav").cloneNode(!0).outerHTML,t.inlineBlockNeedsLayout=!1,t.shrinkWrapBlocks=!1,t.pixelPosition=!1,t.deleteExpando=!0,t.noCloneEvent=!0,t.reliableMarginRight=!0,t.boxSizingReliable=!0,o.checked=!0,t.noCloneChecked=o.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!u.disabled;try{delete d.test}catch(h){t.deleteExpando=!1}o=a.createElement("input"),o.setAttribute("value",""),t.input=""===o.getAttribute("value"),o.value="t",o.setAttribute("type","radio"),t.radioValue="t"===o.value,o.setAttribute("checked","t"),o.setAttribute("name","t"),l=a.createDocumentFragment(),l.appendChild(o),t.appendChecked=o.checked,t.checkClone=l.cloneNode(!0).cloneNode(!0).lastChild.checked,d.attachEvent&&(d.attachEvent("onclick",function(){t.noCloneEvent=!1}),d.cloneNode(!0).click());for(f in{submit:!0,change:!0,focusin:!0})d.setAttribute(c="on"+f,"t"),t[f+"Bubbles"]=c in e||d.attributes[c].expando===!1;d.style.backgroundClip="content-box",d.cloneNode(!0).style.backgroundClip="",t.clearCloneStyle="content-box"===d.style.backgroundClip;for(f in x(t))break;return t.ownLast="0"!==f,x(function(){var n,r,o,s="padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",l=a.getElementsByTagName("body")[0];l&&(n=a.createElement("div"),n.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px",l.appendChild(n).appendChild(d),d.innerHTML="
t
",o=d.getElementsByTagName("td"),o[0].style.cssText="padding:0;margin:0;border:0;display:none",p=0===o[0].offsetHeight,o[0].style.display="",o[1].style.display="none",t.reliableHiddenOffsets=p&&0===o[0].offsetHeight,d.innerHTML="",d.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",x.swap(l,null!=l.style.zoom?{zoom:1}:{},function(){t.boxSizing=4===d.offsetWidth}),e.getComputedStyle&&(t.pixelPosition="1%"!==(e.getComputedStyle(d,null)||{}).top,t.boxSizingReliable="4px"===(e.getComputedStyle(d,null)||{width:"4px"}).width,r=d.appendChild(a.createElement("div")),r.style.cssText=d.style.cssText=s,r.style.marginRight=r.style.width="0",d.style.width="1px",t.reliableMarginRight=!parseFloat((e.getComputedStyle(r,null)||{}).marginRight)),typeof d.style.zoom!==i&&(d.innerHTML="",d.style.cssText=s+"width:1px;padding:1px;display:inline;zoom:1",t.inlineBlockNeedsLayout=3===d.offsetWidth,d.style.display="block",d.innerHTML="
",d.firstChild.style.width="5px",t.shrinkWrapBlocks=3!==d.offsetWidth,t.inlineBlockNeedsLayout&&(l.style.zoom=1)),l.removeChild(n),n=d=o=r=null)}),n=s=l=u=r=o=null,t +}({});var B=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,P=/([A-Z])/g;function R(e,n,r,i){if(x.acceptData(e)){var o,a,s=x.expando,l=e.nodeType,u=l?x.cache:e,c=l?e[s]:e[s]&&s;if(c&&u[c]&&(i||u[c].data)||r!==t||"string"!=typeof n)return c||(c=l?e[s]=p.pop()||x.guid++:s),u[c]||(u[c]=l?{}:{toJSON:x.noop}),("object"==typeof n||"function"==typeof n)&&(i?u[c]=x.extend(u[c],n):u[c].data=x.extend(u[c].data,n)),a=u[c],i||(a.data||(a.data={}),a=a.data),r!==t&&(a[x.camelCase(n)]=r),"string"==typeof n?(o=a[n],null==o&&(o=a[x.camelCase(n)])):o=a,o}}function W(e,t,n){if(x.acceptData(e)){var r,i,o=e.nodeType,a=o?x.cache:e,s=o?e[x.expando]:x.expando;if(a[s]){if(t&&(r=n?a[s]:a[s].data)){x.isArray(t)?t=t.concat(x.map(t,x.camelCase)):t in r?t=[t]:(t=x.camelCase(t),t=t in r?[t]:t.split(" ")),i=t.length;while(i--)delete r[t[i]];if(n?!I(r):!x.isEmptyObject(r))return}(n||(delete a[s].data,I(a[s])))&&(o?x.cleanData([e],!0):x.support.deleteExpando||a!=a.window?delete a[s]:a[s]=null)}}}x.extend({cache:{},noData:{applet:!0,embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(e){return e=e.nodeType?x.cache[e[x.expando]]:e[x.expando],!!e&&!I(e)},data:function(e,t,n){return R(e,t,n)},removeData:function(e,t){return W(e,t)},_data:function(e,t,n){return R(e,t,n,!0)},_removeData:function(e,t){return W(e,t,!0)},acceptData:function(e){if(e.nodeType&&1!==e.nodeType&&9!==e.nodeType)return!1;var t=e.nodeName&&x.noData[e.nodeName.toLowerCase()];return!t||t!==!0&&e.getAttribute("classid")===t}}),x.fn.extend({data:function(e,n){var r,i,o=null,a=0,s=this[0];if(e===t){if(this.length&&(o=x.data(s),1===s.nodeType&&!x._data(s,"parsedAttrs"))){for(r=s.attributes;r.length>a;a++)i=r[a].name,0===i.indexOf("data-")&&(i=x.camelCase(i.slice(5)),$(s,i,o[i]));x._data(s,"parsedAttrs",!0)}return o}return"object"==typeof e?this.each(function(){x.data(this,e)}):arguments.length>1?this.each(function(){x.data(this,e,n)}):s?$(s,e,x.data(s,e)):null},removeData:function(e){return this.each(function(){x.removeData(this,e)})}});function $(e,n,r){if(r===t&&1===e.nodeType){var i="data-"+n.replace(P,"-$1").toLowerCase();if(r=e.getAttribute(i),"string"==typeof r){try{r="true"===r?!0:"false"===r?!1:"null"===r?null:+r+""===r?+r:B.test(r)?x.parseJSON(r):r}catch(o){}x.data(e,n,r)}else r=t}return r}function I(e){var t;for(t in e)if(("data"!==t||!x.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}x.extend({queue:function(e,n,r){var i;return e?(n=(n||"fx")+"queue",i=x._data(e,n),r&&(!i||x.isArray(r)?i=x._data(e,n,x.makeArray(r)):i.push(r)),i||[]):t},dequeue:function(e,t){t=t||"fx";var n=x.queue(e,t),r=n.length,i=n.shift(),o=x._queueHooks(e,t),a=function(){x.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return x._data(e,n)||x._data(e,n,{empty:x.Callbacks("once memory").add(function(){x._removeData(e,t+"queue"),x._removeData(e,n)})})}}),x.fn.extend({queue:function(e,n){var r=2;return"string"!=typeof e&&(n=e,e="fx",r--),r>arguments.length?x.queue(this[0],e):n===t?this:this.each(function(){var t=x.queue(this,e,n);x._queueHooks(this,e),"fx"===e&&"inprogress"!==t[0]&&x.dequeue(this,e)})},dequeue:function(e){return this.each(function(){x.dequeue(this,e)})},delay:function(e,t){return e=x.fx?x.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,n){var r,i=1,o=x.Deferred(),a=this,s=this.length,l=function(){--i||o.resolveWith(a,[a])};"string"!=typeof e&&(n=e,e=t),e=e||"fx";while(s--)r=x._data(a[s],e+"queueHooks"),r&&r.empty&&(i++,r.empty.add(l));return l(),o.promise(n)}});var z,X,U=/[\t\r\n\f]/g,V=/\r/g,Y=/^(?:input|select|textarea|button|object)$/i,J=/^(?:a|area)$/i,G=/^(?:checked|selected)$/i,Q=x.support.getSetAttribute,K=x.support.input;x.fn.extend({attr:function(e,t){return x.access(this,x.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){x.removeAttr(this,e)})},prop:function(e,t){return x.access(this,x.prop,e,t,arguments.length>1)},removeProp:function(e){return e=x.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,o,a=0,s=this.length,l="string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).addClass(e.call(this,t,this.className))});if(l)for(t=(e||"").match(T)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(U," "):" ")){o=0;while(i=t[o++])0>r.indexOf(" "+i+" ")&&(r+=i+" ");n.className=x.trim(r)}return this},removeClass:function(e){var t,n,r,i,o,a=0,s=this.length,l=0===arguments.length||"string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).removeClass(e.call(this,t,this.className))});if(l)for(t=(e||"").match(T)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(U," "):"")){o=0;while(i=t[o++])while(r.indexOf(" "+i+" ")>=0)r=r.replace(" "+i+" "," ");n.className=e?x.trim(r):""}return this},toggleClass:function(e,t){var n=typeof e;return"boolean"==typeof t&&"string"===n?t?this.addClass(e):this.removeClass(e):x.isFunction(e)?this.each(function(n){x(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if("string"===n){var t,r=0,o=x(this),a=e.match(T)||[];while(t=a[r++])o.hasClass(t)?o.removeClass(t):o.addClass(t)}else(n===i||"boolean"===n)&&(this.className&&x._data(this,"__className__",this.className),this.className=this.className||e===!1?"":x._data(this,"__className__")||"")})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;r>n;n++)if(1===this[n].nodeType&&(" "+this[n].className+" ").replace(U," ").indexOf(t)>=0)return!0;return!1},val:function(e){var n,r,i,o=this[0];{if(arguments.length)return i=x.isFunction(e),this.each(function(n){var o;1===this.nodeType&&(o=i?e.call(this,n,x(this).val()):e,null==o?o="":"number"==typeof o?o+="":x.isArray(o)&&(o=x.map(o,function(e){return null==e?"":e+""})),r=x.valHooks[this.type]||x.valHooks[this.nodeName.toLowerCase()],r&&"set"in r&&r.set(this,o,"value")!==t||(this.value=o))});if(o)return r=x.valHooks[o.type]||x.valHooks[o.nodeName.toLowerCase()],r&&"get"in r&&(n=r.get(o,"value"))!==t?n:(n=o.value,"string"==typeof n?n.replace(V,""):null==n?"":n)}}}),x.extend({valHooks:{option:{get:function(e){var t=x.find.attr(e,"value");return null!=t?t:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||0>i,a=o?null:[],s=o?i+1:r.length,l=0>i?s:o?i:0;for(;s>l;l++)if(n=r[l],!(!n.selected&&l!==i||(x.support.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&x.nodeName(n.parentNode,"optgroup"))){if(t=x(n).val(),o)return t;a.push(t)}return a},set:function(e,t){var n,r,i=e.options,o=x.makeArray(t),a=i.length;while(a--)r=i[a],(r.selected=x.inArray(x(r).val(),o)>=0)&&(n=!0);return n||(e.selectedIndex=-1),o}}},attr:function(e,n,r){var o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return typeof e.getAttribute===i?x.prop(e,n,r):(1===s&&x.isXMLDoc(e)||(n=n.toLowerCase(),o=x.attrHooks[n]||(x.expr.match.bool.test(n)?X:z)),r===t?o&&"get"in o&&null!==(a=o.get(e,n))?a:(a=x.find.attr(e,n),null==a?t:a):null!==r?o&&"set"in o&&(a=o.set(e,r,n))!==t?a:(e.setAttribute(n,r+""),r):(x.removeAttr(e,n),t))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(T);if(o&&1===e.nodeType)while(n=o[i++])r=x.propFix[n]||n,x.expr.match.bool.test(n)?K&&Q||!G.test(n)?e[r]=!1:e[x.camelCase("default-"+n)]=e[r]=!1:x.attr(e,n,""),e.removeAttribute(Q?n:r)},attrHooks:{type:{set:function(e,t){if(!x.support.radioValue&&"radio"===t&&x.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},propFix:{"for":"htmlFor","class":"className"},prop:function(e,n,r){var i,o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return a=1!==s||!x.isXMLDoc(e),a&&(n=x.propFix[n]||n,o=x.propHooks[n]),r!==t?o&&"set"in o&&(i=o.set(e,r,n))!==t?i:e[n]=r:o&&"get"in o&&null!==(i=o.get(e,n))?i:e[n]},propHooks:{tabIndex:{get:function(e){var t=x.find.attr(e,"tabindex");return t?parseInt(t,10):Y.test(e.nodeName)||J.test(e.nodeName)&&e.href?0:-1}}}}),X={set:function(e,t,n){return t===!1?x.removeAttr(e,n):K&&Q||!G.test(n)?e.setAttribute(!Q&&x.propFix[n]||n,n):e[x.camelCase("default-"+n)]=e[n]=!0,n}},x.each(x.expr.match.bool.source.match(/\w+/g),function(e,n){var r=x.expr.attrHandle[n]||x.find.attr;x.expr.attrHandle[n]=K&&Q||!G.test(n)?function(e,n,i){var o=x.expr.attrHandle[n],a=i?t:(x.expr.attrHandle[n]=t)!=r(e,n,i)?n.toLowerCase():null;return x.expr.attrHandle[n]=o,a}:function(e,n,r){return r?t:e[x.camelCase("default-"+n)]?n.toLowerCase():null}}),K&&Q||(x.attrHooks.value={set:function(e,n,r){return x.nodeName(e,"input")?(e.defaultValue=n,t):z&&z.set(e,n,r)}}),Q||(z={set:function(e,n,r){var i=e.getAttributeNode(r);return i||e.setAttributeNode(i=e.ownerDocument.createAttribute(r)),i.value=n+="","value"===r||n===e.getAttribute(r)?n:t}},x.expr.attrHandle.id=x.expr.attrHandle.name=x.expr.attrHandle.coords=function(e,n,r){var i;return r?t:(i=e.getAttributeNode(n))&&""!==i.value?i.value:null},x.valHooks.button={get:function(e,n){var r=e.getAttributeNode(n);return r&&r.specified?r.value:t},set:z.set},x.attrHooks.contenteditable={set:function(e,t,n){z.set(e,""===t?!1:t,n)}},x.each(["width","height"],function(e,n){x.attrHooks[n]={set:function(e,r){return""===r?(e.setAttribute(n,"auto"),r):t}}})),x.support.hrefNormalized||x.each(["href","src"],function(e,t){x.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}}),x.support.style||(x.attrHooks.style={get:function(e){return e.style.cssText||t},set:function(e,t){return e.style.cssText=t+""}}),x.support.optSelected||(x.propHooks.selected={get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null}}),x.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){x.propFix[this.toLowerCase()]=this}),x.support.enctype||(x.propFix.enctype="encoding"),x.each(["radio","checkbox"],function(){x.valHooks[this]={set:function(e,n){return x.isArray(n)?e.checked=x.inArray(x(e).val(),n)>=0:t}},x.support.checkOn||(x.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})});var Z=/^(?:input|select|textarea)$/i,et=/^key/,tt=/^(?:mouse|contextmenu)|click/,nt=/^(?:focusinfocus|focusoutblur)$/,rt=/^([^.]*)(?:\.(.+)|)$/;function it(){return!0}function ot(){return!1}function at(){try{return a.activeElement}catch(e){}}x.event={global:{},add:function(e,n,r,o,a){var s,l,u,c,p,f,d,h,g,m,y,v=x._data(e);if(v){r.handler&&(c=r,r=c.handler,a=c.selector),r.guid||(r.guid=x.guid++),(l=v.events)||(l=v.events={}),(f=v.handle)||(f=v.handle=function(e){return typeof x===i||e&&x.event.triggered===e.type?t:x.event.dispatch.apply(f.elem,arguments)},f.elem=e),n=(n||"").match(T)||[""],u=n.length;while(u--)s=rt.exec(n[u])||[],g=y=s[1],m=(s[2]||"").split(".").sort(),g&&(p=x.event.special[g]||{},g=(a?p.delegateType:p.bindType)||g,p=x.event.special[g]||{},d=x.extend({type:g,origType:y,data:o,handler:r,guid:r.guid,selector:a,needsContext:a&&x.expr.match.needsContext.test(a),namespace:m.join(".")},c),(h=l[g])||(h=l[g]=[],h.delegateCount=0,p.setup&&p.setup.call(e,o,m,f)!==!1||(e.addEventListener?e.addEventListener(g,f,!1):e.attachEvent&&e.attachEvent("on"+g,f))),p.add&&(p.add.call(e,d),d.handler.guid||(d.handler.guid=r.guid)),a?h.splice(h.delegateCount++,0,d):h.push(d),x.event.global[g]=!0);e=null}},remove:function(e,t,n,r,i){var o,a,s,l,u,c,p,f,d,h,g,m=x.hasData(e)&&x._data(e);if(m&&(c=m.events)){t=(t||"").match(T)||[""],u=t.length;while(u--)if(s=rt.exec(t[u])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){p=x.event.special[d]||{},d=(r?p.delegateType:p.bindType)||d,f=c[d]||[],s=s[2]&&RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),l=o=f.length;while(o--)a=f[o],!i&&g!==a.origType||n&&n.guid!==a.guid||s&&!s.test(a.namespace)||r&&r!==a.selector&&("**"!==r||!a.selector)||(f.splice(o,1),a.selector&&f.delegateCount--,p.remove&&p.remove.call(e,a));l&&!f.length&&(p.teardown&&p.teardown.call(e,h,m.handle)!==!1||x.removeEvent(e,d,m.handle),delete c[d])}else for(d in c)x.event.remove(e,d+t[u],n,r,!0);x.isEmptyObject(c)&&(delete m.handle,x._removeData(e,"events"))}},trigger:function(n,r,i,o){var s,l,u,c,p,f,d,h=[i||a],g=v.call(n,"type")?n.type:n,m=v.call(n,"namespace")?n.namespace.split("."):[];if(u=f=i=i||a,3!==i.nodeType&&8!==i.nodeType&&!nt.test(g+x.event.triggered)&&(g.indexOf(".")>=0&&(m=g.split("."),g=m.shift(),m.sort()),l=0>g.indexOf(":")&&"on"+g,n=n[x.expando]?n:new x.Event(g,"object"==typeof n&&n),n.isTrigger=o?2:3,n.namespace=m.join("."),n.namespace_re=n.namespace?RegExp("(^|\\.)"+m.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,n.result=t,n.target||(n.target=i),r=null==r?[n]:x.makeArray(r,[n]),p=x.event.special[g]||{},o||!p.trigger||p.trigger.apply(i,r)!==!1)){if(!o&&!p.noBubble&&!x.isWindow(i)){for(c=p.delegateType||g,nt.test(c+g)||(u=u.parentNode);u;u=u.parentNode)h.push(u),f=u;f===(i.ownerDocument||a)&&h.push(f.defaultView||f.parentWindow||e)}d=0;while((u=h[d++])&&!n.isPropagationStopped())n.type=d>1?c:p.bindType||g,s=(x._data(u,"events")||{})[n.type]&&x._data(u,"handle"),s&&s.apply(u,r),s=l&&u[l],s&&x.acceptData(u)&&s.apply&&s.apply(u,r)===!1&&n.preventDefault();if(n.type=g,!o&&!n.isDefaultPrevented()&&(!p._default||p._default.apply(h.pop(),r)===!1)&&x.acceptData(i)&&l&&i[g]&&!x.isWindow(i)){f=i[l],f&&(i[l]=null),x.event.triggered=g;try{i[g]()}catch(y){}x.event.triggered=t,f&&(i[l]=f)}return n.result}},dispatch:function(e){e=x.event.fix(e);var n,r,i,o,a,s=[],l=g.call(arguments),u=(x._data(this,"events")||{})[e.type]||[],c=x.event.special[e.type]||{};if(l[0]=e,e.delegateTarget=this,!c.preDispatch||c.preDispatch.call(this,e)!==!1){s=x.event.handlers.call(this,e,u),n=0;while((o=s[n++])&&!e.isPropagationStopped()){e.currentTarget=o.elem,a=0;while((i=o.handlers[a++])&&!e.isImmediatePropagationStopped())(!e.namespace_re||e.namespace_re.test(i.namespace))&&(e.handleObj=i,e.data=i.data,r=((x.event.special[i.origType]||{}).handle||i.handler).apply(o.elem,l),r!==t&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,e),e.result}},handlers:function(e,n){var r,i,o,a,s=[],l=n.delegateCount,u=e.target;if(l&&u.nodeType&&(!e.button||"click"!==e.type))for(;u!=this;u=u.parentNode||this)if(1===u.nodeType&&(u.disabled!==!0||"click"!==e.type)){for(o=[],a=0;l>a;a++)i=n[a],r=i.selector+" ",o[r]===t&&(o[r]=i.needsContext?x(r,this).index(u)>=0:x.find(r,this,null,[u]).length),o[r]&&o.push(i);o.length&&s.push({elem:u,handlers:o})}return n.length>l&&s.push({elem:this,handlers:n.slice(l)}),s},fix:function(e){if(e[x.expando])return e;var t,n,r,i=e.type,o=e,s=this.fixHooks[i];s||(this.fixHooks[i]=s=tt.test(i)?this.mouseHooks:et.test(i)?this.keyHooks:{}),r=s.props?this.props.concat(s.props):this.props,e=new x.Event(o),t=r.length;while(t--)n=r[t],e[n]=o[n];return e.target||(e.target=o.srcElement||a),3===e.target.nodeType&&(e.target=e.target.parentNode),e.metaKey=!!e.metaKey,s.filter?s.filter(e,o):e},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,n){var r,i,o,s=n.button,l=n.fromElement;return null==e.pageX&&null!=n.clientX&&(i=e.target.ownerDocument||a,o=i.documentElement,r=i.body,e.pageX=n.clientX+(o&&o.scrollLeft||r&&r.scrollLeft||0)-(o&&o.clientLeft||r&&r.clientLeft||0),e.pageY=n.clientY+(o&&o.scrollTop||r&&r.scrollTop||0)-(o&&o.clientTop||r&&r.clientTop||0)),!e.relatedTarget&&l&&(e.relatedTarget=l===e.target?n.toElement:l),e.which||s===t||(e.which=1&s?1:2&s?3:4&s?2:0),e}},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==at()&&this.focus)try{return this.focus(),!1}catch(e){}},delegateType:"focusin"},blur:{trigger:function(){return this===at()&&this.blur?(this.blur(),!1):t},delegateType:"focusout"},click:{trigger:function(){return x.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):t},_default:function(e){return x.nodeName(e.target,"a")}},beforeunload:{postDispatch:function(e){e.result!==t&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=x.extend(new x.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?x.event.trigger(i,null,t):x.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},x.removeEvent=a.removeEventListener?function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)}:function(e,t,n){var r="on"+t;e.detachEvent&&(typeof e[r]===i&&(e[r]=null),e.detachEvent(r,n))},x.Event=function(e,n){return this instanceof x.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.returnValue===!1||e.getPreventDefault&&e.getPreventDefault()?it:ot):this.type=e,n&&x.extend(this,n),this.timeStamp=e&&e.timeStamp||x.now(),this[x.expando]=!0,t):new x.Event(e,n)},x.Event.prototype={isDefaultPrevented:ot,isPropagationStopped:ot,isImmediatePropagationStopped:ot,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=it,e&&(e.preventDefault?e.preventDefault():e.returnValue=!1)},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=it,e&&(e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=it,this.stopPropagation()}},x.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(e,t){x.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj;return(!i||i!==r&&!x.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),x.support.submitBubbles||(x.event.special.submit={setup:function(){return x.nodeName(this,"form")?!1:(x.event.add(this,"click._submit keypress._submit",function(e){var n=e.target,r=x.nodeName(n,"input")||x.nodeName(n,"button")?n.form:t;r&&!x._data(r,"submitBubbles")&&(x.event.add(r,"submit._submit",function(e){e._submit_bubble=!0}),x._data(r,"submitBubbles",!0))}),t)},postDispatch:function(e){e._submit_bubble&&(delete e._submit_bubble,this.parentNode&&!e.isTrigger&&x.event.simulate("submit",this.parentNode,e,!0))},teardown:function(){return x.nodeName(this,"form")?!1:(x.event.remove(this,"._submit"),t)}}),x.support.changeBubbles||(x.event.special.change={setup:function(){return Z.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(x.event.add(this,"propertychange._change",function(e){"checked"===e.originalEvent.propertyName&&(this._just_changed=!0)}),x.event.add(this,"click._change",function(e){this._just_changed&&!e.isTrigger&&(this._just_changed=!1),x.event.simulate("change",this,e,!0)})),!1):(x.event.add(this,"beforeactivate._change",function(e){var t=e.target;Z.test(t.nodeName)&&!x._data(t,"changeBubbles")&&(x.event.add(t,"change._change",function(e){!this.parentNode||e.isSimulated||e.isTrigger||x.event.simulate("change",this.parentNode,e,!0)}),x._data(t,"changeBubbles",!0))}),t)},handle:function(e){var n=e.target;return this!==n||e.isSimulated||e.isTrigger||"radio"!==n.type&&"checkbox"!==n.type?e.handleObj.handler.apply(this,arguments):t},teardown:function(){return x.event.remove(this,"._change"),!Z.test(this.nodeName)}}),x.support.focusinBubbles||x.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){x.event.simulate(t,e.target,x.event.fix(e),!0)};x.event.special[t]={setup:function(){0===n++&&a.addEventListener(e,r,!0)},teardown:function(){0===--n&&a.removeEventListener(e,r,!0)}}}),x.fn.extend({on:function(e,n,r,i,o){var a,s;if("object"==typeof e){"string"!=typeof n&&(r=r||n,n=t);for(a in e)this.on(a,n,r,e[a],o);return this}if(null==r&&null==i?(i=n,r=n=t):null==i&&("string"==typeof n?(i=r,r=t):(i=r,r=n,n=t)),i===!1)i=ot;else if(!i)return this;return 1===o&&(s=i,i=function(e){return x().off(e),s.apply(this,arguments)},i.guid=s.guid||(s.guid=x.guid++)),this.each(function(){x.event.add(this,e,i,r,n)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,n,r){var i,o;if(e&&e.preventDefault&&e.handleObj)return i=e.handleObj,x(e.delegateTarget).off(i.namespace?i.origType+"."+i.namespace:i.origType,i.selector,i.handler),this;if("object"==typeof e){for(o in e)this.off(o,n,e[o]);return this}return(n===!1||"function"==typeof n)&&(r=n,n=t),r===!1&&(r=ot),this.each(function(){x.event.remove(this,e,r,n)})},trigger:function(e,t){return this.each(function(){x.event.trigger(e,t,this)})},triggerHandler:function(e,n){var r=this[0];return r?x.event.trigger(e,n,r,!0):t}});var st=/^.[^:#\[\.,]*$/,lt=/^(?:parents|prev(?:Until|All))/,ut=x.expr.match.needsContext,ct={children:!0,contents:!0,next:!0,prev:!0};x.fn.extend({find:function(e){var t,n=[],r=this,i=r.length;if("string"!=typeof e)return this.pushStack(x(e).filter(function(){for(t=0;i>t;t++)if(x.contains(r[t],this))return!0}));for(t=0;i>t;t++)x.find(e,r[t],n);return n=this.pushStack(i>1?x.unique(n):n),n.selector=this.selector?this.selector+" "+e:e,n},has:function(e){var t,n=x(e,this),r=n.length;return this.filter(function(){for(t=0;r>t;t++)if(x.contains(this,n[t]))return!0})},not:function(e){return this.pushStack(ft(this,e||[],!0))},filter:function(e){return this.pushStack(ft(this,e||[],!1))},is:function(e){return!!ft(this,"string"==typeof e&&ut.test(e)?x(e):e||[],!1).length},closest:function(e,t){var n,r=0,i=this.length,o=[],a=ut.test(e)||"string"!=typeof e?x(e,t||this.context):0;for(;i>r;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(11>n.nodeType&&(a?a.index(n)>-1:1===n.nodeType&&x.find.matchesSelector(n,e))){n=o.push(n);break}return this.pushStack(o.length>1?x.unique(o):o)},index:function(e){return e?"string"==typeof e?x.inArray(this[0],x(e)):x.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){var n="string"==typeof e?x(e,t):x.makeArray(e&&e.nodeType?[e]:e),r=x.merge(this.get(),n);return this.pushStack(x.unique(r))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}});function pt(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}x.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return x.dir(e,"parentNode")},parentsUntil:function(e,t,n){return x.dir(e,"parentNode",n)},next:function(e){return pt(e,"nextSibling")},prev:function(e){return pt(e,"previousSibling")},nextAll:function(e){return x.dir(e,"nextSibling")},prevAll:function(e){return x.dir(e,"previousSibling")},nextUntil:function(e,t,n){return x.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return x.dir(e,"previousSibling",n)},siblings:function(e){return x.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return x.sibling(e.firstChild)},contents:function(e){return x.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:x.merge([],e.childNodes)}},function(e,t){x.fn[e]=function(n,r){var i=x.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=x.filter(r,i)),this.length>1&&(ct[e]||(i=x.unique(i)),lt.test(e)&&(i=i.reverse())),this.pushStack(i)}}),x.extend({filter:function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?x.find.matchesSelector(r,e)?[r]:[]:x.find.matches(e,x.grep(t,function(e){return 1===e.nodeType}))},dir:function(e,n,r){var i=[],o=e[n];while(o&&9!==o.nodeType&&(r===t||1!==o.nodeType||!x(o).is(r)))1===o.nodeType&&i.push(o),o=o[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}});function ft(e,t,n){if(x.isFunction(t))return x.grep(e,function(e,r){return!!t.call(e,r,e)!==n});if(t.nodeType)return x.grep(e,function(e){return e===t!==n});if("string"==typeof t){if(st.test(t))return x.filter(t,e,n);t=x.filter(t,e)}return x.grep(e,function(e){return x.inArray(e,t)>=0!==n})}function dt(e){var t=ht.split("|"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}var ht="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",gt=/ jQuery\d+="(?:null|\d+)"/g,mt=RegExp("<(?:"+ht+")[\\s/>]","i"),yt=/^\s+/,vt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bt=/<([\w:]+)/,xt=/\s*$/g,At={option:[1,""],legend:[1,"
","
"],area:[1,"",""],param:[1,"",""],thead:[1,"","
"],tr:[2,"","
"],col:[2,"","
"],td:[3,"","
"],_default:x.support.htmlSerialize?[0,"",""]:[1,"X
","
"]},jt=dt(a),Dt=jt.appendChild(a.createElement("div"));At.optgroup=At.option,At.tbody=At.tfoot=At.colgroup=At.caption=At.thead,At.th=At.td,x.fn.extend({text:function(e){return x.access(this,function(e){return e===t?x.text(this):this.empty().append((this[0]&&this[0].ownerDocument||a).createTextNode(e))},null,e,arguments.length)},append:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Lt(this,e);t.appendChild(e)}})},prepend:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Lt(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){var n,r=e?x.filter(e,this):this,i=0;for(;null!=(n=r[i]);i++)t||1!==n.nodeType||x.cleanData(Ft(n)),n.parentNode&&(t&&x.contains(n.ownerDocument,n)&&_t(Ft(n,"script")),n.parentNode.removeChild(n));return this},empty:function(){var e,t=0;for(;null!=(e=this[t]);t++){1===e.nodeType&&x.cleanData(Ft(e,!1));while(e.firstChild)e.removeChild(e.firstChild);e.options&&x.nodeName(e,"select")&&(e.options.length=0)}return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return x.clone(this,e,t)})},html:function(e){return x.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return 1===n.nodeType?n.innerHTML.replace(gt,""):t;if(!("string"!=typeof e||Tt.test(e)||!x.support.htmlSerialize&&mt.test(e)||!x.support.leadingWhitespace&&yt.test(e)||At[(bt.exec(e)||["",""])[1].toLowerCase()])){e=e.replace(vt,"<$1>");try{for(;i>r;r++)n=this[r]||{},1===n.nodeType&&(x.cleanData(Ft(n,!1)),n.innerHTML=e);n=0}catch(o){}}n&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var e=x.map(this,function(e){return[e.nextSibling,e.parentNode]}),t=0;return this.domManip(arguments,function(n){var r=e[t++],i=e[t++];i&&(r&&r.parentNode!==i&&(r=this.nextSibling),x(this).remove(),i.insertBefore(n,r))},!0),t?this:this.remove()},detach:function(e){return this.remove(e,!0)},domManip:function(e,t,n){e=d.apply([],e);var r,i,o,a,s,l,u=0,c=this.length,p=this,f=c-1,h=e[0],g=x.isFunction(h);if(g||!(1>=c||"string"!=typeof h||x.support.checkClone)&&Nt.test(h))return this.each(function(r){var i=p.eq(r);g&&(e[0]=h.call(this,r,i.html())),i.domManip(e,t,n)});if(c&&(l=x.buildFragment(e,this[0].ownerDocument,!1,!n&&this),r=l.firstChild,1===l.childNodes.length&&(l=r),r)){for(a=x.map(Ft(l,"script"),Ht),o=a.length;c>u;u++)i=l,u!==f&&(i=x.clone(i,!0,!0),o&&x.merge(a,Ft(i,"script"))),t.call(this[u],i,u);if(o)for(s=a[a.length-1].ownerDocument,x.map(a,qt),u=0;o>u;u++)i=a[u],kt.test(i.type||"")&&!x._data(i,"globalEval")&&x.contains(s,i)&&(i.src?x._evalUrl(i.src):x.globalEval((i.text||i.textContent||i.innerHTML||"").replace(St,"")));l=r=null}return this}});function Lt(e,t){return x.nodeName(e,"table")&&x.nodeName(1===t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody")):e}function Ht(e){return e.type=(null!==x.find.attr(e,"type"))+"/"+e.type,e}function qt(e){var t=Et.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function _t(e,t){var n,r=0;for(;null!=(n=e[r]);r++)x._data(n,"globalEval",!t||x._data(t[r],"globalEval"))}function Mt(e,t){if(1===t.nodeType&&x.hasData(e)){var n,r,i,o=x._data(e),a=x._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;i>r;r++)x.event.add(t,n,s[n][r])}a.data&&(a.data=x.extend({},a.data))}}function Ot(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase(),!x.support.noCloneEvent&&t[x.expando]){i=x._data(t);for(r in i.events)x.removeEvent(t,r,i.handle);t.removeAttribute(x.expando)}"script"===n&&t.text!==e.text?(Ht(t).text=e.text,qt(t)):"object"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),x.support.html5Clone&&e.innerHTML&&!x.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):"input"===n&&Ct.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):"option"===n?t.defaultSelected=t.selected=e.defaultSelected:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}}x.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){x.fn[e]=function(e){var n,r=0,i=[],o=x(e),a=o.length-1;for(;a>=r;r++)n=r===a?this:this.clone(!0),x(o[r])[t](n),h.apply(i,n.get());return this.pushStack(i)}});function Ft(e,n){var r,o,a=0,s=typeof e.getElementsByTagName!==i?e.getElementsByTagName(n||"*"):typeof e.querySelectorAll!==i?e.querySelectorAll(n||"*"):t;if(!s)for(s=[],r=e.childNodes||e;null!=(o=r[a]);a++)!n||x.nodeName(o,n)?s.push(o):x.merge(s,Ft(o,n));return n===t||n&&x.nodeName(e,n)?x.merge([e],s):s}function Bt(e){Ct.test(e.type)&&(e.defaultChecked=e.checked)}x.extend({clone:function(e,t,n){var r,i,o,a,s,l=x.contains(e.ownerDocument,e);if(x.support.html5Clone||x.isXMLDoc(e)||!mt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(Dt.innerHTML=e.outerHTML,Dt.removeChild(o=Dt.firstChild)),!(x.support.noCloneEvent&&x.support.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||x.isXMLDoc(e)))for(r=Ft(o),s=Ft(e),a=0;null!=(i=s[a]);++a)r[a]&&Ot(i,r[a]);if(t)if(n)for(s=s||Ft(e),r=r||Ft(o),a=0;null!=(i=s[a]);a++)Mt(i,r[a]);else Mt(e,o);return r=Ft(o,"script"),r.length>0&&_t(r,!l&&Ft(e,"script")),r=s=i=null,o},buildFragment:function(e,t,n,r){var i,o,a,s,l,u,c,p=e.length,f=dt(t),d=[],h=0;for(;p>h;h++)if(o=e[h],o||0===o)if("object"===x.type(o))x.merge(d,o.nodeType?[o]:o);else if(wt.test(o)){s=s||f.appendChild(t.createElement("div")),l=(bt.exec(o)||["",""])[1].toLowerCase(),c=At[l]||At._default,s.innerHTML=c[1]+o.replace(vt,"<$1>")+c[2],i=c[0];while(i--)s=s.lastChild;if(!x.support.leadingWhitespace&&yt.test(o)&&d.push(t.createTextNode(yt.exec(o)[0])),!x.support.tbody){o="table"!==l||xt.test(o)?""!==c[1]||xt.test(o)?0:s:s.firstChild,i=o&&o.childNodes.length;while(i--)x.nodeName(u=o.childNodes[i],"tbody")&&!u.childNodes.length&&o.removeChild(u)}x.merge(d,s.childNodes),s.textContent="";while(s.firstChild)s.removeChild(s.firstChild);s=f.lastChild}else d.push(t.createTextNode(o));s&&f.removeChild(s),x.support.appendChecked||x.grep(Ft(d,"input"),Bt),h=0;while(o=d[h++])if((!r||-1===x.inArray(o,r))&&(a=x.contains(o.ownerDocument,o),s=Ft(f.appendChild(o),"script"),a&&_t(s),n)){i=0;while(o=s[i++])kt.test(o.type||"")&&n.push(o)}return s=null,f},cleanData:function(e,t){var n,r,o,a,s=0,l=x.expando,u=x.cache,c=x.support.deleteExpando,f=x.event.special;for(;null!=(n=e[s]);s++)if((t||x.acceptData(n))&&(o=n[l],a=o&&u[o])){if(a.events)for(r in a.events)f[r]?x.event.remove(n,r):x.removeEvent(n,r,a.handle); +u[o]&&(delete u[o],c?delete n[l]:typeof n.removeAttribute!==i?n.removeAttribute(l):n[l]=null,p.push(o))}},_evalUrl:function(e){return x.ajax({url:e,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})}}),x.fn.extend({wrapAll:function(e){if(x.isFunction(e))return this.each(function(t){x(this).wrapAll(e.call(this,t))});if(this[0]){var t=x(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&1===e.firstChild.nodeType)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return x.isFunction(e)?this.each(function(t){x(this).wrapInner(e.call(this,t))}):this.each(function(){var t=x(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=x.isFunction(e);return this.each(function(n){x(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){x.nodeName(this,"body")||x(this).replaceWith(this.childNodes)}).end()}});var Pt,Rt,Wt,$t=/alpha\([^)]*\)/i,It=/opacity\s*=\s*([^)]*)/,zt=/^(top|right|bottom|left)$/,Xt=/^(none|table(?!-c[ea]).+)/,Ut=/^margin/,Vt=RegExp("^("+w+")(.*)$","i"),Yt=RegExp("^("+w+")(?!px)[a-z%]+$","i"),Jt=RegExp("^([+-])=("+w+")","i"),Gt={BODY:"block"},Qt={position:"absolute",visibility:"hidden",display:"block"},Kt={letterSpacing:0,fontWeight:400},Zt=["Top","Right","Bottom","Left"],en=["Webkit","O","Moz","ms"];function tn(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=en.length;while(i--)if(t=en[i]+n,t in e)return t;return r}function nn(e,t){return e=t||e,"none"===x.css(e,"display")||!x.contains(e.ownerDocument,e)}function rn(e,t){var n,r,i,o=[],a=0,s=e.length;for(;s>a;a++)r=e[a],r.style&&(o[a]=x._data(r,"olddisplay"),n=r.style.display,t?(o[a]||"none"!==n||(r.style.display=""),""===r.style.display&&nn(r)&&(o[a]=x._data(r,"olddisplay",ln(r.nodeName)))):o[a]||(i=nn(r),(n&&"none"!==n||!i)&&x._data(r,"olddisplay",i?n:x.css(r,"display"))));for(a=0;s>a;a++)r=e[a],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[a]||"":"none"));return e}x.fn.extend({css:function(e,n){return x.access(this,function(e,n,r){var i,o,a={},s=0;if(x.isArray(n)){for(o=Rt(e),i=n.length;i>s;s++)a[n[s]]=x.css(e,n[s],!1,o);return a}return r!==t?x.style(e,n,r):x.css(e,n)},e,n,arguments.length>1)},show:function(){return rn(this,!0)},hide:function(){return rn(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){nn(this)?x(this).show():x(this).hide()})}}),x.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Wt(e,"opacity");return""===n?"1":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":x.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var o,a,s,l=x.camelCase(n),u=e.style;if(n=x.cssProps[l]||(x.cssProps[l]=tn(u,l)),s=x.cssHooks[n]||x.cssHooks[l],r===t)return s&&"get"in s&&(o=s.get(e,!1,i))!==t?o:u[n];if(a=typeof r,"string"===a&&(o=Jt.exec(r))&&(r=(o[1]+1)*o[2]+parseFloat(x.css(e,n)),a="number"),!(null==r||"number"===a&&isNaN(r)||("number"!==a||x.cssNumber[l]||(r+="px"),x.support.clearCloneStyle||""!==r||0!==n.indexOf("background")||(u[n]="inherit"),s&&"set"in s&&(r=s.set(e,r,i))===t)))try{u[n]=r}catch(c){}}},css:function(e,n,r,i){var o,a,s,l=x.camelCase(n);return n=x.cssProps[l]||(x.cssProps[l]=tn(e.style,l)),s=x.cssHooks[n]||x.cssHooks[l],s&&"get"in s&&(a=s.get(e,!0,r)),a===t&&(a=Wt(e,n,i)),"normal"===a&&n in Kt&&(a=Kt[n]),""===r||r?(o=parseFloat(a),r===!0||x.isNumeric(o)?o||0:a):a}}),e.getComputedStyle?(Rt=function(t){return e.getComputedStyle(t,null)},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),l=s?s.getPropertyValue(n)||s[n]:t,u=e.style;return s&&(""!==l||x.contains(e.ownerDocument,e)||(l=x.style(e,n)),Yt.test(l)&&Ut.test(n)&&(i=u.width,o=u.minWidth,a=u.maxWidth,u.minWidth=u.maxWidth=u.width=l,l=s.width,u.width=i,u.minWidth=o,u.maxWidth=a)),l}):a.documentElement.currentStyle&&(Rt=function(e){return e.currentStyle},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),l=s?s[n]:t,u=e.style;return null==l&&u&&u[n]&&(l=u[n]),Yt.test(l)&&!zt.test(n)&&(i=u.left,o=e.runtimeStyle,a=o&&o.left,a&&(o.left=e.currentStyle.left),u.left="fontSize"===n?"1em":l,l=u.pixelLeft+"px",u.left=i,a&&(o.left=a)),""===l?"auto":l});function on(e,t,n){var r=Vt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function an(e,t,n,r,i){var o=n===(r?"border":"content")?4:"width"===t?1:0,a=0;for(;4>o;o+=2)"margin"===n&&(a+=x.css(e,n+Zt[o],!0,i)),r?("content"===n&&(a-=x.css(e,"padding"+Zt[o],!0,i)),"margin"!==n&&(a-=x.css(e,"border"+Zt[o]+"Width",!0,i))):(a+=x.css(e,"padding"+Zt[o],!0,i),"padding"!==n&&(a+=x.css(e,"border"+Zt[o]+"Width",!0,i)));return a}function sn(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=Rt(e),a=x.support.boxSizing&&"border-box"===x.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=Wt(e,t,o),(0>i||null==i)&&(i=e.style[t]),Yt.test(i))return i;r=a&&(x.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+an(e,t,n||(a?"border":"content"),r,o)+"px"}function ln(e){var t=a,n=Gt[e];return n||(n=un(e,t),"none"!==n&&n||(Pt=(Pt||x("