/
zoho-bitbuck
/
zoho
/
lib
/
Upload File
HOME
<?php // Allow OAuthSimple to be autoloaded instead of always including directly. // The class_exists() call fires the autoloader. if (! class_exists ( 'OAuthSimple' )) { require_once __DIR__ . DIRECTORY_SEPARATOR . 'OAuthSimple.php'; } /** * Define a custom Exception for easy trap and detection */ class XeroOAuthException extends Exception { } class XeroOAuth { var $_xero_defaults; var $_xero_consumer_options; var $_action; var $_nonce_chars; /** * Creates a new XeroOAuth object * * @param string $config, * the configuration settings */ function __construct($config) { $this->params = array (); $this->headers = array (); $this->auto_fixed_time = false; $this->buffer = null; $this->request_params = array(); if (! empty ( $config ['application_type'] )) { switch ($config ['application_type']) { case "Public" : $this->_xero_defaults = array ( 'xero_url' => 'https://api.xero.com/', 'site' => 'https://api.xero.com', 'authorize_url' => 'https://api.xero.com/oauth/Authorize', 'signature_method' => 'HMAC-SHA1' ); break; case "Private" : $this->_xero_defaults = array ( 'xero_url' => 'https://api.xero.com/', 'site' => 'https://api.xero.com', 'authorize_url' => 'https://api.xero.com/oauth/Authorize', 'signature_method' => 'RSA-SHA1' ); break; case "Partner" : $this->_xero_defaults = array ( 'xero_url' => 'https://api.xero.com/', 'site' => 'https://api.xero.com', 'authorize_url' => 'https://api.xero.com/oauth/Authorize', 'signature_method' => 'RSA-SHA1' ); break; } } $this->_xero_consumer_options = array ( 'request_token_path' => 'oauth/RequestToken', 'access_token_path' => 'oauth/AccessToken', 'authorize_path' => 'oauth/Authorize' ); // Remove forced dependency on BASE_PATH constant. // Note that __DIR__ is PHP 5.3 and above only. $base_path = defined ( 'BASE_PATH' ) ? BASE_PATH : dirname ( __DIR__ ); $this->_xero_curl_options = array ( // you probably don't want to change any of these curl values 'curl_connecttimeout' => 30, 'curl_timeout' => 20, // for security you may want to set this to TRUE. If you do you need // to install the servers certificate in your local certificate store. 'curl_ssl_verifypeer' => 2, // include ca-bundle.crt from http://curl.haxx.se/ca/cacert.pem 'curl_cainfo' => $base_path . '/certs/ca-bundle.crt', 'curl_followlocation' => false, // whether to follow redirects or not // TRUE/1 is not a valid ssl verifyhost value with curl >= 7.28.1 and 2 is more secure as well. // More details here: http://php.net/manual/en/function.curl-setopt.php 'curl_ssl_verifyhost' => 2, // support for proxy servers 'curl_proxy' => false, // really you don't want to use this if you are using streaming 'curl_proxyuserpwd' => false, // format username:password for proxy, if required 'curl_encoding' => '', // leave blank for all supported formats, else use gzip, deflate, identity 'curl_verbose' => true ); $this->config = array_merge ( $this->_xero_defaults, $this->_xero_consumer_options, $this->_xero_curl_options, $config ); } /** * Utility function to parse the returned curl headers and store them in the * class array variable. * * @param object $ch * curl handle * @param string $header * the response headers * @return the string length of the header */ private function curlHeader($ch, $header) { $i = strpos ( $header, ':' ); if (! empty ( $i )) { $key = str_replace ( '-', '_', strtolower ( substr ( $header, 0, $i ) ) ); $value = trim ( substr ( $header, $i + 2 ) ); $this->response ['headers'] [$key] = $value; } return strlen ( $header ); } /** * Utility function to parse the returned curl buffer and store them until * an EOL is found. * The buffer for curl is an undefined size so we need * to collect the content until an EOL is found. * * This function calls the previously defined streaming callback method. * * @param object $ch * curl handle * @param string $data * the current curl buffer */ private function curlWrite($ch, $data) { $l = strlen ( $data ); if (strpos ( $data, $this->config ['streaming_eol'] ) === false) { $this->buffer .= $data; return $l; } $buffered = explode ( $this->config ['streaming_eol'], $data ); $content = $this->buffer . $buffered [0]; $this->metrics ['tweets'] ++; $this->metrics ['bytes'] += strlen ( $content ); if (! function_exists ( $this->config ['streaming_callback'] )) return 0; $metrics = $this->update_metrics (); $stop = call_user_func ( $this->config ['streaming_callback'], $content, strlen ( $content ), $metrics ); $this->buffer = $buffered [1]; if ($stop) return 0; return $l; } /** * Extracts and decodes OAuth parameters from the passed string * * @param string $body * the response body from an OAuth flow method * @return array the response body safely decoded to an array of key => values */ function extract_params($body) { $kvs = explode ( '&', $body ); $decoded = array (); foreach ( $kvs as $kv ) { $kv = explode ( '=', $kv, 2 ); $kv [0] = $this->safe_decode ( $kv [0] ); $kv [1] = $this->safe_decode ( $kv [1] ); $decoded [$kv [0]] = $kv [1]; } return $decoded; } /** * Encodes the string or array passed in a way compatible with OAuth. * If an array is passed each array value will will be encoded. * * @param mixed $data * the scalar or array to encode * @return $data encoded in a way compatible with OAuth */ private function safe_encode($data) { if (is_array ( $data )) { return array_map ( array ( $this, 'safe_encode' ), $data ); } else if (is_scalar ( $data )) { return str_ireplace ( array ( '+', '%7E' ), array ( ' ', '~' ), rawurlencode ( $data ) ); } else { return ''; } } /** * Decodes the string or array from it's URL encoded form * If an array is passed each array value will will be decoded. * * @param mixed $data * the scalar or array to decode * @return $data decoded from the URL encoded form */ private function safe_decode($data) { if (is_array ( $data )) { return array_map ( array ( $this, 'safe_decode' ), $data ); } else if (is_scalar ( $data )) { return rawurldecode ( $data ); } else { return ''; } } /** * Prepares the HTTP method for use in the base string by converting it to * uppercase. * * @param string $method * an HTTP method such as GET or POST * @return void value is stored to a class variable * @author themattharris */ private function prepare_method($method) { $this->method = strtoupper ( $method ); } /** * Makes a curl request. * Takes no parameters as all should have been prepared * by the request method * * @return void response data is stored in the class variable 'response' */ private function curlit() { $this->request_params = array(); // configure curl $c = curl_init (); $useragent = (isset ( $this->config ['user_agent'] )) ? (empty ( $this->config ['user_agent'] ) ? 'XeroOAuth-PHP' : $this->config ['user_agent']) : 'XeroOAuth-PHP'; curl_setopt_array ( $c, array ( CURLOPT_USERAGENT => $useragent, CURLOPT_CONNECTTIMEOUT => $this->config ['curl_connecttimeout'], CURLOPT_TIMEOUT => $this->config ['curl_timeout'], CURLOPT_RETURNTRANSFER => TRUE, CURLOPT_SSL_VERIFYPEER => $this->config ['curl_ssl_verifypeer'], CURLOPT_CAINFO => $this->config ['curl_cainfo'], CURLOPT_SSL_VERIFYHOST => $this->config ['curl_ssl_verifyhost'], CURLOPT_FOLLOWLOCATION => $this->config ['curl_followlocation'], CURLOPT_PROXY => $this->config ['curl_proxy'], CURLOPT_ENCODING => $this->config ['curl_encoding'], CURLOPT_URL => $this->sign ['signed_url'], CURLOPT_VERBOSE => $this->config ['curl_verbose'], // process the headers CURLOPT_HEADERFUNCTION => array ( $this, 'curlHeader' ), CURLOPT_HEADER => FALSE, CURLINFO_HEADER_OUT => TRUE ) ); /* ENTRUST CERTIFICATE DEPRECATED if ($this->config ['application_type'] == "Partner") { curl_setopt_array ( $c, array ( // ssl client cert options for partner apps CURLOPT_SSLCERT => $this->config ['curl_ssl_cert'], CURLOPT_SSLKEYPASSWD => $this->config ['curl_ssl_password'], CURLOPT_SSLKEY => $this->config ['curl_ssl_key'] ) ); } */ if ($this->config ['curl_proxyuserpwd'] !== false) curl_setopt ( $c, CURLOPT_PROXYUSERPWD, $this->config ['curl_proxyuserpwd'] ); if (isset ( $this->config ['is_streaming'] )) { // process the body $this->response ['content-length'] = 0; curl_setopt ( $c, CURLOPT_TIMEOUT, 0 ); curl_setopt ( $c, CURLOPT_WRITEFUNCTION, array ( $this, 'curlWrite' ) ); } switch ($this->method) { case 'GET' : $contentLength = 0; break; case 'POST' : curl_setopt ( $c, CURLOPT_POST, TRUE ); $post_body = $this->safe_encode ( $this->xml ); curl_setopt ( $c, CURLOPT_POSTFIELDS, $post_body ); $this->request_params ['xml'] = $post_body; $contentLength = strlen ( $post_body ); $this->headers ['Content-Type'] = 'application/x-www-form-urlencoded'; break; case 'PUT' : $fh = tmpfile(); if ($this->format == "file") { $put_body = $this->xml; } else { $put_body = $this->safe_encode ( $this->xml ); $this->headers ['Content-Type'] = 'application/x-www-form-urlencoded'; } fwrite ( $fh, $put_body ); rewind ( $fh ); curl_setopt ( $c, CURLOPT_PUT, true ); curl_setopt ( $c, CURLOPT_INFILE, $fh ); curl_setopt ( $c, CURLOPT_INFILESIZE, strlen ( $put_body ) ); $contentLength = strlen ( $put_body ); break; default : curl_setopt ( $c, CURLOPT_CUSTOMREQUEST, $this->method ); } if (! empty ( $this->request_params )) { // if not doing multipart we need to implode the parameters if (! $this->config ['multipart']) { foreach ( $this->request_params as $k => $v ) { $ps [] = "{$k}={$v}"; } $this->request_payload = implode ( '&', $ps ); } curl_setopt ( $c, CURLOPT_POSTFIELDS, $this->request_payload); } else { // CURL will set length to -1 when there is no data $this->headers ['Content-Length'] = $contentLength; } $this->headers ['Expect'] = ''; if (! empty ( $this->headers )) { foreach ( $this->headers as $k => $v ) { $headers [] = trim ( $k . ': ' . $v ); } curl_setopt ( $c, CURLOPT_HTTPHEADER, $headers ); } if (isset ( $this->config ['prevent_request'] ) && false == $this->config ['prevent_request']) return; // do it! $response = curl_exec ( $c ); if ($response === false) { $response = 'Curl error: ' . curl_error ( $c ); $code = 1; } else { $code = curl_getinfo ( $c, CURLINFO_HTTP_CODE ); } $info = curl_getinfo ( $c ); curl_close ( $c ); if (isset ( $fh )) { fclose( $fh ); } // store the response $this->response ['code'] = $code; $this->response ['response'] = $response; $this->response ['info'] = $info; $this->response ['format'] = $this->format; return $code; } /** * Make an HTTP request using this library. * This method doesn't return anything. * Instead the response should be inspected directly. * * @param string $method * the HTTP method being used. e.g. POST, GET, HEAD etc * @param string $url * the request URL without query string parameters * @param array $params * the request parameters as an array of key=value pairs * @param string $format * the format of the response. Default json. Set to an empty string to exclude the format * */ function request($method, $url, $params = array(), $xml = "", $format = 'xml') { // removed these as function parameters for now $useauth = true; $multipart = false; $this->headers = array (); if (isset ( $format )) { switch ($format) { case "pdf" : $this->headers ['Accept'] = 'application/pdf'; break; case "json" : $this->headers ['Accept'] = 'application/json'; break; case "xml" : default : $this->headers ['Accept'] = 'application/xml'; break; } } if (isset ( $params ['If-Modified-Since'] )) { $modDate = "If-Modified-Since: " . $params ['If-Modified-Since']; $this->headers ['If-Modified-Since'] = $params ['If-Modified-Since']; } if ($xml !== "") { $xml = trim($xml); $this->xml = $xml; } if ($method == "POST") $params ['xml'] = $xml; $this->prepare_method ( $method ); $this->config ['multipart'] = $multipart; $this->url = $url; $oauthObject = new OAuthSimple (); try { $this->sign = $oauthObject->sign ( array ( 'path' => $url, 'action' => $method, 'parameters' => array_merge ( $params, array ( 'oauth_signature_method' => $this->config ['signature_method'] ) ), 'signatures' => $this->config ) ); } catch ( Exception $e ) { $errorMessage = 'XeroOAuth::request() ' . $e->getMessage (); $this->response['response'] = $errorMessage; $this->response['helper'] = $url; return $this->response; } $this->format = $format; $curlRequest = $this->curlit (); if ($this->response ['code'] == 401 && isset ( $this->config ['session_handle'] )) { if ((strpos ( $this->response ['response'], "oauth_problem=token_expired" ) !== false)) { $this->response ['helper'] = "TokenExpired"; } else { $this->response ['helper'] = "TokenFatal"; } } if ($this->response ['code'] == 403) { $errorMessage = "It looks like your Xero Entrust cert issued by Xero is either invalid or has expired. See http://developer.xero.com/api-overview/http-response-codes/#403 for more"; // default IIS page isn't informative, a little swap $this->response ['response'] = $errorMessage; $this->response ['helper'] = "SetupIssue"; } if ($this->response ['code'] == 0) { $errorMessage = "It looks like your Xero Entrust cert issued by Xero is either invalid or has expired. See http://developer.xero.com/api-overview/http-response-codes/#403 for more"; $this->response ['response'] = $errorMessage; $this->response ['helper'] = "SetupIssue"; } return $this->response; } /** * Convert the response into usable data * * @param string $response * the raw response from the API * @param string $format * the format of the response * @return string the concatenation of the host, API version and API method */ function parseResponse($response, $format) { if (isset ( $format )) { switch ($format) { case "pdf" : $theResponse = $response; break; case "json" : $theResponse = json_decode ( $response ); break; default : $theResponse = simplexml_load_string ( $response ); break; } } return $theResponse; } /** * Utility function to create the request URL in the requested format * * @param string $request * the API method without extension * @return string the concatenation of the host, API version and API method */ function url($request, $api = "core") { if ($request == "RequestToken") { $this->config ['host'] = $this->config ['site'] . '/oauth/'; } elseif ($request == "Authorize") { $this->config ['host'] = $this->config ['authorize_url']; $request = ""; } elseif ($request == "AccessToken") { $this->config ['host'] = $this->config ['site'] . '/oauth/'; } else { if (isset ( $api )) { if ($api == "core") { $api_stem = "api.xro"; $api_version = $this->config ['core_version']; } if ($api == "payroll") { $api_stem = "payroll.xro"; $api_version = $this->config ['payroll_version']; } if ($api == "file") { $api_stem = "files.xro"; $api_version = $this->config ['file_version']; } } $this->config ['host'] = $this->config ['xero_url'] . $api_stem . '/' . $api_version . '/'; } return implode ( array ( $this->config ['host'], $request ) ); } /** * Refreshes the access token for partner API type applications * * @param string $accessToken * the current access token for the session * @param string $sessionHandle * the current session handle for the session * @return array response array from request */ function refreshToken($accessToken, $sessionHandle) { $code = $this->request ( 'GET', $this->url ( 'AccessToken', '' ), array ( 'oauth_token' => $accessToken, 'oauth_session_handle' => $sessionHandle ) ); if ($this->response ['code'] == 200) { $response = $this->extract_params ( $this->response ['response'] ); return $response; } else { $this->response ['helper'] = "TokenFatal"; return $this->response; } } /** * Returns the current URL. * This is instead of PHP_SELF which is unsafe * * @param bool $dropqs * whether to drop the querystring or not. Default true * @return string the current URL */ public static function php_self($dropqs = true) { $protocol = 'http'; if (isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on') { $protocol = 'https'; } elseif (isset($_SERVER['SERVER_PORT']) && ($_SERVER['SERVER_PORT'] == '443')) { $protocol = 'https'; } $url = sprintf('%s://%s%s', $protocol, $_SERVER['SERVER_NAME'], $_SERVER['REQUEST_URI']); $parts = parse_url($url); $port = $_SERVER['SERVER_PORT']; $scheme = $parts['scheme']; $host = $parts['host']; $path = @$parts['path']; $qs = @$parts['query']; $port or $port = ($scheme == 'https') ? '443' : '80'; if (($scheme == 'https' && $port != '443') || ($scheme == 'http' && $port != '80')) { $host = "$host:$port"; } $url = "$scheme://$host$path"; if (!$dropqs) return "{$url}?{$qs}"; else return $url; } /* * Run some basic checks on our config options etc to make sure all is ok */ function diagnostics() { $testOutput = array (); /* ENTRUST CERTIFICATE DEPRECATED if ($this->config ['application_type'] == 'Partner') { if (! file_get_contents ( $this->config ['curl_ssl_cert'] )) { $testOutput ['ssl_cert_error'] = "Can't read the Xero Entrust cert. You need one for partner API applications. http://developer.xero.com/documentation/getting-started/partner-applications/ \n"; } else { $data = openssl_x509_parse ( file_get_contents ( $this->config ['curl_ssl_cert'] ) ); $validFrom = date ( 'Y-m-d H:i:s', $data ['validFrom_time_t'] ); if (time () < $data ['validFrom_time_t']) { $testOutput ['ssl_cert_error'] = "Xero Entrust cert not yet valid - cert valid from " . $validFrom . "\n"; } $validTo = date ( 'Y-m-d H:i:s', $data ['validTo_time_t'] ); if (time () > $data ['validTo_time_t']) { $testOutput ['ssl_cert_error'] = "Xero Entrust cert expired - cert valid to " . $validFrom . "\n"; } } } */ if ($this->config ['application_type'] == 'Partner' || $this->config ['application_type'] == 'Private') { if (! file_exists ( $this->config ['rsa_public_key'] )) $testOutput ['rsa_cert_error'] = "Can't read the self-signed SSL cert. Private and Partner API applications require a self-signed X509 cert http://developer.xero.com/documentation/advanced-docs/public-private-keypair/ \n"; if (file_exists ( $this->config ['rsa_public_key'] )) { $data = openssl_x509_parse ( file_get_contents ( $this->config ['rsa_public_key'] ) ); $validFrom = date ( 'Y-m-d H:i:s', $data ['validFrom_time_t'] ); if (time () < $data ['validFrom_time_t']) { $testOutput ['ssl_cert_error'] = "Application cert not yet valid - cert valid from " . $validFrom . "\n"; } $validTo = date ( 'Y-m-d H:i:s', $data ['validTo_time_t'] ); if (time () > $data ['validTo_time_t']) { $testOutput ['ssl_cert_error'] = "Application cert cert expired - cert valid to " . $validFrom . "\n"; } } if (! file_exists ( $this->config ['rsa_private_key'] )) $testOutput ['rsa_cert_error'] = "Can't read the self-signed cert key. Check your rsa_private_key config variable. Private and Partner API applications require a self-signed X509 cert http://developer.xero.com/documentation/advanced-docs/public-private-keypair/ \n"; if (file_exists ( $this->config ['rsa_private_key'] )) { $cert_content = file_get_contents ( $this->config ['rsa_public_key'] ); $priv_key_content = file_get_contents ( $this->config ['rsa_private_key'] ); if (! openssl_x509_check_private_key ( $cert_content, $priv_key_content )) $testOutput ['rsa_cert_error'] = "Application certificate and key do not match \n"; ; } } return $testOutput; } }