Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
88.00% covered (warning)
88.00%
22 / 25
0.00% covered (danger)
0.00%
0 / 1
CRAP
0.00% covered (danger)
0.00%
0 / 1
EpsvCommand
88.00% covered (warning)
88.00%
22 / 25
0.00% covered (danger)
0.00%
0 / 1
3.02
0.00% covered (danger)
0.00%
0 / 1
 run
88.00% covered (warning)
88.00%
22 / 25
0.00% covered (danger)
0.00%
0 / 1
3.02
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\PasvTransfer;
9use Apie\FtpServer\Transfers\TransferInterface;
10use React\Socket\ConnectionInterface;
11use RuntimeException;
12
13class EpsvCommand implements CommandInterface
14{
15    public function run(ApieContext $apieContext, string $arg = ''): ApieContext
16    {
17        $conn = $apieContext->getContext(ConnectionInterface::class);
18
19        $transfer = $apieContext->getContext(TransferInterface::class, false);
20        if ($transfer instanceof PasvTransfer) {
21            $transfer->end();
22        }
23
24        try {
25            $transfer = new PasvTransfer(
26                $apieContext->getContext(ServerFactoryInterface::class, false) ?? new SimpleFtpServerFactory(),
27                $apieContext->getContext(FtpConstants::PASV_MIN_PORT, false) ?? '49152',
28                $apieContext->getContext(FtpConstants::PASV_MAX_PORT, false) ?? '65534',
29            );
30        } catch (RuntimeException $error) {
31            error_log($error->getMessage());
32            $conn->write("522 No port number available, use PORT instead.\r\n");
33            return $apieContext;
34        }
35        $address = $transfer->getAddress();
36        $port = parse_url($address, PHP_URL_PORT);
37
38        $transfer->connectOnly()->then(
39            function() use ($conn, $port) {
40                $conn->write("229 Entering Extended Passive Mode (|||$port|)\r\n");
41            },
42            function (\Throwable $error) use ($conn) {
43                error_log($error->getMessage());
44                $conn->write("425 Can't open data connection.\r\n");
45            }
46        );
47
48        return $apieContext->withContext(TransferInterface::class, $transfer);
49    }
50}