Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
94.74% covered (success)
94.74%
36 / 38
66.67% covered (warning)
66.67%
2 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
FtpServerRunner
94.74% covered (success)
94.74%
36 / 38
66.67% covered (warning)
66.67%
2 / 3
7.01
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 create
100.00% covered (success)
100.00%
22 / 22
100.00% covered (success)
100.00%
1 / 1
1
 run
86.67% covered (warning)
86.67%
13 / 15
0.00% covered (danger)
0.00%
0 / 1
5.06
1<?php
2namespace Apie\FtpServer;
3
4use Apie\Core\Context\ApieContext;
5use Apie\FtpServer\Commands\CdupCommand;
6use Apie\FtpServer\Commands\CwdCommand;
7use Apie\FtpServer\Commands\EprtCommand;
8use Apie\FtpServer\Commands\EpsvCommand;
9use Apie\FtpServer\Commands\FtpFeatureCommand;
10use Apie\FtpServer\Commands\ListCommand;
11use Apie\FtpServer\Commands\NlstCommand;
12use Apie\FtpServer\Commands\PassCommand;
13use Apie\FtpServer\Commands\PasvCommand;
14use Apie\FtpServer\Commands\PbszCommand;
15use Apie\FtpServer\Commands\PortCommand;
16use Apie\FtpServer\Commands\ProtCommand;
17use Apie\FtpServer\Commands\PwdCommand;
18use Apie\FtpServer\Commands\QuitCommand;
19use Apie\FtpServer\Commands\RetrCommand;
20use Apie\FtpServer\Commands\SiteCommand;
21use Apie\FtpServer\Commands\SystCommand;
22use Apie\FtpServer\Commands\TypeCommand;
23use Apie\FtpServer\Commands\UserCommand;
24use Apie\FtpServer\Lists\CommandHashmap;
25use Apie\FtpServer\SiteCommands\SiteCommandInterface;
26use React\Socket\ConnectionInterface;
27
28class FtpServerRunner
29{
30    public function __construct(
31        private readonly CommandHashmap $commands
32    ) {
33    }
34
35    public static function create(SiteCommandInterface... $siteCommands): self
36    {
37        return new self(
38            new CommandHashmap([
39                'CDUP' => new CdupCommand(),
40                'CWD'  => new CwdCommand(),
41                'EPRT' => new EprtCommand(),
42                'EPSV' => new EpsvCommand(),
43                'LIST' => new ListCommand(),
44                'NLST' => new NlstCommand(),
45                'PASS' => new PassCommand(),
46                'PASV' => new PasvCommand(),
47                'PBSZ' => new PbszCommand(),
48                'PORT' => new PortCommand(),
49                'PROT' => new ProtCommand(),
50                'PWD'  => new PwdCommand(),
51                'QUIT' => new QuitCommand(),
52                'RETR' => new RetrCommand(),
53                'SITE' => SiteCommand::create(...$siteCommands),
54                'SYST' => new SystCommand(),
55                'TYPE' => new TypeCommand(),
56                'USER' => new UserCommand(),
57            ])
58        );
59    }
60
61    public function run(ApieContext $apieContext, string $command, string $arguments = ''): ApieContext
62    {
63        if ($command === 'FEAT') {
64            $apieContext->getContext(ConnectionInterface::class)->write("211-Features:\r\n");
65            foreach ($this->commands as $commandName => $commandExecutable) {
66                if ($commandExecutable instanceof FtpFeatureCommand) {
67                    $helpText = implode(' ', $commandExecutable->getFeatures()->toArray());
68                    $apieContext->getContext(ConnectionInterface::class)->write("211-$commandName $helpText\r\n");
69                } else {
70                    $apieContext->getContext(ConnectionInterface::class)->write("211-$commandName\r\n");
71                }
72            }
73            $apieContext->getContext(ConnectionInterface::class)->write("211 End\r\n");
74            return $apieContext;
75        }
76        if (!isset($this->commands[$command])) {
77            $apieContext->getContext(ConnectionInterface::class)->write("502 Command not implemented\r\n");
78            error_log("Unknown command " . $command);
79            return $apieContext;
80        }
81        $commandExecutable = $this->commands[$command];
82        return $commandExecutable->run($apieContext, $arguments);
83    }
84}