From 73df37918d5298dd9e753c27f940dff71cb480b9 Mon Sep 17 00:00:00 2001 From: Zankaria Date: Tue, 15 Apr 2025 22:56:49 +0200 Subject: [PATCH] SearchQueries.php: add --- inc/Data/SearchQueries.php | 98 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 inc/Data/SearchQueries.php diff --git a/inc/Data/SearchQueries.php b/inc/Data/SearchQueries.php new file mode 100644 index 00000000..7aa7cbad --- /dev/null +++ b/inc/Data/SearchQueries.php @@ -0,0 +1,98 @@ +pdo->prepare("SELECT COUNT(2) FROM `search_queries` WHERE `ip` = :ip AND `time` > :time"); + $query->bindValue(':ip', $ip); + $query->bindValue(':time', $now - $this->range_for_single, \PDO::PARAM_INT); + $query->execute(); + if ($query->fetchColumn() > $this->queries_for_single) { + return true; + } + + $query = $this->pdo->prepare("SELECT COUNT(2) FROM `search_queries` WHERE `time` > :time"); + $query->bindValue(':time', $now - $this->range_for_all, \PDO::PARAM_INT); + $query->execute(); + if ($query->fetchColumn() > $this->queries_for_all) { + return true; + } + + $query = $this->pdo->prepare("INSERT INTO `search_queries` VALUES (:ip, :time, :query)"); + $query->bindValue(':ip', $ip); + $query->bindValue(':time', $now, \PDO::PARAM_INT); + $query->bindValue(':query', $phrase); + $query->execute(); + + if ($this->auto_gc) { + $this->purgeExpired(); + } + + return false; + } + + /** + * @param \PDO $pdo PDO to access the DB. + * @param int $queries_for_single Maximum number of queries for a single IP, in seconds. + * @param int $range_for_single Maximum age of the oldest query to consider from a single IP. + * @param int $queries_for_all Maximum number of queries for all IPs. + * @param int $range_for_all Maximum age of the oldest query to consider from all IPs, in seconds. + * @param bool $auto_gc If to run the cleanup at every check. Must be invoked from the outside otherwise. + */ + public function __construct( + \PDO $pdo, + int $queries_for_single, + int $range_for_single, + int $queries_for_all, + int $range_for_all, + bool $auto_gc + ) { + $this->pdo = $pdo; + $this->queries_for_single = $queries_for_single; + $this->range_for_single = $range_for_single; + $this->queries_for_all = $queries_for_all; + $this->range_for_all = $range_for_all; + $this->auto_gc = $auto_gc; + } + + /** + * Check if the IP-query pair overflows the limit. + * + * @param string $ip Source IP. + * @param string $phrase The search query. + * @return bool True if the request goes over the limit. + */ + public function checkFlood(string $ip, string $phrase): bool { + $this->pdo->beginTransaction(); + try { + $ret = $this->checkFloodImpl($ip, $phrase); + $this->pdo->commit(); + return $ret; + } catch (\Exception $e) { + $this->pdo->rollBack(); + throw $e; + } + } + + public function purgeExpired(): int { + // Cleanup search queries table. + $query = $this->pdo->prepare("DELETE FROM `search_queries` WHERE `time` <= :expiry_limit"); + $query->bindValue(':expiry_limit', \time() - $this->range_for_all, \PDO::PARAM_INT); + $query->execute(); + return $query->rowCount(); + } +}