diff --git a/composer.json b/composer.json index 4526a90506075475fd8d76dc3a2b5dc5f6e8ab15..d94ede28bbbcd4c4e83e68ebf028e967ad7227d7 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,7 @@ "twig/twig": "1.15.*", "doctrine/common": "dev-master#a45d110f71c323e29f41eb0696fa230e3fa1b1b5", "doctrine/annotations": "dev-master#463d926a8dcc49271cb7db5a08364a70ed6e3cd3", - "guzzle/http": "3.7.*", + "guzzlehttp/guzzle": "4.0.*", "kriswallsmith/assetic": "1.1.*@alpha", "symfony-cmf/routing": "1.1.*@alpha", "easyrdf/easyrdf": "0.8.*", diff --git a/composer.lock b/composer.lock index aa19f0fa3ada5c949a2e5790a65ac515fe498119..f8712885d8671df99d0219c7cd69293a8e39bb4b 100644 --- a/composer.lock +++ b/composer.lock @@ -3,7 +3,7 @@ "This file locks the dependencies of your project to a known state", "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file" ], - "hash": "5a96c248a0be5c8d138fcbf2a5bf9160", + "hash": "764a98eeaf89d9f52b9c32d343036de0", "packages": [ { "name": "doctrine/annotations", @@ -457,83 +457,41 @@ "time": "2013-12-30 22:31:37" }, { - "name": "guzzle/common", - "version": "v3.7.1", - "target-dir": "Guzzle/Common", + "name": "guzzlehttp/guzzle", + "version": "4.0.0", "source": { "type": "git", - "url": "https://github.com/guzzle/common.git", - "reference": "v3.7.1" + "url": "https://github.com/guzzle/guzzle.git", + "reference": "4063f08ca434efac12bf7a3db0d370b1c451345b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/common/zipball/v3.7.1", - "reference": "v3.7.1", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/4063f08ca434efac12bf7a3db0d370b1c451345b", + "reference": "4063f08ca434efac12bf7a3db0d370b1c451345b", "shasum": "" }, "require": { - "php": ">=5.3.2", - "symfony/event-dispatcher": ">=2.1" + "guzzlehttp/streams": "1.*", + "php": ">=5.4.0" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.7-dev" - } - }, - "autoload": { - "psr-0": { - "Guzzle\\Common": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Common libraries used by Guzzle", - "homepage": "http://guzzlephp.org/", - "keywords": [ - "collection", - "common", - "event", - "exception" - ], - "time": "2013-07-05 20:17:54" - }, - { - "name": "guzzle/http", - "version": "v3.7.1", - "target-dir": "Guzzle/Http", - "source": { - "type": "git", - "url": "https://github.com/guzzle/http.git", - "reference": "v3.7.1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/http/zipball/v3.7.1", - "reference": "v3.7.1", - "shasum": "" - }, - "require": { - "guzzle/common": "self.version", - "guzzle/parser": "self.version", - "guzzle/stream": "self.version", - "php": ">=5.3.2" - }, - "suggest": { - "ext-curl": "*" + "require-dev": { + "ext-curl": "*", + "phpunit/phpunit": "4.*", + "psr/log": "~1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.7-dev" + "dev-master": "4.0.x-dev" } }, "autoload": { - "psr-0": { - "Guzzle\\Http": "" - } + "psr-4": { + "GuzzleHttp\\": "src/" + }, + "files": [ + "src/functions.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -546,93 +504,52 @@ "homepage": "https://github.com/mtdowling" } ], - "description": "HTTP libraries used by Guzzle", + "description": "Guzzle is a PHP HTTP client library and framework for building RESTful web service clients", "homepage": "http://guzzlephp.org/", "keywords": [ - "Guzzle", "client", "curl", + "framework", "http", - "http client" + "http client", + "rest", + "web service" ], - "time": "2013-07-02 19:53:26" + "time": "2014-03-29 23:11:36" }, { - "name": "guzzle/parser", - "version": "v3.7.1", - "target-dir": "Guzzle/Parser", - "source": { - "type": "git", - "url": "https://github.com/guzzle/parser.git", - "reference": "v3.7.1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/parser/zipball/v3.7.1", - "reference": "v3.7.1", - "shasum": "" - }, - "require": { - "php": ">=5.3.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.7-dev" - } - }, - "autoload": { - "psr-0": { - "Guzzle\\Parser": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Interchangeable parsers used by Guzzle", - "homepage": "http://guzzlephp.org/", - "keywords": [ - "URI Template", - "cookie", - "http", - "message", - "url" - ], - "time": "2013-06-11 00:24:07" - }, - { - "name": "guzzle/stream", - "version": "v3.7.1", - "target-dir": "Guzzle/Stream", + "name": "guzzlehttp/streams", + "version": "1.0.0", "source": { "type": "git", - "url": "https://github.com/guzzle/stream.git", - "reference": "v3.7.1" + "url": "https://github.com/guzzle/streams.git", + "reference": "d249beffe5fa5e0da3855974bcc2dd9082069ccf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/stream/zipball/v3.7.1", - "reference": "v3.7.1", + "url": "https://api.github.com/repos/guzzle/streams/zipball/d249beffe5fa5e0da3855974bcc2dd9082069ccf", + "reference": "d249beffe5fa5e0da3855974bcc2dd9082069ccf", "shasum": "" }, "require": { - "guzzle/common": "self.version", - "php": ">=5.3.2" + "php": ">=5.4.0" }, - "suggest": { - "guzzle/http": "To convert Guzzle request objects to PHP streams" + "require-dev": { + "phpunit/phpunit": "4.*" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.7-dev" + "dev-master": "1.0.x-dev" } }, "autoload": { - "psr-0": { - "Guzzle\\Stream": "" - } + "psr-4": { + "GuzzleHttp\\Stream\\": "src/" + }, + "files": [ + "src/functions.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -645,14 +562,13 @@ "homepage": "https://github.com/mtdowling" } ], - "description": "Guzzle stream wrapper component", + "description": "Provides a simple abstraction over streams of data (Guzzle 4+)", "homepage": "http://guzzlephp.org/", "keywords": [ "Guzzle", - "component", "stream" ], - "time": "2013-06-27 00:50:43" + "time": "2014-03-25 18:31:28" }, { "name": "kriswallsmith/assetic", @@ -2143,9 +2059,9 @@ "kriswallsmith/assetic": 15, "symfony-cmf/routing": 15 }, - "platform": [ - - ], + "platform": { + "php": ">=5.4.2" + }, "platform-dev": [ ] diff --git a/core/core.services.yml b/core/core.services.yml index 53487c5b76fffb07edd0b7ad703d5ed0665018e0..f3c6cd91d1c285ec1a18839304eee31aad7f85b5 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -157,19 +157,8 @@ services: path.alias_manager: class: Drupal\Core\Path\AliasManager arguments: ['@path.alias_storage', '@path.alias_whitelist', '@language_manager'] - http_client_simpletest_subscriber: - class: Drupal\Core\Http\Plugin\SimpletestHttpRequestSubscriber http_default_client: - class: Guzzle\Http\Client - # Security consideration: we must not use the certificate authority file - # shipped with Guzzle because it can easily get outdated if a certificate - # authority is hacked. Instead, we rely on the certificate authority file - # provided by the operating system which is more likely going to be updated - # in a timely fashion. - arguments: [null, { curl.CURLOPT_TIMEOUT: 30, curl.CURLOPT_MAXREDIRS: 3, ssl.certificate_authority: system }] - calls: - - [addSubscriber, ['@http_client_simpletest_subscriber']] - - [setUserAgent, ['Drupal (+http://drupal.org/)']] + class: Drupal\Core\Http\Client theme.negotiator: class: Drupal\Core\Theme\ThemeNegotiator arguments: ['@access_check.theme', '@request_stack'] diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc index a0a603880159f81285f974e015dbc1ea6128d6ad..f1cad478f80abba4eb187dbe28ba964075f809df 100644 --- a/core/includes/install.core.inc +++ b/core/includes/install.core.inc @@ -21,7 +21,7 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Guzzle\Http\Exception\RequestException; +use GuzzleHttp\Exception\RequestException; /** * @file @@ -1580,8 +1580,8 @@ function install_retrieve_file($uri, $destination) { } try { - $request = \Drupal::httpClient()->get($uri, array('Accept' => 'text/plain')); - $data = $request->send()->getBody(TRUE); + $request = \Drupal::httpClient()->get($uri, array('headers' => array('Accept' => 'text/plain'))); + $data = $request->getBody(TRUE); if (empty($data)) { return FALSE; } @@ -1603,8 +1603,7 @@ function install_retrieve_file($uri, $destination) { */ function install_check_localization_server($uri) { try { - $request = \Drupal::httpClient()->head($uri); - $request->send(); + \Drupal::httpClient()->head($uri); return TRUE; } catch (RequestException $e) { diff --git a/core/lib/Drupal.php b/core/lib/Drupal.php index faeb0818c5d507f94c83ecfea0e989be3ca3dc29..c22b92251d1f2897a8456965aa89e91cb3fb626e 100644 --- a/core/lib/Drupal.php +++ b/core/lib/Drupal.php @@ -345,7 +345,7 @@ public static function state() { /** * Returns the default http client. * - * @return \Guzzle\Http\ClientInterface + * @return \GuzzleHttp\ClientInterface * A guzzle http client instance. */ public static function httpClient() { diff --git a/core/lib/Drupal/Core/Http/Client.php b/core/lib/Drupal/Core/Http/Client.php new file mode 100644 index 0000000000000000000000000000000000000000..f0b26a116d6e5fba7b5f278788498ade0bfcefe3 --- /dev/null +++ b/core/lib/Drupal/Core/Http/Client.php @@ -0,0 +1,51 @@ +<?php + +/** + * @file + * Contains \Drupal\Core\Http\Client. + */ + +namespace Drupal\Core\Http; + +use Drupal\Core\Http\Plugin\SimpletestHttpRequestSubscriber; +use Drupal\Component\Utility\NestedArray; +use GuzzleHttp\Client as GuzzleClient; + +/** + * Drupal default HTTP client class. + */ +class Client extends GuzzleClient { + + /** + * {@inheritdoc} + */ + public function __construct(array $config = []) { + $default_config = array( + 'defaults' => array( + 'config' => array( + 'curl' => array( + CURLOPT_TIMEOUT => 30, + CURLOPT_MAXREDIRS => 3, + ), + ), + // Security consideration: we must not use the certificate authority + // file shipped with Guzzle because it can easily get outdated if a + // certificate authority is hacked. Instead, we rely on the certificate + // authority file provided by the operating system which is more likely + // going to be updated in a timely fashion. This overrides the default + // path to the pem file bundled with Guzzle. + 'verify' => TRUE, + 'headers' => array( + 'User-Agent' => 'Drupal (+http://drupal.org/)', + ), + ), + ); + $config = NestedArray::mergeDeep($default_config, $config); + + parent::__construct($config); + + $this->getEmitter()->attach(new SimpletestHttpRequestSubscriber()); + } + + +} diff --git a/core/lib/Drupal/Core/Http/Plugin/SimpletestHttpRequestSubscriber.php b/core/lib/Drupal/Core/Http/Plugin/SimpletestHttpRequestSubscriber.php index a8de4a89d209c471fd888eb0e14a262cf2e295ca..825588ca657eb2bea748023d2a17f2e92fa219b6 100644 --- a/core/lib/Drupal/Core/Http/Plugin/SimpletestHttpRequestSubscriber.php +++ b/core/lib/Drupal/Core/Http/Plugin/SimpletestHttpRequestSubscriber.php @@ -1,4 +1,5 @@ <?php + /** * @file * Contains Drupal\Core\Http\Plugin\SimpletestHttpRequestSubscriber @@ -6,28 +7,28 @@ namespace Drupal\Core\Http\Plugin; -use Guzzle\Common\Event; -use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use GuzzleHttp\Event\BeforeEvent; +use GuzzleHttp\Event\SubscriberInterface; /** * Subscribe to HTTP requests and override the User-Agent header if the request * is being dispatched from inside a simpletest. */ -class SimpletestHttpRequestSubscriber implements EventSubscriberInterface { +class SimpletestHttpRequestSubscriber implements SubscriberInterface { /** * {@inheritdoc} */ - public static function getSubscribedEvents() - { - return array('request.before_send' => 'onBeforeSendRequest'); + public function getEvents() { + return array( + 'before' => array('onBeforeSendRequest'), + ); } - /** - * Event callback for request.before_send + * Event callback for the 'before' event */ - public function onBeforeSendRequest(Event $event) { + public function onBeforeSendRequest(BeforeEvent $event) { // If the database prefix is being used by SimpleTest to run the tests in a copied // database then set the user-agent header to the database prefix so that any // calls to other Drupal pages will run the SimpleTest prefixed database. The @@ -35,7 +36,7 @@ public function onBeforeSendRequest(Event $event) { // same time won't interfere with each other as they would if the database // prefix were stored statically in a file or database variable. if ($test_prefix = drupal_valid_test_ua()) { - $event['request']->setHeader('User-Agent', drupal_generate_test_ua($test_prefix)); + $event->getRequest()->setHeader('User-Agent', drupal_generate_test_ua($test_prefix)); } } } diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Form/OpmlFeedAdd.php b/core/modules/aggregator/lib/Drupal/aggregator/Form/OpmlFeedAdd.php index 4284ee10278b85a441a799bb23dcf62b1522cc72..b43cf314a86ba31b1ca691743689229d7a1d6fd6 100644 --- a/core/modules/aggregator/lib/Drupal/aggregator/Form/OpmlFeedAdd.php +++ b/core/modules/aggregator/lib/Drupal/aggregator/Form/OpmlFeedAdd.php @@ -11,9 +11,8 @@ use Drupal\Component\Utility\UrlHelper; use Drupal\Core\Form\FormBase; use Symfony\Component\DependencyInjection\ContainerInterface; -use Guzzle\Http\Exception\RequestException; -use Guzzle\Http\Exception\BadResponseException; -use Guzzle\Http\ClientInterface; +use GuzzleHttp\Exception\RequestException; +use GuzzleHttp\ClientInterface; /** * Imports feeds from OPML. @@ -30,7 +29,7 @@ class OpmlFeedAdd extends FormBase { /** * The HTTP client to fetch the feed data with. * - * @var \Guzzle\Http\ClientInterface + * @var \GuzzleHttp\ClientInterface */ protected $httpClient; @@ -39,7 +38,7 @@ class OpmlFeedAdd extends FormBase { * * @param \Drupal\aggregator\FeedStorageInterface $feed_storage * The feed storage. - * @param \Guzzle\Http\ClientInterface $http_client + * @param \GuzzleHttp\ClientInterface $http_client * The Guzzle HTTP client. */ public function __construct(FeedStorageInterface $feed_storage, ClientInterface $http_client) { @@ -121,15 +120,9 @@ public function submitForm(array &$form, array &$form_state) { else { // @todo Move this to a fetcher implementation. try { - $response = $this->httpClient->get($form_state['values']['remote'])->send(); + $response = $this->httpClient->get($form_state['values']['remote']); $data = $response->getBody(TRUE); } - catch (BadResponseException $e) { - $response = $e->getResponse(); - watchdog('aggregator', 'Failed to download OPML file due to "%error".', array('%error' => $response->getStatusCode() . ' ' . $response->getReasonPhrase()), WATCHDOG_WARNING); - drupal_set_message($this->t('Failed to download OPML file due to "%error".', array('%error' => $response->getStatusCode() . ' ' . $response->getReasonPhrase()))); - return; - } catch (RequestException $e) { watchdog('aggregator', 'Failed to download OPML file due to "%error".', array('%error' => $e->getMessage()), WATCHDOG_WARNING); drupal_set_message($this->t('Failed to download OPML file due to "%error".', array('%error' => $e->getMessage()))); diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Plugin/aggregator/fetcher/DefaultFetcher.php b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/aggregator/fetcher/DefaultFetcher.php index f730033a56f4160fb7ef33340ad92649dc316f52..bc2ba0a75a7c3e40928ee5f2a69a9c9ace63f818 100644 --- a/core/modules/aggregator/lib/Drupal/aggregator/Plugin/aggregator/fetcher/DefaultFetcher.php +++ b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/aggregator/fetcher/DefaultFetcher.php @@ -10,9 +10,8 @@ use Drupal\aggregator\Plugin\FetcherInterface; use Drupal\aggregator\FeedInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; -use Guzzle\Http\ClientInterface; -use Guzzle\Http\Exception\BadResponseException; -use Guzzle\Http\Exception\RequestException; +use GuzzleHttp\ClientInterface; +use GuzzleHttp\Exception\RequestException; use Symfony\Component\DependencyInjection\ContainerInterface; /** @@ -31,14 +30,14 @@ class DefaultFetcher implements FetcherInterface, ContainerFactoryPluginInterfac /** * The HTTP client to fetch the feed data with. * - * @var \Guzzle\Http\ClientInterface + * @var \GuzzleHttp\ClientInterface */ protected $httpClient; /** * Constructs a DefaultFetcher object. * - * @param \Guzzle\Http\ClientInterface $http_client + * @param \GuzzleHttp\ClientInterface $http_client * A Guzzle client object. */ public function __construct(ClientInterface $http_client) { @@ -58,7 +57,7 @@ public static function create(ContainerInterface $container, array $configuratio * {@inheritdoc} */ public function fetch(FeedInterface $feed) { - $request = $this->httpClient->get($feed->getUrl()); + $request = $this->httpClient->createRequest('GET', $feed->getUrl()); $feed->source_string = FALSE; // Generate conditional GET headers. @@ -70,7 +69,7 @@ public function fetch(FeedInterface $feed) { } try { - $response = $request->send(); + $response = $this->httpClient->send($request); // In case of a 304 Not Modified, there is no new content, so return // FALSE. @@ -79,8 +78,8 @@ public function fetch(FeedInterface $feed) { } $feed->source_string = $response->getBody(TRUE); - $feed->setEtag($response->getEtag()); - $feed->setLastModified(strtotime($response->getLastModified())); + $feed->setEtag($response->getHeader('ETag')); + $feed->setLastModified(strtotime($response->getHeader('Last-Modified'))); $feed->http_headers = $response->getHeaders(); // Update the feed URL in case of a 301 redirect. @@ -90,12 +89,6 @@ public function fetch(FeedInterface $feed) { } return TRUE; } - catch (BadResponseException $e) { - $response = $e->getResponse(); - watchdog('aggregator', 'The feed from %site seems to be broken because of error "%error".', array('%site' => $feed->label(), '%error' => $response->getStatusCode() . ' ' . $response->getReasonPhrase()), WATCHDOG_WARNING); - drupal_set_message(t('The feed from %site seems to be broken because of error "%error".', array('%site' => $feed->label(), '%error' => $response->getStatusCode() . ' ' . $response->getReasonPhrase()))); - return FALSE; - } catch (RequestException $e) { watchdog('aggregator', 'The feed from %site seems to be broken because of error "%error".', array('%site' => $feed->label(), '%error' => $e->getMessage()), WATCHDOG_WARNING); drupal_set_message(t('The feed from %site seems to be broken because of error "%error".', array('%site' => $feed->label(), '%error' => $e->getMessage()))); diff --git a/core/modules/aggregator/tests/modules/aggregator_test/lib/Drupal/aggregator_test/Plugin/aggregator/fetcher/TestFetcher.php b/core/modules/aggregator/tests/modules/aggregator_test/lib/Drupal/aggregator_test/Plugin/aggregator/fetcher/TestFetcher.php index 8aaf8e0170ee1080752097ed0316b425fa372f5e..8c06a96d0d4ffab099a3429a9ccd47676a2d5f85 100644 --- a/core/modules/aggregator/tests/modules/aggregator_test/lib/Drupal/aggregator_test/Plugin/aggregator/fetcher/TestFetcher.php +++ b/core/modules/aggregator/tests/modules/aggregator_test/lib/Drupal/aggregator_test/Plugin/aggregator/fetcher/TestFetcher.php @@ -10,7 +10,6 @@ use Drupal\aggregator\Plugin\FetcherInterface; use Drupal\aggregator\Plugin\aggregator\fetcher\DefaultFetcher; use Drupal\aggregator\FeedInterface; -use Guzzle\Http\Exception\BadResponseException; /** * Defines a test fetcher implementation. diff --git a/core/modules/hal/lib/Drupal/hal/Normalizer/FileEntityNormalizer.php b/core/modules/hal/lib/Drupal/hal/Normalizer/FileEntityNormalizer.php index 03c0292d24daeb3d365002158bebedd5d936d5a7..18ddf99d099f2b881542edc62be648ea449e7a52 100644 --- a/core/modules/hal/lib/Drupal/hal/Normalizer/FileEntityNormalizer.php +++ b/core/modules/hal/lib/Drupal/hal/Normalizer/FileEntityNormalizer.php @@ -10,7 +10,7 @@ use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\rest\LinkManager\LinkManagerInterface; -use Guzzle\Http\ClientInterface; +use GuzzleHttp\ClientInterface; /** * Converts the Drupal entity object structure to a HAL array structure. @@ -27,7 +27,7 @@ class FileEntityNormalizer extends ContentEntityNormalizer { /** * The HTTP client. * - * @var \Guzzle\Http\ClientInterface + * @var \GuzzleHttp\ClientInterface */ protected $httpClient; @@ -36,7 +36,7 @@ class FileEntityNormalizer extends ContentEntityNormalizer { * * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager * The entity manager. - * @param \Guzzle\Http\ClientInterface $http_client + * @param \GuzzleHttp\ClientInterface $http_client * The HTTP Client. * @param \Drupal\rest\LinkManager\LinkManagerInterface $link_manager * The hypermedia link manager. @@ -64,9 +64,7 @@ public function normalize($entity, $format = NULL, array $context = array()) { * {@inheritdoc} */ public function denormalize($data, $class, $format = NULL, array $context = array()) { - $file_data = $this->httpClient->get($data['uri'][0]['value']) - ->send() - ->getBody(TRUE); + $file_data = $this->httpClient->get($data['uri'][0]['value'])->getBody(TRUE); $path = 'temporary://' . drupal_basename($data['uri'][0]['value']); $data['uri'] = file_unmanaged_save_data($file_data, $path); diff --git a/core/modules/locale/locale.batch.inc b/core/modules/locale/locale.batch.inc index 5085a2bf5d67db0b4640903d17464edd75b4af1e..6dfcae6364b16074963b0c40b8523cdcfd0856f3 100644 --- a/core/modules/locale/locale.batch.inc +++ b/core/modules/locale/locale.batch.inc @@ -5,8 +5,7 @@ * Batch process to check the availability of remote or local po files. */ -use Guzzle\Http\Exception\BadResponseException; -use Guzzle\Http\Exception\RequestException; +use GuzzleHttp\Exception\RequestException; /** * Load the common translation API. @@ -231,9 +230,7 @@ function locale_translation_batch_fetch_finished($success, $results) { function locale_translation_http_check($uri) { try { - $response = \Drupal::httpClient() - ->head($uri) - ->send(); + $response = \Drupal::httpClient()->head($uri); $result = array(); // Return the effective URL if it differs from the requested. @@ -241,10 +238,10 @@ function locale_translation_http_check($uri) { $result['location'] = $response->getEffectiveUrl(); } - $result['last_modified'] = $response->getLastModified() ? strtotime($response->getLastModified()) : 0; + $result['last_modified'] = $response->hasHeader('Last-Modified') ? strtotime($response->getHeader('Last-Modified')) : 0; return $result; } - catch (BadResponseException $e) { + catch (RequestException $e) { // Handle 4xx and 5xx http responses. $response = $e->getResponse(); if ($response->getStatusCode() == 404) { @@ -258,11 +255,7 @@ function locale_translation_http_check($uri) { } watchdog('locale', 'HTTP request to @url failed with error: @error.', array('@url' => $uri, '@error' => $response->getStatusCode() . ' ' . $response->getReasonPhrase())); } - catch (RequestException $e) { - // Handle connection problems and cURL specific errors (CurlException) and - // other http related errors. - watchdog('locale', 'HTTP request to @url failed with error: @error.', array('@url' => $uri, '@error' => $e->getMessage())); - } + return FALSE; } diff --git a/core/modules/statistics/lib/Drupal/statistics/Tests/StatisticsAdminTest.php b/core/modules/statistics/lib/Drupal/statistics/Tests/StatisticsAdminTest.php index e8abd34066a8de2e277fb180ae39db7063b08baa..ad68b72f3f76bcbf7c384027cf6705b41a52b076 100644 --- a/core/modules/statistics/lib/Drupal/statistics/Tests/StatisticsAdminTest.php +++ b/core/modules/statistics/lib/Drupal/statistics/Tests/StatisticsAdminTest.php @@ -40,7 +40,7 @@ class StatisticsAdminTest extends WebTestBase { /** * The Guzzle HTTP client. * - * @var \Guzzle\Http\ClientInterface; + * @var \GuzzleHttp\ClientInterface; */ protected $client; @@ -63,7 +63,7 @@ function setUp() { $this->drupalLogin($this->privileged_user); $this->test_node = $this->drupalCreateNode(array('type' => 'page', 'uid' => $this->privileged_user->id())); $this->client = \Drupal::httpClient(); - $this->client->setConfig(array('curl.options' => array(CURLOPT_TIMEOUT => 10))); + $this->client->setDefaultOption('config/curl', array(CURLOPT_TIMEOUT => 10)); } /** @@ -86,16 +86,16 @@ function testStatisticsSettings() { $post = array('nid' => $nid); global $base_url; $stats_path = $base_url . '/' . drupal_get_path('module', 'statistics'). '/statistics.php'; - $this->client->post($stats_path, array(), $post)->send(); + $this->client->post($stats_path, array('body' => $post)); // Hit the node again (the counter is incremented after the hit, so // "1 view" will actually be shown when the node is hit the second time). $this->drupalGet('node/' . $this->test_node->id()); - $this->client->post($stats_path, array(), $post)->send(); + $this->client->post($stats_path, array('body' => $post)); $this->assertText('1 view', 'Node is viewed once.'); $this->drupalGet('node/' . $this->test_node->id()); - $this->client->post($stats_path, array(), $post)->send(); + $this->client->post($stats_path, array('body' => $post)); $this->assertText('2 views', 'Node is viewed 2 times.'); } @@ -111,7 +111,7 @@ function testDeleteNode() { $post = array('nid' => $nid); global $base_url; $stats_path = $base_url . '/' . drupal_get_path('module', 'statistics'). '/statistics.php'; - $this->client->post($stats_path, array(), $post)->send(); + $this->client->post($stats_path, array('body' => $post)); $result = db_select('node_counter', 'n') ->fields('n', array('nid')) @@ -145,9 +145,9 @@ function testExpiredLogs() { $post = array('nid' => $nid); global $base_url; $stats_path = $base_url . '/' . drupal_get_path('module', 'statistics'). '/statistics.php'; - $this->client->post($stats_path, array(), $post)->send(); + $this->client->post($stats_path, array('body' => $post)); $this->drupalGet('node/' . $this->test_node->id()); - $this->client->post($stats_path, array(), $post)->send(); + $this->client->post($stats_path, array('body' => $post)); $this->assertText('1 view', 'Node is viewed once.'); // statistics_cron() will subtract diff --git a/core/modules/statistics/lib/Drupal/statistics/Tests/StatisticsLoggingTest.php b/core/modules/statistics/lib/Drupal/statistics/Tests/StatisticsLoggingTest.php index 6177b863cb1f4923fd033bcc1646266d5ce06ee8..3effaf59360e180c703d21c4f9017f040cf9d74a 100644 --- a/core/modules/statistics/lib/Drupal/statistics/Tests/StatisticsLoggingTest.php +++ b/core/modules/statistics/lib/Drupal/statistics/Tests/StatisticsLoggingTest.php @@ -27,7 +27,7 @@ class StatisticsLoggingTest extends WebTestBase { /** * The Guzzle HTTP client. * - * @var \Guzzle\Http\ClientInterface; + * @var \GuzzleHttp\ClientInterface; */ protected $client; @@ -61,7 +61,7 @@ function setUp() { db_truncate('node_counter'); $this->client = \Drupal::httpClient(); - $this->client->setConfig(array('curl.options' => array(CURLOPT_TIMEOUT => 10))); + $this->client->setDefaultOption('config/curl', array(CURLOPT_TIMEOUT => 10)); } /** @@ -93,7 +93,7 @@ function testLogging() { // Manually call statistics.php to simulate ajax data collection behavior. $nid = $this->node->id(); $post = array('nid' => $nid); - $this->client->post($stats_path, array(), $post)->send(); + $this->client->post($stats_path, array('body' => $post)); $node_counter = statistics_get($this->node->id()); $this->assertIdentical($node_counter['totalcount'], '1'); } diff --git a/core/modules/statistics/lib/Drupal/statistics/Tests/StatisticsReportsTest.php b/core/modules/statistics/lib/Drupal/statistics/Tests/StatisticsReportsTest.php index e1dc9f6ada3f5dd1ea58db425bb0d4e1a2b15eee..3d8309aca97b9fbc522cc48512dc40d5caab0423 100644 --- a/core/modules/statistics/lib/Drupal/statistics/Tests/StatisticsReportsTest.php +++ b/core/modules/statistics/lib/Drupal/statistics/Tests/StatisticsReportsTest.php @@ -37,8 +37,8 @@ function testPopularContentBlock() { global $base_url; $stats_path = $base_url . '/' . drupal_get_path('module', 'statistics'). '/statistics.php'; $client = \Drupal::httpClient(); - $client->setConfig(array('curl.options' => array(CURLOPT_TIMEOUT => 10))); - $client->post($stats_path, $headers, $post)->send(); + $client->setDefaultOption('config/curl', array(CURLOPT_TIMEOUT => 10)); + $client->post($stats_path, array('headers' => $headers, 'body' => $post)); // Configure and save the block. $this->drupalPlaceBlock('statistics_popular_block', array( diff --git a/core/modules/statistics/lib/Drupal/statistics/Tests/StatisticsTokenReplaceTest.php b/core/modules/statistics/lib/Drupal/statistics/Tests/StatisticsTokenReplaceTest.php index 712c5ab21a4722b5c3cf2ce904e1038a5e4d3c7b..16c1fda12469ab9b09013f35b60163c5fe8e73e4 100644 --- a/core/modules/statistics/lib/Drupal/statistics/Tests/StatisticsTokenReplaceTest.php +++ b/core/modules/statistics/lib/Drupal/statistics/Tests/StatisticsTokenReplaceTest.php @@ -41,8 +41,8 @@ function testStatisticsTokenReplacement() { global $base_url; $stats_path = $base_url . '/' . drupal_get_path('module', 'statistics'). '/statistics.php'; $client = \Drupal::httpClient(); - $client->setConfig(array('curl.options' => array(CURLOPT_TIMEOUT => 10))); - $client->post($stats_path, $headers, $post)->send(); + $client->setDefaultOption('config/curl', array(CURLOPT_TIMEOUT => 10)); + $client->post($stats_path, array('headers' => $headers, 'body' => $post)); $statistics = statistics_get($node->id()); // Generate and test tokens. diff --git a/core/modules/statistics/lib/Drupal/statistics/Tests/Views/IntegrationTest.php b/core/modules/statistics/lib/Drupal/statistics/Tests/Views/IntegrationTest.php index 7d3e567f381265aec07a5a78e0ac09882ae217b7..f7fec462034faa5eb0a293984e183ca1c1acd94a 100644 --- a/core/modules/statistics/lib/Drupal/statistics/Tests/Views/IntegrationTest.php +++ b/core/modules/statistics/lib/Drupal/statistics/Tests/Views/IntegrationTest.php @@ -83,8 +83,8 @@ public function testNodeCounterIntegration() { global $base_url; $stats_path = $base_url . '/' . drupal_get_path('module', 'statistics'). '/statistics.php'; $client = \Drupal::httpClient(); - $client->setConfig(array('curl.options' => array(CURLOPT_TIMEOUT => 10))); - $client->post($stats_path, array(), array('nid' => $this->node->id()))->send(); + $client->setDefaultOption('config/curl', array(CURLOPT_TIMEOUT => 10)); + $client->post($stats_path, array('body' => array('nid' => $this->node->id()))); $this->drupalGet('test_statistics_integration'); $expected = statistics_get($this->node->id()); diff --git a/core/modules/system/system.module b/core/modules/system/system.module index ad8c0844bb49cbe360bb4a2c19b33fb521a97111..541168da5b50e579453ecef55c7f93fd606b1286 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -12,8 +12,7 @@ use Drupal\block\BlockPluginInterface; use Drupal\user\UserInterface; use Symfony\Component\HttpFoundation\RedirectResponse; -use Guzzle\Http\Exception\BadResponseException; -use Guzzle\Http\Exception\RequestException; +use GuzzleHttp\Exception\RequestException; /** * Maximum age of temporary files in seconds. @@ -1677,15 +1676,9 @@ function system_retrieve_file($url, $destination = NULL, $managed = FALSE, $repl try { $data = \Drupal::httpClient() ->get($url) - ->send() ->getBody(TRUE); $local = $managed ? file_save_data($data, $path, $replace) : file_unmanaged_save_data($data, $path, $replace); } - catch (BadResponseException $exception) { - $response = $exception->getResponse(); - drupal_set_message(t('Failed to fetch file due to HTTP error "%error"', array('%error' => $response->getStatusCode() . ' ' . $response->getReasonPhrase())), 'error'); - return FALSE; - } catch (RequestException $exception) { drupal_set_message(t('Failed to fetch file due to error "%error"', array('%error' => $exception->getMessage())), 'error'); return FALSE; diff --git a/core/modules/update/lib/Drupal/update/UpdateFetcher.php b/core/modules/update/lib/Drupal/update/UpdateFetcher.php index 394810ed1749c65d87d9015a76e3db55632b8143..14bdb29336ec826ff9126f75fc8a40c13e67b2ff 100644 --- a/core/modules/update/lib/Drupal/update/UpdateFetcher.php +++ b/core/modules/update/lib/Drupal/update/UpdateFetcher.php @@ -8,8 +8,8 @@ namespace Drupal\update; use Drupal\Core\Config\ConfigFactoryInterface; -use Guzzle\Http\ClientInterface; -use Guzzle\Http\Exception\RequestException; +use GuzzleHttp\ClientInterface; +use GuzzleHttp\Exception\RequestException; /** * Fetches project information from remote locations. @@ -38,7 +38,7 @@ class UpdateFetcher implements UpdateFetcherInterface { /** * The HTTP client to fetch the feed data with. * - * @var \Guzzle\Http\ClientInterface + * @var \GuzzleHttp\ClientInterface */ protected $httpClient; @@ -47,7 +47,7 @@ class UpdateFetcher implements UpdateFetcherInterface { * * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory * The config factory. - * @param \Guzzle\Http\ClientInterface $http_client + * @param \GuzzleHttp\ClientInterface $http_client * A Guzzle client object. */ public function __construct(ConfigFactoryInterface $config_factory, ClientInterface $http_client) { @@ -64,8 +64,7 @@ public function fetchProjectData(array $project, $site_key = '') { $data = ''; try { $data = $this->httpClient - ->get($url, array('Accept' => 'text/xml')) - ->send() + ->get($url, array('headers' => array('Accept' => 'text/xml'))) ->getBody(TRUE); } catch (RequestException $exception) { diff --git a/core/modules/update/tests/Drupal/update/Tests/UpdateFetcherTest.php b/core/modules/update/tests/Drupal/update/Tests/UpdateFetcherTest.php index e43a3ae47381d3c614b0d635a7d4f433da61b25f..4a3191614d99be788d348d1f72f5d018645220fe 100644 --- a/core/modules/update/tests/Drupal/update/Tests/UpdateFetcherTest.php +++ b/core/modules/update/tests/Drupal/update/Tests/UpdateFetcherTest.php @@ -39,7 +39,10 @@ public static function getInfo() { */ protected function setUp() { $config_factory = $this->getConfigFactoryStub(array('update.settings' => array('fetch_url' => 'http://www.example.com'))); - $this->updateFetcher = new UpdateFetcher($config_factory, $this->getMock('Guzzle\Http\Client')); + $http_client_mock = $this->getMockBuilder('Drupal\Core\Http\Client') + ->disableOriginalConstructor() + ->getMock(); + $this->updateFetcher = new UpdateFetcher($config_factory, $http_client_mock); } /** diff --git a/core/modules/xmlrpc/xmlrpc.inc b/core/modules/xmlrpc/xmlrpc.inc index 445993a1f0db7e6bd8e5a04daf09a4e60411152c..cb2180e114ea00a21401fb07b8af02d084a0a07a 100644 --- a/core/modules/xmlrpc/xmlrpc.inc +++ b/core/modules/xmlrpc/xmlrpc.inc @@ -11,8 +11,7 @@ * This version is made available under the GNU GPL License */ -use Guzzle\Http\Exception\BadResponseException; -use Guzzle\Http\Exception\RequestException; +use GuzzleHttp\Exception\RequestException; /** * Turns a data structure into objects with 'data' and 'type' attributes. @@ -561,15 +560,11 @@ function _xmlrpc($url, array $args, array $headers = array()) { $args = $args[$method]; } $xmlrpc_request = xmlrpc_request($method, $args); - $request = \Drupal::httpClient()->post($url, $headers, $xmlrpc_request->xml); - $request->setHeader('Content-Type', 'text/xml'); + + $headers['Content-Type'] = 'text/xml'; + try { - $response = $request->send(); - } - catch (BadResponseException $exception) { - $response = $exception->getResponse(); - xmlrpc_error($response->getStatusCode(), $response->getReasonPhrase()); - return FALSE; + $response = \Drupal::httpClient()->post($url, array('headers' => $headers, 'body' => $xmlrpc_request->xml)); } catch (RequestException $exception) { xmlrpc_error(NULL, $exception->getMethod()); diff --git a/core/tests/Drupal/Tests/Core/Cache/CacheContextsTest.php b/core/tests/Drupal/Tests/Core/Cache/CacheContextsTest.php index 6e5864de35be6a09194a71f141c6e62761eda449..e4309659bcf6d4d5574b5fdf72762856ab6cd5d0 100644 --- a/core/tests/Drupal/Tests/Core/Cache/CacheContextsTest.php +++ b/core/tests/Drupal/Tests/Core/Cache/CacheContextsTest.php @@ -9,6 +9,7 @@ use Drupal\Core\Cache\CacheContexts; use Drupal\Core\Cache\CacheContextInterface; +use Drupal\Tests\UnitTestCase; /** * Fake cache context class. @@ -38,7 +39,7 @@ public function getContext() { * * @see \Drupal\Core\Cache\CacheContexts */ -class CacheContextsTest extends \PHPUnit_Framework_TestCase { +class CacheContextsTest extends UnitTestCase { public static function getInfo() { return array( diff --git a/core/vendor/composer/ClassLoader.php b/core/vendor/composer/ClassLoader.php index 47ae2ee9252a066b42b9e53778243d80466a3cbf..a7105553143a422546083f2bb49dd6a79a280a6a 100644 --- a/core/vendor/composer/ClassLoader.php +++ b/core/vendor/composer/ClassLoader.php @@ -291,8 +291,25 @@ public function findFile($class) return $this->classMap[$class]; } + $file = $this->findFileWithExtension($class, '.php'); + + // Search for Hack files if we are running on HHVM + if ($file === null && defined('HHVM_VERSION')) { + $file = $this->findFileWithExtension($class, '.hh'); + } + + if ($file === null) { + // Remember that this class does not exist. + return $this->classMap[$class] = false; + } + + return $file; + } + + private function findFileWithExtension($class, $ext) + { // PSR-4 lookup - $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . '.php'; + $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; $first = $class[0]; if (isset($this->prefixLengthsPsr4[$first])) { @@ -321,7 +338,7 @@ public function findFile($class) . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); } else { // PEAR-like class name - $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . '.php'; + $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; } if (isset($this->prefixesPsr0[$first])) { @@ -347,9 +364,6 @@ public function findFile($class) if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { return $file; } - - // Remember that this class does not exist. - return $this->classMap[$class] = false; } } diff --git a/core/vendor/composer/autoload_files.php b/core/vendor/composer/autoload_files.php index 2ebcd822ecfbc396f4edb6472f4e815cade8fe87..2ca5d44933d191b4d91a1250d7a9b647ae95c293 100644 --- a/core/vendor/composer/autoload_files.php +++ b/core/vendor/composer/autoload_files.php @@ -6,6 +6,8 @@ $baseDir = dirname(dirname($vendorDir)); return array( + $vendorDir . '/guzzlehttp/streams/src/functions.php', $vendorDir . '/kriswallsmith/assetic/src/functions.php', + $vendorDir . '/guzzlehttp/guzzle/src/functions.php', $baseDir . '/core/lib/Drupal.php', ); diff --git a/core/vendor/composer/autoload_namespaces.php b/core/vendor/composer/autoload_namespaces.php index 83fb6eda09c2f1f77cc21467eb141d54660e5165..80f7f7a604eb75821473f0d51beb29894033d48d 100644 --- a/core/vendor/composer/autoload_namespaces.php +++ b/core/vendor/composer/autoload_namespaces.php @@ -25,10 +25,6 @@ 'Symfony\\Component\\ClassLoader\\' => array($vendorDir . '/symfony/class-loader'), 'Symfony\\Cmf\\Component\\Routing' => array($vendorDir . '/symfony-cmf/routing'), 'Psr\\Log\\' => array($vendorDir . '/psr/log'), - 'Guzzle\\Stream' => array($vendorDir . '/guzzle/stream'), - 'Guzzle\\Parser' => array($vendorDir . '/guzzle/parser'), - 'Guzzle\\Http' => array($vendorDir . '/guzzle/http'), - 'Guzzle\\Common' => array($vendorDir . '/guzzle/common'), 'Gliph' => array($vendorDir . '/sdboyer/gliph/src'), 'EasyRdf_' => array($vendorDir . '/easyrdf/easyrdf/lib'), 'Drupal\\Driver' => array($baseDir . '/drivers/lib'), diff --git a/core/vendor/composer/autoload_psr4.php b/core/vendor/composer/autoload_psr4.php index 80607ee9774a26436d34c268484ce0e451501438..e53db415a08fe4d77b1f4c597a2b48f3844a344a 100644 --- a/core/vendor/composer/autoload_psr4.php +++ b/core/vendor/composer/autoload_psr4.php @@ -6,4 +6,6 @@ $baseDir = dirname(dirname($vendorDir)); return array( + 'GuzzleHttp\\Stream\\' => array($vendorDir . '/guzzlehttp/streams/src'), + 'GuzzleHttp\\' => array($vendorDir . '/guzzlehttp/guzzle/src'), ); diff --git a/core/vendor/composer/installed.json b/core/vendor/composer/installed.json index a0016750da85b49d2c41bce32df79def5fc452c9..ed8df0cbbb6c34e7f79ecc48292650cf91ac5355 100644 --- a/core/vendor/composer/installed.json +++ b/core/vendor/composer/installed.json @@ -2199,5 +2199,123 @@ ], "description": "Symfony Yaml Component", "homepage": "http://symfony.com" + }, + { + "name": "guzzlehttp/streams", + "version": "1.0.0", + "version_normalized": "1.0.0.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/streams.git", + "reference": "d249beffe5fa5e0da3855974bcc2dd9082069ccf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/streams/zipball/d249beffe5fa5e0da3855974bcc2dd9082069ccf", + "reference": "d249beffe5fa5e0da3855974bcc2dd9082069ccf", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "4.*" + }, + "time": "2014-03-25 18:31:28", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "GuzzleHttp\\Stream\\": "src/" + }, + "files": [ + "src/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Provides a simple abstraction over streams of data (Guzzle 4+)", + "homepage": "http://guzzlephp.org/", + "keywords": [ + "Guzzle", + "stream" + ] + }, + { + "name": "guzzlehttp/guzzle", + "version": "4.0.0", + "version_normalized": "4.0.0.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "4063f08ca434efac12bf7a3db0d370b1c451345b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/4063f08ca434efac12bf7a3db0d370b1c451345b", + "reference": "4063f08ca434efac12bf7a3db0d370b1c451345b", + "shasum": "" + }, + "require": { + "guzzlehttp/streams": "1.*", + "php": ">=5.4.0" + }, + "require-dev": { + "ext-curl": "*", + "phpunit/phpunit": "4.*", + "psr/log": "~1" + }, + "time": "2014-03-29 23:11:36", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "GuzzleHttp\\": "src/" + }, + "files": [ + "src/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Guzzle is a PHP HTTP client library and framework for building RESTful web service clients", + "homepage": "http://guzzlephp.org/", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "rest", + "web service" + ] } ] diff --git a/core/vendor/guzzle/common/Guzzle/Common/AbstractHasDispatcher.php b/core/vendor/guzzle/common/Guzzle/Common/AbstractHasDispatcher.php deleted file mode 100644 index 9c6874fb4e4b359c2324158e68374da40954c294..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/common/Guzzle/Common/AbstractHasDispatcher.php +++ /dev/null @@ -1,49 +0,0 @@ -<?php - -namespace Guzzle\Common; - -use Symfony\Component\EventDispatcher\EventDispatcher; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; -use Symfony\Component\EventDispatcher\EventSubscriberInterface; - -/** - * Class that holds an event dispatcher - */ -class AbstractHasDispatcher implements HasDispatcherInterface -{ - /** @var EventDispatcherInterface */ - protected $eventDispatcher; - - public static function getAllEvents() - { - return array(); - } - - public function setEventDispatcher(EventDispatcherInterface $eventDispatcher) - { - $this->eventDispatcher = $eventDispatcher; - - return $this; - } - - public function getEventDispatcher() - { - if (!$this->eventDispatcher) { - $this->eventDispatcher = new EventDispatcher(); - } - - return $this->eventDispatcher; - } - - public function dispatch($eventName, array $context = array()) - { - $this->getEventDispatcher()->dispatch($eventName, new Event($context)); - } - - public function addSubscriber(EventSubscriberInterface $subscriber) - { - $this->getEventDispatcher()->addSubscriber($subscriber); - - return $this; - } -} diff --git a/core/vendor/guzzle/common/Guzzle/Common/Collection.php b/core/vendor/guzzle/common/Guzzle/Common/Collection.php deleted file mode 100644 index 5cb1535d07a7074e00ec882d9f5b0f5333736dbe..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/common/Guzzle/Common/Collection.php +++ /dev/null @@ -1,403 +0,0 @@ -<?php - -namespace Guzzle\Common; - -use Guzzle\Common\Exception\InvalidArgumentException; -use Guzzle\Common\Exception\RuntimeException; - -/** - * Key value pair collection object - */ -class Collection implements \ArrayAccess, \IteratorAggregate, \Countable, ToArrayInterface -{ - /** @var array Data associated with the object. */ - protected $data; - - /** - * @param array $data Associative array of data to set - */ - public function __construct(array $data = array()) - { - $this->data = $data; - } - - /** - * Create a new collection from an array, validate the keys, and add default values where missing - * - * @param array $config Configuration values to apply. - * @param array $defaults Default parameters - * @param array $required Required parameter names - * - * @return self - * @throws InvalidArgumentException if a parameter is missing - */ - public static function fromConfig(array $config = array(), array $defaults = array(), array $required = array()) - { - $data = $config + $defaults; - - if ($missing = array_diff($required, array_keys($data))) { - throw new InvalidArgumentException('Config is missing the following keys: ' . implode(', ', $missing)); - } - - return new self($data); - } - - public function count() - { - return count($this->data); - } - - public function getIterator() - { - return new \ArrayIterator($this->data); - } - - public function toArray() - { - return $this->data; - } - - /** - * Removes all key value pairs - * - * @return Collection - */ - public function clear() - { - $this->data = array(); - - return $this; - } - - /** - * Get all or a subset of matching key value pairs - * - * @param array $keys Pass an array of keys to retrieve only a subset of key value pairs - * - * @return array Returns an array of all matching key value pairs - */ - public function getAll(array $keys = null) - { - return $keys ? array_intersect_key($this->data, array_flip($keys)) : $this->data; - } - - /** - * Get a specific key value. - * - * @param string $key Key to retrieve. - * - * @return mixed|null Value of the key or NULL - */ - public function get($key) - { - return isset($this->data[$key]) ? $this->data[$key] : null; - } - - /** - * Set a key value pair - * - * @param string $key Key to set - * @param mixed $value Value to set - * - * @return Collection Returns a reference to the object - */ - public function set($key, $value) - { - $this->data[$key] = $value; - - return $this; - } - - /** - * Add a value to a key. If a key of the same name has already been added, the key value will be converted into an - * array and the new value will be pushed to the end of the array. - * - * @param string $key Key to add - * @param mixed $value Value to add to the key - * - * @return Collection Returns a reference to the object. - */ - public function add($key, $value) - { - if (!array_key_exists($key, $this->data)) { - $this->data[$key] = $value; - } elseif (is_array($this->data[$key])) { - $this->data[$key][] = $value; - } else { - $this->data[$key] = array($this->data[$key], $value); - } - - return $this; - } - - /** - * Remove a specific key value pair - * - * @param string $key A key to remove - * - * @return Collection - */ - public function remove($key) - { - unset($this->data[$key]); - - return $this; - } - - /** - * Get all keys in the collection - * - * @return array - */ - public function getKeys() - { - return array_keys($this->data); - } - - /** - * Returns whether or not the specified key is present. - * - * @param string $key The key for which to check the existence. - * - * @return bool - */ - public function hasKey($key) - { - return array_key_exists($key, $this->data); - } - - /** - * Case insensitive search the keys in the collection - * - * @param string $key Key to search for - * - * @return bool|string Returns false if not found, otherwise returns the key - */ - public function keySearch($key) - { - foreach (array_keys($this->data) as $k) { - if (!strcasecmp($k, $key)) { - return $k; - } - } - - return false; - } - - /** - * Checks if any keys contains a certain value - * - * @param string $value Value to search for - * - * @return mixed Returns the key if the value was found FALSE if the value was not found. - */ - public function hasValue($value) - { - return array_search($value, $this->data); - } - - /** - * Replace the data of the object with the value of an array - * - * @param array $data Associative array of data - * - * @return Collection Returns a reference to the object - */ - public function replace(array $data) - { - $this->data = $data; - - return $this; - } - - /** - * Add and merge in a Collection or array of key value pair data. - * - * @param Collection|array $data Associative array of key value pair data - * - * @return Collection Returns a reference to the object. - */ - public function merge($data) - { - foreach ($data as $key => $value) { - $this->add($key, $value); - } - - return $this; - } - - /** - * Over write key value pairs in this collection with all of the data from an array or collection. - * - * @param array|\Traversable $data Values to override over this config - * - * @return self - */ - public function overwriteWith($data) - { - if (is_array($data)) { - $this->data = $data + $this->data; - } elseif ($data instanceof Collection) { - $this->data = $data->toArray() + $this->data; - } else { - foreach ($data as $key => $value) { - $this->data[$key] = $value; - } - } - - return $this; - } - - /** - * Returns a Collection containing all the elements of the collection after applying the callback function to each - * one. The Closure should accept three parameters: (string) $key, (string) $value, (array) $context and return a - * modified value - * - * @param \Closure $closure Closure to apply - * @param array $context Context to pass to the closure - * @param bool $static Set to TRUE to use the same class as the return rather than returning a Collection - * - * @return Collection - */ - public function map(\Closure $closure, array $context = array(), $static = true) - { - $collection = $static ? new static() : new self(); - foreach ($this as $key => $value) { - $collection->add($key, $closure($key, $value, $context)); - } - - return $collection; - } - - /** - * Iterates over each key value pair in the collection passing them to the Closure. If the Closure function returns - * true, the current value from input is returned into the result Collection. The Closure must accept three - * parameters: (string) $key, (string) $value and return Boolean TRUE or FALSE for each value. - * - * @param \Closure $closure Closure evaluation function - * @param bool $static Set to TRUE to use the same class as the return rather than returning a Collection - * - * @return Collection - */ - public function filter(\Closure $closure, $static = true) - { - $collection = ($static) ? new static() : new self(); - foreach ($this->data as $key => $value) { - if ($closure($key, $value)) { - $collection->add($key, $value); - } - } - - return $collection; - } - - public function offsetExists($offset) - { - return isset($this->data[$offset]); - } - - public function offsetGet($offset) - { - return isset($this->data[$offset]) ? $this->data[$offset] : null; - } - - public function offsetSet($offset, $value) - { - $this->data[$offset] = $value; - } - - public function offsetUnset($offset) - { - unset($this->data[$offset]); - } - - /** - * Set a value into a nested array key. Keys will be created as needed to set the value. - * - * @param string $path Path to set - * @param mixed $value Value to set at the key - * - * @return self - * @throws RuntimeException when trying to setPath using a nested path that travels through a scalar value - */ - public function setPath($path, $value) - { - $current =& $this->data; - $queue = explode('/', $path); - while (null !== ($key = array_shift($queue))) { - if (!is_array($current)) { - throw new RuntimeException("Trying to setPath {$path}, but {$key} is set and is not an array"); - } elseif (!$queue) { - $current[$key] = $value; - } elseif (isset($current[$key])) { - $current =& $current[$key]; - } else { - $current[$key] = array(); - $current =& $current[$key]; - } - } - - return $this; - } - - /** - * Gets a value from the collection using an array path (e.g. foo/baz/bar would retrieve bar from two nested arrays) - * Allows for wildcard searches which recursively combine matches up to the level at which the wildcard occurs. This - * can be useful for accepting any key of a sub-array and combining matching keys from each diverging path. - * - * @param string $path Path to traverse and retrieve a value from - * @param string $separator Character used to add depth to the search - * @param mixed $data Optional data to descend into (used when wildcards are encountered) - * - * @return mixed|null - */ - public function getPath($path, $separator = '/', $data = null) - { - if ($data === null) { - $data =& $this->data; - } - - $path = is_array($path) ? $path : explode($separator, $path); - while (null !== ($part = array_shift($path))) { - if (!is_array($data)) { - return null; - } elseif (isset($data[$part])) { - $data =& $data[$part]; - } elseif ($part != '*') { - return null; - } else { - // Perform a wildcard search by diverging and merging paths - $result = array(); - foreach ($data as $value) { - if (!$path) { - $result = array_merge_recursive($result, (array) $value); - } elseif (null !== ($test = $this->getPath($path, $separator, $value))) { - $result = array_merge_recursive($result, (array) $test); - } - } - return $result; - } - } - - return $data; - } - - /** - * Inject configuration settings into an input string - * - * @param string $input Input to inject - * - * @return string - * @deprecated - */ - public function inject($input) - { - Version::warn(__METHOD__ . ' is deprecated'); - $replace = array(); - foreach ($this->data as $key => $val) { - $replace['{' . $key . '}'] = $val; - } - - return strtr($input, $replace); - } -} diff --git a/core/vendor/guzzle/common/Guzzle/Common/Event.php b/core/vendor/guzzle/common/Guzzle/Common/Event.php deleted file mode 100644 index fad76a9b817847782cabc7387a56c9358bae442c..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/common/Guzzle/Common/Event.php +++ /dev/null @@ -1,52 +0,0 @@ -<?php - -namespace Guzzle\Common; - -use Symfony\Component\EventDispatcher\Event as SymfonyEvent; - -/** - * Default event for Guzzle notifications - */ -class Event extends SymfonyEvent implements ToArrayInterface, \ArrayAccess, \IteratorAggregate -{ - /** @var array */ - private $context; - - /** - * @param array $context Contextual information - */ - public function __construct(array $context = array()) - { - $this->context = $context; - } - - public function getIterator() - { - return new \ArrayIterator($this->context); - } - - public function offsetGet($offset) - { - return isset($this->context[$offset]) ? $this->context[$offset] : null; - } - - public function offsetSet($offset, $value) - { - $this->context[$offset] = $value; - } - - public function offsetExists($offset) - { - return isset($this->context[$offset]); - } - - public function offsetUnset($offset) - { - unset($this->context[$offset]); - } - - public function toArray() - { - return $this->context; - } -} diff --git a/core/vendor/guzzle/common/Guzzle/Common/Exception/BadMethodCallException.php b/core/vendor/guzzle/common/Guzzle/Common/Exception/BadMethodCallException.php deleted file mode 100644 index 08d1c7256d720db4282233c4d30486f2c6073c35..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/common/Guzzle/Common/Exception/BadMethodCallException.php +++ /dev/null @@ -1,5 +0,0 @@ -<?php - -namespace Guzzle\Common\Exception; - -class BadMethodCallException extends \BadMethodCallException implements GuzzleException {} diff --git a/core/vendor/guzzle/common/Guzzle/Common/Exception/ExceptionCollection.php b/core/vendor/guzzle/common/Guzzle/Common/Exception/ExceptionCollection.php deleted file mode 100644 index 185d05cddac3d7e9b6d658e47a7ed2c9a22d694e..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/common/Guzzle/Common/Exception/ExceptionCollection.php +++ /dev/null @@ -1,85 +0,0 @@ -<?php - -namespace Guzzle\Common\Exception; - -/** - * Collection of exceptions - */ -class ExceptionCollection extends \Exception implements GuzzleException, \IteratorAggregate, \Countable -{ - /** @var array Array of Exceptions */ - protected $exceptions = array(); - - /** - * Set all of the exceptions - * - * @param array $exceptions Array of exceptions - * - * @return self - */ - public function setExceptions(array $exceptions) - { - $this->exceptions = array(); - foreach ($exceptions as $exception) { - $this->add($exception); - } - - return $this; - } - - /** - * Add exceptions to the collection - * - * @param ExceptionCollection|\Exception $e Exception to add - * - * @return ExceptionCollection; - */ - public function add($e) - { - if ($this->message) { - $this->message .= "\n"; - } - - if ($e instanceof self) { - $this->message .= '(' . get_class($e) . ")"; - foreach (explode("\n", $e->getMessage()) as $message) { - $this->message .= "\n {$message}"; - } - } elseif ($e instanceof \Exception) { - $this->exceptions[] = $e; - $this->message .= '(' . get_class($e) . ') ' . $e->getMessage(); - } - - return $this; - } - - /** - * Get the total number of request exceptions - * - * @return int - */ - public function count() - { - return count($this->exceptions); - } - - /** - * Allows array-like iteration over the request exceptions - * - * @return \ArrayIterator - */ - public function getIterator() - { - return new \ArrayIterator($this->exceptions); - } - - /** - * Get the first exception in the collection - * - * @return \Exception - */ - public function getFirst() - { - return $this->exceptions ? $this->exceptions[0] : null; - } -} diff --git a/core/vendor/guzzle/common/Guzzle/Common/Exception/GuzzleException.php b/core/vendor/guzzle/common/Guzzle/Common/Exception/GuzzleException.php deleted file mode 100644 index 458e6f2ea1653c312ee1d203bbf6f1b402b88fbe..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/common/Guzzle/Common/Exception/GuzzleException.php +++ /dev/null @@ -1,8 +0,0 @@ -<?php - -namespace Guzzle\Common\Exception; - -/** - * Guzzle exception - */ -interface GuzzleException {} diff --git a/core/vendor/guzzle/common/Guzzle/Common/Exception/InvalidArgumentException.php b/core/vendor/guzzle/common/Guzzle/Common/Exception/InvalidArgumentException.php deleted file mode 100644 index ae674be8790c2b252fc132708480531a07452582..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/common/Guzzle/Common/Exception/InvalidArgumentException.php +++ /dev/null @@ -1,5 +0,0 @@ -<?php - -namespace Guzzle\Common\Exception; - -class InvalidArgumentException extends \InvalidArgumentException implements GuzzleException {} diff --git a/core/vendor/guzzle/common/Guzzle/Common/Exception/RuntimeException.php b/core/vendor/guzzle/common/Guzzle/Common/Exception/RuntimeException.php deleted file mode 100644 index 9254094f3e301f4d3a8d63cd3b63a0b4e6c9d165..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/common/Guzzle/Common/Exception/RuntimeException.php +++ /dev/null @@ -1,5 +0,0 @@ -<?php - -namespace Guzzle\Common\Exception; - -class RuntimeException extends \RuntimeException implements GuzzleException {} diff --git a/core/vendor/guzzle/common/Guzzle/Common/Exception/UnexpectedValueException.php b/core/vendor/guzzle/common/Guzzle/Common/Exception/UnexpectedValueException.php deleted file mode 100644 index 843c0179ebf16f91ea9d938fed88801b41ad6ec5..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/common/Guzzle/Common/Exception/UnexpectedValueException.php +++ /dev/null @@ -1,5 +0,0 @@ -<?php - -namespace Guzzle\Common\Exception; - -class UnexpectedValueException extends \UnexpectedValueException implements GuzzleException {} diff --git a/core/vendor/guzzle/common/Guzzle/Common/FromConfigInterface.php b/core/vendor/guzzle/common/Guzzle/Common/FromConfigInterface.php deleted file mode 100644 index c8b1317c446dd97873e1bbb7a9fd859cb6b7e467..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/common/Guzzle/Common/FromConfigInterface.php +++ /dev/null @@ -1,18 +0,0 @@ -<?php - -namespace Guzzle\Common; - -/** - * Interfaces that adds a factory method which is used to instantiate a class from an array of configuration options. - */ -interface FromConfigInterface -{ - /** - * Static factory method used to turn an array or collection of configuration data into an instantiated object. - * - * @param array|Collection $config Configuration data - * - * @return FromConfigInterface - */ - public static function factory($config = array()); -} diff --git a/core/vendor/guzzle/common/Guzzle/Common/HasDispatcherInterface.php b/core/vendor/guzzle/common/Guzzle/Common/HasDispatcherInterface.php deleted file mode 100644 index 75727ed9e2599ffaf8bf37136cc9c510890e3ef7..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/common/Guzzle/Common/HasDispatcherInterface.php +++ /dev/null @@ -1,52 +0,0 @@ -<?php - -namespace Guzzle\Common; - -use Symfony\Component\EventDispatcher\EventDispatcherInterface; -use Symfony\Component\EventDispatcher\EventSubscriberInterface; - -/** - * Holds an event dispatcher - */ -interface HasDispatcherInterface -{ - /** - * Get a list of all of the events emitted from the class - * - * @return array - */ - public static function getAllEvents(); - - /** - * Set the EventDispatcher of the request - * - * @param EventDispatcherInterface $eventDispatcher - * - * @return self - */ - public function setEventDispatcher(EventDispatcherInterface $eventDispatcher); - - /** - * Get the EventDispatcher of the request - * - * @return EventDispatcherInterface - */ - public function getEventDispatcher(); - - /** - * Helper to dispatch Guzzle events and set the event name on the event - * - * @param string $eventName Name of the event to dispatch - * @param array $context Context of the event - */ - public function dispatch($eventName, array $context = array()); - - /** - * Add an event subscriber to the dispatcher - * - * @param EventSubscriberInterface $subscriber Event subscriber - * - * @return self - */ - public function addSubscriber(EventSubscriberInterface $subscriber); -} diff --git a/core/vendor/guzzle/common/Guzzle/Common/Version.php b/core/vendor/guzzle/common/Guzzle/Common/Version.php deleted file mode 100644 index af4529b695d0af53a458af13bea789d3f711eae1..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/common/Guzzle/Common/Version.php +++ /dev/null @@ -1,29 +0,0 @@ -<?php - -namespace Guzzle\Common; - -/** - * Guzzle version information - */ -class Version -{ - const VERSION = '3.7.1'; - - /** - * @var bool Set this value to true to enable warnings for deprecated functionality use. This should be on in your - * unit tests, but probably not in production. - */ - public static $emitWarnings = false; - - /** - * Emit a deprecation warning - * - * @param string $message Warning message - */ - public static function warn($message) - { - if (self::$emitWarnings) { - trigger_error('Deprecation warning: ' . $message, E_USER_DEPRECATED); - } - } -} diff --git a/core/vendor/guzzle/common/Guzzle/Common/composer.json b/core/vendor/guzzle/common/Guzzle/Common/composer.json deleted file mode 100644 index c02fa690102170b7aae7ba34610e9fe096dc6dbc..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/common/Guzzle/Common/composer.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "guzzle/common", - "homepage": "http://guzzlephp.org/", - "description": "Common libraries used by Guzzle", - "keywords": ["common", "event", "exception", "collection"], - "license": "MIT", - "require": { - "php": ">=5.3.2", - "symfony/event-dispatcher": ">=2.1" - }, - "autoload": { - "psr-0": { "Guzzle\\Common": "" } - }, - "target-dir": "Guzzle/Common", - "extra": { - "branch-alias": { - "dev-master": "3.7-dev" - } - } -} diff --git a/core/vendor/guzzle/http/Guzzle/Http/AbstractEntityBodyDecorator.php b/core/vendor/guzzle/http/Guzzle/Http/AbstractEntityBodyDecorator.php deleted file mode 100644 index 5005a887cf5d407e115a6f3906bdd4cd573a88f8..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/http/Guzzle/Http/AbstractEntityBodyDecorator.php +++ /dev/null @@ -1,221 +0,0 @@ -<?php - -namespace Guzzle\Http; - -use Guzzle\Stream\Stream; - -/** - * Abstract decorator used to wrap entity bodies - */ -class AbstractEntityBodyDecorator implements EntityBodyInterface -{ - /** @var EntityBodyInterface Decorated entity body */ - protected $body; - - /** - * @param EntityBodyInterface $body Entity body to decorate - */ - public function __construct(EntityBodyInterface $body) - { - $this->body = $body; - } - - public function __toString() - { - return (string) $this->body; - } - - /** - * Allow decorators to implement custom methods - * - * @param string $method Missing method name - * @param array $args Method arguments - * - * @return mixed - */ - public function __call($method, array $args) - { - return call_user_func_array(array($this->body, $method), $args); - } - - public function close() - { - return $this->body->close(); - } - - public function setRewindFunction($callable) - { - $this->body->setRewindFunction($callable); - - return $this; - } - - public function rewind() - { - return $this->body->rewind(); - } - - public function compress($filter = 'zlib.deflate') - { - return $this->body->compress($filter); - } - - public function uncompress($filter = 'zlib.inflate') - { - return $this->body->uncompress($filter); - } - - public function getContentLength() - { - return $this->getSize(); - } - - public function getContentType() - { - return $this->body->getContentType(); - } - - public function getContentMd5($rawOutput = false, $base64Encode = false) - { - $hash = Stream::getHash($this, 'md5', $rawOutput); - - return $hash && $base64Encode ? base64_encode($hash) : $hash; - } - - public function getContentEncoding() - { - return $this->body->getContentEncoding(); - } - - public function getMetaData($key = null) - { - return $this->body->getMetaData($key); - } - - public function getStream() - { - return $this->body->getStream(); - } - - public function setStream($stream, $size = 0) - { - $this->body->setStream($stream, $size); - - return $this; - } - - public function detachStream() - { - $this->body->detachStream(); - - return $this; - } - - public function getWrapper() - { - return $this->body->getWrapper(); - } - - public function getWrapperData() - { - return $this->body->getWrapperData(); - } - - public function getStreamType() - { - return $this->body->getStreamType(); - } - - public function getUri() - { - return $this->body->getUri(); - } - - public function getSize() - { - return $this->body->getSize(); - } - - public function isReadable() - { - return $this->body->isReadable(); - } - - public function isRepeatable() - { - return $this->isSeekable() && $this->isReadable(); - } - - public function isWritable() - { - return $this->body->isWritable(); - } - - public function isConsumed() - { - return $this->body->isConsumed(); - } - - /** - * Alias of isConsumed() - * {@inheritdoc} - */ - public function feof() - { - return $this->isConsumed(); - } - - public function isLocal() - { - return $this->body->isLocal(); - } - - public function isSeekable() - { - return $this->body->isSeekable(); - } - - public function setSize($size) - { - $this->body->setSize($size); - - return $this; - } - - public function seek($offset, $whence = SEEK_SET) - { - return $this->body->seek($offset, $whence); - } - - public function read($length) - { - return $this->body->read($length); - } - - public function write($string) - { - return $this->body->write($string); - } - - public function readLine($maxLength = null) - { - return $this->body->readLine($maxLength); - } - - public function ftell() - { - return $this->body->ftell(); - } - - public function getCustomData($key) - { - return $this->body->getCustomData($key); - } - - public function setCustomData($key, $value) - { - $this->body->setCustomData($key, $value); - - return $this; - } -} diff --git a/core/vendor/guzzle/http/Guzzle/Http/CachingEntityBody.php b/core/vendor/guzzle/http/Guzzle/Http/CachingEntityBody.php deleted file mode 100644 index c65c1365042f0a5a2b2372c3d46d1bb804ce3b7b..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/http/Guzzle/Http/CachingEntityBody.php +++ /dev/null @@ -1,229 +0,0 @@ -<?php - -namespace Guzzle\Http; - -use Guzzle\Common\Exception\RuntimeException; - -/** - * EntityBody decorator that can cache previously read bytes from a sequentially read tstream - */ -class CachingEntityBody extends AbstractEntityBodyDecorator -{ - /** @var EntityBody Remote stream used to actually pull data onto the buffer */ - protected $remoteStream; - - /** @var int The number of bytes to skip reading due to a write on the temporary buffer */ - protected $skipReadBytes = 0; - - /** - * We will treat the buffer object as the body of the entity body - * {@inheritdoc} - */ - public function __construct(EntityBodyInterface $body) - { - $this->remoteStream = $body; - $this->body = new EntityBody(fopen('php://temp', 'r+')); - } - - /** - * Will give the contents of the buffer followed by the exhausted remote stream. - * - * Warning: Loads the entire stream into memory - * - * @return string - */ - public function __toString() - { - $pos = $this->ftell(); - $this->rewind(); - - $str = ''; - while (!$this->isConsumed()) { - $str .= $this->read(16384); - } - - $this->seek($pos); - - return $str; - } - - public function getSize() - { - return max($this->body->getSize(), $this->remoteStream->getSize()); - } - - /** - * {@inheritdoc} - * @throws RuntimeException When seeking with SEEK_END or when seeking past the total size of the buffer stream - */ - public function seek($offset, $whence = SEEK_SET) - { - if ($whence == SEEK_SET) { - $byte = $offset; - } elseif ($whence == SEEK_CUR) { - $byte = $offset + $this->ftell(); - } else { - throw new RuntimeException(__CLASS__ . ' supports only SEEK_SET and SEEK_CUR seek operations'); - } - - // You cannot skip ahead past where you've read from the remote stream - if ($byte > $this->body->getSize()) { - throw new RuntimeException( - "Cannot seek to byte {$byte} when the buffered stream only contains {$this->body->getSize()} bytes" - ); - } - - return $this->body->seek($byte); - } - - public function rewind() - { - return $this->seek(0); - } - - /** - * Does not support custom rewind functions - * - * @throws RuntimeException - */ - public function setRewindFunction($callable) - { - throw new RuntimeException(__CLASS__ . ' does not support custom stream rewind functions'); - } - - public function read($length) - { - // Perform a regular read on any previously read data from the buffer - $data = $this->body->read($length); - $remaining = $length - strlen($data); - - // More data was requested so read from the remote stream - if ($remaining) { - // If data was written to the buffer in a position that would have been filled from the remote stream, - // then we must skip bytes on the remote stream to emulate overwriting bytes from that position. This - // mimics the behavior of other PHP stream wrappers. - $remoteData = $this->remoteStream->read($remaining + $this->skipReadBytes); - - if ($this->skipReadBytes) { - $len = strlen($remoteData); - $remoteData = substr($remoteData, $this->skipReadBytes); - $this->skipReadBytes = max(0, $this->skipReadBytes - $len); - } - - $data .= $remoteData; - $this->body->write($remoteData); - } - - return $data; - } - - public function write($string) - { - // When appending to the end of the currently read stream, you'll want to skip bytes from being read from - // the remote stream to emulate other stream wrappers. Basically replacing bytes of data of a fixed length. - $overflow = (strlen($string) + $this->ftell()) - $this->remoteStream->ftell(); - if ($overflow > 0) { - $this->skipReadBytes += $overflow; - } - - return $this->body->write($string); - } - - /** - * {@inheritdoc} - * @link http://php.net/manual/en/function.fgets.php - */ - public function readLine($maxLength = null) - { - $buffer = ''; - $size = 0; - while (!$this->isConsumed()) { - $byte = $this->read(1); - $buffer .= $byte; - // Break when a new line is found or the max length - 1 is reached - if ($byte == PHP_EOL || ++$size == $maxLength - 1) { - break; - } - } - - return $buffer; - } - - public function isConsumed() - { - return $this->body->isConsumed() && $this->remoteStream->isConsumed(); - } - - /** - * Close both the remote stream and buffer stream - */ - public function close() - { - return $this->remoteStream->close() && $this->body->close(); - } - - public function setStream($stream, $size = 0) - { - $this->remoteStream->setStream($stream, $size); - } - - public function getContentType() - { - return $this->remoteStream->getContentType(); - } - - public function getContentEncoding() - { - return $this->remoteStream->getContentEncoding(); - } - - public function getMetaData($key = null) - { - return $this->remoteStream->getMetaData($key); - } - - public function getStream() - { - return $this->remoteStream->getStream(); - } - - public function getWrapper() - { - return $this->remoteStream->getWrapper(); - } - - public function getWrapperData() - { - return $this->remoteStream->getWrapperData(); - } - - public function getStreamType() - { - return $this->remoteStream->getStreamType(); - } - - public function getUri() - { - return $this->remoteStream->getUri(); - } - - /** - * Always retrieve custom data from the remote stream - * {@inheritdoc} - */ - public function getCustomData($key) - { - return $this->remoteStream->getCustomData($key); - } - - /** - * Always set custom data on the remote stream - * {@inheritdoc} - */ - public function setCustomData($key, $value) - { - $this->remoteStream->setCustomData($key, $value); - - return $this; - } -} diff --git a/core/vendor/guzzle/http/Guzzle/Http/Client.php b/core/vendor/guzzle/http/Guzzle/Http/Client.php deleted file mode 100644 index 465962149d1bbf533750fc32e1d58e182b9c3190..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/http/Guzzle/Http/Client.php +++ /dev/null @@ -1,506 +0,0 @@ -<?php - -namespace Guzzle\Http; - -use Guzzle\Common\Collection; -use Guzzle\Common\AbstractHasDispatcher; -use Guzzle\Common\Exception\ExceptionCollection; -use Guzzle\Common\Exception\InvalidArgumentException; -use Guzzle\Common\Exception\RuntimeException; -use Guzzle\Common\Version; -use Guzzle\Parser\ParserRegistry; -use Guzzle\Parser\UriTemplate\UriTemplateInterface; -use Guzzle\Http\Message\RequestInterface; -use Guzzle\Http\Message\RequestFactory; -use Guzzle\Http\Message\RequestFactoryInterface; -use Guzzle\Http\Curl\CurlMultiInterface; -use Guzzle\Http\Curl\CurlMultiProxy; -use Guzzle\Http\Curl\CurlHandle; -use Guzzle\Http\Curl\CurlVersion; - -/** - * HTTP client - */ -class Client extends AbstractHasDispatcher implements ClientInterface -{ - /** @deprecated Use [request.options][params] */ - const REQUEST_PARAMS = 'request.params'; - - const REQUEST_OPTIONS = 'request.options'; - const CURL_OPTIONS = 'curl.options'; - const SSL_CERT_AUTHORITY = 'ssl.certificate_authority'; - const DISABLE_REDIRECTS = RedirectPlugin::DISABLE; - - /** @var Collection Default HTTP headers to set on each request */ - protected $defaultHeaders; - - /** @var string The user agent string to set on each request */ - protected $userAgent; - - /** @var Collection Parameter object holding configuration data */ - private $config; - - /** @var Url Base URL of the client */ - private $baseUrl; - - /** @var CurlMultiInterface CurlMulti object used internally */ - private $curlMulti; - - /** @var UriTemplateInterface URI template owned by the client */ - private $uriTemplate; - - /** @var RequestFactoryInterface Request factory used by the client */ - protected $requestFactory; - - public static function getAllEvents() - { - return array(self::CREATE_REQUEST); - } - - /** - * @param string $baseUrl Base URL of the web service - * @param array|Collection $config Configuration settings - * - * @throws RuntimeException if cURL is not installed - */ - public function __construct($baseUrl = '', $config = null) - { - if (!extension_loaded('curl')) { - // @codeCoverageIgnoreStart - throw new RuntimeException('The PHP cURL extension must be installed to use Guzzle.'); - // @codeCoverageIgnoreEnd - } - $this->setConfig($config ?: new Collection()); - $this->initSsl(); - $this->setBaseUrl($baseUrl); - $this->defaultHeaders = new Collection(); - $this->setRequestFactory(RequestFactory::getInstance()); - $this->userAgent = $this->getDefaultUserAgent(); - if (!$this->config[self::DISABLE_REDIRECTS]) { - $this->addSubscriber(new RedirectPlugin()); - } - } - - final public function setConfig($config) - { - if ($config instanceof Collection) { - $this->config = $config; - } elseif (is_array($config)) { - $this->config = new Collection($config); - } else { - throw new InvalidArgumentException('Config must be an array or Collection'); - } - - return $this; - } - - final public function getConfig($key = false) - { - return $key ? $this->config[$key] : $this->config; - } - - /** - * Set a default request option on the client that will be used as a default for each request - * - * @param string $keyOrPath request.options key (e.g. allow_redirects) or path to a nested key (e.g. headers/foo) - * @param mixed $value Value to set - * - * @return $this - */ - public function setDefaultOption($keyOrPath, $value) - { - $keyOrPath = self::REQUEST_OPTIONS . '/' . $keyOrPath; - $this->config->setPath($keyOrPath, $value); - - return $this; - } - - /** - * Retrieve a default request option from the client - * - * @param string $keyOrPath request.options key (e.g. allow_redirects) or path to a nested key (e.g. headers/foo) - * - * @return mixed|null - */ - public function getDefaultOption($keyOrPath) - { - $keyOrPath = self::REQUEST_OPTIONS . '/' . $keyOrPath; - - return $this->config->getPath($keyOrPath); - } - - final public function setSslVerification($certificateAuthority = true, $verifyPeer = true, $verifyHost = 2) - { - $opts = $this->config[self::CURL_OPTIONS] ?: array(); - - if ($certificateAuthority === true) { - // use bundled CA bundle, set secure defaults - $opts[CURLOPT_CAINFO] = __DIR__ . '/Resources/cacert.pem'; - $opts[CURLOPT_SSL_VERIFYPEER] = true; - $opts[CURLOPT_SSL_VERIFYHOST] = 2; - } elseif ($certificateAuthority === false) { - unset($opts[CURLOPT_CAINFO]); - $opts[CURLOPT_SSL_VERIFYPEER] = false; - $opts[CURLOPT_SSL_VERIFYHOST] = 2; - } elseif ($verifyPeer !== true && $verifyPeer !== false && $verifyPeer !== 1 && $verifyPeer !== 0) { - throw new InvalidArgumentException('verifyPeer must be 1, 0 or boolean'); - } elseif ($verifyHost !== 0 && $verifyHost !== 1 && $verifyHost !== 2) { - throw new InvalidArgumentException('verifyHost must be 0, 1 or 2'); - } else { - $opts[CURLOPT_SSL_VERIFYPEER] = $verifyPeer; - $opts[CURLOPT_SSL_VERIFYHOST] = $verifyHost; - if (is_file($certificateAuthority)) { - unset($opts[CURLOPT_CAPATH]); - $opts[CURLOPT_CAINFO] = $certificateAuthority; - } elseif (is_dir($certificateAuthority)) { - unset($opts[CURLOPT_CAINFO]); - $opts[CURLOPT_CAPATH] = $certificateAuthority; - } else { - throw new RuntimeException( - 'Invalid option passed to ' . self::SSL_CERT_AUTHORITY . ': ' . $certificateAuthority - ); - } - } - - $this->config->set(self::CURL_OPTIONS, $opts); - - return $this; - } - - public function createRequest($method = 'GET', $uri = null, $headers = null, $body = null, array $options = array()) - { - if (!$uri) { - $url = $this->getBaseUrl(); - } else { - if (!is_array($uri)) { - $templateVars = null; - } else { - list($uri, $templateVars) = $uri; - } - if (substr($uri, 0, 4) === 'http') { - // Use absolute URLs as-is - $url = $this->expandTemplate($uri, $templateVars); - } else { - $url = Url::factory($this->getBaseUrl())->combine($this->expandTemplate($uri, $templateVars)); - } - } - - // If default headers are provided, then merge them under any explicitly provided headers for the request - if (count($this->defaultHeaders)) { - if (!$headers) { - $headers = $this->defaultHeaders->toArray(); - } elseif (is_array($headers)) { - $headers += $this->defaultHeaders->toArray(); - } elseif ($headers instanceof Collection) { - $headers = $headers->toArray() + $this->defaultHeaders->toArray(); - } - } - - return $this->prepareRequest($this->requestFactory->create($method, (string) $url, $headers, $body), $options); - } - - public function getBaseUrl($expand = true) - { - return $expand ? $this->expandTemplate($this->baseUrl) : $this->baseUrl; - } - - public function setBaseUrl($url) - { - $this->baseUrl = $url; - - return $this; - } - - public function setUserAgent($userAgent, $includeDefault = false) - { - if ($includeDefault) { - $userAgent .= ' ' . $this->getDefaultUserAgent(); - } - $this->userAgent = $userAgent; - - return $this; - } - - /** - * Get the default User-Agent string to use with Guzzle - * - * @return string - */ - public function getDefaultUserAgent() - { - return 'Guzzle/' . Version::VERSION - . ' curl/' . CurlVersion::getInstance()->get('version') - . ' PHP/' . PHP_VERSION; - } - - public function get($uri = null, $headers = null, $options = array()) - { - // BC compat: $options can be a string, resource, etc to specify where the response body is downloaded - return is_array($options) - ? $this->createRequest('GET', $uri, $headers, null, $options) - : $this->createRequest('GET', $uri, $headers, $options); - } - - public function head($uri = null, $headers = null, array $options = array()) - { - return $this->createRequest('HEAD', $uri, $headers, null, $options); - } - - public function delete($uri = null, $headers = null, $body = null, array $options = array()) - { - return $this->createRequest('DELETE', $uri, $headers, $body, $options); - } - - public function put($uri = null, $headers = null, $body = null, array $options = array()) - { - return $this->createRequest('PUT', $uri, $headers, $body, $options); - } - - public function patch($uri = null, $headers = null, $body = null, array $options = array()) - { - return $this->createRequest('PATCH', $uri, $headers, $body, $options); - } - - public function post($uri = null, $headers = null, $postBody = null, array $options = array()) - { - return $this->createRequest('POST', $uri, $headers, $postBody, $options); - } - - public function options($uri = null, array $options = array()) - { - return $this->createRequest('OPTIONS', $uri, $options); - } - - public function send($requests) - { - if (!($requests instanceof RequestInterface)) { - return $this->sendMultiple($requests); - } - - try { - /** @var $requests RequestInterface */ - $this->getCurlMulti()->add($requests)->send(); - return $requests->getResponse(); - } catch (ExceptionCollection $e) { - throw $e->getFirst(); - } - } - - /** - * Set a curl multi object to be used internally by the client for transferring requests. - * - * @param CurlMultiInterface $curlMulti Multi object - * - * @return self - */ - public function setCurlMulti(CurlMultiInterface $curlMulti) - { - $this->curlMulti = $curlMulti; - - return $this; - } - - /** - * @return CurlMultiInterface|CurlMultiProxy - */ - public function getCurlMulti() - { - if (!$this->curlMulti) { - $this->curlMulti = new CurlMultiProxy(); - } - - return $this->curlMulti; - } - - public function setRequestFactory(RequestFactoryInterface $factory) - { - $this->requestFactory = $factory; - - return $this; - } - - /** - * Set the URI template expander to use with the client - * - * @param UriTemplateInterface $uriTemplate URI template expander - * - * @return self - */ - public function setUriTemplate(UriTemplateInterface $uriTemplate) - { - $this->uriTemplate = $uriTemplate; - - return $this; - } - - /** - * Copy the cacert.pem file from the phar if it is not in the temp folder and validate the MD5 checksum - * - * @param bool $md5Check Set to false to not perform the MD5 validation - * - * @return string Returns the path to the extracted cacert - * @throws RuntimeException if the file cannot be copied or there is a MD5 mismatch - */ - public function preparePharCacert($md5Check = true) - { - $from = __DIR__ . '/Resources/cacert.pem'; - $certFile = sys_get_temp_dir() . '/guzzle-cacert.pem'; - if (!file_exists($certFile) && !copy($from, $certFile)) { - throw new RuntimeException("Could not copy {$from} to {$certFile}: " . var_export(error_get_last(), true)); - } elseif ($md5Check) { - $actualMd5 = md5_file($certFile); - $expectedMd5 = trim(file_get_contents("{$from}.md5")); - if ($actualMd5 != $expectedMd5) { - throw new RuntimeException("{$certFile} MD5 mismatch: expected {$expectedMd5} but got {$actualMd5}"); - } - } - - return $certFile; - } - - /** - * Expand a URI template while merging client config settings into the template variables - * - * @param string $template Template to expand - * @param array $variables Variables to inject - * - * @return string - */ - protected function expandTemplate($template, array $variables = null) - { - $expansionVars = $this->getConfig()->toArray(); - if ($variables) { - $expansionVars = $variables + $expansionVars; - } - - return $this->getUriTemplate()->expand($template, $expansionVars); - } - - /** - * Get the URI template expander used by the client - * - * @return UriTemplateInterface - */ - protected function getUriTemplate() - { - if (!$this->uriTemplate) { - $this->uriTemplate = ParserRegistry::getInstance()->getParser('uri_template'); - } - - return $this->uriTemplate; - } - - /** - * Send multiple requests in parallel - * - * @param array $requests Array of RequestInterface objects - * - * @return array Returns an array of Response objects - */ - protected function sendMultiple(array $requests) - { - $curlMulti = $this->getCurlMulti(); - foreach ($requests as $request) { - $curlMulti->add($request); - } - $curlMulti->send(); - - /** @var $request RequestInterface */ - $result = array(); - foreach ($requests as $request) { - $result[] = $request->getResponse(); - } - - return $result; - } - - /** - * Prepare a request to be sent from the Client by adding client specific behaviors and properties to the request. - * - * @param RequestInterface $request Request to prepare for the client - * @param array $options Options to apply to the request - * - * @return RequestInterface - */ - protected function prepareRequest(RequestInterface $request, array $options = array()) - { - $request->setClient($this)->setEventDispatcher(clone $this->getEventDispatcher()); - - if ($curl = $this->config[self::CURL_OPTIONS]) { - $request->getCurlOptions()->overwriteWith(CurlHandle::parseCurlConfig($curl)); - } - - if ($params = $this->config[self::REQUEST_PARAMS]) { - Version::warn('request.params is deprecated. Use request.options to add default request options.'); - $request->getParams()->overwriteWith($params); - } - - if ($this->userAgent && !$request->hasHeader('User-Agent')) { - $request->setHeader('User-Agent', $this->userAgent); - } - - if ($defaults = $this->config[self::REQUEST_OPTIONS]) { - $this->requestFactory->applyOptions($request, $defaults, RequestFactoryInterface::OPTIONS_AS_DEFAULTS); - } - - if ($options) { - $this->requestFactory->applyOptions($request, $options); - } - - $this->dispatch('client.create_request', array('client' => $this, 'request' => $request)); - - return $request; - } - - /** - * Initializes SSL settings - */ - protected function initSsl() - { - if ('system' == ($authority = $this->config[self::SSL_CERT_AUTHORITY])) { - return; - } - - if ($authority === null) { - $authority = true; - } - - if ($authority === true && substr(__FILE__, 0, 7) == 'phar://') { - $authority = $this->preparePharCacert(); - $that = $this; - $this->getEventDispatcher()->addListener('request.before_send', function ($event) use ($authority, $that) { - if ($authority == $event['request']->getCurlOptions()->get(CURLOPT_CAINFO)) { - $that->preparePharCacert(false); - } - }); - } - - $this->setSslVerification($authority); - } - - /** - * @deprecated - */ - public function getDefaultHeaders() - { - Version::warn(__METHOD__ . ' is deprecated. Use the request.options array to retrieve default request options'); - return $this->defaultHeaders; - } - - /** - * @deprecated - */ - public function setDefaultHeaders($headers) - { - Version::warn(__METHOD__ . ' is deprecated. Use the request.options array to specify default request options'); - if ($headers instanceof Collection) { - $this->defaultHeaders = $headers; - } elseif (is_array($headers)) { - $this->defaultHeaders = new Collection($headers); - } else { - throw new InvalidArgumentException('Headers must be an array or Collection'); - } - - return $this; - } -} diff --git a/core/vendor/guzzle/http/Guzzle/Http/ClientInterface.php b/core/vendor/guzzle/http/Guzzle/Http/ClientInterface.php deleted file mode 100644 index 10e4de2ab09951369d13b186d3667aa6c40d1ada..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/http/Guzzle/Http/ClientInterface.php +++ /dev/null @@ -1,223 +0,0 @@ -<?php - -namespace Guzzle\Http; - -use Guzzle\Common\HasDispatcherInterface; -use Guzzle\Common\Collection; -use Guzzle\Common\Exception\InvalidArgumentException; -use Guzzle\Http\Message\EntityEnclosingRequestInterface; -use Guzzle\Http\Message\RequestInterface; - -/** - * Client interface for send HTTP requests - */ -interface ClientInterface extends HasDispatcherInterface -{ - const CREATE_REQUEST = 'client.create_request'; - - /** @var string RFC 1123 HTTP-Date */ - const HTTP_DATE = 'D, d M Y H:i:s \G\M\T'; - - /** - * Set the configuration object to use with the client - * - * @param array|Collection $config Parameters that define how the client behaves - * - * @return self - */ - public function setConfig($config); - - /** - * Get a configuration setting or all of the configuration settings. The Collection result of this method can be - * modified to change the configuration settings of a client. - * - * A client should honor the following special values: - * - * - request.options: Associative array of default RequestFactory options to apply to each request - * - request.params: Associative array of request parameters (data values) to apply to each request - * - curl.options: Associative array of cURL configuration settings to apply to each request - * - ssl.certificate_authority: Path a CAINFO, CAPATH, true to use strict defaults, or false to disable verification - * - redirect.disable: Set to true to disable redirects - * - * @param bool|string $key Configuration value to retrieve. Set to FALSE to retrieve all values of the client. - * The object return can be modified, and modifications will affect the client's config. - * @return mixed|Collection - * @see \Guzzle\Http\Message\RequestFactoryInterface::applyOptions for a full list of request.options options - */ - public function getConfig($key = false); - - /** - * Create and return a new {@see RequestInterface} configured for the client. - * - * Use an absolute path to override the base path of the client, or a relative path to append to the base path of - * the client. The URI can contain the query string as well. Use an array to provide a URI template and additional - * variables to use in the URI template expansion. - * - * @param string $method HTTP method. Defaults to GET - * @param string|array $uri Resource URI. - * @param array|Collection $headers HTTP headers - * @param string|resource|array|EntityBodyInterface $body Entity body of request (POST/PUT) or response (GET) - * @param array $options Array of options to apply to the request - * - * @return RequestInterface - * @throws InvalidArgumentException if a URI array is passed that does not contain exactly two elements: the URI - * followed by template variables - */ - public function createRequest( - $method = RequestInterface::GET, - $uri = null, - $headers = null, - $body = null, - array $options = array() - ); - - /** - * Create a GET request for the client - * - * @param string|array $uri Resource URI - * @param array|Collection $headers HTTP headers - * @param array $options Options to apply to the request. For BC compatibility, you can also pass a - * string to tell Guzzle to download the body of the response to a particular - * location. Use the 'body' option instead for forward compatibility. - * @return RequestInterface - * @see Guzzle\Http\ClientInterface::createRequest() - */ - public function get($uri = null, $headers = null, $options = array()); - - /** - * Create a HEAD request for the client - * - * @param string|array $uri Resource URI - * @param array|Collection $headers HTTP headers - * @param array $options Options to apply to the request - * - * @return RequestInterface - * @see Guzzle\Http\ClientInterface::createRequest() - */ - public function head($uri = null, $headers = null, array $options = array()); - - /** - * Create a DELETE request for the client - * - * @param string|array $uri Resource URI - * @param array|Collection $headers HTTP headers - * @param string|resource|EntityBodyInterface $body Body to send in the request - * @param array $options Options to apply to the request - * - * @return EntityEnclosingRequestInterface - * @see Guzzle\Http\ClientInterface::createRequest() - */ - public function delete($uri = null, $headers = null, $body = null, array $options = array()); - - /** - * Create a PUT request for the client - * - * @param string|array $uri Resource URI - * @param array|Collection $headers HTTP headers - * @param string|resource|EntityBodyInterface $body Body to send in the request - * @param array $options Options to apply to the request - * - * @return EntityEnclosingRequestInterface - * @see Guzzle\Http\ClientInterface::createRequest() - */ - public function put($uri = null, $headers = null, $body = null, array $options = array()); - - /** - * Create a PATCH request for the client - * - * @param string|array $uri Resource URI - * @param array|Collection $headers HTTP headers - * @param string|resource|EntityBodyInterface $body Body to send in the request - * @param array $options Options to apply to the request - * - * @return EntityEnclosingRequestInterface - * @see Guzzle\Http\ClientInterface::createRequest() - */ - public function patch($uri = null, $headers = null, $body = null, array $options = array()); - - /** - * Create a POST request for the client - * - * @param string|array $uri Resource URI - * @param array|Collection $headers HTTP headers - * @param array|Collection|string|EntityBodyInterface $postBody POST body. Can be a string, EntityBody, or - * associative array of POST fields to send in the body of the - * request. Prefix a value in the array with the @ symbol to - * reference a file. - * @param array $options Options to apply to the request - * - * @return EntityEnclosingRequestInterface - * @see Guzzle\Http\ClientInterface::createRequest() - */ - public function post($uri = null, $headers = null, $postBody = null, array $options = array()); - - /** - * Create an OPTIONS request for the client - * - * @param string|array $uri Resource URI - * @param array $options Options to apply to the request - * - * @return RequestInterface - * @see Guzzle\Http\ClientInterface::createRequest() - */ - public function options($uri = null, array $options = array()); - - /** - * Sends a single request or an array of requests in parallel - * - * @param array|RequestInterface $requests One or more RequestInterface objects to send - * - * @return \Guzzle\Http\Message\Response|array Returns a single Response or an array of Response objects - */ - public function send($requests); - - /** - * Get the client's base URL as either an expanded or raw URI template - * - * @param bool $expand Set to FALSE to get the raw base URL without URI template expansion - * - * @return string|null - */ - public function getBaseUrl($expand = true); - - /** - * Set the base URL of the client - * - * @param string $url The base service endpoint URL of the webservice - * - * @return self - */ - public function setBaseUrl($url); - - /** - * Set the User-Agent header to be used on all requests from the client - * - * @param string $userAgent User agent string - * @param bool $includeDefault Set to true to prepend the value to Guzzle's default user agent string - * - * @return self - */ - public function setUserAgent($userAgent, $includeDefault = false); - - /** - * Set SSL verification options. - * - * Setting $certificateAuthority to TRUE will result in the bundled cacert.pem being used to verify against the - * remote host. - * - * Alternate certificates to verify against can be specified with the $certificateAuthority option set to the full - * path to a certificate file, or the path to a directory containing certificates. - * - * Setting $certificateAuthority to FALSE will turn off peer verification, unset the bundled cacert.pem, and - * disable host verification. Please don't do this unless you really know what you're doing, and why you're doing - * it. - * - * @param string|bool $certificateAuthority bool, file path, or directory path - * @param bool $verifyPeer FALSE to stop from verifying the peer's certificate. - * @param int $verifyHost Set to 1 to check the existence of a common name in the SSL peer - * certificate. 2 to check the existence of a common name and also verify - * that it matches the hostname provided. - * @return self - */ - public function setSslVerification($certificateAuthority = true, $verifyPeer = true, $verifyHost = 2); -} diff --git a/core/vendor/guzzle/http/Guzzle/Http/Curl/CurlHandle.php b/core/vendor/guzzle/http/Guzzle/Http/Curl/CurlHandle.php deleted file mode 100644 index 78f9bde87f632c5c77d4a4a565c1f40fed29bec8..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/http/Guzzle/Http/Curl/CurlHandle.php +++ /dev/null @@ -1,451 +0,0 @@ -<?php - -namespace Guzzle\Http\Curl; - -use Guzzle\Common\Exception\InvalidArgumentException; -use Guzzle\Common\Exception\RuntimeException; -use Guzzle\Common\Collection; -use Guzzle\Http\Message\EntityEnclosingRequest; -use Guzzle\Http\Message\RequestInterface; -use Guzzle\Parser\ParserRegistry; -use Guzzle\Http\Url; - -/** - * Immutable wrapper for a cURL handle - */ -class CurlHandle -{ - const BODY_AS_STRING = 'body_as_string'; - const PROGRESS = 'progress'; - const DEBUG = 'debug'; - - /** @var Collection Curl options */ - protected $options; - - /** @var resource Curl resource handle */ - protected $handle; - - /** @var int CURLE_* error */ - protected $errorNo = CURLE_OK; - - /** - * Factory method to create a new curl handle based on an HTTP request. - * - * There are some helpful options you can set to enable specific behavior: - * - debug: Set to true to enable cURL debug functionality to track the actual headers sent over the wire. - * - progress: Set to true to enable progress function callbacks. - * - * @param RequestInterface $request Request - * - * @return CurlHandle - * @throws RuntimeException - */ - public static function factory(RequestInterface $request) - { - $requestCurlOptions = $request->getCurlOptions(); - $mediator = new RequestMediator($request, $requestCurlOptions->get('emit_io')); - $tempContentLength = null; - $method = $request->getMethod(); - $bodyAsString = $requestCurlOptions->get(self::BODY_AS_STRING); - - // Array of default cURL options. - $curlOptions = array( - CURLOPT_URL => $request->getUrl(), - CURLOPT_CONNECTTIMEOUT => 150, - CURLOPT_RETURNTRANSFER => false, - CURLOPT_HEADER => false, - CURLOPT_PORT => $request->getPort(), - CURLOPT_HTTPHEADER => array(), - CURLOPT_WRITEFUNCTION => array($mediator, 'writeResponseBody'), - CURLOPT_HEADERFUNCTION => array($mediator, 'receiveResponseHeader'), - CURLOPT_HTTP_VERSION => $request->getProtocolVersion() === '1.0' - ? CURL_HTTP_VERSION_1_0 : CURL_HTTP_VERSION_1_1, - // Verifies the authenticity of the peer's certificate - CURLOPT_SSL_VERIFYPEER => 1, - // Certificate must indicate that the server is the server to which you meant to connect - CURLOPT_SSL_VERIFYHOST => 2 - ); - - if (defined('CURLOPT_PROTOCOLS')) { - // Allow only HTTP and HTTPS protocols - $curlOptions[CURLOPT_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS; - } - - // Add CURLOPT_ENCODING if Accept-Encoding header is provided - if ($acceptEncodingHeader = $request->getHeader('Accept-Encoding')) { - $curlOptions[CURLOPT_ENCODING] = (string) $acceptEncodingHeader; - // Let cURL set the Accept-Encoding header, prevents duplicate values - $request->removeHeader('Accept-Encoding'); - } - - // Enable curl debug information if the 'debug' param was set - if ($requestCurlOptions->get('debug')) { - $curlOptions[CURLOPT_STDERR] = fopen('php://temp', 'r+'); - // @codeCoverageIgnoreStart - if (false === $curlOptions[CURLOPT_STDERR]) { - throw new RuntimeException('Unable to create a stream for CURLOPT_STDERR'); - } - // @codeCoverageIgnoreEnd - $curlOptions[CURLOPT_VERBOSE] = true; - } - - // Specify settings according to the HTTP method - if ($method == 'GET') { - $curlOptions[CURLOPT_HTTPGET] = true; - } elseif ($method == 'HEAD') { - $curlOptions[CURLOPT_NOBODY] = true; - // HEAD requests do not use a write function - unset($curlOptions[CURLOPT_WRITEFUNCTION]); - } elseif (!($request instanceof EntityEnclosingRequest)) { - $curlOptions[CURLOPT_CUSTOMREQUEST] = $method; - } else { - - $curlOptions[CURLOPT_CUSTOMREQUEST] = $method; - - // Handle sending raw bodies in a request - if ($request->getBody()) { - // You can send the body as a string using curl's CURLOPT_POSTFIELDS - if ($bodyAsString) { - $curlOptions[CURLOPT_POSTFIELDS] = (string) $request->getBody(); - // Allow curl to add the Content-Length for us to account for the times when - // POST redirects are followed by GET requests - if ($tempContentLength = $request->getHeader('Content-Length')) { - $tempContentLength = (int) (string) $tempContentLength; - } - // Remove the curl generated Content-Type header if none was set manually - if (!$request->hasHeader('Content-Type')) { - $curlOptions[CURLOPT_HTTPHEADER][] = 'Content-Type:'; - } - } else { - $curlOptions[CURLOPT_UPLOAD] = true; - // Let cURL handle setting the Content-Length header - if ($tempContentLength = $request->getHeader('Content-Length')) { - $tempContentLength = (int) (string) $tempContentLength; - $curlOptions[CURLOPT_INFILESIZE] = $tempContentLength; - } - // Add a callback for curl to read data to send with the request only if a body was specified - $curlOptions[CURLOPT_READFUNCTION] = array($mediator, 'readRequestBody'); - // Attempt to seek to the start of the stream - $request->getBody()->seek(0); - } - - } else { - - // Special handling for POST specific fields and files - $postFields = false; - if (count($request->getPostFiles())) { - $postFields = $request->getPostFields()->useUrlEncoding(false)->urlEncode(); - foreach ($request->getPostFiles() as $key => $data) { - $prefixKeys = count($data) > 1; - foreach ($data as $index => $file) { - // Allow multiple files in the same key - $fieldKey = $prefixKeys ? "{$key}[{$index}]" : $key; - $postFields[$fieldKey] = $file->getCurlValue(); - } - } - } elseif (count($request->getPostFields())) { - $postFields = (string) $request->getPostFields()->useUrlEncoding(true); - } - - if ($postFields !== false) { - if ($method == 'POST') { - unset($curlOptions[CURLOPT_CUSTOMREQUEST]); - $curlOptions[CURLOPT_POST] = true; - } - $curlOptions[CURLOPT_POSTFIELDS] = $postFields; - $request->removeHeader('Content-Length'); - } - } - - // If the Expect header is not present, prevent curl from adding it - if (!$request->hasHeader('Expect')) { - $curlOptions[CURLOPT_HTTPHEADER][] = 'Expect:'; - } - } - - // If a Content-Length header was specified but we want to allow curl to set one for us - if (null !== $tempContentLength) { - $request->removeHeader('Content-Length'); - } - - // Set custom cURL options - foreach ($requestCurlOptions->toArray() as $key => $value) { - if (is_numeric($key)) { - $curlOptions[$key] = $value; - } - } - - // Do not set an Accept header by default - if (!isset($curlOptions[CURLOPT_ENCODING])) { - $curlOptions[CURLOPT_HTTPHEADER][] = 'Accept:'; - } - - // Add any custom headers to the request. Empty headers will cause curl to not send the header at all. - foreach ($request->getHeaderLines() as $line) { - $curlOptions[CURLOPT_HTTPHEADER][] = $line; - } - - // Add the content-length header back if it was temporarily removed - if ($tempContentLength) { - $request->setHeader('Content-Length', $tempContentLength); - } - - // Apply the options to a new cURL handle. - $handle = curl_init(); - - // Enable the progress function if the 'progress' param was set - if ($requestCurlOptions->get('progress')) { - // Wrap the function in a function that provides the curl handle to the mediator's progress function - // Using this rather than injecting the handle into the mediator prevents a circular reference - $curlOptions[CURLOPT_PROGRESSFUNCTION] = function () use ($mediator, $handle) { - $args = func_get_args(); - $args[] = $handle; - call_user_func_array(array($mediator, 'progress'), $args); - }; - $curlOptions[CURLOPT_NOPROGRESS] = false; - } - - curl_setopt_array($handle, $curlOptions); - - return new static($handle, $curlOptions); - } - - /** - * Construct a new CurlHandle object that wraps a cURL handle - * - * @param resource $handle Configured cURL handle resource - * @param Collection|array $options Curl options to use with the handle - * - * @throws InvalidArgumentException - */ - public function __construct($handle, $options) - { - if (!is_resource($handle)) { - throw new InvalidArgumentException('Invalid handle provided'); - } - if (is_array($options)) { - $this->options = new Collection($options); - } elseif ($options instanceof Collection) { - $this->options = $options; - } else { - throw new InvalidArgumentException('Expected array or Collection'); - } - $this->handle = $handle; - } - - /** - * Destructor - */ - public function __destruct() - { - $this->close(); - } - - /** - * Close the curl handle - */ - public function close() - { - if (is_resource($this->handle)) { - curl_close($this->handle); - } - $this->handle = null; - } - - /** - * Check if the handle is available and still OK - * - * @return bool - */ - public function isAvailable() - { - return is_resource($this->handle); - } - - /** - * Get the last error that occurred on the cURL handle - * - * @return string - */ - public function getError() - { - return $this->isAvailable() ? curl_error($this->handle) : ''; - } - - /** - * Get the last error number that occurred on the cURL handle - * - * @return int - */ - public function getErrorNo() - { - if ($this->errorNo) { - return $this->errorNo; - } - - return $this->isAvailable() ? curl_errno($this->handle) : CURLE_OK; - } - - /** - * Set the curl error number - * - * @param int $error Error number to set - * - * @return CurlHandle - */ - public function setErrorNo($error) - { - $this->errorNo = $error; - - return $this; - } - - /** - * Get cURL curl_getinfo data - * - * @param int $option Option to retrieve. Pass null to retrieve all data as an array. - * - * @return array|mixed - */ - public function getInfo($option = null) - { - if (!is_resource($this->handle)) { - return null; - } - - if (null !== $option) { - return curl_getinfo($this->handle, $option) ?: null; - } - - return curl_getinfo($this->handle) ?: array(); - } - - /** - * Get the stderr output - * - * @param bool $asResource Set to TRUE to get an fopen resource - * - * @return string|resource|null - */ - public function getStderr($asResource = false) - { - $stderr = $this->getOptions()->get(CURLOPT_STDERR); - if (!$stderr) { - return null; - } - - if ($asResource) { - return $stderr; - } - - fseek($stderr, 0); - $e = stream_get_contents($stderr); - fseek($stderr, 0, SEEK_END); - - return $e; - } - - /** - * Get the URL that this handle is connecting to - * - * @return Url - */ - public function getUrl() - { - return Url::factory($this->options->get(CURLOPT_URL)); - } - - /** - * Get the wrapped curl handle - * - * @return resource|null Returns the cURL handle or null if it was closed - */ - public function getHandle() - { - return $this->isAvailable() ? $this->handle : null; - } - - /** - * Get the cURL setopt options of the handle. Changing values in the return object will have no effect on the curl - * handle after it is created. - * - * @return Collection - */ - public function getOptions() - { - return $this->options; - } - - /** - * Update a request based on the log messages of the CurlHandle - * - * @param RequestInterface $request Request to update - */ - public function updateRequestFromTransfer(RequestInterface $request) - { - if (!$request->getResponse()) { - return; - } - - // Update the transfer stats of the response - $request->getResponse()->setInfo($this->getInfo()); - - if (!$log = $this->getStderr(true)) { - return; - } - - // Parse the cURL stderr output for outgoing requests - $headers = ''; - fseek($log, 0); - while (($line = fgets($log)) !== false) { - if ($line && $line[0] == '>') { - $headers = substr(trim($line), 2) . "\r\n"; - while (($line = fgets($log)) !== false) { - if ($line[0] == '*' || $line[0] == '<') { - break; - } else { - $headers .= trim($line) . "\r\n"; - } - } - } - } - - // Add request headers to the request exactly as they were sent - if ($headers) { - $parsed = ParserRegistry::getInstance()->getParser('message')->parseRequest($headers); - if (!empty($parsed['headers'])) { - $request->setHeaders(array()); - foreach ($parsed['headers'] as $name => $value) { - $request->setHeader($name, $value); - } - } - if (!empty($parsed['version'])) { - $request->setProtocolVersion($parsed['version']); - } - } - } - - /** - * Parse the config and replace curl.* configurators into the constant based values so it can be used elsewhere - * - * @param array|Collection $config The configuration we want to parse - * - * @return array - */ - public static function parseCurlConfig($config) - { - $curlOptions = array(); - foreach ($config as $key => $value) { - if (is_string($key) && defined($key)) { - // Convert constants represented as string to constant int values - $key = constant($key); - } - if (is_string($value) && defined($value)) { - $value = constant($value); - } - $curlOptions[$key] = $value; - } - - return $curlOptions; - } -} diff --git a/core/vendor/guzzle/http/Guzzle/Http/Curl/CurlMulti.php b/core/vendor/guzzle/http/Guzzle/Http/Curl/CurlMulti.php deleted file mode 100644 index a8c569984d2a08a567869aee29085478f83b0ef2..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/http/Guzzle/Http/Curl/CurlMulti.php +++ /dev/null @@ -1,390 +0,0 @@ -<?php - -namespace Guzzle\Http\Curl; - -use Guzzle\Common\AbstractHasDispatcher; -use Guzzle\Common\Event; -use Guzzle\Http\Exception\MultiTransferException; -use Guzzle\Http\Exception\CurlException; -use Guzzle\Http\Message\RequestInterface; - -/** - * Send {@see RequestInterface} objects in parallel using curl_multi - */ -class CurlMulti extends AbstractHasDispatcher implements CurlMultiInterface -{ - /** @var resource cURL multi handle. */ - protected $multiHandle; - - /** @var array Attached {@see RequestInterface} objects. */ - protected $requests; - - /** @var \SplObjectStorage RequestInterface to CurlHandle hash */ - protected $handles; - - /** @var array Hash mapping curl handle resource IDs to request objects */ - protected $resourceHash; - - /** @var array Queued exceptions */ - protected $exceptions = array(); - - /** @var array Requests that succeeded */ - protected $successful = array(); - - /** @var array cURL multi error values and codes */ - protected $multiErrors = array( - CURLM_BAD_HANDLE => array('CURLM_BAD_HANDLE', 'The passed-in handle is not a valid CURLM handle.'), - CURLM_BAD_EASY_HANDLE => array('CURLM_BAD_EASY_HANDLE', "An easy handle was not good/valid. It could mean that it isn't an easy handle at all, or possibly that the handle already is in used by this or another multi handle."), - CURLM_OUT_OF_MEMORY => array('CURLM_OUT_OF_MEMORY', 'You are doomed.'), - CURLM_INTERNAL_ERROR => array('CURLM_INTERNAL_ERROR', 'This can only be returned if libcurl bugs. Please report it to us!') - ); - - public function __construct() - { - $this->multiHandle = curl_multi_init(); - // @codeCoverageIgnoreStart - if ($this->multiHandle === false) { - throw new CurlException('Unable to create multi handle'); - } - // @codeCoverageIgnoreEnd - $this->reset(); - } - - public function __destruct() - { - if (is_resource($this->multiHandle)) { - curl_multi_close($this->multiHandle); - } - } - - public function add(RequestInterface $request) - { - $this->requests[] = $request; - // If requests are currently transferring and this is async, then the - // request must be prepared now as the send() method is not called. - $this->beforeSend($request); - $this->dispatch(self::ADD_REQUEST, array('request' => $request)); - - return $this; - } - - public function all() - { - return $this->requests; - } - - public function remove(RequestInterface $request) - { - $this->removeHandle($request); - foreach ($this->requests as $i => $r) { - if ($request === $r) { - unset($this->requests[$i]); - $this->requests = array_values($this->requests); - $this->dispatch(self::REMOVE_REQUEST, array('request' => $request)); - return true; - } - } - - return false; - } - - public function reset($hard = false) - { - // Remove each request - if ($this->requests) { - foreach ($this->requests as $request) { - $this->remove($request); - } - } - - $this->handles = new \SplObjectStorage(); - $this->requests = $this->resourceHash = $this->exceptions = $this->successful = array(); - } - - public function send() - { - $this->perform(); - $exceptions = $this->exceptions; - $successful = $this->successful; - $this->reset(); - - if ($exceptions) { - $this->throwMultiException($exceptions, $successful); - } - } - - public function count() - { - return count($this->requests); - } - - /** - * Build and throw a MultiTransferException - * - * @param array $exceptions Exceptions encountered - * @param array $successful Successful requests - * @throws MultiTransferException - */ - protected function throwMultiException(array $exceptions, array $successful) - { - $multiException = new MultiTransferException('Errors during multi transfer'); - - while ($e = array_shift($exceptions)) { - $multiException->add($e['exception']); - $multiException->addFailedRequest($e['request']); - } - - // Add successful requests - foreach ($successful as $request) { - if (!$multiException->containsRequest($request)) { - $multiException->addSuccessfulRequest($request); - } - } - - throw $multiException; - } - - /** - * Prepare for sending - * - * @param RequestInterface $request Request to prepare - * @throws \Exception on error preparing the request - */ - protected function beforeSend(RequestInterface $request) - { - try { - $state = $request->setState(RequestInterface::STATE_TRANSFER); - if ($state == RequestInterface::STATE_TRANSFER) { - // Add the request curl handle to the multi handle - $this->checkCurlResult(curl_multi_add_handle($this->multiHandle, $this->createCurlHandle($request)->getHandle())); - } else { - // Requests might decide they don't need to be sent just before transfer (e.g. CachePlugin) - $this->remove($request); - if ($state == RequestInterface::STATE_COMPLETE) { - $this->successful[] = $request; - } - } - } catch (\Exception $e) { - // Queue the exception to be thrown when sent - $this->removeErroredRequest($request, $e); - } - } - - /** - * Create a curl handle for a request - * - * @param RequestInterface $request Request - * - * @return CurlHandle - */ - protected function createCurlHandle(RequestInterface $request) - { - $wrapper = CurlHandle::factory($request); - $this->handles[$request] = $wrapper; - $this->resourceHash[(int) $wrapper->getHandle()] = $request; - - return $wrapper; - } - - /** - * Get the data from the multi handle - */ - protected function perform() - { - if (!$this->requests) { - return; - } - - // Initialize the handles with a very quick select timeout - $active = $mrc = null; - $this->executeHandles($active, $mrc, 0.001); - $event = new Event(array('curl_multi' => $this)); - $this->processMessages(); - - while ($this->requests) { - - // Notify each request as polling - $blocking = $total = 0; - foreach ($this->requests as $request) { - ++$total; - $event['request'] = $request; - $request->getEventDispatcher()->dispatch(self::POLLING_REQUEST, $event); - // The blocking variable just has to be non-falsey to block the loop - if ($request->getParams()->hasKey(self::BLOCKING)) { - ++$blocking; - } - } - - if ($blocking == $total) { - // Sleep to prevent eating CPU because no requests are actually pending a select call - usleep(500); - } else { - do { - $this->executeHandles($active, $mrc, 1); - } while ($active); - } - $this->processMessages(); - } - } - - /** - * Process any received curl multi messages - */ - private function processMessages() - { - // Get messages from curl handles - while ($done = curl_multi_info_read($this->multiHandle)) { - try { - $request = $this->resourceHash[(int) $done['handle']]; - $this->processResponse($request, $this->handles[$request], $done); - $this->successful[] = $request; - } catch (MultiTransferException $e) { - $this->removeErroredRequest($request, $e, false); - throw $e; - } catch (\Exception $e) { - $this->removeErroredRequest($request, $e); - } - } - } - - /** - * Execute and select curl handles until there is activity - * - * @param int $active Active value to update - * @param int $mrc Multi result value to update - * @param int $timeout Select timeout in seconds - */ - private function executeHandles(&$active, &$mrc, $timeout = 1) - { - do { - $mrc = curl_multi_exec($this->multiHandle, $active); - } while ($mrc == CURLM_CALL_MULTI_PERFORM && $active); - $this->checkCurlResult($mrc); - - // @codeCoverageIgnoreStart - // Select the curl handles until there is any activity on any of the open file descriptors - // See https://github.com/php/php-src/blob/master/ext/curl/multi.c#L170 - if ($active && $mrc == CURLM_OK && curl_multi_select($this->multiHandle, $timeout) == -1) { - // Perform a usleep if a previously executed select returned -1 - // @see https://bugs.php.net/bug.php?id=61141 - usleep(100); - } - // @codeCoverageIgnoreEnd - } - - /** - * Remove a request that encountered an exception - * - * @param RequestInterface $request Request to remove - * @param \Exception $e Exception encountered - * @param bool $buffer Set to false to not buffer the exception - */ - protected function removeErroredRequest(RequestInterface $request, \Exception $e = null, $buffer = true) - { - if ($buffer) { - $this->exceptions[] = array('request' => $request, 'exception' => $e); - } - - $this->remove($request); - $this->dispatch(self::MULTI_EXCEPTION, array('exception' => $e, 'all_exceptions' => $this->exceptions)); - } - - /** - * Check for errors and fix headers of a request based on a curl response - * - * @param RequestInterface $request Request to process - * @param CurlHandle $handle Curl handle object - * @param array $curl Array returned from curl_multi_info_read - * - * @throws CurlException on Curl error - */ - protected function processResponse(RequestInterface $request, CurlHandle $handle, array $curl) - { - // Set the transfer stats on the response - $handle->updateRequestFromTransfer($request); - // Check if a cURL exception occurred, and if so, notify things - $curlException = $this->isCurlException($request, $handle, $curl); - - // Always remove completed curl handles. They can be added back again - // via events if needed (e.g. ExponentialBackoffPlugin) - $this->removeHandle($request); - - if (!$curlException) { - $state = $request->setState(RequestInterface::STATE_COMPLETE, array('handle' => $handle)); - // Only remove the request if it wasn't resent as a result of the state change - if ($state != RequestInterface::STATE_TRANSFER) { - $this->remove($request); - } - } else { - // Set the state of the request to an error - $state = $request->setState(RequestInterface::STATE_ERROR, array('exception' => $curlException)); - // Allow things to ignore the error if possible - if ($state != RequestInterface::STATE_TRANSFER) { - $this->remove($request); - } - // The error was not handled, so fail - if ($state == RequestInterface::STATE_ERROR) { - /** @var CurlException $curlException */ - throw $curlException; - } - } - } - - /** - * Remove a curl handle from the curl multi object - * - * @param RequestInterface $request Request that owns the handle - */ - protected function removeHandle(RequestInterface $request) - { - if (isset($this->handles[$request])) { - $handle = $this->handles[$request]; - unset($this->handles[$request]); - unset($this->resourceHash[(int) $handle->getHandle()]); - curl_multi_remove_handle($this->multiHandle, $handle->getHandle()); - $handle->close(); - } - } - - /** - * Check if a cURL transfer resulted in what should be an exception - * - * @param RequestInterface $request Request to check - * @param CurlHandle $handle Curl handle object - * @param array $curl Array returned from curl_multi_info_read - * - * @return CurlException|bool - */ - private function isCurlException(RequestInterface $request, CurlHandle $handle, array $curl) - { - if (CURLM_OK == $curl['result'] || CURLM_CALL_MULTI_PERFORM == $curl['result']) { - return false; - } - - $handle->setErrorNo($curl['result']); - $e = new CurlException(sprintf('[curl] %s: %s [url] %s', - $handle->getErrorNo(), $handle->getError(), $handle->getUrl())); - $e->setCurlHandle($handle) - ->setRequest($request) - ->setCurlInfo($handle->getInfo()) - ->setError($handle->getError(), $handle->getErrorNo()); - - return $e; - } - - /** - * Throw an exception for a cURL multi response if needed - * - * @param int $code Curl response code - * @throws CurlException - */ - private function checkCurlResult($code) - { - if ($code != CURLM_OK && $code != CURLM_CALL_MULTI_PERFORM) { - throw new CurlException(isset($this->multiErrors[$code]) - ? "cURL error: {$code} ({$this->multiErrors[$code][0]}): cURL message: {$this->multiErrors[$code][1]}" - : 'Unexpected cURL error: ' . $code - ); - } - } -} diff --git a/core/vendor/guzzle/http/Guzzle/Http/Curl/CurlMultiInterface.php b/core/vendor/guzzle/http/Guzzle/Http/Curl/CurlMultiInterface.php deleted file mode 100644 index 0ead7573502be73f53fe96851d3ac8ac538f81e7..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/http/Guzzle/Http/Curl/CurlMultiInterface.php +++ /dev/null @@ -1,58 +0,0 @@ -<?php - -namespace Guzzle\Http\Curl; - -use Guzzle\Common\HasDispatcherInterface; -use Guzzle\Common\Exception\ExceptionCollection; -use Guzzle\Http\Message\RequestInterface; - -/** - * Interface for sending a pool of {@see RequestInterface} objects in parallel - */ -interface CurlMultiInterface extends \Countable, HasDispatcherInterface -{ - const POLLING_REQUEST = 'curl_multi.polling_request'; - const ADD_REQUEST = 'curl_multi.add_request'; - const REMOVE_REQUEST = 'curl_multi.remove_request'; - const MULTI_EXCEPTION = 'curl_multi.exception'; - const BLOCKING = 'curl_multi.blocking'; - - /** - * Add a request to the pool. - * - * @param RequestInterface $request Request to add - * - * @return CurlMultiInterface - */ - public function add(RequestInterface $request); - - /** - * Get an array of attached {@see RequestInterface} objects - * - * @return array - */ - public function all(); - - /** - * Remove a request from the pool. - * - * @param RequestInterface $request Request to remove - * - * @return bool Returns true on success or false on failure - */ - public function remove(RequestInterface $request); - - /** - * Reset the state and remove any attached RequestInterface objects - * - * @param bool $hard Set to true to close and reopen any open multi handles - */ - public function reset($hard = false); - - /** - * Send a pool of {@see RequestInterface} requests. - * - * @throws ExceptionCollection if any requests threw exceptions during the transfer. - */ - public function send(); -} diff --git a/core/vendor/guzzle/http/Guzzle/Http/Curl/CurlMultiProxy.php b/core/vendor/guzzle/http/Guzzle/Http/Curl/CurlMultiProxy.php deleted file mode 100644 index 665f029a79d9b841bc2d1addd34f2932c52b2332..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/http/Guzzle/Http/Curl/CurlMultiProxy.php +++ /dev/null @@ -1,147 +0,0 @@ -<?php - -namespace Guzzle\Http\Curl; - -use Guzzle\Common\AbstractHasDispatcher; -use Guzzle\Http\Message\RequestInterface; - -/** - * Proxies requests and connections to a pool of internal curl_multi handles. Each recursive call will add requests - * to the next available CurlMulti handle. - */ -class CurlMultiProxy extends AbstractHasDispatcher implements CurlMultiInterface -{ - protected $handles = array(); - protected $groups = array(); - protected $queued = array(); - protected $maxHandles; - - /** - * @param int $maxHandles The maximum number of idle CurlMulti handles to allow to remain open - */ - public function __construct($maxHandles = 3) - { - $this->maxHandles = $maxHandles; - // You can get some weird "Too many open files" errors when sending a large amount of requests in parallel. - // These two statements autoload classes before a system runs out of file descriptors so that you can get back - // valuable error messages if you run out. - class_exists('Guzzle\Http\Message\Response'); - class_exists('Guzzle\Http\Exception\CurlException'); - } - - public function add(RequestInterface $request) - { - $this->queued[] = $request; - - return $this; - } - - public function all() - { - $requests = $this->queued; - foreach ($this->handles as $handle) { - $requests = array_merge($requests, $handle->all()); - } - - return $requests; - } - - public function remove(RequestInterface $request) - { - foreach ($this->queued as $i => $r) { - if ($request === $r) { - unset($this->queued[$i]); - return true; - } - } - - foreach ($this->handles as $handle) { - if ($handle->remove($request)) { - return true; - } - } - - return false; - } - - public function reset($hard = false) - { - $this->queued = array(); - $this->groups = array(); - foreach ($this->handles as $handle) { - $handle->reset(); - } - if ($hard) { - $this->handles = array(); - } - - return $this; - } - - public function send() - { - if ($this->queued) { - $group = $this->getAvailableHandle(); - // Add this handle to a list of handles than is claimed - $this->groups[] = $group; - while ($request = array_shift($this->queued)) { - $group->add($request); - } - try { - $group->send(); - array_pop($this->groups); - $this->cleanupHandles(); - } catch (\Exception $e) { - // Remove the group and cleanup if an exception was encountered and no more requests in group - if (!$group->count()) { - array_pop($this->groups); - $this->cleanupHandles(); - } - throw $e; - } - } - } - - public function count() - { - return count($this->all()); - } - - /** - * Get an existing available CurlMulti handle or create a new one - * - * @return CurlMulti - */ - protected function getAvailableHandle() - { - // Grab a handle that is not claimed - foreach ($this->handles as $h) { - if (!in_array($h, $this->groups, true)) { - return $h; - } - } - - // All are claimed, so create one - $handle = new CurlMulti(); - $handle->setEventDispatcher($this->getEventDispatcher()); - $this->handles[] = $handle; - - return $handle; - } - - /** - * Trims down unused CurlMulti handles to limit the number of open connections - */ - protected function cleanupHandles() - { - if ($diff = max(0, count($this->handles) - $this->maxHandles)) { - for ($i = count($this->handles) - 1; $i > 0 && $diff > 0; $i--) { - if (!count($this->handles[$i])) { - unset($this->handles[$i]); - $diff--; - } - } - $this->handles = array_values($this->handles); - } - } -} diff --git a/core/vendor/guzzle/http/Guzzle/Http/Curl/CurlVersion.php b/core/vendor/guzzle/http/Guzzle/Http/Curl/CurlVersion.php deleted file mode 100644 index c3f99dd25dd8efc625154ab42d1814a7f5fe67d7..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/http/Guzzle/Http/Curl/CurlVersion.php +++ /dev/null @@ -1,66 +0,0 @@ -<?php - -namespace Guzzle\Http\Curl; - -/** - * Class used for querying curl_version data - */ -class CurlVersion -{ - /** @var array curl_version() information */ - protected $version; - - /** @var CurlVersion */ - protected static $instance; - - /** @var string Default user agent */ - protected $userAgent; - - /** - * @return CurlVersion - */ - public static function getInstance() - { - if (!self::$instance) { - self::$instance = new self(); - } - - return self::$instance; - } - - /** - * Get all of the curl_version() data - * - * @return array - */ - public function getAll() - { - if (!$this->version) { - $this->version = curl_version(); - } - - return $this->version; - } - - /** - * Get a specific type of curl information - * - * @param string $type Version information to retrieve. This value is one of: - * - version_number: cURL 24 bit version number - * - version: cURL version number, as a string - * - ssl_version_number: OpenSSL 24 bit version number - * - ssl_version: OpenSSL version number, as a string - * - libz_version: zlib version number, as a string - * - host: Information about the host where cURL was built - * - features: A bitmask of the CURL_VERSION_XXX constants - * - protocols: An array of protocols names supported by cURL - * - * @return string|float|bool if the $type is found, and false if not found - */ - public function get($type) - { - $version = $this->getAll(); - - return isset($version[$type]) ? $version[$type] : false; - } -} diff --git a/core/vendor/guzzle/http/Guzzle/Http/Curl/RequestMediator.php b/core/vendor/guzzle/http/Guzzle/Http/Curl/RequestMediator.php deleted file mode 100644 index 54b1b0d57d359ae1ded7712275733d762ce6c687..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/http/Guzzle/Http/Curl/RequestMediator.php +++ /dev/null @@ -1,142 +0,0 @@ -<?php - -namespace Guzzle\Http\Curl; - -use Guzzle\Http\Message\RequestInterface; -use Guzzle\Http\EntityBody; -use Guzzle\Http\Message\Response; - -/** - * Mediator between curl handles and request objects - */ -class RequestMediator -{ - /** @var RequestInterface */ - protected $request; - - /** @var bool Whether or not to emit read/write events */ - protected $emitIo; - - /** - * @param RequestInterface $request Request to mediate - * @param bool $emitIo Set to true to dispatch events on input and output - */ - public function __construct(RequestInterface $request, $emitIo = false) - { - $this->request = $request; - $this->emitIo = $emitIo; - } - - /** - * Receive a response header from curl - * - * @param resource $curl Curl handle - * @param string $header Received header - * - * @return int - */ - public function receiveResponseHeader($curl, $header) - { - static $normalize = array("\r", "\n"); - $length = strlen($header); - $header = str_replace($normalize, '', $header); - - if (strpos($header, 'HTTP/') === 0) { - - $startLine = explode(' ', $header, 3); - $code = $startLine[1]; - $status = isset($startLine[2]) ? $startLine[2] : ''; - - // Only download the body of the response to the specified response - // body when a successful response is received. - if ($code >= 200 && $code < 300) { - $body = $this->request->getResponseBody(); - } else { - $body = EntityBody::factory(); - } - - $response = new Response($code, null, $body); - $response->setStatus($code, $status); - $this->request->startResponse($response); - - $this->request->dispatch('request.receive.status_line', array( - 'request' => $this, - 'line' => $header, - 'status_code' => $code, - 'reason_phrase' => $status - )); - - } elseif ($pos = strpos($header, ':')) { - $this->request->getResponse()->addHeader( - trim(substr($header, 0, $pos)), - trim(substr($header, $pos + 1)) - ); - } - - return $length; - } - - /** - * Received a progress notification - * - * @param int $downloadSize Total download size - * @param int $downloaded Amount of bytes downloaded - * @param int $uploadSize Total upload size - * @param int $uploaded Amount of bytes uploaded - * @param resource $handle CurlHandle object - */ - public function progress($downloadSize, $downloaded, $uploadSize, $uploaded, $handle = null) - { - $this->request->dispatch('curl.callback.progress', array( - 'request' => $this->request, - 'handle' => $handle, - 'download_size' => $downloadSize, - 'downloaded' => $downloaded, - 'upload_size' => $uploadSize, - 'uploaded' => $uploaded - )); - } - - /** - * Write data to the response body of a request - * - * @param resource $curl Curl handle - * @param string $write Data that was received - * - * @return int - */ - public function writeResponseBody($curl, $write) - { - if ($this->emitIo) { - $this->request->dispatch('curl.callback.write', array( - 'request' => $this->request, - 'write' => $write - )); - } - - return $this->request->getResponse()->getBody()->write($write); - } - - /** - * Read data from the request body and send it to curl - * - * @param resource $ch Curl handle - * @param resource $fd File descriptor - * @param int $length Amount of data to read - * - * @return string - */ - public function readRequestBody($ch, $fd, $length) - { - if (!($body = $this->request->getBody())) { - return ''; - } - - $read = (string) $body->read($length); - if ($this->emitIo) { - $this->request->dispatch('curl.callback.read', array('request' => $this->request, 'read' => $read)); - } - - return $read; - } -} diff --git a/core/vendor/guzzle/http/Guzzle/Http/EntityBody.php b/core/vendor/guzzle/http/Guzzle/Http/EntityBody.php deleted file mode 100644 index b60d170f02a88699df8724b2c559897f445affcf..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/http/Guzzle/Http/EntityBody.php +++ /dev/null @@ -1,201 +0,0 @@ -<?php - -namespace Guzzle\Http; - -use Guzzle\Common\Version; -use Guzzle\Stream\Stream; -use Guzzle\Common\Exception\InvalidArgumentException; -use Guzzle\Http\Mimetypes; - -/** - * Entity body used with an HTTP request or response - */ -class EntityBody extends Stream implements EntityBodyInterface -{ - /** @var bool Content-Encoding of the entity body if known */ - protected $contentEncoding = false; - - /** @var callable Method to invoke for rewinding a stream */ - protected $rewindFunction; - - /** - * Create a new EntityBody based on the input type - * - * @param resource|string|EntityBody $resource Entity body data - * @param int $size Size of the data contained in the resource - * - * @return EntityBody - * @throws InvalidArgumentException if the $resource arg is not a resource or string - */ - public static function factory($resource = '', $size = null) - { - if ($resource instanceof EntityBodyInterface) { - return $resource; - } - - switch (gettype($resource)) { - case 'string': - return self::fromString($resource); - case 'resource': - return new static($resource, $size); - case 'object': - if (method_exists($resource, '__toString')) { - return self::fromString((string) $resource); - } - break; - case 'array': - return self::fromString(http_build_query($resource)); - } - - throw new InvalidArgumentException('Invalid resource type'); - } - - public function setRewindFunction($callable) - { - if (!is_callable($callable)) { - throw new InvalidArgumentException('Must specify a callable'); - } - - $this->rewindFunction = $callable; - - return $this; - } - - public function rewind() - { - return $this->rewindFunction ? call_user_func($this->rewindFunction, $this) : parent::rewind(); - } - - /** - * Create a new EntityBody from a string - * - * @param string $string String of data - * - * @return EntityBody - */ - public static function fromString($string) - { - $stream = fopen('php://temp', 'r+'); - if ($string !== '') { - fwrite($stream, $string); - rewind($stream); - } - - return new static($stream); - } - - public function compress($filter = 'zlib.deflate') - { - $result = $this->handleCompression($filter); - $this->contentEncoding = $result ? $filter : false; - - return $result; - } - - public function uncompress($filter = 'zlib.inflate') - { - $offsetStart = 0; - - // When inflating gzipped data, the first 10 bytes must be stripped - // if a gzip header is present - if ($filter == 'zlib.inflate') { - // @codeCoverageIgnoreStart - if (!$this->isReadable() || ($this->isConsumed() && !$this->isSeekable())) { - return false; - } - // @codeCoverageIgnoreEnd - if (stream_get_contents($this->stream, 3, 0) === "\x1f\x8b\x08") { - $offsetStart = 10; - } - } - - $this->contentEncoding = false; - - return $this->handleCompression($filter, $offsetStart); - } - - public function getContentLength() - { - return $this->getSize(); - } - - public function getContentType() - { - return $this->getUri() ? Mimetypes::getInstance()->fromFilename($this->getUri()) : null; - } - - public function getContentMd5($rawOutput = false, $base64Encode = false) - { - if ($hash = self::getHash($this, 'md5', $rawOutput)) { - return $hash && $base64Encode ? base64_encode($hash) : $hash; - } else { - return false; - } - } - - /** - * Calculate the MD5 hash of an entity body - * - * @param EntityBodyInterface $body Entity body to calculate the hash for - * @param bool $rawOutput Whether or not to use raw output - * @param bool $base64Encode Whether or not to base64 encode raw output (only if raw output is true) - * - * @return bool|string Returns an MD5 string on success or FALSE on failure - * @deprecated This will be deprecated soon - * @codeCoverageIgnore - */ - public static function calculateMd5(EntityBodyInterface $body, $rawOutput = false, $base64Encode = false) - { - Version::warn(__CLASS__ . ' is deprecated. Use getContentMd5()'); - return $body->getContentMd5($rawOutput, $base64Encode); - } - - public function setStreamFilterContentEncoding($streamFilterContentEncoding) - { - $this->contentEncoding = $streamFilterContentEncoding; - - return $this; - } - - public function getContentEncoding() - { - return strtr($this->contentEncoding, array( - 'zlib.deflate' => 'gzip', - 'bzip2.compress' => 'compress' - )) ?: false; - } - - protected function handleCompression($filter, $offsetStart = 0) - { - // @codeCoverageIgnoreStart - if (!$this->isReadable() || ($this->isConsumed() && !$this->isSeekable())) { - return false; - } - // @codeCoverageIgnoreEnd - - $handle = fopen('php://temp', 'r+'); - $filter = @stream_filter_append($handle, $filter, STREAM_FILTER_WRITE); - if (!$filter) { - return false; - } - - // Seek to the offset start if possible - $this->seek($offsetStart); - while ($data = fread($this->stream, 8096)) { - fwrite($handle, $data); - } - - fclose($this->stream); - $this->stream = $handle; - stream_filter_remove($filter); - $stat = fstat($this->stream); - $this->size = $stat['size']; - $this->rebuildCache(); - $this->seek(0); - - // Remove any existing rewind function as the underlying stream has been replaced - $this->rewindFunction = null; - - return true; - } -} diff --git a/core/vendor/guzzle/http/Guzzle/Http/EntityBodyInterface.php b/core/vendor/guzzle/http/Guzzle/Http/EntityBodyInterface.php deleted file mode 100644 index e640f578501c65b8933144724273b1ed07eb5ee7..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/http/Guzzle/Http/EntityBodyInterface.php +++ /dev/null @@ -1,73 +0,0 @@ -<?php - -namespace Guzzle\Http; - -use Guzzle\Stream\StreamInterface; - -/** - * Entity body used with an HTTP request or response - */ -interface EntityBodyInterface extends StreamInterface -{ - /** - * Specify a custom callback used to rewind a non-seekable stream. This can be useful entity enclosing requests - * that are redirected. - * - * @param mixed $callable Callable to invoke to rewind a non-seekable stream. The callback must accept an - * EntityBodyInterface object, perform the rewind if possible, and return a boolean - * representing whether or not the rewind was successful. - * @return self - */ - public function setRewindFunction($callable); - - /** - * If the stream is readable, compress the data in the stream using deflate compression. The uncompressed stream is - * then closed, and the compressed stream then becomes the wrapped stream. - * - * @param string $filter Compression filter - * - * @return bool Returns TRUE on success or FALSE on failure - */ - public function compress($filter = 'zlib.deflate'); - - /** - * Decompress a deflated string. Once uncompressed, the uncompressed string is then used as the wrapped stream. - * - * @param string $filter De-compression filter - * - * @return bool Returns TRUE on success or FALSE on failure - */ - public function uncompress($filter = 'zlib.inflate'); - - /** - * Get the Content-Length of the entity body if possible (alias of getSize) - * - * @return int|bool Returns the Content-Length or false on failure - */ - public function getContentLength(); - - /** - * Guess the Content-Type of a local stream - * - * @return string|null - * @see http://www.php.net/manual/en/function.finfo-open.php - */ - public function getContentType(); - - /** - * Get an MD5 checksum of the stream's contents - * - * @param bool $rawOutput Whether or not to use raw output - * @param bool $base64Encode Whether or not to base64 encode raw output (only if raw output is true) - * - * @return bool|string Returns an MD5 string on success or FALSE on failure - */ - public function getContentMd5($rawOutput = false, $base64Encode = false); - - /** - * Get the Content-Encoding of the EntityBody - * - * @return bool|string - */ - public function getContentEncoding(); -} diff --git a/core/vendor/guzzle/http/Guzzle/Http/Exception/BadResponseException.php b/core/vendor/guzzle/http/Guzzle/Http/Exception/BadResponseException.php deleted file mode 100644 index 9b98a8642ba78f02ccd12ff815748bdf47969782..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/http/Guzzle/Http/Exception/BadResponseException.php +++ /dev/null @@ -1,70 +0,0 @@ -<?php - -namespace Guzzle\Http\Exception; - -use Guzzle\Http\Message\RequestInterface; -use Guzzle\Http\Message\Response; - -/** - * Http request exception thrown when a bad response is received - */ -class BadResponseException extends RequestException -{ - /** @var Response */ - private $response; - - /** - * Factory method to create a new response exception based on the response code. - * - * @param RequestInterface $request Request - * @param Response $response Response received - * - * @return BadResponseException - */ - public static function factory(RequestInterface $request, Response $response) - { - if ($response->isClientError()) { - $label = 'Client error response'; - $class = __NAMESPACE__ . '\\ClientErrorResponseException'; - } elseif ($response->isServerError()) { - $label = 'Server error response'; - $class = __NAMESPACE__ . '\\ServerErrorResponseException'; - } else { - $label = 'Unsuccessful response'; - $class = __CLASS__; - $e = new self(); - } - - $message = $label . PHP_EOL . implode(PHP_EOL, array( - '[status code] ' . $response->getStatusCode(), - '[reason phrase] ' . $response->getReasonPhrase(), - '[url] ' . $request->getUrl(), - )); - - $e = new $class($message); - $e->setResponse($response); - $e->setRequest($request); - - return $e; - } - - /** - * Set the response that caused the exception - * - * @param Response $response Response to set - */ - public function setResponse(Response $response) - { - $this->response = $response; - } - - /** - * Get the response that caused the exception - * - * @return Response - */ - public function getResponse() - { - return $this->response; - } -} diff --git a/core/vendor/guzzle/http/Guzzle/Http/Exception/ClientErrorResponseException.php b/core/vendor/guzzle/http/Guzzle/Http/Exception/ClientErrorResponseException.php deleted file mode 100644 index 04d7ddc05ef03f776caab904712a5e0ced1cefa4..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/http/Guzzle/Http/Exception/ClientErrorResponseException.php +++ /dev/null @@ -1,8 +0,0 @@ -<?php - -namespace Guzzle\Http\Exception; - -/** - * Exception when a client error is encountered (4xx codes) - */ -class ClientErrorResponseException extends BadResponseException {} diff --git a/core/vendor/guzzle/http/Guzzle/Http/Exception/CouldNotRewindStreamException.php b/core/vendor/guzzle/http/Guzzle/Http/Exception/CouldNotRewindStreamException.php deleted file mode 100644 index 63e4ec74dec3841a747fb548212b079590bc997e..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/http/Guzzle/Http/Exception/CouldNotRewindStreamException.php +++ /dev/null @@ -1,7 +0,0 @@ -<?php - -namespace Guzzle\Http\Exception; - -use Guzzle\Common\Exception\RuntimeException; - -class CouldNotRewindStreamException extends RuntimeException implements HttpException {} diff --git a/core/vendor/guzzle/http/Guzzle/Http/Exception/CurlException.php b/core/vendor/guzzle/http/Guzzle/Http/Exception/CurlException.php deleted file mode 100644 index a6a744a74bb24e90cb7bc36a2a4b29cc2f3500a0..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/http/Guzzle/Http/Exception/CurlException.php +++ /dev/null @@ -1,101 +0,0 @@ -<?php - -namespace Guzzle\Http\Exception; - -use Guzzle\Http\Curl\CurlHandle; - -/** - * cURL request exception - */ -class CurlException extends RequestException -{ - private $curlError; - private $curlErrorNo; - private $handle; - private $curlInfo = array(); - - /** - * Set the cURL error message - * - * @param string $error Curl error - * @param int $number Curl error number - * - * @return self - */ - public function setError($error, $number) - { - $this->curlError = $error; - $this->curlErrorNo = $number; - - return $this; - } - - /** - * Set the associated curl handle - * - * @param CurlHandle $handle Curl handle - * - * @return self - */ - public function setCurlHandle(CurlHandle $handle) - { - $this->handle = $handle; - - return $this; - } - - /** - * Get the associated cURL handle - * - * @return CurlHandle|null - */ - public function getCurlHandle() - { - return $this->handle; - } - - /** - * Get the associated cURL error message - * - * @return string|null - */ - public function getError() - { - return $this->curlError; - } - - /** - * Get the associated cURL error number - * - * @return int|null - */ - public function getErrorNo() - { - return $this->curlErrorNo; - } - - /** - * Returns curl information about the transfer - * - * @return array - */ - public function getCurlInfo() - { - return $this->curlInfo; - } - - /** - * Set curl transfer information - * - * @param array $info Array of curl transfer information - * - * @return self - * @link http://php.net/manual/en/function.curl-getinfo.php - */ - public function setCurlInfo(array $info) - { - $this->curlInfo = $info; - - return $this; - } -} diff --git a/core/vendor/guzzle/http/Guzzle/Http/Exception/HttpException.php b/core/vendor/guzzle/http/Guzzle/Http/Exception/HttpException.php deleted file mode 100644 index ee87295d36eec2d398217fe832a33d1ac985416d..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/http/Guzzle/Http/Exception/HttpException.php +++ /dev/null @@ -1,10 +0,0 @@ -<?php - -namespace Guzzle\Http\Exception; - -use Guzzle\Common\Exception\GuzzleException; - -/** - * Http exception interface - */ -interface HttpException extends GuzzleException {} diff --git a/core/vendor/guzzle/http/Guzzle/Http/Exception/MultiTransferException.php b/core/vendor/guzzle/http/Guzzle/Http/Exception/MultiTransferException.php deleted file mode 100644 index 5bea80f3337f1cf35ce495d12d279943265ea4c2..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/http/Guzzle/Http/Exception/MultiTransferException.php +++ /dev/null @@ -1,113 +0,0 @@ -<?php - -namespace Guzzle\Http\Exception; - -use Guzzle\Common\Exception\ExceptionCollection; -use Guzzle\Http\Message\RequestInterface; - -/** - * Exception encountered during a multi transfer - */ -class MultiTransferException extends ExceptionCollection -{ - protected $successfulRequests = array(); - protected $failedRequests = array(); - - /** - * Get all of the requests in the transfer - * - * @return array - */ - public function getAllRequests() - { - return array_merge($this->successfulRequests, $this->failedRequests); - } - - /** - * Add to the array of successful requests - * - * @param RequestInterface $request Successful request - * - * @return self - */ - public function addSuccessfulRequest(RequestInterface $request) - { - $this->successfulRequests[] = $request; - - return $this; - } - - /** - * Add to the array of failed requests - * - * @param RequestInterface $request Failed request - * - * @return self - */ - public function addFailedRequest(RequestInterface $request) - { - $this->failedRequests[] = $request; - - return $this; - } - - /** - * Set all of the successful requests - * - * @param array Array of requests - * - * @return self - */ - public function setSuccessfulRequests(array $requests) - { - $this->successfulRequests = $requests; - - return $this; - } - - /** - * Set all of the failed requests - * - * @param array Array of requests - * - * @return self - */ - public function setFailedRequests(array $requests) - { - $this->failedRequests = $requests; - - return $this; - } - - /** - * Get an array of successful requests sent in the multi transfer - * - * @return array - */ - public function getSuccessfulRequests() - { - return $this->successfulRequests; - } - - /** - * Get an array of failed requests sent in the multi transfer - * - * @return array - */ - public function getFailedRequests() - { - return $this->failedRequests; - } - - /** - * Check if the exception object contains a request - * - * @param RequestInterface $request Request to check - * - * @return bool - */ - public function containsRequest(RequestInterface $request) - { - return in_array($request, $this->failedRequests, true) || in_array($request, $this->successfulRequests, true); - } -} diff --git a/core/vendor/guzzle/http/Guzzle/Http/Exception/RequestException.php b/core/vendor/guzzle/http/Guzzle/Http/Exception/RequestException.php deleted file mode 100644 index 274df2cb1673e1b01674521f56262c1ab41688c4..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/http/Guzzle/Http/Exception/RequestException.php +++ /dev/null @@ -1,39 +0,0 @@ -<?php - -namespace Guzzle\Http\Exception; - -use Guzzle\Common\Exception\RuntimeException; -use Guzzle\Http\Message\RequestInterface; - -/** - * Http request exception - */ -class RequestException extends RuntimeException implements HttpException -{ - /** @var RequestInterface */ - protected $request; - - /** - * Set the request that caused the exception - * - * @param RequestInterface $request Request to set - * - * @return RequestException - */ - public function setRequest(RequestInterface $request) - { - $this->request = $request; - - return $this; - } - - /** - * Get the request that caused the exception - * - * @return RequestInterface - */ - public function getRequest() - { - return $this->request; - } -} diff --git a/core/vendor/guzzle/http/Guzzle/Http/Exception/ServerErrorResponseException.php b/core/vendor/guzzle/http/Guzzle/Http/Exception/ServerErrorResponseException.php deleted file mode 100644 index f0f7cfe48107a251db887dc163c578bcada46689..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/http/Guzzle/Http/Exception/ServerErrorResponseException.php +++ /dev/null @@ -1,8 +0,0 @@ -<?php - -namespace Guzzle\Http\Exception; - -/** - * Exception when a server error is encountered (5xx codes) - */ -class ServerErrorResponseException extends BadResponseException {} diff --git a/core/vendor/guzzle/http/Guzzle/Http/Exception/TooManyRedirectsException.php b/core/vendor/guzzle/http/Guzzle/Http/Exception/TooManyRedirectsException.php deleted file mode 100644 index 2aa43d11d4fe1f95bdf821c785034b6373e62f4e..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/http/Guzzle/Http/Exception/TooManyRedirectsException.php +++ /dev/null @@ -1,5 +0,0 @@ -<?php - -namespace Guzzle\Http\Exception; - -class TooManyRedirectsException extends BadResponseException {} diff --git a/core/vendor/guzzle/http/Guzzle/Http/IoEmittingEntityBody.php b/core/vendor/guzzle/http/Guzzle/Http/IoEmittingEntityBody.php deleted file mode 100644 index ae32dee90571106e23e704365cd62a31d7b4ab0d..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/http/Guzzle/Http/IoEmittingEntityBody.php +++ /dev/null @@ -1,83 +0,0 @@ -<?php - -namespace Guzzle\Http; - -use Guzzle\Common\Event; -use Guzzle\Common\HasDispatcherInterface; -use Symfony\Component\EventDispatcher\EventDispatcher; -use Symfony\Component\EventDispatcher\EventSubscriberInterface; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; - -/** - * EntityBody decorator that emits events for read and write methods - */ -class IoEmittingEntityBody extends AbstractEntityBodyDecorator implements HasDispatcherInterface -{ - /** @var EventDispatcherInterface */ - protected $eventDispatcher; - - public static function getAllEvents() - { - return array('body.read', 'body.write'); - } - - /** - * {@inheritdoc} - * @codeCoverageIgnore - */ - public function setEventDispatcher(EventDispatcherInterface $eventDispatcher) - { - $this->eventDispatcher = $eventDispatcher; - - return $this; - } - - public function getEventDispatcher() - { - if (!$this->eventDispatcher) { - $this->eventDispatcher = new EventDispatcher(); - } - - return $this->eventDispatcher; - } - - public function dispatch($eventName, array $context = array()) - { - $this->getEventDispatcher()->dispatch($eventName, new Event($context)); - } - - /** - * {@inheritdoc} - * @codeCoverageIgnore - */ - public function addSubscriber(EventSubscriberInterface $subscriber) - { - $this->getEventDispatcher()->addSubscriber($subscriber); - - return $this; - } - - public function read($length) - { - $event = array( - 'body' => $this, - 'length' => $length, - 'read' => $this->body->read($length) - ); - $this->dispatch('body.read', $event); - - return $event['read']; - } - - public function write($string) - { - $event = array( - 'body' => $this, - 'write' => $string, - 'result' => $this->body->write($string) - ); - $this->dispatch('body.write', $event); - - return $event['result']; - } -} diff --git a/core/vendor/guzzle/http/Guzzle/Http/Message/AbstractMessage.php b/core/vendor/guzzle/http/Guzzle/Http/Message/AbstractMessage.php deleted file mode 100644 index 0d066ffceb098d7954b70fcda94bbac58fd3e16d..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/http/Guzzle/Http/Message/AbstractMessage.php +++ /dev/null @@ -1,220 +0,0 @@ -<?php - -namespace Guzzle\Http\Message; - -use Guzzle\Common\Version; -use Guzzle\Common\Collection; -use Guzzle\Http\Message\Header\HeaderCollection; -use Guzzle\Http\Message\Header\HeaderFactory; -use Guzzle\Http\Message\Header\HeaderFactoryInterface; -use Guzzle\Http\Message\Header\HeaderInterface; - -/** - * Abstract HTTP request/response message - */ -abstract class AbstractMessage implements MessageInterface -{ - /** @var array HTTP header collection */ - protected $headers; - - /** @var HeaderFactoryInterface $headerFactory */ - protected $headerFactory; - - /** @var Collection Custom message parameters that are extendable by plugins */ - protected $params; - - /** @var string Message protocol */ - protected $protocol = 'HTTP'; - - /** @var string HTTP protocol version of the message */ - protected $protocolVersion = '1.1'; - - public function __construct() - { - $this->params = new Collection(); - $this->headerFactory = new HeaderFactory(); - $this->headers = new HeaderCollection(); - } - - /** - * Set the header factory to use to create headers - * - * @param HeaderFactoryInterface $factory - * - * @return self - */ - public function setHeaderFactory(HeaderFactoryInterface $factory) - { - $this->headerFactory = $factory; - - return $this; - } - - public function getParams() - { - return $this->params; - } - - public function addHeader($header, $value) - { - if (isset($this->headers[$header])) { - $this->headers[$header]->add($value); - } elseif ($value instanceof HeaderInterface) { - $this->headers[$header] = $value; - } else { - $this->headers[$header] = $this->headerFactory->createHeader($header, $value); - } - - return $this; - } - - public function addHeaders(array $headers) - { - foreach ($headers as $key => $value) { - $this->addHeader($key, $value); - } - - return $this; - } - - public function getHeader($header) - { - return $this->headers[$header]; - } - - public function getHeaders() - { - return $this->headers; - } - - public function getHeaderLines() - { - $headers = array(); - foreach ($this->headers as $value) { - $headers[] = $value->getName() . ': ' . $value; - } - - return $headers; - } - - public function setHeader($header, $value) - { - unset($this->headers[$header]); - $this->addHeader($header, $value); - - return $this; - } - - public function setHeaders(array $headers) - { - $this->headers->clear(); - foreach ($headers as $key => $value) { - $this->addHeader($key, $value); - } - - return $this; - } - - public function hasHeader($header) - { - return isset($this->headers[$header]); - } - - public function removeHeader($header) - { - unset($this->headers[$header]); - - return $this; - } - - /** - * @deprecated Use $message->getHeader()->parseParams() - * @codeCoverageIgnore - */ - public function getTokenizedHeader($header, $token = ';') - { - Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader()->parseParams()'); - if ($this->hasHeader($header)) { - $data = new Collection(); - foreach ($this->getHeader($header)->parseParams() as $values) { - foreach ($values as $key => $value) { - if ($value === '') { - $data->set($data->count(), $key); - } else { - $data->add($key, $value); - } - } - } - return $data; - } - } - - /** - * @deprecated - * @codeCoverageIgnore - */ - public function setTokenizedHeader($header, $data, $token = ';') - { - Version::warn(__METHOD__ . ' is deprecated.'); - return $this; - } - - /** - * @deprecated - * @codeCoverageIgnore - */ - public function getCacheControlDirective($directive) - { - Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader(\'Cache-Control\')->getDirective()'); - if (!($header = $this->getHeader('Cache-Control'))) { - return null; - } - - return $header->getDirective($directive); - } - - /** - * @deprecated - * @codeCoverageIgnore - */ - public function hasCacheControlDirective($directive) - { - Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader(\'Cache-Control\')->hasDirective()'); - if ($header = $this->getHeader('Cache-Control')) { - return $header->hasDirective($directive); - } else { - return false; - } - } - - /** - * @deprecated - * @codeCoverageIgnore - */ - public function addCacheControlDirective($directive, $value = true) - { - Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader(\'Cache-Control\')->addDirective()'); - if (!($header = $this->getHeader('Cache-Control'))) { - $this->addHeader('Cache-Control', ''); - $header = $this->getHeader('Cache-Control'); - } - - $header->addDirective($directive, $value); - - return $this; - } - - /** - * @deprecated - * @codeCoverageIgnore - */ - public function removeCacheControlDirective($directive) - { - Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader(\'Cache-Control\')->removeDirective()'); - if ($header = $this->getHeader('Cache-Control')) { - $header->removeDirective($directive); - } - - return $this; - } -} diff --git a/core/vendor/guzzle/http/Guzzle/Http/Message/EntityEnclosingRequest.php b/core/vendor/guzzle/http/Guzzle/Http/Message/EntityEnclosingRequest.php deleted file mode 100644 index d9c83d815bed3ad55b0ea3b67ff1572099fb1186..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/http/Guzzle/Http/Message/EntityEnclosingRequest.php +++ /dev/null @@ -1,248 +0,0 @@ -<?php - -namespace Guzzle\Http\Message; - -use Guzzle\Http\EntityBody; -use Guzzle\Http\EntityBodyInterface; -use Guzzle\Http\QueryString; -use Guzzle\Http\RedirectPlugin; -use Guzzle\Http\Exception\RequestException; -use Guzzle\Http\Mimetypes; - -/** - * HTTP request that sends an entity-body in the request message (POST, PUT, PATCH, DELETE) - */ -class EntityEnclosingRequest extends Request implements EntityEnclosingRequestInterface -{ - /** @var int When the size of the body is greater than 1MB, then send Expect: 100-Continue */ - protected $expectCutoff = 1048576; - - /** @var EntityBodyInterface $body Body of the request */ - protected $body; - - /** @var QueryString POST fields to use in the EntityBody */ - protected $postFields; - - /** @var array POST files to send with the request */ - protected $postFiles = array(); - - public function __construct($method, $url, $headers = array()) - { - $this->postFields = new QueryString(); - parent::__construct($method, $url, $headers); - } - - /** - * @return string - */ - public function __toString() - { - // Only attempt to include the POST data if it's only fields - if (count($this->postFields) && empty($this->postFiles)) { - return parent::__toString() . (string) $this->postFields; - } - - return parent::__toString() . $this->body; - } - - public function setState($state, array $context = array()) - { - parent::setState($state, $context); - if ($state == self::STATE_TRANSFER && !$this->body && !count($this->postFields) && !count($this->postFiles)) { - $this->setHeader('Content-Length', 0)->removeHeader('Transfer-Encoding'); - } - - return $this->state; - } - - public function setBody($body, $contentType = null) - { - $this->body = EntityBody::factory($body); - - // Auto detect the Content-Type from the path of the request if possible - if ($contentType === null && !$this->hasHeader('Content-Type')) { - $contentType = $this->body->getContentType() ?: Mimetypes::getInstance()->fromFilename($this->getPath()); - } - - if ($contentType) { - $this->setHeader('Content-Type', $contentType); - } - - // Always add the Expect 100-Continue header if the body cannot be rewound. This helps with redirects. - if (!$this->body->isSeekable() && $this->expectCutoff !== false) { - $this->setHeader('Expect', '100-Continue'); - } - - // Set the Content-Length header if it can be determined - $size = $this->body->getContentLength(); - if ($size !== null && $size !== false) { - $this->setHeader('Content-Length', $size); - if ($size > $this->expectCutoff) { - $this->setHeader('Expect', '100-Continue'); - } - } elseif (!$this->hasHeader('Content-Length')) { - if ('1.1' == $this->protocolVersion) { - $this->setHeader('Transfer-Encoding', 'chunked'); - } else { - throw new RequestException( - 'Cannot determine Content-Length and cannot use chunked Transfer-Encoding when using HTTP/1.0' - ); - } - } - - return $this; - } - - public function getBody() - { - return $this->body; - } - - /** - * Set the size that the entity body of the request must exceed before adding the Expect: 100-Continue header. - * - * @param int|bool $size Cutoff in bytes. Set to false to never send the expect header (even with non-seekable data) - * - * @return self - */ - public function setExpectHeaderCutoff($size) - { - $this->expectCutoff = $size; - if ($size === false || !$this->body) { - $this->removeHeader('Expect'); - } elseif ($this->body && $this->body->getSize() && $this->body->getSize() > $size) { - $this->setHeader('Expect', '100-Continue'); - } - - return $this; - } - - public function configureRedirects($strict = false, $maxRedirects = 5) - { - $this->getParams()->set(RedirectPlugin::STRICT_REDIRECTS, $strict); - if ($maxRedirects == 0) { - $this->getParams()->set(RedirectPlugin::DISABLE, true); - } else { - $this->getParams()->set(RedirectPlugin::MAX_REDIRECTS, $maxRedirects); - } - - return $this; - } - - public function getPostField($field) - { - return $this->postFields->get($field); - } - - public function getPostFields() - { - return $this->postFields; - } - - public function setPostField($key, $value) - { - $this->postFields->set($key, $value); - $this->processPostFields(); - - return $this; - } - - public function addPostFields($fields) - { - $this->postFields->merge($fields); - $this->processPostFields(); - - return $this; - } - - public function removePostField($field) - { - $this->postFields->remove($field); - $this->processPostFields(); - - return $this; - } - - public function getPostFiles() - { - return $this->postFiles; - } - - public function getPostFile($fieldName) - { - return isset($this->postFiles[$fieldName]) ? $this->postFiles[$fieldName] : null; - } - - public function removePostFile($fieldName) - { - unset($this->postFiles[$fieldName]); - $this->processPostFields(); - - return $this; - } - - public function addPostFile($field, $filename = null, $contentType = null) - { - $data = null; - - if ($field instanceof PostFileInterface) { - $data = $field; - } elseif (is_array($filename)) { - // Allow multiple values to be set in a single key - foreach ($filename as $file) { - $this->addPostFile($field, $file, $contentType); - } - return $this; - } elseif (!is_string($filename)) { - throw new RequestException('The path to a file must be a string'); - } elseif (!empty($filename)) { - // Adding an empty file will cause cURL to error out - $data = new PostFile($field, $filename, $contentType); - } - - if ($data) { - if (!isset($this->postFiles[$data->getFieldName()])) { - $this->postFiles[$data->getFieldName()] = array($data); - } else { - $this->postFiles[$data->getFieldName()][] = $data; - } - $this->processPostFields(); - } - - return $this; - } - - public function addPostFiles(array $files) - { - foreach ($files as $key => $file) { - if ($file instanceof PostFileInterface) { - $this->addPostFile($file, null, null, false); - } elseif (is_string($file)) { - // Convert non-associative array keys into 'file' - if (is_numeric($key)) { - $key = 'file'; - } - $this->addPostFile($key, $file, null, false); - } else { - throw new RequestException('File must be a string or instance of PostFileInterface'); - } - } - - return $this; - } - - /** - * Determine what type of request should be sent based on post fields - */ - protected function processPostFields() - { - if (!$this->postFiles) { - $this->removeHeader('Expect')->setHeader('Content-Type', self::URL_ENCODED); - } else { - $this->setHeader('Content-Type', self::MULTIPART); - if ($this->expectCutoff !== false) { - $this->setHeader('Expect', '100-Continue'); - } - } - } -} diff --git a/core/vendor/guzzle/http/Guzzle/Http/Message/EntityEnclosingRequestInterface.php b/core/vendor/guzzle/http/Guzzle/Http/Message/EntityEnclosingRequestInterface.php deleted file mode 100644 index d9c037de683b03ea0654735074a1a4b01b878947..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/http/Guzzle/Http/Message/EntityEnclosingRequestInterface.php +++ /dev/null @@ -1,136 +0,0 @@ -<?php - -namespace Guzzle\Http\Message; - -use Guzzle\Http\Exception\RequestException; -use Guzzle\Http\EntityBodyInterface; -use Guzzle\Http\QueryString; - -/** - * HTTP request that sends an entity-body in the request message (POST, PUT) - */ -interface EntityEnclosingRequestInterface extends RequestInterface -{ - const URL_ENCODED = 'application/x-www-form-urlencoded; charset=utf-8'; - const MULTIPART = 'multipart/form-data'; - - /** - * Set the body of the request - * - * @param string|resource|EntityBodyInterface $body Body to use in the entity body of the request - * @param string $contentType Content-Type to set. Leave null to use an existing - * Content-Type or to guess the Content-Type - * @return self - * @throws RequestException if the protocol is < 1.1 and Content-Length can not be determined - */ - public function setBody($body, $contentType = null); - - /** - * Get the body of the request if set - * - * @return EntityBodyInterface|null - */ - public function getBody(); - - /** - * Get a POST field from the request - * - * @param string $field Field to retrieve - * - * @return mixed|null - */ - public function getPostField($field); - - /** - * Get the post fields that will be used in the request - * - * @return QueryString - */ - public function getPostFields(); - - /** - * Set a POST field value - * - * @param string $key Key to set - * @param string $value Value to set - * - * @return self - */ - public function setPostField($key, $value); - - /** - * Add POST fields to use in the request - * - * @param QueryString|array $fields POST fields - * - * @return self - */ - public function addPostFields($fields); - - /** - * Remove a POST field or file by name - * - * @param string $field Name of the POST field or file to remove - * - * @return self - */ - public function removePostField($field); - - /** - * Returns an associative array of POST field names to PostFileInterface objects - * - * @return array - */ - public function getPostFiles(); - - /** - * Get a POST file from the request - * - * @param string $fieldName POST fields to retrieve - * - * @return array|null Returns an array wrapping an array of PostFileInterface objects - */ - public function getPostFile($fieldName); - - /** - * Remove a POST file from the request - * - * @param string $fieldName POST file field name to remove - * - * @return self - */ - public function removePostFile($fieldName); - - /** - * Add a POST file to the upload - * - * @param string $field POST field to use (e.g. file). Used to reference content from the server. - * @param string $filename Full path to the file. Do not include the @ symbol. - * @param string $contentType Optional Content-Type to add to the Content-Disposition. - * Default behavior is to guess. Set to false to not specify. - * @return self - */ - public function addPostFile($field, $filename = null, $contentType = null); - - /** - * Add POST files to use in the upload - * - * @param array $files An array of POST fields => filenames where filename can be a string or PostFileInterface - * - * @return self - */ - public function addPostFiles(array $files); - - /** - * Configure how redirects are handled for the request - * - * @param bool $strict Set to true to follow strict RFC compliance when redirecting POST requests. Most - * browsers with follow a 301-302 redirect for a POST request with a GET request. This is - * the default behavior of Guzzle. Enable strict redirects to redirect these responses - * with a POST rather than a GET request. - * @param int $maxRedirects Specify the maximum number of allowed redirects. Set to 0 to disable redirects. - * - * @return self - */ - public function configureRedirects($strict = false, $maxRedirects = 5); -} diff --git a/core/vendor/guzzle/http/Guzzle/Http/Message/Header.php b/core/vendor/guzzle/http/Guzzle/Http/Message/Header.php deleted file mode 100644 index b919166a71f613fd9c47f8420a45fe2451e864b9..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/http/Guzzle/Http/Message/Header.php +++ /dev/null @@ -1,177 +0,0 @@ -<?php - -namespace Guzzle\Http\Message; - -use Guzzle\Common\Version; -use Guzzle\Http\Message\Header\HeaderInterface; - -/** - * Represents a header and all of the values stored by that header - */ -class Header implements HeaderInterface -{ - protected $values = array(); - protected $header; - protected $glue; - - /** - * @param string $header Name of the header - * @param array|string $values Values of the header as an array or a scalar - * @param string $glue Glue used to combine multiple values into a string - */ - public function __construct($header, $values = array(), $glue = ',') - { - $this->header = trim($header); - $this->glue = $glue; - - foreach ((array) $values as $value) { - foreach ((array) $value as $v) { - $this->values[] = $v; - } - } - } - - public function __toString() - { - return implode($this->glue . ' ', $this->toArray()); - } - - public function add($value) - { - $this->values[] = $value; - - return $this; - } - - public function getName() - { - return $this->header; - } - - public function setName($name) - { - $this->header = $name; - - return $this; - } - - public function setGlue($glue) - { - $this->glue = $glue; - - return $this; - } - - public function getGlue() - { - return $this->glue; - } - - /** - * Normalize the header to be a single header with an array of values. - * - * If any values of the header contains the glue string value (e.g. ","), then the value will be exploded into - * multiple entries in the header. - * - * @return self - */ - public function normalize() - { - $values = $this->toArray(); - - for ($i = 0, $total = count($values); $i < $total; $i++) { - if (strpos($values[$i], $this->glue) !== false) { - foreach (explode($this->glue, $values[$i]) as $v) { - $values[] = trim($v); - } - unset($values[$i]); - } - } - - $this->values = array_values($values); - - return $this; - } - - public function hasValue($searchValue) - { - return in_array($searchValue, $this->toArray()); - } - - public function removeValue($searchValue) - { - $this->values = array_values(array_filter($this->values, function ($value) use ($searchValue) { - return $value != $searchValue; - })); - - return $this; - } - - public function toArray() - { - return $this->values; - } - - public function count() - { - return count($this->toArray()); - } - - public function getIterator() - { - return new \ArrayIterator($this->toArray()); - } - - public function parseParams() - { - $params = $matches = array(); - $callback = array($this, 'trimHeader'); - - // Normalize the header into a single array and iterate over all values - foreach ($this->normalize()->toArray() as $val) { - $part = array(); - foreach (preg_split('/;(?=([^"]*"[^"]*")*[^"]*$)/', $val) as $kvp) { - preg_match_all('/<[^>]+>|[^=]+/', $kvp, $matches); - $pieces = array_map($callback, $matches[0]); - $part[$pieces[0]] = isset($pieces[1]) ? $pieces[1] : ''; - } - $params[] = $part; - } - - return $params; - } - - /** - * @deprecated - * @codeCoverageIgnore - */ - public function hasExactHeader($header) - { - Version::warn(__METHOD__ . ' is deprecated'); - return $this->header == $header; - } - - /** - * @deprecated - * @codeCoverageIgnore - */ - public function raw() - { - Version::warn(__METHOD__ . ' is deprecated. Use toArray()'); - return $this->toArray(); - } - - /** - * Trim a header by removing excess spaces and wrapping quotes - * - * @param $str - * - * @return string - */ - protected function trimHeader($str) - { - static $trimmed = "\"' \n\t"; - - return trim($str, $trimmed); - } -} diff --git a/core/vendor/guzzle/http/Guzzle/Http/Message/Header/CacheControl.php b/core/vendor/guzzle/http/Guzzle/Http/Message/Header/CacheControl.php deleted file mode 100644 index 77789e51fd42ad9e97b078c8724a7c755d9ac635..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/http/Guzzle/Http/Message/Header/CacheControl.php +++ /dev/null @@ -1,121 +0,0 @@ -<?php - -namespace Guzzle\Http\Message\Header; - -use Guzzle\Http\Message\Header; - -/** - * Provides helpful functionality for Cache-Control headers - */ -class CacheControl extends Header -{ - /** @var array */ - protected $directives; - - public function add($value) - { - parent::add($value); - $this->directives = null; - } - - public function removeValue($searchValue) - { - parent::removeValue($searchValue); - $this->directives = null; - } - - /** - * Check if a specific cache control directive exists - * - * @param string $param Directive to retrieve - * - * @return bool - */ - public function hasDirective($param) - { - $directives = $this->getDirectives(); - - return isset($directives[$param]); - } - - /** - * Get a specific cache control directive - * - * @param string $param Directive to retrieve - * - * @return string|bool|null - */ - public function getDirective($param) - { - $directives = $this->getDirectives(); - - return isset($directives[$param]) ? $directives[$param] : null; - } - - /** - * Add a cache control directive - * - * @param string $param Directive to add - * @param string $value Value to set - * - * @return self - */ - public function addDirective($param, $value) - { - $directives = $this->getDirectives(); - $directives[$param] = $value; - $this->updateFromDirectives($directives); - - return $this; - } - - /** - * Remove a cache control directive by name - * - * @param string $param Directive to remove - * - * @return self - */ - public function removeDirective($param) - { - $directives = $this->getDirectives(); - unset($directives[$param]); - $this->updateFromDirectives($directives); - - return $this; - } - - /** - * Get an associative array of cache control directives - * - * @return array - */ - public function getDirectives() - { - if ($this->directives === null) { - $this->directives = array(); - foreach ($this->parseParams() as $collection) { - foreach ($collection as $key => $value) { - $this->directives[$key] = $value === '' ? true : $value; - } - } - } - - return $this->directives; - } - - /** - * Updates the header value based on the parsed directives - * - * @param array $directives Array of cache control directives - */ - protected function updateFromDirectives(array $directives) - { - $this->directives = $directives; - $this->values = array(); - - foreach ($directives as $key => $value) { - $this->values[] = $value === true ? $key : "{$key}={$value}"; - } - } -} diff --git a/core/vendor/guzzle/http/Guzzle/Http/Message/Header/HeaderCollection.php b/core/vendor/guzzle/http/Guzzle/Http/Message/Header/HeaderCollection.php deleted file mode 100644 index ec282d9a91187501f550e0b33f888dcc784e7b9e..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/http/Guzzle/Http/Message/Header/HeaderCollection.php +++ /dev/null @@ -1,109 +0,0 @@ -<?php - -namespace Guzzle\Http\Message\Header; - -use Guzzle\Common\Collection; -use Guzzle\Common\ToArrayInterface; - -/** - * Provides a case-insensitive collection of headers - */ -class HeaderCollection implements \IteratorAggregate, \Countable, \ArrayAccess, ToArrayInterface -{ - /** @var array */ - protected $headers; - - public function __construct($headers = array()) - { - $this->headers = $headers; - } - - public function __clone() - { - foreach ($this->headers as &$header) { - $header = clone $header; - } - } - - /** - * Clears the header collection - */ - public function clear() - { - $this->headers = array(); - } - - /** - * Set a header on the collection - * - * @param HeaderInterface $header Header to add - * - * @return self - */ - public function add(HeaderInterface $header) - { - $this->headers[strtolower($header->getName())] = $header; - - return $this; - } - - /** - * Get an array of header objects - * - * @return array - */ - public function getAll() - { - return $this->headers; - } - - /** - * Alias of offsetGet - */ - public function get($key) - { - return $this->offsetGet($key); - } - - public function count() - { - return count($this->headers); - } - - public function offsetExists($offset) - { - return isset($this->headers[strtolower($offset)]); - } - - public function offsetGet($offset) - { - $l = strtolower($offset); - - return isset($this->headers[$l]) ? $this->headers[$l] : null; - } - - public function offsetSet($offset, $value) - { - $this->add($value); - } - - public function offsetUnset($offset) - { - unset($this->headers[strtolower($offset)]); - } - - public function getIterator() - { - return new \ArrayIterator($this->headers); - } - - public function toArray() - { - $result = array(); - foreach ($this->headers as $header) { - $result[$header->getName()] = $header->toArray(); - } - - return $result; - } -} diff --git a/core/vendor/guzzle/http/Guzzle/Http/Message/Header/HeaderFactory.php b/core/vendor/guzzle/http/Guzzle/Http/Message/Header/HeaderFactory.php deleted file mode 100644 index 0273be52f81086a819f32f57ccdeae0313d9f92d..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/http/Guzzle/Http/Message/Header/HeaderFactory.php +++ /dev/null @@ -1,26 +0,0 @@ -<?php - -namespace Guzzle\Http\Message\Header; - -use Guzzle\Http\Message\Header; - -/** - * Default header factory implementation - */ -class HeaderFactory implements HeaderFactoryInterface -{ - /** @var array */ - protected $mapping = array( - 'cache-control' => 'Guzzle\Http\Message\Header\CacheControl', - 'link' => 'Guzzle\Http\Message\Header\Link', - ); - - public function createHeader($header, $value = null) - { - $lowercase = strtolower($header); - - return isset($this->mapping[$lowercase]) - ? new $this->mapping[$lowercase]($header, $value) - : new Header($header, $value); - } -} diff --git a/core/vendor/guzzle/http/Guzzle/Http/Message/Header/HeaderFactoryInterface.php b/core/vendor/guzzle/http/Guzzle/Http/Message/Header/HeaderFactoryInterface.php deleted file mode 100644 index 9457cf64a1c7ed1f2d5644435c2d9c49d411d5fa..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/http/Guzzle/Http/Message/Header/HeaderFactoryInterface.php +++ /dev/null @@ -1,19 +0,0 @@ -<?php - -namespace Guzzle\Http\Message\Header; - -/** - * Interface for creating headers - */ -interface HeaderFactoryInterface -{ - /** - * Create a header from a header name and a single value - * - * @param string $header Name of the header to create - * @param string $value Value to set on the header - * - * @return HeaderInterface - */ - public function createHeader($header, $value = null); -} diff --git a/core/vendor/guzzle/http/Guzzle/Http/Message/Header/HeaderInterface.php b/core/vendor/guzzle/http/Guzzle/Http/Message/Header/HeaderInterface.php deleted file mode 100644 index adcc78e09b93cebf4acbfca9fe17ce81baa5f4ac..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/http/Guzzle/Http/Message/Header/HeaderInterface.php +++ /dev/null @@ -1,83 +0,0 @@ -<?php - -namespace Guzzle\Http\Message\Header; - -use Guzzle\Common\ToArrayInterface; - -interface HeaderInterface extends ToArrayInterface, \Countable, \IteratorAggregate -{ - /** - * Convert the header to a string - * - * @return string - */ - public function __toString(); - - /** - * Add a value to the list of header values - * - * @param string $value Value to add to the header - * - * @return self - */ - public function add($value); - - /** - * Get the name of the header - * - * @return string - */ - public function getName(); - - /** - * Change the name of the header - * - * @param string $name Name to change to - * - * @return self - */ - public function setName($name); - - /** - * Change the glue used to implode the values - * - * @param string $glue Glue used to implode multiple values - * - * @return self - */ - public function setGlue($glue); - - /** - * Get the glue used to implode multiple values into a string - * - * @return string - */ - public function getGlue(); - - /** - * Check if the collection of headers has a particular value - * - * @param string $searchValue Value to search for - * - * @return bool - */ - public function hasValue($searchValue); - - /** - * Remove a specific value from the header - * - * @param string $searchValue Value to remove - * - * @return self - */ - public function removeValue($searchValue); - - /** - * Parse a header containing ";" separated data into an array of associative arrays representing the header - * key value pair data of the header. When a parameter does not contain a value, but just contains a key, this - * function will inject a key with a '' string value. - * - * @return array - */ - public function parseParams(); -} diff --git a/core/vendor/guzzle/http/Guzzle/Http/Message/Header/Link.php b/core/vendor/guzzle/http/Guzzle/Http/Message/Header/Link.php deleted file mode 100644 index a9fb961443cf270617fecb7dd554c80a535db32d..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/http/Guzzle/Http/Message/Header/Link.php +++ /dev/null @@ -1,93 +0,0 @@ -<?php - -namespace Guzzle\Http\Message\Header; - -use Guzzle\Http\Message\Header; - -/** - * Provides helpful functionality for link headers - */ -class Link extends Header -{ - /** - * Add a link to the header - * - * @param string $url Link URL - * @param string $rel Link rel - * @param array $params Other link parameters - * - * @return self - */ - public function addLink($url, $rel, array $params = array()) - { - $values = array("<{$url}>", "rel=\"{$rel}\""); - - foreach ($params as $k => $v) { - $values[] = "{$k}=\"{$v}\""; - } - - return $this->add(implode('; ', $values)); - } - - /** - * Check if a specific link exists for a given rel attribute - * - * @param string $rel rel value - * - * @return bool - */ - public function hasLink($rel) - { - return $this->getLink($rel) !== null; - } - - /** - * Get a specific link for a given rel attribute - * - * @param string $rel Rel value - * - * @return array|null - */ - public function getLink($rel) - { - foreach ($this->getLinks() as $link) { - if (isset($link['rel']) && $link['rel'] == $rel) { - return $link; - } - } - - return null; - } - - /** - * Get an associative array of links - * - * For example: - * Link: <http:/.../front.jpeg>; rel=front; type="image/jpeg", <http://.../back.jpeg>; rel=back; type="image/jpeg" - * - * <code> - * var_export($response->getLinks()); - * array( - * array( - * 'url' => 'http:/.../front.jpeg', - * 'rel' => 'back', - * 'type' => 'image/jpeg', - * ) - * ) - * </code> - * - * @return array - */ - public function getLinks() - { - $links = $this->parseParams(); - - foreach ($links as &$link) { - $key = key($link); - unset($link[$key]); - $link['url'] = trim($key, '<> '); - } - - return $links; - } -} diff --git a/core/vendor/guzzle/http/Guzzle/Http/Message/MessageInterface.php b/core/vendor/guzzle/http/Guzzle/Http/Message/MessageInterface.php deleted file mode 100644 index 62bcd4391339802327691ca1d2cffd138ebb2e2e..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/http/Guzzle/Http/Message/MessageInterface.php +++ /dev/null @@ -1,102 +0,0 @@ -<?php - -namespace Guzzle\Http\Message; - -/** - * Request and response message interface - */ -interface MessageInterface -{ - /** - * Get application and plugin specific parameters set on the message. - * - * @return \Guzzle\Common\Collection - */ - public function getParams(); - - /** - * Add a header to an existing collection of headers. - * - * @param string $header Header name to add - * @param string $value Value of the header - * - * @return self - */ - public function addHeader($header, $value); - - /** - * Add and merge in an array of HTTP headers. - * - * @param array $headers Associative array of header data. - * - * @return self - */ - public function addHeaders(array $headers); - - /** - * Retrieve an HTTP header by name. Performs a case-insensitive search of all headers. - * - * @param string $header Header to retrieve. - * - * @return Header|null - */ - public function getHeader($header); - - /** - * Get all headers as a collection - * - * @return \Guzzle\Http\Message\Header\HeaderCollection - */ - public function getHeaders(); - - /** - * Check if the specified header is present. - * - * @param string $header The header to check. - * - * @return bool - */ - public function hasHeader($header); - - /** - * Remove a specific HTTP header. - * - * @param string $header HTTP header to remove. - * - * @return self - */ - public function removeHeader($header); - - /** - * Set an HTTP header and overwrite any existing value for the header - * - * @param string $header Name of the header to set. - * @param mixed $value Value to set. - * - * @return self - */ - public function setHeader($header, $value); - - /** - * Overwrite all HTTP headers with the supplied array of headers - * - * @param array $headers Associative array of header data. - * - * @return self - */ - public function setHeaders(array $headers); - - /** - * Get an array of message header lines (e.g. ["Host: example.com", ...]) - * - * @return array - */ - public function getHeaderLines(); - - /** - * Get the raw message headers as a string - * - * @return string - */ - public function getRawHeaders(); -} diff --git a/core/vendor/guzzle/http/Guzzle/Http/Message/PostFile.php b/core/vendor/guzzle/http/Guzzle/Http/Message/PostFile.php deleted file mode 100644 index 0beb7a7ce6b85041d0066de7020ebe4b00c92af7..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/http/Guzzle/Http/Message/PostFile.php +++ /dev/null @@ -1,109 +0,0 @@ -<?php - -namespace Guzzle\Http\Message; - -use Guzzle\Common\Version; -use Guzzle\Common\Exception\InvalidArgumentException; -use Guzzle\Http\Mimetypes; - -/** - * POST file upload - */ -class PostFile implements PostFileInterface -{ - protected $fieldName; - protected $contentType; - protected $filename; - - /** - * @param string $fieldName Name of the field - * @param string $filename Path to the file - * @param string $contentType Content-Type of the upload - */ - public function __construct($fieldName, $filename, $contentType = null) - { - $this->fieldName = $fieldName; - $this->setFilename($filename); - $this->contentType = $contentType ?: $this->guessContentType(); - } - - public function setFieldName($name) - { - $this->fieldName = $name; - - return $this; - } - - public function getFieldName() - { - return $this->fieldName; - } - - public function setFilename($filename) - { - // Remove leading @ symbol - if (strpos($filename, '@') === 0) { - $filename = substr($filename, 1); - } - - if (!is_readable($filename)) { - throw new InvalidArgumentException("Unable to open {$filename} for reading"); - } - - $this->filename = $filename; - - return $this; - } - - public function getFilename() - { - return $this->filename; - } - - public function setContentType($type) - { - $this->contentType = $type; - - return $this; - } - - public function getContentType() - { - return $this->contentType; - } - - public function getCurlValue() - { - // PHP 5.5 introduced a CurlFile object that deprecates the old @filename syntax - // See: https://wiki.php.net/rfc/curl-file-upload - if (function_exists('curl_file_create')) { - return curl_file_create($this->filename, $this->contentType, basename($this->filename)); - } - - // Use the old style if using an older version of PHP - $value = "@{$this->filename};filename=" . basename($this->filename); - if ($this->contentType) { - $value .= ';type=' . $this->contentType; - } - - return $value; - } - - /** - * @deprecated - * @codeCoverageIgnore - */ - public function getCurlString() - { - Version::warn(__METHOD__ . ' is deprecated. Use getCurlValue()'); - return $this->getCurlValue(); - } - - /** - * Determine the Content-Type of the file - */ - protected function guessContentType() - { - return Mimetypes::getInstance()->fromFilename($this->filename) ?: 'application/octet-stream'; - } -} diff --git a/core/vendor/guzzle/http/Guzzle/Http/Message/PostFileInterface.php b/core/vendor/guzzle/http/Guzzle/Http/Message/PostFileInterface.php deleted file mode 100644 index 99dc706946f5b50e1a94d183873b9a2585f91f6a..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/http/Guzzle/Http/Message/PostFileInterface.php +++ /dev/null @@ -1,67 +0,0 @@ -<?php - -namespace Guzzle\Http\Message; - -use Guzzle\Common\Exception\InvalidArgumentException; - -/** - * POST file upload - */ -interface PostFileInterface -{ - /** - * Set the name of the field - * - * @param string $name Field name - * - * @return self - */ - public function setFieldName($name); - - /** - * Get the name of the field - * - * @return string - */ - public function getFieldName(); - - /** - * Set the path to the file - * - * @param string $path Full path to the file - * - * @return self - * @throws InvalidArgumentException if the file cannot be read - */ - public function setFilename($path); - - /** - * Get the full path to the file - * - * @return string - */ - public function getFilename(); - - /** - * Set the Content-Type of the file - * - * @param string $type Content type - * - * @return self - */ - public function setContentType($type); - - /** - * Get the Content-Type of the file - * - * @return string - */ - public function getContentType(); - - /** - * Get a cURL ready string or CurlFile object for the upload - * - * @return string - */ - public function getCurlValue(); -} diff --git a/core/vendor/guzzle/http/Guzzle/Http/Message/Request.php b/core/vendor/guzzle/http/Guzzle/Http/Message/Request.php deleted file mode 100644 index 37d769049626ecf6c2a1764e3575bbd310cfd063..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/http/Guzzle/Http/Message/Request.php +++ /dev/null @@ -1,637 +0,0 @@ -<?php - -namespace Guzzle\Http\Message; - -use Guzzle\Common\Version; -use Guzzle\Common\Event; -use Guzzle\Common\Collection; -use Guzzle\Common\Exception\RuntimeException; -use Guzzle\Common\Exception\InvalidArgumentException; -use Guzzle\Http\Exception\RequestException; -use Guzzle\Http\Exception\BadResponseException; -use Guzzle\Http\ClientInterface; -use Guzzle\Http\EntityBody; -use Guzzle\Http\EntityBodyInterface; -use Guzzle\Http\Message\Header\HeaderInterface; -use Guzzle\Http\Url; -use Guzzle\Parser\ParserRegistry; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; -use Symfony\Component\EventDispatcher\EventDispatcher; -use Symfony\Component\EventDispatcher\EventSubscriberInterface; - -/** - * HTTP request class to send requests - */ -class Request extends AbstractMessage implements RequestInterface -{ - /** @var EventDispatcherInterface */ - protected $eventDispatcher; - - /** @var Url HTTP Url */ - protected $url; - - /** @var string HTTP method (GET, PUT, POST, DELETE, HEAD, OPTIONS, TRACE) */ - protected $method; - - /** @var ClientInterface */ - protected $client; - - /** @var Response Response of the request */ - protected $response; - - /** @var EntityBodyInterface Response body */ - protected $responseBody; - - /** @var string State of the request object */ - protected $state; - - /** @var string Authentication username */ - protected $username; - - /** @var string Auth password */ - protected $password; - - /** @var Collection cURL specific transfer options */ - protected $curlOptions; - - /** @var bool */ - protected $isRedirect = false; - - public static function getAllEvents() - { - return array( - // Called when receiving or uploading data through cURL - 'curl.callback.read', 'curl.callback.write', 'curl.callback.progress', - // Cloning a request - 'request.clone', - // About to send the request, sent request, completed transaction - 'request.before_send', 'request.sent', 'request.complete', - // A request received a successful response - 'request.success', - // A request received an unsuccessful response - 'request.error', - // An exception is being thrown because of an unsuccessful response - 'request.exception', - // Received response status line - 'request.receive.status_line' - ); - } - - /** - * @param string $method HTTP method - * @param string|Url $url HTTP URL to connect to. The URI scheme, host header, and URI are parsed from the - * full URL. If query string parameters are present they will be parsed as well. - * @param array|Collection $headers HTTP headers - */ - public function __construct($method, $url, $headers = array()) - { - parent::__construct(); - $this->method = strtoupper($method); - $this->curlOptions = new Collection(); - $this->setUrl($url); - - if ($headers) { - // Special handling for multi-value headers - foreach ($headers as $key => $value) { - // Deal with collisions with Host and Authorization - if ($key == 'host' || $key == 'Host') { - $this->setHeader($key, $value); - } elseif ($value instanceof HeaderInterface) { - $this->addHeader($key, $value); - } else { - foreach ((array) $value as $v) { - $this->addHeader($key, $v); - } - } - } - } - - $this->setState(self::STATE_NEW); - } - - public function __clone() - { - if ($this->eventDispatcher) { - $this->eventDispatcher = clone $this->eventDispatcher; - } - $this->curlOptions = clone $this->curlOptions; - $this->params = clone $this->params; - $this->url = clone $this->url; - $this->response = $this->responseBody = null; - $this->headers = clone $this->headers; - - $this->setState(RequestInterface::STATE_NEW); - $this->dispatch('request.clone', array('request' => $this)); - } - - /** - * Get the HTTP request as a string - * - * @return string - */ - public function __toString() - { - return $this->getRawHeaders() . "\r\n\r\n"; - } - - /** - * Default method that will throw exceptions if an unsuccessful response is received. - * - * @param Event $event Received - * @throws BadResponseException if the response is not successful - */ - public static function onRequestError(Event $event) - { - $e = BadResponseException::factory($event['request'], $event['response']); - $event['request']->setState(self::STATE_ERROR, array('exception' => $e) + $event->toArray()); - throw $e; - } - - public function setClient(ClientInterface $client) - { - $this->client = $client; - - return $this; - } - - public function getClient() - { - return $this->client; - } - - public function getRawHeaders() - { - $protocolVersion = $this->protocolVersion ?: '1.1'; - - return trim($this->method . ' ' . $this->getResource()) . ' ' - . strtoupper(str_replace('https', 'http', $this->url->getScheme())) - . '/' . $protocolVersion . "\r\n" . implode("\r\n", $this->getHeaderLines()); - } - - public function setUrl($url) - { - if ($url instanceof Url) { - $this->url = $url; - } else { - $this->url = Url::factory($url); - } - - // Update the port and host header - $this->setPort($this->url->getPort()); - - if ($this->url->getUsername() || $this->url->getPassword()) { - $this->setAuth($this->url->getUsername(), $this->url->getPassword()); - // Remove the auth info from the URL - $this->url->setUsername(null); - $this->url->setPassword(null); - } - - return $this; - } - - public function send() - { - if (!$this->client) { - throw new RuntimeException('A client must be set on the request'); - } - - return $this->client->send($this); - } - - public function getResponse() - { - return $this->response; - } - - public function getQuery($asString = false) - { - return $asString - ? (string) $this->url->getQuery() - : $this->url->getQuery(); - } - - public function getMethod() - { - return $this->method; - } - - public function getScheme() - { - return $this->url->getScheme(); - } - - public function setScheme($scheme) - { - $this->url->setScheme($scheme); - - return $this; - } - - public function getHost() - { - return $this->url->getHost(); - } - - public function setHost($host) - { - $this->url->setHost($host); - $this->setPort($this->url->getPort()); - - return $this; - } - - public function getProtocolVersion() - { - return $this->protocolVersion; - } - - public function setProtocolVersion($protocol) - { - $this->protocolVersion = $protocol; - - return $this; - } - - public function getPath() - { - return '/' . ltrim($this->url->getPath(), '/'); - } - - public function setPath($path) - { - $this->url->setPath($path); - - return $this; - } - - public function getPort() - { - return $this->url->getPort(); - } - - public function setPort($port) - { - $this->url->setPort($port); - - // Include the port in the Host header if it is not the default port for the scheme of the URL - $scheme = $this->url->getScheme(); - if (($scheme == 'http' && $port != 80) || ($scheme == 'https' && $port != 443)) { - $this->headers['host'] = $this->headerFactory->createHeader('Host', $this->url->getHost() . ':' . $port); - } else { - $this->headers['host'] = $this->headerFactory->createHeader('Host', $this->url->getHost()); - } - - return $this; - } - - public function getUsername() - { - return $this->username; - } - - public function getPassword() - { - return $this->password; - } - - public function setAuth($user, $password = '', $scheme = CURLAUTH_BASIC) - { - static $authMap = array( - 'basic' => CURLAUTH_BASIC, - 'digest' => CURLAUTH_DIGEST, - 'ntlm' => CURLAUTH_NTLM, - 'any' => CURLAUTH_ANY - ); - - // If we got false or null, disable authentication - if (!$user) { - $this->password = $this->username = null; - $this->removeHeader('Authorization'); - $this->getCurlOptions()->remove(CURLOPT_HTTPAUTH); - return $this; - } - - if (!is_numeric($scheme)) { - $scheme = strtolower($scheme); - if (!isset($authMap[$scheme])) { - throw new InvalidArgumentException($scheme . ' is not a valid authentication type'); - } - $scheme = $authMap[$scheme]; - } - - $this->username = $user; - $this->password = $password; - - // Bypass CURL when using basic auth to promote connection reuse - if ($scheme == CURLAUTH_BASIC) { - $this->getCurlOptions()->remove(CURLOPT_HTTPAUTH); - $this->setHeader('Authorization', 'Basic ' . base64_encode($this->username . ':' . $this->password)); - } else { - $this->getCurlOptions() - ->set(CURLOPT_HTTPAUTH, $scheme) - ->set(CURLOPT_USERPWD, $this->username . ':' . $this->password); - } - - return $this; - } - - public function getResource() - { - $resource = $this->getPath(); - if ($query = (string) $this->url->getQuery()) { - $resource .= '?' . $query; - } - - return $resource; - } - - public function getUrl($asObject = false) - { - return $asObject ? clone $this->url : (string) $this->url; - } - - public function getState() - { - return $this->state; - } - - public function setState($state, array $context = array()) - { - $oldState = $this->state; - $this->state = $state; - - switch ($state) { - case self::STATE_NEW: - $this->response = null; - break; - case self::STATE_TRANSFER: - if ($oldState !== $state) { - // Fix Content-Length and Transfer-Encoding collisions - if ($this->hasHeader('Transfer-Encoding') && $this->hasHeader('Content-Length')) { - $this->removeHeader('Transfer-Encoding'); - } - $this->dispatch('request.before_send', array('request' => $this)); - } - break; - case self::STATE_COMPLETE: - if ($oldState !== $state) { - $this->processResponse($context); - $this->responseBody = null; - } - break; - case self::STATE_ERROR: - if (isset($context['exception'])) { - $this->dispatch('request.exception', array( - 'request' => $this, - 'response' => isset($context['response']) ? $context['response'] : $this->response, - 'exception' => isset($context['exception']) ? $context['exception'] : null - )); - } - } - - return $this->state; - } - - public function getCurlOptions() - { - return $this->curlOptions; - } - - public function startResponse(Response $response) - { - $this->state = self::STATE_TRANSFER; - $response->setEffectiveUrl((string) $this->getUrl()); - $this->response = $response; - - return $this; - } - - public function setResponse(Response $response, $queued = false) - { - $response->setEffectiveUrl((string) $this->url); - - if ($queued) { - $ed = $this->getEventDispatcher(); - $ed->addListener('request.before_send', $f = function ($e) use ($response, &$f, $ed) { - $e['request']->setResponse($response); - $ed->removeListener('request.before_send', $f); - }, -9999); - } else { - $this->response = $response; - // If a specific response body is specified, then use it instead of the response's body - if ($this->responseBody && !$this->responseBody->getCustomData('default') && !$response->isRedirect()) { - $this->getResponseBody()->write((string) $this->response->getBody()); - } else { - $this->responseBody = $this->response->getBody(); - } - $this->setState(self::STATE_COMPLETE); - } - - return $this; - } - - public function setResponseBody($body) - { - // Attempt to open a file for writing if a string was passed - if (is_string($body)) { - // @codeCoverageIgnoreStart - if (!($body = fopen($body, 'w+'))) { - throw new InvalidArgumentException('Could not open ' . $body . ' for writing'); - } - // @codeCoverageIgnoreEnd - } - - $this->responseBody = EntityBody::factory($body); - - return $this; - } - - public function getResponseBody() - { - if ($this->responseBody === null) { - $this->responseBody = EntityBody::factory()->setCustomData('default', true); - } - - return $this->responseBody; - } - - /** - * Determine if the response body is repeatable (readable + seekable) - * - * @return bool - * @deprecated Use getResponseBody()->isSeekable() - * @codeCoverageIgnore - */ - public function isResponseBodyRepeatable() - { - Version::warn(__METHOD__ . ' is deprecated. Use $request->getResponseBody()->isRepeatable()'); - return !$this->responseBody ? true : $this->responseBody->isRepeatable(); - } - - public function getCookies() - { - if ($cookie = $this->getHeader('Cookie')) { - $data = ParserRegistry::getInstance()->getParser('cookie')->parseCookie($cookie); - return $data['cookies']; - } - - return array(); - } - - public function getCookie($name) - { - $cookies = $this->getCookies(); - - return isset($cookies[$name]) ? $cookies[$name] : null; - } - - public function addCookie($name, $value) - { - if (!$this->hasHeader('Cookie')) { - $this->setHeader('Cookie', "{$name}={$value}"); - } else { - $this->getHeader('Cookie')->add("{$name}={$value}"); - } - - // Always use semicolons to separate multiple cookie headers - $this->getHeader('Cookie')->setGlue(';'); - - return $this; - } - - public function removeCookie($name) - { - if ($cookie = $this->getHeader('Cookie')) { - foreach ($cookie as $cookieValue) { - if (strpos($cookieValue, $name . '=') === 0) { - $cookie->removeValue($cookieValue); - } - } - } - - return $this; - } - - public function setEventDispatcher(EventDispatcherInterface $eventDispatcher) - { - $this->eventDispatcher = $eventDispatcher; - $this->eventDispatcher->addListener('request.error', array(__CLASS__, 'onRequestError'), -255); - - return $this; - } - - public function getEventDispatcher() - { - if (!$this->eventDispatcher) { - $this->setEventDispatcher(new EventDispatcher()); - } - - return $this->eventDispatcher; - } - - public function dispatch($eventName, array $context = array()) - { - $context['request'] = $this; - $this->getEventDispatcher()->dispatch($eventName, new Event($context)); - } - - public function addSubscriber(EventSubscriberInterface $subscriber) - { - $this->getEventDispatcher()->addSubscriber($subscriber); - - return $this; - } - - /** - * Get an array containing the request and response for event notifications - * - * @return array - */ - protected function getEventArray() - { - return array( - 'request' => $this, - 'response' => $this->response - ); - } - - /** - * Process a received response - * - * @param array $context Contextual information - * @throws RequestException|BadResponseException on unsuccessful responses - */ - protected function processResponse(array $context = array()) - { - if (!$this->response) { - // If no response, then processResponse shouldn't have been called - $e = new RequestException('Error completing request'); - $e->setRequest($this); - throw $e; - } - - $this->state = self::STATE_COMPLETE; - - // A request was sent, but we don't know if we'll send more or if the final response will be successful - $this->dispatch('request.sent', $this->getEventArray() + $context); - - // Some response processors will remove the response or reset the state (example: ExponentialBackoffPlugin) - if ($this->state == RequestInterface::STATE_COMPLETE) { - - // The request completed, so the HTTP transaction is complete - $this->dispatch('request.complete', $this->getEventArray()); - - // If the response is bad, allow listeners to modify it or throw exceptions. You can change the response by - // modifying the Event object in your listeners or calling setResponse() on the request - if ($this->response->isError()) { - $event = new Event($this->getEventArray()); - $this->getEventDispatcher()->dispatch('request.error', $event); - // Allow events of request.error to quietly change the response - if ($event['response'] !== $this->response) { - $this->response = $event['response']; - } - } - - // If a successful response was received, dispatch an event - if ($this->response->isSuccessful()) { - $this->dispatch('request.success', $this->getEventArray()); - } - } - } - - /** - * @deprecated Use Guzzle\Plugin\Cache\DefaultCanCacheStrategy - * @codeCoverageIgnore - */ - public function canCache() - { - Version::warn(__METHOD__ . ' is deprecated. Use Guzzle\Plugin\Cache\DefaultCanCacheStrategy.'); - if (class_exists('Guzzle\Plugin\Cache\DefaultCanCacheStrategy')) { - $canCache = new \Guzzle\Plugin\Cache\DefaultCanCacheStrategy(); - return $canCache->canCacheRequest($this); - } else { - return false; - } - } - - /** - * @deprecated Use the history plugin (not emitting a warning as this is built-into the RedirectPlugin for now) - * @codeCoverageIgnore - */ - public function setIsRedirect($isRedirect) - { - $this->isRedirect = $isRedirect; - - return $this; - } - - /** - * @deprecated Use the history plugin - * @codeCoverageIgnore - */ - public function isRedirect() - { - Version::warn(__METHOD__ . ' is deprecated. Use the HistoryPlugin to track this.'); - return $this->isRedirect; - } -} diff --git a/core/vendor/guzzle/http/Guzzle/Http/Message/RequestFactory.php b/core/vendor/guzzle/http/Guzzle/Http/Message/RequestFactory.php deleted file mode 100644 index 5fae2d258731336a27c1cb2dbd872eaa7927101a..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/http/Guzzle/Http/Message/RequestFactory.php +++ /dev/null @@ -1,356 +0,0 @@ -<?php - -namespace Guzzle\Http\Message; - -use Guzzle\Common\Collection; -use Guzzle\Common\Exception\InvalidArgumentException; -use Guzzle\Http\RedirectPlugin; -use Guzzle\Http\Url; -use Guzzle\Parser\ParserRegistry; -use Guzzle\Plugin\Log\LogPlugin; - -/** - * Default HTTP request factory used to create the default {@see Request} and {@see EntityEnclosingRequest} objects. - */ -class RequestFactory implements RequestFactoryInterface -{ - /** @var RequestFactory Singleton instance of the default request factory */ - protected static $instance; - - /** @var array Hash of methods available to the class (provides fast isset() lookups) */ - protected $methods; - - /** @var string Class to instantiate for requests with no body */ - protected $requestClass = 'Guzzle\\Http\\Message\\Request'; - - /** @var string Class to instantiate for requests with a body */ - protected $entityEnclosingRequestClass = 'Guzzle\\Http\\Message\\EntityEnclosingRequest'; - - /** - * Get a cached instance of the default request factory - * - * @return RequestFactory - */ - public static function getInstance() - { - // @codeCoverageIgnoreStart - if (!static::$instance) { - static::$instance = new static(); - } - // @codeCoverageIgnoreEnd - - return static::$instance; - } - - public function __construct() - { - $this->methods = array_flip(get_class_methods(__CLASS__)); - } - - public function fromMessage($message) - { - $parsed = ParserRegistry::getInstance()->getParser('message')->parseRequest($message); - - if (!$parsed) { - return false; - } - - $request = $this->fromParts($parsed['method'], $parsed['request_url'], - $parsed['headers'], $parsed['body'], $parsed['protocol'], - $parsed['version']); - - // EntityEnclosingRequest adds an "Expect: 100-Continue" header when using a raw request body for PUT or POST - // requests. This factory method should accurately reflect the message, so here we are removing the Expect - // header if one was not supplied in the message. - if (!isset($parsed['headers']['Expect']) && !isset($parsed['headers']['expect'])) { - $request->removeHeader('Expect'); - } - - return $request; - } - - public function fromParts( - $method, - array $urlParts, - $headers = null, - $body = null, - $protocol = 'HTTP', - $protocolVersion = '1.1' - ) { - return $this->create($method, Url::buildUrl($urlParts), $headers, $body) - ->setProtocolVersion($protocolVersion); - } - - public function create($method, $url, $headers = null, $body = null, array $options = array()) - { - $method = strtoupper($method); - - if ($method == 'GET' || $method == 'HEAD' || $method == 'TRACE' || $method == 'OPTIONS') { - // Handle non-entity-enclosing request methods - $request = new $this->requestClass($method, $url, $headers); - if ($body) { - // The body is where the response body will be stored - $type = gettype($body); - if ($type == 'string' || $type == 'resource' || $type == 'object') { - $request->setResponseBody($body); - } - } - } else { - // Create an entity enclosing request by default - $request = new $this->entityEnclosingRequestClass($method, $url, $headers); - if ($body) { - // Add POST fields and files to an entity enclosing request if an array is used - if (is_array($body) || $body instanceof Collection) { - // Normalize PHP style cURL uploads with a leading '@' symbol - foreach ($body as $key => $value) { - if (is_string($value) && substr($value, 0, 1) == '@') { - $request->addPostFile($key, $value); - unset($body[$key]); - } - } - // Add the fields if they are still present and not all files - $request->addPostFields($body); - } else { - // Add a raw entity body body to the request - $request->setBody($body, (string) $request->getHeader('Content-Type')); - if ((string) $request->getHeader('Transfer-Encoding') == 'chunked') { - $request->removeHeader('Content-Length'); - } - } - } - } - - if ($options) { - $this->applyOptions($request, $options); - } - - return $request; - } - - /** - * Clone a request while changing the method. Emulates the behavior of - * {@see Guzzle\Http\Message\Request::clone}, but can change the HTTP method. - * - * @param RequestInterface $request Request to clone - * @param string $method Method to set - * - * @return RequestInterface - */ - public function cloneRequestWithMethod(RequestInterface $request, $method) - { - // Create the request with the same client if possible - if ($client = $request->getClient()) { - $cloned = $request->getClient()->createRequest($method, $request->getUrl(), $request->getHeaders()); - } else { - $cloned = $this->create($method, $request->getUrl(), $request->getHeaders()); - } - - $cloned->getCurlOptions()->replace($request->getCurlOptions()->toArray()); - $cloned->setEventDispatcher(clone $request->getEventDispatcher()); - // Ensure that that the Content-Length header is not copied if changing to GET or HEAD - if (!($cloned instanceof EntityEnclosingRequestInterface)) { - $cloned->removeHeader('Content-Length'); - } elseif ($request instanceof EntityEnclosingRequestInterface) { - $cloned->setBody($request->getBody()); - } - $cloned->getParams()->replace($request->getParams()->toArray()); - $cloned->dispatch('request.clone', array('request' => $cloned)); - - return $cloned; - } - - public function applyOptions(RequestInterface $request, array $options = array(), $flags = self::OPTIONS_NONE) - { - // Iterate over each key value pair and attempt to apply a config using function visitors - foreach ($options as $key => $value) { - $method = "visit_{$key}"; - if (isset($this->methods[$method])) { - $this->{$method}($request, $value, $flags); - } - } - } - - protected function visit_headers(RequestInterface $request, $value, $flags) - { - if (!is_array($value)) { - throw new InvalidArgumentException('headers value must be an array'); - } - - if ($flags & self::OPTIONS_AS_DEFAULTS) { - // Merge headers in but do not overwrite existing values - foreach ($value as $key => $header) { - if (!$request->hasHeader($key)) { - $request->setHeader($key, $header); - } - } - } else { - $request->addHeaders($value); - } - } - - protected function visit_body(RequestInterface $request, $value, $flags) - { - if ($request instanceof EntityEnclosingRequestInterface) { - $request->setBody($value); - } else { - throw new InvalidArgumentException('Attempting to set a body on a non-entity-enclosing request'); - } - } - - protected function visit_allow_redirects(RequestInterface $request, $value, $flags) - { - if ($value === false) { - $request->getParams()->set(RedirectPlugin::DISABLE, true); - } - } - - protected function visit_auth(RequestInterface $request, $value, $flags) - { - if (!is_array($value)) { - throw new InvalidArgumentException('auth value must be an array'); - } - - $request->setAuth($value[0], isset($value[1]) ? $value[1] : null, isset($value[2]) ? $value[2] : 'basic'); - } - - protected function visit_query(RequestInterface $request, $value, $flags) - { - if (!is_array($value)) { - throw new InvalidArgumentException('query value must be an array'); - } - - if ($flags & self::OPTIONS_AS_DEFAULTS) { - // Merge query string values in but do not overwrite existing values - $query = $request->getQuery(); - $query->overwriteWith(array_diff_key($value, $query->toArray())); - } else { - $request->getQuery()->overwriteWith($value); - } - } - - protected function visit_cookies(RequestInterface $request, $value, $flags) - { - if (!is_array($value)) { - throw new InvalidArgumentException('cookies value must be an array'); - } - - foreach ($value as $name => $v) { - $request->addCookie($name, $v); - } - } - - protected function visit_events(RequestInterface $request, $value, $flags) - { - if (!is_array($value)) { - throw new InvalidArgumentException('events value must be an array'); - } - - foreach ($value as $name => $method) { - if (is_array($method)) { - $request->getEventDispatcher()->addListener($name, $method[0], $method[1]); - } else { - $request->getEventDispatcher()->addListener($name, $method); - } - } - } - - protected function visit_plugins(RequestInterface $request, $value, $flags) - { - if (!is_array($value)) { - throw new InvalidArgumentException('plugins value must be an array'); - } - - foreach ($value as $plugin) { - $request->addSubscriber($plugin); - } - } - - protected function visit_exceptions(RequestInterface $request, $value, $flags) - { - if ($value === false || $value === 0) { - $dispatcher = $request->getEventDispatcher(); - foreach ($dispatcher->getListeners('request.error') as $listener) { - if ($listener[0] == 'Guzzle\Http\Message\Request' && $listener[1] = 'onRequestError') { - $dispatcher->removeListener('request.error', $listener); - break; - } - } - } - } - - protected function visit_save_to(RequestInterface $request, $value, $flags) - { - $request->setResponseBody($value); - } - - protected function visit_params(RequestInterface $request, $value, $flags) - { - if (!is_array($value)) { - throw new InvalidArgumentException('params value must be an array'); - } - - $request->getParams()->overwriteWith($value); - } - - protected function visit_timeout(RequestInterface $request, $value, $flags) - { - $request->getCurlOptions()->set(CURLOPT_TIMEOUT_MS, $value * 1000); - } - - protected function visit_connect_timeout(RequestInterface $request, $value, $flags) - { - $request->getCurlOptions()->set(CURLOPT_CONNECTTIMEOUT_MS, $value * 1000); - } - - protected function visit_debug(RequestInterface $request, $value, $flags) - { - if (class_exists('Guzzle\Plugin\Log\LogPlugin')) { - $request->addSubscriber(LogPlugin::getDebugPlugin()); - } else { - // @codeCoverageIgnoreStart - $request->getCurlOptions()->set(CURLOPT_VERBOSE, true); - // @codeCoverageIgnoreEnd - } - } - - protected function visit_verify(RequestInterface $request, $value, $flags) - { - $curl = $request->getCurlOptions(); - if ($value === true || is_string($value)) { - $curl[CURLOPT_SSL_VERIFYHOST] = 2; - $curl[CURLOPT_SSL_VERIFYPEER] = true; - if ($value !== true) { - $curl[CURLOPT_CAINFO] = $value; - } - } elseif ($value === false) { - unset($curl[CURLOPT_CAINFO]); - $curl[CURLOPT_SSL_VERIFYHOST] = 0; - $curl[CURLOPT_SSL_VERIFYPEER] = false; - } - } - - protected function visit_proxy(RequestInterface $request, $value, $flags) - { - $request->getCurlOptions()->set(CURLOPT_PROXY, $value, $flags); - } - - protected function visit_cert(RequestInterface $request, $value, $flags) - { - if (is_array($value)) { - $request->getCurlOptions()->set(CURLOPT_SSLCERT, $value[0]); - $request->getCurlOptions()->set(CURLOPT_SSLCERTPASSWD, $value[1]); - } else { - $request->getCurlOptions()->set(CURLOPT_SSLCERT, $value); - } - } - - protected function visit_ssl_key(RequestInterface $request, $value, $flags) - { - if (is_array($value)) { - $request->getCurlOptions()->set(CURLOPT_SSLKEY, $value[0]); - $request->getCurlOptions()->set(CURLOPT_SSLKEYPASSWD, $value[1]); - } else { - $request->getCurlOptions()->set(CURLOPT_SSLKEY, $value); - } - } -} diff --git a/core/vendor/guzzle/http/Guzzle/Http/Message/RequestFactoryInterface.php b/core/vendor/guzzle/http/Guzzle/Http/Message/RequestFactoryInterface.php deleted file mode 100644 index 6088f10e994cf12738531397836926bc0cdd045c..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/http/Guzzle/Http/Message/RequestFactoryInterface.php +++ /dev/null @@ -1,105 +0,0 @@ -<?php - -namespace Guzzle\Http\Message; - -use Guzzle\Common\Collection; -use Guzzle\Http\EntityBodyInterface; -use Guzzle\Http\Url; - -/** - * Request factory used to create HTTP requests - */ -interface RequestFactoryInterface -{ - const OPTIONS_NONE = 0; - const OPTIONS_AS_DEFAULTS = 1; - - /** - * Create a new request based on an HTTP message - * - * @param string $message HTTP message as a string - * - * @return RequestInterface - */ - public function fromMessage($message); - - /** - * Create a request from URL parts as returned from parse_url() - * - * @param string $method HTTP method (GET, POST, PUT, HEAD, DELETE, etc) - * - * @param array $urlParts URL parts containing the same keys as parse_url() - * - scheme: e.g. http - * - host: e.g. www.guzzle-project.com - * - port: e.g. 80 - * - user: e.g. michael - * - pass: e.g. rocks - * - path: e.g. / OR /index.html - * - query: after the question mark ? - * @param array|Collection $headers HTTP headers - * @param string|resource|array|EntityBodyInterface $body Body to send in the request - * @param string $protocol Protocol (HTTP, SPYDY, etc) - * @param string $protocolVersion 1.0, 1.1, etc - * - * @return RequestInterface - */ - public function fromParts( - $method, - array $urlParts, - $headers = null, - $body = null, - $protocol = 'HTTP', - $protocolVersion = '1.1' - ); - - /** - * Create a new request based on the HTTP method - * - * @param string $method HTTP method (GET, POST, PUT, PATCH, HEAD, DELETE, ...) - * @param string|Url $url HTTP URL to connect to - * @param array|Collection $headers HTTP headers - * @param string|resource|array|EntityBodyInterface $body Body to send in the request - * @param array $options Array of options to apply to the request - * - * @return RequestInterface - */ - public function create($method, $url, $headers = null, $body = null, array $options = array()); - - /** - * Apply an associative array of options to the request - * - * @param RequestInterface $request Request to update - * @param array $options Options to use with the request. Available options are: - * "headers": Associative array of headers - * "query": Associative array of query string values to add to the request - * "body": Body of a request, including an EntityBody, string, or array when sending POST requests. - * "auth": Array of HTTP authentication parameters to use with the request. The array must contain the - * username in index [0], the password in index [2], and can optionally contain the authentication type - * in index [3]. The authentication types are: "Basic", "Digest", "NTLM", "Any" (defaults to "Basic"). - * "cookies": Associative array of cookies - * "allow_redirects": Set to false to disable redirects - * "save_to": String, fopen resource, or EntityBody object used to store the body of the response - * "events": Associative array mapping event names to a closure or array of (priority, closure) - * "plugins": Array of plugins to add to the request - * "exceptions": Set to false to disable throwing exceptions on an HTTP level error (e.g. 404, 500, etc) - * "params": Set custom request data parameters on a request. (Note: these are not query string parameters) - * "timeout": Float describing the timeout of the request in seconds - * "connect_timeout": Float describing the number of seconds to wait while trying to connect. Use 0 to wait - * indefinitely. - * "verify": Set to true to enable SSL cert validation (the default), false to disable, or supply the path - * to a CA bundle to enable verification using a custom certificate. - * "cert": Set to a string to specify the path to a file containing a PEM formatted certificate. If a - * password is required, then set an array containing the path to the PEM file followed by the the - * password required for the certificate. - * "ssl_key": Specify the path to a file containing a private SSL key in PEM format. If a password is - * required, then set an array containing the path to the SSL key followed by the password required for - * the certificate. - * "proxy": Specify an HTTP proxy (e.g. "http://username:password@192.168.16.1:10") - * "debug": Set to true to display all data sent over the wire - * @param int $flags Bitwise flags to apply when applying the options to the request. Defaults to no special - * options. `1` (OPTIONS_AS_DEFAULTS): When specified, options will only update a request when - * the value does not already exist on the request. This is only supported by "query" and - * "headers". Other bitwise options may be added in the future. - */ - public function applyOptions(RequestInterface $request, array $options = array(), $flags = self::OPTIONS_NONE); -} diff --git a/core/vendor/guzzle/http/Guzzle/Http/Message/RequestInterface.php b/core/vendor/guzzle/http/Guzzle/Http/Message/RequestInterface.php deleted file mode 100644 index 2f6b3c8d722c6b6dd6e5914cd6aa46f1c3ef9080..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/http/Guzzle/Http/Message/RequestInterface.php +++ /dev/null @@ -1,318 +0,0 @@ -<?php - -namespace Guzzle\Http\Message; - -use Guzzle\Common\Collection; -use Guzzle\Common\HasDispatcherInterface; -use Guzzle\Http\Exception\RequestException; -use Guzzle\Http\ClientInterface; -use Guzzle\Http\EntityBodyInterface; -use Guzzle\Http\Url; -use Guzzle\Http\QueryString; - -/** - * Generic HTTP request interface - */ -interface RequestInterface extends MessageInterface, HasDispatcherInterface -{ - const STATE_NEW = 'new'; - const STATE_COMPLETE = 'complete'; - const STATE_TRANSFER = 'transfer'; - const STATE_ERROR = 'error'; - - const GET = 'GET'; - const PUT = 'PUT'; - const POST = 'POST'; - const DELETE = 'DELETE'; - const HEAD = 'HEAD'; - const CONNECT = 'CONNECT'; - const OPTIONS = 'OPTIONS'; - const TRACE = 'TRACE'; - const PATCH = 'PATCH'; - - /** - * @return string - */ - public function __toString(); - - /** - * Send the request - * - * @return Response - * @throws RequestException on a request error - */ - public function send(); - - /** - * Set the client used to transport the request - * - * @param ClientInterface $client - * - * @return self - */ - public function setClient(ClientInterface $client); - - /** - * Get the client used to transport the request - * - * @return ClientInterface $client - */ - public function getClient(); - - /** - * Set the URL of the request - * - * @param string $url|Url Full URL to set including query string - * - * @return self - */ - public function setUrl($url); - - /** - * Get the full URL of the request (e.g. 'http://www.guzzle-project.com/') - * - * @param bool $asObject Set to TRUE to retrieve the URL as a clone of the URL object owned by the request. - * - * @return string|Url - */ - public function getUrl($asObject = false); - - /** - * Get the resource part of the the request, including the path, query string, and fragment - * - * @return string - */ - public function getResource(); - - /** - * Get the collection of key value pairs that will be used as the query string in the request - * - * @return QueryString - */ - public function getQuery(); - - /** - * Get the HTTP method of the request - * - * @return string - */ - public function getMethod(); - - /** - * Get the URI scheme of the request (http, https, ftp, etc) - * - * @return string - */ - public function getScheme(); - - /** - * Set the URI scheme of the request (http, https, ftp, etc) - * - * @param string $scheme Scheme to set - * - * @return self - */ - public function setScheme($scheme); - - /** - * Get the host of the request - * - * @return string - */ - public function getHost(); - - /** - * Set the host of the request. Including a port in the host will modify the port of the request. - * - * @param string $host Host to set (e.g. www.yahoo.com, www.yahoo.com:80) - * - * @return self - */ - public function setHost($host); - - /** - * Get the path of the request (e.g. '/', '/index.html') - * - * @return string - */ - public function getPath(); - - /** - * Set the path of the request (e.g. '/', '/index.html') - * - * @param string|array $path Path to set or array of segments to implode - * - * @return self - */ - public function setPath($path); - - /** - * Get the port that the request will be sent on if it has been set - * - * @return int|null - */ - public function getPort(); - - /** - * Set the port that the request will be sent on - * - * @param int $port Port number to set - * - * @return self - */ - public function setPort($port); - - /** - * Get the username to pass in the URL if set - * - * @return string|null - */ - public function getUsername(); - - /** - * Get the password to pass in the URL if set - * - * @return string|null - */ - public function getPassword(); - - /** - * Set HTTP authorization parameters - * - * @param string|bool $user User name or false disable authentication - * @param string $password Password - * @param string $scheme Authentication scheme ('Basic', 'Digest', or a CURLAUTH_* constant (deprecated)) - * - * @return self - * @link http://www.ietf.org/rfc/rfc2617.txt - * @link http://php.net/manual/en/function.curl-setopt.php See the available options for CURLOPT_HTTPAUTH - * @throws RequestException - */ - public function setAuth($user, $password = '', $scheme = 'Basic'); - - /** - * Get the HTTP protocol version of the request - * - * @return string - */ - public function getProtocolVersion(); - - /** - * Set the HTTP protocol version of the request (e.g. 1.1 or 1.0) - * - * @param string $protocol HTTP protocol version to use with the request - * - * @return self - */ - public function setProtocolVersion($protocol); - - /** - * Get the previously received {@see Response} or NULL if the request has not been sent - * - * @return Response|null - */ - public function getResponse(); - - /** - * Manually set a response for the request. - * - * This method is useful for specifying a mock response for the request or setting the response using a cache. - * Manually setting a response will bypass the actual sending of a request. - * - * @param Response $response Response object to set - * @param bool $queued Set to TRUE to keep the request in a state of not having been sent, but queue the - * response for send() - * - * @return self Returns a reference to the object. - */ - public function setResponse(Response $response, $queued = false); - - /** - * The start of a response has been received for a request and the request is still in progress - * - * @param Response $response Response that has been received so far - * - * @return self - */ - public function startResponse(Response $response); - - /** - * Set the EntityBody that will hold a successful response message's entity body. - * - * This method should be invoked when you need to send the response's entity body somewhere other than the normal - * php://temp buffer. For example, you can send the entity body to a socket, file, or some other custom stream. - * - * @param EntityBodyInterface|string|resource $body Response body object. Pass a string to attempt to store the - * response body in a local file. - * @return Request - */ - public function setResponseBody($body); - - /** - * Get the EntityBody that will hold the resulting response message's entity body. This response body will only - * be used for successful responses. Intermediate responses (e.g. redirects) will not use the targeted response - * body. - * - * @return EntityBodyInterface - */ - public function getResponseBody(); - - /** - * Get the state of the request. One of 'complete', 'transfer', 'new', 'error' - * - * @return string - */ - public function getState(); - - /** - * Set the state of the request - * - * @param string $state State of the request ('complete', 'transfer', 'new', 'error') - * @param array $context Contextual information about the state change - * - * @return string Returns the current state of the request (which may have changed due to events being fired) - */ - public function setState($state, array $context = array()); - - /** - * Get the cURL options that will be applied when the cURL handle is created - * - * @return Collection - */ - public function getCurlOptions(); - - /** - * Get an array of Cookies - * - * @return array - */ - public function getCookies(); - - /** - * Get a cookie value by name - * - * @param string $name Cookie to retrieve - * - * @return null|string - */ - public function getCookie($name); - - /** - * Add a Cookie value by name to the Cookie header - * - * @param string $name Name of the cookie to add - * @param string $value Value to set - * - * @return self - */ - public function addCookie($name, $value); - - /** - * Remove a specific cookie value by name - * - * @param string $name Cookie to remove by name - * - * @return self - */ - public function removeCookie($name); -} diff --git a/core/vendor/guzzle/http/Guzzle/Http/Message/Response.php b/core/vendor/guzzle/http/Guzzle/Http/Message/Response.php deleted file mode 100644 index 654ea761ff36684acdef9b6aea124177e7debb96..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/http/Guzzle/Http/Message/Response.php +++ /dev/null @@ -1,948 +0,0 @@ -<?php - -namespace Guzzle\Http\Message; - -use Guzzle\Common\Version; -use Guzzle\Common\ToArrayInterface; -use Guzzle\Common\Exception\RuntimeException; -use Guzzle\Http\EntityBodyInterface; -use Guzzle\Http\EntityBody; -use Guzzle\Http\Exception\BadResponseException; -use Guzzle\Http\RedirectPlugin; -use Guzzle\Parser\ParserRegistry; - -/** - * Guzzle HTTP response object - */ -class Response extends AbstractMessage implements \Serializable -{ - /** - * @var array Array of reason phrases and their corresponding status codes - */ - private static $statusTexts = array( - 100 => 'Continue', - 101 => 'Switching Protocols', - 102 => 'Processing', - 200 => 'OK', - 201 => 'Created', - 202 => 'Accepted', - 203 => 'Non-Authoritative Information', - 204 => 'No Content', - 205 => 'Reset Content', - 206 => 'Partial Content', - 207 => 'Multi-Status', - 208 => 'Already Reported', - 226 => 'IM Used', - 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', - 422 => 'Unprocessable Entity', - 423 => 'Locked', - 424 => 'Failed Dependency', - 425 => 'Reserved for WebDAV advanced collections expired proposal', - 426 => 'Upgrade required', - 428 => 'Precondition Required', - 429 => 'Too Many Requests', - 431 => 'Request Header Fields Too Large', - 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 (Experimental)', - 507 => 'Insufficient Storage', - 508 => 'Loop Detected', - 510 => 'Not Extended', - 511 => 'Network Authentication Required', - ); - - /** @var EntityBodyInterface The response body */ - protected $body; - - /** @var string The reason phrase of the response (human readable code) */ - protected $reasonPhrase; - - /** @var string The status code of the response */ - protected $statusCode; - - /** @var array Information about the request */ - protected $info = array(); - - /** @var string The effective URL that returned this response */ - protected $effectiveUrl; - - /** @var array Cacheable response codes (see RFC 2616:13.4) */ - protected static $cacheResponseCodes = array(200, 203, 206, 300, 301, 410); - - /** - * Create a new Response based on a raw response message - * - * @param string $message Response message - * - * @return self|bool Returns false on error - */ - public static function fromMessage($message) - { - $data = ParserRegistry::getInstance()->getParser('message')->parseResponse($message); - if (!$data) { - return false; - } - - $response = new static($data['code'], $data['headers'], $data['body']); - $response->setProtocol($data['protocol'], $data['version']) - ->setStatus($data['code'], $data['reason_phrase']); - - // Set the appropriate Content-Length if the one set is inaccurate (e.g. setting to X) - $contentLength = (string) $response->getHeader('Content-Length'); - $actualLength = strlen($data['body']); - if (strlen($data['body']) > 0 && $contentLength != $actualLength) { - $response->setHeader('Content-Length', $actualLength); - } - - return $response; - } - - /** - * Construct the response - * - * @param string $statusCode The response status code (e.g. 200, 404, etc) - * @param ToArrayInterface|array $headers The response headers - * @param string|resource|EntityBodyInterface $body The body of the response - * - * @throws BadResponseException if an invalid response code is given - */ - public function __construct($statusCode, $headers = null, $body = null) - { - parent::__construct(); - $this->setStatus($statusCode); - $this->body = EntityBody::factory($body !== null ? $body : ''); - - if ($headers) { - if (is_array($headers)) { - $this->setHeaders($headers); - } elseif ($headers instanceof ToArrayInterface) { - $this->setHeaders($headers->toArray()); - } else { - throw new BadResponseException('Invalid headers argument received'); - } - } - } - - /** - * @return string - */ - public function __toString() - { - return $this->getMessage(); - } - - public function serialize() - { - return json_encode(array( - 'status' => $this->statusCode, - 'body' => (string) $this->body, - 'headers' => $this->headers->toArray() - )); - } - - public function unserialize($serialize) - { - $data = json_decode($serialize, true); - $this->__construct($data['status'], $data['headers'], $data['body']); - } - - /** - * Get the response entity body - * - * @param bool $asString Set to TRUE to return a string of the body rather than a full body object - * - * @return EntityBodyInterface|string - */ - public function getBody($asString = false) - { - return $asString ? (string) $this->body : $this->body; - } - - /** - * Set the response entity body - * - * @param EntityBodyInterface|string $body Body to set - * - * @return self - */ - public function setBody($body) - { - $this->body = EntityBody::factory($body); - - return $this; - } - - /** - * Set the protocol and protocol version of the response - * - * @param string $protocol Response protocol - * @param string $version Protocol version - * - * @return self - */ - public function setProtocol($protocol, $version) - { - $this->protocol = $protocol; - $this->protocolVersion = $version; - - return $this; - } - - /** - * Get the protocol used for the response (e.g. HTTP) - * - * @return string - */ - public function getProtocol() - { - return $this->protocol; - } - - /** - * Get the HTTP protocol version - * - * @return string - */ - public function getProtocolVersion() - { - return $this->protocolVersion; - } - - /** - * Get a cURL transfer information - * - * @param string $key A single statistic to check - * - * @return array|string|null Returns all stats if no key is set, a single stat if a key is set, or null if a key - * is set and not found - * @link http://www.php.net/manual/en/function.curl-getinfo.php - */ - public function getInfo($key = null) - { - if ($key === null) { - return $this->info; - } elseif (array_key_exists($key, $this->info)) { - return $this->info[$key]; - } else { - return null; - } - } - - /** - * Set the transfer information - * - * @param array $info Array of cURL transfer stats - * - * @return self - */ - public function setInfo(array $info) - { - $this->info = $info; - - return $this; - } - - /** - * Set the response status - * - * @param int $statusCode Response status code to set - * @param string $reasonPhrase Response reason phrase - * - * @return self - * @throws BadResponseException when an invalid response code is received - */ - public function setStatus($statusCode, $reasonPhrase = '') - { - $this->statusCode = (int) $statusCode; - - if (!$reasonPhrase && isset(self::$statusTexts[$this->statusCode])) { - $this->reasonPhrase = self::$statusTexts[$this->statusCode]; - } else { - $this->reasonPhrase = $reasonPhrase; - } - - return $this; - } - - /** - * Get the response status code - * - * @return integer - */ - public function getStatusCode() - { - return $this->statusCode; - } - - /** - * Get the entire response as a string - * - * @return string - */ - public function getMessage() - { - $message = $this->getRawHeaders(); - - // Only include the body in the message if the size is < 2MB - $size = $this->body->getSize(); - if ($size < 2097152) { - $message .= (string) $this->body; - } - - return $message; - } - - /** - * Get the the raw message headers as a string - * - * @return string - */ - public function getRawHeaders() - { - $headers = 'HTTP/1.1 ' . $this->statusCode . ' ' . $this->reasonPhrase . "\r\n"; - $lines = $this->getHeaderLines(); - if (!empty($lines)) { - $headers .= implode("\r\n", $lines) . "\r\n"; - } - - return $headers . "\r\n"; - } - - /** - * Get the response reason phrase- a human readable version of the numeric - * status code - * - * @return string - */ - public function getReasonPhrase() - { - return $this->reasonPhrase; - } - - /** - * Get the Accept-Ranges HTTP header - * - * @return string Returns what partial content range types this server supports. - */ - public function getAcceptRanges() - { - return (string) $this->getHeader('Accept-Ranges'); - } - - /** - * Calculate the age of the response - * - * @return integer - */ - public function calculateAge() - { - $age = $this->getHeader('Age'); - - if ($age === null && $this->getDate()) { - $age = time() - strtotime($this->getDate()); - } - - return $age === null ? null : (int) (string) $age; - } - - /** - * Get the Age HTTP header - * - * @return integer|null Returns the age the object has been in a proxy cache in seconds. - */ - public function getAge() - { - return (string) $this->getHeader('Age'); - } - - /** - * Get the Allow HTTP header - * - * @return string|null Returns valid actions for a specified resource. To be used for a 405 Method not allowed. - */ - public function getAllow() - { - return (string) $this->getHeader('Allow'); - } - - /** - * Check if an HTTP method is allowed by checking the Allow response header - * - * @param string $method Method to check - * - * @return bool - */ - public function isMethodAllowed($method) - { - $allow = $this->getHeader('Allow'); - if ($allow) { - foreach (explode(',', $allow) as $allowable) { - if (!strcasecmp(trim($allowable), $method)) { - return true; - } - } - } - - return false; - } - - /** - * Get the Cache-Control HTTP header - * - * @return string - */ - public function getCacheControl() - { - return (string) $this->getHeader('Cache-Control'); - } - - /** - * Get the Connection HTTP header - * - * @return string - */ - public function getConnection() - { - return (string) $this->getHeader('Connection'); - } - - /** - * Get the Content-Encoding HTTP header - * - * @return string|null - */ - public function getContentEncoding() - { - return (string) $this->getHeader('Content-Encoding'); - } - - /** - * Get the Content-Language HTTP header - * - * @return string|null Returns the language the content is in. - */ - public function getContentLanguage() - { - return (string) $this->getHeader('Content-Language'); - } - - /** - * Get the Content-Length HTTP header - * - * @return integer Returns the length of the response body in bytes - */ - public function getContentLength() - { - return (int) (string) $this->getHeader('Content-Length'); - } - - /** - * Get the Content-Location HTTP header - * - * @return string|null Returns an alternate location for the returned data (e.g /index.htm) - */ - public function getContentLocation() - { - return (string) $this->getHeader('Content-Location'); - } - - /** - * Get the Content-Disposition HTTP header - * - * @return string|null Returns the Content-Disposition header - */ - public function getContentDisposition() - { - return (string) $this->getHeader('Content-Disposition'); - } - - /** - * Get the Content-MD5 HTTP header - * - * @return string|null Returns a Base64-encoded binary MD5 sum of the content of the response. - */ - public function getContentMd5() - { - return (string) $this->getHeader('Content-MD5'); - } - - /** - * Get the Content-Range HTTP header - * - * @return string Returns where in a full body message this partial message belongs (e.g. bytes 21010-47021/47022). - */ - public function getContentRange() - { - return (string) $this->getHeader('Content-Range'); - } - - /** - * Get the Content-Type HTTP header - * - * @return string Returns the mime type of this content. - */ - public function getContentType() - { - return (string) $this->getHeader('Content-Type'); - } - - /** - * Checks if the Content-Type is of a certain type. This is useful if the - * Content-Type header contains charset information and you need to know if - * the Content-Type matches a particular type. - * - * @param string $type Content type to check against - * - * @return bool - */ - public function isContentType($type) - { - return stripos($this->getHeader('Content-Type'), $type) !== false; - } - - /** - * Get the Date HTTP header - * - * @return string|null Returns the date and time that the message was sent. - */ - public function getDate() - { - return (string) $this->getHeader('Date'); - } - - /** - * Get the ETag HTTP header - * - * @return string|null Returns an identifier for a specific version of a resource, often a Message digest. - */ - public function getEtag() - { - return (string) $this->getHeader('ETag'); - } - - /** - * Get the Expires HTTP header - * - * @return string|null Returns the date/time after which the response is considered stale. - */ - public function getExpires() - { - return (string) $this->getHeader('Expires'); - } - - /** - * Get the Last-Modified HTTP header - * - * @return string|null Returns the last modified date for the requested object, in RFC 2822 format - * (e.g. Tue, 15 Nov 1994 12:45:26 GMT) - */ - public function getLastModified() - { - return (string) $this->getHeader('Last-Modified'); - } - - /** - * Get the Location HTTP header - * - * @return string|null Used in redirection, or when a new resource has been created. - */ - public function getLocation() - { - return (string) $this->getHeader('Location'); - } - - /** - * Get the Pragma HTTP header - * - * @return Header|null Returns the implementation-specific headers that may have various effects anywhere along - * the request-response chain. - */ - public function getPragma() - { - return (string) $this->getHeader('Pragma'); - } - - /** - * Get the Proxy-Authenticate HTTP header - * - * @return string|null Authentication to access the proxy (e.g. Basic) - */ - public function getProxyAuthenticate() - { - return (string) $this->getHeader('Proxy-Authenticate'); - } - - /** - * Get the Retry-After HTTP header - * - * @return int|null If an entity is temporarily unavailable, this instructs the client to try again after a - * specified period of time. - */ - public function getRetryAfter() - { - return (string) $this->getHeader('Retry-After'); - } - - /** - * Get the Server HTTP header - * - * @return string|null A name for the server - */ - public function getServer() - { - return (string) $this->getHeader('Server'); - } - - /** - * Get the Set-Cookie HTTP header - * - * @return string|null An HTTP cookie. - */ - public function getSetCookie() - { - return (string) $this->getHeader('Set-Cookie'); - } - - /** - * Get the Trailer HTTP header - * - * @return string|null The Trailer general field value indicates that the given set of header fields is present in - * the trailer of a message encoded with chunked transfer-coding. - */ - public function getTrailer() - { - return (string) $this->getHeader('Trailer'); - } - - /** - * Get the Transfer-Encoding HTTP header - * - * @return string|null The form of encoding used to safely transfer the entity to the user - */ - public function getTransferEncoding() - { - return (string) $this->getHeader('Transfer-Encoding'); - } - - /** - * Get the Vary HTTP header - * - * @return string|null Tells downstream proxies how to match future request headers to decide whether the cached - * response can be used rather than requesting a fresh one from the origin server. - */ - public function getVary() - { - return (string) $this->getHeader('Vary'); - } - - /** - * Get the Via HTTP header - * - * @return string|null Informs the client of proxies through which the response was sent. - */ - public function getVia() - { - return (string) $this->getHeader('Via'); - } - - /** - * Get the Warning HTTP header - * - * @return string|null A general warning about possible problems with the entity body - */ - public function getWarning() - { - return (string) $this->getHeader('Warning'); - } - - /** - * Get the WWW-Authenticate HTTP header - * - * @return string|null Indicates the authentication scheme that should be used to access the requested entity - */ - public function getWwwAuthenticate() - { - return (string) $this->getHeader('WWW-Authenticate'); - } - - /** - * Checks if HTTP Status code is a Client Error (4xx) - * - * @return bool - */ - public function isClientError() - { - return $this->statusCode >= 400 && $this->statusCode < 500; - } - - /** - * Checks if HTTP Status code is Server OR Client Error (4xx or 5xx) - * - * @return boolean - */ - public function isError() - { - return $this->isClientError() || $this->isServerError(); - } - - /** - * Checks if HTTP Status code is Information (1xx) - * - * @return bool - */ - public function isInformational() - { - return $this->statusCode < 200; - } - - /** - * Checks if HTTP Status code is a Redirect (3xx) - * - * @return bool - */ - public function isRedirect() - { - return $this->statusCode >= 300 && $this->statusCode < 400; - } - - /** - * Checks if HTTP Status code is Server Error (5xx) - * - * @return bool - */ - public function isServerError() - { - return $this->statusCode >= 500 && $this->statusCode < 600; - } - - /** - * Checks if HTTP Status code is Successful (2xx | 304) - * - * @return bool - */ - public function isSuccessful() - { - return ($this->statusCode >= 200 && $this->statusCode < 300) || $this->statusCode == 304; - } - - /** - * Check if the response can be cached based on the response headers - * - * @return bool Returns TRUE if the response can be cached or false if not - */ - public function canCache() - { - // Check if the response is cacheable based on the code - if (!in_array((int) $this->getStatusCode(), self::$cacheResponseCodes)) { - return false; - } - - // Make sure a valid body was returned and can be cached - if ((!$this->getBody()->isReadable() || !$this->getBody()->isSeekable()) - && ($this->getContentLength() > 0 || $this->getTransferEncoding() == 'chunked')) { - return false; - } - - // Never cache no-store resources (this is a private cache, so private - // can be cached) - if ($this->getHeader('Cache-Control') && $this->getHeader('Cache-Control')->hasDirective('no-store')) { - return false; - } - - return $this->isFresh() || $this->getFreshness() === null || $this->canValidate(); - } - - /** - * Gets the number of seconds from the current time in which this response is still considered fresh - * - * @return int|null Returns the number of seconds - */ - public function getMaxAge() - { - if ($header = $this->getHeader('Cache-Control')) { - // s-max-age, then max-age, then Expires - if ($age = $header->getDirective('s-maxage')) { - return $age; - } - if ($age = $header->getDirective('max-age')) { - return $age; - } - } - - if ($this->getHeader('Expires')) { - return strtotime($this->getExpires()) - time(); - } - - return null; - } - - /** - * Check if the response is considered fresh. - * - * A response is considered fresh when its age is less than or equal to the freshness lifetime (maximum age) of the - * response. - * - * @return bool|null - */ - public function isFresh() - { - $fresh = $this->getFreshness(); - - return $fresh === null ? null : $fresh >= 0; - } - - /** - * Check if the response can be validated against the origin server using a conditional GET request. - * - * @return bool - */ - public function canValidate() - { - return $this->getEtag() || $this->getLastModified(); - } - - /** - * Get the freshness of the response by returning the difference of the maximum lifetime of the response and the - * age of the response (max-age - age). - * - * Freshness values less than 0 mean that the response is no longer fresh and is ABS(freshness) seconds expired. - * Freshness values of greater than zero is the number of seconds until the response is no longer fresh. A NULL - * result means that no freshness information is available. - * - * @return int - */ - public function getFreshness() - { - $maxAge = $this->getMaxAge(); - $age = $this->calculateAge(); - - return $maxAge && $age ? ($maxAge - $age) : null; - } - - /** - * Parse the JSON response body and return an array - * - * @return array|string|int|bool|float - * @throws RuntimeException if the response body is not in JSON format - */ - public function json() - { - $data = json_decode((string) $this->body, true); - if (JSON_ERROR_NONE !== json_last_error()) { - throw new RuntimeException('Unable to parse response body into JSON: ' . json_last_error()); - } - - return $data === null ? array() : $data; - } - - /** - * Parse the XML response body and return a SimpleXMLElement - * - * @return \SimpleXMLElement - * @throws RuntimeException if the response body is not in XML format - */ - public function xml() - { - try { - // Allow XML to be retrieved even if there is no response body - $xml = new \SimpleXMLElement((string) $this->body ?: '<root />'); - } catch (\Exception $e) { - throw new RuntimeException('Unable to parse response body into XML: ' . $e->getMessage()); - } - - return $xml; - } - - /** - * Get the redirect count of this response - * - * @return int - */ - public function getRedirectCount() - { - return (int) $this->params->get(RedirectPlugin::REDIRECT_COUNT); - } - - /** - * Set the effective URL that resulted in this response (e.g. the last redirect URL) - * - * @param string $url The effective URL - * - * @return self - */ - public function setEffectiveUrl($url) - { - $this->effectiveUrl = $url; - - return $this; - } - - /** - * Get the effective URL that resulted in this response (e.g. the last redirect URL) - * - * @return string - */ - public function getEffectiveUrl() - { - return $this->effectiveUrl; - } - - /** - * @deprecated - * @codeCoverageIgnore - */ - public function getPreviousResponse() - { - Version::warn(__METHOD__ . ' is deprecated. Use the HistoryPlugin.'); - return null; - } - - /** - * @deprecated - * @codeCoverageIgnore - */ - public function setRequest($request) - { - Version::warn(__METHOD__ . ' is deprecated'); - return $this; - } - - /** - * @deprecated - * @codeCoverageIgnore - */ - public function getRequest() - { - Version::warn(__METHOD__ . ' is deprecated'); - return null; - } -} diff --git a/core/vendor/guzzle/http/Guzzle/Http/QueryAggregator/CommaAggregator.php b/core/vendor/guzzle/http/Guzzle/Http/QueryAggregator/CommaAggregator.php deleted file mode 100644 index 4b4e49d05201e5d1de6645acf82fec4489d11c11..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/http/Guzzle/Http/QueryAggregator/CommaAggregator.php +++ /dev/null @@ -1,20 +0,0 @@ -<?php - -namespace Guzzle\Http\QueryAggregator; - -use Guzzle\Http\QueryString; - -/** - * Aggregates nested query string variables using commas - */ -class CommaAggregator implements QueryAggregatorInterface -{ - public function aggregate($key, $value, QueryString $query) - { - if ($query->isUrlEncoding()) { - return array($query->encodeValue($key) => implode(',', array_map(array($query, 'encodeValue'), $value))); - } else { - return array($key => implode(',', $value)); - } - } -} diff --git a/core/vendor/guzzle/http/Guzzle/Http/QueryAggregator/DuplicateAggregator.php b/core/vendor/guzzle/http/Guzzle/Http/QueryAggregator/DuplicateAggregator.php deleted file mode 100644 index 1bf1730e4e697bc6796fd4c3ef037b2a3d0f43ef..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/http/Guzzle/Http/QueryAggregator/DuplicateAggregator.php +++ /dev/null @@ -1,22 +0,0 @@ -<?php - -namespace Guzzle\Http\QueryAggregator; - -use Guzzle\Http\QueryString; - -/** - * Does not aggregate nested query string values and allows duplicates in the resulting array - * - * Example: http://test.com?q=1&q=2 - */ -class DuplicateAggregator implements QueryAggregatorInterface -{ - public function aggregate($key, $value, QueryString $query) - { - if ($query->isUrlEncoding()) { - return array($query->encodeValue($key) => array_map(array($query, 'encodeValue'), $value)); - } else { - return array($key => $value); - } - } -} diff --git a/core/vendor/guzzle/http/Guzzle/Http/QueryAggregator/PhpAggregator.php b/core/vendor/guzzle/http/Guzzle/Http/QueryAggregator/PhpAggregator.php deleted file mode 100644 index 133ea2bd9622fa3e8695226804c51bd2850ae5f5..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/http/Guzzle/Http/QueryAggregator/PhpAggregator.php +++ /dev/null @@ -1,27 +0,0 @@ -<?php - -namespace Guzzle\Http\QueryAggregator; - -use Guzzle\Http\QueryString; - -/** - * Aggregates nested query string variables using PHP style [] - */ -class PhpAggregator implements QueryAggregatorInterface -{ - public function aggregate($key, $value, QueryString $query) - { - $ret = array(); - - foreach ($value as $k => $v) { - $k = "{$key}[{$k}]"; - if (is_array($v)) { - $ret = array_merge($ret, self::aggregate($k, $v, $query)); - } else { - $ret[$query->encodeValue($k)] = $query->encodeValue($v); - } - } - - return $ret; - } -} diff --git a/core/vendor/guzzle/http/Guzzle/Http/QueryAggregator/QueryAggregatorInterface.php b/core/vendor/guzzle/http/Guzzle/Http/QueryAggregator/QueryAggregatorInterface.php deleted file mode 100644 index 72bee620c825166dcde60c1c05bf6a8c40745336..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/http/Guzzle/Http/QueryAggregator/QueryAggregatorInterface.php +++ /dev/null @@ -1,22 +0,0 @@ -<?php - -namespace Guzzle\Http\QueryAggregator; - -use Guzzle\Http\QueryString; - -/** - * Interface used for aggregating nested query string variables into a flattened array of key value pairs - */ -interface QueryAggregatorInterface -{ - /** - * Aggregate multi-valued parameters into a flattened associative array - * - * @param string $key The name of the query string parameter - * @param array $value The values of the parameter - * @param QueryString $query The query string that is being aggregated - * - * @return array Returns an array of the combined values - */ - public function aggregate($key, $value, QueryString $query); -} diff --git a/core/vendor/guzzle/http/Guzzle/Http/QueryString.php b/core/vendor/guzzle/http/Guzzle/Http/QueryString.php deleted file mode 100644 index 62bdfa8c7ae122f566c986c2eedc9962eb90c30b..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/http/Guzzle/Http/QueryString.php +++ /dev/null @@ -1,267 +0,0 @@ -<?php - -namespace Guzzle\Http; - -use Guzzle\Common\Collection; -use Guzzle\Http\QueryAggregator\QueryAggregatorInterface; -use Guzzle\Http\QueryAggregator\PhpAggregator; - -/** - * Query string object to handle managing query string parameters and aggregating those parameters together as a string. - */ -class QueryString extends Collection -{ - /** @var string Used to URL encode with rawurlencode */ - const RFC_3986 = 'RFC 3986'; - - /** @var string Used to encode with urlencode */ - const FORM_URLENCODED = 'application/x-www-form-urlencoded'; - - /** @var string Constant used to create blank query string values (e.g. ?foo) */ - const BLANK = "_guzzle_blank_"; - - /** @var string The query string field separator (e.g. '&') */ - protected $fieldSeparator = '&'; - - /** @var string The query string value separator (e.g. '=') */ - protected $valueSeparator = '='; - - /** @var bool URL encode fields and values */ - protected $urlEncode = 'RFC 3986'; - - /** @var QueryAggregatorInterface */ - protected $aggregator; - - /** @var array Cached PHP aggregator */ - protected static $defaultAggregator = null; - - /** - * Parse a query string into a QueryString object - * - * @param string $query Query string to parse - * - * @return self - */ - public static function fromString($query) - { - $q = new static(); - - if ($query || $query === '0') { - if ($query[0] == '?') { - $query = substr($query, 1); - } - foreach (explode('&', $query) as $kvp) { - $parts = explode('=', $kvp, 2); - $key = rawurldecode($parts[0]); - - if ($paramIsPhpStyleArray = substr($key, -2) == '[]') { - $key = substr($key, 0, -2); - } - - if (isset($parts[1])) { - $value = rawurldecode(str_replace('+', '%20', $parts[1])); - if ($paramIsPhpStyleArray && !$q->hasKey($key)) { - $value = array($value); - } - $q->add($key, $value); - } else { - $q->add($key, null); - } - } - } - - return $q; - } - - /** - * Convert the query string parameters to a query string string - * - * @return string - */ - public function __toString() - { - if (!$this->data) { - return ''; - } - - $queryString = ''; - - foreach ($this->prepareData($this->data) as $name => $value) { - foreach ((array) $value as $v) { - if ($queryString) { - $queryString .= $this->fieldSeparator; - } - $queryString .= $name; - if ($v !== self::BLANK) { - $queryString .= $this->valueSeparator . $v; - } - } - } - - return $queryString; - } - - /** - * Get the query string field separator - * - * @return string - */ - public function getFieldSeparator() - { - return $this->fieldSeparator; - } - - /** - * Get the query string value separator - * - * @return string - */ - public function getValueSeparator() - { - return $this->valueSeparator; - } - - /** - * Returns the type of URL encoding used by the query string - * - * One of: false, "RFC 3986", or "application/x-www-form-urlencoded" - * - * @return bool|string - */ - public function getUrlEncoding() - { - return $this->urlEncode; - } - - /** - * Returns true or false if using URL encoding - * - * @return bool - */ - public function isUrlEncoding() - { - return $this->urlEncode !== false; - } - - /** - * Provide a function for combining multi-valued query string parameters into a single or multiple fields - * - * @param null|QueryAggregatorInterface $aggregator Pass in a QueryAggregatorInterface object to handle converting - * deeply nested query string variables into a flattened array. - * Pass null to use the default PHP style aggregator. For legacy - * reasons, this function accepts a callable that must accepts a - * $key, $value, and query object. - * @return self - * @see \Guzzle\Http\QueryString::aggregateUsingComma() - */ - public function setAggregator(QueryAggregatorInterface $aggregator = null) - { - // Use the default aggregator if none was set - if (!$aggregator) { - if (!self::$defaultAggregator) { - self::$defaultAggregator = new PhpAggregator(); - } - $aggregator = self::$defaultAggregator; - } - - $this->aggregator = $aggregator; - - return $this; - } - - /** - * Set whether or not field names and values should be rawurlencoded - * - * @param bool|string $encode Set to TRUE to use RFC 3986 encoding (rawurlencode), false to disable encoding, or - * form_urlencoding to use application/x-www-form-urlencoded encoding (urlencode) - * @return self - */ - public function useUrlEncoding($encode) - { - $this->urlEncode = ($encode === true) ? self::RFC_3986 : $encode; - - return $this; - } - - /** - * Set the query string separator - * - * @param string $separator The query string separator that will separate fields - * - * @return self - */ - public function setFieldSeparator($separator) - { - $this->fieldSeparator = $separator; - - return $this; - } - - /** - * Set the query string value separator - * - * @param string $separator The query string separator that will separate values from fields - * - * @return self - */ - public function setValueSeparator($separator) - { - $this->valueSeparator = $separator; - - return $this; - } - - /** - * Returns an array of url encoded field names and values - * - * @return array - */ - public function urlEncode() - { - return $this->prepareData($this->data); - } - - /** - * URL encodes a value based on the url encoding type of the query string object - * - * @param string $value Value to encode - * - * @return string - */ - public function encodeValue($value) - { - if ($this->urlEncode == self::RFC_3986) { - return rawurlencode($value); - } elseif ($this->urlEncode == self::FORM_URLENCODED) { - return urlencode($value); - } else { - return (string) $value; - } - } - - /** - * Url encode parameter data and convert nested query strings into a flattened hash. - * - * @param array $data The data to encode - * - * @return array Returns an array of encoded values and keys - */ - protected function prepareData(array $data) - { - // If no aggregator is present then set the default - if (!$this->aggregator) { - $this->setAggregator(null); - } - - $temp = array(); - foreach ($data as $key => $value) { - if (is_array($value)) { - $temp = array_merge($temp, $this->aggregator->aggregate($key, $value, $this)); - } else { - $temp[$this->encodeValue($key)] = $this->encodeValue($value); - } - } - - return $temp; - } -} diff --git a/core/vendor/guzzle/http/Guzzle/Http/ReadLimitEntityBody.php b/core/vendor/guzzle/http/Guzzle/Http/ReadLimitEntityBody.php deleted file mode 100644 index d0bc867e3aa7ad544064dc2405f08c0c54a4daec..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/http/Guzzle/Http/ReadLimitEntityBody.php +++ /dev/null @@ -1,106 +0,0 @@ -<?php - -namespace Guzzle\Http; - -/** - * EntityBody decorator used to return only a subset of an entity body - */ -class ReadLimitEntityBody extends AbstractEntityBodyDecorator -{ - /** @var int Limit the number of bytes that can be read */ - protected $limit; - - /** @var int Offset to start reading from */ - protected $offset; - - /** - * @param EntityBodyInterface $body Body to wrap - * @param int $limit Total number of bytes to allow to be read from the stream - * @param int $offset Position to seek to before reading (only works on seekable streams) - */ - public function __construct(EntityBodyInterface $body, $limit, $offset = 0) - { - parent::__construct($body); - $this->setLimit($limit)->setOffset($offset); - $this->body->seek($offset); - } - - /** - * Returns only a subset of the decorated entity body when cast as a string - * {@inheritdoc} - */ - public function __toString() - { - return substr((string) $this->body, $this->offset, $this->limit) ?: ''; - } - - public function isConsumed() - { - return (($this->offset + $this->limit) - $this->body->ftell()) <= 0; - } - - /** - * Returns the Content-Length of the limited subset of data - * {@inheritdoc} - */ - public function getContentLength() - { - $length = $this->body->getContentLength(); - - return $length === false - ? $this->limit - : min($this->limit, min($length, $this->offset + $this->limit) - $this->offset); - } - - /** - * Allow for a bounded seek on the read limited entity body - * {@inheritdoc} - */ - public function seek($offset, $whence = SEEK_SET) - { - return $whence === SEEK_SET - ? $this->body->seek(max($this->offset, min($this->offset + $this->limit, $offset))) - : false; - } - - /** - * Set the offset to start limiting from - * - * @param int $offset Offset to seek to and begin byte limiting from - * - * @return self - */ - public function setOffset($offset) - { - $this->body->seek($offset); - $this->offset = $offset; - - return $this; - } - - /** - * Set the limit of bytes that the decorator allows to be read from the stream - * - * @param int $limit Total number of bytes to allow to be read from the stream - * - * @return self - */ - public function setLimit($limit) - { - $this->limit = $limit; - - return $this; - } - - public function read($length) - { - // Check if the current position is less than the total allowed bytes + original offset - $remaining = ($this->offset + $this->limit) - $this->body->ftell(); - if ($remaining > 0) { - // Only return the amount of requested data, ensuring that the byte limit is not exceeded - return $this->body->read(min($remaining, $length)); - } else { - return false; - } - } -} diff --git a/core/vendor/guzzle/http/Guzzle/Http/RedirectPlugin.php b/core/vendor/guzzle/http/Guzzle/Http/RedirectPlugin.php deleted file mode 100644 index 391edb152b769911385361881cb29914075075f7..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/http/Guzzle/Http/RedirectPlugin.php +++ /dev/null @@ -1,250 +0,0 @@ -<?php - -namespace Guzzle\Http; - -use Guzzle\Common\Event; -use Guzzle\Http\Exception\BadResponseException; -use Guzzle\Http\Url; -use Guzzle\Http\Message\Response; -use Guzzle\Http\Message\RequestInterface; -use Guzzle\Http\Message\RequestFactory; -use Guzzle\Http\Message\EntityEnclosingRequestInterface; -use Guzzle\Http\Exception\TooManyRedirectsException; -use Guzzle\Http\Exception\CouldNotRewindStreamException; -use Symfony\Component\EventDispatcher\EventSubscriberInterface; - -/** - * Plugin to implement HTTP redirects. Can redirect like a web browser or using strict RFC 2616 compliance - */ -class RedirectPlugin implements EventSubscriberInterface -{ - const REDIRECT_COUNT = 'redirect.count'; - const MAX_REDIRECTS = 'redirect.max'; - const STRICT_REDIRECTS = 'redirect.strict'; - const PARENT_REQUEST = 'redirect.parent_request'; - const DISABLE = 'redirect.disable'; - - /** - * @var int Default number of redirects allowed when no setting is supplied by a request - */ - protected $defaultMaxRedirects = 5; - - public static function getSubscribedEvents() - { - return array( - 'request.sent' => array('onRequestSent', 100), - 'request.clone' => 'cleanupRequest', - 'request.before_send' => 'cleanupRequest' - ); - } - - /** - * Clean up the parameters of a request when it is cloned - * - * @param Event $event Event emitted - */ - public function cleanupRequest(Event $event) - { - $params = $event['request']->getParams(); - unset($params[self::REDIRECT_COUNT]); - unset($params[self::PARENT_REQUEST]); - } - - /** - * Called when a request receives a redirect response - * - * @param Event $event Event emitted - */ - public function onRequestSent(Event $event) - { - $response = $event['response']; - $request = $event['request']; - - // Only act on redirect requests with Location headers - if (!$response || $request->getParams()->get(self::DISABLE)) { - return; - } - - // Trace the original request based on parameter history - $original = $this->getOriginalRequest($request); - - // Terminating condition to set the effective repsonse on the original request - if (!$response->isRedirect() || !$response->hasHeader('Location')) { - if ($request !== $original) { - // This is a terminating redirect response, so set it on the original request - $response->getParams()->set(self::REDIRECT_COUNT, $original->getParams()->get(self::REDIRECT_COUNT)); - $original->setResponse($response); - $response->setEffectiveUrl($request->getUrl()); - } - return; - } - - $this->sendRedirectRequest($original, $request, $response); - } - - /** - * Get the original request that initiated a series of redirects - * - * @param RequestInterface $request Request to get the original request from - * - * @return RequestInterface - */ - protected function getOriginalRequest(RequestInterface $request) - { - $original = $request; - // The number of redirects is held on the original request, so determine which request that is - while ($parent = $original->getParams()->get(self::PARENT_REQUEST)) { - $original = $parent; - } - - return $original; - } - - /** - * Create a redirect request for a specific request object - * - * Takes into account strict RFC compliant redirection (e.g. redirect POST with POST) vs doing what most clients do - * (e.g. redirect POST with GET). - * - * @param RequestInterface $request Request being redirected - * @param RequestInterface $original Original request - * @param int $statusCode Status code of the redirect - * @param string $location Location header of the redirect - * - * @return RequestInterface Returns a new redirect request - * @throws CouldNotRewindStreamException If the body needs to be rewound but cannot - */ - protected function createRedirectRequest( - RequestInterface $request, - $statusCode, - $location, - RequestInterface $original - ) { - $redirectRequest = null; - $strict = $original->getParams()->get(self::STRICT_REDIRECTS); - - // Use a GET request if this is an entity enclosing request and we are not forcing RFC compliance, but rather - // emulating what all browsers would do - if ($request instanceof EntityEnclosingRequestInterface && !$strict && $statusCode <= 302) { - $redirectRequest = RequestFactory::getInstance()->cloneRequestWithMethod($request, 'GET'); - } else { - $redirectRequest = clone $request; - } - - $redirectRequest->setIsRedirect(true); - // Always use the same response body when redirecting - $redirectRequest->setResponseBody($request->getResponseBody()); - - $location = Url::factory($location); - // If the location is not absolute, then combine it with the original URL - if (!$location->isAbsolute()) { - $originalUrl = $redirectRequest->getUrl(true); - // Remove query string parameters and just take what is present on the redirect Location header - $originalUrl->getQuery()->clear(); - $location = $originalUrl->combine((string) $location); - } - - $redirectRequest->setUrl($location); - - // Add the parent request to the request before it sends (make sure it's before the onRequestClone event too) - $redirectRequest->getEventDispatcher()->addListener( - 'request.before_send', - $func = function ($e) use (&$func, $request, $redirectRequest) { - $redirectRequest->getEventDispatcher()->removeListener('request.before_send', $func); - $e['request']->getParams()->set(RedirectPlugin::PARENT_REQUEST, $request); - } - ); - - // Rewind the entity body of the request if needed - if ($redirectRequest instanceof EntityEnclosingRequestInterface && $redirectRequest->getBody()) { - $body = $redirectRequest->getBody(); - // Only rewind the body if some of it has been read already, and throw an exception if the rewind fails - if ($body->ftell() && !$body->rewind()) { - throw new CouldNotRewindStreamException( - 'Unable to rewind the non-seekable entity body of the request after redirecting. cURL probably ' - . 'sent part of body before the redirect occurred. Try adding acustom rewind function using on the ' - . 'entity body of the request using setRewindFunction().' - ); - } - } - - return $redirectRequest; - } - - /** - * Prepare the request for redirection and enforce the maximum number of allowed redirects per client - * - * @param RequestInterface $original Origina request - * @param RequestInterface $request Request to prepare and validate - * @param Response $response The current response - * - * @return RequestInterface - */ - protected function prepareRedirection(RequestInterface $original, RequestInterface $request, Response $response) - { - $params = $original->getParams(); - // This is a new redirect, so increment the redirect counter - $current = $params[self::REDIRECT_COUNT] + 1; - $params[self::REDIRECT_COUNT] = $current; - // Use a provided maximum value or default to a max redirect count of 5 - $max = isset($params[self::MAX_REDIRECTS]) ? $params[self::MAX_REDIRECTS] : $this->defaultMaxRedirects; - - // Throw an exception if the redirect count is exceeded - if ($current > $max) { - $this->throwTooManyRedirectsException($original, $max); - return false; - } else { - // Create a redirect request based on the redirect rules set on the request - return $this->createRedirectRequest( - $request, - $response->getStatusCode(), - trim($response->getLocation()), - $original - ); - } - } - - /** - * Send a redirect request and handle any errors - * - * @param RequestInterface $original The originating request - * @param RequestInterface $request The current request being redirected - * @param Response $response The response of the current request - * - * @throws BadResponseException|\Exception - */ - protected function sendRedirectRequest(RequestInterface $original, RequestInterface $request, Response $response) - { - // Validate and create a redirect request based on the original request and current response - if ($redirectRequest = $this->prepareRedirection($original, $request, $response)) { - try { - $redirectRequest->send(); - } catch (BadResponseException $e) { - $e->getResponse(); - if (!$e->getResponse()) { - throw $e; - } - } - } - } - - /** - * Throw a too many redirects exception for a request - * - * @param RequestInterface $original Request - * @param int $max Max allowed redirects - * - * @throws TooManyRedirectsException when too many redirects have been issued - */ - protected function throwTooManyRedirectsException(RequestInterface $original, $max) - { - $original->getEventDispatcher()->addListener( - 'request.complete', - $func = function ($e) use (&$func, $original, $max) { - $original->getEventDispatcher()->removeListener('request.complete', $func); - $str = "{$max} redirects were issued for this request:\n" . $e['request']->getRawHeaders(); - throw new TooManyRedirectsException($str); - } - ); - } -} diff --git a/core/vendor/guzzle/http/Guzzle/Http/Resources/cacert.pem.md5 b/core/vendor/guzzle/http/Guzzle/Http/Resources/cacert.pem.md5 deleted file mode 100644 index 56f626a934867c022a7fe103a6d37313989aea42..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/http/Guzzle/Http/Resources/cacert.pem.md5 +++ /dev/null @@ -1 +0,0 @@ -47961e7ef15667c93cd99be01b51f00a diff --git a/core/vendor/guzzle/http/Guzzle/Http/StaticClient.php b/core/vendor/guzzle/http/Guzzle/Http/StaticClient.php deleted file mode 100644 index dbd4c18413a5870a67ff6de38afa27029b2e1612..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/http/Guzzle/Http/StaticClient.php +++ /dev/null @@ -1,157 +0,0 @@ -<?php - -namespace Guzzle\Http; - -use Guzzle\Http\Client; -use Guzzle\Http\ClientInterface; -use Guzzle\Stream\StreamRequestFactoryInterface; -use Guzzle\Stream\PhpStreamRequestFactory; - -/** - * Simplified interface to Guzzle that does not require a class to be instantiated - */ -final class StaticClient -{ - /** @var Client Guzzle client */ - private static $client; - - /** - * Mount the client to a simpler class name for a specific client - * - * @param string $className Class name to use to mount - * @param ClientInterface $client Client used to send requests - */ - public static function mount($className = 'Guzzle', ClientInterface $client = null) - { - class_alias(__CLASS__, $className); - if ($client) { - self::$client = $client; - } - } - - /** - * @param string $method HTTP request method (GET, POST, HEAD, DELETE, PUT, etc) - * @param string $url URL of the request - * @param array $options Options to use with the request. See: Guzzle\Http\Message\RequestFactory::applyOptions() - * @return \Guzzle\Http\Message\Response|\Guzzle\Stream\Stream - */ - public static function request($method, $url, $options = array()) - { - // @codeCoverageIgnoreStart - if (!self::$client) { - self::$client = new Client(); - } - // @codeCoverageIgnoreEnd - - $request = self::$client->createRequest($method, $url, null, null, $options); - - if (isset($options['stream'])) { - if ($options['stream'] instanceof StreamRequestFactoryInterface) { - return $options['stream']->fromRequest($request); - } elseif ($options['stream'] == true) { - $streamFactory = new PhpStreamRequestFactory(); - return $streamFactory->fromRequest($request); - } - } - - return $request->send(); - } - - /** - * Send a GET request - * - * @param string $url URL of the request - * @param array $options Array of request options - * - * @return \Guzzle\Http\Message\Response - * @see Guzzle::request for a list of available options - */ - public static function get($url, $options = array()) - { - return self::request('GET', $url, $options); - } - - /** - * Send a HEAD request - * - * @param string $url URL of the request - * @param array $options Array of request options - * - * @return \Guzzle\Http\Message\Response - * @see Guzzle::request for a list of available options - */ - public static function head($url, $options = array()) - { - return self::request('HEAD', $url, $options); - } - - /** - * Send a DELETE request - * - * @param string $url URL of the request - * @param array $options Array of request options - * - * @return \Guzzle\Http\Message\Response - * @see Guzzle::request for a list of available options - */ - public static function delete($url, $options = array()) - { - return self::request('DELETE', $url, $options); - } - - /** - * Send a POST request - * - * @param string $url URL of the request - * @param array $options Array of request options - * - * @return \Guzzle\Http\Message\Response - * @see Guzzle::request for a list of available options - */ - public static function post($url, $options = array()) - { - return self::request('POST', $url, $options); - } - - /** - * Send a PUT request - * - * @param string $url URL of the request - * @param array $options Array of request options - * - * @return \Guzzle\Http\Message\Response - * @see Guzzle::request for a list of available options - */ - public static function put($url, $options = array()) - { - return self::request('PUT', $url, $options); - } - - /** - * Send a PATCH request - * - * @param string $url URL of the request - * @param array $options Array of request options - * - * @return \Guzzle\Http\Message\Response - * @see Guzzle::request for a list of available options - */ - public static function patch($url, $options = array()) - { - return self::request('PATCH', $url, $options); - } - - /** - * Send an OPTIONS request - * - * @param string $url URL of the request - * @param array $options Array of request options - * - * @return \Guzzle\Http\Message\Response - * @see Guzzle::request for a list of available options - */ - public static function options($url, $options = array()) - { - return self::request('OPTIONS', $url, $options); - } -} diff --git a/core/vendor/guzzle/http/Guzzle/Http/composer.json b/core/vendor/guzzle/http/Guzzle/Http/composer.json deleted file mode 100644 index 9384a5bf918de990673d64c686bc95e75fcce7a6..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/http/Guzzle/Http/composer.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "name": "guzzle/http", - "description": "HTTP libraries used by Guzzle", - "homepage": "http://guzzlephp.org/", - "keywords": ["http client", "http", "client", "Guzzle", "curl"], - "license": "MIT", - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - } - ], - "require": { - "php": ">=5.3.2", - "guzzle/common": "self.version", - "guzzle/parser": "self.version", - "guzzle/stream": "self.version" - }, - "suggest": { - "ext-curl": "*" - }, - "autoload": { - "psr-0": { "Guzzle\\Http": "" } - }, - "target-dir": "Guzzle/Http", - "extra": { - "branch-alias": { - "dev-master": "3.7-dev" - } - } -} diff --git a/core/vendor/guzzle/parser/Guzzle/Parser/Cookie/CookieParser.php b/core/vendor/guzzle/parser/Guzzle/Parser/Cookie/CookieParser.php deleted file mode 100644 index 8e825f9bdeab5a7554c715af9182642cf93945db..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/parser/Guzzle/Parser/Cookie/CookieParser.php +++ /dev/null @@ -1,86 +0,0 @@ -<?php - -namespace Guzzle\Parser\Cookie; - -/** - * Default Guzzle implementation of a Cookie parser - */ -class CookieParser implements CookieParserInterface -{ - /** @var array Cookie part names to snake_case array values */ - protected static $cookieParts = array( - 'domain' => 'Domain', - 'path' => 'Path', - 'max_age' => 'Max-Age', - 'expires' => 'Expires', - 'version' => 'Version', - 'secure' => 'Secure', - 'port' => 'Port', - 'discard' => 'Discard', - 'comment' => 'Comment', - 'comment_url' => 'Comment-Url', - 'http_only' => 'HttpOnly' - ); - - public function parseCookie($cookie, $host = null, $path = null, $decode = false) - { - // Explode the cookie string using a series of semicolons - $pieces = array_filter(array_map('trim', explode(';', $cookie))); - - // The name of the cookie (first kvp) must include an equal sign. - if (empty($pieces) || !strpos($pieces[0], '=')) { - return false; - } - - // Create the default return array - $data = array_merge(array_fill_keys(array_keys(self::$cookieParts), null), array( - 'cookies' => array(), - 'data' => array(), - 'path' => $path ?: '/', - 'http_only' => false, - 'discard' => false, - 'domain' => $host - )); - $foundNonCookies = 0; - - // Add the cookie pieces into the parsed data array - foreach ($pieces as $part) { - - $cookieParts = explode('=', $part, 2); - $key = trim($cookieParts[0]); - - if (count($cookieParts) == 1) { - // Can be a single value (e.g. secure, httpOnly) - $value = true; - } else { - // Be sure to strip wrapping quotes - $value = trim($cookieParts[1], " \n\r\t\0\x0B\""); - if ($decode) { - $value = urldecode($value); - } - } - - // Only check for non-cookies when cookies have been found - if (!empty($data['cookies'])) { - foreach (self::$cookieParts as $mapValue => $search) { - if (!strcasecmp($search, $key)) { - $data[$mapValue] = $mapValue == 'port' ? array_map('trim', explode(',', $value)) : $value; - $foundNonCookies++; - continue 2; - } - } - } - - // If cookies have not yet been retrieved, or this value was not found in the pieces array, treat it as a - // cookie. IF non-cookies have been parsed, then this isn't a cookie, it's cookie data. Cookies then data. - $data[$foundNonCookies ? 'data' : 'cookies'][$key] = $value; - } - - // Calculate the expires date - if (!$data['expires'] && $data['max_age']) { - $data['expires'] = time() + (int) $data['max_age']; - } - - return $data; - } -} diff --git a/core/vendor/guzzle/parser/Guzzle/Parser/Cookie/CookieParserInterface.php b/core/vendor/guzzle/parser/Guzzle/Parser/Cookie/CookieParserInterface.php deleted file mode 100644 index d21ffe21c1cc6095cf546df0806dc4d8fd78a30c..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/parser/Guzzle/Parser/Cookie/CookieParserInterface.php +++ /dev/null @@ -1,33 +0,0 @@ -<?php - -namespace Guzzle\Parser\Cookie; - -/** - * Cookie parser interface - */ -interface CookieParserInterface -{ - /** - * Parse a cookie string as set in a Set-Cookie HTTP header and return an associative array of data. - * - * @param string $cookie Cookie header value to parse - * @param string $host Host of an associated request - * @param string $path Path of an associated request - * @param bool $decode Set to TRUE to urldecode cookie values - * - * @return array|bool Returns FALSE on failure or returns an array of arrays, with each of the sub arrays including: - * - domain (string) - Domain of the cookie - * - path (string) - Path of the cookie - * - cookies (array) - Associative array of cookie names and values - * - max_age (int) - Lifetime of the cookie in seconds - * - version (int) - Version of the cookie specification. RFC 2965 is 1 - * - secure (bool) - Whether or not this is a secure cookie - * - discard (bool) - Whether or not this is a discardable cookie - * - custom (string) - Custom cookie data array - * - comment (string) - How the cookie is intended to be used - * - comment_url (str)- URL that contains info on how it will be used - * - port (array|str) - Array of ports or null - * - http_only (bool) - HTTP only cookie - */ - public function parseCookie($cookie, $host = null, $path = null, $decode = false); -} diff --git a/core/vendor/guzzle/parser/Guzzle/Parser/Message/AbstractMessageParser.php b/core/vendor/guzzle/parser/Guzzle/Parser/Message/AbstractMessageParser.php deleted file mode 100644 index d25f9cceee045c55c3076000b263e40d90f20adf..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/parser/Guzzle/Parser/Message/AbstractMessageParser.php +++ /dev/null @@ -1,58 +0,0 @@ -<?php - -namespace Guzzle\Parser\Message; - -/** - * Implements shared message parsing functionality - */ -abstract class AbstractMessageParser implements MessageParserInterface -{ - /** - * Create URL parts from HTTP message parts - * - * @param string $requestUrl Associated URL - * @param array $parts HTTP message parts - * - * @return array - */ - protected function getUrlPartsFromMessage($requestUrl, array $parts) - { - // Parse the URL information from the message - $urlParts = array( - 'path' => $requestUrl, - 'scheme' => 'http' - ); - - // Check for the Host header - if (isset($parts['headers']['Host'])) { - $urlParts['host'] = $parts['headers']['Host']; - } elseif (isset($parts['headers']['host'])) { - $urlParts['host'] = $parts['headers']['host']; - } else { - $urlParts['host'] = null; - } - - if (false === strpos($urlParts['host'], ':')) { - $urlParts['port'] = ''; - } else { - $hostParts = explode(':', $urlParts['host']); - $urlParts['host'] = trim($hostParts[0]); - $urlParts['port'] = (int) trim($hostParts[1]); - if ($urlParts['port'] == 443) { - $urlParts['scheme'] = 'https'; - } - } - - // Check if a query is present - $path = $urlParts['path']; - $qpos = strpos($path, '?'); - if ($qpos) { - $urlParts['query'] = substr($path, $qpos + 1); - $urlParts['path'] = substr($path, 0, $qpos); - } else { - $urlParts['query'] = ''; - } - - return $urlParts; - } -} diff --git a/core/vendor/guzzle/parser/Guzzle/Parser/Message/MessageParser.php b/core/vendor/guzzle/parser/Guzzle/Parser/Message/MessageParser.php deleted file mode 100644 index 104740068e8d31cb0addfdff26a2fcf30cb47813..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/parser/Guzzle/Parser/Message/MessageParser.php +++ /dev/null @@ -1,110 +0,0 @@ -<?php - -namespace Guzzle\Parser\Message; - -/** - * Default request and response parser used by Guzzle. Optimized for speed. - */ -class MessageParser extends AbstractMessageParser -{ - public function parseRequest($message) - { - if (!$message) { - return false; - } - - $parts = $this->parseMessage($message); - - // Parse the protocol and protocol version - if (isset($parts['start_line'][2])) { - $startParts = explode('/', $parts['start_line'][2]); - $protocol = strtoupper($startParts[0]); - $version = isset($startParts[1]) ? $startParts[1] : '1.1'; - } else { - $protocol = 'HTTP'; - $version = '1.1'; - } - - $parsed = array( - 'method' => strtoupper($parts['start_line'][0]), - 'protocol' => $protocol, - 'version' => $version, - 'headers' => $parts['headers'], - 'body' => $parts['body'] - ); - - $parsed['request_url'] = $this->getUrlPartsFromMessage($parts['start_line'][1], $parsed); - - return $parsed; - } - - public function parseResponse($message) - { - if (!$message) { - return false; - } - - $parts = $this->parseMessage($message); - list($protocol, $version) = explode('/', trim($parts['start_line'][0])); - - return array( - 'protocol' => $protocol, - 'version' => $version, - 'code' => $parts['start_line'][1], - 'reason_phrase' => isset($parts['start_line'][2]) ? $parts['start_line'][2] : '', - 'headers' => $parts['headers'], - 'body' => $parts['body'] - ); - } - - /** - * Parse a message into parts - * - * @param string $message Message to parse - * - * @return array - */ - protected function parseMessage($message) - { - $startLine = null; - $headers = array(); - $body = ''; - - // Iterate over each line in the message, accounting for line endings - $lines = preg_split('/(\\r?\\n)/', $message, -1, PREG_SPLIT_DELIM_CAPTURE); - for ($i = 0, $totalLines = count($lines); $i < $totalLines; $i += 2) { - - $line = $lines[$i]; - - // If two line breaks were encountered, then this is the end of body - if (empty($line)) { - if ($i < $totalLines - 1) { - $body = implode('', array_slice($lines, $i + 2)); - } - break; - } - - // Parse message headers - if (!$startLine) { - $startLine = explode(' ', $line, 3); - } elseif (strpos($line, ':')) { - $parts = explode(':', $line, 2); - $key = trim($parts[0]); - $value = isset($parts[1]) ? trim($parts[1]) : ''; - if (!isset($headers[$key])) { - $headers[$key] = $value; - } elseif (!is_array($headers[$key])) { - $headers[$key] = array($headers[$key], $value); - } else { - $headers[$key][] = $value; - } - } - } - - return array( - 'start_line' => $startLine, - 'headers' => $headers, - 'body' => $body - ); - } -} diff --git a/core/vendor/guzzle/parser/Guzzle/Parser/Message/MessageParserInterface.php b/core/vendor/guzzle/parser/Guzzle/Parser/Message/MessageParserInterface.php deleted file mode 100644 index cc448088dbd116f0485f48931e29837b3635c9f3..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/parser/Guzzle/Parser/Message/MessageParserInterface.php +++ /dev/null @@ -1,27 +0,0 @@ -<?php - -namespace Guzzle\Parser\Message; - -/** - * HTTP message parser interface used to parse HTTP messages into an array - */ -interface MessageParserInterface -{ - /** - * Parse an HTTP request message into an associative array of parts. - * - * @param string $message HTTP request to parse - * - * @return array|bool Returns false if the message is invalid - */ - public function parseRequest($message); - - /** - * Parse an HTTP response message into an associative array of parts. - * - * @param string $message HTTP response to parse - * - * @return array|bool Returns false if the message is invalid - */ - public function parseResponse($message); -} diff --git a/core/vendor/guzzle/parser/Guzzle/Parser/Message/PeclHttpMessageParser.php b/core/vendor/guzzle/parser/Guzzle/Parser/Message/PeclHttpMessageParser.php deleted file mode 100644 index 944aaa25828006ad2b627040c36817b1f4b147e3..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/parser/Guzzle/Parser/Message/PeclHttpMessageParser.php +++ /dev/null @@ -1,48 +0,0 @@ -<?php - -namespace Guzzle\Parser\Message; - -/** - * Pecl HTTP message parser - */ -class PeclHttpMessageParser extends AbstractMessageParser -{ - public function parseRequest($message) - { - if (!$message) { - return false; - } - - $parts = http_parse_message($message); - - $parsed = array( - 'method' => $parts->requestMethod, - 'protocol' => 'HTTP', - 'version' => number_format($parts->httpVersion, 1), - 'headers' => $parts->headers, - 'body' => $parts->body - ); - - $parsed['request_url'] = $this->getUrlPartsFromMessage($parts->requestUrl, $parsed); - - return $parsed; - } - - public function parseResponse($message) - { - if (!$message) { - return false; - } - - $parts = http_parse_message($message); - - return array( - 'protocol' => 'HTTP', - 'version' => number_format($parts->httpVersion, 1), - 'code' => $parts->responseCode, - 'reason_phrase' => $parts->responseStatus, - 'headers' => $parts->headers, - 'body' => $parts->body - ); - } -} diff --git a/core/vendor/guzzle/parser/Guzzle/Parser/ParserRegistry.php b/core/vendor/guzzle/parser/Guzzle/Parser/ParserRegistry.php deleted file mode 100644 index f8386831c22c8448bfb7d023b00d210fe06e2029..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/parser/Guzzle/Parser/ParserRegistry.php +++ /dev/null @@ -1,75 +0,0 @@ -<?php - -namespace Guzzle\Parser; - -/** - * Registry of parsers used by the application - */ -class ParserRegistry -{ - /** @var ParserRegistry Singleton instance */ - protected static $instance; - - /** @var array Array of parser instances */ - protected $instances = array(); - - /** @var array Mapping of parser name to default class */ - protected $mapping = array( - 'message' => 'Guzzle\\Parser\\Message\\MessageParser', - 'cookie' => 'Guzzle\\Parser\\Cookie\\CookieParser', - 'url' => 'Guzzle\\Parser\\Url\\UrlParser', - 'uri_template' => 'Guzzle\\Parser\\UriTemplate\\UriTemplate', - ); - - /** - * @return self - * @codeCoverageIgnore - */ - public static function getInstance() - { - if (!self::$instance) { - self::$instance = new static; - } - - return self::$instance; - } - - public function __construct() - { - // Use the PECL URI template parser if available - if (extension_loaded('uri_template')) { - $this->mapping['uri_template'] = 'Guzzle\\Parser\\UriTemplate\\PeclUriTemplate'; - } - } - - /** - * Get a parser by name from an instance - * - * @param string $name Name of the parser to retrieve - * - * @return mixed|null - */ - public function getParser($name) - { - if (!isset($this->instances[$name])) { - if (!isset($this->mapping[$name])) { - return null; - } - $class = $this->mapping[$name]; - $this->instances[$name] = new $class(); - } - - return $this->instances[$name]; - } - - /** - * Register a custom parser by name with the register - * - * @param string $name Name or handle of the parser to register - * @param mixed $parser Instantiated parser to register - */ - public function registerParser($name, $parser) - { - $this->instances[$name] = $parser; - } -} diff --git a/core/vendor/guzzle/parser/Guzzle/Parser/UriTemplate/PeclUriTemplate.php b/core/vendor/guzzle/parser/Guzzle/Parser/UriTemplate/PeclUriTemplate.php deleted file mode 100644 index b0764e8377c4f1a9348fc925a8260a7963f41489..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/parser/Guzzle/Parser/UriTemplate/PeclUriTemplate.php +++ /dev/null @@ -1,26 +0,0 @@ -<?php - -namespace Guzzle\Parser\UriTemplate; - -use Guzzle\Common\Exception\RuntimeException; - -/** - * Expands URI templates using the uri_template pecl extension (pecl install uri_template-beta) - * - * @link http://pecl.php.net/package/uri_template - * @link https://github.com/ioseb/uri-template - */ -class PeclUriTemplate implements UriTemplateInterface -{ - public function __construct() - { - if (!extension_loaded('uri_template')) { - throw new RuntimeException('uri_template PECL extension must be installed to use PeclUriTemplate'); - } - } - - public function expand($template, array $variables) - { - return uri_template($template, $variables); - } -} diff --git a/core/vendor/guzzle/parser/Guzzle/Parser/UriTemplate/UriTemplateInterface.php b/core/vendor/guzzle/parser/Guzzle/Parser/UriTemplate/UriTemplateInterface.php deleted file mode 100644 index c81d51548e8e1035e2cd8bb52606a7cec19eab2c..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/parser/Guzzle/Parser/UriTemplate/UriTemplateInterface.php +++ /dev/null @@ -1,21 +0,0 @@ -<?php - -namespace Guzzle\Parser\UriTemplate; - -/** - * Expands URI templates using an array of variables - * - * @link http://tools.ietf.org/html/rfc6570 - */ -interface UriTemplateInterface -{ - /** - * Expand the URI template using the supplied variables - * - * @param string $template URI Template to expand - * @param array $variables Variables to use with the expansion - * - * @return string Returns the expanded template - */ - public function expand($template, array $variables); -} diff --git a/core/vendor/guzzle/parser/Guzzle/Parser/Url/UrlParser.php b/core/vendor/guzzle/parser/Guzzle/Parser/Url/UrlParser.php deleted file mode 100644 index c4cc896088e0beafad9584581385bc2e7f0fbca7..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/parser/Guzzle/Parser/Url/UrlParser.php +++ /dev/null @@ -1,48 +0,0 @@ -<?php - -namespace Guzzle\Parser\Url; - -use Guzzle\Common\Version; - -/** - * Parses URLs into parts using PHP's built-in parse_url() function - * @deprecated Just use parse_url. UTF-8 characters should be percent encoded anyways. - * @codeCoverageIgnore - */ -class UrlParser implements UrlParserInterface -{ - /** @var bool Whether or not to work with UTF-8 strings */ - protected $utf8 = false; - - /** - * Set whether or not to attempt to handle UTF-8 strings (still WIP) - * - * @param bool $utf8 Set to TRUE to handle UTF string - */ - public function setUtf8Support($utf8) - { - $this->utf8 = $utf8; - } - - public function parseUrl($url) - { - Version::warn(__CLASS__ . ' is deprecated. Just use parse_url()'); - - static $defaults = array('scheme' => null, 'host' => null, 'path' => null, 'port' => null, 'query' => null, - 'user' => null, 'pass' => null, 'fragment' => null); - - $parts = parse_url($url); - - // Need to handle query parsing specially for UTF-8 requirements - if ($this->utf8 && isset($parts['query'])) { - $queryPos = strpos($url, '?'); - if (isset($parts['fragment'])) { - $parts['query'] = substr($url, $queryPos + 1, strpos($url, '#') - $queryPos - 1); - } else { - $parts['query'] = substr($url, $queryPos + 1); - } - } - - return $parts + $defaults; - } -} diff --git a/core/vendor/guzzle/parser/Guzzle/Parser/Url/UrlParserInterface.php b/core/vendor/guzzle/parser/Guzzle/Parser/Url/UrlParserInterface.php deleted file mode 100644 index 89ac4b30771a4689e70647dfa8ccb09898035caf..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/parser/Guzzle/Parser/Url/UrlParserInterface.php +++ /dev/null @@ -1,19 +0,0 @@ -<?php - -namespace Guzzle\Parser\Url; - -/** - * URL parser interface - */ -interface UrlParserInterface -{ - /** - * Parse a URL using special handling for a subset of UTF-8 characters in the query string if needed. - * - * @param string $url URL to parse - * - * @return array Returns an array identical to what is returned from parse_url(). When an array key is missing from - * this array, you must fill it in with NULL to avoid warnings in calling code. - */ - public function parseUrl($url); -} diff --git a/core/vendor/guzzle/parser/Guzzle/Parser/composer.json b/core/vendor/guzzle/parser/Guzzle/Parser/composer.json deleted file mode 100644 index 378b281971aea253215eee3ed733dab84025f362..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/parser/Guzzle/Parser/composer.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "guzzle/parser", - "homepage": "http://guzzlephp.org/", - "description": "Interchangeable parsers used by Guzzle", - "keywords": ["HTTP", "message", "cookie", "URL", "URI Template"], - "license": "MIT", - "require": { - "php": ">=5.3.2" - }, - "autoload": { - "psr-0": { "Guzzle\\Parser": "" } - }, - "target-dir": "Guzzle/Parser", - "extra": { - "branch-alias": { - "dev-master": "3.7-dev" - } - } -} diff --git a/core/vendor/guzzle/stream/Guzzle/Stream/PhpStreamRequestFactory.php b/core/vendor/guzzle/stream/Guzzle/Stream/PhpStreamRequestFactory.php deleted file mode 100644 index 91461c6498e80708d9c3bda2c37ab43e092e9334..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/stream/Guzzle/Stream/PhpStreamRequestFactory.php +++ /dev/null @@ -1,270 +0,0 @@ -<?php - -namespace Guzzle\Stream; - -use Guzzle\Common\Exception\InvalidArgumentException; -use Guzzle\Common\Exception\RuntimeException; -use Guzzle\Http\Message\EntityEnclosingRequestInterface; -use Guzzle\Http\Message\RequestInterface; -use Guzzle\Http\Url; - -/** - * Factory used to create fopen streams using PHP's http and https stream wrappers - * - * Note: PHP's http stream wrapper only supports streaming downloads. It does not support streaming uploads. - */ -class PhpStreamRequestFactory implements StreamRequestFactoryInterface -{ - /** @var resource Stream context options */ - protected $context; - - /** @var array Stream context */ - protected $contextOptions; - - /** @var Url Stream URL */ - protected $url; - - /** @var array Last response headers received by the HTTP request */ - protected $lastResponseHeaders; - - /** - * {@inheritdoc} - * - * The $params array can contain the following custom keys specific to the PhpStreamRequestFactory: - * - stream_class: The name of a class to create instead of a Guzzle\Stream\Stream object - */ - public function fromRequest(RequestInterface $request, $context = array(), array $params = array()) - { - if (is_resource($context)) { - $this->contextOptions = stream_context_get_options($context); - $this->context = $context; - } elseif (is_array($context) || !$context) { - $this->contextOptions = $context; - $this->createContext($params); - } elseif ($context) { - throw new InvalidArgumentException('$context must be an array or resource'); - } - - // Dispatch the before send event - $request->dispatch('request.before_send', array( - 'request' => $request, - 'context' => $this->context, - 'context_options' => $this->contextOptions - )); - - $this->setUrl($request); - $this->addDefaultContextOptions($request); - $this->addSslOptions($request); - $this->addBodyOptions($request); - $this->addProxyOptions($request); - - // Create the file handle but silence errors - return $this->createStream($params) - ->setCustomData('request', $request) - ->setCustomData('response_headers', $this->getLastResponseHeaders()); - } - - /** - * Set an option on the context and the internal options array - * - * @param string $wrapper Stream wrapper name of http - * @param string $name Context name - * @param mixed $value Context value - * @param bool $overwrite Set to true to overwrite an existing value - */ - protected function setContextValue($wrapper, $name, $value, $overwrite = false) - { - if (!isset($this->contextOptions[$wrapper])) { - $this->contextOptions[$wrapper] = array($name => $value); - } elseif (!$overwrite && isset($this->contextOptions[$wrapper][$name])) { - return; - } - $this->contextOptions[$wrapper][$name] = $value; - stream_context_set_option($this->context, $wrapper, $name, $value); - } - - /** - * Create a stream context - * - * @param array $params Parameter array - */ - protected function createContext(array $params) - { - $options = $this->contextOptions; - $this->context = $this->createResource(function () use ($params, $options) { - return stream_context_create($options, $params); - }); - } - - /** - * Get the last response headers received by the HTTP request - * - * @return array - */ - public function getLastResponseHeaders() - { - return $this->lastResponseHeaders; - } - - /** - * Adds the default context options to the stream context options - * - * @param RequestInterface $request Request - */ - protected function addDefaultContextOptions(RequestInterface $request) - { - $this->setContextValue('http', 'method', $request->getMethod()); - $this->setContextValue('http', 'header', $request->getHeaderLines()); - // Force 1.0 for now until PHP fully support chunked transfer-encoding decoding - $this->setContextValue('http', 'protocol_version', '1.0'); - $this->setContextValue('http', 'ignore_errors', true); - } - - /** - * Set the URL to use with the factory - * - * @param RequestInterface $request Request that owns the URL - */ - protected function setUrl(RequestInterface $request) - { - $this->url = $request->getUrl(true); - - // Check for basic Auth username - if ($request->getUsername()) { - $this->url->setUsername($request->getUsername()); - } - - // Check for basic Auth password - if ($request->getPassword()) { - $this->url->setPassword($request->getPassword()); - } - } - - /** - * Add SSL options to the stream context - * - * @param RequestInterface $request Request - */ - protected function addSslOptions(RequestInterface $request) - { - if ($verify = $request->getCurlOptions()->get(CURLOPT_SSL_VERIFYPEER)) { - $this->setContextValue('ssl', 'verify_peer', true, true); - if ($cafile = $request->getCurlOptions()->get(CURLOPT_CAINFO)) { - $this->setContextValue('ssl', 'cafile', $cafile, true); - } - } else { - $this->setContextValue('ssl', 'verify_peer', false, true); - } - } - - /** - * Add body (content) specific options to the context options - * - * @param RequestInterface $request - */ - protected function addBodyOptions(RequestInterface $request) - { - // Add the content for the request if needed - if (!($request instanceof EntityEnclosingRequestInterface)) { - return; - } - - if (count($request->getPostFields())) { - $this->setContextValue('http', 'content', (string) $request->getPostFields(), true); - } elseif ($request->getBody()) { - $this->setContextValue('http', 'content', (string) $request->getBody(), true); - } - - // Always ensure a content-length header is sent - if (isset($this->contextOptions['http']['content'])) { - $headers = isset($this->contextOptions['http']['header']) ? $this->contextOptions['http']['header'] : array(); - $headers[] = 'Content-Length: ' . strlen($this->contextOptions['http']['content']); - $this->setContextValue('http', 'header', $headers, true); - } - } - - /** - * Add proxy parameters to the context if needed - * - * @param RequestInterface $request Request - */ - protected function addProxyOptions(RequestInterface $request) - { - if ($proxy = $request->getCurlOptions()->get(CURLOPT_PROXY)) { - $this->setContextValue('http', 'proxy', $proxy); - } - } - - /** - * Create the stream for the request with the context options - * - * @param array $params Parameters of the stream - * - * @return StreamInterface - */ - protected function createStream(array $params) - { - $http_response_header = null; - $url = $this->url; - $context = $this->context; - $fp = $this->createResource(function () use ($context, $url, &$http_response_header) { - return fopen((string) $url, 'r', false, $context); - }); - - // Determine the class to instantiate - $className = isset($params['stream_class']) ? $params['stream_class'] : __NAMESPACE__ . '\\Stream'; - - /** @var $stream StreamInterface */ - $stream = new $className($fp); - - // Track the response headers of the request - if (isset($http_response_header)) { - $this->lastResponseHeaders = $http_response_header; - $this->processResponseHeaders($stream); - } - - return $stream; - } - - /** - * Process response headers - * - * @param StreamInterface $stream - */ - protected function processResponseHeaders(StreamInterface $stream) - { - // Set the size on the stream if it was returned in the response - foreach ($this->lastResponseHeaders as $header) { - if (($pos = stripos($header, 'Content-Length:')) === 0) { - $stream->setSize(trim(substr($header, 15))); - } - } - } - - /** - * Create a resource and check to ensure it was created successfully - * - * @param callable $callback Closure to invoke that must return a valid resource - * - * @return resource - * @throws RuntimeException on error - */ - protected function createResource($callback) - { - // Turn off error reporting while we try to initiate the request - $level = error_reporting(0); - $resource = call_user_func($callback); - error_reporting($level); - - // If the resource could not be created, then grab the last error and throw an exception - if (false === $resource) { - $message = 'Error creating resource. '; - foreach (error_get_last() as $key => $value) { - $message .= "[{$key}] {$value} "; - } - throw new RuntimeException(trim($message)); - } - - return $resource; - } -} diff --git a/core/vendor/guzzle/stream/Guzzle/Stream/Stream.php b/core/vendor/guzzle/stream/Guzzle/Stream/Stream.php deleted file mode 100644 index 299f3b39a66505e92066f2e41b5729bd890e9906..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/stream/Guzzle/Stream/Stream.php +++ /dev/null @@ -1,293 +0,0 @@ -<?php - -namespace Guzzle\Stream; - -use Guzzle\Common\Exception\InvalidArgumentException; - -/** - * PHP stream implementation - */ -class Stream implements StreamInterface -{ - const STREAM_TYPE = 'stream_type'; - const WRAPPER_TYPE = 'wrapper_type'; - const IS_LOCAL = 'is_local'; - const IS_READABLE = 'is_readable'; - const IS_WRITABLE = 'is_writable'; - const SEEKABLE = 'seekable'; - - /** @var resource Stream resource */ - protected $stream; - - /** @var int Size of the stream contents in bytes */ - protected $size; - - /** @var array Stream cached data */ - protected $cache = array(); - - /** @var array Custom stream data */ - protected $customData = array(); - - /** @var array Hash table of readable and writeable stream types for fast lookups */ - protected static $readWriteHash = array( - 'read' => array( - 'r' => true, 'w+' => true, 'r+' => true, 'x+' => true, 'c+' => true, - 'rb' => true, 'w+b' => true, 'r+b' => true, 'x+b' => true, 'c+b' => true, - 'rt' => true, 'w+t' => true, 'r+t' => true, 'x+t' => true, 'c+t' => true, 'a+' => true - ), - 'write' => array( - 'w' => true, 'w+' => true, 'rw' => true, 'r+' => true, 'x+' => true, 'c+' => true, - 'wb' => true, 'w+b' => true, 'r+b' => true, 'x+b' => true, 'c+b' => true, - 'w+t' => true, 'r+t' => true, 'x+t' => true, 'c+t' => true, 'a' => true, 'a+' => true - ) - ); - - /** - * @param resource $stream Stream resource to wrap - * @param int $size Size of the stream in bytes. Only pass if the size cannot be obtained from the stream. - * - * @throws InvalidArgumentException if the stream is not a stream resource - */ - public function __construct($stream, $size = null) - { - $this->setStream($stream, $size); - } - - /** - * Closes the stream when the helper is destructed - */ - public function __destruct() - { - $this->close(); - } - - public function __toString() - { - if (!$this->isReadable() || (!$this->isSeekable() && $this->isConsumed())) { - return ''; - } - - $originalPos = $this->ftell(); - $body = stream_get_contents($this->stream, -1, 0); - $this->seek($originalPos); - - return $body; - } - - public function close() - { - if (is_resource($this->stream)) { - fclose($this->stream); - } - $this->cache[self::IS_READABLE] = false; - $this->cache[self::IS_WRITABLE] = false; - } - - /** - * Calculate a hash of a Stream - * - * @param StreamInterface $stream Stream to calculate the hash for - * @param string $algo Hash algorithm (e.g. md5, crc32, etc) - * @param bool $rawOutput Whether or not to use raw output - * - * @return bool|string Returns false on failure or a hash string on success - */ - public static function getHash(StreamInterface $stream, $algo, $rawOutput = false) - { - $pos = $stream->ftell(); - if (!$stream->seek(0)) { - return false; - } - - $ctx = hash_init($algo); - while ($data = $stream->read(8192)) { - hash_update($ctx, $data); - } - - $out = hash_final($ctx, (bool) $rawOutput); - $stream->seek($pos); - - return $out; - } - - public function getMetaData($key = null) - { - $meta = stream_get_meta_data($this->stream); - - return !$key ? $meta : (array_key_exists($key, $meta) ? $meta[$key] : null); - } - - public function getStream() - { - return $this->stream; - } - - public function setStream($stream, $size = null) - { - if (!is_resource($stream)) { - throw new InvalidArgumentException('Stream must be a resource'); - } - - $this->size = $size; - $this->stream = $stream; - $this->rebuildCache(); - - return $this; - } - - public function detachStream() - { - $this->stream = null; - - return $this; - } - - public function getWrapper() - { - return $this->cache[self::WRAPPER_TYPE]; - } - - public function getWrapperData() - { - return $this->getMetaData('wrapper_data') ?: array(); - } - - public function getStreamType() - { - return $this->cache[self::STREAM_TYPE]; - } - - public function getUri() - { - return $this->cache['uri']; - } - - public function getSize() - { - if ($this->size !== null) { - return $this->size; - } - - // If the stream is a file based stream and local, then use fstat - clearstatcache(true, $this->cache['uri']); - $stats = fstat($this->stream); - if (isset($stats['size'])) { - $this->size = $stats['size']; - return $this->size; - } elseif ($this->cache[self::IS_READABLE] && $this->cache[self::SEEKABLE]) { - // Only get the size based on the content if the the stream is readable and seekable - $pos = $this->ftell(); - $this->size = strlen((string) $this); - $this->seek($pos); - return $this->size; - } - - return false; - } - - public function isReadable() - { - return $this->cache[self::IS_READABLE]; - } - - public function isRepeatable() - { - return $this->cache[self::IS_READABLE] && $this->cache[self::SEEKABLE]; - } - - public function isWritable() - { - return $this->cache[self::IS_WRITABLE]; - } - - public function isConsumed() - { - return feof($this->stream); - } - - public function feof() - { - return $this->isConsumed(); - } - - public function isLocal() - { - return $this->cache[self::IS_LOCAL]; - } - - public function isSeekable() - { - return $this->cache[self::SEEKABLE]; - } - - public function setSize($size) - { - $this->size = $size; - - return $this; - } - - public function seek($offset, $whence = SEEK_SET) - { - return $this->cache[self::SEEKABLE] ? fseek($this->stream, $offset, $whence) === 0 : false; - } - - public function read($length) - { - return $this->cache[self::IS_READABLE] ? fread($this->stream, $length) : false; - } - - public function write($string) - { - if (!$this->cache[self::IS_WRITABLE]) { - return 0; - } - - // We can't know the size after writing anything - $this->size = null; - - return fwrite($this->stream, $string); - } - - public function ftell() - { - return ftell($this->stream); - } - - public function rewind() - { - return $this->seek(0); - } - - public function readLine($maxLength = null) - { - if (!$this->cache[self::IS_READABLE]) { - return false; - } else { - return $maxLength ? fgets($this->getStream(), $maxLength) : fgets($this->getStream()); - } - } - - public function setCustomData($key, $value) - { - $this->customData[$key] = $value; - - return $this; - } - - public function getCustomData($key) - { - return isset($this->customData[$key]) ? $this->customData[$key] : null; - } - - /** - * Reprocess stream metadata - */ - protected function rebuildCache() - { - $this->cache = stream_get_meta_data($this->stream); - $this->cache[self::IS_LOCAL] = stream_is_local($this->stream); - $this->cache[self::IS_READABLE] = isset(self::$readWriteHash['read'][$this->cache['mode']]); - $this->cache[self::IS_WRITABLE] = isset(self::$readWriteHash['write'][$this->cache['mode']]); - } -} diff --git a/core/vendor/guzzle/stream/Guzzle/Stream/StreamInterface.php b/core/vendor/guzzle/stream/Guzzle/Stream/StreamInterface.php deleted file mode 100644 index 6d7dc37613c0dab97ddde1955d1fac58687f3b49..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/stream/Guzzle/Stream/StreamInterface.php +++ /dev/null @@ -1,218 +0,0 @@ -<?php - -namespace Guzzle\Stream; - -/** - * OO interface to PHP streams - */ -interface StreamInterface -{ - /** - * Convert the stream to a string if the stream is readable and the stream is seekable. - * - * @return string - */ - public function __toString(); - - /** - * Close the underlying stream - */ - public function close(); - - /** - * Get stream metadata - * - * @param string $key Specific metadata to retrieve - * - * @return array|mixed|null - */ - public function getMetaData($key = null); - - /** - * Get the stream resource - * - * @return resource - */ - public function getStream(); - - /** - * Set the stream that is wrapped by the object - * - * @param resource $stream Stream resource to wrap - * @param int $size Size of the stream in bytes. Only pass if the size cannot be obtained from the stream. - * - * @return self - */ - public function setStream($stream, $size = null); - - /** - * Detach the current stream resource - * - * @return self - */ - public function detachStream(); - - /** - * Get the stream wrapper type - * - * @return string - */ - public function getWrapper(); - - /** - * Wrapper specific data attached to this stream. - * - * @return array - */ - public function getWrapperData(); - - /** - * Get a label describing the underlying implementation of the stream - * - * @return string - */ - public function getStreamType(); - - /** - * Get the URI/filename associated with this stream - * - * @return string - */ - public function getUri(); - - /** - * Get the size of the stream if able - * - * @return int|bool - */ - public function getSize(); - - /** - * Check if the stream is readable - * - * @return bool - */ - public function isReadable(); - - /** - * Check if the stream is repeatable - * - * @return bool - */ - public function isRepeatable(); - - /** - * Check if the stream is writable - * - * @return bool - */ - public function isWritable(); - - /** - * Check if the stream has been consumed - * - * @return bool - */ - public function isConsumed(); - - /** - * Alias of isConsumed - * - * @return bool - */ - public function feof(); - - /** - * Check if the stream is a local stream vs a remote stream - * - * @return bool - */ - public function isLocal(); - - /** - * Check if the string is repeatable - * - * @return bool - */ - public function isSeekable(); - - /** - * Specify the size of the stream in bytes - * - * @param int $size Size of the stream contents in bytes - * - * @return self - */ - public function setSize($size); - - /** - * Seek to a position in the stream - * - * @param int $offset Stream offset - * @param int $whence Where the offset is applied - * - * @return bool Returns TRUE on success or FALSE on failure - * @link http://www.php.net/manual/en/function.fseek.php - */ - public function seek($offset, $whence = SEEK_SET); - - /** - * Read data from the stream - * - * @param int $length Up to length number of bytes read. - * - * @return string|bool Returns the data read from the stream or FALSE on failure or EOF - */ - public function read($length); - - /** - * Write data to the stream - * - * @param string $string The string that is to be written. - * - * @return int|bool Returns the number of bytes written to the stream on success or FALSE on failure. - */ - public function write($string); - - /** - * Returns the current position of the file read/write pointer - * - * @return int|bool Returns the position of the file pointer or false on error - */ - public function ftell(); - - /** - * Rewind to the beginning of the stream - * - * @return bool Returns true on success or false on failure - */ - public function rewind(); - - /** - * Read a line from the stream up to the maximum allowed buffer length - * - * @param int $maxLength Maximum buffer length - * - * @return string|bool - */ - public function readLine($maxLength = null); - - /** - * Set custom data on the stream - * - * @param string $key Key to set - * @param mixed $value Value to set - * - * @return self - */ - public function setCustomData($key, $value); - - /** - * Get custom data from the stream - * - * @param string $key Key to retrieve - * - * @return null|mixed - */ - public function getCustomData($key); -} diff --git a/core/vendor/guzzle/stream/Guzzle/Stream/StreamRequestFactoryInterface.php b/core/vendor/guzzle/stream/Guzzle/Stream/StreamRequestFactoryInterface.php deleted file mode 100644 index d00e622e1d4192eaf80f07c931e5677f299ad7d1..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/stream/Guzzle/Stream/StreamRequestFactoryInterface.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php - -namespace Guzzle\Stream; - -use Guzzle\Http\Message\RequestInterface; - -/** - * Interface used for creating streams from requests - */ -interface StreamRequestFactoryInterface -{ - /** - * Create a stream based on a request object - * - * @param RequestInterface $request Base the stream on a request - * @param array|resource $context A stream_context_options resource or array of parameters used to create a - * stream context. - * @param array $params Optional array of parameters specific to the factory - * - * @return StreamInterface Returns a stream object - * @throws \Guzzle\Common\Exception\RuntimeException if the stream cannot be opened or an error occurs - */ - public function fromRequest(RequestInterface $request, $context = array(), array $params = array()); -} diff --git a/core/vendor/guzzle/stream/Guzzle/Stream/composer.json b/core/vendor/guzzle/stream/Guzzle/Stream/composer.json deleted file mode 100644 index 9c19d2bd17add7a88f81801e8d39aaeb9022cc3f..0000000000000000000000000000000000000000 --- a/core/vendor/guzzle/stream/Guzzle/Stream/composer.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "name": "guzzle/stream", - "description": "Guzzle stream wrapper component", - "homepage": "http://guzzlephp.org/", - "keywords": ["stream", "component", "guzzle"], - "license": "MIT", - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - } - ], - "require": { - "php": ">=5.3.2", - "guzzle/common": "self.version" - }, - "suggest": { - "guzzle/http": "To convert Guzzle request objects to PHP streams" - }, - "autoload": { - "psr-0": { "Guzzle\\Stream": "" } - }, - "target-dir": "Guzzle/Stream", - "extra": { - "branch-alias": { - "dev-master": "3.7-dev" - } - } -} diff --git a/core/vendor/guzzlehttp/guzzle/.gitignore b/core/vendor/guzzlehttp/guzzle/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..f344b89b9197d01ebfa81a6431bcbe00aaf54912 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/.gitignore @@ -0,0 +1,11 @@ +phpunit.xml +composer.phar +composer.lock +composer-test.lock +vendor/ +build/ +artifacts/ +docs/_build +docs/*.pyc +.idea +.DS_STORE diff --git a/core/vendor/guzzlehttp/guzzle/.travis.yml b/core/vendor/guzzlehttp/guzzle/.travis.yml new file mode 100644 index 0000000000000000000000000000000000000000..1e683366eb9d30483b1dd5631e09473065c6bdc3 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/.travis.yml @@ -0,0 +1,22 @@ +language: php + +php: + - 5.4 + - 5.5 + - 5.6 + - hhvm + +before_script: + - curl --version + - pear config-set php_ini ~/.phpenv/versions/`php -r 'echo phpversion();'`/etc/php.ini || echo 'Error modifying PEAR' + - pecl install uri_template || echo 'Error installing uri_template' + - composer install + - ~/.nvm/nvm.sh install v0.6.14 + - ~/.nvm/nvm.sh run v0.6.14 + +script: make test + +matrix: + allow_failures: + - php: hhvm + fast_finish: true diff --git a/core/vendor/guzzlehttp/guzzle/CHANGELOG.md b/core/vendor/guzzlehttp/guzzle/CHANGELOG.md new file mode 100644 index 0000000000000000000000000000000000000000..d2fe5a49b7e2491ba2adf18c9b7c6a292e0c1352 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/CHANGELOG.md @@ -0,0 +1,805 @@ +CHANGELOG +========= + +4.0.0 (2014-03-29) +------------------ + +* For more information on the 4.0 transition, see: + http://mtdowling.com/blog/2014/03/15/guzzle-4-rc/ +* For information on changes and upgrading, see: + https://github.com/guzzle/guzzle/blob/master/UPGRADING.md#3x-to-40 +* Added `GuzzleHttp\batch()` as a convenience function for sending requests in + parallel without needing to write asynchronous code. +* Restructured how events are added to `GuzzleHttp\ClientInterface::sendAll()`. + You can now pass a callable or an array of associative arrays where each + associative array contains the "fn", "priority", and "once" keys. + +4.0.0.rc-2 (2014-03-25) +----------------------- + +* Removed `getConfig()` and `setConfig()` from clients to avoid confusion + around whether things like base_url, message_factory, etc should be able to + be retrieved or modified. +* Added `getDefaultOption()` and `setDefaultOption()` to ClientInterface +* functions.php functions were renamed using snake_case to match PHP idioms +* Added support for `HTTP_PROXY`, `HTTPS_PROXY`, and + `GUZZLE_CURL_SELECT_TIMEOUT` environment variables +* Added the ability to specify custom `sendAll()` event priorities +* Added the ability to specify custom stream context options to the stream + adapter. +* Added a functions.php function for `get_path()` and `set_path()` +* CurlAdapter and MultiAdapter now use a callable to generate curl resources +* MockAdapter now properly reads a body and emits a `headers` event +* Updated Url class to check if a scheme and host are set before adding ":" + and "//". This allows empty Url (e.g., "") to be serialized as "". +* Parsing invalid XML no longer emits warnings +* Curl classes now properly throw AdapterExceptions +* Various performance optimizations +* Streams are created with the faster `Stream\create()` function +* Marked deprecation_proxy() as internal +* Test server is now a collection of static methods on a class + +4.0.0-rc.1 (2014-03-15) +----------------------- + +* See https://github.com/guzzle/guzzle/blob/master/UPGRADING.md#3x-to-40 + +3.8.1 (2014-01-28) +------------------ + +* Bug: Always using GET requests when redirecting from a 303 response +* Bug: CURLOPT_SSL_VERIFYHOST is now correctly set to false when setting `$certificateAuthority` to false in + `Guzzle\Http\ClientInterface::setSslVerification()` +* Bug: RedirectPlugin now uses strict RFC 3986 compliance when combining a base URL with a relative URL +* Bug: The body of a request can now be set to `"0"` +* Sending PHP stream requests no longer forces `HTTP/1.0` +* Adding more information to ExceptionCollection exceptions so that users have more context, including a stack trace of + each sub-exception +* Updated the `$ref` attribute in service descriptions to merge over any existing parameters of a schema (rather than + clobbering everything). +* Merging URLs will now use the query string object from the relative URL (thus allowing custom query aggregators) +* Query strings are now parsed in a way that they do no convert empty keys with no value to have a dangling `=`. + For example `foo&bar=baz` is now correctly parsed and recognized as `foo&bar=baz` rather than `foo=&bar=baz`. +* Now properly escaping the regular expression delimiter when matching Cookie domains. +* Network access is now disabled when loading XML documents + +3.8.0 (2013-12-05) +------------------ + +* Added the ability to define a POST name for a file +* JSON response parsing now properly walks additionalProperties +* cURL error code 18 is now retried automatically in the BackoffPlugin +* Fixed a cURL error when URLs contain fragments +* Fixed an issue in the BackoffPlugin retry event where it was trying to access all exceptions as if they were + CurlExceptions +* CURLOPT_PROGRESS function fix for PHP 5.5 (69fcc1e) +* Added the ability for Guzzle to work with older versions of cURL that do not support `CURLOPT_TIMEOUT_MS` +* Fixed a bug that was encountered when parsing empty header parameters +* UriTemplate now has a `setRegex()` method to match the docs +* The `debug` request parameter now checks if it is truthy rather than if it exists +* Setting the `debug` request parameter to true shows verbose cURL output instead of using the LogPlugin +* Added the ability to combine URLs using strict RFC 3986 compliance +* Command objects can now return the validation errors encountered by the command +* Various fixes to cache revalidation (#437 and 29797e5) +* Various fixes to the AsyncPlugin +* Cleaned up build scripts + +3.7.4 (2013-10-02) +------------------ + +* Bug fix: 0 is now an allowed value in a description parameter that has a default value (#430) +* Bug fix: SchemaFormatter now returns an integer when formatting to a Unix timestamp + (see https://github.com/aws/aws-sdk-php/issues/147) +* Bug fix: Cleaned up and fixed URL dot segment removal to properly resolve internal dots +* Minimum PHP version is now properly specified as 5.3.3 (up from 5.3.2) (#420) +* Updated the bundled cacert.pem (#419) +* OauthPlugin now supports adding authentication to headers or query string (#425) + +3.7.3 (2013-09-08) +------------------ + +* Added the ability to get the exception associated with a request/command when using `MultiTransferException` and + `CommandTransferException`. +* Setting `additionalParameters` of a response to false is now honored when parsing responses with a service description +* Schemas are only injected into response models when explicitly configured. +* No longer guessing Content-Type based on the path of a request. Content-Type is now only guessed based on the path of + an EntityBody. +* Bug fix: ChunkedIterator can now properly chunk a \Traversable as well as an \Iterator. +* Bug fix: FilterIterator now relies on `\Iterator` instead of `\Traversable`. +* Bug fix: Gracefully handling malformed responses in RequestMediator::writeResponseBody() +* Bug fix: Replaced call to canCache with canCacheRequest in the CallbackCanCacheStrategy of the CachePlugin +* Bug fix: Visiting XML attributes first before visting XML children when serializing requests +* Bug fix: Properly parsing headers that contain commas contained in quotes +* Bug fix: mimetype guessing based on a filename is now case-insensitive + +3.7.2 (2013-08-02) +------------------ + +* Bug fix: Properly URL encoding paths when using the PHP-only version of the UriTemplate expander + See https://github.com/guzzle/guzzle/issues/371 +* Bug fix: Cookie domains are now matched correctly according to RFC 6265 + See https://github.com/guzzle/guzzle/issues/377 +* Bug fix: GET parameters are now used when calculating an OAuth signature +* Bug fix: Fixed an issue with cache revalidation where the If-None-Match header was being double quoted +* `Guzzle\Common\AbstractHasDispatcher::dispatch()` now returns the event that was dispatched +* `Guzzle\Http\QueryString::factory()` now guesses the most appropriate query aggregator to used based on the input. + See https://github.com/guzzle/guzzle/issues/379 +* Added a way to add custom domain objects to service description parsing using the `operation.parse_class` event. See + https://github.com/guzzle/guzzle/pull/380 +* cURL multi cleanup and optimizations + +3.7.1 (2013-07-05) +------------------ + +* Bug fix: Setting default options on a client now works +* Bug fix: Setting options on HEAD requests now works. See #352 +* Bug fix: Moving stream factory before send event to before building the stream. See #353 +* Bug fix: Cookies no longer match on IP addresses per RFC 6265 +* Bug fix: Correctly parsing header parameters that are in `<>` and quotes +* Added `cert` and `ssl_key` as request options +* `Host` header can now diverge from the host part of a URL if the header is set manually +* `Guzzle\Service\Command\LocationVisitor\Request\XmlVisitor` was rewritten to change from using SimpleXML to XMLWriter +* OAuth parameters are only added via the plugin if they aren't already set +* Exceptions are now thrown when a URL cannot be parsed +* Returning `false` if `Guzzle\Http\EntityBody::getContentMd5()` fails +* Not setting a `Content-MD5` on a command if calculating the Content-MD5 fails via the CommandContentMd5Plugin + +3.7.0 (2013-06-10) +------------------ + +* See UPGRADING.md for more information on how to upgrade. +* Requests now support the ability to specify an array of $options when creating a request to more easily modify a + request. You can pass a 'request.options' configuration setting to a client to apply default request options to + every request created by a client (e.g. default query string variables, headers, curl options, etc). +* Added a static facade class that allows you to use Guzzle with static methods and mount the class to `\Guzzle`. + See `Guzzle\Http\StaticClient::mount`. +* Added `command.request_options` to `Guzzle\Service\Command\AbstractCommand` to pass request options to requests + created by a command (e.g. custom headers, query string variables, timeout settings, etc). +* Stream size in `Guzzle\Stream\PhpStreamRequestFactory` will now be set if Content-Length is returned in the + headers of a response +* Added `Guzzle\Common\Collection::setPath($path, $value)` to set a value into an array using a nested key + (e.g. `$collection->setPath('foo/baz/bar', 'test'); echo $collection['foo']['bar']['bar'];`) +* ServiceBuilders now support storing and retrieving arbitrary data +* CachePlugin can now purge all resources for a given URI +* CachePlugin can automatically purge matching cached items when a non-idempotent request is sent to a resource +* CachePlugin now uses the Vary header to determine if a resource is a cache hit +* `Guzzle\Http\Message\Response` now implements `\Serializable` +* Added `Guzzle\Cache\CacheAdapterFactory::fromCache()` to more easily create cache adapters +* `Guzzle\Service\ClientInterface::execute()` now accepts an array, single command, or Traversable +* Fixed a bug in `Guzzle\Http\Message\Header\Link::addLink()` +* Better handling of calculating the size of a stream in `Guzzle\Stream\Stream` using fstat() and caching the size +* `Guzzle\Common\Exception\ExceptionCollection` now creates a more readable exception message +* Fixing BC break: Added back the MonologLogAdapter implementation rather than extending from PsrLog so that older + Symfony users can still use the old version of Monolog. +* Fixing BC break: Added the implementation back in for `Guzzle\Http\Message\AbstractMessage::getTokenizedHeader()`. + Now triggering an E_USER_DEPRECATED warning when used. Use `$message->getHeader()->parseParams()`. +* Several performance improvements to `Guzzle\Common\Collection` +* Added an `$options` argument to the end of the following methods of `Guzzle\Http\ClientInterface`: + createRequest, head, delete, put, patch, post, options, prepareRequest +* Added an `$options` argument to the end of `Guzzle\Http\Message\Request\RequestFactoryInterface::createRequest()` +* Added an `applyOptions()` method to `Guzzle\Http\Message\Request\RequestFactoryInterface` +* Changed `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $body = null)` to + `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $options = array())`. You can still pass in a + resource, string, or EntityBody into the $options parameter to specify the download location of the response. +* Changed `Guzzle\Common\Collection::__construct($data)` to no longer accepts a null value for `$data` but a + default `array()` +* Added `Guzzle\Stream\StreamInterface::isRepeatable` +* Removed `Guzzle\Http\ClientInterface::setDefaultHeaders(). Use + $client->getConfig()->setPath('request.options/headers/{header_name}', 'value')`. or + $client->getConfig()->setPath('request.options/headers', array('header_name' => 'value'))`. +* Removed `Guzzle\Http\ClientInterface::getDefaultHeaders(). Use $client->getConfig()->getPath('request.options/headers')`. +* Removed `Guzzle\Http\ClientInterface::expandTemplate()` +* Removed `Guzzle\Http\ClientInterface::setRequestFactory()` +* Removed `Guzzle\Http\ClientInterface::getCurlMulti()` +* Removed `Guzzle\Http\Message\RequestInterface::canCache` +* Removed `Guzzle\Http\Message\RequestInterface::setIsRedirect` +* Removed `Guzzle\Http\Message\RequestInterface::isRedirect` +* Made `Guzzle\Http\Client::expandTemplate` and `getUriTemplate` protected methods. +* You can now enable E_USER_DEPRECATED warnings to see if you are using a deprecated method by setting + `Guzzle\Common\Version::$emitWarnings` to true. +* Marked `Guzzle\Http\Message\Request::isResponseBodyRepeatable()` as deprecated. Use + `$request->getResponseBody()->isRepeatable()` instead. +* Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use + `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead. +* Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use + `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead. +* Marked `Guzzle\Http\Message\Request::setIsRedirect()` as deprecated. Use the HistoryPlugin instead. +* Marked `Guzzle\Http\Message\Request::isRedirect()` as deprecated. Use the HistoryPlugin instead. +* Marked `Guzzle\Cache\CacheAdapterFactory::factory()` as deprecated +* Marked 'command.headers', 'command.response_body' and 'command.on_complete' as deprecated for AbstractCommand. + These will work through Guzzle 4.0 +* Marked 'request.params' for `Guzzle\Http\Client` as deprecated. Use [request.options][params]. +* Marked `Guzzle\Service\Client::enableMagicMethods()` as deprecated. Magic methods can no longer be disabled on a Guzzle\Service\Client. +* Marked `Guzzle\Service\Client::getDefaultHeaders()` as deprecated. Use $client->getConfig()->getPath('request.options/headers')`. +* Marked `Guzzle\Service\Client::setDefaultHeaders()` as deprecated. Use $client->getConfig()->setPath('request.options/headers/{header_name}', 'value')`. +* Marked `Guzzle\Parser\Url\UrlParser` as deprecated. Just use PHP's `parse_url()` and percent encode your UTF-8. +* Marked `Guzzle\Common\Collection::inject()` as deprecated. +* Marked `Guzzle\Plugin\CurlAuth\CurlAuthPlugin` as deprecated. Use `$client->getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest');` +* CacheKeyProviderInterface and DefaultCacheKeyProvider are no longer used. All of this logic is handled in a + CacheStorageInterface. These two objects and interface will be removed in a future version. +* Always setting X-cache headers on cached responses +* Default cache TTLs are now handled by the CacheStorageInterface of a CachePlugin +* `CacheStorageInterface::cache($key, Response $response, $ttl = null)` has changed to `cache(RequestInterface + $request, Response $response);` +* `CacheStorageInterface::fetch($key)` has changed to `fetch(RequestInterface $request);` +* `CacheStorageInterface::delete($key)` has changed to `delete(RequestInterface $request);` +* Added `CacheStorageInterface::purge($url)` +* `DefaultRevalidation::__construct(CacheKeyProviderInterface $cacheKey, CacheStorageInterface $cache, CachePlugin + $plugin)` has changed to `DefaultRevalidation::__construct(CacheStorageInterface $cache, + CanCacheStrategyInterface $canCache = null)` +* Added `RevalidationInterface::shouldRevalidate(RequestInterface $request, Response $response)` + +3.6.0 (2013-05-29) +------------------ + +* ServiceDescription now implements ToArrayInterface +* Added command.hidden_params to blacklist certain headers from being treated as additionalParameters +* Guzzle can now correctly parse incomplete URLs +* Mixed casing of headers are now forced to be a single consistent casing across all values for that header. +* Messages internally use a HeaderCollection object to delegate handling case-insensitive header resolution +* Removed the whole changedHeader() function system of messages because all header changes now go through addHeader(). +* Specific header implementations can be created for complex headers. When a message creates a header, it uses a + HeaderFactory which can map specific headers to specific header classes. There is now a Link header and + CacheControl header implementation. +* Removed from interface: Guzzle\Http\ClientInterface::setUriTemplate +* Removed from interface: Guzzle\Http\ClientInterface::setCurlMulti() +* Removed Guzzle\Http\Message\Request::receivedRequestHeader() and implemented this functionality in + Guzzle\Http\Curl\RequestMediator +* Removed the optional $asString parameter from MessageInterface::getHeader(). Just cast the header to a string. +* Removed the optional $tryChunkedTransfer option from Guzzle\Http\Message\EntityEnclosingRequestInterface +* Removed the $asObjects argument from Guzzle\Http\Message\MessageInterface::getHeaders() +* Removed Guzzle\Parser\ParserRegister::get(). Use getParser() +* Removed Guzzle\Parser\ParserRegister::set(). Use registerParser(). +* All response header helper functions return a string rather than mixing Header objects and strings inconsistently +* Removed cURL blacklist support. This is no longer necessary now that Expect, Accept, etc are managed by Guzzle + directly via interfaces +* Removed the injecting of a request object onto a response object. The methods to get and set a request still exist + but are a no-op until removed. +* Most classes that used to require a ``Guzzle\Service\Command\CommandInterface` typehint now request a + `Guzzle\Service\Command\ArrayCommandInterface`. +* Added `Guzzle\Http\Message\RequestInterface::startResponse()` to the RequestInterface to handle injecting a response + on a request while the request is still being transferred +* The ability to case-insensitively search for header values +* Guzzle\Http\Message\Header::hasExactHeader +* Guzzle\Http\Message\Header::raw. Use getAll() +* Deprecated cache control specific methods on Guzzle\Http\Message\AbstractMessage. Use the CacheControl header object + instead. +* `Guzzle\Service\Command\CommandInterface` now extends from ToArrayInterface and ArrayAccess +* Added the ability to cast Model objects to a string to view debug information. + +3.5.0 (2013-05-13) +------------------ + +* Bug: Fixed a regression so that request responses are parsed only once per oncomplete event rather than multiple times +* Bug: Better cleanup of one-time events accross the board (when an event is meant to fire once, it will now remove + itself from the EventDispatcher) +* Bug: `Guzzle\Log\MessageFormatter` now properly writes "total_time" and "connect_time" values +* Bug: Cloning an EntityEnclosingRequest now clones the EntityBody too +* Bug: Fixed an undefined index error when parsing nested JSON responses with a sentAs parameter that reference a + non-existent key +* Bug: All __call() method arguments are now required (helps with mocking frameworks) +* Deprecating Response::getRequest() and now using a shallow clone of a request object to remove a circular reference + to help with refcount based garbage collection of resources created by sending a request +* Deprecating ZF1 cache and log adapters. These will be removed in the next major version. +* Deprecating `Response::getPreviousResponse()` (method signature still exists, but it'sdeprecated). Use the + HistoryPlugin for a history. +* Added a `responseBody` alias for the `response_body` location +* Refactored internals to no longer rely on Response::getRequest() +* HistoryPlugin can now be cast to a string +* HistoryPlugin now logs transactions rather than requests and responses to more accurately keep track of the requests + and responses that are sent over the wire +* Added `getEffectiveUrl()` and `getRedirectCount()` to Response objects + +3.4.3 (2013-04-30) +------------------ + +* Bug fix: Fixing bug introduced in 3.4.2 where redirect responses are duplicated on the final redirected response +* Added a check to re-extract the temp cacert bundle from the phar before sending each request + +3.4.2 (2013-04-29) +------------------ + +* Bug fix: Stream objects now work correctly with "a" and "a+" modes +* Bug fix: Removing `Transfer-Encoding: chunked` header when a Content-Length is present +* Bug fix: AsyncPlugin no longer forces HEAD requests +* Bug fix: DateTime timezones are now properly handled when using the service description schema formatter +* Bug fix: CachePlugin now properly handles stale-if-error directives when a request to the origin server fails +* Setting a response on a request will write to the custom request body from the response body if one is specified +* LogPlugin now writes to php://output when STDERR is undefined +* Added the ability to set multiple POST files for the same key in a single call +* application/x-www-form-urlencoded POSTs now use the utf-8 charset by default +* Added the ability to queue CurlExceptions to the MockPlugin +* Cleaned up how manual responses are queued on requests (removed "queued_response" and now using request.before_send) +* Configuration loading now allows remote files + +3.4.1 (2013-04-16) +------------------ + +* Large refactoring to how CurlMulti handles work. There is now a proxy that sits in front of a pool of CurlMulti + handles. This greatly simplifies the implementation, fixes a couple bugs, and provides a small performance boost. +* Exceptions are now properly grouped when sending requests in parallel +* Redirects are now properly aggregated when a multi transaction fails +* Redirects now set the response on the original object even in the event of a failure +* Bug fix: Model names are now properly set even when using $refs +* Added support for PHP 5.5's CurlFile to prevent warnings with the deprecated @ syntax +* Added support for oauth_callback in OAuth signatures +* Added support for oauth_verifier in OAuth signatures +* Added support to attempt to retrieve a command first literally, then ucfirst, the with inflection + +3.4.0 (2013-04-11) +------------------ + +* Bug fix: URLs are now resolved correctly based on http://tools.ietf.org/html/rfc3986#section-5.2. #289 +* Bug fix: Absolute URLs with a path in a service description will now properly override the base URL. #289 +* Bug fix: Parsing a query string with a single PHP array value will now result in an array. #263 +* Bug fix: Better normalization of the User-Agent header to prevent duplicate headers. #264. +* Bug fix: Added `number` type to service descriptions. +* Bug fix: empty parameters are removed from an OAuth signature +* Bug fix: Revalidating a cache entry prefers the Last-Modified over the Date header +* Bug fix: Fixed "array to string" error when validating a union of types in a service description +* Bug fix: Removed code that attempted to determine the size of a stream when data is written to the stream +* Bug fix: Not including an `oauth_token` if the value is null in the OauthPlugin. +* Bug fix: Now correctly aggregating successful requests and failed requests in CurlMulti when a redirect occurs. +* The new default CURLOPT_TIMEOUT setting has been increased to 150 seconds so that Guzzle works on poor connections. +* Added a feature to EntityEnclosingRequest::setBody() that will automatically set the Content-Type of the request if + the Content-Type can be determined based on the entity body or the path of the request. +* Added the ability to overwrite configuration settings in a client when grabbing a throwaway client from a builder. +* Added support for a PSR-3 LogAdapter. +* Added a `command.after_prepare` event +* Added `oauth_callback` parameter to the OauthPlugin +* Added the ability to create a custom stream class when using a stream factory +* Added a CachingEntityBody decorator +* Added support for `additionalParameters` in service descriptions to define how custom parameters are serialized. +* The bundled SSL certificate is now provided in the phar file and extracted when running Guzzle from a phar. +* You can now send any EntityEnclosingRequest with POST fields or POST files and cURL will handle creating bodies +* POST requests using a custom entity body are now treated exactly like PUT requests but with a custom cURL method. This + means that the redirect behavior of POST requests with custom bodies will not be the same as POST requests that use + POST fields or files (the latter is only used when emulating a form POST in the browser). +* Lots of cleanup to CurlHandle::factory and RequestFactory::createRequest + +3.3.1 (2013-03-10) +------------------ + +* Added the ability to create PHP streaming responses from HTTP requests +* Bug fix: Running any filters when parsing response headers with service descriptions +* Bug fix: OauthPlugin fixes to allow for multi-dimensional array signing, and sorting parameters before signing +* Bug fix: Removed the adding of default empty arrays and false Booleans to responses in order to be consistent across + response location visitors. +* Bug fix: Removed the possibility of creating configuration files with circular dependencies +* RequestFactory::create() now uses the key of a POST file when setting the POST file name +* Added xmlAllowEmpty to serialize an XML body even if no XML specific parameters are set + +3.3.0 (2013-03-03) +------------------ + +* A large number of performance optimizations have been made +* Bug fix: Added 'wb' as a valid write mode for streams +* Bug fix: `Guzzle\Http\Message\Response::json()` now allows scalar values to be returned +* Bug fix: Fixed bug in `Guzzle\Http\Message\Response` where wrapping quotes were stripped from `getEtag()` +* BC: Removed `Guzzle\Http\Utils` class +* BC: Setting a service description on a client will no longer modify the client's command factories. +* BC: Emitting IO events from a RequestMediator is now a parameter that must be set in a request's curl options using + the 'emit_io' key. This was previously set under a request's parameters using 'curl.emit_io' +* BC: `Guzzle\Stream\Stream::getWrapper()` and `Guzzle\Stream\Stream::getSteamType()` are no longer converted to + lowercase +* Operation parameter objects are now lazy loaded internally +* Added ErrorResponsePlugin that can throw errors for responses defined in service description operations' errorResponses +* Added support for instantiating responseType=class responseClass classes. Classes must implement + `Guzzle\Service\Command\ResponseClassInterface` +* Added support for additionalProperties for top-level parameters in responseType=model responseClasses. These + additional properties also support locations and can be used to parse JSON responses where the outermost part of the + JSON is an array +* Added support for nested renaming of JSON models (rename sentAs to name) +* CachePlugin + * Added support for stale-if-error so that the CachePlugin can now serve stale content from the cache on error + * Debug headers can now added to cached response in the CachePlugin + +3.2.0 (2013-02-14) +------------------ + +* CurlMulti is no longer reused globally. A new multi object is created per-client. This helps to isolate clients. +* URLs with no path no longer contain a "/" by default +* Guzzle\Http\QueryString does no longer manages the leading "?". This is now handled in Guzzle\Http\Url. +* BadResponseException no longer includes the full request and response message +* Adding setData() to Guzzle\Service\Description\ServiceDescriptionInterface +* Adding getResponseBody() to Guzzle\Http\Message\RequestInterface +* Various updates to classes to use ServiceDescriptionInterface type hints rather than ServiceDescription +* Header values can now be normalized into distinct values when multiple headers are combined with a comma separated list +* xmlEncoding can now be customized for the XML declaration of a XML service description operation +* Guzzle\Http\QueryString now uses Guzzle\Http\QueryAggregator\QueryAggregatorInterface objects to add custom value + aggregation and no longer uses callbacks +* The URL encoding implementation of Guzzle\Http\QueryString can now be customized +* Bug fix: Filters were not always invoked for array service description parameters +* Bug fix: Redirects now use a target response body rather than a temporary response body +* Bug fix: The default exponential backoff BackoffPlugin was not giving when the request threshold was exceeded +* Bug fix: Guzzle now takes the first found value when grabbing Cache-Control directives + +3.1.2 (2013-01-27) +------------------ + +* Refactored how operation responses are parsed. Visitors now include a before() method responsible for parsing the + response body. For example, the XmlVisitor now parses the XML response into an array in the before() method. +* Fixed an issue where cURL would not automatically decompress responses when the Accept-Encoding header was sent +* CURLOPT_SSL_VERIFYHOST is never set to 1 because it is deprecated (see 5e0ff2ef20f839e19d1eeb298f90ba3598784444) +* Fixed a bug where redirect responses were not chained correctly using getPreviousResponse() +* Setting default headers on a client after setting the user-agent will not erase the user-agent setting + +3.1.1 (2013-01-20) +------------------ + +* Adding wildcard support to Guzzle\Common\Collection::getPath() +* Adding alias support to ServiceBuilder configs +* Adding Guzzle\Service\Resource\CompositeResourceIteratorFactory and cleaning up factory interface + +3.1.0 (2013-01-12) +------------------ + +* BC: CurlException now extends from RequestException rather than BadResponseException +* BC: Renamed Guzzle\Plugin\Cache\CanCacheStrategyInterface::canCache() to canCacheRequest() and added CanCacheResponse() +* Added getData to ServiceDescriptionInterface +* Added context array to RequestInterface::setState() +* Bug: Removing hard dependency on the BackoffPlugin from Guzzle\Http +* Bug: Adding required content-type when JSON request visitor adds JSON to a command +* Bug: Fixing the serialization of a service description with custom data +* Made it easier to deal with exceptions thrown when transferring commands or requests in parallel by providing + an array of successful and failed responses +* Moved getPath from Guzzle\Service\Resource\Model to Guzzle\Common\Collection +* Added Guzzle\Http\IoEmittingEntityBody +* Moved command filtration from validators to location visitors +* Added `extends` attributes to service description parameters +* Added getModels to ServiceDescriptionInterface + +3.0.7 (2012-12-19) +------------------ + +* Fixing phar detection when forcing a cacert to system if null or true +* Allowing filename to be passed to `Guzzle\Http\Message\Request::setResponseBody()` +* Cleaning up `Guzzle\Common\Collection::inject` method +* Adding a response_body location to service descriptions + +3.0.6 (2012-12-09) +------------------ + +* CurlMulti performance improvements +* Adding setErrorResponses() to Operation +* composer.json tweaks + +3.0.5 (2012-11-18) +------------------ + +* Bug: Fixing an infinite recursion bug caused from revalidating with the CachePlugin +* Bug: Response body can now be a string containing "0" +* Bug: Using Guzzle inside of a phar uses system by default but now allows for a custom cacert +* Bug: QueryString::fromString now properly parses query string parameters that contain equal signs +* Added support for XML attributes in service description responses +* DefaultRequestSerializer now supports array URI parameter values for URI template expansion +* Added better mimetype guessing to requests and post files + +3.0.4 (2012-11-11) +------------------ + +* Bug: Fixed a bug when adding multiple cookies to a request to use the correct glue value +* Bug: Cookies can now be added that have a name, domain, or value set to "0" +* Bug: Using the system cacert bundle when using the Phar +* Added json and xml methods to Response to make it easier to parse JSON and XML response data into data structures +* Enhanced cookie jar de-duplication +* Added the ability to enable strict cookie jars that throw exceptions when invalid cookies are added +* Added setStream to StreamInterface to actually make it possible to implement custom rewind behavior for entity bodies +* Added the ability to create any sort of hash for a stream rather than just an MD5 hash + +3.0.3 (2012-11-04) +------------------ + +* Implementing redirects in PHP rather than cURL +* Added PECL URI template extension and using as default parser if available +* Bug: Fixed Content-Length parsing of Response factory +* Adding rewind() method to entity bodies and streams. Allows for custom rewinding of non-repeatable streams. +* Adding ToArrayInterface throughout library +* Fixing OauthPlugin to create unique nonce values per request + +3.0.2 (2012-10-25) +------------------ + +* Magic methods are enabled by default on clients +* Magic methods return the result of a command +* Service clients no longer require a base_url option in the factory +* Bug: Fixed an issue with URI templates where null template variables were being expanded + +3.0.1 (2012-10-22) +------------------ + +* Models can now be used like regular collection objects by calling filter, map, etc +* Models no longer require a Parameter structure or initial data in the constructor +* Added a custom AppendIterator to get around a PHP bug with the `\AppendIterator` + +3.0.0 (2012-10-15) +------------------ + +* Rewrote service description format to be based on Swagger + * Now based on JSON schema + * Added nested input structures and nested response models + * Support for JSON and XML input and output models + * Renamed `commands` to `operations` + * Removed dot class notation + * Removed custom types +* Broke the project into smaller top-level namespaces to be more component friendly +* Removed support for XML configs and descriptions. Use arrays or JSON files. +* Removed the Validation component and Inspector +* Moved all cookie code to Guzzle\Plugin\Cookie +* Magic methods on a Guzzle\Service\Client now return the command un-executed. +* Calling getResult() or getResponse() on a command will lazily execute the command if needed. +* Now shipping with cURL's CA certs and using it by default +* Added previousResponse() method to response objects +* No longer sending Accept and Accept-Encoding headers on every request +* Only sending an Expect header by default when a payload is greater than 1MB +* Added/moved client options: + * curl.blacklist to curl.option.blacklist + * Added ssl.certificate_authority +* Added a Guzzle\Iterator component +* Moved plugins from Guzzle\Http\Plugin to Guzzle\Plugin +* Added a more robust backoff retry strategy (replaced the ExponentialBackoffPlugin) +* Added a more robust caching plugin +* Added setBody to response objects +* Updating LogPlugin to use a more flexible MessageFormatter +* Added a completely revamped build process +* Cleaning up Collection class and removing default values from the get method +* Fixed ZF2 cache adapters + +2.8.8 (2012-10-15) +------------------ + +* Bug: Fixed a cookie issue that caused dot prefixed domains to not match where popular browsers did + +2.8.7 (2012-09-30) +------------------ + +* Bug: Fixed config file aliases for JSON includes +* Bug: Fixed cookie bug on a request object by using CookieParser to parse cookies on requests +* Bug: Removing the path to a file when sending a Content-Disposition header on a POST upload +* Bug: Hardening request and response parsing to account for missing parts +* Bug: Fixed PEAR packaging +* Bug: Fixed Request::getInfo +* Bug: Fixed cases where CURLM_CALL_MULTI_PERFORM return codes were causing curl transactions to fail +* Adding the ability for the namespace Iterator factory to look in multiple directories +* Added more getters/setters/removers from service descriptions +* Added the ability to remove POST fields from OAuth signatures +* OAuth plugin now supports 2-legged OAuth + +2.8.6 (2012-09-05) +------------------ + +* Added the ability to modify and build service descriptions +* Added the use of visitors to apply parameters to locations in service descriptions using the dynamic command +* Added a `json` parameter location +* Now allowing dot notation for classes in the CacheAdapterFactory +* Using the union of two arrays rather than an array_merge when extending service builder services and service params +* Ensuring that a service is a string before doing strpos() checks on it when substituting services for references + in service builder config files. +* Services defined in two different config files that include one another will by default replace the previously + defined service, but you can now create services that extend themselves and merge their settings over the previous +* The JsonLoader now supports aliasing filenames with different filenames. This allows you to alias something like + '_default' with a default JSON configuration file. + +2.8.5 (2012-08-29) +------------------ + +* Bug: Suppressed empty arrays from URI templates +* Bug: Added the missing $options argument from ServiceDescription::factory to enable caching +* Added support for HTTP responses that do not contain a reason phrase in the start-line +* AbstractCommand commands are now invokable +* Added a way to get the data used when signing an Oauth request before a request is sent + +2.8.4 (2012-08-15) +------------------ + +* Bug: Custom delay time calculations are no longer ignored in the ExponentialBackoffPlugin +* Added the ability to transfer entity bodies as a string rather than streamed. This gets around curl error 65. Set `body_as_string` in a request's curl options to enable. +* Added a StreamInterface, EntityBodyInterface, and added ftell() to Guzzle\Common\Stream +* Added an AbstractEntityBodyDecorator and a ReadLimitEntityBody decorator to transfer only a subset of a decorated stream +* Stream and EntityBody objects will now return the file position to the previous position after a read required operation (e.g. getContentMd5()) +* Added additional response status codes +* Removed SSL information from the default User-Agent header +* DELETE requests can now send an entity body +* Added an EventDispatcher to the ExponentialBackoffPlugin and added an ExponentialBackoffLogger to log backoff retries +* Added the ability of the MockPlugin to consume mocked request bodies +* LogPlugin now exposes request and response objects in the extras array + +2.8.3 (2012-07-30) +------------------ + +* Bug: Fixed a case where empty POST requests were sent as GET requests +* Bug: Fixed a bug in ExponentialBackoffPlugin that caused fatal errors when retrying an EntityEnclosingRequest that does not have a body +* Bug: Setting the response body of a request to null after completing a request, not when setting the state of a request to new +* Added multiple inheritance to service description commands +* Added an ApiCommandInterface and added ``getParamNames()`` and ``hasParam()`` +* Removed the default 2mb size cutoff from the Md5ValidatorPlugin so that it now defaults to validating everything +* Changed CurlMulti::perform to pass a smaller timeout to CurlMulti::executeHandles + +2.8.2 (2012-07-24) +------------------ + +* Bug: Query string values set to 0 are no longer dropped from the query string +* Bug: A Collection object is no longer created each time a call is made to ``Guzzle\Service\Command\AbstractCommand::getRequestHeaders()`` +* Bug: ``+`` is now treated as an encoded space when parsing query strings +* QueryString and Collection performance improvements +* Allowing dot notation for class paths in filters attribute of a service descriptions + +2.8.1 (2012-07-16) +------------------ + +* Loosening Event Dispatcher dependency +* POST redirects can now be customized using CURLOPT_POSTREDIR + +2.8.0 (2012-07-15) +------------------ + +* BC: Guzzle\Http\Query + * Query strings with empty variables will always show an equal sign unless the variable is set to QueryString::BLANK (e.g. ?acl= vs ?acl) + * Changed isEncodingValues() and isEncodingFields() to isUrlEncoding() + * Changed setEncodeValues(bool) and setEncodeFields(bool) to useUrlEncoding(bool) + * Changed the aggregation functions of QueryString to be static methods + * Can now use fromString() with querystrings that have a leading ? +* cURL configuration values can be specified in service descriptions using ``curl.`` prefixed parameters +* Content-Length is set to 0 before emitting the request.before_send event when sending an empty request body +* Cookies are no longer URL decoded by default +* Bug: URI template variables set to null are no longer expanded + +2.7.2 (2012-07-02) +------------------ + +* BC: Moving things to get ready for subtree splits. Moving Inflection into Common. Moving Guzzle\Http\Parser to Guzzle\Parser. +* BC: Removing Guzzle\Common\Batch\Batch::count() and replacing it with isEmpty() +* CachePlugin now allows for a custom request parameter function to check if a request can be cached +* Bug fix: CachePlugin now only caches GET and HEAD requests by default +* Bug fix: Using header glue when transferring headers over the wire +* Allowing deeply nested arrays for composite variables in URI templates +* Batch divisors can now return iterators or arrays + +2.7.1 (2012-06-26) +------------------ + +* Minor patch to update version number in UA string +* Updating build process + +2.7.0 (2012-06-25) +------------------ + +* BC: Inflection classes moved to Guzzle\Inflection. No longer static methods. Can now inject custom inflectors into classes. +* BC: Removed magic setX methods from commands +* BC: Magic methods mapped to service description commands are now inflected in the command factory rather than the client __call() method +* Verbose cURL options are no longer enabled by default. Set curl.debug to true on a client to enable. +* Bug: Now allowing colons in a response start-line (e.g. HTTP/1.1 503 Service Unavailable: Back-end server is at capacity) +* Guzzle\Service\Resource\ResourceIteratorApplyBatched now internally uses the Guzzle\Common\Batch namespace +* Added Guzzle\Service\Plugin namespace and a PluginCollectionPlugin +* Added the ability to set POST fields and files in a service description +* Guzzle\Http\EntityBody::factory() now accepts objects with a __toString() method +* Adding a command.before_prepare event to clients +* Added BatchClosureTransfer and BatchClosureDivisor +* BatchTransferException now includes references to the batch divisor and transfer strategies +* Fixed some tests so that they pass more reliably +* Added Guzzle\Common\Log\ArrayLogAdapter + +2.6.6 (2012-06-10) +------------------ + +* BC: Removing Guzzle\Http\Plugin\BatchQueuePlugin +* BC: Removing Guzzle\Service\Command\CommandSet +* Adding generic batching system (replaces the batch queue plugin and command set) +* Updating ZF cache and log adapters and now using ZF's composer repository +* Bug: Setting the name of each ApiParam when creating through an ApiCommand +* Adding result_type, result_doc, deprecated, and doc_url to service descriptions +* Bug: Changed the default cookie header casing back to 'Cookie' + +2.6.5 (2012-06-03) +------------------ + +* BC: Renaming Guzzle\Http\Message\RequestInterface::getResourceUri() to getResource() +* BC: Removing unused AUTH_BASIC and AUTH_DIGEST constants from +* BC: Guzzle\Http\Cookie is now used to manage Set-Cookie data, not Cookie data +* BC: Renaming methods in the CookieJarInterface +* Moving almost all cookie logic out of the CookiePlugin and into the Cookie or CookieJar implementations +* Making the default glue for HTTP headers ';' instead of ',' +* Adding a removeValue to Guzzle\Http\Message\Header +* Adding getCookies() to request interface. +* Making it easier to add event subscribers to HasDispatcherInterface classes. Can now directly call addSubscriber() + +2.6.4 (2012-05-30) +------------------ + +* BC: Cleaning up how POST files are stored in EntityEnclosingRequest objects. Adding PostFile class. +* BC: Moving ApiCommand specific functionality from the Inspector and on to the ApiCommand +* Bug: Fixing magic method command calls on clients +* Bug: Email constraint only validates strings +* Bug: Aggregate POST fields when POST files are present in curl handle +* Bug: Fixing default User-Agent header +* Bug: Only appending or prepending parameters in commands if they are specified +* Bug: Not requiring response reason phrases or status codes to match a predefined list of codes +* Allowing the use of dot notation for class namespaces when using instance_of constraint +* Added any_match validation constraint +* Added an AsyncPlugin +* Passing request object to the calculateWait method of the ExponentialBackoffPlugin +* Allowing the result of a command object to be changed +* Parsing location and type sub values when instantiating a service description rather than over and over at runtime + +2.6.3 (2012-05-23) +------------------ + +* [BC] Guzzle\Common\FromConfigInterface no longer requires any config options. +* [BC] Refactoring how POST files are stored on an EntityEnclosingRequest. They are now separate from POST fields. +* You can now use an array of data when creating PUT request bodies in the request factory. +* Removing the requirement that HTTPS requests needed a Cache-Control: public directive to be cacheable. +* [Http] Adding support for Content-Type in multipart POST uploads per upload +* [Http] Added support for uploading multiple files using the same name (foo[0], foo[1]) +* Adding more POST data operations for easier manipulation of POST data. +* You can now set empty POST fields. +* The body of a request is only shown on EntityEnclosingRequest objects that do not use POST files. +* Split the Guzzle\Service\Inspector::validateConfig method into two methods. One to initialize when a command is created, and one to validate. +* CS updates + +2.6.2 (2012-05-19) +------------------ + +* [Http] Better handling of nested scope requests in CurlMulti. Requests are now always prepares in the send() method rather than the addRequest() method. + +2.6.1 (2012-05-19) +------------------ + +* [BC] Removing 'path' support in service descriptions. Use 'uri'. +* [BC] Guzzle\Service\Inspector::parseDocBlock is now protected. Adding getApiParamsForClass() with cache. +* [BC] Removing Guzzle\Common\NullObject. Use https://github.com/mtdowling/NullObject if you need it. +* [BC] Removing Guzzle\Common\XmlElement. +* All commands, both dynamic and concrete, have ApiCommand objects. +* Adding a fix for CurlMulti so that if all of the connections encounter some sort of curl error, then the loop exits. +* Adding checks to EntityEnclosingRequest so that empty POST files and fields are ignored. +* Making the method signature of Guzzle\Service\Builder\ServiceBuilder::factory more flexible. + +2.6.0 (2012-05-15) +------------------ + +* [BC] Moving Guzzle\Service\Builder to Guzzle\Service\Builder\ServiceBuilder +* [BC] Executing a Command returns the result of the command rather than the command +* [BC] Moving all HTTP parsing logic to Guzzle\Http\Parsers. Allows for faster C implementations if needed. +* [BC] Changing the Guzzle\Http\Message\Response::setProtocol() method to accept a protocol and version in separate args. +* [BC] Moving ResourceIterator* to Guzzle\Service\Resource +* [BC] Completely refactored ResourceIterators to iterate over a cloned command object +* [BC] Moved Guzzle\Http\UriTemplate to Guzzle\Http\Parser\UriTemplate\UriTemplate +* [BC] Guzzle\Guzzle is now deprecated +* Moving Guzzle\Common\Guzzle::inject to Guzzle\Common\Collection::inject +* Adding Guzzle\Version class to give version information about Guzzle +* Adding Guzzle\Http\Utils class to provide getDefaultUserAgent() and getHttpDate() +* Adding Guzzle\Curl\CurlVersion to manage caching curl_version() data +* ServiceDescription and ServiceBuilder are now cacheable using similar configs +* Changing the format of XML and JSON service builder configs. Backwards compatible. +* Cleaned up Cookie parsing +* Trimming the default Guzzle User-Agent header +* Adding a setOnComplete() method to Commands that is called when a command completes +* Keeping track of requests that were mocked in the MockPlugin +* Fixed a caching bug in the CacheAdapterFactory +* Inspector objects can be injected into a Command object +* Refactoring a lot of code and tests to be case insensitive when dealing with headers +* Adding Guzzle\Http\Message\HeaderComparison for easy comparison of HTTP headers using a DSL +* Adding the ability to set global option overrides to service builder configs +* Adding the ability to include other service builder config files from within XML and JSON files +* Moving the parseQuery method out of Url and on to QueryString::fromString() as a static factory method. + +2.5.0 (2012-05-08) +------------------ + +* Major performance improvements +* [BC] Simplifying Guzzle\Common\Collection. Please check to see if you are using features that are now deprecated. +* [BC] Using a custom validation system that allows a flyweight implementation for much faster validation. No longer using Symfony2 Validation component. +* [BC] No longer supporting "{{ }}" for injecting into command or UriTemplates. Use "{}" +* Added the ability to passed parameters to all requests created by a client +* Added callback functionality to the ExponentialBackoffPlugin +* Using microtime in ExponentialBackoffPlugin to allow more granular backoff strategies. +* Rewinding request stream bodies when retrying requests +* Exception is thrown when JSON response body cannot be decoded +* Added configurable magic method calls to clients and commands. This is off by default. +* Fixed a defect that added a hash to every parsed URL part +* Fixed duplicate none generation for OauthPlugin. +* Emitting an event each time a client is generated by a ServiceBuilder +* Using an ApiParams object instead of a Collection for parameters of an ApiCommand +* cache.* request parameters should be renamed to params.cache.* +* Added the ability to set arbitrary curl options on requests (disable_wire, progress, etc). See CurlHandle. +* Added the ability to disable type validation of service descriptions +* ServiceDescriptions and ServiceBuilders are now Serializable diff --git a/core/vendor/guzzlehttp/guzzle/LICENSE b/core/vendor/guzzlehttp/guzzle/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..71d3b783cb5b82e732f4555c5b7839036334607b --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2014 Michael Dowling, https://github.com/mtdowling <mtdowling@gmail.com> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/core/vendor/guzzlehttp/guzzle/Makefile b/core/vendor/guzzlehttp/guzzle/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..e21e9dfbf7fcf81aaa40b9c918e94ad21ce691e3 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/Makefile @@ -0,0 +1,37 @@ +all: clean coverage docs + +start-server: + @ps aux | grep 'node tests/server.js' | grep -v grep > /dev/null \ + || node tests/server.js &> /dev/null & + +stop-server: + @PID=$(shell ps axo pid,command | grep 'tests/server.js' | grep -v grep | cut -f 1 -d " ") && \ + [ -n "$$PID" ] && \ + kill $$PID || \ + true + +test: start-server + vendor/bin/phpunit + $(MAKE) stop-server + +coverage: start-server + vendor/bin/phpunit --coverage-html=artifacts/coverage + $(MAKE) stop-server + +view-coverage: + open artifacts/coverage/index.html + +clean: + rm -rf artifacts/* + +docs: + cd docs && make html && cd .. + +view-docs: + open docs/_build/html/index.html + +perf: start-server + php tests/perf.php + $(MAKE) stop-server + +.PHONY: docs diff --git a/core/vendor/guzzlehttp/guzzle/README.md b/core/vendor/guzzlehttp/guzzle/README.md new file mode 100644 index 0000000000000000000000000000000000000000..017720bab06c8980621c2b55722dae4496ea80a5 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/README.md @@ -0,0 +1,68 @@ +Guzzle, PHP HTTP client and webservice framework +================================================ + +[](http://travis-ci.org/guzzle/guzzle) + +Guzzle is a PHP HTTP client that makes it easy to work with HTTP/1.1 and takes +the pain out of consuming web services. + +```php +$client = new GuzzleHttp\Client(); +$response = $client->get('http://guzzlephp.org'); +$res = $client->get('https://api.github.com/user', ['auth' => ['user', 'pass']]); +echo $res->getStatusCode(); +// 200 +echo $res->getHeader('content-type'); +// 'application/json; charset=utf8' +echo $res->getBody(); +// {"type":"User"...' +var_export($res->json()); +// Outputs the JSON decoded data +``` + +- Pluggable HTTP adapters that can send requests serially or in parallel +- Doesn't require cURL, but uses cURL by default +- Streams data for both uploads and downloads +- Provides event hooks & plugins for cookies, caching, logging, OAuth, mocks, + etc... +- Keep-Alive & connection pooling +- SSL Verification +- Automatic decompression of response bodies +- Streaming multipart file uploads +- Connection timeouts + +Get more information and answers with the +[Documentation](http://guzzlephp.org/), +[Forums](https://groups.google.com/forum/?hl=en#!forum/guzzle), +and IRC ([#guzzlephp](irc://irc.freenode.net/#guzzlephp) @ irc.freenode.net). + +### Installing via Composer + +The recommended way to install Guzzle is through +[Composer](http://getcomposer.org). + +```bash +# Install Composer +curl -sS https://getcomposer.org/installer | php +``` + +Next, update your project's composer.json file to include Guzzle: + +```javascript +{ + "require": { + "guzzlehttp/guzzle": "~4.0" + } +} +``` + +After installing, you need to require Composer's autoloader: + +```php +require 'vendor/autoload.php'; +``` + +### Documentation + +More information can be found in the online documentation at +http://guzzlephp.org/. diff --git a/core/vendor/guzzlehttp/guzzle/UPGRADING.md b/core/vendor/guzzlehttp/guzzle/UPGRADING.md new file mode 100644 index 0000000000000000000000000000000000000000..0b1c1156bdac660a9b3d8380baf2352d0ce1fcf2 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/UPGRADING.md @@ -0,0 +1,987 @@ +Guzzle Upgrade Guide +==================== + +3.x to 4.0 +---------- + +## Overarching changes: + +- Now requires PHP 5.4 or greater. +- No longer requires cURL to send requests. +- Guzzle no longer wraps every exception it throws. Only exceptions that are + recoverable are now wrapped by Guzzle. +- Various namespaces have been removed or renamed. +- No longer requiring the Symfony EventDispatcher. A custom event dispatcher + based on the Symfony EventDispatcher is + now utilized in `GuzzleHttp\Event\EmitterInterface` (resulting in significant + speed and functionality improvements). + +Changes per Guzzle 3.x namespace are described below. + +## Batch + +The `Guzzle\Batch` namespace has been removed. This is best left to +third-parties to implement on top of Guzzle's core HTTP library. + +## Cache + +The `Guzzle\Cache` namespace has been removed. (Todo: No suitable replacement +has been implemented yet, but hoping to utilize a PSR cache interface). + +## Common + +- Removed all of the wrapped exceptions. It's better to use the standard PHP + library for unrecoverable exceptions. +- `FromConfigInterface` has been removed. +- `Guzzle\Common\Version` has been removed. The VERSION constant can be found + at `GuzzleHttp\ClientInterface::VERSION`. + +### Collection + +- `getAll` has been removed. Use `toArray` to convert a collection to an array. +- `inject` has been removed. +- `keySearch` has been removed. +- `getPath` no longer supports wildcard expressions. Use something better like + JMESPath for this. +- `setPath` now supports appending to an existing array via the `[]` notation. + +### Events + +Guzzle no longer requires Symfony's EventDispatcher component. Guzzle now uses +`GuzzleHttp\Event\Emitter`. + +- `Symfony\Component\EventDispatcher\EventDispatcherInterface` is replaced by + `GuzzleHttp\Event\EmitterInterface`. +- `Symfony\Component\EventDispatcher\EventDispatcher` is replaced by + `GuzzleHttp\Event\Emitter`. +- `Symfony\Component\EventDispatcher\Event` is replaced by + `GuzzleHttp\Event\Event`, and Guzzle now has an EventInterface in + `GuzzleHttp\Event\EventInterface`. +- `AbstractHasDispatcher` has moved to a trait, `HasEmitterTrait`, and + `HasDispatcherInterface` has moved to `HasEmitterInterface`. Retrieving the + event emitter of a request, client, etc now uses the `getEmitter` method + rather than the `getDispatcher` method. + +#### Emitter + +- Use the `once()` method to add a listener that automatically removes itself + the first time it is invoked. +- Use the `listeners()` method to retrieve a list of event listeners rather than + the `getListeners()` method. +- Use `emit()` instead of `dispatch()` to emit an event from an emitter. +- Use `attach()` instead of `addSubscriber()` and `detach()` instead of + `removeSubscriber()`. + +```php +$mock = new Mock(); +// 3.x +$request->getEventDispatcher()->addSubscriber($mock); +$request->getEventDispatcher()->removeSubscriber($mock); +// 4.x +$request->getEmitter()->attach($mock); +$request->getEmitter()->detach($mock); +``` + +Use the `on()` method to add a listener rather than the `addListener()` method. + +```php +// 3.x +$request->getEventDispatcher()->addListener('foo', function (Event $event) { /* ... */ } ); +// 4.x +$request->getEmitter()->on('foo', function (Event $event, $name) { /* ... */ } ); +``` + +## Http + +### General changes + +- The cacert.pem certificate has been moved to `src/cacert.pem`. +- Added the concept of adapters that are used to transfer requests over the + wire. +- Simplified the event system. +- Sending requests in parallel is still possible, but batching is no longer a + concept of the HTTP layer. Instead, you must use the `complete` and `error` + events to asynchronously manage parallel request transfers. +- `Guzzle\Http\Url` has moved to `GuzzleHttp\Url`. +- `Guzzle\Http\QueryString` has moved to `GuzzleHttp\Query`. +- QueryAggregators have been rewritten so that they are simply callable + functions. +- `GuzzleHttp\StaticClient` has been removed. Use the functions provided in + `functions.php` for an easy to use static client instance. +- Exceptions in `GuzzleHttp\Exception` have been updated to all extend from + `GuzzleHttp\Exception\TransferException`. + +### Client + +Calling methods like `get()`, `post()`, `head()`, etc. no longer create and +return a request, but rather creates a request, sends the request, and returns +the response. + +```php +// 3.0 +$request = $client->get('/'); +$response = $request->send(); + +// 4.0 +$response = $client->get('/'); + +// or, to mirror the previous behavior +$request = $client->createRequest('GET', '/'); +$response = $client->send($request); +``` + +`GuzzleHttp\ClientInterface` has changed. + +- The `send` method no longer accepts more than one request. Use `sendAll` to + send multiple requests in parallel. +- `setUserAgent()` has been removed. Use a default request option instead. You + could, for example, do something like: + `$client->setConfig('defaults/headers/User-Agent', 'Foo/Bar ' . $client::getDefaultUserAgent())`. +- `setSslVerification()` has been removed. Use default request options instead, + like `$client->setConfig('defaults/verify', true)`. + +`GuzzleHttp\Client` has changed. + +- The constructor now accepts only an associative array. You can include a + `base_url` string or array to use a URI template as the base URL of a client. + You can also specify a `defaults` key that is an associative array of default + request options. You can pass an `adapter` to use a custom adapter, + `batch_adapter` to use a custom adapter for sending requests in parallel, or + a `message_factory` to change the factory used to create HTTP requests and + responses. +- The client no longer emits a `client.create_request` event. +- Creating requests with a client no longer automatically utilize a URI + template. You must pass an array into a creational method (e.g., + `createRequest`, `get`, `put`, etc...) in order to expand a URI template. + +### Messages + +Messages no longer have references to their counterparts (i.e., a request no +longer has a reference to it's response, and a response no loger has a +reference to its request). This association is now managed through a +`GuzzleHttp\Adapter\TransactionInterface` object. You can get references to +these transaction objects using request events that are emitted over the +lifecycle of a request. + +#### Requests with a body + +- `GuzzleHttp\Message\EntityEnclosingRequest` and + `GuzzleHttp\Message\EntityEnclosingRequestInterface` have been removed. The + separation between requests that contain a body and requests that do not + contain a body has been removed, and now `GuzzleHttp\Message\RequestInterface` + handles both use cases. +- Any method that previously accepts a `GuzzleHttp\Response` object now accept a + `GuzzleHttp\Message\ResponseInterface`. +- `GuzzleHttp\Message\RequestFactoryInterface` has been renamed to + `GuzzleHttp\Message\MessageFactoryInterface`. This interface is used to create + both requests and responses and is implemented in + `GuzzleHttp\Message\MessageFactory`. +- POST field and file methods have been removed from the request object. You + must now use the methods made available to `GuzzleHttp\Post\PostBodyInterface` + to control the format of a POST body. Requests that are created using a + standard `GuzzleHttp\Message\MessageFactoryInterface` will automatically use + a `GuzzleHttp\Post\PostBody` body if the body was passed as an array or if + the method is POST and no body is provided. + +```php +$request = $client->createRequest('POST', '/'); +$request->getBody()->setField('foo', 'bar'); +$request->getBody()->addFile(new PostFile('file_key', fopen('/path/to/content', 'r'))); +``` + +#### Headers + +- `GuzzleHttp\Message\Header` has been removed. Header values are now simply + represented by an array of values or as a string. Header values are returned + as a string by default when retrieving a header value from a message. You can + pass an optional argument of `true` to retrieve a header value as an array + of strings instead of a single concatenated string. +- `GuzzleHttp\PostFile` and `GuzzleHttp\PostFileInterface` have been moved to + `GuzzleHttp\Post`. This interface has been simplified and now allows the + addition of arbitrary headers. +- Custom headers like `GuzzleHttp\Message\Header\Link` have been removed. Most + of the custom headers are now handled separately in specific + subscribers/plugins, and `GuzzleHttp\Message\HeaderValues::parseParams()` has + been updated to properly handle headers that contain parameters (like the + `Link` header). + +#### Responses + +- `GuzzleHttp\Message\Response::getInfo()` and + `GuzzleHttp\Message\Response::setInfo()` have been removed. Use the event + system to retrieve this type of information. +- `GuzzleHttp\Message\Response::getRawHeaders()` has been removed. +- `GuzzleHttp\Message\Response::getMessage()` has been removed. +- `GuzzleHttp\Message\Response::calculateAge()` and other cache specific + methods have moved to the CacheSubscriber. +- Header specific helper functions like `getContentMd5()` have been removed. + Just use `getHeader('Content-MD5')` instead. +- `GuzzleHttp\Message\Response::setRequest()` and + `GuzzleHttp\Message\Response::getRequest()` have been removed. Use the event + system to work with request and response objects as a transaction. +- `GuzzleHttp\Message\Response::getRedirectCount()` has been removed. Use the + Redirect subscriber instead. +- `GuzzleHttp\Message\Response::isSuccessful()` and other related methods have + been removed. Use `getStatusCode()` instead. + +#### Streaming responses + +Streaming requests can now be created by a client directly, returning a +`GuzzleHttp\Message\ResponseInterface` object that contains a body stream +referencing an open PHP HTTP stream. + +```php +// 3.0 +use Guzzle\Stream\PhpStreamRequestFactory; +$request = $client->get('/'); +$factory = new PhpStreamRequestFactory(); +$stream = $factory->fromRequest($request); +$data = $stream->read(1024); + +// 4.0 +$response = $client->get('/', ['stream' => true]); +// Read some data off of the stream in the response body +$data = $response->getBody()->read(1024); +``` + +#### Redirects + +The `configureRedirects()` method has been removed in favor of a +`allow_redirects` request option. + +```php +// Standard redirects with a default of a max of 5 redirects +$request = $client->createRequest('GET', '/', ['allow_redirects' => true]); + +// Strict redirects with a custom number of redirects +$request = $client->createRequest('GET', '/', [ + 'allow_redirects' => ['max' => 5, 'strict' => true] +]); +``` + +#### EntityBody + +EntityBody interfaces and classes have been removed or moved to +`GuzzleHttp\Stream`. All classes and interfaces that once required +`GuzzleHttp\EntityBodyInterface` now require +`GuzzleHttp\Stream\StreamInterface`. Creating a new body for a request no +longer uses `GuzzleHttp\EntityBody::factory` but now uses +`GuzzleHttp\Stream\Stream::factory` or even better: +`GuzzleHttp\Stream\create()`. + +- `Guzzle\Http\EntityBodyInterface` is now `GuzzleHttp\Stream\StreamInterface` +- `Guzzle\Http\EntityBody` is now `GuzzleHttp\Stream\Stream` +- `Guzzle\Http\CachingEntityBody` is now `GuzzleHttp\Stream\CachingStream` +- `Guzzle\Http\ReadLimitEntityBody` is now `GuzzleHttp\Stream\LimitStream` +- `Guzzle\Http\IoEmittyinEntityBody` has been removed. + +#### Request lifecycle events + +Requests previously submitted a large number of requests. The number of events +emitted over the lifecycle of a request has been significantly reduced to make +it easier to understand how to extend the behavior of a request. All events +emitted during the lifecycle of a request now emit a custom +`GuzzleHttp\Event\EventInterface` object that contains context providing +methods and a way in which to modify the transaction at that specific point in +time (e.g., intercept the request and set a response on the transaction). + +- `request.before_send` has been renamed to ``before`` and now emits a + `GuzzleHttp\Event\BeforeEvent` +- `request.complete` has been renamed to `complete` and now emits a + `GuzzleHttp\Event\CompleteEvent`. +- `request.sent` has been removed. Use `complete`. +- `request.success` has been removed. Use `complete`. +- `error` is now an event that emits a `GuzzleHttp\Event\ErrorEvent`. +- `request.exception` has been removed. Use `error`. +- `request.receive.status_line` has been removed. +- `curl.callback.progress` has been removed. Use a custom `StreamInterface` to + maintain a status update. +- `curl.callback.write` has been removed. Use a custom `StreamInterface` to + intercept writes. +- `curl.callback.read` has been removed. Use a custom `StreamInterface` to + intercept reads. + +`headers` is a new event that is emitted after the response headers of a +request have been received before the body of the response is downloaded. This +event emits a `GuzzleHttp\Event\HeadersEvent`. + +You can intercept a request and inject a response using the `intercept()` event +of a `GuzzleHttp\Event\BeforeEvent`, `GuzzleHttp\Event\CompleteEvent`, and +`GuzzleHttp\Event\ErrorEvent` event. + +See: http://docs.guzzlephp.org/en/latest/events.html + +## Inflection + +The `Guzzle\Inflection` namespace has been removed. This is not a core concern +of Guzzle. + +## Iterator + +The `Guzzle\Iterator` namespace has been removed. + +- `Guzzle\Iterator\AppendIterator`, `Guzzle\Iterator\ChunkedIterator`, and + `Guzzle\Iterator\MethodProxyIterator` are nice, but not a core requirement of + Guzzle itself. +- `Guzzle\Iterator\FilterIterator` is no longer needed because an equivalent + class is shipped with PHP 5.4. +- `Guzzle\Iterator\MapIterator` is not really needed when using PHP 5.5 because + it's easier to just wrap an iterator in a generator that maps values. + +For a replacement of these iterators, see https://github.com/nikic/iter + +## Log + +The LogPlugin has moved to https://github.com/guzzle/log-subscriber. The +`Guzzle\Log` namespace has been removed. Guzzle now relies on +`Psr\Log\LoggerInterface` for all logging. The MessageFormatter class has been +moved to `GuzzleHttp\Subscriber\Log\Formatter`. + +## Parser + +The `Guzzle\Parser` namespace has been removed. This was previously used to +make it possible to plug in custom parsers for cookies, messages, URI +templates, and URLs; however, this level of complexity is not needed in Guzzle +so it has been removed. + +- Cookie: Cookie parsing logic has been moved to + `GuzzleHttp\Cookie\SetCookie::fromString`. +- Message: Message parsing logic for both requests and responses has been moved + to `GuzzleHttp\Message\MessageFactory::fromMessage`. Message parsing is only + used in debugging or deserializing messages, so it doesn't make sense for + Guzzle as a library to add this level of complexity to parsing messages. +- UriTemplate: URI template parsing has been moved to + `GuzzleHttp\UriTemplate`. The Guzzle library will automatically use the PECL + URI template library if it is installed. +- Url: URL parsing is now performed in `GuzzleHttp\Url::fromString` (previously + it was `Guzzle\Http\Url::factory()`). If custom URL parsing is necessary, + then developers are free to subclass `GuzzleHttp\Url`. + +## Plugin + +The `Guzzle\Plugin` namespace has been renamed to `GuzzleHttp\Subscriber`. +Several plugins are shipping with the core Guzzle library under this namespace. + +- `GuzzleHttp\Subscriber\Cookie`: Replaces the old CookiePlugin. Cookie jar + code has moved to `GuzzleHttp\Cookie`. +- `GuzzleHttp\Subscriber\History`: Replaces the old HistoryPlugin. +- `GuzzleHttp\Subscriber\HttpError`: Throws errors when a bad HTTP response is + received. +- `GuzzleHttp\Subscriber\Mock`: Replaces the old MockPlugin. +- `GuzzleHttp\Subscriber\Prepare`: Prepares the body of a request just before + sending. This subscriber is attached to all requests by default. +- `GuzzleHttp\Subscriber\Redirect`: Replaces the RedirectPlugin. + +The following plugins have been removed (third-parties are free to re-implement +these if needed): + +- `GuzzleHttp\Plugin\Async` has been removed. +- `GuzzleHttp\Plugin\CurlAuth` has been removed. +- `GuzzleHttp\Plugin\ErrorResponse\ErrorResponsePlugin` has been removed. This + functionality should instead be implemented with event listeners that occur + after normal response parsing occurs in the guzzle/command package. + +The following plugins are not part of the core Guzzle package, but are provided +in separate repositories: + +- `Guzzle\Http\Plugin\BackoffPlugin` has been rewritten to be muchs simpler + to build custom retry policies using simple functions rather than various + chained classes. See: https://github.com/guzzle/retry-subscriber +- `Guzzle\Http\Plugin\Cache\CachePlugin` has moved to + https://github.com/guzzle/cache-subscriber +- `Guzzle\Http\Plugin\Log\LogPlugin` has moved to + https://github.com/guzzle/log-subscriber +- `Guzzle\Http\Plugin\Md5\Md5Plugin` has moved to + https://github.com/guzzle/message-integrity-subscriber +- `Guzzle\Http\Plugin\Mock\MockPlugin` has moved to + `GuzzleHttp\Subscriber\MockSubscriber`. +- `Guzzle\Http\Plugin\Oauth\OauthPlugin` has moved to + https://github.com/guzzle/oauth-subscriber + +## Service + +The service description layer of Guzzle has moved into two separate packages: + +- http://github.com/guzzle/command Provides a high level abstraction over web + services by representing web service operations using commands. +- http://github.com/guzzle/guzzle-services Provides an implementation of + guzzle/command that provides request serialization and response parsing using + Guzzle service descriptions. + +## Stream + +Stream have moved to a separate package available at +https://github.com/guzzle/streams. + +`Guzzle\Stream\StreamInterface` has been given a large update to cleanly take +on the responsibilities of `Guzzle\Http\EntityBody` and +`Guzzle\Http\EntityBodyInterface` now that they have been removed. The number +of methods implemented by the `StreamInterface` has been drastically reduced to +allow developers to more easily extend and decorate stream behavior. + +## Removed methods from StreamInterface + +- `getStream` and `setStream` have been removed to better encapsulate streams. +- `getMetadata` and `setMetadata` have been removed in favor of + `GuzzleHttp\Stream\MetadataStreamInterface`. +- `getWrapper`, `getWrapperData`, `getStreamType`, and `getUri` have all been + removed. This data is accessible when + using streams that implement `GuzzleHttp\Stream\MetadataStreamInterface`. +- `rewind` has been removed. Use `seek(0)` for a similar behavior. + +## Renamed methods + +- `detachStream` has been renamed to `detach`. +- `feof` has been renamed to `eof`. +- `ftell` has been renamed to `tell`. +- `readLine` has moved from an instance method to a static class method of + `GuzzleHttp\Stream\Stream`. + +## Metadata streams + +`GuzzleHttp\Stream\MetadataStreamInterface` has been added to denote streams +that contain additonal metadata accessible via `getMetadata()`. +`GuzzleHttp\Stream\StreamInterface::getMetadata` and +`GuzzleHttp\Stream\StreamInterface::setMetadata` have been removed. + +## SteamRequestFactory + +The entire concept of the StreamRequestFactory has been removed. The way this +was used in Guzzle 3 broke the actual interface of sending streaming requests +(instead of getting back a Response, you got a StreamInterface). Streeaming +PHP requests are now implemented throught the `GuzzleHttp\Adapter\StreamAdapter`. + +3.6 to 3.7 +---------- + +### Deprecations + +- You can now enable E_USER_DEPRECATED warnings to see if you are using any deprecated methods.: + +```php +\Guzzle\Common\Version::$emitWarnings = true; +``` + +The following APIs and options have been marked as deprecated: + +- Marked `Guzzle\Http\Message\Request::isResponseBodyRepeatable()` as deprecated. Use `$request->getResponseBody()->isRepeatable()` instead. +- Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead. +- Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead. +- Marked `Guzzle\Http\Message\Request::setIsRedirect()` as deprecated. Use the HistoryPlugin instead. +- Marked `Guzzle\Http\Message\Request::isRedirect()` as deprecated. Use the HistoryPlugin instead. +- Marked `Guzzle\Cache\CacheAdapterFactory::factory()` as deprecated +- Marked `Guzzle\Service\Client::enableMagicMethods()` as deprecated. Magic methods can no longer be disabled on a Guzzle\Service\Client. +- Marked `Guzzle\Parser\Url\UrlParser` as deprecated. Just use PHP's `parse_url()` and percent encode your UTF-8. +- Marked `Guzzle\Common\Collection::inject()` as deprecated. +- Marked `Guzzle\Plugin\CurlAuth\CurlAuthPlugin` as deprecated. Use + `$client->getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest|NTLM|Any'));` or + `$client->setDefaultOption('auth', array('user', 'pass', 'Basic|Digest|NTLM|Any'));` + +3.7 introduces `request.options` as a parameter for a client configuration and as an optional argument to all creational +request methods. When paired with a client's configuration settings, these options allow you to specify default settings +for various aspects of a request. Because these options make other previous configuration options redundant, several +configuration options and methods of a client and AbstractCommand have been deprecated. + +- Marked `Guzzle\Service\Client::getDefaultHeaders()` as deprecated. Use `$client->getDefaultOption('headers')`. +- Marked `Guzzle\Service\Client::setDefaultHeaders()` as deprecated. Use `$client->setDefaultOption('headers/{header_name}', 'value')`. +- Marked 'request.params' for `Guzzle\Http\Client` as deprecated. Use `$client->setDefaultOption('params/{param_name}', 'value')` +- Marked 'command.headers', 'command.response_body' and 'command.on_complete' as deprecated for AbstractCommand. These will work through Guzzle 4.0 + + $command = $client->getCommand('foo', array( + 'command.headers' => array('Test' => '123'), + 'command.response_body' => '/path/to/file' + )); + + // Should be changed to: + + $command = $client->getCommand('foo', array( + 'command.request_options' => array( + 'headers' => array('Test' => '123'), + 'save_as' => '/path/to/file' + ) + )); + +### Interface changes + +Additions and changes (you will need to update any implementations or subclasses you may have created): + +- Added an `$options` argument to the end of the following methods of `Guzzle\Http\ClientInterface`: + createRequest, head, delete, put, patch, post, options, prepareRequest +- Added an `$options` argument to the end of `Guzzle\Http\Message\Request\RequestFactoryInterface::createRequest()` +- Added an `applyOptions()` method to `Guzzle\Http\Message\Request\RequestFactoryInterface` +- Changed `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $body = null)` to + `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $options = array())`. You can still pass in a + resource, string, or EntityBody into the $options parameter to specify the download location of the response. +- Changed `Guzzle\Common\Collection::__construct($data)` to no longer accepts a null value for `$data` but a + default `array()` +- Added `Guzzle\Stream\StreamInterface::isRepeatable` +- Made `Guzzle\Http\Client::expandTemplate` and `getUriTemplate` protected methods. + +The following methods were removed from interfaces. All of these methods are still available in the concrete classes +that implement them, but you should update your code to use alternative methods: + +- Removed `Guzzle\Http\ClientInterface::setDefaultHeaders(). Use + `$client->getConfig()->setPath('request.options/headers/{header_name}', 'value')`. or + `$client->getConfig()->setPath('request.options/headers', array('header_name' => 'value'))` or + `$client->setDefaultOption('headers/{header_name}', 'value')`. or + `$client->setDefaultOption('headers', array('header_name' => 'value'))`. +- Removed `Guzzle\Http\ClientInterface::getDefaultHeaders(). Use `$client->getConfig()->getPath('request.options/headers')`. +- Removed `Guzzle\Http\ClientInterface::expandTemplate()`. This is an implementation detail. +- Removed `Guzzle\Http\ClientInterface::setRequestFactory()`. This is an implementation detail. +- Removed `Guzzle\Http\ClientInterface::getCurlMulti()`. This is a very specific implementation detail. +- Removed `Guzzle\Http\Message\RequestInterface::canCache`. Use the CachePlugin. +- Removed `Guzzle\Http\Message\RequestInterface::setIsRedirect`. Use the HistoryPlugin. +- Removed `Guzzle\Http\Message\RequestInterface::isRedirect`. Use the HistoryPlugin. + +### Cache plugin breaking changes + +- CacheKeyProviderInterface and DefaultCacheKeyProvider are no longer used. All of this logic is handled in a + CacheStorageInterface. These two objects and interface will be removed in a future version. +- Always setting X-cache headers on cached responses +- Default cache TTLs are now handled by the CacheStorageInterface of a CachePlugin +- `CacheStorageInterface::cache($key, Response $response, $ttl = null)` has changed to `cache(RequestInterface + $request, Response $response);` +- `CacheStorageInterface::fetch($key)` has changed to `fetch(RequestInterface $request);` +- `CacheStorageInterface::delete($key)` has changed to `delete(RequestInterface $request);` +- Added `CacheStorageInterface::purge($url)` +- `DefaultRevalidation::__construct(CacheKeyProviderInterface $cacheKey, CacheStorageInterface $cache, CachePlugin + $plugin)` has changed to `DefaultRevalidation::__construct(CacheStorageInterface $cache, + CanCacheStrategyInterface $canCache = null)` +- Added `RevalidationInterface::shouldRevalidate(RequestInterface $request, Response $response)` + +3.5 to 3.6 +---------- + +* Mixed casing of headers are now forced to be a single consistent casing across all values for that header. +* Messages internally use a HeaderCollection object to delegate handling case-insensitive header resolution +* Removed the whole changedHeader() function system of messages because all header changes now go through addHeader(). + For example, setHeader() first removes the header using unset on a HeaderCollection and then calls addHeader(). + Keeping the Host header and URL host in sync is now handled by overriding the addHeader method in Request. +* Specific header implementations can be created for complex headers. When a message creates a header, it uses a + HeaderFactory which can map specific headers to specific header classes. There is now a Link header and + CacheControl header implementation. +* Moved getLinks() from Response to just be used on a Link header object. + +If you previously relied on Guzzle\Http\Message\Header::raw(), then you will need to update your code to use the +HeaderInterface (e.g. toArray(), getAll(), etc). + +### Interface changes + +* Removed from interface: Guzzle\Http\ClientInterface::setUriTemplate +* Removed from interface: Guzzle\Http\ClientInterface::setCurlMulti() +* Removed Guzzle\Http\Message\Request::receivedRequestHeader() and implemented this functionality in + Guzzle\Http\Curl\RequestMediator +* Removed the optional $asString parameter from MessageInterface::getHeader(). Just cast the header to a string. +* Removed the optional $tryChunkedTransfer option from Guzzle\Http\Message\EntityEnclosingRequestInterface +* Removed the $asObjects argument from Guzzle\Http\Message\MessageInterface::getHeaders() + +### Removed deprecated functions + +* Removed Guzzle\Parser\ParserRegister::get(). Use getParser() +* Removed Guzzle\Parser\ParserRegister::set(). Use registerParser(). + +### Deprecations + +* The ability to case-insensitively search for header values +* Guzzle\Http\Message\Header::hasExactHeader +* Guzzle\Http\Message\Header::raw. Use getAll() +* Deprecated cache control specific methods on Guzzle\Http\Message\AbstractMessage. Use the CacheControl header object + instead. + +### Other changes + +* All response header helper functions return a string rather than mixing Header objects and strings inconsistently +* Removed cURL blacklist support. This is no longer necessary now that Expect, Accept, etc are managed by Guzzle + directly via interfaces +* Removed the injecting of a request object onto a response object. The methods to get and set a request still exist + but are a no-op until removed. +* Most classes that used to require a `Guzzle\Service\Command\CommandInterface` typehint now request a + `Guzzle\Service\Command\ArrayCommandInterface`. +* Added `Guzzle\Http\Message\RequestInterface::startResponse()` to the RequestInterface to handle injecting a response + on a request while the request is still being transferred +* `Guzzle\Service\Command\CommandInterface` now extends from ToArrayInterface and ArrayAccess + +3.3 to 3.4 +---------- + +Base URLs of a client now follow the rules of http://tools.ietf.org/html/rfc3986#section-5.2.2 when merging URLs. + +3.2 to 3.3 +---------- + +### Response::getEtag() quote stripping removed + +`Guzzle\Http\Message\Response::getEtag()` no longer strips quotes around the ETag response header + +### Removed `Guzzle\Http\Utils` + +The `Guzzle\Http\Utils` class was removed. This class was only used for testing. + +### Stream wrapper and type + +`Guzzle\Stream\Stream::getWrapper()` and `Guzzle\Stream\Stream::getSteamType()` are no longer converted to lowercase. + +### curl.emit_io became emit_io + +Emitting IO events from a RequestMediator is now a parameter that must be set in a request's curl options using the +'emit_io' key. This was previously set under a request's parameters using 'curl.emit_io' + +3.1 to 3.2 +---------- + +### CurlMulti is no longer reused globally + +Before 3.2, the same CurlMulti object was reused globally for each client. This can cause issue where plugins added +to a single client can pollute requests dispatched from other clients. + +If you still wish to reuse the same CurlMulti object with each client, then you can add a listener to the +ServiceBuilder's `service_builder.create_client` event to inject a custom CurlMulti object into each client as it is +created. + +```php +$multi = new Guzzle\Http\Curl\CurlMulti(); +$builder = Guzzle\Service\Builder\ServiceBuilder::factory('/path/to/config.json'); +$builder->addListener('service_builder.create_client', function ($event) use ($multi) { + $event['client']->setCurlMulti($multi); +} +}); +``` + +### No default path + +URLs no longer have a default path value of '/' if no path was specified. + +Before: + +```php +$request = $client->get('http://www.foo.com'); +echo $request->getUrl(); +// >> http://www.foo.com/ +``` + +After: + +```php +$request = $client->get('http://www.foo.com'); +echo $request->getUrl(); +// >> http://www.foo.com +``` + +### Less verbose BadResponseException + +The exception message for `Guzzle\Http\Exception\BadResponseException` no longer contains the full HTTP request and +response information. You can, however, get access to the request and response object by calling `getRequest()` or +`getResponse()` on the exception object. + +### Query parameter aggregation + +Multi-valued query parameters are no longer aggregated using a callback function. `Guzzle\Http\Query` now has a +setAggregator() method that accepts a `Guzzle\Http\QueryAggregator\QueryAggregatorInterface` object. This object is +responsible for handling the aggregation of multi-valued query string variables into a flattened hash. + +2.8 to 3.x +---------- + +### Guzzle\Service\Inspector + +Change `\Guzzle\Service\Inspector::fromConfig` to `\Guzzle\Common\Collection::fromConfig` + +**Before** + +```php +use Guzzle\Service\Inspector; + +class YourClient extends \Guzzle\Service\Client +{ + public static function factory($config = array()) + { + $default = array(); + $required = array('base_url', 'username', 'api_key'); + $config = Inspector::fromConfig($config, $default, $required); + + $client = new self( + $config->get('base_url'), + $config->get('username'), + $config->get('api_key') + ); + $client->setConfig($config); + + $client->setDescription(ServiceDescription::factory(__DIR__ . DIRECTORY_SEPARATOR . 'client.json')); + + return $client; + } +``` + +**After** + +```php +use Guzzle\Common\Collection; + +class YourClient extends \Guzzle\Service\Client +{ + public static function factory($config = array()) + { + $default = array(); + $required = array('base_url', 'username', 'api_key'); + $config = Collection::fromConfig($config, $default, $required); + + $client = new self( + $config->get('base_url'), + $config->get('username'), + $config->get('api_key') + ); + $client->setConfig($config); + + $client->setDescription(ServiceDescription::factory(__DIR__ . DIRECTORY_SEPARATOR . 'client.json')); + + return $client; + } +``` + +### Convert XML Service Descriptions to JSON + +**Before** + +```xml +<?xml version="1.0" encoding="UTF-8"?> +<client> + <commands> + <!-- Groups --> + <command name="list_groups" method="GET" uri="groups.json"> + <doc>Get a list of groups</doc> + </command> + <command name="search_groups" method="GET" uri='search.json?query="{{query}} type:group"'> + <doc>Uses a search query to get a list of groups</doc> + <param name="query" type="string" required="true" /> + </command> + <command name="create_group" method="POST" uri="groups.json"> + <doc>Create a group</doc> + <param name="data" type="array" location="body" filters="json_encode" doc="Group JSON"/> + <param name="Content-Type" location="header" static="application/json"/> + </command> + <command name="delete_group" method="DELETE" uri="groups/{{id}}.json"> + <doc>Delete a group by ID</doc> + <param name="id" type="integer" required="true"/> + </command> + <command name="get_group" method="GET" uri="groups/{{id}}.json"> + <param name="id" type="integer" required="true"/> + </command> + <command name="update_group" method="PUT" uri="groups/{{id}}.json"> + <doc>Update a group</doc> + <param name="id" type="integer" required="true"/> + <param name="data" type="array" location="body" filters="json_encode" doc="Group JSON"/> + <param name="Content-Type" location="header" static="application/json"/> + </command> + </commands> +</client> +``` + +**After** + +```json +{ + "name": "Zendesk REST API v2", + "apiVersion": "2012-12-31", + "description":"Provides access to Zendesk views, groups, tickets, ticket fields, and users", + "operations": { + "list_groups": { + "httpMethod":"GET", + "uri": "groups.json", + "summary": "Get a list of groups" + }, + "search_groups":{ + "httpMethod":"GET", + "uri": "search.json?query=\"{query} type:group\"", + "summary": "Uses a search query to get a list of groups", + "parameters":{ + "query":{ + "location": "uri", + "description":"Zendesk Search Query", + "type": "string", + "required": true + } + } + }, + "create_group": { + "httpMethod":"POST", + "uri": "groups.json", + "summary": "Create a group", + "parameters":{ + "data": { + "type": "array", + "location": "body", + "description":"Group JSON", + "filters": "json_encode", + "required": true + }, + "Content-Type":{ + "type": "string", + "location":"header", + "static": "application/json" + } + } + }, + "delete_group": { + "httpMethod":"DELETE", + "uri": "groups/{id}.json", + "summary": "Delete a group", + "parameters":{ + "id":{ + "location": "uri", + "description":"Group to delete by ID", + "type": "integer", + "required": true + } + } + }, + "get_group": { + "httpMethod":"GET", + "uri": "groups/{id}.json", + "summary": "Get a ticket", + "parameters":{ + "id":{ + "location": "uri", + "description":"Group to get by ID", + "type": "integer", + "required": true + } + } + }, + "update_group": { + "httpMethod":"PUT", + "uri": "groups/{id}.json", + "summary": "Update a group", + "parameters":{ + "id": { + "location": "uri", + "description":"Group to update by ID", + "type": "integer", + "required": true + }, + "data": { + "type": "array", + "location": "body", + "description":"Group JSON", + "filters": "json_encode", + "required": true + }, + "Content-Type":{ + "type": "string", + "location":"header", + "static": "application/json" + } + } + } +} +``` + +### Guzzle\Service\Description\ServiceDescription + +Commands are now called Operations + +**Before** + +```php +use Guzzle\Service\Description\ServiceDescription; + +$sd = new ServiceDescription(); +$sd->getCommands(); // @returns ApiCommandInterface[] +$sd->hasCommand($name); +$sd->getCommand($name); // @returns ApiCommandInterface|null +$sd->addCommand($command); // @param ApiCommandInterface $command +``` + +**After** + +```php +use Guzzle\Service\Description\ServiceDescription; + +$sd = new ServiceDescription(); +$sd->getOperations(); // @returns OperationInterface[] +$sd->hasOperation($name); +$sd->getOperation($name); // @returns OperationInterface|null +$sd->addOperation($operation); // @param OperationInterface $operation +``` + +### Guzzle\Common\Inflection\Inflector + +Namespace is now `Guzzle\Inflection\Inflector` + +### Guzzle\Http\Plugin + +Namespace is now `Guzzle\Plugin`. Many other changes occur within this namespace and are detailed in their own sections below. + +### Guzzle\Http\Plugin\LogPlugin and Guzzle\Common\Log + +Now `Guzzle\Plugin\Log\LogPlugin` and `Guzzle\Log` respectively. + +**Before** + +```php +use Guzzle\Common\Log\ClosureLogAdapter; +use Guzzle\Http\Plugin\LogPlugin; + +/** @var \Guzzle\Http\Client */ +$client; + +// $verbosity is an integer indicating desired message verbosity level +$client->addSubscriber(new LogPlugin(new ClosureLogAdapter(function($m) { echo $m; }, $verbosity = LogPlugin::LOG_VERBOSE); +``` + +**After** + +```php +use Guzzle\Log\ClosureLogAdapter; +use Guzzle\Log\MessageFormatter; +use Guzzle\Plugin\Log\LogPlugin; + +/** @var \Guzzle\Http\Client */ +$client; + +// $format is a string indicating desired message format -- @see MessageFormatter +$client->addSubscriber(new LogPlugin(new ClosureLogAdapter(function($m) { echo $m; }, $format = MessageFormatter::DEBUG_FORMAT); +``` + +### Guzzle\Http\Plugin\CurlAuthPlugin + +Now `Guzzle\Plugin\CurlAuth\CurlAuthPlugin`. + +### Guzzle\Http\Plugin\ExponentialBackoffPlugin + +Now `Guzzle\Plugin\Backoff\BackoffPlugin`, and other changes. + +**Before** + +```php +use Guzzle\Http\Plugin\ExponentialBackoffPlugin; + +$backoffPlugin = new ExponentialBackoffPlugin($maxRetries, array_merge( + ExponentialBackoffPlugin::getDefaultFailureCodes(), array(429) + )); + +$client->addSubscriber($backoffPlugin); +``` + +**After** + +```php +use Guzzle\Plugin\Backoff\BackoffPlugin; +use Guzzle\Plugin\Backoff\HttpBackoffStrategy; + +// Use convenient factory method instead -- see implementation for ideas of what +// you can do with chaining backoff strategies +$backoffPlugin = BackoffPlugin::getExponentialBackoff($maxRetries, array_merge( + HttpBackoffStrategy::getDefaultFailureCodes(), array(429) + )); +$client->addSubscriber($backoffPlugin); +``` + +### Known Issues + +#### [BUG] Accept-Encoding header behavior changed unintentionally. + +(See #217) (Fixed in 09daeb8c666fb44499a0646d655a8ae36456575e) + +In version 2.8 setting the `Accept-Encoding` header would set the CURLOPT_ENCODING option, which permitted cURL to +properly handle gzip/deflate compressed responses from the server. In versions affected by this bug this does not happen. +See issue #217 for a workaround, or use a version containing the fix. diff --git a/core/vendor/guzzlehttp/guzzle/composer.json b/core/vendor/guzzlehttp/guzzle/composer.json new file mode 100644 index 0000000000000000000000000000000000000000..3f7b96f3f3cc0ff427220cb0b2d5a5c2c10885a4 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/composer.json @@ -0,0 +1,40 @@ +{ + "name": "guzzlehttp/guzzle", + "type": "library", + "description": "Guzzle is a PHP HTTP client library and framework for building RESTful web service clients", + "keywords": ["framework", "http", "rest", "web service", "curl", "client", "HTTP client"], + "homepage": "http://guzzlephp.org/", + "license": "MIT", + + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + + "require": { + "php": ">=5.4.0", + "guzzlehttp/streams": "1.*" + }, + + "autoload": { + "psr-4": { + "GuzzleHttp\\": "src/" + }, + "files": ["src/functions.php"] + }, + + "require-dev": { + "ext-curl": "*", + "psr/log": "~1", + "phpunit/phpunit": "4.*" + }, + + "extra": { + "branch-alias": { + "dev-master": "4.0.x-dev" + } + } +} diff --git a/core/vendor/guzzlehttp/guzzle/docs/Makefile b/core/vendor/guzzlehttp/guzzle/docs/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..d92e03f95ea7798bae26ae6848ce8b99203bcf30 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/docs/Makefile @@ -0,0 +1,153 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext + +help: + @echo "Please use \`make <target>' where <target> is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + -rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Guzzle.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Guzzle.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/Guzzle" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Guzzle" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." diff --git a/core/vendor/guzzlehttp/guzzle/docs/_static/guzzle-icon.png b/core/vendor/guzzlehttp/guzzle/docs/_static/guzzle-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..f1017f7e6028c14a9e0694c66a6cfbb2d546adf5 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/docs/_static/guzzle-icon.png @@ -0,0 +1,4 @@ +‰PNG + +��� IHDR���������Ï&‡Ö���bKGD�ÿ‡Ì¿��� pHYs����gŸÒR���tIMEÝ,×B—��´IDAT8Ë•ÓÝKÝe�ðÏïx4íljsÁÍãN¾œ ¡ÑÆŠQDì"ˆ^n¢¨»ÑŸ0‚ê.ˆ#X/x±6ÖÕÖE –Zó¥3_[f:ÌÙ<GQÛÓÅñ˜ÕU¿Ë‡_¾ÏïÃÿø¢I§’R)¦`T½”2ýþ‹ èTfÞ²œf#´«ŠŽMIõ(Ÿ¸Ú¶Ý‡ïÂÿL½áZ8Iø4œexë<†ŸÙNg8$c|Rôš•ðEGÏè6¼ËÐv¼AÖÝb†o¤j1±…cvmÇIònÙK##Uô·ná%üëÚc’î€ßìpÚZx·„Wí'lÇe@›•èKy ˆ#2œPê$$,Š;bÉD_‡—CŸr%<oÃ{4Ë)˜’WÔ Ã4o{Q—ÊRY sÆÜVeMƒ5eÑeØ'š‹>²[¾„›,j6/K…´»Ú,‡Ñp.|zÁªRHzG-»ŽqûêpÙ’Jï„|Ô'«µ„7ì”3çŠ^ç½êž g¢Âà kY)q,«5$î1뎕פ°ùQ‹‚Ôfç9wt›wBÁ%9“2þˆÀº:<hg©ÆKÎúÕa}Z½.£Yµ©Íä¬räüü÷êë²W•kÎiô¨iùÍÙ>"…àÉR2qZ5ëqK.©@«÷A•9Ü/.%†=rî»èyƒb" Û]|špUgx¥ˆÏKºçM_é–S.nUZ(3†i;4gÅrÑs>‘4¨^^Ü°ƒ²`ÕÝ¢ënªN‰áÇ}¨ÝYI5æ½àŠvP£¶¦µkÜö». Þò¹guÈXW~0ÅÁÆ1®É¤c +ÊtYP0iÍ8hRz$ŠÉk–$õ+¸ „1uö[ßêá´£n1ËfLªö´[6Ôh”ó7DŸù؈jg¢·¦üeÒôg¯ß(=����IEND®B`‚ \ No newline at end of file diff --git a/core/vendor/guzzlehttp/guzzle/docs/_static/logo.png b/core/vendor/guzzlehttp/guzzle/docs/_static/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..ecc40ac76040183ec01ef3d7c0546a82a4324589 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/docs/_static/logo.png @@ -0,0 +1,939 @@ +‰PNG + +��� IHDR��_��»���d`–��$iCCPICC Profile��8…UßoÛT>‰oR¤? XG‡ŠÅ¯US[¹ÆI“¥íJ¥éØ*$ä:7‰©Û鶪O{7ü@ÙH<! b{ÙöÀ´IS‡*ªIH{èÄ!&íUá»vb'SÄ\õúË9ß9ç;ç^ÛD=_iµšUˆ–«®Ï$•“§”žMŠÒ³ÔKÔ«éN-‘ËÍ.Á÷ÎëáŠËí‘îþNö#¿z‹Üщ"OÀn}øQÌÔk¶K¿ûøi·ÜóðÓ6«—}œxÑÇ'=N!? ŽÐ*ë¼<¼Øf/·a_È“áUnº"f‘³’arÏá/q·1ÿ.›uôì]ƒXûœ¥¹c¸‰Þ+öT¾‰?×µôðK°_¯¹IaøúÒ|xQô©’}tÞçG__ÞÞ{Ñp§Mûju1{±Ñõ%ë˜È#8×ug³¤€ïVø´Øc葨ÈSiàaàÁJ}ª™_šqVæ„Ý˳Z™Ì#d¿§Í䀀?´¼¨ÍÒ:73¢òKWkn®©AÚ®šYQý2™;^È)m»•Â”˺v¡ËJÆÑé&fzgÚØ»žÚŸÝÒìtyØ?¼:/æÛ]ÔRb¶£ÀGèDD#N-bÕ©J;¤Pž2”ĽF6<%2È„…ÃËa1"OÒlÝy9ãÇ„Œ²½-¢Q¥;ǯp¿É±X?SÙüb³ì0g¤°7Ø›ìKÁ:Á +rm:*Ó}(õó¼OuT:NPòü@}(ˆQÎÙïúÍþ†š°K+ô#OÐ14[í hu7Ò>§kk?ì<Ê»øÎí¾kktüqóÝ‹mÇ6°nƶÂøد±-ümR;`zŠ–¡Êðv x#=\Ó% ëoàYÐÚRÚ±£¥êùÐ#&Á?È>ÌÒ¹áЪþ¢þ©n¨_¨Ôß;j„;¦$}*}+ý(}'}/ýLŠtYº"ý$]•¾‘.9»ï½Ÿ%Ø{¯_aÝŠ]hÕkŸ5'SNÊ{äå”ü¼ü²<°¹_“§ä½ðì öÍý½t +³jMµ{-ñ4%×ÆTÅ„«tYÛŸ“¦R6ÈÆØô#§v\œå–Šx:žŠ'H‰ï‹OÄÇâ3·ž¼ø^ø&°¦õþ“0::àm,L%È3â:qVEô +t›ÐÍ]~ߢI«vÖ6ÊWÙ¯ª¯) |ʸ2]ÕG‡Í4Ïå(6w¸½Â‹£$¾ƒ"ŽèAÞû¾EvÝmî[D‡ÿÂ;ëVh[¨}íõ¿Ú†ðN|æ3¢‹õº½âç£Hä‘S:°ßûéKâÝt·Ñx€÷UÏ'D;7ÿ®7;_"ÿÑeó?Y�qxl+��� pHYs����gŸÒR��@�IDATxì½€]Uµ7¾÷>íö2s§Ï$“ɤM + „@Ê„ÞÅ’ ¢O,iÖ‡íð=}ŸÑÇC‘' +úP$**¡’F™´Éô™;õÎí÷ôýÿIf˜ðó#$AïÑpï{Î.뜻öÚkýÖoQÎ9Éy ä%—@^§Wìôv—ï-/¼òÈKÀ‘@^ù柃¼òÈKàH ¯|Ï€Ðó]æ%—@^yå›òÈK /3 ¼ò=BÏw™—@^y ä•oþÈK /¼Î€òÊ÷=ße^y ä%W¾ùg /¼ò8È+ß3 ô|—y ä%—@^ù柼òÈKàH ¯|Ï€Ðó]æ%—@^yå›òÈK /3 ¼ò=BÏw™—@^y ä•ïßñlêìtoß¾]ú;.ÉŸš—@^y œTïJå»*õ`6‚3#G®<òž½Òž¯Šªãhì,¸ï¾ûNÉÜžo"JC¿okiÆt_®UL›½²±Q>©4OÁÄÍM±ÀóMMÊ)hîm5áÈuCthÜöæ¡àÉrÆy²¿ÿ¿îíõ:rý[çå¿ÏKàYôÝÆçûLSlzÌЯñ‰•¥ÁB5IåK$šÛÉu¦š„\dR«¿WãSÊûãkSJÚVbŸìnϪGîSW¬X1ò½£¬í=PಠN”9aðrÕÌz¼Š`Sª„ãº6U Bgˆþ4«„dNÖîßó7G‰«ü!ÉY„‹”2cU–NW¹*Ûçbü=mžìÜ5mñ9œ™½—UE¢øþ¤ÎŸÆ"¶,\㪠§—ŒÇ†öØt&‰UÜÃ¥ÔÔ8—Çj³‚Ë’·¤jz†û¼„‘åq¡aAÅr"S.*õ@¸%o:Ê™ÈffM:×6×¥ ‘çê;ç³óïòøÇ–€ønšÞʦž"ŸìZNmºœH…›M‰ôˆ¦ye +µ™¾‹bj#¥óê¬M6‘ÒAê(ß•+W +Õç]]Ô'葈›).ÛH-¹ë®¾‰Bæ&Aža.KÔ·ëœ"ËâqËôwË’Û¥›çÌòˆDh¥¦E¤•Y%uÿ×Ê×±b'ÕÖ*‰èP(K¬ˆÄ¥NÄ¢òpe©@í +GñR‹‰ÀJ%…Ša]ÔÓ©þþ×IQÑÛV¾ +cS2¦jÝGHÏ{âñàP’Nt6’Î\m«ÂãÂç»+j‰ÌÏQ<I¦DRÎs!Êò4nš ‰Å^EžŸ’©¶|ÓÖuN¬BìN«š&RÜK S™ ¦[±©ñ«Ý»[?:kÖ°òÝ>D‚¹ljîÏÂYXùM9‹<L§ÃÛb–?à±úP(þVÏâvìvl®²ò‚žS± ½U_ùïò8x×(_ÇJxʯ5¹mº™9“âÙþÏ7Ä(÷Çs®NB:›]ž²›2“Læ(ÝÈ°~XŽî…Ë“$Ï‘M>ƒÛ¼Å=–’Íx-&Í$&ó1·m+\±ˆÍklÆû)‘s’bs3“ÃY&†¥1s¶¸-«f¡6ÜèßøÏæì\˜“üeŃÑT©ÀÄb‹('0.‹,N"œSè]’´Í‰œxLÎKEê¸TÄI"æ1¶g™3g™˜*%¾Þþ 2±¨³žuì9#ï+3Υܶ_ü0±ô–»ºEËãYþætš] ŠôÆÞ¦žÇ<²n‹âí=-ñ‡<±=·'GÚ \m%T*°lK–DA$—Mjm…õ¦Ô©S]ÂZÝN‰h“¡,3L·%2[ i—(Ž.:ÉŒ3¹ñYB…]ŒñAÝ°Z™(¾¨¦x!•èg\iúëç7=¿Ó=~žT_W”éìkØU81ekµÚþ®udjÅàØïòïóx7Jà]£|Ýî’Ù6%×Ã7ðŒ[ ¶ é–à=`K-¡lJ!Ö¥M«_•Au1>¿®xz#nÊðÙ§µÚšê ¸¤s‰i·åc‚$)u”ÒrÎ-Ͷ…8” (2ò*Ôv\àvŸÎ…@JPE²‚•öZfߺçÕeË–YJ‘6uV¸±ŒËBÀÐ-ѹŠýö€éQ;.ŠDœ>9µíëB?@/‚’ ª¶¥{DÖ +…Ë'@ÃJºÉ_]ý$7È ,H¬Ç÷Šœ_‚粒 ›ù#‡cùy.X²h,vŬf(ò +»³3wߣ•Ñ+†{ŠÉJu„莥(…ïñJÄXøé;6†àXîïíßÞ¼¯¬¬HÒ-ó\ŸÂž=¬EM‘n[°¯ rºŸ3i_RèÓ ‰wZ‰ìI“ãRTJc%;$j*óÁÓ£Jj3µ}ª*¥!—Ûr˪šÍd̽ƒƒÃ.‹[æÎU¾9UHRB_2MF j\&qK»ë˜@‚KQ˜+úQiÞ’:[ÍórOrï;tCuõ¨%ì̧*XTǹŒ†¼3v'Ôö¾ÁîØ¥55‰ùä_óx·IàÝ£|_(kµMîqKRдÍj[Š,ÛÜ«HâPÊN¹Üᔘs¹þ¨ÄEšI'z|ò¨åX_]íXˆ»^íM—£ß˜"uU\ß!r)E¸•Óm#í)+êÞ³9&Ü4ßçl½á½§tùrúJ45XÆBK™ +µR°yP‚&ÄÎ;G2 XÊj”ž¼¼¤$c[¶hŠt?lÙ¦Iž3{',u(|r/4}z¸'žÌ<uUmi?:b«[úÚ¿×ͱŠÄá„pb‹„¼,6^¢ô= +‘¿Î¬->(h×ÞIüÛbu–,h¶a®—™^l×îÍñéši2ÂwI:Ë +çû‚¾HLËmQ).ºÄ¤÷f¨ûA´÷EBÉŒ?u×Ö+O§ÏÚZ?pÈyä82–™G?MÅ´Þ8ü¤®¨ˆ@ÎÃAÐ7þNÈÅe®¶Ý½‰Ç,úEâ¥ÜÖZ&ùŠg¤Ó¹e˜Ï*M i|šnqÕÍxuˆÏmh%×W¿aÕÛMئ!Â}ÑêH 2°;aDÓ±ÁrZÖ‹nOº;Žüû¼Î& @/œUÇÈx`Oa;>æ ÌÞaQëçT"ë(#‡÷KF¦Ï0…'dEzÆÐDÝg+³ +3ÂIÌ~éÁì˜&ˆÓ$³íÙt£Õ²ôW{¬ìº+›.¬ïL +fW®/ã›ZaŒß’"Ç &ôÄÆhj±Lù=²$}ÕæüƒŒÓ +N˜ +7…hsrtõrXçóêÂ0:À"Ö>ؤºÍi†ÚÖ-;ýÀ¥Áß›6] oC.è|{BıpmÝemÐ í;°BTG¿+JK³.ÁµQà›&¼‹×'ki+›©,.ƒBŸi¢œÒÌxúC&µw°w6ïì}éùƒ†m¥ Y9·hBqR·ÍD“X¦«æµñ%m‚Èÿ/w©«þM”#rü[Hû2%©ÒÚO4ôgf…åÍÂb„ÚÎ{H¦îM\5#¢(†ˆm¬ó‘ž€„u>Ÿ!ŒmŽ Šó»z7"J®ªÕˆÁ^sIòzìxvÃm¤Š¢$Ùôùn¿AŸ?wÕŽ¾ø9¯öÄ«´ÈÈXó¯y œÍ8ãhGÉ:ÄeRKH´©'`‰.ÃOnÓðíµÑ‘ÈûX!¾M\žÐ³á–²CõõG"ê|Éc*µ&‘¨n§Mùp®©¾~ØÚ{)q”hÝŠ4¸»×,”š̊ܦTB¨Q +Ÿ$þñJIKtn=^ñÿÃöïnëNM/ó~ÒK±‚òû_*’ X-ú¡0B”HS(¿Î‚RÅqé³a_ã•]±ó¡`~aš„Šþàùãƒ_u0) zŸv.¬½ƒZW,,íÁw#È‹cÆ<òáùX,�°ÁS·Ö‚Ø•h‰®Ò–V,ÿ?dµè4õkÕp=*Qò_.Y‰¦ ý^%z$v0–³¾¤FSV~%gõbQæ5¢[ÚwIq°µá`´Àò¸x\Ÿ9¿ÀÝ sÿ?á ¯ÇÓK,•ÌP¼ü7Sýþ“ú]W5GÇy%Ï£²ÈSÛ_îþ–£ˆ—:ú&Ê%¶0z®µ¿4ì–§[š«%ggçË2[¨f\$“P}K '²zîÜ‘�•1-2>â‘Ãfö9"©eÛsàÇÉ@ŽÜȬYPY™;aôù?ä%p–IàLZ ÊK#k;úÜ}Bºœ´æ’Rا¹Í €•Ó26ilì#uu£ÛàÙ±;^ÚýòËö˜q¶ùø~בs¼„TœMȦÎdÏ+T¨ºZ|ýg¾2{Sv±»Ö^•‹“""Ø%ŒJ¥Ð}!Û&Îñ³æ|ÿ`Sl]mmæ,[z“×!¶Ð&\Á9Ï#ðôàEÅáô2¬¨6w't*‰!X¸|À´n%TÈ"ù0¶óˆ¶kñèýç=7g%mêk‡ëa–ÀXÈÖͪÆF²¯®ŽÀßúæGúÅ3á—õQÆlÕÖ¶J‹ÇKnÑȘz‹Aì!FÜŠ”q›¼6é…{cÀþ£à8ÀM†pÿ$ÒŒ·¿E½|Ö¬}èÅùG¶ %ŸgalíþÎC1^0ëÅÛÐÚê +{CÓI©†{9QD¯š/ÀãÃåǜ봗SËz\R"fú>'ýÕݱ†ôÀÁ¥“'òªâÃÎ9ÎquuQ^zœöMË—‰2 + Âum‡¾þâÍõõÃ>ãÇð}D 3Znñ?ä+V8w sdÃÀÀfd¬Åzƒ²þww?íl6Iå®"ç¼ü‘—ÀÙ(Ó|éîÞ^*x*¸ÆŸå§–B±=/¹¤Cܶ;‰ndSMø3.õ庺cp¢#¬¯¯7ñoäãß|……:߶øM‚ ŒÃN?Œ~Bœ’0¶À=¦Í{þêÁ`'Ô„éYÂ¥Ô¢Ss^mxëûÔSO1luσօë¶óÛÜD@ǣʧ°<¸y ;ñ]›[™bwAÛ…Ë`ÅRÿÀO·mû…|š7f”ËêêôW£‰V.1èLî#²Pç^¼¥òuš@èø³™°]| ‚‡]fõ�4`6.ñGC´ºªé¶>ò—‡(/<x_UÓðÊf ¿Œþgˆ�Ð`ƳTø aǸiœsJ +ʪ,Ó¾ XÊ©¸M²¬ßXrÀ±zGç>ÚÞýC“e‘U›&B|”Olî+Ä[|p]Wò¥ Š¾ÁHÇ—>zõÅ¿�7ÅÉäiM‹”ä*‡Ûv½wõ\^â×i²2´úæ»®j¾²ýñ²ƒNqL[{ð\5ÓÃúÜ1¾ÑNòoò8%p:•¯°®3Y#‹J…ï«*še2wÒ±ïDÀgG»¥ž¾¬6»:]x?÷䣰x|2ó†hF ?Âm™OŠrB2R='Ûr +× ÔƒÍa‰ÎB÷AXœ¶m‘'D&oвÉ>ê÷õg£=¦§ \N(CI‡ˆ–2½È—ƒ1ç(NdÆf&âr˜”g{;¬(j I֖׌yø0mqŒFýG¾s^&;ˆæ +Í\DmJƒñGBYX‚¡„蟂àY@qÉ3¯„:—-;’ôP¸\ÜŠ7Ãu[tÛTo6ÜB›’º m÷fy÷”ð¾WM…C›MõfÇÜš°ƒØ:öûÃét±ÑY€”XÙtb@ñùvY&¹Œ2KŒ&Í—/!'E<½¿«0à÷ÝÁm2[i'5ÙÏÐî4,f•D·‚ôs¤hÿò$÷ˆ×y{àOŽ…«K¯*Ž˜{ï½—d~.År›‘·¬«¨ ,\ø‰äCnÇ)£nǧLJJÆN#ÿ>/³ZðÓ2@`TYñ䉳-S¨Ö ï¶tk°P ï·%šÞ›lý±'nþà ¹ê1?¨& lwäR¡¤¦U¶¥µÉÑLvN‡ª/èÿjDñ¯%»ØØ‹M&-‘½€½Ðž0³‡òÀØv¶w¤Ü2yMµøFè¹°xË¡øàe¤3½YT]ÑYå‘R5ÅáL•ßÕÓ6”ƒß–îgCÚîq¾lútÍh»JÛå]*ÈDêÌj¬+Îõ=Öh“&M:Á=òV‚í̥œ×SN}¦e%Yüs•OÎìSE?׬e NTu]uO4Ô]/¬JOÇôÁV¡O<º°S ÚíBIÁÞz¯«Ûp倫ºj +/}‹Œ1‡›bÈbS÷¹åØXùŒçv¤#ïF‹ÓI„ÚWÁíÎźMeŸÎ˜ð‘‡Ì®†Ë†ë†WçzhBIËÚ(ùŒá){P0cÿ- žm·½€Ü]‰uf¥5?¿ÿ»Ø¹Œ^;Òÿô¢"«Ú¯dï=úgwÓÏjX_öîçxèÅÞänÍ6ƒ[:šÖœSRrÒ…m¤½ük^g³N›òu”ÇÁ¾!YHܲy4ëjŸñˇ³?½ú²ÔìPÈq#œðcl¶â³`ÞŠ@Øõ#Wáõ(«k¨@çâ‡<^h~˜ho6>Ï`Ú*a(»oSÀ=0ý¨r˜ZésÅÇC±v¦ÕéHÉš ä–B,¶“»èÖqÏ1ÛýÍ͇•Ö¦Ý‹çÕ—:4#ŽÄ·Y"£•HÉÃúíþt +ê|eÕãÛ™¢vݤ]1%[86öd@SNFªí2ðÂ0©mdêþ¾º@:’¹DÕ †mÇ3:óx˜UX\R7PU gCSß3Xv%jÜn•ƒµE>¹Ä%fVzõ¹g’Ž¬ï³Š—ºÃ7"øS,›=PáuõáœcÎs¶ù·}õŸ¨0IàÂNZS›‡uêyîâý¢(ß|¶ÍµmÏ®½ååå£ÊoGìR—ľûQnówzÖþþËõ+QÝl†Ûæ½2%56—6åÊ +ç–—ZÇu䳓TR9½6ŽVÏéV%z Æpf!›™úãÜWç\ç¼ÆÆFv²y´•ÍKàl“€x:Ti–µBúÁUÀnäâMŸ¼}FC§6¨¥z2UOjeQQvÅË×"‚Ê®vÔ4Œs +6èäSÕ¦öO-*¾ÎM3ƒl-ŲÌ9TÞ#r»Š3v‘„áíÍmdî‰ |°×aýêh‹¨Ô¬eê(˜nÁÙϸ‡r¹–eð+•Mû‹Ï¨\xÅýåqÓ²‹²Òa!’2ÛŽ!Y U²iS¯mî_‰¥ºvC¤Æ62æ}ˆ¤zr¼�ð]^- ¤”{¬²BZ»»·ã ++^4¨+dóìEº-Uô³\ÿší}Úesk’ЖŽÂdØ¢‹ÁÂB).®³¡Øc7X™>Ý ž`…••”&³^fÒ)* +Ä_™BHªÐ0A´š^ïÀ¢bìL/†¼ ìNJ‘M¸ +‘º^Iš,–”4Òû0Þ$U÷ܹ£òy±=V‡¸ÏÃ=Nó·(?¸dr zt¬d05wKa‹&Ajq0ìvÓ†®X¥"ˆå¥p“³øaütm{â\÷KÏKgÄr$a zAH”Ê«ns‰Â¯“¹¨¶z·åÕÂyþó/®r„=ÛïfÔÞrUEAÚpä”?ò8k%pÆ fëû†f &» ¿à~P²[PUÍ’�›ýÉ¢Òá„ÒÚJ\]¾l‘3Ê=Šð˜Hù4àj-™Ð÷ìïo}Ñ \9’ýóiÐõ1ño‡Fð©Ñ6ø{Tœèú1?ÂõÑĹ^I|n‹qNàzìºóàotÚÙÖ›ø™IX‰¡j·,?LBãüyìA×· ”š”Öy]Ò"¸t/b”M§h ðuBmPìÊi”ZæCIQûY}ÑÉÓe3·D“¿’$áý°Ü]\ ·1=õøܲ²á ׃σbj¨ZålFÆ0ªŠ<®¨.fŸ¹ß¯i] 9ŽsAXXF?Þ‹’ð×hºï02Ãì1sv&àXµÝÝÜÝ«$.‚Uûä…Ü£}P%â]Ø5”À'¾2¶kËŠfÍû.þüÊ÷QYyF×Ìk(µ¦šý†ekO¤·&ü†Ï÷¥h|<R½Ajò%"9&'*vꎱ~ݵ]C_Ñmòuf¾Ÿ“kÚ+Bë"]ƒõ.&}þàW)3²¦´`ߊ£oc?ñ5e„‹ñ}+äúXo†´E\Æ�Ü!½},Ò>S‰²>]¹»÷ª†Ù£ˆt·i)Lñ°—Y߬*Ú0âûvæŸ?ò8Û$�ãí̉´ ågå¯ÃZí³t{†L…J&F³Òª‘µtQÄÓýòá;)åûa±x•²ÜœP®µÚ¯SH~¨ ¬&Ž¦ IÜr݇wÇÏ.Mƒ€‘ À…€x)·MsÖ־؅ëÚÓ·àú‹aœÍø]H° '“ w”r ¿pf³‡Rv§NèÇ®û%ÀÏ~$Ä�ÐNìZÊT䡉Ζøø1ýÌ ›¶ÙÈ2@Ò’?È4¸W‡ÑÎ÷w\U«¹¨©¹dÆ=¢X ÿïH"Q‘Îœ4ì™°o[Àec‰Ôøs¢;µÿhJî Š·5!]™”•‘l.|'ü ÆçÒ6ûWÆùó^Bò§‹f»Ä2¬ÿåTxN…k·¾y¯{tn™èüŠÈÁ±Š·J’ØôÊm@ðHQ²(Ê IBÜ#s^ß•ø€$ŸÂ‚äR†ƒƒƒ{¥±Ä×�QÂ?lèµ—'IÈY œëúö6¨À[?>£_:Þc‹’E~—!ŠŠX!Y¢çæj¢·f¤KpêMè¿M–•/å2Æý¸ú^$¾d�=ûšÐÖV<2†ük^g£N¦`Þ‘q:™G#?.§ƒæ5kÒðL…61²ZÕºûZ×1ÇŠ#.€ƒ6:ÀùÆ"“@a8ª¤?å2j¡e4£8Ýθ¨&G,)çû‘ý¿a¡¨e“Ù4‚ Ò•v{è=0ÜÊ`…†s–=®nYã¨r¹väÕÿ/*õõ/.+ÜçêØßàÑØϳºýƒ™_@6[¬'¬ÆMŒ5*#×ÿ*ŠÖaÆ ô4¦ÄÉ9š.snRKbŒ)Q k5‹‰PwñÆ®ÔÍÀò‚熃;6,±^n_ÿâËg ãœïbøsÂ>ñZ_â’ÆXzYQ1-“Ë<ÅÀýЉ{*c¦îçŒ}!Þ HI#ߤ¶ø=@ëCO͉_s†è46“<|/Õ[AôþœM\p¿üÊò7n‘Í’˜ïÞç[ã7¬‹&îA?ßDD5.mdí~׬‰Lôsk”z%Ü!‹‘[ÒñÁ_¼M|}]§6ée`¸+"¯@›W ìóÀ"¶e,ûnnÑÿ}d&2çY.‚ÿ¿>þGÛ{n»ajiëã +öº²Ú%°óÃ$|®óÌýC;†fìïê*„r>a1vÆ’?ò88-Êw],W¥M˜uóšÎØ-ÃÖfêÔÀŸ8Ñ0õžœa=ÏíªÖRßþWÂñ &ß‹mØWœB QS£ÊwK[bä[d#(Ã4üØŸ´YÙðüøvØ3%Îc«m ˆ'ãGJðlÝøwY?B"Pp¬ÊŸ |tÙÈÌڞ͖\y„ÌÝÁ•Î®ÅWÙæÓ°_‚PaÑA3cÂPÎ5:ÎãÇ’3a·!/ +(8>ìa_ÛÖ“úòÖø’FBä÷L™’–í¡&]»lra·¡%4"òJ´Xï%¢[oCGi¨Ã‘¥£ÁŸ0á@B½xo2yÞîݽÈ4>øͧB‰~ܧ†2Ö‚Ê’ý@< +—Å<`åÊÿ‰ôyÄ>ómAºÕm«ñƒ›_Z»ç…U›ë®v´2sï‚Ýþé—£C_]ß™ø¦AÅ;ê‘öŒ©‘Çõ÷X.�7¥)ÂwÝWÐTÝ"6c_7ÔÞW«÷Å–L 8vîÕ[+»DuÏâµ»‡Ûl–}péwN_5—Xï«-è<ÔÓ´NWøþ3nšIÏ–U«VÁõÄ‚ 0´õÃѹs‡g¶¸ù~ˆÂ-RDŸ6 ¼tâ·mûºüøû¾ÞÐÓSÓòJØVþ8£xÇ•¯Ã`©êuH8,3á"ä&LmhN( `i|!˜-…M³2ìæ¢h4üfÒ€É{À²hI[ËjSäám퉚W{7…þ|~WC‘ÅM(PY±wy öž¬=p1B–˜Aq#ØÆ~™´§Ò$õ0ü—ŸÓ™±ÑWVv|ˆ˜U“n}®GÌ”q_ÁÌ®Ðñm_ä¸?¨…Á†[šUJ¸ 넨>{U›:âŸûWXÚ~ó +ÒŸèG8ãŸÁ%©XÉw„׃ÝkýŽ3Ï7Åü¢àc°Ú-p9ì3Œì_÷lq”Y@¯;ûSŸFÛ—aõ–â¥qþnHMiìîBƒ•¢‹pÚÒÝvÆmh™Ì¯ ƒ¯à¾û“9c¼*,ÉŸ#(ö¨œH]uÕUš£ÔŸ£, 9°’-wÉôÓ +£Ua#ü¼ÿçâ ¡ŽT.ºß°Œ/Š^ƒÅ´dÊù‰œÅ>SûžAæѤȊùE—@ë±SÁÿ¬ˆäý>íÿĽõÊT¬·u~E]ã‘gÄIP¹¶,Ü ñ¿öÄs?¸r‚¿ÉÉlÌèÆó Ó¿ÝUs·G‡]2jî gÑû@¦áàºù CÁ‚€¶ý' $ž¸„¥à³ø!ð浘W^sóŸO«N°îNUï[c±*5MæʶK;""ÙŸ°í•Ó;ÔŠþGïkìõ½ãÆ%Ô´U}T—ì & Ÿ03Ù¥KþA…³2P4VÛ&ù¡!“B‰!ÂÏY9´ÝëØÆ>¨ZæKõÅݸºëM¶˜Šm:»P†�[º§/}ù¬‰œCÇï\õÂb®ïy»è«ƒÛÅÞF‰ ø4.¨h³ ë”ÂyÖAn8½8–ç¦þÔ‘ðņ‰L8“7·»¦DFáj#£¡ án¨žÆ¼V}+ÒB¹5‹Ê¨ò�8Öðâáp|nñ¥SM›M&L<ˆã9+“뻤¦¢ï$ã5A™¥ƒå·+”Óÿ×L :u‹,“²ñôͦ߆{B¤¿ŽÈOÐûîKwßrËPŒº·Áê¾!‘Õ÷€íwX_¨¸{Vv֊ز;»„‘1;¯ŽÒ{¾©çÙ Ç;”Õ;0X¾ÅŠu¼N*êlg×™4½¯ –lÄáTÖtq@ËvvÝt48êr~˜ì›°„ú ¸_0ÑüÍeE®ž?·eý�©6B¥ÌÜ°¬îX¸ÞQäˆs_‡¶~cWUDü¶f“*ÅžÊUm ‘¹.²ms*Ð?$‰FoeuÚnîÿîi²Ûô[<ýʃº¥ß°²‘ü}œ°ÀŽ´ŸÍKà–À;¢|Ÿ‡Ò*$Þ‹‘ÕÛ9¸y=p¨ÿíñ¸nöÊ–w¼äa±SÊaL夹ñxfp{ˆœ´tNQeå@_w¢8áiP~(«JXj’mó*ìÜMee f¹Á²Yrå¦MY¶`“}vÒCv™Ë(Ê"¸jŠÇUxå cíHÊê:[⥾‹áø<ª.lrÇã÷¦»Ã+ÉPêvÄE„û¼¡Ì¯ö˜-âK›¢ÉñÌ´—B!M%÷5ý%<¿;‘¥7Š…ŒÁåFƳߦ\ŽÝìÓ}4eÅ¥¬´rÕ¡€Z}„̇;Vb]IõlÁ–fÇõ\U¡—íÑ^lùåÇõEP„N{F°t¾(’`¢~=¾+ÜX¢¡=‰D“m^ø“o5laµd¥Ÿ"Ìý}ø8~åɦ¿¶çî/w€¼8&Æn›)¯i¶¾îÜâ c/XeñYxŽ¼#®@äŒöÅþ âц*üíõ%øþR)›R¹»†²ÞªY,nv,å#‹Qña˜•Íhl¸½µÖ£™éY3ëfTþ¸jÙ*aB…?høéC½œ¯à«wvù<²â¬Puñ®µ}™™’eÔp̓lï3š‘¨7º‹øÜ‚ÊÜÊÆÎ'«…[æ"¿$Kê:ú¤kYãÙ›ëªà@v'©{ånnÔd¸y9á’ -ÖÛ/åH?ä%ðÿ.Gáü¿_}Ü•«ágdAe2²r ž¶n¼ò·ì¬1]cÖ9ŠNêñÓr†ZY*Z¿òû÷õ‘%KØe“ç|[îŒ +È” +»“|£LeÇ5O^¦þiª7A±¹Èù®fèÏÀ1×-‹éÍTÃFPˆòdW}G4“¿m߬·¢°×óúMÀ‹‡+[ +Û-üȹÃekÿHràpÛ2é9‹J’õ;·(Lvö¬°B1x{‡³åÜÄ1¥°"+{”ÿ:ô—?7ŸlëîøŽõ’que¥±âM2ÒÖõ''1ƒWqV4k³á*àáì--/Ø榌ó@"óoªnßàò +ïŸâñŒZ†M¹\¥–ãOÃ¥±_ÌÚwñD6à)ø">ÀÁ%C ~‡XÉX82SŽã[Øл%৞”®›·‡¥@OgV-žT6€gÆ¡¸ ª‡È®7‰¾z„oad\c_×w§Þ“´´û¼T.a/‚ìÌdÍ[<ÖÔ™¿S5dxJ¹D>…q]†{Š©zÜ@q¸NVÙ’þðñÐ=§'rðŠ#ëk7-#‘è˜ìge¼â¿HœÌ7/ÇíO™–Ow‡_6›föû×ÖTÂ[󻣱ȿÏKàÀ)³|_<”¨#.D~x·Þ”lPÊÝm$—[xXÂé_ú·vg‹.œÐáϘ‡VV˜³…Å|ìm½égu=W +žªœeÜ"LJ¬j$ÁõcÌȼõ?„¥£xÜ(ÀàÛ°ùëYÅÜÁˆx;ÈUÔ&Sàmýš-È榦ßϯòéØÃòòìÜ80¢ˆÍñ ²¾¦ìÚö¡n›QÜDAðŸ€Ü5Üâ.(Ô—¿<Ìs}Íñ|¥sÎwò‰il±([dI“0ƒ5Z–½ÁäÖŽóJç!(¨c{&¿x¥™„]Ë*+“Žûñµ¾ùføpÓý’:ÁÃAefz„r4Î÷–k:NtÚV‚Ø£cA*·3¬¶ÁCúê*³'¾ûÝîåw~a‡f¬3ÄÄÞÊÜë}™Gà0_ÏL1›’3ûç Ë©ÁÁ= ÚÇ—.Õœ65-³ùü–j›JÆFÝ$TàÂײ…¤“Î÷Ã};c¯µ¢bô¯vèC îyÃ*u¾{€ìx=ðb+À±q‹HÄzÛ—´µ~ŒâuÎWrbÅhý{.²±»ù5V¸5Œ‰`«ÿ(±å0¸/¾çøÁGÚ?bi»;t䉣˜¬ëÍÌDá½OÊ1YŒëàGÿ]&—íqËÊ@ÖÒ:ßWSÙOÕ1Üoþ?y ü}8eÊW¬b“ãr9«ÇUæþ "y»%ÅU5µ/·{°Ù²:5Ñzâ¶Ææ €dYYLÀo�7ò'˜šèìéí•Æ–¢ž–m7׈<hmJ§™¶¦Ì5!ü¢›fEªûej°1z=TS ˜Ë¾ªz‹'ššþü‘I“R#Jà �¢bp¥EíB¸/ªQ0DÞ?íÊï7l½A;/J§a{L@/É%[`Ý ˆl÷q¹©²RŸÓDÖvzSÛB¢åSs¢S,UŽÁ&)5¤’ûö$–××g55ÉK®¯|ðùç;¯š4 õ‰Dúå[/,¹:)ÍÞpàÀÆ‹¦Lqüº#Ö¶ðÔ¡hÁÎÖ¸ÁE~O’ì¾”6PÀY›ê×[ƒjº8!46ì§ë¢Q ÎÜ&Љe·}~ÆH@ƒûÑ’ì¡å·=Û7}z#_¶b…hö§P‡™ukI@š}˜QìíÅ=XXÈ<¹j]C«'ÏѶtuXÓÜœÌZ¾@Ìà=}{Moq±=¸}¯Å®¼2<…û¸ îÞÔÕÕQdÓ ðó-$G*Ë,Ã÷jÌìâóBG{O\·Õ°•HêàªØ8²3hhì÷e}ôps\ƒÔoð +ñïfTþĸlow›\ê’}²ŒQ•Há „ü±þ8Å=¦;¢gôv›Yßzú<Õ¨:UWí÷÷ ¥|ÍX¹Ná~olÏù÷y üßKà”¹km°5ö~Ý´[ün6Ð &ùÉÏâì·Î†îd¹ºÇIr®ÍÒYDÕÌŒËç*“-¦¥ÔM°rFV[ó€Š}×J›°#c_½ Ú�í;VÝ>Ã-ãQp@ÌÄg†ÚhH‡eß×in#XlºVîÝ«^;}Þ{ ˺QàìAbnÓ´€xà9‰ÑrÔ@#v0bµ…ÐM¨Ø!cÁ@oPõƒÇoyGæõ"ؼXÐW-V.àS*%+»1¡ô$ù.ƒ¦A/SN˜-삽=ä<îi„»¹T2WT[¼–¬±¶½®ÍÙBTåhBúÇ$`ÏÔþ,ûK™;íL&Ò‚qAÉÌÍ“]Þ¾¬¡“d‹÷gÿZ +&/QÊ|îëa¤þÑÿß„$!ž2ÍEÐêw€ìþp‰÷éjÀßFÆ;öÕÁºêλÑ"fŸÂÝÍÎ"Q®› ÔA‰Z9ÓSx˜%‡®)|§âQKYÃÕC,·Ÿr‹i»£h07TfÖÔ÷öGÉF'OæuýýŠ• }ºtéÒáÆQÚKÿõŽ +ì®méåµã}V…±p5@Ë?ƒ§)È»5ù[óÆ{z0çÞ’—Ûb¨$¬†ké/±d×ÍPúHTóÃq !W @Ôó�»Ò´Ì™’,È\庪¶`Ÿ³y»�½yKùoòxg$pÊ,ßùÈpZ…r>+Ët]Š¸$ndz9XEo6|ƒ*ƒnj]¸<.‹ +· € ¨¦ýêñ×(ÔŒª„e`9ÁmÈK‰ÂÇÃOØŽ¼ê¸1ÔˆçÔ—n…À¡1Tà'„oÖþš›+€&ý¸N©}¿äÅ@Ô° íHYn1-ÖÍD£Ç¦®þ\:—Þ)#P)×,fÕšövmÁ¼y0ßÜX*óùAx@/EâGR7‰'©S¸"D15L\CÌTçšáe¢¾À%�òä–Dç0^ØPD¹pÐ0¦¤Ò¹ò°,µ¡ªðÚñ“üsIÐXןƒ9MÆÆ h¶ÇÁpéñx¤UBRÛûÌ‘(2^‰-öùiÛLcwÀDËþMgnpíâÕŽ—åÈg'¡á²Ù¼’ŽR=èËy"èE5[2©žá ¬[È%‚€äY\”+-Ó*ÐDS˜2¦Æƒi‹†Ã *ó†LÒO’ç.ÿHFMf™Yí¶6´öº”içe^ëÏ*ª©¶øÑܱ#ÊfÏ~²y瀺b¼o4€¾Èk}„Er„ïðëû^/_±bXñ:uC×àTé|˜Wy«ÌÉK!íí‰ðn)«9)×#óy=štÒŒ +'½\v‚Óy œLÅXŒ»pßÜÙYø Š«Þq$¸9rÙ»æˆùŒÊï]3ðü@G%pÊ,ß‘W·N¤,â³Ý®ªõC8~ô¸mßØönÚÔéfC‡%ÛMl3�Åää?lAÊitḠãÛþâUØœ{z~5NÄAº_y[mèÙ-ó+‹š_íŒ/ÂÖò'àO%`,Gcƒð÷›ýÌ2ã«DÑ=ÛtE¶¤X…á|Bº=ëWƒÆ›%xŒÌíÍ^7ÇHÀÎÝhU!‚:)èˆv$x4îXAîᶠ.Óš ¼m#´b§")©žd*›™PÖéúœjƒ¶ù~XàXcì5ruaSýÑõ–¾ä(:Ó¶ÄW<ÌŽÁîÑsâõŠû1&çH_G€ÍJ™óàӆџ“¹Ø‹}ߜғÞ\òÆ1’]TTD‹ëë™Ü7Ø/!‹£,jJýH"Q[F}9f»J#…B"§( +\² غŽ£¸Ëòz$£¡Q6™sUÑÉ‹ä$1¸^mÑÆB¡etŸÊìÍýñá£>ÿÑA¼Ô5ô87®Löwcí©ÿX4ÃÈ—À8W¢jêãŠÈ—�Zö›ö=·6oz:Wÿ©Ï}ÍäºC(ñÈ¿ÜÐÿÔS'â’GÚXÙ<,r‹ÒÒ2ÿའÂyÕs¾„%~¯·¶ä¹ú·paŒ\6½ÂÝÄ®üÔå±\¬ÿêI“°q{sÃàlw~,ÇJà”+_‡äÆѸŸ˜r¤|úØî楶؅‚èjïÝ°ªÛñ÷-_¾\¸ýþ‡? I´Û*¸s·R3øì‚Êa¢òáËëÖvÎwQú Y––Ãjõ›233¯?kY?t‘ì¯MœF˜q¶Úý’Ìàf„5›ú¹WȆdÞ÷Ûü`hÉŠ¬þˆÂ:eVCc¿/ÃóÒú Uà+ÐSÉ¡„S†–3;“%ÞJ+“jgIOd¢ßJµgŒ’T禕È…Ÿ§3Ñ?4uUÂçIܵ=£éüÍ9tFY0Š÷ŽÖ×Õ9VÞIÇÞ +"¢¬˜ôôùl»?JEo7ôwþ¥ä±¥°ÞÑ{Ïo"¨`Dh¿ØIk„JÚ• +!qCQ,5ªKqûY”ˆé�£}S<ðXv]ñô`(#‹êð¨^êŠÿÐ' +·jܺÕÓzÂIávîû¶Ã±@ΞŠ{~<Y–iØgM¡gHÝÚŽØç%y7Uû¿Ú¢oš!9vêÿýzǼ2·ûKÆmqi¯|º¼<÷nR`[ɨêôj%Äau÷Úg{Ç>GcçšöJà”+_gU†oÏy–Q —R§™¾L(ò€;F,„ÛíÿƒIºÉÏrO@ÁPÎý:`káëKÙT[ÛõrqëHõGtŽeVráe÷£dü{‹÷8‡‹€t"ÓVdármß÷:)/‡×]’Ô"7-šE`h–õNÝ'Õ¸faTqÃ/²åV@¹`úÇø±(“àß…hNª<ï»°{WƒŽQLGd@„±2y§ær*ÛužX¸ÔñûROHu«ª:‡»ytaéï¯íÝÙò1øwÖ£€é¿¥Ó=I.–…²>gþiχUÛøªV¯¸¤âHñÎ5½PÉõ¬¤ÀïÆ®â[=U5oµè8p¶TZº›‘׊áeÍ.ÖTuý²º²w‘éå·ÜùA Ù8 5-Ñ-;,rýñÅ•Ex¼Ž]ÐFd›=;%pÊ•ïºîXUæÚ-1‹{=~nÓMkÒI§"`3 _-ĶxM*¹óÚÉeÃ$:[û3¥2}J©<kð}R€>}| îÕžäU0| +aùD%ÒK)›Ë¥rþ`$hʽwîÙ)úü¨ÆJ V;•’Ë`1V¤’À[’ºq¾O.HZf¬o¿WMó?®yCÉ8¥â3&]¿}¥, +±*·ÇN¯.JB«®ü±S·î„Eî©ÃKbßµèRšê+÷û‹Tš]ímÌn¿ +LrcÇt¶½6BæÖ×Øni:\ê€JƒH*3'E«BÏÖÙ-mcÏçD œòJ c¡ÎÈÝ‚)/tü(Ëk@Š^ÁAíE2œ¯E€!줊þך@`دזB‰MEªê‹ð^UKˆµlÛ8T]]=úãéM)Ý”»÷eË=ͺåžr¯84>äI—ºˆ:© `®uâóyWH :DÌ^3Ó +‡Êa°MÔl>xŠQµòs‹š?½z|ÑaLftWÐO\’¥iÅCRÀLòe3(¼ìN +3d¿rkÕìóº·vµÄÒDcÐ4˜*‘Qa;˜³Í½^ÂÛ(Uü¼Ìo?ùÀÿ‰Ã*mìugò½cí:㊔NuIn bø c\® ëi8bØ`÷’€»çÑÊÊJ¹%N}2ƒ¬ñ/áùêgòνuߧ\ùd1cZw�¹`‚ï{Ùsƒ{€ÓÀ:VE9[‰j?f‡;F”ë«í9ê÷ÊuøÑMéM x!_[8kÊ1¥ÉËýÄpþU¿‰¿ó§™ÿöÝ J”FÚÛëî(PÔ=V¾ŠÅú/ eúcRÒ×w?×Õ7o^ÞÁ^ßßï”bÝC(çÇ?˯X“ć”î¡”—H”Ý 0ø¹AŸwp÷xçärÿhŠ÷žÞ\Â%yõlªCPE‚Âtï³ìÜîT2Ûy7jži99\«þçÈÈ"°åŸÎ9ïò²Í/<VNºÁff è>[Á®š;¸wû+~_é`Kó®¾?=þ¸uý]wUe¨ç_¸,ß\¢¸VT'º}swý#æLÏ/ßÿ8ån‡M›6¹êéß6þœä³öÛiy>8nAJnÉžT¼®?´¬VÚŽó_ÒÍCÀaRPLÂö¡Â`,Ó»5ïJxãFý¾ƒÑ;œ!èÔ©x½EdLr¸ˆk�e+!Ó a*ص„ +�Ã+EšùÓ@zä`%Ï�F»þ_UH«—ÓÕW¯xøáã†øUcç|Û³øß׊÷6Õ‰]]Z:�³÷ŒY¾ uò»ˆg±‡›Ÿúq�ˆb7‘`Wªo†. +nfZ9ÐxVz˜PkºåÇëÜð9çÙØ +w‘¥;‘‘©N畹„‚¨<ÈŸ\PtbE—Âç鬛ò)W¾Î_9ÔWƒ$zðSÙ�øôÃ�í÷˜¦ýˆ`'6ï4ŒXL×yY.Ç£àeñËmH^Ãla1ÀþLSÉ nþäÒšk°uÌè—Àö¾¾‰CúQƦ>XD„+½¸ÂCÅ©nYS}’èEj÷¤+îÒˆ5ôC¿`±“„ (@[¹€r}åÞUh¾å–[†à'Ǫ‚"¹¬7kd�1É’;—I>}óì7R—ßñ‰é�ÁY ÉD?—ëÖ3©²pw=‚,@É–¾B05Õ{¥Ìw:„íëÛFçÚé{€[·*ÿAvðw¢yV‘éº~§VQð4Ú:!È9¦ûüÛ3 »Nýјìè –ð¯(tøéºîo•%¤ ¸ +æÎt]wy¸ò³Êk–_ü™;g4 ¥Ì 3C„ë;àþ&©<iŠó`úæ[|WIÀ*Žºá' +&PŸëdS£^ÛÃ&ïÅ㱊 ÙÄ|•¨¸~€-÷˦𫜬>p[OðL¶…@?hrùû3¯_~¡³wæ.L,èFpï9l´ê8gf,uQqÈ€à™9ö&a"šÕ(§µ§¯,Ü›míŸñìÁè…ÅÈ-dÒ%~—ÛæmP&Éèm}}Dñ:£}jï^É0ì«$ÃnÕŠ”Ö¹e€r&z^@ÂÃêAÕŸ™)å{ýxG”/8PcÒ2²os(¢(L°máJÛ¶P#Þ qòUp4|S°…OigŒ¶>ؘH[rÉCÈÖJ…¤2Eì}ßÚ–t ¾Î+à¿q#ÿ‘¿ž‹ÚsÙtàEøï ÒïAV¿.#¦ÿ˜‘Õ7ZÀlƒ€'×5 t\^âë«Ge'ëmn8œˆDŠ` Sñ†KP3ïAî½yÍÐPУm.ê5èzXÑ¥2•žÚÚºw”°çtÉÓ ¦9zD“ëDQÒÌ!öúUȺ™<Þs‰s»âld+V0Åš€š/^>kÖ1}5ØEŠ"¾uÑ«Ü]±ÒMX`J¼¥ .‰!›s Œ£ëÓ5¯|?[§Jù +N óˆUá(Jîµô_emý–©}Ͳ´éàq<üfAX-Q`õ¾P°çS¿¢è^¯û¨ÿu'v”3‰ÀÜŠìÐÄ¡áÒA{*ù3þ%@뫉ºe|Ñ.•è÷ñœñ +Wa”äF°ØO„›9ÜÌ>Ð2/ƒž¾Ž0\^^ ¢‘Ajú"%i9üÍÌ>´¸€o‰ù?\³×£VàIù/ŽoóT~¾áö/—QÅ;+ni¥Ì¶÷¬^ùÀð 5÷⊽™äªAj1ø–þŠ‡Jƒ;JÛ!ÒQ”/UX›†ÿ‚õÈݼßç }[/"õðOZªåçNå\òmý}xÛÜŽÂí—ƒ³/7)ˆÒµEMýO××¥ÁÁ‹ÈÊu:5v؆R*0},”‰à\È‚<å1"Ù¯ÁŠ97«¦ý Ä/ô¿2dŽ«wÐœÕf—”BµßV÷]QY?“A¿Oœù³O¥S·l™gNiiHïéé[á$ÌŒ+nFtãáÞ"Õvz’Cê8Ã3Sru˜¨<fºì+©|üöºiêßF©¥M=ÉÈàî¶x°EäÕ{›÷í_,/Ø~2ꎹð4}°\P]u"—vjÚÖ9É)Jé3‹º õ kt[ð”Ê]Ka±;ryÏ]wKšÒ µÌžJMÚ#ØæKH´šÏ2˜íFJá3ºÇ¿þº@ÈÉÞžãÂ;tè<)Ÿ’|šîî[wó¶-ß&EqJ¡-DÚbDî÷¢Kš‘ö:[ú†ÀÌÛì›ØS¢`}…xCß׺ŸV©ý:8”C9É a�Lcç¡:‚druFÑ´•ß2½õ=ü‡ýö3wßS}NEõ‡lÑýdbU#è@‚ ó6×¹ù�â + Â(•WÇ5ðèw¸P¤UÙ3ø%:BŠøÂU•á=Nù ¨NvÙäûsËë>Ü7!ævÚq—ž–-hSuõUA2ºP}eØò^ŽŒN±{ׄ„!¸ÒYne3ɗꊎÔ߸î¶ÏÕغt'"%–nI:å7YDú¬®ªRLþﺬ<d‘ôêóýà5Ѽ˜Í_´+EK^hÍ”Žðyœ–Iæ;9©Þ¶åû2h—Ÿ»à`O ›†Y.{Š‡•å%%Ù;†vÂ5°ŸÇPRrƒ‹õìî:˜¸óê«GÈ@†i¬Ë„d‘±£y‘íËœRAY;»Ž°Cø=rÊI'‘ÿã?žv÷öz]’ï”—j纾+®è@óV]ÞÜÛ»³ísÒÆA¸¿}MT퓧SœuyŽŠãñ“59º`cë= +Ø)i}¸¥}ÿ/›ÏŹð2T3²§X"û†OW¦.úÄgÆuÍøwZŸ5°Òe¶o/�ïþvB"s‡ûþÐÜ¥—|.hùúTT[ÉÅÄu¯ÕUF¯=26¤RóOÙj’leµ‚:y†`îA ì{(s•œ[îß4<ÿP^Ž=¨3žU哸K9ïʃèñ0¸µÏˆÅìÈþ9?½íÕ~2ÐÛ$P Öºeb/È¥âÓ`¡ ‚Šø’Ááëͤþ¬ $î/ +Ï9÷œWìˆw|ÄÇ‹\²HAü�ÚÄW5—?{ÄÐ9þÌüçt Ì*)1�%k·{Ф6ŠuÈË\²0Žz½Žå;rØ—"²/dÕŸgþo~¥ÐQž£Š×±fE*Ô^<ß-��@�IDAT€UDð(¾Š’ù: +gÊ•õ—͉p·$°i“Øô.ÁŠáöíG7#Ÿ®W‡Dh.ª4;ýCËÂyŒ[3Qi–l°Ã4w eÅÑy½ÒÞ�ºãfk¯nPZà—hJd~ló€Ä-yåqÁé£î0'ɨ͒$·è&í2ˆ6Gk‹Í[ R¨Ó5Ï|?ÇJàm+_§9Ý/yyŽõâÑ]’èóx†ÛM±ô.½wp¥áñÌlö�üVÿÎ(»#i±û¦–MœÑp´„üÈDoÁO'á‘7JU$%®†ŸB¥‰‘ïó¯ÿ<À¢åk§˜eͲ,D +ÍÌêÏ z½ƒÂ1Öi=8BRƒ]ëçWR'+rôX²ì6Nœ šœá_£ìNŠÂ±1Tö T×Hݬ0úeÎÍ/â![%xF±°Ž»O™sŽì–¢i›°õûÌ\jïusçŽfÞ¤Cå¯ +Dî¾tÒ2âGY%W‘_²4^WP£‹#ˆ²—N|±+¾xÕö¨ç’©ƒŠ¤o#‚hJ’¸SAÄT.zq +|tùãtKà”(6™±N]´öƒ8$2Ã¥'ÝÃ+÷)®.ÔQÃíÓ0WŽÎAÙ ÿdŒ¼†ÅuñêÖcVÝÕ>ª0{ºFé¤W‹²{zeg'ŠIä6 8¾J“˜†E¸…z–Mv¬»ûͶÉóXÿ¦#/WÁÕ¢rÈø©JéæwÜTü’$By1û!Ë?§¸ƒOöV¾|aY¨—£ÔO·Ìo_|ù8bòs² £Éí&»mMúÃÅ“+{÷"9p4g·øJîø¢É_mþ0êL}'è-ø¢a±ÛPVë×êΞ¨:¶ «¦õ -À>õtWWá>˜–•\³’H·È`XÊV@»ìùͱÀ™ò{Ÿn9Ÿ-ýå«75¥]D,ow�„(åS§””Œ8ôSYî§(¾rÛõw\™Ù½qCZU×¢”Ì´2)pŒòu¨(‘$N&;´XÑ-:ØÛ;v›y¶È-?ŽÓ --=H5復‰ŠÉ–¨¬<æy‚³Uy?òê(’ŒiOEØë–¡ªÉǾ"[Qånšzxq¥wÏü’t0¿¸îŒ*^gÜ FȃŒÝô\“‡zv¦¬¦>„ +(ήšô¡É€•“ô¤U?…xJ�й҈ÿdHÖ_êëß(¹å´çü“ªq žÕTdS܆÷Žy7ß>åG·Íj}MÉ„,į̀º¦{Í +óòK?û¥R\–ÿ½9Â; ÇÛV¾(ÓâeU“çq¦O4+µmV4˜€{wÙ²á›èv$QA·We$0m„€Rwát¤Iâa¢Þtæ˜-’c¹XŒ£¾F´…ŸÍ„Ë&W!0p‚ø4È&ßÅ–€S@ÕcÛÜni<4ãì”-.Þ~\¬ÀQ¼ò”9o!¤® +¸C)u,ĺ}2ÖîÁrGÐmʺ~üM“I÷v µ5\PSÓ‡6‡ñ²Î5Îq†§KÒŠÞŽŒéu6Jžtæudãé%Ô½éÔ‹ l#¨rò°hR_[ºÓдŸd4õ>_Pþoó¿x<«3áœEçbWú)àêG©Øÿ6šŒJ!+>õàâB÷˜»¾@Ë¡œÏ +……ké"ÈóˆøL㟠ÿ·ÍíàD¥mæ¹J7ËD»6Õ-QXÙX‹Lû@Þ»°#~=Š÷.C-ˆÒ3a¬"ÒAžÓ²Ïo¨)ë\1ÆOµ¹)@Õ÷"Í�iú¹ GiRlöÊáôÿÜü“@G–”Zö”’+6oò?SF#ô ý¥LV~‡gå9CÏüJfË„EÔ&Ÿ‚.ž”åûÕ$¿æ†uðÒšpÂé¢á`4’ä’ñHÅ–”KZ áI+fòdõàŽÒ;úÑ©âLÊ%qi8”ØÕ“¨¶á[†aý@GÑ?ŸKü5àe¿ˆ�â@»iÉ®ùhIIf쀋¿ö…K“2{U¢§{mþY©°x3IÇ.08›ß=ðÃ;æÏwPDÈQA•˜® âS¨^I!±ƒv®ûøvÇö‘j$pÂvíïmöÀºuêôE—ü¼$ Ã0À‘?Q&rwãrí”)š£X7{C/¥±ŒâRf"ã‹‚òº%i”¸kQ4þ_dŒ¿ML ®âöÔ€x?P(¤µõÅ(_gE>†›õïkþüwŸoiˆÍ>oèd,E×b$ä?6(æ%iK³÷‹¹™z|"Üd1Yx‰H‘!fÙ¿€csó%UݘýðN˱–ý3Ο/Ù¶"Ú&ŠÒû²C(5°ç´àŒâ3s8 JNÏN^Äî^¹DS@c°–"EÓ‘D¬QÂ>v@ •ž…i$àÆNà÷#H çº?ñÙÒtSßø(¥rYV‘K~ØÑõd(º_dîÍY-µŠ7åœëGýäêê^½{„"+MæLWÜÎŽ y4áãÈ©ùÿžb ¼m>_‡¬¹Èçüó¾Ý¹ª‚Âs.ÀZ pbG¥Ï7ŒÑEa2Í»»:£ûÇwÙP¡9Ýþ‚ÂÄ«QÂ}ûÞÞŽÖBôUу$â)e˜Ýh;DÑ–)øBšöx:”îôôåð)~Îê檫«Í¬KND}rß¼ gØj;àj¯×hI$÷†§‰Û3áw¨å&{ÔÊå~âª.Ú²(à>†mò¶d.é,8b–¦÷9eŠ²¦am¹:¦ŸAå;vnF:ÍtÚÕb'›f––ŠCí¸äž|n%’š¦€]hê€ßý§‰>ßèNà@J«NdÏË»Ö+Èß³5á÷ ÞìOõŠr£ ÄëÞÛ“%O>ô=§œÓè"ã´±µÙ• HÉÜÀ`:ö¯Ëß3Š°;¦üûS'·íó=:Û!ÓÑ]ëa‚Ž•‘d§vÚèÍu\¾îîx{ŸV2Í{Á^6ËîŸaÐP·X;êosü|9f–!ñIŸ(] +ró“Hƒ}} ÇøˆOò-ͨé]ý‘gIp‚HÎVyÌxùÅUŇ--ûQ!ŸµäÈ»#\åôâIe°Î9wä-Ç–H6ó âÖjJd;‚]¢Ï<x<Tkä‚3ñêiVrݲÊÊÜ®††ŒÌío³Hà±—¶¾¼©ù«ÀìFåÁÁ±² Ålñ*ÒÏàëÝJe;«Z]]Y1»®X¬VBãý_¡Õ=ŠGüÒœ›o;Çq Ž×pæ߸â¶kfŽ;íCcÇñÏòþmû|Ç +jG,]2ç›�Ž÷£üïZûÛ_)Çîü`^éŽ]Dláž´m1Éô¹øþ¶!ÔÑŠ‹"ilµ†•õÚÎøð°†Ðõë¦v@—ìhb°¥Ý‰£ÐóÇ?™˜•ßžkk*’Ô½‰ÁþÁøÕ¥mÃèâîˆÄyÆ°vÊßtOmG)y&ÄQpεN«ÊÊÔcâÎß϶ÙãÚ½}ÞPÄó øÀ›=}í¿8¾H,Èvd +§0&¯O.<x/ÄòÛ¦Þ/�atyHV~³´(,ÆWäؾØÒ¥KÆsGoŒpïôéN±×Ñ¿|Ÿ=58få{»MRÝÌr‚j„f-Óš2¥°n¥€{Èà¯úÎø9 +!¯‰2·RY½Rv³[³Yzþ^þ¢ÁÒùN‰ØÉ&¨W%_*PŸŠwtkõvÇ™¿þÝ%E((Ó¹ý}î’2TíÑP8ðá¦&rþû¨¢øÿÙûøªªký=œéÎ7óBS˜ bA,AЊSkûð=ÛjmûŠ¶jµ}¯µƒÒñu´ƒm}¶¾ÚZŸµí³*Š¢€2©A‰B27¹Ó™ÏÿÛRÛI߃óSîͽ眻÷Úû¬½öZßúÖIï‰óÁºçP¼ ¡Ì„4D%íÓ]ñŠvnÝßÒ¢Ú.siaO«®~Ûs!žúGîÙS1)ç€è“xöˆá¹ôl'éݪZÞ>àíÏayRŽY9sɦöxÆmñÍÅ{W°ôŸÿÐО7(ñÛcÇÈJ`D•oP‰ö‚¹ì •y¯¹sgGÇPk1ˆ.gÞVÔzÌR9`šºŠÈÒÊ WI]Ø×M “¡hÐ5ÔŸ’è³>䮫Nºôÿ*õs°Mc¯ÿwì hF§`›=€ÝNŠHUÕ_·n[,ïôe˧öƒ°š–¶â«YréœÝ=ýK^EEñý๧ûk?è©€HØëó”_ÞŸëï€YzRËTËRij~�å,×ϯÖþxÊîAòßÕݺqóSûäxô=€?â¸Å)Ûøœã°š±ÓÜŒQåû`6Ix6ŠZ3è{fMEAÑàÄV‰k±?1Ë[…Zm?Æ~槩¸ù3NœÛ°t¯ËÊ%Ce»{·ïûðý‚-°Â³´ôÁšãÓ&ÿq"»óé&Ç`>ìœAâmM¦¬/«,¸m<õðöFózrÔ:^±b…‚rV_êöø‘-ÖÕû$pçwkLþ¨Ã~8íüåSVàÿ|ûíNO¶¤ûúí¦æ©ûO´Ô‡Y©b1ZPij—´Ò÷&]÷`´/7-ÒŸN[ßv%¤%»î9¶çå€ïá«Oj[°P·¸î]‡€ÝAÉuÞ¸ê4wÃœ£ò÷µbD•¯˜´©mëp߶÷H¶È;gÈõ° 4Ü»¨<rÐØ]×õIV4,MÏê$`}¤–]»†Î[¾|¹a;Ι¹‡,ËÜkr¯’Œ¥ÿ}#< +®’GÍx}®µáÍßüxÕ€À³ +e#Ž“uñ`wÿ§kà‚û�¤ã2öÞU&3jkoÌä¥ÜkD†Ø=®ëv¢ÂöU‹ßæÆ8Ù}ÿ¯?ÏØ VY6"¡DÈb} no‹çÖCy¾ÚÒSóò‘Þùßﱿ¿ª¤Å%ºïW2µU™¯Æ†Yç0v·L$êÓTלkŸLLúÝügÌjüwÔŸþÝ÷Ñöû™mÆHv +ù¯5‘™ö«¯6½„‹¸s8‘MžyAÊð>“´éxÔ×ꕘ½D”<p#yê:T,È´ÇóŠlOž—ôì>Í1²9Ty'‹\dóÇîuJÀ´°Q!ˆì2>}âÄE7Ý€d-[ùiË/;v[[<ˆí@¡=v·[€ŠÑü÷‚€fÞ‘þŠfçÆX'¬`Æ” dÓ¸ãø4$îôö´îòeg¿bQùÖ¢`…XüνÓP'i´téâÊãý_øB}¼·o¡Z]^¾Ö‹$&8ëÜ)³²ÊöãºîÁk¯™P�1ÎuÔä7«Üž“füZi[ö^ÿ¢ø·¦9UbÊ'ý_ò“þ={ª‡ï£{»‹ßÛ=pÅäÜ!Œðà}Ç^ÿ> Œ¨å+š0¿¤ü’V9vwÅH‰ô)Šuœe"VjÛ•>bõX#[N_1È£Ìó–•K=H=š[î“•#–mmqlk=ÞÛaêóãöÇÝëïëòØUgœ¨¤¢ +µ‰óÑ“ßˉö›°$ÿ& óû$¼·ßGÓØÏüaå®ÙÙÅ?(Î.¸XØyã%ëèÀ�E±Ú Ý| RlofÔ‹sf©©¬0,)öçm]Es¦ÈÖ.uMųl²(°qU¯ø4T³ó²¨CKÐmÀ¶W s§ˆäa%÷vç÷9Ôû9’ùÿ[±IŠqº= …E™¥•ðP<I™ó”k’[núöÒâ%¸æ™í~`…¿I}l¹x~ÏîíqËL²fÍjP _‹í3x¬½óǺ«çÍ0 ¯,Af±P¿GQYZòØGLуBAœºóÎUP°wxŒvËŒŸ“äŽâÑ67(‹¢gÔrºþ™Ò>ÃfO¨OÊs€"ËÕã%¬Ò€R!OÂƤ�ñ$þÅ. +]B`À—8¸=·ÕL+¶EÛèLEU©< +õKˆ´TôûH†ò¶üê¼3 +Æ(jði7¥ºbûlË-´)ߤpÉíM +p5àÖ îsZï8æ³J÷òOýû²%SÏ9’ÛS¿·º¤zçÃ{íNÔûÒpTås’K$=úµÌ}) 7 +Ÿú‰’i9²ËcÒf…fáY&àÞ‡p{€»Û˄ˈãÿûŸ·|a‡gmŸR¨žñ‰O4îI$ôÏ-½¬ø¹ƒùh[^UeJšNvs²g/À™>T!ø’ÌÈ@ÚHd«—Hp&odzê1äêÿñðÓO¿ÿ¿‡wt\`S#Œä]Î÷aŸum猹_G¨à‡@×ü'*R<ÇÏyîmØr „Û”¸)Ó)òªA9ýÊŸx5co€ÐIÒn OêÊP(Æû}ðgüç¼ ²uºK/~èP�Ä^,D,®¿b°ÔöÆ5¥Ë³÷ÍÌ í?/÷-ž+ÅÖe~â}?UT±TèüËäÜV‘À‘V8òùaMaÍAUõ|éBÓñB ;ÑAr¾Ï ßD¶à†í؃àå”/ºðÂ5×|jÂUW9h‘ÓiLGÜòûÞ|“çMVŸùö=yªêLO¤e‰nÛÒB_PJÒ>ê>gÊô#“´Â×MÃÚLdøá˜üp¢qo))ÉÈçà=÷¤n¸aÃE㊄Ÿ×%Øg§@WB<ˆH†÷R‡jÕùxÄuqI–- ¤9<n™RÀçcz"%'¹-åû}”=û/¸³ P[ ´R²èCÊ¡&ù*”øG&²åøëß÷ùÏß>ËçœgäŽ*güøXo<þb~N89s˜¿ºö$H([ž>ÔœäZ°¦µªsk·Á…¥‰ªì¤º·û×.£ÿÜýÇ™`»Î/5§ä×z‚KÙéHw˜zìõ“$a`‰™Å\‹Òâ‹þõÛK?¹çÄwcÇ_—Àˆ)_¡tW<*\·žûôáزƒGúª}~°=ÃÞp»gËmI§ûÂ*B5ww¸,ÅÍþ.¯8 LáZIqéÄt#!¯W`"³€“b»„÷½'cgŒZ �AM?ÊT:>¥ë[D’$+4Ôß2"Â8¢ZæEl‡§^pÉŽguÄôÒLJ:Õ½+ñ%JÆ{?OˆVzµ’´—×ùÈoñ°™‹Ï ª²OB¡!ˆæ_jºGéü¥ó±Æ\Œ¨‰IÎÖŒøwf—V>¿¦¡÷ÉUÙVðÃ{›ÎQÂÓ%b©&'¦,Å>’vÜ|p!YáYT?—½'žUIþÔ{©,õtǹ�g:<5•ôHö¹€ý%™ýo|>bn‡GsY1JŸ®Y£pâšÄlÃþâj˜m6»AÏ•YžP± +ºúºbÄô‡ÏîV•:ÿæpéG»÷w^z?àA¢ókÔ?yëø1'ÿÿÆT8=C,ꄲ4(MÛÞ %rèƒHÝ ¼SkÅÖØL§~ÊeõÁåUÄé²&c³ex œ|È?Éü "+SÌ––3@Þ©MÿÛß £åØoò©K.š£•UÔ£3xÙrŒÂƒŽcùŽÂ¨öÁAHÚ¿L.k=Bû6æGs7ö%ó\î^ï—ùïõúF‚'?Ÿ +|Ѷ‹À{m{ìP¹ÈMºû¾-[2)Úb¬ÖîêˆÀÞÿvÏÄß Sn{Ä0'f1í¼)Óç$lóUÇöÚÀWh¸éå&g×qƒ-tUVóÔþ†ž²®µÕŠPvCXQf ømÄKùýêŠ +Ô–o D +Á*Óô¾0¾`bŪaQÛSnìØ Î( 8éãèó¨Wiºî ½é,¡’ß©5¥yæfùšpŽ·úª«„öƱ#}]]Ì°m02ÚÐ5šEv騳؄\úÑOV¼y$ž³½£÷Ó“¾Å»)Ú»Á©ýU“ÿƒrÎ/2æ•Ø®{QÉÂ8_WQ¡×d‘¸g[àšµ€÷uú³ÜsTB/’¨w?³Ì{‘Ù,Š’^©)êÝ›Ÿ}ãÑc~à=ž'›avs|ü¬ï½wñNóS|7bÊ‘å6ÔÆŠTgœæÅû{-³˜V´ÆÝH¯éH?‘å8ùUU™màŒ‚WÉDž„óa«·é*K¿Fi®¤—´µõ}ÖâÚw.ãÌ™2@ù¿Ö™±ïGŸBFU¤ûÁÍ{RÎ?ÞŸuªÃwyR¥),¾ŒÕí,®¯Ìõ^Põz*¾ÉûªÁ¤k×ÙÔØøW3åÎ8¡¾ÐÙPÿç’Šûs”»@ª²Ü¬or¢ÞÞYÝ|n6Üœ6£�Â+¶cÿ6ô[Â5!”ö±ÎºŸ˜U^o[ä»–¿èÓx˜Ò:RzòèW +{žJɃ)B·’Êßýë±£¥tÏÁƒ>T¹8aYKIÍ[\-gœ�ÿ—<b¬f«Á•'“k* ´§ÿdI¥Â][Šd¥÷õ¶ö¦Ö¬2‘:Ì<8lcëÀQ¡ ŠÈôÏuÇø$i?>’‡du¢ìüÞ”Ev$²áýã£ÍIFqÿ/ÉfìgN ì'C[7—ó +ˆçCLá‘P¨ïŽ“Ìd²©z¸¬”QƒÆl×RI´«¦ˆˆ"yÅ#ýþðÇi±$TÖqŸv¤à3"¤ï4éæˆ5Clûý“ç^%ËôK�äMÄòÓdÛî§C{Ö/X° “¨$dš6¼òÔ@²'+7gÆ™dJJGA‘Ö‘ñ%kÍúÆž)qæÝ*{òÝ6k%.NúÝîÄÒ‚ ¼@ejK€Ñß"¶s‘ÍÜõ[Þm|ûºmÑÃn7öö˜FÌòoÚ™°\Êr<Ñ`R/Ø»l\QƒÛŸ¬¬<úcÞk«WÛ;Û3ѱº²Ï¹8øq€ýHâlÜ3P½xµÍí¥Mó«ŽêþÆŒÇ~õÙqþ±6½œeMr}Y–÷-¢{_O¦Ü+«ëëOêSÔòËÊ=ÉþO›ñßé>M‹Ï×áÍ›c˜œÙ&ù¶Ï_êëÿéPû^áÏâ@-bhžvžö×lâ6"A¥Ía|û âýÜ‚¢·¶'儲Âßtm~¯ìçwdëç]ý7oîH䳂ɾžÆA‡Ü<ts +.‡>;ùHÈ6fúdéã–ÍvÈœ4éžù™$1¯ñlçç:Ø>¦xÿúl1å{ܯ°~™»LöhöUðµÙD:ä‚– +Á³pñ¹K§ôÚý~h_Àò8’É#‚„Þ»Ýà¨úEÂH}5œþAˆX¼`\îöÚìì–‹f$}J½;cgŒF 8Š¼„2~%&k1çRº²:“öšá5ÁØÁ�OÌNêH„kDÛlµ+®â„(NZº|Š +\™vMÇ1]nVè‹U…eßÝÝžX4xýh’] jÕ©4ûIø¾‚œ”~;éFÑ¿Œ[A¸eÞU9ýbs¿Ë=ú~¸öBà¸xΣä%•K3Ͻí…Cà;>v8Ú•yû¼xËZ¤ûÿ´¿×eçß듼W‚ýCk_üå”MÎ¥ÖsI]y&ð +2¶Ås=v¼£FÌí ~åù–öù2üêim4Ò=Ïq"”Jw-!œiœñj²-íN€þé¥å‘CPÈŠ&å|V§öÓ%ZÞ¾Y…$5èËkâÁåý¨˜STäµ´´«ÊJõÁïß±Wc_Ž ¤ÂW—]ò2²ÖýVê#e¶ÒPZêÝÙÑ3_VÔñå†ešñþns+êŸ(èZ*ñà·]N>@-V;-ß·Ü"ÜW9ùÇ +åÕà?hËÄ8Ùë¹pžnÞ8uXÁQ#<td}WW0Ÿ&v’äþÚ¼£õávtuM´=í~Îé<Ðt‚`ÐÝáXìÆv3Ù^äS.A!ƒ¯SIYÛ¹eí ¦wP&wÕ·d:\š¼hjÏ P">ÿ#-‰ë�áËöôöh®â¤Ré\)Áš~µwcϘá4(¹·¿Ž˜å+nmèÁC Ë´PŽVTTÓ9âjš2¸B“SRáÝͽ§×gvâtÙn(hÿºy\Þë3Hr¸bu#9óPùŸ«díšœžÄÇ'¨¹ó†®¿½+cŸŒF |kΔf#ªá:{e7gwII¨Q4ϧªÁ]z+¢ß’$~}Q~@dÁÙ3 +I2ß ÿ£é:¦¾ +Ba%%Qæ±âšßBùªÎÛïÅÒúí°øbN@™¾~ºÄ| +wõÏ¿·sPñŠÏ€2»‚32…8_‡-L ÉþtI4¬RI:›5Jk~iiíÛ\2Ÿ©.íýÉrbøýÒ}û¡´¯T(Yˆ*?a!¥<i¹?�Ñ5©�ÿüGÏ]:òQ#Ú?ZŽL¢*Ø‹mK#+&4îΊ¶—(Ô^‡ôÏ…\*‘ÿ¬x¬µ”EJWoÙâB¬Èk_y’ÔNO“ƒx§Ë5™pYqCÏ×Øq6I »ªJÅâL6Òi:"‰êèÒœµ eZ+³é¯víßÙ'”òà÷ø\ç!.Êy9……6”®›öX“Ÿ)÷ƒ[ün•˜Ï/,‰î œíuu;)£vn ¦ìÙw�:*ÈP󀮶ÜäouÛýŽÌù\j™ß” ý8„(Yí3A0(Ï__üÅaû˜æÑuaMÚ› †iܪ›N1¶ÓM2"æ®í]Þ²Exý£?{â]Îî¿GTù"ØQˆ¾K•yÚr%å‰âôë¼nÓp½f—ºy¶â} +É]&Ïy§ä‰ HûØ·çuD¥�š`€Å4! +›|žÝCvvõ¾» ô ¶Æ)׃¡¶º…øG¯ãëBmëf eŽìn9¼3Ï07ƒ°Ô�áuBÀM¥³m))¤õô½÷&Ç{ƒÉtvÊvpƒü¢<‰ ß' >UQš¡Îš€. v ¢¨Áév5uw÷²ñg”ýú&ˆ‹&ƒçx¹ã’VŲ6¦«‰'$BÞâY®@…B¿†àhküªú€«ºp/–ã¶ßw$©®ŒjOâ¾™…ÂD;éYTY¬R†“±CŒ�öú#y<óFwò÷ƒõ¦ý¯mÎ<™=ÿÕiË.„s¿X`Lf‡B2yÉ)ïLíãF‘»d<-COEwÔUC×�ß…½þõ¼¼ƒBÁd{ÇîuzK ;•*î0ÈfÐz×{î‚8jâÄaš%™žôÖ‰ºG¿°9¨¤ÌxÚ¡ªòIöTÉùãüþ6ÑÃí± +îºÎ9ÅÙ9“™ôB™¼Ù«qô`u8,ÊËŸÇ«íýÿÄÀ*¸—°xÝÚÜÛÚZ®( ,‘™w›ç²‡¸|ÀLöNU|òy¶Ž3Û<Ä%å¹Çö½öá–´Øj,x¾º:ÕÊ*û:v¹q‰Hë%™UIÄÞÓ¥3íßekòŸt]²‰%_¾îƳBÐ¥“#®|Å*™"ٵȫŸ«Èî£[ÖÊyײ•¶GÊ°tnsew( wnn6PèOX&™¡E·÷&SãÀÚúü³³3TxÒé2À‹n0@ƒòÀßìÌ íS¾eDGÙׇu}<ôéF¤IdS⤉G-8lX¶0м”¯â·ûif/ò0P›z…–ËZmf.ž±á e+.†‹¨ÄÓƒÀðÏGóûº>qôþÏK’ôA6Û¿Ô6ôúÊÎ;¶2¹Ë±Û£ªü>Èô:ü_�¿8·,ź¼wÌ+íƒ|Ž“£°þåÔ5.)Z®d˜¿ëBõä€D¿å8.ó1þhš8ïÃs|_º.ox²Îz#jDÝb�–TT>ÉLk¨L$–U:í=ê“ž‡ß};ó³˜É–õI† xø}ÈÑfX•T ÕØ„õõ¦”xÂÒL‡lV9ÛÉUÚÅ{Öl …,Ç”aèN£x&ñ9®Ý†�Û>Û¥Ië„Ë!L«XÁÝЩ>L¤\|?±…*Ë¥9è$±u>ÎMu¢âò}bÕ*çÞº6ÿ1ØÛL£q>Ü0ÊÝð›‹X‰ä9_’Ué_ OoKQdâ6—ƒyó3ÄÍÀ÷”ç/m—¬õëB8�oÙ´w/Š]¼ýXQ™Õdéì1ϱá~-Ÿ{öXŽ] ¢–õÄéFîÒ0“¤EÁ9 œ¶pûŒF˜ßÛ%sòON +V?ù©Û§x¼gÀ"#¡3 +P6Œõë¤+fÕ"sí5Yæ–œ¥N½d‡ —wÅæDb[¶î-c²2‰–T6ݵË+(ÿxÛ²ÔÑ) žtÌÿ¶†ufJ@•"ÀîºÔv¿¤3g/‡KŠQ¸™-à +“ÍóKTVÏ +Žf¶]ŠêØŸ€CÓšê¿Òë?vëø˜ç]ô*6®oê3Õò¬º£¼\•PzÕ+Vô”’ðü0ç5‰;3<Î_ûã]÷>rCÜp9½ÿvLç.péqM_¡Äßà„ߢ„.ÜÔMž=/—ÄOïÕÓóEÉòŽM}z9Õ }=Íþ€>—re<÷ømmiq€t"篼¥ŒiÜúúúíµÕÕ ‚9»ŽS¶|ÅVNˆL¼fQCmDÖ% +×TWç ¬ _qÚÀäWÚš²Îï£ÌE†gõ-aŸ +v}d·myI)Ç•3gÚªÃ_W™Ë%&5ºížœ·muÞºÃØ»Ñ(€¤öCáÞŸ²ãÏÍÏî¬)ÈÚ='?°sznäÕ™ám“#ÁFËçÃñØ“©®Ôê—ý‘û'Áœ“Êvó2óE°vínjÊZ½¥Åw¢¥å(l:HC(—¤\Æi¥–Hçôh”§èÛù¿©¨ª¸|Ð܇ê_õú¬GÎ û·ßyçNTó@r½*îEÛQ¿»æW?Þ]Sê~OINK2-½¤ÁË×™9pí ÚÉä´çàž6ÇÓÿCuœÿF½½›“¾#{dmowû6‘%ÚÁmo¡c›‹õ¬ÒóÖ¬YÛìì:NUùÒ]±Xt}ŒD·"%±âü¥SÄ€ôM1¤÷¤]«ÂW8))Æô—<ÊÉžWT¿$ +•@kÓU8Ÿb%_\`(L6]ËšŽ:®)ÜöqÃ*\æ¯ø•ûÙ5Dgwo“zÏ°hýhAiéÉ8<XqnuuµYŠŠç¡¸ãù…Á.æZR˺ß* ™Ò@Â¥å _εEå¡Ëœ‰‡2·„dÍ´nÀE1ΦÞ{áUq¸¡IÇû…GÛøee®•vç:^ H”õÙpè£å+JoNéxˆ¼¥Be¿® E¹¹9UÌUr+b7…u™s°2öVד!F´árYqÿ\Uv [ÑwYž¹ Ì iFª™”Ç=ÍýYH¼’x•Q9ñù|gø”•¯kH—*éØå€Ý*sù½•Z‘þ\xß|ÛÓ³VÁá»�àì41u¿*…TjåEÚ¹Ïu&óß-XþÂGn+Š5¥xÝ@¾j´û•Ç‘ÂÀ™M‰ëÞµ‹.^*Á£–öð{?z%0¯¸8Ý–èìE W°§áãÌ5ùòO}¦ò e .æØRÇõDü^O ÒbÄJ£Ê›ö¼hˆ„îÒe†Â€ãnw]úö˜¶±iÓ#‚À£ö‹ìÊ;-Çìp8ÔŠ&£³=�åë¡:Eþ€î•M!_±Sxº±«PU/¢Ô×{AÛžÔ¯JŠ·‰O˜VR–Õ{-\ˆÙÃÇe¸ð®E%5Ùij?g¨ÊΫ�²þõ÷–pÙóaƒl(qºkImí˜ò.¸¿á=à·ö›�»ûá»éhwVeVfr§m£U"\çÄï[¼~}fp£Œ‡$›¼!«ti‹ÓiãFÓ¦síËäà·<óLãŽ-JÇjé5u§K’½ƒš,Õ(?Ðj÷z«äoùYuŠ°²CùŸ-Í.ü‚•WtË›MÉ" ãêÊ)Ÿ’ƒàÐw¦‡r¯Ùß;oÑ¥—Îúçþ}#¾¼§úÓ!bv'›ÀÊŠtõ6º‡Ç¢YͽŽ‹¯Žª¾eT¦Ø¨±Žˆß?þrR<ê©KŸ(ÒZPÚ`/Ey‡8),X™9%3•¼? hÈ9X9¯¼ú²ð¤YË£šúMHüŸËEv!ýŠ£˜Ogh)õd8}?«ðØ/Ž—'èŠê¼ÄŠ E¯( ÷‰ß˜Z29 +üþ$HA*;۾ܘÉZ<ñºÑþ÷©Z¾Ä¶¸ŸqV‰ýCŸÅÈäT§‘+Æ°Ðï{“3;Õ¥§++*2=ŸÄê1Ñç:®Dßä(ωü¬Nü(2@À²Âþácp2š–ÜÒ¤ÅrS–©:š·å‰ŸüämþÑ>@g{ÿÒª·TâÞõ¶ã^O÷SnX.„L2Ê•Ø4ÀÌÎI™æ-MþYîÞgËä>à|þÁ[o</ÖÙ‰5ß,Åf¹™ùf¤6»^7E:²‹y¨Ú¦«`ïœ<©gƒ¼ï@¿ÛGöÊ"®µà’O}*²ÈÈÔè5_rLû6 :unæŠü]EbïÚ3fS±Kú‘ª,,bOÖ¦Ï)c’|uÀÏÞWØÞ;ÈO¸/Ä×<eèó“� +Z¦ci\~¾~µ-È×϶L¸SV¾�Röl¯Å‘hÌÓõuí‰#Ý°·· Ø£›îN[òÕÃß[§·£Pê+Ê××júù”[…i×- 6…Ÿ<94a<.•ã}ÁÝÃÎ{; +%@Q—y|)** ÎsØÝ?zn"3¿Dwlx!=ØN„%ÍÆ^¹¢)Ð-MXî¹ëñ ‹óX°Ì's@ÿQË¢aÿTÓ e_-ëé5–ëì€õì×]Sdg¬ Æ÷f檸n4WM›–4u„ÎTÕ4Òyð{á?gÂoï=²)Ýÿ·�¾´ +´×¶¿ÖïZ#J€ Å«M™{!¾½ç) |ÃLX›÷¬^íð_rA¼Ð:ódSãY.3žÔ$ÖÆD‹x®‹/P-ÉÙ7=ÝÐ<áy£iLNYùƼ”æPfYºYÆ‚j\jj>3OX¯ññ¹ >\›Xi…ж¤%†�§WØDž7`Áæðœ&µ÷û�?.X1À²Ìt”~yº·A)K-.Ÿ³áýþoÍ‘TT¶Fé fFémifü :+«}‡®À?žãGŽ&Œ]÷‹ó>á—¤¨ç~›1æ¸6“46fv\ ³T–…äõ&^ݽ÷ê—MÊiﲋ4ÂÊŠã6͆¦î…2æÙh—¯Øþ§›rl﹟l[= ¯(=ô@GG $eiH>~?ânS›t!1ðëT‹>x؀𫇪ç\ H5á¼é–K¶x6¹½×Î~mÚ‚÷DJsbÁ¹ƒãë»°@:öÅ~)™ïó‘_¡zÒr^«)ÊT''YHDðÃ\Þ•.¾4“ÎŒ6æq8åÎ…i¨IEq€´Y:nçvåçgî)³¯®îhI—cJ)H‡Ï9 ö“êPG}O°+µ' +…;(ìiÓ¦iàÍñ\?xSÜ(BÔ§ÜÖÁ{½ž°ª‚˜0ã¡·šV¼~Æ´|(qB–‚Aäâ1¡Öiß“YþÐ�δ£ +Ja¨t<8r地n#¼CÝÔâ¼î²?W¸¡oa~$vžëXYšGv…|Ò^š„éÎ’ã¢ÉemSÀ'hW|ü3QYñÝ9•†¾C³ƒ(=D?̵©óí€yL$f±LXtÑl€¬ïÄ–b&eìeF¬¯îmC¡RãˆgjpŸLæÆ·*\R½jØ3›îïÂï ù¥HŒ¢¾¸{™Ów·ÌXÜ€eŸ§:Nri2·¬¦†[y[™ ࢣT Ÿ²Bó…TXÜu¿ê;oZii&â"` çO¾âò›nšü2ˆçsqº»ùŸŒ¨Ò¥p¸-�{6ª÷™–>^ªòåååY¦k²æ1[éÂSwœe<x¿±×Ñ+³³ƒf¨ s3™Rºƒ=vÝþnÎÜûû²}½Ë—/7J‘±g‚:NÛå )±98"²êÒ“3VgZÕ_h¸ÖBƒÈY —œÊÉó$ÉéCP!cUþÆh|\«à¸sÕª!+ÿå†í)ÔsÛŒ¬íeÐïu™§!³í‡ö@ú‘êR2Ä{ÁdX§œÏ†šÒ–þ -/k +'˜ÂÕ�B�_——•|hÁ®]˜©¡ÏlÉ.ÕÀTx‚mxäí}µÇÜ‘ËVÞVêR¾Ò$ÎKfÚþqn(gCÇúõ4e$¯Ï/¯¾õ÷×|j‰–ôh—SV¾ëÖ¬¡†íÐÓö¨mmTÂáTGòÉÅqW&LþåÂÒÈÕ/î.–m 'ÄdH %‡™í=B,ë·Šfoµ]'’Š)a53!u&£&ñn» T±h¨îXe4H}¬“ü +tøtû0IÇÉ Ö?ñ¢`NÎ!Ù¤÷mYµ*“))¾¯®ð©U2Q˜5QàU¤©¾„6ÿX¼[¤–Þø@½O"¿µ<å!”SyˆyòfɳŸsTõH×êÕ£^ùêó&òŽkÎù׫�t€‘‡ÚZ=ì°Gð,ÿÀt¼˜mÓæ´‘~tá„‚®ãäî¹AÆ£Àñ !eoBaFñn8ÒSÊUí6ЀÂ;Œ¼ÔçúgÎÌÒÅõÈ`K&¬ÔS 6kDê¥þŽü|ì +àc%XçŠPýIÿ¤œý³£$vxÃ;¢�ÅÉÇœÝR2wyÎq휲ò½yùr“yÉ=MåÙ{ÇÜUNuár°ÓŽnSÖË-÷ql]&Ùœ‘TÎz— 2!NÏÖ”A÷*E6±\ÃÐdÀÔ¸yGšê2dfø —T£Î›¯õÅ`ßÇ^JàÁÕ«u”7ßI±0ÈÕYõÀå—M!ú„‚`ç` VXG}†t.6IyÌ#ûvë ™Xƒ%³ñ1ÙÅ…ã +l·çæ…²X`¹,Ùצ\ö3eO©¬\qÊÏÄð6žŽï•¬B~ô)>)|ÑUÐ=þtMƒ:£<Ò§jcß nÊ¡šPxCϤèÊ9íFŽ…)qÅèÝK…ò| ð?Wngœ¼¨(^évT{Û,0½bLŽYÞe•EM]±ôËžÓ»û²š£…MSU³¦‚ÖóCÀÿÉ4s^>†t"‹>úÉr,¼çñµ½ÇŽ¨1lJF×1äc=…nypš§jÄ ªª±Š¾ÖÖWïùDϲ¸„R/®öqéOrÆÛGz?¹¡±±½¢PûP¡¬DÂy¤·A7´Y=¸‹'4=s¦;‰« êœëÊD¤()ßS¤3ñÒ;V¬0_ïJ<Æd¶Œ8Þ´¨ô®m ½/6™í:y†ÜP˜/n|Ö%—L1òidaçÛ~9QU•qUÈ”‡)\]HðI!T3ìè\J:V_`·×!JÊñLßA_=ù«Œg¢4ßj³•ŒCªl<o甼Érš¹ÿ6nJh$óTmVVé{Ì‹»ÍÁ,†ªPÃÃþFb–»„0ïCãòKúÜhq«'yWb'{ƒ]XÞ¾Â-ÿ¾¿½©¯¯Ü¯;ùTÛ›€þŒõ]QS%š;Ô˜l®ÌÒ¤À'+O lrf×,‚my9•ŸˆPpzÎόΆú߶Éw]7ÞpdµŒh?VÁĸnöÂk8£Ëà˜ŽeÓÂJC íu¨³ÙÛ¯R‘jL誓6[%Ic‰åéÃÝ—Uäe¶)v¸`~ãWXÔ›€ŠaîkÏ<®öM›vt9¢ »Ùi-ŽžöVOñU‚Òð×£}>^YÉÉí7Œì¶x²¸+¥—©¥3x «‹Ý„àï|XLOÊy¡ÿª%$%(N5U-sµˆ×öîéSC¡ˆD V¹pxC” K¤#ŒØçàãWÍåû+*F7{Þî˜Q7̼D:õ˜/ }�–g¡gÛÏÞwÚî+¦yÅ>Ÿ‹h-³T5!²Þ†OVŸÒàÈ^Çp5T)"Œ] õ¼‚pÑ/w»©µ‰D»¤Züƒ~.Î¥Ò¿�r1“‹Ò¡\÷é½»kŠá‘<vîO–¡²òŽã¼ÔÓØ¿_ONÎÔ:Ôý7x%~4…å¼”öË…S¦NÏÝÚÞ’˜–—7´èÞãL}ËÚˆ·ý¥îîIÌPsLb_êcÒÓýÞ\‹éŽåÅ{_Ý€^ሔÐdBúg@Ïæû©Ufî³Y%Ùu° †xzøÞC<)—Èn}„_$Â÷4òñÞÝp¤%°/–œe;Þ]xÐßÒ%@Òú¦Pe»Rø«ˆ§€7$„ɵ™·Y¢ôóS#þh‡»±-m5˧ùüz"±sQyîkïm., ú‚¶%™*IkiÕ—–QL’îÞg˲êa_jêÒÞì^,KÎgóTe-0ºîªK^§€ì* õ£ÿ™Ã_GÃ-/Ÿ–˜/©›÷# ã\ÄlþÝŠ…ÿK +´¡`y䳜¸×a1+Âáy(öc!á×Ê”ü¢aCËÃ+VTgðÔ/‰çô÷›Àfƒ“Àþ£*ËIŒï5)ÇnðLå6¿¬—¥¹|›°>Âm‡¯XP:äãÿKm<>?eÿ,ˆ!6³Á/ÊÍÝ¿°$´MÕ´_¤¸9¾ë©'%àó]\>gi@œ'°–YÈK&ÌÝ�Z±XA¼±1ã“ë±Òȳ÷€°×yËj8Ò<î‘Á潞a˜ ì>ü‚帿ú© Ý|Ïõ&ÀÇ5®\¤©òrø0¹C½?Ë®û•TûÑÅŒ•ˆóÖëµé½žŸ´"A²ýYïK»Ò‡]g*«§Z™J»ï×¹®^}¼óÕßÔ\—š¶Â¥<%õKªôp2”:mëVÕÒ¯8§¥£æþõZ†ðJlNrî–s³}-¯?óøAo/c‘ä.K5™õ:¡¡KQ›épý”`{h¯³šü!0ÞÝð›.ã·TÍ+.Â3÷>·4Ü(á·Áü'$`aqõ®ÂÀíPýÞwû·7–&MO[\˜ÖL‹\žSÊd! +?ò™A;eËwV.¿fæͼ½vvTäÓš÷·MõÚí(û}{Á :ѹ‡ôDÖsøRËÀ¹È¢€Ñ‹ÖŽ»'-goº¬[Eø|áãuöõ…Ì—}¤«¥UT:>É<ûèì‘�¯ïÓKÃœ¸ÓL¤«•›¦“-˜‰~=¨¹»Öí^YC†¶µk›{ÿ{öÁn§¾'嫇64÷ÌseT17pLwä°¢0Ó±çs‡o}i|Öë£Ýò½Gc4T竾hýòß‘‡:`#ãà®9¢)ÔT¸ü¤gSBþІŽMO´^uÕUH<ºEÁt1僜;b± +k}yVÛû»ìƒyòà غ0µëD�î2Óï%ì{ó"Ê—ð÷ͺë\ÚúbtãðjÂîöìb¸>—³öM÷þ¤å‚ÿí|Å–Xž¸SUù/ÁߺY{›ìH)½R ûzjgâcpÊÊ÷¥¦ÎJ&ûÞÇ9ÕídzwÖø¼m~"„ñrÇÀÕ &«&²ú€ëë#>íR¬’%ºß½{ûk‘M£¹Úy¦Ã–¸–™ÀÊø¼iõܹº™~ý²IE™�Ü™(ر6ÿc$�þ�…Åã!¤»ÀS&9aÕõb±dVaa¼è‡u]SƘ4Ñ‘ê¼â¼Éñýãõ„•-Ûj¢WÖ'ø(IxœN ±Ð/Ü÷ˉ÷M×ç[ |®èÓc «lJ¨ìWæõ¡hW>,¢Yuº¹-=ž”œî’ÎÃ]H@±ÄóÞ‹,‹×îË;ˆøWfwñZ{"ÏUƒ€évû‰£me‰2Wº´§¯û/\‰€çU¸ä x /`’7ÞL)ËÝN°>åAÅ.Ú"vÒâoa€‘‰³éçÞT€�ßG©f¬0HÐçñ/#Ãk¯£Õ²¹wëºßW¦¸þL:NÙí’$+a[Y®ã„AžnÁ3ä—u\ +V37‘:ËóÙ «»5,Ž1³^TP�~ …5(kR‡ïBEɹQQé{¢QxÕ 0—3I°cmýÇH@,ìSB¡¤7ÍÈœis +»*N¢xEPþ|ÛºüHãà–ŠW|¦Q)Iã¨_Ÿ`Ê,™+ïÂó~.·âŠwhþŠsGÛ!¶êÂ¥ÐÙ¹þ-¿¶ë<„¬³àpLu[ǸÈ2í5²z±Ã´þÉOéûûK§ÿÝM›BIOžÊm{ÂâÅoeœÎ‡²È€c¶fƒB +Ýu5v]0©´�¿ŸÃàzˆPF§ÀI¿Á¡ÊÁÅõ]þ÷~êS^hé)Yƒ2öƒrTÄ‚bn¦§@îr±Oõj(s"ÙŠÇ¿€JÖ—ã»ÔYxÂòÈ-‘ùï.¼þLz=eå«$ä489÷ÀﶟêÖĂÜ(ÿ2hü”tšÞÉÓôÇ ÔXŒÜûA;)ê·e°RíG*h-gä\ì㲬ÔY–ó�7ãCœƒçŽ½ŽIà$Á”¶´øTið¼K&æÄOt#X,}Ðr¥I(´ÍåÏZÌ]àÒ:—‘ÁëFëë£WßT´(Rx…;qÞ»«G¸;‹öûMßK©”ôRkŸU*3É€2ÞÑšè9�S Ìq„Z©¸U;~¦Áèdaì&µ†ëQÄ ¬úƒ}–m?‰$ +O™X‹EÎÍtÇR©_Âèú’3¶ëiý‡‰¤©ù³µÛ ñßVÔ_ætô¬E=ï#^±z¶Iž¯ø!0líDÚòíVŽìGjÀŨyx¹ªB–AMPX G~ý™ðþ”•oWk³´B�dÕÙ‰ãK¬ñ–-[ÚeÂ~ng²^vQæÝewÇv̦ ¹åò{ Jy£ié]6-+Ï~•FEDóL˜§s‡?àÃß‹6‹-ê𶗃w \ÉZU•[q™PÀ‚¢PTXyÛâá +Y0s‰Š^Ò˜ä¡ëÂŒ^Z-7T¶Ãï8úÞ+ÌŠpªLs]sVøœs2I+Â÷zÙ¤P÷Êš¢nÙâ/ƒÊì‡WŒÏï‹P弨ª4wëîœÖîFd§Í´LçIY¶y sþòýlÕp²Z<מÍØÙ³p_|4`°U[Ûû.Be\Ewé¯À§óé#ïåPX½T’èµ–ëåÂ_X¦ê +Ç迾î()þЗWetÄš_å–ôX«Ûÿ¬Ï´uå°0ÐM¨@ò�0mª$=o[ÍCAoNYùžSá·aµÁØç@>ý1WÁöîþÙ…ç/Ï>§0ø¦Gäø¼‰úRAhÉæ‚‚¦G}=M=vÿÀŸ•NgÓ,*REœxpÏ{=;% 0ºWÜrKÙzˆ„òïë[ZP5Pñ÷a”°‚®Ì¨Ë- Ð]ïìxÏ÷Zž•¿‡M|��@�IDATUé+y××þµìz÷M+Ÿ|å§O¨f8Ö×á z¢Ø½¥ÌÞæS~N÷‘âšÝï¹F +YÙþJ,ãóÞæÎ)ìr_}nÖ5ÉW!Ýf¤ýî,(Š‡er^DókJ|$I¥É b.TÔÜy(\bqœ_j�GÜí¨Š÷'øy/�ç7áf8çõ–½}·l¨v&“‰UjÀ¾ÚÐÝï{Ôþ5ãü㞧ž·ú è`›²÷ìYc¬e £Ë¶ÝŸÁ)|e^@ës¼WAkù•++ÏÈË)O¶ß46Ú)Óè0=’%‚ÓQ‰Ô/VCÃò>L/B„7Á‰RKÍœƒÊ³Œ¡AáŠHå¥3Êû$@Wn¯[ÙÖ¦æ7÷}€*Yׯoè-Åy£Ü”ÄØë‰E"ù²§,Ï]6·¤òBÏ’Þ•Ò¢+žk‰gÏ+ž¸´-M¯-ãbpšº«Ææ¥\ç?¸áÿŸ2¿ô>ÉG?b +{S¨mñƒSUÁ¿…Ä^˜ÏuÀ +«à2ÐôhÔ@LTû|•*qÕTdn«ùýoù}!± å'‚WÂ÷º¼*ÿ‹'6‚ã!Ãæ… èïJÇâŒÏNZúf\B‚ +Ø™Ò/(°Ž|t&¸"@ŠÎå¼iPýGŒZŸw%úsÕf[‘Xy÷üE—ìhŒE™ã½†Ê!ȸ¢-ÍGˆq´”¹ ×™@êëÜ49c„ö(ÇÒ“I¦<ã¿ÁÇ¿ÇXWè’ç«- 7…8÷L;NYù’ @úˆêÝÄére/[òåNÈóû™CPáB&9|óÍ,Lè*l1šÀ@õ{Ìí`T"™â™Ã…•ìM/àL›ÒG´¹&‚ðõ¤å 6cý0güðóÇÞ~ xq8m}2Hõƒ„JE)¶åæÜ?4àó[ª:ÜÛ›ÙEqM-ÇC”\½M¦ñ©X²?øY³cY·Zº³ +qÞ2êcCU0„•æ—HX_ŒÙñßÌû¯«ËÐ&ŽfÉêîK%Òõ˜«VÈ¥C\Âçê÷^i¾Þ¹ôú{ïô™»ïAíÅ+'tj¶¾“»K‰Ò "ô×,.U$P @Þƒà\7²Ýºj[œµ´x&4!]“sDÍËZçõZªeþn&‡> vº¯'$2ÛcÆ€¢íB¡ÎK'LòU(éæ2ù \?kóüã²_míYòbC{Þ––tIe{ÿǧ\téìA¼V°iômôå7Š+*æÌ=NYù +¨ˆk[**E‘•F£R$ÑUQ ¤ûªg;W—f•~¾ÞKánÔuëMÏr:IÀ/ßy‚Eë9žŸi©‹´õêâÖÛŽU‘nèš g®˜ÇZþ÷H@Ë‘’ +Q6kÌy£—¸ÏÃZk൒]2 rO¦Ù™Q*mµ#û `ÿ7™OýbMZß1˲7ò:Œ¿iÅI@éfš‚ŠÇ” YU3uÇw®¢r}Cmíq–àßÓæÓÿš@•xÊÖV=³`¨¿=S5M»‚©dåå—\’/\;ÇúúÉzÅULûéï·6´WöZvüÏqzTÙ÷h¼Kür ÖÅmw’ëñœUÇj6 +dʹðÛvâo(Û™�kÌêÿ^^^paObG\jÙÚEVÒjµÙ/æåG4½Ì“Ô/£Á¯kšõ}Œ÷Žá-¶bÅ5\fÄ…EÚáHÐ.ïõõ]ÁDmø‡§ñûSV¾b{z9ø}5 }Oœª+Àk¢æŸ)gûׇˆéó’Ì6í¶ûûÀ÷ð3/fµß 8ßp¹HŠ\*Åó|x7ªÉîõÚZ]ÇØå*\}p"¿dìý(—€ñf ·?ݽµáM³þ‰í›[Uªïfy=vþ¾ Ežödÿ#ϾW g87ŒýxPíR/Ê]Ú9÷MŸ‚bM½d…ü+@¨Û6Þ:„á6SGOÚ.£:sÛicòâõoÁ§F«hë«óRžžxŽÉÞÆûVJ\rc/%æ@€B¼ü×/¬ÏÄ\Ö5uŽ«äù3 3òþ›¿^–}pd›>¡ÅNúĺGv9ž”§Ãæt">4žE²åì½ó„JÄH˜2\Îõ’ Ü7ùk<¶Õ†4 ÏÜÙ¦’eÎ;dî ¤&`w{<êý*ì]ÿÇ<ýká~xEuÂàׄp™s•¬jÚø¾>mÿ<eå›é™"·"ŸÞ +ù}¡˜~x=|~¼j0—}/Åå»×Üó£æ›‘¥æ/ÍyM¬†Bi—J´” =¢Jî3¢¯0\ßTQ®¬NCðmÌú.¬³äý¼ƒ«Ý5m¬ìR=¡~¥ròê¼<ÁžgUT½Z¤¥ØU¿råJkãÁ®JRëý©ûd—Ýâpþ7f>1�¿ §ðÏ"ú>Wö¤ûÔÎíC™Òpœà|&`«Îqø~kkG?aÿPž—ûBqö‘Á…HX‹)»Üµl7‚d†Â¸á\K +¦•ašQG’§E2‡Ioˆ;¥Â¥€ÿ= #RBö–‘Þ +Ê'˱ƒš´e Ãèߟ0E½TÂz +u锸ûñTGâvU’¯Q¾ßkÊ+=ª3D=èÓjPA¹þx•=pk‘âE="!ã/¥‡»¦Í(®˜�×RmPÖ¦G`ü¥kN‡ÏG„Õ¬#aEQˆ°Ò¶H²r¼v0ŠU +îRT’%aµg‚_ŽcõËlqŠ1ø¢ã‡ƒRACƒ4)²ZŒR“Ë=ΪiºWÚå“#7ƇŸ{:n¬ ÿx Ô·éá)YáË+"J¶aÚ~åPÎÁÚŠ£ó¿Î›Sš_ï=BÒþ˜³èGå<íÁ–œPÃ{r[y®ßhO»&Ê¡P±»NK»ëÍ:_\³?Íöçú`þvçjJ'•mëSÿÓu6Ì3ñìåÑ|מWܵ=n(ZV>}ÀN~<íkTÎâÀä¶Jž[°£õàö|4 &M¹Šštiôé–{¥óN˧gä„Roè½]r?{óƒç–Üy’©QÕôýí={5¿ +Ze2ŸQž7ÑØ4ßûÇø:ók‰ôÔóá÷]…4ç*$Q¬wmövÔÒ®•]:mò+ÑèÛ2ÅOµëN™K”D¬¾c¸Ñýu[;+**†Ü*'iÒÿùG#bùZ`DÑBíÛÕ1ä›ùK½kÀ™%ïZ\o9’gà]:v ¥%é0Ç…n•Ék²©÷Æýþ®31up°Oc¯¿ê7L›ŒÕÇuÒë×8¼õC7{æpw¾dŸhÒ +.Çì¥x õ¾®Ë·/NÊ,êM݇“€TUùí�v[‚wdèÁ&pÌB`øŸt×… gO¦½åW:q¾ ÎYží(䪲ês‹r#7Ù”¶~ÆŸ1õä×Püà× ×»2ä+šx8ÊÚ]jඹ6mºÁqþüq«Pä„ãƒåå}WÎ,è<‰Õ;tfmUIKÌ ¿³¹û[7?*ʪô›»ö!M™´Çê–s'Æk:è?_²ÿæ@:õŸ ý/,¬ð¼oûÍÁ¸ñ†zk‘e»U©Eé¢Ùꌃߟ®¯±Cÿ? ÈZŸl[»àó1‚™l“!…z²û¤;ûKü~ßW#ý—¾ÜÓS:É,Ð7ÂÒ¶E1½Kt-LÔÕ¹N&ijà3#{퉥+¬ §lÛn$S;²4Gó<6UV¿*{dh £Y²tŸ1wî³{»‹¤©D))@½À÷§ÒÆ<,ôC׊[$H@"žîg¿E¥Vl¥ ÒðŽóvð·Ïü×v#mÛó.½eƒÞgØÎ.ŸM¿¦r÷WWWÛpÃ`‰‹)%»²S²çôx>¥°<®ÒLl[ÿn–ÁÚ¢P÷»r#¯5–E7ÍŒh‡°#¶w´ÇÊʾÈ9›kyΉÙ_Ó +ÂÛ—Âu4ÜQãþ§kKK‡\F'Ê¿://ѾnÍ^*³=¨xN¹Ì‹ÓW(pâ'ž{:ý="ÊWìå\I©À/B]%>¤¡‰.립ݡÁ(¤ð똲kÃù>AVQÕVn¯\r)¢«”NÈÎ0u#b{Ê$íDt;Ë(*Âw: n¬-ÿx 45$aY \2î´e–-ÌÚF¦Òmˆô~[äN m@KÊIúÔéÏCQåý +ý" xw ´P"qÏ '€’ì–Ò$©ž)Ò‹€KEm‹5ùP‰åß«ÿû_x¹ª*âò\'‰}o"Íï +NÉ}I@Ê„åª0ùM®ùokîë{³¶–ØÜ”b ÓîRo ÷èøšY‹Z‹§rx"(?x—ËK$‰Ÿ"|ü¾òþ=±—RB|/|û”å¸ãnâ<±;Žtµ4¥ÓÆ+¶c®•={¢/ƒdÚÓ÷å°¡*-‹k¨’¥75èßÝ>çÂCZžïcáêYÕ›;:òKò +Ïã&¿«\9b°GòTÙòƒÛ(Ãfä€0I‘H`@rntªæ߽ʞ¾bkÙß$ÞNn@ënNXN*íuÃh-‚n‹J¢» ,^‡¯ï ˜¬wõ26ÄøÞ…*óV JJ-‚H;ݤ·á‹–¯¨FÕnȦ†Ÿóßçtt½=Pô75óL; Š0{6ø$öß®*ÿàÔhÓ0߬/Æ꺃Žw>ÿ¥•»n>·*cmöûÓ‡á‹mpñp‰¤ö{}“W¯>~'q*2�çCòºØ¬/§û›_E‘Ýw8D@Mk'žø iNáYLC{Ïåž{Zn#¢Ø'G ©ÀðF’–ðùŽ’KLÅñÎs¹ºXu¤8³éWbÙ¶ëîAÜôÇàëÛÚÉX5\µÇãìŠ0Dˆ”ã75%W<4BÐxÎÆŽ³IÓHëM—T}¥RÄ>¬Ö$Oì>Œ_o +‡‚Œý¾[±}fŸS€œŸI`éŸ`[¨‰KèúŽ‰9páqógÓÁ¶bKÑÎ<MÔp«T¨´/o=æYí‰?1:ÿ¾bòäøã{»ïõÛý=”æ“ÿ>x0ßHÒÅ9ùY´Õ´ì¶ï=‡‡®BóDjïCûÚŠ¹&MÐ{§îJe ¤þØ¥CÖë©H ã·Õ4í6.ÑñHΔ5k–/ÿ‹üÝ|\VÙ¹¯v% „{DÖÛðßÆéÐd)ʽ¨)m<}±|y2w «kaŸ‰m E·k²²”{Oc‹S#sv±ëº=ð×ÿœE·¥äðoÎ) 켬¦F`53Ç–{îI3*=á î¼Êå¹”^ødS�cÇÙ&=w¬°8wë +ƒ¾ºˆFö×Ô÷°¯Ÿ€&u}4ùçê¼@Ç»#‘CY=‡êZš»ÿÇñØϼtúžHYt¶¸Ç= BŽ,äË‘<©‹ÚéWˆD7 `ÜÖÕ5úËÆŸCWLÎÍ)xððᨮk·!AâsXˆÞmxîtÙ¯|üw ½¡ÁóQómˆÖ÷¨’?¦rZY99gÄžIî&‘âí~ƒ8,n¹ôºüsŠEæÌÛ,[Ñð|„€Ù^… üwJ³‹lêî . ‹¸mÃÚOQžêpŸ›ÆÛ:¾Žˆò½dâD$A‰z%Ñ´ßÐópÁ9›= jÉCHÇØ£›Ö×cÜ|pn~V}¶Ùe½|äHv}Ë@¶ VBØÉ!«Û`¬'Á¨¡’õëɈXé§ã Œµé䨹÷^IãR¥aP8LÜ3,Ð#¶Ù¡¾seÍ»5èùß½ô8D»|bÅøªœœÁäBø0.xøÝEBÌ3#í¸Ê¦HŠ1ˆc–‚ù]¨é³ð¨o¡3à®Y”•ÇÏ ’ô4 j ÒAŶëw÷tƒhg<%Ö< tŽ]Atð»SÙLðzKjôÄH$ì¿ÉIøû„Å}²ûʳ+NX|‹%Ê¿ïsÕ_K¹PÂrJçÂùKäøŽF´“]:}6"ÊWtÕˆê±ýºgL*miÁŸo¿ýÅ÷»Ûú#¨êZò¢êÁ®ÖÖÔ¦Öî .~L–Âÿá”oäK\yÃmã0 8“‘VJÛT]ou)éï ¡Ú·î7öîì@OÛE€mÒ)*ãAÇ¢y…ÁàТþB§@å &¬F¥„HY$ì_S¸iú³qÙ]ÿ²¼é'J*gÖ…ËV~ºTvÀEâ¤w©¶–ΓèÀs{Ü®íÄkGëßaÅVLnuTé!`=wéi{\2âÙóçgx„Qä3•íT¦Ï®.[ïX|Ç#¦?ûŸQÜ”·TÀŸÿ—d-µtâGÁ”H¿æ¯¼Ã“ú}nÉ7Ãj~oòXj³PÆ/55eÞÒrÚú}GDxpë"…›°wûTj:lû8:H1p�É?¦Rçî?=õÔ@eNéœ ¦} 0Ÿás[ üE¨@{#QœUïÿì«öýñFš°Wˆê;/áÚç1Ù›ljÚý¥û|tI`FÍë¨AOßïÕ°#dôLÃäë9ô‡N"õœOÍû�ŠB^|ÕxôŠ8×n±Þh)Ã%C +[\î¨laüs1J–ë¶:ËH$g"—êÀÁtº _ŸÔÚ×æ£P–ëøèîŒÅÆËÄû3`{“lâ=‰Xªzúü™àjÈì:Y€õK¦ìE¨}¨*7¯``os>ärœ|ÿ^9 4Ôeuu|ÞªUǹ–ï'PRu ®ô$Çâ÷Ç=qkuÚÖ?‡J8¡pêù†G®±=÷ùëÊ,®A;ßå÷e½¯¤"tþà=N·×Q¾™Îrק0æ®R§UˆÕò¸Ayº$»5~í½Ë–¡Ì7½QTµ�˜ú ”®þ>cî—!®?3º–þ‰ ï¾4Ð>.ÓqaqFäñ¡P¨tý˜ëát›?ÿÐöä/sƒ’L;ûÝ°ŸÛ]¾påÐŽJ缄y^È¡NŒäi’Dn„§êä™_�VôÛpZÛÀÛ•u b;¯£c#×¼LÞqƒÔÜÈqÃ?´c§ÉÍ…ëF¸|³JÛUI{,•‰/Ø�/áêpôÍî6ì/WçOËMîêkÈVZÉŸÖŸZT¦¿sð÷u.kzM¥o”i{¯^ù6ïs`\‹ðìÅN€N’BŸO̸|ð[.)Éß•N™?ƒû¡ÆÛÚ¾¶Ô&üº'xPõ8Xe$ÔÒÓ6ÙbÄ”/Šâµ,f™Ž×åzm",±"2‚À ߧAøßâ¶4+×»ñ厴™øv”FSñÇíTò'–km1Õ`Á.7ZZLäíŽã¼ž$vÔÕ]ëlˆü}Óxô\µ{_s8ž:ŒŒ¨²ˆ/”£HÊ‚«V¯>ª€]·ÕFa/THøÒ+¿yã!FûÝÞ$©ãLiÉv~H€r9N ªD÷a^=Mˆ±9•4_2mïY¿Ì“Ų¿òñÃqŽ’?„²]»«# Àöä¬øÔ¿çÃÓíJQû =3’}A…]ж–‡UöIêñ™qbÌÌfYÐj²Pó@|¡®IîÖ?ÜŸ þc¡#<d"Â�;?Ò¦ ˜Â^ÚÖÐvû{>)”Û�!\ŽŠ7W¢‚Å*+ÀnD…ÚÎõJÕoE]·_\¾ 1Leyã'‚qÚ¶O%ô´E<uòTç–êóÝá‘H?Ž¢wC#|nﮜ2¥¹¹_ªg,a™ÓBYâ¨æg •×ÏŽ’X) ,sËóÚ@³ñ<¹2æc }ŠTç*ú«Ì²îërr¥Ÿê(Y××Ø›BÚ0³µº_××D™–éÄ@Ko=ªüš¹žO¦¬óê8o_He‘Á³Ò%ÞkfÒ"ê¹ã,ô‡èEÔ¯ÍV™2Ýóìxx-$qàœŒÁ0tî(|sÙ§ïE +kyþŠ%¼kâ®Y1KW ´R™ÊzR»M÷œï„dv‘ÆùZJíû—oš¥äf\8¾q½Ñ€º—¦øÄ®ê¢òéãT™å \}7¼ôMb1¢–9 «ïó©\”ŸÿÊÆ.¥»ðe2n’}Ù³ƒƒ›ÁýiËÿcïKन®õoíU½÷ìû0ì;£ DapÇ-š“¸ç¹FEóW“——§Í¾©1>}jž&5 +ñÅTAd¶†Ùgz¦÷îÚ«þßmèÉ€hL˜Tê÷ƒšî®ºuï{Ï=÷œï|§Ü¿ƒÞC™e(©C„oHôúýáx™ðuAÉ�†"$-eý)>ÂNE +!3žžN™†ì8¹¹™úäbÖ$QÇv²Ïx*,K¦EW¾Õ@<À®7)èÍÌfÁ/ç”–ùšÕUU{NQºy~ E9;òHþ =ðÀ•vÂn¬.Uúvw±Þ¨Ân^4/®³vF½æ!Æ#Àtþ{Ö²oÔ8õyhÀu&ø_9‡LDœÖCzó_YÌòµc0c ™@,§à3x?ÁÌ2Û"£Ê{ïx¾ ‡tŽí¢°¡ð Mƒ~YÈòB¸··9'Ú2]]!‘#XÈX…ã~¥(â#>ü¬Â2“S&™…4Lâ[·Ý–â‘ÑR%ÆlÁ6Kèü=¤úàfX*C¶eyy‘iÝÕîÆW¹Eð½ÎNŲK@´³Êr™EfEÁòTÖyÜv¬âŠ PÜÇâZ瘲PvÎV|üùW"TÙ‡&4»×Œ|}t~XCßâÁœ 0”x뙢LàlG œ£ïØÑNêëë‰ÌªMQ6!ÿÌ´Ø5Áë ¥„À¾~&q90â3k +Dv×tt&Þ€U^=r|1{à’åËA~ãNŽ&RF1²µkví _M¨ PNõ-û”ZņÛËõ;õ†ŠQÛ8×¾xs¶-Ó¹ò|ðØsЮVh®±œqxìy¼Ír_huc˜}|ཟ‡ÏK[[3§Åæ’eZ‘¢WÕµæô¼ú\?]8iRfÙÎ؃H~9J5’-ɨVb¹@•à8}réÄU—.œ·ó[ã»yU&%÷Ã;‹¿³“¨r»¥?UÉ%\¦uþ¸¿BSz àóºõªi¿ f#‰FâµH](Þ!ËÓN²!qÔT”;ò‹ÀYn¨× $7"n»Nª ¶–!¾œc¥LNŒ2ª±mù»/ë§ÌŸŸ%½™·ˆDN²m~zÅôÊ=èˆÎÍ`±¯ëéûo†Sn†Ãä{Øý ‚r!†¬Û÷nIÁDz#ííî#ÿž{€Fuë)—éÈpÕí=`-S'*%Šbõ^Päs8ä]àS—ûÎ(¥æÛ=ã»I+;ίTãÙÚ×ÒLó~žûîãÚ¶ÒêîôFa¦ !¼VJ¶ÌÞ§5Òû.±Öb‚µ’'t«®°°ô¬s„ïY¢Ì}»Ô[pYVªŽ«Çz?R¤ÔöþL) +ŠtÇ ¶É¼í§íG~Í`ÂÃó'XrÑRd7n¡ŸÎ×{dîÝ€óSÏ_à㽺¨"T¹|k]‡ikë7c‹ß^œËI‡Û»ã·ùugúºûãÑ¥šd¾÷ÿæÏ×(1FÒˆm\.â2DôºÖ´¦HÄ Ã–HÑk` ü/`ô°på´±«`Vǽ9i™kÕDÂ3˜Œ'ÿŒ#ç/N|iê‰^†g+UXÅ/s‘¬¾pBY=“|‚tX¸7Á6ùdÂt€%ɸb+(ûÎP$ï͉þöƒ"(œ©Y×v¥Š6&aϧGÿÚR¤ýê3KB¦¥1éžv•y¹£)z8Á^›5¬1ƒë‰aÝ©Yñee”Ô“D¥&@¯¡\ÀdÞz_wô£%Þ(o^ö’55I†pK`“ŸÀÖÏ>ðõ +>p®W‘nƒf»Æ§¹oçëÍýÀÁZŽHÓ©'!bn[Uˆã™qr-5kæ¯;œÎC"|›ÁÏÛ—e‚%E¼mÒˆL¢œU]I,ó¶‚çJ<O=Œû¤R’ &#¯gXëg.Ã]pÆkVþ×Àº!‘¿”wÅ[‚aé*ßøé_Ú°a`kq8uÜ‘ºüs{ -à `¼žž0ë†8«ð„‰RšIô?䎤å.s IÆ~µ±,Óì#<7³Öë(´¦4ô´|ÂÔ)|PZ€lÆ·[÷ã«G|uÈž�—>ÿÜV¥Ç5½áÔ[q~!QvûªèÚ³Ëå˜{Y‡¼gJb2ã„fû$wŽŸç¶Æ=.bCÙ’Z„‡Û(Šš!6�AQÔâÈÙßÛy?L�œlè ®±xÅ8öÇWcùz¬íÊšY³� %‘ÃîvlÕ¹ÑYÝ8“¢òf‰üõ‡Ãù…ïbØË÷ì‰Ï* ‡h)õÚ´!_){ÖHú=ÝRô¤=Ãz¤~Û`Í™*!h8_G“à…B{–Þó«õEÁí:qN«ÑB‡a/0±Åq8ò-Žu,9qØ–‡Î:R‡O¯Zwô§ô„ó¢ë¯Y*ûŽé8ï®@n0—å€ryAô%Ç㲬ӜuØQq<RÁ;ŽéúE®òÊÒ =²¨úh‹~ŒÄ®çÁs>»®ÓxNü¾#z¯£«;‡c¸ásüí¡P‚ÕÀ.ÊëâÄp7@³ ëÚsÝY}®ª·‹€b]v›Â‰¿œ#ËBÏ° “n»mè0•èrl~æ¹ÝéRÔóM.(.XÙÚ7…ô¶µr<ÿ3D!~”j#²í×°ÎÿpwíöÞ—{[“ƒÃø›F<ÕÿìVÓNŠ��”gm«8¶1s.¾þPi0‡|TPÇC*ôÑ-{ÊýÞÀÅ2qw1&i7EâOèzQ˜HÏæã«t˜Âìñ@2ŒB¤Ûn?k®«(( œ1¼)’þ²üȆ6úçŽoBÔJÙNÏES€]¿³$¼™š,©²GnþÌ÷À«-ÑSA–ý;sž€ŠƒÔU0;°Î·TÕн²ò=†'Óô¬ó•‡‡ÒÀӴ茕ú½À¹>ðö.e%®ÝÌjåHÚz2v«“-—ü§»gë"*t>óô7@¹-*gžp¶,‰SaO;œÙV™)XR÷á^æþmÝ—øö*POþVäUñ®d_8&cº—†È‚sJKJºƒ¾'S‹ß‚¬ÐpCX!@¸¢£”ôWµ÷—AGwºL3qFù^</}2ØT¯„IPÙK!+$¯È?Õ¯Úe'';Ä|‡u…Mñ==Ô¡8tµ=ô’Yóe×Ü! ¯‰# +ÖxzM±$‡ÈGû†V ÝCôͺkwÚœXÿ¬Í®7#lP'ö×±¸Œu'#~Ó“ì^7œ‡\‹¹£y^œ;ºçÃqú‡Þü#%|ÖzÀÃg×0 Îólè Ø—¼tµß#ÿQnç�+þBÅðð~°DÞŠM„wf,¸�~¥)ΣÁ¸ò|AEøѬšùOøvúxf¿€Ç_€ƒbå�`â{Úl¥³¥19í§Bì€æ»BÖ|1 ¹wyä—^ß”Í&|Òéiùκl–̸{Ù²ƒÚÖ(ç42±Óa6ší¸ìC€_<‹w3›Éö×…¶µ•‚lý?4Kœºööã}x»-L •°ŒÌqÞ!ß³§e;Er”5ƒy% îàÍ`|âÊ| +²ðeùLŒPÁh¼O --¼¢!D‰ŸˆÕRÔêòp¸Ã¶™³NÊæÆÎ>½Œn˜þ‚z¼@v$ö-¼-¿85DZO><AÓ¢ÅÐMÜåÄbr| +ïü3û* fVUÅ\Žü”w™Å6x|1€Ã"ã|M`Ø/!‰ë›Hÿg8{÷Ó`]F©u\ËŽ«ö–™@”jyà I̪.ÝŒ··9–)Ó$éçÂg¥c5ÍyWì6p«„¡7jœãÐdùúS¤ lä>13éîTzzmø£ŒÁ^‰¾Ú¬°îã¦Ã^]=¡¡ +÷Ü—¿ÿ97›Kç˜ó,Ãy7žI?ÅXÌ"ðⳜ_))Ã%g!–à¦</—~LŸZbJ_=b=ànV Ýjx8œø|™iYÝQÕhÞ·cEÿHýþ÷²á<c–vºlLóyÉ™Þí2ñM†-?Y-’aûUÙnNdÖúªm³¢/<lÕªUYR]m:r0‰Ñ0Dixï?§Ë̹x¡èó:E›UždúcÐs‚û•wäë¸;ú+ÖÇI*Òoµ²vœÚK9‘ŽÈÊ +²]Ù¬õÁï×¼Ñ:»vþ~MÓÔ¨¤aÉqŽÃöô+ËiÆ¡PHff‚3vú~º”šdšš’\*äÊ¢Àc3cÍ cÑüùO ül=c$Éhd?ÉÇÙ#E{¯¦ªO–E)j1ßpufê +Bö4þ?ƒŠþÈ?{·wD¿o¤ÍY»¢ÝF²|L #¡:gé~MdâXN㈽dp”kløp¶ÂÑ‘÷“oCJä2aFÙšñ¢àd‹RHdïåX«¸}kŒÔ—}äsÿ•?òjß–Xëx$6€•ÆR k,!Þò®¾öv¤mÙOû üò°’^–ÑÞAFÛ‚S•aSK_jn6Yâ¾'H):qþ©Œ9çªïÔúøèéH5´�ä.!+«êBûm%ÿ•väÙŸ~¼¶½½Lp˜ŸdÔä +ŸðU&è-í#Îî”`¼ÊZäŬîšÑÎd‚ãC~A¶×CÃk–eù{»®ÈS8¯4Trªc1?`y®ApPJ~x¼~úüôž¸¸÷ý$x.€ +p+ÓŽ12ŠåŸžñx@àNqö‡ˆ«jô1Óq÷(„M +®ä¦mñ¨¬iÈ@šŽÚÝæïÿ‡Îa^ãXû™œÑÃ6íÏ@O›,;¼ —ã1‹Âå¡ÕƒËžP1¾fÇÉZÖ:†uw¶¯z;(‡'‹ŒÝ†4Rëh3¸ŒåßûxÿHEŠ‚œY–¦ðL—e2ãDä·êA…:Ô—ÀüКһ°ÝË€*Ð ¯¿îã¦>ÇÇ$dß:Ô¢' øÈá8±’wI1<rÿ‡ÔQo×A"þ‘wðE¸Ç.î8Æ À‚ëŒÌ”UÙrQÜåèî&•sJÔvtåÈ‚u°\ÂAûWsí›]EE½c"ÙŸ›Ä¾ú7à¥9Y{]ËyX‡yJ³×‡@{û,½l±ª=Ú+ˆB«EÄÐÆT!—'ž¿¤®NfOßsIÍ™’¹;¢1IôxªIø ÞË—2*ð³,‰FõæªlDaÆ—d¨ÓëPÚÿú½÷fÁ³|»äjÊÓ¥êk»\«ÂM*2ÿMËrw±6óGÈ€]dþ9Y[ äÜ(/’5C+ÍdGÓøùÜX»<$‰JRMDHFþòÃî|ÈhÚ¢åñÉ >Åf2Ü¥‰ºÂ‡LXÚÕ噂ÀìAŠ±p/âAúèZ‚PFD½„¨¥ +Ø{ºl†{ÆIg7Ô Ä{v=x¤BŸJ¬êÊœgóç<n°Â‘—/qÆ BØ<0«;îúŒÿÚÙuuM’!SQW_ *À«ÙSàéÏ@axÃ(ô/‡Ð@F|*9LÒ”LÖ'Óì<Û5ýº+?||•Ò‰ªå„èm`›tôÜ™édç{%°)ë{“ó¼[4R›ëØE ßͳ]à„%gÖ[ò÷UóÖô¤Däë™6'ü[vÓšÔb)•ãü <SlØNGoýYL9Ã\†?[’¸.‹Õ?Y2†ª>C]Îß—Û¢'ÃÂ=!Õt"¤|~÷ÑÆâbšâ`þcO¼¦ÏôzCfBõ[’ÊìÊ¡wH«ê'®Ä‘Ë Ž¶å…!ÛnçÅR”g“¶Ç`ùz"2ßg]FOfÌŸ¼S_´~ßb¾_;h€Å¨ÒaÅ faéé/{:RQ¶¯d˜Ü} 6µßŸó¯îî-é"‘ãv;Yu§¾{óÆÁŽö[>A%}w´µÉEQÁïÊì-¶cÏ“Ë{Å¢Ø6O%7vᤡ…ž‰C©S™£³1ã‰Æq—'ˆšõÅ4&ПíOѤ ϶D4îh™%6qÞžSQðòáúù¡¨˜k³FËô;ÀT.KŠ?¶d/Vð‰"“^·Î¢Fô…)æ·(o[úV˜26‰>¶Ä#?~n{`<¶;À/V"}+Zœ3çƒiÁM® ¤ïM>‰¥ùÛ|@é8ؘ¡ÑX#Æ3õ͛,ÇH8™ƒG8çÙl:ýßÄçR¬êgé%œ0¬$òV—öNZUÇ WuT;ê3îÞ#/xé§}fõ9;:µ/kÎ\û‡Rˆ[žêÐ\o¡pOiPž€Àª×?nÇ»¯ØO|Ê®mö&;Ç‘˜øœ¹ùU3ÅH0ÉCÈøÄ„yZ}}—IZ0oE×eõÐfÎO*d °>ñÃþ‰ðõðl—åØ>ÞuÁ‹ãrAÊEÿÂ9½%R°xLEM-k0²trm¤¹»»mÝ›ÑÄ|Тi'æ:ò#Š8òõ¬"¬ïkˆ/>ÛêI¯?ªð—Úz·®XÛ“Jm'íøê½éÑMpF{© ÝoìHõ“Ç"éã/ õú5Dë àf<ϺWd³Ž»v[×/ö‘pÁº•(¸ou'<¼»>–ý;ÞL’úyÛ I=ÍJ‚g§cJ«;4b–ûImRׇ1Û_¼"&ƺ9<?¶Àù±±‘PÊ� +Ì:ž/7ÌH»Iå¶\¢°_lá»xá+)Ù{AJ”£†¶p~QjñbÒêgŽž@‡\«‡Dø²<|¡¶8Tt}Ï®åã½H-[�ôØþa)ìFã=Ás¢–õ $çÅðŽk8F‚•ïMiý~Æ k'fQ(36Žüô)õ�ÈUŽ}l:Ã1S‘âbÇà:… +q¢"5PøŸ +)²K÷šÔÖ»Ÿà¥xÕµ×ÜtÀ÷®mX׿%&'*Ö!Èàã.6‡ùŸÄ}[¼÷SjÚ¿ô1T›}eí®1ESÇG¦Ï›÷7i5CE5ºz»÷ð娓½WÓOƺ3lp³u¡1ÀÞ6a'û7Ë9XÃiž¶�È¿7§÷h÷Prœò‚aßtóº¤m–;.ü¥‚‰`bòK”µòØ@јϜ +ÚoáóëH†J![VßCýî ¨„¿·P#êláÜìš®mŒ¥Ó3b|á)O·´ì§ò¿Ú‘*&’u×]ˆ2ó(÷ÃĪì¼%Ø·ËÕõ³ÿ°b…üQϧ[ÈføPûÓG]säûÏW4A£o«Ïeœ¿X¶ý(RÉ4ù$¡ˆcÙF…aæa$¨§w½‰Dlùò‹/F.Hf&ê’âòлSË|¼Ó·iÍúdÒü@û’bº#0®`V–ñyÿ|bÃð5/,¾í6‹Î/꘤s¬¥¥E¦Ÿlÿi…LŠÕ¬»%™s$Öó Ü’dVý."5”¬cþØ;knîù‡æ§,+¥¯];vòÚí]E‹aÏÍ?¿¢`Ä8Óµnƒ}ºZ`Ho¿jF“†ä9.g&I«ßë½óÝËš‡4ò._‡¡>‰ðM‡-Ì ù4¯GþRX–F©ŸPT„(ĽB’†—yÈ¥Ã]¾†R—e¶# æ»~Ùw¡¸8—ü�ÊI�û‹…³'Ï™‹\9XKë¦TõtÄg팺|Ù»ìÈwŸŸX©ªHPlÞÊ©æ-ªæþ”Xæ%Ä´.AÿBÃ&¿àxæU—,„�9°Õà`•WrÛoÄb’sçε9…$@ä”m¥ilÜOc>°œÏûçWb±àIßþÎ0ïø£'pÕ£ŽŽwÇNèñ;í¨|æâ|PmyÏ”ª>N½WK[¿$Š¼xt¹¯.Ãp·dM§!šqŽY¼jÕG*Pùrvf=aqÊnÛ°'™~åª:K9y̸ôZ—s¾¬ÙlIJ7î—8r^¡"ÜÁOðrÛéïLl=G¸÷);¶!¢ßîÇ_')Pª©@¸ü´m¨T5ó©×¶ùâôEÑ0ý%SYF¸¢xL²Çaœ[„HfY—tO÷ûû‡‡”ç€!¤^ÉÑc}eÒÜùž;Ž +dV`7ÈÕDìDýÓ»cAºM9ðº#Ÿ?Û=pÀ¢ÊL«1ÁãŠb†Ù¶]7;ßYÞcÖ„ßÌìã^6ð¿êfÿ;'z PF„è¼Çöü>“ùò»;£Õ ä.ZV$îR†8éþTªù\Àoƒ‰â³ÝsÿXíé¼ +jÜ%’Ëý/Ï:7#Úï*È»K‘iæ(I7Ï©Û›¹xpéÑ_‚$ ®È}Ço[?ÍêÌ/tb:iÃú⑯•GM¸mPLúN©F=¸Œƒý=»´´ÕQ3펈¬Òˆ:4E§-²xqŽí,®ZSáõk¯Ç¯ÎV²),ð¬ÈÞŽsÂwA}½7Ì•€†Äl@9Xù‡ÛwC2àÎ]”î¶,äsa®7ñkbà {˨ÝÍ…³‰cÕÀ"‡ÉàÞísý¯]”Ôp›ÈBÊ€†’™Ó§öùé–ç>âÏOŠsÏ=¦ìN“¸ÛtÓS¥9…ÞŠ‰”h@«Tæ‘?ÿFPÏñ߸äSû™ +J ¿¼3Yˆ‡æ¶;LF'ךŒó8K”ÛÇNñž_yÜ)ÓìÝ™Â]ª,t¤š¹s™»uÈ&ΦBÂA’MšWæÄŸ°AñŒà¿Çk‰Xû-¤˜´ª®¬ëæ¨ÿ´‹¯.É?÷Skôað ˜u\Â3 l]ƒ¶ÁüÉuÍ;`[†Ÿ0\{QI¶8zÀ‚˜«uË’v ¼HŒçÈt0žÝë×ì›Qº/ Öà‰åkáìÚw¼˜Òác1§?R#¦óÏAÌ1|9yŽÆóZjEBQ%N³-üî€ì¥ïÛAô[5|§ªj÷õÑÇÐûs…ÆËZ7ö™ØÉ Î—6~EGübV¼§©GÖô1E/_j?ÊöeÃÛ| +„ïû}YëÂƪÀNÜ2ÐA«<3àå…í÷ärZ3Ó”"Ð;ngÍXº%Ö³3g—ïèxüëéL S\2¤Ø%n•mv},kC¦ãÌšÇ3.\˜£ª¤õú´:P—/_Î ÆKª*íô}.Û3ý<8v~¦BiêÔ©<òßíÝCûGºœ^ÕÈõð-ŒWWÊ•t{Ï Pž¤Ì»‡bE§‚eÂ<°Û}ÔµëÒéb-kÂ1³%¶¤±±N{zgOIH’K_ÇÂÁJR7k˜Ï¸[Árõ®i1ëUÁÝìOE÷ˆ4V§#Ê}ýke[©à¥Ï¢äý¾Ú±ºc]“×4,îD;œsÎ"M Ü;³ŠDWw¥Ž•xRÿæ{+ÿ´`Þ¼¬ãGÕý³þý +ØV=®ümpÊOAßbwOÆ#—ó/ ù¾eX†Xâa€%e’ÃÂáD~7KÛüôÎØd‹¾XwÈOϨ Åé¸ß]4nze¡¤s‰tˉc«¢ôú5½©™Øõ6Š÷‚·¯³é±ƒÌÑ5ð •ù§'ËMd9ëhÃb;Å®ÀŸó4Ù¹sý¿RYí¿Š|âï-—ýa9“‚,?)Éš\‘XÞ¥Ûññ$<ªÂ;™(îïeH4_ÚH°Ì÷!:~'¾°"sqo¶)7ø»¬™p¼«áA4ês+ÔÚ)ƒø7É78—iÀR‰„Ì{aŽË¦MkÂ?¯áeå“Ë«§Ãªî£Âq3Î0é6Ûc½fZaôÈÆyþ¹~xÊÉ—~¯üûVXhNCÖ¾ƒ½H*h©p¤ç—�¡ëBäÞVä’1“F5 Šk¤N£•[ÃTµòƒþþÊÇÀÒòrÚ½ek"\ÓxÊð)óæZÑ’)Ë›ShÏÄSÏᆫŠŒ´ž3Ê'Œ`øøÌb;4Sɺѵ·íÛ"Òß÷i ¹º¬ÍŸô»?"ÁÐDKCãÛZúŽaøî™W>ðÀGn=»»»uÆä!ýÛ·¿dÓz žKA¨_B\ójÖ¶n¶m{‰m;qžãgBPÜ�ë>Awž´B¾_U‰Ü …¼±øÞúucíZµüíH"{µËÁôe™ßæ-æ|?ø-¼´=¬Dd»5N˜7oð¸ü¤MýÌ_7·¤$Ã0öË@+MAÇ;l³ÂÏ3§%ñËä5øï¬êŠÕ¢±}nû`«£k¿êˆ4§_îŒVkµõã‚œº›ØÎ×b’rýýÛ¶Ñ]29^ÛbÊÌŒ5'ST4îë—ÝF!åÐk|¢&ôÌ1à1›f;нYg/Ü@{¸N–&ØÇŠÂ÷-›}�YS§ª¶“í7v‰ôuÄ7ß à1 [_yçe/î±HÇ5=òe.ç!Ó|_lŽçÓ/rr€a‰m›±·‘‚6´©_]€ì7ƒY~#rC]qTIp7ýö7ÅçždšîÏÐ5£R¼Ì+ö¯ÚI Aî‘8ÖçÚd3/º¿íõ?slMM>u»¬9ê+I춌ҺpsF],{dH±·)v»á¯åÖ÷b¡…Ò*ü=ƒm‰DÜѳgËÒRÛN§ Õ ”H$ª;„Bˆ@>ÈÌe†–øÕÔòòV<Àý 3>ÕUø¯Ãõ ¨D!kÜtäÁ)…Ý’LŽwÓ¥œ@ƒc÷gXÛyªÝÕVÔIÇZ¶q$B™ì¾9V–÷ä+LwíM! Ñ_o÷ê Úp8Q~ôÑG›@“èÓu'ÿ~ýÒïä±UWBp©ƒq$"‚¬*»º³¦x]ØZâñPoÔ*1Ã> žÓ6ZwZÖÚ8ŽµÃ ‹G(P:/c›ªk9Ã,GBbDCŽ&ù?71£&%¥j’Ç +9.R{çÌOtJWœ*Á3ʃë–u§ƒÄy4¤MݺÔÆâ‹K‘4[ìáë .ÏC_5A“-“yÎ^3}¼©ž8³®lúÌ¥û¤RVnÊ›2’=þÖb=U‘‚‚¿0Ýמ]_ßG¯ÃvìLœbqÁ’úøòýýy>Ó~êWŠÿŽÉ0œu×~©mEdRäNb–š5 lE{ë'ó¡î-ñº¸kÝ s£"0ìs`åYÑk|ûr¢õ鉥¥<'O ¼-•TÉ2÷ÒÓ÷¬Ý¹paã€pÝЗ SÐQж·À}T”7ž˜WPÌ?çJØŒÏ,þŒe!Îì»^i; 11Íty‰ç¤4XC=¬xÁ×êC+è}TànØ%Sñ‡ekÒaP3dš!¢+†I’÷ëè“`~˜I±Fo¿›y +“ý ×!“¢pñöTjôžD¢.è~Ó5™r,‰ÝIس»UÞÚ€>©’3réè‰gI5Rl^ç‘ýÕèSj¢«˜C3e û@ö¥ªÂ®¢Ñ•o¹Qý]x¸÷(®\¯ûÈ©ÈX?K(Ÿ0P:é©0¡/æÀc_™_S½– +9¨G¦`ÙØ£ÇN8é¤*äûŠ×¦gMr¶Æ¥¤MNƒÒî¦-çM'% +#Ù^*T0·á¾PøѶEz5^ú’¥´bÍ¿Ü *¨¶¬éÌÍDpê ÕzDO·c€Gˆ -¬„±&v€hiša& ¶Ó½íÎÕQíËN•*Õ+—ë%53óÚ2ýq윓ëÔ`ÑÙõJ`~Q¨èšI„~¿Þöp}ª»ËËÚmˆ…y5£ÆŸìW£ä£’b¶p”'¬Üçs¬ïAë–i™ÃfŸ8–ÆÐûéAû¤ f¢¯_ÿï5k¶vÞkÞF4*<÷^¸íívFµ°2µ‚Íwû¼Üz8KŸm4çBÎÃg”z +•‹¬rY3ìô¾^>pMÞÇB'^®[Éd|HáVʱ—õâ¬jF›‚\_o?xçNä{Ý´^äÇ´/´ˆs/²ó�7¬N*L„ +Tº8´yB§:¬ùGÎ4ž(`½i¾>Ùc0¸ç~O¨ø§Ë[âûàX69ÁŸn‰Œ•|[¾g_‹åÈ«ØiÞä·É-vú]¬Í³@KØêñú~ÑñÖËÓ6ûLBSG˜âÀN)ß7*á¦áŽvMò»¬m•õeÓdž†9=¡:ØÀ‚ƒY-)õ¿âv‰“Ù~$šuÖ5JÏEŒñIŽcÏ„yk›Ò™|ùôüÀÒ¥vÂî¿LjßÇë]+rlo"¤$äâyÐî0öFÞµÿ½kǪUùû>èîö +–Ï£\deísðýÉ»ü3å<d• H|›Â1ÏkÄú£e1,EÝí;ª‹Šº5]ÿ!:m9ÒŸošìÃGø¬r?)bã²½ðHÿÖ–ÝÇÂá$µ!Ód–eÀzÇô@æüÈu¹_ôìv[žÙÖçÝØÑ_EWë|ù© †Æ2ojY_aÂÿ.+¨+ Ò£´Ë£‹4ú’£Q(7µ¦þ„y£¡qMñ^2YHwTˆP˜R9zÔôy_e´E¦Px#NS Œo�·i]ƶgd‰“‚Ý9;¤ä"ǪŽ½Ö帷@kw94ø4'”šZÖ¶¹ùhlš,r<Cveup\%îméî2ol;¦¾ ÜÅK!¼«@o<uXap-œ˜ï‚hˆHÒ@ss͆ÖGCl-ô{V7ø„ÍG‹ç.�ë—šåü§fQ ï^çÇcóêS¼G}±½U{õ”ªÀ®yõ•4>ßaÎ|±Œá¸e"WÅŒ)ª*“xñvÉ㙂ˆ¡Ü¢5þÄ3+5Ñý©éº7 ÷âYþÂQ +çý]^;ú¶}ƒ›î4NܨYÆÛmŠþò–=[6®úÝÝù4= ´CÚKó(ÆÅ!™Ì½O€ú›®šíÓ¨ð4EðA§MŸ “ÖED´QËš»‹¾yEÉØÓÀçà‘9&URWð¾‘ÈÜç2înäN\™èïÏõû7ÜPà#ü÷0©ƒÌŒ³›½%žÙkF 5œË84Y.GW7m– :_žoC¾O¾gú¾3¹dçëÏoSéïŸa)HL7æPG¼§×æ¦Mãý¬;‚åغ¬›V0ßö;$×Úæ°ÌZ›XRF³8‡!ÿ†ðÑ’í—£ýQ‰Î¿:B4ï—‹-H§µ„jÊ3+Ž™[Š‚0|ñæm'%”G}³EcöKF©‹¾qùHŒ³p:c¼�V‚‹Xƹ�Þù›4ÓþŸc®äùBC**o³§¾†z0‚l§È¶l +hÔ#÷,ú¼Ãá2á›Br¬”eŸiÜ7lPãwõ$r&‡}tJÃM¦a}O`û0‹á]uý0 +wCP-c9ûFA°ïƶ�¦]⎜ÑXŽß‘Àþbìâ3êÓXæž9í˜ÂÔˆ#ëkù +O¦é`mÞKS=oq/o¾]ÿú¦¼×QsOÑÅ«§K¬3Ù”¯‰¯ø…Þ¿¾%ÂKEùª"³—‹e5õU’çlzžw��@�IDAT^ag™¦}·(X¬ZÆÈ„î°%â]‡=»Øh1¸§Â&¹Ç´Ü2…á‚F¤ý)ìuÝvúÒ¸9ÿ‚± $ÆN¨)—õ˲O·]g]©¿Lˆ¸?ø†å<]†,½·v-ÄܞꑈEèÌáú³iñÖ[o<Öží»1l7£‘ªŠL÷)Þ:ªÙ¯C¢@„΄ƒÓëºì.C´^l(ÇfP‡÷ôX¤}5QP›|NøП¨¦8ì®» ijIuüÞE¢®wlãÑD¢C9~Ö6¾Ê3üŒ´¡ý–¥€¯$‘ŽfS&‘JaR”×Þ³u«¸³të…`•¢ÑJƒM»–/ïu8w5LQ\AÝÞpTŸ[ÔIu^©ÉmiÂ$ §Úèœ,ÆËw“¦öhÈ빫 èý ¬‡¿»=m-ð˜+†l‰ÎO=~畦ŋs[Yئ�ZÅÚúÿƒº}4b¶é, + / +a=\Qr‚o + VHL0£ÝhC®´O¾HGò¢ 6/Iî%Œƒ4§Èó‹Ï7`ªú… óy(-t.ëî.¤»ºë¡}t<vž×º«æu6Çö0hv¯Jöv®õÛ_õin=%Κ^駸'ÅŽcy.ŠRà +léî6܉$Žç3xx@! åë…âÉȘúGÛâ–ýÒw»vwGc¥odø‚'DùG׌=ÖˆÀÎKöå¡£‚·¾¨º¡²Xj@&ãÝ5m°çQg6-î°9†LøªÙl7:ö/ DÿsJ×Þg½ÁÂÁ°´ØžRÚÓkª÷öR¢ßd9ç¼x"u'_2Ê六ÜäMéQø£„î³Ûz;Ëır¢|È?f†Áñ¿ìŒeÏ{¡%R†û„þ&”¤ÎuÞ^ϵ$ñì•®a&ã:ÿ2g9÷ª>Ñû²ãbñÌÙB€½qê óFr<)‡&g”cÀ®ÑT÷¸û]Së„Áê!N`¾{tušÂ²BWšMŠŒø>¸F3^Iz$N2É +¿?‹*nó‹Ò.%°gÑ:Ié´ ª»×Z|ˆº’júøXº¿\ÅÁ@=ÉÇØÇ +U#§A»ø& 3ï#x`5òÙ•ƒ§vªË°¾ ËÍšwà a•k+Õ"±8ù!hߧ–b”1‹Œ/»2?ÙÉ™ŽÍ�€g™Ïâ—Èwú³•BmýÄÊY'Mܘ H!“3ß0[[Ã'^yãØ-0%�×#Ãæl ™zËJ[ÂÁ F¯„m/„År¬ÅØý…¼]ýñ=*'hÄÈ. +x¹÷GM6 Ç{j&/²•lå©´f†š©'x%HRÌ@-¾—Ä|| “™^YÙ?Hø¹zÛ“0Ü„¿í·�F !ØÙ@õr"÷#UK´Ñ¶Q$Œi†ÿ²ãÅÀ®¼×9~d{woÆÞè6ùß…Sö>”s”Gƒ$åø{Ko¦<·@š<ø{U$òw1ñáÙŸÛcÆ0o¯™Òã|À±É)]ÛLÝý•Ï*áü?K‹ä?§zŠ£Nat‚#™&ØwÊ®û?ØÝKttxü#F¦Mçòî„9¯vÎ +j–rahY<v)äÍÏ¿ß¹°c]sÀ€ÎÖ0/³!(ûÉ%Ñaΰ\{–Èñ˜ƒµKS)÷ +$k˜9N“iÎÛ‹ÊÉúBjÊÆÉ.kÏP\¡’8lVØQ ±(ƒ<Ú¯\zí¿ò²ÊØ›ÖôªºÎ&4õDÝâ„Åõ#*иÁ‚1ç`©P”ö UáMõ>ßÖÃJzª°ÍÜ ÈÙÖ�mNÆv;AlaM´”ètâR-QÉϺ;°˜[ëüʹ==äCf„|y[;:@øÃŒÇäí‚m™Áv|L¡-œÎH¬Û“Š¢¦bŽ—9÷ØIì6]'iËÝÉ3V¯Ì2ë-F`ZìÌàEßmæŸÓ†ú•Ñž™YH¸vê‘–X÷£Ã}âó,^Üå„Š'3ÛÑ¥FwÀ6`v u©AµeÄþÏæÙŸØ6iŽ=¥.4á™_#éùŸ½ +w,ÌHÃà4g-r³¹gû.Õ4'B˼Ç~Š•øJCU©2ןTpB .µ‘së¡MÏ@gñäxÃdÖc²lǽJZÓræŸQåç{ýçŽÍ^ðJ7zòæÍÈøº=E +5%´ÐÏ‘{TÇü%œ¢“‘OÌdxƒè€Ká3k»;õ _8a‹3‘5¸6ãØß:æ«%eœ}-§(÷p²oòbh1Tð +ÃF6p¦xe¿™¾^9éœmHpH5몙'Žóár9 ]"¦iPë«/'�éšgTƒßH"4,ÌxÜwÌXò{¤Y®u4Ø\[ûV/¶Éû&]î6ºØî‹çÏ}öl +nYÐ'Lóû×20w-‚¶¼[‹ïÀI7�š•zbSí·-¯Íû5ŠK;rô€ûÒˆâ]ë¹'ÞÓ·Àc&þCùÉ0«]3a?ÛA¨u¾‡÷Cß; t]ë‡IÌmvTa°ößRfö ‘æ‡9 f¼a(97v§—[-'üÊ}ß¾¸~1TYt,Ç‘“’(Œg¬Mƒäi1NÔ.Ö…‹/<Hv +ú)¹ýíW›1ÙÐCíZÏó/&à|pyW|ëÊ£ŽÚO!hé¿è!¾Ô».ÊòhÎv¶Ê"ÿ¼uÞëoIÓíÃÀ¤ÔFúƒ•ˆì(¢¿Ó¸l(]“aÓƒ½—mfÒflØ M=nÞhoXú>´Òÿ€¢'á-ÂȬvcG*õWHÊ gäþSY™q‘Ïp¯¸YÆáQ9FgÅ4ô™¨-ºÍNÆF¿Í�1tä;ÚgÙVXîm ,Ú½~eÇš»ïNk%?3Þ7’[ +Þ›îAáöhxê!�¨GV»ÿŠ+L;Ò¹5ÕÏ()éÅwí¦ÂÂ$!%Íb0Yؕл¬ ¸6Ádþ˜íIþ<f:?×™à݉T÷ºº'{Z_ +ðæOx!p›·î¶ššZQîÞmñ|ä$lÐË + ô| ZúxE'b‘iˆù:4½@òÁ ÎæúضÌï#UW +ìÓ¿6tr/¶ôç¤5r´®§ÎYþl–gŸ@¨æ}(½Ú¶«ò¼®²Æ°û¸ðb7Ò³Üëp€nƒPkæíy¶KïiÍ«Ø’N…7D7uʈ1ã-‹û ¶üˆÜ5·#¿Ú÷36{Úßýn)š‡å0gÄ®Ç{aè†Ý½T€¸5wÐ%E‚cSÉOŠˆù˜«rð>ïµ®ÏÝâ‘ÝWÔTzùoªK·Nž¿wð9ã¸1~ºV¼Úª®.˜QâíEïÅÂ1PµbcÛѤYÞð0Äkü¬ÕÓuŦ¢Áe}Qÿ^ˆþù'q„a=jOO +\§;¦»˜÷Ø·‹vðQl·^±ê[…ä—´´ú²À9DŽÆÿ‡Ë…¢Ã¾$)Ì¥;ög-îò»W7û÷õ¥ÝÁ¿ß¿¯¤°êhptÀÄÆ°ë‹„J}” é¯2ÝDu»=eØfJÍ.ñ§¸-TÈÒÅ<§~컲^(þÒ¹dý¸hÑ"{FIÖ±~Åv´2bóï›®Õáu™ðý޻½PRäÝ,^)°ùeM/=8БT#Ú'h%œ¯ÂS>mäÈ©•ÞÒéÔæ“ï–ùÇÕ`HG†‚zfa™Ò°Êów?”£‹�/¡^î‡Á|Y*‘¹Ê•……žùîâvØ¡¨³ŒjÉùòpÖ4×^èàѼÎÕÂH¬"¥´ßŽ.aùƪºKá="J;ÉÞÿÖÚÍÿ¸ùýÝ¿¼&ÚÒòA9!ýt+Ûq¶æ§Æ½4w{à ‡Ð?kjjâ3€ÀÀŸ‚—~ïJ¡ L€»ŽÓ„+Ž»®¸g£Yã%ØÙ¨ÆïRâùéc*û)é4ÅžÒºSÇO–õÔúÖã{g—±$Û™¬,¿º‘ l�{¡„¦WÀ9iq<w ¡ÌÏVô ÔÂoœXÎ)¸và +6ô¶ô zd±©ýƪ6wŠe[=™xúéÍ=+!w£XØrG +ôº ¼+Ø+ðMCXn‡W<‰E/‘"E;(£à §Ã5Zžk³f:§Á>‚sÙ"ð©–‚õʶ0YLÓQ7jùI•Âü+2/ÂñèĘ¾üãrϤ¼Ä¶~ˆÂN‰´Î4›c&³½–¥ªÎ1rÀÿÍ‘Þéä +Áëÿoûn×É\e¨î¯±Òqè1} Ëò ¾.‰çHÆ,›·ûã†ã·x¯f–/ãÈù¯=°k×.Ž´$ÌlZS©tk{|œn!qøỚ›søm,¾Ø€±ÆHHxEöGJP¹›¬^¾›–B zƒãós“ +NÓæ8ºúe +¦ÃYÏz,ñYHÈývŒ´a‰§LeÄ`Ä‹úD2¯ÁS2kÒ×{dkKÝÓ;Ó%wmê-™úb¡?‰^—?غâ$L„.”ãm¹oðî*Í¿ò<d—&.짤Åkš5>Œð%ß•¤B+ßÙž‘#ëÊüåÇnLfO¨ +—\XVRðÙäîÁ6üôD,6àÊϸêHÄBæ$%æ•a~ uœX#úe9Ç[W«‚ó£1%þÕN 8ë5ØiÝlö»5aï¯2þ—U‚çŠ1sOŸÏ/¿2f²ñç=<ù‹…d¦¢"j¬£ÝRcï%,õöd&þ°iùÿß;n‹M†x3iOŸßØȵ¯XA½£Êø{ÞÏ~B7cw:²Ã4˜·,FÝŒïoá{ÿñ¬òP+~ÿÐõ´Î ÆWÍ™²`žJò'ÆJ=óŽ–G¯€¯ˆ–I¯)™<¹šH5#2@ø‰-Ðî7aðb;îNMÌX ó*Á¥#78‰PÌ”cè•ÓjjBºRTGy—‘<r7œg8ø‚Èä[:ºrx%à~5PÃ5Ͷˎ&T57S ÚœÑ`ŒœŒíÕ<R=Ð�üpµ…-ÄB�È¥‹†Û‰s'1Í¥øÞÇ�k +Ê+ÛÚ°ºÈ£Š´©ZòÂq UÝĘæåè¢=ˆfZòI(šeßooŽVÓn8àÖý>^qEƒ¹½¸x[å{?ä¢ÖèÂè+P~"1î%eÕÕ9{q²*ÐÂ[ÁçCe›-ö+ìȇ½‘—¶»HPøãJYqfY…÷«Ð@.#<³¦’‡…jß±ôµ?o‡òôK#®®9¯&§N<Ñ`ò#"ÆuŒÉÌ™9xßòÞ^XÚ¬£@⎀XRЮÑ[.w¢˜ýæÄô+o,ådä02O®KšöÿÀ„ôˆIÌ?ÿ˜K²÷yYç.iÍÄÀlÉ׃ž¹–Ñ!V‘°K kôþípø{È„/mŒ4·Ï™¹ãƒ†VþßJ8rŠ§5[AÑ<ŽâcyçjWwO›ácBA,ùšT"1Ò™´±eÈÙÜ[8 ós&âA7ˆ<w£ê:;xm*P--q¿Ì¤®†èùÀ®a8vË’“Pä5"ëÞéËžfŸTg˜ñ+kådàuÎ'¿½îÕW·–ÃP?µ¨¨‹“¢åô¸·.\˜{éž²iÉžÔɾQ“gœrŵ£WvªÕЦËÞìN¯@š90é©C‘Ú'é3hû?êh,/ï÷—xß«é—Þiiz¿asñÀ{(ÌÎ1B:TÙ¶(:ªÈ»^žñ†U@z=VpѰů#ÐHÉ$å¨/À¹G ö‹‘fà=L€¦i¶:cÂ~N ;#£3—b\‹L±Ï3)wådŸž6…d„w«}Vm€íë|¾ðŽlwñâŒDquU…¯d2¬íõÐ"ª¸ 7Šâ(5N>ö:Œ#nœ¿¡1DË*À/,^btFev ¿IZ ˆ1WQ6ÁÖšN$´ËÇ–Õ‹ +X ÉSêýþw8w"‹¯¦X:,ÄAÇ´^XTÁ>@8R@à=¸çÀ>̦šv,ûiRôY|ï» aåF#ȺÉæ?çï=rþp쉵?ƒ¹ö.ÏÊÂ}FýÛ$›ùßFø3òWSóëÕŸkš\Õ…ïr¤8œàLx÷Kø8¢¿›¯¿ ƒSM$ÌtÖzÒ67°»¡sžc"MM’GL"Y ˆÜñ¬*‚8RþvÇñyx¡Ô'ñãáŒ>1(q_F?)e»óõ ç¤ð£‚©š@¤vðo‡ÃßØ9ädÎÔåµ®^ärSjšC`øT۪溕,÷.¿Ï”¸àÄ„íÒûh‘ÎÁwQÚeß1ÝL2Ÿ«Æøóº})aÿÛb?ërweŒçBÈwA*hEWjWüc +¯íz(uXã°ï=ÌrîëŒe_›¿‚}4Ï’µìiþÓÉAÐNíËÓ§”œkØnVj/œ<©4ó~_æ—ð†€——á™4 `1€Ï¨"žÈd' 3ýTðN.ƒcI&ŽG ÑÞ(—öKIY^íþ˜ëÖÕ…´:˜<èu:²9—!]0ÄnÞÿqNCsÃf»ªo¢É>Ålo0JÃ[!,@2ÄÝò?Ž´MÃïõØŽîÝ;=åå®?íÔ²åÁ}Î[Å)×ÕKõRüÃí4—š|+Lf*Q7Ì®/ë{o-2¯T%$Ž™À:|L5õ¨O‘&'ÕìÆG§' k¬¥“ËE¯¯Ëc0™¢òÖà±J¯è-±4©›©Çƒ×¤9ò7a™�Є-Kàý3Œådx[Ü›¾X‘°ß#T´4¡?¹=Òöh>eÍ[=©³Àô|oó×f\§\ÜXNZ¾¡'rüˆ‚9.oß)2Ìi3œÁÈu©·]O‚dГ±¶÷÷Û]�ã’>Î÷?5…Ñ Á&üoGÎî´DÄðJ<@ú_º3§è¢ü•`sxü¡©¥L¼€8«“XÌë¢�aœŒB +¹-Dð3}a‹g¹•øn?´dkl¢!8/(¢óL¤• ]ª¦[XäåKÁXWÕ›ÔFH"ÌYL‘Gžü:¬˜{:÷fÖŒ¹@u1¼Í-šVî9¬<ªC*|_Ù;ȤÑ4ÝŽg#¦iéP£ø¸ÚóL®lss³/*9ƒuù³¬\„Xý&QŽ†Q°£?Y‰”bƒ”¨‰ÓÃí0Ê|ÐJ2ø~६ëKÏÀ½ +çH¬ÄΚ£ß(‹¡7ÇøIfgÚšišÆ#˜Q^s¿ñx¹oÛBhLûÞIîDµÕõ])4;ÓtÞd[zþ¸2_ßæþäQºE¾„ÌÉ—!JzØ\p§ƒgįæìÌw&•–öR2 DüíÍ<ƒí~„ß1è‰1dØMÀŽõF<Ýûª¤š·¦˜žI¤m—Í$4Ó@¶k³§Ùqš™*/w°ê;Ž½Îëp0ä…ãЄ92XžH¤‡£9-¶t‹RÎ<‚…”jÔ>ËS-b<GÄq„¡¶jÚhjw7êLVÙH\0°U¤×Ã"¦ZZ?˜¯¶V把Œž¾>1\TäDƤ‹}7B8F„ÖDœ‰d;-2nœ:&‘PH`–î¸ÈЉG#âxE‘Ò)ô®ÒŸ¤(¸¼É#Ù€ëpÁ"Õ6… X7gÍ]Ý›)…:ÿ4tÞr˜L4²°»ï2ºéEðÈ ¾1Ù¹±ï¢ù'ÏáßìI}iäëÁé&ºÐ0u4m’T¹©f;˼vªU7gN*µ^[»Ö Ãn9ËJëöFOâxÞàzgUûšðq¿1B?r|dÐw;0'éUÔwónS×\G̼-‚xewŒ“êKoDNó¯ jí²±ZAƒu‘j±‰žþ¦‡ZÈÅ æVìdñüSŸkíž4ÈÕ’Jîµ¹¾öŒÇÛ¯ÎH|ÚÈò¶ÄzR|3²Ÿ]J<Ž«ÏÅ +än§þdû¼f´BDÄ=9µÄûA¾ÜÃá<¤Â÷ÕŽèLØg¨Mg}¦%’®ªØÍQ½hRioÉ}·W%sæo(øxÒ‘ç±…vŠâ¼Òß?M*Í Ù\¿`&@n䶌û½`ºÍ/›tÔ½ØKž-"áØç38>\¹w÷™gGʲ·ß•–a.ðóüW ’¾„Ì[¤l”Nñ?N˜w6ÒËô;DôZçÝ—½½ïŒ…9¡Ñ9\úÇЬ/‡-Õ‹ +Å“nq´èæ]+Ë;¨mFÉ1‚÷ÇæNçxçt†aeØ`ã€CÑóÃ>6ð›IÝãÊüœ"lq}Ø7ÅYžéÖ±ÛåI¯¡qh Ë’Ð:ªžI[Q/5Mýa>Tã¯6\±;46¥ØÄ3öë›\Çý“þ[A# +±õ? ‘F%>üðÃFþ ª{»eß$¦ZhWOH3Bœ Íøf`˜GÂüDn ØŒa„T8דm¡¥{ŸäÍÎÔïžx"«'ã!B{,Æí¡ÊN+ÒuÃûØ/RÜ6+Ç2Ù„’-»=âJKìÅüy¼=Öõ‡¼öýOêÂÏ}±tŒ´ž_#`jµÏÚLΟ1ÜïÂtØ–Å·º³j8¡YGé&IŽ/S.>³º°#øcÇðŠØ™/þÖooK 7ûõå²ö¾91ͽ@õëht^þÇM½‰ëž‰Û68êË*õ?”ÿíp8©ð}¡35ÖÚsAõ*4Þ]÷¾»"Bqù†nLd�E£ÛÊvWbZ-ŽølŽßPTÔCmtùk?êœN§KZ-f´ÍɘŒ€På"0]Õ"¸û&ì\ê ú=Ì»vÇ»,âtíœqeáWÀ'´474®8ÐEC¸ÏlÛæøJGxîdW··y„wžúµ7í®‹$oG®€¶ëE™?ÊjñßÀvŒ ¸ƒYÕNäp0Umü3.´dÈ8Òߌ³Ø͸۟ٳ¾ã¬qG=[ô9¨õûg�yËb¸9%@%0¼P6ˆl Ÿpu8¿Å†}‹‹»Žõ¾!ˆ÷(ˆ¤Cƒ1#œ%t#²,a +l*™å3’ž0I‰bE�^¡ä´èŒØ‰~¿½ôïÜ‚´ì€?hÈò«}çDÁËw×ÛÉfj/?à’ÜGjÅ+Fœ¿f1¾/òIÄœžåĵÅ^~î?é(tè„o}çxVb‡É:÷~ÌŽšAàÐDÏ�l$ün‡n™Ït%º—AHC¬ìJ-B2Ìy†Cõ Ñ*€^9°°2Ð,ø„EÊ&šãP-+Ìw0\7�! +¯ xãBgûÚçeaÉ·õ‹|¦;µÅ;ºæès9œhÄ[È<ƒ¾µÐ+wõ§ôþºÚ+"kM\°fþ¸¿Ò>Ò{Ÿ}¯SÙ,·|õèÁú÷O@;جá¿`\Õ€É^·~8‚/°]³)VŒ-¾r°ûÿUß ©ð¥ÑL©ê1°Å²S@ª²Ýa„+•;ÕlJ +KW:Ñr¬jÈ£zÝËß>œ†¦~¢-_W<^u…eð׆¯üa›µnL´#>&äo�„êKg²Ã0e0ìEñ¬+ïøYó}×ÜxØCæÃ.ü¸Ë{ž[üëÛzéJºqãV±g¬Í;'Š¢g‹©E^¦º5½É‡AÂ=3`ˆÌ%ª[D¿?ðEmŠªÕàŠ}�€QøÃ"ö—{‹‚›çÁ¡ƒ‘ öuÇŸ“a6œM{ Í-†°ÝÊq`f°ø pg#˜D‚ZÛg„D50[` òC@�Êã¬`yûFCDw¾j‡bš™$¶æО9D¹1hˆ$:‹€J.jÚ|Â; +z™l*e{øÎewÜ;áB¡?J¤°ÅÃ}×ehû$vh°Ç¼^þ:@/á7ÌÍá—óšg¾?¨¯<˜8]°ábbý•eííˆ6›3³ÓAéXˆ»'¶¿¶tíÛjÆq8û/ðî ‡6ô6›î¹Ì¬Gv„”ÔŽKõåAmM4š½dSÞ¾ç²ot&W _"HÚ6ÜRÛ䪿Q ´bQÚ2+À†ÉÇ�ý’CЀãŒM@g Ñvlpµ“9/W†6|Ò1˜oó‘ó‡{`q ±£ÄpQHö«lq…–Žô;uU^«Ôç{°{WÒÈηÿ®v)r¶±ŠrFo<¯(©~ùdì‚é‚<é‚«Šd×ë¦e°{kø‡M»ÈSjµ%öš‘h Üis�`æœÚ¢;ÕˆÙØŸ:±Ÿ >³zT—w¦z+ᛃ. ªð!ýÉŽ-ˆY6†íÞ;�‡ñÙlùk›zò™HiDÒ¦þäwYa#<1Eå¾êº3Ö®m" 9áK;|Îœ9¬=²!¸²Ò;pB´Â;@+„V=¶¤E¢%xF2\\î¦ÚûÀÈÂe˜X öp©öÉÎþÎî·Þj›×ÜÐÓÄ ~†4µ_ùÖ·îĤ©Ž²°Âo IÁv“ýzs³É<ø »úêÿ‡ð[ly]Ö@ŠÆH{´9âª^àÄ'@tÒ|RŒÙÁ›÷þ‚N‚ñ@0¤uCû¥ÊšKf•–&¡!2òÈðl‡6»³ÿÁÔ—røÙ²Æ<„h_g—ÀZI¥ Q¤0v€Y±i8`7v�ŸeË O€ µ„ÖlÙàd[¾ îý¬šU¡e«ŒÊÝ5óª«íéJME Oc¹àéÖŠ½íž¢êDÃi¥SŽÕÞªiV«½ÉPÚ7ì‡'¦Mö Ю´A—i!ûAŽ%:Õa䟬ԃâ’þüe¢šl×<¾gY3‘ì²íÜùóÁT¸ÿS^9¨8ÇÂ$³ÚêSëÖióç×Ã.퇖‹4ŽqÀ~7a1G�SáÛ€Õ½Ê9©•Œ®9 +ïåz‰÷§9®-Ù'y|@=C ªHFeoÆ©,õs†AÆ!ÙâDŒ¡—¶zgû=èȇOܪxg•íÔŽÙÞ×\³<Ž–RWwвDÅ{nÒÐna eh™Øå·øàÅS,ôô§â?ÍØÙ d)ÀÆ"…a±;iôv¥"â9fx.Ž�^â¯9õ©ÁÿÁ;Õ£Fr@îX`:„ÊòÒà߇¿‡Tø®YÕÇM)ê“ ò¦U£,+’XWCÃ~BËaYS"ܲŒeö€§a®Éé…AÐÎÑÎ ÞÉ㮸›g«Ä+§´³ç~2¢´—þF…2ÕTg¤šb駡íL˜8O{§'½¶à~l»VGÉ>.ÓªºL£:6ÖÕQ¢wW,¶A'5€1̦x +&m‰Åö¬Åb”I©2ðþf˜=æ76Únc£ô^o2ŒëXº4౪¶G1ËÚZáiÅÈr@p†áƲĨìË»6ôØUþM˨”$eÙÑ€w£Ø#Lu‚à*faª°ø°z´@›ÒLZÔ*m[IŒ‡L> ¡ÆÝÁ°ìC¢Bv +„¾;ÁFþ4Ü>ªÕ±{¡áT~Ch=KÉõPþx‘eGÁ|QLÍipv@æ£Æ9ãùõ|DÒÊ¡ÃUÖ¶56äMKŽ²E_¤˜%뜑 ËÁá°¥±.”ØôÚîÚ¢ÞŠ=±Íª`N`Y™Iû׊¹€¾÷X +ûw1¯ ìÔ…Ú¹·¿²rÀ,@¯xnWSëicÆ[@^d‘æÕÙ;)÷ÝOO=±dÒïõõÁóù˜ªó+›ÆøuESÄ—V-çu|‰?èbŽ…ógo/[¦åÙ¬~G!¹IOǸŸ›7pYЃÃƤاsf1;ï]i("åø/n=òÇ¡÷ÀÁÞñG• +%ˆùË®H‰Õ\½</8ÅåþF¦³}§lz‚3FªÕ±WTÕrd¢<³ºfX1Øêld·güÏ—OM_Ø•>‰¹òe°û©†ËPèÛauä„ÞPÕhÁ<¤º1œ°ÐwyvmÉ—Oßý’†ßÖÖÄú ¡×1¬4:M×ê'Å1J©‘,þ̥_ÿ ÎòÉÞ»—-“hn¯³¯ý^ùâ½~¸``S%î _6 oIÎM%ãf¶ê¤ÎßÝrï"pÜ4©ÂOv¨£G%â70 j謇6; ᥿uþO +#|ggTZ|±ò6gÀÝ�'ä<Øþm¶×nàjFÍÙxÒÄ÷cj͆žtÉ +l“ÖÇIˆØFlϹEä6˜ÙB еlÔL=ácCwN*ø«ùe¼¯VZ#DþpÒ¶WQžº•§Å ³^Žñ¸;M > +òÄä‘÷¯¶—®£Î˜(ÒŽš5™~ˆ×»û&Ãu.Dß ‹]¦_Ävdô%hù°D¤`Ï€àvkÐ8™a!83ñdß•%æ!Ä,_û.‹¨öBßíFâ"ÀR´€NÁšqC'N•è; +÷iŠù@2œE§íÃÐÒ{éAcñ±Ø®e^ +dâ`î_ú;-YkÅÓÀÜ)^.ËÎE'Ö÷Ú–®Z@É +X¿tqñÿgïKÀ쪪t÷™Ï窺5¥R©¤’ÊLB @B…`�A1QDEA@EhÛÖñµÚmÛølÛnÀ¡EÄØ" aHÂ2O¤2Uª*5ߺCÝùžù¼ßÔ-*¤û}äI|çû’sëÞ3ì³Ï9k¯½Ö¿þ¿Ösœéö (2§Þ‰œ0ô&5ÔJx=ú÷‚Fßè«? w"ìók²‡rÙÞñóä]ßÿë/ÑÔ9Ò8ò[DÚ~°àO”<·Ä9FØçã¼vâ ïE>d{I7Án,§øDVTµ–pL+}~\¬Ö;¹ª®`¶-uÜ]¤TÎüû{ý÷iõ|éÅ€2æ,”˜6 vNpö¨ÉµìÂוi89$“Ê¢#ªi–³È˜¶„§H˜&h_´¨ðZöu”k1¹ Éôå¶ùËŸ`XÃ_T´Píá¯ÖïÛXt/\Xz½ÿÙàécT 8Á¾R+ãRuÓ÷V…5ÔË,ë¼Í³VÂx݃€·Ø ù;0yA$Á¾�ì.PCø.¼nèÛ„DËb|VQð"¯î†¡Fø…Á¶ýuN–u`^AŠ¯e5†Í®”iç‘8ƒÄÆHfXMØ‚Rê r…}é´Æ/;�³A ¸'ÌT{9qb«CÙø¯æz"Ïeu6ãcQˆkšw;ýbÎÝ_ìi¹ìD_î`?ÜZä20½C¼Ä�kO½7˜ú)_îÍ<dº¸ËÔ�ù™ô€¶FjJ>°hZ'<Ô)@¿n?ÀÙü³xàÈ[\„:µLÆžŽðÆWÀ–x¾«ë›¤ù¤åÊ%§ÈU+†«Š¼u5[«ÅM¿Å଱”¨ ½×#ÙŤFÜ@„ sG¼ +<†£Ð±twwK‡9rÚÀHGœmˆ×3Ÿó˜Å#xËV©–òmx3åPÛêÕ<Õ…k™ð\½Õ¢·>Q&77¶ëÌì…®.kbò—š^âw¸¥?É[+dÞ¼·v|ÿÓ_¼FãÇ÷͘6ík==7âÙ£è©àã%]AÛÖÖr–øŠ×iÊ„g½j^ ˆ ×`ÙægNœe‰®áÉV +JFèOºË2˜îÉ¿¿×Ÿvã+3›%KÛí¼±&‹‰cýÖÛÚŽ„Ñ^]ò í*Ië\šV2U6çh¤^ª.:Ù^TIý3lQ–ï2™ß@\ÎÒyý~•kç]qE4Y*Õ;I°e?ä¬ÒF“%×Âä]`¶=‘ÃAPÐ|Yuavûæ¨6ù*2âS{Íà¿_ãŒÇSCi»ª!¼†s,`{uë?p~xˆZš9?'HN`ˆQBÆÄ`ÜŽ@]N‚Çât)ØVBŠÆá¢�¼ˆý°±9ºÂÅÓÖÔµ|Ðé?½¿÷Àåsé¾lhÊÛ±–׶y.M²3¨¢Óôñ߯?AÖ“ƒ9°×a�ÀìᥪkÑ¢–·<4[CèJBxám{ÔÁ‹eÜîZjÄ}ÐÛúdZÙ"s†—†~ìÌðš Tu` ðCœÍêNönnk;äK‘9;èóhj%ÈMAOøyìGÿ™^éå>+ÖgÌž?Ÿ¢EÊýÛ¼ì2 [ÈÐ4¾àv1Ï.)'±Ç¤…&+÷ôg_ó çš³‰žö±mûÂa'‡×ˆáþ„> [‹%h6i>s!’Šš,ðoüq1¯¹ãÞZbiUéá¼wÓ¡¯â>Ï4&žá$_ØZlô%\ÍÕÙõÉõG’©ÊCÒ@‡H(“rZÜ5ÅLö`dõê7'îûþç¿|Œå…Òdν�O1»æSÔï¯ò7¼tM]ùݤš‰ltõj¦„jO.çá-¯ˆ²sã[|)Ü÷„Ø2öÖ˜b©ô2QØ¢Óew•zýwÚïp±qÍ*P +¥|¡~Jä-ƒ§S„]ñô2Ž•ë¥ÂSœSžÁg‹[ãm‘¬¯TR486¡òmÔ«åß(9åé°näKdï•Ä8”eg;qšª§K³«ü«ÛhnîÐDgµ•O)‰žžø”çÖ=—HhK.»ÌƲ¿™ò"YFóçÉó»7=Þhûá]óoV4¶U/ • ¢‚a `ØàV2䕸Á|3lš¥‚Vt¹]>düI�á‚ÎT£êíã Ã[j&ÒJ§‹ms‹¶¾´!ê ö²¡ Õn8~yqYBXµu'ˆ¨o ŽNgãÆwl³ri&ŒœENð•Ž}}bîáʉá”ÃÚÚLéÄغÊì¹d›_ßä¥å$YÚï—Áô�â�ÝAÀwfŒ�•@—E‚Dï͸S2·!äÇO2Ã~`SÙÔÞ„2Ý +¼, V»ÜˆÖå«Qq3¹T׿ðú2tÇ?·�Wg2ÓAÊTo³l8íØ‘›Ûºð®Dj´GrEÉçû*ò=*âú˜Ü•Hµzï»/`_~KæjõÎÀ†s¤ªjÎuR|™ž›*H5ÒÝ"qܪP‡bë%‹ã3@Ú§ €1ä-˜ý"ʃTé³&ÏÜŸîÀ]x§†ÿ¹zÿûÿ§=÷“©‚"„¸‘ÃÒš/íã{lµ9Ž’ii¡ÏŽºþh|+„ã~jšqÊÓEi-/äáܘȻ`û§þŸ6øÿòà§ÍøÒdÂ|Pöî- ¾1Í€JìØ£ë×Ûk'1È£k'[,õ¡‘㇇ó½ÊÍõó¥ü…0*7j"ÿ«*êPfYœ¸Q®q§¯„W<€Ø,pœ5 «SœxWxd˜ŠRK@«rĵw¾¨bßåœRA#apÿÎǼaZ,Zä™nRM$>_®#žýÁq3‡bÚc—Ìz«À†©c§Zk( {@Yç ä1l 2Î6¾¶µSœÙ\s6ÌãL›µt‘!£3˜;'U´‡%bŒ”1çÅŽ—¼©VË°pô;øÏ£"¡îI5 !±Ù$UE¨eü¡ÛM«ÆÀ�+Z¶Ú#œ‘ç Ë»Y;á;¥×<}0žpn ä‚Nxú•M¾ò¹5Ù{þí§T[à +ú-Óà‘†ˆV6_SïÞ¬Ÿ~“�ñÞ÷%«Ãe{|›É ˆ˜ÛËôC*ø<1TÀï4f߈J'ôë¾KÞòb*C$Éâoô KŠKæ6–±Ù;G=D-ÌeEn¤6Ð ã>A¸vl4;a Ö½¨¨ë7œÚƆÒr‰Ðn³í’™G°Ú*r…¢AG5bsÛ)ß䦿ÿ÷{Ð …jãXã6ˬúø}í?EÙýÑO"n?±)£ÙÞúÎyL„Š-ŒÿD¥è‡k*äªâ@iŒ-žßø=ú€ëô,-Ë–!>'“†”ag ¥íÍ[úåmó—Ò˜µå…¾(®˜wïüúÐ <2ýÊÖpŽN9¨ñ¶lýjžc_ù_3ªÖ¾¨æ±ƒ*¿¼?˜Ï[]:Q?|lßdêFêÝÙ^̲&IÀ\ˆ—m1nX¹#P¸ýn1ì¡S²ˆµ!F¼‰žÿñ%³ÂÔß9$Pðåè$¡Œ$ÊǦûÒi.-Ím‚'ëktÁß4x—)*WÑY{”nÓt�àn‡"4qœ=ÕHà;ÅoåEAµXöZ©³L~GuÙ¬üN×®jv®˜7?vqW³jõdûðª…E +m8+@Ÿhâþ49õ3‘и'EŠÐßL"yàÊS=3@[í¤QÔèƒ<~Ý_àa“©Áu#4Ž%Y$ýÆŸx|«núœüðŽûAðó³Ý¿ùÑI/ÅÄm'|¶y•ìBÐ#ç!ü|´m<FûuÂvå4I†Ë»ßÍ•þ£e,‰—ìëØ ñ¶œi,ÈÊ܇w;vJß`ð½Iš8£Ù_f4ëfp�¼€! x̳Š©oG4©tA1Èëá7ã9<åÜ“Ûòþßù€þZ=Îê3Xæã–ƒýqÄ/^ÿûƒÇ£ù"Ú"j;®^�ñä –—ÚÑÉ|ÌÐŒù3X(’—Ììж¿ü¼óO›ñÍ�¢ÔÔŒ[l‚„w {}‚˜©Æh¸ñ¥Íã8é¥.C¸,@¨-öjLÙW•ò!‰åwýÀã�åý‘Õw@½Úª2°téÒS¼Ezì]Ï<“¬v¸×AžçûØ«†LY1JýíÔþLXðB«ài8É�•¶™:œDH°¤6“”½oé±MØÔy<.4-ýhemLö±Ÿ½ç‘,¡lMýV.ûêîáŽñÝ_fI L˜Þ°Câ#©te@?<cøb�"@k”d6oÝwʵbª†Á€#i“„<A3ŽdNUKc£3ò¯#rÎ"ˆIÒïx–!9QîN°¦%3ÓÄ å\ ʺi|†P<ôœÅ‚ÅSß.Öןb˜¨W.܈‡P4òSoÀÝùv¥Ÿô% 01J~ÃW¾‡Z¼3Ï£Ê ÚZ 55U=获C1ÐöÒåÜj÷Èüêjš<-?/”$IA+K¤7mÃvd„P¿•M·§8Vf£ÐfCÒo?Y2Ïœµ SE0õoÇuó:Ç^¿ŸÇ^8œHÑ$kùØåßÿïÌé–ipplœ§×‘Žfu}.:þÝ©»/þé–þà:P ¼]cW®tA¯/d²Ö6ø‹CnˆñªZßnÛ÷ò»Óf|=ÃÝ1Ã+~I¼LÈLhm]€ˆ¤Ó‰zR¼\å)öŸ»PL9ù×ûú OÿB‘ ¼6ÿÍd¬9ªa ѱ¿âõä2ùݶ©þn èî±N2¤•cÓéyµ›Œˆfþ'€‰Ý|;ÜÙi°qu¢¢©²Ýä55´4QƒL{3¬!³$‘‹W´¾&-?àJÀ6 :Ì°ˆÝæ‘Û;ñ"çlž3ü\Þøˆ×iľã}âAÂ>å>X;.@:cò±U;ëƒw†Êkfêì…Ñèºu'=h¸$B4 V`7ÆJç€2¯£ìéò\ŒcòÜ*çñ’։ѵ +ILTxc'ˬ®‘Ä"œóÕœçzÓÅ|-¿ÍDBŽUa÷_× +Ò³‹'y…´Ÿ¢S\+€˜øôÕö°Fá·S'¡èùiX¢öîˆãFAt¯†ÜHæ—)—kj¿ÁH‘’ÆN .^P?»¾âW®¡²¦M¥O+ߥe¯fjË Œ‡ÑëaÜË ÏÊïEËt¡DPŽÄyzÏ28‹1@¹-—àëxø@âÕÿø^fD/ çL¯öMaæýõ{Øôy(šÖ]'ëŒlêó@âÜ,±Ükðd—à9zÀW-~sÉÔséìŽ>“›ê€#ÆD–¸”ý{Àëq¥‡° ·9>„wÛúR¥Iþ9¦Pü>gqÿê…}€’qqž]Ó¬¥ê¥î\óâçý;¼_q²rÒ»‰ƒ›ö'ñWº±_Ù3A¨b¨Éã9JõºþÜËZ9GÔ!fœL˸ˆ„dÆ™Ð@éXù}òšB¦"g-Y(šòg c4w£*—ª©%♸õꉛ†ïÜ ÇævºÈëã^¢dæ@€ŠR?ZˆäÐÞòî´Ý‡ÔÀ®'‹Î°É^+ÝÐd4™Ê<CY0äK®Àcly®£Ã*Íô´`ûÝ+ò O²iÜ€ÏûègyηntͽÉj 6Ð@تŠýΗʄD†½|ß�Ôm-Âkg€zIaà{NW˜¿=oªÜ?Ùðí;žö%ñœL„ûQ¡¶ö$hÙV”"_öÙÛ[˜ÚæsÁ™ð7¨¿[˳âwRŠ2å>œ›^Bêàî>7‡áyδXQÒÔЮ<èË´µ{¤ú•ã‰(bÞ€‚Ÿ¼Äás†¶è– €+0!š-ÆŸ/™py°Í]Å8O#n,± vÇLÁDéßMzqëÙ7±d{K4Û–ôŒŠÑŸ},N>ñûýE{€SÈïÅÞ§VÏŸ1 êÉ™BövLÉþïÏ¡’AÖäuí/¸å‰Cq÷Ć¥Ty3ÿC^4þw¶hý�5ßÈ2¯MÜæLø<þÀžŽÆP]µIœ‡sz ²Bp¨… 7ô¼×I_^4? Û€Dã…&Ë+dPT»åÀêÕoq ô5áÁJ׃á~2Xÿm®AÕ{Žî:·¾&‡å-“(OÚ1Úó Õx�í¾‡¶±Qdü Gi“›|®ð§¾}ÞQ˜Ïr‚èäìe+g"ã¾i9Wù[–Ñü’_ª´Éò½–.U«¹?þ—‹Ä+ÆåÂÛns"VŠºK˜N´@g™”oLEwbƒÀ”q@™'È<ûÔ¶––ñ¬~°fª‹1íI0ÇXP·²Ò]]e¶³ò!BnOI½šŒfŸ˜}dÓ¦r²ABÒ‰†Hh º—aã~˜õÓ(VÙÃü�Ã?¹õ—Kšþ¥Þ»7èôÀtà@yâGÀá¾ìg%»¡}’g,: ×$IÚÿÄŽ?Ž0Gç‰êÉ®Di|—3콦%T¡*Z1sôø4Bj^ìKÔ +^×w²ô“©!échÈxl˜nW[…l¨ä8Ay�ìÍWd·œnôw¤2wcdƒŠ{©>zpíǽê-èö5%ÆøO§E–YûZ$…ûz èÆÚE÷}93z€*XxܵÕÑ©!ÊoÝI‚+Ê&§Ù¯…þ!<ǻӞçák¢Hj'¶š>_ÐFŒ-ðûÓ´R2R±•Pcž¸Í™ðùmc&ï¶aHuë¢þ¢¢ z@ÔBŠU¥-·´Ù˜"ùLkkùE«œC÷ûAýI1MØÅZjÄ`;dQý¼]Ò^˜Æ›•í*kj�®¼õËgY¬î ´µmÅßFÅ°U¶¡kšh-• 1½$¡1Ãw\P7ƒÞÓó0KU˜Îû!»îƒ4I4³`q§œÌ.š˜³r¥Ü¨\ƒJ›ÀE‘4GÞÌ´ç‰÷Q³¶y+€â[›šˆÒÔ@¬7PFÐ*»ƒÞ&1¢Ó,ÑŽÀùäuFÕ´T'OZ(pMÕŸÓíLï^(¹öæÜt3(u„-¡£=`¾lŠÊ„[Êž#õÿÔ“\&s<”>T¥•â4S><Ï€lCµ6Œý§”–}ÌïpÚ0*íà_ò.¯©ªýª~‹ÍGÁC\Þòß7ß :þsHØ•`¨ÿ£¿¾ž¢LNZŸdTëxµQðk~�ƒó‚´á“NÂ:çyïAÁÎO±-Oo;Œ< ù4Ì^ü!¶Š™bèö”K_‚‹jȽ@o–½Ê‰Î‡ó{âñÝ¢èHôoU¶®ZåSæMðœtùo¨ûYH;çóÊ•ÎÂÆ~õ‘û®}¦~AxO&b7ë[u'=•s¼¿~o{@0ôš]ÿ[7á§q.î°WG‰¾Gž‘SÜ>š®‚…†ÒáÊÄË÷°\T#ŠL_&Sæn®_z‰OŒÍ•C]gà�{ÒC}:º›†è|’äçU$×êu…|Ôiï`eòÑ&W˜f¦ËSOz.†TݸQ^*ÉZÖ—1…¬en1˜¹B�NÚÜÔtJ¢é:$P0qnÁpÌõ'Ý—ßzWõ˜&¶ )ºyg5ÌXòÆ°ÒôâÀ@èg¯%<?‡¤Æ{ⶕÏ#¹‘=®ãÛ£É{¡|-í$»V–ö)ØÁ»G㣇°mÙˆÁãÌiÐîNLYwÀvÀ'‡]b>>ã{¸¾r‚«rlÚž¸£êUzöN`{ÿÒ?õˆT èk³N^\�Ñœº~ÿV¶/O•áú +ßõ\ Ð5>%§Û”Šf=Ú€6<gÃÎàH0òÔ¤â?,¦ÉË^Í‚fG£D¸Xh€t®GØ¥³lp˜D£iê,™éIRuŽ~’ÞðÓŸªÖ´…S.˜>§yb¨…>ج Ü韙ð·ÍsúšX¤É2š°£‡§çé<Þ¬ö¬+æßغ5/ph6^<¼s¼Ê‘nwA„ÉsFîéåµ'Ê®µà, ÕuGAÅgq+p8$3‰}Xë8qmt¿±Å^‰t¿¶jU9az’‡¾ù‡ßôVùÿT4“Æ_œEb8û:’¯OÃ#Þˆö[#¥Ä#Iå+ýÞ÷�8!ÆÀ"õ9Þ6¿å¨;Kšu5¼†s ºÀý80àÁ?ÖHùZÌøÝuËrÕŠÙP˜©¹ðâÙ,oM«›¾ðU$“ßû+:µïÚó¥/¤šY*ÿÒ€[øÌùá<ÏC˜á +`´I@<·™cŒM|ÞŠí¹¨>T6t¿ç¥êia„“Ÿr㦬¼8Ç4µG\²Þ·öD˜á¤V'3šäqH-!·Õ¦”„KΚÒÏ:ÙÔ6G¢èõáÌG`Я‚ÐŨ‹óš9@�þДÏ~¥ÿµ¾DÜëJ‡ôˆÓil>AðMƒÚ2ƒ†Çf!]Uð¼ÚÃÞñ@U~Íx¥ß» EÌI‚VˆµòÔS®1@ðP͆†ñƒìر†Ò¸w%¸< )b ÃðºmÞfëÌòx,÷¯¥ä†vP&"ID6õÆmK]v(ØFx†jU‹×�!lW&oÇA`Ãušm¢ÒBT2›ž4à 커3¦©»W×ÕÑ“¼ +VL½kÀuKµóLNe2˜”ïý.ôž¼Ú¯±mzÎæ>1ož.{ÓÂ-3A7ü)LçJºþóÍü{±5º3BMÓ$FœùÆG_¶Ù1*I-fZnÄ´]t¨Åàml<É@Ò{„í)η¼d¿¬£í÷‰ÄYsÌuH濨V«��@�IDAT]‡nP֜ȡØ÷vòbæwï&›x^^ûgOð|ÇfBjú±²P ôJä×£CGž(yœRP«[Z(Òáýåì`8,ÇXLõ5ÙØðLÇ4 0SÂÔíwÈK< ¨ZËÇ–Ú«›îs†êîÍ©æ4¸%xn†,‘^€Bï…99³–wm|RuÓSsÄiÍœ5•óÃCzjj¸!‰’ש!·£^Á+—÷ò—û|éóI¹ ¢ÜO :À0ÿ YŠ i?©›ö?›ºU857¥¾j?p!ã/f¥Ëh<°NÊ'ÓD‚l)Úì¿`Ân{$iv.•zƒƒeI!(˧-´<Æéá’V˜™+Áj|¿™`8±7§’®æPý!Í`º.üÌç{¶årC{~ýëìÍ�¬œk»},¦I ýžóMÆGü+ç‚è÷ÔH³õ7Bnž¹dþ|Ô¼e×(©ùåwÞý€Pbž°Ãˬסàa±ï[Y÷€®ªrSSEMA"s*û +Ø(q${ËܹÕ"Ý‹DÛ4xÏ‹°™‹ÂÌÀûÓhþò¯?øàæM==ÔxS¯ò¨mo¾!Ú}÷cVÝt“,ñÞYÀ,ž/Öt‡Z\M\ã¤×·c0‚nÚ2ðJ%EòÔ¶ß|¯p%lfĺ}X ÁӵВ‡–yÝ—f™ÍfUx7ÚDû¥|LÅbüà8ÿ†ÂÄ꩘Ü;,c¡‘}(ä®Êž‡®Žé;æÄÝi{7f§»Ñ¶ù¦Ûöcð颃åžfÙíÇPGM½ÛW0çB£mLž©H¢3ŠÛã…Í8gÓ¦ž‡ÛÛ›è¬áýåëI`«!¸ú‰?69»£˜Ï›Ðü8ž¦i&saÁd÷ùLŸx°C%ÕÕdöìÙú+©ŸÃ¡ù2 ôO"©qá®ïaû‡ñ.R'âŒ[Þµñ-Š¢—”¬ë$3EàeEÜ)ÅR;• `ÙJ¤][wïÌ]ÞÞ>þ¢Ó^™þ@‰(I°¸€ËüHf†D×YCþMW'ÑZßrZÊFúõîë‡k?øu ç=.Î^O,©j¥¡T>¬¢D¶¼¬j9« sú•03;�òÝΟ‚RiȼÔ"Ë_{Ð�ï2L[D;‘X“bFžl™qéêG�[Úùv¤é»»»}IgDúCOÚg¥©ç^µ6¯[§-¹~°íÑuT¦¨l`p¾“®•zbˆiŠWU¡„0[öíãÏo˜Çîë«ü|)lWÀÊœMË%¢ÐV®,Ô $�á 4ˆåcšŠí‚,®‹¥’ñYÈgë(L`‚}i×¼á[Ðqz¦À�€ºæØ݉ÑWA-c eè!Ü–`r"´î;;¶ÙÎeî�‹ªdLmNªèv~÷ÅSëAqâ:bRd9oÛWÃpï)æŠëWTùé€P¦�itˆ<’Yö~þ•¯=pME¾—%ªfŽDêëO +`»·[¨¶œ¹3ÆÖ dc¦]‚ëǬæ¤þ¤;ÒÁýð-ÄÙÏ/±dëm?øéMø:áóð7Ä52ïcÜ!»ä‚<(ÎYÙ¸(óâH!ãI¥¼×u5 çä¢ê¯°ÏûÆ—vè¶ð–ý¬$»^Öã¹C™ÃÕÊÑÕaÛ·§§§%àÚ¾{á¹sÙü¹ÅÆ9÷¡éG賊w÷ï×À>ùI�ÍžÇuœ±¼¿+¡ä2ïÙp¹ïÚø¦‰Ñ »v�užƒ*c0ÍÑHiÿÞMªçÍ;TU㟢Qõ±hûI^ÏÏàÁÖK:Bñþ<è§CRànLÛ5TÚvA„qÜûìL¥¼Œ(Ê]‹M¡I(_[¡ÍBþ~ħò‚y,5bw\Öv‚í‹Æ)ïºà’ÁøN‡éøR©¨<㑘¾ØT…5�Û¹¤ Ÿ!èæ€ âó,i`YÒˆÙb¨ÍßïýÕ‰JôF¥Á™¼f,÷ ¾t˜X{õjvZéØÈñ%×ß5ì«—Þx×П®¿%¯xÜT�Tá¾%D0œ¤Rê,p)¬XÑNaŠænnö:Õ3µ6äR,Þmô¸r'r‚5Ë.Bóf%Ê'Z(è#Õ'“¡ÌHO‹§êV^ÓPAÆ»Có9®*oص蜱Ñ\–)Û[KhOGÊÈ +Ndp¥m˜Ñ[è:æoK^yÙùœ÷Çëדík:R^ùìWvZ\YWGÉéËÇÙ“N#öFî€öè>õYÓÞ‚–[ßRŸ5QÍÈX‡]\„ÆÄß2°ˆKsÒ–8 +Êlwæ×Ëúß-e|$KqШÆ0+j£÷1n{í‰æŒï^e¹Û˜…®CÍl?—w1ēܨ[ËqoWa55‰PÂn³À‰Lø¼dÑã²uff/±ð¦Æøþ‡3¦Êj—1^éyía :’¯Ç!Öy}*a + +kì$~©ž%ÄïŸAÃÐÆS§ ³¿§g 9ůâíKSX*!“<¹3äJßµñ¥FêõX~'4²‚Ô¸.™Êï¹±½fÁËÅcos¡ h8Û! +÷˜–²Ã[yBÏ꽜“ôÈ~¹fj¸»Œ¿Ä%Kú¥’‹jƒß³bÙï•dùvÑ%|0‡-ó$é÷Û'(ñ~í쳩ôÁe0äêÒºÄiÕ©¥'B£€,õ*‰ÄNSS‚m}ld�™»q¥…xKWûEv6$¾ì÷TQC2xRÓ,d%H®ÙÖØ–‚nÎCÄuŸ)šWX¬; ¥æ¥F8æ.)ä×$;Ìdí:Kí:ÆŠî§vì|ä'B)7Û©nøîtH)¥³XGY_êHQUx§(øEyÎ<ª®.‡RƃÁuH”µsvO°=Ù$Er¦c^ B A&³ëÙ<™å£ KÞÖ3&óPåR\¯ß0€?V^ã|Æ0j"«iXvùÐCuϸ²ucýOg—ñ+WB ò`ŸÍg‹Ï¯¬ Œc™utˆGSœ[vŸ8áµ7.\éªÆ´¿d±çH6dB©ñµ¹X¤müžRdƒã`]Š1y–Ñ zÉ4CWÀ3Ì°‰¤ª–*Á„Ë"Üð‘¬P=ýßaD/ÀumU¢Ñ‘ÇÀxe,]ü£É-÷"Z*Y–ËÏq^„]ZðB‚qžu™‚}8 +Õç‰Ç|ÿó™ÑҼΔe}»0ö.ko|íiBQ]XdíŒÈ6¡,Ü’kÕAµÒjŠ ÷º½¾BÎ<$ú½¨è< ÿ[ÙîLYs˜*¿ë¶ì=œÌ{Üâ¹Y‰[ÜÞldv$ò¶^Õü-3X�ØnÁÈuxϘÈUà˜?ží–“4\Ò?Òñ6ðzÖׯ/%;%ƒ¸ø×ÿpÿý‰Õ«Oæ8bI€êoGWÆñÅì‘ÇfÔÖ–=éZ•F§S‹ò˜Çò«qþaCä.ˆúË–ÅþB„@š‘˜ûãV¯4<{Ât÷Ítq1Œô,¤ªRè÷ÃÚ5ʶ½)ès�è…r“‡]á<¼ˆÚ²ÍèŽs³˜¼™Ê'ÅžÙ ÞòËiQe¢ºžG�`LË#«’ùÌn¿³à飜u9ñfçþäâÿ˜Ì«Ÿ^Sâq§t®cOÁªIÚ™r5ÞÁWø·Ôu—¶+ˆ{dx)BA´R)Ë`fñF:–Üê®’7Åcé.÷1†ú¬e•>ßקéí•nþЇԦ ŸÔ:·U Ü?!¹aCâç«+¦†;qoÆÏqäÕM·«æ衾éyõõŠR;1Ø/¨6w-Ìî5€S¼4@V‡£ÈäÄ’æ´€«x›#Ðîtˆ×?·§ Æ]rz…Âkg5@§ø6Òˆi¿écƒD\oÉÜWžÚÚZ«GÍv8Má9MT·-s¹Š.—KJù0°ˆDÿVÉ2þ�4ÈAäô‚È1Û‘-?�z+J¶ÓˆxôOr^ùàÄëûýõ{Û]鬪_…m¸ ø+dž\&¼¹ˆçúP_:ªÜð(þÚ/©š½ÞÒúÎáHµGºº‹Ÿ4Yv% ¢…ª:ÿ‘IÄþïí•|öÓb|cºÁx@�Õ1pNa)mm®©ÙÛ,ÙššZÖÜÑß°xî»ddÀ»®ƒå)¥”8<=<É.hí ÊÝ‹¤ÔÙˆ/ÝòËLF~NÈuÜ›ªœj0YÂlœ¹•Z ”×éôfl}G×�XKìÁ¬—åa.ë¸f§/« ¤‡´Ò°®[Wá¥o0óñê#ÇéNKWã´Ð¨ˆIBáOjÉ9àä™X¦¬uqøòƒX£Å$8‘w1ª¾°|#µl¾ðÔ‡4Pœkyá"؇Ô>Ûçëi’¤¡ƒ»vIæóR)¶Þ0äD£YËçêûù/^L}ýêåZª¾]l`ôëA(ÝÔ¨fŽLs»õã—ÉÛ€\è>8 ï—Þ�ÁtÇHA˜p”½å¸!PQqëd¦ÊþXwVõgm¤<¤„¥Òh@lÙòCÿ˜ƒS•Fã³½' +ßs³Òà€ø�Ô~Rˆ+¿ŸVÛ6¶Ðß›çÎ%sŽÂüúzí>|?dYÐx¹ i#p)¨ +“YÐ*X©»ä-‡÷fü‘ïàþ|‰¸vÑ&C£½Ò®™-jqFêf"$p5üðK0 ‰¨’8˜0‹wôåÌÙ×ø¹+mhr¹´:¯T¢kúÝÒi>Ä˨17üxÛ¦®¯.]¸:ƒ]Ó¥º}}ji«*È‚À^ÄXüÃ#žè<`UŽýþú½ë<§iüUæy <(:ò(Ý·ö{ñ9àGÁ+òpr(öD JÚqim-Mò’¤f¯Fèð\@#ž{l’³Ù³¥mS¼Ž‘÷îJÞùÌeè;oòßÿ +/²FyÄmÍà]ZÈC1«°c'/ˆÇÈzA¼ù Ûý¼Ö H‘s¿rq懶Ñ:ü“÷ÀA`ΉÉ7â`HæX²ªªÉziµ×)†—îÛ_ïÍèŒý$J[㸠+Á\ó&g|©¶Ú÷Ѽ-_Åz¼7@-áó`cp€jî¨,¡ôËàHI€Ò©�wS5uR�0û¤ã6¿Wpp{ìߟ¸¬ÅpÖ{w_‰‚œ¯{NcÝ«›ÞÙI½›Ó}ã�¿!. [ˆÆl̳n g–ûä¯[—Áˆ!¥Âã3“Õ3#Þ,©k%þšbV]òÐwBUˆ¨?¼óƒ5ü?œŽ¡ÉÔsŠ®ž¥äÙèã1zcÛHašÓ00¸w9ãU®³<Z©ÝÉš—=ן…ø'pl‰³hØ3M,JéÓC^÷Gƒ$p¡n +—å’"Ë] •Ý祇°¤…÷OÓê^ˆrAI'w¥’H´ŸèB½ÔzÞ՞ήÜÔ\á²7ì.e{xøWTõ»K½¬TCøψ¥ÿƒÀaèiQ±RJDi+!qÉæ@û¨�LBGŸAˆöŸPXñ¨ #h* +ÆšåâØšÂîUÏ@Nª|òwø¯•ç!ýdk!Ÿ»Õ +zÿ¬v„œhBqQ”;ýaÞå±#ò8‚=ƒ•Â”w8äû?½=Àð\5èY‹"g?cZì@œz`Jw*öèwLöÅ7^8xÓÒ¶¨Eó賂é•u*TŸÌì㥾ä/ð†ÎìÌ÷ ùÿãS"!2>‹üï4yC*Ÿ>÷*ü"–c/mfc_ºoûä¤Õk‰ÌbŒcWªº‘^oWÂxCvi&@ïZ®‹g¬Õ½{m9^Lϳm8¿ZŒy+¬7t¾š-)I}ŠoÿHg'Ûä÷Õp¸´àŸïø…l(Õk¬öw¼Í\ +D�Êp‘§G=BÈÏÔÀ€`‡±º‹¼mçà‘ˆÿLúB™e~‰}âÎúðÂÿqì3~LZøP•¡jo%'÷CåoÊcqÅ™© ŘêÊv.ƒ’Å£ÝCLÁ«™ãŠ¯0p¬ë—ìbï„>ÕNT +æ œÆ£Œ–TL±*bÚºÛ+1ŒþÓÁ yWPæ~RKUÁù|©Tú4гÀ5¼]Ë{DÛÚXk;3ÿQm‰ì‹ô¤=ož¼ˆbZ%1Cµìd^3PÍÙyÍ„~;t›oÊŒ®“ƒ)k½Ž×†+Œü ÈäýÒ þ©²qϳ õ'ûÔí€sP?Η¥ïÀc˜n`4`ª`cø—®$¤8'Lí–xQ½"˜¯ŽÎqêüeÀ’é”òØE3BƒoŒÞ"ÉRcénÔ»À5áCÂ1ˆÊG(.iƒlÑþÌ¢æÀ)u•>§ëçû‹e^ú¥©Ÿ™~yõXQLe +CƒŽÜ•Š^8Þu¨°¬9XùñýõÓ/ eÎê-]òÆGPÅ–PͶ„†ÇßÇÉÝ<þ¼b'ŠÊ¢T´™»†yàü¨çÕÉÛž)ó§£! w,s¼:02 +ð�Óæt»;pìñ–žØJÝ6`Ù?°æ œß ÁÝ‹� YXI'Þ_•™3É!2\1|aI߈_Ê̬˔:;WµÀ�Âîv×D Åv¹œP:{òÃo4ßZ¹–gëƒ+É·Q®|Èd¹‹P9Õ(X¬.ꈎ²àn4ì—γ‡^Êdæb¹#.(¢NøÉ¡¼U&¯®'{åK<=Ôìñ{B’39`l^4#U1ª€ÆZ;wvö(î‘h5ªÑñÐ<¬XzÅ‘)=ñM=8Þ›RÈ…â¤`ýÅqŽ›a^öJž¸Ì*œ"‹ÞŒ¦É’ඒeöÛÆ+*ž¦Œj5La¾Æ©«à!ÄПÓ05Âr¦Ár=¢`osé¨Æ9òRI“5ϨbV¥Q̈¹ÛV‚HÙœdÜ’;c$PïÛܵ¥¿°zé¼ò}Ä®DWÂ[¼¾Ñ/’×?ÑðÒß `8á‚9(Ò}õ°šÎQÃK¿[Ÿë_÷À:œÅñΓ?/8øZ"qèû¢jìm¯ø¥“KóÛ{¡¥—ƒí48—‡3 +Õ#±ƒJiJµ'/ƒÜ£Iî8/ëcO}dP0I_¹M·w¸TQ×™{MNziî¬èö‰¿éŸéÀVéç3½ï¶}ÉQß›EÑìüììÐسþo)ðüãÔúšarƒä0øþñ¬šØK¢ÿí¾ïÕ§ÅøVŸ+ñÝ>שhV4$k=ÏO,ZÀ¤;…dóyÖ^‚tÒF`G�xÞ&¹ÌáŸ>©åó%;qšþ8Å éògwpÂ-¢d¿F‘ž ËZêYÖ:ú7Åó?õ›ä‰d‘k°%3ä¨ì$R2m”zͤ9°tž·ìåU»ñ¢*Ã27묵>¿ûÅißôK[ý̺Î5Éœé+VG˜¥ûbO¬KT›Q&Ûà-™+/VÿmÏPã°m¸±m SI}–N܉B–Ûˆ.½ +u¾£Á€Sëɇ[˜m°FwFc¦yk&ªH:T›/"ùÒoæ<Uv€¡Í´4l;‚>ˆðz ˆ‹:DFhàm9.n7°V‡a€Q˜é1:6mbîko/¡2îèæJlœ2«ad þ·ÁËë`"úÈbæ_3¶½–NÓÇ—ö&¢¬'Ž5AJƒpR÷‚»}t!ûÒìææqƒ=¾ó„çßy§§0ZhDø¤cœcã0øʱºÊ¦m*ä'MO|Cgmk 3w9ÖÙÉLžEUö{Sˆ´†DõQ"aÀ´Ï”4élèÜþxó†4§ú"9†Á+¨+t{ªy^8 v¹àNÀÀ‡ÇìÊñÞiMC@ÑsÎñ.›òŸ§ô×;í÷n£‰ÓûŠàXS¸»}‚£ñn}&î¿f60ìÿ aº“¾?Ó`7)tgtñÁ§4Ï¥8i·ch¨DZÏ\ã{ZÂúƒy¾?±r¡žœ +ǼÜqÅhRïr4›åßÀM“8>©–!æ÷b}VŸ·™ûgÉüC±9¼§}¬ªlÂqOùËç«€„Xã–å]jŸcG¥FŸÂ˜¤–L/e)™‘MI©3Å‚ö‚Qo_ЄJ±}1¾ºÚåîH!¼QNîQÖ¤j/D1¡|Jè#â8圕/hÌsÎ'¿ð-„O†.n¯¡X ^Ë>êa|U`U4IÌ9öu“Üç\‚Ðq0åôK>¨éÅoú¾;@6ÚŸÌiƒAžß‘° Ð…‘?$sú‹²—»»ÊÁ%<†cS¯^¼Û%’Ìçw”2¿dªÆ"r¿,õ)º½Pî<Pq>¾MtH! ŠJz°óò¹sO"²©´ýº¦×¸jÕMR1À8/žY7> žæ¨'òò"%]~^Kp+Žy’ᮜƒBÖ:âžKY6ó“ã oñæEï:¨ìû?]?ד¼Ê+q÷jT&Š×ˆx�gmµ®¼ÐqT; šº™b4·ö|T7âÍÞ™éºôÊó[)Õ'Ûö‰Ûš!YoºSñ|ÊTëú÷+ÏÆÄv¼xh ÄyœK‚¾ý_6oNTÔ÷ÛÀiOÉ 2x$³xñbãÀȈKŒDt»;ÆŸ5$´ þô]¾\ÈhMSÎ U<¾{îïí ÛÎk‰éÜ££ð³ÚÐ{Ò`–ypÿëéJ&¶ó¯ùs'Â{š\å*ɘ΂¼oÃÀÀ$fÃS.¾óúÔù«x‰°ŒU2 NEûZÕb~Ǿ¯½ÎÑÊNgÀ§ÕóÅõØA·'Wà–Bî|šÓTo"®„«'ÎðÒE‚è#ÍÿœD¸Àø†XÓÚŸj»j +£ý×ÑäûI:~ lP,V%›‚¤;Y7¦{‚ºuàLË#a¿ÂÖ;+`²Fâ�ª¢05& šä¦.œ-HÌy`‹"}„Zvøˆíènâò#ý=ù|M€CÑà\®~æo±K#8¦u„¶ŸLy«ÿÙIC8™ütF5{‹%s[ ê·Q¯w.X€EÎä.=Úÿòè’"ÛÄØU²Ñ#õôçë]º,Du/uçÜ܉:Š™Ñ8¦ÚËG•SÊcúÔ¨²E�´dªÁ€+ƒéIœ3£–~•dËT-u´ÔÓå‹.m©%ƒ*̪×0Æó´˜aͼ¹˜Ñ›zjTWyX>ŸOë²7à•£)Ùôy9>AvŸ«È'‹éŠæ[å‚/þØí®”“›t@݃;w¾DN°Ã‘AŸ¯ÅaÚ³á„1ݺüí /=Ž_fêK¬°h‡mN9²¤!TL?—Íî¿Ôç/⨜ïíÖ”~Á’åû>y;ÈÞoGAÍ¿�æ÷a0¼]t "MlO¬¹H 2àÄAlGà jŠ(9EÎVY8¿µ[rýõ‚Â’Û¨eB‘#u¬ÕWh˜3¸)žíÓ†JCH4–gGÔ@^{×Wý9f›&;ÿ¶ÙíûPЧÆÏœóUËš'8¥¸³®îå}1Åój8“ȇÔüñaÝe6údo,ž0E7:.FŠ‹¡Ïìê/~eJÍ[=ªú18‘xÇ>Z1¬ÇT†¤w_›å´±ÄH €²Ÿ~ö‹8Ä8Šfrßü5þ]#NM6—‚rª¥–¾ÐRïc¸ŽA˜òÜð°sF©dn/“fBMM< h_€þu#ß](áÇô‡»Pd¬ZËT^aÖüñAâ›gZœnã‹Ä{WÑŽÎÈ´Âã>otlÚDÈÌ…×�;Ÿ±˜y üyQ Û‰Ü_Èw^F ä{bZ;�±çò¶+^ˆbøú²Ò!>—:!öè9öXGÇ>h•_‘у¦-Z†¬ °è˱ü¦/ž+;د¶ï® ¯ÇêŒÁÊé5 ¹øÎÀû~•Užœt3ƽ·â…ð?²ÿT)¤Ý?×á9ÔP´ç[V‰ùþyK!Ô·Öz¶{𗃿µb«€ãåÕŒþ ,r1Ë2[AJ58¦Êe;P‡»¸Ì3^^€å¡íÁÕ€ë2êã:†µÐ%*0ÈÍÙþ�f.ž?œUÕ—äëå̃l‰U!e?šá3Ä—5»Ñè¨^²‡ÀqÅ& +…¸¤«^Rc1Ϩ HÀWÂLé[Rõ—\R§åmï�ŽëÐÌËç®b¢^ðúeñrn©ÂõAÑ'lߛȽ¶õø‘î10’›<È$ð‚º§Pî:$²Â/EÕ„Ó»‡‡;»ó¤¦†~ý¶‹GP½EáVÑPfÄX)®:_èÏnÃÀž†ßŸ£x2”‘ŸŸ6;¤«¿3QLë3¨©©n+Þ©ìðzCxøÂò'ŽMÒyÒŽ$Ý1uèÄ>>Øï☀da“ÐwÛ–Ó´‡¯ËGPúÁ)‘ðaÍ2>‡WöC(Û~]€ÛT(š9Ñ/?ùó==Ïß0Z"¾aß¾áÐŒ[”>Ÿõò?~ö¶ iÆà梔=Âè@¤8^ÐÁÜŠöMœÅW•×§À-2f‹3€š®ù* +wBS¶lÒƒ£‹¡–r;¨,öª†=xÕÅã<÷³Ë—¯Å±©îót÷p´`Z‰¥y5F‰@_Ê1¥%Œ,Àÿ_±`5ZÊ‘TïhÍÒJY齦Å71Ûµd8ý&9Lœô{ºlûáÅC™ß#å{ƒÌ²[KÄŒaJ?E5/dm½—¬_ƒwûmÇîxþ?íÆ—NÙñíؽþYã’5k¬ÇŒ¸ p{ˆgŸ5½Ä~ÍÔõËu%y$/Ëî{¼w <ûA:LÃ,bAÝÅH‚-E"é{õ^ïŠ7ת #fZÈ¥3y +Ùm·Ü¶5ó˘V.±D³²!d?ñÎÙ^—À8Àl8õÉ碦ŸG3~ë]4l|ʲr=lYÀ½€»#žMgy¾1Ô£–‚gMKƆóŽ?uîE¹¢Úk;J�ó>í×\‚™ÿ�9û%&oG-ÕV,Öè±-®Ù(åž.±î¾ ÏÝrqÉ3ç†æ›`õ"Y¨KB–ŽcþRi€‰K¯¦› 5Ö3‰Ä×wXÍ«ÛÒ£]£Bs ‰‡bž0?U˜jj¼yqÑíw9¡cæ‰cÁéù°`KHí‰U:Ñ~§ò5….64æzV õA ®Í8€ö€Ä;ˆšiÍy);€`7úåZx /U×ô}tÈü³†ìü¨Swrµ€ZÕ +^xÕyÐGòZÛaÜ Ìdn^ýgtô*úÌýžÑo /hVbŽeé2Ši@iv +¢¯žw)õÚN*¨Ù5È*-’dq*g¡>8‚sq¶iä¡V±'1˜þS–ã·Ì¯vÇñ°”Ÿ—µt„E|zSÜì%%æÃóëŽ},ߟ䂵AÑJA#(˜�6‘µ5e§5#ŠþÄþt°y¹+þä–,;)†Søg6Wô£8ïsÀ+¯«{ŒM ’hG(Œ"Aðü)‘¥í�M8Îjàìòû£šJPnOÌp![t±´›‡6o—û +·*î�æ°%—€ð¤‹Vâü„"hš"u·ƒ·+|ôÏÕÑþ¢?RÛ‰êÇùƒððè@ôF2)é×ßÔ +½–O6F|>VôFý·á|r‡û4ƒ çúkYhâütúuYå§b¸Lë‡÷¦H´Ý>¦.IÒÏã 7®rˆ”/M|¯Eøpý¨ö›)AIÃ3½dSÛ5ÂôtkE«« X2)iq†tÆi7¾ôº¦£Iƒåô¢#4@«� ÷«,ŽÄòã—yfª—un2kBeojb_Ð2è©šk™ú¨€ÚÅæ5FÇëÔ˜$ß³ÛÛ#ŒŽÈððÆ,ÁçG%.¿..y~±n[ <ÈßF¨w;&q1PÃqR£ÂèóQnüp6‰„Ûï<•XÜÄÓ“‹¯œRgèÆ5¬ÊÄ’‡Hï÷ˆÂ%²^‰õ1Âða쀢Wm>ª�šL·¹MÖøý6oƒL•[©ûº“gã�ÉŒzÂâÏx® +$1j’ÍüP×#ùíbQ_Ò•=ê évNg<+ßXëÍ„äyʈå%§¶\Ú0‡³¿—óåÌŒÏͺBE[Gá.’À‚†òà0¨0ˆiº0Ð8#cÆúgnPˆ“í†Àô²šµá٤̪`òP|bî0óG„‡æÆ¡óà†‡{ÔUx¾ƒ rG[„ÆýÓι鋪¤:\>TLòb_SÈyΚ¬OpoÂé:%GǦ•+kÑñòùwò#íH˜þð™íû§´žM�A¤Ü¾2ÞÙ©lb3HÇûžÆ5%Gðã�ž†7Š +¦ÃZT{²`Öá&Ç +TþlÿÈÈ2ÕU#4´R9�*ôÔÙµ3Ì+éoLK¬ƒ&!±MÄÃŽv‚)Íja%j~°Å_FŸTö£kÃ!DÁíéÎÆëÝŽ½ÙZ¿ ÄsqLj¸ß nø‰á}û6yóº-õÖˆemæåEà¢ñFmÆ9ÕZ±ßÄo¢lùy܈Fžoæd}¤F¶Œâ,á*À 9F:ºn;ŽñV÷¨f-DIûßçª|É0 „ ¥šâtã™åMMR>–åìY5l!«5 ¸\NÁ–=ª @â¨0º¶hùÿ«@ü¢ÌåJ¦rN_ßä‡Qˆ?åGP„CfFèeãùcÞÊÍ�ÛaßšÙUy-ý.@Ä<üŠO›š‘�½íIIÝ3«N»ñ¥jµ;†óSsVIzq ×ýjSÓh{,÷$kXw!@¥Ëo6tæP"wüØÊš™§<<á`]<ž©xùœ¢nWqN!e—‚$C<·ë$i +äϪah€?±»å`ðÄt˜ç07f¨øzL’ø§ŸmðS<-c~�e§\1Kœ‚ÇÙ‹çþ.¼À‡øÌL«i¤øäÅËÉ««¼ÀyP*µß#ˆ*¼Žãž§=ëʨNÅÿv¯ÃÃb¦ª ˜Æü„nz3i–i6m_’ãò)=n_ÔÞflßÙ•S‚Tç‚l¨ó®š/Z.™ BΑս#!ËGª†Då*¾gVÚµeÄoZ%fÚé_çF U>¶ +ï ˆ4âFù,ÎÉô£ÃiÆñáÌNAà ò !Íýþ×ò=é]àÆÂ÷_Ƕ>ÄS¶—<•ŒÅò¾…q*/²!OE _‚s"\óÁ¬•ùõFÛ~¢6 IÒÝ$PS|… µØ{Ž…àŠ‚!î7z:=Îz†c/Ôðb´ü`hMÝz�?¼xÇe—©ÈØïCa]Ìët~.o°OEÇáõN-ïG ˜{îY‚¡½_TAáãeù$Î$’``r¨\b˜;Ï/ñ+<ç)îØ1ô`Š•SUwpOÓpß+!$«~n¡F*Zfa{p'¢òcñëʾºj·r£d·ŸøÁ§LÔç{ùJ\mXþ*Î?EDûurà€Io+”4æ*œPDÞÓ*׉f3"Í>�û_&£©þŒåz¸¶*\Påë»öé^0ÆùÐ,€v<I†Û‚Ѳçk¢‚3BUDk7wt°×„ê›@Ypït<^ɹ}<?¯”žs8¥Õëù¥f‹"B׸x×!Ë䋽Ú2:¾chÈ!)H‚ZúË[žËTbÅ•ëûk[K`Ë2£ ©¨Ñƒ‡Ìôs-ðå¨p¬AÌ7i˜rSQ(Ð|oÄ3;Û£ÑÄ&‚sHòÛ4TvF/§ÕøÒ [ݼ\5µO²ŒP„Úo—Æb¿œR*=ÜéMÈ6¿Ä!Š_e6¼:sæèÊ·éÝÐDhk=Kƒ4À7û¹N"ìšS7ðà[;‡rÇ<nV´Yà ÀpzÙ¨Çq,$¦l‘1®ÛžËÄ3kAwK|n9¶†w¯øÆPñ9Å4ÚˆÀ[ÒÖ†òm–Ra/ñ¸SËÖ™ +³=ä7Ä£}ƒÅÈœ6¸â®Ý¿ÿqŽ\0—Œæº•èhKT`5EärùªÀ6¦RùŒ»©”7ý#‰œ·©¶Úω¶ô0¼äÃNV‡|»2l.€Øp�†Õƒø+òr,«ÛÚ¶ '¸É4Õ&”ÞÎÂ?[#V<Á½K9†*±óÂ.ÁCˆ÷•ù=¦±‰¬CÑXeÌÌH8@˜¥Ú,1|‹Yö&¿º'ßa ìC´JJšæ[˜å:eÛ@î ¾ý|ÈFøÎÐÎ,îݲåOdéRcq0>|À�Ocð°oÿü—/Gkc.-ÇÞ+=ˆûƽµÉ×Bþ´Ý…ûö:ð¶”äÞKà%yÔ.ŠƒÑUóÏ-júíðë ÈqM]£»v.Š*më×3ÚòåÝV2y"|‡ +H~«V+nÀ¹é,dÜûµ²¢9©èNhÓÇGIå´)FéL ªÌ'õùµSæŒLáeñ¯ü|Pïéñc±y‚Ž”Ïk[=M¿}sˆ øœW ç›Ùá6õ*ÉU[[Z,<º5uÈáµ\o½Xî©+-*mx“ë§A6žÓÒRö¶vÒŸûÈí‹tîÍ‹¢m¯&b9¾P%éÈQ”ŸÉFŸ/»/‘ßhÛü9×D£S[@àÙÔKëw¦êл=áàfªpÓæçf4õ¸G/�‚§<$ktˆõíÅ~Çð®å’_ðÁAzß%Wî„aê#”Çeþ.&X:‰MaÔ/°˜--„-î3t2]€Èë 7Ü ðüù¸EÊÅg ÉMŸÇD;BPí<²í‚E‹ÊÜ™|õ§Õø"Ñּ<ÊبQ˜9tqË1ËPºT*±¥¦Â‡ƒCk£´&-ôExñ(J¢Ür‡h¹nYÛ¦ÊÝðõ"]±(êIvæµÙôÍûÁøR)›™ÑÀhqÒ¢íc%iê‹Ý…Ô°©–ffºKèx ⶡ~ÿø–ÁÞö+N.®4åŽsZ²ë6‘ÃõÒ–®³†|æêÚÒdÎT4öí÷¸ênýê”-ÉœpåôRÉÒ}¶Ãå½VmÐbŽmìéyÅ㌬ABäjÁéÚ–MÙ³d4 +¢èž¡le¨ñàò¬iöbtI òÅS…õ¡‚4„ÃvIúäàÁëf5ãà~½¢¨LÖㆇŠÄ¥eƒÛ‰µFµ;o"ç^¸úhÄ“…œèû±¥P´‡ÅÐÚÕ"džXA¢%Äãr™/è¦+‰@lí;¨¸\'¼{œ¬rœcw®õ¤ì2„`!=RÚÙ>f@*¿s"{®íC¸çb/?n.‹öOÁ‰Ø ó+ñZmg%ÙTÙäË@´Ú3\•I^Ì'ï½bQkñ +º6ÆR@uÚE¼æõ`¸j‡Ùœ.ˆò’;ɱJÅá¦#Ca¿×µ +Ä+K5Ó¨pÆUãQ-3ÞfcSŸ@às¨CÝŽÙPge�zb�ˆÁÌí°µóm.© ël8¦;Q6ÿK „çâyþ2‚&Š³Úzƒe›Xä-©é7ëÖ‘#k×Ö¥Œsfømû4EÏà¹ÔáaÓ¶<yÚ·û“uUÝj©Â;Ÿ&°~¤ ì2#]ãeák³B?Bc= 2A4]Êø\rÀ«sÆP@–áœt—Tßãài½jõ)£DÿŠfj¿6Y~&¼÷Æ믿þ_qì· «Ñöœé›+µ©œDÉ°DQ¡˜éÂŒ…Ø„ÏÈMiÃ÷ïü –?ŒYgØ@ W�W�íc<²ÝÆ3ý21;M‚×új×àa +÷0«sÕ±ÿ£×'Ñ2³µ% ÷ÌXôöp®£IÛ3¢¤?‰é[=øWˆOX¦ñóºšà±¦Ñèô”›:'òÈõ`ýò˲ MÈsþÖ�ÊÖ`êî…×tc;—Û‚zdŠƒíQ-Ç7Åò#ª^RP^ûqwœ»`õ|ÊË–-ý³Î#§dUŒh€'{4ñ’©œìf{˜Výþ–-{¿„ +¾Ý¾ÖQ037Â<µI0.6Â"fÄŒeùá™f kýÖáõn%(0Aœu.^\0zq#ð^Ž‚_â 0N¤Y®UðƒìÓmîïá½'DÃp›5"YŸ†6´ŒÄKŽal›eyî¬I6,¬q—!qÛb™>„vË׌2á:Ô1Ä€ùj¯õ²&³#’IF·ïÌÏ{ñb!Å<˜â[ÍIxìb ¬xF9Ñ’5Í»» óiÙÃK'⛕>¢ë˜ž›Lr3’u¯&seºÍqÃL‡¡™Žé…çºØ2¸ ák´}û8ÄKé&åe²á¥_¢çbÒªh´’3ߘ§ŽËZ‚Ù#¨eØË Gf"k–Ñ aÍ2gˆÓ=:öKö¤ý®#8„.¥Ìy¸,ðÇ3–·©–k’<ѸJ¦PÃZl‰`̳’+ˆÂ–j ö~!öóÞ)¾}æ¨O§2)Že/Ôu’‚hIfQQa»ƒK:»ßë½ó¸Q+è÷°ù'õä|ܱ1îò%þ~x8bê̹*ù‘u„t¬ó¼Û"Þ®C¹ïÛiÊÎ…,òÓû6l˜sáªÇˆÀµÈrðlÛPfþ¦©s—€Ïcs#x§“:„-v”8£Ïu>€g‹G£H&È'U®ó¯iƒŸ¬#¬®ÏîDYQhþ|0K16’¦Ãòðqå_:‚ÓÕqVCKÜ!ËP^¹.š„;%t¦^ûi3¾Aw¸ÐJ™€40áùgÀ•Ü¢Ð¯¦£8úÀ„_à8² +ÞêÛuÈ ‡Ú„ÔÞ z9¹ +3{9Ö„Âmm࢕¯+µ¹©áÈ·ºí œd8a!’QPÁÁ‹ÃqQÄËVᆠȂŒr¦Ù*³aÍf—³àñMrCØ?ŠCÀ6¹ÏæDn¶Cw†‰høYƒ Y2e»¾Ç±öúéwa³RWº“©ñÔDAÊS\+buAhÄ @ +à ýÆÔŧ…Á£%~êÌAÀ�ªàJðžþI®õn×›K›³y¿.”;lVÉå:bÓj›áòÃéã'L‡¨‚§ŒgÌzÕ@Œi”^ò43å†â?‘†0´0&x<í¨¡–pÿ¼åŸ/hô¥1 Þ/ß\}Ë«_ÖR“ÙÏQOšºNÄÄ#’¶Ò¹¸“ød»V·èHvаŠ›v·¸–ÊIÇÖhÍrðA™BxA5ûarŸ¸püÏtþ·%‡Ì9ò,Þ,(×-o«;qÓÉŸY– NñÄ>~–Lp•+ßÓ5Ï1¨,Ã]ÅÅ0‘ú{ÓÒ+&ÚÐø/@•8€)ó¬nêûƒ9ˆûÒ¥]HZÙ0=]Ø…ú¤4S¾¬uÁU³<ÿ®;Š�<Aw‡j˜âHÂÊ´†‹+ðÐ>´þCÎåWeÁÄÆ¡Ã~!ézÊ`™)hÅÁš™þ&0Ùý-ÃÍ ‚iÎåpº™BÉþ[zhÃÿáîM�ì*Ë»ñ³ŸsÏÝ·¹sgËd2I&“dH²‘„‚ˆ€TD±*"ˆ¢ÛÚÚÚj¿Z´€X¡*nÔjdQ–°2!@’ÉžÉ6™Ì¾Ü}¿÷ìßï½3w˜�mýþXIÿ/Lîv–÷œó¾Ïû,¿ç÷\{ÍÇ<PçëÅ¢muQoµÓÆí££½4¼äZÐ ³ÅKñì2[;M*y´Â¯¿Â«ÙXá˜ÃT¥q‘êlHç©’C´81%”Òu�™“g•ÔR”áÄ#PF^‘)= 1Z3åø_#€pýïh¬àîbé¦uðGwÝUúÒ?ÿ¤òÄù VYѨX²ÄÏ�o]ûZ;˜›…ÕHn™¸¥ï8äyùÅMø²Œ4¼»G±2£H#s;æ²P±aÆ(ý„¥m}Uš ÀýX:>z·@¹;6Ì ,y§3n•V#ð‘F`§Å¡!šª«£$N†ïÁp�‹‚üzq“ýœï‚–“Ð-ýYÀ…òÈñ¯F€¦ÎÔj‰çìðÒ_€a¾œ3LÎúÓŽiþJ *Ö@›]NÞ*Li”Bö,ÀÀN@göæB9}fjîõÁô¿€·Ð,㫆û�nmjʼn?]^e¥ÂíúþhŽ�ÂsP»ÆÕzt ÎÓK||ûKÊ·nÕo¨ årHøžh`Æ–/ †W\öM 6.Ë…§{êZ‚¹}…eÓø1Àb3®(¬Ë�Œ‹T·ñ$\iVePV¾˜|U¡ŽC«Ä:` +:C-E¢‹ƒ²q1*[|Ø`|`±Ãº¡þXOSû6bÙ™~’Õ°Å,)%FSwÃŒ~ÇâùÌÒµI¢à£å/Y?Ž�^ÂIaOý¿ |ÀÙÐo4ׄ—ZíIHTóD=¾J?Š¢ù²õ5¸}@b™2˜ªÂ„ßÖ4¬• ׮͛l'›/ýS- f2XÕ,¸z1Óh¦˜Ã/bY»È´SãÝ›)ëÁ›{Äöº¶QâèÃвۥ†¨¼²¶¹{¢’[iÞU׉*c6Aˆãø‡{óÑyU³ÿ–§Ü‡‹TñÏ]\ömÙÁ?'誢¥?�+øëW_qí>YÑ.ÌRÆm¸ïKq…6“W3E]~òlrŽ_vÿ@Њj(ÄôîÞ¢¶¶N$`ñƒ›MÝ‚Ë ãg3 f7'?NŽG-M@匔ìt¦àòp©Â“Œ®Ö3¼(<ÜìĆ˜hH!—~0Ku4ÝÏNÎùJW—IXeÛóåõ"/•A¶)’&å3>(ñ”QÀÐEAãtªˆRer¹«Ä%(iòwvA™&y;Q0Ö_xð‰$Ï—ëz{?þhÂ7Ïaj¼ùb^fyi%´“« qÌXé&˜»ðcñ™´ûÔºV÷92©PyÔ"f°½ÖÓ£Œe›œq3¿–1Fäõ£½;w«eߟϬ£Ô¯®Po·¹J߸N„²¡–b–ÍöýhZÛÆä299¤|ÑmŠLLÉ9í¨w ´Ÿ¤f–†ˆ6SàÌÚªSF7O mKß ¥Ê…Õéû3ZjÛå ÿ¨ùR½û,”2zÆhú5Ž·®†¡Žd/+¡äÔ峕‚e½<o{…&OS²1N&¹éºª&QN‡f’î—|•‰E\ð_î϶SVQØ–aê¡-Ä`•IÖ‹˜ÀRy¬O–®SA]äíØ]~k²áý;„à ~XO–£•ó%Ið4@6U¨Ä¨æa<A1êÏWµ„rÓÆpùXqÑßÌtv?Kž¥Âî)³½r2Ää&¾Û¸QÝ;žÛ¡*Öò:TÉmýÏûˆˆõ›%ĸR°]‡Í/_�*ˆ7§¥•Ò³WtÀåaÄu°ºI Á÷·ZbùžþÄ51X\ŸÊΩ:±ž® ƒ€p1Ã'ÞÃÉI®Yt„ôuëXµK)îRMv±£ÒPZ (/†6¼‹àF+×EÊR}zÙå3 +:Ó¨hæñ‘x:~ÛÊöüƆÝBøî:¬ÁˆýßõP`xÃêlÔ=¨áMþ5ðxô!Xt +•O>�&¹Ðf^‚Åîb· ŸRö癋çg÷‰w\¸6ö\4æj¤äc|’!jUÆÏ&²P#�Úìó‘®ÑN*Ý1±±;—x‘)É|0dðsÜN2^+Û¢ª…Óæ°fÌOìÎdŽ]ŒìBýÀ§¿8·µzFi7¨ZWÀ²#=_ZùÚ¡½vrÐë z=ZÁt”dkÑoÊg*ý´·-+†23‰œ,"Ô»;ΰ>Cý�š°Žá€"²æÉÊöçÛëMøv]P7Z ”Þ=DæÙÒ™*^‚eõ“@"¬BÏkãÆ;ƒ�¡W6—¬b10"\B²ªòK»ÃÛJ±Z=ω Ïï»Â‰Ü8ˆ¤ìæ°'·±›Õ–Ù1YFïÈ9Gs}{ƒh"Kn38lZ»©þ&/µ[qæ·Àº ²Hå-?ÜÉ'a†Ý‡<u$9RÜÃÑ:™H^èV +@d}‰ÚÚsòÄõ!ãû!µ A¿·(/YÐÙ&\Gí¬ùõØW\ÛîËV¾w¤³¥`ˆ…à¤(ËØüTw7‹Œ½òµ]tŸ=›t¿à„‹Žö3 F6<ÓATÎo·«™´*{“X9LpœzÅ’ê©ðð)R9A9ͨ¨§ôq¾\â³Ðâá·è†Ê>œò(0lg!»2c§×¶7³¾Òå©WS0–#Ÿ�EÔ«3ZZ¼£(¸-\ÞOÈÓ9ú÷÷ݯøÉoÐî ³`ÈŒl† �d{+½óEpi„=’ЩF„k76¤ñUOÕó1,Úõº¡Ý3kUýw¶–ú93O¦=¬h» ÷m6z—7èÏK±ðC•¾D³ý$X”ÍáúètÓ|žŸø+=Ì^V²î;ؽ7±zÆz@ð{HVø9òn<È`§gÁ]¶Ê%6¿‰K(_\ýÔàh.©ëÆô8öLõüK®‘ìž8>ÚX5¬¡WxÍŒ¡:Õü±Û3㼦ÐJsøŒv:z\‚ï ÖßÒ´þ”HKž˜™‰ê´óbSÖï@dÒ¦j£½<§î}:Éüžc©ŒÓŸœ<鬢]¢^eÓ¯»ÿþ4³c²¤/“¥˜Þ!@Ñf駾´þ&ð[뙤z7´ÉWÝãã²åæÃÑä”G³¯þT‰÷õÿìgêôgHŽû~·újWñtQõ¸mbêmƒ“že¤‡-*LÖh˜ªÒ,‰Â?˜÷’äB¸…Ìÿé–NþE(’û,6ûÿ·ð%fé:ðГÜEë¤ÁÝC±Ç)ü¡!®L¤s7¶zÏÑzÉ dc©,Èv³À'YÝh·Xøë˜Î 8=³pkGã”òB÷wµËf;æ*&6RuSÇêè “%<µ9nYPNÔvÃvŽÂîTîÇ(×.sêOkðý•Í®Nu<êà|cR-(ï+‚%B†ÖA´Êw4›e }^i_žöŠEø5åí*áX´[èìÖí;¶ü @5hÕ7Dz87¡TãÚx&ˆŒ½òD&;Bè Ç“™•BA÷uE’¿ïíÚ–s/[×,J¶ç*ßèH|;jþØ5+ŽsB ¤dà!Có æùªw,»toEÁn7úvö¥ââ/¶ñ ³I˜±}TÔ‹_ÒrZ<.Øt§èQ/·©¼”/bÚ?Dó\°äR5”hÆØ~�‡ z¹# ÎTòé9, +m¼ãŽðÕ[÷•³ÆÔB*di4jÄm$r¬lö&)·ÝÃ_iÙØ£%%5��@�IDATêUrõ!ø:__×Þžß=¿ÞdX[È c/3+äÓÕ= Ö8“ßm;âˆ+<ƒ‚Ót´¶æI7‰åä[¾6Ë ¶½ˆ«Cg“ñF;“£ú&Ò+”0)nÁÑK$Æ*,\O<uåm…çNSÛ9'µ³PìClÐãµ<›yñQ…Zу¶}ûv6«²7€ðýƒ@<¤¡±Ö1)³eËK[ö¡^¢Y›Œë*if¢ÑwhTÛí·Ì<ÛŠõq¤Ž‘b«�Úyª—m…•Á¸XúBɳß�£CPóûkÔÔ«qÐì7DA>SÒóëN¼+ϦS¿é¶=Ø ’ù^.ÔäåýíðHùÁúQxñÏî>øì‘3W/lH–;ù_üCÆÝ+£©ŸO°9|Ð|> kmŠeD¿~J|Maõº…67ÝÝ›†p))ŒCÈüó£}õ³/f¾ö/ëÚKÀ!2þp|ÐLg—R·Jr0Yæ XœC(4z6Jwâs˜õ ÝçÔĸ;?®èÜ^Lilç~ýÿö©Ÿ÷/ÖXÛúmñٛ·l©O,"ˆò9£“–Øks‘è»Em¶)9ñy }Z™K›âz†Ò?ê¼_ƒÞîhæ1›Û¹Qf¹/Ïà+`ö•ý»Žx_„gW:ííCüŒÜì·Ÿ§þ-EÏŽ[Eë ô‰à<)‚rx•‘ SÕf,¢?Ø»ùôÄ-òLQÐtR°lJ8’}ˆ&AŽ•Î$HÙÛ¾‚’Lùœ"!¶|«‘>Ìõ×ÖV…‹®»ýÏd?ò+:FüÁà¿„1j˜µn±nêøXÀHé†Çð! mÝà_´‡0.½‹”‡¼ôk{5Ž§c&!YâJ )ŽŽr«AžÃ°’¬™j}Ø.ÌEúßRU3—ü´¯¯|0ÿ£ðw–µDH@Ç^³mŸ&Ò“"еw¼¤Ïü¬Uð©.Ôì+³Ç‰L˜§@H_Å m,3Ñl:]¾>rÈ6r´§¦;…¦Ó‹}ãÉ–—úb-$À•ÐÒK$IøªTlÆ17ëõ-e;z(ì?]dÍMH¨ù¨pŽ Ÿn–c¯€«h=\µðßFtÿ~•Rþþ¹ßTz05¿Ã†Îß„EÓò8Axl�ß ç·”r©”îˆeé[DÑú2XÏ‘íF®ç¶H2ź•¿¶±1uUØ3°«¡¡oºöG’zLÆxJ¡–Ý#¨è‘²‰tsSÓÆòõ&4u$¡h‘Œ¢ÍsRbãÇca¾À_cçØzÄ=¶2‚7zï±nÐHQ‹q=ER�q£T¢5ƒ|x5ÇYí6†ÝšµR/ffȇ2÷ ÌÖðò’>Î;ªÒ"ý-àÞï€i‡í j¾§ÊÒ ¼’üu¢pÀïzÇ>×›\øâH±‹Ù9NQbÀ”ƒMŸZÙ:]97;>Â2¥—f*)Ô‚#”â=ä.yDÔÆ{ç£rÓÆrüR^Ó.»®4P‡“äèhŸ1ó+.‘Ú„ûÜ“I)÷¡€Õ?Èób#2Kù`çá?·ƒÈQ,ƒ[hÙ«Ë—¯}üÙ#é£W/t—Mö7æÆá'l´¿ë—’LÁéQä"E§ôõ–õ*k5{ÐIs½ðë•5Ó&½š×— áÔi†Ñ`³Ûã~*VÀqlv5oŒdR/¤F^Š¦–=ŸŽG +}ª4[wÍíŽ/²¦r-0P¢Á1§ÂŽ÷Êšîhç¦s{dz£<âaÄ‹ f½:˜®;àDÅô\õ„mÜÕŸþ²3M»·ÞÒtP È4+�Ê,úB2Á3ûú¢¡<È}l´Ì›¥Ò†M›ˆ–_¯E|¡à"Ê€§Å4&Šï)jfc£²/‘µd3/ðÍK{zÆEIŠëœ[a29?&ü°õÈG²QDýÁ „•ÆäÃJ¶¦M›J+J•ì>‘/fªbØQ&Ç6—n,<‰r!ç�ß‚Ñfvhì'.þû†zY‹á¨6ñKd´=ättÜ +j‚VV®þì³9ÎvëÕˆLs0ç ž·n0(nžßÁ¤/kiùªÈÓ<´h€NÊç.1òŽõ-ÛŸïiZ»ö;Z‰zJèUgƒø*77€ÞŸ¤d÷ÀJ?•%f¥! ¾$f&ÀÓÎi,†‚‹§ ?sø5�zAî!ü‹u0<ópÕø¤o˜îOÇG‹Œ‹ÊqÉ+Ñn/¾¦fôïEŽ{sŠv¶xQ;U&.Y‰^¯ØðS\æ5ª!Þf³ß1}%‚{‡žýù +X…noÞ²f"†›Q51+…)3¡Yô2„‹%É&½¼ïø@~SS“ñ»B׊îRŒàvÒŽ’Å~ÇýYUJÏÙEf¶®‰"ÍúƒÕÇ“v)¸@3‹7�›e“%ÅDÚ7ëI,º{näü¥5¦1j\ÿÎ#V$»z¥bM +±Qà“Söš™ã¾á3Ê …ý<j¤{Æ2'¡~ÒÜ?ÓèÞ½ÌÙ“CTUÝ}m!G÷(p&jÀ#_ ˜ÐCc®åYö4„/ +sãÉž‡í=_¢Þ¼>’Q9Õ‹*0ÍO‰cç®áìkºP9ÖÙY„`{WÁKîïµ/(™Ú•8Laé¯0¢7"–ÒCL<™dÝSÑBŽNc²É9Bö7Lv×-øU&jV„£ùþ£õÙÃU§æY +JÜð«´Ñ†à’ÇPÞf47cŠs8AãÑ m6FtbúJJdjQZÝÁÚl^3oúh‘%Zš“á€`y´Äˆh7䃻â{½äá_%RfUÁ(õiŠmhºI M„¸èïkm(oIÞ“†NYûJ…(%Ø þ=R¢³ÄV5 >YvF&1ÐÊ#ÐÐ!Àáá¢'JÇN§‹\Ûv.}¤ØÐÞÎôï9á0gÖóC[ÊJ8+ãñ’ +Å'¢i¿–U‚›´ëû– *wâmÿgÛ5žëÀbÁÂÃÝYA‰x–W€át3ÍõŸEÒÀ„Ÿ]g¥¼Z¨Añ<¸=pçp^˜ì‰u²Å‚¹‡ÌGhž¯Ã +÷rL-“þÙt' FÆuL£¬ÏZ®*ŠÝúf5£J—Dn0—ÏôëIRh®ßGäSXô§ !½®hŠ‡3d‚ßPfWê¯ùPu~±†zÛ'`f„k¸vÁâÙ‡Ó¥db¸7 +7Æ[n“i÷á¶rÙ¨pY }T÷¸ˆÊcè« Úåô"ÛN¨¦¹e©æ€Ái0àO„â®#@3šXȱ!4bœJIY~ÓÒŽSÀÄs9k1Žá<RQhi’ìÞ):ätMF…“£Él€ºz9´Ý£ˆ(?¾¡9EŸ“¬¨?«£Ÿó…«iÕÜ\Òôƒ¥~jll^7ꬹ´Î²a3EÂX7n¿¼mÿLA(_¹¼°àrtoÄ5`:Ó:-È8íòß÷·%Sq@‹GÐès,̹sç’gO +e†a‰Öߣ$Ö§€' oEQ•ïVžÕû~!ïÒ÷,|ÉÅíÉsÿA¦ycM×Àáw¥òû?´ð’0NH™Ì?>qÂÑz¢ŽÉ•È0TL¯ÂUœvæcÅbö`b‹@eõ¢/ðV"íÈ‘3Fã¼Â?Ñ°žafóéNnå¬�?b¤!ø^¨SdÓ¥"}ÐD²N˜¶©œì%÷ÚѤù,] 9íAkzý_ÄDA0’¦W «iBT¨Ø‰Ô ï1Y6÷ÃOZºÑŠg‚D6Øbâ{Å噇bi`-§0¾Óx}a5•ê(ËVr @F´VHÁp2§É“®bòÔ G£h°TMbAâKÆ\u½ÎÒXjÕ¬Ç6ˆéB’˜Ìi¦ž*Z4DÁÆòqš—¶¨ˆûЛ6•‹HR‰~†òy0~‘Çå�ÞxùšÏ¥’;Ǹüêêrp¯,LÊ;Oûç $½PFv"gqSÏè å…#Íè3 BÛ³´•¼í}Yæàt%D›†T‹*z)é1µ\¢ó|^Q¾ö륷oÚbê¡"bO•n 9§BåÔ¤Tñã)’¼ãÈú†CˆÐQÜŒ~Çw»½ÒLã<³žkÖ(Ís/ü?<§Œ¥cIÂYQ´³F–dÁp,Ò}¸Y7ÃXø,ã ÆÌŒq*e£¯ªºáùç¢Ñ„¤óH+<°3ñnü¸P8+Þº\ÒßË[jã[††žÎ©™mLRAOiGÏþ\%à¹yädêžæE[ ù~Ä”ø/¢8ëQ¾¦æ/¸<)LjÁç]36Ö÷£ïõs×Ëèr± ½`ˆB#Eœ¬î1fVãÙRÔ5sÙ'Oÿ}º¤~Êõ)hýË0æÿi0_ü9xªKº"àgúÒ•×Á’8†]Tà[.À‚A .‡»††Phò5ÞPcRáfW9jÕ=c#T]Û¶-Ÿƒœç|iD^ìN%Ør1¬j“ÔηõÍÂÊm?;'¤)ã3ÜOÄ3%TÝ2m(¸ypžÏ}ømÛŸWß³ð-_ <(+cçôRA¿ÏPñ³pIÞˆÜÍvA¨ufôa;d°õç“ ðšëìë;NpvÜØ.ÔÐKÏIÀ}@-€UH;(ä²Ô©©`|…dÿF%µõ0B×Ü@±½šÒ³ÝóAvÏz‘÷8BøÏ©å-f&Ls pÔ„ZScY†(†ƒ>ÆYö÷’>r”×GÓC/2srë¦M4?šCÖY$ š9øÚg,æ,ŒÁˆ@3`Ø¢“{…‚ƒËKy㋘Šw"‘Ê9[Á÷ +]§i¨".çå…n×”°'çtP¨6GAI Uù„s¡b¦–I8íÞ®d6¿R`íÕ‘Ö*d¦Qgý¶xJ€QàE –EHÇ¢ê†åÅ6ÄŸ>]*XÐjËŸF"!Óã½ÀWDd“«£n•ú\±FÞìéù!Üï:ñ¬B|ì‰9ð·vE +–ÝÄ1íÓk ÎTpß‘¿r³J‰—x±î•H†Ò”WÉ„‹ ¼–‘ xËÛÍ @ƒDÉ EÀ™ü7Íákp#9ñ*ˆ‘´ÂQÐd6ãæ´bžÙ™üÿÛÏ:\ns©1À]Eu]óDŲ¨nøüçËh²¥¦ +ãDÛ'“úÍñdwq@£ÍSª*R>“ÚXPÕœ#èÈÞ½|ÝìwvVÄ5A|¿“Zy¹·øÁoäþ–ïñFÄ6ð~ +mÐÑØQÞŽü³åëgÆÿµ( +=vQÔü©C/½V¸üâ+ž%II°*>š±$/Èøt][ƒ +0ÏØ9k/Öú,®ÄRQ ã:úvý¯”„9No¼ðÒu Z‘´õüÙ–Úä'pŸŸE¸UM©êH_³¥¯[Ô8ÿBÄ r¦U:ƒÁØ6škÑuãoQ¤/ø”]²©pú>é‰ó ó»t´:ù«R£Xˆz–7¸ˆ+óœ–LäÖÛl¶6æ•ÁHäuIªX•ýÕP÷Þxˤ6O|ãçÛµ‘‹˜ò¹sEÿÈ@†IU :2i¥þñ¾L¾ð£}Û+³ÛÂË€Õ’3@L.Ï(¾¢ç Þ¥!IÍàvMé§P<ÿE°´±']}ø½<±H)&˜¾‚Û]åBÝœX±øm¢YævY|´ØàÛ®v?=–V~èÑé¯Á³ŠR=Æ/�i3'úÓ=çhYÅå,ZKÆU5q#Îe²RýËCxRDÖ©;íûYUÐñôéí[_ïÙñÜÑEAWÏJY£ÿ,´cÏÐËZrÈd¥ÌÒ /ñgxbNO &ÒYCãªn”�S#ºk�¦¹“{Êõ�ŠÁØÌ8YÀxâîn©ƒh““sPd 1ˆ%KN‹¯Ãg^¶Õ^_M6Àõ1u,ò+ !¡sŽkîüh¼ØœGr`?N/C_‰\¹•œ§ÆN¶}·My5`j6.¶A�M¡Kf +—9a?V¬óvƒIL"ŠìO(?w t) 7yLhuCCÄL,¨w;>©&LþÞí·Êw¹áa¥¨f—D‡ô¢ö8A|dEaoZD3v±žÆ&Z¼ÒáqÝ걋ŸaRô5¨w†l<š¾þSŸr)ñ˜yàò™Ur\RuI`ÊSRšf\hñ%'å +Ž–;͈âYzýÆM›8æø-^ºd]v+ßñ^Àÿ>óµ¯ÕœL§·t%'ö•Îþ'¯+Á<&SÞ' +ýч%ªæy¸áT½ÖsL”ø¿ÁsÙ‡¿‹çm“Dþ×PV¿Ýõ«Gðó÷e‚ŒËuxÞý±ð§#MÏÞtgó6Œ 0[Ü.YÛ&Ç™a³�Cd ÐGªÍuU͸·ì\ø‡2Œ—F€¦¯ãis=<Ë/C®?�²'2JoôÚlPšÏ¯Æxà`6pœáZ¥šôÿm=DB*õÄtx/ó‹ar«àâš×1)x \³Î;c&q3½mß÷ýã{Ö|1p¬WûSÚf†Í#ÈÀfÖ×ÔÔĽۖnêõ¦b—µf2‹&ŸÕÁ(.:Š’´;$"¨Ó·ÒéS³‹¦~L["ÒüEÜ=C•å^²Ùà¿3æj&[ësZ‡!„vAPì2å�q̶x&‘XRÊ0œ<©bu‡VdkÝÝõžz´›Å¥|©Qæ)á‹c€M«Õ4ÂÅîÄSMŒ½h€”…ÕÂxhÄBË€›²'^È¡Ë D'c¨```¢B91êÁ¤]¾—\È€¼™ë}Ëe2¹¥É¶¬¥!Ñ“‚[Bæƨ(ÀïDÀ’†ä‰|²ˆºS”¡mn·×Q “ëx¬Îš¥6hFÆ´£4æ#MèÏEâdÙíÞÞÞ©¦&¢mO4¬Ïßu‚>Òš=€&ñ,‡2ôVJÑŒŒì#‡žÜ“l¿µ£²ÇÔ+,É.û.ƒ/…dÙ×»· « ™l&¨#LB~n·9;´Y‹aûáªEKZø.` »îö»gºßo'NeGòJfCõÌ*—»yÒ:pÌOU*ˆÀ|z߈?fS87Ãký|®äŠ%_²~S\Ш¡ÍAQüõ¼ùžÈÇq_^–B°6á/4ÝX +ÊFh9p~ìIåK¿C®¿žå=êQö!ËÏàÔåÉëÑÙZ*]g»…-'âŠÈS§ÁËb5C>J*ÍÌà[V³O€ØÿW@`<bõkl½aªlZ}Mñ„Ž"ËŠb&?“V~C pä)ãôº$ÙQ+iÕ5¿¥ã™w žÚ7è÷g6/‘|™b&yí¢ÆôåЪy¤ëLÓÕMßW5êVÖf¿\'gDJ¯Ù e¹FP6¢Ô=®kd囚œœåºP+¢h¼Ž ·Þév4À$ë)dÔ”ŸF[¯î´1|ðC ~Tožš¨Cú|¾´F„ºG$¶\V‘’£oï,êc_º‡P›î�CQK¤J¯ +' ¡Ê¶ùŒ5_„Fpn7Ì¡Ê÷çÃkY`¼×Žðl®?GËÍ`™ÿCh;eßþû“'_ÝØ:‚Š.ÚfL¸•®ð<LMàÌ€‡P]F}ã$ƒTâF¬Ú+ ÷Àz¢Ý[ÞiòŸ¯&‘TÛ +ºCÕD$.k!–]ºR4ÌåÃ5ÔÃÐg.kjìÇaXæ.B�ÂçrAæ*'P›çÇTuàÉZQ>êé7{Äps(^€DÒ “ªöPõ±X6n¤¡ÂÍi¹tJvRÔ©Ž·„ãdàQب�ÆbÃçX§³9tËAh ¦4Ä©ñ†˜ªøÄç%\=PÄ „€_¦È›—64ÌïÆ5¥(Ù>"¤4QD|•y:-é`šœlyC_Ã1ˆætÈ 4U`8àkM:_IêMù¾Tö!»slì·U÷;ÕÅQø}픞S l¤ÌHg§yëÁ_ÙüʈŽQn„ßßèSâ›6MPtV6"š¥ds]Hÿ_!²¯ÝÌ£ºÑí?ß_/7×Ý!hìÇJ¢¸ûL ß×Q›¯Þ×�‹r-„Çõ`*.ñíAÇûq±Öo~óFõì&/ DÂ¥yu¿_>q´ÝêV[ª±Qǽ-»À .¤»Qgî$¾õçæ…;d‰@³éX¡`âÄó)æbÐÉ]‡äÿX¯ÀMeKŒãÖÌÂ8Ë‚BãÑ\:z¦Ú]çr0ùùªÎÇ9§}7_È}Š£mqlƒ“*g gFW,Bªë6ó÷¢Æ"‹]HýÌ£Iä8Ù‚`yQ®¸¢Î¿àjä0.À‰Ÿ“¸Æ}]£{v‡Pøj|\¯¤“WÌbJWÌ©ç}Ûm„Š2ú»Ü;¯ÀúQMUÎÎôæ™Ó‘g$–½Ùð•™-;Q2½(“%ªj<æêã°(|Â`ãQ[õ•©3È‚¼ðÊÇ,Ùðܵªžós"ûïùxú¥ÏÍ<EŸÞ×Fá×ƵȌ:k´7§úÉüšÞàæ³>üůŽCÕÿð?¨TeÈÈ)Hç +ùã”c"6‡(<`âû¶M-|Óñ~¾ÿ£_År_èÕÍIsïˆz‡ìÆ;‘ÖÁE0¹k½áPꚇçÇJjI56B!CJàxJ´4a'Ã*6ø[O :Gø~^ÕÜL*”¼ç¤&šR2Td2‡~O9ÕMøEQÌÇ3‚ *B)/ã ˜´g`®ôç)Û 'Ò ºvq©¸pèÐ!“´§Àx.kÎî¢^¬©£¬½;]¤¦Ùá‰äbð#¦eɺQ¬*ö –\&ãžìÌäËy†J@</jQ²|±¾lxáéþA²9ɯ¿îÎ{ZC_¨YAÀu€âÔ‡XΚųÂOnÜ¡Ò¿á¶ÛÐÄ!F9!Ž¼ˆÊY‘5 +gÈËX’ðÕŽApÅ %m¼”Še£J¢ )«+“¸²µººšdËMÜʷɈpOmGžÅäÂTÞÖZ|9ÿŒïñ—´ïÍR Äòžhd·\±~)(.‚tT349=ž(Özª" ³ña³9ë˜&‚Qx+‘„ì r·õ`|»XÖz(`5ø&ÄJŽõlÝú +¤ëdôc&Ð@êç×ûû²À3*/le÷ÙÎÍÏbMkUZWiÛ“pO4bß7ª¿Þå2°˜i¯§¶Â…ôqDQ .¾Ç0´œ¢‚y< +àmRÓÕ%zü&W<DgIÑ)ù¦¿DœAkÄ)-sE¶l²;ë!ÃŽärF"“‰×û‚Ë F¬ÅrêFP÷¨ªšKxYÛIv!ÜÂx‘ÓŽ!øËظ‰s„èZO +¦ð¡2¾Â¨•¬ ±\½Míò«‡:•[;:Ê1—kØ’`µ×NÅ7Ü**áÕ8¬¡AªøåFJÿ½›ú§04Bà`¤a@xQ÷ð'ÏvA¢A„dݘQtµÝdc¢~Ѿ„UjÂíïhCÛké£p94çxö‹¸N²LŒ¼y¿Û§Oˆß¬†ÖšÉZJ• TroëÓäg${2Ü^ØŸ‡!ÐØ!C6ZÙ¶!1TõYÖ¿uUåËóä +Ø{kÄ™råkŒz"«¿QÈeîLü�ó󱘽Ú^W üâÕ({`Ó㌯êàzhˆ˜(2¡v+fzLy„:WÀE0‰ë9" ɾe¡À°MØn$;1#ß ˆTÌ�¨¾ ™Gy¦�k2ÈsÁg{!П„dª}¥…„£ë¯-Žûb"ÐøÁ燢³Ø‡FUl„V6æd2á1“`N”!Ö9>%�Iˆ{°/…I)6Q†ë|Á²ê D®qÛüëf.¿d^'ö%ÛVZó,oUÉоbž:&äs + %G€)î!Ð6Â}Õb<Á#^¶+PúŽ!ÝÕôFhä×[íy#‘û^¤ïè£Á ûw}ñþ'îC³ÝâY$Ž_½ªœñôŸMæñ90¢¹l0cÿP¦ùͱDë±d~Qw²T>—õmm…šZ÷3öqûΊÿlêz.½4ÔgARÜ…ˆýª~Tùå*BÔE”mö<‡ˆª¨ðw[\ÐÇÊ•c’ýU kÕ[0ÕmÈf‰Cs†¬3?~áŒE!œ˜&<*KíËëÆ,$hÔ"¸·•i_+¥Æ=4î}¥äZÍÔ +Q9ý§±ZûQh‚7á(ÛE…[² \;ðZEd÷ù •¯Í’›‘ñwµ(Rë|ÇM/]èÖ®doae M<8¤«‰ûi¦½a-ˆHŒÕb>žñò¶ÙaoÕ\”²º×$2©9,:À‚\‹¥½‚ØHNea@1J×ÏQiq.8/f%ö +e[hù×5°ó¿VlZ8/m¥×.Y¸t!’YâCKÞr>Xfw‡ü³ë öëÝEd¥[‚¥rÀ𚪪HR‹=Ï¿XöQNà¿)åR-õê–йªÀ§45ŒS—9³Iµ”• Üø» ½ Žg”_Â| ¨¬•À“·nî<7^@îÝûÙfwàÌÆñ˜‡)ûëIŸŒå‚£Ù¬ò˜Oñ—¢žÿÎðHïo‹Ùü›PþfŠ™ë*}cR +è+¥&yâ¿%xªìó§z=g0ÿ9éeÀ•bÎÀ-ËöúT!µ@ÐãÐîú-N·3ЋÐòFn&ð²UœÀ5E‹JÍØ‘nâ±´^/.6(½…R<`�3ŸZ££¸å¸+&˜Ä¼(¾X0Qx‡Ös'‹Á ~G�Å0)fΰʗ-E»[øoéªö+Ì{²ª4MÕq1¦×ÅøŸvòìŒðµ•ÍóæÁ='8Eü.ÔC–4ˆ´Ë2Ø1}±K³-Ø=m†®Œ&tŒœ‹½ ˆ;AIꧡJÀ=}|°ßÔÛµárÔ¦5”õ+ò,}öëo1à¿C©Ú×(ø’L›w /ýöb<ù—¶þþ³dQ!J0•aD>›C"I� ÖVx§çæÐøA!�t˜ÐÜ)Â.ÛŽ&Šõ})²(L¼R0…ýg’¹ùÑì)\²qÊlø`˃°Zr7ÀÃü—6™ù§dûK$,åu~å󑌺£§F'ý¹sŽIŽË3öF�Ø]šµ Mš)0ìJZ°f#ÃÙÏz©×€6‚ÜCŠu¸×SãÌ«³OÀ·zOVÕÿJä0UÐ_¨¹äuûp=KoÞÄ£BG3ø‘¢2B™Â£¥"YüÔ†ï\wÝ9÷U+  1 ¯ÚÖÕZ[}Všgõ¾ +gÈ}6bg°|Ñ=¤û¸ÉF-þt‰çGA+TÏPz—øKÙ甃d±YÕÐcèn,ß¿,, c7‚9G+ܯŠpÔ0*-!èµäü6”E>ÌÃ\“9RÒTgº¤Àíjõ!âWéº"O¨DYpiè<¶õ€—Í…rQ>ÔÜ«·õZ‘æ.½X•vòê¼y"4ú&F/FFÛlÔ0]V˸VÌ^qM“Ç]Óº™l@±TµøýŒJåè\îµlíûࢹå +!ÅÂØIÜŒo¥Ò׋´ùmœ?N¨ë ¾P™±ìœ&ý™Ë!_8hb2qX_ÉÖèD‚Ðîî!±Ôˆ’Q¾YïÓ?ªäüÝ&4;X±Vs`"hOºã±±—æ5îRÒÇfŸprT²Þ`ÍlÑá¸Y³L$bq‹+Ý6d¬%à‚|õç(D•ßßÏ×÷Ü¡ÙoLÀ{ç•‚ƒÆÛYk m2O¡¬UÙŒÄá‘ßÅZå…æ¦}¬F411Pw£Á0€qeÔdÉ̸e{Q1èþŽi>V©Q ¦M-È?à2âNm»vöõHÅ1d‚ÄúŽÆ¸6ÍåÂMa>ž »Dûm\Ÿ§ðn0ÊCœÎA ¡i ±Y’NŸl¦f"ßi²ÙšÚæpv¡½K°x@¡H·F]Š»µˆãô,¸¼îï°i$ìpˆ)¹€ l]ps$ ¬cÈ¢J›g`×h±O›h¬šÚ·ã¹2·îKfÊkójø\#]ƱövÔ¤BDf̘ھ(Ê9Ö¥¦IyRæ0Nma©6˜·²Kœ!ù(„Yn.ÈQp//–i–(2(=a8S<šÏ?³Àn <Ïçd??z+ccF1~¨µä®qz‰…fǬ¥K]`Cø úü"¢ágXžÍä¹3Lýˆm—ʲL¯¦ƒÃý~P„™&š¦§ñŒÿ QJCå¬60 £¨é¨À‹t\Uñó1&/¾Î¨êÞhº¹ñ‘¬Ówmr½r¶«ˆã$"¸IŽ„òö¢úÂ¥7QññD¬ðܞǾiûÄ—^ÑJÙ—œ^±y8M-MÆ1M4QuÞŸB¯½uÒ‡ŒÀèIà¯MJ¦¿´Ö¬}%–Úà2п¬khÈý +ëpâç›À íÛê#eâÉy‘ý„øI}ÖŠÀ`¥ØŠBkýHz!Ú7þGîa“°sEü‘f=^c¾ã€|€ÇäÇ©ÃrQ®(ÅÅaQ³û‰\¦TÃò%qEãTJP‘Ž.ª2�ýÏÛ!€/ŽçKz<ŸHîd´´bÀ¶oÄØw«ÖÏ“)¦†¢¹Zp€d>€ +£0<åã4ʧ ¯³‡’X¬Žâð—D·ŒkæÎÍbŒÁûnóð8/{RÂ˧Íb}ØÁ»MF0hë2Ìk`½Ø°è>lä†mÜ0:û> 2fßÅ^SI_}çXˆž†¥æOßú³±fdîµÀ½&Ë"}´ØÕeU*ƒ€}T�ÉÎÊß}÷Ùî±ÊE‹K€½\€Í™¿—áó§oÜÂZ¿ÙhØK¹’ŠÕW@@‚ÿôò_œñ=_(jæαÜo±’ß ðy”HGâ’qÐ*äv_6gBøÎhðïA€‹Ê9Õ’Íë=ùgá¹E@”l‹ +!¹ây™Ÿ²äàªÙÈz ¯Nk"ÊDL>*r´lÊ\'&ÇÚ¦&y¸`-ªtºjȲ¾9ó ×)û)o8»k8õ3@6j–tá;R)/é¬ýFà}ï¤8EÙŸá4ÄZžXœÿÞÝ�P:<ÔIô–$üÃ4[†Ê;ŠŸ9“7"ÁBhÔ1:ŸI•�yUb³ªß”ve//:{L¤©â}šòV¾Ákcûd(rÚw“o#ol-Y™fi¶:d³A¡’øò,3¼ )‘Ûl5)K¸™{GšÕÔJ—@Kø+p\ƒã•ê:þª¢aܳóŸQæÅ4ªLØ)æ»�ÁþMó²e'Œ›³àñYî)ÕÒz¹+è%²•ïGÛÜ€vv¨;Z‘çÔΪÂ÷ýø+¥ß?|_)Óo"XDu'óO—Š¥DoØ7FöÅbB¶AÚ·YCZVÚõgwVG´xK{`ÞqüF¶+g =¶?ä©y™Ÿ»h©½d]¶aÆ_ggzŽ]‡JDkÁ"wµ‹fžqpÖ«ªC[àì nn)goåDìmÁß!ÝüÌWpQù!Ä k™5‚ioÌc1ˆy>„4çŠÅ¢øÌ3ð¶S™Lv¿ä±/@`n üϧwžÉ-k˜utÿñÃY+´¢Ü7ô±<m%o¦µMd±„å1õUh"ýù hµyü“¤<SÒ(x0ze$~ôQ}å˜qT;IìyS ¯Xq2_ÊÊu® ˜$+kŠzZ7¹ÿð×/çNÉ¥»Žé’š12@E°‘Ãp™€D•×©Ñ$übiÜGƒA0kN6<r%’ɜȯj(ÿP" !œÂwBí®FFh©ßFÝܼ¨Œó>Óe±®ÔØx~ |?ÑttŒ`¯)+´|„?Ñ?˜Ÿ ‚Õ‡…:ÛßÛ„ qÚd•NÔ]‚bÛw˜Tø4Ïoˆû$FE*²Nçw—ŸcÒÈìzrpò¾çW«YÞS¯¶nííU5 n9aB™)vÖr±J¦PÙo)6ý$ÄÌ—æ]ô èîõxQgfaäW’ÅÇ:Zƒ$m°Òè=±ìlžÁŒË5Ô‡#òEv+¨–€ùä~“5à‹ æ°ÊN¯'Àö{>Éç>’~uk¼aÍš€ªIý±ÙùM—¢Ï’²OW,y5¬Þ¹†nÅàÌ( +y™×4¶˜Í– +;æ„“ð[ +i!/'¼¬MUÎK^L¡—§Â^§5Ez5U·Â]²'”OÀJÌye™Ë¥òl¸†*\qcι78Ý/.59p#hòÉ– ûÍþÓÚªà…ÖŽ¼zêè™»–-'úVPQ<…’2C`nFê.Þ¤¿Ð“ÙÖ¨ý%4þ&UÑ6¶U¹ÏâôéLñN˜pÿ #£°w|¼¯:à{Y°¬mp¤¡aÄ5UÙ6¿ÊC =� ž^ïèF”—È"süàãÿ¾ó¶Ûn#0²s"òŽ œ×ÜšB4ôMº6 +œON‰lƒi(³mŒCËgc]—̪¦®w×P|™Áð¿ãXƒI±/&KÅ[¯jû8Xc㘖¤’ÞsÒŸ8Y Þá™ñ¢öìç'Y½ž?;2—ãå�0cÁ +"íq+7ȇ@ׄd̺‡S#=ßC±Ë²öNOÞ¾VÑ ËpÎ_þÑ«ÝÝ)âÊ9çâþç?Ð^2ÄϽqã&îر-åêÈÓNKoÆsÛ„?âuŠJCG=Á¬AEå1Y +ª@ÕPõ ‘ŽjijœvŒw}͘Ź©é™|ä™ïÍ|ÒЬzÞÆmÝúÐw#¸ù§¾'åþîK߀28€ŒÑ5pÿühiHêÕqÃóóÏ|Aµ« D̉ ú9Ä�T“n=黃<ÏmB½e~¾2ìx×›ñ>}ùžWòÀ\‹«›Áο(˲є‘eý†MúÄK:vB¢Aî–o ’Šk ú¡®£±ìOQ” )ÀðÍÅìÖÁ)óvòžX…£ÎÞ3/¼p¶cÒܤz½Ð}(ë]8ú£ð¡}6í®]½u ׊4—YÛúãót¾ >^·À19#›+g(qƒƒ‰¢iÄÐ(‰úÔ<uÛý9ïËYËùHžÙòZ&Òy2Àzò¡Àé‹ÂÞÁ0€mš&dAö£NÂ…Þ!x‰àqsæå8ï7‹ij6wá%~¦F¼Ö!W·!¢/0‹W·»Ò”'L׊çzÆèÄ”ÆR¹f!9|Pé‘‘l0rÿf4+%“q–àmªb|6ð½šÒý9Õ‡Ót |]„«úÅŒ¦Ù³÷²:>b@÷âø˜h4êžáv1¥‚V4ç»"èW€SÆ&f³EYŸgEîoûò:9§i‰'a(FѬnZ»¦û;ÛF,”Ó/YT{ÄÀÌ{°ØçåÐDfºhnPe³{í©Ñ)·r¤l)y.óà€{ÖZéç„•3ä÷Áÿ8µÆÙç{ÁRȳ%–Îk,!=/o“ÝWÓƒ¨ýßÀby¸h˜;`±B lJÀ ®`ªu|×ådÿ탵OA4Á±ÈÑyP:Võ 'LÔw,,•>þ¾–Ç%Ao1õv”ÎkMŽ;Å +a \±¢®xøJ›êÎœN ]¢;Ÿ&È:Œé?¬-éëão[qI+4á)?:yæ\ 8VL1Âïû¾¸ˆÏÂT@ À)jwjüqóL¿6squu”è}Ý2ó?ʨÖ=ªªÿ ¥xO›ÀDÒ¼D–˲íȉ$ö“F]¸w¿ØÝúŸÙê=ß߈:ºÃäèÏ ñ%;ãú+b?ˆZZë(A¸“N�Ÿ8!\ì>ÛpßÞàõV7Å#éKô\>”טWT¥ð¨¡«ÏSMáIBö‰‹%uÈïè HÙx¢e–¬Riö?PNæ(ôN¼w˜¬õ€—³þé…÷2wÈ®>‡`Òïèš‘.ke‘ÚZÈ°U�Òòz)j*›f¡HLQ)IRF}}}‰|³ àxêÏyè•.¼ë+ñEeÜÁ¿¼Ì]mš ÁÝϸí0}¢0wæ¬ENƒåï@-6wžc�ÝÀ ¶L�éílFh‚ÀÐAãà/•Eý4ܬ[ÑKQÒ#6܈Ò;ŸG¶Üqø³öòbÕ'-rÀ×û¬†ÞÛeÛº“¹ÖùW]»ÌW«�ÇyE螪ü `jæߎf¾)ÉÜ×,Ý|¡+ +…l3’‘s¾6p(oé|T!¤E¬Ao¹/DÀ’çRéÙ–X#Ïu÷Us—×Ê–25ßæ5M€î¾89ðâ÷ý£dÁš¾Ù¯ly€1 ,Q_»ÊZŸ¢úòdA¢Ö}º™7ŒÒD°?ÔÛc¸B¡”¹€PÍŠ‹å>¤Jt×ó§3Yíû¢©Ý)èæ]Š®}ZíÝyS½3žSÜæpD°üOM^¤z-ž> ŒqÊw£¼S¿“sŸïŒK2F‰vgëMMð\&ÏoHÎ)áÛN&HS‹?J:%MËézfç´@éŸò~ðÞMT�#ȹò¥9,}g#–.›J©”Or3,ªœ/)Ùâ¿‚¶~Üï/; +ç8j%LÓÎï˜cï<âŸö›?øýgݪõòU(ÕF ã!̆ÃWÍ y@Ѭ¯c€ØShêšÔd€Èê2 +ïì<¬lw±¢pÖ§(Aþ¨(³——b±snЇ>ù%Wm ¶¾srÿé} Á ¾s¿ €Å¤\ ʃ«ì»jˆ»˜/iúåðጼ"F£”>Jë¥K/Å#FÓ<Ÿì‘;ìÃñÇ"9˜:Ñy¢ÅÂ7M°ÁSƒszÞþ¾>X;¥“nÁÃîà(<Ü̘6 +œ±•Ç*®‹²ÝxYk!Õ¹0ÞWÒÁ¼dK´$<WokäÜ>{hÉŽ¾±Fb"úý~V3D]ÔDÉ+Š‹Ct]¶¬;pÏS€’ïŠb6ec>/PÂÌxFP® FÙ<Ü‹hUE&} +—ò“[·Æï]³†ÙùÄ/cëf…"…ÞÞd>EÙz$ו}ÔeAD‚-1-w�®=«g¢àT–÷EÒMÌÜ‹.nòVw¼Ü[œš¸ÙµÐå÷\ÞŽvê´ADz‚ÿ¥‹ÃÒ °÷ÞK[$Ábçx®mËîÝSÚ¹ìý}é} Ú†¦ƒlãFPÖt♟Ô묀,j”þÐóÏ8â«!»øŸe=ûÀúões˜’ÉqRQclpøÄþR)þ²§Ö³‹ï9v´äsî/ÇH`4çÁƒ2÷ÖÐ7þAÏ—œã|lgU[ÛŒye˜Z¥d%cˆ¤o“4i¼ÖÁ³{9’lcf-º1šÓ,s‰HçæwNƒ™é1 w@ù�NæÎ>²ÈÿÉ£$¨uŽ¥f=3ŒDÞaaV:e…4¥~Ãä™ÖhÄn+Y:‹/ó¦Pö Ë]�Õheûóéõ=ß\hWH`2lARó¨$ØÆÎï<[K¯n¾`�…µäHOCJ»ô¼ôôhÃâËŸ§ÝÌF}ÿ=&h©sàøR0€4¶)í¶|“d±•×ô9ôP”Ž€¾¬H2 Âð™ªD5¿Rï FÎÂJ”•X¶F?ä&J´ëC¥OI‚ù´wÈݳZ3Êìø ô®µ1(,«›¹¥p”˜Yä<°KÞPæqv9�ˆšÞ ®ÛV|±»—Ô0\àÀXêÀæ‡ïú¯Ì0"ŸïO^¡›º÷)ˤ2°Õ<Ë9g ¯”á¼ÖV:˜V/+q¡ùÅ@lTQP†:‚@Ê”_ä WûÅ°%ùšÕ¾Û);öù"‘ûgîÝ{uéÒQC�6¡1b!žJæý�}îÅûQDʃ4Ì S³Žé´–F5ãtgw÷c3kk_äyÑ“�«8 aãÇ^x!M¬ˆ³‘|ÈZ¸´áH<›}u.ñÐz¶êÅ´sj°ol +ar2årcH¼TÑÍ‘ÐÒV‚yhöÏ~ÚùÆ¿ÜŠÄ�”æˆÁÙDs$mrgŽŸLÝÖî+k,Äï¶c0Ñ�dÊ5Àù®u‡çü%nû^ü•Ïóe˜Òé_àÞ_'HþO*ý±W?538öð¾‘HÕ4ƒôì„EŒ«s4àç^Т˜ìLˆ¶†Geßæ8ˆøSôP¦ Ì^dçL붫ږý#¶Bé#'‰z!Ýi?``QÞ ¦@š÷żFŸþ8æ?¬j¥9eÚ·^tˆ2¤ú•k]c,çÒŒøøª€Ãe-ºÖAq!`Ökqõ6(-A©ã-åÂÏzPö%QMÄÉ/�G`ÅÕ÷Çéét»^X*0'0ŸÁsbïyvÇÈwÝצiZŠ’ê¡Ta,.B²'‰Yº§|2gsã®*ñ,ÅL¹Þõ@ï×ïYøV—Rc ÎsÖØVÖf=•ÈmùÄÒ–âÖ Ëh6_0Í1ÛP³Aܫͫ׻5Cm†°XeªÊ‚–>“3½{N1°.DZ“*OÝÝÒ€,éCC‰ÜóÛ·o?ikk“K%þZ'ø:ýÁ¦R±L2¯ÊÕþmÌP½’ÍÁ=êã•X PØ9`U’ãJ.ê�*�þôÉ‹B cÄ÷$aBðHA $\0•gS…{$†ËãÀ_¹¨+-ߺáö/o9‘¥z⹤}ÿÁ7ÆïšÌÆšê,Þ ¹ã”>È_Ú ÷‚Æ”;¡QŒßƒL”"2–‹‰Rñ¬3Dº ÈØö¤ÄË·´5–ýÜDS W ké£-vÐUú¡1߬¨Ò±}«V=Ùwb<ÝvaA»yë'Ç–ò#§�ßÛãÚ&f5àºñ»I8j7#8¼¼½½€µÁDGYrüS¶@§Io�îHàq +í0M O>ùÃÆÚào™ÖLXÖ®áL’J¾…Ì;¿jê'áÒ9ECZ×ø|eó°ïH_hÆŒ\"Qˆ@ÓUWWOPMÍ”nlû�/sŸ„9¸©¢(6ÍmÄÂpl:‡n¦8ö¦‹ íÎú:¨õ«€àX½Ý²žè@ÿ§ú‚ ˆÏåFÜŒjÜŠ,ä"`;¨mB³Q`y„êV*Æ6òØA&Ü6:ÿå«£0¹é âXüdÎMün˜uc¾<yØÿ•/ð]I4ÿ¹<g�†f¤ì”�X˜üÀaŒgExK-OãV1y¨JrÛ5øÂII&”A£†VN‹gÜ»û…ô=«®BzŸQO¥âNØfìO¦ÊŸ¬)*É€ªwØüñd¦Â ÷®çÏd2*ã‘^€‡íªXŠ’‰!;¥¿„»¢ +o‡7ba‡åxõ¦ˆI÷®Çx¿¾|Ï·Qî¾¾èìÃNmßV¬øf ¦r¾PýD*7Ô¿±ÃSÖ,‰©ãUîÍ̧$±Ã´Üv…@ŽZ¢g�«[y;r3ˆÓ½®Ái¥[¤™Z>_Âï&j“1¨n|Ãs-ª¡ÄYïsòÜ)#W<Ë=}àÊö”Dk«b»ÝâêˆÔÄb?*Àû×ÑêÉWuy!ÌnÌ[Ûf…âä7ä&‡¹²k�"°Á¥Ñ”Wò¯AU^ضì~t±¼¯<8,(ÖV¸IÇ^;¹·©ÑáÎj¶™™¢úb¤Ÿ+Ê;U³{Ë–Bxƃj‰ïv64ðò@Ÿv#•¾‰UóE\ 8((Ö¾«›¥è7³"óyJz:áÎè€Üœ<§r8¢ûÇz÷ºP5_wÂáh”숻ôÒK!»¡Ý—‰ºøÝC!mÈþ³ŽÞã±Øí¼Óײ@Y³ùyMδûwL4Bí§Zúç$†™ƒ¨ØœÁü-ç¢ûƆyÝÀbQW¬(BЇ»ºØÃÅÚ\:ôÀj…å¹åÀ“úA(\€”÷Â'þa90ãØþȦɉO°©ÛGRAûíÀÅ-¾¥Ø“ÛN5;¢“×{ÎK¾h}€Å/! 'C{'\lU–,QcÌ:h?ó!fl2Gƒ.*ÄꊧYèÀ«¡�£ÍG-a‡P’�œçþÕÐûö‚fSDÒÊP9¤!·,öÈô�¶,°ÎYRÜ“c¨h1·Âø?5õ]¸:PÍ¢s‚xä¢ÿéŠ+œÃE�ŽítÉôÞ; ø§º)¨{½WøËÌ‚–îO �î´Hü§éºÛm·´…Êý¬ôeæÌ™J×xÆIñ@* ‚ÓÉ3^àÍ jI9Yˆl‘kœo‚—ôë=_rŽÆà80</@¨c€Fø3;—í޼Ŭ”ñÐŽ'NÐMžãÈ"X¦6`ó–"Dÿ“§J•rß䘵µÎFP]„ꤧpë%8c_›&'ÚKFAa·†Ú0¬ú‹UÃZ +Îq Äå!†Š ý%ƒë3„LoN¨=µ+’îud¢Ã+››³UTÈ(Šø—̘ãD\µ ˜ÔHr d…ß*¥B³ÆkàjY UáAd£žõ”…ÌxX‰¥‰À˜8ÒÄ¿ë þså\0+RêI:ù}[z¸oEs;ˆV&¹^'4Jbj—ÍqÊã9G¥@~þÅÈŽí👦ýRÎƨT©¾ªrÜmòxúï7ÿÐÝO“çeføªgiž>¯q.;w麇”¨?còÃ+KÝÉ$\Â’%ÔÌó¦å9œHù +…x„pk 0IK7�…ûRº$†ÂžŽ‹üã òyœaΗ-2mà(à�áz“b³ÇW¸ Ä‘F/idr¬ùèm>ª¦©E¦µëÀÓõ´ÞmdAÝ6”ÿ öý„Á¥(6÷u(ÌõH½ÝØÞ•9Mµ‡§4[ðct¢üÑnä__%0ô%«vÀçþlÍ&ÐH”Ÿœ«¹Ý`·ÿ(ð*,O°šö[ä6ÇJœeÓ &„Šµ6$ À%³ÖbaÁª+ JqCà +¤¤¦idáÎàà7Pj|†Mžxäàÿ›@£ªÉêÈ:ž¨7Áb6Ì–´(Ê\5Ìrb¿(ý¾ˆÊŽØsànr)F.Ê3‚_º›§©ëÍê?Äe÷ÂÚ£_¿ýîv E\:€DX¸šÖ`ã·Êxû“ÜŽZßHg”zBD‰È„mhúÅÑjÖV†Æ‹òõë��@�IDAT?=½D֎皥r‰À3ƒ0� éª @èóÒÏ;½ïDÉúc4Ëáó¹÷FóóNf,?™ˆä §ººèöO!ü‚Vä3ÉPý;^7¿BšÍÐ>¾ƒMæ“϶‡ß"£.GÛ9*ȉX ™VZS8¬xK®çi¦^Ñéßw¥ôœ©Û±š÷ÀÊkðýB‹]àÒð|Ââ!Kg¾!;<“¢™¤•¶"Øì@àÏÖ ]/RRIÔ{ªEGs1Q‘j|¥Ã ž9C£&ÀsŸô;ý5ÐÜÞõþX}ðЉ +®tê€o{C]Y@~‚r†ºAi;*ã”èâ¯,rчkIvÓM_y+U4‡PjÌÅ`6kEþìzøÒ;ÀŠøAŽ1—s~ž×7 RãÕ–©^…ev9Gs‹¨qMÜí®GâÒÊ—L,‹^|i0Vã²×¬4YÇ'ö|ò“U¸Vü§_’lAÖT¼7–!Õ.立5Ÿ¾£IvÛoq÷}pG|¾ÖO)Í„O+|ÏÞãÊ ×~»Àÿg%iÖˆ¥p7„ë¤Y¤•ã¬mò¢t½ù¢Hn1\(O÷¯e[›-0¯ ÌZA\dÛ¡S§œHʘ‹)#Ôwþ…zïîñ¤¯ÓÆä~k‡V½§¨'!Œú²È(ä<ud2_Ž8,˜¾Ø«°v/ñý¥ÐèÛûÎv•¯³Òÿm¯<çˆ�)2nhÚ÷sóm#©üÛ0“s ¿ÄYܯûN]ÝàN=±~Œ6¸})˜ézI1fÁ(gÈb+k4˜Þ¬ŒYb @ÒþÜèè;žõÿÔý!B–¤7C)bOzHM +¤@èΡdkç>Œ„™¯¼8>nûù‘<~J¤éŸk%ó!Nb¾…;ê*.xûvçÛg´l”>õG´YÈ_‰J®ž%˧~ðÏöÂå+T§ÆI—…4+=ÃãÈÀ§)æ9¡Y`Å°Û@I“Ïè…”¯±~¨†˜“mˆv{í6ž˜h6Ùå}s¾@¡0LU5Jì|Ùœžßdñú«N)ð¯ä¶c¼d�¤ûÎ%™€ àa‹"p‹že£®ÛÎA>å’Ç'Wò£9Ínx4é,˜£ÁQa “éã»òy7(ª¸õ Õ™±’6”‘«‘6Ûþ±"eþ +ô1¹�-¾Þ!Lim•¾W^áŽùOµ‚s-pM�-´ù›V¦"´ÍÜs4%^Á IgÅ®ƒ;¶Å¯X¼X(¨ËQhîP[AÉê‰GÎ6z<:x“é‘ŒµUÄb£Ût~Pá&É’k¬–3âÃÈ‚r`;AB}FÐ̘Ã7]-¥H_‰öŸÈ +Õ†Çß&ÜGUŠú"¤ktA>ý…“G»q3¥fô¼Éw@B5aŽ¢Úˆ¾í^yŒð;a³ëlÊ™„¾QÙ?ù÷,yŠL˜£…×ÞtJýW76Hÿηò ,“yŽYi^÷„“oΩy«”Ðpš)!këÿ’÷àq•gÚðéezѨ÷bÙ’{ÁØ,cÀ˜8HLB %!&K€„”MûXÛ›¾ÉR€4‚½@¨¦,÷Þd[n’¬Þf4½þßïز!ßÿ\àI3§¼ç=Ïû”û¹ï8Š(S¥ +E͵qÔ”¤¦Çú"C;§ƒRÒdü|XÉÜ"0|DI^ê•S•¸§*èréõy6µÊi‹tÄ“Nd!öŸ:æ‡LÎ_pêD8¾Ã#q~™ö¼ÞŠÅß·´}u>õÑÜ¢é]£y;¯èë/®ðöUìj#ƫο ¿BëMNêê?º_ïQ"wfãX´-úv¤l Ãü5f;qSÂ.1(Œö©’\ŠBx@Ð2'+Üîäª`h%qLiÍ'|v»×o/,ã)uŸkM†¾ õÙxF}èÜ\á‘ÇŸN<^¦¶”ž¨¼ +óП”õË®ñŸû°½?oÆ·GÕçFR*º< ÜŽbÞ„žAUt•@¶–Ó˜“?$'òæP ðàîP)½m¾3áX-C¨ä4ÕÌ^„–c“µõ`A›š4ôCöŒÕò|뎡&³¾xb>8J?GÚEQ~ìòo¤X¦”b·-ù9§4L©ò~p)£aƒªƒwü=JÏþÅJ=`¾ªFÃ<b*è™íÕgŽUí•hMË|”U"{ûmYA‘<³ŒØä¡ØÚ¾p6N |ø*¯Ëñ¤³~íDKg½ß}ìÙ"¢ “õ¹¡Cik¶,±ß@ô|Îí*ØÜãa¥c$n¶£½îïÅHÌä»Ëë¢Ç‚#Щµ|‡á” 'ZÎæ?üìÇQ’;Äà1›mäR»Øý¶ƒ(L¤cŒisümüðÀ)¾ ¿SÂk¢80�ld¤£0/Œü0‡¾è‚éT–7n8ý^ìû +„š%@jØ¡íÔ|Ž7Êm¶”rõÛšZ)`‹ *¢Uñlˆåä¥v7Wðê‰Cmy@˜i&ÕuHÑ@5 e÷‘ls¥GÊE8_«-E r”q)5KÐ\hÙ˜·žxà?Bäo$ÏOK:T¸«1p»AIYG¬åö¥ý/þ5H>·ûäD'r²òö£l<>¨€OãžÝÑÁ…ôýÔCYbmHq†ŠföjZmâá‚éÅçcË/ž›Jyðó.~5Ø냃}]ËÊFséÿÈ-ý_ý¹?7§Vû‡`@s‹>ñ"W|ã;³‘ûžƒ”OlâtQ©¶·AåíñÜ}cÜ�£vÅïG–ºí$¾!6Æ&Óm +šP@2ðØN<$éUÀ>þøãTùÌ9Ÿ‡Náç’º:´œÓNœ‡¨n“¦hOcþÅA¨vI;ÇŸNHÓ—é4{;.¹‹vB´ +,ªOçÉ|ÏøÏ}ØÞŸ—öbr“¦Þ’UÀÝÅè;=,í(òöù†(IÅŸB._vÄ¾È +œGM%^\6æ¨l¿ièÎ5H;Œy‘{†S—;ìDK×^“=[ ôkGòÓèaÿµ¢S#€ÂÜøÚñ]Ï%þØÚþ:ø=¿ƒ6õ¥$÷3š´€¯¾ßýXAÆGŸY2©‚äs['¨ÃÚ::DYs™¼S¼uÿÙ„=˜Á—o—ð3—à ê@½=\à=ÖôNÁëôÎù—Œ…³~–§ È™*ƒÎ&˜~,c+ë£xŠjæð$kÀ’yó1EÉþN½O’øoƒˆk.`i°®Xqeý«lc¯ã æV¬Rw«™Ðß° •&!§²w/Å£ …’±ãŽ?EE±7“GsF-È`— c)â~0i0”E5Äl˜ìuˆÄŒ¦ÜJ·ù_lj"œÌƒ§]zí<|¾QI;Š\ .l6"=÷9”D0(j*_kgè¿â˜HR5åSÑwŒbñ{f[_äÛ4ÃÞJ2ð\Ó?ÇŸ“ £dx$wMã·¨”âšjºõ"šdš«Y:ö‡xor% +§äZv§¦ªºþ0*Ju°Ðo¢¼‹zr#À=FX^Q4Þ‡®¿ozÖݯüÒÛ:.ON‹F+³ +}È„x°0¬}Êï8¾òŒÑ"ûÿek&pšù+–£+@Ï©!ÿ›@»=9ÆfGŒ…]Ÿ®é?MfGÖ,öèu#mÈ]wûWÊSv= E´ŽKjC˜Lï9ŸF¿s¾^·ö%nÕ)í®¬Á<>—A V‚©s5/››³YîS(N¹¢Ä»büsu4˜þ<Í™‹�õÙ~ïæô±B;okz÷óq¾Îó|ìç¼Ü<••Ž´É98Öpi-IÅ{)7°fy´ÄWVûTR$Aˆ3p%Q”ܳÉt¯Är›LŽßÒQD)³Î\ É˵N¯ÌH-šžêX»6‘²8µRà¨MÓ¨Y}áÒêiOìîw‡M.U€L{”ÊzhNž¯…_–ÑŒeEEéÁ]-¢n¡¸ohÜV‰4feuuö`$²�²ó3U‰{R‹Ãj†¾ŽwzÜöÇyÎ7·Çq©‘q»8ë§qfÔV¯A'ó!�›ñÇܤÞr¨Û+ç{nB}qHÕóQ•þF§÷2W…ÖÇïbuËÚŽb†¿®Æ?–\ŽëX“¹�žIä7Ï|j¿¥Ù«)Þº Tï…*8<Ï:ð™H÷ß{ýžÀØúDw¾[·&š·`ÈËAIiuÊ„ÓzžÖÑ%‡„1ò®·¢ðw‘ÈòvÑIÑÍø®Ýé.„WľÉCXŽ\!pmô3Ð[kËdÈýÓ�±Ð€ >lÔÆ^ÀßïE¾=ßdùÑšLø"F Ÿg€�b£4ê¦Ôð€ë8ì ÐvõÂ'`´_÷ðÆà¶TÊ x߬‹¤¢^‚µF½ÖšqÓäH¿4< +',e°*ÀT%t8_ÎÐN r[bÕÊ\›nnXˆG¸?˜œñ†c J1FŸæù pècÆ'÷ÁÿþÉ:w%¼±ïQƒK¯GDE((2££zö÷™dŸµYÎô +Z;vÅcý@PaŒRÃ:ø¼WÃáÍ”ÏG0!ïûÆ‹ÔvëC:Ýœ¥WÛBéû‰¬B•`N^y²ïÜ“¶·t=Ù¦2|�š6²_ÐHo>ÜÛy9Á8íõÊ"›�_hï`@/®›!Ån/‰àX¤ŽÀxºÝoIܯxBô«ÐëGÂ?ŠEŸ_( ù^äÌiuç`¦¨…™¯“اÕà¨×Äp¾ÃF6¶ÞÎuÈíÞ,Šà‘¤¬=ð´;t™ÓDÝ…y*á)RÚ‹�Î/ÿ,k³¡„Èjíqx…þwÝ]æ²À-ÁþŽ)55ÁÃÊð³ºe»MüñfôÝ7½““~×—Çý"kÐóyκü›‚½ñݵ¥®ÜCù½ ¨>]Ò—Iðn[“¦ÒÓTPBÎ52¥xâ¢3îœû—€½t²vy;ÈM~cQÚŸð¸€lÖRÔ([ärÑ¡�™B1«ÛÀrCŒ;ðßyKBy®„Îÿôú.Á‚²9sB$4@æÀÄ[ÆÎdÆx1RíM’ÝÔdžÎ&ÖJ:wˆó· +‹-û=Š‡9x`ýò‘IÃÊ?f©‘ýæ l§O€xÉ›‡Í¿ Ké:Öå®Ä#¯¾Üѱao"¡.†Ö˜N¹§¢`†DªMæ-Dºì\8ž'UÓˆkÙ‘ûÙLª‘r m`Ÿëqduó…Ù5kÅŠŽ’Îê&ºÇ-°¾Që²»”åMèPù<t=Ú`SŠÍGá²%éH¬:>ÌRg`Œð˜˜}Ã1pÝÀ¥ŽfÆ|»ý=²ÓWôÑý—ðE4wº_áÝ‘ÝÙ4]#0–a”˜gOv` /m«‰‰åÑB»9§…ßC£Ï¹bUÏ4b=ëÅì(öét%~Eп[ÇÀßÎË–>¼·ÃªV+ÛÝËîÝ.[ìZ”¡A»É,CG¨å¸çÇ{½ä ñ-A®´îK8½Ûvšm8×ï`O^?÷³çå$ÏÓNÎòÿÑ}B²$wKÒ (YL*$ô–%·O%˜ÓéTZ[—[.—ÖÕzâÕ54+þ'Š�ûІ|´Â d¶MýÁ†£qËÇi `ÃỪ”…™luFæÌv¡JÐõkø,o�yFz OÍmð„VÁkû>€ŸwMØd^D{3èrs^ ˜RÔK°j +0Øûm–ç]¡úéÝCQgfÚ¼…Óöt+UªÆ6",îÑÌ!”±bàè¹ü½W–W[YoRt};Ë[î]ÇþÕø…¥${5“ù=Ò!¯£ÐÑ`³èO $(ß³gëTgËF´·`’M@.üä<§±L2sa‰¯/®JŸe.q:å…Áº,8/¨zvÇÒ…æß;‹Ó¿'H +!À^ä¥ß°XãA\ÿ-àÃQ{ìïaLrŒñ¦?îƼ¾³—x÷/¢%øH°{ç, Èž*‚ÅŒ‚Ca=o÷÷0À5´òEÆ\xRÀÈ÷¶Â}ά‚ò^!¼Ì¯ûK'\zQù¬UÎs3°¶·Ô +F¶ûD}>£«k ¬ôÊ×þÅUUC@5ævÎpyù¹IUJƒB÷²™aÈ݆·úÑÕø»Œ]uÑ÷g±¯9ìÂ] Ž¸“Ñ•ozø[Lš æÂà~ɨ͟8áö¡üß´¶ñ@‡èƒ‘–�ºÌ2µ$‰ÇæØÿ}D?œ%ª$B¢›Ö`ª<£gÊá�MËtÏÉÛ %òJ\ ½ |ËöpO8§Ò¢Y7hœþ-ÇŒÅöÑïW´_§9QÔ@Ù +ž®Èœ±}ŽÿÜù~œ¾ØÏD`}¿áø㺤?ÅF~þÿ@›äËý‘ž³ò½äørIu9ŠÉÀyù"¢ ´{¼"7C@õ9çtþiãKpÝÐœ1ŶÌXkoi©š™8zRtæ°®'Ò63¬èY‚”Ïp¦p£MËdÒŠº™r{Ö´lX›¾û9±1™J.¢x¡œMlÍèl¢¡\ž\ f‹ïj;AÀû5•ùBæ×ñ?4²è^(YöÀ¨´ ¤z„ÊÄ+ò†ÉwÀë;ø¿Å@,„è´ù†·Ú;–[&Ç&>†Þ‹g7릯š÷qõnŠ?ƒvÙ7x+CöƒCÿÏ6ûÿ °Àß…2ÇNn¢ýôMË ßHUQ/$îžKkéÿ�Êã�ò¼ŸíH%/݇YÊH%à?똮ëŠ*Ù7^~ôQtLS§Sü(Ê} +a|5è×f³ÖÏ/*Ë#ÕÞÿöœ¿+/Çd¼ò:UÈ‘ÆÑýõÓx*ó=¤}~[Û +òb33S"»dÏÜ÷Jü2-Öº¶\4Bo,* +ó<},Dý@¥bc)9Œ|8ÒÄÌ e•S*q.gà&<»¬i>¡˜Ö1D7"¡$bÑ?“eóW"m®†Ñ‡ä÷d<m¼¢©Ú·i;óïÝ öLŠ©"¹ ¿lY“AT‚îßcXÔ¶/=ÓUhf"ÛRõ—;{l(Ý×—² w†ƒ6sQ‘û@\É{]S¬‡àñ¬dÕþƒÙÚ*¬›%I¾Ò)åOlZµŠ.Ž¯±_OeÞY0·!Çu|$7Í?¢bR^ÌîŸy²Ì9cF.$wOxJ¸ÚHe¾UV=µ©ôB‘+|ÑÆ+ë§](Ò2´–¢=×Í2^|aì>ò<ÑM +0|…Æ)ø¼ï^ï;ƒo4)³[`„Û$)*C OccÇÌ=ë"¡w>wúÈ!’c©}´ö6¢²¬³èžÜn÷?mßÎ=ÖùüùŸ^æÌ™Ã÷ÒbuÆÈÚÞ¶^‹µP·!µ¨¢hÅ„<O¶´|:ôUbWò<{ +~–/¦ye—°(N´PÞµ'’<À*Ôõ&ƒÎSïa-sP/ô$ð§1#C ZŽ’zŽš¦ËxÃ.øÖ§¢qÐIpóÁ + ÊÓÞXbðÚ‰%¤r››,ં®VøÖëº7—›<3‚¤�–Ç[³¦R#ÑݯûÀÙ.F6’UTõ@©o[-å3ÖÄ£p44”ÇRTjKU~©3»{!¡„c•´¹ ¹Ôk:±+I¶:¼ýÕyè[ug0ýÓÒœ÷nhÖ•@ÖeHËw³¼ù;vÏóøcÀ\æ®{ 6ÜYeüœrqšÚËô]2ÅM bccBüÆe‚*ÙY¯Ý![œ¬©*Ln/¼ˆäÛ]Á º%^îÛ<šf7÷e†ÿkymmàk/¿+ù"†È 9Ež¾Ù¢^¥ô� ÕfÕ×C²,ð¡dïþææ‘ 7¢¹c¥¹zõƒ±E_¼ï¤p&#ê˜ôu Êù9ép¼QK<‡§F~Ž0ñ+Hö—¢€š¸�'ÖŠ&Ï{©ôs—L,HâPäSM+›ÈKnKÄh¯Z@´Ì©Ähüæ‹®\J„|€ˆ”úç]ê¿tÅWjÑ\à¦E9b£D'Õú‚•”/¾µ#ò�<(Ú©Úø°fÊ~èí%šïº.`Yš‰(âŒ]îÖkIª¹ùÒÿãèæôY~8þÅ%ÒFbóÂr|½Œ5nVá.™U7™ëRø¦É| +�yÀ+)téòÀ¢`YË8™ª}†Ä!]óÎÆKBÐB§’AiѨ¹M8XteÅFÉþÊ0M ÄO +NwŠ†Ç¸)óßÂŽ¼s–§ßEtz5õ*ŒÅIgS[.sìË„ðÜÏ~˜~þ§ï ¡ à0UV‚¨Äˆaa¬¶¬«g@¥Ø +‰|ÅàoT‚Ri/RÝ.¯Üh@£G„Èè`œZß}¢rQþ[¬eGíLÏO +'ö×Ã-CvÑëž…bÐ%‚ÏY®3FQÒ}hª<Tâå”ãX¹#¾¼ÐqÖC”é½-ÔçíO„BTts:9&1äEž¢+yú¼ñ!»ÒŠAÅ°žçÜ\+Ã<êìg®ókÂÈ'ÞësPǯN&ÿ�²�R€z×6 $E›Ág@róË3ŸéÇÝå•Ó:a¼.GXÕ™Þ0$ç탡½Ð?ªÃ›}Ü6X�ÃÚJŠFÅm²ìR ãój3Úp›ˆ¡÷žmìÉ9Ü…ù ²§Ž³ “y¢@Y.È<£jÚk[©Må…j:¶ „HhÃTPJž Rýß1¨¿âé[€Q™6ÿ†ôøeÕžÆÖ×™„…r¶nOŸÁOÞv µq÷ß[iþ3ú‚“¥nqrÌt +|Êð~ŸÃŸŽáÿ±çç«¿,©Ïà?ˆºØ%ðv‘†RVvZñÔ†)µcÄF¹±¥ÐU×PÁ¬Œpòy¯Xj¦HIªO, cß¹›·»F&"O|+"À\ûh¤§DZ‹Jw 'º¡ÕÐvî®ô’/ÌJ*ƒJæXK$µ¿/–È«¥ê( FU +>é +ÆÔ¹ÊÜž?ºÿˆßŒy‚Æ ‰Íj)oÙèÕ¨Šf7F‚wˆf*ƒ¹¨ ƒçAKëFÈ Ì,ë”(© âöŽÞÀhGÈžW•æQɇ>¸m”RüË×ßâtýûˆÖêq µjh»¤Fþtî™Dí*š:÷ÅÞd‚áÝÂsD\Gˆþe1Õ?´øŸvËýpŸ]A€#@ ¡ª@:³Þž”7t±Í6DÈ\~¸ûÍèfvàÆOfLfŽ©[K¡#µ3Qîó*KçAbœ–‚Ö¹^QËny¹¨(—Ÿ%© W‘ŒvSþ‡˜÷Vôq(Ë^¥Š» ]ôSïøþG/©‹FAó>nƒD||¾ÇÓ ÃKªµ£s‹ò¹óË‘+¾9Ø'?r&{+çä¾Ë²ü—tŬkkÏ2âãv"ÿõpæÎàN�ã.¥F$ç·¹+T´o$9 苲›7Ç)N{(5düj¾¢9£Bˆ+ÜÓ/,rÀ³U‡?hQa´µ�ÞõK-‘y“MðY‡#f×Ëʪ¢ "·QòeÎY³<$l‚!ù-Täòˆ‚D‰»l:îc)ñ,J_ÅCwÆçÞ¢ƒçàÖÂü´ÿB?D{ò_AÖ¶gõ¼Ò1¦gó›À@SÏ`�¤Í”AÇys:¯"x¾FRéåü©”n[ŒÙðäM79ÈqÉö‰JO7R!k3#÷¯Þ¤ôO‚.2@h÷€îtøJ&Þb÷ÆtÊøYgâß4»ïß{Ú½84–ÈëÝ‹ÚâŠI¬ßsÓ†ÎÁ +ì&°'½Äs„¥bxÏòtvööúP±ý<¶±ƒ"¨`VÝÅNDñõ&‰âSÛ‡òVbÌRjŠªrÈf¹Mš ”‡|¸'†þSmÅõ}žÎ�¿'B„\ë‡}#÷J1S‡DƒÉ·LåT&ËwgŒä[ðzs÷™æè~È)©;Eé×#í¯èRür\«€f¹bÚHd²¢r—îÙ»wÌÈnÎfuŠ085jöðÁï¤$Þï1!<RCEHÎW’ù�¢ÃCˆFÿF±Ô'HôyîñÑ*º…Q(K‰ÙŒ¾[ÔM‰ŸÓ +Ïýì‡éçºÉâhwVEeP3…(È’\.ÜIºŒšN:‹¦+,(…m"w½xpÒ(Pï³BÌÛ‹à…a„ˆ©kX�¥w¶ Aòæ´ŽLoË ^9g|³U…à +ù!`™õÀ¡¶#L:,*°¦&(h¤3éZx‘³’½ýÓwýäTï¿/ &ÒsQù/DEá¿Ç.p§3!AÚa2„sózfKtº9€œã¥�&É@K‹î2v +÷$§Û½ï{w'ϼp‚,H·¡÷ÍL()(ëO<½uIwQZzrk`´¯BÁJa±UyW µÌË®B_söø¡,[V6Mã´z€2&Óc€Ãâ3#éSô½“ö çA¶6ä©A¥w¹J[ÿ†TÂ%°Ð›‘÷Þóœ™.Ì‚Œ˜ÎõµW/¬+©ïKD#Û,»¹•ýܘ’ý466ZÁh|(iÑWÀhÆ-„Ql9å– ùÎp¦71H󌂲Ul¯ +ØS«Èqo»CÉ!“e¯AêÁ1,±×<þ/:¥CÅg@Œj¯¬L*qe*%*û^ͽÑô—àU”xùP˜j¯uq‘J|ŸÌ£ÜÑÆýs"k.)æNÐBnZâYEÕßdxz?¼·^ñLà“(]LÊ ±ö#ƒr +…²ìàU%¥hªÝ5ùdU^Æo'Ú²èn´©=èp;k±wÈýÛˆÊ8Pˆ.Šb +”Ïùþœtn±úƒç„¨º<ˆ¦î‡º4«Ù&zw›…b«‘‰ ,Ÿ·€¢h™sV>WìÌ©©hxh˜¿ +M(YŽµEJüÿ¨Îp¾«-’òª”9Ý4gMuê!5ÀöKn=õ<麜°ÏÇ©Yõ§¼À^`²à ¡è‹àU.0dá¹|p½Œÿì‡éý?íù.žèw—:Teh—Ô}{~ii–ÜÑ‹Ì_\$²ÎÏI6áóð\kPèÐy$ÒZ"ŸÙ +þÚëîýz£Ê›v–cÃÛr�^Õ)W{Çrˆélv>>:ÑÒ:Ø¥û¢†~_rdø^…Õ¾„|ÖtÃØ„î¬|†åo±ž÷w6²Ržë “¿Úö.M3÷ÉV2 æ§Mš¦ü<+šÿ +2ߦÄþ¨&½³ä$”LyžUcú/ M]Cµ¦ÅþªÆ9“EFú:ŽqPùèF»žç7¡D ¾ÏÌ'© Ýü#eŠ +50ÓÜ�ÑÆBQ46îÈß]B¦¾½#Pù£¥*صtÒHŸÄ8žX[HHEÆŸFî}oOlŠÎrßą͉e³- ú&åá¿Žü0´oc¢¾ÇòXáhC°Éšë£â—ÕÇ5$ f÷c}~â9“uìÈïr`-ŒnÞ¯:Ó·PÁ¼&xNaG~¹àLÆTŸhäØÞr'€TŠ†ïrMGVÈq÷�Ì·P£™‚óèÿ›W² pW…B‘†"ÞÍy|æžWûBuøZîÂÇÏ%rÕBcˆÕ +8ïýX~Ÿ3Ï„"Úm6Ï*à´ï†ú—�ƒ1‡»e0stŽZZæXBSTcÄ^^æ…Ë€¶q@žƒEVÖûhCÍÔt‡á:jš"Lº�ùÞ±çz ëhÑÕ³Šníz³ÀºÒ½¢€J5ßžQU7?£ƒðº,‹ +ñèí"ÞgF³ºAo§ÓçhlŸ£Ÿy?_b}ݨèBíœ*éé~̆Räð;#ßuÖ¡÷¯]ú:æ¢áW(CMŒcè¢"ÝùáÝÎÇ€æäLH¨Hˆsրݬ|ÁǦ7÷D&ïQ‹çð@šPøaMàâþ3 º-µ£äMò±Ópão¶ÉÒµlŠ ƒÑË&O £m,¤Y~~°t•ùS_¸÷à²Rß’É5½üñ¼#TâIxÁßÄR¸vjŽ[”ŠPÔ›DéŠú’g[†ªÎU3ÈNZºû¥©°íá7ó +¼+Žz†P€T—:b§sÒ2ãïœEºðæÖú↮ý YEŽ]|î·t]m³”ú5¤0¶À[\lªæí F/´˜6‡qÛ#üŸ²j®›áñÄVœiˆ˜ëó% %~Ø0´°’È®×nŸn:¬"ÉGÎËdˆJY7¡œ‚k/+õ[1ß¾åbùOL¯ŒfŠÜ©”òÝ$Ãê~ùé’ö9s9o·6—튪±äÑÉß¡7÷,Z7<IDlÆÅи´È‡Û«he0¤mõÈÎãîáƒô¥@0TÂ`F�=;ÔÉ+PN]ͱ™?\>ßñ÷{?Ð6èÂLpA¸ô�|ì@ÃÅõ‚ôåý¡ ¢ÙÊÂyñýÜ<Ý°adf" ×›;t½‡,&ËACº'gy©Ø•þ´à{èŽftFM”(ÅÛÑB Е:PFP]ÕàùÒ¤áHFUq:sÜÛñцšÑ¬ÜÇf‡F1§;²ÛB9î¾ÜâEÈ$ÉýãÖWÚ[Ifü½p)Y`ßAñ nfäböEºFçJîc6‘;'×Éñ_}ßß;ôÀ®¸ +mÙ¤H+½ +üõÒñ¡*b7ÆŸ�ŠöÈ8Y[Cic7²Ùø݆l ÜÙ³±ñßù0¼WþäŸ=©B°›™–úe@Ð̵ã!¨Ö5ã˜Á¨[˜´˜4r¯ìÖþáaå¥þ~ܳ%?Œ†‚ËÑžTƒQÕ¤4O/Rz§7äuðpƒ£”Ž·64äBQüÑ¢šH^«H‡wt²ð’%›Ð±³ÌGNŒøØÍÔª^‘ŒúÆŠÆ>–h½ž:Ù÷pÎÀrÜî” <#NQ3Ú‹¾¤'5züs^Å#ëžÎ¼ú“¿4h¡†1͉‚ðk#™zn0“)r$m‚\ŒÞ…óB_ÚY½uèZ¿ÈéÔÝrž˜SyšÈ}Ü~‘a-ÆØÍU凨ª|#§ +ì¢ +JüÈ-·Œ¢ n_¼Ø«Úy½0ôijÈ讀౮® tä´0™çl’Ç%dS© +S£®P"ùÔóøž“lýŒ¿I<óe<Œž¡n—B¡fLà~üy]tªŒÛ&Èå"«Ñ7Í„¡ñgRÏ[OKtk[ªÍO¶ �#<vûÆ}“¢uvÅÛÑfAÛìbwògHAýµLbÙû±H-·ñ2Z•‰´½m|³í„oè kS_L7h¥¬„e}4½zÄËã Š¶ªùžÂ1Ì€ŽŽéê«iœÝm·T‘ÊGÀpÆøE§ðµyHïx5Žæ@Ìê#¼}ÊÜ«.ûØ,DdpêEÔTb˜ãn‘ÌS²0ÍÂJO6’â#¼¸SÊE¾;UÊDßÏ2Š–Fͦ}ﬢì²Ü§NÿƒVAU¤‡EK\fÝ:–Zºô]i q?ïoƒ‘]vÉ_ÏqÌtð³�ŸmH6ñäú£½?ÂÁÆjÄ·FSóPU\*Óæajyà• <Ý~ÞOê<îð¼_–eÓvnWFQžJÐ~jƒLφói»A‘I \>µA…„·7kÜ…4T��at“¼¥G!Uÿw6ðú#`78óòÅñtË`vhÚóg,„FäS"4¼°(–AD$¤L¢ïŒá&mæÍwë"Z`¯w Éæ§Ol¼q„³BèÑ#1”@þ´Ù(õì…ç}–÷¶3v©*U*rRõô¥× &Y³•ÏîB_lcRæüË«¨à^Ê¿OLþ9èïs<{o²ogÔÔ8ìÜúéRôo,FM]\ëÈM&x£èO‘ò‹ý¬É]m¨Ùò‚Ši÷›oYŽ‡ eAò£^‡,†Ëû¼æ¶¾°šæ"ÂÔ#ØÚjâÜ™ ¨L&cÍF>;f õ‘î.ÉY²Aí¤MQóÖï„.kjŠmë‹>ƒ|9ÐÕ4ËÎO(ÜåP›~©cãÙ›àñžìƵ¿I_¶â¾gA·‘Ü®´Ók"F?© +eî+w0ºŸíÄÿcÇÅ{ LÂ.ŸÃ·�yÙ¥@ÖI2J§4}JÓ/°Îì²"êpËPdµÇ‘¿NW̓ðìÿ›:utq�ƒÚ…Üå ,ëê|kྠo· hZ&íôùQ`Å<_y5eha>u!s]Ïž¦=NZËÜ÷óKÝ¢K¨ê1¬fqè|ÝÁÝ€ïW’3ühn«Ag¡ÊŒÃ¾ØåSš'·ÿ]ól][2pÉŠ/•#G_›I3y5y~Ô‚©gÛ“ñÁ•u6nSÚöÂÊ”ieë*¦\dÇŸ>Pä�Áô¾5]Ž6ñ"ݤ—ÀápùNCvLù¼=zªd~%Rø[B[¥pÒÀ€hþ6æpt~æÃøúOÜν¨f=çcGT^'¨½ª¢oGÅõÂSŽ$rLÀìo—»¥ìŽL†½Ôq¨÷ « o;7Í]áji°rÜäéKªˆL©ËâÏCˆä5ì¢tó´¹Rw4âîÊЋ.‘Xêø»ÝF:ýä•^gÎxw§Wþ úZŸÀÌòp–¤}r°k¨18+Ä"ס…ú‡†JÚB+n<Õ&1¼tŠþ˜Às·á:>k°l£`1[RJx?®%‚}WFÕŽxêèñ‘¡L!/(–¯ê0:Ýžbº3çÔå“ââYƈst#â” Å(•¼âB4Š¬Àµ|ÜSÀ¿PiYjHQöv0=Ú£ë6ïE`GAø .bŠªaQi¡e&NdË<²T敼Ù^eÚËoºïÞ@8ÆKûÚ*Š‹µ2ŸO½ìОŸÛ_ÈÂdJ¦ûÿôÀ#™â@ÈmóT#Ï6M`€FsùŒH÷;D¾ÑÉ°»¢' %9_â¶$Ãí¥‘@ûôR1Ù‰|àæ¸,ò)wŠæï°ñFY{p¤íÁîvåãgŠ#$÷î.(^Fóì*8*%ðÚ!Oçá~^ˆlÇ,AMµït˧5“‘uö/cG]–x·}l\Ê£;•Œår%r¶K¾�EÓi‚]®§4}¼KYʸêAU(Xþ%› +¬'Å×U«À4Ç.:Dçu–Sê¥Á'Ήy ×TëPE/×|™=å‚šiÖ. ¨iÅÇú=|¨rÜóCîÙ[§†Pæ½M¢Å¯À¹ Ïâ¥x.F»ûbt‡Í”y[äH*ÖUã@çõ™ IdÉ.ÌÁf~ïFšþ/sxô{ÿì+qšÀJ'‘‚kW$³”||ڛѤ…ÔµY ì2Sç’÷?N,ÆK)šV×Ia{%.‘¸o°wðÃzoÏ«ÙøA ïI»£A»Ë.ø,سš}þ];Ûè@eÁr<8=%¸©é4#IÐ{‘‡ +%_}<6pjAy91žc† +ý„.c0¾Ú[Ÿ„ŠáG^_/R©Ã`Ë1‰ê¡iÃW¾/S®ßOEAœÇºžá0ª]e™ÒËšLEýÉ„ŸòÙļBß ac“Œ|öïm$¿´{(qNó‹;ÛÎt[åhù·™`ÃÚÙ¯6ÂCÇ7šýÝÈîç9Ù/|J&£þM+÷µ7Kœ{]{]¾@OåM7?Ž<ò|ø©HÒC(šÅÐZ;!|„Ö›Ö—ûŽ®ÄCµm0þ1†b~lÒæ$J™Ô/ÒH(ȉ¢õ^HãûJ²Ûš1õgc +õ6`b¹…i×@l¶)èžxŸ¾ýŠ3Ò,[£M€£=H !¸©õ^ý&Ècö‰;JKIÊæ¬à¥íNgñ`\,˜ƒ—БdjêÝèšÚì-^\ärÏdë@¤Ü.pkÒèŠàxs5çNÐN£èbé«q[M5~+-ûl”®ýð¹?ÇÓÏÌ“±c®]Û*,¨XõÝ{àÝ–¢Êû~: ˆÔ68Àxƒ:c|uÓ#%…#òexønw1w½ÀSÑÆ}3“ÒקífT•¤"÷cçF:cü½i¹´æi¸ÈÆ ”¹]Ü‹äèKj¿ +ɯaA7ÑÆÂۛB„Á‚º‘„ ¦Åò_îÙ(ƒîtºŸ¡7D.Bôy"«¥¿ó%¢Pó~ ‰l;×’éSjJ»Øð%€lìEd€2-O溦σãÏ£$ëYÚQ½�Zh§&Á´°ºïåg“ûŠ�{ÿùÃûóžv Ù/pþ·øü°ve£ ¥°*/a¼B(Þ®›[T9¾kÁšÈj(NZð7÷BšNìéÿBS©88?qJ$ÍàZk€~À£mBÚxœy/¾öðę̂�ä`Š*°sH~°ñ:ëéìÖ»ÕŒÏXRønÃKT2.+š,_TŸ—óPÉ„%m ôË€Nøऽ‚.ý> % åJöRþZ³»ÔÕQ1¹Ò_ÂHÝÍÒ[T&ùƒn3½é••ï™f }ÇȈ3š6òköiàîý¦ûB‰cÁuCO+Ú¢ÀlJÅ"a·7o9.p /ñ®…§Of¸ïä_IÝo—¾žj)Ðvix’M8ôdICéŒt˜Üð[c¢@1s}‚þ]\×säºvZZWK]8õt(Iª<�÷#£aÀ3Ń˄ FßÄiì+]j6EÒçn›7röXK³ÅëJ»šù>*–[Ð2WªõG…ÑܯÀÒ³qZ ð`~˜°ÒÍMÕÞè/O¥¼Áôå5bì0´·üpËΣSkg®lÑzo{ÊÌåËÔ'ZZþVæ)êBÏúå÷É5 `3–– ÿ �}O/®ö_tÆð’sæÊ!0Ä° 5Ê”±„v°<¦@gæèjk£�Nÿ(oÄáñ»òJ²š2IÕ…TÜ&-êM\SjBñôÙš7cA„P3»†6²›3<Ûã—丒Lðx.+9ûm©Ÿuן<@Q§åšðpZynÆîH+˜7HηQ`Ù£´u˜%Bj8ðhý˜ÎÕ¦f¶A�÷MÌÁum}¥KkKzGï™`‰ôyÝY<DÈúÒœD‡S9“[ÆÀçþÿa|”èHDŽ0uúêA~Ãàáâ‰3CÑLè½02pÄsbЛçe³/ƒõêtY`tHO¿Ïi5<Ý+ÃáL…Ù€¼ä¬hõ—dL&Wv½ÜûÆKÝ£žÎéoª<DÖu‡ä¼Š²´× +J+»@Hóž+÷Mg{#´qéö‘‘-º&ªSòªê@¸r$PèpZš6Q7ôÁd,š\ÜPƹDWRn“Àä&äÙ] +FéGÇÕp‘>¾W÷ÝówZ tj[¨NËòóœwàsóEžÉÃV-ëWZÒx-c†z£•õéë‹=æžÇ ùæ`¤¿ãš‚‰0ŒE*ØÈÇþ¼G°l]�¤E=iEÓ¢n_–ø7¦ºÆ¹!x s(ïžáŸ¸c3xÏÒ³[/˺ܨ¯!tÏÚ‹îM(S¾5èVW¹+q1å7²09|ÅÅ<o³´°$’àÚùTêM‡Sœ +>‡B‡Èm5 ?þ»ú²1XÐàaæLÞÈäT°vŸ)zÚwÇ¡`²Â3v{0¨½ÔäBÁЕóXÏž 9—n[Ü°í…ìçó’H¿À$’rʦU:쥧¸È¢wvZ)ˆåÚG÷àþ„t†éãÑ¿lqt £f3Ô>÷8µŸóœ"ÕP‹@Çø¬GdFtU¿21+¤òÝz2ŽR”[~*8¨ßwôäÅ©#kÅË—›«!Ñ Ë©®2o¬ÉñqÜîX±9ödo'}pze9¿dœ¾¾óøgf6…,N(µóú«…=H‰æ)à1mY³ñœ:ÊAê�?½O†¯75HðÌt–Š ”á‚ßq?Í©lZûrˆˆÑ~h^ßÏ—ôTïêK†„̓fFé4D¶Q~“æ÷PŠraïèxyE ñXŠœoü¼½›ºÂHÈ%2v_’ŠvfGÃΕÄ;§(ZQw0À>NÃ-bLRMe¹úÁÕÉ +WŽ=pÄ“½°z + ·¤-•Õ¬Îdí鮟ÕhËE‘B#F}ô<âc„xoH2†›?¢Pß“C‰n*ÏèLož·rmÎðž~°Y/=7¥*YÊÜŒò×ït-³ÎõÜóä£Ç‘,A°öÐæ.BH¯š*Äÿ,æ@*’Þfk(8y5åÏyääèG ¯Y¹2²œòäïè~ˆÚ‡·Á½|Fà3–. FÀѲ®RÅM‰w¨´y e°_C*¡)Æš€`Œ¦stŒÁÔ³iEæϬ¥¼ÓÙ£örg"ÞÚÊÄ×1g™¼m¨oú]¥ÓA±0–ˆ¯ÍsIш¡^Xèwº¡œJ‡O:&T÷7¡�wõèÎñ +!Ë~´„«€.‰Œ¼ÞÜ™è<12bÔøÊ|‡”ñNi©btq+à»?dW„ègÓ@¢f ¹Xà؉‚�¾“Š²nG¢¦¹Œ¼Ðб—â˧,›ä{¹møHÖr×=6\R’õPH$JX*f8,ø8Æ–ÑÔ^## Îí±ŸRŒxÏ3;Ìäµ9Á`æW¯vÓWÜí�ôƒÂTà¶v¼öZ„ð©P°¸dC›¹…|¼iú¦ãy Üñ$/ŒR•þñøqç${Q=_à— ¥‹ŠÞ³P}¾Çj Îo]âE=›5l.,äÅž”ÙšªsÜ'`ê qRŽtÜn’u¶Ä´Õ“fŒuFw×V¨•¡PȦ0âS-oôõázÏ*žŸïóýG÷÷¾ä| ½YV÷-Vè9ŽFˆ(m%ý™ßU:þ¡Ú4¾Bføß�aÃêF&¡ JYËè´Q\ŸÉÓzSÓ`9K'ÕÁìxf#â…ÕM˜ƒªWÛóÿgˆÜòàZŽ~%úûÔ€Ú¨¦Ì>>¡´G"áJb*ˆsFÌ +ÿÉ& 2x›¡Ô ¹í/ ÿ£ +9R0þkÏP.Ï#|âÑ¡Û>öéÕ€±ÜÁÆgÓYã7•NoogtÈ#J¶û‘òp¥3é¯8¸‘åhÄÀ®Æ:Ùï¹)<1u³?'ФežMAö{�⇠«IãWW×ç%È÷H(™ælU;?üòÏhçîsÜÏ$ñ;xç4,ú¡möÉ¥ù¿Y +ykØÔõz‰ûù¦3×=ú]’“5Ò™R&Ï~Tß¿?¢šŽ±µ•zKkƒÓ·Öùcæ_v¶•r~×Oì³Y©=©ŒâV5¡Â$QCHוwá±6v¢À#x]ÒVÜÒ»fÍcg_¸HOê3ÕkÑ÷"äÁc–¦/€ é-Óx¸Üow‚Ä‘ UF’q¾£,¿“ì<ho¿w><|4Â@M3Ý/QքƆN&¤ÚÆ£ïÚp<À£×ö^¯ûRKâ\úàoš›ƒÿÝgßëû¶ßµƒ DnÄPô ïß Ž4Ô“nC-ñÖ©S²ì]§ñHä?WíõÄ:£cð`úmH²—¡~ðo¨¡2™ÌÒþM®`õ1ô“IFyôh´Ô³…Ü‹âÚ}y¯Mp$ÌÛšš²„ûÃï,&lz<'ÛÓsˆçŒ^¯luÌòž¦=mFT%öÆ]óJ]£u r52¼¾]ºatüÞÏ7[1K’dP0¨æ,gÈ–iO%RÙVä�IH8¶ñœ%íHÏ¥�X1ê'Èÿá±¢˜( Tý¼âßÁèt·l«èÚÖex!ΠÏ̯ÎË2æýQÕúѲe˶#ÁÿW{¬üØ`{–wxÚYÌ(Hi뙄š„U´¶¨É™€žýï ‡r¾YŒÃ–.Ëfžyļ¾ÀÎÐ#+Vh›ÃOB†àÌÇerö§ã{%Q®BY«eŠ‡Ê«Š/ýÂpˆQƒìüÓµ¾z«×DÓ²Á +Èa·�Ú'?e—`8CggÜå0¦ÿžHS¯Í»ö‹OcЈq×F&§Ä4;J£¸Æ\¸Žïã-ˆÑý(Â9"¤"“Aƒm&†Ð#÷·ñ;úÅ–õ}wÍ™3|‰»8ÛŒ‰¼6qra•I³×¥8+>ëÄ@QKˆòûÀF¯u³œh� +Ö„N¢)4¯ÿ,aÓN-¯('âã¶O«ª2ù¿|òh9׿7”þ)Ò?ßåx~/B.‹,øS¬M +eüfÝ£ŸÎ×JË£†v-';ä #™?€\²ÿŠåŸÅõmtI^Bî(Ûá«h˜€[€öÙ<Õ¢omZá\þeœ½ë:ÉÂÜÒõH.nˆÆ\¾œð@5ù(ßûÈm XøŒ(¼É¨“@’Rë&†—\ÛÕ5ÂLÌ{5«ëw2SÒ“é’my +jlv©”£Ãmž@3õŠi>rßÉ �Çé 6S¨pì”鱓fªwçû5P+–Í3šr=Z.Úë与 F—úÉÒùÅ#1kȦž!çÐDœ‰w/ÕŽTKJUðº#'ÄR¡AÒö~ï?²ß÷Åø2ÙWVŸ?U°”Nf?‡ŠüÞ0–¸{gÝ@x/5HSöÀ]}xOTé<¾0lE"¼J”*5‚0 +ÙÀ@¡5F[R¿lRŒÄw�“ZÜrèôÂ/Ïše\ÓÙiï0”OôØfwŽDb&t3³ªs†K3Ö†@×À$C¤Ý–¤6¾ZÙ› öÚŽêûBV§¯Ñˆ;i*xÃ1ß º!óC4Ünº (•X,u +EŒÿô2ž-µg/ÉçV_v™#•É¸¼Ýh˜gA…î“GË›LW××ç&xâ»—;"Ïj–q5O3aµ™¯îŠyCC]™Œ‘¼0îÔ…x˜ +¤"y#n0¢þ³Çïå[70›¡ÄW㯬m~ý +‰‘yÞÇV0í¿Œ§@>bµÐìzȳnvúÃå˹Žþp�o{ÅÚÆJ3¨×œ…,„e¦Ë.K…XÂ+k} è¹=!ph|aÒmñ$¸ëd¾¸õHËQª©)gø±� +y®ü¥¨ÞÍ·è¬ó+sš~‚4Ðá…••oÉvFyF1 ˆÑF¡D3Ä߆òý9ÊÊCÝ1¯"QÓ¾.¡>&Zü\t(j³hž½0¼“6e��@�IDAT¼ŸÈ†¹!%2é€ÁICº^~äÅݺT‚KXƒZ�¯}Í{Uæ÷twr}îÚ¸• ˜ém¸îÄ£ÃqÞ— ·›:JÆXp¨‚@¹œuãg6²ào=ú£Mkð]cr¢Ã‚ž1 +2 +-äPn²vó<ÿäò¼ÓXòU“â!Öžê.rõ?´¯…*=}G÷ûA½î}ùÑìÅ·ÞûFŠ2g{œR6ëÓ†µ(¢›qÝkÞká45~*1¦ mÓ²èy +çÛÿAóÿä8çÝøÒ·~á¾ë0£ËPóiUE³‡£4Ï›½™p�:_ïœ1›zc!ÑbÉÊÆÚ<·;5ÜÙ‰\ºÃk©\!ðšµÀ¸N0,¶²2°Wà´[Ð!Cåv‚N4AÙÍ4Ö'3êðʇyMK†¥ì´³LM6.&-½¦;Ñt&ÌÎf‚[$ÖûuÅH&ÅN•¤|žãæ"‡ã¹Þ¤üL¥ƒÐEþ!XúÛhSìØqjø¥LÇèþÈòúwœ¦;@ÑI¦”ç +;$¿¼°²1¯ø’%•ªBÍhç4PS#‘‹[¯÷eûbonΰÐHÐøl㹬ÚÛÓÜ—xOÊD¤[ +¡vq‰û8ÑhÍ üTݘce¬+Ñ€ñÇQXØè(ú9{qV5€öдF×J3.ùË«ÇBá·ûƒN6ÅL×ã6!&Á˜ Іþ[o‘wàÜìëp4*ó)sêS½AÉgRÓº˜Ä±ú0�m%&‰¥�Â÷æŒëÒÚ£0n'‹‹‹Bj¡í23^.{ik^˜û“x‚vÉ~yÍÌï©íûöSS]-h[mU!é;2d”\TŸ½ð쯵7îÓ%v sü¹zÉ'}Ë67|Õ4?dpDHɼf—½OÎË£rÆ^CQíTKË&Ó[‚“¹^£IeîYl÷®uÙf{}KdÒºó"òø¼ºôFP·¢l‹§M¤p´®tÆ×ùÎ%ÿó¯O.úâW¾Áêæ ˜`ó ÌŒ*Ö¤.ª*ÿü_^²mÁwžeË ÊF´Ùÿ™ßHºy0º«¯8EúÈNÙ,æñŒiyõäIá¡!D,ù~8·€“«† VZv^ÓЃÀŸïŒÆ‡ãÝy7¾k00œ–ÎÚò_I¤ ŸÍ¦™S£H’÷$«1¹ËÈUýÎ¥(F™»4§ÜÐPY‰¬…¶Qj¹ãÃÃxˆØôÇz¤R4|¤Vc˜:à<‰ àÀ†JÁxPözs]PŒYÊkL8®³1–Ñ—�¼‚×¥ïc¸»ð¿EBx»-�Ù_ÅȺXâ-ÿ²Ø|ûr@°VSÔÑK{¿DFñÇM¶ËnÉ4PEfËó,ážg„#+)ŸEúrŒmŽ‹—UMã$n)Äû>¨Vy®T‚\ ‡3‘ö°®4Eå‡/Ï>9šÏwûL†~EÔ€aôãâ';Xößá”䡹,ƒ‰ó¼ßiZ»€²çB¬É.øôÞ„d7¦{¨Ð‰´9�Or6Õ>ï䙸Ãàö-ÑjäBY >zxSý}p¨cmSé»qš„/→K„$@X\–ý½W@93‘7´¹[áÚ½ý{µs¼Hk ágÉp½_vlªt"c;zMj*ê=öf:û7…—ÛLú“‚H¯æë§ÿp‡’„j fïÛ|5æÀèwÈB¼/œ\›È�³ÌËÐ÷ƒ°²UÎôl#såõžHœ¤vA&ÔâçYHŒæpX1óÊFðݤùÁ¤ÚŠ?Ç‘ù¶hÑ"R°´Š¹+Í0å:dž-S§vŸË=zNµ×tZ´[F<å_O€µê±¨ P{Ú£'líöÞAY`ø\N4\xŽÑzûÐáVLã¹PÕ%Ÿœxkä¡åR6#‘a»Íîd °ðš›iª©éoH`]õ¤ÑåbÌoćûº{8ŸXåt\cÚÜš‹¶™qE½ã²Ï|é;8ÁÜ¢lˆŒñ®Å8‚¾$‡ƒAìÿ÷Îÿ=Ž|Þïõ×_o®?Då†4»ÍçÆ‚S›¡Ù¾Ö!gçÜRÊD)Šµ3Ëvw“s*ÃGš›3Õ@�Œ=¹8I^ò#i%Ïœ›’«9…ÉÔEµ¶nØlŒd@™Vÿ`'ß„w{ÂL÷j”ÄGºvœ¡I´nÝÌð¼°ÛâS¸!‹xå[VÜ[êänÑ—B¾,ÊñÔt„å÷°å0¼í+m=¤›bù'ÚÂÍÀ•e9PÒêǵÌÊ|»IŠ|5ee¾ÀœÅUœa^BÙO êS:;€×ÀÇö ®‡ +€5žUÇš%è±»›—”¸†ƒ£Äèý¸ +ñÆ¡ð3àžEŽ|QÈîX¡Oýñhd •ñ/Àß´tÔ$djá=ÿZ,kæ'EE‹ìîïÎï){yìp³‹¡>ܯqŠ. +&ß €VÅT×MX/Ý5kVîû£Ç%¯Ä0]]Q íæ's†š/±(2ƲB“ß +lÿËETveѬœÇ;þ{«U) ýinyêeãÜBà"xÆ[_uQ}} +U'@C¡"7|; D÷È'_;›š¶"œÓ‚Óâ¢Â+Ù“ £>¥0)Aç1oÉ¢yäíÁ˜Y‹ÛµôPÜ“ ôvǦ„u-1"1úöv¨tZfUaâ,€JF»e(›G9@ˆQóÔÈìçÚ;0‘‚kôhr¢äA¢~"4Æ#þbç!ü> 7~ +Ž¿ÔÌ{“Ž²ÍwÈ’ TÆîn’Ö;î2hñ‘qØÜ™KgÍàkUvÒ¬“+ª-È'×íÎA>m¶Àÿ¡SÙcëBÒÀQR²Þü$“Š§i-#JTÓ:§Y÷‚êhqýÐöÐ@Ý$ÿæ”…¦Xw©×¥Y5 Z_ÁYü_“š¾1`ÔèIŠNQJ¥1½;Žwí¿«©áC•ï%çy^ÐÄ»zÅ—ËàçC•Á€L-˜¬)ÊÇÒ˜˜ÎÒûl¥®ý³^É”À3¼ÒÒŒ!ws½‹¢„ÎX]Z‹uø í¥‚™´Âã±Z!œZQÊg‚A0Úq/¸—(dןøåêyØÉdÚЛœ&°ú£Ë|-pn߸zµ9íö/Í�É ÖûAƒÍF2ïö)J•ú‹Ð ÇÂ`ãŸÓh5ŒÆ†{á-ÝáߎèÔ¯¯*rŽ :O×Í™ãTíÞOÊŒ0A5´.‘bŸœoF÷Ó!™3(VX «Î×ÃØÙqBk8Í|:¬ˆ{§ÔÊ# <e›?îgà�žŒpyó®*ý!'W3~{ýh¨ÈîáŸFÓÈ|•G�Å¿ªó®Ç›T²0XI8§=Å =#×È÷k“¦†YÕ¸9äf<)ú¬Yý¤³ º‰èŒ5ˆ$okˆJïÉüà7ï‚Û|u{¯|YÔˆë¾8Obœð*u›,>6’J/22Êk-cÂCš3¼dÁ‘ùRá³gºáÆŸûø÷ä¾süÑJ»Â½²¬èÚCàÚ}øµ‡J^ý/_½ä6e–®í•8æ¤N1ežƒŸºé”ÎR™jìA‚€hŽ~"™¶~CzÔ!/ß…!@äÂw3¯_Ê(æ¥GSŠñ;J7þä8£áþÁ¬Ø#é ë1¤§~”(óŸ•Í–„¢ÊgDwØìIs:…Bîö°èP›<ÔY0Èñ×ðQx¿a^ÿiŒM²3[#CVpòà[6nÜh^ö…ûžâY+™M«Öøzq“r÷‰<¿—øê|8O£IacÈŒ¯ sô]ÇüZqáM(\iýÇ÷þ‘<Gï×8yCü&øý õvøDÞ6=+=—”zà˜¹9øfwøß Š +îaªGC«,žûS¸„WCÛ׿©®=Wàù8hO—aεÅìo Þûuîÿè~Ï‹ç{åÊ•#ý–`V¡ +ÆPv¤ªÄŒ*v9A2ÀFC3mp€PħÌþ¡¤$QVyÇÃAWÊ'éRz$¥ˆV¾[t˜iUóŒMfR‚Áó¡t˜¿ò¦{ÐmLåàj˜=™Ò⟇2™WœFtÏÖ®¥´.ϳVž¹aTi)TZ”(:¸gq-Bøõh‰ën*ôŒìŽý +Uó‰Pã¸1Ûɽ±v“3R�²ùGb½,5¯”èÌåŒWžO(S²ÆýàŒ-C?&$L;[Fú¬%áð4/-£bäZK!¯²Ù¬›¾jµ˜æv§ÈËÑ]’ÏnK&å îè?/ŒÀ{ü€þ\¯Äƒ¯S@{ÉWŸ ùȃ²ô‹÷ÙàY#c&k‚µÔÞ§>Étÿ,¿°PîWíF—TR)°Ž×Ö"¬ÁSqñèaÎzm`4—ÂØ®* fÒ³0.'º´Lý”¼5ôŒã¢:S1xªë½HbþÖÞ«Ú…Ö'~™ïf|æKv×ÀáŒR59À»Âš]§ÀÑîùqždϻђepY1ã·s±Ï4°Ÿd9 c¹�W†f±W£Y¦£¬'»i(c»)ó-‘Ðé{6±Í?¿ü³<g¹UŽ¡!£]$Õ°ñÑXÂ!ÂáwÂäë¢@/gE‘QC¿·77pŠxʸ‡V[Pá!Øi)Ë¡Vúù¦±W&e£‡1V›1@c¹ë³ë#ðCAÃÄL¶×³N=Î.±d©ó™;«-²._ñeÜöFÙN÷o*¦b;.ËZüÙ/Õ"zü6ÖµBÕÔÊÌ8Ï";«n"é•Ý5E^ú¾ŽËš#GøoÉPP·a‚ÄÒ››‘l:ó,ƒ~yZÈ?Fº?ɬÙÒîðóåyEs纖——GÆß"» êPÚ>F¬a´Sc:~ø¶ób|S'zNgjœv³PÃÎ0ì'XZ]kÐìEL,vtV¾]'|¬Ðš€VAUçŒ6dä´ˆ )ÃœsØRÓ¦Í&‡¥tÜÔX·•…�¥q ôu|6ÃèNÈcªv&)‡x#žoʺ삉á=õìbÊ5’íK'C ÛÁËåeyeE–dµRÔfÜ@ʤkå´fCõvïpŒÈªÃƒæ¿ƒ¼ò ip¯ »ÛÐ Wo�‡óø-¦†]’¹‰E0îYQ,þ&® yêk½ÙÄs«)gbÔðOuc_dÔ7hí+½²CúÞèåÓ/A®û&ÙÜ,úé6¶i;Øáç Œ|wî…·ÞU”�!ž±ˆ+ˆ~.vÚ駚fÖ8Š¨ð¼÷ÎùíÝ{Bw.xÌí”/Âc#£«/Ý4ÅGàj䄦&R‰‰¿i ¾z0Ó,rßÈÑòCºƒór愾ºòíc=½Q¦+ãE Ö™«Å³vÁ-î]ƒð‡{XÃCªÅÛøG”lèɦúʜѶÖ,'Øßç]6ù°ÙÇúå6Ž|zïÞèUgÐùÎÍu—º +ó)›`Ð-)EÕ‰0^²/£àôðâÞ¹!gâ�jÄ’'P¨€�6£ÑH—‹ý°]€Î+Ô; R&®ä,0Ö†"ƒéüþ“AÚ†à‚>³Æ3R;£ÑlžŒ€œO™Z¿C`žyËa‹Àó!õjû@ô’>à]¡o7”lÉÛ]‰_;<Ž#qß„ƒ€Š)E$çóS:)Õ%¸à�¯Ù;¯NŒÍû8Õ`à3óèe²F! ¡÷Ahî¨h’ñã¡|8�dn£Ž¨¶‚Ûû¯HõmóxY0ä£yê‹‘¬kþþÂøÓÙ Ê=`]aØš¢¤·á½ašã¿óA¿?/Æ—éo2]3@ØõY%[…|cXB½¡É›Sjl›ÙS<@ÕP–ê›(´q|&}r±>¯7¾¡¹y't´ŽZ½ó½¯'N8i"5\ÜÑA¯„E]·n=iéRz1UB¾—[Ɉ‘Àû1Ë÷cñ¯¸ó¾™ÀóÞi)Z9cˆŸßv`ósfÌÃsÉ}Ú_y«á¡ÄªšÉ*mÈ‘Œ ÊîÀº±Â‚¯$kkSc;÷f)ò³È[?�÷ÖLvF·Ê[‰‹<+55C&ÖžŠÜw× &NŠAâšAþ×8(ŠÒöy…ï@yÆí6÷vããt/þÂWŸC=à ImY7k½ñfpxÄ*u8™‹YIúr»S †î\èE /EŒ0‚ˆ¼sw÷ßþ|¦¹ ‘ÁSÀ€ò„Àš(‰P^ï¹B¡ø‹óX-xKEÅž¡8U‘Ò 7ï‹-Ùvmÿp¤Õå�þÓ,™ +ãK*È€8KPá>Ž^r—T{�~v‚;ÇŸÔÜ_ß‘plØy5ñ°Ãñ¶a³§–MòÈŽþ +tb,œƒ†C—½ñôà+¦Ð %ƒ½LQ‰)±²Ý†î<?šòag öÿãîMÀ㨮´áÚ—Þ[Ö¾Y’-[òŠ7lÀÈì„&&BH&Y™Ì7øÏÌ$a²/$qBBB $a36ƒe¼áEØ–mÙÖ¾´¶–Ô{wíUÿ{[–±Á™çKbL=–[ê®®ºUuï¹çžóž÷¥+4øú³c³µ0&Mè%#ŠT¨t&Кò¯y€rMÛT—í¸ÀǖϤíÀðžÑîÓÛú^øý!¨:ÿÇï÷¢rç%Óx Óp}8™4è€÷° žíÎiÖ„`ö "x™Ì 묜Öúz¢~„p6 Ø%äu‘>@Ñ;ÉCµpƒˆñ{[7¢P±g(õ"ˆ~ƒåÓm]oät+äc{PJÿ,©èl® ½T D?>ž˜ˆSŒØÉøfš·ÇHŸ%}…4’ü~(‘É!ü};{¸[Öyokëÿ¶ƒŸãK<¼ŠÚìÈô9.úˆm~å¤[``/ò³¾–ÍS1›š°KÎ2¶H×oùE?D%ð¾Õo»Õ�YÊåSXÏɶfÍšü+ù/oX?yo�¸[iK÷‘(¾s†7y�Foí'?‰À®ýQÌ—+QÈÃjšvþêÅ‹·%²ÔÆÀK×/ƒA `hYj@;FTÍü³ÇM?µ¼¤€ÄþòòÔIßøÅ©Ÿ.¨¨ ´m$ž¦¦œ~@‘oðòF´¯ç}·~ºÈ' E‘ÈEˆƒ#vÁý æ¤z)Êó–äÕô¡I,{wßä3š(Ü"R6áeX†°Æ?e*‡Š%¨ÏÒsYšµÕ±Œ„{¥&#åÜÓÇ È…³Ul‘{^»Öõ™¦¦,õÀôÆ›oæùPHâÆU^5CJy•XøÃM›Ìy²l!öF£`ìæ6åH½ïqTŽõ)’$±tÐHã%§Ú?¨›*(”]€N›ri¼`#˜ç2ð,Ç}†Eâ‡o6¼'ÛëŒê/ +ÕŒèm8øpˉEhò»WôLŽ%6Ž§_"ÏgT�xÏ~}ì~$`¦bwõù d–¨Ä0XEѵƒ“—˜ŠJJN:rˆU~!qÎh.æÎ÷‘’üà¿X… ïâÑ02±¹ÏNÚ‹ôäT¢wúV¾ç^És?ÈAÕdÒŃ]tte3ÄéÅÅXñdn, +÷·vWÍ®Ü-‰ÎO@‡§Täõ'¢Ô#óŠ]¾®xÂz…Îá5©d¨ ¨8«£s®nT’SûÏ]nÓ|š/<½ÇeØ™ÉRø°Sð.´”é{iŠÛDsV:¿…ªÀü“Ä[ÏpíF‹³¨·· +઻¶@Cøî\5óœ‡9WGjÆÒæÙ*Ïñ¸F}ƒcÙŸƒ]j®(pwMP q<¹MZÉl"câ“÷6>ùäÔ@ELvFp‘UTµ‚pçNï¶×ƒPÿ½üŽ/-ŒíV‰?ýÒ¥sHxìTÜ141˜×Û2.ƒl³O�9ð`V£Å< P®;¯Éƒ9B™\£/PO–#7Ú•¹*<>Ïï'±£¿dxÉî§oà‰±~0FpLŸ— $úŠËï¸w±)ŠŸ†ñ¼“± È×tM1E[žõò"†ðôƒœþûæ¾Ã]H|œ@«ô4À‘é/â&}ä1´�$¿Ãó÷Œ&C¿»¼¬`pÚðnhqmî›ñé‹Þ_ù“ý½ ¡À‚x�$ÞM¸,nüç‰^þÌñÒ·~vVÀ_Ö(ç¬ó4ÿPEØ;[‹Û«Ÿ??lCa™cŠÂqͨs:i[vùHÜ^ äFa†› in?ŠM*¡ü=§s +1\â~ÃÔ~]X$ê(”(]¹ºŽðKƒ†8×÷¯—x4óüXk/Åb{ï›Gbÿ.¯[®ÖõüsX�ÊJŠ5îiOõº7‹Ìûɉé²ÿÞÇ3ÖBÎ +ê$±«*C†c?gÌÁd¶'?éƒH3ô£+z5ïè&(Ó›ÓÅ{òw®s~ŸÐŒ>_ád•”‡{p!ùkïKDâšh²Õ3Ê«$·�elZ@Y…N#ˆ½ûg…œ +…R®ªò”.Ä=a[mDi»Û´Ù ¤á¿½Û•%%9¤èÓzöO +cÿfy‘¿åÙζ};0•_ gçu¯fØ›9:{k&ï(ý%tóÙ²‡9…#ƒÌÔ‘¡Dͽ«¥Àêöö¶üo;ú9%SoF®õ‰Êá6Itw9¶9†ÒíP¯Ícp 9ò¿<¹~Ïî‘Ì›™„zn¾Sˆá0gäÔ¹(J]Ž*¶ò½©Îš°;ïŸ~Y{F3&'~‰—Ë°:©@ìxµ¡ØîëÞóØCßÕª}€c„�iºFqB{edZl‹íâ5X˜@™˜¿Ä‰Å ^”Q±±U•!Wæç~a⣧…0Ú♹C‰Te6k›?ü¶ ÂÏ‚8â®>¸Çjn®É·ýá€+VW@!Ê®¾ÙÇqÜÊ^ÌBx22 Twàüå¯Dg¿R·2së—\ÄkÉØ/¾åÎM—\'ax«+™±I§ãÊÀ_0Vþ=0vý'#™Ì£…ñâƒtï ‰¢›¿üïU½ãÃIBrÞ¥«!?g€ñFcØ„mvìŸnÿ +†–TI¾E~Á%çëŽB¿´Ksè5¸?U(öë°x»TÓ핸§£ÅP’Y@…]O—…Üž'àµ÷ˆÕÝ!‹LD ŒÊ̵yFz‡ï:<¸rþÌQF0A넛ߪ03ÆXsyJ�ô‹’9žî-÷Hÿ#Š`½WˆÝª¦ù2y5ÂnÅÔ7-««Î'/’ÚbaïGúµ¨®orÏŒb_úÓ:Ab”zŠ×rCpÖŒ ……Ó}ÇK´%ÜÒ¾š]—÷Š†,”E›é%"#`îÀ„É;/sñù8ôi‡|Ïþ:aj*èÚj +¬*ÞTämÇÅäû*Öã’mÊ?‘Ýü Hß�£rrçkøô)¬ .‚êÙMð$o²?%OïøàŒÚÇo¿{Ø-0Çö>ö³I¬pÿ_’¿úþ‘•Òþ맞cÝ'÷H¹]¾Ù‡Z^Y°øÂ’—\Ò=˜Ž›ðÈÐ@rŽÞ½êáŽ||áEuºÃ.DBuȕ̾V^èËçF†¢–*¹Ùh+6B u;êÎB—kºOüÕm{»¾pN/i$ñên»þzsÃñ#£µZ¨cóì@ªùLOÒ!²ÔÓòå¡¡Ë¢*šyeþ‰qnõ¹Øa·xFöl¾X&{<®™ÀL?» t£PŽoW]æñÈþýæ¬e4"îAnS„G4©äÒ*ÒsÃHº\ +òg,Š2¥ +�qz-:‘|µ><õÀNo#éÕ³>â²k5Æœ;{©U2œÉȉ„!Opâ‡ê–‡"岜°ä{½é\@Äè +¸x!$×ÆPÆ…cvÚÑc]«k§ +à&0šÍR¦¨hžfì‹Æ3î—mÓ&‚peé_Y†›…kØ…¶>š¼ßi¼ú¢cr“^·×#é•Z.½8›rG5fB긋\ƒ1ܯsò¥ å·®å<ÂNbëÃkÆ„°Uí'3hJ:¡Ë€f¨-}–Ý`Ó~?ø5^Kì~ùÈ¡‹–¯dP�lƒ'€§»Ûûh‰ê:2»fÄ¿ùùQ}½iu5Ï2âÜY3«Šy¡–3¨JèÔ5ÒH•2n9‹ò¢I�ž‡YCíÚõÊÀô$‹îqÖùdÿøõÁƒÔŒòò ++§V®¨œJ¤v§ÔË$–»Æ¢–u‹©›rÉÓŠ»ÆÆŠrñµ¸Æ»¨ Á¸üafœÃ윘ð®ª,NÌÁ;}âþÄä‰ËLÉù=pâ†hÑÖO½blº Óû½W_£ +åm«N³©r/ÙˆKh/;i|þïX3—¬ü ªOW“ð¨fs?æ©GŽ&Û*ü%}ÀH‚p.UÒŠè<ó»o}˨ ys`vÊn} /!R*œ»h¡/ WÕy\ÜS%%ã33ñ¡¸f‚¶KXž™`Q[™,'³ª||æ•Ú+’ù4X/°TÌp‚·c<rŒ¨V”„l59`ö/Gâ.8ÊR;jå©>ñnz®ç$æ;}Ad™»o4ÝøJG$Ì ÒÇßèÞÞ?Qú»¾<!‚V;-æ]娫ºGXÈ›!,q*LNŒj +Ñ0}ìŽHjŽ( +ïÓ,ã7™Œ²åÂ*ƒëÄxbH'ñÄ+î¿ŸBåÚÞïßËgèóX‰ +ºáÂjDvcé@åÔ^xGOgèä¿è;P¨ŒÁ€VLŸâŒW„œ&ì22w�Ñ°äøqsbð˜/`tzâHLÙ>Ѷg“ˆÙŒÄÑ+é§àm,D³`\¡/i½Ì%¬NEД+0[3ŒÁêN<â%ÀòÎÇrh†Ì;‹Ñ½“9ë_tÁÎ~'»G¶N”&·ÑTn‡*ÐíE`%·StÃ;% ¢)�HJñXÊô F¤’Áe’†o__lH3ÌzAfc|N[5à¤Ü Î2‡Ün]Ö³¢žG5ý¨‹†t)gÈ’ìE]G6åt€‚Äô’ y�ËÑür92¡^ÿ‰Oø#‹.âÜ´Ptt +ýl@·ËÔœ“Ah' åŽm±é‰Hlø@}¶¶¶•Ù°á§ßdÒ_¦“$ä}»oëÛ‰úçk+Õ{a Ⱦ?jmöä `œ}\7è”Zo‚ì“?À±5ôlÕr àšÆÍ']Ž$ç€ÌÇLg²ÃÑL‘سG’ ÀÄ `Z©'rP9†Ý‡ë|Ïo½ µz<–}¢6ɆÃ;ì„ +È$3¸ø´ä'É)¼:’ìãœçà8ydiyE|Uáà¸öž{¢éŒã‚øk¡G£tê¶à)L=ˆ·é.)ÁJŸ,ð ©´2 K®™x=¶kËÚ•hh/¢·ÇMÕñ¬ûÁI +Éuëh궻o;¢Õ–Õqw…Ì_CãÇIòr2hæ^Õt®Bî1¨“¹£·éþ¦Ãž3ãK2û/D瘌Y+zÝꟾâdÖÿô–yAÇÙæõ}=ñH=x $Á궾œ¸p³1ð7<FäÑ7KJ)”ÞZÐdã+$Ψy}<•,!Xì. +½>œô[¼E îÀ‰ÀH =ÚHmS©£ßc{%$z>ãrSRt× y¬ðè¼Å€|-^|ÖNEªôZ'ROÁû\‰âÿ6 ð@: ©÷a¦õrÿÏ_ìž¿ü{¸¶~ÉR7Q¼ø¤Ë—"›7Ó´øÕv(б¦˜BLzÔ.k8H R+¼Ú/€dh%¨(ÌZm8æ2P-Ž`0ŒÛÉÔ«<¿Ìær™‡@¶¬½ ¢$\�ˆœÀZ•lÝ*cEh›b3Vö@²WYcC6xÊ)d§‡€:>aóL•‡cw«º9¬ÓÐÕ‹&wuîÛÞ¸ø2Y}u ·ûÿìöÅFk\e‡““Û=‚2¥³üý€Ís9Ϊ@Ô\²ÿýw1€ Ô×)Ÿ, ŒSfØT39VòuÂzFZ˪™¤ç¢šê1`eíå˜U)j±…6ù匕J¾:Uî‰&ÜG&AÊéýG6o>DÔ£§Ûd +a‘×Á>†Up«Üóý´¢ªQÕt¡jY«1É¢Vk–íKdÔü{ÌBq®ØÖUWÀ-ûÊÇK ²Ùø.pÉ/Úmä&Ñf×YûÁ ÿA&±=1¥Š•ÙÊQÙ½±¨«ëxÇQêxûÚFsÑtÈádû±:ëGÙzŸIIëøòÓÃHÒ密™Lâ¯(ÉŸý÷2J‚6õ;´KÌKÂËaé Â0¯;rj²»H*¨R$~Í当p£DÀc¥¼cý7ìÍB‚‰m‡QtÂGùí‚B×èîݱ8dZÛ‰øÀôûï¦×sf|e(Ç +¬°ÂDvRä…¬r +ÖSÊýozøÕœ/:h¥2Å~þÚc“ÙãX*.Ã’Q‚Ù‹¿‹M¥&@!õŒeŠ°ü'äÚyøèµú@öÝ¢Ë!Q@ÃćÈÊ#µÃ°Ž@v®(óÐxûÀ®ö'»¯¸"áª\‡hQXÆ47«Û'"U”¾¶â†‘‘¿'F½,YégÀ2Ö„™w=*7™<³9ñˆkgê:îºØôî¢~avòÄy›ì÷ÉòF3=úâºâ’c'Ñ0h›ýî’K?]b ´�V¢ŸµéƒáÙcÐr MûÀÚµyêËæüQaÊ8* 0jŸW‡5¯>ÑŸ0â±ÝÊÞ½{ g¢äC£îÊØ1M¬]tPµ5W‘Äur"}Ô"ãøXwdç5ª +DeBN¨Ø°zC“q{@ §èÙòÜh<^;²ðêY>´Êè~>§Öë+Çì5€’_¤åIDH'G†zÆ-¹07œ¦3Ò‰çÕ@~NêG¨Â)ù¥“Í>ëKÅ*=ÞëP²½ +¤H%H‚"¬m{4ƒ;ŽóÏáK§”/¦@†ao‘ê¶Äcz ¿upÝ{á½ÎAׂ×î‚ =r¦f໓ɼñöu E¥%*³…âœ:~THF4—;¡VpÓ¦¤ùý¤(äíuí¦/æm|e,•…Ū*àüBcc ‚ø³o¶±*zƱá6Bó™[§íè€Ûl�b½ÛÏ”ÂÎW“žöùÛò+¨ÖÒ=ÙlΣ9+¤ý[<&ävõV^ô„iÖªRy0ƒXè„ ’$ xàœ·Ý^~&r2@mP_C41’¢ì|Ÿ>ÙH{¯oKƒÏÑAáLœ›m|àà¤Éð‡…2 “5Üìò+"cM-¤4ö´ €NAn~ÔhÓôM«¹ñØW„ÝQ”Ì„Å}qÌ/eYꞪUW]ÓÒÞî!_/V'ztÝ|+¦ã²ó‘0€=U¬ZÆìfthM9WÂKýt•¿¤èûARŠ›ß]^°O5©±¬úlOÛ‰7cPOkÚ¿./ RÐæ|XGVÊáصš`Í®%ÂÌúÈ«~CÑs?,Hû‡ñ¥¼çÐÜŒ¢VÊz˜ÈnHX¨Î™V’k.îëóYºÁ0àZAN(4' KýoÇÍ}ÍÍ¿ØTä><3Iïç7ݘE•…£¨¯ß™Ô¢GÎú£“ÝÙ½™jçâ‹/fžîž8ïÅèEaŸ|<s~Ig•¿ÍÝXºËEeZÖ@èCšUZÞ(Á¸äY4 %Ñ_–uý¾¶ÁxÓ:8˜Üôù×Ö•n¦Ò#xêªÃÒ"(ŸG©lƒR1ÊУ™<Šòà6_6z¤P¯KgEI™1™§Û‹Ø6–œÑI-?M¯@áJݺuS(r-ø\òÊÒÇçú"ž} œë½€n„M‡ÌÂCT&vðŒ< +€ê.dl?nØw¢ãôŒeàXéA/•ù¦f©ßÐ8jßBä�rKÓ"¯€j2„°JšïîFÚ€r@)S…·©ë±]¦àrøÁlñg9/9Æ{e#΂åH&Ô=8M‡/ƒÕøÉü>"oeÔø!#>ôÛÓKq§÷Ý:kÂ3ÿÄÖ?l$ÍÚÆûï?uœé}ÞŽW4ß6Rô(…)¯EÔbF¦ÒN*L~V¡ÌTR¥^á!€@ÎO®×fåçLËò3¨JE¯ÙèÙÆ"WAúíhßÛuÌ3ãßsxWʎû Œ"Gæz$]HÄ{b¢oºD•ŸÜ8x´GÖ¬±…^Š¸Ô^ʦ™ìí«W~*×ç8à +‘vš…Âê,<²íˆëª}}‰ßÛðêÍ®²EbÙ˜©“0tYU=‚M¯ƒg|±)ËЫÊÃIY°E—z'pbÊù+“]ÛŸ91»ùšõ&e}Z”„å'bifçy¥Òå—úq9SYä“7Î5ÔÛ©Õ¾}&bºL7órˆ.möò[?ý|On¼½„qZLÍ:±Ï½ÝcFÕúg£nÕÕ¾b‚§’Ý?Ãø’stÅ,{2g +–+ëÉ5Â<ª(+»ÃY˜5è&7§ÈX<‚ìVeÅ«¬Píq¹ë-C»hÌSÇ„gˆ%:w œ:¾k( +6^J¼?•ÖŠxÁ£a�°F2ã½î1ÎLÅwΦckŠÈ££§Û6ýŠ¯SÔÞ¡ô¬€»àrp÷¹Fb‘f‘ð»úÎ%/™îY„rr°‚ªŸÀa +âg6¥>:f™© +Ás1»…´®ŸuàäT.Ά52,0ÅJ„ƒ5k›!„ȺÕ�.g‹õûUjXkíëì#qMÒ¦fp4‰ +¤šHIá|lKÙ”`I.…‘®ƒºT©ác‰BÇ×C¾ÿ^Û.&•ÉRŒ’fQ¹7u9Dcpûpü:Š§Û1æ’Éò$Þ:Ÿœ¾F21ò¼gV®·‚¬æj”Î{-7WBpÓÔâÿy•8}Œ¿÷U¨‰ÂTêèEà"ñ.û’ýXÁ-c5#„¢eAIÉëÓצh9äaÂë¡ÎC ±W·úcãuÇ'Çn¬+#¡‹<zHŽD€LjÎ?û¿·çúûçÌó%ê·L!d!L~D2}œ²]† gÝt#é˜BOÍ¢¤Ì÷xÚÙñþÇ^}mÛ#Bòþ +*Bæ*æD滨Zù!änž£®*÷zó8½fÈž[‡÷mãʼ¿ú³ý/®¬.Øó\EðH|ï«(¦"yœ£š,fûSçÃ#ÝU¨4MbÓÏî<A�Ù§>óME¼ÚM°ªíá0c•9ÆyfÖ‹ + ¢&F‚ôî3/9©ÒÁ:w?nªFÈØ°<†œ¼s¼½ë,^,÷ û—trK«õ¨N_%¼Ìê5<ßöøçòIsÑ#ð Ò‚`v7@Ögsßäìgû'®v{<—Ö•,ÙÍ5Œ„Š|–.ÑõÕØâ@WYšTÒÛ<lzø~´É+D˽’«‰7™¬6š:…ƒ|Q¹v\rmS,õ1 %¦ZI«Ál4šˆè="8F/äYŒõµ¾ð¯{qiéDýLŠºdt4´sl,ÜŠxíÔ7Þøßh¦ ‹Â/€¡á³¸f KØ$žƒ+’›Àw[=g5ñ¼bæÀç!´s�]-°®ù&S›áìÚÁ>¼ "4Š£NY7Où\îBP{Jø¸Ž©P¢ýáÝÿÔXõ?Q\a9æn=mݦ†Oå*Dž¦6"Á„ðT—“áäB,C>ñ3foš1ûeN +1Ï®˜ñÞÞÈsQS™¬$ìB·à[w²o©SÕW(]˜â™³Žõg‰Ü“¸‰á¤¯¢Ã~˜<àÖMÔÞ×ûÃø¾C[¢»ºmN’PWãõÄÕ*`Û¡=ÎÕ"–Òzá˜nŠ®´œiŠAÝ âBðC'`®q±ÜÕd+M«ø*§ÙwévN<_ÄG}Õ!i Æ÷œRÿŒ3^7y~6øRE‘(?ø9é¹Ö‘}Ôâ3ؼl’¡ÇOþÖxˆx3ÝÔœ§Ï+ ¶c°Yq¾É¸ÛLF&…Ä{óÚZsšÞ?žXdiõu{Gö^îbnþ…HÚ9³Q‚º CØïŒx©üÚ44^/jZSURôü@rsnÏæ~2L?bøê/½jY ë¹0ãe +®_°¤WSÔmà:||½ŠH·vû(uþôÞôJ»Û±çaÕô£!±Ö…Þ» +Ë;r¬u4sà€‰ó彎q ÐXÓs)ˆâ‘+H½À°»&‡ŠÈó›—¥MªHP ["9ÅXkXæ뎴v>dþ R<ª{è{ÀH´%¡béEËucµô=”é)ÒEñ Z9¶wy}fÍÉ6’ùç`"±ÅÐJ +ØǬ–Þ±jñTÕ×òÙå1”x'OÝ3gÞŠÔ³ÞƒÜâ’`É|ˆJÀåûÇÒF}™°DíLá}wcfƒv˜õ4¢Ø¿‚hÀ<‘Z›æþ/Ãçá;Ÿ¦jšZ‹vX‘ìÝÀg_Oö4g®Çå2u+ÁÖ0ØîÚs¼±mÅIY§é[«+Ê$$±¬vž†í hƒlËìð „à¥J.L~Aã¯c•±ú£˜”òvô8XÔhÆÕfª†C±T<3öˆ)”šä¶ªD]W]d"]ðÀ¿3 %É·þé6¼Û_QÐà +!€³´}¢œ/^BI>4} pR,³ºÒï‹!ÝÏÎ/ÄX+UÚ)êbLïVÜÅÙ”ef:»`ѧމ¬¬LiŸHgx&lš\µÌXÜ ÙØSzV”E‹ªaf«„ó=…×emEóAdêH{é~jÒ\è¢Å;6ôÆz¶ßö¹¨vß ;Ì+øΞwâþÚsüÝÆ—Â'Ž .*òú/ÅÂXg5ÐW<±âðÀÄpÐ?Äà Â5VÔT²Ç12ôð³vò´®§<Œu|r³ŽD£“m1·+cSAÁÛ…H+.ìÔ÷ˆ»{4ƒ²aû–"Þû:<SmR¥)ÊdÖS”ú/ê“sWI4·Ììq(/ÌÑG57?‚ãžòŽ§WáLŸÒi)9V„' ‚´ˆ¶ùoeGžÙöЯõSƒø,wÚmYó–…½sz!5´—²ìm4míèDXF¥Nf–ÉWÃÙ±¾ÑxŠ«EÔÑ”ó*AÊi% MYãçäM Ôë‚™ÉX¶ªäÉEPy9Ÿ“˜6°°gm- ï`/ˆxØIï$W õÐ7ŒÜû‹_䶼¸5se^ øTK"f|8™ü^w\wýú§©U' Nþ™œ¶4k)¬(ô~wü:„æྂHpÐpÕ沈¬;h‹¾èºØ.ÚVì_õ»ô½%¹jÓJGí£8æ<¨O\èÅßî|嶒ÙߣEg ”/ˆIj "H˜P„b—ÎCä$(óE9¤áï…çm€>X·a®Ö§e'žlz±Ð_¶�á/æk(ì.Ì//_u$ª$ƒYÏbõ,ÕG©âˆË<—ƒÎ²ÌÅñÐ tjÐ/UM±1î?uÞ‹¿p½½Š^;»6¬ÆÜdBF?Ë_†ä÷ årú˜ÌZ×óÞ¢¾z£í!U“È]H®%(½½1ñËá0çðþ QêI]iäü•9`§ÅÞ‰{BœƒÃcñ.¯ –B‰$6®ëÊà@¯.1Š:ãóP=€ ó +´åÓí±xa”³í>Ô˜—C߸Îå–P–\‹äq††ƒqOGˆ"ÇŸþÞ»åõ-³áßÒ0:ec§‰úXjü|WKäh²ê¯Ã#âY–Š§'ôS±Ç³cee¥ŠÛó4(›hÞ{;T„¿‚Qq%ø’Ã<©8#™ïéï²´Õ/ +4¼ÎeÈR-Å*¿R?ߛȎniF¼oz?LÞâ†ÖŽB3@“¬ý’\\TM}F‰’ëÓûm‰D OAÿ3²ï*ÅŠ¿�!ÇwqîÍ`‹¼B`ŒË¼bM^¦š,gwÆ2M/Ž.%á”éï“WTô¥aÔ_êAÖÔ¾‘©>yaya'‰‡“wDk_ՂР+Ím»v‚Ä'l*=´Ø<uSNÕ_Gå«—”£—W„{ +¥°µØ(ñŠ¶ý<´ÓxZ²»QÌÅ£¨œ{TÄ][¾ùͼGM”‚Wú}q’¼Ã9ß¼9)@`zÿg¾ô¾Ö±,¼ô3—–äpÞ� Šúyª ŸoA¿}I²ßPÀÇaB:Ëý…(?•nîW€±-ƒ÷°së×oèË£¤sÀE1h^ŸÍSÁ›�ëËv"ddYß@CÿË4˜o‚|ô[@¬¬‡l|/øyçc¥ð©+k–àœy–4ºl;R9ý;»ÒÑ)ÃKÞ<¹žž2«ÙNTp¸f_~€ÑF¶ä`´P¶s¢¹¹ÆðÓ^7–V7Òw¾ ÞD\J•ëCA~ô$g§ù^|%á.”Ù@ìT¥:WS÷OîAþåQÇáJÀLv_ÐåºO1ĵ]ò—0&ÿ %ã×#d>Ùï*¶ô ÝY¼íºššÄ†®CÇr3Žâ‡¿Û9ûkîg•ÄDxH\V8<#ÐdtÙ\+Ä_äu\(¶¨!uÓÇÄ <Ï$•÷¢ õ};”Š¹e¬ƒ214ê[ýVâÅw£á%×ðwß\raHˆâD«`òX +dÀb«/6¾ráùÃ*›DD×%_ë¶è¡T +ª}«Êd‚8µÜ' !ÇÁÃÞc†Ê¡bÃœDˆ•î¯xÙóL6fÎçúüÙášmHl%Ƶ Ø¿sl$ñ]$ÙŠQH0ä˜Úq³óÈ�5¾@b`KJK•‡OœðÌtΑ‹‹çi°¸š£±Ðµ¼ÚÉ©Ý<gîŠAqjªì;,ÌÔs6à_aûã¯ÑT©¡Ìˆmã-¥Áö +ËNcÆõæTFªâÒÖ +^ÜHÖô¢é$œß¢´r¤Ì’†R•þÉ5T¾<š0r "[TèsÁÛâ¤zà@§'>FA]@“³CŽNçLPgÆ` +Ä,Kò´zUmkë+”Á½HxbŠýj´´»’Ÿö’ú·²fm‘TÏoüÒ'€ +>¹aËrnÄÒ=Ðì*Ë(Ê0ºKë\€d_-GYMMoº¬K˼á&_¯/¬žƒy;–žÕ ®ü…M)ߊMU»«| +ïáPÔHÀ#ó[‰ð +XÜ[QîmÀ‹Ê¢zĶµWà…�È Ûº~`Û:Ênüd½èg¬ÂãéÑg™š+Ú5( x£àiY+1°Ô�~ò×~-ô:ñt¯iíg ƒ#{ªMø¦W íÏ÷÷Ö]]û;¯ì)ÅJÛSÇŽi1Ë õзìdÎ�G÷2ˆ®\¨„2ëÇsmiÊ{ÆJñÜ‚È«%D‹—K`5TÝäoŒ$~3t3ñø‚Õð–cà1¸|¸€;²ñ¤ŠU—m¬Š¬öd$7yÍ‚óöjÜ9!•ÿ7¢?ïÈä´ÙïO-)üÄyƒ™õ�íuîwœø¾É,@†”/—E¡ ËrËSIÕ'£P‹)@žé9ÎRzr†0Œ‡r=Uãbµ¤‹‹¬pù–ž¸ 5Ò§ÞUÛßm|ÉÕ*A0Tºê6¶›“™FèhÕ{X½®ïkF�1Xg_,ÓËå>¶;¢þ6Rlǘ:Ã�“pE½ÊŽŽösî‚ĸ–6FöEC‹/,6JÅÖ•…á0)ZhÇñ´äp÷s=²L‡£Q[;bµ'e_ÕcЩ_õQ/‡ÄÐy ÅôºQÏ„ìßżiîçàbÚ’˜u¹Ì#•ªÈææ7Ë2‚Ð`qAv/׿¸T›j[A¤m"37SðcŒl‘º©€¡Sã)eX£Jòï“ÿÖVT ‹ÊkÑí³Rߤ.+ª +š^„gT £šv'1Qdÿááa£0X•‚¬²Lêô"‹£Ó¸+›$ÆaÇÒ¯•†Ã棛»2?»½ÆeJ|zé}ÊÁÐö#¡Gž¼óQ]\_\rf¶õÕA—ôGH?¯†q¼�™ÇzÄPA¼Ã$'©.±žzƒÍ¤f`šk!Agp¼ñ§I¨ëÚPˆµÌ‘hò'&ëò&¢‘ÕÿmïûbÊÒ8œ ú¦™šiŒ�s:„ÕÃD‰âŠ÷aìVùkD©9oñ0.úÛÒ|bèkãí©£7ÇÞò÷‹¥3„×Îuj°{˜ý⼡¦Ô¶|Šå1Üü©g°O&u&“õ¼È,A"_¼°;†J[c4+‰ËYŒÉ®mìwf¡«õþ kÂ1iŽÚ…ï0Õ{z“GÚ-#\MXç°¨{c[ThÇŽÒø—\[ƒDŠSjPU]´PÒÐAç>#8ΰåâ†Êlo$“Ë…Êú¥š[žÄDøæqúÆÑÏíoä<ý{0ÒKb¢w%ü±çq‹š» Úr½fqQT3Ö‘²ñ +§Æ3ÛPlñ]_Øö&ÙûO&Â_yÆ•GBõ¥Çùßi|É# ‰+z�Þ=p+APAEZÏf,¯WœLèî(ŽýÆ™êéŠ}›ª/È“ïOoÍÈmr²r }ƒ'$…¨àÒ^V7N€Åþy¢—Ó +uIQSsöÏåVÞvb–o‰)1ë*7 Óº£ì�J£Æ¬È‚BFæùY¢(Fµ»+6Üšx£z)žÔôÆrÒ mi¨?Ò0çA„E¨U˜s€Ó\Ï¥Õ'_x¹hMõâ(²²ÚË3 ùðÆv$Ú¨‘Qê˜Llq—øžõ¸Ê‹8¾Ü'S5`…`JR”@C[ÌŠèªÖ~Óg”C×@–Çâø¶igaÃœ´£îâÜÞžä1ó¸U4œ[»¸a•©8ù…·/†IÓ?€ZëÝ3gêû‡‡eJ +:J:$òª¦B™}…BîXR¿ +ÌK€‰† +2£<î Þd7Û<}È1œ›à¥6Cm¤JÃAœ#ëÄ?L.Ààm Äœ¦Ê£Û¾·Î†BùÈ™[ä'÷¦¥�pÜOíIµÜ¬/B»T\ÕâO-+/O¬Ã¾ ´±³K(ð_•²Ódªo¨/ÑãéÙøN#+z¸ØFÁ„àƒúÉ•ùô3´óËñÈÈòð¤íðÀ@}wá'«35X iÃïòîÁdßW´òÒÅZ°B{·>9蜌©®Ì4JÉÃ2:ƒ:ULšOaQTœEÛ:/&jÇ#Ù+ÍFid>‘;µÇ{÷ÿŠ¢ÏÐi'Qµ§ ä‚œÅÁ`r]|^Ä©��@�IDAT:Œë>:±;ÂWÕ}–A•Z´Q…ɶYuÛ)µMú<›Í“V…ñµ�#KñÖVÀÍŸá$½wÉ2²Ã ¿E!Þ¡’ÍLEÝþò¬ Ý'1ù8B[JPáØöÚst|Üy¢~Õœgê¿Ô¤m…=l%ç Î~7Æ}ωç;}Í ñSºººÚG9¯Ê1ýûÃQ[›xbç[¥é �‡£[Õ0Ž“ϧ¿?ýZZøM×á£óiŽ>ÆÐæ¦ÉÄÑAšP7&Qq–T+Mo/õ¤f�! ÆxUÒȽHgé}«QÚ³¶Êê7ÚRuAï‰N;}|ƒ7qͬ·âþb#]}î?.âkmÜ ²3a8N¤×-ÕÞê˦Íú5k +”¤æ¢->‹�uäb”(ëðc9·Ð©¬ìŸ“uWª´«Nvì™Â ÑTræˆËÏO°ªw¢ã…Öôí(é%†—lµó‹Í#ÇûžózCéØ®Á4µ¶Ñú Y äE ¿ƒQ@ +4®¾ó£‰&0OIöbI tó†—+йÊÀRVBóÌ£c±Ø«ÐbK!Žº ñà�ä@.‚|R§¯ï)Œ’ÉŠ‰fp{镸ñůÎx‚’"x;?ÀÀüoÀÐQÐÍã:ãƒgoÇÉÿÐœ)#MÚtýçîÛiFÆ\¼}©$?.å]W¸ÜõÙÁ—ÀøSC°bßûSë“Ofj›¯ú=f‘‹ëõ#Ö´‘/¡ôÝXlGyéóˆãf§O8Éù¯�CÒ'A¸4šÐ×Q¹2k©)„¨œÒ—ú¼ý$ƒ6†—.mß{ü&=ƒ‘ù9 pP•„ѹüM8U\ éo65«t^Ýîµ'¯{úœïÕ×;–,1[‡“ýÀ˜{!4È`9zÆ¥œºî@Qê5~1EQ6u±…KJƒ&e”‚û£1ÔÐHÎa(n eu•P?ã@oóñžž\a}Óë,c¤ax±:Ax±ý½ãꔊ7Êi¨«ý÷üçdõè[Z%¶ä€ÝKÊ”1@ö}ËNÿà7Hðœ7kŽÜs¬ –Ï͘ÎÕP|¸ +ÛÕX¢†þŽµf‡nZÇ«Cg�Gbé•€7}ÊÔéí¼àx°<ªuáb=IlŸæ±%R¥vágÞO(¶zM«�ü]®?ù[B4²äÊ p\‚[>6íj'j é‚{ÆS3ÓHB�&¶ËlÝŽnÿy$5¶?�‚n»Ö·�ìÿõ:h£‘?]{ACºeH©Pe Já_Ç’hŒµ¤A¯Lâ…~‹µolكⲠ#ö–Iæd;àxN4ò7»¡�%¤ÄÔJA–fäý˜Ë-–b§Â™tËaéxDAO ‡ûI$T`¹Á*fÜŸ’Š½›ÞÉNŸ4!þ;pÒw1ñìHúökNBýÚGSËtŽÝ„•I2Éwuô®ŸV‰gÆgÀøš"–ó%yÂû£<€4îä¶/š^ Ï%îß[0oÙ'Цƒ/RŠPFE+®&‡°E!è +b ªãß…Š_¯AH†(ƒ&sŃ|S<+¥T59Ö•üxó‰RÒ‡.¬_ø¸Ì2¾Œmþ\d¹~Às!ô2/¼¤B"Uh ¿€’.ÕÐ&®âL™÷\V,ŒÁöOÖºÝQôð|''Ô“¨ì:cY>}Mÿ^[㹫yÊhôÄíõµ³ü¿^ú"FÝ‚yYŒDxÉã)BÙî‡NrE<Bhç½gdbóxÙºT¼K.¿…ðT.gïýŸâ¸ÄÓÝ9’|P4àØÙ\<òÿz?Þ©ýΩç;Ýhbx_îŒÖ†ýe¡pþ±]À²òèk¾€ÕC'0ÿÙIÛ‰|"—¤ôóŒMŠµšá²íÈÅW`)µK0™M¶‹S‡âÒH3âšÓ;cÉQPG@œè¨-ØÃÐ{CÂÓº±t¦ß6²Þƒ›êV¬à•––\%¾DŒ5âÃ$†yjkN7€¶¼^3õQ–~™å¬-n”ÖÆ2“—TTŒ-,òš=¡¸8çÌü%Ýï+óRV”TÊч].ÎÇÓb'¥Ó± $Oì+ñ$nG¼jéIØÏ©“ü…$¡DçØeSÃIB!-]z¹7¦ÛŸfeq"3%·x�1€B@ÖÓ¢{ <�裳ÒD™¶þS””Þ‹ò\xß1£¸þ®.º´$%'GÓ"ÆÍÒN{íb/žÚ‡îÁ³A5ˆrð…>`TNnœÀívL›,Å+@Š¿ÖÇ÷>¹;ÛJEvëM+®ô«"µìbŸ?ÃXÆëÝï¬'E¨tÒ×Âî�báL +:&Ù!Äîv»ݡ¨úK5?‘Sé xœ#á„/šóàmÈGIöòŠpÞ@’}ÈvÙܹ²¢Rõ–m×Gµn˜â<*±è!kd$CUÔRøÍOÆ[n¿û˜c±‹\n·˜œL &|Pu´nàÃ=/=Ösn_ì<ÒÞW’Ý«jŠjëíLמµ¾·ô¹©3¿wÿZÆXÆ‚|å(ÅvrrršÉŠ ¢‚ô“*ñ0—ŠÑK–x1‡žñhþŠ#ÿm»¾Ù𒣬,öŒïOfÁ…âå¦Æï®÷ˆãOŒv#†áT‚û hÞuÆ÷\-)è—Ç/Ú9¾pó‰±-cékôE`D¡ˆÊ^øUĤïçåGÊÑC¯©ºõ \#"G}°ˆs•¬[û¦x¶Y[{û>gÈ%Ì÷K}Í5T>Î:½;‰5gs£)k¢ë†JÜñ8‰Ý‡xæ-€œ4KKå`Ã"׿dIÐ×°`Åþቆ––)¤Ô‹káãÞƒ2Õû 0wŸÅQMf&~ÔV4©€ó-ØÜÖK,Û@ …£k¹Ø‰æššü’h9âÖš¡¶&“êà |ýÅG<'•xƉánãé¯d?8šºZ|w¹y×WùWÙðÏ“ˆ…¾Õf¤¦‘0¢A/ÂÔ2¶}ƒES5Pà\o0ìh–õGøa›5‡%?Z ýoñ0ŠˆæömBÂ5ùû‰•#Â&È"Óo8V mi…±‡?Ú‰Ý`3é¹àÚÈûdíï|‡þ#ø†sÄÓ¶(î+µõÁÛë/Ys»&²_Bìý«€—¤x!*.v{½†AÿK¨ÿ²móÛˆ!þ�^ù·‘D|J_+ò?raUQ/==‚‰VJ>)³ñ}ò;Iœ½¶‡h˜Mq\Àƒ@@à~83…Hï Žð™üD²ü0^‚CF…S¾hgI©wrY±¿okEñQâ!•ŠÙÿ!–åWZ6u54ýþ•ÕkˆL®÷Ó6B'T—óÈäÿâênúz·õÇV¼:ž[DVZÓïÇ„ðào§9H\E`bĦ?ÿG¾¢mß• ±“àG av ™´iúõÍíËåTØ +Nï/íóæ3¿Åèý'Yq>J‚¿à H÷I&õo¼C}�°¤ ¬9Áh?m*ö&à#(JŽoŠ¨Õ÷$UK î?ûY7üàÄ~c¹×^{¦•»³v„kÒ0†Ã:Î ¥%¨tzKážOïÞ<™7:ˆ;Ë�‚ò’k0Y!àm†‰!Å|Áý…P?‚Š§Ú¢odTÔIÜH ]ë).v“Öù±–éE‹œ€t<ÉÈŸò.nh¬;ðdEA;¬>ÙÿlÛ®ÁhD¹ï¤xæß ´ûAË¢wun†}¼E÷žKV霒±ëg‚|TüCÞ£ 3´Y¡ë�%0?ÎdÿʦíÎ+¡V¬9ÌAŽþÂ29” 5KáÆ#‘@Ëø¸X«r¸-9Ų‰—pª½déÍÒöQ<|äEzAòÌîIöDíÊr¶®ý†£mƒðŽ/BÇW€’øwÝkÍŸûˆžÖGŒ?ïÍ’É&VèÝäØÙõ#£?6N<¬t$7/+÷v¢â„ìó–{Ò§ü;'ÊH£ÁW±…ÀW –¿ûb¸¥…”AÑ?Ñ|`-¼e;xáMx+¾ÏÔŸw&ù@=G”l6?âDä\HN]ïN]¿XîÑÐNvJ,UŽ•ƒ¬ªß’\ûߺÀ2¯—Ä×O=볋t{Qä¯c ãÄÊOMB:„KE™Yóê`fÕŸzãó×íéòíûoç{{J"c•LŽd"ØÔ«xu8½Rl˜WÿÝÏßÒkXHÌ·€ØmÁÕÿòù™;Atå¿|~ÅÙÚ„rþq0€d(Ë(»â“ŸÌ;QgÛïõÞ¹ +;ÿjÀGXšºÂáí.¢µMnWÖ±¤w?Œ¸‘¾7;û!¥1¸w˦>1=PÞ|ÈòãÉ')ð¼ºì…·O-KÉ1R¦]"@ç½Êî £Äûþ.æigÓ¨ÐÍjÙŸÛ{'‡g-ùL +<&TŒ‘ˆÉ$µÿ|x]½½½bŒ¡.FõeÑ ã2 1Æí¼ÅUcií@(rÔ›“òßW2z1+�|£Z‰t*u†Wûâؘ+t§§öõq«¼u,%§*¦»“%žŽfœ‡œ{×hØôÞfZöÇ€öØg3Âs”n&áI/àýV„nÈP\0Ö¯¨Z¿~=»ôú�oÌ4 iU¦h±9K<Ǩ9$À¾°TXŽíͬǽ¯Çdw e7ƒ’À´t£ñ12äÃdw²Ñ(.Qþ„70¿0~#Ü1ΰ˵•ðè³%ǯ™L[¤™×€wŸGÓ<¸”MTj:]ï>æ0ÂÞÁj×îá©AÞŒëÜÇq…ÁŠ"'drHÛ‘Ik~™!4€DØP¤…2poŒ3¼Å!*Î_§1À#bóæå›6u=²fM½Fžé«ýñ߃'ô‡<Ç~ êÅ(Ø E„°j\…•·Æ’‰BÈ3]†À|1T7~¤èõðrÎ܈‘ùé‘á«eYâ9‘J¨j‰›ç~ƶçáŸ7ï–O ˆ-·ü¥¤Í™G{wÿõê°Rië9÷¶šPÇÙÆ&c¨1~@ŠvERåziiV6¥yˆñÍß;8J€éSˆ;@G•ÒµºÂ¢ßã³4~¦ûÎÛ~—¼ÔÐùª™%õHUX.¬ÜD–2+N܆¢ã;¢ ÐpÉÉ LýŸxžC/<C½xº‘]1E© +Šn@ë<9K&¤×CÓŸ½^Ï•ñ¥¤Rÿ¡T$ñ|ÏÑ êyƒ³¤s´%u‰Üš¬aR^™ï1½ç2Ô¥ë¹_Æ’Lì@d7ßþvÆöT\rúæ dzÊ°>?Ÿ‹Òß5P„häA$5”IsîªÖ—£TË¥ETßÉw’5%Ô�৺øT§´¦õpˆvRIO‚°�Û~¿È˜l �úû£ñþ„,>#x„ñ¿Ò4íTÙÑÇsóé�½+š £+‹÷ŽUΟŸ÷øH¡hQYäÈïwxê$Ø”™„Æ„`ïÕÚ#ë¨Æ©x¶ä÷¬Æî7bÒqÐé +ÓþÒRk¬wâ°ì‘VB‡jâ¶7¹=eÏa¿ÎÛo¿Ý88–>€àÈ0¬”ï×µôQßvÁ:T‰çØÞÑøCT*G‡m‚·z§¦*› 'âmz+HæÉ�Êo€Åzxïgíº… ÅÛ ¿Ñ ¬bPYOòŸûaT×…¼Õ--}Ëš–@ép³^õpwk<œ^œ]½š&ãôÔ½=yẖҬþ¯C¸à9T%J]]™‚É]zõ§>ï½æλS¼Í6éoð6µŒ—¥›£]п[Hî–ãoð_TòŽGž%øȬí¦Æþ + î[°Jºó*¨”éKAØ´Ü%Ø>¼+€|l•zCë#ëLð3ä›r¡†N]mx`ëÖý(¡•!jºÊЬz@ IšòˆkN…2Ù5ö/(jîYVѼ_zK¿Ëè=ôíd–»]ò%—¥¿wKógûPAÔ-ºÈa©°D¥ßUbÅU†{XDô<“eFŠÎÁeæû›£s’K@mŒ“’©?<:”¥)ß;gyÑØ?Ö¤çÑ€e‚ä?Ü~Ú2 „ªÉÓ”–ÕGy?`ð5 $MÜØ©€`âë§Æ%™xŸ8õ 1�R…ž|þ®2¾ç,ì°1N–Ÿ2,û•¥â´`¬X9Þá~eBKû›àLÞîÖYðq¶ þ`<Ǿ*–—ÚŠsïUw|á:ÄWóK|ܤ·l/ôÖ(ºuh?‹%±±ä>0èò:JR'£Ÿz²c²_Ê/YñJÃÆVÚ§œeg&°0ËtX À<È%EÁêÀòª‚<Öƒð¯Öµ8k8](ZÐôö"{g–ãf±‚œØ¶n]Þã#JË0v·! ñ$ù*a�‡ø +êZ%œ4‡ÅiÉÜ#z%2µÅøbÒ„ËûW§â¹[Qq÷1T”b†'8]ZÀÒ¥-8¾G<w&.—¨44ɾé`gÛ@Y›%ç}§‹Â¾¯äyþˆ{ÑEÛií 2IÀø·ÚE⳿@œö[2û¶…’hÑáþŒëÍ{õ0cöÇ…n +»Ç6üè;³T•†ÃÏš¨ûVgkD²Çpìz[d½¨e‰Gݦ‹×ЦÜÓR›e–Û9½ßæø…¶–Š‹²ãH2!<ÏÛ•HòÔdK–Ñšÿä9ßÄXzÞñ6'E�cºs5ü¿Fiì7¹ÒP'Y%‘6í�6¸M5þÞgë+*¥·¢xfºä,½¦W*ïŒþDDë@ˆ¿o¤åÔùÎvMï•÷S:<O}1ÇÛ_†·÷¯£ý›èb¾ÊqÔÿAÕè ¸s3 ŠË1|ènÃà ìÂ5æí@’›%¶:3…çuèáž9§"ïÔ}P-ý0Š‘~« +ú#.ŠÝ&Ù™t¯Ô‚"‰ES;dEy�•¨X°ÚYTŒÙSF÷ôvV eY,Ý ÉIdðé{üc?gž/¹” G¶¨ß—h) +»IÕPT_¼÷¥úp·H”F½=OYC:/ŒnëVß?³‰åÓ9ß_<Ã@ùð‹$~{úm!ËÖE·|æz8p°Òm¼cßÎ¦Ú äXþw4K}\¤õ È¢ê]]î"Æ_œqT±ÐáÇ^y¬d‘MÅ©6À¼†¬Ù`ãIì'dŽ”¶ÉÌzÄçèÙ\† +‡5ˆf>>Šÿ¦Ms//:q´ÃÉú +ËÍœBзoÄINtÉ’êЉ‰Òp)Ê¿%¹=OuìëSëç•ôØŒQ +–÷(n©á&ØD +’t†ž§é± $e˜´(¦I„]H +õ9Z6:NaÂÆÆe³}Ž(MÒŒSŠ,ï\ÁÎjôȾc½¯/û{ÖK×#DðqLMðÀ!d̃l—ÑPXCv£Ü´6ÉÐâCºÁ$}&5¤òÙ„)Ö—,¦ìGZZøÁ†ó+ÄlBYU_2F8@/ë²;îš6³:]¦çŠŒ8k¦Gü:ÕŸ|;Éq±9¿+ò÷½,1L©Üù¨$|1÷„ 3aȇ:¶ËÓeé +�ÆL±jj‡B¶Ô™‚ècf&R6ÿtËÕÛÓÙzTééѶ—ù³Žãû@]AäŠÊÂÔ@?æ•+whŽZ…‰(ˆv›Ÿ4²©NiìÄPsÅè• Õ=âpŒÎ2Y†Ãl@ì|Nڴ߇°É',èÍ¡”Ö2ëÄz·ýo9›Eø£X}ºº©¢¤ˆ‘O B"*ªG±k¿âè“ +(Ûþ¼Ìå-0¾Í˜lohj2ZÆâ£vÎBŽ6ëÓk½ÝÔ;¼Å¨lg|rÌöôØ/Ùª‰LÜ«@ Qv%Þ,úµ$ùvšçnÆ_¨ˆ¤5¸TVPÚ�‰ª.¢Ó7Ýä\Ú™thÓg£äXãÎn §÷ýG¼’‹9§ç…dÛ×s¦ 9xìHêÄ 9GÕr†;² +ÆDTMg'mëÇPAVòÒ‹˜uâû6—zψ[ínŒÊÒŸ°ÖKwRÖþxôáH¦–YÔü˜ !]þ…ã¸,Ó¾ +c³>Àó:,~‚Ð1ÒúäVBÂBTR›š‘HÃh´�õàmà¥+hªs(2YNI҃݉lg½^§ùeʼÊ2rÝi½ÄîSË›ƒ™f|åðü ^û.Í°/qš34Á)fI®(ÛX“§Á´À}!–Š ÞÁ^Š‰]ÈzËècàN�xb»!Q±G'ìXü²ÚZb°ó^\ÆT|4ñ4íV =`˜ï…ç7$;¾ã³ÃtÏîÍÞ54¾À%¹¾ˆˆ°žÁíŠ8ÔÔ1ñJ!ÌIàvpzÊÃy/Ï k‹óAÞP¯ÖãC/p;>b÷Ž¤ÿ?˜»ËpºZ<ø'nUÊ‚l>Ër½-šû´nçé.éë/}ï½kîøÜRQƒœõë´Æ6è,#q¦Ù¯Z™¨Ír[~ß�n +Ò‚©ömî+r{\çç@»2îGÉ9ùLÀDY!‹BþG»áRÃOµÿ—'N ç.®äd.N[Øé«ð”_³áÿ ¸ñŠ Õ¾‰Ç¹aItÀ!±K¸g®¬ðžOÙ;Éû�‰�f†ex<ù«U‹&Nï#ä|ïö ü~¿›Ùät�˄Ѐ‘p"3Œ.wÖès%{ͦ¤wlŒ2eLfFS7b’;:±csžh\ã¶Þäå’w©9Íå©þ‘¬hßÉk'Ϲù¶Ï\˜aqšfwUºØ&Sv^Ù¼9‹<@¾/„Ã{n7 §ÏÉ/JÌ<ÆbN€\ÿÐÒpààií¥‘¸ãæWÌ<OWèªjïiŸýÃ='ar毄@‡N7¼äýfR‚HÈ)B€„;1'SóB†§>Š¥ßª4ÃícëaÄæ¿Òkgxx©‚@©À03`µ&ÃÚÛóÓŸCI¡K(ÿŽÃ²™sX…w#–y‡iS²Cˆï8E€FÝpU¼ÁE¸#Þ<¨š›)“„Gp¸¼©÷°‘34–Ô÷etÝñÑÒ +ÊÊ£’˱ݮ!ù´e*cYýhÓaØ�ä/ø[€™ý*0_-`\ŸU=É[P°pù“==~Bï¸g0‰åÀÑB«ê÷·u|8Säfy±oï3¿þþ@¡'ܳùˆB÷Ähbo3<"L)tÔ>CýÕ”–¨ƒ!<uÏɽ8¹9C»Zc2ú6îë× çô xH‡Ëóé`xIŒó”ájé)T)Ïy¤‹›õ± ˜9lx¶pšèQ„/ŽãyH[@¬u Õ×wÖU¸yG�ys¼¦Î£ +‚˜3”‡Ç€Fè(ñµfîߥä4Å´]3t9\õáÛÒ¤¼9ïÉù—CV!˜`J—¹ÃMx+¤O‘¾ÕÑÚJ7£?‘ßñ¥ü÷Öƒ|ÞìõŠÂMC'SƒÏÁà?Š ëÊHn¼bØá|9êçe2û_–Ì~ýà«Ï>N/9_ÇÑL©Bës{G’K*»Àɱ³×>1uNòù{eÛùèÒ`Œ�½ý öë9[yp×àÑõé°ç)Û¶^6MÈ‚,¯¢‚RIÑ)Ä™ÌDÿpºá%×j!-—Je|P$åtH ¾Ó׿âƒwÊ€]ÅŠì èœØ|:˵·¯=—'«<€Ž°RdÏ›ÂÜxV3:ñ:¥$ûF£ ¸_P9“f¬Y[úbçCY…ããìo,²Ðw¼‹j»ãÙ™‡FSÔ”´'Øò2¯ùæ#Yv#* >ÌêëYÊøHÁûPZÍëf�´”0/ÏÒ!/ÞWã~#NÙ/Ó-îc9dÓøõg?t P¤íz&. yGzâ©Û†û8V[¯[¦ôÍl¶÷%‰õt@cæX‹½¾¢—|büÏ=“K#I}æá´ÆöK)ŒîSúF”ù¥DmPVÕ&+ú@DBËh_�åóm—-l̇Ⱦ¯ Ò]8D(H˜e`s©Y°t+“^Ž·šB¼'^½ ±‹ãdüÌKPKäEØ!‡AÒ&xÇ…ÖV+ãrÖ.[}Â÷Hà]3ôÌë¨û-4yîßYB‘Ž=‰xq'Œ`ɱ(C™»vxäɳµ½©©ÉÙþÜÓã—/]8Zî•SeHJæpø/Æ63š†u•ÈscY=Ó!x˜ƒWœ$.¡vzFc½¢Kh×ñ2oW þ†åôÇ*¿tFxˆÜ“î‰À öP<£÷ÕÜ™í›ÄщeâЕ>Ÿë-ôW®Aâ‡Qñx¥À2«TÚ™wk"cîñ¹Éõì{áY+›·©ZI‚ûµTW“;ÏGí=>r¼D2ÞãÅžÙ“}ó—¾´Ëíûàé.÷1]$hÞïê�ÚãZ—À%‚ým×ΡÖú݉Yn)Ѽ‚·'·=X§ ㇀XEǹ5k\ËFÖ6½µ_LçÝøJžÓ@Jõ ¹pt"m¹´ª*µ¢²Ò¬Á$Q¸õÔ-`°ÞÚ½wWMMM~2†çà WϺ¬+‹Õ„¼ùåúX*²y°ÑsãÃÇ늋ßQÏwP™€’˜ëbä‡DU§£_R›;Ôùa¢ù´±z<:¸“+ò“íe ¼[œ„Ç/—{¤ÞÓŸÏNŒáâ@h9.â<„cXÆÍô!àö.ØþnãK:ÿ=_þ÷jXŸ«]¢¨±_gø¤¢»³ +Ý‘É•E⿧}¿Ù„Ž0ƒ#ÄÚW l3¯ÇMìe8ó2Pý] 6«àVõ#ž÷Xa±Ø¿ë”±èià/«/¨Á`ê:¡gFbñ¸½c4ð¤'œâƒ ýÿ"°eû?ËøÂ×Í.É<qxßd±¯è„°–k:ýôSGö{\ÁÉw3„0]I%{t\)îH%<m[ÓMMg(°ë;£éØH"kOú\R*š<݈‘ß_}þ©¾@Yâm|ª±"ÆtWƒ$N‹~3¸ØBEã^1¨›ÄY0¦õPíF ØÅ +• »ÄPºAÌ £ÍEÙõ•6÷ÊÁcœƒ¤Ò>ÀÇ6 ‘õ¼Ñ/Ä´É-e†:pA ð–AAV ¡y‹¤YuV4£~>8Hw óUùËBû^Ú¨x½J“_äjãš^ÝMšUžÜgº©ªìpÛéîöîçÌɯ,¦ûg5öùù#V¿|ù¤A¯‰ägþ¹Ê#UræöÚ‹&Ùìâžæ…n2I9M0by£ +åbÁç¾Ãd¨Ûc! :Y¶‰áéK–«åcéc([ÅäaoÉ%²4ç@b)3Öï¿¿øð»5W³ö‚È•X™É›0ˆÐý˜²K`BÒ`³rå||Ž„žöQöL/+o¨+ðdÏléÔ_×Ö‡³õG:Òs +|uÈl:ƒúðì~Ú@?Û÷Þï F“ƒBÔyÑÿÏÝ{�ÆQÞiãÓg¶¯VZõ.Ù’%wÙ¶i¡Ä&@$BBB +Iîøß%w!G: %jìÐL‘®r—¬ÞÛjWÛËôùžwm9†Ü÷]À„ÿ$bÕîÌ;ï¼åWžßóž2H;1àhö³<Šb6f÷Ÿ/;·úƒ±žæ~¨2¾jH˜Ý¬ž:*æ‹¥ç¦[Û¬¿ÑoÔýWz½zWLi�ùΤ¥²ÝÈê¾Gë’‡˜¦ââìÆÏ¥ƒŠ"ð&³–~.ò #œe…KÜŽ©Ó Jòùñ˜V +ƒÏ‡Äm§ÞÓÖ=½ù|P÷ð{ÞxñmÜðe'(É}S€¥Ç�'Èæ@‹tIá}h·ó)»;\ãuÆwŽv%‚ׄÑtÉç#†x6Üj ÚÝcüVŸˆ¶èá¤Øš2Åz¯”Íb~ÿç•ÆÒr †¨UW”fU¦ ~.VŸUààïƒGbésP‘é…2Ù½ëêÙE¡?"åò"ýyøRròÑä+ÑqG]^ ²\ŸñL…º]°Èœ‹¸Œ+qô`]ž=Wšî@²KÖåº2:„ Û¿Ú1{¹»`ÊyÓR!e»_ËݧXñVT€ùà//D–ÕË æÓË +ý½cie’òEã¥Ë̤¡ ‹Ç9AÃ-¢§RxEÏ¥}Þ1_šu;Ú@ ¼5PèÜ«ïÙÙ½tÎŒ±j'âÅ�nßé¯(µf„’²´D±n]II³ŠrrÊ$F*µ×ϲFRéù¸×r»È6p›Çˆ\î¢Îc}— èâ‘ÑÁ‰ÏÕ”G«ªªˆ5ÉKêôs“ßG#ª °¥Ë°É¹QDÓÒå’º*aY„AGÈç…Zªƒçç¼³É;O[¼î‚å=ÓûÄ’oÐaˆàeñÖ+�¬¡ˆÄ\È3ì(Ð#ãÃ[K}>UsZ_¢µ&[gð”0.ëlnl4ú¦âŒB³>l^£6Íjï‹)³‹gÏúUï°é¦÷ ÅRí^‘›�œÏ†C#€¢E†a%(‘}¶ÎcÇ‚4}oh'µ=å´QJæì”J‹¨P˜´¾1¶Äôg>*¯Ù¯Tº +f(ryÁ_¼’ñ$R€©á²³¶Ã£²>c<¥-†Ä×ZäZ.³QÖ¶n·ÔQ…‰¸á¶o÷ ©)Š- LõèŒ×_1~ÐýÑŒçBazLgă¼!'QRQT–Ëçû¹Å•^ÛÈÉë[‰t‘óC—-SL™Écò“¿2Júé<Œ±<lÌÁsêº?èöÿßžÿŽé4qìH†µƒ´ú�²ÊÝ(õ�•‹ÒÖq°—cQ¤…jÎÝ…‘ä dl6%@»‚´ÉwðV.È›šµY©-\wêøò¥•™–±ðU^ËÊy²küOWƒ}j#�÷ï oƒ€ÁÓÖHV¡úŒšƒ0CD“Tà¿/Êa1·©^š×¡HvâÐ’…….g«“u‰wÞÙl<}ͤ ‹+ê LJR¬À ¼žt#¬'¢”ü*áÄ=½q2r¾÷œŒ%9®|“Rnø_°yÄ{ì‘ãB†áÐò¦tÞÔ¨e2$qe&ÚZw›³ÝƒÒ¾VTŸU"ö +yl'TÚ&Óé#ûi54- /L$¼†AÞvâºÍͧ7‰"IÃtIØÀœ>›˜‹Ä£5K×(Aöƒ×‡PPÈÜgÄI£“ѽvI( 𒤙jzn•óªª€Ÿ¤4òlZ!Z¸g,¶0 +̨·pÉw,ò:™'‹›8³&—_ÝGÙr‚û\°sÏ�g„ä*´õl>EUøÑ·'bÍkݥȓ]§TžjX¿£´ðk¢ìU±ºq J¤?®;¼bœ¼ßsÒr„â#Nk‰¶“^Ð…@`<ü’OèëÜ"{™fz7Ít¿pCaS$Qèö ¼M;ùbÌ\,¥5¦ £Óo¤ßÍ£À¸'Æ®&” £mÙ~½ýõ>OÏÊ‹+¬žëb]\$añÑ<*€ªÁXú"Ÿ¯ïk™ˆ¾)Yˆps"èçÅ|ž�a›Paùmlt>$nA)É–ˆ´é�òqèÙçû¹NÕjTP Lp‚çÝî½ÇþÕK.‘™JéÆå¨Lœ8F¡Ñ”’+@ß‘»HšrŸËrí•(~˜ý|5l1ºŽ¼»]ËŒ!GG’ò’\ +PNïé½û{ôïÿðâûÒÔ”<Ïî¦ÒFßÙ•þHÄs5ÄÐðÁ4ïÖÒìöÇ«ldB‘›™Ëm¯,¿®ò×a`Bèâç¶PâŸtW&6®÷¿Ä™cÏ[DÙʇ€ü +µ$®]â‹#vù‚ÛQÕŒX%!\7tÄ"#‚åÈFŒàY^Èû™÷ÙËAôò)d¸A×M¿@«ñð‹c”M‘9¢5Ç0ýLZ=ã1B‹®Är·3uE’Ëßf*??2V—/Z% * $cý}·žú«ça72zšåæÇ[i³1”dpí&o"ðo®ÃDÚ#6Êué`=’y½ãŽÑc¹¹.ŸÍ&2É $*lTèÏ*=ݸȾ™mA%½5êDÖ0©LZV¹_,Š¦Õ¤“Ý6’‰è¥+º™IGZ¡$V´C³™¶Ñœ)0ºàƒ÷°±u˜Ó8à8I@áðYºJÔLËP\âh^�GX:Q¾y +5š!pâ·£íd'E ã!á’ðʉŒOD}E˜¬• ñ]"™Ò–±ÖãwýrvGL*% baË@ê;Í•rß0¢¬¹øC¥ÆÐ*¥‘ó_‘¡õ*Àqʪü0Š!d²BAd²g¡sH’$R2gœÐ¢ÃjiÑ……ÁýCêp…”áƒ't›ÇU&`l<C-8{ñõžŠêÇ‚Ò²y[,Cª 2fvó„©ÙY}}[›ª«³¬_)N/jÜîrÕѵŒI£ñ4=Ò–ÊQ90 S³|e B_ì¡©Å0Hœ(/@%aÒ«å€{‚NÛBØÏ +ÃÛŠ |t^Äl…6ÚT_’+Ø“†U€pP1ô h›ÉÝpâú3ÜQ“N³JB8pLµØŸ”GÂk”þ¹„ÉîÒªü�i•µMÅ“)°óÃ:v@´ Óá¯_GÊ+’ä(³ÌË9ka|Ad7ß3|kï¸Ü?vX‚øQ…Ë–(õ:RwbÅ)u8Òåv{25¥ó)`w|•Þ><¼w¸-m¦/Ç¢f(––Ÿ\*0’àØ ¹ô|Úq‰È°—bpÌÁŽ×‘rNµW»ÝÙ$‰±>þÓD'<ŽÁ™ë»‚/¿0üñ+´¡¨’L˜z&ò\ìÙKʆ…šcó<°"‘5c;¼oÜò×΃¬ð—Ñë! üüØ£¥ã7\éJu†ÓEˆ;hRŽE_˜“Ÿ“BÖb™Nq_¹úYÅÞ¼dO4ãÛ;‘ð½v •ZVã<u?©© %ÉÙ +@ÝhBå—¸6¤‚È‹]¶M3™§’TæùÅ~G6Ax²çê<w¦Ð!Fîý¯„¯¾üâX1Ül`›³›ÓP,éãxÛe@Z–ƒ¥_tRŒ¯ÀÆ{a™ŸkçmHŒ™3L”ê!f>ýƒ²_NÔtÝŽÊ=18 bVq°6 FÄ€žx)À™±¸Åv„9e„-Êxëà®áø¬Ñ5KݧÂ,Á”>žÈM ¯É‘i~Ûh,UÞ“È,›µx…¿gÿÛ#‹/ÖFF5Š8VÀû mš Âv'~ú�ÖMÎ�…()«°8ïïm•Ü¡ÌḲ–Ô:T>ò¬À`3—¢/f"<�€ˆÙ…²îÍÐS{Ù+WIáÍÈ$^׌—L¸þ”ZSë¶~áksT“iÔ)« ü=€5"!z'8倞êê ¨6ÈŽ‹çæØ2ˆm*—4ˆ*žüÁa?üK´±“Z&Z IòóSyØ8]N‘7ÀI“&ÙûúøG1ì@b™Ã ¹‘géK1öæ#A n +ðt#$Ñä!PQ¼ %•W¡„ýžóÄß¡?I—ëÿ|Ø¥'m*;%S( ‡ñKs’>Õß18ï'Ý⨥ ¤Ò!JjÂ`Ž,„€ÛLx°+ ‰e–»¤¾“C„…(!RÝ0j,‹ŽÒ=3|6…@'ɼ"Ÿ{ðwkUV‚(^~ß~Ó×¾—ARýô99}º3úú[¾'[{ú6’ÝQæ–{¢ø±.²‹Êéwuí’Úø¶ÁØ#g5�\ÞŽÑèq¸º%"ϯë<LFQÕŒ·—]‚b©ÔÞÑÄ—�Dv+b:çIºÆæJoôdž›fù!‘£æ"�QA+–W`èM¼i>“ñòCö9‹ËÒª~µGdó`j*>~ǹ&±êÊ7Ü4O§ULI¶ÃæÌɆ¨OX:bÓL>ð¸$l�ZZ¬¬±vƒðãéKë²z—VUU•²o0ðÆÚ_‡ïlV>¦0òPpϞы/¾8›EÞý1«Ò™ïE©ï¦ŸÞ½âŠë< Ÿÿ¼‹L-]ºTXVZš•*Ðâ¡À �uuᚘæü¸IµƒgÁOSé ps%3²Ü‡¸©*3zÔníZZƒÃ!M€1*>ɤÝ+¨y±ƒ«oÏZr¹„¿}—-¢`…ãô'Òë?«7ÃÒ“AÞ‚*rðÖm‚r I.kè#÷àÓº(»5“•¡äE‹=¬©úc@a!Q1¡f 1ž6‘ød/kG^3%T!\Ydšx‘g^T5s�…5¬™êè˜J ÉÕMçÛ(ï_»¸ÃÕdTý‹OvOB‘YÊ0üî‘ÉΔ¥ÚHéD‚§¸Û0ùnɤX' t;¶#|qìÄýž¸GI„ «SHøYC×X»70äã +ƒÚ€éWJJXF•ñ¿¿¯'»é#ñ‰•£ðrà¼X¯Ñ¼Ñª+Ö8/ð£J\›´±ÂäXz8Ð âŸl0ƒÍH>ŒÄG }vÜQ.õÕ%îú%•¼à†pM†bs“j¦aAaÍNc";¯ÏD'ôÙíÔù%––©ƒ~88bQÆ}2šÍ½cÑ\*=}¡aÊÁ/¡LNó¸¨E»ÇcQ^p–£ÏãÇBÎj¾á”h€gì à ¡¶ýx__“«â;-žyXóð<°?’t(šu7øbˆ£<רÓyLÕ—Q£´[‘»¤^¨³}ç;e¥½»çšf—{Â[áN·f%×Û²žº}s}¯í諮ɳ߈RÓ+ð9Y±öˆÎʬ5½üK_ò„©•ˆÎj ¸y“SBxßKAB^Å&usÀ‚pXx^I¤¡ÀÁ}9ƒ‚ò»wv>üm©“ méÇÏ`ËI—¹÷½žr˜ÔÅŸº½ÊBx}A$Q/ûÚ·ÆËÌ-G”ÚX¹n¸Ö¢}C“½›ÊóÛá:®áUD홉§(ªhvÃmT›sT¦E›GS9Ž=n.«¥c um©n=>2b la`Ñ7ƒI34ªµ¨ÚZ±x,TÉÀ•£€aF;Oµ ÿÎK?uK‰,27¢üîr‡†¾%–(n9àº`9í…HÚÞþÉÉl†<2¥wˆn:�«Õ Ïâød\æ`]^êœxá ¯GÆYpg—&Ó'ÈLxžë„•Å¨]☠¬Ç~?‹¨¶û§y×+¯ÐÜxÛ +ÝÔk>xlšØ}º}²,Oé‚´ü³Q 15÷!D`nÖ&ªp;Ó4b2œlÌÍpŸE}lš´gúûäµÁï„54æ`œ¥HnÎFÁÊb§×ÆJhÕòÅ==ßÉ°^§¤Fåi¼ÓOðù7ž\/&b….‡‚#oªs ·…>öaBŸXT݉~A|‡w=ôÓ¾åŸùR¢\,@„À7ikÍM)ä±äI°©ÝaòžÁ…—´ì€ò›Ã‘>0˜É r!?dd’ಡÃ;âÐ,£ÞD1”F%«¦`ò Ñô›1ÙóP?ÐïS V2G_é*‚ÝVŽ¼N»›O÷“ë|Øòÿøñ6&ÎÎÑèê]‘Ȝ㉙{Fâ3 ¯S>2•)ÙL`5ˆF½›Zû<szòŠ–˜BP¾I±u˜í…†n=×ÿ’læCF¤ÿ�*Ægg +QUÞ7>þ77 +’MŸ-¤]B¡W"ÞþÔ[JMá¶+æúFš‰µ†"To:xv�õQ7ü²>êÜÔ¬M¤¬ n°X²q”p9àã44—χä‘Á«‘¸ö§‡~c³è_c(¸‘,ûô¬RoÖ7ü?{X HÑhŒÏ»x„YÈBõŽÉ‚r/ÀÐV€äe¡ÁR•ø}™]` ÆvqZêýü*|Ñ`¸‘LfK0¦�|”Ð#F&+”¸ñ´FÒ(´¤”ß¹ºJ9Êx+ê‹;(ʇªÒ ²%d¯ß_ì´p–Ói[t<”.ˆbgy×á¶[NXKgp0H*¡ÁTa™Å°~ +:Ð(Ñ?Þ²n]Ö#ð$ ¹nt£îÃ�®ÞÎÇ™;Nx7V3’£ºnìÅóÔb¨äB%.Åh‘LŸ®[{|…áI-àiÇ…>gIé’bî–©ºs¿xë`ƒÓå>ëóÀvy×6ø¸@"G•Bð²ÔÍU›N_üº¥ÙSáXÆ2»¦ßr¢'©?N‘½Â«±i0PëÞ Å4Gßz¥7®ÝËX‚ FW{}LuÆ ájÓn‘·Ã{ýè)YÆÎû;ä>\4gNô¤ +…Až:$Û'§ßñ$%›mm’§?F +ÈB…$%¢£|ŦËëÏ<Úí³ìß¹xòZ‹šÅÚ¼4_ ôwâ$ŸÀ7K^i6>‹z¥pDÓô ò˜cÖqZg#áü<ÕS· +¿ð{ÛÁl(ôíؑ˧þø!ýã}Y|m½�Í7ì:ÿ}Cb¾ËÛØïò{„2¾kcÛÓ©ïMiüíUeù_¯)(»hו5ûçV”GQ-õâwƒØ^Zû›£#ÛGŽ»|>h=2½¨{Õ6í9|°§õæiv>‹Ô˜M¬•dsTªèœöMíÙœ|ì¬BsГcý +ºd?„U÷+Úà¢C„ÖuŠ¶¾ˆñæÖxCs$[j½19™HÖù"k)~Á‚ë\#z‚@u†3 V´¨°2ˆd$ ·—‚kÝNî+zHù–½hÖ/÷GWBöÝA®=}˜vw•cýœÁuÓŠÞ‰Û(0¼Ç3‘x\EÙ0o®$B«W¯6ÔÎýoŸrQQ‰ò¡”ܨª,J«]M1–pË%X|ˆæ¦uoëpMá†5ª÷?eß*Ðpfï½'L¹}Áx9àað(ÌB,®çä虊ÅèÔ³/´±ãä¿Ruù?ühEâ³²kf¢çû<ƒ'ËŒ³·B7À +p�P.T˜Y6Ú]ˆ?œåXÓv\IÃTGÕZ€Í–;§./äÉïuÓêÃ(@èàk"OÝ>ž¡¾ç´ßóûàb˜ Ö¸cªÍv*ž>Ýw^dTUÃ| ¹€ûA’ã7yö6¾7xÁØÌo(šµr*•nÎáÙTÊøÉáѶc'7?RÇMÏœ}Óß.ûÄ«ÈBL¨*mL¬›ú›Ûž|¤¿¿S®¥{‰_d†ÕháøÈ&XãÓ×þȽ´…tYþ—“ +¾¿ZlO¿2Ò#”—ówéxq1ìLŠ +Ã5”M3—æ8a¸g²üµ±0 gyƧŸãƒúwFQÜYu`fä +€‰9$v>¹V„ú ‘Y‰*ÎRÔVxE£RñÆJÂNÅPvÃ#ëØþÒååVÀ1ݾ a¡8TR©8w5¬äÿø‡nä&’²™ýë€]Eå‚žu¦&”ŒPD Ô›Så°ZÖ¼4ƒ§Ã[òö¿òÜ”³¤3ÌÛ´¯A¸ Á׉EÛuCY¡RÚ¼¯|ã–Ñߺ\Éæw *5ÏN‡&£nÈûÐ#”lœW_îóu‡Æ”G_×¾°¢N…ëÞ=ÈWoLŸ¡RHÊ3Á¯o“r&U³;都;b.åxj°°‡`8?|m£+g:̬D5Û°T»é˜òT V‡3ìgQ•vV›j¸ÚõKʤ#¹'쟓»ïÏ›ä‚YÕ"ïž‹ �·§øCB�Y4ÙHøÅ÷ߧQy2ùv<ƒ*J€ÎƒƒF†¢¼†9i Ç@õšy(•žc3©õÇ¿€¤Ê\—E »V8&+aé�€p™Y…g Ï9‹.«‘«~é²²H_ϸIL/O½.±'ðŠcØ[aTúµŸuŠ–ð›y~˜TÍ‘g2}L¤|ÁËšq[µµwŸàú‰k, —"¼Î´˜’L.g?v,O%9j@RÀòË^HSeˆ£ÏAUà\¼ˆwÃò~&“6ðí7N%»^~ø𨙯ò±‰¡L{Ú XBÔù(…ž‡ô]±b0—Anä /<QWÐwoee6<BÚºàÓ7à:ÜО5H>6:¥ÜñÞ|×`³Ã¡Ük#·IñDgtF‰ŸÍ+‡Y§ÔF%=ò«ðÍÍï¸çé{ÿg%cqŠ—ŒÙ…Îâ^IVP¶»G-*4›9¡QjšÎ^&¼‹Z8!?)®$Ÿ¬÷zåCÁ°WäE%˜šSÜÈ"˜»7“˜¿¿˜¿Ÿ}Ó—1ªi”z¦-=‰b¤|¬ýceN¡}`lH½×½_¥ä›ûðÁ]ƒ=›ž˜0³&}>if.g Ž +LÍKm#$qûvd‚Êì3áa;$Á-wÙ ÜðC=þ¦+ÿÿÒªh,q$'Ç}@–?À„z¬±Ã0ˆhî3Qh¾ÀdÍa°•p^»Ó)¶,ð¯Bí{½ +Ù÷'¸xÚyx| «—êƒ59.Û<à%IBaœ·@, E’’<K{÷ѵÁi·š´³Ð°$#¶ÆY£ˆ3Á±ª‡áÓٜʥKÝ]¯ LQ±ñ®#ë››If7¾±{<ÏN%{œiîÞ€dÕ:)vQØõRŸËa±Üµ§_$NÅ"¹ä"ÂÀ.ÁÜÌúZ„.lp_§D:ÑY$™/ ÎX ¢ ñvñy5÷ÐþsXñ7”šâ~˜„ØÅÙûtÊ‚kGÝ®^œr?’wJa±á\ű¦ +lM§»ÀaqÁúÏ¿-—šéÄd® BË—cCnXÍÀÌvh`À[ÀæNÈtf5½õŠªµP¯§GK6ÍòŽlÈ¥“GÞL’±›JÆp󆦎Ç39]bn÷ÞéË5c—!¡Î}#ê>á¸on…¥56®ßÏœZÐÈç±Åµ!‹áY³ègS•›)ª2+˜Zâ/χ¾7¸(ˆ)³Ì¸o; úÁXwgq4ú‡€""}2×2ÈÂsq é†p„Ku®M¬®]Ÿ]ô^„Bgy>îr™%fXÝõÄ_^QÛüCGÇ$Ñ«+w%<£µ`<§Ím=Ü7¹.4”ecâN;|>:J |e Êö92¾evLÒ›êó߸ãä=99³`è+:uNQ8P¶+d\’ûü¨‚ Ô@ÄõêhXß) +S”¦çT³6Gg +¥jE´(‚Uº¶l`i`<3`á]Œ`cµœ'šiBÓƒ¬õè6aœÄæÏdd2ÉA—ÍIéI¥?Íè圥'|¾D&ê`p;„U—‰v,¹��@�IDAT¬0Ê$ÅÔÖn[Š¹ V38e\aÚSâµé敨–íšSPD»~®²Ry}hÊÔê¡hÏ™¼—¿u÷…ÕŒ°…eò*š3òÔ¾7ÊËcO&¾ÁA!P³¯£ø"4%WÂú¸ÀîIXÆ„äSKJ]@/œÚIé»0§1! 3¯¥ë +L÷JÓdz•TæuÆ%TCF¦˜ÝOX–v/gú_h~—–ÛÉdˆ›››S踔ÌR‘³%£„ö!†‰cÄPß&ê9ªp÷êJJÙ“ÛßÞnÛ¥”ÛÎËG SU Ôj)Ô„¦[/`Ÿø÷X*.ÁÚœKüáîØŒvà`FìA®þi@æ⸓WPº[ÖÕv‘åï@E£Xß=UùǺ¦Š€ìX ÒVEìß>AèÑyëE@„ Ý$9F¨!þî„':wwÜAYP¸àn—ç¦. ’hZÜè무r—?×¾�Õ…2ðÓ‡,Fbcéá>$°™1ud(ædt{…$5§%˜9â{³¹ùþ6¸˜Ôor´Uƒà÷Ó‰‰þÏ]ð.ÅçGTŸa:ßÄâ»H6ÌŒný ÃQE]×DAú ’š—@U9Á²•™a™îU쵚õ4Nï‹8\NMÓ‚bTzSM'ÂS“û¡ÉjÀ¾>’ó³/·±<@Ÿê½I}úZhæ=ÐñØÚÙ.‰ñ¥ÒÌa¡Á×IB 4Üèg¿pó\”—:Žì:^˜LZ„d©‚zAÕÕ QÕºQ�¾Xé{Z¶þé ² ¢_蛑9Ø}6`‘Áƒø»Ïü§:ȆS»b¯gûæ0 «ì /7Ã`6ëÇXN Ü”Ä:”8F@ OÑà$ѧ·ŸDü^BɳH—vz·±=á¢\ý´è¥dcöiC˜LŒCtôÃÜ™¸yrOþåk—9³¡D;Ü©¶`lè…é6�-UåUðD1LÙÞÄñ®yÍMŽ|…å„—`ɹ™Mo +|ž§I›w"sS‰Òþ1Fn&áŠ3q/ëïKظ®î8:¤;&ߌÚü�äj¡ÞøI˜?@õPX0©Õ°„Zyƒºo,9‰…³(zç'¨ +++É@·šñCbP•‹Ï¾ ƒáË0µJ±`—YUŒs´ð’ñ–¦±"i3SÓâo¾»VŸ|7^4ÛqŬ +ù?ùQ¼ÜÁ‡Äœ’$2qÕ@ÊŽ¢gÀ•rƒTºÎ’ti•}ð°öqÝJ¿_]Y,fbg/PÖ”äu*,ÛîàŒnDX-=lÒÒuX"¯–w6¦å^�¼~/*¢éÛQ.ëFYÑ°rp�´ÏÔª aŒ¹¸KeôGª§d$ÔŒ›à,}ˆ™RˆYúc[¤YfÙ@Hž:œkl¼D58æ~úæsv“3Þ<Ð7¹¬æ/„&d Ö]\Qß“JJtpy|ãÍÍÙ…êX(Z‡ÐkMä€bζ‰Ò~Ð…]Œò‰ã¸v â¸uv§=ÞSŠþUI¦fûâ3‘ ˜Ë(©¡¥³ÿšï`ïp¿ì²;ÎC©ñ[…¾ü»»ÿc +±hjÝ¿Zé—ÝμÅÈ_^…ø¬&6Õ†�®[!¡ûiY i?¨É^4îϲ©½â0é¶e³g8U‰ ¤EJ™Ó‘¾ïg’|Ò->µØ‘¤Ï¨àEŽép0ÌC Bu–µ’„Tâ%5ÈŒ{AÔnOebíCÏ=¼¹¹Ù„WÁ_Y»V½° +½YŸ—_ z==zó§V¨¸Þhd¢G¥øJtó%à“®šÑxúõ—ßBÙr–¡òJÚøQ;d_y.Ð9kìE%ÌîW^Ú*K30d¯‚ÁRd¬Z¤’Msã´‹T±©™›ðÝ·÷eK37ÃØ9‚ñºAª£‘þžƒRpìfä´¢-È* ™ÖäÀ»¹O>È~B™_P¶ÉT ZÌq@QÝN~|"{ÂjÕªro÷˜¬•>—å¨ZÓŸ{dïÞJEYE!mšK ½:¥Yìn¨§p‘¨ó»ÅÌTÁ'”é y›/ˆLo‰µ d!úŽSI—ôú3 «öÜW*µ¬A¹±«z`Î} XÝj${6*šq÷Pë5 âäZpãݞƅCòñ8@8€‰¿3ubª¬F騖ü0þâ†eøéX’¹Öc1‚ïÕe�Ã;Žso¼-×-ÑŸÝ<«†ËI+ëßýlÀlWÉ]iÃÜÅ :U0EÍv?þ®Âœ'eÌgUV’åù»sG•GLûc´@39š BÃNCH Û¢°}Ê+/)g”Çà¦ß“Ñ©ÇD‘>;ö(Èrß4UØWþSи±&È^›ƒ�íK§û`%_‚çk¨®©Â:‡‹_H4où5Ó0?§z\ÎÓoŽ_Rùj<~Í·*yôŽqªø-�‚‹šh@èê)P'{¶%Õä(P¿� ‡[ÏÕŠ¹.*ó_‹ÉUHÚÿã‹ŸZ¡QœâÔ¢7}=bY ½ ¿#”²Š| +/ÚH^cðì-”¿Âé¥kX“±ƒ4è,“×aIF¼2z4Ã=2þ‘ÚýµZè|.°ó=ÌÀ…Þq÷²ô Ïp_ÔŒÆÑ,¾‡³aÓ2rºþ”@1)À„¾ã¬ï¨,»Š´—’1³/8£¢ ¼Â›çý²ä.ÍP#/ÎhXŸŸßdó2øÜå¹’èCØh#ˆ•œØ(¾5k᪹¹ç¯ÆÒt_|T^yQ7ŠYçùÍͶ7ÃáŠÁPQô߯Á…ò#0Zý‡ª³ÿ¡a£÷é?õç{ÞÜ\â=ZÀy †W:ß–Ép™rN†IZeÔb3•Q¦2ôžõëÏœÕKúž±^·ä¸¦šiŒo·®We4éjŽ™Æ“ų£÷¹Œ´Õ¡%“Aä€;¥•õC?² uÉ3-SFº$çD½F!B$Û,YÕ>üÐÃû¶ø’›£tý<xßD-õ¬E,"æ8vš{YÿÅ04ÞÖÔX"P‘p0¶†¥ØùœËYk›0Eõ½j<ð´hò»œ”ú"¬Hà]jç¬ï�ÒusGF1Säó§ ¢ô¢töóð>?ð|Fâ~„ÃwͬŠ‰á©þVIí¢5}KkGèT¹3YÈ�9sû4â^œÊ#;Á.]Zšùf{jïý÷O²ü0vàÿDbè?Q µTµZ’gbðO¡@Ûiž¯1‡‚ãrzøÏ#ÿ +Vîi#s¿CÈñ+†v¥¡S„…ñ^[’zž¯+ÙfòÌó°,-¨:$®A°9¿¢Éê÷ˆø(Š)xä6N¿7Ên¯ÐêBÔ?œN¤]Ø'²¾¥–Šk‡ZŽêŽÔö¡Î}O'â©$º¶æ +R Ï¡¤ÃÎNÊÊl b!C)–§Ò«ó<E3¯½ûWç¿Ú«$Ùÿw\çä/†Î½Ëÿ*”l_™Ÿï–þ/è£r†±Xý(Ê?×5ãý»6ø!ÛÓ¶•_ž•ï8r9—U*Õ0}ÁªÒ5kþf|,Š›‡"ç<Þ3ZÚ +2ðPQýå…w)Šö$žãþ )žƒM«á†_¡ ¿°³ÀLÍS5.1ŽÍžx;üš¥êPe»V™Äš(* ·"h}ŒjkËƪó®M¨ú·°ü‡l,ý߸Í{�d•¹ÍY溬5¦Ôl9>2㮓ðµ÷ê“æ÷Ê|¾—Ã@™ÐK(�Oœ£ÉdÕ&IOFFþÔWèÜƘ‘Al’à¹?®9?Fµ·³wÀ¬üæ7Äá©ŒY:Õ@ûý¢¿M¶x.Žx¾=i³œþ¡Ã5ß>èþ áM–ÑT„LR&Gv¯Ç4Åê$×ïk?|\UÕ-ýTüæ“ê7?�”VC�Æ Iêæèª6‰ ?¨ =šD ‘zZÐD8>t¸Ù{N>ÒÐÿ׃Xš#ÉA¸:×aq@¸–Ú¬§Ô§ì¢¾¡ÔO*ÄNP¤õº:Û 4jË’¦Séð–ðS@” ¼u…jØÀƒúÁ<¸ó]X�že%îqq²'aÊSçÛ‹ø#�+Ì0ešÀoyˆ|�Ï!{XÄJ‚¹H,°©•þìâF\ÕǺ'šì,»|(;‡ün2g¬îÚ6ܺѲHi4™À!,‘ §óXÝÂ…žƒÜ†ÅEù*Ÿey榄¢I,Ï-«àÅþ,‡±Hì¨ó¬ê~O�ªLFÌNÓ ßþÄÜüì ~2mIvëwˆÂ\ˆi†ä}âEÈdA6ƒ),Éñ^þÊXb+ê‰qœf“ü#Ø%ŸB…Øh"š‚ÅzBÉ™Üé3¼¤©"’?¡Ô—‚/c"– +´õF*¬°À€1Œ½ªyk&Á™±Åpù¼`‰gÏ^ƒû:©®fn\´h¿«úÑ@GÇü’ºŸ n½ÅR5ª +€»oC +î™èîÉo}Ãã==ëÌóQ‚Cx0¿¼p x!^°¥w‚<Þ\ZÉ”ËQýà–•m“öž~8,àd͸lúeTŽ)s;¶ïzÃñjñ ”1·/t5¬”\cä ´IÅ}q°Æo:z¶¶Å‚ÖÍ7ÝÖÎè5 ØÝ^o¡¤Ä·NhÆ°¨slëÖÃØ|³‹/fâR„Ú›�“ø·Á±¶Þ +Äx+æpšfüÔ`o„’;íÞœ—©––~ +!ŒÓÛøQø÷¾Þ€èÏ…¬¡¬J¦b z=Pn²Çð(®I&"yí d‘ÝjØÚ@>ŽqWëp‰G×_‡·a~þ¿ï«Ã|„ÞŸ à‡Ý?Æóâ‰C.ëÂ8úÒÄü>ã]Áˆb[ÚÒz¬5IÈ`àv$‡ïή[—×ëOð[‚ÔÀtã6A`áÖÑp‚å9™*}–ÃQþæ½÷l_oxCY™¼m<Fñ–J'„éñ>ýÝ3ýú¾-¾¤á†žÙÃÑŽ€Îè[’šù‡‰áT÷‚¥¥| +,~}à H?g'CPOÆ}v7⬖åñ„äÅ׳K[Žì_U¿è—¬aÜ}¶9à + Ös¥¼lªæö‰žÉõ뛳ç9zt('QàYlŠô¥°@×`â‡EËÜÉÛDªßjÆÏi^üÿ„ûK¬¦¦Ï|iµjÑ7 c>ePU×ERB¼hÝ”Ȩ̂:Æ×2QtãN?¹È¡°€ +Z'³áSƒ'È(�a;d~˜Ëâ*†«ymâ]{Z[û×æV ÛCóNŽê@�‚ûþlk®™?ùÖ@ô¾¤˜Qwïˆè³æçµ:s%Ö®c`<›”,æRFµâ!Gq1`dupëºz–“ƒÝŸ›?“”l¿ç‘½¿J�“/èí3Ò'ÀÁôˆ´Ö,f{!êv£žRžÖrØ"-&WRg«PnðÊàT›Q×ÉEXû}KË‹5uzAÍV èR.V—}¤oøu€îê ÏðRv!-g +[QuN‘C½þùA*þ¬©½ôìóÎ79º,d°;1ñ‘T¤i¢0{:c\¥ÏÒY«ð¿ŽÄZ†2Ø?ðÔÑûo¹%‹õ}‰ãXÊp§uó6òà°1=íªÈÛremüú`Ñ5¥,?öq¯ˆKÇKÁäôÜzQŠª.²N_,àR‹XlXÍ0f7ä7\ßàO‚ÁëÙmð<Qùl¦Àé1@*ß³cÿÉßœ™+ªÁdz’±³LŽh¿‹²Jn2ŽÃZD½ŒcöÅÔA +Unj6a^<™°®«ƒB8¬TΟïÉ0×ãñ GŠ()Ï–£·´t6ÔÖò~‘b‰ùatA/•lÙÕ©¾ºµÙ¼ÉæãþóiÇæž°³À-V.Ì?A¨CTv½ºKýÄ:’‹Òù£øJ–ž�ÉÈQ𻕋¢»�ïe-èÓNwFÿù¾$ܦ[<€l"h’I?mTvå»V© år½¬Z¶æ’üÑxh¬Äí–oõUeYuÏèFƒ?Çå0ú¶¿d’„Üs�$.Tn1˜²Ì%>¦ã«;¾{Õüâìg@û(Æi^ÂÎ^Ð,mYQú¸Î›¨ý±±í]G›U‰sM·íݯ’ºzãþ°“³Áßð÷I<÷Z¦í£+ÅsÌžåÊÌüÌþnÛ©RÅ;O;8ŠccV'ÐÝ(}Žkô¸iém:+îºsnm¼#˜žKó\OK¹ÖkÏz�ä•^Iž âÒF?7M~aïy°>îÓTú ¯E¥X,j7Ô5¯G4ïÉqì™a³ýîI"v'3¹ uÿŽ[ȳŒcXM9EŽ¾±øíèW0ûsXuônÆ„”ûY~Gnðò/ßZK§%ÊïK~¬(oäíážž55¥=å)ry£SÉäq‚xŠ&r1/í.;JEaV±Ïß³¤À[Ô:nË)/w[ñ,Äiu•Ø̶¶êú¯4Yœû’ñØÔ,(Jn~˜ê«ó `?™.a-”W1·cþI"¢©DŠ’MFTM¹=ë Ýnl<Ïéˆß‰ìLÉ ²ZQÞT• +±n¡kë¯}“Ø(É:MaR–ö‡ÒÅ–Ü$‹MA_™1…Rý½©´òÇUŠ™o£åèì³ +‡›OnÌäû¥#Ç&)Ÿùº:M÷o}øç¡;îh6£i¼÷ܧ@Ôႉ3 b*|lÏ ˜ë>ÀŽù×KƒÝ+I¢ÏÌ@Â.‡H¿Ä¦Sír)Êá°çs¶™Z‚)MŽ†Çò™îrM2‡É3½óo\|R¡LSýêñtlkóáÕ/?Ÿ9§læ9(šÁ#7ó2œŒ°m’œbÐREK¦Ï…nmïµÿÓž‘·ß×Å·YåεÊÄ'²_òè3€¹Ô1åƒa-äA¸½Ã}Ýÿ²r¥íï·9³P8PÁ2öXU7ó7ëNŒ¥‰–z(p{Û—-kP‰õtáu·Vñ¢ý"–£¯Ç„À± šzü;]{y~Þ¸½óŒä¾dx(šøß•h~פ"‰)gÞ �o_…©Ú¶ÿ¾rF^{[LÏEþº[œÝ`™œ«êPöµ4ú)T¹]ºY0²wrza¯Î3‡r¤‘JŸóè¾Tp__ϱÖY:/»¸‰&d†¡Ào€ì½Ñ“LŠíQ½¼{*Y½µÿø$ÀßÚ¾‰p=¥3ß@Ñ€!k‰'ŠT¹mí+sìÀˆ¡ ¢oç™f¸Šà ¦·¤ÀôÈ ¥Ú£ã¦ôØ}÷°ô²‹ÎôßöEÒÐä3TZ?:03¿×I‰ µ¹ÄÔq_#ˆA«`Oʨً†Å|R‘å¶Å\Í‹’-“Òó»ãiQpå~åÅ�Q˜ çÞ“H‹)Yóš8`sÑC©¡j¿¤Æz×Æwâ6°ò¼}âï%ˆFôª]ÙŒž„Îdê›ðz»ßì9ÖA€ïä|ƒ)mf"ìåOX4È0,Ï@z¬¨µ˜bâiEQv6íÍKŠ‹§,ëÎì}ñ4¯ÙyVÌcJ[¸ §÷æfpÀ£IÕÌ[ž6µ«Òu!êÖêx‡Ô«(òâ2sÁ!²¬s‘Ü\Œxòf`öÜÐøÑT® ýG²I§™Å`Ç;9÷ÆT»ÿÅA>ǤÊc&è¤åøñKfÎTHqÙ Éw¿¾aƒ>÷ŠO;zàÍ=î¹KrNá¤Ó¥qK%XnÖ¬<)åD¾óÏpܱ/£Ï6íR {>Ç·ÊÜöQÒ6ÂH×;ןK1JôîR§-HþÖ1˜$ÁóÁè''KÜ^±ÖÅ÷÷hžé +Ùi¹ñO ©tAÀ_€”ænÊ0Ÿ1(cI©#ŸÔå³mpÁ!u o +1ñÌŒ}}}bë`¨þáãeâ¬ÜÒÝŸÀ8«O×ÎÙÙ)ß5[‹Ã·€‘½¨ˆÙXÔ^læÇiJþÅ9E9»š›³±ekd hGÖýF*ÇyÍòÑÑœwßa²¸Îuc¨CIgc"ýøLÖå„ocubÞâŽ"ð›ÎM°ÙŒx‚㽈ÖoˆÕ̽ró±Éšé„‰7!f}‚þ¿8É@®Ù1|¼‹ê/@5‰Ì}9ÒÇß©õwaés +~¸Â4Nç!ìQŒpó._N^ï 89¾n¦FGé‰Öìf÷zDFA.B¤þLp%/|>á ožsÍW>óX×ä¼»6mšæÍ ®®ÊŸ›ê}©(=4@Îi“QU\M¯eô©Ã‡;â/ m RØ& +‰ lPQMgZy²¥Oð(íÔ-öZ*c]ÊÍYë%=Ù´¾ÑXi‰š/·tØç9¸íÀŠú¢0#'Þn±gÀ�OY~àYᬠ+бd,ƒëݺSŸÜeA‚ñe]ËLÎðUToêÌÙ€„›K“ß Í𓚬߫|³®›WØ%ú;FBù2Óg‡´ÌXgCñadÝFr.rœ?ËÎÔ7Ávµuàŧ{É3 ï—\uÝx —BéZøC‚κÙJe–R‰g,û#äÁ·`AïçI;mÿÅèìüì¤$ßý(d®ñ@~Úe¹üí`t>Id^�^c„Sö.ØEÓ ;µ© ±‡w$x~#Hf?ä’ϬÎÏOÑ"#ƒ3Á„Õ© ÿÝßÿ 'óƒp ýpO(Xú®ñøÚ”i¿¨$‚™ë1ŸÓæ>A`ŠÓí9\Ú¡ú•ŸIʨÉßFÅd˜ãÄNzÉiеNþÃx}ß_rÏ-ÀyT“3¿Š&‚Ouä:Þrç»7Ë@1vÎR„I’–)ý°ÎÐû –VjœygSvñv"\¼jÕ!Jæú"ocF5aü&ÀAß3׿âô‡ç8(Sʽ™DâGžRïK`¦'.Evò®‰³ŠbÕAðzç™× +Ô +i×ôáp» +l[‰6öKzæðWgÌ�Pû`:nÔýb®26‘›#MU#தƒä=â7—VϽ!÷;¿ù—ßCÔ½T╘)ÿX6õ'QiuÄãHFîæsC(‚šDh"Ìn–N¥ÿòmˆQºr!$23â('èm5r‰éŽC¢à;0–¯„E\§ÓÖy€¼}£aaó¹ˆÕJ'Ï‘M2.òû”‹C)ÁCb<ÒwÍÌ™Sw¬«û»ÔÕ‚D\Dg^Lq,ÐA-8¸üm¸ïëçEµÆ%Ÿ¬_¸íÀDÞëçWÇ_¾v]¼jù¬uAUi™š…frÄÆ9ÎâDl©“ãCÇô:±øÆwå¶Fâ©gR²ofúö/¬\³èá-Ï… ¼½¾ µŸÀSz +;Ÿá¸|Ы=>|dwè²v¿ë ±½ó± ·VyÞp¢b0[BÝÅO)¦ÇS¤¨D„Gi^nŸSZà‚ÚÆn1aýˆbÔçŠ??ôø=ïuîw]êŸúW y€r´¯ÔY×…S¡ôKá–¯>Á]€8÷Ó¨4}ãn>ÍoÉÎtú¼'Ò¿–ª‡¬Ï›Y^ž5>þÇ/½Ï óc,Ã\üöxt徉øâÝøµÏ";pÿß+ÝUXt ‡ÉÓ0àî†7'Lz²ª,ÙV,)q…=Y(YeAêóYä×UUá)£ªÇ`«Ê~øCúϲøâ±eÀ…F×H3¬Ab•DÉØÈ%Š.…8’xaíf«¡H‹2â“`§'[¬î#é×^{¹sß…ØÁIRÀ†ÑÇPôŸ^:÷÷k늻NÒLäÂÞÑè¼DŠ¹I£õÛ�s)×Ëh²Ù×Þž]<§ûØÐ5l]ø�=bJÎgÓ#¯zTz(í8ðÚ¥bHöãPP¡JžÂ Bdž/ë6~SÏ„ÿwÇzˈEO>wúAîßv°domØxªw#äôƒ1u3,¹¬+ Lí�LÓ,3ÌÌ€ááû4±2ñp.”uÝj³WcQ#K"S¾…iʼ`´zÈ5=ËXÚo¿|ÐǹÕUZ_ƒï3{€£~²µ+(ƒö\ +5âÎZÿá5sk²*y @@ÙTùw¦¼Z’›‡Œõúáh4 ¨‘ÕC!XʯÃ{¹Ðâ¬Y*wQÈÃ-yº'\òtûD㜊ù% +e6£h×À2†cÂ]�—†òÑ"/ûÁ·›}ÿAyªÑ +Ê)M+�¨I—õo/;ï²³ˆ¥FÁ½å¾Ê~æ‘lÿ…Eón}"ýçóšVW¾ËöÉô©N½’çF¾7ý†ñb°r™D9š*É3ªéÙÈ=ƒí™·CñÌ•iŠ[7áIÖ×”ö.‡¢Ê‡•HšnïûñJJ©!IR ßB„t@µlÉvI’ˆÕ˜‘ãcؼ둇¨à³z÷øTýöP¨¨%t’ñE>ó÷Ú�8À�¼Ý–´øàßýìß;ÏÿöoX|QaQ‹ý<³ï£:íÚÑì.]5ïC¾æ?SNêžÎ®)ŒùdR‚*ËÉsÔ’lbÜÑ ÁêÄú[MÝä¢ I‘À•%øšþΙ~}_c¾ÓIÁ¾¥,(3cÝC}ƒ¤‚lD5jÀg[[ôö¼üœ.ØûÆà”ÆJ"·Zcá ú�=¯ç–å;8ú`į&ܶ=¡T l.b,4æožÜãKfS§\¡vÐW:„¼‹@>súöp7Ó*Ï¢Äð÷>Á<°²¢"»ÐM·ïð@œ¢E¥†Ce/ÌÌu'¦ÿFäg¾ýóñûBCîåfŒ6¢M¡„zÈ{nă5-íš®ì9üdw¨ä¬’*h}Ê×}Ý™húÍ':SM'ÈÂR}vi¥UÄÉ–ø#ÈL§—–®&÷7—Y¡Ê ÍšÅòl‘Á¡þ(0¹ÞÂÙàÓ½¤âÝ,ûcefÞ&ÔAÄ[‘^‹ãs +øÍ[w÷±æâ¾²œÌlc/FEÓÞþ£;/7éÊ\Z¨E‰ÞøÃûõ†Þ^ë÷ˆ§Oß+ymÆ}‘{#@ã9ö~9›¸aÎe×@Àå²Ûh»ivÊ4} <¿Ã¥ò lÁúf,„ø(X“¬EÈA¨B{1xänÕ[?¿‚ÕøR<ÏjCIZ5ß5KdŸ%ا¼4—wyZS}ð'6Cùp…/ȲÙh•VŒîùSçX•]È÷‘ÐÛýýþÞc£zIMŽÍÅŸÇ&d_ëøÐàéÊÑÙ ’sØŠ!}ù*ÂUýáTÑ0ÅYS¸ÊÁÓM ͪ°óôo'BúBÓ¨(Ì·%ÆìrjÉ}§Ÿ‡ôÁGù ch0šÊ¡†Ž†)%%¥Æ_¼ÿ¿ä@Šò™ŒùÿÁHXDsÔ<`|ƒ¤©I°„9l~Á¬¯|ûûU#±dIoÒòÆÌ´Ôu|„G-±ET¡IŸLFuŸÄW@P%³v¨'z¦û ‹/5œÚÉп†A‹tâ-sXþ6¹*¼[—8ÅÑ:›M~î÷¿×÷o~aгwx°Û_|6í,ªAu:½ý|ÃíV¸m“Ýñ” ˆ‰cŽÖ¸lÙ¢¯c| ‹ï`$0Å3R\–•÷ïD‰‰äà%vÐ¥O9„lLq‹¦š–ŒT/eôû +Ü]ÓI·éÎ(EaFw*Â^ rüSã‘Ñö‹û³;ÜÀ@Ô{(m,\í:HÉ|aă·')óŒ¢mÔJ}‹ìvaËVÔí?r„«-(ÐÍm›å¦²Zà+YÄ´Á +¤ÀÜä¾õoÿ½…!ßÂ^P»àL(þêúâœL¬aÍF‹Ð|¸Ç÷ê2õ2”äÊùþRÈøœŒñ-`[hj¼ÇVáH›=2'øú=Vq»±øV‡#‰un˜=›"ƒkwH³}%’„æâ°ðÇ,š»4¡ªŸ@hdnZ³ºVx|ƒÏî ÓÄŒlbõCJŽµß‰ÐFs%euÆÔK`.™ãÙisÓìHc܆…q9£ë¨ûÊYråÎ@¹Ø¡-/ßÍTFú¨' òïn-¼´ª&Œo*¦ê‚Ý2‚�«RjŠÏèÌ\‡‡%‘Ò? òœí8kùZŸÊ —±´á@Iw*ÃœK¶ïÉ4ä*¾Òå_Ê" ÷?ãrMÊZ +烕lŽ«:G²‹tc$åê¼eI}zÔȃjHÙyÁƒMÒÏy<“3‘êJ¦V…“©w¦bn^Ú@A#î�bÃŽeMw�:Õ´ÇL‰~F¼JÖø‡>;»"HîïÿOÇp:#X³ÀÐ /:ÆÕïf™èFô”¦«ì•(wo€™n2«÷½Õ}gƒånø@Ά”ø¨½,‚#Ød9M`þšÕ?÷¿Òs¼¯hlÌ óËk$#MË610=6Îdß%{"²Ý³X¡©¤üKº"ïðJ.†ñ†M·ðúïüÛÂ*§0ø^ãï#ÍbNœkš Ì-í¯z RDæÂKúûG;’X NÁ€Ø/Àì|åÈ[”é†[P«Ê%:Š½NÇÅÈ|Ï…*Ÿ6´9ëƒñg7µ¸ûáŠ*o齆bååÛ¹a¬vÈx[Y±¾Þ€'v`ÛÁ׆·{ÝËYZaq~Yâžã.Á?übãM} R Š£Ž�Pð’¥jopý‰þÍ•Ù‰OîX³[úc v_ )é}²½=œœ»r“ÍÆ£bíãIZ/z¾/4¤”48€Mý º,ÌÏH¬µ±½©:kÓŽ¬Hä¹²n¤ ÈGÈEðnËÓÐŒÉÅ6¬«CIKÔ‰¼aOCaÄlEW½¼iS€kÉZ·,=¿ñ¶ñX)Ͷ_[—o2JD¶q±ÚàGÙŒÂøV SÂÌ_†Z²öß»}B®M6÷³ìlÇåµ(y·(ð 0È¡¡@@wæÕŒ²”˜…D0àF”X«3Ú¼”¦®–šVÜ}¨ˆï8Íe÷8 ¥Öxo?VËøø‹†.TIëL£ÃTR«ñØX5#ÓIlÐ.᪪Â(å:’_MQµë©M}}}ö9g!|þIôíëvF:f…ñúsr”‡;;ŸL5ÐÌ‘�sC«æѼ|]½ë®Ÿ1ç|î+åvšYˆ$íÏmÈY»Ðâúfû™l!©i×¹û<BÁFe”·rúúÌXSñ³iô‘ƒç× Úïsð”Rª©ƒlç×Ãác½°õ³ßÇy²qEÕ²Y¾ÜèHd:>>ý·Êk‘芅&áªÈ ‰/GÑÌQ´Ý8;77±s,:ŠÐñˆŸA?ZXÌWÐ……x†¯´}�5.(BÍzX•À¾ÒnVÚæÌر´ÉÞLÆdŠµ9;S&Utæ{„T¨¾=y†Hhumavã$IÆóoøN™[®Ð }õ¦]#{×£"õ=[”™]8ˆ{ßœö–ÉxÇ8xÏï~Àoþ˯C¢‹AA8Ù†KU×-X0…6®tu]‰@$ÞúŽn0+uÕ´9²ë\ð:ÚêV&ïCA¨!-Ò¡„g�aËl…ýRot @+ųW?…÷Ag× BòùÓ´Ý®gÌ¥¢˜; +›…4Å-Û³ù¨Á/yèœsKQíU¾—“YxwvªeΚÈ‚åuí-‡ócý#JÎì_F3ɵ¨ž«ª¢V§q^1ÖߢTúÅÝw”6››AJŽM˜Iº–è0gPp% | aÍmQ-¦Û!˜}Sý‘Èá®Ê—ø .ÃçZ ê#èf§55uUjîjñ1¹ž«4XaŠV>Ô=ž]ß%Ýœü¢„ÞØÖþ§TÆñ*|ê`/‚šk‰“Jè2Ë#žŽø èûѵ–EbÝy¾Š‹A913£©‡ÁœÖscS“þ|_xâx›1ÞÔ2ôÀxàäMærT‰Üé‡PCKûqªj¿8ˆeê'�ôâ3'‹I¦»/Û«ñBµ_¤ý®»¬¢‹¿œôÁÐÂ,uæòS¨$Ì£\<ä–ݯ8¬BS‰§ PŸlÌ’Íñ‰$Ç~R¢Í:—ÅÞÝìÜ~ëIDÈõ ¨àèÀ–µ PåeÝf>wAAaʸöF¿u”§Ð瓬“?$Œ£Ë¼§‚ó<§¶+&eƒ×�úyŽ×h; ©?Ð:~ŸMÔZ<_®fŒ$øvô6ä·ÝAågÝé»°à‚†Wzz4GCÓYNÓòÐîhoû›Ó¬YÓðQxZ!¢ÈSØyÓpÆGÌdc¶vLDw@ü¬Ó`œOHù”lLÄç!™|!6÷› +Tõ´Ó-ñnˆf6 |ý*P}Ö`³_„jC²ÔFÔd<@;<g%‹¡åz¢êŒwI’S[ø$=ŸE-|•^sã×#åv úâŠrOÕõ^ kªÎ‰Ædâºbæ2§Â‹ÓŸ%ŒŒdŸþýL¾Â`üÇ,@vsx‘ BœnÂV[K,&’~1)ˆàåpÍnÓiö§&ùؼ@2¬¬J³6¶µ‘AB&8™4¬;ÇæÁè¹k\%³b×™ÙnmÍÅ׿vŒsQ öu¸ÿñÍí¨|ý‰Ìr?ÓÚrvÍ+D¬÷=v4ËçñxÏ59E,P¦9‰òÙ[¶¾0l +*Ê|Pÿ4JÉWTåî©hâþCµ…Ä¢#í +Vá´çª†Õ™Ö3a”N‰[eg™vN×îÒ|êû¶ìXSâ¹Ú%—\ÂGlF¥ZÆŒubˆ"çyTš×y‹æ1 x–Ø9îVß}ÕÉ$ùTpÿb0ÜØT1áaÂo'9á:¥l„G‘îÇ“~òÀ ½ì™¾©Å–£øìþ×£?RØ8ãycòGö”n¬FFûüðuoþ¯<‰ jáúç‰yr½§zÇÊ\KοZ‘™¯ÚŸÿùz‚ÈâDþ6}I< 庱)o"$‡v›™øÐÄ{T©‘Ï}‰Å+j}£c;<w¾Ã3wfxx~ž¶†ø¡RŒñź²™sÉb?}n“÷Ê`bûd÷?´¡¨(Ô20�h¸8“1ç:9úˆv‘äØ’úzÈÿÅZIE@¨¼"L™í[]šVÙ/>rl²+ 8ÁsÆÒLïHtôþ@×C½OæE{t²“dÓšÚ9Më^E^†²Ö"C¼}~Ž$}¨ ˜é>ù_¾†ÁpÂ.Ôš§ ‰tò`4ú9‹<zNE¨;·¢ìÍå€s6ºÛ3 K¶Q™=°9æpœYN+¤Vè—HÑ 1E.Ï‚ÌJ²ÞíBïù¤¹ ™R– •#ïó}€%¿‡‚¨›°®”#žýOSÁuõécjúÞ§__üõOûVÖ@RÌëÍâïO¾Oï‚œž[²|úsgúõ“ìsqKpN¡†Ÿ‡è™Ë9)o®úœvþ, ðB<O?0¶{kçºuë4`ñ$ª¢öc°ç7À5Õ–`!c³û½v™fj«AX3D©æ-¿Bp–vÃEÄü7wz…ö{Wb¡üð}ÅõÌl²ÿx\ ñy3Ò³÷ÇŠvŒÄ!Uéå«ýY^²p<úÚ‘‚yž”8ÆÉ•'Õ3±D ¨ºÚ"6œ¿ «ëñC-hM3e’…ƒ¸£[jÖDÓñ0d]‡¬¿>ïO!G÷˜H%—K¬ã¬vJ#·”#ù»2Ý[:‡s‹ó}y†J盂Ü>4î©ÎŒZΚu¬Å|ñÛ:p +ìDíù„a—Q,ûU ZJ›{&6]X[H¸'ðÿì¬Ö¼š2µQ¢ÿd ùDû胢èÁÏ°ßl�8ËPzŒè„ù Ãμ¼âƒOjÁåšf5ge(cªûþù™döŸíMx¢¸/ ÆwM'Ä8>›ÒÍO:YªrNÍ¢–Í*ix¡?D +i·yl/úâ—ëƧèŸC«¬4êqPô† íèaò©ìÒø¿×ù“»ßÆm’F87’TÕÏFÒÚ·õ¹«~‚‚•`Œ³¥s#Õïj]!äîaø[vo›Ñg!I',sx]ƒÍ'úƒ¼’±rº~ÃM Áé£ÄÿáîM૪Îõá=ï3Ÿ““œÌs€@˜ £EœŠÖY«b[µÖ¡Ã½ýßzïWí`oµZmëlÐjµ( +a&@„BÈ|’œ“3O{þžuÈÁ€øÿ¾û§»âINö°öÚk¿ë]ïû¼ÏƒG?œô +pãm1¡MdÔ[š~ò û~XW§à™RMኆ¾P6V*ÐX5¦+†�$n*ªãŒ$³øòh*øY #rÍoÂïýXTãª\:š-c+-J_÷ÒoN€žšƒŸÉ}h:Ó)'>‘`§UÍ:TÖ¸ˆÖ´EÍšÂк(žUˆ±¯>t8лb…M¬¹;ºˆsìÚ*¨¨´}ú¤¿äNClÆLL�»P¥ø6<à}šb>b²¨uÀ¦ÿ{áÌù@“úÎÖ,2ö½ÁR•U$Ž'ÂjÖØÅ“8ù2¼Ûëà<œíØ/ò»ÚøæÊÑ1˛ᙢ8Œ)pÃQ�4ÓR$¦Æax‰koðcJ4šEÕ²OI„X*ÇEe9ÌÙC1åNBù¥]c¹±P¤™nôÀÉ`÷)˜XÜÖx!‰Yøf·‹õ´B¸¨À,\,ìE ºXºÕÖyw‡ÙH“oY4)NŽ‡GwbQÅ`f=0t-U›öÚÈ ÅŸIã©Í—›Ë8 õ<öØ êà0ãemóªìPímñVÄHb`‘ªÑ¥8¼%gÚ@`.º³\3Ô”f 5´kû‰Ë—ÔÊÛúãþˆÌ¦bY÷šÃ0^i͸ս€Ñ] +Ò›¬¢`y¯;ñgªÄâE#Òƒ<©É“¬,_e.6oÅ + iImmâXMQS7øß�çÕ¢p£‚0ÇZ²5švÚœ ùõ#ê•¢À_R”1Ï™XS_RÕ—Tdá°–›ºMJ¼P3;àIß`€ŒÝ`-?ŽÆ"àpÿÓÊÑ·ã´C"ñµyË8ßÐäŠδ¬Û&�À0ÖÇ!(Nmì{¬pÍ»2ËuRŒ!ZsçðFí¼ÉHÊ]^Ú—ßcÍb*ænŒ•ŸEóQg¬¼tÖ,ò¤ãuöe#92ßÌ‹AZM5£§rO½ð/íë.|öp&J¨ô¶qí¹-G¨zª$·|ÜHª,…ã§`¢ì²òÉ#ÍãÑEÃ[JäTR]IûJƒnCìCõ¤ö€Yc-]©è¡+kÏ7ÌœäküIB£D ¡ÏYh+]Pðâ‹Ôòå:&*£qùÏÛek~ódç·øÞ°ÌÆt»la F3Îz…e~+‚Yk^Û•ok»1Ï–^Šƒ!|öÍ“_UpûÈ£'ÿ Š6ïø»’Û©YÅ2VcúÚ.ÂÊ‹eÈÿä“v~nûxHhiæ&Âk‹Œ K +J˜bÝíå.ÁÆéÆ÷Ÿ;ìíµi‚fÈþ”Ìw&�ò"‹“á ¥œ[À½À±tE«×¯ž ¤ ÿu�pEA¥›?œ€Ç§‡ƒzÁÄA†ÖÜ@ãêšÑ€õiÿu'cú¬‘"÷>`üžÇ¡Ãf�¢uÞ°‡€C¹BÒP¬¢ú�ÞËЖÉõ#’Š$Œà3Ë}LŠÛº© �±ÊO—±é“üè“)¥Z’ËìaŸƒC´–:ìAÂE¼p&!^Fº“‘9%kà{×½¶|ÇÑ{®˜–~™%¾�ªSQÑ>‹×Övd· dm…ä/“ºö>`yùP7ù¾‡—ï¬oé/ËœORä(bËN9)OÉò”ž·Íï·/ꉸ²µ¸/í^åbÏ™S¿kío_sÇÔ|_ÇcÆĶ�jD‰XDÉuRõioqCÛ`<ïK`¬º¬~d-/Wì’¢ÆT5焘EJšR;(]û;¤1ùOÅÁDEeV5Ä\€"°™IÕ„ˆ5Þ(�“P•ÇbIß?1Ô´è¹Æõb Hrñ쓧ªümC—Ì•é¬"·ò[3dUI(ÙË<ƒ=¾QªÃ<eŸnJ•|)Ÿº7cÐÉcYÓ(òõXW]‡þúi¾»/1!ZIqË~¸`oO‚ã÷x£V"NþÄ–ŽžTÓOU)W9LLRàYÓÄÍ%†û¤+×zøh‘c÷•U…=™¶¤ø†ýˆ”êÀ>¢¢¢“zIù•W +äÈÊOaé+µ¸ò½‹îzd¾?Î^@ôri‘ðz°Ü²’ø¯ÈPç§æÛŽÀ¨zöÃÝpjüªºº”‰§^ÐiÆRXlʲ¶kÖõ†n¶ +ÂÝðØû˜8•v€>¯}<%„ñNTâFVÚ¦ëÜ[(‹{‘aù?:ž<ó~?ï4çôûj¶ÛwÄð¸r%ÎÏz¯ÿÍ“ÁñÃF-îí +%ÑÎ ë–}·Æ‡3cÊ…‡¥æ#ò‘CCåÀVfY ©sO2t×ÁûY„À:è°íõÜçÿGmá)f4VN>=y,7ìëöö›a¿‹ˆÄQö.àee*™üÃê[P^x ±Yö%wäC¨¶Z4µÑ]¼ù…'Ÿ” Nòl=J +³mýBD=zÝ6Éu‰bêm3‡"–ÚH2–ª¼øqVÏqp?x ˆãRÛ"ñ%è'¦X,1âus¥®%Ìgˆ+v…(ýå{&[Ò³†L\tÂÄC5 ·åÆ!€cªŠwîɲ„̱`ˆ¦,QØFÖíÕ˜GRÀñV÷'uÿÍãGÇ6wJ¹P³òô’òÏÇú¨|‡I²ÐlÚNéT4GךçÜ÷£Kž¹à[6ö†åø³RÏ IɶéŠÌÊ ‰Ÿl¸ê,Ú<�¾ùžá÷¶ŒÍ¬Ãs$ýƒiÈ¥4”›î‡ËFõÚmûå¸QB‰zã–’Âô¾ýU@}üãéõtŠúñf€Ûõq¸÷KÌ&îÐÊÀ‰ÃS,î +¾·³úó»Çö’k ¸È,«;\Ëc@\ïGUÖÁíãrûÉßÐ}ÔêF¯E˜;�å;»ÃÄsç™ûW%ÍVzÞùÇÿü›Û‡Ü%9~—gJc4Ô³£7PÞüäõ£Ó9r<Ùò]&É^ +Uzýû7ïÝ„ñsKÍèƪN xÞÌÙàJ'þfü¿[VÝŒ"ù>ŠòÝSj±¤Jo(µ$ÿ7ÁH¯„ZpH!ÆT7*À>�ñÁKð�ßNq›îùÒ½¿ÿIÏ~ Esiá zfa,,ÂÄÕ¸jõGa mcQQ‘Aà›gÛŽ÷¦“˜mbØU<ÛÙ6a°Ð.Æó-|Œpbœí˜/ú»Úó%„R2r˜yÍ¥ª£=Ÿ–òŽBÌP¤ä75UZ)‰16Îú�JkB±ÜmLB¹Ï=qÚ$žˆ›4.*pué‚. 2�¤ìál‹0”ê¬u£§<×ÜL2§6"-¿5 Úh†¾KÇ8e¿yôSœNÙtÁ:Si$ç@aI"\ÓSžüÁ˜<ºxB,Éܶôᇡ¤û©§~Æ~”Sv¯‹&Gål1ÆŸ[èhŸ½ �‘ÈÚÀ "Jb…å8TÞºÆEÅÙ}sPM†s2ê(µ$î>Ê=Kl4;ù™5kÈ=§«¾b»Ö5¡^a3ÆDNôl¬oG|ô~wo´jë±ca2òhaÂ,AH¾LÅx§må³l¿C²¯{Lþ:Zñÿ™®ŸùaÂbf,sÈØbYÅ!zUÉbGñy¢jº’âïœäiP:–Nð#‹hwûÖ¿sbňÕÆbô·&¨L¨ ”x¶qª 8ÙÑÞ×Ñvj_ÚVC;‘‹)Çd· +¥Ykšö<nY—•KoÈ®eÁÓò‰Ý*>óÊO¶ö㈷¼ò†;°¬â9Á‘Ò&ÔÝzꜸO[‘9·?!߶¦Í©ÁÈ&øÙ>šF–l™¬Þú‹ç®·WMD5®ŠÝ;cìЊºOó™n"9š +2£Èzð²Ë.“—TWÁTIóPdÆîÁеUs/YP_ÿÏC/3×û*>shiÓ‡]ëõˆ¾m=4£3m`ŠäV²àù×áÓÕχï=Þ]çû磰©î,}†}¿VÛò‚ð…Ð}ÈûÝxá*ù�H ';cƒ¯§ +Ê*ëû"sÉêpÇ@|bƒ7\K’«™˜7©,ÄÂ.€·tÁ¦ÇÓ×·íxþ¥ø»‘ÙçËþü§/1r0oƒ©˜ífŸµ¯#obbnî Ls~/€…ÌXŠü$Tžvá1ý[EyyåÙJ#,›v£ùƒUQGÃÓ»$4sVš™-ÚËÏ|1ˆ’Àãˆ6ÐÉØÐÞ¼D`3¼ºyF[fÑL÷㽋¤\‡fR?3«Á`ƒá^Wl<•ÙnòsŽ;TþÜE“ ãÿÜRgðAÄÛ[â²Âj-à €¶MÓµ™î"G.1"ÃÇæaJrÐb½pÚó!ÿ½ñ53ædäk¡8HeÈàÆðB¼Ïv¤p7 tñYÀŸÂóTÕ`³‰êñ7VÙ#œöÙ7-u) \—•»šÜSK¶12=à²ðEãžOA¼‘ŸT˜A ˜ |(ƒû\jX7‘¿ ôþ6SKIß-BÄàô-ì0¾I'Ôò¡õè΄ØKJ”3{²î’¦“°dêÑ£Ö:´å„·e`M›q~§£ho±cÏ¢<gÇÊ•Ë4¨ÍZÝýýÉY6w.GénF“}ªÄ´’ã2ç$ŸM‡hÝÐê³å;/NѺ*2Ì/�uÛ–Ðõ«ÁÖv+×d6û¼mãÆliy¹ò!ÌïXCŽk Iº„ÓPít‰Áq—Çì§&ÑÏ;Ï×ùûjLúË—×*$‘AêöBŒô(B^(ؤ: èû ?WeÇ˵åÙDxô¬Kn‘ÒNØÿ/ÎÉWÐpó«Á²"+O )Ô¯¶½ëÚÞ|I~ +XW*KüýàæþBoƒ~ÿ _̤ÄNé) ¥XvÁÒï>x½ËÊ—h²´¬ª¾‚ûH_2c0þ©ë#Ì ˆÚ@tÓ#GÛHðQ€“,‘ab9Ä—4Àzô7‘m}!ÒnJ䌢cÀaž_9wYÚ¼n”ÍNh*±â=-´Ùhc¼ÀYíöÆSç$g&ƒ‰°O€·™œÎbxÙª!ªo�~¤ ²îfÄwN€Îó»Ën +µP¶Íݾ°ÌºñP¬Õµ«Rq-½��@�IDAT?íÜ™ý“$ÂÙÌÂ>§ø±á¸.sÛŽeoyá·¥<ŽA½/ÞëcHF™ld°6´dàfrLÔ›Pdò6<²]‹‚ðÈÄÉž›·÷†/²ˆ97bé´@6ÔN3+üV‘Õ7�#‰©›LÓ’À"‰Æc¾ãº*íNl·ÑOó¬3í%z¨?¯ÂŒf¤áa„8¼éÄÁÍΨö<(Lÿ3$ë)F‚>ˆÐD^”¢oÔÍÙcÌpÑï,ŒdŽ•+??>€ÄR‘»òú}±˜dúo·#w‰ƒ”Òè¾´cæ=„¾]ÎYroZ…¶ÌÎUEÐ,s›+‹AJÝ°kz‚²*b…ʙʨT¢JÈ^‚4îåNc ~ù‰–þòWðÏœøÿ´)'gÈÆ7€:}0ª…êÛ„Éï(ÿ0{ô½ÃçÎtÇ©ÏÝd‰}ìÔ«½áë.«»ô¦-HT²%œÀ‡àëîÂ<9û-;û4ÇáÔ ¾á?¨F´ŸÒˆ/Eéøï:7}´kVq:É™~g»=)Á,ì^µÇ0Ò“ÕÙöù*¾3Q‰fTŠüU0dm’4+£§LX|σ7«êåþ"Ž¦nÆ}¤ڰ✕d]Ó3í‡A8,åAf +³•Å¥Ö¢Ìß¿ìÏ:æKìM(c€=rkÑŇþºoO°n8IþÞJ^Îs\lÊ£À¶«»b}{X›ˆˆ Tñ]… û’Øé‰84…’.š+ +>l·àt´ú{‚˜ÝOÍÔÞ°<k75;#²(0.Ça”5ùW"%îuÃ{±º¶Zp°W‰'°ŽsØ9�ˆ×z‹¬î)uÛäÚ#7bÈØÜüx£eœEÌΊ€=mÈËË)½Ü.&oýå“Éh–å……òÇGüßýé¿.”yæ²D,5¯ô¼™ÅT$¼³>UVu Aä6˜R@³P¢lLÄà˜Œ%àb«< ^Š%äáÎ7#DQ‹Ôe bÄÝÀ^íB™®Dúeͱ#ƒMý]Ýwž?! #†þ¢{7˜’kŽu¶ %²+& fú];Åuݺä¿|¤äžÈ³ qwžs…²wŸøþ¼Irs,æ…jò3ì$ 3&' c¼Öd\b^‰†ŽÊðí6÷Dܪv/Lî]Ý!Ùr×þ£c²\ñÇ£é•G½Ó#JòF˜ÖYðtБ£2eòqj +J¾ç'roÇb:³jÁ3OE®ž8±ð| œ«=Ô4$"¦©:„@—9ö=ãŒ%¡“·Ø—µÌÂu1ÏI%R‡³EAVTíaUËsRÚL†gþ‘h}ãæ ™ç·ÐEoJ½´3$÷x '~¸Ð<LÈA”„ÏE¦ +h›h߶D4Œ"×`%ühfQþqœãs Ræü_×OB¿¸§/™‹íÚšûû5“h5tjÓ–?äö~ÿû'¹™Oí0â2îC.—yã@•zz"ùû׉˷åïÝÉ’b¨’Zr¬HsWã}^Åjêâ½X +k‘”ô/½¹pS•¥vó6r‹m¾oâ˜ó‡€* +;‹<h|¥‹H +þôíœÌjÐ)ƒî#5Gù¸<”¢jÎHbá†"2r°²hÑcõ#slêx,žfS©SKWÒÇBÝJ¹ÓøTr¹©ÀüVƒvRt˜Ý±ÇV5^1¬¤úQ±ãø¢žÐ*›Îx.‰&‡êáÐGfkw_G²cY‘-I0ÄÜùÀlÌtKRŠ1Ÿæõ`°+cŠü!ågvΙ’ÆC€³LÓ{öìI÷‡œ7ºŒ=±Ì`„ +žÕ$YÕÊl<åX…LiÆ»"Ë4U¥óm~#¿ÖØÒóvînÔ _‰ pàpà{×�•û¸hvÝËbŸëpÊ>ô{Äv{^„Êœ@nG£B+Œ¾y3N)ï.•æ#Žnéµ·:'©H’ +Œ\â“~"MØØZ~„K„ìâÀm<Buy†,ÁºÏ8ç0Æuiƒ¼ïOϵϽþ¡ßõóñ]WÃkíÕ¦U¦„G^'™ã°1Ý>ˆÁê'0Ý‘Ò-ôhìùOåQ9õÔb'ø‚ç]š§hÎ:ó1Ä*5•Õ!¡ãâ a0‹SÖ<Óàö¶Ü{/xÌSž]ºôÈš5kz¥qãº,º4¤¦•Vó¡%èOˆÊ±Ú”.Õ$Q ÀýÀ.„'3BHMýúP“E³~€.ù<÷›{ß`âGwΙûé“RðËÀçð=äzÅ„½ýs ŒëOr[»}QE÷Giù÷ó*+ÃûÁTI‘ʺc¤«,3'ú¦}vËìx‡ÍXPßÞf§Ûka'÷@ðÚ{{#ƒ(¹™¹dÕ¯Òc€ZûäÉV†6»Ôh<W„Ó¸©¹)‰.Còå|]C2Ë0HX.½ÁçW¶‘J´0óȲð¦vI£ÊasfÃQi�vû‘Qv äX*vÞ»î#/TÔ*[z‚›ðf]Wî +íñ%ÁÍA¦T”Ç,yðWQZ~NŒo2)!즛 @wäzœƒˆ‘R55dy™ÞdNi`U°UeIJµüÑ"0Û–ºžVéͬI«—£ŒØ¾_SëèèØyXÍOq +Ètõµ6PÄê*uɬJ{ñ’²dcDÅ®õg²–ˆ”šÊÄÇ€^« ú,Û¼Áy¼(\†*˜… 0sB¿jfÈ@jh“½ÂÝW[ŒÌáë.[¹’‰T^*¤äF`f±š>YºS¦è (ý÷£ÊozÐŽh*±piý©7x’Mß݉ÁÜ :Š2èi`‹¼•bĬDQäÙ¡ÀÀ~àyCS{t55Q8g\%åÕÌöÆWw/üËêVoŽÃb-{äÓ®ˆ'3±m%öì²|2±4œ¾o4ý@mèð÷È,U†éðk–¦³'Sj[f’ÙÖ‘?c‡«·¯£ì›Jl662K4¼dÿÁž¶`Ëùc0’�†š½Çm)½Ì㌪Œü2û�ðH²×€=êL.¬Šo³š<m¨dÌNm`žËžWJ~„ˆ’†#Fic§Ì3ôÞQÿ&)fcæUÛÈ$H½Y=›d4¯7Qü¯ãzŠF?\dbè&ÅøÊ8gE—_4ªKR@}¤é›ß™ZÐCžAæÞÖuû²-â\䈶M³¡´;‰·MåÙ*¯/ÒNéº[¥Ø^Y•Ò+¨”JOD-Á)TˆŒ©Ìy¾‰ŸL*U¤óP®ùSDãsw7û6̨ñþÁR»±Ðo(ÞÀsX¨.~ô„\FaóN-dÍb¯êEÇç(º– Î%j’›²æã}ò8ËØÿ2ûHÍÏÏþRNÀälÛ† ½€Ÿ‹zÍ'zþ6Ø›d¼£ƒ¯¶º;^à<Ê‚ÔÐÑcÇ ÓVŽŠ¥››L$ö¢`‚ª8ࣸW–½QËóþø¥{¿çÄø²‚)¨ÏœÅŸ–+IßpmVVÏ¡È_u» Áž2ôuºÆyƒ±X“5G³²².„,È–I(#Ì<ÐrTœ´ôùc‚Æ·f,‘…¯BÑFŠO©Eh£ˆš¯<ow¶7ä5Ñ‚†žÁ±ØÛ!Î…Ïx)ªu&a𵪱ÀòµR‘³¥î@51x?8ÿB<¤‰†Å’ƒêÁÐé O°7˜h†{Cg¥L;I@ÿƒ¾ àsÔ ¼nñÿ‚.;d-ì¤n) -×a)tÚ.Úl¿%öHDóõŠFÙ¡¢dµv¨\»‡\?:ŸZð_¡Ùn]†YyÚ|\’åML= ÿéóÆ*SˆmÏìéi¤Š‹ ¦‘Ø +£!@Åw!Ay!ÆkÚD‡Cœ<²ða{÷`•˜ÔÂÓÆŠLüwrkÒäšjÏ„¦xű›F¹O[ªfö×D¾®Úº®Gž0vŒ{�Ê-È|ÈfYK[øçcJÉb7*ËîE0ú!•wL‘õè{¥=Çv×UÔN†‰‡Yê•£ªâ2?a×»«¶º€Ä¯Om6+²Q¦z ™ÌJ¨ÆÅvžÙ Žá²òÊÃ}C¾¡Xî÷¾¶î×±ƒTCÐþÔý`2¢÷ôÇêÃõnÕÆÒÜa«°•£Ô×Y"‹ŽØÄ< ý¬½Ä¥Ã&¬™;(iĆ#þ ÜÈ}×wÍ̦i ä—ÈgXñrÉ©ÛöPÔ˸%EN%:i“…¨Zÿ+˜Çp.FäÝøÙi þ†0\+ €Âã¯;ATîWUæ ¹k.D(ÎñÇãËì*EB€Ž£ršëă26ÊÌûº¨÷|RäiªÙ^O[Ä‚qùæì*ˆìŠ[bÄ[?@ÐV£FÞÊ +6uÄK™/‡³*½2z9öúf_#…¡•Á#Œ!ï±ëjjNy”÷®ÛTæí°.eG;"‘µ³Š}³ššzªFOr‡£‘E´F_'Ó¦Vì{ÿзi©·¢Z²Àj5˜>«S(UiÕDóªÃÒ0£ª§ ÷Cö%^ÞÖ˜ÌÒva%—,ög©i€nE1ç¿«KÒ‡V#¾ã½òòÈŠƒˆ$uÊùbÁæ²L‘Ym<®n쀹(Pøñ'©ËFåÃÐå§ÛD®5r#ƒ~CWph0çËvþŸð¯<ðê«‘š+hOo²C¦~”±Þ œÀíªàØ&«á$ü¯—W—G——ST ¼Z¾k¨NaØ…,„A} +]ý…pŠ9¸KqKæÌÄ€sÎÒäG¤®î$" ÔßÃqNò‚ÊjÀ&ò ÿ³ŒÑî"gþk@ԛ̀ ˆwEú¶õÔGÔðqä>‡§(Á0óÇÙi~Õ*jw¦4xä=ÿ”Ã2 Þþ¼–_ªfö~L6ËÀa·ÊŠýƲҎ×Þ–‚4*œÿ0‹t»¤› Ã^7âd£�YƒÁoåʘ¤”›âõÇö +Êœ¥¤uFTñ¶Û<(þ£Ñ¥ôCGuÂ<%Ú©Žöþëiƒš‡DíËÞ%µ©ßŽxŽ™K°‘Á�Qc¨ípxfÆy¢®MCòòø z²#$Óн#Ù´l™+=)è2Õ@j«T\|Zè+sÎoÊ'I Vr(ƒC´Öa2ô]¨åRÒ©~Ü„(˜Iù¸Ô«µE±ìEZª Fy½ÉÖ>ô“B‚‚Ãâ Ij˜£•Ñˆ›ŽÇ”ôN‘ª?ÌAo¿’SrÊ7&ð›ÍGŠ,ÙÞ%¬:wÑRØžk’bµ8z׆Né‹Ë³Q|súj¦ˆ%¿?ž’áÞ3"sýVÅŠh©Æ/ûÞΉçËX²£t؇:ckrÄðcE·Q”O:3?*Ïæ[éK&&UÚó¯ŠWªEåãÑÈXÄï¦a÷5¡f#¦ÚI)éU¬Xö_j&8£µ ”]hSdзڜÞ[öâ‹ý'´†ì¯ò‰>Ú0«?ˆƒE”ÖnA”ùïaFû$÷õ‘j©)dÇá ,iÖ1ù£¡~Ë:T]Ëg5nG±[-±×õôôŒ¢‚9ôv¶°T÷["s':C8ô”&†w›7< þÀýXjO‡Ñds%VÀ‡'½„"sïj¯ú’ •ŸXÚ>ÄrÂbÄA«AòûrWÎ)O…#T7EÕÀÍǹ:P€¿Z’ä•K«scÛŽúí¼!,o•"¤½ã¼KÔ}Ú¶ÀªÑ íÜò.CS ®aÜŽÚ÷…Hæå9uëÄPL×Ežþ6«Q´:\hxÛíóåk†0 °F^,ô>™ÆFldrÊÎ*œlñ‰Õç»éóÛ}sŒ\~«=Åÿ*” œ'ˆ£‡W÷=oÚ»ù„cÚE :Ë|»ç‡¦5¯8‹$ÞÈÚ¦¦¶üŠðа‰4ýÚ‘ËDZ½›¦óßµÐÔ{½IDè—Xi&è´2“‚£˜ê«æx;)Ä‘ó6")G›™QI^iÆUSƒN'“OÓ€ô±ƒˆ©¡ð2hc8ƒ)8)ÌXu8væ²O½¸=ýdzÌfz™§æ+5.#ºýü#10õý¡]Šá]"|Ê{P¨oY‹·vØh®>Úº´zZ7&'Þ‹WSšÒ$ÒTJ1r4éë÷©1Ϭb10ƒ°×=úè +cW¬Ð²¯—UPð?n×¹<€0 Æ#ãz”_‘·$V4Â!›iÛDdç†I“ÊYU=ä`xú†-GІÏT¾ER‰¿')iÈfí¢ÅL<ÅS˜èsÙæÿ·s´Ã»½mT>o¯·È{œŽ¡v_Òuà€J™³–ö¥TO,™òÜKÎpS¦KI©#Ö8Wi +=³°á¸NÄaƒ™ng$ÐB²™¤ÑzpP¤,¥(œØâ *[²z¾BÑÙ€ýM‡|y’bgÙrøÃcó]ée9©.ë +ö †3K ¨µkÿ槓^^˜ë_ù»ßi!ÏÓŽo‹$hTˆ•šÞŽXòUâÉ‚GÚƒzooJŠ+-Í;<:›ŽV`J,ª»ìbŠ¾<FÕ¡¡èñÑ.Hjo€‘Ñw?ø㥨§¯ÂØ×͇:eb®ÉÞ°ÒNÔdÉ®ÕSòºð£jìZ 0•¯”ù^|<HªÇÈ>¨ôÐo{ø᱌IùÀœ ×ÌÌ Ð¸Fñù³gƒÉc!Ë£1J£g «cAa¡J&XÎcD·š'›»Ýyg‡î·Œ½Ü;ÕKÉ0 IÍ# +'¿8ßj ëu¡/¬°�ñp«S祆öΙ3ç3žß`Âd“àš]ta?9ÞEM(ÙpðuOßèZk0)çYþ +ÜU–œ[™ãàé ôHvă»oõœ$¼'×Ël×K¹«òò”<‹„=d¾ýÔÓqå,U†ZíhQ ‰$Û_`å¥0ÆI2=öhOo7 ɺeb™ÿ:$_¸ÜìÑà)Æ,'C%GòÏÏ<cTWÛmÄ}Rpp7':à=÷œ°ŠÝ—C&½üädîóL›ªãËVhÈ\û\~–ÛLáΔÆ#®‚ê?Þ&Ðü¬¤šJìqYºÆà ÖñÒKÎ%,à7þIo°og°¢¤cǯþËë5Kâã¡®Bú'½/Æå£RtW4y+ƶè¢<ïÚÓ¢®ç²ÉÿãsõE‚1ƒ5U*cüé×OônD;¡âq%³‘|4Mü5»"·aPŽ5Ù(þVè+µ•ÙÍðk>݈CqÍØQþˆD`ëF8;n `”ãþhW™Ëò¥&ω狸 ÕÒh‰BÎKž¨ZÜÉöÖõ‰Úq3Fã‹› é»�‚p ¹/º°ª>UWè´/©Ð'ì"û-,Ù/Suq-º)¡-GÌ7ÚþÛN.¯-O}Üè ^|(¡M‚xdAN6â·¥q§éjfy~ç¶Áø«†®|†âØù¨ˆÁ†¸¨#Åp|YK–NI)]¹Ðl¦ö'“¬+³0ŽAXq<UY€wÚ Ë5ÁðÜjÂ2…µQ¸áÛ`,ü¤è<JrÞúŽÀ>'ǵÆdÃC³úwÀV6eÈ?€±ã×ôÇV.&Hlkºx¾T6öïFŠziS¹¹oáwΩïè`ªE1‚“%çSÔA¬{šÛWQúôéü²Uë™—?4È™ +ÖGª!o—öï:1í²ËÔ =“íŒm>e(£ŒD(�)¦mæ:߶:O<W{ƒÏŠšÞ*@¶)©ékØÜÜNÒì]½½Ù”Õ6™’µJ?ll•ƒP9JOä¾FnDËHAà‰îÔl¹Ç´uÑ+ŠQ™ö붊ëïý5²Ïà¤nÀøuD8ngÖ±Cíˆû(s®_65Y‹Ä‚y²ÎŒûC°kíºGZ–=8KˆçW/þø[Vž+RÆ™‹lm·;•G7Öob³«Ã<#L'UwÌ º=*½þÊó‹¼ ½Q·ZPRC«T)–Í4JÑÇzjŠ÷]#4B8õÌ7€?~Óîaã×a–=}ñ™iÙÿªO€Ž€<˜2ÈO±àÐf4ã<õõ4ñøÉfpl3>&Ø¥HäoÆ +ôÖY6Ä«E“0edãë?ºysjÜÂ%q‹Hºéþû¢Ù¼¦Æá–Õo¯'Bª$$QT9¶K9ðFÙ#o ;¦;‹j'VFÓ0\X]¦·Ñ<åâL< =xGîÿEÿÌœ‹ôÔ×kil-gHIñêH‹y½^LFê^X*¥Ð&$9v‚ÐãE¡dZße‚ÌŽ{ëæÚ[e5E˜²'0,$cNÓt³&¢ª3vÚ0°Nz¥q´@$HŠwq"-@(pî:RT�«:|HrkQ1öGˆG €… ¶`¸¯�Ë’‰¨€9Zò0Ç6àY‹¢²²žO2;±é˜Uãd+9&q1Ä™@9ç̼“S´ä&|߆òÌLñ„¯Ü½»-ÏZßíz'E¥~DÿZ唣˜äQÑnÛ<.¯ïN°³D«Óx•bÌÏûv¯ëÆpבÑ+wØ<÷Q¶óŸTWÖ1*w=JgÕÜ>mn1oès00Ç<˜ŒûÀ'mêè/Yñˆ"~BT— ßï5C ØH +7-344™YÝš|#x<L=åò:ëë°ü$¥—ÞVËÜÖBí‰õ‡.ó¤ám8ì³Ûª;d‹`ò!T‚® +cñduÉEÕ$²’”æsƒþüñ“ƒfŽû[OS¨sd’ývµôfW9 +oQíÇ Mý‘È™o^pk¥èÊ‹ºU££ÏCŒ`ŽÒÇ¥(ƒÐ\[Y°·óÈ®—>úŒ9ÕûÖÒꜾ•˜ñ¢TY11Eé +Í´ ¹Ôðæªæ0™äÒíáÝ.¿0á&ç$#»ÖÖh4g¸¬ýä—ÿ[þ¯šP‚®mFâøoÑxê¼Ü5òy�[¿Ø±ÉžUóyü·dÛÖç¯Fâó^„zŠ ƒ²K(N;_‡nRSR@¥Ô<J7!¢‰™®5`È+–,»vç@ti²`ôrUR†gnNÅí#Ûl·#¹“†@€ ‘à—PR;ñlAgIôïK݈·ðO_<°÷ÚnÒN°×冫«ò|õ½"·Iøø^–µ!;''Š¾ ËÁL½E´™ËYÕX.x¹SÁë/(.†}ûôeÙ8x`Û§JbʃYf¶•¡j(Ä°;=Ÿí¤Óê¾0·ôÜHt©p’’ÊmÀÓî¡E-.*\�ú!Yíò%¬!ý~§ôSWæšÀ NBUÚlˆ÷ƺ7zÅù€«]©ðÚfWoö~HqŸZª#¢È•Ñóïxµ—`æñ¡-]M´ôó}Føøž×ج¼L©bÒÙ—“çZ[>±¦sè"Ln�À=)ƒ’4iÓÒŠÜAÒ/[û#÷#þ=$?F¸æŒO-Š#ê›KáZ+u¨:P)•+pÒ-oºÝ1bèÉý©abf�F7 À‘þ¶OæNœø™xÙ—l$ÖÜwËŒ÷ˆ€’Å5êJDãËACø‡D$ù– Tu’.!cÌ4%!=£tÇ7Þ^wºýJ}‡IËïÉ3 %µã`p³¹MôÑÞÁÏTŒËSÃÊXG9“*õÝ”®Ûú…xDuRy¾Üîõi¤:ïdkðP±òEëR] +RiÔ/ÑŸDv|œF-Œ¯«óP>_àóT *ÅiΚ ÂZ¼o9’®PÚÚÚRE{öñ–¹ö7å³ÊÄ1‰¿«¯½l¼ëÌŠ +pŠ|zoÍ‘ˆ;œÔ߇Êñ Z“_€zúÑCt<Q#äq€AŠ9n[~2!)Õ%6W!DÖ’#·/ü¿¼“_vß4G +ªÆ§ep¨Êii$a„±ùU7"Ì6«²N¹¬"PýƒƒIý/“‘8¿À¹—<c2†Àù}©Ed³KêÚRI5Ž‰,J£qJ¶ý½/ó~ÎIÌ—0 5ùå§H„¶ŒW-Éõíå±PÜpæFBjlûì¼<\& ŒÚ%Xƒ_Šå¤{ØyŒJi%ÐîND6ÏÊ:ë˜tJニ C,$}¨÷¡\ÌÂð=žÚûÚ?‘†J=Šý2ÕYä²}ðúËÉì1“Òl†¢Ät“~Ø\ànírÉÉd¼¸[7M. +…Ø¡¡”kPŠ0Í*ŸÓ¸úÝäâYµýò€©é…u¯«5Ån¸{‘Ÿ¿ØÖ2ÖzßÙüIÿù£G§„å唾Ç)öÙ"ñN\rŒÒÂèñ¸la? oÝäýþø“•EdùÃåæU¡€À…*¿½fa |ØH"êYa¬Àp9¬€˜‹”ع·2¿Ÿª¯g»9Ëx×·ƒ+acžk§ƒ›ó\æ&MV#,ÃŽÃøº�ƒ°Lç¡ôœb[{Ûc(ÎÏS, +DQÖ3—Vµ>E2µ²$ð(z¶í(¿í“¨›Ìy]¤[†Žx·Z<–TËê¥P!ÉÕå,LÕÉ„ñÖñæÃ?¬›’ÆÍŽ<_KÍJª÷Ûî-·‰‚Ô“¸Ímh·L™ø«'#ÁlˉÁãÍG9%^ôëxœo!Ê}yIJÕ8 +DïO<‚ç–öÈÔË‘ˆ£©º_6’[æ倉ŒbÊBðVU°ò=6S{áp_fÚ’Fµä•|Ïã&Tæišz* ÑPvHÕ›sLÜ©ê¸Ì1ßÄOp&fÇÚ·¡"¥XaÔw‰èÖ}$Õ„ä~rMbêîG0"ý:@ÍÆArª:Ëàk-9O‘ ˜Ë Ís œ†ùàž>¦iÌ/¬…9 èÏôÿ:ôI®‰NÝyÏ#þ/û ky×{äX;«VC²nìFÜðý¤¯km¹ÝV 3ÜQ »¹È!&Óc(¦„e]©Àb™ý0V¤yxo@aË›C^0,žÊë|Ñ÷{NŒ/iä‰H´\²N;%ªóeãŸÄÐþMë;NÍÜÈjxÁ“;L—cñB!ö«¦¾gLUÁ!É°tuæ˜Ìg9’õÑ⩳…%ˆbϱP”ÛÛÛ1t¶ä ªWôì9Ë …W:çæç‡^PBêTĈGƒG8‹çø°ÎUÊ197Ë]É{«ävSš=;JǨY¾2+((7&Â{Ï/ðòõôŒv»Óƒ“$+f¯=ž*Å3D6› Eö뀯çÈÈ8èîD‚s˜ˆ29F\ß;Çe +èU8w•f˜¬¨Ê¹TÏ1UN¶ÄRÇ¿ï¡Ô.Î9…„!6>Õj#!ï¼jtQâOOˆ¡Ü*„S 13™Ñ]‘”¾eœ-k¬™Ë£‘Àü,xÆ�$>©ÍÏö>Š‹gúuä'a‚ÍäwŠv4dJ²á4˜VÙ—å´uÚcRWPR „;‘c)>¥h)Ÿ¿ÿöòòt•P!^„̹Hh&bí3¤¢Êà/¿5ªlpb–)2*Ûž@6Ç FµÏ³™Î57n}ïXiIM?Ç1‘”œ‰Mw$$í¼ï[KB™ó‘ÏþD¼ªØ€OkGfäyHüRGåoXÿƸ›‘´6u}4©Ø-<.Ï™3àŒ'(†jDü-LÚMà™ƒåælUWÃG½{HÂvä1ß´Ÿu‡«IíbL.㓆<Èü%H6–M˯Ú^î:™üÅüEµõù»9¨Q¢ë¦1} +ƒfR3ÑQ-Æ•È?� Ânå(ê÷íkæUå}®3ôUô1 …Yf)C—IÚ@~>î“dA£éfu7°çÈš‰„Û->¼:Æ0ïVX…²o¡MHÞèhs ôAM(Mž>C5«õ�[d7µ‘k|Û9‰ù’† +¢9fæ™ü�¥'cŒê6n$NIÜ}²‘ýÞ›~4&=‡£§8E~„dz-/‡2¨|ù[xSÖUÍC©‹v%g‹KݨªÕT*ƒ|§q/XÒ&€l&Åf=6L€C®5r#a"ÓC”-F.Å�*ívORãtŽ @¤¡€‡ëV½§ŒSd�ÎçB5ø¯’®?ƒŒq+R= ´ùˆÅ’½èx«™k¬Ü:ÓÄÁÇfMýµÜ*¹1¼SóªUª˜K˜“1tŒ1¡xÌDÌÄ—¢D}bâðŒ]P…>¼¬†’I? Ž·È¹H/æ‰õmÑÉ_ô@ò%Æ%ˆW.µ<¹ Bê•hü% ¢ŠX††ÈdÄE=W¯Q?´ukšqêÞ2í&ŸÄ`&ÊŒ€¯ó.pµ³B@;L?šKgøQMv±'ð’þ’•é_÷wù>ªÃü·ZXt^iÙu#´Ù.\ö}‹¢S3f£¯ÊR“N¢â$4{ÚµËÌôEK]3ÛLù¶˜Y£ chsDÑ_gÇæ“,õiû¢"r .½GsÀ|“yÂp˜ÙôÝ.L:Í•E¦S¡ òw2î"ªºy�Â?òWðgœ R +˜X²_/<ÀÓ®AŽù&nxÉr ®ZF1´MS8B{G„BI¿w™{ªSàO"ß‚2Ü'‘ƒyÄôð}ŠÆ¿¢ÿþ„™íI&™øOÁc{oÑ°Læدó' +4K)û=Mï4™…«*õ/° +Ú+¨ò"²Ú$íÇÃ&“~ª¹ ¸o¬àZ°zþ7'°cΟ_ä=§t..À$¢IÖdåcR\d1÷Ÿ<+IòìˆL°;…At@'0°°·ÔÀJ`ôÊ¢Q— ¿ê‹]AÛÌ@0>�ÅJ<¶@·H✟YòîßÑ°C½ÁD‹€Î€™T£Á|%:&ïïp¬±©IRÏ~æ}‘ŽÏ|G.dŸ+KÝ-=VÏ̹e²UG¹„Ã:ˆÊXI`Ò©ã_|1uÑ=÷ÿ%ìã<J™ê„¿^æ\¼Þ¦ +ÆjªoöI:Y2Be,«5)q¶•ô\LyN‡\»ap0ï²Û¡9³¶/棸ÅUù骳¿s‚u1¸wJá>î4èðÞÙÅ¥©Lƒ1! ‰¦á¥¡Ç w1T4r¶‚úßß ~Ö¯ò¯ïbqmÕ˜¢8äÐó°4²"“C¥8k¤n½Àíè=ÿ¤Âo¦N}®†vû}çk’´Ùd/x›õ‚ãÝb¢tþ™S.hõ¾`̱²Rs÷šîî#Äè=R“~6¼AOˆpbÎ#ó/w³á ™p]…<×Õ/[†í õZëÊ‹ÎêAÁh\ª(jIqnù=Áhâï¬Â6÷<\7&u)iqÒ×™*Êágº…üŠ¼>óº¬HÀ9‹v³«ü¨?ªWçØáoiOmãp2z¢³ÌS,òÀ›Pÿ ȱêíN'æ3Pùƒ·‡ä’n~7¥ÈP„Ù@@ á(%ÔÔ¡JKO»³º1v=E½'má9‡6ÙQé•’°ô™r,U9uÅ¡›ÓNð5ü…KÈþ(Ç]‰ø-ꤌHäîäY~›¬(e¨Æ¼#X2n š}hdÓ%Vê2«&Œ ¹‚´ÓMZOQÖ‘»|¡?Ÿ3Ï×"Z#ÐOKPYÕ�”Ú÷† +ªù[¬¡×…BÒÙí&ƒ<=ûŒî•)j9û~ +cü|»˜&R‡‰ã�Àû^ó°òYzÀ˜÷Îo òéŠ!°²¤]P÷$iµF³YoJž¢³yÍ™ólˆåÚÍYׯïŒ!NJÑ+VÔ© 1½`\q@Ú¶®iûŸž> +Ka3uûÚú=‡kjèyùî–%cÆø—c?o°ï]x¨¿ÂD±UeÓEÑôåäïs󃌮^Ù“.®t4ÖñBïùŽ“7Ïp[ô*Ä¥œ½„-†¹KV¡¶€…©#E«û.Gr}atÀ«6Ï+&xÇ?´÷9žw=¡èòsð>UÄ«¾œëC¼C¸‹PÆ8é•’XöæQ`r#þô|÷:Ô@çĶÜî¾ÌµðyÚ¶ª½Ý‰0ÅmèÿŸÉ,{[ÊåïžY9èV´—@Ûþ<¢…n†û‰F©·úúu't½âá““ì¨Ð9Þ‚Åj%¤ëç “æ¢P +‚D”a‡·y^y®e!¡Æ$ßÜ>nîqÃN�ô°´¯7@›\쪗÷k"zB¤Ò4×ì^Žc¤lÄè’§m5Y¦n·èÜ#ˆòX¸í?–üÞ–ÞÞ,²ñdPñDÈÎ}/j.Æ€1o-¾¿l|»ÂL…yŠ¿‰(Ùbw<‚oævA~¾^s¨7ù×:ðô^VQе¨ºÄK*ÏvGuX9Ì®² ¾q=ÕÅ\ýì¯[gäÚj� X©ª]ƒÑY ƒñ¼Ž…á×ûljƒÇÀ³@úã((zb·x¾¦£Hj;-œPvæ]lzî¹A0,¶!{ú/½Ê–3÷ù"?'hÒÀmÁ`i4b|+¥ª(÷gZ¼í€M^€€÷T>²ËCјÿ·pùC4–·»¿ûÐàÅþ6ÈrÖ#¸¿-&¥ŽÛ8!†Š›'Q¡òxÿ[ÃâÅ'ˇGv@ ^ûvìP–Íš•Ämsù¥^÷`Ê ËòqHªÄDU{viuá)ÏgäñÀ¥8üGÁIx- ++ÚÁ ôDa"wc –÷™ý¶ôN’ þJ&�À„Ùl1›^û虧N‰Q2ô‰ãrÆ™Mì¿‚¥ú2¼Û)û·)’ô«¡¤o癡âÅNMâ]À;È*m6”×z<®ƒuxðÖÓ;:Äîˆæ0Mª"î9ŽÂ%(>XŠCMº¡äEþï;ß|¹-ÿâoóùn±†6tL\L<pÀoØÔÐjbt3÷‘ù|nú +y’ñï|ìÌò@Ô‚ ùê“X'éÆ\–ÒW+¬þø·›?pUB×ÞC¨åmÔoY•Ëp ²Œ£+&MO±””vÈ$jƒ¢®wD¤ð®ÌªãÝߥ¾¸ò'ˆ[JPYþ£ƒ×ßÜýڋ݈©¯7wä¢åÇ0¾· ˜â_B¡¼W–£GšÀÐćÐ%$±jYþ¶ëÿ±ãÌv“û#É4Ï)¾pÔR³…_Ž"Ãjp,»ðlÿÉ×õ"A?ôÇ.Äóy0C7!ÿÑÞ<»çgxÙ—bf„öÍPJ¿n”ÛŒáñiõ9ÿÿ¦m¸—é!ÿ’ÃáÎwW@ŒõsÇÕp 'Êrj“Yà…`®sCÝp˜çsŽùR¿&s² 2dè£:7¯Ý”uMÇstûKÜ뇱ÝÆo¨8ÉJL¼Õ³¸fªËÔ9²±¦ +GBGy=C`žä=ý²BçÌøBúÜòïÙCXïdÙLÁ@¯ÖZU"ÌÔuu>Ø”ê!Ó¯y¬‡ñ¢ Ž¦"¿GìvT*©þ„JôS]€‘0.ˆ]Ư½E9Ç3Þé,rÌúßhIånÑ4Ùk8˜×—‚¹Ÿ–Ù=ÁK÷[f¯w”¤E)f:šì\¼øôÐñ†=³|/©?�‘r!Œ<¸v¨ý¯ý¼íHx=J‘°€jX~ö«K0cf¡`öF¹ª¶åÇOôlýèXæa§Ï7û¢[ÀŽô+ÄdJ©Ôc‰²œí‹áyŽ|ÀýÐêˆ #Oå˜5ÔTð».D_?öñûí™ó¼O˜RÄÖvúo²Šü]×AþÉâ)Î!©ê�é\_˜sê´ìä¥7ÞË¿£‰Ïêrè÷˜ØÐ&¦rÁÛêßf¡âSCBð¶=¢é‡@ +Тh¬q´'놡a$“¢íNŽ7<ŠÆý}³(&«PŸ Þwòô,h¨2ÞtW_Æ°"ÌbC«~ˆÙ`BÕ9—Ê×w[RÝä™dîý™†6GNŽãn<—a\(Y"Ódåù݈C‡C)yh#çBfͱ_[îéÏúNȼÇþ' +‚#†*6š[©„;snòyh0\ECœ“Õõ"oJêz‘¬P•Ã? DHÙ¾=:[hiöù¬õý ÒøÍ'Àeð7⼘ñΉQV_ïÁaø÷*›„¹¾q‡c.\t1âØY +2ÁäH¥*#h„L¤€5q#)$ªª³¶¶EïxbAåç"<â7Ò ¥‚èk·¦(Û£ùY×}ŒoÓPø¢8à¦àÅÉÉbµ×ªóœ'È=“qìp¹„L¹9ùŽ8휂 þ—(¼xí‚Ò¬È÷gÛpßÁ³ õ#””O.(t“¼Ã¶3ãK*K¼öBg_´O1ó¼zˤI‰¶d²(–PÀ;ËS4uo,Ü‘Ž5¥+R²¼Ü©Qü¥Ð~춎Ák·gãÅgóâq!Ày×ÄøníÝ‰ä ’áBØî6qFìr¾µ]áZÄY+Pu8ªH·Åa‰sz§$3›�´OCÐȹÒçè^Šµ,ŠVaøïóYFSj3@€OÈ)çj]ˆÜ…ìÙÿÊÃœ³LÃD|. P)Þ¬“XíqêðþCdiLη±;ô0g%RêÏ°ñz"54²Ýäç¦@ìZ,‰ +9.ó1Ê£ÕÀ|äTôÈÙ<Õ†@² +ׯ€‰k®·&t<C~™÷ øÓd2é#F°÷-sÆå Žÿ3%²±„ÏG¹Ý.xÉúBˆ;mî+´§©%ÉË©N©û) +2¾ƒÃAÇ+žT\áÝÙyÕ¬F€:ôQpÞÑ? +O÷em‰‹ç Gõ&ý—õ›º>ya˜GƒÜÁÓNîa¬=œ¡®nò¦öÝ30¹Ó'²ÚxM0íuŠí«š;ò@,yBC·è4=ÁÊR’Ò @8ãçþxâõßµâÃÀ¹Q†äràyÝ(¼y‰¿<(*/÷çÚÞŸYp¯ÏW`e,—A0õ*UÓD…zdí¾þí³sÃzvU•5ª±Ï`^½YÒß…Á¤„Re@ÙünóZŒªI`8#Å*”u¿5Î#bQõÙ¾ûÚoäù–]xé[x1à†ª`¬“ˆKú~Ò¡E|…ïs_$Ñ%XôÅ‹F0žyƒÛº‚sía)BkA)Iÿõ#„áF>Ÿ3÷ÿ²ßí‹]`M¨€@Ú{žÇAž]z#4Þ˜Ïëê¡€ÆLæzCঠ!?+Ôç8Ìì;ò³ÑÍ¡xí6§º”Œ1ŒnŽù“'’L#÷;—?Ÿ3ãK…)ïɧ˜,MÝã§]…¥ûXÈ()Vyžˆ_’}÷wt¸b&×”íjÎ¥B³Pz²˜Ñ˜bˆ€Ið`>V‡Ro‘'|�Ù2Wí,óö÷E‘½Ô¦KŒ±œ±‚1¹Íµð¤Á¨eÍãʾP,B$Beå £èÚümG\ì�Ð|�îN^_Œ‘–‹–û ¤6ktZ'õ<–'7jºn£yHž+Z3¤Þ=:Ï߉Rè[Ê6â॒ÒÏÍ.öp_H5åÙÌóÁ¢Ó#ÔæºEäwõF³íº�ô”KPá5ÙÄóSTÝf³‰};½0àgMò@üo oÏAýá;œÍ8 QO‰œ> +AËŸ™ºZ’ù£î�wng¿ÝSªŠÂê‡|Ž>?ß8�ãí;¥±Trõ¢²¼N46¦_=2°ÅØ?âæ ÀŠâª>ÅÊrþ¤í…¨å‡£lò¦ó +òýÄ�½©»BÒMÓÜ"kÇx¤U4ö?rÆËJ–û“ÇMBˆ,ËT‰™·„Bå’ÆÏcÔ$¼üGvìÝúæ@ûøfW—K‹YÇP¬:Á!0ùpæ¢Q[Ì’Ðtåw/i#鯑ÛcXÑÔ47s ++¾§jÚ{låôÄ-‡ëë}Õ/þ70Ð]ñ6ñÜ_&8êéYGš·‚œã â•(³ý3‹GÓÒ-‡Ö¹¶aIÉA•£ùÈÙseH\BÂN>*£è?2™’c¿*bmríÿ¿}Ý*vÝo}�z¤ý3ÓÇá3q^ÒÁ’ªMIÆô~=xÙíg¬"2×&ÏuZyõrXðR8ÍDiÜ«Ô®¡ÈÀšÌª'³ïWõIôå [q 4ç):Ӻطkì~Òž÷á ÊâUÁ»áƒyRË2QÛáütë:ó-ŠÕ¿?ÛãÚfÛ÷Ä&˜hz*6Äa•ze*©o„vcû™ûž«ßÏYÂ4h¤á%¿“ <XsJ“Á®.$ÙSUíBò7²„ÌÏwípë!ˆ2?ÃÌ´P©ÄÌ¿TÈ2/ÝÑC™ÑѪ,Šù=Cs±”œqL<¡ôcí<-ê\¿Ïï/lÿ}^ò’wßð+”¢]ìo(Wõ†ÉÉCJ’ƒ§"Qa6ÊM¯r¹B³jŠ\L‡'IýËÐ=À9f¢?—lâ…/ê´¼}>Ôz/(óH&觑Èy®äÔE²ÀýÜPÄgóÌæåvPG{6Ο'÷•ÙÒÉ5;3]Ö´[’O åhO© ˉ`ÿç^r<âV›¬I“Œs·•7&ªö\°+¶7ši™½VSŒ«-†Ý˜V–Ó?Ôyœ’(JŠ‰ã´*°¢u¥R!2ÓF,ç&úï³Á S «ù@ÌÅ�=¨Ñ¥à3}¬9B?û‡Ã}·¾q°<GeB1Õ÷ÀX+÷½ü|Ù†—´‘LhGŽî:¼±8ëp±Èq†¢WÇRL âãË0qu‚ÙÌ?oÖãˆvciið‡7ìaX~•Ù¢ÿÁ‰¯úèØú«&¸{HÉä@Î9r[ïIÜ<¯Bßo‚7]çÌWU.[Æ°ºº Vu\¹€›v¼½öé§OG´Èáý,½©Z®ª O\à` ö³€bå÷YÉø¸E^×û{ë§!Üá^Å‘@ìú¦Pü6º´r&Ú”Ö7h[u.¥Ô+ôÚÞ $Þ=ó_ß &äçºã‰·7´‘ˆílý>¦pŒ¨–éïJºÒBŠ€MÍ+2yÈ!_‡^?x~=¹é>Œá €W’¶¥·}ù]\gt(çc�E}…å”Gâqê¿™�û*fWªUï8Û}ŽÒ8c'Ð¥"(1‚’xÊ·™ó~ŸçÔøžv·d¡ šB‚…Ÿ9zÔ<b?8]Ì-KıJúO<Ù6¼Šk@y÷""Vó:ý(2ëƒ)ix\EhÀé,†d}!êKQ™£'B27íâ;ÂN rÔ¼¸£\Äj€ßÛ4ÄÄNo ³á¥+VØ3æ_ß�Ï7Óùé%…ÙÞ•C|#ø +E +ttãëŠb•žöâõÅ£œíQ:ö®ÿ,[e+ËφG0l7ÍöW?bÓiŸ÷î(fÔ²Ê~ˆ˜ghè=l0Úþùåå§Å„3džünà„ý'Üò˜ïÀhÿÙäâ×_e=+º/d)ýÕk3A8mDÉož;+ˆ,ªæÉHûöUWŸ6t£–�Æw%M«‹HRÔœgú›I㟶™™ªâ,fžï6‹?E^ +üÏ +˜@ñÜ Ì´/ó¹'˜œÓ×Z}uÊÔ£~kB ²†ST 12¶ÏJ£˜J`l‰hœ,ÙÐDŠZ‰¬»°ï“¸¶wo˜@^{]{б턿zK÷Ð%ŸÇµ0§$ÇŒî3Àá… î†¢Un%Ù€º€R‚T” NÇ$!(©ÂZÚ`(c>&Í+u•;DQ®Æjª"×:v5?IÆ‘‰ÉFé6¨‡Ö¢Šz +¢õõ_#CúíÿËFî!ÒóÆÓí‹Ï!+°³ýøÃÅ3yÃ'ÝÐî³ Iª(ÙÍ\g”ŽÖ˜À$½ˆ…hÒ¶1L'Ÿcf¿¯úä™m¢Á6á}ÜlŒ±bô×{X½‚Õã+[_<¼òAg¢pbÐÌAÂ÷3“ˆ£È£eè¶äæ‚ïÛŸó£ÎN´_D‡Ž"É'Qìƒq ªH/(˜ÍNîGý6ŠeJàÅ}`¨± .¸W£™Áˆ÷á%Åó¢o.½¬Ü3ÀšL»¬¬ùížÃ;‘µó<µzŠÛ@M9&!²EQ!¦ËýmŠªìT5%Oö<âÀˆ¼qXè‹}ƒI15‘b2°¥ôí’x 'æß ßÇ¢ŠþJ/5“ÀÝ^Põ]’¤î㲂‚®¥üa´4>Ì¿‘’ñÍ£ îˆ@ÃHñHqÐ`.¼¡>EWFII“#VC°»iƒDÂ2[¨ÄáÌcÈàn}zÑ];ÂEØ®”¢¾ŸPµ"Ìà¥@«1¦v«7ºUIÿ*iúPXR×ô—@¸ú·¬ ˜Mb NkQUn‡)Øî]1ìõfšFŒ§2o È`PÞîK8õÔõ<ÍöÌ0ŠÛÿž¢ÅOÕà,Úîo<ÍxgÎC>µÀp +_8ߟç +VÆCÚƒçXFß�hÃMжº¢þnôÅ©ÉÖãùÛ˜óœ$¾¶§'2Ãfæo0Y„Z€a*í‚Ç2ò#~6úƒÒVÄäßÇP%õ1‹;ci.~Ï…‘øå ÷?ü¯m¡ÄT“^U‘l8GkWâ÷8Ü»·ë{<¥HoC)%“áO÷æC'ñœ6`¢ô¥»¿®î$Œ.ó÷oÂç²ÚÊðȸ,ñmï‹þøÀPì¿wû"¿Ù=yvBEñSe~i'7Ý× +Ê‘U–Ÿ®†C¬é ã>é@Ã$âêäôB¾éXG*Êfs_°—¬¤Në·¯²_d*Þ3Œ\¸c‘¼®-,µ¢ +úäì‘š ,ÞžßIsnŸ8zCOø.Îjù’“±b*¾àŽûÇeöÍ|v´õÚP¢¾ù“ùQ]øÓ_ùûñyÎÊ‹ÓTnEE¨}ô3íìJF#vÞ4 +¬øãð½%àë$%ÄÝF¬çÜU4ωÒê.pðz¯…á<8ËIJ¬,qZ ù‹QåS²s‘ÉUyò‰Hœ‚êdA\ejFŸÁIêÆçÛþuÞ<åÿ¬ÿ0<³¬z"ªÇDIÑWà c?íZŠa;Q=õþá}Ûº?|ã´7G2¡ûRlvÏÎ-±Þƒl¿©ÊÖ™ûlÄ|§!Z8^á( åÛ„îʈ)…ð’/V îy}0ùÎ%O“å!‰"Éä¬ÃS¼VFºàðЂl‹¬)!he‰F(Øžá,îâsê@ù£Pœ,2;ÎÃÐ~�3ö9¨‰®ÿ‡º÷€ªLÛ‡OŸš™LzO „^¤Cl Š}a-kwíºÖ]]õµ·µ¡ØËÚØP¤÷J€^'™–é3§×301 ¾ï÷Ûÿ/œŸ2“™3ç<§ÝÏ]®ûºªEtN›8º?zƒ 1™ãé3‹ËˆIúÜP¬kõ¨ÌLJ÷î(X��@�IDATiù¦þýru‰)Cº¦ ܸ;¦-=f¸´´»;•³äAjæâ£0u ¼á8X±:ÝžßÍqôi˼“½?]qÊ)Áß\ÈÃ4b'F#7‚×¹ªºEᘑ&Wˆp5ÝÀP>—T)5ìí6åô/‚~Ý$÷Á}-+=¬YP0é—1à@îËr†<¤˜Ð̧7C +|×þEuîç6Ò£ŠËGDu¢3RŒt‘Ýâ‡oÜTÆ60½HõA˜ƒI\ÝNèS€F •†‰Qè*ãM©ŽˆSøî'ÚÌ}µfúʋONRÇ4±h,Ü®£åX¸fa¡£¶ü¨‰ë÷ÎÉüyS\¹ÔÀ±7Ç‘êZdΡzÿ°¬7”÷›ÊÕ«USqÿià1@¨ußÈ4Ác7â÷eõçÊ…ˆÖŠ€ëÎàb즟ßyÉsÃÅÿ&ú¿<~b?:‘B8CÝm£ÕÄŽo¯Ú·Ÿ´¸¥îNIÊkÔ³p6ε +Ìix® z†nwè¦5úœK{·”w2BšW24Uq¢Ðk•5å Âpb®Yhþ£Žó¸ÜV¶{úk*{ªÈÇ—ž—°C•ÖĸI˜¿Ýå½Ä@› +¡zqhâϼ¾·ë±ÇNCå:ô‰‰Ñ˜t/m3‡²¼�u#àdoø:¥Ï¦ÿU×-yHŽQ¥¨ú5ñF#<›b`„»Á@¶lj®õà†WqTãÏDø” ¸T}6Ë¿Õ³¼¾#©|±¡-8½0³ahû¢Ãê3-ÐþK ¦&ž?þÌA2Cÿª‹1OøU]™¯èÊÛtœJ¡2Rü\Dì×êïºlè!–±ä˜ˆ!7™R¯U¤_2ðÍZ\¾Ô,íš_*©”´§BÒ±jP¶µ¿IÜÈ«À^%×æP$�[A‡ ¯žyzB¦9jjÌåw•d¤pÓ]>ý°Y˜˜ü~vIüÒóûX\ ÝÝ6WŒ?ßGx-–cúš°ýß°’ã\tÐ?¯ǿ+œÍÂ7Awxé,¨Ü®·›&evÓil¸~¤fþÛ…iºQ0p+Á£6CA¬F3Æl,Ê«¾•îvµŽ†*ðÚvßË¥¢ø—‰œ€|Zœ¥µÎ"H¥xB²Ò¢Jþ.žI¡X„ì¹=(³· ª¢ÓÈ›à‰7qº6wPºÍ>{Ä)Ï|&p “ãb<¶Æj0WhÊúX$"‚¨øV),õekqFcÛ8Šc9:roV»# §T¯ÖhyuMZÚv¤‘u‚ßýÉóÉFgð-ˆ¹ê]ÑØÒ\›õaËþŧ,0Ë])½k ª hÖ9¨µ&‹øãüZ}ÍRR&G#(Xê\.g�È=Úý¢Ãžbæ‰t¶…¨ô¯_Í·²SÐÕ7‘§âO“¶a2ÆÊúpV'ƒÖጯô3¦6^Œ°ÿ“•e.›cÛž<žííFƒý:h0~‹EM(V ½nT†}Grãýz\Ò$ßÃ?#ß:„r©¯ÕÖÑÍDd5m‰¡-àbMíP·\xû°lò|ñ=ÈB¼‘ï/u4x¡/ÿ4¥Äï¦MÜ·0¼É0ñˆã&Æ¢ã h(—_a¦Y2æòÞx<<¤Âͪ[UÕÁ¨(g§Bó;“/ôË%}R[‰á%E·í¾9Àó=,ïÕ¨þŸÖ?u{ñ� +%ñå…Ž*ä’ŸiÌWm¡xL_ÁÓ«Œ{ŽîdÚÛúˇe<hïjå<cæÁ A󅸂ÉSߢï–n_W}QNªORŒ;굩œöxÍíB>s4æû 2�ž+à-"ý8V"!ä¹eöÆXÜý=ųO‚vóÐiz¬Uôý@/Ùÿº@@„úƒµlm5ÛÛCN|þ»†ßQŒ§¹íï,†ŠˆnŠDØÜïêÚvt³¸’äcÉå"ëö^oÃ𥵌ͪQUxYŽ~ͱühƒ³¸lìÖŸ-‰á%¿¢ "!Tàý"-C-Ä™¥]¾hx™`e×wháý¨ž·”+ë—¹ûA°z:ä_~€Ö0!iã7UgøG[Âák×®•DÅð1&ª\óŠ¥ùŠ +©EUËa@Úð¹ í/PÕ~Ò îYklƒá%cÑW‚Št›;tõgð +¢�Òû¸~ÙÓiÑþéèŒâÁC}rÚzÈoÞƒÔ”ÕMkÜ ´ˆòSƒÃ†µ°zYAj®‘ Óf#Ï"®Úcž°†7q_Æ¡Lz« ‚²[–ã ¨ˆ3ŠúÍîþ>g£}ým\>è%ÿŒ‰ÞR„‰ÉaŒ(µº!}*)ÒûT@ünY^戸- â—$×%¯°>†g~nÞ¸ªÕò„#×/àWÑÿã–ÿµñ%ÆLKg¯RT…CùÎòJ1ïøë2ãÀ{ëÙ~ÓÊE&£5›b”vžÒö‡"bXu¿×{Pe”'U?·-ÕœÇqÀ{±‚i<§ÿ¶Ó¾ˆàIuø$—|ù€</ br;ëµÎ@Ëyã±"iX¹ªÇ¨Ê˜úÌMgn?ù0X“„QÊšxÖT@¬ît=à°ú<¨K|Ž<姺àñhâÿUà{ˆHáài.fÊŠ¶éñHìo0ÖæáÙT´·q"òjQœå.ƒ®D¨¶‚âØ¿ð<W]ÜÕh‰\òøãÑïÞz®…L½EiØ‚Ñ0¡ÈØ ¯7ÐT£Ãðät—o|EE¢¥VW9ÛøXTWÕêW½óRõl7úñÇi‚¦(5SÀHUjöKÕ«îžPpLèÙ'ñð£ÛÇP`Ó)öãžmEßÃ}þ¸òLZJJ£¡Aû=ìç +pÿ}Ê9W ’“xf–\DÃŒ^µ] ”÷ÃœaÇ›mÇ¿æ¿Ey§.¨ðÖ ²‘’Á . ri{cKñêDÆš0ò9F@—ãåh›^¦´ïÔŽ¦‘ZyCÞŠúÀ)a™¾fðô醅o>×iMqª~íSÿ¤T¶ºoQ{»[ +©s77U¯íŸ››T¶¦Ö·ù.±„Wá?„Éë^¦‘\{ò:8¯,Ç´FT~};cN(?$¿<‰_C’¶ÜÄðƒŠl¦bQÕ¿†ëí<ów”g›››ÛcÑtAôÅÄoÓiEYl=t°²ÎO@çá9PpΡëfHAÁ çbÍ/•ØfœSPO¸Ýšˆ¸ô™‰ç²-hƒ=Íž¼º½ûi†ž½Ã,%T=z®0F–”ñ£'œë³d]‰ú<p=+üozŒãºmSßÉ+€¯ô£ÏßQU¥é˜sÓX¯ÂÃ}ëÙ,éâEõ@Œ1¬(þ—•%<$‚—åØg)4Ñ”™ý/„©€èü:Ô¯/¢eê1k†pMeׯl¤˜‚æò�‘“Ï)ÑõxŠín›åvxsý¡¾B›ªJ<ª’€gß5%TAªƒˆ+l¯B(+&És)£ðž¦ŸG±îÚÚJ2Ë%ŒÁc€T”ä”)e·¢1pŒ¨Ït]úPÜòsY‡¾êPˆã˜²RÇ8„Ò)Š¢—ºý*rÀPm\å÷tîÿ‚™¬¯b�žìo.$¼9pŸ3MÀ¯ýLhw#Ü®„|¦$3ÏK…ÑëÖ·n€T;£Oç,×SPA{®Æð6V5%‹¢…·Û +3ŽöÆÉP1¼Ëš}¡'ôbFiÞÀ&6P‡^–õh)6¸R˜i˜3gvbrJþ&ùºÎé”janCÛ(KÚÏhÞXˆHIº$Ï–â‘õÒMX?qî’¿k‹v5Ä»µmùº´gIÀs`2På‡BB\µ#×…„ù( ²8ÖÜ©ÕÔø‡Yœc=€¢ÉëðžÑÞM]8rðà\¿ +Í +��3¢†¢»o$>Q-¯ìhkþyx±½©7¦{E³wÇqöa´º Mó ðÛ@T•“ãœ3dˆo¨W<#=Ÿ'¿?Y_Û#]kQ}P©µ´AZæ©7�½úØ#Å÷íÍ÷ƒ× +ZÅfàê™p©²¾’m•DqµávAÞG^«í|Ô¬[â5#‰±†~€ `g¬Œ3²cCx®ûZô™Ö^úé4f4 ‰øžýš§¾÷±ÄÜêLº®0ðY–IR¸#"øÞë÷ÿkã[ª‘¢ª²Àú›5ÿjÏ™ +³¬ÀÁ¸;ê™ý“3ÌÎÁ6ìrÏNò›r8t%È~Ó{D}ZÖ•"1¾B%²ò¬§�›¸;Ò‹ŠR#!í®´A£ +Égîhwƒ*+ÛzoS9í-6êjÆç‰mƒÇ7gÄ߉Åí!—ÈA·"K»§`,奶úòc3‘®!+A÷˶֘9© œã”]QŸóŠöæê‚ÌF2t†¥Á‚A½V‘Ù§÷vwAøÝ i–ÃÑNV‹¼Î[ÔHlíÄÒRwïc<4‚CÿnÞÜfZç +L?ûÖJã‘À·ðì¾/QKsFþ›ù7ÉPxŠà ^D×Ü0Ûä®HÏ$B¶"*âXÈŽæ¾Wèúò^éŒC{9ô/1¼+Z<cDU}Au§jâ:®íÓGD.u€sLì3¢mÉ$·Çêo6™ÐðÇ¢c ³bUGkõzÖ | ÓìR4¸(£G'¼y�ßëÂÒêîÈ0ì™!ÝrÈøkM ]¸*Ah„(áâ}Þè-{=ÁË÷E"õDÀ‚_Aˆ2 yÌ?H»àòzÊIWw`²,”ªd¶Ëil9þî�©Ñs¢,>ëÓĆ4XL‡ÁÆÏŽ˜�RƱ(2 ‰+j],.¼Ô%(ó.kª'Û&û ™À£¬ÅñÙT<•À¦±Ck|ÿ^?x°oiãîõ#úYÝ@êx²>—Ë3ßRû®0UÈDÎs*KgÓ‚„H@§Hk4™‡s7)¦kCe†ó†Ö¬9¡Ïy&ÂÂsCm¡hª&jv'Œ/ùh¡u¸¬C�o_dÕý%?`£KÀ`¬9èÌH^Ù† j¡}‰f¯ÕO|'è‡í?pù__b´°¬ $"•6F¯Õ9ýÔ5ù¬¬h°õ{WyÔB¾Ÿ5k +ÓÌ´?nåxfž¯aï7á}»—"A´‰2¢$òù[}áA;Ýá9-aæ1€¨/E«í¤&x²ä!ÿê¡Û›ã!}ÓŽ+w~{ï¶ç=ªÙfUUÈfYmötr³áKrsõÜ`v‹è@Î÷~–u\2ô5oz™s7Ö‚ìV«A‚ÔY’nYM:�ù!*tòœP •àMTq-¸¥iûwïmö�o¶ÅË3ݯþ©˜{6=#SÛòÍOÚ©×¼óÊ®8ÇÿdáÏBé£ðª( so†j٬ѹ‰Ü["gÝÕ•ÂÖt]’œQQÚI¼˜ä>�º¶ÈéøóÞ€ŸÑµº.@Âç2›0½Yõñ›®„Á‰†c¡÷Öðº9 j3~³ÓE”4rEËÈŸ†”Æ,AÑ.ÐÕØ'WÇ[Uþâôìl}á+ÏÕÂÄ…íëUÝñ¢½¾ÐµÍðZ<.¾Eöx<È +%&Ëžóº½3< …WܸжÛà‘<.ÐÛ‚b¿`\¹9„SiýÜ‚üACðÛÄ}ÙœÄh�ÙßÁÕ 8§‘;¾ÑÂoÌrlIvL’ïŽ^P_�Y?¨1psqF4×$®G¬< §Ða„EüšCã>âë“åï뻇~}Ð5âqœ?¢ÃvÞ°QS·#zÚÙå?£ÔLnŸ–nå.²ð&ãjà«áíyP4ˆ…%6ŠÒ Ó$Ÿ"éš+¢]{1°i‡äuùÿnDŸ!`ï‹Pc×ÍJ~ÇÊÝP¢ø¼+¦¿®Åµ}ÝÁMÒ"k¾‚O1¿³×èKÖ%†zÚ”OÍ´t=(£3Ñæ7A–*¹?âõ¸ ðìÒ¯ÕúRr¤(J³)¬ÊOŠbÁL/›fÐ]y¹PîeàŽ>’>ïÖ»¿Ð4:ŒÐáÒ*2+%ñýÐi0””qp¶€jH¬^ŠÖ²:‚Np7üJžCœ'bXŽÞ~òo( +g‚à|`4ƒy3®ŸšqT%w]{`Ž‘£_Ghý²ÆŠo€¤ƒTOuÒv9dÐ9¨Š_¥èÌ(¨õ쫘ÖÙƘ&»¼±UcûüÕ©måå¿Õ’û&¯Äp:Æžug¤g€[sN(Üš/£÷ÏžÁÛ;#./!Z'ÊÆ~¨ašáÓÒ[§˜©Dú‚hÑ)ñX¡ +wÐh¦i&v[gÞ“Ô’û Æyv +Ã^u°‚¡Qƒeßi–¼;R5ËXJ’¼Æ¡Å“•||ÿE¯éŽGWÂ�P: HͶ½{°®.žkÏyFg¯¢ˆµ0ÎûX•ž¸Òæï÷WlNF Õþp9<Ò»@¿>Q&Â6~¢=s®,…Ÿ®–x£ÔNo3êM(ï5™e˜ÔìÀ[ÞÔÂ`dÞ¿ÇÍñ°QômfbRù,iÿ�Ãñ^ô}îð8ŽscZdƘŒŒƒË*+Í'2h²C“&iÐHOÍ:Fy< gq=¿;ßÆßYèv™Ï3:ØÑÍÕµó{§%ÈØÈB*‹ão ʯ‘žò>úMŠ(±â üÏœUÖÇç|ŒóšYÕ¾tt_ûläÂ/× I.4 VèS¢ÌíÑXç³/.,¹ÄŒê0Èñ³ëbÎWn8ÐKRyæÁ£ïD~<H5¾-á;W-[ãô‡zÿ›SKîåݾðlU¦<«[�k“íHp÷ÒxæŸ^bçè °þï6Eðã +Š-,ý'ܵŽõË'Æ÷¨1Óö»¶ä¨uÿ£?{ +ÿѯÿèÐC@8Ò¨EÍ!—HÇB×¢’ú×j†¯Y]Ù¾êñáùö +÷zï„@;<Ñ/Ð{\Ñûv{#P£5bÍ,tà ™V�ù÷@u·©˜ƒ‚Âu!I¶7D ÛC´0¨ºÚ}�Û õÞvï÷ÛÖ·¦NÊ_Ž[?ä/ ÅL¾E½Î꟞ÈY&mtýJ\Ä�zx7i…ÄÛL\�<´ +϶˜Ñõþ‡ËdT~(–[…¶™ÁÈËÚÚƒ¡ŠÅ]Tί!mï}“÷ÖQSÒ3G•µ•û:¡c‚5å“M¿²Þ%Ù\þnÓõ·¿µ<ÛŠ& k¢Wýð|’CÐK1›ÙA¬‘JSiµFÆ6ÃÙ¤(Ò©Ã{ËQM`ñ™>Û—³=½_d6ý}hnbIŽ ¿Il“LX€ûý‰„=”]õ +Ö/ËJV - ¨QÁÔ“åŠé_3Ö¸7ixk<1q†} +N)š,”à4’›½¡Ýõš¤"…©Ùþnwè2¤ J‚ªòOÅŽJ>w(ŸšiòÀhÃàRX)ú;à±çé2=Ç5Ký€/´ÏGà€¸QÞñut´Qz2²IN±Äð¢çŽ˜H •‡ñ´&qËʺ¦ÀÏUÄíÊ&4’LÇ}uGYnñŽo» V†šbs°_&ù ͦ}ÆyT\²ÀóçÊ!‹”<O'Ëë®Îf™£òp꺖”¨OTÒR)ÖŽ«pü1G¢HÕ#òœ³ÁÃUZx“YÔEÑ5ÞcODU©#GZÒcŠé#¥œúˆðÑÒ¢‰gTãç‰ÉðD<ä^®ˆ¨F+“‘è &šµá:Öá:ÆuÕÝM~o€ÑïÐX8@¡¡Žó kä +‰ê¼šz5{êô"WãQÇ–¸úì¸þ™ïŽç]ûc:ýEŠÀÓŽ +ˆK3›äì.�g&ŽýØK8ìZ³åCÑérp\Ž‡2 ý ºJ=¨éÒý¼`x)÷ÌíK]ÿ#ªþà¤*DîÔX úÁ0Å1RÌøH®}8•@vbØØêž²¢%0ç£ÝmeȲÎQõ#˜ÍM ËK³ +ìMŽþ¹5ÎÀk;ü×ÆcÒ=Ç⬿òûö–ééã²²\’X+pÚÇY‘ؤHâ*5¦~k�9öÙ“K$8zIçMV|–ÏqÔ¸jœj±ï´˜§†OÖx0r¼×¤0ü*»LXlˆ«¤™ÁHj‹f7!ÒhÑõÔ=0’2ø®Þ5©!ªÜ¸Ì>^gßRâê<‰RŸ„giç÷ð�ž:ï‹zOùãü¦€@¶¤…òôÀϾˆ«ë.þrí«; Èt[Œæ*OTïpÁ,!¿.ÑÜ]¸˜¥*%ýËh`?ä0¯°„<_EtåošÿhÔ?ÈñÿDLžÀ:\cEÒÖo®SÙ|xª6xË—„à Ч”G½Û/)Z9<¶ÛáLBÒîŽâ>ÑíIí&“%{¬™ånÂŽŠ€]~Gó7BUÃÃ×&@^ Þø˘¼›á7Ðü“éý*˜®nQýzŸÇ“é¾} ùÊ$p9î“o©»óØVñ-Jcÿµ¸vy("I[]1ùM%À}æwÓ_ÿÛBÑÏÝ1yU4 FU.¶¹3еˆ½•c§´¶Åb Ib+¯œŽŽIKTÇA�ÐqqÒþȳªßÕŒ®ù|A Ì@âç…ôâäþ\&1)Há}ªÐêÓ@ëlÿNZU§Cêj†þÿ`9î'õ +ô–#ξúoV:iÝqQ"Œ~]êøÀ¢yK6í½23G«{ûöýt@n)ùcñ´‘Öœ¥ËÀʾ†¬ì8y˜({‘NaŽÊ:åî4¼²ØR.ZgÃ@‰Ä6ÅdcæìÙ4É{fÍ·±¼a†Š…éÖ:¾+0·Êkßp ÖX[šUôä‡bð’¦Jv>ø&"Æ@¿xy(öC#„!×+ +œ{ä 1;qZd±˜¬ì2´GtêÀÔ[;U�{…ì;Çdº)š1>DQ¯E¡§Ø½´èß +Odr +ö5š`Z7�[÷‡&£¿²árª‘ÉDÈÞ{¤_…°zÝQè6uWI¨ôyóT@þÉQ꤈¬|bŒ)Î9£r=¯mõu÷Íg$´)?ÿ!%®¿]œ™¹¹÷ö’ï»ãïÓŒéU ƒ…ÎiænV¤›º¹A2ÑÁ³Ž &¤O@àm#ðçéÈÑnSŒü +ðà’‰j5EÒÖÔÀ¥µaàÚHn×͆š7ý©k(ªüp¡®Ùý:¬ÑW¢áyRÐ&U–#Ze6÷áAÒËQt#$ɼò¸_ïµ½ävÉ«…QO˜±çó#w°½u�RhMì?ÂD7sªå'-ÝÊpì n<¨Ú½ P±æG){âš-Z´H=ó¶Û6¦ÉFËÚ¦¦0<&²é“j9<É5’AOž=[_ØàËFX5”Z»'‚.„|þiuØ’j`ògÏ¢Ï-Ê £dîFªîйº)//¶³;´ óP6¦ø çIɶÖF»¤nì ¼`RW¶zÂn£¬@¨‘•{99QÐÑ~ÆèÔ@^ãtÉr‡½�Ïý4X•z™WºÿׇwÜ=_r�$àw¨ Çø•hÉŠR§ñõFVÏŸ8ræ1½ß9sæàAÐ×€8àËTÞ±øÙÕËvš—ÖFò†0¼$“¸øij4®8�W,;ýÁË +"=ŠFÖÕ²j³ÒMY2ÆÐõ42`У³ÁýyÔŽŸò¥‡¦ÚG3ÔPx™÷_HÐ/A‹ë¨¬.…ºï#A*üöDxyò<©}±tw—ÿ‚º€<²àôY9#Ø«ÄŽ0<Cýôã®+ùØä…ËáM…,«_¢¯[)Q<¥ªêPõž|?>ñ¼ð<Rÿf8<½ÌÓ!9ú/ÑÕ°˜F1�E™•µ&‹É~J#¹mòJŠ–crS¼ä•ü 6ÿ¨æË_0ýìN^ÿëëÕíiwŒK2qÏz({@“· õ¤e×Àë$¿9z)ÏÏoïl?¸%.Óeš¤à ÂÊOXý^Zû™`‰?âÁ‹kâ©@`8 ò~ àñ‘æáí¹VÉýÀ°nÆäÑ`þê—+=ùyGØW "xn†1Yt°U5n^Uq°³ñg[È¿h€Ý¼s°òÁð&Œ$Ia$›|%ê™Hªš]”x‘`ñ]b¼d};m9 Þ‘›9×ÀÖ¥qPØÞVž›ÛSL!÷+rûÎùo>WŸL«$·’½ê40à¤`jA[zªÙ\Ö^Sc$õ +ÂÂòTq¶Å05›eìÉãJ^ò÷6 ¨q}¨KóXUžbJ‘k¿þ¤íh|zò·'Ú«ßå߃”•‹a)7ÏÎß$2F21y¢‘O19hŽ~ÊÂ2å yÝÆù·!#w~&’ÿ¼ü!Æ—‘ù©ýbnjصŒ.ïCWÉêZÜÚã.Ò4Ažú«ê¦’¯Z£yÄ ’õHßvpÔ–~iTˆ°`%z².ÙfS“?µìô³gA9a€…¡bÖB6‹Ùŵ�ÛõèäBÞ«Pv‹6Ü·b!ñ!´²¾€½Eq £#¢ô„`ð]hâúÑ„ +1Æ„>V Êc’zHRâ/Gxiá¹ïæ¶bW‰‡ì²ftYÝ£c +y9&³|°kWdxš©ã‡7uG"ÐÛ¾òù¥²Q©¡1«;üçZY‰Ü¨®ïùî³Jm½Õä—åx`] +c8EÈê;“æõ"ÄnXy‰;ó¿ï*"ç…<L?6º²©Ž.šÍ\hVåGáÿoËàØk29þŽ¯÷¹J]”Í&PÌçaQz%¥²O§Çˆ’tͦfß„eÝCñ9=cĈ( +íÍ´¥ø‹¥;|3o9–t&ˆŽ0zÈïfA€ùL1”P%øù¿àú,DÇR%ú$–µÞàAŽžüÂnÉÈ„Qt ´99¥#Þ<h˜ ?°Jâ:4±Ú;œ[¶aÜø¼g<PÞ ŒuÅÏR¯\HÎÙ6y%ç›RåL MÇ>‰”V#R þ¹Â®²^¯6þ‘ïõÙI÷ö•óo§õÛ&À³cô‹sSsß]PöQqzÁGý,æ;€fU)rbâ>úàÑPd§¨Ä—0¼Pað³±o¼ñw£ÿý÷ÐÄ"ZæÑ1Ñ Rz®ñ¥ympð;AJeàY¨jéñ@ü£Ó +Ò·ÇuSùfÐÓ"v‡üuÇXçXF( 8#™zZ“¸k™ÛÒ§/—ÊPÂöv9åæ‡>#¨PÁ ,©`6KEæp™ÉÔã£,fæ »ûÁGFtc%QZKÕY®/€H%ÀÂB¡4¶5ÀªõSr2|áAuœ*°O@|?¥Èm.Ë®ÏóK¹È÷M3:àùl´\fx9xŠ/ÞÑŒ¹‘€Òúï]ëÛœ4ÎðYz¬üWµÙìgdY¸»À°6>]úÄ»S¨‘î¼<J-ïµnòø@�t½ßUG[ û2ýž¢ÿì0;lÍÝjíÅgŒÚÌ¡b«Ñß¡ÒàµÞÅSúE€Ù‰2ÏÍÓÅ@Ź©©ÿí ÿm“B‘–[r¹]¥i<+uëÝ»íÐV ½*ælp·á¡Ô€‚Ëò¼Žƒõçž[B<Òžå`0†ÞuîQœÇý…)†F"irÌíö+cû¦ûæP–,‹‰½Œ§¹´z\ØþËOî!C†$Œw·Lx—µrò Ǥè·9©Æ#0È=;ÁRAÏÉω¡ýÌ4Àç†Æ¶¸+*G[ùµ(º!í¦¾0*+£«÷LpEìÕnÿ¸îhˆQ)}9ž/G~9ÇÁZ·®iv—•¥%Ž§Ú§5ÀÈ1ÃBé“&Ø*Z"ÒXo½6Ê©•-.½Õn¦7XqXÝpŠéiHÔå[ypÿÑ bé=Þ“ýýÆ—Ía²äxzÚÖѨF‰$°ÀýoEK’Ç“ÞÙ¶yMEù!GÜä¸Û¢âtµM` Ç4@æÁŽk‡žTÅG§…\$À¨œö‹S•<x¦{ž¥Ön0s3ºGöw|ºº´¤ý*«ÙÔÕ•©jìù°)·‚ïT�ÎGvVï]Ezþè{á5¾dðÀaªýÓ„Øü!C´UT<zfT‹]ŒXÀnáY£¤ˆN%ÄTÉ´üf6rÊBöy†àÂ0Þ(—iŸ.Rû]¡Ø¾ÎÂl繋øØá‡ÖŠL·Ø?c·qŽÒ–P(�"Ö5åãØ°8éGÔ.bŒV.í\Á,ìèÓRçaé9ÙÄÓ¾àÎûƒ3xRb`Dâº]¬°¬Skæó¡ò^†ûëYÃ;0.…—|¶/U–qKSç",N�|$Ú¨Kƒñæ¦[<%%%Z£„ +ï¬aÞWŽºíÓNTžÉÄÓîÈ-ø×—ŸÆÎMºz-«Z¥½¡¿²Û ×.¶¶iÈÚMV…Ô6QrVlJ;*h³eYm†Ñ‚ýóê¯éU½'ÛmfSËL{ÅBʼnшxk½";!7ÕU–f"ç“o%i,Ò:\–Ù�[^±£2ô€WîŒI©�{ #|›ÖZ„æ!¿s>¼)éý‘ºé¯ŽußοÍÔhªšSÙ·[ýΊ²´´#ò ÝÙ)Fþ 0Èuí&Ÿy(†3m‰BëÉXl¬+8<I“ñ6EÄçÆ‚8„ûƒPx;n<þåg÷IkؼäG秎 ]v£»‘5ðƒ1îaHÉ—X›Þ·Ýå¿3öCgü¤ù—Dô7Q(7ȵ€m‚Š®Æü&Øë–£øº"¯lÑÙ÷N>¶xf#Š-|<Ôby]H•ÅÌAý«òzE'ÙظhQ˜Oíß>© µ‹8S½Ç¼4ê÷ÊtÍIJbÓìèŒIWªw»ÎDA³ŒÞ›ð]ÓžŠê¤£Ñû÷ÇûýqÁù’A!×ÄÜ1\óg6Eü¦á%BkÀ=ɶîÊëNËÌÔ|ùCçh$(.ád‰‰?-mή:œ+<âø*HŠÝ˜‹p†ÙbÚ„>îß³Íë¯ùK}°ŸÝB?¤èj}<æ›g6¥Ÿ ±F�ûùg5OüÇ€)7ÒúFp£Õ4‚|áà ~~|¶5ÑxÜ9Ú‡áS· åêgF¡ƒ¨ü\QÆ7‚M¡æ½{ý$4N®û{¯ä<¬jé¾Ö�·IÉ*î×Nb8¼ÅJ ^Wý«5söé@÷·ªœ©cm¦…ðô ÝsX+SC7ˆÆZr?‹›=÷û$ØbÙø½(¶Ë9™BqýQä½Û�¿¸A±ØâZ8ü·8cZÈFÛv5›™.Kú˜ƒÎÆ] ß›yË=ãüŠ†‰E†‰í+ÃŒ•UU1Ò7£32zŠƒdüëš"9AΗT:C0Óù†¨´tDAzG]8œ‚çBœçq(.4ÈÚÍ¥8‡É1ö~=�….½$«ò]ZDØΧÐÅ<Ǥ„ÔÖÊeÚ{§nÈäÐ÷ÌsäHðƒ‹‘Ì¥Ã¡T«yº&Q^]`*"•;jH%¹`-–Ìâ—àÕ^”IJTk²þ�q®·FONš|³Ñž‚bè‡Huà,~aЩ{(¾bº ‚‰mØf÷Üþ‰üJ0òg^w[8˜Ü´Ò1ê+d¼_Õ:o*J±žÑà ½;hhÞšÑ(<ŽÏqõLäGמP|²ÏA«;.+ÝhvY?ìê}ôú'Ãß¿@™;ƒáÍ£íõd¼‰{ÀíN—ÐWŠe#ÎÈ +4w9 :Ó×Ï+W÷ý#©ÐÿýB—_sçLè)KÛ=ghQuŠTj¿0ô\*@¬ÒlÎ0iV6á20òáU6Í´Và©|QŽw²¬Ùnç»è—æh.F‚sóë2º®ot¶£ +‚àµkaÏ.ÈzÞž¼ íþ+·uE.S¢~/êsZ4ú1׈aE?9f¬Ô<iÎx¸ãgì ÷Ö6è9½4ê{-Y–#X¼Ö#Ç ÅVD^Ìn„lç ?ˆR¹Ö`Ä:@tbxI>‘`‚7¹s0ÚßœÃMPÕ]ÝØ1ÀhV‘:©ïTì'ðu?ŒâÞ"tŠÁq™-ë†gï5±¦´,w³/ixI˜þiµëbQ¡ïeùÈn‡R8ÁS1ÞaL£ê¾aÔ ¿È,ƇÀËÒ¥qÖXÐÙZ2moÕloÛEx‚¶´‘`o»mPvaŸâiWhW>¾Í0NµP$àö{~Pcâ’E.—»é1<$_:¥ØÜÙÜTµŸ±²lD[³µK"… ýó—^òâ¦}ö9xÜéQ–º`“ãL¾‚&2UÔ™ÙˆryÁhoØö³·få¢íMW¬ÎLÝ®«h+œ<}è.\» +w`öV´v—–ŸsŒìéݼ€øoÀ æbRì§é¢¼DøUM-µ½ /1>BFñ¥ˆnÊeY#¯Î:5®*Ý„Ô(9òŠƒÓÛUÿv@ŒÞÃä+Xý|‰ÑEé¹hÞ¹yQEøTòÔ:ÔÝóã&:DÏvXòé…)&¨Ð¥³)öü‚Äàj¹ìÂz_!a¨;ÖAÊQ¹?ËòÐ'èÈ8DL¬õNÄÏ44Ø“$\ ‚Ú„ZÆvW…oþÑZQ*OŽ™ÜŒDŸ®¨–h¯ ñç~‘ßGdúžª²îÌ8.‡—5 ý'möÉ¿÷ëqI;ÔøýFFÁõCžÄn€:IЦšY.%#·{+o¼üÂð©©‘~•›ÄŒÔ#§é]èÉç =,iW»Ôöq™Æx/BvbôæxØÇ´6v:4’““@<³q×Ü> +Äv¶¢©å&‹‘ w7œ9 ßõg›fæê4‰ƒ÷ Bk3¬Ö¢ Ž.YþšÓÄÏ¡ÏÖ€p¹Ç‹?n*Ï@ÚÙf…Y¥ß :·þd¨m‚$Ôg ŸKöÝÈä1Ï} §1{ÃæºBÛ¯ëÊfÿi(> +Ù xmæý]ñ–†óûô‰?óÙ×€¡)UÀ²iP÷>Þï`ô!,FBKÆèÛãÙÖëæ¡.Y¿F-7ÄÀ4£Ù[7WõO5Ø{ïQ9#N S›ÅªsZRZ¯jŠ5ø£>U×fP1z±qDiëù)|@ÊÐŒwÝs×1E!ëƒ(Ÿ™>xHvE(™‚H ÃÄ3~†Â9‘ŒcX<Ôþý´jÊÈ�øù<-ÉùíÛÓ:8¯k4HªÉñÃøé>9Þª©Ã¤µ±¸èŽãÁ°3n5…RQ§¬ò…H÷w¹óeÚkóÑãFøHz§¬¬L´‡Òé™7Ë°âàKvá!™F9hÀ‰](dá³AHSAX‹úÍ~o㨢<ÿÌÌžë•KK 6Æׄ +àÛˆ3ËÐÍU±Ó”}]ÝëÊÒ¬G!ñÐ-èr:…¸Bœ™¾P@j*ka`PiÝï¥OȾNÔ¥>¦÷cB1ƒÏ,´þ5Å ã¬(§ ÚÈÆ"î9Xu‰&£9e�"½[,¬}㸂´žÚJò¸ÜQ1KÓå€üFiŠº'Pj¬.ùo<åäïN„×8eʆÎÔ=íaù,‰×/hÔˆ\À–*´kÙ¢ÉTèü†ã©ûO+²W—Z-‘1ý>dõ-k7õ0Á퇼U“}=ÀZ´»)Ñ-w¼ó7^Û²)??Œ§²ARÐ2ÛÍïDþd©,{SQãz6à_¸=Òä!³ùŸÀy$¯w*üÛ fDbáA*C +Œ±1ßô÷YpìƒôïM’áƒrè,=†‚ÑpIÓ½°/v4d¡'+œŸmë´z2ȱS7³Ã\Y�//¥Å[¾ú£l‚j8âAéüxäŠu™õKFÍ"´]0ßc{Df,º©ãɲ +Ðô±Å£!É;s%Ò,ؽ¦É=’çô{±û6ÿYugºk¤ZŸ×?¤ox#£EE ÐA˜¾mÎvnìÀo±‹C‹¦]º&½öeº3»$†ºM=!ò¤£ûU{ÒžûXºÍó§åmÁ2–g§˜X®‚gõV˃÷ž‘¢þ.Ó–—ò,ú…š"µ�Òårw…oà)óOPÅ ÃägèðF„•°4•6lìå1Ž~ñ@4šC´äª}¡ .QìK<K¢–#!ЩòGÏw»ÝfžW_F®ì3üÞ¼óÕ€¾DçîõE_ƒQ{}³Ð\ñŒÜsµ9Fr¾{ŽÕÈp +)}«²û�ùù©“1à¤B N„ɧ(†¶®Ö–ŸßË«=ÓId°®Á]¶<kÑD³ -˜ÆñÒ +Q•ž ‹îïÁÓ< 4qÑÎ1Í—-¨¢Žð\Ȥm #9hµµ£ŽÐ€ûo®¨E_˜è†ÃÆÃWâäy1ÓqF51Fs·Ü½tGƒ FHBõO·½U'±_K&ök‡Õ®}„%õؘ]ø¼Û#³õŸÀ²nw¯ëu¢Ÿêƒm~•ÑNu-nþvÃ: ´þ%Œìk¸ƒ–±å™Éc@m�°¾4U ìL”NPŒIP,{óNg`”náM§ò@(ßËKþþx½ãkr»y›®‚ZW[VÔ +£-º÷ž×·oë'¾} åKzXÃ’·8ì¬QåJ‘&@1–*“Q%B‘à<#¯^Öà)=Ú�'G^‰ì<Ãó·Ÿ!¥¸§@�w*é·ÓQêý”Pnbæ?§¬L+ÚGóà}›8fÖA«Ì<—·¥Œc=áñx÷wÇ.ÅÃOÈÓ'êG¬…㢺O\ŽœçZ³ÊV‹\†IÌé7%¦¨Ó"²– +ÁÅm¼ EõT>#Eƒík`itå}ùß ,)~ª¬ûú!õ3ûzæ´[ƒ¥@@PûŠÑç¶v[ë“?)OçЬâ]¯2üã1UÙï«wtt³W¾ˆÜ&¯°ë�¡yÍÄP}aôDâó‚’r*J¼_äÈÀóBHb Ç0nä—Ç@Í gÎ@Ñ2N:ôc¡–±Øb°m9õÍÌ %I10g ž K7í뜿8û’'J9ëÑG!*™tøãS:ÿ"îÖ§ÒY•Ö”ˆf>Çc"ÀWCÜ\»³Ò†‚œü�Oƒ<`îðlkÕцmAÝÎ k4|Išƒ31c<;6@:¦…CŒo%†ÿR6éëïÄuL²Ã-¬ë¬õõ¤ÉbzÆ+àÓx í¯ˆ”OõOMug‚ëX¦¢?€úb!î‹£nËËJòÈä¼…kLãÊÑå¶è‰û0î<›²ö-¿Ckœ|ÿ†e•e.;kéÉç‚<§4!+Âó Ô aèƒÐ +“Œ…{Îᵨ�]%Hæ‡Ú»'CÝ'ˆp2‰¿Œ/¡›ó[4XÌ3‹ü|ðR€’XG<Úå/L¾™£HwibùåW[0±ÿÂ_iOßîö\Óá»Qé7aSÆ5ílg}È)iü]±°¶8ù»ãýz\ +näæ.˜0ÃNØbi:NnpòÙÀÉg—ɬ:XÔ˜\„vhš‘e#½=õÝu;¨È�)‰þ1Z©‡4Íœ‘á²FZ ‰”dïÂC{DÅ’œ€Í`È‚’ð^QüP«ÿºh`¾wN"‰Lž ’?T‡æÂpè M1»Ñ +³±.ÖYqE¿„'—X•è®Y¶‹UVõ˜"¨Q™Òžx4ßÑóToûüsùê«ÿf[æ€ ¯¬°W¼¾.*2㌶ ý˜»göËñã{úwGw4EyêÅàð¡ÿ7n)`8Í,©Ì·1&úŒ×ŒÑmÝ0¼‡%þ]åœl3³C�…ªá´xµ£®‹ä5ixïþù¶~fFyÐÊó0Ѽ[×ü`\:tië(“ÎŒ*r’Û8&X}¸Á ïŠtØ-Æ+áâíyQ”'ƒx_8;åçò^Å=r"êBÑóE™úEˆ0"ˆtÿ,„ìÁŸ>÷\Ý´[o5§QÆrOõCMÿ¨OúÙí ø©Nr»-a½zW9ϛ㢃ñv—Iþú[Ý¡ÉðPnŽË Òä´|†æ¾ „]>t<ʽ'¦ÍmÞSŽ‚Ñt¤Kô*Fghp>ƒ;oz#i·-/I|žÌäûà?†tÓPE£—+bäÅc®§ì΂ôyž~^eº"]õÉsFÎÁɺ¬#Aç&yÑ/Á~—H³|_ï½’A†~f £!‡%,±EUšµnûñçÏ?Tˆ$òQ6Þò +î“N†b#1QR‘š°›Ì'Cn÷ôýüüÜÒâ8·¨ˆÔ%¨UmÞ|CDÊÎ}1¹hH—Ø6{¯,ÔlÆ£3öY(%µ�³>-íiøY œ¢¥I[:£(mÿ}¼ÇÅø=H‚EÅêjô!·Ieàš¢k*$‚ñ +’lê£Æ†}ËÊ¢”tÖr°£Æ?¬x@~TbFäxZv‡«WÓ +)æÜzô²¦%t5ä¢_†¥ý4qCº'gšË&g`:ŒÕùêy#wûò…åëÚŠ@ï&¢G–aÒ¡èð7d¥ßa 7×&0§¢rYªð‡Œã¤ $Áz–…1ˆÅÕZ™Q'Bìs-—Îo_…âÓ¬knËn�#YvjþÝF†¹—†mD˜/ÄTݤǷ¨]ìàýVxv}"RôœéÅÙdœ$^Òäß—ŸA»c_U¥P#¤<0à‘£Ýœn¥=¨i5˜iw¯_RNƒû ǯww7D³ÒÓ-]ŠvǸ~¤¢ßÒ'ÏÁ2�Æ´v¶›*Óü×j¼«Åd³E¦al úúæUÐ<ž…ßBƒúEÅGç:Hõ_#E9«ä5–AûµÌ|…]íýþí—<$ ’Üy%),Èncâ%äñG|×{½Þï‰H~qÊ‹ÈÙï•EjYû–eM½.Y—(i yð9ŒCP ãÏÃuí±Fsš‰>]}O Ç›ŠVﯣý6B¢O&þœ‰g]‰¼ú«+2ô&ÜwFìc&ûŸ> |ÆÄAHŽãÝ*g‘™ÖÔwå~´ü׈(ùý‰þºÑ/Ò¨èt–~œ©™¾H¡ +Á¤<ÖŠ‡u΂ͦ‘}‹ÆoØT¹iñ‡ZüÉ3 ÒútÎÀºId%Öd¥uu +EGçÉÊ"÷iÏy:ÑÏß4b™ÌzÑžÖ†rú]ȪTÅ”âÔÆä9HÃG»›RK²R„ k?@òHb¥—÷`òï\ùáÜ#” “¿9Þ¯ÇÝøVz¢§Ä5õä\&ÁûHÇØæ%hílD{)8oõ™*ºLÀÿZ«å3ÒÍDŠäϾ>Õ`´L0Ôpü\&C4íóBÍ�”9·ÈNŒ¬¾¦5x)èò^âÊí‘#W¨Ûr÷ UÛÔøÂ`+*7¦úˆÌÔ…©L1ºj>àI³c2]È0j+ÂÐͱzó>BÓHÈ–©f[Œâ¬áhìZPy7#6¯Š"ÖŽ3¡Š‹ú”Ö´ùË‘›½-¼õÀý`âMå¸yG`;õQþrÔ5©ƒ¡œ²wQ(+çñ•gf</µ´©³¡Í?bºz¶�9 àÑœèÌ«M1ðYØ”~´p\¢<ÈÏVq:@‚zð¹6@b>¶jÚ¼¥Ùn<ÿíñ©Ý?Lg kÊÞI«N†ð-èæ §"i(ä@·4TÕ¥;`ô›4½¡—ÄRÊåûÓík§è*w°ÔÏkrûòåm½Œ#òÜMP¿¬é£oprŒ½¢¥FþÞ�™¦G{TÄ`§ 30$Å}+þýF籌ö–¶àL…¡>G:á!{¾ã}À¦!qE7eDº†‘½lí1p&^î)vüBÆŒü{¤sÖ¢hY†I¸øcÔ}¿ü%×Z“Ü÷wþb;Lð**D=™‰žZ²¹yÿ¦ÞtïñŸ¨ï«÷ösÚѨ¾“OOný—vÿˆ–J‹€+ÔÈ¡¢i9.I?¯)ÈÞzË0{ÂC$ç,s¦Õ%‚”žµíE<õõJ¤=ßäÈG"ý8qM@G(Žÿÿׄúuž +)Óž÷Zc"•ùž)'Þ§M͵}~¬1hÜWÕS}¯,\å`A[€4L&îgòþX¿9žŸ—œor@»;ý%2/Šg!Ì„·ñ‹¬èw©<ÿ¼Ûü^Ý«²¤Þ‹\òOÚ}Å™E3‰WI~OºÙh´Àn%‰ëT='%”ÉòãóRPÃ+Î$ë2š¼'ÕÄU_;c¼?{´»/ 6=ÇBàg@]Ì‚—Ö/¾yÁàßz3Šž +4ƒÊsÛtQß匼¦¯ +€o ÉË[,ý" +õ }: &|&†I¥8)Þ!ëÛ.,)ñ×z)u3w!·:4“sØ°Cìò)&=�ŠÞ—¡f°1ê%oÀµ¨®‚¡ÿÌO"4!Çካ!–Ó߇·|'¼•€Œ™‘NÙ$éÊ=`“z\3?²<ÒUf + + ¤‹Q(€Ë|‚ôÇà<“í…xÑdB!TœrX)¿p [¯´Æƒ ÄðuŠ*o䙀…ýJ–Ù/zcwð¼Ô…¨äÙˆ›z¶ü0‹¥é×Ä.$}0ˆ¦'¦¦6÷2¼Ôn(›„´ëpÃÿ¥Â¼:ÙÚ{ø7G¼¬íôŸfæ¸W <óÔ„ú0¢¡CI¯@ +æÀ$LHIÃKÒ-¾ˆ5LJָáÈÛ›Œ,›…ÞÁžë<dë<¥¼‹P .ƒ“?ÏTQa ç0‰iÈaƒ—–Z“•¿!ŠzcB®5aôɤº²½=]Ñ䀣ÅnBÐj`¶zr¡Éñ¯16Ñš›Œœ ÏAâœýØèŸêàÙ·Á*ø†ÕÀ¼Gèu@Ÿ‚£kïUB"矰̑èyo ÞXé L[#ásèF-À·ç!½cC ¦ç^;QÏI&0혈3ŠþÏâhÌJQ{8…8vdz$ɽünR®£Å`H)YÕx*—µ}k2¥µ¹+p=vëwÇë³cê?Ù8 õšý+Bîr Ë@zÂêZ>.ÍÔvî°¢îqY–®Ýuë᫽‰|œWó6U6ça=víûó:$Ê°‘cÔÆl³yÑtxöB8„¢Obh9½‚ó1»‰ÏÏþó…3;B³Hžg4ÇÀŠ÷�ò’ƒÐññn»·¥ Õ }G‡TU0r6öȃöžˆWž†Dà0ôx´Ø/÷VÔ%/äáG¡ <îúqE��@�IDAT>ñ3(€Zrì¨ýî¾n@F˜<à~:hšVŒIôÚýŒfÚ¾ìõ9§öͬ5ˆb Fi— Ú:Tm·Âgøʤ+‹çüª)×øõûÝ;>|c—Ð'c8khº‚KÐv[ä÷„~‰Ó湬"ß‹\ù¨þ?]-„þÌ‚ª¿`º;’\•O\í þ=.E>•Yö}H½l)%('Æ'Øê®ÁLÞ2‚a^MÎx3&ÉRÑïG.üÖÌl{×h(CÊçu„`°Úå~…ý3I9bÛnw𜣱¼~¿?ŽÜ¶ÜDšI’YZtôNî‹ÎÀ 6Ž{ü7Ó�>™a0*áP +¡·A')†Í7ß{»;Êÿq # ô‚Lü{Éó-f_n–žû¥móšF|¿¼a}èÀ>£4Îy!*ÿFÉý’ؽ„Ü{Ø„¾Þ·j1^"h–)ŽQxž-À½µScº;Ýþ]'›×KW$è6 .'Æ€{"±8ôyˆ0»Ý’øMðú»¢Êü„~§Cr#ˆ:r©pv9N”Y*÷p*(Ú*Hn^£j5Žé\0æ÷_Ö~ü1¢/m]œÒ/ÇøÇbvÃÐ’“úÑÃ7·ìkˆF¢K7v„hÿjN7 zP÷hSÞYe9¥—ý›ãù÷q3¾E“ω|haQãšòœ1?¢·Ò8iP£Ú¤¼Ð>;Õj3 «:¬ÛE¾''jNiZÐÂÃãc>©Æo£B´ú¦1‡ägö£ÄhœzÓgByv:r¬/¢˜ô¹NqŸ"µqâ|*¦~‡=‚Js<û' ®‚'ùD¥7tËÊ Ú + °ØGÒåzSï<Ï°²mxŠyãü…¨Ð 8¡·nXö5¹ÚjföKÂîRë[»/”âÚ¨®"mDmvÒ§õ±v%/4!è–ùÀª€A©²ðá}²\Ä�ã$Y—ü?’l²äÛ¢š!æØ¢jôýY9©ênnŽÌWCïÞ°‘1HD“ðš¢Kç"R4$`ùôQßRhæ:lØO…]šä§_ÔÕeJ¦ˆJÌݸ©²qåاáI?ŽC +"€[‚¾àT‰6×í…QþŸC‰ºGÎî{PÜÿ9xNqŒ%µ·á#E1餸N¯‚·Q¬°ê´Ó¯¿…õq_!EFW@˜ºq˜÷æö‹Ùƒ#×Û挱Z¸— u~¿¡öÛSPÇ‚4Ön¤$C1±ü‹ù#Ÿ“…nEÖÖc²�böµ4cÔEšúuX–_žZàØ×û¤)ƒQdŠcÒ´}šQYáëÐ6Ïî×Ï}M¯NÂC[?9þÝ&c¡<´ªõ·dIÂ[š##¦èÍÀ{èDôú%z[‡/ð%Ô|KLTš÷‘¥N`ïmg¬—1´qD,NÏÜ ¹PœQI6EpßšÙ3áõþí‰ôž<KI« +Ir'¼¦Gq<LIòÏëš}6#5³)ÂÞã%^ÿåp +‘"¿4�’¢?D)êÀ˜¿ÈëÔ‹pÎfm@z®÷oŽçû#’ÿtÃÄde&ÍÒùH- V¦|Œ6‹ŠdnîèíÒf‰Àz–£@¤EõSº~¥]Äç$†"VstÉbü§4ä$aÐZW°ß¦VWiŠ40‚mºA}ôãàØ;ýëÃa€TJ¥"kx©£bE;¶GŒf~æ fý&´4ïÁ¤~E@¢žB:žÑ +G4°{øðìˆlR}bXí+÷1ŽÓ(HÓYš&–DTa;Åë“u»ü}`¸ê|–¤ñ+Æq¯-®=Â(0u{wÂ{s¿³ñ§3úXõ¤¾²º=}E«÷‚D $öy¼m<Í<„»¼Ð¸{r‹sn^PÑ`· ™`åæê …÷<눚9çOœ1ÊÉúC`i²øLw@,ð\‚yÀÍn—Û²{"P#ÈE�pà|î€ó�äcJ>Í"ÎÎRH¬´<Žñ<5¯ÑÂûýc+âiíox²ç`å¢ÆúëäAŸ_‹,êb%P+2¼žá ô„ïdX»ù'¾,+Ê?‘.ªÇMWdâøû€]&fŠÝÖwoÇù~&'«Æ¿ž‘0¾šÈTÀ×ÝÖ6R?·ä¤fÏX³æ×,L´éØ1{³3¸âÂPIèÌ‘}$—$1 ¦Ø²ùõ^·ç쬬úë&e„’ߟ”¯(p¦¨t%¨øfÎLäǬ2óÜùÃ2Rse•Ú™ÂRÏ䦦<Z×Ù`ÄóðëÂÑRˆ\WE¡ì1I)EÖÔ€gFÖ(" †5æ?úëê'ô;F×Ø9~T¹ë d±‚ávô<þ%¤êvÜ#Ž9xZÏGtý¹;(}=¥(ŠÜ;F&~�SO)ȼòù›ãðáq1¾°•0:ôXè¯ALWoFøÓ¸~i‰BÚ±ÆÈ1V@Šh+žØAÑ’›<8ÀïúS·v…ØäôO]²¤Î@<\G{Míĉ…q3©Ì(&ë¹ÖB¬ÎNr8ZBÑè‡qJº½«r\%iÒMq{gý[Ï%Ô†“û¨è/8ÁP9¥û±:ýÞ×b–¨ÄSZÓRf XZŒ)4=ž/’¨P|ÚÔé³øÊ{%àE Pœ-õâáíØ&Éù¾xÊÈ´+—UvYßZgû´¾+ëM·[Ÿ‚ê3Œvˆ±˜zĪg±õOH‘›8vò1î|ý®Ý&–yÀðZUWo ˜M/ቔOÎsϺ;vìà`@®/éPÄÙŸÒý§!©–=е[ê±¹íÝ®ÝÉÜ5Ù¶Ñ$ Óz[Z/+Í(|-o/c{ô{%F~Â"†šnt:ùw¢Ÿ—eJõL,Ÿ¡hõ�¨±Þœ™!Ûê½€×e¯@««ú‹†)’³½ç-n4¹Þééíàpt7le4u.Ž"5éé”é +Òa-Ât=C¬‚÷Ær±OngÒк!_AÎÜ)0tAPTŸ2_¿¦Ý=rKG÷yF»+¦h¨l~Õ†¼er¿Çz%“Ž¯m©g>„ c%“Á¼ë7'úgÄã« ¶5ïw67ÀH&kk4ò…®«_šyM”Ú{ Éï€'kFqôÕözW—196¹±±KRÙ)Z¬CCL¬Ñ-ÑØ® °a¬²·LàÎI°�ÙàGi_8bÏ�ÅHÕ]ß!èÝN]Ò¿Ç!üæ8ðAI)™æˆBjP¤HÑLO"JËCÓØoîùãuŽÚÈgÙøEìx_ß}¡;ÊûçzŽ5Hr“ïpúÿNsìƒ8h;rÃ× '7ŸäZoçU„Pƒ™þH’}_O>ŒÛ[�¹õ¢²4ôc¡û °*Û»šiäf#Û|¶V˜hGwÂ%Ç@ÖÙã _…¢Ö™(ú½n0 +®¦vO�v7ÖÑ;ű¨pž…£:X¨ðá…ÝüøÓÌ™¿â‡I1hÃ¥·¡C“Kø˜ º‹SŒÜÍí.ÇLæÉú7àv!…ªZÒ_%†#9òúC] ÔnÒ>CäsÔÅFUûwkqz°%Y‡¤=”’ácb´þ¡7ª ;X–W¤¯HˆD¾'Ë‚*·u@¶yŒþÀùî°EÍçæ‚=ùw–zÀÎ",½³y!Îo=&žOû]BI‘FBX¿’îH7´:̯PhTÆRÙìO•Ì¼‘’ш<~gÓ‰ X=3%½‰Çs%k 6¡ u¾<"¿—§sÍ{)¼àf4:\q ½i×ðüÒüˆÂL6±Ú»à*¸4¼oÛb&÷I¼ck¦ùJàOÁåÊŒ±O7µ (EÑ—¼üER´3ù»ÿéusÆ“šr«³Mº&³ºø¾÷Dû?ýþDûþ•=mù“rí§‚žm÷…«$›Ò¸�)°ëO)ïc6pfZcZ'B:ô;½Ã¸ÑO?D’H5ìb²lóY¿??a¦„bñMÛ>«‘ù^¿9aß.kõý RlXVöX +M¨r÷tD¶¤~áwäŸ×-S«€œêy–H^b¸gÌ,k6š3s€TRÿnõ¶=I&ì?â`‹ç›jŒ¢‡†-æZŠª5!Aù{ƒÝÑâï‘ÅBZàÙ8½Ú׶ù·Þµ_(õêÔ °î?h68^Úê Œ^”Ãx¿Z¯J”ô¤º SÏöùN=è¤2ÀA“‹ß3¼d,ðòtE@ø:gb›wmq’ÔÆãkÖ°ÀŽL¥-YP<0£åô⎇eé;ßÕ˜4¼¤’¿¤ÑsÞú›îy–¶ñwFeª¨²…7~ôê—?òœÒçg2¢¥ój¸ÿSQ7¯”5Çp• --«QûÚŠÁÔ¦¿†B’¦ü ¹mþ)óàé'Ï1:@|Ô]Ñl3ò¯êzüû+†ù“ß“×!”[B)"!ËCýÕo_½Ã.¯BÓBUP<›Ó(ºV…âSjjjRv®[âÅ)XŽDœv¼FÚÊák1V;ÜþQpþ³4¥«¡!‘:!çknÐш?–á%mÆû|>$< ’WE3HT‚]Œ®ŸlOê¡÷¸1Éú�9|Ê!°¿ˆ¢ÞWš“cŸ“ÚD<Ü@ÿS™m4vZoO4Ñ<`Ó¿–Tê6´B~¥ÑªžryùŪ¬ß&[èù¿gx‰ö~op,JF¿“Æe|¶žÂÒªÓ]R’Û{ùdyÿ´ +Çe¥¼Žü<b¸gí©æçã~¶dHU•>£4³nJ£ò†—Ï#sÅfŠªº]kÁ?H;øÂhEátCÃÿÓ}"Ÿ#KmB¢º²ÙÝx“<¯Ù…i+Úº¦¼ŠšÇÍv–Éì=þÉE™N¨9¼„çq>GˆZ¤Ä½ñ˜øFIff`«ê½þñz\Œ/©¨ã‘Ù‡™BÊÀÊ¥§ýƵ'&!9%°wàà†€mžƒ¡þ!苾tÈ;ê7eæ�<óÓ€›2#DjÏšÈØçÍÁÙ›Z[С¢5îj É¡< ÕšëÐc&ÌÈGœy»ZV園Ø_¯oöþÙ%Rê<±T4¯]›˜ ¦ä ËVyz6ðF fQC= +’c»µ]9=c¤ÚŸçÈ»ÕfþÜ‚}¸ 7é¶À=ƒg]™rÞ€ìF_<þf í¸ñ¡q©¿£"ëIÚäÿãî=पÎ÷ñÛïôí}ÙÆR—^¤ +‹ E±Æ®±'±÷¨Xcïݨ±EeAé½-m{›-³ÓgîÜþÎÀ¬¢¿DÉ÷/Þrggn9çÜsßó–ç}ÞÄí‘чÉq3°Ç(Ú§¾„ ÓÇmq(^UÔ4îë]–ZŠc»ž‡/Æ€j~”6Ôw®è_Ô'®Eö õõ&>6!a¹¾ö{Ý}NƒßTSµ'±>f0Ì#¼n¼(§å?1r¤¢çEL*L03Wc¸ù{<Á›«|Ñ?óÿ´Ðdâ½F•”ü¤†Kî*NFçoVu~ÒRmä;²•efFbQå˜ôÏÜ2µ _áÿp3önÚ‹ø1 IÂÈ.šnaì—Í‹ÍÉ Ä‡:Q³g(Öùì·MóHuL(º£¥%ª+_Åúþ`”»&,Enà<Ôk¶"åŽ~x'œ„ÍdÄ~ Z¿—ßÿæµ<jQ£EŒmˆÝÚJº=ƒc]ï×úégïtç5ˆî“s{X3¶Œ9¶G²íò‰—_Þ‹8Vû‡ +(‹ø‡…çOBàL™ä¸D?¿tz}ÈuÿcØÕ±Úõs¿‹¡î#´ž€Vx5¬Q"°|.ä XÙ:ÌG¢\”Ä‚º6¢hL쑺[ K¯º2GS”¿·Ö?ƒ»ÂÉÜ�ÜC’7ºN8Nº^ö_r=ÒxØéŸàm@(”G‚îÜyGMâ:øR’²m7 0s>´^ï.DߦZrâñ»}þFhs(Bµšñ8´é ¨÷r<}·Õ–<“øcˆV89/¯³ÝŒî°ÐÆÚºúNâÞè2‡>é +X¸‡¥Ø„T•‰ßºúíÚºÔAu€¨…^ÖÔ9J™³ÍµøÚ³Qñ3Éƪ_¿´~²¬¾ÑœÞ'¡-bŠ~�ÓõpJ|‰¹^‚`›5·°$~oyó*úå‡Fû/:j.˜Ü7¯Ë¯FàWõÍËÁc{9Ú3 n³È™É¡”;(Áì³….(²‡¶š…/IR̳Yqþ…Ä÷‰=rÆŒ~¿jýç0¿Cðÿ¦Âï +4œ?0)ð,ì~`ü¢.ËHʹ(ºW üx4æ^h÷án¿©^7²!Yn#Á³£ƒ¤dÌw¶‡ÏÛãÎÛÚœ»£#:ÔÎY{¢^?`g'G ®�mŠ/¶¤M'#ÈÜ¿mGw³Ž -67JÖ{Ã**=CT|þ]€>ÃÇËþ19×z ȆúaXVõ û‚cu6„ɣȵki£ï™œÔÜy!§påKOÕO)´m›R’µk´–ì^ÓºCÜÈbYZÏ �ÚÞ|Ød8T,ff ¹?¿KŒ!ÙS:¦ÑÍÁ¨6AÛ¬öªª®ùÒý¸_ûç_®w‚HþdO4ö2§ï«ò}I}K£ôQ8Ó°xeéW{sˆë¬{¶mÛ¦ÁzZƒ²;1¯?UTµ>_ÌyL{ÌT€H?óì~Þ¯õóº—^"ý(&åvàëŸFñ„§A¦ó&/Ÿ·ž +kï趓¹ë¥…Þ,Åç‚ *7·Èy³h߇%‹ä#ªC Š>ç—þ}\(%I#ê}Ž³ˆDS^ÌΉh5Ëß{Ë;bÄóìëo) +‰Ž›l¸*Ñìf¾¼¿1úíÔ±N™¬Ú7Þþס ^‡÷؉@ÏßyÉûɰܬf·ª4B$]„¢“CtŠ]Yà²Å…)R±hQxÖ´1G¸8#±R+Í ƒÐñYyÞ÷öW‹:»—L)B ÒÞíbŽæÇ…ˆžú$TÁäètÅÌ]_d´=ߪE¼oaéZÐ@æ@ý› hna/G�£ +øµ§-Þ©N3Ï°» +:U–ÙƒŠßL,JoÄyÆÛUí=«;ý‡9è…Ûá=A½1m1²û.v8©]uo§EÞëÌæpQšû£¹ß/"XdLTwˆkŽËÁ–¶# Ñ%ۢ圤MdKGé¥Ö ¯FļËtC]¬QüB–Ö>FZÉgŠi,…l‡/ÕÌ´ÌìŒ;BþŒ)làþ�Ú°‹Éç¾þFºÜ¹~Pnn”øÅZ%Á•d‘çbá +j,˜Aòë°�q}ð,aÙXiá™<#´6Ùø]¹ÝÀÄh/q²=¯øV”ÿéùJ2ÚüUÝÞ3ÆD÷ù¥Zh$�‹ê…[¡FÖMrÌ€Â7 ØW/Q/x0ŠÐݾËcÚŠ+ÏžÞ¥åÎÐæšÑcnì?zL¯˜n¦´FÔ™FRÚµË^ÿÅ90§G´)ÁâhnÛÓÓ&ìèÞV\—:è g,W,Òھ΢üÚ²ncL~?¶z¯šŒòT—ûUù±…é•W'Ù½Ëý¢SäFkŠù©“æ¤{#Yæ–~¹½Ö”w£”¤“z¤è=cdAÍ�Uk¾Ëj…-² Íh3&o:QÆ…¼;-¡X+H‰÷Š¬0vÊUÇ%W&æ汞iM 6Ö&²Cì㌠bŒF©—&È«wàN"°4ªýÀî/Žgy¡øŠx¬†ü·ß‘€Ùçû=oå¥Z†ÿ>™Ñû?û¢›Ï¹t®fÜà! ¬FÆ“ÁˆùU‚dm³oMSDG³a>‚"£~ööÛÁ!p'ˆ!½Y²Ò!”â˜Øà·*ê†L¨Ê<©ºÐrt;½dd l'…}fvÿ±æ¼ +jÏœÃ9ûD¸t&å\d£ŠÂ:“0kX`µ¤fÙ¿jÃûïÓ·…�\64±tVÖReŽ¿Ä9`Ucî<Øì800Wî nÛTQÛ¤EıŸh§6‹•»ÑÉq£¡¶†äÈ“6Jl‹¨j /0+}TItv1$«n✣ö4Òd³T–mÕé½%•MÔÎî!‡ýœUDÐmžœ>Ư7sæLÖìQj˜;~Í|Sá,xN8vËå7ß|p¯ŸãK’c± …juȪ¢s +o…^¼y‹û¶ªâäTȶÇû‹V¾Sea7Üg÷9Ä–Ÿò†ÞÜF³‹ì ÝYwðóA€Àuk?;éê[®FôüzÝdø‚í¨K”Ž…înçˆÌ 8î@`ýŠ½–±ÓŸÀ"ñ4V@²(íîtׂ>²²¥¥V aöÒô½0ÿjÎ|ÙÊX¿hÞü¹›*žÕ5^&4×½¾(ÊD÷Àu•Ä1 ”4(ÜV±IÒ™vJËCæT°UpTþ¡ôäD[EQ,c Åfrb1o JDǽÄï'¾^òêãñzõIvÝÕ«-Ù„¨Ù tæ7=À’Á=¿ÍR²íö¼€¡;,!¼iݶ£A5æ]ÐJ,*G;d]ÚI~ŽBS�Z–ºát,©â¨¨Àu‚lö¶š}áì^÷(º9ÉN$³ \j*ûǺ ˜40šÔ‚1 ¶’iQµÅ0™ÕÜ"âD¬âølG˜¿ô’göIoa¬ÔËÀ»ÿ öfT"(‡ñ|3ú‚â°Í~“Ûçþ,A†CÀÏ¢À߆ˆý�ü´¦¼ÁfYšˆ9HÚ£ðÔHªd#:š`!}h_WOäáÏgXí…‚qSO!Qöîí&Iþh ¤4ûÕoA’Ã)#zxO"3bþz\9çÃ,+@ú2'+(£fÄ>×dmMMϞǼäÚÐÈ‘Þš-ðÜ£ÐƦ@É{2Ò,mºüÈ™††ÓšpYGkX¥à5h^µXöÓšYCÕmo’Mõ±ÀšÖÍE|›°¶%0muKàªU#‘¿È'úDƒ]!¤ì”ÞžàdŠø籑ñ*+/Ïè3a‚Ÿ!“æÄ‘H"AÓüoÖÔÕ‡œJ~+JNöÏ(utôIÿãºÅ+�.˜^z&ôÀb» ØÃÌÖkqïN—$‹Ò†…SAÌÎOƒ…pq$A0vàÙЩ©ƒâÏ+Ñ´¯ê}Ó‘J|;îÝŠÔí[QÃí +˜ÂW¿2Ía À؉'HØmÎOÁ(õÁ CHÈOÉ÷üû“^yÅ‹6»¶[¨‚±Ì+ÓoÉÂut„¥CîGëêzÄH]†©ïGvåc¦fÜ|²×´hÒb ·b’�œ¨oŒk…÷áÈ n—&ð¡:LE÷fôïD?Ž<ò×û×?«¡z‡2JDÉ‚–qðL&êÙµ!í«LS©!Vž„kJÉ‘ýp‡âú;ˆqËDF›× ŽmümêPðκuYÿæ›Ûº'Gžýëü«ÆjEdÙ@ÜÞ|ÐÐÕ4‚`køSíܼ¼ ŠÆx_ÿŠ´}1.×¹8ÐJpZ±eÒ£éømÇMóÝ"“ºh‡°ŒŽe‘¶ÏÒiÛFÓõ0MK*x ‚RTêÔmfKÇ·Ä÷Jº@´5Ô» +¾ß©ð·ñÈTr¡ºÂe±æ€¶Ià*ÁÖÙWw´™ º_ÕÁÃß +Â1ЂyœÓ3Þù*òôÿIüÀ‰a!Ð5¾5):=d)^Ô£*±XRUnèÐHù’u„� £Ô7’äÛvÈ}¦&N>Æ^UåE‚År>¸~Ç" V1TH%iñOY{5¼Ê›_l×l�áv!-9B[\ Mõ—B„Ð_ËÚ3îâ9¾œQ9&é{ý]t¡zæ=1ÃœŒkn4ivˆ§Þ×k±û”$”fd´ï‰IÅCÖù¢Úh§•ã4–M*æ>®ò–JëÁî˜ÝÀŽµßË²Æ ^`7@1¶að¡Þ +0JÑQoZ;ÀþP0œBÖN‰W—#g"ã·éÒ"Á9»Ý¸2†gb°~ÿ;tzOô¼1m@¨¬éÈTÛˆR¸ßº#¦ââ¨Çñ,Ú"1Ó[†´éÄ…È‚h³e\ëd˲v“¤ùV6Ø3£Ç¾R…ß~æCÚ>ágXëš2‚¢Ù>~•yè$Þµ¿âê?¾îQ̯² êë}žª†ëFŒÐö´J N†Ãßíñ}RÑ",¿ŒÖÙgN©âXn«3™`˹:“›À_ÑŒ<ôY0§‹ð~0çyS'‹ÞgÈ\‰Lïæ>!ÏàDÙƈ +O 4Û—RX³ƒAø^…*€šJÐi0àÑB¦ VHãL_ zuX�W� RiŸ`„åƒ1‘Þb¡˜Ü¯OÂ…‘/‰Ä !ÿ‚³N¬EiÏ›Ô4Œ¶lhfƒªàáŸzžD.¢à-({ú¤Ô®w‡Áÿ¡Í@ü„@T[ îÿ§ÎÿoûÁDüo/@´É‚±ÓFÖÒ·Ÿ³tÃæÆ66¿GV]MWôÑGµŽ¡C9kS“¡shåD$+±~Ã-“¡=]‰÷?å[Ð~:À•pŽ]àãðaÖ¤ñ,[‚é#Àßøo ¼€‹oG¡2C ‚�¾ÙÅ:úmïð¿àß“¼»ü°{aV¬…ûš×GQ2!Y5.2Yñ$TÂÔÏ^Ípi¬Óz%G)IIZZBf$šEUy)—;ìh°f. +÷6§¸¸=!ɘ Õý@ô yúoª"øì• “J)ÿ¢ŸÄ“Ö+–"›ƒË‚´'‚M!‹ÌŽS®øck«IꤥÇï‡,«ó±€`Fà ÙÖÁ +æÝ,m?œï62^äï=a#=è²1§Z;Y~Œ¢*ƒ8!õ-çKŒ+‡œøuË0QƱ¦eD*) _[9¦ç9ýËÁHâ n>%\¶h+›š>éèéx¦’’;™VÕ×@¤>ëL:³GÓŸÑ9f´J‡Áð[JÝþÁóÿh»822Ö ;ü\»b’=?;¦EG@àHóh«äòÚ“œàø.ñA?è}†,gMªT„ö¢¢øk;w'7ž‹‰l)pìñJ£A„SÂ@ æ,Â`žä•¸¥QššÚÕz„mo¬±egà%»Sg €L-óðÒ‚¤ÅP¶÷}�ͧ'î|h¯2T.ˆñûДŽú|P.?ä>òŒ_ß_Ua%0T£îÿê³5ž9Lyt+á<(óì–Øšw"Ô”9ñŸEYÌ6iãÊÆ{ºáAæf@ŽÂIäöJÒòöì©.öƒw=ºÇão"HW6u®Á{æ�”c,‚2“æ÷ÿ¿®¬Ô¶H¯!3pÞ•ˆ7¤aÝʼ©Æÿó«;ª'Cy9^Û/N² /s45ˆSÀKÃrÌ_/H;Uí©;IÉ·¨&jÀ”$PɆÎþKòxd›ííäõ§4;Ë(á‹á£ÝÈôCÌS‚¡?Ñ¡Óµ}œ©2͆ò!I2☻QmlŒªª½"ùÔ…åý3‰"ÚõЋÿ0ºÍPÖ`‹9Þ\l´)»èñt«É¢r…ïßå`(K¿€k îE„s<ì{dm2À]«ëà·~ånìŒ@]a6B]f÷GMóU]ò|‘‹IWV†(ZNexð‹oß¼‚ñœoâ¤Mc¯ºùÀ{Ådï=£0©nu‹”ÏP±¥€{Íu1æÆ0/(€Æ‘IDŽ§&NdÊ{ 9ÙSÆÓ�ÁÊ@©¥·2Zj´Llª=•›Í«`ˆÃå‰Tj“ÞÛ%(1³e¨ÃÑAã:ß]sãdh˜#€5^o¥#ØØm^Tl6¨÷Y÷¦ÁÐ'á1žÒò·ô 6/â�°Z×lˆ!s1˜èÇí ¹« l¡^~Nk^ÊRˆÛ#ü…˜Ôôš¦ÎÑ^FÞ‘Á²N]GUÔ}„;èeZ /ª1åÚ+g’ˆ©â¨ìØ5´÷ÛCfÇ)ÅÉ ¸—Ì'lm–» +)Ì=‘39 ¾ˆÇe *fúGÕ¤OÆÂTƒçùŽ›¾N,XDq(>Ũ±vc‡To8åQø’gAÆ”(º?ö\ŽuAeÑpÐ~ã4ÕôŽ.ʮDZæ +`¹…ˆ>lr íTlKÂ"û©ëÿš~#H“qõÔ(æ¸5 þÕÇêôü …IµÇ‹Äwß4ù¯·ÈS ¶øx—ò†¾ÞÇËq¦·ãÙ¿_,|É_ÙÞn·«¨’fáòÙÌCaINg”€]Ó²uÙ(g’˜k[BgYú6@ÉÔÀ&ƒoïF)ÇõïrÀ°ê0`n{j¾Å4‹ ¶¤Aó@ Ô†+·5—¦ +xÙ^ ùÂõÂÙê´Ûþ žördæhÀźUÚX„)ôRMNÚÁÄÿl…HŠ{¢(ÚJ ©¢}€öË;-÷HoKGúòM“÷TÓ@ðÏ™¨/Ms¢žI8m¢:sQNÈ:l),á>ó6x ݆‰¶ì•¼e€§”†ehêšð ~ÔT[Zç}ZAçý±ØƒeY6-¦þ‚òeçÁy¡Áñie^ + ÿÒª1©:à—…ñ�mžÄ3,,hvʤì:´4ž›O(=m<;Tç„PÀòZÁäu`q-Ü<ýQE„¦·€ÖÀã“àÓm@bÂ’Š™x.ÛCc uþ ¯Õ0µöË|Düçuu{çÏì–Ý}òm@ý8‹•Õ!iVE.;8'. A·Â˜ßÝÒ’àÝP»tC÷@$¹Fª_pQæßð§}Ô }•b±Ý£ñôbQv•2zl‰{‘ÅçÂ;IHzE1lËƶmë*B”·»=’epæR¤ƒöB…Ò/Ñè=î «êžzjEf²H +(R ž†×%û8ï¯`ç¦ +×EFG ñýîîœîÇþÚ?o;—(ŽÎ°©;;«÷tO:"Mé©gäÕ{ZºOúõ�°ÛÒY,°Á»:ÓU:!ܶ´m0HѼϬ]YýÑawá¯}º·ïë&ߣà00‡ùøËiXSZ&÷Hyny£w�²ý²&$è~<ùzƒÂj>ङÂB¬•@ÓÈøeddÐÝçÜÑçý·3ÿí GO^’òŒŒð—¯>ÛܪJ»Â¡è*˜ëÛECP‘šx Á[ˆCºîcêò&š¨/�Ë„\¥+aF®žDç‹ÚÎpÇ*·UD$ßgO®^V1"3©&ÑéR0€áz2Ó8›C¼p©¡À#0k>§ú&øQfѼø|©'<kÊI“kÞÍÐ’fÙàWbPâÆŠ2>1Ñjú·lBð’㾯OÆü;Í ôÐÆûtE�)¼~-|—k�…)ÉÆ}Qä´.ãï[Fçf®•Úd‡ÉàÉù€B!e˜F2£›˜òqÁK¢q’ß»o W|•ø|z8·A𒤋é0ž÷ª:Ò\é°&ÂÅ‘,¥¸±¦ÐX´)Šð(¨³)“Á`Õþ…1”-mâ5¾({n’\/„:—Á4º0}ë`«µ9û䂤¨ý¶=aŠ•nÿ +|Ý%xɵ3}¯¤ê7+ +óš'ä]„‰úˆ“K†b’#Ç$6³ñ;x3x™á21°2Ô_,¼å)°Gáº~íR\ÛX—¡‚œéL/ØrT0‘ Ó³žešZMáQ7Ô|lû¦ö'åQqß=¸‰û‘�aµÛ½ÏJÊÏ|°Þ¿?1 ããBŽ[۸ϋ¤FhÏ•œN?ôuA&°Ú³ô¾ÉÉuƒ³“6Osî;Zð®kê(e9n +„’ÀçâЂõïß%ô÷?öñ±àÌ“$šÌóLy(- Æã÷[Êð)y‘¨23–[rÕÚúИîÙ~¨O2�óm+‚©5x/y'¡1–c8-:ÃâÀž•Ÿ5ÿÄÛàíFh‚©¥¸²�@œç¼ÕM'�›ëpx÷9”è²{38¹çŽí‘TÀ—L™âû›œ8æxì±æ{¬F»V\luröLÖ]×Ö·OŸ0ftפޚ6°ËA’Ÿðú‡¾+ñ|^Þ¼æX×íþݺ6ßLðÛ>[ì\Ô+zX}ÄêdhÀO +<3œ¸íøÿõ¾¦• MæSތŒQø²Å3»ŒPM~ߦŠ¦`)Ê䜃À×ùE¾>¼1c'I´ (‰lWN_˜²OÁ„‹ÿªœòqyÑ÷¼¼¤]Û<ž\C³œ ¼ªß»‹h„ÂN2KàØÒÔÐþ®³eg”r<Y<Æ÷�¶0B¤ô<åú¢Ñµ¢ œk¡™;‘žü¡Õ_t‰BÏ°©Ó¢òåäÌC2boPä7µs‘œQ,ª6‡ßÛZXX?§›¶M(±VL¶¨¬™(ËN7›lìëѸN÷çAÚsô¶5œÌ ¶Æ!6¶Äï¤R³Óf¹°.À>éåЖû`/âV¨Ú‹´¯á¯„uí»öÈ`]U>€ÉŠßéj¸ž¡5q¡ÎQäÚvÇÆ÷0õk–‚ö1ÑnÂk,ø; eÆ€owa° meb§p¯$"úJÙŒr´ôOýt¹˜R}#^&vÙþ//ì“_Q†“½ÿîc¥HolöôV¦D6xdÊîF´ÖûßIÌ—DŸO”ýÖŽàt¤\˜`é ¤JñµÓpÛ8˜T®TS˜rX§Àñë¥ÊMßVhèÞðýp3u"¾‚ª$Òžhe*\3”VƒJ!µÁŽó9ÞÖݱzõŒ‡J(ãAÚYÑè?ñ?¾'¢j’ƒgýPzH2ISX‘ª¦e×ýTˆâ´‚鶌¤°®Oqùš®¤ýS§ýG¿—$‹OïΑÓ,É}|@3() NµZc/<:ßÞyçÉsjJV’%¶®¡ +ZŒk³•ÇÐèT0ÌSû¶64Ð1k¿mî½_ºë˜þRr©µžŽˆf¨!pnnÖ%éæâ´ªSïÕwÜÛè�«kY}ÇêJ.kÉiŸ|·²óò‘##3¬ž^™ÛQ„¤F´°÷[Xæb¬¤ü|LO‰ìéf—edèw~µØsR~q2²]&Â¥²ƒ› +àèÑnM{êP(å Q»Çõ)Š,k‘z(šr=²À¢€[MæíÉTNsP%9¹´†W¦öþæûïjð…ä]¼ªv1øHB¹û휬>ùmÏôúâXl,¨Çîà)®¾ÚÓZ ˆ˜VbµÊzŠÅßéÔñ— +DZÁŠÖìÁž°´=¤ôßài‰ NMºCa§Î +£±â—ò,×äøWœRÓŠ®Cöbb6øƃ¸ÇöªSô–w[Éo9!šaºùnˆÎ «ð ,3P-y€ŸF•è:dʤÝì’¤>Ž¤¤=Õ›,Uûb»<Ù®ôNhT'ñUˆq„ÂÄ“J<Çž—J½nªýg[ÝÖÇss»üÁ{57ËE¨8K\3¬~Iù¦ÚV9üRÍûv‡¨¤Œ<C7m6+·ìy…»í–ú¢ÃN{X—)©²WZÎðG }Ô€Í�%âÀ:oh;I ýHl Èmo‘7Gi)#@åÕX{Tu[ÀÇþÚ÷a"Z¬ã8,„ ùf]q^{9ž)1—]½{ý‘2¸aðá/‡[‹æ |Ç—õÍ=Ô)¶Î'e1†V˜jjlãØB=~2·Ä|Feûèº\;mÄàòÝ¡SNœC ÐcYW8¦}1´å.žA?zç¾öꃹ©.VmmÕS‘ÌD„ìEEâòåËMŸ3?¹9ªµI±!¿:¶Ìiµgj´q*xQ”=©#Ë&Ƴ_: +]¦Ý/¹hZ†@x‘ºIiá¨n¦wþŸvã]yk@êBþ<úúdUžï:Š*Ï7KÒR¨÷RFRv¦ÈhÃ,6û¥+ê;†TÀ’?ú<X¢9úOˆÐž„Uåˆô'd3²—hGpG�ÞI£X/…²éÆ13TÇì@˜ø:þ¢ŠÊJ`¾÷%&®“Ä÷„àís· ¡>žf.Ou²k Žœ·`pë˜1äæB°ˆÄ70±\ч6Òž€-u"‚}¬óµ¤^~1Ežâ“cæë!ÕQ;8P•r–1R\)±vâÁ‘áà$@õãŽrhýà�)Lµòy¨¶üïˆh8¹ª½—EZ +æ´^ñDóœH)$ýY¥™ÕtLYmµqd®^ž)©ù°,TɦёäÜô:]~ŠfWúåØ’™½óã/y!×5f‰"÷DH3æLnhˆ»g}ú±½æ +áÇ=n'²v–ç8;ñÌ|x%5ÔË¢�fO74önª´£H-ÑáÜŒ*ΟÁU�vNå@?Ê€è^Sîp´Ö÷òaÄB|@ðO¼œa¾ìH;þ�–Ã-EéÞ‘hC¬ =¬mhÉ‹"šáÐX¶¯³ÞCX¾ñE €ÛÊXR€)¿ð:ASxDhdùéa«¿´®Ž:NÜ„¸? Îs@‰<‹ØÞ±`Á '`Hß]öü,(1&¢°Q%fC¾ÄF,-Ý`«Pz«±Ž xtn[¸¯Œ÷sÂ<À>?Ìp!&LíÆiñÅ0…J¼Á]půw¢ý£‹Z`jOnnØUqAϬ݄«—äè¢@Û&ûÁÄ+¯ÿæëo*9Ò.Í7õ|‡9Ÿg̈{ô`8j<j&–"<Ó‚ÊÙ{�SÃ@Ç8á‹Gî‡à²H¿]7cäámnñôÐð.Õ‘Õþ%†˜Ù›À,µ=0euKœ +~oÂ�¼i[Ee˜Üø‘ >V�¯É‰%çÝIALš-HJ¯Ë[AWâ8dD½§húm ©üš|ï ÆÁï#úgGfy[ë;ûu÷w‘ób6Ž”©ûtSþ³¦2’tc4³éØ¿0~ìÔ'c”ã9ˆì³ð÷˜"}»ìõü.Âœâä|€ö5æÓ¢OµA@Ä3èaPÀ™¡¢²»‚º< +U0¦™[W½=Oùc°Ñœ¶¡6’…&Ä…8*A^^œ…™.[êÙx+@Þ®jêã-ÞÖƒœÕ:žóOvû{sÈBB·"(kaö{M.4Ã&YuqîÜy4ÐÙùYÛÛÜ‹ÊóR „ÈŒ“Ÿz'] “g¸MdNgؤß÷ +~ÿÉMÔŒC2È*ìÛ¹³ÍŠîGŠðIXz–BË”au_ñ¯lq[I‹[ƒhǼ«Áe 9¼$ß×Ç¢±''f€oøTl9ø8>©ñ<¸¨Þûð¢ê¶‹0^œ‘ÒŠñ1”x7ž\·..8 ÓZAÌçá4³T`X#Ùe²²[™H±‘‘Ø.êýxwÂûþ*(#G˜¶ö,2€CB)7AO稊Zà+)ùÁœûÉAùµüû𢓤ªJ~{kÀöDD\›Á–¾4›®»nD—òP0qb:, 'PJh¨Eî&ºŸ>Ê/cYIß=.¬-ÝýOÛAâQ'å¥5¼Š+aºÙ ©?b¿3LnLYä—ÀÁ§›Ä +¨d4jCëï‡TíK¹aFY˸k[ÿÓ{ÿ¿Ž;.-Oä½G'‰œÚDÜ¿Ãà¡šuEqWΫ göJ8SŸEvÙ3¨ŸööšÖà´îÙiŒKÌðÎ<ÊÊKÚV¯NWö?\¥6щžèpL”?!7 +¡á¾ ²ò#L�{ïSÿW“ŠŠâùÿ©.Þ)•ŠUD8ôº{©¸#:µû½ #VÛÆ¥Æå¦ïÏ+p.3åðИ@›ëmaÌKPBgø^‘dm¾· ³YÏ](oCeÀÉÓëÛZ‚›_øî;ÚŸ¤2+¯@)êfôï9&-}ø¼šI²÷L�B†CPõã]±Tœm ÄÆd�ÊÀlPöxF0:÷´ê©ßà²-"ÏÂl7fÿÐ*wh|B`ž••ÕÂ;à;¯‚`ÒÂ$HEÆàsõìzkjjøÚed²½4â xÚÀÃssIJÞ�è6]óajzmj±¢¡cö¦¶Èä +øˇ +ู’Ýóy{'œi¹Ì.З ˇ#ÚŸá{_ªn ˜ÌÅÅ–K׺Ãç¶âeù®š%/>±alžkÝdЖe´¢’‡‹hà¤,Ëf ÑäBõ\’D×kúzå|¯UUŸÑ(¿1B>9ððöÞ{ï©v][ˆÄ4ˆ“’;"ðS¼íÀô‚Ð˨¥Ôè> |¿a8v´Á³‘"ËŒ¡Gm³‘Š úÁï@Rê‡óƽëÙuè¯úO‹GÕNâiTvâ.Š÷Õài'¯A¨R9Q°Š[·‘În‹A.³T +„NoT5ȲIÑC’ˆ¶ëÏœ\]…9Õƒð#‹Úcü†®yÁ«ˆ=uâèM‘ˆ±P£¢KBZhY–×P-ªk+–ï$Å�†@[‡lЄÂpŒËý×_u½lÿõ™ÝOàA<§¡ÌeÍl>Ó,D”Ѭ¶ðÔ?+Ò Í0g9j$½ +ÁÖ2•kzØRãTÄtGlš`a0Ô¶û×·‘$ \Ë•[Á"îF昬j·C›)ƒ†EÈ–žNÊqÖà˜#^"tˆÆ‹™gB›v ùFP�—¦ý*àS€ˆƒó»w!#Âê”’ü!&ú(kÐ(&Ä‚ƒ„Ž™.x_a¦‘ö&*›ç{ „7x*<Š³¸ÆW°¤¾ó>#Æ?»f–w¯'ŠÄW˜Ñÿ2”ðF‚!EŸ +¡¥”i:Û¦HLÑŽN)›ŒqÅìk©^gDõW E'`_u1-´>‹Ê€³ÖÐÁA? +„Ù~s3íÙÃ+'1Û/ß‹bßR¦¸8ºaÃbqI?YY}_‹F_Dåýð™7ƒõ¬§B±·8ì~Xß:ÓA±¯Á·û$‹ð0òœžåÅÔg¿q{†¹9Ÿ—~`ª+£�‚ûO@™ÄÀ=ñ +Hãwƒòq 5Ú‘„÷„ù3<÷ëW<±¢‚!Ï%ñ,G¥�À”OSGMu‘g`èôŒÇëܪ¥@J¼M±±˜ Ÿ¡vл•cÆtYäZj¨£‹”ŽÅªIsÅu~üZ3¶ýÉ´;’‘åƮâW†d–›¿hkÍ®#|¾Qñݵ·þnŠÖµÁè¥XÐŽ@ öm3Ñ•‚ÒkHn$á1fXq4Ò¿‚t¿Î0¥†U«ÊãïÉlÛ�žZ˜E]5–™›AÔÐÜ^ï'ï9ß•ŠFÔÚ´Âøþ nASÙO±±%€0~ÑVÚ´ò§êOÎÎî Ù²Dc&‚6!þÝ?.ƒC¬?"êž™[ÐJò’mj €g–î2ÅUÅõB‘Êí¬D½q%{MŸçK§C˜¦ÙŒ@ŽÃib’&²CtE³wàŒqŸ¸bí`¦¹ËbZS£#°¬> f/3ü5°mƒ²Ÿ!År!P§þ…Ê|RŽ? x^Ù™ðŒÜxÖÕ1O?,$ê—Tµ¾fmu`¾sô4EÕÓóÆM{’{L¿\çjá/»Ã—ç§Æh×£ÔXCqh©ºhjª¤¿Úµ5ƒzƵô ¬u¢Øçw*/÷D¡«ljy;ARã y÷Có"oNóšÏ³¸Î‰±Œe÷êoÑõóÁ;ûV„gþÅi1§f +c•›ÝïÙM¡ô÷á6Ç“EèâÙôW Þ‘6÷e}ûØ[üeJtv$4OòÚì‹òجæ ùd¦SOúfMk¸Æ©@‘ÀõÒ—êV%0à +ËO?ãZû§]wÛ8 ?àïaê5¯¿•Êv˜Á ¼#=‰÷‚«A",™ԫ +-mš4©o0L‰¦&K&“4EÔÔ'å‹‚^yöë•M®©=ÆúU“gsáf˜Ž t·“¾f ¨A+5ú°iªý´ä';)k>ì ‹Ñ~wÓ½ß2t¬Œá„aMS 8—S{ö¸ËN±Å ΩÄC¹êÑÑL»ßŒ.5fL—{ŠôÆÕ·¥ÀM“…ñB툳+£Ü0¹N´M–˜fV0Zá*cö ê]@Qa…ˆ5¥ŠÝ¶•×¤ˆV›âÁ¸ÄôbúŠvg§Ë–´æúóðÖQQ!‰#&~#KtdNÖ‘JNâ˜iOÊ”¹RÅ+PI`A>`˜DÀ¢ý‡‚Êù.jæð9ÿ§ÝùÅš/I.ð+(ä‡)&yÉwñ•ºå2|t€°ä1T¸CbDÑdj{E˜í.++”&!Pfz´€¿„Ø Ú¿PÇaa¡65Añ¤#£Ë +Sùä\'!¦° çï¯Ïwue¥kÄ6!XR‘ë 4²Q±øÃhLÿh\INã8`{g¤Æ³´â‰héÇ:Ÿ|Gü©`S†Ï˜º¶-vã¼{úÀ¬B[[—ïŒJ”£½BÅ`îcY +¾Š€Ïà UQÿ\6>6 +yÚBÕ\ +º”Q\¿ÈfMqxð$Rd³eÍÊ*›J} +Z/…?/€‰½S“õS³¨û-"}ßLV'[’þ:j#‚¥|'¸rýzûÀ£}܉É…`°±7Ñ$'P6$½ÜF›úc ±Á¿}?–¹ËQhórXïâ’&c-´~çÄY³l1CåP닆Ù_ :¿_ß•êào‰ ©ÛÀqy8æyo,\"¼ægUýxÕzžª))†`íLµïXÎHî‘–Œ©AÓd|‘'š³Qå‡-vûÕºm¦=Ѷ’Óο"l8ßAý¥#ðÆÂêŽé$ãÏh9€±¶ÃgW ¸¶D’L¢©Âòë›×DÍE9ª7CÛe3Å›vû#—tw7ÆaSC ä„e-×YÞeu'†é„Øw²‘ÝõÕí!ÌT_Qdµ‰aX-Ù(ï<$j0(FÐe= 0ê�Zh—žœ!UHZÖ,2MÊÏÉézÈX’x̬n£'Ä`£‘ôl¸¸œìµ²fÞ‘Í'W"&uŒÃâ_튫ý‘˶{Bíò„çUû£g×�ÞùcÇÿÜï±æÛöæ‹6ËP×ñ’®¤WÂ…ÅAo^¿¬®`´ùÈ ý#̾Ið©Mt â8³ÿ0ªè2Ñ[ŒÒAåPyøðÿUçšä²Ê¬xõyŠ"j?´Ë•šb«·Ø…ßAÀ_S“… þ“‹Æ·0L��@�IDATÖv|šý‘n»CSèç ›UT¸d°Ö5uæ ‚0rC{°Ï–ŽP~Ãù·KÖ˜ïâ ‡�’ªj]FRþ<¤=ÿÆ)ð£Ì#¾6ÏÎ ‡F‰ÁhVØ4óu-žÕ¿¨A¤f@é:8ú€<L§…0ð膽µ8-©þÑ +EOv9í2;kV`Eù?oo_j‘x;aâ7’DzYu[™ÄrE‘Í:(˜+RT«‚ŸÉ|W-²vSqNãÄŽTáNÂ’8ˆ³[^Å÷£Žkš½Ã}Ëà﬈¯37´F[—^Ø»·‡¼ÄzÉÐ:$Τr‚1 çZÓ?ˆ ®—&˜n;˘øÝ.C0A¿Wè@óŠÑñä“¢®û¦ Ö\‰1G£&n8'j,[ç0uïºÎ†®dé(%ô•ÛóP$f<§QjyÈ-VãÒ}KAÚSÌ*ŠË!Å>0¼d�OÕ¤/‘JK¢a¿·)Ôg~cà£&xpº#ýF‡•žç²WŠ=&ôÈ‚Ôâÿ^ÿu™Sý°¾î$T⨣mEùÔÅøþøeïwuÿþ!žúÿ¶xŸÏÐ!�åLaY~/,Ó°tÕ�ÿÒ5¯kÐñ¥6‡ÒAùæ)œ0¶T¨ÀNÿÏÛû}ƒª×N;Iò7ä΅ºrzºhI®ìþ¥škc¢-D � ``™·ë(–�W +—ò+M [aš>=(͵þ°•™8ígï!˺äÑϺñ«æe‰e&ËÃK%ušÊ{£i–yq™ÒaÅvÎ:5šq_,«àO1hªþƘüÈêC°–ÄÍI&mð)ÙU¡ºv˜à]¾Yâó ê–Sqܵààý0ì•ßîÎáÐýüÒS¦ß WÃmðY‚Q»±¾.²0ÁK¸(¤Ô‚³àï¼ÑþÞð÷9át±WàgE0‚Þ†"/[Û“×$Òzɵ7·JdFóSV’Ÿ F0ŒÜƒ‡:QQ›ìÕ¨fîCJcµ“gz÷ý±ƒ +-%Zf¢ÇÚ¿ƒK%Ii¨N‘oU˜Ê¡EÉÄõIrˆ L€à³½(ŠS€Yñ*ý‹ÅÜÂ{¡E„_VM½Å®ÑsŒqªHÓW#Tý´Åä>8VÝ®îmÀâ"ä%å\…l¯¿AG6µy×öížnìV8t}K` +¸|ßEVbÜ!¯*¾ÆÛÙ¤Üsq?�Õ‘‡qiõ}§bYpR‘ýeÖ´xOèÌ�´±ÃŒ K[BâŽÉîϘ´‰Ì™P~¿`4f:¬î¦PtI²È¢¾}êµíHž74á…Ö†šuɹýâìfÝ»ÿ¼Óyœbô<NgoÉÎør!Ð� ôu¦ÙçäB^6rpâe"÷.?LD¾?Q7âÏ K9‘ˆ[Œ~aYÍ7_mîî¿$}u >Æ”Í)pÊ¥cÞWK1éÓñ™µè÷óúD‡D»‰ï¢>E‰ªa@2´yÓT�›ÜŽšUsz¦Ú—’c÷{B}àFû–Wþä@{Ág;ù $M`“û, ×Âñ_&æùíçn¿Xø’×uD²u;”à{Qj¸·§4ÛáÁOñ‡¸=ì…ºº½QúÉO«†oÙëϵ!pr„ÙN®?¤%Â&틪‚Û=ÃU7—16’‘—Ä´0‚?˜ã8XN0^GmëÜÁéÐrHyòR¬pÏXlô£ƒÎ8…!2¨ÅÆüUÈ$íwü\ k¥¦ECÉš9A¨¨*ËeôH[Bpä$+/À1gkLdeyvv¾êÒ"VûÄ0‹%=Ð+¤MB¼øT dÁðë®q(âŠéý]>ÄÿsBapæ}/u±uïY”rÆœŒ7G6º¨¢Ì…%q´í‹C{Zù„§©&SU´ÁèÇÛ¼M}yZVVÇú*¯S·CxY:`çD¥,ÿ‡í…‘ªó®ç€e„¢ÌT³”|ñÒœôms÷s‹Ûm›¶÷u6I#{b²YW‰ ß}!˜i¨{Õ™lòõ„ÿ´{»ŸIñP·a)Ÿ®Ïšíù©ñ }%‚Ï;›¶;æP¸ùBÑI®·²Á7.dè5;7´aźîB%q¿¡ù†mqúG¬Lm ÒÀòæ„tc< +ã\;ü'*='®q¢î¿yU²Å:[QTÍ1Ù ß›œçìB%Æ—¼kÉÉÉÙ±�gÕ™X`ÅÏ·’`æ‰Úïk7¹ž3pøÅ@| ȨhH"êhµÐͽ°K”áøª›ÏÇr|7,ÀVX–/4u7OýpÝ‹\Hæ,¬éÝÐ ¯˜n#á_´ý¤ùþŸ^™uaÑdÅ$ÅV DÕx Œ<ä mþß› +ûÀÝG4ùA‘æJe-Ôy,ÁKî·êí·=ª¯æE®:¦”º.s[mÜßB|OÍk¿ÞXýÝ’å‡/¹q¤“úT„™¦ïaÅ Y§™ÚƒdD°õHÏŸ3ìz 3€ícð;Ï×Yê¯ÞPû»™9®OAéYÔ ¿œ`É¢U¸Ï×D4Ò®OÞ|*²¨¯zùå#/‰˜ƒŒär'¼˜#dÞàŒ=t,ú²Ój}ËF–N/û¡ #×#i;aìZ®ò™´“{²?tÄ÷ÿS‰l0™–ïå5ö²Ì,Âä,¸oVÁ“þ¡XµsÌÆMˆ,ŽÿÁW2%ÿµ¬âB.¬I< Ʊ6ËË”/ÞÕüý•}BàЫÊæ? Ô@»-…~[ý@Ž é¹HQ®‡ŸÐ�A=@ A‡À¨LûÎöµËWO.ÎÜ™¼¤/ xìK +m&î3¾ À7¾YCáGï¶äÉœ#Ï'qlB˜"½9‡°›ÜÓPâwÉ0{ãep àg?šž2qwN‚ßüÏðë&k´ÖM£†ã¹7Bû+~2P›¸Æ‰¸'ó1‰ãOV�a0xs32Bð’ k|#ûÂñÓ¦žUEE +àƒu£{Ißs¯¼%é«FßÉKë½Gp?$Î=Q÷ã2åOZ´ð'Ÿ<ûĸ®æ Îñ8Þ2‚Š*ƒvû |»÷@>S3zÛ»TF}ù`[ã*¢¿‚²8×#ÖTcAž˜1îî;ø¹ãr\4ß8¤…¶LB„»Å£Çþãë¯#vE´P˜Ý×XºŸ$I§ÍñH#¨ˆ«ç8Ìé¦=vï�xz¢ú4x 8Ä”ª;¼æPUŽ™Y²P4ÎÊÿÜâÑýðÆzÔiêOi™ŽÅ Í•¤$ó"ó6¼Å£0ˆQ°ªÍGÈè]`÷ܸ7¾:´-††—ÁÙÁË{9)ð‘Œ9‰ª‰cŽÞ/®÷>€‡r +¤´·YR¢NŽ‘’-Â^3ôIâ>¸îç×#5g¢ªSáï©‹ˆ®E6Ñ +MZÚÿ'äåºxÎþà†*‘ËK[†¾î‡`½×B³éðá=þû‘5p¨„ÛÒž»i84z„Ûµœ©ŠXÛ`†ûyò«"“Ã4ì_G–ò~9½®¯Â#°@ea>ÙdŸ;§¯³sM«¦÷*þ(8jÚk®Jh¢ÝûFæ’a]°g¨Štûˆì´Í߶zûÚi~ Yá´oà–Š'|W†Ed&t"ÌÁ†h¥çòòïÝ3¤¯Ã¯¼ùFÎ4~íd>b +{ P89á^YÓSA²?F€”EÉBнû!øÚv3ã©ÈvÙÜ*SóóEY©ñûcè~ìoéó7í=EA,gt16j,cš» UÿnEAê^ò¾UÔyG³VÖîD]2÷¶öÎÅÇâ-hÁE|òï€p€1û¦3bÚT¹iɱžñoa¼µmv’}6¬Èû°g@¢J«î¯)Ý ï*Q&úN:}(Þ„¥94Œ!PTŽí—âØýKÆâ¸p;¼ðøãÚ°1#[¬ko)”UVfߪls^ ?É2YÓ>EåuvÁòJSO¤h%ÏcOÁÝè]”ss.‰hz²¨Å˜ßYÞM@vï°bJ�šÒùxÏF2Ä`˜H?fžÂMutá]›£ò àL8—·!9ãŸZDa\^Z\t¿^o§Sm•ÔdLÚéP—s ‘ù¼À&viÝM|>èƒ0ŒÆÐês,Å倿¹¢É5nk®k&ü‰c}ˆšš–Â/zú8Qö]8ôé¾hûÓnÉìŒödØlq;q^bŸ¤‰¬ý_¯?«„ð™3j"²6~‡óEWdòSv^VÖuÏÖhÄ´]'î“*r”äá%¼N'#™¡L³`´t£¯l(;Ï8Pªu{khAèþâH©.³‰ÆÀƒ~©ü¿×ñ,. º©ÂH½ +«!»-Ò>B%©ÓÜÝàTè({ÖäÞq«j6›ò<‡ûS6$ÚÌn +D¶º£zÏÑs° o¡4z£Èa¯ªM %×#}îð×p—Ôä‹‘,q^’ÀŸŽç’ìÚ|‹ TaAyÎSÎÜãDîÚ\ßz\C&ÅE›¢ƒ†B¯e y¦bј™nw–¸CjC–]ˆÎ%7ùm¡h:²C§ q�Žùjàç3†®½ÜiiCWÍöˆ&¡:¯)ZogaRZk‹7œ›ê†>ý´Ñc؈dp –ÇÀ¥)ÛasÙ½‘²Í>¹µ,Åú›Z° +“má–öèVdª‚$øypI›,²+Ý6SX‘aãAÐ0heg/ýýᶣƑù~:„´È#m`ö¿dú·ñ‘DƒÄ*JTÅfŠ0ös@Û¶§mÍ×-SQ}D¶c7ü,[a¶Zq«áëÝÑ¡ŠF ‚ç´ÕnÓMVìıdJ|&ûwžy&¥#ï`µ¾‚}92–‡¨Îź‚uë*›RYŽ +Ák‡P¨uë[jCÊ…�¬Ço!ë'Ï¥+) š‘{‘{“E…|î¾yCÆVäÅû#:ŸŸ’¸ÙWÝ= ›L–5%%î¶ Ç“Ô^·!üÄ2ׂýÁ†´ìGc²v3„æÝß„€ä×øíµÜ?:ô¿7ʼnOvYƒg8"”ñû“1o•;– Éë~4®Î«žÈìz®ÅŒn-?Êž ð‘-¬¬|ð†×Jº¾Xaô•áuTê2›[vêñ—«¼wŽ‡3µ¿7[¤J&äùD +‚@E�ó# I+ÞÖªõpw wÒ·ŠîtÈÏCæwÔ^ Õs(”?–¥è¿‘Ûò>žA.´îSá<;O&Hô{}I”6ÛÃ(aNú¼·32¥&…'iO h§g�|ØÆ3_�>ø7ÄÔΑ²Ö)SÚ€Iê$ͼoPÁÀ3+ ž“öm8¬Ì7 ú̹ ž©t¿±úY¨«wÿÖÖÖôCGývþhöæ#µ{&’‰šQúg¿Ÿ .òù¨wc¬L„Dü•ïò¶Ë჆ÀmE°67fGm÷G¬1ˆí�ìîE¼x-XÐë‘?0l˜©=ïÀïÝoaÔHªº¦„?CAÜ?ÁçE ¤â“A€ûtSP†ÑüýFÞ·¨nTª‡Ô õ °´¾?ò?ÿtLøŸŸþãG‚¹MAg,¼ÉÎÊ33{6ך_„d!§Ru€˜u ÇÄUT…3‚ÁSýº& Õ™eì¶+Ë‹ºVÜîPz¯òó¶´‡¯']r’•Òhÿ†Rå;ñî>”Ã)IŽÌÒ„¿–JN-ÄÜ+A|Ê1½,”ö——SëŽ7³Z(…2oÀ=@$Ú—6nú¨PjÞC[Û#]¾`òÛ¥¨X¡¿Ì0ô[ÁÂèE£ï*:÷xÈ\¹%'§åÀ•N@`ìrHv¸SèT5úÖˆlצÁéÎKà‹ŠRòcºI·á¡\b¦%qoX†MFÚB—a’ïé‹KKƒmk–n�4æU¸E+P~©®¨¨(„¤a²'ÎÅw1ÂK:<D¸ÒûvîßÕT³Ï"SËÚsc,,áz×ópÛ¶98î9PËZðwaP¯ôÉæ<‹{_űÈÕ“’]y0Ù†Z.‡a¨ÓÁ à´[AÏa2œÙcÛ$A÷ý’ŠÄ ;ùø¿4Tóu¼ÖY¨2rÆÐY³â%Ç` +â¡3ƒOIJŠ÷áT`³uÙ³„VÔçh|ºÝ~A`pû6,²Šöz{Ô|×Ý ÊÀÛƒû=™‰~l'°Ë—Áû‰¤gãðý¶KŠú¤Ò9&oÙrdÁÒÄy'êq/¢÷ySÈ”uß>ßÌþ) NZÒ/,´&ù›?P+i5J +0ùCϸúÆõR[hZqúþ¶ˆ±E¡ƒ@ßÕwª†S“ô6,ú‡ÄΉ:8?ÒnRÉ…ëhú¡÷‘â¾Z-àeÔé1Sö@gtdâ´y*‘€KŸE„Œ©îIüös÷]/èϽÀGØ©`ú,AµAýÎ Ó_DÂÅ+€Cx)]=�,e×ËN®A„…¤‡„bÔJèB²Ù"K™]("HÃ,u%f@Yk„˜™µ£3xÉ.Ol"ñ¡?Þp3PQéš5ëÐC 0[ãÚð© TéÃ(.¾Öæ0é?&í Õ”YÎ,G»�üwH"×�Ké€{`44µ'†˜±œš¤N›Ôjóþ•z¤G7\2¼°Kk'5Òén_ª�÷ý X† V"„,"]GÝ:ˆep*¢ÁXìÐ}‰ÆŒ¸4MlbP)©Ž 7JœµŠiÞ�Êú\o‡y°)Ñ7\{Û™¨NñØ:wàž-àiXXÝ–¹²Á[F\“0.âEVþ³Á×privW;â×DÏÆÞC +ó»¨köIÍþm«Çå¤ožŠò+?–Û®åZŒÍMǼa…‰RlI‚*}AP˺ؿØM룀?Õ§X-+°#ƒXí¯‚k@§Tø®Ù¦º:2·ñ=lEÏì°Xºæ'y¾Äz: A»+°˜*ΑS®VYaPϤ¾³†g‡pvkñ“~ Jw–fê¯yݱ7€ùƒÀ+®ú°´á¿mŽ`Ûô/4E*CgiQµ*1ÿÛçû[ú¼¸«!…<ǯ<ÓDY’6 Êo5x&‹†ö¿Ui*ßy&t:¤¢$ÒŸúÔ¶ê9ݬÏÄõNä==¯"žÜCú@(v}½t£¦PwÀ5¶�BÞ’SP~ç…ý¾è}u邳&õxnÂK°vµaÍÏs~Äü¹ãp\n?vsbŠ:S\ƒÔd:™ZÐ&P/¾ÝÜ#%NÙý<#3æ2°iIW¥ƒ›Î9„þ#ebÀí@Ûß!~U9¼x©~¹TSµpÔõÛ“í»¦0ÆqjBŒ9wk»oÉ‹`®fð"?SÚóí±„™œ ‘¼ +Qöù€”d"»ë½ˆº…T ×!e|ŒãS–c†A0µ@’ÿÖSÞ!°%rîGûs“’xÿ´ì,<·#5‚!„cx„ÚŸôìé¶m¸ä¦{‚!gú(«ìB±§§¥Å]#`±$ýÁ�~Ø'Ë-VÁ2\åb‹ˆûñHÛºo¤-«ÜðÜnך2V áD›s¹j’õï-)€huÝŸ˜”D`± V ,Q¤ƒœ>0É×ýú?õ™Œ}Nrö³š®íUÌÛËž|ÒÆU·–˜¦‡.)Ç ‹öo>8£ÐÔ‰Ã"·êØ ÙTæÍN¢Ímñ[\à‰6ÛšŸ+=û¼¶¥OPìⶕ;H ‘à¢Ó’„w}²ZWM§¢Sý]»ÏÎQM9ÑFð'?¦iò>-#ù]±3üLŸ,eŒÉ=eº«_?^Ä؉ûýÿ±'Ï{cGø"¸Ô$E’jyV½Õv°>A×™hYÄ™¡“þ·“SäÔÇfõ̯Æo&±" @2Te°Ê³š5j¬3,RLjÜ\‰hʉóûÊ`0USÙ3À?2�ë„ô]¨æÀ"È8nnó—Øyt¦ÔÕ2"øx)<H¶ÐP%MS¯jä/|’TLÕuÛÃXä~�yýOƪKCøOþoŽ!Aééˆ_1Þe4å6ÔÕºIáØ'›×¯ø¡àŤ-ôD}à¾c(‰¥šwÔÕQýÂû^24¿æ¨¿Â…Ð�-p.Å:ðxÉ%[âÂ#ÞD¢%uŸ4&OÕÃÌÃÙŒ5“Û›³G÷绦ÎQK]‹ïSA‘DªùQfNNWÐÎÆ8/D ¬‚å´éžà,©‘\‡ÜoVïüfbÒ-xÉï´ÀöÄ$#UwO$nÄWG^rÜ2kH¯"ýäoÒÿB|â7[§ÄtmY(Ho�6œ!Fš¨g®jöŽFµÝ¸˜œŸØH[좾U‘Á}`º0‡ŠÀ»í +P?E4…â’’-Êdçmh þac[èå í¡×7wî[ºl¸NÜÕRŽŒ³ÿFð’û“±G +ñÓ,+|8Æåò~X¾«ªÝߺ¶SöVËqì%Ð×?Cdý) =¶ÀWý``ÛÆ8Ý2C&%êÙÕPnž\“l¨£{†•¢nõéwÅÔ,|IBPh+ȱ óÚž{×»ƒ¯ÞM2ؾßt})Ãò—8<Áð>¬ÄXDç¾Èè˹¤¼øo`#´˜1Iö xh^Œfu¨¡£/é&Y´\VnrªÏdiñéÅUž‰K"ûí!÷‡Ãú-h>ÝN³<o뻺14aÉ’%ñùý&ª>îÇÜ˦s!-ñy‹f2/q…½ùIT¤Ã3“ª J~ïêãxûd¸N3ˆCgóVÌ›CQÿ"âª`u~8-Ð×p¥vBäÿDøígKkà“u¾ãâÍ鲞VÇ‚9‡Õä³òO™÷W&&ÒÊÐÉv¡‚Û-×ÊQŠF7“4qY±a"G%)6&ÿpä½ÄêÆ¿@4÷®YDæÒðáT×ñ‰óûȸƒiû|‘àçÑÏí•íJü–ØxÇr÷#H8Quäo,½©ÒqLè6˜Ä¸šZ +^à(´y¸Áøy˜A*qÛ¸e…H£¡Õ6ÛRÅÙšHílíg³¹»i¥ñ—¦iõŠJ«‹Î„À’T¶MÍO·y'-AQÑ£ïKHÚq?0Í!27ÉÁà'hìsø{]X•š9…%vA|øð‰¢î¨YwŽE`ž«ô†û%Lw2ö«ÛÚŠ×6ú¬mj\0}¿îÌNšnsã»øâA~#B™üŸ8ŽÐSʶlô‘¯Gç&o=\¢†¶¨ì††„¤X(Þu¾ˆjËÈ@‘äÊ»ìsôϼiŽÁb¸²‡ÀG™§÷g—fl=Zéh—6ÁwŸn"j0ÜP(õAÆ&3s'œvI¼I´éDÝÏž=ÛdjÎÁ_¼ÊEÓÇ‘g‰1ë…E8©%ªNFóoƒ^ójü‘góÑÓ52¸N&Ra"ˆÍ€PÜ•6~†ƒ¼£'êØ$Ú]…²X…~÷L°m˜XëÀ´ˆKœõî÷I,wt–¸+¯JJƒªO!Þ1ò%„ùq=Ÿ Hsì%DZdÁRU3ãÛàOrÌ$î¬ýÿDø¦9³QŠš: €ä„Üˉᑊ©ÀÅžr¥44¤ëa~ZíàdµkTU?>Ñ6�>wÁ_J4–°Ì—L˜öíá7ìVqüÅi*¶œá¹‹@~ lï3•¾@Æç“=Ñ%:¼õÝÀœmN +ÅÌ{ÖÀÿI0½„hCKçYà¯ùLÒò¸V«›Ÿh‚úòجx–µîpåÞ*ÌÞ⨌`lCèPõBqĵ'2±×¹}o #‹O÷û“Ï’j4ãaÁÕH¶ËòO¾ðÀµ¹öúšý‘!•@9àtšh-°ÄAF¤ncôZšæ†ðœ®O;ÄåöÜYÎœwÈu¿5ñ‘“[ß1†‰Þ©—fûP|yLSnJåÔ]4/ü“p*ú²ÄEODcòKðAý@óÑAÕ8÷¤‹/æI¡ÌÂò÷Xië;,ϽÀSækëÝ{׸ݤMäÁ'-‰ßµë"4»g×·G} ‚’h]äùŸÀÇ€°7&Rô#榯9²W|þ¶k>ýΨ.(æÕ4öÑ`Â;ý¨‚ ‰[ÅSÐuó5{Þ94ÛÑñmí¾ +Wr2ªo0ƒŽ¾½ofÁc;|¡»I²KâœmOÆoˆëA˜ß⢫Ëá?V”!“f„UíRÐ} +À~TÙž½2É‘wß¿H¬r ÍÍ«Ä솵*0àI«à~ÂõºÞÙc]û×þa¡/…;Á߇5U»þÛÀýwtJÇËÈ (mŽ.µ[UÅ| ˜;1_ƒUÚ5¦£K‡Ay£GÂiÌÄ¢Q2~ÖvÄÿYW8ê¤ø*i'›*ó1‚*·BòE€™=¾Y]ÑÓæôᘮÓÐc``Q/äÁ!¸/æ!ævÜÜ$)½p+€s€´i…ÿòB0{ >Ã�.ö™Á°ÏKî5ååßÌQÍéú“ "b±ècð·äØ9¼…};›q¾ÐCï¡håckN„‰Ž¬Cú “–v´ƒh€i.Ç•hQ9øjeC{Éq(Ò}V'Œ{l¤Í°ýXŽ~$Íšqå˜Ì]7ÇïªD0m\ÁÃRÛ9„£ûï‰Ï`5Ýî>ƒçú&zŸ6l©¯íõ‡Î&~ߤ̤¦ÝÛ7펲Z_h0^ +á[s#3=d1gŒikƒe~hçöI±N ˜Å‰/§¶}!/Ö(h¥ +mÉ‚{g&žB¨�о™œÍ6 ¢ê§ÀžIÏ´—²¼“~4FPVÏâõ[Í0ܵe›„ÎÖÈL3¿÷³kž™úðÚÆð�Rr{u‹(YØHêj¢-ÿÍ^çèRÖ4 +ÄQhtb#Ü"/½‹zÈÜ\1f†Ì˜KX•ºÓnn"ÊÙuXÇwß“òBJkkl lczöùkÌyu6ÁÂ=È‹É€B$[øàqº·áÿêó±’zȽ—4xû[Yú^dûÀhÛædÙGÅá͘¢½æSµKlªå¼q¥€PvR£CSt7^Ú4¸â¦ìr‡ú$8¬ÿ¯úrïc…–{$k[K,ôÏ~®ƒ´Nû!<'â0†üºÊžç‚Àc�Ë™Êû£|ܽ-Zë gðvȳíß{¶Üý˜ÿäóqŸpñ²,4]ˆ¥&)ÛµþÙ÷Ò–‰�ü´0¬Ò¥é´ZdãôD‰—QŠHJ{fwã\Õ,Ôp+Ê¢üZk˜>Sk“‹¯†v{›·ŒëÓ'®%oðz];:ÃçîóFîØŒ]RÕ å‹ŽÀ&N(̪ÕxöqUÑæC ¯‡;�>Ç2Ø Þ3>Pu挦ýmÝ + kùdϺá–ɨq%ˆ¾á60žbÂÊh;ÜŒ@ë^WVV\ø’šPð} ×F (,ïͱ¤>¶©1821a{¥Ñ!ô‡³b/4ÎÛS³¬«ìµÏ.?àÿÿÈûÀ¨ªìý×çMϤ'$BèÒ¤!(»`±¡bA×¾ºØ{A±#v]ÑEDš€R¤×„H/“L2}æõ÷ÿn`bÀ ëÿ÷vq&3oÞ»÷¾{Ï=ç;ç|Gš¸7N¹ß)�>G5µ h×+ØöAP>`µºÏ J�:¡‚,ï0ž_²Èw¨8»KD +.ÈSä4f´8ÌÒÜö$&diPâZ úÓ‚Ÿê&ÓŽ4œ®‚6i¢=xZ{\ €Þ—hk#ŒÐCö…fŽg[Ãñ†%Mqù#\¦ÐAG¸ëÃ÷á ¼tÛ/ nwes,"÷ÞüÄŠÃ%’~Ë<l>瀪aü`³`=g·ƒ‹<ïT°š6ÚñMñ¨2:°a5+á¥??µãyk‘6K*'Ã9ʺ0ÒÚ_�ˆåY8-/C¿±ïP_`3üÊÁvÕŒ¿{hË–{ýæþ—NÜ‚*ÄP(D”ÐɬCBÒÃ[·|·ãL—« E?‡BÒ€ªÝ7Û¼þ. ¸éÀ²eá€dìBÈU)±%pFŒœÑã7ÁNÿ¥n÷¶ Ѩa>„j™˜QœhwóL¬³Óaí½ÊÆŒ·�MÂÛkA%•‡Þ6³ +ʹ ! #yOKS¡tY®¤é,§G‰µ@¾ûwŽ–€ôçÇmýIf~áYNhSꪋ:÷-2—AÕÁò’5æ”WùËD¿žêR¥ƒ®P·ÓxJ¯#ê'sBÂ-¨&ç‘ì¡wT©QážSZgÝW S…&毧×À©pÀ]¯þ—` +NŽ1Ü£=›ÂK·$;ÿÑéĉ¶N¶VÝýŒ±«9+—,é’�>³8¼ïÄ›6ŸNR—Qdä.ìüP„ðE–u+( KêBN0ÑO7ÅŠ!Ô�Ϙ1>5s0µÓ@Í8h¦§ëÀ2úäæÞ‡‹y‰iˆ·%/#çÐ^‰ót”B:ß!Aߪãåk»Ra0&ÅÇࣈú*(Þµ>Å*Œâ(öºp’oª‚×Nâæ¦Ù©5Áb3I2ãõC{81QcM¾�î¥ `H†p.òj‹Óü›Xº�/<¸»”¨<Ãíx™a²€ñu€ÀÎ×54'+Cm¬ n† Dl¹qnª˜º±H|ÃÉÌ=YI\ÝPÄ ,’¶;ÓÄe؇†±,´&ƒy0F"QÆÃ)8Ñ.êQhÀ&Ì`’(B±®Aà(Î…Æê«—£ÛPº»MÂc‡CxBòöz“’hGïG3u"rxS]Ç6¿Uj “Oæsªš¢6{ðXm9p>kððLºs±«èØHîöIR‰°{ô>>75µj�`–kþô†ÀaüÉ…·ÁQt-I`CÌôwÓÇoö;t²i±M€.cæ‚%nĸéÓ÷¡ûÆ‘Ôô0"!vQÛi +>AY›ŸÕŸiˆÂ ++ ˆ•c]7F; 3ôrTÞ™ß�†Â‘í’I\‚³ƒdP‹Ÿ²X¦ÛG¢XvpgwG0¤QCÐÛÔ£»ÇþàWþ>áš/Ù àáy_2ôfD’Ê7.€xZeÕU™CTÍÕ¯»QÊ\¾Öfå³ ]Û, —eÖ÷Œ-ø©2É$£m„ˆ´˜©HEðµÃš2 ~¶9,#Ž÷°“-ÌãKå‹ÒòWº¤|²L¬Öõ3ów8ÈÌaR”ôMµm’ž¼v`»¤íCàÙ'ä1‰ñ"f¾na¦ÁQ3Zw‘!Io€'¶nWSÜ…‡è@Š2ê-RþúúUÍ;ß7N;ãDÌLÈãÄ°Þ‚Ú`sã±X‹óƒhheß-Ù–äga¾O^ù.døJ\~—ÖÙT€ûà³0k2û/¡¯Ú+ªñÐ=ܦ`ß‚ôdM”@Ó'm%t‘Þï—¶Ò{5—�ùäq„™¸ñ~Á¤Dßxž¯CØhx©žêŸéÞð{¿¡×Üp0ø´JKb?Qm¢{¹ª+„éɈìÝÄäÇ€¯F{ Š(ÙŒ§"ïÑezcµä[Ê +®÷5U}NI8×̉.9÷¥‹¾çzIcc:™·$‰öw6Õ…¯†™Ñ¬¥+Äö•—·àk‰6ÿÚ+ ’TOŽ²&ÒEÎ+³J«aŒöÓ®#ø;}›y•Bë†g&uÊpÔG(i3ÌÏ×2ŽpàþÚ=þŒßsýG_Ö$kSP¸4 sS•u¦l[mm3ß HAsœaãØNHRýŠ™V_Rr”L ˜<‰à!I9ÄËÿg²N¶Õ ½´K˜°kËþ“‡aõÞ|qu\ÕfjƤOd,P âZøGcOm˜÷ê«-²€|O²!º fr2d@U^Þ‚zøÛý¿G ô¿þó¶q°QZIE¥wOëÖÎOŒ°#¸Ë ¬ñtš‡ V$e™©0VåGÄ¢òV—²‹ ߈éõE +å´ÜÙeé 3ýEhS•À7ÏçÀ[À~B‹ù”*Kkéx +!‘.‡0ë$R–œ’p½PÁ»ˆåSYUóDk«¥D%Gâ;brmª ŒÜŒûV÷4¤^ï8¡2zÉ[_V„ót”yuBƒÇ†`FMŠÁidl@2vÂi�_ )þ#J™ï”6Ô.·y®S¬Vv{}xøŽÆÀE[ëCg¦ Ûop»äšXÉîÕ‘¸ùf$&=åÕþÀYød@,Hé¢×Õûüeä^¤]»›¢£°‰äáaW…e5f•ùvÏ>7-³KÃá®8…9¢¥Ó©xÕΘÄQ¯CY™‚¦_‹ô݇±QÔ;Ý„/þð×¥íˆÔ(&ÓÓ"$ݱ£"Ü3=);ÙTÃ'¹=Ž®úÞLÿ¿M¨iÖçàr˜ŠÈ’ƒØÐ&ºíìŒ%U¡f<›˜aÀŒ»«ï¶%ååEú{¨`}´¶ àÆ2±‘y€ìÏD?Æ‚ýî³h\}PÓÃh `ró!½>:’@DÛw*î×t(˜½¶&rúzàÇ›«üc7FÀ¹¿8Wu“ƒ;r!c•7é}µ¯gYä]5Í‹Èfèïlé+,À–q {eØÉsý·MÇÃ#ú¿ùßµáSPr6âvðWl‚·×'ëÚc};ô¸cîÆ1&ß|¼ÿÓ¡§``ÌiÅÖ*X“mÇò.·qÊÿÌGDÛL~bú_Û²ÔÔÔt¹p¸AîR e zƒÕW]IœÅDÑÛûÝ—a‚Ay2›¬òQ"n©µë<Wiª°âTpñ#§e.ý;?á°iÄ‘²#-ÚÂמ,=ÿêiï?¾é›0ØÎLht„kìè; 2&Eƒµ”±çþ#þ?¢´;g&AÓ³!£ÖÒ"ËÓXÏä÷÷ÎÂó´m}ÕªÅ{ p° >†åæJE¾àÓÀŸ„ÖýH—¬ÜGB>}aDò›µ¨qãƱ:ÍŒFµŠó©•syæm3_znÿþq2ÒÁ¸ár +È–£©B€<{×7Ð<u +âU·!‡îUovRÅø·¾ª:žã¡Ô»AT2 +euXáºKÐË·y#s•ŒŒ¯ Kâ_2U”«c@›G"Ez¶®©o±Üí; Xèç4¯hùwדh°¢²jq ²]Qµïû¦%íÆO4A€×D0nàº]63Ë&.œ5;ÜútrYÊØ¿³Vïzòã&CßìzŠ$êç.!!5ž¨b~™SHñ×Êgr‚p÷oÙpp÷ý³»T2VùºÌ.Jym&øå³Ú°æãw`ÕIv]³ËÊÏâ)$”…J•UÁ9œ¼§¬±;Âéž–Z=cÆLúœ©w}~<5õóÓʨ5š¨42´gc?–Ò»7=HF'<e]mèkÐZ¿€Øa2f?;Ž°šµX4Bí~<ЂÐh¾¿öbÙy7Þõq;}.YYßÜæC’Ô>†"t'¥§·(Oú³¼ÿê`S.ÀzDå‚»aÊAuiæ€Ië÷aû‚•ÂÜ…R]·»XÙØŒUÚ¥"–È?è ¾¦Öä‚}¾ìz]ï–.ÒY¢Nñ8]…p€=gž¯ÿÓCA„éž×ΞˆE0ºœGxb!š¨Uò©M¤OÏÇ%ˆ8‚þÄ^Ãeååí~=¯Ê].© ƒ(ño´¿z;•—Iª8Cꪲ†%¤â6¡>µXYpmÓXŽê¯vêCà?²îþãÍp#`c•¿«]ÒÁn4ÝâhkÝR’3=}<¼«GŽ- ¡G06¥‚¢.U¹i@VV|“?Ò“’”®™Yq,½#Ì‹öNJ–Ë9‡#\·OãR½ 7ÞY¨Y’œ\`qRá‹9šë|¯ÌÔcº§¦’ˆŠ6w-’¦igðõБ:"Ù÷rOšã+@Ë0£‘-çõŸK±ü¨|¥¾¸|a +/Ž�QËó$)8.ËWÛëR=G©˜ÏKËyxèÀ–+þWpô”CØŒiD'88^¬ÖÎF|e3Kv랧fMÍ€¹.¡rÜ,Þw†9ø*Ê÷<Ѧòî¦Ð=ÐZï‡Öº‚s4È+q.‹²;÷Ôú“¾tÓÜNÒÖ_:Hûr“³³ñÛ‘]Á`†šl†OÖ,K_ùËÄC·>ÿÖÝ6v³o8JyÒd‘ÚnR ê!YÚð¡Y)ûP䳋…À†a‰ÇÕ¡§€¶÷4¨ñyn8,"fPl:ù§Þ4†7Xó\"Þ±àãÂö.¢-V¼‘Kƒû¾—:Œ<ë8†n¤ó</2Ÿ 6|.vÏ[á0my²)ì}!ñû«BƒiÞ¼�Úw&ÔµÝSYôÔšï:Ó_êû1ß1û|Á“c4;Œ5õ5½SÝÛñ}³@>æ¼ÿù?Éâú¾îÖB„”¹+e òX¸IUßÎ[âñxŒhÏ´t; +Ðô0ÝòpQkeÙEœ®¾‚&Ç8Ü·D@†á•™Œ˜kP‹šYQ£ªYšíâ^ ó~ +–õú¿4(DÞû"WÖ`Œ*’ÐD¢œÞ+ÙA¬,ª.I¨ÌtLŽ å +ø‚©í4ÅãÃ#`<Y«GŸ"0 ¡ŒfLÂg1@Yßxo#` ]…µ\ìdŒE‰EÑ¿;hÃwŒ@ÍZÌœXCdòZ¤í‘ÉqìÝZ^ò4Neé‹$,Õc¨î ö=Õïѱ¿·S¤‹>âáÝyöï/E*ÇÝ4‡µl^ÞÁíöˆzäQX¬`±7vcÆôQ)ë#€ Ò×!œ‰÷ä•„¤ùŒè»¸ïd˜'Ë»tâ÷£tšF†Ki|ŒòÚ-8¨Ì»ñÓtU{Á§7ÁKNë`o×-‡7•rÃìô¿Fª´§uŘïÆZ¤:ß‘æÎœ´šÉù:°…|‘5GŠ~z¡Qðª†L¿ƒé'.U¿7èAs <ôõ(óLÌ”_FYpBM)0{¦Å—B®õ[‚?Íò”ïÙë_¨†ÔYq¿ô|ƒ»rΓáXÀ”‹ÍA +Ë]˜ÏÈãÞ„8zZ0a©*‹—ãÞ°tųB3í-Âtž8?í?¼ PÑ÷{L¡½ÌÇé¬ÆèG™µÍ /€,ŒgrF³“×ÊÁØ34Ï®=¸ü›½šª–±ºù,Ʋ„>1ºÍV8*†‚Ÿy.kÒ—CÀÈcŒE Æyú9«VýÄlFÆ‚˜•‰ð¡6ÆÆÀFÝH<ÇtÐS§ü9>"sˆ3Ø×˾aP™È!f]<ûž¯_=eÊ”æ2è“{¤Õ5µwÁ¦s%ðͳ#u©%¢Í¶ ª-ïNžƒv»È³'Y®h|‘õgêþ&à0ÐÙ²ÏqƒÒ¦/Ej÷ÿâˆ4CˆMzÖÏ‹XGЉ˜É{|±fS'Óáhhå!ðC_…9¶�}¨„àõa~ök«Ê³Dð..k¼ ")§ksDÍúOéø±ÎÔH–=6–ª§÷ÙSš#þݱøC`‡Dcˆ²ÜïÿÁ%Sy‚ižÛ±ðÌýóÖW$1›¤,<’¾F¨¾D}oÖ6¥SÄ3ü ýP™Çi]ݧNøN3Í&dâúä•Óè=(TÕ ªðöÝçÅUÕ†„ˆHé…uä#Uí\”ñÅL3kò!P>^ääl)¨´à³ëFÝ•™¾³)R/Xèwê6mÚCÌö#;ß=§àf“á»jk›LÏ;©az0i'¢ º®ß¼×ž€'ÀãôÙ‚äH ²²f-îöPÐS?¦sÝ°ÐÕÁÏhž ¬ô.‡fYƒËîÄ?#6츅´‚be<ÔºˆÑÑfDêtwR™,Üß±«·ÃHÝ#ëá=ýS3¢ðF¯M2E/BÄzuš˜Xµ wtάU´Ú\7ºLnm~JsÙ“£4º#x7eZ x»ñ'Eö3lï±1ú·Uì�³Ìa*ªWµêåäù-ãÇÚp_ôÄbT%„íýi9'Kžöú{ËÂ`ckC$"õtC˜âðn(„ÐÆA6âÜSÎ+aÞìƲP±¾¾Ê.fWÒòº/0!™¹ÓôC[ý0%Àllþ=‚/@¼j>¸jD¢o²6.uæ—™º™å,<¼QŠEÇO2RrN…kÖºÙ>¤Ëú;í’¾G<K‹vghŽÕõL$®lÖwøú¤‘ÂCÚ¶b/SpÊm¼ƒÞ Åá^<‹TÖšyÔ:',|èZ°¹¼Š²¦ïÄg%[ØSáܶÂÞÆÆ£¨ô»¦DgãÖP”Îq'}7´GL¹Ét1¡bŠz¡Ç‘ìÏÿ…aªÊùŠõyC¡,"sbž“° +¿ÂUhã3¤$l +ׂˆ=c¥Çnz°érÁ(ÕX÷…ÆäIJë3P¥[tsB!œÆ^-$¯ycóf</g!«´ +Q(ljH˜:2GçÿPÍ—hðÌ´Ç–é QVÕ(#-:#“µFòëK¨†x9²¸ˆ'¾Y³ädåmàS?ÀÉõ “£—X,Ió!1P™‚]9,\½ÇÛÊŠP>íZL¤µà¯¼Yj“�®#™Æ[œNE_ƒcgPÝ·X7uÈCf£ág^pðÝ| wŽÊ’9ÝSÐŽïÃ!ÚŒˆ†`ÍŠÂæJ¼‡?ŒàBPÁÜèt¼Ö 4ͳñ;ëðyË• ©Õj€/2—‚g~8ê!i =qOÀÚt’i‰ÙÒò;r_Â66Ðn¯¯l›ðŒqC˜â>ÂïNCѹŨÈ{H‹2a²ír¡ýr³fa²ÆZ_ßé¿å°¥¼“éV¤‘¶D:ßÁWä"$?¿pM弑½[·î®Ú÷ȹ/ï!Þoò2÷€•õ„ÄFD=‡äŒYH:鶞“Ô4¦"ò#áÀy[€“iHöØóHìuî)§Ÿx(`“Ðs¬€&®Âߘˣ/P°Ë(oÁ^%΂ƒÿQ•N-yÁQc²ew—1iêV//‚y‡Óf[¾pæL}sE0˜ü{pÂÞ ,¾í;¦F9ž“]å?¿l±¢Àñ›†öšLcŒôåYàýŸî Ú/)ÕÄ4…ÁLß•)Œ“æÍû©¿‰N ´Ä±·¢ú +»´URÍ0oØiƾ¹óï^DÝ6¸ tQ÷´NÕ|0(k>U¿³âPcبüH´ó×^w65åÄýc(ë÷îØQ>™Œg’²X&ý6kèFÔ $½8qBÖÔ?ËéËw¹JÞ}î¹}½çÉ`³ÑL÷˜¦£Ø/-ð³‹Dôä{òmˆóO‘UÕ ©^ªD—$®õ{^OøàKxŠAø¢ev<™RÙKŸ[¡ÚwFȵ–i0Ê7ìà |‰êË%«ûz¯{mcu O£Xè{°2Â"\ ¡ò®•ŸC°ú¶vb>ÿóÍç÷ÛK°§®eiõRÙBÝñÔÍ߈šÖb05h»#£sN{‰Ž3w~ÈÆ +ëŽl±³ìD`™¡ÑžL‰1§š7ÒFr¿¾Hþ&4s ¾=ÚMXbá {&ì--"óá3ÿ÷Õ5kÉǬ€Ç¹Ä#z—ydÁϘaZ9vL膮>Õj¨Æ/Ž»àIP¶äÌ¡O±˜B@Z‰}$P¡¸Ûñ{‡…åÞhÉŸ®6"^‡ýlÅq¸rÈzˆS Eëí}ÚØ‹c¦ðeª/|'4˜f3þp«Ûþ/éñv›ð�'Î@@:‚ÐÍî„òin G_s!îw6˜u.ŒÇš> + ™œ¯*úl˜k;pÞ]aotÆ_ø/¥³A¾ÎèªùhMÀ[ŽÓL”›ŠnÙÁí[Lå¸Z¢T0VÝ1XY3vÕGk¬º ½a¿™‰ç$ó®÷:Ž:kHù~ù뇗.(ƒöj 8îV0ôÛÙ¼€¬>Qe?V@»‰gr¢IH-o›RDNlF¯ÝÝ’¤’èçŸõõJL©.þ9*„\M0Áý¤é·îÓ×%M=°>&Ã1g‡ òÚúîêå;§F‰sÉ<\ý᫇ µ¬á hÚ…ƒJþû|È$äE;^|0 ¬™™¤‰6W5Ö¬iê[l*ùºH_‘ø¼Õ+¦ôOsš|ÎQ‘!®ŸÂo0¹h)ùl-(6Q'ñ†1žGìüªó»V@Èw¿ç8áÂ×f³ášl¯hr”à¬.˜†6„†¹›P2¢ÛON·xÑÖR_HyÑm#‰‹À求È}’ÜgÐ ÁŠŠxL~JëÔã9Zä#;¤ÖA[c«*R‹c;Lpp2Ù)»²KRRé §³ñ³ÏÆërÑú2ñ´ò©¼ ý¼OZ8‘$*Ò MDÝ誆ú&œ+àXøe×I<`ËÃ;ö>äo”Y¥2‚'¡á+qæëÑ={ŽÞR<ƒ(Äi\4]ÿ5ðYdqˤ΀“Lò›¼äÊÈÞàwÇ +÷Ä=ÉõïÁSo£OìãF�”ë’}K‚Òuœ“{ vNÓ"Ëé. —óBÄ憴¢ÑÖÇ6nWó5Qj"›R$UTý\#JÜ·WÒ®M ™&ëºÖCždhõHüÀ˜üü`ÿ\÷ÁÒ–l–•TžÐ$È5ê6¬ØƱIp/‚R¥ˆÌ‹€eï5djr½¨~J*X Ðê#Ðà†v/bgÑQw3ƒ¢H6ðÓ*" ÌNk¥\¹rÙ²J+£Oïu4i6¸ÛuáÇß—?ž!õ¹ HÆ";ÐjåÙ!)¢õ¡Ž¼ó +„Ê¥Ka‚ݺY²šçø$:½/h“Óél"Ƚÿì‡vôÆž+Á)„„—¡Â&²Ó¯Õ6–oš2¥››Ygne‹„aú•Væ”þoÓ®`УQ–Y˜Ôçà¹ìœ=þØ+%iñåµ¥RÆ“€ +„ÙÝH¸ºdÞ¿¢Qþçè7ßlŽ¬!ý&Ñ4£sRª£Ñ†Õü¯]ï—¾‡bö‹²æ—~ÛæwÛ ,'(\,–E–S±[Áôíò,OÕc'ýz��@�IDATôVÚWâÇ$ZVØlT·0M;ÂúL´è{¨‘û!<‚f@›C8 VTW§°´£ÑøCݳ<7µ®ë%®›x%^K`¨Þeºƒ†akÚÐt0{p„ø&0×T”ü§à�çv=PÐî0ö<&Jr h¬Aœò±÷(ÃPmñœü%šl|˜à&ÞR=Ž£YŠiK1e* M!Ss†y‹¤)“£Ço+ß·Žh‘DˆýÒCÙËM2l)YîïóŽ`²°u,ÛàÔê›Óá<Iú;–@{`½Õ§^¶ËíÞNp̽¾p•f>F˜W>ØF§*2áÄ &@ëœn£˜pçÇ>@.@ #%E*úâµKÉäJŒ×¯½¬¼Êïq´1øI*¢@aÞ*Äô¯Õ‹Ûº‰¡sÙ-vI0lq“·dY¥Øºh4|Y(WÆí€Ç‡â™tÆÐ$¡òÄ}”jÄ€©ß!j¼¦�&»3ÊÛ¹W·ó†Ç ´1¥›vR‹‡Îå8êYuÄ{[ +Y#–2ÜbT1¿CÝÐö4cdër‚E ³»´Â{ÊøþùÁ óñhHë}©Îoë·Õþ?Ûg‰øT"€HòJ;W^Á~k¼ìÎV]É\pÈ7ü«ç#7U¥µS/ï’µ}ÅüùAÎ_Têû +Å]ÆÆd}#jžsGß¼ÀÏÏüÏ|Rˆö‰™ô@Wqøa¡I;A‚Ž9`„ñ÷f0.@iÅà‡™‰ >I5[Oò8^ÿÏ´î×ïrÂ…ïí‹K,SežƒUƒxzo£:²7ðcaaÛä7¥õÑŒÍEJ«Øµ S ÅýÎ�þà†Ò´kBYžõ…XÈàÑÄÑ +<9/ÕP…š0\ nTw„œÎŸšp›{\úÖp Õ/(�µ %ˆ”‡ éÀ[‰ !‚`ÜÍwtä~Ç‚T%ƒU}ªw†»¿2Ás;hŸ_ÍÊB°Á«šôï‘Ó¢ÍËÚ»ÐöÜM¢$2`SYC&ã?Óx.Jã<p$†—Ú€X]˜¹_ "¢³iPsѸw„ûøääíuÆ9}¡6^Åùó~II•hW‹€$ž{“w¤LíÄ�÷FúóÅÛ–ÚvôëWÂé)Ù/Bsœóã-^6kÔƒQA“<v[;™¥^FVðC¦ß jC$>º `EE}¾ˆÁYžzàØ͇lfj‡NÃyŠ-€˜êWÚIQKãM©mÛ=íþMaP£< U[ÏçÅ¢ÑJ‹Mìâ +àmùàÈÃ}¶‰RÓ%Š˜ò�,•©Š©ÝÆT$}TKÕò)Ùö5ˆ½ìÍL_ +Î3V`¾D³Ý¦&oæåÙÎd;[—Ýíy“8Œf6À¯Q•²qp–{Û2ο©áÿƒ'‘òïHaÕ™Eq¾¹ˆAGÀe}Ý4óá²\Oqâ¡ÌÛÛ-‰(ÆÈ|·ø•Wj˜|í®”¶ºõÍ!_Pw.kjb»ˆ6õŠ#éÞͧ“5ÖÖ|jëZ¿ç3rŸápªRQŒ¤æöM£¡à©ó3Ý%(ì×fZqðþìÈáHØÚ…j§v»ãݟȆÑ×ßQÕ?ÃÊå;>Þ¹¿÷óŸ™ðÿî‰FäŠÇõIÒOÕÀÅLºÛD‰ephnoKðîE9ÅÊ܇]éŒNNVaûz‡V ûés«˜4AÑ›#rpW!ˇ¼zµO<mÜZYÂx²öŒ@LrŸ‘a5=<PKèÈ)´;ÍkJ¼LõÀ.(Šzè`=»²¦AAÕƒ=˜ÄÃÛ¼£À¥£¦NýÈ-a%ÎÁ±D2æL"XËSò}HÈI±ÙFÄuÙL²‹Æ§û|ßìûtvݨQ£˜#øn‚—4ŠZ4 ^80Ý·".ÂÚ9rì¨.=Ð?§K ‡p+d"ºeʃ @CšÛ’8¼’°³ÞcξZ˜K¢áÊ©ëÖúꨱIG pþF ×pyyûMS,ŒqðÿÚŒÝþô1"ã�)”j´9§g)uâ$¿¿[VæèNÛê=×é‚ž®çokk¡¬�ûzqžaäŒÚp÷µHB“‚ÁÕkê Ç2qB£_g{6’dŠÆ¦ƒ–dkº‡¢ƒ•Ó)ÛQí$}"Zí¼^!çƒX7‡ßQxµ«W¯6úO™Rê·¬¨öM*;hæÆÆÆhïvÉ«N4ØößéÜØàÝ¾8(V5ÈMÕ™–:Ž|uìÿ¹†dXÏÌO=€Eåí1~ü~wJÊ*´…à% ‰å�¯ã.¥(Àøòƒ¦eåM"a’Ãç⑳ÁLßh1ä%lܬ˜Þjs!Ø?KêÝ<:?s?SkQ©®m�±ú|â-7j²’Ž6ð˜0k£þT µ©ÖWYÓ’¹s”úú}VR/yËMiXókϼí;ýú§±KdÅœ&eä^_þÝ· ŸñÊ•++©¼¾ÛÓ\B{¬¯‹`*š!þJ#ø*`¯$»…Áã>þAÖw“¬Þk¨\¯¡î¢rŽîïýæ„”Ž_UHbDá<NHŠÁ…Ò»Ï(xžÃMªºpLÞaÒŠÖ %ÎLϺN!Ðkš�Ĺt8Æ&ÒŒ¨Ô· {Œôe#:¦·ì:¤|óº…_F·®ø6T‘ŸÛÃäU”•—Hw;)šZ�·ÑÊqP‹ÁÊ2íY“ÊD@XLc—•ê¶¯AÖ+“MyG&¨™g·+í\–x¾ÇÞ"˜±žŽ¨ÞzŠU`8d?Vu³l>Bª;6Xv¦·¿ÒWÔ%ÃÝyñÞóÏKùÛ(Ħ#zeÅÛÏXü……”)…;oã®ÁDh\JSâô‡o'Yš‰<cBÌEÓî>Én2g f'ÅäJârpÑüW^©¹þú‹Ú4ƒadÚláÒ¦ú’Åo¼$ý©‹)“1¤#!Ößó¼ß¢¢tK2—¬2Ìù) +e<<"/e?îoM›Œm¢-[£ƒƒŠ +†Î†Êñ5z)œèpÚ“»‚Í^; Òãòó#g;Õl+%wJNŽ¶s‰Mo<ûlˆC®E°ÚÑè~Ö>©Ðã‹üÑn;}¡=½R‡‚(qUÓYüRÊp(ÒÙãL_ª,f·eìÏÍÌŒå9±—5´æÛ¯ëÍWÙe'¤”øj«ß»LcY„¸1�÷ *5³Ð4•'˜“UÃõ©Y(Ë0+Ãô’Y¦CUX÷ ‚|‹³Ÿ‡xîÝÝ37WÛïtrÒSABg«ºPIæBbþ¬¯°î¨} Õ^Š¶&¡"W>ùÎ@åêÝñÈÁ‹ +Úý,Tó@Pn‡TÙS1Ê¢fÝâþÙÙG=ÇÄ8ôïÐs|@1ïw +Œ o%k¨¿¸›³~9²¾lNö(w çp8R‡j”v¾NÙº6Fb›:¸m‘‰‹´z%¿ó*ò•Þ{ëùÃk¥Õ×Ç}» 1h뛀zÂB^qÚ7ö›+WšyyyF^’(¥ Ü8Ìå°f¿€fùWd¢B‘¢ÀƒY¤ZßR¹cÃrî±7 ë`—˜vW£˜jA±Åbôçï©W‚™ƒÇž"þþÝ°ký“‘ +š©Ên±î‹‡èÓh!¾¼bUÚ¡¶¼¬;½ÁŽ2Í>*«ÒLkZÕ˜Ã82û4� gÆó*Yn,+<ÆÛúK%m˜±r%;('‡…�¦£aƪØ,¼Åæ¶ZâtN’•°¿qMRU[íi}mbŠuqẅ²ä—˜xZ²Ð †L•¤ŽðXÄŽŠ®V‰:ûöyÁiý»õØý…Îî›ÁÞ¯h‚¨Ù.»P‰ª— àZìÀH¦—šTlÒàôtoâ·Ä[kpÈ’3¨LhrNÃT×Ñ)®==iåˆ0…þ…cgCì:†3Ÿá(zƒij3c¥¶M•\LNOÀùm—~=ØP1“PqǙȊOð¿Ž‘ýó‘vëû©ÍÐFÐ0+tMÚƳât°µmAŒ±,0F_ñô"äf!OëRÜ¢–“p´¶š¸¸¤.,× ‡hQ™*e£xè{Ö¼3¹KÆ;wT\”bµÞRÞïÓD`im„©RÏØÜú0B¦!0ª›=¨ÊÓXÇì3:9ê½yæ_×ÔXóu»XÎÅØô˜EÐ)9”³M Àì#QÝeå˜829PŽÔ´"[p醲'öî IG4hE^¿aPËfìýþlÉêêRQÞ³ÙGÂÏHûɘ%Æ'Ñкæ4™Úb°Py€Ÿ» mÇôVÚ19oe¹ÿœÊ¸úb’ÈvB6œD•èHÅî×'JËoÇ>SÒ)‡dó,ûÂû@sœ€�÷U™nÇ-#ALÞú¾¤’ù}£Ï<Åtû锶h@ZÒÜæg1ÑÆÄkIS¤GÌ ?`ª/ž'ÃAÄ—‡ x‘Áêk»8>Bˆå²y6ã~‚Õ)¨wÂÊ´ÇÊ ©l fW¢&`âšäõýu…™K#Óóý’z•Sˆ]/Òîóâ†>,×.ΚéÚØúüõžhã¿û�'ï2$O×#Hý3®¥ÁËÿé·¯¾zðx‚‰Õ0 º9ykÇCV?Ij¨·Y/*ªÑÀqºØ°)ïg»/q hbþÛ`ò`‰ILÌ"´ÏéÕÞQAfæ7Ÿª<ˆ<ívÙÉß¿>mʯ +^r]bŠþèåšqH2 ÁèÞC{ø²¢ÝnNü’dˆ „g\‡ Ô[·c(<ËÕò[púüŽ¨l࿳YŸìÄóº±À½HžÝ"´|XèÇþÆÐ@š³]�sùd0ëçƒMlíÚ,²ÍTìÖ÷;ö=YXMQå\ë ¬48¸ŽÜnÝ t0 †ú|-0¯ƒ(4‚·9VW€´ÞÝ µ'KÒÄMñœ7-;iE 2òX¶· +•VÄ‚ú1]É@:j"¡±t¬UÔ«¬ºÐ·9|îØÆàï½ÑÒ¨78¤\´³YF¢<Te“©îQz““Eño 6YZÌ/LèE˜‰Û`>,‡…ö˜0-¿ ›ê»²NO©–B§jL ‚7÷ÔómQ¥§1ÄŒ©0¥VIì¹>ƒÖ¼lÕ²]µñøõˆ˜¹‰"µûeš£þ +¿÷c‡z÷nÖp—³¶¬AZ"ÊÆ’ú[[4ï6ºó§ûh@2:¿W~}Bð’¼W\}Úœ"ï$©"Ñ!׸þ Ä:8dššÕ¿¼i0Æøp$6§¯6Þ^#+³œ6ŸGl&¤YMþ¦‰à%Úb@ÒÎðˆÃ/këá^¹·¤D{? +÷ƒ"öql £½ÁèŸ<èJܼÞ1rl +a½u][CµÃeuíñ1M"…6B1C’áÏÖ8qtvzÙ’} ’ ¶"X¦#´Ü+ázñ1ß5FŸÌ°§öǵ–ïì.a4)§Õñú;W.Úv¬à%Š±Ú‘!ÙPÃÌHH[ÊÍ"Óp>[WV#ëmz RO6†?äøÝš/iY£ÆOµ9Ò„K-Ó‰;—Í¡Žk‘ò=9œãCàRÝä0›Õc‹á”? Âé|ž‹ÿ_êOÕg½^V˜(©Úx”Yàuð¶L^(—ê"_ +M{d¦c÷\ÃÆÄÓš`F'[³ÖH„°Ù£˜¿×üW^I‡]wg§€×W?¡~oS+%¸mëìŽ è"@cÃáëƒÉÔÀ÷~Œ•W“$…P‡ÆBà ¥ça·HF|™ªóɲÚ3¨];¢}µ\›´SuC“·¦Ù‰ø3 h× >u´[`K\Òy±›Wô€aL@ºòPLÔHuÎÔtõ†õ%_’ †ô”_]9µæ¢pÉn¨FÙµµºªÎÄ„-'í@F¢u°\iîΚ®‡0¨‹Úë²WÆLuI(X»šhäZm¤íŸî¯Iä™+€/}«Ê–i"§fDc=¯M©Hÿ +ÐŒ¶¤"0±,›å`#+}UU}2ÛeEhýŠªˆ|ZOß| G»Óôû‹’ºž¨}m†EœÇJºÊ2qCeHì¨pÀÇUUa³(ÖÁÊÕuR]µJiÌÉÐGÚ"ËúÒ9säc£;H;ó¥~üÿòÙ'»ë3$ðH#Ôî”\‘y!/¹â7yƶ,q¢bÐE²Ùxf ¢‚›bºÞe†zq¦™,°lýjTb^º¦gúa~jÌ»`ÏSæclÇ*x¤êýN/ìѼ±"‚k gÅúæP¸ìÓļ#B»Ã°³’bŒ’æâ„‹�J`¤ÛÇ Œ|›²y£ñƒY³ÂŸY§Ý_„™‚¬ ÌMs¸N1§NèƒÅâÀ?È`*þ2À¹àY"—î'ÖÏCçHôGÙφ?,*›_as9VaT°ÅpŽëF òmnF +G’1þ¨çB„o¢qPbg±1¡°Gƒÿ}Iž„Û`B€žëQ_Ó𘃣áªË Ueï×k½S-EGhK@s£]`lÈB "4i>ã þF‚gãQæ5¿®u…æ¶qL#*=€@Ëx$íWÕ›aEÇ€Õ@ÖÂ#îeYÕgrbC0 +ºt¼ÁÊITѪxkMáèÖÎû¥ïç'¢!â¡ŒjVvºåeˆ$„sˆ6jìENÜ!ÉòÚÝ»ëÒÓ™ 6ĵÈ+© gø ¨˜<¶Ô{ÓÓËÇ“Éväø¡6x22“ºEúÇr_ÍžâyóšqâñS§õQþvôÔ˜tµ@ÅÇ€±¿ŠüŒ´1½÷ ×’vC±PZø3•'³› + yg8oé‘F5/$r>ðçv)f¡Z%"n/‡© »{$ï™ÞÆf@ÎOoí¨îv–‹ðLî’uõ~hÏÿýB”´Ù—éÐî6$›æÕ”70á»xTzì…½3êçlx|—ƒÜ}©§î<ï¤Ã0M³¯ Ï_�ì;àÉŽ‰p,ß„r7É*&†`bQ`hÿi,ÅÕ;4éƒAyp¶1wz#'Ñ¢£ºwø¿ xÉ󘜕£µgc’±<ÓeééàùÙCÝEúÿ,øz»ù>qÃœŠ½Ñàºu&C¦rrÜÀï52Ñï¯@5‘ij%¯Ë|Ó‘;þ€O’£™VîŒõíS·‘9±¼Â?"¤ióñHÜØ÷&ûýåó<‡¤ÜVB“ç™Ü½_.$åhp…ÕÂ[=B0ÌoCº^ ~íòu >»ç,JÄË×XQðz5cGd=Ž<§X�¥íDªŽµ‘ܘKDö f1ß꯻¼µI´}IyJã ±ªm7ÍÊ™ÓMŽ¥u†W®JËu¼‘ù>ú£‡'Tø&:F ‚”¤¤lyji‚h†(ÜâVÕ¬@k+âòÇv‹UO§õ“‘ïúNd羡C‡Æ·ûÃÃÁ£’C«¡… gò â9½3ÎXne¹<àw1íúügÏö%ÝW¿@œúÚ0f¾aåèE0=ž•$êdZ05³<1*vvÃ%ð •ÖÁqcWbšÖ$éf}KÝ'¼êloÍÔ7ªja}†,ûY^@¦œ/Nˆ�þ=Úù•€‹éXt5H›[ïð‰1lýº!ò2~¢"µq®®ê×À[ÿI ŵðHLêbÄ%ÛäX ª¤6€¢qËø‚L""8%£³3ÉÁŽC ù_!ƒ>Ù<=ÚßÞ;ï»ÜÐL™Û5ƒ™xrš½v3ðgÁà¿BÐF߸*'UˆÑ2‘[ÒöÏŠêíÅõEzZ¾8ÎËã§ä$Y¸]¨÷¤ >ï¦Ü=œu+¬Éó´&¨Õàôô «$c»¡n¾u‘U îƒø`JûÔ¹°%U²Ü9òìr<–y±£&k6'�i4„¶šJ¬Ö¯³6¦†:<9«–%6¼c›·Ã}|M\]SÈû6àªÿZ¼ê±mû#þ^u°¡�¬ü×a£GÎœ\‘î°×{hoB’{’5&§¸ä$G#"er¢UQ¥ˆi¶Õ¥Ìá˜ì%H`au6v„“‚ /cªßB{#ý’ +=@ ¿…¾QY{Û%0ßEjâ!á£iq¹ÿVÃÐûZDöá1Y>iQG¶S€Ãž 2§àòßÓÈÏç ¥†ÕiIé9}’{pþϬ>Òfþ¤“¬‘(å(H¶wÄ3Pð,6^Õ®/@)xÒ7r¬ªME5°sê%%¬'dMv)î1$%Ýn ë3”4ÃbŒƒ…ˆífù¨lÏÔæü‡_d ,æI,âEŠ"Ïyxõö†ÈTƒ6oÅ„±œó K¦ú¬wÈ>-¾°fK€,fb’äž9ƒ§‘bªG$„/¼5X”AŠ_Ž‡ŒJÏê˜1Ùi-@=yR~ŸÇàw³¬QÿDÙȇ#Š¯’¶¤>”$ð7`7|IΈ@ën^xµqÃó<ÛM&?‘†GqÊiC¼Œh—w@U"dÐZš +kB&‘µÅ¡Q—h’<·2X8µ +¢uþ%.q´`#}<ö؆¢”ÇÍGµç¢ª´UĹ`÷™c‘•7È&Õ†*Õi”=µó¢¼–Þ›¹˜õÍN«ë$ÎäLÕIEéqHÂ;±Ÿ6ˆ®ËŠSÍøa•¶LÑÝd”ÁógÍ*>÷–{Îád©a_»äMµícÛyìß?TOóÊÊ�X'•!U?Í#pô=i]^ÚÎóæÑ\¯aI +ÏeAð²pÄÕ~¨ò¿‰:xdI†ÞtS˜Ù‚²§kÿ$ƒÜƒ<g†Fúëâh;~ _œ´‘ìv3™#F+yò1±åÄÞ ûÙMàëì;ДDXAOÕáOç8<¶_Ö¿¿.o<n*«JwœýÑÛ5ß\3ív'§Ô”È2",[÷‹Œ?hö¸¬xܬ]¸PO(Û×̹(ÉÔ6”€Âó`›Ú÷|‘KW!ÂÈœþ¬‹’‹_.)Ú3)]D9ä;³{‡ºO÷{O…†9S>%®˜[\vöÞȺ›–$Y÷Ù£GÛJ<•PºBqÊWun%Ϩ¢Cªv-]´=qn붶~OæFzÏž"RÐvå¿yëåêDÛÉy‹+où’ZS +í<÷œÛÂŒAd–îÄ–^Ó3gÇòCÞtQ°ŒBI*¬ +$Lm;Ymïq"ߟpáK‚ðé]Ä ÷AÚð×í•usô«M]]¤[mËXEŽ¬¯;Q€1i£U`¿ú+Ÿ™´–h7»|±þ À9X0ä ˜<½2—磄OÍJ˜×ªf,Dn9h5õѤTvb0ˆF¶¼¢é5à¬kbÚƒZL‹8Ì–Vù¯„ÆÕÁÕÞY]_äËHï|UG§ý9LÄ-19|³…âC¬ <D1ô„ $ŸæÅG€ƒwþŸ %#mG_A×Z<½^¿dtNY«†~Å£‘ó’lÖaïó+†„ˆ†jì +…Ñ¡4S9)²c]×Ü63ñH{©3hI‰>´õºÙºÁcGyÞŒ!q8DÑ–—ú§[JqþQ¿ ¶ù´ƒhˆÁ¤A×Hmãyï²={šz44˜iii´§Ï ³Pïqàí=SøE]À;eRƒÉ½‹›Â×BƒysEµ_ží&¼«)úpéQPRç{½'Qä¨0¹¶ÚL>[[+µ/i +ÚD&ƒ|{åA8/[kZdÁ‘s‹êÅíu…¹.zˆ@sÕHá.Dàô�YѯÞÑ9ó%çÿ«ëáKà,<Æ‚Ìáäì¥Ðæ¬ÎŸ×ëðX¥áÿ«÷ø_?Ÿàº©™b{ÉÍÔJ!s44Ì3àhÚ§RiÜo¾àW4²®½©ùIºv3ïP]õ¬óºf”aÐÌ¥åMúþeD5RYò¤ÓL{˜ðJ!N„Á£» +q†ÍpȆY•k·>¸æÇ%ÿLÔ“#cH¬f·='ÓBÇÉ&×´˜<ê,®—ëØ #z?9éxã„©v°çKª¾æê5 Ë�ªÈ‡\<}’&«‹dNRw”•EFôÂA¨.–)Ð˽XýÞ{Jká}¼{ü»ŸC&žØƒ€Û²˜Õè)˜ì×?Ô +ò +\Ý,<»¥2I¬L‹5}fš«!dÃ(¶¸G’Ô½DðnI´?뀿Ì6y K<ÀCFm²$pÊ2 Aò›9hp»¥kr‰Ç²²²RŒ›t +0:8¯îŠ7c¢Þ&åKUÒ_;‘—hÖÙ61ä·Ø@§§1±ˆŠ° +æG<M¿•çFRRTQÓêUY=á¨ÏAšÌBl*2—†…d¡µ Õ8YB(•®Ä(TÜ5÷;Xæt8¼nBëéQÅ ‰Ï36ç×Ã*ýSìÛòÃÑBWÔ4þMiÓ“_]}Ûk‹Êý³VU&/¯õ·Ç-σìâ„ Ü=ƒl.š°—Lt“¶¼x÷kO–úè´ebˆ6‚ß-š"„Û»×ø+ö~nڅ໵ʚõ¬¡¹Ý»“ +"è,Ú´BRÕ+¹1ÐXf·¬,Ç®Š +϶¦ðP”ñºã—UýYÿ–åï…‡ZÅ”™k£W»–^»ýòQ³æëê½þ òf<FÍY3gÖîÖ‚—üšÝ„àm¾š.ƒpˆª@'Ó`žnAê÷‹CEÓÙd~ù·¿Ý^¸æ¡m¾Ðåë*ƒ”wº ›ÑV” ù¤¯”×›±¯°ÉÄiAŽ˜d{"÷·\ûÏxáyøþýÙ¬¡j¦ê½9Ê\ËîlŠ)°®z{§·#úEaé΄`ꨣfß b ½KÒ‘Ž|Ør«) mÅøþ\ßÀ:º N5¥‘'—Ó¢†-5Ž¾; …:šÓp”E‚ŠübÏ“O»om«õAðÕeˆRR´ØBÀ_é†Z‰«ÂÙÀßÑ‘Ø°j\ó>;Ôp6øKÎ"Š +ùü¹Ev!Öj-”¤Â˜®¡fdfC Œ’N-èÓìPguQ×™ÐW£ÁäG¬DûÛ¸ü ùè„k¾à’uFMñjšÕkƒ!sEªË‚J¿Ê$x£o� J Ö5-öõ_KJÔ{õ¿ØëâXqXÁ(íGT‰`(î|}lnTµÅŒðRŠÅnY`|‡ÊŒn¾|Öv%”œ¡u@ ÜQx-ÂÉv·ð,ã|gmÊêÞÜz¤VV‡žCI†áÙ}Sµé3Ç e&8 ™"Ân"£®žšÞ`pññùB¬M»nÚ{N–¹¦ÈCµMÒùñ +¹*¹ý½pV¤k33´~IÐ9tTOÆ!æ0Ü´×cÚw4+w²q¶'ZtTMý> ½cG€]“…™ Mú¿¬zl<íµ1œLH•²¡½ÞÐ?";ɹ2'C¬N²ðôTYUF€…rœFÅ®){?(½™3Ìy(ôž¬àÂGËb ý%“/ÌG}Ðì.˜½ô=\þš|G4’‹®¹Ã±p9n¯ªú½Çeñ3ºôƒ‘¸ÿeBÒC&;ÊÑ|ŒŸ�1 .·kÓê{8ŸâÍ|K„ý¨k®ßa ž˜ƒF»Þ7EÌu[8_LÕ'õÉŒýV(‡ÄŽ£v]ÇØ.÷÷É'{£Tνk¡í\žžçwš *Ñ®&KY±¥D¨ˆ~/ïä§5ÞóÍŒ¡?Ý+Ý]z"ûsbFåÄ^…X¯î,OJE�¬;ƒ¾É¡ž +P.Cë{#Í&Ôƒh*#+fÝV)ÄÎȲS+†gd4Ï+ò»o€ÙF5õiC§öÙ¬Ì9ãZ™äïïñ¾ˆßßÑ`šæ¨â‚ô 7Mb•íÉKtþëq +Ïx` +?©^ÖŸwñ¨8¥ËA¯õ÷+:§5þD!x.oXNB +ý8T¯¤Œô8Éð<vT>Ø[7 +Ñ.õvÎÚ³iÐv»¥kmDꈟŒV|«Ýɉ*z;TŸá‹…£åB,6*-MãÌÿwÁ,>â¥à tçúc¯ýGý}B…/‰Å8SŸ„Zy)BO°ÉЗmûrÿ¦ž£:ˆ¬ëråw!쪼‹âUãÚ'bCé~¢-cl©¤£¬Å:ÕÕ¾+üh_ª³$5~~Èw‡ã¦#ˆ{•HéïC¸ñ˜,i(<™ŠµZ€øÚó°XUð?œ~‚Åq©£ÌrüM/¯¼‹E9!¢š3«òJ‚Ÿ–Ü÷؃hC¬%eœU£Ñ–)a:òRÞ|Ñ® +Iâ�û C1 +`õk„8%ƒ¸ý슆’ïɵ4kÖÖG€—‚^`èOÉè«ÀŒ]Xð35S]–bPæ—¾2õzÚ"L‹Üs2Ùl4L¤.7j°›x'LÿÓ\ÆêÔ½HÜ@.„YÁqÔpÆ{±tٌӔuò¾]%DË%×ø¬¨ˆ?žÓ‰èçžÛÎQ§as¼××S,ÎÂwž›Y<qút§ÒýB§CÓyTSÃÏü}«7x)/p/@³®Ttå¦>¿1@þر=CëÏ4~X}DýÆãnƒ)}©–Ï4hñrÄž)ë('éÈôúm®ÉÚÙ(‡ð½¡¢ûBÉa—Õ?ÂÁ±õ;¼UÛ7N'¢ÿ‹×XSÕ88b2— ˜›¥± üDŽ M×ÉÚ=EçC#ó~JX_\•Œ€üO²62®šŸ¥¸˜™µO/…iŸÝ(ÿ�1Ôö ÐË»dnn-|[÷Ÿ8TeùĤ¨Áô#æ_ôˆüNY6’:¥¬N¬÷ÄoH<nïNÚxx�W½ï«cCÇ`Ùtòªêø˜êTÉûƒË™y%®{ËÆ&ÕÇ,7¡Ú-XÛèI,³íû…iOÏkRÔa·YÔƒ5Õ…†lˆÅŒÿäh1sý=¯6Ï5X¨§À‹8ÜSNÝMÂDH©vÞ{p- ˆkQ¾d¢ÆÇì ‰š/1VÑu*+XnWy&Åë/×9vmLÑR{B'Ú„´a”§8¤¾î‰pê÷¡¸ã«Ê=•ïFcÆ «zNÕõ îå-fæ+žíѲÐD˜ip¶í ,rZ34qôY?ýÕõßà†Kvmꈈ B9Íç“$B;‡3›…»“§Sà\4J…Á'ôêo¥~jÀ$®I&•K©&øR™Á™6! “¡$%ÙöO$‚¯œ3«ÈÐé÷!ø|·k”ô`ó5‰ÆGh2ŸÙðÃÞÕï½ZOÇå±™Mgb`_EÊ$©³¼2ŸÖQßa†€°þ,Ťöò4wžÛÉW¬¯j(øxÕ£ÇqûHLþ5_]sú#H⛄MsW\ 'õ˜>ÖÑó¡uÃÍ6Cоթ¾dd�Aµ¡Ý€môTTikœ#HÊͬÙ÷Õ«¬~¼„ŒÄù¿å•dn¯m춻 …")!FÆ3©6~ ÖüˆŒòJ7‰�ˆCe:ÚXýû¨¢LÇÆd« þqÌxžYÃxxO]LK*à 7.«ÙµuërÑý–~þ'ÎYòn3eõuðX$ŒÝF°¨CèÞ>sê½—ìñµ¶<†öÈir1æ_!d¦XÙ‹@A9ÿë2ߧ~ÕüHHÖ°ôuˆ<(=žà%Jˆ®y󷢞b í¸ÝeaïiŠ©]B†>WÝ[w3¸CûOÖÒ’—^ª0´À¢„¼ ß#;ϵ¶*p2…pô¿¢™ÉwÊ1ãó¸®ïöÇ,7ÛYz“È À«"¯WÅ—‚aÚ¬âYàŸá�ªÍ÷®_uˆÀÿé9p„/=œÔ‡²üœ©9°bprY +•Ôµ+êøñS–ߎ»†1ÍWØôôçªh.uVšŸ˜åâgÀ£ßÇ!šóf¦ø#Zz6”WÎÆÐõЀ%êO<£w”±‹S¬UDz`ËAª ?¾r–fÁ52HL„•? •«·~æTMØ›‚ n–ÆW\¿ªÍóèŠ É�o†?NoLdöxlý]M +OÖ† %Ž"±`ªFXÕrÊÃÝÏÌ\Éņ÷€Óa’6Ÿl¨«;*Ój + ƒ¢¢"z�(*S3œKÍ}Bç¸WxP9r&?0ÎVhx'Q¦úMY¥V„Âœ'CÀÔ³œµUïPlòÙŽ‡iLJ«xÃ5K5SºÜ›'Ìœ‰l_ú„oX‘êûæ-m.7³íp5�¢Ë1Y}ìÀÎÃÕ;DBRuI(Xë¬înœ¬’—æ´XÓVÂð·yï󖓉Ãg{cäڈ̴±ÂãÐnoò6F– ¿kMÆ8+;Ã(¹„aÙKP»w-CëÅ&#LspÜHÝ^N·Yê ÅfFbÚÌÒ€·%9+VBËþ½™Ž¸ó½VªZdP¸ÒÛAšS£sü=€þv¦³†Ôí{¢jÎvK}h÷+Å^Ki%võ®°0ÎÃœ�¨Ð‚"©»RÜß%™&üÙA,:Ƹܺc!¥ ?Ïóq:¼°²^yBû]£`³PbÚu„ŽµõÈ:!PùŒÀfkPx.ù‚ºy1lÌ¿3!£8G`?AÀ01û9há#„QÃZkl +Ež¸¨{NSùê÷”–Gz +BƒvÝnÓê÷^ª8ÊïÐú¦ðû&|³Ò¹”¬IC°óv¤õEúËÉ,¥¾Î‰NÔUŠÿõÐ÷UÛ +1jä»æ„žë‰Æ¤Áüy~1y_ìÜY‹’bÍo+(䀓v¼ÀƒÀ»VÏÈ8jÁ躜 +͆ÁŽê³PlsX¹vëC¤’’ð£$Äõªq…òWk™Ïiý^fÄ”8Ê×CŒG(; âÔúûÄ{ÍàÒÀïKH‚XÞd¯�¥ß²ÊÀ_à”›,ƒ7�å/>NvnX� 8Î<v†yy¸+ãFâ,$“ŠÙ¿ecõËŠ ûM$ ¾ú™·Ïš_Zê.�¹&¡öíËO×ÔPñ%LÌ|wkmù!A07#º‚îØž»ÙœaÑv[Xê8Ê5‘"®óïíÜ)ƒ~Ió$Bˆd ÀÄDÇ䬆öƒ¼#·ù¤- Þþ¢$ú!'Ž¬ýл׹QIm·¨ÊàVÿí´³¿ÙÝœð2hEÉØLƵªÍèw©ö"Z#ÓYXØ"d‰o`¯_éµ»!rûv_øõÍÞÈ'„?KpªË¸íõ¡©»›â¹–ünÝ%N„£æ-Xï¡pèˆ:*ƒ²ôsxÏn¨Ÿx.T®fÔÉg܉D§ÊöœÀ;^�E€8Xµs6’ç¾ +›éi'H~<«ê¤<²¨Éßÿ—"Ô-$Rk?×Uêø“9 Ä9µš9Ãirã‡]wë¹ÄìOŒ ³ÜÕ»¶„%sŠG`¯þUÆ4Þƒ®ó J�_¡Ç«ýSPâwͯ½F^ +ëä&̬ßy<m¼z%ªwOéŸsY„Ý(Qž +!cîGh¡GïC°x¶Õú댺zšË¯j÷2:õ �æ$UgG‘r?omYuñüsT†z¾ŒT;MߦÇkwOç0©Ã6èê[Ï3 ÄW†¼»î`Ñ™˜¤ÿGµñ?øÇ ›l?2C9m”Y@eg·<¬D_t…g34WZç¶U&ÊO“¼W æ´É„?ª¼¦©®ÔW¹iò±Aü_–ù¦TùäOyšéƒØ;VÒÌ‘L‰¯ç*äü“ô*Ì5{ƒ8¥v$HàÄ}[¿BïEJ!;h<sæ¸&9ù<ü)žã1a¹°&͘>ýgÚ4¹?82!è‰|;†»ç¿„§"V±E6﵊òœÑiiQïú%‡Üœ9 šÂ +hƒíTy˜âé·‘Ó>Ìé,¤Ý—ÔÕÙÒE±‚QONb“.Xâ~hV3'1Y0¤Ö1ɪ•�ãO¿Ø`´ïìj ‚;![_²¦Bpô¤¨l"$ä[}ÁvÕ‡‡þ’ &}§õ}ÀGëPptŠÇžò’ÞÅ» Zï^N¡g‹(ŒYÜù;‚ †Å’ƲìGø~#T?mHö5‹/nÀÀ'$HÈ6\VÖ,X‰«1¶;@Ä4ÅN3ñ¸¾�É9²EG ]'“ûo©ßBèÑàéú+ÐðÛcL08Aƒ +MÛYOÚIur`9à’ïွ‡ãX¿h2W³Rüý‡—.-ÛZ¹ã—o<·'ØèKû¶Êß›8pÈ¿^yéþFù¬Ÿ5ÏSY—á£Uûd#A*ÿ7âämˆUíSØØc&ÇÈð‹ì@L{ÀS]²:]µ²Æ×5Á¹@0©‚\UZÛæá ÒxOe…þòèöÉEÇ3Ý¿.ñ«Þtðä¥!ëm+ªfœ×1@wÍGXU3Qœ<Š”•Úý;ñáQk8(Gä_DÈ †DÊ|fCùŽb<°ÇÀ÷|Á‘çRÈw9ŠS8æÅÚÊȆ Ý2ËÈú +NïÌ‚a¥EuÍ¿»êÐ>òykHåH3þ£/'LøV•jaò—�_»ÅÂ:ú®jE†BzÄìéÞDâÊÞÉ……-šé@hU˜€+Úi°!eg{¦tJÉ9™8Š#áâ? Š=0)B ®†ƒm”Eä'h~¿}á~oÇLkú§Ø9¯·0´&þp‡`¹ õíg Ý"n;€¤ë'ógAŠ±b9B°ü3aš¸'yB€Š¶ÐÄL&3A¾Õ”AÎÃÂÏ�ç}W«’t«ªPWƒ‚äBÝÂ_bªÞ™™IhíL"\‹½e[AD~êš<¡šF”¥sL“{·&9pÙ<8Éõ‡M¦34Z/)mrv‰NÙ\L|ÿÓ+hA +”Âxléï¢bÑ5Ɔ¨ˆ9š¿•ƒ×[v6žÇ“SPGgBǺÆþ[|¾,â¬HŒÓO×£¨QºÈ‚!d=<dÐÌZ®Y¤‰Úƒž³lÛÊZkûn§a·½j:`Ž^Åx¿1ä{s`6—ÛóL!×#aM ¸†[°¦Â¡ibÁì|_DÈï`a£+°¹FëQuvè(᤼¿Q5¼h”^«*Ü,Ì&]½ÝИ³Ð×C£Ï…)ô�J‚ßÎkÑʼ9¯"eœÈ#Ù1;ËJÑ]w9;u0:uê Ñbj2ÏFû Îõσõ–ÄäSQ#ÛcíßÆض•ÿ¿ß“ñ"éÃñÚÈ'ËoÊ° +ûDQ¨ÑmHc”:·Ç•uÝZÏ’Ä‚¨ àu=z4];<5|<aFJX!Œâ)X‘ùMoD(çÅU{›)M# +Ά¹YV~¢‘dÎ,×Äç‰W·Å¡ ñ ºŠˆÛÓÀ¦U»ˆrVæ+ÝÔe1…zýâš~TÉÖ²ªÒð7$´Žü–T°æúÌ0ÈÍì· +Q›I?×ýo¾ž0á{vN„ó<ƒ�~·ÆÒo'5Do!Ž‘²(•¹©®i¢îÀý+™Uèp‹Ð눂šÉÉöU¾ˆöªI<‚ÅÜñïÑrN¬N^$°Ñ¿™>ej„Ò/åyúlEŽÎö{<J’ü¨ù!øãpn= z9Ìå¾ÈFH;XóbbºôíåeM˜(ôãðzºÛ˜*¨ÔßÞß×0œ„™û�ˆ€ GÜüí]pUå?¯ïœsßÉÍ’4@yƒ +²jdɬӮ–nãØ.ÚµÛu;íìlµììt‡meU´jíÔuºSug¡Êê +Bl$D!R!¼“\ò¸Inrßç}ö÷O¼r ¤`'Ø6gnrsî9ç~ç;ÿïÿøý?§…)B³âØýèw +g�;±+¢{¦Ó–µ3‡^Þz¼…´µÅÞî;A<C†—B7tý(tãïª wu ?òÒý`Û:,qT~PÀù—æ‡x} éËh|mZWÅâmO××å'<º†RV é‘Ô²¾µ¨(™0’O) +xŸµMjÂjËî.È …°@K-Nxù Å+®û\4õ0þF¤HV‡0šà3Ë +Ú£Vz[×`ö§»Ü›4ä–O™u«jóÎ}ˆtB2'þÞ𜑩4P¸ÇFä¶ù•ŒŽ?-E›O‚µ.¸ %tͯ>ýï]°Ìÿ…LÑÊÒÛŒzÁ½øjÛŠ++“½rÜ„` ïGQÈ6^€ÀÅ‹^�¡òÏx¯Tpù¹È®lA#ÎíæÀ@xne0vK8ÜE$ZàòCIòÒÎgù¬¦U)k2–÷Þ´®¯ŽCYÞÓ’"›Zá󇼒¬$ÅS#Þ:]ߟòF†KvãõŽaDs Ë~XýW.ùþeæßر¢g>zB«Þ^ÓýÚË—¢Ðì*¼ôoÉd¤1Õæ>ûåê²h:Ó»÷«y¥tœ0žÍ€<Æä +¤/`ÖéKÜš‹éy +©rÈÛõDzåC¹Ýw"C^ïèE'�Å7êÍØ;)òl}ùE²?×Å6¡P3*ê””…Íû.¼YÈf:"—ÏûáA½nèҦ啞n@ÒnútnÏ#áüh£„|åòu¡-šÎ‘aPˆªÕÌ‹«Ò…ƒ?Ù2R ƒÂÚüâ‹# hºAíííÊ›ŸTÄ,§¥EËSÈN6?²~ýˆ‡ý.d~lIY-ðN®+€´E9XÇ*tHR<Ö/ƶ–Öw%Ö)Ìͩ¢í±ça ¾°=ﮞMêjŽ0ÀYUÓ¹Ë}¬_‹>s9)–úÈq$ Ìèü³=±ÎýžQÊ¥4Pv£Î‰ÿ ¯¹”§RöÖWð;¨äbu\óAÞŠí]‰jn¬ò_iÜf__â*)Oæpdì—¨¥À=§KAúÇœÉØ�—À 2»Ë´Øc&o‘lñlů‡ý=`X‘¬‘øa®¸‘;ñ›¼z‡nX!h¼a +Þ=ˆdJcÉÔŸ+²uÜ߀މ¼kºß%í»WNÅ7àAEW¦ns†»b ¯GíÂT„^üËvÊŒµ7ÇÜŸZm§~ÁªfÞæo�;ýÒ)ŠÂ™¿jN'öNãCÞR_ÀÖ“+€Ù݆ûùk×ä¿=ž4Í7Ú£Äîö!ªn ço› .m¦ø ‹ƒëÓe½¿»ÄF¯dçÚå�sc}½¼R|ÑO+0ÝÔgAŠhBòË2%¢™]ƒ³ÑHñC„‚@ÁZ Ò (È°¿”4´ïÜ]’Í=Éb[âæ0ǹM’Åž±",ö<DVÛ]^k¸±èbÛ/9ZŒ y¸píí!ýET»Q0GÆ{v®åXO¨ñ¥/òÐH%Ñ +ѧ‚‡GñCà,“kA²þÍ瞈’W²ëüÐzXÜ›%¹óÜŽ®9Ö"zPÈ“Á¥Ž"±¡cVݶnºw[²’Rc))\U˜::ŠlTÈ•R1eJXIŽ5ô¹ÜF“èί}MiI(âpÜyŠ¡'b(Y&dœUL€í�úÌî®ØH#Üm8îtÃû3V²–LåøG§Wï‚G *\ȵt Þ 1Æo"ç¸4m›*Œïî 'na™È>òxsç§}Ñ‚¹ÙËÄ¿ÔÍÍ€Â<·ú;˜š +lV¸—p#òa)jáÖ@g³>T<½VTXu:k,Ãúõ+£vÊ©þñ, +F¿7›”²$tÄÈõÓ¸å +mCC¡„«ÎNôY…èb?:�±`º-:‹P:kEö.À7aúÛ’¾Ç¼Â¦¯ÂWýÅ^Û1Å cY+‹x0(îBƒÌÝoy7ë&‡7–næ™njÚ€^�1K= +²š§‚ê+ÔµHß‘T+\™»‡9Â)Kƒ¡öö¢€`ãáféU3·Ûß"d´çEŸç[HCÜÅ»uÿŠ6Ô€Wt<jH=?v¬ßmîFÇ<2˜|@=øNt/×Hû’ÖÚÑ©�?Âý`„Xu)ÀJ–ºÞr¾@ûOnG€æ.ýFc´¿+Z#*J5Ò>é{NZS½Ø[æ)Ýð‰hì̽TÌüGÃåò1¤ƒœ•´C ¸ïžêLßO>oφwð‹$,y¦Ì„Å:,ÍZ!ˆì¼"¸¾¡”µÍÃ2úίÜçÈé+”ÜŽ¦£³7C9›Þoèþ,"¾ô@ꇿknäŽq-^'ÜøÒ— FÞèYMâ÷¤!9Ï}ÁÆÞDú›4Žð¦�·JØ/¹}<“dàV=€kG”ÁI¤ SÊr qý6°²;YBw†àŸõ4]½ÒÉH¬ã׿‹[6wî«y…ATõŒäuÐü>Œêœ_p½®&J|b˜D©¤ Ï¿Ýûˆ6þ%*ùeèì!Ç»Þ`jÌ__W<GûÑùh_°<ý($q' n�0·'`¿“ÒøŒÈØ—à]?šµ-8ÜçKú;Œ²2I“ü€]Š&³§ßk;Ú±^<…`>¦˜ðø€/t[û©Æßü¶®®Î ç‘ßÜDÆ&œó¾G¾ï´†?g Y;gM×ArÉÊ•ž@g§®•Ï\æäv¤åŠh•¾Ë0%š®ÿSQ::8—<sx%’3OJ²¾ç©p¬ŽÂ²Î¦×dy÷%TÒê½*·EÑ…!'c¦ûýràlwÛ &üÈâBç=½¾a×z°Pdç ZQ 2¡a×sO¶áš]´IoÁö‰sþÅaâ oZ«ÁGõž›èÛEzmŸÁ"\wà —KžÆc}ü¸F©:OþF÷ÐyÈeàGnŽ$j,•_i%íݜʒU:piT]ö€¿Ãê°b‘Ɖ,òû§ú3Íá}ݱÛ/Wkf¦C ºÐˆ~`ˆou¤:Ïä8Ú—ÔÑlZç‘åDZˆÎB]àñó>ñ�HØscHûíOÌDwgµèd•½µb†6f^‘š8ÓX†JÃI�`Z†ÒF;ER„FYû3K§µîOOq,«©`t¨ KòÚßöÞ[ÿM) Š*K¢Qçrs#wþkýú1¾WúRû{S_�¡[2åêAÉUÒ ¢© +Ê\¼ B +JÍ=À¼[ ~E%n^A®Ñ§hr„žîŒYXå—fuP™hL<ÅÒ¯å½Òu\íßi’q#cA[îsô€§ÁÑN1iˆb0Èùϼ¶>xflhFé ?Ǿ¯ðÈ¥è¶ß‡kcbBTˆÃy>•Î<•äØžHt¡k³ùŒ hðˆÅö+ä)L‹ÙÔÓpàƒ)ˆe¡KÎëÚ2`Ævš/€Äžò†Æ��|IDAT®:çˆEé¬ýãÛŠÈ]sî•&g¹Xª¢·rZ¤¿·ºn3˜¾íÏæ¼ö&4i¤ rTƒjA„Ô›ä샢ÆØÇôÊ©w�šôòüµ(ðÁÉ·ïéøyþƒHçÁKEâ‹jÃÃoE²ƒƒ”«k$Â[û™÷²cn—ã€Á˜tæDJ'÷ÜX€r5Ïy–»¦Û^ÏVÛ§[~ÎGÇ–\¡pPïâÚ ÐSF9/¨Svȹ‹B’ôó^ËT<¼øwœeG$EšaÚú/ÈÌ¢ò%/ºå¶C÷òò?"¡Ü}žÈWâÑ4µ€%†Ri¿2“ ò¤˜N8†åå™ØŽúB‡šåâˆÆFˆšèùp¤ÀÂHÆþQ@‚Ù¾¿ñgeÇ >J˜ðMkêþÄZåh†Ý-°È +³]Ó®�Ó™¡”V½RcÖζ›ÇŽeɈ¶t ”›Šò%ò3…ɾ]É‚)ßEš ž\/"¦ùHÑ5ö'£ÛÇ>{9y¬kb|w Ôt¿0\(ôhf—Ç#+§œÒ™µ|ÛÑV»¿´„û³r&gÌ…>4BŒÔÍ9®FJ-Æ{NZš1Í7âí$Ï«ReÀvÖ-, þøáÌDÚxÇÊéü‚On_J},½ï?˜€Œ0ƒÉv>¼dŒ>”ôÄSò±//§¬»¯'¾“ªÚµ¹!S0γ³ÇÛ¥êê€ßWô0Œ4šù„~Ì8Ò;€7CÿYrO¸_)É®ŽÙ>€Ö:´‹2 ªPVãþÞúeAï¹k£W*´9‚4OM¤ÞŸ? +röÄ—dg#ÉM›å÷HVBwýLpö÷ïâ浊EE!Å7MƒÕŠ)ìU¦vàp/PtlÚöõÿŠ’s2Y÷Ù0ÀdÔ€CžxÕŠÅŠ\ªðÁ»Z¾W4ú©ÑÿDã5‡µÈVR˜rR@Âð*ք̘x¨)¡b�ô³‚k¡ö!UGèG÷[XpõzZ[×Cye‘ZÝŽÂKšf¼øwPœuÈ·¿ÙÕðöá\!'ÿü“?Ž�ÍoC .1ÒÏ+´XGñ[¦Qå@à@°yÜ^¶,ÛRž}o<¹¹Ð&ç+k¦2ßCRÖ|ùÐl_íèX-]ñZz~ç¼n61¿¼AÓ¸7 üòÂqCKuîIçß–rÔs¿¢+ðoÛ¨ç@øB½ˆ<wK’ÓrõØÚ<¿ëý¾]ã{½ÊòúhÒÕ#%Ó?ˆ”r2(ìšÌ¯þR¡lFÜ'6£±™ûˆÖŽä‰Ê¼Þ +3 ®m1BF*ÃX6r<‘ U£4…M.+³•¦&^_¾Ü…¾¼Ð¼sçU±3åòâ»òdv¨kQR?³S±„ë X>£ÏDñýhÅ(0“r̈́훋£¿å¶ÃÝñµÐhY€¦ˆˆ"ØÃGßh¯ÏåúsûŒ÷ÚB%““þ5bˆ†æjGxTÌK®ðáà gr}’£Ãèòšë(¼ÎËj;2*K¶©*S“fÒFÅŠ€”é„oC„ehØÍF{uëå"„ñ®iò}˜>l„ö)4ʤ’ZEd?›“ØMÐÌôù¹#7tbœFRt»ÏÅ‚mï¼–Ù¸qãǨ!Z|kV¬ÿ:ºH«€ÏÞ +$’èafvZÁS ¸´ðIûO¿cÝ"8Ó1Þ¡ðΓ–#*íÃ]íPo¹œÓs=ߧIã{=ß¼k£‰N^Š^.yýàç%$šçí:a?Ò9é`túùùƳ%Å•þ5†ï·‹jý#Øå«=ÙHâ¤,T¯nôżNŠšD®ö³Äˆ5Õ+ÏþiÀ6pÎÙ1UúòíHô¦L{&ªÓƒŠPŽtÚ¦vw›;“I—5ºvJÑÃI?ç-:sQÅ›*=ÊäÿŸf(ÍÀtF>ÍŠ45ù^ëxÇ9Ü›XŠ®ÄY3v\Mñ°Õy¼$-ÌhúáúŸ=wµ‰Kš“Æ;×õöþ¤ñ½ÞîÈu|=d¨ÂPøæe +`Wºljt¡}VZ…úÊżù•>—û;å¨Q\³iÁɽG¯¿Ï±ò??ùó§Z¼¸ ¯ +î«£rôŸîÓ—îM÷5TTÄÆÂ=/ÝsôŠ²Hµé…²ÿß<ݱßiÒøŽ‘Éß'G`r&dÆFrÐ?¢ƒü†¤1æ=¬Ã����IEND®B`‚ \ No newline at end of file diff --git a/core/vendor/guzzlehttp/guzzle/docs/_templates/nav_links.html b/core/vendor/guzzlehttp/guzzle/docs/_templates/nav_links.html new file mode 100644 index 0000000000000000000000000000000000000000..7950a0f8e9d4bfc851897810c0793232d37a1b7c --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/docs/_templates/nav_links.html @@ -0,0 +1,3 @@ +<li><a href="https://github.com/guzzle/guzzle">GitHub</a></li> +<li><a href="https://groups.google.com/forum/?hl=en#!forum/guzzle">Forum</a></li> +<li><a href="irc:irc.freenode.com/#guzzlephp">IRC</a></li> diff --git a/core/vendor/guzzlehttp/guzzle/docs/adapters.rst b/core/vendor/guzzlehttp/guzzle/docs/adapters.rst new file mode 100644 index 0000000000000000000000000000000000000000..27ad668fcb7865e915883e366605084b86c10bb6 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/docs/adapters.rst @@ -0,0 +1,222 @@ +======== +Adapters +======== + +Guzzle uses *adapters* to send HTTP requests. Adapters emit the lifecycle +events of requests, transfer HTTP requests, and normalize error handling. + +Default Adapter +=============== + +Guzzle will use the best possible adapter based on your environment. + +If cURL is present, Guzzle will use the following adapters by default: + +- ``GuzzleHttp\Adapter\Curl\MultiAdapter`` is used to transfer requests in + parallel. +- If ``allow_url_fopen`` is enabled, then a + ``GuzzleHttp\Adapter\StreamingProxyAdapter`` is added so that streaming + requests are sent using the PHP stream wrapper. If this setting is disabled, + then streaming requests are sent through a cURL adapter. +- If using PHP 5.5 or greater, then a a ``GuzzleHttp\Adapter\Curl\CurlAdapter`` + is used to send serial requests. Otherwise, the + ``GuzzleHttp\Adapter\Curl\MultiAdapter`` is used for serial and parallel + requests. + +If cURL is not installed, then Guzzle will use a +``GuzzleHttp\Adapter\StreamingAdapter`` to send requests through PHP's +HTTP stream wrapper. ``allow_url_fopen`` must be enabled if cURL is not +installed on your system. + +Creating an Adapter +=================== + +Creating a custom HTTP adapter allows you to completely customize the way an +HTTP request is sent over the wire. In some cases, you might need to use a +different mechanism for transferring HTTP requests other than cURL or PHP's +stream wrapper. For example, you might need to use a socket because the version +of cURL on your system has an old bug, maybe you'd like to implement future +response objects, or you want to create a thread pool and send parallel +requests using pthreads. + +The first thing you need to know about implementing custom adapters are the +responsibilities of an adapter. + +Adapter Responsibilities +------------------------ + +Adapters use a ``GuzzleHttp\Adapter\TransactionInterface`` which acts as a +mediator between ``GuzzleHttp\Message\RequestInterface`` and +``GuzzleHttp\Message\ResponseInterface`` objects. The main goal of an adapter +is to set a response on the provided transaction object. + +1. The adapter MUST return a ``GuzzleHttp\Message\ResponseInterface`` object in + a successful condition. + +2. When preparing requests, adapters MUST properly handle as many of the + following request configuration options as possible: + + - :ref:`cert-option` + - :ref:`connect_timeout-option` + - :ref:`debug-option` + - :ref:`expect-option` + - :ref:`proxy-option` + - :ref:`save_to-option` + - :ref:`ssl_key-option` + - :ref:`stream-option` + - :ref:`timeout-option` + - :ref:`verify-option` + +3. Adapters SHOULD not follow redirects. In the normal case, redirects are + followed by ``GuzzleHttp\Subscriber\Redirect``. Redirects SHOULD be + implemented using Guzzle event subscribers, not by an adapter. + +4. The adapter MUST emit a ``before`` event with a + ``GuzzleHttp\Event\BeforeEvent`` object before sending a request. If the + event is intercepted and a response is associated with a transaction during + the ``before`` event, then the adapter MUST not send the request over the + wire, but rather return the response. + +5. When all of the headers of a response have been received, the adapter MUST + emit a ``headers`` event with a ``GuzzleHttp\Event\HeadersEvent``. This + event MUST be emitted before any data is written to the body of the response + object. It is important to keep in mind that event listeners MAY mutate a + response during the emission of this event. + +6. The adapter MUST emit a ``complete`` event with a + ``GuzzleHttp\Event\CompleteEvent`` when a request has completed sending. + Adapters MUST emit the complete event for all valid HTTP responses, + including responses that resulted in a non 2xx level response. + +7. The adapter MUST emit an ``error`` event with a + ``GuzzleHttp\Event\ErrorEvent``when an error occurs during the transfer. + This includes when preparing a request for transfer, during the ``before`` + event, during the ``headers`` event, during the ``complete`` event, when + a networking error occurs, and so on. + +8. After emitting the ``error`` event, the adapter MUST check if the + error event was intercepted and a response was associated with the + transaction. If the propagation of the ``error`` event was not stopped, then + the adapter MUST throw the exception. If the propagation was stopped, then + the adapter MUST NOT throw the exception. + +Parallel Adapters +----------------- + +Parallel adapters are used when using a client's ``sendAll()`` method. Parallel +adapters are expected to send one or more transactions in parallel. Parallel +adapters accept an ``\Iterator`` that yields +``GuzzleHttp\Adapter\TransactionInterface`` object. In addition to the +iterator, the adapter is also provided an integer representing the number of +transactions to execute in parallel. + +Parallel adapters are similar to adapters (described earlier), except for the +following: + +1. RequestExceptions are never thrown from a parallel adapter. Error handling + for parallel transfers is handled through event listeners that use ``error`` + events. + +2. Parallel adapters are not expected to return responses. Because parallel + adapters can, in theory, send an infinite number of requests, developers + must use event listeners to receive the ``complete`` event and handle + responses accordingly. + +Emitting Lifecycle Events +------------------------- + +Request lifecycle events MUST be emitted by adapters and parallel adapters. +These lifecycle events are used by event listeners to modify requests, modify +responses, perform validation, and anything else required by an application. + +Emitting request lifecycle events in an adapter is much simpler if you use the +static helper method of ``GuzzleHttp\Event\RequestEvents``. These methods are +used by the built-in in curl and stream wrapper adapters of Guzzle, so you +should use them too. + +Example Adapter +=============== + +Here's a really simple example of creating a custom HTTP adapter. For +simplicity, this example uses a magic ``send_request()`` function. + +.. code-block:: php + + <?php + + namespace MyProject\Adapter; + + use GuzzleHttp\Event\RequestEvents; + use GuzzleHttp\Event\HeadersEvent; + use GuzzleHttp\Message\MessageFactoryInterface; + + class MyAdapter implements AdapterInterface + { + private $messageFactory; + + public function __construct(MessageFactoryInterface $messageFactory) + { + $this->messageFactory = $messageFactory; + } + + public function send(TransactionInterface $transaction) + { + RequestEvents::emitBefore($transaction); + + // Check if the transaction was intercepted + if (!$transaction->getResponse()) { + // It wasn't intercepted, so send the request + $this->getResponse($transaction); + } + + // Adapters always return a response in the successful case. + return $transaction->getResponse(); + } + + private function getResponse(TransactionInterface $transaction) + { + $request = $transaction->getRequest(); + + $response = send_request( + $request->getMethod(), + $request->getUrl(), + $request->getHeaders(), + $request->getBody() + ); + + if ($response) { + $this->processResponse($response, $transaction); + } else { + // Emit the error event which allows listeners to intercept + // the error with a valid response. If it is not intercepted, + // a RequestException is thrown. + RequestEvents::emitError($transaction, $e); + } + } + + private function processResponse( + array $response, + TransactionInterface $transaction + ) { + // Process the response, create a Guzzle Response object, and + // associate the response with the transaction. + $responseObject = $this->messageFactory->createResponse( + $response['status_code'], + $response['headers'] + ); + + $transaction->setResponse($responseObject); + + // Emit the headers event before downloading the body + RequestEvents::emitHeaders($transaction); + + if ($response['body']) { + // Assuming the response body is a stream or something, + // associate it with the response object. + $responseObject->setBody(Stream::factory($response['body'])); + } + + // Emit the complete event + RequestEvents::emitComplete($transaction); + } + } diff --git a/core/vendor/guzzlehttp/guzzle/docs/clients.rst b/core/vendor/guzzlehttp/guzzle/docs/clients.rst new file mode 100644 index 0000000000000000000000000000000000000000..d1943d1de27d1d9aa739e4a993f762e1bbbba21e --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/docs/clients.rst @@ -0,0 +1,1081 @@ +======= +Clients +======= + +Clients are used to create requests, create transactions, send requests +through an HTTP adapter, and return a response. You can add default request +options to a client that are applied to every request (e.g., default headers, +default query string parameters, etc), and you can add event listeners and +subscribers to every request created by a client. + +Creating a client +================= + +The constructor of a client accepts an associative array of configuration +options. + +base_url + Configures a base URL for the client so that requests created + using a relative URL are combined with the ``base_url`` of the client + according to section `5.2 of RFC 3986 <http://tools.ietf.org/html/rfc3986#section-5.2>`_. + + .. code-block:: php + + // Create a client with a base URL + $client = new GuzzleHttp\Client(['base_url' => 'https://github.com']); + // Send a request to https://github.com/notifications + $response = $client->get('/notifications'); + + `Absolute URLs <http://tools.ietf.org/html/rfc3986#section-4.3>`_ sent + through a client will not use the base URL of the client. + +adapter + Configures the HTTP adapter (``GuzzleHttp\Adapter\AdapterInterface``) used + to transfer the HTTP requests of a client. Guzzle will, by default, utilize + a stacked adapter that chooses the best adapter to use based on the provided + request options and based on the extensions available in the environment. If + cURL is installed, it will be used as the default adapter. However, if a + request has the ``stream`` request option, the PHP stream wrapper adapter + will be used (assuming ``allow_url_fopen`` is enabled in your PHP + environment). + +parallel_adapter + Just like the ``adapter`` option, you can choose to specify an adapter + that is used to send requests in parallel + (``GuzzleHttp\Adapter\ParallelAdapterInterface``). Guzzle will by default + use cURL to send requests in parallel, but if cURL is not available it will + use the PHP stream wrapper and simply send requests serially. + +message_factory + Specifies the factory used to create HTTP requests and responses + (``GuzzleHttp\Message\MessageFactoryInterface``). + +defaults + Associative array of :ref:`request-options` that are applied to every + request created by the client. This allows you to specify things like + default headers (e.g., User-Agent), default query string parameters, SSL + configurations, and any other supported request options. + +Here's an example of creating a client with various options, including using +a mock adapter that just returns the result of a callable function and a +base URL that is a URI template with parameters. + +.. code-block:: php + + use GuzzleHttp\Client; + + $client = new Client([ + 'base_url' => ['https://api.twitter.com/{version}', ['version' => 'v1.1']], + 'defaults' => [ + 'headers' => ['Foo' => 'Bar'], + 'query' => ['testing' => '123'], + 'auth' => ['username', 'password'], + 'proxy' => 'tcp://localhost:80' + ] + ]); + +Sending Requests +================ + +Requests can be created using various methods of a client. You can create +**and** send requests using one of the following methods: + +- ``GuzzleHttp\Client::get``: Sends a GET request. +- ``GuzzleHttp\Client::head``: Sends a HEAD request +- ``GuzzleHttp\Client::post``: Sends a POST request +- ``GuzzleHttp\Client::put``: Sends a PUT request +- ``GuzzleHttp\Client::delete``: Sends a DELETE request +- ``GuzzleHttp\Client::options``: Sends an OPTIONS request + +Each of the above methods accepts a URL as the first argument and an optional +associative array of :ref:`request-options` as the second argument. + +.. code-block:: php + + $client = new GuzzleHttp\Client(); + + $client->put('http://httpbin.org', [ + 'headers' => ['X-Foo' => 'Bar'], + 'body' => 'this is the body!', + 'save_to' => '/path/to/local/file', + 'allow_redirects' => false, + 'timeout' => 5 + ]); + +Error Handling +-------------- + +When a recoverable error is encountered while calling the ``send()`` method of +a client, a ``GuzzleHttp\Exception\RequestException`` is thrown. + +.. code-block:: php + + use GuzzleHttp\Client; + use GuzzleHttp\Exception\RequestException; + + $client = new Client(); + + try { + $client->get('http://httpbin.org'); + } catch (RequestException $e) { + echo $e->getRequest() . "\n"; + if ($e->hasResponse()) { + echo $e->getResponse() . "\n"; + } + } + +``GuzzleHttp\Exception\RequestException`` always contains a +``GuzzleHttp\Message\RequestInterface`` object that can be accessed using the +exception's ``getRequest()`` method. + +A response might be present in the exception. In the event of a networking +error, no response will be received. You can check if a ``RequestException`` +has a response using the ``hasResponse()`` method. If the exception has a +response, then you can access the associated +``GuzzleHttp\Message\ResponseInterface`` using the ``getResponse()`` method of +the exception. + +HTTP Errors +~~~~~~~~~~~ + +If the ``exceptions`` request option is not set to ``false``, then exceptions +are thrown for HTTP protocol errors as well: +``GuzzleHttp\Exception\ClientErrorResponseException`` for 4xx level HTTP +responses and ``GuzzleHttp\Exception\ServerException`` for 5xx level responses, +both of which extend from ``GuzzleHttp\Exception\BadResponseException``. + +Creating Requests +----------------- + +You can create a request without sending it. This is useful for building up +requests over time or sending requests in parallel. + +.. code-block:: php + + $request = $client->createRequest('GET', 'http://httpbin.org', [ + 'headers' => ['X-Foo' => 'Bar'] + ]); + + // Modify the request as needed + $request->setHeader('Baz', 'bar'); + +After creating a request, you can send it with the client's ``send()`` method. + +.. code-block:: php + + $response = $client->send($request); + +Sending Requests in Parallel +============================ + +You can send requests in parallel using a client object's ``sendAll()`` method. +The ``sendAll()`` method accepts an array or ``\Iterator`` that contains +``GuzzleHttp\Message\RequestInterface`` objects. In addition to providing the +requests to send, you can also specify an associative array of options that +will affect the transfer. + +.. code-block:: php + + $requests = [ + $client->createRequest('GET', 'http://httpbin.org'), + $client->createRequest('DELETE', 'http://httpbin.org/delete'), + $client->createRequest('PUT', 'http://httpbin.org/put', ['body' => 'test']) + ]; + + $client->sendAll($requests); + +The ``sendAll()`` method accepts the following associative array of options: + +- **parallel**: Integer representing the maximum number of requests that are + allowed to be sent in parallel. +- **before**: Callable or array representing the event listeners to add to + each request's :ref:`before_event` event. +- **complete**: Callable or array representing the event listeners to add to + each request's :ref:`complete_event` event. +- **error**: Callable or array representing the event listeners to add to + each request's :ref:`error_event` event. + +The "before", "complete", and "error" event options accept a callable or an +array of associative arrays where each associative array contains a "fn" key +with a callable value, an optional "priority" key representing the event +priority (with a default value is 0), and an optional "once" key that can be +set to true so that the event listener will be removed from the request after +it is first triggered. + +.. code-block:: php + + use GuzzleHttp\Event\CompleteEvent; + + // Add a single event listener using a callable. + $client->sendAll($requests, [ + 'complete' => function (CompleteEvent $event) { + echo 'Completed request to ' . $event->getRequest()->getUrl() . "\n"; + echo 'Response: ' . $event->getResponse()->getBody() . "\n\n"; + } + ]); + + // The above is equivalent to the following, but the following structure + // allows you to add multiple event listeners to the same event name. + $client->sendAll($requests, [ + 'complete' => [ + [ + 'fn' => function (CompleteEvent $event) { /* ... */ }, + 'priority' => 0, // Optional + 'once' => false // Optional + ] + ] + ]); + +Asynchronous Response Handling +------------------------------ + +When sending requests in parallel, the request/response/error lifecycle must be +handled asynchronously. This means that you give the ``sendAll()`` method +multiple requests and handle the response or errors that is associated with the +request using event callbacks. + +.. code-block:: php + + use GuzzleHttp\Event\ErrorEvent; + + $client->sendAll($requests, [ + 'complete' => function (CompleteEvent $event) { + echo 'Completed request to ' . $event->getRequest()->getUrl() . "\n"; + echo 'Response: ' . $event->getResponse()->getBody() . "\n\n"; + // Do something with the completion of the request... + }, + 'error' => function (ErrorEvent $event) { + echo 'Request failed: ' . $event->getRequest()->getUrl() . "\n" + echo $event->getException(); + // Do something to handle the error... + } + ]); + +The ``GuzzleHttp\Event\ErrorEvent`` event object is emitted when an error +occurs during a transfer. With this event, you have access to the request that +was sent, the response that was received (if one was received), access to +transfer statistics, and the ability to intercept the exception with a +different ``GuzzleHttp\Message\ResponseInterface`` object. See :doc:`events` +for more information. + +Handling Errors After Transferring +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It sometimes might be easier to handle all of the errors that occurred during a +transfer after all of the requests have been sent. Here we are adding each +failed request to an array that we can use to process errors later. + +.. code-block:: php + + use GuzzleHttp\Event\ErrorEvent; + + $errors = []; + $client->sendAll($requests, [ + 'error' => function (ErrorEvent $event) use (&$errors) { + $errors[] = $event; + } + ]); + + foreach ($errors as $error) { + // Handle the error... + } + +Throwing Errors Immediately +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It sometimes is useful to throw exceptions immediately when the occur. The +following exmaple shows how to use an event listener to throw exceptions +immeditaley and prevent subsequent requests from being sent. + +.. code-block:: php + + use GuzzleHttp\Event\ErrorEvent; + + $client->sendAll($requests, [ + 'error' => function (ErrorEvent $event) use (&$errors) { + throw $event->getException(); + } + ]); + +.. _request-options: + +Batching Requests +----------------- + +Sometimes you just want to send a few requests in parallel and then process +the results all at once after they've sent. Guzzle provides a convenience +function ``GuzzleHttp\batch()`` that makes this very simple: + +.. code-block:: php + + $client = new GuzzleHttp\Client(); + + $requests = [ + $client->createRequest('GET', 'http://httpbin.org/get'), + $client->createRequest('HEAD', 'http://httpbin.org/get'), + $client->createRequest('PUT', 'http://httpbin.org/put'), + ]; + + $results = GuzzleHttp\batch($client, $requests); + + // Results is an SplObjectStorage object where each request is a key + foreach ($results as $request) { + echo $request->getUrl() . "\n"; + // Get the result (either a ResponseInterface or RequestException) + $result = $results[$request]; + if ($result instanceof ResponseInterface) { + // Interact with the response directly + echo $result->getStatusCode(); + } else { + // Get the exception message + echo $result->getMessage(); + } + } + +``GuzzleHttp\batch()`` accepts an optional associative array of options in the +third argument that allows you to specify the 'before', 'complete' and 'error' +events as well as specify the maximum number of request to send in parallel +using the 'parallel' option key. This options array is the exact same format as +the options array exposed in ``GuzzleHttp\ClientInterface::sendAll()``. + +Request Options +=============== + +You can customize requests created by a client using **request options**. +Request options control various aspects of a request including, headers, +query string parameters, timeout settings, the body of a request, and much +more. + +All of the following examples use the following client: + +.. code-block:: php + + $client = new GuzzleHttp\Client(['base_url' => 'http://httpbin.org']); + +headers +------- + +:Summary: Associative array of headers to add to the request. Each key is the + name of a header, and each value is a string or array of strings + representing the header field values. +:Types: array +:Defaults: None + +.. code-block:: php + + // Set various headers on a request + $client->get('/get', [ + 'headers' => [ + 'User-Agent' => 'testing/1.0', + 'Accept' => 'application/json', + 'X-Foo' => ['Bar', 'Baz'] + ] + ]); + +body +---- + +:Summary: The ``body`` option is used to control the body of an entity + enclosing request (e.g., PUT, POST, PATCH). +:Types: + - string + - ``fopen()`` resource + - ``GuzzleHttp\Stream\StreamInterface`` + - ``GuzzleHttp\Post\PostBodyInterface`` +:Default: None + +This setting can be set to any of the following types: + +- string + + .. code-block:: php + + // You can send requests that use a string as the message body. + $client->put('/put', ['body' => 'foo']); + +- resource returned from ``fopen()`` + + .. code-block:: php + + // You can send requests that use a stream resource as the body. + $resource = fopen('http://httpbin.org', 'r'); + $client->put('/put', ['body' => $resource]); + +- Array + + Use an array to send POST style requests that use a + ``GuzzleHttp\Post\PostBodyInterface`` object as the body. + + .. code-block:: php + + // You can send requests that use a POST body containing fields & files. + $client->post('/post', [ + 'body' => [ + 'field' => 'abc', + 'other_field' => '123', + 'file_name' => fopen('/path/to/file', 'r') + ] + ]); + +- ``GuzzleHttp\Stream\StreamInterface`` + + .. code-block:: php + + // You can send requests that use a Guzzle stream object as the body + $stream = GuzzleHttp\Stream\Stream::factory('contents...'); + $client->post('/post', ['body' => $stream]); + +query +----- + +:Summary: Associative array of query string values to add to the request. +:Types: + - array + - ``GuzzleHttp\Query`` +:Default: None + +.. code-block:: php + + // Send a GET request to /get?foo=bar + $client->get('/get', ['query' => ['foo' => 'bar']); + +Query strings specified in the ``query`` option are combined with any query +string values that are parsed from the URL. + +.. code-block:: php + + // Send a GET request to /get?abc=123&foo=bar + $client->get('/get?abc=123', ['query' => ['foo' => 'bar']); + +auth +---- + +:Summary: Pass an array of HTTP authentication parameters to use with the + request. The array must contain the username in index [0], the password in + index [1], and you can optionally provide a built-in authentication type in + index [2]. Pass ``null`` to disable authentication for a request. +:Types: + - array + - string + - null +:Default: None + +The built-in authentication types are as follows: + +basic + Use `basic HTTP authentication <http://www.ietf.org/rfc/rfc2069.txt>`_ in + the ``Authorization`` header (the default setting used if none is + specified). + + .. code-block:: php + + $client->get('/get', ['auth' => ['username', 'password']]); + +digest + Use `digest authentication <http://www.ietf.org/rfc/rfc2069.txt>`_ (must be + supported by the HTTP adapter). + + .. code-block:: php + + $client->get('/get', ['auth' => ['username', 'password', 'digest']]); + + *This is currently only supported when using the cURL adapter, but creating + a replacement that can be used with any HTTP adapter is planned.* + +.. important:: + + The authentication type (whether it's provided as a string or as the third + option in an array) is always converted to a lowercase string. Take this + into account when implementing custom authentication types and when + implementing custom message factories. + +Custom Authentication Schemes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can also provide a string representing a custom authentication type name. +When using a custom authentication type string, you will need to implement +the authentication method in an event listener that checks the ``auth`` request +option of a request before it is sent. Authentication listeners that require +a request is not modified after they are signed should have a very low priority +to ensure that they are fired last or near last in the event chain. + +.. code-block:: php + + use GuzzleHttp\Event\BeforeEvent; + use GuzzleHttp\Event\RequestEvents; + + /** + * Custom authentication listener that handles the "foo" auth type. + * + * Listens to the "before" event of a request and only modifies the request + * when the "auth" config setting of the request is "foo". + */ + class FooAuth implements GuzzleHttp\Common\SubscriberInterface + { + private $password; + + public function __construct($password) + { + $this->password = $password; + } + + public function getEvents() + { + return ['before' => ['sign', RequestEvents::SIGN_REQUEST]]; + } + + public function sign(BeforeEvent $e) + { + if ($e->getRequest()->getConfig()['auth'] == 'foo') { + $e->getRequest()->setHeader('X-Foo', 'Foo ' . $this->password); + } + } + } + + $client->getEmitter->attach(new FooAuth('password')); + $client->get('/', ['auth' => 'foo']); + +Adapter Specific Authentication Schemes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you need to use authentication methods provided by cURL (e.g., NTLM, GSS, +etc...), then you need to specify a curl adapter option in the ``options`` +request option array. See :ref:`config-option` for more information. + +.. _cookies-option: + +cookies +------- + +:Summary: Specifies whether or not cookies are used in a request or what cookie + jar to use or what cookies to send. +:Types: + - bool + - array + - ``GuzzleHttp\Cookie\CookieJarInterface`` +:Default: None + +Set to ``true`` to use a shared cookie session associated with the client. + +.. code-block:: php + + // Enable cookies using the shared cookie jar of the client. + $client->get('/get', ['cookies' => true]); + +Pass an associative array containing cookies to send in the request and start a +new cookie session. + +.. code-block:: php + + // Enable cookies and send specific cookies + $client->get('/get', ['cookies' => ['foo' => 'bar']]); + +Set to a ``GuzzleHttp\Cookie\CookieJarInterface`` object to use an existing +cookie jar. + +.. code-block:: php + + $jar = new GuzzleHttp\Cookie\CookieJar(); + $client->get('/get', ['cookies' => $jar]); + +.. _allow_redirects-option: + +allow_redirects +--------------- + +:Summary: Describes the redirect behavior of a request +:Types: + - bool + - array +:Default: ``['max' => 5, 'strict' => false, 'referer' => true]`` + +Set to ``false`` to disable redirects. + +.. code-block:: php + + $res = $client->get('/redirect/3', ['allow_redirects' => false]); + echo $res->getStatusCode(); + // 302 + +Set to ``true`` (the default setting) to enable normal redirects with a maximum +number of 5 redirects. + +.. code-block:: php + + $res = $client->get('/redirect/3'); + echo $res->getStatusCode(); + // 200 + +Pass an associative array containing the 'max' key to specify the maximum +number of redirects, optionally provide a 'strict' key value to specify +whether or not to use strict RFC compliant redirects (meaning redirect POST +requests with POST requests vs. doing what most browsers do which is redirect +POST requests with GET requests), and optionally provide a 'referer' key to +specify whether or not the "Referer" header should be added when redirecting. + +.. code-block:: php + + $res = $client->get('/redirect/3', [ + 'allow_redirects' => [ + 'max' => 10, + 'strict' => true, + 'referer' => true + ] + ]); + echo $res->getStatusCode(); + // 200 + +.. _save_to-option: + +save_to +------- + +:Summary: Specify where the body of a response will be saved. +:Types: + - string + - ``fopen()`` resource + - ``GuzzleHttp\Stream\StreamInterface`` +:Default: PHP temp stream + +Pass a string to specify the path to a file that will store the contents of the +response body: + +.. code-block:: php + + $client->get('/stream/20', ['save_to' => '/path/to/file']); + +Pass a resource returned from ``fopen()`` to write the response to a PHP stream: + +.. code-block:: php + + $resource = fopen('/path/to/file', 'w'); + $client->get('/stream/20', ['save_to' => $resource]); + +Pass a ``GuzzleHttp\Stream\StreamInterface`` object to stream the response body +to an open Guzzle stream: + +.. code-block:: php + + $resource = fopen('/path/to/file', 'w'); + $stream = GuzzleHttp\Stream\Stream::factory($resource); + $client->get('/stream/20', ['save_to' => $stream]); + +.. _events-option: + +events +------ + +:Summary: Associative array mapping event names to a callable. or an + associative array containing the 'fn' key that maps to a callable, an + optional 'priority' key used to specify the event priority, and an optional + 'once' key used to specify if the event should remove itself the first time + it is triggered. +:Types: array +:Default: None + +.. code-block:: php + + use GuzzleHttp\Event\BeforeEvent; + use GuzzleHttp\Event\HeadersEvent; + use GuzzleHttp\Event\CompleteEvent; + use GuzzleHttp\Event\ErrorEvent; + + $client->get('/', [ + 'events' => [ + 'before' => function (BeforeEvent $e) { echo 'Before'; }, + 'headers' => function (HeadersEvent $e) { echo 'Headers'; }, + 'complete' => function (CompleteEvent $e) { echo 'Complete'; }, + 'error' => function (ErrorEvent $e) { echo 'Error'; }, + ] + ]); + +Here's an example of using the associative array format for control over the +priority and whether or not an event should be triggered more than once. + +.. code-block:: php + + $client->get('/', [ + 'events' => [ + 'before' => [ + 'fn' => function (BeforeEvent $e) { echo 'Before'; }, + 'priority' => 100, + 'once' => true + ] + ] + ]); + +.. _subscribers-option: + +subscribers +----------- + +:Summary: Array of event subscribers to add to the request. Each value in the + array must be an instance of ``GuzzleHttp\Event\SubscriberInterface``. +:Types: array +:Default: None + +.. code-block:: php + + use GuzzleHttp\Subscriber\History; + use GuzzleHttp\Subscriber\Mock; + use GuzzleHttp\Message\Response; + + $history = new History(); + $mock = new Mock([new Response(200)]); + $client->get('/', ['subscribers' => [$history, $mock]]); + + echo $history; + // Outputs the request and response history + +.. _exceptions-option: + +exceptions +---------- + +:Summary: Set to ``false`` to disable throwing exceptions on an HTTP protocol + errors (i.e., 4xx and 5xx responses). Exceptions are thrown by default when + HTTP protocol errors are encountered. +:Types: bool +:Default: ``true`` + +.. code-block:: php + + $client->get('/status/500'); + // Throws a GuzzleHttp\Exception\ServerException + + $res = $client->get('/status/500', ['exceptions' => false]); + echo $res->getStatusCode(); + // 500 + +.. _timeout-option: + +timeout +------- + +:Summary: Float describing the timeout of the request in seconds. Use ``0`` + to wait indefinitely (the default behavior). +:Types: float +:Default: ``0`` + +.. code-block:: php + + // Timeout if a server does not return a response in 3.14 seconds. + $client->get('/delay/5', ['timeout' => 3.14]); + // PHP Fatal error: Uncaught exception 'GuzzleHttp\Exception\RequestException' + +.. _connect_timeout-option: + +connect_timeout +--------------- + +:Summary: Float describing the number of seconds to wait while trying to connect + to a server. Use ``0`` to wait indefinitely (the default behavior). +:Types: float +:Default: ``0`` + +.. code-block:: php + + // Timeout if the client fails to connect to the server in 3.14 seconds. + $client->get('/delay/5', ['connect_timeout' => 3.14]); + +.. note:: + + This setting must be supported by the HTTP adapter used to send a request. + ``connect_timeout`` is currently only supported by the built-in cURL + adapter. + +.. _verify-option: + +verify +------ + +:Summary: Describes the SSL certificate verification behavior of a request. + Set to ``true`` to enable SSL certificate verification (the default). Set + to ``false`` to disable certificate verification (this is insecure!). Set + to a string to provide the path to a CA bundle to enable verification using + a custom certificate. +:Types: + - bool + - string +:Default: ``true`` + +.. code-block:: php + + // Use a custom SSL certificate + $client->get('/', ['verify' => '/path/to/cert.pem']); + + // Disable validation + $client->get('/', ['verify' => false]); + +.. _cert-option: + +cert +---- + +:Summary: Set to a string to specify the path to a file containing a PEM + formatted client side certificate. If a password is required, then set to + an array containing the path to the PEM file in the first array element + followed by the password required for the certificate in the second array + element. +:Types: + - string + - array +:Default: None + +.. code-block:: php + + $client->get('/', ['cert' => ['/path/server.pem', 'password']]); + +.. _ssl_key-option: + +ssl_key +------- + +:Summary: Specify the path to a file containing a private SSL key in PEM + format. If a password is required, then set to an array containing the path + to the SSL key in the first array element followed by the password required + for the certificate in the second element. +:Types: + - string + - array +:Default: None + +.. note:: + + ``ssl_key`` is implemented by HTTP adapters. This is currently only + supported by the cURL adapter, but might be supported by other third-part + adapters. + +.. _proxy-option: + +proxy +----- + +:Summary: Pass a string to specify an HTTP proxy, or an array to specify + different proxies for different protocols. +:Types: + - string + - array +:Default: None + +Pass a string to specify a proxy for all protocols. + +.. code-block:: php + + $client->get('/', ['proxy' => 'tcp://localhost:8124']); + +Pass an associative array to specify HTTP proxies for specific URI schemes +(i.e., "http", "https"). + +.. code-block:: php + + $client->get('/', [ + 'proxy' => [ + 'http' => 'tcp://localhost:8124', // Use this proxy with "http" + 'https' => 'tcp://localhost:9124' // Use this proxy with "https" + ] + ]); + +.. note:: + + You can provide proxy URLs that contain a scheme, username, and password. + For example, ``"http://username:password@192.168.16.1:10"``. + +.. _debug-option: + +debug +----- + +:Summary: Set to ``true`` or set to a PHP stream returned by ``fopen()`` to + enable debug output with the adapter used to send a request. For example, + when using cURL to transfer requests, cURL's verbose of ``CURLOPT_VERBOSE`` + will be emitted. When using the PHP stream wrapper, stream wrapper + notifications will be emitted. If set to true, the output is written to + PHP's STDOUT. If a PHP stream is provided, output is written to the stream. +:Types: + - bool + - ``fopen()`` resource +:Default: None + +.. code-block:: php + + $client->get('/get', ['debug' => true]); + +Running the above example would output something like the following: + +:: + + * About to connect() to httpbin.org port 80 (#0) + * Trying 107.21.213.98... * Connected to httpbin.org (107.21.213.98) port 80 (#0) + > GET /get HTTP/1.1 + Host: httpbin.org + User-Agent: Guzzle/4.0 curl/7.21.4 PHP/5.5.7 + + < HTTP/1.1 200 OK + < Access-Control-Allow-Origin: * + < Content-Type: application/json + < Date: Sun, 16 Feb 2014 06:50:09 GMT + < Server: gunicorn/0.17.4 + < Content-Length: 335 + < Connection: keep-alive + < + * Connection #0 to host httpbin.org left intact + +.. _stream-option: + +stream +------ + +:Summary: Set to ``true`` to stream a response rather than download it all + up-front. +:Types: bool +:Default: ``false`` + +.. code-block:: php + + $response = $client->get('/stream/20', ['stream' => true]); + // Read bytes off of the stream until the end of the stream is reached + $body = $response->getBody(); + while (!$body->eof()) { + echo $body->read(1024); + } + +.. note:: + + Streaming response support must be implemented by the HTTP adapter used by + a client. This option might not be supported by every HTTP adapter, but the + interface of the response object remains the same regardless of whether or + not it is supported by the adapter. + +.. _expect-option: + +expect +------ + +:Summary: Controls the behavior of the "Expect: 100-Continue" header. +:Types: + - bool + - integer +:Default: ``1048576`` + +Set to ``true`` to enable the "Expect: 100-Continue" header for all requests +that sends a body. Set to ``false`` to disable the "Expect: 100-Continue" +header for all requests. Set to a number so that the size of the payload must +be greater than the number in order to send the Expect header. Setting to a +number will send the Expect header for all requests in which the size of the +payload cannot be determined or where the body is not rewindable. + +By default, Guzzle will add the "Expect: 100-Continue" header when the size of +the body of a request is greater than 1 MB and a request is using HTTP/1.1. + +.. note:: + + This option only takes effect when using HTTP/1.1. The HTTP/1.0 and + HTTP/2.0 protocols do not support the "Expect: 100-Continue" header. + Support for handling the "Expect: 100-Continue" workflow must be + implemented by Guzzle HTTP adapters used by a client. + +.. _version-option: + +version +------- + +:Summary: Protocol version to use with the request. +:Types: string, float +:Default: ``1.1`` + +.. code-block:: php + + // Force HTTP/1.0 + $request = $client->createRequest('GET', '/get', ['version' => 1.0]); + echo $request->getProtocolVersion(); + // 1.0 + +.. _config-option: + +config +------ + +:Summary: Associative array of config options that are forwarded to a request's + configuration collection. These values are used as configuration options + that can be consumed by plugins and adapters. +:Types: array +:Default: None + +.. code-block:: php + + $request = $client->createRequest('GET', '/get', ['config' => ['foo' => 'bar']]); + echo $request->getConfig('foo'); + // 'bar' + +Some HTTP adapters allow you to specify custom adapter-specific settings. For +example, you can pass custom cURL options to requests by passing an associative +array in the ``config`` request option under the ``curl`` key. + +.. code-block:: php + + // Use custom cURL options with the request. This example uses NTLM auth + // to authenticate with a server. + $client->get('/', [ + 'config' => [ + 'curl' => [ + CURLOPT_HTTPAUTH => CURLAUTH_NTLM, + CURLOPT_USERPWD => 'username:password' + ] + ] + ]); + +Event Subscribers +================= + +Requests emit lifecycle events when they are transferred. A client object has a +``GuzzleHttp\Common\EventEmitter`` object that can be used to add event +*listeners* and event *subscribers* to all requests created by the client. + +.. important:: + + **Every** event listener or subscriber added to a client will be added to + every request created by the client. + +.. code-block:: php + + use GuzzleHttp\Client; + use GuzzleHttp\Event\BeforeEvent; + + $client = new Client(); + + // Add a listener that will echo out requests before they are sent + $client->getEmitter()->on('before', function (BeforeEvent $e) { + echo 'About to send request: ' . $e->getRequest(); + }); + + $client->get('http://httpbin.org/get'); + // Outputs the request as a string because of the event + +See :doc:`events` for more information on the event system used in Guzzle. + +Environment Variables +===================== + +Guzzle exposes a few environment variables that can be used to customize the +behavior of the library. + +``GUZZLE_CURL_SELECT_TIMEOUT`` + Controls the duration in seconds that ``GuzzleHttp\Adapter\Curl\MultiAdapter`` + will use when selecting handles using ``curl_multi_select()``. Some systems + have issues with PHP's implementation of ``curl_multi_select()`` where + calling this function always results in waiting for the maximum duration of + the timeout. +``HTTP_PROXY`` + Defines the proxy to use when sending requests using the "http" protocol. +``HTTPS_PROXY`` + Defines the proxy to use when sending requests using the "https": protocol. + +Relevant ini Settings +--------------------- + +Guzzle can utilize PHP ini settings when configuring clients. + +``openssl.cafile`` + Specifies the path on disk to a CA file in PEM format to use when sending + requests over "https". See: https://wiki.php.net/rfc/tls-peer-verification#phpini_defaults diff --git a/core/vendor/guzzlehttp/guzzle/docs/conf.py b/core/vendor/guzzlehttp/guzzle/docs/conf.py new file mode 100644 index 0000000000000000000000000000000000000000..c787faca402af1a7feaa887d98add1d7c780d9d2 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/docs/conf.py @@ -0,0 +1,93 @@ +import sys, os +from sphinx.highlighting import lexers +from pygments.lexers.web import PhpLexer + +lexers['php'] = PhpLexer(startinline=True, linenos=1) +lexers['php-annotations'] = PhpLexer(startinline=True, linenos=1) +primary_domain = 'php' + +# -- General configuration ----------------------------------------------------- + +extensions = [] +templates_path = ['_templates'] +source_suffix = '.rst' +master_doc = 'index' + +project = u'Guzzle' +copyright = u'2012, Michael Dowling' +version = '3.0.0' +release = '3.0.0' + +exclude_patterns = ['_build'] + +# -- Options for HTML output --------------------------------------------------- + +# The name for this set of Sphinx documents. If None, it defaults to +# "<project> v<release> documentation". +html_title = "Guzzle documentation" +html_short_title = "Guzzle" + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# Custom sidebar templates, maps document names to template names. +html_sidebars = { + '**': ['localtoc.html', 'searchbox.html'] +} + +# Output file base name for HTML help builder. +htmlhelp_basename = 'Guzzledoc' + +# -- Guzzle Sphinx theme setup ------------------------------------------------ + +sys.path.insert(0, '/Users/dowling/projects/guzzle_sphinx_theme') + +import guzzle_sphinx_theme +html_translator_class = 'guzzle_sphinx_theme.HTMLTranslator' +html_theme_path = guzzle_sphinx_theme.html_theme_path() +html_theme = 'guzzle_sphinx_theme' + +# Guzzle theme options (see theme.conf for more information) +html_theme_options = { + "project_nav_name": "Guzzle", + "github_user": "guzzle", + "github_repo": "guzzle", + "disqus_comments_shortname": "guzzle", + "google_analytics_account": "UA-22752917-1" +} + +# -- Options for LaTeX output -------------------------------------------------- + +latex_elements = {} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('index', 'Guzzle.tex', u'Guzzle Documentation', + u'Michael Dowling', 'manual'), +] + +# -- Options for manual page output -------------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'guzzle', u'Guzzle Documentation', + [u'Michael Dowling'], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + +# -- Options for Texinfo output ------------------------------------------------ + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ('index', 'Guzzle', u'Guzzle Documentation', + u'Michael Dowling', 'Guzzle', 'One line description of project.', + 'Miscellaneous'), +] diff --git a/core/vendor/guzzlehttp/guzzle/docs/events.rst b/core/vendor/guzzlehttp/guzzle/docs/events.rst new file mode 100644 index 0000000000000000000000000000000000000000..77800087e21920e95fd8268a248a51633d92860c --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/docs/events.rst @@ -0,0 +1,479 @@ +============ +Event System +============ + +Guzzle uses an event emitter to allow you to easily extend the behavior of a +request, change the response associated with a request, and implement custom +error handling. All events in Guzzle are managed and emitted by an +**event emitter**. + +Event Emitters +============== + +Clients, requests, and any other class that implements the +``GuzzleHttp\Common\HasEmitterInterface`` interface have a +``GuzzleHttp\Common\EventEmitter`` object. You can add event *listeners* and +event *subscribers* to an event *emitter*. + +emitter + An object that implements ``GuzzleHttp\Common\EventEmitterInterface``. This + object emits named events to event listeners. You may register event + listeners on subscribers on an emitter. + +event listeners + Callable functions that are registered on an event emitter for specific + events. Event listeners are registered on an emitter with a *priority* + setting. If no priority is provided, ``0`` is used by default. + +event subscribers + Classes that tell an event emitter what methods to listen to and what + functions on the class to invoke when the event is triggered. Event + subscribers subscribe event listeners to an event emitter. They should be + used when creating more complex event based logic in applications (i.e., + cookie handling is implemented using an event subscriber because it's + easier to share a subscriber than an anonymous function and because + handling cookies is a complex process). + +priority + Describes the order in which event listeners are invoked when an event is + emitted. The higher a priority value, the earlier the event listener will + be invoked (a higher priority means the listener is more important). If + no priority is provided, the priority is assumed to be ``0``. + + When specifying an event priority, you can pass ``"first"`` or ``"last"`` to + dynamically specify the priority based on the current event priorities + associated with the given event name in the emitter. Use ``"first"`` to set + the priority to the current highest priority plus one. Use ``"last"`` to + set the priority to the current lowest event priority minus one. It is + important to remember that these dynamic priorities are calculated only at + the point of insertion into the emitter and they are not rearranged after + subsequent listeners are added to an emitter. + +propagation + Describes whether or not other event listeners are triggered. Event + emitters will trigger every event listener registered to a specific event + in priority order until all of the listeners have been triggered **or** + until the propagation of an event is stopped. + +Getting an EventEmitter +----------------------- + +You can get the event emitter of ``GuzzleHttp\Common\HasEmitterInterface`` +object using the the ``getEmitter()`` method. Here's an example of getting a +client object's event emitter. + +.. code-block:: php + + $client = new GuzzleHttp\Client(); + $emitter = $client->getEmitter(); + +.. note:: + + You'll notice that the event emitter used in Guzzle is very similar to the + `Symfony2 EventDispatcher component <https://github.com/symfony/symfony/tree/master/src/Symfony/Component/EventDispatcher>`_. + This is because the Guzzle event system is based on the Symfony2 event + system with several changes. Guzzle uses its own event emitter to improve + performance, isolate Guzzle from changes to the Symfony, and provide a few + improvements that make it easier to use for an HTTP client (e.g., the + addition of the ``once()`` method). + +Adding Event Listeners +---------------------- + +After you have the emitter, you can register event listeners that listen to +specific events using the ``on()`` method. When registering an event listener, +you must tell the emitter what event to listen to (e.g., "before", "after", +"headers", "complete", "error", etc...), what callable to invoke when the +event is triggered, and optionally provide a priority. + +.. code-block:: php + + use GuzzleHttp\Event\BeforeEvent; + + $emitter->on('before', function (BeforeEvent $event) { + echo $event->getRequest(); + }); + +When a listener is triggered, it is passed an event that implements the +``GuzzleHttp\Common\EventInterface`` interface, the name of the event, and the +event emitter itself. The above example could more verbosely be written as +follows: + +.. code-block:: php + + use GuzzleHttp\Event\BeforeEvent; + + $emitter->on('before', function ( + BeforeEvent $event, + $name, + EmitterInterface $emitter + ) { + echo $event->getRequest(); + }); + +You can add an event listener that automatically removes itself after it is +triggered using the ``once()`` method of an event emitter. + +.. code-block:: php + + $client = new GuzzleHttp\Client(); + $client->getEmitter()->once('before', function () { + echo 'This will only happen once... per request!'; + }); + +Event Propagation +----------------- + +Event listeners can prevent other event listeners from being triggered by +stopping an event's propagation. + +Stopping event propagation can be useful, for example, if an event listener has +changed the state of the subject to such an extent that allowing subsequent +event listeners to be triggered could place the subject in an inconsistent +state. This technique is used in Guzzle extensively when intercepting error +events with responses. + +You can stop the propagation of an event using the ``stopPropagation()`` method +of a ``GuzzleHttp\Common\EventInterface`` object: + +.. code-block:: php + + use GuzzleHttp\Event\ErrorEvent; + + $emitter->on('error', function (ErrorEvent $event) { + $event->stopPropagation(); + }); + +After stopping the propagation of an event, any subsequent event listeners that +have not yet been trigger will not be triggered. You can check to see if the +propagation of an event was stopped using the ``isPropagationStopped()`` method +of the event. + +.. code-block:: php + + $client = new GuzzleHttp\Client(); + $emitter = $client->getEmitter(); + // Note: assume that the $errorEvent was created + if ($emitter->emit('error', $errorEvent)->isPropagationStopped()) { + echo 'It was stopped!'; + } + +.. hint:: + + When emitting events, the event that was emitted is returned from the + emitter. This allows you to easily chain calls as shown in the above + example. + +Event Subscribers +----------------- + +Event subscribers are classes that implement the +``GuzzleHttp\Common\EventSubscriberInterface`` object. They are used to register +one or more event listeners to methods of the class. Event subscribers tell +event emitters exactly which events to listen to and what method to invoke on +the class when the event is triggered by called the ``getEvents()`` method of +a subscriber. + +The following example registers event listeners to the ``before`` and +``complete`` event of a request. When the ``before`` event is emitted, the +``onBefore`` instance method of the subscriber is invoked. When the +``complete`` event is emitted, the ``onComplete`` event of the subscriber is +invoked. Each array value in the ``getEvents()`` return value MUST +contain the name of the method to invoke and can optionally contain the +priority of the listener (as shown in the ``before`` listener in the example). + +.. code-block:: php + + use GuzzleHttp\Event\EventEmitterInterface; + use GuzzleHttp\Event\SubscriberInterface; + use GuzzleHttp\Event\BeforeEvent; + use GuzzleHttp\Event\CompleteEvent; + + class SimpleSubscriber implements SubscriberInterface + { + public function getEvents() + { + return [ + 'before' => ['onBefore', 100], // Provide name and optional priority + 'complete' => ['onComplete'] + ]; + } + + public function onBefore(BeforeEvent $event, $name, EmitterInterface $emitter) + { + echo 'Before!'; + } + + public function onComplete(CompleteEvent $event, $name, EmitterInterface $emitter) + { + echo 'Complete!'; + } + } + +.. note:: + + You can specify event priorities using integers or ``"first"`` and + ``"last"`` to dynamically determine the priority. + +Event Priorities +================ + +When adding event listeners or subscribers, you can provide an optional event +priority. This priority is used to determine how early or late a listener is +triggered. Specifying the correct priority is an important aspect of ensuring +a listener behaves as expected. For example, if you wanted to ensure that +cookies associated with a redirect were added to a cookie jar, you'd need to +make sure that the listener that collects the cookies is triggered before the +listener that performs the redirect. + +In order to help make the process of determining the correct event priority of +a listener easier, Guzzle provides several pre-determined named event +priorities. These priorities are exposed as constants on the +``GuzzleHttp\Event\RequestEvents`` object. + +last + Use ``"last"`` as an event priority to set the priority to the current + lowest event priority minus one. + +first + Use ``"first"`` as an event priority to set the priority to the current + highest priority plus one. + +``GuzzleHttp\Event\RequestEvents::EARLY`` + Used when you want a listener to be triggered as early as possible in the + event chain. + +``GuzzleHttp\Event\RequestEvents::LATE`` + Used when you want a listener to be to be triggered as late as possible in + the event chain. + +``GuzzleHttp\Event\RequestEvents::PREPARE_REQUEST`` + Used when you want a listener to be trigger while a request is being + prepared during the ``before`` event. This event priority is used by the + ``GuzzleHttp\Subscriber\Prepare`` event subscriber which is responsible for + guessing a Content-Type, Content-Length, and Expect header of a request. + You should subscribe after this event is triggered if you want to ensure + that this subscriber has already been triggered. + +``GuzzleHttp\Event\RequestEvents::SIGN_REQUEST`` + Used when you want a listener to be triggered when a request is about to be + signed. Any listener triggered at this point should expect that the request + object will no longer be mutated. If you are implementing a custom + signature subscriber, then you should use this event priority to sign + requests. + +``GuzzleHttp\Event\RequestEvents::VERIFY_RESPONSE`` + Used when you want a listener to be triggered when a response is being + validated during the ``complete`` event. The + ``GuzzleHttp\Subscriber\HttpError`` event subscriber uses this event + priority to check if an exception should be thrown due to a 4xx or 5xx + level response status code. If you are doing any kind of verification of a + response during the complete event, it should happen at this priority. + +``GuzzleHttp\Event\RequestEvents::REDIRECT_RESPONSE`` + Used when you want a listener to be triggered when a response is being + redirected during the ``complete`` event. The + ``GuzzleHttp\Subscriber\Redirect`` event subscriber uses this event + priority when performing redirects. + +You can use the above event priorities as a guideline for determining the +priority of you event listeners. You can use these constants and add to or +subtract from them to ensure that a listener happens before or after the named +priority. + +.. note:: + + "first" and "last" priorities are not adjusted after they added to an + emitter. For example, if you add a listener with a priority of "first", + you can still add subsequent listeners with a higher priority which would + be triggered before the listener added with a priority of "first". + +Working With Request Events +=========================== + +Requests emit lifecycle events when they are transferred. + +.. important:: + + Request lifecycle events may be triggered multiple times due to redirects, + retries, or reusing a request multiple times. Use the ``once()`` method + of an event emitter if you only want the event to be triggered once. You + can also remove an event listener from an emitter by using the emitter the + is provided to the listener. + +.. _before_event: + +before +------ + +The ``before`` event is emitted before a request is sent. The event emitted is +a ``GuzzleHttp\Event\BeforeEvent``. + +.. code-block:: php + + use GuzzleHttp\Client; + use GuzzleHttp\Common\EmitterInterface; + use GuzzleHttp\Event\BeforeEvent; + + $client = new Client(['base_url' => 'http://httpbin.org']); + $request = $client->createRequest('GET', '/'); + $request->getEmitter()->on( + 'before', + function (BeforeEvent $e, $name, EmitterInterface $emitter) { + echo $name . "\n"; + // "before" + echo $e->getRequest()->getMethod() . "\n"; + // "GET" / "POST" / "PUT" / etc... + echo get_class($e->getClient()); + // "GuzzleHttp\Client" + } + ); + +You can intercept a request with a response before the request is sent over the +wire. The ``intercept()`` method of the ``BeforeEvent`` accepts a +``GuzzleHttp\Message\ResponseInterface``. Intercepting the event will prevent +the request from being sent over the wire and stops the propagation of the +``before`` event, preventing subsequent event listeners from being invoked. + +.. code-block:: php + + use GuzzleHttp\Client; + use GuzzleHttp\Event\BeforeEvent; + use GuzzleHttp\Message\Response; + + $client = new Client(['base_url' => 'http://httpbin.org']); + $request = $client->createRequest('GET', '/status/500'); + $request->getEmitter()->on('before', function (BeforeEvent $e) { + $response = new Response(200); + $e->intercept($response); + }); + + $response = $client->send($request); + echo $response->getStatusCode(); + // 200 + +.. attention:: + + Any exception encountered while executing the ``before`` event will trigger + the ``error`` event of a request. + +.. _headers_event: + +headers +------- + +The ``headers`` event is emitted after the headers of a response have been +received before any of the response body has been downloaded. The event +emitted is a ``GuzzleHttp\Event\HeadersEvent``. + +This event can be useful if you need to conditionally wrap the response body +of a request in a special decorator or if you only want to conditionally +download a response body based on response headers. + +This event cannot be intercepted. + +.. code-block:: php + + use GuzzleHttp\Client; + use GuzzleHttp\Event\HeadersEvent; + + $client = new Client(['base_url' => 'http://httpbin.org']); + $request = $client->createRequest('GET', '/stream/100'); + $request->getEmitter()->on('headers', function (HeadersEvent $e) { + echo $e->getResponse(); + // Prints the response headers + + // Wrap the response body in a custom decorator if the response has a body + if ($e->getResponse()->getHeader('Content-Length') || + $e->getResponse()->getHeader('Content-Encoding') + ) { + $customBody = new MyCustomStreamDecorator($e->getResponse()->getBody()); + $e->getResponse()->setBody($customBody); + } + }); + +.. note:: + + A response may or may not yet have a body associated with it. If a request + used a ``save_to`` request option, then the response will have a body. + Otherwise, the response will have no body but you are free to associate one + with the response. As an example, this is done in the + `progress subscriber <https://github.com/guzzle/progress-subscriber/blob/master/src/Progress.php>`_. + +.. _complete_event: + +complete +-------- + +The ``complete`` event is emitted after a transaction completes and an entire +response has been received. The event is a ``GuzzleHttp\Event\CompleteEvent``. + +You can intercept the ``complete`` event with a different response if needed +using the ``intercept()`` method of the event. This can be useful, for example, +for changing the response for caching. + +.. code-block:: php + + use GuzzleHttp\Client; + use GuzzleHttp\Event\CompleteEvent; + use GuzzleHttp\Message\Response; + + $client = new Client(['base_url' => 'http://httpbin.org']); + $request = $client->createRequest('GET', '/status/302'); + $cachedResponse = new Response(200); + + $request->getEmitter()->on( + 'complete', + function (CompleteEvent $e) use ($cachedResponse) { + if ($e->getResponse()->getStatusCode() == 302) { + // Intercept the original transaction with the new response + $e->intercept($cachedResponse); + } + } + ); + + $response = $client->send($request); + echo $response->getStatusCode(); + // 200 + +.. attention:: + + Any ``GuzzleHttp\Exception\RequestException`` encountered while executing + the ``complete`` event will trigger the ``error`` event of a request. + +.. _error_event: + +error +----- + +The ``error`` event is emitted when a request fails (whether it's from a +networking error or an HTTP protocol error). The event emitted is a +``GuzzleHttp\Event\ErrorEvent``. + +This event is useful for retrying failed requests. Here's an example of +retrying failed basic auth requests by re-sending the original request with +a username and password. + +.. code-block:: php + + use GuzzleHttp\Client; + use GuzzleHttp\Event\ErrorEvent; + + $client = new Client(['base_url' => 'http://httpbin.org']); + $request = $client->createRequest('GET', '/basic-auth/foo/bar'); + $request->getEmitter()->on('error', function (ErrorEvent $e) { + if ($e->getResponse()->getStatusCode() == 401) { + // Add authentication stuff as needed and retry the request + $e->getRequest()->setHeader('Authorization', 'Basic ' . base64_encode('foo:bar')); + // Get the client of the event and retry the request + $newResponse = $e->getClient()->send($e->getRequest()); + // Intercept the original transaction with the new response + $e->intercept($newResponse); + } + }); + +.. attention:: + + If an ``error`` event is intercepted with a response, then the ``complete`` + event of a request is triggered. If the ``complete`` event fails, then the + ``error`` event is triggered once again. diff --git a/core/vendor/guzzlehttp/guzzle/docs/faq.rst b/core/vendor/guzzlehttp/guzzle/docs/faq.rst new file mode 100644 index 0000000000000000000000000000000000000000..8ad7c460a28fde6d26c6cb7dff11fa8bd42f1dfb --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/docs/faq.rst @@ -0,0 +1,111 @@ +=== +FAQ +=== + +Is it possible to use Guzzle 3 and 4 in the same project? +========================================================= + +Yes, because Guzzle 3 and 4 use different Packagist packages and different +namespaced. You simply need to add ``guzzle/guzzle`` (Guzzle 3) and +``guzzlehttp/guzzle`` (Guzzle 4+) to your project's composer.json file. + +.. code-block:: javascript + + { + "require": { + "guzzle/guzzle": 3.*, + "guzzlehttp/guzzle": 4.* + } + } + +You might need to use Guzzle 3 and Guzzle 4 in the same project due to a +requirement of a legacy application or a dependency that has not yet migrated +to Guzzle 4.0. + +How do I migrate from Guzzle 3 to 4? +==================================== + +See https://github.com/guzzle/guzzle/blob/master/UPGRADING.md#3x-to-40. + +What is this Maximum function nesting error? +============================================ + + Maximum function nesting level of '100' reached, aborting + +You could run into this error if you have the XDebug extension installed and +you execute a lot of requests in callbacks. This error message comes +specifically from the XDebug extension. PHP itself does not have a function +nesting limit. Change this setting in your php.ini to increase the limit:: + + xdebug.max_nesting_level = 1000 + +[`source <http://stackoverflow.com/a/4293870/151504>`_] + +Why am I getting a 417 error response? +====================================== + +This can occur for a number of reasons, but if you are sending PUT, POST, or +PATCH requests with an ``Expect: 100-Continue`` header, a server that does not +support this header will return a 417 response. You can work around this by +setting the ``expect`` request option to ``false``: + +.. code-block:: php + + $client = new GuzzleHttp\Client(); + + // Disable the expect header on a single request + $response = $client->put('/', [], 'the body', [ + 'expect' => false + ]); + + // Disable the expect header on all client requests + $client->setDefaultOption('expect', false) + +How can I add custom cURL options? +================================== + +cURL offer a huge number of `customizable options <http://us1.php.net/curl_setopt>`_. +While Guzzle normalizes many of these options across different adapters, there +are times when you need to set custom cURL options. This can be accomplished +by passing an associative array of cURL settings in the **curl** key of the +**config** request option. + +For example, let's say you need to customize the outgoing network interface +used with a client. + +.. code-block:: php + + $client->get('/', [ + 'config' => [ + 'curl' => [ + CURLOPT_INTERFACE => 'xxx.xxx.xxx.xxx' + ] + ] + ]); + +How can I add custom stream context options? +============================================ + +You can pass custom `stream context options <http://www.php.net/manual/en/context.php>`_ +using the **stream_context** key of the **config** request option. The +**stream_context** array is an associative array where each key is a PHP +transport, and each value is an associative array of transport options. + +For example, let's say you need to customize the outgoing network interface +used with a client and allow self-signed certificates. + +.. code-block:: php + + $client->get('/', [ + 'stream' => true, + 'config' => [ + 'stream_context' => [ + 'ssl' => [ + 'allow_self_signed' => true + ], + 'socket' => [ + 'bindto' => 'xxx.xxx.xxx.xxx' + ] + ] + ] + ]); diff --git a/core/vendor/guzzlehttp/guzzle/docs/http-messages.rst b/core/vendor/guzzlehttp/guzzle/docs/http-messages.rst new file mode 100644 index 0000000000000000000000000000000000000000..1ccf3949033683a23c3a42bb7bc542b8b606bd31 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/docs/http-messages.rst @@ -0,0 +1,480 @@ +============================= +Request and Response Messages +============================= + +Guzzle is an HTTP client that sends HTTP requests to a server and receives HTTP +responses. Both requests and responses are referred to as messages. + +Headers +======= + +Both request and response messages contain HTTP headers. + +Complex Headers +--------------- + +Some headers contain additional key value pair information. For example, Link +headers contain a link and several key value pairs: + +:: + + <http://foo.com>; rel="thing"; type="image/jpeg" + +Guzzle provides a convenience feature that can be used to parse these types of +headers: + +.. code-block:: php + + use GuzzleHttp\Message\Request; + + $request = new Request('GET', '/', [ + 'Link' => '<http:/.../front.jpeg>; rel="front"; type="image/jpeg"' + ]); + + $parsed = Request::parseHeader($request, 'Link'); + echo json_encode($parsed, JSON_PRETTY_PRINT); + +:: + + [ + { + "0": "<http:\/...\/front.jpeg>", + "rel": "front", + "type": "image\/jpeg" + } + ] + +The result contains a hash of key value pairs. Header values that have no key +(i.e., the link) are indexed numerically while headers parts that form a key +value pair are added as a key value pair. + +See :ref:`headers` for information on how the headers of a request and response +can be accessed and modified. + +Body +==== + +Both request and response messages can contain a body. + +You can check to see if a request or response has a body using the +``getBody()`` method: + +.. code-block:: php + + $response = GuzzleHttp\get('http://httpbin.org/get'); + if ($response->getBody()) { + echo $response->getBody(); + // JSON string: { ... } + } + +The body used in request and response objects is a +``GuzzleHttp\Stream\StreamInterface``. This stream is used for both uploading +data and downloading data. Guzzle will, by default, store the body of a message +in a stream that uses PHP temp streams. When the size of a the body exceeds +2 MB, the stream will automatically switch to storing data on disk rather than +in memory (protecting your application from memory exhaustion). + +You can change the body used in a request or response using the ``setBody()`` +method: + +.. code-block:: php + + use GuzzleHttp\Stream\Stream; + $request = $client->createRequest('PUT', 'http://httpbin.org/put'); + $request->setBody(Stream::factory('foo')); + +The easiest way to create a body for a request is using the static +``GuzzleHttp\Stream\Stream::factory()`` method. This method accepts various +inputs like strings, resources returned from ``fopen()``, and other +``GuzzleHttp\Stream\StreamInterface`` objects. + +The body of a request or response can be cast to a string or you can read and +write bytes off of the stream as needed. + +.. code-block:: php + + use GuzzleHttp\Stream\Stream; + $request = $client->createRequest('PUT', 'http://httpbin.org/put', ['body' => 'testing...']); + + echo $request->getBody()->read(4); + // test + echo $request->getBody()->read(4); + // ing. + echo $request->getBody()->read(1024); + // .. + var_export($request->eof()); + // true + +You can find out more about Guzzle stream objects in :doc:`streams`. + +Requests +======== + +Requests are sent from a client to a server. Requests include the method to +be applied to a resource, the identifier of the resource, and the protocol +version to use. + +Clients are used to create request messages. More precisely, clients use +a ``GuzzleHttp\Message\MessageFactoryInterface`` to create request messages. +You create requests with a client using the ``createRequest()`` method. + +.. code-block:: php + + // Create a request but don't sent it immediately + $request = $client->createRequest('GET', 'http://httpbin.org/get'); + +Request Methods +--------------- + +When creating a request, you are expected to provide the HTTP method you wish +to perform. You can specfiy any method you'd like, including a custom method +that might not be part of RFC 2616 (like "MOVE"). + +.. code-block:: php + + // Create a request using a completely custom HTTP method + $request = $client->createRequest('MOVE', 'http://httpbin.org/move', ['exceptions' => false]); + + echo $request->getMethod(); + // MOVE + + $response = $client->send($request); + echo $response->getStatusCode(); + // 405 + +You can create and send a request using methods on a client that map to the +HTTP method you wish to use. + +:GET: ``$client->get('http://httpbin.org/get', [/** options **/])`` +:POST: ``$client->post('http://httpbin.org/post', [/** options **/])`` +:HEAD: ``$client->head('http://httpbin.org/get', [/** options **/])`` +:PUT: ``$client->put('http://httpbin.org/put', [/** options **/])`` +:DELETE: ``$client->delete('http://httpbin.org/delete', [/** options **/])`` +:OPTIONS: ``$client->options('http://httpbin.org/get', [/** options **/])`` +:PATCH: ``$client->patch('http://httpbin.org/put', [/** options **/])`` + +.. code-block:: php + + $response = $client->patch('http://httpbin.org/patch', ['body' => 'content']); + +Request URI +----------- + +The resource you are requesting with an HTTP request is identified by the +path of the request, the query string, and the "Host" header of the request. + +When creating a request, you can provide the entire resource URI as a URL. + +.. code-block:: php + + $response = $client->get('http://httbin.org/get?q=foo'); + +Using the above code, you will send a request that uses ``httpbin.org`` as +the Host header, sends the request over port 80, uses ``/get`` as the path, +and sends ``?q=foo`` as the query string. All of this is parsed automatically +from the provided URI. + +Sometimes you don't know what the entire request will be when it is created. +In these cases, you can modify the request as needed before sending it using +the ``createRequest()`` method of the client and methods on the request that +allow you to change it. + +.. code-block:: php + + $request = $client->createRequest('GET', 'http://httbin.org'); + +You can change the path of the request using ``setPath()``: + +.. code-block:: php + + $request->setPath('/get'); + echo $request->getPath(); + // /get + echo $request->getUrl(); + // http://httpbin.com/get + +Scheme +~~~~~~ + +The `scheme <http://tools.ietf.org/html/rfc3986#section-3.1>`_ of a request +specifies the protocol to use when sending the request. When using Guzzle, the +scheme can be set to "http" or "https". + +You can change the scheme of the request using the ``setScheme()`` method: + +.. code-block:: php + + $request = $client->createRequest('GET', 'http://httbin.org'); + $request->setScheme('https'); + echo $request->getScheme(); + // https + echo $request->getUrl(); + // https://httpbin.com/get + +Port +~~~~ + +No port is necessary when using the "http" or "https" schemes, but you can +override the port using ``setPort()``. If you need to modify the port used with +the specified scheme from the default setting, then you must use the +``setPort()`` method. + +.. code-block:: php + + $request = $client->createRequest('GET', 'http://httbin.org'); + $request->setPort(8080); + echo $request->getPort(); + // 8080 + echo $request->getUrl(); + // https://httpbin.com:8080/get + + // Set the port back to the default value for the scheme + $request->setPort(443); + echo $request->getUrl(); + // https://httpbin.com/get + +Query string +~~~~~~~~~~~~ + +You can get the query string of the request using the ``getQuery()`` method. +This method returns a ``GuzzleHttp\Query`` object. A Query object can be +accessed like a PHP array, iterated in a foreach statement like a PHP array, +and cast to a string. + +.. code-block:: php + + $request = $client->createRequest('GET', 'http://httbin.org'); + $query = $request->getQuery(); + $query['foo'] = 'bar'; + $query['baz'] = 'bam'; + $query['bam'] = ['test' => 'abc']; + + echo $request->getQuery(); + // foo=bar&baz=bam&bam%5Btest%5D=abc + + echo $request->getQuery()['foo']; + // bar + echo $request->getQuery()->get('foo'); + // bar + echo $request->getQuery()->get('foo'); + // bar + + var_export($request->getQuery()['bam']); + // array('test' => 'abc') + + foreach ($query as $key => $value) { + var_export($value); + } + + echo $request->getUrl(); + // https://httpbin.com/get?foo=bar&baz=bam&bam%5Btest%5D=abc + +Query Aggregators +^^^^^^^^^^^^^^^^^ + +Query objects can store scalar values or arrays of values. When an array of +values is added to a query object, the query object uses a query aggregator to +convert the complex structure into a string. Query objects will use +`PHP style query strings <http://www.php.net/http_build_query>`_ when complex +query string parameters are converted to a string. You can customize how +complex query string parameters are aggregated using the ``setAggregator()`` +method of a query string object. + +.. code-block:: php + + $query->setAggregator($query::duplicateAggregator()); + +In the above example, we've changed the query object to use the +"duplicateAggregator". This aggregator will allow duplicate entries to appear +in a query string rather than appending "[n]" to each value. So if you had a +query string with ``['a' => ['b', 'c']]``, the duplicate aggregator would +convert this to "a=b&a=c" while the default aggregator would convert this to +"a[0]=b&a[1]=c" (with urlencoded brackets). + +The ``setAggregator()`` method accepts a ``callable`` which is used to convert +a deeply nested array of query string variables into a flattened array of key +value pairs. The callable accepts an array of query data and returns a +flattened array of key value pairs where each value is an array of strings. +You can use the ``GuzzleHttp\Query::walkQuery()`` static function to easily +create custom query aggregators. + +Host +~~~~ + +You can change the host header of the request in a predictable way using the +``setHost()`` method of a request: + +.. code-block:: php + + $request->setHost('www.google.com'); + echo $request->getHost(); + // www.google.com + echo $request->getUrl(); + // https://www.google.com/get?foo=bar&baz=bam + +.. note:: + + The Host header can also be changed by modifying the Host header of a + request directly, but modifying the Host header directly could result in + sending a request to a different Host than what is specified in the Host + header (sometimes this is actually the desired behavior). + +Resource +~~~~~~~~ + +You can use the ``getResource()`` method of a request to return the path and +query string of a request in a single string. + +.. code-block:: php + + $request = $client->createRequest('GET', 'http://httpbin.org/get?baz=bar'); + echo $request->getResource(); + // /get?baz=bar + +Request Config +-------------- + +Request messages contain a configuration collection that can be used by +event listeners and HTTP adapters to modify how a request behaves or is +transferred over the wire. For example, many of the request options that are +specified when creating a request are actually set as config options that are +only acted upon by adapters and listeners when the request is sent. + +You can get access to the request's config object using the ``getConfig()`` +method of a request. + +.. code-block:: php + + $request = $client->createRequest('GET', '/'); + $config = $request->getConfig(); + +The config object is a ``GuzzleHttp\Common\Collection`` object that acts like +an associative array. You can grab values from the collection using array like +access. You can also modify and remove values using array like access. + +.. code-block:: php + + $config['foo'] = 'bar'; + echo $config['foo']; + // bar + + var_export(isset($config['foo'])); + // true + + unset($config['foo']); + var_export(isset($config['foo'])); + // false + + var_export($config['foo']); + // NULL + +HTTP adapters and event listeners can expose additional customization options +through request config settings. For example, in order to specify custom cURL +options to the cURL adapter, you need to specify an associative array in the +``curl`` ``config`` request option. + +.. code-block:: php + + $client->get('/', [ + 'config' => [ + 'curl' => [ + CURLOPT_HTTPAUTH => CURLAUTH_NTLM, + CURLOPT_USERPWD => 'username:password' + ] + ] + ]); + +Consult the HTTP adapters and event listeners you are using to see if they +allow customization through request configuration options. + +Event Emitter +------------- + +Request objects implement ``GuzzleHttp\Common\HasEmitterInterface``, so they +have a method called ``getEmitter()`` that can be used to get an event emitter +used by the request. Any listener or subscriber attached to a request will only +be triggered for the lifecycle events of a specific request. Conversely, adding +an event listener or subscriber to a client will listen to all lifecycle events +of all requests created by the client. + +See :doc:`events` for more information. + +Responses +========= + +Responses are the HTTP messages a client receives from a server after sending +an HTTP request message. + +Start-Line +---------- + +The start-line of a response contains the protocol and protocol version, +status code, and reason phrase. + +.. code-block:: php + + $response = GuzzleHttp\get('http://httpbin.org/get'); + echo $response->getStatusCode(); + // 200 + echo $response->getReasonPhrase(); + // OK + echo $response->getProtocolVersion(); + // 1.1 + +Body +---- + +As described earlier, you can get the body of a response using the +``getBody()`` method. + +.. code-block:: php + + if ($body = $response->getBody()) { + echo $body; + // Cast to a string: { ... } + $body->seek(0); + // Rewind the body + $body->read(1024); + // Read bytes of the body + } + +When working with JSON responses, you can use the ``json()`` method of a +response: + +.. code-block:: php + + $json = $response->json(); + +.. note:: + + Guzzle uses the ``json_decode()`` method of PHP and uses arrays rather than + ``stdClass`` objects for objects. + +You can use the ``xml()`` method when working with XML data. + +.. code-block:: php + + $xml = $response->xml(); + +.. note:: + + Guzzle uses the ``SimpleXMLElement`` objects when converting response + bodies to XML. + +Effective URL +------------- + +The URL that was ultimately accessed that returned a response can be accessed +using the ``getEffectiveUrl()`` method of a response. This method will return +the URL of a reqeust or the URL of the last redirected URL if any redirects +occurred while transferring a request. + +.. code-block:: php + + $response = GuzzleHttp\get('http://httpbin.org/get'); + echo $response->getEffectiveUrl(); + // http://httpbin.org/get + + $response = GuzzleHttp\get('http://httpbin.org/redirect-to?url=http://www.google.com'); + echo $response->getEffectiveUrl(); + // http://www.google.com diff --git a/core/vendor/guzzlehttp/guzzle/docs/index.rst b/core/vendor/guzzlehttp/guzzle/docs/index.rst new file mode 100644 index 0000000000000000000000000000000000000000..c06e0bf3f318dca31bfb23a21eb31a46a34de041 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/docs/index.rst @@ -0,0 +1,94 @@ +.. title:: Guzzle | PHP HTTP client and framework for consuming RESTful web services + +====== +Guzzle +====== + +Guzzle is a PHP HTTP client that makes it easy to work with HTTP/1.1 and takes +the pain out of consuming web services. + +- Pluggable HTTP adapters that can send requests serially or in parallel +- Doesn't require cURL, but uses cURL by default +- Streams data for both uploads and downloads +- Provides event hooks & plugins for cookies, caching, logging, OAuth, mocks, etc... +- Keep-Alive & connection pooling +- SSL Verification +- Automatic decompression of response bodies +- Streaming multipart file uploads +- Connection timeouts + +.. code-block:: php + + $client = new GuzzleHttp\Client(); + $response = $client->get('http://guzzlephp.org'); + $res = $client->get('https://api.github.com/user', ['auth' => ['user', 'pass']]); + echo $res->statusCode(); + // 200 + echo $res->getHeader('content-type'); + // 'application/json; charset=utf8' + echo $res->getBody(); + // {"type":"User"...' + var_export($res->json()); + // Outputs the JSON decoded data + +User guide +---------- + +.. toctree:: + :maxdepth: 2 + + overview + quickstart + clients + http-messages + events + streams + adapters + testing + faq + +HTTP Components +--------------- + +There are a number of optional libraries you can use along with Guzzle's HTTP +layer to add capabilities to the client. + +`Log Subscriber <https://github.com/guzzle/log-subscriber>`_ + Logs HTTP requests and responses sent over the wire using customizable + log message templates. + +`OAuth Subscriber <https://github.com/guzzle/oauth-subscriber>`_ + Signs requests using OAuth 1.0. + +`Progress Subscriber <https://github.com/guzzle/progress-subscriber>`_ + Emits progress events when uploading and downloading data. + +`Cache Subscriber <https://github.com/guzzle/cache-subscriber>`_ + Implements a private transparent proxy cache that caches HTTP responses. + +`Retry Subscriber <https://github.com/guzzle/retry-subscriber>`_ + Retries failed requests using customizable retry strategies (e.g., retry + based on response status code, cURL error codes, etc...) + +`Message Integrity Subscriber <https://github.com/guzzle/message-integrity-subscriber>`_ + Verifies the message integrity of HTTP responses using customizable + validators. This plugin can be used, for example, to verify the Content-MD5 + headers of responses. + +Service Description Commands +---------------------------- + +You can use the **Guzzle Command** library to encapsulate interaction with a +web service using command objects. Building on top of Guzzle's command +abstraction allows you to easily implement things like service description that +can be used to serialize requests and parse responses using a meta-description +of a web service. + +`Guzzle Command <https://github.com/guzzle/command>`_ + Provides the foundational elements used to build high-level, command based, + web service clients with Guzzle. + +`Guzzle Services <https://github.com/guzzle/guzzle-services>`_ + Provides an implementation of the *Guzzle Command* library that uses + Guzzle service descriptions to describe web services, serialize requests, + and parse responses into easy to use model structures. diff --git a/core/vendor/guzzlehttp/guzzle/docs/overview.rst b/core/vendor/guzzlehttp/guzzle/docs/overview.rst new file mode 100644 index 0000000000000000000000000000000000000000..35c74d86ce116545805c38cf4b6a6e3ed424acb4 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/docs/overview.rst @@ -0,0 +1,150 @@ +======== +Overview +======== + +Requirements +============ + +#. PHP 5.4.0 +#. To use the PHP stream adapter, ``allow_url_fopen`` must be enabled in your + system's php.ini. +#. To use the cURL adapter, you must have a recent version of cURL >= 7.16.2 + compiled with OpenSSL and zlib. + +.. note:: + + Guzzle no longer requires cURL in order to send HTTP requests. Guzzle will + use the PHP stream wrapper to send HTTP requests if cURL is not installed. + Alternatively, you can provide your own HTTP adapter used to send requests. + +.. _installation: + +Installation +============ + +The recommended way to install Guzzle is with `Composer <http://getcomposer.org>`_. Composer is a dependency +management tool for PHP that allows you to declare the dependencies your project needs and installs them into your +project. + +.. code-block:: bash + + # Install Composer + curl -sS https://getcomposer.org/installer | php + +You can add Guzzle as a dependency using the composer.phar CLI: + +.. code-block:: bash + + php composer.phar require guzzlehttp/guzzle:~4 + +Alternatively, you can specify Guzzle as a dependency in your project's +existing composer.json file: + +.. code-block:: js + + { + "require": { + "guzzlehttp/guzzle": "4.*" + } + } + +After installing, you need to require Composer's autoloader: + +.. code-block:: php + + require 'vendor/autoload.php'; + +You can find out more on how to install Composer, configure autoloading, and +other best-practices for defining dependencies at `getcomposer.org <http://getcomposer.org>`_. + +Bleeding edge +------------- + +During your development, you can keep up with the latest changes on the master +branch by setting the version requirement for Guzzle to ``dev-master``. + +.. code-block:: js + + { + "require": { + "guzzlehttp/guzzle": "dev-master" + } + } + +License +======= + +Licensed using the `MIT license <http://opensource.org/licenses/MIT>`_. + + Copyright (c) 2014 Michael Dowling <https://github.com/mtdowling> + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +Contributing +============ + +Guidelines +---------- + +1. Guzzle follows PSR-0, PSR-1, and PSR-2. +2. Guzzle is meant to be lean and fast with very few dependencies. +3. Guzzle has a minimum PHP version requirement of PHP 5.4. Pull requests must + not require a PHP version greater than PHP 5.4. +4. All pull requests must include unit tests to ensure the change works as + expected and to prevent regressions. + +Running the tests +----------------- + +In order to contribute, you'll need to checkout the source from GitHub and +install Guzzle's dependencies using Composer: + +.. code-block:: bash + + git clone https://github.com/guzzle/guzzle.git + cd guzzle && curl -s http://getcomposer.org/installer | php && ./composer.phar install --dev + +Guzzle is unit tested with PHPUnit. Run the tests using the vendored PHPUnit +binary: + +.. code-block:: bash + + vendor/bin/phpunit + +.. note:: + + You'll need to install node.js v0.5.0 or newer in order to perform + integration tests on Guzzle's HTTP adapters. + +Reporting a security vulnerability +================================== + +We want to ensure that Guzzle is a secure HTTP client library for everyone. If +you've discovered a security vulnerability in Guzzle, we appreciate your help +in disclosing it to us in a `responsible manner <http://en.wikipedia.org/wiki/Responsible_disclosure>`_. + +Publicly disclosing a vulnerability can put the entire community at risk. If +you've discovered a security concern, please email us at +security@guzzlephp.org. We'll work with you to make sure that we understand the +scope of the issue, and that we fully address your concern. We consider +correspondence sent to security@guzzlephp.org our highest priority, and work to +address any issues that arise as quickly as possible. + +After a security vulnerability has been corrected, a security hotfix release will +be deployed as soon as possible. diff --git a/core/vendor/guzzlehttp/guzzle/docs/quickstart.rst b/core/vendor/guzzlehttp/guzzle/docs/quickstart.rst new file mode 100644 index 0000000000000000000000000000000000000000..51c7ba8d2bca459c5b4924704e410e2ac9965d65 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/docs/quickstart.rst @@ -0,0 +1,454 @@ +========== +Quickstart +========== + +This page provides a quick introduction to Guzzle and introductory examples. +If you have not already installed, Guzzle, head over to the :ref:`installation` +page. + +Make a Request +============== + +You can send requests with Guzzle in one of two ways: through the procedural +API or using a ``GuzzleHttp\ClientInterface`` object. Using the procedural API +is an easy way to send a quick HTTP request. Using a Client object provides +much more flexibility in how requests are transferred and allows you to more +easily test the client. + +Procedural API +-------------- + +Here's an example of sending a ``GET`` request using the procedural API. + +.. code-block:: php + + $response = GuzzleHttp\post('http://httpbin.org/post', [ + 'headers' => ['X-Foo' => 'Bar'], + 'body' => ['field_name' => 'value'] + ]); + +You can send all kinds of HTTP requests with the procedural API. Just call +the function that maps to the HTTP method name. + +.. code-block:: php + + $response = GuzzleHttp\head('http://httpbin.org/get'); + $response = GuzzleHttp\post('http://httpbin.org/post'); + $response = GuzzleHttp\put('http://httpbin.org/put'); + $response = GuzzleHttp\delete('http://httpbin.org/delete'); + $response = GuzzleHttp\options('http://httpbin.org/get'); + +Creating a Client +----------------- + +The procedural API is simple but not very testable; it's best left for quick +prototyping. If you want to use Guzzle in a more flexible and testable way, +then you'll need to use a ``GuzzleHttp\ClientInterface`` object. + +.. code-block:: php + + use GuzzleHttp\Client; + + $client = new Client(); + $response = $client->get('https://github.com/timeline.json'); + + // You can use the same methods you saw in the procedural API + $response = $client->delete('http://httpbin.org/delete'); + $response = $client->head('http://httpbin.org/get'); + $response = $client->options('http://httpbin.org/get'); + $response = $client->patch('http://httpbin.org/patch'); + $response = $client->post('http://httpbin.org/post'); + $response = $client->put('http://httpbin.org/put'); + +You can create a request with a client and then send the request with the +client when you're ready. + +.. code-block:: php + + $request = $client->createRequest('GET', 'http://www.foo.com'); + $response = $client->send($request); + +Client objects provide a great deal of flexibility in how request are +transferred including default request options, subscribers that are attached +to each request, and a base URL that allows you to send requests with relative +URLs. You can find out all about clients in the :doc:`clients` page of the +documentation. + +Using Responses +=============== + +In the previous examples, we retrieved a ``$response`` variable. This value is +actually a ``GuzzleHttp\Message\ResponseInterface`` object and contains lots +of helpful information. + +You can get the status code and reason phrase of the response. + +.. code-block:: php + + $code = $response->getStatusCode(); + // 200 + + $reason = $response->getReasonPhrase(); + // OK + +Response Body +------------- + +The body of a response can be retrieved and cast to a string. + +.. code-block:: php + + $body = $response->getBody(); + echo $body; + // { "some_json_data" ...} + +You can also read read bytes from body of a response like a stream. + +.. code-block:: php + + $body = $response->getBody(); + + while (!$body->eof()) { + echo $body->read(1024); + } + +JSON Responses +~~~~~~~~~~~~~~ + +You can more easily work with JSON responses using the ``json()`` method of a +response. + +.. code-block:: php + + $response = $client->get('https://github.com/timeline.json'); + $json = $response->json(); + var_dump($json[0]['repository']); + +Guzzle internally uses PHP's ``json_decode()`` function to parse responses. If +Guzzle is unable to parse the JSON response body, then a +``GuzzleHttp\Exception\ParseException`` is thrown. + +XML Responses +~~~~~~~~~~~~~ + +You can use a response's ``xml()`` method to more easily work with responses +that contain XML data. + +.. code-block:: php + + $response = $client->get('https://github.com/mtdowling.atom'); + $xml = $response->xml(); + echo $xml->id; + // tag:github.com,2008:/mtdowling + +Guzzle internally uses a ``SimpleXMLElement`` object to parse responses. If +Guzzle is unable to parse the XML response body, then a +``GuzzleHttp\Exception\ParseException`` is thrown. + +Query String Parameters +======================= + +Sending query string parameters with a request is easy. You can set query +string parameters in the request's URL. + +.. code-block:: php + + $response = $client->get('http://httpbin.org?foo=bar'); + +You can also specify the query string parameters using the ``query`` request +option. + +.. code-block:: php + + $client->get('http://httpbin.org', [ + 'query' => ['foo' => 'bar'] + ]); + +And finally, you can build up the query string of a request as needed by +calling the ``getQuery()`` method of a request and modifying the request's +``GuzzleHttp\Query`` object as needed. + +.. code-block:: php + + $request = $client->createRequest('GET', 'http://httpbin.org'); + $query = $request->getQuery(); + $query->set('foo', 'bar'); + + // You can use the query string object like an array + $query['baz'] = 'bam'; + + // The query object can be cast to a string + echo $query; + // foo=bar&baz=bam + + // Setting a value to false or null will cause the "=" sign to be omitted + $query['empty'] = null; + echo $query; + // foo=bar&baz=bam&empty + + // Use an empty string to include the "=" sign with an empty value + $query['empty'] = ''; + echo $query; + // foo=bar&baz=bam&empty= + +.. _headers: + +Request and Response Headers +---------------------------- + +You can specify request headers when sending or creating requests with a +client. In the following example, we send the ``X-Foo-Header`` with a value of +``value`` by setting the ``headers`` request option. + +.. code-block:: php + + $response = $client->get('http://httpbin.org/get', [ + 'headers' => ['X-Foo-Header' => 'value'] + ]); + +You can view the headers of a response using header specific methods of a +response class. Headers work exactly the same way for request and response +object. + +You can retrieve a header from a request or response using the ``getHeader()`` +method of the object. This method is case-insensitive and by default will +return a string containing the header field value. + +.. code-block:: php + + $response = $client->get('http://www.yahoo.com'); + $length = $response->getHeader('Content-Length'); + +Header fields that contain multiple values can be retrieved as a string or as +an array. Retrieving the field values as a string will naively concatenate all +of the header values together with a comma. Because not all header fields +should be represented this way (e.g., ``Set-Cookie``), you can pass an optional +flag to the ``getHeader()`` method to retrieve the header values as an array. + +.. code-block:: php + + $values = $response->getHeader('Set-Cookie', true); + foreach ($values as $value) { + echo $value; + } + +You can test if a request or response has a specific header using the +``hasHeader()`` method. This method accepts a case-insensitive string and +returns true if the header is present or false if it is not. + +You can retrieve all of the headers of a message using the ``getHeaders()`` +method of a request or response. The return value is an associative array where +the keys represent the header name as it will be sent over the wire, and each +value is an array of strings associated with the header. + +.. code-block:: php + + $headers = $response->getHeaders(); + foreach ($message->getHeaders() as $name => $values) { + echo $name . ": " . implode(", ", $values); + } + +Modifying headers +----------------- + +The headers of a message can be modified using the ``setHeader()``, +``addHeader()``, ``setHeaders()``, and ``removeHeader()`` methods of a request +or response object. + +.. code-block:: php + + $request = $client->createRequest('GET', 'http://httpbin.org/get'); + + // Set a single value for a header + $request->setHeader('User-Agent', 'Testing!'); + + // Set multiple values for a header in one call + $request->setHeader('X-Foo', ['Baz', 'Bar']); + + // Add a header to the message + $request->addHeader('X-Foo', 'Bam'); + + echo $request->getHeader('X-Foo'); + // Baz, Bar, Bam + + // Remove a specific header using a case-insensitive name + $request->removeHeader('x-foo'); + echo $request->getHeader('X-Foo'); + // Echoes an empty string: '' + +POST Requests +============= + +You can send POST requests that contain a raw POST body by passing a +string, resource returned from ``fopen``, or a +``GuzzleHttp\Stream\StreamInterface`` object to the ``body`` request option. + +.. code-block:: php + + $r = $client->post('http://httpbin.org/post', ['body' => 'raw data']); + +Sending POST Fields +------------------- + +Sending ``application/x-www-form-urlencoded`` POST requests requires that you +specify the body of a POST request as an array. + +.. code-block:: php + + $response = $client->post('http://httpbin.org/post', [ + 'body' => [ + 'field_name' => 'abc', + 'other_field' => '123' + ] + ]); + +You can also build up POST requests before sending them. + +.. code-block:: php + + $request = $client->createRequest('POST', 'http://httpbin.org/post'); + $postBody = $request->getBody(); + + // $postBody is an instance of GuzzleHttp\Post\PostBodyInterface + $postBody->setField('foo', 'bar'); + echo $postBody->getField('foo'); + // 'bar' + + echo json_encode($postBody->getFields()); + // {"foo": "bar"} + + // Send the POST request + $response = $client->send($request); + +Sending POST Files +------------------ + +Sending ``multipart/form-data`` POST requests (POST requests that contain +files) is the same as sending ``application/x-www-form-urlencoded``, except +some of the array values of the POST fields map to PHP ``fopen`` resources, or +``GuzzleHttp\Stream\StreamInterface``, or +``GuzzleHttp\Post\PostFileInterface`` objects. + +.. code-block:: php + + use GuzzleHttp\Post\PostFile; + + $response = $client->post('http://httpbin.org/post', [ + 'body' => [ + 'field_name' => 'abc', + 'file_filed' => fopen('/path/to/file', 'r'), + 'other_file' => new PostFile('other_file', 'this is the content') + ] + ]); + +Just like when sending POST fields, you can also build up POST requests with +files before sending them. + +.. code-block:: php + + use GuzzleHttp\Post\PostFile; + + $request = $client->createRequest('POST', 'http://httpbin.org/post'); + $postBody = $request->getBody(); + $postBody->setField('foo', 'bar'); + $postBody->addFile(new PostFile('test', fopen('/path/to/file', 'r'))); + $response = $client->send($request); + +Cookies +======= + +Guzzle can maintain a cookie session for you if instructed using the +``cookies`` request option. + +- Set to ``true`` to use a shared cookie session associated with the client. +- Pass an associative array containing cookies to send in the request and start + a new cookie session. +- Set to a ``GuzzleHttp\Subscriber\CookieJar\CookieJarInterface`` object to uss + an existing cookie jar. + +Redirects +========= + +Guzzle will automatically follow redirects unless you tell it not to. You can +customize the redirect behavior using the ``allow_redirects`` request option. + +- Set to true to enable normal redirects with a maximum number of 5 redirects. + This is the default setting. +- Set to false to disable redirects. +- Pass an associative array containing the 'max' key to specify the maximum + number of redirects and optionally provide a 'strict' key value to specify + whether or not to use strict RFC compliant redirects (meaning redirect POST + requests with POST requests vs. doing what most browsers do which is + redirect POST requests with GET requests). + +.. code-block:: php + + $response = $client->get('http://github.com'); + echo $response->getStatusCode(); + // 200 + echo $response->getEffectiveUrl(); + // 'https://github.com/' + +The following example shows that redirects can be disabled. + +.. code-block:: php + + $response = $client->get('http://github.com', ['allow_redirects' => false]); + echo $response->getStatusCode(); + // 301 + echo $response->getEffectiveUrl(); + // 'http://github.com/' + +Exceptions +========== + +Guzzle throws exceptions for errors that occur during a transfer. + +- In the event of a networking error (connection timeout, DNS errors, etc), + a ``GuzzleHttp\Exception\RequestException`` is thrown. This exception + extends from ``GuzzleHttp\Exception\TransferException``. Catching this + exception will catch any exception that can be thrown while transferring + (non-parallel) requests. + + .. code-block:: php + + use GuzzleHttp\Exception\RequestException; + + try { + $client->get('https://github.com/_abc_123_404'); + } catch (RequestException $e) { + echo $e->getRequest(); + if ($e->hasResponse()) { + echo $e->getResponse(); + } + } + +- A ``GuzzleHttp\Exception\ClientErrorResponseException`` is thrown for 400 + level errors if the ``exceptions`` request option is not set to true. This + exception extends from ``GuzzleHttp\Exception\BadResponseException`` and + ``GuzzleHttp\Exception\BadResponseException`` extends from + ``GuzzleHttp\Exception\RequestException``. + + .. code-block:: php + + use GuzzleHttp\Exception\ClientErrorResponseException; + + try { + $client->get('https://github.com/_abc_123_404'); + } catch (ClientErrorResponseException $e) { + echo $e->getRequest(); + echo $e->getResponse(); + } + +- A ``GuzzleHttp\Exception\ServerErrorResponse`` is thrown for 500 level + errors if the ``exceptions`` request option is not set to true. This + exception extends from ``GuzzleHttp\Exception\BadResponseException``. +- A ``GuzzleHttp\Exception\TooManyRedirectsException`` is thrown when too + many redirects are followed. This exception extends from extends from + ``GuzzleHttp\Exception\RequestException``. +- A ``GuzzleHttp\Exception\AdapterException`` is thrown when an error occurs + in an HTTP adapter during a parallel request. This exception is only thrown + when using the ``sendAll()`` method of a client. + +All of the above exceptions extend from +``GuzzleHttp\Exception\TransferException``. diff --git a/core/vendor/guzzlehttp/guzzle/docs/requirements.txt b/core/vendor/guzzlehttp/guzzle/docs/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fe7a4eab46595174330774b6bd97d8e5dd964ef7 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/docs/requirements.txt @@ -0,0 +1,2 @@ +Sphinx>=1.2b1 +guzzle_sphinx_theme>=0.6.0 diff --git a/core/vendor/guzzlehttp/guzzle/docs/streams.rst b/core/vendor/guzzlehttp/guzzle/docs/streams.rst new file mode 100644 index 0000000000000000000000000000000000000000..c743e2c4bb4ff9d74d7dc70c7c2e428142cddbb0 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/docs/streams.rst @@ -0,0 +1,227 @@ +======= +Streams +======= + +Guzzle uses stream objects to represent request and response message bodies. +These stream objects allow you to work with various types of data all using a +common interface. + +HTTP messages consist of a start-line, headers, and a body. The body of an HTTP +message can be very small or extremely large. Attempting to represent the body +of a message as a string can easily consume more memory than intended because +the body must be stored completely in memory. Attempting to store the body of a +request or response in memory would preclude the use of that implementation from +being able to work with large message bodies. The StreamInterface is used in +order to hide the implementation details of where a stream of data is read from +or written to. + +Guzzle's StreamInterface exposes several methods that enable streams to be read +from, written to, and traversed effectively. + +Streams expose their capabilities using three methods: ``isReadable()``, +``isWritable()``, and ``isSeekable()``. These methods can be used by stream +collaborators to determine if a stream is capable of their requirements. + +Each stream instance has various capabilities: they can be read-only, +write-only, read-write, allow arbitrary random access (seeking forwards or +backwards to any location), or only allow sequential access (for example in the +case of a socket or pipe). + +Creating Streams +================ + +The best way to create a stream is using the +``GuzzleHttp\Stream\create()`` function. This function accepts strings, +resources returned from ``fopen()``, an object that implements +``__toString()``, and an object that implements +``GuzzleHttp\Stream\StreamInterface``. + +.. code-block:: php + + use GuzzleHttp\Stream; + + $stream = Stream\create('string data'); + echo $stream; + // string data + echo $stream->read(3); + // str + echo $stream->getContents(); + // ing data + var_export($stream->eof()); + // true + var_export($stream->tell()); + // 11 + +.. note:: + + You can use this ``create()`` function or use the static ``factory()`` + method of the ``GuzzleHttp\Stream\Stream`` class via + ``GuzzleHttp\Stream\Stream::factory()``. This static factory method has the + same method signature as the ``create()`` function. Actually, the static + factory method is just a convenience method for the ``create()`` function. + +Metadata Streams +================ + +Guzzle streams that implement ``GuzzleHttp\Stream\MetadataStreamInterface`` +expose stream metadata through the ``getMetadata()`` method. This method +provides the data you would retrieve when calling PHP's +`stream_get_meta_data() function <http://php.net/manual/en/function.stream-get-meta-data.php>`_. + +.. code-block:: php + + use GuzzleHttp\Stream; + + $resource = fopen('/path/to/file', 'r'); + $stream = Stream\create($resource); + echo $stream->getMetadata('uri'); + // /path/to/file + var_export($stream->isReadable()); + // true + var_export($stream->isWritable()); + // false + var_export($stream->isSeekable()); + // true + +.. note:: + + Streams created using ``GuzzleHttp\Stream\create()`` and + ``GuzzleHttp\Stream\Stream::factory()`` all implement + ``GuzzleHttp\Stream\MetadataStreamInterface``. + +Stream Decorators +================= + +With the small and focused interface, add custom functionality to streams is +very simple with stream decorators. Guzzle provides several built-in decorators +that provide additional stream functionality. + +CachingStream +------------- + +The CachingStream is used to allow seeking over previously read bytes on +non-seekable streams. This can be useful when transferring a non-seekable +entity body fails due to needing to rewind the stream (for example, resulting +from a redirect). Data that is read from the remote stream will be buffered in +a PHP temp stream so that previously read bytes are cached first in memory, +then on disk. + +.. code-block:: php + + use GuzzleHttp\Stream; + use GuzzleHttp\Stream\CachingStream; + + $original = Stream\create(fopen('http://www.google.com', 'r')); + $stream = new CachingStream($original); + + $stream->read(1024); + echo $stream->tell(); + // 1024 + + $stream->seek(0); + echo $stream->tell(); + // 0 + +LimitStream +----------- + +LimitStream can be used to read a subset or slice of an existing stream object. +This can be useful for breaking a large file into smaller pieces to be sent in +chunks (e.g. Amazon S3's multipart upload API). + +.. code-block:: php + + use GuzzleHttp\Stream; + use GuzzleHttp\Stream\LimitStream; + + $original = Stream\create(fopen('/tmp/test.txt', 'r+')); + echo $original->getSize(); + // >>> 1048576 + + // Limit the size of the body to 1024 bytes and start reading from byte 2048 + $stream = new LimitStream($original, 1024, 2048); + echo $stream->getSize(); + // >>> 1024 + echo $stream->tell(); + // >>> 0 + +NoSeekStream +------------ + +NoSeekStream wraps a stream and does not allow seeking. + +.. code-block:: php + + use GuzzleHttp\Stream; + use GuzzleHttp\Stream\LimitStream; + + $original = Stream\create('foo'); + $noSeek = new NoSeekStream($original); + + echo $noSeek->read(3); + // foo + var_export($noSeek->isSeekable()); + // false + $noSeek->seek(0); + var_export($noSeek->read(3)); + // NULL + +Creating Custom Decorators +-------------------------- + +Creating a stream decorator is very easy thanks to the +``GuzzleHttp\Stream\StreamDecoratorTrait``. This trait provides methods that +implement ``GuzzleHttp\Stream\StreamInterface`` by proxying to an underlying +stream. Just ``use`` the ``StreamDecoratorTrait`` and implement your custom +methods. + +For example, let's say we wanted to call a specific function each time the last +byte is read from a stream. This could be implemented by overriding the +``read()`` method. + +.. code-block:: php + + use GuzzleHttp\Stream\StreamDecoratorTrait; + + class EofCallbackStream implements StreamInterface, MetadataStreamInterface + { + use StreamDecoratorTrait; + + private $callback; + + public function __construct(StreamInterface $stream, callable $callback) + { + $this->stream = $stream; + $this->callback = $callback; + } + + public function read($length) + { + $result = $this->stream->read($length); + + // Invoke the callback when EOF is hit. + if ($this->eof()) { + call_user_func($this->callback); + } + + return $result; + } + } + +This decorator could be added to any existing stream and used like so: + +.. code-block:: php + + use GuzzleHttp\Stream; + + $original = Stream\create('foo'); + $eofStream = new EofCallbackStream($original, function () { + echo 'EOF!'; + }); + + $eofStream->read(2); + $eofStream->read(1); + // echoes "EOF!" + $eofStream->seek(0); + $eofStream->read(3); + // echoes "EOF!" diff --git a/core/vendor/guzzlehttp/guzzle/docs/testing.rst b/core/vendor/guzzlehttp/guzzle/docs/testing.rst new file mode 100644 index 0000000000000000000000000000000000000000..d67bd615c013fb8f95f5f897d9d74d01db9e4fe9 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/docs/testing.rst @@ -0,0 +1,236 @@ +====================== +Testing Guzzle Clients +====================== + +Guzzle provides several tools that will enable you to easily mock the HTTP +layer without needing to send requests over the internet. + +* Mock subscriber +* Mock adapter +* Node.js web server for integration testing + +Mock Subscriber +=============== + +When testing HTTP clients, you often need to simulate specific scenarios like +returning a successful response, returning an error, or returning specific +responses in a certain order. Because unit tests need to be predictable, easy +to bootstrap, and fast, hitting an actual remote API is a test smell. + +Guzzle provides a mock subscriber that can be attached to clients or requests +that allows you to queue up a list of responses to use rather than hitting a +remote API. + +.. code-block:: php + + use GuzzleHttp\Client; + use GuzzleHttp\Subscriber\Mock; + use GuzzleHttp\Message\Response; + + $client = new Client(); + + // Create a mock subscriber and queue two responses. + $mock = new Mock([ + new Response(200, ['X-Foo' => 'Bar']), // Use response object + "HTTP/1.1 202 OKr\nContent-Length: 0\r\n\r\n" // Use a response string + ]); + + // Add the mock subscriber to the client. + $client->getEmitter()->attach($mock); + // The first request is intercepted with the first response. + echo $client->get('/')->getStatus(); + //> 200 + // The second request is intercepted with the second response. + echo $client->get('/')->getStatus(); + //> 202 + +When no more responses are in the queue and a request is sent, an +``OutOfBoundsException`` is thrown. + +History Subscriber +================== + +When using things like the ``Mock`` subscriber, you often need to know if the +requests you expected to send were sent exactly as you intended. While the mock +subscriber responds with mocked responses, the ``GuzzleHttp\Subscriber\History`` +subscriber maintains a history of the requests that were sent by a client. + +.. code-block:: php + + use GuzzleHttp\Client; + use GuzzleHttp\Subscriber\History; + + $client = new Client(); + $history = new History(); + + // Add the history subscriber to the client. + $client->getEmitter()->attach($history); + + $client->get('http://httpbin.org/get'); + $client->head('http://httpbin.org/get'); + + // Count the number of transactions + echo count($history); + //> 2 + // Get the last request + $lastRequest = $history->getLastRequest(); + // Get the last response + $lastRequest = $history->getLastResponse(); + + // Iterate over the transactions that were sent + foreach ($history as $transaction) { + echo $transaction['request']->getMethod(); + //> GET, HEAD + echo $transaction['response']->getStatusCode(); + //> 200, 200 + } + +The history subscriber can also be printed, revealing the requests and +responses that were sent as a string, in order. + +.. code-block:: php + + echo $history; + +:: + + > GET /get HTTP/1.1 + Host: httpbin.org + User-Agent: Guzzle/4.0-dev curl/7.21.4 PHP/5.5.8 + + < HTTP/1.1 200 OK + Access-Control-Allow-Origin: * + Content-Type: application/json + Date: Tue, 25 Mar 2014 03:53:27 GMT + Server: gunicorn/0.17.4 + Content-Length: 270 + Connection: keep-alive + + { + "headers": { + "Connection": "close", + "X-Request-Id": "3d0f7d5c-c937-4394-8248-2b8e03fcccdb", + "User-Agent": "Guzzle/4.0-dev curl/7.21.4 PHP/5.5.8", + "Host": "httpbin.org" + }, + "origin": "76.104.247.1", + "args": {}, + "url": "http://httpbin.org/get" + } + + > HEAD /get HTTP/1.1 + Host: httpbin.org + User-Agent: Guzzle/4.0-dev curl/7.21.4 PHP/5.5.8 + + < HTTP/1.1 200 OK + Access-Control-Allow-Origin: * + Content-length: 270 + Content-Type: application/json + Date: Tue, 25 Mar 2014 03:53:27 GMT + Server: gunicorn/0.17.4 + Connection: keep-alive + +Mock Adapter +============ + +In addition to using the Mock subscriber, you can use the +``GuzzleHttp\Adapter\MockAdapter`` as the adapter of a client to return the +same response over and over or return the result of a callable function. + +.. code-block:: php + + use GuzzleHttp\Client; + use GuzzleHttp\Adapter\MockAdapter; + use GuzzleHttp\Adapter\TransactionInterface; + use GuzzleHttp\Message\Response; + + $mockAdapter = new MockAdapter(function (TransactionInterface $trans) { + // You have access to the request + $request = $trans->getRequest(); + // Return a response + return new Response(200); + }); + + $client = new Client(['adapter' => $mockAdapter]); + +Test Web Server +=============== + +Using mock responses is usually enough when testing a web service client. When +implementing custom :doc:`HTTP adapters <adapters>`, you'll need to send actual +HTTP requests in order to sufficiently test the adapter. However, a best +practice is to contact a local web server rather than a server over the +internet. + +- Tests are more reliable +- Tests do not require a network connection +- Tests have no external dependencies + +Using the test server +--------------------- + +Guzzle ships with a node.js test server that receives requests and returns +responses from a queue. The test server exposes a simple API that is used to +enqueue responses and inspect the requests that it has received. + +In order to use the web server, you'll need to manually require +``tests/Server.php``. Any operation on the ``Server`` object will ensure that +the server is running and wait until it is able to receive requets before +returning. + +.. code-block:: php + + // Require the test server (using something like this). + require __DIR__ . '/../vendor/guzzlehttp/guzzle/tests/Server.php'; + + use GuzzleHttp\Client; + use GuzzleHttp\Tests\Server; + + // Start the server and queue a response + Server::enqueue("HTTP/1.1 200 OK\r\n\Content-Length: 0r\n\r\n"); + + $client = new Client(['base_url' => Server::$url]); + echo $client->get('/foo')->getStatusCode(); + // 200 + +``GuzzleHttp\Tests\Server`` provides a static interface to the test server. You +can queue an HTTP response or an array of responses by calling +``Server::enqueue()``. This method accepts a string representing an HTTP +response message, a ``GuzzleHttp\Message\ResponseInterface``, or an array of +HTTP message strings / ``GuzzleHttp\Message\ResponseInterface`` objects. + +.. code-block:: php + + // Queue single response + Server::enqueue("HTTP/1.1 200 OK\r\n\Content-Length: 0r\n\r\n"); + + // Clear the queue and queue an array of responses + Server::enqueue([ + "HTTP/1.1 200 OK\r\n\Content-Length: 0r\n\r\n", + "HTTP/1.1 404 Not Found\r\n\Content-Length: 0r\n\r\n" + ]); + +When a response is queued on the test server, the test server will remove any +previously queued responses. As the server receives requests, queued responses +are dequeued and returned to the request. When the queue is empty, the server +will return a 500 response. + +You can inspect the requests that the server has retrieved by calling +``Server::received()``. This method accepts an optional ``$hydrate`` parameter +that specifies if you are retrieving an array of HTTP requests as strings or an +array of ``GuzzleHttp\Message\RequestInterface`` objects. + +.. code-block:: php + + foreach (Server::received() as $response) { + echo $response; + } + +You can clear the list of received requests from the web server using the +``Server::flush()`` method. + +.. code-block:: php + + Server::flush(); + echo count(Server::received()); + // 0 diff --git a/core/vendor/guzzlehttp/guzzle/phpunit.xml.dist b/core/vendor/guzzlehttp/guzzle/phpunit.xml.dist new file mode 100644 index 0000000000000000000000000000000000000000..d75d0ba878b148eb4ea4a9691bd10de79d4bca14 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/phpunit.xml.dist @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<phpunit bootstrap="./tests/bootstrap.php" + colors="true"> + <testsuites> + <testsuite> + <directory>tests</directory> + </testsuite> + </testsuites> + <filter> + <whitelist> + <directory suffix=".php">src</directory> + </whitelist> + </filter> +</phpunit> diff --git a/core/vendor/guzzlehttp/guzzle/src/Adapter/AdapterInterface.php b/core/vendor/guzzlehttp/guzzle/src/Adapter/AdapterInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..f3c035e2b46683f47f0278c5e51530b3aa311404 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Adapter/AdapterInterface.php @@ -0,0 +1,23 @@ +<?php + +namespace GuzzleHttp\Adapter; + +use GuzzleHttp\Message\ResponseInterface; + +/** + * Adapter interface used to transfer HTTP requests. + * + * @link http://docs.guzzlephp.org/en/guzzle4/adapters.html for a full + * explanation of adapters and their responsibilities. + */ +interface AdapterInterface +{ + /** + * Transfers an HTTP request and populates a response + * + * @param TransactionInterface $transaction Transaction abject to populate + * + * @return ResponseInterface + */ + public function send(TransactionInterface $transaction); +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Adapter/Curl/BatchContext.php b/core/vendor/guzzlehttp/guzzle/src/Adapter/Curl/BatchContext.php new file mode 100644 index 0000000000000000000000000000000000000000..f153198ab3ac300055bba1e066e84289533df6ce --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Adapter/Curl/BatchContext.php @@ -0,0 +1,158 @@ +<?php + +namespace GuzzleHttp\Adapter\Curl; + +use GuzzleHttp\Adapter\TransactionInterface; +use GuzzleHttp\Exception\AdapterException; + +/** + * Provides context for a Curl transaction, including active handles, + * pending transactions, and whether or not this is a batch or single + * transaction. + */ +class BatchContext +{ + /** @var resource Curl multi resource */ + private $multi; + + /** @var \SplObjectStorage Map of transactions to curl resources */ + private $handles; + + /** @var \Iterator Yields pending transactions */ + private $pending; + + /** @var bool Whether or not to throw transactions */ + private $throwsExceptions; + + /** + * @param resource $multiHandle Initialized curl_multi resource + * @param bool $throwsExceptions Whether or not exceptions are thrown + * @param \Iterator $pending Iterator yielding pending transactions + */ + public function __construct( + $multiHandle, + $throwsExceptions, + \Iterator $pending = null + ) { + $this->multi = $multiHandle; + $this->handles = new \SplObjectStorage(); + $this->throwsExceptions = $throwsExceptions; + $this->pending = $pending; + } + + /** + * Find a transaction for a given curl handle + * + * @param resource $handle Curl handle + * + * @return TransactionInterface + * @throws AdapterException if a transaction is not found + */ + public function findTransaction($handle) + { + foreach ($this->handles as $transaction) { + if ($this->handles[$transaction] === $handle) { + return $transaction; + } + } + + throw new AdapterException('No curl handle was found'); + } + + /** + * Returns true if there are any remaining pending transactions + * + * @return bool + */ + public function hasPending() + { + return $this->pending && $this->pending->valid(); + } + + /** + * Pop the next transaction from the transaction queue + * + * @return TransactionInterface|null + */ + public function nextPending() + { + if (!$this->hasPending()) { + return null; + } + + $current = $this->pending->current(); + $this->pending->next(); + + return $current; + } + + /** + * Checks if the batch is to throw exceptions on error + * + * @return bool + */ + public function throwsExceptions() + { + return $this->throwsExceptions; + } + + /** + * Get the curl_multi handle + * + * @return resource + */ + public function getMultiHandle() + { + return $this->multi; + } + + /** + * Add a transaction to the multi handle + * + * @param TransactionInterface $transaction Transaction to add + * @param resource $handle Resource to use with the handle + * + * @throws AdapterException If the handle is already registered + */ + public function addTransaction(TransactionInterface $transaction, $handle) + { + if (isset($this->handles[$transaction])) { + throw new AdapterException('Transaction already registered'); + } + + $code = curl_multi_add_handle($this->multi, $handle); + if ($code != CURLM_OK) { + CurlAdapter::throwMultiError($code); + } + + $this->handles[$transaction] = $handle; + } + + /** + * Remove a transaction and associated handle from the context + * + * @param TransactionInterface $transaction Transaction to remove + * + * @return array Returns the curl_getinfo array + * @throws AdapterException if the transaction is not found + */ + public function removeTransaction(TransactionInterface $transaction) + { + if (!isset($this->handles[$transaction])) { + throw new AdapterException('Transaction not registered'); + } + + $handle = $this->handles[$transaction]; + + $code = curl_multi_remove_handle($this->multi, $handle); + if ($code != CURLM_OK) { + CurlAdapter::throwMultiError($code); + } + + $info = curl_getinfo($handle); + curl_close($handle); + unset($this->handles[$transaction]); + + return $info; + } +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Adapter/Curl/CurlAdapter.php b/core/vendor/guzzlehttp/guzzle/src/Adapter/Curl/CurlAdapter.php new file mode 100644 index 0000000000000000000000000000000000000000..0f12035317157ba60d1f158a981a5c45b671f51d --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Adapter/Curl/CurlAdapter.php @@ -0,0 +1,142 @@ +<?php + +namespace GuzzleHttp\Adapter\Curl; + +use GuzzleHttp\Adapter\AdapterInterface; +use GuzzleHttp\Adapter\TransactionInterface; +use GuzzleHttp\Event\RequestEvents; +use GuzzleHttp\Exception\AdapterException; +use GuzzleHttp\Message\MessageFactoryInterface; + +/** + * HTTP adapter that uses cURL easy handles as a transport layer. + * + * Requires PHP 5.5+ + * + * When using the CurlAdapter, custom curl options can be specified as an + * associative array of curl option constants mapping to values in the + * **curl** key of a request's configuration options. + */ +class CurlAdapter implements AdapterInterface +{ + /** @var CurlFactory */ + private $curlFactory; + + /** @var MessageFactoryInterface */ + private $messageFactory; + + /** @var array Array of curl easy handles */ + private $handles = []; + + /** @var array Array of owned curl easy handles */ + private $ownedHandles = []; + + /** @var int Total number of idle handles to keep in cache */ + private $maxHandles; + + /** + * Accepts an associative array of options: + * + * - handle_factory: Optional callable factory used to create cURL handles. + * The callable is invoked with the following arguments: + * TransactionInterface, MessageFactoryInterface, and an optional cURL + * handle to modify. The factory method must then return a cURL resource. + * - max_handles: Maximum number of idle handles (defaults to 5). + * + * @param MessageFactoryInterface $messageFactory + * @param array $options Array of options to use with the adapter + */ + public function __construct( + MessageFactoryInterface $messageFactory, + array $options = [] + ) { + $this->handles = $this->ownedHandles = []; + $this->messageFactory = $messageFactory; + $this->curlFactory = isset($options['handle_factory']) + ? $options['handle_factory'] + : new CurlFactory(); + $this->maxHandles = isset($options['max_handles']) + ? $options['max_handles'] + : 5; + } + + public function __destruct() + { + foreach ($this->handles as $handle) { + if (is_resource($handle)) { + curl_close($handle); + } + } + } + + public function send(TransactionInterface $transaction) + { + RequestEvents::emitBefore($transaction); + if ($response = $transaction->getResponse()) { + return $response; + } + + $factory = $this->curlFactory; + $handle = $factory( + $transaction, + $this->messageFactory, + $this->checkoutEasyHandle() + ); + + curl_exec($handle); + $info = curl_getinfo($handle); + $info['curl_result'] = curl_errno($handle); + + if ($info['curl_result']) { + $this->handleError($transaction, $info, $handle); + } else { + $this->releaseEasyHandle($handle); + RequestEvents::emitComplete($transaction, $info); + } + + return $transaction->getResponse(); + } + + private function handleError( + TransactionInterface $transaction, + $info, + $handle + ) { + $error = curl_error($handle); + $this->releaseEasyHandle($handle); + RequestEvents::emitError( + $transaction, + new AdapterException("cURL error {$info['curl_result']}: {$error}"), + $info + ); + } + + private function checkoutEasyHandle() + { + // Find an unused handle in the cache + if (false !== ($key = array_search(false, $this->ownedHandles, true))) { + $this->ownedHandles[$key] = true; + return $this->handles[$key]; + } + + // Add a new handle + $handle = curl_init(); + $id = (int) $handle; + $this->handles[$id] = $handle; + $this->ownedHandles[$id] = true; + + return $handle; + } + + private function releaseEasyHandle($handle) + { + $id = (int) $handle; + if (count($this->ownedHandles) > $this->maxHandles) { + curl_close($this->handles[$id]); + unset($this->handles[$id], $this->ownedHandles[$id]); + } else { + curl_reset($handle); + $this->ownedHandles[$id] = false; + } + } +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Adapter/Curl/CurlFactory.php b/core/vendor/guzzlehttp/guzzle/src/Adapter/Curl/CurlFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..0af3d7566e581abd21a6a887148c775c33ab518e --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Adapter/Curl/CurlFactory.php @@ -0,0 +1,334 @@ +<?php + +namespace GuzzleHttp\Adapter\Curl; + +use GuzzleHttp\Adapter\TransactionInterface; +use GuzzleHttp\Message\MessageFactoryInterface; +use GuzzleHttp\Message\RequestInterface; +use GuzzleHttp\Stream; +use GuzzleHttp\Exception\AdapterException; + +/** + * Creates curl resources from a request and response object + */ +class CurlFactory +{ + /** + * Creates a cURL handle based on a transaction. + * + * @param TransactionInterface $transaction Holds a request and response + * @param MessageFactoryInterface $messageFactory Used to create responses + * @param null|resource $handle Optionally provide a curl handle to modify + * + * @return resource Returns a prepared cURL handle + * @throws AdapterException when an option cannot be applied + */ + public function __invoke( + TransactionInterface $transaction, + MessageFactoryInterface $messageFactory, + $handle = null + ) { + $request = $transaction->getRequest(); + $mediator = new RequestMediator($transaction, $messageFactory); + $options = $this->getDefaultOptions($request, $mediator); + $this->applyMethod($request, $options); + $this->applyTransferOptions($request, $mediator, $options); + $this->applyHeaders($request, $options); + unset($options['_headers']); + + // Add adapter options from the request's configuration options + if ($config = $request->getConfig()['curl']) { + $options = $this->applyCustomCurlOptions($config, $options); + } + + if (!$handle) { + $handle = curl_init(); + } + + curl_setopt_array($handle, $options); + + return $handle; + } + + protected function getDefaultOptions( + RequestInterface $request, + RequestMediator $mediator + ) { + $url = $request->getUrl(); + + // Strip fragment from URL. See: + // https://github.com/guzzle/guzzle/issues/453 + if (($pos = strpos($url, '#')) !== false) { + $url = substr($url, 0, $pos); + } + + $config = $request->getConfig(); + $options = array( + CURLOPT_URL => $url, + CURLOPT_CONNECTTIMEOUT => $config['connect_timeout'] ?: 150, + CURLOPT_RETURNTRANSFER => false, + CURLOPT_HEADER => false, + CURLOPT_WRITEFUNCTION => array($mediator, 'writeResponseBody'), + CURLOPT_HEADERFUNCTION => array($mediator, 'receiveResponseHeader'), + CURLOPT_READFUNCTION => array($mediator, 'readRequestBody'), + CURLOPT_HTTP_VERSION => $request->getProtocolVersion() === '1.0' + ? CURL_HTTP_VERSION_1_0 : CURL_HTTP_VERSION_1_1, + CURLOPT_SSL_VERIFYPEER => 1, + CURLOPT_SSL_VERIFYHOST => 2, + '_headers' => $request->getHeaders() + ); + + if (defined('CURLOPT_PROTOCOLS')) { + // Allow only HTTP and HTTPS protocols + $options[CURLOPT_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS; + } + + // Add CURLOPT_ENCODING if Accept-Encoding header is provided + if ($request->hasHeader('Accept-Encoding')) { + $options[CURLOPT_ENCODING] = $request->getHeader('Accept-Encoding'); + // Let cURL set the Accept-Encoding header. Without this change + // curl could add a duplicate value. + $this->removeHeader('Accept-Encoding', $options); + } + + return $options; + } + + private function applyMethod(RequestInterface $request, array &$options) + { + $method = $request->getMethod(); + if ($method == 'GET') { + $options[CURLOPT_HTTPGET] = true; + unset($options[CURLOPT_READFUNCTION]); + } elseif ($method == 'HEAD') { + $options[CURLOPT_NOBODY] = true; + unset($options[CURLOPT_WRITEFUNCTION], $options[CURLOPT_READFUNCTION]); + } else { + $options[CURLOPT_CUSTOMREQUEST] = $method; + if (!$request->getBody()) { + unset($options[CURLOPT_READFUNCTION]); + } else { + $this->applyBody($request, $options); + } + } + } + + private function applyBody(RequestInterface $request, array &$options) + { + if ($request->hasHeader('Content-Length')) { + $size = (int) $request->getHeader('Content-Length'); + } else { + $size = null; + } + + $request->getBody()->seek(0); + + // You can send the body as a string using curl's CURLOPT_POSTFIELDS + if (($size !== null && $size < 32768) || + isset($request->getConfig()['curl']['body_as_string']) + ) { + $options[CURLOPT_POSTFIELDS] = $request->getBody()->getContents(); + // Don't duplicate the Content-Length header + $this->removeHeader('Content-Length', $options); + $this->removeHeader('Transfer-Encoding', $options); + } else { + $options[CURLOPT_UPLOAD] = true; + // Let cURL handle setting the Content-Length header + if ($size !== null) { + $options[CURLOPT_INFILESIZE] = $size; + $this->removeHeader('Content-Length', $options); + } + } + + // If the Expect header is not present, prevent curl from adding it + if (!$request->hasHeader('Expect')) { + $options[CURLOPT_HTTPHEADER][] = 'Expect:'; + } + } + + private function applyHeaders(RequestInterface $request, array &$options) + { + foreach ($options['_headers'] as $name => $values) { + $options[CURLOPT_HTTPHEADER][] = $name . ': ' . implode(', ', $values); + } + + // Remove the Expect header if one was not set + if (!$request->hasHeader('Accept')) { + $options[CURLOPT_HTTPHEADER][] = 'Accept:'; + } + } + + private function applyTransferOptions( + RequestInterface $request, + RequestMediator $mediator, + array &$options + ) { + static $methods; + if (!$methods) { + $methods = array_flip(get_class_methods(__CLASS__)); + } + + foreach ($request->getConfig()->toArray() as $key => $value) { + $method = "add_{$key}"; + if (isset($methods[$method])) { + $this->{$method}($request, $mediator, $options, $value); + } + } + } + + private function add_debug( + RequestInterface $request, + RequestMediator $mediator, + &$options, + $value + ) { + if ($value) { + $options[CURLOPT_STDERR] = is_resource($value) ? $value : STDOUT; + $options[CURLOPT_VERBOSE] = true; + } + } + + private function add_proxy( + RequestInterface $request, + RequestMediator $mediator, + &$options, + $value + ) { + if (!is_array($value)) { + $options[CURLOPT_PROXY] = $value; + } else { + $scheme = $request->getScheme(); + if (isset($value[$scheme])) { + $options[CURLOPT_PROXY] = $value[$scheme]; + } + } + } + + private function add_timeout( + RequestInterface $request, + RequestMediator $mediator, + &$options, + $value + ) { + $options[CURLOPT_TIMEOUT_MS] = $value * 1000; + } + + private function add_connect_timeout( + RequestInterface $request, + RequestMediator $mediator, + &$options, + $value + ) { + $options[CURLOPT_CONNECTTIMEOUT_MS] = $value * 1000; + } + + private function add_verify( + RequestInterface $request, + RequestMediator $mediator, + &$options, + $value + ) { + if ($value === false) { + unset($options[CURLOPT_CAINFO]); + $options[CURLOPT_SSL_VERIFYHOST] = 0; + $options[CURLOPT_SSL_VERIFYPEER] = false; + } elseif ($value === true || is_string($value)) { + $options[CURLOPT_SSL_VERIFYHOST] = 2; + $options[CURLOPT_SSL_VERIFYPEER] = true; + if ($value !== true) { + if (!file_exists($value)) { + throw new AdapterException('SSL certificate authority file' + . " not found: {$value}"); + } + $options[CURLOPT_CAINFO] = $value; + } + } + } + + private function add_cert( + RequestInterface $request, + RequestMediator $mediator, + &$options, + $value + ) { + if (!file_exists($value)) { + throw new AdapterException("SSL certificate not found: {$value}"); + } + + $options[CURLOPT_SSLCERT] = $value; + } + + private function add_ssl_key( + RequestInterface $request, + RequestMediator $mediator, + &$options, + $value + ) { + if (is_array($value)) { + $options[CURLOPT_SSLKEYPASSWD] = $value[1]; + $value = $value[0]; + } + + if (!file_exists($value)) { + throw new AdapterException("SSL private key not found: {$value}"); + } + + $options[CURLOPT_SSLKEY] = $value; + } + + private function add_save_to( + RequestInterface $request, + RequestMediator $mediator, + &$options, + $value + ) { + $mediator->setResponseBody(is_string($value) + ? Stream\create(fopen($value, 'w')) + : Stream\create($value)); + } + + /** + * Takes an array of curl options specified in the 'curl' option of a + * request's configuration array and maps them to CURLOPT_* options. + * + * This method is only called when a request has a 'curl' config setting. + * Array key strings that start with CURL that have a matching constant + * value will be automatically converted to the matching constant. + * + * @param array $config Configuration array of custom curl option + * @param array $options Array of existing curl options + * + * @return array Returns a new array of curl options + */ + private function applyCustomCurlOptions(array $config, array $options) + { + unset($config['body_as_string']); + $curlOptions = []; + + // Map curl constant strings to defined values + foreach ($config as $key => $value) { + if (defined($key) && substr($key, 0, 4) === 'CURL') { + $key = constant($key); + } + $curlOptions[$key] = $value; + } + + return $curlOptions + $options; + } + + /** + * Remove a header from the options array + * + * @param string $name Case-insensitive header to remove + * @param array $options Array of options to modify + */ + private function removeHeader($name, array &$options) + { + foreach (array_keys($options['_headers']) as $key) { + if (!strcasecmp($key, $name)) { + unset($options['_headers'][$key]); + return; + } + } + } +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Adapter/Curl/MultiAdapter.php b/core/vendor/guzzlehttp/guzzle/src/Adapter/Curl/MultiAdapter.php new file mode 100644 index 0000000000000000000000000000000000000000..90ac98992f9224d4ff9fb20b601191810d98f8ee --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Adapter/Curl/MultiAdapter.php @@ -0,0 +1,285 @@ +<?php + +namespace GuzzleHttp\Adapter\Curl; + +use GuzzleHttp\Adapter\AdapterInterface; +use GuzzleHttp\Adapter\ParallelAdapterInterface; +use GuzzleHttp\Adapter\TransactionInterface; +use GuzzleHttp\Event\RequestEvents; +use GuzzleHttp\Exception\AdapterException; +use GuzzleHttp\Exception\RequestException; +use GuzzleHttp\Message\MessageFactoryInterface; + +/** + * HTTP adapter that uses cURL multi as a transport layer + * + * When using the CurlAdapter, custom curl options can be specified as an + * associative array of curl option constants mapping to values in the + * **curl** key of a request's configuration options. + * + * In addition to being able to supply configuration options via the curl + * request config, you can also specify the select_timeout variable using the + * `GUZZLE_CURL_SELECT_TIMEOUT` environment variable. + */ +class MultiAdapter implements AdapterInterface, ParallelAdapterInterface +{ + const ERROR_STR = 'See http://curl.haxx.se/libcurl/c/libcurl-errors.html for an explanation of cURL errors'; + const ENV_SELECT_TIMEOUT = 'GUZZLE_CURL_SELECT_TIMEOUT'; + + /** @var CurlFactory */ + private $curlFactory; + + /** @var MessageFactoryInterface */ + private $messageFactory; + + /** @var array Array of curl multi handles */ + private $multiHandles = []; + + /** @var array Array of curl multi handles */ + private $multiOwned = []; + + /** @var double */ + private $selectTimeout; + + /** + * Accepts an associative array of options: + * + * - handle_factory: Optional callable factory used to create cURL handles. + * The callable is invoked with the following arguments: + * TransactionInterface, MessageFactoryInterface, and an optional cURL + * handle to modify. The factory method must then return a cURL resource. + * - select_timeout: Specify a float in seconds to use for a + * curl_multi_select timeout. + * + * @param MessageFactoryInterface $messageFactory + * @param array $options Array of options to use with the adapter: + */ + public function __construct( + MessageFactoryInterface $messageFactory, + array $options = [] + ) { + $this->handles = new \SplObjectStorage(); + $this->messageFactory = $messageFactory; + $this->curlFactory = isset($options['handle_factory']) + ? $options['handle_factory'] + : new CurlFactory(); + + if (isset($options['select_timeout'])) { + $this->selectTimeout = $options['select_timeout']; + } elseif (isset($_SERVER[self::ENV_SELECT_TIMEOUT])) { + $this->selectTimeout = $_SERVER[self::ENV_SELECT_TIMEOUT]; + } else { + $this->selectTimeout = 1; + } + } + + public function __destruct() + { + foreach ($this->multiHandles as $handle) { + if (is_resource($handle)) { + curl_multi_close($handle); + } + } + } + + /** + * Throw an exception for a cURL multi response + * + * @param int $code Curl response code + * @throws AdapterException + */ + public static function throwMultiError($code) + { + $buffer = function_exists('curl_multi_strerror') + ? curl_multi_strerror($code) + : self::ERROR_STR; + + throw new AdapterException(sprintf('cURL error %s: %s', $code, $buffer)); + } + + public function send(TransactionInterface $transaction) + { + $context = new BatchContext($this->checkoutMultiHandle(), true); + $this->addHandle($transaction, $context); + $this->perform($context); + + return $transaction->getResponse(); + } + + public function sendAll(\Iterator $transactions, $parallel) + { + $context = new BatchContext( + $this->checkoutMultiHandle(), + false, + $transactions + ); + + foreach (new \LimitIterator($transactions, 0, $parallel) as $trans) { + $this->addHandle($trans, $context); + } + + $this->perform($context); + } + + private function perform(BatchContext $context) + { + // The first curl_multi_select often times out no matter what, but is + // usually required for fast transfers. + $active = false; + $multi = $context->getMultiHandle(); + + do { + while (($mrc = curl_multi_exec($multi, $active)) == CURLM_CALL_MULTI_PERFORM); + if ($mrc != CURLM_OK && $mrc != CURLM_CALL_MULTI_PERFORM) { + self::throwMultiError($mrc); + } + // Need to check if there are pending transactions before processing + // them so that we don't bail from the loop too early. + $pending = $context->hasPending(); + $this->processMessages($context); + if ($active && curl_multi_select($multi, $this->selectTimeout) === -1) { + // Perform a usleep if a select returns -1. + // See: https://bugs.php.net/bug.php?id=61141 + usleep(250); + } + } while ($active || $pending); + + $this->releaseMultiHandle($multi); + } + + private function processMessages(BatchContext $context) + { + $multi = $context->getMultiHandle(); + + while ($done = curl_multi_info_read($multi)) { + $transaction = $context->findTransaction($done['handle']); + $this->processResponse($transaction, $done, $context); + // Add the next transaction if there are more in the queue + if ($next = $context->nextPending()) { + $this->addHandle($next, $context); + } + } + } + + private function processResponse( + TransactionInterface $transaction, + array $curl, + BatchContext $context + ) { + $info = $context->removeTransaction($transaction); + + try { + if (!$this->isCurlException($transaction, $curl, $context, $info)) { + RequestEvents::emitComplete($transaction, $info); + } + } catch (RequestException $e) { + $this->throwException($e, $context); + } + } + + private function addHandle( + TransactionInterface $transaction, + BatchContext $context + ) { + try { + RequestEvents::emitBefore($transaction); + // Only transfer if the request was not intercepted + if (!$transaction->getResponse()) { + $factory = $this->curlFactory; + $context->addTransaction( + $transaction, + $factory($transaction, $this->messageFactory) + ); + } + } catch (RequestException $e) { + $this->throwException($e, $context); + } + } + + private function isCurlException( + TransactionInterface $transaction, + array $curl, + BatchContext $context, + array $info + ) { + if (CURLM_OK == $curl['result'] || + CURLM_CALL_MULTI_PERFORM == $curl['result'] + ) { + return false; + } + + $request = $transaction->getRequest(); + try { + // Send curl stats along if they are available + $stats = ['curl_result' => $curl['result']] + $info; + RequestEvents::emitError( + $transaction, + new RequestException( + sprintf( + '[curl] (#%s) %s [url] %s', + $curl['result'], + function_exists('curl_strerror') + ? curl_strerror($curl['result']) + : self::ERROR_STR, + $request->getUrl() + ), + $request + ), + $stats + ); + } catch (RequestException $e) { + $this->throwException($e, $context); + } + + return true; + } + + private function throwException(RequestException $e, BatchContext $context) + { + if ($context->throwsExceptions()) { + $this->releaseMultiHandle($context->getMultiHandle()); + throw $e; + } + } + + /** + * Returns a curl_multi handle from the cache or creates a new one + * + * @return resource + */ + private function checkoutMultiHandle() + { + // Find an unused handle in the cache + $key = array_search(false, $this->multiOwned, true); + if (false !== $key) { + $this->multiOwned[$key] = true; + return $this->multiHandles[$key]; + } + + // Add a new handle + $handle = curl_multi_init(); + $id = (int) $handle; + $this->multiHandles[$id] = $handle; + $this->multiOwned[$id] = true; + + return $handle; + } + + /** + * Releases a curl_multi handle back into the cache and removes excess cache + * + * @param resource $handle Curl multi handle to remove + */ + private function releaseMultiHandle($handle) + { + $id = (int) $handle; + + if (count($this->multiHandles) <= 3) { + $this->multiOwned[$id] = false; + } else { + // Prune excessive handles + curl_multi_close($this->multiHandles[$id]); + unset($this->multiHandles[$id], $this->multiOwned[$id]); + } + } +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Adapter/Curl/RequestMediator.php b/core/vendor/guzzlehttp/guzzle/src/Adapter/Curl/RequestMediator.php new file mode 100644 index 0000000000000000000000000000000000000000..19cbcfd090cbf9211f6d393f4d8e8ced5d264eb7 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Adapter/Curl/RequestMediator.php @@ -0,0 +1,130 @@ +<?php + +namespace GuzzleHttp\Adapter\Curl; + +use GuzzleHttp\Adapter\TransactionInterface; +use GuzzleHttp\Event\RequestEvents; +use GuzzleHttp\Message\MessageFactoryInterface; +use GuzzleHttp\Stream\Stream; +use GuzzleHttp\Stream\StreamInterface; + +/** + * Mediator between curl handles and request objects + */ +class RequestMediator +{ + /** @var TransactionInterface */ + private $transaction; + /** @var MessageFactoryInterface */ + private $messageFactory; + private $statusCode; + private $reasonPhrase; + private $body; + private $protocolVersion; + private $headers; + + /** + * @param TransactionInterface $transaction Transaction to populate + * @param MessageFactoryInterface $messageFactory Creates responses + */ + public function __construct( + TransactionInterface $transaction, + MessageFactoryInterface $messageFactory + ) { + $this->transaction = $transaction; + $this->messageFactory = $messageFactory; + } + + /** + * Set the body that will hold the response body + * + * @param StreamInterface $body Response body + */ + public function setResponseBody(StreamInterface $body = null) + { + $this->body = $body; + } + + /** + * Receive a response header from curl + * + * @param resource $curl Curl handle + * @param string $header Received header + * + * @return int + */ + public function receiveResponseHeader($curl, $header) + { + static $normalize = ["\r", "\n"]; + $length = strlen($header); + $header = str_replace($normalize, '', $header); + + if (strpos($header, 'HTTP/') === 0) { + $startLine = explode(' ', $header, 3); + // Only download the body to a target body when a successful + // response is received. + if ($startLine[1][0] != '2') { + $this->body = null; + } + $this->statusCode = $startLine[1]; + $this->reasonPhrase = isset($startLine[2]) ? $startLine[2] : null; + $this->protocolVersion = substr($startLine[0], -3); + $this->headers = []; + } elseif ($pos = strpos($header, ':')) { + $this->headers[substr($header, 0, $pos)][] = substr($header, $pos + 1); + } elseif ($header == '' && $this->statusCode >= 200) { + $response = $this->messageFactory->createResponse( + $this->statusCode, + $this->headers, + $this->body, + [ + 'protocol_version' => $this->protocolVersion, + 'reason_phrase' => $this->reasonPhrase + ] + ); + $this->headers = $this->body = null; + $this->transaction->setResponse($response); + // Allows events to react before downloading any of the body + RequestEvents::emitHeaders($this->transaction); + } + + return $length; + } + + /** + * Write data to the response body of a request + * + * @param resource $curl + * @param string $write + * + * @return int + */ + public function writeResponseBody($curl, $write) + { + if (!($response = $this->transaction->getResponse())) { + return 0; + } + + // Add a default body on the response if one was not found + if (!($body = $response->getBody())) { + $body = new Stream(fopen('php://temp', 'r+')); + $response->setBody($body); + } + + return $body->write($write); + } + + /** + * Read data from the request body and send it to curl + * + * @param resource $ch Curl handle + * @param resource $fd File descriptor + * @param int $length Amount of data to read + * + * @return string + */ + public function readRequestBody($ch, $fd, $length) + { + return (string) $this->transaction->getRequest()->getBody()->read($length); + } +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Adapter/FakeParallelAdapter.php b/core/vendor/guzzlehttp/guzzle/src/Adapter/FakeParallelAdapter.php new file mode 100644 index 0000000000000000000000000000000000000000..302656837f1956a9181780d9ea319e753fe8eb30 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Adapter/FakeParallelAdapter.php @@ -0,0 +1,34 @@ +<?php + +namespace GuzzleHttp\Adapter; + +use GuzzleHttp\Exception\RequestException; + +/** + * Decorates a regular AdapterInterface object and creates a + * ParallelAdapterInterface object that sends multiple HTTP requests serially. + */ +class FakeParallelAdapter implements ParallelAdapterInterface +{ + /** @var AdapterInterface */ + private $adapter; + + /** + * @param AdapterInterface $adapter Adapter used to send requests + */ + public function __construct(AdapterInterface $adapter) + { + $this->adapter = $adapter; + } + + public function sendAll(\Iterator $transactions, $parallel) + { + foreach ($transactions as $transaction) { + try { + $this->adapter->send($transaction); + } catch (RequestException $e) { + // no op for batch transaction + } + } + } +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Adapter/MockAdapter.php b/core/vendor/guzzlehttp/guzzle/src/Adapter/MockAdapter.php new file mode 100644 index 0000000000000000000000000000000000000000..3e8020e69842da49d331b8b5c68396b681b55fa7 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Adapter/MockAdapter.php @@ -0,0 +1,60 @@ +<?php + +namespace GuzzleHttp\Adapter; + +use GuzzleHttp\Event\RequestEvents; +use GuzzleHttp\Message\ResponseInterface; + +/** + * Adapter that can be used to associate mock responses with a transaction + * while still emulating the event workflow of real adapters. + */ +class MockAdapter implements AdapterInterface +{ + private $response; + + /** + * @param ResponseInterface|callable $response Response to serve or function + * to invoke that handles a transaction + */ + public function __construct($response = null) + { + $this->setResponse($response); + } + + /** + * Set the response that will be served by the adapter + * + * @param ResponseInterface|callable $response Response to serve or + * function to invoke that handles a transaction + */ + public function setResponse($response) + { + $this->response = $response; + } + + public function send(TransactionInterface $transaction) + { + RequestEvents::emitBefore($transaction); + if (!$transaction->getResponse()) { + + // Read the request body if it is present + if ($transaction->getRequest()->getBody()) { + $transaction->getRequest()->getBody()->__toString(); + } + + $response = is_callable($this->response) + ? call_user_func($this->response, $transaction) + : $this->response; + if (!$response instanceof ResponseInterface) { + throw new \RuntimeException('Invalid mocked response'); + } + + $transaction->setResponse($response); + RequestEvents::emitHeaders($transaction); + RequestEvents::emitComplete($transaction); + } + + return $transaction->getResponse(); + } +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Adapter/ParallelAdapterInterface.php b/core/vendor/guzzlehttp/guzzle/src/Adapter/ParallelAdapterInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..79a25b7e0dc3a6591f1a15d080f8e489489ab689 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Adapter/ParallelAdapterInterface.php @@ -0,0 +1,23 @@ +<?php + +namespace GuzzleHttp\Adapter; + +/** + * Adapter interface used to transfer multiple HTTP requests in parallel. + * + * Parallel adapters follow the same rules as AdapterInterface except that + * RequestExceptions are never thrown in a parallel transfer and parallel + * adapters do not return responses. + */ +interface ParallelAdapterInterface +{ + /** + * Transfers multiple HTTP requests in parallel. + * + * RequestExceptions MUST not be thrown from a parallel transfer. + * + * @param \Iterator $transactions Iterator that yields TransactionInterface + * @param int $parallel Max number of requests to send in parallel + */ + public function sendAll(\Iterator $transactions, $parallel); +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Adapter/StreamAdapter.php b/core/vendor/guzzlehttp/guzzle/src/Adapter/StreamAdapter.php new file mode 100644 index 0000000000000000000000000000000000000000..d5a45d04409b426d22dc7a22acfe3e51be42e985 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Adapter/StreamAdapter.php @@ -0,0 +1,345 @@ +<?php + +namespace GuzzleHttp\Adapter; + +use GuzzleHttp\Event\RequestEvents; +use GuzzleHttp\Exception\AdapterException; +use GuzzleHttp\Exception\RequestException; +use GuzzleHttp\Message\MessageFactoryInterface; +use GuzzleHttp\Message\RequestInterface; +use GuzzleHttp\Stream; + +/** + * HTTP adapter that uses PHP's HTTP stream wrapper. + * + * When using the StreamAdapter, custom stream context options can be specified + * using the **stream_context** option in a request's **config** option. The + * structure of the "stream_context" option is an associative array where each + * key is a transport name and each option is an associative array of options. + */ +class StreamAdapter implements AdapterInterface +{ + /** @var MessageFactoryInterface */ + private $messageFactory; + + /** + * @param MessageFactoryInterface $messageFactory + */ + public function __construct(MessageFactoryInterface $messageFactory) + { + $this->messageFactory = $messageFactory; + } + + public function send(TransactionInterface $transaction) + { + // HTTP/1.1 streams using the PHP stream wrapper require a + // Connection: close header. Setting here so that it is added before + // emitting the request.before_send event. + $request = $transaction->getRequest(); + if ($request->getProtocolVersion() == '1.1' && + !$request->hasHeader('Connection') + ) { + $transaction->getRequest()->setHeader('Connection', 'close'); + } + + RequestEvents::emitBefore($transaction); + if (!$transaction->getResponse()) { + $this->createResponse($transaction); + RequestEvents::emitComplete($transaction); + } + + return $transaction->getResponse(); + } + + private function createResponse(TransactionInterface $transaction) + { + $request = $transaction->getRequest(); + $stream = $this->createStream($request, $http_response_header); + + if (!$request->getConfig()['stream']) { + $stream = $this->getSaveToBody($request, $stream); + } + + // Track the response headers of the request + $this->createResponseObject($http_response_header, $transaction, $stream); + } + + /** + * Drain the steam into the destination stream + */ + private function getSaveToBody(RequestInterface $request, $stream) + { + if ($saveTo = $request->getConfig()['save_to']) { + // Stream the response into the destination stream + $saveTo = is_string($saveTo) + ? Stream\create(fopen($saveTo, 'r+')) + : Stream\create($saveTo); + } else { + // Stream into the default temp stream + $saveTo = Stream\create(); + } + + while (!feof($stream)) { + $saveTo->write(fread($stream, 8096)); + } + + fclose($stream); + $saveTo->seek(0); + + return $saveTo; + } + + private function createResponseObject( + array $headers, + TransactionInterface $transaction, + $stream + ) { + $parts = explode(' ', array_shift($headers), 3); + $options = ['protocol_version' => substr($parts[0], -3)]; + if (isset($parts[2])) { + $options['reason_phrase'] = $parts[2]; + } + + // Set the size on the stream if it was returned in the response + $responseHeaders = []; + foreach ($headers as $header) { + $headerParts = explode(':', $header, 2); + $responseHeaders[$headerParts[0]] = isset($headerParts[1]) + ? $headerParts[1] + : ''; + } + + $response = $this->messageFactory->createResponse( + $parts[1], + $responseHeaders, + $stream, + $options + ); + + $transaction->setResponse($response); + RequestEvents::emitHeaders($transaction); + + return $response; + } + + /** + * Create a resource and check to ensure it was created successfully + * + * @param callable $callback Callable that returns stream resource + * @param RequestInterface $request Request used when throwing exceptions + * @param array $options Options used when throwing exceptions + * + * @return resource + * @throws RequestException on error + */ + private function createResource(callable $callback, RequestInterface $request, $options) + { + // Turn off error reporting while we try to initiate the request + $level = error_reporting(0); + $resource = call_user_func($callback); + error_reporting($level); + + // If the resource could not be created, then grab the last error and + // throw an exception. + if (!is_resource($resource)) { + $message = 'Error creating resource. [url] ' . $request->getUrl() . ' '; + if (isset($options['http']['proxy'])) { + $message .= "[proxy] {$options['http']['proxy']} "; + } + foreach (error_get_last() as $key => $value) { + $message .= "[{$key}] {$value} "; + } + throw new RequestException(trim($message), $request); + } + + return $resource; + } + + /** + * Create the stream for the request with the context options. + * + * @param RequestInterface $request Request being sent + * @param mixed $http_response_header Populated by stream wrapper + * + * @return resource + */ + private function createStream( + RequestInterface $request, + &$http_response_header + ) { + static $methods; + if (!$methods) { + $methods = array_flip(get_class_methods(__CLASS__)); + } + + $params = []; + $options = $this->getDefaultOptions($request); + foreach ($request->getConfig()->toArray() as $key => $value) { + $method = "add_{$key}"; + if (isset($methods[$method])) { + $this->{$method}($request, $options, $value, $params); + } + } + + $this->applyCustomOptions($request, $options); + $context = $this->createStreamContext($request, $options, $params); + + return $this->createStreamResource( + $request, + $options, + $context, + $http_response_header + ); + } + + private function getDefaultOptions(RequestInterface $request) + { + $headers = ''; + foreach ($request->getHeaders() as $name => $values) { + $headers .= $name . ': ' . implode(', ', $values) . "\r\n"; + } + + return [ + 'http' => [ + 'method' => $request->getMethod(), + 'header' => trim($headers), + 'protocol_version' => $request->getProtocolVersion(), + 'ignore_errors' => true, + 'follow_location' => 0, + 'content' => (string) $request->getBody() + ] + ]; + } + + private function add_proxy(RequestInterface $request, &$options, $value, &$params) + { + if (!is_array($value)) { + $options['http']['proxy'] = $value; + } else { + $scheme = $request->getScheme(); + if (isset($value[$scheme])) { + $options['http']['proxy'] = $value[$scheme]; + } + } + } + + private function add_timeout(RequestInterface $request, &$options, $value, &$params) + { + $options['http']['timeout'] = $value; + } + + private function add_verify(RequestInterface $request, &$options, $value, &$params) + { + if ($value === true || is_string($value)) { + $options['http']['verify_peer'] = true; + if ($value !== true) { + if (!file_exists($value)) { + throw new \RuntimeException("SSL certificate authority file not found: {$value}"); + } + $options['http']['allow_self_signed'] = true; + $options['http']['cafile'] = $value; + } + } elseif ($value === false) { + $options['http']['verify_peer'] = false; + } + } + + private function add_cert(RequestInterface $request, &$options, $value, &$params) + { + if (is_array($value)) { + $options['http']['passphrase'] = $value[1]; + $value = $value[0]; + } + + if (!file_exists($value)) { + throw new \RuntimeException("SSL certificate not found: {$value}"); + } + + $options['http']['local_cert'] = $value; + } + + private function add_debug(RequestInterface $request, &$options, $value, &$params) + { + static $map = [ + STREAM_NOTIFY_CONNECT => 'CONNECT', + STREAM_NOTIFY_AUTH_REQUIRED => 'AUTH_REQUIRED', + STREAM_NOTIFY_AUTH_RESULT => 'AUTH_RESULT', + STREAM_NOTIFY_MIME_TYPE_IS => 'MIME_TYPE_IS', + STREAM_NOTIFY_FILE_SIZE_IS => 'FILE_SIZE_IS', + STREAM_NOTIFY_REDIRECTED => 'REDIRECTED', + STREAM_NOTIFY_PROGRESS => 'PROGRESS', + STREAM_NOTIFY_FAILURE => 'FAILURE', + STREAM_NOTIFY_COMPLETED => 'COMPLETED', + STREAM_NOTIFY_RESOLVE => 'RESOLVE' + ]; + + static $args = ['severity', 'message', 'message_code', + 'bytes_transferred', 'bytes_max']; + + if (!is_resource($value)) { + $value = fopen('php://output', 'w'); + } + + $params['notification'] = function () use ($request, $value, $map, $args) { + $passed = func_get_args(); + $code = array_shift($passed); + fprintf($value, '<%s> [%s] ', $request->getUrl(), $map[$code]); + foreach (array_filter($passed) as $i => $v) { + fwrite($value, $args[$i] . ': "' . $v . '" '); + } + fwrite($value, "\n"); + }; + } + + private function applyCustomOptions( + RequestInterface $request, + array &$options + ) { + // Overwrite any generated options with custom options + if ($custom = $request->getConfig()['stream_context']) { + if (!is_array($custom)) { + throw new AdapterException('stream_context must be an array'); + } + $options = array_replace_recursive($options, $custom); + } + } + + private function createStreamContext( + RequestInterface $request, + array $options, + array $params + ) { + return $this->createResource(function () use ( + $request, + $options, + $params + ) { + return stream_context_create($options, $params); + }, $request, $options); + } + + private function createStreamResource( + RequestInterface $request, + array $options, + $context, + &$http_response_header + ) { + $url = $request->getUrl(); + // Add automatic gzip decompression + if (strpos($request->getHeader('Accept-Encoding'), 'gzip') !== false) { + $url = 'compress.zlib://' . $url; + } + + return $this->createResource(function () use ( + $url, + &$http_response_header, + $context + ) { + if (false === strpos($url, 'http')) { + trigger_error("URL is invalid: {$url}", E_USER_WARNING); + return null; + } + return fopen($url, 'r', null, $context); + }, $request, $options); + } +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Adapter/StreamingProxyAdapter.php b/core/vendor/guzzlehttp/guzzle/src/Adapter/StreamingProxyAdapter.php new file mode 100644 index 0000000000000000000000000000000000000000..128eb1d1e12c5a31dadf64c77f1cd6ed4fe66c3b --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Adapter/StreamingProxyAdapter.php @@ -0,0 +1,36 @@ +<?php + +namespace GuzzleHttp\Adapter; + +/** + * Sends streaming requests to a streaming compatible adapter while sending all + * other requests to a default adapter. + * + * This, for example, could be useful for taking advantage of the performance + * benefits of the CurlAdapter while still supporting true streaming through + * the StreamAdapter. + */ +class StreamingProxyAdapter implements AdapterInterface +{ + private $defaultAdapter; + private $streamingAdapter; + + /** + * @param AdapterInterface $defaultAdapter Adapter used for non-streaming responses + * @param AdapterInterface $streamingAdapter Adapter used for streaming responses + */ + public function __construct( + AdapterInterface $defaultAdapter, + AdapterInterface $streamingAdapter + ) { + $this->defaultAdapter = $defaultAdapter; + $this->streamingAdapter = $streamingAdapter; + } + + public function send(TransactionInterface $transaction) + { + return $transaction->getRequest()->getConfig()['stream'] + ? $this->streamingAdapter->send($transaction) + : $this->defaultAdapter->send($transaction); + } +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Adapter/Transaction.php b/core/vendor/guzzlehttp/guzzle/src/Adapter/Transaction.php new file mode 100644 index 0000000000000000000000000000000000000000..74bb6b44234e0564c20fe2f557f7ffb0f182f3cd --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Adapter/Transaction.php @@ -0,0 +1,49 @@ +<?php + +namespace GuzzleHttp\Adapter; + +use GuzzleHttp\ClientInterface; +use GuzzleHttp\Message\RequestInterface; +use GuzzleHttp\Message\ResponseInterface; + +class Transaction implements TransactionInterface +{ + /** @var ClientInterface */ + private $client; + /** @var RequestInterface */ + private $request; + /** @var ResponseInterface */ + private $response; + + /** + * @param ClientInterface $client Client that is used to send the requests + * @param RequestInterface $request + */ + public function __construct( + ClientInterface $client, + RequestInterface $request + ) { + $this->client = $client; + $this->request = $request; + } + + public function getRequest() + { + return $this->request; + } + + public function getResponse() + { + return $this->response; + } + + public function setResponse(ResponseInterface $response) + { + $this->response = $response; + } + + public function getClient() + { + return $this->client; + } +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Adapter/TransactionInterface.php b/core/vendor/guzzlehttp/guzzle/src/Adapter/TransactionInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..b9bf50cbaccb276b488d2699f5e510e1b154e06a --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Adapter/TransactionInterface.php @@ -0,0 +1,35 @@ +<?php + +namespace GuzzleHttp\Adapter; + +use GuzzleHttp\ClientInterface; +use GuzzleHttp\Message\RequestInterface; +use GuzzleHttp\Message\ResponseInterface; + +/** + * Represents a transactions that consists of a request, response, and client + */ +interface TransactionInterface +{ + /** + * @return RequestInterface + */ + public function getRequest(); + + /** + * @return ResponseInterface|null + */ + public function getResponse(); + + /** + * Set a response on the transaction + * + * @param ResponseInterface $response Response to set + */ + public function setResponse(ResponseInterface $response); + + /** + * @return ClientInterface + */ + public function getClient(); +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Adapter/TransactionIterator.php b/core/vendor/guzzlehttp/guzzle/src/Adapter/TransactionIterator.php new file mode 100644 index 0000000000000000000000000000000000000000..ee4caad52675bcff22c37b9eee944b3f70b4436d --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Adapter/TransactionIterator.php @@ -0,0 +1,116 @@ +<?php + +namespace GuzzleHttp\Adapter; + +use GuzzleHttp\ClientInterface; +use GuzzleHttp\Message\RequestInterface; + +/** + * Converts a sequence of request objects into a transaction. + * @internal + */ +class TransactionIterator implements \Iterator +{ + /** @var \Iterator */ + private $source; + + /** @var ClientInterface */ + private $client; + + /** @var array of hashes containing 'name', 'fn', 'priority', and 'once' */ + private $eventListeners; + + public function __construct( + $source, ClientInterface $client, + array $options + ) { + $this->client = $client; + $this->configureEvents($options); + if ($source instanceof \Iterator) { + $this->source = $source; + } elseif (is_array($source)) { + $this->source = new \ArrayIterator($source); + } else { + throw new \InvalidArgumentException('Expected an Iterator or array'); + } + } + + public function current() + { + $request = $this->source->current(); + + if (!$request instanceof RequestInterface) { + throw new \RuntimeException('All must implement RequestInterface'); + } + + if ($this->eventListeners) { + $emitter = $request->getEmitter(); + foreach ($this->eventListeners as $ev) { + if ($ev['once']) { + $emitter->once($ev['name'], $ev['fn'], $ev['priority']); + } else { + $emitter->on($ev['name'], $ev['fn'], $ev['priority']); + } + } + } + + return new Transaction($this->client, $request); + } + + public function next() + { + $this->source->next(); + } + + public function key() + { + return $this->source->key(); + } + + public function valid() + { + return $this->source->valid(); + } + + public function rewind() {} + + private function configureEvents(array $options) + { + static $namedEvents = ['before', 'complete', 'error']; + + foreach ($namedEvents as $event) { + if (isset($options[$event])) { + if (is_callable($options[$event])) { + $this->eventListeners[] = [ + 'name' => $event, + 'fn' => $options[$event], + 'priority' => 0, + 'once' => false + ]; + } else { + $this->addEvent($event, $options[$event]); + } + } + } + } + + private function addEvent($eventName, $event) + { + static $default = ['priority' => 0, 'once' => false]; + + if (!is_array($event)) { + throw new \InvalidArgumentException('Each event listener must be a' + . ' callable or an array of associative arrays where each' + . ' associative array contains a "fn" key.'); + } + + if (isset($event['fn'])) { + $event['name'] = $eventName; + $this->eventListeners[] = $event + $default; + } else { + foreach ($event as $e) { + $this->addEvent($eventName, $e); + } + } + } +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Client.php b/core/vendor/guzzlehttp/guzzle/src/Client.php new file mode 100644 index 0000000000000000000000000000000000000000..617a38e9390c225293c5b9bb56bedf8006115922 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Client.php @@ -0,0 +1,361 @@ +<?php + +namespace GuzzleHttp; + +use GuzzleHttp\Adapter\Curl\MultiAdapter; +use GuzzleHttp\Event\HasEmitterTrait; +use GuzzleHttp\Adapter\FakeParallelAdapter; +use GuzzleHttp\Adapter\ParallelAdapterInterface; +use GuzzleHttp\Adapter\AdapterInterface; +use GuzzleHttp\Adapter\StreamAdapter; +use GuzzleHttp\Adapter\StreamingProxyAdapter; +use GuzzleHttp\Adapter\Curl\CurlAdapter; +use GuzzleHttp\Adapter\Transaction; +use GuzzleHttp\Adapter\TransactionIterator; +use GuzzleHttp\Exception\RequestException; +use GuzzleHttp\Message\MessageFactory; +use GuzzleHttp\Message\MessageFactoryInterface; +use GuzzleHttp\Message\RequestInterface; + +/** + * HTTP client + */ +class Client implements ClientInterface +{ + use HasEmitterTrait; + + const DEFAULT_CONCURRENCY = 25; + + /** @var MessageFactoryInterface Request factory used by the client */ + private $messageFactory; + + /** @var AdapterInterface */ + private $adapter; + + /** @var ParallelAdapterInterface */ + private $parallelAdapter; + + /** @var Url Base URL of the client */ + private $baseUrl; + + /** @var array Default request options */ + private $defaults; + + /** + * Clients accept an array of constructor parameters. + * + * Here's an example of creating a client using an URI template for the + * client's base_url and an array of default request options to apply + * to each request: + * + * $client = new Client([ + * 'base_url' => [ + * 'http://www.foo.com/{version}/', + * ['version' => '123'] + * ], + * 'defaults' => [ + * 'timeout' => 10, + * 'allow_redirects' => false, + * 'proxy' => '192.168.16.1:10' + * ] + * ]); + * + * @param array $config Client configuration settings + * - base_url: Base URL of the client that is merged into relative URLs. + * Can be a string or an array that contains a URI template followed + * by an associative array of expansion variables to inject into the + * URI template. + * - adapter: Adapter used to transfer requests + * - parallel_adapter: Adapter used to transfer requests in parallel + * - message_factory: Factory used to create request and response object + * - defaults: Default request options to apply to each request + */ + public function __construct(array $config = []) + { + $this->configureBaseUrl($config); + $this->configureDefaults($config); + $this->configureAdapter($config); + } + + /** + * Get the default User-Agent string to use with Guzzle + * + * @return string + */ + public static function getDefaultUserAgent() + { + static $defaultAgent = ''; + if (!$defaultAgent) { + $defaultAgent = 'Guzzle/' . self::VERSION; + if (extension_loaded('curl')) { + $defaultAgent .= ' curl/' . curl_version()['version']; + } + $defaultAgent .= ' PHP/' . PHP_VERSION; + } + + return $defaultAgent; + } + + public function __call($name, $arguments) + { + return \GuzzleHttp\deprecation_proxy( + $this, + $name, + $arguments, + ['getEventDispatcher' => 'getEmitter'] + ); + } + + public function getDefaultOption($keyOrPath = null) + { + return $keyOrPath === null + ? $this->defaults + : \GuzzleHttp\get_path($this->defaults, $keyOrPath); + } + + public function setDefaultOption($keyOrPath, $value) + { + \GuzzleHttp\set_path($this->defaults, $keyOrPath, $value); + } + + public function getBaseUrl() + { + return (string) $this->baseUrl; + } + + public function createRequest($method, $url = null, array $options = []) + { + // Merge in default options + $options = array_replace_recursive($this->defaults, $options); + + // Use a clone of the client's emitter + $options['config']['emitter'] = clone $this->getEmitter(); + + $request = $this->messageFactory->createRequest( + $method, + $url ? (string) $this->buildUrl($url) : (string) $this->baseUrl, + $options + ); + + return $request; + } + + public function get($url = null, $options = []) + { + return $this->send($this->createRequest('GET', $url, $options)); + } + + public function head($url = null, array $options = []) + { + return $this->send($this->createRequest('HEAD', $url, $options)); + } + + public function delete($url = null, array $options = []) + { + return $this->send($this->createRequest('DELETE', $url, $options)); + } + + public function put($url = null, array $options = []) + { + return $this->send($this->createRequest('PUT', $url, $options)); + } + + public function patch($url = null, array $options = []) + { + return $this->send($this->createRequest('PATCH', $url, $options)); + } + + public function post($url = null, array $options = []) + { + return $this->send($this->createRequest('POST', $url, $options)); + } + + public function options($url = null, array $options = []) + { + return $this->send($this->createRequest('OPTIONS', $url, $options)); + } + + public function send(RequestInterface $request) + { + $transaction = new Transaction($this, $request); + try { + if ($response = $this->adapter->send($transaction)) { + return $response; + } + throw new \LogicException('No response was associated with the transaction'); + } catch (RequestException $e) { + throw $e; + } catch (\Exception $e) { + // Wrap exceptions in a RequestException to adhere to the interface + throw new RequestException($e->getMessage(), $request, null, $e); + } + } + + public function sendAll($requests, array $options = []) + { + if (!($requests instanceof TransactionIterator)) { + $requests = new TransactionIterator($requests, $this, $options); + } + + $this->parallelAdapter->sendAll( + $requests, + isset($options['parallel']) + ? $options['parallel'] + : self::DEFAULT_CONCURRENCY + ); + } + + /** + * Get an array of default options to apply to the client + * + * @return array + */ + protected function getDefaultOptions() + { + $settings = [ + 'allow_redirects' => true, + 'exceptions' => true, + 'verify' => __DIR__ . '/cacert.pem' + ]; + + // Use the bundled cacert if it is a regular file, or set to true if + // using a phar file (because curL and the stream wrapper can't read + // cacerts from the phar stream wrapper). Favor the ini setting over + // the system's cacert. + if (substr(__FILE__, 0, 7) == 'phar://') { + $settings['verify'] = ini_get('openssl.cafile') ?: true; + } + + // Use the standard Linux HTTP_PROXY and HTTPS_PROXY if set + if (isset($_SERVER['HTTP_PROXY'])) { + $settings['proxy']['http'] = $_SERVER['HTTP_PROXY']; + } + + if (isset($_SERVER['HTTPS_PROXY'])) { + $settings['proxy']['https'] = $_SERVER['HTTPS_PROXY']; + } + + return $settings; + } + + /** + * Expand a URI template and inherit from the base URL if it's relative + * + * @param string|array $url URL or URI template to expand + * + * @return string + */ + private function buildUrl($url) + { + if (!is_array($url)) { + if (strpos($url, '://')) { + return (string) $url; + } + return (string) $this->baseUrl->combine($url); + } elseif (strpos($url[0], '://')) { + return \GuzzleHttp\uri_template($url[0], $url[1]); + } + + return (string) $this->baseUrl->combine( + \GuzzleHttp\uri_template($url[0], $url[1]) + ); + } + + /** + * Get a default parallel adapter to use based on the environment + * + * @return ParallelAdapterInterface|null + * @throws \RuntimeException + */ + private function getDefaultParallelAdapter() + { + return extension_loaded('curl') + ? new CurlAdapter($this->messageFactory) + : new FakeParallelAdapter($this->adapter); + } + + /** + * Create a default adapter to use based on the environment + * @throws \RuntimeException + */ + private function getDefaultAdapter() + { + if (extension_loaded('curl')) { + $this->parallelAdapter = new MultiAdapter($this->messageFactory); + $this->adapter = function_exists('curl_reset') + ? new CurlAdapter($this->messageFactory) + : $this->parallelAdapter; + if (ini_get('allow_url_fopen')) { + $this->adapter = new StreamingProxyAdapter( + $this->adapter, + new StreamAdapter($this->messageFactory) + ); + } + } elseif (ini_get('allow_url_fopen')) { + $this->adapter = new StreamAdapter($this->messageFactory); + } else { + throw new \RuntimeException('Guzzle requires cURL, the ' + . 'allow_url_fopen ini setting, or a custom HTTP adapter.'); + } + } + + private function configureBaseUrl(&$config) + { + if (!isset($config['base_url'])) { + $this->baseUrl = new Url('', ''); + } elseif (is_array($config['base_url'])) { + $this->baseUrl = Url::fromString( + \GuzzleHttp\uri_template( + $config['base_url'][0], + $config['base_url'][1] + ) + ); + $config['base_url'] = (string) $this->baseUrl; + } else { + $this->baseUrl = Url::fromString($config['base_url']); + } + } + + private function configureDefaults($config) + { + if (!isset($config['defaults'])) { + $this->defaults = $this->getDefaultOptions(); + } else { + $this->defaults = array_replace( + $this->getDefaultOptions(), + $config['defaults'] + ); + } + + // Add the default user-agent header + if (!isset($this->defaults['headers'])) { + $this->defaults['headers'] = [ + 'User-Agent' => static::getDefaultUserAgent() + ]; + } elseif (!isset(array_change_key_case($this->defaults['headers'])['user-agent'])) { + // Add the User-Agent header if one was not already set + $this->defaults['headers']['User-Agent'] = static::getDefaultUserAgent(); + } + } + + private function configureAdapter(&$config) + { + if (isset($config['message_factory'])) { + $this->messageFactory = $config['message_factory']; + } else { + $this->messageFactory = new MessageFactory(); + } + if (isset($config['adapter'])) { + $this->adapter = $config['adapter']; + } else { + $this->getDefaultAdapter(); + } + // If no parallel adapter was explicitly provided and one was not + // defaulted when creating the default adapter, then create one now. + if (isset($config['parallel_adapter'])) { + $this->parallelAdapter = $config['parallel_adapter']; + } elseif (!$this->parallelAdapter) { + $this->parallelAdapter = $this->getDefaultParallelAdapter(); + } + } +} diff --git a/core/vendor/guzzlehttp/guzzle/src/ClientInterface.php b/core/vendor/guzzlehttp/guzzle/src/ClientInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..60c6a2cecf99465b2bd2027753496d94af22e6ac --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/ClientInterface.php @@ -0,0 +1,179 @@ +<?php + +namespace GuzzleHttp; + +use GuzzleHttp\Event\HasEmitterInterface; +use GuzzleHttp\Message\RequestInterface; +use GuzzleHttp\Message\ResponseInterface; +use GuzzleHttp\Exception\RequestException; +use GuzzleHttp\Exception\AdapterException; + +/** + * Client interface for sending HTTP requests + */ +interface ClientInterface extends HasEmitterInterface +{ + const VERSION = '4.0.0'; + + /** + * Create and return a new {@see RequestInterface} object. + * + * Use an absolute path to override the base path of the client, or a + * relative path to append to the base path of the client. The URL can + * contain the query string as well. Use an array to provide a URL + * template and additional variables to use in the URL template expansion. + * + * @param string $method HTTP method + * @param string|array|Url $url URL or URI template + * @param array $options Array of request options to apply. + * + * @return RequestInterface + */ + public function createRequest($method, $url = null, array $options = []); + + /** + * Send a GET request + * + * @param string|array|Url $url URL or URI template + * @param array $options Array of request options to apply. + * + * @return ResponseInterface + * @throws RequestException When an error is encountered + */ + public function get($url = null, $options = []); + + /** + * Send a HEAD request + * + * @param string|array|Url $url URL or URI template + * @param array $options Array of request options to apply. + * + * @return ResponseInterface + * @throws RequestException When an error is encountered + */ + public function head($url = null, array $options = []); + + /** + * Send a DELETE request + * + * @param string|array|Url $url URL or URI template + * @param array $options Array of request options to apply. + * + * @return ResponseInterface + * @throws RequestException When an error is encountered + */ + public function delete($url = null, array $options = []); + + /** + * Send a PUT request + * + * @param string|array|Url $url URL or URI template + * @param array $options Array of request options to apply. + * + * @return ResponseInterface + * @throws RequestException When an error is encountered + */ + public function put($url = null, array $options = []); + + /** + * Send a PATCH request + * + * @param string|array|Url $url URL or URI template + * @param array $options Array of request options to apply. + * + * @return ResponseInterface + * @throws RequestException When an error is encountered + */ + public function patch($url = null, array $options = []); + + /** + * Send a POST request + * + * @param string|array|Url $url URL or URI template + * @param array $options Array of request options to apply. + * + * @return ResponseInterface + * @throws RequestException When an error is encountered + */ + public function post($url = null, array $options = []); + + /** + * Send an OPTIONS request + * + * @param string|array|Url $url URL or URI template + * @param array $options Array of request options to apply. + * + * @return ResponseInterface + * @throws RequestException When an error is encountered + */ + public function options($url = null, array $options = []); + + /** + * Sends a single request + * + * @param RequestInterface $request Request to send + * + * @return \GuzzleHttp\Message\ResponseInterface + * @throws \LogicException When the adapter does not populate a response + * @throws RequestException When an error is encountered + */ + public function send(RequestInterface $request); + + /** + * Sends multiple requests in parallel. + * + * Exceptions are not thrown for failed requests. Callers are expected to + * register an "error" option to handle request errors OR directly register + * an event handler for the "error" event of a request's + * event emitter. + * + * The option values for 'before', 'after', and 'error' can be a callable, + * an associative array containing event data, or an array of event data + * arrays. Event data arrays contain the following keys: + * + * - fn: callable to invoke that receives the event + * - priority: Optional event priority (defaults to 0) + * - once: Set to true so that the event is removed after it is triggered + * + * @param array|\Iterator $requests Requests to send in parallel + * @param array $options Associative array of options + * - parallel: (int) Maximum number of requests to send in parallel + * - before: (callable|array) Receives a BeforeEvent + * - after: (callable|array) Receives a CompleteEvent + * - error: (callable|array) Receives a ErrorEvent + * + * @throws AdapterException When an error occurs in the HTTP adapter. + */ + public function sendAll($requests, array $options = []); + + /** + * Get default request options of the client. + * + * @param string|null $keyOrPath The Path to a particular default request + * option to retrieve or pass null to retrieve all default request + * options. The syntax uses "/" to denote a path through nested PHP + * arrays. For example, "headers/content-type". + * + * @return mixed + */ + public function getDefaultOption($keyOrPath = null); + + /** + * Set a default request option on the client so that any request created + * by the client will use the provided default value unless overridden + * explicitly when creating a request. + * + * @param string|null $keyOrPath The Path to a particular configuration + * value to set. The syntax uses a path notation that allows you to + * specify nested configuration values (e.g., 'headers/content-type'). + * @param mixed $value Default request option value to set + */ + public function setDefaultOption($keyOrPath, $value); + + /** + * Get the base URL of the client. + * + * @return string Returns the base URL if present + */ + public function getBaseUrl(); +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Collection.php b/core/vendor/guzzlehttp/guzzle/src/Collection.php new file mode 100644 index 0000000000000000000000000000000000000000..bd66df64b00dcf839f35157d6eb5fa2951e3898c --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Collection.php @@ -0,0 +1,265 @@ +<?php + +namespace GuzzleHttp; + +/** + * Key value pair collection object + */ +class Collection implements + \ArrayAccess, + \IteratorAggregate, + \Countable, + ToArrayInterface +{ + use HasDataTrait; + + /** + * @param array $data Associative array of data to set + */ + public function __construct(array $data = []) + { + $this->data = $data; + } + + /** + * Create a new collection from an array, validate the keys, and add default + * values where missing + * + * @param array $config Configuration values to apply. + * @param array $defaults Default parameters + * @param array $required Required parameter names + * + * @return self + * @throws \InvalidArgumentException if a parameter is missing + */ + public static function fromConfig( + array $config = [], + array $defaults = [], + array $required = [] + ) { + $data = $config + $defaults; + + if ($missing = array_diff($required, array_keys($data))) { + throw new \InvalidArgumentException( + 'Config is missing the following keys: ' . + implode(', ', $missing)); + } + + return new self($data); + } + + /** + * Removes all key value pairs + * + * @return Collection + */ + public function clear() + { + $this->data = []; + + return $this; + } + + /** + * Get a specific key value. + * + * @param string $key Key to retrieve. + * + * @return mixed|null Value of the key or NULL + */ + public function get($key) + { + return isset($this->data[$key]) ? $this->data[$key] : null; + } + + /** + * Set a key value pair + * + * @param string $key Key to set + * @param mixed $value Value to set + * + * @return Collection Returns a reference to the object + */ + public function set($key, $value) + { + $this->data[$key] = $value; + + return $this; + } + + /** + * Add a value to a key. If a key of the same name has already been added, + * the key value will be converted into an array and the new value will be + * pushed to the end of the array. + * + * @param string $key Key to add + * @param mixed $value Value to add to the key + * + * @return Collection Returns a reference to the object. + */ + public function add($key, $value) + { + if (!array_key_exists($key, $this->data)) { + $this->data[$key] = $value; + } elseif (is_array($this->data[$key])) { + $this->data[$key][] = $value; + } else { + $this->data[$key] = array($this->data[$key], $value); + } + + return $this; + } + + /** + * Remove a specific key value pair + * + * @param string $key A key to remove + * + * @return Collection + */ + public function remove($key) + { + unset($this->data[$key]); + + return $this; + } + + /** + * Get all keys in the collection + * + * @return array + */ + public function getKeys() + { + return array_keys($this->data); + } + + /** + * Returns whether or not the specified key is present. + * + * @param string $key The key for which to check the existence. + * + * @return bool + */ + public function hasKey($key) + { + return array_key_exists($key, $this->data); + } + + /** + * Checks if any keys contains a certain value + * + * @param string $value Value to search for + * + * @return mixed Returns the key if the value was found FALSE if the value + * was not found. + */ + public function hasValue($value) + { + return array_search($value, $this->data, true); + } + + /** + * Replace the data of the object with the value of an array + * + * @param array $data Associative array of data + * + * @return Collection Returns a reference to the object + */ + public function replace(array $data) + { + $this->data = $data; + + return $this; + } + + /** + * Add and merge in a Collection or array of key value pair data. + * + * @param Collection|array $data Associative array of key value pair data + * + * @return Collection Returns a reference to the object. + */ + public function merge($data) + { + foreach ($data as $key => $value) { + $this->add($key, $value); + } + + return $this; + } + + /** + * Over write key value pairs in this collection with all of the data from + * an array or collection. + * + * @param array|\Traversable $data Values to override over this config + * + * @return self + */ + public function overwriteWith($data) + { + if (is_array($data)) { + $this->data = $data + $this->data; + } elseif ($data instanceof Collection) { + $this->data = $data->toArray() + $this->data; + } else { + foreach ($data as $key => $value) { + $this->data[$key] = $value; + } + } + + return $this; + } + + /** + * Returns a Collection containing all the elements of the collection after + * applying the callback function to each one. + * + * The callable should accept three arguments: + * - (string) $key + * - (string) $value + * - (array) $context + * + * The callable must return a the altered or unaltered value. + * + * @param callable $closure Map function to apply + * @param array $context Context to pass to the callable + * + * @return Collection + */ + public function map(callable $closure, array $context = []) + { + $collection = new static(); + foreach ($this as $key => $value) { + $collection[$key] = $closure($key, $value, $context); + } + + return $collection; + } + + /** + * Iterates over each key value pair in the collection passing them to the + * callable. If the callable returns true, the current value from input is + * returned into the result Collection. + * + * The callable must accept two arguments: + * - (string) $key + * - (string) $value + * + * @param callable $closure Evaluation function + * + * @return Collection + */ + public function filter(callable $closure) + { + $collection = new static(); + foreach ($this->data as $key => $value) { + if ($closure($key, $value)) { + $collection[$key] = $value; + } + } + + return $collection; + } +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php b/core/vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php new file mode 100644 index 0000000000000000000000000000000000000000..1b526fb9c542d32c19fb9b29b33db168c16586e0 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php @@ -0,0 +1,232 @@ +<?php + +namespace GuzzleHttp\Cookie; + +use GuzzleHttp\Message\RequestInterface; +use GuzzleHttp\Message\ResponseInterface; + +/** + * Cookie jar that stores cookies an an array + */ +class CookieJar implements CookieJarInterface +{ + /** @var SetCookie[] Loaded cookie data */ + private $cookies = []; + + /** @var bool */ + private $strictMode; + + /** + * @param bool $strictMode Set to true to throw exceptions when invalid + * cookies are added to the cookie jar. + */ + public function __construct($strictMode = false) + { + $this->strictMode = $strictMode; + } + + /** + * Create a new Cookie jar from an associative array and domain. + * + * @param array $cookies Cookies to create the jar from + * @param string $domain Domain to set the cookies to + * + * @return self + */ + public static function fromArray(array $cookies, $domain) + { + $cookieJar = new self(); + foreach ($cookies as $name => $value) { + $cookieJar->setCookie(new SetCookie([ + 'Domain' => $domain, + 'Name' => $name, + 'Value' => $value, + 'Discard' => true + ])); + } + + return $cookieJar; + } + + /** + * Quote the cookie value if it is not already quoted and it contains + * problematic characters. + * + * @param string $value Value that may or may not need to be quoted + * + * @return string + */ + public static function getCookieValue($value) + { + if (substr($value, 0, 1) !== '"' && + substr($value, -1, 1) !== '"' && + strpbrk($value, ';,') + ) { + $value = '"' . $value . '"'; + } + + return $value; + } + + public function clear($domain = null, $path = null, $name = null) + { + if (!$domain) { + $this->cookies = []; + return; + } elseif (!$path) { + $this->cookies = array_filter( + $this->cookies, + function (SetCookie $cookie) use ($path, $domain) { + return !$cookie->matchesDomain($domain); + } + ); + } elseif (!$name) { + $this->cookies = array_filter( + $this->cookies, + function (SetCookie $cookie) use ($path, $domain) { + return !($cookie->matchesPath($path) && + $cookie->matchesDomain($domain)); + } + ); + } else { + $this->cookies = array_filter( + $this->cookies, + function (SetCookie $cookie) use ($path, $domain, $name) { + return !($cookie->getName() == $name && + $cookie->matchesPath($path) && + $cookie->matchesDomain($domain)); + } + ); + } + } + + public function clearSessionCookies() + { + $this->cookies = array_filter( + $this->cookies, + function (SetCookie $cookie) { + return !$cookie->getDiscard() && $cookie->getExpires(); + } + ); + } + + public function setCookie(SetCookie $cookie) + { + // Only allow cookies with set and valid domain, name, value + $result = $cookie->validate(); + if ($result !== true) { + if ($this->strictMode) { + throw new \RuntimeException('Invalid cookie: ' . $result); + } else { + $this->removeCookieIfEmpty($cookie); + return false; + } + } + + // Resolve conflicts with previously set cookies + foreach ($this->cookies as $i => $c) { + + // Two cookies are identical, when their path, and domain are + // identical. + if ($c->getPath() != $cookie->getPath() || + $c->getDomain() != $cookie->getDomain() || + $c->getName() != $cookie->getName() + ) { + continue; + } + + // The previously set cookie is a discard cookie and this one is + // not so allow the new cookie to be set + if (!$cookie->getDiscard() && $c->getDiscard()) { + unset($this->cookies[$i]); + continue; + } + + // If the new cookie's expiration is further into the future, then + // replace the old cookie + if ($cookie->getExpires() > $c->getExpires()) { + unset($this->cookies[$i]); + continue; + } + + // If the value has changed, we better change it + if ($cookie->getValue() !== $c->getValue()) { + unset($this->cookies[$i]); + continue; + } + + // The cookie exists, so no need to continue + return false; + } + + $this->cookies[] = $cookie; + + return true; + } + + public function count() + { + return count($this->cookies); + } + + public function getIterator() + { + return new \ArrayIterator(array_values($this->cookies)); + } + + public function extractCookies( + RequestInterface $request, + ResponseInterface $response + ) { + if ($cookieHeader = $response->getHeader('Set-Cookie', true)) { + foreach ($cookieHeader as $cookie) { + $sc = SetCookie::fromString($cookie); + if (!$sc->getDomain()) { + $sc->setDomain($request->getHost()); + } + $this->setCookie($sc); + } + } + } + + public function addCookieHeader(RequestInterface $request) + { + $values = []; + $scheme = $request->getScheme(); + $host = $request->getHost(); + $path = $request->getPath(); + + foreach ($this->cookies as $cookie) { + if ($cookie->matchesPath($path) && + $cookie->matchesDomain($host) && + !$cookie->isExpired() && + (!$cookie->getSecure() || $scheme == 'https') + ) { + $values[] = $cookie->getName() . '=' + . self::getCookieValue($cookie->getValue()); + } + } + + if ($values) { + $request->setHeader('Cookie', implode(';', $values)); + } + } + + /** + * If a cookie already exists and the server asks to set it again with a + * null value, the cookie must be deleted. + * + * @param SetCookie $cookie + */ + private function removeCookieIfEmpty(SetCookie $cookie) + { + $cookieValue = $cookie->getValue(); + if ($cookieValue === null || $cookieValue === '') { + $this->clear( + $cookie->getDomain(), + $cookie->getPath(), + $cookie->getName() + ); + } + } +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php b/core/vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..360fbe3c3b710df13309ec7764e177ea464b8670 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php @@ -0,0 +1,76 @@ +<?php + +namespace GuzzleHttp\Cookie; + +use GuzzleHttp\Message\RequestInterface; +use GuzzleHttp\Message\ResponseInterface; + +/** + * Stores HTTP cookies. + * + * It extracts cookies from HTTP requests, and returns them in HTTP responses. + * CookieJarInterface instances automatically expire contained cookies when + * necessary. Subclasses are also responsible for storing and retrieving + * cookies from a file, database, etc... + * + * @link http://docs.python.org/2/library/cookielib.html Inspiration + */ +interface CookieJarInterface extends \Countable, \IteratorAggregate +{ + /** + * Add a Cookie header to a request. + * + * If no matching cookies are found in the cookie jar, then no Cookie + * header is added to the request. + * + * @param RequestInterface $request Request object to update + */ + public function addCookieHeader(RequestInterface $request); + + /** + * Extract cookies from an HTTP response and store them in the CookieJar. + * + * @param RequestInterface $request Request that was sent + * @param ResponseInterface $response Response that was received + */ + public function extractCookies( + RequestInterface $request, + ResponseInterface $response + ); + + /** + * Sets a cookie in the cookie jar. + * + * @param SetCookie $cookie Cookie to set. + * + * @return bool Returns true on success or false on failure + */ + public function setCookie(SetCookie $cookie); + + /** + * Remove cookies currently held in the cookie jar. + * + * Invoking this method without arguments will empty the whole cookie jar. + * If given a $domain argument only cookies belonging to that domain will + * be removed. If given a $domain and $path argument, cookies belonging to + * the specified path within that domain are removed. If given all three + * arguments, then the cookie with the specified name, path and domain is + * removed. + * + * @param string $domain Clears cookies matching a domain + * @param string $path Clears cookies matching a domain and path + * @param string $name Clears cookies matching a domain, path, and name + * + * @return CookieJarInterface + */ + public function clear($domain = null, $path = null, $name = null); + + /** + * Discard all sessions cookies. + * + * Removes cookies that don't have an expire field or a have a discard + * field set to true. To be called when the user agent shuts down according + * to RFC 2965. + */ + public function clearSessionCookies(); +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php b/core/vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php new file mode 100644 index 0000000000000000000000000000000000000000..7e7549f5a5a24f168a558ff54282d7403ea5c98d --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php @@ -0,0 +1,85 @@ +<?php + +namespace GuzzleHttp\Cookie; + +/** + * Persists non-session cookies using a JSON formatted file + */ +class FileCookieJar extends CookieJar +{ + /** @var string filename */ + private $filename; + + /** + * Create a new FileCookieJar object + * + * @param string $cookieFile File to store the cookie data + * + * @throws \RuntimeException if the file cannot be found or created + */ + public function __construct($cookieFile) + { + $this->filename = $cookieFile; + + if (file_exists($cookieFile)) { + $this->load($cookieFile); + } + } + + /** + * Saves the file when shutting down + */ + public function __destruct() + { + $this->save($this->filename); + } + + /** + * Saves the cookies to a file. + * + * @param string $filename File to save + * @throws \RuntimeException if the file cannot be found or created + */ + public function save($filename) + { + $json = []; + foreach ($this as $cookie) { + if ($cookie->getExpires() && !$cookie->getDiscard()) { + $json[] = $cookie->toArray(); + } + } + + if (false === file_put_contents($filename, json_encode($json))) { + // @codeCoverageIgnoreStart + throw new \RuntimeException("Unable to save file {$filename}"); + // @codeCoverageIgnoreEnd + } + } + + /** + * Load cookies from a JSON formatted file. + * + * Old cookies are kept unless overwritten by newly loaded ones. + * + * @param string $filename Cookie file to load. + * @throws \RuntimeException if the file cannot be loaded. + */ + public function load($filename) + { + $json = file_get_contents($filename); + if (false === $json) { + // @codeCoverageIgnoreStart + throw new \RuntimeException("Unable to load file {$filename}"); + // @codeCoverageIgnoreEnd + } + + $data = json_decode($json, true); + if (is_array($data)) { + foreach (json_decode($json, true) as $cookie) { + $this->setCookie(new SetCookie($cookie)); + } + } elseif (strlen($data)) { + throw new \RuntimeException("Invalid cookie file: {$filename}"); + } + } +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php b/core/vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php new file mode 100644 index 0000000000000000000000000000000000000000..76f6f8d15c989c7617def57159b100c4161bfe2a --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php @@ -0,0 +1,410 @@ +<?php + +namespace GuzzleHttp\Cookie; + +use GuzzleHttp\ToArrayInterface; + +/** + * Set-Cookie object + */ +class SetCookie implements ToArrayInterface +{ + /** @var array */ + private static $defaults = [ + 'Name' => null, + 'Value' => null, + 'Domain' => null, + 'Path' => '/', + 'Max-Age' => null, + 'Expires' => null, + 'Secure' => false, + 'Discard' => false, + 'HttpOnly' => false + ]; + + /** @var array Cookie data */ + private $data; + + /** + * Create a new SetCookie object from a string + * + * @param string $cookie Set-Cookie header string + * + * @return self + */ + public static function fromString($cookie) + { + // Create the default return array + $data = self::$defaults; + // Explode the cookie string using a series of semicolons + $pieces = array_filter(array_map('trim', explode(';', $cookie))); + // The name of the cookie (first kvp) must include an equal sign. + if (empty($pieces) || !strpos($pieces[0], '=')) { + return new self($data); + } + + // Add the cookie pieces into the parsed data array + foreach ($pieces as $part) { + + $cookieParts = explode('=', $part, 2); + $key = trim($cookieParts[0]); + $value = isset($cookieParts[1]) + ? trim($cookieParts[1], " \n\r\t\0\x0B\"") + : true; + + // Only check for non-cookies when cookies have been found + if (empty($data['Name'])) { + $data['Name'] = $key; + $data['Value'] = $value; + } else { + foreach (array_keys(self::$defaults) as $search) { + if (!strcasecmp($search, $key)) { + $data[$search] = $value; + continue 2; + } + } + $data[$key] = $value; + } + } + + return new self($data); + } + + /** + * @param array $data Array of cookie data provided by a Cookie parser + */ + public function __construct(array $data = []) + { + $this->data = array_replace(self::$defaults, $data); + // Extract the Expires value and turn it into a UNIX timestamp if needed + if (!$this->getExpires() && $this->getMaxAge()) { + // Calculate the Expires date + $this->setExpires(time() + $this->getMaxAge()); + } elseif ($this->getExpires() && !is_numeric($this->getExpires())) { + $this->setExpires($this->getExpires()); + } + } + + public function __toString() + { + $str = $this->data['Name'] . '=' . $this->data['Value'] . '; '; + foreach ($this->data as $k => $v) { + if ($k != 'Name' && $k != 'Value'&& $v !== null && $v !== false) { + if ($k == 'Expires') { + $str .= 'Expires=' . gmdate('D, d M Y H:i:s \G\M\T', $v) . '; '; + } else { + $str .= ($v === true ? $k : "{$k}={$v}") . '; '; + } + } + } + + return rtrim($str, '; '); + } + + public function toArray() + { + return $this->data; + } + + /** + * Get the cookie name + * + * @return string + */ + public function getName() + { + return $this->data['Name']; + } + + /** + * Set the cookie name + * + * @param string $name Cookie name + * + * @return self + */ + public function setName($name) + { + $this->data['Name'] = $name; + + return $this; + } + + /** + * Get the cookie value + * + * @return string + */ + public function getValue() + { + return $this->data['Value']; + } + + /** + * Set the cookie value + * + * @param string $value Cookie value + * + * @return self + */ + public function setValue($value) + { + $this->data['Value'] = $value; + + return $this; + } + + /** + * Get the domain + * + * @return string|null + */ + public function getDomain() + { + return $this->data['Domain']; + } + + /** + * Set the domain of the cookie + * + * @param string $domain + * + * @return self + */ + public function setDomain($domain) + { + $this->data['Domain'] = $domain; + + return $this; + } + + /** + * Get the path + * + * @return string + */ + public function getPath() + { + return $this->data['Path']; + } + + /** + * Set the path of the cookie + * + * @param string $path Path of the cookie + * + * @return self + */ + public function setPath($path) + { + $this->data['Path'] = $path; + + return $this; + } + + /** + * Maximum lifetime of the cookie in seconds + * + * @return int|null + */ + public function getMaxAge() + { + return $this->data['Max-Age']; + } + + /** + * Set the max-age of the cookie + * + * @param int $maxAge Max age of the cookie in seconds + * + * @return self + */ + public function setMaxAge($maxAge) + { + $this->data['Max-Age'] = $maxAge; + + return $this; + } + + /** + * The UNIX timestamp when the cookie Expires + * + * @return mixed + */ + public function getExpires() + { + return $this->data['Expires']; + } + + /** + * Set the unix timestamp for which the cookie will expire + * + * @param int $timestamp Unix timestamp + * + * @return self + */ + public function setExpires($timestamp) + { + $this->data['Expires'] = is_numeric($timestamp) + ? (int) $timestamp + : strtotime($timestamp); + + return $this; + } + + /** + * Get whether or not this is a secure cookie + * + * @return null|bool + */ + public function getSecure() + { + return $this->data['Secure']; + } + + /** + * Set whether or not the cookie is secure + * + * @param bool $secure Set to true or false if secure + * + * @return self + */ + public function setSecure($secure) + { + $this->data['Secure'] = $secure; + + return $this; + } + + /** + * Get whether or not this is a session cookie + * + * @return null|bool + */ + public function getDiscard() + { + return $this->data['Discard']; + } + + /** + * Set whether or not this is a session cookie + * + * @param bool $discard Set to true or false if this is a session cookie + * + * @return self + */ + public function setDiscard($discard) + { + $this->data['Discard'] = $discard; + + return $this; + } + + /** + * Get whether or not this is an HTTP only cookie + * + * @return bool + */ + public function getHttpOnly() + { + return $this->data['HttpOnly']; + } + + /** + * Set whether or not this is an HTTP only cookie + * + * @param bool $httpOnly Set to true or false if this is HTTP only + * + * @return self + */ + public function setHttpOnly($httpOnly) + { + $this->data['HttpOnly'] = $httpOnly; + + return $this; + } + + /** + * Check if the cookie matches a path value + * + * @param string $path Path to check against + * + * @return bool + */ + public function matchesPath($path) + { + return !$this->getPath() || 0 === stripos($path, $this->getPath()); + } + + /** + * Check if the cookie matches a domain value + * + * @param string $domain Domain to check against + * + * @return bool + */ + public function matchesDomain($domain) + { + // Remove the leading '.' as per spec in RFC 6265. + // http://tools.ietf.org/html/rfc6265#section-5.2.3 + $cookieDomain = ltrim($this->getDomain(), '.'); + + // Domain not set or exact match. + if (!$cookieDomain || !strcasecmp($domain, $cookieDomain)) { + return true; + } + + // Matching the subdomain according to RFC 6265. + // http://tools.ietf.org/html/rfc6265#section-5.1.3 + if (filter_var($domain, FILTER_VALIDATE_IP)) { + return false; + } + + return (bool) preg_match('/\.' . preg_quote($cookieDomain) . '$/i', $domain); + } + + /** + * Check if the cookie is expired + * + * @return bool + */ + public function isExpired() + { + return $this->getExpires() && time() > $this->getExpires(); + } + + /** + * Check if the cookie is valid according to RFC 6265 + * + * @return bool|string Returns true if valid or an error message if invalid + */ + public function validate() + { + // Names must not be empty, but can be 0 + $name = $this->getName(); + if (empty($name) && !is_numeric($name)) { + return 'The cookie name must not be empty'; + } + + // Check if any of the invalid characters are present in the cookie name + if (preg_match("/[=,; \t\r\n\013\014]/", $name)) { + return "Cookie name must not cannot invalid characters: =,; \\t\\r\\n\\013\\014"; + } + + // Value must not be empty, but can be 0 + $value = $this->getValue(); + if (empty($value) && !is_numeric($value)) { + return 'The cookie value must not be empty'; + } + + // Domains must not be empty, but can be 0 + // A "0" is not a valid internet domain, but may be used as server name + // in a private network. + $domain = $this->getDomain(); + if (empty($domain) && !is_numeric($domain)) { + return 'The cookie domain must not be empty'; + } + + return true; + } +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Event/AbstractEvent.php b/core/vendor/guzzlehttp/guzzle/src/Event/AbstractEvent.php new file mode 100644 index 0000000000000000000000000000000000000000..fa1453c84206fc035c9ce2324ec68fa59e686f33 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Event/AbstractEvent.php @@ -0,0 +1,21 @@ +<?php + +namespace GuzzleHttp\Event; + +/** + * Basic event class that can be extended. + */ +abstract class AbstractEvent implements EventInterface +{ + private $propagationStopped = false; + + public function isPropagationStopped() + { + return $this->propagationStopped; + } + + public function stopPropagation() + { + $this->propagationStopped = true; + } +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Event/AbstractRequestEvent.php b/core/vendor/guzzlehttp/guzzle/src/Event/AbstractRequestEvent.php new file mode 100644 index 0000000000000000000000000000000000000000..d188248ac6f495779cafe37bea3b18a086eb2e57 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Event/AbstractRequestEvent.php @@ -0,0 +1,49 @@ +<?php + +namespace GuzzleHttp\Event; + +use GuzzleHttp\Adapter\TransactionInterface; +use GuzzleHttp\ClientInterface; +use GuzzleHttp\Message\RequestInterface; + +abstract class AbstractRequestEvent extends AbstractEvent +{ + /** @var TransactionInterface */ + private $transaction; + + /** + * @param TransactionInterface $transaction + */ + public function __construct(TransactionInterface $transaction) + { + $this->transaction = $transaction; + } + + /** + * Get the client associated with the event + * + * @return ClientInterface + */ + public function getClient() + { + return $this->transaction->getClient(); + } + + /** + * Get the request object + * + * @return RequestInterface + */ + public function getRequest() + { + return $this->transaction->getRequest(); + } + + /** + * @return TransactionInterface + */ + protected function getTransaction() + { + return $this->transaction; + } +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Event/AbstractTransferEvent.php b/core/vendor/guzzlehttp/guzzle/src/Event/AbstractTransferEvent.php new file mode 100644 index 0000000000000000000000000000000000000000..f6b8f38d7239784d2ce50870f6c66349137ee64d --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Event/AbstractTransferEvent.php @@ -0,0 +1,83 @@ +<?php + +namespace GuzzleHttp\Event; + +use GuzzleHttp\Adapter\TransactionInterface; +use GuzzleHttp\Message\ResponseInterface; + +/** + * Event that contains transaction statistics (time over the wire, lookup time, + * etc). + * + * Adapters that create this event SHOULD add, at a minimum, the 'total_time' + * transfer statistic that measures the amount of time, in seconds, taken to + * complete a transfer for the current request/response cycle. Each event + * pertains to a single request/response transaction, NOT the entire + * transaction (e.g. redirects). + * + * Adapters that add transaction statistics SHOULD follow the same string + * attribute names that are provided by cURL listed at + * http://php.net/manual/en/function.curl-getinfo.php. However, not all + * adapters will have access to the advanced statistics provided by cURL. The + * most useful transfer statistics are as follows: + * + * - total_time: Total transaction time in seconds for last transfer + * - namelookup_time: Time in seconds until name resolving was complete + * - connect_time: Time in seconds it took to establish the connection + * - pretransfer_time: Time in seconds from start until just before file + * transfer begins. + * - starttransfer_time: Time in seconds until the first byte is about to be + * transferred. + * - speed_download: Average download speed + * - speed_upload: Average upload speed + */ +abstract class AbstractTransferEvent extends AbstractRequestEvent +{ + private $transferInfo; + + /** + * @param TransactionInterface $transaction Transaction + * @param array $transferInfo Transfer statistics + */ + public function __construct( + TransactionInterface $transaction, + $transferInfo = [] + ) { + parent::__construct($transaction); + $this->transferInfo = $transferInfo; + } + + /** + * Get all transfer information as an associative array if no $name + * argument is supplied, or gets a specific transfer statistic if + * a $name attribute is supplied (e.g., 'total_time'). + * + * @param string $name Name of the transfer stat to retrieve + * + * @return mixed|null|array + */ + public function getTransferInfo($name = null) + { + if (!$name) { + return $this->transferInfo; + } + + return isset($this->transferInfo[$name]) + ? $this->transferInfo[$name] + : null; + } + + /** + * Get the response + * + * @return ResponseInterface|null + */ + abstract public function getResponse(); + + /** + * Intercept the request and associate a response + * + * @param ResponseInterface $response Response to set + */ + abstract public function intercept(ResponseInterface $response); +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Event/BeforeEvent.php b/core/vendor/guzzlehttp/guzzle/src/Event/BeforeEvent.php new file mode 100644 index 0000000000000000000000000000000000000000..34a7811d6dce263594215f2e8213b91b285bdb4e --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Event/BeforeEvent.php @@ -0,0 +1,26 @@ +<?php + +namespace GuzzleHttp\Event; + +use GuzzleHttp\Message\ResponseInterface; + +/** + * Event object emitted before a request is sent. + * + * You may change the Response associated with the request using the + * intercept() method of the event. + */ +class BeforeEvent extends AbstractRequestEvent +{ + /** + * Intercept the request and associate a response + * + * @param ResponseInterface $response Response to set + */ + public function intercept(ResponseInterface $response) + { + $this->getTransaction()->setResponse($response); + $this->stopPropagation(); + RequestEvents::emitComplete($this->getTransaction()); + } +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Event/CompleteEvent.php b/core/vendor/guzzlehttp/guzzle/src/Event/CompleteEvent.php new file mode 100644 index 0000000000000000000000000000000000000000..344576001ae151b398c682c90804021970e4c156 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Event/CompleteEvent.php @@ -0,0 +1,35 @@ +<?php + +namespace GuzzleHttp\Event; + +use GuzzleHttp\Message\ResponseInterface; + +/** + * Event object emitted after a request has been completed. + * + * You may change the Response associated with the request using the + * intercept() method of the event. + */ +class CompleteEvent extends AbstractTransferEvent +{ + /** + * Intercept the request and associate a response + * + * @param ResponseInterface $response Response to set + */ + public function intercept(ResponseInterface $response) + { + $this->stopPropagation(); + $this->getTransaction()->setResponse($response); + } + + /** + * Get the response of the request + * + * @return ResponseInterface + */ + public function getResponse() + { + return $this->getTransaction()->getResponse(); + } +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Event/Emitter.php b/core/vendor/guzzlehttp/guzzle/src/Event/Emitter.php new file mode 100644 index 0000000000000000000000000000000000000000..49172bcb50bad513c51efe10f57dc70f589c45b3 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Event/Emitter.php @@ -0,0 +1,147 @@ +<?php + +namespace GuzzleHttp\Event; + +/** + * Guzzle event emitter. + * + * Some of this class is based on the Symfony EventDispatcher component, which + * ships with the following license: + * + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * @link https://github.com/symfony/symfony/tree/master/src/Symfony/Component/EventDispatcher + */ +class Emitter implements EmitterInterface +{ + /** @var array */ + private $listeners = []; + + /** @var array */ + private $sorted = []; + + public function on($eventName, callable $listener, $priority = 0) + { + if ($priority === 'first') { + $priority = isset($this->listeners[$eventName]) + ? max(array_keys($this->listeners[$eventName])) + 1 + : 1; + } elseif ($priority === 'last') { + $priority = isset($this->listeners[$eventName]) + ? min(array_keys($this->listeners[$eventName])) - 1 + : -1; + } + + $this->listeners[$eventName][$priority][] = $listener; + unset($this->sorted[$eventName]); + } + + public function once($eventName, callable $listener, $priority = 0) + { + $onceListener = function ( + EventInterface $event, + $eventName + ) use (&$onceListener, $eventName, $listener, $priority) { + $this->removeListener($eventName, $onceListener); + $listener($event, $eventName, $this); + }; + + $this->on($eventName, $onceListener, $priority); + } + + public function removeListener($eventName, callable $listener) + { + if (!isset($this->listeners[$eventName])) { + return; + } + + foreach ($this->listeners[$eventName] as $priority => $listeners) { + if (false !== ($key = array_search($listener, $listeners, true))) { + unset( + $this->listeners[$eventName][$priority][$key], + $this->sorted[$eventName] + ); + } + } + } + + public function listeners($eventName = null) + { + // Return all events in a sorted priority order + if ($eventName === null) { + foreach (array_keys($this->listeners) as $eventName) { + if (!isset($this->sorted[$eventName])) { + $this->listeners($eventName); + } + } + return $this->sorted; + } + + // Return the listeners for a specific event, sorted in priority order + if (!isset($this->sorted[$eventName])) { + if (!isset($this->listeners[$eventName])) { + return []; + } else { + krsort($this->listeners[$eventName]); + $this->sorted[$eventName] = call_user_func_array( + 'array_merge', + $this->listeners[$eventName] + ); + } + } + + return $this->sorted[$eventName]; + } + + public function emit($eventName, EventInterface $event) + { + if (isset($this->listeners[$eventName])) { + foreach ($this->listeners($eventName) as $listener) { + $listener($event, $eventName); + if ($event->isPropagationStopped()) { + break; + } + } + } + + return $event; + } + + public function attach(SubscriberInterface $subscriber) + { + foreach ($subscriber->getEvents() as $eventName => $listener) { + $this->on( + $eventName, + array($subscriber, $listener[0]), + isset($listener[1]) ? $listener[1] : 0 + ); + } + } + + public function detach(SubscriberInterface $subscriber) + { + foreach ($subscriber->getEvents() as $eventName => $listener) { + $this->removeListener($eventName, array($subscriber, $listener[0])); + } + } + + public function __call($name, $arguments) + { + return \GuzzleHttp\deprecation_proxy( + $this, + $name, + $arguments, + [ + 'addSubscriber' => 'attach', + 'removeSubscriber' => 'detach', + 'addListener' => 'on', + 'dispatch' => 'emit' + ] + ); + } +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Event/EmitterInterface.php b/core/vendor/guzzlehttp/guzzle/src/Event/EmitterInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..0181e7fe713aaa2cff9d40a5f0cb8ce7c2fd1083 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Event/EmitterInterface.php @@ -0,0 +1,88 @@ +<?php + +namespace GuzzleHttp\Event; + +/** + * Guzzle event emitter. + */ +interface EmitterInterface +{ + /** + * Binds a listener to a specific event. + * + * @param string $eventName Name of the event to bind to. + * @param callable $listener Listener to invoke when triggered. + * @param int|string $priority The higher this value, the earlier an event + * listener will be triggered in the chain (defaults to 0). You can + * pass "first" or "last" to dynamically specify the event priority + * based on the current event priorities associated with the given + * event name in the emitter. Use "first" to set the priority to the + * current highest priority plus one. Use "last" to set the priority to + * the current lowest event priority minus one. + */ + public function on($eventName, callable $listener, $priority = 0); + + /** + * Binds a listener to a specific event. After the listener is triggered + * once, it is removed as a listener. + * + * @param string $eventName Name of the event to bind to. + * @param callable $listener Listener to invoke when triggered. + * @param int $priority The higher this value, the earlier an event + * listener will be triggered in the chain (defaults to 0) + */ + public function once($eventName, callable $listener, $priority = 0); + + /** + * Removes an event listener from the specified event. + * + * @param string $eventName The event to remove a listener from + * @param callable $listener The listener to remove + */ + public function removeListener($eventName, callable $listener); + + /** + * Gets the listeners of a specific event or all listeners if no event is + * specified. + * + * @param string $eventName The name of the event. Pass null (the default) + * to retrieve all listeners. + * + * @return array The event listeners for the specified event, or all event + * listeners by event name. The format of the array when retrieving a + * specific event list is an array of callables. The format of the array + * when retrieving all listeners is an associative array of arrays of + * callables. + */ + public function listeners($eventName = null); + + /** + * Emits an event to all registered listeners. + * + * Each event that is bound to the emitted eventName receives a + * EventInterface, the name of the event, and the event emitter. + * + * @param string $eventName The name of the event to dispatch. + * @param EventInterface $event The event to pass to the event handlers/listeners. + * + * @return EventInterface Returns the provided event object + */ + public function emit($eventName, EventInterface $event); + + /** + * Attaches an event subscriber. + * + * The subscriber is asked for all the events it is interested in and added + * as an event listener for each event. + * + * @param SubscriberInterface $subscriber Subscriber to attach. + */ + public function attach(SubscriberInterface $subscriber); + + /** + * Detaches an event subscriber. + * + * @param SubscriberInterface $subscriber Subscriber to detach. + */ + public function detach(SubscriberInterface $subscriber); +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Event/ErrorEvent.php b/core/vendor/guzzlehttp/guzzle/src/Event/ErrorEvent.php new file mode 100644 index 0000000000000000000000000000000000000000..02d4dff269ea9ae3b07c4b4c74825830a3e511c4 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Event/ErrorEvent.php @@ -0,0 +1,65 @@ +<?php + +namespace GuzzleHttp\Event; + +use GuzzleHttp\Exception\RequestException; +use GuzzleHttp\Message\ResponseInterface; +use GuzzleHttp\Adapter\TransactionInterface; + +/** + * Event object emitted after a request has been sent and an error was + * encountered. + * + * You may intercept the exception and inject a response into the event to + * rescue the request. + */ +class ErrorEvent extends AbstractTransferEvent +{ + private $exception; + + /** + * @param TransactionInterface $transaction Transaction that contains the request + * @param RequestException $e Exception encountered + * @param array $transferStats Array of transfer statistics + */ + public function __construct( + TransactionInterface $transaction, + RequestException $e, + $transferStats = [] + ) { + parent::__construct($transaction, $transferStats); + $this->exception = $e; + } + + /** + * Intercept the exception and inject a response + * + * @param ResponseInterface $response Response to set + */ + public function intercept(ResponseInterface $response) + { + $this->stopPropagation(); + $this->getTransaction()->setResponse($response); + RequestEvents::emitComplete($this->getTransaction()); + } + + /** + * Get the exception that was encountered + * + * @return RequestException + */ + public function getException() + { + return $this->exception; + } + + /** + * Get the response the was received (if any) + * + * @return ResponseInterface|null + */ + public function getResponse() + { + return $this->getException()->getResponse(); + } +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Event/EventInterface.php b/core/vendor/guzzlehttp/guzzle/src/Event/EventInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..bf58f8b5146b93894fe687311ded6b012651b80d --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Event/EventInterface.php @@ -0,0 +1,24 @@ +<?php + +namespace GuzzleHttp\Event; + +/** + * Base event interface used when dispatching events to listeners using an + * event emitter. + */ +interface EventInterface +{ + /** + * Returns whether or not stopPropagation was called on the event. + * + * @return bool + * @see Event::stopPropagation + */ + public function isPropagationStopped(); + + /** + * Stops the propagation of the event, preventing subsequent listeners + * registered to the same event from being invoked. + */ + public function stopPropagation(); +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Event/HasEmitterInterface.php b/core/vendor/guzzlehttp/guzzle/src/Event/HasEmitterInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..4503dbb0b209acb6c016fb4f9462cb611dbbf490 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Event/HasEmitterInterface.php @@ -0,0 +1,16 @@ +<?php + +namespace GuzzleHttp\Event; + +/** + * Holds an event emitter + */ +interface HasEmitterInterface +{ + /** + * Get the event emitter of the object + * + * @return EmitterInterface + */ + public function getEmitter(); +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Event/HasEmitterTrait.php b/core/vendor/guzzlehttp/guzzle/src/Event/HasEmitterTrait.php new file mode 100644 index 0000000000000000000000000000000000000000..8c9be214f5b04085eff89ac8c662f3f8bbb2a3fc --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Event/HasEmitterTrait.php @@ -0,0 +1,21 @@ +<?php + +namespace GuzzleHttp\Event; + +/** + * Trait that implements the methods of HasEmitterInterface + */ +trait HasEmitterTrait +{ + /** @var EmitterInterface */ + private $emitter; + + public function getEmitter() + { + if (!$this->emitter) { + $this->emitter = new Emitter(); + } + + return $this->emitter; + } +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Event/HeadersEvent.php b/core/vendor/guzzlehttp/guzzle/src/Event/HeadersEvent.php new file mode 100644 index 0000000000000000000000000000000000000000..81274ff84a670d48f65a8e51cd2b3793cd1844cf --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Event/HeadersEvent.php @@ -0,0 +1,39 @@ +<?php + +namespace GuzzleHttp\Event; + +use GuzzleHttp\Message\ResponseInterface; +use GuzzleHttp\Adapter\TransactionInterface; + +/** + * Event object emitted after the response headers of a request have been + * received. + * + * You may intercept the exception and inject a response into the event to + * rescue the request. + */ +class HeadersEvent extends AbstractRequestEvent +{ + /** + * @param TransactionInterface $transaction Transaction that contains the + * request and response. + * @throws \RuntimeException + */ + public function __construct(TransactionInterface $transaction) + { + parent::__construct($transaction); + if (!$transaction->getResponse()) { + throw new \RuntimeException('A response must be present'); + } + } + + /** + * Get the response the was received + * + * @return ResponseInterface + */ + public function getResponse() + { + return $this->getTransaction()->getResponse(); + } +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Event/RequestEvents.php b/core/vendor/guzzlehttp/guzzle/src/Event/RequestEvents.php new file mode 100644 index 0000000000000000000000000000000000000000..252c0af9b430cd84ebd8c1b7e0271bd943590a49 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Event/RequestEvents.php @@ -0,0 +1,130 @@ +<?php + +namespace GuzzleHttp\Event; + +use GuzzleHttp\Adapter\TransactionInterface; +use GuzzleHttp\Exception\RequestException; + +/** + * Contains methods used to manage the request event lifecycle. + */ +final class RequestEvents +{ + // Generic event priorities + const EARLY = 10000; + const LATE = -10000; + + // "before" priorities + const PREPARE_REQUEST = -100; + const SIGN_REQUEST = -10000; + + // "complete" and "error" response priorities + const VERIFY_RESPONSE = 100; + const REDIRECT_RESPONSE = 200; + + /** + * Emits the before send event for a request and emits an error + * event if an error is encountered during the before send. + * + * @param TransactionInterface $transaction + * + * @throws RequestException + */ + public static function emitBefore(TransactionInterface $transaction) { + $request = $transaction->getRequest(); + try { + $request->getEmitter()->emit( + 'before', + new BeforeEvent($transaction) + ); + } catch (RequestException $e) { + // When a RequestException has been emitted through emitError, the + // exception is marked as "emitted". This means that the exception + // had a chance to be rescued but was not. In this case, this method + // must not emit the error again, but rather throw the exception. + // This prevents RequestExceptions encountered during the before + // event from being emitted to listeners twice. + if ($e->emittedError()) { + throw $e; + } + self::emitError($transaction, $e); + } catch (\Exception $e) { + self::emitError($transaction, $e); + } + } + + /** + * Emits the complete event for a request and emits an error + * event if an error is encountered during the after send. + * + * @param TransactionInterface $transaction Transaction to emit for + * @param array $stats Transfer stats + * + * @throws RequestException + */ + public static function emitComplete( + TransactionInterface $transaction, + array $stats = [] + ) { + $request = $transaction->getRequest(); + $transaction->getResponse()->setEffectiveUrl($request->getUrl()); + try { + $request->getEmitter()->emit( + 'complete', + new CompleteEvent($transaction, $stats) + ); + } catch (RequestException $e) { + self::emitError($transaction, $e, $stats); + } + } + + /** + * Emits the headers event for a request. + * + * @param TransactionInterface $transaction Transaction to emit for + */ + public static function emitHeaders(TransactionInterface $transaction) + { + $transaction->getRequest()->getEmitter()->emit( + 'headers', + new HeadersEvent($transaction) + ); + } + + /** + * Emits an error event for a request and accounts for the propagation + * of an error event being stopped to prevent the exception from being + * thrown. + * + * @param TransactionInterface $transaction + * @param \Exception $e + * @param array $stats + * + * @throws \GuzzleHttp\Exception\RequestException + */ + public static function emitError( + TransactionInterface $transaction, + \Exception $e, + array $stats = [] + ) { + $request = $transaction->getRequest(); + + // Convert non-request exception to a wrapped exception + if (!($e instanceof RequestException)) { + $e = new RequestException($e->getMessage(), $request, null, $e); + } + + // Mark the exception as having been emitted for an error event. This + // works in tandem with the emitBefore method to prevent the error + // event from being triggered twice for the same exception. + $e->emittedError(true); + + // Dispatch an event and allow interception + if (!$request->getEmitter()->emit( + 'error', + new ErrorEvent($transaction, $e, $stats) + )->isPropagationStopped()) { + throw $e; + } + } +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Event/SubscriberInterface.php b/core/vendor/guzzlehttp/guzzle/src/Event/SubscriberInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..22c7311900f8786c67b0efdea25072053f5546cd --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Event/SubscriberInterface.php @@ -0,0 +1,31 @@ +<?php + +namespace GuzzleHttp\Event; + +/** + * SubscriberInterface provides an array of events to an + * EventEmitterInterface when it is registered. The emitter then binds the + * listeners specified by the EventSubscriber. + * + * This interface is based on the SubscriberInterface of the Symfony. + * @link https://github.com/symfony/symfony/tree/master/src/Symfony/Component/EventDispatcher + */ +interface SubscriberInterface +{ + /** + * Returns an array of event names this subscriber wants to listen to. + * + * The returned array keys MUST map to an event name. Each array value + * MUST be an array in which the first element is the name of a function + * on the EventSubscriber. The second element in the array is optional, and + * if specified, designates the event priority. + * + * For example: + * + * - ['eventName' => ['methodName']] + * - ['eventName' => ['methodName', $priority]] + * + * @return array + */ + public function getEvents(); +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Exception/AdapterException.php b/core/vendor/guzzlehttp/guzzle/src/Exception/AdapterException.php new file mode 100644 index 0000000000000000000000000000000000000000..55334c464c2132daaba5a7d6c463f6675d6c14e5 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Exception/AdapterException.php @@ -0,0 +1,5 @@ +<?php + +namespace GuzzleHttp\Exception; + +class AdapterException extends TransferException {} diff --git a/core/vendor/guzzlehttp/guzzle/src/Exception/BadResponseException.php b/core/vendor/guzzlehttp/guzzle/src/Exception/BadResponseException.php new file mode 100644 index 0000000000000000000000000000000000000000..c4ee1d8235ec72031463f6fc81a9c7a64e7bf516 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Exception/BadResponseException.php @@ -0,0 +1,8 @@ +<?php + +namespace GuzzleHttp\Exception; + +/** + * Exception when an HTTP error occurs (4xx or 5xx error) + */ +class BadResponseException extends RequestException {} diff --git a/core/vendor/guzzlehttp/guzzle/src/Exception/ClientException.php b/core/vendor/guzzlehttp/guzzle/src/Exception/ClientException.php new file mode 100644 index 0000000000000000000000000000000000000000..d7f15c0895d3fb81544505ab9ae19c2868a3f66e --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Exception/ClientException.php @@ -0,0 +1,8 @@ +<?php + +namespace GuzzleHttp\Exception; + +/** + * Exception when a client error is encountered (4xx codes) + */ +class ClientException extends BadResponseException {} diff --git a/core/vendor/guzzlehttp/guzzle/src/Exception/CouldNotRewindStreamException.php b/core/vendor/guzzlehttp/guzzle/src/Exception/CouldNotRewindStreamException.php new file mode 100644 index 0000000000000000000000000000000000000000..c3a0964597d493220861f5d21ffdd726d465c905 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Exception/CouldNotRewindStreamException.php @@ -0,0 +1,5 @@ +<?php + +namespace GuzzleHttp\Exception; + +class CouldNotRewindStreamException extends RequestException {} diff --git a/core/vendor/guzzlehttp/guzzle/src/Exception/ParseException.php b/core/vendor/guzzlehttp/guzzle/src/Exception/ParseException.php new file mode 100644 index 0000000000000000000000000000000000000000..378b9c07dff2550fc5c8b78b00ffea9a3ea52ed5 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Exception/ParseException.php @@ -0,0 +1,32 @@ +<?php + +namespace GuzzleHttp\Exception; + +use GuzzleHttp\Message\ResponseInterface; + +/** + * Exception when a client is unable to parse the response body as XML or JSON + */ +class ParseException extends TransferException +{ + /** @var ResponseInterface */ + private $response; + + public function __construct( + $message = '', + ResponseInterface $response = null, + \Exception $previous = null + ) { + parent::__construct($message, 0, $previous); + $this->response = $response; + } + /** + * Get the associated response + * + * @return ResponseInterface|null + */ + public function getResponse() + { + return $this->response; + } +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Exception/RequestException.php b/core/vendor/guzzlehttp/guzzle/src/Exception/RequestException.php new file mode 100644 index 0000000000000000000000000000000000000000..2c3e426ff80dcee391b4026eac575c7e7943a294 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Exception/RequestException.php @@ -0,0 +1,123 @@ +<?php + +namespace GuzzleHttp\Exception; + +use GuzzleHttp\Message\RequestInterface; +use GuzzleHttp\Message\ResponseInterface; + +/** + * HTTP Request exception + */ +class RequestException extends TransferException +{ + /** @var bool */ + private $emittedErrorEvent = false; + + /** @var RequestInterface */ + private $request; + + /** @var ResponseInterface */ + private $response; + + public function __construct( + $message = '', + RequestInterface $request, + ResponseInterface $response = null, + \Exception $previous = null + ) { + parent::__construct($message, 0, $previous); + $this->request = $request; + $this->response = $response; + } + + /** + * Factory method to create a new exception with a normalized error message + * + * @param RequestInterface $request Request + * @param ResponseInterface $response Response received + * @param \Exception $previous Previous exception + * + * @return self + */ + public static function create( + RequestInterface $request, + ResponseInterface $response = null, + \Exception $previous = null + ) { + if (!$response) { + return new self('Error completing request', $request, null, $previous); + } + + $level = $response->getStatusCode()[0]; + if ($level == '4') { + $label = 'Client error response'; + $className = __NAMESPACE__ . '\\ClientException'; + } elseif ($level == '5') { + $label = 'Server error response'; + $className = __NAMESPACE__ . '\\ServerException'; + } else { + $label = 'Unsuccessful response'; + $className = __CLASS__; + } + + $message = $label . ' [url] ' . $request->getUrl() + . ' [status code] ' . $response->getStatusCode() + . ' [reason phrase] ' . $response->getReasonPhrase(); + + return new $className($message, $request, $response, $previous); + } + + /** + * Get the request that caused the exception + * + * @return RequestInterface + */ + public function getRequest() + { + return $this->request; + } + + /** + * Get the associated response + * + * @return ResponseInterface|null + */ + public function getResponse() + { + return $this->response; + } + + /** + * Check if a response was received + * + * @return bool + */ + public function hasResponse() + { + return $this->response !== null; + } + + /** + * Check or set if the exception was emitted in an error event. + * + * This value is used in the RequestEvents::emitBefore() method to check + * to see if an exception has already been emitted in an error event. + * + * @param bool|null Set to true to set the exception as having emitted an + * error. Leave null to retrieve the current setting. + * + * @return null|bool + * @throws \InvalidArgumentException if you attempt to set the value to false + */ + public function emittedError($value = null) + { + if ($value === null) { + return $this->emittedErrorEvent; + } elseif ($value === true) { + return $this->emittedErrorEvent = true; + } else { + throw new \InvalidArgumentException('You cannot set the emitted ' + . 'error value to false.'); + } + } +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Exception/ServerException.php b/core/vendor/guzzlehttp/guzzle/src/Exception/ServerException.php new file mode 100644 index 0000000000000000000000000000000000000000..d67ed27e3429d7e75f5290abdc1baa0087ac3943 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Exception/ServerException.php @@ -0,0 +1,8 @@ +<?php + +namespace GuzzleHttp\Exception; + +/** + * Exception when a server error is encountered (5xx codes) + */ +class ServerException extends BadResponseException {} diff --git a/core/vendor/guzzlehttp/guzzle/src/Exception/TooManyRedirectsException.php b/core/vendor/guzzlehttp/guzzle/src/Exception/TooManyRedirectsException.php new file mode 100644 index 0000000000000000000000000000000000000000..e7feaa53e8dddd71a7d2cff7bc878a35c243b870 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Exception/TooManyRedirectsException.php @@ -0,0 +1,5 @@ +<?php + +namespace GuzzleHttp\Exception; + +class TooManyRedirectsException extends RequestException {} diff --git a/core/vendor/guzzlehttp/guzzle/src/Exception/TransferException.php b/core/vendor/guzzlehttp/guzzle/src/Exception/TransferException.php new file mode 100644 index 0000000000000000000000000000000000000000..72887b47506164a60edf4785ce5eb77306c56886 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Exception/TransferException.php @@ -0,0 +1,5 @@ +<?php + +namespace GuzzleHttp\Exception; + +class TransferException extends \RuntimeException {} diff --git a/core/vendor/guzzlehttp/guzzle/src/HasDataTrait.php b/core/vendor/guzzlehttp/guzzle/src/HasDataTrait.php new file mode 100644 index 0000000000000000000000000000000000000000..3fc32f563c2ffc9daf63c00ce1fd12efe363431d --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/HasDataTrait.php @@ -0,0 +1,76 @@ +<?php + +namespace GuzzleHttp; + +/** + * Trait implementing ToArrayInterface, \ArrayAccess, \Countable, + * \IteratorAggregate, and some path style methods. + */ +trait HasDataTrait +{ + /** @var array */ + protected $data; + + public function getIterator() + { + return new \ArrayIterator($this->data); + } + + public function offsetGet($offset) + { + return isset($this->data[$offset]) ? $this->data[$offset] : null; + } + + public function offsetSet($offset, $value) + { + $this->data[$offset] = $value; + } + + public function offsetExists($offset) + { + return isset($this->data[$offset]); + } + + public function offsetUnset($offset) + { + unset($this->data[$offset]); + } + + public function toArray() + { + return $this->data; + } + + public function count() + { + return count($this->data); + } + + /** + * Get a value from the collection using a path syntax to retrieve nested + * data. + * + * @param string $path Path to traverse and retrieve a value from + * + * @return mixed|null + */ + public function getPath($path) + { + return \GuzzleHttp\get_path($this->data, $path); + } + + /** + * Set a value into a nested array key. Keys will be created as needed to + * set the value. + * + * @param string $path Path to set + * @param mixed $value Value to set at the key + * + * @throws \RuntimeException when trying to setPath using a nested path + * that travels through a scalar value + */ + public function setPath($path, $value) + { + \GuzzleHttp\set_path($this->data, $path, $value); + } +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Message/AbstractMessage.php b/core/vendor/guzzlehttp/guzzle/src/Message/AbstractMessage.php new file mode 100644 index 0000000000000000000000000000000000000000..6037a70e6a2801c3a3b3d88cb3bb59531a47055c --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Message/AbstractMessage.php @@ -0,0 +1,237 @@ +<?php + +namespace GuzzleHttp\Message; + +use GuzzleHttp\Stream\StreamInterface; + +abstract class AbstractMessage implements MessageInterface +{ + /** @var array HTTP header collection */ + private $headers = []; + + /** @var array mapping a lowercase header name to its name over the wire */ + private $headerNames = []; + + /** @var StreamInterface Message body */ + private $body; + + /** @var string HTTP protocol version of the message */ + private $protocolVersion = '1.1'; + + public function __toString() + { + $result = $this->getStartLine(); + foreach ($this->getHeaders() as $name => $values) { + $result .= "\r\n{$name}: " . implode(', ', $values); + } + + return $result . "\r\n\r\n" . $this->body; + } + + public function getProtocolVersion() + { + return $this->protocolVersion; + } + + public function getBody() + { + return $this->body; + } + + public function setBody(StreamInterface $body = null) + { + if ($body === null) { + // Setting a null body will remove the body of the request + $this->removeHeader('Content-Length') + ->removeHeader('Transfer-Encoding'); + } + + $this->body = $body; + + return $this; + } + + public function addHeader($header, $value) + { + static $valid = ['string' => true, 'integer' => true, + 'double' => true, 'array' => true]; + + $type = gettype($value); + if (!isset($valid[$type])) { + throw new \InvalidArgumentException('Invalid header value'); + } + + if ($type == 'array') { + $current = array_merge($this->getHeader($header, true), $value); + } else { + $current = $this->getHeader($header, true); + $current[] = $value; + } + + return $this->setHeader($header, $current); + } + + public function addHeaders(array $headers) + { + foreach ($headers as $name => $header) { + $this->addHeader($name, $header); + } + } + + public function getHeader($header, $asArray = false) + { + $name = strtolower($header); + + if (!isset($this->headers[$name])) { + return $asArray ? [] : ''; + } + + return $asArray + ? $this->headers[$name] + : implode(', ', $this->headers[$name]); + } + + public function getHeaders() + { + $headers = []; + foreach ($this->headers as $name => $values) { + $headers[$this->headerNames[$name]] = $values; + } + + return $headers; + } + + public function setHeader($header, $value) + { + $header = trim($header); + $name = strtolower($header); + $this->headerNames[$name] = $header; + + switch (gettype($value)) { + case 'string': + $this->headers[$name] = [trim($value)]; + break; + case 'integer': + case 'double': + $this->headers[$name] = [(string) $value]; + break; + case 'array': + foreach ($value as &$v) { + $v = trim($v); + } + $this->headers[$name] = $value; + break; + default: + throw new \InvalidArgumentException('Invalid header value ' + . 'provided: ' . var_export($value, true)); + } + + return $this; + } + + public function setHeaders(array $headers) + { + $this->headers = $this->headerNames = []; + foreach ($headers as $key => $value) { + $this->setHeader($key, $value); + } + + return $this; + } + + public function hasHeader($header) + { + return isset($this->headers[strtolower($header)]); + } + + public function removeHeader($header) + { + $name = strtolower($header); + unset($this->headers[$name], $this->headerNames[$name]); + + return $this; + } + + /** + * Parse an array of header values containing ";" separated data into an + * array of associative arrays representing the header key value pair + * data of the header. When a parameter does not contain a value, but just + * contains a key, this function will inject a key with a '' string value. + * + * @param MessageInterface $message That contains the header + * @param string $header Header to retrieve from the message + * + * @return array Returns the parsed header values. + */ + public static function parseHeader(MessageInterface $message, $header) + { + static $trimmed = "\"' \n\t\r"; + $params = $matches = []; + + foreach (self::normalizeHeader($message, $header) as $val) { + $part = []; + foreach (preg_split('/;(?=([^"]*"[^"]*")*[^"]*$)/', $val) as $kvp) { + if (preg_match_all('/<[^>]+>|[^=]+/', $kvp, $matches)) { + $m = $matches[0]; + if (isset($m[1])) { + $part[trim($m[0], $trimmed)] = trim($m[1], $trimmed); + } else { + $part[] = trim($m[0], $trimmed); + } + } + } + if ($part) { + $params[] = $part; + } + } + + return $params; + } + + /** + * Converts an array of header values that may contain comma separated + * headers into an array of headers with no comma separated values. + * + * @param MessageInterface $message That contains the header + * @param string $header Header to retrieve from the message + * + * @return array Returns the normalized header field values. + */ + public static function normalizeHeader(MessageInterface $message, $header) + { + $h = $message->getHeader($header, true); + for ($i = 0, $total = count($h); $i < $total; $i++) { + if (strpos($h[$i], ',') === false) { + continue; + } + foreach (preg_split('/,(?=([^"]*"[^"]*")*[^"]*$)/', $h[$i]) as $v) { + $h[] = trim($v); + } + unset($h[$i]); + } + + return $h; + } + + /** + * Returns the start line of a message. + * + * @return string + */ + abstract protected function getStartLine(); + + /** + * Accepts and modifies the options provided to the message in the + * constructor. + * + * Can be overridden in subclasses as necessary. + * + * @param array $options Options array passed by reference. + */ + protected function handleOptions(array &$options) + { + if (isset($options['protocol_version'])) { + $this->protocolVersion = $options['protocol_version']; + } + } +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Message/MessageFactory.php b/core/vendor/guzzlehttp/guzzle/src/Message/MessageFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..9305fe697282acf45e2e1cd4ce35fd0b2e839087 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Message/MessageFactory.php @@ -0,0 +1,335 @@ +<?php + +namespace GuzzleHttp\Message; + +use GuzzleHttp\Post\PostFileInterface; +use GuzzleHttp\Subscriber\Cookie; +use GuzzleHttp\Cookie\CookieJar; +use GuzzleHttp\Cookie\CookieJarInterface; +use GuzzleHttp\Subscriber\HttpError; +use GuzzleHttp\Post\PostBody; +use GuzzleHttp\Post\PostFile; +use GuzzleHttp\Subscriber\Redirect; +use GuzzleHttp\Stream; +use GuzzleHttp\Query; +use GuzzleHttp\Url; + +/** + * Default HTTP request factory used to create Request and Response objects. + */ +class MessageFactory implements MessageFactoryInterface +{ + /** @var HttpError */ + private $errorPlugin; + + /** @var Redirect */ + private $redirectPlugin; + + public function __construct() + { + $this->errorPlugin = new HttpError(); + $this->redirectPlugin = new Redirect(); + } + + public function createResponse( + $statusCode, + array $headers = [], + $body = null, + array $options = [] + ) { + if (null !== $body) { + $body = Stream\create($body); + } + + return new Response($statusCode, $headers, $body, $options); + } + + public function createRequest($method, $url, array $options = []) + { + // Handle the request protocol version option that needs to be + // specified in the request constructor. + if (isset($options['version'])) { + $options['config']['protocol_version'] = $options['version']; + unset($options['version']); + } + + $request = new Request($method, $url, [], null, + isset($options['config']) ? $options['config'] : []); + + unset($options['config']); + + // Use a POST body by default + if ($method == 'POST' && !isset($options['body'])) { + $options['body'] = []; + } + + if ($options) { + $this->applyOptions($request, $options); + } + + return $request; + } + + /** + * Create a request or response object from an HTTP message string + * + * @param string $message Message to parse + * + * @return RequestInterface|ResponseInterface + * @throws \InvalidArgumentException if unable to parse a message + */ + public function fromMessage($message) + { + static $parser; + if (!$parser) { + $parser = new MessageParser(); + } + + // Parse a response + if (strtoupper(substr($message, 0, 4)) == 'HTTP') { + $data = $parser->parseResponse($message); + return $this->createResponse( + $data['code'], + $data['headers'], + $data['body'] === '' ? null : $data['body'], + $data + ); + } + + // Parse a request + if (!($data = ($parser->parseRequest($message)))) { + throw new \InvalidArgumentException('Unable to parse request'); + } + + return $this->createRequest( + $data['method'], + Url::buildUrl($data['request_url']), + [ + 'headers' => $data['headers'], + 'body' => $data['body'] === '' ? null : $data['body'], + 'config' => [ + 'protocol_version' => $data['protocol_version'] + ] + ] + ); + } + + /** + * Apply POST fields and files to a request to attempt to give an accurate + * representation. + * + * @param RequestInterface $request Request to update + * @param array $body Body to apply + */ + protected function addPostData(RequestInterface $request, array $body) + { + $post = new PostBody(); + foreach ($body as $key => $value) { + if (is_string($value) || is_array($value)) { + $post->setField($key, $value); + } elseif ($value instanceof PostFileInterface) { + $post->addFile($value); + } else { + $post->addFile(new PostFile($key, $value)); + } + } + + $request->setBody($post); + $post->applyRequestHeaders($request); + } + + protected function applyOptions( + RequestInterface $request, + array $options = [] + ) { + // Values specified in the config map are passed to request options + static $configMap = ['connect_timeout' => 1, 'timeout' => 1, + 'verify' => 1, 'ssl_key' => 1, 'cert' => 1, 'proxy' => 1, + 'debug' => 1, 'save_to' => 1, 'stream' => 1, 'expect' => 1]; + static $methods; + if (!$methods) { + $methods = array_flip(get_class_methods(__CLASS__)); + } + + // Iterate over each key value pair and attempt to apply a config using + // double dispatch. + $config = $request->getConfig(); + foreach ($options as $key => $value) { + $method = "add_{$key}"; + if (isset($methods[$method])) { + $this->{$method}($request, $value); + } elseif (isset($configMap[$key])) { + $config[$key] = $value; + } else { + throw new \InvalidArgumentException("No method is configured " + . "to handle the {$key} config key"); + } + } + } + + private function add_body(RequestInterface $request, $value) + { + if ($value !== null) { + if (is_array($value)) { + $this->addPostData($request, $value); + } else { + $request->setBody(Stream\create($value)); + } + } + } + + private function add_allow_redirects(RequestInterface $request, $value) + { + static $defaultRedirect = [ + 'max' => 5, + 'strict' => false, + 'referer' => false + ]; + + if ($value === false) { + return; + } + + if ($value === true) { + $value = $defaultRedirect; + } elseif (!isset($value['max'])) { + throw new \InvalidArgumentException('allow_redirects must be ' + . 'true, false, or an array that contains the \'max\' key'); + } else { + // Merge the default settings with the provided settings + $value += $defaultRedirect; + } + + $request->getConfig()['redirect'] = $value; + $request->getEmitter()->attach($this->redirectPlugin); + } + + private function add_exceptions(RequestInterface $request, $value) + { + if ($value === true) { + $request->getEmitter()->attach($this->errorPlugin); + } + } + + private function add_auth(RequestInterface $request, $value) + { + if (!$value) { + return; + } elseif (is_array($value)) { + $authType = isset($value[2]) ? strtolower($value[2]) : 'basic'; + } else { + $authType = strtolower($value); + } + + $request->getConfig()->set('auth', $value); + + if ($authType == 'basic') { + $request->setHeader( + 'Authorization', + 'Basic ' . base64_encode("$value[0]:$value[1]") + ); + } elseif ($authType == 'digest') { + // Currently only implemented by the cURL adapter. + // @todo: Need an event listener solution that does not rely on cURL + $config = $request->getConfig(); + $config->setPath('curl/' . CURLOPT_HTTPAUTH, CURLAUTH_DIGEST); + $config->setPath('curl/' . CURLOPT_USERPWD, "$value[0]:$value[1]"); + } + } + + private function add_query(RequestInterface $request, $value) + { + if ($value instanceof Query) { + $original = $request->getQuery(); + // Do not overwrite existing query string variables by overwriting + // the object with the query string data passed in the URL + $request->setQuery($value->overwriteWith($original->toArray())); + } elseif (is_array($value)) { + // Do not overwrite existing query string variables + $query = $request->getQuery(); + foreach ($value as $k => $v) { + if (!isset($query[$k])) { + $query[$k] = $v; + } + } + } else { + throw new \InvalidArgumentException('query value must be an array ' + . 'or Query object'); + } + } + + private function add_headers(RequestInterface $request, $value) + { + if (!is_array($value)) { + throw new \InvalidArgumentException('header value must be an array'); + } + + // Do not overwrite existing headers + foreach ($value as $k => $v) { + if (!$request->hasHeader($k)) { + $request->setHeader($k, $v); + } + } + } + + private function add_cookies(RequestInterface $request, $value) + { + if ($value === true) { + static $cookie = null; + if (!$cookie) { + $cookie = new Cookie(); + } + $request->getEmitter()->attach($cookie); + } elseif (is_array($value)) { + $request->getEmitter()->attach( + new Cookie(CookieJar::fromArray($value, $request->getHost())) + ); + } elseif ($value instanceof CookieJarInterface) { + $request->getEmitter()->attach(new Cookie($value)); + } elseif ($value !== false) { + throw new \InvalidArgumentException('cookies must be an array, ' + . 'true, or a CookieJarInterface object'); + } + } + + private function add_events(RequestInterface $request, $value) + { + if (!is_array($value)) { + throw new \InvalidArgumentException('events value must be an array'); + } + + $emitter = $request->getEmitter(); + foreach ($value as $name => $method) { + if (is_callable($method)) { + $emitter->on($name, $method); + } elseif (!is_array($method) || !isset($method['fn'])) { + throw new \InvalidArgumentException('Each event must be a ' + . 'callable or associative array containing a "fn" key'); + } elseif (isset($method['once']) && $method['once'] === true) { + $emitter->once( + $name, + $method['fn'], + isset($method['priority']) ? $method['priority'] : 0 + ); + } else { + $emitter->on( + $name, + $method['fn'], + isset($method['priority']) ? $method['priority'] : 0 + ); + } + } + } + + private function add_subscribers(RequestInterface $request, $value) + { + if (!is_array($value)) { + throw new \InvalidArgumentException('subscribers must be an array'); + } + + $emitter = $request->getEmitter(); + foreach ($value as $subscribers) { + $emitter->attach($subscribers); + } + } +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Message/MessageFactoryInterface.php b/core/vendor/guzzlehttp/guzzle/src/Message/MessageFactoryInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..d8de3d6f54c49366decab3780e2a1996cd53a45b --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Message/MessageFactoryInterface.php @@ -0,0 +1,70 @@ +<?php + +namespace GuzzleHttp\Message; + +use GuzzleHttp\Url; + +/** + * Request and response factory + */ +interface MessageFactoryInterface +{ + /** + * Creates a response + * + * @param string $statusCode HTTP status code + * @param array $headers Response headers + * @param mixed $body Response body + * @param array $options Response options + * - protocol_version: HTTP protocol version + * - header_factory: Factory used to create headers + * - And any other options used by a concrete message implementation + * + * @return ResponseInterface + */ + public function createResponse( + $statusCode, + array $headers = [], + $body = null, + array $options = [] + ); + + /** + * Create a new request based on the HTTP method. + * + * This method accepts an associative array of request options. Below is a + * brief description of each parameter. See + * http://docs.guzzlephp.org/clients.html#request-options for a much more + * in-depth description of each parameter. + * + * - headers: Associative array of headers to add to the request + * - body: string|resource|array|StreamInterface request body to send + * - query: Associative array of query string values to add to the request + * - auth: array|string HTTP auth settings (user, pass[, type="basic"]) + * - version: The HTTP protocol version to use with the request + * - cookies: true|false|CookieJarInterface To enable or disable cookies + * - allow_redirects: true|false|array Controls HTTP redirects + * - save_to: string|resource|StreamInterface Where the response is saved + * - events: Associative array of event names to callables or arrays + * - subscribers: Array of event subscribers to add to the request + * - exceptions: Specifies whether or not exceptions are thrown for HTTP protocol errors + * - timeout: Timeout of the request in seconds. Use 0 to wait indefinitely + * - connect_timeout: Number of seconds to wait while trying to connect. (0 to wait indefinitely) + * - verify: SSL validation. True/False or the path to a PEM file + * - cert: Path a SSL cert or array of (path, pwd) + * - ssl_key: Path to a private SSL key or array of (path, pwd) + * - proxy: Specify an HTTP proxy or hash of protocols to proxies + * - debug: Set to true or a resource to view adapter specific debug info + * - stream: Set to true to stream a response body rather than download it all up front + * - expect: true/false/integer Controls the "Expect: 100-Continue" header + * - config: Associative array of request config collection options + * + * @param string $method HTTP method (GET, POST, PUT, etc ...) + * @param string|Url $url HTTP URL to connect to + * @param array $options Array of options to apply to the request + * + * @return RequestInterface + * @link http://docs.guzzlephp.org/clients.html#request-options + */ + public function createRequest($method, $url, array $options = []); +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Message/MessageInterface.php b/core/vendor/guzzlehttp/guzzle/src/Message/MessageInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..2900d0e13ab936154edb4744bf4c43dfff485cc1 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Message/MessageInterface.php @@ -0,0 +1,148 @@ +<?php + +namespace GuzzleHttp\Message; + +use GuzzleHttp\Stream\StreamInterface; + +/** + * Request and response message interface + */ +interface MessageInterface +{ + /** + * Get a string representation of the message + * + * @return string + */ + public function __toString(); + + /** + * Get the HTTP protocol version of the message + * + * @return string + */ + public function getProtocolVersion(); + + /** + * Sets the body of the message. + * + * The body MUST be a StreamInterface object. Setting the body to null MUST + * remove the existing body. + * + * @param StreamInterface|null $body Body. + * + * @return self Returns the message. + */ + public function setBody(StreamInterface $body = null); + + /** + * Get the body of the message + * + * @return StreamInterface|null + */ + public function getBody(); + + /** + * Gets all message headers. + * + * The keys represent the header name as it will be sent over the wire, and + * each value is an array of strings associated with the header. + * + * // Represent the headers as a string + * foreach ($message->getHeaders() as $name => $values) { + * echo $name . ": " . implode(", ", $values); + * } + * + * @return array Returns an associative array of the message's headers. + */ + public function getHeaders(); + + /** + * Retrieve a header by the given case-insensitive name. + * + * By default, this method returns all of the header values of the given + * case-insensitive header name as a string concatenated together using + * a comma. Because some header should not be concatenated together using a + * comma, this method provides a Boolean argument that can be used to + * retrieve the associated header values as an array of strings. + * + * @param string $header Case-insensitive header name. + * @param bool $asArray Set to true to retrieve the header value as an + * array of strings. + * + * @return array|string + */ + public function getHeader($header, $asArray = false); + + /** + * Checks if a header exists by the given case-insensitive name. + * + * @param string $header Case-insensitive header name. + * + * @return bool Returns true if any header names match the given header + * name using a case-insensitive string comparison. Returns false if + * no matching header name is found in the message. + */ + public function hasHeader($header); + + /** + * Remove a specific header by case-insensitive name. + * + * @param string $header Case-insensitive header name. + * + * @return self + */ + public function removeHeader($header); + + /** + * Appends a header value to any existing values associated with the + * given header name. + * + * @param string $header Header name to add + * @param string $value Value of the header + * + * @return self + */ + public function addHeader($header, $value); + + /** + * Merges in an associative array of headers. + * + * Each array key MUST be a string representing the case-insensitive name + * of a header. Each value MUST be either a string or an array of strings. + * For each value, the value is appended to any existing header of the same + * name, or, if a header does not already exist by the given name, then the + * header is added. + * + * @param array $headers Associative array of headers to add to the message + * + * @return self + */ + public function addHeaders(array $headers); + + /** + * Sets a header, replacing any existing values of any headers with the + * same case-insensitive name. + * + * The header values MUST be a string or an array of strings. + * + * @param string $header Header name + * @param string|array $value Header value(s) + * + * @return self Returns the message. + */ + public function setHeader($header, $value); + + /** + * Sets headers, replacing any headers that have already been set on the + * message. + * + * The array keys MUST be a string. The array values must be either a + * string or an array of strings. + * + * @param array $headers Headers to set. + * + * @return self Returns the message. + */ + public function setHeaders(array $headers); +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Message/MessageParser.php b/core/vendor/guzzlehttp/guzzle/src/Message/MessageParser.php new file mode 100644 index 0000000000000000000000000000000000000000..2c49c9ecc193a48653617d123df01f0b25265f89 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Message/MessageParser.php @@ -0,0 +1,171 @@ +<?php + +namespace GuzzleHttp\Message; + +/** + * Request and response parser used by Guzzle + */ +class MessageParser +{ + /** + * Parse an HTTP request message into an associative array of parts. + * + * @param string $message HTTP request to parse + * + * @return array|bool Returns false if the message is invalid + */ + public function parseRequest($message) + { + if (!($parts = $this->parseMessage($message))) { + return false; + } + + // Parse the protocol and protocol version + if (isset($parts['start_line'][2])) { + $startParts = explode('/', $parts['start_line'][2]); + $protocol = strtoupper($startParts[0]); + $version = isset($startParts[1]) ? $startParts[1] : '1.1'; + } else { + $protocol = 'HTTP'; + $version = '1.1'; + } + + $parsed = [ + 'method' => strtoupper($parts['start_line'][0]), + 'protocol' => $protocol, + 'protocol_version' => $version, + 'headers' => $parts['headers'], + 'body' => $parts['body'] + ]; + + $parsed['request_url'] = $this->getUrlPartsFromMessage($parts['start_line'][1], $parsed); + + return $parsed; + } + + /** + * Parse an HTTP response message into an associative array of parts. + * + * @param string $message HTTP response to parse + * + * @return array|bool Returns false if the message is invalid + */ + public function parseResponse($message) + { + if (!($parts = $this->parseMessage($message))) { + return false; + } + + list($protocol, $version) = explode('/', trim($parts['start_line'][0])); + + return [ + 'protocol' => $protocol, + 'protocol_version' => $version, + 'code' => $parts['start_line'][1], + 'reason_phrase' => isset($parts['start_line'][2]) ? $parts['start_line'][2] : '', + 'headers' => $parts['headers'], + 'body' => $parts['body'] + ]; + } + + /** + * Parse a message into parts + * + * @param string $message Message to parse + * + * @return array|bool + */ + private function parseMessage($message) + { + if (!$message) { + return false; + } + + $startLine = null; + $headers = []; + $body = ''; + + // Iterate over each line in the message, accounting for line endings + $lines = preg_split('/(\\r?\\n)/', $message, -1, PREG_SPLIT_DELIM_CAPTURE); + for ($i = 0, $totalLines = count($lines); $i < $totalLines; $i += 2) { + + $line = $lines[$i]; + + // If two line breaks were encountered, then this is the end of body + if (empty($line)) { + if ($i < $totalLines - 1) { + $body = implode('', array_slice($lines, $i + 2)); + } + break; + } + + // Parse message headers + if (!$startLine) { + $startLine = explode(' ', $line, 3); + } elseif (strpos($line, ':')) { + $parts = explode(':', $line, 2); + $key = trim($parts[0]); + $value = isset($parts[1]) ? trim($parts[1]) : ''; + if (!isset($headers[$key])) { + $headers[$key] = $value; + } elseif (!is_array($headers[$key])) { + $headers[$key] = [$headers[$key], $value]; + } else { + $headers[$key][] = $value; + } + } + } + + return [ + 'start_line' => $startLine, + 'headers' => $headers, + 'body' => $body + ]; + } + + /** + * Create URL parts from HTTP message parts + * + * @param string $requestUrl Associated URL + * @param array $parts HTTP message parts + * + * @return array + */ + private function getUrlPartsFromMessage($requestUrl, array $parts) + { + // Parse the URL information from the message + $urlParts = ['path' => $requestUrl, 'scheme' => 'http']; + + // Check for the Host header + if (isset($parts['headers']['Host'])) { + $urlParts['host'] = $parts['headers']['Host']; + } elseif (isset($parts['headers']['host'])) { + $urlParts['host'] = $parts['headers']['host']; + } else { + $urlParts['host'] = null; + } + + if (false === strpos($urlParts['host'], ':')) { + $urlParts['port'] = ''; + } else { + $hostParts = explode(':', $urlParts['host']); + $urlParts['host'] = trim($hostParts[0]); + $urlParts['port'] = (int) trim($hostParts[1]); + if ($urlParts['port'] == 443) { + $urlParts['scheme'] = 'https'; + } + } + + // Check if a query is present + $path = $urlParts['path']; + $qpos = strpos($path, '?'); + if ($qpos) { + $urlParts['query'] = substr($path, $qpos + 1); + $urlParts['path'] = substr($path, 0, $qpos); + } else { + $urlParts['query'] = ''; + } + + return $urlParts; + } +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Message/Request.php b/core/vendor/guzzlehttp/guzzle/src/Message/Request.php new file mode 100644 index 0000000000000000000000000000000000000000..aa37034c92d0868b6ba7117454473639e7903361 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Message/Request.php @@ -0,0 +1,203 @@ +<?php + +namespace GuzzleHttp\Message; + +use GuzzleHttp\Event\HasEmitterTrait; +use GuzzleHttp\Collection; +use GuzzleHttp\Subscriber\Prepare; +use GuzzleHttp\Url; + +/** + * HTTP request class to send requests + */ +class Request extends AbstractMessage implements RequestInterface +{ + use HasEmitterTrait; + + /** @var Url HTTP Url */ + private $url; + + /** @var string HTTP method */ + private $method; + + /** @var Collection Transfer options */ + private $transferOptions; + + /** + * @param string $method HTTP method + * @param string|Url $url HTTP URL to connect to. The URI scheme, + * host header, and URI are parsed from the full URL. If query string + * parameters are present they will be parsed as well. + * @param array|Collection $headers HTTP headers + * @param mixed $body Body to send with the request + * @param array $options Array of options to use with the request + * - emitter: Event emitter to use with the request + */ + public function __construct( + $method, + $url, + $headers = [], + $body = null, + array $options = [] + ) { + $this->setUrl($url); + $this->method = strtoupper($method); + $this->handleOptions($options); + $this->transferOptions = new Collection($options); + $this->addPrepareEvent(); + + if ($body !== null) { + $this->setBody($body); + } + + if ($headers) { + foreach ($headers as $key => $value) { + $this->setHeader($key, $value); + } + } + } + + public function __clone() + { + if ($this->emitter) { + $this->emitter = clone $this->emitter; + } + $this->transferOptions = clone $this->transferOptions; + $this->url = clone $this->url; + } + + public function setUrl($url) + { + $this->url = $url instanceof Url ? $url : Url::fromString($url); + $this->updateHostHeaderFromUrl(); + + return $this; + } + + public function getUrl() + { + return (string) $this->url; + } + + public function setQuery($query) + { + $this->url->setQuery($query); + + return $this; + } + + public function getQuery() + { + return $this->url->getQuery(); + } + + public function setMethod($method) + { + $this->method = strtoupper($method); + + return $this; + } + + public function getMethod() + { + return $this->method; + } + + public function getScheme() + { + return $this->url->getScheme(); + } + + public function setScheme($scheme) + { + $this->url->setScheme($scheme); + + return $this; + } + + public function getHost() + { + return $this->url->getHost(); + } + + public function setHost($host) + { + $this->url->setHost($host); + $this->updateHostHeaderFromUrl(); + + return $this; + } + + public function getPath() + { + return '/' . ltrim($this->url->getPath(), '/'); + } + + public function setPath($path) + { + $this->url->setPath($path); + + return $this; + } + + public function getResource() + { + $resource = $this->getPath(); + if ($query = (string) $this->url->getQuery()) { + $resource .= '?' . $query; + } + + return $resource; + } + + public function getConfig() + { + return $this->transferOptions; + } + + protected function handleOptions(array &$options) + { + parent::handleOptions($options); + // Use a custom emitter if one is specified, and remove it from + // options that are exposed through getConfig() + if (isset($options['emitter'])) { + $this->emitter = $options['emitter']; + unset($options['emitter']); + } + } + + protected function getStartLine() + { + return trim($this->method . ' ' . $this->getResource()) + . ' HTTP/' . $this->getProtocolVersion(); + } + + /** + * Adds a subscriber that ensures a request's body is prepared before + * sending. + */ + private function addPrepareEvent() + { + static $subscriber; + if (!$subscriber) { + $subscriber = new Prepare(); + } + + $this->getEmitter()->attach($subscriber); + } + + private function updateHostHeaderFromUrl() + { + $port = $this->url->getPort(); + $scheme = $this->url->getScheme(); + if ($host = $this->url->getHost()) { + if (($port == 80 && $scheme == 'http') || + ($port == 443 && $scheme == 'https') + ) { + $this->setHeader('Host', $this->url->getHost()); + } else { + $this->setHeader('Host', $this->url->getHost() . ':' . $port); + } + } + } +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Message/RequestInterface.php b/core/vendor/guzzlehttp/guzzle/src/Message/RequestInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..255632296ca358f1ee28837ef6cc8a1967fe6c0c --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Message/RequestInterface.php @@ -0,0 +1,144 @@ +<?php + +namespace GuzzleHttp\Message; + +use GuzzleHttp\Event\HasEmitterInterface; +use GuzzleHttp\Query; + +/** + * Generic HTTP request interface + */ +interface RequestInterface extends MessageInterface, HasEmitterInterface +{ + const GET = 'GET'; + const PUT = 'PUT'; + const POST = 'POST'; + const DELETE = 'DELETE'; + const HEAD = 'HEAD'; + const CONNECT = 'CONNECT'; + const OPTIONS = 'OPTIONS'; + const TRACE = 'TRACE'; + const PATCH = 'PATCH'; + + /** + * @return string + */ + public function __toString(); + + /** + * Sets the request URL. + * + * The URL MUST be a string, or an object that implements the + * `__toString()` method. + * + * @param string $url Request URL. + * + * @return self Reference to the request. + * @throws \InvalidArgumentException If the URL is invalid. + */ + public function setUrl($url); + + /** + * Gets the request URL. + * + * @return string Returns the URL as a string. + */ + public function getUrl(); + + /** + * Get the resource part of the the request, including the path, query + * string, and fragment. + * + * @return string + */ + public function getResource(); + + /** + * Get the collection of key value pairs that will be used as the query + * string in the request. + * + * @return Query + */ + public function getQuery(); + + /** + * Set the query string used by the request + * + * @param array|Query $query Query to set + * + * @return self + */ + public function setQuery($query); + + /** + * Get the HTTP method of the request + * + * @return string + */ + public function getMethod(); + + /** + * Set the HTTP method of the request + * + * @param string $method HTTP method + * + * @return self + */ + public function setMethod($method); + + /** + * Get the URI scheme of the request (http, https, etc) + * + * @return string + */ + public function getScheme(); + + /** + * Set the URI scheme of the request (http, https, etc) + * + * @param string $scheme Scheme to set + * + * @return self + */ + public function setScheme($scheme); + + /** + * Get the host of the request + * + * @return string + */ + public function getHost(); + + /** + * Set the host of the request. Including a port in the host will modify + * the port of the request. + * + * @param string $host Host to set (e.g. www.yahoo.com, www.yahoo.com:80) + * + * @return self + */ + public function setHost($host); + + /** + * Get the path of the request (e.g. '/', '/index.html') + * + * @return string + */ + public function getPath(); + + /** + * Set the path of the request (e.g. '/', '/index.html') + * + * @param string|array $path Path to set or array of segments to implode + * + * @return self + */ + public function setPath($path); + + /** + * Get the request's configuration options + * + * @return \GuzzleHttp\Collection + */ + public function getConfig(); +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Message/Response.php b/core/vendor/guzzlehttp/guzzle/src/Message/Response.php new file mode 100644 index 0000000000000000000000000000000000000000..4e69cb07998a6d2fe188302ba4388dd78d008802 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Message/Response.php @@ -0,0 +1,204 @@ +<?php + +namespace GuzzleHttp\Message; + +use GuzzleHttp\Exception\ParseException; +use GuzzleHttp\Stream\StreamInterface; + +/** + * Guzzle HTTP response object + */ +class Response extends AbstractMessage implements ResponseInterface +{ + /** @var array Mapping of status codes to reason phrases */ + private static $statusTexts = array( + 100 => 'Continue', + 101 => 'Switching Protocols', + 102 => 'Processing', + 200 => 'OK', + 201 => 'Created', + 202 => 'Accepted', + 203 => 'Non-Authoritative Information', + 204 => 'No Content', + 205 => 'Reset Content', + 206 => 'Partial Content', + 207 => 'Multi-Status', + 208 => 'Already Reported', + 226 => 'IM Used', + 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', + 422 => 'Unprocessable Entity', + 423 => 'Locked', + 424 => 'Failed Dependency', + 425 => 'Reserved for WebDAV advanced collections expired proposal', + 426 => 'Upgrade required', + 428 => 'Precondition Required', + 429 => 'Too Many Requests', + 431 => 'Request Header Fields Too Large', + 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 (Experimental)', + 507 => 'Insufficient Storage', + 508 => 'Loop Detected', + 510 => 'Not Extended', + 511 => 'Network Authentication Required', + ); + + /** @var string The reason phrase of the response (human readable code) */ + private $reasonPhrase; + + /** @var string The status code of the response */ + private $statusCode; + + /** @var string The effective URL that returned this response */ + private $effectiveUrl; + + /** + * @param string $statusCode The response status code (e.g. 200) + * @param array $headers The response headers + * @param StreamInterface $body The body of the response + * @param array $options Response message options + * - reason_phrase: Set a custom reason phrase + * - protocol_version: Set a custom protocol version + */ + public function __construct( + $statusCode, + array $headers = [], + StreamInterface $body = null, + array $options = [] + ) { + $this->statusCode = (string) $statusCode; + $this->handleOptions($options); + + // Assume a reason phrase if one was not applied as an option + if (!$this->reasonPhrase && + isset(self::$statusTexts[$this->statusCode]) + ) { + $this->reasonPhrase = self::$statusTexts[$this->statusCode]; + } + + if ($headers) { + $this->setHeaders($headers); + } + + if ($body) { + $this->setBody($body); + } + } + + public function getStatusCode() + { + return $this->statusCode; + } + + public function getReasonPhrase() + { + return $this->reasonPhrase; + } + + public function json(array $config = []) + { + $data = json_decode( + (string) $this->getBody(), + isset($config['object']) ? !$config['object'] : true, + 512, + isset($config['big_int_strings']) ? JSON_BIGINT_AS_STRING : 0 + ); + + if (JSON_ERROR_NONE !== json_last_error()) { + throw new ParseException( + 'Unable to parse response body into JSON: ' . json_last_error(), + $this + ); + } + + return $data; + } + + public function xml(array $config = []) + { + $disableEntities = libxml_disable_entity_loader(true); + $internalErrors = libxml_use_internal_errors(true); + + try { + // Allow XML to be retrieved even if there is no response body + $xml = new \SimpleXMLElement( + (string) $this->getBody() ?: '<root />', + LIBXML_NONET, + isset($config['ns']) ? $config['ns'] : '', + isset($config['ns_is_prefix']) ? $config['ns_is_prefix'] : false + ); + libxml_disable_entity_loader($disableEntities); + libxml_use_internal_errors($internalErrors); + } catch (\Exception $e) { + libxml_disable_entity_loader($disableEntities); + libxml_use_internal_errors($internalErrors); + throw new ParseException( + 'Unable to parse response body into XML: ' . $e->getMessage(), + $this + ); + } + + return $xml; + } + + public function getEffectiveUrl() + { + return $this->effectiveUrl; + } + + public function setEffectiveUrl($url) + { + $this->effectiveUrl = $url; + + return $this; + } + + /** + * Accepts and modifies the options provided to the response in the + * constructor. + * + * @param array $options Options array passed by reference. + */ + protected function handleOptions(array &$options = []) + { + parent::handleOptions($options); + if (isset($options['reason_phrase'])) { + $this->reasonPhrase = $options['reason_phrase']; + } + } + + protected function getStartLine() + { + return 'HTTP/' . $this->getProtocolVersion() + . " {$this->statusCode} {$this->reasonPhrase}"; + } +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Message/ResponseInterface.php b/core/vendor/guzzlehttp/guzzle/src/Message/ResponseInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..8db7215cd5b42d13d70fd7bd218253ef0936d686 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Message/ResponseInterface.php @@ -0,0 +1,86 @@ +<?php + +namespace GuzzleHttp\Message; + +/** + * Represents an HTTP response message. + */ +interface ResponseInterface extends MessageInterface +{ + /** + * Get the response status code (e.g. "200", "404", etc) + * + * @return string + */ + public function getStatusCode(); + + /** + * Get the response reason phrase- a human readable version of the numeric + * status code + * + * @return string + */ + public function getReasonPhrase(); + + /** + * Get the effective URL that resulted in this response (e.g. the last + * redirect URL). + * + * @return string + */ + public function getEffectiveUrl(); + + /** + * Set the effective URL that resulted in this response (e.g. the last + * redirect URL). + * + * @param string $url Effective URL + * + * @return self + */ + public function setEffectiveUrl($url); + + /** + * Parse the JSON response body and return the JSON decoded data. + * + * @param array $config Associative array of configuration settings used + * to control how the JSON data is parsed. Concrete implementations MAY + * add further configuration settings as needed, but they MUST implement + * functionality for the following options: + * + * - object: Set to true to parse JSON objects as PHP objects rather + * than associative arrays. Defaults to false. + * - big_int_strings: When set to true, large integers are converted to + * strings rather than floats. Defaults to false. + * + * Implementations are free to add further configuration settings as + * needed. + * + * @return mixed Returns the JSON decoded data based on the provided + * parse settings. + * @throws \RuntimeException if the response body is not in JSON format + */ + public function json(array $config = []); + + /** + * Parse the XML response body and return a \SimpleXMLElement. + * + * In order to prevent XXE attacks, this method disables loading external + * entities. If you rely on external entities, then you must parse the + * XML response manually by accessing the response body directly. + * + * @param array $config Associative array of configuration settings used + * to control how the XML is parsed. Concrete implementations MAY add + * further configuration settings as needed, but they MUST implement + * functionality for the following options: + * + * - ns: Set to a string to represent the namespace prefix or URI + * - ns_is_prefix: Set to true to specify that the NS is a prefix rather + * than a URI (defaults to false). + * + * @return \SimpleXMLElement + * @throws \RuntimeException if the response body is not in XML format + * @link http://websec.io/2012/08/27/Preventing-XXE-in-PHP.html + */ + public function xml(array $config = []); +} diff --git a/core/vendor/guzzle/http/Guzzle/Http/Mimetypes.php b/core/vendor/guzzlehttp/guzzle/src/Mimetypes.php similarity index 99% rename from core/vendor/guzzle/http/Guzzle/Http/Mimetypes.php rename to core/vendor/guzzlehttp/guzzle/src/Mimetypes.php index 15af061f6f3d8e162ac0d3d3feb8ead6d0dc4bbc..939c2c51d4e413394f8f729db097b1a7ea90e47e 100644 --- a/core/vendor/guzzle/http/Guzzle/Http/Mimetypes.php +++ b/core/vendor/guzzlehttp/guzzle/src/Mimetypes.php @@ -1,6 +1,6 @@ <?php -namespace Guzzle\Http; +namespace GuzzleHttp; /** * Provides mappings of file extensions to mimetypes @@ -943,7 +943,11 @@ public static function getInstance() */ public function fromExtension($extension) { - return isset($this->mimetypes[$extension]) ? $this->mimetypes[$extension] : null; + $extension = strtolower($extension); + + return isset($this->mimetypes[$extension]) + ? $this->mimetypes[$extension] + : null; } /** diff --git a/core/vendor/guzzlehttp/guzzle/src/Post/MultipartBody.php b/core/vendor/guzzlehttp/guzzle/src/Post/MultipartBody.php new file mode 100644 index 0000000000000000000000000000000000000000..08c137d28735de0fbb42da2d2464c3f228bfc443 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Post/MultipartBody.php @@ -0,0 +1,293 @@ +<?php + +namespace GuzzleHttp\Post; + +use GuzzleHttp\Stream; + +/** + * Stream that when read returns bytes for a streaming multipart/form-data body + */ +class MultipartBody implements Stream\StreamInterface +{ + /** @var Stream\StreamInterface */ + private $files; + private $fields; + private $size; + private $buffer; + private $bufferedHeaders = []; + private $pos = 0; + private $currentFile = 0; + private $currentField = 0; + private $sentLast; + private $boundary; + + /** + * @param array $fields Associative array of field names to values where + * each value is a string. + * @param array $files Associative array of PostFileInterface objects + * @param string $boundary You can optionally provide a specific boundary + * @throws \InvalidArgumentException + */ + public function __construct( + array $fields = [], + array $files = [], + $boundary = null + ) { + $this->boundary = $boundary ?: uniqid(); + $this->fields = $fields; + $this->files = $files; + $this->meta['mode'] = 'r'; + + // Ensure each file is a PostFileInterface + foreach ($this->files as $file) { + if (!$file instanceof PostFileInterface) { + throw new \InvalidArgumentException('All POST fields must ' + . 'implement PostFieldInterface'); + } + } + } + + public function __toString() + { + $this->seek(0); + + return $this->getContents(); + } + + public function getContents($maxLength = -1) + { + $buffer = ''; + + while (!$this->eof()) { + if ($maxLength === -1) { + $read = 1048576; + } else { + $len = strlen($buffer); + if ($len == $maxLength) { + break; + } + $read = min(1048576, $maxLength - $len); + } + $buffer .= $this->read($read); + } + + return $buffer; + } + + /** + * Get the boundary + * + * @return string + */ + public function getBoundary() + { + return $this->boundary; + } + + public function close() + { + $this->detach(); + } + + public function detach() + { + $this->fields = $this->files = []; + } + + /** + * The stream has reached an EOF when all of the fields and files have been + * read. + * {@inheritdoc} + */ + public function eof() + { + return $this->currentField == count($this->fields) && + $this->currentFile == count($this->files); + } + + public function tell() + { + return $this->pos; + } + + public function isReadable() + { + return true; + } + + public function isWritable() + { + return false; + } + + /** + * The steam is seekable by default, but all attached files must be + * seekable too. + * {@inheritdoc} + */ + public function isSeekable() + { + foreach ($this->files as $file) { + if (!$file->getContent()->isSeekable()) { + return false; + } + } + + return true; + } + + public function getSize() + { + if ($this->size === null) { + foreach ($this->files as $file) { + // We must be able to ascertain the size of each attached file + if (null === ($size = $file->getContent()->getSize())) { + return null; + } + $this->size += strlen($this->getFileHeaders($file)) + $size; + } + foreach (array_keys($this->fields) as $key) { + $this->size += strlen($this->getFieldString($key)); + } + $this->size += strlen("\r\n--{$this->boundary}--"); + } + + return $this->size; + } + + public function read($length) + { + $content = ''; + if ($this->buffer && !$this->buffer->eof()) { + $content .= $this->buffer->read($length); + } + if ($delta = $length - strlen($content)) { + $content .= $this->readData($delta); + } + + if ($content === '' && !$this->sentLast) { + $this->sentLast = true; + $content = "\r\n--{$this->boundary}--"; + } + + return $content; + } + + public function seek($offset, $whence = SEEK_SET) + { + if ($offset != 0 || $whence != SEEK_SET || !$this->isSeekable()) { + return false; + } + + foreach ($this->files as $file) { + if (!$file->getContent()->seek(0)) { + throw new \RuntimeException('Rewind on multipart file failed ' + . 'even though it shouldn\'t have'); + } + } + + $this->buffer = $this->sentLast = null; + $this->pos = $this->currentField = $this->currentFile = 0; + $this->bufferedHeaders = []; + + return true; + } + + public function write($string) + { + return false; + } + + /** + * No data is in the read buffer, so more needs to be pulled in from fields + * and files. + * + * @param int $length Amount of data to read + * + * @return string + */ + private function readData($length) + { + $result = ''; + + if ($this->currentField < count($this->fields)) { + $result = $this->readField($length); + } + + if ($result === '' && $this->currentFile < count($this->files)) { + $result = $this->readFile($length); + } + + return $result; + } + + /** + * Create a new stream buffer and inject form-data + * + * @param int $length Amount of data to read from the stream buffer + * + * @return string + */ + private function readField($length) + { + $name = array_keys($this->fields)[++$this->currentField - 1]; + $this->buffer = Stream\create($this->getFieldString($name)); + + return $this->buffer->read($length); + } + + /** + * Read data from a POST file, fill the read buffer with any overflow + * + * @param int $length Amount of data to read from the file + * + * @return string + */ + private function readFile($length) + { + $current = $this->files[$this->currentFile]; + + // Got to the next file and recursively return the read value, or bail + // if no more data can be read. + if ($current->getContent()->eof()) { + return ++$this->currentFile == count($this->files) + ? '' + : $this->readFile($length); + } + + // If this is the start of a file, then send the headers to the read + // buffer. + if (!isset($this->bufferedHeaders[$this->currentFile])) { + $this->buffer = Stream\create($this->getFileHeaders($current)); + $this->bufferedHeaders[$this->currentFile] = true; + } + + // More data needs to be read to meet the limit, so pull from the file + $content = $this->buffer ? $this->buffer->read($length) : ''; + if (($remaining = $length - strlen($content)) > 0) { + $content .= $current->getContent()->read($remaining); + } + + return $content; + } + + private function getFieldString($key) + { + return sprintf( + "--%s\r\nContent-Disposition: form-data; name=\"%s\"\r\n\r\n%s\r\n", + $this->boundary, + $key, + $this->fields[$key] + ); + } + + private function getFileHeaders(PostFileInterface $file) + { + $headers = ''; + foreach ($file->getHeaders() as $key => $value) { + $headers .= "{$key}: {$value}\r\n"; + } + + return "--{$this->boundary}\r\n" . trim($headers) . "\r\n\r\n"; + } +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Post/PostBody.php b/core/vendor/guzzlehttp/guzzle/src/Post/PostBody.php new file mode 100644 index 0000000000000000000000000000000000000000..d6a2f3046f331fd2db13e0e9826db364601db703 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Post/PostBody.php @@ -0,0 +1,287 @@ +<?php + +namespace GuzzleHttp\Post; + +use GuzzleHttp\Message\RequestInterface; +use GuzzleHttp\Stream; +use GuzzleHttp\Query; + +/** + * Holds POST fields and files and creates a streaming body when read methods + * are called on the object. + */ +class PostBody implements PostBodyInterface +{ + /** @var Stream\StreamInterface */ + private $body; + + /** @var callable */ + private $aggregator; + + private $fields = []; + + /** @var PostFileInterface[] */ + private $files = []; + private $forceMultipart = false; + + /** + * Applies request headers to a request based on the POST state + * + * @param RequestInterface $request Request to update + */ + public function applyRequestHeaders(RequestInterface $request) + { + if ($this->files || $this->forceMultipart) { + $request->setHeader( + 'Content-Type', + 'multipart/form-data; boundary=' . $this->getBody()->getBoundary() + ); + } elseif ($this->fields) { + $request->setHeader('Content-Type', 'application/x-www-form-urlencoded'); + } + + if ($size = $this->getSize()) { + $request->setHeader('Content-Length', $size); + } + } + + public function forceMultipartUpload($force) + { + $this->forceMultipart = $force; + + return $this; + } + + public function setAggregator(callable $aggregator) + { + $this->aggregator = $aggregator; + } + + public function setField($name, $value) + { + $this->fields[$name] = $value; + $this->mutate(); + + return $this; + } + + public function replaceFields(array $fields) + { + $this->fields = $fields; + $this->mutate(); + + return $this; + } + + public function getField($name) + { + return isset($this->fields[$name]) ? $this->fields[$name] : null; + } + + public function removeField($name) + { + unset($this->fields[$name]); + $this->mutate(); + + return $this; + } + + public function getFields($asString = false) + { + if (!$asString) { + return $this->fields; + } + + return (string) (new Query($this->fields)) + ->setEncodingType(Query::RFC1738) + ->setAggregator($this->getAggregator()); + } + + public function hasField($name) + { + return isset($this->fields[$name]); + } + + public function getFile($name) + { + foreach ($this->files as $file) { + if ($file->getName() == $name) { + return $file; + } + } + + return null; + } + + public function getFiles() + { + return $this->files; + } + + public function addFile(PostFileInterface $file) + { + $this->files[] = $file; + $this->mutate(); + + return $this; + } + + public function clearFiles() + { + $this->files = []; + $this->mutate(); + + return $this; + } + + /** + * Returns the numbers of fields + files + * + * @return int + */ + public function count() + { + return count($this->files) + count($this->fields); + } + + public function __toString() + { + return (string) $this->getBody(); + } + + public function getContents($maxLength = -1) + { + return $this->getBody()->getContents(); + } + + public function close() + { + return $this->body ? $this->body->close() : true; + } + + public function detach() + { + $this->body = null; + $this->fields = $this->files = []; + + return $this; + } + + public function eof() + { + return $this->getBody()->eof(); + } + + public function tell() + { + return $this->body ? $this->body->tell() : 0; + } + + public function isSeekable() + { + return true; + } + + public function isReadable() + { + return true; + } + + public function isWritable() + { + return false; + } + + public function getSize() + { + return $this->getBody()->getSize(); + } + + public function seek($offset, $whence = SEEK_SET) + { + return $this->getBody()->seek($offset, $whence); + } + + public function read($length) + { + return $this->getBody()->read($length); + } + + public function write($string) + { + return false; + } + + /** + * Return a stream object that is built from the POST fields and files. + * + * If one has already been created, the previously created stream will be + * returned. + */ + private function getBody() + { + if ($this->body) { + return $this->body; + } elseif ($this->files || $this->forceMultipart) { + return $this->body = $this->createMultipart(); + } elseif ($this->fields) { + return $this->body = $this->createUrlEncoded(); + } else { + return $this->body = Stream\create(); + } + } + + /** + * Get the aggregator used to join multi-valued field parameters + * + * @return callable + */ + final protected function getAggregator() + { + if (!$this->aggregator) { + $this->aggregator = Query::phpAggregator(); + } + + return $this->aggregator; + } + + /** + * Creates a multipart/form-data body stream + * + * @return MultipartBody + */ + private function createMultipart() + { + $fields = $this->fields; + $query = (new Query()) + ->setEncodingType(false) + ->setAggregator($this->getAggregator()); + + // Account for fields with an array value + foreach ($fields as $name => &$field) { + if (is_array($field)) { + $field = (string) $query->replace([$name => $field]); + } + } + + return new MultipartBody($fields, $this->files); + } + + /** + * Creates an application/x-www-form-urlencoded stream body + * + * @return Stream\StreamInterface + */ + private function createUrlEncoded() + { + return Stream\create($this->getFields(true)); + } + + /** + * Get rid of any cached data + */ + private function mutate() + { + $this->body = null; + } +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Post/PostBodyInterface.php b/core/vendor/guzzlehttp/guzzle/src/Post/PostBodyInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..4405ce21a42eaf99eb88d1134f464a9a44f9c07a --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Post/PostBodyInterface.php @@ -0,0 +1,129 @@ +<?php + +namespace GuzzleHttp\Post; + +use GuzzleHttp\Message\RequestInterface; +use GuzzleHttp\Stream\StreamInterface; + +/** + * Represents a POST body that is sent as either a multipart/form-data stream + * or application/x-www-urlencoded stream. + */ +interface PostBodyInterface extends StreamInterface, \Countable +{ + /** + * Apply headers to the request appropriate for the current state of the object + * + * @param RequestInterface $request Request + */ + public function applyRequestHeaders(RequestInterface $request); + + /** + * Set a specific field + * + * @param string $name Name of the field to set + * @param string|array $value Value to set + * + * @return $this + */ + public function setField($name, $value); + + /** + * Set the aggregation strategy that will be used to turn multi-valued + * fields into a string. + * + * The aggregation function accepts a deeply nested array of query string + * values and returns a flattened associative array of key value pairs. + * + * @param callable $aggregator + */ + public function setAggregator(callable $aggregator); + + /** + * Set to true to force a multipart upload even if there are no files. + * + * @param bool $force Set to true to force multipart uploads or false to + * remove this flag. + * + * @return self + */ + public function forceMultipartUpload($force); + + /** + * Replace all existing form fields with an array of fields + * + * @param array $fields Associative array of fields to set + * + * @return $this + */ + public function replaceFields(array $fields); + + /** + * Get a specific field by name + * + * @param string $name Name of the POST field to retrieve + * + * @return string|null + */ + public function getField($name); + + /** + * Remove a field by name + * + * @param string $name Name of the field to remove + * + * @return $this + */ + public function removeField($name); + + /** + * Returns an associative array of names to values or a query string. + * + * @param bool $asString Set to true to retrieve the fields as a query + * string. + * + * @return array|string + */ + public function getFields($asString = false); + + /** + * Returns true if a field is set + * + * @param string $name Name of the field to set + * + * @return bool + */ + public function hasField($name); + + /** + * Get all of the files + * + * @return array Returns an array of PostFileInterface objects + */ + public function getFiles(); + + /** + * Get a POST file by name. + * + * @param string $name Name of the POST file to retrieve + * + * @return PostFileInterface|null + */ + public function getFile($name); + + /** + * Add a file to the POST + * + * @param PostFileInterface $file File to add + * + * @return $this + */ + public function addFile(PostFileInterface $file); + + /** + * Remove all files from the collection + * + * @return $this + */ + public function clearFiles(); +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Post/PostFile.php b/core/vendor/guzzlehttp/guzzle/src/Post/PostFile.php new file mode 100644 index 0000000000000000000000000000000000000000..bcd517d710c03038e27bc013edfd02652afc822a --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Post/PostFile.php @@ -0,0 +1,138 @@ +<?php + +namespace GuzzleHttp\Post; + +use GuzzleHttp\Mimetypes; +use GuzzleHttp\Stream\MetadataStreamInterface; +use GuzzleHttp\Stream\StreamInterface; + +/** + * Post file upload + */ +class PostFile implements PostFileInterface +{ + private $name; + private $filename; + private $content; + private $headers = []; + + /** + * @param null $name Name of the form field + * @param mixed $content Data to send + * @param null $filename Filename content-disposition attribute + * @param array $headers Array of headers to set on the file + * (can override any default headers) + * @throws \RuntimeException when filename is not passed or can't be determined + */ + public function __construct( + $name, + $content, + $filename = null, + array $headers = [] + ) { + $this->headers = $headers; + $this->name = $name; + $this->prepareContent($content); + $this->prepareFilename($filename); + $this->prepareDefaultHeaders(); + } + + public function getName() + { + return $this->name; + } + + public function getFilename() + { + return $this->filename; + } + + public function getContent() + { + return $this->content; + } + + public function getHeaders() + { + return $this->headers; + } + + /** + * Prepares the contents of a POST file. + * + * @param mixed $content Content of the POST file + */ + private function prepareContent($content) + { + $this->content = $content; + + if (!($this->content instanceof StreamInterface)) { + $this->content = \GuzzleHttp\Stream\create($this->content); + } elseif ($this->content instanceof MultipartBody) { + if (!$this->hasHeader('Content-Disposition')) { + $disposition = 'form-data; name="' . $this->name .'"'; + $this->headers['Content-Disposition'] = $disposition; + } + + if (!$this->hasHeader('Content-Type')) { + $this->headers['Content-Type'] = sprintf( + "multipart/form-data; boundary=%s", + $this->content->getBoundary() + ); + } + } + } + + /** + * Applies a file name to the POST file based on various checks. + * + * @param string|null $filename Filename to apply (or null to guess) + */ + private function prepareFilename($filename) + { + $this->filename = $filename; + + if (!$this->filename && + $this->content instanceof MetadataStreamInterface + ) { + $this->filename = $this->content->getMetadata('uri'); + } + + if (!$this->filename || substr($this->filename, 0, 6) === 'php://') { + $this->filename = $this->name; + } + } + + /** + * Applies default Content-Disposition and Content-Type headers if needed. + */ + private function prepareDefaultHeaders() + { + // Set a default content-disposition header if one was no provided + if (!$this->hasHeader('Content-Disposition')) { + $this->headers['Content-Disposition'] = sprintf( + 'form-data; filename="%s"; name="%s"', + basename($this->filename), + $this->name + ); + } + + // Set a default Content-Type if one was not supplied + if (!$this->hasHeader('Content-Type')) { + $this->headers['Content-Type'] = Mimetypes::getInstance() + ->fromFilename($this->filename) ?: 'text/plain'; + } + } + + /** + * Check if a specific header exists on the POST file by name. + * + * @param string $name Case-insensitive header to check + * + * @return bool + */ + private function hasHeader($name) + { + return isset(array_change_key_case($this->headers)[strtolower($name)]); + } +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Post/PostFileInterface.php b/core/vendor/guzzlehttp/guzzle/src/Post/PostFileInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..205dd9676c9bc7e4395955d276336578101cc082 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Post/PostFileInterface.php @@ -0,0 +1,42 @@ +<?php + +namespace GuzzleHttp\Post; + +use GuzzleHttp\Stream\StreamInterface; + +/** + * Post file upload interface + */ +interface PostFileInterface +{ + /** + * Get the name of the form field + * + * @return string + */ + public function getName(); + + /** + * Get the full path to the file + * + * @return string + */ + public function getFilename(); + + /** + * Get the content + * + * @return StreamInterface + */ + public function getContent(); + + /** + * Gets all POST file headers. + * + * The keys represent the header name as it will be sent over the wire, and + * each value is a string. + * + * @return array Returns an associative array of the file's headers. + */ + public function getHeaders(); +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Query.php b/core/vendor/guzzlehttp/guzzle/src/Query.php new file mode 100644 index 0000000000000000000000000000000000000000..1cb7dcd0859e37ea37e6f0e2dbe44abd006e351e --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Query.php @@ -0,0 +1,226 @@ +<?php + +namespace GuzzleHttp; + +/** + * Manages query string variables and can aggregate them into a string + */ +class Query extends Collection +{ + const RFC3986 = 'RFC3986'; + const RFC1738 = 'RFC1738'; + + /** @var bool URL encode fields and values */ + private $encoding = self::RFC3986; + + /** @var callable */ + private $aggregator; + + /** + * Parse a query string into a Query object + * + * @param string $query Query string to parse + * + * @return self + */ + public static function fromString($query) + { + $q = new static(); + if ($query === '') { + return $q; + } + + $foundDuplicates = $foundPhpStyle = false; + + foreach (explode('&', $query) as $kvp) { + $parts = explode('=', $kvp, 2); + $key = rawurldecode($parts[0]); + if ($paramIsPhpStyleArray = substr($key, -2) == '[]') { + $foundPhpStyle = true; + $key = substr($key, 0, -2); + } + if (isset($parts[1])) { + $value = rawurldecode(str_replace('+', '%20', $parts[1])); + if (isset($q[$key])) { + $q->add($key, $value); + $foundDuplicates = true; + } elseif ($paramIsPhpStyleArray) { + $q[$key] = array($value); + } else { + $q[$key] = $value; + } + } else { + $q->add($key, null); + } + } + + // Use the duplicate aggregator if duplicates were found and not using + // PHP style arrays. + if ($foundDuplicates && !$foundPhpStyle) { + $q->setAggregator(self::duplicateAggregator()); + } + + return $q; + } + + /** + * Convert the query string parameters to a query string string + * + * @return string + */ + public function __toString() + { + if (!$this->data) { + return ''; + } + + // The default aggregator is statically cached + static $defaultAggregator; + + if (!$this->aggregator) { + if (!$defaultAggregator) { + $defaultAggregator = self::phpAggregator(); + } + $this->aggregator = $defaultAggregator; + } + + $result = ''; + $aggregator = $this->aggregator; + + foreach ($aggregator($this->data) as $key => $values) { + foreach ($values as $value) { + if ($result) { + $result .= '&'; + } + if ($this->encoding == self::RFC1738) { + $result .= urlencode($key); + if ($value !== null) { + $result .= '=' . urlencode($value); + } + } elseif ($this->encoding == self::RFC3986) { + $result .= rawurlencode($key); + if ($value !== null) { + $result .= '=' . rawurlencode($value); + } + } else { + $result .= $key; + if ($value !== null) { + $result .= '=' . $value; + } + } + } + } + + return $result; + } + + /** + * Controls how multi-valued query string parameters are aggregated into a + * string. + * + * $query->setAggregator($query::duplicateAggregator()); + * + * @param callable $aggregator Callable used to convert a deeply nested + * array of query string variables into a flattened array of key value + * pairs. The callable accepts an array of query data and returns a + * flattened array of key value pairs where each value is an array of + * strings. + * + * @return self + */ + public function setAggregator(callable $aggregator) + { + $this->aggregator = $aggregator; + + return $this; + } + + /** + * Specify how values are URL encoded + * + * @param string|bool $type One of 'RFC1738', 'RFC3986', or false to disable encoding + * + * @return self + * @throws \InvalidArgumentException + */ + public function setEncodingType($type) + { + if ($type === false || $type === self::RFC1738 || $type === self::RFC3986) { + $this->encoding = $type; + } else { + throw new \InvalidArgumentException('Invalid URL encoding type'); + } + + return $this; + } + + /** + * Query string aggregator that does not aggregate nested query string + * values and allows duplicates in the resulting array. + * + * Example: http://test.com?q=1&q=2 + * + * @return callable + */ + public static function duplicateAggregator() + { + return function (array $data) { + return self::walkQuery($data, '', function ($key, $prefix) { + return is_int($key) ? $prefix : "{$prefix}[{$key}]"; + }); + }; + } + + /** + * Aggregates nested query string variables using the same technique as + * ``http_build_query()``. + * + * @param bool $numericIndices Pass false to not include numeric indices + * when multi-values query string parameters are present. + * + * @return callable + */ + public static function phpAggregator($numericIndices = true) + { + return function (array $data) use ($numericIndices) { + return self::walkQuery( + $data, + '', + function ($key, $prefix) use ($numericIndices) { + return !$numericIndices && is_int($key) + ? "{$prefix}[]" + : "{$prefix}[{$key}]"; + } + ); + }; + } + + /** + * Easily create query aggregation functions by providing a key prefix + * function to this query string array walker. + * + * @param array $query Query string to walk + * @param string $keyPrefix Key prefix (start with '') + * @param callable $prefixer Function used to create a key prefix + * + * @return array + */ + public static function walkQuery(array $query, $keyPrefix, callable $prefixer) + { + $result = []; + foreach ($query as $key => $value) { + if ($keyPrefix) { + $key = $prefixer($key, $keyPrefix); + } + if (is_array($value)) { + $result += self::walkQuery($value, $key, $prefixer); + } elseif (isset($result[$key])) { + $result[$key][] = $value; + } else { + $result[$key] = array($value); + } + } + + return $result; + } +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Subscriber/Cookie.php b/core/vendor/guzzlehttp/guzzle/src/Subscriber/Cookie.php new file mode 100644 index 0000000000000000000000000000000000000000..4b8a2c081be3745c79afc9a095a9f50cc6cb6f58 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Subscriber/Cookie.php @@ -0,0 +1,59 @@ +<?php + +namespace GuzzleHttp\Subscriber; + +use GuzzleHttp\Event\RequestEvents; +use GuzzleHttp\Event\SubscriberInterface; +use GuzzleHttp\Event\CompleteEvent; +use GuzzleHttp\Event\BeforeEvent; +use GuzzleHttp\Cookie\CookieJar; +use GuzzleHttp\Cookie\CookieJarInterface; + +/** + * Adds, extracts, and persists cookies between HTTP requests + */ +class Cookie implements SubscriberInterface +{ + /** @var CookieJarInterface */ + private $cookieJar; + + /** + * @param CookieJarInterface $cookieJar Cookie jar used to hold cookies + */ + public function __construct(CookieJarInterface $cookieJar = null) + { + $this->cookieJar = $cookieJar ?: new CookieJar(); + } + + public function getEvents() + { + // Fire the cookie plugin complete event before redirecting + return [ + 'before' => ['onBefore'], + 'complete' => ['onComplete', RequestEvents::REDIRECT_RESPONSE + 10] + ]; + } + + /** + * Get the cookie cookieJar + * + * @return CookieJarInterface + */ + public function getCookieJar() + { + return $this->cookieJar; + } + + public function onBefore(BeforeEvent $event) + { + $this->cookieJar->addCookieHeader($event->getRequest()); + } + + public function onComplete(CompleteEvent $event) + { + $this->cookieJar->extractCookies( + $event->getRequest(), + $event->getResponse() + ); + } +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Subscriber/History.php b/core/vendor/guzzlehttp/guzzle/src/Subscriber/History.php new file mode 100644 index 0000000000000000000000000000000000000000..b2a49e04453684b9772b4c5536b3735847044802 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Subscriber/History.php @@ -0,0 +1,138 @@ +<?php + +namespace GuzzleHttp\Subscriber; + +use GuzzleHttp\Event\RequestEvents; +use GuzzleHttp\Event\SubscriberInterface; +use GuzzleHttp\Event\CompleteEvent; +use GuzzleHttp\Event\ErrorEvent; +use GuzzleHttp\Message\RequestInterface; +use GuzzleHttp\Message\ResponseInterface; + +/** + * Maintains a list of requests and responses sent using a request or client + */ +class History implements SubscriberInterface, \IteratorAggregate, \Countable +{ + /** @var int The maximum number of requests to maintain in the history */ + private $limit; + + /** @var array Requests and responses that have passed through the plugin */ + private $transactions = []; + + public function __construct($limit = 10) + { + $this->limit = $limit; + } + + public function getEvents() + { + return [ + 'complete' => ['onComplete', RequestEvents::EARLY], + 'error' => ['onError', RequestEvents::EARLY], + ]; + } + + /** + * Convert to a string that contains all request and response headers + * + * @return string + */ + public function __toString() + { + $lines = array(); + foreach ($this->transactions as $entry) { + $response = isset($entry['response']) ? $entry['response'] : ''; + $lines[] = '> ' . trim($entry['request']) . "\n\n< " . trim($response) . "\n"; + } + + return implode("\n", $lines); + } + + public function onComplete(CompleteEvent $event) + { + $this->add($event->getRequest(), $event->getResponse()); + } + + public function onError(ErrorEvent $event) + { + $this->add($event->getRequest(), $event->getResponse()); + } + + /** + * Returns an Iterator that yields associative array values where each + * associative array contains a 'request' and 'response' key. + * + * @return \Iterator + */ + public function getIterator() + { + return new \ArrayIterator($this->transactions); + } + + /** + * Get all of the requests sent through the plugin + * + * @return RequestInterface[] + */ + public function getRequests() + { + return array_map(function ($t) { + return $t['request']; + }, $this->transactions); + } + + /** + * Get the number of requests in the history + * + * @return int + */ + public function count() + { + return count($this->transactions); + } + + /** + * Get the last request sent + * + * @return RequestInterface + */ + public function getLastRequest() + { + return end($this->transactions)['request']; + } + + /** + * Get the last response in the history + * + * @return ResponseInterface|null + */ + public function getLastResponse() + { + return end($this->transactions)['response']; + } + + /** + * Clears the history + */ + public function clear() + { + $this->transactions = array(); + } + + /** + * Add a request to the history + * + * @param RequestInterface $request Request to add + * @param ResponseInterface $response Response of the request + */ + private function add( + RequestInterface $request, + ResponseInterface $response = null + ) { + $this->transactions[] = ['request' => $request, 'response' => $response]; + if (count($this->transactions) > $this->limit) { + array_shift($this->transactions); + } + } +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Subscriber/HttpError.php b/core/vendor/guzzlehttp/guzzle/src/Subscriber/HttpError.php new file mode 100644 index 0000000000000000000000000000000000000000..f2f72f15e9d1337950f307bc023c60a870b169e3 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Subscriber/HttpError.php @@ -0,0 +1,34 @@ +<?php + +namespace GuzzleHttp\Subscriber; + +use GuzzleHttp\Event\RequestEvents; +use GuzzleHttp\Event\SubscriberInterface; +use GuzzleHttp\Event\CompleteEvent; +use GuzzleHttp\Exception\RequestException; + +/** + * Throws exceptions when a 4xx or 5xx response is received + */ +class HttpError implements SubscriberInterface +{ + public function getEvents() + { + return ['complete' => ['onComplete', RequestEvents::VERIFY_RESPONSE]]; + } + + /** + * Throw a RequestException on an HTTP protocol error + * + * @param CompleteEvent $event Emitted event + * @throws RequestException + */ + public function onComplete(CompleteEvent $event) + { + $code = (string) $event->getResponse()->getStatusCode(); + // Throw an exception for an unsuccessful response + if ($code[0] === '4' || $code[0] === '5') { + throw RequestException::create($event->getRequest(), $event->getResponse()); + } + } +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Subscriber/Mock.php b/core/vendor/guzzlehttp/guzzle/src/Subscriber/Mock.php new file mode 100644 index 0000000000000000000000000000000000000000..99a3d18d72cd77f8d059f75ce7126f0063d433e3 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Subscriber/Mock.php @@ -0,0 +1,143 @@ +<?php + +namespace GuzzleHttp\Subscriber; + +use GuzzleHttp\Event\RequestEvents; +use GuzzleHttp\Event\SubscriberInterface; +use GuzzleHttp\Adapter\Transaction; +use GuzzleHttp\Event\BeforeEvent; +use GuzzleHttp\Event\HeadersEvent; +use GuzzleHttp\Exception\RequestException; +use GuzzleHttp\Message\MessageFactory; +use GuzzleHttp\Message\ResponseInterface; + +/** + * Queues mock responses or exceptions and delivers mock responses or + * exceptions in a fifo order. + */ +class Mock implements SubscriberInterface, \Countable +{ + /** @var array Array of mock responses / exceptions */ + private $queue = []; + + /** @var bool Whether or not to consume an entity body when mocking */ + private $readBodies; + + /** @var MessageFactory */ + private $factory; + + /** + * @param array $items Array of responses or exceptions to queue + * @param bool $readBodies Set to false to not consume the entity body of + * a request when a mock is served. + */ + public function __construct(array $items = [], $readBodies = true) + { + $this->factory = new MessageFactory(); + $this->readBodies = $readBodies; + $this->addMultiple($items); + } + + public function getEvents() + { + // Fire the event last, after signing + return ['before' => ['onBefore', RequestEvents::SIGN_REQUEST - 10]]; + } + + /** + * @throws \OutOfBoundsException|\Exception + */ + public function onBefore(BeforeEvent $event) + { + if (!$item = array_shift($this->queue)) { + throw new \OutOfBoundsException('Mock queue is empty'); + } elseif ($item instanceof RequestException) { + throw $item; + } + + // Emulate the receiving of the response headers + $request = $event->getRequest(); + $transaction = new Transaction($event->getClient(), $request); + $transaction->setResponse($item); + $request->getEmitter()->emit( + 'headers', + new HeadersEvent($transaction) + ); + + // Emulate reading a response body + if ($this->readBodies && $request->getBody()) { + while (!$request->getBody()->eof()) { + $request->getBody()->read(8096); + } + } + + $event->intercept($item); + } + + public function count() + { + return count($this->queue); + } + + /** + * Add a response to the end of the queue + * + * @param string|ResponseInterface $response Response or path to response file + * + * @return self + * @throws \InvalidArgumentException if a string or Response is not passed + */ + public function addResponse($response) + { + if (is_string($response)) { + $response = file_exists($response) + ? $this->factory->fromMessage(file_get_contents($response)) + : $this->factory->fromMessage($response); + } elseif (!($response instanceof ResponseInterface)) { + throw new \InvalidArgumentException('Response must a message ' + . 'string, response object, or path to a file'); + } + + $this->queue[] = $response; + + return $this; + } + + /** + * Add an exception to the end of the queue + * + * @param RequestException $e Exception to throw when the request is executed + * + * @return self + */ + public function addException(RequestException $e) + { + $this->queue[] = $e; + + return $this; + } + + /** + * Add multiple items to the queue + * + * @param array $items Items to add + */ + public function addMultiple(array $items) + { + foreach ($items as $item) { + if ($item instanceof RequestException) { + $this->addException($item); + } else { + $this->addResponse($item); + } + } + } + + /** + * Clear the queue + */ + public function clearQueue() + { + $this->queue = []; + } +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Subscriber/Prepare.php b/core/vendor/guzzlehttp/guzzle/src/Subscriber/Prepare.php new file mode 100644 index 0000000000000000000000000000000000000000..2d82acd6d8a443844b4e67ff41f4af6f2461d9a2 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Subscriber/Prepare.php @@ -0,0 +1,136 @@ +<?php + +namespace GuzzleHttp\Subscriber; + +use GuzzleHttp\Event\RequestEvents; +use GuzzleHttp\Event\SubscriberInterface; +use GuzzleHttp\Message\RequestInterface; +use GuzzleHttp\Event\BeforeEvent; +use GuzzleHttp\Post\PostBodyInterface; +use GuzzleHttp\Stream\StreamInterface; +use GuzzleHttp\Stream\MetadataStreamInterface; +use GuzzleHttp\Mimetypes; + +/** + * Prepares requests with a body before sending + * + * **Request Options** + * + * - expect: Set to true to enable the "Expect: 100-Continue" header for a + * request that send a body. Set to false to disable "Expect: 100-Continue". + * Set to a number so that the size of the payload must be greater than the + * number in order to send the Expect header. Setting to a number will send + * the Expect header for all requests in which the size of the payload cannot + * be determined or where the body is not rewindable. + */ +class Prepare implements SubscriberInterface +{ + public function getEvents() + { + return ['before' => ['onBefore', RequestEvents::PREPARE_REQUEST]]; + } + + public function onBefore(BeforeEvent $event) + { + $request = $event->getRequest(); + + // Set the appropriate Content-Type for a request if one is not set and + // there are form fields + if (!($body = $request->getBody())) { + return; + } + + $this->addContentLength($request, $body); + + if ($body instanceof PostBodyInterface) { + // Synchronize the POST body with the request's headers + $body->applyRequestHeaders($request); + } elseif (!$request->hasHeader('Content-Type')) { + $this->addContentType($request, $body); + } + + $this->addExpectHeader($request, $body); + } + + private function addContentType( + RequestInterface $request, + StreamInterface $body + ) { + if (!($body instanceof MetadataStreamInterface)) { + return; + } + + if (!($uri = $body->getMetadata('uri'))) { + return; + } + + // Guess the content-type based on the stream's "uri" metadata value. + // The file extension is used to determine the appropriate mime-type. + if ($contentType = Mimetypes::getInstance()->fromFilename($uri)) { + $request->setHeader('Content-Type', $contentType); + } + } + + private function addContentLength( + RequestInterface $request, + StreamInterface $body + ) { + // Set the Content-Length header if it can be determined, and never + // send a Transfer-Encoding: chunked and Content-Length header in + // the same request. + if ($request->hasHeader('Content-Length')) { + // Remove transfer-encoding if content-length is set. + $request->removeHeader('Transfer-Encoding'); + return; + } + + if ($request->hasHeader('Transfer-Encoding')) { + return; + } + + if (null !== ($size = $body->getSize())) { + $request->setHeader('Content-Length', $size) + ->removeHeader('Transfer-Encoding'); + } elseif ('1.1' == $request->getProtocolVersion()) { + // Use chunked Transfer-Encoding if there is no determinable + // content-length header and we're using HTTP/1.1. + $request->setHeader('Transfer-Encoding', 'chunked') + ->removeHeader('Content-Length'); + } + } + + private function addExpectHeader( + RequestInterface $request, + StreamInterface $body + ) { + // Determine if the Expect header should be used + if ($request->hasHeader('Expect')) { + return; + } + + $expect = $request->getConfig()['expect']; + + // Return if disabled or if you're not using HTTP/1.1 + if ($expect === false || $request->getProtocolVersion() !== '1.1') { + return; + } + + // The expect header is unconditionally enabled + if ($expect === true) { + $request->setHeader('Expect', '100-Continue'); + return; + } + + // By default, send the expect header when the payload is > 1mb + if ($expect === null) { + $expect = 1048576; + } + + // Always add if the body cannot be rewound, the size cannot be + // determined, or the size is greater than the cutoff threshold + $size = $body->getSize(); + if ($size === null || $size >= (int) $expect || !$body->isSeekable()) { + $request->setHeader('Expect', '100-Continue'); + } + } +} diff --git a/core/vendor/guzzlehttp/guzzle/src/Subscriber/Redirect.php b/core/vendor/guzzlehttp/guzzle/src/Subscriber/Redirect.php new file mode 100644 index 0000000000000000000000000000000000000000..a8962091ea7cb050f70acefb541efbe85de9c540 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/Subscriber/Redirect.php @@ -0,0 +1,171 @@ +<?php + +namespace GuzzleHttp\Subscriber; + +use GuzzleHttp\Event\RequestEvents; +use GuzzleHttp\Event\SubscriberInterface; +use GuzzleHttp\Event\CompleteEvent; +use GuzzleHttp\Exception\TooManyRedirectsException; +use GuzzleHttp\Exception\CouldNotRewindStreamException; +use GuzzleHttp\Message\RequestInterface; +use GuzzleHttp\Message\ResponseInterface; +use GuzzleHttp\Url; + +/** + * Subscriber used to implement HTTP redirects. + * + * **Request options** + * + * - redirect: Associative array containing the 'max', 'strict', and 'referer' + * keys. + * + * - max: Maximum number of redirects allowed per-request + * - strict: You can use strict redirects by setting this value to ``true``. + * Strict redirects adhere to strict RFC compliant redirection (e.g., + * redirect POST with POST) vs doing what most clients do (e.g., redirect + * POST request with a GET request). + * - referer: Set to true to automatically add the "Referer" header when a + * redirect request is sent. + */ +class Redirect implements SubscriberInterface +{ + public function getEvents() + { + return ['complete' => ['onComplete', RequestEvents::REDIRECT_RESPONSE]]; + } + + /** + * Rewind the entity body of the request if needed + * + * @param RequestInterface $redirectRequest + * @throws CouldNotRewindStreamException + */ + public static function rewindEntityBody(RequestInterface $redirectRequest) + { + // Rewind the entity body of the request if needed + if ($redirectRequest->getBody()) { + $body = $redirectRequest->getBody(); + // Only rewind the body if some of it has been read already, and + // throw an exception if the rewind fails + if ($body->tell() && !$body->seek(0)) { + throw new CouldNotRewindStreamException( + 'Unable to rewind the non-seekable request body after redirecting', + $redirectRequest + ); + } + } + } + + /** + * Called when a request receives a redirect response + * + * @param CompleteEvent $event Event emitted + * @throws TooManyRedirectsException + */ + public function onComplete(CompleteEvent $event) + { + $response = $event->getResponse(); + + if (substr($response->getStatusCode(), 0, 1) != '3' || + !$response->hasHeader('Location') + ) { + return; + } + + $redirectCount = 0; + $request = $event->getRequest(); + $redirectResponse = $response; + $max = $request->getConfig()->getPath('redirect/max') ?: 5; + + do { + if (++$redirectCount > $max) { + throw new TooManyRedirectsException( + "Will not follow more than {$redirectCount} redirects", + $request + ); + } + $redirectRequest = $this->createRedirectRequest($request, $redirectResponse); + $redirectResponse = $event->getClient()->send($redirectRequest); + } while (substr($redirectResponse->getStatusCode(), 0, 1) == '3' && + $redirectResponse->hasHeader('Location') + ); + + if ($redirectResponse !== $response) { + $event->intercept($redirectResponse); + } + } + + /** + * Create a redirect request for a specific request object + * + * Takes into account strict RFC compliant redirection (e.g. redirect POST + * with POST) vs doing what most clients do (e.g. redirect POST with GET). + * + * @param RequestInterface $request + * @param ResponseInterface $response + * + * @return RequestInterface Returns a new redirect request + * @throws CouldNotRewindStreamException If the body cannot be rewound. + */ + private function createRedirectRequest( + RequestInterface $request, + ResponseInterface $response + ) { + $config = $request->getConfig(); + + // Use a GET request if this is an entity enclosing request and we are + // not forcing RFC compliance, but rather emulating what all browsers + // would do. Be sure to disable redirects on the clone. + $redirectRequest = clone $request; + $redirectRequest->getEmitter()->detach($this); + + if ($request->getBody() && + !$config->getPath('redirect/strict') && + $response->getStatusCode() <= 302 + ) { + $redirectRequest->setMethod('GET'); + $redirectRequest->setBody(null); + } + + $this->setRedirectUrl($redirectRequest, $response); + $this->rewindEntityBody($redirectRequest); + + // Add the Referer header if it is told to do so and only + // add the header if we are not redirecting from https to http. + if ($config->getPath('redirect/referer') && ( + $redirectRequest->getScheme() == 'https' || + $redirectRequest->getScheme() == $request->getScheme() + )) { + $url = Url::fromString($request->getUrl()); + $url->setUsername(null)->setPassword(null); + $redirectRequest->setHeader('Referer', (string) $url); + } + + return $redirectRequest; + } + + /** + * Set the appropriate URL on the request based on the location header + * + * @param RequestInterface $redirectRequest + * @param ResponseInterface $response + */ + private function setRedirectUrl( + RequestInterface $redirectRequest, + ResponseInterface $response + ) { + $location = $response->getHeader('Location'); + $location = Url::fromString($location); + + // Combine location with the original URL if it is not absolute. + if (!$location->isAbsolute()) { + $originalUrl = Url::fromString($redirectRequest->getUrl()); + // Remove query string parameters and just take what is present on + // the redirect Location header + $originalUrl->getQuery()->clear(); + $location = $originalUrl->combine($location); + } + + $redirectRequest->setUrl($location); + } +} diff --git a/core/vendor/guzzle/common/Guzzle/Common/ToArrayInterface.php b/core/vendor/guzzlehttp/guzzle/src/ToArrayInterface.php similarity index 89% rename from core/vendor/guzzle/common/Guzzle/Common/ToArrayInterface.php rename to core/vendor/guzzlehttp/guzzle/src/ToArrayInterface.php index 245328c1b3a851be20063dc2619a804a2ead1a26..7c4120fbca6c89d19e0c8cf4b5f5ba08b67caf88 100644 --- a/core/vendor/guzzle/common/Guzzle/Common/ToArrayInterface.php +++ b/core/vendor/guzzlehttp/guzzle/src/ToArrayInterface.php @@ -1,6 +1,6 @@ <?php -namespace Guzzle\Common; +namespace GuzzleHttp; /** * An object that can be represented as an array diff --git a/core/vendor/guzzle/parser/Guzzle/Parser/UriTemplate/UriTemplate.php b/core/vendor/guzzlehttp/guzzle/src/UriTemplate.php similarity index 58% rename from core/vendor/guzzle/parser/Guzzle/Parser/UriTemplate/UriTemplate.php rename to core/vendor/guzzlehttp/guzzle/src/UriTemplate.php index 2044810b5d46005f2dd6e920176c99cf8ce907f6..785ff3da8e87e61058946f1c40b8b10db629eb8b 100644 --- a/core/vendor/guzzle/parser/Guzzle/Parser/UriTemplate/UriTemplate.php +++ b/core/vendor/guzzlehttp/guzzle/src/UriTemplate.php @@ -1,13 +1,13 @@ <?php -namespace Guzzle\Parser\UriTemplate; +namespace GuzzleHttp; /** - * Expands URI templates using an array of variables + * Expands URI templates. Userland implementation of PECL uri_template. * - * @link http://tools.ietf.org/html/draft-gregorio-uritemplate-08 + * @link http://tools.ietf.org/html/rfc6570 */ -class UriTemplate implements UriTemplateInterface +class UriTemplate { /** @var string URI template */ private $template; @@ -15,36 +15,41 @@ class UriTemplate implements UriTemplateInterface /** @var array Variables to use in the template expansion */ private $variables; - /** @var string Regex used to parse expressions */ - private static $regex = '/\{([^\}]+)\}/'; - /** @var array Hash for quick operator lookups */ private static $operatorHash = array( - '+' => true, '#' => true, '.' => true, '/' => true, ';' => true, '?' => true, '&' => true + '' => array('prefix' => '', 'joiner' => ',', 'query' => false), + '+' => array('prefix' => '', 'joiner' => ',', 'query' => false), + '#' => array('prefix' => '#', 'joiner' => ',', 'query' => false), + '.' => array('prefix' => '.', 'joiner' => '.', 'query' => false), + '/' => array('prefix' => '/', 'joiner' => '/', 'query' => false), + ';' => array('prefix' => ';', 'joiner' => ';', 'query' => true), + '?' => array('prefix' => '?', 'joiner' => '&', 'query' => true), + '&' => array('prefix' => '&', 'joiner' => '&', 'query' => true) ); /** @var array Delimiters */ - private static $delims = array( - ':', '/', '?', '#', '[', ']', '@', '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=' - ); + private static $delims = array(':', '/', '?', '#', '[', ']', '@', '!', '$', + '&', '\'', '(', ')', '*', '+', ',', ';', '='); /** @var array Percent encoded delimiters */ - private static $delimsPct = array( - '%3A', '%2F', '%3F', '%23', '%5B', '%5D', '%40', '%21', '%24', '%26', '%27', '%28', '%29', '%2A', '%2B', '%2C', - '%3B', '%3D' - ); + private static $delimsPct = array('%3A', '%2F', '%3F', '%23', '%5B', '%5D', + '%40', '%21', '%24', '%26', '%27', '%28', '%29', '%2A', '%2B', '%2C', + '%3B', '%3D'); public function expand($template, array $variables) { + if (false === strpos($template, '{')) { + return $template; + } + $this->template = $template; $this->variables = $variables; - // Check to ensure that the preg_* function is needed - if (false === strpos($this->template, '{')) { - return $this->template; - } - - return preg_replace_callback(self::$regex, array($this, 'expandMatch'), $this->template); + return preg_replace_callback( + '/\{([^\}]+)\}/', + [$this, 'expandMatch'], + $this->template + ); } /** @@ -56,23 +61,22 @@ public function expand($template, array $variables) */ private function parseExpression($expression) { - // Check for URI operators - $operator = ''; + $result = array(); if (isset(self::$operatorHash[$expression[0]])) { - $operator = $expression[0]; + $result['operator'] = $expression[0]; $expression = substr($expression, 1); + } else { + $result['operator'] = ''; } - $values = explode(',', $expression); - foreach ($values as &$value) { + foreach (explode(',', $expression) as $value) { $value = trim($value); $varspec = array(); - $substrPos = strpos($value, ':'); - if ($substrPos) { - $varspec['value'] = substr($value, 0, $substrPos); + if ($colonPos = strpos($value, ':')) { + $varspec['value'] = substr($value, 0, $colonPos); $varspec['modifier'] = ':'; - $varspec['position'] = (int) substr($value, $substrPos + 1); + $varspec['position'] = (int) substr($value, $colonPos + 1); } elseif (substr($value, -1) == '*') { $varspec['modifier'] = '*'; $varspec['value'] = substr($value, 0, -1); @@ -80,13 +84,10 @@ private function parseExpression($expression) $varspec['value'] = (string) $value; $varspec['modifier'] = ''; } - $value = $varspec; + $result['values'][] = $varspec; } - return array( - 'operator' => $operator, - 'values' => $values - ); + return $result; } /** @@ -98,39 +99,22 @@ private function parseExpression($expression) */ private function expandMatch(array $matches) { - static $rfc1738to3986 = array( - '+' => '%20', - '%7e' => '~' - ); + static $rfc1738to3986 = array('+' => '%20', '%7e' => '~'); - $parsed = self::parseExpression($matches[1]); $replacements = array(); - - $prefix = $parsed['operator']; - $joiner = $parsed['operator']; - $useQueryString = false; - if ($parsed['operator'] == '?') { - $joiner = '&'; - $useQueryString = true; - } elseif ($parsed['operator'] == '&') { - $useQueryString = true; - } elseif ($parsed['operator'] == '#') { - $joiner = ','; - } elseif ($parsed['operator'] == ';') { - $useQueryString = true; - } elseif ($parsed['operator'] == '' || $parsed['operator'] == '+') { - $joiner = ','; - $prefix = ''; - } + $parsed = self::parseExpression($matches[1]); + $prefix = self::$operatorHash[$parsed['operator']]['prefix']; + $joiner = self::$operatorHash[$parsed['operator']]['joiner']; + $useQuery = self::$operatorHash[$parsed['operator']]['query']; foreach ($parsed['values'] as $value) { - if (!array_key_exists($value['value'], $this->variables) || $this->variables[$value['value']] === null) { + if (!isset($this->variables[$value['value']])) { continue; } $variable = $this->variables[$value['value']]; - $actuallyUseQueryString = $useQueryString; + $actuallyUseQuery = $useQuery; $expanded = ''; if (is_array($variable)) { @@ -148,7 +132,9 @@ private function expandMatch(array $matches) if (!$isNestedArray) { $var = rawurlencode($var); - if ($parsed['operator'] == '+' || $parsed['operator'] == '#') { + if ($parsed['operator'] == '+' || + $parsed['operator'] == '#' + ) { $var = $this->decodeReserved($var); } } @@ -156,12 +142,16 @@ private function expandMatch(array $matches) if ($value['modifier'] == '*') { if ($isAssoc) { if ($isNestedArray) { - // Nested arrays must allow for deeply nested structures - $var = strtr(http_build_query(array($key => $var)), $rfc1738to3986); + // Nested arrays must allow for deeply nested + // structures. + $var = strtr( + http_build_query([$key => $var]), + $rfc1738to3986 + ); } else { $var = $key . '=' . $var; } - } elseif ($key > 0 && $actuallyUseQueryString) { + } elseif ($key > 0 && $actuallyUseQuery) { $var = $value['value'] . '=' . $var; } } @@ -170,17 +160,20 @@ private function expandMatch(array $matches) } if (empty($variable)) { - $actuallyUseQueryString = false; + $actuallyUseQuery = false; } elseif ($value['modifier'] == '*') { $expanded = implode($joiner, $kvp); if ($isAssoc) { - // Don't prepend the value name when using the explode modifier with an associative array - $actuallyUseQueryString = false; + // Don't prepend the value name when using the explode + // modifier with an associative array. + $actuallyUseQuery = false; } } else { if ($isAssoc) { - // When an associative array is encountered and the explode modifier is not set, then the - // result must be a comma separated list of keys followed by their respective values. + // When an associative array is encountered and the + // explode modifier is not set, then the result must be + // a comma separated list of keys followed by their + // respective values. foreach ($kvp as $k => &$v) { $v = $k . ',' . $v; } @@ -198,7 +191,7 @@ private function expandMatch(array $matches) } } - if ($actuallyUseQueryString) { + if ($actuallyUseQuery) { if (!$expanded && $joiner != '&') { $expanded = $value['value']; } else { @@ -218,7 +211,12 @@ private function expandMatch(array $matches) } /** - * Determines if an array is associative + * Determines if an array is associative. + * + * This makes the assumption that input arrays are sequences or hashes. + * This assumption is a tradeoff for accuracy in favor of speed, but it + * should work in almost every case where input is supplied for a URI + * template. * * @param array $array Array to check * @@ -226,11 +224,12 @@ private function expandMatch(array $matches) */ private function isAssoc(array $array) { - return (bool) count(array_filter(array_keys($array), 'is_string')); + return $array && array_keys($array)[0] !== 0; } /** - * Removes percent encoding on reserved characters (used with + and # modifiers) + * Removes percent encoding on reserved characters (used with + and # + * modifiers). * * @param string $string String to fix * diff --git a/core/vendor/guzzle/http/Guzzle/Http/Url.php b/core/vendor/guzzlehttp/guzzle/src/Url.php similarity index 53% rename from core/vendor/guzzle/http/Guzzle/Http/Url.php rename to core/vendor/guzzlehttp/guzzle/src/Url.php index b9b87c634fe58eba153b5954f1ba7c7ab775ca16..6bf68e8e79cd94c33e93bba7a1e0237e3ec74280 100644 --- a/core/vendor/guzzle/http/Guzzle/Http/Url.php +++ b/core/vendor/guzzlehttp/guzzle/src/Url.php @@ -1,25 +1,23 @@ <?php -namespace Guzzle\Http; - -use Guzzle\Common\Exception\InvalidArgumentException; -use Guzzle\Parser\ParserRegistry; +namespace GuzzleHttp; /** - * Parses and generates URLs based on URL parts. In favor of performance, URL parts are not validated. + * Parses and generates URLs based on URL parts */ class Url { - protected $scheme; - protected $host; - protected $port; - protected $username; - protected $password; - protected $path = ''; - protected $fragment; - - /** @var QueryString Query part of the URL */ - protected $query; + private $scheme; + private $host; + private $port; + private $username; + private $password; + private $path = ''; + private $fragment; + private static $defaultPorts = ['http' => 80, 'https' => 443, 'ftp' => 21]; + + /** @var Query Query part of the URL */ + private $query; /** * Factory method to create a new URL from a URL string @@ -27,31 +25,34 @@ class Url * @param string $url Full URL used to create a Url object * * @return Url - * @throws InvalidArgumentException + * @throws \InvalidArgumentException */ - public static function factory($url) + public static function fromString($url) { - static $defaults = array('scheme' => null, 'host' => null, 'path' => null, 'port' => null, 'query' => null, + static $defaults = array('scheme' => null, 'host' => null, + 'path' => null, 'port' => null, 'query' => null, 'user' => null, 'pass' => null, 'fragment' => null); if (false === ($parts = parse_url($url))) { - throw new InvalidArgumentException('Was unable to parse malformed url: ' . $url); + throw new \InvalidArgumentException('Unable to parse malformed ' + . 'url: ' . $url); } $parts += $defaults; - // Convert the query string into a QueryString object + // Convert the query string into a Query object if ($parts['query'] || 0 !== strlen($parts['query'])) { - $parts['query'] = QueryString::fromString($parts['query']); + $parts['query'] = Query::fromString($parts['query']); } - return new self($parts['scheme'], $parts['host'], $parts['user'], + return new static($parts['scheme'], $parts['host'], $parts['user'], $parts['pass'], $parts['port'], $parts['path'], $parts['query'], $parts['fragment']); } /** - * Build a URL from parse_url parts. The generated URL will be a relative URL if a scheme or host are not provided. + * Build a URL from parse_url parts. The generated URL will be a relative + * URL if a scheme or host are not provided. * * @param array $parts Array of parse_url parts * @@ -61,12 +62,12 @@ public static function buildUrl(array $parts) { $url = $scheme = ''; - if (isset($parts['scheme'])) { + if (!empty($parts['scheme'])) { $scheme = $parts['scheme']; $url .= $scheme . ':'; } - if (isset($parts['host'])) { + if (!empty($parts['host'])) { $url .= '//'; if (isset($parts['user'])) { $url .= $parts['user']; @@ -79,17 +80,19 @@ public static function buildUrl(array $parts) $url .= $parts['host']; // Only include the port if it is not the default port of the scheme - if (isset($parts['port']) - && !(($scheme == 'http' && $parts['port'] == 80) || ($scheme == 'https' && $parts['port'] == 443)) + if (isset($parts['port']) && + (!isset(self::$defaultPorts[$scheme]) || + $parts['port'] != self::$defaultPorts[$scheme]) ) { $url .= ':' . $parts['port']; } } // Add the path component if present - if (isset($parts['path']) && 0 !== strlen($parts['path'])) { - // Always ensure that the path begins with '/' if set and something is before the path - if ($url && $parts['path'][0] != '/' && substr($url, -1) != '/') { + if (isset($parts['path']) && strlen($parts['path'])) { + // Always ensure that the path begins with '/' if set and something + // is before the path + if (isset($parts['host']) && $parts['path'][0] != '/') { $url .= '/'; } $url .= $parts['path']; @@ -97,7 +100,10 @@ public static function buildUrl(array $parts) // Add the query string if present if (isset($parts['query'])) { - $url .= '?' . $parts['query']; + $queryStr = (string) $parts['query']; + if ($queryStr || $queryStr === '0') { + $url .= '?' . $queryStr; + } } // Ensure that # is only added to the url if fragment contains anything. @@ -117,11 +123,19 @@ public static function buildUrl(array $parts) * @param string $password Password of the URL * @param int $port Port of the URL * @param string $path Path of the URL - * @param QueryString|array|string $query Query string of the URL + * @param Query|array|string $query Query string of the URL * @param string $fragment Fragment of the URL */ - public function __construct($scheme, $host, $username = null, $password = null, $port = null, $path = null, QueryString $query = null, $fragment = null) - { + public function __construct( + $scheme, + $host, + $username = null, + $password = null, + $port = null, + $path = null, + Query $query = null, + $fragment = null + ) { $this->scheme = $scheme; $this->host = $host; $this->port = $port; @@ -129,7 +143,7 @@ public function __construct($scheme, $host, $username = null, $password = null, $this->password = $password; $this->fragment = $fragment; if (!$query) { - $this->query = new QueryString(); + $this->query = new Query(); } else { $this->setQuery($query); } @@ -151,7 +165,7 @@ public function __clone() */ public function __toString() { - return self::buildUrl($this->getParts()); + return static::buildUrl($this->getParts()); } /** @@ -162,13 +176,13 @@ public function __toString() public function getParts() { return array( - 'scheme' => $this->scheme, - 'user' => $this->username, - 'pass' => $this->password, - 'host' => $this->host, - 'port' => $this->port, - 'path' => $this->getPath(), - 'query' => (string) $this->query ?: null, + 'scheme' => $this->scheme, + 'user' => $this->username, + 'pass' => $this->password, + 'host' => $this->host, + 'port' => $this->port, + 'path' => $this->path, + 'query' => $this->query, 'fragment' => $this->fragment, ); } @@ -212,6 +226,13 @@ public function getHost() */ public function setScheme($scheme) { + // Remove the default port if one is specified + if ($this->port && isset(self::$defaultPorts[$this->scheme]) && + self::$defaultPorts[$this->scheme] == $this->port + ) { + $this->port = null; + } + $this->scheme = $scheme; return $this; @@ -242,7 +263,10 @@ public function setPort($port) } /** - * Get the port part of the URl. Will return the default port for a given scheme if no port has been set. + * Get the port part of the URl. + * + * If no port was set, this method will return the default port for the + * scheme of the URI. * * @return int|null */ @@ -250,10 +274,8 @@ public function getPort() { if ($this->port) { return $this->port; - } elseif ($this->scheme == 'http') { - return 80; - } elseif ($this->scheme == 'https') { - return 443; + } elseif (isset(self::$defaultPorts[$this->scheme])) { + return self::$defaultPorts[$this->scheme]; } return null; @@ -262,68 +284,61 @@ public function getPort() /** * Set the path part of the URL * - * @param array|string $path Path string or array of path segments + * @param string $path Path string to set * * @return Url */ public function setPath($path) { - if (is_array($path)) { - $this->path = '/' . implode('/', $path); - } else { - $this->path = (string) $path; - } + static $search = [' ', '?']; + static $replace = ['%20', '%3F']; + $this->path = str_replace($search, $replace, $path); return $this; } /** - * Normalize the URL so that double slashes and relative paths are removed + * Removes dot segments from a URL * * @return Url + * @link http://tools.ietf.org/html/rfc3986#section-5.2.4 */ - public function normalizePath() + public function removeDotSegments() { - if (!$this->path || $this->path == '/' || $this->path == '*') { + static $noopPaths = ['' => true, '/' => true, '*' => true]; + static $ignoreSegments = ['' => true, '.' => true, '..' => true]; + + if (isset($noopPaths[$this->path])) { return $this; } - // Replace // and /./ with / - $this->path = str_replace(array('/./', '//'), '/', $this->path); - - // Remove dot segments - if (strpos($this->path, '..') !== false) { - - // Remove trailing relative paths if possible - $segments = $this->getPathSegments(); - $last = end($segments); - $trailingSlash = false; - if ($last === '') { - array_pop($segments); - $trailingSlash = true; + $results = []; + $segments = $this->getPathSegments(); + foreach ($segments as $segment) { + if ($segment == '..') { + array_pop($results); + } elseif (!isset($ignoreSegments[$segment])) { + $results[] = $segment; } + } - while ($last == '..' || $last == '.') { - if ($last == '..') { - array_pop($segments); - $last = array_pop($segments); - } - if ($last == '.' || $last == '') { - $last = array_pop($segments); - } - } + // Combine the normalized parts and add the leading slash if needed + if ($this->path[0] == '/') { + $this->path = '/' . implode('/', $results); + } else { + $this->path = implode('/', $results); + } - $this->path = implode('/', $segments); - if ($trailingSlash) { - $this->path .= '/'; - } + // Add the trailing slash if necessary + if ($this->path != '/' && isset($ignoreSegments[end($segments)])) { + $this->path .= '/'; } return $this; } /** - * Add a relative path to the currently set path + * Add a relative path to the currently set path. * * @param string $relativePath Relative path to add * @@ -331,16 +346,18 @@ public function normalizePath() */ public function addPath($relativePath) { - if (!$relativePath || $relativePath == '/') { - return $this; - } - - // Add a leading slash if needed - if ($relativePath[0] != '/') { - $relativePath = '/' . $relativePath; + if ($relativePath != '/' && + is_string($relativePath) && + strlen($relativePath) > 0 + ) { + // Add a leading slash if needed + if ($relativePath[0] != '/') { + $relativePath = '/' . $relativePath; + } + $this->setPath(str_replace('//', '/', $this->path . $relativePath)); } - return $this->setPath(str_replace('//', '/', $this->getPath() . $relativePath)); + return $this; } /** @@ -360,7 +377,7 @@ public function getPath() */ public function getPathSegments() { - return array_slice(explode('/', $this->getPath()), 1); + return explode('/', $this->path); } /** @@ -412,9 +429,9 @@ public function getUsername() } /** - * Get the query part of the URL as a QueryString object + * Get the query part of the URL as a Query object * - * @return QueryString + * @return Query */ public function getQuery() { @@ -424,20 +441,24 @@ public function getQuery() /** * Set the query part of the URL * - * @param QueryString|string|array $query Query to set + * @param Query|string|array $query Query string value to set. Can + * be a string that will be parsed into a Query object, an array + * of key value pairs, or a Query object. * * @return Url + * @throws \InvalidArgumentException */ public function setQuery($query) { - if (is_string($query)) { - $output = null; - parse_str($query, $output); - $this->query = new QueryString($output); - } elseif (is_array($query)) { - $this->query = new QueryString($query); - } elseif ($query instanceof QueryString) { + if ($query instanceof Query) { $this->query = $query; + } elseif (is_string($query)) { + $this->query = Query::fromString($query); + } elseif (is_array($query)) { + $this->query = new Query($query); + } else { + throw new \InvalidArgumentException('Query must be a ' + . 'QueryInterface, array, or string'); } return $this; @@ -478,66 +499,87 @@ public function isAbsolute() } /** - * Combine the URL with another URL. Follows the rules specific in RFC 3986 section 5.4. + * Combine the URL with another URL and return a new URL instance. + * + * Follows the rules specific in RFC 3986 section 5.4. * * @param string $url Relative URL to combine with * * @return Url - * @throws InvalidArgumentException + * @throws \InvalidArgumentException * @link http://tools.ietf.org/html/rfc3986#section-5.4 */ public function combine($url) { - $url = self::factory($url); + $url = static::fromString($url); // Use the more absolute URL as the base URL if (!$this->isAbsolute() && $url->isAbsolute()) { $url = $url->combine($this); } + $parts = $url->getParts(); + // Passing a URL with a scheme overrides everything - if ($buffer = $url->getScheme()) { - $this->scheme = $buffer; - $this->host = $url->getHost(); - $this->port = $url->getPort(); - $this->username = $url->getUsername(); - $this->password = $url->getPassword(); - $this->path = $url->getPath(); - $this->query = $url->getQuery(); - $this->fragment = $url->getFragment(); - return $this; + if ($parts['scheme']) { + return new static( + $parts['scheme'], + $parts['host'], + $parts['user'], + $parts['pass'], + $parts['port'], + $parts['path'], + clone $parts['query'], + $parts['fragment'] + ); } // Setting a host overrides the entire rest of the URL - if ($buffer = $url->getHost()) { - $this->host = $buffer; - $this->port = $url->getPort(); - $this->username = $url->getUsername(); - $this->password = $url->getPassword(); - $this->path = $url->getPath(); - $this->fragment = $url->getFragment(); - return $this; + if ($parts['host']) { + return new static( + $this->scheme, + $parts['host'], + $parts['user'], + $parts['pass'], + $parts['port'], + $parts['path'], + clone $parts['query'], + $parts['fragment'] + ); } - $path = $url->getPath(); - $query = $url->getQuery(); - - if (!$path) { - if (count($query)) { - $this->query = $query; - } + if (!$parts['path']) { + // The relative URL has no path, so check if it is just a query + $path = $this->path ?: ''; + $query = count($parts['query']) ? $parts['query'] : $this->query; } else { - if ($path[0] == '/') { - $this->path = $path; + $query = $parts['query']; + if ($parts['path'][0] == '/' || !$this->path) { + // Overwrite the existing path if the rel path starts with "/" + $path = $parts['path']; } else { - $this->path .= '/' . $path; + // If the relative URL does not have a path or the base URL + // path does not end in a "/" then overwrite the existing path + // up to the last "/" + $path = substr($this->path, 0, strrpos($this->path, '/') + 1) . $parts['path']; } - $this->normalizePath(); - $this->query = $query; } - $this->fragment = $url->getFragment(); + $result = new self( + $this->scheme, + $this->host, + $this->username, + $this->password, + $this->port, + $path, + clone $query, + $parts['fragment'] + ); - return $this; + if ($path) { + $result->removeDotSegments(); + } + + return $result; } } diff --git a/core/vendor/guzzle/http/Guzzle/Http/Resources/cacert.pem b/core/vendor/guzzlehttp/guzzle/src/cacert.pem similarity index 88% rename from core/vendor/guzzle/http/Guzzle/Http/Resources/cacert.pem rename to core/vendor/guzzlehttp/guzzle/src/cacert.pem index 99b310bce9129a7d5ec72ef8e8aa22626a1a6d9c..67f696abc44075dcb18122ffef5ca89eb5a8e349 100644 --- a/core/vendor/guzzle/http/Guzzle/Http/Resources/cacert.pem +++ b/core/vendor/guzzlehttp/guzzle/src/cacert.pem @@ -1,12 +1,12 @@ ## ## ca-bundle.crt -- Bundle of CA Root Certificates ## -## Certificate data from Mozilla as of: Sat Dec 29 20:03:40 2012 +## Certificate data from Mozilla as of: Tue Jan 28 09:38:07 2014 ## ## This is a bundle of X.509 certificates of public Certificate Authorities ## (CA). These were automatically extracted from Mozilla's root certificates ## file (certdata.txt). This file can be found in the mozilla source tree: -## http://mxr.mozilla.org/mozilla/source/security/nss/lib/ckfw/builtins/certdata.txt?raw=1 +## http://mxr.mozilla.org/mozilla-release/source/security/nss/lib/ckfw/builtins/certdata.txt?raw=1 ## ## It contains the certificates in PEM format and therefore ## can be directly used with curl / libcurl / php_curl, or with @@ -14,7 +14,6 @@ ## Just configure this file as the SSLCACertificateFile. ## -# @(#) $RCSfile: certdata.txt,v $ $Revision: 1.87 $ $Date: 2012/12/29 16:32:45 $ GTE CyberTrust Global Root ========================== @@ -91,46 +90,6 @@ BIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee95 70+sB3c4 -----END CERTIFICATE----- -Digital Signature Trust Co. Global CA 1 -======================================= ------BEGIN CERTIFICATE----- -MIIDKTCCApKgAwIBAgIENnAVljANBgkqhkiG9w0BAQUFADBGMQswCQYDVQQGEwJVUzEkMCIGA1UE -ChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMREwDwYDVQQLEwhEU1RDQSBFMTAeFw05ODEy -MTAxODEwMjNaFw0xODEyMTAxODQwMjNaMEYxCzAJBgNVBAYTAlVTMSQwIgYDVQQKExtEaWdpdGFs -IFNpZ25hdHVyZSBUcnVzdCBDby4xETAPBgNVBAsTCERTVENBIEUxMIGdMA0GCSqGSIb3DQEBAQUA -A4GLADCBhwKBgQCgbIGpzzQeJN3+hijM3oMv+V7UQtLodGBmE5gGHKlREmlvMVW5SXIACH7TpWJE -NySZj9mDSI+ZbZUTu0M7LklOiDfBu1h//uG9+LthzfNHwJmm8fOR6Hh8AMthyUQncWlVSn5JTe2i -o74CTADKAqjuAQIxZA9SLRN0dja1erQtcQIBA6OCASQwggEgMBEGCWCGSAGG+EIBAQQEAwIABzBo -BgNVHR8EYTBfMF2gW6BZpFcwVTELMAkGA1UEBhMCVVMxJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0 -dXJlIFRydXN0IENvLjERMA8GA1UECxMIRFNUQ0EgRTExDTALBgNVBAMTBENSTDEwKwYDVR0QBCQw -IoAPMTk5ODEyMTAxODEwMjNagQ8yMDE4MTIxMDE4MTAyM1owCwYDVR0PBAQDAgEGMB8GA1UdIwQY -MBaAFGp5fpFpRhgTCgJ3pVlbYJglDqL4MB0GA1UdDgQWBBRqeX6RaUYYEwoCd6VZW2CYJQ6i+DAM -BgNVHRMEBTADAQH/MBkGCSqGSIb2fQdBAAQMMAobBFY0LjADAgSQMA0GCSqGSIb3DQEBBQUAA4GB -ACIS2Hod3IEGtgllsofIH160L+nEHvI8wbsEkBFKg05+k7lNQseSJqBcNJo4cvj9axY+IO6CizEq -kzaFI4iKPANo08kJD038bKTaKHKTDomAsH3+gG9lbRgzl4vCa4nuYD3Im+9/KzJic5PLPON74nZ4 -RbyhkwS7hp86W0N6w4pl ------END CERTIFICATE----- - -Digital Signature Trust Co. Global CA 3 -======================================= ------BEGIN CERTIFICATE----- -MIIDKTCCApKgAwIBAgIENm7TzjANBgkqhkiG9w0BAQUFADBGMQswCQYDVQQGEwJVUzEkMCIGA1UE -ChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMREwDwYDVQQLEwhEU1RDQSBFMjAeFw05ODEy -MDkxOTE3MjZaFw0xODEyMDkxOTQ3MjZaMEYxCzAJBgNVBAYTAlVTMSQwIgYDVQQKExtEaWdpdGFs -IFNpZ25hdHVyZSBUcnVzdCBDby4xETAPBgNVBAsTCERTVENBIEUyMIGdMA0GCSqGSIb3DQEBAQUA -A4GLADCBhwKBgQC/k48Xku8zExjrEH9OFr//Bo8qhbxe+SSmJIi2A7fBw18DW9Fvrn5C6mYjuGOD -VvsoLeE4i7TuqAHhzhy2iCoiRoX7n6dwqUcUP87eZfCocfdPJmyMvMa1795JJ/9IKn3oTQPMx7JS -xhcxEzu1TdvIxPbDDyQq2gyd55FbgM2UnQIBA6OCASQwggEgMBEGCWCGSAGG+EIBAQQEAwIABzBo -BgNVHR8EYTBfMF2gW6BZpFcwVTELMAkGA1UEBhMCVVMxJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0 -dXJlIFRydXN0IENvLjERMA8GA1UECxMIRFNUQ0EgRTIxDTALBgNVBAMTBENSTDEwKwYDVR0QBCQw -IoAPMTk5ODEyMDkxOTE3MjZagQ8yMDE4MTIwOTE5MTcyNlowCwYDVR0PBAQDAgEGMB8GA1UdIwQY -MBaAFB6CTShlgDzJQW6sNS5ay97u+DlbMB0GA1UdDgQWBBQegk0oZYA8yUFurDUuWsve7vg5WzAM -BgNVHRMEBTADAQH/MBkGCSqGSIb2fQdBAAQMMAobBFY0LjADAgSQMA0GCSqGSIb3DQEBBQUAA4GB -AEeNg61i8tuwnkUiBbmi1gMOOHLnnvx75pO2mqWilMg0HZHRxdf0CiUPPXiBng+xZ8SQTGPdXqfi -up/1902lMXucKS1M/mQ+7LZT/uqb7YLbdHVLB3luHtgZg3Pe9T7Qtd7nS2h9Qy4qIOF+oHhEngj1 -mPnHfxsb1gYgAlihw6ID ------END CERTIFICATE----- - Verisign Class 3 Public Primary Certification Authority ======================================================= -----BEGIN CERTIFICATE----- @@ -147,44 +106,6 @@ WM1pF+NEHJwZRDmJXNycAA9WjQKZ7aKQRUzkuxCkPfAyAw7xzvjoyVGM5mKf5p/AfbdynMk2Omuf Tqj/ZA1k -----END CERTIFICATE----- -Verisign Class 1 Public Primary Certification Authority - G2 -============================================================ ------BEGIN CERTIFICATE----- -MIIDAjCCAmsCEEzH6qqYPnHTkxD4PTqJkZIwDQYJKoZIhvcNAQEFBQAwgcExCzAJBgNVBAYTAlVT -MRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMSBQdWJsaWMgUHJpbWFy -eSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2ln -biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVz -dCBOZXR3b3JrMB4XDTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVowgcExCzAJBgNVBAYTAlVT -MRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMSBQdWJsaWMgUHJpbWFy -eSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2ln -biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVz -dCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCq0Lq+Fi24g9TK0g+8djHKlNgd -k4xWArzZbxpvUjZudVYKVdPfQ4chEWWKfo+9Id5rMj8bhDSVBZ1BNeuS65bdqlk/AVNtmU/t5eIq -WpDBucSmFc/IReumXY6cPvBkJHalzasab7bYe1FhbqZ/h8jit+U03EGI6glAvnOSPWvndQIDAQAB -MA0GCSqGSIb3DQEBBQUAA4GBAKlPww3HZ74sy9mozS11534Vnjty637rXC0Jh9ZrbWB85a7FkCMM -XErQr7Fd88e2CtvgFZMN3QO8x3aKtd1Pw5sTdbgBwObJW2uluIncrKTdcu1OofdPvAbT6shkdHvC -lUGcZXNY8ZCaPGqxmMnEh7zPRW1F4m4iP/68DzFc6PLZ ------END CERTIFICATE----- - -Verisign Class 2 Public Primary Certification Authority - G2 -============================================================ ------BEGIN CERTIFICATE----- -MIIDAzCCAmwCEQC5L2DMiJ+hekYJuFtwbIqvMA0GCSqGSIb3DQEBBQUAMIHBMQswCQYDVQQGEwJV -UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xPDA6BgNVBAsTM0NsYXNzIDIgUHVibGljIFByaW1h -cnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMjE6MDgGA1UECxMxKGMpIDE5OTggVmVyaVNp -Z24sIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1 -c3QgTmV0d29yazAeFw05ODA1MTgwMDAwMDBaFw0yODA4MDEyMzU5NTlaMIHBMQswCQYDVQQGEwJV -UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xPDA6BgNVBAsTM0NsYXNzIDIgUHVibGljIFByaW1h -cnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMjE6MDgGA1UECxMxKGMpIDE5OTggVmVyaVNp -Z24sIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1 -c3QgTmV0d29yazCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAp4gBIXQs5xoD8JjhlzwPIQjx -nNuX6Zr8wgQGE75fUsjMHiwSViy4AWkszJkfrbCWrnkE8hM5wXuYuggs6MKEEyyqaekJ9MepAqRC -wiNPStjwDqL7MWzJ5m+ZJwf15vRMeJ5t60aG+rmGyVTyssSv1EYcWskVMP8NbPUtDm3Of3cCAwEA -ATANBgkqhkiG9w0BAQUFAAOBgQByLvl/0fFx+8Se9sVeUYpAmLho+Jscg9jinb3/7aHmZuovCfTK -1+qlK5X2JGCGTUQug6XELaDTrnhpb3LabK4I8GOSN+a7xDAXrXfMSTWqz9iP0b63GJZHc2pUIjRk -LbYWm1lbtFFZOrMLFPQS32eg9K0yZF6xRnInjBJ7xUS0rg== ------END CERTIFICATE----- - Verisign Class 3 Public Primary Certification Authority - G2 ============================================================ -----BEGIN CERTIFICATE----- @@ -304,54 +225,6 @@ V9+vqDWzf4mH6eglkrh/hXqu1rweN1gqZ8mRzyqBPu3GOd/APhmcGcwTTYJBtYze4D1gCCAPRX5r on+jjBXu -----END CERTIFICATE----- -Verisign Class 1 Public Primary Certification Authority - G3 -============================================================ ------BEGIN CERTIFICATE----- -MIIEGjCCAwICEQCLW3VWhFSFCwDPrzhIzrGkMA0GCSqGSIb3DQEBBQUAMIHKMQswCQYDVQQGEwJV -UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv -cmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl -IG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDEgUHVibGljIFByaW1hcnkgQ2VydGlmaWNh -dGlvbiBBdXRob3JpdHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQsw -CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRy -dXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhv -cml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDEgUHVibGljIFByaW1hcnkg -Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC -ggEBAN2E1Lm0+afY8wR4nN493GwTFtl63SRRZsDHJlkNrAYIwpTRMx/wgzUfbhvI3qpuFU5UJ+/E -bRrsC+MO8ESlV8dAWB6jRx9x7GD2bZTIGDnt/kIYVt/kTEkQeE4BdjVjEjbdZrwBBDajVWjVojYJ -rKshJlQGrT/KFOCsyq0GHZXi+J3x4GD/wn91K0zM2v6HmSHquv4+VNfSWXjbPG7PoBMAGrgnoeS+ -Z5bKoMWznN3JdZ7rMJpfo83ZrngZPyPpXNspva1VyBtUjGP26KbqxzcSXKMpHgLZ2x87tNcPVkeB -FQRKr4Mn0cVYiMHd9qqnoxjaaKptEVHhv2Vrn5Z20T0CAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA -q2aN17O6x5q25lXQBfGfMY1aqtmqRiYPce2lrVNWYgFHKkTp/j90CxObufRNG7LRX7K20ohcs5/N -y9Sn2WCVhDr4wTcdYcrnsMXlkdpUpqwxga6X3s0IrLjAl4B/bnKk52kTlWUfxJM8/XmPBNQ+T+r3 -ns7NZ3xPZQL/kYVUc8f/NveGLezQXk//EZ9yBta4GvFMDSZl4kSAHsef493oCtrspSCAaWihT37h -a88HQfqDjrw43bAuEbFrskLMmrz5SCJ5ShkPshw+IHTZasO+8ih4E1Z5T21Q6huwtVexN2ZYI/Pc -D98Kh8TvhgXVOBRgmaNL3gaWcSzy27YfpO8/7g== ------END CERTIFICATE----- - -Verisign Class 2 Public Primary Certification Authority - G3 -============================================================ ------BEGIN CERTIFICATE----- -MIIEGTCCAwECEGFwy0mMX5hFKeewptlQW3owDQYJKoZIhvcNAQEFBQAwgcoxCzAJBgNVBAYTAlVT -MRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29y -azE6MDgGA1UECxMxKGMpIDE5OTkgVmVyaVNpZ24sIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ug -b25seTFFMEMGA1UEAxM8VmVyaVNpZ24gQ2xhc3MgMiBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0 -aW9uIEF1dGhvcml0eSAtIEczMB4XDTk5MTAwMTAwMDAwMFoXDTM2MDcxNjIzNTk1OVowgcoxCzAJ -BgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1 -c3QgTmV0d29yazE6MDgGA1UECxMxKGMpIDE5OTkgVmVyaVNpZ24sIEluYy4gLSBGb3IgYXV0aG9y -aXplZCB1c2Ugb25seTFFMEMGA1UEAxM8VmVyaVNpZ24gQ2xhc3MgMiBQdWJsaWMgUHJpbWFyeSBD -ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEczMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC -AQEArwoNwtUs22e5LeWUJ92lvuCwTY+zYVY81nzD9M0+hsuiiOLh2KRpxbXiv8GmR1BeRjmL1Za6 -tW8UvxDOJxOeBUebMXoT2B/Z0wI3i60sR/COgQanDTAM6/c8DyAd3HJG7qUCyFvDyVZpTMUYwZF7 -C9UTAJu878NIPkZgIIUq1ZC2zYugzDLdt/1AVbJQHFauzI13TccgTacxdu9okoqQHgiBVrKtaaNS -0MscxCM9H5n+TOgWY47GCI72MfbS+uV23bUckqNJzc0BzWjNqWm6o+sdDZykIKbBoMXRRkwXbdKs -Zj+WjOCE1Db/IlnF+RFgqF8EffIa9iVCYQ/ESrg+iQIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQA0 -JhU8wI1NQ0kdvekhktdmnLfexbjQ5F1fdiLAJvmEOjr5jLX77GDx6M4EsMjdpwOPMPOY36TmpDHf -0xwLRtxyID+u7gU8pDM/CzmscHhzS5kr3zDCVLCoO1Wh/hYozUK9dG6A2ydEp85EXdQbkJgNHkKU -sQAsBNB0owIFImNjzYO1+8FtYmtpdf1dcEG59b98377BMnMiIYtYgXsVkXq642RIsH/7NiXaldDx -JBQX3RiAa0YjOVT1jmIJBB2UkKab5iXiQkWquJCtvgiPqQtCGJTPcjnhsUPgKM+351psE2tJs//j -GHyJizNdrDPXp/naOlXJWBD5qu9ats9LS98q ------END CERTIFICATE----- - Verisign Class 3 Public Primary Certification Authority - G3 ============================================================ -----BEGIN CERTIFICATE----- @@ -430,11 +303,11 @@ n9cd2cNgQ4xYDiKWL2KjLB+6rQXvqzJ4h6BUcxm1XAX5Uj5tLUUL9wqT6u0G+bI= Entrust.net Premium 2048 Secure Server CA ========================================= -----BEGIN CERTIFICATE----- -MIIEXDCCA0SgAwIBAgIEOGO5ZjANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChMLRW50cnVzdC5u +MIIEKjCCAxKgAwIBAgIEOGPe+DANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChMLRW50cnVzdC5u ZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBpbmNvcnAuIGJ5IHJlZi4gKGxp bWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNV BAMTKkVudHJ1c3QubmV0IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQx -NzUwNTFaFw0xOTEyMjQxODIwNTFaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3 +NzUwNTFaFw0yOTA3MjQxNDE1MTJaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3 d3d3LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTEl MCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5u ZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgpMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A @@ -442,14 +315,13 @@ MIIBCgKCAQEArU1LqRKGsuqjIAcVFmQqK0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOL Gp18EzoOH1u3Hs/lJBQesYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSr hRSGlVuXMlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVTXTzW nLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/HoZdenoVve8AjhUi -VBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH4QIDAQABo3QwcjARBglghkgBhvhC -AQEEBAMCAAcwHwYDVR0jBBgwFoAUVeSB0RGAvtiJuQijMfmhJAkWuXAwHQYDVR0OBBYEFFXkgdER -gL7YibkIozH5oSQJFrlwMB0GCSqGSIb2fQdBAAQQMA4bCFY1LjA6NC4wAwIEkDANBgkqhkiG9w0B -AQUFAAOCAQEAWUesIYSKF8mciVMeuoCFGsY8Tj6xnLZ8xpJdGGQC49MGCBFhfGPjK50xA3B20qMo -oPS7mmNz7W3lKtvtFKkrxjYR0CvrB4ul2p5cGZ1WEvVUKcgF7bISKo30Axv/55IQh7A6tcOdBTcS -o8f0FbnVpDkWm1M6I5HxqIKiaohowXkCIryqptau37AUX7iH0N18f3v/rxzP5tsHrV7bhZ3QKw0z -2wTR5klAEyt2+z7pnIkPFc4YsIV4IU9rTw76NmfNB/L/CNDi3tm/Kq+4h4YhPATKt5Rof8886ZjX -OP/swNlQ8C5LWK5Gb9Auw2DaclVyvUxFnmG6v4SBkgPR0ml8xQ== +VBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH4QIDAQABo0IwQDAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUVeSB0RGAvtiJuQijMfmhJAkWuXAwDQYJ +KoZIhvcNAQEFBQADggEBADubj1abMOdTmXx6eadNl9cZlZD7Bh/KM3xGY4+WZiT6QBshJ8rmcnPy +T/4xmf3IDExoU8aAghOY+rat2l098c5u9hURlIIM7j+VrxGrD9cv3h8Dj1csHsm7mhpElesYT6Yf +zX1XEC+bBAlahLVu2B064dae0Wx5XnkcFMXj0EyTO2U87d89vqbllRrDtRnDvV5bu/8j72gZyxKT +J1wDLW8w0B62GqzeWvfRqqgnpv55gcR5mTNXuhKwqeBCbJPKVt7+bYQLCIt+jerXmCHG8+c8eS9e +nNFMFY3h7CI3zJpDC5fcgJCNs2ebb0gIFVbPv/ErfF6adulZkMV8gzURZVE= -----END CERTIFICATE----- Baltimore CyberTrust Root @@ -507,26 +379,6 @@ lSE/9dR+WB5Hh1Q+WKG1tfgq73HnvMP2sUlG4tega+VWeponmHxGYhTnyfxuAxJ5gDgdSIKN/Bf+ KpYrtWKmpj29f5JZzVoqgrI3eQ== -----END CERTIFICATE----- -Equifax Secure eBusiness CA 2 -============================= ------BEGIN CERTIFICATE----- -MIIDIDCCAomgAwIBAgIEN3DPtTANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJVUzEXMBUGA1UE -ChMORXF1aWZheCBTZWN1cmUxJjAkBgNVBAsTHUVxdWlmYXggU2VjdXJlIGVCdXNpbmVzcyBDQS0y -MB4XDTk5MDYyMzEyMTQ0NVoXDTE5MDYyMzEyMTQ0NVowTjELMAkGA1UEBhMCVVMxFzAVBgNVBAoT -DkVxdWlmYXggU2VjdXJlMSYwJAYDVQQLEx1FcXVpZmF4IFNlY3VyZSBlQnVzaW5lc3MgQ0EtMjCB -nzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA5Dk5kx5SBhsoNviyoynF7Y6yEb3+6+e0dMKP/wXn -2Z0GvxLIPw7y1tEkshHe0XMJitSxLJgJDR5QRrKDpkWNYmi7hRsgcDKqQM2mll/EcTc/BPO3QSQ5 -BxoeLmFYoBIL5aXfxavqN3HMHMg3OrmXUqesxWoklE6ce8/AatbfIb0CAwEAAaOCAQkwggEFMHAG -A1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORXF1aWZheCBTZWN1cmUx -JjAkBgNVBAsTHUVxdWlmYXggU2VjdXJlIGVCdXNpbmVzcyBDQS0yMQ0wCwYDVQQDEwRDUkwxMBoG -A1UdEAQTMBGBDzIwMTkwNjIzMTIxNDQ1WjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUUJ4L6q9e -uSBIplBqy/3YIHqngnYwHQYDVR0OBBYEFFCeC+qvXrkgSKZQasv92CB6p4J2MAwGA1UdEwQFMAMB -Af8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUAA4GBAAyGgq3oThr1 -jokn4jVYPSm0B482UJW/bsGe68SQsoWou7dC4A8HOd/7npCy0cE+U58DRLB+S/Rv5Hwf5+Kx5Lia -78O9zt4LMjTZ3ijtM2vE1Nc9ElirfQkty3D1E4qUoSek1nDFbZS1yX2doNLGCEnZZpum0/QL3MUm -V+GRMOrN ------END CERTIFICATE----- - AddTrust Low-Value Services Root ================================ -----BEGIN CERTIFICATE----- @@ -772,31 +624,6 @@ gn2Z9DH2canPLAEnpQW5qrJITirvn5NSUZU8UnOOVkwXQMAJKOSLakhT2+zNVVXxxvjpoixMptEm X36vWkzaH6byHCx+rgIW0lbQL1dTR+iS -----END CERTIFICATE----- -UTN-USER First-Network Applications -=================================== ------BEGIN CERTIFICATE----- -MIIEZDCCA0ygAwIBAgIQRL4Mi1AAJLQR0zYwS8AzdzANBgkqhkiG9w0BAQUFADCBozELMAkGA1UE -BhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEeMBwGA1UEChMVVGhl -IFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xKzAp -BgNVBAMTIlVUTi1VU0VSRmlyc3QtTmV0d29yayBBcHBsaWNhdGlvbnMwHhcNOTkwNzA5MTg0ODM5 -WhcNMTkwNzA5MTg1NzQ5WjCBozELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5T -YWx0IExha2UgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho -dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xKzApBgNVBAMTIlVUTi1VU0VSRmlyc3QtTmV0d29yayBB -cHBsaWNhdGlvbnMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCz+5Gh5DZVhawGNFug -mliy+LUPBXeDrjKxdpJo7CNKyXY/45y2N3kDuatpjQclthln5LAbGHNhSuh+zdMvZOOmfAz6F4Cj -DUeJT1FxL+78P/m4FoCHiZMlIJpDgmkkdihZNaEdwH+DBmQWICzTSaSFtMBhf1EI+GgVkYDLpdXu -Ozr0hAReYFmnjDRy7rh4xdE7EkpvfmUnuaRVxblvQ6TFHSyZwFKkeEwVs0CYCGtDxgGwenv1axwi -P8vv/6jQOkt2FZ7S0cYu49tXGzKiuG/ohqY/cKvlcJKrRB5AUPuco2LkbG6gyN7igEL66S/ozjIE -j3yNtxyjNTwV3Z7DrpelAgMBAAGjgZEwgY4wCwYDVR0PBAQDAgHGMA8GA1UdEwEB/wQFMAMBAf8w -HQYDVR0OBBYEFPqGydvguul49Uuo1hXf8NPhahQ8ME8GA1UdHwRIMEYwRKBCoECGPmh0dHA6Ly9j -cmwudXNlcnRydXN0LmNvbS9VVE4tVVNFUkZpcnN0LU5ldHdvcmtBcHBsaWNhdGlvbnMuY3JsMA0G -CSqGSIb3DQEBBQUAA4IBAQCk8yXM0dSRgyLQzDKrm5ZONJFUICU0YV8qAhXhi6r/fWRRzwr/vH3Y -IWp4yy9Rb/hCHTO967V7lMPDqaAt39EpHx3+jz+7qEUqf9FuVSTiuwL7MT++6LzsQCv4AdRWOOTK -RIK1YSAhZ2X28AvnNPilwpyjXEAfhZOVBt5P1CeptqX8Fs1zMT+4ZSfP1FMa8Kxun08FDAOBp4Qp -xFq9ZFdyrTvPNximmMatBrTcCKME1SmklpoSZ0qMYEWd8SOasACcaLWYUNPvji6SZbFIPiG+FTAq -DbUMo2s/rn9X9R+WfN9v3YIwLGUbQErNaLly7HF27FSOH4UMAWr6pjisH8SE ------END CERTIFICATE----- - America Online Root Certification Authority 1 ============================================= -----BEGIN CERTIFICATE----- @@ -1084,26 +911,6 @@ s58+OmJYxUmtYg5xpTKqL8aJdkNAExNnPaJUJRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ FL39vmwLAw== -----END CERTIFICATE----- -Sonera Class 1 Root CA -====================== ------BEGIN CERTIFICATE----- -MIIDIDCCAgigAwIBAgIBJDANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEPMA0GA1UEChMG -U29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MxIENBMB4XDTAxMDQwNjEwNDkxM1oXDTIxMDQw -NjEwNDkxM1owOTELMAkGA1UEBhMCRkkxDzANBgNVBAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJh -IENsYXNzMSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALWJHytPZwp5/8Ue+H88 -7dF+2rDNbS82rDTG29lkFwhjMDMiikzujrsPDUJVyZ0upe/3p4zDq7mXy47vPxVnqIJyY1MPQYx9 -EJUkoVqlBvqSV536pQHydekfvFYmUk54GWVYVQNYwBSujHxVX3BbdyMGNpfzJLWaRpXk3w0LBUXl -0fIdgrvGE+D+qnr9aTCU89JFhfzyMlsy3uhsXR/LpCJ0sICOXZT3BgBLqdReLjVQCfOAl/QMF645 -2F/NM8EcyonCIvdFEu1eEpOdY6uCLrnrQkFEy0oaAIINnvmLVz5MxxftLItyM19yejhW1ebZrgUa -HXVFsculJRwSVzb9IjcCAwEAAaMzMDEwDwYDVR0TAQH/BAUwAwEB/zARBgNVHQ4ECgQIR+IMi/ZT -iFIwCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQCLGrLJXWG04bkruVPRsoWdd44W7hE9 -28Jj2VuXZfsSZ9gqXLar5V7DtxYvyOirHYr9qxp81V9jz9yw3Xe5qObSIjiHBxTZ/75Wtf0HDjxV -yhbMp6Z3N/vbXB9OWQaHowND9Rart4S9Tu+fMTfwRvFAttEMpWT4Y14h21VOTzF2nBBhjrZTOqMR -vq9tfB69ri3iDGnHhVNoomG6xT60eVR4ngrHAr5i0RGCS2UvkVrCqIexVmiUefkl98HVrhq4uz2P -qYo4Ffdz0Fpg0YCw8NzVUM1O7pJIae2yIx4wzMiUyLb1O4Z/P6Yun/Y+LLWSlj7fLJOK/4GMDw9Z -IRlXvVWa ------END CERTIFICATE----- - Sonera Class 2 Root CA ====================== -----BEGIN CERTIFICATE----- @@ -1170,34 +977,6 @@ O0ESm70NRyN/PErQr8Cv9u8btRXE64PECV90i9kR+8JWsTz4cMo0jUNAE4z9mQNUecYu6oah9jrU Cbz0vGbMPVjQV0kK7iXiQe4T+Zs4NNEA9X7nlB38aQNiuJkFBT1reBK9sG9l -----END CERTIFICATE----- -TDC OCES Root CA -================ ------BEGIN CERTIFICATE----- -MIIFGTCCBAGgAwIBAgIEPki9xDANBgkqhkiG9w0BAQUFADAxMQswCQYDVQQGEwJESzEMMAoGA1UE -ChMDVERDMRQwEgYDVQQDEwtUREMgT0NFUyBDQTAeFw0wMzAyMTEwODM5MzBaFw0zNzAyMTEwOTA5 -MzBaMDExCzAJBgNVBAYTAkRLMQwwCgYDVQQKEwNUREMxFDASBgNVBAMTC1REQyBPQ0VTIENBMIIB -IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArGL2YSCyz8DGhdfjeebM7fI5kqSXLmSjhFuH -nEz9pPPEXyG9VhDr2y5h7JNp46PMvZnDBfwGuMo2HP6QjklMxFaaL1a8z3sM8W9Hpg1DTeLpHTk0 -zY0s2RKY+ePhwUp8hjjEqcRhiNJerxomTdXkoCJHhNlktxmW/OwZ5LKXJk5KTMuPJItUGBxIYXvV -iGjaXbXqzRowwYCDdlCqT9HU3Tjw7xb04QxQBr/q+3pJoSgrHPb8FTKjdGqPqcNiKXEx5TukYBde -dObaE+3pHx8b0bJoc8YQNHVGEBDjkAB2QMuLt0MJIf+rTpPGWOmlgtt3xDqZsXKVSQTwtyv6e1mO -3QIDAQABo4ICNzCCAjMwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwgewGA1UdIASB -5DCB4TCB3gYIKoFQgSkBAQEwgdEwLwYIKwYBBQUHAgEWI2h0dHA6Ly93d3cuY2VydGlmaWthdC5k -ay9yZXBvc2l0b3J5MIGdBggrBgEFBQcCAjCBkDAKFgNUREMwAwIBARqBgUNlcnRpZmlrYXRlciBm -cmEgZGVubmUgQ0EgdWRzdGVkZXMgdW5kZXIgT0lEIDEuMi4yMDguMTY5LjEuMS4xLiBDZXJ0aWZp -Y2F0ZXMgZnJvbSB0aGlzIENBIGFyZSBpc3N1ZWQgdW5kZXIgT0lEIDEuMi4yMDguMTY5LjEuMS4x -LjARBglghkgBhvhCAQEEBAMCAAcwgYEGA1UdHwR6MHgwSKBGoESkQjBAMQswCQYDVQQGEwJESzEM -MAoGA1UEChMDVERDMRQwEgYDVQQDEwtUREMgT0NFUyBDQTENMAsGA1UEAxMEQ1JMMTAsoCqgKIYm -aHR0cDovL2NybC5vY2VzLmNlcnRpZmlrYXQuZGsvb2Nlcy5jcmwwKwYDVR0QBCQwIoAPMjAwMzAy -MTEwODM5MzBagQ8yMDM3MDIxMTA5MDkzMFowHwYDVR0jBBgwFoAUYLWF7FZkfhIZJ2cdUBVLc647 -+RIwHQYDVR0OBBYEFGC1hexWZH4SGSdnHVAVS3OuO/kSMB0GCSqGSIb2fQdBAAQQMA4bCFY2LjA6 -NC4wAwIEkDANBgkqhkiG9w0BAQUFAAOCAQEACromJkbTc6gJ82sLMJn9iuFXehHTuJTXCRBuo7E4 -A9G28kNBKWKnctj7fAXmMXAnVBhOinxO5dHKjHiIzxvTkIvmI/gLDjNDfZziChmPyQE+dF10yYsc -A+UYyAFMP8uXBV2YcaaYb7Z8vTd/vuGTJW1v8AqtFxjhA7wHKcitJuj4YfD9IQl+mo6paH1IYnK9 -AOoBmbgGglGBTvH1tJFUuSN6AJqfXY3gPGS5GhKSKseCRHI53OI8xthV9RVOyAUO28bQYqbsFbS1 -AoLbrIyigfCbmTH1ICCoiGEKB5+U/NDXG8wuF/MEJ3Zn61SD/aSQfgY9BKNDLdr8C2LqL19iUw== ------END CERTIFICATE----- - UTN DATACorp SGC Root CA ======================== -----BEGIN CERTIFICATE----- @@ -1223,32 +1002,6 @@ EZ4du5A/EkdOjtd+D2JzHVImOBwYSf0wdJrE5SIv2MCN7ZF6TACPcn9d2t0bi0Vr591pl6jFVkwP DPafepE39peC4N1xaf92P2BNPM/3mfnGV/TJVTl4uix5yaaIK/QI -----END CERTIFICATE----- -UTN USERFirst Email Root CA -=========================== ------BEGIN CERTIFICATE----- -MIIEojCCA4qgAwIBAgIQRL4Mi1AAJLQR0zYlJWfJiTANBgkqhkiG9w0BAQUFADCBrjELMAkGA1UE -BhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEeMBwGA1UEChMVVGhl -IFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xNjA0 -BgNVBAMTLVVUTi1VU0VSRmlyc3QtQ2xpZW50IEF1dGhlbnRpY2F0aW9uIGFuZCBFbWFpbDAeFw05 -OTA3MDkxNzI4NTBaFw0xOTA3MDkxNzM2NThaMIGuMQswCQYDVQQGEwJVUzELMAkGA1UECBMCVVQx -FzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsx -ITAfBgNVBAsTGGh0dHA6Ly93d3cudXNlcnRydXN0LmNvbTE2MDQGA1UEAxMtVVROLVVTRVJGaXJz -dC1DbGllbnQgQXV0aGVudGljYXRpb24gYW5kIEVtYWlsMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A -MIIBCgKCAQEAsjmFpPJ9q0E7YkY3rs3BYHW8OWX5ShpHornMSMxqmNVNNRm5pELlzkniii8efNIx -B8dOtINknS4p1aJkxIW9hVE1eaROaJB7HHqkkqgX8pgV8pPMyaQylbsMTzC9mKALi+VuG6JG+ni8 -om+rWV6lL8/K2m2qL+usobNqqrcuZzWLeeEeaYji5kbNoKXqvgvOdjp6Dpvq/NonWz1zHyLmSGHG -TPNpsaguG7bUMSAsvIKKjqQOpdeJQ/wWWq8dcdcRWdq6hw2v+vPhwvCkxWeM1tZUOt4KpLoDd7Nl -yP0e03RiqhjKaJMeoYV+9Udly/hNVyh00jT/MLbu9mIwFIws6wIDAQABo4G5MIG2MAsGA1UdDwQE -AwIBxjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSJgmd9xJ0mcABLtFBIfN49rgRufTBYBgNV -HR8EUTBPME2gS6BJhkdodHRwOi8vY3JsLnVzZXJ0cnVzdC5jb20vVVROLVVTRVJGaXJzdC1DbGll -bnRBdXRoZW50aWNhdGlvbmFuZEVtYWlsLmNybDAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUH -AwQwDQYJKoZIhvcNAQEFBQADggEBALFtYV2mGn98q0rkMPxTbyUkxsrt4jFcKw7u7mFVbwQ+zzne -xRtJlOTrIEy05p5QLnLZjfWqo7NK2lYcYJeA3IKirUq9iiv/Cwm0xtcgBEXkzYABurorbs6q15L+ -5K/r9CYdFip/bDCVNy8zEqx/3cfREYxRmLLQo5HQrfafnoOTHh1CuEava2bwm3/q4wMC5QJRwarV -NZ1yQAOJujEdxRBoUp7fooXFXAimeOZTT7Hot9MUnpOmw2TjrH5xzbyf6QMbzPvprDHBr3wVdAKZ -w7JHpsIyYdfHb0gkUSeh1YdV8nuPmD0Wnu51tvjQjvLzxq4oW6fw8zYX/MMF08oDSlQ= ------END CERTIFICATE----- - UTN USERFirst Hardware Root CA ============================== -----BEGIN CERTIFICATE----- @@ -1275,31 +1028,6 @@ iCrVWFCVH/A7HFe7fRQ5YiuayZSSKqMiDP+JJn1fIytH1xUdqWqeUQ0qUZ6B+dQ7XnASfxAynB67 nfhmqA== -----END CERTIFICATE----- -UTN USERFirst Object Root CA -============================ ------BEGIN CERTIFICATE----- -MIIEZjCCA06gAwIBAgIQRL4Mi1AAJLQR0zYt4LNfGzANBgkqhkiG9w0BAQUFADCBlTELMAkGA1UE -BhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEeMBwGA1UEChMVVGhl -IFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHTAb -BgNVBAMTFFVUTi1VU0VSRmlyc3QtT2JqZWN0MB4XDTk5MDcwOTE4MzEyMFoXDTE5MDcwOTE4NDAz -NlowgZUxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJVVDEXMBUGA1UEBxMOU2FsdCBMYWtlIENpdHkx -HjAcBgNVBAoTFVRoZSBVU0VSVFJVU1QgTmV0d29yazEhMB8GA1UECxMYaHR0cDovL3d3dy51c2Vy -dHJ1c3QuY29tMR0wGwYDVQQDExRVVE4tVVNFUkZpcnN0LU9iamVjdDCCASIwDQYJKoZIhvcNAQEB -BQADggEPADCCAQoCggEBAM6qgT+jo2F4qjEAVZURnicPHxzfOpuCaDDASmEd8S8O+r5596Uj71VR -loTN2+O5bj4x2AogZ8f02b+U60cEPgLOKqJdhwQJ9jCdGIqXsqoc/EHSoTbL+z2RuufZcDX65OeQ -w5ujm9M89RKZd7G3CeBo5hy485RjiGpq/gt2yb70IuRnuasaXnfBhQfdDWy/7gbHd2pBnqcP1/vu -lBe3/IW+pKvEHDHd17bR5PDv3xaPslKT16HUiaEHLr/hARJCHhrh2JU022R5KP+6LhHC5ehbkkj7 -RwvCbNqtMoNB86XlQXD9ZZBt+vpRxPm9lisZBCzTbafc8H9vg2XiaquHhnUCAwEAAaOBrzCBrDAL -BgNVHQ8EBAMCAcYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU2u1kdBScFDyr3ZmpvVsoTYs8 -ydgwQgYDVR0fBDswOTA3oDWgM4YxaHR0cDovL2NybC51c2VydHJ1c3QuY29tL1VUTi1VU0VSRmly -c3QtT2JqZWN0LmNybDApBgNVHSUEIjAgBggrBgEFBQcDAwYIKwYBBQUHAwgGCisGAQQBgjcKAwQw -DQYJKoZIhvcNAQEFBQADggEBAAgfUrE3RHjb/c652pWWmKpVZIC1WkDdIaXFwfNfLEzIR1pp6ujw -NTX00CXzyKakh0q9G7FzCL3Uw8q2NbtZhncxzaeAFK4T7/yxSPlrJSUtUbYsbUXBmMiKVl0+7kNO -PmsnjtA6S4ULX9Ptaqd1y9Fahy85dRNacrACgZ++8A+EVCBibGnU4U3GDZlDAQ0Slox4nb9QorFE -qmrPF3rPbw/U+CRVX/A0FklmPlBGyWNxODFiuGK581OtbLUrohKqGU8J2l7nk8aOFAj+8DCAGKCG -hU3IfdeLA/5u1fedFqySLKAj5ZyRUh+U3xeUc8OzwcFxBSAAeL0TUh2oPs0AH8g= ------END CERTIFICATE----- - Camerfirma Chambers of Commerce Root ==================================== -----BEGIN CERTIFICATE----- @@ -1354,42 +1082,6 @@ IBHNfTIzSJRUTN3cecQwn+uOuFW114hcxWokPbLTBQNRxgfvzBRydD1ucs4YKIxKoHflCStFREes t2d/AYoFWpO+ocH/+OcOZ6RHSXZddZAa9SaP8A== -----END CERTIFICATE----- -NetLock Qualified (Class QA) Root -================================= ------BEGIN CERTIFICATE----- -MIIG0TCCBbmgAwIBAgIBezANBgkqhkiG9w0BAQUFADCByTELMAkGA1UEBhMCSFUxETAPBgNVBAcT -CEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0b25zYWdpIEtmdC4xGjAYBgNV -BAsTEVRhbnVzaXR2YW55a2lhZG9rMUIwQAYDVQQDEzlOZXRMb2NrIE1pbm9zaXRldHQgS296amVn -eXpvaSAoQ2xhc3MgUUEpIFRhbnVzaXR2YW55a2lhZG8xHjAcBgkqhkiG9w0BCQEWD2luZm9AbmV0 -bG9jay5odTAeFw0wMzAzMzAwMTQ3MTFaFw0yMjEyMTUwMTQ3MTFaMIHJMQswCQYDVQQGEwJIVTER -MA8GA1UEBxMIQnVkYXBlc3QxJzAlBgNVBAoTHk5ldExvY2sgSGFsb3phdGJpenRvbnNhZ2kgS2Z0 -LjEaMBgGA1UECxMRVGFudXNpdHZhbnlraWFkb2sxQjBABgNVBAMTOU5ldExvY2sgTWlub3NpdGV0 -dCBLb3pqZWd5em9pIChDbGFzcyBRQSkgVGFudXNpdHZhbnlraWFkbzEeMBwGCSqGSIb3DQEJARYP -aW5mb0BuZXRsb2NrLmh1MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx1Ilstg91IRV -CacbvWy5FPSKAtt2/GoqeKvld/Bu4IwjZ9ulZJm53QE+b+8tmjwi8F3JV6BVQX/yQ15YglMxZc4e -8ia6AFQer7C8HORSjKAyr7c3sVNnaHRnUPYtLmTeriZ539+Zhqurf4XsoPuAzPS4DB6TRWO53Lhb -m+1bOdRfYrCnjnxmOCyqsQhjF2d9zL2z8cM/z1A57dEZgxXbhxInlrfa6uWdvLrqOU+L73Sa58XQ -0uqGURzk/mQIKAR5BevKxXEOC++r6uwSEaEYBTJp0QwsGj0lmT+1fMptsK6ZmfoIYOcZwvK9UdPM -0wKswREMgM6r3JSda6M5UzrWhQIDAMV9o4ICwDCCArwwEgYDVR0TAQH/BAgwBgEB/wIBBDAOBgNV -HQ8BAf8EBAMCAQYwggJ1BglghkgBhvhCAQ0EggJmFoICYkZJR1lFTEVNISBFemVuIHRhbnVzaXR2 -YW55IGEgTmV0TG9jayBLZnQuIE1pbm9zaXRldHQgU3pvbGdhbHRhdGFzaSBTemFiYWx5emF0YWJh -biBsZWlydCBlbGphcmFzb2sgYWxhcGphbiBrZXN6dWx0LiBBIG1pbm9zaXRldHQgZWxla3Ryb25p -a3VzIGFsYWlyYXMgam9naGF0YXMgZXJ2ZW55ZXN1bGVzZW5laywgdmFsYW1pbnQgZWxmb2dhZGFz -YW5hayBmZWx0ZXRlbGUgYSBNaW5vc2l0ZXR0IFN6b2xnYWx0YXRhc2kgU3phYmFseXphdGJhbiwg -YXogQWx0YWxhbm9zIFN6ZXJ6b2Rlc2kgRmVsdGV0ZWxla2JlbiBlbG9pcnQgZWxsZW5vcnplc2kg -ZWxqYXJhcyBtZWd0ZXRlbGUuIEEgZG9rdW1lbnR1bW9rIG1lZ3RhbGFsaGF0b2sgYSBodHRwczov -L3d3dy5uZXRsb2NrLmh1L2RvY3MvIGNpbWVuIHZhZ3kga2VyaGV0b2sgYXogaW5mb0BuZXRsb2Nr -Lm5ldCBlLW1haWwgY2ltZW4uIFdBUk5JTkchIFRoZSBpc3N1YW5jZSBhbmQgdGhlIHVzZSBvZiB0 -aGlzIGNlcnRpZmljYXRlIGFyZSBzdWJqZWN0IHRvIHRoZSBOZXRMb2NrIFF1YWxpZmllZCBDUFMg -YXZhaWxhYmxlIGF0IGh0dHBzOi8vd3d3Lm5ldGxvY2suaHUvZG9jcy8gb3IgYnkgZS1tYWlsIGF0 -IGluZm9AbmV0bG9jay5uZXQwHQYDVR0OBBYEFAlqYhaSsFq7VQ7LdTI6MuWyIckoMA0GCSqGSIb3 -DQEBBQUAA4IBAQCRalCc23iBmz+LQuM7/KbD7kPgz/PigDVJRXYC4uMvBcXxKufAQTPGtpvQMznN -wNuhrWw3AkxYQTvyl5LGSKjN5Yo5iWH5Upfpvfb5lHTocQ68d4bDBsxafEp+NFAwLvt/MpqNPfMg -W/hqyobzMUwsWYACff44yTB1HLdV47yfuqhthCgFdbOLDcCRVCHnpgu0mfVRQdzNo0ci2ccBgcTc -R08m6h/t280NmPSjnLRzMkqWmf68f8glWPhY83ZmiVSkpj7EUFy6iRiCdUgh0k8T6GB+B3bbELVR -5qq5aKrN9p2QdRLqOBrKROi3macqaJVmlaut74nLYKkGEsaUR+ko ------END CERTIFICATE----- - NetLock Notary (Class A) Root ============================= -----BEGIN CERTIFICATE----- @@ -1651,29 +1343,6 @@ wUpgpZKpsaSgYMN4h7Mi8yrrW6ntBas3D7Hi05V2Y1Z0jFhyGzflZKG+TQyTmAyX9odtsz/ny4Cm VM+h4k0460tQtcsm9MracEpqoeJ5quGnM/b9Sh/22WA= -----END CERTIFICATE----- -Wells Fargo Root CA -=================== ------BEGIN CERTIFICATE----- -MIID5TCCAs2gAwIBAgIEOeSXnjANBgkqhkiG9w0BAQUFADCBgjELMAkGA1UEBhMCVVMxFDASBgNV -BAoTC1dlbGxzIEZhcmdvMSwwKgYDVQQLEyNXZWxscyBGYXJnbyBDZXJ0aWZpY2F0aW9uIEF1dGhv -cml0eTEvMC0GA1UEAxMmV2VsbHMgRmFyZ28gUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcN -MDAxMDExMTY0MTI4WhcNMjEwMTE0MTY0MTI4WjCBgjELMAkGA1UEBhMCVVMxFDASBgNVBAoTC1dl -bGxzIEZhcmdvMSwwKgYDVQQLEyNXZWxscyBGYXJnbyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEv -MC0GA1UEAxMmV2VsbHMgRmFyZ28gUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0GCSqG -SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDVqDM7Jvk0/82bfuUER84A4n135zHCLielTWi5MbqNQ1mX -x3Oqfz1cQJ4F5aHiidlMuD+b+Qy0yGIZLEWukR5zcUHESxP9cMIlrCL1dQu3U+SlK93OvRw6esP3 -E48mVJwWa2uv+9iWsWCaSOAlIiR5NM4OJgALTqv9i86C1y8IcGjBqAr5dE8Hq6T54oN+J3N0Prj5 -OEL8pahbSCOz6+MlsoCultQKnMJ4msZoGK43YjdeUXWoWGPAUe5AeH6orxqg4bB4nVCMe+ez/I4j -sNtlAHCEAQgAFG5Uhpq6zPk3EPbg3oQtnaSFN9OH4xXQwReQfhkhahKpdv0SAulPIV4XAgMBAAGj -YTBfMA8GA1UdEwEB/wQFMAMBAf8wTAYDVR0gBEUwQzBBBgtghkgBhvt7hwcBCzAyMDAGCCsGAQUF -BwIBFiRodHRwOi8vd3d3LndlbGxzZmFyZ28uY29tL2NlcnRwb2xpY3kwDQYJKoZIhvcNAQEFBQAD -ggEBANIn3ZwKdyu7IvICtUpKkfnRLb7kuxpo7w6kAOnu5+/u9vnldKTC2FJYxHT7zmu1Oyl5GFrv -m+0fazbuSCUlFLZWohDo7qd/0D+j0MNdJu4HzMPBJCGHHt8qElNvQRbn7a6U+oxy+hNH8Dx+rn0R -OhPs7fpvcmR7nX1/Jv16+yWt6j4pf0zjAFcysLPp7VMX2YuyFA4w6OXVE8Zkr8QA1dhYJPz1j+zx -x32l2w8n0cbyQIjmH/ZhqPRCyLk306m+LFZ4wnKbWV01QIroTmMatukgalHizqSQ33ZwmVxwQ023 -tqcZZE6St8WRPH9IFmV7Fv3L/PvZ1dZPIWU7Sn9Ho/s= ------END CERTIFICATE----- - Swisscom Root CA 1 ================== -----BEGIN CERTIFICATE----- @@ -1886,37 +1555,6 @@ hGIAF728JRhX8tepb1mIvDS3LoV4nZbcFMMsilKbloxSZj2GFotHuFEJjOp9zYhys2AzsfAKRO8P UrbnBEI= -----END CERTIFICATE----- -SwissSign Platinum CA - G2 -========================== ------BEGIN CERTIFICATE----- -MIIFwTCCA6mgAwIBAgIITrIAZwwDXU8wDQYJKoZIhvcNAQEFBQAwSTELMAkGA1UEBhMCQ0gxFTAT -BgNVBAoTDFN3aXNzU2lnbiBBRzEjMCEGA1UEAxMaU3dpc3NTaWduIFBsYXRpbnVtIENBIC0gRzIw -HhcNMDYxMDI1MDgzNjAwWhcNMzYxMDI1MDgzNjAwWjBJMQswCQYDVQQGEwJDSDEVMBMGA1UEChMM -U3dpc3NTaWduIEFHMSMwIQYDVQQDExpTd2lzc1NpZ24gUGxhdGludW0gQ0EgLSBHMjCCAiIwDQYJ -KoZIhvcNAQEBBQADggIPADCCAgoCggIBAMrfogLi2vj8Bxax3mCq3pZcZB/HL37PZ/pEQtZ2Y5Wu -669yIIpFR4ZieIbWIDkm9K6j/SPnpZy1IiEZtzeTIsBQnIJ71NUERFzLtMKfkr4k2HtnIuJpX+UF -eNSH2XFwMyVTtIc7KZAoNppVRDBopIOXfw0enHb/FZ1glwCNioUD7IC+6ixuEFGSzH7VozPY1kne -WCqv9hbrS3uQMpe5up1Y8fhXSQQeol0GcN1x2/ndi5objM89o03Oy3z2u5yg+gnOI2Ky6Q0f4nIo -j5+saCB9bzuohTEJfwvH6GXp43gOCWcwizSC+13gzJ2BbWLuCB4ELE6b7P6pT1/9aXjvCR+htL/6 -8++QHkwFix7qepF6w9fl+zC8bBsQWJj3Gl/QKTIDE0ZNYWqFTFJ0LwYfexHihJfGmfNtf9dng34T -aNhxKFrYzt3oEBSa/m0jh26OWnA81Y0JAKeqvLAxN23IhBQeW71FYyBrS3SMvds6DsHPWhaPpZjy -domyExI7C3d3rLvlPClKknLKYRorXkzig3R3+jVIeoVNjZpTxN94ypeRSCtFKwH3HBqi7Ri6Cr2D -+m+8jVeTO9TUps4e8aCxzqv9KyiaTxvXw3LbpMS/XUz13XuWae5ogObnmLo2t/5u7Su9IPhlGdpV -CX4l3P5hYnL5fhgC72O00Puv5TtjjGePAgMBAAGjgawwgakwDgYDVR0PAQH/BAQDAgEGMA8GA1Ud -EwEB/wQFMAMBAf8wHQYDVR0OBBYEFFCvzAeHFUdvOMW0ZdHelarp35zMMB8GA1UdIwQYMBaAFFCv -zAeHFUdvOMW0ZdHelarp35zMMEYGA1UdIAQ/MD0wOwYJYIV0AVkBAQEBMC4wLAYIKwYBBQUHAgEW -IGh0dHA6Ly9yZXBvc2l0b3J5LnN3aXNzc2lnbi5jb20vMA0GCSqGSIb3DQEBBQUAA4ICAQAIhab1 -Fgz8RBrBY+D5VUYI/HAcQiiWjrfFwUF1TglxeeVtlspLpYhg0DB0uMoI3LQwnkAHFmtllXcBrqS3 -NQuB2nEVqXQXOHtYyvkv+8Bldo1bAbl93oI9ZLi+FHSjClTTLJUYFzX1UWs/j6KWYTl4a0vlpqD4 -U99REJNi54Av4tHgvI42Rncz7Lj7jposiU0xEQ8mngS7twSNC/K5/FqdOxa3L8iYq/6KUFkuozv8 -KV2LwUvJ4ooTHbG/u0IdUt1O2BReEMYxB+9xJ/cbOQncguqLs5WGXv312l0xpuAxtpTmREl0xRbl -9x8DYSjFyMsSoEJL+WuICI20MhjzdZ/EfwBPBZWcoxcCw7NTm6ogOSkrZvqdr16zktK1puEa+S1B -aYEUtLS17Yk9zvupnTVCRLEcFHOBzyoBNZox1S2PbYTfgE1X4z/FhHXaicYwu+uPyyIIoK6q8QNs -OktNCaUOcsZWayFCTiMlFGiudgp8DAdwZPmaL/YFOSbGDI8Zf0NebvRbFS/bYV3mZy8/CJT5YLSY -Mdp08YSTcU1f+2BY0fvEwW2JorsgH51xkcsymxM9Pn2SUjWskpSi0xjCfMfqr3YFFt1nJ8J+HAci -IfNAChs0B0QTwoRqjt8ZWr9/6x3iGjjRXK9HkmuAtTClyY3YqzGBH9/CZjfTk6mFhnll0g== ------END CERTIFICATE----- - SwissSign Gold CA - G2 ====================== -----BEGIN CERTIFICATE----- @@ -2254,32 +1892,6 @@ hNVQA7bihKOmNqoROgHhGEvWRGizPflTdISzRpFGlgC3gCy24eMQ4tui5yiPAZZiFj4A4xylNoEY okxSdsARo27mHbrjWr42U8U+dY+GaSlYU7Wcu2+fXMUY7N0v4ZjJ/L7fCg0= -----END CERTIFICATE----- -S-TRUST Authentication and Encryption Root CA 2005 PN -===================================================== ------BEGIN CERTIFICATE----- -MIIEezCCA2OgAwIBAgIQNxkY5lNUfBq1uMtZWts1tzANBgkqhkiG9w0BAQUFADCBrjELMAkGA1UE -BhMCREUxIDAeBgNVBAgTF0JhZGVuLVd1ZXJ0dGVtYmVyZyAoQlcpMRIwEAYDVQQHEwlTdHV0dGdh -cnQxKTAnBgNVBAoTIERldXRzY2hlciBTcGFya2Fzc2VuIFZlcmxhZyBHbWJIMT4wPAYDVQQDEzVT -LVRSVVNUIEF1dGhlbnRpY2F0aW9uIGFuZCBFbmNyeXB0aW9uIFJvb3QgQ0EgMjAwNTpQTjAeFw0w -NTA2MjIwMDAwMDBaFw0zMDA2MjEyMzU5NTlaMIGuMQswCQYDVQQGEwJERTEgMB4GA1UECBMXQmFk -ZW4tV3VlcnR0ZW1iZXJnIChCVykxEjAQBgNVBAcTCVN0dXR0Z2FydDEpMCcGA1UEChMgRGV1dHNj -aGVyIFNwYXJrYXNzZW4gVmVybGFnIEdtYkgxPjA8BgNVBAMTNVMtVFJVU1QgQXV0aGVudGljYXRp -b24gYW5kIEVuY3J5cHRpb24gUm9vdCBDQSAyMDA1OlBOMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A -MIIBCgKCAQEA2bVKwdMz6tNGs9HiTNL1toPQb9UY6ZOvJ44TzbUlNlA0EmQpoVXhOmCTnijJ4/Ob -4QSwI7+Vio5bG0F/WsPoTUzVJBY+h0jUJ67m91MduwwA7z5hca2/OnpYH5Q9XIHV1W/fuJvS9eXL -g3KSwlOyggLrra1fFi2SU3bxibYs9cEv4KdKb6AwajLrmnQDaHgTncovmwsdvs91DSaXm8f1Xgqf -eN+zvOyauu9VjxuapgdjKRdZYgkqeQd3peDRF2npW932kKvimAoA0SVtnteFhy+S8dF2g08LOlk3 -KC8zpxdQ1iALCvQm+Z845y2kuJuJja2tyWp9iRe79n+Ag3rm7QIDAQABo4GSMIGPMBIGA1UdEwEB -/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgEGMCkGA1UdEQQiMCCkHjAcMRowGAYDVQQDExFTVFJv -bmxpbmUxLTIwNDgtNTAdBgNVHQ4EFgQUD8oeXHngovMpttKFswtKtWXsa1IwHwYDVR0jBBgwFoAU -D8oeXHngovMpttKFswtKtWXsa1IwDQYJKoZIhvcNAQEFBQADggEBAK8B8O0ZPCjoTVy7pWMciDMD -pwCHpB8gq9Yc4wYfl35UvbfRssnV2oDsF9eK9XvCAPbpEW+EoFolMeKJ+aQAPzFoLtU96G7m1R08 -P7K9n3frndOMusDXtk3sU5wPBG7qNWdX4wple5A64U8+wwCSersFiXOMy6ZNwPv2AtawB6MDwidA -nwzkhYItr5pCHdDHjfhA7p0GVxzZotiAFP7hYy0yh9WUUpY6RsZxlj33mA6ykaqP2vROJAA5Veit -F7nTNCtKqUDMFypVZUF0Qn71wK/Ik63yGFs9iQzbRzkk+OBM8h+wPQrKBU6JIRrjKpms/H+h8Q8b -Hz2eBIPdltkdOpQ= ------END CERTIFICATE----- - Microsec e-Szigno Root CA ========================= -----BEGIN CERTIFICATE----- @@ -2475,28 +2087,6 @@ dyd1Lx+4ivn+xbrYNuSD7Odlt79jWvNGr4GUN9RBjNYj1h7P9WgbRGOiWrqnNVmh5XAFmw4jV5mU Cm26OWMohpLzGITY+9HPBVZkVw== -----END CERTIFICATE----- -ComSign CA -========== ------BEGIN CERTIFICATE----- -MIIDkzCCAnugAwIBAgIQFBOWgxRVjOp7Y+X8NId3RDANBgkqhkiG9w0BAQUFADA0MRMwEQYDVQQD -EwpDb21TaWduIENBMRAwDgYDVQQKEwdDb21TaWduMQswCQYDVQQGEwJJTDAeFw0wNDAzMjQxMTMy -MThaFw0yOTAzMTkxNTAyMThaMDQxEzARBgNVBAMTCkNvbVNpZ24gQ0ExEDAOBgNVBAoTB0NvbVNp -Z24xCzAJBgNVBAYTAklMMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA8ORUaSvTx49q -ROR+WCf4C9DklBKK8Rs4OC8fMZwG1Cyn3gsqrhqg455qv588x26i+YtkbDqthVVRVKU4VbirgwTy -P2Q298CNQ0NqZtH3FyrV7zb6MBBC11PN+fozc0yz6YQgitZBJzXkOPqUm7h65HkfM/sb2CEJKHxN -GGleZIp6GZPKfuzzcuc3B1hZKKxC+cX/zT/npfo4sdAMx9lSGlPWgcxCejVb7Us6eva1jsz/D3zk -YDaHL63woSV9/9JLEYhwVKZBqGdTUkJe5DSe5L6j7KpiXd3DTKaCQeQzC6zJMw9kglcq/QytNuEM -rkvF7zuZ2SOzW120V+x0cAwqTwIDAQABo4GgMIGdMAwGA1UdEwQFMAMBAf8wPQYDVR0fBDYwNDAy -oDCgLoYsaHR0cDovL2ZlZGlyLmNvbXNpZ24uY28uaWwvY3JsL0NvbVNpZ25DQS5jcmwwDgYDVR0P -AQH/BAQDAgGGMB8GA1UdIwQYMBaAFEsBmz5WGmU2dst7l6qSBe4y5ygxMB0GA1UdDgQWBBRLAZs+ -VhplNnbLe5eqkgXuMucoMTANBgkqhkiG9w0BAQUFAAOCAQEA0Nmlfv4pYEWdfoPPbrxHbvUanlR2 -QnG0PFg/LUAlQvaBnPGJEMgOqnhPOAlXsDzACPw1jvFIUY0McXS6hMTXcpuEfDhOZAYnKuGntewI -mbQKDdSFc8gS4TXt8QUxHXOZDOuWyt3T5oWq8Ir7dcHyCTxlZWTzTNity4hp8+SDtwy9F1qWF8pb -/627HOkthIDYIb6FUtnUdLlphbpN7Sgy6/lhSuTENh4Z3G+EER+V9YMoGKgzkkMn3V0TBEVPh9VG -zT2ouvDzuFYkRes3x+F2T3I5GN9+dHLHcy056mDmrRGiVod7w2ia/viMcKjfZTL0pECMocJEAw6U -AGegcQCCSA== ------END CERTIFICATE----- - ComSign Secured CA ================== -----BEGIN CERTIFICATE----- @@ -3045,22 +2635,6 @@ MCwXEGCSn1WHElkQwg9naRHMTh5+Spqtr0CodaxWkHS4oJyleW/c6RrIaQXpuvoDs3zk4E7Czp3o tkYNbn5XOmeUwssfnHdKZ05phkOTOPu220+DkdRgfks+KzgHVZhepA== -----END CERTIFICATE----- -Verisign Class 1 Public Primary Certification Authority -======================================================= ------BEGIN CERTIFICATE----- -MIICPDCCAaUCED9pHoGc8JpK83P/uUii5N0wDQYJKoZIhvcNAQEFBQAwXzELMAkGA1UEBhMCVVMx -FzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAxIFB1YmxpYyBQcmltYXJ5 -IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2MDEyOTAwMDAwMFoXDTI4MDgwMjIzNTk1OVow -XzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAx -IFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUA -A4GNADCBiQKBgQDlGb9to1ZhLZlIcfZn3rmN67eehoAKkQ76OCWvRoiC5XOooJskXQ0fzGVuDLDQ -VoQYh5oGmxChc9+0WDlrbsH2FdWoqD+qEgaNMax/sDTXjzRniAnNFBHiTkVWaR94AoDa3EeRKbs2 -yWNcxeDXLYd7obcysHswuiovMaruo2fa2wIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFgVKTk8d6Pa -XCUDfGD67gmZPCcQcMgMCeazh88K4hiWNWLMv5sneYlfycQJ9M61Hd8qveXbhpxoJeUwfLaJFf5n -0a3hUKw8fGJLj7qE1xIVGx/KXQ/BUpQqEZnae88MNhPVNdwQGVnqlMEAv3WP2fr9dgTbYruQagPZ -RjXZ+Hxb ------END CERTIFICATE----- - Verisign Class 3 Public Primary Certification Authority ======================================================= -----BEGIN CERTIFICATE----- @@ -3144,29 +2718,6 @@ YIvDQVETI53O9zJrlAGomecsMx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7r kpeDMdmztcpHWD9f -----END CERTIFICATE----- -TC TrustCenter Universal CA III -=============================== ------BEGIN CERTIFICATE----- -MIID4TCCAsmgAwIBAgIOYyUAAQACFI0zFQLkbPQwDQYJKoZIhvcNAQEFBQAwezELMAkGA1UEBhMC -REUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxJDAiBgNVBAsTG1RDIFRydXN0Q2VudGVy -IFVuaXZlcnNhbCBDQTEoMCYGA1UEAxMfVEMgVHJ1c3RDZW50ZXIgVW5pdmVyc2FsIENBIElJSTAe -Fw0wOTA5MDkwODE1MjdaFw0yOTEyMzEyMzU5NTlaMHsxCzAJBgNVBAYTAkRFMRwwGgYDVQQKExNU -QyBUcnVzdENlbnRlciBHbWJIMSQwIgYDVQQLExtUQyBUcnVzdENlbnRlciBVbml2ZXJzYWwgQ0Ex -KDAmBgNVBAMTH1RDIFRydXN0Q2VudGVyIFVuaXZlcnNhbCBDQSBJSUkwggEiMA0GCSqGSIb3DQEB -AQUAA4IBDwAwggEKAoIBAQDC2pxisLlxErALyBpXsq6DFJmzNEubkKLF5+cvAqBNLaT6hdqbJYUt -QCggbergvbFIgyIpRJ9Og+41URNzdNW88jBmlFPAQDYvDIRlzg9uwliT6CwLOunBjvvya8o84pxO -juT5fdMnnxvVZ3iHLX8LR7PH6MlIfK8vzArZQe+f/prhsq75U7Xl6UafYOPfjdN/+5Z+s7Vy+Eut -CHnNaYlAJ/Uqwa1D7KRTyGG299J5KmcYdkhtWyUB0SbFt1dpIxVbYYqt8Bst2a9c8SaQaanVDED1 -M4BDj5yjdipFtK+/fz6HP3bFzSreIMUWWMv5G/UPyw0RUmS40nZid4PxWJ//AgMBAAGjYzBhMB8G -A1UdIwQYMBaAFFbn4VslQ4Dg9ozhcbyO5YAvxEjiMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ -BAQDAgEGMB0GA1UdDgQWBBRW5+FbJUOA4PaM4XG8juWAL8RI4jANBgkqhkiG9w0BAQUFAAOCAQEA -g8ev6n9NCjw5sWi+e22JLumzCecYV42FmhfzdkJQEw/HkG8zrcVJYCtsSVgZ1OK+t7+rSbyUyKu+ -KGwWaODIl0YgoGhnYIg5IFHYaAERzqf2EQf27OysGh+yZm5WZ2B6dF7AbZc2rrUNXWZzwCUyRdhK -BgePxLcHsU0GDeGl6/R1yrqc0L2z0zIkTO5+4nYES0lT2PLpVDP85XEfPRRclkvxOvIAu2y0+pZV -CIgJwcyRGSmwIC3/yzikQOEXvnlhgP8HA4ZMTnsGnxGGjYnuJ8Tb4rwZjgvDwxPHLQNjO9Po5KIq -woIIlBZU8O8fJ5AluA0OKBtHd0e9HKgl8ZS0Zg== ------END CERTIFICATE----- - Autoridad de Certificacion Firmaprofesional CIF A62634068 ========================================================= -----BEGIN CERTIFICATE----- @@ -3893,3 +3444,342 @@ uSlNDUmJEYcyW+ZLBMjkXOZ0c5RdFpgTlf7727FE5TpwrDdr5rMzcijJs1eg9gIWiAYLtqZLICjU 3j2LrTcFU3T+bsy8QxdxXvnFzBqpYe73dgzzcvRyrc9yAjYHR8/vGVCJYMzpJJUPwssd8m92kMfM dcGWxZ0= -----END CERTIFICATE----- + +TURKTRUST Certificate Services Provider Root 2007 +================================================= +-----BEGIN CERTIFICATE----- +MIIEPTCCAyWgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBvzE/MD0GA1UEAww2VMOcUktUUlVTVCBF +bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGEwJUUjEP +MA0GA1UEBwwGQW5rYXJhMV4wXAYDVQQKDFVUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUg +QmlsacWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLiAoYykgQXJhbMSxayAyMDA3MB4X +DTA3MTIyNTE4MzcxOVoXDTE3MTIyMjE4MzcxOVowgb8xPzA9BgNVBAMMNlTDnFJLVFJVU1QgRWxl +a3Ryb25payBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsTELMAkGA1UEBhMCVFIxDzAN +BgNVBAcMBkFua2FyYTFeMFwGA1UECgxVVMOcUktUUlVTVCBCaWxnaSDEsGxldGnFn2ltIHZlIEJp +bGnFn2ltIEfDvHZlbmxpxJ9pIEhpem1ldGxlcmkgQS7Fni4gKGMpIEFyYWzEsWsgMjAwNzCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKu3PgqMyKVYFeaK7yc9SrToJdPNM8Ig3BnuiD9N +YvDdE3ePYakqtdTyuTFYKTsvP2qcb3N2Je40IIDu6rfwxArNK4aUyeNgsURSsloptJGXg9i3phQv +KUmi8wUG+7RP2qFsmmaf8EMJyupyj+sA1zU511YXRxcw9L6/P8JorzZAwan0qafoEGsIiveGHtya +KhUG9qPw9ODHFNRRf8+0222vR5YXm3dx2KdxnSQM9pQ/hTEST7ruToK4uT6PIzdezKKqdfcYbwnT +rqdUKDT74eA7YH2gvnmJhsifLfkKS8RQouf9eRbHegsYz85M733WB2+Y8a+xwXrXgTW4qhe04MsC +AwEAAaNCMEAwHQYDVR0OBBYEFCnFkKslrxHkYb+j/4hhkeYO/pyBMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQAQDdr4Ouwo0RSVgrESLFF6QSU2TJ/s +Px+EnWVUXKgWAkD6bho3hO9ynYYKVZ1WKKxmLNA6VpM0ByWtCLCPyA8JWcqdmBzlVPi5RX9ql2+I +aE1KBiY3iAIOtsbWcpnOa3faYjGkVh+uX4132l32iPwa2Z61gfAyuOOI0JzzaqC5mxRZNTZPz/OO +Xl0XrRWV2N2y1RVuAE6zS89mlOTgzbUF2mNXi+WzqtvALhyQRNsaXRik7r4EW5nVcV9VZWRi1aKb +BFmGyGJ353yCRWo9F7/snXUMrqNvWtMvmDb08PUZqxFdyKbjKlhqQgnDvZImZjINXQhVdP+MmNAK +poRq0Tl9 +-----END CERTIFICATE----- + +D-TRUST Root Class 3 CA 2 2009 +============================== +-----BEGIN CERTIFICATE----- +MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQK +DAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTAe +Fw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NThaME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxE +LVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANOySs96R+91myP6Oi/WUEWJNTrGa9v+2wBoqOAD +ER03UAifTUpolDWzU9GUY6cgVq/eUXjsKj3zSEhQPgrfRlWLJ23DEE0NkVJD2IfgXU42tSHKXzlA +BF9bfsyjxiupQB7ZNoTWSPOSHjRGICTBpFGOShrvUD9pXRl/RcPHAY9RySPocq60vFYJfxLLHLGv +KZAKyVXMD9O0Gu1HNVpK7ZxzBCHQqr0ME7UAyiZsxGsMlFqVlNpQmvH/pStmMaTJOKDfHR+4CS7z +p+hnUquVH+BGPtikw8paxTGA6Eian5Rp/hnd2HN8gcqW3o7tszIFZYQ05ub9VxC1X3a/L7AQDcUC +AwEAAaOCARowggEWMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP3aFMSfMN4hvR5COfyrYyNJ +4PGEMA4GA1UdDwEB/wQEAwIBBjCB0wYDVR0fBIHLMIHIMIGAoH6gfIZ6bGRhcDovL2RpcmVjdG9y +eS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwUm9vdCUyMENsYXNzJTIwMyUyMENBJTIwMiUyMDIw +MDksTz1ELVRydXN0JTIwR21iSCxDPURFP2NlcnRpZmljYXRlcmV2b2NhdGlvbmxpc3QwQ6BBoD+G +PWh0dHA6Ly93d3cuZC10cnVzdC5uZXQvY3JsL2QtdHJ1c3Rfcm9vdF9jbGFzc18zX2NhXzJfMjAw +OS5jcmwwDQYJKoZIhvcNAQELBQADggEBAH+X2zDI36ScfSF6gHDOFBJpiBSVYEQBrLLpME+bUMJm +2H6NMLVwMeniacfzcNsgFYbQDfC+rAF1hM5+n02/t2A7nPPKHeJeaNijnZflQGDSNiH+0LS4F9p0 +o3/U37CYAqxva2ssJSRyoWXuJVrl5jLn8t+rSfrzkGkj2wTZ51xY/GXUl77M/C4KzCUqNQT4YJEV +dT1B/yMfGchs64JTBKbkTCJNjYy6zltz7GRUUG3RnFX7acM2w4y8PIWmawomDeCTmGCufsYkl4ph +X5GOZpIJhzbNi5stPvZR1FDUWSi9g/LMKHtThm3YJohw1+qRzT65ysCQblrGXnRl11z+o+I= +-----END CERTIFICATE----- + +D-TRUST Root Class 3 CA 2 EV 2009 +================================= +-----BEGIN CERTIFICATE----- +MIIEQzCCAyugAwIBAgIDCYP0MA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQK +DAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAw +OTAeFw0wOTExMDUwODUwNDZaFw0yOTExMDUwODUwNDZaMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQK +DAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAw +OTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJnxhDRwui+3MKCOvXwEz75ivJn9gpfS +egpnljgJ9hBOlSJzmY3aFS3nBfwZcyK3jpgAvDw9rKFs+9Z5JUut8Mxk2og+KbgPCdM03TP1YtHh +zRnp7hhPTFiu4h7WDFsVWtg6uMQYZB7jM7K1iXdODL/ZlGsTl28So/6ZqQTMFexgaDbtCHu39b+T +7WYxg4zGcTSHThfqr4uRjRxWQa4iN1438h3Z0S0NL2lRp75mpoo6Kr3HGrHhFPC+Oh25z1uxav60 +sUYgovseO3Dvk5h9jHOW8sXvhXCtKSb8HgQ+HKDYD8tSg2J87otTlZCpV6LqYQXY+U3EJ/pure35 +11H3a6UCAwEAAaOCASQwggEgMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNOUikxiEyoZLsyv +cop9NteaHNxnMA4GA1UdDwEB/wQEAwIBBjCB3QYDVR0fBIHVMIHSMIGHoIGEoIGBhn9sZGFwOi8v +ZGlyZWN0b3J5LmQtdHJ1c3QubmV0L0NOPUQtVFJVU1QlMjBSb290JTIwQ2xhc3MlMjAzJTIwQ0El +MjAyJTIwRVYlMjAyMDA5LE89RC1UcnVzdCUyMEdtYkgsQz1ERT9jZXJ0aWZpY2F0ZXJldm9jYXRp +b25saXN0MEagRKBChkBodHRwOi8vd3d3LmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X3Jvb3RfY2xh +c3NfM19jYV8yX2V2XzIwMDkuY3JsMA0GCSqGSIb3DQEBCwUAA4IBAQA07XtaPKSUiO8aEXUHL7P+ +PPoeUSbrh/Yp3uDx1MYkCenBz1UbtDDZzhr+BlGmFaQt77JLvyAoJUnRpjZ3NOhk31KxEcdzes05 +nsKtjHEh8lprr988TlWvsoRlFIm5d8sqMb7Po23Pb0iUMkZv53GMoKaEGTcH8gNFCSuGdXzfX2lX +ANtu2KZyIktQ1HWYVt+3GP9DQ1CuekR78HlR10M9p9OB0/DJT7naxpeG0ILD5EJt/rDiZE4OJudA +NCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqXKVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVv +w9y4AyHqnxbxLFS1 +-----END CERTIFICATE----- + +PSCProcert +========== +-----BEGIN CERTIFICATE----- +MIIJhjCCB26gAwIBAgIBCzANBgkqhkiG9w0BAQsFADCCAR4xPjA8BgNVBAMTNUF1dG9yaWRhZCBk +ZSBDZXJ0aWZpY2FjaW9uIFJhaXogZGVsIEVzdGFkbyBWZW5lem9sYW5vMQswCQYDVQQGEwJWRTEQ +MA4GA1UEBxMHQ2FyYWNhczEZMBcGA1UECBMQRGlzdHJpdG8gQ2FwaXRhbDE2MDQGA1UEChMtU2lz +dGVtYSBOYWNpb25hbCBkZSBDZXJ0aWZpY2FjaW9uIEVsZWN0cm9uaWNhMUMwQQYDVQQLEzpTdXBl +cmludGVuZGVuY2lhIGRlIFNlcnZpY2lvcyBkZSBDZXJ0aWZpY2FjaW9uIEVsZWN0cm9uaWNhMSUw +IwYJKoZIhvcNAQkBFhZhY3JhaXpAc3VzY2VydGUuZ29iLnZlMB4XDTEwMTIyODE2NTEwMFoXDTIw +MTIyNTIzNTk1OVowgdExJjAkBgkqhkiG9w0BCQEWF2NvbnRhY3RvQHByb2NlcnQubmV0LnZlMQ8w +DQYDVQQHEwZDaGFjYW8xEDAOBgNVBAgTB01pcmFuZGExKjAoBgNVBAsTIVByb3ZlZWRvciBkZSBD +ZXJ0aWZpY2Fkb3MgUFJPQ0VSVDE2MDQGA1UEChMtU2lzdGVtYSBOYWNpb25hbCBkZSBDZXJ0aWZp +Y2FjaW9uIEVsZWN0cm9uaWNhMQswCQYDVQQGEwJWRTETMBEGA1UEAxMKUFNDUHJvY2VydDCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANW39KOUM6FGqVVhSQ2oh3NekS1wwQYalNo97BVC +wfWMrmoX8Yqt/ICV6oNEolt6Vc5Pp6XVurgfoCfAUFM+jbnADrgV3NZs+J74BCXfgI8Qhd19L3uA +3VcAZCP4bsm+lU/hdezgfl6VzbHvvnpC2Mks0+saGiKLt38GieU89RLAu9MLmV+QfI4tL3czkkoh +RqipCKzx9hEC2ZUWno0vluYC3XXCFCpa1sl9JcLB/KpnheLsvtF8PPqv1W7/U0HU9TI4seJfxPmO +EO8GqQKJ/+MMbpfg353bIdD0PghpbNjU5Db4g7ayNo+c7zo3Fn2/omnXO1ty0K+qP1xmk6wKImG2 +0qCZyFSTXai20b1dCl53lKItwIKOvMoDKjSuc/HUtQy9vmebVOvh+qBa7Dh+PsHMosdEMXXqP+UH +0quhJZb25uSgXTcYOWEAM11G1ADEtMo88aKjPvM6/2kwLkDd9p+cJsmWN63nOaK/6mnbVSKVUyqU +td+tFjiBdWbjxywbk5yqjKPK2Ww8F22c3HxT4CAnQzb5EuE8XL1mv6JpIzi4mWCZDlZTOpx+FIyw +Bm/xhnaQr/2v/pDGj59/i5IjnOcVdo/Vi5QTcmn7K2FjiO/mpF7moxdqWEfLcU8UC17IAggmosvp +r2uKGcfLFFb14dq12fy/czja+eevbqQ34gcnAgMBAAGjggMXMIIDEzASBgNVHRMBAf8ECDAGAQH/ +AgEBMDcGA1UdEgQwMC6CD3N1c2NlcnRlLmdvYi52ZaAbBgVghl4CAqASDBBSSUYtRy0yMDAwNDAz +Ni0wMB0GA1UdDgQWBBRBDxk4qpl/Qguk1yeYVKIXTC1RVDCCAVAGA1UdIwSCAUcwggFDgBStuyId +xuDSAaj9dlBSk+2YwU2u06GCASakggEiMIIBHjE+MDwGA1UEAxM1QXV0b3JpZGFkIGRlIENlcnRp +ZmljYWNpb24gUmFpeiBkZWwgRXN0YWRvIFZlbmV6b2xhbm8xCzAJBgNVBAYTAlZFMRAwDgYDVQQH +EwdDYXJhY2FzMRkwFwYDVQQIExBEaXN0cml0byBDYXBpdGFsMTYwNAYDVQQKEy1TaXN0ZW1hIE5h +Y2lvbmFsIGRlIENlcnRpZmljYWNpb24gRWxlY3Ryb25pY2ExQzBBBgNVBAsTOlN1cGVyaW50ZW5k +ZW5jaWEgZGUgU2VydmljaW9zIGRlIENlcnRpZmljYWNpb24gRWxlY3Ryb25pY2ExJTAjBgkqhkiG +9w0BCQEWFmFjcmFpekBzdXNjZXJ0ZS5nb2IudmWCAQowDgYDVR0PAQH/BAQDAgEGME0GA1UdEQRG +MESCDnByb2NlcnQubmV0LnZloBUGBWCGXgIBoAwMClBTQy0wMDAwMDKgGwYFYIZeAgKgEgwQUklG +LUotMzE2MzUzNzMtNzB2BgNVHR8EbzBtMEagRKBChkBodHRwOi8vd3d3LnN1c2NlcnRlLmdvYi52 +ZS9sY3IvQ0VSVElGSUNBRE8tUkFJWi1TSEEzODRDUkxERVIuY3JsMCOgIaAfhh1sZGFwOi8vYWNy +YWl6LnN1c2NlcnRlLmdvYi52ZTA3BggrBgEFBQcBAQQrMCkwJwYIKwYBBQUHMAGGG2h0dHA6Ly9v +Y3NwLnN1c2NlcnRlLmdvYi52ZTBBBgNVHSAEOjA4MDYGBmCGXgMBAjAsMCoGCCsGAQUFBwIBFh5o +dHRwOi8vd3d3LnN1c2NlcnRlLmdvYi52ZS9kcGMwDQYJKoZIhvcNAQELBQADggIBACtZ6yKZu4Sq +T96QxtGGcSOeSwORR3C7wJJg7ODU523G0+1ng3dS1fLld6c2suNUvtm7CpsR72H0xpkzmfWvADmN +g7+mvTV+LFwxNG9s2/NkAZiqlCxB3RWGymspThbASfzXg0gTB1GEMVKIu4YXx2sviiCtxQuPcD4q +uxtxj7mkoP3YldmvWb8lK5jpY5MvYB7Eqvh39YtsL+1+LrVPQA3uvFd359m21D+VJzog1eWuq2w1 +n8GhHVnchIHuTQfiSLaeS5UtQbHh6N5+LwUeaO6/u5BlOsju6rEYNxxik6SgMexxbJHmpHmJWhSn +FFAFTKQAVzAswbVhltw+HoSvOULP5dAssSS830DD7X9jSr3hTxJkhpXzsOfIt+FTvZLm8wyWuevo +5pLtp4EJFAv8lXrPj9Y0TzYS3F7RNHXGRoAvlQSMx4bEqCaJqD8Zm4G7UaRKhqsLEQ+xrmNTbSjq +3TNWOByyrYDT13K9mmyZY+gAu0F2BbdbmRiKw7gSXFbPVgx96OLP7bx0R/vu0xdOIk9W/1DzLuY5 +poLWccret9W6aAjtmcz9opLLabid+Qqkpj5PkygqYWwHJgD/ll9ohri4zspV4KuxPX+Y1zMOWj3Y +eMLEYC/HYvBhkdI4sPaeVdtAgAUSM84dkpvRabP/v/GSCmE1P93+hvS84Bpxs2Km +-----END CERTIFICATE----- + +China Internet Network Information Center EV Certificates Root +============================================================== +-----BEGIN CERTIFICATE----- +MIID9zCCAt+gAwIBAgIESJ8AATANBgkqhkiG9w0BAQUFADCBijELMAkGA1UEBhMCQ04xMjAwBgNV +BAoMKUNoaW5hIEludGVybmV0IE5ldHdvcmsgSW5mb3JtYXRpb24gQ2VudGVyMUcwRQYDVQQDDD5D +aGluYSBJbnRlcm5ldCBOZXR3b3JrIEluZm9ybWF0aW9uIENlbnRlciBFViBDZXJ0aWZpY2F0ZXMg +Um9vdDAeFw0xMDA4MzEwNzExMjVaFw0zMDA4MzEwNzExMjVaMIGKMQswCQYDVQQGEwJDTjEyMDAG +A1UECgwpQ2hpbmEgSW50ZXJuZXQgTmV0d29yayBJbmZvcm1hdGlvbiBDZW50ZXIxRzBFBgNVBAMM +PkNoaW5hIEludGVybmV0IE5ldHdvcmsgSW5mb3JtYXRpb24gQ2VudGVyIEVWIENlcnRpZmljYXRl +cyBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm35z7r07eKpkQ0H1UN+U8i6y +jUqORlTSIRLIOTJCBumD1Z9S7eVnAztUwYyZmczpwA//DdmEEbK40ctb3B75aDFk4Zv6dOtouSCV +98YPjUesWgbdYavi7NifFy2cyjw1l1VxzUOFsUcW9SxTgHbP0wBkvUCZ3czY28Sf1hNfQYOL+Q2H +klY0bBoQCxfVWhyXWIQ8hBouXJE0bhlffxdpxWXvayHG1VA6v2G5BY3vbzQ6sm8UY78WO5upKv23 +KzhmBsUs4qpnHkWnjQRmQvaPK++IIGmPMowUc9orhpFjIpryp9vOiYurXccUwVswah+xt54ugQEC +7c+WXmPbqOY4twIDAQABo2MwYTAfBgNVHSMEGDAWgBR8cks5x8DbYqVPm6oYNJKiyoOCWTAPBgNV +HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUfHJLOcfA22KlT5uqGDSSosqD +glkwDQYJKoZIhvcNAQEFBQADggEBACrDx0M3j92tpLIM7twUbY8opJhJywyA6vPtI2Z1fcXTIWd5 +0XPFtQO3WKwMVC/GVhMPMdoG52U7HW8228gd+f2ABsqjPWYWqJ1MFn3AlUa1UeTiH9fqBk1jjZaM +7+czV0I664zBechNdn3e9rG3geCg+aF4RhcaVpjwTj2rHO3sOdwHSPdj/gauwqRcalsyiMXHM4Ws +ZkJHwlgkmeHlPuV1LI5D1l08eB6olYIpUNHRFrrvwb562bTYzB5MRuF3sTGrvSrIzo9uoV1/A3U0 +5K2JRVRevq4opbs/eHnrc7MKDf2+yfdWrPa37S+bISnHOLaVxATywy39FCqQmbkHzJ8= +-----END CERTIFICATE----- + +Swisscom Root CA 2 +================== +-----BEGIN CERTIFICATE----- +MIIF2TCCA8GgAwIBAgIQHp4o6Ejy5e/DfEoeWhhntjANBgkqhkiG9w0BAQsFADBkMQswCQYDVQQG +EwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsTHERpZ2l0YWwgQ2VydGlmaWNhdGUgU2Vy +dmljZXMxGzAZBgNVBAMTElN3aXNzY29tIFJvb3QgQ0EgMjAeFw0xMTA2MjQwODM4MTRaFw0zMTA2 +MjUwNzM4MTRaMGQxCzAJBgNVBAYTAmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGln +aXRhbCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAyMIIC +IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAlUJOhJ1R5tMJ6HJaI2nbeHCOFvErjw0DzpPM +LgAIe6szjPTpQOYXTKueuEcUMncy3SgM3hhLX3af+Dk7/E6J2HzFZ++r0rk0X2s682Q2zsKwzxNo +ysjL67XiPS4h3+os1OD5cJZM/2pYmLcX5BtS5X4HAB1f2uY+lQS3aYg5oUFgJWFLlTloYhyxCwWJ +wDaCFCE/rtuh/bxvHGCGtlOUSbkrRsVPACu/obvLP+DHVxxX6NZp+MEkUp2IVd3Chy50I9AU/SpH +Wrumnf2U5NGKpV+GY3aFy6//SSj8gO1MedK75MDvAe5QQQg1I3ArqRa0jG6F6bYRzzHdUyYb3y1a +SgJA/MTAtukxGggo5WDDH8SQjhBiYEQN7Aq+VRhxLKX0srwVYv8c474d2h5Xszx+zYIdkeNL6yxS +NLCK/RJOlrDrcH+eOfdmQrGrrFLadkBXeyq96G4DsguAhYidDMfCd7Camlf0uPoTXGiTOmekl9Ab +mbeGMktg2M7v0Ax/lZ9vh0+Hio5fCHyqW/xavqGRn1V9TrALacywlKinh/LTSlDcX3KwFnUey7QY +Ypqwpzmqm59m2I2mbJYV4+by+PGDYmy7Velhk6M99bFXi08jsJvllGov34zflVEpYKELKeRcVVi3 +qPyZ7iVNTA6z00yPhOgpD/0QVAKFyPnlw4vP5w8CAwEAAaOBhjCBgzAOBgNVHQ8BAf8EBAMCAYYw +HQYDVR0hBBYwFDASBgdghXQBUwIBBgdghXQBUwIBMBIGA1UdEwEB/wQIMAYBAf8CAQcwHQYDVR0O +BBYEFE0mICKJS9PVpAqhb97iEoHF8TwuMB8GA1UdIwQYMBaAFE0mICKJS9PVpAqhb97iEoHF8Twu +MA0GCSqGSIb3DQEBCwUAA4ICAQAyCrKkG8t9voJXiblqf/P0wS4RfbgZPnm3qKhyN2abGu2sEzsO +v2LwnN+ee6FTSA5BesogpxcbtnjsQJHzQq0Qw1zv/2BZf82Fo4s9SBwlAjxnffUy6S8w5X2lejjQ +82YqZh6NM4OKb3xuqFp1mrjX2lhIREeoTPpMSQpKwhI3qEAMw8jh0FcNlzKVxzqfl9NX+Ave5XLz +o9v/tdhZsnPdTSpxsrpJ9csc1fV5yJmz/MFMdOO0vSk3FQQoHt5FRnDsr7p4DooqzgB53MBfGWcs +a0vvaGgLQ+OswWIJ76bdZWGgr4RVSJFSHMYlkSrQwSIjYVmvRRGFHQEkNI/Ps/8XciATwoCqISxx +OQ7Qj1zB09GOInJGTB2Wrk9xseEFKZZZ9LuedT3PDTcNYtsmjGOpI99nBjx8Oto0QuFmtEYE3saW +mA9LSHokMnWRn6z3aOkquVVlzl1h0ydw2Df+n7mvoC5Wt6NlUe07qxS/TFED6F+KBZvuim6c779o ++sjaC+NCydAXFJy3SuCvkychVSa1ZC+N8f+mQAWFBVzKBxlcCxMoTFh/wqXvRdpg065lYZ1Tg3TC +rvJcwhbtkj6EPnNgiLx29CzP0H1907he0ZESEOnN3col49XtmS++dYFLJPlFRpTJKSFTnCZFqhMX +5OfNeOI5wSsSnqaeG8XmDtkx2Q== +-----END CERTIFICATE----- + +Swisscom Root EV CA 2 +===================== +-----BEGIN CERTIFICATE----- +MIIF4DCCA8igAwIBAgIRAPL6ZOJ0Y9ON/RAdBB92ylgwDQYJKoZIhvcNAQELBQAwZzELMAkGA1UE +BhMCY2gxETAPBgNVBAoTCFN3aXNzY29tMSUwIwYDVQQLExxEaWdpdGFsIENlcnRpZmljYXRlIFNl +cnZpY2VzMR4wHAYDVQQDExVTd2lzc2NvbSBSb290IEVWIENBIDIwHhcNMTEwNjI0MDk0NTA4WhcN +MzEwNjI1MDg0NTA4WjBnMQswCQYDVQQGEwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsT +HERpZ2l0YWwgQ2VydGlmaWNhdGUgU2VydmljZXMxHjAcBgNVBAMTFVN3aXNzY29tIFJvb3QgRVYg +Q0EgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMT3HS9X6lds93BdY7BxUglgRCgz +o3pOCvrY6myLURYaVa5UJsTMRQdBTxB5f3HSek4/OE6zAMaVylvNwSqD1ycfMQ4jFrclyxy0uYAy +Xhqdk/HoPGAsp15XGVhRXrwsVgu42O+LgrQ8uMIkqBPHoCE2G3pXKSinLr9xJZDzRINpUKTk4Rti +GZQJo/PDvO/0vezbE53PnUgJUmfANykRHvvSEaeFGHR55E+FFOtSN+KxRdjMDUN/rhPSays/p8Li +qG12W0OfvrSdsyaGOx9/5fLoZigWJdBLlzin5M8J0TbDC77aO0RYjb7xnglrPvMyxyuHxuxenPaH +Za0zKcQvidm5y8kDnftslFGXEBuGCxobP/YCfnvUxVFkKJ3106yDgYjTdLRZncHrYTNaRdHLOdAG +alNgHa/2+2m8atwBz735j9m9W8E6X47aD0upm50qKGsaCnw8qyIL5XctcfaCNYGu+HuB5ur+rPQa +m3Rc6I8k9l2dRsQs0h4rIWqDJ2dVSqTjyDKXZpBy2uPUZC5f46Fq9mDU5zXNysRojddxyNMkM3Ox +bPlq4SjbX8Y96L5V5jcb7STZDxmPX2MYWFCBUWVv8p9+agTnNCRxunZLWB4ZvRVgRaoMEkABnRDi +xzgHcgplwLa7JSnaFp6LNYth7eVxV4O1PHGf40+/fh6Bn0GXAgMBAAGjgYYwgYMwDgYDVR0PAQH/ +BAQDAgGGMB0GA1UdIQQWMBQwEgYHYIV0AVMCAgYHYIV0AVMCAjASBgNVHRMBAf8ECDAGAQH/AgED +MB0GA1UdDgQWBBRF2aWBbj2ITY1x0kbBbkUe88SAnTAfBgNVHSMEGDAWgBRF2aWBbj2ITY1x0kbB +bkUe88SAnTANBgkqhkiG9w0BAQsFAAOCAgEAlDpzBp9SSzBc1P6xXCX5145v9Ydkn+0UjrgEjihL +j6p7jjm02Vj2e6E1CqGdivdj5eu9OYLU43otb98TPLr+flaYC/NUn81ETm484T4VvwYmneTwkLbU +wp4wLh/vx3rEUMfqe9pQy3omywC0Wqu1kx+AiYQElY2NfwmTv9SoqORjbdlk5LgpWgi/UOGED1V7 +XwgiG/W9mR4U9s70WBCCswo9GcG/W6uqmdjyMb3lOGbcWAXH7WMaLgqXfIeTK7KK4/HsGOV1timH +59yLGn602MnTihdsfSlEvoqq9X46Lmgxk7lq2prg2+kupYTNHAq4Sgj5nPFhJpiTt3tm7JFe3VE/ +23MPrQRYCd0EApUKPtN236YQHoA96M2kZNEzx5LH4k5E4wnJTsJdhw4Snr8PyQUQ3nqjsTzyP6Wq +J3mtMX0f/fwZacXduT98zca0wjAefm6S139hdlqP65VNvBFuIXxZN5nQBrz5Bm0yFqXZaajh3DyA +HmBR3NdUIR7KYndP+tiPsys6DXhyyWhBWkdKwqPrGtcKqzwyVcgKEZzfdNbwQBUdyLmPtTbFr/gi +uMod89a2GQ+fYWVq6nTIfI/DT11lgh/ZDYnadXL77/FHZxOzyNEZiCcmmpl5fx7kLD977vHeTYuW +l8PVP3wbI+2ksx0WckNLIOFZfsLorSa/ovc= +-----END CERTIFICATE----- + +CA Disig Root R1 +================ +-----BEGIN CERTIFICATE----- +MIIFaTCCA1GgAwIBAgIJAMMDmu5QkG4oMA0GCSqGSIb3DQEBBQUAMFIxCzAJBgNVBAYTAlNLMRMw +EQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMuMRkwFwYDVQQDExBDQSBEaXNp +ZyBSb290IFIxMB4XDTEyMDcxOTA5MDY1NloXDTQyMDcxOTA5MDY1NlowUjELMAkGA1UEBhMCU0sx +EzARBgNVBAcTCkJyYXRpc2xhdmExEzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERp +c2lnIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCqw3j33Jijp1pedxiy +3QRkD2P9m5YJgNXoqqXinCaUOuiZc4yd39ffg/N4T0Dhf9Kn0uXKE5Pn7cZ3Xza1lK/oOI7bm+V8 +u8yN63Vz4STN5qctGS7Y1oprFOsIYgrY3LMATcMjfF9DCCMyEtztDK3AfQ+lekLZWnDZv6fXARz2 +m6uOt0qGeKAeVjGu74IKgEH3G8muqzIm1Cxr7X1r5OJeIgpFy4QxTaz+29FHuvlglzmxZcfe+5nk +CiKxLU3lSCZpq+Kq8/v8kiky6bM+TR8noc2OuRf7JT7JbvN32g0S9l3HuzYQ1VTW8+DiR0jm3hTa +YVKvJrT1cU/J19IG32PK/yHoWQbgCNWEFVP3Q+V8xaCJmGtzxmjOZd69fwX3se72V6FglcXM6pM6 +vpmumwKjrckWtc7dXpl4fho5frLABaTAgqWjR56M6ly2vGfb5ipN0gTco65F97yLnByn1tUD3AjL +LhbKXEAz6GfDLuemROoRRRw1ZS0eRWEkG4IupZ0zXWX4Qfkuy5Q/H6MMMSRE7cderVC6xkGbrPAX +ZcD4XW9boAo0PO7X6oifmPmvTiT6l7Jkdtqr9O3jw2Dv1fkCyC2fg69naQanMVXVz0tv/wQFx1is +XxYb5dKj6zHbHzMVTdDypVP1y+E9Tmgt2BLdqvLmTZtJ5cUoobqwWsagtQIDAQABo0IwQDAPBgNV +HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUiQq0OJMa5qvum5EY+fU8PjXQ +04IwDQYJKoZIhvcNAQEFBQADggIBADKL9p1Kyb4U5YysOMo6CdQbzoaz3evUuii+Eq5FLAR0rBNR +xVgYZk2C2tXck8An4b58n1KeElb21Zyp9HWc+jcSjxyT7Ff+Bw+r1RL3D65hXlaASfX8MPWbTx9B +LxyE04nH4toCdu0Jz2zBuByDHBb6lM19oMgY0sidbvW9adRtPTXoHqJPYNcHKfyyo6SdbhWSVhlM +CrDpfNIZTUJG7L399ldb3Zh+pE3McgODWF3vkzpBemOqfDqo9ayk0d2iLbYq/J8BjuIQscTK5Gfb +VSUZP/3oNn6z4eGBrxEWi1CXYBmCAMBrTXO40RMHPuq2MU/wQppt4hF05ZSsjYSVPCGvxdpHyN85 +YmLLW1AL14FABZyb7bq2ix4Eb5YgOe2kfSnbSM6C3NQCjR0EMVrHS/BsYVLXtFHCgWzN4funodKS +ds+xDzdYpPJScWc/DIh4gInByLUfkmO+p3qKViwaqKactV2zY9ATIKHrkWzQjX2v3wvkF7mGnjix +lAxYjOBVqjtjbZqJYLhkKpLGN/R+Q0O3c+gB53+XD9fyexn9GtePyfqFa3qdnom2piiZk4hA9z7N +UaPK6u95RyG1/jLix8NRb76AdPCkwzryT+lf3xkK8jsTQ6wxpLPn6/wY1gGp8yqPNg7rtLG8t0zJ +a7+h89n07eLw4+1knj0vllJPgFOL +-----END CERTIFICATE----- + +CA Disig Root R2 +================ +-----BEGIN CERTIFICATE----- +MIIFaTCCA1GgAwIBAgIJAJK4iNuwisFjMA0GCSqGSIb3DQEBCwUAMFIxCzAJBgNVBAYTAlNLMRMw +EQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMuMRkwFwYDVQQDExBDQSBEaXNp +ZyBSb290IFIyMB4XDTEyMDcxOTA5MTUzMFoXDTQyMDcxOTA5MTUzMFowUjELMAkGA1UEBhMCU0sx +EzARBgNVBAcTCkJyYXRpc2xhdmExEzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERp +c2lnIFJvb3QgUjIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCio8QACdaFXS1tFPbC +w3OeNcJxVX6B+6tGUODBfEl45qt5WDza/3wcn9iXAng+a0EE6UG9vgMsRfYvZNSrXaNHPWSb6Wia +xswbP7q+sos0Ai6YVRn8jG+qX9pMzk0DIaPY0jSTVpbLTAwAFjxfGs3Ix2ymrdMxp7zo5eFm1tL7 +A7RBZckQrg4FY8aAamkw/dLukO8NJ9+flXP04SXabBbeQTg06ov80egEFGEtQX6sx3dOy1FU+16S +GBsEWmjGycT6txOgmLcRK7fWV8x8nhfRyyX+hk4kLlYMeE2eARKmK6cBZW58Yh2EhN/qwGu1pSqV +g8NTEQxzHQuyRpDRQjrOQG6Vrf/GlK1ul4SOfW+eioANSW1z4nuSHsPzwfPrLgVv2RvPN3YEyLRa +5Beny912H9AZdugsBbPWnDTYltxhh5EF5EQIM8HauQhl1K6yNg3ruji6DOWbnuuNZt2Zz9aJQfYE +koopKW1rOhzndX0CcQ7zwOe9yxndnWCywmZgtrEE7snmhrmaZkCo5xHtgUUDi/ZnWejBBhG93c+A +Ak9lQHhcR1DIm+YfgXvkRKhbhZri3lrVx/k6RGZL5DJUfORsnLMOPReisjQS1n6yqEm70XooQL6i +Fh/f5DcfEXP7kAplQ6INfPgGAVUzfbANuPT1rqVCV3w2EYx7XsQDnYx5nQIDAQABo0IwQDAPBgNV +HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUtZn4r7CU9eMg1gqtzk5WpC5u +Qu0wDQYJKoZIhvcNAQELBQADggIBACYGXnDnZTPIgm7ZnBc6G3pmsgH2eDtpXi/q/075KMOYKmFM +tCQSin1tERT3nLXK5ryeJ45MGcipvXrA1zYObYVybqjGom32+nNjf7xueQgcnYqfGopTpti72TVV +sRHFqQOzVju5hJMiXn7B9hJSi+osZ7z+Nkz1uM/Rs0mSO9MpDpkblvdhuDvEK7Z4bLQjb/D907Je +dR+Zlais9trhxTF7+9FGs9K8Z7RiVLoJ92Owk6Ka+elSLotgEqv89WBW7xBci8QaQtyDW2QOy7W8 +1k/BfDxujRNt+3vrMNDcTa/F1balTFtxyegxvug4BkihGuLq0t4SOVga/4AOgnXmt8kHbA7v/zjx +mHHEt38OFdAlab0inSvtBfZGR6ztwPDUO+Ls7pZbkBNOHlY667DvlruWIxG68kOGdGSVyCh13x01 +utI3gzhTODY7z2zp+WsO0PsE6E9312UBeIYMej4hYvF/Y3EMyZ9E26gnonW+boE+18DrG5gPcFw0 +sorMwIUY6256s/daoQe/qUKS82Ail+QUoQebTnbAjn39pCXHR+3/H3OszMOl6W8KjptlwlCFtaOg +UxLMVYdh84GuEEZhvUQhuMI9dM9+JDX6HAcOmz0iyu8xL4ysEr3vQCj8KWefshNPZiTEUxnpHikV +7+ZtsH8tZ/3zbBt1RqPlShfppNcL +-----END CERTIFICATE----- + +ACCVRAIZ1 +========= +-----BEGIN CERTIFICATE----- +MIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UEAwwJQUNDVlJB +SVoxMRAwDgYDVQQLDAdQS0lBQ0NWMQ0wCwYDVQQKDARBQ0NWMQswCQYDVQQGEwJFUzAeFw0xMTA1 +MDUwOTM3MzdaFw0zMDEyMzEwOTM3MzdaMEIxEjAQBgNVBAMMCUFDQ1ZSQUlaMTEQMA4GA1UECwwH +UEtJQUNDVjENMAsGA1UECgwEQUNDVjELMAkGA1UEBhMCRVMwggIiMA0GCSqGSIb3DQEBAQUAA4IC +DwAwggIKAoICAQCbqau/YUqXry+XZpp0X9DZlv3P4uRm7x8fRzPCRKPfmt4ftVTdFXxpNRFvu8gM +jmoYHtiP2Ra8EEg2XPBjs5BaXCQ316PWywlxufEBcoSwfdtNgM3802/J+Nq2DoLSRYWoG2ioPej0 +RGy9ocLLA76MPhMAhN9KSMDjIgro6TenGEyxCQ0jVn8ETdkXhBilyNpAlHPrzg5XPAOBOp0KoVdD +aaxXbXmQeOW1tDvYvEyNKKGno6e6Ak4l0Squ7a4DIrhrIA8wKFSVf+DuzgpmndFALW4ir50awQUZ +0m/A8p/4e7MCQvtQqR0tkw8jq8bBD5L/0KIV9VMJcRz/RROE5iZe+OCIHAr8Fraocwa48GOEAqDG +WuzndN9wrqODJerWx5eHk6fGioozl2A3ED6XPm4pFdahD9GILBKfb6qkxkLrQaLjlUPTAYVtjrs7 +8yM2x/474KElB0iryYl0/wiPgL/AlmXz7uxLaL2diMMxs0Dx6M/2OLuc5NF/1OVYm3z61PMOm3WR +5LpSLhl+0fXNWhn8ugb2+1KoS5kE3fj5tItQo05iifCHJPqDQsGH+tUtKSpacXpkatcnYGMN285J +9Y0fkIkyF/hzQ7jSWpOGYdbhdQrqeWZ2iE9x6wQl1gpaepPluUsXQA+xtrn13k/c4LOsOxFwYIRK +Q26ZIMApcQrAZQIDAQABo4ICyzCCAscwfQYIKwYBBQUHAQEEcTBvMEwGCCsGAQUFBzAChkBodHRw +Oi8vd3d3LmFjY3YuZXMvZmlsZWFkbWluL0FyY2hpdm9zL2NlcnRpZmljYWRvcy9yYWl6YWNjdjEu +Y3J0MB8GCCsGAQUFBzABhhNodHRwOi8vb2NzcC5hY2N2LmVzMB0GA1UdDgQWBBTSh7Tj3zcnk1X2 +VuqB5TbMjB4/vTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNKHtOPfNyeTVfZW6oHlNsyM +Hj+9MIIBcwYDVR0gBIIBajCCAWYwggFiBgRVHSAAMIIBWDCCASIGCCsGAQUFBwICMIIBFB6CARAA +QQB1AHQAbwByAGkAZABhAGQAIABkAGUAIABDAGUAcgB0AGkAZgBpAGMAYQBjAGkA8wBuACAAUgBh +AO0AegAgAGQAZQAgAGwAYQAgAEEAQwBDAFYAIAAoAEEAZwBlAG4AYwBpAGEAIABkAGUAIABUAGUA +YwBuAG8AbABvAGcA7QBhACAAeQAgAEMAZQByAHQAaQBmAGkAYwBhAGMAaQDzAG4AIABFAGwAZQBj +AHQAcgDzAG4AaQBjAGEALAAgAEMASQBGACAAUQA0ADYAMAAxADEANQA2AEUAKQAuACAAQwBQAFMA +IABlAG4AIABoAHQAdABwADoALwAvAHcAdwB3AC4AYQBjAGMAdgAuAGUAczAwBggrBgEFBQcCARYk +aHR0cDovL3d3dy5hY2N2LmVzL2xlZ2lzbGFjaW9uX2MuaHRtMFUGA1UdHwROMEwwSqBIoEaGRGh0 +dHA6Ly93d3cuYWNjdi5lcy9maWxlYWRtaW4vQXJjaGl2b3MvY2VydGlmaWNhZG9zL3JhaXphY2N2 +MV9kZXIuY3JsMA4GA1UdDwEB/wQEAwIBBjAXBgNVHREEEDAOgQxhY2N2QGFjY3YuZXMwDQYJKoZI +hvcNAQEFBQADggIBAJcxAp/n/UNnSEQU5CmH7UwoZtCPNdpNYbdKl02125DgBS4OxnnQ8pdpD70E +R9m+27Up2pvZrqmZ1dM8MJP1jaGo/AaNRPTKFpV8M9xii6g3+CfYCS0b78gUJyCpZET/LtZ1qmxN +YEAZSUNUY9rizLpm5U9EelvZaoErQNV/+QEnWCzI7UiRfD+mAM/EKXMRNt6GGT6d7hmKG9Ww7Y49 +nCrADdg9ZuM8Db3VlFzi4qc1GwQA9j9ajepDvV+JHanBsMyZ4k0ACtrJJ1vnE5Bc5PUzolVt3OAJ +TS+xJlsndQAJxGJ3KQhfnlmstn6tn1QwIgPBHnFk/vk4CpYY3QIUrCPLBhwepH2NDd4nQeit2hW3 +sCPdK6jT2iWH7ehVRE2I9DZ+hJp4rPcOVkkO1jMl1oRQQmwgEh0q1b688nCBpHBgvgW1m54ERL5h +I6zppSSMEYCUWqKiuUnSwdzRp+0xESyeGabu4VXhwOrPDYTkF7eifKXeVSUG7szAh1xA2syVP1Xg +Nce4hL60Xc16gwFy7ofmXx2utYXGJt/mwZrpHgJHnyqobalbz+xFd3+YJ5oyXSrjhO7FmGYvliAd +3djDJ9ew+f7Zfc3Qn48LFFhRny+Lwzgt3uiP1o2HpPVWQxaZLPSkVrQ0uGE3ycJYgBugl6H8WY3p +EfbRD0tVNEYqi4Y7 +-----END CERTIFICATE----- + +TWCA Global Root CA +=================== +-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgICDL4wDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVFcxEjAQBgNVBAoT +CVRBSVdBTi1DQTEQMA4GA1UECxMHUm9vdCBDQTEcMBoGA1UEAxMTVFdDQSBHbG9iYWwgUm9vdCBD +QTAeFw0xMjA2MjcwNjI4MzNaFw0zMDEyMzExNTU5NTlaMFExCzAJBgNVBAYTAlRXMRIwEAYDVQQK +EwlUQUlXQU4tQ0ExEDAOBgNVBAsTB1Jvb3QgQ0ExHDAaBgNVBAMTE1RXQ0EgR2xvYmFsIFJvb3Qg +Q0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCwBdvI64zEbooh745NnHEKH1Jw7W2C +nJfF10xORUnLQEK1EjRsGcJ0pDFfhQKX7EMzClPSnIyOt7h52yvVavKOZsTuKwEHktSz0ALfUPZV +r2YOy+BHYC8rMjk1Ujoog/h7FsYYuGLWRyWRzvAZEk2tY/XTP3VfKfChMBwqoJimFb3u/Rk28OKR +Q4/6ytYQJ0lM793B8YVwm8rqqFpD/G2Gb3PpN0Wp8DbHzIh1HrtsBv+baz4X7GGqcXzGHaL3SekV +tTzWoWH1EfcFbx39Eb7QMAfCKbAJTibc46KokWofwpFFiFzlmLhxpRUZyXx1EcxwdE8tmx2RRP1W +KKD+u4ZqyPpcC1jcxkt2yKsi2XMPpfRaAok/T54igu6idFMqPVMnaR1sjjIsZAAmY2E2TqNGtz99 +sy2sbZCilaLOz9qC5wc0GZbpuCGqKX6mOL6OKUohZnkfs8O1CWfe1tQHRvMq2uYiN2DLgbYPoA/p +yJV/v1WRBXrPPRXAb94JlAGD1zQbzECl8LibZ9WYkTunhHiVJqRaCPgrdLQABDzfuBSO6N+pjWxn +kjMdwLfS7JLIvgm/LCkFbwJrnu+8vyq8W8BQj0FwcYeyTbcEqYSjMq+u7msXi7Kx/mzhkIyIqJdI +zshNy/MGz19qCkKxHh53L46g5pIOBvwFItIm4TFRfTLcDwIDAQABoyMwITAOBgNVHQ8BAf8EBAMC +AQYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAXzSBdu+WHdXltdkCY4QWwa6g +cFGn90xHNcgL1yg9iXHZqjNB6hQbbCEAwGxCGX6faVsgQt+i0trEfJdLjbDorMjupWkEmQqSpqsn +LhpNgb+E1HAerUf+/UqdM+DyucRFCCEK2mlpc3INvjT+lIutwx4116KD7+U4x6WFH6vPNOw/KP4M +8VeGTslV9xzU2KV9Bnpv1d8Q34FOIWWxtuEXeZVFBs5fzNxGiWNoRI2T9GRwoD2dKAXDOXC4Ynsg +/eTb6QihuJ49CcdP+yz4k3ZB3lLg4VfSnQO8d57+nile98FRYB/e2guyLXW3Q0iT5/Z5xoRdgFlg +lPx4mI88k1HtQJAH32RjJMtOcQWh15QaiDLxInQirqWm2BJpTGCjAu4r7NRjkgtevi92a6O2JryP +A9gK8kxkRr05YuWW6zRjESjMlfGt7+/cgFhI6Uu46mWs6fyAtbXIRfmswZ/ZuepiiI7E8UuDEq3m +i4TWnsLrgxifarsbJGAzcMzs9zLzXNl5fe+epP7JI8Mk7hWSsT2RTyaGvWZzJBPqpK5jwa19hAM8 +EHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWzaGHQRiapIVJpLesux+t3 +zqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmyKwbQBM0= +-----END CERTIFICATE----- diff --git a/core/vendor/guzzlehttp/guzzle/src/functions.php b/core/vendor/guzzlehttp/guzzle/src/functions.php new file mode 100644 index 0000000000000000000000000000000000000000..852c6db91fcaca1b7928acd2dfca43f89dd17943 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/src/functions.php @@ -0,0 +1,296 @@ +<?php + +namespace GuzzleHttp; + +use GuzzleHttp\Event\CompleteEvent; +use GuzzleHttp\Event\ErrorEvent; +use GuzzleHttp\Event\RequestEvents; +use GuzzleHttp\Message\ResponseInterface; +use GuzzleHttp\UriTemplate; + +/** + * Send a custom request + * + * @param string $method HTTP request method + * @param string $url URL of the request + * @param array $options Options to use with the request. + * + * @return ResponseInterface + */ +function request($method, $url, array $options = []) +{ + static $client; + if (!$client) { + $client = new Client(); + } + + return $client->send($client->createRequest($method, $url, $options)); +} + +/** + * Send a GET request + * + * @param string $url URL of the request + * @param array $options Array of request options + * + * @return ResponseInterface + */ +function get($url, array $options = []) +{ + return request('GET', $url, $options); +} + +/** + * Send a HEAD request + * + * @param string $url URL of the request + * @param array $options Array of request options + * + * @return ResponseInterface + */ +function head($url, array $options = []) +{ + return request('HEAD', $url, $options); +} + +/** + * Send a DELETE request + * + * @param string $url URL of the request + * @param array $options Array of request options + * + * @return ResponseInterface + */ +function delete($url, array $options = []) +{ + return request('DELETE', $url, $options); +} + +/** + * Send a POST request + * + * @param string $url URL of the request + * @param array $options Array of request options + * + * @return ResponseInterface + */ +function post($url, array $options = []) +{ + return request('POST', $url, $options); +} + +/** + * Send a PUT request + * + * @param string $url URL of the request + * @param array $options Array of request options + * + * @return ResponseInterface + */ +function put($url, array $options = []) +{ + return request('PUT', $url, $options); +} + +/** + * Send a PATCH request + * + * @param string $url URL of the request + * @param array $options Array of request options + * + * @return ResponseInterface + */ +function patch($url, array $options = []) +{ + return request('PATCH', $url, $options); +} + +/** + * Send an OPTIONS request + * + * @param string $url URL of the request + * @param array $options Array of request options + * + * @return ResponseInterface + */ +function options($url, array $options = []) +{ + return request('OPTIONS', $url, $options); +} + +/** + * Convenience method for sending multiple requests in parallel and retrieving + * a hash map of requests to response objects or RequestException objects. + * + * Note: This method keeps every request and response in memory, and as such is + * NOT recommended when sending a large number or an indeterminable number of + * requests in parallel. + * + * @param ClientInterface $client Client used to send the requests + * @param array|\Iterator $requests Requests to send in parallel + * @param array $options Passes through the options available in + * {@see GuzzleHttp\ClientInterface::sendAll()} + * @return \SplObjectStorage Requests are the key and each value is a + * {@see GuzzleHttp\Message\ResponseInterface} if the request succeeded or + * a {@see GuzzleHttp\Exception\RequestException} if it failed. + * @throws \InvalidArgumentException if the event format is incorrect. + */ +function batch(ClientInterface $client, $requests, array $options = []) +{ + $hash = new \SplObjectStorage(); + foreach ($requests as $request) { + $hash->attach($request); + } + + $handler = [ + 'priority' => RequestEvents::EARLY, + 'once' => true, + 'fn' => function ($e) use ($hash) { $hash[$e->getRequest()] = $e; } + ]; + + // Merge the necessary complete and error events to the event listeners so + // that as each request succeeds or fails, it is added to the result hash. + foreach (['complete', 'error'] as $name) { + if (!isset($options[$name])) { + $options[$name] = $handler; + } elseif (is_callable($options[$name])) { + $options[$name] = [['fn' => $options[$name]], $handler]; + } elseif (is_array($options[$name])) { + $options[$name][] = $handler; + } else { + throw new \InvalidArgumentException('Invalid event format'); + } + } + + // Send the requests in parallel and aggregate the results. + $client->sendAll($requests, $options); + + // Update the received value for any of the intercepted requests. + foreach ($hash as $request) { + if ($hash[$request] instanceof CompleteEvent) { + $hash[$request] = $hash[$request]->getResponse(); + } elseif ($hash[$request] instanceof ErrorEvent) { + $hash[$request] = $hash[$request]->getException(); + } + } + + return $hash; +} + +/** + * Gets a value from an array using a path syntax to retrieve nested data. + * + * This method does not allow for keys that contain "/". You must traverse + * the array manually or using something more advanced like JMESPath to + * work with keys that contain "/". + * + * // Get the bar key of a set of nested arrays. + * // This is equivalent to $collection['foo']['baz']['bar'] but won't + * // throw warnings for missing keys. + * GuzzleHttp\get_path($data, 'foo/baz/bar'); + * + * @param array $data Data to retrieve values from + * @param string $path Path to traverse and retrieve a value from + * + * @return mixed|null + */ +function get_path($data, $path) +{ + $path = explode('/', $path); + + while (null !== ($part = array_shift($path))) { + if (!is_array($data) || !isset($data[$part])) { + return null; + } + $data = $data[$part]; + } + + return $data; +} + +/** + * Set a value in a nested array key. Keys will be created as needed to set the + * value. + * + * This function does not support keys that contain "/" or "[]" characters + * because these are special tokens used when traversing the data structure. + * A value may be prepended to an existing array by using "[]" as the final + * key of a path. + * + * GuzzleHttp\get_path($data, 'foo/baz'); // null + * GuzzleHttp\set_path($data, 'foo/baz/[]', 'a'); + * GuzzleHttp\set_path($data, 'foo/baz/[]', 'b'); + * GuzzleHttp\get_path($data, 'foo/baz'); + * // Returns ['a', 'b'] + * + * @param array $data Data to modify by reference + * @param string $path Path to set + * @param mixed $value Value to set at the key + * @throws \RuntimeException when trying to setPath using a nested path that + * travels through a scalar value. + */ +function set_path(&$data, $path, $value) +{ + $current =& $data; + $queue = explode('/', $path); + while (null !== ($key = array_shift($queue))) { + if (!is_array($current)) { + throw new \RuntimeException("Trying to setPath {$path}, but " + . "{$key} is set and is not an array"); + } elseif (!$queue) { + if ($key == '[]') { + $current[] = $value; + } else { + $current[$key] = $value; + } + } elseif (isset($current[$key])) { + $current =& $current[$key]; + } else { + $current[$key] = []; + $current =& $current[$key]; + } + } +} + +/** + * Expands a URI template + * + * @param string $template URI template + * @param array $variables Template variables + * + * @return string + */ +function uri_template($template, array $variables) +{ + if (function_exists('\\uri_template')) { + return \uri_template($template, $variables); + } + + static $uriTemplate; + if (!$uriTemplate) { + $uriTemplate = new UriTemplate(); + } + + return $uriTemplate->expand($template, $variables); +} + +/** + * @internal + */ +function deprecation_proxy($object, $name, $arguments, $map) +{ + if (!isset($map[$name])) { + throw new \BadMethodCallException('Unknown method, ' . $name); + } + + $message = sprintf('%s is deprecated and will be removed in a future ' + . 'version. Update your code to use the equivalent %s method ' + . 'instead to avoid breaking changes when this shim is removed.', + get_class($object) . '::' . $name . '()', + get_class($object) . '::' . $map[$name] . '()' + ); + + trigger_error($message, E_USER_DEPRECATED); + + return call_user_func_array([$object, $map[$name]], $arguments); +} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Adapter/Curl/AbstractCurl.php b/core/vendor/guzzlehttp/guzzle/tests/Adapter/Curl/AbstractCurl.php new file mode 100644 index 0000000000000000000000000000000000000000..fa8f1716d8853937a752e10a34eefc9ad175c9ff --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/tests/Adapter/Curl/AbstractCurl.php @@ -0,0 +1,92 @@ +<?php + +namespace GuzzleHttp\Tests\Adapter\Curl; + +use GuzzleHttp\Adapter\Transaction; +use GuzzleHttp\Client; +use GuzzleHttp\Event\CompleteEvent; +use GuzzleHttp\Event\ErrorEvent; +use GuzzleHttp\Exception\RequestException; +use GuzzleHttp\Message\Request; +use GuzzleHttp\Message\Response; +use GuzzleHttp\Tests\Server; +use GuzzleHttp\Url; + +abstract class AbstractCurl extends \PHPUnit_Framework_TestCase +{ + abstract protected function getAdapter($factory = null, $options = []); + + public function testSendsRequest() + { + Server::flush(); + Server::enqueue("HTTP/1.1 200 OK\r\nFoo: bar\r\nContent-Length: 0\r\n\r\n"); + $t = new Transaction(new Client(), new Request('GET', Server::$url)); + $a = $this->getAdapter(); + $response = $a->send($t); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('bar', $response->getHeader('Foo')); + } + + /** + * @expectedException \GuzzleHttp\Exception\RequestException + */ + public function testCatchesErrorWhenPreparing() + { + $r = new Request('GET', Server::$url); + $f = $this->getMockBuilder('GuzzleHttp\Adapter\Curl\CurlFactory') + ->setMethods(['__invoke']) + ->getMock(); + $f->expects($this->once()) + ->method('__invoke') + ->will($this->throwException(new RequestException('foo', $r))); + + $t = new Transaction(new Client(), $r); + $a = $this->getAdapter(null, ['handle_factory' => $f]); + $a->send($t); + } + + public function testDispatchesAfterSendEvent() + { + Server::flush(); + Server::enqueue("HTTP/1.1 201 OK\r\nContent-Length: 0\r\n\r\n"); + $r = new Request('GET', Server::$url); + $t = new Transaction(new Client(), $r); + $a = $this->getAdapter(); + $ev = null; + $r->getEmitter()->on('complete', function (CompleteEvent $e) use (&$ev) { + $ev = $e; + $e->intercept(new Response(200, ['Foo' => 'bar'])); + }); + $response = $a->send($t); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('bar', $response->getHeader('Foo')); + } + + public function testDispatchesErrorEventAndRecovers() + { + Server::flush(); + Server::enqueue("HTTP/1.1 201 OK\r\nContent-Length: 0\r\n\r\n"); + $r = new Request('GET', Server::$url); + $t = new Transaction(new Client(), $r); + $a = $this->getAdapter(); + $r->getEmitter()->once('complete', function (CompleteEvent $e) { + throw new RequestException('Foo', $e->getRequest()); + }); + $r->getEmitter()->on('error', function (ErrorEvent $e) { + $e->intercept(new Response(200, ['Foo' => 'bar'])); + }); + $response = $a->send($t); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('bar', $response->getHeader('Foo')); + } + + public function testStripsFragmentFromHost() + { + Server::flush(); + Server::enqueue("HTTP/1.1 200 OK\r\n\r\nContent-Length: 0\r\n\r\n"); + // This will fail if the removal of the #fragment is not performed + $url = Url::fromString(Server::$url)->setPath(null)->setFragment('foo'); + $client = new Client(); + $client->get($url); + } +} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Adapter/Curl/BatchContextTest.php b/core/vendor/guzzlehttp/guzzle/tests/Adapter/Curl/BatchContextTest.php new file mode 100644 index 0000000000000000000000000000000000000000..a3c82f2c553c9fb01f18df21d2ad3ccff86db86e --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/tests/Adapter/Curl/BatchContextTest.php @@ -0,0 +1,93 @@ +<?php + +namespace GuzzleHttp\Tests\Adapter\Curl; + +use GuzzleHttp\Adapter\Curl\BatchContext; +use GuzzleHttp\Adapter\Transaction; +use GuzzleHttp\Client; +use GuzzleHttp\Message\Request; + +/** + * @covers GuzzleHttp\Adapter\Curl\BatchContext + */ +class BatchContextTest extends \PHPUnit_Framework_TestCase +{ + public function testProvidesGetters() + { + $m = curl_multi_init(); + $b = new BatchContext($m, true); + $this->assertTrue($b->throwsExceptions()); + $this->assertSame($m, $b->getMultiHandle()); + $this->assertFalse($b->hasPending()); + curl_multi_close($m); + } + + public function testValidatesTransactionsAreNotAddedTwice() + { + $m = curl_multi_init(); + $b = new BatchContext($m, true); + $h = curl_init(); + $t = new Transaction( + new Client(), + new Request('GET', 'http://httbin.org') + ); + $b->addTransaction($t, $h); + try { + $b->addTransaction($t, $h); + $this->fail('Did not throw'); + } catch (\RuntimeException $e) { + curl_close($h); + curl_multi_close($m); + } + } + + public function testManagesHandles() + { + $m = curl_multi_init(); + $b = new BatchContext($m, true); + $h = curl_init(); + $t = new Transaction( + new Client(), + new Request('GET', 'http://httbin.org') + ); + $b->addTransaction($t, $h); + $this->assertSame($t, $b->findTransaction($h)); + $b->removeTransaction($t); + try { + $this->assertEquals([], $b->findTransaction($h)); + $this->fail('Did not throw'); + } catch (\RuntimeException $e) {} + curl_multi_close($m); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage Transaction not registered + */ + public function testThrowsWhenRemovingNonExistentTransaction() + { + $b = new BatchContext('foo', false); + $t = new Transaction( + new Client(), + new Request('GET', 'http://httbin.org') + ); + $b->removeTransaction($t); + } + + public function testReturnsPendingAsIteratorTypeObject() + { + $t1 = new Transaction(new Client(), new Request('GET', 'http://t.com')); + $t2 = new Transaction(new Client(), new Request('GET', 'http://t.com')); + $t3 = new Transaction(new Client(), new Request('GET', 'http://t.com')); + $iter = new \ArrayIterator([$t1, $t2, $t3]); + $b = new BatchContext('foo', false, $iter); + $this->assertTrue($b->hasPending()); + $this->assertSame($t1, $b->nextPending()); + $this->assertTrue($b->hasPending()); + $this->assertSame($t2, $b->nextPending()); + $this->assertTrue($b->hasPending()); + $this->assertSame($t3, $b->nextPending()); + $this->assertFalse($b->hasPending()); + $this->assertNull($b->nextPending()); + } +} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Adapter/Curl/CurlAdapterTest.php b/core/vendor/guzzlehttp/guzzle/tests/Adapter/Curl/CurlAdapterTest.php new file mode 100644 index 0000000000000000000000000000000000000000..dd6389a58ffa04763cf36c408586fad08016e4a4 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/tests/Adapter/Curl/CurlAdapterTest.php @@ -0,0 +1,120 @@ +<?php + +namespace GuzzleHttp\Tests\Adapter\Curl; + +require_once __DIR__ . '/AbstractCurl.php'; + +use GuzzleHttp\Adapter\Curl\CurlAdapter; +use GuzzleHttp\Adapter\Transaction; +use GuzzleHttp\Client; +use GuzzleHttp\Event\ErrorEvent; +use GuzzleHttp\Event\HeadersEvent; +use GuzzleHttp\Message\MessageFactory; +use GuzzleHttp\Message\Request; +use GuzzleHttp\Event\BeforeEvent; +use GuzzleHttp\Message\Response; +use GuzzleHttp\Tests\Server; + +/** + * @covers GuzzleHttp\Adapter\Curl\CurlAdapter + */ +class CurlAdapterTest extends AbstractCurl +{ + protected function setUp() + { + if (!function_exists('curl_reset')) { + $this->markTestSkipped('curl_reset() is not available'); + } + } + + protected function getAdapter($factory = null, $options = []) + { + return new CurlAdapter($factory ?: new MessageFactory(), $options); + } + + public function testCanSetMaxHandles() + { + $a = new CurlAdapter(new MessageFactory(), ['max_handles' => 10]); + $this->assertEquals(10, $this->readAttribute($a, 'maxHandles')); + } + + public function testCanInterceptBeforeSending() + { + $client = new Client(); + $request = new Request('GET', 'http://httpbin.org/get'); + $response = new Response(200); + $request->getEmitter()->on( + 'before', + function (BeforeEvent $e) use ($response) { + $e->intercept($response); + } + ); + $transaction = new Transaction($client, $request); + $f = 'does_not_work'; + $a = new CurlAdapter(new MessageFactory(), ['handle_factory' => $f]); + $a->send($transaction); + $this->assertSame($response, $transaction->getResponse()); + } + + /** + * @expectedException \GuzzleHttp\Exception\RequestException + * @expectedExceptionMessage cURL error + */ + public function testThrowsCurlErrors() + { + $client = new Client(); + $request = $client->createRequest('GET', 'http://localhost:123', [ + 'connect_timeout' => 0.001, + 'timeout' => 0.001, + ]); + $transaction = new Transaction($client, $request); + $a = new CurlAdapter(new MessageFactory()); + $a->send($transaction); + } + + public function testHandlesCurlErrors() + { + $client = new Client(); + $request = $client->createRequest('GET', 'http://localhost:123', [ + 'connect_timeout' => 0.001, + 'timeout' => 0.001, + ]); + $r = new Response(200); + $request->getEmitter()->on('error', function (ErrorEvent $e) use ($r) { + $e->intercept($r); + }); + $transaction = new Transaction($client, $request); + $a = new CurlAdapter(new MessageFactory()); + $a->send($transaction); + $this->assertSame($r, $transaction->getResponse()); + } + + public function testReleasesAdditionalEasyHandles() + { + Server::flush(); + Server::enqueue([ + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n" + ]); + $a = new CurlAdapter(new MessageFactory(), ['max_handles' => 2]); + $client = new Client(['base_url' => Server::$url, 'adapter' => $a]); + $request = $client->createRequest('GET', '/', [ + 'events' => [ + 'headers' => function (HeadersEvent $e) use ($client) { + $client->get('/', [ + 'events' => [ + 'headers' => function (HeadersEvent $e) { + $e->getClient()->get('/'); + } + ] + ]); + } + ] + ]); + $transaction = new Transaction($client, $request); + $a->send($transaction); + $this->assertCount(2, $this->readAttribute($a, 'handles')); + } +} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Adapter/Curl/CurlFactoryTest.php b/core/vendor/guzzlehttp/guzzle/tests/Adapter/Curl/CurlFactoryTest.php new file mode 100644 index 0000000000000000000000000000000000000000..9bf8fc70675e690927341fd9dc3654a0808a62de --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/tests/Adapter/Curl/CurlFactoryTest.php @@ -0,0 +1,291 @@ +<?php + +// Override curl_setopt_array() to get the last set curl options +namespace GuzzleHttp\Adapter\Curl +{ + function curl_setopt_array($handle, array $options) + { + $_SERVER['last_curl'] = $options; + \curl_setopt_array($handle, $options); + } +} + +namespace GuzzleHttp\Tests\Adapter\Curl { + + use GuzzleHttp\Adapter\Curl\MultiAdapter; + use GuzzleHttp\Event\BeforeEvent; + use GuzzleHttp\Message\RequestInterface; + use GuzzleHttp\Stream\Stream; + use GuzzleHttp\Adapter\Curl\CurlFactory; + use GuzzleHttp\Adapter\Transaction; + use GuzzleHttp\Client; + use GuzzleHttp\Message\MessageFactory; + use GuzzleHttp\Message\Request; + use GuzzleHttp\Tests\Server; + + /** + * @covers GuzzleHttp\Adapter\Curl\CurlFactory + */ + class CurlFactoryTest extends \PHPUnit_Framework_TestCase + { + /** @var \GuzzleHttp\Tests\Server */ + static $server; + + public static function setUpBeforeClass() + { + unset($_SERVER['last_curl']); + } + + public static function tearDownAfterClass() + { + unset($_SERVER['last_curl']); + } + + public function testCreatesCurlHandle() + { + Server::flush(); + Server::enqueue(["HTTP/1.1 200 OK\r\nFoo: Bar\r\n Baz: bam\r\nContent-Length: 2\r\n\r\nhi"]); + $request = new Request( + 'PUT', + Server::$url . 'haha', + ['Hi' => ' 123'], + Stream::factory('testing') + ); + $stream = Stream::factory(); + $request->getConfig()->set('save_to', $stream); + $request->getConfig()->set('verify', true); + $this->emit($request); + + $t = new Transaction(new Client(), $request); + $f = new CurlFactory(); + $h = $f($t, new MessageFactory()); + $this->assertInternalType('resource', $h); + curl_exec($h); + $response = $t->getResponse(); + $this->assertInstanceOf('GuzzleHttp\Message\ResponseInterface', $response); + $this->assertEquals('hi', $response->getBody()); + $this->assertEquals('Bar', $response->getHeader('Foo')); + $this->assertEquals('bam', $response->getHeader('Baz')); + curl_close($h); + + $sent = Server::received(true)[0]; + $this->assertEquals('PUT', $sent->getMethod()); + $this->assertEquals('/haha', $sent->getPath()); + $this->assertEquals('123', $sent->getHeader('Hi')); + $this->assertEquals('7', $sent->getHeader('Content-Length')); + $this->assertEquals('testing', $sent->getBody()); + $this->assertEquals('1.1', $sent->getProtocolVersion()); + $this->assertEquals('hi', (string) $stream); + + $this->assertEquals(2, $_SERVER['last_curl'][CURLOPT_SSL_VERIFYHOST]); + $this->assertEquals(true, $_SERVER['last_curl'][CURLOPT_SSL_VERIFYPEER]); + } + + public function testSendsHeadRequests() + { + Server::flush(); + Server::enqueue(["HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\n"]); + $request = new Request('HEAD', Server::$url); + $this->emit($request); + + $t = new Transaction(new Client(), $request); + $f = new CurlFactory(); + $h = $f($t, new MessageFactory()); + curl_exec($h); + curl_close($h); + $response = $t->getResponse(); + $this->assertEquals('2', $response->getHeader('Content-Length')); + $this->assertEquals('', $response->getBody()); + + $sent = Server::received(true)[0]; + $this->assertEquals('HEAD', $sent->getMethod()); + $this->assertEquals('/', $sent->getPath()); + } + + public function testSendsPostRequestWithNoBody() + { + Server::flush(); + Server::enqueue(["HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"]); + $request = new Request('POST', Server::$url); + $this->emit($request); + $t = new Transaction(new Client(), $request); + $f = new CurlFactory(); + $h = $f($t, new MessageFactory()); + curl_exec($h); + curl_close($h); + $sent = Server::received(true)[0]; + $this->assertEquals('POST', $sent->getMethod()); + $this->assertEquals('', $sent->getBody()); + } + + public function testSendsChunkedRequests() + { + $stream = $this->getMockBuilder('GuzzleHttp\Stream\Stream') + ->setConstructorArgs([fopen('php://temp', 'r+')]) + ->setMethods(['getSize']) + ->getMock(); + $stream->expects($this->any()) + ->method('getSize') + ->will($this->returnValue(null)); + $stream->write('foo'); + $stream->seek(0); + + Server::flush(); + Server::enqueue(["HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"]); + $request = new Request('PUT', Server::$url, [], $stream); + $this->emit($request); + $this->assertNull($request->getBody()->getSize()); + $t = new Transaction(new Client(), $request); + $f = new CurlFactory(); + $h = $f($t, new MessageFactory()); + curl_exec($h); + curl_close($h); + $sent = Server::received(false)[0]; + $this->assertContains('PUT / HTTP/1.1', $sent); + $this->assertContains('transfer-encoding: chunked', strtolower($sent)); + $this->assertContains("\r\n\r\nfoo", $sent); + } + + public function testDecodesGzippedResponses() + { + Server::flush(); + Server::enqueue(["HTTP/1.1 200 OK\r\nContent-Length: 3\r\n\r\nfoo"]); + $request = new Request('GET', Server::$url, ['Accept-Encoding' => '']); + $this->emit($request); + $t = new Transaction(new Client(), $request); + $f = new CurlFactory(); + $h = $f($t, new MessageFactory()); + curl_exec($h); + curl_close($h); + $this->assertEquals('foo', $t->getResponse()->getBody()); + $sent = Server::received(true)[0]; + $this->assertContains('gzip', $sent->getHeader('Accept-Encoding')); + } + + public function testAddsDebugInfoToBuffer() + { + $r = fopen('php://temp', 'r+'); + Server::flush(); + Server::enqueue(["HTTP/1.1 200 OK\r\nContent-Length: 3\r\n\r\nfoo"]); + $request = new Request('GET', Server::$url); + $request->getConfig()->set('debug', $r); + $this->emit($request); + $t = new Transaction(new Client(), $request); + $f = new CurlFactory(); + $h = $f($t, new MessageFactory()); + curl_exec($h); + curl_close($h); + rewind($r); + $this->assertNotEmpty(stream_get_contents($r)); + } + + public function testAddsProxyOptions() + { + $request = new Request('GET', Server::$url); + $this->emit($request); + $request->getConfig()->set('proxy', '123'); + $request->getConfig()->set('connect_timeout', 1); + $request->getConfig()->set('timeout', 2); + $request->getConfig()->set('cert', __FILE__); + $request->getConfig()->set('ssl_key', [__FILE__, '123']); + $request->getConfig()->set('verify', false); + $t = new Transaction(new Client(), $request); + $f = new CurlFactory(); + curl_close($f($t, new MessageFactory())); + $this->assertEquals('123', $_SERVER['last_curl'][CURLOPT_PROXY]); + $this->assertEquals(1000, $_SERVER['last_curl'][CURLOPT_CONNECTTIMEOUT_MS]); + $this->assertEquals(2000, $_SERVER['last_curl'][CURLOPT_TIMEOUT_MS]); + $this->assertEquals(__FILE__, $_SERVER['last_curl'][CURLOPT_SSLCERT]); + $this->assertEquals(__FILE__, $_SERVER['last_curl'][CURLOPT_SSLKEY]); + $this->assertEquals('123', $_SERVER['last_curl'][CURLOPT_SSLKEYPASSWD]); + $this->assertEquals(0, $_SERVER['last_curl'][CURLOPT_SSL_VERIFYHOST]); + $this->assertEquals(false, $_SERVER['last_curl'][CURLOPT_SSL_VERIFYPEER]); + } + + /** + * @expectedException \RuntimeException + */ + public function testEnsuresCertExists() + { + $request = new Request('GET', Server::$url); + $this->emit($request); + $request->getConfig()->set('cert', __FILE__ . 'ewfwef'); + $f = new CurlFactory(); + $f(new Transaction(new Client(), $request), new MessageFactory()); + } + + /** + * @expectedException \RuntimeException + */ + public function testEnsuresKeyExists() + { + $request = new Request('GET', Server::$url); + $this->emit($request); + $request->getConfig()->set('ssl_key', __FILE__ . 'ewfwef'); + $f = new CurlFactory(); + $f(new Transaction(new Client(), $request), new MessageFactory()); + } + + /** + * @expectedException \RuntimeException + */ + public function testEnsuresCacertExists() + { + $request = new Request('GET', Server::$url); + $this->emit($request); + $request->getConfig()->set('verify', __FILE__ . 'ewfwef'); + $f = new CurlFactory(); + $f(new Transaction(new Client(), $request), new MessageFactory()); + } + + public function testClientUsesSslByDefault() + { + Server::flush(); + Server::enqueue(["HTTP/1.1 200 OK\r\nContent-Length: 3\r\n\r\nfoo"]); + $f = new CurlFactory(); + $client = new Client([ + 'base_url' => Server::$url, + 'adapter' => new MultiAdapter(new MessageFactory(), ['handle_factory' => $f]) + ]); + $client->get(); + $this->assertEquals(2, $_SERVER['last_curl'][CURLOPT_SSL_VERIFYHOST]); + $this->assertEquals(true, $_SERVER['last_curl'][CURLOPT_SSL_VERIFYPEER]); + $this->assertFileExists($_SERVER['last_curl'][CURLOPT_CAINFO]); + } + + public function testConvertsConstantNameKeysToValues() + { + $request = new Request('GET', Server::$url); + $request->getConfig()->set('curl', ['CURLOPT_USERAGENT' => 'foo']); + $this->emit($request); + $f = new CurlFactory(); + curl_close($f(new Transaction(new Client(), $request), new MessageFactory())); + $this->assertEquals('foo', $_SERVER['last_curl'][CURLOPT_USERAGENT]); + } + + public function testStripsFragment() + { + $request = new Request('GET', Server::$url . '#foo'); + $this->emit($request); + $f = new CurlFactory(); + curl_close($f(new Transaction(new Client(), $request), new MessageFactory())); + $this->assertEquals(Server::$url, $_SERVER['last_curl'][CURLOPT_URL]); + } + + public function testDoesNotSendSizeTwice() + { + $request = new Request('PUT', Server::$url, [], Stream::factory(str_repeat('a', 32769))); + $this->emit($request); + $f = new CurlFactory(); + curl_close($f(new Transaction(new Client(), $request), new MessageFactory())); + $this->assertEquals(32769, $_SERVER['last_curl'][CURLOPT_INFILESIZE]); + $this->assertNotContains('Content-Length', implode(' ', $_SERVER['last_curl'][CURLOPT_HTTPHEADER])); + } + + private function emit(RequestInterface $request) + { + $event = new BeforeEvent(new Transaction(new Client(), $request)); + $request->getEmitter()->emit('before', $event); + } + } +} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Adapter/Curl/MultiAdapterTest.php b/core/vendor/guzzlehttp/guzzle/tests/Adapter/Curl/MultiAdapterTest.php new file mode 100644 index 0000000000000000000000000000000000000000..7477b53b9a928e8ead1848407ca7c435f97c89e0 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/tests/Adapter/Curl/MultiAdapterTest.php @@ -0,0 +1,196 @@ +<?php + +namespace GuzzleHttp\Tests\Adapter\Curl; + +require_once __DIR__ . '/AbstractCurl.php'; + +use GuzzleHttp\Adapter\Curl\MultiAdapter; +use GuzzleHttp\Adapter\Transaction; +use GuzzleHttp\Client; +use GuzzleHttp\Event\CompleteEvent; +use GuzzleHttp\Event\ErrorEvent; +use GuzzleHttp\Exception\RequestException; +use GuzzleHttp\Message\MessageFactory; +use GuzzleHttp\Message\Request; +use GuzzleHttp\Tests\Server; + +/** + * @covers GuzzleHttp\Adapter\Curl\MultiAdapter + */ +class MultiAdapterTest extends AbstractCurl +{ + protected function getAdapter($factory = null, $options = []) + { + return new MultiAdapter($factory ?: new MessageFactory(), $options); + } + + public function testSendsSingleRequest() + { + Server::flush(); + Server::enqueue("HTTP/1.1 200 OK\r\nFoo: bar\r\nContent-Length: 0\r\n\r\n"); + $t = new Transaction(new Client(), new Request('GET', Server::$url)); + $a = new MultiAdapter(new MessageFactory()); + $response = $a->send($t); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('bar', $response->getHeader('Foo')); + } + + public function testCanSetSelectTimeout() + { + $current = isset($_SERVER[MultiAdapter::ENV_SELECT_TIMEOUT]) + ? $_SERVER[MultiAdapter::ENV_SELECT_TIMEOUT]: null; + unset($_SERVER[MultiAdapter::ENV_SELECT_TIMEOUT]); + $a = new MultiAdapter(new MessageFactory()); + $this->assertEquals(1, $this->readAttribute($a, 'selectTimeout')); + $a = new MultiAdapter(new MessageFactory(), ['select_timeout' => 10]); + $this->assertEquals(10, $this->readAttribute($a, 'selectTimeout')); + $_SERVER[MultiAdapter::ENV_SELECT_TIMEOUT] = 2; + $a = new MultiAdapter(new MessageFactory()); + $this->assertEquals(2, $this->readAttribute($a, 'selectTimeout')); + $_SERVER[MultiAdapter::ENV_SELECT_TIMEOUT] = $current; + } + + /** + * @expectedException \GuzzleHttp\Exception\AdapterException + * @expectedExceptionMessage cURL error -2: + */ + public function testChecksCurlMultiResult() + { + MultiAdapter::throwMultiError(-2); + } + + public function testChecksForCurlException() + { + $request = new Request('GET', 'http://httbin.org'); + $transaction = $this->getMockBuilder('GuzzleHttp\Adapter\Transaction') + ->setMethods(['getRequest']) + ->disableOriginalConstructor() + ->getMock(); + $transaction->expects($this->exactly(2)) + ->method('getRequest') + ->will($this->returnValue($request)); + $context = $this->getMockBuilder('GuzzleHttp\Adapter\Curl\BatchContext') + ->setMethods(['throwsExceptions']) + ->disableOriginalConstructor() + ->getMock(); + $context->expects($this->once()) + ->method('throwsExceptions') + ->will($this->returnValue(true)); + $a = new MultiAdapter(new MessageFactory()); + $r = new \ReflectionMethod($a, 'isCurlException'); + $r->setAccessible(true); + try { + $r->invoke($a, $transaction, ['result' => -10], $context, []); + $this->fail('Did not throw'); + } catch (RequestException $e) { + $this->assertSame($request, $e->getRequest()); + $this->assertContains('[curl] (#-10) ', $e->getMessage()); + $this->assertContains($request->getUrl(), $e->getMessage()); + } + } + + public function testSendsParallelRequestsFromQueue() + { + $c = new Client(); + Server::flush(); + Server::enqueue([ + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n" + ]); + $transactions = [ + new Transaction($c, new Request('GET', Server::$url)), + new Transaction($c, new Request('PUT', Server::$url)), + new Transaction($c, new Request('HEAD', Server::$url)), + new Transaction($c, new Request('GET', Server::$url)) + ]; + $a = new MultiAdapter(new MessageFactory()); + $a->sendAll(new \ArrayIterator($transactions), 2); + foreach ($transactions as $t) { + $response = $t->getResponse(); + $this->assertNotNull($response); + $this->assertEquals(200, $response->getStatusCode()); + } + } + + public function testCreatesAndReleasesHandlesWhenNeeded() + { + $a = new MultiAdapter(new MessageFactory()); + $c = new Client([ + 'adapter' => $a, + 'base_url' => Server::$url + ]); + + Server::flush(); + Server::enqueue([ + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + ]); + + $ef = function (ErrorEvent $e) { throw $e->getException(); }; + + $request1 = $c->createRequest('GET', '/'); + $request1->getEmitter()->on('headers', function () use ($a, $c, $ef) { + $a->send(new Transaction($c, $c->createRequest('GET', '/', [ + 'events' => [ + 'headers' => function () use ($a, $c, $ef) { + $r = $c->createRequest('GET', '/', [ + 'events' => ['error' => ['fn' => $ef, 'priority' => 9999]] + ]); + $r->getEmitter()->once('headers', function () use ($a, $c, $r) { + $a->send(new Transaction($c, $r)); + }); + $a->send(new Transaction($c, $r)); + // Now, reuse an existing handle + $a->send(new Transaction($c, $r)); + }, + 'error' => ['fn' => $ef, 'priority' => 9999] + ] + ]))); + }); + + $request1->getEmitter()->on('error', $ef); + + $transactions = [ + new Transaction($c, $request1), + new Transaction($c, $c->createRequest('PUT')), + new Transaction($c, $c->createRequest('HEAD')) + ]; + + $a->sendAll(new \ArrayIterator($transactions), 2); + + foreach ($transactions as $index => $t) { + $response = $t->getResponse(); + $this->assertInstanceOf( + 'GuzzleHttp\\Message\\ResponseInterface', + $response, + 'Transaction at index ' . $index . ' did not populate response' + ); + $this->assertEquals(200, $response->getStatusCode()); + } + } + + public function testThrowsAndReleasesWhenErrorDuringCompleteEvent() + { + Server::flush(); + Server::enqueue("HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n"); + $request = new Request('GET', Server::$url); + $request->getEmitter()->on('complete', function (CompleteEvent $e) { + throw new RequestException('foo', $e->getRequest()); + }); + $t = new Transaction(new Client(), $request); + $a = new MultiAdapter(new MessageFactory()); + try { + $a->send($t); + $this->fail('Did not throw'); + } catch (RequestException $e) { + $this->assertSame($request, $e->getRequest()); + } + } +} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Adapter/Curl/RequestMediatorTest.php b/core/vendor/guzzlehttp/guzzle/tests/Adapter/Curl/RequestMediatorTest.php new file mode 100644 index 0000000000000000000000000000000000000000..740ceaa1e52791cfe01216f4f407ca8adb051c93 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/tests/Adapter/Curl/RequestMediatorTest.php @@ -0,0 +1,113 @@ +<?php + +namespace GuzzleHttp\Tests\Adapter\Curl; + +use GuzzleHttp\Adapter\Curl\MultiAdapter; +use GuzzleHttp\Adapter\Curl\RequestMediator; +use GuzzleHttp\Adapter\Transaction; +use GuzzleHttp\Client; +use GuzzleHttp\Event\HeadersEvent; +use GuzzleHttp\Message\MessageFactory; +use GuzzleHttp\Message\Request; +use GuzzleHttp\Stream\Stream; +use GuzzleHttp\Message\Response; +use GuzzleHttp\Tests\Server; + +/** + * @covers GuzzleHttp\Adapter\Curl\RequestMediator + */ +class RequestMediatorTest extends \PHPUnit_Framework_TestCase +{ + public function testSetsResponseBodyForDownload() + { + $body = Stream::factory(); + $request = new Request('GET', 'http://httbin.org'); + $ee = null; + $request->getEmitter()->on( + 'headers', + function (HeadersEvent $e) use (&$ee) { + $ee = $e; + } + ); + $t = new Transaction(new Client(), $request); + $m = new RequestMediator($t, new MessageFactory()); + $m->setResponseBody($body); + $this->assertEquals(18, $m->receiveResponseHeader(null, "HTTP/1.1 202 FOO\r\n")); + $this->assertEquals(10, $m->receiveResponseHeader(null, "Foo: Bar\r\n")); + $this->assertEquals(11, $m->receiveResponseHeader(null, "Baz : Bam\r\n")); + $this->assertEquals(19, $m->receiveResponseHeader(null, "Content-Length: 3\r\n")); + $this->assertEquals(2, $m->receiveResponseHeader(null, "\r\n")); + $this->assertNotNull($ee); + $this->assertEquals(202, $t->getResponse()->getStatusCode()); + $this->assertEquals('FOO', $t->getResponse()->getReasonPhrase()); + $this->assertEquals('Bar', $t->getResponse()->getHeader('Foo')); + $this->assertEquals('Bam', $t->getResponse()->getHeader('Baz')); + $m->writeResponseBody(null, 'foo'); + $this->assertEquals('foo', (string) $body); + $this->assertEquals('3', $t->getResponse()->getHeader('Content-Length')); + } + + public function testSendsToNewBodyWhenNot2xxResponse() + { + $body = Stream::factory(); + $request = new Request('GET', 'http://httbin.org'); + $t = new Transaction(new Client(), $request); + $m = new RequestMediator($t, new MessageFactory()); + $m->setResponseBody($body); + $this->assertEquals(27, $m->receiveResponseHeader(null, "HTTP/1.1 304 Not Modified\r\n")); + $this->assertEquals(2, $m->receiveResponseHeader(null, "\r\n")); + $this->assertEquals(304, $t->getResponse()->getStatusCode()); + $m->writeResponseBody(null, 'foo'); + $this->assertEquals('', (string) $body); + $this->assertEquals('foo', (string) $t->getResponse()->getBody()); + } + + public function testUsesDefaultBodyIfNoneSet() + { + $t = new Transaction(new Client(), new Request('GET', 'http://httbin.org')); + $t->setResponse(new Response(200)); + $m = new RequestMediator($t, new MessageFactory()); + $this->assertEquals(3, $m->writeResponseBody(null, 'foo')); + $this->assertEquals('foo', (string) $t->getResponse()->getBody()); + } + + public function testCanUseResponseBody() + { + $body = Stream::factory(); + $t = new Transaction(new Client(), new Request('GET', 'http://httbin.org')); + $t->setResponse(new Response(200, [], $body)); + $m = new RequestMediator($t, new MessageFactory()); + $this->assertEquals(3, $m->writeResponseBody(null, 'foo')); + $this->assertEquals('foo', (string) $body); + } + + public function testHandlesTransactionWithNoResponseWhenWritingBody() + { + $t = new Transaction(new Client(), new Request('GET', 'http://httbin.org')); + $m = new RequestMediator($t, new MessageFactory()); + $this->assertEquals(0, $m->writeResponseBody(null, 'test')); + } + + public function testReadsFromRequestBody() + { + $body = Stream::factory('foo'); + $t = new Transaction(new Client(), new Request('PUT', 'http://httbin.org', [], $body)); + $m = new RequestMediator($t, new MessageFactory()); + $this->assertEquals('foo', $m->readRequestBody(null, null, 3)); + } + + public function testEmitsHeadersEventForHeadRequest() + { + Server::enqueue(["HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nOK"]); + $ee = null; + $client = new Client(['adapter' => new MultiAdapter(new MessageFactory())]); + $client->head(Server::$url, [ + 'events' => [ + 'headers' => function (HeadersEvent $e) use (&$ee) { + $ee = $e; + } + ] + ]); + $this->assertInstanceOf('GuzzleHttp\\Event\\HeadersEvent', $ee); + } +} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Adapter/FakeParallelAdapterTest.php b/core/vendor/guzzlehttp/guzzle/tests/Adapter/FakeParallelAdapterTest.php new file mode 100644 index 0000000000000000000000000000000000000000..8140d462d0c9d753d425b1f08538b4b7c1195eda --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/tests/Adapter/FakeParallelAdapterTest.php @@ -0,0 +1,35 @@ +<?php + +namespace GuzzleHttp\Tests\Adapter; + +use GuzzleHttp\Adapter\FakeParallelAdapter; +use GuzzleHttp\Adapter\MockAdapter; +use GuzzleHttp\Client; +use GuzzleHttp\Message\Response; +use GuzzleHttp\Adapter\TransactionIterator; + +/** + * @covers GuzzleHttp\Adapter\FakeParallelAdapter + */ +class FakeParallelAdapterTest extends \PHPUnit_Framework_TestCase +{ + public function testSendsAllTransactions() + { + $client = new Client(); + $requests = [ + $client->createRequest('GET', 'http://httbin.org'), + $client->createRequest('HEAD', 'http://httbin.org'), + ]; + + $sent = []; + $f = new FakeParallelAdapter(new MockAdapter(function ($trans) use (&$sent) { + $sent[] = $trans->getRequest()->getMethod(); + return new Response(200); + })); + + $tIter = new TransactionIterator($requests, $client, []); + $f->sendAll($tIter, 2); + $this->assertContains('GET', $sent); + $this->assertContains('HEAD', $sent); + } +} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Adapter/MockAdapterTest.php b/core/vendor/guzzlehttp/guzzle/tests/Adapter/MockAdapterTest.php new file mode 100644 index 0000000000000000000000000000000000000000..07dbe0d587bcbdd725e418f77d71afb45d353e8f --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/tests/Adapter/MockAdapterTest.php @@ -0,0 +1,103 @@ +<?php + +namespace GuzzleHttp\Tests\Adapter; + +use GuzzleHttp\Adapter\MockAdapter; +use GuzzleHttp\Adapter\Transaction; +use GuzzleHttp\Adapter\TransactionInterface; +use GuzzleHttp\Client; +use GuzzleHttp\Event\CompleteEvent; +use GuzzleHttp\Event\ErrorEvent; +use GuzzleHttp\Exception\RequestException; +use GuzzleHttp\Message\Request; +use GuzzleHttp\Message\Response; +use GuzzleHttp\Stream; + +/** + * @covers GuzzleHttp\Adapter\MockAdapter + */ +class MockAdapterTest extends \PHPUnit_Framework_TestCase +{ + public function testYieldsMockResponse() + { + $response = new Response(200); + $m = new MockAdapter(); + $m->setResponse($response); + $this->assertSame($response, $m->send(new Transaction(new Client(), new Request('GET', 'http://httbin.org')))); + } + + public function testMocksWithCallable() + { + $response = new Response(200); + $r = function (TransactionInterface $trans) use ($response) { + return $response; + }; + $m = new MockAdapter($r); + $this->assertSame($response, $m->send(new Transaction(new Client(), new Request('GET', 'http://httbin.org')))); + } + + /** + * @expectedException \RuntimeException + */ + public function testValidatesResponses() + { + $m = new MockAdapter(); + $m->setResponse('foo'); + $m->send(new Transaction(new Client(), new Request('GET', 'http://httbin.org'))); + } + + public function testHandlesErrors() + { + $m = new MockAdapter(); + $m->setResponse(new Response(404)); + $request = new Request('GET', 'http://httbin.org'); + $c = false; + $request->getEmitter()->once('complete', function (CompleteEvent $e) use (&$c) { + $c = true; + throw new RequestException('foo', $e->getRequest()); + }); + $request->getEmitter()->on('error', function (ErrorEvent $e) { + $e->intercept(new Response(201)); + }); + $r = $m->send(new Transaction(new Client(), $request)); + $this->assertTrue($c); + $this->assertEquals(201, $r->getStatusCode()); + } + + /** + * @expectedException \GuzzleHttp\Exception\RequestException + */ + public function testThrowsUnhandledErrors() + { + $m = new MockAdapter(); + $m->setResponse(new Response(404)); + $request = new Request('GET', 'http://httbin.org'); + $request->getEmitter()->once('complete', function (CompleteEvent $e) { + throw new RequestException('foo', $e->getRequest()); + }); + $m->send(new Transaction(new Client(), $request)); + } + + public function testReadsRequestBody() + { + $response = new Response(200); + $m = new MockAdapter($response); + $m->setResponse($response); + $body = Stream\create('foo'); + $request = new Request('PUT', 'http://httpbin.org/put', [], $body); + $this->assertSame($response, $m->send(new Transaction(new Client(), $request))); + $this->assertEquals(3, $body->tell()); + } + + public function testEmitsHeadersEvent() + { + $m = new MockAdapter(new Response(404)); + $request = new Request('GET', 'http://httbin.org'); + $called = false; + $request->getEmitter()->once('headers', function () use (&$called) { + $called = true; + }); + $m->send(new Transaction(new Client(), $request)); + $this->assertTrue($called); + } +} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Adapter/StreamAdapterTest.php b/core/vendor/guzzlehttp/guzzle/tests/Adapter/StreamAdapterTest.php new file mode 100644 index 0000000000000000000000000000000000000000..b4ed7683bc8b64a3fa252e2fdb3851eed1a45336 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/tests/Adapter/StreamAdapterTest.php @@ -0,0 +1,371 @@ +<?php + +namespace GuzzleHttp\Tests\Adapter; + +use GuzzleHttp\Adapter\StreamAdapter; +use GuzzleHttp\Client; +use GuzzleHttp\Event\ErrorEvent; +use GuzzleHttp\Message\MessageFactory; +use GuzzleHttp\Message\Response; +use GuzzleHttp\Stream\Stream; +use GuzzleHttp\Tests\Server; + +/** + * @covers GuzzleHttp\Adapter\StreamAdapter + */ +class StreamAdapterTest extends \PHPUnit_Framework_TestCase +{ + public function testReturnsResponseForSuccessfulRequest() + { + Server::flush(); + Server::enqueue( + "HTTP/1.1 200 OK\r\nFoo: Bar\r\nContent-Length: 2\r\n\r\nhi" + ); + $client = new Client([ + 'base_url' => Server::$url, + 'adapter' => new StreamAdapter(new MessageFactory()) + ]); + $response = $client->get('/', ['headers' => ['Foo' => 'Bar']]); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('OK', $response->getReasonPhrase()); + $this->assertEquals('Bar', $response->getHeader('Foo')); + $this->assertEquals('2', $response->getHeader('Content-Length')); + $this->assertEquals('hi', $response->getBody()); + $sent = Server::received(true)[0]; + $this->assertEquals('GET', $sent->getMethod()); + $this->assertEquals('/', $sent->getResource()); + $this->assertEquals('127.0.0.1:8124', $sent->getHeader('host')); + $this->assertEquals('Bar', $sent->getHeader('foo')); + $this->assertTrue($sent->hasHeader('user-agent')); + } + + /** + * @expectedException \GuzzleHttp\Exception\RequestException + * @expectedExceptionMessage Error creating resource. [url] http://localhost:123 [proxy] tcp://localhost:1234 + */ + public function testThrowsExceptionsCaughtDuringTransfer() + { + Server::flush(); + $client = new Client([ + 'adapter' => new StreamAdapter(new MessageFactory()), + ]); + $client->get('http://localhost:123', [ + 'timeout' => 0.01, + 'proxy' => 'tcp://localhost:1234' + ]); + } + + /** + * @expectedException \GuzzleHttp\Exception\RequestException + * @expectedExceptionMessage URL is invalid: ftp://localhost:123 + */ + public function testEnsuresTheHttpProtocol() + { + Server::flush(); + $client = new Client([ + 'adapter' => new StreamAdapter(new MessageFactory()), + ]); + $client->get('ftp://localhost:123'); + } + + public function testCanHandleExceptionsUsingEvents() + { + Server::flush(); + $client = new Client([ + 'adapter' => new StreamAdapter(new MessageFactory()) + ]); + $request = $client->createRequest('GET', Server::$url); + $mockResponse = new Response(200); + $request->getEmitter()->on( + 'error', + function (ErrorEvent $e) use ($mockResponse) { + $e->intercept($mockResponse); + } + ); + $this->assertSame($mockResponse, $client->send($request)); + } + + public function testEmitsAfterSendEvent() + { + $ee = null; + Server::flush(); + Server::enqueue( + "HTTP/1.1 200 OK\r\nFoo: Bar\r\nContent-Length: 8\r\n\r\nhi there" + ); + $client = new Client(['adapter' => new StreamAdapter(new MessageFactory())]); + $request = $client->createRequest('GET', Server::$url); + $request->getEmitter()->on('complete', function ($e) use (&$ee) { + $ee = $e; + }); + $client->send($request); + $this->assertInstanceOf('GuzzleHttp\Event\CompleteEvent', $ee); + $this->assertSame($request, $ee->getRequest()); + $this->assertEquals(200, $ee->getResponse()->getStatusCode()); + } + + public function testStreamAttributeKeepsStreamOpen() + { + Server::flush(); + Server::enqueue( + "HTTP/1.1 200 OK\r\nFoo: Bar\r\nContent-Length: 8\r\n\r\nhi there" + ); + $client = new Client([ + 'base_url' => Server::$url, + 'adapter' => new StreamAdapter(new MessageFactory()) + ]); + $response = $client->put('/foo', [ + 'headers' => ['Foo' => 'Bar'], + 'body' => 'test', + 'stream' => true + ]); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('OK', $response->getReasonPhrase()); + $this->assertEquals('8', $response->getHeader('Content-Length')); + $body = $response->getBody(); + $this->assertEquals('http', $body->getMetadata()['wrapper_type']); + $this->assertEquals(8, $body->getMetadata()['unread_bytes']); + $this->assertEquals(Server::$url . 'foo', $body->getMetadata()['uri']); + $this->assertEquals('hi', $body->read(2)); + $body->close(); + + $sent = Server::received(true)[0]; + $this->assertEquals('PUT', $sent->getMethod()); + $this->assertEquals('/foo', $sent->getResource()); + $this->assertEquals('127.0.0.1:8124', $sent->getHeader('host')); + $this->assertEquals('Bar', $sent->getHeader('foo')); + $this->assertTrue($sent->hasHeader('user-agent')); + } + + public function testDrainsResponseIntoTempStream() + { + Server::flush(); + Server::enqueue("HTTP/1.1 200 OK\r\nFoo: Bar\r\nContent-Length: 8\r\n\r\nhi there"); + $client = new Client([ + 'base_url' => Server::$url, + 'adapter' => new StreamAdapter(new MessageFactory()) + ]); + $response = $client->get('/'); + $body = $response->getBody(); + $this->assertEquals('php://temp', $body->getMetadata()['uri']); + $this->assertEquals('hi', $body->read(2)); + $body->close(); + } + + public function testDrainsResponseIntoSaveToBody() + { + $r = fopen('php://temp', 'r+'); + Server::flush(); + Server::enqueue("HTTP/1.1 200 OK\r\nFoo: Bar\r\nContent-Length: 8\r\n\r\nhi there"); + $client = new Client([ + 'base_url' => Server::$url, + 'adapter' => new StreamAdapter(new MessageFactory()) + ]); + $response = $client->get('/', ['save_to' => $r]); + $body = $response->getBody(); + $this->assertEquals('php://temp', $body->getMetadata()['uri']); + $this->assertEquals('hi', $body->read(2)); + $this->assertEquals(' there', stream_get_contents($r)); + $body->close(); + } + + public function testDrainsResponseIntoSaveToBodyAtPath() + { + $tmpfname = tempnam('/tmp', 'save_to_path'); + Server::flush(); + Server::enqueue("HTTP/1.1 200 OK\r\nFoo: Bar\r\nContent-Length: 8\r\n\r\nhi there"); + $client = new Client([ + 'base_url' => Server::$url, + 'adapter' => new StreamAdapter(new MessageFactory()) + ]); + $response = $client->get('/', ['save_to' => $tmpfname]); + $body = $response->getBody(); + $this->assertEquals($tmpfname, $body->getMetadata()['uri']); + $this->assertEquals('hi', $body->read(2)); + $body->close(); + unlink($tmpfname); + } + + public function testAddsGzipFilterIfAcceptHeaderIsPresent() + { + Server::flush(); + Server::enqueue("HTTP/1.1 200 OK\r\nFoo: Bar\r\nContent-Length: 8\r\n\r\nhi there"); + $client = new Client([ + 'base_url' => Server::$url, + 'adapter' => new StreamAdapter(new MessageFactory()) + ]); + $response = $client->get('/', [ + 'headers' => ['Accept-Encoding' => 'gzip'], + 'stream' => true + ]); + $body = $response->getBody(); + $this->assertEquals('compress.zlib://http://127.0.0.1:8124/', $body->getMetadata()['uri']); + } + + protected function getStreamFromBody(Stream $body) + { + $r = new \ReflectionProperty($body, 'stream'); + $r->setAccessible(true); + + return $r->getValue($body); + } + + protected function getSendResult(array $opts) + { + Server::enqueue("HTTP/1.1 200 OK\r\nFoo: Bar\r\nContent-Length: 8\r\n\r\nhi there"); + $client = new Client(['adapter' => new StreamAdapter(new MessageFactory())]); + + return $client->get(Server::$url, $opts); + } + + public function testAddsProxy() + { + $body = $this->getSendResult(['stream' => true, 'proxy' => '127.0.0.1:8124'])->getBody(); + $opts = stream_context_get_options($this->getStreamFromBody($body)); + $this->assertEquals('127.0.0.1:8124', $opts['http']['proxy']); + } + + public function testAddsTimeout() + { + $body = $this->getSendResult(['stream' => true, 'timeout' => 200])->getBody(); + $opts = stream_context_get_options($this->getStreamFromBody($body)); + $this->assertEquals(200, $opts['http']['timeout']); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage SSL certificate authority file not found: /does/not/exist + */ + public function testVerifiesVerifyIsValidIfPath() + { + (new Client([ + 'adapter' => new StreamAdapter(new MessageFactory()), + 'base_url' => Server::$url, + 'defaults' => ['verify' => '/does/not/exist'] + ]))->get('/'); + } + + public function testVerifyCanBeDisabled() + { + Server::enqueue("HTTP/1.1 200\r\nContent-Length: 0\r\n\r\n"); + (new Client([ + 'adapter' => new StreamAdapter(new MessageFactory()), + 'base_url' => Server::$url, + 'defaults' => ['verify' => false] + ]))->get('/'); + } + + public function testVerifyCanBeSetToPath() + { + $path = __DIR__ . '/../../src/cacert.pem'; + $this->assertFileExists($path); + $body = $this->getSendResult(['stream' => true, 'verify' => $path])->getBody(); + $opts = stream_context_get_options($this->getStreamFromBody($body)); + $this->assertEquals(true, $opts['http']['verify_peer']); + $this->assertEquals($path, $opts['http']['cafile']); + $this->assertTrue(file_exists($opts['http']['cafile'])); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage SSL certificate not found: /does/not/exist + */ + public function testVerifiesCertIfValidPath() + { + (new Client([ + 'adapter' => new StreamAdapter(new MessageFactory()), + 'base_url' => Server::$url, + 'defaults' => ['cert' => '/does/not/exist'] + ]))->get('/'); + } + + public function testCanSetPasswordWhenSettingCert() + { + $path = __DIR__ . '/../../src/cacert.pem'; + $body = $this->getSendResult(['stream' => true, 'cert' => [$path, 'foo']])->getBody(); + $opts = stream_context_get_options($this->getStreamFromBody($body)); + $this->assertEquals($path, $opts['http']['local_cert']); + $this->assertEquals('foo', $opts['http']['passphrase']); + } + + public function testDebugAttributeWritesStreamInfoToTempBufferByDefault() + { + + Server::flush(); + Server::enqueue("HTTP/1.1 200 OK\r\nFoo: Bar\r\nContent-Length: 8\r\n\r\nhi there"); + $client = new Client([ + 'base_url' => Server::$url, + 'adapter' => new StreamAdapter(new MessageFactory()) + ]); + ob_start(); + $client->get('/', ['debug' => true]); + $contents = ob_get_clean(); + $this->assertContains('<http://127.0.0.1:8124/> [CONNECT]', $contents); + $this->assertContains('<http://127.0.0.1:8124/> [FILE_SIZE_IS]', $contents); + $this->assertContains('<http://127.0.0.1:8124/> [PROGRESS]', $contents); + } + + public function testDebugAttributeWritesStreamInfoToBuffer() + { + $buffer = fopen('php://temp', 'r+'); + Server::flush(); + Server::enqueue("HTTP/1.1 200 OK\r\nContent-Length: 8\r\nContent-Type: text/plain\r\n\r\nhi there"); + $client = new Client([ + 'base_url' => Server::$url, + 'adapter' => new StreamAdapter(new MessageFactory()) + ]); + $client->get('/', ['debug' => $buffer]); + fseek($buffer, 0); + $contents = stream_get_contents($buffer); + $this->assertContains('<http://127.0.0.1:8124/> [CONNECT]', $contents); + $this->assertContains('<http://127.0.0.1:8124/> [FILE_SIZE_IS] message: "Content-Length: 8"', $contents); + $this->assertContains('<http://127.0.0.1:8124/> [PROGRESS] bytes_max: "8"', $contents); + $this->assertContains('<http://127.0.0.1:8124/> [MIME_TYPE_IS] message: "text/plain"', $contents); + } + + public function testAddsProxyByProtocol() + { + $url = str_replace('http', 'tcp', Server::$url); + $body = $this->getSendResult(['stream' => true, 'proxy' => ['http' => $url]])->getBody(); + $opts = stream_context_get_options($this->getStreamFromBody($body)); + $this->assertEquals($url, $opts['http']['proxy']); + } + + public function testPerformsShallowMergeOfCustomContextOptions() + { + $body = $this->getSendResult([ + 'stream' => true, + 'config' => [ + 'stream_context' => [ + 'http' => [ + 'request_fulluri' => true, + 'method' => 'HEAD' + ], + 'socket' => [ + 'bindto' => '127.0.0.1:0' + ], + 'ssl' => [ + 'verify_peer' => false + ] + ] + ] + ])->getBody(); + + $opts = stream_context_get_options($this->getStreamFromBody($body)); + $this->assertEquals('HEAD', $opts['http']['method']); + $this->assertTrue($opts['http']['request_fulluri']); + $this->assertFalse($opts['ssl']['verify_peer']); + $this->assertEquals('127.0.0.1:0', $opts['socket']['bindto']); + } + + /** + * @expectedException \GuzzleHttp\Exception\RequestException + * @expectedExceptionMessage stream_context must be an array + */ + public function testEnsuresThatStreamContextIsAnArray() + { + $this->getSendResult([ + 'stream' => true, + 'config' => ['stream_context' => 'foo'] + ]); + } +} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Adapter/StreamingProxyAdapterTest.php b/core/vendor/guzzlehttp/guzzle/tests/Adapter/StreamingProxyAdapterTest.php new file mode 100644 index 0000000000000000000000000000000000000000..8eb6e686149bf2850970fa6c1ec7836a31a0094e --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/tests/Adapter/StreamingProxyAdapterTest.php @@ -0,0 +1,54 @@ +<?php + +namespace GuzzleHttp\Tests\Adapter; + +use GuzzleHttp\Adapter\Transaction; +use GuzzleHttp\Client; +use GuzzleHttp\Message\Request; +use GuzzleHttp\Adapter\StreamingProxyAdapter; +use GuzzleHttp\Message\Response; + +/** + * @covers GuzzleHttp\Adapter\StreamingProxyAdapter + */ +class StreamingProxyAdapterTest extends \PHPUnit_Framework_TestCase +{ + public function testSendsWithDefaultAdapter() + { + $response = new Response(200); + $mock = $this->getMockBuilder('GuzzleHttp\Adapter\AdapterInterface') + ->setMethods(['send']) + ->getMockForAbstractClass(); + $mock->expects($this->once()) + ->method('send') + ->will($this->returnValue($response)); + $streaming = $this->getMockBuilder('GuzzleHttp\Adapter\AdapterInterface') + ->setMethods(['send']) + ->getMockForAbstractClass(); + $streaming->expects($this->never()) + ->method('send'); + + $s = new StreamingProxyAdapter($mock, $streaming); + $this->assertSame($response, $s->send(new Transaction(new Client(), new Request('GET', '/')))); + } + + public function testSendsWithStreamingAdapter() + { + $response = new Response(200); + $mock = $this->getMockBuilder('GuzzleHttp\Adapter\AdapterInterface') + ->setMethods(['send']) + ->getMockForAbstractClass(); + $mock->expects($this->never()) + ->method('send'); + $streaming = $this->getMockBuilder('GuzzleHttp\Adapter\AdapterInterface') + ->setMethods(['send']) + ->getMockForAbstractClass(); + $streaming->expects($this->once()) + ->method('send') + ->will($this->returnValue($response)); + $request = new Request('GET', '/'); + $request->getConfig()->set('stream', true); + $s = new StreamingProxyAdapter($mock, $streaming); + $this->assertSame($response, $s->send(new Transaction(new Client(), $request))); + } +} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Adapter/TransactionIteratorTest.php b/core/vendor/guzzlehttp/guzzle/tests/Adapter/TransactionIteratorTest.php new file mode 100644 index 0000000000000000000000000000000000000000..c33b1757d5eb6aff2c8af064a096b3f84620351a --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/tests/Adapter/TransactionIteratorTest.php @@ -0,0 +1,172 @@ +<?php + +namespace GuzzleHttp\Tests\Http; + +use GuzzleHttp\Client; +use GuzzleHttp\Adapter\TransactionIterator; + +class TransactionIteratorTest extends \PHPUnit_Framework_TestCase +{ + /** + * @expectedException \InvalidArgumentException + */ + public function testValidatesConstructor() + { + new TransactionIterator('foo', new Client(), []); + } + + public function testCreatesTransactions() + { + $client = new Client(); + $requests = [ + $client->createRequest('GET', 'http://test.com'), + $client->createRequest('POST', 'http://test.com'), + $client->createRequest('PUT', 'http://test.com'), + ]; + $t = new TransactionIterator($requests, $client, []); + $this->assertEquals(0, $t->key()); + $this->assertTrue($t->valid()); + $this->assertEquals('GET', $t->current()->getRequest()->getMethod()); + $t->next(); + $this->assertEquals(1, $t->key()); + $this->assertTrue($t->valid()); + $this->assertEquals('POST', $t->current()->getRequest()->getMethod()); + $t->next(); + $this->assertEquals(2, $t->key()); + $this->assertTrue($t->valid()); + $this->assertEquals('PUT', $t->current()->getRequest()->getMethod()); + } + + public function testCanForeach() + { + $c = new Client(); + $requests = [ + $c->createRequest('GET', 'http://test.com'), + $c->createRequest('POST', 'http://test.com'), + $c->createRequest('PUT', 'http://test.com'), + ]; + + $t = new TransactionIterator(new \ArrayIterator($requests), $c, []); + $methods = []; + + foreach ($t as $trans) { + $this->assertInstanceOf( + 'GuzzleHttp\Adapter\TransactionInterface', + $trans + ); + $methods[] = $trans->getRequest()->getMethod(); + } + + $this->assertEquals(['GET', 'POST', 'PUT'], $methods); + } + + /** + * @expectedException \RuntimeException + */ + public function testValidatesEachElement() + { + $c = new Client(); + $requests = ['foo']; + $t = new TransactionIterator(new \ArrayIterator($requests), $c, []); + iterator_to_array($t); + } + + public function testRegistersEvents() + { + $fn = function() {}; + $c = new Client(); + $requests = [$c->createRequest('GET', 'http://test.com')]; + $trans = new TransactionIterator(new \ArrayIterator($requests), $c, [ + 'before' => $fn, + 'complete' => $fn, + 'error' => $fn, + ]); + + $t = iterator_to_array($trans)[0]; + $em = $t->getRequest()->getEmitter(); + $this->assertSame($fn, $em->listeners('before')[0]); + $this->assertSame($fn, $em->listeners('complete')[2]); + $this->assertSame($fn, $em->listeners('error')[0]); + } + + public function testRegistersEventsWithPriorities() + { + $fn = function() {}; + $client = new Client(); + $requests = [$client->createRequest('GET', 'http://test.com')]; + $trans = new TransactionIterator( + new \ArrayIterator($requests), + $client, + [ + 'before' => [['fn' => $fn, 'priority' => 99]], + 'complete' => [['fn' => $fn, 'priority' => 99]], + 'error' => [['fn' => $fn, 'priority' => 99]] + ] + ); + + $t = iterator_to_array($trans)[0]; + $em = $t->getRequest()->getEmitter(); + $this->assertSame($fn, $em->listeners('before')[0]); + $this->assertSame($fn, $em->listeners('complete')[2]); + $this->assertSame($fn, $em->listeners('error')[0]); + } + + public function testRegistersMultipleEvents() + { + $fn = function() {}; + $c = new Client(); + $eventArray = [['fn' => $fn], ['fn' => $fn]]; + $requests = [$c->createRequest('GET', 'http://test.com')]; + $trans = new TransactionIterator(new \ArrayIterator($requests), $c, [ + 'before' => $eventArray, + 'complete' => $eventArray, + 'error' => $eventArray, + ]); + + $t = iterator_to_array($trans)[0]; + $em = $t->getRequest()->getEmitter(); + $this->assertSame($fn, $em->listeners('before')[0]); + $this->assertSame($fn, $em->listeners('before')[1]); + $this->assertSame($fn, $em->listeners('complete')[2]); + $this->assertSame($fn, $em->listeners('complete')[3]); + $this->assertSame($fn, $em->listeners('error')[0]); + $this->assertSame($fn, $em->listeners('error')[1]); + } + + public function testRegistersEventsWithOnce() + { + $called = 0; + $fn = function () use (&$called) { $called++; }; + $client = new Client(); + $requests = [$client->createRequest('GET', 'http://test.com')]; + // Remove an default listeners + foreach ($requests[0]->getEmitter()->listeners('before') as $l) { + $requests[0]->getEmitter()->removeListener('before', $l); + } + $trans = new TransactionIterator( + new \ArrayIterator($requests), + $client, + ['before' => [['fn' => $fn, 'once' => true]]] + ); + // Apply the listeners to the request + iterator_to_array($trans)[0]; + $ev = $this->getMockBuilder('GuzzleHttp\Event\BeforeEvent') + ->disableOriginalConstructor() + ->getMock(); + $requests[0]->getEmitter()->emit('before', $ev); + $requests[0]->getEmitter()->emit('before', $ev); + $this->assertEquals(1, $called); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testValidatesEvents() + { + $client = new Client(); + $requests = [$client->createRequest('GET', 'http://test.com')]; + new TransactionIterator(new \ArrayIterator($requests), $client, [ + 'before' => 'foo' + ]); + } +} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Adapter/TransactionTest.php b/core/vendor/guzzlehttp/guzzle/tests/Adapter/TransactionTest.php new file mode 100644 index 0000000000000000000000000000000000000000..9b7ee1f7976ebde3c63a663d6c3c9576c8d22a9c --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/tests/Adapter/TransactionTest.php @@ -0,0 +1,27 @@ +<?php + +namespace GuzzleHttp\Tests\Adapter; + +use GuzzleHttp\Adapter\Transaction; +use GuzzleHttp\Client; +use GuzzleHttp\Message\Request; +use GuzzleHttp\Message\Response; + +/** + * @covers GuzzleHttp\Adapter\Transaction + */ +class TransactionTest extends \PHPUnit_Framework_TestCase +{ + public function testHasRequestAndClient() + { + $c = new Client(); + $req = new Request('GET', '/'); + $response = new Response(200); + $t = new Transaction($c, $req); + $this->assertSame($c, $t->getClient()); + $this->assertSame($req, $t->getRequest()); + $this->assertNull($t->getResponse()); + $t->setResponse($response); + $this->assertSame($response, $t->getResponse()); + } +} diff --git a/core/vendor/guzzlehttp/guzzle/tests/ClientTest.php b/core/vendor/guzzlehttp/guzzle/tests/ClientTest.php new file mode 100644 index 0000000000000000000000000000000000000000..5f06c333c41e0e4b99f46e60b1ea91f439a58d21 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/tests/ClientTest.php @@ -0,0 +1,442 @@ +<?php + +namespace GuzzleHttp\Tests; + +use GuzzleHttp\Adapter\FakeParallelAdapter; +use GuzzleHttp\Adapter\MockAdapter; +use GuzzleHttp\Client; +use GuzzleHttp\Event\BeforeEvent; +use GuzzleHttp\Message\MessageFactory; +use GuzzleHttp\Message\Response; +use GuzzleHttp\Exception\RequestException; +use GuzzleHttp\Subscriber\History; +use GuzzleHttp\Subscriber\Mock; + +/** + * @covers GuzzleHttp\Client + */ +class ClientTest extends \PHPUnit_Framework_TestCase +{ + public function testProvidesDefaultUserAgent() + { + $this->assertEquals(1, preg_match('#^Guzzle/.+ curl/.+ PHP/.+$#', Client::getDefaultUserAgent())); + } + + public function testUsesDefaultDefaultOptions() + { + $client = new Client(); + $this->assertTrue($client->getDefaultOption('allow_redirects')); + $this->assertTrue($client->getDefaultOption('exceptions')); + $this->assertContains('cacert.pem', $client->getDefaultOption('verify')); + } + + public function testUsesProvidedDefaultOptions() + { + $client = new Client([ + 'defaults' => [ + 'allow_redirects' => false, + 'query' => ['foo' => 'bar'] + ] + ]); + $this->assertFalse($client->getDefaultOption('allow_redirects')); + $this->assertTrue($client->getDefaultOption('exceptions')); + $this->assertContains('cacert.pem', $client->getDefaultOption('verify')); + $this->assertEquals(['foo' => 'bar'], $client->getDefaultOption('query')); + } + + public function testCanSpecifyBaseUrl() + { + $this->assertSame('', (new Client())->getBaseUrl()); + $this->assertEquals('http://foo', (new Client([ + 'base_url' => 'http://foo' + ]))->getBaseUrl()); + } + + public function testCanSpecifyBaseUrlUriTemplate() + { + $client = new Client(['base_url' => ['http://foo.com/{var}/', ['var' => 'baz']]]); + $this->assertEquals('http://foo.com/baz/', $client->getBaseUrl()); + } + + public function testClientUsesDefaultAdapterWhenNoneIsSet() + { + $client = new Client(); + if (!extension_loaded('curl')) { + $adapter = 'GuzzleHttp\Adapter\StreamAdapter'; + } elseif (ini_get('allow_url_fopen')) { + $adapter = 'GuzzleHttp\Adapter\StreamingProxyAdapter'; + } else { + $adapter = 'GuzzleHttp\Adapter\Curl\CurlAdapter'; + } + $this->assertInstanceOf($adapter, $this->readAttribute($client, 'adapter')); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage Foo + */ + public function testCanSpecifyAdapter() + { + $adapter = $this->getMockBuilder('GuzzleHttp\Adapter\AdapterInterface') + ->setMethods(['send']) + ->getMockForAbstractClass(); + $adapter->expects($this->once()) + ->method('send') + ->will($this->throwException(new \Exception('Foo'))); + $client = new Client(['adapter' => $adapter]); + $client->get('http://httpbin.org'); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage Foo + */ + public function testCanSpecifyMessageFactory() + { + $factory = $this->getMockBuilder('GuzzleHttp\Message\MessageFactoryInterface') + ->setMethods(['createRequest']) + ->getMockForAbstractClass(); + $factory->expects($this->once()) + ->method('createRequest') + ->will($this->throwException(new \Exception('Foo'))); + $client = new Client(['message_factory' => $factory]); + $client->get(); + } + + public function testAddsDefaultUserAgentHeaderWithDefaultOptions() + { + $client = new Client(['defaults' => ['allow_redirects' => false]]); + $this->assertFalse($client->getDefaultOption('allow_redirects')); + $this->assertEquals( + ['User-Agent' => Client::getDefaultUserAgent()], + $client->getDefaultOption('headers') + ); + } + + public function testAddsDefaultUserAgentHeaderWithoutDefaultOptions() + { + $client = new Client(); + $this->assertEquals( + ['User-Agent' => Client::getDefaultUserAgent()], + $client->getDefaultOption('headers') + ); + } + + private function getRequestClient() + { + $client = $this->getMockBuilder('GuzzleHttp\Client') + ->setMethods(['send']) + ->getMock(); + $client->expects($this->once()) + ->method('send') + ->will($this->returnArgument(0)); + + return $client; + } + + public function requestMethodProvider() + { + return [ + ['GET', false], + ['HEAD', false], + ['DELETE', false], + ['OPTIONS', false], + ['POST', 'foo'], + ['PUT', 'foo'], + ['PATCH', 'foo'] + ]; + } + + /** + * @dataProvider requestMethodProvider + */ + public function testClientProvidesMethodShortcut($method, $body) + { + $client = $this->getRequestClient(); + if ($body) { + $request = $client->{$method}('http://foo.com', [ + 'headers' => ['X-Baz' => 'Bar'], + 'body' => $body, + 'query' => ['a' => 'b'] + ]); + } else { + $request = $client->{$method}('http://foo.com', [ + 'headers' => ['X-Baz' => 'Bar'], + 'query' => ['a' => 'b'] + ]); + } + $this->assertEquals($method, $request->getMethod()); + $this->assertEquals('Bar', $request->getHeader('X-Baz')); + $this->assertEquals('a=b', $request->getQuery()); + if ($body) { + $this->assertEquals($body, $request->getBody()); + } + } + + public function testClientMergesDefaultOptionsWithRequestOptions() + { + $f = $this->getMockBuilder('GuzzleHttp\Message\MessageFactoryInterface') + ->setMethods(array('createRequest')) + ->getMockForAbstractClass(); + + $o = null; + // Intercept the creation + $f->expects($this->once()) + ->method('createRequest') + ->will($this->returnCallback( + function ($method, $url, array $options = []) use (&$o) { + $o = $options; + return (new MessageFactory())->createRequest($method, $url, $options); + } + )); + + $client = new Client([ + 'message_factory' => $f, + 'defaults' => [ + 'headers' => ['Foo' => 'Bar'], + 'query' => ['baz' => 'bam'], + 'exceptions' => false + ] + ]); + + $request = $client->createRequest('GET', 'http://foo.com?a=b', [ + 'headers' => ['Hi' => 'there', '1' => 'one'], + 'allow_redirects' => false, + 'query' => ['t' => 1] + ]); + + $this->assertFalse($o['allow_redirects']); + $this->assertFalse($o['exceptions']); + $this->assertEquals('Bar', $request->getHeader('Foo')); + $this->assertEquals('there', $request->getHeader('Hi')); + $this->assertEquals('one', $request->getHeader('1')); + $this->assertEquals('a=b&baz=bam&t=1', $request->getQuery()); + } + + public function testUsesBaseUrlWhenNoUrlIsSet() + { + $client = new Client(['base_url' => 'http://www.foo.com/baz?bam=bar']); + $this->assertEquals( + 'http://www.foo.com/baz?bam=bar', + $client->createRequest('GET')->getUrl() + ); + } + + public function testUsesBaseUrlCombinedWithProvidedUrl() + { + $client = new Client(['base_url' => 'http://www.foo.com/baz?bam=bar']); + $this->assertEquals( + 'http://www.foo.com/bar/bam', + $client->createRequest('GET', 'bar/bam')->getUrl() + ); + } + + public function testUsesBaseUrlCombinedWithProvidedUrlViaUriTemplate() + { + $client = new Client(['base_url' => 'http://www.foo.com/baz?bam=bar']); + $this->assertEquals( + 'http://www.foo.com/bar/123', + $client->createRequest('GET', ['bar/{bam}', ['bam' => '123']])->getUrl() + ); + } + + public function testSettingAbsoluteUrlOverridesBaseUrl() + { + $client = new Client(['base_url' => 'http://www.foo.com/baz?bam=bar']); + $this->assertEquals( + 'http://www.foo.com/foo', + $client->createRequest('GET', '/foo')->getUrl() + ); + } + + public function testSettingAbsoluteUriTemplateOverridesBaseUrl() + { + $client = new Client(['base_url' => 'http://www.foo.com/baz?bam=bar']); + $this->assertEquals( + 'http://goo.com/1', + $client->createRequest( + 'GET', + ['http://goo.com/{bar}', ['bar' => '1']] + )->getUrl() + ); + } + + public function testCanSetRelativeUrlStartingWithHttp() + { + $client = new Client(['base_url' => 'http://www.foo.com']); + $this->assertEquals( + 'http://www.foo.com/httpfoo', + $client->createRequest('GET', 'httpfoo')->getUrl() + ); + } + + public function testClientSendsRequests() + { + $response = new Response(200); + $adapter = new MockAdapter(); + $adapter->setResponse($response); + $client = new Client(['adapter' => $adapter]); + $this->assertSame($response, $client->get('http://test.com')); + $this->assertEquals('http://test.com', $response->getEffectiveUrl()); + } + + public function testSendingRequestCanBeIntercepted() + { + $response = new Response(200); + $response2 = new Response(200); + $adapter = new MockAdapter(); + $adapter->setResponse($response); + $client = new Client(['adapter' => $adapter]); + $client->getEmitter()->on( + 'before', + function (BeforeEvent $e) use ($response2) { + $e->intercept($response2); + } + ); + $this->assertSame($response2, $client->get('http://test.com')); + $this->assertEquals('http://test.com', $response2->getEffectiveUrl()); + } + + /** + * @expectedException \GuzzleHttp\Exception\RequestException + * @expectedExceptionMessage No response + */ + public function testEnsuresResponseIsPresentAfterSending() + { + $adapter = $this->getMockBuilder('GuzzleHttp\Adapter\MockAdapter') + ->setMethods(['send']) + ->getMock(); + $adapter->expects($this->once()) + ->method('send'); + $client = new Client(['adapter' => $adapter]); + $client->get('http://httpbin.org'); + } + + public function testClientHandlesErrorsDuringBeforeSend() + { + $client = new Client(); + $client->getEmitter()->on('before', function ($e) { + throw new \Exception('foo'); + }); + $client->getEmitter()->on('error', function ($e) { + $e->intercept(new Response(200)); + }); + $this->assertEquals(200, $client->get('http://test.com')->getStatusCode()); + } + + /** + * @expectedException \GuzzleHttp\Exception\RequestException + * @expectedExceptionMessage foo + */ + public function testClientHandlesErrorsDuringBeforeSendAndThrowsIfUnhandled() + { + $client = new Client(); + $client->getEmitter()->on('before', function ($e) { + throw new RequestException('foo', $e->getRequest()); + }); + $client->get('http://httpbin.org'); + } + + /** + * @expectedException \GuzzleHttp\Exception\RequestException + * @expectedExceptionMessage foo + */ + public function testClientWrapsExceptions() + { + $client = new Client(); + $client->getEmitter()->on('before', function ($e) { + throw new \Exception('foo'); + }); + $client->get('http://httpbin.org'); + } + + public function testCanSetDefaultValues() + { + $client = new Client(['foo' => 'bar']); + $client->setDefaultOption('headers/foo', 'bar'); + $this->assertNull($client->getDefaultOption('foo')); + $this->assertEquals('bar', $client->getDefaultOption('headers/foo')); + } + + public function testSendsAllInParallel() + { + $client = new Client(); + $client->getEmitter()->attach(new Mock([ + new Response(200), + new Response(201), + new Response(202), + ])); + $history = new History(); + $client->getEmitter()->attach($history); + + $requests = [ + $client->createRequest('GET', 'http://test.com'), + $client->createRequest('POST', 'http://test.com'), + $client->createRequest('PUT', 'http://test.com') + ]; + + $client->sendAll($requests); + $requests = array_map(function($r) { return $r->getMethod(); }, $history->getRequests()); + $this->assertContains('GET', $requests); + $this->assertContains('POST', $requests); + $this->assertContains('PUT', $requests); + } + + public function testCanSetCustomParallelAdapter() + { + $called = false; + $pa = new FakeParallelAdapter(new MockAdapter(function () use (&$called) { + $called = true; + return new Response(203); + })); + $client = new Client(['parallel_adapter' => $pa]); + $client->sendAll([$client->createRequest('GET', 'http://www.foo.com')]); + $this->assertTrue($called); + } + + public function testCanDisableAuthPerRequest() + { + $client = new Client(['defaults' => ['auth' => 'foo']]); + $request = $client->createRequest('GET', 'http://test.com'); + $this->assertEquals('foo', $request->getConfig()['auth']); + $request = $client->createRequest('GET', 'http://test.com', ['auth' => null]); + $this->assertFalse($request->getConfig()->hasKey('auth')); + } + + /** + * @expectedException \PHPUnit_Framework_Error_Deprecated + */ + public function testHasDeprecatedGetEmitter() + { + $client = new Client(); + $client->getEventDispatcher(); + } + + public function testUsesProxyEnvironmentVariables() + { + $http = isset($_SERVER['HTTP_PROXY']) ? $_SERVER['HTTP_PROXY'] : null; + $https = isset($_SERVER['HTTPS_PROXY']) ? $_SERVER['HTTPS_PROXY'] : null; + unset($_SERVER['HTTP_PROXY']); + unset($_SERVER['HTTPS_PROXY']); + + $client = new Client(); + $this->assertNull($client->getDefaultOption('proxy')); + + $_SERVER['HTTP_PROXY'] = '127.0.0.1'; + $client = new Client(); + $this->assertEquals( + ['http' => '127.0.0.1'], + $client->getDefaultOption('proxy') + ); + + $_SERVER['HTTPS_PROXY'] = '127.0.0.2'; + $client = new Client(); + $this->assertEquals( + ['http' => '127.0.0.1', 'https' => '127.0.0.2'], + $client->getDefaultOption('proxy') + ); + + $_SERVER['HTTP_PROXY'] = $http; + $_SERVER['HTTPS_PROXY'] = $https; + } +} diff --git a/core/vendor/guzzlehttp/guzzle/tests/CollectionTest.php b/core/vendor/guzzlehttp/guzzle/tests/CollectionTest.php new file mode 100644 index 0000000000000000000000000000000000000000..9e6155f7b39eaaf714e099f8851d8980842bfc13 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/tests/CollectionTest.php @@ -0,0 +1,419 @@ +<?php + +namespace GuzzleHttp\Tests; + +use GuzzleHttp\Collection; + +class CollectionTest extends \PHPUnit_Framework_TestCase +{ + /** @var Collection */ + protected $coll; + + protected function setUp() + { + $this->coll = new Collection(); + } + + public function testConstructorCanBeCalledWithNoParams() + { + $this->coll = new Collection(); + $p = $this->coll->toArray(); + $this->assertEmpty($p, '-> Collection must be empty when no data is passed'); + } + + public function testConstructorCanBeCalledWithParams() + { + $testData = array( + 'test' => 'value', + 'test_2' => 'value2' + ); + $this->coll = new Collection($testData); + $this->assertEquals($this->coll->toArray(), $testData); + $this->assertEquals($this->coll->toArray(), $this->coll->toArray()); + } + + public function testImplementsIteratorAggregate() + { + $this->coll->set('key', 'value'); + $this->assertInstanceOf('ArrayIterator', $this->coll->getIterator()); + $this->assertEquals(1, count($this->coll)); + $total = 0; + foreach ($this->coll as $key => $value) { + $this->assertEquals('key', $key); + $this->assertEquals('value', $value); + $total++; + } + $this->assertEquals(1, $total); + } + + public function testCanAddValuesToExistingKeysByUsingArray() + { + $this->coll->add('test', 'value1'); + $this->assertEquals($this->coll->toArray(), array('test' => 'value1')); + $this->coll->add('test', 'value2'); + $this->assertEquals($this->coll->toArray(), array('test' => array('value1', 'value2'))); + $this->coll->add('test', 'value3'); + $this->assertEquals($this->coll->toArray(), array('test' => array('value1', 'value2', 'value3'))); + } + + public function testHandlesMergingInDisparateDataSources() + { + $params = array( + 'test' => 'value1', + 'test2' => 'value2', + 'test3' => array('value3', 'value4') + ); + $this->coll->merge($params); + $this->assertEquals($this->coll->toArray(), $params); + + // Pass the same object to itself + $this->assertEquals($this->coll->merge($this->coll), $this->coll); + } + + public function testCanClearAllDataOrSpecificKeys() + { + $this->coll->merge(array( + 'test' => 'value1', + 'test2' => 'value2' + )); + + // Clear a specific parameter by name + $this->coll->remove('test'); + + $this->assertEquals($this->coll->toArray(), array( + 'test2' => 'value2' + )); + + // Clear all parameters + $this->coll->clear(); + + $this->assertEquals($this->coll->toArray(), array()); + } + + public function testProvidesKeys() + { + $this->assertEquals(array(), $this->coll->getKeys()); + $this->coll->merge(array( + 'test1' => 'value1', + 'test2' => 'value2' + )); + $this->assertEquals(array('test1', 'test2'), $this->coll->getKeys()); + // Returns the cached array previously returned + $this->assertEquals(array('test1', 'test2'), $this->coll->getKeys()); + $this->coll->remove('test1'); + $this->assertEquals(array('test2'), $this->coll->getKeys()); + $this->coll->add('test3', 'value3'); + $this->assertEquals(array('test2', 'test3'), $this->coll->getKeys()); + } + + public function testChecksIfHasKey() + { + $this->assertFalse($this->coll->hasKey('test')); + $this->coll->add('test', 'value'); + $this->assertEquals(true, $this->coll->hasKey('test')); + $this->coll->add('test2', 'value2'); + $this->assertEquals(true, $this->coll->hasKey('test')); + $this->assertEquals(true, $this->coll->hasKey('test2')); + $this->assertFalse($this->coll->hasKey('testing')); + $this->assertEquals(false, $this->coll->hasKey('AB-C', 'junk')); + } + + public function testChecksIfHasValue() + { + $this->assertFalse($this->coll->hasValue('value')); + $this->coll->add('test', 'value'); + $this->assertEquals('test', $this->coll->hasValue('value')); + $this->coll->add('test2', 'value2'); + $this->assertEquals('test', $this->coll->hasValue('value')); + $this->assertEquals('test2', $this->coll->hasValue('value2')); + $this->assertFalse($this->coll->hasValue('val')); + } + + public function testImplementsCount() + { + $data = new Collection(); + $this->assertEquals(0, $data->count()); + $data->add('key', 'value'); + $this->assertEquals(1, count($data)); + $data->add('key', 'value2'); + $this->assertEquals(1, count($data)); + $data->add('key_2', 'value3'); + $this->assertEquals(2, count($data)); + } + + public function testAddParamsByMerging() + { + $params = array( + 'test' => 'value1', + 'test2' => 'value2', + 'test3' => array('value3', 'value4') + ); + + // Add some parameters + $this->coll->merge($params); + + // Add more parameters by merging them in + $this->coll->merge(array( + 'test' => 'another', + 'different_key' => 'new value' + )); + + $this->assertEquals(array( + 'test' => array('value1', 'another'), + 'test2' => 'value2', + 'test3' => array('value3', 'value4'), + 'different_key' => 'new value' + ), $this->coll->toArray()); + } + + public function testAllowsFunctionalFilter() + { + $this->coll->merge(array( + 'fruit' => 'apple', + 'number' => 'ten', + 'prepositions' => array('about', 'above', 'across', 'after'), + 'same_number' => 'ten' + )); + + $filtered = $this->coll->filter(function($key, $value) { + return $value == 'ten'; + }); + + $this->assertNotSame($filtered, $this->coll); + + $this->assertEquals(array( + 'number' => 'ten', + 'same_number' => 'ten' + ), $filtered->toArray()); + } + + public function testAllowsFunctionalMapping() + { + $this->coll->merge(array( + 'number_1' => 1, + 'number_2' => 2, + 'number_3' => 3 + )); + + $mapped = $this->coll->map(function($key, $value) { + return $value * $value; + }); + + $this->assertNotSame($mapped, $this->coll); + + $this->assertEquals(array( + 'number_1' => 1, + 'number_2' => 4, + 'number_3' => 9 + ), $mapped->toArray()); + } + + public function testImplementsArrayAccess() + { + $this->coll->merge(array( + 'k1' => 'v1', + 'k2' => 'v2' + )); + + $this->assertTrue($this->coll->offsetExists('k1')); + $this->assertFalse($this->coll->offsetExists('Krull')); + + $this->coll->offsetSet('k3', 'v3'); + $this->assertEquals('v3', $this->coll->offsetGet('k3')); + $this->assertEquals('v3', $this->coll->get('k3')); + + $this->coll->offsetUnset('k1'); + $this->assertFalse($this->coll->offsetExists('k1')); + } + + public function testCanReplaceAllData() + { + $this->assertSame($this->coll, $this->coll->replace(array( + 'a' => '123' + ))); + + $this->assertEquals(array( + 'a' => '123' + ), $this->coll->toArray()); + } + + public function testPreparesFromConfig() + { + $c = Collection::fromConfig(array( + 'a' => '123', + 'base_url' => 'http://www.test.com/' + ), array( + 'a' => 'xyz', + 'b' => 'lol' + ), array('a')); + + $this->assertInstanceOf('GuzzleHttp\Collection', $c); + $this->assertEquals(array( + 'a' => '123', + 'b' => 'lol', + 'base_url' => 'http://www.test.com/' + ), $c->toArray()); + + try { + $c = Collection::fromConfig(array(), array(), array('a')); + $this->fail('Exception not throw when missing config'); + } catch (\InvalidArgumentException $e) { + } + } + + function falseyDataProvider() + { + return array( + array(false, false), + array(null, null), + array('', ''), + array(array(), array()), + array(0, 0), + ); + } + + /** + * @dataProvider falseyDataProvider + */ + public function testReturnsCorrectData($a, $b) + { + $c = new Collection(array('value' => $a)); + $this->assertSame($b, $c->get('value')); + } + + public function testRetrievesNestedKeysUsingPath() + { + $data = array( + 'foo' => 'bar', + 'baz' => array( + 'mesa' => array( + 'jar' => 'jar' + ) + ) + ); + $collection = new Collection($data); + $this->assertEquals('bar', $collection->getPath('foo')); + $this->assertEquals('jar', $collection->getPath('baz/mesa/jar')); + $this->assertNull($collection->getPath('wewewf')); + $this->assertNull($collection->getPath('baz/mesa/jar/jar')); + } + + public function testFalseyKeysStillDescend() + { + $collection = new Collection(array( + '0' => array( + 'a' => 'jar' + ), + 1 => 'other' + )); + $this->assertEquals('jar', $collection->getPath('0/a')); + $this->assertEquals('other', $collection->getPath('1')); + } + + public function getPathProvider() + { + $data = array( + 'foo' => 'bar', + 'baz' => array( + 'mesa' => array( + 'jar' => 'jar', + 'array' => array('a', 'b', 'c') + ), + 'bar' => array( + 'baz' => 'bam', + 'array' => array('d', 'e', 'f') + ) + ), + 'bam' => array( + array('foo' => 1), + array('foo' => 2), + array('array' => array('h', 'i')) + ) + ); + $c = new Collection($data); + + return array( + // Simple path selectors + array($c, 'foo', 'bar'), + array($c, 'baz', $data['baz']), + array($c, 'bam', $data['bam']), + array($c, 'baz/mesa', $data['baz']['mesa']), + array($c, 'baz/mesa/jar', 'jar'), + // Does not barf on missing keys + array($c, 'fefwfw', null), + array($c, 'baz/mesa/array', $data['baz']['mesa']['array']) + ); + } + + /** + * @dataProvider getPathProvider + */ + public function testGetPath(Collection $c, $path, $expected, $separator = '/') + { + $this->assertEquals($expected, $c->getPath($path, $separator)); + } + + public function testOverridesSettings() + { + $c = new Collection(array('foo' => 1, 'baz' => 2, 'bar' => 3)); + $c->overwriteWith(array('foo' => 10, 'bar' => 300)); + $this->assertEquals(array('foo' => 10, 'baz' => 2, 'bar' => 300), $c->toArray()); + } + + public function testOverwriteWithCollection() + { + $c = new Collection(array('foo' => 1, 'baz' => 2, 'bar' => 3)); + $b = new Collection(array('foo' => 10, 'bar' => 300)); + $c->overwriteWith($b); + $this->assertEquals(array('foo' => 10, 'baz' => 2, 'bar' => 300), $c->toArray()); + } + + public function testOverwriteWithTraversable() + { + $c = new Collection(array('foo' => 1, 'baz' => 2, 'bar' => 3)); + $b = new Collection(array('foo' => 10, 'bar' => 300)); + $c->overwriteWith($b->getIterator()); + $this->assertEquals(array('foo' => 10, 'baz' => 2, 'bar' => 300), $c->toArray()); + } + + public function testCanSetNestedPathValueThatDoesNotExist() + { + $c = new Collection(array()); + $c->setPath('foo/bar/baz/123', 'hi'); + $this->assertEquals('hi', $c['foo']['bar']['baz']['123']); + } + + public function testCanSetNestedPathValueThatExists() + { + $c = new Collection(array('foo' => array('bar' => 'test'))); + $c->setPath('foo/bar', 'hi'); + $this->assertEquals('hi', $c['foo']['bar']); + } + + /** + * @expectedException \RuntimeException + */ + public function testVerifiesNestedPathIsValidAtExactLevel() + { + $c = new Collection(array('foo' => 'bar')); + $c->setPath('foo/bar', 'hi'); + $this->assertEquals('hi', $c['foo']['bar']); + } + + /** + * @expectedException \RuntimeException + */ + public function testVerifiesThatNestedPathIsValidAtAnyLevel() + { + $c = new Collection(array('foo' => 'bar')); + $c->setPath('foo/bar/baz', 'test'); + } + + public function testCanAppendToNestedPathValues() + { + $c = new Collection(); + $c->setPath('foo/bar/[]', 'a'); + $c->setPath('foo/bar/[]', 'b'); + $this->assertEquals(['a', 'b'], $c['foo']['bar']); + } +} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Cookie/CookieJarTest.php b/core/vendor/guzzlehttp/guzzle/tests/Cookie/CookieJarTest.php new file mode 100644 index 0000000000000000000000000000000000000000..795194636bb7354024976c081920c999c6046084 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/tests/Cookie/CookieJarTest.php @@ -0,0 +1,325 @@ +<?php + +namespace GuzzleHttp\Tests\CookieJar; + +use GuzzleHttp\Cookie\CookieJar; +use GuzzleHttp\Cookie\SetCookie; +use GuzzleHttp\Message\Response; +use GuzzleHttp\Message\Request; + +/** + * @covers GuzzleHttp\Cookie\CookieJar + */ +class CookieJarTest extends \PHPUnit_Framework_TestCase +{ + /** @var CookieJar */ + private $jar; + + public function setUp() + { + $this->jar = new CookieJar(); + } + + protected function getTestCookies() + { + return [ + new SetCookie(['Name' => 'foo', 'Value' => 'bar', 'Domain' => 'foo.com', 'Path' => '/', 'Discard' => true]), + new SetCookie(['Name' => 'test', 'Value' => '123', 'Domain' => 'baz.com', 'Path' => '/foo', 'Expires' => 2]), + new SetCookie(['Name' => 'you', 'Value' => '123', 'Domain' => 'bar.com', 'Path' => '/boo', 'Expires' => time() + 1000]) + ]; + } + + public function testQuotesBadCookieValues() + { + $this->assertEquals('foo', CookieJar::getCookieValue('foo')); + $this->assertEquals('"foo,bar"', CookieJar::getCookieValue('foo,bar')); + } + + public function testCreatesFromArray() + { + $jar = CookieJar::fromArray([ + 'foo' => 'bar', + 'baz' => 'bam' + ], 'example.com'); + $this->assertCount(2, $jar); + } + + /** + * Provides test data for cookie cookieJar retrieval + */ + public function getCookiesDataProvider() + { + return [ + [['foo', 'baz', 'test', 'muppet', 'googoo'], '', '', '', false], + [['foo', 'baz', 'muppet', 'googoo'], '', '', '', true], + [['googoo'], 'www.example.com', '', '', false], + [['muppet', 'googoo'], 'test.y.example.com', '', '', false], + [['foo', 'baz'], 'example.com', '', '', false], + [['muppet'], 'x.y.example.com', '/acme/', '', false], + [['muppet'], 'x.y.example.com', '/acme/test/', '', false], + [['googoo'], 'x.y.example.com', '/test/acme/test/', '', false], + [['foo', 'baz'], 'example.com', '', '', false], + [['baz'], 'example.com', '', 'baz', false], + ]; + } + + public function testStoresAndRetrievesCookies() + { + $cookies = $this->getTestCookies(); + foreach ($cookies as $cookie) { + $this->assertTrue($this->jar->setCookie($cookie)); + } + + $this->assertEquals(3, count($this->jar)); + $this->assertEquals(3, count($this->jar->getIterator())); + $this->assertEquals($cookies, $this->jar->getIterator()->getArrayCopy()); + } + + public function testRemovesTemporaryCookies() + { + $cookies = $this->getTestCookies(); + foreach ($this->getTestCookies() as $cookie) { + $this->jar->setCookie($cookie); + } + $this->jar->clearSessionCookies(); + $this->assertEquals( + [$cookies[1], $cookies[2]], + $this->jar->getIterator()->getArrayCopy() + ); + } + + public function testRemovesSelectively() + { + foreach ($this->getTestCookies() as $cookie) { + $this->jar->setCookie($cookie); + } + + // Remove foo.com cookies + $this->jar->clear('foo.com'); + $this->assertEquals(2, count($this->jar)); + // Try again, removing no further cookies + $this->jar->clear('foo.com'); + $this->assertEquals(2, count($this->jar)); + + // Remove bar.com cookies with path of /boo + $this->jar->clear('bar.com', '/boo'); + $this->assertEquals(1, count($this->jar)); + + // Remove cookie by name + $this->jar->clear(null, null, 'test'); + $this->assertEquals(0, count($this->jar)); + } + + public function testDoesNotAddIncompleteCookies() + { + $this->assertEquals(false, $this->jar->setCookie(new SetCookie())); + $this->assertFalse($this->jar->setCookie(new SetCookie(array( + 'Name' => 'foo' + )))); + $this->assertFalse($this->jar->setCookie(new SetCookie(array( + 'Name' => false + )))); + $this->assertFalse($this->jar->setCookie(new SetCookie(array( + 'Name' => true + )))); + $this->assertFalse($this->jar->setCookie(new SetCookie(array( + 'Name' => 'foo', + 'Domain' => 'foo.com' + )))); + } + + public function testDoesAddValidCookies() + { + $this->assertTrue($this->jar->setCookie(new SetCookie(array( + 'Name' => 'foo', + 'Domain' => 'foo.com', + 'Value' => 0 + )))); + $this->assertTrue($this->jar->setCookie(new SetCookie(array( + 'Name' => 'foo', + 'Domain' => 'foo.com', + 'Value' => 0.0 + )))); + $this->assertTrue($this->jar->setCookie(new SetCookie(array( + 'Name' => 'foo', + 'Domain' => 'foo.com', + 'Value' => '0' + )))); + } + + public function testOverwritesCookiesThatAreOlderOrDiscardable() + { + $t = time() + 1000; + $data = array( + 'Name' => 'foo', + 'Value' => 'bar', + 'Domain' => '.example.com', + 'Path' => '/', + 'Max-Age' => '86400', + 'Secure' => true, + 'Discard' => true, + 'Expires' => $t + ); + + // Make sure that the discard cookie is overridden with the non-discard + $this->assertTrue($this->jar->setCookie(new SetCookie($data))); + $this->assertEquals(1, count($this->jar)); + + $data['Discard'] = false; + $this->assertTrue($this->jar->setCookie(new SetCookie($data))); + $this->assertEquals(1, count($this->jar)); + + $c = $this->jar->getIterator()->getArrayCopy(); + $this->assertEquals(false, $c[0]->getDiscard()); + + // Make sure it doesn't duplicate the cookie + $this->jar->setCookie(new SetCookie($data)); + $this->assertEquals(1, count($this->jar)); + + // Make sure the more future-ful expiration date supersede the other + $data['Expires'] = time() + 2000; + $this->assertTrue($this->jar->setCookie(new SetCookie($data))); + $this->assertEquals(1, count($this->jar)); + $c = $this->jar->getIterator()->getArrayCopy(); + $this->assertNotEquals($t, $c[0]->getExpires()); + } + + public function testOverwritesCookiesThatHaveChanged() + { + $t = time() + 1000; + $data = array( + 'Name' => 'foo', + 'Value' => 'bar', + 'Domain' => '.example.com', + 'Path' => '/', + 'Max-Age' => '86400', + 'Secure' => true, + 'Discard' => true, + 'Expires' => $t + ); + + // Make sure that the discard cookie is overridden with the non-discard + $this->assertTrue($this->jar->setCookie(new SetCookie($data))); + + $data['Value'] = 'boo'; + $this->assertTrue($this->jar->setCookie(new SetCookie($data))); + $this->assertEquals(1, count($this->jar)); + + // Changing the value plus a parameter also must overwrite the existing one + $data['Value'] = 'zoo'; + $data['Secure'] = false; + $this->assertTrue($this->jar->setCookie(new SetCookie($data))); + $this->assertEquals(1, count($this->jar)); + + $c = $this->jar->getIterator()->getArrayCopy(); + $this->assertEquals('zoo', $c[0]->getValue()); + } + + public function testAddsCookiesFromResponseWithRequest() + { + $response = new Response(200, array( + 'Set-Cookie' => "fpc=d=.Hm.yh4.1XmJWjJfs4orLQzKzPImxklQoxXSHOZATHUSEFciRueW_7704iYUtsXNEXq0M92Px2glMdWypmJ7HIQl6XIUvrZimWjQ3vIdeuRbI.FNQMAfcxu_XN1zSx7l.AcPdKL6guHc2V7hIQFhnjRW0rxm2oHY1P4bGQxFNz7f.tHm12ZD3DbdMDiDy7TBXsuP4DM-&v=2; expires=Fri, 02-Mar-2019 02:17:40 GMT;" + )); + $request = new Request('GET', 'http://www.example.com'); + $this->jar->extractCookies($request, $response); + $this->assertEquals(1, count($this->jar)); + } + + public function getMatchingCookiesDataProvider() + { + return array( + array('https://example.com', 'foo=bar;baz=foobar'), + array('http://example.com', ''), + array('https://example.com:8912', 'foo=bar;baz=foobar'), + array('https://foo.example.com', 'foo=bar;baz=foobar'), + array('http://foo.example.com/test/acme/', 'googoo=gaga') + ); + } + + /** + * @dataProvider getMatchingCookiesDataProvider + */ + public function testReturnsCookiesMatchingRequests($url, $cookies) + { + $bag = [ + new SetCookie([ + 'Name' => 'foo', + 'Value' => 'bar', + 'Domain' => 'example.com', + 'Path' => '/', + 'Max-Age' => '86400', + 'Secure' => true + ]), + new SetCookie([ + 'Name' => 'baz', + 'Value' => 'foobar', + 'Domain' => 'example.com', + 'Path' => '/', + 'Max-Age' => '86400', + 'Secure' => true + ]), + new SetCookie([ + 'Name' => 'test', + 'Value' => '123', + 'Domain' => 'www.foobar.com', + 'Path' => '/path/', + 'Discard' => true + ]), + new SetCookie([ + 'Name' => 'muppet', + 'Value' => 'cookie_monster', + 'Domain' => '.y.example.com', + 'Path' => '/acme/', + 'Expires' => time() + 86400 + ]), + new SetCookie([ + 'Name' => 'googoo', + 'Value' => 'gaga', + 'Domain' => '.example.com', + 'Path' => '/test/acme/', + 'Max-Age' => 1500 + ]) + ]; + + foreach ($bag as $cookie) { + $this->jar->setCookie($cookie); + } + + $request = new Request('GET', $url); + $this->jar->addCookieHeader($request); + $this->assertEquals($cookies, $request->getHeader('Cookie')); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage Invalid cookie: Cookie name must not cannot invalid characters: + */ + public function testThrowsExceptionWithStrictMode() + { + $a = new CookieJar(true); + $a->setCookie(new SetCookie(['Name' => "abc\n", 'Value' => 'foo', 'Domain' => 'bar'])); + } + + public function testDeletesCookiesByName() + { + $cookies = $this->getTestCookies(); + $cookies[] = new SetCookie([ + 'Name' => 'other', + 'Value' => '123', + 'Domain' => 'bar.com', + 'Path' => '/boo', + 'Expires' => time() + 1000 + ]); + $jar = new CookieJar(); + foreach ($cookies as $cookie) { + $jar->setCookie($cookie); + } + $this->assertCount(4, $jar); + $jar->clear('bar.com', '/boo', 'other'); + $this->assertCount(3, $jar); + $names = array_map(function (SetCookie $c) { + return $c->getName(); + }, $jar->getIterator()->getArrayCopy()); + $this->assertEquals(['foo', 'test', 'you'], $names); + } +} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Cookie/FileCookieJarTest.php b/core/vendor/guzzlehttp/guzzle/tests/Cookie/FileCookieJarTest.php new file mode 100644 index 0000000000000000000000000000000000000000..40431c40b5a378b7c1773c73364c876c2b4d8c90 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/tests/Cookie/FileCookieJarTest.php @@ -0,0 +1,72 @@ +<?php + +namespace GuzzleHttp\Tests\CookieJar; + +use GuzzleHttp\Cookie\FileCookieJar; +use GuzzleHttp\Cookie\SetCookie; + +/** + * @covers GuzzleHttp\Cookie\FileCookieJar + */ +class FileCookieJarTest extends \PHPUnit_Framework_TestCase +{ + private $file; + + public function setUp() + { + $this->file = tempnam('/tmp', 'file-cookies'); + } + + /** + * @expectedException \RuntimeException + */ + public function testValidatesCookieFile() + { + file_put_contents($this->file, 'true'); + new FileCookieJar($this->file); + } + + public function testLoadsFromFileFile() + { + $jar = new FileCookieJar($this->file); + $this->assertEquals([], $jar->getIterator()->getArrayCopy()); + unlink($this->file); + } + + public function testPersistsToFileFile() + { + $jar = new FileCookieJar($this->file); + $jar->setCookie(new SetCookie([ + 'Name' => 'foo', + 'Value' => 'bar', + 'Domain' => 'foo.com', + 'Expires' => time() + 1000 + ])); + $jar->setCookie(new SetCookie([ + 'Name' => 'baz', + 'Value' => 'bar', + 'Domain' => 'foo.com', + 'Expires' => time() + 1000 + ])); + $jar->setCookie(new SetCookie([ + 'Name' => 'boo', + 'Value' => 'bar', + 'Domain' => 'foo.com', + ])); + + $this->assertEquals(3, count($jar)); + unset($jar); + + // Make sure it wrote to the file + $contents = file_get_contents($this->file); + $this->assertNotEmpty($contents); + + // Load the cookieJar from the file + $jar = new FileCookieJar($this->file); + + // Weeds out temporary and session cookies + $this->assertEquals(2, count($jar)); + unset($jar); + unlink($this->file); + } +} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Cookie/SetCookieTest.php b/core/vendor/guzzlehttp/guzzle/tests/Cookie/SetCookieTest.php new file mode 100644 index 0000000000000000000000000000000000000000..20cb23b74225027b4d09b9a5b5892933454b2eb5 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/tests/Cookie/SetCookieTest.php @@ -0,0 +1,364 @@ +<?php + +namespace GuzzleHttp\Tests\CookieJar; + +use GuzzleHttp\Cookie\SetCookie; + +/** + * @covers GuzzleHttp\Cookie\SetCookie + */ +class SetCookieTest extends \PHPUnit_Framework_TestCase +{ + public function testInitializesDefaultValues() + { + $cookie = new SetCookie(); + $this->assertEquals('/', $cookie->getPath()); + } + + public function testConvertsDateTimeMaxAgeToUnixTimestamp() + { + $cookie = new SetCookie(['Expires' => 'November 20, 1984']); + $this->assertInternalType('integer', $cookie->getExpires()); + } + + public function testAddsExpiresBasedOnMaxAge() + { + $t = time(); + $cookie = new SetCookie(['Max-Age' => 100]); + $this->assertEquals($t + 100, $cookie->getExpires()); + } + + public function testHoldsValues() + { + $t = time(); + $data = array( + 'Name' => 'foo', + 'Value' => 'baz', + 'Path' => '/bar', + 'Domain' => 'baz.com', + 'Expires' => $t, + 'Max-Age' => 100, + 'Secure' => true, + 'Discard' => true, + 'HttpOnly' => true, + 'foo' => 'baz', + 'bar' => 'bam' + ); + + $cookie = new SetCookie($data); + $this->assertEquals($data, $cookie->toArray()); + + $this->assertEquals('foo', $cookie->getName()); + $this->assertEquals('baz', $cookie->getValue()); + $this->assertEquals('baz.com', $cookie->getDomain()); + $this->assertEquals('/bar', $cookie->getPath()); + $this->assertEquals($t, $cookie->getExpires()); + $this->assertEquals(100, $cookie->getMaxAge()); + $this->assertTrue($cookie->getSecure()); + $this->assertTrue($cookie->getDiscard()); + $this->assertTrue($cookie->getHttpOnly()); + $this->assertEquals('baz', $cookie->toArray()['foo']); + $this->assertEquals('bam', $cookie->toArray()['bar']); + + $cookie->setName('a') + ->setValue('b') + ->setPath('c') + ->setDomain('bar.com') + ->setExpires(10) + ->setMaxAge(200) + ->setSecure(false) + ->setHttpOnly(false) + ->setDiscard(false); + + $this->assertEquals('a', $cookie->getName()); + $this->assertEquals('b', $cookie->getValue()); + $this->assertEquals('c', $cookie->getPath()); + $this->assertEquals('bar.com', $cookie->getDomain()); + $this->assertEquals(10, $cookie->getExpires()); + $this->assertEquals(200, $cookie->getMaxAge()); + $this->assertFalse($cookie->getSecure()); + $this->assertFalse($cookie->getDiscard()); + $this->assertFalse($cookie->getHttpOnly()); + } + + public function testDeterminesIfExpired() + { + $c = new SetCookie(); + $c->setExpires(10); + $this->assertTrue($c->isExpired()); + $c->setExpires(time() + 10000); + $this->assertFalse($c->isExpired()); + } + + public function testMatchesDomain() + { + $cookie = new SetCookie(); + $this->assertTrue($cookie->matchesDomain('baz.com')); + + $cookie->setDomain('baz.com'); + $this->assertTrue($cookie->matchesDomain('baz.com')); + $this->assertFalse($cookie->matchesDomain('bar.com')); + + $cookie->setDomain('.baz.com'); + $this->assertTrue($cookie->matchesDomain('.baz.com')); + $this->assertTrue($cookie->matchesDomain('foo.baz.com')); + $this->assertFalse($cookie->matchesDomain('baz.bar.com')); + $this->assertTrue($cookie->matchesDomain('baz.com')); + + $cookie->setDomain('.127.0.0.1'); + $this->assertTrue($cookie->matchesDomain('127.0.0.1')); + + $cookie->setDomain('127.0.0.1'); + $this->assertTrue($cookie->matchesDomain('127.0.0.1')); + + $cookie->setDomain('.com.'); + $this->assertFalse($cookie->matchesDomain('baz.com')); + + $cookie->setDomain('.local'); + $this->assertTrue($cookie->matchesDomain('example.local')); + } + + public function testMatchesPath() + { + $cookie = new SetCookie(); + $this->assertTrue($cookie->matchesPath('/foo')); + + $cookie->setPath('/foo'); + $this->assertTrue($cookie->matchesPath('/foo')); + $this->assertTrue($cookie->matchesPath('/foo/bar')); + $this->assertFalse($cookie->matchesPath('/bar')); + } + + public function cookieValidateProvider() + { + return array( + array('foo', 'baz', 'bar', true), + array('0', '0', '0', true), + array('', 'baz', 'bar', 'The cookie name must not be empty'), + array('foo', '', 'bar', 'The cookie value must not be empty'), + array('foo', 'baz', '', 'The cookie domain must not be empty'), + array("foo\r", 'baz', '0', 'Cookie name must not cannot invalid characters: =,; \t\r\n\013\014'), + ); + } + + /** + * @dataProvider cookieValidateProvider + */ + public function testValidatesCookies($name, $value, $domain, $result) + { + $cookie = new SetCookie(array( + 'Name' => $name, + 'Value' => $value, + 'Domain' => $domain + )); + $this->assertSame($result, $cookie->validate()); + } + + public function testDoesNotMatchIp() + { + $cookie = new SetCookie(['Domain' => '192.168.16.']); + $this->assertFalse($cookie->matchesDomain('192.168.16.121')); + } + + public function testConvertsToString() + { + $t = 1382916008; + $cookie = new SetCookie([ + 'Name' => 'test', + 'Value' => '123', + 'Domain' => 'foo.com', + 'Expires' => $t, + 'Path' => '/abc', + 'HttpOnly' => true, + 'Secure' => true + ]); + $this->assertEquals( + 'test=123; Domain=foo.com; Path=/abc; Expires=Sun, 27 Oct 2013 23:20:08 GMT; Secure; HttpOnly', + (string) $cookie + ); + } + + /** + * Provides the parsed information from a cookie + * + * @return array + */ + public function cookieParserDataProvider() + { + return array( + array( + 'ASIHTTPRequestTestCookie=This+is+the+value; expires=Sat, 26-Jul-2008 17:00:42 GMT; path=/tests; domain=allseeing-i.com; PHPSESSID=6c951590e7a9359bcedde25cda73e43c; path=/";', + array( + 'Domain' => 'allseeing-i.com', + 'Path' => '/', + 'PHPSESSID' => '6c951590e7a9359bcedde25cda73e43c', + 'Max-Age' => NULL, + 'Expires' => 'Sat, 26-Jul-2008 17:00:42 GMT', + 'Secure' => NULL, + 'Discard' => NULL, + 'Name' => 'ASIHTTPRequestTestCookie', + 'Value' => 'This+is+the+value', + 'HttpOnly' => false + ) + ), + array('', []), + array('foo', []), + // Test setting a blank value for a cookie + array(array( + 'foo=', 'foo =', 'foo =;', 'foo= ;', 'foo =', 'foo= '), + array( + 'Name' => 'foo', + 'Value' => '', + 'Discard' => null, + 'Domain' => null, + 'Expires' => null, + 'Max-Age' => null, + 'Path' => '/', + 'Secure' => null, + 'HttpOnly' => false + ) + ), + // Test setting a value and removing quotes + array(array( + 'foo=1', 'foo =1', 'foo =1;', 'foo=1 ;', 'foo =1', 'foo= 1', 'foo = 1 ;', 'foo="1"', 'foo="1";', 'foo= "1";'), + array( + 'Name' => 'foo', + 'Value' => '1', + 'Discard' => null, + 'Domain' => null, + 'Expires' => null, + 'Max-Age' => null, + 'Path' => '/', + 'Secure' => null, + 'HttpOnly' => false + ) + ), + // Some of the following tests are based on http://framework.zend.com/svn/framework/standard/trunk/tests/Zend/Http/CookieTest.php + array( + 'justacookie=foo; domain=example.com', + array( + 'Name' => 'justacookie', + 'Value' => 'foo', + 'Domain' => 'example.com', + 'Discard' => null, + 'Expires' => null, + 'Max-Age' => null, + 'Path' => '/', + 'Secure' => null, + 'HttpOnly' => false + ) + ), + array( + 'expires=tomorrow; secure; path=/Space Out/; expires=Tue, 21-Nov-2006 08:33:44 GMT; domain=.example.com', + array( + 'Name' => 'expires', + 'Value' => 'tomorrow', + 'Domain' => '.example.com', + 'Path' => '/Space Out/', + 'Expires' => 'Tue, 21-Nov-2006 08:33:44 GMT', + 'Discard' => null, + 'Secure' => true, + 'Max-Age' => null, + 'HttpOnly' => false + ) + ), + array( + 'domain=unittests; expires=Tue, 21-Nov-2006 08:33:44 GMT; domain=example.com; path=/some value/', + array( + 'Name' => 'domain', + 'Value' => 'unittests', + 'Domain' => 'example.com', + 'Path' => '/some value/', + 'Expires' => 'Tue, 21-Nov-2006 08:33:44 GMT', + 'Secure' => false, + 'Discard' => null, + 'Max-Age' => null, + 'HttpOnly' => false + ) + ), + array( + 'path=indexAction; path=/; domain=.foo.com; expires=Tue, 21-Nov-2006 08:33:44 GMT', + array( + 'Name' => 'path', + 'Value' => 'indexAction', + 'Domain' => '.foo.com', + 'Path' => '/', + 'Expires' => 'Tue, 21-Nov-2006 08:33:44 GMT', + 'Secure' => false, + 'Discard' => null, + 'Max-Age' => null, + 'HttpOnly' => false + ) + ), + array( + 'secure=sha1; secure; SECURE; domain=some.really.deep.domain.com; version=1; Max-Age=86400', + array( + 'Name' => 'secure', + 'Value' => 'sha1', + 'Domain' => 'some.really.deep.domain.com', + 'Path' => '/', + 'Secure' => true, + 'Discard' => null, + 'Expires' => time() + 86400, + 'Max-Age' => 86400, + 'HttpOnly' => false, + 'version' => '1' + ) + ), + array( + 'PHPSESSID=123456789+abcd%2Cef; secure; discard; domain=.localdomain; path=/foo/baz; expires=Tue, 21-Nov-2006 08:33:44 GMT;', + array( + 'Name' => 'PHPSESSID', + 'Value' => '123456789+abcd%2Cef', + 'Domain' => '.localdomain', + 'Path' => '/foo/baz', + 'Expires' => 'Tue, 21-Nov-2006 08:33:44 GMT', + 'Secure' => true, + 'Discard' => true, + 'Max-Age' => null, + 'HttpOnly' => false + ) + ), + ); + } + + /** + * @dataProvider cookieParserDataProvider + */ + public function testParseCookie($cookie, $parsed) + { + foreach ((array) $cookie as $v) { + $c = SetCookie::fromString($v); + $p = $c->toArray(); + + if (isset($p['Expires'])) { + // Remove expires values from the assertion if they are relatively equal + if (abs($p['Expires'] != strtotime($parsed['Expires'])) < 40) { + unset($p['Expires']); + unset($parsed['Expires']); + } + } + + if (!empty($parsed)) { + foreach ($parsed as $key => $value) { + $this->assertEquals($parsed[$key], $p[$key], 'Comparing ' . $key . ' ' . var_export($value, true) . ' : ' . var_export($parsed, true) . ' | ' . var_export($p, true)); + } + foreach ($p as $key => $value) { + $this->assertEquals($p[$key], $parsed[$key], 'Comparing ' . $key . ' ' . var_export($value, true) . ' : ' . var_export($parsed, true) . ' | ' . var_export($p, true)); + } + } else { + $this->assertEquals([ + 'Name' => null, + 'Value' => null, + 'Domain' => null, + 'Path' => '/', + 'Max-Age' => null, + 'Expires' => null, + 'Secure' => false, + 'Discard' => false, + 'HttpOnly' => false, + ], $p); + } + } + } +} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Event/AbstractEventTest.php b/core/vendor/guzzlehttp/guzzle/tests/Event/AbstractEventTest.php new file mode 100644 index 0000000000000000000000000000000000000000..357a6a6f5522d3e8b7ae610a853afb034d427cff --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/tests/Event/AbstractEventTest.php @@ -0,0 +1,15 @@ +<?php + +namespace GuzzleHttp\Tests\Event; + +class AbstractEventTest extends \PHPUnit_Framework_TestCase +{ + public function testStopsPropagation() + { + $e = $this->getMockBuilder('GuzzleHttp\Event\AbstractEvent') + ->getMockForAbstractClass(); + $this->assertFalse($e->isPropagationStopped()); + $e->stopPropagation(); + $this->assertTrue($e->isPropagationStopped()); + } +} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Event/AbstractRequestEventTest.php b/core/vendor/guzzlehttp/guzzle/tests/Event/AbstractRequestEventTest.php new file mode 100644 index 0000000000000000000000000000000000000000..eeda4e4ffa40ef19d6df1aa72ba980b8ad211c94 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/tests/Event/AbstractRequestEventTest.php @@ -0,0 +1,34 @@ +<?php + +namespace GuzzleHttp\Tests\Event; + +use GuzzleHttp\Client; +use GuzzleHttp\Adapter\Transaction; +use GuzzleHttp\Message\Request; + +/** + * @covers GuzzleHttp\Event\AbstractRequestEvent + */ +class AbstractRequestEventTest extends \PHPUnit_Framework_TestCase +{ + public function testHasTransactionMethods() + { + $t = new Transaction(new Client(), new Request('GET', '/')); + $e = $this->getMockBuilder('GuzzleHttp\Event\AbstractRequestEvent') + ->setConstructorArgs([$t]) + ->getMockForAbstractClass(); + $this->assertSame($t->getClient(), $e->getClient()); + $this->assertSame($t->getRequest(), $e->getRequest()); + } + + public function testHasTransaction() + { + $t = new Transaction(new Client(), new Request('GET', '/')); + $e = $this->getMockBuilder('GuzzleHttp\Event\AbstractRequestEvent') + ->setConstructorArgs([$t]) + ->getMockForAbstractClass(); + $r = new \ReflectionMethod($e, 'getTransaction'); + $r->setAccessible(true); + $this->assertSame($t, $r->invoke($e)); + } +} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Event/AbstractTransferEventTest.php b/core/vendor/guzzlehttp/guzzle/tests/Event/AbstractTransferEventTest.php new file mode 100644 index 0000000000000000000000000000000000000000..418e555adabb49b06a9871392a8e4fc3803d0101 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/tests/Event/AbstractTransferEventTest.php @@ -0,0 +1,25 @@ +<?php + +namespace GuzzleHttp\Tests\Event; + +use GuzzleHttp\Client; +use GuzzleHttp\Adapter\Transaction; +use GuzzleHttp\Message\Request; + +/** + * @covers GuzzleHttp\Event\AbstractTransferEvent + */ +class AbstractTransferEventTest extends \PHPUnit_Framework_TestCase +{ + public function testHasStats() + { + $s = ['foo' => 'bar']; + $t = new Transaction(new Client(), new Request('GET', '/')); + $e = $this->getMockBuilder('GuzzleHttp\Event\AbstractTransferEvent') + ->setConstructorArgs([$t, $s]) + ->getMockForAbstractClass(); + $this->assertNull($e->getTransferInfo('baz')); + $this->assertEquals('bar', $e->getTransferInfo('foo')); + $this->assertEquals($s, $e->getTransferInfo()); + } +} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Event/EmitterTest.php b/core/vendor/guzzlehttp/guzzle/tests/Event/EmitterTest.php new file mode 100644 index 0000000000000000000000000000000000000000..431b60b1eb6f6b2565dd207dc3089a7eeba06304 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/tests/Event/EmitterTest.php @@ -0,0 +1,345 @@ +<?php + +namespace GuzzleHttp\Tests\Event; + +use GuzzleHttp\Event\Emitter; +use GuzzleHttp\Event\EventInterface; +use GuzzleHttp\Event\SubscriberInterface; + +/** + * @link https://github.com/symfony/symfony/blob/master/src/Symfony/Component/EventDispatcher/Tests/EventDispatcherTest.php Based on this test. + */ +class EmitterTest extends \PHPUnit_Framework_TestCase +{ + /* Some pseudo events */ + const preFoo = 'pre.foo'; + const postFoo = 'post.foo'; + const preBar = 'pre.bar'; + const postBar = 'post.bar'; + + /** @var Emitter */ + private $emitter; + private $listener; + + protected function setUp() + { + $this->emitter = new Emitter(); + $this->listener = new TestEventListener(); + } + + protected function tearDown() + { + $this->emitter = null; + $this->listener = null; + } + + public function testInitialState() + { + $this->assertEquals(array(), $this->emitter->listeners()); + } + + public function testAddListener() + { + $this->emitter->on('pre.foo', array($this->listener, 'preFoo')); + $this->emitter->on('post.foo', array($this->listener, 'postFoo')); + $this->assertCount(1, $this->emitter->listeners(self::preFoo)); + $this->assertCount(1, $this->emitter->listeners(self::postFoo)); + $this->assertCount(2, $this->emitter->listeners()); + } + + public function testGetListenersSortsByPriority() + { + $listener1 = new TestEventListener(); + $listener2 = new TestEventListener(); + $listener3 = new TestEventListener(); + $listener1->name = '1'; + $listener2->name = '2'; + $listener3->name = '3'; + + $this->emitter->on('pre.foo', array($listener1, 'preFoo'), -10); + $this->emitter->on('pre.foo', array($listener2, 'preFoo'), 10); + $this->emitter->on('pre.foo', array($listener3, 'preFoo')); + + $expected = array( + array($listener2, 'preFoo'), + array($listener3, 'preFoo'), + array($listener1, 'preFoo'), + ); + + $this->assertSame($expected, $this->emitter->listeners('pre.foo')); + } + + public function testGetAllListenersSortsByPriority() + { + $listener1 = new TestEventListener(); + $listener2 = new TestEventListener(); + $listener3 = new TestEventListener(); + $listener4 = new TestEventListener(); + $listener5 = new TestEventListener(); + $listener6 = new TestEventListener(); + + $this->emitter->on('pre.foo', [$listener1, 'preFoo'], -10); + $this->emitter->on('pre.foo', [$listener2, 'preFoo']); + $this->emitter->on('pre.foo', [$listener3, 'preFoo'], 10); + $this->emitter->on('post.foo', [$listener4, 'preFoo'], -10); + $this->emitter->on('post.foo', [$listener5, 'preFoo']); + $this->emitter->on('post.foo', [$listener6, 'preFoo'], 10); + + $expected = [ + 'pre.foo' => [[$listener3, 'preFoo'], [$listener2, 'preFoo'], [$listener1, 'preFoo']], + 'post.foo' => [[$listener6, 'preFoo'], [$listener5, 'preFoo'], [$listener4, 'preFoo']], + ]; + + $this->assertSame($expected, $this->emitter->listeners()); + } + + public function testDispatch() + { + $this->emitter->on('pre.foo', array($this->listener, 'preFoo')); + $this->emitter->on('post.foo', array($this->listener, 'postFoo')); + $this->emitter->emit(self::preFoo, $this->getEvent()); + $this->assertTrue($this->listener->preFooInvoked); + $this->assertFalse($this->listener->postFooInvoked); + $this->assertInstanceOf('GuzzleHttp\Event\EventInterface', $this->emitter->emit(self::preFoo, $this->getEvent())); + $event = $this->getEvent(); + $return = $this->emitter->emit(self::preFoo, $event); + $this->assertSame($event, $return); + } + + public function testDispatchForClosure() + { + $invoked = 0; + $listener = function () use (&$invoked) { + $invoked++; + }; + $this->emitter->on('pre.foo', $listener); + $this->emitter->on('post.foo', $listener); + $this->emitter->emit(self::preFoo, $this->getEvent()); + $this->assertEquals(1, $invoked); + } + + public function testStopEventPropagation() + { + $otherListener = new TestEventListener(); + + // postFoo() stops the propagation, so only one listener should + // be executed + // Manually set priority to enforce $this->listener to be called first + $this->emitter->on('post.foo', array($this->listener, 'postFoo'), 10); + $this->emitter->on('post.foo', array($otherListener, 'preFoo')); + $this->emitter->emit(self::postFoo, $this->getEvent()); + $this->assertTrue($this->listener->postFooInvoked); + $this->assertFalse($otherListener->postFooInvoked); + } + + public function testDispatchByPriority() + { + $invoked = array(); + $listener1 = function () use (&$invoked) { + $invoked[] = '1'; + }; + $listener2 = function () use (&$invoked) { + $invoked[] = '2'; + }; + $listener3 = function () use (&$invoked) { + $invoked[] = '3'; + }; + $this->emitter->on('pre.foo', $listener1, -10); + $this->emitter->on('pre.foo', $listener2); + $this->emitter->on('pre.foo', $listener3, 10); + $this->emitter->emit(self::preFoo, $this->getEvent()); + $this->assertEquals(array('3', '2', '1'), $invoked); + } + + public function testRemoveListener() + { + $this->emitter->on('pre.bar', [$this->listener, 'preFoo']); + $this->assertNotEmpty($this->emitter->listeners(self::preBar)); + $this->emitter->removeListener('pre.bar', [$this->listener, 'preFoo']); + $this->assertEmpty($this->emitter->listeners(self::preBar)); + $this->emitter->removeListener('notExists', [$this->listener, 'preFoo']); + } + + public function testAddSubscriber() + { + $eventSubscriber = new TestEventSubscriber(); + $this->emitter->attach($eventSubscriber); + $this->assertNotEmpty($this->emitter->listeners(self::preFoo)); + $this->assertNotEmpty($this->emitter->listeners(self::postFoo)); + } + + public function testAddSubscriberWithPriorities() + { + $eventSubscriber = new TestEventSubscriber(); + $this->emitter->attach($eventSubscriber); + + $eventSubscriber = new TestEventSubscriberWithPriorities(); + $this->emitter->attach($eventSubscriber); + + $listeners = $this->emitter->listeners('pre.foo'); + $this->assertNotEmpty($this->emitter->listeners(self::preFoo)); + $this->assertCount(2, $listeners); + $this->assertInstanceOf('GuzzleHttp\Tests\Event\TestEventSubscriberWithPriorities', $listeners[0][0]); + } + + public function testdetach() + { + $eventSubscriber = new TestEventSubscriber(); + $this->emitter->attach($eventSubscriber); + $this->assertNotEmpty($this->emitter->listeners(self::preFoo)); + $this->assertNotEmpty($this->emitter->listeners(self::postFoo)); + $this->emitter->detach($eventSubscriber); + $this->assertEmpty($this->emitter->listeners(self::preFoo)); + $this->assertEmpty($this->emitter->listeners(self::postFoo)); + } + + public function testdetachWithPriorities() + { + $eventSubscriber = new TestEventSubscriberWithPriorities(); + $this->emitter->attach($eventSubscriber); + $this->assertNotEmpty($this->emitter->listeners(self::preFoo)); + $this->assertNotEmpty($this->emitter->listeners(self::postFoo)); + $this->emitter->detach($eventSubscriber); + $this->assertEmpty($this->emitter->listeners(self::preFoo)); + $this->assertEmpty($this->emitter->listeners(self::postFoo)); + } + + public function testEventReceivesEventNameAsArgument() + { + $listener = new TestWithDispatcher(); + $this->emitter->on('test', array($listener, 'foo')); + $this->assertNull($listener->name); + $this->emitter->emit('test', $this->getEvent()); + $this->assertEquals('test', $listener->name); + } + + /** + * @see https://bugs.php.net/bug.php?id=62976 + * + * This bug affects: + * - The PHP 5.3 branch for versions < 5.3.18 + * - The PHP 5.4 branch for versions < 5.4.8 + * - The PHP 5.5 branch is not affected + */ + public function testWorkaroundForPhpBug62976() + { + $dispatcher = new Emitter(); + $dispatcher->on('bug.62976', new CallableClass()); + $dispatcher->removeListener('bug.62976', function () {}); + $this->assertNotEmpty($dispatcher->listeners('bug.62976')); + } + + public function testRegistersEventsOnce() + { + $this->emitter->once('pre.foo', array($this->listener, 'preFoo')); + $this->emitter->on('pre.foo', array($this->listener, 'preFoo')); + $this->assertCount(2, $this->emitter->listeners(self::preFoo)); + $this->emitter->emit(self::preFoo, $this->getEvent()); + $this->assertTrue($this->listener->preFooInvoked); + $this->assertCount(1, $this->emitter->listeners(self::preFoo)); + } + + public function testReturnsEmptyArrayForNonExistentEvent() + { + $this->assertEquals([], $this->emitter->listeners('doesnotexist')); + } + + public function testCanAddFirstAndLastListeners() + { + $b = ''; + $this->emitter->on('foo', function() use (&$b) { $b .= 'a'; }, 'first'); // 1 + $this->emitter->on('foo', function() use (&$b) { $b .= 'b'; }, 'last'); // 0 + $this->emitter->on('foo', function() use (&$b) { $b .= 'c'; }, 'first'); // 2 + $this->emitter->on('foo', function() use (&$b) { $b .= 'd'; }, 'first'); // 3 + $this->emitter->on('foo', function() use (&$b) { $b .= 'e'; }, 'first'); // 4 + $this->emitter->on('foo', function() use (&$b) { $b .= 'f'; }); // 0 + $this->emitter->emit('foo', $this->getEvent()); + $this->assertEquals('edcabf', $b); + } + + /** + * @return \GuzzleHttp\Event\EventInterface + */ + private function getEvent() + { + return $this->getMockBuilder('GuzzleHttp\Event\AbstractEvent') + ->getMockForAbstractClass(); + } +} + +class CallableClass +{ + public function __invoke() + { + } +} + +class TestEventListener +{ + public $preFooInvoked = false; + public $postFooInvoked = false; + + /* Listener methods */ + + public function preFoo(EventInterface $e) + { + $this->preFooInvoked = true; + } + + public function postFoo(EventInterface $e) + { + $this->postFooInvoked = true; + + $e->stopPropagation(); + } + + /** + * @expectedException \PHPUnit_Framework_Error_Deprecated + */ + public function testHasDeprecatedAddListener() + { + $emitter = new Emitter(); + $emitter->addListener('foo', function () {}); + } + + /** + * @expectedException \PHPUnit_Framework_Error_Deprecated + */ + public function testHasDeprecatedAddSubscriber() + { + $emitter = new Emitter(); + $emitter->addSubscriber('foo', new TestEventSubscriber()); + } +} + +class TestWithDispatcher +{ + public $name; + + public function foo(EventInterface $e, $name) + { + $this->name = $name; + } +} + +class TestEventSubscriber extends TestEventListener implements SubscriberInterface +{ + public function getEvents() + { + return [ + 'pre.foo' => ['preFoo'], + 'post.foo' => ['postFoo'] + ]; + } +} + +class TestEventSubscriberWithPriorities extends TestEventListener implements SubscriberInterface +{ + public function getEvents() + { + return [ + 'pre.foo' => ['preFoo', 10], + 'post.foo' => ['postFoo'] + ]; + } +} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Event/HasEmitterTraitTest.php b/core/vendor/guzzlehttp/guzzle/tests/Event/HasEmitterTraitTest.php new file mode 100644 index 0000000000000000000000000000000000000000..bfd0870e77395af599f2b9309c89089513eb874a --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/tests/Event/HasEmitterTraitTest.php @@ -0,0 +1,28 @@ +<?php + +namespace GuzzleHttp\Tests\Event; + +use GuzzleHttp\Event\HasEmitterInterface; +use GuzzleHttp\Event\HasEmitterTrait; + +class AbstractHasEmitter implements HasEmitterInterface +{ + use HasEmitterTrait; +} + +/** + * @covers GuzzleHttp\Event\HasEmitterTrait + */ +class HasEmitterTraitTest extends \PHPUnit_Framework_TestCase +{ + public function testHelperAttachesSubscribers() + { + $mock = $this->getMockBuilder('GuzzleHttp\Tests\Event\AbstractHasEmitter') + ->getMockForAbstractClass(); + + $result = $mock->getEmitter(); + $this->assertInstanceOf('GuzzleHttp\Event\EmitterInterface', $result); + $result2 = $mock->getEmitter(); + $this->assertSame($result, $result2); + } +} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Event/HeadersEventTest.php b/core/vendor/guzzlehttp/guzzle/tests/Event/HeadersEventTest.php new file mode 100644 index 0000000000000000000000000000000000000000..02db4dce3d3dc792d1688c0d8e3d7117f198649b --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/tests/Event/HeadersEventTest.php @@ -0,0 +1,39 @@ +<?php + +namespace GuzzleHttp\Tests\Event; + +use GuzzleHttp\Adapter\Transaction; +use GuzzleHttp\Client; +use GuzzleHttp\Event\HeadersEvent; +use GuzzleHttp\Message\Request; +use GuzzleHttp\Message\Response; + +/** + * @covers GuzzleHttp\Event\HeadersEvent + */ +class HeadersEventTest extends \PHPUnit_Framework_TestCase +{ + public function testHasValues() + { + $c = new Client(); + $r = new Request('GET', '/'); + $t = new Transaction($c, $r); + $response = new Response(200); + $t->setResponse($response); + $e = new HeadersEvent($t); + $this->assertSame($c, $e->getClient()); + $this->assertSame($r, $e->getRequest()); + $this->assertSame($response, $e->getResponse()); + } + + /** + * @expectedException \RuntimeException + */ + public function testEnsuresResponseIsSet() + { + $c = new Client(); + $r = new Request('GET', '/'); + $t = new Transaction($c, $r); + new HeadersEvent($t); + } +} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Event/RequestAfterSendEventTest.php b/core/vendor/guzzlehttp/guzzle/tests/Event/RequestAfterSendEventTest.php new file mode 100644 index 0000000000000000000000000000000000000000..5b56a625764266e170522b8c550f336095230eb0 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/tests/Event/RequestAfterSendEventTest.php @@ -0,0 +1,27 @@ +<?php + +namespace GuzzleHttp\Tests\Event; + +use GuzzleHttp\Adapter\Transaction; +use GuzzleHttp\Client; +use GuzzleHttp\Event\CompleteEvent; +use GuzzleHttp\Message\Request; +use GuzzleHttp\Message\Response; + +/** + * @covers GuzzleHttp\Event\CompleteEvent + */ +class CompleteEventTest extends \PHPUnit_Framework_TestCase +{ + public function testHasValues() + { + $c = new Client(); + $r = new Request('GET', '/'); + $res = new Response(200); + $t = new Transaction($c, $r); + $e = new CompleteEvent($t); + $e->intercept($res); + $this->assertTrue($e->isPropagationStopped()); + $this->assertSame($res, $e->getResponse()); + } +} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Event/RequestBeforeSendEventTest.php b/core/vendor/guzzlehttp/guzzle/tests/Event/RequestBeforeSendEventTest.php new file mode 100644 index 0000000000000000000000000000000000000000..91590c8263b3a43348d46fa2a45b85339e55306a --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/tests/Event/RequestBeforeSendEventTest.php @@ -0,0 +1,29 @@ +<?php + +namespace GuzzleHttp\Tests\Event; + +use GuzzleHttp\Adapter\Transaction; +use GuzzleHttp\Client; +use GuzzleHttp\Event\BeforeEvent; +use GuzzleHttp\Message\Request; +use GuzzleHttp\Message\Response; + +/** + * @covers GuzzleHttp\Event\BeforeEvent + */ +class BeforeEventTest extends \PHPUnit_Framework_TestCase +{ + public function testInterceptsWithEvent() + { + $response = new Response(200); + $res = null; + $t = new Transaction(new Client(), new Request('GET', '/')); + $t->getRequest()->getEmitter()->on('complete', function ($e) use (&$res) { + $res = $e; + }); + $e = new BeforeEvent($t); + $e->intercept($response); + $this->assertTrue($e->isPropagationStopped()); + $this->assertSame($res->getClient(), $e->getClient()); + } +} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Event/RequestErrorEventTest.php b/core/vendor/guzzlehttp/guzzle/tests/Event/RequestErrorEventTest.php new file mode 100644 index 0000000000000000000000000000000000000000..14a27e7d87398419de3b3df04032ac3e25e2a9db --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/tests/Event/RequestErrorEventTest.php @@ -0,0 +1,41 @@ +<?php + +namespace GuzzleHttp\Tests\Event; + +use GuzzleHttp\Adapter\Transaction; +use GuzzleHttp\Client; +use GuzzleHttp\Event\ErrorEvent; +use GuzzleHttp\Exception\RequestException; +use GuzzleHttp\Message\Request; +use GuzzleHttp\Message\Response; + +/** + * @covers GuzzleHttp\Event\ErrorEvent + */ +class ErrorEventTest extends \PHPUnit_Framework_TestCase +{ + public function testInterceptsWithEvent() + { + $client = new Client(); + $request = new Request('GET', '/'); + $response = new Response(404); + $transaction = new Transaction($client, $request); + $except = new RequestException('foo', $request, $response); + $event = new ErrorEvent($transaction, $except); + + $this->assertSame($except, $event->getException()); + $this->assertSame($response, $event->getResponse()); + $this->assertSame($request, $event->getRequest()); + + $res = null; + $request->getEmitter()->on('complete', function ($e) use (&$res) { + $res = $e; + }); + + $good = new Response(200); + $event->intercept($good); + $this->assertTrue($event->isPropagationStopped()); + $this->assertSame($res->getClient(), $event->getClient()); + $this->assertSame($good, $res->getResponse()); + } +} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Event/RequestEventsTest.php b/core/vendor/guzzlehttp/guzzle/tests/Event/RequestEventsTest.php new file mode 100644 index 0000000000000000000000000000000000000000..64c52dc92cf5ad0bc1afe550b70e303e58ce0945 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/tests/Event/RequestEventsTest.php @@ -0,0 +1,156 @@ +<?php + +namespace GuzzleHttp\Tests\Event; + +use GuzzleHttp\Client; +use GuzzleHttp\Adapter\Transaction; +use GuzzleHttp\Event\BeforeEvent; +use GuzzleHttp\Event\ErrorEvent; +use GuzzleHttp\Event\RequestEvents; +use GuzzleHttp\Exception\RequestException; +use GuzzleHttp\Message\Request; +use GuzzleHttp\Message\Response; +use GuzzleHttp\Subscriber\Mock; + +/** + * @covers GuzzleHttp\Event\RequestEvents + */ +class RequestEventsTest extends \PHPUnit_Framework_TestCase +{ + public function testEmitsAfterSendEvent() + { + $res = null; + $t = new Transaction(new Client(), new Request('GET', '/')); + $t->setResponse(new Response(200)); + $t->getRequest()->getEmitter()->on('complete', function ($e) use (&$res) { + $res = $e; + }); + RequestEvents::emitComplete($t); + $this->assertSame($res->getClient(), $t->getClient()); + $this->assertSame($res->getRequest(), $t->getRequest()); + $this->assertEquals('/', $t->getResponse()->getEffectiveUrl()); + } + + public function testEmitsAfterSendEventAndEmitsErrorIfNeeded() + { + $ex2 = $res = null; + $request = new Request('GET', '/'); + $t = new Transaction(new Client(), $request); + $t->setResponse(new Response(200)); + $ex = new RequestException('foo', $request); + $t->getRequest()->getEmitter()->on('complete', function ($e) use ($ex) { + $ex->e = $e; + throw $ex; + }); + $t->getRequest()->getEmitter()->on('error', function ($e) use (&$ex2) { + $ex2 = $e->getException(); + $e->stopPropagation(); + }); + RequestEvents::emitComplete($t); + $this->assertSame($ex, $ex2); + } + + public function testBeforeSendEmitsErrorEvent() + { + $ex = new \Exception('Foo'); + $client = new Client(); + $request = new Request('GET', '/'); + $response = new Response(200); + $t = new Transaction($client, $request); + $beforeCalled = $errCalled = 0; + + $request->getEmitter()->on( + 'before', + function (BeforeEvent $e) use ($request, $client, &$beforeCalled, $ex) { + $this->assertSame($request, $e->getRequest()); + $this->assertSame($client, $e->getClient()); + $beforeCalled++; + throw $ex; + } + ); + + $request->getEmitter()->on( + 'error', + function (ErrorEvent $e) use (&$errCalled, $response, $ex) { + $errCalled++; + $this->assertInstanceOf('GuzzleHttp\Exception\RequestException', $e->getException()); + $this->assertSame($ex, $e->getException()->getPrevious()); + $e->intercept($response); + } + ); + + RequestEvents::emitBefore($t); + $this->assertEquals(1, $beforeCalled); + $this->assertEquals(1, $errCalled); + $this->assertSame($response, $t->getResponse()); + } + + public function testThrowsUnInterceptedErrors() + { + $ex = new \Exception('Foo'); + $client = new Client(); + $request = new Request('GET', '/'); + $t = new Transaction($client, $request); + $errCalled = 0; + + $request->getEmitter()->on('before', function (BeforeEvent $e) use ($ex) { + throw $ex; + }); + + $request->getEmitter()->on('error', function (ErrorEvent $e) use (&$errCalled) { + $errCalled++; + }); + + try { + RequestEvents::emitBefore($t); + $this->fail('Did not throw'); + } catch (RequestException $e) { + $this->assertEquals(1, $errCalled); + } + } + + public function testDoesNotEmitErrorEventTwice() + { + $client = new Client(); + $mock = new Mock([new Response(500)]); + $client->getEmitter()->attach($mock); + + $r = []; + $client->getEmitter()->on('error', function (ErrorEvent $event) use (&$r) { + $r[] = $event->getRequest(); + }); + + try { + $client->get('http://foo.com'); + $this->fail('Did not throw'); + } catch (RequestException $e) { + $this->assertCount(1, $r); + } + } + + /** + * Note: Longest test name ever. + */ + public function testEmitsErrorEventForRequestExceptionsThrownDuringBeforeThatHaveNotEmittedAnErrorEvent() + { + $request = new Request('GET', '/'); + $ex = new RequestException('foo', $request); + + $client = new Client(); + $client->getEmitter()->on('before', function (BeforeEvent $event) use ($ex) { + throw $ex; + }); + $called = false; + $client->getEmitter()->on('error', function (ErrorEvent $event) use ($ex, &$called) { + $called = true; + $this->assertSame($ex, $event->getException()); + }); + + try { + $client->get('http://foo.com'); + $this->fail('Did not throw'); + } catch (RequestException $e) { + $this->assertTrue($called); + } + } +} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Exception/ParseExceptionTest.php b/core/vendor/guzzlehttp/guzzle/tests/Exception/ParseExceptionTest.php new file mode 100644 index 0000000000000000000000000000000000000000..4ff9bfb6cecedf5ec707c0f0625dbbbee29c8d50 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/tests/Exception/ParseExceptionTest.php @@ -0,0 +1,20 @@ +<?php + +namespace GuzzleHttp\Tests\Event; + +use GuzzleHttp\Exception\ParseException; +use GuzzleHttp\Message\Response; + +/** + * @covers GuzzleHttp\Exception\ParseException + */ +class ParseExceptionTest extends \PHPUnit_Framework_TestCase +{ + public function testHasResponse() + { + $res = new Response(200); + $e = new ParseException('foo', $res); + $this->assertSame($res, $e->getResponse()); + $this->assertEquals('foo', $e->getMessage()); + } +} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Exception/RequestExceptionTest.php b/core/vendor/guzzlehttp/guzzle/tests/Exception/RequestExceptionTest.php new file mode 100644 index 0000000000000000000000000000000000000000..cfa726961395f8e457a7849be53dbfdbfeceff7a --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/tests/Exception/RequestExceptionTest.php @@ -0,0 +1,79 @@ +<?php + +namespace GuzzleHttp\Tests\Event; + +use GuzzleHttp\Exception\RequestException; +use GuzzleHttp\Message\Request; +use GuzzleHttp\Message\Response; + +/** + * @covers GuzzleHttp\Exception\RequestException + */ +class RequestExceptionTest extends \PHPUnit_Framework_TestCase +{ + public function testHasRequestAndResponse() + { + $req = new Request('GET', '/'); + $res = new Response(200); + $e = new RequestException('foo', $req, $res); + $this->assertSame($req, $e->getRequest()); + $this->assertSame($res, $e->getResponse()); + $this->assertTrue($e->hasResponse()); + $this->assertEquals('foo', $e->getMessage()); + } + + public function testCreatesGenerateException() + { + $e = RequestException::create(new Request('GET', '/')); + $this->assertEquals('Error completing request', $e->getMessage()); + $this->assertInstanceOf('GuzzleHttp\Exception\RequestException', $e); + } + + public function testCreatesClientErrorResponseException() + { + $e = RequestException::create(new Request('GET', '/'), new Response(400)); + $this->assertEquals( + 'Client error response [url] / [status code] 400 [reason phrase] Bad Request', + $e->getMessage() + ); + $this->assertInstanceOf('GuzzleHttp\Exception\ClientException', $e); + } + + public function testCreatesServerErrorResponseException() + { + $e = RequestException::create(new Request('GET', '/'), new Response(500)); + $this->assertEquals( + 'Server error response [url] / [status code] 500 [reason phrase] Internal Server Error', + $e->getMessage() + ); + $this->assertInstanceOf('GuzzleHttp\Exception\ServerException', $e); + } + + public function testCreatesGenericErrorResponseException() + { + $e = RequestException::create(new Request('GET', '/'), new Response(600)); + $this->assertEquals( + 'Unsuccessful response [url] / [status code] 600 [reason phrase] ', + $e->getMessage() + ); + $this->assertInstanceOf('GuzzleHttp\Exception\RequestException', $e); + } + + public function testCanSetAndRetrieveErrorEmitted() + { + $e = RequestException::create(new Request('GET', '/'), new Response(600)); + $this->assertFalse($e->emittedError()); + $e->emittedError(true); + $this->assertTrue($e->emittedError()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testCannotSetEmittedErrorToFalse() + { + $e = RequestException::create(new Request('GET', '/'), new Response(600)); + $e->emittedError(true); + $e->emittedError(false); + } +} diff --git a/core/vendor/guzzlehttp/guzzle/tests/FunctionsTest.php b/core/vendor/guzzlehttp/guzzle/tests/FunctionsTest.php new file mode 100644 index 0000000000000000000000000000000000000000..6e47b0cb8ffb75efa9461969f0d1b54470bf664b --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/tests/FunctionsTest.php @@ -0,0 +1,159 @@ +<?php + +namespace GuzzleHttp\Tests; + +use GuzzleHttp\Client; +use GuzzleHttp\Event\BeforeEvent; +use GuzzleHttp\Event\CompleteEvent; +use GuzzleHttp\Event\ErrorEvent; +use GuzzleHttp\Message\Response; +use GuzzleHttp\Subscriber\Mock; + +class FunctionsTest extends \PHPUnit_Framework_TestCase +{ + public function testExpandsTemplate() + { + $this->assertEquals('foo/123', \GuzzleHttp\uri_template('foo/{bar}', ['bar' => '123'])); + } + + public function noBodyProvider() + { + return [['get'], ['head'], ['delete']]; + } + + /** + * @dataProvider noBodyProvider + */ + public function testSendsNoBody($method) + { + Server::flush(); + Server::enqueue([new Response(200)]); + call_user_func("GuzzleHttp\\{$method}", Server::$url, [ + 'headers' => ['foo' => 'bar'], + 'query' => ['a' => '1'] + ]); + $sent = Server::received(true)[0]; + $this->assertEquals(strtoupper($method), $sent->getMethod()); + $this->assertEquals('/?a=1', $sent->getResource()); + $this->assertEquals('bar', $sent->getHeader('foo')); + } + + public function testSendsOptionsRequest() + { + Server::flush(); + Server::enqueue([new Response(200)]); + \GuzzleHttp\options(Server::$url, ['headers' => ['foo' => 'bar']]); + $sent = Server::received(true)[0]; + $this->assertEquals('OPTIONS', $sent->getMethod()); + $this->assertEquals('/', $sent->getResource()); + $this->assertEquals('bar', $sent->getHeader('foo')); + } + + public function hasBodyProvider() + { + return [['put'], ['post'], ['patch']]; + } + + /** + * @dataProvider hasBodyProvider + */ + public function testSendsWithBody($method) + { + Server::flush(); + Server::enqueue([new Response(200)]); + call_user_func("GuzzleHttp\\{$method}", Server::$url, [ + 'headers' => ['foo' => 'bar'], + 'body' => 'test', + 'query' => ['a' => '1'] + ]); + $sent = Server::received(true)[0]; + $this->assertEquals(strtoupper($method), $sent->getMethod()); + $this->assertEquals('/?a=1', $sent->getResource()); + $this->assertEquals('bar', $sent->getHeader('foo')); + $this->assertEquals('test', $sent->getBody()); + } + + /** + * @expectedException \PHPUnit_Framework_Error_Deprecated + * @expectedExceptionMessage GuzzleHttp\Tests\HasDeprecations::baz() is deprecated and will be removed in a future version. Update your code to use the equivalent GuzzleHttp\Tests\HasDeprecations::foo() method instead to avoid breaking changes when this shim is removed. + */ + public function testManagesDeprecatedMethods() + { + $d = new HasDeprecations(); + $d->baz(); + } + + /** + * @expectedException \BadMethodCallException + */ + public function testManagesDeprecatedMethodsAndHandlesMissingMethods() + { + $d = new HasDeprecations(); + $d->doesNotExist(); + } + + public function testBatchesRequests() + { + $client = new Client(); + $responses = [ + new Response(301, ['Location' => 'http://foo.com/bar']), + new Response(200), + new Response(200), + new Response(404) + ]; + $client->getEmitter()->attach(new Mock($responses)); + $requests = [ + $client->createRequest('GET', 'http://foo.com/baz'), + $client->createRequest('HEAD', 'http://httpbin.org/get'), + $client->createRequest('PUT', 'http://httpbin.org/put'), + ]; + + $a = $b = $c = 0; + $result = \GuzzleHttp\batch($client, $requests, [ + 'before' => function (BeforeEvent $e) use (&$a) { $a++; }, + 'complete' => function (CompleteEvent $e) use (&$b) { $b++; }, + 'error' => function (ErrorEvent $e) use (&$c) { $c++; }, + ]); + + $this->assertEquals(4, $a); + $this->assertEquals(2, $b); + $this->assertEquals(1, $c); + $this->assertCount(3, $result); + + foreach ($result as $i => $request) { + $this->assertSame($requests[$i], $request); + } + + // The first result is actually the second (redirect) response. + $this->assertSame($responses[1], $result[$requests[0]]); + // The second result is a 1:1 request:response map + $this->assertSame($responses[2], $result[$requests[1]]); + // The third entry is the 404 RequestException + $this->assertSame($responses[3], $result[$requests[2]]->getResponse()); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Invalid event format + */ + public function testBatchValidatesTheEventFormat() + { + $client = new Client(); + $requests = [$client->createRequest('GET', 'http://foo.com/baz')]; + \GuzzleHttp\batch($client, $requests, ['complete' => 'foo']); + } +} + +class HasDeprecations +{ + function foo() + { + return 'abc'; + } + function __call($name, $arguments) + { + return \GuzzleHttp\deprecation_proxy($this, $name, $arguments, [ + 'baz' => 'foo' + ]); + } +} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Message/AbstractMessageTest.php b/core/vendor/guzzlehttp/guzzle/tests/Message/AbstractMessageTest.php new file mode 100644 index 0000000000000000000000000000000000000000..9ed91ee9a5fb3259bed5f220316eb6355c87e5ef --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/tests/Message/AbstractMessageTest.php @@ -0,0 +1,273 @@ +<?php + +namespace GuzzleHttp\Tests\Message; + +use GuzzleHttp\Message\AbstractMessage; +use GuzzleHttp\Message\Request; +use GuzzleHttp\Stream\Stream; + +/** + * @covers \GuzzleHttp\Message\AbstractMessage + */ +class AbstractMessageTest extends \PHPUnit_Framework_TestCase +{ + public function testHasProtocolVersion() + { + $m = new Message(); + $this->assertEquals(1.1, $m->getProtocolVersion()); + } + + public function testHasHeaders() + { + $m = new Message(); + $this->assertFalse($m->hasHeader('foo')); + $m->addHeader('foo', 'bar'); + $this->assertTrue($m->hasHeader('foo')); + } + + public function testInitializesMessageWithProtocolVersionOption() + { + $m = new Request('GET', '/', [], null, [ + 'protocol_version' => '10' + ]); + $this->assertEquals(10, $m->getProtocolVersion()); + } + + public function testHasBody() + { + $m = new Message(); + $this->assertNull($m->getBody()); + $s = Stream::factory('test'); + $m->setBody($s); + $this->assertSame($s, $m->getBody()); + $this->assertFalse($m->hasHeader('Content-Length')); + } + + public function testCanRemoveBodyBySettingToNullAndRemovesCommonBodyHeaders() + { + $m = new Message(); + $m->setBody(Stream::factory('foo')); + $m->setHeader('Content-Length', 3)->setHeader('Transfer-Encoding', 'chunked'); + $m->setBody(null); + $this->assertNull($m->getBody()); + $this->assertFalse($m->hasHeader('Content-Length')); + $this->assertFalse($m->hasHeader('Transfer-Encoding')); + } + + public function testCastsToString() + { + $m = new Message(); + $m->setHeader('foo', 'bar'); + $m->setBody(Stream::factory('baz')); + $this->assertEquals("Foo!\r\nfoo: bar\r\n\r\nbaz", (string) $m); + } + + public function parseParamsProvider() + { + $res1 = array( + array( + '<http:/.../front.jpeg>', + 'rel' => 'front', + 'type' => 'image/jpeg', + ), + array( + '<http://.../back.jpeg>', + 'rel' => 'back', + 'type' => 'image/jpeg', + ), + ); + + return array( + array( + '<http:/.../front.jpeg>; rel="front"; type="image/jpeg", <http://.../back.jpeg>; rel=back; type="image/jpeg"', + $res1 + ), + array( + '<http:/.../front.jpeg>; rel="front"; type="image/jpeg",<http://.../back.jpeg>; rel=back; type="image/jpeg"', + $res1 + ), + array( + 'foo="baz"; bar=123, boo, test="123", foobar="foo;bar"', + array( + array('foo' => 'baz', 'bar' => '123'), + array('boo'), + array('test' => '123'), + array('foobar' => 'foo;bar') + ) + ), + array( + '<http://.../side.jpeg?test=1>; rel="side"; type="image/jpeg",<http://.../side.jpeg?test=2>; rel=side; type="image/jpeg"', + array( + array('<http://.../side.jpeg?test=1>', 'rel' => 'side', 'type' => 'image/jpeg'), + array('<http://.../side.jpeg?test=2>', 'rel' => 'side', 'type' => 'image/jpeg') + ) + ), + array( + '', + array() + ) + ); + } + + /** + * @dataProvider parseParamsProvider + */ + public function testParseParams($header, $result) + { + $request = new Request('GET', '/', ['foo' => $header]); + $this->assertEquals($result, Message::parseHeader($request, 'foo')); + } + + public function testAddsHeadersWhenNotPresent() + { + $h = new Message(); + $h->addHeader('foo', 'bar'); + $this->assertInternalType('string', $h->getHeader('foo')); + $this->assertEquals('bar', $h->getHeader('foo')); + } + + public function testAddsHeadersWhenPresentSameCase() + { + $h = new Message(); + $h->addHeader('foo', 'bar')->addHeader('foo', 'baz'); + $this->assertEquals('bar, baz', $h->getHeader('foo')); + $this->assertEquals(['bar', 'baz'], $h->getHeader('foo', true)); + } + + public function testAddsMultipleHeaders() + { + $h = new Message(); + $h->addHeaders([ + 'foo' => ' bar', + 'baz' => [' bam ', 'boo'] + ]); + $this->assertEquals([ + 'foo' => ['bar'], + 'baz' => ['bam', 'boo'] + ], $h->getHeaders()); + } + + public function testAddsHeadersWhenPresentDifferentCase() + { + $h = new Message(); + $h->addHeader('Foo', 'bar')->addHeader('fOO', 'baz'); + $this->assertEquals('bar, baz', $h->getHeader('foo')); + } + + public function testAddsHeadersWithArray() + { + $h = new Message(); + $h->addHeader('Foo', ['bar', 'baz']); + $this->assertEquals('bar, baz', $h->getHeader('foo')); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testThrowsExceptionWhenInvalidValueProvidedToAddHeader() + { + (new Message())->addHeader('foo', false); + } + + public function testGetHeadersReturnsAnArrayOfOverTheWireHeaderValues() + { + $h = new Message(); + $h->addHeader('foo', 'bar'); + $h->addHeader('Foo', 'baz'); + $h->addHeader('boO', 'test'); + $result = $h->getHeaders(); + $this->assertInternalType('array', $result); + $this->assertArrayHasKey('Foo', $result); + $this->assertArrayNotHasKey('foo', $result); + $this->assertArrayHasKey('boO', $result); + $this->assertEquals(['bar', 'baz'], $result['Foo']); + $this->assertEquals(['test'], $result['boO']); + } + + public function testSetHeaderOverwritesExistingValues() + { + $h = new Message(); + $h->setHeader('foo', 'bar'); + $this->assertEquals('bar', $h->getHeader('foo')); + $h->setHeader('Foo', 'baz'); + $this->assertEquals('baz', $h->getHeader('foo')); + $this->assertArrayHasKey('Foo', $h->getHeaders()); + } + + public function testSetHeaderOverwritesExistingValuesUsingHeaderArray() + { + $h = new Message(); + $h->setHeader('foo', ['bar']); + $this->assertEquals('bar', $h->getHeader('foo')); + } + + public function testSetHeaderOverwritesExistingValuesUsingArray() + { + $h = new Message(); + $h->setHeader('foo', ['bar']); + $this->assertEquals('bar', $h->getHeader('foo')); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testThrowsExceptionWhenInvalidValueProvidedToSetHeader() + { + (new Message())->setHeader('foo', false); + } + + public function testSetHeadersOverwritesAllHeaders() + { + $h = new Message(); + $h->setHeader('foo', 'bar'); + $h->setHeaders(['foo' => 'a', 'boo' => 'b']); + $this->assertEquals(['foo' => ['a'], 'boo' => ['b']], $h->getHeaders()); + } + + public function testChecksIfCaseInsensitiveHeaderIsPresent() + { + $h = new Message(); + $h->setHeader('foo', 'bar'); + $this->assertTrue($h->hasHeader('foo')); + $this->assertTrue($h->hasHeader('Foo')); + $h->setHeader('fOo', 'bar'); + $this->assertTrue($h->hasHeader('Foo')); + } + + public function testRemovesHeaders() + { + $h = new Message(); + $h->setHeader('foo', 'bar'); + $h->removeHeader('foo'); + $this->assertFalse($h->hasHeader('foo')); + $h->setHeader('Foo', 'bar'); + $h->removeHeader('FOO'); + $this->assertFalse($h->hasHeader('foo')); + } + + public function testReturnsCorrectTypeWhenMissing() + { + $h = new Message(); + $this->assertInternalType('string', $h->getHeader('foo')); + $this->assertInternalType('array', $h->getHeader('foo', true)); + } + + public function testSetsIntegersAndFloatsAsHeaders() + { + $h = new Message(); + $h->setHeader('foo', 10); + $h->setHeader('bar', 10.5); + $h->addHeader('foo', 10); + $h->addHeader('bar', 10.5); + $this->assertSame('10, 10', $h->getHeader('foo')); + $this->assertSame('10.5, 10.5', $h->getHeader('bar')); + } +} + +class Message extends AbstractMessage +{ + protected function getStartLine() + { + return 'Foo!'; + } +} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Message/MessageFactoryTest.php b/core/vendor/guzzlehttp/guzzle/tests/Message/MessageFactoryTest.php new file mode 100644 index 0000000000000000000000000000000000000000..78cef277093e784db012909d02321f3d02dc90e3 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/tests/Message/MessageFactoryTest.php @@ -0,0 +1,458 @@ +<?php + +namespace GuzzleHttp\Tests\Message; + +use GuzzleHttp\Client; +use GuzzleHttp\Post\PostFile; +use GuzzleHttp\Message\Response; +use GuzzleHttp\Message\MessageFactory; +use GuzzleHttp\Subscriber\Cookie; +use GuzzleHttp\Cookie\CookieJar; +use GuzzleHttp\Subscriber\Mock; +use GuzzleHttp\Stream\Stream; +use GuzzleHttp\Query; + +/** + * @covers GuzzleHttp\Message\MessageFactory + */ +class MessageFactoryTest extends \PHPUnit_Framework_TestCase +{ + public function testCreatesResponses() + { + $f = new MessageFactory(); + $response = $f->createResponse(200, ['foo' => 'bar'], 'test', [ + 'protocol_version' => 1.0 + ]); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals(['foo' => ['bar']], $response->getHeaders()); + $this->assertEquals('test', $response->getBody()); + $this->assertEquals(1.0, $response->getProtocolVersion()); + } + + public function testCreatesRequestFromMessage() + { + $f = new MessageFactory(); + $req = $f->fromMessage("GET / HTTP/1.1\r\nBaz: foo\r\n\r\n"); + $this->assertEquals('GET', $req->getMethod()); + $this->assertEquals('/', $req->getPath()); + $this->assertEquals('foo', $req->getHeader('Baz')); + $this->assertNull($req->getBody()); + } + + public function testCreatesRequestFromMessageWithBody() + { + $req = (new MessageFactory())->fromMessage("GET / HTTP/1.1\r\nBaz: foo\r\n\r\ntest"); + $this->assertEquals('test', $req->getBody()); + } + + public function testCreatesRequestWithPostBody() + { + $req = (new MessageFactory())->createRequest('GET', 'http://www.foo.com', ['body' => ['abc' => '123']]); + $this->assertEquals('abc=123', $req->getBody()); + } + + public function testCreatesRequestWithPostBodyAndPostFiles() + { + $pf = fopen(__FILE__, 'r'); + $pfi = new PostFile('ghi', 'abc', __FILE__); + $req = (new MessageFactory())->createRequest('GET', 'http://www.foo.com', [ + 'body' => [ + 'abc' => '123', + 'def' => $pf, + 'ghi' => $pfi + ] + ]); + $this->assertInstanceOf('GuzzleHttp\Post\PostBody', $req->getBody()); + $s = (string) $req; + $this->assertContains('testCreatesRequestWithPostBodyAndPostFiles', $s); + $this->assertContains('multipart/form-data', $s); + $this->assertTrue(in_array($pfi, $req->getBody()->getFiles(), true)); + } + + public function testCreatesResponseFromMessage() + { + $response = (new MessageFactory)->fromMessage("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ntest"); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('OK', $response->getReasonPhrase()); + $this->assertEquals('4', $response->getHeader('Content-Length')); + $this->assertEquals('test', $response->getBody(true)); + } + + public function testCanCreateHeadResponses() + { + $response = (new MessageFactory)->fromMessage("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\n"); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('OK', $response->getReasonPhrase()); + $this->assertEquals(null, $response->getBody()); + $this->assertEquals('4', $response->getHeader('Content-Length')); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testFactoryRequiresMessageForRequest() + { + (new MessageFactory)->fromMessage(''); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage foo + */ + public function testValidatesOptionsAreImplemented() + { + (new MessageFactory)->createRequest('GET', 'http://test.com', ['foo' => 'bar']); + } + + public function testOptionsAddsRequestOptions() + { + $request = (new MessageFactory)->createRequest( + 'GET', 'http://test.com', ['config' => ['baz' => 'bar']] + ); + $this->assertEquals('bar', $request->getConfig()->get('baz')); + } + + public function testCanDisableRedirects() + { + $request = (new MessageFactory)->createRequest('GET', '/', ['allow_redirects' => false]); + $this->assertEmpty($request->getEmitter()->listeners('complete')); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testValidatesRedirects() + { + (new MessageFactory)->createRequest('GET', '/', ['allow_redirects' => []]); + } + + public function testCanEnableStrictRedirectsAndSpecifyMax() + { + $request = (new MessageFactory)->createRequest('GET', '/', [ + 'allow_redirects' => ['max' => 10, 'strict' => true] + ]); + $this->assertTrue($request->getConfig()['redirect']['strict']); + $this->assertEquals(10, $request->getConfig()['redirect']['max']); + } + + public function testCanAddCookiesFromHash() + { + $request = (new MessageFactory)->createRequest('GET', 'http://www.test.com/', [ + 'cookies' => ['Foo' => 'Bar'] + ]); + $cookies = null; + foreach ($request->getEmitter()->listeners('before') as $l) { + if ($l[0] instanceof Cookie) { + $cookies = $l[0]; + break; + } + } + if (!$cookies) { + $this->fail('Did not add cookie listener'); + } else { + $this->assertCount(1, $cookies->getCookieJar()); + } + } + + public function testAddsCookieUsingTrue() + { + $factory = new MessageFactory(); + $request1 = $factory->createRequest('GET', '/', ['cookies' => true]); + $request2 = $factory->createRequest('GET', '/', ['cookies' => true]); + $listeners = function ($r) { + return array_filter($r->getEmitter()->listeners('before'), function ($l) { + return $l[0] instanceof Cookie; + }); + }; + $this->assertSame($listeners($request1), $listeners($request2)); + } + + public function testAddsCookieFromCookieJar() + { + $jar = new CookieJar(); + $request = (new MessageFactory)->createRequest('GET', '/', ['cookies' => $jar]); + foreach ($request->getEmitter()->listeners('before') as $l) { + if ($l[0] instanceof Cookie) { + $this->assertSame($jar, $l[0]->getCookieJar()); + } + } + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testValidatesCookies() + { + (new MessageFactory)->createRequest('GET', '/', ['cookies' => 'baz']); + } + + public function testCanAddQuery() + { + $request = (new MessageFactory)->createRequest('GET', 'http://foo.com', [ + 'query' => ['Foo' => 'Bar'] + ]); + $this->assertEquals('Bar', $request->getQuery()->get('Foo')); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testValidatesQuery() + { + (new MessageFactory)->createRequest('GET', 'http://foo.com', [ + 'query' => 'foo' + ]); + } + + public function testCanSetDefaultQuery() + { + $request = (new MessageFactory)->createRequest('GET', 'http://foo.com?test=abc', [ + 'query' => ['Foo' => 'Bar', 'test' => 'def'] + ]); + $this->assertEquals('Bar', $request->getQuery()->get('Foo')); + $this->assertEquals('abc', $request->getQuery()->get('test')); + } + + public function testCanSetDefaultQueryWithObject() + { + $request = (new MessageFactory)->createRequest('GET', 'http://foo.com?test=abc', [ + 'query' => new Query(['Foo' => 'Bar', 'test' => 'def']) + ]); + $this->assertEquals('Bar', $request->getQuery()->get('Foo')); + $this->assertEquals('abc', $request->getQuery()->get('test')); + } + + public function testCanAddBasicAuth() + { + $request = (new MessageFactory)->createRequest('GET', 'http://foo.com', [ + 'auth' => ['michael', 'test'] + ]); + $this->assertTrue($request->hasHeader('Authorization')); + } + + public function testCanAddDigestAuth() + { + $request = (new MessageFactory)->createRequest('GET', 'http://foo.com', [ + 'auth' => ['michael', 'test', 'digest'] + ]); + $this->assertEquals('michael:test', $request->getConfig()->getPath('curl/' . CURLOPT_USERPWD)); + $this->assertEquals(CURLAUTH_DIGEST, $request->getConfig()->getPath('curl/' . CURLOPT_HTTPAUTH)); + } + + public function testCanDisableAuth() + { + $request = (new MessageFactory)->createRequest('GET', 'http://foo.com', [ + 'auth' => false + ]); + $this->assertFalse($request->hasHeader('Authorization')); + } + + public function testCanSetCustomAuth() + { + $request = (new MessageFactory)->createRequest('GET', 'http://foo.com', [ + 'auth' => 'foo' + ]); + $this->assertEquals('foo', $request->getConfig()['auth']); + } + + public function testCanAddEvents() + { + $foo = null; + $client = new Client(); + $client->getEmitter()->attach(new Mock([new Response(200)])); + $client->get('http://test.com', [ + 'events' => [ + 'before' => function () use (&$foo) { $foo = true; } + ] + ]); + $this->assertTrue($foo); + } + + public function testCanAddEventsWithPriority() + { + $foo = null; + $client = new Client(); + $client->getEmitter()->attach(new Mock(array(new Response(200)))); + $request = $client->createRequest('GET', 'http://test.com', [ + 'events' => [ + 'before' => [ + 'fn' => function () use (&$foo) { $foo = true; }, + 'priority' => 123 + ] + ] + ]); + $client->send($request); + $this->assertTrue($foo); + $l = $this->readAttribute($request->getEmitter(), 'listeners'); + $this->assertArrayHasKey(123, $l['before']); + } + + public function testCanAddEventsOnce() + { + $foo = 0; + $client = new Client(); + $client->getEmitter()->attach(new Mock([ + new Response(200), + new Response(200), + ])); + $fn = function () use (&$foo) { ++$foo; }; + $request = $client->createRequest('GET', 'http://test.com', [ + 'events' => ['before' => ['fn' => $fn, 'once' => true]] + ]); + $client->send($request); + $this->assertEquals(1, $foo); + $client->send($request); + $this->assertEquals(1, $foo); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testValidatesEventContainsFn() + { + $client = new Client(['base_url' => 'http://test.com']); + $client->createRequest('GET', '/', ['events' => ['before' => ['foo' => 'bar']]]); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testValidatesEventIsArray() + { + $client = new Client(['base_url' => 'http://test.com']); + $client->createRequest('GET', '/', ['events' => ['before' => '123']]); + } + + public function testCanAddSubscribers() + { + $mock = new Mock([new Response(200)]); + $client = new Client(); + $client->getEmitter()->attach($mock); + $request = $client->get('http://test.com', ['subscribers' => [$mock]]); + } + + public function testCanDisableExceptions() + { + $client = new Client(); + $this->assertEquals(500, $client->get('http://test.com', [ + 'subscribers' => [new Mock([new Response(500)])], + 'exceptions' => false + ])->getStatusCode()); + } + + public function testCanChangeSaveToLocation() + { + $saveTo = Stream::factory(); + $request = (new MessageFactory)->createRequest('GET', '/', ['save_to' => $saveTo]); + $this->assertSame($saveTo, $request->getConfig()->get('save_to')); + } + + public function testCanSetProxy() + { + $request = (new MessageFactory)->createRequest('GET', '/', ['proxy' => '192.168.16.121']); + $this->assertEquals('192.168.16.121', $request->getConfig()->get('proxy')); + } + + public function testCanSetHeadersOption() + { + $request = (new MessageFactory)->createRequest('GET', '/', ['headers' => ['Foo' => 'Bar']]); + $this->assertEquals('Bar', (string) $request->getHeader('Foo')); + } + + public function testCanSetHeaders() + { + $request = (new MessageFactory())->createRequest('GET', '/', [ + 'headers' => ['Foo' => ['Baz', 'Bar'], 'Test' => '123'] + ]); + $this->assertEquals('Baz, Bar', $request->getHeader('Foo')); + $this->assertEquals('123', $request->getHeader('Test')); + } + + public function testCanSetTimeoutOption() + { + $request = (new MessageFactory())->createRequest('GET', '/', ['timeout' => 1.5]); + $this->assertEquals(1.5, $request->getConfig()->get('timeout')); + } + + public function testCanSetConnectTimeoutOption() + { + $request = (new MessageFactory())->createRequest('GET', '/', ['connect_timeout' => 1.5]); + $this->assertEquals(1.5, $request->getConfig()->get('connect_timeout')); + } + + public function testCanSetDebug() + { + $request = (new MessageFactory())->createRequest('GET', '/', ['debug' => true]); + $this->assertTrue($request->getConfig()->get('debug')); + } + + public function testCanSetVerifyToOff() + { + $request = (new MessageFactory())->createRequest('GET', '/', ['verify' => false]); + $this->assertFalse($request->getConfig()->get('verify')); + } + + public function testCanSetVerifyToOn() + { + $request = (new MessageFactory())->createRequest('GET', '/', ['verify' => true]); + $this->assertTrue($request->getConfig()->get('verify')); + } + + public function testCanSetVerifyToPath() + { + $request = (new MessageFactory())->createRequest('GET', '/', ['verify' => '/foo.pem']); + $this->assertEquals('/foo.pem', $request->getConfig()->get('verify')); + } + + public function inputValidation() + { + return array_map(function ($option) { return array($option); }, array( + 'headers', 'events', 'subscribers', 'params' + )); + } + + /** + * @dataProvider inputValidation + * @expectedException \InvalidArgumentException + */ + public function testValidatesInput($option) + { + (new MessageFactory())->createRequest('GET', '/', [$option => 'foo']); + } + + public function testCanAddSslKey() + { + $request = (new MessageFactory())->createRequest('GET', '/', ['ssl_key' => '/foo.pem']); + $this->assertEquals('/foo.pem', $request->getConfig()->get('ssl_key')); + } + + public function testCanAddSslKeyPassword() + { + $request = (new MessageFactory())->createRequest('GET', '/', ['ssl_key' => ['/foo.pem', 'bar']]); + $this->assertEquals(['/foo.pem', 'bar'], $request->getConfig()->get('ssl_key')); + } + + public function testCanAddSslCert() + { + $request = (new MessageFactory())->createRequest('GET', '/', ['cert' => '/foo.pem']); + $this->assertEquals('/foo.pem', $request->getConfig()->get('cert')); + } + + public function testCanAddSslCertPassword() + { + $request = (new MessageFactory())->createRequest('GET', '/', ['cert' => ['/foo.pem', 'bar']]); + $this->assertEquals(['/foo.pem', 'bar'], $request->getConfig()->get('cert')); + } + + public function testCreatesBodyWithoutZeroString() + { + $request = (new MessageFactory())->createRequest('PUT', 'http://test.com', ['body' => '0']); + $this->assertSame('0', (string) $request->getBody()); + } + + public function testCanSetProtocolVersion() + { + $request = (new MessageFactory())->createRequest('GET', 'http://test.com', ['version' => 1.0]); + $this->assertEquals(1.0, $request->getProtocolVersion()); + } +} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Message/MessageParserTest.php b/core/vendor/guzzlehttp/guzzle/tests/Message/MessageParserTest.php new file mode 100644 index 0000000000000000000000000000000000000000..0bcc9430f2903a7dde579d713e51ef1ea09a729f --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/tests/Message/MessageParserTest.php @@ -0,0 +1,276 @@ +<?php + +namespace GuzzleHttp\Tests\Message; + +use GuzzleHttp\Message\MessageParser; + +/** + * @covers \GuzzleHttp\Message\MessageParser + */ +class MessageParserTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider requestProvider + */ + public function testParsesRequests($message, $parts) + { + $parser = new MessageParser(); + $this->compareRequestResults($parts, $parser->parseRequest($message)); + } + + /** + * @dataProvider responseProvider + */ + public function testParsesResponses($message, $parts) + { + $parser = new MessageParser(); + $this->compareResponseResults($parts, $parser->parseResponse($message)); + } + + public function testParsesRequestsWithMissingProtocol() + { + $parser = new MessageParser(); + $parts = $parser->parseRequest("GET /\r\nHost: Foo.com\r\n\r\n"); + $this->assertEquals('GET', $parts['method']); + $this->assertEquals('HTTP', $parts['protocol']); + $this->assertEquals('1.1', $parts['protocol_version']); + } + + public function testParsesRequestsWithMissingVersion() + { + $parser = new MessageParser(); + $parts = $parser->parseRequest("GET / HTTP\r\nHost: Foo.com\r\n\r\n"); + $this->assertEquals('GET', $parts['method']); + $this->assertEquals('HTTP', $parts['protocol']); + $this->assertEquals('1.1', $parts['protocol_version']); + } + + public function testParsesResponsesWithMissingReasonPhrase() + { + $parser = new MessageParser(); + $parts = $parser->parseResponse("HTTP/1.1 200\r\n\r\n"); + $this->assertEquals('200', $parts['code']); + $this->assertEquals('', $parts['reason_phrase']); + $this->assertEquals('HTTP', $parts['protocol']); + $this->assertEquals('1.1', $parts['protocol_version']); + } + + public function requestProvider() + { + $auth = base64_encode('michael:foo'); + + return array( + + // Empty request + array('', false), + + // Converts casing of request. Does not require host header. + array("GET / HTTP/1.1\r\n\r\n", array( + 'method' => 'GET', + 'protocol' => 'HTTP', + 'protocol_version' => '1.1', + 'request_url' => array( + 'scheme' => 'http', + 'host' => '', + 'port' => '', + 'path' => '/', + 'query' => '' + ), + 'headers' => array(), + 'body' => '' + )), + // Path and query string, multiple header values per header and case sensitive storage + array("HEAD /path?query=foo HTTP/1.0\r\nHost: example.com\r\nX-Foo: foo\r\nx-foo: Bar\r\nX-Foo: foo\r\nX-Foo: Baz\r\n\r\n", array( + 'method' => 'HEAD', + 'protocol' => 'HTTP', + 'protocol_version' => '1.0', + 'request_url' => array( + 'scheme' => 'http', + 'host' => 'example.com', + 'port' => '', + 'path' => '/path', + 'query' => 'query=foo' + ), + 'headers' => array( + 'Host' => 'example.com', + 'X-Foo' => array('foo', 'foo', 'Baz'), + 'x-foo' => 'Bar' + ), + 'body' => '' + )), + // Includes a body + array("PUT / HTTP/1.0\r\nhost: example.com:443\r\nContent-Length: 4\r\n\r\ntest", array( + 'method' => 'PUT', + 'protocol' => 'HTTP', + 'protocol_version' => '1.0', + 'request_url' => array( + 'scheme' => 'https', + 'host' => 'example.com', + 'port' => '443', + 'path' => '/', + 'query' => '' + ), + 'headers' => array( + 'host' => 'example.com:443', + 'Content-Length' => '4' + ), + 'body' => 'test' + )), + // Includes Authorization headers + array("GET / HTTP/1.1\r\nHost: example.com:8080\r\nAuthorization: Basic {$auth}\r\n\r\n", array( + 'method' => 'GET', + 'protocol' => 'HTTP', + 'protocol_version' => '1.1', + 'request_url' => array( + 'scheme' => 'http', + 'host' => 'example.com', + 'port' => '8080', + 'path' => '/', + 'query' => '' + ), + 'headers' => array( + 'Host' => 'example.com:8080', + 'Authorization' => "Basic {$auth}" + ), + 'body' => '' + )), + // Include authorization header + array("GET / HTTP/1.1\r\nHost: example.com:8080\r\nauthorization: Basic {$auth}\r\n\r\n", array( + 'method' => 'GET', + 'protocol' => 'HTTP', + 'protocol_version' => '1.1', + 'request_url' => array( + 'scheme' => 'http', + 'host' => 'example.com', + 'port' => '8080', + 'path' => '/', + 'query' => '' + ), + 'headers' => array( + 'Host' => 'example.com:8080', + 'authorization' => "Basic {$auth}" + ), + 'body' => '' + )), + ); + } + + public function responseProvider() + { + return array( + // Empty request + array('', false), + + array("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", array( + 'protocol' => 'HTTP', + 'protocol_version' => '1.1', + 'code' => '200', + 'reason_phrase' => 'OK', + 'headers' => array( + 'Content-Length' => 0 + ), + 'body' => '' + )), + array("HTTP/1.0 400 Bad Request\r\nContent-Length: 0\r\n\r\n", array( + 'protocol' => 'HTTP', + 'protocol_version' => '1.0', + 'code' => '400', + 'reason_phrase' => 'Bad Request', + 'headers' => array( + 'Content-Length' => 0 + ), + 'body' => '' + )), + array("HTTP/1.0 100 Continue\r\n\r\n", array( + 'protocol' => 'HTTP', + 'protocol_version' => '1.0', + 'code' => '100', + 'reason_phrase' => 'Continue', + 'headers' => array(), + 'body' => '' + )), + array("HTTP/1.1 204 No Content\r\nX-Foo: foo\r\nx-foo: Bar\r\nX-Foo: foo\r\n\r\n", array( + 'protocol' => 'HTTP', + 'protocol_version' => '1.1', + 'code' => '204', + 'reason_phrase' => 'No Content', + 'headers' => array( + 'X-Foo' => array('foo', 'foo'), + 'x-foo' => 'Bar' + ), + 'body' => '' + )), + array("HTTP/1.1 200 Ok that is great!\r\nContent-Length: 4\r\n\r\nTest", array( + 'protocol' => 'HTTP', + 'protocol_version' => '1.1', + 'code' => '200', + 'reason_phrase' => 'Ok that is great!', + 'headers' => array( + 'Content-Length' => 4 + ), + 'body' => 'Test' + )), + ); + } + + public function compareRequestResults($result, $expected) + { + if (!$result) { + $this->assertFalse($expected); + return; + } + + $this->assertEquals($result['method'], $expected['method']); + $this->assertEquals($result['protocol'], $expected['protocol']); + $this->assertEquals($result['protocol_version'], $expected['protocol_version']); + $this->assertEquals($result['request_url'], $expected['request_url']); + $this->assertEquals($result['body'], $expected['body']); + $this->compareHttpHeaders($result['headers'], $expected['headers']); + } + + public function compareResponseResults($result, $expected) + { + if (!$result) { + $this->assertFalse($expected); + return; + } + + $this->assertEquals($result['protocol'], $expected['protocol']); + $this->assertEquals($result['protocol_version'], $expected['protocol_version']); + $this->assertEquals($result['code'], $expected['code']); + $this->assertEquals($result['reason_phrase'], $expected['reason_phrase']); + $this->assertEquals($result['body'], $expected['body']); + $this->compareHttpHeaders($result['headers'], $expected['headers']); + } + + protected function normalizeHeaders($headers) + { + $normalized = array(); + foreach ($headers as $key => $value) { + $key = strtolower($key); + if (!isset($normalized[$key])) { + $normalized[$key] = $value; + } elseif (!is_array($normalized[$key])) { + $normalized[$key] = array($value); + } else { + $normalized[$key][] = $value; + } + } + + foreach ($normalized as $key => &$value) { + if (is_array($value)) { + sort($value); + } + } + + return $normalized; + } + + public function compareHttpHeaders($result, $expected) + { + // Aggregate all headers case-insensitively + $result = $this->normalizeHeaders($result); + $expected = $this->normalizeHeaders($expected); + $this->assertEquals($result, $expected); + } +} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Message/RequestTest.php b/core/vendor/guzzlehttp/guzzle/tests/Message/RequestTest.php new file mode 100644 index 0000000000000000000000000000000000000000..a117ef2a3d192126e2031b3398c0529b1e759c54 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/tests/Message/RequestTest.php @@ -0,0 +1,120 @@ +<?php + +namespace GuzzleHttp\Tests\Message; + +use GuzzleHttp\Event\Emitter; +use GuzzleHttp\Message\Request; +use GuzzleHttp\Stream\Stream; +use GuzzleHttp\Query; + +/** + * @covers GuzzleHttp\Message\Request + */ +class RequestTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructorInitializesMessage() + { + $r = new Request('PUT', '/test', ['test' => '123'], Stream::factory('foo')); + $this->assertEquals('PUT', $r->getMethod()); + $this->assertEquals('/test', $r->getUrl()); + $this->assertEquals('123', $r->getHeader('test')); + $this->assertEquals('foo', $r->getBody()); + } + + public function testConstructorInitializesMessageWithProtocolVersion() + { + $r = new Request('GET', '', [], null, ['protocol_version' => 10]); + $this->assertEquals(10, $r->getProtocolVersion()); + } + + public function testConstructorInitializesMessageWithEmitter() + { + $e = new Emitter(); + $r = new Request('GET', '', [], null, ['emitter' => $e]); + $this->assertSame($r->getEmitter(), $e); + } + + public function testCloneIsDeep() + { + $r = new Request('GET', '/test', ['foo' => 'baz'], Stream::factory('foo')); + $r2 = clone $r; + + $this->assertNotSame($r->getEmitter(), $r2->getEmitter()); + $this->assertEquals('foo', $r2->getBody()); + + $r->getConfig()->set('test', 123); + $this->assertFalse($r2->getConfig()->hasKey('test')); + + $r->setPath('/abc'); + $this->assertEquals('/test', $r2->getPath()); + } + + public function testCastsToString() + { + $r = new Request('GET', 'http://test.com/test', ['foo' => 'baz'], Stream::factory('body')); + $s = explode("\r\n", (string) $r); + $this->assertEquals("GET /test HTTP/1.1", $s[0]); + $this->assertContains('Host: test.com', $s); + $this->assertContains('foo: baz', $s); + $this->assertContains('', $s); + $this->assertContains('body', $s); + } + + public function testSettingUrlOverridesHostHeaders() + { + $r = new Request('GET', 'http://test.com/test'); + $r->setUrl('https://baz.com/bar'); + $this->assertEquals('baz.com', $r->getHost()); + $this->assertEquals('baz.com', $r->getHeader('Host')); + $this->assertEquals('/bar', $r->getPath()); + $this->assertEquals('https', $r->getScheme()); + } + + public function testQueryIsMutable() + { + $r = new Request('GET', 'http://www.foo.com?baz=bar'); + $this->assertEquals('baz=bar', $r->getQuery()); + $this->assertInstanceOf('GuzzleHttp\Query', $r->getQuery()); + $r->getQuery()->set('hi', 'there'); + $this->assertEquals('/?baz=bar&hi=there', $r->getResource()); + } + + public function testQueryCanChange() + { + $r = new Request('GET', 'http://www.foo.com?baz=bar'); + $r->setQuery(new Query(['foo' => 'bar'])); + $this->assertEquals('foo=bar', $r->getQuery()); + } + + public function testCanChangeMethod() + { + $r = new Request('GET', 'http://www.foo.com'); + $r->setMethod('put'); + $this->assertEquals('PUT', $r->getMethod()); + } + + public function testCanChangeSchemeWithPort() + { + $r = new Request('GET', 'http://www.foo.com:80'); + $r->setScheme('https'); + $this->assertEquals('https://www.foo.com', $r->getUrl()); + } + + public function testCanChangeScheme() + { + $r = new Request('GET', 'http://www.foo.com'); + $r->setScheme('https'); + $this->assertEquals('https://www.foo.com', $r->getUrl()); + } + + public function testCanChangeHost() + { + $r = new Request('GET', 'http://www.foo.com:222'); + $r->setHost('goo'); + $this->assertEquals('http://goo:222', $r->getUrl()); + $this->assertEquals('goo:222', $r->getHeader('host')); + $r->setHost('goo:80'); + $this->assertEquals('http://goo', $r->getUrl()); + $this->assertEquals('goo', $r->getHeader('host')); + } +} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Message/ResponseTest.php b/core/vendor/guzzlehttp/guzzle/tests/Message/ResponseTest.php new file mode 100644 index 0000000000000000000000000000000000000000..6dd0a70a930ba8e197a14a82e3b3a5d7c8de5f0d --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/tests/Message/ResponseTest.php @@ -0,0 +1,103 @@ +<?php + +namespace GuzzleHttp\Tests\Message; + +use GuzzleHttp\Message\Response; +use GuzzleHttp\Stream\Stream; + +/** + * @covers GuzzleHttp\Message\Response + */ +class ResponseTest extends \PHPUnit_Framework_TestCase +{ + public function testCanProvideCustomStatusCodeAndReasonPhrase() + { + $response = new Response(999, [], null, ['reason_phrase' => 'hi!']); + $this->assertEquals(999, $response->getStatusCode()); + $this->assertEquals('hi!', $response->getReasonPhrase()); + } + + public function testConvertsToString() + { + $response = new Response(200); + $this->assertEquals("HTTP/1.1 200 OK\r\n\r\n", (string) $response); + // Add another header + $response = new Response(200, ['X-Test' => 'Guzzle']); + $this->assertEquals("HTTP/1.1 200 OK\r\nX-Test: Guzzle\r\n\r\n", (string) $response); + $response = new Response(200, ['Content-Length' => 4], Stream::factory('test')); + $this->assertEquals("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ntest", (string) $response); + } + + public function testConvertsToStringAndSeeksToByteZero() + { + $response = new Response(200); + $s = Stream::factory('foo'); + $s->read(1); + $response->setBody($s); + $this->assertEquals("HTTP/1.1 200 OK\r\n\r\nfoo", (string) $response); + } + + public function testParsesJsonResponses() + { + $json = '{"foo": "bar"}'; + $response = new Response(200, [], Stream::factory($json)); + $this->assertEquals(['foo' => 'bar'], $response->json()); + $this->assertEquals(json_decode($json), $response->json(['object' => true])); + + $response = new Response(200); + $this->assertEquals(null, $response->json()); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage Unable to parse response body into JSON: 4 + */ + public function testThrowsExceptionWhenFailsToParseJsonResponse() + { + $response = new Response(200, [], Stream::factory('{"foo": "')); + $response->json(); + } + + public function testParsesXmlResponses() + { + $response = new Response(200, [], Stream::factory('<abc><foo>bar</foo></abc>')); + $this->assertEquals('bar', (string) $response->xml()->foo); + // Always return a SimpleXMLElement from the xml method + $response = new Response(200); + $this->assertEmpty((string) $response->xml()->foo); + } + + /** + * @expectedException \GuzzleHttp\Exception\ParseException + * @expectedExceptionMessage Unable to parse response body into XML: String could not be parsed as XML + */ + public function testThrowsExceptionWhenFailsToParseXmlResponse() + { + $response = new Response(200, [], Stream::factory('<abc')); + $response->xml(); + } + + public function testHasEffectiveUrl() + { + $r = new Response(200); + $this->assertNull($r->getEffectiveUrl()); + $r->setEffectiveUrl('http://www.test.com'); + $this->assertEquals('http://www.test.com', $r->getEffectiveUrl()); + } + + public function testPreventsComplexExternalEntities() + { + $xml = '<?xml version="1.0"?><!DOCTYPE scan[<!ENTITY test SYSTEM "php://filter/read=convert.base64-encode/resource=ResponseTest.php">]><scan>&test;</scan>'; + $response = new Response(200, array(), Stream::factory($xml)); + + $oldCwd = getcwd(); + chdir(__DIR__); + try { + $xml = $response->xml(); + chdir($oldCwd); + $this->markTestIncomplete('Did not throw the expected exception! XML resolved as: ' . $xml->asXML()); + } catch (\Exception $e) { + chdir($oldCwd); + } + } +} diff --git a/core/vendor/guzzlehttp/guzzle/tests/MimetypesTest.php b/core/vendor/guzzlehttp/guzzle/tests/MimetypesTest.php new file mode 100644 index 0000000000000000000000000000000000000000..a18ec38136e866a63277ec1f98472b92c4e3130c --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/tests/MimetypesTest.php @@ -0,0 +1,31 @@ +<?php + +namespace GuzzleHttp\Tests; + +use GuzzleHttp\Mimetypes; + +/** + * @covers GuzzleHttp\Mimetypes + */ +class MimetypesTest extends \PHPUnit_Framework_TestCase +{ + public function testGetsFromExtension() + { + $this->assertEquals('text/x-php', Mimetypes::getInstance()->fromExtension('php')); + } + + public function testGetsFromFilename() + { + $this->assertEquals('text/x-php', Mimetypes::getInstance()->fromFilename(__FILE__)); + } + + public function testGetsFromCaseInsensitiveFilename() + { + $this->assertEquals('text/x-php', Mimetypes::getInstance()->fromFilename(strtoupper(__FILE__))); + } + + public function testReturnsNullWhenNoMatchFound() + { + $this->assertNull(Mimetypes::getInstance()->fromExtension('foobar')); + } +} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Post/MultipartBodyTest.php b/core/vendor/guzzlehttp/guzzle/tests/Post/MultipartBodyTest.php new file mode 100644 index 0000000000000000000000000000000000000000..32ce7d18e14a4db27b2f29ffa4ce07461277ba6e --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/tests/Post/MultipartBodyTest.php @@ -0,0 +1,136 @@ +<?php + +namespace GuzzleHttp\Tests\Post; + +use GuzzleHttp\Post\MultipartBody; +use GuzzleHttp\Post\PostFile; + +/** + * @covers GuzzleHttp\Post\MultipartBody + */ +class MultipartBodyTest extends \PHPUnit_Framework_TestCase +{ + protected function getTestBody() + { + return new MultipartBody(['foo' => 'bar'], [ + new PostFile('foo', 'abc', 'foo.txt') + ], 'abcdef'); + } + + public function testConstructorAddsFieldsAndFiles() + { + $b = $this->getTestBody(); + $this->assertEquals('abcdef', $b->getBoundary()); + $c = (string) $b; + $this->assertContains("--abcdef\r\nContent-Disposition: form-data; name=\"foo\"\r\n\r\nbar\r\n", $c); + $this->assertContains("--abcdef\r\nContent-Disposition: form-data; filename=\"foo.txt\"; name=\"foo\"\r\n" + . "Content-Type: text/plain\r\n\r\nabc\r\n--abcdef--", $c); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testConstructorValidatesFiles() + { + new MultipartBody([], ['bar']); + } + + public function testConstructorCanCreateBoundary() + { + $b = new MultipartBody(); + $this->assertNotNull($b->getBoundary()); + } + + public function testWrapsStreamMethods() + { + $b = $this->getTestBody(); + $this->assertFalse($b->write('foo')); + $this->assertFalse($b->isWritable()); + $this->assertTrue($b->isReadable()); + $this->assertTrue($b->isSeekable()); + $this->assertEquals(0, $b->tell()); + } + + public function testCanDetachFieldsAndFiles() + { + $b = $this->getTestBody(); + $b->detach(); + $b->close(); + $this->assertEquals('', (string) $b); + } + + public function testCanOnlySeekTo0() + { + $b = new MultipartBody(); + $this->assertFalse($b->seek(10)); + } + + public function testIsSeekableReturnsTrueIfAllAreSeekable() + { + $s = $this->getMockBuilder('GuzzleHttp\Stream\StreamInterface') + ->setMethods(['isSeekable']) + ->getMockForAbstractClass(); + $s->expects($this->once()) + ->method('isSeekable') + ->will($this->returnValue(false)); + $p = new PostFile('foo', $s, 'foo.php'); + $b = new MultipartBody([], [$p]); + $this->assertFalse($b->isSeekable()); + $this->assertFalse($b->seek(10)); + } + + /** + * @expectedException \RuntimeException + */ + public function testThrowsExceptionWhenStreamFailsToRewind() + { + $s = $this->getMockBuilder('GuzzleHttp\Stream\StreamInterface') + ->setMethods(['seek', 'isSeekable']) + ->getMockForAbstractClass(); + $s->expects($this->once()) + ->method('isSeekable') + ->will($this->returnValue(true)); + $s->expects($this->once()) + ->method('seek') + ->will($this->returnValue(false)); + $b = new MultipartBody([], [new PostFile('foo', $s, 'foo.php')]); + $b->seek(0); + } + + public function testGetContentsCanCap() + { + $b = $this->getTestBody(); + $c = (string) $b; + $b->seek(0); + $this->assertSame(substr($c, 0, 10), $b->getContents(10)); + } + + public function testReadsFromBuffer() + { + $b = $this->getTestBody(); + $c = $b->read(1); + $c .= $b->read(1); + $c .= $b->read(1); + $c .= $b->read(1); + $c .= $b->read(1); + $this->assertEquals('--abc', $c); + } + + public function testCalculatesSize() + { + $b = $this->getTestBody(); + $this->assertEquals(strlen($b), $b->getSize()); + } + + public function testCalculatesSizeAndReturnsNullForUnknown() + { + $s = $this->getMockBuilder('GuzzleHttp\Stream\StreamInterface') + ->setMethods(['getSize']) + ->getMockForAbstractClass(); + $s->expects($this->once()) + ->method('getSize') + ->will($this->returnValue(null)); + $b = new MultipartBody([], [new PostFile('foo', $s, 'foo.php')]); + $this->assertNull($b->getSize()); + } +} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Post/PostBodyTest.php b/core/vendor/guzzlehttp/guzzle/tests/Post/PostBodyTest.php new file mode 100644 index 0000000000000000000000000000000000000000..bbbd6d0196f7f1f154fee17f72c78ac092e0285c --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/tests/Post/PostBodyTest.php @@ -0,0 +1,140 @@ +<?php + +namespace GuzzleHttp\Tests\Post; + +use GuzzleHttp\Post\PostBody; +use GuzzleHttp\Message\Request; +use GuzzleHttp\Post\PostFile; +use GuzzleHttp\Query; + +/** + * @covers GuzzleHttp\Post\PostBody + */ +class PostBodyTest extends \PHPUnit_Framework_TestCase +{ + public function testWrapsBasicStreamFunctionality() + { + $b = new PostBody(); + $this->assertTrue($b->isSeekable()); + $this->assertTrue($b->isReadable()); + $this->assertFalse($b->isWritable()); + $this->assertFalse($b->write('foo')); + } + + public function testApplyingWithNothingDoesNothing() + { + $b = new PostBody(); + $m = new Request('POST', '/'); + $b->applyRequestHeaders($m); + $this->assertFalse($m->hasHeader('Content-Length')); + $this->assertFalse($m->hasHeader('Content-Type')); + } + + public function testCanForceMultipartUploadsWhenApplying() + { + $b = new PostBody(); + $b->forceMultipartUpload(true); + $m = new Request('POST', '/'); + $b->applyRequestHeaders($m); + $this->assertContains('multipart/form-data', (string) $m->getHeader('Content-Type')); + } + + public function testApplyingWithFilesAddsMultipartUpload() + { + $b = new PostBody(); + $p = new PostFile('foo', fopen(__FILE__, 'r')); + $b->addFile($p); + $this->assertEquals([$p], $b->getFiles()); + $this->assertNull($b->getFile('missing')); + $this->assertSame($p, $b->getFile('foo')); + $m = new Request('POST', '/'); + $b->applyRequestHeaders($m); + $this->assertContains('multipart/form-data', (string) $m->getHeader('Content-Type')); + $this->assertTrue($m->hasHeader('Content-Length')); + } + + public function testApplyingWithFieldsAddsMultipartUpload() + { + $b = new PostBody(); + $b->setField('foo', 'bar'); + $this->assertEquals(['foo' => 'bar'], $b->getFields()); + $m = new Request('POST', '/'); + $b->applyRequestHeaders($m); + $this->assertContains('application/x-www-form', (string) $m->getHeader('Content-Type')); + $this->assertTrue($m->hasHeader('Content-Length')); + } + + public function testCountProvidesFieldsAndFiles() + { + $b = new PostBody(); + $b->setField('foo', 'bar'); + $b->addFile(new PostFile('foo', fopen(__FILE__, 'r'))); + $this->assertEquals(2, count($b)); + $b->clearFiles(); + $b->removeField('foo'); + $this->assertEquals(0, count($b)); + $this->assertEquals([], $b->getFiles()); + $this->assertEquals([], $b->getFields()); + } + + public function testHasFields() + { + $b = new PostBody(); + $b->setField('foo', 'bar'); + $b->setField('baz', '123'); + $this->assertEquals('bar', $b->getField('foo')); + $this->assertEquals('123', $b->getField('baz')); + $this->assertNull($b->getField('ahh')); + $this->assertTrue($b->hasField('foo')); + $this->assertFalse($b->hasField('test')); + $b->replaceFields(['abc' => '123']); + $this->assertFalse($b->hasField('foo')); + $this->assertTrue($b->hasField('abc')); + } + + public function testConvertsFieldsToQueryStyleBody() + { + $b = new PostBody(); + $b->setField('foo', 'bar'); + $b->setField('baz', '123'); + $this->assertEquals('foo=bar&baz=123', $b); + $this->assertEquals(15, $b->getSize()); + $b->seek(0); + $this->assertEquals('foo=bar&baz=123', $b->getContents()); + $b->seek(0); + $this->assertEquals('foo=bar&baz=123', $b->read(1000)); + $this->assertEquals(15, $b->tell()); + $this->assertTrue($b->eof()); + } + + public function testCanSpecifyQueryAggregator() + { + $b = new PostBody(); + $b->setField('foo', ['baz', 'bar']); + $this->assertEquals('foo%5B0%5D=baz&foo%5B1%5D=bar', (string) $b); + $b = new PostBody(); + $b->setField('foo', ['baz', 'bar']); + $agg = Query::duplicateAggregator(); + $b->setAggregator($agg); + $this->assertEquals('foo=baz&foo=bar', (string) $b); + } + + public function testDetachesAndCloses() + { + $b = new PostBody(); + $b->setField('foo', 'bar'); + $b->detach(); + $this->assertTrue($b->close()); + $this->assertEquals('', $b->read(10)); + } + + public function testCreatesMultipartUploadWithMultiFields() + { + $b = new PostBody(); + $b->setField('testing', ['baz', 'bar']); + $b->addFile(new PostFile('foo', fopen(__FILE__, 'r'))); + $s = (string) $b; + $this->assertContains(file_get_contents(__FILE__), $s); + $this->assertContains('testing=bar', $s); + } +} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Post/PostFileTest.php b/core/vendor/guzzlehttp/guzzle/tests/Post/PostFileTest.php new file mode 100644 index 0000000000000000000000000000000000000000..a233b3084d7d718683c5e55fd12c3ed6305812c9 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/tests/Post/PostFileTest.php @@ -0,0 +1,68 @@ +<?php + +namespace GuzzleHttp\Tests\Post; + +use GuzzleHttp\Post\PostFile; +use GuzzleHttp\Stream\Stream; + +/** + * @covers GuzzleHttp\Post\PostFile + */ +class PostFileTest extends \PHPUnit_Framework_TestCase +{ + public function testCreatesFromString() + { + $p = new PostFile('foo', 'hi', '/path/to/test.php'); + $this->assertInstanceOf('GuzzleHttp\Post\PostFileInterface', $p); + $this->assertEquals('hi', $p->getContent()); + $this->assertEquals('foo', $p->getName()); + $this->assertEquals('/path/to/test.php', $p->getFilename()); + $this->assertEquals( + 'form-data; filename="test.php"; name="foo"', + $p->getHeaders()['Content-Disposition'] + ); + } + + public function testGetsFilenameFromMetadata() + { + $p = new PostFile('foo', fopen(__FILE__, 'r')); + $this->assertEquals(__FILE__, $p->getFilename()); + } + + public function testDefaultsToNameWhenNoFilenameExists() + { + $p = new PostFile('foo', 'bar'); + $this->assertEquals('foo', $p->getFilename()); + } + + public function testCreatesFromMultipartFormData() + { + $mp = $this->getMockBuilder('GuzzleHttp\Post\MultipartBody') + ->setMethods(['getBoundary']) + ->disableOriginalConstructor() + ->getMock(); + $mp->expects($this->once()) + ->method('getBoundary') + ->will($this->returnValue('baz')); + + $p = new PostFile('foo', $mp); + $this->assertEquals( + 'form-data; name="foo"', + $p->getHeaders()['Content-Disposition'] + ); + $this->assertEquals( + 'multipart/form-data; boundary=baz', + $p->getHeaders()['Content-Type'] + ); + } + + public function testCanAddHeaders() + { + $p = new PostFile('foo', Stream::factory('hi'), 'test.php', [ + 'X-Foo' => '123', + 'Content-Disposition' => 'bar' + ]); + $this->assertEquals('bar', $p->getHeaders()['Content-Disposition']); + $this->assertEquals('123', $p->getHeaders()['X-Foo']); + } +} diff --git a/core/vendor/guzzlehttp/guzzle/tests/QueryTest.php b/core/vendor/guzzlehttp/guzzle/tests/QueryTest.php new file mode 100644 index 0000000000000000000000000000000000000000..6d0dde948d262d4d485b5e9517077404864efb37 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/tests/QueryTest.php @@ -0,0 +1,225 @@ +<?php + +namespace GuzzleHttp\Tests; + +use GuzzleHttp\Query; + +class QueryTest extends \PHPUnit_Framework_TestCase +{ + public function testCanCastToString() + { + $q = new Query(['foo' => 'baz', 'bar' => 'bam boozle']); + $this->assertEquals('foo=baz&bar=bam%20boozle', (string) $q); + } + + public function testCanDisableUrlEncoding() + { + $q = new Query(['bar' => 'bam boozle']); + $q->setEncodingType(false); + $this->assertEquals('bar=bam boozle', (string) $q); + } + + public function testCanSpecifyRfc1783UrlEncodingType() + { + $q = new Query(['bar abc' => 'bam boozle']); + $q->setEncodingType(Query::RFC1738); + $this->assertEquals('bar+abc=bam+boozle', (string) $q); + } + + public function testCanSpecifyRfc3986UrlEncodingType() + { + $q = new Query(['bar abc' => 'bam boozle', 'ሴ' => 'hi']); + $q->setEncodingType(Query::RFC3986); + $this->assertEquals('bar%20abc=bam%20boozle&%E1%88%B4=hi', (string) $q); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testValidatesEncodingType() + { + (new Query(['bar' => 'bam boozle']))->setEncodingType('foo'); + } + + public function testAggregatesMultipleValues() + { + $q = new Query(['foo' => ['bar', 'baz']]); + $this->assertEquals('foo%5B0%5D=bar&foo%5B1%5D=baz', (string) $q); + } + + public function testCanSetAggregator() + { + $q = new Query(['foo' => ['bar', 'baz']]); + $q->setAggregator(function (array $data) { + return ['foo' => ['barANDbaz']]; + }); + $this->assertEquals('foo=barANDbaz', (string) $q); + } + + public function testAllowsMultipleValuesPerKey() + { + $q = new Query(); + $q->add('facet', 'size'); + $q->add('facet', 'width'); + $q->add('facet.field', 'foo'); + // Use the duplicate aggregator + $q->setAggregator($q::duplicateAggregator()); + $this->assertEquals('facet=size&facet=width&facet.field=foo', (string) $q); + } + + public function parseQueryProvider() + { + return array( + // Ensure that multiple query string values are allowed per value + array('q=a&q=b', array('q' => array('a', 'b'))), + // Ensure that PHP array style query string values are parsed + array('q[]=a&q[]=b', array('q' => array('a', 'b'))), + // Ensure that a single PHP array style query string value is parsed into an array + array('q[]=a', array('q' => array('a'))), + // Ensure that decimals are allowed in query strings + array('q.a=a&q.b=b', array( + 'q.a' => 'a', + 'q.b' => 'b' + )), + // Ensure that query string values are percent decoded + array('q%20a=a%20b', array('q a' => 'a b')), + // Ensure null values can be added + array('q&a', array('q' => null, 'a' => null)), + ); + } + + /** + * @dataProvider parseQueryProvider + */ + public function testParsesQueries($query, $data) + { + $query = Query::fromString($query); + $this->assertEquals($data, $query->toArray()); + } + + public function testProperlyDealsWithDuplicateQueryValues() + { + $query = Query::fromString('foo=a&foo=b&?µ=c'); + $this->assertEquals(array('a', 'b'), $query->get('foo')); + $this->assertEquals('c', $query->get('?µ')); + } + + public function testAllowsNullQueryValues() + { + $query = Query::fromString('foo'); + $this->assertEquals('foo', (string) $query); + $query->set('foo', null); + $this->assertEquals('foo', (string) $query); + } + + public function testAllowsFalsyQueryValues() + { + $query = Query::fromString('0'); + $this->assertEquals('0', (string) $query); + $query->set('0', ''); + $this->assertSame('0=', (string) $query); + } + + public function testConvertsPlusSymbolsToSpaces() + { + $query = Query::fromString('var=foo+bar'); + $this->assertEquals('foo bar', $query->get('var')); + } + + public function testFromStringDoesntMangleZeroes() + { + $query = Query::fromString('var=0'); + $this->assertSame('0', $query->get('var')); + } + + public function testAllowsZeroValues() + { + $query = new Query(array( + 'foo' => 0, + 'baz' => '0', + 'bar' => null, + 'boo' => false + )); + $this->assertEquals('foo=0&baz=0&bar&boo=', (string) $query); + } + + public function testFromStringDoesntStripTrailingEquals() + { + $query = Query::fromString('data=mF0b3IiLCJUZWFtIERldiJdfX0='); + $this->assertEquals('mF0b3IiLCJUZWFtIERldiJdfX0=', $query->get('data')); + } + + public function testGuessesIfDuplicateAggregatorShouldBeUsed() + { + $query = Query::fromString('test=a&test=b'); + $this->assertEquals('test=a&test=b', (string) $query); + } + + public function testGuessesIfDuplicateAggregatorShouldBeUsedAndChecksForPhpStyle() + { + $query = Query::fromString('test[]=a&test[]=b'); + $this->assertEquals('test%5B0%5D=a&test%5B1%5D=b', (string) $query); + } + + public function testCastingToAndCreatingFromStringWithEmptyValuesIsFast() + { + $this->assertEquals('', (string) Query::fromString('')); + } + + private $encodeData = [ + 't' => [ + 'v1' => ['a', '1'], + 'v2' => 'b', + 'v3' => ['v4' => 'c', 'v5' => 'd'] + ] + ]; + + public function testEncodesDuplicateAggregator() + { + $agg = Query::duplicateAggregator(); + $result = $agg($this->encodeData); + $this->assertEquals(array( + 't[v1]' => ['a', '1'], + 't[v2]' => ['b'], + 't[v3][v4]' => ['c'], + 't[v3][v5]' => ['d'], + ), $result); + } + + public function testDuplicateEncodesNoNumericIndices() + { + $agg = Query::duplicateAggregator(); + $result = $agg($this->encodeData); + $this->assertEquals(array( + 't[v1]' => ['a', '1'], + 't[v2]' => ['b'], + 't[v3][v4]' => ['c'], + 't[v3][v5]' => ['d'], + ), $result); + } + + public function testEncodesPhpAggregator() + { + $agg = Query::phpAggregator(); + $result = $agg($this->encodeData); + $this->assertEquals(array( + 't[v1][0]' => ['a'], + 't[v1][1]' => ['1'], + 't[v2]' => ['b'], + 't[v3][v4]' => ['c'], + 't[v3][v5]' => ['d'], + ), $result); + } + + public function testPhpEncodesNoNumericIndices() + { + $agg = Query::phpAggregator(false); + $result = $agg($this->encodeData); + $this->assertEquals(array( + 't[v1][]' => ['a', '1'], + 't[v2]' => ['b'], + 't[v3][v4]' => ['c'], + 't[v3][v5]' => ['d'], + ), $result); + } +} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Server.php b/core/vendor/guzzlehttp/guzzle/tests/Server.php new file mode 100644 index 0000000000000000000000000000000000000000..6b33dadb1f62456f9872349f54ed75e3b9363a6e --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/tests/Server.php @@ -0,0 +1,177 @@ +<?php + +namespace GuzzleHttp\Tests; + +use GuzzleHttp\Message\MessageFactory; +use GuzzleHttp\Message\ResponseInterface; +use GuzzleHttp\Client; + +/** + * The Server class is used to control a scripted webserver using node.js that + * will respond to HTTP requests with queued responses. + * + * Queued responses will be served to requests using a FIFO order. All requests + * received by the server are stored on the node.js server and can be retrieved + * by calling {@see Server::received()}. + * + * Mock responses that don't require data to be transmitted over HTTP a great + * for testing. Mock response, however, cannot test the actual sending of an + * HTTP request using cURL. This test server allows the simulation of any + * number of HTTP request response transactions to test the actual sending of + * requests over the wire without having to leave an internal network. + */ +class Server +{ + const REQUEST_DELIMITER = "\n----[request]\n"; + + /** @var Client */ + private static $client; + + public static $started; + public static $url = 'http://127.0.0.1:8124/'; + public static $port = 8124; + + /** + * Flush the received requests from the server + * @throws \RuntimeException + */ + public static function flush() + { + self::$started && self::$client->delete('guzzle-server/requests'); + } + + /** + * Queue an array of responses or a single response on the server. + * + * Any currently queued responses will be overwritten. Subsequent requests + * on the server will return queued responses in FIFO order. + * + * @param array|ResponseInterface $responses A single or array of Responses + * to queue. + * @throws \Exception + */ + public static function enqueue($responses) + { + static $factory; + if (!$factory) { + $factory = new MessageFactory(); + } + + self::start(); + + $data = []; + foreach ((array) $responses as $response) { + + // Create the response object from a string + if (is_string($response)) { + $response = $factory->fromMessage($response); + } elseif (!($response instanceof ResponseInterface)) { + throw new \Exception('Responses must be strings or Responses'); + } + + $headers = array_map(function ($h) { + return implode(' ,', $h); + }, $response->getHeaders()); + + $data[] = [ + 'statusCode' => $response->getStatusCode(), + 'reasonPhrase' => $response->getReasonPhrase(), + 'headers' => $headers, + 'body' => (string) $response->getBody() + ]; + } + + self::getClient()->put('guzzle-server/responses', [ + 'body' => json_encode($data) + ]); + } + + /** + * Get all of the received requests + * + * @param bool $hydrate Set to TRUE to turn the messages into + * actual {@see RequestInterface} objects. If $hydrate is FALSE, + * requests will be returned as strings. + * + * @return array + * @throws \RuntimeException + */ + public static function received($hydrate = false) + { + if (!self::$started) { + return []; + } + + $response = self::getClient()->get('guzzle-server/requests'); + $data = array_filter(explode(self::REQUEST_DELIMITER, (string) $response->getBody())); + if ($hydrate) { + $factory = new MessageFactory(); + $data = array_map(function($message) use ($factory) { + return $factory->fromMessage($message); + }, $data); + } + + return $data; + } + + /** + * Stop running the node.js server + */ + public static function stop() + { + if (self::$started) { + self::getClient()->delete('guzzle-server'); + } + + self::$started = false; + } + + public static function wait($maxTries = 3) + { + $tries = 0; + while (!self::isListening() && ++$tries < $maxTries) { + usleep(100000); + } + + if (!self::isListening()) { + throw new \RuntimeException('Unable to contact node.js server'); + } + } + + private static function start() + { + if (self::$started){ + return; + } + + if (!self::isListening()) { + exec('node ' . __DIR__ . \DIRECTORY_SEPARATOR . 'server.js ' + . self::$port . ' >> /tmp/server.log 2>&1 &'); + self::wait(); + } + + self::$started = true; + } + + private static function isListening() + { + try { + self::getClient()->get('guzzle-server/perf', [ + 'connect_timeout' => 5, + 'timeout' => 5 + ]); + return true; + } catch (\Exception $e) { + return false; + } + } + + private static function getClient() + { + if (!self::$client) { + self::$client = new Client(['base_url' => self::$url]); + } + + return self::$client; + } +} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Subscriber/CookieTest.php b/core/vendor/guzzlehttp/guzzle/tests/Subscriber/CookieTest.php new file mode 100644 index 0000000000000000000000000000000000000000..e1897424e8f31320623a211550c7964371f44e1d --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/tests/Subscriber/CookieTest.php @@ -0,0 +1,75 @@ +<?php + +namespace GuzzleHttp\Tests\Subscriber; + +use GuzzleHttp\Adapter\Transaction; +use GuzzleHttp\Client; +use GuzzleHttp\Event\CompleteEvent; +use GuzzleHttp\Subscriber\Cookie; +use GuzzleHttp\Cookie\CookieJar; +use GuzzleHttp\Message\Response; +use GuzzleHttp\Message\Request; +use GuzzleHttp\Subscriber\History; +use GuzzleHttp\Subscriber\Mock; + +/** + * @covers GuzzleHttp\Subscriber\Cookie + */ +class CookieTest extends \PHPUnit_Framework_TestCase +{ + public function testExtractsAndStoresCookies() + { + $request = new Request('GET', '/'); + $response = new Response(200); + $mock = $this->getMockBuilder('GuzzleHttp\Cookie\CookieJar') + ->setMethods(array('extractCookies')) + ->getMock(); + + $mock->expects($this->exactly(1)) + ->method('extractCookies') + ->with($request, $response); + + $plugin = new Cookie($mock); + $t = new Transaction(new Client(), $request); + $t->setResponse($response); + $plugin->onComplete(new CompleteEvent($t)); + } + + public function testProvidesCookieJar() + { + $jar = new CookieJar(); + $plugin = new Cookie($jar); + $this->assertSame($jar, $plugin->getCookieJar()); + } + + public function testCookiesAreExtractedFromRedirectResponses() + { + $jar = new CookieJar(); + $cookie = new Cookie($jar); + $history = new History(); + $mock = new Mock([ + "HTTP/1.1 302 Moved Temporarily\r\n" . + "Set-Cookie: test=583551; Domain=www.foo.com; Expires=Wednesday, 23-Mar-2050 19:49:45 GMT; Path=/\r\n" . + "Location: /redirect\r\n\r\n", + "HTTP/1.1 200 OK\r\n" . + "Content-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\n" . + "Content-Length: 0\r\n\r\n" + ]); + $client = new Client(['base_url' => 'http://www.foo.com']); + $client->getEmitter()->attach($cookie); + $client->getEmitter()->attach($mock); + $client->getEmitter()->attach($history); + + $client->get(); + $request = $client->createRequest('GET', '/'); + $client->send($request); + + $this->assertEquals('test=583551', $request->getHeader('Cookie')); + $requests = $history->getRequests(); + // Confirm subsequent requests have the cookie. + $this->assertEquals('test=583551', $requests[2]->getHeader('Cookie')); + // Confirm the redirected request has the cookie. + $this->assertEquals('test=583551', $requests[1]->getHeader('Cookie')); + } +} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Subscriber/HistoryTest.php b/core/vendor/guzzlehttp/guzzle/tests/Subscriber/HistoryTest.php new file mode 100644 index 0000000000000000000000000000000000000000..d5f1dc84c5aded0965c1b78301790bdc477005df --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/tests/Subscriber/HistoryTest.php @@ -0,0 +1,88 @@ +<?php + +namespace GuzzleHttp\Tests\Subscriber; + +use GuzzleHttp\Adapter\Transaction; +use GuzzleHttp\Client; +use GuzzleHttp\Event\CompleteEvent; +use GuzzleHttp\Event\ErrorEvent; +use GuzzleHttp\Exception\RequestException; +use GuzzleHttp\Message\Request; +use GuzzleHttp\Message\Response; +use GuzzleHttp\Subscriber\History; +use GuzzleHttp\Subscriber\Mock; +use GuzzleHttp\Stream\Stream; + +/** + * @covers GuzzleHttp\Subscriber\History + */ +class HistoryTest extends \PHPUnit_Framework_TestCase +{ + public function testAddsForErrorEvent() + { + $request = new Request('GET', '/'); + $response = new Response(400); + $t = new Transaction(new Client(), $request); + $t->setResponse($response); + $e = new RequestException('foo', $request, $response); + $ev = new ErrorEvent($t, $e); + $h = new History(2); + $h->onError($ev); + $this->assertEquals([$request], $h->getRequests()); + } + + public function testMaintainsLimitValue() + { + $request = new Request('GET', '/'); + $response = new Response(200); + $t = new Transaction(new Client(), $request); + $t->setResponse($response); + $ev = new CompleteEvent($t); + $h = new History(2); + $h->onComplete($ev); + $h->onComplete($ev); + $h->onComplete($ev); + $this->assertEquals(2, count($h)); + $this->assertSame($request, $h->getLastRequest()); + $this->assertSame($response, $h->getLastResponse()); + foreach ($h as $trans) { + $this->assertInstanceOf('GuzzleHttp\Message\RequestInterface', $trans['request']); + $this->assertInstanceOf('GuzzleHttp\Message\ResponseInterface', $trans['response']); + } + return $h; + } + + /** + * @depends testMaintainsLimitValue + */ + public function testClearsHistory($h) + { + $this->assertEquals(2, count($h)); + $h->clear(); + $this->assertEquals(0, count($h)); + } + + public function testCanCastToString() + { + $client = new Client(['base_url' => 'http://localhost/']); + $h = new History(); + $client->getEmitter()->attach($h); + + $mock = new Mock(array( + new Response(301, array('Location' => '/redirect1', 'Content-Length' => 0)), + new Response(307, array('Location' => '/redirect2', 'Content-Length' => 0)), + new Response(200, array('Content-Length' => '2'), Stream::factory('HI')) + )); + + $client->getEmitter()->attach($mock); + $request = $client->createRequest('GET', '/'); + $client->send($request); + $this->assertEquals(3, count($h)); + + $h = str_replace("\r", '', $h); + $this->assertContains("> GET / HTTP/1.1\nHost: localhost\nUser-Agent:", $h); + $this->assertContains("< HTTP/1.1 301 Moved Permanently\nLocation: /redirect1", $h); + $this->assertContains("< HTTP/1.1 307 Temporary Redirect\nLocation: /redirect2", $h); + $this->assertContains("< HTTP/1.1 200 OK\nContent-Length: 2\n\nHI", $h); + } +} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Subscriber/HttpErrorTest.php b/core/vendor/guzzlehttp/guzzle/tests/Subscriber/HttpErrorTest.php new file mode 100644 index 0000000000000000000000000000000000000000..12a1aef41e070a2351fbbf090609bf3f02e35925 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/tests/Subscriber/HttpErrorTest.php @@ -0,0 +1,61 @@ +<?php + +namespace GuzzleHttp\Tests\Message; + +use GuzzleHttp\Event\CompleteEvent; +use GuzzleHttp\Message\Response; +use GuzzleHttp\Subscriber\HttpError; +use GuzzleHttp\Adapter\Transaction; +use GuzzleHttp\Message\Request; +use GuzzleHttp\Client; +use GuzzleHttp\Subscriber\Mock; + +/** + * @covers GuzzleHttp\Subscriber\HttpError + */ +class HttpErrorTest extends \PHPUnit_Framework_TestCase +{ + public function testIgnoreSuccessfulRequests() + { + $event = $this->getEvent(); + $event->intercept(new Response(200)); + (new HttpError())->onComplete($event); + } + + /** + * @expectedException \GuzzleHttp\Exception\ClientException + */ + public function testThrowsClientExceptionOnFailure() + { + $event = $this->getEvent(); + $event->intercept(new Response(403)); + (new HttpError())->onComplete($event); + } + + /** + * @expectedException \GuzzleHttp\Exception\ServerException + */ + public function testThrowsServerExceptionOnFailure() + { + $event = $this->getEvent(); + $event->intercept(new Response(500)); + (new HttpError())->onComplete($event); + } + + private function getEvent() + { + return new CompleteEvent(new Transaction(new Client(), new Request('PUT', '/'))); + } + + /** + * @expectedException \GuzzleHttp\Exception\ClientException + */ + public function testFullTransaction() + { + $client = new Client(); + $client->getEmitter()->attach(new Mock([ + new Response(403) + ])); + $client->get('http://httpbin.org'); + } +} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Subscriber/MockTest.php b/core/vendor/guzzlehttp/guzzle/tests/Subscriber/MockTest.php new file mode 100644 index 0000000000000000000000000000000000000000..c4d51aff7669716ba83cba3435fb878c43c95e8e --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/tests/Subscriber/MockTest.php @@ -0,0 +1,108 @@ +<?php + +namespace GuzzleHttp\Tests\Subscriber; + +use GuzzleHttp\Adapter\Transaction; +use GuzzleHttp\Event\BeforeEvent; +use GuzzleHttp\Exception\RequestException; +use GuzzleHttp\Subscriber\Mock; +use GuzzleHttp\Client; +use GuzzleHttp\Message\Request; +use GuzzleHttp\Message\Response; +use GuzzleHttp\Message\MessageFactory; +use GuzzleHttp\Stream\Stream; + +/** + * @covers GuzzleHttp\Subscriber\Mock + */ +class MockTest extends \PHPUnit_Framework_TestCase +{ + public function testDescribesSubscribedEvents() + { + $mock = new Mock(); + $this->assertInternalType('array', $mock->getEvents()); + } + + public function testIsCountable() + { + $plugin = new Mock(); + $plugin->addResponse((new MessageFactory())->fromMessage("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n")); + $this->assertEquals(1, count($plugin)); + } + + public function testCanClearQueue() + { + $plugin = new Mock(); + $plugin->addResponse((new MessageFactory())->fromMessage("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n")); + $plugin->clearQueue(); + $this->assertEquals(0, count($plugin)); + } + + public function testRetrievesResponsesFromFiles() + { + $tmp = tempnam('/tmp', 'tfile'); + file_put_contents($tmp, "HTTP/1.1 201 OK\r\nContent-Length: 0\r\n\r\n"); + $plugin = new Mock(); + $plugin->addResponse($tmp); + unlink($tmp); + $this->assertEquals(1, count($plugin)); + $q = $this->readAttribute($plugin, 'queue'); + $this->assertEquals(201, $q[0]->getStatusCode()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testThrowsExceptionWhenInvalidResponse() + { + (new Mock())->addResponse(false); + } + + public function testAddsMockResponseToRequestFromClient() + { + $response = new Response(200); + $t = new Transaction(new Client(), new Request('GET', '/')); + $m = new Mock([$response]); + $ev = new BeforeEvent($t); + $m->onBefore($ev); + $this->assertSame($response, $t->getResponse()); + } + + /** + * @expectedException \OutOfBoundsException + */ + public function testUpdateThrowsExceptionWhenEmpty() + { + $p = new Mock(); + $ev = new BeforeEvent(new Transaction(new Client(), new Request('GET', '/'))); + $p->onBefore($ev); + } + + public function testReadsBodiesFromMockedRequests() + { + $m = new Mock([new Response(200)]); + $client = new Client(['base_url' => 'http://test.com']); + $client->getEmitter()->attach($m); + $body = Stream::factory('foo'); + $client->put('/', ['body' => $body]); + $this->assertEquals(3, $body->tell()); + } + + public function testCanMockBadRequestExceptions() + { + $client = new Client(['base_url' => 'http://test.com']); + $request = $client->createRequest('GET', '/'); + $ex = new RequestException('foo', $request); + $mock = new Mock([$ex]); + $this->assertCount(1, $mock); + $request->getEmitter()->attach($mock); + + try { + $client->send($request); + $this->fail('Did not dequeue an exception'); + } catch (RequestException $e) { + $this->assertSame($e, $ex); + $this->assertSame($request, $ex->getRequest()); + } + } +} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Subscriber/PrepareTest.php b/core/vendor/guzzlehttp/guzzle/tests/Subscriber/PrepareTest.php new file mode 100644 index 0000000000000000000000000000000000000000..bce7d53cdc2e4fbb3edb75c82aefc0b367d93f0d --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/tests/Subscriber/PrepareTest.php @@ -0,0 +1,198 @@ +<?php + +namespace GuzzleHttp\Tests\Message; + +use GuzzleHttp\Adapter\Transaction; +use GuzzleHttp\Client; +use GuzzleHttp\Event\BeforeEvent; +use GuzzleHttp\Subscriber\Prepare; +use GuzzleHttp\Message\Request; +use GuzzleHttp\Stream\NoSeekStream; +use GuzzleHttp\Stream\Stream; + +/** + * @covers GuzzleHttp\Subscriber\Prepare + */ +class PrepareTest extends \PHPUnit_Framework_TestCase +{ + public function testIgnoresRequestsWithNoBody() + { + $s = new Prepare(); + $t = $this->getTrans(); + $s->onBefore(new BeforeEvent($t)); + $this->assertFalse($t->getRequest()->hasHeader('Expect')); + } + + public function testAppliesPostBody() + { + $s = new Prepare(); + $t = $this->getTrans(); + $p = $this->getMockBuilder('GuzzleHttp\Post\PostBody') + ->setMethods(['applyRequestHeaders']) + ->getMockForAbstractClass(); + $p->expects($this->once()) + ->method('applyRequestHeaders'); + $t->getRequest()->setBody($p); + $s->onBefore(new BeforeEvent($t)); + } + + public function testAddsExpectHeaderWithTrue() + { + $s = new Prepare(); + $t = $this->getTrans(); + $t->getRequest()->getConfig()->set('expect', true); + $t->getRequest()->setBody(Stream::factory('foo')); + $s->onBefore(new BeforeEvent($t)); + $this->assertEquals('100-Continue', $t->getRequest()->getHeader('Expect')); + } + + public function testAddsExpectHeaderBySize() + { + $s = new Prepare(); + $t = $this->getTrans(); + $t->getRequest()->getConfig()->set('expect', 2); + $t->getRequest()->setBody(Stream::factory('foo')); + $s->onBefore(new BeforeEvent($t)); + $this->assertTrue($t->getRequest()->hasHeader('Expect')); + } + + public function testDoesNotModifyExpectHeaderIfPresent() + { + $s = new Prepare(); + $t = $this->getTrans(); + $t->getRequest()->setHeader('Expect', 'foo'); + $t->getRequest()->setBody(Stream::factory('foo')); + $s->onBefore(new BeforeEvent($t)); + $this->assertEquals('foo', $t->getRequest()->getHeader('Expect')); + } + + public function testDoesAddExpectHeaderWhenSetToFalse() + { + $s = new Prepare(); + $t = $this->getTrans(); + $t->getRequest()->getConfig()->set('expect', false); + $t->getRequest()->setBody(Stream::factory('foo')); + $s->onBefore(new BeforeEvent($t)); + $this->assertFalse($t->getRequest()->hasHeader('Expect')); + } + + public function testDoesNotAddExpectHeaderBySize() + { + $s = new Prepare(); + $t = $this->getTrans(); + $t->getRequest()->getConfig()->set('expect', 10); + $t->getRequest()->setBody(Stream::factory('foo')); + $s->onBefore(new BeforeEvent($t)); + $this->assertFalse($t->getRequest()->hasHeader('Expect')); + } + + public function testAddsExpectHeaderForNonSeekable() + { + $s = new Prepare(); + $t = $this->getTrans(); + $t->getRequest()->setBody(new NoSeekStream(Stream::factory('foo'))); + $s->onBefore(new BeforeEvent($t)); + $this->assertTrue($t->getRequest()->hasHeader('Expect')); + } + + public function testRemovesContentLengthWhenSendingWithChunked() + { + $s = new Prepare(); + $t = $this->getTrans(); + $t->getRequest()->setBody(Stream::factory('foo')); + $t->getRequest()->setHeader('Transfer-Encoding', 'chunked'); + $s->onBefore(new BeforeEvent($t)); + $this->assertFalse($t->getRequest()->hasHeader('Content-Length')); + } + + public function testUsesProvidedContentLengthAndRemovesXferEncoding() + { + $s = new Prepare(); + $t = $this->getTrans(); + $t->getRequest()->setBody(Stream::factory('foo')); + $t->getRequest()->setHeader('Content-Length', '3'); + $t->getRequest()->setHeader('Transfer-Encoding', 'chunked'); + $s->onBefore(new BeforeEvent($t)); + $this->assertEquals(3, $t->getRequest()->getHeader('Content-Length')); + $this->assertFalse($t->getRequest()->hasHeader('Transfer-Encoding')); + } + + public function testSetsContentTypeIfPossibleFromStream() + { + $body = $this->getMockBody(); + $sub = new Prepare(); + $t = $this->getTrans(); + $t->getRequest()->setBody($body); + $sub->onBefore(new BeforeEvent($t)); + $this->assertEquals( + 'image/jpeg', + $t->getRequest()->getHeader('Content-Type') + ); + $this->assertEquals(4, $t->getRequest()->getHeader('Content-Length')); + } + + public function testDoesNotOverwriteExistingContentType() + { + $s = new Prepare(); + $t = $this->getTrans(); + $t->getRequest()->setBody($this->getMockBody()); + $t->getRequest()->setHeader('Content-Type', 'foo/baz'); + $s->onBefore(new BeforeEvent($t)); + $this->assertEquals( + 'foo/baz', + $t->getRequest()->getHeader('Content-Type') + ); + } + + public function testSetsContentLengthIfPossible() + { + $s = new Prepare(); + $t = $this->getTrans(); + $t->getRequest()->setBody($this->getMockBody()); + $s->onBefore(new BeforeEvent($t)); + $this->assertEquals(4, $t->getRequest()->getHeader('Content-Length')); + } + + public function testSetsTransferEncodingChunkedIfNeeded() + { + $r = new Request('PUT', '/'); + $s = $this->getMockBuilder('GuzzleHttp\Stream\StreamInterface') + ->setMethods(['getSize']) + ->getMockForAbstractClass(); + $s->expects($this->exactly(2)) + ->method('getSize') + ->will($this->returnValue(null)); + $r->setBody($s); + $t = $this->getTrans($r); + $s = new Prepare(); + $s->onBefore(new BeforeEvent($t)); + $this->assertEquals('chunked', $r->getHeader('Transfer-Encoding')); + } + + private function getTrans($request = null) + { + return new Transaction( + new Client(), + $request ?: new Request('PUT', '/') + ); + } + + /** + * @return \GuzzleHttp\Stream\StreamInterface + */ + private function getMockBody() + { + $s = $this->getMockBuilder('GuzzleHttp\Stream\MetadataStreamInterface') + ->setMethods(['getMetadata', 'getSize']) + ->getMockForAbstractClass(); + $s->expects($this->any()) + ->method('getMetadata') + ->with('uri') + ->will($this->returnValue('/foo/baz/bar.jpg')); + $s->expects($this->exactly(2)) + ->method('getSize') + ->will($this->returnValue(4)); + + return $s; + } +} diff --git a/core/vendor/guzzlehttp/guzzle/tests/Subscriber/RedirectTest.php b/core/vendor/guzzlehttp/guzzle/tests/Subscriber/RedirectTest.php new file mode 100644 index 0000000000000000000000000000000000000000..cf4487e57aac8095e3452b7cc2f9514c7ee37fd1 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/tests/Subscriber/RedirectTest.php @@ -0,0 +1,230 @@ +<?php + +namespace GuzzleHttp\Tests\Plugin\Redirect; + +use GuzzleHttp\Client; +use GuzzleHttp\Subscriber\History; +use GuzzleHttp\Subscriber\Mock; + +/** + * @covers GuzzleHttp\Subscriber\Redirect + */ +class RedirectTest extends \PHPUnit_Framework_TestCase +{ + public function testRedirectsRequests() + { + $mock = new Mock(); + $history = new History(); + $mock->addMultiple([ + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect1\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect2\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + ]); + + $client = new Client(['base_url' => 'http://test.com']); + $client->getEmitter()->attach($history); + $client->getEmitter()->attach($mock); + + $response = $client->get('/foo'); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertContains('/redirect2', $response->getEffectiveUrl()); + + // Ensure that two requests were sent + $requests = $history->getRequests(); + + $this->assertEquals('/foo', $requests[0]->getPath()); + $this->assertEquals('GET', $requests[0]->getMethod()); + $this->assertEquals('/redirect1', $requests[1]->getPath()); + $this->assertEquals('GET', $requests[1]->getMethod()); + $this->assertEquals('/redirect2', $requests[2]->getPath()); + $this->assertEquals('GET', $requests[2]->getMethod()); + } + + /** + * @expectedException \GuzzleHttp\Exception\TooManyRedirectsException + * @expectedExceptionMessage Will not follow more than + */ + public function testCanLimitNumberOfRedirects() + { + $mock = new Mock([ + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect1\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect2\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect3\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect4\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect5\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect6\r\nContent-Length: 0\r\n\r\n" + ]); + $client = new Client(); + $client->getEmitter()->attach($mock); + $client->get('http://www.example.com/foo'); + } + + public function testDefaultBehaviorIsToRedirectWithGetForEntityEnclosingRequests() + { + $h = new History(); + $mock = new Mock([ + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + ]); + $client = new Client(); + $client->getEmitter()->attach($mock); + $client->getEmitter()->attach($h); + $client->post('http://test.com/foo', [ + 'headers' => ['X-Baz' => 'bar'], + 'body' => 'testing' + ]); + + $requests = $h->getRequests(); + $this->assertEquals('POST', $requests[0]->getMethod()); + $this->assertEquals('GET', $requests[1]->getMethod()); + $this->assertEquals('bar', (string) $requests[1]->getHeader('X-Baz')); + $this->assertEquals('GET', $requests[2]->getMethod()); + } + + public function testCanRedirectWithStrictRfcCompliance() + { + $h = new History(); + $mock = new Mock([ + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + ]); + $client = new Client(['base_url' => 'http://test.com']); + $client->getEmitter()->attach($mock); + $client->getEmitter()->attach($h); + $client->post('/foo', [ + 'headers' => ['X-Baz' => 'bar'], + 'body' => 'testing', + 'allow_redirects' => ['max' => 10, 'strict' => true] + ]); + + $requests = $h->getRequests(); + $this->assertEquals('POST', $requests[0]->getMethod()); + $this->assertEquals('POST', $requests[1]->getMethod()); + $this->assertEquals('bar', (string) $requests[1]->getHeader('X-Baz')); + $this->assertEquals('POST', $requests[2]->getMethod()); + } + + public function testRewindsStreamWhenRedirectingIfNeeded() + { + $h = new History(); + $mock = new Mock([ + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + ]); + $client = new Client(['base_url' => 'http://test.com']); + $client->getEmitter()->attach($mock); + $client->getEmitter()->attach($h); + + $body = $this->getMockBuilder('GuzzleHttp\Stream\StreamInterface') + ->setMethods(['seek', 'read', 'eof', 'tell']) + ->getMockForAbstractClass(); + $body->expects($this->once())->method('tell')->will($this->returnValue(1)); + $body->expects($this->once())->method('seek')->will($this->returnValue(true)); + $body->expects($this->any())->method('eof')->will($this->returnValue(true)); + $body->expects($this->any())->method('read')->will($this->returnValue('foo')); + $client->post('/foo', [ + 'body' => $body, + 'allow_redirects' => ['max' => 5, 'strict' => true] + ]); + } + + /** + * @expectedException \GuzzleHttp\Exception\CouldNotRewindStreamException + * @expectedExceptionMessage Unable to rewind the non-seekable request body after redirecting + */ + public function testThrowsExceptionWhenStreamCannotBeRewound() + { + $h = new History(); + $mock = new Mock([ + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + ]); + $client = new Client(); + $client->getEmitter()->attach($mock); + $client->getEmitter()->attach($h); + + $body = $this->getMockBuilder('GuzzleHttp\Stream\StreamInterface') + ->setMethods(['seek', 'read', 'eof', 'tell']) + ->getMockForAbstractClass(); + $body->expects($this->once())->method('tell')->will($this->returnValue(1)); + $body->expects($this->once())->method('seek')->will($this->returnValue(false)); + $body->expects($this->any())->method('eof')->will($this->returnValue(true)); + $body->expects($this->any())->method('read')->will($this->returnValue('foo')); + $client->post('http://example.com/foo', [ + 'body' => $body, + 'allow_redirects' => ['max' => 10, 'strict' => true] + ]); + } + + public function testRedirectsCanBeDisabledPerRequest() + { + $client = new Client(['base_url' => 'http://test.com']); + $client->getEmitter()->attach(new Mock([ + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + ])); + $response = $client->put('/', ['body' => 'test', 'allow_redirects' => false]); + $this->assertEquals(301, $response->getStatusCode()); + } + + public function testCanRedirectWithNoLeadingSlashAndQuery() + { + $h = new History(); + $client = new Client(['base_url' => 'http://www.foo.com']); + $client->getEmitter()->attach(new Mock([ + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect?foo=bar\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + ])); + $client->getEmitter()->attach($h); + $client->get('?foo=bar'); + $requests = $h->getRequests(); + $this->assertEquals('http://www.foo.com?foo=bar', $requests[0]->getUrl()); + $this->assertEquals('http://www.foo.com/redirect?foo=bar', $requests[1]->getUrl()); + } + + public function testHandlesRedirectsWithSpacesProperly() + { + $client = new Client(['base_url' => 'http://www.foo.com']); + $client->getEmitter()->attach(new Mock([ + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect 1\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n" + ])); + $h = new History(); + $client->getEmitter()->attach($h); + $client->get('/foo'); + $reqs = $h->getRequests(); + $this->assertEquals('/redirect%201', $reqs[1]->getResource()); + } + + public function testAddsRefererWhenPossible() + { + $client = new Client(['base_url' => 'http://www.foo.com']); + $client->getEmitter()->attach(new Mock([ + "HTTP/1.1 301 Moved Permanently\r\nLocation: /bar\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n" + ])); + $h = new History(); + $client->getEmitter()->attach($h); + $client->get('/foo', ['allow_redirects' => ['max' => 5, 'referer' => true]]); + $reqs = $h->getRequests(); + $this->assertEquals('http://www.foo.com/foo', $reqs[1]->getHeader('Referer')); + } + + public function testDoesNotAddRefererWhenChangingProtocols() + { + $client = new Client(['base_url' => 'https://www.foo.com']); + $client->getEmitter()->attach(new Mock([ + "HTTP/1.1 301 Moved Permanently\r\n" + . "Location: http://www.foo.com/foo\r\n" + . "Content-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n" + ])); + $h = new History(); + $client->getEmitter()->attach($h); + $client->get('/foo', ['allow_redirects' => ['max' => 5, 'referer' => true]]); + $reqs = $h->getRequests(); + $this->assertFalse($reqs[1]->hasHeader('Referer')); + } +} diff --git a/core/vendor/guzzlehttp/guzzle/tests/UriTemplateTest.php b/core/vendor/guzzlehttp/guzzle/tests/UriTemplateTest.php new file mode 100644 index 0000000000000000000000000000000000000000..b65ec1893d962e7263bd58557e823c17567806e2 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/tests/UriTemplateTest.php @@ -0,0 +1,202 @@ +<?php + +namespace GuzzleHttp\Tests; + +use GuzzleHttp\UriTemplate; + +/** + * @covers GuzzleHttp\UriTemplate + */ +class UriTemplateTest extends \PHPUnit_Framework_TestCase +{ + /** + * @return array + */ + public function templateProvider() + { + $params = array( + 'var' => 'value', + 'hello' => 'Hello World!', + 'empty' => '', + 'path' => '/foo/bar', + 'x' => '1024', + 'y' => '768', + 'null' => null, + 'list' => array('red', 'green', 'blue'), + 'keys' => array( + "semi" => ';', + "dot" => '.', + "comma" => ',' + ), + 'empty_keys' => array(), + ); + + return array_map(function($t) use ($params) { + $t[] = $params; + return $t; + }, array( + array('foo', 'foo'), + array('{var}', 'value'), + array('{hello}', 'Hello%20World%21'), + array('{+var}', 'value'), + array('{+hello}', 'Hello%20World!'), + array('{+path}/here', '/foo/bar/here'), + array('here?ref={+path}', 'here?ref=/foo/bar'), + array('X{#var}', 'X#value'), + array('X{#hello}', 'X#Hello%20World!'), + array('map?{x,y}', 'map?1024,768'), + array('{x,hello,y}', '1024,Hello%20World%21,768'), + array('{+x,hello,y}', '1024,Hello%20World!,768'), + array('{+path,x}/here', '/foo/bar,1024/here'), + array('{#x,hello,y}', '#1024,Hello%20World!,768'), + array('{#path,x}/here', '#/foo/bar,1024/here'), + array('X{.var}', 'X.value'), + array('X{.x,y}', 'X.1024.768'), + array('{/var}', '/value'), + array('{/var,x}/here', '/value/1024/here'), + array('{;x,y}', ';x=1024;y=768'), + array('{;x,y,empty}', ';x=1024;y=768;empty'), + array('{?x,y}', '?x=1024&y=768'), + array('{?x,y,empty}', '?x=1024&y=768&empty='), + array('?fixed=yes{&x}', '?fixed=yes&x=1024'), + array('{&x,y,empty}', '&x=1024&y=768&empty='), + array('{var:3}', 'val'), + array('{var:30}', 'value'), + array('{list}', 'red,green,blue'), + array('{list*}', 'red,green,blue'), + array('{keys}', 'semi,%3B,dot,.,comma,%2C'), + array('{keys*}', 'semi=%3B,dot=.,comma=%2C'), + array('{+path:6}/here', '/foo/b/here'), + array('{+list}', 'red,green,blue'), + array('{+list*}', 'red,green,blue'), + array('{+keys}', 'semi,;,dot,.,comma,,'), + array('{+keys*}', 'semi=;,dot=.,comma=,'), + array('{#path:6}/here', '#/foo/b/here'), + array('{#list}', '#red,green,blue'), + array('{#list*}', '#red,green,blue'), + array('{#keys}', '#semi,;,dot,.,comma,,'), + array('{#keys*}', '#semi=;,dot=.,comma=,'), + array('X{.var:3}', 'X.val'), + array('X{.list}', 'X.red,green,blue'), + array('X{.list*}', 'X.red.green.blue'), + array('X{.keys}', 'X.semi,%3B,dot,.,comma,%2C'), + array('X{.keys*}', 'X.semi=%3B.dot=..comma=%2C'), + array('{/var:1,var}', '/v/value'), + array('{/list}', '/red,green,blue'), + array('{/list*}', '/red/green/blue'), + array('{/list*,path:4}', '/red/green/blue/%2Ffoo'), + array('{/keys}', '/semi,%3B,dot,.,comma,%2C'), + array('{/keys*}', '/semi=%3B/dot=./comma=%2C'), + array('{;hello:5}', ';hello=Hello'), + array('{;list}', ';list=red,green,blue'), + array('{;list*}', ';list=red;list=green;list=blue'), + array('{;keys}', ';keys=semi,%3B,dot,.,comma,%2C'), + array('{;keys*}', ';semi=%3B;dot=.;comma=%2C'), + array('{?var:3}', '?var=val'), + array('{?list}', '?list=red,green,blue'), + array('{?list*}', '?list=red&list=green&list=blue'), + array('{?keys}', '?keys=semi,%3B,dot,.,comma,%2C'), + array('{?keys*}', '?semi=%3B&dot=.&comma=%2C'), + array('{&var:3}', '&var=val'), + array('{&list}', '&list=red,green,blue'), + array('{&list*}', '&list=red&list=green&list=blue'), + array('{&keys}', '&keys=semi,%3B,dot,.,comma,%2C'), + array('{&keys*}', '&semi=%3B&dot=.&comma=%2C'), + array('{.null}', ''), + array('{.null,var}', '.value'), + array('X{.empty_keys*}', 'X'), + array('X{.empty_keys}', 'X'), + // Test that missing expansions are skipped + array('test{&missing*}', 'test'), + // Test that multiple expansions can be set + array('http://{var}/{var:2}{?keys*}', 'http://value/va?semi=%3B&dot=.&comma=%2C'), + // Test more complex query string stuff + array('http://www.test.com{+path}{?var,keys*}', 'http://www.test.com/foo/bar?var=value&semi=%3B&dot=.&comma=%2C') + )); + } + + /** + * @dataProvider templateProvider + */ + public function testExpandsUriTemplates($template, $expansion, $params) + { + $uri = new UriTemplate($template); + $this->assertEquals($expansion, $uri->expand($template, $params)); + } + + public function expressionProvider() + { + return array( + array( + '{+var*}', array( + 'operator' => '+', + 'values' => array( + array('value' => 'var', 'modifier' => '*') + ) + ), + ), + array( + '{?keys,var,val}', array( + 'operator' => '?', + 'values' => array( + array('value' => 'keys', 'modifier' => ''), + array('value' => 'var', 'modifier' => ''), + array('value' => 'val', 'modifier' => '') + ) + ), + ), + array( + '{+x,hello,y}', array( + 'operator' => '+', + 'values' => array( + array('value' => 'x', 'modifier' => ''), + array('value' => 'hello', 'modifier' => ''), + array('value' => 'y', 'modifier' => '') + ) + ) + ) + ); + } + + /** + * @dataProvider expressionProvider + */ + public function testParsesExpressions($exp, $data) + { + $template = new UriTemplate($exp); + + // Access the config object + $class = new \ReflectionClass($template); + $method = $class->getMethod('parseExpression'); + $method->setAccessible(true); + + $exp = substr($exp, 1, -1); + $this->assertEquals($data, $method->invokeArgs($template, array($exp))); + } + + /** + * @ticket https://github.com/guzzle/guzzle/issues/90 + */ + public function testAllowsNestedArrayExpansion() + { + $template = new UriTemplate(); + + $result = $template->expand('http://example.com{+path}{/segments}{?query,data*,foo*}', array( + 'path' => '/foo/bar', + 'segments' => array('one', 'two'), + 'query' => 'test', + 'data' => array( + 'more' => array('fun', 'ice cream') + ), + 'foo' => array( + 'baz' => array( + 'bar' => 'fizz', + 'test' => 'buzz' + ), + 'bam' => 'boo' + ) + )); + + $this->assertEquals('http://example.com/foo/bar/one,two?query=test&more%5B0%5D=fun&more%5B1%5D=ice%20cream&baz%5Bbar%5D=fizz&baz%5Btest%5D=buzz&bam=boo', $result); + } +} diff --git a/core/vendor/guzzlehttp/guzzle/tests/UrlTest.php b/core/vendor/guzzlehttp/guzzle/tests/UrlTest.php new file mode 100644 index 0000000000000000000000000000000000000000..ce2543d44f25aa10947a0e10422dd7a308e9c9a0 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/tests/UrlTest.php @@ -0,0 +1,301 @@ +<?php + +namespace GuzzleHttp\Tests; + +use GuzzleHttp\Query; +use GuzzleHttp\Url; + +/** + * @covers GuzzleHttp\Url + */ +class UrlTest extends \PHPUnit_Framework_TestCase +{ + const RFC3986_BASE = "http://a/b/c/d;p?q"; + + public function testEmptyUrl() + { + $url = Url::fromString(''); + $this->assertEquals('', (string) $url); + } + + public function testPortIsDeterminedFromScheme() + { + $this->assertEquals(80, Url::fromString('http://www.test.com/')->getPort()); + $this->assertEquals(443, Url::fromString('https://www.test.com/')->getPort()); + $this->assertEquals(21, Url::fromString('ftp://www.test.com/')->getPort()); + $this->assertEquals(8192, Url::fromString('http://www.test.com:8192/')->getPort()); + $this->assertEquals(null, Url::fromString('foo://www.test.com/')->getPort()); + } + + public function testRemovesDefaultPortWhenSettingScheme() + { + $url = Url::fromString('http://www.test.com/'); + $url->setPort(80); + $url->setScheme('https'); + $this->assertEquals(443, $url->getPort()); + } + + public function testCloneCreatesNewInternalObjects() + { + $u1 = Url::fromString('http://www.test.com/'); + $u2 = clone $u1; + $this->assertNotSame($u1->getQuery(), $u2->getQuery()); + } + + public function testValidatesUrlPartsInFactory() + { + $url = Url::fromString('/index.php'); + $this->assertEquals('/index.php', (string) $url); + $this->assertFalse($url->isAbsolute()); + + $url = 'http://michael:test@test.com:80/path/123?q=abc#test'; + $u = Url::fromString($url); + $this->assertEquals('http://michael:test@test.com/path/123?q=abc#test', (string) $u); + $this->assertTrue($u->isAbsolute()); + } + + public function testAllowsFalsyUrlParts() + { + $url = Url::fromString('http://a:50/0?0#0'); + $this->assertSame('a', $url->getHost()); + $this->assertEquals(50, $url->getPort()); + $this->assertSame('/0', $url->getPath()); + $this->assertEquals('0', (string) $url->getQuery()); + $this->assertSame('0', $url->getFragment()); + $this->assertEquals('http://a:50/0?0#0', (string) $url); + + $url = Url::fromString(''); + $this->assertSame('', (string) $url); + + $url = Url::fromString('0'); + $this->assertSame('0', (string) $url); + } + + public function testBuildsRelativeUrlsWithFalsyParts() + { + $url = Url::buildUrl(['path' => '/0']); + $this->assertSame('/0', $url); + + $url = Url::buildUrl(['path' => '0']); + $this->assertSame('0', $url); + } + + public function testUrlStoresParts() + { + $url = Url::fromString('http://test:pass@www.test.com:8081/path/path2/?a=1&b=2#fragment'); + $this->assertEquals('http', $url->getScheme()); + $this->assertEquals('test', $url->getUsername()); + $this->assertEquals('pass', $url->getPassword()); + $this->assertEquals('www.test.com', $url->getHost()); + $this->assertEquals(8081, $url->getPort()); + $this->assertEquals('/path/path2/', $url->getPath()); + $this->assertEquals('fragment', $url->getFragment()); + $this->assertEquals('a=1&b=2', (string) $url->getQuery()); + + $this->assertEquals(array( + 'fragment' => 'fragment', + 'host' => 'www.test.com', + 'pass' => 'pass', + 'path' => '/path/path2/', + 'port' => 8081, + 'query' => 'a=1&b=2', + 'scheme' => 'http', + 'user' => 'test' + ), $url->getParts()); + } + + public function testHandlesPathsCorrectly() + { + $url = Url::fromString('http://www.test.com'); + $this->assertEquals('', $url->getPath()); + $url->setPath('test'); + $this->assertEquals('test', $url->getPath()); + + $url->setPath('/test/123/abc'); + $this->assertEquals(array('', 'test', '123', 'abc'), $url->getPathSegments()); + + $parts = parse_url('http://www.test.com/test'); + $parts['path'] = ''; + $this->assertEquals('http://www.test.com', Url::buildUrl($parts)); + $parts['path'] = 'test'; + $this->assertEquals('http://www.test.com/test', Url::buildUrl($parts)); + } + + public function testAddsQueryIfPresent() + { + $this->assertEquals('?foo=bar', Url::buildUrl(array( + 'query' => 'foo=bar' + ))); + } + + public function testAddsToPath() + { + // Does nothing here + $this->assertEquals('http://e.com/base?a=1', (string) Url::fromString('http://e.com/base?a=1')->addPath(false)); + $this->assertEquals('http://e.com/base?a=1', (string) Url::fromString('http://e.com/base?a=1')->addPath('')); + $this->assertEquals('http://e.com/base?a=1', (string) Url::fromString('http://e.com/base?a=1')->addPath('/')); + $this->assertEquals('http://e.com/base/0', (string) Url::fromString('http://e.com/base')->addPath('0')); + + $this->assertEquals('http://e.com/base/relative?a=1', (string) Url::fromString('http://e.com/base?a=1')->addPath('relative')); + $this->assertEquals('http://e.com/base/relative?a=1', (string) Url::fromString('http://e.com/base?a=1')->addPath('/relative')); + } + + /** + * URL combination data provider + * + * @return array + */ + public function urlCombineDataProvider() + { + return [ + // Specific test cases + ['http://www.example.com/', 'http://www.example.com/', 'http://www.example.com/'], + ['http://www.example.com/path', '/absolute', 'http://www.example.com/absolute'], + ['http://www.example.com/path', '/absolute?q=2', 'http://www.example.com/absolute?q=2'], + ['http://www.example.com/', '?q=1', 'http://www.example.com/?q=1'], + ['http://www.example.com/path', 'http://test.com', 'http://test.com'], + ['http://www.example.com:8080/path', 'http://test.com', 'http://test.com'], + ['http://www.example.com:8080/path', '?q=2#abc', 'http://www.example.com:8080/path?q=2#abc'], + ['http://www.example.com/path', 'http://u:a@www.example.com/', 'http://u:a@www.example.com/'], + ['/path?q=2', 'http://www.test.com/', 'http://www.test.com/path?q=2'], + ['http://api.flickr.com/services/', 'http://www.flickr.com/services/oauth/access_token', 'http://www.flickr.com/services/oauth/access_token'], + ['https://www.example.com/path', '//foo.com/abc', 'https://foo.com/abc'], + // RFC 3986 test cases + [self::RFC3986_BASE, 'g:h', 'g:h'], + [self::RFC3986_BASE, 'g', 'http://a/b/c/g'], + [self::RFC3986_BASE, './g', 'http://a/b/c/g'], + [self::RFC3986_BASE, 'g/', 'http://a/b/c/g/'], + [self::RFC3986_BASE, '/g', 'http://a/g'], + [self::RFC3986_BASE, '//g', 'http://g'], + [self::RFC3986_BASE, '?y', 'http://a/b/c/d;p?y'], + [self::RFC3986_BASE, 'g?y', 'http://a/b/c/g?y'], + [self::RFC3986_BASE, '#s', 'http://a/b/c/d;p?q#s'], + [self::RFC3986_BASE, 'g#s', 'http://a/b/c/g#s'], + [self::RFC3986_BASE, 'g?y#s', 'http://a/b/c/g?y#s'], + [self::RFC3986_BASE, ';x', 'http://a/b/c/;x'], + [self::RFC3986_BASE, 'g;x', 'http://a/b/c/g;x'], + [self::RFC3986_BASE, 'g;x?y#s', 'http://a/b/c/g;x?y#s'], + [self::RFC3986_BASE, '', self::RFC3986_BASE], + [self::RFC3986_BASE, '.', 'http://a/b/c/'], + [self::RFC3986_BASE, './', 'http://a/b/c/'], + [self::RFC3986_BASE, '..', 'http://a/b/'], + [self::RFC3986_BASE, '../', 'http://a/b/'], + [self::RFC3986_BASE, '../g', 'http://a/b/g'], + [self::RFC3986_BASE, '../..', 'http://a/'], + [self::RFC3986_BASE, '../../', 'http://a/'], + [self::RFC3986_BASE, '../../g', 'http://a/g'], + [self::RFC3986_BASE, '../../../g', 'http://a/g'], + [self::RFC3986_BASE, '../../../../g', 'http://a/g'], + [self::RFC3986_BASE, '/./g', 'http://a/g'], + [self::RFC3986_BASE, '/../g', 'http://a/g'], + [self::RFC3986_BASE, 'g.', 'http://a/b/c/g.'], + [self::RFC3986_BASE, '.g', 'http://a/b/c/.g'], + [self::RFC3986_BASE, 'g..', 'http://a/b/c/g..'], + [self::RFC3986_BASE, '..g', 'http://a/b/c/..g'], + [self::RFC3986_BASE, './../g', 'http://a/b/g'], + [self::RFC3986_BASE, './g/.', 'http://a/b/c/g/'], + [self::RFC3986_BASE, 'g/./h', 'http://a/b/c/g/h'], + [self::RFC3986_BASE, 'g/../h', 'http://a/b/c/h'], + [self::RFC3986_BASE, 'g;x=1/./y', 'http://a/b/c/g;x=1/y'], + [self::RFC3986_BASE, 'g;x=1/../y', 'http://a/b/c/y'], + [self::RFC3986_BASE, 'http:g', 'http:g'], + ]; + } + + /** + * @dataProvider urlCombineDataProvider + */ + public function testCombinesUrls($a, $b, $c) + { + $this->assertEquals($c, (string) Url::fromString($a)->combine($b)); + } + + public function testHasGettersAndSetters() + { + $url = Url::fromString('http://www.test.com/'); + $this->assertEquals('example.com', $url->setHost('example.com')->getHost()); + $this->assertEquals('8080', $url->setPort(8080)->getPort()); + $this->assertEquals('/foo/bar', $url->setPath('/foo/bar')->getPath()); + $this->assertEquals('a', $url->setPassword('a')->getPassword()); + $this->assertEquals('b', $url->setUsername('b')->getUsername()); + $this->assertEquals('abc', $url->setFragment('abc')->getFragment()); + $this->assertEquals('https', $url->setScheme('https')->getScheme()); + $this->assertEquals('a=123', (string) $url->setQuery('a=123')->getQuery()); + $this->assertEquals('https://b:a@example.com:8080/foo/bar?a=123#abc', (string) $url); + $this->assertEquals('b=boo', (string) $url->setQuery(new Query(array( + 'b' => 'boo' + )))->getQuery()); + $this->assertEquals('https://b:a@example.com:8080/foo/bar?b=boo#abc', (string) $url); + } + + public function testSetQueryAcceptsArray() + { + $url = Url::fromString('http://www.test.com'); + $url->setQuery(array('a' => 'b')); + $this->assertEquals('http://www.test.com?a=b', (string) $url); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testQueryMustBeValid() + { + $url = Url::fromString('http://www.test.com'); + $url->setQuery(false); + } + + public function urlProvider() + { + return array( + array('/foo/..', '/'), + array('//foo//..', '/'), + array('/foo/../..', '/'), + array('/foo/../.', '/'), + array('/./foo/..', '/'), + array('/./foo', '/foo'), + array('/./foo/', '/foo/'), + array('*', '*'), + array('/foo', '/foo'), + array('/abc/123/../foo/', '/abc/foo/'), + array('/a/b/c/./../../g', '/a/g'), + array('/b/c/./../../g', '/g'), + array('/b/c/./../../g', '/g'), + array('/c/./../../g', '/g'), + array('/./../../g', '/g'), + array('foo', 'foo'), + ); + } + + /** + * @dataProvider urlProvider + */ + public function testRemoveDotSegments($path, $result) + { + $url = Url::fromString('http://www.example.com'); + $url->setPath($path)->removeDotSegments(); + $this->assertEquals($result, $url->getPath()); + } + + public function testSettingHostWithPortModifiesPort() + { + $url = Url::fromString('http://www.example.com'); + $url->setHost('foo:8983'); + $this->assertEquals('foo', $url->getHost()); + $this->assertEquals(8983, $url->getPort()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testValidatesUrlCanBeParsed() + { + Url::fromString('foo:////'); + } + + public function testConvertsSpecialCharsInPathWhenCastingToString() + { + $url = Url::fromString('http://foo.com/baz bar?a=b'); + $url->addPath('?'); + $this->assertEquals('http://foo.com/baz%20bar/%3F?a=b', (string) $url); + } +} diff --git a/core/vendor/guzzlehttp/guzzle/tests/bootstrap.php b/core/vendor/guzzlehttp/guzzle/tests/bootstrap.php new file mode 100644 index 0000000000000000000000000000000000000000..6c3ded321d798d31fb6b4f61991ce1acb75c557b --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/tests/bootstrap.php @@ -0,0 +1,12 @@ +<?php + +require __DIR__ . '/../vendor/autoload.php'; +require __DIR__ . '/Server.php'; + +use GuzzleHttp\Tests\Server; + +register_shutdown_function(function () { + if (Server::$started) { + Server::stop(); + } +}); diff --git a/core/vendor/guzzlehttp/guzzle/tests/perf.php b/core/vendor/guzzlehttp/guzzle/tests/perf.php new file mode 100644 index 0000000000000000000000000000000000000000..1f8567aac0550a39ae0da0b65fc80369bc1de335 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/tests/perf.php @@ -0,0 +1,47 @@ +<?php +/* + * Runs a performance test against the node.js server for both serial and + * parallel requests. Requires PHP 5.5 or greater. + * + * # Basic usage + * make perf + * # With custom options + * REQUESTS=100 PARALLEL=5000 make perf + */ + +require __DIR__ . '/bootstrap.php'; + +use GuzzleHttp\Client; +use GuzzleHttp\Tests\Server; + +// Wait until the server is responding +Server::wait(); + +// Get custom make variables +$total = isset($_SERVER['REQUESTS']) ? $_SERVER['REQUESTS'] : 1000; +$parallel = isset($_SERVER['PARALLEL']) ? $_SERVER['PARALLEL'] : 25; + +$client = new Client(['base_url' => Server::$url]); + +$t = microtime(true); +for ($i = 0; $i < $total; $i++) { + $client->get('/guzzle-server/perf'); +} +$totalTime = microtime(true) - $t; +$perRequest = ($totalTime / $total) * 1000; +printf("Serial: %f (%f ms / request) %d total\n", + $totalTime, $perRequest, $total); + +// Create a generator used to yield batches of requests to sendAll +$reqs = function () use ($client, $total) { + for ($i = 0; $i < $total; $i++) { + yield $client->createRequest('GET', '/guzzle-server/perf'); + } +}; + +$t = microtime(true); +$client->sendAll($reqs(), ['parallel' => $parallel]); +$totalTime = microtime(true) - $t; +$perRequest = ($totalTime / $total) * 1000; +printf("Parallel: %f (%f ms / request) %d total with %d in parallel\n", + $totalTime, $perRequest, $total, $parallel); diff --git a/core/vendor/guzzlehttp/guzzle/tests/server.js b/core/vendor/guzzlehttp/guzzle/tests/server.js new file mode 100644 index 0000000000000000000000000000000000000000..4156f1aad787c066990d99367dd855468947f902 --- /dev/null +++ b/core/vendor/guzzlehttp/guzzle/tests/server.js @@ -0,0 +1,146 @@ +/** + * Guzzle node.js test server to return queued responses to HTTP requests and + * expose a RESTful API for enqueueing responses and retrieving the requests + * that have been received. + * + * - Delete all requests that have been received: + * DELETE /guzzle-server/requests + * Host: 127.0.0.1:8124 + * + * - Enqueue responses + * PUT /guzzle-server/responses + * Host: 127.0.0.1:8124 + * + * [{ "statusCode": 200, "reasonPhrase": "OK", "headers": {}, "body": "" }] + * + * - Get the received requests + * GET /guzzle-server/requests + * Host: 127.0.0.1:8124 + * + * - Shutdown the server + * DELETE /guzzle-server + * Host: 127.0.0.1:8124 + * + * @package Guzzle PHP <http://www.guzzlephp.org> + * @license See the LICENSE file that was distributed with this source code. + */ + +var http = require("http"); + +/** + * Guzzle node.js server + * @class + */ +var GuzzleServer = function(port, log) { + + this.port = port; + this.log = log; + this.responses = []; + this.requests = []; + var that = this; + + var controlRequest = function(request, req, res) { + if (req.url == '/guzzle-server/perf') { + res.writeHead(200, "OK", {"Content-Length": 16}); + res.end("Body of response"); + } else if (req.method == "DELETE") { + if (req.url == "/guzzle-server/requests") { + // Clear the received requests + that.requests = []; + res.writeHead(200, "OK", { "Content-Length": 0 }); + res.end(); + if (this.log) { + console.log("Flushing requests"); + } + } else if (req.url == "/guzzle-server") { + // Shutdown the server + res.writeHead(200, "OK", { "Content-Length": 0, "Connection": "close" }); + res.end(); + if (this.log) { + console.log("Shutting down"); + } + that.server.close(); + } + } else if (req.method == "GET") { + if (req.url === "/guzzle-server/requests") { + // Get received requests + var data = that.requests.join("\n----[request]\n"); + res.writeHead(200, "OK", { "Content-Length": data.length }); + res.end(data); + if (that.log) { + console.log("Sending receiving requests"); + } + } + } else if (req.method == "PUT") { + if (req.url == "/guzzle-server/responses") { + if (that.log) { + console.log("Adding responses..."); + } + // Received response to queue + var data = request.split("\r\n\r\n")[1]; + if (!data) { + if (that.log) { + console.log("No response data was provided"); + } + res.writeHead(400, "NO RESPONSES IN REQUEST", { "Content-Length": 0 }); + } else { + that.responses = eval("(" + data + ")"); + if (that.log) { + console.log(that.responses); + } + res.writeHead(200, "OK", { "Content-Length": 0 }); + } + res.end(); + } + } + }; + + var receivedRequest = function(request, req, res) { + if (req.url.indexOf("/guzzle-server") === 0) { + controlRequest(request, req, res); + } else if (req.url.indexOf("/guzzle-server") == -1 && !that.responses.length) { + res.writeHead(500); + res.end("No responses in queue"); + } else { + var response = that.responses.shift(); + res.writeHead(response.statusCode, response.reasonPhrase, response.headers); + res.end(response.body); + that.requests.push(request); + } + }; + + this.start = function() { + + that.server = http.createServer(function(req, res) { + + var request = req.method + " " + req.url + " HTTP/" + req.httpVersion + "\r\n"; + for (var i in req.headers) { + request += i + ": " + req.headers[i] + "\r\n"; + } + request += "\r\n"; + + // Receive each chunk of the request body + req.addListener("data", function(chunk) { + request += chunk; + }); + + // Called when the request completes + req.addListener("end", function() { + receivedRequest(request, req, res); + }); + }); + that.server.listen(port, "127.0.0.1"); + + if (this.log) { + console.log("Server running at http://127.0.0.1:8124/"); + } + }; +}; + +// Get the port from the arguments +port = process.argv.length >= 3 ? process.argv[2] : 8124; +log = process.argv.length >= 4 ? process.argv[3] : false; + +// Start the server +server = new GuzzleServer(port, log); +server.start(); diff --git a/core/vendor/guzzlehttp/streams/.gitignore b/core/vendor/guzzlehttp/streams/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..c33d3965fddada3e75ebb868d561a0768bb616e2 --- /dev/null +++ b/core/vendor/guzzlehttp/streams/.gitignore @@ -0,0 +1,6 @@ +.idea +.DS_STORE +coverage +phpunit.xml +composer.lock +vendor/ diff --git a/core/vendor/guzzlehttp/streams/.travis.yml b/core/vendor/guzzlehttp/streams/.travis.yml new file mode 100644 index 0000000000000000000000000000000000000000..90df122be4da6706b281bed754ac91d6819b7aeb --- /dev/null +++ b/core/vendor/guzzlehttp/streams/.travis.yml @@ -0,0 +1,12 @@ +language: php + +php: + - 5.4 + - 5.5 + - 5.6 + - hhvm + +before_script: + - composer install + +script: vendor/bin/phpunit diff --git a/core/vendor/guzzlehttp/streams/LICENSE b/core/vendor/guzzlehttp/streams/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..71d3b783cb5b82e732f4555c5b7839036334607b --- /dev/null +++ b/core/vendor/guzzlehttp/streams/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2014 Michael Dowling, https://github.com/mtdowling <mtdowling@gmail.com> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/core/vendor/guzzlehttp/streams/Makefile b/core/vendor/guzzlehttp/streams/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..c39ff76e72ed414e6e1264f759701af7c4a7b4be --- /dev/null +++ b/core/vendor/guzzlehttp/streams/Makefile @@ -0,0 +1,13 @@ +all: clean coverage + +test: + vendor/bin/phpunit + +coverage: + vendor/bin/phpunit --coverage-html=artifacts/coverage + +view-coverage: + open artifacts/coverage/index.html + +clean: + rm -rf artifacts/* diff --git a/core/vendor/guzzlehttp/streams/README.rst b/core/vendor/guzzlehttp/streams/README.rst new file mode 100644 index 0000000000000000000000000000000000000000..3fe5dad05402cf8ce99224f0b2b6645f8ea22815 --- /dev/null +++ b/core/vendor/guzzlehttp/streams/README.rst @@ -0,0 +1,36 @@ +============== +Guzzle Streams +============== + +Provides a simple abstraction over streams of data. + +This library is used in `Guzzle 4 <https://github.com/guzzle/guzzle>`_ and is +an implementation of the proposed `PSR-7 stream interface <https://github.com/php-fig/fig-standards/blob/master/proposed/http-message.md#34-psrhttpstreaminterface>`_. + +Installation +============ + +This package can be installed easily using `Composer <http://getcomposer.org>`_. +Simply add the following to the composer.json file at the root of your project: + +.. code-block:: javascript + + { + "require": { + "guzzlehttp/streams": "1.*" + } + } + +Then install your dependencies using ``composer.phar install``. + +Documentation +============= + +The documentation for this package can be found on the main Guzzle website at +http://docs.guzzlephp.org/en/guzzle4/streams.html. + +Testing +======= + +This library is tested using PHPUnit. You'll need to install the dependencies +using `Composer <http://getcomposer.org>`_ then run ``make test``. diff --git a/core/vendor/guzzlehttp/streams/composer.json b/core/vendor/guzzlehttp/streams/composer.json new file mode 100644 index 0000000000000000000000000000000000000000..6cae568f603f639617274b66d6708a5a5d215c92 --- /dev/null +++ b/core/vendor/guzzlehttp/streams/composer.json @@ -0,0 +1,29 @@ +{ + "name": "guzzlehttp/streams", + "description": "Provides a simple abstraction over streams of data (Guzzle 4+)", + "homepage": "http://guzzlephp.org/", + "keywords": ["stream", "guzzle"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "4.*" + }, + "autoload": { + "psr-4": { "GuzzleHttp\\Stream\\": "src/" }, + "files": ["src/functions.php"] + }, + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + } +} diff --git a/core/vendor/guzzlehttp/streams/phpunit.xml.dist b/core/vendor/guzzlehttp/streams/phpunit.xml.dist new file mode 100644 index 0000000000000000000000000000000000000000..994e1584eac8299fa9d9d37e1856908b52f5f98d --- /dev/null +++ b/core/vendor/guzzlehttp/streams/phpunit.xml.dist @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<phpunit bootstrap="./vendor/autoload.php" + colors="true"> + <testsuites> + <testsuite> + <directory>tests</directory> + </testsuite> + </testsuites> + <filter> + <whitelist> + <directory suffix=".php">src</directory> + </whitelist> + </filter> +</phpunit> diff --git a/core/vendor/guzzlehttp/streams/src/CachingStream.php b/core/vendor/guzzlehttp/streams/src/CachingStream.php new file mode 100644 index 0000000000000000000000000000000000000000..35419f1ed95a41115564da29a2184567f3e289ce --- /dev/null +++ b/core/vendor/guzzlehttp/streams/src/CachingStream.php @@ -0,0 +1,119 @@ +<?php + +namespace GuzzleHttp\Stream; + +/** + * Stream decorator that can cache previously read bytes from a sequentially + * read stream. + */ +class CachingStream implements StreamInterface, MetadataStreamInterface +{ + use StreamDecoratorTrait; + + /** @var StreamInterface Stream being wrapped */ + private $remoteStream; + + /** @var int Number of bytes to skip reading due to a write on the buffer */ + private $skipReadBytes = 0; + + /** + * We will treat the buffer object as the body of the stream + * + * @param StreamInterface $stream Stream to cache + * @param StreamInterface $target Optionally specify where data is cached + */ + public function __construct( + StreamInterface $stream, + StreamInterface $target = null + ) { + $this->remoteStream = $stream; + $this->stream = $target ?: new Stream(fopen('php://temp', 'r+')); + } + + public function getSize() + { + return max($this->stream->getSize(), $this->remoteStream->getSize()); + } + + /** + * {@inheritdoc} + * @throws \RuntimeException When seeking with SEEK_END or when seeking + * past the total size of the buffer stream + */ + public function seek($offset, $whence = SEEK_SET) + { + if ($whence == SEEK_SET) { + $byte = $offset; + } elseif ($whence == SEEK_CUR) { + $byte = $offset + $this->tell(); + } else { + throw new \RuntimeException(__CLASS__ . ' supports only SEEK_SET' + .' and SEEK_CUR seek operations'); + } + + // You cannot skip ahead past where you've read from the remote stream + if ($byte > $this->stream->getSize()) { + throw new \RuntimeException(sprintf('Cannot seek to byte %d when ' + . ' the buffered stream only contains %d bytes', + $byte, $this->stream->getSize())); + } + + return $this->stream->seek($byte); + } + + public function read($length) + { + // Perform a regular read on any previously read data from the buffer + $data = $this->stream->read($length); + $remaining = $length - strlen($data); + + // More data was requested so read from the remote stream + if ($remaining) { + // If data was written to the buffer in a position that would have + // been filled from the remote stream, then we must skip bytes on + // the remote stream to emulate overwriting bytes from that + // position. This mimics the behavior of other PHP stream wrappers. + $remoteData = $this->remoteStream->read( + $remaining + $this->skipReadBytes + ); + + if ($this->skipReadBytes) { + $len = strlen($remoteData); + $remoteData = substr($remoteData, $this->skipReadBytes); + $this->skipReadBytes = max(0, $this->skipReadBytes - $len); + } + + $data .= $remoteData; + $this->stream->write($remoteData); + } + + return $data; + } + + public function write($string) + { + // When appending to the end of the currently read stream, you'll want + // to skip bytes from being read from the remote stream to emulate + // other stream wrappers. Basically replacing bytes of data of a fixed + // length. + $overflow = (strlen($string) + $this->tell()) - $this->remoteStream->tell(); + if ($overflow > 0) { + $this->skipReadBytes += $overflow; + } + + return $this->stream->write($string); + } + + public function eof() + { + return $this->stream->eof() && $this->remoteStream->eof(); + } + + /** + * Close both the remote stream and buffer stream + */ + public function close() + { + $this->remoteStream->close() && $this->stream->close(); + } +} diff --git a/core/vendor/guzzlehttp/streams/src/LimitStream.php b/core/vendor/guzzlehttp/streams/src/LimitStream.php new file mode 100644 index 0000000000000000000000000000000000000000..8c481a42cd65b2a7af440963832e9971020cd65b --- /dev/null +++ b/core/vendor/guzzlehttp/streams/src/LimitStream.php @@ -0,0 +1,149 @@ +<?php + +namespace GuzzleHttp\Stream; + +/** + * Decorator used to return only a subset of a stream + */ +class LimitStream implements StreamInterface, MetadataStreamInterface +{ + use StreamDecoratorTrait; + + /** @var int Offset to start reading from */ + private $offset; + + /** @var int Limit the number of bytes that can be read */ + private $limit; + + /** + * @param StreamInterface $stream Stream to wrap + * @param int $limit Total number of bytes to allow to be read + * from the stream. Pass -1 for no limit. + * @param int|null $offset Position to seek to before reading (only + * works on seekable streams). + */ + public function __construct( + StreamInterface $stream, + $limit = -1, + $offset = 0 + ) { + $this->stream = $stream; + $this->setLimit($limit); + $this->setOffset($offset); + } + + public function eof() + { + if ($this->limit == -1) { + return $this->stream->eof(); + } + + $tell = $this->stream->tell(); + + return $tell === false || (($this->offset + $this->limit) - $tell) <= 0; + } + + /** + * Returns the size of the limited subset of data + * {@inheritdoc} + */ + public function getSize() + { + if (null === ($length = $this->stream->getSize())) { + return null; + } elseif ($this->limit == -1) { + return $length - $this->offset; + } else { + return min($this->limit, $length - $this->offset); + } + } + + /** + * Allow for a bounded seek on the read limited stream + * {@inheritdoc} + */ + public function seek($offset, $whence = SEEK_SET) + { + if ($whence != SEEK_SET) { + return false; + } + + if ($offset < $this->offset) { + $offset = $this->offset; + } + + if ($this->limit !== -1 && $offset > ($this->offset + $this->limit)) { + $offset = $this->offset + $this->limit; + } + + return $this->stream->seek($offset); + } + + /** + * Give a relative tell() + * {@inheritdoc} + */ + public function tell() + { + return $this->stream->tell() - $this->offset; + } + + /** + * Set the offset to start limiting from + * + * @param int $offset Offset to seek to and begin byte limiting from + * + * @return self + * @throws \RuntimeException + */ + public function setOffset($offset) + { + $this->offset = $offset; + $current = $this->stream->tell(); + if ($current !== $offset) { + // If the stream cannot seek to the offset position, then read to it + if (!$this->stream->seek($offset)) { + if ($current > $offset) { + throw new \RuntimeException("Cannot seek to stream offset {$offset}"); + } else { + $this->stream->read($offset - $current); + } + } + } + + return $this; + } + + /** + * Set the limit of bytes that the decorator allows to be read from the + * stream. + * + * @param int $limit Number of bytes to allow to be read from the stream. + * Use -1 for no limit. + * @return self + */ + public function setLimit($limit) + { + $this->limit = $limit; + + return $this; + } + + public function read($length) + { + if ($this->limit == -1) { + return $this->stream->read($length); + } + + // Check if the current position is less than the total allowed + // bytes + original offset + $remaining = ($this->offset + $this->limit) - $this->stream->tell(); + if ($remaining > 0) { + // Only return the amount of requested data, ensuring that the byte + // limit is not exceeded + return $this->stream->read(min($remaining, $length)); + } else { + return false; + } + } +} diff --git a/core/vendor/guzzlehttp/streams/src/MetadataStreamInterface.php b/core/vendor/guzzlehttp/streams/src/MetadataStreamInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..3cd96d68aa8063047f13b8b3dd37873fe62531ff --- /dev/null +++ b/core/vendor/guzzlehttp/streams/src/MetadataStreamInterface.php @@ -0,0 +1,25 @@ +<?php + +namespace GuzzleHttp\Stream; + +/** + * Represents a stream that contains metadata + */ +interface MetadataStreamInterface extends StreamInterface +{ + /** + * Get stream metadata as an associative array or retrieve a specific key. + * + * The keys returned are identical to the keys returned from PHP's + * stream_get_meta_data() function. + * + * @param string $key Specific metadata to retrieve. + * + * @return array|mixed|null Returns an associative array if no key is + * no key is provided. Returns a specific key + * value if a key is provided and the value is + * found, or null if the key is not found. + * @see http://php.net/manual/en/function.stream-get-meta-data.php + */ + public function getMetadata($key = null); +} diff --git a/core/vendor/guzzlehttp/streams/src/NoSeekStream.php b/core/vendor/guzzlehttp/streams/src/NoSeekStream.php new file mode 100644 index 0000000000000000000000000000000000000000..7d02fc31b655e6047f77af41b2f3d29b5737aa75 --- /dev/null +++ b/core/vendor/guzzlehttp/streams/src/NoSeekStream.php @@ -0,0 +1,21 @@ +<?php + +namespace GuzzleHttp\Stream; + +/** + * Stream decorator that prevents a stream from being seeked + */ +class NoSeekStream implements StreamInterface, MetadataStreamInterface +{ + use StreamDecoratorTrait; + + public function seek($offset, $whence = SEEK_SET) + { + return false; + } + + public function isSeekable() + { + return false; + } +} diff --git a/core/vendor/guzzlehttp/streams/src/Stream.php b/core/vendor/guzzlehttp/streams/src/Stream.php new file mode 100644 index 0000000000000000000000000000000000000000..cc0d6b3b5905b873eb1ea256589e25dbab4c9c07 --- /dev/null +++ b/core/vendor/guzzlehttp/streams/src/Stream.php @@ -0,0 +1,198 @@ +<?php + +namespace GuzzleHttp\Stream; + +/** + * PHP stream implementation + */ +class Stream implements MetadataStreamInterface +{ + /** @var resource Stream resource */ + private $stream; + + /** @var int Size of the stream contents in bytes */ + private $size; + + /** @var bool */ + private $seekable; + private $readable; + private $writable; + + /** @var array Stream metadata */ + private $meta = []; + + /** @var array Hash of readable and writable stream types */ + private static $readWriteHash = [ + 'read' => [ + 'r' => true, 'w+' => true, 'r+' => true, 'x+' => true, 'c+' => true, + 'rb' => true, 'w+b' => true, 'r+b' => true, 'x+b' => true, + 'c+b' => true, 'rt' => true, 'w+t' => true, 'r+t' => true, + 'x+t' => true, 'c+t' => true, 'a+' => true + ], + 'write' => [ + 'w' => true, 'w+' => true, 'rw' => true, 'r+' => true, 'x+' => true, + 'c+' => true, 'wb' => true, 'w+b' => true, 'r+b' => true, + 'x+b' => true, 'c+b' => true, 'w+t' => true, 'r+t' => true, + 'x+t' => true, 'c+t' => true, 'a' => true, 'a+' => true + ] + ]; + + /** + * Create a new stream based on the input type + * + * @param resource|string|StreamInterface $resource Entity body data + * @param int $size Size of the data contained in the resource + * + * @return StreamInterface + * @throws \InvalidArgumentException if the $resource arg is not valid. + */ + public static function factory($resource = '', $size = null) + { + return create($resource, $size); + } + + /** + * @param resource $stream Stream resource to wrap + * @param int $size Size of the stream in bytes. Only pass if the + * size cannot be obtained from the stream. + * + * @throws \InvalidArgumentException if the stream is not a stream resource + */ + public function __construct($stream, $size = null) + { + if (!is_resource($stream)) { + throw new \InvalidArgumentException('Stream must be a resource'); + } + + $this->size = $size; + $this->stream = $stream; + $this->meta = stream_get_meta_data($this->stream); + $this->seekable = $this->meta['seekable']; + $this->readable = isset(self::$readWriteHash['read'][$this->meta['mode']]); + $this->writable = isset(self::$readWriteHash['write'][$this->meta['mode']]); + } + + /** + * Closes the stream when the destructed + */ + public function __destruct() + { + $this->close(); + } + + public function __toString() + { + $this->seek(0); + + return (string) stream_get_contents($this->stream); + } + + public function getContents($maxLength = -1) + { + return stream_get_contents($this->stream, $maxLength); + } + + public function close() + { + if (is_resource($this->stream)) { + fclose($this->stream); + } + $this->meta = []; + $this->stream = null; + } + + public function detach() + { + $this->stream = null; + } + + public function getSize() + { + if ($this->size !== null) { + return $this->size; + } + + // If the stream is a file based stream and local, then use fstat + clearstatcache(true, $this->meta['uri']); + $stats = fstat($this->stream); + if (isset($stats['size'])) { + $this->size = $stats['size']; + return $this->size; + } + + return null; + } + + public function isReadable() + { + return $this->stream && $this->readable; + } + + public function isWritable() + { + return $this->stream && $this->writable; + } + + public function isSeekable() + { + return $this->stream && $this->seekable; + } + + public function eof() + { + return feof($this->stream); + } + + public function tell() + { + return ftell($this->stream); + } + + public function setSize($size) + { + $this->size = $size; + + return $this; + } + + public function seek($offset, $whence = SEEK_SET) + { + return $this->seekable + ? fseek($this->stream, $offset, $whence) === 0 + : false; + } + + public function read($length) + { + return fread($this->stream, $length); + } + + public function write($string) + { + // We can't know the size after writing anything + $this->size = null; + + return fwrite($this->stream, $string); + } + + /** + * Get stream metadata as an associative array or retrieve a specific key. + * + * The keys returned are identical to the keys returned from PHP's + * stream_get_meta_data() function. + * + * @param string $key Specific metadata to retrieve. + * + * @return array|mixed|null Returns an associative array if no key is + * no key is provided. Returns a specific key + * value if a key is provided and the value is + * found, or null if the key is not found. + * @see http://php.net/manual/en/function.stream-get-meta-data.php + */ + public function getMetadata($key = null) + { + return !$key + ? $this->meta + : (isset($this->meta[$key]) ? $this->meta[$key] : null); + } +} diff --git a/core/vendor/guzzlehttp/streams/src/StreamDecoratorTrait.php b/core/vendor/guzzlehttp/streams/src/StreamDecoratorTrait.php new file mode 100644 index 0000000000000000000000000000000000000000..801129742c116d97dafcc5136ff95b12df50f485 --- /dev/null +++ b/core/vendor/guzzlehttp/streams/src/StreamDecoratorTrait.php @@ -0,0 +1,118 @@ +<?php + +namespace GuzzleHttp\Stream; + +/** + * Stream decorator trait + */ +trait StreamDecoratorTrait +{ + /** @var StreamInterface Decorated stream */ + private $stream; + + /** + * @param StreamInterface $stream Stream to decorate + */ + public function __construct(StreamInterface $stream) + { + $this->stream = $stream; + } + + public function __toString() + { + try { + $this->seek(0); + return $this->getContents(); + } catch (\Exception $e) { + // Really, PHP? https://bugs.php.net/bug.php?id=53648 + trigger_error('StreamDecorator::__toString exception: ' + . (string) $e, E_USER_ERROR); + return ''; + } + } + + public function getContents($maxLength = -1) + { + return copy_to_string($this, $maxLength); + } + + /** + * Allow decorators to implement custom methods + * + * @param string $method Missing method name + * @param array $args Method arguments + * + * @return mixed + */ + public function __call($method, array $args) + { + $result = call_user_func_array(array($this->stream, $method), $args); + + // Always return the wrapped object if the result is a return $this + return $result === $this->stream ? $this : $result; + } + + public function close() + { + return $this->stream->close(); + } + + public function getMetadata($key = null) + { + return $this->stream instanceof MetadataStreamInterface + ? $this->stream->getMetadata($key) + : null; + } + + public function detach() + { + $this->stream->detach(); + + return $this; + } + + public function getSize() + { + return $this->stream->getSize(); + } + + public function eof() + { + return $this->stream->eof(); + } + + public function tell() + { + return $this->stream->tell(); + } + + public function isReadable() + { + return $this->stream->isReadable(); + } + + public function isWritable() + { + return $this->stream->isWritable(); + } + + public function isSeekable() + { + return $this->stream->isSeekable(); + } + + public function seek($offset, $whence = SEEK_SET) + { + return $this->stream->seek($offset, $whence); + } + + public function read($length) + { + return $this->stream->read($length); + } + + public function write($string) + { + return $this->stream->write($string); + } +} diff --git a/core/vendor/guzzlehttp/streams/src/StreamInterface.php b/core/vendor/guzzlehttp/streams/src/StreamInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..7e07cf73cdbe5921bcf75f4fdb3c084de60272e9 --- /dev/null +++ b/core/vendor/guzzlehttp/streams/src/StreamInterface.php @@ -0,0 +1,119 @@ +<?php + +namespace GuzzleHttp\Stream; + +/** + * Describes a stream instance. + */ +interface StreamInterface +{ + /** + * Attempts to seek to the beginning of the stream and reads all data into + * a string until the end of the stream is reached. + * + * Warning: This could attempt to load a large amount of data into memory. + * + * @return string + */ + public function __toString(); + + /** + * Closes the stream and any underlying resources. + */ + public function close(); + + /** + * Separates any underlying resources from the stream. + * + * After the stream has been detached, the stream is in an unusable state. + */ + public function detach(); + + /** + * Get the size of the stream if known + * + * @return int|null Returns the size in bytes if known, or null if unknown + */ + public function getSize(); + + /** + * Returns the current position of the file read/write pointer + * + * @return int|bool Returns the position of the file pointer or false on error + */ + public function tell(); + + /** + * Returns true if the stream is at the end of the stream. + * + * @return bool + */ + public function eof(); + + /** + * Returns whether or not the stream is seekable + * + * @return bool + */ + public function isSeekable(); + + /** + * Seek to a position in the stream + * + * @param int $offset Stream offset + * @param int $whence Specifies how the cursor position will be calculated + * based on the seek offset. Valid values are identical + * to the built-in PHP $whence values for `fseek()`. + * SEEK_SET: Set position equal to offset bytes + * SEEK_CUR: Set position to current location plus offset + * SEEK_END: Set position to end-of-stream plus offset + * + * @return bool Returns TRUE on success or FALSE on failure + * @link http://www.php.net/manual/en/function.fseek.php + */ + public function seek($offset, $whence = SEEK_SET); + + /** + * Returns whether or not the stream is writable + * + * @return bool + */ + public function isWritable(); + + /** + * Write data to the stream + * + * @param string $string The string that is to be written. + * + * @return int|bool Returns the number of bytes written to the stream on + * success or FALSE on failure. + */ + public function write($string); + + /** + * Returns whether or not the stream is readable + * + * @return bool + */ + public function isReadable(); + + /** + * Read data from the stream + * + * @param int $length Read up to $length bytes from the object and return + * them. Fewer than $length bytes may be returned if + * underlying stream call returns fewer bytes. + * + * @return string Returns the data read from the stream. + */ + public function read($length); + + /** + * Returns the remaining contents in a string, up to maxlength bytes. + * + * @param int $maxLength The maximum bytes to read. Defaults to -1 (read + * all the remaining buffer). + * @return string + */ + public function getContents($maxLength = -1); +} diff --git a/core/vendor/guzzlehttp/streams/src/functions.php b/core/vendor/guzzlehttp/streams/src/functions.php new file mode 100644 index 0000000000000000000000000000000000000000..3e9d99924a59f3b00f8a27a3125f20ef9d46c7c1 --- /dev/null +++ b/core/vendor/guzzlehttp/streams/src/functions.php @@ -0,0 +1,170 @@ +<?php + +namespace GuzzleHttp\Stream; + +/** + * Create a new stream based on the input type + * + * @param resource|string|StreamInterface $resource Entity body data + * @param int $size Size of the data contained in the resource + * + * @return StreamInterface + * @throws \InvalidArgumentException if the $resource arg is not valid. + */ +function create($resource = '', $size = null) +{ + $type = gettype($resource); + + if ($type == 'string') { + $stream = fopen('php://temp', 'r+'); + if ($resource !== '') { + fwrite($stream, $resource); + fseek($stream, 0); + } + return new Stream($stream); + } + + if ($type == 'resource') { + return new Stream($resource, $size); + } + + if ($resource instanceof StreamInterface) { + return $resource; + } + + if ($type == 'object' && method_exists($resource, '__toString')) { + return create((string) $resource, $size); + } + + throw new \InvalidArgumentException('Invalid resource type: ' . $type); +} + +/** + * Copy the contents of a stream into a string until the given number of bytes + * have been read. + * + * @param StreamInterface $stream Stream to read + * @param int $maxLen Maximum number of bytes to read. Pass -1 to + * read the entire stream. + * @return string + */ +function copy_to_string(StreamInterface $stream, $maxLen = -1) +{ + $buffer = ''; + + if ($maxLen === -1) { + while (!$stream->eof()) { + $buf = $stream->read(1048576); + if ($buf === '' || $buf === false) { + break; + } + $buffer .= $buf; + } + } else { + $len = 0; + while (!$stream->eof() && $len < $maxLen) { + $buf = $stream->read($maxLen - $len); + if ($buf === '' || $buf === false) { + break; + } + $buffer .= $buf; + $len = strlen($buffer); + } + } + + return $buffer; +} + +/** + * Copy the contents of a stream into another stream until the given number of + * bytes have been read. + * + * @param StreamInterface $source Stream to read from + * @param StreamInterface $dest Stream to write to + * @param int $maxLen Maximum number of bytes to read. Pass -1 to + * read the entire stream. + */ +function copy_to_stream( + StreamInterface $source, + StreamInterface $dest, + $maxLen = -1 +) { + if ($maxLen === -1) { + while (!$source->eof()) { + if (!$dest->write($source->read(1048576))) { + break; + } + } + return; + } + + $bytes = 0; + while (!$source->eof()) { + $buf = $source->read($maxLen - $bytes); + if (!($len = strlen($buf))) { + break; + } + $bytes += $len; + $dest->write($buf); + if ($bytes == $maxLen) { + break; + } + } +} + +/** + * Calculate a hash of a Stream + * + * @param StreamInterface $stream Stream to calculate the hash for + * @param string $algo Hash algorithm (e.g. md5, crc32, etc) + * @param bool $rawOutput Whether or not to use raw output + * + * @return bool|string Returns false on failure or a hash string on success + */ +function hash( + StreamInterface $stream, + $algo, + $rawOutput = false +) { + $pos = $stream->tell(); + if (!$stream->seek(0)) { + return false; + } + + $ctx = hash_init($algo); + while ($data = $stream->read(1048576)) { + hash_update($ctx, $data); + } + + $out = hash_final($ctx, (bool) $rawOutput); + $stream->seek($pos); + + return $out; +} + +/** + * Read a line from the stream up to the maximum allowed buffer length + * + * @param StreamInterface $stream Stream to read from + * @param int $maxLength Maximum buffer length + * + * @return string|bool + */ +function read_line(StreamInterface $stream, $maxLength = null) +{ + $buffer = ''; + $size = 0; + + while (!$stream->eof()) { + if (false === ($byte = $stream->read(1))) { + return $buffer; + } + $buffer .= $byte; + // Break when a new line is found or the max length - 1 is reached + if ($byte == PHP_EOL || ++$size == $maxLength - 1) { + break; + } + } + + return $buffer; +} diff --git a/core/vendor/guzzlehttp/streams/tests/CachingStreamTest.php b/core/vendor/guzzlehttp/streams/tests/CachingStreamTest.php new file mode 100644 index 0000000000000000000000000000000000000000..a7688ba26dae6d2066e524cac829884ed8b20d89 --- /dev/null +++ b/core/vendor/guzzlehttp/streams/tests/CachingStreamTest.php @@ -0,0 +1,141 @@ +<?php + +namespace GuzzleHttp\Tests\Stream; + +use GuzzleHttp\Stream\Stream; +use GuzzleHttp\Stream\CachingStream; + +/** + * @covers GuzzleHttp\Stream\CachingStream + */ +class CachingStreamTest extends \PHPUnit_Framework_TestCase +{ + /** @var CachingStream */ + protected $body; + + /** @var Stream */ + protected $decorated; + + public function setUp() + { + $this->decorated = Stream::factory('testing'); + $this->body = new CachingStream($this->decorated); + } + + public function tearDown() + { + $this->decorated->close(); + $this->body->close(); + } + + public function testUsesRemoteSizeIfPossible() + { + $body = Stream::factory('test'); + $caching = new CachingStream($body); + $this->assertEquals(4, $caching->getSize()); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage Cannot seek to byte 10 + */ + public function testCannotSeekPastWhatHasBeenRead() + { + $this->body->seek(10); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage supports only SEEK_SET and SEEK_CUR + */ + public function testCannotUseSeekEnd() + { + $this->body->seek(2, SEEK_END); + } + + public function testRewindUsesSeek() + { + $a = Stream::factory('foo'); + $d = $this->getMockBuilder('GuzzleHttp\Stream\CachingStream') + ->setMethods(array('seek')) + ->setConstructorArgs(array($a)) + ->getMock(); + $d->expects($this->once()) + ->method('seek') + ->with(0) + ->will($this->returnValue(true)); + $d->seek(0); + } + + public function testCanSeekToReadBytes() + { + $this->assertEquals('te', $this->body->read(2)); + $this->body->seek(0); + $this->assertEquals('test', $this->body->read(4)); + $this->assertEquals(4, $this->body->tell()); + $this->body->seek(2); + $this->assertEquals(2, $this->body->tell()); + $this->body->seek(2, SEEK_CUR); + $this->assertEquals(4, $this->body->tell()); + $this->assertEquals('ing', $this->body->read(3)); + } + + public function testWritesToBufferStream() + { + $this->body->read(2); + $this->body->write('hi'); + $this->body->seek(0); + $this->assertEquals('tehiing', (string) $this->body); + } + + public function testSkipsOverwrittenBytes() + { + $decorated = Stream::factory( + implode("\n", array_map(function ($n) { + return str_pad($n, 4, '0', STR_PAD_LEFT); + }, range(0, 25))), + true + ); + + $body = new CachingStream($decorated); + + $this->assertEquals("0000\n", \GuzzleHttp\Stream\read_line($body)); + $this->assertEquals("0001\n", \GuzzleHttp\Stream\read_line($body)); + // Write over part of the body yet to be read, so skip some bytes + $this->assertEquals(5, $body->write("TEST\n")); + $this->assertEquals(5, $this->readAttribute($body, 'skipReadBytes')); + // Read, which skips bytes, then reads + $this->assertEquals("0003\n", \GuzzleHttp\Stream\read_line($body)); + $this->assertEquals(0, $this->readAttribute($body, 'skipReadBytes')); + $this->assertEquals("0004\n", \GuzzleHttp\Stream\read_line($body)); + $this->assertEquals("0005\n", \GuzzleHttp\Stream\read_line($body)); + + // Overwrite part of the cached body (so don't skip any bytes) + $body->seek(5); + $this->assertEquals(5, $body->write("ABCD\n")); + $this->assertEquals(0, $this->readAttribute($body, 'skipReadBytes')); + $this->assertEquals("TEST\n", \GuzzleHttp\Stream\read_line($body)); + $this->assertEquals("0003\n", \GuzzleHttp\Stream\read_line($body)); + $this->assertEquals("0004\n", \GuzzleHttp\Stream\read_line($body)); + $this->assertEquals("0005\n", \GuzzleHttp\Stream\read_line($body)); + $this->assertEquals("0006\n", \GuzzleHttp\Stream\read_line($body)); + $this->assertEquals(5, $body->write("1234\n")); + $this->assertEquals(5, $this->readAttribute($body, 'skipReadBytes')); + + // Seek to 0 and ensure the overwritten bit is replaced + $body->seek(0); + $this->assertEquals("0000\nABCD\nTEST\n0003\n0004\n0005\n0006\n1234\n0008\n0009\n", $body->read(50)); + + // Ensure that casting it to a string does not include the bit that was overwritten + $this->assertContains("0000\nABCD\nTEST\n0003\n0004\n0005\n0006\n1234\n0008\n0009\n", (string) $body); + } + + public function testClosesBothStreams() + { + $s = fopen('php://temp', 'r'); + $a = Stream::factory($s); + $d = new CachingStream($a); + $d->close(); + $this->assertFalse(is_resource($s)); + } +} diff --git a/core/vendor/guzzlehttp/streams/tests/LimitStreamTest.php b/core/vendor/guzzlehttp/streams/tests/LimitStreamTest.php new file mode 100644 index 0000000000000000000000000000000000000000..8a07c492518e4f51584aff4bbec00f8b92a8038b --- /dev/null +++ b/core/vendor/guzzlehttp/streams/tests/LimitStreamTest.php @@ -0,0 +1,95 @@ +<?php + +namespace GuzzleHttp\Tests\Http; + +use GuzzleHttp\Stream\Stream; +use GuzzleHttp\Stream\LimitStream; + +/** + * @covers GuzzleHttp\Stream\LimitStream + */ +class LimitStreamTest extends \PHPUnit_Framework_TestCase +{ + /** @var LimitStream */ + protected $body; + + /** @var Stream */ + protected $decorated; + + public function setUp() + { + $this->decorated = Stream::factory(fopen(__FILE__, 'r')); + $this->body = new LimitStream($this->decorated, 10, 3); + } + + public function testReturnsSubset() + { + $body = new LimitStream(Stream::factory('foo'), -1, 1); + $this->assertEquals('oo', (string) $body); + $this->assertTrue($body->eof()); + $body->seek(0); + $this->assertFalse($body->eof()); + $this->assertEquals('oo', $body->read(100)); + $this->assertTrue($body->eof()); + } + + public function testReturnsSubsetWhenCastToString() + { + $body = Stream::factory('foo_baz_bar'); + $limited = new LimitStream($body, 3, 4); + $this->assertEquals('baz', (string) $limited); + } + + public function testReturnsSubsetOfEmptyBodyWhenCastToString() + { + $body = Stream::factory(''); + $limited = new LimitStream($body, 0, 10); + $this->assertEquals('', (string) $limited); + } + + public function testSeeksWhenConstructed() + { + $this->assertEquals(0, $this->body->tell()); + $this->assertEquals(3, $this->decorated->tell()); + } + + public function testAllowsBoundedSeek() + { + $this->body->seek(100); + $this->assertEquals(13, $this->decorated->tell()); + $this->body->seek(0); + $this->assertEquals(0, $this->body->tell()); + $this->assertEquals(3, $this->decorated->tell()); + $this->assertEquals(false, $this->body->seek(1000, SEEK_END)); + } + + public function testReadsOnlySubsetOfData() + { + $data = $this->body->read(100); + $this->assertEquals(10, strlen($data)); + $this->assertFalse($this->body->read(1000)); + + $this->body->setOffset(10); + $newData = $this->body->read(100); + $this->assertEquals(10, strlen($newData)); + $this->assertNotSame($data, $newData); + } + + public function testClaimsConsumedWhenReadLimitIsReached() + { + $this->assertFalse($this->body->eof()); + $this->body->read(1000); + $this->assertTrue($this->body->eof()); + } + + public function testContentLengthIsBounded() + { + $this->assertEquals(10, $this->body->getSize()); + } + + public function testGetContentsIsBasedOnSubset() + { + $body = new LimitStream(Stream::factory('foobazbar'), 3, 3); + $this->assertEquals('baz', $body->getContents()); + } +} diff --git a/core/vendor/guzzlehttp/streams/tests/NoSeekStreamTest.php b/core/vendor/guzzlehttp/streams/tests/NoSeekStreamTest.php new file mode 100644 index 0000000000000000000000000000000000000000..b8988eead30f6b07126b6bb011e31175382177b0 --- /dev/null +++ b/core/vendor/guzzlehttp/streams/tests/NoSeekStreamTest.php @@ -0,0 +1,24 @@ +<?php + +namespace GuzzleHttp\Tests\Stream; + +use GuzzleHttp\Stream\Stream; +use GuzzleHttp\Stream\NoSeekStream; + +/** + * @covers GuzzleHttp\Stream\NoSeekStream + */ +class NoSeekStreamTest extends \PHPUnit_Framework_TestCase +{ + public function testCannotSeek() + { + $s = $this->getMockBuilder('GuzzleHttp\Stream\StreamInterface') + ->setMethods(['isSeekable', 'seek']) + ->getMockForAbstractClass(); + $s->expects($this->never())->method('seek'); + $s->expects($this->never())->method('isSeekable'); + $wrapped = new NoSeekStream($s); + $this->assertFalse($wrapped->isSeekable()); + $this->assertFalse($wrapped->seek(2)); + } +} diff --git a/core/vendor/guzzlehttp/streams/tests/StreamDecoratorTraitTest.php b/core/vendor/guzzlehttp/streams/tests/StreamDecoratorTraitTest.php new file mode 100644 index 0000000000000000000000000000000000000000..1d49936bfb69a21df6eeec3cda14747629e639fc --- /dev/null +++ b/core/vendor/guzzlehttp/streams/tests/StreamDecoratorTraitTest.php @@ -0,0 +1,117 @@ +<?php + +namespace GuzzleHttp\Tests\Stream; + +use GuzzleHttp\Stream\StreamInterface; +use GuzzleHttp\Stream\Stream; +use GuzzleHttp\Stream\StreamDecoratorTrait; + +class Str implements StreamInterface +{ + use StreamDecoratorTrait; +} + +/** + * @covers GuzzleHttp\Stream\StreamDecoratorTrait + */ +class StreamDecoratorTraitTest extends \PHPUnit_Framework_TestCase +{ + private $a; + private $b; + private $c; + + public function setUp() + { + $this->c = fopen('php://temp', 'r+'); + fwrite($this->c, 'foo'); + fseek($this->c, 0); + $this->a = Stream::factory($this->c); + $this->b = new Str($this->a); + } + + public function testCatchesExceptionsWhenCastingToString() + { + $s = $this->getMockBuilder('GuzzleHttp\Stream\StreamInterface') + ->setMethods(['read']) + ->getMockForAbstractClass(); + $s->expects($this->once()) + ->method('read') + ->will($this->throwException(new \Exception('foo'))); + $msg = ''; + set_error_handler(function ($errNo, $str) use (&$msg) { $msg = $str; }); + echo new Str($s); + restore_error_handler(); + $this->assertContains('foo', $msg); + } + + public function testToString() + { + $this->assertEquals('foo', (string) $this->b); + } + + public function testHasSize() + { + $this->assertEquals(3, $this->b->getSize()); + $this->assertSame($this->b, $this->b->setSize(2)); + $this->assertEquals(2, $this->b->getSize()); + } + + public function testReads() + { + $this->assertEquals('foo', $this->b->read(10)); + } + + public function testCheckMethods() + { + $this->assertEquals($this->a->isReadable(), $this->b->isReadable()); + $this->assertEquals($this->a->isWritable(), $this->b->isWritable()); + $this->assertEquals($this->a->isSeekable(), $this->b->isSeekable()); + } + + public function testSeeksAndTells() + { + $this->assertTrue($this->b->seek(1)); + $this->assertEquals(1, $this->a->tell()); + $this->assertEquals(1, $this->b->tell()); + $this->assertTrue($this->b->seek(0)); + $this->assertEquals(0, $this->a->tell()); + $this->assertEquals(0, $this->b->tell()); + $this->assertTrue($this->b->seek(0, SEEK_END)); + $this->assertEquals(3, $this->a->tell()); + $this->assertEquals(3, $this->b->tell()); + } + + public function testGetsContents() + { + $this->assertEquals('foo', $this->b->getContents()); + $this->assertEquals('', $this->b->getContents()); + $this->b->seek(1); + $this->assertEquals('o', $this->b->getContents(1)); + $this->assertEquals('', $this->b->getContents(0)); + } + + public function testCloses() + { + $this->b->close(); + $this->assertFalse(is_resource($this->c)); + } + + public function testDetaches() + { + $this->b->detach(); + $this->assertFalse($this->b->isReadable()); + } + + public function testWrapsMetadata() + { + $this->assertSame($this->b->getMetadata(), $this->a->getMetadata()); + $this->assertSame($this->b->getMetadata('uri'), $this->a->getMetadata('uri')); + } + + public function testWrapsWrites() + { + $this->b->seek(0, SEEK_END); + $this->b->write('foo'); + $this->assertEquals('foofoo', (string) $this->a); + } +} diff --git a/core/vendor/guzzlehttp/streams/tests/StreamTest.php b/core/vendor/guzzlehttp/streams/tests/StreamTest.php new file mode 100644 index 0000000000000000000000000000000000000000..d1460aa8a0eb6ed4f50f4ae35c4ee5fc4e4bece0 --- /dev/null +++ b/core/vendor/guzzlehttp/streams/tests/StreamTest.php @@ -0,0 +1,150 @@ +<?php + +namespace GuzzleHttp\Tests\Stream; + +use GuzzleHttp\Stream\Stream; + +/** + * @covers GuzzleHttp\Stream\Stream + */ +class StreamTest extends \PHPUnit_Framework_TestCase +{ + /** + * @expectedException \InvalidArgumentException + */ + public function testConstructorThrowsExceptionOnInvalidArgument() + { + new Stream(true); + } + + public function testConstructorInitializesProperties() + { + $handle = fopen('php://temp', 'r+'); + fwrite($handle, 'data'); + $stream = new Stream($handle); + $this->assertTrue($stream->isReadable()); + $this->assertTrue($stream->isWritable()); + $this->assertTrue($stream->isSeekable()); + $this->assertEquals('php://temp', $stream->getMetadata('uri')); + $this->assertInternalType('array', $stream->getMetadata()); + $this->assertEquals(4, $stream->getSize()); + $this->assertFalse($stream->eof()); + $stream->close(); + } + + public function testStreamClosesHandleOnDestruct() + { + $handle = fopen('php://temp', 'r'); + $stream = new Stream($handle); + unset($stream); + $this->assertFalse(is_resource($handle)); + } + + public function testConvertsToString() + { + $handle = fopen('php://temp', 'w+'); + fwrite($handle, 'data'); + $stream = new Stream($handle); + $this->assertEquals('data', (string) $stream); + $this->assertEquals('data', (string) $stream); + $stream->close(); + } + + public function testGetsContents() + { + $handle = fopen('php://temp', 'w+'); + fwrite($handle, 'data'); + $stream = new Stream($handle); + $this->assertEquals('', $stream->getContents()); + $stream->seek(0); + $this->assertEquals('data', $stream->getContents()); + $this->assertEquals('', $stream->getContents()); + $stream->seek(0); + $this->assertEquals('da', $stream->getContents(2)); + $stream->close(); + } + + public function testChecksEof() + { + $handle = fopen('php://temp', 'w+'); + fwrite($handle, 'data'); + $stream = new Stream($handle); + $this->assertFalse($stream->eof()); + $stream->read(4); + $this->assertTrue($stream->eof()); + $stream->close(); + } + + public function testAllowsSettingManualSize() + { + $handle = fopen('php://temp', 'w+'); + fwrite($handle, 'data'); + $stream = new Stream($handle); + $stream->setSize(10); + $this->assertEquals(10, $stream->getSize()); + $stream->close(); + } + + public function testGetSize() + { + $size = filesize(__FILE__); + $handle = fopen(__FILE__, 'r'); + $stream = new Stream($handle); + $this->assertEquals($size, $stream->getSize()); + // Load from cache + $this->assertEquals($size, $stream->getSize()); + $stream->close(); + } + + public function testEnsuresSizeIsConsistent() + { + $h = fopen('php://temp', 'w+'); + $this->assertEquals(3, fwrite($h, 'foo')); + $stream = new Stream($h); + $this->assertEquals(3, $stream->getSize()); + $this->assertEquals(4, $stream->write('test')); + $this->assertEquals(7, $stream->getSize()); + $this->assertEquals(7, $stream->getSize()); + $stream->close(); + } + + public function testProvidesStreamPosition() + { + $handle = fopen('php://temp', 'w+'); + $stream = new Stream($handle); + $this->assertEquals(0, $stream->tell()); + $stream->write('foo'); + $this->assertEquals(3, $stream->tell()); + $stream->seek(1); + $this->assertEquals(1, $stream->tell()); + $this->assertSame(ftell($handle), $stream->tell()); + $stream->close(); + } + + public function testKeepsPositionOfResource() + { + $h = fopen(__FILE__, 'r'); + fseek($h, 10); + $stream = Stream::factory($h); + $this->assertEquals(10, $stream->tell()); + $stream->close(); + } + + public function testCanDetachStream() + { + $r = fopen('php://temp', 'w+'); + $stream = new Stream($r); + $this->assertTrue($stream->isReadable()); + $stream->detach(); + $this->assertFalse($stream->isReadable()); + $stream->close(); + } + + public function testCreatesWithFactory() + { + $stream = Stream::factory('foo'); + $this->assertInstanceOf('GuzzleHttp\Stream\Stream', $stream); + $this->assertEquals('foo', $stream->getContents()); + $stream->close(); + } +} diff --git a/core/vendor/guzzlehttp/streams/tests/functionsTest.php b/core/vendor/guzzlehttp/streams/tests/functionsTest.php new file mode 100644 index 0000000000000000000000000000000000000000..f6af8a0ffd2866b0cbb8db67a373dfb99c0e3785 --- /dev/null +++ b/core/vendor/guzzlehttp/streams/tests/functionsTest.php @@ -0,0 +1,126 @@ +<?php + +namespace GuzzleHttp\Stream; + +use GuzzleHttp\Stream; + +class functionsTest extends \PHPUnit_Framework_TestCase +{ + public function testCopiesToMemory() + { + $s = Stream\create('foobaz'); + $this->assertEquals('foobaz', Stream\copy_to_string($s)); + $s->seek(0); + $this->assertEquals('foo', Stream\copy_to_string($s, 3)); + $this->assertEquals('baz', Stream\copy_to_string($s, 3)); + $this->assertEquals('', Stream\copy_to_string($s)); + } + + public function testCopiesToStream() + { + $s1 = Stream\create('foobaz'); + $s2 = Stream\create(''); + Stream\copy_to_stream($s1, $s2); + $this->assertEquals('foobaz', (string) $s2); + $s2 = Stream\create(''); + $s1->seek(0); + Stream\copy_to_stream($s1, $s2, 3); + $this->assertEquals('foo', (string) $s2); + Stream\copy_to_stream($s1, $s2, 3); + $this->assertEquals('foobaz', (string) $s2); + } + + public function testReadsLines() + { + $s = Stream\create("foo\nbaz\nbar"); + $this->assertEquals("foo\n", Stream\read_line($s)); + $this->assertEquals("baz\n", Stream\read_line($s)); + $this->assertEquals("bar", Stream\read_line($s)); + } + + public function testReadsLinesUpToMaxLength() + { + $s = Stream\create("12345\n"); + $this->assertEquals("123", Stream\read_line($s, 4)); + $this->assertEquals("45\n", Stream\read_line($s)); + } + + public function testReadsLineUntilFalseReturnedFromRead() + { + $s = $this->getMockBuilder('GuzzleHttp\Stream\Stream') + ->setMethods(['read', 'eof']) + ->disableOriginalConstructor() + ->getMock(); + $s->expects($this->exactly(2)) + ->method('read') + ->will($this->returnCallback(function () { + static $c = false; + if ($c) { + return false; + } + $c = true; + return 'h'; + })); + $s->expects($this->exactly(2)) + ->method('eof') + ->will($this->returnValue(false)); + $this->assertEquals("h", Stream\read_line($s)); + } + + public function testCalculatesHash() + { + $s = Stream\create('foobazbar'); + $this->assertEquals(md5('foobazbar'), Stream\hash($s, 'md5')); + } + + public function testCalculatesHashSeeksToOriginalPosition() + { + $s = Stream\create('foobazbar'); + $s->seek(4); + $this->assertEquals(md5('foobazbar'), Stream\hash($s, 'md5')); + $this->assertEquals(4, $s->tell()); + } + + public function testFactoryCreatesFromEmptyString() + { + $s = Stream\create(); + $this->assertInstanceOf('GuzzleHttp\Stream\Stream', $s); + } + + public function testFactoryCreatesFromResource() + { + $r = fopen(__FILE__, 'r'); + $s = Stream\create($r); + $this->assertInstanceOf('GuzzleHttp\Stream\Stream', $s); + $this->assertSame(file_get_contents(__FILE__), (string) $s); + } + + public function testFactoryCreatesFromObjectWithToString() + { + $r = new HasToString(); + $s = Stream\create($r); + $this->assertInstanceOf('GuzzleHttp\Stream\Stream', $s); + $this->assertEquals('foo', (string) $s); + } + + public function testCreatePassesThrough() + { + $s = Stream\create('foo'); + $this->assertSame($s, Stream\create($s)); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testThrowsExceptionForUnknown() + { + Stream\create(new \stdClass()); + } +} + +class HasToString +{ + public function __toString() { + return 'foo'; + } +}