From 552897476f8904012c26b869e300f3f769248c25 Mon Sep 17 00:00:00 2001 From: Chris Kacerguis Date: Mon, 18 May 2015 09:21:26 -0500 Subject: [PATCH 001/444] closes #449 --- 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 9b3e945b..5af42061 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -420,7 +420,20 @@ public function _remap($object_called, $arguments) } // and...... GO! - $this->_fire_method([$this, $controller_method], $arguments); + try { + $this->_fire_method([$this, $controller_method], $arguments); + } + catch(Exception $ex) { + $response = [ + config_item('rest_status_field_name') => FALSE, + config_item('rest_message_field_name') => [ + 'classname' => get_class($ex), 'message' => $ex->getMessage() + ] + ]; + $this->response($response, 500); + } + + // should not get here. } /** From 5310ebc21b74ae96c770b5a9644e6ed4b93c8281 Mon Sep 17 00:00:00 2001 From: Chris Kacerguis Date: Thu, 21 May 2015 21:07:03 -0500 Subject: [PATCH 002/444] gets field names from config file. closes #450 --- 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 5af42061..641a5567 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -1414,7 +1414,7 @@ protected function _check_php_session() { $key = $this->config->item('auth_source'); if (!$this->session->userdata($key)) { - $this->response(['status' => FALSE, 'error' => 'Not Authorized'], 401); + $this->response([config_item('rest_status_field_name') => FALSE, config_item('rest_message_field_name') => 'Not Authorized'], 401); } } From 8f0af49e26329ddd6d5c20e6200b3c4aedd5a0f1 Mon Sep 17 00:00:00 2001 From: Chris Kacerguis Date: Thu, 21 May 2015 21:10:34 -0500 Subject: [PATCH 003/444] made a method to respond to client on server issues. fixes #449 --- application/libraries/REST_Controller.php | 26 +++++++++++++++++------ 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 641a5567..69f876f9 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -424,13 +424,7 @@ public function _remap($object_called, $arguments) $this->_fire_method([$this, $controller_method], $arguments); } catch(Exception $ex) { - $response = [ - config_item('rest_status_field_name') => FALSE, - config_item('rest_message_field_name') => [ - 'classname' => get_class($ex), 'message' => $ex->getMessage() - ] - ]; - $this->response($response, 500); + $this->_server_error_response($ex); } // should not get here. @@ -551,6 +545,24 @@ protected function _detect_ssl() return (isset($_SERVER['HTTPS']) && !empty($_SERVER['HTTPS'])); } + /** + * Return server response + * + * Method to send a response to the client in the event of a server error. + * + * @access public + * @param object $ex + */ + protected function _server_error_response($ex) + { + $response = [ + config_item('rest_status_field_name') => FALSE, + config_item('rest_message_field_name') => [ + 'classname' => get_class($ex), 'message' => $ex->getMessage() + ] + ]; + $this->response($response, 500); + } /** * Detect input format From e14659617b35a822a1df365b8203b038c0f8a100 Mon Sep 17 00:00:00 2001 From: mychaelgo Date: Wed, 27 May 2015 10:40:52 +0700 Subject: [PATCH 004/444] Add JSON_NUMERIC_CHECK for auto convert string to int --- application/libraries/Format.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/application/libraries/Format.php b/application/libraries/Format.php index 13df0cf0..013e50f2 100644 --- a/application/libraries/Format.php +++ b/application/libraries/Format.php @@ -249,7 +249,7 @@ public function to_json() $callback = isset($_GET['callback']) ? $_GET['callback'] : ''; if ($callback === '') { - return json_encode($this->_data); + return json_encode($this->_data, JSON_NUMERIC_CHECK); /* Had to take out this code, it doesn't work on Objects. $str = $this->_data; @@ -270,13 +270,13 @@ public function to_json() { // this is a jsonp request, the content-type must be updated to be text/javascript header("Content-Type: application/javascript"); - return $callback . "(" . json_encode($this->_data) . ");"; + return $callback . "(" . json_encode($this->_data, JSON_NUMERIC_CHECK) . ");"; } 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 json_encode($this->_data); + return json_encode($this->_data, JSON_NUMERIC_CHECK); } } From 6f9aaf781ca52a7d7d35ed555c066757ee6c6702 Mon Sep 17 00:00:00 2001 From: Chris Kacerguis Date: Sat, 30 May 2015 13:47:37 -0500 Subject: [PATCH 005/444] Adds CI 3.x check. --- 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 69f876f9..f5497f88 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -197,6 +197,12 @@ public function __construct($config = 'rest') { parent::__construct(); + // Check to see if this is CI 3.x + if(explode('.', CI_VERSION, 2)[0] > 2) + { + die('REST Server requires CodeIgniter 3.x'); + } + // Start the timer for how long the request takes $this->_start_rtime = microtime(TRUE); From 0779c79c5fc18b93fee2ac6795da57a26a6d1989 Mon Sep 17 00:00:00 2001 From: Chris Kacerguis Date: Sat, 30 May 2015 19:13:09 -0500 Subject: [PATCH 006/444] fix for having the dumb today. --- 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 f5497f88..5ae579df 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -198,7 +198,7 @@ public function __construct($config = 'rest') parent::__construct(); // Check to see if this is CI 3.x - if(explode('.', CI_VERSION, 2)[0] > 2) + if(explode('.', CI_VERSION, 2)[0] < 3) { die('REST Server requires CodeIgniter 3.x'); } From a7968a6d65e8fa756bbc9d55018476a2be639d98 Mon Sep 17 00:00:00 2001 From: Ivan Tcholakov Date: Wed, 3 Jun 2015 13:42:59 +0300 Subject: [PATCH 007/444] Removing an obsolete fragment of code. --- application/libraries/REST_Controller.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 5ae579df..8f5bd7c8 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -1219,10 +1219,6 @@ public function patch($key = NULL, $xss_clean = TRUE) */ 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; } From 776a0fcfeb265ad9281114d8b5e28fe01b9d3917 Mon Sep 17 00:00:00 2001 From: Chris Kacerguis Date: Thu, 4 Jun 2015 08:42:15 -0500 Subject: [PATCH 008/444] removed JSON_NUMERIC_CHECK. it was causing too many issues --- 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 013e50f2..a8ee194d 100644 --- a/application/libraries/Format.php +++ b/application/libraries/Format.php @@ -249,7 +249,7 @@ public function to_json() $callback = isset($_GET['callback']) ? $_GET['callback'] : ''; if ($callback === '') { - return json_encode($this->_data, JSON_NUMERIC_CHECK); + return json_encode($this->_data); /* Had to take out this code, it doesn't work on Objects. $str = $this->_data; From f24dbdfd839e12a800323940c3a92e6d5b4f5a9f Mon Sep 17 00:00:00 2001 From: Ivan Tcholakov Date: Thu, 4 Jun 2015 20:38:22 +0300 Subject: [PATCH 009/444] Complete removal of JSON_NUMERIC_CHECK. --- 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 a8ee194d..b4f23d74 100644 --- a/application/libraries/Format.php +++ b/application/libraries/Format.php @@ -270,13 +270,13 @@ public function to_json() { // this is a jsonp request, the content-type must be updated to be text/javascript header("Content-Type: application/javascript"); - return $callback . "(" . json_encode($this->_data, JSON_NUMERIC_CHECK) . ");"; + 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 json_encode($this->_data, JSON_NUMERIC_CHECK); + return json_encode($this->_data); } } From 852740e7c636e4f44b3a8e3afc15155795e22c9b Mon Sep 17 00:00:00 2001 From: Chris Kacerguis Date: Tue, 9 Jun 2015 22:26:04 -0500 Subject: [PATCH 010/444] Security fix. Fixes #461 --- 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 8f5bd7c8..17299945 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -197,6 +197,9 @@ public function __construct($config = 'rest') { parent::__construct(); + // disable XML Entity (security vulnerability) + libxml_disable_entity_loader(true); + // Check to see if this is CI 3.x if(explode('.', CI_VERSION, 2)[0] < 3) { From 55d1456acff2af32992a25610b7345d8c730ad0b Mon Sep 17 00:00:00 2001 From: muru Date: Fri, 26 Jun 2015 23:31:44 +0300 Subject: [PATCH 011/444] Fixed error occurring when $ldaphost not set Even though the user might have not configured ldap correctly, there is no need to "bug out" like this --- 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 17299945..b6ce6438 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -1269,7 +1269,7 @@ protected function _perform_ldap_auth($username = '', $password = NULL) 'basedn' => $this->config->item('basedn', 'ldap'), ]; - log_message('debug', 'LDAP Auth: Connect to ' . $ldaphost); + log_message('debug', 'LDAP Auth: Connect to ' . (isset($ldaphost) ? $ldaphost : '[ldap not configured]')); $ldapconfig['authrealm'] = $this->config->item('domain', 'ldap'); From eb6a05fbc446c78b940098fdd74fe9464d65a20b Mon Sep 17 00:00:00 2001 From: muru Date: Fri, 26 Jun 2015 23:50:20 +0300 Subject: [PATCH 012/444] Using NULL as default for $xss_clean If NULL is passed then it will use the user's specified setting under global_xss_filtering in config --- application/libraries/REST_Controller.php | 54 +++++++++++++++-------- 1 file changed, 35 insertions(+), 19 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index b6ce6438..1f12b1ed 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -180,6 +180,17 @@ abstract class REST_Controller extends CI_Controller */ protected $_apiuser; + /** + * Enable XSS flag + * + * Determines whether the XSS filter is always active when + * GET, OPTIONS, HEAD, POST, PUT, DELETE and PATCH data is encountered. + * Set automatically based on config setting. + * + * @var bool + */ + protected $_enable_xss = FALSE; + /** * Developers can extend this class and add a check in here. */ @@ -200,6 +211,9 @@ public function __construct($config = 'rest') // disable XML Entity (security vulnerability) libxml_disable_entity_loader(true); + // Set the default value of global xss filtering. Same approach as CodeIgniter 3 + $this->_enable_xss = (config_item('global_xss_filtering') === TRUE); + // Check to see if this is CI 3.x if(explode('.', CI_VERSION, 2)[0] < 3) { @@ -1098,10 +1112,10 @@ protected function _parse_delete() * * @access public * @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. + * @param boolean $xss_clean Whether to apply XSS filtering * @return string The GET argument value. */ - public function get($key = NULL, $xss_clean = TRUE) + public function get($key = NULL, $xss_clean = NULL) { if ($key === NULL) { return $this->_get_args; @@ -1115,10 +1129,10 @@ public function get($key = NULL, $xss_clean = TRUE) * * @access public * @param string $key The OPTIONS/GET argument key - * @param boolean $xss_clean Whether the value should be XSS cleaned or not + * @param boolean $xss_clean Whether to apply XSS filtering * @return string The OPTIONS/GET argument value */ - public function options($key = NULL, $xss_clean = TRUE) + public function options($key = NULL, $xss_clean = NULL) { if ($key === NULL) { return $this->_options_args; @@ -1132,10 +1146,10 @@ public function options($key = NULL, $xss_clean = TRUE) * * @access public * @param string $key The HEAD/GET argument key - * @param boolean $xss_clean Whether the value should be XSS cleaned or not + * @param boolean $xss_clean Whether to apply XSS filtering * @return string The HEAD/GET argument value */ - public function head($key = NULL, $xss_clean = TRUE) + public function head($key = NULL, $xss_clean = NULL) { if ($key === NULL) { return $this->head_args; @@ -1149,10 +1163,10 @@ public function head($key = NULL, $xss_clean = TRUE) * * @access public * @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. + * @param boolean $xss_clean Whether to apply XSS filtering * @return string The POST argument value. */ - public function post($key = NULL, $xss_clean = TRUE) + public function post($key = NULL, $xss_clean = NULL) { if ($key === NULL) { return $this->_post_args; @@ -1166,10 +1180,10 @@ public function post($key = NULL, $xss_clean = TRUE) * * @access public * @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. + * @param boolean $xss_clean Whether to apply XSS filtering * @return string The PUT argument value. */ - public function put($key = NULL, $xss_clean = TRUE) + public function put($key = NULL, $xss_clean = NULL) { if ($key === NULL) { return $this->_put_args; @@ -1183,10 +1197,10 @@ public function put($key = NULL, $xss_clean = TRUE) * * @access public * @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. + * @param boolean $xss_clean Whether to apply XSS filtering * @return string The DELETE argument value. */ - public function delete($key = NULL, $xss_clean = TRUE) + public function delete($key = NULL, $xss_clean = NULL) { if ($key === NULL) { return $this->_delete_args; @@ -1200,10 +1214,10 @@ public function delete($key = NULL, $xss_clean = TRUE) * * @access public * @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. + * @param boolean $xss_clean Whether to apply XSS filtering * @return string The PATCH argument value. */ - public function patch($key = NULL, $xss_clean = TRUE) + public function patch($key = NULL, $xss_clean = NULL) { if ($key === NULL) { return $this->_patch_args; @@ -1213,16 +1227,18 @@ public function patch($key = NULL, $xss_clean = TRUE) } /** - * Process to protect from XSS attacks. + * Sanitizes data so that Cross Site Scripting Hacks can be + * prevented. * * @access protected - * @param string $val The input. - * @param boolean $process Do clean or note the input. + * @param string $value Input data + * @param boolean $xss_clean Whether to apply XSS filtering * @return string */ - protected function _xss_clean($val, $process) + protected function _xss_clean($value, $xss_clean) { - return $process ? $this->security->xss_clean($val) : $val; + is_bool($xss_clean) OR $xss_clean = $this->_enable_xss; + return $xss_clean === TRUE ? $this->security->xss_clean($value) : $value; } /** From be00649c59c34232363b963d054876f134048197 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Sat, 27 Jun 2015 08:18:24 +0300 Subject: [PATCH 013/444] Added possible formats and defaulted to json json is now the most widely used format for api responses --- application/config/rest.php | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/application/config/rest.php b/application/config/rest.php index 1e70532c..0e4396fa 100644 --- a/application/config/rest.php +++ b/application/config/rest.php @@ -17,12 +17,19 @@ | REST Format |-------------------------------------------------------------------------- | -| What format should the data be returned in by default? +| The default format of the response | -| Default: xml +| 'array': Array data structure +| 'csv': Comma separated file +| 'json': Uses json_encode(). Note: If a GET query string +| called 'callback' is passed then jsonp will be returned +| 'html' HTML using the table library +| 'php': Uses var_export() +| 'serialized: Uses serialize() +| 'xml': Uses simplexml_load_string() | */ -$config['rest_default_format'] = 'xml'; +$config['rest_default_format'] = 'json'; /* |-------------------------------------------------------------------------- From f009a686f9161f4a408c672a4d38adc90e48ad6b Mon Sep 17 00:00:00 2001 From: softwarespot Date: Sat, 27 Jun 2015 10:07:51 +0300 Subject: [PATCH 014/444] Changed the config file to match that of CI3's layout Improved structure, fixed typos and now similar to the layout in CodeIgniter 3 --- application/config/rest.php | 320 +++++++++++++++++------------------- 1 file changed, 150 insertions(+), 170 deletions(-) diff --git a/application/config/rest.php b/application/config/rest.php index 0e4396fa..104d84b1 100644 --- a/application/config/rest.php +++ b/application/config/rest.php @@ -1,13 +1,13 @@ -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' +| For digest authentication the library function should return already a stored +| md5(username:restrealm:password) for that username +| +| e.g: md5('admin:REST API:1234') = '1e957ebc35631ab22d5bd6526bd14ea2' | */ $config['auth_library_class'] = ''; @@ -134,7 +133,7 @@ | | Set as many config entries as needed. Any methods not set will use the default 'rest_auth' config value. | -| example: +| e.g: | | $config['auth_override_class_method']['deals']['view'] = 'none'; | $config['auth_override_class_method']['deals']['insert'] = 'digest'; @@ -151,48 +150,47 @@ // $config['auth_override_class_method']['dashboard']['*'] = 'basic'; -//---Uncomment list line for the wildard unit test -//$config['auth_override_class_method']['wildcard_test_cases']['*'] = 'basic'; +// ---Uncomment list line for the wildard unit test +// $config['auth_override_class_method']['wildcard_test_cases']['*'] = 'basic'; + /* |-------------------------------------------------------------------------- -| REST Login usernames +| REST Login Usernames |-------------------------------------------------------------------------- | | Array of usernames and passwords for login, if ldap is configured this is ignored | -| array('admin' => '1234') -| */ -$config['rest_valid_logins'] = array('admin' => '1234'); +$config['rest_valid_logins'] = ['admin' => '1234']; /* |-------------------------------------------------------------------------- | Global IP Whitelisting |-------------------------------------------------------------------------- | -| Limit connections to your REST server to whitelisted IP addresses. +| 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 +| 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 +| 2. Set to TRUE with auth set to FALSE to allow whitelisted IPs access with no login +| 3. Set to FALSE but set 'auth_override_class_method' to 'whitelist' to | restrict certain methods to IPs in your whitelist | */ -$config['rest_ip_whitelist_enabled'] = false; +$config['rest_ip_whitelist_enabled'] = FALSE; /* |-------------------------------------------------------------------------- | REST IP Whitelist |-------------------------------------------------------------------------- | -| Limit connections to your REST server to a comma separated +| Limit connections to your REST server with a comma separated | list of IP addresses | -| Example: $config['rest_ip_whitelist'] = '123.456.789.0, 987.654.32.1'; +| e.g: '123.456.789.0, 987.654.32.1' | -| 127.0.0.1 and 0.0.0.0 are allowed by default. +| 127.0.0.1 and 0.0.0.0 are allowed by default | */ $config['rest_ip_whitelist'] = ''; @@ -202,23 +200,22 @@ | Global IP Blacklisting |-------------------------------------------------------------------------- | -| Prevent connections to your REST server from blacklisted IP addresses. +| Prevent connections to the REST server from blacklisted IP addresses | | Usage: -| 1. Set to true *and* add any IP address to "rest_ip_blacklist" option +| 1. Set to TRUE and add any IP address to 'rest_ip_blacklist' | */ -$config['rest_ip_blacklist_enabled'] = false; +$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'; +| Prevent connections from the following IP addresses | +| e.g: '123.456.789.0, 987.654.32.1' | */ $config['rest_ip_blacklist'] = ''; @@ -229,9 +226,7 @@ |-------------------------------------------------------------------------- | | Connect to a database group for keys, logging, etc. It will only connect -| if you have any of these features enabled. -| -| 'default' +| if you have any of these features enabled | */ $config['rest_database_group'] = 'default'; @@ -241,9 +236,7 @@ | REST API Keys Table Name |-------------------------------------------------------------------------- | -| The table name in your database that stores API Keys. -| -| 'keys' +| The table name in your database that stores API keys | */ $config['rest_keys_table'] = 'keys'; @@ -253,21 +246,21 @@ | REST Enable Keys |-------------------------------------------------------------------------- | -| When set to true REST_Controller will look for a key and match it to the DB. -| If no key is provided, the request will return an error. +| When set to TRUE, the REST API will look for a column name called 'key'. +| If no key is provided, the request will result in an error. To override the +| column name see 'rest_key_column' | -| FALSE - - 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', - `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; +| Default table schema: +| CREATE TABLE `keys` ( +| `id` INT(11) NOT NULL AUTO_INCREMENT, +| `key` VARCHAR(40) NOT NULL, +| `level` INT(2) NOT NULL, +| `ignore_limits` TINY(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; | */ $config['rest_enable_keys'] = FALSE; @@ -277,8 +270,8 @@ | 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? +| If not using the default table schema in 'rest_enable_keys', specify the +| column name to match e.g. my_key | */ $config['rest_key_column'] = 'key'; @@ -288,10 +281,10 @@ | REST Key Length |-------------------------------------------------------------------------- | -| How long should created keys be? Double check this in your db schema. +| Length of the created keys. Check your default database schema on the +| maximum length allowed | -| Default: 32 -| Max: 40 +| Note: The maximum length is 40 | */ $config['rest_key_length'] = 40; @@ -301,141 +294,133 @@ | REST API Key Variable |-------------------------------------------------------------------------- | -| Which variable will provide us the API Key -| -| Default: X-API-KEY +| Custom header to specify the API key + +| Note: Custom headers with the X- prefix are deprecated as of +| 2012/06/12. See RFC 6648 specification for more details | */ $config['rest_key_name'] = 'X-API-KEY'; /* |-------------------------------------------------------------------------- -| REST API Logs Table Name +| REST Enable Logging |-------------------------------------------------------------------------- | -| The table name in your database that stores logs. -| -| 'logs' +| When set to TRUE, the REST API will log actions based on the column names 'key', 'date', +| 'time' and 'ip_address'. This is a general rule that can be overridden in the +| $this->method array for each controller +| +| Default table schema: +| CREATE TABLE `logs` ( +| `id` INT(11) NOT NULL AUTO_INCREMENT, +| `uri` VARCHAR(255) NOT NULL, +| `method` VARCHAR(6) NOT NULL, +| `params` TEXT DEFAULT NULL, +| `api_key` VARCHAR(40) NOT NULL, +| `ip_address` VARCHAR(45) NOT NULL, +| `time` INT(11) NOT NULL, +| `rtime` FLOAT DEFAULT NULL, +| `authorized` VARCHAR(1) NOT NULL, +| `response_code` SMALLINT(3) NOT NULL, +| PRIMARY KEY (`id`) +| ) ENGINE=InnoDB DEFAULT CHARSET=utf8; | */ -$config['rest_logs_table'] = 'logs'; +$config['rest_enable_logging'] = FALSE; /* |-------------------------------------------------------------------------- -| REST Enable Logging +| REST API Logs Table Name |-------------------------------------------------------------------------- | -| When set to true REST_Controller will log actions based on key, date, -| time and IP address. This is a general rule that can be overridden in the -| $this->method array in each controller. -| -| FALSE -| - CREATE TABLE `logs` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `uri` varchar(255) NOT NULL, - `method` varchar(6) NOT NULL, - `params` text DEFAULT NULL, - `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, - `response_code` smallint(3) NOT NULL, - PRIMARY KEY (`id`) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8; +| If not using the default table schema in 'rest_enable_logging', specify the +| table name to match e.g. my_logs | */ -$config['rest_enable_logging'] = FALSE; - +$config['rest_logs_table'] = 'logs'; /* |-------------------------------------------------------------------------- -| REST API Access Table Name +| REST Method Access Control |-------------------------------------------------------------------------- +| When set to TRUE, the REST API will check the access table to see if +| the API key can access that controller. 'rest_enable_keys' must be enabled +| to use this | -| The table name in your database that stores the access controls. -| -| 'access' +| Default table schema: +| 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_access_table'] = 'access'; +$config['rest_enable_access'] = FALSE; /* |-------------------------------------------------------------------------- -| REST Method Access Control +| REST API Access Table Name |-------------------------------------------------------------------------- | -| 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; +| If not using the default table schema in 'rest_enable_access', specify the +| table name to match e.g. my_access | */ -$config['rest_enable_access'] = FALSE; - +$config['rest_access_table'] = 'access'; /* |-------------------------------------------------------------------------- | 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. +| When set to TRUE, the REST API log parameters will be stored in the database as JSON +| Set to FALSE to log as serialized PHP | */ $config['rest_logs_json_params'] = FALSE; /* |-------------------------------------------------------------------------- -| REST API Limits Table Name +| REST Enable Limits |-------------------------------------------------------------------------- | -| The table name in your database that stores limits. +| When set to TRUE, the REST API will count the number of uses of each method +| by an API key each hour. This is a general rule that can be overridden in the +| $this->method array in each controller +| +| Default table schema: +| CREATE TABLE `limits` ( +| `id` INT(11) NOT NULL AUTO_INCREMENT, +| `uri` VARCHAR(255) NOT NULL, +| `count` INT(10) NOT NULL, +| `hour_started` INT(11) NOT NULL, +| `api_key` VARCHAR(40) NOT NULL, +| PRIMARY KEY (`id`) +| ) ENGINE=InnoDB DEFAULT CHARSET=utf8; +| +| To specify the limits within the controller's __construct() method, add per-method +| limits with: | -| 'limits' +| $this->method['METHOD_NAME']['limit'] = [NUM_REQUESTS_PER_HOUR]; | +| See application/controllers/api/example.php for examples */ -$config['rest_limits_table'] = 'limits'; +$config['rest_enable_limits'] = FALSE; /* |-------------------------------------------------------------------------- -| REST Enable Limits +| REST API Limits Table Name |-------------------------------------------------------------------------- | -| When set to true REST_Controller will count the number of uses of each method -| by an API key each hour. This is a general rule that can be overridden in the -| $this->method array in each controller. -| -| FALSE -| - CREATE TABLE `limits` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `uri` varchar(255) NOT NULL, - `count` int(10) NOT NULL, - `hour_started` int(11) NOT NULL, - `api_key` varchar(40) NOT NULL, - PRIMARY KEY (`id`) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8; +| If not using the default table schema in 'rest_enable_limits', specify the +| table name to match e.g. my_limits | -| 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; +$config['rest_limits_table'] = 'limits'; /* |-------------------------------------------------------------------------- @@ -445,8 +430,6 @@ | Set to TRUE to ignore the HTTP Accept and speed up each request a little. | Only do this if you are using the $this->rest_format or /format/xml in URLs | -| FALSE -| */ $config['rest_ignore_http_accept'] = FALSE; @@ -455,15 +438,12 @@ | 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 allow AJAX requests only. Set to FALSE to accept HTTP requests +| +| Note: If set to TRUE and the request is not AJAX, a 505 response with the +| error message 'Only AJAX requests are accepted.' will be returned. | -| FALSE +| Hint: This is good for production environments | */ $config['rest_ajax_only'] = FALSE; - -/* End of file config.php */ -/* Location: ./system/application/config/rest.php */ From d75377ccecbd815519cb111f69376927b2992ced Mon Sep 17 00:00:00 2001 From: softwarespot Date: Sat, 27 Jun 2015 10:19:01 +0300 Subject: [PATCH 015/444] Moved the 'no direct access' to a new line This is similar to CodeIgniter 3 --- 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 1f12b1ed..5ec01197 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -1,4 +1,6 @@ - Date: Sat, 27 Jun 2015 10:21:46 +0300 Subject: [PATCH 016/444] Added PHP version check. Fixes #454 --- application/libraries/REST_Controller.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 5ec01197..70c7d416 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -213,8 +213,12 @@ public function __construct($config = 'rest') // disable XML Entity (security vulnerability) libxml_disable_entity_loader(true); - // Set the default value of global xss filtering. Same approach as CodeIgniter 3 - $this->_enable_xss = (config_item('global_xss_filtering') === TRUE); + // Check to see if PHP is equal to or greater than 5.4.x + if (is_php('5.4') === FALSE) + { + // CodeIgniter 3 is recommended for v5.4 or above + exit('Using PHP v' . PHP_VERSION . ', though PHP v5.4 or greater is required'); + } // Check to see if this is CI 3.x if(explode('.', CI_VERSION, 2)[0] < 3) @@ -222,6 +226,9 @@ public function __construct($config = 'rest') die('REST Server requires CodeIgniter 3.x'); } + // Set the default value of global xss filtering. Same approach as CodeIgniter 3 + $this->_enable_xss = (config_item('global_xss_filtering') === TRUE); + // Start the timer for how long the request takes $this->_start_rtime = microtime(TRUE); From ffa51c8c7e391433d32ca4559dd72d1dc339ba55 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Sat, 27 Jun 2015 10:23:37 +0300 Subject: [PATCH 017/444] Changed die to exit --- 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 70c7d416..c58a3ff7 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -223,7 +223,7 @@ public function __construct($config = 'rest') // Check to see if this is CI 3.x if(explode('.', CI_VERSION, 2)[0] < 3) { - die('REST Server requires CodeIgniter 3.x'); + exit('REST Server requires CodeIgniter 3.x'); } // Set the default value of global xss filtering. Same approach as CodeIgniter 3 From 0abb563841fe15030d1441c1f1958f96c75028e6 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Sat, 27 Jun 2015 21:50:54 +0300 Subject: [PATCH 018/444] Moved new default json to the top of the array --- 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 c58a3ff7..4308ae56 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -166,8 +166,8 @@ abstract class REST_Controller extends CI_Controller * @var array */ protected $_supported_formats = [ + 'json' => 'application/json', 'xml' => 'application/xml', - 'json' => 'application/json', 'jsonp' => 'application/javascript', 'serialized' => 'application/vnd.php.serialized', 'php' => 'text/plain', From e2df332186d294d89270a5c32a7a3a19c8c7ae49 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Sat, 27 Jun 2015 23:12:32 +0300 Subject: [PATCH 019/444] Removed pre from the supported CI version --- 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 4308ae56..126d17f5 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -13,7 +13,7 @@ * @author Phil Sturgeon, Chris Kacerguis * @license MIT * @link https://github.com/chriskacerguis/codeigniter-restserver - * @version 3.0.0-pre + * @version 3.0.0 */ abstract class REST_Controller extends CI_Controller { From a99a978510efb553446a7d0e8864c5fd8639c152 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Sat, 27 Jun 2015 23:15:41 +0300 Subject: [PATCH 020/444] Re-wrote _check_blacklist_auth() with a regex Instead of enumerating through the array twice, just match with a single regex pattern --- 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 126d17f5..82ad86fb 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -1542,21 +1542,25 @@ protected function _prepare_digest_auth() } } - /** - * Check if the client's ip is in the 'rest_ip_blacklist' config + /** + * Checks if the client's ip is in the 'rest_ip_blacklist' config and generates a 401 response * * @access protected */ protected function _check_blacklist_auth() { - $blacklist = explode(',', config_item('rest_ip_blacklist')); + // Match an ip address in a blacklist e.g. 127.0.0.0, 0.0.0.0 + $pattern = sprintf('/(?:,\s*|^)\Q%s\E(?=,\s*|$)/m', $this->input->ip_address()); - foreach ($blacklist AS &$ip) { - $ip = trim($ip); - } - - if (in_array($this->input->ip_address(), $blacklist)) { - $this->response(['status' => FALSE, 'error' => 'IP Denied'], 401); + // Returns 1, 0 or FALSE (on error only). Therefore implicitly convert 1 to TRUE + if (preg_match($pattern, config_item('rest_ip_blacklist'))) + { + // Display an error response + $this->response([ + 'status' => FALSE, + 'error' => 'IP Denied' + ], + 401); } } From f72e50135bd01c692bed39bd717a539d63dd1e66 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Sat, 27 Jun 2015 23:16:50 +0300 Subject: [PATCH 021/444] Updated whitelist function comment --- 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 82ad86fb..8e1064b8 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -1565,7 +1565,7 @@ protected function _check_blacklist_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 and generates a 401 response * * @access protected */ From 9c8703373057922c817e906cd7dec2438909f279 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Sat, 27 Jun 2015 23:27:13 +0300 Subject: [PATCH 022/444] Fixed a standard class for the rest property being created twice --- 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 8e1064b8..02cfb74d 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -302,8 +302,6 @@ public function __construct($config = 'rest') // Developers can extend this class and add a check in here $this->early_checks(); - $this->rest = new StdClass(); - // Load DB if its enabled if (config_item('rest_database_group') && (config_item('rest_enable_keys') || config_item('rest_enable_logging'))) { $this->rest->db = $this->load->database(config_item('rest_database_group'), TRUE); From b022f4e5ef8c9c3147dd7841869d38b83406eacd Mon Sep 17 00:00:00 2001 From: softwarespot Date: Sat, 27 Jun 2015 23:30:42 +0300 Subject: [PATCH 023/444] Typo in comment where it's a function not class --- application/libraries/REST_Controller.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 02cfb74d..e9b2c62c 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -194,7 +194,9 @@ abstract class REST_Controller extends CI_Controller protected $_enable_xss = FALSE; /** - * Developers can extend this class and add a check in here. + * Extend this function to apply additional checking early on in the process + * + * @access protected */ protected function early_checks() { @@ -299,7 +301,7 @@ public function __construct($config = 'rest') // 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 + // Extend this function to apply additional checking early on in the process $this->early_checks(); // Load DB if its enabled From efd2f12b805ab7f538f19bacdf9491854a28496d Mon Sep 17 00:00:00 2001 From: softwarespot Date: Sat, 27 Jun 2015 23:34:16 +0300 Subject: [PATCH 024/444] Updated the constructor doc --- application/libraries/REST_Controller.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index e9b2c62c..07f42d20 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -204,9 +204,10 @@ protected function early_checks() } /** - * Constructor function - * @todo Document more please. - * @access public + * Constructor for the REST API + * + * @param string $config Configuration filename minus the file extension + * e.g: my_rest.php is passed as 'my_rest' */ public function __construct($config = 'rest') { From 4f530943e59b64037158ed79a75f62c08e68a8e0 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Sun, 28 Jun 2015 09:07:29 +0300 Subject: [PATCH 025/444] Fixed incorrect quotes This used to print the $username and not the value of $username --- 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 07f42d20..392a6b15 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -1426,7 +1426,7 @@ protected function _check_login($username = '', $password = FALSE) } if ($auth_source == 'ldap') { - log_message('debug', 'performing LDAP authentication for $username'); + log_message('debug', "performing LDAP authentication for $username"); return $this->_perform_ldap_auth($username, $password); } From 742245d74a4937053c06729cf3756c3654980f97 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Sun, 28 Jun 2015 09:37:13 +0300 Subject: [PATCH 026/444] Re-worked _prepare_basic_auth() and added a doc comment --- application/libraries/REST_Controller.php | 35 +++++++++++++---------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 392a6b15..929ebde9 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -1461,35 +1461,40 @@ protected function _check_php_session() } } - /** - * @todo document this. - * - * @access protected + /** + * Prepares for basic authentication */ 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')) { + if (config_item('rest_ip_whitelist_enabled')) + { $this->_check_whitelist_auth(); } - $username = NULL; - $password = NULL; + // Returns NULL if the SERVER variables PHP_AUTH_USER and HTTP_AUTHENTICATION don't exist + $username = $this->input->server('PHP_AUTH_USER'); + $http_auth = $this->input->server('HTTP_AUTHENTICATION'); - // mod_php - if ($this->input->server('PHP_AUTH_USER')) { - $username = $this->input->server('PHP_AUTH_USER'); + $password = NULL; + if ($username !== NULL) + { $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) { + elseif ($http_auth !== NULL) + { + // If the authentication header is set as basic, then extract the username and password from + // HTTP_AUTHORIZATION e.g. my_username:my_password. This is passed in the .htaccess file + if (strpos(strtolower($http_auth), 'basic') === 0) + { + // Search online for HTTP_AUTHORIZATION workaround to explain what this is doing list($username, $password) = explode(':', base64_decode(substr($this->input->server('HTTP_AUTHORIZATION'), 6))); } } - if ( ! $this->_check_login($username, $password)) { + // Check if the user is logged into the system + if ($this->_check_login($username, $password) === FALSE) + { $this->_force_login(); } } From 85ed47b57bcbf4b4b6494eb37912716060217088 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Sun, 28 Jun 2015 10:59:12 +0300 Subject: [PATCH 027/444] Fixed the use of tabs instead of whitespace --- application/libraries/REST_Controller.php | 24 +++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 929ebde9..ffa14afc 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -7,12 +7,12 @@ * * A fully RESTful server implementation for CodeIgniter using one library, one config file and one controller. * - * @package CodeIgniter - * @subpackage Libraries - * @category Libraries - * @author Phil Sturgeon, Chris Kacerguis + * @package CodeIgniter + * @subpackage Libraries + * @category Libraries + * @author Phil Sturgeon, Chris Kacerguis * @license MIT - * @link https://github.com/chriskacerguis/codeigniter-restserver + * @link https://github.com/chriskacerguis/codeigniter-restserver * @version 3.0.0 */ abstract class REST_Controller extends CI_Controller @@ -166,7 +166,7 @@ abstract class REST_Controller extends CI_Controller * @var array */ protected $_supported_formats = [ - 'json' => 'application/json', + 'json' => 'application/json', 'xml' => 'application/xml', 'jsonp' => 'application/javascript', 'serialized' => 'application/vnd.php.serialized', @@ -189,7 +189,7 @@ abstract class REST_Controller extends CI_Controller * GET, OPTIONS, HEAD, POST, PUT, DELETE and PATCH data is encountered. * Set automatically based on config setting. * - * @var bool + * @var bool */ protected $_enable_xss = FALSE; @@ -320,7 +320,7 @@ public function __construct($config = 'rest') $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') && $this->auth_override !== TRUE) { $this->_allow = $this->_detect_api_key(); } @@ -573,8 +573,8 @@ public function response($data = NULL, $http_code = NULL, $continue = FALSE) */ protected function _detect_ssl() { - // $_SERVER['HTTPS'] (http://php.net/manual/en/reserved.variables.server.php) - // Set to a non-empty value if the script was queried through the HTTPS protocol + // $_SERVER['HTTPS'] (http://php.net/manual/en/reserved.variables.server.php) + // Set to a non-empty value if the script was queried through the HTTPS protocol return (isset($_SERVER['HTTPS']) && !empty($_SERVER['HTTPS'])); } @@ -1461,7 +1461,7 @@ protected function _check_php_session() } } - /** + /** * Prepares for basic authentication */ protected function _prepare_basic_auth() @@ -1548,7 +1548,7 @@ protected function _prepare_digest_auth() } } - /** + /** * Checks if the client's ip is in the 'rest_ip_blacklist' config and generates a 401 response * * @access protected From 637e24a6aaa49d70ee6333f1b4a1d40d9ce1ecef Mon Sep 17 00:00:00 2001 From: softwarespot Date: Sun, 28 Jun 2015 11:20:33 +0300 Subject: [PATCH 028/444] Formatted brackets on a new line Adhere to the style guide of CI3 - http://www.codeigniter.com/user_guide/general/styleguide.html --- application/libraries/REST_Controller.php | 195 ++++++++++++++-------- 1 file changed, 130 insertions(+), 65 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index ffa14afc..dc9f06a0 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -251,7 +251,8 @@ public function __construct($config = 'rest') $this->request = new stdClass(); // Check to see if this IP is Blacklisted - if ($this->config->item('rest_ip_blacklist_enabled')) { + if ($this->config->item('rest_ip_blacklist_enabled')) + { $this->_check_blacklist_auth(); } @@ -262,7 +263,8 @@ public function __construct($config = 'rest') $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'} = []; } @@ -278,14 +280,16 @@ public function __construct($config = 'rest') $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 && $this->request->body) { + if ($this->request->format && $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->_args = array_merge( + $this->_get_args, $this->_options_args, $this->_patch_args, $this->_head_args , @@ -306,12 +310,14 @@ public function __construct($config = 'rest') $this->early_checks(); // Load DB if its enabled - if (config_item('rest_database_group') && (config_item('rest_enable_keys') || config_item('rest_enable_logging'))) { + if (config_item('rest_database_group') && (config_item('rest_enable_keys') || 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")) { + elseif (property_exists($this, "db")) + { $this->rest->db = $this->db; } @@ -321,18 +327,21 @@ public function __construct($config = 'rest') // Checking for keys? GET TO WorK! // Skip keys test for $config['auth_override_class_method']['class'['method'] = 'none' - if (config_item('rest_enable_keys') && $this->auth_override !== TRUE) { + if (config_item('rest_enable_keys') && $this->auth_override !== TRUE) + { $this->_allow = $this->_detect_api_key(); } // only allow ajax requests - if (!$this->input->is_ajax_request() && config_item('rest_ajax_only')) { + if (!$this->input->is_ajax_request() && config_item('rest_ajax_only')) + { $response = [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 } // 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 && !(config_item('rest_enable_keys') && $this->_allow === TRUE)) { + 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': @@ -345,7 +354,8 @@ public function __construct($config = 'rest') $this->_check_php_session(); break; } - if ($this->config->item('rest_ip_whitelist_enabled')) { + if ($this->config->item('rest_ip_whitelist_enabled')) + { $this->_check_whitelist_auth(); } } @@ -362,7 +372,8 @@ 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')) { + if (config_item('rest_enable_logging')) + { $this->_log_access_time(); } @@ -388,7 +399,8 @@ public function _remap($object_called, $arguments) $pattern = '/^(.*)\.('.implode('|', array_keys($this->_supported_formats)).')$/'; $matches = []; - if (preg_match($pattern, $object_called, $matches)) { + if (preg_match($pattern, $object_called, $matches)) + { $object_called = $matches[1]; } @@ -401,8 +413,10 @@ public function _remap($object_called, $arguments) $use_key = !(isset($this->methods[$controller_method]['key']) && $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') && $use_key && $this->_allow === FALSE) { - if (config_item('rest_enable_logging') && $log_method) { + if (config_item('rest_enable_keys') && $use_key && $this->_allow === FALSE) + { + if (config_item('rest_enable_logging') && $log_method) + { $this->_log_request(); } @@ -410,8 +424,10 @@ public function _remap($object_called, $arguments) } // Check to see if this key has access to the requested controller. - if (config_item('rest_enable_keys') && $use_key && !empty($this->rest->key) && !$this->_check_access()) { - if (config_item('rest_enable_logging') && $log_method) { + if (config_item('rest_enable_keys') && $use_key && !empty($this->rest->key) && !$this->_check_access()) + { + if (config_item('rest_enable_logging') && $log_method) + { $this->_log_request(); } @@ -419,14 +435,17 @@ 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([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') && !empty($this->rest->key)) { + if (config_item('rest_enable_keys') && !empty($this->rest->key)) + { // Check the limit - if (config_item('rest_enable_limits') && !$this->_check_limit($controller_method)) { + if (config_item('rest_enable_limits') && !$this->_check_limit($controller_method)) + { $response = [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); } @@ -438,7 +457,8 @@ public function _remap($object_called, $arguments) $authorized = $level <= $this->rest->level; // IM TELLIN! - if (config_item('rest_enable_logging') && $log_method) { + if (config_item('rest_enable_logging') && $log_method) + { $this->_log_request($authorized); } @@ -448,15 +468,18 @@ public function _remap($object_called, $arguments) } // No key stuff, but record that stuff is happening - elseif (config_item('rest_enable_logging') && $log_method) { + elseif (config_item('rest_enable_logging') && $log_method) + { $this->_log_request($authorized = TRUE); } // and...... GO! - try { + try + { $this->_fire_method([$this, $controller_method], $arguments); } - catch(Exception $ex) { + catch(Exception $ex) + { $this->_server_error_response($ex); } @@ -491,7 +514,8 @@ protected function _fire_method($method, $args) public function response($data = NULL, $http_code = NULL, $continue = FALSE) { // If data is NULL and not code provide, error and bail - if ($data === NULL && $http_code === NULL) { + if ($data === NULL && $http_code === NULL) + { $http_code = 404; // create the output variable here in the case of $this->response(array()); @@ -499,16 +523,20 @@ public function response($data = NULL, $http_code = NULL, $continue = FALSE) } // If data is NULL but http code provided, keep the output empty - elseif ($data === NULL && is_numeric($http_code)) { + elseif ($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 ($this->config->item('compress_output') === TRUE && $this->_zlib_oc == FALSE) { - if (extension_loaded('zlib')) { - if (isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE) { + if ($this->config->item('compress_output') === TRUE && $this->_zlib_oc == FALSE) + { + if (extension_loaded('zlib')) + { + if (isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE) + { ob_start('ob_gzhandler'); } } @@ -518,7 +546,8 @@ public function response($data = NULL, $http_code = NULL, $continue = FALSE) // @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)) { + if (method_exists($this, '_format_'.$this->response->format)) + { // Set the correct format header header('Content-Type: '.$this->_supported_formats[$this->response->format] . '; charset=' . strtolower($this->config->item('charset'))); @@ -526,7 +555,8 @@ 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)) { + elseif (method_exists($this->format, 'to_'.$this->response->format)) + { // Set the correct format header header('Content-Type: '.$this->_supported_formats[$this->response->format] . '; charset=' . strtolower($this->config->item('charset'))); @@ -542,7 +572,8 @@ public function response($data = NULL, $http_code = NULL, $continue = FALSE) set_status_header($http_code); // JC: Log response code only if rest logging enabled - if (config_item('rest_enable_logging')) { + if (config_item('rest_enable_logging')) + { $this->_log_response_code($http_code); } @@ -550,11 +581,13 @@ 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 && ! $this->config->item('compress_output')) { + if ( ! $this->_zlib_oc && ! $this->config->item('compress_output')) + { header('Content-Length: ' . strlen($output)); } - if($continue){ + if($continue) + { echo($output); ob_end_flush(); ob_flush(); @@ -606,14 +639,18 @@ protected function _server_error_response($ex) */ protected function _detect_input_format() { - if ($this->input->server('CONTENT_TYPE')) { + 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'), ';')) { + foreach ($this->_supported_formats as $format => $mime) + { + if (strpos($match = $this->input->server('CONTENT_TYPE'), ';')) + { $match = current(explode(';', $match)); } - if ($match == $mime) { + if ($match == $mime) + { return $format; } } @@ -636,12 +673,14 @@ protected function _detect_output_format() // Check if a file extension is used when no get arguments provided $matches = []; - if (!$this->_get_args && preg_match($pattern, $this->uri->uri_string(), $matches)) { + if (!$this->_get_args && preg_match($pattern, $this->uri->uri_string(), $matches)) + { return $matches[1]; } // Check if a file extension is used - elseif ($this->_get_args && !is_array(end($this->_get_args)) && preg_match($pattern, end($this->_get_args), $matches)) { + elseif ($this->_get_args && !is_array(end($this->_get_args)) && 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 $arg_keys = array_keys($this->_get_args); @@ -655,30 +694,37 @@ 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']) && array_key_exists($this->_get_args['format'], $this->_supported_formats)) { + if (isset($this->_get_args['format']) && 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 && $this->input->server('HTTP_ACCEPT')) { + if ($this->config->item('rest_ignore_http_accept') === FALSE && $this->input->server('HTTP_ACCEPT')) + { // Check all formats against the HTTP_ACCEPT header - foreach (array_keys($this->_supported_formats) as $format) { + 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' && $format != 'xml') { + if ($format != 'html' && $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' && strpos($this->input->server('HTTP_ACCEPT'), 'xml') === FALSE) { + if ($format == 'html' && strpos($this->input->server('HTTP_ACCEPT'), 'xml') === FALSE) + { return $format; } // If it is truly XML, it wont want any HTML - elseif ($format == 'xml' && strpos($this->input->server('HTTP_ACCEPT'), 'html') === FALSE) { + elseif ($format == 'xml' && strpos($this->input->server('HTTP_ACCEPT'), 'html') === FALSE) + { return $format; } } @@ -687,7 +733,8 @@ 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)) { + if ( ! empty($this->rest_format)) + { return $this->rest_format; } @@ -707,15 +754,19 @@ protected function _detect_method() { $method = strtolower($this->input->server('REQUEST_METHOD')); - if ($this->config->item('enable_emulate_request')) { - if ($this->input->post('_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')) { + } 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)) { + if (in_array($method, $this->allowed_http_methods) && method_exists($this, '_parse_' . $method)) + { return $method; } @@ -744,8 +795,10 @@ protected function _detect_api_key() $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())) { + 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; } @@ -761,15 +814,19 @@ protected function _detect_api_key() * 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)) { + if (!empty($row->is_private_key)) + { // Check for a list of valid ip addresses - if (isset($row->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)) { + 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; @@ -800,16 +857,19 @@ 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; } // They might have sent a few, make it an array - if (strpos($lang, ',') !== FALSE) { + if (strpos($lang, ',') !== FALSE) + { $langs = explode(',', $lang); $return_langs = []; - foreach ($langs as $lang) { + foreach ($langs as $lang) + { // Remove weight and strip space list($lang) = explode(';', $lang); $return_langs[] = trim($lang); @@ -860,7 +920,8 @@ 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) || !isset($this->methods[$controller_method]['limit'])) { + if ( ! empty($this->rest->ignore_limits) || !isset($this->methods[$controller_method]['limit'])) + { // On your way sonny-jim. return TRUE; } @@ -876,7 +937,8 @@ protected function _check_limit($controller_method) ->row(); // No calls yet for this key - if ( ! $result ) { + if ( ! $result ) + { // Right, set one up from scratch $this->rest->db->insert(config_item('rest_limits_table'), [ 'uri' => $this->uri->uri_string(), @@ -887,7 +949,8 @@ protected function _check_limit($controller_method) } // Been an hour since they called - elseif ($result->hour_started < time() - (60 * 60)) { + elseif ($result->hour_started < time() - (60 * 60)) + { // Reset the started period $this->rest->db ->where('uri', $this->uri->uri_string()) @@ -900,7 +963,8 @@ protected function _check_limit($controller_method) // They have called within the hour, so lets update else { // Your luck is out, you've called too many times! - if ($result->count >= $limit) { + if ($result->count >= $limit) + { return FALSE; } @@ -935,7 +999,8 @@ protected function _auth_override_check() } // check for wildcard flag for rules for classes - if(!empty($this->overrides_array[$this->router->class]['*'])){//check for class overides + if(!empty($this->overrides_array[$this->router->class]['*'])) // Check for class overrides + { // None auth override found, prepare nothing but send back a TRUE override flag if ($this->overrides_array[$this->router->class]['*'] == 'none') { From fc0867ad3a419305dd530471061e174fc1eff8b5 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Sun, 28 Jun 2015 11:41:44 +0300 Subject: [PATCH 029/444] Additional formatting of the code Adhering to the style guide CI3 --- application/libraries/REST_Controller.php | 556 +++++++++++++--------- 1 file changed, 326 insertions(+), 230 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index dc9f06a0..3d220044 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -4,7 +4,6 @@ /** * CodeIgniter Rest Controller - * * A fully RESTful server implementation for CodeIgniter using one library, one config file and one controller. * * @package CodeIgniter @@ -15,23 +14,21 @@ * @link https://github.com/chriskacerguis/codeigniter-restserver * @version 3.0.0 */ -abstract class REST_Controller extends CI_Controller -{ +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; + protected $rest_format = NULL; /** * Defines the list of method properties such as limit, log and level * * @var array */ - protected $methods = []; + protected $methods = []; /** * List of allowed HTTP methods @@ -46,134 +43,135 @@ 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 * * @var array */ - protected $_get_args = []; + protected $_get_args = []; /** * The arguments for the POST request method * * @var array */ - protected $_post_args = []; + protected $_post_args = []; /** * The insert_id of the log entry (if we have one) * * @var string - */ - protected $_insert_id = ''; + */ + protected $_insert_id = ''; /** * The arguments for the PUT request method * * @var array */ - protected $_put_args = []; + protected $_put_args = []; /** * The arguments for the DELETE request method * * @var array */ - protected $_delete_args = []; + protected $_delete_args = []; /** * The arguments for the PATCH request method * * @var array */ - protected $_patch_args = []; + protected $_patch_args = []; /** * The arguments for the HEAD request method * * @var array */ - protected $_head_args = []; + protected $_head_args = []; /** * The arguments for the OPTIONS request method * * @var array */ - protected $_options_args = []; + protected $_options_args = []; /** * The arguments from GET, POST, PUT, DELETE request methods combined. * * @var array */ - protected $_args = []; + protected $_args = []; /** * If the request is allowed based on the API key provided. * * @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 * * @var string - */ - protected $_user_ldap_dn = ''; + */ + protected $_user_ldap_dn = ''; /** * The start of the response time from the server * * @var string - */ - protected $_start_rtime = ''; + */ + protected $_start_rtime = ''; /** * The end of the response time from the server * * @var string - */ - protected $_end_rtime = ''; + */ + protected $_end_rtime = ''; /** * List all supported methods, the first will be the default format * * @var array */ - protected $_supported_formats = [ - 'json' => 'application/json', - 'xml' => 'application/xml', - 'jsonp' => 'application/javascript', - 'serialized' => 'application/vnd.php.serialized', - 'php' => 'text/plain', - 'html' => 'text/html', - 'csv' => 'application/csv' - ]; + protected $_supported_formats + = [ + 'json' => 'application/json', + 'xml' => 'application/xml', + 'jsonp' => 'application/javascript', + 'serialized' => 'application/vnd.php.serialized', + 'php' => 'text/plain', + 'html' => 'text/html', + 'csv' => 'application/csv' + ]; /** * Information about the current API user @@ -184,7 +182,6 @@ abstract class REST_Controller extends CI_Controller /** * Enable XSS flag - * * Determines whether the XSS filter is always active when * GET, OPTIONS, HEAD, POST, PUT, DELETE and PATCH data is encountered. * Set automatically based on config setting. @@ -200,7 +197,6 @@ abstract class REST_Controller extends CI_Controller */ protected function early_checks() { - } /** @@ -214,7 +210,7 @@ public function __construct($config = 'rest') parent::__construct(); // disable XML Entity (security vulnerability) - libxml_disable_entity_loader(true); + libxml_disable_entity_loader(TRUE); // Check to see if PHP is equal to or greater than 5.4.x if (is_php('5.4') === FALSE) @@ -224,7 +220,7 @@ public function __construct($config = 'rest') } // Check to see if this is CI 3.x - if(explode('.', CI_VERSION, 2)[0] < 3) + if (explode('.', CI_VERSION, 2)[0] < 3) { exit('REST Server requires CodeIgniter 3.x'); } @@ -242,13 +238,13 @@ public function __construct($config = 'rest') $this->load->library('format'); // init objects - $this->response = new stdClass(); - $this->rest = new stdClass(); + $this->response = new stdClass(); + $this->rest = new stdClass(); - $this->_zlib_oc = @ini_get('zlib.output_compression'); + $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')) @@ -257,25 +253,25 @@ public function __construct($config = 'rest') } // 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'} = []; + $this->{'_' . $this->request->method . '_args'} = []; } // Set up our GET variables - $this->_get_args = array_merge($this->_get_args, $this->uri->ruri_to_assoc()); + $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}(); @@ -284,7 +280,7 @@ public function __construct($config = 'rest') { $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; + $this->{'_' . $this->request->method . '_args'} = $this->request->body; } // Merge both for one mega-args variable @@ -292,19 +288,19 @@ public function __construct($config = 'rest') $this->_get_args, $this->_options_args, $this->_patch_args, - $this->_head_args , + $this->_head_args, $this->_put_args, $this->_post_args, $this->_delete_args, - $this->{'_'.$this->request->method.'_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(); // Extend this function to apply additional checking early on in the process $this->early_checks(); @@ -312,21 +308,21 @@ public function __construct($config = 'rest') // Load DB if its enabled if (config_item('rest_database_group') && (config_item('rest_enable_keys') || 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) elseif (property_exists($this, "db")) { - $this->rest->db = $this->db; + $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(); + $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') && $this->auth_override !== TRUE) { $this->_allow = $this->_detect_api_key(); @@ -343,7 +339,8 @@ public function __construct($config = 'rest') if ($this->auth_override !== TRUE && !(config_item('rest_enable_keys') && $this->_allow === TRUE)) { $rest_auth = strtolower($this->config->item('rest_auth')); - switch ($rest_auth) { + switch ($rest_auth) + { case 'basic': $this->_prepare_basic_auth(); break; @@ -363,8 +360,8 @@ public function __construct($config = 'rest') /** * Destructor function - * @author Chris Kacerguis * + * @author Chris Kacerguis * @access public */ public function __destruct() @@ -381,30 +378,31 @@ public function __destruct() /** * 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. * * @access public + * * @param string $object_called - * @param array $arguments The arguments passed to the controller method. + * @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') && !$this->_detect_ssl()) { + if (config_item('force_https') && !$this->_detect_ssl()) + { $this->response([config_item('rest_status_field_name') => FALSE, config_item('rest_message_field_name') => 'Unsupported protocol'], 403); } - $pattern = '/^(.*)\.('.implode('|', array_keys($this->_supported_formats)).')$/'; + $pattern = '/^(.*)\.(' . implode('|', array_keys($this->_supported_formats)) . ')$/'; $matches = []; 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']) && $this->methods[$controller_method]['log'] == FALSE); @@ -420,7 +418,7 @@ public function _remap($object_called, $arguments) $this->_log_request(); } - $this->response([config_item('rest_status_field_name') => FALSE, config_item('rest_message_field_name') => 'Invalid API Key '.$this->rest->key], 403); + $this->response([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. @@ -435,7 +433,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([config_item('rest_status_field_name') => FALSE, config_item('rest_message_field_name') => 'Unknown method.'], 404); } @@ -478,7 +476,7 @@ public function _remap($object_called, $arguments) { $this->_fire_method([$this, $controller_method], $arguments); } - catch(Exception $ex) + catch (Exception $ex) { $this->_server_error_response($ex); } @@ -488,12 +486,12 @@ public function _remap($object_called, $arguments) /** * Fire Method - * * Fires the designated controller method with the given arguments. * * @access protected + * * @param array $method The controller method to fire - * @param array $args The arguments to pass to the controller method + * @param array $args The arguments to pass to the controller method */ protected function _fire_method($method, $args) { @@ -502,12 +500,12 @@ 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. * * @access public - * @param array $data + * + * @param array $data * @param NULL|int $http_code * @param bool $continue */ @@ -529,7 +527,8 @@ public function response($data = NULL, $http_code = NULL, $continue = FALSE) } // Otherwise (if no data but 200 provided) or some data, carry on camping! - else { + else + { // Is compression requested? if ($this->config->item('compress_output') === TRUE && $this->_zlib_oc == FALSE) { @@ -546,25 +545,26 @@ public function response($data = NULL, $http_code = NULL, $continue = FALSE) // @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)) + if (method_exists($this, '_format_' . $this->response->format)) { // Set the correct format header - header('Content-Type: '.$this->_supported_formats[$this->response->format] . '; charset=' . strtolower($this->config->item('charset'))); + header('Content-Type: ' . $this->_supported_formats[$this->response->format] . '; charset=' . strtolower($this->config->item('charset'))); - $output = $this->{'_format_'.$this->response->format}($data); + $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)) + elseif (method_exists($this->format, 'to_' . $this->response->format)) { // Set the correct format header - header('Content-Type: '.$this->_supported_formats[$this->response->format] . '; charset=' . strtolower($this->config->item('charset'))); + header('Content-Type: ' . $this->_supported_formats[$this->response->format] . '; charset=' . strtolower($this->config->item('charset'))); - $output = $this->format->factory($data)->{'to_'.$this->response->format}(); + $output = $this->format->factory($data)->{'to_' . $this->response->format}(); } // Format not supported, output directly - else { + else + { $output = $data; } } @@ -581,58 +581,58 @@ 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 && ! $this->config->item('compress_output')) + if (!$this->_zlib_oc && !$this->config->item('compress_output')) { header('Content-Length: ' . strlen($output)); } - if($continue) + if ($continue) { echo($output); ob_end_flush(); ob_flush(); flush(); - }else{ + } + else + { exit($output); } } /** * Detect SSL use - * * Detect whether SSL is being used or not. * * @access protected */ protected function _detect_ssl() { - // $_SERVER['HTTPS'] (http://php.net/manual/en/reserved.variables.server.php) - // Set to a non-empty value if the script was queried through the HTTPS protocol - return (isset($_SERVER['HTTPS']) && !empty($_SERVER['HTTPS'])); + // $_SERVER['HTTPS'] (http://php.net/manual/en/reserved.variables.server.php) + // Set to a non-empty value if the script was queried through the HTTPS protocol + return (isset($_SERVER['HTTPS']) && !empty($_SERVER['HTTPS'])); } /** * Return server response - * * Method to send a response to the client in the event of a server error. * * @access public + * * @param object $ex */ protected function _server_error_response($ex) { $response = [ - config_item('rest_status_field_name') => FALSE, - config_item('rest_message_field_name') => [ - 'classname' => get_class($ex), 'message' => $ex->getMessage() - ] + config_item('rest_status_field_name') => FALSE, + config_item('rest_message_field_name') => [ + 'classname' => get_class($ex), 'message' => $ex->getMessage() + ] ]; $this->response($response, 500); } /** * Detect input format - * * Detect which format the HTTP Body is provided in * * @access protected @@ -661,7 +661,6 @@ protected function _detect_input_format() /** * Detect format - * * Detect which format should be used to output the data. * * @access protected @@ -669,7 +668,7 @@ protected function _detect_input_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 $matches = []; @@ -681,14 +680,14 @@ protected function _detect_output_format() // Check if a file extension is used elseif ($this->_get_args && !is_array(end($this->_get_args)) && 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)) { + //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 $arg_keys = array_keys($this->_get_args); $last_key = end($arg_keys); // 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]; } @@ -715,7 +714,8 @@ protected function _detect_output_format() } // HTML or XML have shown up as a match - else { + else + { // If it is truly HTML, it wont want any XML if ($format == 'html' && strpos($this->input->server('HTTP_ACCEPT'), 'xml') === FALSE) { @@ -733,7 +733,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)) + if (!empty($this->rest_format)) { return $this->rest_format; } @@ -744,7 +744,6 @@ protected function _detect_output_format() /** * Detect method - * * Detect which HTTP method is being used * * @access protected @@ -759,7 +758,8 @@ protected function _detect_method() if ($this->input->post('_method')) { $method = strtolower($this->input->post('_method')); - } elseif ($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')); } @@ -775,7 +775,6 @@ protected function _detect_method() /** * Detect API Key - * * See if the user has provided an API key * * @access protected @@ -787,7 +786,7 @@ protected function _detect_api_key() $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; @@ -797,7 +796,7 @@ 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(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; } @@ -808,7 +807,7 @@ protected function _detect_api_key() isset($row->level) && $this->rest->level = $row->level; isset($row->ignore_limits) && $this->rest->ignore_limits = $row->ignore_limits; - $this->_apiuser = $row; + $this->_apiuser = $row; /* * If "is private key" is enabled, compare the ip address with the list @@ -834,7 +833,9 @@ protected function _detect_api_key() } return $found_address; - } else { + } + else + { // There should be at least one IP address for this private key. return FALSE; } @@ -849,7 +850,6 @@ protected function _detect_api_key() /** * Detect language(s) - * * What language do they want it in? * * @access protected @@ -857,7 +857,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; } @@ -884,24 +884,26 @@ protected function _detect_lang() /** * Log request - * * Record the entry for awesomeness purposes * * @access protected + * * @param boolean $authorized + * * @return object */ protected function _log_request($authorized = FALSE) { - $status = $this->rest->db->insert(config_item('rest_logs_table'), [ - '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 - ]); + $status = $this->rest->db->insert( + config_item('rest_logs_table'), [ + '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(); @@ -910,17 +912,18 @@ protected function _log_request($authorized = FALSE) /** * Limiting requests - * * Check if the requests are coming in a tad too fast. * * @access protected - * @param string $controller_method The method being called. + * + * @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) || !isset($this->methods[$controller_method]['limit'])) + if (!empty($this->rest->ignore_limits) || !isset($this->methods[$controller_method]['limit'])) { // On your way sonny-jim. return TRUE; @@ -931,16 +934,17 @@ 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 for this key - if ( ! $result ) + if (!$result) { // Right, set one up from scratch - $this->rest->db->insert(config_item('rest_limits_table'), [ + $this->rest->db->insert( + config_item('rest_limits_table'), [ 'uri' => $this->uri->uri_string(), 'api_key' => isset($this->rest->key) ? $this->rest->key : '', 'count' => 1, @@ -953,15 +957,16 @@ protected function _check_limit($controller_method) { // Reset the started period $this->rest->db - ->where('uri', $this->uri->uri_string()) - ->where('api_key', isset($this->rest->key) ? $this->rest->key : '') - ->set('hour_started', time()) - ->set('count', 1) - ->update(config_item('rest_limits_table')); + ->where('uri', $this->uri->uri_string()) + ->where('api_key', isset($this->rest->key) ? $this->rest->key : '') + ->set('hour_started', time()) + ->set('count', 1) + ->update(config_item('rest_limits_table')); } // They have called within the hour, so lets update - else { + else + { // Your luck is out, you've called too many times! if ($result->count >= $limit) { @@ -969,10 +974,10 @@ protected function _check_limit($controller_method) } $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')); + ->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; @@ -980,7 +985,6 @@ protected function _check_limit($controller_method) /** * Auth override check - * * Check if there is a specific auth type set for the current class/method * being called. * @@ -994,12 +998,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 for wildcard flag for rules for classes - if(!empty($this->overrides_array[$this->router->class]['*'])) // Check for class overrides + if (!empty($this->overrides_array[$this->router->class]['*'])) // Check for class overrides { // None auth override found, prepare nothing but send back a TRUE override flag if ($this->overrides_array[$this->router->class]['*'] == 'none') @@ -1011,6 +1016,7 @@ protected function _auth_override_check() if ($this->overrides_array[$this->router->class]['*'] == 'basic') { $this->_prepare_basic_auth(); + return TRUE; } @@ -1018,6 +1024,7 @@ protected function _auth_override_check() if ($this->overrides_array[$this->router->class]['*'] == 'digest') { $this->_prepare_digest_auth(); + return TRUE; } @@ -1025,36 +1032,42 @@ protected function _auth_override_check() 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])) { + 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') { + 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') { + 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') { + 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') { + if ($this->overrides_array[$this->router->class][$this->router->method] == 'whitelist') + { $this->_check_whitelist_auth(); return TRUE; @@ -1073,10 +1086,11 @@ protected function _auth_override_check() protected function _parse_get() { // Fix for Issue #247 - if (is_cli()) { + if (is_cli()) + { $args = $_SERVER['argv']; unset($args[0]); - $_SERVER['QUERY_STRING'] = $_SERVER['PATH_INFO'] = $_SERVER['REQUEST_URI'] = '/' . implode('/', $args) . '/'; + $_SERVER['QUERY_STRING'] = $_SERVER['PATH_INFO'] = $_SERVER['REQUEST_URI'] = '/' . implode('/', $args) . '/'; } // Grab proper GET variables @@ -1106,13 +1120,16 @@ protected function _parse_post() protected function _parse_put() { // It might be a HTTP body - if ($this->request->format) { + if ($this->request->format) + { $this->request->body = file_get_contents('php://input'); } // If no file type is provided, this is probably just arguments - else { - if ($this->input->method() == 'put') { + else + { + if ($this->input->method() == 'put') + { $this->_put_args = $this->input->input_stream(); } } @@ -1155,13 +1172,16 @@ protected function _parse_options() protected function _parse_patch() { // It might be a HTTP body - if ($this->request->format) { + if ($this->request->format) + { $this->request->body = file_get_contents('php://input'); } // If no file type is provided, this is probably just arguments - else { - if ($this->input->method() == 'patch') { + else + { + if ($this->input->method() == 'patch') + { $this->_patch_args = $this->input->input_stream(); } } @@ -1175,7 +1195,8 @@ protected function _parse_patch() protected function _parse_delete() { // Set up out DELETE variables (which shouldn't really exist, but sssh!) - if ($this->input->method() == 'delete') { + if ($this->input->method() == 'delete') + { $this->_delete_args = $this->input->input_stream(); } } @@ -1186,13 +1207,16 @@ protected function _parse_delete() * Retrieve a value from the GET request arguments. * * @access public - * @param string $key The key for the GET request argument to retrieve + * + * @param string $key The key for the GET request argument to retrieve * @param boolean $xss_clean Whether to apply XSS filtering + * * @return string The GET argument value. */ public function get($key = NULL, $xss_clean = NULL) { - if ($key === NULL) { + if ($key === NULL) + { return $this->_get_args; } @@ -1203,13 +1227,16 @@ public function get($key = NULL, $xss_clean = NULL) * This function retrieves a values from the OPTIONS request arguments * * @access public - * @param string $key The OPTIONS/GET argument key + * + * @param string $key The OPTIONS/GET argument key * @param boolean $xss_clean Whether to apply XSS filtering + * * @return string The OPTIONS/GET argument value */ public function options($key = NULL, $xss_clean = NULL) { - if ($key === NULL) { + if ($key === NULL) + { return $this->_options_args; } @@ -1220,13 +1247,16 @@ public function options($key = NULL, $xss_clean = NULL) * This function retrieves a values from the HEAD request arguments * * @access public - * @param string $key The HEAD/GET argument key + * + * @param string $key The HEAD/GET argument key * @param boolean $xss_clean Whether to apply XSS filtering + * * @return string The HEAD/GET argument value */ public function head($key = NULL, $xss_clean = NULL) { - if ($key === NULL) { + if ($key === NULL) + { return $this->head_args; } @@ -1237,13 +1267,16 @@ public function head($key = NULL, $xss_clean = NULL) * Retrieve a value from the POST request arguments. * * @access public - * @param string $key The key for the POST request argument to retrieve + * + * @param string $key The key for the POST request argument to retrieve * @param boolean $xss_clean Whether to apply XSS filtering + * * @return string The POST argument value. */ public function post($key = NULL, $xss_clean = NULL) { - if ($key === NULL) { + if ($key === NULL) + { return $this->_post_args; } @@ -1254,13 +1287,16 @@ public function post($key = NULL, $xss_clean = NULL) * Retrieve a value from the PUT request arguments. * * @access public - * @param string $key The key for the PUT request argument to retrieve + * + * @param string $key The key for the PUT request argument to retrieve * @param boolean $xss_clean Whether to apply XSS filtering + * * @return string The PUT argument value. */ public function put($key = NULL, $xss_clean = NULL) { - if ($key === NULL) { + if ($key === NULL) + { return $this->_put_args; } @@ -1271,13 +1307,16 @@ public function put($key = NULL, $xss_clean = NULL) * Retrieve a value from the DELETE request arguments. * * @access public - * @param string $key The key for the DELETE request argument to retrieve + * + * @param string $key The key for the DELETE request argument to retrieve * @param boolean $xss_clean Whether to apply XSS filtering + * * @return string The DELETE argument value. */ public function delete($key = NULL, $xss_clean = NULL) { - if ($key === NULL) { + if ($key === NULL) + { return $this->_delete_args; } @@ -1288,13 +1327,16 @@ public function delete($key = NULL, $xss_clean = NULL) * Retrieve a value from the PATCH request arguments. * * @access public - * @param string $key The key for the PATCH request argument to retrieve + * + * @param string $key The key for the PATCH request argument to retrieve * @param boolean $xss_clean Whether to apply XSS filtering + * * @return string The PATCH argument value. */ public function patch($key = NULL, $xss_clean = NULL) { - if ($key === NULL) { + if ($key === NULL) + { return $this->_patch_args; } @@ -1306,13 +1348,16 @@ public function patch($key = NULL, $xss_clean = NULL) * prevented. * * @access protected - * @param string $value Input data + * + * @param string $value Input data * @param boolean $xss_clean Whether to apply XSS filtering + * * @return string */ protected function _xss_clean($value, $xss_clean) { is_bool($xss_clean) OR $xss_clean = $this->_enable_xss; + return $xss_clean === TRUE ? $this->security->xss_clean($value) : $value; } @@ -1335,13 +1380,16 @@ public function validation_errors() * Perform LDAP Authentication * * @access protected - * @param string $username The username to validate - * @param string $password The password to validate + * + * @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)) { + if (empty($username)) + { log_message('debug', 'LDAP Auth: failure, empty username'); return FALSE; @@ -1353,12 +1401,12 @@ protected function _perform_ldap_auth($username = '', $password = NULL) $ldap = [ 'timeout' => $this->config->item('timeout', 'ldap'), - 'host' => $this->config->item('server', 'ldap'), - 'port' => $this->config->item('port', 'ldap'), - 'rdn' => $this->config->item('binduser', 'ldap'), - 'pass' => $this->config->item('bindpw', 'ldap'), - 'basedn' => $this->config->item('basedn', 'ldap'), - ]; + 'host' => $this->config->item('server', 'ldap'), + 'port' => $this->config->item('port', 'ldap'), + 'rdn' => $this->config->item('binduser', 'ldap'), + 'pass' => $this->config->item('bindpw', 'ldap'), + 'basedn' => $this->config->item('basedn', 'ldap'), + ]; log_message('debug', 'LDAP Auth: Connect to ' . (isset($ldaphost) ? $ldaphost : '[ldap not configured]')); @@ -1367,7 +1415,8 @@ protected function _perform_ldap_auth($username = '', $password = NULL) // connect to ldap server $ldapconn = ldap_connect($ldap['host'], $ldap['port']); - if ($ldapconn) { + if ($ldapconn) + { log_message('debug', 'Setting timeout to ' . $ldap['timeout'] . ' seconds'); @@ -1379,9 +1428,12 @@ protected function _perform_ldap_auth($username = '', $password = NULL) $ldapbind = ldap_bind($ldapconn, $ldap['rdn'], $ldap['pass']); // verify binding - if ($ldapbind) { + if ($ldapbind) + { log_message('debug', 'LDAP Auth: bind successful'); - } else { + } + else + { log_message('error', 'LDAP Auth: bind unsuccessful'); return FALSE; @@ -1390,32 +1442,37 @@ protected function _perform_ldap_auth($username = '', $password = NULL) } // search for user - if (($res_id = ldap_search( $ldapconn, $ldap['basedn'], "uid=$username")) == FALSE) { + if (($res_id = ldap_search($ldapconn, $ldap['basedn'], "uid=$username")) == FALSE) + { log_message('error', 'LDAP Auth: User ' . $username . ' not found in search'); return FALSE; } - if (ldap_count_entries($ldapconn, $res_id) != 1) { + 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) { + 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) { + 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) { + 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; @@ -1433,31 +1490,40 @@ protected function _perform_ldap_auth($username = '', $password = NULL) * Perform Library Authentication - Override this function to change the way the library is called * * @access protected - * @param string $username The username to validate - * @param string $password The password to validate + * + * @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)) { + if (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)) { + if (empty($auth_library_class)) + { log_message('debug', 'Library Auth: failure, empty auth_library_class'); + return FALSE; } - if (empty($auth_library_function)) { + if (empty($auth_library_function)) + { log_message('debug', 'Library Auth: failure, empty auth_library_function'); + return FALSE; } - if (!is_callable([$auth_library_class, $auth_library_function])) { + if (!is_callable([$auth_library_class, $auth_library_function])) + { $this->load->library($auth_library_class); } @@ -1468,13 +1534,16 @@ protected function _perform_library_auth($username = '', $password = NULL) * Check if the user is logged in. * * @access protected - * @param string $username The user's name - * @param string $password The user's password + * + * @param string $username The user's name + * @param string $password The user's password + * * @return boolean */ protected function _check_login($username = '', $password = FALSE) { - if (empty($username)) { + if (empty($username)) + { return FALSE; } @@ -1482,31 +1551,37 @@ protected function _check_login($username = '', $password = FALSE) $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 (!$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) { + if ($password === FALSE) + { return FALSE; } - if ($auth_source == 'ldap') { + 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); + if ($auth_source == 'library') + { + log_message('debug', 'performing Library authentication for ' . $username); return $this->_perform_library_auth($username, $password); } - if (!array_key_exists($username, $valid_logins)) { + if (!array_key_exists($username, $valid_logins)) + { return FALSE; } - if ($valid_logins[$username] != $password) { + if ($valid_logins[$username] != $password) + { return FALSE; } @@ -1521,7 +1596,8 @@ protected function _check_login($username = '', $password = FALSE) protected function _check_php_session() { $key = $this->config->item('auth_source'); - if (!$this->session->userdata($key)) { + if (!$this->session->userdata($key)) + { $this->response([config_item('rest_status_field_name') => FALSE, config_item('rest_message_field_name') => 'Not Authorized'], 401); } } @@ -1566,30 +1642,36 @@ protected function _prepare_basic_auth() /** * @todo Document this. - * * @access protected */ 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')) { + 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')) { + if ($this->input->server('PHP_AUTH_DIGEST')) + { $digest_string = $this->input->server('PHP_AUTH_DIGEST'); - } elseif ($this->input->server('HTTP_AUTHORIZATION')) { + } + elseif ($this->input->server('HTTP_AUTHORIZATION')) + { $digest_string = $this->input->server('HTTP_AUTHORIZATION'); - } else { + } + 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)) { + if (empty($digest_string)) + { $this->_force_login($uniqid); } @@ -1600,14 +1682,16 @@ protected function _prepare_digest_auth() // 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'], TRUE); - if ( ! array_key_exists('username', $digest) || ! $A1 ) { + if (!array_key_exists('username', $digest) || !$A1) + { $this->_force_login($uniqid); } - $A2 = md5(strtoupper($this->request->method).':'.$digest['uri']); - $valid_response = md5($A1.':'.$digest['nonce'].':'.$digest['nc'].':'.$digest['cnonce'].':'.$digest['qop'].':'.$A2); + $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) { + if ($digest['response'] != $valid_response) + { $this->response([config_item('rest_status_field_name') => 0, config_item('rest_message_field_name') => 'Invalid credentials'], 401); exit; } @@ -1627,7 +1711,8 @@ protected function _check_blacklist_auth() if (preg_match($pattern, config_item('rest_ip_blacklist'))) { // Display an error response - $this->response([ + $this->response( + [ 'status' => FALSE, 'error' => 'IP Denied' ], @@ -1646,27 +1731,32 @@ protected function _check_whitelist_auth() array_push($whitelist, '127.0.0.1', '0.0.0.0'); - foreach ($whitelist AS &$ip) { + foreach ($whitelist AS &$ip) + { $ip = trim($ip); } - if ( ! in_array($this->input->ip_address(), $whitelist)) { + if (!in_array($this->input->ip_address(), $whitelist)) + { $this->response([config_item('rest_status_field_name') => FALSE, config_item('rest_message_field_name') => 'IP not authorized'], 401); } } /** * @todo Document this. - * * @access protected + * * @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')).'"'); + 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([config_item('rest_status_field_name') => FALSE, config_item('rest_message_field_name') => 'Not authorized'], 401); @@ -1676,13 +1766,16 @@ protected function _force_login($nonce = '') * Force it into an array * * @access protected + * * @param object|array $data + * * @return array */ protected function _force_loopable($data) { // Force it to be something useful - if ( ! is_array($data) && !is_object($data)) { + if (!is_array($data) && !is_object($data)) + { $data = (array) $data; } @@ -1714,6 +1807,7 @@ protected function _log_access_time() protected function _log_response_code($http_code) { $payload['response_code'] = $http_code; + return $this->rest->db->update(config_item('rest_logs_table'), $payload, ['id' => $this->_insert_id]); } @@ -1726,12 +1820,13 @@ protected function _log_response_code($http_code) protected function _check_access() { // if we don't want to check acccess, just return TRUE - if (config_item('rest_enable_access') === FALSE) { + if (config_item('rest_enable_access') === FALSE) + { return TRUE; } // Fetch controller based on path and controller name - $controller = implode( '/', [$this->router->directory, $this->router->class] ); + $controller = implode('/', [$this->router->directory, $this->router->class]); // Remove any double slashes for safety $controller = str_replace('//', '/', $controller); @@ -1743,7 +1838,8 @@ protected function _check_access() $query = $this->rest->db->get(config_item('rest_access_table')); - if ($query->num_rows() > 0) { + if ($query->num_rows() > 0) + { return TRUE; } From 2b7668968c2d753af5c845984c057b16ad5a0293 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Sun, 28 Jun 2015 11:44:20 +0300 Subject: [PATCH 030/444] Check the correct type and value It will always be lowercase --- application/libraries/REST_Controller.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 3d220044..df720c2f 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -1128,7 +1128,7 @@ protected function _parse_put() // If no file type is provided, this is probably just arguments else { - if ($this->input->method() == 'put') + if ($this->input->method() === 'put') { $this->_put_args = $this->input->input_stream(); } @@ -1180,7 +1180,7 @@ protected function _parse_patch() // If no file type is provided, this is probably just arguments else { - if ($this->input->method() == 'patch') + if ($this->input->method() === 'patch') { $this->_patch_args = $this->input->input_stream(); } @@ -1195,7 +1195,7 @@ protected function _parse_patch() protected function _parse_delete() { // Set up out DELETE variables (which shouldn't really exist, but sssh!) - if ($this->input->method() == 'delete') + if ($this->input->method() === 'delete') { $this->_delete_args = $this->input->input_stream(); } From e2351ab8ea980a087b5453b0fd468c2bc258d74a Mon Sep 17 00:00:00 2001 From: softwarespot Date: Sun, 28 Jun 2015 12:12:30 +0300 Subject: [PATCH 031/444] Removed redundant function for checking HTTPS --- application/libraries/REST_Controller.php | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index df720c2f..e2f517df 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -253,7 +253,7 @@ public function __construct($config = 'rest') } // Is it over SSL? - $this->request->ssl = $this->_detect_ssl(); + $this->request->ssl = is_https(); // How is this request being made? POST, DELETE, GET, PUT? $this->request->method = $this->_detect_method(); @@ -390,7 +390,7 @@ public function __destruct() public function _remap($object_called, $arguments) { // Should we answer if not over SSL? - if (config_item('force_https') && !$this->_detect_ssl()) + if (config_item('force_https') && $this->request->ssl === FALSE) { $this->response([config_item('rest_status_field_name') => FALSE, config_item('rest_message_field_name') => 'Unsupported protocol'], 403); } @@ -599,19 +599,6 @@ public function response($data = NULL, $http_code = NULL, $continue = FALSE) } } - /** - * Detect SSL use - * Detect whether SSL is being used or not. - * - * @access protected - */ - protected function _detect_ssl() - { - // $_SERVER['HTTPS'] (http://php.net/manual/en/reserved.variables.server.php) - // Set to a non-empty value if the script was queried through the HTTPS protocol - return (isset($_SERVER['HTTPS']) && !empty($_SERVER['HTTPS'])); - } - /** * Return server response * Method to send a response to the client in the event of a server error. From 84a883c1e76f14c7cb90856fd7d96d3531ff796c Mon Sep 17 00:00:00 2001 From: softwarespot Date: Sun, 28 Jun 2015 14:19:13 +0300 Subject: [PATCH 032/444] Added a comment for _force_login() Also updated code readability --- application/libraries/REST_Controller.php | 26 ++++++++++++++++------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index e2f517df..b020c081 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -1730,23 +1730,33 @@ protected function _check_whitelist_auth() } /** - * @todo Document this. - * @access protected + * Force logging in by setting the WWW-Authenticate header * - * @param string $nonce + * @param string $nonce A server-specified data string which should be uniquely generated + * each time */ protected function _force_login($nonce = '') { - if (strtolower($this->config->item('rest_auth')) == 'basic') + $restAuth = $this->config->item('rest_auth'); + $restRealm = $this->config->item('rest_realm'); + if (strtolower($restAuth) === 'basic') { - header('WWW-Authenticate: Basic realm="' . $this->config->item('rest_realm') . '"'); + // See http://tools.ietf.org/html/rfc2617#page-5 + header('WWW-Authenticate: Basic realm="' . $restRealm . '"'); } - elseif (strtolower($this->config->item('rest_auth')) == 'digest') + elseif (strtolower($restAuth) === 'digest') { - header('WWW-Authenticate: Digest realm="' . $this->config->item('rest_realm') . '", qop="auth", nonce="' . $nonce . '", opaque="' . md5($this->config->item('rest_realm')) . '"'); + // See http://tools.ietf.org/html/rfc2617#page-18 + header('WWW-Authenticate: Digest realm="' . $restRealm + . '", qop="auth", nonce="' . $nonce + . '", opaque="' . md5($restRealm) . '"'); } - $this->response([config_item('rest_status_field_name') => FALSE, config_item('rest_message_field_name') => 'Not authorized'], 401); + // Display an error response + $this->response([ + config_item('rest_status_field_name') => FALSE, + config_item('rest_message_field_name') => 'Not authorized' + ], 401); } /** From fdc262a4968fff8f6a47a13ff2ba51191955171f Mon Sep 17 00:00:00 2001 From: softwarespot Date: Sun, 28 Jun 2015 14:28:27 +0300 Subject: [PATCH 033/444] Removed exit as this is handled by response() --- 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 b020c081..7411432c 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -1680,7 +1680,6 @@ protected function _prepare_digest_auth() if ($digest['response'] != $valid_response) { $this->response([config_item('rest_status_field_name') => 0, config_item('rest_message_field_name') => 'Invalid credentials'], 401); - exit; } } From f25a804b87e3092945fd72acb5f7e0fda67495e9 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Sun, 28 Jun 2015 14:32:23 +0300 Subject: [PATCH 034/444] Added comment --- 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 7411432c..2db57ee1 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -1628,7 +1628,8 @@ protected function _prepare_basic_auth() } /** - * @todo Document this. + * Prepares for digest authentication + * * @access protected */ protected function _prepare_digest_auth() From fe29d01e7041d756988b0913fff0f02b9106cded Mon Sep 17 00:00:00 2001 From: softwarespot Date: Sun, 28 Jun 2015 14:39:18 +0300 Subject: [PATCH 035/444] Tidied _prepare_digest_auth() --- application/libraries/REST_Controller.php | 30 +++++++++++++---------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 2db57ee1..35151c65 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -1640,9 +1640,9 @@ protected function _prepare_digest_auth() $this->_check_whitelist_auth(); } - $uniqid = uniqid(""); // Empty argument for backward compatibility - // We need to test which server authentication variable to use + // We need to test which server authentication variable to use, // because the PHP ISAPI module in IIS acts different from CGI + $digest_string = ''; if ($this->input->server('PHP_AUTH_DIGEST')) { $digest_string = $this->input->server('PHP_AUTH_DIGEST'); @@ -1651,36 +1651,40 @@ protected function _prepare_digest_auth() { $digest_string = $this->input->server('HTTP_AUTHORIZATION'); } - else - { - $digest_string = ""; - } + + $uniqueId = uniqid(); // The $_SESSION['error_prompted'] variable is used to ask the password - // again if none given or if the user enters wrong auth information. + // again if none given or if the user enters wrong auth information if (empty($digest_string)) { - $this->_force_login($uniqid); + $this->_force_login($uniqueId); } - // We need to retrieve authentication informations from the $auth_data variable + // We need to retrieve authentication data from the $digest_string variable $matches = []; preg_match_all('@(username|nonce|uri|nc|cnonce|qop|response)=[\'"]?([^\'",]+)@', $digest_string, $matches); $digest = (empty($matches[1]) || empty($matches[2])) ? [] : 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'], TRUE); - if (!array_key_exists('username', $digest) || !$A1) + if (array_key_exists('username', $digest) === FALSE || $A1 === FALSE) { - $this->_force_login($uniqid); + $this->_force_login($uniqueId); } $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) + // Check if the string don't compare (case-insensitive) + if (strcasecmp($digest['response'], $valid_response) !== 0) { - $this->response([config_item('rest_status_field_name') => 0, config_item('rest_message_field_name') => 'Invalid credentials'], 401); + // Display an error response + $this->response( + [ + config_item('rest_status_field_name') => 0, + config_item('rest_message_field_name') => 'Invalid credentials' + ], 401); } } From 86b06c24ec509b8296585e45c5f3e3a21dc2c28e Mon Sep 17 00:00:00 2001 From: softwarespot Date: Sun, 28 Jun 2015 14:43:32 +0300 Subject: [PATCH 036/444] Removed superfluous functions --- application/libraries/REST_Controller.php | 48 +++++------------------ 1 file changed, 10 insertions(+), 38 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 35151c65..8622716a 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -471,31 +471,22 @@ public function _remap($object_called, $arguments) $this->_log_request($authorized = TRUE); } - // and...... GO! + // Call the controller method and passed arguments try { - $this->_fire_method([$this, $controller_method], $arguments); + call_user_func_array([$this, $controller_method], $arguments); } catch (Exception $ex) { - $this->_server_error_response($ex); + // If the method doesn't exist, then the error will be caught and an error response shown + $this->response([ + config_item('rest_status_field_name') => FALSE, + config_item('rest_message_field_name') => [ + 'classname' => get_class($ex), + 'message' => $ex->getMessage() + ] + ], 500); } - - // should not get here. - } - - /** - * Fire Method - * Fires the designated controller method with the given arguments. - * - * @access protected - * - * @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); } /** @@ -599,25 +590,6 @@ public function response($data = NULL, $http_code = NULL, $continue = FALSE) } } - /** - * Return server response - * Method to send a response to the client in the event of a server error. - * - * @access public - * - * @param object $ex - */ - protected function _server_error_response($ex) - { - $response = [ - config_item('rest_status_field_name') => FALSE, - config_item('rest_message_field_name') => [ - 'classname' => get_class($ex), 'message' => $ex->getMessage() - ] - ]; - $this->response($response, 500); - } - /** * Detect input format * Detect which format the HTTP Body is provided in From 5ca3bf1910544f6ae1b18ca514b4ac242f29ca32 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Sun, 28 Jun 2015 14:47:37 +0300 Subject: [PATCH 037/444] Removed unused function --- application/libraries/REST_Controller.php | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 8622716a..a8b22773 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -1735,26 +1735,6 @@ protected function _force_login($nonce = '') ], 401); } - /** - * Force it into an array - * - * @access protected - * - * @param object|array $data - * - * @return array - */ - protected function _force_loopable($data) - { - // Force it to be something useful - if (!is_array($data) && !is_object($data)) - { - $data = (array) $data; - } - - return $data; - } - /** * updates the log with the access time * From 12f0e3dc57142eb6a1a1ac974b1d5b577bb307c7 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Sun, 28 Jun 2015 14:48:07 +0300 Subject: [PATCH 038/444] Tidied array formatting --- 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 a8b22773..a86e5a60 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -479,7 +479,8 @@ public function _remap($object_called, $arguments) catch (Exception $ex) { // If the method doesn't exist, then the error will be caught and an error response shown - $this->response([ + $this->response( + [ config_item('rest_status_field_name') => FALSE, config_item('rest_message_field_name') => [ 'classname' => get_class($ex), From 23d0439759e339a479b21d269ee769961c366252 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Sun, 28 Jun 2015 15:54:54 +0300 Subject: [PATCH 039/444] Removed recreation of response --- 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 a86e5a60..5f676ff1 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -296,7 +296,6 @@ public function __construct($config = 'rest') ); // 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? From b12ee28fcff9f344bf4ed80b825f5b1fc0e1b039 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Sun, 28 Jun 2015 15:58:17 +0300 Subject: [PATCH 040/444] Fixed use of double quotes, when single should be used --- 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 5f676ff1..65317212 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -311,7 +311,7 @@ public function __construct($config = 'rest') } // Use whatever database is in use (isset returns FALSE) - elseif (property_exists($this, "db")) + elseif (property_exists($this, 'db')) { $this->rest->db = $this->db; } @@ -778,7 +778,7 @@ 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); + $list_ip_addresses = explode(',', $row->ip_addresses); $found_address = FALSE; foreach ($list_ip_addresses as $ip_address) From 0776f196a056f1097be6ee6e33d26a2ad7a164a8 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Sun, 28 Jun 2015 16:07:44 +0300 Subject: [PATCH 041/444] Explicitly check for TRUE --- 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 65317212..40e9dfdb 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -857,7 +857,7 @@ protected function _log_request($authorized = FALSE) config_item('rest_logs_table'), [ '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 ? (config_item('rest_logs_json_params') === TRUE ? 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 b052a7f4868325d99fbb591fa12208a59d5e9e36 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Sun, 28 Jun 2015 16:22:40 +0300 Subject: [PATCH 042/444] Removed deprecated _format_*() functions This is not even documented, so it's safe to remove as well --- application/libraries/REST_Controller.php | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 40e9dfdb..d1e57757 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -534,18 +534,8 @@ public function response($data = NULL, $http_code = NULL, $continue = FALSE) is_numeric($http_code) || $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] . '; charset=' . strtolower($this->config->item('charset'))); - - $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)) + if (method_exists($this->format, 'to_' . $this->response->format)) { // Set the correct format header header('Content-Type: ' . $this->_supported_formats[$this->response->format] . '; charset=' . strtolower($this->config->item('charset'))); From 6e80232b6c3b82de70021bb7504b1ac7093741d4 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Sun, 28 Jun 2015 16:51:29 +0300 Subject: [PATCH 043/444] Re-worked response() function --- application/libraries/REST_Controller.php | 72 ++++++++++++----------- 1 file changed, 37 insertions(+), 35 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index d1e57757..5e8b5b05 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -490,70 +490,74 @@ public function _remap($object_called, $arguments) } /** - * 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. + * Takes mixed data and optionally a status code, then creates the response * * @access public * - * @param array $data - * @param NULL|int $http_code - * @param bool $continue + * @param array|NULL $data Data to output to the user + * @param int|NULL $http_code HTTP status code + * @param bool $continue TRUE to flush the response to the client and continue + * running the script; otherwise, exit */ public function response($data = NULL, $http_code = NULL, $continue = FALSE) { - // If data is NULL and not code provide, error and bail - if ($data === NULL && $http_code === NULL) + // If the HTTP status is not NULL, then cast as an integer + if ($http_code !== NULL) { - $http_code = 404; - - // create the output variable here in the case of $this->response(array()); - $output = NULL; + // So as to be safe later on in the process + $http_code = (int) $http_code; } - // If data is NULL but http code provided, keep the output empty - elseif ($data === NULL && is_numeric($http_code)) + // Set the output as NULL by default + $output = NULL; + + // If data is NULL and no HTTP status code provided, then display, error and exit + if ($data === NULL && $http_code === NULL) { - $output = NULL; + $http_code = 404; } - // Otherwise (if no data but 200 provided) or some data, carry on camping! - else + // If data is not NULL and a HTTP status code provided, then continue + elseif ($data !== NULL) { - // Is compression requested? + // Is compression enabled and available? if ($this->config->item('compress_output') === TRUE && $this->_zlib_oc == FALSE) { if (extension_loaded('zlib')) { - if (isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE) + $http_encoding = $this->input->server('HTTP_ACCEPT_ENCODING'); + if ($http_encoding !== NULL && strpos($http_encoding, 'gzip') !== FALSE) { ob_start('ob_gzhandler'); } } } - is_numeric($http_code) || $http_code = 200; - // If the format method exists, call and return the output in that format if (method_exists($this->format, 'to_' . $this->response->format)) { - // Set the correct format header - header('Content-Type: ' . $this->_supported_formats[$this->response->format] . '; charset=' . strtolower($this->config->item('charset'))); + // Set the format header + header('Content-Type: ' . $this->_supported_formats[$this->response->format] + . '; charset=' . strtolower($this->config->item('charset'))); $output = $this->format->factory($data)->{'to_' . $this->response->format}(); } - - // Format not supported, output directly else { + // Format is not supported, so output the raw data $output = $data; } } + // If not greater than zero, then set the HTTP status code as 200 by default + // Though perhaps 500 should be set instead, for the developer not passing a + // correct HTTP status code + $http_code > 0 || $http_code = 200; + set_status_header($http_code); // JC: Log response code only if rest logging enabled - if (config_item('rest_enable_logging')) + if (config_item('rest_enable_logging') === TRUE) { $this->_log_response_code($http_code); } @@ -561,23 +565,21 @@ public function response($data = NULL, $http_code = NULL, $continue = FALSE) // 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. + // We'll just skip content-length in those cases if (!$this->_zlib_oc && !$this->config->item('compress_output')) { header('Content-Length: ' . strlen($output)); } - if ($continue) - { - echo($output); - ob_end_flush(); - ob_flush(); - flush(); - } - else + if ($continue === FALSE) { exit($output); } + + echo($output); + ob_end_flush(); + ob_flush(); + flush(); } /** From 382ad4bffec6952289eeddbe595cbffb1650943c Mon Sep 17 00:00:00 2001 From: softwarespot Date: Sun, 28 Jun 2015 17:03:51 +0300 Subject: [PATCH 044/444] Re-wrote comments for better clarity --- application/libraries/REST_Controller.php | 56 +++++++++++------------ 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 5e8b5b05..8e42d8b3 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -1155,14 +1155,14 @@ protected function _parse_delete() // INPUT FUNCTION -------------------------------------------------------------- /** - * Retrieve a value from the GET request arguments. + * Retrieve a value from a GET request * * @access public * - * @param string $key The key for the GET request argument to retrieve - * @param boolean $xss_clean Whether to apply XSS filtering + * @param NULL $key Key to retrieve from the GET request + * @param NULL $xss_clean Whether to apply XSS filtering * - * @return string The GET argument value. + * @return string|NULL Value from the GET request; otherwise, FALSE */ public function get($key = NULL, $xss_clean = NULL) { @@ -1175,14 +1175,14 @@ public function get($key = NULL, $xss_clean = NULL) } /** - * This function retrieves a values from the OPTIONS request arguments + * Retrieve a value from a OPTIONS request * * @access public * - * @param string $key The OPTIONS/GET argument key - * @param boolean $xss_clean Whether to apply XSS filtering + * @param NULL $key Key to retrieve from the OPTIONS request + * @param NULL $xss_clean Whether to apply XSS filtering * - * @return string The OPTIONS/GET argument value + * @return string|NULL Value from the OPTIONS request; otherwise, FALSE */ public function options($key = NULL, $xss_clean = NULL) { @@ -1195,14 +1195,14 @@ public function options($key = NULL, $xss_clean = NULL) } /** - * This function retrieves a values from the HEAD request arguments + * Retrieve a value from a HEAD request * * @access public * - * @param string $key The HEAD/GET argument key - * @param boolean $xss_clean Whether to apply XSS filtering + * @param NULL $key Key to retrieve from the HEAD request + * @param NULL $xss_clean Whether to apply XSS filtering * - * @return string The HEAD/GET argument value + * @return string|NULL Value from the HEAD request; otherwise, FALSE */ public function head($key = NULL, $xss_clean = NULL) { @@ -1215,14 +1215,14 @@ public function head($key = NULL, $xss_clean = NULL) } /** - * Retrieve a value from the POST request arguments. + * Retrieve a value from a POST request * * @access public * - * @param string $key The key for the POST request argument to retrieve - * @param boolean $xss_clean Whether to apply XSS filtering + * @param NULL $key Key to retrieve from the POST request + * @param NULL $xss_clean Whether to apply XSS filtering * - * @return string The POST argument value. + * @return string|NULL Value from the POST request; otherwise, FALSE */ public function post($key = NULL, $xss_clean = NULL) { @@ -1235,14 +1235,14 @@ public function post($key = NULL, $xss_clean = NULL) } /** - * Retrieve a value from the PUT request arguments. + * Retrieve a value from a PUT request * * @access public * - * @param string $key The key for the PUT request argument to retrieve - * @param boolean $xss_clean Whether to apply XSS filtering + * @param NULL $key Key to retrieve from the PUT request + * @param NULL $xss_clean Whether to apply XSS filtering * - * @return string The PUT argument value. + * @return string|NULL Value from the PUT request; otherwise, FALSE */ public function put($key = NULL, $xss_clean = NULL) { @@ -1255,14 +1255,14 @@ public function put($key = NULL, $xss_clean = NULL) } /** - * Retrieve a value from the DELETE request arguments. + * Retrieve a value from a DELETE request * * @access public * - * @param string $key The key for the DELETE request argument to retrieve - * @param boolean $xss_clean Whether to apply XSS filtering + * @param NULL $key Key to retrieve from the DELETE request + * @param NULL $xss_clean Whether to apply XSS filtering * - * @return string The DELETE argument value. + * @return string|NULL Value from the DELETE request; otherwise, FALSE */ public function delete($key = NULL, $xss_clean = NULL) { @@ -1275,14 +1275,14 @@ public function delete($key = NULL, $xss_clean = NULL) } /** - * Retrieve a value from the PATCH request arguments. + * Retrieve a value from a PATCH request * * @access public * - * @param string $key The key for the PATCH request argument to retrieve - * @param boolean $xss_clean Whether to apply XSS filtering + * @param NULL $key Key to retrieve from the PATCH request + * @param NULL $xss_clean Whether to apply XSS filtering * - * @return string The PATCH argument value. + * @return string|NULL Value from the PATCH request; otherwise, FALSE */ public function patch($key = NULL, $xss_clean = NULL) { From 898381fa192bfa4451eb9ebb21fcc57620d0b3f5 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Sun, 28 Jun 2015 17:22:47 +0300 Subject: [PATCH 045/444] Normalized doc comments Improved consistency with the documentation comments for functions --- application/libraries/REST_Controller.php | 54 ++++++++++++++--------- 1 file changed, 32 insertions(+), 22 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 8e42d8b3..fe1d4d35 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -125,14 +125,14 @@ abstract class REST_Controller extends CI_Controller { /** * If the request is allowed based on the API key provided. * - * @var boolean + * @var bool */ protected $_allow = TRUE; /** * Determines if output compression is enabled * - * @var boolean + * @var bool */ protected $_zlib_oc = FALSE; @@ -202,6 +202,8 @@ protected function early_checks() /** * Constructor for the REST API * + * @access public + * * @param string $config Configuration filename minus the file extension * e.g: my_rest.php is passed as 'my_rest' */ @@ -376,7 +378,6 @@ public function __destruct() } /** - * 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. @@ -583,7 +584,6 @@ public function response($data = NULL, $http_code = NULL, $continue = FALSE) } /** - * Detect input format * Detect which format the HTTP Body is provided in * * @access protected @@ -611,10 +611,10 @@ protected function _detect_input_format() } /** - * Detect format - * Detect which format should be used to output the data. + * Detect which format should be used to output the data * * @access protected + * * @return string The output format. */ protected function _detect_output_format() @@ -694,10 +694,10 @@ protected function _detect_output_format() } /** - * Detect method * Detect which HTTP method is being used * * @access protected + * * @return string */ protected function _detect_method() @@ -725,11 +725,11 @@ protected function _detect_method() } /** - * Detect API Key * See if the user has provided an API key * * @access protected - * @return boolean + * + * @return bool */ protected function _detect_api_key() { @@ -800,10 +800,10 @@ protected function _detect_api_key() } /** - * Detect language(s) * What language do they want it in? * * @access protected + * * @return NULL|string The language code. */ protected function _detect_lang() @@ -839,7 +839,7 @@ protected function _detect_lang() * * @access protected * - * @param boolean $authorized + * @param bool $authorized * * @return object */ @@ -869,7 +869,7 @@ protected function _log_request($authorized = FALSE) * * @param string $controller_method The method being called. * - * @return boolean + * @return bool */ protected function _check_limit($controller_method) { @@ -940,7 +940,8 @@ protected function _check_limit($controller_method) * being called. * * @access protected - * @return boolean + * + * @return bool */ protected function _auth_override_check() { @@ -1301,7 +1302,7 @@ public function patch($key = NULL, $xss_clean = NULL) * @access protected * * @param string $value Input data - * @param boolean $xss_clean Whether to apply XSS filtering + * @param bool $xss_clean Whether to apply XSS filtering * * @return string */ @@ -1313,9 +1314,10 @@ protected function _xss_clean($value, $xss_clean) } /** - * Retrieve the validation errors. + * Retrieve the validation errors * * @access public + * * @return array */ public function validation_errors() @@ -1335,7 +1337,7 @@ public function validation_errors() * @param string $username The username to validate * @param string $password The password to validate * - * @return boolean + * @return bool */ protected function _perform_ldap_auth($username = '', $password = NULL) { @@ -1445,7 +1447,7 @@ protected function _perform_ldap_auth($username = '', $password = NULL) * @param string $username The username to validate * @param string $password The password to validate * - * @return boolean + * @return bool */ protected function _perform_library_auth($username = '', $password = NULL) { @@ -1482,14 +1484,14 @@ protected function _perform_library_auth($username = '', $password = NULL) } /** - * Check if the user is logged in. + * Check if the user is logged in * * @access protected * * @param string $username The user's name * @param string $password The user's password * - * @return boolean + * @return bool */ protected function _check_login($username = '', $password = FALSE) { @@ -1555,6 +1557,8 @@ protected function _check_php_session() /** * Prepares for basic authentication + * + * @access protected */ protected function _prepare_basic_auth() { @@ -1700,6 +1704,8 @@ protected function _check_whitelist_auth() /** * Force logging in by setting the WWW-Authenticate header * + * @access protected + * * @param string $nonce A server-specified data string which should be uniquely generated * each time */ @@ -1732,7 +1738,8 @@ protected function _force_login($nonce = '') * * @access protected * @author Chris Kacerguis - * @return boolean + * + * @return bool */ protected function _log_access_time() @@ -1745,8 +1752,10 @@ protected function _log_access_time() /** * updates the log with response code result * + * @access protected * @author Justin Chen - * @return boolean + * + * @return bool */ protected function _log_response_code($http_code) @@ -1760,7 +1769,8 @@ protected function _log_response_code($http_code) * Check to see if the API key has access to the controller and methods * * @access protected - * @return boolean + * + * @return bool */ protected function _check_access() { From 674efb28b8346121cf015821edd509b9f15d58ff Mon Sep 17 00:00:00 2001 From: softwarespot Date: Sun, 28 Jun 2015 17:24:47 +0300 Subject: [PATCH 046/444] Removed whitespace that was not needed --- 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 fe1d4d35..2ecf670e 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -945,7 +945,6 @@ protected function _check_limit($controller_method) */ 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'); From a924bb65ff5bf9b2e8884cc3dd93405e640a40ac Mon Sep 17 00:00:00 2001 From: softwarespot Date: Sun, 28 Jun 2015 17:33:13 +0300 Subject: [PATCH 047/444] Fixed typo (access), doc comments and whitespace --- application/libraries/REST_Controller.php | 45 ++++++++++------------- 1 file changed, 19 insertions(+), 26 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 2ecf670e..38d55bac 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -480,13 +480,13 @@ public function _remap($object_called, $arguments) { // If the method doesn't exist, then the error will be caught and an error response shown $this->response( - [ - config_item('rest_status_field_name') => FALSE, - config_item('rest_message_field_name') => [ - 'classname' => get_class($ex), - 'message' => $ex->getMessage() - ] - ], 500); + [ + config_item('rest_status_field_name') => FALSE, + config_item('rest_message_field_name') => [ + 'classname' => get_class($ex), + 'message' => $ex->getMessage() + ] + ], 500); } } @@ -538,8 +538,9 @@ public function response($data = NULL, $http_code = NULL, $continue = FALSE) if (method_exists($this->format, 'to_' . $this->response->format)) { // Set the format header - header('Content-Type: ' . $this->_supported_formats[$this->response->format] - . '; charset=' . strtolower($this->config->item('charset'))); + header( + 'Content-Type: ' . $this->_supported_formats[$this->response->format] + . '; charset=' . strtolower($this->config->item('charset'))); $output = $this->format->factory($data)->{'to_' . $this->response->format}(); } @@ -614,7 +615,6 @@ protected function _detect_input_format() * Detect which format should be used to output the data * * @access protected - * * @return string The output format. */ protected function _detect_output_format() @@ -697,7 +697,6 @@ protected function _detect_output_format() * Detect which HTTP method is being used * * @access protected - * * @return string */ protected function _detect_method() @@ -728,7 +727,6 @@ protected function _detect_method() * See if the user has provided an API key * * @access protected - * * @return bool */ protected function _detect_api_key() @@ -803,7 +801,6 @@ protected function _detect_api_key() * What language do they want it in? * * @access protected - * * @return NULL|string The language code. */ protected function _detect_lang() @@ -940,7 +937,6 @@ protected function _check_limit($controller_method) * being called. * * @access protected - * * @return bool */ protected function _auth_override_check() @@ -1316,7 +1312,6 @@ protected function _xss_clean($value, $xss_clean) * Retrieve the validation errors * * @access public - * * @return array */ public function validation_errors() @@ -1488,7 +1483,7 @@ protected function _perform_library_auth($username = '', $password = NULL) * @access protected * * @param string $username The user's name - * @param string $password The user's password + * @param bool|string $password The user's password * * @return bool */ @@ -1648,7 +1643,7 @@ protected function _prepare_digest_auth() { // Display an error response $this->response( - [ + [ config_item('rest_status_field_name') => 0, config_item('rest_message_field_name') => 'Invalid credentials' ], 401); @@ -1720,13 +1715,15 @@ protected function _force_login($nonce = '') elseif (strtolower($restAuth) === 'digest') { // See http://tools.ietf.org/html/rfc2617#page-18 - header('WWW-Authenticate: Digest realm="' . $restRealm - . '", qop="auth", nonce="' . $nonce - . '", opaque="' . md5($restRealm) . '"'); + header( + 'WWW-Authenticate: Digest realm="' . $restRealm + . '", qop="auth", nonce="' . $nonce + . '", opaque="' . md5($restRealm) . '"'); } // Display an error response - $this->response([ + $this->response( + [ config_item('rest_status_field_name') => FALSE, config_item('rest_message_field_name') => 'Not authorized' ], 401); @@ -1737,7 +1734,6 @@ protected function _force_login($nonce = '') * * @access protected * @author Chris Kacerguis - * * @return bool */ @@ -1753,10 +1749,8 @@ protected function _log_access_time() * * @access protected * @author Justin Chen - * * @return bool */ - protected function _log_response_code($http_code) { $payload['response_code'] = $http_code; @@ -1768,12 +1762,11 @@ protected function _log_response_code($http_code) * Check to see if the API key has access to the controller and methods * * @access protected - * * @return bool */ protected function _check_access() { - // if we don't want to check acccess, just return TRUE + // if we don't want to check access, just return TRUE if (config_item('rest_enable_access') === FALSE) { return TRUE; From 5e9bcb2a88fdfe702b81a34fd052dfeda7413afd Mon Sep 17 00:00:00 2001 From: softwarespot Date: Sun, 28 Jun 2015 17:40:14 +0300 Subject: [PATCH 048/444] Re-worked the __constructor function Improvements to comments and explicit checking --- application/libraries/REST_Controller.php | 42 ++++++++++++----------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 38d55bac..ae681438 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -211,7 +211,7 @@ public function __construct($config = 'rest') { parent::__construct(); - // disable XML Entity (security vulnerability) + // Disable XML Entity (security vulnerability) libxml_disable_entity_loader(TRUE); // Check to see if PHP is equal to or greater than 5.4.x @@ -233,46 +233,44 @@ public function __construct($config = 'rest') // Start the timer for how long the request takes $this->_start_rtime = microtime(TRUE); - // Lets grab the config and get ready to party + // Load the rest.php configuration file $this->load->config($config); - // This library is bundled with REST_Controller 2.5+, but will eventually be part of CodeIgniter itself + // At present the library is bundled with REST_Controller 2.5+, but will eventually be part of CodeIgniter (no citation) $this->load->library('format'); - // init objects + // Initialise the response, request and rest 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(); - - // Check to see if this IP is Blacklisted - if ($this->config->item('rest_ip_blacklist_enabled')) + // Check to see if the current IP address is blacklisted + if ($this->config->item('rest_ip_blacklist_enabled') === TRUE) { $this->_check_blacklist_auth(); } - // Is it over SSL? + // Store whether the the connection is HTTPS $this->request->ssl = is_https(); - // How is this request being made? POST, DELETE, GET, PUT? + // How is this request being made? GET, POST, PATCH, DELETE, INSERT, PUT, HEAD or OPTIONS $this->request->method = $this->_detect_method(); - // Create argument container, if nonexistent + // Create an argument container if it doesn't exist e.g. _get_args if (!isset($this->{'_' . $this->request->method . '_args'})) { $this->{'_' . $this->request->method . '_args'} = []; } - // Set up our GET variables + // Set up the GET variables $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(); - // Some Methods cant have a body + // Some method can't have a body $this->request->body = NULL; $this->{'_parse_' . $this->request->method}(); @@ -329,15 +327,19 @@ public function __construct($config = 'rest') $this->_allow = $this->_detect_api_key(); } - // only allow ajax requests - if (!$this->input->is_ajax_request() && config_item('rest_ajax_only')) + // Only allow ajax requests + if ($this->input->is_ajax_request() === FALSE && config_item('rest_ajax_only')) { - $response = [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 + // Display an error response + $this->response( + [ + config_item('rest_status_field_name') => FALSE, + config_item('rest_message_field_name') => 'Only AJAX requests are acceptable' + ], 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 - if ($this->auth_override !== TRUE && !(config_item('rest_enable_keys') && $this->_allow === TRUE)) + if ($this->auth_override === FALSE && !(config_item('rest_enable_keys') && $this->_allow === TRUE)) { $rest_auth = strtolower($this->config->item('rest_auth')); switch ($rest_auth) @@ -352,7 +354,7 @@ public function __construct($config = 'rest') $this->_check_php_session(); break; } - if ($this->config->item('rest_ip_whitelist_enabled')) + if ($this->config->item('rest_ip_whitelist_enabled') === TRUE) { $this->_check_whitelist_auth(); } From d50ff1eb2dc9bb1117d4d61e6de1ad57b20374af Mon Sep 17 00:00:00 2001 From: softwarespot Date: Sun, 28 Jun 2015 17:44:22 +0300 Subject: [PATCH 049/444] Updated the deconstructor function --- 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 ae681438..aaf9f3c4 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -362,21 +362,21 @@ public function __construct($config = 'rest') } /** - * Destructor function + * Deconstructor * * @author Chris Kacerguis * @access public */ public function __destruct() { - // Record the "stop" time of the request + // Get the current timestamp $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')) + + // Log the loading time to the log table + if (config_item('rest_enable_logging') === TRUE) { $this->_log_access_time(); } - } /** From e25f31266954fe58068fefe7d9dff7589ace8759 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Sun, 28 Jun 2015 17:47:08 +0300 Subject: [PATCH 050/444] Updated _detect_method() comments and body --- application/libraries/REST_Controller.php | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index aaf9f3c4..c10ad02c 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -696,14 +696,15 @@ protected function _detect_output_format() } /** - * Detect which HTTP method is being used + * Get the HTTP request string * * @access protected - * @return string + * @return string Request method as a lowercase string */ protected function _detect_method() { - $method = strtolower($this->input->server('REQUEST_METHOD')); + // Get the request method as a lowercase string + $method = $this->input->method(); if ($this->config->item('enable_emulate_request')) { @@ -717,12 +718,7 @@ protected function _detect_method() } } - if (in_array($method, $this->allowed_http_methods) && method_exists($this, '_parse_' . $method)) - { - return $method; - } - - return 'get'; + return in_array($method, $this->allowed_http_methods) && method_exists($this, '_parse_' . $method) ? $method : 'get'; } /** From 401ae4631f17b43e3be8fb1960be274c4d934787 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Sun, 28 Jun 2015 17:53:17 +0300 Subject: [PATCH 051/444] Updated to HTML5 index file This was introduced in CI3 --- application/libraries/index.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/application/libraries/index.html b/application/libraries/index.html index c942a79c..b702fbc3 100755 --- a/application/libraries/index.html +++ b/application/libraries/index.html @@ -1,3 +1,4 @@ + 403 Forbidden @@ -7,4 +8,4 @@

Directory access is forbidden.

- \ No newline at end of file + From 1feb37d265e334ae0743bcfd1aa1e1f8485f17e0 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Sun, 28 Jun 2015 20:53:16 +0300 Subject: [PATCH 052/444] Fixed missing quote and whitespace --- application/config/rest.php | 66 ++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/application/config/rest.php b/application/config/rest.php index 104d84b1..08df0cef 100644 --- a/application/config/rest.php +++ b/application/config/rest.php @@ -25,7 +25,7 @@ | called 'callback' is passed, then jsonp will be returned | 'html' HTML using the table library in CodeIgniter | 'php': Uses var_export() -| 'serialized: Uses serialize() +| 'serialized': Uses serialize() | 'xml': Uses simplexml_load_string() | */ @@ -135,10 +135,10 @@ | | e.g: | -| $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'; +| $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', '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'. @@ -172,10 +172,10 @@ | | 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) +| 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 but set 'auth_override_class_method' to 'whitelist' to -| restrict certain methods to IPs in your whitelist +| restrict certain methods to IPs in your whitelist | */ $config['rest_ip_whitelist_enabled'] = FALSE; @@ -253,14 +253,14 @@ | Default table schema: | CREATE TABLE `keys` ( | `id` INT(11) NOT NULL AUTO_INCREMENT, -| `key` VARCHAR(40) NOT NULL, -| `level` INT(2) NOT NULL, -| `ignore_limits` TINY(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; +| `key` VARCHAR(40) NOT NULL, +| `level` INT(2) NOT NULL, +| `ignore_limits` TINY(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; | */ $config['rest_enable_keys'] = FALSE; @@ -314,17 +314,17 @@ | Default table schema: | CREATE TABLE `logs` ( | `id` INT(11) NOT NULL AUTO_INCREMENT, -| `uri` VARCHAR(255) NOT NULL, -| `method` VARCHAR(6) NOT NULL, -| `params` TEXT DEFAULT NULL, -| `api_key` VARCHAR(40) NOT NULL, -| `ip_address` VARCHAR(45) NOT NULL, -| `time` INT(11) NOT NULL, -| `rtime` FLOAT DEFAULT NULL, -| `authorized` VARCHAR(1) NOT NULL, -| `response_code` SMALLINT(3) NOT NULL, -| PRIMARY KEY (`id`) -| ) ENGINE=InnoDB DEFAULT CHARSET=utf8; +| `uri` VARCHAR(255) NOT NULL, +| `method` VARCHAR(6) NOT NULL, +| `params` TEXT DEFAULT NULL, +| `api_key` VARCHAR(40) NOT NULL, +| `ip_address` VARCHAR(45) NOT NULL, +| `time` INT(11) NOT NULL, +| `rtime` FLOAT DEFAULT NULL, +| `authorized` VARCHAR(1) NOT NULL, +| `response_code` SMALLINT(3) NOT NULL, +| PRIMARY KEY (`id`) +| ) ENGINE=InnoDB DEFAULT CHARSET=utf8; | */ $config['rest_enable_logging'] = FALSE; @@ -394,13 +394,13 @@ | | Default table schema: | CREATE TABLE `limits` ( -| `id` INT(11) NOT NULL AUTO_INCREMENT, -| `uri` VARCHAR(255) NOT NULL, -| `count` INT(10) NOT NULL, -| `hour_started` INT(11) NOT NULL, -| `api_key` VARCHAR(40) NOT NULL, -| PRIMARY KEY (`id`) -| ) ENGINE=InnoDB DEFAULT CHARSET=utf8; +| `id` INT(11) NOT NULL AUTO_INCREMENT, +| `uri` VARCHAR(255) NOT NULL, +| `count` INT(10) NOT NULL, +| `hour_started` INT(11) NOT NULL, +| `api_key` VARCHAR(40) NOT NULL, +| PRIMARY KEY (`id`) +| ) ENGINE=InnoDB DEFAULT CHARSET=utf8; | | To specify the limits within the controller's __construct() method, add per-method | limits with: From 5e2cf853d88630625992f4224e2b8b00f70210b7 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Sun, 28 Jun 2015 21:23:48 +0300 Subject: [PATCH 053/444] Reformatted code and removed do_hash() do_hash() is deprecated in CI3 --- application/controllers/api/example.php | 132 +++---- application/controllers/api/key.php | 461 +++++++++++++----------- 2 files changed, 315 insertions(+), 278 deletions(-) diff --git a/application/controllers/api/example.php b/application/controllers/api/example.php index c483ca83..cef70daf 100644 --- a/application/controllers/api/example.php +++ b/application/controllers/api/example.php @@ -1,108 +1,114 @@ -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; //50 requests per hour per user/key } - + function user_get() { - if(!$this->get('id')) + if (!$this->get('id')) { - $this->response(NULL, 400); + $this->response(NULL, 400); } // $user = $this->some_model->getSomething( $this->get('id') ); - $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!', array('hobbies' => array('fartings', 'bikes'))), - ); - - $user = @$users[$this->get('id')]; - - if($user) + $users = [ + 1 => ['id' => 1, 'name' => 'John', 'email' => 'john@example.com', 'fact' => 'Loves coding'], + 2 => ['id' => 2, 'name' => 'Jim', 'email' => 'jim@example.com', 'fact' => 'Developed on CodeIgniter'], + 3 => ['id' => 3, 'name' => 'Jane', 'email' => 'jane@example.com', 'fact' => 'Lives in the USA', ['hobbies' => ['guitar', 'cycling']]], + ]; + + $user = @$users[$this->get('id')]; + + if ($user) { $this->response($user, 200); // 200 being the HTTP response code } else { - $this->response(array('error' => 'User could not be found'), 404); + $this->response(['error' => 'User could not be found'], 404); } } - + function user_post() { - //$this->some_model->updateUser( $this->get('id') ); - $message = array('id' => $this->get('id'), 'name' => $this->post('name'), 'email' => $this->post('email'), 'message' => 'ADDED!'); - - $this->response($message, 200); // 200 being the HTTP response code + // $this->some_model->update_user($this->get('id')); + $message = [ + 'id' => $this->get('id'), + 'name' => $this->post('name'), + 'email' => $this->post('email'), + 'message' => 'Added a resource' + ]; + + $this->response($message, 201); // 201 being the HTTP response code } - + function user_delete() { - //$this->some_model->deletesomething( $this->get('id') ); - $message = array('id' => $this->get('id'), 'message' => 'DELETED!'); - - $this->response($message, 200); // 200 being the HTTP response code + // $this->some_model->delete_something($this->get(); + $message = [ + 'id' => $this->get('id'), + 'message' => 'Deleted the resource' + ]; + + $this->response($message, 204); // 204 being the HTTP response code } - + function users_get() { - //$users = $this->some_model->getSomething( $this->get('limit') ); - $users = array( - array('id' => 1, 'name' => 'Some Guy', 'email' => 'example1@example.com'), - array('id' => 2, 'name' => 'Person Face', 'email' => 'example2@example.com'), - 3 => array('id' => 3, 'name' => 'Scotty', 'email' => 'example3@example.com', 'fact' => array('hobbies' => array('fartings', 'bikes'))), - ); - - if($users) + // $users = $this->some_model->get_something($this->get('limit')); + $users = [ + ['id' => 1, 'name' => 'John', 'email' => 'john@example.com', 'fact' => 'Loves coding'], + ['id' => 2, 'name' => 'Jim', 'email' => 'jim@example.com', 'fact' => 'Developed on CodeIgniter'], + 3 => ['id' => 3, 'name' => 'Jane', 'email' => 'jane@example.com', 'fact' => 'Lives in the USA', ['hobbies' => ['guitar', 'cycling']]], + ]; + + if ($users) { $this->response($users, 200); // 200 being the HTTP response code } else { - $this->response(array('error' => 'Couldn\'t find any users!'), 404); + $this->response(['error' => 'Couldn\'t find any users!'], 404); } } + public function send_post() + { + var_dump($this->request->body); + } - public function send_post() - { - var_dump($this->request->body); - } - - - public function send_put() - { - var_dump($this->put('foo')); - } + public function send_put() + { + var_dump($this->put('foo')); + } } \ No newline at end of file diff --git a/application/controllers/api/key.php b/application/controllers/api/key.php index 33d8049a..3427575d 100644 --- a/application/controllers/api/key.php +++ b/application/controllers/api/key.php @@ -1,251 +1,282 @@ - array('level' => 10, 'limit' => 10), - 'index_delete' => array('level' => 10), - 'level_post' => array('level' => 10), - 'regenerate_post' => array('level' => 10), - ); - - /** - * Key Create - * - * Insert a key into the database. - * - * @access public - * @return void - */ - public function index_put() + * @package CodeIgniter + * @subpackage Rest Server + * @category Controller + * @author Phil Sturgeon, Chris Kacerguis + * @license MIT + * @link https://github.com/chriskacerguis/codeigniter-restserver + */ +class Key extends REST_Controller { + + protected $methods = [ + 'index_put' => ['level' => 10, 'limit' => 10], + 'index_delete' => ['level' => 10], + 'level_post' => ['level' => 10], + 'regenerate_post' => ['level' => 10], + ]; + + /** + * Key Create + * Insert a key into the database + * + * @access public + * @return void + */ + public function index_put() { - // Build a new key - $key = self::_generate_key(); - - // If no key level provided, give them a rubbish one - $level = $this->put('level') ? $this->put('level') : 1; - $ignore_limits = $this->put('ignore_limits') ? $this->put('ignore_limits') : 1; - - // Insert the new key - if (self::_insert_key($key, array('level' => $level, 'ignore_limits' => $ignore_limits))) - { - $this->response(array('status' => 1, 'key' => $key), 201); // 201 = Created - } - - else - { - $this->response(array('status' => 0, 'error' => 'Could not save the key.'), 500); // 500 = Internal Server Error - } + // Build a new key + $key = self::_generate_key(); + + // If no key level provided, provide a generic key + $level = $this->put('level') ? $this->put('level') : 1; + $ignore_limits = $this->put('ignore_limits') ? $this->put('ignore_limits') : 1; + + // Insert the new key + if (self::_insert_key($key, ['level' => $level, 'ignore_limits' => $ignore_limits])) + { + $this->response([ + 'status' => 1, + 'key' => $key + ], 201); // 201 = Created + } + else + { + $this->response([ + 'status' => 0, + 'error' => 'Could not save the key' + ], 500); // 500 = Internal Server Error + } } - // -------------------------------------------------------------------- - - /** - * Key Delete - * - * Remove a key from the database to stop it working. - * - * @access public - * @return void - */ - public function index_delete() - { - $key = $this->delete('key'); - - // Does this key even exist? - if ( ! self::_key_exists($key)) - { - // NOOOOOOOOO! - $this->response(array('status' => 0, 'error' => 'Invalid API Key.'), 400); - } - - // Kill it - self::_delete_key($key); + // -------------------------------------------------------------------- - // Tell em we killed it - $this->response(array('status' => 1, 'success' => 'API Key was deleted.'), 200); + /** + * Key Delete + * Remove a key from the database to stop it working + * + * @access public + * @return void + */ + public function index_delete() + { + $key = $this->delete('key'); + + // Does this key exist? + if (!self::_key_exists($key)) + { + // It doesn't appear the key exists + $this->response([ + 'error' => 'Invalid API Key' + ], 400); // 400 = Bad Request + } + + // Destroy it + self::_delete_key($key); + + // Respond that the key was destroyed + $this->response([ + 'status' => 1, + 'success' => 'API Key was deleted' + ], 204); // 204 = Success, No Content } - // -------------------------------------------------------------------- - - /** - * Update Key - * - * Change the level - * - * @access public - * @return void - */ - public function level_post() + // -------------------------------------------------------------------- + + /** + * Update Key + * Change the level + * + * @access public + * @return void + */ + public function level_post() { - $key = $this->post('key'); - $new_level = $this->post('level'); - - // Does this key even exist? - if ( ! self::_key_exists($key)) - { - // NOOOOOOOOO! - $this->response(array('error' => 'Invalid API Key.'), 400); - } - - // Update the key level - if (self::_update_key($key, array('level' => $new_level))) - { - $this->response(array('status' => 1, 'success' => 'API Key was updated.'), 200); // 200 = OK - } - - else - { - $this->response(array('status' => 0, 'error' => 'Could not update the key level.'), 500); // 500 = Internal Server Error - } + $key = $this->post('key'); + $new_level = $this->post('level'); + + // Does this key exist? + if (!self::_key_exists($key)) + { + // It doesn't appear the key exists + $this->response([ + 'error' => 'Invalid API Key' + ], 400); // 400 = Bad Request + } + + // Update the key level + if (self::_update_key($key, ['level' => $new_level])) + { + $this->response([ + 'status' => 1, + 'success' => 'API Key was updated' + ], 200); // 200 = OK + } + else + { + $this->response([ + 'status' => 0, + 'error' => 'Could not update the key level' + ], 500); // 500 = Internal Server Error + } } - // -------------------------------------------------------------------- - - /** - * Update Key - * - * Change the level - * - * @access public - * @return void - */ - public function suspend_post() + // -------------------------------------------------------------------- + + /** + * Update Key + * Change the level + * + * @access public + * @return void + */ + public function suspend_post() { - $key = $this->post('key'); - - // Does this key even exist? - if ( ! self::_key_exists($key)) - { - // NOOOOOOOOO! - $this->response(array('error' => 'Invalid API Key.'), 400); - } - - // Update the key level - if (self::_update_key($key, array('level' => 0))) - { - $this->response(array('status' => 1, 'success' => 'Key was suspended.'), 200); // 200 = OK - } - - else - { - $this->response(array('status' => 0, 'error' => 'Could not suspend the user.'), 500); // 500 = Internal Server Error - } + $key = $this->post('key'); + + // Does this key exist? + if (!self::_key_exists($key)) + { + // It doesn't appear the key exists + $this->response([ + 'error' => 'Invalid API Key' + ], 400); // 400 = Bad Request + } + + // Update the key level + if (self::_update_key($key, ['level' => 0])) + { + $this->response([ + 'status' => 1, + 'success' => 'Key was suspended' + ], 200); // 200 = OK + } + else + { + $this->response([ + 'status' => 0, + 'error' => 'Could not suspend the user' + ], 500); // 500 = Internal Server Error + } } - // -------------------------------------------------------------------- - - /** - * Regenerate Key - * - * Remove a key from the database to stop it working. - * - * @access public - * @return void - */ - public function regenerate_post() + // -------------------------------------------------------------------- + + /** + * Regenerate Key + * Remove a key from the database to stop it working + * + * @access public + * @return void + */ + public function regenerate_post() { - $old_key = $this->post('key'); - $key_details = self::_get_key($old_key); - - // The key wasnt found - if ( ! $key_details) - { - // NOOOOOOOOO! - $this->response(array('status' => 0, 'error' => 'Invalid API Key.'), 400); - } - - // Build a new key - $new_key = self::_generate_key(); - - // Insert the new key - if (self::_insert_key($new_key, array('level' => $key_details->level, 'ignore_limits' => $key_details->ignore_limits))) - { - // Suspend old key - self::_update_key($old_key, array('level' => 0)); - - $this->response(array('status' => 1, 'key' => $new_key), 201); // 201 = Created - } - - else - { - $this->response(array('status' => 0, 'error' => 'Could not save the key.'), 500); // 500 = Internal Server Error - } + $old_key = $this->post('key'); + $key_details = self::_get_key($old_key); + + // Does this key exist? + if (!$key_details) + { + // It doesn't appear the key exists + $this->response([ + 'error' => 'Invalid API Key' + ], 400); // 400 = Bad Request + } + + // Build a new key + $new_key = self::_generate_key(); + + // Insert the new key + if (self::_insert_key($new_key, ['level' => $key_details->level, 'ignore_limits' => $key_details->ignore_limits])) + { + // Suspend old key + self::_update_key($old_key, ['level' => 0]); + + $this->response([ + 'status' => 1, + 'key' => $new_key + ], 201); // 201 = Created + } + else + { + $this->response([ + 'status' => 0, + 'error' => 'Could not save the key' + ], 500); // 500 = Internal Server Error + } } - // -------------------------------------------------------------------- + // -------------------------------------------------------------------- - /* Helper Methods */ - - private function _generate_key() - { - //$this->load->helper('security'); - - do - { - $salt = do_hash(time().mt_rand()); - $new_key = substr($salt, 0, config_item('rest_key_length')); - } + /* Helper Methods */ - // Already in the DB? Fail. Try again - while (self::_key_exists($new_key)); + private function _generate_key() + { + do + { + // Generate a random salt + $salt = mcrypt_create_iv(16, MCRYPT_DEV_URANDOM); + $new_key = substr($salt, 0, config_item('rest_key_length')); + } + while (self::_key_exists($new_key)); + // Already in the DB? Fail. Try again + + return $new_key; + } - return $new_key; - } + // -------------------------------------------------------------------- - // -------------------------------------------------------------------- + /* Private Data Methods */ - /* Private Data Methods */ + private function _get_key($key) + { + return $this->db->where(config_item('rest_key_column'), $key)->get(config_item('rest_keys_table'))->row(); + } - private function _get_key($key) - { - 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(config_item('rest_key_column'), $key)->count_all_results(config_item('rest_keys_table')) > 0; + } - private function _key_exists($key) - { - return $this->db->where(config_item('rest_key_column'), $key)->count_all_results(config_item('rest_keys_table')) > 0; - } + // -------------------------------------------------------------------- - // -------------------------------------------------------------------- + private function _insert_key($key, $data) + { - private function _insert_key($key, $data) - { - - $data[config_item('rest_key_column')] = $key; - $data['date_created'] = function_exists('now') ? now() : time(); + $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')); - } + return $this->db + ->set($data) + ->insert(config_item('rest_keys_table')); + } - // -------------------------------------------------------------------- + // -------------------------------------------------------------------- - private function _update_key($key, $data) - { - return $this->db->where(config_item('rest_key_column'), $key)->update(config_item('rest_keys_table'), $data); - } + private function _update_key($key, $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(config_item('rest_key_column'), $key)->delete(config_item('rest_keys_table')); - } + private function _delete_key($key) + { + return $this->db + ->where(config_item('rest_key_column'), $key) + ->delete(config_item('rest_keys_table')); + } } From 556503ca9c1326e0ffdc4e4a3fddb3f11783b9b8 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Sun, 28 Jun 2015 21:24:53 +0300 Subject: [PATCH 054/444] Fixed whitespace and removed period --- application/controllers/api/example.php | 2 +- application/controllers/api/key.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/application/controllers/api/example.php b/application/controllers/api/example.php index cef70daf..570a2a57 100644 --- a/application/controllers/api/example.php +++ b/application/controllers/api/example.php @@ -111,4 +111,4 @@ public function send_put() { var_dump($this->put('foo')); } -} \ No newline at end of file +} diff --git a/application/controllers/api/key.php b/application/controllers/api/key.php index 3427575d..a7bb9cd5 100644 --- a/application/controllers/api/key.php +++ b/application/controllers/api/key.php @@ -7,7 +7,7 @@ /** * Keys Controller - * This is a basic Key Management REST controller to make and delete keys. + * This is a basic Key Management REST controller to make and delete keys * * @package CodeIgniter * @subpackage Rest Server From 2630f2c87985c45b2887d72b7f78bbd250b404e1 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Sun, 28 Jun 2015 21:29:36 +0300 Subject: [PATCH 055/444] Removed whitespace, improper indentation and changed to new array syntax --- LICENSE.txt | 1 - README.md | 26 +++++++++++++------------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/LICENSE.txt b/LICENSE.txt index 77a4a2ef..365ffb87 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -19,4 +19,3 @@ 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. - diff --git a/README.md b/README.md index ebadd4aa..0c1f3139 100644 --- a/README.md +++ b/README.md @@ -51,9 +51,9 @@ The HTTP spec for DELETE requests precludes the use of parameters. For delete r ```php public function index_delete($id) { - $this->response(array( - 'returned from delete:' => $id, - )); + $this->response([ + 'returned from delete:' => $id, + ]); } ``` @@ -101,7 +101,7 @@ public function index_post() 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`: ```php -$this->response(array()); // HTTP 404 Not Found +$this->response([]); // HTTP 404 Not Found ``` ## Multilingual Support @@ -131,7 +131,7 @@ This class also provides rudimentary support for HTTP basic authentication and/o 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: ```php -$config['rest_valid_logins'] = array( 'username' => 'password', 'other_person' => 'secure123' ); +$config['rest_valid_logins'] = ['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. @@ -160,12 +160,12 @@ You'll need to create a new database table to store and access the keys. `REST_C ```sql 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`) + `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=InnoDB DEFAULT CHARSET=utf8; ``` @@ -183,8 +183,8 @@ $ curl -X POST -H "X-API-KEY: some_key_here" http://example.com/books ## Contributions -This project was originally written by 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 +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 023d04d4988fba578f84c819f3b6bfa7e19b56c6 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Sun, 28 Jun 2015 21:48:31 +0300 Subject: [PATCH 056/444] Fixed #411 --- application/controllers/api/key.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/controllers/api/key.php b/application/controllers/api/key.php index a7bb9cd5..a708f52b 100644 --- a/application/controllers/api/key.php +++ b/application/controllers/api/key.php @@ -39,7 +39,7 @@ public function index_put() // If no key level provided, provide a generic key $level = $this->put('level') ? $this->put('level') : 1; - $ignore_limits = $this->put('ignore_limits') ? $this->put('ignore_limits') : 1; + $ignore_limits = ctype_digit($this->put('ignore_limits')) ? (int) $this->put('ignore_limits') : 1; // Insert the new key if (self::_insert_key($key, ['level' => $level, 'ignore_limits' => $ignore_limits])) From 08f017ee0b5abaf19fcb0541cc9ae5ef940ea29f Mon Sep 17 00:00:00 2001 From: softwarespot Date: Sun, 28 Jun 2015 22:22:35 +0300 Subject: [PATCH 057/444] Fixes #445 --- application/libraries/REST_Controller.php | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index c10ad02c..efa86a1e 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -540,15 +540,20 @@ public function response($data = NULL, $http_code = NULL, $continue = FALSE) if (method_exists($this->format, 'to_' . $this->response->format)) { // Set the format header - header( - 'Content-Type: ' . $this->_supported_formats[$this->response->format] - . '; charset=' . strtolower($this->config->item('charset'))); + header('Content-Type: ' . $this->_supported_formats[$this->response->format] + . '; charset=' . strtolower($this->config->item('charset'))); $output = $this->format->factory($data)->{'to_' . $this->response->format}(); } else { - // Format is not supported, so output the raw data + // If an array or object, then parse as a json, so as to be a 'string' + if (is_array($data) || is_object($data)) + { + $data = $this->format->factory($data)->{'to_json'}(); + } + + // Format is not supported, so output the raw data as a string $output = $data; } } From cb309bd155591cba4b947b010f6f1ce24d2e9101 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Sun, 28 Jun 2015 22:35:26 +0300 Subject: [PATCH 058/444] Tidied the code and removed the body to_array() The reason is simple, a string datatype should be returned via exit() or echo and not an array datatype --- application/libraries/Format.php | 693 +++++++++++++++---------------- 1 file changed, 327 insertions(+), 366 deletions(-) diff --git a/application/libraries/Format.php b/application/libraries/Format.php index b4f23d74..985f7c75 100644 --- a/application/libraries/Format.php +++ b/application/libraries/Format.php @@ -1,380 +1,341 @@ format->factory(array('foo' => 'bar'))->to_xml(); - * - * @access public - * @param $data, mixed general date to be converted - * @param $from_type, 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 equivalent - $class = __CLASS__; - return new $class($data, $from_type); - } - - /** - * Do not use this directly, call factory() - * - * @access public - * @param $data, bool - * @param $from_type, bool - */ - 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([$this, '_from_' . $from_type], $data); - } - - else - { - throw new Exception('Format class does not support conversion from "' . $from_type . '".'); - } - } - - $this->_data = $data; - } - - // FORMATING OUTPUT --------------------------------------------------------- - - /** - * to_array - * - * @access public - * @param $data - */ - public function to_array($data = NULL) - { - // If not just NULL, but nothing is provided - if ($data === NULL && ! func_num_args()) - { - $data = $this->_data; - } - - $array = []; - - foreach ((array) $data as $key => $value) - { - if (is_object($value) || is_array($value)) - { - $array[$key] = $this->to_array($value); - } - - else - { - $array[$key] = $value; - } - } - - return $array; - } - - /** - * Format XML for output - * - * @access public - * @param $data - * @param $structure - * @param $basenode - */ - public function to_xml($data = NULL, $structure = NULL, $basenode = 'xml') - { - if ($data === NULL && ! 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) && ! is_object($data)) - { - $data = (array) $data; - } - - 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... - $key = (singular($basenode) != $basenode) ? singular($basenode) : 'item'; - } - - // 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 - elseif (is_array($value) || is_object($value)) - { - $node = $structure->addChild($key); - - // recursive 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 - * - * @access public - */ - public function to_html() - { - $data = (array)$this->_data; - - // Multi-dimensional array - if (isset($data[0]) && is_array($data[0])) - { - $headings = array_keys($data[0]); - } - - // Single array - else - { - $headings = array_keys($data); - $data = [$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 CSV for output - * - * @access public - */ - public function to_csv() - { - $data = (array)$this->_data; - - // Multi-dimensional array - if (isset($data[0]) && is_array($data[0])) - { - $headings = array_keys($data[0]); - } - - // Single array - else - { - $headings = array_keys($data); - $data = [$data]; - } - - $output = '"'.implode('","', $headings).'"'.PHP_EOL; - foreach ($data as &$row) - { - $row = str_replace('"', '""', $row); // Escape dbl quotes per RFC 4180 - $output .= '"'.implode('","', $row).'"'.PHP_EOL; - } - - return $output; - } - - /** - * Encode as JSON - * - * @access public - */ - public function to_json() - { - $callback = isset($_GET['callback']) ? $_GET['callback'] : ''; - if ($callback === '') - { - return json_encode($this->_data); + protected $_data = []; // Array to convert + protected $_from_type = NULL; // View filename + + /** + * DO NOT CALL THIS DIRECTLY, USE factory() + * + * @param null $data + * @param null $from_type + * + * @throws Exception + */ + 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([$this, '_from_' . $from_type], $data); + } + else + { + throw new Exception('Format class does not support conversion from "' . $from_type . '".'); + } + } + + $this->_data = $data; + } + + /** + * Returns an instance of the Format class + * e.g: echo $this->format->factory(array('foo' => 'bar'))->to_xml(); + * + * @param $data + * @param null $from_type + * + * @return mixed + */ + public function factory($data, $from_type = NULL) + { + // Stupid stuff to emulate the "new static()" stuff in this libraries PHP 5.3 equivalent + $class = __CLASS__; + + return new $class($data, $from_type); + } + + // FORMATING OUTPUT --------------------------------------------------------- + + /** + * to_array + * + * @param null $data + * + * @return array + */ + public function to_array($data = NULL) + { + // As the return value should be a string, it makes no sense + // to return an array datatype as that will result in an error or sorts + return $this->to_json($data); + } + + /** + * Format XML for output + * + * @param null $data + * @param null $structure + * @param string $basenode + * + * @return mixed + */ + public function to_xml($data = NULL, $structure = NULL, $basenode = 'xml') + { + if ($data === NULL && !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) && !is_object($data)) + { + $data = (array) $data; + } + + foreach ($data as $key => $value) + { + + //change false/true to 0/1 + if (is_bool($value)) + { + $value = (int) $value; + } - /* Had to take out this code, it doesn't work on Objects. - $str = $this->_data; - array_walk_recursive($str, function(&$item, $key) + // no numeric keys in our xml please! + if (is_numeric($key)) { - if(!mb_detect_encoding($item, 'utf-8', true)) + // 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 ($key === '_attributes' && (is_array($value) || is_object($value))) + { + $attributes = $value; + if (is_object($attributes)) { - $item = utf8_encode($item); + $attributes = get_object_vars($attributes); } - }); - - return json_encode($str); - */ - } - - // we only honour jsonp callback which are valid javascript identifiers - elseif (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 $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 json_encode($this->_data); - } - } - - /** - * Encode as Serialized array - * - * @access public - */ - public function to_serialized() - { - return serialize($this->_data); - } - - /** - * Output as a string representing the PHP structure - * - * @access public - */ - public function to_php() - { - return var_export($this->_data, TRUE); - } - - /** - * Format XML for output - * - * @access protected - * @param $string - */ - protected function _from_xml($string) - { - return $string ? (array) simplexml_load_string($string, 'SimpleXMLElement', LIBXML_NOCDATA) : []; - } - - /** - * Format CSV for output - * This function is DODGY! Not perfect CSV support but works with my REST_Controller - * - * @access protected - * @param $string - */ - protected function _from_csv($string) - { - $data = []; - - // 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 - * - * @access private - * @param string - */ - private function _from_json($string) - { - return json_decode(trim($string)); - } - - /** - * Encode as Serialized array - * - * @access private - * @param $string - * - */ - 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 - * - * @access private - * @param $string - */ - private function _from_php($string) - { - return trim($string); - } -} + foreach ($attributes as $attributeName => $attributeValue) + { + $structure->addAttribute($attributeName, $attributeValue); + } + } + // if there is another array found recursively call this function + elseif (is_array($value) || is_object($value)) + { + $node = $structure->addChild($key); + + // recursive 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 + * + * @return mixed + */ + public function to_html() + { + $data = (array) $this->_data; + + // Multi-dimensional array + if (isset($data[0]) && is_array($data[0])) + { + $headings = array_keys($data[0]); + } + + // Single array + else + { + $headings = array_keys($data); + $data = [$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 CSV for output + * + * @return mixed + */ + public function to_csv() + { + $data = (array) $this->_data; + + // Multi-dimensional array + if (isset($data[0]) && is_array($data[0])) + { + $headings = array_keys($data[0]); + } + + // Single array + else + { + $headings = array_keys($data); + $data = [$data]; + } + + $output = '"' . implode('","', $headings) . '"' . PHP_EOL; + foreach ($data as &$row) + { + $row = str_replace('"', '""', $row); // Escape dbl quotes per RFC 4180 + $output .= '"' . implode('","', $row) . '"' . PHP_EOL; + } + + return $output; + } + + /** + * Encode as JSON + * + * @return mixed + */ + public function to_json() + { + $callback = isset($_GET['callback']) ? $_GET['callback'] : ''; + if ($callback === '') + { + return json_encode($this->_data, JSON_PRETTY_PRINT); + } + + // we only honour jsonp callback which are valid javascript identifiers + elseif (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 $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; -/* End of file format.php */ + return json_encode($this->_data); + } + } + + /** + * Encode as a 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); + } + + /** + * @param $string + * + * @return array + */ + protected function _from_xml($string) + { + return $string ? (array) simplexml_load_string($string, 'SimpleXMLElement', LIBXML_NOCDATA) : []; + } + + /** + * @param $string + * + * @return array + */ + protected function _from_csv($string) + { + $data = []; + + // 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; + } + + /** + * @param $string + * + * @return mixed + */ + private function _from_json($string) + { + return json_decode(trim($string)); + } + + /** + * @param $string + * + * @return mixed + */ + private function _from_serialize($string) + { + return unserialize(trim($string)); + } + + + /** + * @param $string + * + * @return string + */ + private function _from_php($string) + { + return trim($string); + } + +} From 46d31746741d0cb65efadf41819df8131d12a0be Mon Sep 17 00:00:00 2001 From: softwarespot Date: Sun, 28 Jun 2015 22:46:12 +0300 Subject: [PATCH 059/444] Formatted the to_json() function --- application/libraries/Format.php | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/application/libraries/Format.php b/application/libraries/Format.php index 985f7c75..63d0823e 100644 --- a/application/libraries/Format.php +++ b/application/libraries/Format.php @@ -232,27 +232,27 @@ public function to_csv() */ public function to_json() { - $callback = isset($_GET['callback']) ? $_GET['callback'] : ''; - if ($callback === '') + $callback = $this->input->get('callback'); + if (empty($callback) === TRUE) { return json_encode($this->_data, JSON_PRETTY_PRINT); } - // we only honour jsonp callback which are valid javascript identifiers + // We only honour jsonp callback which are valid javascript identifiers elseif (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 + // Set the content type header("Content-Type: application/javascript"); + // Return the data as encoded json with a callback 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 json_encode($this->_data); - } + // An invalid jsonp callback function provided. + // Though I don't believe this should be hardcoded here + $this->_data['warning'] = 'INVALID JSONP CALLBACK: ' . $callback; + + return json_encode($this->_data); } /** From 195a3ba68528a7a934523f8369636b834f364df6 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Sun, 28 Jun 2015 22:59:02 +0300 Subject: [PATCH 060/444] Tidied _check_access() --- application/libraries/REST_Controller.php | 29 ++++++++++------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index efa86a1e..bfd55d87 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -1765,35 +1765,32 @@ protected function _log_response_code($http_code) * Check to see if the API key has access to the controller and methods * * @access protected - * @return bool + * @return bool TRUE the API key has access; otherwise, FALSE */ protected function _check_access() { - // if we don't want to check access, just return TRUE + // If we don't want to check access, just return TRUE if (config_item('rest_enable_access') === FALSE) { return TRUE; } // Fetch controller based on path and controller name - $controller = implode('/', [$this->router->directory, $this->router->class]); + $controller = implode( + '/', [ + $this->router->directory, + $this->router->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); - - $query = $this->rest->db->get(config_item('rest_access_table')); - - if ($query->num_rows() > 0) - { - return TRUE; - } - - return FALSE; + // Query the access table and get the number of results + return $this->rest->db + ->where('key', $this->rest->key) + ->where('controller', $controller) + ->get(config_item('rest_access_table')) + ->num_rows() > 0; } } From 6dd477397b629310686b7aea51f2349157758538 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Sun, 28 Jun 2015 23:06:21 +0300 Subject: [PATCH 061/444] Tidied up logging functions adding better documentation --- application/libraries/REST_Controller.php | 56 ++++++++++++++--------- 1 file changed, 34 insertions(+), 22 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index bfd55d87..bf78322e 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -834,31 +834,33 @@ protected function _detect_lang() } /** - * Log request - * Record the entry for awesomeness purposes + * Add the request to the log table * * @access protected * - * @param bool $authorized + * @param bool $authorized TRUE the user is authorized; otherwise, FALSE * - * @return object + * @return bool TRUE the data was inserted; otherwise, FALSE */ protected function _log_request($authorized = FALSE) { - $status = $this->rest->db->insert( - config_item('rest_logs_table'), [ - 'uri' => $this->uri->uri_string(), - 'method' => $this->request->method, - 'params' => $this->_args ? (config_item('rest_logs_json_params') === TRUE ? 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 - ]); + // Insert the request into the log table + $isInserted = $this->rest->db + ->insert( + config_item('rest_logs_table'), [ + 'uri' => $this->uri->uri_string(), + 'method' => $this->request->method, + 'params' => $this->_args ? (config_item('rest_logs_json_params') === TRUE ? json_encode($this->_args) : serialize($this->_args)) : NULL, + 'api_key' => isset($this->rest->key) ? $this->rest->key : '', + 'ip_address' => $this->input->ip_address(), + 'time' => now(), // Used to be: function_exists('now') ? now() : time() + 'authorized' => $authorized + ]); + // Get the last insert id to update at a later stage of the request $this->_insert_id = $this->rest->db->insert_id(); - return $status; + return $isInserted; } /** @@ -1733,32 +1735,42 @@ protected function _force_login($nonce = '') } /** - * updates the log with the access time + * Updates the log table with the total access time * * @access protected * @author Chris Kacerguis - * @return bool + * + * @return bool TRUE log table updated; otherwise, FALSE */ - protected function _log_access_time() { $payload['rtime'] = $this->_end_rtime - $this->_start_rtime; - return $this->rest->db->update(config_item('rest_logs_table'), $payload, ['id' => $this->_insert_id]); + return $this->rest->db + ->update( + config_item('rest_logs_table'), $payload, [ + 'id' => $this->_insert_id + ]); } /** - * updates the log with response code result + * Updates the log table with HTTP response code * * @access protected * @author Justin Chen - * @return bool + * + * @param $http_code int HTTP status code + * + * @return bool TRUE log table updated; otherwise, FALSE */ protected function _log_response_code($http_code) { $payload['response_code'] = $http_code; - return $this->rest->db->update(config_item('rest_logs_table'), $payload, ['id' => $this->_insert_id]); + return $this->rest->db->update( + config_item('rest_logs_table'), $payload, [ + 'id' => $this->_insert_id + ]); } /** From 965432f21716f529bd587621028ca9646bf43dfc Mon Sep 17 00:00:00 2001 From: softwarespot Date: Sun, 28 Jun 2015 23:38:44 +0300 Subject: [PATCH 062/444] Tidied _check_limit() --- application/libraries/REST_Controller.php | 31 +++++++++++------------ 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index bf78322e..0452aa37 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -864,40 +864,38 @@ protected function _log_request($authorized = FALSE) } /** - * Limiting requests - * Check if the requests are coming in a tad too fast. + * Check if the requests to a controller method exceed a limit * * @access protected * - * @param string $controller_method The method being called. + * @param string $controller_method The method being called * - * @return bool + * @return bool TRUE the call limit is below the threshold; otherwise, FALSE */ protected function _check_limit($controller_method) { // They are special, or it might not even have a limit if (!empty($this->rest->ignore_limits) || !isset($this->methods[$controller_method]['limit'])) { - // On your way sonny-jim. + // Everything is fine return TRUE; } - // How many times can you get to this method an hour? + // How many times can you get to this method in an hour? $limit = $this->methods[$controller_method]['limit']; - // Get data on a keys usage + // Get data about a keys' usage and limit to one row $result = $this->rest->db ->where('uri', $this->uri->uri_string()) ->where('api_key', $this->rest->key) - ->get(config_item('rest_limits_table')) - ->row(); + ->limit(1) + ->get(config_item('rest_limits_table')); - // No calls yet for this key + // No calls have been made for this key if (!$result) { - // Right, set one up from scratch - $this->rest->db->insert( - config_item('rest_limits_table'), [ + // Create a new row for the following key + $this->rest->db->insert(config_item('rest_limits_table'), [ 'uri' => $this->uri->uri_string(), 'api_key' => isset($this->rest->key) ? $this->rest->key : '', 'count' => 1, @@ -906,9 +904,9 @@ protected function _check_limit($controller_method) } // Been an hour since they called - elseif ($result->hour_started < time() - (60 * 60)) + elseif ($result->hour_started < (time() - 3600)) { - // Reset the started period + // Reset the started period and count $this->rest->db ->where('uri', $this->uri->uri_string()) ->where('api_key', isset($this->rest->key) ? $this->rest->key : '') @@ -920,12 +918,13 @@ protected function _check_limit($controller_method) // They have called within the hour, so lets update else { - // Your luck is out, you've called too many times! + // The limit has been exceeded if ($result->count >= $limit) { return FALSE; } + // Increase the count by one $this->rest->db ->where('uri', $this->uri->uri_string()) ->where('api_key', $this->rest->key) From da017b2b6715f7da87a19e6786abb0e9bdb51cf8 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Sun, 28 Jun 2015 23:43:18 +0300 Subject: [PATCH 063/444] Tidied up _check_php_session() and _check_login() --- application/libraries/REST_Controller.php | 29 +++++++++++++++-------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 0452aa37..2f8df4da 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -1491,7 +1491,7 @@ protected function _perform_library_auth($username = '', $password = NULL) * * @return bool */ - protected function _check_login($username = '', $password = FALSE) + protected function _check_login($username = NULL, $password = FALSE) { if (empty($username)) { @@ -1502,8 +1502,9 @@ protected function _check_login($username = '', $password = FALSE) $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 + 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] : '')); } @@ -1512,21 +1513,21 @@ protected function _check_login($username = '', $password = FALSE) return FALSE; } - if ($auth_source == 'ldap') + if ($auth_source === 'ldap') { - log_message('debug', "performing LDAP authentication for $username"); + log_message('debug', "Performing LDAP authentication for $username"); return $this->_perform_ldap_auth($username, $password); } - if ($auth_source == 'library') + 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); } - if (!array_key_exists($username, $valid_logins)) + if (array_key_exists($username, $valid_logins) === FALSE) { return FALSE; } @@ -1540,16 +1541,24 @@ protected function _check_login($username = '', $password = FALSE) } /** - * Check to see if the user is logged into the web app with a php session key. + * Check to see if the user is logged in with a PHP session key * * @access protected */ protected function _check_php_session() { + // Get the auth_source config item $key = $this->config->item('auth_source'); + + // If falsy, then the user isn't logged in if (!$this->session->userdata($key)) { - $this->response([config_item('rest_status_field_name') => FALSE, config_item('rest_message_field_name') => 'Not Authorized'], 401); + // Display an error response + $this->response( + [ + config_item('rest_status_field_name') => FALSE, + config_item('rest_message_field_name') => 'Not Authorized' + ], 401); } } From c2e3a200830682f78d28bec24bcb42240e48dbfe Mon Sep 17 00:00:00 2001 From: softwarespot Date: Sun, 28 Jun 2015 23:58:57 +0300 Subject: [PATCH 064/444] Fixed incorrect return parameters --- application/libraries/REST_Controller.php | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 2f8df4da..3a3d3b24 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -1160,9 +1160,10 @@ protected function _parse_delete() * @access public * * @param NULL $key Key to retrieve from the GET request + * If NULL an array of arguments is returned * @param NULL $xss_clean Whether to apply XSS filtering * - * @return string|NULL Value from the GET request; otherwise, FALSE + * @return array|string|FALSE Value from the GET request; otherwise, FALSE */ public function get($key = NULL, $xss_clean = NULL) { @@ -1179,10 +1180,11 @@ public function get($key = NULL, $xss_clean = NULL) * * @access public * - * @param NULL $key Key to retrieve from the OPTIONS request + * @param NULL $key Key to retrieve from the OPTIONS request. + * If NULL an array of arguments is returned * @param NULL $xss_clean Whether to apply XSS filtering * - * @return string|NULL Value from the OPTIONS request; otherwise, FALSE + * @return array|string|FALSE Value from the OPTIONS request; otherwise, FALSE */ public function options($key = NULL, $xss_clean = NULL) { @@ -1200,9 +1202,10 @@ public function options($key = NULL, $xss_clean = NULL) * @access public * * @param NULL $key Key to retrieve from the HEAD request + * If NULL an array of arguments is returned * @param NULL $xss_clean Whether to apply XSS filtering * - * @return string|NULL Value from the HEAD request; otherwise, FALSE + * @return array|string|FALSE Value from the HEAD request; otherwise, FALSE */ public function head($key = NULL, $xss_clean = NULL) { @@ -1220,9 +1223,10 @@ public function head($key = NULL, $xss_clean = NULL) * @access public * * @param NULL $key Key to retrieve from the POST request + * If NULL an array of arguments is returned * @param NULL $xss_clean Whether to apply XSS filtering * - * @return string|NULL Value from the POST request; otherwise, FALSE + * @return array|string|FALSE Value from the POST request; otherwise, FALSE */ public function post($key = NULL, $xss_clean = NULL) { @@ -1240,9 +1244,10 @@ public function post($key = NULL, $xss_clean = NULL) * @access public * * @param NULL $key Key to retrieve from the PUT request + * If NULL an array of arguments is returned * @param NULL $xss_clean Whether to apply XSS filtering * - * @return string|NULL Value from the PUT request; otherwise, FALSE + * @return array|string|FALSE Value from the PUT request; otherwise, FALSE */ public function put($key = NULL, $xss_clean = NULL) { @@ -1260,9 +1265,10 @@ public function put($key = NULL, $xss_clean = NULL) * @access public * * @param NULL $key Key to retrieve from the DELETE request + * If NULL an array of arguments is returned * @param NULL $xss_clean Whether to apply XSS filtering * - * @return string|NULL Value from the DELETE request; otherwise, FALSE + * @return array|string|FALSE Value from the DELETE request; otherwise, FALSE */ public function delete($key = NULL, $xss_clean = NULL) { @@ -1280,9 +1286,10 @@ public function delete($key = NULL, $xss_clean = NULL) * @access public * * @param NULL $key Key to retrieve from the PATCH request + * If NULL an array of arguments is returned * @param NULL $xss_clean Whether to apply XSS filtering * - * @return string|NULL Value from the PATCH request; otherwise, FALSE + * @return array|string|FALSE Value from the PATCH request; otherwise, FALSE */ public function patch($key = NULL, $xss_clean = NULL) { From c167ffc03f078cfb26162c34b6a74afd6f04ac3f Mon Sep 17 00:00:00 2001 From: softwarespot Date: Mon, 29 Jun 2015 08:44:11 +0300 Subject: [PATCH 065/444] Fixed issue with using $this->input and not getting an instance --- 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 63d0823e..26b0b961 100644 --- a/application/libraries/Format.php +++ b/application/libraries/Format.php @@ -232,13 +232,17 @@ public function to_csv() */ public function to_json() { - $callback = $this->input->get('callback'); + // Get the CodeIgniter reference + $ci = &get_instance(); + + // Get the callback parameter (if set) + $callback = $ci->input->get('callback'); if (empty($callback) === TRUE) { return json_encode($this->_data, JSON_PRETTY_PRINT); } - // We only honour jsonp callback which are valid javascript identifiers + // We only honour a jsonp callback which are valid javascript identifiers elseif (preg_match('/^[a-z_\$][a-z0-9\$_]*(\.[a-z_\$][a-z0-9\$_]*)*$/i', $callback)) { // Set the content type From 07b2df2534c9889ee36f42bb271d3853c55a883a Mon Sep 17 00:00:00 2001 From: softwarespot Date: Mon, 29 Jun 2015 08:52:20 +0300 Subject: [PATCH 066/444] Added comment about unused variable --- 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 3a3d3b24..7ab59d8c 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -1368,6 +1368,7 @@ protected function _perform_ldap_auth($username = '', $password = NULL) log_message('debug', 'LDAP Auth: Connect to ' . (isset($ldaphost) ? $ldaphost : '[ldap not configured]')); + // Appears to be unused $ldapconfig['authrealm'] = $this->config->item('domain', 'ldap'); // connect to ldap server From dcd6723ff1f45125ff86b7d21ebd6244648a9c7d Mon Sep 17 00:00:00 2001 From: softwarespot Date: Mon, 29 Jun 2015 08:58:00 +0300 Subject: [PATCH 067/444] Reverted code for applying a limit --- 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 7ab59d8c..a43bd09a 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -888,8 +888,8 @@ protected function _check_limit($controller_method) $result = $this->rest->db ->where('uri', $this->uri->uri_string()) ->where('api_key', $this->rest->key) - ->limit(1) - ->get(config_item('rest_limits_table')); + ->get(config_item('rest_limits_table')) + ->row(); // No calls have been made for this key if (!$result) From 94ed038dfb37e428ca2ecad44a4668cae9fd18c6 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Mon, 29 Jun 2015 09:05:58 +0300 Subject: [PATCH 068/444] Call get_instance() only once --- application/libraries/Format.php | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/application/libraries/Format.php b/application/libraries/Format.php index 26b0b961..790fcf43 100644 --- a/application/libraries/Format.php +++ b/application/libraries/Format.php @@ -9,6 +9,13 @@ */ class Format { + /** + * CodeIgniter instance + * + * @var object + */ + private $_ci; + protected $_data = []; // Array to convert protected $_from_type = NULL; // View filename @@ -20,9 +27,14 @@ class Format { * * @throws Exception */ + public function __construct($data = NULL, $from_type = NULL) { - get_instance()->load->helper('inflector'); + // Get the CodeIgniter reference + $ci = &get_instance(); + + // Load the inflector helper + $ci->load->helper('inflector'); // If the provided data is already formatted we should probably convert it to an array if ($from_type !== NULL) @@ -180,7 +192,7 @@ public function to_html() $data = [$data]; } - $ci = get_instance(); + // Load the table library $ci->load->library('table'); $ci->table->set_heading($headings); @@ -232,9 +244,6 @@ public function to_csv() */ public function to_json() { - // Get the CodeIgniter reference - $ci = &get_instance(); - // Get the callback parameter (if set) $callback = $ci->input->get('callback'); if (empty($callback) === TRUE) @@ -331,7 +340,6 @@ private function _from_serialize($string) return unserialize(trim($string)); } - /** * @param $string * From 2bd0ce3c96662bc59871fda67a4c0560bf947456 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Mon, 29 Jun 2015 09:07:24 +0300 Subject: [PATCH 069/444] Added doc comments for member variables --- application/libraries/Format.php | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/application/libraries/Format.php b/application/libraries/Format.php index 790fcf43..252a483e 100644 --- a/application/libraries/Format.php +++ b/application/libraries/Format.php @@ -16,8 +16,19 @@ class Format { */ private $_ci; - protected $_data = []; // Array to convert - protected $_from_type = NULL; // View filename + /** + * Array to convert + * + * @var array + */ + protected $_data = []; + + /** + * Type to convert from + * + * @var string + */ + protected $_from_type = NULL; /** * DO NOT CALL THIS DIRECTLY, USE factory() From e26ddcff847513e1fc6cbcdcebb8e8b1fc39811b Mon Sep 17 00:00:00 2001 From: softwarespot Date: Mon, 29 Jun 2015 09:21:12 +0300 Subject: [PATCH 070/444] Removed passing data as an optional parameter The other functions (apart from XML) don't have an optional data parameter --- application/libraries/Format.php | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/application/libraries/Format.php b/application/libraries/Format.php index 252a483e..b9cd678a 100644 --- a/application/libraries/Format.php +++ b/application/libraries/Format.php @@ -83,17 +83,15 @@ public function factory($data, $from_type = NULL) // FORMATING OUTPUT --------------------------------------------------------- /** - * to_array - * - * @param null $data - * - * @return array + * Encode as JSON + * + * @return string JSON representation of a value */ - public function to_array($data = NULL) + public function to_array() { // As the return value should be a string, it makes no sense // to return an array datatype as that will result in an error or sorts - return $this->to_json($data); + return $this->to_json(); } /** @@ -251,7 +249,7 @@ public function to_csv() /** * Encode as JSON * - * @return mixed + * @return string JSON representation of a value */ public function to_json() { From 588832bf0a44b2722285ebc4e50bfcab8bbfc091 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Mon, 29 Jun 2015 09:25:26 +0300 Subject: [PATCH 071/444] Cast as an array if not one already --- application/libraries/Format.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/application/libraries/Format.php b/application/libraries/Format.php index b9cd678a..144b6ee5 100644 --- a/application/libraries/Format.php +++ b/application/libraries/Format.php @@ -186,7 +186,8 @@ public function to_xml($data = NULL, $structure = NULL, $basenode = 'xml') */ public function to_html() { - $data = (array) $this->_data; + // Cast as an array if no already + is_array($this->_data) || $data = (array) $this->_data; // Multi-dimensional array if (isset($data[0]) && is_array($data[0])) @@ -221,7 +222,8 @@ public function to_html() */ public function to_csv() { - $data = (array) $this->_data; + // Cast as an array if no already + is_array($this->_data) || $data = (array) $this->_data; // Multi-dimensional array if (isset($data[0]) && is_array($data[0])) @@ -255,6 +257,7 @@ public function to_json() { // Get the callback parameter (if set) $callback = $ci->input->get('callback'); + if (empty($callback) === TRUE) { return json_encode($this->_data, JSON_PRETTY_PRINT); From e6ce4538fe3ea0a2911e47885541958cdae63fce Mon Sep 17 00:00:00 2001 From: softwarespot Date: Mon, 29 Jun 2015 09:26:38 +0300 Subject: [PATCH 072/444] Incorrect instance variable for CI being referenced --- application/libraries/Format.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/application/libraries/Format.php b/application/libraries/Format.php index 144b6ee5..404a5360 100644 --- a/application/libraries/Format.php +++ b/application/libraries/Format.php @@ -42,10 +42,10 @@ class Format { public function __construct($data = NULL, $from_type = NULL) { // Get the CodeIgniter reference - $ci = &get_instance(); + $_ci = &get_instance(); // Load the inflector helper - $ci->load->helper('inflector'); + $_ci->load->helper('inflector'); // If the provided data is already formatted we should probably convert it to an array if ($from_type !== NULL) @@ -203,16 +203,16 @@ public function to_html() } // Load the table library - $ci->load->library('table'); + $_ci->load->library('table'); - $ci->table->set_heading($headings); + $_ci->table->set_heading($headings); foreach ($data as &$row) { - $ci->table->add_row($row); + $_ci->table->add_row($row); } - return $ci->table->generate(); + return $_ci->table->generate(); } /** @@ -256,7 +256,7 @@ public function to_csv() public function to_json() { // Get the callback parameter (if set) - $callback = $ci->input->get('callback'); + $callback = $_ci->input->get('callback'); if (empty($callback) === TRUE) { From ef2b55bbaf2399fc681624067db5b35aa33bac0a Mon Sep 17 00:00:00 2001 From: softwarespot Date: Mon, 29 Jun 2015 09:29:37 +0300 Subject: [PATCH 073/444] Added comment and using single quotes where appropriate --- application/libraries/Format.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/application/libraries/Format.php b/application/libraries/Format.php index 404a5360..c9bcfae7 100644 --- a/application/libraries/Format.php +++ b/application/libraries/Format.php @@ -170,7 +170,7 @@ public function to_xml($data = NULL, $structure = NULL, $basenode = 'xml') else { // add single node. - $value = htmlspecialchars(html_entity_decode($value, ENT_QUOTES, 'UTF-8'), ENT_QUOTES, "UTF-8"); + $value = htmlspecialchars(html_entity_decode($value, ENT_QUOTES, 'UTF-8'), ENT_QUOTES, 'UTF-8'); $structure->addChild($key, $value); } @@ -206,7 +206,8 @@ public function to_html() $_ci->load->library('table'); $_ci->table->set_heading($headings); - + + // Should row used as a reference? foreach ($data as &$row) { $_ci->table->add_row($row); @@ -267,7 +268,7 @@ public function to_json() elseif (preg_match('/^[a-z_\$][a-z0-9\$_]*(\.[a-z_\$][a-z0-9\$_]*)*$/i', $callback)) { // Set the content type - header("Content-Type: application/javascript"); + header('Content-Type: application/javascript'); // Return the data as encoded json with a callback return $callback . '(' . json_encode($this->_data) . ');'; From a4313904bfd427ba2849b7bb96f3144998f9c294 Mon Sep 17 00:00:00 2001 From: Ivan Tcholakov Date: Mon, 29 Jun 2015 15:21:53 +0300 Subject: [PATCH 074/444] Polishing about #477. --- application/libraries/Format.php | 42 ++++++++++++----------- application/libraries/REST_Controller.php | 2 +- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/application/libraries/Format.php b/application/libraries/Format.php index c9bcfae7..5c96e9f3 100644 --- a/application/libraries/Format.php +++ b/application/libraries/Format.php @@ -1,5 +1,7 @@ _ci = &get_instance(); // Load the inflector helper - $_ci->load->helper('inflector'); + $this->_ci->load->helper('inflector'); // If the provided data is already formatted we should probably convert it to an array if ($from_type !== NULL) @@ -84,8 +86,8 @@ public function factory($data, $from_type = NULL) /** * Encode as JSON - * - * @return string JSON representation of a value + * + * @return string JSON representation of a value */ public function to_array() { @@ -203,17 +205,17 @@ public function to_html() } // Load the table library - $_ci->load->library('table'); + $this->_ci->load->library('table'); + + $this->_ci->table->set_heading($headings); - $_ci->table->set_heading($headings); - // Should row used as a reference? foreach ($data as &$row) { - $_ci->table->add_row($row); + $this->_ci->table->add_row($row); } - return $_ci->table->generate(); + return $this->_ci->table->generate(); } /** @@ -252,13 +254,13 @@ public function to_csv() /** * Encode as JSON * - * @return string JSON representation of a value + * @return string JSON representation of a value */ public function to_json() { // Get the callback parameter (if set) - $callback = $_ci->input->get('callback'); - + $callback = $this->_ci->input->get('callback'); + if (empty($callback) === TRUE) { return json_encode($this->_data, JSON_PRETTY_PRINT); diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index a43bd09a..5677d310 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -1368,7 +1368,7 @@ protected function _perform_ldap_auth($username = '', $password = NULL) log_message('debug', 'LDAP Auth: Connect to ' . (isset($ldaphost) ? $ldaphost : '[ldap not configured]')); - // Appears to be unused + // Appears to be unused $ldapconfig['authrealm'] = $this->config->item('domain', 'ldap'); // connect to ldap server From 1113f7b991afd140d5279347c4c4cdc7b6e9ed02 Mon Sep 17 00:00:00 2001 From: Ivan Tcholakov Date: Mon, 29 Jun 2015 15:49:52 +0300 Subject: [PATCH 075/444] Polishing (2) about #477. --- 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 5c96e9f3..a8f24218 100644 --- a/application/libraries/Format.php +++ b/application/libraries/Format.php @@ -226,7 +226,7 @@ public function to_html() public function to_csv() { // Cast as an array if no already - is_array($this->_data) || $data = (array) $this->_data; + $data = is_array($this->_data) ? $this->_data : (array) $this->_data; // Multi-dimensional array if (isset($data[0]) && is_array($data[0])) From 1280df70a85e6c601f59a1e0be6476f2515cb9af Mon Sep 17 00:00:00 2001 From: softwarespot Date: Mon, 29 Jun 2015 16:21:32 +0300 Subject: [PATCH 076/444] Fixed unused variable and error in comment --- application/libraries/Format.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/application/libraries/Format.php b/application/libraries/Format.php index a8f24218..b5435968 100644 --- a/application/libraries/Format.php +++ b/application/libraries/Format.php @@ -62,6 +62,7 @@ public function __construct($data = NULL, $from_type = NULL) } } + // Set the member variable to the data passed $this->_data = $data; } @@ -188,8 +189,8 @@ public function to_xml($data = NULL, $structure = NULL, $basenode = 'xml') */ public function to_html() { - // Cast as an array if no already - is_array($this->_data) || $data = (array) $this->_data; + // Cast as an array if not already + $data = is_array($this->_data) ? $this->_data : (array) $this->_data; // Multi-dimensional array if (isset($data[0]) && is_array($data[0])) @@ -225,7 +226,7 @@ public function to_html() */ public function to_csv() { - // Cast as an array if no already + // Cast as an array if not already $data = is_array($this->_data) ? $this->_data : (array) $this->_data; // Multi-dimensional array From 98d477f76c6c989642c75f89a9cf7f0875ae5044 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Tue, 30 Jun 2015 13:01:57 +0300 Subject: [PATCH 077/444] Fixed typo in comment --- 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 5677d310..6d351eb2 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -252,7 +252,7 @@ public function __construct($config = 'rest') $this->_check_blacklist_auth(); } - // Store whether the the connection is HTTPS + // Determine whether the connection is HTTPS $this->request->ssl = is_https(); // How is this request being made? GET, POST, PATCH, DELETE, INSERT, PUT, HEAD or OPTIONS From bd7b34a6f84e30b897dcc05ac3a400ca8e0aa3b4 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Tue, 30 Jun 2015 13:05:28 +0300 Subject: [PATCH 078/444] Changed comment summary --- 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 6d351eb2..72c2cb09 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -270,7 +270,7 @@ public function __construct($config = 'rest') // Try to find a format for the request (means we have a request body) $this->request->format = $this->_detect_input_format(); - // Some method can't have a body + // Not all methods have a body attached with them $this->request->body = NULL; $this->{'_parse_' . $this->request->method}(); From a9c97f8568a92eff20776004d609ad0d38b9a479 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Tue, 30 Jun 2015 18:41:28 +0300 Subject: [PATCH 079/444] Updated author and license --- 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 b5435968..4c9bc310 100644 --- a/application/libraries/Format.php +++ b/application/libraries/Format.php @@ -6,8 +6,8 @@ * Format class * Help convert between various formats such as XML, JSON, CSV, etc. * - * @author Phil Sturgeon - * @license http://philsturgeon.co.uk/code/dbad-license + * @author Phil Sturgeon, Chris Kacerguis + * @license http://www.dbad-license.org/ */ class Format { From e67154895c69a700d1a4168c7fdf9ed03d791343 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Tue, 30 Jun 2015 18:43:28 +0300 Subject: [PATCH 080/444] Using new static() and updated doc comment --- application/libraries/Format.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/application/libraries/Format.php b/application/libraries/Format.php index 4c9bc310..ead482d6 100644 --- a/application/libraries/Format.php +++ b/application/libraries/Format.php @@ -19,9 +19,9 @@ class Format { private $_ci; /** - * Array to convert + * Data to parse * - * @var array + * @var mixed */ protected $_data = []; @@ -77,10 +77,10 @@ public function __construct($data = NULL, $from_type = NULL) */ public function factory($data, $from_type = NULL) { - // Stupid stuff to emulate the "new static()" stuff in this libraries PHP 5.3 equivalent - $class = __CLASS__; + // $class = __CLASS__; + // return new $class(); - return new $class($data, $from_type); + return new static($data, $from_type); } // FORMATING OUTPUT --------------------------------------------------------- From a5225e073f1d623e142cf8c84e493f652fc620a5 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Tue, 30 Jun 2015 19:13:37 +0300 Subject: [PATCH 081/444] Unified all public functions, by having an optional param This is similar to how to_array() used to be --- application/libraries/Format.php | 162 +++++++++++++++++++++---------- 1 file changed, 113 insertions(+), 49 deletions(-) diff --git a/application/libraries/Format.php b/application/libraries/Format.php index ead482d6..8d18637b 100644 --- a/application/libraries/Format.php +++ b/application/libraries/Format.php @@ -67,13 +67,13 @@ public function __construct($data = NULL, $from_type = NULL) } /** - * Returns an instance of the Format class - * e.g: echo $this->format->factory(array('foo' => 'bar'))->to_xml(); + * Create an instance of the format class + * e.g: echo $this->format->factory(['foo' => 'bar'])->to_csv(); * - * @param $data - * @param null $from_type + * @param mixed $data Data to convert/parse + * @param string $from_type Type to convert from e.g. json, csv, html * - * @return mixed + * @return object Instance of the format class */ public function factory($data, $from_type = NULL) { @@ -83,12 +83,12 @@ public function factory($data, $from_type = NULL) return new static($data, $from_type); } - // FORMATING OUTPUT --------------------------------------------------------- + // FORMATTING OUTPUT --------------------------------------------------------- /** - * Encode as JSON + * Encode as json * - * @return string JSON representation of a value + * @return string Json representation of a value */ public function to_array() { @@ -100,7 +100,8 @@ public function to_array() /** * Format XML for output * - * @param null $data + * @param array|null $data Optional data to pass, so as to override the data passed + * to the constructor * @param null $structure * @param string $basenode * @@ -108,7 +109,7 @@ public function to_array() */ public function to_xml($data = NULL, $structure = NULL, $basenode = 'xml') { - if ($data === NULL && !func_num_args()) + if ($data === NULL && func_num_args() === 0) { $data = $this->_data; } @@ -185,12 +186,25 @@ public function to_xml($data = NULL, $structure = NULL, $basenode = 'xml') /** * Format HTML for output * + * @param mixed|null $data Optional data to pass, so as to override the data passed + * to the constructor + * * @return mixed */ - public function to_html() + public function to_html($data = NULL) { + // If no data is passed as a parameter, then use the data passed + // via the constructor + if ($data === NULL && func_num_args() === 0) + { + $data = $this->_data; + } + // Cast as an array if not already - $data = is_array($this->_data) ? $this->_data : (array) $this->_data; + if (is_array($data) === FALSE) + { + $data = (array) $data; + } // Multi-dimensional array if (isset($data[0]) && is_array($data[0])) @@ -222,12 +236,25 @@ public function to_html() /** * Format CSV for output * + * @param mixed|null $data Optional data to pass, so as to override the data passed + * to the constructor + * * @return mixed */ - public function to_csv() + public function to_csv($data = NULL) { + // If no data is passed as a parameter, then use the data passed + // via the constructor + if ($data === NULL && func_num_args() === 0) + { + $data = $this->_data; + } + // Cast as an array if not already - $data = is_array($this->_data) ? $this->_data : (array) $this->_data; + if (is_array($data) === FALSE) + { + $data = (array) $data; + } // Multi-dimensional array if (isset($data[0]) && is_array($data[0])) @@ -253,18 +280,28 @@ public function to_csv() } /** - * Encode as JSON + * Encode as json + * + * @param mixed|null $data Optional data to pass, so as to override the data passed + * to the constructor * - * @return string JSON representation of a value + * @return string Json representation of a value */ - public function to_json() + public function to_json($data = NULL) { + // If no data is passed as a parameter, then use the data passed + // via the constructor + if ($data === NULL && func_num_args() === 0) + { + $data = $this->_data; + } + // Get the callback parameter (if set) $callback = $this->_ci->input->get('callback'); if (empty($callback) === TRUE) { - return json_encode($this->_data, JSON_PRETTY_PRINT); + return json_encode($data, JSON_PRETTY_PRINT); } // We only honour a jsonp callback which are valid javascript identifiers @@ -274,96 +311,123 @@ public function to_json() header('Content-Type: application/javascript'); // Return the data as encoded json with a callback - return $callback . '(' . json_encode($this->_data) . ');'; + return $callback . '(' . json_encode($data) . ');'; } // An invalid jsonp callback function provided. // Though I don't believe this should be hardcoded here - $this->_data['warning'] = 'INVALID JSONP CALLBACK: ' . $callback; + $data['warning'] = 'INVALID JSONP CALLBACK: ' . $callback; - return json_encode($this->_data); + return json_encode($data); } /** * Encode as a serialized array + * + * @param mixed|null $data Optional data to pass, so as to override the data passed + * to the constructor + * + * @return string Serialized data */ - public function to_serialized() + public function to_serialized($data = NULL) { - return serialize($this->_data); + // If no data is passed as a parameter, then use the data passed + // via the constructor + if ($data === NULL && func_num_args() === 0) + { + $data = $this->_data; + } + + return serialize($data); } /** * Output as a string representing the PHP structure + * + * @param mixed|null $data Optional data to pass, so as to override the data passed + * to the constructor + * + * @return mixed String representation of a variable */ - public function to_php() + public function to_php($data = NULL) { - return var_export($this->_data, TRUE); + // If no data is passed as a parameter, then use the data passed + // via the constructor + if ($data === NULL && func_num_args() === 0) + { + $data = $this->_data; + } + + return var_export($data, TRUE); } + // INTERNAL FUNCTIONS + /** - * @param $string + * @param $data XML string * - * @return array + * @return SimpleXMLElement XML element object; otherwise, empty array */ - protected function _from_xml($string) + protected function _from_xml($data) { - return $string ? (array) simplexml_load_string($string, 'SimpleXMLElement', LIBXML_NOCDATA) : []; + return $data ? (array) simplexml_load_string($data, 'SimpleXMLElement', LIBXML_NOCDATA) : []; } /** - * @param $string + * @param string $data CSV string * - * @return array + * @return array A multi-dimensional array with the outer array being the number of rows + * and the inner arrays the individual fields */ - protected function _from_csv($string) + protected function _from_csv($data) { - $data = []; + $array = []; // Splits - $rows = explode("\n", trim($string)); + $rows = explode("\n", trim($data)); $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)) + if (count($data_fields) === count($headings)) { - $data[] = array_combine($headings, $data_fields); + $array[] = array_combine($headings, $data_fields); } } - return $data; + return $array; } /** - * @param $string + * @param $data Encoded json string * - * @return mixed + * @return mixed Decoded json string with leading and trailing whitespace removed */ - private function _from_json($string) + protected function _from_json($data) { - return json_decode(trim($string)); + return json_decode(trim($data)); } /** - * @param $string + * @param string Data to unserialized * - * @return mixed + * @return mixed Unserialized data */ - private function _from_serialize($string) + protected function _from_serialize($data) { - return unserialize(trim($string)); + return unserialize(trim($data)); } /** - * @param $string + * @param $data Data to trim leading and trailing whitespace * - * @return string + * @return string Data with leading and trailing whitespace removed */ - private function _from_php($string) + protected function _from_php($data) { - return trim($string); + return trim($data); } } From 34318a8e43f3adb0390029f30d76ee25789c5b8d Mon Sep 17 00:00:00 2001 From: softwarespot Date: Tue, 30 Jun 2015 19:15:37 +0300 Subject: [PATCH 082/444] Removed setting content header This has no business being here, as this is the role of the REST_Controller --- application/libraries/Format.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/application/libraries/Format.php b/application/libraries/Format.php index 8d18637b..e270aed5 100644 --- a/application/libraries/Format.php +++ b/application/libraries/Format.php @@ -307,9 +307,6 @@ public function to_json($data = NULL) // We only honour a jsonp callback which are valid javascript identifiers elseif (preg_match('/^[a-z_\$][a-z0-9\$_]*(\.[a-z_\$][a-z0-9\$_]*)*$/i', $callback)) { - // Set the content type - header('Content-Type: application/javascript'); - // Return the data as encoded json with a callback return $callback . '(' . json_encode($data) . ');'; } From 0bb71c7a503009f565964173a2711f380e774ca7 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Tue, 30 Jun 2015 19:20:55 +0300 Subject: [PATCH 083/444] Re-implemented to_array() --- application/libraries/Format.php | 41 ++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/application/libraries/Format.php b/application/libraries/Format.php index e270aed5..4717de51 100644 --- a/application/libraries/Format.php +++ b/application/libraries/Format.php @@ -86,21 +86,48 @@ public function factory($data, $from_type = NULL) // FORMATTING OUTPUT --------------------------------------------------------- /** - * Encode as json + * Parse as an array * - * @return string Json representation of a value + * @param mixed|null $data Optional data to pass, so as to override the data passed + * to the constructor + * + * @return array Data parsed as an array; otherwise, an empty array */ - public function to_array() + public function to_array($data = NULL) { - // As the return value should be a string, it makes no sense - // to return an array datatype as that will result in an error or sorts - return $this->to_json(); + // If no data is passed as a parameter, then use the data passed + // via the constructor + if ($data === NULL && func_num_args() === 0) + { + $data = $this->_data; + } + + // Cast as an array if not already + if (is_array($data) === FALSE) + { + $data = (array) $data; + } + + $array = []; + foreach ((array) $data as $key => $value) + { + if (is_object($value) || is_array($value)) + { + $array[$key] = $this->_to_array($value); + } + else + { + $array[$key] = $value; + } + } + + return $array; } /** * Format XML for output * - * @param array|null $data Optional data to pass, so as to override the data passed + * @param mixed|null $data Optional data to pass, so as to override the data passed * to the constructor * @param null $structure * @param string $basenode From 8a6d6bc73b339ba67232cddb856e7cf26659290a Mon Sep 17 00:00:00 2001 From: softwarespot Date: Tue, 30 Jun 2015 19:21:53 +0300 Subject: [PATCH 084/444] Fixed case of NULL --- application/libraries/Format.php | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/application/libraries/Format.php b/application/libraries/Format.php index 4717de51..1dacaf48 100644 --- a/application/libraries/Format.php +++ b/application/libraries/Format.php @@ -35,8 +35,8 @@ class Format { /** * DO NOT CALL THIS DIRECTLY, USE factory() * - * @param null $data - * @param null $from_type + * @param NULL $data + * @param NULL $from_type * * @throws Exception */ @@ -88,7 +88,7 @@ public function factory($data, $from_type = NULL) /** * Parse as an array * - * @param mixed|null $data Optional data to pass, so as to override the data passed + * @param mixed|NULL $data Optional data to pass, so as to override the data passed * to the constructor * * @return array Data parsed as an array; otherwise, an empty array @@ -127,9 +127,9 @@ public function to_array($data = NULL) /** * Format XML for output * - * @param mixed|null $data Optional data to pass, so as to override the data passed + * @param mixed|NULL $data Optional data to pass, so as to override the data passed * to the constructor - * @param null $structure + * @param NULL $structure * @param string $basenode * * @return mixed @@ -213,7 +213,7 @@ public function to_xml($data = NULL, $structure = NULL, $basenode = 'xml') /** * Format HTML for output * - * @param mixed|null $data Optional data to pass, so as to override the data passed + * @param mixed|NULL $data Optional data to pass, so as to override the data passed * to the constructor * * @return mixed @@ -263,7 +263,7 @@ public function to_html($data = NULL) /** * Format CSV for output * - * @param mixed|null $data Optional data to pass, so as to override the data passed + * @param mixed|NULL $data Optional data to pass, so as to override the data passed * to the constructor * * @return mixed @@ -309,7 +309,7 @@ public function to_csv($data = NULL) /** * Encode as json * - * @param mixed|null $data Optional data to pass, so as to override the data passed + * @param mixed|NULL $data Optional data to pass, so as to override the data passed * to the constructor * * @return string Json representation of a value @@ -348,7 +348,7 @@ public function to_json($data = NULL) /** * Encode as a serialized array * - * @param mixed|null $data Optional data to pass, so as to override the data passed + * @param mixed|NULL $data Optional data to pass, so as to override the data passed * to the constructor * * @return string Serialized data @@ -368,7 +368,7 @@ public function to_serialized($data = NULL) /** * Output as a string representing the PHP structure * - * @param mixed|null $data Optional data to pass, so as to override the data passed + * @param mixed|NULL $data Optional data to pass, so as to override the data passed * to the constructor * * @return mixed String representation of a variable From 3e626abb317289c4ff2c29548116f487af236d2f Mon Sep 17 00:00:00 2001 From: softwarespot Date: Tue, 30 Jun 2015 19:25:17 +0300 Subject: [PATCH 085/444] Unified comments --- application/libraries/Format.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/application/libraries/Format.php b/application/libraries/Format.php index 1dacaf48..e4b555a0 100644 --- a/application/libraries/Format.php +++ b/application/libraries/Format.php @@ -86,7 +86,7 @@ public function factory($data, $from_type = NULL) // FORMATTING OUTPUT --------------------------------------------------------- /** - * Parse as an array + * Format data as an array * * @param mixed|NULL $data Optional data to pass, so as to override the data passed * to the constructor @@ -125,7 +125,7 @@ public function to_array($data = NULL) } /** - * Format XML for output + * Format data as XML * * @param mixed|NULL $data Optional data to pass, so as to override the data passed * to the constructor @@ -211,7 +211,7 @@ public function to_xml($data = NULL, $structure = NULL, $basenode = 'xml') } /** - * Format HTML for output + * Format data as HTML * * @param mixed|NULL $data Optional data to pass, so as to override the data passed * to the constructor @@ -261,7 +261,7 @@ public function to_html($data = NULL) } /** - * Format CSV for output + * Format data as CSV * * @param mixed|NULL $data Optional data to pass, so as to override the data passed * to the constructor @@ -307,7 +307,7 @@ public function to_csv($data = NULL) } /** - * Encode as json + * Encode data as json * * @param mixed|NULL $data Optional data to pass, so as to override the data passed * to the constructor @@ -346,7 +346,7 @@ public function to_json($data = NULL) } /** - * Encode as a serialized array + * Encode data as a serialized array * * @param mixed|NULL $data Optional data to pass, so as to override the data passed * to the constructor @@ -366,7 +366,7 @@ public function to_serialized($data = NULL) } /** - * Output as a string representing the PHP structure + * Format data using a PHP structure * * @param mixed|NULL $data Optional data to pass, so as to override the data passed * to the constructor From 635663b4587690691daaa562049d058f17b56612 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Tue, 30 Jun 2015 19:37:24 +0300 Subject: [PATCH 086/444] Fixes #483 This is also in reference to #445, in which the workaround was first applied --- application/libraries/REST_Controller.php | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 72c2cb09..5d84e700 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -162,15 +162,15 @@ abstract class REST_Controller extends CI_Controller { * * @var array */ - protected $_supported_formats - = [ + protected $_supported_formats = [ 'json' => 'application/json', - 'xml' => 'application/xml', + 'array' => 'application/json', + 'csv' => 'application/csv', + 'html' => 'text/html', 'jsonp' => 'application/javascript', - 'serialized' => 'application/vnd.php.serialized', 'php' => 'text/plain', - 'html' => 'text/html', - 'csv' => 'application/csv' + 'serialized' => 'application/vnd.php.serialized', + 'xml' => 'application/xml' ]; /** @@ -544,6 +544,13 @@ public function response($data = NULL, $http_code = NULL, $continue = FALSE) . '; charset=' . strtolower($this->config->item('charset'))); $output = $this->format->factory($data)->{'to_' . $this->response->format}(); + + // An array must be parsed as a string, so as not to cause an array to string error. + // Json is the most appropriate form for such a datatype + if ($this->response->format === 'array') + { + $output = $this->format->factory($output)->{'to_json'}(); + } } else { From 732b47b6897f8951f8906b47fa93408392797662 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Tue, 30 Jun 2015 19:41:10 +0300 Subject: [PATCH 087/444] Removed JSON_PRETTY_PRINT --- 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 e4b555a0..b61e5adb 100644 --- a/application/libraries/Format.php +++ b/application/libraries/Format.php @@ -328,7 +328,7 @@ public function to_json($data = NULL) if (empty($callback) === TRUE) { - return json_encode($data, JSON_PRETTY_PRINT); + return json_encode($data); } // We only honour a jsonp callback which are valid javascript identifiers From 7f5a9a4e50ce55eec1671eb487587952fe9f4bb6 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Tue, 30 Jun 2015 19:49:07 +0300 Subject: [PATCH 088/444] Closes #478 Instead of using preg_match(), simply strip from the end of the string --- application/libraries/REST_Controller.php | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 5d84e700..c5cee51f 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -397,12 +397,8 @@ public function _remap($object_called, $arguments) $this->response([config_item('rest_status_field_name') => FALSE, config_item('rest_message_field_name') => 'Unsupported protocol'], 403); } - $pattern = '/^(.*)\.(' . implode('|', array_keys($this->_supported_formats)) . ')$/'; - $matches = []; - if (preg_match($pattern, $object_called, $matches)) - { - $object_called = $matches[1]; - } + // Remove the supported format from the function name e.g. index.json => index + $object_called = preg_replace('/^(.*)\.(?:' . implode('|', array_keys($this->_supported_formats)) . ')$/', '$1', $object_called); $controller_method = $object_called . '_' . $this->request->method; From a50723ace012cad5914fc8a17854112d010ede5c Mon Sep 17 00:00:00 2001 From: softwarespot Date: Tue, 30 Jun 2015 19:58:06 +0300 Subject: [PATCH 089/444] Re-worked _detect_method() as suggest in #482 --- application/libraries/REST_Controller.php | 29 +++++++++++++---------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index c5cee51f..64e2cf04 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -704,26 +704,31 @@ protected function _detect_output_format() } /** - * Get the HTTP request string + * Get the HTTP request string e.g. get or post * - * @access protected - * @return string Request method as a lowercase string + * @return string|NULL Supported request method as a lowercase string; otherwise, NULL if not supported */ protected function _detect_method() { - // Get the request method as a lowercase string - $method = $this->input->method(); + // Declare a variable to store the method + $method = NULL; - if ($this->config->item('enable_emulate_request')) + // Determine whether the 'enable_emulate_request' setting is enabled + if ($this->config->item('enable_emulate_request') === TRUE) { - if ($this->input->post('_method')) - { - $method = strtolower($this->input->post('_method')); - } - elseif ($this->input->server('HTTP_X_HTTP_METHOD_OVERRIDE')) + $method = $this->input->post('_method'); + if ($method === NULL) { - $method = strtolower($this->input->server('HTTP_X_HTTP_METHOD_OVERRIDE')); + $method = $this->input->server('HTTP_X_HTTP_METHOD_OVERRIDE'); } + + $method = strtolower($method); + } + + if (empty($method)) + { + // Get the request method as a lowercase string. + $method = $this->input->method(); } return in_array($method, $this->allowed_http_methods) && method_exists($this, '_parse_' . $method) ? $method : 'get'; From 072cd25a66c10f67e5425af50eb1b9bd202d1623 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Wed, 1 Jul 2015 08:37:17 +0300 Subject: [PATCH 090/444] Fixed left over underscore from testing function --- 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 b61e5adb..210e2f5b 100644 --- a/application/libraries/Format.php +++ b/application/libraries/Format.php @@ -113,7 +113,7 @@ public function to_array($data = NULL) { if (is_object($value) || is_array($value)) { - $array[$key] = $this->_to_array($value); + $array[$key] = $this->to_array($value); } else { From 1406528428ff354f1fc48ca617eb163dda519166 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Thu, 2 Jul 2015 21:24:22 +0300 Subject: [PATCH 091/444] Replace config_item() with $this->config->item() --- application/libraries/REST_Controller.php | 104 +++++++++++----------- 1 file changed, 52 insertions(+), 52 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 64e2cf04..0e2d0406 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -228,7 +228,7 @@ public function __construct($config = 'rest') } // Set the default value of global xss filtering. Same approach as CodeIgniter 3 - $this->_enable_xss = (config_item('global_xss_filtering') === TRUE); + $this->_enable_xss = ($this->config->item('global_xss_filtering') === TRUE); // Start the timer for how long the request takes $this->_start_rtime = microtime(TRUE); @@ -305,9 +305,9 @@ public function __construct($config = 'rest') $this->early_checks(); // Load DB if its enabled - if (config_item('rest_database_group') && (config_item('rest_enable_keys') || config_item('rest_enable_logging'))) + if ($this->config->item('rest_database_group') && ($this->config->item('rest_enable_keys') || $this->config->item('rest_enable_logging'))) { - $this->rest->db = $this->load->database(config_item('rest_database_group'), TRUE); + $this->rest->db = $this->load->database($this->config->item('rest_database_group'), TRUE); } // Use whatever database is in use (isset returns FALSE) @@ -322,24 +322,24 @@ public function __construct($config = 'rest') // Checking for keys? GET TO WorK! // Skip keys test for $config['auth_override_class_method']['class'['method'] = 'none' - if (config_item('rest_enable_keys') && $this->auth_override !== TRUE) + if ($this->config->item('rest_enable_keys') && $this->auth_override !== TRUE) { $this->_allow = $this->_detect_api_key(); } // Only allow ajax requests - if ($this->input->is_ajax_request() === FALSE && config_item('rest_ajax_only')) + if ($this->input->is_ajax_request() === FALSE && $this->config->item('rest_ajax_only')) { // Display an error response $this->response( [ - config_item('rest_status_field_name') => FALSE, - config_item('rest_message_field_name') => 'Only AJAX requests are acceptable' + $this->config->item('rest_status_field_name') => FALSE, + $this->config->item('rest_message_field_name') => 'Only AJAX requests are acceptable' ], 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 - if ($this->auth_override === FALSE && !(config_item('rest_enable_keys') && $this->_allow === TRUE)) + if ($this->auth_override === FALSE && !($this->config->item('rest_enable_keys') && $this->_allow === TRUE)) { $rest_auth = strtolower($this->config->item('rest_auth')); switch ($rest_auth) @@ -373,7 +373,7 @@ public function __destruct() $this->_end_rtime = microtime(TRUE); // Log the loading time to the log table - if (config_item('rest_enable_logging') === TRUE) + if ($this->config->item('rest_enable_logging') === TRUE) { $this->_log_access_time(); } @@ -392,9 +392,9 @@ public function __destruct() public function _remap($object_called, $arguments) { // Should we answer if not over SSL? - if (config_item('force_https') && $this->request->ssl === FALSE) + if ($this->config->item('force_https') && $this->request->ssl === FALSE) { - $this->response([config_item('rest_status_field_name') => FALSE, config_item('rest_message_field_name') => 'Unsupported protocol'], 403); + $this->response([$this->config->item('rest_status_field_name') => FALSE, $this->config->item('rest_message_field_name') => 'Unsupported protocol'], 403); } // Remove the supported format from the function name e.g. index.json => index @@ -409,40 +409,40 @@ public function _remap($object_called, $arguments) $use_key = !(isset($this->methods[$controller_method]['key']) && $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') && $use_key && $this->_allow === FALSE) + if ($this->config->item('rest_enable_keys') && $use_key && $this->_allow === FALSE) { - if (config_item('rest_enable_logging') && $log_method) + if ($this->config->item('rest_enable_logging') && $log_method) { $this->_log_request(); } - $this->response([config_item('rest_status_field_name') => FALSE, config_item('rest_message_field_name') => 'Invalid API Key ' . $this->rest->key], 403); + $this->response([$this->config->item('rest_status_field_name') => FALSE, $this->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. - if (config_item('rest_enable_keys') && $use_key && !empty($this->rest->key) && !$this->_check_access()) + if ($this->config->item('rest_enable_keys') && $use_key && !empty($this->rest->key) && !$this->_check_access()) { - if (config_item('rest_enable_logging') && $log_method) + if ($this->config->item('rest_enable_logging') && $log_method) { $this->_log_request(); } - $this->response([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); + $this->response([$this->config->item('rest_status_field_name') => FALSE, $this->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([config_item('rest_status_field_name') => FALSE, config_item('rest_message_field_name') => 'Unknown method.'], 404); + $this->response([$this->config->item('rest_status_field_name') => FALSE, $this->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') && !empty($this->rest->key)) + if ($this->config->item('rest_enable_keys') && !empty($this->rest->key)) { // Check the limit - if (config_item('rest_enable_limits') && !$this->_check_limit($controller_method)) + if ($this->config->item('rest_enable_limits') && !$this->_check_limit($controller_method)) { - $response = [config_item('rest_status_field_name') => FALSE, config_item('rest_message_field_name') => 'This API key has reached the hourly limit for this method.']; + $response = [$this->config->item('rest_status_field_name') => FALSE, $this->config->item('rest_message_field_name') => 'This API key has reached the hourly limit for this method.']; $this->response($response, 401); } @@ -453,18 +453,18 @@ public function _remap($object_called, $arguments) $authorized = $level <= $this->rest->level; // IM TELLIN! - if (config_item('rest_enable_logging') && $log_method) + if ($this->config->item('rest_enable_logging') && $log_method) { $this->_log_request($authorized); } // They don't have good enough perms - $response = [config_item('rest_status_field_name') => FALSE, config_item('rest_message_field_name') => 'This API key does not have enough permissions.']; + $response = [$this->config->item('rest_status_field_name') => FALSE, $this->config->item('rest_message_field_name') => 'This API key does not have enough permissions.']; $authorized || $this->response($response, 401); } // No key stuff, but record that stuff is happening - elseif (config_item('rest_enable_logging') && $log_method) + elseif ($this->config->item('rest_enable_logging') && $log_method) { $this->_log_request($authorized = TRUE); } @@ -479,8 +479,8 @@ public function _remap($object_called, $arguments) // If the method doesn't exist, then the error will be caught and an error response shown $this->response( [ - config_item('rest_status_field_name') => FALSE, - config_item('rest_message_field_name') => [ + $this->config->item('rest_status_field_name') => FALSE, + $this->config->item('rest_message_field_name') => [ 'classname' => get_class($ex), 'message' => $ex->getMessage() ] @@ -569,7 +569,7 @@ public function response($data = NULL, $http_code = NULL, $continue = FALSE) set_status_header($http_code); // JC: Log response code only if rest logging enabled - if (config_item('rest_enable_logging') === TRUE) + if ($this->config->item('rest_enable_logging') === TRUE) { $this->_log_response_code($http_code); } @@ -700,7 +700,7 @@ protected function _detect_output_format() } // Just use the default format - return config_item('rest_default_format'); + return $this->config->item('rest_default_format'); } /** @@ -743,7 +743,7 @@ 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'); + $api_key_variable = $this->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)); @@ -756,12 +756,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 (!($row = $this->rest->db->where(config_item('rest_key_column'), $key)->get(config_item('rest_keys_table'))->row())) + if (!($row = $this->rest->db->where($this->config->item('rest_key_column'), $key)->get($this->config->item('rest_keys_table'))->row())) { return FALSE; } - $this->rest->key = $row->{config_item('rest_key_column')}; + $this->rest->key = $row->{$this->config->item('rest_key_column')}; isset($row->user_id) && $this->rest->user_id = $row->user_id; isset($row->level) && $this->rest->level = $row->level; @@ -855,10 +855,10 @@ protected function _log_request($authorized = FALSE) // Insert the request into the log table $isInserted = $this->rest->db ->insert( - config_item('rest_logs_table'), [ + $this->config->item('rest_logs_table'), [ 'uri' => $this->uri->uri_string(), 'method' => $this->request->method, - 'params' => $this->_args ? (config_item('rest_logs_json_params') === TRUE ? json_encode($this->_args) : serialize($this->_args)) : NULL, + 'params' => $this->_args ? ($this->config->item('rest_logs_json_params') === TRUE ? json_encode($this->_args) : serialize($this->_args)) : NULL, 'api_key' => isset($this->rest->key) ? $this->rest->key : '', 'ip_address' => $this->input->ip_address(), 'time' => now(), // Used to be: function_exists('now') ? now() : time() @@ -896,14 +896,14 @@ protected function _check_limit($controller_method) $result = $this->rest->db ->where('uri', $this->uri->uri_string()) ->where('api_key', $this->rest->key) - ->get(config_item('rest_limits_table')) + ->get($this->config->item('rest_limits_table')) ->row(); // No calls have been made for this key if (!$result) { // Create a new row for the following key - $this->rest->db->insert(config_item('rest_limits_table'), [ + $this->rest->db->insert($this->config->item('rest_limits_table'), [ 'uri' => $this->uri->uri_string(), 'api_key' => isset($this->rest->key) ? $this->rest->key : '', 'count' => 1, @@ -920,7 +920,7 @@ protected function _check_limit($controller_method) ->where('api_key', isset($this->rest->key) ? $this->rest->key : '') ->set('hour_started', time()) ->set('count', 1) - ->update(config_item('rest_limits_table')); + ->update($this->config->item('rest_limits_table')); } // They have called within the hour, so lets update @@ -937,7 +937,7 @@ protected function _check_limit($controller_method) ->where('uri', $this->uri->uri_string()) ->where('api_key', $this->rest->key) ->set('count', 'count + 1', FALSE) - ->update(config_item('rest_limits_table')); + ->update($this->config->item('rest_limits_table')); } return TRUE; @@ -1572,8 +1572,8 @@ protected function _check_php_session() // Display an error response $this->response( [ - config_item('rest_status_field_name') => FALSE, - config_item('rest_message_field_name') => 'Not Authorized' + $this->config->item('rest_status_field_name') => FALSE, + $this->config->item('rest_message_field_name') => 'Not Authorized' ], 401); } } @@ -1586,7 +1586,7 @@ protected function _check_php_session() 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')) + if ($this->config->item('rest_ip_whitelist_enabled')) { $this->_check_whitelist_auth(); } @@ -1626,7 +1626,7 @@ 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')) + if ($this->config->item('rest_ip_whitelist_enabled')) { $this->_check_whitelist_auth(); } @@ -1673,8 +1673,8 @@ protected function _prepare_digest_auth() // Display an error response $this->response( [ - config_item('rest_status_field_name') => 0, - config_item('rest_message_field_name') => 'Invalid credentials' + $this->config->item('rest_status_field_name') => 0, + $this->config->item('rest_message_field_name') => 'Invalid credentials' ], 401); } } @@ -1690,7 +1690,7 @@ protected function _check_blacklist_auth() $pattern = sprintf('/(?:,\s*|^)\Q%s\E(?=,\s*|$)/m', $this->input->ip_address()); // Returns 1, 0 or FALSE (on error only). Therefore implicitly convert 1 to TRUE - if (preg_match($pattern, config_item('rest_ip_blacklist'))) + if (preg_match($pattern, $this->config->item('rest_ip_blacklist'))) { // Display an error response $this->response( @@ -1709,7 +1709,7 @@ protected function _check_blacklist_auth() */ protected function _check_whitelist_auth() { - $whitelist = explode(',', config_item('rest_ip_whitelist')); + $whitelist = explode(',', $this->config->item('rest_ip_whitelist')); array_push($whitelist, '127.0.0.1', '0.0.0.0'); @@ -1720,7 +1720,7 @@ protected function _check_whitelist_auth() if (!in_array($this->input->ip_address(), $whitelist)) { - $this->response([config_item('rest_status_field_name') => FALSE, config_item('rest_message_field_name') => 'IP not authorized'], 401); + $this->response([$this->config->item('rest_status_field_name') => FALSE, $this->config->item('rest_message_field_name') => 'IP not authorized'], 401); } } @@ -1753,8 +1753,8 @@ protected function _force_login($nonce = '') // Display an error response $this->response( [ - config_item('rest_status_field_name') => FALSE, - config_item('rest_message_field_name') => 'Not authorized' + $this->config->item('rest_status_field_name') => FALSE, + $this->config->item('rest_message_field_name') => 'Not authorized' ], 401); } @@ -1772,7 +1772,7 @@ protected function _log_access_time() return $this->rest->db ->update( - config_item('rest_logs_table'), $payload, [ + $this->config->item('rest_logs_table'), $payload, [ 'id' => $this->_insert_id ]); } @@ -1792,7 +1792,7 @@ protected function _log_response_code($http_code) $payload['response_code'] = $http_code; return $this->rest->db->update( - config_item('rest_logs_table'), $payload, [ + $this->config->item('rest_logs_table'), $payload, [ 'id' => $this->_insert_id ]); } @@ -1806,7 +1806,7 @@ protected function _log_response_code($http_code) protected function _check_access() { // If we don't want to check access, just return TRUE - if (config_item('rest_enable_access') === FALSE) + if ($this->config->item('rest_enable_access') === FALSE) { return TRUE; } @@ -1825,7 +1825,7 @@ protected function _check_access() return $this->rest->db ->where('key', $this->rest->key) ->where('controller', $controller) - ->get(config_item('rest_access_table')) + ->get($this->config->item('rest_access_table')) ->num_rows() > 0; } From 9eac95ad454ba62ae78aa32859ed59c9a6571c68 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Thu, 2 Jul 2015 21:27:16 +0300 Subject: [PATCH 092/444] Updated comments for request, response and rest It outlines the properties i.e. fields of the objects --- application/libraries/REST_Controller.php | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 0e2d0406..ed1ad3e1 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -38,22 +38,27 @@ abstract class REST_Controller extends CI_Controller { protected $allowed_http_methods = ['get', 'delete', 'post', 'put', 'options', 'patch', 'head']; /** - * General request data and information. - * Stores accept, language, body, headers, etc. + * Contains details about the request + * Fields: body, format, method, ssl + * Note: This is a dynamic object (stdClass) * * @var object */ protected $request = NULL; - /** - * What is gonna happen in output? + /** + * Contains details about the response + * Fields: format, lang + * Note: This is a dynamic object (stdClass) * * @var object */ protected $response = NULL; /** - * Stores DB, keys, key level, etc + * Contains details about the REST API + * Fields: db, ignore_limits, key, level, user_id + * Note: This is a dynamic object (stdClass) * * @var object */ From 9d89a904087ce00dc844a1ae3cb982264ff598c6 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Thu, 2 Jul 2015 21:32:42 +0300 Subject: [PATCH 093/444] Updated method readability --- application/libraries/REST_Controller.php | 29 ++++++++++++++--------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index ed1ad3e1..6e6e1efa 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -600,25 +600,32 @@ public function response($data = NULL, $http_code = NULL, $continue = FALSE) } /** - * Detect which format the HTTP Body is provided in + * Get the input format e.g. json or xml * - * @access protected + * @access private + * @return string|NULL Supported input format; otherwise, NULL */ - protected function _detect_input_format() + private function _detect_input_format() { - if ($this->input->server('CONTENT_TYPE')) + // Get the CONTENT-TYPE value from the SERVER variable + $contentType = $this->input->server('CONTENT_TYPE'); + + if (empty($contentType) === FALSE) { // Check all formats against the HTTP_ACCEPT header - foreach ($this->_supported_formats as $format => $mime) + foreach ($this->_supported_formats as $key => $value) { - if (strpos($match = $this->input->server('CONTENT_TYPE'), ';')) - { - $match = current(explode(';', $match)); - } + // $key = format e.g. csv + // $value = mime type e.g. application/csv + + // If a semi-colon exists in the string, then explode by ; and get the value of where + // the current array pointer resides. This will generally be the first element of the array + $contentType = (strpos($contentType, ';') !== FALSE ? current(explode(';', $contentType)) : $contentType); - if ($match == $mime) + // If both the mime types match, then return the format + if ($contentType === $value) { - return $format; + return $key; } } } From 3149ff102922d6dad04a76c51b815f21162d2f8b Mon Sep 17 00:00:00 2001 From: softwarespot Date: Thu, 2 Jul 2015 21:36:58 +0300 Subject: [PATCH 094/444] Update parsing GET variables function --- application/libraries/REST_Controller.php | 27 ++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 6e6e1efa..a928369b 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -1050,22 +1050,39 @@ protected function _auth_override_check() } /** - * Parse GET + * Parse the GET request arguments * * @access protected + * @return void */ protected function _parse_get() { + // Declare a variable that will hold the REQUEST_URI + $request_uri = NULL; + // Fix for Issue #247 if (is_cli()) { - $args = $_SERVER['argv']; + $args = $this->input->server('argv'); unset($args[0]); - $_SERVER['QUERY_STRING'] = $_SERVER['PATH_INFO'] = $_SERVER['REQUEST_URI'] = '/' . implode('/', $args) . '/'; + // Combine the arguments using '/' as the delimiter + $request_uri = '/' . implode('/', $args) . '/'; + + // Set the following server variables + $_SERVER['REQUEST_URI'] = $request_uri; + $_SERVER['PATH_INFO'] = $request_uri; + $_SERVER['QUERY_STRING'] = $request_uri; + } + else + { + $request_uri = $this->input->server('REQUEST_URI'); } - // Grab proper GET variables - parse_str(parse_url(http://github.com/$_SERVER['REQUEST_URI'],%20PHP_URL_QUERY), $get); + // Declare a variable that will hold the parameters + $get = NULL; + + // Grab the GET variables from the query string + parse_str(parse_url(http://github.com/$request_uri,%20PHP_URL_QUERY), $get); // Merge both the URI segments and GET params $this->_get_args = array_merge($this->_get_args, $get); From 3ad8dd658c3331b5766652a84bb4e8e5e43d23da Mon Sep 17 00:00:00 2001 From: softwarespot Date: Thu, 2 Jul 2015 21:37:44 +0300 Subject: [PATCH 095/444] Updated parsing POST variables function --- 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 a928369b..272f5dc1 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -1089,15 +1089,19 @@ protected function _parse_get() } /** - * Parse POST + * Parse the POST request arguments * * @access protected + * @return void */ protected function _parse_post() { $this->_post_args = $_POST; - $this->request->format && $this->request->body = file_get_contents('php://input'); + if ($this->request->format) + { + $this->request->body = $this->input->raw_input_stream; + } } /** From df64259449d702a98f75b2b4cf18d4f265dc9957 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Thu, 2 Jul 2015 21:40:24 +0300 Subject: [PATCH 096/444] Updated parsing PUT variables function --- application/libraries/REST_Controller.php | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 272f5dc1..32fa6e03 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -1105,27 +1105,25 @@ protected function _parse_post() } /** - * Parse PUT + * Parse the PUT request arguments * * @access protected + * @return void */ protected function _parse_put() { - // It might be a HTTP body if ($this->request->format) { - $this->request->body = file_get_contents('php://input'); + $this->request->body = $this->input->raw_input_stream; } - - // If no file type is provided, this is probably just arguments else { + // If no filetype is provided, then there are probably just arguments if ($this->input->method() === 'put') { $this->_put_args = $this->input->input_stream(); } } - } /** From 0532769fab7a3569f430090606300a07268873be Mon Sep 17 00:00:00 2001 From: softwarespot Date: Thu, 2 Jul 2015 21:43:02 +0300 Subject: [PATCH 097/444] Updated parsing PUT variables function --- 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 32fa6e03..17b352ca 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -1155,21 +1155,21 @@ protected function _parse_options() } /** - * Parse PATCH + * Parse the PATCH request arguments * * @access protected + * @return void */ protected function _parse_patch() { // It might be a HTTP body if ($this->request->format) { - $this->request->body = file_get_contents('php://input'); + $this->request->body = $this->input->raw_input_stream; } - - // If no file type is provided, this is probably just arguments else { + // If no filetype is provided, then there are probably just arguments if ($this->input->method() === 'patch') { $this->_patch_args = $this->input->input_stream(); From 942475ce08dff4070391883e6a00fcdd0b3ac847 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Thu, 2 Jul 2015 21:43:57 +0300 Subject: [PATCH 098/444] Updated parsing DELETE variables function --- 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 17b352ca..d8bfd49f 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -1178,13 +1178,14 @@ protected function _parse_patch() } /** - * Parse DELETE + * Parse the DELETE request arguments * * @access protected + * @return void */ protected function _parse_delete() { - // Set up out DELETE variables (which shouldn't really exist, but sssh!) + // These should exist if a DELETE request if ($this->input->method() === 'delete') { $this->_delete_args = $this->input->input_stream(); From b83180df0c69bdd7db6b8741acf42d13de681a43 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Thu, 2 Jul 2015 21:46:20 +0300 Subject: [PATCH 099/444] Updated parsing OPTIONS variables --- application/libraries/REST_Controller.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index d8bfd49f..a8899503 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -1141,14 +1141,15 @@ protected function _parse_head() } /** - * Parse OPTIONS + * Parse the OPTIONS request arguments * * @access protected + * @return void */ protected function _parse_options() { - // Grab proper OPTIONS variables - parse_str(parse_url(http://github.com/$_SERVER['REQUEST_URI'],%20PHP_URL_QUERY), $options); + // Parse the OPTIONS variables + parse_str(parse_url(http://github.com/$this-%3Einput-%3Eserver('REQUEST_URI'), PHP_URL_QUERY), $options); // Merge both the URI segments and OPTIONS params $this->_options_args = array_merge($this->_options_args, $options); From 5e366838e40447da0c23b08aa2844d39d4baaf06 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Thu, 2 Jul 2015 21:48:00 +0300 Subject: [PATCH 100/444] Updated parsing HEAD variables function --- application/libraries/REST_Controller.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index a8899503..b6cc5366 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -1127,14 +1127,15 @@ protected function _parse_put() } /** - * Parse HEAD + * Parse the HEAD request arguments * * @access protected + * @return void */ protected function _parse_head() { - // Grab proper HEAD variables - parse_str(parse_url(http://github.com/$_SERVER['REQUEST_URI'],%20PHP_URL_QUERY), $head); + // Parse the HEAD variables + parse_str(parse_url$this->input->server('REQUEST_URI'), PHP_URL_QUERY), $head); // Merge both the URI segments and HEAD params $this->_head_args = array_merge($this->_head_args, $head); From e5fe7611774aa22962e106bcaaf940b2c045103d Mon Sep 17 00:00:00 2001 From: softwarespot Date: Thu, 2 Jul 2015 21:50:08 +0300 Subject: [PATCH 101/444] Fixed redundant whitespace --- 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 b6cc5366..c1b56460 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -1418,7 +1418,6 @@ protected function _perform_ldap_auth($username = '', $password = NULL) if ($ldapconn) { - log_message('debug', 'Setting timeout to ' . $ldap['timeout'] . ' seconds'); ldap_set_option($ldapconn, LDAP_OPT_NETWORK_TIMEOUT, $ldap['timeout']); @@ -1439,7 +1438,6 @@ protected function _perform_ldap_auth($username = '', $password = NULL) return FALSE; } - } // search for user From ee5d64cc7fad756e6edd9b00e4faae6975436ecb Mon Sep 17 00:00:00 2001 From: softwarespot Date: Thu, 2 Jul 2015 21:53:53 +0300 Subject: [PATCH 102/444] Commented unused variable Closes #479 --- 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 c1b56460..df28d17c 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -1411,7 +1411,7 @@ protected function _perform_ldap_auth($username = '', $password = NULL) log_message('debug', 'LDAP Auth: Connect to ' . (isset($ldaphost) ? $ldaphost : '[ldap not configured]')); // Appears to be unused - $ldapconfig['authrealm'] = $this->config->item('domain', 'ldap'); + // $ldapconfig['authrealm'] = $this->config->item('domain', 'ldap'); // connect to ldap server $ldapconn = ldap_connect($ldap['host'], $ldap['port']); From 47ef72639bbbc092de83d8c88e9eb6ee2d183e57 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Thu, 2 Jul 2015 22:13:30 +0300 Subject: [PATCH 103/444] Fixed comparison checking to check both value and type --- application/libraries/REST_Controller.php | 36 +++++++++++------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index df28d17c..1c6bfa15 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -264,7 +264,7 @@ public function __construct($config = 'rest') $this->request->method = $this->_detect_method(); // Create an argument container if it doesn't exist e.g. _get_args - if (!isset($this->{'_' . $this->request->method . '_args'})) + if (isset($this->{'_' . $this->request->method . '_args'}) === FALSE) { $this->{'_' . $this->request->method . '_args'} = []; } @@ -425,7 +425,7 @@ public function _remap($object_called, $arguments) } // Check to see if this key has access to the requested controller. - if ($this->config->item('rest_enable_keys') && $use_key && !empty($this->rest->key) && !$this->_check_access()) + if ($this->config->item('rest_enable_keys') && $use_key && empty($this->rest->key) === FALSE && $this->_check_access() === FALSE) { if ($this->config->item('rest_enable_logging') && $log_method) { @@ -436,16 +436,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) === FALSE) { $this->response([$this->config->item('rest_status_field_name') => FALSE, $this->config->item('rest_message_field_name') => 'Unknown method.'], 404); } // Doing key related stuff? Can only do it if they have a key right? - if ($this->config->item('rest_enable_keys') && !empty($this->rest->key)) + if ($this->config->item('rest_enable_keys') && empty($this->rest->key) === FALSE) { // Check the limit - if ($this->config->item('rest_enable_limits') && !$this->_check_limit($controller_method)) + if ($this->config->item('rest_enable_limits') && $this->_check_limit($controller_method) === FALSE) { $response = [$this->config->item('rest_status_field_name') => FALSE, $this->config->item('rest_message_field_name') => 'This API key has reached the hourly limit for this method.']; $this->response($response, 401); @@ -637,7 +637,7 @@ private function _detect_input_format() * Detect which format should be used to output the data * * @access protected - * @return string The output format. + * @return mixed|NULL|string Output format */ protected function _detect_output_format() { @@ -651,9 +651,9 @@ protected function _detect_output_format() } // Check if a file extension is used - elseif ($this->_get_args && !is_array(end($this->_get_args)) && preg_match($pattern, end($this->_get_args), $matches)) + elseif ($this->_get_args && is_array(end($this->_get_args)) === FALSE && 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)) { + //elseif ($this->_get_args and is_array(end($this->_get_args)) === FALSE and preg_match($pattern, end(array_keys($this->_get_args)), $matches)) { // The key of the last argument $arg_keys = array_keys($this->_get_args); $last_key = end($arg_keys); @@ -681,7 +681,7 @@ protected function _detect_output_format() 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' && $format != 'xml') + if ($format !== 'html' && $format !== 'xml') { return $format; } @@ -706,7 +706,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)) + if (empty($this->rest_format) === FALSE) { return $this->rest_format; } @@ -785,7 +785,7 @@ protected function _detect_api_key() * 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)) + if (empty($row->is_private_key) === FALSE) { // Check for a list of valid ip addresses if (isset($row->ip_addresses)) @@ -895,7 +895,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) || !isset($this->methods[$controller_method]['limit'])) + if (empty($this->rest->ignore_limits) === FALSE || isset($this->methods[$controller_method]['limit']) === FALSE) { // Everything is fine return TRUE; @@ -912,7 +912,7 @@ protected function _check_limit($controller_method) ->row(); // No calls have been made for this key - if (!$result) + if ($result === NULL) { // Create a new row for the following key $this->rest->db->insert($this->config->item('rest_limits_table'), [ @@ -975,7 +975,7 @@ protected function _auth_override_check() } // check for wildcard flag for rules for classes - if (!empty($this->overrides_array[$this->router->class]['*'])) // Check for class overrides + if (empty($this->overrides_array[$this->router->class]['*']) === FALSE) // Check for class overrides { // None auth override found, prepare nothing but send back a TRUE override flag if ($this->overrides_array[$this->router->class]['*'] == 'none') @@ -1448,7 +1448,7 @@ protected function _perform_ldap_auth($username = '', $password = NULL) return FALSE; } - if (ldap_count_entries($ldapconn, $res_id) != 1) + if (ldap_count_entries($ldapconn, $res_id) !== 1) { log_message('error', 'LDAP Auth: failure, username ' . $username . 'found more than once'); @@ -1521,7 +1521,7 @@ protected function _perform_library_auth($username = '', $password = NULL) return FALSE; } - if (!is_callable([$auth_library_class, $auth_library_function])) + if (is_callable([$auth_library_class, $auth_library_function]) === FALSE) { $this->load->library($auth_library_class); } @@ -1580,7 +1580,7 @@ protected function _check_login($username = NULL, $password = FALSE) return FALSE; } - if ($valid_logins[$username] != $password) + if ($valid_logins[$username] !== $password) { return FALSE; } @@ -1750,7 +1750,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) === FALSE) { $this->response([$this->config->item('rest_status_field_name') => FALSE, $this->config->item('rest_message_field_name') => 'IP not authorized'], 401); } From e80b48e4aa21a94d71a820ece837faccff457107 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Thu, 2 Jul 2015 22:29:20 +0300 Subject: [PATCH 104/444] Fixed missing bracket during refactoring --- 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 1c6bfa15..6f7952e2 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -1135,7 +1135,7 @@ protected function _parse_put() protected function _parse_head() { // Parse the HEAD variables - parse_str(parse_url$this->input->server('REQUEST_URI'), PHP_URL_QUERY), $head); + parse_str(parse_url(http://github.com/$this-%3Einput-%3Eserver('REQUEST_URI'), PHP_URL_QUERY), $head); // Merge both the URI segments and HEAD params $this->_head_args = array_merge($this->_head_args, $head); From dffbce315ae407062a37aca0fcd6ef41fe4ecf0c Mon Sep 17 00:00:00 2001 From: softwarespot Date: Thu, 2 Jul 2015 22:47:13 +0300 Subject: [PATCH 105/444] Fixed comparison --- 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 210e2f5b..165b6384 100644 --- a/application/libraries/Format.php +++ b/application/libraries/Format.php @@ -111,7 +111,7 @@ public function to_array($data = NULL) $array = []; foreach ((array) $data as $key => $value) { - if (is_object($value) || is_array($value)) + if (is_object($value) === TRUE || is_array($value) === TRUE) { $array[$key] = $this->to_array($value); } @@ -153,7 +153,7 @@ public function to_xml($data = NULL, $structure = NULL, $basenode = 'xml') } // Force it to be something useful - if (!is_array($data) && !is_object($data)) + if (is_array($data) === FALSE && is_object($data) === FALSE) { $data = (array) $data; } From 6eb7e9d8fee29674609e0e268f24e6084e0ef5d4 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Mon, 6 Jul 2015 09:22:51 +0300 Subject: [PATCH 106/444] Removed empty line --- application/controllers/api/key.php | 1 - 1 file changed, 1 deletion(-) diff --git a/application/controllers/api/key.php b/application/controllers/api/key.php index a708f52b..c89b64a5 100644 --- a/application/controllers/api/key.php +++ b/application/controllers/api/key.php @@ -253,7 +253,6 @@ private function _key_exists($key) private function _insert_key($key, $data) { - $data[config_item('rest_key_column')] = $key; $data['date_created'] = function_exists('now') ? now() : time(); From e2543ecfbc5316765af44cef9023628b75968622 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Mon, 6 Jul 2015 09:43:48 +0300 Subject: [PATCH 107/444] Error on some systems with mcrypt_create_iv() not returning a string Uses the native CI method get_random_bytes(), which is a simple wrapper for mcrypt_create_iv() . Due to the changes made in 5e2cf85 --- application/controllers/api/key.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/application/controllers/api/key.php b/application/controllers/api/key.php index c89b64a5..4e9a3834 100644 --- a/application/controllers/api/key.php +++ b/application/controllers/api/key.php @@ -224,7 +224,13 @@ private function _generate_key() do { // Generate a random salt - $salt = mcrypt_create_iv(16, MCRYPT_DEV_URANDOM); + $salt = $this->security->get_random_bytes(64); + + // If an error occurred, then fall back to the previous method + if ($salt === FALSE) + { + $salt = hash('sha256', time() . mt_rand()); + } $new_key = substr($salt, 0, config_item('rest_key_length')); } while (self::_key_exists($new_key)); From efef7ea7253b10c31bbe18f08da5f40dfcf111b9 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Wed, 8 Jul 2015 20:24:31 +0300 Subject: [PATCH 108/444] Temporary rename Mentioned in #468, as CI3 has controllers as Ucfirst --- application/controllers/api/{example.php => Example_.php} | 0 application/controllers/api/{key.php => Key_.php} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename application/controllers/api/{example.php => Example_.php} (100%) rename application/controllers/api/{key.php => Key_.php} (100%) diff --git a/application/controllers/api/example.php b/application/controllers/api/Example_.php similarity index 100% rename from application/controllers/api/example.php rename to application/controllers/api/Example_.php diff --git a/application/controllers/api/key.php b/application/controllers/api/Key_.php similarity index 100% rename from application/controllers/api/key.php rename to application/controllers/api/Key_.php From 3c4559174aadb42e6b9b73e4615e300540c5f447 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Wed, 8 Jul 2015 20:25:24 +0300 Subject: [PATCH 109/444] Renamed controllers back to be ucfirst Mentioned in #468 --- application/controllers/api/{Example_.php => Example.php} | 0 application/controllers/api/{Key_.php => Key.php} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename application/controllers/api/{Example_.php => Example.php} (100%) rename application/controllers/api/{Key_.php => Key.php} (100%) diff --git a/application/controllers/api/Example_.php b/application/controllers/api/Example.php similarity index 100% rename from application/controllers/api/Example_.php rename to application/controllers/api/Example.php diff --git a/application/controllers/api/Key_.php b/application/controllers/api/Key.php similarity index 100% rename from application/controllers/api/Key_.php rename to application/controllers/api/Key.php From 0eec9b5f3cd9fff25a671c9ef57a90c9ebf02bec Mon Sep 17 00:00:00 2001 From: Kevin Lagaisse Date: Fri, 10 Jul 2015 22:02:29 +0200 Subject: [PATCH 110/444] api time limit --- application/libraries/REST_Controller.php | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 6f7952e2..f9314bb3 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -447,7 +447,7 @@ public function _remap($object_called, $arguments) // Check the limit if ($this->config->item('rest_enable_limits') && $this->_check_limit($controller_method) === FALSE) { - $response = [$this->config->item('rest_status_field_name') => FALSE, $this->config->item('rest_message_field_name') => 'This API key has reached the hourly limit for this method.']; + $response = [$this->config->item('rest_status_field_name') => FALSE, $this->config->item('rest_message_field_name') => 'This API key has reached the time limit for this method.']; $this->response($response, 401); } @@ -901,16 +901,24 @@ protected function _check_limit($controller_method) return TRUE; } - // How many times can you get to this method in an hour? + // How many times can you get to this method in a defined timelimit (default: 1hr)? $limit = $this->methods[$controller_method]['limit']; + $uri_noext = $this->uri->uri_string(); + if (strpos(strrev($this->uri->uri_string()), strrev($this->response->format)) === 0) + { + $uri_noext = substr($this->uri->uri_string(),0, -strlen($this->response->format)-1); + } + // Get data about a keys' usage and limit to one row $result = $this->rest->db - ->where('uri', $this->uri->uri_string()) + ->where('uri', $uri_noext) ->where('api_key', $this->rest->key) ->get($this->config->item('rest_limits_table')) ->row(); + $timelimit = (isset($this->methods[$controller_method]['time']) ? $this->methods[$controller_method]['time'] : 60 * 60); + // No calls have been made for this key if ($result === NULL) { @@ -923,12 +931,12 @@ protected function _check_limit($controller_method) ]); } - // Been an hour since they called - elseif ($result->hour_started < (time() - 3600)) + // Been a time limit (or by default an hour) since they called + elseif ($result->hour_started < (time() - $timelimit)) { // Reset the started period and count $this->rest->db - ->where('uri', $this->uri->uri_string()) + ->where('uri', $uri_noext) ->where('api_key', isset($this->rest->key) ? $this->rest->key : '') ->set('hour_started', time()) ->set('count', 1) @@ -946,7 +954,7 @@ protected function _check_limit($controller_method) // Increase the count by one $this->rest->db - ->where('uri', $this->uri->uri_string()) + ->where('uri', $uri_noext) ->where('api_key', $this->rest->key) ->set('count', 'count + 1', FALSE) ->update($this->config->item('rest_limits_table')); From d52d82a724e1ce6629f55a1e97bdf18ae58dcb24 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Mon, 13 Jul 2015 18:21:19 +0300 Subject: [PATCH 111/444] Removed whitespace --- 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 f9314bb3..73ad657f 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -905,8 +905,8 @@ protected function _check_limit($controller_method) $limit = $this->methods[$controller_method]['limit']; $uri_noext = $this->uri->uri_string(); - if (strpos(strrev($this->uri->uri_string()), strrev($this->response->format)) === 0) - { + if (strpos(strrev($this->uri->uri_string()), strrev($this->response->format)) === 0) + { $uri_noext = substr($this->uri->uri_string(),0, -strlen($this->response->format)-1); } From 4f8c122ba81295dd8445569412948464ead120d2 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Mon, 13 Jul 2015 18:58:12 +0300 Subject: [PATCH 112/444] Using PHP_EOL --- application/libraries/Format.php | 2 +- application/libraries/REST_Controller.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/application/libraries/Format.php b/application/libraries/Format.php index 165b6384..6efb03ea 100644 --- a/application/libraries/Format.php +++ b/application/libraries/Format.php @@ -408,7 +408,7 @@ protected function _from_csv($data) $array = []; // Splits - $rows = explode("\n", trim($data)); + $rows = explode(PHP_EOL, trim($data)); $headings = explode(',', array_shift($rows)); foreach ($rows as $row) { diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 73ad657f..a16ea0df 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -1379,7 +1379,7 @@ public function validation_errors() { $string = strip_tags($this->form_validation->error_string()); - return explode("\n", trim($string, "\n")); + return explode(PHP_EOL, trim($string, PHP_EOL)); } // SECURITY FUNCTIONS --------------------------------------------------------- From 279039f27d364eaeeafcad46f79ca2cd4071578f Mon Sep 17 00:00:00 2001 From: softwarespot Date: Mon, 13 Jul 2015 19:12:04 +0300 Subject: [PATCH 113/444] Added additional methods --- 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 a16ea0df..b1dca730 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -121,7 +121,7 @@ abstract class REST_Controller extends CI_Controller { protected $_options_args = []; /** - * The arguments from GET, POST, PUT, DELETE request methods combined. + * The arguments from GET, POST, PUT, DELETE, PATCH, HEAD and OPTIONS request methods combined * * @var array */ From 115923da2ba61becc4be7b5e31c72ad2dbfaf902 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Mon, 13 Jul 2015 19:48:45 +0300 Subject: [PATCH 114/444] Added $this->query() to parse query parameters Regardless of whether it's a GET or POST request. Fixes #405 --- README.md | 6 ++ application/libraries/REST_Controller.php | 97 ++++++++++++++++------- 2 files changed, 74 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 0c1f3139..ecff303d 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,12 @@ public function index_delete($id) } ``` +If query parameters are passed via the URL, regardless of whether it's a GET request, can be obtainable by the query method: + +```php +$this->query('blah'); // Query 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. diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index b1dca730..cff2617f 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -120,6 +120,13 @@ abstract class REST_Controller extends CI_Controller { */ protected $_options_args = []; + /** + * The arguments for the query parameters + * + * @var array + */ + protected $_query_args = []; + /** * The arguments from GET, POST, PUT, DELETE, PATCH, HEAD and OPTIONS request methods combined * @@ -269,6 +276,9 @@ public function __construct($config = 'rest') $this->{'_' . $this->request->method . '_args'} = []; } + // Set up the query parameters + $this->_parse_query(); + // Set up the GET variables $this->_get_args = array_merge($this->_get_args, $this->uri->ruri_to_assoc()); @@ -1065,35 +1075,8 @@ protected function _auth_override_check() */ protected function _parse_get() { - // Declare a variable that will hold the REQUEST_URI - $request_uri = NULL; - - // Fix for Issue #247 - if (is_cli()) - { - $args = $this->input->server('argv'); - unset($args[0]); - // Combine the arguments using '/' as the delimiter - $request_uri = '/' . implode('/', $args) . '/'; - - // Set the following server variables - $_SERVER['REQUEST_URI'] = $request_uri; - $_SERVER['PATH_INFO'] = $request_uri; - $_SERVER['QUERY_STRING'] = $request_uri; - } - else - { - $request_uri = $this->input->server('REQUEST_URI'); - } - - // Declare a variable that will hold the parameters - $get = NULL; - - // Grab the GET variables from the query string - parse_str(parse_url(http://github.com/$request_uri,%20PHP_URL_QUERY), $get); - - // Merge both the URI segments and GET params - $this->_get_args = array_merge($this->_get_args, $get); + // Merge both the URI segments and query parameters + $this->_get_args = array_merge($this->_get_args, $this->_query_args); } /** @@ -1202,6 +1185,41 @@ protected function _parse_delete() } } + /** + * Parse the query parameters + * + * @access protected + * + * @return void + */ + public function _parse_query() + { + // Declare a variable that will hold the REQUEST_URI + $request_uri = NULL; + + // If using the commandline version + if (is_cli()) + { + $args = $this->input->server('argv'); + unset($args[0]); + + // Combine the arguments using '/' as the delimiter + $request_uri = '/' . implode('/', $args) . '/'; + + // Set the following server variables (perhaps not required anymore?) + $_SERVER['REQUEST_URI'] = $request_uri; + $_SERVER['PATH_INFO'] = $request_uri; + $_SERVER['QUERY_STRING'] = $request_uri; + } + else + { + $request_uri = $this->input->server('REQUEST_URI'); + } + + // Parse the query parameters from the query string + parse_str(parse_url(http://github.com/$request_uri,%20PHP_URL_QUERY), $this->_query_args); + } + // INPUT FUNCTION -------------------------------------------------------------- /** @@ -1351,6 +1369,27 @@ public function patch($key = NULL, $xss_clean = NULL) return array_key_exists($key, $this->_patch_args) ? $this->_xss_clean($this->_patch_args[$key], $xss_clean) : FALSE; } + /** + * Retrieve a value from the query parameters + * + * @access public + * + * @param NULL $key Key to retrieve from the query parameters + * If NULL an array of arguments is returned + * @param NULL $xss_clean Whether to apply XSS filtering + * + * @return array|string|FALSE Value from the query parameters; otherwise, FALSE + */ + public function query($key = NULL, $xss_clean = NULL) + { + if ($key === NULL) + { + return $this->_query_args; + } + + return array_key_exists($key, $this->_query_args) ? $this->_xss_clean($this->_query_args[$key], $xss_clean) : FALSE; + } + /** * Sanitizes data so that Cross Site Scripting Hacks can be * prevented. From 75d7893295d9da875fbcb8011e04c1531f32450e Mon Sep 17 00:00:00 2001 From: softwarespot Date: Mon, 13 Jul 2015 19:49:20 +0300 Subject: [PATCH 115/444] Unified comments --- application/libraries/Format.php | 13 ------ application/libraries/REST_Controller.php | 50 +++++------------------ 2 files changed, 11 insertions(+), 52 deletions(-) diff --git a/application/libraries/Format.php b/application/libraries/Format.php index 6efb03ea..ddff3c69 100644 --- a/application/libraries/Format.php +++ b/application/libraries/Format.php @@ -37,7 +37,6 @@ class Format { * * @param NULL $data * @param NULL $from_type - * * @throws Exception */ @@ -90,7 +89,6 @@ public function factory($data, $from_type = NULL) * * @param mixed|NULL $data Optional data to pass, so as to override the data passed * to the constructor - * * @return array Data parsed as an array; otherwise, an empty array */ public function to_array($data = NULL) @@ -131,7 +129,6 @@ public function to_array($data = NULL) * to the constructor * @param NULL $structure * @param string $basenode - * * @return mixed */ public function to_xml($data = NULL, $structure = NULL, $basenode = 'xml') @@ -215,7 +212,6 @@ public function to_xml($data = NULL, $structure = NULL, $basenode = 'xml') * * @param mixed|NULL $data Optional data to pass, so as to override the data passed * to the constructor - * * @return mixed */ public function to_html($data = NULL) @@ -265,7 +261,6 @@ public function to_html($data = NULL) * * @param mixed|NULL $data Optional data to pass, so as to override the data passed * to the constructor - * * @return mixed */ public function to_csv($data = NULL) @@ -311,7 +306,6 @@ public function to_csv($data = NULL) * * @param mixed|NULL $data Optional data to pass, so as to override the data passed * to the constructor - * * @return string Json representation of a value */ public function to_json($data = NULL) @@ -350,7 +344,6 @@ public function to_json($data = NULL) * * @param mixed|NULL $data Optional data to pass, so as to override the data passed * to the constructor - * * @return string Serialized data */ public function to_serialized($data = NULL) @@ -370,7 +363,6 @@ public function to_serialized($data = NULL) * * @param mixed|NULL $data Optional data to pass, so as to override the data passed * to the constructor - * * @return mixed String representation of a variable */ public function to_php($data = NULL) @@ -389,7 +381,6 @@ public function to_php($data = NULL) /** * @param $data XML string - * * @return SimpleXMLElement XML element object; otherwise, empty array */ protected function _from_xml($data) @@ -399,7 +390,6 @@ protected function _from_xml($data) /** * @param string $data CSV string - * * @return array A multi-dimensional array with the outer array being the number of rows * and the inner arrays the individual fields */ @@ -426,7 +416,6 @@ protected function _from_csv($data) /** * @param $data Encoded json string - * * @return mixed Decoded json string with leading and trailing whitespace removed */ protected function _from_json($data) @@ -436,7 +425,6 @@ protected function _from_json($data) /** * @param string Data to unserialized - * * @return mixed Unserialized data */ protected function _from_serialize($data) @@ -446,7 +434,6 @@ protected function _from_serialize($data) /** * @param $data Data to trim leading and trailing whitespace - * * @return string Data with leading and trailing whitespace removed */ protected function _from_php($data) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index cff2617f..e4f61cc1 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -206,6 +206,7 @@ abstract class REST_Controller extends CI_Controller { * Extend this function to apply additional checking early on in the process * * @access protected + * @return void */ protected function early_checks() { @@ -215,9 +216,9 @@ protected function early_checks() * Constructor for the REST API * * @access public - * * @param string $config Configuration filename minus the file extension * e.g: my_rest.php is passed as 'my_rest' + * @return void */ public function __construct($config = 'rest') { @@ -381,6 +382,7 @@ public function __construct($config = 'rest') * * @author Chris Kacerguis * @access public + * @return void */ public function __destruct() { @@ -400,7 +402,6 @@ public function __destruct() * Controller method. * * @access public - * * @param string $object_called * @param array $arguments The arguments passed to the controller method. */ @@ -507,7 +508,6 @@ public function _remap($object_called, $arguments) * Takes mixed data and optionally a status code, then creates the response * * @access public - * * @param array|NULL $data Data to output to the user * @param int|NULL $http_code HTTP status code * @param bool $continue TRUE to flush the response to the client and continue @@ -728,6 +728,7 @@ protected function _detect_output_format() /** * Get the HTTP request string e.g. get or post * + * @access protected * @return string|NULL Supported request method as a lowercase string; otherwise, NULL if not supported */ protected function _detect_method() @@ -867,9 +868,7 @@ protected function _detect_lang() * Add the request to the log table * * @access protected - * * @param bool $authorized TRUE the user is authorized; otherwise, FALSE - * * @return bool TRUE the data was inserted; otherwise, FALSE */ protected function _log_request($authorized = FALSE) @@ -897,9 +896,7 @@ protected function _log_request($authorized = FALSE) * Check if the requests to a controller method exceed a limit * * @access protected - * * @param string $controller_method The method being called - * * @return bool TRUE the call limit is below the threshold; otherwise, FALSE */ protected function _check_limit($controller_method) @@ -974,9 +971,7 @@ protected function _check_limit($controller_method) } /** - * 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 * * @access protected * @return bool @@ -1189,7 +1184,6 @@ protected function _parse_delete() * Parse the query parameters * * @access protected - * * @return void */ public function _parse_query() @@ -1226,11 +1220,9 @@ public function _parse_query() * Retrieve a value from a GET request * * @access public - * * @param NULL $key Key to retrieve from the GET request * If NULL an array of arguments is returned * @param NULL $xss_clean Whether to apply XSS filtering - * * @return array|string|FALSE Value from the GET request; otherwise, FALSE */ public function get($key = NULL, $xss_clean = NULL) @@ -1247,11 +1239,9 @@ public function get($key = NULL, $xss_clean = NULL) * Retrieve a value from a OPTIONS request * * @access public - * * @param NULL $key Key to retrieve from the OPTIONS request. * If NULL an array of arguments is returned * @param NULL $xss_clean Whether to apply XSS filtering - * * @return array|string|FALSE Value from the OPTIONS request; otherwise, FALSE */ public function options($key = NULL, $xss_clean = NULL) @@ -1268,11 +1258,9 @@ public function options($key = NULL, $xss_clean = NULL) * Retrieve a value from a HEAD request * * @access public - * * @param NULL $key Key to retrieve from the HEAD request * If NULL an array of arguments is returned * @param NULL $xss_clean Whether to apply XSS filtering - * * @return array|string|FALSE Value from the HEAD request; otherwise, FALSE */ public function head($key = NULL, $xss_clean = NULL) @@ -1289,11 +1277,9 @@ public function head($key = NULL, $xss_clean = NULL) * Retrieve a value from a POST request * * @access public - * * @param NULL $key Key to retrieve from the POST request * If NULL an array of arguments is returned * @param NULL $xss_clean Whether to apply XSS filtering - * * @return array|string|FALSE Value from the POST request; otherwise, FALSE */ public function post($key = NULL, $xss_clean = NULL) @@ -1310,11 +1296,9 @@ public function post($key = NULL, $xss_clean = NULL) * Retrieve a value from a PUT request * * @access public - * * @param NULL $key Key to retrieve from the PUT request * If NULL an array of arguments is returned * @param NULL $xss_clean Whether to apply XSS filtering - * * @return array|string|FALSE Value from the PUT request; otherwise, FALSE */ public function put($key = NULL, $xss_clean = NULL) @@ -1331,11 +1315,9 @@ public function put($key = NULL, $xss_clean = NULL) * Retrieve a value from a DELETE request * * @access public - * * @param NULL $key Key to retrieve from the DELETE request * If NULL an array of arguments is returned * @param NULL $xss_clean Whether to apply XSS filtering - * * @return array|string|FALSE Value from the DELETE request; otherwise, FALSE */ public function delete($key = NULL, $xss_clean = NULL) @@ -1352,11 +1334,9 @@ public function delete($key = NULL, $xss_clean = NULL) * Retrieve a value from a PATCH request * * @access public - * * @param NULL $key Key to retrieve from the PATCH request * If NULL an array of arguments is returned * @param NULL $xss_clean Whether to apply XSS filtering - * * @return array|string|FALSE Value from the PATCH request; otherwise, FALSE */ public function patch($key = NULL, $xss_clean = NULL) @@ -1373,11 +1353,9 @@ public function patch($key = NULL, $xss_clean = NULL) * Retrieve a value from the query parameters * * @access public - * * @param NULL $key Key to retrieve from the query parameters * If NULL an array of arguments is returned * @param NULL $xss_clean Whether to apply XSS filtering - * * @return array|string|FALSE Value from the query parameters; otherwise, FALSE */ public function query($key = NULL, $xss_clean = NULL) @@ -1395,10 +1373,8 @@ public function query($key = NULL, $xss_clean = NULL) * prevented. * * @access protected - * * @param string $value Input data * @param bool $xss_clean Whether to apply XSS filtering - * * @return string */ protected function _xss_clean($value, $xss_clean) @@ -1427,10 +1403,8 @@ public function validation_errors() * Perform LDAP Authentication * * @access protected - * * @param string $username The username to validate * @param string $password The password to validate - * * @return bool */ protected function _perform_ldap_auth($username = '', $password = NULL) @@ -1536,10 +1510,8 @@ protected function _perform_ldap_auth($username = '', $password = NULL) * Perform Library Authentication - Override this function to change the way the library is called * * @access protected - * * @param string $username The username to validate * @param string $password The password to validate - * * @return bool */ protected function _perform_library_auth($username = '', $password = NULL) @@ -1580,10 +1552,8 @@ protected function _perform_library_auth($username = '', $password = NULL) * Check if the user is logged in * * @access protected - * * @param string $username The user's name * @param bool|string $password The user's password - * * @return bool */ protected function _check_login($username = NULL, $password = FALSE) @@ -1639,6 +1609,7 @@ protected function _check_login($username = NULL, $password = FALSE) * Check to see if the user is logged in with a PHP session key * * @access protected + * @return void */ protected function _check_php_session() { @@ -1661,6 +1632,7 @@ protected function _check_php_session() * Prepares for basic authentication * * @access protected + * @return void */ protected function _prepare_basic_auth() { @@ -1701,6 +1673,7 @@ protected function _prepare_basic_auth() * Prepares for digest authentication * * @access protected + * @return void */ protected function _prepare_digest_auth() { @@ -1762,6 +1735,7 @@ protected function _prepare_digest_auth() * Checks if the client's ip is in the 'rest_ip_blacklist' config and generates a 401 response * * @access protected + * @return void */ protected function _check_blacklist_auth() { @@ -1785,6 +1759,7 @@ protected function _check_blacklist_auth() * Check if the client's ip is in the 'rest_ip_whitelist' config and generates a 401 response * * @access protected + * @return void */ protected function _check_whitelist_auth() { @@ -1807,9 +1782,9 @@ protected function _check_whitelist_auth() * Force logging in by setting the WWW-Authenticate header * * @access protected - * * @param string $nonce A server-specified data string which should be uniquely generated * each time + * @return void */ protected function _force_login($nonce = '') { @@ -1842,7 +1817,6 @@ protected function _force_login($nonce = '') * * @access protected * @author Chris Kacerguis - * * @return bool TRUE log table updated; otherwise, FALSE */ protected function _log_access_time() @@ -1861,9 +1835,7 @@ protected function _log_access_time() * * @access protected * @author Justin Chen - * * @param $http_code int HTTP status code - * * @return bool TRUE log table updated; otherwise, FALSE */ protected function _log_response_code($http_code) From 8dfee5647a83567dda57a743b52bf7741efe48e5 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Mon, 13 Jul 2015 19:49:54 +0300 Subject: [PATCH 116/444] Fixed typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ecff303d..df0084fc 100644 --- a/README.md +++ b/README.md @@ -189,7 +189,7 @@ $ curl -X POST -H "X-API-KEY: some_key_here" http://example.com/books ## Contributions -This project was originally written by Phil Sturgeon, however his involvment has shifted +This project was originally written by Phil Sturgeon, however his involvement 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 3e74048521268b6d7d17f4df7683fd83c4c6d0b0 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Mon, 13 Jul 2015 19:50:42 +0300 Subject: [PATCH 117/444] Using ISO 8601 date format We're developers after all --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index df0084fc..8df1e889 100644 --- a/README.md +++ b/README.md @@ -190,7 +190,7 @@ $ curl -X POST -H "X-API-KEY: some_key_here" http://example.com/books ## Contributions This project was originally written by Phil Sturgeon, however his involvement has shifted -as he is no longer using it. As of 11/20/2013 further developement and support will be done by Chris Kacerguis. +as he is no longer using it. As of 2013/11/20 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 a954158486bb78745a1626f51502d993c7b20ce0 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Mon, 13 Jul 2015 19:53:58 +0300 Subject: [PATCH 118/444] Added the most comment HTTP status codes --- application/libraries/REST_Controller.php | 96 +++++++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index e4f61cc1..129c92d6 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -202,6 +202,102 @@ abstract class REST_Controller extends CI_Controller { */ protected $_enable_xss = FALSE; + // Success + + /** + * The request has succeeded + */ + const OK = 200; + + /** + * The server successfully created a new resource + */ + const CREATED = 201; + + /** + * The server successfully processed the request, though no content is returned + */ + const NO_CONTENT = 204; + + // Redirection + + /** + * The resource has not been modified since the last request + */ + const NOT_MODIFIED = 304; + + // Client Error + + /** + * The request cannot be fulfilled due to multiple errors + */ + const BAD_REQUEST = 400; + + /** + * The user is unauthorized to access the requested resource + */ + const UNAUTHORIZED = 401; + + /** + * The requested resource is unavailable at this present time + */ + const FORBIDDEN = 403; + + /** + * The requested resource could not be found + * + * Note: This is sometimes used to mask if there was an UNAUTHORIZED (401) or + * FORBIDDEN (403) error, for security reasons + */ + const NOT_FOUND = 404; + + /** + * The request method is not supported by the following resource + */ + const METHOD_NOT_ALLOWED = 405; + + /** + * The request could not be completed due to a conflict with the current state + * of the resource + */ + const CONFLICT = 409; + + // Server Error + + /** + * The server encountered an unexpected error + * + * Note: This is a generic error message when no specific message + * is suitable + */ + const INTERNAL_SERVER_ERROR = 500; + + /** + * The server does not recognised the request method + */ + const NOT_IMPLEMENTED = 501; + + /** + * HTTP status codes and their respective description + * + * @var array + * @link http://www.restapitutorial.com/httpstatuscodes.html + */ + protected $http_status_codes = [ + self::OK => 'OK', + self::CREATED => 'CREATED', + self::NO_CONTENT => 'NO CONTENT', + self::NOT_MODIFIED => 'NOT MODIFIED', + self::BAD_REQUEST => 'BAD REQUEST', + self::UNAUTHORIZED => 'UNAUTHORIZED', + self::FORBIDDEN => 'FORBIDDEN', + self::NOT_FOUND => 'NOT FOUND', + self::METHOD_NOT_ALLOWED => 'METHOD NOT ALLOWED', + self::CONFLICT => 'CONFLICT', + self::INTERNAL_SERVER_ERROR => 'INTERNAL SERVER ERROR', + self::NOT_IMPLEMENTED => 'NOT IMPLEMENTED' + ]; + /** * Extend this function to apply additional checking early on in the process * From 26e2a772367f87935f55c9128f84d23249dec952 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Mon, 13 Jul 2015 19:55:37 +0300 Subject: [PATCH 119/444] Sentence correction --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8df1e889..ccd236d9 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ public function index_delete($id) } ``` -If query parameters are passed via the URL, regardless of whether it's a GET request, can be obtainable by the query method: +If query parameters are passed via the URL, regardless of whether it's a GET request, can be obtained by the query method: ```php $this->query('blah'); // Query param From e46d91f75d73a906d0761e37b36e9a9b85683f49 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Mon, 13 Jul 2015 20:48:42 +0300 Subject: [PATCH 120/444] Added format constants --- application/libraries/Format.php | 40 ++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/application/libraries/Format.php b/application/libraries/Format.php index ddff3c69..2cd73cb4 100644 --- a/application/libraries/Format.php +++ b/application/libraries/Format.php @@ -11,6 +11,46 @@ */ class Format { + /** + * Array output format + */ + const ARRAY_FORMAT = 'array'; + + /** + * Comma Separated Value (CSV) output format + */ + const CSV_FORMAT = 'csv'; + + /** + * Json output format + */ + const JSON_FORMAT = 'json'; + + /** + * HTML output format + */ + const HTML_FORMAT = 'html'; + + /** + * PHP output format + */ + const PHP_FORMAT = 'php'; + + /** + * Serialized output format + */ + const SERIALIZED_FORMAT = 'serialized'; + + /** + * XML output format + */ + const XML_FORMAT = 'xml'; + + /** + * Default format of this class + */ + const DEFAULT_FORMAT = self::JSON; // Couldn't be DEFAULT, as this is a keyword + /** * CodeIgniter instance * From a9821930a4ff1267dfd276896998ef6e01a74a4f Mon Sep 17 00:00:00 2001 From: softwarespot Date: Mon, 13 Jul 2015 20:57:57 +0300 Subject: [PATCH 121/444] Using class constants --- application/libraries/REST_Controller.php | 52 +++++++++++------------ 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 129c92d6..5dfef0a0 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -256,6 +256,11 @@ abstract class REST_Controller extends CI_Controller { */ const METHOD_NOT_ALLOWED = 405; + /** + * The request was not acceptable + */ + const NOT_ACCEPTABLE = 406; + /** * The request could not be completed due to a conflict with the current state * of the resource @@ -293,6 +298,7 @@ abstract class REST_Controller extends CI_Controller { self::FORBIDDEN => 'FORBIDDEN', self::NOT_FOUND => 'NOT FOUND', self::METHOD_NOT_ALLOWED => 'METHOD NOT ALLOWED', + self::NOT_ACCEPTABLE => 'NOT ACCEPTABLE', self::CONFLICT => 'CONFLICT', self::INTERNAL_SERVER_ERROR => 'INTERNAL SERVER ERROR', self::NOT_IMPLEMENTED => 'NOT IMPLEMENTED' @@ -443,11 +449,10 @@ public function __construct($config = 'rest') if ($this->input->is_ajax_request() === FALSE && $this->config->item('rest_ajax_only')) { // Display an error response - $this->response( - [ + $this->response([ $this->config->item('rest_status_field_name') => FALSE, $this->config->item('rest_message_field_name') => 'Only AJAX requests are acceptable' - ], 406); // Set status to 406 NOT ACCEPTABLE + ], self::NOT_ACCEPTABLE); } // When there is no specific override for the current class/method, use the default auth value set in the config @@ -506,7 +511,7 @@ public function _remap($object_called, $arguments) // Should we answer if not over SSL? if ($this->config->item('force_https') && $this->request->ssl === FALSE) { - $this->response([$this->config->item('rest_status_field_name') => FALSE, $this->config->item('rest_message_field_name') => 'Unsupported protocol'], 403); + $this->response([$this->config->item('rest_status_field_name') => FALSE, $this->config->item('rest_message_field_name') => 'Unsupported protocol'], self::FORBIDDEN); } // Remove the supported format from the function name e.g. index.json => index @@ -528,7 +533,7 @@ public function _remap($object_called, $arguments) $this->_log_request(); } - $this->response([$this->config->item('rest_status_field_name') => FALSE, $this->config->item('rest_message_field_name') => 'Invalid API Key ' . $this->rest->key], 403); + $this->response([$this->config->item('rest_status_field_name') => FALSE, $this->config->item('rest_message_field_name') => 'Invalid API Key ' . $this->rest->key], self::FORBIDDEN); } // Check to see if this key has access to the requested controller. @@ -539,13 +544,13 @@ public function _remap($object_called, $arguments) $this->_log_request(); } - $this->response([$this->config->item('rest_status_field_name') => FALSE, $this->config->item('rest_message_field_name') => 'This API key does not have access to the requested controller.'], 401); + $this->response([$this->config->item('rest_status_field_name') => FALSE, $this->config->item('rest_message_field_name') => 'This API key does not have access to the requested controller.'], self::UNAUTHORIZED); } // Sure it exists, but can they do anything with it? if (method_exists($this, $controller_method) === FALSE) { - $this->response([$this->config->item('rest_status_field_name') => FALSE, $this->config->item('rest_message_field_name') => 'Unknown method.'], 404); + $this->response([$this->config->item('rest_status_field_name') => FALSE, $this->config->item('rest_message_field_name') => 'Unknown method.'], self::NOT_FOUND); } // Doing key related stuff? Can only do it if they have a key right? @@ -555,7 +560,7 @@ public function _remap($object_called, $arguments) if ($this->config->item('rest_enable_limits') && $this->_check_limit($controller_method) === FALSE) { $response = [$this->config->item('rest_status_field_name') => FALSE, $this->config->item('rest_message_field_name') => 'This API key has reached the time limit for this method.']; - $this->response($response, 401); + $this->response($response, self:UNAUTHORIZED); } // If no level is set use 0, they probably aren't using permissions @@ -572,7 +577,7 @@ public function _remap($object_called, $arguments) // They don't have good enough perms $response = [$this->config->item('rest_status_field_name') => FALSE, $this->config->item('rest_message_field_name') => 'This API key does not have enough permissions.']; - $authorized || $this->response($response, 401); + $authorized || $this->response($response, self:UNAUTHORIZED); } // No key stuff, but record that stuff is happening @@ -589,14 +594,13 @@ public function _remap($object_called, $arguments) catch (Exception $ex) { // If the method doesn't exist, then the error will be caught and an error response shown - $this->response( - [ + $this->response([ $this->config->item('rest_status_field_name') => FALSE, $this->config->item('rest_message_field_name') => [ 'classname' => get_class($ex), 'message' => $ex->getMessage() ] - ], 500); + ], self:INTERNAL_SERVER_ERROR); } } @@ -624,7 +628,7 @@ public function response($data = NULL, $http_code = NULL, $continue = FALSE) // If data is NULL and no HTTP status code provided, then display, error and exit if ($data === NULL && $http_code === NULL) { - $http_code = 404; + $http_code = self::NOT_FOUND; } // If data is not NULL and a HTTP status code provided, then continue @@ -1716,11 +1720,10 @@ protected function _check_php_session() if (!$this->session->userdata($key)) { // Display an error response - $this->response( - [ + $this->response([ $this->config->item('rest_status_field_name') => FALSE, $this->config->item('rest_message_field_name') => 'Not Authorized' - ], 401); + ], self::UNAUTHORIZED); } } @@ -1819,11 +1822,10 @@ protected function _prepare_digest_auth() if (strcasecmp($digest['response'], $valid_response) !== 0) { // Display an error response - $this->response( - [ + $this->response([ $this->config->item('rest_status_field_name') => 0, $this->config->item('rest_message_field_name') => 'Invalid credentials' - ], 401); + ], self::UNAUTHORIZED); } } @@ -1842,12 +1844,11 @@ protected function _check_blacklist_auth() if (preg_match($pattern, $this->config->item('rest_ip_blacklist'))) { // Display an error response - $this->response( - [ + $this->response([ 'status' => FALSE, 'error' => 'IP Denied' ], - 401); + self::UNAUTHORIZED); } } @@ -1870,7 +1871,7 @@ protected function _check_whitelist_auth() if (in_array($this->input->ip_address(), $whitelist) === FALSE) { - $this->response([$this->config->item('rest_status_field_name') => FALSE, $this->config->item('rest_message_field_name') => 'IP not authorized'], 401); + $this->response([$this->config->item('rest_status_field_name') => FALSE, $this->config->item('rest_message_field_name') => 'IP not authorized'], self::UNAUTHORIZED); } } @@ -1901,11 +1902,10 @@ protected function _force_login($nonce = '') } // Display an error response - $this->response( - [ + $this->response([ $this->config->item('rest_status_field_name') => FALSE, $this->config->item('rest_message_field_name') => 'Not authorized' - ], 401); + ], self::UNAUTHORIZED); } /** From 8659531ee89a812ec46ddf34e808c7ba9699b2f4 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Mon, 13 Jul 2015 21:15:02 +0300 Subject: [PATCH 122/444] Updated CSV formatting methods This closes #484 --- application/libraries/Format.php | 106 +++++++++++++++++++++++++++---- 1 file changed, 92 insertions(+), 14 deletions(-) diff --git a/application/libraries/Format.php b/application/libraries/Format.php index 2cd73cb4..4692cfb3 100644 --- a/application/libraries/Format.php +++ b/application/libraries/Format.php @@ -296,6 +296,84 @@ public function to_html($data = NULL) return $this->_ci->table->generate(); } + /** + * @link http://www.metashock.de/2014/02/create-csv-file-in-memory-php/ + * @param mixed|NULL $data Optional data to pass, so as to override the data passed + * to the constructor + * @param string $delimiter The optional delimiter parameter sets the field + * delimiter (one character only). NULL will use the default value (,) + * @param string $enclosure The optional enclosure parameter sets the field + * enclosure (one character only). NULL will use the default value (") + * @return string A csv string + */ + public function to_csv($data = NULL, $delimiter = ',', $enclosure = '"') + { + // Use a threshold of 1 MB (1024 * 1024) + $handle = fopen('php://temp/maxmemory:1048576', 'w'); + if ($handle === FALSE) + { + return NULL; + } + + // If no data is passed as a parameter, then use the data passed + // via the constructor + if ($data === NULL && func_num_args() === 0) + { + $data = $this->_data; + } + + // If NULL, then set as the default delimiter + if ($delimiter === NULL) + { + $delimiter = ','; + } + + // If NULL, then set as the default enclosure + if ($enclosure === NULL) + { + $enclosure = '"'; + } + + // Cast as an array if not already + if (is_array($data) === FALSE) + { + $data = (array) $data; + } + + // Check if it's a multi-dimensional array + if (isset($data[0]) && count($data) !== count($data, COUNT_RECURSIVE)) + { + // Multi-dimensional array + $headings = array_keys($data[0]); + } + else + { + // Single array + $headings = array_keys($data); + $data = [$data]; + } + + // Apply the headings + fputcsv($handle, $headers, $delimiter, $enclosure); + + foreach ($data as $record) + { + // Returns the length of the string written or FALSE + fputcsv($handle, $record, $delimiter, $enclosure); + } + + // Reset the file pointer + rewind($handle); + + // Retrieve the csv contents + $csv = stream_get_contents($handle); + + // Close the handle + fclose($handle); + + return $csv; + } + /** * Format data as CSV * @@ -430,28 +508,28 @@ protected function _from_xml($data) /** * @param string $data CSV string + * @param string $delimiter The optional delimiter parameter sets the field + * delimiter (one character only). NULL will use the default value (,) + * @param string $enclosure The optional enclosure parameter sets the field + * enclosure (one character only). NULL will use the default value (") * @return array A multi-dimensional array with the outer array being the number of rows * and the inner arrays the individual fields */ - protected function _from_csv($data) + protected function _from_csv($data, $delimiter = ',', $enclosure = '"') { - $array = []; - - // Splits - $rows = explode(PHP_EOL, trim($data)); - $headings = explode(',', array_shift($rows)); - foreach ($rows as $row) + // If NULL, then set as the default delimiter + if ($delimiter === NULL) { - // The substr removes " from start and end - $data_fields = explode('","', trim(substr($row, 1, -1))); + $delimiter = ','; + } - if (count($data_fields) === count($headings)) - { - $array[] = array_combine($headings, $data_fields); - } + // If NULL, then set as the default enclosure + if ($enclosure === NULL) + { + $enclosure = '"'; } - return $array; + return str_getcsv($data, $delimiter, $enclosure); } /** From 8021fc4c389e1ea43adbd98526d2c1d82f0a1f06 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Mon, 13 Jul 2015 21:17:53 +0300 Subject: [PATCH 123/444] Fixed incorrect format --- 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 4692cfb3..a0d0857c 100644 --- a/application/libraries/Format.php +++ b/application/libraries/Format.php @@ -49,7 +49,7 @@ class Format { /** * Default format of this class */ - const DEFAULT_FORMAT = self::JSON; // Couldn't be DEFAULT, as this is a keyword + const DEFAULT_FORMAT = self::JSON_FORMAT; // Couldn't be DEFAULT, as this is a keyword /** * CodeIgniter instance From 45c135371cfdac94190734854c126412537abfbe Mon Sep 17 00:00:00 2001 From: softwarespot Date: Mon, 13 Jul 2015 21:28:46 +0300 Subject: [PATCH 124/444] Using constants in the REST_Controller --- application/controllers/api/Example.php | 14 ++++++------- application/controllers/api/Key.php | 26 ++++++++++++------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/application/controllers/api/Example.php b/application/controllers/api/Example.php index 570a2a57..e9f58070 100644 --- a/application/controllers/api/Example.php +++ b/application/controllers/api/Example.php @@ -35,7 +35,7 @@ function user_get() { if (!$this->get('id')) { - $this->response(NULL, 400); + $this->response(NULL, REST_Controller::BAD_REQUEST); } // $user = $this->some_model->getSomething( $this->get('id') ); @@ -49,12 +49,12 @@ function user_get() if ($user) { - $this->response($user, 200); // 200 being the HTTP response code + $this->response($user, REST_Controller::OK); // OK (200) being the HTTP response code } else { - $this->response(['error' => 'User could not be found'], 404); + $this->response(['error' => 'User could not be found'], REST_Controller::NOT_FOUND); } } @@ -68,7 +68,7 @@ function user_post() 'message' => 'Added a resource' ]; - $this->response($message, 201); // 201 being the HTTP response code + $this->response($message, REST_Controller::CREATED); // CREATED (201) being the HTTP response code } function user_delete() @@ -79,7 +79,7 @@ function user_delete() 'message' => 'Deleted the resource' ]; - $this->response($message, 204); // 204 being the HTTP response code + $this->response($message, REST_Controller::NO_CONTENT); // NO_CONTENT (204) being the HTTP response code } function users_get() @@ -93,12 +93,12 @@ function users_get() if ($users) { - $this->response($users, 200); // 200 being the HTTP response code + $this->response($users, REST_Controller::OK); // OK (200) being the HTTP response code } else { - $this->response(['error' => 'Couldn\'t find any users!'], 404); + $this->response(['error' => 'Couldn\'t find any users!'], REST_Controller::NOT_FOUND); } } diff --git a/application/controllers/api/Key.php b/application/controllers/api/Key.php index 4e9a3834..b303c9c7 100644 --- a/application/controllers/api/Key.php +++ b/application/controllers/api/Key.php @@ -47,14 +47,14 @@ public function index_put() $this->response([ 'status' => 1, 'key' => $key - ], 201); // 201 = Created + ], REST_Controller::CREATED); // CREATED (201) being the HTTP response code } else { $this->response([ 'status' => 0, 'error' => 'Could not save the key' - ], 500); // 500 = Internal Server Error + ], REST_Controller::INTERNAL_SERVER_ERROR); // INTERNAL_SERVER_ERROR (500) being the HTTP response code } } @@ -77,7 +77,7 @@ public function index_delete() // It doesn't appear the key exists $this->response([ 'error' => 'Invalid API Key' - ], 400); // 400 = Bad Request + ], REST_Controller::BAD_REQUEST); // BAD_REQUEST (400) being the HTTP response code } // Destroy it @@ -87,7 +87,7 @@ public function index_delete() $this->response([ 'status' => 1, 'success' => 'API Key was deleted' - ], 204); // 204 = Success, No Content + ], REST_Controller::NO_CONTENT); // NO_CONTENT (204) being the HTTP response code } // -------------------------------------------------------------------- @@ -110,7 +110,7 @@ public function level_post() // It doesn't appear the key exists $this->response([ 'error' => 'Invalid API Key' - ], 400); // 400 = Bad Request + ], REST_Controller::BAD_REQUEST); // BAD_REQUEST (400) being the HTTP response code } // Update the key level @@ -119,14 +119,14 @@ public function level_post() $this->response([ 'status' => 1, 'success' => 'API Key was updated' - ], 200); // 200 = OK + ], REST_Controller::OK); // OK (200) being the HTTP response code } else { $this->response([ 'status' => 0, 'error' => 'Could not update the key level' - ], 500); // 500 = Internal Server Error + ], REST_Controller::INTERNAL_SERVER_ERROR); // INTERNAL_SERVER_ERROR (500) being the HTTP response code } } @@ -149,7 +149,7 @@ public function suspend_post() // It doesn't appear the key exists $this->response([ 'error' => 'Invalid API Key' - ], 400); // 400 = Bad Request + ], REST_Controller::BAD_REQUEST); // BAD_REQUEST (400) being the HTTP response code } // Update the key level @@ -158,14 +158,14 @@ public function suspend_post() $this->response([ 'status' => 1, 'success' => 'Key was suspended' - ], 200); // 200 = OK + ], REST_Controller::OK); // OK (200) being the HTTP response code } else { $this->response([ 'status' => 0, 'error' => 'Could not suspend the user' - ], 500); // 500 = Internal Server Error + ], REST_Controller::INTERNAL_SERVER_ERROR); // INTERNAL_SERVER_ERROR (500) being the HTTP response code } } @@ -189,7 +189,7 @@ public function regenerate_post() // It doesn't appear the key exists $this->response([ 'error' => 'Invalid API Key' - ], 400); // 400 = Bad Request + ], REST_Controller::BAD_REQUEST); // BAD_REQUEST (400) being the HTTP response code } // Build a new key @@ -204,14 +204,14 @@ public function regenerate_post() $this->response([ 'status' => 1, 'key' => $new_key - ], 201); // 201 = Created + ], REST_Controller::CREATED); // CREATED (201) being the HTTP response code } else { $this->response([ 'status' => 0, 'error' => 'Could not save the key' - ], 500); // 500 = Internal Server Error + ], REST_Controller::INTERNAL_SERVER_ERROR); // INTERNAL_SERVER_ERROR (500) being the HTTP response code } } From 7a42fafc6e3efa1d2963953f859f72d3554adae7 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Mon, 13 Jul 2015 21:30:15 +0300 Subject: [PATCH 125/444] Tidied database calls --- application/controllers/api/Key.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/application/controllers/api/Key.php b/application/controllers/api/Key.php index b303c9c7..7577c4d8 100644 --- a/application/controllers/api/Key.php +++ b/application/controllers/api/Key.php @@ -245,14 +245,19 @@ private function _generate_key() private function _get_key($key) { - return $this->db->where(config_item('rest_key_column'), $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(config_item('rest_key_column'), $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; } // -------------------------------------------------------------------- From cf824a6a7d7c9068269ac620b3ec190b127c0878 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Mon, 13 Jul 2015 21:36:25 +0300 Subject: [PATCH 126/444] Fixed missing colon for calling class-level constants --- application/libraries/REST_Controller.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index 5dfef0a0..a456a05e 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -560,7 +560,7 @@ public function _remap($object_called, $arguments) if ($this->config->item('rest_enable_limits') && $this->_check_limit($controller_method) === FALSE) { $response = [$this->config->item('rest_status_field_name') => FALSE, $this->config->item('rest_message_field_name') => 'This API key has reached the time limit for this method.']; - $this->response($response, self:UNAUTHORIZED); + $this->response($response, self::UNAUTHORIZED); } // If no level is set use 0, they probably aren't using permissions @@ -577,7 +577,7 @@ public function _remap($object_called, $arguments) // They don't have good enough perms $response = [$this->config->item('rest_status_field_name') => FALSE, $this->config->item('rest_message_field_name') => 'This API key does not have enough permissions.']; - $authorized || $this->response($response, self:UNAUTHORIZED); + $authorized || $this->response($response, self::UNAUTHORIZED); } // No key stuff, but record that stuff is happening @@ -600,7 +600,7 @@ public function _remap($object_called, $arguments) 'classname' => get_class($ex), 'message' => $ex->getMessage() ] - ], self:INTERNAL_SERVER_ERROR); + ], self::INTERNAL_SERVER_ERROR); } } From bd68661d16645847c2b78a17f0e41d2c8c02d7c3 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Mon, 13 Jul 2015 21:36:40 +0300 Subject: [PATCH 127/444] Removed redundant method --- application/libraries/Format.php | 45 -------------------------------- 1 file changed, 45 deletions(-) diff --git a/application/libraries/Format.php b/application/libraries/Format.php index a0d0857c..6ec4a224 100644 --- a/application/libraries/Format.php +++ b/application/libraries/Format.php @@ -374,51 +374,6 @@ public function to_csv($data = NULL, $delimiter = ',', $enclosure = '"') return $csv; } - /** - * Format data as CSV - * - * @param mixed|NULL $data Optional data to pass, so as to override the data passed - * to the constructor - * @return mixed - */ - public function to_csv($data = NULL) - { - // If no data is passed as a parameter, then use the data passed - // via the constructor - if ($data === NULL && func_num_args() === 0) - { - $data = $this->_data; - } - - // Cast as an array if not already - if (is_array($data) === FALSE) - { - $data = (array) $data; - } - - // Multi-dimensional array - if (isset($data[0]) && is_array($data[0])) - { - $headings = array_keys($data[0]); - } - - // Single array - else - { - $headings = array_keys($data); - $data = [$data]; - } - - $output = '"' . implode('","', $headings) . '"' . PHP_EOL; - foreach ($data as &$row) - { - $row = str_replace('"', '""', $row); // Escape dbl quotes per RFC 4180 - $output .= '"' . implode('","', $row) . '"' . PHP_EOL; - } - - return $output; - } - /** * Encode data as json * From 13391e9df9e36ddb6c664538756f8b4f24b6977d Mon Sep 17 00:00:00 2001 From: softwarespot Date: Tue, 14 Jul 2015 09:07:15 +0300 Subject: [PATCH 128/444] Fixed incorrect access type --- 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 a456a05e..bb9407e9 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -1286,7 +1286,7 @@ protected function _parse_delete() * @access protected * @return void */ - public function _parse_query() + protected function _parse_query() { // Declare a variable that will hold the REQUEST_URI $request_uri = NULL; From eb627ee14259894094a19d2e4d7c0c4b04724ad1 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Tue, 14 Jul 2015 09:19:02 +0300 Subject: [PATCH 129/444] Changed access level of method --- 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 bb9407e9..c3f437db 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -712,10 +712,10 @@ public function response($data = NULL, $http_code = NULL, $continue = FALSE) /** * Get the input format e.g. json or xml * - * @access private + * @access protected * @return string|NULL Supported input format; otherwise, NULL */ - private function _detect_input_format() + protected function _detect_input_format() { // Get the CONTENT-TYPE value from the SERVER variable $contentType = $this->input->server('CONTENT_TYPE'); From b94fa069c6a8a7fd43b91d1eddaa5982846661c2 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Tue, 14 Jul 2015 10:02:51 +0300 Subject: [PATCH 130/444] Slight efficency increase --- application/libraries/REST_Controller.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php index c3f437db..0c7b2f58 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -774,9 +774,12 @@ protected function _detect_output_format() return $matches[1]; } + + // Get the format and convert to lowercase + $format = strtolower($this->_get_args['format']); // A format has been passed as an argument in the URL and it is supported - if (isset($this->_get_args['format']) && array_key_exists($this->_get_args['format'], $this->_supported_formats)) + if ($format !== FALSE && isset($this->_supported_formats[$format])) { return $this->_get_args['format']; } From 1b4590e8e3f655f5bc25436aaa8f4676d34ccdda Mon Sep 17 00:00:00 2001 From: softwarespot Date: Tue, 14 Jul 2015 10:14:38 +0300 Subject: [PATCH 131/444] Fixed error from previous commit --- 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 0c7b2f58..f00e546d 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -774,14 +774,14 @@ protected function _detect_output_format() return $matches[1]; } - - // Get the format and convert to lowercase - $format = strtolower($this->_get_args['format']); + + // Get the format + $format = isset($this->_get_args['format']) ? strtolower($this->_get_args['format']) : NULL; // A format has been passed as an argument in the URL and it is supported - if ($format !== FALSE && isset($this->_supported_formats[$format])) + if ($format !== NULL && isset($this->_supported_formats[$format])) { - return $this->_get_args['format']; + return $format; } // Otherwise, check the HTTP_ACCEPT (if it exists and we are allowed) From cb3b29af5ec4aa642d3edf927c36bd3add0a8f84 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Tue, 14 Jul 2015 10:16:22 +0300 Subject: [PATCH 132/444] Checking type and value --- 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 f00e546d..cf1f3f44 100644 --- a/application/libraries/REST_Controller.php +++ b/application/libraries/REST_Controller.php @@ -803,13 +803,13 @@ protected function _detect_output_format() else { // If it is truly HTML, it wont want any XML - if ($format == 'html' && strpos($this->input->server('HTTP_ACCEPT'), 'xml') === FALSE) + if ($format === 'html' && strpos($this->input->server('HTTP_ACCEPT'), 'xml') === FALSE) { return $format; } // If it is truly XML, it wont want any HTML - elseif ($format == 'xml' && strpos($this->input->server('HTTP_ACCEPT'), 'html') === FALSE) + elseif ($format === 'xml' && strpos($this->input->server('HTTP_ACCEPT'), 'html') === FALSE) { return $format; } From fef68358d230e2be5f0f97a837b3f468b2149a39 Mon Sep 17 00:00:00 2001 From: Tjoosten Date: Wed, 15 Jul 2015 16:19:23 +0200 Subject: [PATCH 133/444] PR: #475 regenerate documentation --- documentation/404.html | 15 +- documentation/class-Example.html | 57 +- documentation/class-Format.html | 367 +- documentation/class-Key.html | 95 +- documentation/class-REST_Controller.html | 676 ++-- documentation/elementlist.js | 2 +- .../function-_perform_ldap_auth.html | 135 - documentation/index.html | 26 +- .../package-CodeIgniter.Libraries.html | 28 +- ...ree.html => package-CodeIgniter.Rest.html} | 59 +- documentation/package-CodeIgniter.html | 18 +- documentation/package-None.html | 29 +- documentation/source-class-Example.html | 190 +- documentation/source-class-Format.html | 760 ++-- documentation/source-class-Key.html | 527 +-- .../source-class-REST_Controller.html | 3308 +++++++++-------- .../source-function-_perform_ldap_auth.html | 205 - documentation/todo.html | 153 - 18 files changed, 3378 insertions(+), 3272 deletions(-) delete mode 100644 documentation/function-_perform_ldap_auth.html rename documentation/{tree.html => package-CodeIgniter.Rest.html} (58%) delete mode 100644 documentation/source-function-_perform_ldap_auth.html delete mode 100644 documentation/todo.html diff --git a/documentation/404.html b/documentation/404.html index 762f8381..d40111fb 100644 --- a/documentation/404.html +++ b/documentation/404.html @@ -29,6 +29,11 @@

Packages

Libraries + +
  • + + Rest +
  • @@ -45,8 +50,6 @@

    Packages

    Classes

    @@ -80,12 +83,6 @@

    Classes

    @@ -95,7 +92,7 @@

    Classes

    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.

    +

    To continue please try to find requested page in the menu, or use search field on the top.

    @@ -163,23 +158,25 @@

    Class Example

    # - __construct( ) + __construct( string $config,… )
    -

    Constructor function

    +

    Constructor for the REST API

    @@ -74,45 +76,38 @@

    Functions

  • Overview
  • -
  • +
  • Package
  • - Function + Class
    • -
    • -Tree
    • -
    • - Todo -
    -
    -

    Tree

    +
    +

    Package CodeIgniter\Rest

    -

    Classes

    -
    -
      -
    • CI_Controller - -
      -
    • + + + + + + + + + + +
      Classes summary
      Example

      This is an example of a few basic user interaction methods you could use +all done with a hardcoded array

      Key

      Keys Controller +This is a basic Key Management REST controller to make and delete keys

      -
    • Format - - -
      -
    • - -
    -
    @@ -124,6 +119,6 @@

    Classes

    - + diff --git a/documentation/package-CodeIgniter.html b/documentation/package-CodeIgniter.html index 0b48c862..1cb20267 100644 --- a/documentation/package-CodeIgniter.html +++ b/documentation/package-CodeIgniter.html @@ -28,6 +28,11 @@

    Packages

    Libraries + +
  • + + Rest +
  • @@ -68,16 +73,10 @@

    Packages

  • Package
  • - Function + Class
    @@ -91,6 +90,9 @@

    Package CodeIgniter

    CodeIgniter\Libraries + + CodeIgniter\Rest + @@ -107,6 +109,6 @@

    Package CodeIgniter

    - + diff --git a/documentation/package-None.html b/documentation/package-None.html index 9909f104..4f19a520 100644 --- a/documentation/package-None.html +++ b/documentation/package-None.html @@ -28,6 +28,11 @@

    Packages

    Libraries + +
  • + + Rest +
  • @@ -51,10 +56,6 @@

    Classes

    -

    Functions

    - @@ -77,16 +78,10 @@

    Functions

  • Package
  • - Function + Class
    @@ -101,7 +96,8 @@

    No package

    Classes summary Format - Format class +

    Format class +Help convert between various formats such as XML, JSON, CSV, etc.

    @@ -109,13 +105,6 @@

    No package

    - - - - - - -
    Functions summary
    _perform_ldap_auth
    - + diff --git a/documentation/source-class-Example.html b/documentation/source-class-Example.html index bc0f03a7..cdeda9a8 100644 --- a/documentation/source-class-Example.html +++ b/documentation/source-class-Example.html @@ -4,7 +4,7 @@ - File application/controllers/api/example.php + File application/controllers/api/Example.php @@ -29,6 +29,11 @@

    Packages

    Libraries + +
  • + + Rest +
  • @@ -52,10 +57,6 @@

    Classes

    -

    Functions

    - @@ -79,129 +80,130 @@

    Functions

    Package
  • - Function + Class
    -
      1: <?php defined('BASEPATH') OR exit('No direct script access allowed');
    +
      1: <?php
       2: 
    -  3: /**
    -  4:  * Example
    -  5:  *
    -  6:  * This is an example of a few basic user interaction methods you could use
    -  7:  * all done with a hardcoded array.
    -  8:  *
    -  9:  * @package     CodeIgniter
    - 10:  * @subpackage  Rest Server
    - 11:  * @category    Controller
    - 12:  * @author      Phil Sturgeon
    - 13:  * @link        http://philsturgeon.co.uk/code/
    - 14: */
    - 15: 
    - 16: // This can be removed if you use __autoload() in config.php OR use Modular Extensions
    - 17: require APPPATH.'/libraries/REST_Controller.php';
    - 18: 
    - 19: class Example extends REST_Controller
    - 20: {
    +  3: defined('BASEPATH') OR exit('No direct script access allowed');
    +  4: 
    +  5: // This can be removed if you use __autoload() in config.php OR use Modular Extensions
    +  6: require APPPATH . '/libraries/REST_Controller.php';
    +  7: 
    +  8: /**
    +  9:  * This is an example of a few basic user interaction methods you could use
    + 10:  * all done with a hardcoded array
    + 11:  *
    + 12:  * @package         CodeIgniter
    + 13:  * @subpackage      Rest Server
    + 14:  * @category        Controller
    + 15:  * @author          Phil Sturgeon, Chris Kacerguis
    + 16:  * @license         MIT
    + 17:  * @link            https://github.com/chriskacerguis/codeigniter-restserver
    + 18:  */
    + 19: class Example extends REST_Controller {
    + 20: 
      21:     function __construct()
      22:     {
    - 23:         // Construct our parent class
    + 23:         // Construct the parent class
      24:         parent::__construct();
    - 25:         
    - 26:         // Configure limits on our controller methods. Ensure
    - 27:         // you have created the 'limits' table and enabled 'limits'
    + 25: 
    + 26:         // Configure limits on our controller methods
    + 27:         // Ensure you have created the 'limits' table and enabled 'limits'
      28:         // within application/config/rest.php
      29:         $this->methods['user_get']['limit'] = 500; //500 requests per hour per user/key
      30:         $this->methods['user_post']['limit'] = 100; //100 requests per hour per user/key
      31:         $this->methods['user_delete']['limit'] = 50; //50 requests per hour per user/key
      32:     }
    - 33:     
    + 33: 
      34:     function user_get()
      35:     {
    - 36:         if(!$this->get('id'))
    + 36:         if (!$this->get('id'))
      37:         {
      38:             $this->response(NULL, 400);
      39:         }
      40: 
      41:         // $user = $this->some_model->getSomething( $this->get('id') );
    - 42:         $users = array(
    - 43:             1 => array('id' => 1, 'name' => 'Some Guy', 'email' => 'example1@example.com', 'fact' => 'Loves swimming'),
    - 44:             2 => array('id' => 2, 'name' => 'Person Face', 'email' => 'example2@example.com', 'fact' => 'Has a huge face'),
    - 45:             3 => array('id' => 3, 'name' => 'Scotty', 'email' => 'example3@example.com', 'fact' => 'Is a Scott!', array('hobbies' => array('fartings', 'bikes'))),
    - 46:         );
    - 47:         
    + 42:         $users = [
    + 43:             1 => ['id' => 1, 'name' => 'John', 'email' => 'john@example.com', 'fact' => 'Loves coding'],
    + 44:             2 => ['id' => 2, 'name' => 'Jim', 'email' => 'jim@example.com', 'fact' => 'Developed on CodeIgniter'],
    + 45:             3 => ['id' => 3, 'name' => 'Jane', 'email' => 'jane@example.com', 'fact' => 'Lives in the USA', ['hobbies' => ['guitar', 'cycling']]],
    + 46:         ];
    + 47: 
      48:         $user = @$users[$this->get('id')];
    - 49:         
    - 50:         if($user)
    + 49: 
    + 50:         if ($user)
      51:         {
      52:             $this->response($user, 200); // 200 being the HTTP response code
      53:         }
      54: 
      55:         else
      56:         {
    - 57:             $this->response(array('error' => 'User could not be found'), 404);
    + 57:             $this->response(['error' => 'User could not be found'], 404);
      58:         }
      59:     }
    - 60:     
    + 60: 
      61:     function user_post()
      62:     {
    - 63:         //$this->some_model->updateUser( $this->get('id') );
    - 64:         $message = array('id' => $this->get('id'), 'name' => $this->post('name'), 'email' => $this->post('email'), 'message' => 'ADDED!');
    - 65:         
    - 66:         $this->response($message, 200); // 200 being the HTTP response code
    - 67:     }
    - 68:     
    - 69:     function user_delete()
    - 70:     {
    - 71:         //$this->some_model->deletesomething( $this->get('id') );
    - 72:         $message = array('id' => $this->get('id'), 'message' => 'DELETED!');
    - 73:         
    - 74:         $this->response($message, 200); // 200 being the HTTP response code
    - 75:     }
    - 76:     
    - 77:     function users_get()
    - 78:     {
    - 79:         //$users = $this->some_model->getSomething( $this->get('limit') );
    - 80:         $users = array(
    - 81:             array('id' => 1, 'name' => 'Some Guy', 'email' => 'example1@example.com'),
    - 82:             array('id' => 2, 'name' => 'Person Face', 'email' => 'example2@example.com'),
    - 83:             3 => array('id' => 3, 'name' => 'Scotty', 'email' => 'example3@example.com', 'fact' => array('hobbies' => array('fartings', 'bikes'))),
    - 84:         );
    - 85:         
    - 86:         if($users)
    - 87:         {
    - 88:             $this->response($users, 200); // 200 being the HTTP response code
    - 89:         }
    - 90: 
    - 91:         else
    - 92:         {
    - 93:             $this->response(array('error' => 'Couldn\'t find any users!'), 404);
    - 94:         }
    - 95:     }
    - 96: 
    - 97: 
    - 98:     public function send_post()
    - 99:     {
    -100:         var_dump($this->request->body);
    -101:     }
    -102: 
    -103: 
    -104:     public function send_put()
    -105:     {
    -106:         var_dump($this->put('foo'));
    -107:     }
    -108: }
    +
    63: // $this->some_model->update_user($this->get('id')); + 64: $message = [ + 65: 'id' => $this->get('id'), + 66: 'name' => $this->post('name'), + 67: 'email' => $this->post('email'), + 68: 'message' => 'Added a resource' + 69: ]; + 70: + 71: $this->response($message, 201); // 201 being the HTTP response code + 72: } + 73: + 74: function user_delete() + 75: { + 76: // $this->some_model->delete_something($this->get(); + 77: $message = [ + 78: 'id' => $this->get('id'), + 79: 'message' => 'Deleted the resource' + 80: ]; + 81: + 82: $this->response($message, 204); // 204 being the HTTP response code + 83: } + 84: + 85: function users_get() + 86: { + 87: // $users = $this->some_model->get_something($this->get('limit')); + 88: $users = [ + 89: ['id' => 1, 'name' => 'John', 'email' => 'john@example.com', 'fact' => 'Loves coding'], + 90: ['id' => 2, 'name' => 'Jim', 'email' => 'jim@example.com', 'fact' => 'Developed on CodeIgniter'], + 91: 3 => ['id' => 3, 'name' => 'Jane', 'email' => 'jane@example.com', 'fact' => 'Lives in the USA', ['hobbies' => ['guitar', 'cycling']]], + 92: ]; + 93: + 94: if ($users) + 95: { + 96: $this->response($users, 200); // 200 being the HTTP response code + 97: } + 98: + 99: else +100: { +101: $this->response(['error' => 'Couldn\'t find any users!'], 404); +102: } +103: } +104: +105: public function send_post() +106: { +107: var_dump($this->request->body); +108: } +109: +110: public function send_put() +111: { +112: var_dump($this->put('foo')); +113: } +114: } +115:
    - + diff --git a/documentation/source-class-Format.html b/documentation/source-class-Format.html index bca9fc38..e769e16d 100644 --- a/documentation/source-class-Format.html +++ b/documentation/source-class-Format.html @@ -29,6 +29,11 @@

    Packages

    Libraries + +
  • + + Rest +
  • @@ -52,10 +57,6 @@

    Classes

    -

    Functions

    - @@ -79,340 +80,473 @@

    Functions

    Package
  • - Function + Class
      1: <?php
    -  2: /**
    -  3:  * Format class
    -  4:  *
    -  5:  * Help convert between various formats such as XML, JSON, CSV, etc.
    -  6:  *
    -  7:  * @author      Phil Sturgeon
    -  8:  * @license     http://philsturgeon.co.uk/code/dbad-license
    -  9:  */
    - 10: class Format {
    - 11: 
    - 12:     // Array to convert
    - 13:     protected $_data = array();
    - 14: 
    - 15:     // View filename
    - 16:     protected $_from_type = NULL;
    - 17: 
    - 18:     /**
    - 19:      * Returns an instance of the Format object.
    - 20:      *
    - 21:      *     echo $this->format->factory(array('foo' => 'bar'))->to_xml();
    - 22:      *
    - 23:      * @param   mixed  general date to be converted
    - 24:      * @param   string  data format the file was provided in
    - 25:      * @return  Factory
    - 26:      */
    - 27:     public function factory($data, $from_type = NULL)
    - 28:     {
    - 29:         // Stupid stuff to emulate the "new static()" stuff in this libraries PHP 5.3 equivalent
    - 30:         $class = __CLASS__;
    - 31:         return new $class($data, $from_type);
    - 32:     }
    - 33: 
    - 34:     /**
    - 35:      * Do not use this directly, call factory()
    - 36:      */
    - 37:     public function __construct($data = NULL, $from_type = NULL)
    - 38:     {
    - 39:         get_instance()->load->helper('inflector');
    - 40: 
    - 41:         // If the provided data is already formatted we should probably convert it to an array
    - 42:         if ($from_type !== NULL)
    - 43:         {
    - 44:             if (method_exists($this, '_from_' . $from_type))
    - 45:             {
    - 46:                 $data = call_user_func(array($this, '_from_' . $from_type), $data);
    - 47:             }
    +  2: 
    +  3: defined('BASEPATH') OR exit('No direct script access allowed');
    +  4: 
    +  5: /**
    +  6:  * Format class
    +  7:  * Help convert between various formats such as XML, JSON, CSV, etc.
    +  8:  *
    +  9:  * @author    Phil Sturgeon, Chris Kacerguis
    + 10:  * @license   http://www.dbad-license.org/
    + 11:  */
    + 12: class Format {
    + 13: 
    + 14:     /**
    + 15:      * CodeIgniter instance
    + 16:      *
    + 17:      * @var object
    + 18:      */
    + 19:     private $_ci;
    + 20: 
    + 21:     /**
    + 22:      * Data to parse
    + 23:      *
    + 24:      * @var mixed
    + 25:      */
    + 26:     protected $_data = [];
    + 27: 
    + 28:     /**
    + 29:      * Type to convert from
    + 30:      *
    + 31:      * @var string
    + 32:      */
    + 33:     protected $_from_type = NULL;
    + 34: 
    + 35:     /**
    + 36:      * DO NOT CALL THIS DIRECTLY, USE factory()
    + 37:      *
    + 38:      * @param NULL $data
    + 39:      * @param NULL $from_type
    + 40:      *
    + 41:      * @throws Exception
    + 42:      */
    + 43: 
    + 44:     public function __construct($data = NULL, $from_type = NULL)
    + 45:     {
    + 46:         // Get the CodeIgniter reference
    + 47:         $this->_ci = &get_instance();
      48: 
    - 49:             else
    - 50:             {
    - 51:                 throw new Exception('Format class does not support conversion from "' . $from_type . '".');
    - 52:             }
    - 53:         }
    - 54: 
    - 55:         $this->_data = $data;
    - 56:     }
    - 57: 
    - 58:     // FORMATING OUTPUT ---------------------------------------------------------
    - 59: 
    - 60:     public function to_array($data = NULL)
    - 61:     {
    - 62:         // If not just NULL, but nothing is provided
    - 63:         if ($data === NULL and ! func_num_args())
    - 64:         {
    - 65:             $data = $this->_data;
    - 66:         }
    - 67: 
    - 68:         $array = array();
    - 69: 
    - 70:         foreach ((array) $data as $key => $value)
    - 71:         {
    - 72:             if (is_object($value) or is_array($value))
    - 73:             {
    - 74:                 $array[$key] = $this->to_array($value);
    - 75:             }
    - 76: 
    - 77:             else
    - 78:             {
    - 79:                 $array[$key] = $value;
    - 80:             }
    - 81:         }
    + 49:         // Load the inflector helper
    + 50:         $this->_ci->load->helper('inflector');
    + 51: 
    + 52:         // If the provided data is already formatted we should probably convert it to an array
    + 53:         if ($from_type !== NULL)
    + 54:         {
    + 55:             if (method_exists($this, '_from_' . $from_type))
    + 56:             {
    + 57:                 $data = call_user_func([$this, '_from_' . $from_type], $data);
    + 58:             }
    + 59:             else
    + 60:             {
    + 61:                 throw new Exception('Format class does not support conversion from "' . $from_type . '".');
    + 62:             }
    + 63:         }
    + 64: 
    + 65:         // Set the member variable to the data passed
    + 66:         $this->_data = $data;
    + 67:     }
    + 68: 
    + 69:     /**
    + 70:      * Create an instance of the format class
    + 71:      * e.g: echo $this->format->factory(['foo' => 'bar'])->to_csv();
    + 72:      *
    + 73:      * @param mixed $data Data to convert/parse
    + 74:      * @param string $from_type Type to convert from e.g. json, csv, html
    + 75:      *
    + 76:      * @return object Instance of the format class
    + 77:      */
    + 78:     public function factory($data, $from_type = NULL)
    + 79:     {
    + 80:         // $class = __CLASS__;
    + 81:         // return new $class();
      82: 
    - 83:         return $array;
    + 83:         return new static($data, $from_type);
      84:     }
      85: 
    - 86:     // Format XML for output
    - 87:     public function to_xml($data = NULL, $structure = NULL, $basenode = 'xml')
    - 88:     {
    - 89:         if ($data === NULL and ! func_num_args())
    - 90:         {
    - 91:             $data = $this->_data;
    - 92:         }
    - 93: 
    - 94:         // turn off compatibility mode as simple xml throws a wobbly if you don't.
    - 95:         if (ini_get('zend.ze1_compatibility_mode') == 1)
    - 96:         {
    - 97:             ini_set('zend.ze1_compatibility_mode', 0);
    - 98:         }
    - 99: 
    -100:         if ($structure === NULL)
    + 86:     // FORMATTING OUTPUT ---------------------------------------------------------
    + 87: 
    + 88:     /**
    + 89:      * Format data as an array
    + 90:      *
    + 91:      * @param mixed|NULL $data Optional data to pass, so as to override the data passed
    + 92:      * to the constructor
    + 93:      *
    + 94:      * @return array Data parsed as an array; otherwise, an empty array
    + 95:      */
    + 96:     public function to_array($data = NULL)
    + 97:     {
    + 98:         // If no data is passed as a parameter, then use the data passed
    + 99:         // via the constructor
    +100:         if ($data === NULL && func_num_args() === 0)
     101:         {
    -102:             $structure = simplexml_load_string("<?xml version='1.0' encoding='utf-8'?><$basenode />");
    +102:             $data = $this->_data;
     103:         }
     104: 
    -105:         // Force it to be something useful
    -106:         if ( ! is_array($data) AND ! is_object($data))
    +105:         // Cast as an array if not already
    +106:         if (is_array($data) === FALSE)
     107:         {
     108:             $data = (array) $data;
     109:         }
     110: 
    -111:         foreach ($data as $key => $value)
    -112:         {
    -113: 
    -114:             //change false/true to 0/1
    -115:             if(is_bool($value))
    -116:             {
    -117:                 $value = (int) $value;
    -118:             }
    -119: 
    -120:             // no numeric keys in our xml please!
    -121:             if (is_numeric($key))
    -122:             {
    -123:                 // make string key...
    -124:                 $key = (singular($basenode) != $basenode) ? singular($basenode) : 'item';
    -125:             }
    +111:         $array = [];
    +112:         foreach ((array) $data as $key => $value)
    +113:         {
    +114:             if (is_object($value) === TRUE || is_array($value) === TRUE)
    +115:             {
    +116:                 $array[$key] = $this->to_array($value);
    +117:             }
    +118:             else
    +119:             {
    +120:                 $array[$key] = $value;
    +121:             }
    +122:         }
    +123: 
    +124:         return $array;
    +125:     }
     126: 
    -127:             // replace anything not alpha numeric
    -128:             $key = preg_replace('/[^a-z_\-0-9]/i', '', $key);
    -129: 
    -130:             if ($key === '_attributes' && (is_array($value) || is_object($value)))
    -131:             {
    -132:                 $attributes = $value;
    -133:                 if (is_object($attributes)) $attributes = get_object_vars($attributes);
    -134: 
    -135:                 foreach ($attributes as $attributeName => $attributeValue)
    -136:                 {
    -137:                     $structure->addAttribute($attributeName, $attributeValue);
    -138:                 }
    -139:             }
    -140:             // if there is another array found recursively call this function
    -141:             else if (is_array($value) || is_object($value))
    -142:             {
    -143:                 $node = $structure->addChild($key);
    -144: 
    -145:                 // recursive call.
    -146:                 $this->to_xml($value, $node, $key);
    -147:             }
    -148:             else
    -149:             {
    -150:                 // add single node.
    -151:                 $value = htmlspecialchars(html_entity_decode($value, ENT_QUOTES, 'UTF-8'), ENT_QUOTES, "UTF-8");
    -152: 
    -153:                 $structure->addChild($key, $value);
    -154:             }
    -155:         }
    -156: 
    -157:         return $structure->asXML();
    -158:     }
    -159: 
    -160:     // Format HTML for output
    -161:     public function to_html()
    -162:     {
    -163:         $data = (array)$this->_data;
    -164: 
    -165:         // Multi-dimensional array
    -166:         if (isset($data[0]) && is_array($data[0]))
    -167:         {
    -168:             $headings = array_keys($data[0]);
    -169:         }
    -170: 
    -171:         // Single array
    -172:         else
    -173:         {
    -174:             $headings = array_keys($data);
    -175:             $data = array($data);
    -176:         }
    -177: 
    -178:         $ci = get_instance();
    -179:         $ci->load->library('table');
    -180: 
    -181:         $ci->table->set_heading($headings);
    -182: 
    -183:         foreach ($data as &$row)
    -184:         {
    -185:             $ci->table->add_row($row);
    -186:         }
    +127:     /**
    +128:      * Format data as XML
    +129:      *
    +130:      * @param mixed|NULL $data Optional data to pass, so as to override the data passed
    +131:      * to the constructor
    +132:      * @param NULL $structure
    +133:      * @param string $basenode
    +134:      *
    +135:      * @return mixed
    +136:      */
    +137:     public function to_xml($data = NULL, $structure = NULL, $basenode = 'xml')
    +138:     {
    +139:         if ($data === NULL && func_num_args() === 0)
    +140:         {
    +141:             $data = $this->_data;
    +142:         }
    +143: 
    +144:         // turn off compatibility mode as simple xml throws a wobbly if you don't.
    +145:         if (ini_get('zend.ze1_compatibility_mode') == 1)
    +146:         {
    +147:             ini_set('zend.ze1_compatibility_mode', 0);
    +148:         }
    +149: 
    +150:         if ($structure === NULL)
    +151:         {
    +152:             $structure = simplexml_load_string("<?xml version='1.0' encoding='utf-8'?><$basenode />");
    +153:         }
    +154: 
    +155:         // Force it to be something useful
    +156:         if (is_array($data) === FALSE && is_object($data) === FALSE)
    +157:         {
    +158:             $data = (array) $data;
    +159:         }
    +160: 
    +161:         foreach ($data as $key => $value)
    +162:         {
    +163: 
    +164:             //change false/true to 0/1
    +165:             if (is_bool($value))
    +166:             {
    +167:                 $value = (int) $value;
    +168:             }
    +169: 
    +170:             // no numeric keys in our xml please!
    +171:             if (is_numeric($key))
    +172:             {
    +173:                 // make string key...
    +174:                 $key = (singular($basenode) != $basenode) ? singular($basenode) : 'item';
    +175:             }
    +176: 
    +177:             // replace anything not alpha numeric
    +178:             $key = preg_replace('/[^a-z_\-0-9]/i', '', $key);
    +179: 
    +180:             if ($key === '_attributes' && (is_array($value) || is_object($value)))
    +181:             {
    +182:                 $attributes = $value;
    +183:                 if (is_object($attributes))
    +184:                 {
    +185:                     $attributes = get_object_vars($attributes);
    +186:                 }
     187: 
    -188:         return $ci->table->generate();
    -189:     }
    -190: 
    -191:     // Format CSV for output
    -192:     public function to_csv()
    -193:     {
    -194:         $data = (array)$this->_data;
    -195: 
    -196:         // Multi-dimensional array
    -197:         if (isset($data[0]) && is_array($data[0]))
    -198:         {
    -199:             $headings = array_keys($data[0]);
    -200:         }
    -201: 
    -202:         // Single array
    -203:         else
    -204:         {
    -205:             $headings = array_keys($data);
    -206:             $data = array($data);
    -207:         }
    -208: 
    -209:         $output = '"'.implode('","', $headings).'"'.PHP_EOL;
    -210:         foreach ($data as &$row)
    -211:         {
    -212:                     $row    = str_replace('"', '""', $row); // Escape dbl quotes per RFC 4180
    -213:                     $output .= '"'.implode('","', $row).'"'.PHP_EOL;
    -214:         }
    -215: 
    -216:         return $output;
    -217:     }
    -218: 
    -219:     // Encode as JSON
    -220:     public function to_json()
    -221:     {
    -222:         $callback = isset($_GET['callback']) ? $_GET['callback'] : '';
    -223:         if ($callback === '')
    -224:         {
    -225:             return json_encode($this->_data);
    -226: 
    -227:             /* Had to take out this code, it doesn't work on Objects.
    -228:             $str = $this->_data;
    -229:             array_walk_recursive($str, function(&$item, $key)
    -230:             {
    -231:                 if(!mb_detect_encoding($item, 'utf-8', true))
    -232:                 {
    -233:                     $item = utf8_encode($item);
    -234:                 }
    -235:             });
    -236: 
    -237:             return json_encode($str);
    -238:             */
    -239:         }
    -240: 
    -241:         // we only honour jsonp callback which are valid javascript identifiers
    -242:         else if (preg_match('/^[a-z_\$][a-z0-9\$_]*(\.[a-z_\$][a-z0-9\$_]*)*$/i', $callback))
    -243:         {
    -244:             // this is a jsonp request, the content-type must be updated to be text/javascript
    -245:             header("Content-Type: application/javascript");
    -246:             return $callback . "(" . json_encode($this->_data) . ");";
    +188:                 foreach ($attributes as $attributeName => $attributeValue)
    +189:                 {
    +190:                     $structure->addAttribute($attributeName, $attributeValue);
    +191:                 }
    +192:             }
    +193:             // if there is another array found recursively call this function
    +194:             elseif (is_array($value) || is_object($value))
    +195:             {
    +196:                 $node = $structure->addChild($key);
    +197: 
    +198:                 // recursive call.
    +199:                 $this->to_xml($value, $node, $key);
    +200:             }
    +201:             else
    +202:             {
    +203:                 // add single node.
    +204:                 $value = htmlspecialchars(html_entity_decode($value, ENT_QUOTES, 'UTF-8'), ENT_QUOTES, 'UTF-8');
    +205: 
    +206:                 $structure->addChild($key, $value);
    +207:             }
    +208:         }
    +209: 
    +210:         return $structure->asXML();
    +211:     }
    +212: 
    +213:     /**
    +214:      * Format data as HTML
    +215:      *
    +216:      * @param mixed|NULL $data Optional data to pass, so as to override the data passed
    +217:      * to the constructor
    +218:      *
    +219:      * @return mixed
    +220:      */
    +221:     public function to_html($data = NULL)
    +222:     {
    +223:         // If no data is passed as a parameter, then use the data passed
    +224:         // via the constructor
    +225:         if ($data === NULL && func_num_args() === 0)
    +226:         {
    +227:             $data = $this->_data;
    +228:         }
    +229: 
    +230:         // Cast as an array if not already
    +231:         if (is_array($data) === FALSE)
    +232:         {
    +233:             $data = (array) $data;
    +234:         }
    +235: 
    +236:         // Multi-dimensional array
    +237:         if (isset($data[0]) && is_array($data[0]))
    +238:         {
    +239:             $headings = array_keys($data[0]);
    +240:         }
    +241: 
    +242:         // Single array
    +243:         else
    +244:         {
    +245:             $headings = array_keys($data);
    +246:             $data = [$data];
     247:         }
    -248:         else
    -249:         {
    -250:             // we have an invalid jsonp callback identifier, we'll return plain json with a warning field
    -251:             $this->_data['warning'] = "invalid jsonp callback provided: ".$callback;
    -252:             return json_encode($this->_data);
    -253:         }
    -254:     }
    -255: 
    -256:     // Encode as Serialized array
    -257:     public function to_serialized()
    -258:     {
    -259:         return serialize($this->_data);
    -260:     }
    -261: 
    -262:     // Output as a string representing the PHP structure
    -263:     public function to_php()
    -264:     {
    -265:         return var_export($this->_data, TRUE);
    -266:     }
    -267: 
    -268:     // Format XML for output
    -269:     protected function _from_xml($string)
    -270:     {
    -271:         return $string ? (array) simplexml_load_string($string, 'SimpleXMLElement', LIBXML_NOCDATA) : array();
    -272:     }
    -273: 
    -274:     // Format CSV for output
    -275:     // This function is DODGY! Not perfect CSV support but works with my REST_Controller
    -276:     protected function _from_csv($string)
    -277:     {
    -278:         $data = array();
    +248: 
    +249:         // Load the table library
    +250:         $this->_ci->load->library('table');
    +251: 
    +252:         $this->_ci->table->set_heading($headings);
    +253: 
    +254:         // Should row used as a reference?
    +255:         foreach ($data as &$row)
    +256:         {
    +257:             $this->_ci->table->add_row($row);
    +258:         }
    +259: 
    +260:         return $this->_ci->table->generate();
    +261:     }
    +262: 
    +263:     /**
    +264:      * Format data as CSV
    +265:      *
    +266:      * @param mixed|NULL $data Optional data to pass, so as to override the data passed
    +267:      * to the constructor
    +268:      *
    +269:      * @return mixed
    +270:      */
    +271:     public function to_csv($data = NULL)
    +272:     {
    +273:         // If no data is passed as a parameter, then use the data passed
    +274:         // via the constructor
    +275:         if ($data === NULL && func_num_args() === 0)
    +276:         {
    +277:             $data = $this->_data;
    +278:         }
     279: 
    -280:         // Splits
    -281:         $rows = explode("\n", trim($string));
    -282:         $headings = explode(',', array_shift($rows));
    -283:         foreach ($rows as $row)
    -284:         {
    -285:             // The substr removes " from start and end
    -286:             $data_fields = explode('","', trim(substr($row, 1, -1)));
    -287: 
    -288:             if (count($data_fields) == count($headings))
    -289:             {
    -290:                 $data[] = array_combine($headings, $data_fields);
    -291:             }
    -292:         }
    -293: 
    -294:         return $data;
    -295:     }
    -296: 
    -297:     // Encode as JSON
    -298:     private function _from_json($string)
    -299:     {
    -300:         return json_decode(trim($string));
    -301:     }
    -302: 
    -303:     // Encode as Serialized array
    -304:     private function _from_serialize($string)
    -305:     {
    -306:         return unserialize(trim($string));
    +280:         // Cast as an array if not already
    +281:         if (is_array($data) === FALSE)
    +282:         {
    +283:             $data = (array) $data;
    +284:         }
    +285: 
    +286:         // Multi-dimensional array
    +287:         if (isset($data[0]) && is_array($data[0]))
    +288:         {
    +289:             $headings = array_keys($data[0]);
    +290:         }
    +291: 
    +292:         // Single array
    +293:         else
    +294:         {
    +295:             $headings = array_keys($data);
    +296:             $data = [$data];
    +297:         }
    +298: 
    +299:         $output = '"' . implode('","', $headings) . '"' . PHP_EOL;
    +300:         foreach ($data as &$row)
    +301:         {
    +302:             $row = str_replace('"', '""', $row); // Escape dbl quotes per RFC 4180
    +303:             $output .= '"' . implode('","', $row) . '"' . PHP_EOL;
    +304:         }
    +305: 
    +306:         return $output;
     307:     }
     308: 
    -309:     // If you provide text/plain value on the Content-type header on a request
    -310:     // just return the string
    -311:     private function _from_php($string)
    -312:     {
    -313:         return trim($string);
    -314:     }
    -315: 
    -316: }
    -317: 
    -318: /* End of file format.php */
    -319: 
    +309: /** +310: * Encode data as json +311: * +312: * @param mixed|NULL $data Optional data to pass, so as to override the data passed +313: * to the constructor +314: * +315: * @return string Json representation of a value +316: */ +317: public function to_json($data = NULL) +318: { +319: // If no data is passed as a parameter, then use the data passed +320: // via the constructor +321: if ($data === NULL && func_num_args() === 0) +322: { +323: $data = $this->_data; +324: } +325: +326: // Get the callback parameter (if set) +327: $callback = $this->_ci->input->get('callback'); +328: +329: if (empty($callback) === TRUE) +330: { +331: return json_encode($data); +332: } +333: +334: // We only honour a jsonp callback which are valid javascript identifiers +335: elseif (preg_match('/^[a-z_\$][a-z0-9\$_]*(\.[a-z_\$][a-z0-9\$_]*)*$/i', $callback)) +336: { +337: // Return the data as encoded json with a callback +338: return $callback . '(' . json_encode($data) . ');'; +339: } +340: +341: // An invalid jsonp callback function provided. +342: // Though I don't believe this should be hardcoded here +343: $data['warning'] = 'INVALID JSONP CALLBACK: ' . $callback; +344: +345: return json_encode($data); +346: } +347: +348: /** +349: * Encode data as a serialized array +350: * +351: * @param mixed|NULL $data Optional data to pass, so as to override the data passed +352: * to the constructor +353: * +354: * @return string Serialized data +355: */ +356: public function to_serialized($data = NULL) +357: { +358: // If no data is passed as a parameter, then use the data passed +359: // via the constructor +360: if ($data === NULL && func_num_args() === 0) +361: { +362: $data = $this->_data; +363: } +364: +365: return serialize($data); +366: } +367: +368: /** +369: * Format data using a PHP structure +370: * +371: * @param mixed|NULL $data Optional data to pass, so as to override the data passed +372: * to the constructor +373: * +374: * @return mixed String representation of a variable +375: */ +376: public function to_php($data = NULL) +377: { +378: // If no data is passed as a parameter, then use the data passed +379: // via the constructor +380: if ($data === NULL && func_num_args() === 0) +381: { +382: $data = $this->_data; +383: } +384: +385: return var_export($data, TRUE); +386: } +387: +388: // INTERNAL FUNCTIONS +389: +390: /** +391: * @param $data XML string +392: * +393: * @return SimpleXMLElement XML element object; otherwise, empty array +394: */ +395: protected function _from_xml($data) +396: { +397: return $data ? (array) simplexml_load_string($data, 'SimpleXMLElement', LIBXML_NOCDATA) : []; +398: } +399: +400: /** +401: * @param string $data CSV string +402: * +403: * @return array A multi-dimensional array with the outer array being the number of rows +404: * and the inner arrays the individual fields +405: */ +406: protected function _from_csv($data) +407: { +408: $array = []; +409: +410: // Splits +411: $rows = explode("\n", trim($data)); +412: $headings = explode(',', array_shift($rows)); +413: foreach ($rows as $row) +414: { +415: // The substr removes " from start and end +416: $data_fields = explode('","', trim(substr($row, 1, -1))); +417: +418: if (count($data_fields) === count($headings)) +419: { +420: $array[] = array_combine($headings, $data_fields); +421: } +422: } +423: +424: return $array; +425: } +426: +427: /** +428: * @param $data Encoded json string +429: * +430: * @return mixed Decoded json string with leading and trailing whitespace removed +431: */ +432: protected function _from_json($data) +433: { +434: return json_decode(trim($data)); +435: } +436: +437: /** +438: * @param string Data to unserialized +439: * +440: * @return mixed Unserialized data +441: */ +442: protected function _from_serialize($data) +443: { +444: return unserialize(trim($data)); +445: } +446: +447: /** +448: * @param $data Data to trim leading and trailing whitespace +449: * +450: * @return string Data with leading and trailing whitespace removed +451: */ +452: protected function _from_php($data) +453: { +454: return trim($data); +455: } +456: +457: } +458: - + diff --git a/documentation/source-class-Key.html b/documentation/source-class-Key.html index f8223e28..d47c058c 100644 --- a/documentation/source-class-Key.html +++ b/documentation/source-class-Key.html @@ -4,7 +4,7 @@ - File application/controllers/api/key.php + File application/controllers/api/Key.php @@ -29,6 +29,11 @@

    Packages

    Libraries + +
  • + + Rest +
  • @@ -52,10 +57,6 @@

    Classes

    -

    Functions

    - @@ -79,273 +80,303 @@

    Functions

    Package
  • - Function + Class
    -
      1: <?php defined('BASEPATH') OR exit('No direct script access allowed');
    +
      1: <?php
       2: 
    -  3: /**
    -  4:  * Keys Controller
    -  5:  *
    -  6:  * This is a basic Key Management REST controller to make and delete keys.
    -  7:  *
    -  8:  * @package     CodeIgniter
    -  9:  * @subpackage  Rest Server
    - 10:  * @category    Controller
    - 11:  * @author      Phil Sturgeon
    - 12:  * @link        http://philsturgeon.co.uk/code/
    - 13: */
    - 14: 
    - 15: // This can be removed if you use __autoload() in config.php
    - 16: require(APPPATH.'/libraries/REST_Controller.php');
    - 17: 
    - 18: class Key extends REST_Controller
    - 19: {
    - 20:     protected $methods = array(
    - 21:         'index_put' => array('level' => 10, 'limit' => 10),
    - 22:         'index_delete' => array('level' => 10),
    - 23:         'level_post' => array('level' => 10),
    - 24:         'regenerate_post' => array('level' => 10),
    - 25:     );
    - 26: 
    - 27:     /**
    - 28:      * Key Create
    - 29:      *
    - 30:      * Insert a key into the database.
    +  3: defined('BASEPATH') OR exit('No direct script access allowed');
    +  4: 
    +  5: // This can be removed if you use __autoload() in config.php OR use Modular Extensions
    +  6: require APPPATH . '/libraries/REST_Controller.php';
    +  7: 
    +  8: /**
    +  9:  * Keys Controller
    + 10:  * This is a basic Key Management REST controller to make and delete keys
    + 11:  *
    + 12:  * @package         CodeIgniter
    + 13:  * @subpackage      Rest Server
    + 14:  * @category        Controller
    + 15:  * @author          Phil Sturgeon, Chris Kacerguis
    + 16:  * @license         MIT
    + 17:  * @link            https://github.com/chriskacerguis/codeigniter-restserver
    + 18:  */
    + 19: class Key extends REST_Controller {
    + 20: 
    + 21:     protected $methods = [
    + 22:             'index_put' => ['level' => 10, 'limit' => 10],
    + 23:             'index_delete' => ['level' => 10],
    + 24:             'level_post' => ['level' => 10],
    + 25:             'regenerate_post' => ['level' => 10],
    + 26:         ];
    + 27: 
    + 28:     /**
    + 29:      * Key Create
    + 30:      * Insert a key into the database
      31:      *
    - 32:      * @access  public
    - 33:      * @return  void
    + 32:      * @access    public
    + 33:      * @return    void
      34:      */
      35:     public function index_put()
      36:     {
      37:         // Build a new key
      38:         $key = self::_generate_key();
      39: 
    - 40:         // If no key level provided, give them a rubbish one
    + 40:         // If no key level provided, provide a generic key
      41:         $level = $this->put('level') ? $this->put('level') : 1;
    - 42:         $ignore_limits = $this->put('ignore_limits') ? $this->put('ignore_limits') : 1;
    + 42:         $ignore_limits = ctype_digit($this->put('ignore_limits')) ? (int) $this->put('ignore_limits') : 1;
      43: 
      44:         // Insert the new key
    - 45:         if (self::_insert_key($key, array('level' => $level, 'ignore_limits' => $ignore_limits)))
    + 45:         if (self::_insert_key($key, ['level' => $level, 'ignore_limits' => $ignore_limits]))
      46:         {
    - 47:             $this->response(array('status' => 1, 'key' => $key), 201); // 201 = Created
    - 48:         }
    - 49: 
    - 50:         else
    - 51:         {
    - 52:             $this->response(array('status' => 0, 'error' => 'Could not save the key.'), 500); // 500 = Internal Server Error
    - 53:         }
    - 54:     }
    - 55: 
    - 56:     // --------------------------------------------------------------------
    - 57: 
    - 58:     /**
    - 59:      * Key Delete
    - 60:      *
    - 61:      * Remove a key from the database to stop it working.
    - 62:      *
    - 63:      * @access  public
    - 64:      * @return  void
    - 65:      */
    - 66:     public function index_delete()
    - 67:     {
    - 68:         $key = $this->delete('key');
    - 69: 
    - 70:         // Does this key even exist?
    - 71:         if ( ! self::_key_exists($key))
    - 72:         {
    - 73:             // NOOOOOOOOO!
    - 74:             $this->response(array('status' => 0, 'error' => 'Invalid API Key.'), 400);
    - 75:         }
    - 76: 
    - 77:         // Kill it
    - 78:         self::_delete_key($key);
    - 79: 
    - 80:         // Tell em we killed it
    - 81:         $this->response(array('status' => 1, 'success' => 'API Key was deleted.'), 200);
    - 82:     }
    - 83: 
    - 84:     // --------------------------------------------------------------------
    + 47:             $this->response([
    + 48:                 'status' => 1,
    + 49:                 'key' => $key
    + 50:             ], 201); // 201 = Created
    + 51:         }
    + 52:         else
    + 53:         {
    + 54:             $this->response([
    + 55:                 'status' => 0,
    + 56:                 'error' => 'Could not save the key'
    + 57:             ], 500); // 500 = Internal Server Error
    + 58:         }
    + 59:     }
    + 60: 
    + 61:     // --------------------------------------------------------------------
    + 62: 
    + 63:     /**
    + 64:      * Key Delete
    + 65:      * Remove a key from the database to stop it working
    + 66:      *
    + 67:      * @access    public
    + 68:      * @return    void
    + 69:      */
    + 70:     public function index_delete()
    + 71:     {
    + 72:         $key = $this->delete('key');
    + 73: 
    + 74:         // Does this key exist?
    + 75:         if (!self::_key_exists($key))
    + 76:         {
    + 77:             // It doesn't appear the key exists
    + 78:             $this->response([
    + 79:                 'error' => 'Invalid API Key'
    + 80:             ], 400); // 400 = Bad Request
    + 81:         }
    + 82: 
    + 83:         // Destroy it
    + 84:         self::_delete_key($key);
      85: 
    - 86:     /**
    - 87:      * Update Key
    - 88:      *
    - 89:      * Change the level
    - 90:      *
    - 91:      * @access  public
    - 92:      * @return  void
    - 93:      */
    - 94:     public function level_post()
    - 95:     {
    - 96:         $key = $this->post('key');
    - 97:         $new_level = $this->post('level');
    - 98: 
    - 99:         // Does this key even exist?
    -100:         if ( ! self::_key_exists($key))
    -101:         {
    -102:             // NOOOOOOOOO!
    -103:             $this->response(array('error' => 'Invalid API Key.'), 400);
    -104:         }
    -105: 
    -106:         // Update the key level
    -107:         if (self::_update_key($key, array('level' => $new_level)))
    -108:         {
    -109:             $this->response(array('status' => 1, 'success' => 'API Key was updated.'), 200); // 200 = OK
    -110:         }
    -111: 
    -112:         else
    -113:         {
    -114:             $this->response(array('status' => 0, 'error' => 'Could not update the key level.'), 500); // 500 = Internal Server Error
    -115:         }
    -116:     }
    -117: 
    -118:     // --------------------------------------------------------------------
    -119: 
    -120:     /**
    -121:      * Update Key
    -122:      *
    -123:      * Change the level
    -124:      *
    -125:      * @access  public
    -126:      * @return  void
    -127:      */
    -128:     public function suspend_post()
    -129:     {
    -130:         $key = $this->post('key');
    -131: 
    -132:         // Does this key even exist?
    -133:         if ( ! self::_key_exists($key))
    -134:         {
    -135:             // NOOOOOOOOO!
    -136:             $this->response(array('error' => 'Invalid API Key.'), 400);
    -137:         }
    -138: 
    -139:         // Update the key level
    -140:         if (self::_update_key($key, array('level' => 0)))
    -141:         {
    -142:             $this->response(array('status' => 1, 'success' => 'Key was suspended.'), 200); // 200 = OK
    -143:         }
    -144: 
    -145:         else
    -146:         {
    -147:             $this->response(array('status' => 0, 'error' => 'Could not suspend the user.'), 500); // 500 = Internal Server Error
    -148:         }
    -149:     }
    -150: 
    -151:     // --------------------------------------------------------------------
    -152: 
    -153:     /**
    -154:      * Regenerate Key
    -155:      *
    -156:      * Remove a key from the database to stop it working.
    -157:      *
    -158:      * @access  public
    -159:      * @return  void
    -160:      */
    -161:     public function regenerate_post()
    -162:     {
    -163:         $old_key = $this->post('key');
    -164:         $key_details = self::_get_key($old_key);
    -165: 
    -166:         // The key wasnt found
    -167:         if ( ! $key_details)
    -168:         {
    -169:             // NOOOOOOOOO!
    -170:             $this->response(array('status' => 0, 'error' => 'Invalid API Key.'), 400);
    -171:         }
    -172: 
    -173:         // Build a new key
    -174:         $new_key = self::_generate_key();
    -175: 
    -176:         // Insert the new key
    -177:         if (self::_insert_key($new_key, array('level' => $key_details->level, 'ignore_limits' => $key_details->ignore_limits)))
    -178:         {
    -179:             // Suspend old key
    -180:             self::_update_key($old_key, array('level' => 0));
    -181: 
    -182:             $this->response(array('status' => 1, 'key' => $new_key), 201); // 201 = Created
    -183:         }
    -184: 
    -185:         else
    -186:         {
    -187:             $this->response(array('status' => 0, 'error' => 'Could not save the key.'), 500); // 500 = Internal Server Error
    -188:         }
    -189:     }
    -190: 
    -191:     // --------------------------------------------------------------------
    -192: 
    -193:     /* Helper Methods */
    -194:     
    -195:     private function _generate_key()
    -196:     {
    -197:         //$this->load->helper('security');
    -198:         
    -199:         do
    + 86:         // Respond that the key was destroyed
    + 87:         $this->response([
    + 88:             'status' => 1,
    + 89:             'success' => 'API Key was deleted'
    + 90:             ], 204); // 204 = Success, No Content
    + 91:     }
    + 92: 
    + 93:     // --------------------------------------------------------------------
    + 94: 
    + 95:     /**
    + 96:      * Update Key
    + 97:      * Change the level
    + 98:      *
    + 99:      * @access    public
    +100:      * @return    void
    +101:      */
    +102:     public function level_post()
    +103:     {
    +104:         $key = $this->post('key');
    +105:         $new_level = $this->post('level');
    +106: 
    +107:         // Does this key exist?
    +108:         if (!self::_key_exists($key))
    +109:         {
    +110:             // It doesn't appear the key exists
    +111:             $this->response([
    +112:                 'error' => 'Invalid API Key'
    +113:             ], 400); // 400 = Bad Request
    +114:         }
    +115: 
    +116:         // Update the key level
    +117:         if (self::_update_key($key, ['level' => $new_level]))
    +118:         {
    +119:             $this->response([
    +120:                 'status' => 1,
    +121:                 'success' => 'API Key was updated'
    +122:             ], 200); // 200 = OK
    +123:         }
    +124:         else
    +125:         {
    +126:             $this->response([
    +127:                 'status' => 0,
    +128:                 'error' => 'Could not update the key level'
    +129:             ], 500); // 500 = Internal Server Error
    +130:         }
    +131:     }
    +132: 
    +133:     // --------------------------------------------------------------------
    +134: 
    +135:     /**
    +136:      * Update Key
    +137:      * Change the level
    +138:      *
    +139:      * @access    public
    +140:      * @return    void
    +141:      */
    +142:     public function suspend_post()
    +143:     {
    +144:         $key = $this->post('key');
    +145: 
    +146:         // Does this key exist?
    +147:         if (!self::_key_exists($key))
    +148:         {
    +149:             // It doesn't appear the key exists
    +150:             $this->response([
    +151:                 'error' => 'Invalid API Key'
    +152:             ], 400); // 400 = Bad Request
    +153:         }
    +154: 
    +155:         // Update the key level
    +156:         if (self::_update_key($key, ['level' => 0]))
    +157:         {
    +158:             $this->response([
    +159:                 'status' => 1,
    +160:                 'success' => 'Key was suspended'
    +161:             ], 200); // 200 = OK
    +162:         }
    +163:         else
    +164:         {
    +165:             $this->response([
    +166:                 'status' => 0,
    +167:                 'error' => 'Could not suspend the user'
    +168:             ], 500); // 500 = Internal Server Error
    +169:         }
    +170:     }
    +171: 
    +172:     // --------------------------------------------------------------------
    +173: 
    +174:     /**
    +175:      * Regenerate Key
    +176:      * Remove a key from the database to stop it working
    +177:      *
    +178:      * @access    public
    +179:      * @return    void
    +180:      */
    +181:     public function regenerate_post()
    +182:     {
    +183:         $old_key = $this->post('key');
    +184:         $key_details = self::_get_key($old_key);
    +185: 
    +186:         // Does this key exist?
    +187:         if (!$key_details)
    +188:         {
    +189:             // It doesn't appear the key exists
    +190:             $this->response([
    +191:                 'error' => 'Invalid API Key'
    +192:             ], 400); // 400 = Bad Request
    +193:         }
    +194: 
    +195:         // Build a new key
    +196:         $new_key = self::_generate_key();
    +197: 
    +198:         // Insert the new key
    +199:         if (self::_insert_key($new_key, ['level' => $key_details->level, 'ignore_limits' => $key_details->ignore_limits]))
     200:         {
    -201:             $salt = do_hash(time().mt_rand());
    -202:             $new_key = substr($salt, 0, config_item('rest_key_length'));
    -203:         }
    -204: 
    -205:         // Already in the DB? Fail. Try again
    -206:         while (self::_key_exists($new_key));
    -207: 
    -208:         return $new_key;
    -209:     }
    -210: 
    -211:     // --------------------------------------------------------------------
    -212: 
    -213:     /* Private Data Methods */
    -214: 
    -215:     private function _get_key($key)
    -216:     {
    -217:         return $this->db->where(config_item('rest_key_column'), $key)->get(config_item('rest_keys_table'))->row();
    -218:     }
    +201:             // Suspend old key
    +202:             self::_update_key($old_key, ['level' => 0]);
    +203: 
    +204:             $this->response([
    +205:                 'status' => 1,
    +206:                 'key' => $new_key
    +207:             ], 201); // 201 = Created
    +208:         }
    +209:         else
    +210:         {
    +211:             $this->response([
    +212:                 'status' => 0,
    +213:                 'error' => 'Could not save the key'
    +214:             ], 500); // 500 = Internal Server Error
    +215:         }
    +216:     }
    +217: 
    +218:     // --------------------------------------------------------------------
     219: 
    -220:     // --------------------------------------------------------------------
    +220:     /* Helper Methods */
     221: 
    -222:     private function _key_exists($key)
    +222:     private function _generate_key()
     223:     {
    -224:         return $this->db->where(config_item('rest_key_column'), $key)->count_all_results(config_item('rest_keys_table')) > 0;
    -225:     }
    -226: 
    -227:     // --------------------------------------------------------------------
    -228: 
    -229:     private function _insert_key($key, $data)
    -230:     {
    -231:         
    -232:         $data[config_item('rest_key_column')] = $key;
    -233:         $data['date_created'] = function_exists('now') ? now() : time();
    -234: 
    -235:         return $this->db->set($data)->insert(config_item('rest_keys_table'));
    -236:     }
    -237: 
    -238:     // --------------------------------------------------------------------
    -239: 
    -240:     private function _update_key($key, $data)
    -241:     {
    -242:         return $this->db->where(config_item('rest_key_column'), $key)->update(config_item('rest_keys_table'), $data);
    -243:     }
    -244: 
    -245:     // --------------------------------------------------------------------
    -246: 
    -247:     private function _delete_key($key)
    -248:     {
    -249:         return $this->db->where(config_item('rest_key_column'), $key)->delete(config_item('rest_keys_table'));
    -250:     }
    -251: }
    -252: 
    +
    224: do +225: { +226: // Generate a random salt +227: $salt = $this->security->get_random_bytes(64); +228: +229: // If an error occurred, then fall back to the previous method +230: if ($salt === FALSE) +231: { +232: $salt = hash('sha256', time() . mt_rand()); +233: } +234: $new_key = substr($salt, 0, config_item('rest_key_length')); +235: } +236: while (self::_key_exists($new_key)); +237: // Already in the DB? Fail. Try again +238: +239: return $new_key; +240: } +241: +242: // -------------------------------------------------------------------- +243: +244: /* Private Data Methods */ +245: +246: private function _get_key($key) +247: { +248: return $this->db->where(config_item('rest_key_column'), $key)->get(config_item('rest_keys_table'))->row(); +249: } +250: +251: // -------------------------------------------------------------------- +252: +253: private function _key_exists($key) +254: { +255: return $this->db->where(config_item('rest_key_column'), $key)->count_all_results(config_item('rest_keys_table')) > 0; +256: } +257: +258: // -------------------------------------------------------------------- +259: +260: private function _insert_key($key, $data) +261: { +262: $data[config_item('rest_key_column')] = $key; +263: $data['date_created'] = function_exists('now') ? now() : time(); +264: +265: return $this->db +266: ->set($data) +267: ->insert(config_item('rest_keys_table')); +268: } +269: +270: // -------------------------------------------------------------------- +271: +272: private function _update_key($key, $data) +273: { +274: return $this->db +275: ->where(config_item('rest_key_column'), $key) +276: ->update(config_item('rest_keys_table'), $data); +277: } +278: +279: // -------------------------------------------------------------------- +280: +281: private function _delete_key($key) +282: { +283: return $this->db +284: ->where(config_item('rest_key_column'), $key) +285: ->delete(config_item('rest_keys_table')); +286: } +287: } +288:
    - + diff --git a/documentation/source-class-REST_Controller.html b/documentation/source-class-REST_Controller.html index 1242de05..122dd871 100644 --- a/documentation/source-class-REST_Controller.html +++ b/documentation/source-class-REST_Controller.html @@ -29,6 +29,11 @@

    Packages

    Libraries + +
  • + + Rest +
  • @@ -52,10 +57,6 @@

    Classes

    -

    Functions

    - @@ -79,1563 +80,1888 @@

    Functions

    Package
  • - Function + Class
    -
       1: <?php defined('BASEPATH') or exit('No direct script access allowed');
    +
       1: <?php
        2: 
    -   3: /**
    -   4:  * CodeIgniter Rest Controller
    -   5:  *
    -   6:  * A fully RESTful server implementation for CodeIgniter using one library, one config file and one controller.
    -   7:  *
    -   8:  * @package         CodeIgniter
    -   9:  * @subpackage      Libraries
    -  10:  * @category        Libraries
    -  11:  * @author          Phil Sturgeon, Chris Kacerguis
    -  12:  * @license         MIT
    -  13:  * @link            https://github.com/chriskacerguis/codeigniter-restserver
    -  14:  * @version         3.0.0-pre
    -  15:  */
    -  16: abstract class REST_Controller extends CI_Controller
    -  17: {
    +   3: defined('BASEPATH') OR exit('No direct script access allowed');
    +   4: 
    +   5: /**
    +   6:  * CodeIgniter Rest Controller
    +   7:  * A fully RESTful server implementation for CodeIgniter using one library, one config file and one controller.
    +   8:  *
    +   9:  * @package         CodeIgniter
    +  10:  * @subpackage      Libraries
    +  11:  * @category        Libraries
    +  12:  * @author          Phil Sturgeon, Chris Kacerguis
    +  13:  * @license         MIT
    +  14:  * @link            https://github.com/chriskacerguis/codeigniter-restserver
    +  15:  * @version         3.0.0
    +  16:  */
    +  17: abstract class REST_Controller extends CI_Controller {
       18:     /**
       19:      * This defines the rest format.
    -  20:      *
    -  21:      * Must be overridden it in a controller so that it is set.
    -  22:      *
    -  23:      * @var string|NULL
    -  24:      */
    -  25:     protected $rest_format          = NULL;
    -  26: 
    -  27:     /**
    -  28:      * Defines the list of method properties such as limit, log and level
    -  29:      *
    -  30:      * @var array
    -  31:      */
    -  32:     protected $methods              = array();
    -  33: 
    -  34:     /**
    -  35:      * List of allowed HTTP methods
    -  36:      *
    -  37:      * @var array
    -  38:      */
    -  39:     protected $allowed_http_methods = array('get', 'delete', 'post', 'put', 'options', 'patch', 'head');
    -  40: 
    -  41:     /**
    -  42:      * General request data and information.
    -  43:      * Stores accept, language, body, headers, etc.
    +  20:      * Must be overridden it in a controller so that it is set.
    +  21:      *
    +  22:      * @var string|NULL
    +  23:      */
    +  24:     protected $rest_format = NULL;
    +  25: 
    +  26:     /**
    +  27:      * Defines the list of method properties such as limit, log and level
    +  28:      *
    +  29:      * @var array
    +  30:      */
    +  31:     protected $methods = [];
    +  32: 
    +  33:     /**
    +  34:      * List of allowed HTTP methods
    +  35:      *
    +  36:      * @var array
    +  37:      */
    +  38:     protected $allowed_http_methods = ['get', 'delete', 'post', 'put', 'options', 'patch', 'head'];
    +  39: 
    +  40:     /**
    +  41:      * Contains details about the request
    +  42:      * Fields: body, format, method, ssl
    +  43:      * Note: This is a dynamic object (stdClass)
       44:      *
       45:      * @var object
       46:      */
    -  47:     protected $request              = NULL;
    +  47:     protected $request = NULL;
       48: 
    -  49:     /**
    -  50:      * What is gonna happen in output?
    -  51:      *
    -  52:      * @var object
    -  53:      */
    -  54:     protected $response             = NULL;
    -  55: 
    -  56:     /**
    -  57:      * Stores DB, keys, key level, etc
    -  58:      *
    -  59:      * @var object
    -  60:      */
    -  61:     protected $rest                 = NULL;
    -  62: 
    -  63:     /**
    -  64:      * The arguments for the GET request method
    -  65:      *
    -  66:      * @var array
    -  67:      */
    -  68:     protected $_get_args            = array();
    -  69: 
    -  70:     /**
    -  71:      * The arguments for the POST request method
    -  72:      *
    -  73:      * @var array
    -  74:      */
    -  75:     protected $_post_args           = array();
    -  76: 
    -  77:     /**
    -  78:      * The insert_id of the log entry (if we have one)
    -  79:      *
    -  80:      * @var string
    -  81:     */
    -  82:     protected $_insert_id           = '';
    -  83: 
    -  84:     /**
    -  85:      * The arguments for the PUT request method
    -  86:      *
    -  87:      * @var array
    -  88:      */
    -  89:     protected $_put_args            = array();
    -  90: 
    -  91:     /**
    -  92:      * The arguments for the DELETE request method
    -  93:      *
    -  94:      * @var array
    -  95:      */
    -  96:     protected $_delete_args         = array();
    -  97: 
    -  98:     /**
    -  99:      * The arguments for the PATCH request method
    - 100:      *
    - 101:      * @var array
    - 102:      */
    - 103:     protected $_patch_args          = array();
    - 104: 
    - 105:     /**
    - 106:      * The arguments for the HEAD request method
    - 107:      *
    - 108:      * @var array
    - 109:      */
    - 110:     protected $_head_args           = array();
    - 111: 
    - 112:     /**
    - 113:      * The arguments for the OPTIONS request method
    - 114:      *
    - 115:      * @var array
    - 116:      */
    - 117:     protected $_options_args        = array();
    - 118: 
    - 119:     /**
    - 120:      * The arguments from GET, POST, PUT, DELETE request methods combined.
    - 121:      *
    - 122:      * @var array
    - 123:      */
    - 124:     protected $_args                = array();
    - 125: 
    - 126:     /**
    - 127:      * If the request is allowed based on the API key provided.
    - 128:      *
    - 129:      * @var boolean
    - 130:      */
    - 131:     protected $_allow               = TRUE;
    - 132: 
    - 133:     /**
    - 134:      * Determines if output compression is enabled
    - 135:      *
    - 136:      * @var boolean
    - 137:      */
    - 138:     protected $_zlib_oc             = FALSE;
    - 139: 
    - 140:     /**
    - 141:      * The LDAP Distinguished Name of the User post authentication
    - 142:      *
    - 143:      * @var string
    - 144:     */
    - 145:     protected $_user_ldap_dn        = '';
    - 146: 
    - 147:     /**
    - 148:      * The start of the response time from the server
    - 149:      *
    - 150:      * @var string
    - 151:     */
    - 152:     protected $_start_rtime         = '';
    - 153: 
    - 154:     /**
    - 155:      * The end of the response time from the server
    - 156:      *
    - 157:      * @var string
    - 158:     */
    - 159:     protected $_end_rtime           = '';
    - 160: 
    - 161:     /**
    - 162:      * List all supported methods, the first will be the default format
    - 163:      *
    - 164:      * @var array
    - 165:      */
    - 166:     protected $_supported_formats   = array(
    - 167:         'xml'           => 'application/xml',
    - 168:         'json'          => 'application/json',
    - 169:         'jsonp'         => 'application/javascript',
    - 170:         'serialized'    => 'application/vnd.php.serialized',
    - 171:         'php'           => 'text/plain',
    - 172:         'html'          => 'text/html',
    - 173:         'csv'           => 'application/csv'
    - 174:     );
    - 175: 
    - 176:     /**
    - 177:      * Information about the current API user
    - 178:      *
    - 179:      * @var object
    - 180:      */
    - 181:     protected $_apiuser;
    - 182: 
    - 183:     /**
    - 184:      * Developers can extend this class and add a check in here.
    +  49:         /**
    +  50:      * Contains details about the response
    +  51:      * Fields: format, lang
    +  52:      * Note: This is a dynamic object (stdClass)
    +  53:      *
    +  54:      * @var object
    +  55:      */
    +  56:     protected $response = NULL;
    +  57: 
    +  58:     /**
    +  59:      * Contains details about the REST API
    +  60:      * Fields: db, ignore_limits, key, level, user_id
    +  61:      * Note: This is a dynamic object (stdClass)
    +  62:      *
    +  63:      * @var object
    +  64:      */
    +  65:     protected $rest = NULL;
    +  66: 
    +  67:     /**
    +  68:      * The arguments for the GET request method
    +  69:      *
    +  70:      * @var array
    +  71:      */
    +  72:     protected $_get_args = [];
    +  73: 
    +  74:     /**
    +  75:      * The arguments for the POST request method
    +  76:      *
    +  77:      * @var array
    +  78:      */
    +  79:     protected $_post_args = [];
    +  80: 
    +  81:     /**
    +  82:      * The insert_id of the log entry (if we have one)
    +  83:      *
    +  84:      * @var string
    +  85:      */
    +  86:     protected $_insert_id = '';
    +  87: 
    +  88:     /**
    +  89:      * The arguments for the PUT request method
    +  90:      *
    +  91:      * @var array
    +  92:      */
    +  93:     protected $_put_args = [];
    +  94: 
    +  95:     /**
    +  96:      * The arguments for the DELETE request method
    +  97:      *
    +  98:      * @var array
    +  99:      */
    + 100:     protected $_delete_args = [];
    + 101: 
    + 102:     /**
    + 103:      * The arguments for the PATCH request method
    + 104:      *
    + 105:      * @var array
    + 106:      */
    + 107:     protected $_patch_args = [];
    + 108: 
    + 109:     /**
    + 110:      * The arguments for the HEAD request method
    + 111:      *
    + 112:      * @var array
    + 113:      */
    + 114:     protected $_head_args = [];
    + 115: 
    + 116:     /**
    + 117:      * The arguments for the OPTIONS request method
    + 118:      *
    + 119:      * @var array
    + 120:      */
    + 121:     protected $_options_args = [];
    + 122: 
    + 123:     /**
    + 124:      * The arguments from GET, POST, PUT, DELETE request methods combined.
    + 125:      *
    + 126:      * @var array
    + 127:      */
    + 128:     protected $_args = [];
    + 129: 
    + 130:     /**
    + 131:      * If the request is allowed based on the API key provided.
    + 132:      *
    + 133:      * @var bool
    + 134:      */
    + 135:     protected $_allow = TRUE;
    + 136: 
    + 137:     /**
    + 138:      * Determines if output compression is enabled
    + 139:      *
    + 140:      * @var bool
    + 141:      */
    + 142:     protected $_zlib_oc = FALSE;
    + 143: 
    + 144:     /**
    + 145:      * The LDAP Distinguished Name of the User post authentication
    + 146:      *
    + 147:      * @var string
    + 148:      */
    + 149:     protected $_user_ldap_dn = '';
    + 150: 
    + 151:     /**
    + 152:      * The start of the response time from the server
    + 153:      *
    + 154:      * @var string
    + 155:      */
    + 156:     protected $_start_rtime = '';
    + 157: 
    + 158:     /**
    + 159:      * The end of the response time from the server
    + 160:      *
    + 161:      * @var string
    + 162:      */
    + 163:     protected $_end_rtime = '';
    + 164: 
    + 165:     /**
    + 166:      * List all supported methods, the first will be the default format
    + 167:      *
    + 168:      * @var array
    + 169:      */
    + 170:     protected $_supported_formats = [
    + 171:             'json' => 'application/json',
    + 172:             'array' => 'application/json',
    + 173:             'csv' => 'application/csv',
    + 174:             'html' => 'text/html',
    + 175:             'jsonp' => 'application/javascript',
    + 176:             'php' => 'text/plain',
    + 177:             'serialized' => 'application/vnd.php.serialized',
    + 178:             'xml' => 'application/xml'
    + 179:         ];
    + 180: 
    + 181:     /**
    + 182:      * Information about the current API user
    + 183:      *
    + 184:      * @var object
      185:      */
    - 186:     protected function early_checks()
    - 187:     {
    - 188: 
    - 189:     }
    - 190: 
    - 191:     /**
    - 192:      * Constructor function
    - 193:      * @todo Document more please.
    - 194:      */
    - 195:     public function __construct($config = 'rest')
    - 196:     {
    - 197:         parent::__construct();
    - 198: 
    - 199:         // Start the timer for how long the request takes
    - 200:         $this->_start_rtime = microtime(TRUE);
    - 201: 
    - 202:         // Lets grab the config and get ready to party
    - 203:         $this->load->config($config);
    - 204: 
    - 205:         // This library is bundled with REST_Controller 2.5+, but will eventually be part of CodeIgniter itself
    - 206:         $this->load->library('format');
    - 207: 
    - 208:         // init objects
    - 209:         $this->response     = new stdClass();
    - 210:         $this->rest         = new stdClass();
    - 211: 
    - 212:         $this->_zlib_oc     = @ini_get('zlib.output_compression');
    - 213: 
    - 214:         // let's learn about the request
    - 215:         $this->request      = new stdClass();
    - 216: 
    - 217:         // Check to see if this IP is Blacklisted
    - 218:         if ($this->config->item('rest_ip_blacklist_enabled')) {
    - 219:             $this->_check_blacklist_auth();
    - 220:         }
    + 186:     protected $_apiuser;
    + 187: 
    + 188:     /**
    + 189:      * Enable XSS flag
    + 190:      * Determines whether the XSS filter is always active when
    + 191:      * GET, OPTIONS, HEAD, POST, PUT, DELETE and PATCH data is encountered.
    + 192:      * Set automatically based on config setting.
    + 193:      *
    + 194:      * @var bool
    + 195:      */
    + 196:     protected $_enable_xss = FALSE;
    + 197: 
    + 198:     /**
    + 199:      * Extend this function to apply additional checking early on in the process
    + 200:      *
    + 201:      * @access protected
    + 202:      */
    + 203:     protected function early_checks()
    + 204:     {
    + 205:     }
    + 206: 
    + 207:     /**
    + 208:      * Constructor for the REST API
    + 209:      *
    + 210:      * @access public
    + 211:      *
    + 212:      * @param string $config Configuration filename minus the file extension
    + 213:      * e.g: my_rest.php is passed as 'my_rest'
    + 214:      */
    + 215:     public function __construct($config = 'rest')
    + 216:     {
    + 217:         parent::__construct();
    + 218: 
    + 219:         // Disable XML Entity (security vulnerability)
    + 220:         libxml_disable_entity_loader(TRUE);
      221: 
    - 222:         // Is it over SSL?
    - 223:         $this->request->ssl     = $this->_detect_ssl();
    - 224: 
    - 225:         // How is this request being made? POST, DELETE, GET, PUT?
    - 226:         $this->request->method  = $this->_detect_method();
    - 227: 
    - 228:         // Create argument container, if nonexistent
    - 229:         if (!isset($this->{'_'.$this->request->method.'_args'})) {
    - 230:             $this->{'_'.$this->request->method.'_args'} = array();
    - 231:         }
    - 232: 
    - 233:         // Set up our GET variables
    - 234:         $this->_get_args        = array_merge($this->_get_args, $this->uri->ruri_to_assoc());
    - 235: 
    - 236:         // Try to find a format for the request (means we have a request body)
    - 237:         $this->request->format  = $this->_detect_input_format();
    - 238: 
    - 239:         // Some Methods cant have a body
    - 240:         $this->request->body    = NULL;
    - 241: 
    - 242:         $this->{'_parse_' . $this->request->method}();
    + 222:         // Check to see if PHP is equal to or greater than 5.4.x
    + 223:         if (is_php('5.4') === FALSE)
    + 224:         {
    + 225:             // CodeIgniter 3 is recommended for v5.4 or above
    + 226:             exit('Using PHP v' . PHP_VERSION . ', though PHP v5.4 or greater is required');
    + 227:         }
    + 228: 
    + 229:         // Check to see if this is CI 3.x
    + 230:         if (explode('.', CI_VERSION, 2)[0] < 3)
    + 231:         {
    + 232:             exit('REST Server requires CodeIgniter 3.x');
    + 233:         }
    + 234: 
    + 235:         // Set the default value of global xss filtering. Same approach as CodeIgniter 3
    + 236:         $this->_enable_xss = ($this->config->item('global_xss_filtering') === TRUE);
    + 237: 
    + 238:         // Start the timer for how long the request takes
    + 239:         $this->_start_rtime = microtime(TRUE);
    + 240: 
    + 241:         // Load the rest.php configuration file
    + 242:         $this->load->config($config);
      243: 
    - 244:         // Now we know all about our request, let's try and parse the body if it exists
    - 245:         if ($this->request->format and $this->request->body) {
    - 246:             $this->request->body = $this->format->factory($this->request->body, $this->request->format)->to_array();
    - 247:             // Assign payload arguments to proper method container
    - 248:             $this->{'_'.$this->request->method.'_args'} = $this->request->body;
    - 249:         }
    - 250: 
    - 251:         // Merge both for one mega-args variable
    - 252:         $this->_args = array_merge($this->_get_args,
    - 253:             $this->_options_args,
    - 254:             $this->_patch_args,
    - 255:             $this->_head_args ,
    - 256:             $this->_put_args,
    - 257:             $this->_post_args,
    - 258:             $this->_delete_args,
    - 259:             $this->{'_'.$this->request->method.'_args'}
    - 260:         );
    - 261: 
    - 262:         // Which format should the data be returned in?
    - 263:         $this->response         = new stdClass();
    - 264:         $this->response->format = $this->_detect_output_format();
    + 244:         // At present the library is bundled with REST_Controller 2.5+, but will eventually be part of CodeIgniter (no citation)
    + 245:         $this->load->library('format');
    + 246: 
    + 247:         // Initialise the response, request and rest objects
    + 248:         $this->request = new stdClass();
    + 249:         $this->response = new stdClass();
    + 250:         $this->rest = new stdClass();
    + 251: 
    + 252:         $this->_zlib_oc = @ini_get('zlib.output_compression');
    + 253: 
    + 254:         // Check to see if the current IP address is blacklisted
    + 255:         if ($this->config->item('rest_ip_blacklist_enabled') === TRUE)
    + 256:         {
    + 257:             $this->_check_blacklist_auth();
    + 258:         }
    + 259: 
    + 260:         // Determine whether the connection is HTTPS
    + 261:         $this->request->ssl = is_https();
    + 262: 
    + 263:         // How is this request being made? GET, POST, PATCH, DELETE, INSERT, PUT, HEAD or OPTIONS
    + 264:         $this->request->method = $this->_detect_method();
      265: 
    - 266:         // Which format should the data be returned in?
    - 267:         $this->response->lang   = $this->_detect_lang();
    - 268: 
    - 269:         // Developers can extend this class and add a check in here
    - 270:         $this->early_checks();
    + 266:         // Create an argument container if it doesn't exist e.g. _get_args
    + 267:         if (isset($this->{'_' . $this->request->method . '_args'}) === FALSE)
    + 268:         {
    + 269:             $this->{'_' . $this->request->method . '_args'} = [];
    + 270:         }
      271: 
    - 272:         $this->rest             = new StdClass();
    - 273: 
    - 274:         // Load DB if its enabled
    - 275:         if (config_item('rest_database_group') and (config_item('rest_enable_keys') or config_item('rest_enable_logging'))) {
    - 276:             $this->rest->db     = $this->load->database(config_item('rest_database_group'), TRUE);
    - 277:         }
    - 278: 
    - 279:         // Use whatever database is in use (isset returns FALSE)
    - 280:         elseif (property_exists($this, "db")) {
    - 281:             $this->rest->db     = $this->db;
    - 282:         }
    - 283: 
    - 284:         // Check if there is a specific auth type for the current class/method
    - 285:         // _auth_override_check could exit so we need $this->rest->db initialized before
    - 286:         $this->auth_override    = $this->_auth_override_check();
    - 287: 
    - 288:         // Checking for keys? GET TO WorK!
    - 289:           // Skip keys test for $config['auth_override_class_method']['class'['method'] = 'none'
    - 290:         if (config_item('rest_enable_keys') and $this->auth_override !== TRUE) {
    - 291:             $this->_allow = $this->_detect_api_key();
    - 292:         }
    - 293: 
    - 294:         // only allow ajax requests
    - 295:         if (!$this->input->is_ajax_request() and config_item('rest_ajax_only')) {
    - 296:             $response = array(config_item('rest_status_field_name') => FALSE, config_item('rest_message_field_name') => 'Only AJAX requests are accepted.');
    - 297:             $this->response($response, 406); // Set status to 406 NOT ACCEPTABLE
    - 298:         }
    - 299: 
    - 300:         // When there is no specific override for the current class/method, use the default auth value set in the config
    - 301:         if ($this->auth_override !== TRUE && !(config_item('rest_enable_keys') && $this->_allow === TRUE)) {
    - 302:             $rest_auth = strtolower($this->config->item('rest_auth'));
    - 303:             switch ($rest_auth) {
    - 304:                 case 'basic':
    - 305:                     $this->_prepare_basic_auth();
    - 306:                     break;
    - 307:                 case 'digest':
    - 308:                     $this->_prepare_digest_auth();
    - 309:                     break;
    - 310:                 case 'session':
    - 311:                     $this->_check_php_session();
    - 312:                     break;
    - 313:             }
    - 314:             if ($this->config->item('rest_ip_whitelist_enabled')) {
    - 315:                 $this->_check_whitelist_auth();
    - 316:             }
    - 317:         }
    - 318:     }
    - 319: 
    - 320:     /**
    - 321:      * Destructor function
    - 322:      * @author Chris Kacerguis
    - 323:      */
    - 324:     public function __destruct()
    - 325:     {
    - 326:         // Record the "stop" time of the request
    - 327:         $this->_end_rtime = microtime(TRUE);
    - 328:         // CK: if, we are logging, log the access time here, as we are done!
    - 329:         if (config_item('rest_enable_logging')) {
    - 330:             $this->_log_access_time();
    - 331:         }
    - 332: 
    - 333:     }
    + 272:         // Set up the GET variables
    + 273:         $this->_get_args = array_merge($this->_get_args, $this->uri->ruri_to_assoc());
    + 274: 
    + 275:         // Try to find a format for the request (means we have a request body)
    + 276:         $this->request->format = $this->_detect_input_format();
    + 277: 
    + 278:         // Not all methods have a body attached with them
    + 279:         $this->request->body = NULL;
    + 280: 
    + 281:         $this->{'_parse_' . $this->request->method}();
    + 282: 
    + 283:         // Now we know all about our request, let's try and parse the body if it exists
    + 284:         if ($this->request->format && $this->request->body)
    + 285:         {
    + 286:             $this->request->body = $this->format->factory($this->request->body, $this->request->format)->to_array();
    + 287:             // Assign payload arguments to proper method container
    + 288:             $this->{'_' . $this->request->method . '_args'} = $this->request->body;
    + 289:         }
    + 290: 
    + 291:         // Merge both for one mega-args variable
    + 292:         $this->_args = array_merge(
    + 293:             $this->_get_args,
    + 294:             $this->_options_args,
    + 295:             $this->_patch_args,
    + 296:             $this->_head_args,
    + 297:             $this->_put_args,
    + 298:             $this->_post_args,
    + 299:             $this->_delete_args,
    + 300:             $this->{'_' . $this->request->method . '_args'}
    + 301:         );
    + 302: 
    + 303:         // Which format should the data be returned in?
    + 304:         $this->response->format = $this->_detect_output_format();
    + 305: 
    + 306:         // Which format should the data be returned in?
    + 307:         $this->response->lang = $this->_detect_lang();
    + 308: 
    + 309:         // Extend this function to apply additional checking early on in the process
    + 310:         $this->early_checks();
    + 311: 
    + 312:         // Load DB if its enabled
    + 313:         if ($this->config->item('rest_database_group') && ($this->config->item('rest_enable_keys') || $this->config->item('rest_enable_logging')))
    + 314:         {
    + 315:             $this->rest->db = $this->load->database($this->config->item('rest_database_group'), TRUE);
    + 316:         }
    + 317: 
    + 318:         // Use whatever database is in use (isset returns FALSE)
    + 319:         elseif (property_exists($this, 'db'))
    + 320:         {
    + 321:             $this->rest->db = $this->db;
    + 322:         }
    + 323: 
    + 324:         // Check if there is a specific auth type for the current class/method
    + 325:         // _auth_override_check could exit so we need $this->rest->db initialized before
    + 326:         $this->auth_override = $this->_auth_override_check();
    + 327: 
    + 328:         // Checking for keys? GET TO WorK!
    + 329:         // Skip keys test for $config['auth_override_class_method']['class'['method'] = 'none'
    + 330:         if ($this->config->item('rest_enable_keys') && $this->auth_override !== TRUE)
    + 331:         {
    + 332:             $this->_allow = $this->_detect_api_key();
    + 333:         }
      334: 
    - 335:     /**
    - 336:      * Remap
    - 337:      *
    - 338:      * Requests are not made to methods directly, the request will be for
    - 339:      * an "object". This simply maps the object and method to the correct
    - 340:      * Controller method.
    - 341:      *
    - 342:      * @param string $object_called
    - 343:      * @param array  $arguments     The arguments passed to the controller method.
    - 344:      */
    - 345:     public function _remap($object_called, $arguments)
    - 346:     {
    - 347:         // Should we answer if not over SSL?
    - 348:         if (config_item('force_https') and !$this->_detect_ssl()) {
    - 349:             $this->response(array(config_item('rest_status_field_name') => FALSE, config_item('rest_message_field_name') => 'Unsupported protocol'), 403);
    - 350:         }
    - 351: 
    - 352:         $pattern = '/^(.*)\.('.implode('|', array_keys($this->_supported_formats)).')$/';
    - 353:         $matches = array();
    - 354:         if (preg_match($pattern, $object_called, $matches)) {
    - 355:             $object_called = $matches[1];
    - 356:         }
    - 357: 
    - 358:         $controller_method = $object_called.'_'.$this->request->method;
    - 359: 
    - 360:         // Do we want to log this method (if allowed by config)?
    - 361:         $log_method = !(isset($this->methods[$controller_method]['log']) and $this->methods[$controller_method]['log'] == FALSE);
    - 362: 
    - 363:         // Use keys for this method?
    - 364:         $use_key = !(isset($this->methods[$controller_method]['key']) and $this->methods[$controller_method]['key'] == FALSE);
    - 365: 
    - 366:         // They provided a key, but it wasn't valid, so get them out of here.
    - 367:         if (config_item('rest_enable_keys') and $use_key and $this->_allow === FALSE) {
    - 368:             if (config_item('rest_enable_logging') and $log_method) {
    - 369:                 $this->_log_request();
    - 370:             }
    - 371: 
    - 372:             $this->response(array(config_item('rest_status_field_name') => FALSE, config_item('rest_message_field_name') => 'Invalid API Key '.$this->rest->key), 403);
    - 373:         }
    - 374: 
    - 375:         // Check to see if this key has access to the requested controller.
    - 376:         if (config_item('rest_enable_keys') and $use_key and !empty($this->rest->key) and !$this->_check_access()) {
    - 377:             if (config_item('rest_enable_logging') and $log_method) {
    - 378:                 $this->_log_request();
    - 379:             }
    - 380: 
    - 381:             $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);
    - 382:         }
    - 383: 
    - 384:         // Sure it exists, but can they do anything with it?
    - 385:         if ( ! method_exists($this, $controller_method)) {
    - 386:             $this->response(array(config_item('rest_status_field_name') => FALSE, config_item('rest_message_field_name') => 'Unknown method.'), 404);
    - 387:         }
    - 388: 
    - 389:         // Doing key related stuff? Can only do it if they have a key right?
    - 390:         if (config_item('rest_enable_keys') and !empty($this->rest->key)) {
    - 391:             // Check the limit
    - 392:             if (config_item('rest_enable_limits') and !$this->_check_limit($controller_method)) {
    - 393:                 $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.');
    - 394:                 $this->response($response, 401);
    - 395:             }
    - 396: 
    - 397:             // If no level is set use 0, they probably aren't using permissions
    - 398:             $level = isset($this->methods[$controller_method]['level']) ? $this->methods[$controller_method]['level'] : 0;
    - 399: 
    - 400:             // If no level is set, or it is lower than/equal to the key's level
    - 401:             $authorized = $level <= $this->rest->level;
    - 402: 
    - 403:             // IM TELLIN!
    - 404:             if (config_item('rest_enable_logging') and $log_method) {
    - 405:                 $this->_log_request($authorized);
    - 406:             }
    + 335:         // Only allow ajax requests
    + 336:         if ($this->input->is_ajax_request() === FALSE && $this->config->item('rest_ajax_only'))
    + 337:         {
    + 338:             // Display an error response
    + 339:             $this->response(
    + 340:                 [
    + 341:                     $this->config->item('rest_status_field_name') => FALSE,
    + 342:                     $this->config->item('rest_message_field_name') => 'Only AJAX requests are acceptable'
    + 343:                 ], 406); // Set status to 406 NOT ACCEPTABLE
    + 344:         }
    + 345: 
    + 346:         // When there is no specific override for the current class/method, use the default auth value set in the config
    + 347:         if ($this->auth_override === FALSE && !($this->config->item('rest_enable_keys') && $this->_allow === TRUE))
    + 348:         {
    + 349:             $rest_auth = strtolower($this->config->item('rest_auth'));
    + 350:             switch ($rest_auth)
    + 351:             {
    + 352:                 case 'basic':
    + 353:                     $this->_prepare_basic_auth();
    + 354:                     break;
    + 355:                 case 'digest':
    + 356:                     $this->_prepare_digest_auth();
    + 357:                     break;
    + 358:                 case 'session':
    + 359:                     $this->_check_php_session();
    + 360:                     break;
    + 361:             }
    + 362:             if ($this->config->item('rest_ip_whitelist_enabled') === TRUE)
    + 363:             {
    + 364:                 $this->_check_whitelist_auth();
    + 365:             }
    + 366:         }
    + 367:     }
    + 368: 
    + 369:     /**
    + 370:      * Deconstructor
    + 371:      *
    + 372:      * @author Chris Kacerguis
    + 373:      * @access public
    + 374:      */
    + 375:     public function __destruct()
    + 376:     {
    + 377:         // Get the current timestamp
    + 378:         $this->_end_rtime = microtime(TRUE);
    + 379: 
    + 380:         // Log the loading time to the log table
    + 381:         if ($this->config->item('rest_enable_logging') === TRUE)
    + 382:         {
    + 383:             $this->_log_access_time();
    + 384:         }
    + 385:     }
    + 386: 
    + 387:     /**
    + 388:      * Requests are not made to methods directly, the request will be for
    + 389:      * an "object". This simply maps the object and method to the correct
    + 390:      * Controller method.
    + 391:      *
    + 392:      * @access public
    + 393:      *
    + 394:      * @param  string $object_called
    + 395:      * @param  array $arguments The arguments passed to the controller method.
    + 396:      */
    + 397:     public function _remap($object_called, $arguments)
    + 398:     {
    + 399:         // Should we answer if not over SSL?
    + 400:         if ($this->config->item('force_https') && $this->request->ssl === FALSE)
    + 401:         {
    + 402:             $this->response([$this->config->item('rest_status_field_name') => FALSE, $this->config->item('rest_message_field_name') => 'Unsupported protocol'], 403);
    + 403:         }
    + 404: 
    + 405:         // Remove the supported format from the function name e.g. index.json => index
    + 406:         $object_called = preg_replace('/^(.*)\.(?:' . implode('|', array_keys($this->_supported_formats)) . ')$/', '$1', $object_called);
      407: 
    - 408:             // They don't have good enough perms
    - 409:             $response = array(config_item('rest_status_field_name') => FALSE, config_item('rest_message_field_name') => 'This API key does not have enough permissions.');
    - 410:             $authorized or $this->response($response, 401);
    - 411:         }
    + 408:         $controller_method = $object_called . '_' . $this->request->method;
    + 409: 
    + 410:         // Do we want to log this method (if allowed by config)?
    + 411:         $log_method = !(isset($this->methods[$controller_method]['log']) && $this->methods[$controller_method]['log'] == FALSE);
      412: 
    - 413:         // No key stuff, but record that stuff is happening
    - 414:         else if (config_item('rest_enable_logging') and $log_method) {
    - 415:             $this->_log_request($authorized = TRUE);
    - 416:         }
    - 417: 
    - 418:         // and...... GO!
    - 419:         $this->_fire_method(array($this, $controller_method), $arguments);
    - 420:     }
    - 421: 
    - 422:     /**
    - 423:      * Fire Method
    - 424:      *
    - 425:      * Fires the designated controller method with the given arguments.
    - 426:      *
    - 427:      * @param array $method The controller method to fire
    - 428:      * @param array $args   The arguments to pass to the controller method
    - 429:      */
    - 430:     protected function _fire_method($method, $args)
    - 431:     {
    - 432:         call_user_func_array($method, $args);
    - 433:     }
    + 413:         // Use keys for this method?
    + 414:         $use_key = !(isset($this->methods[$controller_method]['key']) && $this->methods[$controller_method]['key'] == FALSE);
    + 415: 
    + 416:         // They provided a key, but it wasn't valid, so get them out of here.
    + 417:         if ($this->config->item('rest_enable_keys') && $use_key && $this->_allow === FALSE)
    + 418:         {
    + 419:             if ($this->config->item('rest_enable_logging') && $log_method)
    + 420:             {
    + 421:                 $this->_log_request();
    + 422:             }
    + 423: 
    + 424:             $this->response([$this->config->item('rest_status_field_name') => FALSE, $this->config->item('rest_message_field_name') => 'Invalid API Key ' . $this->rest->key], 403);
    + 425:         }
    + 426: 
    + 427:         // Check to see if this key has access to the requested controller.
    + 428:         if ($this->config->item('rest_enable_keys') && $use_key && empty($this->rest->key) === FALSE && $this->_check_access() === FALSE)
    + 429:         {
    + 430:             if ($this->config->item('rest_enable_logging') && $log_method)
    + 431:             {
    + 432:                 $this->_log_request();
    + 433:             }
      434: 
    - 435:     /**
    - 436:      * Response
    - 437:      *
    - 438:      * Takes pure data and optionally a status code, then creates the response.
    - 439:      * Set $continue to TRUE to flush the response to the client and continue running the script.
    - 440:      *
    - 441:      * @param array    $data
    - 442:      * @param NULL|int $http_code
    - 443:      * @param bool $continue
    - 444:      */
    - 445:     public function response($data = NULL, $http_code = NULL, $continue = FALSE)
    - 446:     {
    - 447:         // If data is NULL and not code provide, error and bail
    - 448:         if ($data === NULL && $http_code === NULL) {
    - 449:             $http_code = 404;
    - 450: 
    - 451:             // create the output variable here in the case of $this->response(array());
    - 452:             $output = NULL;
    - 453:         }
    - 454: 
    - 455:         // If data is NULL but http code provided, keep the output empty
    - 456:         else if ($data === NULL && is_numeric($http_code)) {
    - 457:             $output = NULL;
    - 458:         }
    + 435:             $this->response([$this->config->item('rest_status_field_name') => FALSE, $this->config->item('rest_message_field_name') => 'This API key does not have access to the requested controller.'], 401);
    + 436:         }
    + 437: 
    + 438:         // Sure it exists, but can they do anything with it?
    + 439:         if (method_exists($this, $controller_method) === FALSE)
    + 440:         {
    + 441:             $this->response([$this->config->item('rest_status_field_name') => FALSE, $this->config->item('rest_message_field_name') => 'Unknown method.'], 404);
    + 442:         }
    + 443: 
    + 444:         // Doing key related stuff? Can only do it if they have a key right?
    + 445:         if ($this->config->item('rest_enable_keys') && empty($this->rest->key) === FALSE)
    + 446:         {
    + 447:             // Check the limit
    + 448:             if ($this->config->item('rest_enable_limits') && $this->_check_limit($controller_method) === FALSE)
    + 449:             {
    + 450:                 $response = [$this->config->item('rest_status_field_name') => FALSE, $this->config->item('rest_message_field_name') => 'This API key has reached the time limit for this method.'];
    + 451:                 $this->response($response, 401);
    + 452:             }
    + 453: 
    + 454:             // If no level is set use 0, they probably aren't using permissions
    + 455:             $level = isset($this->methods[$controller_method]['level']) ? $this->methods[$controller_method]['level'] : 0;
    + 456: 
    + 457:             // If no level is set, or it is lower than/equal to the key's level
    + 458:             $authorized = $level <= $this->rest->level;
      459: 
    - 460:         // Otherwise (if no data but 200 provided) or some data, carry on camping!
    - 461:         else {
    - 462:             // Is compression requested?
    - 463:             if ($this->config->item('compress_output') === TRUE && $this->_zlib_oc == FALSE) {
    - 464:                 if (extension_loaded('zlib')) {
    - 465:                     if (isset($_SERVER['HTTP_ACCEPT_ENCODING']) and strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE) {
    - 466:                         ob_start('ob_gzhandler');
    - 467:                     }
    - 468:                 }
    - 469:             }
    + 460:             // IM TELLIN!
    + 461:             if ($this->config->item('rest_enable_logging') && $log_method)
    + 462:             {
    + 463:                 $this->_log_request($authorized);
    + 464:             }
    + 465: 
    + 466:             // They don't have good enough perms
    + 467:             $response = [$this->config->item('rest_status_field_name') => FALSE, $this->config->item('rest_message_field_name') => 'This API key does not have enough permissions.'];
    + 468:             $authorized || $this->response($response, 401);
    + 469:         }
      470: 
    - 471:             is_numeric($http_code) or $http_code = 200;
    - 472: 
    - 473:             // @deprecated the following statement can be deleted.
    - 474:             // If the format method exists, call and return the output in that format
    - 475:             if (method_exists($this, '_format_'.$this->response->format)) {
    - 476:                 // Set the correct format header
    - 477:                 header('Content-Type: '.$this->_supported_formats[$this->response->format] . '; charset=' . strtolower($this->config->item('charset')));
    - 478: 
    - 479:                 $output = $this->{'_format_'.$this->response->format}($data);
    - 480:             }
    - 481: 
    - 482:             // If the format method exists, call and return the output in that format
    - 483:             elseif (method_exists($this->format, 'to_'.$this->response->format)) {
    - 484:                 // Set the correct format header
    - 485:                 header('Content-Type: '.$this->_supported_formats[$this->response->format] . '; charset=' . strtolower($this->config->item('charset')));
    - 486: 
    - 487:                 $output = $this->format->factory($data)->{'to_'.$this->response->format}();
    - 488:             }
    - 489: 
    - 490:             // Format not supported, output directly
    - 491:             else {
    - 492:                 $output = $data;
    - 493:             }
    - 494:         }
    + 471:         // No key stuff, but record that stuff is happening
    + 472:         elseif ($this->config->item('rest_enable_logging') && $log_method)
    + 473:         {
    + 474:             $this->_log_request($authorized = TRUE);
    + 475:         }
    + 476: 
    + 477:         // Call the controller method and passed arguments
    + 478:         try
    + 479:         {
    + 480:             call_user_func_array([$this, $controller_method], $arguments);
    + 481:         }
    + 482:         catch (Exception $ex)
    + 483:         {
    + 484:             // If the method doesn't exist, then the error will be caught and an error response shown
    + 485:             $this->response(
    + 486:                 [
    + 487:                     $this->config->item('rest_status_field_name') => FALSE,
    + 488:                     $this->config->item('rest_message_field_name') => [
    + 489:                         'classname' => get_class($ex),
    + 490:                         'message' => $ex->getMessage()
    + 491:                     ]
    + 492:                 ], 500);
    + 493:         }
    + 494:     }
      495: 
    - 496:         set_status_header($http_code);
    - 497: 
    - 498:         // If zlib.output_compression is enabled it will compress the output,
    - 499:         // but it will not modify the content-length header to compensate for
    - 500:         // the reduction, causing the browser to hang waiting for more data.
    - 501:         // We'll just skip content-length in those cases.
    - 502:         if ( ! $this->_zlib_oc && ! $this->config->item('compress_output')) {
    - 503:             header('Content-Length: ' . strlen($output));
    - 504:         }
    - 505: 
    - 506:         if($continue){
    - 507:             echo($output);
    - 508:             ob_end_flush();
    - 509:             ob_flush();
    - 510:             flush();
    - 511:         }else{
    - 512:             exit($output);
    + 496:     /**
    + 497:      * Takes mixed data and optionally a status code, then creates the response
    + 498:      *
    + 499:      * @access public
    + 500:      *
    + 501:      * @param array|NULL $data Data to output to the user
    + 502:      * @param int|NULL $http_code HTTP status code
    + 503:      * @param bool $continue TRUE to flush the response to the client and continue
    + 504:      * running the script; otherwise, exit
    + 505:      */
    + 506:     public function response($data = NULL, $http_code = NULL, $continue = FALSE)
    + 507:     {
    + 508:         // If the HTTP status is not NULL, then cast as an integer
    + 509:         if ($http_code !== NULL)
    + 510:         {
    + 511:             // So as to be safe later on in the process
    + 512:             $http_code = (int) $http_code;
      513:         }
    - 514:     }
    - 515: 
    - 516:     /*
    - 517:      * Detect SSL use
    - 518:      *
    - 519:      * Detect whether SSL is being used or not
    - 520:      */
    - 521:     protected function _detect_ssl()
    - 522:     {
    - 523:             // $_SERVER['HTTPS'] (http://php.net/manual/en/reserved.variables.server.php)
    - 524:             // Set to a non-empty value if the script was queried through the HTTPS protocol
    - 525:             return (isset($_SERVER['HTTPS']) && !empty($_SERVER['HTTPS']));
    - 526:     }
    - 527: 
    - 528: 
    - 529:     /*
    - 530:      * Detect input format
    - 531:      *
    - 532:      * Detect which format the HTTP Body is provided in
    - 533:      */
    - 534:     protected function _detect_input_format()
    - 535:     {
    - 536:         if ($this->input->server('CONTENT_TYPE')) {
    - 537:             // Check all formats against the HTTP_ACCEPT header
    - 538:             foreach ($this->_supported_formats as $format => $mime) {
    - 539:                 if (strpos($match = $this->input->server('CONTENT_TYPE'), ';')) {
    - 540:                     $match = current(explode(';', $match));
    - 541:                 }
    - 542: 
    - 543:                 if ($match == $mime) {
    - 544:                     return $format;
    - 545:                 }
    - 546:             }
    - 547:         }
    + 514: 
    + 515:         // Set the output as NULL by default
    + 516:         $output = NULL;
    + 517: 
    + 518:         // If data is NULL and no HTTP status code provided, then display, error and exit
    + 519:         if ($data === NULL && $http_code === NULL)
    + 520:         {
    + 521:             $http_code = 404;
    + 522:         }
    + 523: 
    + 524:         // If data is not NULL and a HTTP status code provided, then continue
    + 525:         elseif ($data !== NULL)
    + 526:         {
    + 527:             // Is compression enabled and available?
    + 528:             if ($this->config->item('compress_output') === TRUE && $this->_zlib_oc == FALSE)
    + 529:             {
    + 530:                 if (extension_loaded('zlib'))
    + 531:                 {
    + 532:                     $http_encoding = $this->input->server('HTTP_ACCEPT_ENCODING');
    + 533:                     if ($http_encoding !== NULL && strpos($http_encoding, 'gzip') !== FALSE)
    + 534:                     {
    + 535:                         ob_start('ob_gzhandler');
    + 536:                     }
    + 537:                 }
    + 538:             }
    + 539: 
    + 540:             // If the format method exists, call and return the output in that format
    + 541:             if (method_exists($this->format, 'to_' . $this->response->format))
    + 542:             {
    + 543:                 // Set the format header
    + 544:                 header('Content-Type: ' . $this->_supported_formats[$this->response->format]
    + 545:                        . '; charset=' . strtolower($this->config->item('charset')));
    + 546: 
    + 547:                 $output = $this->format->factory($data)->{'to_' . $this->response->format}();
      548: 
    - 549:         return NULL;
    - 550:     }
    - 551: 
    - 552:     /**
    - 553:      * Detect format
    - 554:      *
    - 555:      * Detect which format should be used to output the data.
    - 556:      *
    - 557:      * @return string The output format.
    - 558:      */
    - 559:     protected function _detect_output_format()
    - 560:     {
    - 561:         $pattern = '/\.('.implode('|', array_keys($this->_supported_formats)).')$/';
    - 562: 
    - 563:         // Check if a file extension is used when no get arguments provided
    - 564:         $matches = array();
    - 565:         if (!$this->_get_args and preg_match($pattern, $this->uri->uri_string(), $matches)) {
    - 566:             return $matches[1];
    + 549:                 // An array must be parsed as a string, so as not to cause an array to string error.
    + 550:                 // Json is the most appropriate form for such a datatype
    + 551:                 if ($this->response->format === 'array')
    + 552:                 {
    + 553:                     $output = $this->format->factory($output)->{'to_json'}();
    + 554:                 }
    + 555:             }
    + 556:             else
    + 557:             {
    + 558:                 // If an array or object, then parse as a json, so as to be a 'string'
    + 559:                 if (is_array($data) || is_object($data))
    + 560:                 {
    + 561:                     $data = $this->format->factory($data)->{'to_json'}();
    + 562:                 }
    + 563: 
    + 564:                 // Format is not supported, so output the raw data as a string
    + 565:                 $output = $data;
    + 566:             }
      567:         }
      568: 
    - 569:         // Check if a file extension is used
    - 570:         elseif ($this->_get_args and !is_array(end($this->_get_args)) and preg_match($pattern, end($this->_get_args), $matches)) {
    - 571:         //elseif ($this->_get_args and !is_array(end($this->_get_args)) and preg_match($pattern, end(array_keys($this->_get_args)), $matches)) {
    - 572:             // The key of the last argument
    - 573:             $last_key = end(array_keys($this->_get_args));
    - 574: 
    - 575:             // Remove the extension from arguments too
    - 576:             $this->_get_args[$last_key] = preg_replace($pattern, '', $this->_get_args[$last_key]);
    - 577:             $this->_args[$last_key]     = preg_replace($pattern, '', $this->_args[$last_key]);
    - 578: 
    - 579:             return $matches[1];
    + 569:         // If not greater than zero, then set the HTTP status code as 200 by default
    + 570:         // Though perhaps 500 should be set instead, for the developer not passing a
    + 571:         // correct HTTP status code
    + 572:         $http_code > 0 || $http_code = 200;
    + 573: 
    + 574:         set_status_header($http_code);
    + 575: 
    + 576:         // JC: Log response code only if rest logging enabled
    + 577:         if ($this->config->item('rest_enable_logging') === TRUE)
    + 578:         {
    + 579:             $this->_log_response_code($http_code);
      580:         }
      581: 
    - 582:         // A format has been passed as an argument in the URL and it is supported
    - 583:         if (isset($this->_get_args['format']) and array_key_exists($this->_get_args['format'], $this->_supported_formats)) {
    - 584:             return $this->_get_args['format'];
    - 585:         }
    - 586: 
    - 587:         // Otherwise, check the HTTP_ACCEPT (if it exists and we are allowed)
    - 588:         if ($this->config->item('rest_ignore_http_accept') === FALSE and $this->input->server('HTTP_ACCEPT')) {
    - 589:             // Check all formats against the HTTP_ACCEPT header
    - 590:             foreach (array_keys($this->_supported_formats) as $format) {
    - 591:                 // Has this format been requested?
    - 592:                 if (strpos($this->input->server('HTTP_ACCEPT'), $format) !== FALSE) {
    - 593:                     // If not HTML or XML assume its right and send it on its way
    - 594:                     if ($format != 'html' and $format != 'xml') {
    - 595:                         return $format;
    - 596:                     }
    - 597: 
    - 598:                     // HTML or XML have shown up as a match
    - 599:                     else {
    - 600:                         // If it is truly HTML, it wont want any XML
    - 601:                         if ($format == 'html' and strpos($this->input->server('HTTP_ACCEPT'), 'xml') === FALSE) {
    - 602:                             return $format;
    - 603:                         }
    - 604: 
    - 605:                         // If it is truly XML, it wont want any HTML
    - 606:                         elseif ($format == 'xml' and strpos($this->input->server('HTTP_ACCEPT'), 'html') === FALSE) {
    - 607:                             return $format;
    - 608:                         }
    - 609:                     }
    - 610:                 }
    - 611:             }
    - 612:         } // End HTTP_ACCEPT checking
    - 613: 
    - 614:         // Well, none of that has worked! Let's see if the controller has a default
    - 615:         if ( ! empty($this->rest_format)) {
    - 616:             return $this->rest_format;
    - 617:         }
    - 618: 
    - 619:         // Just use the default format
    - 620:         return config_item('rest_default_format');
    - 621:     }
    - 622: 
    - 623:     /**
    - 624:      * Detect method
    - 625:      *
    - 626:      * Detect which HTTP method is being used
    - 627:      *
    - 628:      * @return string
    - 629:      */
    - 630:     protected function _detect_method()
    - 631:     {
    - 632:         $method = strtolower($this->input->server('REQUEST_METHOD'));
    - 633: 
    - 634:         if ($this->config->item('enable_emulate_request')) {
    - 635:             if ($this->input->post('_method')) {
    - 636:                 $method = strtolower($this->input->post('_method'));
    - 637:             } elseif ($this->input->server('HTTP_X_HTTP_METHOD_OVERRIDE')) {
    - 638:                 $method = strtolower($this->input->server('HTTP_X_HTTP_METHOD_OVERRIDE'));
    - 639:             }
    - 640:         }
    - 641: 
    - 642:         if (in_array($method, $this->allowed_http_methods) && method_exists($this, '_parse_' . $method)) {
    - 643:             return $method;
    - 644:         }
    + 582:         // If zlib.output_compression is enabled it will compress the output,
    + 583:         // but it will not modify the content-length header to compensate for
    + 584:         // the reduction, causing the browser to hang waiting for more data.
    + 585:         // We'll just skip content-length in those cases
    + 586:         if (!$this->_zlib_oc && !$this->config->item('compress_output'))
    + 587:         {
    + 588:             header('Content-Length: ' . strlen($output));
    + 589:         }
    + 590: 
    + 591:         if ($continue === FALSE)
    + 592:         {
    + 593:             exit($output);
    + 594:         }
    + 595: 
    + 596:         echo($output);
    + 597:         ob_end_flush();
    + 598:         ob_flush();
    + 599:         flush();
    + 600:     }
    + 601: 
    + 602:     /**
    + 603:      * Get the input format e.g. json or xml
    + 604:      *
    + 605:      * @access private
    + 606:      * @return string|NULL Supported input format; otherwise, NULL
    + 607:      */
    + 608:     private function _detect_input_format()
    + 609:     {
    + 610:         // Get the CONTENT-TYPE value from the SERVER variable
    + 611:         $contentType = $this->input->server('CONTENT_TYPE');
    + 612: 
    + 613:         if (empty($contentType) === FALSE)
    + 614:         {
    + 615:             // Check all formats against the HTTP_ACCEPT header
    + 616:             foreach ($this->_supported_formats as $key => $value)
    + 617:             {
    + 618:                 // $key = format e.g. csv
    + 619:                 // $value = mime type e.g. application/csv
    + 620: 
    + 621:                 // If a semi-colon exists in the string, then explode by ; and get the value of where
    + 622:                 // the current array pointer resides. This will generally be the first element of the array
    + 623:                 $contentType = (strpos($contentType, ';') !== FALSE ? current(explode(';', $contentType)) : $contentType);
    + 624: 
    + 625:                 // If both the mime types match, then return the format
    + 626:                 if ($contentType === $value)
    + 627:                 {
    + 628:                     return $key;
    + 629:                 }
    + 630:             }
    + 631:         }
    + 632: 
    + 633:         return NULL;
    + 634:     }
    + 635: 
    + 636:     /**
    + 637:      * Detect which format should be used to output the data
    + 638:      *
    + 639:      * @access protected
    + 640:      * @return mixed|NULL|string Output format
    + 641:      */
    + 642:     protected function _detect_output_format()
    + 643:     {
    + 644:         $pattern = '/\.(' . implode('|', array_keys($this->_supported_formats)) . ')$/';
      645: 
    - 646:         return 'get';
    - 647:     }
    - 648: 
    - 649:     /**
    - 650:      * Detect API Key
    - 651:      *
    - 652:      * See if the user has provided an API key
    - 653:      *
    - 654:      * @return boolean
    - 655:      */
    - 656:     protected function _detect_api_key()
    - 657:     {
    - 658:         // Get the api key name variable set in the rest config file
    - 659:         $api_key_variable = config_item('rest_key_name');
    + 646:         // Check if a file extension is used when no get arguments provided
    + 647:         $matches = [];
    + 648:         if (!$this->_get_args && preg_match($pattern, $this->uri->uri_string(), $matches))
    + 649:         {
    + 650:             return $matches[1];
    + 651:         }
    + 652: 
    + 653:         // Check if a file extension is used
    + 654:         elseif ($this->_get_args && is_array(end($this->_get_args)) === FALSE && preg_match($pattern, end($this->_get_args), $matches))
    + 655:         {
    + 656:             //elseif ($this->_get_args and is_array(end($this->_get_args)) === FALSE and preg_match($pattern, end(array_keys($this->_get_args)), $matches)) {
    + 657:             // The key of the last argument
    + 658:             $arg_keys = array_keys($this->_get_args);
    + 659:             $last_key = end($arg_keys);
      660: 
    - 661:         // Work out the name of the SERVER entry based on config
    - 662:         $key_name = 'HTTP_'.strtoupper(str_replace('-', '_', $api_key_variable));
    - 663: 
    - 664:         $this->rest->key = NULL;
    - 665:         $this->rest->level = NULL;
    - 666:         $this->rest->user_id = NULL;
    - 667:         $this->rest->ignore_limits = FALSE;
    - 668: 
    - 669:         // Find the key from server or arguments
    - 670:         if (($key = isset($this->_args[$api_key_variable]) ? $this->_args[$api_key_variable] : $this->input->server($key_name))) {
    - 671:             if ( ! ($row = $this->rest->db->where(config_item('rest_key_column'), $key)->get(config_item('rest_keys_table'))->row())) {
    - 672:                 return FALSE;
    - 673:             }
    - 674: 
    - 675:             $this->rest->key = $row->{config_item('rest_key_column')};
    - 676: 
    - 677:             isset($row->user_id) and $this->rest->user_id = $row->user_id;
    - 678:             isset($row->level) and $this->rest->level = $row->level;
    - 679:             isset($row->ignore_limits) and $this->rest->ignore_limits = $row->ignore_limits;
    - 680: 
    - 681:             $this->_apiuser =  $row;
    - 682: 
    - 683:             /*
    - 684:              * If "is private key" is enabled, compare the ip address with the list
    - 685:              * of valid ip addresses stored in the database.
    - 686:              */
    - 687:             if (!empty($row->is_private_key)) {
    - 688:                 // Check for a list of valid ip addresses
    - 689:                 if (isset($row->ip_addresses)) {
    - 690:                     // multiple ip addresses must be separated using a comma, explode and loop
    - 691:                     $list_ip_addresses = explode(",", $row->ip_addresses);
    - 692:                     $found_address = FALSE;
    - 693: 
    - 694:                     foreach ($list_ip_addresses as $ip_address) {
    - 695:                         if ($this->input->ip_address() == trim($ip_address)) {
    - 696:                             // there is a match, set the the value to TRUE and break out of the loop
    - 697:                             $found_address = TRUE;
    - 698:                             break;
    - 699:                         }
    - 700:                     }
    - 701: 
    - 702:                     return $found_address;
    - 703:                 } else {
    - 704:                     // There should be at least one IP address for this private key.
    - 705:                     return FALSE;
    - 706:                 }
    - 707:             }
    - 708: 
    - 709:             return $row;
    - 710:         }
    - 711: 
    - 712:         // No key has been sent
    - 713:         return FALSE;
    - 714:     }
    - 715: 
    - 716:     /**
    - 717:      * Detect language(s)
    - 718:      *
    - 719:      * What language do they want it in?
    + 661:             // Remove the extension from arguments too
    + 662:             $this->_get_args[$last_key] = preg_replace($pattern, '', $this->_get_args[$last_key]);
    + 663:             $this->_args[$last_key] = preg_replace($pattern, '', $this->_args[$last_key]);
    + 664: 
    + 665:             return $matches[1];
    + 666:         }
    + 667: 
    + 668:         // A format has been passed as an argument in the URL and it is supported
    + 669:         if (isset($this->_get_args['format']) && array_key_exists($this->_get_args['format'], $this->_supported_formats))
    + 670:         {
    + 671:             return $this->_get_args['format'];
    + 672:         }
    + 673: 
    + 674:         // Otherwise, check the HTTP_ACCEPT (if it exists and we are allowed)
    + 675:         if ($this->config->item('rest_ignore_http_accept') === FALSE && $this->input->server('HTTP_ACCEPT'))
    + 676:         {
    + 677:             // Check all formats against the HTTP_ACCEPT header
    + 678:             foreach (array_keys($this->_supported_formats) as $format)
    + 679:             {
    + 680:                 // Has this format been requested?
    + 681:                 if (strpos($this->input->server('HTTP_ACCEPT'), $format) !== FALSE)
    + 682:                 {
    + 683:                     // If not HTML or XML assume its right and send it on its way
    + 684:                     if ($format !== 'html' && $format !== 'xml')
    + 685:                     {
    + 686:                         return $format;
    + 687:                     }
    + 688: 
    + 689:                     // HTML or XML have shown up as a match
    + 690:                     else
    + 691:                     {
    + 692:                         // If it is truly HTML, it wont want any XML
    + 693:                         if ($format == 'html' && strpos($this->input->server('HTTP_ACCEPT'), 'xml') === FALSE)
    + 694:                         {
    + 695:                             return $format;
    + 696:                         }
    + 697: 
    + 698:                         // If it is truly XML, it wont want any HTML
    + 699:                         elseif ($format == 'xml' && strpos($this->input->server('HTTP_ACCEPT'), 'html') === FALSE)
    + 700:                         {
    + 701:                             return $format;
    + 702:                         }
    + 703:                     }
    + 704:                 }
    + 705:             }
    + 706:         } // End HTTP_ACCEPT checking
    + 707: 
    + 708:         // Well, none of that has worked! Let's see if the controller has a default
    + 709:         if (empty($this->rest_format) === FALSE)
    + 710:         {
    + 711:             return $this->rest_format;
    + 712:         }
    + 713: 
    + 714:         // Just use the default format
    + 715:         return $this->config->item('rest_default_format');
    + 716:     }
    + 717: 
    + 718:     /**
    + 719:      * Get the HTTP request string e.g. get or post
      720:      *
    - 721:      * @return NULL|string The language code.
    + 721:      * @return string|NULL Supported request method as a lowercase string; otherwise, NULL if not supported
      722:      */
    - 723:     protected function _detect_lang()
    + 723:     protected function _detect_method()
      724:     {
    - 725:         if ( ! $lang = $this->input->server('HTTP_ACCEPT_LANGUAGE')) {
    - 726:             return NULL;
    - 727:         }
    - 728: 
    - 729:         // They might have sent a few, make it an array
    - 730:         if (strpos($lang, ',') !== FALSE) {
    - 731:             $langs = explode(',', $lang);
    - 732: 
    - 733:             $return_langs = array();
    - 734:             foreach ($langs as $lang) {
    - 735:                 // Remove weight and strip space
    - 736:                 list($lang) = explode(';', $lang);
    - 737:                 $return_langs[] = trim($lang);
    - 738:             }
    + 725:         // Declare a variable to store the method
    + 726:         $method = NULL;
    + 727: 
    + 728:         // Determine whether the 'enable_emulate_request' setting is enabled
    + 729:         if ($this->config->item('enable_emulate_request') === TRUE)
    + 730:         {
    + 731:             $method = $this->input->post('_method');
    + 732:             if ($method === NULL)
    + 733:             {
    + 734:                 $method = $this->input->server('HTTP_X_HTTP_METHOD_OVERRIDE');
    + 735:             }
    + 736: 
    + 737:             $method = strtolower($method);
    + 738:         }
      739: 
    - 740:             return $return_langs;
    - 741:         }
    - 742: 
    - 743:         // Nope, just return the string
    - 744:         return $lang;
    - 745:     }
    - 746: 
    - 747:     /**
    - 748:      * Log request
    - 749:      *
    - 750:      * Record the entry for awesomeness purposes
    + 740:         if (empty($method))
    + 741:         {
    + 742:             // Get the request method as a lowercase string.
    + 743:             $method = $this->input->method();
    + 744:         }
    + 745: 
    + 746:         return in_array($method, $this->allowed_http_methods) && method_exists($this, '_parse_' . $method) ? $method : 'get';
    + 747:     }
    + 748: 
    + 749:     /**
    + 750:      * See if the user has provided an API key
      751:      *
    - 752:      * @param  boolean $authorized
    - 753:      * @return object
    + 752:      * @access protected
    + 753:      * @return bool
      754:      */
    - 755:     protected function _log_request($authorized = FALSE)
    + 755:     protected function _detect_api_key()
      756:     {
    - 757:         $status = $this->rest->db->insert(config_item('rest_logs_table'), array(
    - 758:                     'uri' => $this->uri->uri_string(),
    - 759:                     'method' => $this->request->method,
    - 760:                     'params' => $this->_args ? (config_item('rest_logs_json_params') ? json_encode($this->_args) : serialize($this->_args)) : NULL,
    - 761:                     'api_key' => isset($this->rest->key) ? $this->rest->key : '',
    - 762:                     'ip_address' => $this->input->ip_address(),
    - 763:                     'time' => function_exists('now') ? now() : time(),
    - 764:                     'authorized' => $authorized
    - 765:                 ));
    - 766: 
    - 767:         $this->_insert_id = $this->rest->db->insert_id();
    - 768: 
    - 769:         return $status;
    - 770:     }
    - 771: 
    - 772:     /**
    - 773:      * Limiting requests
    - 774:      *
    - 775:      * Check if the requests are coming in a tad too fast.
    - 776:      *
    - 777:      * @param  string  $controller_method The method being called.
    - 778:      * @return boolean
    - 779:      */
    - 780:     protected function _check_limit($controller_method)
    - 781:     {
    - 782:         // They are special, or it might not even have a limit
    - 783:         if ( ! empty($this->rest->ignore_limits) or !isset($this->methods[$controller_method]['limit'])) {
    - 784:             // On your way sonny-jim.
    - 785:             return TRUE;
    - 786:         }
    - 787: 
    - 788:         // How many times can you get to this method an hour?
    - 789:         $limit = $this->methods[$controller_method]['limit'];
    - 790: 
    - 791:         // Get data on a keys usage
    - 792:         $result = $this->rest->db
    - 793:                 ->where('uri', $this->uri->uri_string())
    - 794:                 ->where('api_key', $this->rest->key)
    - 795:                 ->get(config_item('rest_limits_table'))
    - 796:                 ->row();
    - 797: 
    - 798:         // No calls yet for this key
    - 799:         if ( ! $result ) {
    - 800:             // Right, set one up from scratch
    - 801:             $this->rest->db->insert(config_item('rest_limits_table'), array(
    - 802:                 'uri' => $this->uri->uri_string(),
    - 803:                 'api_key' => isset($this->rest->key) ? $this->rest->key : '',
    - 804:                 'count' => 1,
    - 805:                 'hour_started' => time()
    - 806:             ));
    - 807:         }
    - 808: 
    - 809:         // Been an hour since they called
    - 810:         else if ($result->hour_started < time() - (60 * 60)) {
    - 811:             // Reset the started period
    - 812:             $this->rest->db
    - 813:                     ->where('uri', $this->uri->uri_string())
    - 814:                     ->where('api_key', isset($this->rest->key) ? $this->rest->key : '')
    - 815:                     ->set('hour_started', time())
    - 816:                     ->set('count', 1)
    - 817:                     ->update(config_item('rest_limits_table'));
    - 818:         }
    - 819: 
    - 820:         // They have called within the hour, so lets update
    - 821:         else {
    - 822:             // Your luck is out, you've called too many times!
    - 823:             if ($result->count >= $limit) {
    - 824:                 return FALSE;
    - 825:             }
    - 826: 
    - 827:             $this->rest->db
    - 828:                     ->where('uri', $this->uri->uri_string())
    - 829:                     ->where('api_key', $this->rest->key)
    - 830:                     ->set('count', 'count + 1', FALSE)
    - 831:                     ->update(config_item('rest_limits_table'));
    - 832:         }
    - 833: 
    - 834:         return TRUE;
    - 835:     }
    - 836: 
    - 837:     /**
    - 838:      * Auth override check
    - 839:      *
    - 840:      * Check if there is a specific auth type set for the current class/method
    - 841:      * being called.
    - 842:      *
    - 843:      * @return boolean
    - 844:      */
    - 845:     protected function _auth_override_check()
    - 846:     {
    - 847: 
    - 848:         // Assign the class/method auth type override array from the config
    - 849:         $this->overrides_array = $this->config->item('auth_override_class_method');
    - 850: 
    - 851:         // Check to see if the override array is even populated, otherwise return FALSE
    - 852:         if (empty($this->overrides_array)) {
    - 853:             return FALSE;
    - 854:         }
    + 757:         // Get the api key name variable set in the rest config file
    + 758:         $api_key_variable = $this->config->item('rest_key_name');
    + 759: 
    + 760:         // Work out the name of the SERVER entry based on config
    + 761:         $key_name = 'HTTP_' . strtoupper(str_replace('-', '_', $api_key_variable));
    + 762: 
    + 763:         $this->rest->key = NULL;
    + 764:         $this->rest->level = NULL;
    + 765:         $this->rest->user_id = NULL;
    + 766:         $this->rest->ignore_limits = FALSE;
    + 767: 
    + 768:         // Find the key from server or arguments
    + 769:         if (($key = isset($this->_args[$api_key_variable]) ? $this->_args[$api_key_variable] : $this->input->server($key_name)))
    + 770:         {
    + 771:             if (!($row = $this->rest->db->where($this->config->item('rest_key_column'), $key)->get($this->config->item('rest_keys_table'))->row()))
    + 772:             {
    + 773:                 return FALSE;
    + 774:             }
    + 775: 
    + 776:             $this->rest->key = $row->{$this->config->item('rest_key_column')};
    + 777: 
    + 778:             isset($row->user_id) && $this->rest->user_id = $row->user_id;
    + 779:             isset($row->level) && $this->rest->level = $row->level;
    + 780:             isset($row->ignore_limits) && $this->rest->ignore_limits = $row->ignore_limits;
    + 781: 
    + 782:             $this->_apiuser = $row;
    + 783: 
    + 784:             /*
    + 785:              * If "is private key" is enabled, compare the ip address with the list
    + 786:              * of valid ip addresses stored in the database.
    + 787:              */
    + 788:             if (empty($row->is_private_key) === FALSE)
    + 789:             {
    + 790:                 // Check for a list of valid ip addresses
    + 791:                 if (isset($row->ip_addresses))
    + 792:                 {
    + 793:                     // multiple ip addresses must be separated using a comma, explode and loop
    + 794:                     $list_ip_addresses = explode(',', $row->ip_addresses);
    + 795:                     $found_address = FALSE;
    + 796: 
    + 797:                     foreach ($list_ip_addresses as $ip_address)
    + 798:                     {
    + 799:                         if ($this->input->ip_address() == trim($ip_address))
    + 800:                         {
    + 801:                             // there is a match, set the the value to TRUE and break out of the loop
    + 802:                             $found_address = TRUE;
    + 803:                             break;
    + 804:                         }
    + 805:                     }
    + 806: 
    + 807:                     return $found_address;
    + 808:                 }
    + 809:                 else
    + 810:                 {
    + 811:                     // There should be at least one IP address for this private key.
    + 812:                     return FALSE;
    + 813:                 }
    + 814:             }
    + 815: 
    + 816:             return $row;
    + 817:         }
    + 818: 
    + 819:         // No key has been sent
    + 820:         return FALSE;
    + 821:     }
    + 822: 
    + 823:     /**
    + 824:      * What language do they want it in?
    + 825:      *
    + 826:      * @access protected
    + 827:      * @return NULL|string The language code.
    + 828:      */
    + 829:     protected function _detect_lang()
    + 830:     {
    + 831:         if (!$lang = $this->input->server('HTTP_ACCEPT_LANGUAGE'))
    + 832:         {
    + 833:             return NULL;
    + 834:         }
    + 835: 
    + 836:         // They might have sent a few, make it an array
    + 837:         if (strpos($lang, ',') !== FALSE)
    + 838:         {
    + 839:             $langs = explode(',', $lang);
    + 840: 
    + 841:             $return_langs = [];
    + 842:             foreach ($langs as $lang)
    + 843:             {
    + 844:                 // Remove weight and strip space
    + 845:                 list($lang) = explode(';', $lang);
    + 846:                 $return_langs[] = trim($lang);
    + 847:             }
    + 848: 
    + 849:             return $return_langs;
    + 850:         }
    + 851: 
    + 852:         // Nope, just return the string
    + 853:         return $lang;
    + 854:     }
      855: 
    - 856:         // check for wildcard flag for rules for classes
    - 857:         if(!empty($this->overrides_array[$this->router->class]['*'])){//check for class overides
    - 858:             // None auth override found, prepare nothing but send back a TRUE override flag
    - 859:             if ($this->overrides_array[$this->router->class]['*'] == 'none')
    - 860:             {
    - 861:                 return TRUE;
    - 862:             }
    - 863: 
    - 864:             // Basic auth override found, prepare basic
    - 865:             if ($this->overrides_array[$this->router->class]['*'] == 'basic')
    - 866:             {
    - 867:                 $this->_prepare_basic_auth();
    - 868:                 return TRUE;
    - 869:             }
    - 870: 
    - 871:             // Digest auth override found, prepare digest
    - 872:             if ($this->overrides_array[$this->router->class]['*'] == 'digest')
    - 873:             {
    - 874:                 $this->_prepare_digest_auth();
    - 875:                 return TRUE;
    - 876:             }
    - 877: 
    - 878:             // Whitelist auth override found, check client's ip against config whitelist
    - 879:             if ($this->overrides_array[$this->router->class]['*'] == 'whitelist')
    - 880:             {
    - 881:                 $this->_check_whitelist_auth();
    - 882:                 return TRUE;
    - 883:             }
    - 884:         }
    + 856:     /**
    + 857:      * Add the request to the log table
    + 858:      *
    + 859:      * @access protected
    + 860:      *
    + 861:      * @param bool $authorized TRUE the user is authorized; otherwise, FALSE
    + 862:      *
    + 863:      * @return bool TRUE the data was inserted; otherwise, FALSE
    + 864:      */
    + 865:     protected function _log_request($authorized = FALSE)
    + 866:     {
    + 867:         // Insert the request into the log table
    + 868:         $isInserted = $this->rest->db
    + 869:             ->insert(
    + 870:                 $this->config->item('rest_logs_table'), [
    + 871:                 'uri' => $this->uri->uri_string(),
    + 872:                 'method' => $this->request->method,
    + 873:                 'params' => $this->_args ? ($this->config->item('rest_logs_json_params') === TRUE ? json_encode($this->_args) : serialize($this->_args)) : NULL,
    + 874:                 'api_key' => isset($this->rest->key) ? $this->rest->key : '',
    + 875:                 'ip_address' => $this->input->ip_address(),
    + 876:                 'time' => now(), // Used to be: function_exists('now') ? now() : time()
    + 877:                 'authorized' => $authorized
    + 878:             ]);
    + 879: 
    + 880:         // Get the last insert id to update at a later stage of the request
    + 881:         $this->_insert_id = $this->rest->db->insert_id();
    + 882: 
    + 883:         return $isInserted;
    + 884:     }
      885: 
    - 886:         // Check to see if there's an override value set for the current class/method being called
    - 887:         if (empty($this->overrides_array[$this->router->class][$this->router->method])) {
    - 888:             return FALSE;
    - 889:         }
    - 890: 
    - 891:         // None auth override found, prepare nothing but send back a TRUE override flag
    - 892:         if ($this->overrides_array[$this->router->class][$this->router->method] == 'none') {
    - 893:             return TRUE;
    - 894:         }
    - 895: 
    - 896:         // Basic auth override found, prepare basic
    - 897:         if ($this->overrides_array[$this->router->class][$this->router->method] == 'basic') {
    - 898:             $this->_prepare_basic_auth();
    - 899: 
    - 900:             return TRUE;
    - 901:         }
    - 902: 
    - 903:         // Digest auth override found, prepare digest
    - 904:         if ($this->overrides_array[$this->router->class][$this->router->method] == 'digest') {
    - 905:             $this->_prepare_digest_auth();
    + 886:     /**
    + 887:      * Check if the requests to a controller method exceed a limit
    + 888:      *
    + 889:      * @access protected
    + 890:      *
    + 891:      * @param  string $controller_method The method being called
    + 892:      *
    + 893:      * @return bool TRUE the call limit is below the threshold; otherwise, FALSE
    + 894:      */
    + 895:     protected function _check_limit($controller_method)
    + 896:     {
    + 897:         // They are special, or it might not even have a limit
    + 898:         if (empty($this->rest->ignore_limits) === FALSE || isset($this->methods[$controller_method]['limit']) === FALSE)
    + 899:         {
    + 900:             // Everything is fine
    + 901:             return TRUE;
    + 902:         }
    + 903: 
    + 904:         // How many times can you get to this method in a defined timelimit (default: 1hr)?
    + 905:         $limit = $this->methods[$controller_method]['limit'];
      906: 
    - 907:             return TRUE;
    - 908:         }
    - 909: 
    - 910:         // Whitelist auth override found, check client's ip against config whitelist
    - 911:         if ($this->overrides_array[$this->router->class][$this->router->method] == 'whitelist') {
    - 912:             $this->_check_whitelist_auth();
    - 913: 
    - 914:             return TRUE;
    - 915:         }
    - 916: 
    - 917:         // Return FALSE when there is an override value set but it does not match
    - 918:         // 'basic', 'digest', or 'none'. (the value was misspelled)
    - 919:         return FALSE;
    - 920:     }
    + 907:         $uri_noext = $this->uri->uri_string();
    + 908:         if (strpos(strrev($this->uri->uri_string()), strrev($this->response->format)) === 0) 
    + 909:         { 
    + 910:             $uri_noext = substr($this->uri->uri_string(),0, -strlen($this->response->format)-1);
    + 911:         }
    + 912: 
    + 913:         // Get data about a keys' usage and limit to one row
    + 914:         $result = $this->rest->db
    + 915:             ->where('uri', $uri_noext)
    + 916:             ->where('api_key', $this->rest->key)
    + 917:             ->get($this->config->item('rest_limits_table'))
    + 918:             ->row();
    + 919: 
    + 920:         $timelimit = (isset($this->methods[$controller_method]['time']) ? $this->methods[$controller_method]['time'] : 60 * 60);
      921: 
    - 922:     /**
    - 923:      * Parse GET
    - 924:      */
    - 925:     protected function _parse_get()
    - 926:     {
    - 927:         // Fix for Issue #247
    - 928:         if ($this->input->is_cli_request()) {
    - 929:             $args = $_SERVER['argv'];
    - 930:             unset($args[0]);
    - 931:             $_SERVER['QUERY_STRING'] =  $_SERVER['PATH_INFO'] = $_SERVER['REQUEST_URI'] = '/' . implode('/', $args) . '/';
    + 922:         // No calls have been made for this key
    + 923:         if ($result === NULL)
    + 924:         {
    + 925:             // Create a new row for the following key
    + 926:             $this->rest->db->insert($this->config->item('rest_limits_table'), [
    + 927:                 'uri' => $this->uri->uri_string(),
    + 928:                 'api_key' => isset($this->rest->key) ? $this->rest->key : '',
    + 929:                 'count' => 1,
    + 930:                 'hour_started' => time()
    + 931:             ]);
      932:         }
      933: 
    - 934:         // Grab proper GET variables
    - 935:         parse_str(parse_url($_SERVER['REQUEST_URI'], PHP_URL_QUERY), $get);
    - 936: 
    - 937:         // Merge both the URI segments and GET params
    - 938:         $this->_get_args = array_merge($this->_get_args, $get);
    - 939:     }
    - 940: 
    - 941:     /**
    - 942:      * Parse POST
    - 943:      */
    - 944:     protected function _parse_post()
    - 945:     {
    - 946:         $this->_post_args = $_POST;
    - 947: 
    - 948:         $this->request->format and $this->request->body = file_get_contents('php://input');
    - 949:     }
    - 950: 
    - 951:     /**
    - 952:      * Parse PUT
    - 953:      */
    - 954:     protected function _parse_put()
    - 955:     {
    - 956:         // It might be a HTTP body
    - 957:         if ($this->request->format) {
    - 958:             $this->request->body = file_get_contents('php://input');
    - 959:         }
    - 960: 
    - 961:         // If no file type is provided, this is probably just arguments
    - 962:         else {
    - 963:             parse_str(file_get_contents('php://input'), $this->_put_args);
    - 964:         }
    + 934:         // Been a time limit (or by default an hour) since they called
    + 935:         elseif ($result->hour_started < (time() - $timelimit))
    + 936:         {
    + 937:             // Reset the started period and count
    + 938:             $this->rest->db
    + 939:                 ->where('uri', $uri_noext)
    + 940:                 ->where('api_key', isset($this->rest->key) ? $this->rest->key : '')
    + 941:                 ->set('hour_started', time())
    + 942:                 ->set('count', 1)
    + 943:                 ->update($this->config->item('rest_limits_table'));
    + 944:         }
    + 945: 
    + 946:         // They have called within the hour, so lets update
    + 947:         else
    + 948:         {
    + 949:             // The limit has been exceeded
    + 950:             if ($result->count >= $limit)
    + 951:             {
    + 952:                 return FALSE;
    + 953:             }
    + 954: 
    + 955:             // Increase the count by one
    + 956:             $this->rest->db
    + 957:                 ->where('uri', $uri_noext)
    + 958:                 ->where('api_key', $this->rest->key)
    + 959:                 ->set('count', 'count + 1', FALSE)
    + 960:                 ->update($this->config->item('rest_limits_table'));
    + 961:         }
    + 962: 
    + 963:         return TRUE;
    + 964:     }
      965: 
    - 966:     }
    - 967: 
    - 968:     /**
    - 969:      * Parse HEAD
    - 970:      */
    - 971:     protected function _parse_head()
    - 972:     {
    - 973:         // Grab proper HEAD variables
    - 974:         parse_str(parse_url($_SERVER['REQUEST_URI'], PHP_URL_QUERY), $head);
    - 975: 
    - 976:         // Merge both the URI segments and HEAD params
    - 977:         $this->_head_args = array_merge($this->_head_args, $head);
    - 978:     }
    - 979: 
    - 980:     /**
    - 981:      * Parse OPTIONS
    - 982:      */
    - 983:     protected function _parse_options()
    - 984:     {
    - 985:         // Grab proper OPTIONS variables
    - 986:         parse_str(parse_url($_SERVER['REQUEST_URI'], PHP_URL_QUERY), $options);
    - 987: 
    - 988:         // Merge both the URI segments and OPTIONS params
    - 989:         $this->_options_args = array_merge($this->_options_args, $options);
    - 990:     }
    - 991: 
    - 992:     /**
    - 993:      * Parse PATCH
    - 994:      */
    - 995:     protected function _parse_patch()
    - 996:     {
    - 997:         // It might be a HTTP body
    - 998:         if ($this->request->format) {
    - 999:             $this->request->body = file_get_contents('php://input');
    -1000:         }
    + 966:     /**
    + 967:      * Auth override check
    + 968:      * Check if there is a specific auth type set for the current class/method
    + 969:      * being called.
    + 970:      *
    + 971:      * @access protected
    + 972:      * @return bool
    + 973:      */
    + 974:     protected function _auth_override_check()
    + 975:     {
    + 976:         // Assign the class/method auth type override array from the config
    + 977:         $this->overrides_array = $this->config->item('auth_override_class_method');
    + 978: 
    + 979:         // Check to see if the override array is even populated, otherwise return FALSE
    + 980:         if (empty($this->overrides_array))
    + 981:         {
    + 982:             return FALSE;
    + 983:         }
    + 984: 
    + 985:         // check for wildcard flag for rules for classes
    + 986:         if (empty($this->overrides_array[$this->router->class]['*']) === FALSE) // Check for class overrides
    + 987:         {
    + 988:             // None auth override found, prepare nothing but send back a TRUE override flag
    + 989:             if ($this->overrides_array[$this->router->class]['*'] == 'none')
    + 990:             {
    + 991:                 return TRUE;
    + 992:             }
    + 993: 
    + 994:             // Basic auth override found, prepare basic
    + 995:             if ($this->overrides_array[$this->router->class]['*'] == 'basic')
    + 996:             {
    + 997:                 $this->_prepare_basic_auth();
    + 998: 
    + 999:                 return TRUE;
    +1000:             }
     1001: 
    -1002:         // If no file type is provided, this is probably just arguments
    -1003:         else {
    -1004:             parse_str(file_get_contents('php://input'), $this->_patch_args);
    -1005:         }
    -1006:     }
    -1007: 
    -1008:     /**
    -1009:      * Parse DELETE
    -1010:      */
    -1011:     protected function _parse_delete()
    -1012:     {
    -1013:         // Set up out DELETE variables (which shouldn't really exist, but sssh!)
    -1014:         parse_str(file_get_contents('php://input'), $this->_delete_args);
    -1015:     }
    -1016: 
    -1017:     // INPUT FUNCTION --------------------------------------------------------------
    +1002:             // Digest auth override found, prepare digest
    +1003:             if ($this->overrides_array[$this->router->class]['*'] == 'digest')
    +1004:             {
    +1005:                 $this->_prepare_digest_auth();
    +1006: 
    +1007:                 return TRUE;
    +1008:             }
    +1009: 
    +1010:             // Whitelist auth override found, check client's ip against config whitelist
    +1011:             if ($this->overrides_array[$this->router->class]['*'] == 'whitelist')
    +1012:             {
    +1013:                 $this->_check_whitelist_auth();
    +1014: 
    +1015:                 return TRUE;
    +1016:             }
    +1017:         }
     1018: 
    -1019:     /**
    -1020:      * Retrieve a value from the GET request arguments.
    -1021:      *
    -1022:      * @param  string  $key       The key for the GET request argument to retrieve
    -1023:      * @param  boolean $xss_clean Whether the value should be XSS cleaned or not.
    -1024:      * @return string  The GET argument value.
    -1025:      */
    -1026:     public function get($key = NULL, $xss_clean = TRUE)
    -1027:     {
    -1028:         if ($key === NULL) {
    -1029:             return $this->_get_args;
    -1030:         }
    -1031: 
    -1032:         return array_key_exists($key, $this->_get_args) ? $this->_xss_clean($this->_get_args[$key], $xss_clean) : FALSE;
    -1033:     }
    -1034: 
    -1035:     /**
    -1036:      * This function retrieves a values from the OPTIONS request arguments
    -1037:      *
    -1038:      * @param  string  $key       The OPTIONS/GET argument key
    -1039:      * @param  boolean $xss_clean Whether the value should be XSS cleaned or not
    -1040:      * @return string  The OPTIONS/GET argument value
    -1041:      */
    -1042:     public function options($key = NULL, $xss_clean = TRUE)
    -1043:     {
    -1044:         if ($key === NULL) {
    -1045:             return $this->_options_args;
    -1046:         }
    -1047: 
    -1048:         return array_key_exists($key, $this->_options_args) ? $this->_xss_clean($this->_options_args[$key], $xss_clean) : FALSE;
    -1049:     }
    -1050: 
    -1051:     /**
    -1052:      * This function retrieves a values from the HEAD request arguments
    -1053:      *
    -1054:      * @param  string  $key       The HEAD/GET argument key
    -1055:      * @param  boolean $xss_clean Whether the value should be XSS cleaned or not
    -1056:      * @return string  The HEAD/GET argument value
    -1057:      */
    -1058:     public function head($key = NULL, $xss_clean = TRUE)
    -1059:     {
    -1060:         if ($key === NULL) {
    -1061:             return $this->head_args;
    -1062:         }
    -1063: 
    -1064:         return array_key_exists($key, $this->head_args) ? $this->_xss_clean($this->head_args[$key], $xss_clean) : FALSE;
    -1065:     }
    -1066: 
    -1067:     /**
    -1068:      * Retrieve a value from the POST request arguments.
    -1069:      *
    -1070:      * @param  string  $key       The key for the POST request argument to retrieve
    -1071:      * @param  boolean $xss_clean Whether the value should be XSS cleaned or not.
    -1072:      * @return string  The POST argument value.
    -1073:      */
    -1074:     public function post($key = NULL, $xss_clean = TRUE)
    -1075:     {
    -1076:         if ($key === NULL) {
    -1077:             return $this->_post_args;
    -1078:         }
    -1079: 
    -1080:         return array_key_exists($key, $this->_post_args) ? $this->_xss_clean($this->_post_args[$key], $xss_clean) : FALSE;
    -1081:     }
    -1082: 
    -1083:     /**
    -1084:      * Retrieve a value from the PUT request arguments.
    -1085:      *
    -1086:      * @param  string  $key       The key for the PUT request argument to retrieve
    -1087:      * @param  boolean $xss_clean Whether the value should be XSS cleaned or not.
    -1088:      * @return string  The PUT argument value.
    -1089:      */
    -1090:     public function put($key = NULL, $xss_clean = TRUE)
    -1091:     {
    -1092:         if ($key === NULL) {
    -1093:             return $this->_put_args;
    -1094:         }
    -1095: 
    -1096:         return array_key_exists($key, $this->_put_args) ? $this->_xss_clean($this->_put_args[$key], $xss_clean) : FALSE;
    +1019:         // Check to see if there's an override value set for the current class/method being called
    +1020:         if (empty($this->overrides_array[$this->router->class][$this->router->method]))
    +1021:         {
    +1022:             return FALSE;
    +1023:         }
    +1024: 
    +1025:         // None auth override found, prepare nothing but send back a TRUE override flag
    +1026:         if ($this->overrides_array[$this->router->class][$this->router->method] == 'none')
    +1027:         {
    +1028:             return TRUE;
    +1029:         }
    +1030: 
    +1031:         // Basic auth override found, prepare basic
    +1032:         if ($this->overrides_array[$this->router->class][$this->router->method] == 'basic')
    +1033:         {
    +1034:             $this->_prepare_basic_auth();
    +1035: 
    +1036:             return TRUE;
    +1037:         }
    +1038: 
    +1039:         // Digest auth override found, prepare digest
    +1040:         if ($this->overrides_array[$this->router->class][$this->router->method] == 'digest')
    +1041:         {
    +1042:             $this->_prepare_digest_auth();
    +1043: 
    +1044:             return TRUE;
    +1045:         }
    +1046: 
    +1047:         // Whitelist auth override found, check client's ip against config whitelist
    +1048:         if ($this->overrides_array[$this->router->class][$this->router->method] == 'whitelist')
    +1049:         {
    +1050:             $this->_check_whitelist_auth();
    +1051: 
    +1052:             return TRUE;
    +1053:         }
    +1054: 
    +1055:         // Return FALSE when there is an override value set but it does not match
    +1056:         // 'basic', 'digest', or 'none'. (the value was misspelled)
    +1057:         return FALSE;
    +1058:     }
    +1059: 
    +1060:     /**
    +1061:      * Parse the GET request arguments
    +1062:      *
    +1063:      * @access protected
    +1064:      * @return void
    +1065:      */
    +1066:     protected function _parse_get()
    +1067:     {
    +1068:         // Declare a variable that will hold the REQUEST_URI
    +1069:         $request_uri = NULL;
    +1070: 
    +1071:         // Fix for Issue #247
    +1072:         if (is_cli())
    +1073:         {
    +1074:             $args = $this->input->server('argv');
    +1075:             unset($args[0]);
    +1076:             // Combine the arguments using '/' as the delimiter
    +1077:             $request_uri = '/' . implode('/', $args) . '/';
    +1078: 
    +1079:             // Set the following server variables
    +1080:             $_SERVER['REQUEST_URI'] = $request_uri;
    +1081:             $_SERVER['PATH_INFO'] = $request_uri;
    +1082:             $_SERVER['QUERY_STRING'] = $request_uri;
    +1083:         }
    +1084:         else
    +1085:         {
    +1086:             $request_uri = $this->input->server('REQUEST_URI');
    +1087:         }
    +1088: 
    +1089:         // Declare a variable that will hold the parameters
    +1090:         $get = NULL;
    +1091: 
    +1092:         // Grab the GET variables from the query string
    +1093:         parse_str(parse_url($request_uri, PHP_URL_QUERY), $get);
    +1094: 
    +1095:         // Merge both the URI segments and GET params
    +1096:         $this->_get_args = array_merge($this->_get_args, $get);
     1097:     }
     1098: 
     1099:     /**
    -1100:      * Retrieve a value from the DELETE request arguments.
    +1100:      * Parse the POST request arguments
     1101:      *
    -1102:      * @param  string  $key       The key for the DELETE request argument to retrieve
    -1103:      * @param  boolean $xss_clean Whether the value should be XSS cleaned or not.
    -1104:      * @return string  The DELETE argument value.
    -1105:      */
    -1106:     public function delete($key = NULL, $xss_clean = TRUE)
    -1107:     {
    -1108:         if ($key === NULL) {
    -1109:             return $this->_delete_args;
    -1110:         }
    -1111: 
    -1112:         return array_key_exists($key, $this->_delete_args) ? $this->_xss_clean($this->_delete_args[$key], $xss_clean) : FALSE;
    +1102:      * @access protected
    +1103:      * @return void
    +1104:      */
    +1105:     protected function _parse_post()
    +1106:     {
    +1107:         $this->_post_args = $_POST;
    +1108: 
    +1109:         if ($this->request->format)
    +1110:         {
    +1111:             $this->request->body = $this->input->raw_input_stream;
    +1112:         }
     1113:     }
     1114: 
     1115:     /**
    -1116:      * Retrieve a value from the PATCH request arguments.
    +1116:      * Parse the PUT request arguments
     1117:      *
    -1118:      * @param  string  $key       The key for the PATCH request argument to retrieve
    -1119:      * @param  boolean $xss_clean Whether the value should be XSS cleaned or not.
    -1120:      * @return string  The PATCH argument value.
    -1121:      */
    -1122:     public function patch($key = NULL, $xss_clean = TRUE)
    -1123:     {
    -1124:         if ($key === NULL) {
    -1125:             return $this->_patch_args;
    +1118:      * @access protected
    +1119:      * @return void
    +1120:      */
    +1121:     protected function _parse_put()
    +1122:     {
    +1123:         if ($this->request->format)
    +1124:         {
    +1125:             $this->request->body = $this->input->raw_input_stream;
     1126:         }
    -1127: 
    -1128:         return array_key_exists($key, $this->_patch_args) ? $this->_xss_clean($this->_patch_args[$key], $xss_clean) : FALSE;
    -1129:     }
    -1130: 
    -1131:     /**
    -1132:      * Process to protect from XSS attacks.
    -1133:      *
    -1134:      * @param  string  $val     The input.
    -1135:      * @param  boolean $process Do clean or note the input.
    -1136:      * @return string
    -1137:      */
    -1138:     protected function _xss_clean($val, $process)
    -1139:     {
    -1140:         if (CI_VERSION < 2) {
    -1141:             return $process ? $this->input->xss_clean($val) : $val;
    -1142:         }
    -1143: 
    -1144:         return $process ? $this->security->xss_clean($val) : $val;
    -1145:     }
    -1146: 
    -1147:     /**
    -1148:      * Retrieve the validation errors.
    -1149:      *
    -1150:      * @return array
    -1151:      */
    -1152:     public function validation_errors()
    -1153:     {
    -1154:         $string = strip_tags($this->form_validation->error_string());
    -1155: 
    -1156:         return explode("\n", trim($string, "\n"));
    -1157:     }
    -1158: 
    -1159:     // SECURITY FUNCTIONS ---------------------------------------------------------
    -1160: 
    -1161:     /**
    -1162:      * Perform LDAP Authentication
    -1163:      *
    -1164:      * @param  string  $username The username to validate
    -1165:      * @param  string  $password The password to validate
    -1166:      * @return boolean
    -1167:      */
    -1168:     protected function _perform_ldap_auth($username = '', $password = NULL)
    -1169:     {
    -1170:         if (empty($username)) {
    -1171:             log_message('debug', 'LDAP Auth: failure, empty username');
    -1172: 
    -1173:             return FALSE;
    -1174:         }
    -1175: 
    -1176:         log_message('debug', 'LDAP Auth: Loading Config');
    -1177: 
    -1178:         $this->config->load('ldap.php', TRUE);
    -1179: 
    -1180:         $ldap = array(
    -1181:             'timeout' => $this->config->item('timeout', 'ldap');
    -1182:             'host'    => $this->config->item('server', 'ldap');
    -1183:             'port'    => $this->config->item('port', 'ldap');
    -1184:             'rdn'     => $this->config->item('binduser', 'ldap');
    -1185:             'pass'    => $this->config->item('bindpw', 'ldap');
    -1186:             'basedn'  => $this->config->item('basedn', 'ldap');
    -1187:           );
    -1188: 
    -1189:         log_message('debug', 'LDAP Auth: Connect to ' . $ldaphost);
    -1190: 
    -1191:         $ldapconfig['authrealm'] = $this->config->item('domain', 'ldap');
    -1192: 
    -1193:         // connect to ldap server
    -1194:         $ldapconn = ldap_connect($ldap['host'], $ldap['port']);
    -1195: 
    -1196:         if ($ldapconn) {
    -1197: 
    -1198:             log_message('debug', 'Setting timeout to ' . $ldap['timeout'] . ' seconds');
    -1199: 
    -1200:             ldap_set_option($ldapconn, LDAP_OPT_NETWORK_TIMEOUT, $ldap['timeout']);
    -1201: 
    -1202:             log_message('debug', 'LDAP Auth: Binding to ' . $ldap['host'] . ' with dn ' . $ldap['rdn']);
    -1203: 
    -1204:             // binding to ldap server
    -1205:             $ldapbind = ldap_bind($ldapconn, $ldap['rdn'], $ldap['pass']);
    +1127:         else
    +1128:         {
    +1129:             // If no filetype is provided, then there are probably just arguments
    +1130:             if ($this->input->method() === 'put')
    +1131:             {
    +1132:                 $this->_put_args = $this->input->input_stream();
    +1133:             }
    +1134:         }
    +1135:     }
    +1136: 
    +1137:     /**
    +1138:      * Parse the HEAD request arguments
    +1139:      *
    +1140:      * @access protected
    +1141:      * @return void
    +1142:      */
    +1143:     protected function _parse_head()
    +1144:     {
    +1145:         // Parse the HEAD variables
    +1146:         parse_str(parse_url($this->input->server('REQUEST_URI'), PHP_URL_QUERY), $head);
    +1147: 
    +1148:         // Merge both the URI segments and HEAD params
    +1149:         $this->_head_args = array_merge($this->_head_args, $head);
    +1150:     }
    +1151: 
    +1152:     /**
    +1153:      * Parse the OPTIONS request arguments
    +1154:      *
    +1155:      * @access protected
    +1156:      * @return void
    +1157:      */
    +1158:     protected function _parse_options()
    +1159:     {
    +1160:         // Parse the OPTIONS variables
    +1161:         parse_str(parse_url($this->input->server('REQUEST_URI'), PHP_URL_QUERY), $options);
    +1162: 
    +1163:         // Merge both the URI segments and OPTIONS params
    +1164:         $this->_options_args = array_merge($this->_options_args, $options);
    +1165:     }
    +1166: 
    +1167:     /**
    +1168:      * Parse the PATCH request arguments
    +1169:      *
    +1170:      * @access protected
    +1171:      * @return void
    +1172:      */
    +1173:     protected function _parse_patch()
    +1174:     {
    +1175:         // It might be a HTTP body
    +1176:         if ($this->request->format)
    +1177:         {
    +1178:             $this->request->body = $this->input->raw_input_stream;
    +1179:         }
    +1180:         else
    +1181:         {
    +1182:             // If no filetype is provided, then there are probably just arguments
    +1183:             if ($this->input->method() === 'patch')
    +1184:             {
    +1185:                 $this->_patch_args = $this->input->input_stream();
    +1186:             }
    +1187:         }
    +1188:     }
    +1189: 
    +1190:     /**
    +1191:      * Parse the DELETE request arguments
    +1192:      *
    +1193:      * @access protected
    +1194:      * @return void
    +1195:      */
    +1196:     protected function _parse_delete()
    +1197:     {
    +1198:         // These should exist if a DELETE request
    +1199:         if ($this->input->method() === 'delete')
    +1200:         {
    +1201:             $this->_delete_args = $this->input->input_stream();
    +1202:         }
    +1203:     }
    +1204: 
    +1205:     // INPUT FUNCTION --------------------------------------------------------------
     1206: 
    -1207:             // verify binding
    -1208:             if ($ldapbind) {
    -1209:                 log_message('debug', 'LDAP Auth: bind successful');
    -1210:             } else {
    -1211:                 log_message('error', 'LDAP Auth: bind unsuccessful');
    -1212: 
    -1213:                 return FALSE;
    -1214:             }
    -1215: 
    -1216:         }
    -1217: 
    -1218:         // search for user
    -1219:         if (($res_id = ldap_search( $ldapconn, $ldap['basedn'], "uid=$username")) == FALSE) {
    -1220:             log_message('error', 'LDAP Auth: User ' . $username . ' not found in search');
    -1221: 
    -1222:             return FALSE;
    +1207:     /**
    +1208:      * Retrieve a value from a GET request
    +1209:      *
    +1210:      * @access public
    +1211:      *
    +1212:      * @param NULL $key Key to retrieve from the GET request
    +1213:      * If NULL an array of arguments is returned
    +1214:      * @param NULL $xss_clean Whether to apply XSS filtering
    +1215:      *
    +1216:      * @return array|string|FALSE Value from the GET request; otherwise, FALSE
    +1217:      */
    +1218:     public function get($key = NULL, $xss_clean = NULL)
    +1219:     {
    +1220:         if ($key === NULL)
    +1221:         {
    +1222:             return $this->_get_args;
     1223:         }
     1224: 
    -1225:         if (ldap_count_entries($ldapconn, $res_id) != 1) {
    -1226:             log_message('error', 'LDAP Auth: failure, username ' . $username . 'found more than once');
    +1225:         return array_key_exists($key, $this->_get_args) ? $this->_xss_clean($this->_get_args[$key], $xss_clean) : FALSE;
    +1226:     }
     1227: 
    -1228:             return FALSE;
    -1229:         }
    -1230: 
    -1231:         if (( $entry_id = ldap_first_entry($ldapconn, $res_id))== FALSE) {
    -1232:             log_message('error', 'LDAP Auth: failure, entry of searchresult could not be fetched');
    -1233: 
    -1234:             return FALSE;
    -1235:         }
    -1236: 
    -1237:         if (( $user_dn = ldap_get_dn($ldapconn, $entry_id)) == FALSE) {
    -1238:             log_message('error', 'LDAP Auth: failure, user-dn could not be fetched');
    -1239: 
    -1240:             return FALSE;
    -1241:         }
    -1242: 
    -1243:         // User found, could not authenticate as user
    -1244:         if (($link_id = ldap_bind($ldapconn, $user_dn, $password)) == FALSE) {
    -1245:             log_message('error', 'LDAP Auth: failure, username/password did not match: ' . $user_dn);
    -1246: 
    -1247:             return FALSE;
    -1248:         }
    -1249: 
    -1250:         log_message('debug', 'LDAP Auth: Success ' . $user_dn . ' authenticated successfully');
    -1251: 
    -1252:         $this->_user_ldap_dn = $user_dn;
    -1253:         ldap_close($ldapconn);
    -1254: 
    -1255:         return TRUE;
    -1256:     }
    -1257: 
    -1258:     /**
    -1259:      * Perform Library Authentication - Override this function to change the way the library is called
    -1260:      *
    -1261:      * @param  string  $username The username to validate
    -1262:      * @param  string  $password The password to validate
    -1263:      * @return boolean
    -1264:      */
    -1265:     protected function _perform_library_auth($username = '', $password = NULL)
    -1266:     {
    -1267:         if (empty($username)) {
    -1268:             log_message('error', 'Library Auth: failure, empty username');
    -1269:             return FALSE;
    -1270:         }
    -1271: 
    -1272:         $auth_library_class     = strtolower($this->config->item('auth_library_class'));
    -1273:         $auth_library_function  = strtolower($this->config->item('auth_library_function'));
    -1274: 
    -1275:         if (empty($auth_library_class)) {
    -1276:             log_message('debug', 'Library Auth: failure, empty auth_library_class');
    -1277:             return FALSE;
    -1278:         }
    -1279: 
    -1280:         if (empty($auth_library_function)) {
    -1281:             log_message('debug', 'Library Auth: failure, empty auth_library_function');
    -1282:             return FALSE;
    -1283:         }
    -1284: 
    -1285:         if (!is_callable(array($auth_library_class, $auth_library_function))) {
    -1286:             $this->load->library($auth_library_class);
    -1287:         }
    -1288: 
    -1289:         return $this->{$auth_library_class}->$auth_library_function($username, $password);
    -1290:     }
    -1291: 
    -1292:     /**
    -1293:      * Check if the user is logged in.
    -1294:      *
    -1295:      * @param  string  $username The user's name
    -1296:      * @param  string  $password The user's password
    -1297:      * @return boolean
    -1298:      */
    -1299:     protected function _check_login($username = '', $password = FALSE)
    -1300:     {
    -1301:         if (empty($username)) {
    -1302:             return FALSE;
    -1303:         }
    -1304: 
    -1305:         $auth_source = strtolower($this->config->item('auth_source'));
    -1306:         $rest_auth = strtolower($this->config->item('rest_auth'));
    -1307:         $valid_logins = $this->config->item('rest_valid_logins');
    +1228:     /**
    +1229:      * Retrieve a value from a OPTIONS request
    +1230:      *
    +1231:      * @access public
    +1232:      *
    +1233:      * @param NULL $key Key to retrieve from the OPTIONS request.
    +1234:      * If NULL an array of arguments is returned
    +1235:      * @param NULL $xss_clean Whether to apply XSS filtering
    +1236:      *
    +1237:      * @return array|string|FALSE Value from the OPTIONS request; otherwise, FALSE
    +1238:      */
    +1239:     public function options($key = NULL, $xss_clean = NULL)
    +1240:     {
    +1241:         if ($key === NULL)
    +1242:         {
    +1243:             return $this->_options_args;
    +1244:         }
    +1245: 
    +1246:         return array_key_exists($key, $this->_options_args) ? $this->_xss_clean($this->_options_args[$key], $xss_clean) : FALSE;
    +1247:     }
    +1248: 
    +1249:     /**
    +1250:      * Retrieve a value from a HEAD request
    +1251:      *
    +1252:      * @access public
    +1253:      *
    +1254:      * @param NULL $key Key to retrieve from the HEAD request
    +1255:      * If NULL an array of arguments is returned
    +1256:      * @param NULL $xss_clean Whether to apply XSS filtering
    +1257:      *
    +1258:      * @return array|string|FALSE Value from the HEAD request; otherwise, FALSE
    +1259:      */
    +1260:     public function head($key = NULL, $xss_clean = NULL)
    +1261:     {
    +1262:         if ($key === NULL)
    +1263:         {
    +1264:             return $this->head_args;
    +1265:         }
    +1266: 
    +1267:         return array_key_exists($key, $this->head_args) ? $this->_xss_clean($this->head_args[$key], $xss_clean) : FALSE;
    +1268:     }
    +1269: 
    +1270:     /**
    +1271:      * Retrieve a value from a POST request
    +1272:      *
    +1273:      * @access public
    +1274:      *
    +1275:      * @param NULL $key Key to retrieve from the POST request
    +1276:      * If NULL an array of arguments is returned
    +1277:      * @param NULL $xss_clean Whether to apply XSS filtering
    +1278:      *
    +1279:      * @return array|string|FALSE Value from the POST request; otherwise, FALSE
    +1280:      */
    +1281:     public function post($key = NULL, $xss_clean = NULL)
    +1282:     {
    +1283:         if ($key === NULL)
    +1284:         {
    +1285:             return $this->_post_args;
    +1286:         }
    +1287: 
    +1288:         return array_key_exists($key, $this->_post_args) ? $this->_xss_clean($this->_post_args[$key], $xss_clean) : FALSE;
    +1289:     }
    +1290: 
    +1291:     /**
    +1292:      * Retrieve a value from a PUT request
    +1293:      *
    +1294:      * @access public
    +1295:      *
    +1296:      * @param NULL $key Key to retrieve from the PUT request
    +1297:      * If NULL an array of arguments is returned
    +1298:      * @param NULL $xss_clean Whether to apply XSS filtering
    +1299:      *
    +1300:      * @return array|string|FALSE Value from the PUT request; otherwise, FALSE
    +1301:      */
    +1302:     public function put($key = NULL, $xss_clean = NULL)
    +1303:     {
    +1304:         if ($key === NULL)
    +1305:         {
    +1306:             return $this->_put_args;
    +1307:         }
     1308: 
    -1309:         if (!$this->config->item('auth_source') && $rest_auth == 'digest') { // for digest we do not have a password passed as argument
    -1310:             return md5($username.':'.$this->config->item('rest_realm').':'.(isset($valid_logins[$username])?$valid_logins[$username]:''));
    -1311:         }
    -1312: 
    -1313:         if ($password === FALSE) {
    -1314:             return FALSE;
    -1315:         }
    -1316: 
    -1317:         if ($auth_source == 'ldap') {
    -1318:             log_message('debug', 'performing LDAP authentication for $username');
    -1319: 
    -1320:             return $this->_perform_ldap_auth($username, $password);
    -1321:         }
    -1322: 
    -1323:         if ($auth_source == 'library') {
    -1324:             log_message('debug', 'performing Library authentication for '.$username);
    -1325: 
    -1326:             return $this->_perform_library_auth($username, $password);
    -1327:         }
    -1328: 
    -1329:         if (!array_key_exists($username, $valid_logins)) {
    -1330:             return FALSE;
    -1331:         }
    +1309:         return array_key_exists($key, $this->_put_args) ? $this->_xss_clean($this->_put_args[$key], $xss_clean) : FALSE;
    +1310:     }
    +1311: 
    +1312:     /**
    +1313:      * Retrieve a value from a DELETE request
    +1314:      *
    +1315:      * @access public
    +1316:      *
    +1317:      * @param NULL $key Key to retrieve from the DELETE request
    +1318:      * If NULL an array of arguments is returned
    +1319:      * @param NULL $xss_clean Whether to apply XSS filtering
    +1320:      *
    +1321:      * @return array|string|FALSE Value from the DELETE request; otherwise, FALSE
    +1322:      */
    +1323:     public function delete($key = NULL, $xss_clean = NULL)
    +1324:     {
    +1325:         if ($key === NULL)
    +1326:         {
    +1327:             return $this->_delete_args;
    +1328:         }
    +1329: 
    +1330:         return array_key_exists($key, $this->_delete_args) ? $this->_xss_clean($this->_delete_args[$key], $xss_clean) : FALSE;
    +1331:     }
     1332: 
    -1333:         if ($valid_logins[$username] != $password) {
    -1334:             return FALSE;
    -1335:         }
    -1336: 
    -1337:         return TRUE;
    -1338:     }
    -1339: 
    -1340:     /**
    -1341:      * Check to see if the user is logged into the web app with a php session key.
    -1342:      */
    -1343:     protected function _check_php_session()
    -1344:     {
    -1345:         $key = $this->config->item('auth_source');
    -1346:         if (!$this->session->userdata($key)) {
    -1347:             $this->response(array('status' => FALSE, 'error' => 'Not Authorized'), 401);
    -1348:         }
    -1349:     }
    +1333:     /**
    +1334:      * Retrieve a value from a PATCH request
    +1335:      *
    +1336:      * @access public
    +1337:      *
    +1338:      * @param NULL $key Key to retrieve from the PATCH request
    +1339:      * If NULL an array of arguments is returned
    +1340:      * @param NULL $xss_clean Whether to apply XSS filtering
    +1341:      *
    +1342:      * @return array|string|FALSE Value from the PATCH request; otherwise, FALSE
    +1343:      */
    +1344:     public function patch($key = NULL, $xss_clean = NULL)
    +1345:     {
    +1346:         if ($key === NULL)
    +1347:         {
    +1348:             return $this->_patch_args;
    +1349:         }
     1350: 
    -1351:     /**
    -1352:      * @todo document this.
    -1353:      */
    -1354:     protected function _prepare_basic_auth()
    -1355:     {
    -1356:         // If whitelist is enabled it has the first chance to kick them out
    -1357:         if (config_item('rest_ip_whitelist_enabled')) {
    -1358:             $this->_check_whitelist_auth();
    -1359:         }
    -1360: 
    -1361:         $username = NULL;
    -1362:         $password = NULL;
    -1363: 
    -1364:         // mod_php
    -1365:         if ($this->input->server('PHP_AUTH_USER')) {
    -1366:             $username = $this->input->server('PHP_AUTH_USER');
    -1367:             $password = $this->input->server('PHP_AUTH_PW');
    -1368:         }
    -1369: 
    -1370:         // most other servers
    -1371:         elseif ($this->input->server('HTTP_AUTHENTICATION')) {
    -1372:             if (strpos(strtolower($this->input->server('HTTP_AUTHENTICATION')), 'basic') === 0) {
    -1373:                 list($username, $password) = explode(':', base64_decode(substr($this->input->server('HTTP_AUTHORIZATION'), 6)));
    -1374:             }
    -1375:         }
    -1376: 
    -1377:         if ( ! $this->_check_login($username, $password)) {
    -1378:             $this->_force_login();
    -1379:         }
    -1380:     }
    +1351:         return array_key_exists($key, $this->_patch_args) ? $this->_xss_clean($this->_patch_args[$key], $xss_clean) : FALSE;
    +1352:     }
    +1353: 
    +1354:     /**
    +1355:      * Sanitizes data so that Cross Site Scripting Hacks can be
    +1356:      * prevented.
    +1357:      *
    +1358:      * @access protected
    +1359:      *
    +1360:      * @param  string $value Input data
    +1361:      * @param  bool $xss_clean Whether to apply XSS filtering
    +1362:      *
    +1363:      * @return string
    +1364:      */
    +1365:     protected function _xss_clean($value, $xss_clean)
    +1366:     {
    +1367:         is_bool($xss_clean) OR $xss_clean = $this->_enable_xss;
    +1368: 
    +1369:         return $xss_clean === TRUE ? $this->security->xss_clean($value) : $value;
    +1370:     }
    +1371: 
    +1372:     /**
    +1373:      * Retrieve the validation errors
    +1374:      *
    +1375:      * @access public
    +1376:      * @return array
    +1377:      */
    +1378:     public function validation_errors()
    +1379:     {
    +1380:         $string = strip_tags($this->form_validation->error_string());
     1381: 
    -1382:     /**
    -1383:      * @todo Document this.
    -1384:      */
    -1385:     protected function _prepare_digest_auth()
    -1386:     {
    -1387:         // If whitelist is enabled it has the first chance to kick them out
    -1388:         if (config_item('rest_ip_whitelist_enabled')) {
    -1389:             $this->_check_whitelist_auth();
    -1390:         }
    -1391: 
    -1392:         $uniqid = uniqid(""); // Empty argument for backward compatibility
    -1393:         // We need to test which server authentication variable to use
    -1394:         // because the PHP ISAPI module in IIS acts different from CGI
    -1395:         if ($this->input->server('PHP_AUTH_DIGEST')) {
    -1396:             $digest_string = $this->input->server('PHP_AUTH_DIGEST');
    -1397:         } elseif ($this->input->server('HTTP_AUTHORIZATION')) {
    -1398:             $digest_string = $this->input->server('HTTP_AUTHORIZATION');
    -1399:         } else {
    -1400:             $digest_string = "";
    -1401:         }
    +1382:         return explode("\n", trim($string, "\n"));
    +1383:     }
    +1384: 
    +1385:     // SECURITY FUNCTIONS ---------------------------------------------------------
    +1386: 
    +1387:     /**
    +1388:      * Perform LDAP Authentication
    +1389:      *
    +1390:      * @access protected
    +1391:      *
    +1392:      * @param  string $username The username to validate
    +1393:      * @param  string $password The password to validate
    +1394:      *
    +1395:      * @return bool
    +1396:      */
    +1397:     protected function _perform_ldap_auth($username = '', $password = NULL)
    +1398:     {
    +1399:         if (empty($username))
    +1400:         {
    +1401:             log_message('debug', 'LDAP Auth: failure, empty username');
     1402: 
    -1403:         // The $_SESSION['error_prompted'] variable is used to ask the password
    -1404:         // again if none given or if the user enters wrong auth information.
    -1405:         if (empty($digest_string)) {
    -1406:             $this->_force_login($uniqid);
    -1407:         }
    -1408: 
    -1409:         // We need to retrieve authentication informations from the $auth_data variable
    -1410:         $matches = array();
    -1411:         preg_match_all('@(username|nonce|uri|nc|cnonce|qop|response)=[\'"]?([^\'",]+)@', $digest_string, $matches);
    -1412:         $digest = (empty($matches[1]) || empty($matches[2])) ? array() : array_combine($matches[1], $matches[2]);
    -1413: 
    -1414:         // For digest authentication the library function should return already stored md5(username:restrealm:password) for that username @see rest.php::auth_library_function config
    -1415:         $A1 = $this->_check_login($digest['username'], TRUE);
    -1416:         if ( ! array_key_exists('username', $digest) or ! $A1 ) {
    -1417:             $this->_force_login($uniqid);
    -1418:         }
    -1419: 
    -1420:         $A2 = md5(strtoupper($this->request->method).':'.$digest['uri']);
    -1421:         $valid_response = md5($A1.':'.$digest['nonce'].':'.$digest['nc'].':'.$digest['cnonce'].':'.$digest['qop'].':'.$A2);
    -1422: 
    -1423:         if ($digest['response'] != $valid_response) {
    -1424:             $this->response(array(config_item('rest_status_field_name') => 0, config_item('rest_message_field_name') => 'Invalid credentials'), 401);
    -1425:             exit;
    -1426:         }
    -1427:     }
    -1428: 
    -1429:     /**
    -1430:      * Check if the client's ip is in the 'rest_ip_blacklist' config
    -1431:      */
    -1432:     protected function _check_blacklist_auth()
    -1433:     {
    -1434:         $blacklist = explode(',', config_item('rest_ip_blacklist'));
    -1435: 
    -1436:         foreach ($blacklist AS &$ip) {
    -1437:             $ip = trim($ip);
    -1438:         }
    -1439: 
    -1440:         if (in_array($this->input->ip_address(), $blacklist)) {
    -1441:             $this->response(array('status' => FALSE, 'error' => 'IP Denied'), 401);
    -1442:         }
    -1443:     }
    -1444: 
    -1445:     /**
    -1446:      * Check if the client's ip is in the 'rest_ip_whitelist' config
    -1447:      */
    -1448:     protected function _check_whitelist_auth()
    -1449:     {
    -1450:         $whitelist = explode(',', config_item('rest_ip_whitelist'));
    -1451: 
    -1452:         array_push($whitelist, '127.0.0.1', '0.0.0.0');
    -1453: 
    -1454:         foreach ($whitelist AS &$ip) {
    -1455:             $ip = trim($ip);
    -1456:         }
    -1457: 
    -1458:         if ( ! in_array($this->input->ip_address(), $whitelist)) {
    -1459:             $this->response(array(config_item('rest_status_field_name') => FALSE, config_item('rest_message_field_name') => 'IP not authorized'), 401);
    -1460:         }
    -1461:     }
    +1403:             return FALSE;
    +1404:         }
    +1405: 
    +1406:         log_message('debug', 'LDAP Auth: Loading Config');
    +1407: 
    +1408:         $this->config->load('ldap.php', TRUE);
    +1409: 
    +1410:         $ldap = [
    +1411:             'timeout' => $this->config->item('timeout', 'ldap'),
    +1412:             'host' => $this->config->item('server', 'ldap'),
    +1413:             'port' => $this->config->item('port', 'ldap'),
    +1414:             'rdn' => $this->config->item('binduser', 'ldap'),
    +1415:             'pass' => $this->config->item('bindpw', 'ldap'),
    +1416:             'basedn' => $this->config->item('basedn', 'ldap'),
    +1417:         ];
    +1418: 
    +1419:         log_message('debug', 'LDAP Auth: Connect to ' . (isset($ldaphost) ? $ldaphost : '[ldap not configured]'));
    +1420: 
    +1421:         // Appears to be unused
    +1422:         // $ldapconfig['authrealm'] = $this->config->item('domain', 'ldap');
    +1423: 
    +1424:         // connect to ldap server
    +1425:         $ldapconn = ldap_connect($ldap['host'], $ldap['port']);
    +1426: 
    +1427:         if ($ldapconn)
    +1428:         {
    +1429:             log_message('debug', 'Setting timeout to ' . $ldap['timeout'] . ' seconds');
    +1430: 
    +1431:             ldap_set_option($ldapconn, LDAP_OPT_NETWORK_TIMEOUT, $ldap['timeout']);
    +1432: 
    +1433:             log_message('debug', 'LDAP Auth: Binding to ' . $ldap['host'] . ' with dn ' . $ldap['rdn']);
    +1434: 
    +1435:             // binding to ldap server
    +1436:             $ldapbind = ldap_bind($ldapconn, $ldap['rdn'], $ldap['pass']);
    +1437: 
    +1438:             // verify binding
    +1439:             if ($ldapbind)
    +1440:             {
    +1441:                 log_message('debug', 'LDAP Auth: bind successful');
    +1442:             }
    +1443:             else
    +1444:             {
    +1445:                 log_message('error', 'LDAP Auth: bind unsuccessful');
    +1446: 
    +1447:                 return FALSE;
    +1448:             }
    +1449:         }
    +1450: 
    +1451:         // search for user
    +1452:         if (($res_id = ldap_search($ldapconn, $ldap['basedn'], "uid=$username")) == FALSE)
    +1453:         {
    +1454:             log_message('error', 'LDAP Auth: User ' . $username . ' not found in search');
    +1455: 
    +1456:             return FALSE;
    +1457:         }
    +1458: 
    +1459:         if (ldap_count_entries($ldapconn, $res_id) !== 1)
    +1460:         {
    +1461:             log_message('error', 'LDAP Auth: failure, username ' . $username . 'found more than once');
     1462: 
    -1463:     /**
    -1464:      * @todo Document this.
    -1465:      *
    -1466:      * @param string $nonce
    -1467:      */
    -1468:     protected function _force_login($nonce = '')
    -1469:     {
    -1470:         if (strtolower( $this->config->item('rest_auth') ) == 'basic') {
    -1471:             header('WWW-Authenticate: Basic realm="'.$this->config->item('rest_realm').'"');
    -1472:         } elseif (strtolower( $this->config->item('rest_auth') ) == 'digest') {
    -1473:             header('WWW-Authenticate: Digest realm="'.$this->config->item('rest_realm').'", qop="auth", nonce="'.$nonce.'", opaque="'.md5($this->config->item('rest_realm')).'"');
    -1474:         }
    -1475: 
    -1476:         $this->response(array(config_item('rest_status_field_name') => FALSE, config_item('rest_message_field_name') => 'Not authorized'), 401);
    -1477:     }
    -1478: 
    -1479:     /**
    -1480:      * Force it into an array
    -1481:      *
    -1482:      * @param  object|array $data
    -1483:      * @return array
    -1484:      */
    -1485:     protected function _force_loopable($data)
    -1486:     {
    -1487:         // Force it to be something useful
    -1488:         if ( ! is_array($data) and !is_object($data)) {
    -1489:             $data = (array) $data;
    -1490:         }
    -1491: 
    -1492:         return $data;
    -1493:     }
    -1494: 
    -1495:     /**
    -1496:      * updates the log with the access time
    -1497:      *
    -1498:      * @author Chris Kacerguis
    -1499:      * @return boolean
    -1500:      */
    -1501: 
    -1502:     protected function _log_access_time()
    -1503:     {
    -1504:         $payload['rtime'] = $this->_end_rtime - $this->_start_rtime;
    -1505: 
    -1506:         return $this->rest->db->update(config_item('rest_logs_table'), $payload, array('id' => $this->_insert_id));
    -1507:     }
    -1508: 
    -1509:     /**
    -1510:      * Check to see if the API key has access to the controller and methods
    -1511:      *
    -1512:      * @return boolean
    -1513:      */
    -1514:     protected function _check_access()
    -1515:     {
    -1516:         // if we don't want to check acccess, just return TRUE
    -1517:         if (config_item('rest_enable_access') === FALSE) {
    -1518:             return TRUE;
    -1519:         }
    -1520: 
    -1521:         // Fetch controller based on path and controller name
    -1522:         $controller = implode( '/', array($this->router->fetch_directory(), $this->router->fetch_class()) );
    -1523: 
    -1524:         // Remove any double slashes for safety
    -1525:         $controller = str_replace('//', '/', $controller);
    -1526: 
    -1527:         // Build access table query
    -1528:         $this->rest->db->select();
    -1529:         $this->rest->db->where('key', $this->rest->key);
    -1530:         $this->rest->db->where('controller', $controller);
    +1463:             return FALSE;
    +1464:         }
    +1465: 
    +1466:         if (($entry_id = ldap_first_entry($ldapconn, $res_id)) == FALSE)
    +1467:         {
    +1468:             log_message('error', 'LDAP Auth: failure, entry of searchresult could not be fetched');
    +1469: 
    +1470:             return FALSE;
    +1471:         }
    +1472: 
    +1473:         if (($user_dn = ldap_get_dn($ldapconn, $entry_id)) == FALSE)
    +1474:         {
    +1475:             log_message('error', 'LDAP Auth: failure, user-dn could not be fetched');
    +1476: 
    +1477:             return FALSE;
    +1478:         }
    +1479: 
    +1480:         // User found, could not authenticate as user
    +1481:         if (($link_id = ldap_bind($ldapconn, $user_dn, $password)) == FALSE)
    +1482:         {
    +1483:             log_message('error', 'LDAP Auth: failure, username/password did not match: ' . $user_dn);
    +1484: 
    +1485:             return FALSE;
    +1486:         }
    +1487: 
    +1488:         log_message('debug', 'LDAP Auth: Success ' . $user_dn . ' authenticated successfully');
    +1489: 
    +1490:         $this->_user_ldap_dn = $user_dn;
    +1491:         ldap_close($ldapconn);
    +1492: 
    +1493:         return TRUE;
    +1494:     }
    +1495: 
    +1496:     /**
    +1497:      * Perform Library Authentication - Override this function to change the way the library is called
    +1498:      *
    +1499:      * @access protected
    +1500:      *
    +1501:      * @param  string $username The username to validate
    +1502:      * @param  string $password The password to validate
    +1503:      *
    +1504:      * @return bool
    +1505:      */
    +1506:     protected function _perform_library_auth($username = '', $password = NULL)
    +1507:     {
    +1508:         if (empty($username))
    +1509:         {
    +1510:             log_message('error', 'Library Auth: failure, empty username');
    +1511: 
    +1512:             return FALSE;
    +1513:         }
    +1514: 
    +1515:         $auth_library_class = strtolower($this->config->item('auth_library_class'));
    +1516:         $auth_library_function = strtolower($this->config->item('auth_library_function'));
    +1517: 
    +1518:         if (empty($auth_library_class))
    +1519:         {
    +1520:             log_message('debug', 'Library Auth: failure, empty auth_library_class');
    +1521: 
    +1522:             return FALSE;
    +1523:         }
    +1524: 
    +1525:         if (empty($auth_library_function))
    +1526:         {
    +1527:             log_message('debug', 'Library Auth: failure, empty auth_library_function');
    +1528: 
    +1529:             return FALSE;
    +1530:         }
     1531: 
    -1532:         $query = $this->rest->db->get(config_item('rest_access_table'));
    -1533: 
    -1534:         if ($query->num_rows > 0) {
    -1535:             return TRUE;
    -1536:         }
    -1537: 
    -1538:         return FALSE;
    -1539:     }
    -1540: 
    -1541: }
    -1542: 
    +
    1532: if (is_callable([$auth_library_class, $auth_library_function]) === FALSE) +1533: { +1534: $this->load->library($auth_library_class); +1535: } +1536: +1537: return $this->{$auth_library_class}->$auth_library_function($username, $password); +1538: } +1539: +1540: /** +1541: * Check if the user is logged in +1542: * +1543: * @access protected +1544: * +1545: * @param string $username The user's name +1546: * @param bool|string $password The user's password +1547: * +1548: * @return bool +1549: */ +1550: protected function _check_login($username = NULL, $password = FALSE) +1551: { +1552: if (empty($username)) +1553: { +1554: return FALSE; +1555: } +1556: +1557: $auth_source = strtolower($this->config->item('auth_source')); +1558: $rest_auth = strtolower($this->config->item('rest_auth')); +1559: $valid_logins = $this->config->item('rest_valid_logins'); +1560: +1561: if (!$this->config->item('auth_source') && $rest_auth === 'digest') +1562: { +1563: // For digest we do not have a password passed as argument +1564: return md5($username . ':' . $this->config->item('rest_realm') . ':' . (isset($valid_logins[$username]) ? $valid_logins[$username] : '')); +1565: } +1566: +1567: if ($password === FALSE) +1568: { +1569: return FALSE; +1570: } +1571: +1572: if ($auth_source === 'ldap') +1573: { +1574: log_message('debug', "Performing LDAP authentication for $username"); +1575: +1576: return $this->_perform_ldap_auth($username, $password); +1577: } +1578: +1579: if ($auth_source === 'library') +1580: { +1581: log_message('debug', "Performing Library authentication for $username"); +1582: +1583: return $this->_perform_library_auth($username, $password); +1584: } +1585: +1586: if (array_key_exists($username, $valid_logins) === FALSE) +1587: { +1588: return FALSE; +1589: } +1590: +1591: if ($valid_logins[$username] !== $password) +1592: { +1593: return FALSE; +1594: } +1595: +1596: return TRUE; +1597: } +1598: +1599: /** +1600: * Check to see if the user is logged in with a PHP session key +1601: * +1602: * @access protected +1603: */ +1604: protected function _check_php_session() +1605: { +1606: // Get the auth_source config item +1607: $key = $this->config->item('auth_source'); +1608: +1609: // If falsy, then the user isn't logged in +1610: if (!$this->session->userdata($key)) +1611: { +1612: // Display an error response +1613: $this->response( +1614: [ +1615: $this->config->item('rest_status_field_name') => FALSE, +1616: $this->config->item('rest_message_field_name') => 'Not Authorized' +1617: ], 401); +1618: } +1619: } +1620: +1621: /** +1622: * Prepares for basic authentication +1623: * +1624: * @access protected +1625: */ +1626: protected function _prepare_basic_auth() +1627: { +1628: // If whitelist is enabled it has the first chance to kick them out +1629: if ($this->config->item('rest_ip_whitelist_enabled')) +1630: { +1631: $this->_check_whitelist_auth(); +1632: } +1633: +1634: // Returns NULL if the SERVER variables PHP_AUTH_USER and HTTP_AUTHENTICATION don't exist +1635: $username = $this->input->server('PHP_AUTH_USER'); +1636: $http_auth = $this->input->server('HTTP_AUTHENTICATION'); +1637: +1638: $password = NULL; +1639: if ($username !== NULL) +1640: { +1641: $password = $this->input->server('PHP_AUTH_PW'); +1642: } +1643: elseif ($http_auth !== NULL) +1644: { +1645: // If the authentication header is set as basic, then extract the username and password from +1646: // HTTP_AUTHORIZATION e.g. my_username:my_password. This is passed in the .htaccess file +1647: if (strpos(strtolower($http_auth), 'basic') === 0) +1648: { +1649: // Search online for HTTP_AUTHORIZATION workaround to explain what this is doing +1650: list($username, $password) = explode(':', base64_decode(substr($this->input->server('HTTP_AUTHORIZATION'), 6))); +1651: } +1652: } +1653: +1654: // Check if the user is logged into the system +1655: if ($this->_check_login($username, $password) === FALSE) +1656: { +1657: $this->_force_login(); +1658: } +1659: } +1660: +1661: /** +1662: * Prepares for digest authentication +1663: * +1664: * @access protected +1665: */ +1666: protected function _prepare_digest_auth() +1667: { +1668: // If whitelist is enabled it has the first chance to kick them out +1669: if ($this->config->item('rest_ip_whitelist_enabled')) +1670: { +1671: $this->_check_whitelist_auth(); +1672: } +1673: +1674: // We need to test which server authentication variable to use, +1675: // because the PHP ISAPI module in IIS acts different from CGI +1676: $digest_string = ''; +1677: if ($this->input->server('PHP_AUTH_DIGEST')) +1678: { +1679: $digest_string = $this->input->server('PHP_AUTH_DIGEST'); +1680: } +1681: elseif ($this->input->server('HTTP_AUTHORIZATION')) +1682: { +1683: $digest_string = $this->input->server('HTTP_AUTHORIZATION'); +1684: } +1685: +1686: $uniqueId = uniqid(); +1687: +1688: // The $_SESSION['error_prompted'] variable is used to ask the password +1689: // again if none given or if the user enters wrong auth information +1690: if (empty($digest_string)) +1691: { +1692: $this->_force_login($uniqueId); +1693: } +1694: +1695: // We need to retrieve authentication data from the $digest_string variable +1696: $matches = []; +1697: preg_match_all('@(username|nonce|uri|nc|cnonce|qop|response)=[\'"]?([^\'",]+)@', $digest_string, $matches); +1698: $digest = (empty($matches[1]) || empty($matches[2])) ? [] : array_combine($matches[1], $matches[2]); +1699: +1700: // For digest authentication the library function should return already stored md5(username:restrealm:password) for that username @see rest.php::auth_library_function config +1701: $A1 = $this->_check_login($digest['username'], TRUE); +1702: if (array_key_exists('username', $digest) === FALSE || $A1 === FALSE) +1703: { +1704: $this->_force_login($uniqueId); +1705: } +1706: +1707: $A2 = md5(strtoupper($this->request->method) . ':' . $digest['uri']); +1708: $valid_response = md5($A1 . ':' . $digest['nonce'] . ':' . $digest['nc'] . ':' . $digest['cnonce'] . ':' . $digest['qop'] . ':' . $A2); +1709: +1710: // Check if the string don't compare (case-insensitive) +1711: if (strcasecmp($digest['response'], $valid_response) !== 0) +1712: { +1713: // Display an error response +1714: $this->response( +1715: [ +1716: $this->config->item('rest_status_field_name') => 0, +1717: $this->config->item('rest_message_field_name') => 'Invalid credentials' +1718: ], 401); +1719: } +1720: } +1721: +1722: /** +1723: * Checks if the client's ip is in the 'rest_ip_blacklist' config and generates a 401 response +1724: * +1725: * @access protected +1726: */ +1727: protected function _check_blacklist_auth() +1728: { +1729: // Match an ip address in a blacklist e.g. 127.0.0.0, 0.0.0.0 +1730: $pattern = sprintf('/(?:,\s*|^)\Q%s\E(?=,\s*|$)/m', $this->input->ip_address()); +1731: +1732: // Returns 1, 0 or FALSE (on error only). Therefore implicitly convert 1 to TRUE +1733: if (preg_match($pattern, $this->config->item('rest_ip_blacklist'))) +1734: { +1735: // Display an error response +1736: $this->response( +1737: [ +1738: 'status' => FALSE, +1739: 'error' => 'IP Denied' +1740: ], +1741: 401); +1742: } +1743: } +1744: +1745: /** +1746: * Check if the client's ip is in the 'rest_ip_whitelist' config and generates a 401 response +1747: * +1748: * @access protected +1749: */ +1750: protected function _check_whitelist_auth() +1751: { +1752: $whitelist = explode(',', $this->config->item('rest_ip_whitelist')); +1753: +1754: array_push($whitelist, '127.0.0.1', '0.0.0.0'); +1755: +1756: foreach ($whitelist AS &$ip) +1757: { +1758: $ip = trim($ip); +1759: } +1760: +1761: if (in_array($this->input->ip_address(), $whitelist) === FALSE) +1762: { +1763: $this->response([$this->config->item('rest_status_field_name') => FALSE, $this->config->item('rest_message_field_name') => 'IP not authorized'], 401); +1764: } +1765: } +1766: +1767: /** +1768: * Force logging in by setting the WWW-Authenticate header +1769: * +1770: * @access protected +1771: * +1772: * @param string $nonce A server-specified data string which should be uniquely generated +1773: * each time +1774: */ +1775: protected function _force_login($nonce = '') +1776: { +1777: $restAuth = $this->config->item('rest_auth'); +1778: $restRealm = $this->config->item('rest_realm'); +1779: if (strtolower($restAuth) === 'basic') +1780: { +1781: // See http://tools.ietf.org/html/rfc2617#page-5 +1782: header('WWW-Authenticate: Basic realm="' . $restRealm . '"'); +1783: } +1784: elseif (strtolower($restAuth) === 'digest') +1785: { +1786: // See http://tools.ietf.org/html/rfc2617#page-18 +1787: header( +1788: 'WWW-Authenticate: Digest realm="' . $restRealm +1789: . '", qop="auth", nonce="' . $nonce +1790: . '", opaque="' . md5($restRealm) . '"'); +1791: } +1792: +1793: // Display an error response +1794: $this->response( +1795: [ +1796: $this->config->item('rest_status_field_name') => FALSE, +1797: $this->config->item('rest_message_field_name') => 'Not authorized' +1798: ], 401); +1799: } +1800: +1801: /** +1802: * Updates the log table with the total access time +1803: * +1804: * @access protected +1805: * @author Chris Kacerguis +1806: * +1807: * @return bool TRUE log table updated; otherwise, FALSE +1808: */ +1809: protected function _log_access_time() +1810: { +1811: $payload['rtime'] = $this->_end_rtime - $this->_start_rtime; +1812: +1813: return $this->rest->db +1814: ->update( +1815: $this->config->item('rest_logs_table'), $payload, [ +1816: 'id' => $this->_insert_id +1817: ]); +1818: } +1819: +1820: /** +1821: * Updates the log table with HTTP response code +1822: * +1823: * @access protected +1824: * @author Justin Chen +1825: * +1826: * @param $http_code int HTTP status code +1827: * +1828: * @return bool TRUE log table updated; otherwise, FALSE +1829: */ +1830: protected function _log_response_code($http_code) +1831: { +1832: $payload['response_code'] = $http_code; +1833: +1834: return $this->rest->db->update( +1835: $this->config->item('rest_logs_table'), $payload, [ +1836: 'id' => $this->_insert_id +1837: ]); +1838: } +1839: +1840: /** +1841: * Check to see if the API key has access to the controller and methods +1842: * +1843: * @access protected +1844: * @return bool TRUE the API key has access; otherwise, FALSE +1845: */ +1846: protected function _check_access() +1847: { +1848: // If we don't want to check access, just return TRUE +1849: if ($this->config->item('rest_enable_access') === FALSE) +1850: { +1851: return TRUE; +1852: } +1853: +1854: // Fetch controller based on path and controller name +1855: $controller = implode( +1856: '/', [ +1857: $this->router->directory, +1858: $this->router->class +1859: ]); +1860: +1861: // Remove any double slashes for safety +1862: $controller = str_replace('//', '/', $controller); +1863: +1864: // Query the access table and get the number of results +1865: return $this->rest->db +1866: ->where('key', $this->rest->key) +1867: ->where('controller', $controller) +1868: ->get($this->config->item('rest_access_table')) +1869: ->num_rows() > 0; +1870: } +1871: +1872: } +1873:
    - + diff --git a/documentation/source-function-_perform_ldap_auth.html b/documentation/source-function-_perform_ldap_auth.html deleted file mode 100644 index 3f21a03d..00000000 --- a/documentation/source-function-_perform_ldap_auth.html +++ /dev/null @@ -1,205 +0,0 @@ - - - - - - - File dummy.php - - - - - - -
    - -
    - -
    - - - - - - diff --git a/documentation/todo.html b/documentation/todo.html deleted file mode 100644 index 37babc69..00000000 --- a/documentation/todo.html +++ /dev/null @@ -1,153 +0,0 @@ - - - - - - Todo - - - - - - -
    - -
    - -
    - - - - - - From ce5f56019a424eb38ff9e6f44d65b4457a668288 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Thu, 16 Jul 2015 08:59:55 +0300 Subject: [PATCH 134/444] Updated documentation with new changes --- documentation/404.html | 10 +- documentation/class-Example.html | 27 +- documentation/class-Format.html | 253 +- documentation/class-Key.html | 27 +- documentation/class-REST_Controller.html | 609 ++- documentation/index.html | 10 +- .../package-CodeIgniter.Libraries.html | 7 +- documentation/package-CodeIgniter.Rest.html | 5 +- documentation/package-CodeIgniter.html | 5 +- documentation/package-None.html | 5 +- documentation/source-class-Example.html | 29 +- documentation/source-class-Format.html | 933 ++--- documentation/source-class-Key.html | 124 +- .../source-class-REST_Controller.html | 3457 +++++++++-------- 14 files changed, 3217 insertions(+), 2284 deletions(-) diff --git a/documentation/404.html b/documentation/404.html index d40111fb..8f9bffdb 100644 --- a/documentation/404.html +++ b/documentation/404.html @@ -50,7 +50,10 @@

    Packages

    Classes

    @@ -79,8 +82,7 @@

    Classes

  • Package
  • - Class -
  • +Class
    @@ -100,7 +102,7 @@

    Page not found

    - + diff --git a/documentation/class-Example.html b/documentation/class-Example.html index 76e48ed4..21f1d336 100644 --- a/documentation/class-Example.html +++ b/documentation/class-Example.html @@ -138,7 +138,7 @@

    Class Example

    Phil Sturgeon, Chris Kacerguis
    Link: https://github.com/chriskacerguis/codeigniter-restserver
    - Located at application/controllers/api/Example.php + Located at controllers/api/Example.php
    @@ -379,6 +379,7 @@

    Overrides

    _check_php_session(), _check_whitelist_auth(), _detect_api_key(), + _detect_input_format(), _detect_lang(), _detect_method(), _detect_output_format(), @@ -393,6 +394,7 @@

    Overrides

    _parse_patch(), _parse_post(), _parse_put(), + _parse_query(), _perform_ldap_auth(), _perform_library_auth(), _prepare_basic_auth(), @@ -407,6 +409,7 @@

    Overrides

    patch(), post(), put(), + query(), response(), validation_errors() @@ -420,6 +423,26 @@

    Overrides

    + + + + + +
    Constants inherited from REST_Controller
    + BAD_REQUEST, + CONFLICT, + CREATED, + FORBIDDEN, + INTERNAL_SERVER_ERROR, + METHOD_NOT_ALLOWED, + NOT_ACCEPTABLE, + NOT_FOUND, + NOT_IMPLEMENTED, + NOT_MODIFIED, + NO_CONTENT, + OK, + UNAUTHORIZED +
    @@ -441,11 +464,13 @@

    Overrides

    $_patch_args, $_post_args, $_put_args, + $_query_args, $_start_rtime, $_supported_formats, $_user_ldap_dn, $_zlib_oc, $allowed_http_methods, + $http_status_codes, $methods, $request, $response, diff --git a/documentation/class-Format.html b/documentation/class-Format.html index 7e4e5323..274eb215 100644 --- a/documentation/class-Format.html +++ b/documentation/class-Format.html @@ -113,7 +113,7 @@

    Class Format

    http://www.dbad-license.org/
    Author: Phil Sturgeon, Chris Kacerguis
    - Located at application/libraries/Format.php + Located at libraries/Format.php
    @@ -133,7 +133,7 @@

    Class Format

    # - __construct( null $data = NULL, null $from_type = NULL ) + __construct( null $data = NULL, null $from_type = NULL )

    DO NOT CALL THIS DIRECTLY, USE factory()

    @@ -174,7 +174,7 @@

    Throws

    # - factory( mixed $data, string $from_type = NULL ) + factory( mixed $data, string $from_type = NULL )

    Create an instance of the format class @@ -217,7 +217,7 @@

    Returns

    # - to_array( mixed|null $data = NULL ) + to_array( mixed|null $data = NULL )

    Format data as an array

    @@ -257,7 +257,7 @@

    Returns

    # - to_xml( mixed|null $data = NULL, null $structure = NULL, string $basenode = 'xml' ) + to_xml( mixed|null $data = NULL, null $structure = NULL, string $basenode = 'xml' )

    Format data as XML

    @@ -301,7 +301,7 @@

    Returns

    # - to_html( mixed|null $data = NULL ) + to_html( mixed|null $data = NULL )

    Format data as HTML

    @@ -334,21 +334,21 @@

    Returns

    public - mixed + string
    # - to_csv( mixed|null $data = NULL ) + to_csv( mixed|null $data = NULL, string $delimiter = ',', string $enclosure = '"' )
    -

    Format data as CSV

    +

    Returns

    - mixed + string
    A csv string
    +

    Link

    +
    @@ -381,7 +391,7 @@

    Returns

    # - to_json( mixed|null $data = NULL ) + to_json( mixed|null $data = NULL )

    Encode data as json

    @@ -421,7 +431,7 @@

    Returns

    # - to_serialized( mixed|null $data = NULL ) + to_serialized( mixed|null $data = NULL )

    Encode data as a serialized array

    @@ -461,7 +471,7 @@

    Returns

    # - to_php( mixed|null $data = NULL ) + to_php( mixed|null $data = NULL )

    Format data using a PHP structure

    @@ -501,7 +511,7 @@

    Returns

    # - _from_xml( $data ) + _from_xml( $data )
    @@ -540,7 +550,7 @@

    Returns

    # - _from_csv( string $data ) + _from_csv( string $data, string $delimiter = ',', string $enclosure = '"' )
    @@ -554,6 +564,12 @@

    Parameters

    $data
    CSV string
    +
    $delimiter
    +

    The optional delimiter parameter sets the field +delimiter (one character only). NULL will use the default value (,)

    +
    $enclosure
    +

    The optional enclosure parameter sets the field +enclosure (one character only). NULL will use the default value (")

    Returns

    @@ -580,7 +596,7 @@

    Returns

    # - _from_json( $data ) + _from_json( $data )
    @@ -619,7 +635,7 @@

    Returns

    # - _from_serialize( string $data ) + _from_serialize( string $data )
    @@ -658,7 +674,7 @@

    Returns

    # - _from_php( $data ) + _from_php( $data )
    @@ -694,6 +710,201 @@

    Returns

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Constants summary
    string + + ARRAY_FORMAT + + +
    +

    Array output format

    +
    + + +
    +
    + # + 'array' +
    +
    string + + CSV_FORMAT + + +
    +

    Comma Separated Value (CSV) output format

    +
    + + +
    +
    + # + 'csv' +
    +
    string + + JSON_FORMAT + + +
    +

    Json output format

    +
    + + +
    +
    + # + 'json' +
    +
    string + + HTML_FORMAT + + +
    +

    HTML output format

    +
    + + +
    +
    + # + 'html' +
    +
    string + + PHP_FORMAT + + +
    +

    PHP output format

    +
    + + +
    +
    + # + 'php' +
    +
    string + + SERIALIZED_FORMAT + + +
    +

    Serialized output format

    +
    + + +
    +
    + # + 'serialized' +
    +
    string + + XML_FORMAT + + +
    +

    XML output format

    +
    + + +
    +
    + # + 'xml' +
    +
    string + + DEFAULT_FORMAT + + +
    +

    Default format of this class

    +
    + + +
    + +
    @@ -707,7 +918,7 @@

    Returns

    - $_data + $_data

    Data to parse

    @@ -732,7 +943,7 @@

    Returns

    - $_from_type + $_from_type

    Type to convert from

    diff --git a/documentation/class-Key.html b/documentation/class-Key.html index b286a2d5..6de485a6 100644 --- a/documentation/class-Key.html +++ b/documentation/class-Key.html @@ -138,7 +138,7 @@

    Class Key

    Phil Sturgeon, Chris Kacerguis
    Link: https://github.com/chriskacerguis/codeigniter-restserver
    - Located at application/controllers/api/Key.php + Located at controllers/api/Key.php
    @@ -322,6 +322,7 @@

    Class Key

    _check_php_session(), _check_whitelist_auth(), _detect_api_key(), + _detect_input_format(), _detect_lang(), _detect_method(), _detect_output_format(), @@ -336,6 +337,7 @@

    Class Key

    _parse_patch(), _parse_post(), _parse_put(), + _parse_query(), _perform_ldap_auth(), _perform_library_auth(), _prepare_basic_auth(), @@ -350,6 +352,7 @@

    Class Key

    patch(), post(), put(), + query(), response(), validation_errors() @@ -363,6 +366,26 @@

    Class Key

    + + + + + +
    Constants inherited from REST_Controller
    + BAD_REQUEST, + CONFLICT, + CREATED, + FORBIDDEN, + INTERNAL_SERVER_ERROR, + METHOD_NOT_ALLOWED, + NOT_ACCEPTABLE, + NOT_FOUND, + NOT_IMPLEMENTED, + NOT_MODIFIED, + NO_CONTENT, + OK, + UNAUTHORIZED +
    @@ -417,11 +440,13 @@

    Class Key

    $_patch_args, $_post_args, $_put_args, + $_query_args, $_start_rtime, $_supported_formats, $_user_ldap_dn, $_zlib_oc, $allowed_http_methods, + $http_status_codes, $request, $response, $rest, diff --git a/documentation/class-REST_Controller.html b/documentation/class-REST_Controller.html index a12d2c52..84a6ae64 100644 --- a/documentation/class-REST_Controller.html +++ b/documentation/class-REST_Controller.html @@ -137,7 +137,7 @@

    Direct known subclasses

    3.0.0
    Link: https://github.com/chriskacerguis/codeigniter-restserver
    - Located at application/libraries/REST_Controller.php + Located at libraries/REST_Controller.php
    @@ -157,7 +157,7 @@

    Direct known subclasses

    # - early_checks( ) + early_checks( )

    Extend this function to apply additional checking early on in the process

    @@ -187,7 +187,7 @@

    Direct known subclasses

    # - __construct( string $config = 'rest' ) + __construct( string $config = 'rest' )

    Constructor for the REST API

    @@ -223,7 +223,7 @@

    Parameters

    # - __destruct( ) + __destruct( )

    Deconstructor

    @@ -257,7 +257,7 @@

    Author

    # - _remap( string $object_called, array $arguments ) + _remap( string $object_called, array $arguments )

    Requests are not made to methods directly, the request will be for @@ -298,7 +298,7 @@

    Parameters

    # - response( array|null $data = NULL, integer|null $http_code = NULL, boolean $continue = FALSE ) + response( array|null $data = NULL, integer|null $http_code = NULL, boolean $continue = FALSE )

    Takes mixed data and optionally a status code, then creates the response

    @@ -323,6 +323,40 @@

    Parameters

    +
    +
    + + + + + protected + + string|null + + + + +
    + # + _detect_input_format( ) + +
    +

    Get the input format e.g. json or xml

    +
    + +
    @@ -338,7 +372,7 @@

    Parameters

    # - _detect_output_format( ) + _detect_output_format( )

    Detect which format should be used to output the data

    @@ -372,7 +406,7 @@

    Returns

    # - _detect_method( ) + _detect_method( )

    Get the HTTP request string e.g. get or post

    @@ -406,7 +440,7 @@

    Returns

    # - _detect_api_key( ) + _detect_api_key( )

    See if the user has provided an API key

    @@ -440,7 +474,7 @@

    Returns

    # - _detect_lang( ) + _detect_lang( )

    What language do they want it in?

    @@ -474,7 +508,7 @@

    Returns

    # - _log_request( boolean $authorized = FALSE ) + _log_request( boolean $authorized = FALSE )

    Add the request to the log table

    @@ -513,7 +547,7 @@

    Returns

    # - _check_limit( string $controller_method ) + _check_limit( string $controller_method )

    Check if the requests to a controller method exceed a limit

    @@ -552,18 +586,14 @@

    Returns

    # - _auth_override_check( ) + _auth_override_check( )
    -

    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