From 1406528428ff354f1fc48ca617eb163dda519166 Mon Sep 17 00:00:00 2001 From: softwarespot Date: Thu, 2 Jul 2015 21:24:22 +0300 Subject: [PATCH 001/354] 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 002/354] 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 003/354] 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 004/354] 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 005/354] 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 006/354] 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 007/354] 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 008/354] 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 009/354] 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 010/354] 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 011/354] 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 012/354] 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 013/354] 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 014/354] 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 015/354] 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 016/354] 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 017/354] 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 018/354] 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 019/354] 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 020/354] 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 021/354] 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 022/354] 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 023/354] 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 024/354] 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 025/354] 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 026/354] 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 027/354] 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 028/354] 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 029/354] 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 030/354] 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 031/354] 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 032/354] 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 033/354] 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 034/354] 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 035/354] 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 036/354] 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 037/354] 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 038/354] 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 039/354] 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 040/354] 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 041/354] 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 042/354] 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 043/354] 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 044/354] 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