aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/sabre/http
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/sabre/http')
-rw-r--r--vendor/sabre/http/.gitignore15
-rw-r--r--vendor/sabre/http/.travis.yml24
-rw-r--r--vendor/sabre/http/CHANGELOG.md250
-rw-r--r--vendor/sabre/http/LICENSE27
-rw-r--r--vendor/sabre/http/README.md746
-rw-r--r--vendor/sabre/http/bin/.empty0
-rw-r--r--vendor/sabre/http/lib/Auth/AWS.php234
-rw-r--r--vendor/sabre/http/lib/Auth/AbstractAuth.php73
-rw-r--r--vendor/sabre/http/lib/Auth/Basic.php63
-rw-r--r--vendor/sabre/http/lib/Auth/Bearer.php56
-rw-r--r--vendor/sabre/http/lib/Auth/Digest.php231
-rw-r--r--vendor/sabre/http/lib/Client.php601
-rw-r--r--vendor/sabre/http/lib/ClientException.php15
-rw-r--r--vendor/sabre/http/lib/ClientHttpException.php58
-rw-r--r--vendor/sabre/http/lib/HttpException.php30
-rw-r--r--vendor/sabre/http/lib/Message.php314
-rw-r--r--vendor/sabre/http/lib/MessageDecoratorTrait.php250
-rw-r--r--vendor/sabre/http/lib/MessageInterface.php177
-rw-r--r--vendor/sabre/http/lib/Request.php316
-rw-r--r--vendor/sabre/http/lib/RequestDecorator.php231
-rw-r--r--vendor/sabre/http/lib/RequestInterface.php147
-rw-r--r--vendor/sabre/http/lib/Response.php194
-rw-r--r--vendor/sabre/http/lib/ResponseDecorator.php84
-rw-r--r--vendor/sabre/http/lib/ResponseInterface.php45
-rw-r--r--vendor/sabre/http/lib/Sapi.php194
-rw-r--r--vendor/sabre/http/lib/URLUtil.php103
-rw-r--r--vendor/sabre/http/lib/Util.php74
-rw-r--r--vendor/sabre/http/lib/Version.php19
-rw-r--r--vendor/sabre/http/lib/functions.php445
29 files changed, 5016 insertions, 0 deletions
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
--- /dev/null
+++ b/vendor/sabre/http/bin/.empty
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 @@
+<?php
+
+namespace Sabre\HTTP\Auth;
+
+use Sabre\HTTP\Util;
+
+/**
+ * HTTP AWS Authentication handler
+ *
+ * Use this class to leverage amazon's AWS authentication header
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class AWS extends AbstractAuth {
+
+ /**
+ * The signature supplied by the HTTP client
+ *
+ * @var string
+ */
+ private $signature = null;
+
+ /**
+ * The accesskey supplied by the HTTP client
+ *
+ * @var string
+ */
+ private $accessKey = null;
+
+ /**
+ * An error code, if any
+ *
+ * This value will be filled with one of the ERR_* constants
+ *
+ * @var int
+ */
+ public $errorCode = 0;
+
+ const ERR_NOAWSHEADER = 1;
+ const ERR_MD5CHECKSUMWRONG = 2;
+ const ERR_INVALIDDATEFORMAT = 3;
+ const ERR_REQUESTTIMESKEWED = 4;
+ const ERR_INVALIDSIGNATURE = 5;
+
+ /**
+ * Gathers all information from the headers
+ *
+ * This method needs to be called prior to anything else.
+ *
+ * @return bool
+ */
+ function init() {
+
+ $authHeader = $this->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 @@
+<?php
+
+namespace Sabre\HTTP\Auth;
+
+use Sabre\HTTP\RequestInterface;
+use Sabre\HTTP\ResponseInterface;
+
+/**
+ * HTTP Authentication base class.
+ *
+ * This class provides some common functionality for the various base classes.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+abstract class AbstractAuth {
+
+ /**
+ * Authentication realm
+ *
+ * @var string
+ */
+ protected $realm;
+
+ /**
+ * Request object
+ *
+ * @var RequestInterface
+ */
+ protected $request;
+
+ /**
+ * Response object
+ *
+ * @var ResponseInterface
+ */
+ protected $response;
+
+ /**
+ * Creates the object
+ *
+ * @param string $realm
+ * @return void
+ */
+ function __construct($realm = 'SabreTooth', RequestInterface $request, ResponseInterface $response) {
+
+ $this->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 @@
+<?php
+
+namespace Sabre\HTTP\Auth;
+
+/**
+ * HTTP Basic authentication utility.
+ *
+ * This class helps you setup basic auth. The process is fairly simple:
+ *
+ * 1. Instantiate the class.
+ * 2. Call getCredentials (this will return null or a user/pass pair)
+ * 3. If you didn't get valid credentials, call 'requireLogin'
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Basic extends AbstractAuth {
+
+ /**
+ * This method returns a numeric array with a username and password as the
+ * only elements.
+ *
+ * If no credentials were found, this method returns null.
+ *
+ * @return null|array
+ */
+ function getCredentials() {
+
+ $auth = $this->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 @@
+<?php
+
+namespace Sabre\HTTP\Auth;
+
+/**
+ * HTTP Bearer authentication utility.
+ *
+ * This class helps you setup bearer auth. The process is fairly simple:
+ *
+ * 1. Instantiate the class.
+ * 2. Call getToken (this will return null or a token as string)
+ * 3. If you didn't get a valid token, call 'requireLogin'
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author François Kooman (fkooman@tuxed.net)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Bearer extends AbstractAuth {
+
+ /**
+ * This method returns a string with an access token.
+ *
+ * If no token was found, this method returns null.
+ *
+ * @return null|string
+ */
+ function getToken() {
+
+ $auth = $this->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 @@
+<?php
+
+namespace Sabre\HTTP\Auth;
+
+use Sabre\HTTP\RequestInterface;
+use Sabre\HTTP\ResponseInterface;
+
+/**
+ * HTTP Digest Authentication handler
+ *
+ * Use this class for easy http digest authentication.
+ * Instructions:
+ *
+ * 1. Create the object
+ * 2. Call the setRealm() method with the realm you plan to use
+ * 3. Call the init method function.
+ * 4. Call the getUserName() function. This function may return null if no
+ * authentication information was supplied. Based on the username you
+ * should check your internal database for either the associated password,
+ * or the so-called A1 hash of the digest.
+ * 5. Call either validatePassword() or validateA1(). This will return true
+ * or false.
+ * 6. To make sure an authentication prompt is displayed, call the
+ * requireLogin() method.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Digest extends AbstractAuth {
+
+ /**
+ * These constants are used in setQOP();
+ */
+ const QOP_AUTH = 1;
+ const QOP_AUTHINT = 2;
+
+ protected $nonce;
+ protected $opaque;
+ protected $digestParts;
+ protected $A1;
+ protected $qop = self::QOP_AUTH;
+
+ /**
+ * Initializes the object
+ */
+ function __construct($realm = 'SabreTooth', RequestInterface $request, ResponseInterface $response) {
+
+ $this->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 @@
+<?php
+
+namespace Sabre\HTTP;
+
+use Sabre\Event\EventEmitter;
+use Sabre\Uri;
+
+/**
+ * A rudimentary HTTP client.
+ *
+ * This object wraps PHP's curl extension and provides an easy way to send it a
+ * Request object, and return a Response object.
+ *
+ * This is by no means intended as the next best HTTP client, but it does the
+ * job and provides a simple integration with the rest of sabre/http.
+ *
+ * This client emits the following events:
+ * beforeRequest(RequestInterface $request)
+ * afterRequest(RequestInterface $request, ResponseInterface $response)
+ * error(RequestInterface $request, ResponseInterface $response, bool &$retry, int $retryCount)
+ * exception(RequestInterface $request, ClientException $e, bool &$retry, int $retryCount)
+ *
+ * The beforeRequest event allows you to do some last minute changes to the
+ * request before it's done, such as adding authentication headers.
+ *
+ * The afterRequest event will be emitted after the request is completed
+ * succesfully.
+ *
+ * If a HTTP error is returned (status code higher than 399) the error event is
+ * triggered. It's possible using this event to retry the request, by setting
+ * retry to true.
+ *
+ * The amount of times a request has retried is passed as $retryCount, which
+ * can be used to avoid retrying indefinitely. The first time the event is
+ * called, this will be 0.
+ *
+ * It's also possible to intercept specific http errors, by subscribing to for
+ * example 'error:401'.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Client extends EventEmitter {
+
+ /**
+ * List of curl settings
+ *
+ * @var array
+ */
+ protected $curlSettings = [];
+
+ /**
+ * Wether or not exceptions should be thrown when a HTTP error is returned.
+ *
+ * @var bool
+ */
+ protected $throwExceptions = false;
+
+ /**
+ * The maximum number of times we'll follow a redirect.
+ *
+ * @var int
+ */
+ protected $maxRedirects = 5;
+
+ /**
+ * Initializes the client.
+ *
+ * @return void
+ */
+ function __construct() {
+
+ $this->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 @@
+<?php
+
+namespace Sabre\HTTP;
+
+/**
+ * This exception may be emitted by the HTTP\Client class, in case there was a
+ * problem emitting the request.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class ClientException extends \Exception {
+
+}
diff --git a/vendor/sabre/http/lib/ClientHttpException.php b/vendor/sabre/http/lib/ClientHttpException.php
new file mode 100644
index 000000000..2923ef3b5
--- /dev/null
+++ b/vendor/sabre/http/lib/ClientHttpException.php
@@ -0,0 +1,58 @@
+<?php
+
+namespace Sabre\HTTP;
+
+/**
+ * This exception represents a HTTP error coming from the Client.
+ *
+ * By default the Client will not emit these, this has to be explicitly enabled
+ * with the setThrowExceptions method.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class ClientHttpException extends \Exception implements HttpException {
+
+ /**
+ * Response object
+ *
+ * @var ResponseInterface
+ */
+ protected $response;
+
+ /**
+ * Constructor
+ *
+ * @param ResponseInterface $response
+ */
+ function __construct(ResponseInterface $response) {
+
+ $this->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 @@
+<?php
+
+namespace Sabre\HTTP;
+
+/**
+ * An exception representing a HTTP error.
+ *
+ * This can be used as a generic exception in your application, if you'd like
+ * to map HTTP errors to exceptions.
+ *
+ * If you'd like to use this, create a new exception class, extending Exception
+ * and implementing this interface.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+interface HttpException {
+
+ /**
+ * The http status code for the error.
+ *
+ * This may either be just the number, or a number and a human-readable
+ * message, separated by a space.
+ *
+ * @return string|null
+ */
+ function getHttpStatus();
+
+}
diff --git a/vendor/sabre/http/lib/Message.php b/vendor/sabre/http/lib/Message.php
new file mode 100644
index 000000000..5c6887d8a
--- /dev/null
+++ b/vendor/sabre/http/lib/Message.php
@@ -0,0 +1,314 @@
+<?php
+
+namespace Sabre\HTTP;
+
+/**
+ * This is the abstract base class for both the Request and Response objects.
+ *
+ * This object contains a few simple methods that are shared by both.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+abstract class Message implements MessageInterface {
+
+ /**
+ * Request body
+ *
+ * This should be a stream resource
+ *
+ * @var resource
+ */
+ protected $body;
+
+ /**
+ * Contains the list of HTTP headers
+ *
+ * @var array
+ */
+ protected $headers = [];
+
+ /**
+ * HTTP message version (1.0 or 1.1)
+ *
+ * @var string
+ */
+ protected $httpVersion = '1.1';
+
+ /**
+ * 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() {
+
+ $body = $this->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 @@
+<?php
+
+namespace Sabre\HTTP;
+
+/**
+ * This trait contains a bunch of methods, shared by both the RequestDecorator
+ * and the ResponseDecorator.
+ *
+ * Didn't seem needed to create a full class for this, so we're just
+ * implementing it as a trait.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+trait MessageDecoratorTrait {
+
+ /**
+ * The inner request object.
+ *
+ * All method calls will be forwarded here.
+ *
+ * @var MessageInterface
+ */
+ protected $inner;
+
+ /**
+ * 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() {
+
+ return $this->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 @@
+<?php
+
+namespace Sabre\HTTP;
+
+/**
+ * The MessageInterface is the base interface that's used by both
+ * the RequestInterface and ResponseInterface.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+interface MessageInterface {
+
+ /**
+ * 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.
+ *
+ * Every header is returned as an array, with one or more values.
+ *
+ * @return array
+ */
+ function getHeaders();
+
+ /**
+ * Will return true or false, depending on if a HTTP header exists.
+ *
+ * @param string $name
+ * @return bool
+ */
+ function 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);
+
+ /**
+ * 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);
+
+ /**
+ * 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);
+
+ /**
+ * 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);
+
+ /**
+ * 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);
+
+ /**
+ * Adds a new set of HTTP headers.
+ *
+ * Any existing headers will not be overwritten.
+ *
+ * @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();
+
+}
diff --git a/vendor/sabre/http/lib/Request.php b/vendor/sabre/http/lib/Request.php
new file mode 100644
index 000000000..8bcaf32a6
--- /dev/null
+++ b/vendor/sabre/http/lib/Request.php
@@ -0,0 +1,316 @@
+<?php
+
+namespace Sabre\HTTP;
+
+use InvalidArgumentException;
+use Sabre\Uri;
+
+/**
+ * The Request class represents a single HTTP request.
+ *
+ * You can either simply construct the object from scratch, or if you need
+ * access to the current HTTP request, use Sapi::getRequest.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Request extends Message implements RequestInterface {
+
+ /**
+ * HTTP Method
+ *
+ * @var string
+ */
+ protected $method;
+
+ /**
+ * Request Url
+ *
+ * @var string
+ */
+ protected $url;
+
+ /**
+ * Creates the request object
+ *
+ * @param string $method
+ * @param string $url
+ * @param array $headers
+ * @param resource $body
+ */
+ function __construct($method = null, $url = null, array $headers = null, $body = null) {
+
+ if (is_array($method)) {
+ throw new InvalidArgumentException('The first argument for this constructor should be a string or null, not an array. Did you upgrade from sabre/http 1.0 to 2.0?');
+ }
+ if (!is_null($method)) $this->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 @@
+<?php
+
+namespace Sabre\HTTP;
+
+/**
+ * Request Decorator
+ *
+ * This helper class allows you to easily create decorators for the Request
+ * object.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class RequestDecorator implements RequestInterface {
+
+ use MessageDecoratorTrait;
+
+ /**
+ * Constructor.
+ *
+ * @param RequestInterface $inner
+ */
+ function __construct(RequestInterface $inner) {
+
+ $this->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 @@
+<?php
+
+namespace Sabre\HTTP;
+
+/**
+ * The RequestInterface represents a HTTP request.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+interface RequestInterface extends MessageInterface {
+
+ /**
+ * 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);
+
+
+}
diff --git a/vendor/sabre/http/lib/Response.php b/vendor/sabre/http/lib/Response.php
new file mode 100644
index 000000000..d2ba6d40d
--- /dev/null
+++ b/vendor/sabre/http/lib/Response.php
@@ -0,0 +1,194 @@
+<?php
+
+namespace Sabre\HTTP;
+
+/**
+ * This class represents a single HTTP response.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Response extends Message implements ResponseInterface {
+
+ /**
+ * This is the list of currently registered HTTP status codes.
+ *
+ * @var array
+ */
+ static $statusCodes = [
+ 100 => '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 @@
+<?php
+
+namespace Sabre\HTTP;
+
+/**
+ * Response Decorator
+ *
+ * This helper class allows you to easily create decorators for the Response
+ * object.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class ResponseDecorator implements ResponseInterface {
+
+ use MessageDecoratorTrait;
+
+ /**
+ * Constructor.
+ *
+ * @param ResponseInterface $inner
+ */
+ function __construct(ResponseInterface $inner) {
+
+ $this->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 @@
+<?php
+
+namespace Sabre\HTTP;
+
+/**
+ * This interface represents a HTTP response.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+interface ResponseInterface extends MessageInterface {
+
+ /**
+ * Returns the current HTTP status code.
+ *
+ * @return int
+ */
+ function getStatus();
+
+ /**
+ * Returns the human-readable status string.
+ *
+ * In the case of a 200, this may for example be 'OK'.
+ *
+ * @return string
+ */
+ function 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
+ * @throws \InvalidArgumentExeption
+ * @return void
+ */
+ function setStatus($status);
+
+}
diff --git a/vendor/sabre/http/lib/Sapi.php b/vendor/sabre/http/lib/Sapi.php
new file mode 100644
index 000000000..6c83c8719
--- /dev/null
+++ b/vendor/sabre/http/lib/Sapi.php
@@ -0,0 +1,194 @@
+<?php
+
+namespace Sabre\HTTP;
+
+/**
+ * PHP SAPI
+ *
+ * This object is responsible for:
+ * 1. Constructing a Request object based on the current HTTP request sent to
+ * the PHP process.
+ * 2. Sending the Response object back to the client.
+ *
+ * It could be said that this class provides a mapping between the Request and
+ * Response objects, and php's:
+ *
+ * * $_SERVER
+ * * $_POST
+ * * $_FILES
+ * * php://input
+ * * echo()
+ * * header()
+ * * php://output
+ *
+ * You can choose to either call all these methods statically, but you can also
+ * instantiate this as an object to allow for polymorhpism.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Sapi {
+
+ /**
+ * This static method will create a new Request object, based on the
+ * current PHP request.
+ *
+ * @return Request
+ */
+ static function getRequest() {
+
+ $r = self::createFromServerArray($_SERVER);
+ $r->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 @@
+<?php
+
+namespace Sabre\HTTP;
+
+use Sabre\URI;
+
+/**
+ * URL utility class
+ *
+ * Note: this class is deprecated. All its functionality moved to functions.php
+ * or sabre\uri.
+ *
+ * @deprectated
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class URLUtil {
+
+ /**
+ * Encodes the path of a url.
+ *
+ * slashes (/) are treated as path-separators.
+ *
+ * @deprecated use \Sabre\HTTP\encodePath()
+ * @param string $path
+ * @return string
+ */
+ static function encodePath($path) {
+
+ return encodePath($path);
+
+ }
+
+ /**
+ * Encodes a 1 segment of a path
+ *
+ * Slashes are considered part of the name, and are encoded as %2f
+ *
+ * @deprecated use \Sabre\HTTP\encodePathSegment()
+ * @param string $pathSegment
+ * @return string
+ */
+ static function encodePathSegment($pathSegment) {
+
+ return encodePathSegment($pathSegment);
+
+ }
+
+ /**
+ * Decodes a url-encoded path
+ *
+ * @deprecated use \Sabre\HTTP\decodePath
+ * @param string $path
+ * @return string
+ */
+ static function decodePath($path) {
+
+ return decodePath($path);
+
+ }
+
+ /**
+ * Decodes a url-encoded path segment
+ *
+ * @deprecated use \Sabre\HTTP\decodePathSegment()
+ * @param string $path
+ * @return string
+ */
+ static function decodePathSegment($path) {
+
+ return decodePathSegment($path);
+
+ }
+
+ /**
+ * Returns the 'dirname' and 'basename' for a path.
+ *
+ * @deprecated Use Sabre\Uri\split().
+ * @param string $path
+ * @return array
+ */
+ static function splitPath($path) {
+
+ return Uri\split($path);
+
+ }
+
+ /**
+ * Resolves relative urls, like a browser would.
+ *
+ * @deprecated Use Sabre\Uri\resolve().
+ * @param string $basePath
+ * @param string $newPath
+ * @return string
+ */
+ static function resolve($basePath, $newPath) {
+
+ return Uri\resolve($basePath, $newPath);
+
+ }
+
+}
diff --git a/vendor/sabre/http/lib/Util.php b/vendor/sabre/http/lib/Util.php
new file mode 100644
index 000000000..83bde50a4
--- /dev/null
+++ b/vendor/sabre/http/lib/Util.php
@@ -0,0 +1,74 @@
+<?php
+
+namespace Sabre\HTTP;
+
+/**
+ * HTTP utility methods
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @author Paul Voegler
+ * @deprecated All these functions moved to functions.php
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Util {
+
+ /**
+ * Content negotiation
+ *
+ * @deprecated Use \Sabre\HTTP\negotiateContentType
+ * @param string|null $acceptHeaderValue
+ * @param array $availableOptions
+ * @return string|null
+ */
+ static function negotiateContentType($acceptHeaderValue, array $availableOptions) {
+
+ return negotiateContentType($acceptHeaderValue, $availableOptions);
+
+ }
+
+ /**
+ * Deprecated! Use negotiateContentType.
+ *
+ * @deprecated Use \Sabre\HTTP\NegotiateContentType
+ * @param string|null $acceptHeader
+ * @param array $availableOptions
+ * @return string|null
+ */
+ static function negotiate($acceptHeaderValue, array $availableOptions) {
+
+ return negotiateContentType($acceptHeaderValue, $availableOptions);
+
+ }
+
+ /**
+ * Parses a RFC2616-compatible date string
+ *
+ * This method returns false if the date is invalid
+ *
+ * @deprecated Use parseDate
+ * @param string $dateHeader
+ * @return bool|DateTime
+ */
+ static function parseHTTPDate($dateHeader) {
+
+ return parseDate($dateHeader);
+
+ }
+
+ /**
+ * Transforms a DateTime object to HTTP's most common date format.
+ *
+ * We're serializing it as the RFC 1123 date, which, for HTTP must be
+ * specified as GMT.
+ *
+ * @deprecated Use toDate
+ * @param \DateTime $dateTime
+ * @return string
+ */
+ static function toHTTPDate(\DateTime $dateTime) {
+
+ return toDate($dateTime);
+
+ }
+}
diff --git a/vendor/sabre/http/lib/Version.php b/vendor/sabre/http/lib/Version.php
new file mode 100644
index 000000000..789ee4543
--- /dev/null
+++ b/vendor/sabre/http/lib/Version.php
@@ -0,0 +1,19 @@
+<?php
+
+namespace Sabre\HTTP;
+
+/**
+ * This class contains the version number for the HTTP package
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Version {
+
+ /**
+ * Full version number
+ */
+ const VERSION = '4.2.1';
+
+}
diff --git a/vendor/sabre/http/lib/functions.php b/vendor/sabre/http/lib/functions.php
new file mode 100644
index 000000000..1ec123f2e
--- /dev/null
+++ b/vendor/sabre/http/lib/functions.php
@@ -0,0 +1,445 @@
+<?php
+
+namespace Sabre\HTTP;
+
+use DateTime;
+
+/**
+ * A collection of useful helpers for parsing or generating various HTTP
+ * headers.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+
+/**
+ * Parses a HTTP date-string.
+ *
+ * This method returns false if the date is invalid.
+ *
+ * The following formats are supported:
+ * Sun, 06 Nov 1994 08:49:37 GMT ; IMF-fixdate
+ * Sunday, 06-Nov-94 08:49:37 GMT ; obsolete RFC 850 format
+ * Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format
+ *
+ * See:
+ * http://tools.ietf.org/html/rfc7231#section-7.1.1.1
+ *
+ * @param string $dateString
+ * @return bool|DateTime
+ */
+function parseDate($dateString) {
+
+ // Only the format is checked, valid ranges are checked by strtotime below
+ $month = '(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)';
+ $weekday = '(Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday)';
+ $wkday = '(Mon|Tue|Wed|Thu|Fri|Sat|Sun)';
+ $time = '([0-1]\d|2[0-3])(\:[0-5]\d){2}';
+ $date3 = $month . ' ([12]\d|3[01]| [1-9])';
+ $date2 = '(0[1-9]|[12]\d|3[01])\-' . $month . '\-\d{2}';
+ // 4-digit year cannot begin with 0 - unix timestamp begins in 1970
+ $date1 = '(0[1-9]|[12]\d|3[01]) ' . $month . ' [1-9]\d{3}';
+
+ // ANSI C's asctime() format
+ // 4-digit year cannot begin with 0 - unix timestamp begins in 1970
+ $asctime_date = $wkday . ' ' . $date3 . ' ' . $time . ' [1-9]\d{3}';
+ // RFC 850, obsoleted by RFC 1036
+ $rfc850_date = $weekday . ', ' . $date2 . ' ' . $time . ' GMT';
+ // RFC 822, updated by RFC 1123
+ $rfc1123_date = $wkday . ', ' . $date1 . ' ' . $time . ' GMT';
+ // allowed date formats by RFC 2616
+ $HTTP_date = "($rfc1123_date|$rfc850_date|$asctime_date)";
+
+ // allow for space around the string and strip it
+ $dateString = trim($dateString, ' ');
+ if (!preg_match('/^' . $HTTP_date . '$/', $dateString))
+ return false;
+
+ // append implicit GMT timezone to ANSI C time format
+ if (strpos($dateString, ' GMT') === false)
+ $dateString .= ' GMT';
+
+ try {
+ return new DateTime($dateString, new \DateTimeZone('UTC'));
+ } catch (\Exception $e) {
+ return false;
+ }
+
+}
+
+/**
+ * Transforms a DateTime object to a valid HTTP/1.1 Date header value
+ *
+ * @param DateTime $dateTime
+ * @return string
+ */
+function toDate(DateTime $dateTime) {
+
+ // We need to clone it, as we don't want to affect the existing
+ // DateTime.
+ $dateTime = clone $dateTime;
+ $dateTime->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 = <<<REGEX
+/
+^
+(?<name> $token) # Prefer property name
+\s* # Optional space
+(?: = \s* # Prefer property value
+ (?<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;
+
+}