diff --git a/app/Client/Client.php b/app/Client/Client.php index e4829c8..0aef2aa 100644 --- a/app/Client/Client.php +++ b/app/Client/Client.php @@ -49,6 +49,11 @@ class Client } } + public function sharePort(int $port) + { + $this->connectToServerAndShareTcp($port, config('expose.auth_token')); + } + protected function prepareSharedUrl(string $sharedUrl): string { if (! $parsedUrl = parse_url($sharedUrl)) { @@ -87,14 +92,12 @@ class Client $clientConnection->on('close', function () use ($sharedUrl, $subdomain, $authToken) { $this->logger->error('Connection to server closed.'); - $this->retryConnectionOrExit($sharedUrl, $subdomain, $authToken); + $this->retryConnectionOrExit(function () use ($sharedUrl, $subdomain, $authToken) { + $this->connectToServer($sharedUrl, $subdomain, $authToken); + }); }); - $connection->on('authenticationFailed', function ($data) use ($deferred) { - $this->logger->error($data->message); - - $this->exit($deferred); - }); + $this->attachCommonConnectionListeners($connection, $deferred); $connection->on('subdomainTaken', function ($data) use ($deferred) { $this->logger->error($data->message); @@ -102,20 +105,6 @@ class Client $this->exit($deferred); }); - $connection->on('setMaximumConnectionLength', function ($data) { - $timeoutSection = $this->logger->getOutput()->section(); - - $this->loop->addPeriodicTimer(1, function () use ($data, $timeoutSection) { - $this->timeConnected++; - - $secondsRemaining = $data->length * 60 - $this->timeConnected; - $remaining = Carbon::now()->diff(Carbon::now()->addSeconds($secondsRemaining)); - - $timeoutSection->clear(); - $timeoutSection->writeln('Remaining time: '.$remaining->format('%H:%I:%S')); - }); - }); - $connection->on('authenticated', function ($data) use ($deferred, $sharedUrl) { $httpProtocol = $this->configuration->port() === 443 ? 'https' : 'http'; $host = $this->configuration->host(); @@ -136,7 +125,9 @@ class Client }); }, function (\Exception $e) use ($deferred, $sharedUrl, $subdomain, $authToken) { if ($this->connectionRetries > 0) { - $this->retryConnectionOrExit($sharedUrl, $subdomain, $authToken); + $this->retryConnectionOrExit(function () use ($sharedUrl, $subdomain, $authToken) { + $this->connectToServer($sharedUrl, $subdomain, $authToken); + }); return; } @@ -149,6 +140,84 @@ class Client return $promise; } + public function connectToServerAndShareTcp(int $port, $authToken = ''): PromiseInterface + { + $deferred = new Deferred(); + $promise = $deferred->promise(); + + $wsProtocol = $this->configuration->port() === 443 ? 'wss' : 'ws'; + + connect($wsProtocol."://{$this->configuration->host()}:{$this->configuration->port()}/expose/control?authToken={$authToken}", [], [ + 'X-Expose-Control' => 'enabled', + ], $this->loop) + ->then(function (WebSocket $clientConnection) use ($port, $deferred, $authToken) { + $this->connectionRetries = 0; + + $connection = ControlConnection::create($clientConnection); + + $connection->authenticateTcp($port); + + $this->attachCommonConnectionListeners($connection, $deferred); + + $clientConnection->on('close', function () use ($port, $authToken) { + $this->logger->error('Connection to server closed.'); + + $this->retryConnectionOrExit(function () use ($port, $authToken) { + $this->connectToServerAndShareTcp($port, $authToken); + }); + }); + + $connection->on('authenticated', function ($data) use ($deferred, $port) { + $host = $this->configuration->host(); + + $this->logger->info($data->message); + $this->logger->info("Local-Port:\t\t{$port}"); + $this->logger->info("Shared-Port:\t\t{$data->shared_port}"); + $this->logger->info("Expose-URL:\t\ttcp://{$host}:{$data->shared_port}."); + $this->logger->line(''); + + $deferred->resolve($data); + }); + }, function (\Exception $e) use ($deferred, $port, $authToken) { + if ($this->connectionRetries > 0) { + $this->retryConnectionOrExit(function () use ($port, $authToken) { + $this->connectToServerAndShareTcp($port, $authToken); + }); + + return; + } + $this->logger->error('Could not connect to the server.'); + $this->logger->error($e->getMessage()); + + $this->exit($deferred); + }); + + return $promise; + } + + protected function attachCommonConnectionListeners(ControlConnection $connection, Deferred $deferred) + { + $connection->on('authenticationFailed', function ($data) use ($deferred) { + $this->logger->error($data->message); + + $this->exit($deferred); + }); + + $connection->on('setMaximumConnectionLength', function ($data) { + $timeoutSection = $this->logger->getOutput()->section(); + + $this->loop->addPeriodicTimer(1, function () use ($data, $timeoutSection) { + $this->timeConnected++; + + $secondsRemaining = $data->length * 60 - $this->timeConnected; + $remaining = Carbon::now()->diff(Carbon::now()->addSeconds($secondsRemaining)); + + $timeoutSection->clear(); + $timeoutSection->writeln('Remaining time: '.$remaining->format('%H:%I:%S')); + }); + }); + } + protected function exit(Deferred $deferred) { $deferred->reject(); @@ -158,15 +227,15 @@ class Client }); } - protected function retryConnectionOrExit(string $sharedUrl, $subdomain, $authToken = '') + protected function retryConnectionOrExit(callable $retry) { $this->connectionRetries++; if ($this->connectionRetries <= static::MAX_CONNECTION_RETRIES) { - $this->loop->addTimer($this->connectionRetries, function () use ($sharedUrl, $subdomain, $authToken) { + $this->loop->addTimer($this->connectionRetries, function () use ($retry) { $this->logger->info("Retrying connection ({$this->connectionRetries}/".static::MAX_CONNECTION_RETRIES.')'); - $this->connectToServer($sharedUrl, $subdomain, $authToken); + $retry(); }); } else { exit(1); diff --git a/app/Client/Connections/ControlConnection.php b/app/Client/Connections/ControlConnection.php index 83ecae9..02bc31b 100644 --- a/app/Client/Connections/ControlConnection.php +++ b/app/Client/Connections/ControlConnection.php @@ -52,17 +52,34 @@ class ControlConnection $this->proxyManager->createProxy($this->clientId, $data); } + public function createTcpProxy($data) + { + $this->proxyManager->createTcpProxy($this->clientId, $data); + } + public function authenticate(string $sharedHost, string $subdomain) { $this->socket->send(json_encode([ 'event' => 'authenticate', 'data' => [ + 'type' => 'http', 'host' => $sharedHost, 'subdomain' => empty($subdomain) ? null : $subdomain, ], ])); } + public function authenticateTcp(int $port) + { + $this->socket->send(json_encode([ + 'event' => 'authenticate', + 'data' => [ + 'type' => 'tcp', + 'port' => $port, + ], + ])); + } + public function ping() { $this->socket->send(json_encode([ diff --git a/app/Client/Factory.php b/app/Client/Factory.php index 0363ed3..d391397 100644 --- a/app/Client/Factory.php +++ b/app/Client/Factory.php @@ -109,6 +109,13 @@ class Factory return $this; } + public function sharePort(int $port) + { + app('expose.client')->sharePort($port); + + return $this; + } + protected function addRoutes() { $this->router->get('/', DashboardController::class); diff --git a/app/Client/ProxyManager.php b/app/Client/ProxyManager.php index 2a9a07c..b59664d 100644 --- a/app/Client/ProxyManager.php +++ b/app/Client/ProxyManager.php @@ -5,7 +5,9 @@ namespace App\Client; use App\Client\Http\HttpClient; use function Ratchet\Client\connect; use Ratchet\Client\WebSocket; +use Ratchet\RFC6455\Messaging\Frame; use React\EventLoop\LoopInterface; +use React\Socket\Connector; class ProxyManager { @@ -43,6 +45,37 @@ class ProxyManager }); } + public function createTcpProxy(string $clientId, $connectionData) + { + $protocol = $this->configuration->port() === 443 ? 'wss' : 'ws'; + + connect($protocol."://{$this->configuration->host()}:{$this->configuration->port()}/expose/control", [], [ + 'X-Expose-Control' => 'enabled', + ], $this->loop) + ->then(function (WebSocket $proxyConnection) use ($clientId, $connectionData) { + $connector = new Connector($this->loop); + + $connector->connect('127.0.0.1:'.$connectionData->port)->then(function ($connection) use ($proxyConnection) { + $connection->on('data', function ($data) use ($proxyConnection) { + $binaryMsg = new Frame($data, true, Frame::OP_BINARY); + $proxyConnection->send($binaryMsg); + }); + + $proxyConnection->on('message', function ($message) use ($connection) { + $connection->write($message); + }); + }); + + $proxyConnection->send(json_encode([ + 'event' => 'registerTcpProxy', + 'data' => [ + 'tcp_request_id' => $connectionData->tcp_request_id ?? null, + 'client_id' => $clientId, + ], + ])); + }); + } + protected function performRequest(WebSocket $proxyConnection, $requestId, string $requestData) { app(HttpClient::class)->performRequest((string) $requestData, $proxyConnection, $requestId); diff --git a/app/Commands/SharePortCommand.php b/app/Commands/SharePortCommand.php new file mode 100644 index 0000000..b05e068 --- /dev/null +++ b/app/Commands/SharePortCommand.php @@ -0,0 +1,40 @@ +bind(CliRequestLogger::class, function () { + return new CliRequestLogger(new ConsoleOutput()); + }); + + return $this; + } + + public function handle() + { + $this->configureConnectionLogger(); + + (new Factory()) + ->setLoop(app(LoopInterface::class)) + ->setHost(config('expose.host', 'localhost')) + ->setPort(config('expose.port', 8080)) + ->setAuth($this->option('auth')) + ->createClient() + ->sharePort($this->argument('port')) + ->createHttpServer() + ->run(); + } +} diff --git a/app/Contracts/ConnectionManager.php b/app/Contracts/ConnectionManager.php index 813bdd8..b554a9e 100644 --- a/app/Contracts/ConnectionManager.php +++ b/app/Contracts/ConnectionManager.php @@ -10,6 +10,8 @@ interface ConnectionManager { public function storeConnection(string $host, ?string $subdomain, ConnectionInterface $connection): ControlConnection; + public function storeTcpConnection(int $port, ConnectionInterface $connection): ControlConnection; + public function limitConnectionLength(ControlConnection $connection, int $maximumConnectionLength); public function storeHttpConnection(ConnectionInterface $httpConnection, $requestId): HttpConnection; @@ -25,4 +27,6 @@ interface ConnectionManager public function getConnections(): array; public function getConnectionsForAuthToken(string $authToken): array; + + public function getTcpConnectionsForAuthToken(string $authToken): array; } diff --git a/app/Server/Connections/ConnectionManager.php b/app/Server/Connections/ConnectionManager.php index cb0186d..3978d4d 100644 --- a/app/Server/Connections/ConnectionManager.php +++ b/app/Server/Connections/ConnectionManager.php @@ -5,8 +5,10 @@ namespace App\Server\Connections; use App\Contracts\ConnectionManager as ConnectionManagerContract; use App\Contracts\SubdomainGenerator; use App\Http\QueryParameters; +use App\Server\Exceptions\NoFreePortAvailable; use Ratchet\ConnectionInterface; use React\EventLoop\LoopInterface; +use React\Socket\Server; class ConnectionManager implements ConnectionManagerContract { @@ -60,6 +62,49 @@ class ConnectionManager implements ConnectionManagerContract return $storedConnection; } + public function storeTcpConnection(int $port, ConnectionInterface $connection): ControlConnection + { + $clientId = (string) uniqid(); + + $connection->client_id = $clientId; + + $storedConnection = new TcpControlConnection( + $connection, + $port, + $this->getSharedTcpServer(), + $clientId, + $this->getAuthTokenFromConnection($connection) + ); + + $this->connections[] = $storedConnection; + + return $storedConnection; + } + + protected function getSharedTcpServer(): Server + { + $portRange = config('expose.admin.tcp_port_range'); + + $port = $portRange['from'] ?? 50000; + $maxPort = $portRange['to'] ?? 60000; + + do { + try { + $portFound = true; + $server = new Server('0.0.0.0:'.$port, $this->loop); + } catch (\RuntimeException $exception) { + $portFound = false; + $port++; + + if ($port > $maxPort) { + throw new NoFreePortAvailable(); + } + } + } while (! $portFound); + + return $server; + } + public function storeHttpConnection(ConnectionInterface $httpConnection, $requestId): HttpConnection { $this->httpConnections[$requestId] = new HttpConnection($httpConnection); @@ -82,6 +127,16 @@ class ConnectionManager implements ConnectionManagerContract if (isset($connection->client_id)) { $clientId = $connection->client_id; + + $controlConnection = collect($this->connections)->first(function ($connection) use ($clientId) { + return $connection->client_id == $clientId; + }); + + if ($controlConnection instanceof TcpControlConnection) { + $controlConnection->stop(); + $controlConnection = null; + } + $this->connections = collect($this->connections)->reject(function ($connection) use ($clientId) { return $connection->client_id == $clientId; })->toArray(); @@ -118,9 +173,29 @@ class ConnectionManager implements ConnectionManagerContract ->filter(function ($connection) use ($authToken) { return $connection->authToken === $authToken; }) + ->filter(function ($connection) { + return get_class($connection) === ControlConnection::class; + }) ->map(function ($connection) { return $connection->toArray(); }) + ->values() + ->toArray(); + } + + public function getTcpConnectionsForAuthToken(string $authToken): array + { + return collect($this->connections) + ->filter(function ($connection) use ($authToken) { + return $connection->authToken === $authToken; + }) + ->filter(function ($connection) { + return get_class($connection) === TcpControlConnection::class; + }) + ->map(function ($connection) { + return $connection->toArray(); + }) + ->values() ->toArray(); } } diff --git a/app/Server/Connections/ControlConnection.php b/app/Server/Connections/ControlConnection.php index ef3198f..b95c8a9 100644 --- a/app/Server/Connections/ControlConnection.php +++ b/app/Server/Connections/ControlConnection.php @@ -57,6 +57,7 @@ class ControlConnection public function toArray() { return [ + 'type' => 'http', 'host' => $this->host, 'client_id' => $this->client_id, 'auth_token' => $this->authToken, diff --git a/app/Server/Connections/TcpControlConnection.php b/app/Server/Connections/TcpControlConnection.php new file mode 100644 index 0000000..db5c284 --- /dev/null +++ b/app/Server/Connections/TcpControlConnection.php @@ -0,0 +1,107 @@ +socket = $socket; + $this->client_id = $clientId; + $this->shared_server = $sharedServer; + $this->port = $port; + $this->shared_at = now()->toDateTimeString(); + $this->shared_port = parse_url($sharedServer->getAddress(), PHP_URL_PORT); + $this->authToken = $authToken; + + $this->configureServer($sharedServer); + } + + public function setMaximumConnectionLength(int $maximumConnectionLength) + { + $this->socket->send(json_encode([ + 'event' => 'setMaximumConnectionLength', + 'data' => [ + 'length' => $maximumConnectionLength, + ], + ])); + } + + public function registerProxy($requestId) + { + $this->socket->send(json_encode([ + 'event' => 'createProxy', + 'data' => [ + 'request_id' => $requestId, + 'client_id' => $this->client_id, + ], + ])); + } + + public function registerTcpProxy($requestId) + { + $this->socket->send(json_encode([ + 'event' => 'createTcpProxy', + 'data' => [ + 'port' => $this->port, + 'tcp_request_id' => $requestId, + 'client_id' => $this->client_id, + ], + ])); + } + + public function stop() + { + $this->shared_server->close(); + $this->shared_server = null; + } + + public function close() + { + $this->socket->close(); + } + + public function toArray() + { + return [ + 'type' => 'tcp', + 'port' => $this->port, + 'client_id' => $this->client_id, + 'shared_port' => $this->shared_port, + 'shared_at' => $this->shared_at, + ]; + } + + protected function configureServer(Server $sharedServer) + { + $requestId = uniqid(); + + $sharedServer->on('connection', function (\React\Socket\ConnectionInterface $connection) use ($requestId) { + $this->proxyConnection = $connection; + + $this->once('tcp_proxy_ready_'.$requestId, function (ConnectionInterface $proxy) use ($connection) { + $this->proxy = $proxy; + + $connection->on('data', function ($data) use ($proxy) { + $binaryMsg = new Frame($data, true, Frame::OP_BINARY); + $proxy->send($binaryMsg); + }); + + $connection->resume(); + }); + + $connection->pause(); + $this->registerTcpProxy($requestId); + }); + } +} diff --git a/app/Server/Exceptions/NoFreePortAvailable.php b/app/Server/Exceptions/NoFreePortAvailable.php new file mode 100644 index 0000000..1151b70 --- /dev/null +++ b/app/Server/Exceptions/NoFreePortAvailable.php @@ -0,0 +1,7 @@ +router->get('/users', ListUsersController::class, $adminCondition); $this->router->get('/settings', ShowSettingsController::class, $adminCondition); $this->router->get('/sites', ListSitesController::class, $adminCondition); + $this->router->get('/tcp', ListTcpConnectionsController::class, $adminCondition); $this->router->get('/api/settings', GetSettingsController::class, $adminCondition); $this->router->post('/api/settings', StoreSettingsController::class, $adminCondition); @@ -129,6 +133,8 @@ class Factory $this->router->delete('/api/users/{id}', DeleteUsersController::class, $adminCondition); $this->router->get('/api/sites', GetSitesController::class, $adminCondition); $this->router->delete('/api/sites/{id}', DisconnectSiteController::class, $adminCondition); + $this->router->get('/api/tcp', GetTcpConnectionsController::class, $adminCondition); + $this->router->delete('/api/tcp/{id}', DisconnectTcpConnectionController::class, $adminCondition); } protected function bindConfiguration() diff --git a/app/Server/Http/Controllers/Admin/DisconnectTcpConnectionController.php b/app/Server/Http/Controllers/Admin/DisconnectTcpConnectionController.php new file mode 100644 index 0000000..f371c0c --- /dev/null +++ b/app/Server/Http/Controllers/Admin/DisconnectTcpConnectionController.php @@ -0,0 +1,42 @@ +connectionManager = $connectionManager; + } + + public function handle(Request $request, ConnectionInterface $httpConnection) + { + $connection = $this->connectionManager->findControlConnectionForClientId($request->get('id')); + + if (! is_null($connection)) { + $connection->close(); + + $this->connectionManager->removeControlConnection($connection); + } + + $httpConnection->send(respond_json([ + 'tcp_connections' => collect($this->connectionManager->getConnections()) + ->filter(function ($connection) { + return get_class($connection) === TcpControlConnection::class; + }) + ->values(), + ])); + } +} diff --git a/app/Server/Http/Controllers/Admin/GetSitesController.php b/app/Server/Http/Controllers/Admin/GetSitesController.php index 0a9af71..e7df0f7 100644 --- a/app/Server/Http/Controllers/Admin/GetSitesController.php +++ b/app/Server/Http/Controllers/Admin/GetSitesController.php @@ -4,6 +4,7 @@ namespace App\Server\Http\Controllers\Admin; use App\Contracts\ConnectionManager; use App\Server\Configuration; +use App\Server\Connections\ControlConnection; use Illuminate\Http\Request; use Ratchet\ConnectionInterface; @@ -23,12 +24,16 @@ class GetSitesController extends AdminController { $httpConnection->send( respond_json([ - 'sites' => collect($this->connectionManager->getConnections())->map(function ($site, $siteId) { - $site = $site->toArray(); - $site['id'] = $siteId; + 'sites' => collect($this->connectionManager->getConnections()) + ->filter(function ($connection) { + return get_class($connection) === ControlConnection::class; + }) + ->map(function ($site, $siteId) { + $site = $site->toArray(); + $site['id'] = $siteId; - return $site; - })->values(), + return $site; + })->values(), ]) ); } diff --git a/app/Server/Http/Controllers/Admin/GetTcpConnectionsController.php b/app/Server/Http/Controllers/Admin/GetTcpConnectionsController.php new file mode 100644 index 0000000..5f98546 --- /dev/null +++ b/app/Server/Http/Controllers/Admin/GetTcpConnectionsController.php @@ -0,0 +1,41 @@ +connectionManager = $connectionManager; + } + + public function handle(Request $request, ConnectionInterface $httpConnection) + { + $httpConnection->send( + respond_json([ + 'tcp_connections' => collect($this->connectionManager->getConnections()) + ->filter(function ($connection) { + return get_class($connection) === TcpControlConnection::class; + }) + ->map(function ($site, $siteId) { + $site = $site->toArray(); + $site['id'] = $siteId; + + return $site; + }) + ->values(), + ]) + ); + } +} diff --git a/app/Server/Http/Controllers/Admin/ListSitesController.php b/app/Server/Http/Controllers/Admin/ListSitesController.php index 8e23976..3ca6bcc 100644 --- a/app/Server/Http/Controllers/Admin/ListSitesController.php +++ b/app/Server/Http/Controllers/Admin/ListSitesController.php @@ -4,6 +4,7 @@ namespace App\Server\Http\Controllers\Admin; use App\Contracts\ConnectionManager; use App\Server\Configuration; +use App\Server\Connections\ControlConnection; use Illuminate\Http\Request; use Ratchet\ConnectionInterface; @@ -25,12 +26,17 @@ class ListSitesController extends AdminController $sites = $this->getView($httpConnection, 'server.sites.index', [ 'scheme' => $this->configuration->port() === 443 ? 'https' : 'http', 'configuration' => $this->configuration, - 'sites' => collect($this->connectionManager->getConnections())->map(function ($site, $siteId) { - $site = $site->toArray(); - $site['id'] = $siteId; + 'sites' => collect($this->connectionManager->getConnections()) + ->filter(function ($connection) { + return get_class($connection) === ControlConnection::class; + }) + ->map(function ($site, $siteId) { + $site = $site->toArray(); + $site['id'] = $siteId; - return $site; - })->values(), + return $site; + }) + ->values(), ]); $httpConnection->send( diff --git a/app/Server/Http/Controllers/Admin/ListTcpConnectionsController.php b/app/Server/Http/Controllers/Admin/ListTcpConnectionsController.php new file mode 100644 index 0000000..2d219fe --- /dev/null +++ b/app/Server/Http/Controllers/Admin/ListTcpConnectionsController.php @@ -0,0 +1,46 @@ +connectionManager = $connectionManager; + $this->configuration = $configuration; + } + + public function handle(Request $request, ConnectionInterface $httpConnection) + { + $sites = $this->getView($httpConnection, 'server.tcp.index', [ + 'scheme' => $this->configuration->port() === 443 ? 'https' : 'http', + 'configuration' => $this->configuration, + 'connections' => collect($this->connectionManager->getConnections()) + ->filter(function ($connection) { + return get_class($connection) === TcpControlConnection::class; + }) + ->map(function ($connection, $connectionId) { + $connection = $connection->toArray(); + $connection['id'] = $connectionId; + + return $connection; + }) + ->values(), + ]); + + $httpConnection->send( + respond_html($sites) + ); + } +} diff --git a/app/Server/Http/Controllers/Admin/StoreUsersController.php b/app/Server/Http/Controllers/Admin/StoreUsersController.php index 14e3ff8..ab2986e 100644 --- a/app/Server/Http/Controllers/Admin/StoreUsersController.php +++ b/app/Server/Http/Controllers/Admin/StoreUsersController.php @@ -40,6 +40,7 @@ class StoreUsersController extends AdminController 'name' => $request->get('name'), 'auth_token' => (string) Str::uuid(), 'can_specify_subdomains' => (int) $request->get('can_specify_subdomains'), + 'can_share_tcp_ports' => (int) $request->get('can_share_tcp_ports'), ]; $this->userRepository diff --git a/app/Server/Http/Controllers/ControlMessageController.php b/app/Server/Http/Controllers/ControlMessageController.php index 729a73f..d1bccc2 100644 --- a/app/Server/Http/Controllers/ControlMessageController.php +++ b/app/Server/Http/Controllers/ControlMessageController.php @@ -5,6 +5,7 @@ namespace App\Server\Http\Controllers; use App\Contracts\ConnectionManager; use App\Contracts\UserRepository; use App\Http\QueryParameters; +use App\Server\Exceptions\NoFreePortAvailable; use Ratchet\ConnectionInterface; use Ratchet\WebSocket\MessageComponentInterface; use React\Promise\Deferred; @@ -53,6 +54,10 @@ class ControlMessageController implements MessageComponentInterface if (isset($connection->request_id)) { return $this->sendResponseToHttpConnection($connection->request_id, $msg); } + if (isset($connection->tcp_request_id)) { + $connectionInfo = $this->connectionManager->findControlConnectionForClientId($connection->tcp_client_id); + $connectionInfo->proxyConnection->write($msg); + } try { $payload = json_decode($msg); @@ -77,22 +82,11 @@ class ControlMessageController implements MessageComponentInterface { $this->verifyAuthToken($connection) ->then(function ($user) use ($connection, $data) { - if (! $this->hasValidSubdomain($connection, $data->subdomain, $user)) { - return; + if ($data->type === 'http') { + $this->handleHttpConnection($connection, $data, $user); + } elseif ($data->type === 'tcp') { + $this->handleTcpConnection($connection, $data, $user); } - - $connectionInfo = $this->connectionManager->storeConnection($data->host, $data->subdomain, $connection); - - $this->connectionManager->limitConnectionLength($connectionInfo, config('expose.admin.maximum_connection_length')); - - $connection->send(json_encode([ - 'event' => 'authenticated', - 'data' => [ - 'message' => config('expose.admin.messages.message_of_the_day'), - 'subdomain' => $connectionInfo->subdomain, - 'client_id' => $connectionInfo->client_id, - ], - ])); }, function () use ($connection) { $connection->send(json_encode([ 'event' => 'authenticationFailed', @@ -104,6 +98,57 @@ class ControlMessageController implements MessageComponentInterface }); } + protected function handleHttpConnection(ConnectionInterface $connection, $data, $user = null) + { + if (! $this->hasValidSubdomain($connection, $data->subdomain, $user)) { + return; + } + + $connectionInfo = $this->connectionManager->storeConnection($data->host, $data->subdomain, $connection); + + $this->connectionManager->limitConnectionLength($connectionInfo, config('expose.admin.maximum_connection_length')); + + $connection->send(json_encode([ + 'event' => 'authenticated', + 'data' => [ + 'message' => config('expose.admin.messages.message_of_the_day'), + 'subdomain' => $connectionInfo->subdomain, + 'client_id' => $connectionInfo->client_id, + ], + ])); + } + + protected function handleTcpConnection(ConnectionInterface $connection, $data, $user = null) + { + if (! $this->canShareTcpPorts($connection, $data, $user)) { + return; + } + + try { + $connectionInfo = $this->connectionManager->storeTcpConnection($data->port, $connection); + } catch (NoFreePortAvailable $exception) { + $connection->send(json_encode([ + 'event' => 'authenticationFailed', + 'data' => [ + 'message' => config('expose.admin.messages.no_free_tcp_port_available'), + ], + ])); + $connection->close(); + + return; + } + + $connection->send(json_encode([ + 'event' => 'authenticated', + 'data' => [ + 'message' => config('expose.admin.messages.message_of_the_day'), + 'port' => $connectionInfo->port, + 'shared_port' => $connectionInfo->shared_port, + 'client_id' => $connectionInfo->client_id, + ], + ])); + } + protected function registerProxy(ConnectionInterface $connection, $data) { $connection->request_id = $data->request_id; @@ -115,6 +160,18 @@ class ControlMessageController implements MessageComponentInterface ]); } + protected function registerTcpProxy(ConnectionInterface $connection, $data) + { + $connection->tcp_client_id = $data->client_id; + $connection->tcp_request_id = $data->tcp_request_id; + + $connectionInfo = $this->connectionManager->findControlConnectionForClientId($data->client_id); + + $connectionInfo->emit('tcp_proxy_ready_'.$data->tcp_request_id, [ + $connection, + ]); + } + /** * {@inheritdoc} */ @@ -180,4 +237,21 @@ class ControlMessageController implements MessageComponentInterface return true; } + + protected function canShareTcpPorts(ConnectionInterface $connection, $data, $user) + { + if (! is_null($user) && $user['can_share_tcp_ports'] === 0) { + $connection->send(json_encode([ + 'event' => 'authenticationFailed', + 'data' => [ + 'message' => config('expose.admin.messages.custom_subdomain_unauthorized'), + ], + ])); + $connection->close(); + + return false; + } + + return true; + } } diff --git a/app/Server/UserRepository/DatabaseUserRepository.php b/app/Server/UserRepository/DatabaseUserRepository.php index 43b15b1..d572f17 100644 --- a/app/Server/UserRepository/DatabaseUserRepository.php +++ b/app/Server/UserRepository/DatabaseUserRepository.php @@ -72,6 +72,7 @@ class DatabaseUserRepository implements UserRepository protected function getUserDetails(array $user) { $user['sites'] = $user['auth_token'] !== '' ? $this->connectionManager->getConnectionsForAuthToken($user['auth_token']) : []; + $user['tcp_connections'] = $user['auth_token'] !== '' ? $this->connectionManager->getTcpConnectionsForAuthToken($user['auth_token']) : []; return $user; } @@ -113,8 +114,8 @@ class DatabaseUserRepository implements UserRepository $deferred = new Deferred(); $this->database->query(" - INSERT INTO users (name, auth_token, can_specify_subdomains, created_at) - VALUES (:name, :auth_token, :can_specify_subdomains, DATETIME('now')) + INSERT INTO users (name, auth_token, can_specify_subdomains, can_share_tcp_ports, created_at) + VALUES (:name, :auth_token, :can_specify_subdomains, :can_share_tcp_ports, DATETIME('now')) ", $data) ->then(function (Result $result) use ($deferred) { $this->database->query('SELECT * FROM users WHERE id = :id', ['id' => $result->insertId]) diff --git a/builds/expose b/builds/expose index 7a457c1..e9d7bf6 100755 Binary files a/builds/expose and b/builds/expose differ diff --git a/composer.json b/composer.json index 8ba0004..270f178 100644 --- a/composer.json +++ b/composer.json @@ -38,7 +38,7 @@ "phpunit/phpunit": "^8.5", "ratchet/pawl": "^0.3.4", "react/http": "^0.8.6", - "react/socket": "dev-master as 1.1", + "react/socket": "^1.6", "react/stream": "^1.1.1", "riverline/multipart-parser": "^2.0", "symfony/expression-language": "^5.0", diff --git a/composer.lock b/composer.lock index df609b9..ad2ac1f 100644 --- a/composer.lock +++ b/composer.lock @@ -4,27 +4,27 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "dd987a6f4f036893204c0d006d66001b", + "content-hash": "899c6312c9047b9bb304b318cd0d7ca2", "packages": [], "packages-dev": [ { "name": "cboden/ratchet", - "version": "v0.4.2", + "version": "v0.4.3", "source": { "type": "git", "url": "https://github.com/ratchetphp/Ratchet.git", - "reference": "57721e1f184f9e29378fc5363867c47ddda743fd" + "reference": "466a0ecc83209c75b76645eb823401b5c52e5f21" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ratchetphp/Ratchet/zipball/57721e1f184f9e29378fc5363867c47ddda743fd", - "reference": "57721e1f184f9e29378fc5363867c47ddda743fd", + "url": "https://api.github.com/repos/ratchetphp/Ratchet/zipball/466a0ecc83209c75b76645eb823401b5c52e5f21", + "reference": "466a0ecc83209c75b76645eb823401b5c52e5f21", "shasum": "" }, "require": { "guzzlehttp/psr7": "^1.0", "php": ">=5.4.2", - "ratchet/rfc6455": "^0.2", + "ratchet/rfc6455": "^0.3", "react/socket": "^1.0 || ^0.8 || ^0.7 || ^0.6 || ^0.5", "symfony/http-foundation": "^2.6|^3.0|^4.0|^5.0", "symfony/routing": "^2.6|^3.0|^4.0|^5.0" @@ -47,6 +47,10 @@ "name": "Chris Boden", "email": "cboden@gmail.com", "role": "Developer" + }, + { + "name": "Matt Bonneau", + "role": "Developer" } ], "description": "PHP WebSocket library", @@ -58,20 +62,20 @@ "sockets", "websocket" ], - "time": "2020-01-27T23:08:40+00:00" + "time": "2020-07-07T15:50:14+00:00" }, { "name": "clue/block-react", - "version": "v1.3.1", + "version": "v1.4.0", "source": { "type": "git", "url": "https://github.com/clue/reactphp-block.git", - "reference": "2f516b28259c203d67c4c963772dd7e9db652737" + "reference": "c8e7583ae55127b89d6915480ce295bac81c4f88" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/clue/reactphp-block/zipball/2f516b28259c203d67c4c963772dd7e9db652737", - "reference": "2f516b28259c203d67c4c963772dd7e9db652737", + "url": "https://api.github.com/repos/clue/reactphp-block/zipball/c8e7583ae55127b89d6915480ce295bac81c4f88", + "reference": "c8e7583ae55127b89d6915480ce295bac81c4f88", "shasum": "" }, "require": { @@ -81,7 +85,8 @@ "react/promise-timer": "^1.5" }, "require-dev": { - "phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35" + "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35", + "react/http": "^1.0" }, "type": "library", "autoload": { @@ -96,7 +101,7 @@ "authors": [ { "name": "Christian Lück", - "email": "christian@lueck.tv" + "email": "christian@clue.engineering" } ], "description": "Lightweight library that eases integrating async components built for ReactPHP in a traditional, blocking environment.", @@ -111,20 +116,20 @@ "sleep", "synchronous" ], - "time": "2019-04-09T11:45:04+00:00" + "time": "2020-08-21T14:09:44+00:00" }, { "name": "clue/buzz-react", - "version": "v2.8.2", + "version": "v2.9.0", "source": { "type": "git", "url": "https://github.com/clue/reactphp-buzz.git", - "reference": "65b441d9e914952464558fb2dbd4f57d27640c99" + "reference": "eb180b5e0db00a3febaa458289706b8ca80ffe2a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/clue/reactphp-buzz/zipball/65b441d9e914952464558fb2dbd4f57d27640c99", - "reference": "65b441d9e914952464558fb2dbd4f57d27640c99", + "url": "https://api.github.com/repos/clue/reactphp-buzz/zipball/eb180b5e0db00a3febaa458289706b8ca80ffe2a", + "reference": "eb180b5e0db00a3febaa458289706b8ca80ffe2a", "shasum": "" }, "require": { @@ -133,7 +138,7 @@ "react/event-loop": "^1.0 || ^0.5", "react/http-client": "^0.5.10", "react/promise": "^2.2.1 || ^1.2.1", - "react/promise-stream": "^1.0 || ^0.1.1", + "react/promise-stream": "^1.0 || ^0.1.2", "react/socket": "^1.1", "react/stream": "^1.0 || ^0.7", "ringcentral/psr7": "^1.2" @@ -143,7 +148,7 @@ "clue/http-proxy-react": "^1.3", "clue/reactphp-ssh-proxy": "^1.0", "clue/socks-react": "^1.0", - "phpunit/phpunit": "^7.0 || ^6.4 || ^5.7 || ^4.8.35", + "phpunit/phpunit": "^9.0 || ^5.7 || ^4.8.35", "react/http": "^0.8" }, "type": "library", @@ -171,17 +176,7 @@ "psr-7", "reactphp" ], - "funding": [ - { - "url": "https://clue.engineering/support", - "type": "custom" - }, - { - "url": "https://github.com/clue", - "type": "github" - } - ], - "time": "2020-06-02T09:43:30+00:00" + "time": "2020-07-03T10:43:43+00:00" }, { "name": "clue/ndjson-react", @@ -290,72 +285,6 @@ }, "time": "2020-05-10T03:16:55+00:00" }, - { - "name": "composer/ca-bundle", - "version": "1.2.7", - "source": { - "type": "git", - "url": "https://github.com/composer/ca-bundle.git", - "reference": "95c63ab2117a72f48f5a55da9740a3273d45b7fd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/ca-bundle/zipball/95c63ab2117a72f48f5a55da9740a3273d45b7fd", - "reference": "95c63ab2117a72f48f5a55da9740a3273d45b7fd", - "shasum": "" - }, - "require": { - "ext-openssl": "*", - "ext-pcre": "*", - "php": "^5.3.2 || ^7.0 || ^8.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || 6.5 - 8", - "psr/log": "^1.0", - "symfony/process": "^2.5 || ^3.0 || ^4.0 || ^5.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Composer\\CaBundle\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - } - ], - "description": "Lets you find a path to the system CA bundle, and includes a fallback to the Mozilla CA bundle.", - "keywords": [ - "cabundle", - "cacert", - "certificate", - "ssl", - "tls" - ], - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } - ], - "time": "2020-04-08T08:27:21+00:00" - }, { "name": "container-interop/container-interop", "version": "1.2.0", @@ -463,20 +392,6 @@ "uppercase", "words" ], - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finflector", - "type": "tidelift" - } - ], "time": "2020-05-29T15:13:26+00:00" }, { @@ -533,20 +448,6 @@ "constructor", "instantiate" ], - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", - "type": "tidelift" - } - ], "time": "2020-05-29T17:27:14+00:00" }, { @@ -609,20 +510,6 @@ "parser", "php" ], - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer", - "type": "tidelift" - } - ], "time": "2020-05-25T17:44:05+00:00" }, { @@ -681,16 +568,16 @@ }, { "name": "egulias/email-validator", - "version": "2.1.18", + "version": "2.1.20", "source": { "type": "git", "url": "https://github.com/egulias/EmailValidator.git", - "reference": "cfa3d44471c7f5bfb684ac2b0da7114283d78441" + "reference": "f46887bc48db66c7f38f668eb7d6ae54583617ff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/cfa3d44471c7f5bfb684ac2b0da7114283d78441", - "reference": "cfa3d44471c7f5bfb684ac2b0da7114283d78441", + "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/f46887bc48db66c7f38f668eb7d6ae54583617ff", + "reference": "f46887bc48db66c7f38f668eb7d6ae54583617ff", "shasum": "" }, "require": { @@ -735,7 +622,7 @@ "validation", "validator" ], - "time": "2020-06-16T20:11:17+00:00" + "time": "2020-09-06T13:44:32+00:00" }, { "name": "evenement/evenement", @@ -782,21 +669,26 @@ }, { "name": "facade/ignition-contracts", - "version": "1.0.0", + "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/facade/ignition-contracts.git", - "reference": "f445db0fb86f48e205787b2592840dd9c80ded28" + "reference": "aeab1ce8b68b188a43e81758e750151ad7da796b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/facade/ignition-contracts/zipball/f445db0fb86f48e205787b2592840dd9c80ded28", - "reference": "f445db0fb86f48e205787b2592840dd9c80ded28", + "url": "https://api.github.com/repos/facade/ignition-contracts/zipball/aeab1ce8b68b188a43e81758e750151ad7da796b", + "reference": "aeab1ce8b68b188a43e81758e750151ad7da796b", "shasum": "" }, "require": { "php": "^7.1" }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.14", + "phpunit/phpunit": "^7.5|^8.0", + "vimeo/psalm": "^3.12" + }, "type": "library", "autoload": { "psr-4": { @@ -822,7 +714,7 @@ "flare", "ignition" ], - "time": "2019-08-30T14:06:08+00:00" + "time": "2020-07-14T10:10:28+00:00" }, { "name": "filp/whoops", @@ -1084,20 +976,20 @@ }, { "name": "hamcrest/hamcrest-php", - "version": "v2.0.0", + "version": "v2.0.1", "source": { "type": "git", "url": "https://github.com/hamcrest/hamcrest-php.git", - "reference": "776503d3a8e85d4f9a1148614f95b7a608b046ad" + "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/776503d3a8e85d4f9a1148614f95b7a608b046ad", - "reference": "776503d3a8e85d4f9a1148614f95b7a608b046ad", + "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/8c3d0a3f6af734494ad8f6fbbee0ba92422859f3", + "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3", "shasum": "" }, "require": { - "php": "^5.3|^7.0" + "php": "^5.3|^7.0|^8.0" }, "replace": { "cordoval/hamcrest-php": "*", @@ -1105,14 +997,13 @@ "kodova/hamcrest-php": "*" }, "require-dev": { - "phpunit/php-file-iterator": "1.3.3", - "phpunit/phpunit": "~4.0", - "satooshi/php-coveralls": "^1.0" + "phpunit/php-file-iterator": "^1.4 || ^2.0", + "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "2.1-dev" } }, "autoload": { @@ -1122,26 +1013,26 @@ }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD" + "BSD-3-Clause" ], "description": "This is the PHP port of Hamcrest Matchers", "keywords": [ "test" ], - "time": "2016-01-20T08:20:44+00:00" + "time": "2020-07-09T08:09:16+00:00" }, { "name": "illuminate/cache", - "version": "v7.16.1", + "version": "v7.27.0", "source": { "type": "git", "url": "https://github.com/illuminate/cache.git", - "reference": "107906faad4c45ed31aa0bf36f1b877862d25092" + "reference": "04e0b4c05de0b23874e953bc323835e493ceb234" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/cache/zipball/107906faad4c45ed31aa0bf36f1b877862d25092", - "reference": "107906faad4c45ed31aa0bf36f1b877862d25092", + "url": "https://api.github.com/repos/illuminate/cache/zipball/04e0b4c05de0b23874e953bc323835e493ceb234", + "reference": "04e0b4c05de0b23874e953bc323835e493ceb234", "shasum": "" }, "require": { @@ -1179,20 +1070,20 @@ ], "description": "The Illuminate Cache package.", "homepage": "https://laravel.com", - "time": "2020-05-18T19:54:47+00:00" + "time": "2020-08-13T14:33:41+00:00" }, { "name": "illuminate/config", - "version": "v7.16.1", + "version": "v7.27.0", "source": { "type": "git", "url": "https://github.com/illuminate/config.git", - "reference": "d1e898de7a0cefe9ce2c94dede6679839e6a7b39" + "reference": "0084b12f82e8031d36a55ad28295e65c39420047" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/config/zipball/d1e898de7a0cefe9ce2c94dede6679839e6a7b39", - "reference": "d1e898de7a0cefe9ce2c94dede6679839e6a7b39", + "url": "https://api.github.com/repos/illuminate/config/zipball/0084b12f82e8031d36a55ad28295e65c39420047", + "reference": "0084b12f82e8031d36a55ad28295e65c39420047", "shasum": "" }, "require": { @@ -1223,20 +1114,20 @@ ], "description": "The Illuminate Config package.", "homepage": "https://laravel.com", - "time": "2020-01-07T13:49:44+00:00" + "time": "2020-08-24T13:15:53+00:00" }, { "name": "illuminate/console", - "version": "v7.16.1", + "version": "v7.27.0", "source": { "type": "git", "url": "https://github.com/illuminate/console.git", - "reference": "b6586e349d668c50b8fd3d1253067ca44785d1b0" + "reference": "6f0788320dc7358b032bae5c07697a6e0b3b5898" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/console/zipball/b6586e349d668c50b8fd3d1253067ca44785d1b0", - "reference": "b6586e349d668c50b8fd3d1253067ca44785d1b0", + "url": "https://api.github.com/repos/illuminate/console/zipball/6f0788320dc7358b032bae5c07697a6e0b3b5898", + "reference": "6f0788320dc7358b032bae5c07697a6e0b3b5898", "shasum": "" }, "require": { @@ -1277,20 +1168,20 @@ ], "description": "The Illuminate Console package.", "homepage": "https://laravel.com", - "time": "2020-06-02T22:33:52+00:00" + "time": "2020-08-31T13:37:25+00:00" }, { "name": "illuminate/container", - "version": "v7.16.1", + "version": "v7.27.0", "source": { "type": "git", "url": "https://github.com/illuminate/container.git", - "reference": "aaf1612f558d856975746cd3d0da4f230035ef55" + "reference": "c0d892450f12ed22e4d8f7bec647230d0be8405b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/container/zipball/aaf1612f558d856975746cd3d0da4f230035ef55", - "reference": "aaf1612f558d856975746cd3d0da4f230035ef55", + "url": "https://api.github.com/repos/illuminate/container/zipball/c0d892450f12ed22e4d8f7bec647230d0be8405b", + "reference": "c0d892450f12ed22e4d8f7bec647230d0be8405b", "shasum": "" }, "require": { @@ -1324,20 +1215,20 @@ ], "description": "The Illuminate Container package.", "homepage": "https://laravel.com", - "time": "2020-06-11T14:29:51+00:00" + "time": "2020-08-13T14:33:41+00:00" }, { "name": "illuminate/contracts", - "version": "v7.16.1", + "version": "v7.27.0", "source": { "type": "git", "url": "https://github.com/illuminate/contracts.git", - "reference": "42792e136e3a77312fb89df29373d6221a1f794e" + "reference": "80c27ab89284ad3629ab9000d75feab5c7a1e420" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/contracts/zipball/42792e136e3a77312fb89df29373d6221a1f794e", - "reference": "42792e136e3a77312fb89df29373d6221a1f794e", + "url": "https://api.github.com/repos/illuminate/contracts/zipball/80c27ab89284ad3629ab9000d75feab5c7a1e420", + "reference": "80c27ab89284ad3629ab9000d75feab5c7a1e420", "shasum": "" }, "require": { @@ -1368,20 +1259,20 @@ ], "description": "The Illuminate Contracts package.", "homepage": "https://laravel.com", - "time": "2020-05-13T20:55:30+00:00" + "time": "2020-09-01T13:40:29+00:00" }, { "name": "illuminate/events", - "version": "v7.16.1", + "version": "v7.27.0", "source": { "type": "git", "url": "https://github.com/illuminate/events.git", - "reference": "723bcb4d31b84b6372cfa7007ef491497422cf24" + "reference": "cd6170a6ef48d284a66429a786ce45575abc9c6b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/events/zipball/723bcb4d31b84b6372cfa7007ef491497422cf24", - "reference": "723bcb4d31b84b6372cfa7007ef491497422cf24", + "url": "https://api.github.com/repos/illuminate/events/zipball/cd6170a6ef48d284a66429a786ce45575abc9c6b", + "reference": "cd6170a6ef48d284a66429a786ce45575abc9c6b", "shasum": "" }, "require": { @@ -1413,20 +1304,20 @@ ], "description": "The Illuminate Events package.", "homepage": "https://laravel.com", - "time": "2020-06-16T14:30:08+00:00" + "time": "2020-08-24T13:15:53+00:00" }, { "name": "illuminate/filesystem", - "version": "v7.16.1", + "version": "v7.27.0", "source": { "type": "git", "url": "https://github.com/illuminate/filesystem.git", - "reference": "9ab0b617e80d01a09fefcf596e5b355370e13175" + "reference": "192ceb4d7fe0900bec84258554be0d8d74fa0818" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/filesystem/zipball/9ab0b617e80d01a09fefcf596e5b355370e13175", - "reference": "9ab0b617e80d01a09fefcf596e5b355370e13175", + "url": "https://api.github.com/repos/illuminate/filesystem/zipball/192ceb4d7fe0900bec84258554be0d8d74fa0818", + "reference": "192ceb4d7fe0900bec84258554be0d8d74fa0818", "shasum": "" }, "require": { @@ -1468,20 +1359,20 @@ ], "description": "The Illuminate Filesystem package.", "homepage": "https://laravel.com", - "time": "2020-06-14T09:08:06+00:00" + "time": "2020-08-25T13:29:03+00:00" }, { "name": "illuminate/http", - "version": "v7.16.1", + "version": "v7.27.0", "source": { "type": "git", "url": "https://github.com/illuminate/http.git", - "reference": "906d9e11f350f0d6ec917bcd8ba4066f96d10af7" + "reference": "268c1fcbc049540d27317337888259359086291b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/http/zipball/906d9e11f350f0d6ec917bcd8ba4066f96d10af7", - "reference": "906d9e11f350f0d6ec917bcd8ba4066f96d10af7", + "url": "https://api.github.com/repos/illuminate/http/zipball/268c1fcbc049540d27317337888259359086291b", + "reference": "268c1fcbc049540d27317337888259359086291b", "shasum": "" }, "require": { @@ -1520,20 +1411,20 @@ ], "description": "The Illuminate Http package.", "homepage": "https://laravel.com", - "time": "2020-06-09T15:44:28+00:00" + "time": "2020-08-17T13:28:58+00:00" }, { "name": "illuminate/pipeline", - "version": "v7.16.1", + "version": "v7.27.0", "source": { "type": "git", "url": "https://github.com/illuminate/pipeline.git", - "reference": "1a7ef33dec9cbb73757f0654ae48d997944a4d3f" + "reference": "93fa6f688bd1bb87d837e7cff31f256cdcd4a248" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/pipeline/zipball/1a7ef33dec9cbb73757f0654ae48d997944a4d3f", - "reference": "1a7ef33dec9cbb73757f0654ae48d997944a4d3f", + "url": "https://api.github.com/repos/illuminate/pipeline/zipball/93fa6f688bd1bb87d837e7cff31f256cdcd4a248", + "reference": "93fa6f688bd1bb87d837e7cff31f256cdcd4a248", "shasum": "" }, "require": { @@ -1564,20 +1455,20 @@ ], "description": "The Illuminate Pipeline package.", "homepage": "https://laravel.com", - "time": "2020-02-25T14:26:37+00:00" + "time": "2020-08-24T13:15:53+00:00" }, { "name": "illuminate/session", - "version": "v7.16.1", + "version": "v7.27.0", "source": { "type": "git", "url": "https://github.com/illuminate/session.git", - "reference": "f18c8772d76928e54f65fdb8cbc90b723b9e1e7e" + "reference": "1d426cbcb7ea582474f1f8d47e25f24aa305dfd2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/session/zipball/f18c8772d76928e54f65fdb8cbc90b723b9e1e7e", - "reference": "f18c8772d76928e54f65fdb8cbc90b723b9e1e7e", + "url": "https://api.github.com/repos/illuminate/session/zipball/1d426cbcb7ea582474f1f8d47e25f24aa305dfd2", + "reference": "1d426cbcb7ea582474f1f8d47e25f24aa305dfd2", "shasum": "" }, "require": { @@ -1615,20 +1506,20 @@ ], "description": "The Illuminate Session package.", "homepage": "https://laravel.com", - "time": "2020-06-15T14:06:36+00:00" + "time": "2020-08-26T13:35:34+00:00" }, { "name": "illuminate/support", - "version": "v7.16.1", + "version": "v7.27.0", "source": { "type": "git", "url": "https://github.com/illuminate/support.git", - "reference": "dee189db0aff526393b350d3bfb33d0cc32861c3" + "reference": "8ee8e0e719cda577e2ca5b78766a4b079bf332f8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/support/zipball/dee189db0aff526393b350d3bfb33d0cc32861c3", - "reference": "dee189db0aff526393b350d3bfb33d0cc32861c3", + "url": "https://api.github.com/repos/illuminate/support/zipball/8ee8e0e719cda577e2ca5b78766a4b079bf332f8", + "reference": "8ee8e0e719cda577e2ca5b78766a4b079bf332f8", "shasum": "" }, "require": { @@ -1677,20 +1568,20 @@ ], "description": "The Illuminate Support package.", "homepage": "https://laravel.com", - "time": "2020-06-11T14:29:51+00:00" + "time": "2020-08-28T14:23:23+00:00" }, { "name": "illuminate/testing", - "version": "v7.16.1", + "version": "v7.27.0", "source": { "type": "git", "url": "https://github.com/illuminate/testing.git", - "reference": "444fd70a3c56e5daeee1e3b4c8bf9bf8fa28b2c5" + "reference": "40f991e87810797ec43c34a8411906ca1d3bdaa7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/testing/zipball/444fd70a3c56e5daeee1e3b4c8bf9bf8fa28b2c5", - "reference": "444fd70a3c56e5daeee1e3b4c8bf9bf8fa28b2c5", + "url": "https://api.github.com/repos/illuminate/testing/zipball/40f991e87810797ec43c34a8411906ca1d3bdaa7", + "reference": "40f991e87810797ec43c34a8411906ca1d3bdaa7", "shasum": "" }, "require": { @@ -1729,20 +1620,20 @@ ], "description": "The Illuminate Testing package.", "homepage": "https://laravel.com", - "time": "2020-05-29T09:44:42+00:00" + "time": "2020-08-24T15:38:17+00:00" }, { "name": "illuminate/translation", - "version": "v7.16.1", + "version": "v7.27.0", "source": { "type": "git", "url": "https://github.com/illuminate/translation.git", - "reference": "74c6c0c15efc2a3e1a7e8b893dcbe68007467ea6" + "reference": "2f9c3ffca0b93c83a272da8b4265c9148a5d9973" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/translation/zipball/74c6c0c15efc2a3e1a7e8b893dcbe68007467ea6", - "reference": "74c6c0c15efc2a3e1a7e8b893dcbe68007467ea6", + "url": "https://api.github.com/repos/illuminate/translation/zipball/2f9c3ffca0b93c83a272da8b4265c9148a5d9973", + "reference": "2f9c3ffca0b93c83a272da8b4265c9148a5d9973", "shasum": "" }, "require": { @@ -1775,20 +1666,20 @@ ], "description": "The Illuminate Translation package.", "homepage": "https://laravel.com", - "time": "2020-04-15T20:57:47+00:00" + "time": "2020-08-24T13:15:53+00:00" }, { "name": "illuminate/validation", - "version": "v7.16.1", + "version": "v7.27.0", "source": { "type": "git", "url": "https://github.com/illuminate/validation.git", - "reference": "5d41eb5cba79c691a34b745213626c91cfda5b1c" + "reference": "412aede5b5cb2130ac40254703bd3057b6d95922" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/validation/zipball/5d41eb5cba79c691a34b745213626c91cfda5b1c", - "reference": "5d41eb5cba79c691a34b745213626c91cfda5b1c", + "url": "https://api.github.com/repos/illuminate/validation/zipball/412aede5b5cb2130ac40254703bd3057b6d95922", + "reference": "412aede5b5cb2130ac40254703bd3057b6d95922", "shasum": "" }, "require": { @@ -1828,20 +1719,20 @@ ], "description": "The Illuminate Validation package.", "homepage": "https://laravel.com", - "time": "2020-05-29T13:44:16+00:00" + "time": "2020-08-25T13:44:44+00:00" }, { "name": "jolicode/jolinotif", - "version": "v2.1.0", + "version": "v2.2.0", "source": { "type": "git", "url": "https://github.com/jolicode/JoliNotif.git", - "reference": "b1a7ccfeb13f1daa12a5188531863b739e388e13" + "reference": "52f5b98f964f6009b8ec4c0e951edcd0862e2ac7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/jolicode/JoliNotif/zipball/b1a7ccfeb13f1daa12a5188531863b739e388e13", - "reference": "b1a7ccfeb13f1daa12a5188531863b739e388e13", + "url": "https://api.github.com/repos/jolicode/JoliNotif/zipball/52f5b98f964f6009b8ec4c0e951edcd0862e2ac7", + "reference": "52f5b98f964f6009b8ec4c0e951edcd0862e2ac7", "shasum": "" }, "require": { @@ -1885,7 +1776,7 @@ "notification", "windows" ], - "time": "2020-01-10T15:37:00+00:00" + "time": "2020-06-17T08:25:38+00:00" }, { "name": "laminas/laminas-escaper", @@ -1938,16 +1829,16 @@ }, { "name": "laminas/laminas-http", - "version": "2.11.2", + "version": "2.13.0", "source": { "type": "git", "url": "https://github.com/laminas/laminas-http.git", - "reference": "8c66963b933c80da59433da56a44dfa979f3ec88" + "reference": "33b7942f51ce905ce9bfc8bf28badc501d3904b5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-http/zipball/8c66963b933c80da59433da56a44dfa979f3ec88", - "reference": "8c66963b933c80da59433da56a44dfa979f3ec88", + "url": "https://api.github.com/repos/laminas/laminas-http/zipball/33b7942f51ce905ce9bfc8bf28badc501d3904b5", + "reference": "33b7942f51ce905ce9bfc8bf28badc501d3904b5", "shasum": "" }, "require": { @@ -1959,7 +1850,7 @@ "php": "^5.6 || ^7.0" }, "replace": { - "zendframework/zend-http": "self.version" + "zendframework/zend-http": "^2.11.2" }, "require-dev": { "laminas/laminas-coding-standard": "~1.0.0", @@ -1970,12 +1861,6 @@ "paragonie/certainty": "For automated management of cacert.pem" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.11.x-dev", - "dev-develop": "2.12.x-dev" - } - }, "autoload": { "psr-4": { "Laminas\\Http\\": "src/" @@ -1992,7 +1877,7 @@ "http client", "laminas" ], - "time": "2019-12-31T17:02:36+00:00" + "time": "2020-08-18T17:11:58+00:00" }, { "name": "laminas/laminas-loader", @@ -2045,35 +1930,35 @@ }, { "name": "laminas/laminas-stdlib", - "version": "3.2.1", + "version": "3.3.0", "source": { "type": "git", "url": "https://github.com/laminas/laminas-stdlib.git", - "reference": "2b18347625a2f06a1a485acfbc870f699dbe51c6" + "reference": "b9d84eaa39fde733356ea948cdef36c631f202b6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-stdlib/zipball/2b18347625a2f06a1a485acfbc870f699dbe51c6", - "reference": "2b18347625a2f06a1a485acfbc870f699dbe51c6", + "url": "https://api.github.com/repos/laminas/laminas-stdlib/zipball/b9d84eaa39fde733356ea948cdef36c631f202b6", + "reference": "b9d84eaa39fde733356ea948cdef36c631f202b6", "shasum": "" }, "require": { "laminas/laminas-zendframework-bridge": "^1.0", - "php": "^5.6 || ^7.0" + "php": "^7.3 || ^8.0" }, "replace": { - "zendframework/zend-stdlib": "self.version" + "zendframework/zend-stdlib": "^3.2.1" }, "require-dev": { "laminas/laminas-coding-standard": "~1.0.0", - "phpbench/phpbench": "^0.13", - "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2" + "phpbench/phpbench": "^0.17.1", + "phpunit/phpunit": "^9.3.7" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.2.x-dev", - "dev-develop": "3.3.x-dev" + "dev-master": "3.3.x-dev", + "dev-develop": "3.4.x-dev" } }, "autoload": { @@ -2091,7 +1976,7 @@ "laminas", "stdlib" ], - "time": "2019-12-31T17:51:15+00:00" + "time": "2020-08-25T09:08:16+00:00" }, { "name": "laminas/laminas-uri", @@ -2225,23 +2110,23 @@ }, { "name": "laminas/laminas-zendframework-bridge", - "version": "1.0.4", + "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/laminas/laminas-zendframework-bridge.git", - "reference": "fcd87520e4943d968557803919523772475e8ea3" + "reference": "4939c81f63a8a4968c108c440275c94955753b19" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-zendframework-bridge/zipball/fcd87520e4943d968557803919523772475e8ea3", - "reference": "fcd87520e4943d968557803919523772475e8ea3", + "url": "https://api.github.com/repos/laminas/laminas-zendframework-bridge/zipball/4939c81f63a8a4968c108c440275c94955753b19", + "reference": "4939c81f63a8a4968c108c440275c94955753b19", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "php": "^5.6 || ^7.0 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^5.7 || ^6.5 || ^7.5 || ^8.1", + "phpunit/phpunit": "^5.7 || ^6.5 || ^7.5 || ^8.1 || ^9.3", "squizlabs/php_codesniffer": "^3.5" }, "type": "library", @@ -2273,26 +2158,20 @@ "laminas", "zf" ], - "funding": [ - { - "url": "https://funding.communitybridge.org/projects/laminas-project", - "type": "community_bridge" - } - ], - "time": "2020-05-20T16:45:56+00:00" + "time": "2020-08-18T16:34:51+00:00" }, { "name": "laravel-zero/foundation", - "version": "v7.16.1", + "version": "v7.27.0", "source": { "type": "git", "url": "https://github.com/laravel-zero/foundation.git", - "reference": "64ac03d083ced3b140caddae863ef62813afe1e3" + "reference": "6ca331c6f30d81341b23aa141e014eda470dc6c1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel-zero/foundation/zipball/64ac03d083ced3b140caddae863ef62813afe1e3", - "reference": "64ac03d083ced3b140caddae863ef62813afe1e3", + "url": "https://api.github.com/repos/laravel-zero/foundation/zipball/6ca331c6f30d81341b23aa141e014eda470dc6c1", + "reference": "6ca331c6f30d81341b23aa141e014eda470dc6c1", "shasum": "" }, "require": { @@ -2321,7 +2200,7 @@ "framework", "laravel" ], - "time": "2020-06-17T12:47:58+00:00" + "time": "2020-09-02T15:54:20+00:00" }, { "name": "laravel-zero/framework", @@ -2411,28 +2290,29 @@ }, { "name": "league/flysystem", - "version": "1.0.69", + "version": "1.1.3", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem.git", - "reference": "7106f78428a344bc4f643c233a94e48795f10967" + "reference": "9be3b16c877d477357c015cec057548cf9b2a14a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/7106f78428a344bc4f643c233a94e48795f10967", - "reference": "7106f78428a344bc4f643c233a94e48795f10967", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/9be3b16c877d477357c015cec057548cf9b2a14a", + "reference": "9be3b16c877d477357c015cec057548cf9b2a14a", "shasum": "" }, "require": { "ext-fileinfo": "*", - "php": ">=5.5.9" + "league/mime-type-detection": "^1.3", + "php": "^7.2.5 || ^8.0" }, "conflict": { "league/flysystem-sftp": "<1.0.6" }, "require-dev": { - "phpspec/phpspec": "^3.4", - "phpunit/phpunit": "^5.7.26" + "phpspec/prophecy": "^1.11.1", + "phpunit/phpunit": "^8.5.8" }, "suggest": { "ext-fileinfo": "Required for MimeType", @@ -2491,38 +2371,73 @@ "sftp", "storage" ], - "funding": [ - { - "url": "https://offset.earth/frankdejonge", - "type": "other" - } - ], - "time": "2020-05-18T15:13:39+00:00" + "time": "2020-08-23T07:39:11+00:00" }, { - "name": "mockery/mockery", + "name": "league/mime-type-detection", "version": "1.4.0", "source": { "type": "git", - "url": "https://github.com/mockery/mockery.git", - "reference": "6c6a7c533469873deacf998237e7649fc6b36223" + "url": "https://github.com/thephpleague/mime-type-detection.git", + "reference": "fda190b62b962d96a069fcc414d781db66d65b69" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mockery/mockery/zipball/6c6a7c533469873deacf998237e7649fc6b36223", - "reference": "6c6a7c533469873deacf998237e7649fc6b36223", + "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/fda190b62b962d96a069fcc414d781db66d65b69", + "reference": "fda190b62b962d96a069fcc414d781db66d65b69", "shasum": "" }, "require": { - "hamcrest/hamcrest-php": "~2.0", + "ext-fileinfo": "*", + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^0.12.36", + "phpunit/phpunit": "^8.5.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\MimeTypeDetection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "Mime-type detection for Flysystem", + "time": "2020-08-09T10:34:01+00:00" + }, + { + "name": "mockery/mockery", + "version": "1.4.2", + "source": { + "type": "git", + "url": "https://github.com/mockery/mockery.git", + "reference": "20cab678faed06fac225193be281ea0fddb43b93" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mockery/mockery/zipball/20cab678faed06fac225193be281ea0fddb43b93", + "reference": "20cab678faed06fac225193be281ea0fddb43b93", + "shasum": "" + }, + "require": { + "hamcrest/hamcrest-php": "^2.0.1", "lib-pcre": ">=7.0", - "php": "^7.3.0" + "php": "^7.3 || ^8.0" }, "conflict": { "phpunit/phpunit": "<8.0" }, "require-dev": { - "phpunit/phpunit": "^8.0.0 || ^9.0.0" + "phpunit/phpunit": "^8.5 || ^9.3" }, "type": "library", "extra": { @@ -2565,24 +2480,24 @@ "test double", "testing" ], - "time": "2020-05-19T14:25:16+00:00" + "time": "2020-08-11T18:10:13+00:00" }, { "name": "myclabs/deep-copy", - "version": "1.9.5", + "version": "1.10.1", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "b2c28789e80a97badd14145fda39b545d83ca3ef" + "reference": "969b211f9a51aa1f6c01d1d2aef56d3bd91598e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/b2c28789e80a97badd14145fda39b545d83ca3ef", - "reference": "b2c28789e80a97badd14145fda39b545d83ca3ef", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/969b211f9a51aa1f6c01d1d2aef56d3bd91598e5", + "reference": "969b211f9a51aa1f6c01d1d2aef56d3bd91598e5", "shasum": "" }, "require": { - "php": "^7.1" + "php": "^7.1 || ^8.0" }, "replace": { "myclabs/deep-copy": "self.version" @@ -2613,7 +2528,7 @@ "object", "object graph" ], - "time": "2020-01-17T21:11:47+00:00" + "time": "2020-06-29T13:22:24+00:00" }, { "name": "namshi/cuzzle", @@ -2667,16 +2582,16 @@ }, { "name": "nesbot/carbon", - "version": "2.35.0", + "version": "2.39.1", "source": { "type": "git", "url": "https://github.com/briannesbitt/Carbon.git", - "reference": "4b9bd835261ef23d36397a46a76b496a458305e5" + "reference": "7af467873250583cc967a59ee9df29fabab193c1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/4b9bd835261ef23d36397a46a76b496a458305e5", - "reference": "4b9bd835261ef23d36397a46a76b496a458305e5", + "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/7af467873250583cc967a59ee9df29fabab193c1", + "reference": "7af467873250583cc967a59ee9df29fabab193c1", "shasum": "" }, "require": { @@ -2688,9 +2603,10 @@ "require-dev": { "doctrine/orm": "^2.7", "friendsofphp/php-cs-fixer": "^2.14 || ^3.0", - "kylekatarnls/multi-tester": "^1.1", - "phpmd/phpmd": "^2.8", - "phpstan/phpstan": "^0.11", + "kylekatarnls/multi-tester": "^2.0", + "phpmd/phpmd": "^2.9", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^0.12.35", "phpunit/phpunit": "^7.5 || ^8.0", "squizlabs/php_codesniffer": "^3.4" }, @@ -2707,6 +2623,11 @@ "providers": [ "Carbon\\Laravel\\ServiceProvider" ] + }, + "phpstan": { + "includes": [ + "extension.neon" + ] } }, "autoload": { @@ -2736,30 +2657,20 @@ "datetime", "time" ], - "funding": [ - { - "url": "https://opencollective.com/Carbon", - "type": "open_collective" - }, - { - "url": "https://tidelift.com/funding/github/packagist/nesbot/carbon", - "type": "tidelift" - } - ], - "time": "2020-05-24T18:27:52+00:00" + "time": "2020-09-04T13:11:37+00:00" }, { "name": "nikic/php-parser", - "version": "v4.5.0", + "version": "v4.9.1", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "53c2753d756f5adb586dca79c2ec0e2654dd9463" + "reference": "88e519766fc58bd46b8265561fb79b54e2e00b28" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/53c2753d756f5adb586dca79c2ec0e2654dd9463", - "reference": "53c2753d756f5adb586dca79c2ec0e2654dd9463", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/88e519766fc58bd46b8265561fb79b54e2e00b28", + "reference": "88e519766fc58bd46b8265561fb79b54e2e00b28", "shasum": "" }, "require": { @@ -2767,8 +2678,8 @@ "php": ">=7.0" }, "require-dev": { - "ircmaxell/php-yacc": "0.0.5", - "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0" + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" }, "bin": [ "bin/php-parse" @@ -2776,7 +2687,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.9-dev" } }, "autoload": { @@ -2798,7 +2709,7 @@ "parser", "php" ], - "time": "2020-06-03T07:24:19+00:00" + "time": "2020-08-30T16:15:20+00:00" }, { "name": "nunomaduro/collision", @@ -2868,39 +2779,25 @@ "php", "symfony" ], - "funding": [ - { - "url": "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=66BYDWAT92N6L", - "type": "custom" - }, - { - "url": "https://github.com/nunomaduro", - "type": "github" - }, - { - "url": "https://www.patreon.com/nunomaduro", - "type": "patreon" - } - ], "time": "2020-04-04T19:56:08+00:00" }, { "name": "nunomaduro/laravel-console-summary", - "version": "v1.4.0", + "version": "v1.5.0", "source": { "type": "git", "url": "https://github.com/nunomaduro/laravel-console-summary.git", - "reference": "1338b803ffd0fa7d8c497c300e751d819d3a5410" + "reference": "4766be89ac4c93268d1ad165f7f0281a8c02d450" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nunomaduro/laravel-console-summary/zipball/1338b803ffd0fa7d8c497c300e751d819d3a5410", - "reference": "1338b803ffd0fa7d8c497c300e751d819d3a5410", + "url": "https://api.github.com/repos/nunomaduro/laravel-console-summary/zipball/4766be89ac4c93268d1ad165f7f0281a8c02d450", + "reference": "4766be89ac4c93268d1ad165f7f0281a8c02d450", "shasum": "" }, "require": { - "illuminate/console": "^7.0", - "illuminate/support": "^7.0", + "illuminate/console": "^7.0|^8.0", + "illuminate/support": "^7.0|^8.0", "php": "^7.2.5" }, "type": "library", @@ -2937,29 +2834,29 @@ "php", "symfony" ], - "time": "2020-01-28T17:42:52+00:00" + "time": "2020-08-27T10:28:12+00:00" }, { "name": "nunomaduro/laravel-console-task", - "version": "v1.4.0", + "version": "v1.5.0", "source": { "type": "git", "url": "https://github.com/nunomaduro/laravel-console-task.git", - "reference": "513746f6809485121898e21c8a5da1ad72dca8aa" + "reference": "b18b5fde2c1cc29578e9ebf60a7c41e7662db4d4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nunomaduro/laravel-console-task/zipball/513746f6809485121898e21c8a5da1ad72dca8aa", - "reference": "513746f6809485121898e21c8a5da1ad72dca8aa", + "url": "https://api.github.com/repos/nunomaduro/laravel-console-task/zipball/b18b5fde2c1cc29578e9ebf60a7c41e7662db4d4", + "reference": "b18b5fde2c1cc29578e9ebf60a7c41e7662db4d4", "shasum": "" }, "require": { - "illuminate/console": "~5.5.26|5.6.*|5.7.*|5.8.*|^6.0|^7.0", - "illuminate/support": "~5.5.26|5.6.*|5.7.*|5.8.*|^6.0|^7.0", - "php": "^7.0" + "illuminate/console": "^6.0|^7.0|^8.0", + "illuminate/support": "^6.0|^7.0|^8.0", + "php": "^7.2.5" }, "require-dev": { - "phpunit/phpunit": "^8.2" + "phpunit/phpunit": "^8.5.8|^9.0" }, "type": "library", "extra": { @@ -2995,31 +2892,31 @@ "php", "symfony" ], - "time": "2020-01-02T23:29:26+00:00" + "time": "2020-08-27T10:22:01+00:00" }, { "name": "nunomaduro/laravel-desktop-notifier", - "version": "v2.3.0", + "version": "v2.4.0", "source": { "type": "git", "url": "https://github.com/nunomaduro/laravel-desktop-notifier.git", - "reference": "a1cb05575ad195fee3e0cf1ae6f1e88f832f16d2" + "reference": "f125c852d515d0fe2e25f579f1b0d4a748288d73" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nunomaduro/laravel-desktop-notifier/zipball/a1cb05575ad195fee3e0cf1ae6f1e88f832f16d2", - "reference": "a1cb05575ad195fee3e0cf1ae6f1e88f832f16d2", + "url": "https://api.github.com/repos/nunomaduro/laravel-desktop-notifier/zipball/f125c852d515d0fe2e25f579f1b0d4a748288d73", + "reference": "f125c852d515d0fe2e25f579f1b0d4a748288d73", "shasum": "" }, "require": { - "illuminate/console": "5.6.*|5.7.*|5.8.*|^6.0|^7.0", - "illuminate/support": "5.6.*|5.7.*|5.8.*|^6.0|^7.0", + "illuminate/console": "^6.0|^7.0|^8.0", + "illuminate/support": "^6.0|^7.0|^8.0", "jolicode/jolinotif": "^2.0", - "php": "^7.1.3" + "php": "^7.2.5" }, "require-dev": { - "graham-campbell/testbench": "dev-master", - "phpunit/phpunit": "^7.0" + "graham-campbell/testbench": "^5.5", + "phpunit/phpunit": "^8.5.8|^9.0" }, "type": "library", "extra": { @@ -3061,7 +2958,7 @@ "php", "wrapper" ], - "time": "2020-01-15T13:26:31+00:00" + "time": "2020-09-07T09:34:02+00:00" }, { "name": "nyholm/psr7", @@ -3124,87 +3021,8 @@ "psr-17", "psr-7" ], - "funding": [ - { - "url": "https://github.com/Zegnat", - "type": "github" - }, - { - "url": "https://github.com/nyholm", - "type": "github" - } - ], "time": "2020-05-23T11:29:07+00:00" }, - { - "name": "padraic/humbug_get_contents", - "version": "1.1.2", - "source": { - "type": "git", - "url": "https://github.com/humbug/file_get_contents.git", - "reference": "dcb086060c9dd6b2f51d8f7a895500307110b7a7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/humbug/file_get_contents/zipball/dcb086060c9dd6b2f51d8f7a895500307110b7a7", - "reference": "dcb086060c9dd6b2f51d8f7a895500307110b7a7", - "shasum": "" - }, - "require": { - "composer/ca-bundle": "^1.0", - "ext-openssl": "*", - "php": "^5.3 || ^7.0 || ^7.1 || ^7.2" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.1", - "mikey179/vfsstream": "^1.6", - "phpunit/phpunit": "^4.8 || ^5.7 || ^6.5" - }, - "type": "library", - "extra": { - "bamarni-bin": { - "bin-links": false - }, - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "psr-4": { - "Humbug\\": "src/" - }, - "files": [ - "src/function.php", - "src/functions.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Pádraic Brady", - "email": "padraic.brady@gmail.com", - "homepage": "http://blog.astrumfutura.com" - }, - { - "name": "Théo Fidry", - "email": "theo.fidry@gmail.com" - } - ], - "description": "Secure wrapper for accessing HTTPS resources with file_get_contents for PHP 5.3+", - "homepage": "https://github.com/padraic/file_get_contents", - "keywords": [ - "download", - "file_get_contents", - "http", - "https", - "ssl", - "tls" - ], - "time": "2018-02-12T18:47:17+00:00" - }, { "name": "paragonie/random_compat", "version": "v9.99.99", @@ -3404,25 +3222,25 @@ }, { "name": "phpdocumentor/reflection-common", - "version": "2.1.0", + "version": "2.2.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "6568f4687e5b41b054365f9ae03fcb1ed5f2069b" + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/6568f4687e5b41b054365f9ae03fcb1ed5f2069b", - "reference": "6568f4687e5b41b054365f9ae03fcb1ed5f2069b", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", "shasum": "" }, "require": { - "php": ">=7.1" + "php": "^7.2 || ^8.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.x-dev" + "dev-2.x": "2.x-dev" } }, "autoload": { @@ -3449,32 +3267,31 @@ "reflection", "static analysis" ], - "time": "2020-04-27T09:25:28+00:00" + "time": "2020-06-27T09:03:43+00:00" }, { "name": "phpdocumentor/reflection-docblock", - "version": "5.1.0", + "version": "5.2.1", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "cd72d394ca794d3466a3b2fc09d5a6c1dc86b47e" + "reference": "d870572532cd70bc3fab58f2e23ad423c8404c44" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/cd72d394ca794d3466a3b2fc09d5a6c1dc86b47e", - "reference": "cd72d394ca794d3466a3b2fc09d5a6c1dc86b47e", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/d870572532cd70bc3fab58f2e23ad423c8404c44", + "reference": "d870572532cd70bc3fab58f2e23ad423c8404c44", "shasum": "" }, "require": { - "ext-filter": "^7.1", - "php": "^7.2", - "phpdocumentor/reflection-common": "^2.0", - "phpdocumentor/type-resolver": "^1.0", - "webmozart/assert": "^1" + "ext-filter": "*", + "php": "^7.2 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^1.3", + "webmozart/assert": "^1.9.1" }, "require-dev": { - "doctrine/instantiator": "^1", - "mockery/mockery": "^1" + "mockery/mockery": "~1.3.2" }, "type": "library", "extra": { @@ -3502,29 +3319,28 @@ } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "time": "2020-02-22T12:28:44+00:00" + "time": "2020-08-15T11:14:08+00:00" }, { "name": "phpdocumentor/type-resolver", - "version": "1.2.0", + "version": "1.3.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "30441f2752e493c639526b215ed81d54f369d693" + "reference": "e878a14a65245fbe78f8080eba03b47c3b705651" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/30441f2752e493c639526b215ed81d54f369d693", - "reference": "30441f2752e493c639526b215ed81d54f369d693", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/e878a14a65245fbe78f8080eba03b47c3b705651", + "reference": "e878a14a65245fbe78f8080eba03b47c3b705651", "shasum": "" }, "require": { - "php": "^7.2", + "php": "^7.2 || ^8.0", "phpdocumentor/reflection-common": "^2.0" }, "require-dev": { - "ext-tokenizer": "^7.2", - "mockery/mockery": "~1" + "ext-tokenizer": "*" }, "type": "library", "extra": { @@ -3548,28 +3364,28 @@ } ], "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", - "time": "2020-06-19T20:22:09+00:00" + "time": "2020-06-27T10:12:23+00:00" }, { "name": "phpoption/phpoption", - "version": "1.7.4", + "version": "1.7.5", "source": { "type": "git", "url": "https://github.com/schmittjoh/php-option.git", - "reference": "b2ada2ad5d8a32b89088b8adc31ecd2e3a13baf3" + "reference": "994ecccd8f3283ecf5ac33254543eb0ac946d525" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/b2ada2ad5d8a32b89088b8adc31ecd2e3a13baf3", - "reference": "b2ada2ad5d8a32b89088b8adc31ecd2e3a13baf3", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/994ecccd8f3283ecf5ac33254543eb0ac946d525", + "reference": "994ecccd8f3283ecf5ac33254543eb0ac946d525", "shasum": "" }, "require": { "php": "^5.5.9 || ^7.0 || ^8.0" }, "require-dev": { - "bamarni/composer-bin-plugin": "^1.3", - "phpunit/phpunit": "^4.8.35 || ^5.0 || ^6.0 || ^7.0" + "bamarni/composer-bin-plugin": "^1.4.1", + "phpunit/phpunit": "^4.8.35 || ^5.7.27 || ^6.5.6 || ^7.0 || ^8.0 || ^9.0" }, "type": "library", "extra": { @@ -3603,47 +3419,37 @@ "php", "type" ], - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption", - "type": "tidelift" - } - ], - "time": "2020-06-07T10:40:07+00:00" + "time": "2020-07-20T17:29:33+00:00" }, { "name": "phpspec/prophecy", - "version": "v1.10.3", + "version": "1.11.1", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "451c3cd1418cf640de218914901e51b064abb093" + "reference": "b20034be5efcdab4fb60ca3a29cba2949aead160" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/451c3cd1418cf640de218914901e51b064abb093", - "reference": "451c3cd1418cf640de218914901e51b064abb093", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/b20034be5efcdab4fb60ca3a29cba2949aead160", + "reference": "b20034be5efcdab4fb60ca3a29cba2949aead160", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.0.2", - "php": "^5.3|^7.0", - "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0|^5.0", - "sebastian/comparator": "^1.2.3|^2.0|^3.0|^4.0", - "sebastian/recursion-context": "^1.0|^2.0|^3.0|^4.0" + "doctrine/instantiator": "^1.2", + "php": "^7.2", + "phpdocumentor/reflection-docblock": "^5.0", + "sebastian/comparator": "^3.0 || ^4.0", + "sebastian/recursion-context": "^3.0 || ^4.0" }, "require-dev": { - "phpspec/phpspec": "^2.5 || ^3.2", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" + "phpspec/phpspec": "^6.0", + "phpunit/phpunit": "^8.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.10.x-dev" + "dev-master": "1.11.x-dev" } }, "autoload": { @@ -3676,7 +3482,7 @@ "spy", "stub" ], - "time": "2020-03-05T15:02:03+00:00" + "time": "2020-07-08T12:44:21+00:00" }, { "name": "phpunit/php-code-coverage", @@ -3928,7 +3734,6 @@ "keywords": [ "tokenizer" ], - "abandoned": true, "time": "2019-09-17T06:23:10+00:00" }, { @@ -4012,16 +3817,6 @@ "testing", "xunit" ], - "funding": [ - { - "url": "https://phpunit.de/donate.html", - "type": "custom" - }, - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], "time": "2020-06-22T07:06:58+00:00" }, { @@ -4491,22 +4286,22 @@ }, { "name": "ratchet/pawl", - "version": "v0.3.4", + "version": "v0.3.5", "source": { "type": "git", "url": "https://github.com/ratchetphp/Pawl.git", - "reference": "3a7d5b78e0deaec82f42513a4a3193a8eb12feb1" + "reference": "89ec703c76dc893484a2a0ed44b48a37d445abd5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ratchetphp/Pawl/zipball/3a7d5b78e0deaec82f42513a4a3193a8eb12feb1", - "reference": "3a7d5b78e0deaec82f42513a4a3193a8eb12feb1", + "url": "https://api.github.com/repos/ratchetphp/Pawl/zipball/89ec703c76dc893484a2a0ed44b48a37d445abd5", + "reference": "89ec703c76dc893484a2a0ed44b48a37d445abd5", "shasum": "" }, "require": { "evenement/evenement": "^3.0 || ^2.0", "php": ">=5.4", - "ratchet/rfc6455": "^0.2.3", + "ratchet/rfc6455": "^0.3", "react/socket": "^1.0 || ^0.8 || ^0.7" }, "require-dev": { @@ -4536,20 +4331,20 @@ "websocket", "websocket client" ], - "time": "2019-01-14T14:09:36+00:00" + "time": "2020-07-17T15:32:47+00:00" }, { "name": "ratchet/rfc6455", - "version": "v0.2.6", + "version": "v0.3", "source": { "type": "git", "url": "https://github.com/ratchetphp/RFC6455.git", - "reference": "879e48c840f8dbc296d68d6a5030673df79bd916" + "reference": "c8651c7938651c2d55f5d8c2422ac5e57a183341" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ratchetphp/RFC6455/zipball/879e48c840f8dbc296d68d6a5030673df79bd916", - "reference": "879e48c840f8dbc296d68d6a5030673df79bd916", + "url": "https://api.github.com/repos/ratchetphp/RFC6455/zipball/c8651c7938651c2d55f5d8c2422ac5e57a183341", + "reference": "c8651c7938651c2d55f5d8c2422ac5e57a183341", "shasum": "" }, "require": { @@ -4557,7 +4352,7 @@ "php": ">=5.4.2" }, "require-dev": { - "phpunit/phpunit": "4.8.*", + "phpunit/phpunit": "5.7.*", "react/socket": "^1.3" }, "type": "library", @@ -4575,6 +4370,10 @@ "name": "Chris Boden", "email": "cboden@gmail.com", "role": "Developer" + }, + { + "name": "Matt Bonneau", + "role": "Developer" } ], "description": "RFC6455 WebSocket protocol handler", @@ -4584,7 +4383,7 @@ "rfc6455", "websocket" ], - "time": "2019-12-15T10:18:18+00:00" + "time": "2020-05-15T18:31:24+00:00" }, { "name": "react/cache", @@ -4671,28 +4470,28 @@ }, { "name": "react/dns", - "version": "v1.2.0", + "version": "v1.3.0", "source": { "type": "git", "url": "https://github.com/reactphp/dns.git", - "reference": "a214d90c2884dac18d0cac6176202f247b66d762" + "reference": "89d83794e959ef3e0f1ab792f070b0157de1abf2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/dns/zipball/a214d90c2884dac18d0cac6176202f247b66d762", - "reference": "a214d90c2884dac18d0cac6176202f247b66d762", + "url": "https://api.github.com/repos/reactphp/dns/zipball/89d83794e959ef3e0f1ab792f070b0157de1abf2", + "reference": "89d83794e959ef3e0f1ab792f070b0157de1abf2", "shasum": "" }, "require": { "php": ">=5.3.0", "react/cache": "^1.0 || ^0.6 || ^0.5", "react/event-loop": "^1.0 || ^0.5", - "react/promise": "^2.7 || ^1.2.1", + "react/promise": "^3.0 || ^2.7 || ^1.2.1", "react/promise-timer": "^1.2" }, "require-dev": { "clue/block-react": "^1.2", - "phpunit/phpunit": "^7.0 || ^6.4 || ^5.7 || ^4.8.35" + "phpunit/phpunit": "^9.0 || ^4.8.35" }, "type": "library", "autoload": { @@ -4711,7 +4510,7 @@ "dns-resolver", "reactphp" ], - "time": "2019-08-15T09:06:31+00:00" + "time": "2020-07-10T12:12:50+00:00" }, { "name": "react/event-loop", @@ -4757,16 +4556,16 @@ }, { "name": "react/http", - "version": "v0.8.6", + "version": "v0.8.7", "source": { "type": "git", "url": "https://github.com/reactphp/http.git", - "reference": "248202e57195d06a4375f6d2f5c5b9ff9da3ea9e" + "reference": "9aa446fd86745403a09c315c90d4e464fd84382b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/http/zipball/248202e57195d06a4375f6d2f5c5b9ff9da3ea9e", - "reference": "248202e57195d06a4375f6d2f5c5b9ff9da3ea9e", + "url": "https://api.github.com/repos/reactphp/http/zipball/9aa446fd86745403a09c315c90d4e464fd84382b", + "reference": "9aa446fd86745403a09c315c90d4e464fd84382b", "shasum": "" }, "require": { @@ -4780,7 +4579,7 @@ }, "require-dev": { "clue/block-react": "^1.1", - "phpunit/phpunit": "^7.0 || ^6.4 || ^5.7 || ^4.8.35" + "phpunit/phpunit": "^9.0 || ^5.7 || ^4.8.35" }, "type": "library", "autoload": { @@ -4801,7 +4600,7 @@ "server", "streaming" ], - "time": "2020-01-12T13:15:06+00:00" + "time": "2020-07-05T11:32:28+00:00" }, { "name": "react/http-client", @@ -4951,25 +4750,25 @@ }, { "name": "react/promise-timer", - "version": "v1.5.1", + "version": "v1.6.0", "source": { "type": "git", "url": "https://github.com/reactphp/promise-timer.git", - "reference": "35fb910604fd86b00023fc5cda477c8074ad0abc" + "reference": "daee9baf6ef30c43ea4c86399f828bb5f558f6e6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/promise-timer/zipball/35fb910604fd86b00023fc5cda477c8074ad0abc", - "reference": "35fb910604fd86b00023fc5cda477c8074ad0abc", + "url": "https://api.github.com/repos/reactphp/promise-timer/zipball/daee9baf6ef30c43ea4c86399f828bb5f558f6e6", + "reference": "daee9baf6ef30c43ea4c86399f828bb5f558f6e6", "shasum": "" }, "require": { "php": ">=5.3", "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5", - "react/promise": "^2.7.0 || ^1.2.1" + "react/promise": "^3.0 || ^2.7.0 || ^1.2.1" }, "require-dev": { - "phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35" + "phpunit/phpunit": "^9.0 || ^5.7 || ^4.8.35" }, "type": "library", "autoload": { @@ -5000,11 +4799,11 @@ "timeout", "timer" ], - "time": "2019-03-27T18:10:32+00:00" + "time": "2020-07-10T12:18:06+00:00" }, { "name": "react/socket", - "version": "dev-master", + "version": "v1.6.0", "source": { "type": "git", "url": "https://github.com/reactphp/socket.git", @@ -5070,16 +4869,6 @@ "reactphp", "stream" ], - "funding": [ - { - "url": "https://github.com/WyriHaximus", - "type": "github" - }, - { - "url": "https://github.com/clue", - "type": "github" - } - ], "time": "2020-08-28T12:49:05+00:00" }, { @@ -5855,16 +5644,16 @@ }, { "name": "symfony/cache", - "version": "v5.1.2", + "version": "v5.1.5", "source": { "type": "git", "url": "https://github.com/symfony/cache.git", - "reference": "787eb05e137ad74fa5e51857b9884719760c7b2f" + "reference": "c31bdd71f30435baff03693e684469c7ecb3ca1a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/cache/zipball/787eb05e137ad74fa5e51857b9884719760c7b2f", - "reference": "787eb05e137ad74fa5e51857b9884719760c7b2f", + "url": "https://api.github.com/repos/symfony/cache/zipball/c31bdd71f30435baff03693e684469c7ecb3ca1a", + "reference": "c31bdd71f30435baff03693e684469c7ecb3ca1a", "shasum": "" }, "require": { @@ -5931,34 +5720,20 @@ "caching", "psr6" ], - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-06-09T14:15:34+00:00" + "time": "2020-09-01T05:52:18+00:00" }, { "name": "symfony/cache-contracts", - "version": "v2.1.2", + "version": "v2.1.3", "source": { "type": "git", "url": "https://github.com/symfony/cache-contracts.git", - "reference": "87c92f62c494626598e9148208aaa6d1716b8e3c" + "reference": "9771a09d2e6b84ecb8c9f0a7dbc72ee92aeba009" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/cache-contracts/zipball/87c92f62c494626598e9148208aaa6d1716b8e3c", - "reference": "87c92f62c494626598e9148208aaa6d1716b8e3c", + "url": "https://api.github.com/repos/symfony/cache-contracts/zipball/9771a09d2e6b84ecb8c9f0a7dbc72ee92aeba009", + "reference": "9771a09d2e6b84ecb8c9f0a7dbc72ee92aeba009", "shasum": "" }, "require": { @@ -5972,6 +5747,10 @@ "extra": { "branch-alias": { "dev-master": "2.1-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" } }, "autoload": { @@ -6003,34 +5782,20 @@ "interoperability", "standards" ], - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-05-20T17:43:50+00:00" + "time": "2020-07-06T13:23:11+00:00" }, { "name": "symfony/console", - "version": "v5.1.2", + "version": "v5.1.5", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "34ac555a3627e324b660e318daa07572e1140123" + "reference": "186f395b256065ba9b890c0a4e48a91d598fa2cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/34ac555a3627e324b660e318daa07572e1140123", - "reference": "34ac555a3627e324b660e318daa07572e1140123", + "url": "https://api.github.com/repos/symfony/console/zipball/186f395b256065ba9b890c0a4e48a91d598fa2cf", + "reference": "186f395b256065ba9b890c0a4e48a91d598fa2cf", "shasum": "" }, "require": { @@ -6096,21 +5861,7 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-06-15T12:59:21+00:00" + "time": "2020-09-02T07:07:40+00:00" }, { "name": "symfony/deprecation-contracts", @@ -6160,20 +5911,6 @@ ], "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], "time": "2020-06-06T08:49:21+00:00" }, { @@ -6231,20 +5968,6 @@ ], "description": "Symfony ErrorHandler Component", "homepage": "https://symfony.com", - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], "time": "2020-08-17T10:01:29+00:00" }, { @@ -6317,20 +6040,6 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], "time": "2020-08-13T14:19:42+00:00" }, { @@ -6393,25 +6102,11 @@ "interoperability", "standards" ], - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], "time": "2020-07-06T13:23:11+00:00" }, { "name": "symfony/expression-language", - "version": "v5.1.2", + "version": "v5.1.5", "source": { "type": "git", "url": "https://github.com/symfony/expression-language.git", @@ -6459,34 +6154,20 @@ ], "description": "Symfony ExpressionLanguage Component", "homepage": "https://symfony.com", - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], "time": "2020-05-31T07:33:39+00:00" }, { "name": "symfony/finder", - "version": "v5.1.2", + "version": "v5.1.5", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "4298870062bfc667cb78d2b379be4bf5dec5f187" + "reference": "2b765f0cf6612b3636e738c0689b29aa63088d5d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/4298870062bfc667cb78d2b379be4bf5dec5f187", - "reference": "4298870062bfc667cb78d2b379be4bf5dec5f187", + "url": "https://api.github.com/repos/symfony/finder/zipball/2b765f0cf6612b3636e738c0689b29aa63088d5d", + "reference": "2b765f0cf6612b3636e738c0689b29aa63088d5d", "shasum": "" }, "require": { @@ -6522,21 +6203,7 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-05-20T17:43:50+00:00" + "time": "2020-08-17T10:01:29+00:00" }, { "name": "symfony/http-foundation", @@ -6597,20 +6264,6 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "https://symfony.com", - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], "time": "2020-08-17T07:48:54+00:00" }, { @@ -6710,34 +6363,20 @@ ], "description": "Symfony HttpKernel Component", "homepage": "https://symfony.com", - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], "time": "2020-09-02T08:15:18+00:00" }, { "name": "symfony/mime", - "version": "v5.1.2", + "version": "v5.1.5", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "c0c418f05e727606e85b482a8591519c4712cf45" + "reference": "89a2c9b4cb7b5aa516cf55f5194c384f444c81dc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/c0c418f05e727606e85b482a8591519c4712cf45", - "reference": "c0c418f05e727606e85b482a8591519c4712cf45", + "url": "https://api.github.com/repos/symfony/mime/zipball/89a2c9b4cb7b5aa516cf55f5194c384f444c81dc", + "reference": "89a2c9b4cb7b5aa516cf55f5194c384f444c81dc", "shasum": "" }, "require": { @@ -6787,21 +6426,7 @@ "mime", "mime-type" ], - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-06-09T15:07:35+00:00" + "time": "2020-08-17T10:01:29+00:00" }, { "name": "symfony/polyfill-ctype", @@ -6863,34 +6488,20 @@ "polyfill", "portable" ], - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], "time": "2020-07-14T12:35:20+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.17.1", + "version": "v1.18.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "6e4dbcf5e81eba86e36731f94fe56b1726835846" + "reference": "b740103edbdcc39602239ee8860f0f45a8eb9aa5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/6e4dbcf5e81eba86e36731f94fe56b1726835846", - "reference": "6e4dbcf5e81eba86e36731f94fe56b1726835846", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b740103edbdcc39602239ee8860f0f45a8eb9aa5", + "reference": "b740103edbdcc39602239ee8860f0f45a8eb9aa5", "shasum": "" }, "require": { @@ -6902,7 +6513,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.17-dev" + "dev-master": "1.18-dev" }, "thanks": { "name": "symfony/polyfill", @@ -6941,39 +6552,26 @@ "portable", "shim" ], - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-06-06T08:46:27+00:00" + "time": "2020-07-14T12:35:20+00:00" }, { "name": "symfony/polyfill-intl-idn", - "version": "v1.17.1", + "version": "v1.18.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-idn.git", - "reference": "a57f8161502549a742a63c09f0a604997bf47027" + "reference": "5dcab1bc7146cf8c1beaa4502a3d9be344334251" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/a57f8161502549a742a63c09f0a604997bf47027", - "reference": "a57f8161502549a742a63c09f0a604997bf47027", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/5dcab1bc7146cf8c1beaa4502a3d9be344334251", + "reference": "5dcab1bc7146cf8c1beaa4502a3d9be344334251", "shasum": "" }, "require": { "php": ">=5.3.3", - "symfony/polyfill-mbstring": "^1.3", + "symfony/polyfill-intl-normalizer": "^1.10", + "symfony/polyfill-php70": "^1.10", "symfony/polyfill-php72": "^1.10" }, "suggest": { @@ -6982,7 +6580,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.17-dev" + "dev-master": "1.18-dev" }, "thanks": { "name": "symfony/polyfill", @@ -7006,6 +6604,10 @@ "name": "Laurent Bassin", "email": "laurent@bassin.info" }, + { + "name": "Trevor Rowbotham", + "email": "trevor.rowbotham@pm.me" + }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" @@ -7021,34 +6623,20 @@ "portable", "shim" ], - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-06-06T08:46:27+00:00" + "time": "2020-08-04T06:02:08+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.17.1", + "version": "v1.18.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "40309d1700e8f72447bb9e7b54af756eeea35620" + "reference": "37078a8dd4a2a1e9ab0231af7c6cb671b2ed5a7e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/40309d1700e8f72447bb9e7b54af756eeea35620", - "reference": "40309d1700e8f72447bb9e7b54af756eeea35620", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/37078a8dd4a2a1e9ab0231af7c6cb671b2ed5a7e", + "reference": "37078a8dd4a2a1e9ab0231af7c6cb671b2ed5a7e", "shasum": "" }, "require": { @@ -7060,7 +6648,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.17-dev" + "dev-master": "1.18-dev" }, "thanks": { "name": "symfony/polyfill", @@ -7102,21 +6690,7 @@ "portable", "shim" ], - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-06-14T14:40:37+00:00" + "time": "2020-07-14T12:35:20+00:00" }, { "name": "symfony/polyfill-mbstring", @@ -7179,34 +6753,83 @@ "portable", "shim" ], - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" + "time": "2020-07-14T12:35:20+00:00" + }, + { + "name": "symfony/polyfill-php70", + "version": "v1.18.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php70.git", + "reference": "0dd93f2c578bdc9c72697eaa5f1dd25644e618d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/0dd93f2c578bdc9c72697eaa5f1dd25644e618d3", + "reference": "0dd93f2c578bdc9c72697eaa5f1dd25644e618d3", + "shasum": "" + }, + "require": { + "paragonie/random_compat": "~1.0|~2.0|~9.99", + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.18-dev" }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php70\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 7.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" ], "time": "2020-07-14T12:35:20+00:00" }, { "name": "symfony/polyfill-php72", - "version": "v1.17.0", + "version": "v1.18.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php72.git", - "reference": "f048e612a3905f34931127360bdd2def19a5e582" + "reference": "639447d008615574653fb3bc60d1986d7172eaae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/f048e612a3905f34931127360bdd2def19a5e582", - "reference": "f048e612a3905f34931127360bdd2def19a5e582", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/639447d008615574653fb3bc60d1986d7172eaae", + "reference": "639447d008615574653fb3bc60d1986d7172eaae", "shasum": "" }, "require": { @@ -7215,7 +6838,11 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.17-dev" + "dev-master": "1.18-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" } }, "autoload": { @@ -7248,21 +6875,7 @@ "portable", "shim" ], - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-05-12T16:47:27+00:00" + "time": "2020-07-14T12:35:20+00:00" }, { "name": "symfony/polyfill-php73", @@ -7324,20 +6937,6 @@ "portable", "shim" ], - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], "time": "2020-07-14T12:35:20+00:00" }, { @@ -7404,34 +7003,20 @@ "portable", "shim" ], - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], "time": "2020-07-14T12:35:20+00:00" }, { "name": "symfony/process", - "version": "v5.1.2", + "version": "v5.1.5", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "7f6378c1fa2147eeb1b4c385856ce9de0d46ebd1" + "reference": "1864216226af21eb76d9477f691e7cbf198e0402" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/7f6378c1fa2147eeb1b4c385856ce9de0d46ebd1", - "reference": "7f6378c1fa2147eeb1b4c385856ce9de0d46ebd1", + "url": "https://api.github.com/repos/symfony/process/zipball/1864216226af21eb76d9477f691e7cbf198e0402", + "reference": "1864216226af21eb76d9477f691e7cbf198e0402", "shasum": "" }, "require": { @@ -7468,38 +7053,24 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-05-30T20:35:19+00:00" + "time": "2020-07-23T08:36:24+00:00" }, { "name": "symfony/psr-http-message-bridge", - "version": "v2.0.0", + "version": "v2.0.1", "source": { "type": "git", "url": "https://github.com/symfony/psr-http-message-bridge.git", - "reference": "ce709cd9c90872c08c2427b45739d5f3c781ab4f" + "reference": "e44f249afab496b4e8c0f7461fb8140eaa4b24d2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/psr-http-message-bridge/zipball/ce709cd9c90872c08c2427b45739d5f3c781ab4f", - "reference": "ce709cd9c90872c08c2427b45739d5f3c781ab4f", + "url": "https://api.github.com/repos/symfony/psr-http-message-bridge/zipball/e44f249afab496b4e8c0f7461fb8140eaa4b24d2", + "reference": "e44f249afab496b4e8c0f7461fb8140eaa4b24d2", "shasum": "" }, "require": { - "php": "^7.1", + "php": ">=7.1", "psr/http-message": "^1.0", "symfony/http-foundation": "^4.4 || ^5.0" }, @@ -7546,20 +7117,20 @@ "psr-17", "psr-7" ], - "time": "2020-01-02T08:07:11+00:00" + "time": "2020-06-25T08:21:47+00:00" }, { "name": "symfony/routing", - "version": "v5.1.2", + "version": "v5.1.5", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "bbd0ba121d623f66d165a55a108008968911f3eb" + "reference": "47b0218344cb6af25c93ca8ee1137fafbee5005d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/bbd0ba121d623f66d165a55a108008968911f3eb", - "reference": "bbd0ba121d623f66d165a55a108008968911f3eb", + "url": "https://api.github.com/repos/symfony/routing/zipball/47b0218344cb6af25c93ca8ee1137fafbee5005d", + "reference": "47b0218344cb6af25c93ca8ee1137fafbee5005d", "shasum": "" }, "require": { @@ -7624,34 +7195,20 @@ "uri", "url" ], - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-06-10T11:49:58+00:00" + "time": "2020-08-10T08:03:57+00:00" }, { "name": "symfony/service-contracts", - "version": "v2.1.2", + "version": "v2.1.3", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "66a8f0957a3ca54e4f724e49028ab19d75a8918b" + "reference": "58c7475e5457c5492c26cc740cc0ad7464be9442" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/66a8f0957a3ca54e4f724e49028ab19d75a8918b", - "reference": "66a8f0957a3ca54e4f724e49028ab19d75a8918b", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/58c7475e5457c5492c26cc740cc0ad7464be9442", + "reference": "58c7475e5457c5492c26cc740cc0ad7464be9442", "shasum": "" }, "require": { @@ -7665,6 +7222,10 @@ "extra": { "branch-alias": { "dev-master": "2.1-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" } }, "autoload": { @@ -7696,34 +7257,20 @@ "interoperability", "standards" ], - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-05-20T17:43:50+00:00" + "time": "2020-07-06T13:23:11+00:00" }, { "name": "symfony/string", - "version": "v5.1.2", + "version": "v5.1.5", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "ac70459db781108db7c6d8981dd31ce0e29e3298" + "reference": "0de4cc1e18bb596226c06a82e2e7e9bc6001a63a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/ac70459db781108db7c6d8981dd31ce0e29e3298", - "reference": "ac70459db781108db7c6d8981dd31ce0e29e3298", + "url": "https://api.github.com/repos/symfony/string/zipball/0de4cc1e18bb596226c06a82e2e7e9bc6001a63a", + "reference": "0de4cc1e18bb596226c06a82e2e7e9bc6001a63a", "shasum": "" }, "require": { @@ -7781,34 +7328,20 @@ "utf-8", "utf8" ], - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-06-11T12:16:36+00:00" + "time": "2020-08-17T07:48:54+00:00" }, { "name": "symfony/translation", - "version": "v5.1.2", + "version": "v5.1.5", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "d387f07d4c15f9c09439cf3f13ddbe0b2c5e8be2" + "reference": "917b02cdc5f33e0309b8e9d33ee1480b20687413" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/d387f07d4c15f9c09439cf3f13ddbe0b2c5e8be2", - "reference": "d387f07d4c15f9c09439cf3f13ddbe0b2c5e8be2", + "url": "https://api.github.com/repos/symfony/translation/zipball/917b02cdc5f33e0309b8e9d33ee1480b20687413", + "reference": "917b02cdc5f33e0309b8e9d33ee1480b20687413", "shasum": "" }, "require": { @@ -7873,34 +7406,20 @@ ], "description": "Symfony Translation Component", "homepage": "https://symfony.com", - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-05-30T20:35:19+00:00" + "time": "2020-08-17T10:01:29+00:00" }, { "name": "symfony/translation-contracts", - "version": "v2.1.2", + "version": "v2.1.3", "source": { "type": "git", "url": "https://github.com/symfony/translation-contracts.git", - "reference": "e5ca07c8f817f865f618aa072c2fe8e0e637340e" + "reference": "616a9773c853097607cf9dd6577d5b143ffdcd63" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/e5ca07c8f817f865f618aa072c2fe8e0e637340e", - "reference": "e5ca07c8f817f865f618aa072c2fe8e0e637340e", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/616a9773c853097607cf9dd6577d5b143ffdcd63", + "reference": "616a9773c853097607cf9dd6577d5b143ffdcd63", "shasum": "" }, "require": { @@ -7913,6 +7432,10 @@ "extra": { "branch-alias": { "dev-master": "2.1-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" } }, "autoload": { @@ -7944,21 +7467,7 @@ "interoperability", "standards" ], - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-05-20T17:43:50+00:00" + "time": "2020-07-06T13:23:11+00:00" }, { "name": "symfony/var-dumper", @@ -8034,25 +7543,11 @@ "debug", "dump" ], - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], "time": "2020-08-17T07:42:30+00:00" }, { "name": "symfony/var-exporter", - "version": "v5.1.2", + "version": "v5.1.5", "source": { "type": "git", "url": "https://github.com/symfony/var-exporter.git", @@ -8109,41 +7604,27 @@ "instantiate", "serialize" ], - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], "time": "2020-06-07T15:42:22+00:00" }, { "name": "theseer/tokenizer", - "version": "1.1.3", + "version": "1.2.0", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9" + "reference": "75a63c33a8577608444246075ea0af0d052e452a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/11336f6f84e16a720dae9d8e6ed5019efa85a0f9", - "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/75a63c33a8577608444246075ea0af0d052e452a", + "reference": "75a63c33a8577608444246075ea0af0d052e452a", "shasum": "" }, "require": { "ext-dom": "*", "ext-tokenizer": "*", "ext-xmlwriter": "*", - "php": "^7.0" + "php": "^7.2 || ^8.0" }, "type": "library", "autoload": { @@ -8163,30 +7644,30 @@ } ], "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", - "time": "2019-06-13T22:48:21+00:00" + "time": "2020-07-12T23:59:07+00:00" }, { "name": "twig/twig", - "version": "v3.0.3", + "version": "v3.0.5", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "3b88ccd180a6b61ebb517aea3b1a8906762a1dc2" + "reference": "9b76b1535483cdf4edf01bb787b0217b62bd68a5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/3b88ccd180a6b61ebb517aea3b1a8906762a1dc2", - "reference": "3b88ccd180a6b61ebb517aea3b1a8906762a1dc2", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/9b76b1535483cdf4edf01bb787b0217b62bd68a5", + "reference": "9b76b1535483cdf4edf01bb787b0217b62bd68a5", "shasum": "" }, "require": { - "php": "^7.2.5", + "php": ">=7.2.5", "symfony/polyfill-ctype": "^1.8", "symfony/polyfill-mbstring": "^1.3" }, "require-dev": { "psr/container": "^1.0", - "symfony/phpunit-bridge": "^4.4|^5.0" + "symfony/phpunit-bridge": "^4.4.9|^5.0.9" }, "type": "library", "extra": { @@ -8225,26 +7706,26 @@ "keywords": [ "templating" ], - "time": "2020-02-11T15:33:47+00:00" + "time": "2020-08-05T15:13:19+00:00" }, { "name": "vlucas/phpdotenv", - "version": "v4.1.7", + "version": "v4.1.8", "source": { "type": "git", "url": "https://github.com/vlucas/phpdotenv.git", - "reference": "db63b2ea280fdcf13c4ca392121b0b2450b51193" + "reference": "572af79d913627a9d70374d27a6f5d689a35de32" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/db63b2ea280fdcf13c4ca392121b0b2450b51193", - "reference": "db63b2ea280fdcf13c4ca392121b0b2450b51193", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/572af79d913627a9d70374d27a6f5d689a35de32", + "reference": "572af79d913627a9d70374d27a6f5d689a35de32", "shasum": "" }, "require": { "php": "^5.5.9 || ^7.0 || ^8.0", "phpoption/phpoption": "^1.7.3", - "symfony/polyfill-ctype": "^1.16" + "symfony/polyfill-ctype": "^1.17" }, "require-dev": { "bamarni/composer-bin-plugin": "^1.4.1", @@ -8289,30 +7770,20 @@ "env", "environment" ], - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv", - "type": "tidelift" - } - ], - "time": "2020-06-07T18:25:35+00:00" + "time": "2020-07-14T19:22:52+00:00" }, { "name": "voku/portable-ascii", - "version": "1.5.2", + "version": "1.5.3", "source": { "type": "git", "url": "https://github.com/voku/portable-ascii.git", - "reference": "618631dc601d8eb6ea0a9fbf654ec82f066c4e97" + "reference": "25bcbf01678930251fd572891447d9e318a6e2b8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/voku/portable-ascii/zipball/618631dc601d8eb6ea0a9fbf654ec82f066c4e97", - "reference": "618631dc601d8eb6ea0a9fbf654ec82f066c4e97", + "url": "https://api.github.com/repos/voku/portable-ascii/zipball/25bcbf01678930251fd572891447d9e318a6e2b8", + "reference": "25bcbf01678930251fd572891447d9e318a6e2b8", "shasum": "" }, "require": { @@ -8347,42 +7818,24 @@ "clean", "php" ], - "funding": [ - { - "url": "https://www.paypal.me/moelleken", - "type": "custom" - }, - { - "url": "https://github.com/voku", - "type": "github" - }, - { - "url": "https://www.patreon.com/voku", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/voku/portable-ascii", - "type": "tidelift" - } - ], - "time": "2020-06-15T23:49:30+00:00" + "time": "2020-07-22T23:32:04+00:00" }, { "name": "webmozart/assert", - "version": "1.9.0", + "version": "1.9.1", "source": { "type": "git", "url": "https://github.com/webmozart/assert.git", - "reference": "9dc4f203e36f2b486149058bade43c851dd97451" + "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/9dc4f203e36f2b486149058bade43c851dd97451", - "reference": "9dc4f203e36f2b486149058bade43c851dd97451", + "url": "https://api.github.com/repos/webmozart/assert/zipball/bafc69caeb4d49c39fd0779086c03a3738cbb389", + "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389", "shasum": "" }, "require": { - "php": "^5.3.3 || ^7.0", + "php": "^5.3.3 || ^7.0 || ^8.0", "symfony/polyfill-ctype": "^1.8" }, "conflict": { @@ -8414,7 +7867,7 @@ "check", "validate" ], - "time": "2020-06-16T10:16:42+00:00" + "time": "2020-07-08T17:02:28+00:00" } ], "aliases": [ @@ -8423,19 +7876,12 @@ "alias_normalized": "1.6.1.0", "version": "9999999-dev", "package": "guzzlehttp/psr7" - }, - { - "alias": "1.1", - "alias_normalized": "1.1.0.0", - "version": "9999999-dev", - "package": "react/socket" } ], "minimum-stability": "dev", "stability-flags": { "clue/reactphp-sqlite": 20, - "guzzlehttp/psr7": 20, - "react/socket": 20 + "guzzlehttp/psr7": 20 }, "prefer-stable": true, "prefer-lowest": false, @@ -8443,6 +7889,5 @@ "php": "^7.3.0", "ext-json": "*" }, - "platform-dev": [], - "plugin-api-version": "1.1.0" + "platform-dev": [] } diff --git a/config/expose.php b/config/expose.php index 7835ceb..a48fa6f 100644 --- a/config/expose.php +++ b/config/expose.php @@ -151,6 +151,24 @@ return [ */ 'validate_auth_tokens' => false, + /* + |-------------------------------------------------------------------------- + | TCP Port Range + |-------------------------------------------------------------------------- + | + | Expose allows you to also share TCP ports, for example when sharing your + | local SSH server with the public. This setting allows you to define the + | port range that Expose will use to assign new ports to the users. + | + | Note: Do not use port ranges below 1024, as it might require root + | privileges to assign these ports. + | + */ + 'tcp_port_range' => [ + 'from' => 50000, + 'to' => 60000, + ], + /* |-------------------------------------------------------------------------- | Maximum connection length @@ -232,6 +250,8 @@ return [ 'subdomain_taken' => 'The chosen subdomain :subdomain is already taken. Please choose a different subdomain.', 'custom_subdomain_unauthorized' => 'You are not allowed to specify custom subdomains. Please upgrade to Expose Pro.', + + 'no_free_tcp_port_available' => 'There are no free TCP ports available on this server. Please try again later.', ], ], ]; diff --git a/database/migrations/03_add_tcp_sharing_flag_to_users_table.sql b/database/migrations/03_add_tcp_sharing_flag_to_users_table.sql new file mode 100644 index 0000000..2d180de --- /dev/null +++ b/database/migrations/03_add_tcp_sharing_flag_to_users_table.sql @@ -0,0 +1 @@ +ALTER TABLE users ADD can_share_tcp_ports BOOLEAN DEFAULT 1; diff --git a/resources/views/server/layouts/app.twig b/resources/views/server/layouts/app.twig index cd29021..d1bdadf 100644 --- a/resources/views/server/layouts/app.twig +++ b/resources/views/server/layouts/app.twig @@ -27,6 +27,12 @@ ml-8 inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium leading-5 focus:outline-none transition duration-150 ease-in-out"> Shared sites + + TCP connections + +
+
+ + + + + + + + + + + + + + + + + +
+ Local Port + + Expose Port + + Shared At +
+ @{ connection.port } + + @{ connection.shared_port } + + @{ connection.shared_at } + + Disconnect +
+
+ There are no TCP connections shared with this server yet. +
+
+
+ +{% endblock %} +{% block scripts %} + +{% endblock %} diff --git a/resources/views/server/users/index.twig b/resources/views/server/users/index.twig index fbdd6e7..22f3f2c 100644 --- a/resources/views/server/users/index.twig +++ b/resources/views/server/users/index.twig @@ -43,6 +43,25 @@ +
+ +
+
+
+ + +
+
+
+
@@ -73,6 +92,9 @@ Custom Subdomains + + TCP ports + Created At @@ -95,6 +117,14 @@ Yes + + + No + + + Yes + + @{ user.created_at } @@ -144,6 +174,7 @@ userForm: { name: '', can_specify_subdomains: true, + can_share_tcp_ports: true, errors: {}, }, paginated: {{ paginated|json_encode|raw }} @@ -186,7 +217,8 @@ }).then((data) => { if (data.user) { this.userForm.name = ''; - this.userForm.can_specify_subdomains = 0; + this.userForm.can_specify_subdomains = true; + this.userForm.can_share_tcp_ports = true; this.userForm.errors = {}; this.users.unshift(data.user); } diff --git a/tests/Feature/Server/ApiTest.php b/tests/Feature/Server/ApiTest.php index ad53dc8..620a91e 100644 --- a/tests/Feature/Server/ApiTest.php +++ b/tests/Feature/Server/ApiTest.php @@ -88,6 +88,7 @@ class ApiTest extends TestCase $this->assertSame('Marcel', $user->name); $this->assertSame([], $user->sites); + $this->assertSame([], $user->tcp_connections); } /** @test */ @@ -115,6 +116,10 @@ class ApiTest extends TestCase $connection->httpRequest = new Request('GET', '/?authToken=some-other-token'); $connectionManager->storeConnection('some-different-host.test', 'different-subdomain', $connection); + $connection = \Mockery::mock(IoConnection::class); + $connection->httpRequest = new Request('GET', '/?authToken='.$createdUser->auth_token); + $connectionManager->storeTcpConnection(2525, $connection); + /** @var Response $response */ $response = $this->await($this->browser->get('http://127.0.0.1:8080/api/users', [ 'Host' => 'expose.localhost', @@ -126,6 +131,7 @@ class ApiTest extends TestCase $users = $body->paginated->users; $this->assertCount(1, $users[0]->sites); + $this->assertCount(1, $users[0]->tcp_connections); $this->assertSame('some-host.test', $users[0]->sites[0]->host); $this->assertSame('fixed-subdomain', $users[0]->sites[0]->subdomain); } diff --git a/tests/Feature/Server/TunnelTest.php b/tests/Feature/Server/TunnelTest.php index cc612f6..73f543c 100644 --- a/tests/Feature/Server/TunnelTest.php +++ b/tests/Feature/Server/TunnelTest.php @@ -9,6 +9,7 @@ use Clue\React\Buzz\Message\ResponseException; use GuzzleHttp\Psr7\Response; use Psr\Http\Message\ServerRequestInterface; use React\Http\Server; +use React\Socket\Connection; use Tests\Feature\TestCase; class TunnelTest extends TestCase @@ -22,6 +23,9 @@ class TunnelTest extends TestCase /** @var \React\Socket\Server */ protected $testHttpServer; + /** @var \React\Socket\Server */ + protected $testTcpServer; + public function setUp(): void { parent::setUp(); @@ -42,6 +46,10 @@ class TunnelTest extends TestCase $this->testHttpServer->close(); } + if (isset($this->testTcpServer)) { + $this->testTcpServer->close(); + } + parent::tearDown(); } @@ -81,6 +89,93 @@ class TunnelTest extends TestCase $this->assertSame('Hello World!', $response->getBody()->getContents()); } + /** @test */ + public function it_sends_incoming_requests_to_the_connected_client_via_tcp() + { + $this->createTestTcpServer(); + + $this->app['config']['expose.admin.validate_auth_tokens'] = false; + + /** + * We create an expose client that connects to our server and shares + * the created test HTTP server. + */ + $client = $this->createClient(); + $response = $this->await($client->connectToServerAndShareTcp(8085)); + + /** + * Once the client is connected, we connect to the + * created tunnel. + */ + $connector = new \React\Socket\Connector($this->loop); + $connection = $this->await($connector->connect('127.0.0.1:'.$response->shared_port)); + + $this->assertInstanceOf(Connection::class, $connection); + } + + /** @test */ + public function it_rejects_tcp_sharing_if_forbidden() + { + $this->createTestTcpServer(); + + $this->app['config']['expose.admin.validate_auth_tokens'] = true; + + $response = $this->await($this->browser->post('http://127.0.0.1:8080/api/users', [ + 'Host' => 'expose.localhost', + 'Authorization' => base64_encode('username:secret'), + 'Content-Type' => 'application/json', + ], json_encode([ + 'name' => 'Marcel', + 'can_share_tcp_ports' => 0, + ]))); + + $user = json_decode($response->getBody()->getContents())->user; + + $this->expectException(\UnexpectedValueException::class); + + /** + * We create an expose client that connects to our server and shares + * the created test HTTP server. + */ + $client = $this->createClient(); + $this->await($client->connectToServerAndShareTcp(8085, $user->auth_token)); + } + + /** @test */ + public function it_allows_tcp_sharing_if_enabled_for_user() + { + $this->createTestTcpServer(); + + $this->app['config']['expose.admin.validate_auth_tokens'] = true; + + $response = $this->await($this->browser->post('http://127.0.0.1:8080/api/users', [ + 'Host' => 'expose.localhost', + 'Authorization' => base64_encode('username:secret'), + 'Content-Type' => 'application/json', + ], json_encode([ + 'name' => 'Marcel', + 'can_share_tcp_ports' => 1, + ]))); + + $user = json_decode($response->getBody()->getContents())->user; + + /** + * We create an expose client that connects to our server and shares + * the created test HTTP server. + */ + $client = $this->createClient(); + $response = $this->await($client->connectToServerAndShareTcp(8085, $user->auth_token)); + + /** + * Once the client is connected, we connect to the + * created tunnel. + */ + $connector = new \React\Socket\Connector($this->loop); + $connection = $this->await($connector->connect('127.0.0.1:'.$response->shared_port)); + + $this->assertInstanceOf(Connection::class, $connection); + } + /** @test */ public function it_rejects_clients_with_invalid_auth_tokens() { @@ -221,4 +316,15 @@ class TunnelTest extends TestCase $this->testHttpServer = new \React\Socket\Server(8085, $this->loop); $server->listen($this->testHttpServer); } + + protected function createTestTcpServer() + { + $this->testTcpServer = new \React\Socket\Server(8085, $this->loop); + + $this->testTcpServer->on('connection', function (\React\Socket\ConnectionInterface $connection) { + $connection->write('Hello '.$connection->getRemoteAddress()."!\n"); + + $connection->pipe($connection); + }); + } }