Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
91.43% |
32 / 35 |
|
0.00% |
0 / 2 |
CRAP | |
0.00% |
0 / 1 |
| CwdCommand | |
91.43% |
32 / 35 |
|
0.00% |
0 / 2 |
12.09 | |
0.00% |
0 / 1 |
| run | |
96.15% |
25 / 26 |
|
0.00% |
0 / 1 |
7 | |||
| normalizePath | |
77.78% |
7 / 9 |
|
0.00% |
0 / 1 |
5.27 | |||
| 1 | <?php |
| 2 | namespace Apie\FtpServer\Commands; |
| 3 | |
| 4 | use Apie\ApieFileSystem\ApieFilesystem; |
| 5 | use Apie\ApieFileSystem\Virtual\VirtualFolderInterface; |
| 6 | use Apie\Core\Context\ApieContext; |
| 7 | use Apie\FtpServer\FtpConstants; |
| 8 | use React\Socket\ConnectionInterface; |
| 9 | |
| 10 | class CwdCommand implements CommandInterface |
| 11 | { |
| 12 | public function run(ApieContext $apieContext, string $arg = ''): ApieContext |
| 13 | { |
| 14 | $conn = $apieContext->getContext(ConnectionInterface::class); |
| 15 | if (!$arg) { |
| 16 | $conn->write("550 Name invalid\r\n"); |
| 17 | return $apieContext; |
| 18 | } |
| 19 | |
| 20 | $filesystem = $apieContext->getContext(ApieFilesystem::class); |
| 21 | assert($filesystem instanceof ApieFilesystem); |
| 22 | $pwd = trim($apieContext->getContext(FtpConstants::CURRENT_PWD), '/'); |
| 23 | if (str_starts_with($arg, '/')) { |
| 24 | $pwd = ''; |
| 25 | } |
| 26 | if ($arg === '.') { |
| 27 | $conn->write("250 Directory successfully changed.\r\n"); |
| 28 | return $apieContext; |
| 29 | } |
| 30 | if ($arg === '..') { |
| 31 | return (new CdupCommand())->run($apieContext, $arg); |
| 32 | } |
| 33 | $path = $this->normalizePath($pwd . '/' . $arg); |
| 34 | |
| 35 | |
| 36 | $child = $filesystem->visit($path); |
| 37 | if (!$child) { |
| 38 | $conn->write("550 Folder $path not found\r\n"); |
| 39 | return $apieContext; |
| 40 | } |
| 41 | if ($child instanceof VirtualFolderInterface) { |
| 42 | $conn->write("250 Directory successfully changed.\r\n"); |
| 43 | return $apieContext |
| 44 | ->withContext(FtpConstants::CURRENT_FOLDER, $child) |
| 45 | ->withContext(FtpConstants::CURRENT_PWD, $path); |
| 46 | } |
| 47 | |
| 48 | $conn->write("550 Failed to change directory: $path is a file.\r\n"); |
| 49 | return $apieContext; |
| 50 | } |
| 51 | |
| 52 | private function normalizePath($path) |
| 53 | { |
| 54 | $parts = explode('/', $path); |
| 55 | $stack = []; |
| 56 | |
| 57 | foreach ($parts as $part) { |
| 58 | if ($part === '' || $part === '.') { |
| 59 | continue; |
| 60 | } |
| 61 | if ($part === '..') { |
| 62 | array_pop($stack); |
| 63 | } else { |
| 64 | $stack[] = $part; |
| 65 | } |
| 66 | } |
| 67 | |
| 68 | return implode('/', $stack); |
| 69 | } |
| 70 | } |