Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 35
0.00% covered (danger)
0.00%
0 / 1
CRAP
0.00% covered (danger)
0.00%
0 / 1
EprtCommand
0.00% covered (danger)
0.00%
0 / 35
0.00% covered (danger)
0.00%
0 / 1
56
0.00% covered (danger)
0.00%
0 / 1
 run
0.00% covered (danger)
0.00%
0 / 35
0.00% covered (danger)
0.00%
0 / 1
56
1<?php
2namespace Apie\FtpServer\Commands;
3
4use Apie\Core\Context\ApieContext;
5use Apie\FtpServer\Factories\ServerFactoryInterface;
6use Apie\FtpServer\Factories\SimpleFtpServerFactory;
7use Apie\FtpServer\FtpConstants;
8use Apie\FtpServer\Transfers\PortTransfer;
9use Apie\FtpServer\Transfers\TransferInterface;
10use React\Socket\ConnectionInterface;
11
12class EprtCommand implements CommandInterface
13{
14    public function run(ApieContext $apieContext, string $arg = ''): ApieContext
15    {
16        $conn = $apieContext->getContext(ConnectionInterface::class);
17        $factory = $apieContext->getContext(ServerFactoryInterface::class, false) ?? new SimpleFtpServerFactory();
18
19        // EPRT format: |af|host|port|
20        // The delimiter is the first character.
21        if ($arg === '') {
22            $conn->write("501 Syntax error in parameters or arguments\r\n");
23            return $apieContext;
24        }
25
26        $delim = $arg[0];
27        $parts = explode($delim, $arg);
28
29        if (count($parts) !== 5) {
30            $conn->write("501 Syntax error in parameters or arguments\r\n");
31            return $apieContext;
32        }
33
34        [$empty, $af, $host, $port, $empty2] = $parts;
35
36        // Only IPv4 supported
37        if ($af !== '1') {
38            $conn->write("522 Network protocol not supported (IPv6 not available)\r\n");
39            return $apieContext;
40        }
41
42        if (!filter_var($host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
43            $conn->write("501 Invalid IP address\r\n");
44            return $apieContext;
45        }
46
47        $port = (int)$port;
48        if ($port <= 0 || $port > 65535) {
49            $conn->write("501 Invalid port number\r\n");
50            return $apieContext;
51        }
52
53        $transfer = new PortTransfer($factory->createConnector(), $host, $port);
54        $transfer->connectOnly()->then(
55            function() use ($conn) {
56                $conn->write("200 EPRT command successful.\r\n");
57            },
58            function (\Throwable $error) use ($conn) {
59                error_log($error->getMessage());
60                $conn->write("425 Can't open data connection.\r\n");
61            }
62        );
63
64        return $apieContext
65            ->withContext(FtpConstants::IP, $host)
66            ->withContext(FtpConstants::PORT, $port)
67            ->withContext(TransferInterface::class, $transfer);
68    }
69}