Explorar el Código

add transmission framework

causefx hace 8 años
padre
commit
c2d3978ac4
Se han modificado 100 ficheros con 13017 adiciones y 2 borrados
  1. 2 1
      api/composer.json
  2. 333 1
      api/composer.lock
  3. 1 0
      api/vendor/composer/autoload_files.php
  4. 1 0
      api/vendor/composer/autoload_namespaces.php
  5. 5 0
      api/vendor/composer/autoload_psr4.php
  6. 42 0
      api/vendor/composer/autoload_static.php
  7. 344 0
      api/vendor/composer/installed.json
  8. 110 0
      api/vendor/guzzlehttp/psr7/CHANGELOG.md
  9. 19 0
      api/vendor/guzzlehttp/psr7/LICENSE
  10. 739 0
      api/vendor/guzzlehttp/psr7/README.md
  11. 39 0
      api/vendor/guzzlehttp/psr7/composer.json
  12. 233 0
      api/vendor/guzzlehttp/psr7/src/AppendStream.php
  13. 137 0
      api/vendor/guzzlehttp/psr7/src/BufferStream.php
  14. 138 0
      api/vendor/guzzlehttp/psr7/src/CachingStream.php
  15. 42 0
      api/vendor/guzzlehttp/psr7/src/DroppingStream.php
  16. 149 0
      api/vendor/guzzlehttp/psr7/src/FnStream.php
  17. 52 0
      api/vendor/guzzlehttp/psr7/src/InflateStream.php
  18. 39 0
      api/vendor/guzzlehttp/psr7/src/LazyOpenStream.php
  19. 155 0
      api/vendor/guzzlehttp/psr7/src/LimitStream.php
  20. 183 0
      api/vendor/guzzlehttp/psr7/src/MessageTrait.php
  21. 153 0
      api/vendor/guzzlehttp/psr7/src/MultipartStream.php
  22. 22 0
      api/vendor/guzzlehttp/psr7/src/NoSeekStream.php
  23. 165 0
      api/vendor/guzzlehttp/psr7/src/PumpStream.php
  24. 142 0
      api/vendor/guzzlehttp/psr7/src/Request.php
  25. 132 0
      api/vendor/guzzlehttp/psr7/src/Response.php
  26. 358 0
      api/vendor/guzzlehttp/psr7/src/ServerRequest.php
  27. 257 0
      api/vendor/guzzlehttp/psr7/src/Stream.php
  28. 149 0
      api/vendor/guzzlehttp/psr7/src/StreamDecoratorTrait.php
  29. 121 0
      api/vendor/guzzlehttp/psr7/src/StreamWrapper.php
  30. 316 0
      api/vendor/guzzlehttp/psr7/src/UploadedFile.php
  31. 702 0
      api/vendor/guzzlehttp/psr7/src/Uri.php
  32. 216 0
      api/vendor/guzzlehttp/psr7/src/UriNormalizer.php
  33. 219 0
      api/vendor/guzzlehttp/psr7/src/UriResolver.php
  34. 828 0
      api/vendor/guzzlehttp/psr7/src/functions.php
  35. 6 0
      api/vendor/guzzlehttp/psr7/src/functions_include.php
  36. 4 0
      api/vendor/kleiram/transmission-php/.gitignore
  37. 9 0
      api/vendor/kleiram/transmission-php/.travis.yml
  38. 27 0
      api/vendor/kleiram/transmission-php/CHANGELOG
  39. 26 0
      api/vendor/kleiram/transmission-php/LICENSE
  40. 197 0
      api/vendor/kleiram/transmission-php/README.md
  41. 22 0
      api/vendor/kleiram/transmission-php/composer.json
  42. 28 0
      api/vendor/kleiram/transmission-php/examples/queue.php
  43. 254 0
      api/vendor/kleiram/transmission-php/lib/Transmission/Client.php
  44. 46 0
      api/vendor/kleiram/transmission-php/lib/Transmission/Model/AbstractModel.php
  45. 91 0
      api/vendor/kleiram/transmission-php/lib/Transmission/Model/File.php
  46. 17 0
      api/vendor/kleiram/transmission-php/lib/Transmission/Model/ModelInterface.php
  47. 347 0
      api/vendor/kleiram/transmission-php/lib/Transmission/Model/Peer.php
  48. 369 0
      api/vendor/kleiram/transmission-php/lib/Transmission/Model/Session.php
  49. 459 0
      api/vendor/kleiram/transmission-php/lib/Transmission/Model/Torrent.php
  50. 105 0
      api/vendor/kleiram/transmission-php/lib/Transmission/Model/Tracker.php
  51. 238 0
      api/vendor/kleiram/transmission-php/lib/Transmission/Transmission.php
  52. 41 0
      api/vendor/kleiram/transmission-php/lib/Transmission/Util/PropertyMapper.php
  53. 81 0
      api/vendor/kleiram/transmission-php/lib/Transmission/Util/ResponseValidator.php
  54. 13 0
      api/vendor/kleiram/transmission-php/phpunit.xml.dist
  55. 50 0
      api/vendor/kleiram/transmission-php/tests/Transmission/Mock/Model.php
  56. 212 0
      api/vendor/kleiram/transmission-php/tests/Transmission/Tests/ClientTest.php
  57. 52 0
      api/vendor/kleiram/transmission-php/tests/Transmission/Tests/Model/AbstractModelTest.php
  58. 57 0
      api/vendor/kleiram/transmission-php/tests/Transmission/Tests/Model/FileTest.php
  59. 79 0
      api/vendor/kleiram/transmission-php/tests/Transmission/Tests/Model/PeerTest.php
  60. 126 0
      api/vendor/kleiram/transmission-php/tests/Transmission/Tests/Model/SessionTest.php
  61. 286 0
      api/vendor/kleiram/transmission-php/tests/Transmission/Tests/Model/TorrentTest.php
  62. 56 0
      api/vendor/kleiram/transmission-php/tests/Transmission/Tests/Model/TrackerTest.php
  63. 253 0
      api/vendor/kleiram/transmission-php/tests/Transmission/Tests/TransmissionTest.php
  64. 40 0
      api/vendor/kleiram/transmission-php/tests/Transmission/Tests/Util/PropertyMapperTest.php
  65. 184 0
      api/vendor/kleiram/transmission-php/tests/Transmission/Tests/Util/ResponseValidatorTest.php
  66. 15 0
      api/vendor/kleiram/transmission-php/tests/bootstrap.php
  67. 9 0
      api/vendor/kriswallsmith/buzz/.gitattributes
  68. 11 0
      api/vendor/kriswallsmith/buzz/.php_cs
  69. 19 0
      api/vendor/kriswallsmith/buzz/LICENSE
  70. 40 0
      api/vendor/kriswallsmith/buzz/composer.json
  71. 8 0
      api/vendor/kriswallsmith/buzz/doc/index.md
  72. 85 0
      api/vendor/kriswallsmith/buzz/doc/middlewares.md
  73. 288 0
      api/vendor/kriswallsmith/buzz/lib/Buzz/Browser.php
  74. 73 0
      api/vendor/kriswallsmith/buzz/lib/Buzz/Client/AbstractClient.php
  75. 278 0
      api/vendor/kriswallsmith/buzz/lib/Buzz/Client/AbstractCurl.php
  76. 52 0
      api/vendor/kriswallsmith/buzz/lib/Buzz/Client/AbstractStream.php
  77. 27 0
      api/vendor/kriswallsmith/buzz/lib/Buzz/Client/BatchClientInterface.php
  78. 20 0
      api/vendor/kriswallsmith/buzz/lib/Buzz/Client/ClientInterface.php
  79. 90 0
      api/vendor/kriswallsmith/buzz/lib/Buzz/Client/Curl.php
  80. 69 0
      api/vendor/kriswallsmith/buzz/lib/Buzz/Client/FileGetContents.php
  81. 140 0
      api/vendor/kriswallsmith/buzz/lib/Buzz/Client/MultiCurl.php
  82. 60 0
      api/vendor/kriswallsmith/buzz/lib/Buzz/Converter/HeaderConverter.php
  83. 64 0
      api/vendor/kriswallsmith/buzz/lib/Buzz/Converter/RequestConverter.php
  84. 74 0
      api/vendor/kriswallsmith/buzz/lib/Buzz/Converter/ResponseConverter.php
  85. 10 0
      api/vendor/kriswallsmith/buzz/lib/Buzz/Exception/ClientException.php
  86. 10 0
      api/vendor/kriswallsmith/buzz/lib/Buzz/Exception/ExceptionInterface.php
  87. 10 0
      api/vendor/kriswallsmith/buzz/lib/Buzz/Exception/InvalidArgumentException.php
  88. 10 0
      api/vendor/kriswallsmith/buzz/lib/Buzz/Exception/LogicException.php
  89. 33 0
      api/vendor/kriswallsmith/buzz/lib/Buzz/Exception/RequestException.php
  90. 7 0
      api/vendor/kriswallsmith/buzz/lib/Buzz/Exception/RuntimeException.php
  91. 27 0
      api/vendor/kriswallsmith/buzz/lib/Buzz/Listener/BasicAuthListener.php
  92. 30 0
      api/vendor/kriswallsmith/buzz/lib/Buzz/Listener/BearerAuthListener.php
  93. 49 0
      api/vendor/kriswallsmith/buzz/lib/Buzz/Listener/CallbackListener.php
  94. 50 0
      api/vendor/kriswallsmith/buzz/lib/Buzz/Listener/CookieListener.php
  95. 837 0
      api/vendor/kriswallsmith/buzz/lib/Buzz/Listener/DigestAuthListener.php
  96. 42 0
      api/vendor/kriswallsmith/buzz/lib/Buzz/Listener/History/Entry.php
  97. 76 0
      api/vendor/kriswallsmith/buzz/lib/Buzz/Listener/History/Journal.php
  98. 33 0
      api/vendor/kriswallsmith/buzz/lib/Buzz/Listener/HistoryListener.php
  99. 41 0
      api/vendor/kriswallsmith/buzz/lib/Buzz/Listener/ListenerChain.php
  100. 12 0
      api/vendor/kriswallsmith/buzz/lib/Buzz/Listener/ListenerInterface.php

+ 2 - 1
api/composer.json

@@ -4,6 +4,7 @@
         "lcobucci/jwt": "^3.2",
         "composer/semver": "^1.4",
         "phpmailer/phpmailer": "^6.0",
-        "rmccue/requests": "^1.7"
+        "rmccue/requests": "^1.7",
+        "kleiram/transmission-php": "^1.0"
     }
 }

+ 333 - 1
api/composer.lock

@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
         "This file is @generated automatically"
     ],
-    "content-hash": "b23a4e199a4e93b84375b6091c164a53",
+    "content-hash": "99433a6dcb34337f94104a05f873bf77",
     "packages": [
         {
             "name": "composer/semver",
@@ -135,6 +135,164 @@
             ],
             "time": "2017-09-25T15:57:54+00:00"
         },
+        {
+            "name": "guzzlehttp/psr7",
+            "version": "1.4.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/guzzle/psr7.git",
+                "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/guzzle/psr7/zipball/f5b8a8512e2b58b0071a7280e39f14f72e05d87c",
+                "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.4.0",
+                "psr/http-message": "~1.0"
+            },
+            "provide": {
+                "psr/http-message-implementation": "1.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "~4.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.4-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "GuzzleHttp\\Psr7\\": "src/"
+                },
+                "files": [
+                    "src/functions_include.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Michael Dowling",
+                    "email": "mtdowling@gmail.com",
+                    "homepage": "https://github.com/mtdowling"
+                },
+                {
+                    "name": "Tobias Schultze",
+                    "homepage": "https://github.com/Tobion"
+                }
+            ],
+            "description": "PSR-7 message implementation that also provides common utility methods",
+            "keywords": [
+                "http",
+                "message",
+                "request",
+                "response",
+                "stream",
+                "uri",
+                "url"
+            ],
+            "time": "2017-03-20T17:10:46+00:00"
+        },
+        {
+            "name": "kleiram/transmission-php",
+            "version": "1.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/kleiram/transmission-php.git",
+                "reference": "5f7c715871154ea7a37af00d2dff52d8c5f6bdbc"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/kleiram/transmission-php/zipball/5f7c715871154ea7a37af00d2dff52d8c5f6bdbc",
+                "reference": "5f7c715871154ea7a37af00d2dff52d8c5f6bdbc",
+                "shasum": ""
+            },
+            "require": {
+                "kriswallsmith/buzz": ">=0.9",
+                "symfony/property-access": ">=2.2.1"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-0": {
+                    "Transmission": "lib/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-2-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Ramon Kleiss",
+                    "email": "ramon@cubilon.nl",
+                    "homepage": "http://cubilon.nl"
+                }
+            ],
+            "description": "PHP Transmission client",
+            "keywords": [
+                "download",
+                "torrent",
+                "transmission"
+            ],
+            "time": "2013-09-11T10:42:52+00:00"
+        },
+        {
+            "name": "kriswallsmith/buzz",
+            "version": "v0.16.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/kriswallsmith/Buzz.git",
+                "reference": "bd3299b7b6be3eeeb7a2842e677685031b93aa26"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/kriswallsmith/Buzz/zipball/bd3299b7b6be3eeeb7a2842e677685031b93aa26",
+                "reference": "bd3299b7b6be3eeeb7a2842e677685031b93aa26",
+                "shasum": ""
+            },
+            "require": {
+                "guzzlehttp/psr7": "^1.4",
+                "php": "^5.4 || ^7.0"
+            },
+            "require-dev": {
+                "php-http/client-integration-tests": "^0.6.2",
+                "symfony/phpunit-bridge": "^3.4 || ^4.0"
+            },
+            "suggest": {
+                "ext-curl": "*"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Buzz\\": "lib/Buzz"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Kris Wallsmith",
+                    "email": "kris.wallsmith@gmail.com",
+                    "homepage": "http://kriswallsmith.net/"
+                }
+            ],
+            "description": "Lightweight HTTP client",
+            "homepage": "https://github.com/kriswallsmith/Buzz",
+            "keywords": [
+                "curl",
+                "http client"
+            ],
+            "time": "2018-02-03T19:40:45+00:00"
+        },
         {
             "name": "lcobucci/jwt",
             "version": "3.2.2",
@@ -259,6 +417,56 @@
             "description": "PHPMailer is a full-featured email creation and transfer class for PHP",
             "time": "2018-01-05T13:19:58+00:00"
         },
+        {
+            "name": "psr/http-message",
+            "version": "1.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/http-message.git",
+                "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
+                "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Http\\Message\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "http://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interface for HTTP messages",
+            "homepage": "https://github.com/php-fig/http-message",
+            "keywords": [
+                "http",
+                "http-message",
+                "psr",
+                "psr-7",
+                "request",
+                "response"
+            ],
+            "time": "2016-08-06T14:39:51+00:00"
+        },
         {
             "name": "rmccue/requests",
             "version": "v1.7.0",
@@ -307,6 +515,130 @@
                 "sockets"
             ],
             "time": "2016-10-13T00:11:37+00:00"
+        },
+        {
+            "name": "symfony/inflector",
+            "version": "v4.0.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/inflector.git",
+                "reference": "da634a9968162f7c5c94f8d6949a4ede86085304"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/inflector/zipball/da634a9968162f7c5c94f8d6949a4ede86085304",
+                "reference": "da634a9968162f7c5c94f8d6949a4ede86085304",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.1.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "4.0-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\Inflector\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Bernhard Schussek",
+                    "email": "bschussek@gmail.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony Inflector Component",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "inflection",
+                "pluralize",
+                "singularize",
+                "string",
+                "symfony",
+                "words"
+            ],
+            "time": "2018-01-03T17:15:19+00:00"
+        },
+        {
+            "name": "symfony/property-access",
+            "version": "v4.0.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/property-access.git",
+                "reference": "e0fef10eb7e11cae9421d8d89024dfeae0acffb7"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/property-access/zipball/e0fef10eb7e11cae9421d8d89024dfeae0acffb7",
+                "reference": "e0fef10eb7e11cae9421d8d89024dfeae0acffb7",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.1.3",
+                "symfony/inflector": "~3.4|~4.0"
+            },
+            "require-dev": {
+                "symfony/cache": "~3.4|~4.0"
+            },
+            "suggest": {
+                "psr/cache-implementation": "To cache access methods."
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "4.0-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\PropertyAccess\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony PropertyAccess Component",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "access",
+                "array",
+                "extraction",
+                "index",
+                "injection",
+                "object",
+                "property",
+                "property path",
+                "reflection"
+            ],
+            "time": "2018-01-03T07:38:00+00:00"
         }
     ],
     "packages-dev": [],

+ 1 - 0
api/vendor/composer/autoload_files.php

@@ -6,5 +6,6 @@ $vendorDir = dirname(dirname(__FILE__));
 $baseDir = dirname($vendorDir);
 
 return array(
+    'a0edc8309cc5e1d60e3047b5df6b7052' => $vendorDir . '/guzzlehttp/psr7/src/functions_include.php',
     '0097ca414fcb37c7130ac24b05f485f8' => $vendorDir . '/dibi/dibi/src/loader.php',
 );

+ 1 - 0
api/vendor/composer/autoload_namespaces.php

@@ -6,5 +6,6 @@ $vendorDir = dirname(dirname(__FILE__));
 $baseDir = dirname($vendorDir);
 
 return array(
+    'Transmission' => array($vendorDir . '/kleiram/transmission-php/lib'),
     'Requests' => array($vendorDir . '/rmccue/requests/library'),
 );

+ 5 - 0
api/vendor/composer/autoload_psr4.php

@@ -6,7 +6,12 @@ $vendorDir = dirname(dirname(__FILE__));
 $baseDir = dirname($vendorDir);
 
 return array(
+    'Symfony\\Component\\PropertyAccess\\' => array($vendorDir . '/symfony/property-access'),
+    'Symfony\\Component\\Inflector\\' => array($vendorDir . '/symfony/inflector'),
+    'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src'),
     'PHPMailer\\PHPMailer\\' => array($vendorDir . '/phpmailer/phpmailer/src'),
     'Lcobucci\\JWT\\' => array($vendorDir . '/lcobucci/jwt/src'),
+    'GuzzleHttp\\Psr7\\' => array($vendorDir . '/guzzlehttp/psr7/src'),
     'Composer\\Semver\\' => array($vendorDir . '/composer/semver/src'),
+    'Buzz\\' => array($vendorDir . '/kriswallsmith/buzz/lib/Buzz'),
 );

+ 42 - 0
api/vendor/composer/autoload_static.php

@@ -7,25 +7,52 @@ namespace Composer\Autoload;
 class ComposerStaticInitcbdc783d76f8e7563dcce7d8af053ecb
 {
     public static $files = array (
+        'a0edc8309cc5e1d60e3047b5df6b7052' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/functions_include.php',
         '0097ca414fcb37c7130ac24b05f485f8' => __DIR__ . '/..' . '/dibi/dibi/src/loader.php',
     );
 
     public static $prefixLengthsPsr4 = array (
+        'S' => 
+        array (
+            'Symfony\\Component\\PropertyAccess\\' => 33,
+            'Symfony\\Component\\Inflector\\' => 28,
+        ),
         'P' => 
         array (
+            'Psr\\Http\\Message\\' => 17,
             'PHPMailer\\PHPMailer\\' => 20,
         ),
         'L' => 
         array (
             'Lcobucci\\JWT\\' => 13,
         ),
+        'G' => 
+        array (
+            'GuzzleHttp\\Psr7\\' => 16,
+        ),
         'C' => 
         array (
             'Composer\\Semver\\' => 16,
         ),
+        'B' => 
+        array (
+            'Buzz\\' => 5,
+        ),
     );
 
     public static $prefixDirsPsr4 = array (
+        'Symfony\\Component\\PropertyAccess\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/symfony/property-access',
+        ),
+        'Symfony\\Component\\Inflector\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/symfony/inflector',
+        ),
+        'Psr\\Http\\Message\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/psr/http-message/src',
+        ),
         'PHPMailer\\PHPMailer\\' => 
         array (
             0 => __DIR__ . '/..' . '/phpmailer/phpmailer/src',
@@ -34,13 +61,28 @@ class ComposerStaticInitcbdc783d76f8e7563dcce7d8af053ecb
         array (
             0 => __DIR__ . '/..' . '/lcobucci/jwt/src',
         ),
+        'GuzzleHttp\\Psr7\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/guzzlehttp/psr7/src',
+        ),
         'Composer\\Semver\\' => 
         array (
             0 => __DIR__ . '/..' . '/composer/semver/src',
         ),
+        'Buzz\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/kriswallsmith/buzz/lib/Buzz',
+        ),
     );
 
     public static $prefixesPsr0 = array (
+        'T' => 
+        array (
+            'Transmission' => 
+            array (
+                0 => __DIR__ . '/..' . '/kleiram/transmission-php/lib',
+            ),
+        ),
         'R' => 
         array (
             'Requests' => 

+ 344 - 0
api/vendor/composer/installed.json

@@ -310,5 +310,349 @@
             "iri",
             "sockets"
         ]
+    },
+    {
+        "name": "symfony/inflector",
+        "version": "v4.0.4",
+        "version_normalized": "4.0.4.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/symfony/inflector.git",
+            "reference": "da634a9968162f7c5c94f8d6949a4ede86085304"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/symfony/inflector/zipball/da634a9968162f7c5c94f8d6949a4ede86085304",
+            "reference": "da634a9968162f7c5c94f8d6949a4ede86085304",
+            "shasum": ""
+        },
+        "require": {
+            "php": "^7.1.3"
+        },
+        "time": "2018-01-03T17:15:19+00:00",
+        "type": "library",
+        "extra": {
+            "branch-alias": {
+                "dev-master": "4.0-dev"
+            }
+        },
+        "installation-source": "dist",
+        "autoload": {
+            "psr-4": {
+                "Symfony\\Component\\Inflector\\": ""
+            },
+            "exclude-from-classmap": [
+                "/Tests/"
+            ]
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "Bernhard Schussek",
+                "email": "bschussek@gmail.com"
+            },
+            {
+                "name": "Symfony Community",
+                "homepage": "https://symfony.com/contributors"
+            }
+        ],
+        "description": "Symfony Inflector Component",
+        "homepage": "https://symfony.com",
+        "keywords": [
+            "inflection",
+            "pluralize",
+            "singularize",
+            "string",
+            "symfony",
+            "words"
+        ]
+    },
+    {
+        "name": "symfony/property-access",
+        "version": "v4.0.4",
+        "version_normalized": "4.0.4.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/symfony/property-access.git",
+            "reference": "e0fef10eb7e11cae9421d8d89024dfeae0acffb7"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/symfony/property-access/zipball/e0fef10eb7e11cae9421d8d89024dfeae0acffb7",
+            "reference": "e0fef10eb7e11cae9421d8d89024dfeae0acffb7",
+            "shasum": ""
+        },
+        "require": {
+            "php": "^7.1.3",
+            "symfony/inflector": "~3.4|~4.0"
+        },
+        "require-dev": {
+            "symfony/cache": "~3.4|~4.0"
+        },
+        "suggest": {
+            "psr/cache-implementation": "To cache access methods."
+        },
+        "time": "2018-01-03T07:38:00+00:00",
+        "type": "library",
+        "extra": {
+            "branch-alias": {
+                "dev-master": "4.0-dev"
+            }
+        },
+        "installation-source": "dist",
+        "autoload": {
+            "psr-4": {
+                "Symfony\\Component\\PropertyAccess\\": ""
+            },
+            "exclude-from-classmap": [
+                "/Tests/"
+            ]
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "Fabien Potencier",
+                "email": "fabien@symfony.com"
+            },
+            {
+                "name": "Symfony Community",
+                "homepage": "https://symfony.com/contributors"
+            }
+        ],
+        "description": "Symfony PropertyAccess Component",
+        "homepage": "https://symfony.com",
+        "keywords": [
+            "access",
+            "array",
+            "extraction",
+            "index",
+            "injection",
+            "object",
+            "property",
+            "property path",
+            "reflection"
+        ]
+    },
+    {
+        "name": "psr/http-message",
+        "version": "1.0.1",
+        "version_normalized": "1.0.1.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/php-fig/http-message.git",
+            "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
+            "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
+            "shasum": ""
+        },
+        "require": {
+            "php": ">=5.3.0"
+        },
+        "time": "2016-08-06T14:39:51+00:00",
+        "type": "library",
+        "extra": {
+            "branch-alias": {
+                "dev-master": "1.0.x-dev"
+            }
+        },
+        "installation-source": "dist",
+        "autoload": {
+            "psr-4": {
+                "Psr\\Http\\Message\\": "src/"
+            }
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "PHP-FIG",
+                "homepage": "http://www.php-fig.org/"
+            }
+        ],
+        "description": "Common interface for HTTP messages",
+        "homepage": "https://github.com/php-fig/http-message",
+        "keywords": [
+            "http",
+            "http-message",
+            "psr",
+            "psr-7",
+            "request",
+            "response"
+        ]
+    },
+    {
+        "name": "guzzlehttp/psr7",
+        "version": "1.4.2",
+        "version_normalized": "1.4.2.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/guzzle/psr7.git",
+            "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/guzzle/psr7/zipball/f5b8a8512e2b58b0071a7280e39f14f72e05d87c",
+            "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c",
+            "shasum": ""
+        },
+        "require": {
+            "php": ">=5.4.0",
+            "psr/http-message": "~1.0"
+        },
+        "provide": {
+            "psr/http-message-implementation": "1.0"
+        },
+        "require-dev": {
+            "phpunit/phpunit": "~4.0"
+        },
+        "time": "2017-03-20T17:10:46+00:00",
+        "type": "library",
+        "extra": {
+            "branch-alias": {
+                "dev-master": "1.4-dev"
+            }
+        },
+        "installation-source": "dist",
+        "autoload": {
+            "psr-4": {
+                "GuzzleHttp\\Psr7\\": "src/"
+            },
+            "files": [
+                "src/functions_include.php"
+            ]
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "Michael Dowling",
+                "email": "mtdowling@gmail.com",
+                "homepage": "https://github.com/mtdowling"
+            },
+            {
+                "name": "Tobias Schultze",
+                "homepage": "https://github.com/Tobion"
+            }
+        ],
+        "description": "PSR-7 message implementation that also provides common utility methods",
+        "keywords": [
+            "http",
+            "message",
+            "request",
+            "response",
+            "stream",
+            "uri",
+            "url"
+        ]
+    },
+    {
+        "name": "kriswallsmith/buzz",
+        "version": "v0.16.0",
+        "version_normalized": "0.16.0.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/kriswallsmith/Buzz.git",
+            "reference": "bd3299b7b6be3eeeb7a2842e677685031b93aa26"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/kriswallsmith/Buzz/zipball/bd3299b7b6be3eeeb7a2842e677685031b93aa26",
+            "reference": "bd3299b7b6be3eeeb7a2842e677685031b93aa26",
+            "shasum": ""
+        },
+        "require": {
+            "guzzlehttp/psr7": "^1.4",
+            "php": "^5.4 || ^7.0"
+        },
+        "require-dev": {
+            "php-http/client-integration-tests": "^0.6.2",
+            "symfony/phpunit-bridge": "^3.4 || ^4.0"
+        },
+        "suggest": {
+            "ext-curl": "*"
+        },
+        "time": "2018-02-03T19:40:45+00:00",
+        "type": "library",
+        "installation-source": "dist",
+        "autoload": {
+            "psr-4": {
+                "Buzz\\": "lib/Buzz"
+            }
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "Kris Wallsmith",
+                "email": "kris.wallsmith@gmail.com",
+                "homepage": "http://kriswallsmith.net/"
+            }
+        ],
+        "description": "Lightweight HTTP client",
+        "homepage": "https://github.com/kriswallsmith/Buzz",
+        "keywords": [
+            "curl",
+            "http client"
+        ]
+    },
+    {
+        "name": "kleiram/transmission-php",
+        "version": "1.0.0",
+        "version_normalized": "1.0.0.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/kleiram/transmission-php.git",
+            "reference": "5f7c715871154ea7a37af00d2dff52d8c5f6bdbc"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/kleiram/transmission-php/zipball/5f7c715871154ea7a37af00d2dff52d8c5f6bdbc",
+            "reference": "5f7c715871154ea7a37af00d2dff52d8c5f6bdbc",
+            "shasum": ""
+        },
+        "require": {
+            "kriswallsmith/buzz": ">=0.9",
+            "symfony/property-access": ">=2.2.1"
+        },
+        "time": "2013-09-11T10:42:52+00:00",
+        "type": "library",
+        "installation-source": "dist",
+        "autoload": {
+            "psr-0": {
+                "Transmission": "lib/"
+            }
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "BSD-2-Clause"
+        ],
+        "authors": [
+            {
+                "name": "Ramon Kleiss",
+                "email": "ramon@cubilon.nl",
+                "homepage": "http://cubilon.nl"
+            }
+        ],
+        "description": "PHP Transmission client",
+        "keywords": [
+            "download",
+            "torrent",
+            "transmission"
+        ]
     }
 ]

+ 110 - 0
api/vendor/guzzlehttp/psr7/CHANGELOG.md

@@ -0,0 +1,110 @@
+# CHANGELOG
+
+## 1.4.2 - 2017-03-20
+
+* Reverted BC break to `Uri::resolve` and `Uri::removeDotSegments` by removing 
+  calls to `trigger_error` when deprecated methods are invoked.
+
+## 1.4.1 - 2017-02-27
+
+* Reverted BC break by reintroducing behavior to automagically fix a URI with a
+  relative path and an authority by adding a leading slash to the path. It's only
+  deprecated now.
+* Added triggering of silenced deprecation warnings.
+
+## 1.4.0 - 2017-02-21
+
+* Fix `Stream::read` when length parameter <= 0.
+* `copy_to_stream` reads bytes in chunks instead of `maxLen` into memory.
+* Fix `ServerRequest::getUriFromGlobals` when `Host` header contains port.
+* Ensure `ServerRequest::getUriFromGlobals` returns a URI in absolute form.
+* Allow `parse_response` to parse a response without delimiting space and reason.
+* Ensure each URI modification results in a valid URI according to PSR-7 discussions.
+  Invalid modifications will throw an exception instead of returning a wrong URI or
+  doing some magic.
+  - `(new Uri)->withPath('foo')->withHost('example.com')` will throw an exception
+    because the path of a URI with an authority must start with a slash "/" or be empty
+  - `(new Uri())->withScheme('http')` will return `'http://localhost'`
+* Fix compatibility of URIs with `file` scheme and empty host.
+* Added common URI utility methods based on RFC 3986 (see documentation in the readme):
+  - `Uri::isDefaultPort`
+  - `Uri::isAbsolute`
+  - `Uri::isNetworkPathReference`
+  - `Uri::isAbsolutePathReference`
+  - `Uri::isRelativePathReference`
+  - `Uri::isSameDocumentReference`
+  - `Uri::composeComponents`
+  - `UriNormalizer::normalize`
+  - `UriNormalizer::isEquivalent`
+  - `UriResolver::relativize`
+* Deprecated `Uri::resolve` in favor of `UriResolver::resolve`
+* Deprecated `Uri::removeDotSegments` in favor of `UriResolver::removeDotSegments`
+
+## 1.3.1 - 2016-06-25
+
+* Fix `Uri::__toString` for network path references, e.g. `//example.org`.
+* Fix missing lowercase normalization for host.
+* Fix handling of URI components in case they are `'0'` in a lot of places,
+  e.g. as a user info password.
+* Fix `Uri::withAddedHeader` to correctly merge headers with different case.
+* Fix trimming of header values in `Uri::withAddedHeader`. Header values may
+  be surrounded by whitespace which should be ignored according to RFC 7230
+  Section 3.2.4. This does not apply to header names.
+* Fix `Uri::withAddedHeader` with an array of header values.
+* Fix `Uri::resolve` when base path has no slash and handling of fragment.
+* Fix handling of encoding in `Uri::with(out)QueryValue` so one can pass the
+  key/value both in encoded as well as decoded form to those methods. This is
+  consistent with withPath, withQuery etc.
+* Fix `ServerRequest::withoutAttribute` when attribute value is null.
+
+## 1.3.0 - 2016-04-13
+
+* Added remaining interfaces needed for full PSR7 compatibility
+  (ServerRequestInterface, UploadedFileInterface, etc.).
+* Added support for stream_for from scalars.
+* Can now extend Uri.
+* Fixed a bug in validating request methods by making it more permissive.
+
+## 1.2.3 - 2016-02-18
+
+* Fixed support in `GuzzleHttp\Psr7\CachingStream` for seeking forward on remote
+  streams, which can sometimes return fewer bytes than requested with `fread`.
+* Fixed handling of gzipped responses with FNAME headers.
+
+## 1.2.2 - 2016-01-22
+
+* Added support for URIs without any authority.
+* Added support for HTTP 451 'Unavailable For Legal Reasons.'
+* Added support for using '0' as a filename.
+* Added support for including non-standard ports in Host headers.
+
+## 1.2.1 - 2015-11-02
+
+* Now supporting negative offsets when seeking to SEEK_END.
+
+## 1.2.0 - 2015-08-15
+
+* Body as `"0"` is now properly added to a response.
+* Now allowing forward seeking in CachingStream.
+* Now properly parsing HTTP requests that contain proxy targets in
+  `parse_request`.
+* functions.php is now conditionally required.
+* user-info is no longer dropped when resolving URIs.
+
+## 1.1.0 - 2015-06-24
+
+* URIs can now be relative.
+* `multipart/form-data` headers are now overridden case-insensitively.
+* URI paths no longer encode the following characters because they are allowed
+  in URIs: "(", ")", "*", "!", "'"
+* A port is no longer added to a URI when the scheme is missing and no port is
+  present.
+
+## 1.0.0 - 2015-05-19
+
+Initial release.
+
+Currently unsupported:
+
+- `Psr\Http\Message\ServerRequestInterface`
+- `Psr\Http\Message\UploadedFileInterface`

+ 19 - 0
api/vendor/guzzlehttp/psr7/LICENSE

@@ -0,0 +1,19 @@
+Copyright (c) 2015 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.

+ 739 - 0
api/vendor/guzzlehttp/psr7/README.md

@@ -0,0 +1,739 @@
+# PSR-7 Message Implementation
+
+This repository contains a full [PSR-7](http://www.php-fig.org/psr/psr-7/)
+message implementation, several stream decorators, and some helpful
+functionality like query string parsing.
+
+
+[![Build Status](https://travis-ci.org/guzzle/psr7.svg?branch=master)](https://travis-ci.org/guzzle/psr7)
+
+
+# Stream implementation
+
+This package comes with a number of stream implementations and stream
+decorators.
+
+
+## AppendStream
+
+`GuzzleHttp\Psr7\AppendStream`
+
+Reads from multiple streams, one after the other.
+
+```php
+use GuzzleHttp\Psr7;
+
+$a = Psr7\stream_for('abc, ');
+$b = Psr7\stream_for('123.');
+$composed = new Psr7\AppendStream([$a, $b]);
+
+$composed->addStream(Psr7\stream_for(' Above all listen to me'));
+
+echo $composed; // abc, 123. Above all listen to me.
+```
+
+
+## BufferStream
+
+`GuzzleHttp\Psr7\BufferStream`
+
+Provides a buffer stream that can be written to fill a buffer, and read
+from to remove bytes from the buffer.
+
+This stream returns a "hwm" metadata value that tells upstream consumers
+what the configured high water mark of the stream is, or the maximum
+preferred size of the buffer.
+
+```php
+use GuzzleHttp\Psr7;
+
+// When more than 1024 bytes are in the buffer, it will begin returning
+// false to writes. This is an indication that writers should slow down.
+$buffer = new Psr7\BufferStream(1024);
+```
+
+
+## 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.
+
+```php
+use GuzzleHttp\Psr7;
+
+$original = Psr7\stream_for(fopen('http://www.google.com', 'r'));
+$stream = new Psr7\CachingStream($original);
+
+$stream->read(1024);
+echo $stream->tell();
+// 1024
+
+$stream->seek(0);
+echo $stream->tell();
+// 0
+```
+
+
+## DroppingStream
+
+`GuzzleHttp\Psr7\DroppingStream`
+
+Stream decorator that begins dropping data once the size of the underlying
+stream becomes too full.
+
+```php
+use GuzzleHttp\Psr7;
+
+// Create an empty stream
+$stream = Psr7\stream_for();
+
+// Start dropping data when the stream has more than 10 bytes
+$dropping = new Psr7\DroppingStream($stream, 10);
+
+$dropping->write('01234567890123456789');
+echo $stream; // 0123456789
+```
+
+
+## FnStream
+
+`GuzzleHttp\Psr7\FnStream`
+
+Compose stream implementations based on a hash of functions.
+
+Allows for easy testing and extension of a provided stream without needing
+to create a concrete class for a simple extension point.
+
+```php
+
+use GuzzleHttp\Psr7;
+
+$stream = Psr7\stream_for('hi');
+$fnStream = Psr7\FnStream::decorate($stream, [
+    'rewind' => function () use ($stream) {
+        echo 'About to rewind - ';
+        $stream->rewind();
+        echo 'rewound!';
+    }
+]);
+
+$fnStream->rewind();
+// Outputs: About to rewind - rewound!
+```
+
+
+## InflateStream
+
+`GuzzleHttp\Psr7\InflateStream`
+
+Uses PHP's zlib.inflate filter to inflate deflate or gzipped content.
+
+This stream decorator skips the first 10 bytes of the given stream to remove
+the gzip header, converts the provided stream to a PHP stream resource,
+then appends the zlib.inflate filter. The stream is then converted back
+to a Guzzle stream resource to be used as a Guzzle stream.
+
+
+## LazyOpenStream
+
+`GuzzleHttp\Psr7\LazyOpenStream`
+
+Lazily reads or writes to a file that is opened only after an IO operation
+take place on the stream.
+
+```php
+use GuzzleHttp\Psr7;
+
+$stream = new Psr7\LazyOpenStream('/path/to/file', 'r');
+// The file has not yet been opened...
+
+echo $stream->read(10);
+// The file is opened and read from only when needed.
+```
+
+
+## LimitStream
+
+`GuzzleHttp\Psr7\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).
+
+```php
+use GuzzleHttp\Psr7;
+
+$original = Psr7\stream_for(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 Psr7\LimitStream($original, 1024, 2048);
+echo $stream->getSize();
+// >>> 1024
+echo $stream->tell();
+// >>> 0
+```
+
+
+## MultipartStream
+
+`GuzzleHttp\Psr7\MultipartStream`
+
+Stream that when read returns bytes for a streaming multipart or
+multipart/form-data stream.
+
+
+## NoSeekStream
+
+`GuzzleHttp\Psr7\NoSeekStream`
+
+NoSeekStream wraps a stream and does not allow seeking.
+
+```php
+use GuzzleHttp\Psr7;
+
+$original = Psr7\stream_for('foo');
+$noSeek = new Psr7\NoSeekStream($original);
+
+echo $noSeek->read(3);
+// foo
+var_export($noSeek->isSeekable());
+// false
+$noSeek->seek(0);
+var_export($noSeek->read(3));
+// NULL
+```
+
+
+## PumpStream
+
+`GuzzleHttp\Psr7\PumpStream`
+
+Provides a read only stream that pumps data from a PHP callable.
+
+When invoking the provided callable, the PumpStream will pass the amount of
+data requested to read to the callable. The callable can choose to ignore
+this value and return fewer or more bytes than requested. Any extra data
+returned by the provided callable is buffered internally until drained using
+the read() function of the PumpStream. The provided callable MUST return
+false when there is no more data to read.
+
+
+## Implementing stream decorators
+
+Creating a stream decorator is very easy thanks to the
+`GuzzleHttp\Psr7\StreamDecoratorTrait`. This trait provides methods that
+implement `Psr\Http\Message\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.
+
+```php
+use Psr\Http\Message\StreamInterface;
+use GuzzleHttp\Psr7\StreamDecoratorTrait;
+
+class EofCallbackStream implements StreamInterface
+{
+    use StreamDecoratorTrait;
+
+    private $callback;
+
+    public function __construct(StreamInterface $stream, callable $cb)
+    {
+        $this->stream = $stream;
+        $this->callback = $cb;
+    }
+
+    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:
+
+```php
+use GuzzleHttp\Psr7;
+
+$original = Psr7\stream_for('foo');
+
+$eofStream = new EofCallbackStream($original, function () {
+    echo 'EOF!';
+});
+
+$eofStream->read(2);
+$eofStream->read(1);
+// echoes "EOF!"
+$eofStream->seek(0);
+$eofStream->read(3);
+// echoes "EOF!"
+```
+
+
+## PHP StreamWrapper
+
+You can use the `GuzzleHttp\Psr7\StreamWrapper` class if you need to use a
+PSR-7 stream as a PHP stream resource.
+
+Use the `GuzzleHttp\Psr7\StreamWrapper::getResource()` method to create a PHP
+stream from a PSR-7 stream.
+
+```php
+use GuzzleHttp\Psr7\StreamWrapper;
+
+$stream = GuzzleHttp\Psr7\stream_for('hello!');
+$resource = StreamWrapper::getResource($stream);
+echo fread($resource, 6); // outputs hello!
+```
+
+
+# Function API
+
+There are various functions available under the `GuzzleHttp\Psr7` namespace.
+
+
+## `function str`
+
+`function str(MessageInterface $message)`
+
+Returns the string representation of an HTTP message.
+
+```php
+$request = new GuzzleHttp\Psr7\Request('GET', 'http://example.com');
+echo GuzzleHttp\Psr7\str($request);
+```
+
+
+## `function uri_for`
+
+`function uri_for($uri)`
+
+This function accepts a string or `Psr\Http\Message\UriInterface` and returns a
+UriInterface for the given value. If the value is already a `UriInterface`, it
+is returned as-is.
+
+```php
+$uri = GuzzleHttp\Psr7\uri_for('http://example.com');
+assert($uri === GuzzleHttp\Psr7\uri_for($uri));
+```
+
+
+## `function stream_for`
+
+`function stream_for($resource = '', array $options = [])`
+
+Create a new stream based on the input type.
+
+Options is an associative array that can contain the following keys:
+
+* - metadata: Array of custom metadata.
+* - size: Size of the stream.
+
+This method accepts the following `$resource` types:
+
+- `Psr\Http\Message\StreamInterface`: Returns the value as-is.
+- `string`: Creates a stream object that uses the given string as the contents.
+- `resource`: Creates a stream object that wraps the given PHP stream resource.
+- `Iterator`: If the provided value implements `Iterator`, then a read-only
+  stream object will be created that wraps the given iterable. Each time the
+  stream is read from, data from the iterator will fill a buffer and will be
+  continuously called until the buffer is equal to the requested read size.
+  Subsequent read calls will first read from the buffer and then call `next`
+  on the underlying iterator until it is exhausted.
+- `object` with `__toString()`: If the object has the `__toString()` method,
+  the object will be cast to a string and then a stream will be returned that
+  uses the string value.
+- `NULL`: When `null` is passed, an empty stream object is returned.
+- `callable` When a callable is passed, a read-only stream object will be
+  created that invokes the given callable. The callable is invoked with the
+  number of suggested bytes to read. The callable can return any number of
+  bytes, but MUST return `false` when there is no more data to return. The
+  stream object that wraps the callable will invoke the callable until the
+  number of requested bytes are available. Any additional bytes will be
+  buffered and used in subsequent reads.
+
+```php
+$stream = GuzzleHttp\Psr7\stream_for('foo');
+$stream = GuzzleHttp\Psr7\stream_for(fopen('/path/to/file', 'r'));
+
+$generator function ($bytes) {
+    for ($i = 0; $i < $bytes; $i++) {
+        yield ' ';
+    }
+}
+
+$stream = GuzzleHttp\Psr7\stream_for($generator(100));
+```
+
+
+## `function parse_header`
+
+`function parse_header($header)`
+
+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.
+
+
+## `function normalize_header`
+
+`function normalize_header($header)`
+
+Converts an array of header values that may contain comma separated headers
+into an array of headers with no comma separated values.
+
+
+## `function modify_request`
+
+`function modify_request(RequestInterface $request, array $changes)`
+
+Clone and modify a request with the given changes. This method is useful for
+reducing the number of clones needed to mutate a message.
+
+The changes can be one of:
+
+- method: (string) Changes the HTTP method.
+- set_headers: (array) Sets the given headers.
+- remove_headers: (array) Remove the given headers.
+- body: (mixed) Sets the given body.
+- uri: (UriInterface) Set the URI.
+- query: (string) Set the query string value of the URI.
+- version: (string) Set the protocol version.
+
+
+## `function rewind_body`
+
+`function rewind_body(MessageInterface $message)`
+
+Attempts to rewind a message body and throws an exception on failure. The body
+of the message will only be rewound if a call to `tell()` returns a value other
+than `0`.
+
+
+## `function try_fopen`
+
+`function try_fopen($filename, $mode)`
+
+Safely opens a PHP stream resource using a filename.
+
+When fopen fails, PHP normally raises a warning. This function adds an error
+handler that checks for errors and throws an exception instead.
+
+
+## `function copy_to_string`
+
+`function copy_to_string(StreamInterface $stream, $maxLen = -1)`
+
+Copy the contents of a stream into a string until the given number of bytes
+have been read.
+
+
+## `function copy_to_stream`
+
+`function copy_to_stream(StreamInterface $source, StreamInterface $dest, $maxLen = -1)`
+
+Copy the contents of a stream into another stream until the given number of
+bytes have been read.
+
+
+## `function hash`
+
+`function hash(StreamInterface $stream, $algo, $rawOutput = false)`
+
+Calculate a hash of a Stream. This method reads the entire stream to calculate
+a rolling hash (based on PHP's hash_init functions).
+
+
+## `function readline`
+
+`function readline(StreamInterface $stream, $maxLength = null)`
+
+Read a line from the stream up to the maximum allowed buffer length.
+
+
+## `function parse_request`
+
+`function parse_request($message)`
+
+Parses a request message string into a request object.
+
+
+## `function parse_response`
+
+`function parse_response($message)`
+
+Parses a response message string into a response object.
+
+
+## `function parse_query`
+
+`function parse_query($str, $urlEncoding = true)`
+
+Parse a query string into an associative array.
+
+If multiple values are found for the same key, the value of that key value pair
+will become an array. This function does not parse nested PHP style arrays into
+an associative array (e.g., `foo[a]=1&foo[b]=2` will be parsed into
+`['foo[a]' => '1', 'foo[b]' => '2']`).
+
+
+## `function build_query`
+
+`function build_query(array $params, $encoding = PHP_QUERY_RFC3986)`
+
+Build a query string from an array of key value pairs.
+
+This function can use the return value of parse_query() to build a query string.
+This function does not modify the provided keys when an array is encountered
+(like http_build_query would).
+
+
+## `function mimetype_from_filename`
+
+`function mimetype_from_filename($filename)`
+
+Determines the mimetype of a file by looking at its extension.
+
+
+## `function mimetype_from_extension`
+
+`function mimetype_from_extension($extension)`
+
+Maps a file extensions to a mimetype.
+
+
+# Additional URI Methods
+
+Aside from the standard `Psr\Http\Message\UriInterface` implementation in form of the `GuzzleHttp\Psr7\Uri` class,
+this library also provides additional functionality when working with URIs as static methods.
+
+## URI Types
+
+An instance of `Psr\Http\Message\UriInterface` can either be an absolute URI or a relative reference.
+An absolute URI has a scheme. A relative reference is used to express a URI relative to another URI,
+the base URI. Relative references can be divided into several forms according to
+[RFC 3986 Section 4.2](https://tools.ietf.org/html/rfc3986#section-4.2):
+
+- network-path references, e.g. `//example.com/path`
+- absolute-path references, e.g. `/path`
+- relative-path references, e.g. `subpath`
+
+The following methods can be used to identify the type of the URI.
+
+### `GuzzleHttp\Psr7\Uri::isAbsolute`
+
+`public static function isAbsolute(UriInterface $uri): bool`
+
+Whether the URI is absolute, i.e. it has a scheme.
+
+### `GuzzleHttp\Psr7\Uri::isNetworkPathReference`
+
+`public static function isNetworkPathReference(UriInterface $uri): bool`
+
+Whether the URI is a network-path reference. A relative reference that begins with two slash characters is
+termed an network-path reference.
+
+### `GuzzleHttp\Psr7\Uri::isAbsolutePathReference`
+
+`public static function isAbsolutePathReference(UriInterface $uri): bool`
+
+Whether the URI is a absolute-path reference. A relative reference that begins with a single slash character is
+termed an absolute-path reference.
+
+### `GuzzleHttp\Psr7\Uri::isRelativePathReference`
+
+`public static function isRelativePathReference(UriInterface $uri): bool`
+
+Whether the URI is a relative-path reference. A relative reference that does not begin with a slash character is
+termed a relative-path reference.
+
+### `GuzzleHttp\Psr7\Uri::isSameDocumentReference`
+
+`public static function isSameDocumentReference(UriInterface $uri, UriInterface $base = null): bool`
+
+Whether the URI is a same-document reference. A same-document reference refers to a URI that is, aside from its
+fragment component, identical to the base URI. When no base URI is given, only an empty URI reference
+(apart from its fragment) is considered a same-document reference.
+
+## URI Components
+
+Additional methods to work with URI components.
+
+### `GuzzleHttp\Psr7\Uri::isDefaultPort`
+
+`public static function isDefaultPort(UriInterface $uri): bool`
+
+Whether the URI has the default port of the current scheme. `Psr\Http\Message\UriInterface::getPort` may return null
+or the standard port. This method can be used independently of the implementation.
+
+### `GuzzleHttp\Psr7\Uri::composeComponents`
+
+`public static function composeComponents($scheme, $authority, $path, $query, $fragment): string`
+
+Composes a URI reference string from its various components according to
+[RFC 3986 Section 5.3](https://tools.ietf.org/html/rfc3986#section-5.3). Usually this method does not need to be called
+manually but instead is used indirectly via `Psr\Http\Message\UriInterface::__toString`.
+
+### `GuzzleHttp\Psr7\Uri::fromParts`
+
+`public static function fromParts(array $parts): UriInterface`
+
+Creates a URI from a hash of [`parse_url`](http://php.net/manual/en/function.parse-url.php) components.
+
+
+### `GuzzleHttp\Psr7\Uri::withQueryValue`
+
+`public static function withQueryValue(UriInterface $uri, $key, $value): UriInterface`
+
+Creates a new URI with a specific query string value. Any existing query string values that exactly match the
+provided key are removed and replaced with the given key value pair. A value of null will set the query string
+key without a value, e.g. "key" instead of "key=value".
+
+
+### `GuzzleHttp\Psr7\Uri::withoutQueryValue`
+
+`public static function withoutQueryValue(UriInterface $uri, $key): UriInterface`
+
+Creates a new URI with a specific query string value removed. Any existing query string values that exactly match the
+provided key are removed.
+
+## Reference Resolution
+
+`GuzzleHttp\Psr7\UriResolver` provides methods to resolve a URI reference in the context of a base URI according
+to [RFC 3986 Section 5](https://tools.ietf.org/html/rfc3986#section-5). This is for example also what web browsers
+do when resolving a link in a website based on the current request URI.
+
+### `GuzzleHttp\Psr7\UriResolver::resolve`
+
+`public static function resolve(UriInterface $base, UriInterface $rel): UriInterface`
+
+Converts the relative URI into a new URI that is resolved against the base URI.
+
+### `GuzzleHttp\Psr7\UriResolver::removeDotSegments`
+
+`public static function removeDotSegments(string $path): string`
+
+Removes dot segments from a path and returns the new path according to
+[RFC 3986 Section 5.2.4](https://tools.ietf.org/html/rfc3986#section-5.2.4).
+
+### `GuzzleHttp\Psr7\UriResolver::relativize`
+
+`public static function relativize(UriInterface $base, UriInterface $target): UriInterface`
+
+Returns the target URI as a relative reference from the base URI. This method is the counterpart to resolve():
+
+```php
+(string) $target === (string) UriResolver::resolve($base, UriResolver::relativize($base, $target))
+```
+
+One use-case is to use the current request URI as base URI and then generate relative links in your documents
+to reduce the document size or offer self-contained downloadable document archives.
+
+```php
+$base = new Uri('http://example.com/a/b/');
+echo UriResolver::relativize($base, new Uri('http://example.com/a/b/c'));  // prints 'c'.
+echo UriResolver::relativize($base, new Uri('http://example.com/a/x/y'));  // prints '../x/y'.
+echo UriResolver::relativize($base, new Uri('http://example.com/a/b/?q')); // prints '?q'.
+echo UriResolver::relativize($base, new Uri('http://example.org/a/b/'));   // prints '//example.org/a/b/'.
+```
+
+## Normalization and Comparison
+
+`GuzzleHttp\Psr7\UriNormalizer` provides methods to normalize and compare URIs according to
+[RFC 3986 Section 6](https://tools.ietf.org/html/rfc3986#section-6).
+
+### `GuzzleHttp\Psr7\UriNormalizer::normalize`
+
+`public static function normalize(UriInterface $uri, $flags = self::PRESERVING_NORMALIZATIONS): UriInterface`
+
+Returns a normalized URI. The scheme and host component are already normalized to lowercase per PSR-7 UriInterface.
+This methods adds additional normalizations that can be configured with the `$flags` parameter which is a bitmask
+of normalizations to apply. The following normalizations are available:
+
+- `UriNormalizer::PRESERVING_NORMALIZATIONS`
+
+    Default normalizations which only include the ones that preserve semantics.
+
+- `UriNormalizer::CAPITALIZE_PERCENT_ENCODING`
+
+    All letters within a percent-encoding triplet (e.g., "%3A") are case-insensitive, and should be capitalized.
+
+    Example: `http://example.org/a%c2%b1b` → `http://example.org/a%C2%B1b`
+
+- `UriNormalizer::DECODE_UNRESERVED_CHARACTERS`
+
+    Decodes percent-encoded octets of unreserved characters. For consistency, percent-encoded octets in the ranges of
+    ALPHA (%41–%5A and %61–%7A), DIGIT (%30–%39), hyphen (%2D), period (%2E), underscore (%5F), or tilde (%7E) should
+    not be created by URI producers and, when found in a URI, should be decoded to their corresponding unreserved
+    characters by URI normalizers.
+
+    Example: `http://example.org/%7Eusern%61me/` → `http://example.org/~username/`
+
+- `UriNormalizer::CONVERT_EMPTY_PATH`
+
+    Converts the empty path to "/" for http and https URIs.
+
+    Example: `http://example.org` → `http://example.org/`
+
+- `UriNormalizer::REMOVE_DEFAULT_HOST`
+
+    Removes the default host of the given URI scheme from the URI. Only the "file" scheme defines the default host
+    "localhost". All of `file:/myfile`, `file:///myfile`, and `file://localhost/myfile` are equivalent according to
+    RFC 3986.
+
+    Example: `file://localhost/myfile` → `file:///myfile`
+
+- `UriNormalizer::REMOVE_DEFAULT_PORT`
+
+    Removes the default port of the given URI scheme from the URI.
+
+    Example: `http://example.org:80/` → `http://example.org/`
+
+- `UriNormalizer::REMOVE_DOT_SEGMENTS`
+
+    Removes unnecessary dot-segments. Dot-segments in relative-path references are not removed as it would
+    change the semantics of the URI reference.
+
+    Example: `http://example.org/../a/b/../c/./d.html` → `http://example.org/a/c/d.html`
+
+- `UriNormalizer::REMOVE_DUPLICATE_SLASHES`
+
+    Paths which include two or more adjacent slashes are converted to one. Webservers usually ignore duplicate slashes
+    and treat those URIs equivalent. But in theory those URIs do not need to be equivalent. So this normalization
+    may change the semantics. Encoded slashes (%2F) are not removed.
+
+    Example: `http://example.org//foo///bar.html` → `http://example.org/foo/bar.html`
+
+- `UriNormalizer::SORT_QUERY_PARAMETERS`
+
+    Sort query parameters with their values in alphabetical order. However, the order of parameters in a URI may be
+    significant (this is not defined by the standard). So this normalization is not safe and may change the semantics
+    of the URI.
+
+    Example: `?lang=en&article=fred` → `?article=fred&lang=en`
+
+### `GuzzleHttp\Psr7\UriNormalizer::isEquivalent`
+
+`public static function isEquivalent(UriInterface $uri1, UriInterface $uri2, $normalizations = self::PRESERVING_NORMALIZATIONS): bool`
+
+Whether two URIs can be considered equivalent. Both URIs are normalized automatically before comparison with the given
+`$normalizations` bitmask. The method also accepts relative URI references and returns true when they are equivalent.
+This of course assumes they will be resolved against the same base URI. If this is not the case, determination of
+equivalence or difference of relative references does not mean anything.

+ 39 - 0
api/vendor/guzzlehttp/psr7/composer.json

@@ -0,0 +1,39 @@
+{
+    "name": "guzzlehttp/psr7",
+    "type": "library",
+    "description": "PSR-7 message implementation that also provides common utility methods",
+    "keywords": ["request", "response", "message", "stream", "http", "uri", "url"],
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "Michael Dowling",
+            "email": "mtdowling@gmail.com",
+            "homepage": "https://github.com/mtdowling"
+        },
+        {
+            "name": "Tobias Schultze",
+            "homepage": "https://github.com/Tobion"
+        }
+    ],
+    "require": {
+        "php": ">=5.4.0",
+        "psr/http-message": "~1.0"
+    },
+    "require-dev": {
+        "phpunit/phpunit": "~4.0"
+    },
+    "provide": {
+        "psr/http-message-implementation": "1.0"
+    },
+    "autoload": {
+        "psr-4": {
+            "GuzzleHttp\\Psr7\\": "src/"
+        },
+        "files": ["src/functions_include.php"]
+    },
+    "extra": {
+        "branch-alias": {
+            "dev-master": "1.4-dev"
+        }
+    }
+}

+ 233 - 0
api/vendor/guzzlehttp/psr7/src/AppendStream.php

@@ -0,0 +1,233 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * Reads from multiple streams, one after the other.
+ *
+ * This is a read-only stream decorator.
+ */
+class AppendStream implements StreamInterface
+{
+    /** @var StreamInterface[] Streams being decorated */
+    private $streams = [];
+
+    private $seekable = true;
+    private $current = 0;
+    private $pos = 0;
+    private $detached = false;
+
+    /**
+     * @param StreamInterface[] $streams Streams to decorate. Each stream must
+     *                                   be readable.
+     */
+    public function __construct(array $streams = [])
+    {
+        foreach ($streams as $stream) {
+            $this->addStream($stream);
+        }
+    }
+
+    public function __toString()
+    {
+        try {
+            $this->rewind();
+            return $this->getContents();
+        } catch (\Exception $e) {
+            return '';
+        }
+    }
+
+    /**
+     * Add a stream to the AppendStream
+     *
+     * @param StreamInterface $stream Stream to append. Must be readable.
+     *
+     * @throws \InvalidArgumentException if the stream is not readable
+     */
+    public function addStream(StreamInterface $stream)
+    {
+        if (!$stream->isReadable()) {
+            throw new \InvalidArgumentException('Each stream must be readable');
+        }
+
+        // The stream is only seekable if all streams are seekable
+        if (!$stream->isSeekable()) {
+            $this->seekable = false;
+        }
+
+        $this->streams[] = $stream;
+    }
+
+    public function getContents()
+    {
+        return copy_to_string($this);
+    }
+
+    /**
+     * Closes each attached stream.
+     *
+     * {@inheritdoc}
+     */
+    public function close()
+    {
+        $this->pos = $this->current = 0;
+
+        foreach ($this->streams as $stream) {
+            $stream->close();
+        }
+
+        $this->streams = [];
+    }
+
+    /**
+     * Detaches each attached stream
+     *
+     * {@inheritdoc}
+     */
+    public function detach()
+    {
+        $this->close();
+        $this->detached = true;
+    }
+
+    public function tell()
+    {
+        return $this->pos;
+    }
+
+    /**
+     * Tries to calculate the size by adding the size of each stream.
+     *
+     * If any of the streams do not return a valid number, then the size of the
+     * append stream cannot be determined and null is returned.
+     *
+     * {@inheritdoc}
+     */
+    public function getSize()
+    {
+        $size = 0;
+
+        foreach ($this->streams as $stream) {
+            $s = $stream->getSize();
+            if ($s === null) {
+                return null;
+            }
+            $size += $s;
+        }
+
+        return $size;
+    }
+
+    public function eof()
+    {
+        return !$this->streams ||
+            ($this->current >= count($this->streams) - 1 &&
+             $this->streams[$this->current]->eof());
+    }
+
+    public function rewind()
+    {
+        $this->seek(0);
+    }
+
+    /**
+     * Attempts to seek to the given position. Only supports SEEK_SET.
+     *
+     * {@inheritdoc}
+     */
+    public function seek($offset, $whence = SEEK_SET)
+    {
+        if (!$this->seekable) {
+            throw new \RuntimeException('This AppendStream is not seekable');
+        } elseif ($whence !== SEEK_SET) {
+            throw new \RuntimeException('The AppendStream can only seek with SEEK_SET');
+        }
+
+        $this->pos = $this->current = 0;
+
+        // Rewind each stream
+        foreach ($this->streams as $i => $stream) {
+            try {
+                $stream->rewind();
+            } catch (\Exception $e) {
+                throw new \RuntimeException('Unable to seek stream '
+                    . $i . ' of the AppendStream', 0, $e);
+            }
+        }
+
+        // Seek to the actual position by reading from each stream
+        while ($this->pos < $offset && !$this->eof()) {
+            $result = $this->read(min(8096, $offset - $this->pos));
+            if ($result === '') {
+                break;
+            }
+        }
+    }
+
+    /**
+     * Reads from all of the appended streams until the length is met or EOF.
+     *
+     * {@inheritdoc}
+     */
+    public function read($length)
+    {
+        $buffer = '';
+        $total = count($this->streams) - 1;
+        $remaining = $length;
+        $progressToNext = false;
+
+        while ($remaining > 0) {
+
+            // Progress to the next stream if needed.
+            if ($progressToNext || $this->streams[$this->current]->eof()) {
+                $progressToNext = false;
+                if ($this->current === $total) {
+                    break;
+                }
+                $this->current++;
+            }
+
+            $result = $this->streams[$this->current]->read($remaining);
+
+            // Using a loose comparison here to match on '', false, and null
+            if ($result == null) {
+                $progressToNext = true;
+                continue;
+            }
+
+            $buffer .= $result;
+            $remaining = $length - strlen($buffer);
+        }
+
+        $this->pos += strlen($buffer);
+
+        return $buffer;
+    }
+
+    public function isReadable()
+    {
+        return true;
+    }
+
+    public function isWritable()
+    {
+        return false;
+    }
+
+    public function isSeekable()
+    {
+        return $this->seekable;
+    }
+
+    public function write($string)
+    {
+        throw new \RuntimeException('Cannot write to an AppendStream');
+    }
+
+    public function getMetadata($key = null)
+    {
+        return $key ? null : [];
+    }
+}

+ 137 - 0
api/vendor/guzzlehttp/psr7/src/BufferStream.php

@@ -0,0 +1,137 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * Provides a buffer stream that can be written to to fill a buffer, and read
+ * from to remove bytes from the buffer.
+ *
+ * This stream returns a "hwm" metadata value that tells upstream consumers
+ * what the configured high water mark of the stream is, or the maximum
+ * preferred size of the buffer.
+ */
+class BufferStream implements StreamInterface
+{
+    private $hwm;
+    private $buffer = '';
+
+    /**
+     * @param int $hwm High water mark, representing the preferred maximum
+     *                 buffer size. If the size of the buffer exceeds the high
+     *                 water mark, then calls to write will continue to succeed
+     *                 but will return false to inform writers to slow down
+     *                 until the buffer has been drained by reading from it.
+     */
+    public function __construct($hwm = 16384)
+    {
+        $this->hwm = $hwm;
+    }
+
+    public function __toString()
+    {
+        return $this->getContents();
+    }
+
+    public function getContents()
+    {
+        $buffer = $this->buffer;
+        $this->buffer = '';
+
+        return $buffer;
+    }
+
+    public function close()
+    {
+        $this->buffer = '';
+    }
+
+    public function detach()
+    {
+        $this->close();
+    }
+
+    public function getSize()
+    {
+        return strlen($this->buffer);
+    }
+
+    public function isReadable()
+    {
+        return true;
+    }
+
+    public function isWritable()
+    {
+        return true;
+    }
+
+    public function isSeekable()
+    {
+        return false;
+    }
+
+    public function rewind()
+    {
+        $this->seek(0);
+    }
+
+    public function seek($offset, $whence = SEEK_SET)
+    {
+        throw new \RuntimeException('Cannot seek a BufferStream');
+    }
+
+    public function eof()
+    {
+        return strlen($this->buffer) === 0;
+    }
+
+    public function tell()
+    {
+        throw new \RuntimeException('Cannot determine the position of a BufferStream');
+    }
+
+    /**
+     * Reads data from the buffer.
+     */
+    public function read($length)
+    {
+        $currentLength = strlen($this->buffer);
+
+        if ($length >= $currentLength) {
+            // No need to slice the buffer because we don't have enough data.
+            $result = $this->buffer;
+            $this->buffer = '';
+        } else {
+            // Slice up the result to provide a subset of the buffer.
+            $result = substr($this->buffer, 0, $length);
+            $this->buffer = substr($this->buffer, $length);
+        }
+
+        return $result;
+    }
+
+    /**
+     * Writes data to the buffer.
+     */
+    public function write($string)
+    {
+        $this->buffer .= $string;
+
+        // TODO: What should happen here?
+        if (strlen($this->buffer) >= $this->hwm) {
+            return false;
+        }
+
+        return strlen($string);
+    }
+
+    public function getMetadata($key = null)
+    {
+        if ($key == 'hwm') {
+            return $this->hwm;
+        }
+
+        return $key ? null : [];
+    }
+}

+ 138 - 0
api/vendor/guzzlehttp/psr7/src/CachingStream.php

@@ -0,0 +1,138 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * Stream decorator that can cache previously read bytes from a sequentially
+ * read stream.
+ */
+class CachingStream implements StreamInterface
+{
+    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());
+    }
+
+    public function rewind()
+    {
+        $this->seek(0);
+    }
+
+    public function seek($offset, $whence = SEEK_SET)
+    {
+        if ($whence == SEEK_SET) {
+            $byte = $offset;
+        } elseif ($whence == SEEK_CUR) {
+            $byte = $offset + $this->tell();
+        } elseif ($whence == SEEK_END) {
+            $size = $this->remoteStream->getSize();
+            if ($size === null) {
+                $size = $this->cacheEntireStream();
+            }
+            $byte = $size + $offset;
+        } else {
+            throw new \InvalidArgumentException('Invalid whence');
+        }
+
+        $diff = $byte - $this->stream->getSize();
+
+        if ($diff > 0) {
+            // Read the remoteStream until we have read in at least the amount
+            // of bytes requested, or we reach the end of the file.
+            while ($diff > 0 && !$this->remoteStream->eof()) {
+                $this->read($diff);
+                $diff = $byte - $this->stream->getSize();
+            }
+        } else {
+            // We can just do a normal seek since we've already seen this byte.
+            $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();
+    }
+
+    private function cacheEntireStream()
+    {
+        $target = new FnStream(['write' => 'strlen']);
+        copy_to_stream($this, $target);
+
+        return $this->tell();
+    }
+}

+ 42 - 0
api/vendor/guzzlehttp/psr7/src/DroppingStream.php

@@ -0,0 +1,42 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * Stream decorator that begins dropping data once the size of the underlying
+ * stream becomes too full.
+ */
+class DroppingStream implements StreamInterface
+{
+    use StreamDecoratorTrait;
+
+    private $maxLength;
+
+    /**
+     * @param StreamInterface $stream    Underlying stream to decorate.
+     * @param int             $maxLength Maximum size before dropping data.
+     */
+    public function __construct(StreamInterface $stream, $maxLength)
+    {
+        $this->stream = $stream;
+        $this->maxLength = $maxLength;
+    }
+
+    public function write($string)
+    {
+        $diff = $this->maxLength - $this->stream->getSize();
+
+        // Begin returning 0 when the underlying stream is too large.
+        if ($diff <= 0) {
+            return 0;
+        }
+
+        // Write the stream or a subset of the stream if needed.
+        if (strlen($string) < $diff) {
+            return $this->stream->write($string);
+        }
+
+        return $this->stream->write(substr($string, 0, $diff));
+    }
+}

+ 149 - 0
api/vendor/guzzlehttp/psr7/src/FnStream.php

@@ -0,0 +1,149 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * Compose stream implementations based on a hash of functions.
+ *
+ * Allows for easy testing and extension of a provided stream without needing
+ * to create a concrete class for a simple extension point.
+ */
+class FnStream implements StreamInterface
+{
+    /** @var array */
+    private $methods;
+
+    /** @var array Methods that must be implemented in the given array */
+    private static $slots = ['__toString', 'close', 'detach', 'rewind',
+        'getSize', 'tell', 'eof', 'isSeekable', 'seek', 'isWritable', 'write',
+        'isReadable', 'read', 'getContents', 'getMetadata'];
+
+    /**
+     * @param array $methods Hash of method name to a callable.
+     */
+    public function __construct(array $methods)
+    {
+        $this->methods = $methods;
+
+        // Create the functions on the class
+        foreach ($methods as $name => $fn) {
+            $this->{'_fn_' . $name} = $fn;
+        }
+    }
+
+    /**
+     * Lazily determine which methods are not implemented.
+     * @throws \BadMethodCallException
+     */
+    public function __get($name)
+    {
+        throw new \BadMethodCallException(str_replace('_fn_', '', $name)
+            . '() is not implemented in the FnStream');
+    }
+
+    /**
+     * The close method is called on the underlying stream only if possible.
+     */
+    public function __destruct()
+    {
+        if (isset($this->_fn_close)) {
+            call_user_func($this->_fn_close);
+        }
+    }
+
+    /**
+     * Adds custom functionality to an underlying stream by intercepting
+     * specific method calls.
+     *
+     * @param StreamInterface $stream  Stream to decorate
+     * @param array           $methods Hash of method name to a closure
+     *
+     * @return FnStream
+     */
+    public static function decorate(StreamInterface $stream, array $methods)
+    {
+        // If any of the required methods were not provided, then simply
+        // proxy to the decorated stream.
+        foreach (array_diff(self::$slots, array_keys($methods)) as $diff) {
+            $methods[$diff] = [$stream, $diff];
+        }
+
+        return new self($methods);
+    }
+
+    public function __toString()
+    {
+        return call_user_func($this->_fn___toString);
+    }
+
+    public function close()
+    {
+        return call_user_func($this->_fn_close);
+    }
+
+    public function detach()
+    {
+        return call_user_func($this->_fn_detach);
+    }
+
+    public function getSize()
+    {
+        return call_user_func($this->_fn_getSize);
+    }
+
+    public function tell()
+    {
+        return call_user_func($this->_fn_tell);
+    }
+
+    public function eof()
+    {
+        return call_user_func($this->_fn_eof);
+    }
+
+    public function isSeekable()
+    {
+        return call_user_func($this->_fn_isSeekable);
+    }
+
+    public function rewind()
+    {
+        call_user_func($this->_fn_rewind);
+    }
+
+    public function seek($offset, $whence = SEEK_SET)
+    {
+        call_user_func($this->_fn_seek, $offset, $whence);
+    }
+
+    public function isWritable()
+    {
+        return call_user_func($this->_fn_isWritable);
+    }
+
+    public function write($string)
+    {
+        return call_user_func($this->_fn_write, $string);
+    }
+
+    public function isReadable()
+    {
+        return call_user_func($this->_fn_isReadable);
+    }
+
+    public function read($length)
+    {
+        return call_user_func($this->_fn_read, $length);
+    }
+
+    public function getContents()
+    {
+        return call_user_func($this->_fn_getContents);
+    }
+
+    public function getMetadata($key = null)
+    {
+        return call_user_func($this->_fn_getMetadata, $key);
+    }
+}

+ 52 - 0
api/vendor/guzzlehttp/psr7/src/InflateStream.php

@@ -0,0 +1,52 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * Uses PHP's zlib.inflate filter to inflate deflate or gzipped content.
+ *
+ * This stream decorator skips the first 10 bytes of the given stream to remove
+ * the gzip header, converts the provided stream to a PHP stream resource,
+ * then appends the zlib.inflate filter. The stream is then converted back
+ * to a Guzzle stream resource to be used as a Guzzle stream.
+ *
+ * @link http://tools.ietf.org/html/rfc1952
+ * @link http://php.net/manual/en/filters.compression.php
+ */
+class InflateStream implements StreamInterface
+{
+    use StreamDecoratorTrait;
+
+    public function __construct(StreamInterface $stream)
+    {
+        // read the first 10 bytes, ie. gzip header
+        $header = $stream->read(10);
+        $filenameHeaderLength = $this->getLengthOfPossibleFilenameHeader($stream, $header);
+        // Skip the header, that is 10 + length of filename + 1 (nil) bytes
+        $stream = new LimitStream($stream, -1, 10 + $filenameHeaderLength);
+        $resource = StreamWrapper::getResource($stream);
+        stream_filter_append($resource, 'zlib.inflate', STREAM_FILTER_READ);
+        $this->stream = new Stream($resource);
+    }
+
+    /**
+     * @param StreamInterface $stream
+     * @param $header
+     * @return int
+     */
+    private function getLengthOfPossibleFilenameHeader(StreamInterface $stream, $header)
+    {
+        $filename_header_length = 0;
+
+        if (substr(bin2hex($header), 6, 2) === '08') {
+            // we have a filename, read until nil
+            $filename_header_length = 1;
+            while ($stream->read(1) !== chr(0)) {
+                $filename_header_length++;
+            }
+        }
+
+        return $filename_header_length;
+    }
+}

+ 39 - 0
api/vendor/guzzlehttp/psr7/src/LazyOpenStream.php

@@ -0,0 +1,39 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * Lazily reads or writes to a file that is opened only after an IO operation
+ * take place on the stream.
+ */
+class LazyOpenStream implements StreamInterface
+{
+    use StreamDecoratorTrait;
+
+    /** @var string File to open */
+    private $filename;
+
+    /** @var string $mode */
+    private $mode;
+
+    /**
+     * @param string $filename File to lazily open
+     * @param string $mode     fopen mode to use when opening the stream
+     */
+    public function __construct($filename, $mode)
+    {
+        $this->filename = $filename;
+        $this->mode = $mode;
+    }
+
+    /**
+     * Creates the underlying stream lazily when required.
+     *
+     * @return StreamInterface
+     */
+    protected function createStream()
+    {
+        return stream_for(try_fopen($this->filename, $this->mode));
+    }
+}

+ 155 - 0
api/vendor/guzzlehttp/psr7/src/LimitStream.php

@@ -0,0 +1,155 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+
+/**
+ * Decorator used to return only a subset of a stream
+ */
+class LimitStream implements StreamInterface
+{
+    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             $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()
+    {
+        // Always return true if the underlying stream is EOF
+        if ($this->stream->eof()) {
+            return true;
+        }
+
+        // No limit and the underlying stream is not at EOF
+        if ($this->limit == -1) {
+            return false;
+        }
+
+        return $this->stream->tell() >= $this->offset + $this->limit;
+    }
+
+    /**
+     * 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 || $offset < 0) {
+            throw new \RuntimeException(sprintf(
+                'Cannot seek to offset % with whence %s',
+                $offset,
+                $whence
+            ));
+        }
+
+        $offset += $this->offset;
+
+        if ($this->limit !== -1) {
+            if ($offset > $this->offset + $this->limit) {
+                $offset = $this->offset + $this->limit;
+            }
+        }
+
+        $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
+     *
+     * @throws \RuntimeException if the stream cannot be seeked.
+     */
+    public function setOffset($offset)
+    {
+        $current = $this->stream->tell();
+
+        if ($current !== $offset) {
+            // If the stream cannot seek to the offset position, then read to it
+            if ($this->stream->isSeekable()) {
+                $this->stream->seek($offset);
+            } elseif ($current > $offset) {
+                throw new \RuntimeException("Could not seek to stream offset $offset");
+            } else {
+                $this->stream->read($offset - $current);
+            }
+        }
+
+        $this->offset = $offset;
+    }
+
+    /**
+     * 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.
+     */
+    public function setLimit($limit)
+    {
+        $this->limit = $limit;
+    }
+
+    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));
+        }
+
+        return '';
+    }
+}

+ 183 - 0
api/vendor/guzzlehttp/psr7/src/MessageTrait.php

@@ -0,0 +1,183 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * Trait implementing functionality common to requests and responses.
+ */
+trait MessageTrait
+{
+    /** @var array Map of all registered headers, as original name => array of values */
+    private $headers = [];
+
+    /** @var array Map of lowercase header name => original name at registration */
+    private $headerNames  = [];
+
+    /** @var string */
+    private $protocol = '1.1';
+
+    /** @var StreamInterface */
+    private $stream;
+
+    public function getProtocolVersion()
+    {
+        return $this->protocol;
+    }
+
+    public function withProtocolVersion($version)
+    {
+        if ($this->protocol === $version) {
+            return $this;
+        }
+
+        $new = clone $this;
+        $new->protocol = $version;
+        return $new;
+    }
+
+    public function getHeaders()
+    {
+        return $this->headers;
+    }
+
+    public function hasHeader($header)
+    {
+        return isset($this->headerNames[strtolower($header)]);
+    }
+
+    public function getHeader($header)
+    {
+        $header = strtolower($header);
+
+        if (!isset($this->headerNames[$header])) {
+            return [];
+        }
+
+        $header = $this->headerNames[$header];
+
+        return $this->headers[$header];
+    }
+
+    public function getHeaderLine($header)
+    {
+        return implode(', ', $this->getHeader($header));
+    }
+
+    public function withHeader($header, $value)
+    {
+        if (!is_array($value)) {
+            $value = [$value];
+        }
+
+        $value = $this->trimHeaderValues($value);
+        $normalized = strtolower($header);
+
+        $new = clone $this;
+        if (isset($new->headerNames[$normalized])) {
+            unset($new->headers[$new->headerNames[$normalized]]);
+        }
+        $new->headerNames[$normalized] = $header;
+        $new->headers[$header] = $value;
+
+        return $new;
+    }
+
+    public function withAddedHeader($header, $value)
+    {
+        if (!is_array($value)) {
+            $value = [$value];
+        }
+
+        $value = $this->trimHeaderValues($value);
+        $normalized = strtolower($header);
+
+        $new = clone $this;
+        if (isset($new->headerNames[$normalized])) {
+            $header = $this->headerNames[$normalized];
+            $new->headers[$header] = array_merge($this->headers[$header], $value);
+        } else {
+            $new->headerNames[$normalized] = $header;
+            $new->headers[$header] = $value;
+        }
+
+        return $new;
+    }
+
+    public function withoutHeader($header)
+    {
+        $normalized = strtolower($header);
+
+        if (!isset($this->headerNames[$normalized])) {
+            return $this;
+        }
+
+        $header = $this->headerNames[$normalized];
+
+        $new = clone $this;
+        unset($new->headers[$header], $new->headerNames[$normalized]);
+
+        return $new;
+    }
+
+    public function getBody()
+    {
+        if (!$this->stream) {
+            $this->stream = stream_for('');
+        }
+
+        return $this->stream;
+    }
+
+    public function withBody(StreamInterface $body)
+    {
+        if ($body === $this->stream) {
+            return $this;
+        }
+
+        $new = clone $this;
+        $new->stream = $body;
+        return $new;
+    }
+
+    private function setHeaders(array $headers)
+    {
+        $this->headerNames = $this->headers = [];
+        foreach ($headers as $header => $value) {
+            if (!is_array($value)) {
+                $value = [$value];
+            }
+
+            $value = $this->trimHeaderValues($value);
+            $normalized = strtolower($header);
+            if (isset($this->headerNames[$normalized])) {
+                $header = $this->headerNames[$normalized];
+                $this->headers[$header] = array_merge($this->headers[$header], $value);
+            } else {
+                $this->headerNames[$normalized] = $header;
+                $this->headers[$header] = $value;
+            }
+        }
+    }
+
+    /**
+     * Trims whitespace from the header values.
+     *
+     * Spaces and tabs ought to be excluded by parsers when extracting the field value from a header field.
+     *
+     * header-field = field-name ":" OWS field-value OWS
+     * OWS          = *( SP / HTAB )
+     *
+     * @param string[] $values Header values
+     *
+     * @return string[] Trimmed header values
+     *
+     * @see https://tools.ietf.org/html/rfc7230#section-3.2.4
+     */
+    private function trimHeaderValues(array $values)
+    {
+        return array_map(function ($value) {
+            return trim($value, " \t");
+        }, $values);
+    }
+}

+ 153 - 0
api/vendor/guzzlehttp/psr7/src/MultipartStream.php

@@ -0,0 +1,153 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * Stream that when read returns bytes for a streaming multipart or
+ * multipart/form-data stream.
+ */
+class MultipartStream implements StreamInterface
+{
+    use StreamDecoratorTrait;
+
+    private $boundary;
+
+    /**
+     * @param array  $elements Array of associative arrays, each containing a
+     *                         required "name" key mapping to the form field,
+     *                         name, a required "contents" key mapping to a
+     *                         StreamInterface/resource/string, an optional
+     *                         "headers" associative array of custom headers,
+     *                         and an optional "filename" key mapping to a
+     *                         string to send as the filename in the part.
+     * @param string $boundary You can optionally provide a specific boundary
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function __construct(array $elements = [], $boundary = null)
+    {
+        $this->boundary = $boundary ?: sha1(uniqid('', true));
+        $this->stream = $this->createStream($elements);
+    }
+
+    /**
+     * Get the boundary
+     *
+     * @return string
+     */
+    public function getBoundary()
+    {
+        return $this->boundary;
+    }
+
+    public function isWritable()
+    {
+        return false;
+    }
+
+    /**
+     * Get the headers needed before transferring the content of a POST file
+     */
+    private function getHeaders(array $headers)
+    {
+        $str = '';
+        foreach ($headers as $key => $value) {
+            $str .= "{$key}: {$value}\r\n";
+        }
+
+        return "--{$this->boundary}\r\n" . trim($str) . "\r\n\r\n";
+    }
+
+    /**
+     * Create the aggregate stream that will be used to upload the POST data
+     */
+    protected function createStream(array $elements)
+    {
+        $stream = new AppendStream();
+
+        foreach ($elements as $element) {
+            $this->addElement($stream, $element);
+        }
+
+        // Add the trailing boundary with CRLF
+        $stream->addStream(stream_for("--{$this->boundary}--\r\n"));
+
+        return $stream;
+    }
+
+    private function addElement(AppendStream $stream, array $element)
+    {
+        foreach (['contents', 'name'] as $key) {
+            if (!array_key_exists($key, $element)) {
+                throw new \InvalidArgumentException("A '{$key}' key is required");
+            }
+        }
+
+        $element['contents'] = stream_for($element['contents']);
+
+        if (empty($element['filename'])) {
+            $uri = $element['contents']->getMetadata('uri');
+            if (substr($uri, 0, 6) !== 'php://') {
+                $element['filename'] = $uri;
+            }
+        }
+
+        list($body, $headers) = $this->createElement(
+            $element['name'],
+            $element['contents'],
+            isset($element['filename']) ? $element['filename'] : null,
+            isset($element['headers']) ? $element['headers'] : []
+        );
+
+        $stream->addStream(stream_for($this->getHeaders($headers)));
+        $stream->addStream($body);
+        $stream->addStream(stream_for("\r\n"));
+    }
+
+    /**
+     * @return array
+     */
+    private function createElement($name, StreamInterface $stream, $filename, array $headers)
+    {
+        // Set a default content-disposition header if one was no provided
+        $disposition = $this->getHeader($headers, 'content-disposition');
+        if (!$disposition) {
+            $headers['Content-Disposition'] = ($filename === '0' || $filename)
+                ? sprintf('form-data; name="%s"; filename="%s"',
+                    $name,
+                    basename($filename))
+                : "form-data; name=\"{$name}\"";
+        }
+
+        // Set a default content-length header if one was no provided
+        $length = $this->getHeader($headers, 'content-length');
+        if (!$length) {
+            if ($length = $stream->getSize()) {
+                $headers['Content-Length'] = (string) $length;
+            }
+        }
+
+        // Set a default Content-Type if one was not supplied
+        $type = $this->getHeader($headers, 'content-type');
+        if (!$type && ($filename === '0' || $filename)) {
+            if ($type = mimetype_from_filename($filename)) {
+                $headers['Content-Type'] = $type;
+            }
+        }
+
+        return [$stream, $headers];
+    }
+
+    private function getHeader(array $headers, $key)
+    {
+        $lowercaseHeader = strtolower($key);
+        foreach ($headers as $k => $v) {
+            if (strtolower($k) === $lowercaseHeader) {
+                return $v;
+            }
+        }
+
+        return null;
+    }
+}

+ 22 - 0
api/vendor/guzzlehttp/psr7/src/NoSeekStream.php

@@ -0,0 +1,22 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * Stream decorator that prevents a stream from being seeked
+ */
+class NoSeekStream implements StreamInterface
+{
+    use StreamDecoratorTrait;
+
+    public function seek($offset, $whence = SEEK_SET)
+    {
+        throw new \RuntimeException('Cannot seek a NoSeekStream');
+    }
+
+    public function isSeekable()
+    {
+        return false;
+    }
+}

+ 165 - 0
api/vendor/guzzlehttp/psr7/src/PumpStream.php

@@ -0,0 +1,165 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * Provides a read only stream that pumps data from a PHP callable.
+ *
+ * When invoking the provided callable, the PumpStream will pass the amount of
+ * data requested to read to the callable. The callable can choose to ignore
+ * this value and return fewer or more bytes than requested. Any extra data
+ * returned by the provided callable is buffered internally until drained using
+ * the read() function of the PumpStream. The provided callable MUST return
+ * false when there is no more data to read.
+ */
+class PumpStream implements StreamInterface
+{
+    /** @var callable */
+    private $source;
+
+    /** @var int */
+    private $size;
+
+    /** @var int */
+    private $tellPos = 0;
+
+    /** @var array */
+    private $metadata;
+
+    /** @var BufferStream */
+    private $buffer;
+
+    /**
+     * @param callable $source Source of the stream data. The callable MAY
+     *                         accept an integer argument used to control the
+     *                         amount of data to return. The callable MUST
+     *                         return a string when called, or false on error
+     *                         or EOF.
+     * @param array $options   Stream options:
+     *                         - metadata: Hash of metadata to use with stream.
+     *                         - size: Size of the stream, if known.
+     */
+    public function __construct(callable $source, array $options = [])
+    {
+        $this->source = $source;
+        $this->size = isset($options['size']) ? $options['size'] : null;
+        $this->metadata = isset($options['metadata']) ? $options['metadata'] : [];
+        $this->buffer = new BufferStream();
+    }
+
+    public function __toString()
+    {
+        try {
+            return copy_to_string($this);
+        } catch (\Exception $e) {
+            return '';
+        }
+    }
+
+    public function close()
+    {
+        $this->detach();
+    }
+
+    public function detach()
+    {
+        $this->tellPos = false;
+        $this->source = null;
+    }
+
+    public function getSize()
+    {
+        return $this->size;
+    }
+
+    public function tell()
+    {
+        return $this->tellPos;
+    }
+
+    public function eof()
+    {
+        return !$this->source;
+    }
+
+    public function isSeekable()
+    {
+        return false;
+    }
+
+    public function rewind()
+    {
+        $this->seek(0);
+    }
+
+    public function seek($offset, $whence = SEEK_SET)
+    {
+        throw new \RuntimeException('Cannot seek a PumpStream');
+    }
+
+    public function isWritable()
+    {
+        return false;
+    }
+
+    public function write($string)
+    {
+        throw new \RuntimeException('Cannot write to a PumpStream');
+    }
+
+    public function isReadable()
+    {
+        return true;
+    }
+
+    public function read($length)
+    {
+        $data = $this->buffer->read($length);
+        $readLen = strlen($data);
+        $this->tellPos += $readLen;
+        $remaining = $length - $readLen;
+
+        if ($remaining) {
+            $this->pump($remaining);
+            $data .= $this->buffer->read($remaining);
+            $this->tellPos += strlen($data) - $readLen;
+        }
+
+        return $data;
+    }
+
+    public function getContents()
+    {
+        $result = '';
+        while (!$this->eof()) {
+            $result .= $this->read(1000000);
+        }
+
+        return $result;
+    }
+
+    public function getMetadata($key = null)
+    {
+        if (!$key) {
+            return $this->metadata;
+        }
+
+        return isset($this->metadata[$key]) ? $this->metadata[$key] : null;
+    }
+
+    private function pump($length)
+    {
+        if ($this->source) {
+            do {
+                $data = call_user_func($this->source, $length);
+                if ($data === false || $data === null) {
+                    $this->source = null;
+                    return;
+                }
+                $this->buffer->write($data);
+                $length -= strlen($data);
+            } while ($length > 0);
+        }
+    }
+}

+ 142 - 0
api/vendor/guzzlehttp/psr7/src/Request.php

@@ -0,0 +1,142 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use InvalidArgumentException;
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\StreamInterface;
+use Psr\Http\Message\UriInterface;
+
+/**
+ * PSR-7 request implementation.
+ */
+class Request implements RequestInterface
+{
+    use MessageTrait;
+
+    /** @var string */
+    private $method;
+
+    /** @var null|string */
+    private $requestTarget;
+
+    /** @var UriInterface */
+    private $uri;
+
+    /**
+     * @param string                               $method  HTTP method
+     * @param string|UriInterface                  $uri     URI
+     * @param array                                $headers Request headers
+     * @param string|null|resource|StreamInterface $body    Request body
+     * @param string                               $version Protocol version
+     */
+    public function __construct(
+        $method,
+        $uri,
+        array $headers = [],
+        $body = null,
+        $version = '1.1'
+    ) {
+        if (!($uri instanceof UriInterface)) {
+            $uri = new Uri($uri);
+        }
+
+        $this->method = strtoupper($method);
+        $this->uri = $uri;
+        $this->setHeaders($headers);
+        $this->protocol = $version;
+
+        if (!$this->hasHeader('Host')) {
+            $this->updateHostFromUri();
+        }
+
+        if ($body !== '' && $body !== null) {
+            $this->stream = stream_for($body);
+        }
+    }
+
+    public function getRequestTarget()
+    {
+        if ($this->requestTarget !== null) {
+            return $this->requestTarget;
+        }
+
+        $target = $this->uri->getPath();
+        if ($target == '') {
+            $target = '/';
+        }
+        if ($this->uri->getQuery() != '') {
+            $target .= '?' . $this->uri->getQuery();
+        }
+
+        return $target;
+    }
+
+    public function withRequestTarget($requestTarget)
+    {
+        if (preg_match('#\s#', $requestTarget)) {
+            throw new InvalidArgumentException(
+                'Invalid request target provided; cannot contain whitespace'
+            );
+        }
+
+        $new = clone $this;
+        $new->requestTarget = $requestTarget;
+        return $new;
+    }
+
+    public function getMethod()
+    {
+        return $this->method;
+    }
+
+    public function withMethod($method)
+    {
+        $new = clone $this;
+        $new->method = strtoupper($method);
+        return $new;
+    }
+
+    public function getUri()
+    {
+        return $this->uri;
+    }
+
+    public function withUri(UriInterface $uri, $preserveHost = false)
+    {
+        if ($uri === $this->uri) {
+            return $this;
+        }
+
+        $new = clone $this;
+        $new->uri = $uri;
+
+        if (!$preserveHost) {
+            $new->updateHostFromUri();
+        }
+
+        return $new;
+    }
+
+    private function updateHostFromUri()
+    {
+        $host = $this->uri->getHost();
+
+        if ($host == '') {
+            return;
+        }
+
+        if (($port = $this->uri->getPort()) !== null) {
+            $host .= ':' . $port;
+        }
+
+        if (isset($this->headerNames['host'])) {
+            $header = $this->headerNames['host'];
+        } else {
+            $header = 'Host';
+            $this->headerNames['host'] = 'Host';
+        }
+        // Ensure Host is the first header.
+        // See: http://tools.ietf.org/html/rfc7230#section-5.4
+        $this->headers = [$header => [$host]] + $this->headers;
+    }
+}

+ 132 - 0
api/vendor/guzzlehttp/psr7/src/Response.php

@@ -0,0 +1,132 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * PSR-7 response implementation.
+ */
+class Response implements ResponseInterface
+{
+    use MessageTrait;
+
+    /** @var array Map of standard HTTP status code/reason phrases */
+    private static $phrases = [
+        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',
+        300 => 'Multiple Choices',
+        301 => 'Moved Permanently',
+        302 => 'Found',
+        303 => 'See Other',
+        304 => 'Not Modified',
+        305 => 'Use Proxy',
+        306 => 'Switch Proxy',
+        307 => 'Temporary 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 Time-out',
+        409 => 'Conflict',
+        410 => 'Gone',
+        411 => 'Length Required',
+        412 => 'Precondition Failed',
+        413 => 'Request Entity Too Large',
+        414 => 'Request-URI Too Large',
+        415 => 'Unsupported Media Type',
+        416 => 'Requested range not satisfiable',
+        417 => 'Expectation Failed',
+        418 => 'I\'m a teapot',
+        422 => 'Unprocessable Entity',
+        423 => 'Locked',
+        424 => 'Failed Dependency',
+        425 => 'Unordered Collection',
+        426 => 'Upgrade Required',
+        428 => 'Precondition Required',
+        429 => 'Too Many Requests',
+        431 => 'Request Header Fields Too Large',
+        451 => 'Unavailable For Legal Reasons',
+        500 => 'Internal Server Error',
+        501 => 'Not Implemented',
+        502 => 'Bad Gateway',
+        503 => 'Service Unavailable',
+        504 => 'Gateway Time-out',
+        505 => 'HTTP Version not supported',
+        506 => 'Variant Also Negotiates',
+        507 => 'Insufficient Storage',
+        508 => 'Loop Detected',
+        511 => 'Network Authentication Required',
+    ];
+
+    /** @var string */
+    private $reasonPhrase = '';
+
+    /** @var int */
+    private $statusCode = 200;
+
+    /**
+     * @param int                                  $status  Status code
+     * @param array                                $headers Response headers
+     * @param string|null|resource|StreamInterface $body    Response body
+     * @param string                               $version Protocol version
+     * @param string|null                          $reason  Reason phrase (when empty a default will be used based on the status code)
+     */
+    public function __construct(
+        $status = 200,
+        array $headers = [],
+        $body = null,
+        $version = '1.1',
+        $reason = null
+    ) {
+        $this->statusCode = (int) $status;
+
+        if ($body !== '' && $body !== null) {
+            $this->stream = stream_for($body);
+        }
+
+        $this->setHeaders($headers);
+        if ($reason == '' && isset(self::$phrases[$this->statusCode])) {
+            $this->reasonPhrase = self::$phrases[$this->statusCode];
+        } else {
+            $this->reasonPhrase = (string) $reason;
+        }
+
+        $this->protocol = $version;
+    }
+
+    public function getStatusCode()
+    {
+        return $this->statusCode;
+    }
+
+    public function getReasonPhrase()
+    {
+        return $this->reasonPhrase;
+    }
+
+    public function withStatus($code, $reasonPhrase = '')
+    {
+        $new = clone $this;
+        $new->statusCode = (int) $code;
+        if ($reasonPhrase == '' && isset(self::$phrases[$new->statusCode])) {
+            $reasonPhrase = self::$phrases[$new->statusCode];
+        }
+        $new->reasonPhrase = $reasonPhrase;
+        return $new;
+    }
+}

+ 358 - 0
api/vendor/guzzlehttp/psr7/src/ServerRequest.php

@@ -0,0 +1,358 @@
+<?php
+
+namespace GuzzleHttp\Psr7;
+
+use InvalidArgumentException;
+use Psr\Http\Message\ServerRequestInterface;
+use Psr\Http\Message\UriInterface;
+use Psr\Http\Message\StreamInterface;
+use Psr\Http\Message\UploadedFileInterface;
+
+/**
+ * Server-side HTTP request
+ *
+ * Extends the Request definition to add methods for accessing incoming data,
+ * specifically server parameters, cookies, matched path parameters, query
+ * string arguments, body parameters, and upload file information.
+ *
+ * "Attributes" are discovered via decomposing the request (and usually
+ * specifically the URI path), and typically will be injected by the application.
+ *
+ * Requests are considered immutable; all methods that might change state are
+ * implemented such that they retain the internal state of the current
+ * message and return a new instance that contains the changed state.
+ */
+class ServerRequest extends Request implements ServerRequestInterface
+{
+    /**
+     * @var array
+     */
+    private $attributes = [];
+
+    /**
+     * @var array
+     */
+    private $cookieParams = [];
+
+    /**
+     * @var null|array|object
+     */
+    private $parsedBody;
+
+    /**
+     * @var array
+     */
+    private $queryParams = [];
+
+    /**
+     * @var array
+     */
+    private $serverParams;
+
+    /**
+     * @var array
+     */
+    private $uploadedFiles = [];
+
+    /**
+     * @param string                               $method       HTTP method
+     * @param string|UriInterface                  $uri          URI
+     * @param array                                $headers      Request headers
+     * @param string|null|resource|StreamInterface $body         Request body
+     * @param string                               $version      Protocol version
+     * @param array                                $serverParams Typically the $_SERVER superglobal
+     */
+    public function __construct(
+        $method,
+        $uri,
+        array $headers = [],
+        $body = null,
+        $version = '1.1',
+        array $serverParams = []
+    ) {
+        $this->serverParams = $serverParams;
+
+        parent::__construct($method, $uri, $headers, $body, $version);
+    }
+
+    /**
+     * Return an UploadedFile instance array.
+     *
+     * @param array $files A array which respect $_FILES structure
+     * @throws InvalidArgumentException for unrecognized values
+     * @return array
+     */
+    public static function normalizeFiles(array $files)
+    {
+        $normalized = [];
+
+        foreach ($files as $key => $value) {
+            if ($value instanceof UploadedFileInterface) {
+                $normalized[$key] = $value;
+            } elseif (is_array($value) && isset($value['tmp_name'])) {
+                $normalized[$key] = self::createUploadedFileFromSpec($value);
+            } elseif (is_array($value)) {
+                $normalized[$key] = self::normalizeFiles($value);
+                continue;
+            } else {
+                throw new InvalidArgumentException('Invalid value in files specification');
+            }
+        }
+
+        return $normalized;
+    }
+
+    /**
+     * Create and return an UploadedFile instance from a $_FILES specification.
+     *
+     * If the specification represents an array of values, this method will
+     * delegate to normalizeNestedFileSpec() and return that return value.
+     *
+     * @param array $value $_FILES struct
+     * @return array|UploadedFileInterface
+     */
+    private static function createUploadedFileFromSpec(array $value)
+    {
+        if (is_array($value['tmp_name'])) {
+            return self::normalizeNestedFileSpec($value);
+        }
+
+        return new UploadedFile(
+            $value['tmp_name'],
+            (int) $value['size'],
+            (int) $value['error'],
+            $value['name'],
+            $value['type']
+        );
+    }
+
+    /**
+     * Normalize an array of file specifications.
+     *
+     * Loops through all nested files and returns a normalized array of
+     * UploadedFileInterface instances.
+     *
+     * @param array $files
+     * @return UploadedFileInterface[]
+     */
+    private static function normalizeNestedFileSpec(array $files = [])
+    {
+        $normalizedFiles = [];
+
+        foreach (array_keys($files['tmp_name']) as $key) {
+            $spec = [
+                'tmp_name' => $files['tmp_name'][$key],
+                'size'     => $files['size'][$key],
+                'error'    => $files['error'][$key],
+                'name'     => $files['name'][$key],
+                'type'     => $files['type'][$key],
+            ];
+            $normalizedFiles[$key] = self::createUploadedFileFromSpec($spec);
+        }
+
+        return $normalizedFiles;
+    }
+
+    /**
+     * Return a ServerRequest populated with superglobals:
+     * $_GET
+     * $_POST
+     * $_COOKIE
+     * $_FILES
+     * $_SERVER
+     *
+     * @return ServerRequestInterface
+     */
+    public static function fromGlobals()
+    {
+        $method = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : 'GET';
+        $headers = function_exists('getallheaders') ? getallheaders() : [];
+        $uri = self::getUriFromGlobals();
+        $body = new LazyOpenStream('php://input', 'r+');
+        $protocol = isset($_SERVER['SERVER_PROTOCOL']) ? str_replace('HTTP/', '', $_SERVER['SERVER_PROTOCOL']) : '1.1';
+
+        $serverRequest = new ServerRequest($method, $uri, $headers, $body, $protocol, $_SERVER);
+
+        return $serverRequest
+            ->withCookieParams($_COOKIE)
+            ->withQueryParams($_GET)
+            ->withParsedBody($_POST)
+            ->withUploadedFiles(self::normalizeFiles($_FILES));
+    }
+
+    /**
+     * Get a Uri populated with values from $_SERVER.
+     *
+     * @return UriInterface
+     */
+    public static function getUriFromGlobals() {
+        $uri = new Uri('');
+
+        $uri = $uri->withScheme(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' ? 'https' : 'http');
+
+        $hasPort = false;
+        if (isset($_SERVER['HTTP_HOST'])) {
+            $hostHeaderParts = explode(':', $_SERVER['HTTP_HOST']);
+            $uri = $uri->withHost($hostHeaderParts[0]);
+            if (isset($hostHeaderParts[1])) {
+                $hasPort = true;
+                $uri = $uri->withPort($hostHeaderParts[1]);
+            }
+        } elseif (isset($_SERVER['SERVER_NAME'])) {
+            $uri = $uri->withHost($_SERVER['SERVER_NAME']);
+        } elseif (isset($_SERVER['SERVER_ADDR'])) {
+            $uri = $uri->withHost($_SERVER['SERVER_ADDR']);
+        }
+
+        if (!$hasPort && isset($_SERVER['SERVER_PORT'])) {
+            $uri = $uri->withPort($_SERVER['SERVER_PORT']);
+        }
+
+        $hasQuery = false;
+        if (isset($_SERVER['REQUEST_URI'])) {
+            $requestUriParts = explode('?', $_SERVER['REQUEST_URI']);
+            $uri = $uri->withPath($requestUriParts[0]);
+            if (isset($requestUriParts[1])) {
+                $hasQuery = true;
+                $uri = $uri->withQuery($requestUriParts[1]);
+            }
+        }
+
+        if (!$hasQuery && isset($_SERVER['QUERY_STRING'])) {
+            $uri = $uri->withQuery($_SERVER['QUERY_STRING']);
+        }
+
+        return $uri;
+    }
+
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getServerParams()
+    {
+        return $this->serverParams;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getUploadedFiles()
+    {
+        return $this->uploadedFiles;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function withUploadedFiles(array $uploadedFiles)
+    {
+        $new = clone $this;
+        $new->uploadedFiles = $uploadedFiles;
+
+        return $new;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getCookieParams()
+    {
+        return $this->cookieParams;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function withCookieParams(array $cookies)
+    {
+        $new = clone $this;
+        $new->cookieParams = $cookies;
+
+        return $new;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getQueryParams()
+    {
+        return $this->queryParams;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function withQueryParams(array $query)
+    {
+        $new = clone $this;
+        $new->queryParams = $query;
+
+        return $new;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getParsedBody()
+    {
+        return $this->parsedBody;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function withParsedBody($data)
+    {
+        $new = clone $this;
+        $new->parsedBody = $data;
+
+        return $new;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getAttributes()
+    {
+        return $this->attributes;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getAttribute($attribute, $default = null)
+    {
+        if (false === array_key_exists($attribute, $this->attributes)) {
+            return $default;
+        }
+
+        return $this->attributes[$attribute];
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function withAttribute($attribute, $value)
+    {
+        $new = clone $this;
+        $new->attributes[$attribute] = $value;
+
+        return $new;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function withoutAttribute($attribute)
+    {
+        if (false === array_key_exists($attribute, $this->attributes)) {
+            return $this;
+        }
+
+        $new = clone $this;
+        unset($new->attributes[$attribute]);
+
+        return $new;
+    }
+}

+ 257 - 0
api/vendor/guzzlehttp/psr7/src/Stream.php

@@ -0,0 +1,257 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * PHP stream implementation.
+ *
+ * @var $stream
+ */
+class Stream implements StreamInterface
+{
+    private $stream;
+    private $size;
+    private $seekable;
+    private $readable;
+    private $writable;
+    private $uri;
+    private $customMetadata;
+
+    /** @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
+        ]
+    ];
+
+    /**
+     * This constructor accepts an associative array of options.
+     *
+     * - size: (int) If a read stream would otherwise have an indeterminate
+     *   size, but the size is known due to foreknowledge, then you can
+     *   provide that size, in bytes.
+     * - metadata: (array) Any additional metadata to return when the metadata
+     *   of the stream is accessed.
+     *
+     * @param resource $stream  Stream resource to wrap.
+     * @param array    $options Associative array of options.
+     *
+     * @throws \InvalidArgumentException if the stream is not a stream resource
+     */
+    public function __construct($stream, $options = [])
+    {
+        if (!is_resource($stream)) {
+            throw new \InvalidArgumentException('Stream must be a resource');
+        }
+
+        if (isset($options['size'])) {
+            $this->size = $options['size'];
+        }
+
+        $this->customMetadata = isset($options['metadata'])
+            ? $options['metadata']
+            : [];
+
+        $this->stream = $stream;
+        $meta = stream_get_meta_data($this->stream);
+        $this->seekable = $meta['seekable'];
+        $this->readable = isset(self::$readWriteHash['read'][$meta['mode']]);
+        $this->writable = isset(self::$readWriteHash['write'][$meta['mode']]);
+        $this->uri = $this->getMetadata('uri');
+    }
+
+    public function __get($name)
+    {
+        if ($name == 'stream') {
+            throw new \RuntimeException('The stream is detached');
+        }
+
+        throw new \BadMethodCallException('No value for ' . $name);
+    }
+
+    /**
+     * Closes the stream when the destructed
+     */
+    public function __destruct()
+    {
+        $this->close();
+    }
+
+    public function __toString()
+    {
+        try {
+            $this->seek(0);
+            return (string) stream_get_contents($this->stream);
+        } catch (\Exception $e) {
+            return '';
+        }
+    }
+
+    public function getContents()
+    {
+        $contents = stream_get_contents($this->stream);
+
+        if ($contents === false) {
+            throw new \RuntimeException('Unable to read stream contents');
+        }
+
+        return $contents;
+    }
+
+    public function close()
+    {
+        if (isset($this->stream)) {
+            if (is_resource($this->stream)) {
+                fclose($this->stream);
+            }
+            $this->detach();
+        }
+    }
+
+    public function detach()
+    {
+        if (!isset($this->stream)) {
+            return null;
+        }
+
+        $result = $this->stream;
+        unset($this->stream);
+        $this->size = $this->uri = null;
+        $this->readable = $this->writable = $this->seekable = false;
+
+        return $result;
+    }
+
+    public function getSize()
+    {
+        if ($this->size !== null) {
+            return $this->size;
+        }
+
+        if (!isset($this->stream)) {
+            return null;
+        }
+
+        // Clear the stat cache if the stream has a URI
+        if ($this->uri) {
+            clearstatcache(true, $this->uri);
+        }
+
+        $stats = fstat($this->stream);
+        if (isset($stats['size'])) {
+            $this->size = $stats['size'];
+            return $this->size;
+        }
+
+        return null;
+    }
+
+    public function isReadable()
+    {
+        return $this->readable;
+    }
+
+    public function isWritable()
+    {
+        return $this->writable;
+    }
+
+    public function isSeekable()
+    {
+        return $this->seekable;
+    }
+
+    public function eof()
+    {
+        return !$this->stream || feof($this->stream);
+    }
+
+    public function tell()
+    {
+        $result = ftell($this->stream);
+
+        if ($result === false) {
+            throw new \RuntimeException('Unable to determine stream position');
+        }
+
+        return $result;
+    }
+
+    public function rewind()
+    {
+        $this->seek(0);
+    }
+
+    public function seek($offset, $whence = SEEK_SET)
+    {
+        if (!$this->seekable) {
+            throw new \RuntimeException('Stream is not seekable');
+        } elseif (fseek($this->stream, $offset, $whence) === -1) {
+            throw new \RuntimeException('Unable to seek to stream position '
+                . $offset . ' with whence ' . var_export($whence, true));
+        }
+    }
+
+    public function read($length)
+    {
+        if (!$this->readable) {
+            throw new \RuntimeException('Cannot read from non-readable stream');
+        }
+        if ($length < 0) {
+            throw new \RuntimeException('Length parameter cannot be negative');
+        }
+
+        if (0 === $length) {
+            return '';
+        }
+
+        $string = fread($this->stream, $length);
+        if (false === $string) {
+            throw new \RuntimeException('Unable to read from stream');
+        }
+
+        return $string;
+    }
+
+    public function write($string)
+    {
+        if (!$this->writable) {
+            throw new \RuntimeException('Cannot write to a non-writable stream');
+        }
+
+        // We can't know the size after writing anything
+        $this->size = null;
+        $result = fwrite($this->stream, $string);
+
+        if ($result === false) {
+            throw new \RuntimeException('Unable to write to stream');
+        }
+
+        return $result;
+    }
+
+    public function getMetadata($key = null)
+    {
+        if (!isset($this->stream)) {
+            return $key ? null : [];
+        } elseif (!$key) {
+            return $this->customMetadata + stream_get_meta_data($this->stream);
+        } elseif (isset($this->customMetadata[$key])) {
+            return $this->customMetadata[$key];
+        }
+
+        $meta = stream_get_meta_data($this->stream);
+
+        return isset($meta[$key]) ? $meta[$key] : null;
+    }
+}

+ 149 - 0
api/vendor/guzzlehttp/psr7/src/StreamDecoratorTrait.php

@@ -0,0 +1,149 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * Stream decorator trait
+ * @property StreamInterface stream
+ */
+trait StreamDecoratorTrait
+{
+    /**
+     * @param StreamInterface $stream Stream to decorate
+     */
+    public function __construct(StreamInterface $stream)
+    {
+        $this->stream = $stream;
+    }
+
+    /**
+     * Magic method used to create a new stream if streams are not added in
+     * the constructor of a decorator (e.g., LazyOpenStream).
+     *
+     * @param string $name Name of the property (allows "stream" only).
+     *
+     * @return StreamInterface
+     */
+    public function __get($name)
+    {
+        if ($name == 'stream') {
+            $this->stream = $this->createStream();
+            return $this->stream;
+        }
+
+        throw new \UnexpectedValueException("$name not found on class");
+    }
+
+    public function __toString()
+    {
+        try {
+            if ($this->isSeekable()) {
+                $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()
+    {
+        return copy_to_string($this);
+    }
+
+    /**
+     * 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([$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()
+    {
+        $this->stream->close();
+    }
+
+    public function getMetadata($key = null)
+    {
+        return $this->stream->getMetadata($key);
+    }
+
+    public function detach()
+    {
+        return $this->stream->detach();
+    }
+
+    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 rewind()
+    {
+        $this->seek(0);
+    }
+
+    public function seek($offset, $whence = SEEK_SET)
+    {
+        $this->stream->seek($offset, $whence);
+    }
+
+    public function read($length)
+    {
+        return $this->stream->read($length);
+    }
+
+    public function write($string)
+    {
+        return $this->stream->write($string);
+    }
+
+    /**
+     * Implement in subclasses to dynamically create streams when requested.
+     *
+     * @return StreamInterface
+     * @throws \BadMethodCallException
+     */
+    protected function createStream()
+    {
+        throw new \BadMethodCallException('Not implemented');
+    }
+}

+ 121 - 0
api/vendor/guzzlehttp/psr7/src/StreamWrapper.php

@@ -0,0 +1,121 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * Converts Guzzle streams into PHP stream resources.
+ */
+class StreamWrapper
+{
+    /** @var resource */
+    public $context;
+
+    /** @var StreamInterface */
+    private $stream;
+
+    /** @var string r, r+, or w */
+    private $mode;
+
+    /**
+     * Returns a resource representing the stream.
+     *
+     * @param StreamInterface $stream The stream to get a resource for
+     *
+     * @return resource
+     * @throws \InvalidArgumentException if stream is not readable or writable
+     */
+    public static function getResource(StreamInterface $stream)
+    {
+        self::register();
+
+        if ($stream->isReadable()) {
+            $mode = $stream->isWritable() ? 'r+' : 'r';
+        } elseif ($stream->isWritable()) {
+            $mode = 'w';
+        } else {
+            throw new \InvalidArgumentException('The stream must be readable, '
+                . 'writable, or both.');
+        }
+
+        return fopen('guzzle://stream', $mode, null, stream_context_create([
+            'guzzle' => ['stream' => $stream]
+        ]));
+    }
+
+    /**
+     * Registers the stream wrapper if needed
+     */
+    public static function register()
+    {
+        if (!in_array('guzzle', stream_get_wrappers())) {
+            stream_wrapper_register('guzzle', __CLASS__);
+        }
+    }
+
+    public function stream_open($path, $mode, $options, &$opened_path)
+    {
+        $options = stream_context_get_options($this->context);
+
+        if (!isset($options['guzzle']['stream'])) {
+            return false;
+        }
+
+        $this->mode = $mode;
+        $this->stream = $options['guzzle']['stream'];
+
+        return true;
+    }
+
+    public function stream_read($count)
+    {
+        return $this->stream->read($count);
+    }
+
+    public function stream_write($data)
+    {
+        return (int) $this->stream->write($data);
+    }
+
+    public function stream_tell()
+    {
+        return $this->stream->tell();
+    }
+
+    public function stream_eof()
+    {
+        return $this->stream->eof();
+    }
+
+    public function stream_seek($offset, $whence)
+    {
+        $this->stream->seek($offset, $whence);
+
+        return true;
+    }
+
+    public function stream_stat()
+    {
+        static $modeMap = [
+            'r'  => 33060,
+            'r+' => 33206,
+            'w'  => 33188
+        ];
+
+        return [
+            'dev'     => 0,
+            'ino'     => 0,
+            'mode'    => $modeMap[$this->mode],
+            'nlink'   => 0,
+            'uid'     => 0,
+            'gid'     => 0,
+            'rdev'    => 0,
+            'size'    => $this->stream->getSize() ?: 0,
+            'atime'   => 0,
+            'mtime'   => 0,
+            'ctime'   => 0,
+            'blksize' => 0,
+            'blocks'  => 0
+        ];
+    }
+}

+ 316 - 0
api/vendor/guzzlehttp/psr7/src/UploadedFile.php

@@ -0,0 +1,316 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use InvalidArgumentException;
+use Psr\Http\Message\StreamInterface;
+use Psr\Http\Message\UploadedFileInterface;
+use RuntimeException;
+
+class UploadedFile implements UploadedFileInterface
+{
+    /**
+     * @var int[]
+     */
+    private static $errors = [
+        UPLOAD_ERR_OK,
+        UPLOAD_ERR_INI_SIZE,
+        UPLOAD_ERR_FORM_SIZE,
+        UPLOAD_ERR_PARTIAL,
+        UPLOAD_ERR_NO_FILE,
+        UPLOAD_ERR_NO_TMP_DIR,
+        UPLOAD_ERR_CANT_WRITE,
+        UPLOAD_ERR_EXTENSION,
+    ];
+
+    /**
+     * @var string
+     */
+    private $clientFilename;
+
+    /**
+     * @var string
+     */
+    private $clientMediaType;
+
+    /**
+     * @var int
+     */
+    private $error;
+
+    /**
+     * @var null|string
+     */
+    private $file;
+
+    /**
+     * @var bool
+     */
+    private $moved = false;
+
+    /**
+     * @var int
+     */
+    private $size;
+
+    /**
+     * @var StreamInterface|null
+     */
+    private $stream;
+
+    /**
+     * @param StreamInterface|string|resource $streamOrFile
+     * @param int $size
+     * @param int $errorStatus
+     * @param string|null $clientFilename
+     * @param string|null $clientMediaType
+     */
+    public function __construct(
+        $streamOrFile,
+        $size,
+        $errorStatus,
+        $clientFilename = null,
+        $clientMediaType = null
+    ) {
+        $this->setError($errorStatus);
+        $this->setSize($size);
+        $this->setClientFilename($clientFilename);
+        $this->setClientMediaType($clientMediaType);
+
+        if ($this->isOk()) {
+            $this->setStreamOrFile($streamOrFile);
+        }
+    }
+
+    /**
+     * Depending on the value set file or stream variable
+     *
+     * @param mixed $streamOrFile
+     * @throws InvalidArgumentException
+     */
+    private function setStreamOrFile($streamOrFile)
+    {
+        if (is_string($streamOrFile)) {
+            $this->file = $streamOrFile;
+        } elseif (is_resource($streamOrFile)) {
+            $this->stream = new Stream($streamOrFile);
+        } elseif ($streamOrFile instanceof StreamInterface) {
+            $this->stream = $streamOrFile;
+        } else {
+            throw new InvalidArgumentException(
+                'Invalid stream or file provided for UploadedFile'
+            );
+        }
+    }
+
+    /**
+     * @param int $error
+     * @throws InvalidArgumentException
+     */
+    private function setError($error)
+    {
+        if (false === is_int($error)) {
+            throw new InvalidArgumentException(
+                'Upload file error status must be an integer'
+            );
+        }
+
+        if (false === in_array($error, UploadedFile::$errors)) {
+            throw new InvalidArgumentException(
+                'Invalid error status for UploadedFile'
+            );
+        }
+
+        $this->error = $error;
+    }
+
+    /**
+     * @param int $size
+     * @throws InvalidArgumentException
+     */
+    private function setSize($size)
+    {
+        if (false === is_int($size)) {
+            throw new InvalidArgumentException(
+                'Upload file size must be an integer'
+            );
+        }
+
+        $this->size = $size;
+    }
+
+    /**
+     * @param mixed $param
+     * @return boolean
+     */
+    private function isStringOrNull($param)
+    {
+        return in_array(gettype($param), ['string', 'NULL']);
+    }
+
+    /**
+     * @param mixed $param
+     * @return boolean
+     */
+    private function isStringNotEmpty($param)
+    {
+        return is_string($param) && false === empty($param);
+    }
+
+    /**
+     * @param string|null $clientFilename
+     * @throws InvalidArgumentException
+     */
+    private function setClientFilename($clientFilename)
+    {
+        if (false === $this->isStringOrNull($clientFilename)) {
+            throw new InvalidArgumentException(
+                'Upload file client filename must be a string or null'
+            );
+        }
+
+        $this->clientFilename = $clientFilename;
+    }
+
+    /**
+     * @param string|null $clientMediaType
+     * @throws InvalidArgumentException
+     */
+    private function setClientMediaType($clientMediaType)
+    {
+        if (false === $this->isStringOrNull($clientMediaType)) {
+            throw new InvalidArgumentException(
+                'Upload file client media type must be a string or null'
+            );
+        }
+
+        $this->clientMediaType = $clientMediaType;
+    }
+
+    /**
+     * Return true if there is no upload error
+     *
+     * @return boolean
+     */
+    private function isOk()
+    {
+        return $this->error === UPLOAD_ERR_OK;
+    }
+
+    /**
+     * @return boolean
+     */
+    public function isMoved()
+    {
+        return $this->moved;
+    }
+
+    /**
+     * @throws RuntimeException if is moved or not ok
+     */
+    private function validateActive()
+    {
+        if (false === $this->isOk()) {
+            throw new RuntimeException('Cannot retrieve stream due to upload error');
+        }
+
+        if ($this->isMoved()) {
+            throw new RuntimeException('Cannot retrieve stream after it has already been moved');
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     * @throws RuntimeException if the upload was not successful.
+     */
+    public function getStream()
+    {
+        $this->validateActive();
+
+        if ($this->stream instanceof StreamInterface) {
+            return $this->stream;
+        }
+
+        return new LazyOpenStream($this->file, 'r+');
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see http://php.net/is_uploaded_file
+     * @see http://php.net/move_uploaded_file
+     * @param string $targetPath Path to which to move the uploaded file.
+     * @throws RuntimeException if the upload was not successful.
+     * @throws InvalidArgumentException if the $path specified is invalid.
+     * @throws RuntimeException on any error during the move operation, or on
+     *     the second or subsequent call to the method.
+     */
+    public function moveTo($targetPath)
+    {
+        $this->validateActive();
+
+        if (false === $this->isStringNotEmpty($targetPath)) {
+            throw new InvalidArgumentException(
+                'Invalid path provided for move operation; must be a non-empty string'
+            );
+        }
+
+        if ($this->file) {
+            $this->moved = php_sapi_name() == 'cli'
+                ? rename($this->file, $targetPath)
+                : move_uploaded_file($this->file, $targetPath);
+        } else {
+            copy_to_stream(
+                $this->getStream(),
+                new LazyOpenStream($targetPath, 'w')
+            );
+
+            $this->moved = true;
+        }
+
+        if (false === $this->moved) {
+            throw new RuntimeException(
+                sprintf('Uploaded file could not be moved to %s', $targetPath)
+            );
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @return int|null The file size in bytes or null if unknown.
+     */
+    public function getSize()
+    {
+        return $this->size;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see http://php.net/manual/en/features.file-upload.errors.php
+     * @return int One of PHP's UPLOAD_ERR_XXX constants.
+     */
+    public function getError()
+    {
+        return $this->error;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @return string|null The filename sent by the client or null if none
+     *     was provided.
+     */
+    public function getClientFilename()
+    {
+        return $this->clientFilename;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getClientMediaType()
+    {
+        return $this->clientMediaType;
+    }
+}

+ 702 - 0
api/vendor/guzzlehttp/psr7/src/Uri.php

@@ -0,0 +1,702 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\UriInterface;
+
+/**
+ * PSR-7 URI implementation.
+ *
+ * @author Michael Dowling
+ * @author Tobias Schultze
+ * @author Matthew Weier O'Phinney
+ */
+class Uri implements UriInterface
+{
+    /**
+     * Absolute http and https URIs require a host per RFC 7230 Section 2.7
+     * but in generic URIs the host can be empty. So for http(s) URIs
+     * we apply this default host when no host is given yet to form a
+     * valid URI.
+     */
+    const HTTP_DEFAULT_HOST = 'localhost';
+
+    private static $defaultPorts = [
+        'http'  => 80,
+        'https' => 443,
+        'ftp' => 21,
+        'gopher' => 70,
+        'nntp' => 119,
+        'news' => 119,
+        'telnet' => 23,
+        'tn3270' => 23,
+        'imap' => 143,
+        'pop' => 110,
+        'ldap' => 389,
+    ];
+
+    private static $charUnreserved = 'a-zA-Z0-9_\-\.~';
+    private static $charSubDelims = '!\$&\'\(\)\*\+,;=';
+    private static $replaceQuery = ['=' => '%3D', '&' => '%26'];
+
+    /** @var string Uri scheme. */
+    private $scheme = '';
+
+    /** @var string Uri user info. */
+    private $userInfo = '';
+
+    /** @var string Uri host. */
+    private $host = '';
+
+    /** @var int|null Uri port. */
+    private $port;
+
+    /** @var string Uri path. */
+    private $path = '';
+
+    /** @var string Uri query string. */
+    private $query = '';
+
+    /** @var string Uri fragment. */
+    private $fragment = '';
+
+    /**
+     * @param string $uri URI to parse
+     */
+    public function __construct($uri = '')
+    {
+        // weak type check to also accept null until we can add scalar type hints
+        if ($uri != '') {
+            $parts = parse_url($uri);
+            if ($parts === false) {
+                throw new \InvalidArgumentException("Unable to parse URI: $uri");
+            }
+            $this->applyParts($parts);
+        }
+    }
+
+    public function __toString()
+    {
+        return self::composeComponents(
+            $this->scheme,
+            $this->getAuthority(),
+            $this->path,
+            $this->query,
+            $this->fragment
+        );
+    }
+
+    /**
+     * Composes a URI reference string from its various components.
+     *
+     * Usually this method does not need to be called manually but instead is used indirectly via
+     * `Psr\Http\Message\UriInterface::__toString`.
+     *
+     * PSR-7 UriInterface treats an empty component the same as a missing component as
+     * getQuery(), getFragment() etc. always return a string. This explains the slight
+     * difference to RFC 3986 Section 5.3.
+     *
+     * Another adjustment is that the authority separator is added even when the authority is missing/empty
+     * for the "file" scheme. This is because PHP stream functions like `file_get_contents` only work with
+     * `file:///myfile` but not with `file:/myfile` although they are equivalent according to RFC 3986. But
+     * `file:///` is the more common syntax for the file scheme anyway (Chrome for example redirects to
+     * that format).
+     *
+     * @param string $scheme
+     * @param string $authority
+     * @param string $path
+     * @param string $query
+     * @param string $fragment
+     *
+     * @return string
+     *
+     * @link https://tools.ietf.org/html/rfc3986#section-5.3
+     */
+    public static function composeComponents($scheme, $authority, $path, $query, $fragment)
+    {
+        $uri = '';
+
+        // weak type checks to also accept null until we can add scalar type hints
+        if ($scheme != '') {
+            $uri .= $scheme . ':';
+        }
+
+        if ($authority != ''|| $scheme === 'file') {
+            $uri .= '//' . $authority;
+        }
+
+        $uri .= $path;
+
+        if ($query != '') {
+            $uri .= '?' . $query;
+        }
+
+        if ($fragment != '') {
+            $uri .= '#' . $fragment;
+        }
+
+        return $uri;
+    }
+
+    /**
+     * Whether the URI has the default port of the current scheme.
+     *
+     * `Psr\Http\Message\UriInterface::getPort` may return null or the standard port. This method can be used
+     * independently of the implementation.
+     *
+     * @param UriInterface $uri
+     *
+     * @return bool
+     */
+    public static function isDefaultPort(UriInterface $uri)
+    {
+        return $uri->getPort() === null
+            || (isset(self::$defaultPorts[$uri->getScheme()]) && $uri->getPort() === self::$defaultPorts[$uri->getScheme()]);
+    }
+
+    /**
+     * Whether the URI is absolute, i.e. it has a scheme.
+     *
+     * An instance of UriInterface can either be an absolute URI or a relative reference. This method returns true
+     * if it is the former. An absolute URI has a scheme. A relative reference is used to express a URI relative
+     * to another URI, the base URI. Relative references can be divided into several forms:
+     * - network-path references, e.g. '//example.com/path'
+     * - absolute-path references, e.g. '/path'
+     * - relative-path references, e.g. 'subpath'
+     *
+     * @param UriInterface $uri
+     *
+     * @return bool
+     * @see Uri::isNetworkPathReference
+     * @see Uri::isAbsolutePathReference
+     * @see Uri::isRelativePathReference
+     * @link https://tools.ietf.org/html/rfc3986#section-4
+     */
+    public static function isAbsolute(UriInterface $uri)
+    {
+        return $uri->getScheme() !== '';
+    }
+
+    /**
+     * Whether the URI is a network-path reference.
+     *
+     * A relative reference that begins with two slash characters is termed an network-path reference.
+     *
+     * @param UriInterface $uri
+     *
+     * @return bool
+     * @link https://tools.ietf.org/html/rfc3986#section-4.2
+     */
+    public static function isNetworkPathReference(UriInterface $uri)
+    {
+        return $uri->getScheme() === '' && $uri->getAuthority() !== '';
+    }
+
+    /**
+     * Whether the URI is a absolute-path reference.
+     *
+     * A relative reference that begins with a single slash character is termed an absolute-path reference.
+     *
+     * @param UriInterface $uri
+     *
+     * @return bool
+     * @link https://tools.ietf.org/html/rfc3986#section-4.2
+     */
+    public static function isAbsolutePathReference(UriInterface $uri)
+    {
+        return $uri->getScheme() === ''
+            && $uri->getAuthority() === ''
+            && isset($uri->getPath()[0])
+            && $uri->getPath()[0] === '/';
+    }
+
+    /**
+     * Whether the URI is a relative-path reference.
+     *
+     * A relative reference that does not begin with a slash character is termed a relative-path reference.
+     *
+     * @param UriInterface $uri
+     *
+     * @return bool
+     * @link https://tools.ietf.org/html/rfc3986#section-4.2
+     */
+    public static function isRelativePathReference(UriInterface $uri)
+    {
+        return $uri->getScheme() === ''
+            && $uri->getAuthority() === ''
+            && (!isset($uri->getPath()[0]) || $uri->getPath()[0] !== '/');
+    }
+
+    /**
+     * Whether the URI is a same-document reference.
+     *
+     * A same-document reference refers to a URI that is, aside from its fragment
+     * component, identical to the base URI. When no base URI is given, only an empty
+     * URI reference (apart from its fragment) is considered a same-document reference.
+     *
+     * @param UriInterface      $uri  The URI to check
+     * @param UriInterface|null $base An optional base URI to compare against
+     *
+     * @return bool
+     * @link https://tools.ietf.org/html/rfc3986#section-4.4
+     */
+    public static function isSameDocumentReference(UriInterface $uri, UriInterface $base = null)
+    {
+        if ($base !== null) {
+            $uri = UriResolver::resolve($base, $uri);
+
+            return ($uri->getScheme() === $base->getScheme())
+                && ($uri->getAuthority() === $base->getAuthority())
+                && ($uri->getPath() === $base->getPath())
+                && ($uri->getQuery() === $base->getQuery());
+        }
+
+        return $uri->getScheme() === '' && $uri->getAuthority() === '' && $uri->getPath() === '' && $uri->getQuery() === '';
+    }
+
+    /**
+     * Removes dot segments from a path and returns the new path.
+     *
+     * @param string $path
+     *
+     * @return string
+     *
+     * @deprecated since version 1.4. Use UriResolver::removeDotSegments instead.
+     * @see UriResolver::removeDotSegments
+     */
+    public static function removeDotSegments($path)
+    {
+        return UriResolver::removeDotSegments($path);
+    }
+
+    /**
+     * Converts the relative URI into a new URI that is resolved against the base URI.
+     *
+     * @param UriInterface        $base Base URI
+     * @param string|UriInterface $rel  Relative URI
+     *
+     * @return UriInterface
+     *
+     * @deprecated since version 1.4. Use UriResolver::resolve instead.
+     * @see UriResolver::resolve
+     */
+    public static function resolve(UriInterface $base, $rel)
+    {
+        if (!($rel instanceof UriInterface)) {
+            $rel = new self($rel);
+        }
+
+        return UriResolver::resolve($base, $rel);
+    }
+
+    /**
+     * Creates a new URI with a specific query string value removed.
+     *
+     * Any existing query string values that exactly match the provided key are
+     * removed.
+     *
+     * @param UriInterface $uri URI to use as a base.
+     * @param string       $key Query string key to remove.
+     *
+     * @return UriInterface
+     */
+    public static function withoutQueryValue(UriInterface $uri, $key)
+    {
+        $current = $uri->getQuery();
+        if ($current === '') {
+            return $uri;
+        }
+
+        $decodedKey = rawurldecode($key);
+        $result = array_filter(explode('&', $current), function ($part) use ($decodedKey) {
+            return rawurldecode(explode('=', $part)[0]) !== $decodedKey;
+        });
+
+        return $uri->withQuery(implode('&', $result));
+    }
+
+    /**
+     * Creates a new URI with a specific query string value.
+     *
+     * Any existing query string values that exactly match the provided key are
+     * removed and replaced with the given key value pair.
+     *
+     * A value of null will set the query string key without a value, e.g. "key"
+     * instead of "key=value".
+     *
+     * @param UriInterface $uri   URI to use as a base.
+     * @param string       $key   Key to set.
+     * @param string|null  $value Value to set
+     *
+     * @return UriInterface
+     */
+    public static function withQueryValue(UriInterface $uri, $key, $value)
+    {
+        $current = $uri->getQuery();
+
+        if ($current === '') {
+            $result = [];
+        } else {
+            $decodedKey = rawurldecode($key);
+            $result = array_filter(explode('&', $current), function ($part) use ($decodedKey) {
+                return rawurldecode(explode('=', $part)[0]) !== $decodedKey;
+            });
+        }
+
+        // Query string separators ("=", "&") within the key or value need to be encoded
+        // (while preventing double-encoding) before setting the query string. All other
+        // chars that need percent-encoding will be encoded by withQuery().
+        $key = strtr($key, self::$replaceQuery);
+
+        if ($value !== null) {
+            $result[] = $key . '=' . strtr($value, self::$replaceQuery);
+        } else {
+            $result[] = $key;
+        }
+
+        return $uri->withQuery(implode('&', $result));
+    }
+
+    /**
+     * Creates a URI from a hash of `parse_url` components.
+     *
+     * @param array $parts
+     *
+     * @return UriInterface
+     * @link http://php.net/manual/en/function.parse-url.php
+     *
+     * @throws \InvalidArgumentException If the components do not form a valid URI.
+     */
+    public static function fromParts(array $parts)
+    {
+        $uri = new self();
+        $uri->applyParts($parts);
+        $uri->validateState();
+
+        return $uri;
+    }
+
+    public function getScheme()
+    {
+        return $this->scheme;
+    }
+
+    public function getAuthority()
+    {
+        $authority = $this->host;
+        if ($this->userInfo !== '') {
+            $authority = $this->userInfo . '@' . $authority;
+        }
+
+        if ($this->port !== null) {
+            $authority .= ':' . $this->port;
+        }
+
+        return $authority;
+    }
+
+    public function getUserInfo()
+    {
+        return $this->userInfo;
+    }
+
+    public function getHost()
+    {
+        return $this->host;
+    }
+
+    public function getPort()
+    {
+        return $this->port;
+    }
+
+    public function getPath()
+    {
+        return $this->path;
+    }
+
+    public function getQuery()
+    {
+        return $this->query;
+    }
+
+    public function getFragment()
+    {
+        return $this->fragment;
+    }
+
+    public function withScheme($scheme)
+    {
+        $scheme = $this->filterScheme($scheme);
+
+        if ($this->scheme === $scheme) {
+            return $this;
+        }
+
+        $new = clone $this;
+        $new->scheme = $scheme;
+        $new->removeDefaultPort();
+        $new->validateState();
+
+        return $new;
+    }
+
+    public function withUserInfo($user, $password = null)
+    {
+        $info = $user;
+        if ($password != '') {
+            $info .= ':' . $password;
+        }
+
+        if ($this->userInfo === $info) {
+            return $this;
+        }
+
+        $new = clone $this;
+        $new->userInfo = $info;
+        $new->validateState();
+
+        return $new;
+    }
+
+    public function withHost($host)
+    {
+        $host = $this->filterHost($host);
+
+        if ($this->host === $host) {
+            return $this;
+        }
+
+        $new = clone $this;
+        $new->host = $host;
+        $new->validateState();
+
+        return $new;
+    }
+
+    public function withPort($port)
+    {
+        $port = $this->filterPort($port);
+
+        if ($this->port === $port) {
+            return $this;
+        }
+
+        $new = clone $this;
+        $new->port = $port;
+        $new->removeDefaultPort();
+        $new->validateState();
+
+        return $new;
+    }
+
+    public function withPath($path)
+    {
+        $path = $this->filterPath($path);
+
+        if ($this->path === $path) {
+            return $this;
+        }
+
+        $new = clone $this;
+        $new->path = $path;
+        $new->validateState();
+
+        return $new;
+    }
+
+    public function withQuery($query)
+    {
+        $query = $this->filterQueryAndFragment($query);
+
+        if ($this->query === $query) {
+            return $this;
+        }
+
+        $new = clone $this;
+        $new->query = $query;
+
+        return $new;
+    }
+
+    public function withFragment($fragment)
+    {
+        $fragment = $this->filterQueryAndFragment($fragment);
+
+        if ($this->fragment === $fragment) {
+            return $this;
+        }
+
+        $new = clone $this;
+        $new->fragment = $fragment;
+
+        return $new;
+    }
+
+    /**
+     * Apply parse_url parts to a URI.
+     *
+     * @param array $parts Array of parse_url parts to apply.
+     */
+    private function applyParts(array $parts)
+    {
+        $this->scheme = isset($parts['scheme'])
+            ? $this->filterScheme($parts['scheme'])
+            : '';
+        $this->userInfo = isset($parts['user']) ? $parts['user'] : '';
+        $this->host = isset($parts['host'])
+            ? $this->filterHost($parts['host'])
+            : '';
+        $this->port = isset($parts['port'])
+            ? $this->filterPort($parts['port'])
+            : null;
+        $this->path = isset($parts['path'])
+            ? $this->filterPath($parts['path'])
+            : '';
+        $this->query = isset($parts['query'])
+            ? $this->filterQueryAndFragment($parts['query'])
+            : '';
+        $this->fragment = isset($parts['fragment'])
+            ? $this->filterQueryAndFragment($parts['fragment'])
+            : '';
+        if (isset($parts['pass'])) {
+            $this->userInfo .= ':' . $parts['pass'];
+        }
+
+        $this->removeDefaultPort();
+    }
+
+    /**
+     * @param string $scheme
+     *
+     * @return string
+     *
+     * @throws \InvalidArgumentException If the scheme is invalid.
+     */
+    private function filterScheme($scheme)
+    {
+        if (!is_string($scheme)) {
+            throw new \InvalidArgumentException('Scheme must be a string');
+        }
+
+        return strtolower($scheme);
+    }
+
+    /**
+     * @param string $host
+     *
+     * @return string
+     *
+     * @throws \InvalidArgumentException If the host is invalid.
+     */
+    private function filterHost($host)
+    {
+        if (!is_string($host)) {
+            throw new \InvalidArgumentException('Host must be a string');
+        }
+
+        return strtolower($host);
+    }
+
+    /**
+     * @param int|null $port
+     *
+     * @return int|null
+     *
+     * @throws \InvalidArgumentException If the port is invalid.
+     */
+    private function filterPort($port)
+    {
+        if ($port === null) {
+            return null;
+        }
+
+        $port = (int) $port;
+        if (1 > $port || 0xffff < $port) {
+            throw new \InvalidArgumentException(
+                sprintf('Invalid port: %d. Must be between 1 and 65535', $port)
+            );
+        }
+
+        return $port;
+    }
+
+    private function removeDefaultPort()
+    {
+        if ($this->port !== null && self::isDefaultPort($this)) {
+            $this->port = null;
+        }
+    }
+
+    /**
+     * Filters the path of a URI
+     *
+     * @param string $path
+     *
+     * @return string
+     *
+     * @throws \InvalidArgumentException If the path is invalid.
+     */
+    private function filterPath($path)
+    {
+        if (!is_string($path)) {
+            throw new \InvalidArgumentException('Path must be a string');
+        }
+
+        return preg_replace_callback(
+            '/(?:[^' . self::$charUnreserved . self::$charSubDelims . '%:@\/]++|%(?![A-Fa-f0-9]{2}))/',
+            [$this, 'rawurlencodeMatchZero'],
+            $path
+        );
+    }
+
+    /**
+     * Filters the query string or fragment of a URI.
+     *
+     * @param string $str
+     *
+     * @return string
+     *
+     * @throws \InvalidArgumentException If the query or fragment is invalid.
+     */
+    private function filterQueryAndFragment($str)
+    {
+        if (!is_string($str)) {
+            throw new \InvalidArgumentException('Query and fragment must be a string');
+        }
+
+        return preg_replace_callback(
+            '/(?:[^' . self::$charUnreserved . self::$charSubDelims . '%:@\/\?]++|%(?![A-Fa-f0-9]{2}))/',
+            [$this, 'rawurlencodeMatchZero'],
+            $str
+        );
+    }
+
+    private function rawurlencodeMatchZero(array $match)
+    {
+        return rawurlencode($match[0]);
+    }
+
+    private function validateState()
+    {
+        if ($this->host === '' && ($this->scheme === 'http' || $this->scheme === 'https')) {
+            $this->host = self::HTTP_DEFAULT_HOST;
+        }
+
+        if ($this->getAuthority() === '') {
+            if (0 === strpos($this->path, '//')) {
+                throw new \InvalidArgumentException('The path of a URI without an authority must not start with two slashes "//"');
+            }
+            if ($this->scheme === '' && false !== strpos(explode('/', $this->path, 2)[0], ':')) {
+                throw new \InvalidArgumentException('A relative URI must not have a path beginning with a segment containing a colon');
+            }
+        } elseif (isset($this->path[0]) && $this->path[0] !== '/') {
+            @trigger_error(
+                'The path of a URI with an authority must start with a slash "/" or be empty. Automagically fixing the URI ' .
+                'by adding a leading slash to the path is deprecated since version 1.4 and will throw an exception instead.',
+                E_USER_DEPRECATED
+            );
+            $this->path = '/'. $this->path;
+            //throw new \InvalidArgumentException('The path of a URI with an authority must start with a slash "/" or be empty');
+        }
+    }
+}

+ 216 - 0
api/vendor/guzzlehttp/psr7/src/UriNormalizer.php

@@ -0,0 +1,216 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\UriInterface;
+
+/**
+ * Provides methods to normalize and compare URIs.
+ *
+ * @author Tobias Schultze
+ *
+ * @link https://tools.ietf.org/html/rfc3986#section-6
+ */
+final class UriNormalizer
+{
+    /**
+     * Default normalizations which only include the ones that preserve semantics.
+     *
+     * self::CAPITALIZE_PERCENT_ENCODING | self::DECODE_UNRESERVED_CHARACTERS | self::CONVERT_EMPTY_PATH |
+     * self::REMOVE_DEFAULT_HOST | self::REMOVE_DEFAULT_PORT | self::REMOVE_DOT_SEGMENTS
+     */
+    const PRESERVING_NORMALIZATIONS = 63;
+
+    /**
+     * All letters within a percent-encoding triplet (e.g., "%3A") are case-insensitive, and should be capitalized.
+     *
+     * Example: http://example.org/a%c2%b1b → http://example.org/a%C2%B1b
+     */
+    const CAPITALIZE_PERCENT_ENCODING = 1;
+
+    /**
+     * Decodes percent-encoded octets of unreserved characters.
+     *
+     * For consistency, percent-encoded octets in the ranges of ALPHA (%41–%5A and %61–%7A), DIGIT (%30–%39),
+     * hyphen (%2D), period (%2E), underscore (%5F), or tilde (%7E) should not be created by URI producers and,
+     * when found in a URI, should be decoded to their corresponding unreserved characters by URI normalizers.
+     *
+     * Example: http://example.org/%7Eusern%61me/ → http://example.org/~username/
+     */
+    const DECODE_UNRESERVED_CHARACTERS = 2;
+
+    /**
+     * Converts the empty path to "/" for http and https URIs.
+     *
+     * Example: http://example.org → http://example.org/
+     */
+    const CONVERT_EMPTY_PATH = 4;
+
+    /**
+     * Removes the default host of the given URI scheme from the URI.
+     *
+     * Only the "file" scheme defines the default host "localhost".
+     * All of `file:/myfile`, `file:///myfile`, and `file://localhost/myfile`
+     * are equivalent according to RFC 3986. The first format is not accepted
+     * by PHPs stream functions and thus already normalized implicitly to the
+     * second format in the Uri class. See `GuzzleHttp\Psr7\Uri::composeComponents`.
+     *
+     * Example: file://localhost/myfile → file:///myfile
+     */
+    const REMOVE_DEFAULT_HOST = 8;
+
+    /**
+     * Removes the default port of the given URI scheme from the URI.
+     *
+     * Example: http://example.org:80/ → http://example.org/
+     */
+    const REMOVE_DEFAULT_PORT = 16;
+
+    /**
+     * Removes unnecessary dot-segments.
+     *
+     * Dot-segments in relative-path references are not removed as it would
+     * change the semantics of the URI reference.
+     *
+     * Example: http://example.org/../a/b/../c/./d.html → http://example.org/a/c/d.html
+     */
+    const REMOVE_DOT_SEGMENTS = 32;
+
+    /**
+     * Paths which include two or more adjacent slashes are converted to one.
+     *
+     * Webservers usually ignore duplicate slashes and treat those URIs equivalent.
+     * But in theory those URIs do not need to be equivalent. So this normalization
+     * may change the semantics. Encoded slashes (%2F) are not removed.
+     *
+     * Example: http://example.org//foo///bar.html → http://example.org/foo/bar.html
+     */
+    const REMOVE_DUPLICATE_SLASHES = 64;
+
+    /**
+     * Sort query parameters with their values in alphabetical order.
+     *
+     * However, the order of parameters in a URI may be significant (this is not defined by the standard).
+     * So this normalization is not safe and may change the semantics of the URI.
+     *
+     * Example: ?lang=en&article=fred → ?article=fred&lang=en
+     *
+     * Note: The sorting is neither locale nor Unicode aware (the URI query does not get decoded at all) as the
+     * purpose is to be able to compare URIs in a reproducible way, not to have the params sorted perfectly.
+     */
+    const SORT_QUERY_PARAMETERS = 128;
+
+    /**
+     * Returns a normalized URI.
+     *
+     * The scheme and host component are already normalized to lowercase per PSR-7 UriInterface.
+     * This methods adds additional normalizations that can be configured with the $flags parameter.
+     *
+     * PSR-7 UriInterface cannot distinguish between an empty component and a missing component as
+     * getQuery(), getFragment() etc. always return a string. This means the URIs "/?#" and "/" are
+     * treated equivalent which is not necessarily true according to RFC 3986. But that difference
+     * is highly uncommon in reality. So this potential normalization is implied in PSR-7 as well.
+     *
+     * @param UriInterface $uri   The URI to normalize
+     * @param int          $flags A bitmask of normalizations to apply, see constants
+     *
+     * @return UriInterface The normalized URI
+     * @link https://tools.ietf.org/html/rfc3986#section-6.2
+     */
+    public static function normalize(UriInterface $uri, $flags = self::PRESERVING_NORMALIZATIONS)
+    {
+        if ($flags & self::CAPITALIZE_PERCENT_ENCODING) {
+            $uri = self::capitalizePercentEncoding($uri);
+        }
+
+        if ($flags & self::DECODE_UNRESERVED_CHARACTERS) {
+            $uri = self::decodeUnreservedCharacters($uri);
+        }
+
+        if ($flags & self::CONVERT_EMPTY_PATH && $uri->getPath() === '' &&
+            ($uri->getScheme() === 'http' || $uri->getScheme() === 'https')
+        ) {
+            $uri = $uri->withPath('/');
+        }
+
+        if ($flags & self::REMOVE_DEFAULT_HOST && $uri->getScheme() === 'file' && $uri->getHost() === 'localhost') {
+            $uri = $uri->withHost('');
+        }
+
+        if ($flags & self::REMOVE_DEFAULT_PORT && $uri->getPort() !== null && Uri::isDefaultPort($uri)) {
+            $uri = $uri->withPort(null);
+        }
+
+        if ($flags & self::REMOVE_DOT_SEGMENTS && !Uri::isRelativePathReference($uri)) {
+            $uri = $uri->withPath(UriResolver::removeDotSegments($uri->getPath()));
+        }
+
+        if ($flags & self::REMOVE_DUPLICATE_SLASHES) {
+            $uri = $uri->withPath(preg_replace('#//++#', '/', $uri->getPath()));
+        }
+
+        if ($flags & self::SORT_QUERY_PARAMETERS && $uri->getQuery() !== '') {
+            $queryKeyValues = explode('&', $uri->getQuery());
+            sort($queryKeyValues);
+            $uri = $uri->withQuery(implode('&', $queryKeyValues));
+        }
+
+        return $uri;
+    }
+
+    /**
+     * Whether two URIs can be considered equivalent.
+     *
+     * Both URIs are normalized automatically before comparison with the given $normalizations bitmask. The method also
+     * accepts relative URI references and returns true when they are equivalent. This of course assumes they will be
+     * resolved against the same base URI. If this is not the case, determination of equivalence or difference of
+     * relative references does not mean anything.
+     *
+     * @param UriInterface $uri1           An URI to compare
+     * @param UriInterface $uri2           An URI to compare
+     * @param int          $normalizations A bitmask of normalizations to apply, see constants
+     *
+     * @return bool
+     * @link https://tools.ietf.org/html/rfc3986#section-6.1
+     */
+    public static function isEquivalent(UriInterface $uri1, UriInterface $uri2, $normalizations = self::PRESERVING_NORMALIZATIONS)
+    {
+        return (string) self::normalize($uri1, $normalizations) === (string) self::normalize($uri2, $normalizations);
+    }
+
+    private static function capitalizePercentEncoding(UriInterface $uri)
+    {
+        $regex = '/(?:%[A-Fa-f0-9]{2})++/';
+
+        $callback = function (array $match) {
+            return strtoupper($match[0]);
+        };
+
+        return
+            $uri->withPath(
+                preg_replace_callback($regex, $callback, $uri->getPath())
+            )->withQuery(
+                preg_replace_callback($regex, $callback, $uri->getQuery())
+            );
+    }
+
+    private static function decodeUnreservedCharacters(UriInterface $uri)
+    {
+        $regex = '/%(?:2D|2E|5F|7E|3[0-9]|[46][1-9A-F]|[57][0-9A])/i';
+
+        $callback = function (array $match) {
+            return rawurldecode($match[0]);
+        };
+
+        return
+            $uri->withPath(
+                preg_replace_callback($regex, $callback, $uri->getPath())
+            )->withQuery(
+                preg_replace_callback($regex, $callback, $uri->getQuery())
+            );
+    }
+
+    private function __construct()
+    {
+        // cannot be instantiated
+    }
+}

+ 219 - 0
api/vendor/guzzlehttp/psr7/src/UriResolver.php

@@ -0,0 +1,219 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\UriInterface;
+
+/**
+ * Resolves a URI reference in the context of a base URI and the opposite way.
+ *
+ * @author Tobias Schultze
+ *
+ * @link https://tools.ietf.org/html/rfc3986#section-5
+ */
+final class UriResolver
+{
+    /**
+     * Removes dot segments from a path and returns the new path.
+     *
+     * @param string $path
+     *
+     * @return string
+     * @link http://tools.ietf.org/html/rfc3986#section-5.2.4
+     */
+    public static function removeDotSegments($path)
+    {
+        if ($path === '' || $path === '/') {
+            return $path;
+        }
+
+        $results = [];
+        $segments = explode('/', $path);
+        foreach ($segments as $segment) {
+            if ($segment === '..') {
+                array_pop($results);
+            } elseif ($segment !== '.') {
+                $results[] = $segment;
+            }
+        }
+
+        $newPath = implode('/', $results);
+
+        if ($path[0] === '/' && (!isset($newPath[0]) || $newPath[0] !== '/')) {
+            // Re-add the leading slash if necessary for cases like "/.."
+            $newPath = '/' . $newPath;
+        } elseif ($newPath !== '' && ($segment === '.' || $segment === '..')) {
+            // Add the trailing slash if necessary
+            // If newPath is not empty, then $segment must be set and is the last segment from the foreach
+            $newPath .= '/';
+        }
+
+        return $newPath;
+    }
+
+    /**
+     * Converts the relative URI into a new URI that is resolved against the base URI.
+     *
+     * @param UriInterface $base Base URI
+     * @param UriInterface $rel  Relative URI
+     *
+     * @return UriInterface
+     * @link http://tools.ietf.org/html/rfc3986#section-5.2
+     */
+    public static function resolve(UriInterface $base, UriInterface $rel)
+    {
+        if ((string) $rel === '') {
+            // we can simply return the same base URI instance for this same-document reference
+            return $base;
+        }
+
+        if ($rel->getScheme() != '') {
+            return $rel->withPath(self::removeDotSegments($rel->getPath()));
+        }
+
+        if ($rel->getAuthority() != '') {
+            $targetAuthority = $rel->getAuthority();
+            $targetPath = self::removeDotSegments($rel->getPath());
+            $targetQuery = $rel->getQuery();
+        } else {
+            $targetAuthority = $base->getAuthority();
+            if ($rel->getPath() === '') {
+                $targetPath = $base->getPath();
+                $targetQuery = $rel->getQuery() != '' ? $rel->getQuery() : $base->getQuery();
+            } else {
+                if ($rel->getPath()[0] === '/') {
+                    $targetPath = $rel->getPath();
+                } else {
+                    if ($targetAuthority != '' && $base->getPath() === '') {
+                        $targetPath = '/' . $rel->getPath();
+                    } else {
+                        $lastSlashPos = strrpos($base->getPath(), '/');
+                        if ($lastSlashPos === false) {
+                            $targetPath = $rel->getPath();
+                        } else {
+                            $targetPath = substr($base->getPath(), 0, $lastSlashPos + 1) . $rel->getPath();
+                        }
+                    }
+                }
+                $targetPath = self::removeDotSegments($targetPath);
+                $targetQuery = $rel->getQuery();
+            }
+        }
+
+        return new Uri(Uri::composeComponents(
+            $base->getScheme(),
+            $targetAuthority,
+            $targetPath,
+            $targetQuery,
+            $rel->getFragment()
+        ));
+    }
+
+    /**
+     * Returns the target URI as a relative reference from the base URI.
+     *
+     * This method is the counterpart to resolve():
+     *
+     *    (string) $target === (string) UriResolver::resolve($base, UriResolver::relativize($base, $target))
+     *
+     * One use-case is to use the current request URI as base URI and then generate relative links in your documents
+     * to reduce the document size or offer self-contained downloadable document archives.
+     *
+     *    $base = new Uri('http://example.com/a/b/');
+     *    echo UriResolver::relativize($base, new Uri('http://example.com/a/b/c'));  // prints 'c'.
+     *    echo UriResolver::relativize($base, new Uri('http://example.com/a/x/y'));  // prints '../x/y'.
+     *    echo UriResolver::relativize($base, new Uri('http://example.com/a/b/?q')); // prints '?q'.
+     *    echo UriResolver::relativize($base, new Uri('http://example.org/a/b/'));   // prints '//example.org/a/b/'.
+     *
+     * This method also accepts a target that is already relative and will try to relativize it further. Only a
+     * relative-path reference will be returned as-is.
+     *
+     *    echo UriResolver::relativize($base, new Uri('/a/b/c'));  // prints 'c' as well
+     *
+     * @param UriInterface $base   Base URI
+     * @param UriInterface $target Target URI
+     *
+     * @return UriInterface The relative URI reference
+     */
+    public static function relativize(UriInterface $base, UriInterface $target)
+    {
+        if ($target->getScheme() !== '' &&
+            ($base->getScheme() !== $target->getScheme() || $target->getAuthority() === '' && $base->getAuthority() !== '')
+        ) {
+            return $target;
+        }
+
+        if (Uri::isRelativePathReference($target)) {
+            // As the target is already highly relative we return it as-is. It would be possible to resolve
+            // the target with `$target = self::resolve($base, $target);` and then try make it more relative
+            // by removing a duplicate query. But let's not do that automatically.
+            return $target;
+        }
+
+        if ($target->getAuthority() !== '' && $base->getAuthority() !== $target->getAuthority()) {
+            return $target->withScheme('');
+        }
+
+        // We must remove the path before removing the authority because if the path starts with two slashes, the URI
+        // would turn invalid. And we also cannot set a relative path before removing the authority, as that is also
+        // invalid.
+        $emptyPathUri = $target->withScheme('')->withPath('')->withUserInfo('')->withPort(null)->withHost('');
+
+        if ($base->getPath() !== $target->getPath()) {
+            return $emptyPathUri->withPath(self::getRelativePath($base, $target));
+        }
+
+        if ($base->getQuery() === $target->getQuery()) {
+            // Only the target fragment is left. And it must be returned even if base and target fragment are the same.
+            return $emptyPathUri->withQuery('');
+        }
+
+        // If the base URI has a query but the target has none, we cannot return an empty path reference as it would
+        // inherit the base query component when resolving.
+        if ($target->getQuery() === '') {
+            $segments = explode('/', $target->getPath());
+            $lastSegment = end($segments);
+
+            return $emptyPathUri->withPath($lastSegment === '' ? './' : $lastSegment);
+        }
+
+        return $emptyPathUri;
+    }
+
+    private static function getRelativePath(UriInterface $base, UriInterface $target)
+    {
+        $sourceSegments = explode('/', $base->getPath());
+        $targetSegments = explode('/', $target->getPath());
+        array_pop($sourceSegments);
+        $targetLastSegment = array_pop($targetSegments);
+        foreach ($sourceSegments as $i => $segment) {
+            if (isset($targetSegments[$i]) && $segment === $targetSegments[$i]) {
+                unset($sourceSegments[$i], $targetSegments[$i]);
+            } else {
+                break;
+            }
+        }
+        $targetSegments[] = $targetLastSegment;
+        $relativePath = str_repeat('../', count($sourceSegments)) . implode('/', $targetSegments);
+
+        // A reference to am empty last segment or an empty first sub-segment must be prefixed with "./".
+        // This also applies to a segment with a colon character (e.g., "file:colon") that cannot be used
+        // as the first segment of a relative-path reference, as it would be mistaken for a scheme name.
+        if ('' === $relativePath || false !== strpos(explode('/', $relativePath, 2)[0], ':')) {
+            $relativePath = "./$relativePath";
+        } elseif ('/' === $relativePath[0]) {
+            if ($base->getAuthority() != '' && $base->getPath() === '') {
+                // In this case an extra slash is added by resolve() automatically. So we must not add one here.
+                $relativePath = ".$relativePath";
+            } else {
+                $relativePath = "./$relativePath";
+            }
+        }
+
+        return $relativePath;
+    }
+
+    private function __construct()
+    {
+        // cannot be instantiated
+    }
+}

+ 828 - 0
api/vendor/guzzlehttp/psr7/src/functions.php

@@ -0,0 +1,828 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\MessageInterface;
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
+use Psr\Http\Message\StreamInterface;
+use Psr\Http\Message\UriInterface;
+
+/**
+ * Returns the string representation of an HTTP message.
+ *
+ * @param MessageInterface $message Message to convert to a string.
+ *
+ * @return string
+ */
+function str(MessageInterface $message)
+{
+    if ($message instanceof RequestInterface) {
+        $msg = trim($message->getMethod() . ' '
+                . $message->getRequestTarget())
+            . ' HTTP/' . $message->getProtocolVersion();
+        if (!$message->hasHeader('host')) {
+            $msg .= "\r\nHost: " . $message->getUri()->getHost();
+        }
+    } elseif ($message instanceof ResponseInterface) {
+        $msg = 'HTTP/' . $message->getProtocolVersion() . ' '
+            . $message->getStatusCode() . ' '
+            . $message->getReasonPhrase();
+    } else {
+        throw new \InvalidArgumentException('Unknown message type');
+    }
+
+    foreach ($message->getHeaders() as $name => $values) {
+        $msg .= "\r\n{$name}: " . implode(', ', $values);
+    }
+
+    return "{$msg}\r\n\r\n" . $message->getBody();
+}
+
+/**
+ * Returns a UriInterface for the given value.
+ *
+ * This function accepts a string or {@see Psr\Http\Message\UriInterface} and
+ * returns a UriInterface for the given value. If the value is already a
+ * `UriInterface`, it is returned as-is.
+ *
+ * @param string|UriInterface $uri
+ *
+ * @return UriInterface
+ * @throws \InvalidArgumentException
+ */
+function uri_for($uri)
+{
+    if ($uri instanceof UriInterface) {
+        return $uri;
+    } elseif (is_string($uri)) {
+        return new Uri($uri);
+    }
+
+    throw new \InvalidArgumentException('URI must be a string or UriInterface');
+}
+
+/**
+ * Create a new stream based on the input type.
+ *
+ * Options is an associative array that can contain the following keys:
+ * - metadata: Array of custom metadata.
+ * - size: Size of the stream.
+ *
+ * @param resource|string|null|int|float|bool|StreamInterface|callable $resource Entity body data
+ * @param array                                                        $options  Additional options
+ *
+ * @return Stream
+ * @throws \InvalidArgumentException if the $resource arg is not valid.
+ */
+function stream_for($resource = '', array $options = [])
+{
+    if (is_scalar($resource)) {
+        $stream = fopen('php://temp', 'r+');
+        if ($resource !== '') {
+            fwrite($stream, $resource);
+            fseek($stream, 0);
+        }
+        return new Stream($stream, $options);
+    }
+
+    switch (gettype($resource)) {
+        case 'resource':
+            return new Stream($resource, $options);
+        case 'object':
+            if ($resource instanceof StreamInterface) {
+                return $resource;
+            } elseif ($resource instanceof \Iterator) {
+                return new PumpStream(function () use ($resource) {
+                    if (!$resource->valid()) {
+                        return false;
+                    }
+                    $result = $resource->current();
+                    $resource->next();
+                    return $result;
+                }, $options);
+            } elseif (method_exists($resource, '__toString')) {
+                return stream_for((string) $resource, $options);
+            }
+            break;
+        case 'NULL':
+            return new Stream(fopen('php://temp', 'r+'), $options);
+    }
+
+    if (is_callable($resource)) {
+        return new PumpStream($resource, $options);
+    }
+
+    throw new \InvalidArgumentException('Invalid resource type: ' . gettype($resource));
+}
+
+/**
+ * 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 string|array $header Header to parse into components.
+ *
+ * @return array Returns the parsed header values.
+ */
+function parse_header($header)
+{
+    static $trimmed = "\"'  \n\t\r";
+    $params = $matches = [];
+
+    foreach (normalize_header($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 string|array $header Header to normalize.
+ *
+ * @return array Returns the normalized header field values.
+ */
+function normalize_header($header)
+{
+    if (!is_array($header)) {
+        return array_map('trim', explode(',', $header));
+    }
+
+    $result = [];
+    foreach ($header as $value) {
+        foreach ((array) $value as $v) {
+            if (strpos($v, ',') === false) {
+                $result[] = $v;
+                continue;
+            }
+            foreach (preg_split('/,(?=([^"]*"[^"]*")*[^"]*$)/', $v) as $vv) {
+                $result[] = trim($vv);
+            }
+        }
+    }
+
+    return $result;
+}
+
+/**
+ * Clone and modify a request with the given changes.
+ *
+ * The changes can be one of:
+ * - method: (string) Changes the HTTP method.
+ * - set_headers: (array) Sets the given headers.
+ * - remove_headers: (array) Remove the given headers.
+ * - body: (mixed) Sets the given body.
+ * - uri: (UriInterface) Set the URI.
+ * - query: (string) Set the query string value of the URI.
+ * - version: (string) Set the protocol version.
+ *
+ * @param RequestInterface $request Request to clone and modify.
+ * @param array            $changes Changes to apply.
+ *
+ * @return RequestInterface
+ */
+function modify_request(RequestInterface $request, array $changes)
+{
+    if (!$changes) {
+        return $request;
+    }
+
+    $headers = $request->getHeaders();
+
+    if (!isset($changes['uri'])) {
+        $uri = $request->getUri();
+    } else {
+        // Remove the host header if one is on the URI
+        if ($host = $changes['uri']->getHost()) {
+            $changes['set_headers']['Host'] = $host;
+
+            if ($port = $changes['uri']->getPort()) {
+                $standardPorts = ['http' => 80, 'https' => 443];
+                $scheme = $changes['uri']->getScheme();
+                if (isset($standardPorts[$scheme]) && $port != $standardPorts[$scheme]) {
+                    $changes['set_headers']['Host'] .= ':'.$port;
+                }
+            }
+        }
+        $uri = $changes['uri'];
+    }
+
+    if (!empty($changes['remove_headers'])) {
+        $headers = _caseless_remove($changes['remove_headers'], $headers);
+    }
+
+    if (!empty($changes['set_headers'])) {
+        $headers = _caseless_remove(array_keys($changes['set_headers']), $headers);
+        $headers = $changes['set_headers'] + $headers;
+    }
+
+    if (isset($changes['query'])) {
+        $uri = $uri->withQuery($changes['query']);
+    }
+
+    if ($request instanceof ServerRequestInterface) {
+        return new ServerRequest(
+            isset($changes['method']) ? $changes['method'] : $request->getMethod(),
+            $uri,
+            $headers,
+            isset($changes['body']) ? $changes['body'] : $request->getBody(),
+            isset($changes['version'])
+                ? $changes['version']
+                : $request->getProtocolVersion(),
+            $request->getServerParams()
+        );
+    }
+
+    return new Request(
+        isset($changes['method']) ? $changes['method'] : $request->getMethod(),
+        $uri,
+        $headers,
+        isset($changes['body']) ? $changes['body'] : $request->getBody(),
+        isset($changes['version'])
+            ? $changes['version']
+            : $request->getProtocolVersion()
+    );
+}
+
+/**
+ * Attempts to rewind a message body and throws an exception on failure.
+ *
+ * The body of the message will only be rewound if a call to `tell()` returns a
+ * value other than `0`.
+ *
+ * @param MessageInterface $message Message to rewind
+ *
+ * @throws \RuntimeException
+ */
+function rewind_body(MessageInterface $message)
+{
+    $body = $message->getBody();
+
+    if ($body->tell()) {
+        $body->rewind();
+    }
+}
+
+/**
+ * Safely opens a PHP stream resource using a filename.
+ *
+ * When fopen fails, PHP normally raises a warning. This function adds an
+ * error handler that checks for errors and throws an exception instead.
+ *
+ * @param string $filename File to open
+ * @param string $mode     Mode used to open the file
+ *
+ * @return resource
+ * @throws \RuntimeException if the file cannot be opened
+ */
+function try_fopen($filename, $mode)
+{
+    $ex = null;
+    set_error_handler(function () use ($filename, $mode, &$ex) {
+        $ex = new \RuntimeException(sprintf(
+            'Unable to open %s using mode %s: %s',
+            $filename,
+            $mode,
+            func_get_args()[1]
+        ));
+    });
+
+    $handle = fopen($filename, $mode);
+    restore_error_handler();
+
+    if ($ex) {
+        /** @var $ex \RuntimeException */
+        throw $ex;
+    }
+
+    return $handle;
+}
+
+/**
+ * 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
+ * @throws \RuntimeException on error.
+ */
+function copy_to_string(StreamInterface $stream, $maxLen = -1)
+{
+    $buffer = '';
+
+    if ($maxLen === -1) {
+        while (!$stream->eof()) {
+            $buf = $stream->read(1048576);
+            // Using a loose equality here to match on '' and false.
+            if ($buf == null) {
+                break;
+            }
+            $buffer .= $buf;
+        }
+        return $buffer;
+    }
+
+    $len = 0;
+    while (!$stream->eof() && $len < $maxLen) {
+        $buf = $stream->read($maxLen - $len);
+        // Using a loose equality here to match on '' and false.
+        if ($buf == null) {
+            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.
+ *
+ * @throws \RuntimeException on error.
+ */
+function copy_to_stream(
+    StreamInterface $source,
+    StreamInterface $dest,
+    $maxLen = -1
+) {
+    $bufferSize = 8192;
+
+    if ($maxLen === -1) {
+        while (!$source->eof()) {
+            if (!$dest->write($source->read($bufferSize))) {
+                break;
+            }
+        }
+    } else {
+        $remaining = $maxLen;
+        while ($remaining > 0 && !$source->eof()) {
+            $buf = $source->read(min($bufferSize, $remaining));
+            $len = strlen($buf);
+            if (!$len) {
+                break;
+            }
+            $remaining -= $len;
+            $dest->write($buf);
+        }
+    }
+}
+
+/**
+ * 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 string Returns the hash of the stream
+ * @throws \RuntimeException on error.
+ */
+function hash(
+    StreamInterface $stream,
+    $algo,
+    $rawOutput = false
+) {
+    $pos = $stream->tell();
+
+    if ($pos > 0) {
+        $stream->rewind();
+    }
+
+    $ctx = hash_init($algo);
+    while (!$stream->eof()) {
+        hash_update($ctx, $stream->read(1048576));
+    }
+
+    $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 readline(StreamInterface $stream, $maxLength = null)
+{
+    $buffer = '';
+    $size = 0;
+
+    while (!$stream->eof()) {
+        // Using a loose equality here to match on '' and false.
+        if (null == ($byte = $stream->read(1))) {
+            return $buffer;
+        }
+        $buffer .= $byte;
+        // Break when a new line is found or the max length - 1 is reached
+        if ($byte === "\n" || ++$size === $maxLength - 1) {
+            break;
+        }
+    }
+
+    return $buffer;
+}
+
+/**
+ * Parses a request message string into a request object.
+ *
+ * @param string $message Request message string.
+ *
+ * @return Request
+ */
+function parse_request($message)
+{
+    $data = _parse_message($message);
+    $matches = [];
+    if (!preg_match('/^[\S]+\s+([a-zA-Z]+:\/\/|\/).*/', $data['start-line'], $matches)) {
+        throw new \InvalidArgumentException('Invalid request string');
+    }
+    $parts = explode(' ', $data['start-line'], 3);
+    $version = isset($parts[2]) ? explode('/', $parts[2])[1] : '1.1';
+
+    $request = new Request(
+        $parts[0],
+        $matches[1] === '/' ? _parse_request_uri($parts[1], $data['headers']) : $parts[1],
+        $data['headers'],
+        $data['body'],
+        $version
+    );
+
+    return $matches[1] === '/' ? $request : $request->withRequestTarget($parts[1]);
+}
+
+/**
+ * Parses a response message string into a response object.
+ *
+ * @param string $message Response message string.
+ *
+ * @return Response
+ */
+function parse_response($message)
+{
+    $data = _parse_message($message);
+    // According to https://tools.ietf.org/html/rfc7230#section-3.1.2 the space
+    // between status-code and reason-phrase is required. But browsers accept
+    // responses without space and reason as well.
+    if (!preg_match('/^HTTP\/.* [0-9]{3}( .*|$)/', $data['start-line'])) {
+        throw new \InvalidArgumentException('Invalid response string');
+    }
+    $parts = explode(' ', $data['start-line'], 3);
+
+    return new Response(
+        $parts[1],
+        $data['headers'],
+        $data['body'],
+        explode('/', $parts[0])[1],
+        isset($parts[2]) ? $parts[2] : null
+    );
+}
+
+/**
+ * Parse a query string into an associative array.
+ *
+ * If multiple values are found for the same key, the value of that key
+ * value pair will become an array. This function does not parse nested
+ * PHP style arrays into an associative array (e.g., foo[a]=1&foo[b]=2 will
+ * be parsed into ['foo[a]' => '1', 'foo[b]' => '2']).
+ *
+ * @param string      $str         Query string to parse
+ * @param bool|string $urlEncoding How the query string is encoded
+ *
+ * @return array
+ */
+function parse_query($str, $urlEncoding = true)
+{
+    $result = [];
+
+    if ($str === '') {
+        return $result;
+    }
+
+    if ($urlEncoding === true) {
+        $decoder = function ($value) {
+            return rawurldecode(str_replace('+', ' ', $value));
+        };
+    } elseif ($urlEncoding == PHP_QUERY_RFC3986) {
+        $decoder = 'rawurldecode';
+    } elseif ($urlEncoding == PHP_QUERY_RFC1738) {
+        $decoder = 'urldecode';
+    } else {
+        $decoder = function ($str) { return $str; };
+    }
+
+    foreach (explode('&', $str) as $kvp) {
+        $parts = explode('=', $kvp, 2);
+        $key = $decoder($parts[0]);
+        $value = isset($parts[1]) ? $decoder($parts[1]) : null;
+        if (!isset($result[$key])) {
+            $result[$key] = $value;
+        } else {
+            if (!is_array($result[$key])) {
+                $result[$key] = [$result[$key]];
+            }
+            $result[$key][] = $value;
+        }
+    }
+
+    return $result;
+}
+
+/**
+ * Build a query string from an array of key value pairs.
+ *
+ * This function can use the return value of parse_query() to build a query
+ * string. This function does not modify the provided keys when an array is
+ * encountered (like http_build_query would).
+ *
+ * @param array     $params   Query string parameters.
+ * @param int|false $encoding Set to false to not encode, PHP_QUERY_RFC3986
+ *                            to encode using RFC3986, or PHP_QUERY_RFC1738
+ *                            to encode using RFC1738.
+ * @return string
+ */
+function build_query(array $params, $encoding = PHP_QUERY_RFC3986)
+{
+    if (!$params) {
+        return '';
+    }
+
+    if ($encoding === false) {
+        $encoder = function ($str) { return $str; };
+    } elseif ($encoding === PHP_QUERY_RFC3986) {
+        $encoder = 'rawurlencode';
+    } elseif ($encoding === PHP_QUERY_RFC1738) {
+        $encoder = 'urlencode';
+    } else {
+        throw new \InvalidArgumentException('Invalid type');
+    }
+
+    $qs = '';
+    foreach ($params as $k => $v) {
+        $k = $encoder($k);
+        if (!is_array($v)) {
+            $qs .= $k;
+            if ($v !== null) {
+                $qs .= '=' . $encoder($v);
+            }
+            $qs .= '&';
+        } else {
+            foreach ($v as $vv) {
+                $qs .= $k;
+                if ($vv !== null) {
+                    $qs .= '=' . $encoder($vv);
+                }
+                $qs .= '&';
+            }
+        }
+    }
+
+    return $qs ? (string) substr($qs, 0, -1) : '';
+}
+
+/**
+ * Determines the mimetype of a file by looking at its extension.
+ *
+ * @param $filename
+ *
+ * @return null|string
+ */
+function mimetype_from_filename($filename)
+{
+    return mimetype_from_extension(pathinfo($filename, PATHINFO_EXTENSION));
+}
+
+/**
+ * Maps a file extensions to a mimetype.
+ *
+ * @param $extension string The file extension.
+ *
+ * @return string|null
+ * @link http://svn.apache.org/repos/asf/httpd/httpd/branches/1.3.x/conf/mime.types
+ */
+function mimetype_from_extension($extension)
+{
+    static $mimetypes = [
+        '7z' => 'application/x-7z-compressed',
+        'aac' => 'audio/x-aac',
+        'ai' => 'application/postscript',
+        'aif' => 'audio/x-aiff',
+        'asc' => 'text/plain',
+        'asf' => 'video/x-ms-asf',
+        'atom' => 'application/atom+xml',
+        'avi' => 'video/x-msvideo',
+        'bmp' => 'image/bmp',
+        'bz2' => 'application/x-bzip2',
+        'cer' => 'application/pkix-cert',
+        'crl' => 'application/pkix-crl',
+        'crt' => 'application/x-x509-ca-cert',
+        'css' => 'text/css',
+        'csv' => 'text/csv',
+        'cu' => 'application/cu-seeme',
+        'deb' => 'application/x-debian-package',
+        'doc' => 'application/msword',
+        'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
+        'dvi' => 'application/x-dvi',
+        'eot' => 'application/vnd.ms-fontobject',
+        'eps' => 'application/postscript',
+        'epub' => 'application/epub+zip',
+        'etx' => 'text/x-setext',
+        'flac' => 'audio/flac',
+        'flv' => 'video/x-flv',
+        'gif' => 'image/gif',
+        'gz' => 'application/gzip',
+        'htm' => 'text/html',
+        'html' => 'text/html',
+        'ico' => 'image/x-icon',
+        'ics' => 'text/calendar',
+        'ini' => 'text/plain',
+        'iso' => 'application/x-iso9660-image',
+        'jar' => 'application/java-archive',
+        'jpe' => 'image/jpeg',
+        'jpeg' => 'image/jpeg',
+        'jpg' => 'image/jpeg',
+        'js' => 'text/javascript',
+        'json' => 'application/json',
+        'latex' => 'application/x-latex',
+        'log' => 'text/plain',
+        'm4a' => 'audio/mp4',
+        'm4v' => 'video/mp4',
+        'mid' => 'audio/midi',
+        'midi' => 'audio/midi',
+        'mov' => 'video/quicktime',
+        'mp3' => 'audio/mpeg',
+        'mp4' => 'video/mp4',
+        'mp4a' => 'audio/mp4',
+        'mp4v' => 'video/mp4',
+        'mpe' => 'video/mpeg',
+        'mpeg' => 'video/mpeg',
+        'mpg' => 'video/mpeg',
+        'mpg4' => 'video/mp4',
+        'oga' => 'audio/ogg',
+        'ogg' => 'audio/ogg',
+        'ogv' => 'video/ogg',
+        'ogx' => 'application/ogg',
+        'pbm' => 'image/x-portable-bitmap',
+        'pdf' => 'application/pdf',
+        'pgm' => 'image/x-portable-graymap',
+        'png' => 'image/png',
+        'pnm' => 'image/x-portable-anymap',
+        'ppm' => 'image/x-portable-pixmap',
+        'ppt' => 'application/vnd.ms-powerpoint',
+        'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
+        'ps' => 'application/postscript',
+        'qt' => 'video/quicktime',
+        'rar' => 'application/x-rar-compressed',
+        'ras' => 'image/x-cmu-raster',
+        'rss' => 'application/rss+xml',
+        'rtf' => 'application/rtf',
+        'sgm' => 'text/sgml',
+        'sgml' => 'text/sgml',
+        'svg' => 'image/svg+xml',
+        'swf' => 'application/x-shockwave-flash',
+        'tar' => 'application/x-tar',
+        'tif' => 'image/tiff',
+        'tiff' => 'image/tiff',
+        'torrent' => 'application/x-bittorrent',
+        'ttf' => 'application/x-font-ttf',
+        'txt' => 'text/plain',
+        'wav' => 'audio/x-wav',
+        'webm' => 'video/webm',
+        'wma' => 'audio/x-ms-wma',
+        'wmv' => 'video/x-ms-wmv',
+        'woff' => 'application/x-font-woff',
+        'wsdl' => 'application/wsdl+xml',
+        'xbm' => 'image/x-xbitmap',
+        'xls' => 'application/vnd.ms-excel',
+        'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
+        'xml' => 'application/xml',
+        'xpm' => 'image/x-xpixmap',
+        'xwd' => 'image/x-xwindowdump',
+        'yaml' => 'text/yaml',
+        'yml' => 'text/yaml',
+        'zip' => 'application/zip',
+    ];
+
+    $extension = strtolower($extension);
+
+    return isset($mimetypes[$extension])
+        ? $mimetypes[$extension]
+        : null;
+}
+
+/**
+ * Parses an HTTP message into an associative array.
+ *
+ * The array contains the "start-line" key containing the start line of
+ * the message, "headers" key containing an associative array of header
+ * array values, and a "body" key containing the body of the message.
+ *
+ * @param string $message HTTP request or response to parse.
+ *
+ * @return array
+ * @internal
+ */
+function _parse_message($message)
+{
+    if (!$message) {
+        throw new \InvalidArgumentException('Invalid message');
+    }
+
+    // Iterate over each line in the message, accounting for line endings
+    $lines = preg_split('/(\\r?\\n)/', $message, -1, PREG_SPLIT_DELIM_CAPTURE);
+    $result = ['start-line' => array_shift($lines), 'headers' => [], 'body' => ''];
+    array_shift($lines);
+
+    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) {
+                $result['body'] = implode('', array_slice($lines, $i + 2));
+            }
+            break;
+        }
+        if (strpos($line, ':')) {
+            $parts = explode(':', $line, 2);
+            $key = trim($parts[0]);
+            $value = isset($parts[1]) ? trim($parts[1]) : '';
+            $result['headers'][$key][] = $value;
+        }
+    }
+
+    return $result;
+}
+
+/**
+ * Constructs a URI for an HTTP request message.
+ *
+ * @param string $path    Path from the start-line
+ * @param array  $headers Array of headers (each value an array).
+ *
+ * @return string
+ * @internal
+ */
+function _parse_request_uri($path, array $headers)
+{
+    $hostKey = array_filter(array_keys($headers), function ($k) {
+        return strtolower($k) === 'host';
+    });
+
+    // If no host is found, then a full URI cannot be constructed.
+    if (!$hostKey) {
+        return $path;
+    }
+
+    $host = $headers[reset($hostKey)][0];
+    $scheme = substr($host, -4) === ':443' ? 'https' : 'http';
+
+    return $scheme . '://' . $host . '/' . ltrim($path, '/');
+}
+
+/** @internal */
+function _caseless_remove($keys, array $data)
+{
+    $result = [];
+
+    foreach ($keys as &$key) {
+        $key = strtolower($key);
+    }
+
+    foreach ($data as $k => $v) {
+        if (!in_array(strtolower($k), $keys)) {
+            $result[$k] = $v;
+        }
+    }
+
+    return $result;
+}

+ 6 - 0
api/vendor/guzzlehttp/psr7/src/functions_include.php

@@ -0,0 +1,6 @@
+<?php
+
+// Don't redefine the functions if included multiple times.
+if (!function_exists('GuzzleHttp\Psr7\str')) {
+    require __DIR__ . '/functions.php';
+}

+ 4 - 0
api/vendor/kleiram/transmission-php/.gitignore

@@ -0,0 +1,4 @@
+vendor/
+composer.lock
+composer.phar
+phpunit.xml

+ 9 - 0
api/vendor/kleiram/transmission-php/.travis.yml

@@ -0,0 +1,9 @@
+language: php
+
+php: [5.3, 5.4, 5.5]
+
+before_script:
+    - composer install --dev --prefer-source
+
+script:
+    phpunit --coverage-text

+ 27 - 0
api/vendor/kleiram/transmission-php/CHANGELOG

@@ -0,0 +1,27 @@
+Version     Changes
+
+0.1.0       - Initial release
+
+0.2.0       - Rewrote the entire public API
+
+0.3.0       - Added support for authentication
+
+0.4.0       - The library now requires at least PHP 5.3.2
+            - Added support for getting files downloaded by torrent
+            - Added support for getting trackers used by a torrent
+            - Added support for getting peers connected to
+            - The torrent now contains:
+                * Whether it is finished
+                * The up- and download rate (in bytes/s)
+                * The size of the download (when completed)
+                * The ETA of the download
+                * The percentage of the download completed
+            - Made the authentication more flexible
+            - The client now sends an User-Agent header with each request
+            - Added support for starting, stopping, veryfing and
+              requesting a reannounce of torrents
+
+0.5.0       - Fix a bug in the authentication/authorization mechanism
+            - A whole lot of other stuff including management of the
+              Transmission session (setting global download speed limit
+              and toggling the speed limit among others).

+ 26 - 0
api/vendor/kleiram/transmission-php/LICENSE

@@ -0,0 +1,26 @@
+Copyright (c) 2013, Ramon Kleiss <ramon@cubilon.n>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+   list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+The views and conclusions contained in the software and documentation are those
+of the authors and should not be interpreted as representing official policies,
+either expressed or implied, of the FreeBSD Project.

+ 197 - 0
api/vendor/kleiram/transmission-php/README.md

@@ -0,0 +1,197 @@
+# PHP Transmission API
+
+[![Build Status](https://travis-ci.org/kleiram/transmission-php.png)](https://travis-ci.org/kleiram/transmission-php)
+
+This library provides an interface to the [Transmission](http://transmissionbt.com)
+bit-torrent downloader. It provides means to get and remove torrents from
+the downloader as well as adding new torrents to the download queue.
+
+## Installation
+
+Installation is easy using [Composer](https://getcomposer.org):
+
+```json
+{
+    "require": {
+        "kleiram/transmission-php": "dev-master"
+    }
+}
+```
+
+## Usage
+
+Using the library is as easy as installing it:
+
+```php
+<?php
+use Transmission\Transmission;
+
+$transmission = new Transmission();
+
+// Getting all the torrents currently in the download queue
+$torrents = $transmission->all();
+
+// Getting a specific torrent from the download queue
+$torrent = $transmission->get(1);
+
+// (you can also get a torrent by the hash of the torrent)
+$torrent = $transmission->get(/* torrent hash */);
+
+// Adding a torrent to the download queue
+$torrent = $transmission->add(/* path to torrent */);
+
+// Removing a torrent from the download queue
+$torrent = $transmission->get(1);
+$torrent->remove();
+
+// Or if you want to delete all local data too
+$torrent->remove(true);
+
+// You can also get the Trackers that the torrent currently uses
+// These are instances of the Transmission\Model\Tracker class
+$trackers = $torrent->getTrackers();
+
+// Getting the files downloaded by the torrent are available too
+// These are instances of Transmission\Model\File
+$files = $torrent->getFiles();
+
+// You can start, stop, verify the torrent and ask the tracker for
+// more peers to connect to
+$torrent->stop();
+$torrent->start();
+$torrent->start(true); // Pass true if you want to start the torrent immediatly
+$torrent->verify();
+$torrent->reannounce();
+```
+
+To find out which information is contained by the torrent, check
+[`Transmission\Model\Torrent`](https://github.com/kleiram/transmission-php/tree/master/lib/Transmission/Model/Torrent.php).
+
+By default, the library will try to connect to `localhost:9091`. If you want to
+connect to another host or post you can pass those to the constructor of the
+`Transmission` class:
+
+```php
+<?php
+use Transmission\Transmission;
+
+$transmission = new Transmission('example.com', 33);
+
+$torrents = $transmission->all();
+$torrent  = $transmission->get(1);
+$torrent  = $transmission->add(/* path to torrent */);
+
+// When you already have a torrent, you don't have to pass the client again
+$torrent->delete();
+```
+
+It is also possible to pass the torrent data directly instead of using a file
+but the metadata must be base64-encoded:
+
+```php
+<?php
+$torrent = $transmission->add(/* base64-encoded metainfo */, true);
+```
+
+If the Transmission server is secured with a username and password you can
+authenticate using the `Client` class:
+
+```php
+<?php
+use Transmission\Client;
+use Transmission\Transmission;
+
+$client = new Client();
+$client->authenticate('username', 'password');
+$transmission = new Transmission();
+$transmission->setClient($client);
+```
+
+Additionally, you can control the actual Transmission setting. This means
+you can modify the global download limit or change the download directory:
+
+```php
+<?php
+use Transmission\Transmission;
+
+$transmission = new Transmission();
+$session = $transmission->getSession();
+
+$session->setDownloadDir('/home/foo/downloads/complete');
+$session->setIncompleteDir('/home/foo/downloads/incomplete');
+$session->setIncompleteDirEnabled(true);
+$session->save();
+```
+
+## Testing
+
+Testing is done using [PHPUnit](https://github.com/sebastianbergmann/phpunit). To
+test the application, you have to install the dependencies using Composer before
+running the tests:
+
+```bash
+$ curl -s https://getcomposer.org/installer | php
+$ php composer.phar install
+$ phpunit --coverage-text
+```
+
+## Changelog
+
+    Version     Changes
+
+    0.1.0       - Initial release
+
+    0.2.0       - Rewrote the entire public API
+
+    0.3.0       - Added support for authentication
+
+    0.4.0       - The library now requires at least PHP 5.3.2
+                - Added support for getting files downloaded by torrent
+                - Added support for getting trackers used by a torrent
+                - Added support for getting peers connected to
+                - The torrent now contains:
+                    * Whether it is finished
+                    * The up- and download rate (in bytes/s)
+                    * The size of the download (when completed)
+                    * The ETA of the download
+                    * The percentage of the download completed
+                - Made the authentication more flexible
+                - The client now sends an User-Agent header with each request
+                - Added support for starting, stopping, veryfing and
+                  requesting a reannounce of torrents
+
+    0.5.0       - Fix a bug in the authentication/authorization mechanism
+                - A whole lot of other stuff including management of the
+                  Transmission session (setting global download speed limit
+                  and toggling the speed limit among others).
+
+## License
+
+This library is licensed under the BSD 2-clause license.
+
+    Copyright (c) 2013, Ramon Kleiss <ramon@cubilon.n>
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are met:
+
+    1. Redistributions of source code must retain the above copyright notice, this
+       list of conditions and the following disclaimer.
+    2. Redistributions in binary form must reproduce the above copyright notice,
+       this list of conditions and the following disclaimer in the documentation
+       and/or other materials provided with the distribution.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+    ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+    ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+    The views and conclusions contained in the software and documentation are those
+    of the authors and should not be interpreted as representing official policies,
+    either expressed or implied, of the FreeBSD Project.

+ 22 - 0
api/vendor/kleiram/transmission-php/composer.json

@@ -0,0 +1,22 @@
+{
+    "name": "kleiram/transmission-php",
+    "description": "PHP Transmission client",
+    "keywords": ["transmission", "torrent", "download"],
+    "type": "library",
+    "license": "BSD-2-Clause",
+    "authors": [
+        {
+            "name": "Ramon Kleiss",
+            "email": "ramon@cubilon.nl"
+        }
+    ],
+    "require": {
+        "kriswallsmith/buzz": ">=0.9",
+        "symfony/property-access": ">=2.2.1"
+    },
+    "autoload": {
+        "psr-0": {
+            "Transmission": "lib/"
+        }
+    }
+}

+ 28 - 0
api/vendor/kleiram/transmission-php/examples/queue.php

@@ -0,0 +1,28 @@
+<?php
+require_once __DIR__.'/../vendor/autoload.php';
+
+$transmission = new Transmission\Transmission();
+$queue = $transmission->all();
+
+echo "Downloading to: {$transmission->getSession()->getDownloadDir()}\n";
+
+foreach ($queue as $torrent) {
+    echo "{$torrent->getName()}";
+
+    if ($torrent->isFinished()) {
+        echo ": done\n";
+    } else {
+        if ($torrent->isDownloading()) {
+            echo ": {$torrent->getPercentDone()}% ";
+            echo "(eta: ". gmdate("H:i:s", $torrent->getEta()) .")\n";
+        } else{
+            echo ": paused\n";
+        }
+    }
+}
+
+// Change download directories
+// $session = $transmission->getSession();
+// $session->setDownloadDir('/var/www/downloads/complete');
+// $session->setIncompleteDir('/tmp/downloads');
+// $session->save();

+ 254 - 0
api/vendor/kleiram/transmission-php/lib/Transmission/Client.php

@@ -0,0 +1,254 @@
+<?php
+namespace Transmission;
+
+use Buzz\Message\Request;
+use Buzz\Message\Response;
+use Buzz\Client\Curl;
+use Buzz\Client\ClientInterface;
+
+/**
+ * The Client class is used to make API calls to the Transmission server
+ *
+ * @author Ramon Kleiss <ramon@cubilon.nl>
+ */
+class Client
+{
+    /**
+     * @var string
+     */
+    const DEFAULT_HOST = 'localhost';
+
+    /**
+     * @var integer
+     */
+    const DEFAULT_PORT = 9091;
+
+    /**
+     * @var string
+     */
+    const DEFAULT_PATH = '/transmission/rpc';
+
+    /**
+     * @var string
+     */
+    const TOKEN_HEADER = 'X-Transmission-Session-Id';
+
+    /**
+     * @var string
+     */
+    protected $host;
+
+    /**
+     * @var integer
+     */
+    protected $port;
+
+    /**
+     * @var string
+     */
+    protected $path;
+
+    /**
+     * @var string
+     */
+    protected $token;
+
+    /**
+     * @var Buzz\Client\ClientInterface
+     */
+    protected $client;
+
+    /**
+     * @var string
+     */
+    protected $auth;
+
+    /**
+     * Constructor
+     *
+     * @param string  $host The hostname of the Transmission server
+     * @param integer $port The port the Transmission server is listening on
+     * @param string  $path The path to Transmission server rpc api
+     */
+    public function __construct($host = null, $port = null, $path = null)
+    {
+        $this->setHost($host ?: self::DEFAULT_HOST);
+        $this->setPort($port ?: self::DEFAULT_PORT);
+        $this->setPath($path ?: self::DEFAULT_PATH);
+        $this->setToken(null);
+        $this->setClient(new Curl());
+    }
+
+    /**
+     * Authenticate against the Transmission server
+     *
+     * @param string $username
+     * @param string $password
+     */
+    public function authenticate($username, $password)
+    {
+        $this->auth = base64_encode($username .':'. $password);
+    }
+
+    /**
+     * Make an API call
+     *
+     * @param string $method
+     * @param array  $arguments
+     * @return stdClass
+     * @throws RuntimeException
+     */
+    public function call($method, array $arguments)
+    {
+        $request = new Request('POST', $this->getPath(), $this->getUrl());
+        $response = new Response();
+        $content = array('method' => $method, 'arguments' => $arguments);
+
+        $request->addHeader(sprintf('%s: %s', self::TOKEN_HEADER, $this->getToken()));
+        $request->setContent(json_encode($content));
+
+        if (is_string($this->auth)) {
+            $request->addHeader(sprintf('Authorization: Basic %s', $this->auth));
+        }
+
+        try {
+            $this->getClient()->send($request, $response);
+        } catch (\Exception $e) {
+            throw new \RuntimeException(
+                'Could not connect to Transmission',
+                0,
+                $e
+            );
+        }
+
+        if ($response->getStatusCode() != 200 &&
+            $response->getStatusCode() != 401 &&
+            $response->getStatusCode() != 409) {
+            throw new \RuntimeException('Unexpected response received from Transmission');
+        }
+
+        if ($response->getStatusCode() == 401) {
+            throw new \RuntimeException('Access to Transmission requires authentication');
+        }
+
+        if ($response->getStatusCode() == 409) {
+            $this->setToken($response->getHeader(self::TOKEN_HEADER));
+
+            return $this->call($method, $arguments);
+        }
+
+        return json_decode($response->getContent());
+    }
+
+    /**
+     * Get the URL used to connect to Transmission
+     *
+     * @return string
+     */
+    public function getUrl()
+    {
+        return sprintf(
+            'http://%s:%d',
+            $this->getHost(),
+            $this->getPort()
+        );
+    }
+
+    /**
+     * Set the hostname of the Transmission server
+     *
+     * @param string $host
+     */
+    public function setHost($host)
+    {
+        $this->host = (string) $host;
+    }
+
+    /**
+     * Get the hostname of the Transmission server
+     *
+     * @return string
+     */
+    public function getHost()
+    {
+        return $this->host;
+    }
+
+    /**
+     * Set the port the Transmission server is listening on
+     *
+     * @param integer $port
+     */
+    public function setPort($port)
+    {
+        $this->port = (integer) $port;
+    }
+
+    /**
+     * Get the port the Transmission server is listening on
+     *
+     * @return integer
+     */
+    public function getPort()
+    {
+        return $this->port;
+    }
+
+    /**
+     * Set the path to Transmission server rpc api
+     *
+     * @param string $path
+     */
+    public function setPath($path)
+    {
+        return $this->path = (string) $path;
+    }
+
+    /**
+     * Get the path to Transmission server rpc api
+     */
+    public function getPath()
+    {
+        return $this->path;
+    }
+
+    /**
+     * Set the CSRF-token of the Transmission client
+     *
+     * @param string $token
+     */
+    public function setToken($token)
+    {
+        $this->token = (string) $token;
+    }
+
+    /**
+     * Get the CSRF-token for the Transmission client
+     *
+     * @return string
+     */
+    public function getToken()
+    {
+        return $this->token;
+    }
+
+    /**
+     * Set the Buzz client used to connect to Transmission
+     *
+     * @param Buzz\Client\ClientInterface $client
+     */
+    public function setClient(ClientInterface $client)
+    {
+        $this->client = $client;
+    }
+
+    /**
+     * Get the Buzz client used to connect to Transmission
+     *
+     * @return Buzz\Client\ClientInterface
+     */
+    public function getClient()
+    {
+        return $this->client;
+    }
+}

+ 46 - 0
api/vendor/kleiram/transmission-php/lib/Transmission/Model/AbstractModel.php

@@ -0,0 +1,46 @@
+<?php
+namespace Transmission\Model;
+
+use Transmission\Client;
+
+/**
+ * Base class for Transmission models
+ *
+ * @author Ramon Kleiss <ramon@cubilon.nl>
+ */
+abstract class AbstractModel implements ModelInterface
+{
+    /**
+     * Constructor
+     *
+     * @param Transmission\Client $client
+     */
+    public function __construct(Client $client = null)
+    {
+        $this->client = $client;
+    }
+
+    /**
+     * @param Transmission\Client $client
+     */
+    public function setClient(Client $client)
+    {
+        $this->client = $client;
+    }
+
+    /**
+     * @return Transmission\Client
+     */
+    public function getClient()
+    {
+        return $this->client;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public static function getMapping()
+    {
+        return array();
+    }
+}

+ 91 - 0
api/vendor/kleiram/transmission-php/lib/Transmission/Model/File.php

@@ -0,0 +1,91 @@
+<?php
+namespace Transmission\Model;
+
+/**
+ * @author Ramon Kleiss <ramon@cubilon.nl>
+ */
+class File extends AbstractModel
+{
+    /**
+     * @var string
+     */
+    protected $name;
+
+    /**
+     * @var integer
+     */
+    protected $size;
+
+    /**
+     * @var integer
+     */
+    protected $completed;
+
+    /**
+     * @param string $name
+     */
+    public function setName($name)
+    {
+        $this->name = (string) $name;
+    }
+
+    /**
+     * @return string
+     */
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    /**
+     * @param integer $size
+     */
+    public function setSize($size)
+    {
+        $this->size = (integer) $size;
+    }
+
+    /**
+     * @return integer
+     */
+    public function getSize()
+    {
+        return $this->size;
+    }
+
+    /**
+     * @param integer $size
+     */
+    public function setCompleted($completed)
+    {
+        $this->completed = (integer) $completed;
+    }
+
+    /**
+     * @return integer
+     */
+    public function getCompleted()
+    {
+        return $this->completed;
+    }
+
+    /**
+     * @return boolean
+     */
+    public function isDone()
+    {
+        return $this->getSize() == $this->getCompleted();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public static function getMapping()
+    {
+        return array(
+            'name' => 'name',
+            'length' => 'size',
+            'bytesCompleted' => 'completed'
+        );
+    }
+}

+ 17 - 0
api/vendor/kleiram/transmission-php/lib/Transmission/Model/ModelInterface.php

@@ -0,0 +1,17 @@
+<?php
+namespace Transmission\Model;
+
+/**
+ * The interface Transmission models must implement
+ *
+ * @author Ramon Kleiss <ramon@cubilon.nl>
+ */
+interface ModelInterface
+{
+    /**
+     * Get the mapping of the model
+     *
+     * @return array
+     */
+    public static function getMapping();
+}

+ 347 - 0
api/vendor/kleiram/transmission-php/lib/Transmission/Model/Peer.php

@@ -0,0 +1,347 @@
+<?php
+namespace Transmission\Model;
+
+/**
+ * @author Ramon Kleiss <ramon@cubilon.nl>
+ */
+class Peer extends AbstractModel
+{
+    /**
+     * @var string
+     */
+    protected $address;
+
+    /**
+     * @var integer
+     */
+    protected $port;
+
+    /**
+     * @var string
+     */
+    protected $clientName;
+
+    /**
+     * @var boolean
+     */
+    protected $clientChoked;
+
+    /**
+     * @var boolean
+     */
+    protected $clientInterested;
+
+    /**
+     * @var boolean
+     */
+    protected $downloading;
+
+    /**
+     * @var boolean
+     */
+    protected $encrypted;
+
+    /**
+     * @var boolean
+     */
+    protected $incoming;
+
+    /**
+     * @var boolean
+     */
+    protected $uploading;
+
+    /**
+     * @var boolean
+     */
+    protected $utp;
+
+    /**
+     * @var boolean
+     */
+    protected $peerChoked;
+
+    /**
+     * @var boolean
+     */
+    protected $peerInterested;
+
+    /**
+     * @var double
+     */
+    protected $progress;
+
+    /**
+     * @var integer
+     */
+    protected $uploadRate;
+
+    /**
+     * @var integer
+     */
+    protected $downloadRate;
+
+    /**
+     * @param string $address
+     */
+    public function setAddress($address)
+    {
+        $this->address = (string) $address;
+    }
+
+    /**
+     * @return string
+     */
+    public function getAddress()
+    {
+        return $this->address;
+    }
+
+    /**
+     * @param integer $port
+     */
+    public function setPort($port)
+    {
+        $this->port = (integer) $port;
+    }
+
+    /**
+     * @return integer
+     */
+    public function getPort()
+    {
+        return $this->port;
+    }
+
+    /**
+     * @param string $clientName
+     */
+    public function setClientName($clientName)
+    {
+        $this->clientName = (string) $clientName;
+    }
+
+    /**
+     * @return string
+     */
+    public function getClientName()
+    {
+        return $this->clientName;
+    }
+
+    /**
+     * @param boolean $choked
+     */
+    public function setClientChoked($choked)
+    {
+        $this->clientChoked = (boolean) $choked;
+    }
+
+    /**
+     * @return boolean
+     */
+    public function isClientChoked()
+    {
+        return $this->clientChoked;
+    }
+
+    /**
+     * @param boolean $interested
+     */
+    public function setClientInterested($interested)
+    {
+        $this->clientInterested = (boolean) $interested;
+    }
+
+    /**
+     * @return boolean
+     */
+    public function isClientInterested()
+    {
+        return $this->clientInterested;
+    }
+
+    /**
+     * @param boolean $downloading
+     */
+    public function setDownloading($downloading)
+    {
+        $this->downloading = (boolean) $downloading;
+    }
+
+    /**
+     * @return boolean
+     */
+    public function isDownloading()
+    {
+        return $this->downloading;
+    }
+
+    /**
+     * @param boolean $encrypted
+     */
+    public function setEncrypted($encrypted)
+    {
+        $this->encrypted = (boolean) $encrypted;
+    }
+
+    /**
+     * @return boolean
+     */
+    public function isEncrypted()
+    {
+        return $this->encrypted;
+    }
+
+    /**
+     * @param boolean $incoming
+     */
+    public function setIncoming($incoming)
+    {
+        $this->incoming = (boolean) $incoming;
+    }
+
+    /**
+     * @return boolean
+     */
+    public function isIncoming()
+    {
+        return $this->incoming;
+    }
+
+    /**
+     * @param boolean $uploading
+     */
+    public function setUploading($uploading)
+    {
+        $this->uploading = (boolean) $uploading;
+    }
+
+    /**
+     * @return boolean
+     */
+    public function isUploading()
+    {
+        return $this->uploading;
+    }
+
+    /**
+     * @param boolean $utp
+     */
+    public function setUtp($utp)
+    {
+        $this->utp = (boolean) $utp;
+    }
+
+    /**
+     * @return boolean
+     */
+    public function isUtp()
+    {
+        return $this->utp;
+    }
+
+    /**
+     * @param boolean $choked
+     */
+    public function setPeerChoked($choked)
+    {
+        $this->peerChoked = (boolean) $choked;
+    }
+
+    /**
+     * @return boolean
+     */
+    public function isPeerChoked()
+    {
+        return $this->peerChoked;
+    }
+
+    /**
+     * @param boolean $interested
+     */
+    public function setPeerInterested($interested)
+    {
+        $this->peerInterested = (boolean) $interested;
+    }
+
+    /**
+     * @return boolean
+     */
+    public function isPeerInterested()
+    {
+        return $this->peerInterested;
+    }
+
+    /**
+     * @param double $progress
+     */
+    public function setProgress($progress)
+    {
+        $this->progress = (double) $progress;
+    }
+
+    /**
+     * @return double
+     */
+    public function getProgress()
+    {
+        return $this->progress;
+    }
+
+    /**
+     * @param integer $rate
+     */
+    public function setUploadRate($rate)
+    {
+        $this->uploadRate = (integer) $rate;
+    }
+
+    /**
+     * @return integer
+     */
+    public function getUploadRate()
+    {
+        return $this->uploadRate;
+    }
+
+    /**
+     * @param integer $rate
+     */
+    public function setDownloadRate($rate)
+    {
+        $this->downloadRate = (integer) $rate;
+    }
+
+    /**
+     * @return integer
+     */
+    public function getDownloadRate()
+    {
+        return $this->downloadRate;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public static function getMapping()
+    {
+        return array(
+            'address' => 'address',
+            'port' => 'port',
+            'clientName' => 'clientName',
+            'clientIsChoked' => 'clientChoked',
+            'clientIsInterested' => 'clientInterested',
+            'isDownloadingFrom' => 'downloading',
+            'isEncrypted' => 'encrypted',
+            'isIncoming' => 'incoming',
+            'isUploadingTo' => 'uploading',
+            'isUTP' => 'utp',
+            'peerIsChoked' => 'peerChoked',
+            'peerIsInterested' => 'peerInterested',
+            'progress' => 'progress',
+            'rateToClient' => 'uploadRate',
+            'rateFromClient' => 'downloadRate'
+        );
+    }
+}

+ 369 - 0
api/vendor/kleiram/transmission-php/lib/Transmission/Model/Session.php

@@ -0,0 +1,369 @@
+<?php
+namespace Transmission\Model;
+
+use Transmission\Util\ResponseValidator;
+
+/**
+ * @author Joysen Chellem
+ * @author Ramon Kleiss <ramon@cubilon.nl>
+ */
+class Session extends AbstractModel
+{
+    /**
+     * @var integer
+     */
+    protected $altSpeedDown;
+
+    /**
+     * @var boolean
+     */
+    protected $altSpeedEnabled;
+
+    /**
+     * @var string
+     */
+    protected $downloadDir;
+
+    /**
+     * @var boolean
+     */
+    protected $downloadQueueEnabled;
+
+    /**
+     * @var integer
+     */
+    protected $downloadQueueSize;
+
+    /**
+     * @var string
+     */
+    protected $incompleteDir;
+
+    /**
+     * @var boolean
+     */
+    protected $incompleteDirEnabled;
+
+    /**
+     * @var string
+     */
+    protected $torrentDoneScript;
+
+    /**
+     * @var boolean
+     */
+    protected $torrentDoneScriptEnabled;
+
+    /**
+     * @var double
+     */
+    protected $seedRatioLimit;
+
+    /**
+     * @var boolean
+     */
+    protected $seedRatioLimited;
+
+    /**
+     * @var integer
+     */
+    protected $seedQueueSize;
+
+    /**
+     * @var boolean
+     */
+    protected $seedQueueEnabled;
+
+    /**
+     * @var integer
+     */
+    protected $downloadSpeedLimit;
+
+    /**
+     * @var boolean
+     */
+    protected $downloadSpeedLimitEnabled;
+
+    /**
+     * @param integer $speed
+     */
+    public function setAltSpeedDown($speed)
+    {
+        $this->altSpeedDown = (integer) $speed;
+    }
+
+    /**
+     * @return integer
+     */
+    public function getAltSpeedDown()
+    {
+        return $this->altSpeedDown;
+    }
+
+    /**
+     * @param boolean $enabled
+     */
+    public function setAltSpeedEnabled($enabled)
+    {
+        $this->altSpeedEnabled = (boolean) $enabled;
+    }
+
+    /**
+     * @return boolean
+     */
+    public function isAltSpeedEnabled()
+    {
+        return $this->altSpeedEnabled;
+    }
+
+    /**
+     * @param string $downloadDir
+     */
+    public function setDownloadDir($downloadDir)
+    {
+        $this->downloadDir = (string) $downloadDir;
+    }
+
+    /**
+     * @return string
+     */
+    public function getDownloadDir()
+    {
+        return $this->downloadDir;
+    }
+
+    /**
+     * @param boolean $enabled
+     */
+    public function setDownloadQueueEnabled($enabled)
+    {
+        $this->downloadQueueEnabled = (boolean) $enabled;
+    }
+
+    /**
+     * @return boolean
+     */
+    public function isDownloadQueueEnabled()
+    {
+        return $this->downloadQueueEnabled;
+    }
+
+    /**
+     * @param integer $size
+     */
+    public function setDownloadQueueSize($size)
+    {
+        $this->downloadQueueSize = (integer) $size;
+    }
+
+    /**
+     * @return integer
+     */
+    public function getDownloadQueueSize()
+    {
+        return $this->downloadQueueSize;
+    }
+
+    /**
+     * @param string $directory
+     */
+    public function setIncompleteDir($directory)
+    {
+        $this->incompleteDir = (string) $directory;
+    }
+
+    /**
+     * @return string
+     */
+    public function getIncompleteDir()
+    {
+        return $this->incompleteDir;
+    }
+
+    /**
+     * @param boolean $enabled
+     */
+    public function setIncompleteDirEnabled($enabled)
+    {
+        $this->incompleteDirEnabled = (boolean) $enabled;
+    }
+
+    /**
+     * @return boolean
+     */
+    public function isIncompleteDirEnabled()
+    {
+        return $this->incompleteDirEnabled;
+    }
+
+    /**
+     * @param string $filename
+     */
+    public function setTorrentDoneScript($filename)
+    {
+        $this->torrentDoneScript = (string) $filename;
+    }
+
+    /**
+     * @return string
+     */
+    public function getTorrentDoneScript()
+    {
+        return $this->torrentDoneScript;
+    }
+
+    /**
+     * @param boolean $enabled
+     */
+    public function setTorrentDoneScriptEnabled($enabled)
+    {
+        $this->torrentDoneScriptEnabled = (boolean) $enabled;
+    }
+
+    /**
+     * @return boolean
+     */
+    public function isTorrentDoneScriptEnabled()
+    {
+        return $this->torrentDoneScriptEnabled;
+    }
+
+    /**
+     * @param double $limit
+     */
+    public function setSeedRatioLimit($limit)
+    {
+        $this->seedRatioLimit = (double) $limit;
+    }
+
+    /**
+     * @return double
+     */
+    public function getSeedRatioLimit()
+    {
+        return $this->seedRatioLimit;
+    }
+
+    /**
+     * @param boolean $limited
+     */
+    public function setSeedRatioLimited($limited)
+    {
+        $this->seedRatioLimited = (boolean) $limited;
+    }
+
+    /**
+     * @return boolean
+     */
+    public function isSeedRatioLimited()
+    {
+        return $this->seedRatioLimited;
+    }
+
+    /**
+     * @param integer $size
+     */
+    public function setSeedQueueSize($size)
+    {
+        $this->seedQueueSize = (integer) $size;
+    }
+
+    /**
+     * @return integer
+     */
+    public function getSeedQueueSize()
+    {
+        return $this->seedQueueSize;
+    }
+
+    /**
+     * @param boolean $enabled
+     */
+    public function setSeedQueueEnabled($enabled)
+    {
+        $this->seedQueueEnabled = (boolean) $enabled;
+    }
+
+    /**
+     * @return boolean
+     */
+    public function isSeedQueueEnabled()
+    {
+        return $this->seedQueueEnabled;
+    }
+
+    /**
+     * @param integer $limit
+     */
+    public function setDownloadSpeedLimit($limit)
+    {
+        $this->downloadSpeedLimit = (integer) $limit;
+    }
+
+    /**
+     * @return integer
+     */
+    public function getDownloadSpeedLimit()
+    {
+        return $this->downloadSpeedLimit;
+    }
+
+    /**
+     * @param boolean $enabled
+     */
+    public function setDownloadSpeedLimitEnabled($enabled)
+    {
+        $this->downloadSpeedLimitEnabled = (boolean) $enabled;
+    }
+
+    /**
+     * @return boolean
+     */
+    public function isDownloadSpeedLimitEnabled()
+    {
+        return $this->downloadSpeedLimitEnabled;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public static function getMapping()
+    {
+        return array(
+            'alt-speed-down' => 'altSpeedDown',
+            'alt-speed-enabled' => 'altSpeedEnabled',
+            'download-dir' => 'downloadDir',
+            'download-queue-enabled' => 'downloadQueueEnabled',
+            'download-queue-size' => 'downloadQueueSize',
+            'incomplete-dir' => 'incompleteDir',
+            'incomplete-dir-enabled' => 'incompleteDirEnabled',
+            'script-torrent-done-filename' => 'torrentDoneScript',
+            'script-torrent-done-enabled' => 'torrentDoneScriptEnabled',
+            'seedRatioLimit' => 'seedRatioLimit',
+            'seedRatioLimited' => 'seedRatioLimited',
+            'seed-queue-size' => 'seedQueueSize',
+            'seed-queue-enabled' => 'seedQueueEnabled',
+            'speed-limit-down' => 'downloadSpeedLimit',
+            'speed-limit-down-enabled' => 'downloadSpeedLimitEnabled',
+        );
+    }
+
+    public function save()
+    {
+        $arguments = array();
+        $method    = 'session-set';
+
+        foreach ($this->getMapping() as $key => $value) {
+            $arguments[$key] = $this->$value;
+        }
+
+        if (!($client = $this->getClient())) {
+            return;
+        }
+
+        ResponseValidator::validate(
+            $method,
+            $client->call($method, $arguments)
+        );
+    }
+}

+ 459 - 0
api/vendor/kleiram/transmission-php/lib/Transmission/Model/Torrent.php

@@ -0,0 +1,459 @@
+<?php
+namespace Transmission\Model;
+
+use Transmission\Util\PropertyMapper;
+use Transmission\Util\ResponseValidator;
+
+/**
+ * @author Ramon Kleiss <ramon@cubilon.nl>
+ */
+class Torrent extends AbstractModel
+{
+    /**
+     * @var integer
+     */
+    const STATUS_STOPPED = 0;
+
+    /**
+     * @var integer
+     */
+    const STATUS_CHECK_WAIT = 1;
+
+    /**
+     * @var integer
+     */
+    const STATUS_CHECK = 2;
+
+    /**
+     * @var integer
+     */
+    const STATUS_DOWNLOAD_WAIT = 3;
+
+    /**
+     * @var integer
+     */
+    const STATUS_DOWNLOAD = 4;
+
+    /**
+     * @var integer
+     */
+    const STATUS_SEED_WAIT = 5;
+
+    /**
+     * @var integer
+     */
+    const STATUS_SEED = 6;
+
+    /**
+     * @var integer
+     */
+    protected $id;
+
+    /**
+     * @var integer
+     */
+    protected $eta;
+
+    /**
+     * @var integer
+     */
+    protected $size;
+
+    /**
+     * @var string
+     */
+    protected $name;
+
+    /**
+     * @var string
+     */
+    protected $hash;
+
+    /**
+     * @var integer
+     */
+    protected $status;
+
+    /**
+     * @var boolean
+     */
+    protected $finished;
+
+    /**
+     * @var integer
+     */
+    protected $uploadRate;
+
+    /**
+     * @var integer
+     */
+    protected $downloadRate;
+
+    /**
+     * @var double
+     */
+    protected $percentDone;
+
+    /**
+     * @var array
+     */
+    protected $files = array();
+
+    /**
+     * @var array
+     */
+    protected $peers = array();
+
+    /**
+     * @var array
+     */
+    protected $trackers = array();
+
+    /**
+     * @param integer $id
+     */
+    public function setId($id)
+    {
+        $this->id = (integer) $id;
+    }
+
+    /**
+     * @return integer
+     */
+    public function getId()
+    {
+        return $this->id;
+    }
+
+    /**
+     * @param integer $eta
+     */
+    public function setEta($eta)
+    {
+        $this->eta = (integer) $eta;
+    }
+
+    /**
+     * @return integer
+     */
+    public function getEta()
+    {
+        return $this->eta;
+    }
+
+    /**
+     * @param integer $size
+     */
+    public function setSize($size)
+    {
+        $this->size = (integer) $size;
+    }
+
+    /**
+     * @return integer
+     */
+    public function getSize()
+    {
+        return $this->size;
+    }
+
+    /**
+     * @param string $name
+     */
+    public function setName($name)
+    {
+        $this->name = (string) $name;
+    }
+
+    /**
+     * @return string
+     */
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    /**
+     * @param string $hash
+     */
+    public function setHash($hash)
+    {
+        $this->hash = (string) $hash;
+    }
+
+    /**
+     * @return string
+     */
+    public function getHash()
+    {
+        return $this->hash;
+    }
+
+    /**
+     * @param integer $status
+     */
+    public function setStatus($status)
+    {
+        $this->status = (integer) $status;
+    }
+
+    /**
+     * @return integer
+     */
+    public function getStatus()
+    {
+        return $this->status;
+    }
+
+    /**
+     * @param boolean $finished
+     */
+    public function setFinished($finished)
+    {
+        $this->finished = (boolean) $finished;
+    }
+
+    /**
+     * @return boolean
+     */
+    public function isFinished()
+    {
+        return ($this->finished || (int) $this->getPercentDone() == 100);
+    }
+
+    /**
+     * @var integer $rate
+     */
+    public function setUploadRate($rate)
+    {
+        $this->uploadRate = (integer) $rate;
+    }
+
+    /**
+     * @return integer
+     */
+    public function getUploadRate()
+    {
+        return $this->uploadRate;
+    }
+
+    /**
+     * @param integer $rate
+     */
+    public function setDownloadRate($rate)
+    {
+        $this->downloadRate = (integer) $rate;
+    }
+
+    /**
+     * @return integer
+     */
+    public function getDownloadRate()
+    {
+        return $this->downloadRate;
+    }
+
+    /**
+     * @param double $done
+     */
+    public function setPercentDone($done)
+    {
+        $this->percentDone = (double) $done;
+    }
+
+    /**
+     * @return double
+     */
+    public function getPercentDone()
+    {
+        return $this->percentDone * 100.0;
+    }
+
+    /**
+     * @param array $files
+     */
+    public function setFiles(array $files)
+    {
+        $this->files = array();
+
+        foreach ($files as $file) {
+            $this->files[] = PropertyMapper::map(new File(), $file);
+        }
+    }
+
+    /**
+     * @return array
+     */
+    public function getFiles()
+    {
+        return $this->files;
+    }
+
+    /**
+     * @param array $peers
+     */
+    public function setPeers(array $peers)
+    {
+        $this->peers = array();
+
+        foreach ($peers as $peer) {
+            $this->peers[] = PropertyMapper::map(new Peer(), $peer);
+        }
+    }
+
+    /**
+     * @return array
+     */
+    public function getPeers()
+    {
+        return $this->peers;
+    }
+
+    /**
+     * @param array $trackers
+     */
+    public function setTrackers(array $trackers)
+    {
+        $this->trackers = array();
+
+        foreach ($trackers as $tracker) {
+            $this->trackers[] = PropertyMapper::map(new Tracker(), $tracker);
+        }
+    }
+
+    /**
+     * @return array
+     */
+    public function getTrackers()
+    {
+        return $this->trackers;
+    }
+
+    /**
+     * @return boolean
+     */
+    public function isStopped()
+    {
+        return $this->getStatus() == self::STATUS_STOPPED;
+    }
+
+    /**
+     * @return boolean
+     */
+    public function isChecking()
+    {
+        return ($this->getStatus() == self::STATUS_CHECK ||
+                $this->getStatus() == self::STATUS_CHECK_WAIT);
+    }
+
+    /**
+     * @return boolean
+     */
+    public function isDownloading()
+    {
+        return ($this->getStatus() == self::STATUS_DOWNLOAD ||
+                $this->getStatus() == self::STATUS_DOWNLOAD_WAIT);
+    }
+
+    /**
+     * @return boolean
+     */
+    public function isSeeding()
+    {
+        return ($this->getStatus() == self::STATUS_SEED ||
+                $this->getStatus() == self::STATUS_SEED_WAIT);
+    }
+
+    /**
+     */
+    public function stop()
+    {
+        $this->call(
+            'torrent-stop',
+            array('ids' => array($this->getId()))
+        );
+    }
+
+    /**
+     * @param boolean $now
+     */
+    public function start($now = false)
+    {
+        $this->call(
+            $now ? 'torrent-start-now' : 'torrent-start',
+            array('ids' => array($this->getId()))
+        );
+    }
+
+    /**
+     */
+    public function verify()
+    {
+        $this->call(
+            'torrent-verify',
+            array('ids' => array($this->getId()))
+        );
+    }
+
+    /**
+     */
+    public function reannounce()
+    {
+        $this->call(
+            'torrent-reannounce',
+            array('ids' => array($this->getId()))
+        );
+    }
+
+    /**
+     * @param boolean $localData
+     */
+    public function remove($localData = false)
+    {
+        $arguments = array('ids' => array($this->getId()));
+
+        if ($localData) {
+            $arguments['delete-local-data'] = true;
+        }
+
+        $this->call('torrent-remove', $arguments);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public static function getMapping()
+    {
+        return array(
+            'id' => 'id',
+            'eta' => 'eta',
+            'sizeWhenDone' => 'size',
+            'name' => 'name',
+            'status' => 'status',
+            'isFinished' => 'finished',
+            'rateUpload' => 'uploadRate',
+            'rateDownload' => 'downloadRate',
+            'percentDone' => 'percentDone',
+            'files' => 'files',
+            'peers' => 'peers',
+            'trackers' => 'trackers',
+            'hashString' => 'hash'
+        );
+    }
+
+    /**
+     * @param string $method
+     * @param array  $arguments
+     */
+    protected function call($method, $arguments)
+    {
+        if (!($client = $this->getClient())) {
+            return;
+        }
+
+        ResponseValidator::validate(
+            $method,
+            $client->call($method, $arguments)
+        );
+    }
+}

+ 105 - 0
api/vendor/kleiram/transmission-php/lib/Transmission/Model/Tracker.php

@@ -0,0 +1,105 @@
+<?php
+namespace Transmission\Model;
+
+/**
+ * @author Ramon Kleiss <ramon@cubilon.nl>
+ */
+class Tracker extends AbstractModel
+{
+    /**
+     * @var integer
+     */
+    protected $id;
+
+    /**
+     * @var integer
+     */
+    protected $tier;
+
+    /**
+     * @var string
+     */
+    protected $scrape;
+
+    /**
+     * @var string
+     */
+    protected $announce;
+
+    /**
+     * @param integer $id
+     */
+    public function setId($id)
+    {
+        $this->id = (integer) $id;
+    }
+
+    /**
+     * @return integer
+     */
+    public function getId()
+    {
+        return $this->id;
+    }
+
+    /**
+     * @param integer $tier
+     */
+    public function setTier($tier)
+    {
+        $this->tier = (integer) $tier;
+    }
+
+    /**
+     * @return integer
+     */
+    public function getTier()
+    {
+        return $this->tier;
+    }
+
+    /**
+     * @param string $scrape
+     */
+    public function setScrape($scrape)
+    {
+        $this->scrape = (string) $scrape;
+    }
+
+    /**
+     * @return string
+     */
+    public function getScrape()
+    {
+        return $this->scrape;
+    }
+
+    /**
+     * @param string $announce
+     */
+    public function setAnnounce($announce)
+    {
+        $this->announce = (string) $announce;
+    }
+
+    /**
+     * @return string
+     */
+    public function getAnnounce()
+    {
+        return $this->announce;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public static function getMapping()
+    {
+        return array(
+            'id' => 'id',
+            'tier' => 'tier',
+            'scrape' => 'scrape',
+            'announce' => 'announce'
+        );
+    }
+}

+ 238 - 0
api/vendor/kleiram/transmission-php/lib/Transmission/Transmission.php

@@ -0,0 +1,238 @@
+<?php
+namespace Transmission;
+
+use Transmission\Model\Torrent;
+use Transmission\Model\Session;
+use Transmission\Util\PropertyMapper;
+use Transmission\Util\ResponseValidator;
+
+/**
+ * @author Ramon Kleiss <ramon@cubilon.nl>
+ */
+class Transmission
+{
+    /**
+     * @var Transmission\Client
+     */
+    protected $client;
+
+    /**
+     * @var Transmission\Util\ResponseValidator
+     */
+    protected $validator;
+
+    /**
+     * @var Transmission\Util\PropertyMapper
+     */
+    protected $mapper;
+
+    /**
+     * Constructor
+     *
+     * @param string  $host
+     * @param integer $port
+     * @param string  $path
+     */
+    public function __construct($host = null, $port = null, $path = null)
+    {
+        $this->setClient(new Client($host, $port, $path));
+        $this->setMapper(new PropertyMapper());
+        $this->setValidator(new ResponseValidator());
+    }
+
+    /**
+     * Get all the torrents in the download queue
+     *
+     * @return array
+     */
+    public function all()
+    {
+        $response = $this->getClient()->call(
+            'torrent-get',
+            array('fields' => array_keys(Torrent::getMapping()))
+        );
+
+        $torrents = array();
+
+        foreach ($this->getValidator()->validate('torrent-get', $response) as $t) {
+            $torrents[] = $this->getMapper()->map(
+                new Torrent($this->getClient()),
+                $t
+            );
+        }
+
+        return $torrents;
+    }
+
+    /**
+     * Get a specific torrent from the download queue
+     *
+     * @param integer $id
+     * @return Transmission\Model\Torrent
+     * @throws RuntimeException
+     */
+    public function get($id)
+    {
+        $response = $this->getClient()->call(
+            'torrent-get',
+            array(
+                'fields' => array_keys(Torrent::getMapping()),
+                'ids'    => array($id)
+            )
+        );
+
+        $torrent = null;
+
+        foreach ($this->getValidator()->validate('torrent-get', $response) as $t) {
+            $torrent = $this->getMapper()->map(
+                new Torrent($this->getClient()),
+                $t
+            );
+        }
+
+        if (!$torrent instanceof Torrent) {
+            throw new \RuntimeException(
+                sprintf("Torrent with ID %s not found", $id)
+            );
+        }
+
+        return $torrent;
+    }
+
+    /**
+     * Get the Transmission session
+     * 
+     * @return Transmission\Model\Session
+     */
+    public function getSession(){
+        $response = $this->getClient()->call(
+            'session-get',
+            array()
+        );
+
+        return $this->getMapper()->map(
+            new Session($this->getClient()),
+            $this->getValidator()->validate('session-get', $response)
+        );
+    }
+
+    /**
+     * Add a torrent to the download queue
+     *
+     * @param string  $filename
+     * @param boolean $metainfo
+     * @return Transmission\Model\Torrent
+     */
+    public function add($torrent, $metainfo = false)
+    {
+        $response = $this->getClient()->call(
+            'torrent-add',
+            array($metainfo ? 'metainfo' : 'filename' => $torrent)
+        );
+
+        return $this->getMapper()->map(
+            new Torrent($this->getClient()),
+            $this->getValidator()->validate('torrent-add', $response)
+        );
+    }
+
+    /**
+     * Set the client used to connect to Transmission
+     *
+     * @param Transmission\Client $client
+     */
+    public function setClient(Client $client)
+    {
+        $this->client = $client;
+    }
+
+    /**
+     * Get the client used to connect to Transmission
+     *
+     * @return Transmission\Client
+     */
+    public function getClient()
+    {
+        return $this->client;
+    }
+
+    /**
+     * Set the hostname of the Transmission server
+     *
+     * @param string $host
+     */
+    public function setHost($host)
+    {
+        return $this->getClient()->setHost($host);
+    }
+
+    /**
+     * Get the hostname of the Transmission server
+     *
+     * @return string
+     */
+    public function getHost()
+    {
+        return $this->getClient()->getHost();
+    }
+
+    /**
+     * Set the port the Transmission server is listening on
+     *
+     * @param integer $port
+     */
+    public function setPort($port)
+    {
+        return $this->getClient()->setPort($port);
+    }
+
+    /**
+     * Get the port the Transmission server is listening on
+     *
+     * @return integer
+     */
+    public function getPort()
+    {
+        return $this->getClient()->getPort();
+    }
+
+    /**
+     * Set the mapper used to map responses from Transmission to models
+     *
+     * @param Transmission\Util\PropertyMapper $mapper
+     */
+    public function setMapper(PropertyMapper $mapper)
+    {
+        $this->mapper = $mapper;
+    }
+
+    /**
+     * Get the mapper used to map responses from Transmission to models
+     *
+     * @return Transmission\Util\PropertyMapper
+     */
+    public function getMapper()
+    {
+        return $this->mapper;
+    }
+
+    /**
+     * Set the validator used to validate Transmission responses
+     *
+     * @param Transmission\Util\ResponseValidator $validator
+     */
+    public function setValidator(ResponseValidator $validator)
+    {
+        $this->validator = $validator;
+    }
+
+    /**
+     * Get the validator used to validate Transmission responses
+     *
+     * @return Transmission\Util\ResponseValidator
+     */
+    public function getValidator()
+    {
+        return $this->validator;
+    }
+}

+ 41 - 0
api/vendor/kleiram/transmission-php/lib/Transmission/Util/PropertyMapper.php

@@ -0,0 +1,41 @@
+<?php
+namespace Transmission\Util;
+
+use Transmission\Model\ModelInterface;
+use Symfony\Component\PropertyAccess\PropertyAccess;
+
+/**
+ * The PropertyMapper is used to map responses from Transmission to models
+ *
+ * @author Ramon Kleiss <ramon@cubilon.nl>
+ */
+class PropertyMapper
+{
+    /**
+     * @param Transmission\Model\ModelInterface $model
+     * @param stdClass                          $dto
+     * @return Transmission\Model\ModelInterface
+     */
+    public static function map(ModelInterface $model, $dto)
+    {
+        $accessor = PropertyAccess::getPropertyAccessor();
+
+        $mapping  = array_filter($model->getMapping(), function ($value) {
+            return !is_null($value);
+        });
+
+        foreach ($mapping as $source => $dest) {
+            try {
+                $accessor->setValue(
+                    $model,
+                    $dest,
+                    $accessor->getValue($dto, $source)
+                );
+            } catch (\Exception $e) {
+                continue;
+            }
+        }
+
+        return $model;
+    }
+}

+ 81 - 0
api/vendor/kleiram/transmission-php/lib/Transmission/Util/ResponseValidator.php

@@ -0,0 +1,81 @@
+<?php
+namespace Transmission\Util;
+
+/**
+ * @author Ramon Kleiss <ramon@cubilon.nl>
+ */
+class ResponseValidator
+{
+    /**
+     * @param string   $method
+     * @param stdClass $response
+     * @throws RuntimeException
+     */
+    public static function validate($method, \stdClass $response)
+    {
+        if (!isset($response->result)) {
+            throw new \RuntimeException('Invalid response received from Transmission');
+        }
+
+        if ($response->result !== 'success' &&
+            $response->result !== 'duplicate torrent') {
+            throw new \RuntimeException(
+                sprintf('An error occured: "%s"', $response->result)
+            );
+        }
+        switch ($method) {
+            case 'torrent-get':
+                return self::validateGetResponse($response);
+            case 'torrent-add':
+                return self::validateAddResponse($response);
+            case 'session-get':
+            	return self::validateSessionGetResponse($response);
+        }
+    }
+
+    /**
+     * @param stdClass $response
+     * @throws RuntimeException
+     */
+    public static function validateGetResponse(\stdClass $response)
+    {
+        if (!isset($response->arguments) ||
+            !isset($response->arguments->torrents)) {
+            throw new \RuntimeException(
+                'Invalid response received from Transmission'
+            );
+        }
+
+        return $response->arguments->torrents;
+    }
+
+    /**
+     * @param stdClass $response
+     * @throws RuntimeException
+     */
+    public static function validateAddResponse(\stdClass $response)
+    {
+        $fields = array('torrent-added', 'torrent-duplicate');
+
+        foreach ($fields as $field) {
+            if (isset($response->arguments) &&
+                isset($response->arguments->$field) &&
+                count($response->arguments->$field)) {
+                return $response->arguments->$field;
+            }
+        }
+
+        throw new \RuntimeException('Invalid response received from Transmission');
+    }
+
+    public static function validateSessionGetResponse(\stdClass $response)
+    {
+        if (!isset($response->arguments)) {
+            throw new \RuntimeException(
+                'Invalid response received from Transmission'
+            );
+        }
+
+    	return $response->arguments;
+    }
+}

+ 13 - 0
api/vendor/kleiram/transmission-php/phpunit.xml.dist

@@ -0,0 +1,13 @@
+<phpunit bootstrap="./tests/bootstrap.php" colors="true">
+    <testsuites>
+        <testsuite name="Transmission Test Suite">
+            <directory suffix="Test.php">./tests/Transmission/</directory>
+        </testsuite>
+    </testsuites>
+
+    <filter>
+        <whitelist>
+            <directory suffix=".php">./lib/Transmission/</directory>
+        </whitelist>
+    </filter>
+</phpunit>

+ 50 - 0
api/vendor/kleiram/transmission-php/tests/Transmission/Mock/Model.php

@@ -0,0 +1,50 @@
+<?php
+namespace Transmission\Mock;
+
+use Transmission\Model\ModelInterface;
+
+class Model implements ModelInterface
+{
+    private $fo;
+    private $bar;
+    private $unused;
+
+    public function setFo($fo)
+    {
+        $this->fo = $fo;
+    }
+
+    public function getFo()
+    {
+        return $this->fo;
+    }
+
+    public function setBar($bar)
+    {
+        $this->bar = $bar;
+    }
+
+    public function getBar()
+    {
+        return $this->bar;
+    }
+
+    public function setUnused($unused)
+    {
+        $this->unused = $unused;
+    }
+
+    public function getUnused()
+    {
+        return $this->unused;
+    }
+
+    public static function getMapping()
+    {
+        return array(
+            'foo' => 'fo',
+            'bar' => 'bar',
+            'unused' => null
+        );
+    }
+}

+ 212 - 0
api/vendor/kleiram/transmission-php/tests/Transmission/Tests/ClientTest.php

@@ -0,0 +1,212 @@
+<?php
+namespace Transmission\Tests;
+
+use Transmission\Client;
+
+class ClientTest extends \PHPUnit_Framework_TestCase
+{
+    protected $client;
+
+    /**
+     * @test
+     */
+    public function shouldHaveDefaultHost()
+    {
+        $this->assertEquals('localhost', $this->getClient()->getHost());
+    }
+
+    /**
+     * @test
+     */
+    public function shouldHaveDefaultPort()
+    {
+        $this->assertEquals(9091, $this->getClient()->getPort());
+    }
+
+    /**
+     * @test
+     */
+    public function shouldHaveNoTokenOnInstantiation()
+    {
+        $this->assertEmpty($this->getClient()->getToken());
+    }
+
+    /**
+     * @test
+     */
+    public function shouldHaveDefaultClient()
+    {
+        $this->assertInstanceOf('Buzz\Client\Curl', $this->getClient()->getClient());
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGenerateDefaultUrl()
+    {
+        $this->assertEquals('http://localhost:9091', $this->getClient()->getUrl());
+    }
+
+    /**
+     * @test
+     */
+    public function shouldMakeApiCall()
+    {
+        $test   = $this;
+        $client = $this->getMock('Buzz\Client\Curl');
+        $client->expects($this->once())
+            ->method('send')
+            ->with(
+                $this->isInstanceOf('Buzz\Message\Request'),
+                $this->isInstanceOf('Buzz\Message\Response')
+            )
+            ->will($this->returnCallback(function ($request, $response) use ($test) {
+                $test->assertEquals('POST', $request->getMethod());
+                $test->assertEquals('/transmission/rpc', $request->getResource());
+                $test->assertEquals('http://localhost:9091', $request->getHost());
+                $test->assertEmpty($request->getHeader('X-Transmission-Session-Id'));
+                $test->assertEquals('{"method":"foo","arguments":{"bar":"baz"}}', $request->getContent());
+
+                $response->addHeader('HTTP/1.1 200 OK');
+                $response->addHeader('Content-Type: application/json');
+                $response->addHeader('X-Transmission-Session-Id: foo');
+                $response->setContent('{"foo":"bar"}');
+            }));
+
+        $this->getClient()->setClient($client);
+        $response = $this->getClient()->call('foo', array('bar' => 'baz'));
+
+        $this->assertInstanceOf('stdClass', $response);
+        $this->assertObjectHasAttribute('foo', $response);
+        $this->assertEquals('bar', $response->foo);
+    }
+
+    /**
+     * @test
+     */
+    public function shouldAuthenticate()
+    {
+        $test   = $this;
+        $client = $this->getMock('Buzz\Client\Curl');
+        $client->expects($this->once())
+            ->method('send')
+            ->with(
+                $this->isInstanceOf('Buzz\Message\Request'),
+                $this->isInstanceOf('Buzz\Message\Response')
+            )
+            ->will($this->returnCallback(function ($request, $response) use ($test) {
+                $test->assertEquals('POST', $request->getMethod());
+                $test->assertEquals('/transmission/rpc', $request->getResource());
+                $test->assertEquals('http://localhost:9091', $request->getHost());
+                $test->assertEmpty($request->getHeader('X-Transmission-Session-Id'));
+                $test->assertEquals('Basic '. base64_encode('foo:bar'), $request->getHeader('Authorization'));
+                $test->assertEquals('{"method":"foo","arguments":{"bar":"baz"}}', $request->getContent());
+
+                $response->addHeader('HTTP/1.1 200 OK');
+                $response->addHeader('Content-Type: application/json');
+                $response->addHeader('X-Transmission-Session-Id: foo');
+                $response->setContent('{"foo":"bar"}');
+            }));
+
+        $this->getClient()->authenticate('foo', 'bar');
+        $this->getClient()->setClient($client);
+        $response = $this->getClient()->call('foo', array('bar' => 'baz'));
+
+        $this->assertInstanceOf('stdClass', $response);
+        $this->assertObjectHasAttribute('foo', $response);
+        $this->assertEquals('bar', $response->foo);
+    }
+
+    /**
+     * @test
+     * @expectedException RuntimeException
+     */
+    public function shouldThrowExceptionOnExceptionDuringApiCall()
+    {
+        $client = $this->getMock('Buzz\Client\Curl');
+        $client->expects($this->once())
+            ->method('send')
+            ->will($this->throwException(new \Exception()));
+
+        $this->getClient()->setClient($client);
+        $this->getClient()->call('foo', array());
+    }
+
+    /**
+     * @test
+     * @expectedException RuntimeException
+     */
+    public function shouldThrowExceptionOnUnexpectedStatusCode()
+    {
+        $client = $this->getMock('Buzz\Client\Curl');
+        $client->expects($this->once())
+            ->method('send')
+            ->will($this->returnCallback(function ($request, $response) {
+                $response->addHeader('HTTP/1.1 500 Internal Server Error');
+            }));
+
+        $this->getClient()->setClient($client);
+        $this->getClient()->call('foo', array());
+    }
+
+    /**
+     * @test
+     * @expectedException RuntimeException
+     */
+    public function shouldThrowExceptionOnAccessDenied()
+    {
+        $client = $this->getMock('Buzz\Client\Curl');
+        $client->expects($this->once())
+            ->method('send')
+            ->will($this->returnCallback(function ($request, $response) {
+                $response->addHeader('HTTP/1.1 401 Access Denied');
+            }));
+
+        $this->getClient()->setClient($client);
+        $this->getClient()->call('foo', array());
+    }
+
+    /**
+     * @test
+     */
+    public function shouldHandle409ResponseWhenMakingAnApiCall()
+    {
+        $test   = $this;
+        $client = $this->getMock('Buzz\Client\Curl');
+        $client->expects($this->at(0))
+            ->method('send')
+            ->will($this->returnCallback(function ($request, $response) use ($test) {
+                $test->assertEmpty($request->getHeader('X-Transmission-Session-Id'));
+
+                $response->addHeader('HTTP/1.1 409 Conflict');
+                $response->addHeader('X-Transmission-Session-Id: foo');
+            }));
+
+        $client->expects($this->at(1))
+            ->method('send')
+            ->will($this->returnCallback(function ($request, $response) {
+                $response->addHeader('HTTP/1.1 200 OK');
+                $response->addHeader('Content-Type: application/json');
+                $response->addHeader('X-Transmission-Session-Id: foo');
+                $response->setContent('{"foo":"bar"}');
+            }));
+
+        $this->getClient()->setClient($client);
+        $response = $this->getClient()->call('foo', array());
+
+        $this->assertEquals('foo', $this->getClient()->getToken());
+        $this->assertInstanceOf('stdClass', $response);
+        $this->assertObjectHasAttribute('foo', $response);
+        $this->assertEquals('bar', $response->foo);
+    }
+
+    public function setup()
+    {
+        $this->client = new Client();
+    }
+
+    private function getClient()
+    {
+        return $this->client;
+    }
+}

+ 52 - 0
api/vendor/kleiram/transmission-php/tests/Transmission/Tests/Model/AbstractModelTest.php

@@ -0,0 +1,52 @@
+<?php
+namespace Transmission\Tests\Model;
+
+class AbstractModelTest extends \PHPUnit_Framework_TestCase
+{
+    protected $model;
+
+    /**
+     * @test
+     */
+    public function shouldImplementModelInterface()
+    {
+        $this->assertInstanceOf('Transmission\Model\ModelInterface', $this->getModel());
+    }
+
+    /**
+     * @test
+     */
+    public function shouldHaveEmptyMappingByDefault()
+    {
+        $this->assertEmpty($this->getModel()->getMapping());
+    }
+
+    /**
+     * @test
+     */
+    public function shouldHaveNoClientByDefault()
+    {
+        $this->assertNull($this->getModel()->getClient());
+    }
+
+    /**
+     * @test
+     */
+    public function shouldHaveClientIfSetByUser()
+    {
+        $client = $this->getMock('Transmission\Client');
+
+        $this->getModel()->setClient($client);
+        $this->assertEquals($client, $this->getModel()->getClient());
+    }
+
+    public function setup()
+    {
+        $this->model = $this->getMockForAbstractClass('Transmission\Model\AbstractModel');
+    }
+
+    private function getModel()
+    {
+        return $this->model;
+    }
+}

+ 57 - 0
api/vendor/kleiram/transmission-php/tests/Transmission/Tests/Model/FileTest.php

@@ -0,0 +1,57 @@
+<?php
+namespace Transmission\Tests\Model;
+
+use Transmission\Model\File;
+use Transmission\Util\PropertyMapper;
+
+
+class FileTest extends \PHPUnit_Framework_TestCase
+{
+    protected $file;
+
+    /**
+     * @test
+     */
+    public function shouldImplementModelInterface()
+    {
+        $this->assertInstanceOf('Transmission\Model\ModelInterface', $this->getFile());
+    }
+
+    /**
+     * @test
+     */
+    public function shouldHaveNonEmptyMapping()
+    {
+        $this->assertNotEmpty($this->getFile()->getMapping());
+    }
+
+    /**
+     * @test
+     */
+    public function shouldBeCreatedFromMapping()
+    {
+        $source = (object) array(
+            'name' => 'foo',
+            'length' => 100,
+            'bytesCompleted' => 10
+        );
+
+        PropertyMapper::map($this->getFile(), $source);
+
+        $this->assertEquals('foo', $this->getFile()->getName());
+        $this->assertEquals(100, $this->getFile()->getSize());
+        $this->assertEquals(10, $this->getFile()->getCompleted());
+        $this->assertFalse($this->getFile()->isDone());
+    }
+
+    public function setup()
+    {
+        $this->file = new File();
+    }
+
+    private function getFile()
+    {
+        return $this->file;
+    }
+}
+

+ 79 - 0
api/vendor/kleiram/transmission-php/tests/Transmission/Tests/Model/PeerTest.php

@@ -0,0 +1,79 @@
+<?php
+namespace Transmission\Tests\Model;
+
+use Transmission\Model\Peer;
+use Transmission\Util\PropertyMapper;
+
+class PeerTest extends \PHPUnit_Framework_TestCase
+{
+    protected $peer;
+
+    /**
+     * @test
+     */
+    public function shouldImplementModelInterface()
+    {
+        $this->assertInstanceOf('Transmission\Model\ModelInterface', $this->getPeer());
+    }
+
+    /**
+     * @test
+     */
+    public function shouldHaveNonEmptyMapping()
+    {
+        $this->assertNotEmpty($this->getPeer()->getMapping());
+    }
+
+    /**
+     * @test
+     */
+    public function shouldBeCreatedFromMapping()
+    {
+        $source = (object) array(
+            'address' => 'foo',
+            'clientName' => 'foo',
+            'clientIsChoked' => false,
+            'clientIsInterested' => true,
+            'flagStr' => 'foo',
+            'isDownloadingFrom' => false,
+            'isEncrypted' => true,
+            'isIncoming' => false,
+            'isUploadingTo' => true,
+            'isUTP' => false,
+            'peerIsChoked' => true,
+            'peerIsInterested' => false,
+            'port' => 3000,
+            'progress' => 10.5,
+            'rateToClient' => 1000,
+            'rateFromClient' => 10000
+        );
+
+        PropertyMapper::map($this->getPeer(), $source);
+
+        $this->assertEquals('foo', $this->getPeer()->getAddress());
+        $this->assertEquals('foo', $this->getPeer()->getClientName());
+        $this->assertFalse($this->getPeer()->isClientChoked());
+        $this->assertTrue($this->getPeer()->isClientInterested());
+        $this->assertFalse($this->getPeer()->isDownloading());
+        $this->assertTrue($this->getPeer()->isEncrypted());
+        $this->assertFalse($this->getPeer()->isIncoming());
+        $this->assertTrue($this->getPeer()->isUploading());
+        $this->assertFalse($this->getPeer()->isUtp());
+        $this->assertTrue($this->getPeer()->isPeerChoked());
+        $this->assertFalse($this->getPeer()->isPeerInterested());
+        $this->assertEquals(3000, $this->getPeer()->getPort());
+        $this->assertEquals(10.5, $this->getPeer()->getProgress());
+        $this->assertEquals(1000, $this->getPeer()->getUploadRate());
+        $this->assertEquals(10000, $this->getPeer()->getDownloadRate());
+    }
+
+    public function setup()
+    {
+        $this->peer = new Peer();
+    }
+
+    public function getPeer()
+    {
+        return $this->peer;
+    }
+}

+ 126 - 0
api/vendor/kleiram/transmission-php/tests/Transmission/Tests/Model/SessionTest.php

@@ -0,0 +1,126 @@
+<?php
+namespace Transmission\Tests\Model;
+
+use Transmission\Model\Session;
+use Transmission\Util\PropertyMapper;
+use Symfony\Component\PropertyAccess\PropertyAccess;
+
+class SessionTest extends \PHPUnit_Framework_TestCase
+{
+    protected $session;
+
+    /**
+     * @test
+     */
+    public function shouldImplementModelInterface()
+    {
+        $this->assertInstanceOf('Transmission\Model\ModelInterface', $this->getSession());
+    }
+
+    /**
+     * @test
+     */
+    public function shouldHaveNonEmptyMapping()
+    {
+        $this->assertNotEmpty($this->getSession()->getMapping());
+    }
+
+    /** 
+     * @test
+     */
+    public function shouldBeCreatedFromMapping()
+    {
+        $source = (object) array(
+            'alt-speed-down' => 1,
+            'alt-speed-enabled' => true,
+            'download-dir' => 'foo',
+            'download-queue-enabled' => true,
+            'download-queue-size' => 5,
+            'incomplete-dir' => 'bar',
+            'incomplete-dir-enabled' => true,
+            'script-torrent-done-filename' => 'baz',
+            'script-torrent-done-enabled' => true,
+            'seedRatioLimit' => 3.14,
+            'seedRatioLimited' => true,
+            'seed-queue-size' => 5,
+            'seed-queue-enabled' => true,
+            'speed-limit-down' => 100,
+            'speed-limit-down-enabled' => true,
+        );
+
+        PropertyMapper::map($this->getSession(), $source);
+
+        $this->assertEquals(1, $this->getSession()->getAltSpeedDown());
+        $this->assertTrue($this->getSession()->isAltSpeedEnabled());
+        $this->assertEquals('foo', $this->getSession()->getDownloadDir());
+        $this->assertEquals(5, $this->getSession()->getDownloadQueueSize());
+        $this->assertTrue($this->getSession()->isDownloadQueueEnabled());
+        $this->assertEquals('bar', $this->getSession()->getIncompleteDir());
+        $this->assertTrue($this->getSession()->isIncompleteDirEnabled());
+        $this->assertEquals('baz', $this->getSession()->getTorrentDoneScript());
+        $this->assertTrue($this->getSession()->isTorrentDoneScriptEnabled());
+        $this->assertEquals(3.14, $this->getSession()->getSeedRatioLimit());
+        $this->assertTrue($this->getSession()->isSeedRatioLimited());
+        $this->assertEquals(5, $this->getSession()->getSeedQueueSize());
+        $this->assertTrue($this->getSession()->isSeedQueueEnabled());
+        $this->assertEquals(100, $this->getSession()->getDownloadSpeedLimit());
+        $this->assertTrue($this->getSession()->isDownloadSpeedLimitEnabled());
+    }
+
+    /**
+     * @test
+     */
+    public function shouldSave()
+    {
+        $expected = array(
+            'alt-speed-down' => 1,
+            'alt-speed-enabled' => true,
+            'download-dir' => 'foo',
+            'download-queue-enabled' => true,
+            'download-queue-size' => 5,
+            'incomplete-dir' => 'bar',
+            'incomplete-dir-enabled' => true,
+            'script-torrent-done-filename' => 'baz',
+            'script-torrent-done-enabled' => true,
+            'seedRatioLimit' => 3.14,
+            'seedRatioLimited' => true,
+            'seed-queue-size' => 5,
+            'seed-queue-enabled' => true,
+            'speed-limit-down' => 100,
+            'speed-limit-down-enabled' => true
+        );
+
+        PropertyMapper::map($this->getSession(), (object) $expected);
+
+        $client = $this->getMock('Transmission\Client');
+        $client->expects($this->once())
+            ->method('call')
+            ->with('session-set', $expected)
+            ->will($this->returnCallback(function () {
+                return (object) array(
+                    'result' => 'success',
+                );
+            }));
+
+        $this->getSession()->setClient($client);
+        $this->getSession()->save();
+    }
+
+    /**
+     * @test
+     */
+    public function shouldNotSaveWithNoClient()
+    {
+        $this->getSession()->save();
+    }
+
+    public function setup()
+    {
+        $this->session = new Session();
+    }
+
+    protected function getSession()
+    {
+        return $this->session;
+    }
+}

+ 286 - 0
api/vendor/kleiram/transmission-php/tests/Transmission/Tests/Model/TorrentTest.php

@@ -0,0 +1,286 @@
+<?php
+namespace Transmission\Tests\Model;
+
+use Transmission\Model\Torrent;
+use Transmission\Util\PropertyMapper;
+use Symfony\Component\PropertyAccess\PropertyAccess;
+
+class TorrentTest extends \PHPUnit_Framework_TestCase
+{
+    protected $torrent;
+
+    /**
+     * @test
+     */
+    public function shouldImplementModelInterface()
+    {
+        $this->assertInstanceOf('Transmission\Model\ModelInterface', $this->getTorrent());
+    }
+
+    /**
+     * @test
+     */
+    public function shouldHaveNonEmptyMapping()
+    {
+        $this->assertNotEmpty($this->getTorrent()->getMapping());
+    }
+
+    /**
+     * @test
+     */
+    public function shouldBeCreatedFromMapping()
+    {
+        $source = (object) array(
+            'id' => 1,
+            'eta' => 10,
+            'sizeWhenDone' => 10000,
+            'name' => 'foo',
+            'hashString' => 'bar',
+            'status' => 0,
+            'isFinished' => false,
+            'rateUpload' => 10,
+            'rateDownload' => 100,
+            'files' => array(
+                (object) array()
+            ),
+            'peers' => array(
+                (object) array(),
+                (object) array()
+            ),
+            'trackers' => array(
+                (object) array(),
+                (object) array(),
+                (object) array()
+            )
+        );
+
+        PropertyMapper::map($this->getTorrent(), $source);
+
+        $this->assertEquals(1, $this->getTorrent()->getId());
+        $this->assertEquals(10, $this->getTorrent()->getEta());
+        $this->assertEquals(10000, $this->getTorrent()->getSize());
+        $this->assertEquals('foo', $this->getTorrent()->getName());
+        $this->assertEquals('bar', $this->getTorrent()->getHash());
+        $this->assertEquals(0, $this->getTorrent()->getStatus());
+        $this->assertFalse($this->getTorrent()->isFinished());
+        $this->assertEquals(10, $this->getTorrent()->getUploadRate());
+        $this->assertEquals(100, $this->getTorrent()->getDownloadRate());
+        $this->assertCount(1, $this->getTorrent()->getFiles());
+        $this->assertCount(2, $this->getTorrent()->getPeers());
+        $this->assertCount(3, $this->getTorrent()->getTrackers());
+    }
+
+    /**
+     * @test
+     */
+    public function shouldBeDoneWhenFinishedFlagIsSet()
+    {
+        $this->getTorrent()->setFinished(true);
+
+        $this->assertTrue($this->getTorrent()->isFinished());
+    }
+
+    /**
+     * @test
+     */
+    public function shouldBeDoneWhenPercentDoneIs100Percent()
+    {
+        $this->getTorrent()->setPercentDone(1);
+
+        $this->assertTrue($this->getTorrent()->isFinished());
+    }
+
+    /**
+     * @test
+     * @dataProvider statusProvider
+     */
+    public function shouldHaveConvenienceMethods($status, $method)
+    {
+        $methods = array('stopped', 'checking', 'downloading', 'seeding');
+        $accessor = PropertyAccess::getPropertyAccessor();
+        $this->getTorrent()->setStatus($status);
+
+        $methods = array_filter($methods, function ($value) use ($method) {
+            return $method !== $value;
+        });
+
+        $this->assertTrue($accessor->getValue($this->getTorrent(), $method));
+        foreach ($methods as $m) {
+            $this->assertFalse($accessor->getValue($this->getTorrent(), $m), $m);
+        }
+    }
+
+    /**
+     * @test
+     */
+    public function shouldStopDownloading()
+    {
+        $client = $this->getMock('Transmission\Client');
+        $client->expects($this->once())
+            ->method('call')
+            ->with('torrent-stop', array('ids' => array(1)))
+            ->will($this->returnCallback(function () {
+                return (object) array(
+                    'result' => 'success'
+                );
+            }));
+
+        $this->getTorrent()->setId(1);
+        $this->getTorrent()->setClient($client);
+        $this->getTorrent()->stop();
+    }
+
+    /**
+     * @test
+     */
+    public function shouldStartDownloading()
+    {
+        $client = $this->getMock('Transmission\Client');
+        $client->expects($this->once())
+            ->method('call')
+            ->with('torrent-start', array('ids' => array(1)))
+            ->will($this->returnCallback(function () {
+                return (object) array(
+                    'result' => 'success'
+                );
+            }));
+
+        $this->getTorrent()->setId(1);
+        $this->getTorrent()->setClient($client);
+        $this->getTorrent()->start();
+    }
+
+    /**
+     * @test
+     */
+    public function shouldStartDownloadingNow()
+    {
+        $client = $this->getMock('Transmission\Client');
+        $client->expects($this->once())
+            ->method('call')
+            ->with('torrent-start-now', array('ids' => array(1)))
+            ->will($this->returnCallback(function () {
+                return (object) array(
+                    'result' => 'success'
+                );
+            }));
+
+        $this->getTorrent()->setId(1);
+        $this->getTorrent()->setClient($client);
+        $this->getTorrent()->start(true);
+    }
+
+    /**
+     * @test
+     */
+    public function shouldVerifyTorrent()
+    {
+        $client = $this->getMock('Transmission\Client');
+        $client->expects($this->once())
+            ->method('call')
+            ->with('torrent-verify', array('ids' => array(1)))
+            ->will($this->returnCallback(function () {
+                return (object) array(
+                    'result' => 'success'
+                );
+            }));
+
+        $this->getTorrent()->setId(1);
+        $this->getTorrent()->setClient($client);
+        $this->getTorrent()->verify();
+    }
+
+    /**
+     * @test
+     */
+    public function shouldReannounceTorrent()
+    {
+        $client = $this->getMock('Transmission\Client');
+        $client->expects($this->once())
+            ->method('call')
+            ->with('torrent-reannounce', array('ids' => array(1)))
+            ->will($this->returnCallback(function () {
+                return (object) array(
+                    'result' => 'success'
+                );
+            }));
+
+        $this->getTorrent()->setId(1);
+        $this->getTorrent()->setClient($client);
+        $this->getTorrent()->reannounce();
+    }
+
+    /**
+     * @test
+     */
+    public function shouldBeAbleToRemoveItselfFromTheDownloadQueue()
+    {
+        $client = $this->getMock('Transmission\Client');
+        $client->expects($this->once())
+            ->method('call')
+            ->with('torrent-remove', array('ids' => array(1)))
+            ->will($this->returnCallback(function () {
+                return (object) array(
+                    'result' => 'success'
+                );
+            }));
+
+        $this->getTorrent()->setId(1);
+        $this->getTorrent()->setClient($client);
+        $this->getTorrent()->remove();
+    }
+
+    /**
+     * @test
+     */
+    public function shouldRemoveLocalData()
+    {
+        $client = $this->getMock('Transmission\Client');
+        $client->expects($this->once())
+            ->method('call')
+            ->with(
+                'torrent-remove',
+                array('ids' => array(1), 'delete-local-data' => true)
+            )
+            ->will($this->returnCallback(function () {
+                return (object) array(
+                    'result' => 'success'
+                );
+            }));
+
+        $this->getTorrent()->setId(1);
+        $this->getTorrent()->setClient($client);
+        $this->getTorrent()->remove(true);
+    }
+
+    /**
+     * @test
+     */
+    public function shouldNotRemoveItselfWhenNoClientIsSet()
+    {
+        $this->getTorrent()->remove();
+    }
+
+    public function statusProvider()
+    {
+        return array(
+            array(0, 'stopped'),
+            array(1, 'checking'),
+            array(2, 'checking'),
+            array(3, 'downloading'),
+            array(4, 'downloading'),
+            array(5, 'seeding'),
+            array(6, 'seeding')
+        );
+    }
+
+    public function setup()
+    {
+        $this->torrent = new Torrent();
+    }
+
+    public function getTorrent()
+    {
+        return $this->torrent;
+    }
+}

+ 56 - 0
api/vendor/kleiram/transmission-php/tests/Transmission/Tests/Model/TrackerTest.php

@@ -0,0 +1,56 @@
+<?php
+namespace Transmission\Tests\Model;
+
+use Transmission\Model\Tracker;
+use Transmission\Util\PropertyMapper;
+
+class TrackerTest extends \PHPUnit_Framework_TestCase
+{
+    protected $tracker;
+
+    /**
+     * @test
+     */
+    public function shouldImplementModelInterface()
+    {
+        $this->assertInstanceOf('Transmission\Model\ModelInterface', $this->getTracker());
+    }
+
+    /**
+     * @test
+     */
+    public function shouldHaveNonEmptyMapping()
+    {
+        $this->assertNotEmpty($this->getTracker()->getMapping());
+    }
+
+    /**
+     * @test
+     */
+    public function shouldBeCreatedFromMapping()
+    {
+        $source = (object) array(
+            'id' => 1,
+            'tier' => 1,
+            'scrape' => 'foo',
+            'announce' => 'bar'
+        );
+
+        PropertyMapper::map($this->getTracker(), $source);
+
+        $this->assertEquals(1, $this->getTracker()->getId());
+        $this->assertEquals(1, $this->getTracker()->getTier());
+        $this->assertEquals('foo', $this->getTracker()->getScrape());
+        $this->assertEquals('bar', $this->getTracker()->getAnnounce());
+    }
+
+    public function setup()
+    {
+        $this->tracker = new Tracker();
+    }
+
+    private function getTracker()
+    {
+        return $this->tracker;
+    }
+}

+ 253 - 0
api/vendor/kleiram/transmission-php/tests/Transmission/Tests/TransmissionTest.php

@@ -0,0 +1,253 @@
+<?php
+namespace Transmission\Tests;
+
+use Transmission\Transmission;
+
+class TransmissionTest extends \PHPUnit_Framework_TestCase
+{
+    protected $transmission;
+
+    /**
+     * @test
+     */
+    public function shouldHaveDefaultHost()
+    {
+        $this->assertEquals('localhost', $this->getTransmission()->getClient()->getHost());
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetAllTorrentsInDownloadQueue()
+    {
+        $client = $this->getMock('Transmission\Client');
+        $client->expects($this->once())
+            ->method('call')
+            ->with('torrent-get')
+            ->will($this->returnCallback(function ($method, $arguments) {
+                return (object) array(
+                    'result' => 'success',
+                    'arguments' => (object) array(
+                        'torrents' => array(
+                            (object) array(),
+                            (object) array(),
+                            (object) array(),
+                        )
+                    )
+                );
+            }));
+
+        $this->getTransmission()->setClient($client);
+
+        $torrents = $this->getTransmission()->all();
+
+        $this->assertCount(3, $torrents);
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetTorrentById()
+    {
+        $that   = $this;
+        $client = $this->getMock('Transmission\Client');
+        $client->expects($this->once())
+            ->method('call')
+            ->with('torrent-get')
+            ->will($this->returnCallback(function ($method, $arguments) use ($that) {
+                $that->assertEquals(1, $arguments['ids'][0]);
+
+                return (object) array(
+                    'result' => 'success',
+                    'arguments' => (object) array(
+                        'torrents' => array(
+                            (object) array()
+                        )
+                    )
+                );
+            }));
+
+        $this->getTransmission()->setClient($client);
+
+        $torrent = $this->getTransmission()->get(1);
+
+        $this->assertInstanceOf('Transmission\Model\Torrent', $torrent);
+    }
+
+    /**
+     * @test
+     * @expectedException RuntimeException
+     */
+    public function shouldThrowExceptionWhenTorrentIsNotFound()
+    {
+        $client = $this->getMock('Transmission\Client');
+        $client->expects($this->once())
+            ->method('call')
+            ->with('torrent-get')
+            ->will($this->returnCallback(function ($method, $arguments) {
+                return (object) array(
+                    'result' => 'success',
+                    'arguments' => (object) array(
+                        'torrents' => array()
+                    )
+                );
+            }));
+
+        $this->getTransmission()->setClient($client);
+        $this->getTransmission()->get(1);
+    }
+
+    /**
+     * @test
+     */
+    public function shouldAddTorrentByFilename()
+    {
+        $that   = $this;
+        $client = $this->getMock('Transmission\Client');
+        $client->expects($this->once())
+            ->method('call')
+            ->with('torrent-add')
+            ->will($this->returnCallback(function ($method, $arguments) use ($that) {
+                $that->assertArrayHasKey('filename', $arguments);
+
+                return (object) array(
+                    'result' => 'success',
+                    'arguments' => (object) array(
+                        'torrent-added' => (object) array()
+                    )
+                );
+            }));
+
+        $this->getTransmission()->setClient($client);
+
+        $torrent = $this->getTransmission()->add('foo');
+        $this->assertInstanceOf('Transmission\Model\Torrent', $torrent);
+    }
+
+    /**
+     * @test
+     */
+    public function shouldAddTorrentByMetainfo()
+    {
+        $that   = $this;
+        $client = $this->getMock('Transmission\Client');
+        $client->expects($this->once())
+            ->method('call')
+            ->with('torrent-add')
+            ->will($this->returnCallback(function ($method, $arguments) use ($that) {
+                $that->assertArrayHasKey('metainfo', $arguments);
+
+                return (object) array(
+                    'result' => 'success',
+                    'arguments' => (object) array(
+                        'torrent-added' => (object) array()
+                    )
+                );
+            }));
+
+        $this->getTransmission()->setClient($client);
+
+        $torrent = $this->getTransmission()->add('foo', true);
+        $this->assertInstanceOf('Transmission\Model\Torrent', $torrent);
+    }
+
+    /**
+     * @test
+     */
+    public function shouldHandleDuplicateTorrent()
+    {
+        $that   = $this;
+        $client = $this->getMock('Transmission\Client');
+        $client->expects($this->once())
+            ->method('call')
+            ->with('torrent-add')
+            ->will($this->returnCallback(function ($method, $arguments) use ($that) {
+                $that->assertArrayHasKey('metainfo', $arguments);
+
+                return (object) array(
+                    'result' => 'duplicate torrent',
+                    'arguments' => (object) array(
+                        'torrent-duplicate' => (object) array()
+                    )
+                );
+            }));
+
+        $this->getTransmission()->setClient($client);
+
+        $torrent = $this->getTransmission()->add('foo', true);
+        $this->assertInstanceOf('Transmission\Model\Torrent', $torrent);
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetSession()
+    {
+        $that   = $this;
+        $client = $this->getMock('Transmission\Client');
+        $client->expects($this->once())
+            ->method('call')
+            ->with('session-get')
+            ->will($this->returnCallback(function ($method, $arguments) use ($that) {
+                $that->assertEmpty($arguments);
+
+                return (object) array(
+                    'result' => 'success',
+                    'arguments' => (object) array()
+                );
+            }));
+
+        $this->getTransmission()->setClient($client);
+        $session = $this->getTransmission()->getSession();
+
+        $this->assertInstanceOf('Transmission\Model\Session', $session);
+    }
+
+    /**
+     * @test
+     */
+    public function shouldHaveDefaultPort()
+    {
+        $this->assertEquals(9091, $this->getTransmission()->getClient()->getPort());
+    }
+
+    /**
+     * @test
+     */
+    public function shouldProvideFacadeForClient()
+    {
+        $client = $this->getMock('Transmission\Client');
+        $client->expects($this->once())
+            ->method('setHost')
+            ->with('example.org');
+
+        $client->expects($this->once())
+            ->method('getHost')
+            ->will($this->returnValue('example.org'));
+
+        $client->expects($this->once())
+            ->method('setPort')
+            ->with(80);
+
+        $client->expects($this->once())
+            ->method('getPort')
+            ->will($this->returnValue(80));
+
+        $this->getTransmission()->setClient($client);
+        $this->getTransmission()->setHost('example.org');
+        $this->getTransmission()->setPort(80);
+
+        $this->assertEquals('example.org', $this->getTransmission()->getHost());
+        $this->assertEquals(80, $this->getTransmission()->getPort());
+    }
+
+    public function setup()
+    {
+        $this->transmission = new Transmission();
+    }
+
+    private function getTransmission()
+    {
+        return $this->transmission;
+    }
+}

+ 40 - 0
api/vendor/kleiram/transmission-php/tests/Transmission/Tests/Util/PropertyMapperTest.php

@@ -0,0 +1,40 @@
+<?php
+namespace Transmission\Tests\Util;
+
+use Transmission\Util\PropertyMapper;
+
+class PropertyMapperTest extends \PHPUnit_Framework_TestCase
+{
+    protected $mapper;
+
+    /**
+     * @test
+     */
+    public function shouldMapSourcesToModelWithMethodCall()
+    {
+        $source = (object) array(
+            'foo' => 'this',
+            'bar' => 'that',
+            'ba' => 'thus',
+            'unused' => false
+        );
+
+        $model = new \Transmission\Mock\Model();
+
+        $this->getMapper()->map($model, $source);
+
+        $this->assertEquals('this', $model->getFo());
+        $this->assertEquals('that', $model->getBar());
+        $this->assertNull($model->getUnused());
+    }
+
+    public function setup()
+    {
+        $this->mapper = new PropertyMapper();
+    }
+
+    private function getMapper()
+    {
+        return $this->mapper;
+    }
+}

+ 184 - 0
api/vendor/kleiram/transmission-php/tests/Transmission/Tests/Util/ResponseValidatorTest.php

@@ -0,0 +1,184 @@
+<?php
+namespace Transmission\Tests\Util;
+
+use Transmission\Util\ResponseValidator;
+
+class ResponseValidatorTest extends \PHPUnit_Framework_TestCase
+{
+    protected $validator;
+
+    /**
+     * @test
+     * @expectedException RuntimeException
+     */
+    public function shouldThrowExceptionOnMissingResultField()
+    {
+        $response = (object) array();
+
+        $this->getValidator()->validate('', $response);
+    }
+
+    /**
+     * @test
+     * @expectedException RuntimeException
+     */
+    public function shouldThrowExceptionOnErrorResultField()
+    {
+        $response = (object) array('result' => 'error');
+
+        $this->getValidator()->validate('', $response);
+    }
+
+    /**
+     * @test
+     */
+    public function shouldThrowNoExceptionOnValidTorrentGetResponse()
+    {
+        $response = (object) array(
+            'result' => 'success',
+            'arguments' => (object) array(
+                'torrents' => array(
+                    (object) array('foo' => 'bar')
+                )
+            )
+        );
+
+        $expected  = array((object) array('foo' => 'bar'));
+        $container = $this->getValidator()->validate('torrent-get', $response);
+        $this->assertEquals($expected, $container);
+    }
+
+    /**
+     * @test
+     * @expectedException RuntimeException
+     */
+    public function shouldThrowExceptionOnMissingArgumentsInTorrentGetResponse()
+    {
+        $response = (object) array('result' => 'success');
+
+        $this->getValidator()->validate('torrent-get', $response);
+    }
+
+    /**
+     * @test
+     * @expectedException RuntimeException
+     */
+    public function shouldThrowExceptionOnMissingTorrentArgumentInTorrentGetResponse()
+    {
+        $response = (object) array('result' => 'success', 'arguments' => (object) array());
+
+        $this->getValidator()->validate('torrent-get', $response);
+    }
+
+    /**
+     * @test
+     */
+    public function shouldThrowNoExceptionOnValidTorrentAddResponse()
+    {
+        $response = (object) array(
+            'result' => 'success',
+            'arguments' => (object) array(
+                'torrent-added' => (object) array(
+                    'foo' => 'bar'
+                )
+            )
+        );
+
+        $expected  = (object) array('foo' => 'bar');
+        $container = $this->getValidator()->validate('torrent-add', $response);
+        $this->assertEquals($expected, $container);
+    }
+
+    /**
+     * @test
+     */
+    public function shouldThrowNoExceptionOnValidSessionGetResponse()
+    {
+        $response = (object) array(
+            'result' => 'success',
+            'arguments' => (object) array(
+                    'foo' => 'bar'
+            )
+        );
+
+        $expected  = (object) array('foo' => 'bar');
+        $container = $this->getValidator()->validate('session-get', $response);
+        $this->assertEquals($expected, $container);
+    }
+
+    /**
+     * @test
+     * @expectedException RuntimeException
+     */
+    public function shouldThrowExceptionOnMissingArgumentsInSessionGetResponse()
+    {
+        $response = (object) array('result' => 'success');
+
+        $this->getValidator()->validate('session-get', $response);
+    }
+    
+    /**
+     * @test
+     * @expectedException RuntimeException
+     */
+    public function shouldThrowExceptionOnMissingArgumentsSessionGetResponse()
+    {
+        $response = (object) array('result' => 'success');
+
+        $this->getValidator()->validate('session-get', $response);
+    }
+
+    /**
+     * @test
+     * @expectedException RuntimeException
+     */
+    public function shouldThrowExceptionOnMissingArgumentsInTorrentAddResponse()
+    {
+        $response = (object) array('result' => 'success');
+
+        $this->getValidator()->validate('torrent-add', $response);
+    }
+
+    /**
+     * @test
+     * @expectedException RuntimeException
+     */
+    public function shouldThrowExceptionOnMissingTorrentFieldArgumentInTorrentAddResponse()
+    {
+        $response = (object) array('result' => 'success', 'arguments' => (object) array());
+
+        $this->getValidator()->validate('torrent-add', $response);
+    }
+
+    /**
+     * @test
+     * @expectedException RuntimeException
+     */
+    public function shouldThrowExceptionOnEmptyTorrentFieldInTorrentAddResponse()
+    {
+        $response = (object) array('result' => 'success', 'arguments' => (object) array('torrent-added' => array()));
+
+        $this->getValidator()->validate('torrent-add', $response);
+    }
+
+    /**
+     * @test
+     */
+    public function shouldThrowNoExceptionOnValidOtherResponses()
+    {
+        $response = (object) array('result' => 'success');
+
+        $container = $this->getValidator()->validate('torrent-remove', $response);
+        $this->assertNull($container);
+    }
+
+    public function setup()
+    {
+        $this->validator = new ResponseValidator();
+    }
+
+    private function getValidator()
+    {
+        return $this->validator;
+    }
+}

+ 15 - 0
api/vendor/kleiram/transmission-php/tests/bootstrap.php

@@ -0,0 +1,15 @@
+<?php
+if (!$loader = @include __DIR__.'/../vendor/autoload.php') {
+    echo <<<EOM
+    Install dependencies using Composer:
+
+        curl -s https://getcomposer.org/installer | php
+        php composer.phar install
+
+EOM;
+
+    exit(1);
+}
+
+$loader->add('Transmission\Mock', __DIR__);
+$loader->add('Transmission\Tests', __DIR__);

+ 9 - 0
api/vendor/kriswallsmith/buzz/.gitattributes

@@ -0,0 +1,9 @@
+* text=auto
+
+/examples export-ignore
+/test export-ignore
+/.gitignore export-ignore
+/.travis.yml export-ignore
+/CHANGELOG.md export-ignore
+/README.md export-ignore
+/phpunit.xml.dist export-ignore

+ 11 - 0
api/vendor/kriswallsmith/buzz/.php_cs

@@ -0,0 +1,11 @@
+<?php
+
+return PhpCsFixer\Config::create()
+    ->setRules(array(
+        '@Symfony' => true,
+        '@Symfony:risky' => true,
+        'array_syntax' => array('syntax' => 'short'),
+        'protected_to_private' => false,
+    ))
+    ->setRiskyAllowed(true)
+;

+ 19 - 0
api/vendor/kriswallsmith/buzz/LICENSE

@@ -0,0 +1,19 @@
+Copyright (c) 2010-2011 Kris Wallsmith
+
+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.

+ 40 - 0
api/vendor/kriswallsmith/buzz/composer.json

@@ -0,0 +1,40 @@
+{
+    "name": "kriswallsmith/buzz",
+    "description": "Lightweight HTTP client",
+    "keywords": ["http client", "curl"],
+    "homepage": "https://github.com/kriswallsmith/Buzz",
+    "type": "library",
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "Kris Wallsmith",
+            "email": "kris.wallsmith@gmail.com",
+            "homepage": "http://kriswallsmith.net/"
+        }
+    ],
+    "require": {
+        "php": "^5.4 || ^7.0",
+        "guzzlehttp/psr7": "^1.4"
+    },
+    "require-dev": {
+        "symfony/phpunit-bridge": "^3.4 || ^4.0",
+        "php-http/client-integration-tests": "^0.6.2"
+    },
+    "suggest": {
+        "ext-curl": "*"
+    },
+    "autoload": {
+        "psr-4": {
+            "Buzz\\": "lib/Buzz"
+        }
+    },
+    "autoload-dev": {
+        "psr-4": {
+            "Buzz\\Test\\": "test/Buzz/Test"
+        }
+    },
+    "scripts": {
+        "test": "vendor/bin/simple-phpunit",
+        "test-ci": "vendor/bin/simple-phpunit --coverage-text --coverage-clover=build/coverage.xml"
+    }
+}

+ 8 - 0
api/vendor/kriswallsmith/buzz/doc/index.md

@@ -0,0 +1,8 @@
+# Buzz documentation
+
+Buzz is a simple and lightweight HTTP client which is easy to use. This page is 
+the index of the documentation. Please use the table of contents below to start
+reading. 
+
+
+* [Middlewares](/doc/middlewares.md) 

+ 85 - 0
api/vendor/kriswallsmith/buzz/doc/middlewares.md

@@ -0,0 +1,85 @@
+# Buzz middlewares
+
+If you want to modify the request or response somehow, a middleware is the way to
+go. Every time you send a request with the `Browser` it will run through all the
+middlewares. The order of the middlewares is important. The first middleware added
+to the `Browser` will be the first one that is executed when handling the request and
+the last one to be executed when handling the response. 
+
+```
+Request  ---> Middleware1 ---> Middleware2 ---> HttpClient ----
+                                                               | (processing call)
+Response <--- Middleware1 <--- Middleware2 <--- HttpClient <---
+```
+## Creating a middleware
+
+You are free to create any custom middleware you want. It is super simple to do so. 
+Let's look at the example when we create a middleware for adding the User-Agent 
+request header. 
+
+First we need to create a class that implements `Buzz\Middleware\MiddlewareInterface`
+
+```php
+<?php
+
+use Buzz\Middleware\MiddlewareInterface;
+
+class UserAgentMiddleware extends MiddlewareInterface 
+{
+  // ...
+``` 
+
+The interface has two functions; `handleRequest` and `handleResponse`. The last
+parameter to these functions is a `callable`. That callable is actually the next
+middleware in the chain. It is **very important** that you end your function by
+returning the the result when calling that callable. If you forget about that,
+then no request will be sent.
+
+Let's look at an example implementation of `handleRequest`:
+
+```php
+public function handleRequest(RequestInterface $request, callable $next)
+{
+    $request = $request->withAddedHeader('User-Agent', 'Buzz');
+    
+    return $next($request);
+}
+```
+
+Note that PSR-7 requests and responses are immutable. That is why we do 
+`$request = $request->with...`. 
+
+Sine this middleware does not need to modify the response we just let the 
+`handleResponse` function to be empty like: 
+
+ ```php
+ public function handleResponse(RequestInterface $request, ResponseInterface $response, callable $next)
+ {     
+     return $next($request, $response);
+ }
+ ```
+
+### The full example
+
+```php
+<?php
+
+use Buzz\Middleware\MiddlewareInterface;
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+
+class UserAgentMiddleware extends MiddlewareInterface 
+{
+  public function handleRequest(RequestInterface $request, callable $next)
+  {
+      $request = $request->withAddedHeader('User-Agent', 'Buzz');
+      
+      return $next($request);
+  }
+  
+   public function handleResponse(RequestInterface $request, ResponseInterface $response, callable $next)
+   {     
+       return $next($request, $response);
+   }
+}
+``` 

+ 288 - 0
api/vendor/kriswallsmith/buzz/lib/Buzz/Browser.php

@@ -0,0 +1,288 @@
+<?php
+
+namespace Buzz;
+
+use Buzz\Client\ClientInterface;
+use Buzz\Client\FileGetContents;
+use Buzz\Listener\ListenerChain;
+use Buzz\Listener\ListenerInterface;
+use Buzz\Message\Factory\Factory;
+use Buzz\Message\Factory\FactoryInterface;
+use Buzz\Message\MessageInterface;
+use Buzz\Message\RequestInterface;
+use Buzz\Middleware\MiddlewareInterface;
+use Buzz\Util\Url;
+use Psr\Http\Message\RequestInterface as Psr7RequestInterface;
+use Psr\Http\Message\ResponseInterface as Psr7ResponseInterface;
+
+class Browser
+{
+    /** @var ClientInterface */
+    private $client;
+
+    /** @var FactoryInterface */
+    private $factory;
+
+    /** @var ListenerInterface */
+    private $listener;
+
+    /**
+     * @var MiddlewareInterface[]
+     */
+    private $middlewares;
+
+    /** @var RequestInterface */
+    private $lastRequest;
+
+    /** @var MessageInterface */
+    private $lastResponse;
+
+    public function __construct(ClientInterface $client = null, FactoryInterface $factory = null)
+    {
+        $this->client = $client ?: new FileGetContents();
+        $this->factory = $factory ?: new Factory();
+    }
+
+    public function get($url, $headers = array())
+    {
+        return $this->call($url, RequestInterface::METHOD_GET, $headers);
+    }
+
+    public function post($url, $headers = array(), $content = '')
+    {
+        return $this->call($url, RequestInterface::METHOD_POST, $headers, $content);
+    }
+
+    public function head($url, $headers = array())
+    {
+        return $this->call($url, RequestInterface::METHOD_HEAD, $headers);
+    }
+
+    public function patch($url, $headers = array(), $content = '')
+    {
+        return $this->call($url, RequestInterface::METHOD_PATCH, $headers, $content);
+    }
+
+    public function put($url, $headers = array(), $content = '')
+    {
+        return $this->call($url, RequestInterface::METHOD_PUT, $headers, $content);
+    }
+
+    public function delete($url, $headers = array(), $content = '')
+    {
+        return $this->call($url, RequestInterface::METHOD_DELETE, $headers, $content);
+    }
+
+    /**
+     * Sends a request.
+     *
+     * @param string $url     The URL to call
+     * @param string $method  The request method to use
+     * @param array  $headers An array of request headers
+     * @param string $content The request content
+     *
+     * @return MessageInterface The response object
+     */
+    public function call($url, $method, $headers = array(), $content = '')
+    {
+        $request = $this->factory->createRequest($method);
+
+        if (!$url instanceof Url) {
+            $url = new Url($url);
+        }
+
+        $url->applyToRequest($request);
+
+        $request->addHeaders($headers);
+        $request->setContent($content);
+
+        return $this->send($request);
+    }
+
+    /**
+     * Sends a form request.
+     *
+     * @param string $url     The URL to submit to
+     * @param array  $fields  An array of fields
+     * @param string $method  The request method to use
+     * @param array  $headers An array of request headers
+     *
+     * @return MessageInterface The response object
+     */
+    public function submit($url, array $fields, $method = RequestInterface::METHOD_POST, $headers = array())
+    {
+        $request = $this->factory->createFormRequest();
+
+        if (!$url instanceof Url) {
+            $url = new Url($url);
+        }
+
+        $url->applyToRequest($request);
+
+        $request->addHeaders($headers);
+        $request->setMethod($method);
+        $request->setFields($fields);
+
+        return $this->send($request);
+    }
+
+    /**
+     * Sends a request.
+     *
+     * @param RequestInterface $request  A request object
+     * @param MessageInterface $response A response object
+     *
+     * @return MessageInterface The response
+     */
+    public function send(RequestInterface $request, MessageInterface $response = null)
+    {
+        if (null === $response) {
+            $response = $this->factory->createResponse();
+        }
+
+        if ($this->listener) {
+            $this->listener->preSend($request);
+        }
+
+        $this->client->send($request, $response);
+
+        $this->lastRequest = $request;
+        $this->lastResponse = $response;
+
+        if ($this->listener) {
+            $this->listener->postSend($request, $response);
+        }
+
+        return $response;
+    }
+
+    /**
+     * Send a PSR7 request.
+     *
+     * @param Psr7RequestInterface $request
+     * @return Psr7ResponseInterface
+     */
+    public function sendRequest(Psr7RequestInterface $request)
+    {
+        $chain = $this->createMiddlewareChain($this->middlewares, function(Psr7RequestInterface $request) {
+            return $this->client->sendRequest($request);
+        }, function (Psr7RequestInterface $request, Psr7ResponseInterface $response) {
+            $this->lastRequest = $request;
+            $this->lastResponse = $response;
+        });
+
+        // Call the chain
+        $chain($request);
+
+        return $this->lastResponse;
+    }
+
+    /**
+     * @param MiddlewareInterface[] $middlewares
+     * @param callable $requestChainLast
+     * @param callable $responseChainLast
+     *
+     * @return callable
+     */
+    private function createMiddlewareChain(array $middlewares, callable $requestChainLast, callable $responseChainLast)
+    {
+        $responseChainNext = $responseChainLast;
+
+        // Build response chain
+        /** @var MiddlewareInterface $middleware */
+        foreach ($middlewares as $middleware) {
+            $lastCallable = function (Psr7RequestInterface $request, Psr7ResponseInterface $response) use ($middleware, $responseChainNext) {
+                return $middleware->handleResponse($request, $response, $responseChainNext);
+            };
+
+            $responseChainNext = $lastCallable;
+        }
+
+        $requestChainLast = function (Psr7RequestInterface $request) use ($requestChainLast, $responseChainNext) {
+            // Send the actual request and get the response
+            $response = $requestChainLast($request);
+            $responseChainNext($request, $response);
+        };
+
+        $middlewares = array_reverse($middlewares);
+
+        // Build request chain
+        $requestChainNext = $requestChainLast;
+        /** @var MiddlewareInterface $middleware */
+        foreach ($middlewares as $middleware) {
+            $lastCallable = function (Psr7RequestInterface $request) use ($middleware, $requestChainNext) {
+                return $middleware->handleRequest($request, $requestChainNext);
+            };
+
+            $requestChainNext = $lastCallable;
+        }
+
+        return $requestChainNext;
+    }
+
+    public function getLastRequest()
+    {
+        return $this->lastRequest;
+    }
+
+    public function getLastResponse()
+    {
+        return $this->lastResponse;
+    }
+
+    public function setClient(ClientInterface $client)
+    {
+        $this->client = $client;
+    }
+
+    public function getClient()
+    {
+        return $this->client;
+    }
+
+    public function setMessageFactory(FactoryInterface $factory)
+    {
+        $this->factory = $factory;
+    }
+
+    public function getMessageFactory()
+    {
+        return $this->factory;
+    }
+
+    public function setListener(ListenerInterface $listener)
+    {
+        $this->listener = $listener;
+    }
+
+    public function getListener()
+    {
+        return $this->listener;
+    }
+
+    /**
+     * Add a new middleware to the stack.
+     *
+     * @param MiddlewareInterface $middleware
+     */
+    public function addMiddleware(MiddlewareInterface $middleware)
+    {
+        $this->middlewares[] = $middleware;
+    }
+
+
+
+    public function addListener(ListenerInterface $listener)
+    {
+        if (!$this->listener) {
+            $this->listener = $listener;
+        } elseif ($this->listener instanceof ListenerChain) {
+            $this->listener->addListener($listener);
+        } else {
+            $this->listener = new ListenerChain(array(
+                $this->listener,
+                $listener,
+            ));
+        }
+    }
+}

+ 73 - 0
api/vendor/kriswallsmith/buzz/lib/Buzz/Client/AbstractClient.php

@@ -0,0 +1,73 @@
+<?php
+
+namespace Buzz\Client;
+
+abstract class AbstractClient implements ClientInterface
+{
+    protected $ignoreErrors = true;
+    protected $maxRedirects = 5;
+    protected $timeout = 5;
+    protected $verifyPeer = true;
+    protected $verifyHost = 2;
+    protected $proxy;
+
+    public function setIgnoreErrors($ignoreErrors)
+    {
+        $this->ignoreErrors = $ignoreErrors;
+    }
+
+    public function getIgnoreErrors()
+    {
+        return $this->ignoreErrors;
+    }
+
+    public function setMaxRedirects($maxRedirects)
+    {
+        $this->maxRedirects = $maxRedirects;
+    }
+
+    public function getMaxRedirects()
+    {
+        return $this->maxRedirects;
+    }
+
+    public function setTimeout($timeout)
+    {
+        $this->timeout = $timeout;
+    }
+
+    public function getTimeout()
+    {
+        return $this->timeout;
+    }
+
+    public function setVerifyPeer($verifyPeer)
+    {
+        $this->verifyPeer = $verifyPeer;
+    }
+
+    public function getVerifyPeer()
+    {
+        return $this->verifyPeer;
+    }
+
+    public function getVerifyHost()
+    {
+        return $this->verifyHost;
+    }
+
+    public function setVerifyHost($verifyHost)
+    {
+        $this->verifyHost = $verifyHost;
+    }
+
+    public function setProxy($proxy)
+    {
+        $this->proxy = $proxy;
+    }
+
+    public function getProxy()
+    {
+        return $this->proxy;
+    }
+}

+ 278 - 0
api/vendor/kriswallsmith/buzz/lib/Buzz/Client/AbstractCurl.php

@@ -0,0 +1,278 @@
+<?php
+
+namespace Buzz\Client;
+
+use Buzz\Converter\HeaderConverter;
+use Buzz\Converter\RequestConverter;
+use Buzz\Converter\ResponseConverter;
+use Buzz\Message\Form\FormRequestInterface;
+use Buzz\Message\Form\FormUploadInterface;
+use Buzz\Message\MessageInterface;
+use Buzz\Message\RequestInterface as BuzzRequestInterface;
+use Buzz\Exception\ClientException;
+use Buzz\Message\Response;
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+
+/**
+ * Base client class with helpers for working with cURL.
+ */
+abstract class AbstractCurl extends AbstractClient
+{
+    protected $options = array();
+
+    public function __construct()
+    {
+        if (defined('CURLOPT_PROTOCOLS')) {
+            $this->options = array(
+                CURLOPT_PROTOCOLS => CURLPROTO_HTTP | CURLPROTO_HTTPS,
+                CURLOPT_REDIR_PROTOCOLS => CURLPROTO_HTTP | CURLPROTO_HTTPS,
+            );
+        }
+    }
+
+    /**
+     * Creates a new cURL resource.
+     *
+     * @see curl_init()
+     *
+     * @return resource A new cURL resource
+     *
+     * @throws ClientException If unable to create a cURL resource
+     */
+    protected static function createCurlHandle()
+    {
+        if (false === $curl = curl_init()) {
+            throw new ClientException('Unable to create a new cURL handle');
+        }
+
+        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
+        curl_setopt($curl, CURLOPT_HEADER, true);
+
+        return $curl;
+    }
+
+    /**
+     * Populates a response object.
+     *
+     * @param resource         $curl     A cURL resource
+     * @param string           $raw      The raw response string
+     * @param MessageInterface $response The response object
+     *
+     * @deprecated Will be removed in 1.0. Use createResponse instead.
+     */
+    protected static function populateResponse($curl, $raw, MessageInterface $response)
+    {
+        @trigger_error('AbstractCurl::populateResponse() is deprecated. Use AbstractCurl::createResponse instead.', E_USER_DEPRECATED);
+
+        // fixes bug https://sourceforge.net/p/curl/bugs/1204/
+        $version = curl_version();
+        if (version_compare($version['version'], '7.30.0', '<')) {
+            $pos = strlen($raw) - curl_getinfo($curl, CURLINFO_SIZE_DOWNLOAD);
+        } else {
+            $pos = curl_getinfo($curl, CURLINFO_HEADER_SIZE);
+        }
+
+        $response->setHeaders(static::getLastHeaders(rtrim(substr($raw, 0, $pos))));
+        $response->setContent(strlen($raw) > $pos ? substr($raw, $pos) : '');
+    }
+
+    /**
+     * @param $curl
+     * @param $raw
+     * @return ResponseInterface
+     */
+    protected function createResponse($curl, $raw)
+    {
+        // fixes bug https://sourceforge.net/p/curl/bugs/1204/
+        $version = curl_version();
+        if (version_compare($version['version'], '7.30.0', '<')) {
+            $pos = strlen($raw) - curl_getinfo($curl, CURLINFO_SIZE_DOWNLOAD);
+        } else {
+            $pos = curl_getinfo($curl, CURLINFO_HEADER_SIZE);
+        }
+
+        // TODO rewrite me to avoid using BuzzRequest
+        $response = new Response();
+        $response->setHeaders(static::getLastHeaders(rtrim(substr($raw, 0, $pos))));
+        $response->setContent(strlen($raw) > $pos ? substr($raw, $pos) : '');
+
+        $response = ResponseConverter::psr7($response);
+
+        return $response;
+    }
+
+    /**
+     * Sets options on a cURL resource based on a request.
+     *
+     * @param resource         $curl    A cURL resource
+     * @param RequestInterface $request A request object
+     */
+    private static function setOptionsFromRequest($curl, RequestInterface $request)
+    {
+        $options = array(
+            CURLOPT_HTTP_VERSION  => $request->getProtocolVersion() == 1.0 ? CURL_HTTP_VERSION_1_0 : CURL_HTTP_VERSION_1_1,
+            CURLOPT_CUSTOMREQUEST => $request->getMethod(),
+            CURLOPT_URL           => $request->getUri()->__toString(),
+            CURLOPT_HTTPHEADER    => HeaderConverter::toBuzzHeaders($request->getHeaders()),
+        );
+
+        switch ($request->getMethod()) {
+            case BuzzRequestInterface::METHOD_HEAD:
+                $options[CURLOPT_NOBODY] = true;
+                break;
+
+            case BuzzRequestInterface::METHOD_GET:
+                $options[CURLOPT_HTTPGET] = true;
+                break;
+
+            case BuzzRequestInterface::METHOD_POST:
+            case BuzzRequestInterface::METHOD_PUT:
+            case BuzzRequestInterface::METHOD_DELETE:
+            case BuzzRequestInterface::METHOD_PATCH:
+            case BuzzRequestInterface::METHOD_OPTIONS:
+                $options[CURLOPT_POSTFIELDS] = $fields = static::getPostFields($request);
+
+                // remove the content-type header
+                if (is_array($fields)) {
+                    $options[CURLOPT_HTTPHEADER] = array_filter($options[CURLOPT_HTTPHEADER], function($header) {
+                        return 0 !== stripos($header, 'Content-Type: ');
+                    });
+                }
+
+                break;
+        }
+
+        curl_setopt_array($curl, $options);
+    }
+
+    /**
+     * Returns a value for the CURLOPT_POSTFIELDS option.
+     *
+     * @param RequestInterface $request A request object
+     *
+     * @return string|array A post fields value
+     */
+    private static function getPostFields(RequestInterface $request)
+    {
+        if (!$request instanceof FormRequestInterface) {
+            return $request->getBody()->__toString();
+        }
+
+        // TODO move this code to request converter... I think...
+        $fields = $request->getFields();
+        $multipart = false;
+
+        foreach ($fields as $name => $value) {
+            if (!$value instanceof FormUploadInterface) {
+                continue;
+            }
+
+            if (!$file = $value->getFile()) {
+                return $request->getContent();
+            }
+
+            $multipart = true;
+
+            if (version_compare(PHP_VERSION, '5.5', '>=')) {
+                $curlFile = new \CURLFile($file);
+                if ($contentType = $value->getContentType()) {
+                    $curlFile->setMimeType($contentType);
+                }
+
+                if (basename($file) != $value->getFilename()) {
+                    $curlFile->setPostFilename($value->getFilename());
+                }
+
+                $fields[$name] = $curlFile;
+            } else {
+                // replace value with upload string
+                $fields[$name] = '@'.$file;
+
+                if ($contentType = $value->getContentType()) {
+                    $fields[$name] .= ';type='.$contentType;
+                }
+                if (basename($file) != $value->getFilename()) {
+                    $fields[$name] .= ';filename='.$value->getFilename();
+                }
+            }
+        }
+
+        return $multipart ? $fields : http_build_query($fields, '', '&');
+    }
+
+    /**
+     * A helper for getting the last set of headers.
+     *
+     * @param string $raw A string of many header chunks
+     *
+     * @return array An array of header lines
+     */
+    private static function getLastHeaders($raw)
+    {
+        $headers = array();
+        foreach (preg_split('/(\\r?\\n)/', $raw) as $header) {
+            if ($header) {
+                $headers[] = $header;
+            } else {
+                $headers = array();
+            }
+        }
+
+        return $headers;
+    }
+
+    /**
+     * Stashes a cURL option to be set on send, when the resource is created.
+     *
+     * If the supplied value it set to null the option will be removed.
+     *
+     * @param integer $option The option
+     * @param mixed   $value  The value
+     *
+     * @see curl_setopt()
+     */
+    public function setOption($option, $value)
+    {
+        if (null === $value) {
+            unset($this->options[$option]);
+        } else {
+            $this->options[$option] = $value;
+        }
+    }
+
+    /**
+     * Prepares a cURL resource to send a request.
+     *
+     * @param $curl
+     * @param RequestInterface $request
+     * @param array $options
+     */
+    protected function prepare($curl, $request, array $options = array())
+    {
+        $request = RequestConverter::psr7($request);
+        static::setOptionsFromRequest($curl, $request);
+
+        // apply settings from client
+        if ($this->getTimeout() < 1) {
+            curl_setopt($curl, CURLOPT_TIMEOUT_MS, $this->getTimeout() * 1000);
+        } else {
+            curl_setopt($curl, CURLOPT_TIMEOUT, $this->getTimeout());
+        }
+
+        if ($this->proxy) {
+            curl_setopt($curl, CURLOPT_PROXY, $this->proxy);
+        }
+
+        $canFollow = !ini_get('safe_mode') && !ini_get('open_basedir');
+
+        curl_setopt($curl, CURLOPT_FOLLOWLOCATION, $canFollow && $this->getMaxRedirects() > 0);
+        curl_setopt($curl, CURLOPT_MAXREDIRS, $canFollow ? $this->getMaxRedirects() : 0);
+        curl_setopt($curl, CURLOPT_FAILONERROR, !$this->getIgnoreErrors());
+        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $this->getVerifyPeer());
+        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, $this->getVerifyHost());
+
+        // apply additional options
+        curl_setopt_array($curl, $options + $this->options);
+    }
+}

+ 52 - 0
api/vendor/kriswallsmith/buzz/lib/Buzz/Client/AbstractStream.php

@@ -0,0 +1,52 @@
+<?php
+
+namespace Buzz\Client;
+
+use Buzz\Converter\HeaderConverter;
+use Buzz\Converter\RequestConverter;
+use Buzz\Message\RequestInterface;
+use Psr\Http\Message\RequestInterface as Psr7RequestInterface;
+
+abstract class AbstractStream extends AbstractClient
+{
+    /**
+     * Converts a request into an array for stream_context_create().
+     *
+     * @param Psr7RequestInterface|RequestInterface $request A request object
+     *
+     * @return array An array for stream_context_create()
+     */
+    public function getStreamContextArray($request)
+    {
+        $request = RequestConverter::psr7($request);
+
+        $headers = $request->getHeaders();
+        unset($headers['Host']);
+        $options = array(
+            'http' => array(
+                // values from the request
+                'method'           => $request->getMethod(),
+                'header'           => implode("\r\n", HeaderConverter::toBuzzHeaders($headers)),
+                'content'          => $request->getBody()->__toString(),
+                'protocol_version' => $request->getProtocolVersion(),
+
+                // values from the current client
+                'ignore_errors'    => $this->getIgnoreErrors(),
+                'follow_location'  => $this->getMaxRedirects() > 0,
+                'max_redirects'    => $this->getMaxRedirects() + 1,
+                'timeout'          => $this->getTimeout(),
+            ),
+            'ssl' => array(
+                'verify_peer'      => $this->getVerifyPeer(),
+                'verify_host'      => $this->getVerifyHost(),
+            ),
+        );
+
+        if ($this->proxy) {
+            $options['http']['proxy'] = $this->proxy;
+            $options['http']['request_fulluri'] = true;
+        }
+
+        return $options;
+    }
+}

+ 27 - 0
api/vendor/kriswallsmith/buzz/lib/Buzz/Client/BatchClientInterface.php

@@ -0,0 +1,27 @@
+<?php
+
+namespace Buzz\Client;
+
+use Buzz\Exception\ClientException;
+
+/**
+ * A client capable of running batches of requests.
+ *
+ * The Countable implementation should return the number of queued requests.
+ */
+interface BatchClientInterface extends ClientInterface, \Countable
+{
+    /**
+     * Processes all queued requests.
+     *
+     * @throws ClientException If something goes wrong
+     */
+    public function flush();
+
+    /**
+     * Processes zero or more queued requests.
+     *
+     * @throws ClientException If something goes wrong
+     */
+    public function proceed();
+}

+ 20 - 0
api/vendor/kriswallsmith/buzz/lib/Buzz/Client/ClientInterface.php

@@ -0,0 +1,20 @@
+<?php
+
+namespace Buzz\Client;
+
+use Buzz\Exception\ClientException;
+use Buzz\Message\MessageInterface;
+use Buzz\Message\RequestInterface;
+
+interface ClientInterface
+{
+    /**
+     * Populates the supplied response with the response for the supplied request.
+     *
+     * @param RequestInterface $request  A request object
+     * @param MessageInterface $response A response object
+     *
+     * @throws ClientException If something goes wrong
+     */
+    public function send(RequestInterface $request, MessageInterface $response);
+}

+ 90 - 0
api/vendor/kriswallsmith/buzz/lib/Buzz/Client/Curl.php

@@ -0,0 +1,90 @@
+<?php
+
+namespace Buzz\Client;
+
+use Buzz\Converter\RequestConverter;
+use Buzz\Converter\ResponseConverter;
+use Buzz\Exception\RequestException;
+use Buzz\Message\MessageInterface;
+use Buzz\Message\RequestInterface;
+use Buzz\Exception\LogicException;
+use GuzzleHttp\Psr7\Response;
+use Psr\Http\Message\RequestInterface as PSR7RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+
+class Curl extends AbstractCurl
+{
+    private $lastCurl;
+
+    /**
+     * @param RequestInterface $request
+     * @param MessageInterface $response
+     * @param array $options
+     *
+     * @deprecated Will be removed in 1.0. Use sendRequest instead.
+     */
+    public function send(RequestInterface $request, MessageInterface $response, array $options = array())
+    {
+        @trigger_error('Curl::send() is deprecated. Use Curl::sendRequest instead.', E_USER_DEPRECATED);
+        $request = RequestConverter::psr7($request);
+        ResponseConverter::copy(ResponseConverter::buzz($this->sendRequest($request, $options)), $response);
+    }
+
+    /**
+     * @param PSR7RequestInterface $request
+     * @param array $options
+     *
+     * @return ResponseInterface
+     */
+    public function sendRequest(PSR7RequestInterface $request, $options = [])
+    {
+        if (is_resource($this->lastCurl)) {
+            curl_close($this->lastCurl);
+        }
+
+        $this->lastCurl = static::createCurlHandle();
+        $this->prepare($this->lastCurl, $request, $options);
+
+        $data = curl_exec($this->lastCurl);
+
+        if (false === $data) {
+            $errorMsg = curl_error($this->lastCurl);
+            $errorNo  = curl_errno($this->lastCurl);
+
+            $e = new RequestException($errorMsg, $errorNo);
+            $e->setRequest($request);
+
+            throw $e;
+        }
+
+        return $this->createResponse($this->lastCurl, $data);
+    }
+
+    /**
+     * Introspects the last cURL request.
+     *
+     * @param int $opt
+     *
+     * @return string|array
+     * @throws LogicException
+     *
+     * @see curl_getinfo()
+     *
+     * @throws LogicException If there is no cURL resource
+     */
+    public function getInfo($opt = 0)
+    {
+        if (!is_resource($this->lastCurl)) {
+            throw new LogicException('There is no cURL resource');
+        }
+
+        return 0 === $opt ? curl_getinfo($this->lastCurl) : curl_getinfo($this->lastCurl, $opt);
+    }
+
+    public function __destruct()
+    {
+        if (is_resource($this->lastCurl)) {
+            curl_close($this->lastCurl);
+        }
+    }
+}

+ 69 - 0
api/vendor/kriswallsmith/buzz/lib/Buzz/Client/FileGetContents.php

@@ -0,0 +1,69 @@
+<?php
+
+namespace Buzz\Client;
+
+use Buzz\Converter\RequestConverter;
+use Buzz\Converter\ResponseConverter;
+use Buzz\Exception\RequestException;
+use Buzz\Message\MessageInterface;
+use Buzz\Message\RequestInterface;
+use Psr\Http\Message\RequestInterface as PSR7RequestInterface;
+use Buzz\Exception\ClientException;
+
+class FileGetContents extends AbstractStream
+{
+    /**
+     * @see ClientInterface
+     *
+     * @throws ClientException If file_get_contents() fires an error
+     *
+     * @deprecated Will be removed in 1.0. Use sendRequest instead.
+     */
+    public function send(RequestInterface $request, MessageInterface $response)
+    {
+        @trigger_error('FileGetContents::send() is deprecated. FileGetContents Curl::sendRequest instead.', E_USER_DEPRECATED);
+        $request = RequestConverter::psr7($request);
+        ResponseConverter::copy(ResponseConverter::buzz($this->sendRequest($request)), $response);
+    }
+
+    /**
+     * @param PSR7RequestInterface $request
+     * @return \Psr\Http\Message\ResponseInterface
+     */
+    public function sendRequest(PSR7RequestInterface $request)
+    {
+        $context = stream_context_create($this->getStreamContextArray($request));
+
+        $level = error_reporting(0);
+        $content = file_get_contents($request->getUri()->__toString(), 0, $context);
+        error_reporting($level);
+        if (false === $content) {
+            $error = error_get_last();
+            $e = new RequestException($error['message']);
+            $e->setRequest($request);
+
+            throw $e;
+        }
+
+        // TODO rewrite to not use Buzz reponse
+        $response = new \Buzz\Message\Response();
+        $response->setHeaders($this->filterHeaders((array) $http_response_header));
+        $response->setContent($content);
+
+        return ResponseConverter::psr7($response);
+    }
+
+    private function filterHeaders(array $headers)
+    {
+        $filtered = array();
+        foreach ($headers as $header) {
+            if (0 === stripos($header, 'http/')) {
+                $filtered = array();
+            }
+
+            $filtered[] = $header;
+        }
+
+        return $filtered;
+    }
+}

+ 140 - 0
api/vendor/kriswallsmith/buzz/lib/Buzz/Client/MultiCurl.php

@@ -0,0 +1,140 @@
+<?php
+
+namespace Buzz\Client;
+
+use Buzz\Converter\RequestConverter;
+use Buzz\Converter\ResponseConverter;
+use Buzz\Exception\RequestException;
+use Buzz\Message\MessageInterface;
+use Buzz\Message\RequestInterface;
+use Buzz\Exception\ClientException;
+use Psr\Http\Message\RequestInterface as PSR7RequestInterface;
+
+class MultiCurl extends AbstractCurl implements BatchClientInterface
+{
+    private $queue = array();
+    private $curlm;
+
+    /**
+     * Populates the supplied response with the response for the supplied request.
+     *
+     * The array of options will be passed to curl_setopt_array().
+     *
+     * If a "callback" option is supplied, its value will be called when the
+     * request completes. The callable should have the following signature:
+     *
+     *     $callback = function($client, $request, $response, $options, $error) {
+     *         if (!$error) {
+     *             // success
+     *         } else {
+     *             // error ($error is one of the CURLE_* constants)
+     *         }
+     *     };
+     *
+     * @param RequestInterface $request  A request object
+     * @param MessageInterface $response A response object
+     * @param array            $options  An array of options
+     *
+     * @deprecated Will be removed in 1.0. Use sendRequest instead.
+     */
+    public function send(RequestInterface $request, MessageInterface $response, array $options = array())
+    {
+        @trigger_error('MultiCurl::send() is deprecated. Use MultiCurl::sendRequest instead.', E_USER_DEPRECATED);
+
+        $request = RequestConverter::psr7($request);
+        $this->queue[] = array($request, $response, $options);
+    }
+
+    public function sendRequest(PSR7RequestInterface $request, $options = [])
+    {
+        $this->queue[] = array($request, null, $options);
+    }
+
+    public function count()
+    {
+        return count($this->queue);
+    }
+
+    public function flush()
+    {
+        while ($this->queue) {
+            $this->proceed();
+        }
+    }
+
+    public function proceed()
+    {
+        if (!$this->queue) {
+            return;
+        }
+
+        if (!$this->curlm && false === $this->curlm = curl_multi_init()) {
+            throw new ClientException('Unable to create a new cURL multi handle');
+        }
+
+        foreach (array_keys($this->queue) as $i) {
+            if (3 == count($this->queue[$i])) {
+                // prepare curl handle
+                list($request, , $options) = $this->queue[$i];
+                $curl = static::createCurlHandle();
+
+                // remove custom option
+                unset($options['callback']);
+
+                $this->prepare($curl, $request, $options);
+                $this->queue[$i][] = $curl;
+                curl_multi_add_handle($this->curlm, $curl);
+            }
+        }
+
+        // process outstanding perform
+        $active = null;
+        do {
+            $mrc = curl_multi_exec($this->curlm, $active);
+        } while ($active && CURLM_CALL_MULTI_PERFORM == $mrc);
+
+        // handle any completed requests
+        while ($done = curl_multi_info_read($this->curlm)) {
+            foreach (array_keys($this->queue) as $i) {
+                list($request, $response, $options, $curl) = $this->queue[$i];
+
+                if ($curl !== $done['handle']) {
+                    continue;
+                }
+
+                // populate the response object
+                if (CURLE_OK === $done['result']) {
+                    $psr7Response = $this->createResponse($curl, curl_multi_getcontent($curl));
+                    ResponseConverter::copy(ResponseConverter::buzz($psr7Response), $response);
+                } else if (!isset($e)) {
+                    $errorMsg = curl_error($curl);
+                    $errorNo  = curl_errno($curl);
+
+                    $e = new RequestException($errorMsg, $errorNo);
+                    $e->setRequest($request);
+                }
+
+                // remove from queue
+                curl_multi_remove_handle($this->curlm, $curl);
+                curl_close($curl);
+                unset($this->queue[$i]);
+
+                // callback
+                if (isset($options['callback'])) {
+                    $returnResponse = isset($options['psr7_response']) && $options['psr7_response'] === true ? $psr7Response : $response;
+                    call_user_func($options['callback'], $this, $request, $returnResponse, $options, $done['result']);
+                }
+            }
+        }
+
+        // cleanup
+        if (!$this->queue) {
+            curl_multi_close($this->curlm);
+            $this->curlm = null;
+        }
+
+        if (isset($e)) {
+            throw $e;
+        }
+    }
+}

+ 60 - 0
api/vendor/kriswallsmith/buzz/lib/Buzz/Converter/HeaderConverter.php

@@ -0,0 +1,60 @@
+<?php
+
+namespace Buzz\Converter;
+
+/**
+ * Convert between Buzz style:
+ * array(
+ *   'foo: bar',
+ *   'baz: biz',
+ * )
+ *
+ * and PSR style:
+ * array(
+ *   'foo' => 'bar'
+ *   'baz' => ['biz', 'buz'],
+ * )
+ *
+ * @author Tobias Nyholm <tobias.nyholm@gmail.com>
+ */
+class HeaderConverter
+{
+    /**
+     * Convert from Buzz style headers to PSR style
+     * @param array $headers
+     *
+     * @return array
+     */
+    public static function toBuzzHeaders(array $headers)
+    {
+        $buzz = [];
+
+        foreach ($headers as $key => $values) {
+            if (!is_array($values)) {
+                $buzz[] = sprintf('%s: %s', $key, $values);
+            } else {
+                foreach ($values as $value) {
+                    $buzz[] = sprintf('%s: %s', $key, $value);
+                }
+            }
+        }
+
+        return $buzz;
+    }
+
+    /**
+     * Convert from PSR style headers to Buzz style
+     * @param array $headers
+     * @return array
+     */
+    public static function toPsrHeaders(array $headers)
+    {
+        $psr = [];
+        foreach ($headers as $header) {
+            list($key, $value) = explode(':', $header, 2);
+            $psr[trim($key)][] = trim($value);
+        }
+
+        return $psr;
+    }
+}

+ 64 - 0
api/vendor/kriswallsmith/buzz/lib/Buzz/Converter/RequestConverter.php

@@ -0,0 +1,64 @@
+<?php
+
+namespace Buzz\Converter;
+
+use Buzz\Exception\LogicException;
+use Buzz\Message\Request;
+use Buzz\Message\RequestInterface;
+use Psr\Http\Message\RequestInterface as Psr7RequestInterface;
+
+/**
+ *
+ *
+ * @author Tobias Nyholm <tobias.nyholm@gmail.com>
+ */
+class RequestConverter
+{
+    /**
+     * @param Psr7RequestInterface|RequestInterface $request
+     * @return Psr7RequestInterface
+     */
+    public static function psr7($request)
+    {
+        if ($request instanceof Psr7RequestInterface) {
+            return $request;
+        } elseif (!$request instanceof RequestInterface) {
+            throw new LogicException('Only instances of PSR7 Request and Buzz requests are allowed');
+        }
+
+        // Convert the response to psr7
+        return new \GuzzleHttp\Psr7\Request(
+            $request->getMethod(),
+            sprintf('%s%s', $request->getHost(), $request->getResource()),
+            HeaderConverter::toPsrHeaders($request->getHeaders()),
+            $request->getContent(),
+            $request->getProtocolVersion()
+        );
+    }
+    /**
+     * @param Psr7RequestInterface|RequestInterface $request
+     * @return RequestInterface
+     */
+    public static function buzz($request)
+    {
+        if ($request instanceof RequestInterface) {
+            return $request;
+        } elseif (!$request instanceof Psr7RequestInterface) {
+            throw new LogicException('Only instances of PSR7 Request and Buzz requests are allowed');
+        }
+
+        // Convert the response to buzz response
+        $uri = $request->getUri();
+        $buzzRequest = new Request(
+            $request->getMethod(),
+            sprintf('%s?%s', $uri->getPath(), $uri->getQuery()),
+            $uri->getScheme().'://'.$uri->getHost().($uri->getPort() !== null ? $uri->getPort() : '')
+        );
+
+        $buzzRequest->addHeaders(HeaderConverter::toBuzzHeaders($request->getHeaders()));
+        $buzzRequest->setContent($request->getBody()->__toString());
+        $buzzRequest->setProtocolVersion($request->getProtocolVersion());
+
+        return $buzzRequest;
+    }
+}

+ 74 - 0
api/vendor/kriswallsmith/buzz/lib/Buzz/Converter/ResponseConverter.php

@@ -0,0 +1,74 @@
+<?php
+
+namespace Buzz\Converter;
+
+use Buzz\Exception\LogicException;
+use Buzz\Message\Response;
+use Psr\Http\Message\ResponseInterface;
+
+/**
+ * @author Tobias Nyholm <tobias.nyholm@gmail.com>
+ */
+class ResponseConverter
+{
+    /**
+     * @param ResponseInterface|Response $response
+     * @return ResponseInterface
+     */
+    public static function psr7($response)
+    {
+        if ($response instanceof ResponseInterface) {
+            return $response;
+        } elseif (!$response instanceof Response) {
+            throw new LogicException('Only instances of PSR7 Response and Buzz responses are allowed');
+        }
+
+        // Convert the response to psr7
+        $headers = $response->getHeaders();
+        // Remove status line
+        array_shift($headers);
+
+        return new \GuzzleHttp\Psr7\Response(
+            $response->getStatusCode(),
+            HeaderConverter::toPsrHeaders($headers),
+            $response->getContent(),
+            (string) $response->getProtocolVersion(),
+            $response->getReasonPhrase()
+        );
+    }
+
+    /**
+     * @param ResponseInterface|Response $response
+     * @return Response
+     */
+    public static function buzz($response)
+    {
+        if ($response instanceof Response) {
+            return $response;
+        } elseif (!$response instanceof ResponseInterface) {
+            throw new LogicException('Only instances of PSR7 Response and Buzz responses are allowed');
+        }
+
+        // Convert the response to buzz response
+        $headers =HeaderConverter::toBuzzHeaders($response->getHeaders());
+        array_unshift($headers, sprintf('HTTP/%s %d %s', $response->getProtocolVersion(), $response->getStatusCode(), $response->getReasonPhrase()));
+
+        $buzzResponse = new Response();
+        $buzzResponse->addHeaders($headers);
+        $buzzResponse->setContent($response->getBody()->__toString());
+
+        return $buzzResponse;
+    }
+
+    /**
+     * Copy one buzz request to another buzz request.
+     *
+     * @param Response $from
+     * @param Response $to
+     */
+    public static function copy(Response $from, Response $to)
+    {
+        $to->setHeaders($from->getHeaders());
+        $to->setContent($from->getContent());
+    }
+}

+ 10 - 0
api/vendor/kriswallsmith/buzz/lib/Buzz/Exception/ClientException.php

@@ -0,0 +1,10 @@
+<?php
+
+namespace Buzz\Exception;
+
+/**
+ * Thrown whenever a client process fails.
+ */
+class ClientException extends RuntimeException
+{
+}

+ 10 - 0
api/vendor/kriswallsmith/buzz/lib/Buzz/Exception/ExceptionInterface.php

@@ -0,0 +1,10 @@
+<?php
+
+namespace Buzz\Exception;
+
+/**
+ * Marker interface to denote exceptions thrown from the Buzz context.
+ */
+interface ExceptionInterface
+{
+}

+ 10 - 0
api/vendor/kriswallsmith/buzz/lib/Buzz/Exception/InvalidArgumentException.php

@@ -0,0 +1,10 @@
+<?php
+
+namespace Buzz\Exception;
+
+/**
+ * Thrown when an invalid argument is provided.
+ */
+class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
+{
+}

+ 10 - 0
api/vendor/kriswallsmith/buzz/lib/Buzz/Exception/LogicException.php

@@ -0,0 +1,10 @@
+<?php
+
+namespace Buzz\Exception;
+
+/**
+ * Thrown whenever a required call-flow is not respected.
+ */
+class LogicException extends \LogicException implements ExceptionInterface
+{
+}

+ 33 - 0
api/vendor/kriswallsmith/buzz/lib/Buzz/Exception/RequestException.php

@@ -0,0 +1,33 @@
+<?php
+
+namespace Buzz\Exception;
+
+use Buzz\Message\RequestInterface;
+use Psr\Http\Message\RequestInterface as Psr7RequestInterface;
+
+class RequestException extends ClientException
+{
+    /**
+     * Request object
+     *
+     * @var RequestInterface|Psr7RequestInterface
+     */
+    private $request;
+
+    /**
+     * @return RequestInterface|Psr7RequestInterface
+     */
+    public function getRequest()
+    {
+        return $this->request;
+    }
+
+    /**
+     * @param RequestInterface|Psr7RequestInterface $request
+     */
+    public function setRequest($request)
+    {
+        $this->request = $request;
+    }
+
+}

+ 7 - 0
api/vendor/kriswallsmith/buzz/lib/Buzz/Exception/RuntimeException.php

@@ -0,0 +1,7 @@
+<?php
+
+namespace Buzz\Exception;
+
+class RuntimeException extends \RuntimeException implements ExceptionInterface
+{
+}

+ 27 - 0
api/vendor/kriswallsmith/buzz/lib/Buzz/Listener/BasicAuthListener.php

@@ -0,0 +1,27 @@
+<?php
+
+namespace Buzz\Listener;
+
+use Buzz\Message\MessageInterface;
+use Buzz\Message\RequestInterface;
+
+class BasicAuthListener implements ListenerInterface
+{
+    private $username;
+    private $password;
+
+    public function __construct($username, $password)
+    {
+        $this->username = $username;
+        $this->password = $password;
+    }
+
+    public function preSend(RequestInterface $request)
+    {
+        $request->addHeader('Authorization: Basic '.base64_encode($this->username.':'.$this->password));
+    }
+
+    public function postSend(RequestInterface $request, MessageInterface $response)
+    {
+    }
+}

+ 30 - 0
api/vendor/kriswallsmith/buzz/lib/Buzz/Listener/BearerAuthListener.php

@@ -0,0 +1,30 @@
+<?php
+
+namespace Buzz\Listener;
+
+use Buzz\Exception\InvalidArgumentException;
+use Buzz\Message\MessageInterface;
+use Buzz\Message\RequestInterface;
+
+class BearerAuthListener implements ListenerInterface
+{
+    private $accessToken;
+
+    public function __construct($accessToken)
+    {
+        if ($accessToken === null || $accessToken === '') {
+            throw new InvalidArgumentException('You must supply a non empty accessToken');
+        }
+
+        $this->accessToken = $accessToken;
+    }
+
+    public function preSend(RequestInterface $request)
+    {
+        $request->addHeader(sprintf('Authorization: Bearer %s', $this->accessToken));
+    }
+
+    public function postSend(RequestInterface $request, MessageInterface $response)
+    {
+    }
+}

+ 49 - 0
api/vendor/kriswallsmith/buzz/lib/Buzz/Listener/CallbackListener.php

@@ -0,0 +1,49 @@
+<?php
+
+namespace Buzz\Listener;
+
+use Buzz\Message\MessageInterface;
+use Buzz\Message\RequestInterface;
+use Buzz\Exception\InvalidArgumentException;
+
+class CallbackListener implements ListenerInterface
+{
+    private $callable;
+
+    /**
+     * Constructor.
+     *
+     * The callback should expect either one or two arguments, depending on
+     * whether it is receiving a pre or post send notification.
+     *
+     *     $listener = new CallbackListener(function($request, $response = null) {
+     *         if ($response) {
+     *             // postSend
+     *         } else {
+     *             // preSend
+     *         }
+     *     });
+     *
+     * @param mixed $callable A PHP callable
+     *
+     * @throws InvalidArgumentException If the argument is not callable
+     */
+    public function __construct($callable)
+    {
+        if (!is_callable($callable)) {
+            throw new InvalidArgumentException('The argument is not callable.');
+        }
+
+        $this->callable = $callable;
+    }
+
+    public function preSend(RequestInterface $request)
+    {
+        call_user_func($this->callable, $request);
+    }
+
+    public function postSend(RequestInterface $request, MessageInterface $response)
+    {
+        call_user_func($this->callable, $request, $response);
+    }
+}

+ 50 - 0
api/vendor/kriswallsmith/buzz/lib/Buzz/Listener/CookieListener.php

@@ -0,0 +1,50 @@
+<?php
+
+namespace Buzz\Listener;
+
+use Buzz\Message\RequestInterface;
+use Buzz\Message\MessageInterface;
+
+use Buzz\Util\Cookie;
+use Buzz\Util\CookieJar;
+
+class CookieListener implements ListenerInterface
+{
+    private $cookieJar;
+
+    public function __construct()
+    {
+        $this->cookieJar = new CookieJar();
+    }
+
+    public function setCookies($cookies)
+    {
+        $this->cookieJar->setCookies($cookies);
+    }
+
+    public function getCookies()
+    {
+        return $this->cookieJar->getCookies();
+    }
+
+    /**
+     * Adds a cookie to the current cookie jar.
+     *
+     * @param Cookie $cookie A cookie object
+     */
+    public function addCookie(Cookie $cookie)
+    {
+        $this->cookieJar->addCookie($cookie);
+    }
+
+    public function preSend(RequestInterface $request)
+    {
+        $this->cookieJar->clearExpiredCookies();
+        $this->cookieJar->addCookieHeaders($request);
+    }
+
+    public function postSend(RequestInterface $request, MessageInterface $response)
+    {
+        $this->cookieJar->processSetCookieHeaders($request, $response);
+    }
+}

+ 837 - 0
api/vendor/kriswallsmith/buzz/lib/Buzz/Listener/DigestAuthListener.php

@@ -0,0 +1,837 @@
+<?php
+
+namespace Buzz\Listener;
+
+use Buzz\Message\MessageInterface;
+use Buzz\Message\RequestInterface;
+
+class DigestAuthListener implements ListenerInterface
+{
+    private $username;
+    private $password;
+    private $realm;
+
+    private $algorithm;
+    private $authenticationMethod;
+    private $clientNonce;
+    private $domain;
+    private $entityBody;
+    private $method;
+    private $nonce;
+    private $nonceCount;
+    private $opaque;
+    private $uri;
+
+    /**
+     * QOP options: Only one of the following can be set at any time. setOptions will throw an exception otherwise.
+     * OPTION_QOP_AUTH_INT       - Always use auth-int   (if available)
+     * OPTION_QOP_AUTH           - Always use auth       (even if auth-int available)
+     */
+    const OPTION_QOP_AUTH_INT             = 1;
+    const OPTION_QOP_AUTH                 = 2;
+    /**
+     * Ignore server request to downgrade authentication from Digest to Basic.
+     * Breaks RFC compatibility, but ensures passwords are never sent using base64 which is trivial for an attacker to decode.
+     */
+    const OPTION_IGNORE_DOWNGRADE_REQUEST = 4;
+    /**
+     * Discard Client Nonce on each request.
+     */
+    const OPTION_DISCARD_CLIENT_NONCE     = 8;
+
+    private $options;
+
+    /**
+     * Set OPTION_QOP_BEST_AVAILABLE and OPTION_DISCARD_CLIENT_NONCE by default.
+     */
+    public function __construct($username = null, $password = null, $realm = null)
+    {
+        $this->setUsername($username);
+        $this->setPassword($password);
+        $this->setRealm($realm);
+        $this->setOptions(DigestAuthListener::OPTION_QOP_AUTH_INT & DigestAuthListener::OPTION_DISCARD_CLIENT_NONCE);
+    }
+
+    /**
+     * Passes the returned server headers to parseServerHeaders() to check if any authentication variables need to be set.
+     * Inteprets the returned status code and attempts authentication if status is 401 (Authentication Required) by resending
+     * the last request with an Authentication header.
+     *
+     * @param RequestInterface $request  A request object
+     * @param MessageInterface $response A response object
+     */
+    public function postSend(RequestInterface $request, MessageInterface $response)
+    {
+        $statusCode = $response->getStatusCode();
+        $this->parseServerHeaders($response->getHeaders(), $statusCode);
+    }
+
+    /**
+     * Populates uri, method and entityBody used to generate the Authentication header using the specified request object.
+     * Appends the Authentication header if it is present and has been able to be calculated.
+     *
+     * @param RequestInterface $request  A request object
+     */
+    public function preSend(RequestInterface $request)
+    {
+        $this->setUri($request->getResource());
+        $this->setMethod($request->getMethod());
+        $this->setEntityBody($request->getContent());
+
+        $header = $this->getHeader();
+        if($header) {
+            $request->addHeader($header);
+        }
+    }
+
+    /**
+     * Sets the password to be used to authenticate the client.
+     *
+     * @param string $password The password
+     *
+     * @return void
+     */
+    public function setPassword($password)
+    {
+        $this->password = $password;
+    }
+
+    /**
+     * Sets the realm to be used to authenticate the client.
+     *
+     * @param string $realm The realm
+     *
+     * @return void
+     */
+    public function setRealm($realm)
+    {
+        $this->realm = $realm;
+    }
+
+    /**
+     * Sets the username to be used to authenticate the client.
+     *
+     * @param string $username The username
+     *
+     * @return void
+     */
+    public function setUsername($username)
+    {
+        $this->username = $username;
+    }
+
+    /**
+     * Sets the options to be used by this class.
+     *
+     * @param int $options A bitmask of the constants defined in this class.
+     *
+     * @return void
+     */
+    public function setOptions($options)
+    {
+        if(($options & DigestAuthListener::OPTION_QOP_AUTH_INT) === true) {
+            if(($options & DigestAuthListener::OPTION_QOP_AUTH) === true) {
+                throw new \InvalidArgumentException('DigestAuthListener: Only one value of OPTION_QOP_AUTH_INT or OPTION_QOP_AUTH may be set.');
+            }
+            $this->options = $this->options | DigestAuthListener::OPTION_QOP_AUTH_INT;
+        } else {
+            if(($options & DigestAuthListener::OPTION_QOP_AUTH) === true) {
+                $this->options = $this->options | DigestAuthListener::OPTION_QOP_AUTH;
+            }
+        }
+
+        if(($options & DigestAuthListener::OPTION_IGNORE_DOWNGRADE_REQUEST) === true) {
+            $this->options = $this->options | DigestAuthListener::OPTION_IGNORE_DOWNGRADE_REQUEST;
+        }
+
+        if(($options & DigestAuthListener::OPTION_DISCARD_CLIENT_NONCE) === true) {
+            $this->options = $this->options | DigestAuthListener::OPTION_DISCARD_CLIENT_NONCE;
+        }
+    }
+
+    /**
+     * Discards the Client Nonce forcing the generation of a new Client Nonce on the next request.
+     *
+     * @return void
+     */
+    private function discardClientNonce()
+    {
+        $this->clientNonce = null;
+    }
+
+    /**
+     * Returns the hashing algorithm to be used to generate the digest value. Currently only returns MD5.
+     *
+     * @return string The hashing algorithm to be used.
+     */
+    private function getAlgorithm()
+    {
+        if($this->algorithm == null) {
+            $this->algorithm = 'MD5';
+        }
+        return $this->algorithm;
+    }
+
+    /**
+     * Returns the authentication method requested by the server.
+     * If OPTION_IGNORE_DOWNGRADE_REQUEST is set this will always return "Digest"
+     *
+     * @return string Returns either "Digest" or "Basic".
+     */
+    private function getAuthenticationMethod()
+    {
+        if(($this->options & DigestAuthListener::OPTION_IGNORE_DOWNGRADE_REQUEST) === true) {
+            return "Digest";
+        }
+        return $this->authenticationMethod;
+    }
+
+    /**
+     * Returns either the current value of clientNonce or generates a new value if clientNonce is null.
+     * Also increments nonceCount.
+     *
+     * @return string Returns either the current value of clientNonce the newly generated clientNonce;
+     */
+    private function getClientNonce()
+    {
+        if($this->clientNonce == null) {
+            $this->clientNonce = uniqid();
+
+            if($this->nonceCount == null) {
+// If nonceCount is not set then set it to 00000001.
+                $this->nonceCount = '00000001';
+            } else {
+// If it is set then increment it.
+                $this->nonceCount++;
+// Ensure nonceCount is zero-padded at the start of the string to a length of 8
+                while(strlen($this->nonceCount) < 8) {
+                    $this->nonceCount = '0' . $this->nonceCount;
+                }
+            }
+        }
+        return $this->clientNonce;
+    }
+
+    /**
+     * Returns a space separated list of uris that the server nonce can be used to generate an authentication response against.
+     *
+     * @return string Space separated list of uris.
+     */
+    private function getDomain()
+    {
+        return $this->domain;
+    }
+
+    /**
+     * Returns the entity body of the current request.
+     * The entity body is the request before it has been encoded with the content-encoding and minus the request headers.
+     *
+     * @return string The full entity-body.
+     */
+    private function getEntityBody()
+    {
+        return (string)$this->entityBody;
+    }
+
+    /**
+     * Calculates the value of HA1 according to RFC 2617 and RFC 2069.
+     *
+     * @return string The value of HA1
+     */
+    private function getHA1()
+    {
+        $username = $this->getUsername();
+        $password = $this->getPassword();
+        $realm = $this->getRealm();
+
+        if(($username) AND ($password) AND ($realm)) {
+            $algorithm = $this->getAlgorithm();
+
+            if(!isset($algorithm) OR ($algorithm == "MD5")) {
+
+                $A1 = "{$username}:{$realm}:{$password}";
+            }
+            if($this->algorithm == "MD5-sess") {
+
+                $nonce = $this->getNonce();
+                $cnonce = $this->getClientNonce();
+                if(($nonce) AND ($cnonce)) {
+                    $A1 = $this->hash("{$username}:{$realm}:{$password}") . ":{$nonce}:{$cnonce}";              
+                }
+            }
+            if(isset($A1)) {
+                $HA1 = $this->hash($A1);
+                return $HA1;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Calculates the value of HA2 according to RFC 2617 and RFC 2069.
+     *
+     * @return string The value of HA2
+     */
+    private function getHA2()
+    {
+        $method = $this->getMethod();
+        $uri = $this->getUri();
+
+        if(($method) AND ($uri)) {
+            $qop = $this->getQOP();
+
+            if(!isset($qop) OR ($qop == 'auth')) {
+                $A2 = "{$method}:{$uri}";
+            }
+            if($qop == 'auth-int') {
+                $entityBody = $this->getEntityBody();
+                $A2 = "{$method}:{$uri}:" . $this->hash($entityBody);
+            }
+
+            if(isset($A2)) {
+                $HA2 = $this->hash($A2);
+                return $HA2;
+            }           
+        }
+        return null;
+    }
+
+    /**
+     * Returns the full Authentication header for use in authenticating the client with either Digest or Basic authentication.
+     *
+     * @return string The Authentication header to be sent to the server.
+     */
+    private function getHeader()
+    {
+        if($this->getAuthenticationMethod() == 'Digest') {
+            $username = $this->getUsername();
+            $realm = $this->getRealm();
+            $nonce = $this->getNonce();
+            $response = $this->getResponse();
+            if(($username) AND ($realm) AND ($nonce) AND ($response)) {
+                $uri = $this->getUri();
+                $opaque = $this->getOpaque();
+                $domain = $this->getDomain();
+                $qop = $this->getQOP();
+
+                $header = "Authorization: Digest";
+                $header .= " username=\"" . $username . "\",";
+                $header .= " realm=\"" . $realm . "\",";
+                $header .= " nonce=\"" . $nonce . "\",";
+                $header .= " response=\"" . $response . "\",";
+
+                if($uri) {
+                    $header .= " uri=\"" . $uri . "\",";
+                }
+                if($opaque) {
+                    $header .= " opaque=\"" . $opaque . "\",";
+                }
+
+                if($qop) {
+                    $header .= " qop=" . $qop . ",";
+                
+                    $cnonce = $this->getClientNonce();
+                    $nc = $this->getNonceCount();
+
+                    if($cnonce) {
+                        $header .= " nc=" . $nc . ",";
+                    }
+                    if($cnonce) {
+                        $header .= " cnonce=\"" . $cnonce . "\",";
+                    }
+                }
+
+// Remove the last comma from the header
+                $header = substr($header, 0, strlen($header) - 1);
+// Discard the Client Nonce if OPTION_DISCARD_CLIENT_NONCE is set.
+                if(($this->options & DigestAuthListener::OPTION_DISCARD_CLIENT_NONCE) === true) {
+                    $this->discardClientNonce();
+                }
+                return $header;
+            }
+        }
+        if($this->getAuthenticationMethod() == 'Basic') {
+            $username = $this->getUsername();
+            $password = $this->getPassword();
+            if(($username) AND ($password)) {
+                $header = 'Authorization: Basic ' . base64_encode("{$username}:{$password}");
+                return $header;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns the HTTP method used in the current request.
+     *
+     * @return string One of GET,POST,PUT,DELETE or HEAD.
+     */
+    private function getMethod()
+    {
+        return $this->method;
+    }
+
+    /**
+     * Returns the value of nonce we have received in the server headers.
+     *
+     * @return string The value of the server nonce.
+     */
+    private function getNonce()
+    {
+        return $this->nonce;
+    }
+
+    /**
+     * Returns the current nonce counter for the client nonce.
+     *
+     * @return string An eight digit zero-padded string which reflects the number of times the clientNonce has been generated.
+     */
+    private function getNonceCount()
+    {
+        return $this->nonceCount;
+    }
+
+    /**
+     * Returns the opaque value that was sent to us from the server.
+     *
+     * @return string The value of opaque.
+     */
+    private function getOpaque()
+    {
+        return $this->opaque;
+    }
+
+    /**
+     * Returns the plaintext password for the client.
+     *
+     * @return string The value of password.
+     */
+    private function getPassword()
+    {
+        return $this->password;
+    }
+
+    /**
+     * Returns either the realm specified by the client, or the realm specified by the server.
+     * If the server set the value of realm then anything set by our client is overwritten.
+     *
+     * @return string The value of realm.
+     */
+    private function getRealm()
+    {
+        return $this->realm;
+    }
+
+    /**
+     * Calculates the value of response according to RFC 2617 and RFC 2069.
+     *
+     * @return string The value of response
+     */
+    private function getResponse()
+    {
+        $HA1 = $this->getHA1();
+        $nonce = $this->getNonce();
+        $HA2 = $this->getHA2();
+
+        if(($HA1) AND ($nonce) AND ($HA2)) {
+            $qop = $this->getQOP();
+
+            if(!isset($qop)) {
+                $response = $this->hash("{$HA1}:{$nonce}:{$HA2}");
+                return $response;
+            } else {
+                $cnonce = $this->getClientNonce();
+                $nc = $this->getNonceCount();
+                if(($cnonce) AND ($nc)) {
+                    $response = $this->hash("{$HA1}:{$nonce}:{$nc}:{$cnonce}:{$qop}:{$HA2}");
+                    return $response;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns the Quality of Protection to be used when authenticating with the server.
+     *
+     * @return string This will either be auth-int or auth.
+     */
+    private function getQOP()
+    {
+// Has the server specified any options for Quality of Protection
+        if(isset($this->qop) AND count($this->qop)) {
+            if(($this->options & DigestAuthListener::OPTION_QOP_AUTH_INT) === true) {
+                if(in_array('auth-int', $this->qop)) {
+                    return 'auth-int';
+                }
+                if(in_array('auth', $this->qop)) {
+                    return 'auth';
+                }
+            }
+            if(($this->options & DigestAuthListener::OPTION_QOP_AUTH) === true) {
+                if(in_array('auth', $this->qop)) {
+                    return 'auth';
+                }
+                if(in_array('auth-int', $this->qop)) {
+                    return 'auth-int';
+                }
+            }            
+        }
+// Server has not specified any value for Quality of Protection so return null
+        return null;
+    }
+
+    /**
+     * Returns the username set by the client to authenticate with the server.
+     *
+     * @return string The value of username
+     */
+    private function getUsername()
+    {
+        return $this->username;
+    }
+
+    /**
+     * Returns the uri that we are requesting access to.
+     *
+     * @return string The value of uri
+     */
+    private function getUri()
+    {
+        return $this->uri;
+    }
+
+    /**
+     * Calculates the hash for a given value using the algorithm specified by the server.
+     *
+     * @param string $value The value to be hashed
+     *
+     * @return string The hashed value.
+     */
+    private function hash($value)
+    {
+        $algorithm = $this->getAlgorithm();
+        if(($algorithm == 'MD5') OR ($algorithm == 'MD5-sess')) {
+            return hash('md5', $value);
+        }
+        return null;
+    }
+
+    /**
+     * Parses the Authentication-Info header received from the server and calls the relevant setter method on each variable received.
+     *
+     * @param string $authenticationInfo The full Authentication-Info header.
+     *
+     * @return void
+     */
+    private function parseAuthenticationInfoHeader($authenticationInfo)
+    {
+// Remove "Authentication-Info: " from start of header
+        $wwwAuthenticate = substr($wwwAuthenticate, 21, strlen($wwwAuthenticate) - 21);
+
+        $nameValuePairs = $this->parseNameValuePairs($wwwAuthenticate);
+        foreach($nameValuePairs as $name => $value) {
+            switch($name) {
+                case 'message-qop':
+
+                break;
+                case 'nextnonce':
+// This function needs to only set the Nonce once the rspauth has been verified.
+                    $this->setNonce($value);
+                break;
+                case 'rspauth':
+// Check server rspauth value
+                break;
+            }
+        }
+    }
+
+    /**
+     * Parses a string of name=value pairs separated by commas and returns and array with the name as the index.
+     *
+     * @param string $nameValuePairs The string containing the name=value pairs.
+     *
+     * @return array An array with the name used as the index and the values stored within.
+     */
+    private function parseNameValuePairs($nameValuePairs)
+    {
+        $parsedNameValuePairs = array();
+        $nameValuePairs = explode(',', $nameValuePairs);
+        foreach($nameValuePairs as $nameValuePair) {
+// Trim the Whitespace from the start and end of the name value pair string
+            $nameValuePair = trim($nameValuePair);
+// Split $nameValuePair (name=value) into $name and $value
+            list($name, $value) = explode('=', $nameValuePair, 2);
+// Remove quotes if the string is quoted
+            $value = $this->unquoteString($value);
+// Add pair to array[name] => value
+            $parsedNameValuePairs[$name] = $value;
+        }
+        return $parsedNameValuePairs;
+    }
+
+    /**
+     * Parses the server headers received and checks for WWW-Authenticate and Authentication-Info headers.
+     * Calls parseWwwAuthenticateHeader() and parseAuthenticationInfoHeader() respectively if either of these headers are present.
+     *
+     * @param array $headers An array of the headers received by the client.
+     *
+     * @return void
+     */
+    private function parseServerHeaders(array $headers)
+    {
+        foreach($headers as $header) {
+// Check to see if the WWW-Authenticate header is present and if so set $authHeader
+            if(strtolower(substr($header, 0, 18)) == 'www-authenticate: ') {
+                $wwwAuthenticate = $header;
+                $this->parseWwwAuthenticateHeader($wwwAuthenticate);
+            }
+// Check to see if the Authentication-Info header is present and if so set $authInfo
+            if(strtolower(substr($header, 0, 21)) == 'authentication-info: ') {
+                $authenticationInfo = $header;
+                $this->parseAuthenticationInfoHeader($wwwAuthenticate);
+            }
+        }
+    }
+
+    /**
+     * Parses the WWW-Authenticate header received from the server and calls the relevant setter method on each variable received.
+     *
+     * @param string $wwwAuthenticate The full WWW-Authenticate header.
+     *
+     * @return void
+     */
+    private function parseWwwAuthenticateHeader($wwwAuthenticate)
+    {
+// Remove "WWW-Authenticate: " from start of header
+        $wwwAuthenticate = substr($wwwAuthenticate, 18, strlen($wwwAuthenticate) - 18);
+        if(substr($wwwAuthenticate, 0, 7) == 'Digest ') {
+            $this->setAuthenticationMethod('Digest');
+// Remove "Digest " from start of header
+            $wwwAuthenticate = substr($wwwAuthenticate, 7, strlen($wwwAuthenticate) - 7);
+
+            $nameValuePairs = $this->parseNameValuePairs($wwwAuthenticate);
+
+            foreach($nameValuePairs as $name => $value) {
+                switch($name) {
+                    case 'algorithm':
+                        $this->setAlgorithm($value);
+                    break;
+                    case 'domain':
+                        $this->setDomain($value);
+                    break;
+                    case 'nonce':
+                        $this->setNonce($value);
+                    break;
+                    case 'realm':
+                        $this->setRealm($value);
+                    break;
+                    case 'opaque':
+                        $this->setOpaque($value);
+                    break;
+                    case 'qop':
+                        $this->setQOP(explode(',', $value));
+                    break;
+                }
+            }
+        }
+        if (substr($wwwAuthenticate, 0, 6) == 'Basic ') {
+            $this->setAuthenticationMethod('Basic');
+// Remove "Basic " from start of header
+            $wwwAuthenticate = substr($wwwAuthenticate, 6, strlen($wwwAuthenticate) - 6);
+
+            $nameValuePairs = $this->parseNameValuePairs($wwwAuthenticate);
+
+            foreach($nameValuePairs as $name => $value) {
+                switch($name) {
+                    case 'realm':
+                        $this->setRealm($value);
+                    break;
+                }
+            }
+        }
+    }
+
+    /**
+     * Sets the hashing algorithm to be used. Currently only uses MD5 specified by either MD5 or MD5-sess.
+     * RFCs are currently in draft stage for the proposal of SHA-256 and SHA-512-256.
+     * Support will be added once the RFC leaves the draft stage.
+     *
+     * @param string $algorithm The algorithm the server has requested to use.
+     *
+     * @throws \InvalidArgumentException If $algorithm is set to anything other than MD5 or MD5-sess.
+     *
+     * @return void
+     */
+    private function setAlgorithm($algorithm)
+    {
+        if(($algorithm == 'MD5') OR ($algorithm == 'MD5-sess')) {
+            $this->algorithm = $algorithm;
+        } else {
+            throw new \InvalidArgumentException('DigestAuthListener: Only MD5 and MD5-sess algorithms are currently supported.');
+        }
+    }
+
+    /**
+     * Sets authentication method to be used. Options are "Digest" and "Basic".
+     * If the server and the client are unable to authenticate using Digest then the RFCs state that the server should attempt
+     * to authenticate the client using Basic authentication. This ensures that we adhere to that behaviour.
+     * This does however create the possibilty of a downgrade attack so it may be an idea to add a way of disabling this functionality
+     * as Basic authentication is trivial to decrypt and exposes the username/password to a man-in-the-middle attack.
+     *
+     * @param string $authenticationMethod The authentication method requested by the server.
+     *
+     * @throws \InvalidArgumentException If $authenticationMethod is set to anything other than Digest or Basic
+     *
+     * @return void
+     */
+    private function setAuthenticationMethod($authenticationMethod)
+    {
+        if(($authenticationMethod == 'Digest') OR ($authenticationMethod == 'Basic')) {
+            $this->authenticationMethod = $authenticationMethod;
+        } else {
+            throw new \InvalidArgumentException('DigestAuthListener: Only Digest and Basic authentication methods are currently supported.');
+        }
+    }
+
+    /**
+     * Sets the domain to be authenticated against. THIS IS NOT TO BE CONFUSED WITH THE HOSTNAME/DOMAIN.
+     * This is specified by the RFC to be a list of uris separated by spaces that the client will be allowed to access.
+     * An RFC in draft stage is proposing the removal of this functionality, it does not seem to be in widespread use.
+     *
+     * @param string $domain The list of uris separated by spaces that the client will be able to access upon successful authentication.
+     *
+     * @return void
+     */
+    private function setDomain($value)
+    {
+        $this->domain = $value;
+    }
+
+    /**
+     * Sets the Entity Body of the Request for use with qop=auth-int
+     *
+     * @param string $entityBody The body of the entity (The unencoded request minus the headers).
+     *
+     * @return void
+     */
+    private function setEntityBody($entityBody = null)
+    {
+        $this->entityBody = $entityBody;
+    }
+
+    /**
+     * Sets the HTTP method being used for the request
+     *
+     * @param string $method The HTTP method
+     *
+     * @throws \InvalidArgumentException If $method is set to anything other than GET,POST,PUT,DELETE or HEAD.
+     *
+     * @return void
+     */
+    private function setMethod($method = null)
+    {
+        if($method == 'GET') {
+            $this->method = 'GET';
+            return;
+        }
+        if($method == 'POST') {
+            $this->method = 'POST';
+            return;
+        }
+        if($method == 'PUT') {
+            $this->method = 'PUT';
+            return;
+        }
+        if($method == 'DELETE') {
+            $this->method = 'DELETE';
+            return;
+        }
+        if($method == 'HEAD') {
+            $this->method = 'HEAD';
+            return;
+        }
+        throw new \InvalidArgumentException('DigestAuthListener: Only GET,POST,PUT,DELETE,HEAD HTTP methods are currently supported.');
+    }
+
+    /**
+     * Sets the value of nonce
+     *
+     * @param string $opaque The server nonce value
+     *
+     * @return void
+     */
+    private function setNonce($nonce = null)
+    {
+        $this->nonce = $nonce;
+    }
+
+    /**
+     * Sets the value of opaque
+     *
+     * @param string $opaque The opaque value
+     *
+     * @return void
+     */
+    private function setOpaque($opaque)
+    {
+        $this->opaque = $opaque;
+    }
+
+    /**
+     * Sets the acceptable value(s) for the quality of protection used by the server. Supported values are auth and auth-int.
+     * TODO: This method should give precedence to using qop=auth-int first as this offers integrity protection.
+     *
+     * @param array $qop An array with the values of qop that the server has specified it will accept.
+     *
+     * @throws \InvalidArgumentException If $qop contains any values other than auth-int or auth.
+     *
+     * @return void
+     */
+    private function setQOP(array $qop = array())
+    {
+        $this->qop = array();
+        foreach($qop as $protection) {
+            $protection = trim($protection);
+            if($protection == 'auth-int') {
+                $this->qop[] = 'auth-int';
+            } elseif($protection == 'auth') {
+                $this->qop[] = 'auth';
+            } else {
+                throw new \InvalidArgumentException('DigestAuthListener: Only auth-int and auth are supported Quality of Protection mechanisms.');
+            }
+        }
+    }
+
+    /**
+     * Sets the value of uri
+     *
+     * @param string $uri The uri
+     *
+     * @return void
+     */
+    private function setUri($uri = null)
+    {
+        $this->uri = $uri;
+    }
+
+    /**
+     * If a string contains quotation marks at either end this function will strip them. Otherwise it will remain unchanged.
+     *
+     * @param string $str The string to be stripped of quotation marks.
+     *
+     * @return string Returns the original string without the quotation marks at either end.
+     */
+    private function unquoteString($str = null)
+    {
+        if($str) {
+            if(substr($str, 0, 1) == '"') {
+                $str = substr($str, 1, strlen($str) - 1);
+            }
+            if(substr($str, strlen($str) - 1, 1) == '"') {
+                $str = substr($str, 0, strlen($str) - 1);
+            }
+        }
+        return $str;
+    }
+}

+ 42 - 0
api/vendor/kriswallsmith/buzz/lib/Buzz/Listener/History/Entry.php

@@ -0,0 +1,42 @@
+<?php
+
+namespace Buzz\Listener\History;
+
+use Buzz\Message\MessageInterface;
+use Buzz\Message\RequestInterface;
+
+class Entry
+{
+    private $request;
+    private $response;
+    private $duration;
+
+    /**
+     * Constructor.
+     *
+     * @param RequestInterface $request  The request
+     * @param MessageInterface $response The response
+     * @param integer          $duration The duration in seconds
+     */
+    public function __construct(RequestInterface $request, MessageInterface $response, $duration = null)
+    {
+        $this->request = $request;
+        $this->response = $response;
+        $this->duration = $duration;
+    }
+
+    public function getRequest()
+    {
+        return $this->request;
+    }
+
+    public function getResponse()
+    {
+        return $this->response;
+    }
+
+    public function getDuration()
+    {
+        return $this->duration;
+    }
+}

+ 76 - 0
api/vendor/kriswallsmith/buzz/lib/Buzz/Listener/History/Journal.php

@@ -0,0 +1,76 @@
+<?php
+
+namespace Buzz\Listener\History;
+
+use Buzz\Message\MessageInterface;
+use Buzz\Message\RequestInterface;
+
+class Journal implements \Countable, \IteratorAggregate
+{
+    private $entries = array();
+    private $limit = 10;
+
+    /**
+     * Records an entry in the journal.
+     *
+     * @param RequestInterface $request  The request
+     * @param MessageInterface $response The response
+     * @param integer          $duration The duration in seconds
+     */
+    public function record(RequestInterface $request, MessageInterface $response, $duration = null)
+    {
+        $this->addEntry(new Entry($request, $response, $duration));
+    }
+
+    public function addEntry(Entry $entry)
+    {
+        array_push($this->entries, $entry);
+        $this->entries = array_slice($this->entries, $this->getLimit() * -1);
+        end($this->entries);
+    }
+
+    public function getEntries()
+    {
+        return $this->entries;
+    }
+
+    public function getLast()
+    {
+        return end($this->entries);
+    }
+
+    public function getLastRequest()
+    {
+        return $this->getLast()->getRequest();
+    }
+
+    public function getLastResponse()
+    {
+        return $this->getLast()->getResponse();
+    }
+
+    public function clear()
+    {
+        $this->entries = array();
+    }
+
+    public function count()
+    {
+        return count($this->entries);
+    }
+
+    public function setLimit($limit)
+    {
+        $this->limit = $limit;
+    }
+
+    public function getLimit()
+    {
+        return $this->limit;
+    }
+
+    public function getIterator()
+    {
+        return new \ArrayIterator(array_reverse($this->entries));
+    }
+}

+ 33 - 0
api/vendor/kriswallsmith/buzz/lib/Buzz/Listener/HistoryListener.php

@@ -0,0 +1,33 @@
+<?php
+
+namespace Buzz\Listener;
+
+use Buzz\Listener\History\Journal;
+use Buzz\Message\MessageInterface;
+use Buzz\Message\RequestInterface;
+
+class HistoryListener implements ListenerInterface
+{
+    private $journal;
+    private $startTime;
+
+    public function __construct(Journal $journal)
+    {
+        $this->journal = $journal;
+    }
+
+    public function getJournal()
+    {
+        return $this->journal;
+    }
+
+    public function preSend(RequestInterface $request)
+    {
+        $this->startTime = microtime(true);
+    }
+
+    public function postSend(RequestInterface $request, MessageInterface $response)
+    {
+        $this->journal->record($request, $response, microtime(true) - $this->startTime);
+    }
+}

+ 41 - 0
api/vendor/kriswallsmith/buzz/lib/Buzz/Listener/ListenerChain.php

@@ -0,0 +1,41 @@
+<?php
+
+namespace Buzz\Listener;
+
+use Buzz\Message\MessageInterface;
+use Buzz\Message\RequestInterface;
+
+class ListenerChain implements ListenerInterface
+{
+    /** @var ListenerInterface[] */
+    private $listeners;
+
+    public function __construct(array $listeners = array())
+    {
+        $this->listeners = $listeners;
+    }
+
+    public function addListener(ListenerInterface $listener)
+    {
+        $this->listeners[] = $listener;
+    }
+
+    public function getListeners()
+    {
+        return $this->listeners;
+    }
+
+    public function preSend(RequestInterface $request)
+    {
+        foreach ($this->listeners as $listener) {
+            $listener->preSend($request);
+        }
+    }
+
+    public function postSend(RequestInterface $request, MessageInterface $response)
+    {
+        foreach ($this->listeners as $listener) {
+            $listener->postSend($request, $response);
+        }
+    }
+}

+ 12 - 0
api/vendor/kriswallsmith/buzz/lib/Buzz/Listener/ListenerInterface.php

@@ -0,0 +1,12 @@
+<?php
+
+namespace Buzz\Listener;
+
+use Buzz\Message\MessageInterface;
+use Buzz\Message\RequestInterface;
+
+interface ListenerInterface
+{
+    public function preSend(RequestInterface $request);
+    public function postSend(RequestInterface $request, MessageInterface $response);
+}

Algunos archivos no se mostraron porque demasiados archivos cambiaron en este cambio