Last active
March 1, 2024 12:46
-
-
Save ethaniel/bfebaaf8b65390bb3020e92f7534ee63 to your computer and use it in GitHub Desktop.
Simple FTP Server written in PHP to receive images from Hikvision cameras
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
require __DIR__ . '/vendor/autoload.php'; | |
use Amp\Loop; | |
use Amp\Socket\Socket; | |
use function Amp\asyncCall; | |
$port1 = 1021; // Primary FTP port (you list it in Hikvision), it receives FTP commands | |
$port2 = 1022; // Secondary FTP port (for receiving binary data - images) | |
$sockets = 0; | |
// this script will automatically exit if you update it | |
$mymtime = filemtime(__FILE__); | |
Loop::repeat(5000, static function () { | |
asyncCall(function () { | |
if ($GLOBALS["sockets"] == 0) { | |
clearstatcache(); | |
if (filemtime(__FILE__) != $GLOBALS["mymtime"]) { | |
echo "version changed, exiting" . PHP_EOL; | |
Loop::stop(); | |
} | |
} | |
}); | |
}); | |
// Start main loop | |
Loop::run(static function () { | |
// First port to receive FTP commands | |
asyncCall(function () { | |
$clientHandler = function (Socket $socket) { | |
$GLOBALS["sockets"]++; | |
$address = $socket->getRemoteAddress(); | |
$socket->write("220 Hello" . PHP_EOL); //yield | |
echo "starting waiting for data from " . $address->getPort() . PHP_EOL; | |
while (null !== $chunk = yield $socket->read()) { | |
$response = ""; | |
if (preg_match('#^USER#', $chunk)) { | |
$response = "331 Please specify the password."; | |
} elseif (preg_match('#^PASS#', $chunk)) { | |
$response = "230 Login successful."; | |
} elseif (preg_match('#^PWD#', $chunk)) { | |
$response = '257 "root" is cwd.'; | |
} elseif (preg_match('#^NOOP#', $chunk)) { | |
$response = '421 OK.'; | |
} elseif (preg_match('#^TYPE#', $chunk)) { | |
$response = "200 Switching to Binary mode."; | |
} elseif (preg_match('#^CWD#', $chunk)) { | |
$response = "250 Directory successfully changed."; | |
} elseif (preg_match('#^PASV#', $chunk)) { | |
$floor = floor($GLOBALS["port2"] / 256); | |
$remainder = $GLOBALS["port2"] - 256 * $floor; | |
$response = "227 Entering Passive Mode (172,17,0,2,$floor,$remainder)."; | |
} elseif (preg_match('#^PORT#', $chunk)) { | |
$response = "500 Illegal PORT command."; | |
} elseif (preg_match('#^STOR#', $chunk)) { | |
//$response = "425 Use PORT or PASV first."; | |
$response = "150 Accepted, please start" . PHP_EOL . "226 Successfully transferred"; | |
} elseif (preg_match('#^QUIT#', $chunk)) { | |
$response = "221 Goodbye."; | |
} | |
if (empty($response)) { | |
echo $address->getPort() . " < " . strlen($chunk) . " bytes"; | |
if (strlen($chunk) < 50) { | |
echo ": " . substr($chunk, 0, 10); | |
} | |
echo PHP_EOL; | |
} else { | |
echo $address->getPort() . " < " . $chunk; | |
echo $address->getPort() . " > " . $response . PHP_EOL; | |
echo PHP_EOL; | |
yield $socket->write($response . PHP_EOL); | |
} | |
} | |
$address = $socket->getRemoteAddress(); | |
echo "connection closed with " . $address->getPort() . PHP_EOL; | |
$GLOBALS["sockets"]--; | |
}; | |
$server = Amp\Socket\Server::listen('0.0.0.0:' . $GLOBALS["port1"]); | |
echo 'Listening for new connections on ' . $server->getAddress() . ' ...' . PHP_EOL; | |
while ($socket = yield $server->accept()) { | |
$address = $socket->getRemoteAddress(); | |
echo "got connection from " . $address->getPort() . PHP_EOL; | |
asyncCall($clientHandler, $socket); | |
} | |
}); | |
// The second port for receiving images | |
asyncCall(function () { | |
$uploadHandler = function (Socket $socket) { | |
$GLOBALS["sockets"]++; | |
$address = $socket->getRemoteAddress(); | |
$data = ""; | |
while (null !== $chunk = yield $socket->read()) { | |
$data .= $chunk; | |
} | |
$bytes = strlen($data); | |
$address = $socket->getRemoteAddress(); | |
echo "upload connection closed with " . $address->getPort() . " (" . $bytes . " bytes received)" . PHP_EOL; | |
if ($bytes > 1000) { | |
// bytes is your image | |
} | |
$GLOBALS["sockets"]--; | |
}; | |
$server = Amp\Socket\Server::listen('0.0.0.0:' . $GLOBALS["port2"]); | |
echo 'Listening for new connections on ' . $server->getAddress() . ' ...' . PHP_EOL; | |
while ($socket = yield $server->accept()) { | |
$address = $socket->getRemoteAddress(); | |
echo "got upload connection from " . $address->getPort() . PHP_EOL; | |
asyncCall($uploadHandler, $socket); | |
} | |
}); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment