From 0b02a6d123b2014705998c94ddf3d460948d3eac Mon Sep 17 00:00:00 2001 From: redmatrix Date: Tue, 10 May 2016 17:26:44 -0700 Subject: initial sabre upgrade (needs lots of work - to wit: authentication, redo the browser interface, and rework event export/import) --- vendor/sabre/http/.gitignore | 15 + vendor/sabre/http/.travis.yml | 24 + vendor/sabre/http/CHANGELOG.md | 250 ++++++++ vendor/sabre/http/LICENSE | 27 + vendor/sabre/http/README.md | 746 ++++++++++++++++++++++++ vendor/sabre/http/bin/.empty | 0 vendor/sabre/http/lib/Auth/AWS.php | 234 ++++++++ vendor/sabre/http/lib/Auth/AbstractAuth.php | 73 +++ vendor/sabre/http/lib/Auth/Basic.php | 63 ++ vendor/sabre/http/lib/Auth/Bearer.php | 56 ++ vendor/sabre/http/lib/Auth/Digest.php | 231 ++++++++ vendor/sabre/http/lib/Client.php | 601 +++++++++++++++++++ vendor/sabre/http/lib/ClientException.php | 15 + vendor/sabre/http/lib/ClientHttpException.php | 58 ++ vendor/sabre/http/lib/HttpException.php | 30 + vendor/sabre/http/lib/Message.php | 314 ++++++++++ vendor/sabre/http/lib/MessageDecoratorTrait.php | 250 ++++++++ vendor/sabre/http/lib/MessageInterface.php | 177 ++++++ vendor/sabre/http/lib/Request.php | 316 ++++++++++ vendor/sabre/http/lib/RequestDecorator.php | 231 ++++++++ vendor/sabre/http/lib/RequestInterface.php | 147 +++++ vendor/sabre/http/lib/Response.php | 194 ++++++ vendor/sabre/http/lib/ResponseDecorator.php | 84 +++ vendor/sabre/http/lib/ResponseInterface.php | 45 ++ vendor/sabre/http/lib/Sapi.php | 194 ++++++ vendor/sabre/http/lib/URLUtil.php | 103 ++++ vendor/sabre/http/lib/Util.php | 74 +++ vendor/sabre/http/lib/Version.php | 19 + vendor/sabre/http/lib/functions.php | 445 ++++++++++++++ 29 files changed, 5016 insertions(+) create mode 100644 vendor/sabre/http/.gitignore create mode 100644 vendor/sabre/http/.travis.yml create mode 100644 vendor/sabre/http/CHANGELOG.md create mode 100644 vendor/sabre/http/LICENSE create mode 100644 vendor/sabre/http/README.md create mode 100644 vendor/sabre/http/bin/.empty create mode 100644 vendor/sabre/http/lib/Auth/AWS.php create mode 100644 vendor/sabre/http/lib/Auth/AbstractAuth.php create mode 100644 vendor/sabre/http/lib/Auth/Basic.php create mode 100644 vendor/sabre/http/lib/Auth/Bearer.php create mode 100644 vendor/sabre/http/lib/Auth/Digest.php create mode 100644 vendor/sabre/http/lib/Client.php create mode 100644 vendor/sabre/http/lib/ClientException.php create mode 100644 vendor/sabre/http/lib/ClientHttpException.php create mode 100644 vendor/sabre/http/lib/HttpException.php create mode 100644 vendor/sabre/http/lib/Message.php create mode 100644 vendor/sabre/http/lib/MessageDecoratorTrait.php create mode 100644 vendor/sabre/http/lib/MessageInterface.php create mode 100644 vendor/sabre/http/lib/Request.php create mode 100644 vendor/sabre/http/lib/RequestDecorator.php create mode 100644 vendor/sabre/http/lib/RequestInterface.php create mode 100644 vendor/sabre/http/lib/Response.php create mode 100644 vendor/sabre/http/lib/ResponseDecorator.php create mode 100644 vendor/sabre/http/lib/ResponseInterface.php create mode 100644 vendor/sabre/http/lib/Sapi.php create mode 100644 vendor/sabre/http/lib/URLUtil.php create mode 100644 vendor/sabre/http/lib/Util.php create mode 100644 vendor/sabre/http/lib/Version.php create mode 100644 vendor/sabre/http/lib/functions.php (limited to 'vendor/sabre/http') diff --git a/vendor/sabre/http/.gitignore b/vendor/sabre/http/.gitignore new file mode 100644 index 000000000..8c97686fb --- /dev/null +++ b/vendor/sabre/http/.gitignore @@ -0,0 +1,15 @@ +# Composer +vendor/ +composer.lock + +# Tests +tests/cov/ + +# Composer binaries +bin/phpunit +bin/phpcs +bin/php-cs-fixer +bin/sabre-cs-fixer + +# Vim +.*.swp diff --git a/vendor/sabre/http/.travis.yml b/vendor/sabre/http/.travis.yml new file mode 100644 index 000000000..490e42e1e --- /dev/null +++ b/vendor/sabre/http/.travis.yml @@ -0,0 +1,24 @@ +language: php +php: + - 5.4 + - 5.5 + - 5.6 + - 7 + - hhvm + +matrix: + fast_finish: true + +env: + matrix: + - PREFER_LOWEST="" + - PREFER_LOWEST="--prefer-lowest" + + +before_script: + - composer self-update + - composer update --prefer-source $PREFER_LOWEST + +script: + - ./bin/phpunit --configuration tests/phpunit.xml + - ./bin/sabre-cs-fixer fix . --dry-run --diff diff --git a/vendor/sabre/http/CHANGELOG.md b/vendor/sabre/http/CHANGELOG.md new file mode 100644 index 000000000..9a751d8fb --- /dev/null +++ b/vendor/sabre/http/CHANGELOG.md @@ -0,0 +1,250 @@ +ChangeLog +========= + +4.2.1 (2016-01-06) +------------------ + +* #56: `getBodyAsString` now returns at most as many bytes as the contents of + the `Content-Length` header. This allows users to pass much larger strings + without having to copy and truncate them. + + +4.2.0 (2016-01-04) +------------------ + +* This package now supports sabre/event 3.0. +* The client now sets a default `User-Agent` header identifying this library. + + +4.1.0 (2015-09-04) +------------------ + +* The async client wouldn't `wait()` for new http requests being started + after the (previous) last request in the queue was resolved. +* Added `Sabre\HTTP\Auth\Bearer`, to easily extract a OAuth2 bearer token. + + +4.0.0 (2015-05-20) +------------------ + +* Deprecated: All static functions from `Sabre\HTTP\URLUtil` and + `Sabre\HTTP\Util` moved to a separate `functions.php`, which is also + autoloaded. The old functions are still there, but will be removed in a + future version. (#49) + + +4.0.0-alpha3 (2015-05-19) +------------------------- + +* Added a parser for the HTTP `Prefer` header, as defined in [RFC7240][rfc7240]. +* Deprecated `Sabre\HTTP\Util::parseHTTPDate`, use `Sabre\HTTP\parseDate()`. +* Deprecated `Sabre\HTTP\Util::toHTTPDate` use `Sabre\HTTP\toDate()`. + + +4.0.0-alpha2 (2015-05-18) +------------------------- + +* #45: Don't send more data than what is promised in the HTTP content-length. + (@dratini0). +* #43: `getCredentials` returns null if incomplete. (@Hywan) +* #48: Now using php-cs-fixer to make our CS consistent (yay!) +* This includes fixes released in version 3.0.5. + + +4.0.0-alpha1 (2015-02-25) +------------------------- + +* #41: Fixing bugs related to comparing URLs in `Request::getPath()`. +* #41: This library now uses the `sabre/uri` package for uri handling. +* Added `421 Misdirected Request` from the HTTP/2.0 spec. + + +3.0.5 (2015-05-11) +------------------ + +* #47 #35: When re-using the client and doing any request after a `HEAD` + request, the client discards the body. + + +3.0.4 (2014-12-10) +------------------ + +* #38: The Authentication helpers no longer overwrite any existing + `WWW-Authenticate` headers, but instead append new headers. This ensures + that multiple authentication systems can exist in the same environment. + + +3.0.3 (2014-12-03) +------------------ + +* Hiding `Authorization` header value from `Request::__toString`. + + +3.0.2 (2014-10-09) +------------------ + +* When parsing `Accept:` headers, we're ignoring invalid parts. Before we + would throw a PHP E_NOTICE. + + +3.0.1 (2014-09-29) +------------------ + +* Minor change in unittests. + + +3.0.0 (2014-09-23) +------------------ + +* `getHeaders()` now returns header values as an array, just like psr/http. +* Added `hasHeader()`. + + +2.1.0-alpha1 (2014-09-15) +------------------------- + +* Changed: Copied most of the header-semantics for the PSR draft for + representing HTTP messages. [Reference here][psr-http]. +* This means that `setHeaders()` does not wipe out every existing header + anymore. +* We also support multiple headers with the same name. +* Use `Request::getHeaderAsArray()` and `Response::getHeaderAsArray()` to + get a hold off multiple headers with the same name. +* If you use `getHeader()`, and there's more than 1 header with that name, we + concatenate all these with a comma. +* `addHeader()` will now preserve an existing header with that name, and add a + second header with the same name. +* The message class should be a lot faster now for looking up headers. No more + array traversal, because we maintain a tiny index. +* Added: `URLUtil::resolve()` to make resolving relative urls super easy. +* Switched to PSR-4. +* #12: Circumventing CURL's FOLLOW_LOCATION and doing it in PHP instead. This + fixes compatibility issues with people that have open_basedir turned on. +* Added: Content negotiation now correctly support mime-type parameters such as + charset. +* Changed: `Util::negotiate()` is now deprecated. Use + `Util::negotiateContentType()` instead. +* #14: The client now only follows http and https urls. + + +2.0.4 (2014-07-14) +------------------ + +* Changed: No longer escaping @ in urls when it's not needed. +* Fixed: #7: Client now correctly deals with responses without a body. + + +2.0.3 (2014-04-17) +------------------ + +* Now works on hhvm! +* Fixed: Now throwing an error when a Request object is being created with + arguments that were valid for sabre/http 1.0. Hopefully this will aid with + debugging for upgraders. + + +2.0.2 (2014-02-09) +------------------ + +* Fixed: Potential security problem in the client. + + +2.0.1 (2014-01-09) +------------------ + +* Fixed: getBodyAsString on an empty body now works. +* Fixed: Version string + + +2.0.0 (2014-01-08) +------------------ + +* Removed: Request::createFromPHPRequest. This is now handled by + Sapi::getRequest. + + +2.0.0alpha6 (2014-01-03) +------------------------ + +* Added: Asynchronous HTTP client. See examples/asyncclient.php. +* Fixed: Issue #4: Don't escape colon (:) when it's not needed. +* Fixed: Fixed a bug in the content negotation script. +* Fixed: Fallback for when CURLOPT_POSTREDIR is not defined (mainly for hhvm). +* Added: The Request and Response object now have a `__toString()` method that + serializes the objects into a standard HTTP message. This is mainly for + debugging purposes. +* Changed: Added Response::getStatusText(). This method returns the + human-readable HTTP status message. This part has been removed from + Response::getStatus(), which now always returns just the status code as an + int. +* Changed: Response::send() is now Sapi::sendResponse($response). +* Changed: Request::createFromPHPRequest is now Sapi::getRequest(). +* Changed: Message::getBodyAsStream and Message::getBodyAsString were added. The + existing Message::getBody changed it's behavior, so be careful. + + +2.0.0alpha5 (2013-11-07) +------------------------ + +* Added: HTTP Status 451 Unavailable For Legal Reasons. Fight government + censorship! +* Added: Ability to catch and retry http requests in the client when a curl + error occurs. +* Changed: Request::getPath does not return the query part of the url, so + everything after the ? is stripped. +* Added: a reverse proxy example. + + +2.0.0alpha4 (2013-08-07) +------------------------ + +* Fixed: Doing a GET request with the client uses the last used HTTP method + instead. +* Added: HttpException +* Added: The Client class can now automatically emit exceptions when HTTP errors + occurred. + + +2.0.0alpha3 (2013-07-24) +------------------------ + +* Changed: Now depends on sabre/event package. +* Changed: setHeaders() now overwrites any existing http headers. +* Added: getQueryParameters to RequestInterface. +* Added: Util::negotiate. +* Added: RequestDecorator, ResponseDecorator. +* Added: A very simple HTTP client. +* Added: addHeaders() to append a list of new headers. +* Fixed: Not erroring on unknown HTTP status codes. +* Fixed: Throwing exceptions on invalid HTTP status codes (not 3 digits). +* Fixed: Much better README.md +* Changed: getBody() now uses a bitfield to specify what type to return. + + +2.0.0alpha2 (2013-07-02) +------------------------ + +* Added: Digest & AWS Authentication. +* Added: Message::getHttpVersion and Message::setHttpVersion. +* Added: Request::setRawServerArray, getRawServerValue. +* Added: Request::createFromPHPRequest +* Added: Response::send +* Added: Request::getQueryParameters +* Added: Utility for dealing with HTTP dates. +* Added: Request::setPostData and Request::getPostData. +* Added: Request::setAbsoluteUrl and Request::getAbsoluteUrl. +* Added: URLUtil, methods for calculation relative and base urls. +* Removed: Response::sendBody + + +2.0.0alpha1 (2012-10-07) +------------------------ + +* Fixed: Lots of small naming improvements +* Added: Introduction of Message, MessageInterface, Response, ResponseInterface. + +Before 2.0.0, this package was built-into SabreDAV, where it first appeared in +January 2009. + +[psr-http]: https://github.com/php-fig/fig-standards/blob/master/proposed/http-message.md +[rfc-7240]: http://tools.ietf.org/html/rfc7240 diff --git a/vendor/sabre/http/LICENSE b/vendor/sabre/http/LICENSE new file mode 100644 index 000000000..19812ad7a --- /dev/null +++ b/vendor/sabre/http/LICENSE @@ -0,0 +1,27 @@ +Copyright (C) 2009-2016 fruux GmbH (https://fruux.com/) + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name Sabre nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/sabre/http/README.md b/vendor/sabre/http/README.md new file mode 100644 index 000000000..ae03a796e --- /dev/null +++ b/vendor/sabre/http/README.md @@ -0,0 +1,746 @@ +sabre/http +========== + +This library provides a toolkit to make working with the HTTP protocol easier. + +Most PHP scripts run within a HTTP request but accessing information about the +HTTP request is cumbersome at least. + +There's bad practices, inconsistencies and confusion. This library is +effectively a wrapper around the following PHP constructs: + +For Input: + +* `$_GET`, +* `$_POST`, +* `$_SERVER`, +* `php://input` or `$HTTP_RAW_POST_DATA`. + +For output: + +* `php://output` or `echo`, +* `header()`. + +What this library provides, is a `Request` object, and a `Response` object. + +The objects are extendable and easily mockable. + +Build status +------------ + +| branch | status | +| ------ | ------ | +| master | [![Build Status](https://travis-ci.org/fruux/sabre-http.svg?branch=master)](https://travis-ci.org/fruux/sabre-http) | +| 3.0 | [![Build Status](https://travis-ci.org/fruux/sabre-http.svg?branch=3.0)](https://travis-ci.org/fruux/sabre-http) | + +Installation +------------ + +Make sure you have [composer][1] installed. In your project directory, create, +or edit a `composer.json` file, and make sure it contains something like this: + +```json +{ + "require" : { + "sabre/http" : "~3.0.0" + } +} +``` + +After that, just hit `composer install` and you should be rolling. + +Quick history +------------- + +This library came to existence in 2009, as a part of the [`sabre/dav`][2] +project, which uses it heavily. + +It got split off into a separate library to make it easier to manage +releases and hopefully giving it use outside of the scope of just `sabre/dav`. + +Although completely independently developed, this library has a LOT of +overlap with [Symfony's `HttpFoundation`][3]. + +Said library does a lot more stuff and is significantly more popular, +so if you are looking for something to fulfill this particular requirement, +I'd recommend also considering [`HttpFoundation`][3]. + + +Getting started +--------------- + +First and foremost, this library wraps the superglobals. The easiest way to +instantiate a request object is as follows: + +```php +use Sabre\HTTP; + +include 'vendor/autoload.php'; + +$request = HTTP\Sapi::getRequest(); +``` + +This line should only happen once in your entire application. Everywhere else +you should pass this request object around using dependency injection. + +You should always typehint on it's interface: + +```php +function handleRequest(HTTP\RequestInterface $request) { + + // Do something with this request :) + +} +``` + +A response object you can just create as such: + +```php +use Sabre\HTTP; + +include 'vendor/autoload.php'; + +$response = new HTTP\Response(); +$response->setStatus(201); // created ! +$response->setHeader('X-Foo', 'bar'); +$response->setBody( + 'success!' +); + +``` + +After you fully constructed your response, you must call: + +```php +HTTP\Sapi::sendResponse($response); +``` + +This line should generally also appear once in your application (at the very +end). + +Decorators +---------- + +It may be useful to extend the `Request` and `Response` objects in your +application, if you for example would like them to carry a bit more +information about the current request. + +For instance, you may want to add an `isLoggedIn` method to the Request +object. + +Simply extending Request and Response may pose some problems: + +1. You may want to extend the objects with new behaviors differently, in + different subsystems of your application, +2. The `Sapi::getRequest` factory always returns a instance of + `Request` so you would have to override the factory method as well, +3. By controlling the instantation and depend on specific `Request` and + `Response` instances in your library or application, you make it harder to + work with other applications which also use `sabre/http`. + +In short: it would be bad design. Instead, it's recommended to use the +[decorator pattern][6] to add new behavior where you need it. `sabre/http` +provides helper classes to quickly do this. + +Example: + +```php +use Sabre\HTTP; + +class MyRequest extends HTTP\RequestDecorator { + + function isLoggedIn() { + + return true; + + } + +} +``` + +Our application assumes that the true `Request` object was instantiated +somewhere else, by some other subsystem. This could simply be a call like +`$request = Sapi::getRequest()` at the top of your application, +but could also be somewhere in a unittest. + +All we know in the current subsystem, is that we received a `$request` and +that it implements `Sabre\HTTP\RequestInterface`. To decorate this object, +all we need to do is: + +```php +$request = new MyRequest($request); +``` + +And that's it, we now have an `isLoggedIn` method, without having to mess +with the core instances. + + +Client +------ + +This package also contains a simple wrapper around [cURL][4], which will allow +you to write simple clients, using the `Request` and `Response` objects you're +already familiar with. + +It's by no means a replacement for something like [Guzzle][7], but it provides +a simple and lightweight API for making the occasional API call. + +### Usage + +```php +use Sabre\HTTP; + +$request = new HTTP\Request('GET', 'http://example.org/'); +$request->setHeader('X-Foo', 'Bar'); + +$client = new HTTP\Client(); +$response = $client->send($request); + +echo $response->getBodyAsString(); +``` + +The client emits 3 event using [`sabre/event`][5]. `beforeRequest`, +`afterRequest` and `error`. + +```php +$client = new HTTP\Client(); +$client->on('beforeRequest', function($request) { + + // You could use beforeRequest to for example inject a few extra headers. + // into the Request object. + +}); + +$client->on('afterRequest', function($request, $response) { + + // The afterRequest event could be a good time to do some logging, or + // do some rewriting in the response. + +}); + +$client->on('error', function($request, $response, &$retry, $retryCount) { + + // The error event is triggered for every response with a HTTP code higher + // than 399. + +}); + +$client->on('error:401', function($request, $response, &$retry, $retryCount) { + + // You can also listen for specific error codes. This example shows how + // to inject HTTP authentication headers if a 401 was returned. + + if ($retryCount > 1) { + // We're only going to retry exactly once. + } + + $request->setHeader('Authorization', 'Basic xxxxxxxxxx'); + $retry = true; + +}); +``` + +### Asynchronous requests + +The `Client` also supports doing asynchronous requests. This is especially handy +if you need to perform a number of requests, that are allowed to be executed +in parallel. + +The underlying system for this is simply [cURL's multi request handler][8], +but this provides a much nicer API to handle this. + +Sample usage: + +```php + +use Sabre\HTTP; + +$request = new Request('GET', 'http://localhost/'); +$client = new Client(); + +// Executing 1000 requests +for ($i = 0; $i < 1000; $i++) { + $client->sendAsync( + $request, + function(ResponseInterface $response) { + // Success handler + }, + function($error) { + // Error handler + } + ); +} + +// Wait for all requests to get a result. +$client->wait(); + +``` + +Check out `examples/asyncclient.php` for more information. + +Writing a reverse proxy +----------------------- + +With all these tools combined, it becomes very easy to write a simple reverse +http proxy. + +```php +use + Sabre\HTTP\Sapi, + Sabre\HTTP\Client; + +// The url we're proxying to. +$remoteUrl = 'http://example.org/'; + +// The url we're proxying from. Please note that this must be a relative url, +// and basically acts as the base url. +// +// If youre $remoteUrl doesn't end with a slash, this one probably shouldn't +// either. +$myBaseUrl = '/reverseproxy.php'; +// $myBaseUrl = '/~evert/sabre/http/examples/reverseproxy.php/'; + +$request = Sapi::getRequest(); +$request->setBaseUrl($myBaseUrl); + +$subRequest = clone $request; + +// Removing the Host header. +$subRequest->removeHeader('Host'); + +// Rewriting the url. +$subRequest->setUrl($remoteUrl . $request->getPath()); + +$client = new Client(); + +// Sends the HTTP request to the server +$response = $client->send($subRequest); + +// Sends the response back to the client that connected to the proxy. +Sapi::sendResponse($response); +``` + +The Request and Response API's +------------------------------ + +### Request + +```php + +/** + * Creates the request object + * + * @param string $method + * @param string $url + * @param array $headers + * @param resource $body + */ +public function __construct($method = null, $url = null, array $headers = null, $body = null); + +/** + * Returns the current HTTP method + * + * @return string + */ +function getMethod(); + +/** + * Sets the HTTP method + * + * @param string $method + * @return void + */ +function setMethod($method); + +/** + * Returns the request url. + * + * @return string + */ +function getUrl(); + +/** + * Sets the request url. + * + * @param string $url + * @return void + */ +function setUrl($url); + +/** + * Returns the absolute url. + * + * @return string + */ +function getAbsoluteUrl(); + +/** + * Sets the absolute url. + * + * @param string $url + * @return void + */ +function setAbsoluteUrl($url); + +/** + * Returns the current base url. + * + * @return string + */ +function getBaseUrl(); + +/** + * Sets a base url. + * + * This url is used for relative path calculations. + * + * The base url should default to / + * + * @param string $url + * @return void + */ +function setBaseUrl($url); + +/** + * Returns the relative path. + * + * This is being calculated using the base url. This path will not start + * with a slash, so it will always return something like + * 'example/path.html'. + * + * If the full path is equal to the base url, this method will return an + * empty string. + * + * This method will also urldecode the path, and if the url was incoded as + * ISO-8859-1, it will convert it to UTF-8. + * + * If the path is outside of the base url, a LogicException will be thrown. + * + * @return string + */ +function getPath(); + +/** + * Returns the list of query parameters. + * + * This is equivalent to PHP's $_GET superglobal. + * + * @return array + */ +function getQueryParameters(); + +/** + * Returns the POST data. + * + * This is equivalent to PHP's $_POST superglobal. + * + * @return array + */ +function getPostData(); + +/** + * Sets the post data. + * + * This is equivalent to PHP's $_POST superglobal. + * + * This would not have been needed, if POST data was accessible as + * php://input, but unfortunately we need to special case it. + * + * @param array $postData + * @return void + */ +function setPostData(array $postData); + +/** + * Returns an item from the _SERVER array. + * + * If the value does not exist in the array, null is returned. + * + * @param string $valueName + * @return string|null + */ +function getRawServerValue($valueName); + +/** + * Sets the _SERVER array. + * + * @param array $data + * @return void + */ +function setRawServerData(array $data); + +/** + * Returns the body as a readable stream resource. + * + * Note that the stream may not be rewindable, and therefore may only be + * read once. + * + * @return resource + */ +function getBodyAsStream(); + +/** + * Returns the body as a string. + * + * Note that because the underlying data may be based on a stream, this + * method could only work correctly the first time. + * + * @return string + */ +function getBodyAsString(); + +/** + * Returns the message body, as it's internal representation. + * + * This could be either a string or a stream. + * + * @return resource|string + */ +function getBody(); + +/** + * Updates the body resource with a new stream. + * + * @param resource $body + * @return void + */ +function setBody($body); + +/** + * Returns all the HTTP headers as an array. + * + * @return array + */ +function getHeaders(); + +/** + * Returns a specific HTTP header, based on it's name. + * + * The name must be treated as case-insensitive. + * + * If the header does not exist, this method must return null. + * + * @param string $name + * @return string|null + */ +function getHeader($name); + +/** + * Updates a HTTP header. + * + * The case-sensitity of the name value must be retained as-is. + * + * @param string $name + * @param string $value + * @return void + */ +function setHeader($name, $value); + +/** + * Resets HTTP headers + * + * This method overwrites all existing HTTP headers + * + * @param array $headers + * @return void + */ +function setHeaders(array $headers); + +/** + * Adds a new set of HTTP headers. + * + * Any header specified in the array that already exists will be + * overwritten, but any other existing headers will be retained. + * + * @param array $headers + * @return void + */ +function addHeaders(array $headers); + +/** + * Removes a HTTP header. + * + * The specified header name must be treated as case-insenstive. + * This method should return true if the header was successfully deleted, + * and false if the header did not exist. + * + * @return bool + */ +function removeHeader($name); + +/** + * Sets the HTTP version. + * + * Should be 1.0 or 1.1. + * + * @param string $version + * @return void + */ +function setHttpVersion($version); + +/** + * Returns the HTTP version. + * + * @return string + */ +function getHttpVersion(); +``` + +### Response + +```php +/** + * Returns the current HTTP status. + * + * This is the status-code as well as the human readable string. + * + * @return string + */ +function getStatus(); + +/** + * Sets the HTTP status code. + * + * This can be either the full HTTP status code with human readable string, + * for example: "403 I can't let you do that, Dave". + * + * Or just the code, in which case the appropriate default message will be + * added. + * + * @param string|int $status + * @throws \InvalidArgumentExeption + * @return void + */ +function setStatus($status); + +/** + * Returns the body as a readable stream resource. + * + * Note that the stream may not be rewindable, and therefore may only be + * read once. + * + * @return resource + */ +function getBodyAsStream(); + +/** + * Returns the body as a string. + * + * Note that because the underlying data may be based on a stream, this + * method could only work correctly the first time. + * + * @return string + */ +function getBodyAsString(); + +/** + * Returns the message body, as it's internal representation. + * + * This could be either a string or a stream. + * + * @return resource|string + */ +function getBody(); + + +/** + * Updates the body resource with a new stream. + * + * @param resource $body + * @return void + */ +function setBody($body); + +/** + * Returns all the HTTP headers as an array. + * + * @return array + */ +function getHeaders(); + +/** + * Returns a specific HTTP header, based on it's name. + * + * The name must be treated as case-insensitive. + * + * If the header does not exist, this method must return null. + * + * @param string $name + * @return string|null + */ +function getHeader($name); + +/** + * Updates a HTTP header. + * + * The case-sensitity of the name value must be retained as-is. + * + * @param string $name + * @param string $value + * @return void + */ +function setHeader($name, $value); + +/** + * Resets HTTP headers + * + * This method overwrites all existing HTTP headers + * + * @param array $headers + * @return void + */ +function setHeaders(array $headers); + +/** + * Adds a new set of HTTP headers. + * + * Any header specified in the array that already exists will be + * overwritten, but any other existing headers will be retained. + * + * @param array $headers + * @return void + */ +function addHeaders(array $headers); + +/** + * Removes a HTTP header. + * + * The specified header name must be treated as case-insenstive. + * This method should return true if the header was successfully deleted, + * and false if the header did not exist. + * + * @return bool + */ +function removeHeader($name); + +/** + * Sets the HTTP version. + * + * Should be 1.0 or 1.1. + * + * @param string $version + * @return void + */ +function setHttpVersion($version); + +/** + * Returns the HTTP version. + * + * @return string + */ +function getHttpVersion(); +``` + +Made at fruux +------------- + +This library is being developed by [fruux](https://fruux.com/). Drop us a line for commercial services or enterprise support. + +[1]: http://getcomposer.org/ +[2]: http://sabre.io/ +[3]: https://github.com/symfony/HttpFoundation +[4]: http://php.net/curl +[5]: https://github.com/fruux/sabre-event +[6]: http://en.wikipedia.org/wiki/Decorator_pattern +[7]: http://guzzlephp.org/ +[8]: http://php.net/curl_multi_init diff --git a/vendor/sabre/http/bin/.empty b/vendor/sabre/http/bin/.empty new file mode 100644 index 000000000..e69de29bb diff --git a/vendor/sabre/http/lib/Auth/AWS.php b/vendor/sabre/http/lib/Auth/AWS.php new file mode 100644 index 000000000..5e176646a --- /dev/null +++ b/vendor/sabre/http/lib/Auth/AWS.php @@ -0,0 +1,234 @@ +request->getHeader('Authorization'); + $authHeader = explode(' ', $authHeader); + + if ($authHeader[0] != 'AWS' || !isset($authHeader[1])) { + $this->errorCode = self::ERR_NOAWSHEADER; + return false; + } + + list($this->accessKey, $this->signature) = explode(':', $authHeader[1]); + + return true; + + } + + /** + * Returns the username for the request + * + * @return string + */ + function getAccessKey() { + + return $this->accessKey; + + } + + /** + * Validates the signature based on the secretKey + * + * @param string $secretKey + * @return bool + */ + function validate($secretKey) { + + $contentMD5 = $this->request->getHeader('Content-MD5'); + + if ($contentMD5) { + // We need to validate the integrity of the request + $body = $this->request->getBody(); + $this->request->setBody($body); + + if ($contentMD5 != base64_encode(md5($body, true))) { + // content-md5 header did not match md5 signature of body + $this->errorCode = self::ERR_MD5CHECKSUMWRONG; + return false; + } + + } + + if (!$requestDate = $this->request->getHeader('x-amz-date')) + $requestDate = $this->request->getHeader('Date'); + + if (!$this->validateRFC2616Date($requestDate)) + return false; + + $amzHeaders = $this->getAmzHeaders(); + + $signature = base64_encode( + $this->hmacsha1($secretKey, + $this->request->getMethod() . "\n" . + $contentMD5 . "\n" . + $this->request->getHeader('Content-type') . "\n" . + $requestDate . "\n" . + $amzHeaders . + $this->request->getUrl() + ) + ); + + if ($this->signature != $signature) { + + $this->errorCode = self::ERR_INVALIDSIGNATURE; + return false; + + } + + return true; + + } + + + /** + * Returns an HTTP 401 header, forcing login + * + * This should be called when username and password are incorrect, or not supplied at all + * + * @return void + */ + function requireLogin() { + + $this->response->addHeader('WWW-Authenticate', 'AWS'); + $this->response->setStatus(401); + + } + + /** + * Makes sure the supplied value is a valid RFC2616 date. + * + * If we would just use strtotime to get a valid timestamp, we have no way of checking if a + * user just supplied the word 'now' for the date header. + * + * This function also makes sure the Date header is within 15 minutes of the operating + * system date, to prevent replay attacks. + * + * @param string $dateHeader + * @return bool + */ + protected function validateRFC2616Date($dateHeader) { + + $date = Util::parseHTTPDate($dateHeader); + + // Unknown format + if (!$date) { + $this->errorCode = self::ERR_INVALIDDATEFORMAT; + return false; + } + + $min = new \DateTime('-15 minutes'); + $max = new \DateTime('+15 minutes'); + + // We allow 15 minutes around the current date/time + if ($date > $max || $date < $min) { + $this->errorCode = self::ERR_REQUESTTIMESKEWED; + return false; + } + + return $date; + + } + + /** + * Returns a list of AMZ headers + * + * @return string + */ + protected function getAmzHeaders() { + + $amzHeaders = []; + $headers = $this->request->getHeaders(); + foreach ($headers as $headerName => $headerValue) { + if (strpos(strtolower($headerName), 'x-amz-') === 0) { + $amzHeaders[strtolower($headerName)] = str_replace(["\r\n"], [' '], $headerValue[0]) . "\n"; + } + } + ksort($amzHeaders); + + $headerStr = ''; + foreach ($amzHeaders as $h => $v) { + $headerStr .= $h . ':' . $v; + } + + return $headerStr; + + } + + /** + * Generates an HMAC-SHA1 signature + * + * @param string $key + * @param string $message + * @return string + */ + private function hmacsha1($key, $message) { + + if (function_exists('hash_hmac')) { + return hash_hmac('sha1', $message, $key, true); + } + + $blocksize = 64; + if (strlen($key) > $blocksize) { + $key = pack('H*', sha1($key)); + } + $key = str_pad($key, $blocksize, chr(0x00)); + $ipad = str_repeat(chr(0x36), $blocksize); + $opad = str_repeat(chr(0x5c), $blocksize); + $hmac = pack('H*', sha1(($key ^ $opad) . pack('H*', sha1(($key ^ $ipad) . $message)))); + return $hmac; + + } + +} diff --git a/vendor/sabre/http/lib/Auth/AbstractAuth.php b/vendor/sabre/http/lib/Auth/AbstractAuth.php new file mode 100644 index 000000000..ae45b3ee2 --- /dev/null +++ b/vendor/sabre/http/lib/Auth/AbstractAuth.php @@ -0,0 +1,73 @@ +realm = $realm; + $this->request = $request; + $this->response = $response; + + } + + /** + * This method sends the needed HTTP header and statuscode (401) to force + * the user to login. + * + * @return void + */ + abstract function requireLogin(); + + /** + * Returns the HTTP realm + * + * @return string + */ + function getRealm() { + + return $this->realm; + + } + +} diff --git a/vendor/sabre/http/lib/Auth/Basic.php b/vendor/sabre/http/lib/Auth/Basic.php new file mode 100644 index 000000000..60633b957 --- /dev/null +++ b/vendor/sabre/http/lib/Auth/Basic.php @@ -0,0 +1,63 @@ +request->getHeader('Authorization'); + + if (!$auth) { + return null; + } + + if (strtolower(substr($auth, 0, 6)) !== 'basic ') { + return null; + } + + $credentials = explode(':', base64_decode(substr($auth, 6)), 2); + + if (2 !== count($credentials)) { + return null; + } + + return $credentials; + + } + + /** + * This method sends the needed HTTP header and statuscode (401) to force + * the user to login. + * + * @return void + */ + function requireLogin() { + + $this->response->addHeader('WWW-Authenticate', 'Basic realm="' . $this->realm . '"'); + $this->response->setStatus(401); + + } + +} diff --git a/vendor/sabre/http/lib/Auth/Bearer.php b/vendor/sabre/http/lib/Auth/Bearer.php new file mode 100644 index 000000000..eefdf11ee --- /dev/null +++ b/vendor/sabre/http/lib/Auth/Bearer.php @@ -0,0 +1,56 @@ +request->getHeader('Authorization'); + + if (!$auth) { + return null; + } + + if (strtolower(substr($auth, 0, 7)) !== 'bearer ') { + return null; + } + + return substr($auth, 7); + + } + + /** + * This method sends the needed HTTP header and statuscode (401) to force + * authentication. + * + * @return void + */ + function requireLogin() { + + $this->response->addHeader('WWW-Authenticate', 'Bearer realm="' . $this->realm . '"'); + $this->response->setStatus(401); + + } + +} diff --git a/vendor/sabre/http/lib/Auth/Digest.php b/vendor/sabre/http/lib/Auth/Digest.php new file mode 100644 index 000000000..4b3f0746f --- /dev/null +++ b/vendor/sabre/http/lib/Auth/Digest.php @@ -0,0 +1,231 @@ +nonce = uniqid(); + $this->opaque = md5($realm); + parent::__construct($realm, $request, $response); + + } + + /** + * Gathers all information from the headers + * + * This method needs to be called prior to anything else. + * + * @return void + */ + function init() { + + $digest = $this->getDigest(); + $this->digestParts = $this->parseDigest($digest); + + } + + /** + * Sets the quality of protection value. + * + * Possible values are: + * Sabre\HTTP\DigestAuth::QOP_AUTH + * Sabre\HTTP\DigestAuth::QOP_AUTHINT + * + * Multiple values can be specified using logical OR. + * + * QOP_AUTHINT ensures integrity of the request body, but this is not + * supported by most HTTP clients. QOP_AUTHINT also requires the entire + * request body to be md5'ed, which can put strains on CPU and memory. + * + * @param int $qop + * @return void + */ + function setQOP($qop) { + + $this->qop = $qop; + + } + + /** + * Validates the user. + * + * The A1 parameter should be md5($username . ':' . $realm . ':' . $password); + * + * @param string $A1 + * @return bool + */ + function validateA1($A1) { + + $this->A1 = $A1; + return $this->validate(); + + } + + /** + * Validates authentication through a password. The actual password must be provided here. + * It is strongly recommended not store the password in plain-text and use validateA1 instead. + * + * @param string $password + * @return bool + */ + function validatePassword($password) { + + $this->A1 = md5($this->digestParts['username'] . ':' . $this->realm . ':' . $password); + return $this->validate(); + + } + + /** + * Returns the username for the request + * + * @return string + */ + function getUsername() { + + return $this->digestParts['username']; + + } + + /** + * Validates the digest challenge + * + * @return bool + */ + protected function validate() { + + $A2 = $this->request->getMethod() . ':' . $this->digestParts['uri']; + + if ($this->digestParts['qop'] == 'auth-int') { + // Making sure we support this qop value + if (!($this->qop & self::QOP_AUTHINT)) return false; + // We need to add an md5 of the entire request body to the A2 part of the hash + $body = $this->request->getBody($asString = true); + $this->request->setBody($body); + $A2 .= ':' . md5($body); + } else { + + // We need to make sure we support this qop value + if (!($this->qop & self::QOP_AUTH)) return false; + } + + $A2 = md5($A2); + + $validResponse = md5("{$this->A1}:{$this->digestParts['nonce']}:{$this->digestParts['nc']}:{$this->digestParts['cnonce']}:{$this->digestParts['qop']}:{$A2}"); + + return $this->digestParts['response'] == $validResponse; + + + } + + /** + * Returns an HTTP 401 header, forcing login + * + * This should be called when username and password are incorrect, or not supplied at all + * + * @return void + */ + function requireLogin() { + + $qop = ''; + switch ($this->qop) { + case self::QOP_AUTH : + $qop = 'auth'; + break; + case self::QOP_AUTHINT : + $qop = 'auth-int'; + break; + case self::QOP_AUTH | self::QOP_AUTHINT : + $qop = 'auth,auth-int'; + break; + } + + $this->response->addHeader('WWW-Authenticate', 'Digest realm="' . $this->realm . '",qop="' . $qop . '",nonce="' . $this->nonce . '",opaque="' . $this->opaque . '"'); + $this->response->setStatus(401); + + } + + + /** + * This method returns the full digest string. + * + * It should be compatibile with mod_php format and other webservers. + * + * If the header could not be found, null will be returned + * + * @return mixed + */ + function getDigest() { + + return $this->request->getHeader('Authorization'); + + } + + + /** + * Parses the different pieces of the digest string into an array. + * + * This method returns false if an incomplete digest was supplied + * + * @param string $digest + * @return mixed + */ + protected function parseDigest($digest) { + + // protect against missing data + $needed_parts = ['nonce' => 1, 'nc' => 1, 'cnonce' => 1, 'qop' => 1, 'username' => 1, 'uri' => 1, 'response' => 1]; + $data = []; + + preg_match_all('@(\w+)=(?:(?:")([^"]+)"|([^\s,$]+))@', $digest, $matches, PREG_SET_ORDER); + + foreach ($matches as $m) { + $data[$m[1]] = $m[2] ? $m[2] : $m[3]; + unset($needed_parts[$m[1]]); + } + + return $needed_parts ? false : $data; + + } + +} diff --git a/vendor/sabre/http/lib/Client.php b/vendor/sabre/http/lib/Client.php new file mode 100644 index 000000000..0810c4a25 --- /dev/null +++ b/vendor/sabre/http/lib/Client.php @@ -0,0 +1,601 @@ +curlSettings = [ + CURLOPT_RETURNTRANSFER => true, + CURLOPT_HEADER => true, + CURLOPT_NOBODY => false, + CURLOPT_USERAGENT => 'sabre-http/' . Version::VERSION . ' (http://sabre.io/)', + ]; + + } + + /** + * Sends a request to a HTTP server, and returns a response. + * + * @param RequestInterface $request + * @return ResponseInterface + */ + function send(RequestInterface $request) { + + $this->emit('beforeRequest', [$request]); + + $retryCount = 0; + $redirects = 0; + + do { + + $doRedirect = false; + $retry = false; + + try { + + $response = $this->doRequest($request); + + $code = (int)$response->getStatus(); + + // We are doing in-PHP redirects, because curl's + // FOLLOW_LOCATION throws errors when PHP is configured with + // open_basedir. + // + // https://github.com/fruux/sabre-http/issues/12 + if (in_array($code, [301, 302, 307, 308]) && $redirects < $this->maxRedirects) { + + $oldLocation = $request->getUrl(); + + // Creating a new instance of the request object. + $request = clone $request; + + // Setting the new location + $request->setUrl(Uri\resolve( + $oldLocation, + $response->getHeader('Location') + )); + + $doRedirect = true; + $redirects++; + + } + + // This was a HTTP error + if ($code >= 400) { + + $this->emit('error', [$request, $response, &$retry, $retryCount]); + $this->emit('error:' . $code, [$request, $response, &$retry, $retryCount]); + + } + + } catch (ClientException $e) { + + $this->emit('exception', [$request, $e, &$retry, $retryCount]); + + // If retry was still set to false, it means no event handler + // dealt with the problem. In this case we just re-throw the + // exception. + if (!$retry) { + throw $e; + } + + } + + if ($retry) { + $retryCount++; + } + + } while ($retry || $doRedirect); + + $this->emit('afterRequest', [$request, $response]); + + if ($this->throwExceptions && $code >= 400) { + throw new ClientHttpException($response); + } + + return $response; + + } + + /** + * Sends a HTTP request asynchronously. + * + * Due to the nature of PHP, you must from time to time poll to see if any + * new responses came in. + * + * After calling sendAsync, you must therefore occasionally call the poll() + * method, or wait(). + * + * @param RequestInterface $request + * @param callable $success + * @param callable $error + * @return void + */ + function sendAsync(RequestInterface $request, callable $success = null, callable $error = null) { + + $this->emit('beforeRequest', [$request]); + $this->sendAsyncInternal($request, $success, $error); + $this->poll(); + + } + + + /** + * This method checks if any http requests have gotten results, and if so, + * call the appropriate success or error handlers. + * + * This method will return true if there are still requests waiting to + * return, and false if all the work is done. + * + * @return bool + */ + function poll() { + + // nothing to do? + if (!$this->curlMultiMap) { + return false; + } + + do { + $r = curl_multi_exec( + $this->curlMultiHandle, + $stillRunning + ); + } while ($r === CURLM_CALL_MULTI_PERFORM); + + do { + + messageQueue: + + $status = curl_multi_info_read( + $this->curlMultiHandle, + $messagesInQueue + ); + + if ($status && $status['msg'] === CURLMSG_DONE) { + + $resourceId = intval($status['handle']); + list( + $request, + $successCallback, + $errorCallback, + $retryCount, + ) = $this->curlMultiMap[$resourceId]; + unset($this->curlMultiMap[$resourceId]); + $curlResult = $this->parseCurlResult(curl_multi_getcontent($status['handle']), $status['handle']); + $retry = false; + + if ($curlResult['status'] === self::STATUS_CURLERROR) { + + $e = new ClientException($curlResult['curl_errmsg'], $curlResult['curl_errno']); + $this->emit('exception', [$request, $e, &$retry, $retryCount]); + + if ($retry) { + $retryCount++; + $this->sendAsyncInternal($request, $successCallback, $errorCallback, $retryCount); + goto messageQueue; + } + + $curlResult['request'] = $request; + + if ($errorCallback) { + $errorCallback($curlResult); + } + + } elseif ($curlResult['status'] === self::STATUS_HTTPERROR) { + + $this->emit('error', [$request, $curlResult['response'], &$retry, $retryCount]); + $this->emit('error:' . $curlResult['http_code'], [$request, $curlResult['response'], &$retry, $retryCount]); + + if ($retry) { + + $retryCount++; + $this->sendAsyncInternal($request, $successCallback, $errorCallback, $retryCount); + goto messageQueue; + + } + + $curlResult['request'] = $request; + + if ($errorCallback) { + $errorCallback($curlResult); + } + + } else { + + $this->emit('afterRequest', [$request, $curlResult['response']]); + + if ($successCallback) { + $successCallback($curlResult['response']); + } + + } + } + + } while ($messagesInQueue > 0); + + return count($this->curlMultiMap) > 0; + + } + + /** + * Processes every HTTP request in the queue, and waits till they are all + * completed. + * + * @return void + */ + function wait() { + + do { + curl_multi_select($this->curlMultiHandle); + $stillRunning = $this->poll(); + } while ($stillRunning); + + } + + /** + * If this is set to true, the Client will automatically throw exceptions + * upon HTTP errors. + * + * This means that if a response came back with a status code greater than + * or equal to 400, we will throw a ClientHttpException. + * + * This only works for the send() method. Throwing exceptions for + * sendAsync() is not supported. + * + * @param bool $throwExceptions + * @return void + */ + function setThrowExceptions($throwExceptions) { + + $this->throwExceptions = $throwExceptions; + + } + + /** + * Adds a CURL setting. + * + * These settings will be included in every HTTP request. + * + * @param int $name + * @param mixed $value + * @return void + */ + function addCurlSetting($name, $value) { + + $this->curlSettings[$name] = $value; + + } + + /** + * This method is responsible for performing a single request. + * + * @param RequestInterface $request + * @return ResponseInterface + */ + protected function doRequest(RequestInterface $request) { + + $settings = $this->createCurlSettingsArray($request); + + if (!$this->curlHandle) { + $this->curlHandle = curl_init(); + } + + curl_setopt_array($this->curlHandle, $settings); + $response = $this->curlExec($this->curlHandle); + $response = $this->parseCurlResult($response, $this->curlHandle); + + if ($response['status'] === self::STATUS_CURLERROR) { + throw new ClientException($response['curl_errmsg'], $response['curl_errno']); + } + + return $response['response']; + + } + + /** + * Cached curl handle. + * + * By keeping this resource around for the lifetime of this object, things + * like persistent connections are possible. + * + * @var resource + */ + private $curlHandle; + + /** + * Handler for curl_multi requests. + * + * The first time sendAsync is used, this will be created. + * + * @var resource + */ + private $curlMultiHandle; + + /** + * Has a list of curl handles, as well as their associated success and + * error callbacks. + * + * @var array + */ + private $curlMultiMap = []; + + /** + * Turns a RequestInterface object into an array with settings that can be + * fed to curl_setopt + * + * @param RequestInterface $request + * @return array + */ + protected function createCurlSettingsArray(RequestInterface $request) { + + $settings = $this->curlSettings; + + switch ($request->getMethod()) { + case 'HEAD' : + $settings[CURLOPT_NOBODY] = true; + $settings[CURLOPT_CUSTOMREQUEST] = 'HEAD'; + $settings[CURLOPT_POSTFIELDS] = ''; + $settings[CURLOPT_PUT] = false; + break; + case 'GET' : + $settings[CURLOPT_CUSTOMREQUEST] = 'GET'; + $settings[CURLOPT_POSTFIELDS] = ''; + $settings[CURLOPT_PUT] = false; + break; + default : + $body = $request->getBody(); + if (is_resource($body)) { + // This needs to be set to PUT, regardless of the actual + // method used. Without it, INFILE will be ignored for some + // reason. + $settings[CURLOPT_PUT] = true; + $settings[CURLOPT_INFILE] = $request->getBody(); + } else { + // For security we cast this to a string. If somehow an array could + // be passed here, it would be possible for an attacker to use @ to + // post local files. + $settings[CURLOPT_POSTFIELDS] = (string)$body; + } + $settings[CURLOPT_CUSTOMREQUEST] = $request->getMethod(); + break; + + } + + $nHeaders = []; + foreach ($request->getHeaders() as $key => $values) { + + foreach ($values as $value) { + $nHeaders[] = $key . ': ' . $value; + } + + } + $settings[CURLOPT_HTTPHEADER] = $nHeaders; + $settings[CURLOPT_URL] = $request->getUrl(); + // FIXME: CURLOPT_PROTOCOLS is currently unsupported by HHVM + if (defined('CURLOPT_PROTOCOLS')) { + $settings[CURLOPT_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS; + } + // FIXME: CURLOPT_REDIR_PROTOCOLS is currently unsupported by HHVM + if (defined('CURLOPT_REDIR_PROTOCOLS')) { + $settings[CURLOPT_REDIR_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS; + } + + return $settings; + + } + + const STATUS_SUCCESS = 0; + const STATUS_CURLERROR = 1; + const STATUS_HTTPERROR = 2; + + /** + * Parses the result of a curl call in a format that's a bit more + * convenient to work with. + * + * The method returns an array with the following elements: + * * status - one of the 3 STATUS constants. + * * curl_errno - A curl error number. Only set if status is + * STATUS_CURLERROR. + * * curl_errmsg - A current error message. Only set if status is + * STATUS_CURLERROR. + * * response - Response object. Only set if status is STATUS_SUCCESS, or + * STATUS_HTTPERROR. + * * http_code - HTTP status code, as an int. Only set if Only set if + * status is STATUS_SUCCESS, or STATUS_HTTPERROR + * + * @param string $response + * @param resource $curlHandle + * @return Response + */ + protected function parseCurlResult($response, $curlHandle) { + + list( + $curlInfo, + $curlErrNo, + $curlErrMsg + ) = $this->curlStuff($curlHandle); + + if ($curlErrNo) { + return [ + 'status' => self::STATUS_CURLERROR, + 'curl_errno' => $curlErrNo, + 'curl_errmsg' => $curlErrMsg, + ]; + } + + $headerBlob = substr($response, 0, $curlInfo['header_size']); + // In the case of 204 No Content, strlen($response) == $curlInfo['header_size]. + // This will cause substr($response, $curlInfo['header_size']) return FALSE instead of NULL + // An exception will be thrown when calling getBodyAsString then + $responseBody = substr($response, $curlInfo['header_size']) ?: null; + + unset($response); + + // In the case of 100 Continue, or redirects we'll have multiple lists + // of headers for each separate HTTP response. We can easily split this + // because they are separated by \r\n\r\n + $headerBlob = explode("\r\n\r\n", trim($headerBlob, "\r\n")); + + // We only care about the last set of headers + $headerBlob = $headerBlob[count($headerBlob) - 1]; + + // Splitting headers + $headerBlob = explode("\r\n", $headerBlob); + + $response = new Response(); + $response->setStatus($curlInfo['http_code']); + + foreach ($headerBlob as $header) { + $parts = explode(':', $header, 2); + if (count($parts) == 2) { + $response->addHeader(trim($parts[0]), trim($parts[1])); + } + } + + $response->setBody($responseBody); + + $httpCode = intval($response->getStatus()); + + return [ + 'status' => $httpCode >= 400 ? self::STATUS_HTTPERROR : self::STATUS_SUCCESS, + 'response' => $response, + 'http_code' => $httpCode, + ]; + + } + + /** + * Sends an asynchronous HTTP request. + * + * We keep this in a separate method, so we can call it without triggering + * the beforeRequest event and don't do the poll(). + * + * @param RequestInterface $request + * @param callable $success + * @param callable $error + * @param int $retryCount + */ + protected function sendAsyncInternal(RequestInterface $request, callable $success, callable $error, $retryCount = 0) { + + if (!$this->curlMultiHandle) { + $this->curlMultiHandle = curl_multi_init(); + } + $curl = curl_init(); + curl_setopt_array( + $curl, + $this->createCurlSettingsArray($request) + ); + curl_multi_add_handle($this->curlMultiHandle, $curl); + $this->curlMultiMap[intval($curl)] = [ + $request, + $success, + $error, + $retryCount + ]; + + } + + // @codeCoverageIgnoreStart + + /** + * Calls curl_exec + * + * This method exists so it can easily be overridden and mocked. + * + * @param resource $curlHandle + * @return string + */ + protected function curlExec($curlHandle) { + + return curl_exec($curlHandle); + + } + + /** + * Returns a bunch of information about a curl request. + * + * This method exists so it can easily be overridden and mocked. + * + * @param resource $curlHandle + * @return array + */ + protected function curlStuff($curlHandle) { + + return [ + curl_getinfo($curlHandle), + curl_errno($curlHandle), + curl_error($curlHandle), + ]; + + } + // @codeCoverageIgnoreEnd + +} diff --git a/vendor/sabre/http/lib/ClientException.php b/vendor/sabre/http/lib/ClientException.php new file mode 100644 index 000000000..69631f44e --- /dev/null +++ b/vendor/sabre/http/lib/ClientException.php @@ -0,0 +1,15 @@ +response = $response; + parent::__construct($response->getStatusText(), $response->getStatus()); + + } + + /** + * The http status code for the error. + * + * @return int + */ + function getHttpStatus() { + + return $this->response->getStatus(); + + } + + /** + * Returns the full response object. + * + * @return ResponseInterface + */ + function getResponse() { + + return $this->response; + + } + +} diff --git a/vendor/sabre/http/lib/HttpException.php b/vendor/sabre/http/lib/HttpException.php new file mode 100644 index 000000000..1303dec97 --- /dev/null +++ b/vendor/sabre/http/lib/HttpException.php @@ -0,0 +1,30 @@ +getBody(); + if (is_string($body) || is_null($body)) { + $stream = fopen('php://temp', 'r+'); + fwrite($stream, $body); + rewind($stream); + return $stream; + } + return $body; + + } + + /** + * Returns the body as a string. + * + * Note that because the underlying data may be based on a stream, this + * method could only work correctly the first time. + * + * @return string + */ + function getBodyAsString() { + + $body = $this->getBody(); + if (is_string($body)) { + return $body; + } + if (is_null($body)) { + return ''; + } + $contentLength = $this->getHeader('Content-Length'); + if (null === $contentLength) { + return stream_get_contents($body); + } else { + return stream_get_contents($body, $contentLength); + } + + } + + /** + * Returns the message body, as it's internal representation. + * + * This could be either a string or a stream. + * + * @return resource|string + */ + function getBody() { + + return $this->body; + + } + + /** + * Replaces the body resource with a new stream or string. + * + * @param resource|string $body + */ + function setBody($body) { + + $this->body = $body; + + } + + /** + * Returns all the HTTP headers as an array. + * + * Every header is returned as an array, with one or more values. + * + * @return array + */ + function getHeaders() { + + $result = []; + foreach ($this->headers as $headerInfo) { + $result[$headerInfo[0]] = $headerInfo[1]; + } + return $result; + + } + + /** + * Will return true or false, depending on if a HTTP header exists. + * + * @param string $name + * @return bool + */ + function hasHeader($name) { + + return isset($this->headers[strtolower($name)]); + + } + + /** + * Returns a specific HTTP header, based on it's name. + * + * The name must be treated as case-insensitive. + * If the header does not exist, this method must return null. + * + * If a header appeared more than once in a HTTP request, this method will + * concatenate all the values with a comma. + * + * Note that this not make sense for all headers. Some, such as + * `Set-Cookie` cannot be logically combined with a comma. In those cases + * you *should* use getHeaderAsArray(). + * + * @param string $name + * @return string|null + */ + function getHeader($name) { + + $name = strtolower($name); + + if (isset($this->headers[$name])) { + return implode(',', $this->headers[$name][1]); + } + return null; + + } + + /** + * Returns a HTTP header as an array. + * + * For every time the HTTP header appeared in the request or response, an + * item will appear in the array. + * + * If the header did not exists, this method will return an empty array. + * + * @param string $name + * @return string[] + */ + function getHeaderAsArray($name) { + + $name = strtolower($name); + + if (isset($this->headers[$name])) { + return $this->headers[$name][1]; + } + + return []; + + } + + /** + * Updates a HTTP header. + * + * The case-sensitity of the name value must be retained as-is. + * + * If the header already existed, it will be overwritten. + * + * @param string $name + * @param string|string[] $value + * @return void + */ + function setHeader($name, $value) { + + $this->headers[strtolower($name)] = [$name, (array)$value]; + + } + + /** + * Sets a new set of HTTP headers. + * + * The headers array should contain headernames for keys, and their value + * should be specified as either a string or an array. + * + * Any header that already existed will be overwritten. + * + * @param array $headers + * @return void + */ + function setHeaders(array $headers) { + + foreach ($headers as $name => $value) { + $this->setHeader($name, $value); + } + + } + + /** + * Adds a HTTP header. + * + * This method will not overwrite any existing HTTP header, but instead add + * another value. Individual values can be retrieved with + * getHeadersAsArray. + * + * @param string $name + * @param string $value + * @return void + */ + function addHeader($name, $value) { + + $lName = strtolower($name); + if (isset($this->headers[$lName])) { + $this->headers[$lName][1] = array_merge( + $this->headers[$lName][1], + (array)$value + ); + } else { + $this->headers[$lName] = [ + $name, + (array)$value + ]; + } + + } + + /** + * Adds a new set of HTTP headers. + * + * Any existing headers will not be overwritten. + * + * @param array $headers + * @return void + */ + function addHeaders(array $headers) { + + foreach ($headers as $name => $value) { + $this->addHeader($name, $value); + } + + } + + + /** + * Removes a HTTP header. + * + * The specified header name must be treated as case-insenstive. + * This method should return true if the header was successfully deleted, + * and false if the header did not exist. + * + * @return bool + */ + function removeHeader($name) { + + $name = strtolower($name); + if (!isset($this->headers[$name])) { + return false; + } + unset($this->headers[$name]); + return true; + + } + + /** + * Sets the HTTP version. + * + * Should be 1.0 or 1.1. + * + * @param string $version + * @return void + */ + function setHttpVersion($version) { + + $this->httpVersion = $version; + + } + + /** + * Returns the HTTP version. + * + * @return string + */ + function getHttpVersion() { + + return $this->httpVersion; + + } +} diff --git a/vendor/sabre/http/lib/MessageDecoratorTrait.php b/vendor/sabre/http/lib/MessageDecoratorTrait.php new file mode 100644 index 000000000..f104af38d --- /dev/null +++ b/vendor/sabre/http/lib/MessageDecoratorTrait.php @@ -0,0 +1,250 @@ +inner->getBodyAsStream(); + + } + + /** + * Returns the body as a string. + * + * Note that because the underlying data may be based on a stream, this + * method could only work correctly the first time. + * + * @return string + */ + function getBodyAsString() { + + return $this->inner->getBodyAsString(); + + } + + /** + * Returns the message body, as it's internal representation. + * + * This could be either a string or a stream. + * + * @return resource|string + */ + function getBody() { + + return $this->inner->getBody(); + + } + + /** + * Updates the body resource with a new stream. + * + * @param resource $body + * @return void + */ + function setBody($body) { + + $this->inner->setBody($body); + + } + + /** + * Returns all the HTTP headers as an array. + * + * Every header is returned as an array, with one or more values. + * + * @return array + */ + function getHeaders() { + + return $this->inner->getHeaders(); + + } + + /** + * Will return true or false, depending on if a HTTP header exists. + * + * @param string $name + * @return bool + */ + function hasHeader($name) { + + return $this->inner->hasHeader($name); + + } + + /** + * Returns a specific HTTP header, based on it's name. + * + * The name must be treated as case-insensitive. + * If the header does not exist, this method must return null. + * + * If a header appeared more than once in a HTTP request, this method will + * concatenate all the values with a comma. + * + * Note that this not make sense for all headers. Some, such as + * `Set-Cookie` cannot be logically combined with a comma. In those cases + * you *should* use getHeaderAsArray(). + * + * @param string $name + * @return string|null + */ + function getHeader($name) { + + return $this->inner->getHeader($name); + + } + + /** + * Returns a HTTP header as an array. + * + * For every time the HTTP header appeared in the request or response, an + * item will appear in the array. + * + * If the header did not exists, this method will return an empty array. + * + * @param string $name + * @return string[] + */ + function getHeaderAsArray($name) { + + return $this->inner->getHeaderAsArray($name); + + } + + /** + * Updates a HTTP header. + * + * The case-sensitity of the name value must be retained as-is. + * + * If the header already existed, it will be overwritten. + * + * @param string $name + * @param string|string[] $value + * @return void + */ + function setHeader($name, $value) { + + $this->inner->setHeader($name, $value); + + } + + /** + * Sets a new set of HTTP headers. + * + * The headers array should contain headernames for keys, and their value + * should be specified as either a string or an array. + * + * Any header that already existed will be overwritten. + * + * @param array $headers + * @return void + */ + function setHeaders(array $headers) { + + $this->inner->setHeaders($headers); + + } + + /** + * Adds a HTTP header. + * + * This method will not overwrite any existing HTTP header, but instead add + * another value. Individual values can be retrieved with + * getHeadersAsArray. + * + * @param string $name + * @param string $value + * @return void + */ + function addHeader($name, $value) { + + $this->inner->addHeader($name, $value); + + } + + /** + * Adds a new set of HTTP headers. + * + * Any existing headers will not be overwritten. + * + * @param array $headers + * @return void + */ + function addHeaders(array $headers) { + + $this->inner->addHeaders($headers); + + } + + + /** + * Removes a HTTP header. + * + * The specified header name must be treated as case-insenstive. + * This method should return true if the header was successfully deleted, + * and false if the header did not exist. + * + * @return bool + */ + function removeHeader($name) { + + $this->inner->removeHeader($name); + + } + + /** + * Sets the HTTP version. + * + * Should be 1.0 or 1.1. + * + * @param string $version + * @return void + */ + function setHttpVersion($version) { + + $this->inner->setHttpVersion($version); + + } + + /** + * Returns the HTTP version. + * + * @return string + */ + function getHttpVersion() { + + return $this->inner->getHttpVersion(); + + } + +} diff --git a/vendor/sabre/http/lib/MessageInterface.php b/vendor/sabre/http/lib/MessageInterface.php new file mode 100644 index 000000000..55d8485c1 --- /dev/null +++ b/vendor/sabre/http/lib/MessageInterface.php @@ -0,0 +1,177 @@ +setMethod($method); + if (!is_null($url)) $this->setUrl($url); + if (!is_null($headers)) $this->setHeaders($headers); + if (!is_null($body)) $this->setBody($body); + + } + + /** + * Returns the current HTTP method + * + * @return string + */ + function getMethod() { + + return $this->method; + + } + + /** + * Sets the HTTP method + * + * @param string $method + * @return void + */ + function setMethod($method) { + + $this->method = $method; + + } + + /** + * Returns the request url. + * + * @return string + */ + function getUrl() { + + return $this->url; + + } + + /** + * Sets the request url. + * + * @param string $url + * @return void + */ + function setUrl($url) { + + $this->url = $url; + + } + + /** + * Returns the list of query parameters. + * + * This is equivalent to PHP's $_GET superglobal. + * + * @return array + */ + function getQueryParameters() { + + $url = $this->getUrl(); + if (($index = strpos($url, '?')) === false) { + return []; + } else { + parse_str(substr($url, $index + 1), $queryParams); + return $queryParams; + } + + } + + /** + * Sets the absolute url. + * + * @param string $url + * @return void + */ + function setAbsoluteUrl($url) { + + $this->absoluteUrl = $url; + + } + + /** + * Returns the absolute url. + * + * @return string + */ + function getAbsoluteUrl() { + + return $this->absoluteUrl; + + } + + /** + * Base url + * + * @var string + */ + protected $baseUrl = '/'; + + /** + * Sets a base url. + * + * This url is used for relative path calculations. + * + * @param string $url + * @return void + */ + function setBaseUrl($url) { + + $this->baseUrl = $url; + + } + + /** + * Returns the current base url. + * + * @return string + */ + function getBaseUrl() { + + return $this->baseUrl; + + } + + /** + * Returns the relative path. + * + * This is being calculated using the base url. This path will not start + * with a slash, so it will always return something like + * 'example/path.html'. + * + * If the full path is equal to the base url, this method will return an + * empty string. + * + * This method will also urldecode the path, and if the url was incoded as + * ISO-8859-1, it will convert it to UTF-8. + * + * If the path is outside of the base url, a LogicException will be thrown. + * + * @return string + */ + function getPath() { + + // Removing duplicated slashes. + $uri = str_replace('//', '/', $this->getUrl()); + + $uri = Uri\normalize($uri); + $baseUri = Uri\normalize($this->getBaseUrl()); + + if (strpos($uri, $baseUri) === 0) { + + // We're not interested in the query part (everything after the ?). + list($uri) = explode('?', $uri); + return trim(URLUtil::decodePath(substr($uri, strlen($baseUri))), '/'); + + } + // A special case, if the baseUri was accessed without a trailing + // slash, we'll accept it as well. + elseif ($uri . '/' === $baseUri) { + + return ''; + + } + + throw new \LogicException('Requested uri (' . $this->getUrl() . ') is out of base uri (' . $this->getBaseUrl() . ')'); + } + + /** + * Equivalent of PHP's $_POST. + * + * @var array + */ + protected $postData = []; + + /** + * Sets the post data. + * + * This is equivalent to PHP's $_POST superglobal. + * + * This would not have been needed, if POST data was accessible as + * php://input, but unfortunately we need to special case it. + * + * @param array $postData + * @return void + */ + function setPostData(array $postData) { + + $this->postData = $postData; + + } + + /** + * Returns the POST data. + * + * This is equivalent to PHP's $_POST superglobal. + * + * @return array + */ + function getPostData() { + + return $this->postData; + + } + + /** + * An array containing the raw _SERVER array. + * + * @var array + */ + protected $rawServerData; + + /** + * Returns an item from the _SERVER array. + * + * If the value does not exist in the array, null is returned. + * + * @param string $valueName + * @return string|null + */ + function getRawServerValue($valueName) { + + if (isset($this->rawServerData[$valueName])) { + return $this->rawServerData[$valueName]; + } + + } + + /** + * Sets the _SERVER array. + * + * @param array $data + * @return void + */ + function setRawServerData(array $data) { + + $this->rawServerData = $data; + + } + + /** + * Serializes the request object as a string. + * + * This is useful for debugging purposes. + * + * @return string + */ + function __toString() { + + $out = $this->getMethod() . ' ' . $this->getUrl() . ' HTTP/' . $this->getHTTPVersion() . "\r\n"; + + foreach ($this->getHeaders() as $key => $value) { + foreach ($value as $v) { + if ($key === 'Authorization') { + list($v) = explode(' ', $v, 2); + $v .= ' REDACTED'; + } + $out .= $key . ": " . $v . "\r\n"; + } + } + $out .= "\r\n"; + $out .= $this->getBodyAsString(); + + return $out; + + } + +} diff --git a/vendor/sabre/http/lib/RequestDecorator.php b/vendor/sabre/http/lib/RequestDecorator.php new file mode 100644 index 000000000..7ee3f6fc8 --- /dev/null +++ b/vendor/sabre/http/lib/RequestDecorator.php @@ -0,0 +1,231 @@ +inner = $inner; + + } + + /** + * Returns the current HTTP method + * + * @return string + */ + function getMethod() { + + return $this->inner->getMethod(); + + } + + /** + * Sets the HTTP method + * + * @param string $method + * @return void + */ + function setMethod($method) { + + $this->inner->setMethod($method); + + } + + /** + * Returns the request url. + * + * @return string + */ + function getUrl() { + + return $this->inner->getUrl(); + + } + + /** + * Sets the request url. + * + * @param string $url + * @return void + */ + function setUrl($url) { + + $this->inner->setUrl($url); + + } + + /** + * Returns the absolute url. + * + * @return string + */ + function getAbsoluteUrl() { + + return $this->inner->getAbsoluteUrl(); + + } + + /** + * Sets the absolute url. + * + * @param string $url + * @return void + */ + function setAbsoluteUrl($url) { + + $this->inner->setAbsoluteUrl($url); + + } + + /** + * Returns the current base url. + * + * @return string + */ + function getBaseUrl() { + + return $this->inner->getBaseUrl(); + + } + + /** + * Sets a base url. + * + * This url is used for relative path calculations. + * + * The base url should default to / + * + * @param string $url + * @return void + */ + function setBaseUrl($url) { + + $this->inner->setBaseUrl($url); + + } + + /** + * Returns the relative path. + * + * This is being calculated using the base url. This path will not start + * with a slash, so it will always return something like + * 'example/path.html'. + * + * If the full path is equal to the base url, this method will return an + * empty string. + * + * This method will also urldecode the path, and if the url was incoded as + * ISO-8859-1, it will convert it to UTF-8. + * + * If the path is outside of the base url, a LogicException will be thrown. + * + * @return string + */ + function getPath() { + + return $this->inner->getPath(); + + } + + /** + * Returns the list of query parameters. + * + * This is equivalent to PHP's $_GET superglobal. + * + * @return array + */ + function getQueryParameters() { + + return $this->inner->getQueryParameters(); + + } + + /** + * Returns the POST data. + * + * This is equivalent to PHP's $_POST superglobal. + * + * @return array + */ + function getPostData() { + + return $this->inner->getPostData(); + + } + + /** + * Sets the post data. + * + * This is equivalent to PHP's $_POST superglobal. + * + * This would not have been needed, if POST data was accessible as + * php://input, but unfortunately we need to special case it. + * + * @param array $postData + * @return void + */ + function setPostData(array $postData) { + + $this->inner->setPostData($postData); + + } + + + /** + * Returns an item from the _SERVER array. + * + * If the value does not exist in the array, null is returned. + * + * @param string $valueName + * @return string|null + */ + function getRawServerValue($valueName) { + + return $this->inner->getRawServerValue($valueName); + + } + + /** + * Sets the _SERVER array. + * + * @param array $data + * @return void + */ + function setRawServerData(array $data) { + + $this->inner->setRawServerData($data); + + } + + /** + * Serializes the request object as a string. + * + * This is useful for debugging purposes. + * + * @return string + */ + function __toString() { + + return $this->inner->__toString(); + + } +} diff --git a/vendor/sabre/http/lib/RequestInterface.php b/vendor/sabre/http/lib/RequestInterface.php new file mode 100644 index 000000000..63d9cbb51 --- /dev/null +++ b/vendor/sabre/http/lib/RequestInterface.php @@ -0,0 +1,147 @@ + 'Continue', + 101 => 'Switching Protocols', + 102 => 'Processing', + 200 => 'OK', + 201 => 'Created', + 202 => 'Accepted', + 203 => 'Non-Authorative Information', + 204 => 'No Content', + 205 => 'Reset Content', + 206 => 'Partial Content', + 207 => 'Multi-Status', // RFC 4918 + 208 => 'Already Reported', // RFC 5842 + 226 => 'IM Used', // RFC 3229 + 300 => 'Multiple Choices', + 301 => 'Moved Permanently', + 302 => 'Found', + 303 => 'See Other', + 304 => 'Not Modified', + 305 => 'Use Proxy', + 307 => 'Temporary Redirect', + 308 => 'Permanent Redirect', + 400 => 'Bad Request', + 401 => 'Unauthorized', + 402 => 'Payment Required', + 403 => 'Forbidden', + 404 => 'Not Found', + 405 => 'Method Not Allowed', + 406 => 'Not Acceptable', + 407 => 'Proxy Authentication Required', + 408 => 'Request Timeout', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition failed', + 413 => 'Request Entity Too Large', + 414 => 'Request-URI Too Long', + 415 => 'Unsupported Media Type', + 416 => 'Requested Range Not Satisfiable', + 417 => 'Expectation Failed', + 418 => 'I\'m a teapot', // RFC 2324 + 421 => 'Misdirected Request', // RFC7540 (HTTP/2) + 422 => 'Unprocessable Entity', // RFC 4918 + 423 => 'Locked', // RFC 4918 + 424 => 'Failed Dependency', // RFC 4918 + 426 => 'Upgrade Required', + 428 => 'Precondition Required', // RFC 6585 + 429 => 'Too Many Requests', // RFC 6585 + 431 => 'Request Header Fields Too Large', // RFC 6585 + 451 => 'Unavailable For Legal Reasons', // draft-tbray-http-legally-restricted-status + 500 => 'Internal Server Error', + 501 => 'Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Unavailable', + 504 => 'Gateway Timeout', + 505 => 'HTTP Version not supported', + 506 => 'Variant Also Negotiates', + 507 => 'Insufficient Storage', // RFC 4918 + 508 => 'Loop Detected', // RFC 5842 + 509 => 'Bandwidth Limit Exceeded', // non-standard + 510 => 'Not extended', + 511 => 'Network Authentication Required', // RFC 6585 + ]; + + /** + * HTTP status code + * + * @var int + */ + protected $status; + + /** + * HTTP status text + * + * @var string + */ + protected $statusText; + + /** + * Creates the response object + * + * @param string|int $status + * @param array $headers + * @param resource $body + * @return void + */ + function __construct($status = null, array $headers = null, $body = null) { + + if (!is_null($status)) $this->setStatus($status); + if (!is_null($headers)) $this->setHeaders($headers); + if (!is_null($body)) $this->setBody($body); + + } + + + /** + * Returns the current HTTP status code. + * + * @return int + */ + function getStatus() { + + return $this->status; + + } + + /** + * Returns the human-readable status string. + * + * In the case of a 200, this may for example be 'OK'. + * + * @return string + */ + function getStatusText() { + + return $this->statusText; + + } + + /** + * Sets the HTTP status code. + * + * This can be either the full HTTP status code with human readable string, + * for example: "403 I can't let you do that, Dave". + * + * Or just the code, in which case the appropriate default message will be + * added. + * + * @param string|int $status + * @throws \InvalidArgumentExeption + * @return void + */ + function setStatus($status) { + + if (ctype_digit($status) || is_int($status)) { + + $statusCode = $status; + $statusText = isset(self::$statusCodes[$status]) ? self::$statusCodes[$status] : 'Unknown'; + + } else { + list( + $statusCode, + $statusText + ) = explode(' ', $status, 2); + } + if ($statusCode < 100 || $statusCode > 999) { + throw new \InvalidArgumentException('The HTTP status code must be exactly 3 digits'); + } + + $this->status = $statusCode; + $this->statusText = $statusText; + + } + + /** + * Serializes the response object as a string. + * + * This is useful for debugging purposes. + * + * @return string + */ + function __toString() { + + $str = 'HTTP/' . $this->httpVersion . ' ' . $this->getStatus() . ' ' . $this->getStatusText() . "\r\n"; + foreach ($this->getHeaders() as $key => $value) { + foreach ($value as $v) { + $str .= $key . ": " . $v . "\r\n"; + } + } + $str .= "\r\n"; + $str .= $this->getBodyAsString(); + return $str; + + } + +} diff --git a/vendor/sabre/http/lib/ResponseDecorator.php b/vendor/sabre/http/lib/ResponseDecorator.php new file mode 100644 index 000000000..db3a67507 --- /dev/null +++ b/vendor/sabre/http/lib/ResponseDecorator.php @@ -0,0 +1,84 @@ +inner = $inner; + + } + + /** + * Returns the current HTTP status code. + * + * @return int + */ + function getStatus() { + + return $this->inner->getStatus(); + + } + + + /** + * Returns the human-readable status string. + * + * In the case of a 200, this may for example be 'OK'. + * + * @return string + */ + function getStatusText() { + + return $this->inner->getStatusText(); + + } + /** + * Sets the HTTP status code. + * + * This can be either the full HTTP status code with human readable string, + * for example: "403 I can't let you do that, Dave". + * + * Or just the code, in which case the appropriate default message will be + * added. + * + * @param string|int $status + * @return void + */ + function setStatus($status) { + + $this->inner->setStatus($status); + + } + + /** + * Serializes the request object as a string. + * + * This is useful for debugging purposes. + * + * @return string + */ + function __toString() { + + return $this->inner->__toString(); + + } +} diff --git a/vendor/sabre/http/lib/ResponseInterface.php b/vendor/sabre/http/lib/ResponseInterface.php new file mode 100644 index 000000000..c0ecc35ae --- /dev/null +++ b/vendor/sabre/http/lib/ResponseInterface.php @@ -0,0 +1,45 @@ +setBody(fopen('php://input', 'r')); + $r->setPostData($_POST); + return $r; + + } + + /** + * Sends the HTTP response back to a HTTP client. + * + * This calls php's header() function and streams the body to php://output. + * + * @param ResponseInterface $response + * @return void + */ + static function sendResponse(ResponseInterface $response) { + + header('HTTP/' . $response->getHttpVersion() . ' ' . $response->getStatus() . ' ' . $response->getStatusText()); + foreach ($response->getHeaders() as $key => $value) { + + foreach ($value as $k => $v) { + if ($k === 0) { + header($key . ': ' . $v); + } else { + header($key . ': ' . $v, false); + } + } + + } + + $body = $response->getBody(); + if (is_null($body)) return; + + $contentLength = $response->getHeader('Content-Length'); + if ($contentLength !== null) { + $output = fopen('php://output', 'wb'); + if (is_resource($body) && get_resource_type($body) == 'stream') { + stream_copy_to_stream($body, $output, $contentLength); + } else { + fwrite($output, $body, $contentLength); + } + } else { + file_put_contents('php://output', $body); + } + + if (is_resource($body)) { + fclose($body); + } + + } + + /** + * This static method will create a new Request object, based on a PHP + * $_SERVER array. + * + * @param array $serverArray + * @return Request + */ + static function createFromServerArray(array $serverArray) { + + $headers = []; + $method = null; + $url = null; + $httpVersion = '1.1'; + + $protocol = 'http'; + $hostName = 'localhost'; + + foreach ($serverArray as $key => $value) { + + switch ($key) { + + case 'SERVER_PROTOCOL' : + if ($value === 'HTTP/1.0') { + $httpVersion = '1.0'; + } + break; + case 'REQUEST_METHOD' : + $method = $value; + break; + case 'REQUEST_URI' : + $url = $value; + break; + + // These sometimes show up without a HTTP_ prefix + case 'CONTENT_TYPE' : + $headers['Content-Type'] = $value; + break; + case 'CONTENT_LENGTH' : + $headers['Content-Length'] = $value; + break; + + // mod_php on apache will put credentials in these variables. + // (fast)cgi does not usually do this, however. + case 'PHP_AUTH_USER' : + if (isset($serverArray['PHP_AUTH_PW'])) { + $headers['Authorization'] = 'Basic ' . base64_encode($value . ':' . $serverArray['PHP_AUTH_PW']); + } + break; + + // Similarly, mod_php may also screw around with digest auth. + case 'PHP_AUTH_DIGEST' : + $headers['Authorization'] = 'Digest ' . $value; + break; + + // Apache may prefix the HTTP_AUTHORIZATION header with + // REDIRECT_, if mod_rewrite was used. + case 'REDIRECT_HTTP_AUTHORIZATION' : + $headers['Authorization'] = $value; + break; + + case 'HTTP_HOST' : + $hostName = $value; + $headers['Host'] = $value; + break; + + case 'HTTPS' : + if (!empty($value) && $value !== 'off') { + $protocol = 'https'; + } + break; + + default : + if (substr($key, 0, 5) === 'HTTP_') { + // It's a HTTP header + + // Normalizing it to be prettier + $header = strtolower(substr($key, 5)); + + // Transforming dashes into spaces, and uppercasing + // every first letter. + $header = ucwords(str_replace('_', ' ', $header)); + + // Turning spaces into dashes. + $header = str_replace(' ', '-', $header); + $headers[$header] = $value; + + } + break; + + + } + + } + + $r = new Request($method, $url, $headers); + $r->setHttpVersion($httpVersion); + $r->setRawServerData($serverArray); + $r->setAbsoluteUrl($protocol . '://' . $hostName . $url); + return $r; + + } + +} diff --git a/vendor/sabre/http/lib/URLUtil.php b/vendor/sabre/http/lib/URLUtil.php new file mode 100644 index 000000000..474856348 --- /dev/null +++ b/vendor/sabre/http/lib/URLUtil.php @@ -0,0 +1,103 @@ +setTimeZone(new \DateTimeZone('GMT')); + return $dateTime->format('D, d M Y H:i:s \G\M\T'); + +} + +/** + * This function can be used to aid with content negotiation. + * + * It takes 2 arguments, the $acceptHeaderValue, which usually comes from + * an Accept header, and $availableOptions, which contains an array of + * items that the server can support. + * + * The result of this function will be the 'best possible option'. If no + * best possible option could be found, null is returned. + * + * When it's null you can according to the spec either return a default, or + * you can choose to emit 406 Not Acceptable. + * + * The method also accepts sending 'null' for the $acceptHeaderValue, + * implying that no accept header was sent. + * + * @param string|null $acceptHeaderValue + * @param array $availableOptions + * @return string|null + */ +function negotiateContentType($acceptHeaderValue, array $availableOptions) { + + if (!$acceptHeaderValue) { + // Grabbing the first in the list. + return reset($availableOptions); + } + + $proposals = array_map( + 'Sabre\HTTP\parseMimeType', + explode(',', $acceptHeaderValue) + ); + + // Ensuring array keys are reset. + $availableOptions = array_values($availableOptions); + + $options = array_map( + 'Sabre\HTTP\parseMimeType', + $availableOptions + ); + + $lastQuality = 0; + $lastSpecificity = 0; + $lastOptionIndex = 0; + $lastChoice = null; + + foreach ($proposals as $proposal) { + + // Ignoring broken values. + if (is_null($proposal)) continue; + + // If the quality is lower we don't have to bother comparing. + if ($proposal['quality'] < $lastQuality) { + continue; + } + + foreach ($options as $optionIndex => $option) { + + if ($proposal['type'] !== '*' && $proposal['type'] !== $option['type']) { + // no match on type. + continue; + } + if ($proposal['subType'] !== '*' && $proposal['subType'] !== $option['subType']) { + // no match on subtype. + continue; + } + + // Any parameters appearing on the options must appear on + // proposals. + foreach ($option['parameters'] as $paramName => $paramValue) { + if (!array_key_exists($paramName, $proposal['parameters'])) { + continue 2; + } + if ($paramValue !== $proposal['parameters'][$paramName]) { + continue 2; + } + } + + // If we got here, we have a match on parameters, type and + // subtype. We need to calculate a score for how specific the + // match was. + $specificity = + ($proposal['type'] !== '*' ? 20 : 0) + + ($proposal['subType'] !== '*' ? 10 : 0) + + count($option['parameters']); + + + // Does this entry win? + if ( + ($proposal['quality'] > $lastQuality) || + ($proposal['quality'] === $lastQuality && $specificity > $lastSpecificity) || + ($proposal['quality'] === $lastQuality && $specificity === $lastSpecificity && $optionIndex < $lastOptionIndex) + ) { + + $lastQuality = $proposal['quality']; + $lastSpecificity = $specificity; + $lastOptionIndex = $optionIndex; + $lastChoice = $availableOptions[$optionIndex]; + + } + + } + + } + + return $lastChoice; + +} + +/** + * Parses the Prefer header, as defined in RFC7240. + * + * Input can be given as a single header value (string) or multiple headers + * (array of string). + * + * This method will return a key->value array with the various Prefer + * parameters. + * + * Prefer: return=minimal will result in: + * + * [ 'return' => 'minimal' ] + * + * Prefer: foo, wait=10 will result in: + * + * [ 'foo' => true, 'wait' => '10'] + * + * This method also supports the formats from older drafts of RFC7240, and + * it will automatically map them to the new values, as the older values + * are still pretty common. + * + * Parameters are currently discarded. There's no known prefer value that + * uses them. + * + * @param string|string[] $header + * @return array + */ +function parsePrefer($input) { + + $token = '[!#$%&\'*+\-.^_`~A-Za-z0-9]+'; + + // Work in progress + $word = '(?: [a-zA-Z0-9]+ | "[a-zA-Z0-9]*" )'; + + $regex = << $token) # Prefer property name +\s* # Optional space +(?: = \s* # Prefer property value + (? $word) +)? +(?: \s* ; (?: .*))? # Prefer parameters (ignored) +$ +/x +REGEX; + + $output = []; + foreach (getHeaderValues($input) as $value) { + + if (!preg_match($regex, $value, $matches)) { + // Ignore + continue; + } + + // Mapping old values to their new counterparts + switch ($matches['name']) { + case 'return-asynch' : + $output['respond-async'] = true; + break; + case 'return-representation' : + $output['return'] = 'representation'; + break; + case 'return-minimal' : + $output['return'] = 'minimal'; + break; + case 'strict' : + $output['handling'] = 'strict'; + break; + case 'lenient' : + $output['handling'] = 'lenient'; + break; + default : + if (isset($matches['value'])) { + $value = trim($matches['value'], '"'); + } else { + $value = true; + } + $output[strtolower($matches['name'])] = empty($value) ? true : $value; + break; + } + + } + + return $output; + +} + +/** + * This method splits up headers into all their individual values. + * + * A HTTP header may have more than one header, such as this: + * Cache-Control: private, no-store + * + * Header values are always split with a comma. + * + * You can pass either a string, or an array. The resulting value is always + * an array with each spliced value. + * + * If the second headers argument is set, this value will simply be merged + * in. This makes it quicker to merge an old list of values with a new set. + * + * @param string|string[] $values + * @param string|string[] $values2 + * @return string[] + */ +function getHeaderValues($values, $values2 = null) { + + $values = (array)$values; + if ($values2) { + $values = array_merge($values, (array)$values2); + } + foreach ($values as $l1) { + foreach (explode(',', $l1) as $l2) { + $result[] = trim($l2); + } + } + return $result; + +} + +/** + * Parses a mime-type and splits it into: + * + * 1. type + * 2. subtype + * 3. quality + * 4. parameters + * + * @param string $str + * @return array + */ +function parseMimeType($str) { + + $parameters = []; + // If no q= parameter appears, then quality = 1. + $quality = 1; + + $parts = explode(';', $str); + + // The first part is the mime-type. + $mimeType = array_shift($parts); + + $mimeType = explode('/', trim($mimeType)); + if (count($mimeType) !== 2) { + // Illegal value + return null; + } + list($type, $subType) = $mimeType; + + foreach ($parts as $part) { + + $part = trim($part); + if (strpos($part, '=')) { + list($partName, $partValue) = + explode('=', $part, 2); + } else { + $partName = $part; + $partValue = null; + } + + // The quality parameter, if it appears, also marks the end of + // the parameter list. Anything after the q= counts as an + // 'accept extension' and could introduce new semantics in + // content-negotation. + if ($partName !== 'q') { + $parameters[$partName] = $part; + } else { + $quality = (float)$partValue; + break; // Stop parsing parts + } + + } + + return [ + 'type' => $type, + 'subType' => $subType, + 'quality' => $quality, + 'parameters' => $parameters, + ]; + +} + +/** + * Encodes the path of a url. + * + * slashes (/) are treated as path-separators. + * + * @param string $path + * @return string + */ +function encodePath($path) { + + return preg_replace_callback('/([^A-Za-z0-9_\-\.~\(\)\/:@])/', function($match) { + + return '%' . sprintf('%02x', ord($match[0])); + + }, $path); + +} + +/** + * Encodes a 1 segment of a path + * + * Slashes are considered part of the name, and are encoded as %2f + * + * @param string $pathSegment + * @return string + */ +function encodePathSegment($pathSegment) { + + return preg_replace_callback('/([^A-Za-z0-9_\-\.~\(\):@])/', function($match) { + + return '%' . sprintf('%02x', ord($match[0])); + + }, $pathSegment); +} + +/** + * Decodes a url-encoded path + * + * @param string $path + * @return string + */ +function decodePath($path) { + + return decodePathSegment($path); + +} + +/** + * Decodes a url-encoded path segment + * + * @param string $path + * @return string + */ +function decodePathSegment($path) { + + $path = rawurldecode($path); + $encoding = mb_detect_encoding($path, ['UTF-8', 'ISO-8859-1']); + + switch ($encoding) { + + case 'ISO-8859-1' : + $path = utf8_encode($path); + + } + + return $path; + +} -- cgit v1.2.3