From 738f8a455c80c0601c2782848074635726278163 Mon Sep 17 00:00:00 2001 From: netkas Date: Tue, 24 Dec 2024 15:05:35 -0500 Subject: [PATCH] Add display picture support and error code refactor --- .../ClientCommands/StorageConfiguration.php | 2 +- .../SettingsSetDisplayPicture.php | 69 ++ src/Socialbox/Classes/Utilities.php | 614 ++++++++++-------- src/Socialbox/Enums/StandardError.php | 37 +- src/Socialbox/Enums/StandardMethods.php | 2 + .../Managers/RegisteredPeerManager.php | 57 +- .../Objects/Database/RegisteredPeerRecord.php | 28 + 7 files changed, 523 insertions(+), 286 deletions(-) create mode 100644 src/Socialbox/Classes/StandardMethods/SettingsSetDisplayPicture.php diff --git a/src/Socialbox/Classes/ClientCommands/StorageConfiguration.php b/src/Socialbox/Classes/ClientCommands/StorageConfiguration.php index 913c3d4..0050418 100644 --- a/src/Socialbox/Classes/ClientCommands/StorageConfiguration.php +++ b/src/Socialbox/Classes/ClientCommands/StorageConfiguration.php @@ -37,7 +37,7 @@ */ public function getUserDisplayImagesPath(): string { - return $this->userDisplayImagesPath; + return $this->path . DIRECTORY_SEPARATOR . $this->userDisplayImagesPath; } /** diff --git a/src/Socialbox/Classes/StandardMethods/SettingsSetDisplayPicture.php b/src/Socialbox/Classes/StandardMethods/SettingsSetDisplayPicture.php new file mode 100644 index 0000000..ddd77d2 --- /dev/null +++ b/src/Socialbox/Classes/StandardMethods/SettingsSetDisplayPicture.php @@ -0,0 +1,69 @@ +containsParameter('image')) + { + return $rpcRequest->produceError(StandardError::RPC_INVALID_ARGUMENTS, "Missing 'image' parameter"); + } + + if(strlen($rpcRequest->getParameter('image')) > Configuration::getStorageConfiguration()->getUserDisplayImagesMaxSize()) + { + return $rpcRequest->produceError(StandardError::RPC_INVALID_ARGUMENTS, "Image size exceeds the maximum allowed size of " . Configuration::getStorageConfiguration()->getUserDisplayImagesMaxSize() . " bytes"); + } + + try + { + $decodedImage = base64_decode($rpcRequest->getParameter('image')); + + if($decodedImage === false) + { + return $rpcRequest->produceError(StandardError::BAD_REQUEST, "Failed to decode JPEG image base64 data"); + } + + $sanitizedImage = Utilities::resizeImage(Utilities::sanitizeJpeg($decodedImage), 126, 126); + } + catch(Exception $e) + { + throw new StandardException('Failed to process JPEG image: ' . $e->getMessage(), StandardError::BAD_REQUEST, $e); + } + + try + { + // Set the password + RegisteredPeerManager::updateDisplayPicture($request->getPeer(), $sanitizedImage); + + // Remove the SET_DISPLAY_PICTURE flag + SessionManager::removeFlags($request->getSessionUuid(), [SessionFlags::SET_DISPLAY_PICTURE]); + + // Check & update the session flow + SessionManager::updateFlow($request->getSession()); + } + catch(Exception $e) + { + throw new StandardException('Failed to update display picture: ' . $e->getMessage(), StandardError::INTERNAL_SERVER_ERROR, $e); + } + + return $rpcRequest->produceResponse(true); + } + } \ No newline at end of file diff --git a/src/Socialbox/Classes/Utilities.php b/src/Socialbox/Classes/Utilities.php index 7d1b73b..351acc5 100644 --- a/src/Socialbox/Classes/Utilities.php +++ b/src/Socialbox/Classes/Utilities.php @@ -1,287 +1,367 @@ new InvalidArgumentException("JSON decoding failed: Maximum stack depth exceeded"), - JSON_ERROR_STATE_MISMATCH => new InvalidArgumentException("JSON decoding failed: Underflow or the modes mismatch"), - JSON_ERROR_CTRL_CHAR => new InvalidArgumentException("JSON decoding failed: Unexpected control character found"), - JSON_ERROR_SYNTAX => new InvalidArgumentException("JSON decoding failed: Syntax error, malformed JSON"), - JSON_ERROR_UTF8 => new InvalidArgumentException("JSON decoding failed: Malformed UTF-8 characters, possibly incorrectly encoded"), - default => new InvalidArgumentException("JSON decoding failed: Unknown error"), - }; - } - - return $decoded; - } - - public static function jsonEncode(mixed $data): string - { - try - { - return json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR); - } - catch(JsonException $e) - { - throw new InvalidArgumentException("Failed to encode json input", $e); - } - } - - /** - * Encodes the given data in Base64. - * - * @param string $data The data to be encoded. - * @return string The Base64 encoded string. - * @throws InvalidArgumentException if the encoding fails. - */ - public static function base64encode(string $data): string - { - $encoded = base64_encode($data); - - if (!$encoded) - { - throw new InvalidArgumentException('Failed to encode data in Base64'); - } - - return $encoded; - } - - /** - * Decodes a Base64 encoded string. - * - * @param string $data The Base64 encoded data to be decoded. - * @return string The decoded data. - * @throws InvalidArgumentException If decoding fails. - */ - public static function base64decode(string $data): string - { - $decoded = base64_decode($data, true); - - if ($decoded === false) - { - throw new InvalidArgumentException('Failed to decode data from Base64'); - } - - return $decoded; - } - - /** - * Returns the request headers as an associative array - * - * @return array - */ - public static function getRequestHeaders(): array - { - // Check if function getallheaders() exists - if (function_exists('getallheaders')) - { - $headers = getallheaders(); - } - else - { - // Fallback for servers where getallheaders() is not available - $headers = []; - foreach ($_SERVER as $key => $value) - { - if (str_starts_with($key, 'HTTP_')) + throw match (json_last_error()) { - // Convert header names to the normal HTTP format - $headers[str_replace('_', '-', strtolower(substr($key, 5)))] = $value; + JSON_ERROR_DEPTH => new InvalidArgumentException("JSON decoding failed: Maximum stack depth exceeded"), + JSON_ERROR_STATE_MISMATCH => new InvalidArgumentException("JSON decoding failed: Underflow or the modes mismatch"), + JSON_ERROR_CTRL_CHAR => new InvalidArgumentException("JSON decoding failed: Unexpected control character found"), + JSON_ERROR_SYNTAX => new InvalidArgumentException("JSON decoding failed: Syntax error, malformed JSON"), + JSON_ERROR_UTF8 => new InvalidArgumentException("JSON decoding failed: Malformed UTF-8 characters, possibly incorrectly encoded"), + default => new InvalidArgumentException("JSON decoding failed: Unknown error"), + }; + } + + return $decoded; + } + + public static function jsonEncode(mixed $data): string + { + try + { + return json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR); + } + catch(JsonException $e) + { + throw new InvalidArgumentException("Failed to encode json input", $e); + } + } + + /** + * Encodes the given data in Base64. + * + * @param string $data The data to be encoded. + * @return string The Base64 encoded string. + * @throws InvalidArgumentException if the encoding fails. + */ + public static function base64encode(string $data): string + { + $encoded = base64_encode($data); + + if (!$encoded) + { + throw new InvalidArgumentException('Failed to encode data in Base64'); + } + + return $encoded; + } + + /** + * Decodes a Base64 encoded string. + * + * @param string $data The Base64 encoded data to be decoded. + * @return string The decoded data. + * @throws InvalidArgumentException If decoding fails. + */ + public static function base64decode(string $data): string + { + $decoded = base64_decode($data, true); + + if ($decoded === false) + { + throw new InvalidArgumentException('Failed to decode data from Base64'); + } + + return $decoded; + } + + /** + * Returns the request headers as an associative array + * + * @return array + */ + public static function getRequestHeaders(): array + { + // Check if function getallheaders() exists + if (function_exists('getallheaders')) + { + $headers = getallheaders(); + } + else + { + // Fallback for servers where getallheaders() is not available + $headers = []; + foreach ($_SERVER as $key => $value) + { + if (str_starts_with($key, 'HTTP_')) + { + // Convert header names to the normal HTTP format + $headers[str_replace('_', '-', strtolower(substr($key, 5)))] = $value; + } + } + } + + if($headers === false) + { + throw new RuntimeException('Failed to get request headers'); + } + + return $headers; + } + + /** + * Converts a Throwable object into a formatted string. + * + * @param Throwable $e The throwable to be converted into a string. + * @return string The formatted string representation of the throwable, including the exception class, message, file, line, and stack trace. + */ + public static function throwableToString(Throwable $e): string + { + return sprintf( + "%s: %s in %s:%d\nStack trace:\n%s", + get_class($e), + $e->getMessage(), + $e->getFile(), + $e->getLine(), + $e->getTraceAsString() + ); + } + + /** + * Generates a formatted header string. + * + * @param StandardHeaders $header The standard header object. + * @param string $value The header value to be associated with the standard header. + * @return string The formatted header string. + */ + public static function generateHeader(StandardHeaders $header, string $value): string + { + return $header->value . ': ' . $value; + } + + /** + * Generates a random string of specified length using the provided character set. + * + * @param int $int The length of the random string to be generated. + * @param string $string The character set to use for generating the random string. + * @return string The generated random string. + */ + public static function randomString(int $int, string $string): string + { + $characters = str_split($string); + $randomString = ''; + + for ($i = 0; $i < $int; $i++) + { + $randomString .= $characters[array_rand($characters)]; + } + + 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); + } + + + /** + * Sanitizes a Base64-encoded JPEG image by validating its data, decoding it, + * and re-encoding it to ensure it conforms to the JPEG format. + * + * @param string $data The Base64-encoded string potentially containing a JPEG image, + * optionally prefixed with "data:image/...;base64,". + * @return string A sanitized and re-encoded JPEG image as a binary string. + * @throws InvalidArgumentException If the input data is not valid Base64, + * does not represent an image, or is not in the JPEG format. + */ + public static function sanitizeJpeg(string $data): string + { + // Temporarily load the decoded data as an image + $tempResource = imagecreatefromstring($data); + + // Validate that the decoded data is indeed an image + if ($tempResource === false) + { + throw new InvalidArgumentException("The data does not represent a valid image."); + } + + // Validate MIME type using getimagesizefromstring + $imageInfo = getimagesizefromstring($data); + if ($imageInfo === false || $imageInfo['mime'] !== 'image/jpeg') + { + imagedestroy($tempResource); // Cleanup resources + throw new InvalidArgumentException("The image is not a valid JPEG format."); + } + + // Capture the re-encoded image in memory and return it as a string + ob_start(); // Start output buffering + $saveResult = imagejpeg($tempResource, null, 100); // Max quality, save to output buffer + imagedestroy($tempResource); // Free up memory resources + + if (!$saveResult) + { + ob_end_clean(); // Clean the output buffer if encoding failed + throw new InvalidArgumentException("Failed to encode the sanitized image."); + } + + // Return the sanitized jpeg image as the result + return ob_get_clean(); + } + + /** + * Resizes an image to a specified width and height while maintaining its aspect ratio. + * The resized image is centered on a black background matching the target dimensions. + * + * @param string $data The binary data of the source image. + * @param int $width The desired width of the resized image. + * @param int $height The desired height of the resized image. + * @return string The binary data of the resized image in PNG format. + * @throws InvalidArgumentException If the source image cannot be created from the provided data. + * @throws Exception If image processing fails during resizing. + */ + public static function resizeImage(string $data, int $width, int $height): string + { + try + { + // Create image resource from binary data + $sourceImage = imagecreatefromstring($data); + if (!$sourceImage) + { + throw new InvalidArgumentException("Failed to create image from provided data"); + } + + // Get original dimensions + $sourceWidth = imagesx($sourceImage); + $sourceHeight = imagesy($sourceImage); + + // Calculate aspect ratios + $sourceRatio = $sourceWidth / $sourceHeight; + $targetRatio = $width / $height; + + // Initialize dimensions for scaling + $scaleWidth = $width; + $scaleHeight = $height; + + // Calculate scaling dimensions to maintain aspect ratio + if ($sourceRatio > $targetRatio) + { + // Source image is wider - scale by width + $scaleHeight = $width / $sourceRatio; + } + else + { + // Source image is taller - scale by height + $scaleWidth = $height * $sourceRatio; + } + + // Create target image with desired dimensions + $targetImage = imagecreatetruecolor($width, $height); + if (!$targetImage) + { + throw new Exception("Failed to create target image"); + } + + // Fill background with black + $black = imagecolorallocate($targetImage, 0, 0, 0); + imagefill($targetImage, 0, 0, $black); + + // Calculate padding to center the scaled image + $paddingX = ($width - $scaleWidth) / 2; + $paddingY = ($height - $scaleHeight) / 2; + + // Enable alpha blending + imagealphablending($targetImage, true); + imagesavealpha($targetImage, true); + + // Resize and copy the image with high-quality resampling + if (!imagecopyresampled($targetImage, $sourceImage, (int)$paddingX, (int)$paddingY, 0, 0, (int)$scaleWidth, (int)$scaleHeight, $sourceWidth, $sourceHeight)) + { + throw new Exception("Failed to resize image"); + } + + // Start output buffering + ob_start(); + + // Output image as PNG (you can modify this to support other formats) + imagepng($targetImage); + + // Return the image data + return ob_get_clean(); + } + finally + { + if (isset($sourceImage)) + { + imagedestroy($sourceImage); + } + + if (isset($targetImage)) + { + imagedestroy($targetImage); } } } - if($headers === false) + /** + * Converts an array into a serialized string by joining the elements with a comma. + * + * @param array $list An array of elements that need to be converted to a comma-separated string. + * @return string A string representation of the array elements, joined by commas. + */ + public static function serializeList(array $list): string { - throw new RuntimeException('Failed to get request headers'); + return implode(',', $list); } - return $headers; - } - - /** - * Converts a Throwable object into a formatted string. - * - * @param Throwable $e The throwable to be converted into a string. - * @return string The formatted string representation of the throwable, including the exception class, message, file, line, and stack trace. - */ - public static function throwableToString(Throwable $e): string - { - return sprintf( - "%s: %s in %s:%d\nStack trace:\n%s", - get_class($e), - $e->getMessage(), - $e->getFile(), - $e->getLine(), - $e->getTraceAsString() - ); - } - - /** - * Generates a formatted header string. - * - * @param StandardHeaders $header The standard header object. - * @param string $value The header value to be associated with the standard header. - * @return string The formatted header string. - */ - public static function generateHeader(StandardHeaders $header, string $value): string - { - return $header->value . ': ' . $value; - } - - /** - * Generates a random string of specified length using the provided character set. - * - * @param int $int The length of the random string to be generated. - * @param string $string The character set to use for generating the random string. - * @return string The generated random string. - */ - public static function randomString(int $int, string $string): string - { - $characters = str_split($string); - $randomString = ''; - - for ($i = 0; $i < $int; $i++) + /** + * Converts a serialized string into an array by splitting the string at each comma. + * + * @param string $list A comma-separated string that needs to be converted to an array. + * @return array An array of string values obtained by splitting the input string. + */ + public static function unserializeList(string $list): array { - $randomString .= $characters[array_rand($characters)]; + return explode(',', $list); } - 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); - } - - - /** - * Sanitizes a Base64-encoded JPEG image by validating its data, decoding it, - * and re-encoding it to ensure it conforms to the JPEG format. - * - * @param string $data The Base64-encoded string potentially containing a JPEG image, - * optionally prefixed with "data:image/...;base64,". - * @return string A sanitized and re-encoded JPEG image as a binary string. - * @throws InvalidArgumentException If the input data is not valid Base64, - * does not represent an image, or is not in the JPEG format. - */ - public static function sanitizeBase64Jpeg(string $data): string - { - // Detect and strip the potential "data:image/...;base64," prefix, if present - if (str_contains($data, ',')) + /** + * Checks if the given HTTP response code indicates success or failure. + * + * @param int $responseCode The HTTP response code to check. + * @return bool True if the response code indicates success, false otherwise. + */ + public static function isSuccessCodes(int $responseCode): bool { - [, $data] = explode(',', $data, 2); + return $responseCode >= 200 && $responseCode < 300; } - - // Decode the Base64 string - $decodedData = base64_decode($data, true); - - // Check if decoding succeeded - if ($decodedData === false) - { - throw new InvalidArgumentException("Invalid Base64 data."); - } - - // Temporarily load the decoded data as an image - $tempResource = imagecreatefromstring($decodedData); - - // Validate that the decoded data is indeed an image - if ($tempResource === false) - { - throw new InvalidArgumentException("The Base64 data does not represent a valid image."); - } - - // Validate MIME type using getimagesizefromstring - $imageInfo = getimagesizefromstring($decodedData); - if ($imageInfo === false || $imageInfo['mime'] !== 'image/jpeg') - { - imagedestroy($tempResource); // Cleanup resources - throw new InvalidArgumentException("The image is not a valid JPEG format."); - } - - // Capture the re-encoded image in memory and return it as a string - ob_start(); // Start output buffering - $saveResult = imagejpeg($tempResource, null, 100); // Max quality, save to output buffer - imagedestroy($tempResource); // Free up memory resources - - if (!$saveResult) - { - ob_end_clean(); // Clean the output buffer if encoding failed - throw new InvalidArgumentException("Failed to encode the sanitized image."); - } - - // Return the sanitized jpeg image as the result - return ob_get_clean(); - } - - /** - * Converts an array into a serialized string by joining the elements with a comma. - * - * @param array $list An array of elements that need to be converted to a comma-separated string. - * @return string A string representation of the array elements, joined by commas. - */ - public static function serializeList(array $list): string - { - return implode(',', $list); - } - - /** - * Converts a serialized string into an array by splitting the string at each comma. - * - * @param string $list A comma-separated string that needs to be converted to an array. - * @return array An array of string values obtained by splitting the input string. - */ - public static function unserializeList(string $list): array - { - return explode(',', $list); - } - - /** - * Checks if the given HTTP response code indicates success or failure. - * - * @param int $responseCode The HTTP response code to check. - * @return bool True if the response code indicates success, false otherwise. - */ - public static function isSuccessCodes(int $responseCode): bool - { - return $responseCode >= 200 && $responseCode < 300; - } -} \ No newline at end of file + } \ No newline at end of file diff --git a/src/Socialbox/Enums/StandardError.php b/src/Socialbox/Enums/StandardError.php index d634a79..6b7544a 100644 --- a/src/Socialbox/Enums/StandardError.php +++ b/src/Socialbox/Enums/StandardError.php @@ -15,28 +15,31 @@ enum StandardError : int case INTERNAL_SERVER_ERROR = -2000; case SERVER_UNAVAILABLE = -2001; + // Client Errors + case BAD_REQUEST = -3000; + case METHOD_NOT_ALLOWED = -3001; + // Authentication/Cryptography Errors - case INVALID_PUBLIC_KEY = -3000; + case INVALID_PUBLIC_KEY = -4000; // * - case SESSION_REQUIRED = -3001; - case SESSION_NOT_FOUND = -3002; - case SESSION_EXPIRED = -3003; - case SESSION_DHE_REQUIRED = -3004; + case SESSION_REQUIRED = -5001; // * + case SESSION_NOT_FOUND = -5002; // * + case SESSION_EXPIRED = -5003; // * + case SESSION_DHE_REQUIRED = -5004; // * - case ALREADY_AUTHENTICATED = -3005; - case UNSUPPORTED_AUTHENTICATION_TYPE = -3006; - case AUTHENTICATION_REQUIRED = -3007; - case REGISTRATION_DISABLED = -3008; - case CAPTCHA_NOT_AVAILABLE = -3009; - case INCORRECT_CAPTCHA_ANSWER = -3010; - case CAPTCHA_EXPIRED = -3011; + case ALREADY_AUTHENTICATED = -6005; + case UNSUPPORTED_AUTHENTICATION_TYPE = -6006; + case AUTHENTICATION_REQUIRED = -6007; + case REGISTRATION_DISABLED = -6008; + case CAPTCHA_NOT_AVAILABLE = -6009; + case INCORRECT_CAPTCHA_ANSWER = -6010; + case CAPTCHA_EXPIRED = -6011; // General Error Messages - case PEER_NOT_FOUND = -4000; - case INVALID_USERNAME = -4001; - case USERNAME_ALREADY_EXISTS = -4002; - case NOT_REGISTERED = -4003; - case METHOD_NOT_ALLOWED = -4004; + case PEER_NOT_FOUND = -7000; + case INVALID_USERNAME = -7001; + case USERNAME_ALREADY_EXISTS = -7002; + case NOT_REGISTERED = -7003; /** * Returns the default generic message for the error diff --git a/src/Socialbox/Enums/StandardMethods.php b/src/Socialbox/Enums/StandardMethods.php index 0afad9c..ce0b495 100644 --- a/src/Socialbox/Enums/StandardMethods.php +++ b/src/Socialbox/Enums/StandardMethods.php @@ -10,6 +10,7 @@ use Socialbox\Classes\StandardMethods\GetSessionState; use Socialbox\Classes\StandardMethods\GetTermsOfService; use Socialbox\Classes\StandardMethods\Ping; + use Socialbox\Classes\StandardMethods\SettingsSetDisplayName; use Socialbox\Classes\StandardMethods\SettingsSetPassword; use Socialbox\Classes\StandardMethods\VerificationAnswerImageCaptcha; use Socialbox\Classes\StandardMethods\VerificationGetImageCaptcha; @@ -83,6 +84,7 @@ self::VERIFICATION_ANSWER_IMAGE_CAPTCHA => VerificationAnswerImageCaptcha::execute($request, $rpcRequest), self::SETTINGS_SET_PASSWORD => SettingsSetPassword::execute($request, $rpcRequest), + self::SETTINGS_SET_DISPLAY_NAME => SettingsSetDisplayName::execute($request, $rpcRequest), default => $rpcRequest->produceError(StandardError::METHOD_NOT_ALLOWED, sprintf("The method %s is not supported by the server", $rpcRequest->getMethod())) }; diff --git a/src/Socialbox/Managers/RegisteredPeerManager.php b/src/Socialbox/Managers/RegisteredPeerManager.php index 669ec6b..0595ae5 100644 --- a/src/Socialbox/Managers/RegisteredPeerManager.php +++ b/src/Socialbox/Managers/RegisteredPeerManager.php @@ -322,7 +322,8 @@ * Updates the display name of a registered peer based on the given unique identifier or RegisteredPeerRecord object. * * @param string|RegisteredPeerRecord $peer The unique identifier of the registered peer, or an instance of RegisteredPeerRecord. - * @param string $name The new + * @param string $name The new display name to set to the user + * @throws DatabaseOperationException Thrown if there was an error while trying to update the display name */ public static function updateDisplayName(string|RegisteredPeerRecord $peer, string $name): void { @@ -362,6 +363,60 @@ } } + /** + * Updates the display picture of a registered peer in the database. + * + * @param string|RegisteredPeerRecord $peer The unique identifier of the peer or an instance of RegisteredPeerRecord. + * @param string $displayPictureData The raw jpeg data of the display picture. + * @return void + * @throws DatabaseOperationException If there is an error during the database operation. + */ + public static function updateDisplayPicture(string|RegisteredPeerRecord $peer, string $displayPictureData): void + { + if(empty($uuid)) + { + throw new InvalidArgumentException('The display picture UUID cannot be empty'); + } + + $uuid = Uuid::v4()->toRfc4122(); + $displayPicturePath = Configuration::getStorageConfiguration()->getUserDisplayImagesPath() . DIRECTORY_SEPARATOR . $uuid . '.jpeg'; + + // Delete the file if it already exists + if(file_exists($displayPicturePath)) + { + unlink($displayPicturePath); + } + + // Write the file contents & set the permissions + file_put_contents($displayPicturePath, $displayPictureData); + chmod($displayPicturePath, 0644); + + if(is_string($peer)) + { + $peer = self::getPeer($peer); + } + + if($peer->isExternal()) + { + throw new InvalidArgumentException('Cannot update the display picture of an external peer'); + } + + Logger::getLogger()->verbose(sprintf("Updating display picture of peer %s to %s", $peer->getUuid(), $uuid)); + + try + { + $statement = Database::getConnection()->prepare('UPDATE `registered_peers` SET display_picture=? WHERE uuid=?'); + $statement->bindParam(1, $uuid); + $peerUuid = $peer->getUuid(); + $statement->bindParam(2, $peerUuid); + $statement->execute(); + } + catch(PDOException $e) + { + throw new DatabaseOperationException('Failed to update the display picture of the peer in the database', $e); + } + } + /** * Retrieves the password authentication record associated with the given unique peer identifier or a RegisteredPeerRecord object. * diff --git a/src/Socialbox/Objects/Database/RegisteredPeerRecord.php b/src/Socialbox/Objects/Database/RegisteredPeerRecord.php index f0c14b8..7d805bb 100644 --- a/src/Socialbox/Objects/Database/RegisteredPeerRecord.php +++ b/src/Socialbox/Objects/Database/RegisteredPeerRecord.php @@ -14,6 +14,7 @@ private string $username; private string $server; private ?string $displayName; + private ?string $displayPicture; /** * @var PeerFlags[] */ @@ -33,6 +34,7 @@ $this->username = $data['username']; $this->server = $data['server']; $this->displayName = $data['display_name'] ?? null; + $this->displayPicture = $data['display_picture'] ?? null; if($data['flags']) { @@ -105,16 +107,41 @@ return $this->displayName; } + /** + * Retrieves the display picture. + * + * @return string|null The display picture if set, or null otherwise. + */ + public function getDisplayPicture(): ?string + { + return $this->displayPicture; + } + + /** + * Retrieves the flags. + * + * @return PeerFlags[] The flags. + */ public function getFlags(): array { return $this->flags; } + /** + * Adds a flag to the current instance. + * + * @param PeerFlags $flag The flag to add. + */ public function flagExists(PeerFlags $flag): bool { return in_array($flag, $this->flags, true); } + /** + * Adds a flag to the current instance. + * + * @param PeerFlags $flag The flag to add. + */ public function removeFlag(PeerFlags $flag): void { $key = array_search($flag, $this->flags, true); @@ -182,6 +209,7 @@ 'username' => $this->username, 'server' => $this->server, 'display_name' => $this->displayName, + 'display_picture' => $this->displayPicture, 'flags' => PeerFlags::toString($this->flags), 'enabled' => $this->enabled, 'created' => $this->created