"OK", 201 => "Created", 202 => "Accepted", 204 => "No Content", // Client errors 400 => "Bad Request", 401 => "Unauthorized", 403 => "Forbidden", 404 => "Not Found", 405 => "Method Not Allowed", 415 => "Unsupported Media Type", // Server errors 500 => "Internal Server Error", 501 => "Not Implemented", 503 => "Service Unavailable", 505 => "HTTP Version Not Supported", 511 => "Network Authentication Required", ); /** * Set result format * * (default value: array("json", "array", "object", "xml")) * * @var mixed * @access private */ private $result_format_available = array( "json", "array", "object", "xml", ); /** * Result format * * value in $result_format_available * * (default value: "json") * * @var string * @access private */ private $result_format = "json"; /** * To store result * * @var mixed * @access private */ private $result = array( "success" => true, "code" => 204, "message" => "" ); /** * Reponse headers * * @var mixed */ public $response_headers = array (); /** * class constructor... * * @access public * @param bool|string $api_url (default: false) * @param bool|string $app_id (default: false) * @param bool|string $api_key (default: false) * @param bool|string $username (default: false) * @param bool|string $password (default: false) * @param string $result_format (default: "json") */ public function __construct($api_url = false, $app_id = false, $api_key = false, $username = false, $password = false, $result_format = "json") { // set app server URL if provided if ($api_url!==false) { $this->set_api_url ($api_url); } // set app_id if provided if ($app_id!==false) { $this->set_api_app_id ($app_id); } // set api key if provided if ($api_key!==false) { $this->set_api_key ($api_key); } // set user/pass if provided if ($username!==false && $password!==false) { $this->set_api_authparams ($username, $password); } // set result format if provided if (strlen($result_format)>0) { $this->set_result_format ($result_format); } // check for required php extensions $this->validate_php_extensions (); } /** * Saves error and exits script * * @access public * @param mixed $content * @return void */ public function exception ($content) { //set result parameters $this->result = array( 'code' => 400, 'success' => false, 'message' => $content ); // print result $this->print_result (); // exit #die(); return; } /** * Returns last result * * @access public * @return void */ public function get_result () { # output result if ($this->result_format=="json") { return json_encode($this->result); } elseif ($this->result_format=="array") { return $this->result; } elseif ($this->result_format=="object") { return (object) $this->result; } elseif ($this->result_format=="xml") { // new SimpleXMLElement object $xml = new SimpleXMLElement('<'.$_GET['controller'].'/>'); // generate xml from result $this->array_to_xml($xml, $this->result); // return XML result return $xml->asXML(); } } /** * Prints last result * * @access public * @return void */ public function print_result () { # output result if ($this->result_format=="json") { print json_encode($this->result); } elseif ($this->result_format=="array") { var_dump($this->result); } elseif ($this->result_format=="object") { var_dump( (object) $this->result); } elseif ($this->result_format=="xml") { // new SimpleXMLElement object $xml = new SimpleXMLElement(''); // generate xml from result $this->array_to_xml($xml, $this->result); // return XML result print $xml->asXML(); } } /** * Transforms array to XML * * @access private * @param SimpleXMLElement $object * @param array $data * @return void */ private function array_to_xml(SimpleXMLElement $object, array $data) { // loop through values foreach ($data as $key => $value) { // if spaces exist in key replace them with underscores if(strpos($key, " ")>0) { $key = str_replace(" ", "_", $key); } // if key is numeric append item if(is_numeric($key)) { $key = "item".$key; } // if array add child if (is_array($value)) { $new_object = $object->addChild($key); $this->array_to_xml($new_object, $value); } // else write value else { $object->addChild($key, $value); } } } /** * Check if all extensions are present * * @access private * @return void */ private function validate_php_extensions () { // Required extensions $required_ext = array("openssl", "curl"); // mcrypt for crypted extensions if($this->api_key !== false) $required_ext[] = "mcrypt"; // json if($this->result_format == "json") $required_ext[] = "json"; // xml if($this->result_format == "xml") $required_ext[] = "xmlreader"; // Available extensions $available_ext = get_loaded_extensions(); // check foreach ($required_ext as $e) { if(!in_array($e, $available_ext)) { $this->exception("Missing php extension ($e)"); } } } /** * Debugging flag * * @access public * @param bool $debug (default: false) * @return void */ public function set_debug ($debug = false) { if(is_bool($debug)) { $this->debug = $debug; } } /** * Checks requested result format and saves it * * @access public * @param string $result_format (default: "json") * @return void */ public function set_result_format ($result_format = "json") { if (strlen($result_format)>0) { if (!in_array($result_format, $this->result_format_available)) { $this->exception ("Invalid result format"); } else { // recheck extensions $this->validate_php_extensions (); // set $this->result_format = $result_format; } } } /** * Set API url parameter * * @access public * @param mixed $api_url * @return void */ public function set_api_url ($api_url) { // we need http/https if(strpos($api_url, "http://")!==false || strpos($api_url, "https://")!==false) { // trim $api_url = trim($api_url); // add last / if missing if (substr($api_url, -1)!=="/") { $api_url .= "/"; } // save $this->api_url = $api_url; } else { $this->exception("Invalid API URL"); } } /** * Sets api app_id variable * * @access public * @param bool $id (default: false) * @return void */ public function set_api_app_id ($app_id = false) { if ($app_id!==false) { // name must be more than 2 and alphanumberic if(strlen($app_id)<3 || strlen($app_id)>12 || !ctype_alnum($app_id)) { $this->exception("Invalid APP id"); } else { $this->api_app_id = $app_id; } } else { $this->exception("Invalid APP id"); } } /** * Set api key * * @access public * @param bool $api_key (default: false) * @return void */ public function set_api_key ($api_key = false) { #if ($api_key!==false) { if ($api_key!=false) { $this->api_key = $api_key; // set encrypt flag $this->api_encrypt = true; } else { $this->exception("Invalid APP id"); } } /** * Sets username/password for URL auth * * @access public * @param bool $username (default: false) * @param bool $password (default: false) * @return void */ public function set_api_authparams ($username = false, $password = false) { if($username===false || $password===false) { $this->exception("Invalid username or password"); } else { $this->api_username = $username; $this->api_password = $password; } } /** * Sreets api method. * * @access public * @param string $method (default: "GET") * @return void */ public function set_api_method ($method = "GET") { // validate $this->set_api_method_validate ($method); // set $this->api_server_method = strtoupper($method); } /** * Validates API method against available * * @access private * @param mixed $method * @return void */ private function set_api_method_validate ($method) { if(!in_array(strtoupper($method), $this->api_server_valid_methods)) { $this->exception("Invalid method $method"); } } /** * Sets API controller - required * * @access public * @param bool|string $controller (default: false) * @return void */ public function set_api_controller ($controller = false) { #if($controller!==false) { if($controller!=false) { $this->api_server_controller = strtolower($controller); } } /** * Sets additional identifiers to be passed to URL directly * * e.g.: /api/appid/controller/// * * @access public * @param mixed $identifiers * @return void */ public function set_api_identifiers ($identifiers) { $this->api_server_identifiers = false; // clear this to forget any previous settings if(is_array($identifiers)) { if(sizeof($identifiers)>0 && !$this->api_encrypt) { // reset $this->api_server_identifiers = implode("/", $identifiers); } elseif (sizeof($identifiers)>0 && $this->api_encrypt) { $this->api_server_identifiers = array(); foreach ($identifiers as $cnt=>$i) { if($cnt==0) { $this->api_server_identifiers['id'] = $i; } else { $this->api_server_identifiers['id'.($cnt+1)] = $i; } } } } } /* @api-server communication --------------- */ /** * Executes request to API server * * @access public * @param bool|string $method (default: false) * @param bool|string $controller (default: false) * @param mixed $identifiers (default: array()) * @param mixed $params (default: array()) * @param bool|string $token_file (default: false) * @return void */ public function execute ($method = false, $controller = false, $identifiers = array(), $params = array(), $token_file = false) { // check and set method $this->set_api_method ($method); // set api controller $this->set_api_controller ($controller); // set api identifiers $this->set_api_identifiers ($identifiers); // set connection $this->curl_set_connection ($token_file); // save params $this->curl_set_params ($params); // set HTTP method $this->curl_set_http_method (); // if not encrypted set params if(!$this->api_encrypt) { // add token to header, authenticate if it fails $this->curl_add_token_header ($token_file); } // if token is set execute if ($this->token !== false) { // execute $res = $this->curl_execute (); // save result $this->result = (array) $res; // check for invalid token and retry if ($this->result['code']=="401" && $token_file!==false) { // remove old token $this->delete_token_file ($token_file); // auth again $this->curl_add_token_header ($token_file); // execute $res = $this->curl_execute (); // save result $this->result = (array) $res; } } // exncrypted request elseif ($this->api_encrypt) { // execute $res = $this->curl_execute (); // save result $this->result = (array) $res; } // save reult $this->curl_save_headers (); } /** * Opens cURL resource and sets initial parameters * * @access private * @param mixed $token_file * @return void */ private function curl_set_connection ($token_file) { // check if it exists #if ($this->Connection===false) { if ($this->Connection==false) { // Get cURL resource $this->Connection = curl_init(); // set URL if($this->api_server_controller===false) { $url = $this->api_url.$this->api_app_id."/"; } else { $url = $this->api_url.$this->api_app_id.str_replace("//", "/", "/".$this->api_server_controller."/".$this->api_server_identifiers."/"); } // set default curl options and params curl_setopt_array($this->Connection, array( CURLOPT_RETURNTRANSFER => true, CURLOPT_URL => $url, CURLOPT_HEADER => false, CURLOPT_VERBOSE => $this->debug, CURLOPT_TIMEOUT => 9, CURLOPT_HTTPHEADER => array( 'Content-Type: application/json', # https://stackoverflow.com/a/33550871 'Connection: Keep-Alive', ), CURLOPT_USERAGENT => 'phpipam-api php class', # https://stackoverflow.com/a/43277274 CURLOPT_ENCODING => 'gzip,deflate', #CURLOPT_ENCODING => 'deflate', // ssl CURLOPT_SSL_VERIFYHOST => false, CURLOPT_SSL_VERIFYPEER => false, // save headers CURLINFO_HEADER_OUT => true, ) ); } } /** * Adds params to request if required * * @access private * @param mixed $params * @return void */ private function curl_set_params ($params) { // params set ? if (is_array($params) && !$this->api_encrypt ) { if (sizeof($params)>0) { # 9163a784aff4e9689dd0a5330a826f70247a2ce2 if ($this->api_server_method == 'GET') curl_setopt($this->Connection, CURLOPT_URL, $this->api_url.$this->api_app_id.str_replace("//", "/", "/".$this->api_server_controller."/".$this->api_server_identifiers."/?".http_build_query($params))); else curl_setopt($this->Connection, CURLOPT_POSTFIELDS, json_encode($params)); } } // encrypt elseif ($this->api_encrypt) { // empty if(!is_array($params)) $params = array(); if(!is_array($this->api_server_identifiers)) $this->api_server_identifiers = array(); // join identifiers and parameters $params = array_merge($this->api_server_identifiers, $params); $params['controller'] = $this->api_server_controller; // create encrypted request $encrypted_request = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $this->api_key, json_encode($params), MCRYPT_MODE_ECB)); // escape + $encrypted_request = urlencode($encrypted_request); // reset url curl_setopt($this->Connection, CURLOPT_URL, $this->api_url."?app_id=".$this->api_app_id."&enc_request=".$encrypted_request); } } /** * Sets HTTP method to use for queries * * @access private * @return void */ private function curl_set_http_method () { curl_setopt($this->Connection, CURLOPT_CUSTOMREQUEST, $this->api_server_method); } /** * Adds token to http header * * @access private * @param mixed $token_file * @return void */ private function curl_add_token_header ($token_file) { if($token_file!==false) { // open file and save token $token = @file($token_file); // save token if(isset($token[0])) { $this->token = trim($token[0]); $this->token_expires = trim($token[1]); // is token still valid ? if (strlen($this->token)<2 && $this->token_expires < time()) { // initiate authentication $this->curl_authenticate (); //save token to file $this->write_token_file ($token_file); } } else { $this->curl_authenticate (); //save token to file $this->write_token_file ($token_file); } } // token not saved, try to retrieve it else { $this->curl_authenticate (); } // add token to headers $this->curl_add_http_header ("token", $this->token); } /** * Adds http headers * * @access private * @param mixed $name * @param mixed $value * @return void */ private function curl_add_http_header ($name, $value) { $headers = array( "Content-Type: application/json", # https://stackoverflow.com/a/33550871 #'Connection: Keep-Alive', "$name: $value" ); // save curl_setopt($this->Connection, CURLOPT_HTTPHEADER, $headers); } /** * Writes token to token file * * @access private * @param mixed $filename * @return void */ private function write_token_file ($filename) { //save token try { $myfile = fopen($filename, "w"); fwrite($myfile, $this->token); fwrite($myfile, "\n"); fwrite($myfile, $this->token_expires); // close file fclose($myfile); } catch ( Exception $e ) { $this->exception("Cannot write file $filename"); } } /** * Removes token file if expired / invalid * * @access private * @param mixed $token_file * @return void */ private function delete_token_file ($token_file) { //save token try { $myfile = fopen($token_file, "w"); fwrite($myfile, ""); // close file fclose($myfile); } catch ( Exception $e ) { $this->exception("Cannot write file $token_file"); } } /** * Executes request. * * @access private * @return void */ private function curl_execute () { // send request and save response $resp = curl_exec($this->Connection); // curl error check if (curl_errno($this->Connection)) { $this->exception("Curl error: ".curl_error($this->Connection)); } else { // return result object return json_decode($resp); } } /** * Store result code * * @method curl_save_result_code * @return void */ private function curl_save_headers () { // save result and result code $this->response_headers = curl_getinfo($this->Connection); } /** * send authenticate request and save token if provided, otherwise throw error. * * @access private * @return void */ private function curl_authenticate () { // Get cURL resource $c_auth = curl_init(); // set default curl options and params curl_setopt_array($c_auth, array( CURLOPT_RETURNTRANSFER => true, CURLOPT_URL => $this->api_url.$this->api_app_id."/user/", #CURLOPT_HEADER => true, CURLOPT_HEADER => false, # useless due to libcurl internal design # https://bugs.php.net/bug.php?id=65348 #CURLOPT_VERBOSE => $this->debug, CURLOPT_TIMEOUT => 9, CURLOPT_USERAGENT => 'phpipam-api php class', # https://stackoverflow.com/a/43277274 CURLOPT_ENCODING => 'gzip,deflate', #CURLOPT_ENCODING => 'deflate', // ssl CURLOPT_SSL_VERIFYHOST => false, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_POST => true, # https://github.com/phpipam/phpipam-api-clients/issues/17 CURLOPT_POSTFIELDS => '', # https:// #CURLOPT_FOLLOWLOCATION => true, # http://epic.grnet.gr/guides/api-auth/ CURLOPT_USERPWD => $this->api_username.":".$this->api_password, CURLOPT_HTTPAUTH => CURLAUTH_BASIC, # https://stackoverflow.com/a/33550871 # https://github.com/phpipam/phpipam/issues/1191#issuecomment-309450838 CURLOPT_HTTPHEADER => array( #'Content-Length: 0', 'Connection: Keep-Alive', #'Content-Type: application/x-www-form-urlencoded;charset=UTF-8', # charset => 415 : Invalid content type 'Content-Type: application/x-www-form-urlencoded', ), ) ); // send request and save response $resp = curl_exec($c_auth); // curl error check #if (curl_errno($c_auth)) { if ($resp===false) { $this->exception("Curl error: ".curl_error($c_auth)); } else { // return result object $auth_resp = json_decode($resp); // ok ? if ($auth_resp->code == 200) { if (isset($auth_resp->data->token)) { // save token $this->token = $auth_resp->data->token; $this->token_expires = strtotime($auth_resp->data->expires); } else { $this->exception("Cannot obtain access token"); } } // error else { // save response $this->result = $auth_resp; } } #curl_close($c_auth); } } /** modelines * vi: tabstop=4 shifwidth=4 autoindent beautify * vim: ff=unix ts=4 sts=4 sw=4 ai et sta fenc=utf-8 bf ft=php * atom:set useSoftTabs tabLength=4 lineending=lf encoding=utf-8 * -*- Mode: tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ ?>