From bc05135566a798d0e2f922a7aa4ed3feeb835e21 Mon Sep 17 00:00:00 2001 From: netkas Date: Fri, 7 Mar 2025 15:41:28 -0500 Subject: [PATCH] Implement message count management and deletion for encryption channels https://github.com/nosial/Socialbox-PHP/issues/14 --- .../Managers/EncryptionChannelManager.php | 161 ++++++++++++++++++ 1 file changed, 161 insertions(+) diff --git a/src/Socialbox/Managers/EncryptionChannelManager.php b/src/Socialbox/Managers/EncryptionChannelManager.php index 004f4ec..ebf10bc 100644 --- a/src/Socialbox/Managers/EncryptionChannelManager.php +++ b/src/Socialbox/Managers/EncryptionChannelManager.php @@ -531,6 +531,15 @@ $messageTimestamp = time(); } + $currentMessageCount = self::getMessageCount($channelUuid); + if($currentMessageCount > Configuration::getPoliciesConfiguration()->getEncryptionChannelMaxMessages()) + { + // Delete the oldest messages to make room for the new one + self::deleteMessages($channelUuid, self::getOldestMessage($channelUuid, ( + $currentMessageCount - Configuration::getPoliciesConfiguration()->getEncryptionChannelMaxMessages() + ))); + } + try { $stmt = Database::getConnection()->prepare('INSERT INTO encryption_channels_com (uuid, channel_uuid, recipient, checksum, data, timestamp) VALUES (:uuid, :channel_uuid, :recipient, :checksum, :data, :timestamp)'); @@ -737,4 +746,156 @@ throw new DatabaseOperationException('There was an error while rejecting the message record', $e); } } + + /** + * Returns the total message count in a given channel + * + * @param string $channelUuid The channel UUID to check + * @return int The number of messages there is + * @throws DatabaseOperationException Thrown if there was a database operation error + */ + public static function getMessageCount(string $channelUuid): int + { + if(!Validator::validateUuid($channelUuid)) + { + throw new InvalidArgumentException('The given Channel UUID is not a valid V4 UUID'); + } + + try + { + $stmt = Database::getConnection()->prepare("SELECT COUNT(*) FROM encryption_channels_com WHERE channel_uuid=:channel_uuid"); + $stmt->bindParam(':channel_uuid', $channelUuid); + $stmt->execute(); + + return (int)$stmt->fetchColumn(); + } + catch(PDOException $e) + { + throw new DatabaseOperationException('There was an error while trying to retrieve the message count', $e); + } + } + + /** + * Returns an array of oldest messages in the queue, sorted by timestamp + * + * @param string $channelUuid The channel UUID to check + * @param int $amount The number of messages to retrieve + * @return string[] An array of message records + * @throws DatabaseOperationException Thrown if there was a database operation error + */ + public static function getOldestMessage(string $channelUuid, int $amount=1): array + { + if(!Validator::validateUuid($channelUuid)) + { + throw new InvalidArgumentException('The given Channel UUID is not a valid V4 UUID'); + } + + if($amount < 1) + { + throw new InvalidArgumentException('The amount of messages to retrieve cannot be less than 1'); + } + + try + { + $stmt = Database::getConnection()->prepare("SELECT uuid FROM encryption_channels_com WHERE channel_uuid=:channel_uuid AND status='SENT' ORDER BY timestamp LIMIT :amount"); + $stmt->bindParam(':channel_uuid', $channelUuid); + $stmt->bindParam(':amount', $amount, PDO::PARAM_INT); + $stmt->execute(); + + $results = $stmt->fetchAll(); + if(!$results) + { + return []; + } + + return array_map(fn($result) => $result['uuid'], $results); + } + catch(PDOException $e) + { + throw new DatabaseOperationException('There was an error while trying to retrieve the oldest message', $e); + } + } + + /** + * Deletes a message record from the database + * + * @param string $channelUuid The Unique Universal Identifier of the channel + * @param string $messageUuid The Unique Universal Identifier of the message + * @return void + * @throws DatabaseOperationException Thrown if there was an error with the database operation + */ + public static function deleteMessage(string $channelUuid, string $messageUuid): void + { + if(!Validator::validateUuid($channelUuid)) + { + throw new InvalidArgumentException('The given Channel UUID is not a valid V4 UUID'); + } + + if(!Validator::validateUuid($messageUuid)) + { + throw new InvalidArgumentException('The given Message UUID is not a valid V4 UUID'); + } + + try + { + $stmt = Database::getConnection()->prepare("DELETE FROM encryption_channels_com WHERE channel_uuid=:channel_uuid AND uuid=:message_uuid LIMIT 1"); + $stmt->bindParam(':channel_uuid', $channelUuid); + $stmt->bindParam(':message_uuid', $messageUuid); + $stmt->execute(); + } + catch(PDOException $e) + { + throw new DatabaseOperationException('There was an error while trying to delete the message record', $e); + } + } + + /** + * Deletes a batch of messages from the database + * + * @param string $channelUuid The Unique Universal Identifier of the channel + * @param array $messageUuids An array of message UUIDs to delete + * @return void + * @throws DatabaseOperationException Thrown if there was an error with the database operation + */ + public static function deleteMessages(string $channelUuid, array $messageUuids): void + { + if(count($messageUuids) === 0) + { + return; + } + elseif(count($messageUuids) === 1) + { + self::deleteMessage($channelUuid, $messageUuids[0]); + return; + } + + if(!Validator::validateUuid($channelUuid)) + { + throw new InvalidArgumentException('The given Channel UUID is not a valid V4 UUID'); + } + + if(empty($messageUuids)) + { + throw new InvalidArgumentException('The given Message UUIDs array is empty'); + } + + $placeholders = implode(',', array_fill(0, count($messageUuids), '?')); + $query = "DELETE FROM encryption_channels_com WHERE channel_uuid=:channel_uuid AND uuid IN ($placeholders)"; + + try + { + $stmt = Database::getConnection()->prepare($query); + $stmt->bindParam(':channel_uuid', $channelUuid); + foreach($messageUuids as $index => $messageUuid) + { + $stmt->bindValue($index + 1, $messageUuid); + } + + $stmt->execute(); + } + catch(PDOException $e) + { + throw new DatabaseOperationException('There was an error while deleting the message records', $e); + } + } } \ No newline at end of file