diff options
author | Snap <snapwilliam@gmail.com> | 2015-04-16 14:51:26 -0700 |
---|---|---|
committer | Snap <snapwilliam@gmail.com> | 2015-04-16 14:51:26 -0700 |
commit | 5c7f2f17f9c471d306955df457c7cab4e5c6ed3b (patch) | |
tree | 0fe6d049f1af552af6a74d264a3f571cfdb1ee7c /includes/google-api-php-client-master/src/Google/Http | |
parent | d0b9b771a876922afdf00b2c55d5e1388a4ea2a3 (diff) | |
download | pathery-5c7f2f17f9c471d306955df457c7cab4e5c6ed3b.tar.xz |
Google's OpenID Connect method
$google_client_id & $google_client_secret must be added to db.inc.php!
Diffstat (limited to 'includes/google-api-php-client-master/src/Google/Http')
5 files changed, 1312 insertions, 0 deletions
diff --git a/includes/google-api-php-client-master/src/Google/Http/Batch.php b/includes/google-api-php-client-master/src/Google/Http/Batch.php new file mode 100644 index 0000000..6286f31 --- /dev/null +++ b/includes/google-api-php-client-master/src/Google/Http/Batch.php @@ -0,0 +1,143 @@ +<?php +/* + * Copyright 2012 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +if (!class_exists('Google_Client')) { + require_once dirname(__FILE__) . '/../autoload.php'; +} + +/** + * Class to handle batched requests to the Google API service. + */ +class Google_Http_Batch +{ + /** @var string Multipart Boundary. */ + private $boundary; + + /** @var array service requests to be executed. */ + private $requests = array(); + + /** @var Google_Client */ + private $client; + + private $expected_classes = array(); + + private $base_path; + + public function __construct(Google_Client $client, $boundary = false) + { + $this->client = $client; + $this->base_path = $this->client->getBasePath(); + $this->expected_classes = array(); + $boundary = (false == $boundary) ? mt_rand() : $boundary; + $this->boundary = str_replace('"', '', $boundary); + } + + public function add(Google_Http_Request $request, $key = false) + { + if (false == $key) { + $key = mt_rand(); + } + + $this->requests[$key] = $request; + } + + public function execute() + { + $body = ''; + + /** @var Google_Http_Request $req */ + foreach ($this->requests as $key => $req) { + $body .= "--{$this->boundary}\n"; + $body .= $req->toBatchString($key) . "\n"; + $this->expected_classes["response-" . $key] = $req->getExpectedClass(); + } + + $body = rtrim($body); + $body .= "\n--{$this->boundary}--"; + + $url = $this->base_path . '/batch'; + $httpRequest = new Google_Http_Request($url, 'POST'); + $httpRequest->setRequestHeaders( + array('Content-Type' => 'multipart/mixed; boundary=' . $this->boundary) + ); + + $httpRequest->setPostBody($body); + $response = $this->client->getIo()->makeRequest($httpRequest); + + return $this->parseResponse($response); + } + + public function parseResponse(Google_Http_Request $response) + { + $contentType = $response->getResponseHeader('content-type'); + $contentType = explode(';', $contentType); + $boundary = false; + foreach ($contentType as $part) { + $part = (explode('=', $part, 2)); + if (isset($part[0]) && 'boundary' == trim($part[0])) { + $boundary = $part[1]; + } + } + + $body = $response->getResponseBody(); + if ($body) { + $body = str_replace("--$boundary--", "--$boundary", $body); + $parts = explode("--$boundary", $body); + $responses = array(); + + foreach ($parts as $part) { + $part = trim($part); + if (!empty($part)) { + list($metaHeaders, $part) = explode("\r\n\r\n", $part, 2); + $metaHeaders = $this->client->getIo()->getHttpResponseHeaders($metaHeaders); + + $status = substr($part, 0, strpos($part, "\n")); + $status = explode(" ", $status); + $status = $status[1]; + + list($partHeaders, $partBody) = $this->client->getIo()->ParseHttpResponse($part, false); + $response = new Google_Http_Request(""); + $response->setResponseHttpCode($status); + $response->setResponseHeaders($partHeaders); + $response->setResponseBody($partBody); + + // Need content id. + $key = $metaHeaders['content-id']; + + if (isset($this->expected_classes[$key]) && + strlen($this->expected_classes[$key]) > 0) { + $class = $this->expected_classes[$key]; + $response->setExpectedClass($class); + } + + try { + $response = Google_Http_REST::decodeHttpResponse($response, $this->client); + $responses[$key] = $response; + } catch (Google_Service_Exception $e) { + // Store the exception as the response, so successful responses + // can be processed. + $responses[$key] = $e; + } + } + } + + return $responses; + } + + return null; + } +} diff --git a/includes/google-api-php-client-master/src/Google/Http/CacheParser.php b/includes/google-api-php-client-master/src/Google/Http/CacheParser.php new file mode 100644 index 0000000..a6167ad --- /dev/null +++ b/includes/google-api-php-client-master/src/Google/Http/CacheParser.php @@ -0,0 +1,185 @@ +<?php +/* + * Copyright 2012 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +if (!class_exists('Google_Client')) { + require_once dirname(__FILE__) . '/../autoload.php'; +} + +/** + * Implement the caching directives specified in rfc2616. This + * implementation is guided by the guidance offered in rfc2616-sec13. + */ +class Google_Http_CacheParser +{ + public static $CACHEABLE_HTTP_METHODS = array('GET', 'HEAD'); + public static $CACHEABLE_STATUS_CODES = array('200', '203', '300', '301'); + + /** + * Check if an HTTP request can be cached by a private local cache. + * + * @static + * @param Google_Http_Request $resp + * @return bool True if the request is cacheable. + * False if the request is uncacheable. + */ + public static function isRequestCacheable(Google_Http_Request $resp) + { + $method = $resp->getRequestMethod(); + if (! in_array($method, self::$CACHEABLE_HTTP_METHODS)) { + return false; + } + + // Don't cache authorized requests/responses. + // [rfc2616-14.8] When a shared cache receives a request containing an + // Authorization field, it MUST NOT return the corresponding response + // as a reply to any other request... + if ($resp->getRequestHeader("authorization")) { + return false; + } + + return true; + } + + /** + * Check if an HTTP response can be cached by a private local cache. + * + * @static + * @param Google_Http_Request $resp + * @return bool True if the response is cacheable. + * False if the response is un-cacheable. + */ + public static function isResponseCacheable(Google_Http_Request $resp) + { + // First, check if the HTTP request was cacheable before inspecting the + // HTTP response. + if (false == self::isRequestCacheable($resp)) { + return false; + } + + $code = $resp->getResponseHttpCode(); + if (! in_array($code, self::$CACHEABLE_STATUS_CODES)) { + return false; + } + + // The resource is uncacheable if the resource is already expired and + // the resource doesn't have an ETag for revalidation. + $etag = $resp->getResponseHeader("etag"); + if (self::isExpired($resp) && $etag == false) { + return false; + } + + // [rfc2616-14.9.2] If [no-store is] sent in a response, a cache MUST NOT + // store any part of either this response or the request that elicited it. + $cacheControl = $resp->getParsedCacheControl(); + if (isset($cacheControl['no-store'])) { + return false; + } + + // Pragma: no-cache is an http request directive, but is occasionally + // used as a response header incorrectly. + $pragma = $resp->getResponseHeader('pragma'); + if ($pragma == 'no-cache' || strpos($pragma, 'no-cache') !== false) { + return false; + } + + // [rfc2616-14.44] Vary: * is extremely difficult to cache. "It implies that + // a cache cannot determine from the request headers of a subsequent request + // whether this response is the appropriate representation." + // Given this, we deem responses with the Vary header as uncacheable. + $vary = $resp->getResponseHeader('vary'); + if ($vary) { + return false; + } + + return true; + } + + /** + * @static + * @param Google_Http_Request $resp + * @return bool True if the HTTP response is considered to be expired. + * False if it is considered to be fresh. + */ + public static function isExpired(Google_Http_Request $resp) + { + // HTTP/1.1 clients and caches MUST treat other invalid date formats, + // especially including the value “0”, as in the past. + $parsedExpires = false; + $responseHeaders = $resp->getResponseHeaders(); + + if (isset($responseHeaders['expires'])) { + $rawExpires = $responseHeaders['expires']; + // Check for a malformed expires header first. + if (empty($rawExpires) || (is_numeric($rawExpires) && $rawExpires <= 0)) { + return true; + } + + // See if we can parse the expires header. + $parsedExpires = strtotime($rawExpires); + if (false == $parsedExpires || $parsedExpires <= 0) { + return true; + } + } + + // Calculate the freshness of an http response. + $freshnessLifetime = false; + $cacheControl = $resp->getParsedCacheControl(); + if (isset($cacheControl['max-age'])) { + $freshnessLifetime = $cacheControl['max-age']; + } + + $rawDate = $resp->getResponseHeader('date'); + $parsedDate = strtotime($rawDate); + + if (empty($rawDate) || false == $parsedDate) { + // We can't default this to now, as that means future cache reads + // will always pass with the logic below, so we will require a + // date be injected if not supplied. + throw new Google_Exception("All cacheable requests must have creation dates."); + } + + if (false == $freshnessLifetime && isset($responseHeaders['expires'])) { + $freshnessLifetime = $parsedExpires - $parsedDate; + } + + if (false == $freshnessLifetime) { + return true; + } + + // Calculate the age of an http response. + $age = max(0, time() - $parsedDate); + if (isset($responseHeaders['age'])) { + $age = max($age, strtotime($responseHeaders['age'])); + } + + return $freshnessLifetime <= $age; + } + + /** + * Determine if a cache entry should be revalidated with by the origin. + * + * @param Google_Http_Request $response + * @return bool True if the entry is expired, else return false. + */ + public static function mustRevalidate(Google_Http_Request $response) + { + // [13.3] When a cache has a stale entry that it would like to use as a + // response to a client's request, it first has to check with the origin + // server to see if its cached entry is still usable. + return self::isExpired($response); + } +} diff --git a/includes/google-api-php-client-master/src/Google/Http/MediaFileUpload.php b/includes/google-api-php-client-master/src/Google/Http/MediaFileUpload.php new file mode 100644 index 0000000..b7c9fea --- /dev/null +++ b/includes/google-api-php-client-master/src/Google/Http/MediaFileUpload.php @@ -0,0 +1,302 @@ +<?php +/** + * Copyright 2012 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +if (!class_exists('Google_Client')) { + require_once dirname(__FILE__) . '/../autoload.php'; +} + +/** + * Manage large file uploads, which may be media but can be any type + * of sizable data. + */ +class Google_Http_MediaFileUpload +{ + const UPLOAD_MEDIA_TYPE = 'media'; + const UPLOAD_MULTIPART_TYPE = 'multipart'; + const UPLOAD_RESUMABLE_TYPE = 'resumable'; + + /** @var string $mimeType */ + private $mimeType; + + /** @var string $data */ + private $data; + + /** @var bool $resumable */ + private $resumable; + + /** @var int $chunkSize */ + private $chunkSize; + + /** @var int $size */ + private $size; + + /** @var string $resumeUri */ + private $resumeUri; + + /** @var int $progress */ + private $progress; + + /** @var Google_Client */ + private $client; + + /** @var Google_Http_Request */ + private $request; + + /** @var string */ + private $boundary; + + /** + * Result code from last HTTP call + * @var int + */ + private $httpResultCode; + + /** + * @param $mimeType string + * @param $data string The bytes you want to upload. + * @param $resumable bool + * @param bool $chunkSize File will be uploaded in chunks of this many bytes. + * only used if resumable=True + */ + public function __construct( + Google_Client $client, + Google_Http_Request $request, + $mimeType, + $data, + $resumable = false, + $chunkSize = false, + $boundary = false + ) { + $this->client = $client; + $this->request = $request; + $this->mimeType = $mimeType; + $this->data = $data; + $this->size = strlen($this->data); + $this->resumable = $resumable; + if (!$chunkSize) { + $chunkSize = 256 * 1024; + } + $this->chunkSize = $chunkSize; + $this->progress = 0; + $this->boundary = $boundary; + + // Process Media Request + $this->process(); + } + + /** + * Set the size of the file that is being uploaded. + * @param $size - int file size in bytes + */ + public function setFileSize($size) + { + $this->size = $size; + } + + /** + * Return the progress on the upload + * @return int progress in bytes uploaded. + */ + public function getProgress() + { + return $this->progress; + } + + /** + * Return the HTTP result code from the last call made. + * @return int code + */ + public function getHttpResultCode() + { + return $this->httpResultCode; + } + + /** + * Send the next part of the file to upload. + * @param [$chunk] the next set of bytes to send. If false will used $data passed + * at construct time. + */ + public function nextChunk($chunk = false) + { + if (false == $this->resumeUri) { + $this->resumeUri = $this->getResumeUri(); + } + + if (false == $chunk) { + $chunk = substr($this->data, $this->progress, $this->chunkSize); + } + + $lastBytePos = $this->progress + strlen($chunk) - 1; + $headers = array( + 'content-range' => "bytes $this->progress-$lastBytePos/$this->size", + 'content-type' => $this->request->getRequestHeader('content-type'), + 'content-length' => $this->chunkSize, + 'expect' => '', + ); + + $httpRequest = new Google_Http_Request( + $this->resumeUri, + 'PUT', + $headers, + $chunk + ); + + if ($this->client->getClassConfig("Google_Http_Request", "enable_gzip_for_uploads")) { + $httpRequest->enableGzip(); + } else { + $httpRequest->disableGzip(); + } + + $response = $this->client->getIo()->makeRequest($httpRequest); + $response->setExpectedClass($this->request->getExpectedClass()); + $code = $response->getResponseHttpCode(); + $this->httpResultCode = $code; + + if (308 == $code) { + // Track the amount uploaded. + $range = explode('-', $response->getResponseHeader('range')); + $this->progress = $range[1] + 1; + + // Allow for changing upload URLs. + $location = $response->getResponseHeader('location'); + if ($location) { + $this->resumeUri = $location; + } + + // No problems, but upload not complete. + return false; + } else { + return Google_Http_REST::decodeHttpResponse($response, $this->client); + } + } + + /** + * @param $meta + * @param $params + * @return array|bool + * @visible for testing + */ + private function process() + { + $postBody = false; + $contentType = false; + + $meta = $this->request->getPostBody(); + $meta = is_string($meta) ? json_decode($meta, true) : $meta; + + $uploadType = $this->getUploadType($meta); + $this->request->setQueryParam('uploadType', $uploadType); + $this->transformToUploadUrl(); + $mimeType = $this->mimeType ? + $this->mimeType : + $this->request->getRequestHeader('content-type'); + + if (self::UPLOAD_RESUMABLE_TYPE == $uploadType) { + $contentType = $mimeType; + $postBody = is_string($meta) ? $meta : json_encode($meta); + } else if (self::UPLOAD_MEDIA_TYPE == $uploadType) { + $contentType = $mimeType; + $postBody = $this->data; + } else if (self::UPLOAD_MULTIPART_TYPE == $uploadType) { + // This is a multipart/related upload. + $boundary = $this->boundary ? $this->boundary : mt_rand(); + $boundary = str_replace('"', '', $boundary); + $contentType = 'multipart/related; boundary=' . $boundary; + $related = "--$boundary\r\n"; + $related .= "Content-Type: application/json; charset=UTF-8\r\n"; + $related .= "\r\n" . json_encode($meta) . "\r\n"; + $related .= "--$boundary\r\n"; + $related .= "Content-Type: $mimeType\r\n"; + $related .= "Content-Transfer-Encoding: base64\r\n"; + $related .= "\r\n" . base64_encode($this->data) . "\r\n"; + $related .= "--$boundary--"; + $postBody = $related; + } + + $this->request->setPostBody($postBody); + + if (isset($contentType) && $contentType) { + $contentTypeHeader['content-type'] = $contentType; + $this->request->setRequestHeaders($contentTypeHeader); + } + } + + private function transformToUploadUrl() + { + $base = $this->request->getBaseComponent(); + $this->request->setBaseComponent($base . '/upload'); + } + + /** + * Valid upload types: + * - resumable (UPLOAD_RESUMABLE_TYPE) + * - media (UPLOAD_MEDIA_TYPE) + * - multipart (UPLOAD_MULTIPART_TYPE) + * @param $meta + * @return string + * @visible for testing + */ + public function getUploadType($meta) + { + if ($this->resumable) { + return self::UPLOAD_RESUMABLE_TYPE; + } + + if (false == $meta && $this->data) { + return self::UPLOAD_MEDIA_TYPE; + } + + return self::UPLOAD_MULTIPART_TYPE; + } + + private function getResumeUri() + { + $result = null; + $body = $this->request->getPostBody(); + if ($body) { + $headers = array( + 'content-type' => 'application/json; charset=UTF-8', + 'content-length' => Google_Utils::getStrLen($body), + 'x-upload-content-type' => $this->mimeType, + 'x-upload-content-length' => $this->size, + 'expect' => '', + ); + $this->request->setRequestHeaders($headers); + } + + $response = $this->client->getIo()->makeRequest($this->request); + $location = $response->getResponseHeader('location'); + $code = $response->getResponseHttpCode(); + + if (200 == $code && true == $location) { + return $location; + } + $message = $code; + $body = @json_decode($response->getResponseBody()); + if (!empty( $body->error->errors ) ) { + $message .= ': '; + foreach ($body->error->errors as $error) { + $message .= "{$error->domain}, {$error->message};"; + } + $message = rtrim($message, ';'); + } + + $error = "Failed to start the resumable upload (HTTP {$message})"; + $this->client->getLogger()->error($error); + throw new Google_Exception($error); + } +} diff --git a/includes/google-api-php-client-master/src/Google/Http/REST.php b/includes/google-api-php-client-master/src/Google/Http/REST.php new file mode 100644 index 0000000..10b23e5 --- /dev/null +++ b/includes/google-api-php-client-master/src/Google/Http/REST.php @@ -0,0 +1,178 @@ +<?php +/* + * Copyright 2010 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +if (!class_exists('Google_Client')) { + require_once dirname(__FILE__) . '/../autoload.php'; +} + +/** + * This class implements the RESTful transport of apiServiceRequest()'s + */ +class Google_Http_REST +{ + /** + * Executes a Google_Http_Request and (if applicable) automatically retries + * when errors occur. + * + * @param Google_Client $client + * @param Google_Http_Request $req + * @return array decoded result + * @throws Google_Service_Exception on server side error (ie: not authenticated, + * invalid or malformed post body, invalid url) + */ + public static function execute(Google_Client $client, Google_Http_Request $req) + { + $runner = new Google_Task_Runner( + $client, + sprintf('%s %s', $req->getRequestMethod(), $req->getUrl()), + array(get_class(), 'doExecute'), + array($client, $req) + ); + + return $runner->run(); + } + + /** + * Executes a Google_Http_Request + * + * @param Google_Client $client + * @param Google_Http_Request $req + * @return array decoded result + * @throws Google_Service_Exception on server side error (ie: not authenticated, + * invalid or malformed post body, invalid url) + */ + public static function doExecute(Google_Client $client, Google_Http_Request $req) + { + $httpRequest = $client->getIo()->makeRequest($req); + $httpRequest->setExpectedClass($req->getExpectedClass()); + return self::decodeHttpResponse($httpRequest, $client); + } + + /** + * Decode an HTTP Response. + * @static + * @throws Google_Service_Exception + * @param Google_Http_Request $response The http response to be decoded. + * @param Google_Client $client + * @return mixed|null + */ + public static function decodeHttpResponse($response, Google_Client $client = null) + { + $code = $response->getResponseHttpCode(); + $body = $response->getResponseBody(); + $decoded = null; + + if ((intVal($code)) >= 300) { + $decoded = json_decode($body, true); + $err = 'Error calling ' . $response->getRequestMethod() . ' ' . $response->getUrl(); + if (isset($decoded['error']) && + isset($decoded['error']['message']) && + isset($decoded['error']['code'])) { + // if we're getting a json encoded error definition, use that instead of the raw response + // body for improved readability + $err .= ": ({$decoded['error']['code']}) {$decoded['error']['message']}"; + } else { + $err .= ": ($code) $body"; + } + + $errors = null; + // Specific check for APIs which don't return error details, such as Blogger. + if (isset($decoded['error']) && isset($decoded['error']['errors'])) { + $errors = $decoded['error']['errors']; + } + + $map = null; + if ($client) { + $client->getLogger()->error( + $err, + array('code' => $code, 'errors' => $errors) + ); + + $map = $client->getClassConfig( + 'Google_Service_Exception', + 'retry_map' + ); + } + throw new Google_Service_Exception($err, $code, null, $errors, $map); + } + + // Only attempt to decode the response, if the response code wasn't (204) 'no content' + if ($code != '204') { + if ($response->getExpectedRaw()) { + return $body; + } + + $decoded = json_decode($body, true); + if ($decoded === null || $decoded === "") { + $error = "Invalid json in service response: $body"; + if ($client) { + $client->getLogger()->error($error); + } + throw new Google_Service_Exception($error); + } + + if ($response->getExpectedClass()) { + $class = $response->getExpectedClass(); + $decoded = new $class($decoded); + } + } + return $decoded; + } + + /** + * Parse/expand request parameters and create a fully qualified + * request uri. + * @static + * @param string $servicePath + * @param string $restPath + * @param array $params + * @return string $requestUrl + */ + public static function createRequestUri($servicePath, $restPath, $params) + { + $requestUrl = $servicePath . $restPath; + $uriTemplateVars = array(); + $queryVars = array(); + foreach ($params as $paramName => $paramSpec) { + if ($paramSpec['type'] == 'boolean') { + $paramSpec['value'] = ($paramSpec['value']) ? 'true' : 'false'; + } + if ($paramSpec['location'] == 'path') { + $uriTemplateVars[$paramName] = $paramSpec['value']; + } else if ($paramSpec['location'] == 'query') { + if (isset($paramSpec['repeated']) && is_array($paramSpec['value'])) { + foreach ($paramSpec['value'] as $value) { + $queryVars[] = $paramName . '=' . rawurlencode($value); + } + } else { + $queryVars[] = $paramName . '=' . rawurlencode($paramSpec['value']); + } + } + } + + if (count($uriTemplateVars)) { + $uriTemplateParser = new Google_Utils_URITemplate(); + $requestUrl = $uriTemplateParser->parse($requestUrl, $uriTemplateVars); + } + + if (count($queryVars)) { + $requestUrl .= '?' . implode($queryVars, '&'); + } + + return $requestUrl; + } +} diff --git a/includes/google-api-php-client-master/src/Google/Http/Request.php b/includes/google-api-php-client-master/src/Google/Http/Request.php new file mode 100644 index 0000000..b3113d5 --- /dev/null +++ b/includes/google-api-php-client-master/src/Google/Http/Request.php @@ -0,0 +1,504 @@ +<?php +/* + * Copyright 2010 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +if (!class_exists('Google_Client')) { + require_once dirname(__FILE__) . '/../autoload.php'; +} + +/** + * HTTP Request to be executed by IO classes. Upon execution, the + * responseHttpCode, responseHeaders and responseBody will be filled in. + * + * @author Chris Chabot <chabotc@google.com> + * @author Chirag Shah <chirags@google.com> + * + */ +class Google_Http_Request +{ + const GZIP_UA = " (gzip)"; + + private $batchHeaders = array( + 'Content-Type' => 'application/http', + 'Content-Transfer-Encoding' => 'binary', + 'MIME-Version' => '1.0', + ); + + protected $queryParams; + protected $requestMethod; + protected $requestHeaders; + protected $baseComponent = null; + protected $path; + protected $postBody; + protected $userAgent; + protected $canGzip = null; + + protected $responseHttpCode; + protected $responseHeaders; + protected $responseBody; + + protected $expectedClass; + protected $expectedRaw = false; + + public $accessKey; + + public function __construct( + $url, + $method = 'GET', + $headers = array(), + $postBody = null + ) { + $this->setUrl($url); + $this->setRequestMethod($method); + $this->setRequestHeaders($headers); + $this->setPostBody($postBody); + } + + /** + * Misc function that returns the base url component of the $url + * used by the OAuth signing class to calculate the base string + * @return string The base url component of the $url. + */ + public function getBaseComponent() + { + return $this->baseComponent; + } + + /** + * Set the base URL that path and query parameters will be added to. + * @param $baseComponent string + */ + public function setBaseComponent($baseComponent) + { + $this->baseComponent = $baseComponent; + } + + /** + * Enable support for gzipped responses with this request. + */ + public function enableGzip() + { + $this->setRequestHeaders(array("Accept-Encoding" => "gzip")); + $this->canGzip = true; + $this->setUserAgent($this->userAgent); + } + + /** + * Disable support for gzip responses with this request. + */ + public function disableGzip() + { + if ( + isset($this->requestHeaders['accept-encoding']) && + $this->requestHeaders['accept-encoding'] == "gzip" + ) { + unset($this->requestHeaders['accept-encoding']); + } + $this->canGzip = false; + $this->userAgent = str_replace(self::GZIP_UA, "", $this->userAgent); + } + + /** + * Can this request accept a gzip response? + * @return bool + */ + public function canGzip() + { + return $this->canGzip; + } + + /** + * Misc function that returns an array of the query parameters of the current + * url used by the OAuth signing class to calculate the signature + * @return array Query parameters in the query string. + */ + public function getQueryParams() + { + return $this->queryParams; + } + + /** + * Set a new query parameter. + * @param $key - string to set, does not need to be URL encoded + * @param $value - string to set, does not need to be URL encoded + */ + public function setQueryParam($key, $value) + { + $this->queryParams[$key] = $value; + } + + /** + * @return string HTTP Response Code. + */ + public function getResponseHttpCode() + { + return (int) $this->responseHttpCode; + } + + /** + * @param int $responseHttpCode HTTP Response Code. + */ + public function setResponseHttpCode($responseHttpCode) + { + $this->responseHttpCode = $responseHttpCode; + } + + /** + * @return $responseHeaders (array) HTTP Response Headers. + */ + public function getResponseHeaders() + { + return $this->responseHeaders; + } + + /** + * @return string HTTP Response Body + */ + public function getResponseBody() + { + return $this->responseBody; + } + + /** + * Set the class the response to this request should expect. + * + * @param $class string the class name + */ + public function setExpectedClass($class) + { + $this->expectedClass = $class; + } + + /** + * Retrieve the expected class the response should expect. + * @return string class name + */ + public function getExpectedClass() + { + return $this->expectedClass; + } + + /** + * Enable expected raw response + */ + public function enableExpectedRaw() + { + $this->expectedRaw = true; + } + + /** + * Disable expected raw response + */ + public function disableExpectedRaw() + { + $this->expectedRaw = false; + } + + /** + * Expected raw response or not. + * @return boolean expected raw response + */ + public function getExpectedRaw() + { + return $this->expectedRaw; + } + + /** + * @param array $headers The HTTP response headers + * to be normalized. + */ + public function setResponseHeaders($headers) + { + $headers = Google_Utils::normalize($headers); + if ($this->responseHeaders) { + $headers = array_merge($this->responseHeaders, $headers); + } + + $this->responseHeaders = $headers; + } + + /** + * @param string $key + * @return array|boolean Returns the requested HTTP header or + * false if unavailable. + */ + public function getResponseHeader($key) + { + return isset($this->responseHeaders[$key]) + ? $this->responseHeaders[$key] + : false; + } + + /** + * @param string $responseBody The HTTP response body. + */ + public function setResponseBody($responseBody) + { + $this->responseBody = $responseBody; + } + + /** + * @return string $url The request URL. + */ + public function getUrl() + { + return $this->baseComponent . $this->path . + (count($this->queryParams) ? + "?" . $this->buildQuery($this->queryParams) : + ''); + } + + /** + * @return string $method HTTP Request Method. + */ + public function getRequestMethod() + { + return $this->requestMethod; + } + + /** + * @return array $headers HTTP Request Headers. + */ + public function getRequestHeaders() + { + return $this->requestHeaders; + } + + /** + * @param string $key + * @return array|boolean Returns the requested HTTP header or + * false if unavailable. + */ + public function getRequestHeader($key) + { + return isset($this->requestHeaders[$key]) + ? $this->requestHeaders[$key] + : false; + } + + /** + * @return string $postBody HTTP Request Body. + */ + public function getPostBody() + { + return $this->postBody; + } + + /** + * @param string $url the url to set + */ + public function setUrl($url) + { + if (substr($url, 0, 4) != 'http') { + // Force the path become relative. + if (substr($url, 0, 1) !== '/') { + $url = '/' . $url; + } + } + $parts = parse_url($url); + if (isset($parts['host'])) { + $this->baseComponent = sprintf( + "%s%s%s", + isset($parts['scheme']) ? $parts['scheme'] . "://" : '', + isset($parts['host']) ? $parts['host'] : '', + isset($parts['port']) ? ":" . $parts['port'] : '' + ); + } + $this->path = isset($parts['path']) ? $parts['path'] : ''; + $this->queryParams = array(); + if (isset($parts['query'])) { + $this->queryParams = $this->parseQuery($parts['query']); + } + } + + /** + * @param string $method Set he HTTP Method and normalize + * it to upper-case, as required by HTTP. + * + */ + public function setRequestMethod($method) + { + $this->requestMethod = strtoupper($method); + } + + /** + * @param array $headers The HTTP request headers + * to be set and normalized. + */ + public function setRequestHeaders($headers) + { + $headers = Google_Utils::normalize($headers); + if ($this->requestHeaders) { + $headers = array_merge($this->requestHeaders, $headers); + } + $this->requestHeaders = $headers; + } + + /** + * @param string $postBody the postBody to set + */ + public function setPostBody($postBody) + { + $this->postBody = $postBody; + } + + /** + * Set the User-Agent Header. + * @param string $userAgent The User-Agent. + */ + public function setUserAgent($userAgent) + { + $this->userAgent = $userAgent; + if ($this->canGzip) { + $this->userAgent = $userAgent . self::GZIP_UA; + } + } + + /** + * @return string The User-Agent. + */ + public function getUserAgent() + { + return $this->userAgent; + } + + /** + * Returns a cache key depending on if this was an OAuth signed request + * in which case it will use the non-signed url and access key to make this + * cache key unique per authenticated user, else use the plain request url + * @return string The md5 hash of the request cache key. + */ + public function getCacheKey() + { + $key = $this->getUrl(); + + if (isset($this->accessKey)) { + $key .= $this->accessKey; + } + + if (isset($this->requestHeaders['authorization'])) { + $key .= $this->requestHeaders['authorization']; + } + + return md5($key); + } + + public function getParsedCacheControl() + { + $parsed = array(); + $rawCacheControl = $this->getResponseHeader('cache-control'); + if ($rawCacheControl) { + $rawCacheControl = str_replace(', ', '&', $rawCacheControl); + parse_str($rawCacheControl, $parsed); + } + + return $parsed; + } + + /** + * @param string $id + * @return string A string representation of the HTTP Request. + */ + public function toBatchString($id) + { + $str = ''; + $path = parse_url($this->getUrl(), PHP_URL_PATH) . "?" . + http_build_query($this->queryParams); + $str .= $this->getRequestMethod() . ' ' . $path . " HTTP/1.1\n"; + + foreach ($this->getRequestHeaders() as $key => $val) { + $str .= $key . ': ' . $val . "\n"; + } + + if ($this->getPostBody()) { + $str .= "\n"; + $str .= $this->getPostBody(); + } + + $headers = ''; + foreach ($this->batchHeaders as $key => $val) { + $headers .= $key . ': ' . $val . "\n"; + } + + $headers .= "Content-ID: $id\n"; + $str = $headers . "\n" . $str; + + return $str; + } + + /** + * Our own version of parse_str that allows for multiple variables + * with the same name. + * @param $string - the query string to parse + */ + private function parseQuery($string) + { + $return = array(); + $parts = explode("&", $string); + foreach ($parts as $part) { + list($key, $value) = explode('=', $part, 2); + $value = urldecode($value); + if (isset($return[$key])) { + if (!is_array($return[$key])) { + $return[$key] = array($return[$key]); + } + $return[$key][] = $value; + } else { + $return[$key] = $value; + } + } + return $return; + } + + /** + * A version of build query that allows for multiple + * duplicate keys. + * @param $parts array of key value pairs + */ + private function buildQuery($parts) + { + $return = array(); + foreach ($parts as $key => $value) { + if (is_array($value)) { + foreach ($value as $v) { + $return[] = urlencode($key) . "=" . urlencode($v); + } + } else { + $return[] = urlencode($key) . "=" . urlencode($value); + } + } + return implode('&', $return); + } + + /** + * If we're POSTing and have no body to send, we can send the query + * parameters in there, which avoids length issues with longer query + * params. + */ + public function maybeMoveParametersToBody() + { + if ($this->getRequestMethod() == "POST" && empty($this->postBody)) { + $this->setRequestHeaders( + array( + "content-type" => + "application/x-www-form-urlencoded; charset=UTF-8" + ) + ); + $this->setPostBody($this->buildQuery($this->queryParams)); + $this->queryParams = array(); + } + } +} |