diff --git a/src/Socialbox/Classes/ClientCommands/ConnectCommand.php b/src/Socialbox/Classes/ClientCommands/ConnectCommand.php new file mode 100644 index 0000000..e2229b8 --- /dev/null +++ b/src/Socialbox/Classes/ClientCommands/ConnectCommand.php @@ -0,0 +1,117 @@ +error('The name argument is required, this is the name of the session'); + } + + $workingDirectory = getcwd(); + + if(isset($args['directory'])) + { + if(!is_dir($args['directory'])) + { + Logger::getLogger()->error('The directory provided does not exist'); + return 1; + } + + $workingDirectory = $args['directory']; + } + + $sessionFile = $workingDirectory . DIRECTORY_SEPARATOR . Utilities::sanitizeFileName($args['name']) . '.json'; + + if(!file_exists($sessionFile)) + { + return self::createSession($args, $sessionFile); + } + + Logger::getLogger()->info(sprintf('Session file already exists at %s', $sessionFile)); + return 0; + } + + private static function createSession(array $args, string $sessionFile): int + { + if(!isset($args['domain'])) + { + Logger::getLogger()->error('The domain argument is required, this is the domain of the socialbox instance'); + return 1; + } + + try + { + $client = new SocialClient($args['domain']); + } + catch (DatabaseOperationException $e) + { + Logger::getLogger()->error('Failed to create the client session', $e); + return 1; + } + catch (ResolutionException $e) + { + Logger::getLogger()->error('Failed to resolve the domain', $e); + return 1; + } + + try + { + $keyPair = Cryptography::generateKeyPair(); + $session = $client->createSession($keyPair); + } + catch (CryptographyException | RpcException $e) + { + Logger::getLogger()->error('Failed to create the session', $e); + return 1; + } + + $sessionData = new ClientSession([ + 'domain' => $args['domain'], + 'session_uuid' => $session, + 'public_key' => $keyPair->getPublicKey(), + 'private_key' => $keyPair->getPrivateKey() + ]); + + $sessionData->save($sessionFile); + Logger::getLogger()->info(sprintf('Session created and saved to %s', $sessionFile)); + return 0; + } + + public static function getHelpMessage(): string + { + return << --domain [--directory ] + +Creates a new session with the specified name and domain. The session will be saved to the current working directory by default, or to the specified directory if provided. + +Options: + --name The name of the session to create. + --domain The domain of the socialbox instance. + --directory The directory where the session file should be saved. + +Example: + socialbox connect --name mysession --domain socialbox.example.com +HELP; + + } + + public static function getShortHelpMessage(): string + { + return 'Connect Command - Creates a new session with the specified name and domain'; + } +} \ No newline at end of file diff --git a/src/Socialbox/Classes/RpcClient.php b/src/Socialbox/Classes/RpcClient.php index 0b8963e..269e679 100644 --- a/src/Socialbox/Classes/RpcClient.php +++ b/src/Socialbox/Classes/RpcClient.php @@ -27,8 +27,8 @@ * Constructor for initializing the server connection with a given domain. * * @param string $domain The domain used to resolve the server's endpoint and public key. - * @throws DatabaseOperationException * @throws ResolutionException + * @noinspection PhpUnhandledExceptionInspection */ public function __construct(string $domain) { diff --git a/src/Socialbox/Classes/StandardMethods/VerificationGetImageCaptcha.php b/src/Socialbox/Classes/StandardMethods/VerificationGetImageCaptcha.php index b99a358..681e366 100644 --- a/src/Socialbox/Classes/StandardMethods/VerificationGetImageCaptcha.php +++ b/src/Socialbox/Classes/StandardMethods/VerificationGetImageCaptcha.php @@ -75,8 +75,8 @@ class VerificationGetImageCaptcha extends Method // Build the captcha return $rpcRequest->produceResponse(new ImageCaptcha([ - 'expires' => $captchaRecord->getExpires()->getTimestamp(), - 'image' => (new CaptchaBuilder($answer))->build()->inline()] // Returns HTML base64 encoded image of the captcha - )); + 'expires' => $captchaRecord->getExpires(), + 'image' => (new CaptchaBuilder($answer))->build()->inline() + ])); // Returns HTML base64 encoded image of the captcha } } \ No newline at end of file diff --git a/src/Socialbox/Classes/Utilities.php b/src/Socialbox/Classes/Utilities.php index 5f9a513..b538531 100644 --- a/src/Socialbox/Classes/Utilities.php +++ b/src/Socialbox/Classes/Utilities.php @@ -2,6 +2,7 @@ namespace Socialbox\Classes; +use DateTime; use InvalidArgumentException; use JsonException; use RuntimeException; @@ -170,4 +171,25 @@ class Utilities return $randomString; } + + /** + * Generates a random CRC32 hash. + * + * @return string The generated CRC32 hash as a string. + */ + public static function randomCrc32(): string + { + return hash('crc32b', uniqid()); + } + + /** + * Sanitizes a file name by removing any characters that are not alphanumeric, hyphen, or underscore. + * + * @param string $name The file name to be sanitized. + * @return string The sanitized file name. + */ + public static function sanitizeFileName(string $name): string + { + return preg_replace('/[^a-zA-Z0-9-_]/', '', $name); + } } \ No newline at end of file diff --git a/src/Socialbox/Enums/CliCommands.php b/src/Socialbox/Enums/CliCommands.php index d160006..40ef855 100644 --- a/src/Socialbox/Enums/CliCommands.php +++ b/src/Socialbox/Enums/CliCommands.php @@ -8,6 +8,7 @@ use Socialbox\Classes\CliCommands\InitializeCommand; enum CliCommands : string { case INITIALIZE = 'init'; + case CLIENT = 'client'; /** * Handles the command execution, returns the exit code. @@ -20,6 +21,7 @@ enum CliCommands : string return match ($this) { self::INITIALIZE => InitializeCommand::execute($args), + self::CLIENT => ClientCommand::execute($args) }; } public function getHelpMessage(): string diff --git a/src/Socialbox/Enums/ClientCommands.php b/src/Socialbox/Enums/ClientCommands.php new file mode 100644 index 0000000..9a1e83a --- /dev/null +++ b/src/Socialbox/Enums/ClientCommands.php @@ -0,0 +1,8 @@ +getError(), $error->getCode()->value, $e); + } } \ No newline at end of file diff --git a/src/Socialbox/Managers/CaptchaManager.php b/src/Socialbox/Managers/CaptchaManager.php index ddf62ca..f599161 100644 --- a/src/Socialbox/Managers/CaptchaManager.php +++ b/src/Socialbox/Managers/CaptchaManager.php @@ -3,7 +3,6 @@ namespace Socialbox\Managers; use DateTime; -use DateTimeInterface; use PDOException; use Socialbox\Classes\Database; use Socialbox\Classes\Logger; @@ -31,13 +30,15 @@ class CaptchaManager } $answer = Utilities::randomString(6, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'); + $current_time = (new DateTime())->setTimestamp(time()); if(!self::captchaExists($peer_uuid)) { Logger::getLogger()->debug('Creating a new captcha record for peer ' . $peer_uuid); - $statement = Database::getConnection()->prepare("INSERT INTO captcha_images (peer_uuid, answer) VALUES (?, ?)"); + $statement = Database::getConnection()->prepare("INSERT INTO captcha_images (peer_uuid, created, answer) VALUES (?, ?, ?)"); $statement->bindParam(1, $peer_uuid); - $statement->bindParam(2, $answer); + $statement->bindParam(2, $current_time); + $statement->bindParam(3, $answer); try { @@ -52,9 +53,10 @@ class CaptchaManager } Logger::getLogger()->debug('Updating an existing captcha record for peer ' . $peer_uuid); - $statement = Database::getConnection()->prepare("UPDATE captcha_images SET answer=?, status='UNSOLVED', created=NOW() WHERE peer_uuid=?"); + $statement = Database::getConnection()->prepare("UPDATE captcha_images SET answer=?, status='UNSOLVED', created=? WHERE peer_uuid=?"); $statement->bindParam(1, $answer); - $statement->bindParam(2, $peer_uuid); + $statement->bindParam(2, $current_time); + $statement->bindParam(3, $peer_uuid); try { diff --git a/src/Socialbox/Objects/Database/CaptchaRecord.php b/src/Socialbox/Objects/Database/CaptchaRecord.php index 6526e26..0e94d68 100644 --- a/src/Socialbox/Objects/Database/CaptchaRecord.php +++ b/src/Socialbox/Objects/Database/CaptchaRecord.php @@ -4,6 +4,7 @@ namespace Socialbox\Objects\Database; use DateTime; use Socialbox\Classes\Configuration; +use Socialbox\Classes\Logger; use Socialbox\Enums\Status\CaptchaStatus; use Socialbox\Interfaces\SerializableInterface; @@ -56,14 +57,14 @@ class CaptchaRecord implements SerializableInterface return $this->created; } - public function getExpires(): DateTime + public function getExpires(): int { - return $this->created->modify(sprintf("+%s seconds", Configuration::getSecurityConfiguration()->getCaptchaTtl())); + return $this->created->getTimestamp() + Configuration::getSecurityConfiguration()->getCaptchaTtl(); } public function isExpired(): bool { - return $this->getExpires() < new DateTime(); + return time() > $this->getExpires(); } /** diff --git a/src/Socialbox/SocialClient.php b/src/Socialbox/SocialClient.php new file mode 100644 index 0000000..c11c100 --- /dev/null +++ b/src/Socialbox/SocialClient.php @@ -0,0 +1,57 @@ +sendRequest(new RpcRequest('createSession', Utilities::randomCrc32(), [ + 'public_key' => $keyPair->getPublicKey() + ])); + + if($response === null) + { + throw new RpcException('Failed to create the session, no response received'); + } + + if($response instanceof RpcError) + { + throw RpcException::fromRpcError($response); + } + + $this->setSessionUuid($response->getResult()); + $this->setPrivateKey($keyPair->getPrivateKey()); + + return $response->getResult(); + } + + } \ No newline at end of file diff --git a/src/Socialbox/Socialclient.php b/src/Socialbox/Socialclient.php deleted file mode 100644 index 7e33df6..0000000 --- a/src/Socialbox/Socialclient.php +++ /dev/null @@ -1,11 +0,0 @@ -