From 0b558de03998c40f84eed0f296104b46460a30e5 Mon Sep 17 00:00:00 2001 From: Zankaria Date: Tue, 18 Feb 2025 12:16:52 +0100 Subject: [PATCH 1/5] IpNoteQueries.php: add ip notes wrapper --- inc/Data/IpNoteQueries.php | 76 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 inc/Data/IpNoteQueries.php diff --git a/inc/Data/IpNoteQueries.php b/inc/Data/IpNoteQueries.php new file mode 100644 index 00000000..ba6fdb15 --- /dev/null +++ b/inc/Data/IpNoteQueries.php @@ -0,0 +1,76 @@ +pdo = $pdo; + $this->cache = $cache; + } + + /** + * Get all the notes relative to an IP. + * + * @param string $ip The IP of the notes. THE STRING IS NOT VALIDATED. + * @return array Returns an array of notes sorted by the most recent. Includes the username of the mods. + */ + public function getByIp(string $ip) { + $ret = $this->cache->get("ip_note_queries_$ip"); + if ($ret !== null) { + return $ret; + } + + $query = $this->pdo->prepare('SELECT `ip_notes`.*, `username` FROM `ip_notes` LEFT JOIN `mods` ON `mod` = `mods`.`id` WHERE `ip` = :ip ORDER BY `time` DESC'); + $query->bindValue(':ip', $ip); + $query->execute(); + $ret = $query->fetchAll(\PDO::FETCH_ASSOC); + + $this->cache->set("ip_note_queries_$ip", $ret); + return $ret; + } + + /** + * Creates a new note relative to the given ip. + * + * @param string $ip The IP of the note. THE STRING IS NOT VALIDATED. + * @param int $mod_id The id of the mod who created the note. + * @param string $body The text of the note. + * @return void + */ + public function add(string $ip, int $mod_id, string $body) { + $query = $this->pdo->prepare('INSERT INTO `ip_notes` (`ip`, `mod`, `time`, `body`) VALUES (:ip, :mod, :time, :body)'); + $query->bindValue(':ip', $ip); + $query->bindValue(':mod', $mod_id); + $query->bindValue(':time', time()); + $query->bindValue(':body', $body); + $query->execute(); + + $this->cache->delete("ip_note_queries_$ip"); + } + + /** + * Delete a note only if it's of a particular IP address. + * + * @param int $id The id of the note. + * @param int $ip The expected IP of the note. THE STRING IS NOT VALIDATED. + * @return bool True if any note was deleted. + */ + public function deleteWhereIp(int $id, string $ip): bool { + $query = $this->pdo->prepare('DELETE FROM `ip_notes` WHERE `ip` = :ip AND `id` = :id'); + $query->bindValue(':ip', $ip); + $query->bindValue(':id', $id); + $query->execute(); + $any = $query->rowCount() != 0; + + if ($any) { + $this->cache->delete("ip_note_queries_$ip"); + } + return $any; + } +} From 251d45070547f51030a2d7a96858aff717985edf Mon Sep 17 00:00:00 2001 From: Zankaria Date: Fri, 21 Feb 2025 11:52:19 +0100 Subject: [PATCH 2/5] context.php: provide IpNoteQueries --- inc/context.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/inc/context.php b/inc/context.php index b0ad871a..f8b00f35 100644 --- a/inc/context.php +++ b/inc/context.php @@ -2,7 +2,7 @@ namespace Vichan; use Vichan\Data\Driver\{CacheDriver, HttpDriver, ErrorLogLogDriver, FileLogDriver, LogDriver, StderrLogDriver, SyslogLogDriver}; -use Vichan\Data\ReportQueries; +use Vichan\Data\{IpNoteQueries, ReportQueries}; use Vichan\Service\HCaptchaQuery; use Vichan\Service\SecureImageCaptchaQuery; use Vichan\Service\ReCaptchaQuery; @@ -106,6 +106,7 @@ function build_context(array $config): Context { $auto_maintenance = (bool)$c->get('config')['auto_maintenance']; $pdo = $c->get(\PDO::class); return new ReportQueries($pdo, $auto_maintenance); - } + }, + IpNoteQueries::class => fn($c) => new IpNoteQueries($c->get(\PDO::class), $c->get(CacheDriver::class)), ]); } From 886fd074ed37af5dd1dd6777d251fd73f5ea4544 Mon Sep 17 00:00:00 2001 From: Zankaria Date: Tue, 18 Feb 2025 12:17:06 +0100 Subject: [PATCH 3/5] pages.php: use IpNoteQueries --- inc/mod/pages.php | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/inc/mod/pages.php b/inc/mod/pages.php index c51435fb..9fcfc7c3 100644 --- a/inc/mod/pages.php +++ b/inc/mod/pages.php @@ -3,7 +3,7 @@ * Copyright (c) 2010-2013 Tinyboard Development Group */ use Vichan\Context; -use Vichan\Data\ReportQueries; +use Vichan\Data\{IpNoteQueries, ReportQueries}; use Vichan\Functions\Format; use Vichan\Functions\Net; use Vichan\Data\Driver\CacheDriver; @@ -878,16 +878,24 @@ function mod_ip_remove_note(Context $ctx, $cloaked_ip, $id) { $ip = uncloak_ip($cloaked_ip); $config = $ctx->get('config'); - if (!hasPermission($config['mod']['remove_notes'])) + if (!hasPermission($config['mod']['remove_notes'])) { error($config['error']['noaccess']); + } - if (filter_var($ip, FILTER_VALIDATE_IP) === false) + if (\filter_var($ip, \FILTER_VALIDATE_IP) === false) { error("Invalid IP address."); + } - $query = prepare('DELETE FROM ``ip_notes`` WHERE `ip` = :ip AND `id` = :id'); - $query->bindValue(':ip', $ip); - $query->bindValue(':id', $id); - $query->execute() or error(db_error($query)); + if (!\is_numeric($id)) { + error('Invalid note ID'); + } + + $queries = $ctx->get(IpNoteQueries::class); + $deleted = $queries->deleteWhereIp((int)$id, $ip); + + if (!$deleted) { + error("Note $id does not exist for $cloaked_ip"); + } modLog("Removed a note for {$cloaked_ip}"); @@ -928,12 +936,9 @@ function mod_ip(Context $ctx, $cip) { $_POST['note'] = escape_markup_modifiers($_POST['note']); markup($_POST['note']); - $query = prepare('INSERT INTO ``ip_notes`` VALUES (NULL, :ip, :mod, :time, :body)'); - $query->bindValue(':ip', $ip); - $query->bindValue(':mod', $mod['id']); - $query->bindValue(':time', time()); - $query->bindValue(':body', $_POST['note']); - $query->execute() or error(db_error($query)); + + $note_queries = $ctx->get(IpNoteQueries::class); + $note_queries->add($ip, $mod['id'], $_POST['note']); modLog("Added a note for {$cip}"); @@ -980,9 +985,9 @@ function mod_ip(Context $ctx, $cip) { } if (hasPermission($config['mod']['view_notes'])) { - $query = prepare("SELECT ``ip_notes``.*, `username` FROM ``ip_notes`` LEFT JOIN ``mods`` ON `mod` = ``mods``.`id` WHERE `ip` = :ip ORDER BY `time` DESC"); - $query->bindValue(':ip', $ip); - $query->execute() or error(db_error($query)); + $note_queries = $ctx->get(IpNoteQueries::class); + $note_queries->getByIp($ip); + $args['notes'] = $query->fetchAll(PDO::FETCH_ASSOC); } From e7c96a34562dbd87dedcf07445de0b8dc8e90571 Mon Sep 17 00:00:00 2001 From: Zankaria Date: Fri, 21 Feb 2025 12:28:47 +0100 Subject: [PATCH 4/5] filters.php: use IpNoteQueries --- inc/filters.php | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/inc/filters.php b/inc/filters.php index 31140919..2a7c1554 100644 --- a/inc/filters.php +++ b/inc/filters.php @@ -4,6 +4,9 @@ * Copyright (c) 2010-2013 Tinyboard Development Group */ +use Vichan\Context; +use Vichan\Data\IpNoteQueries; + defined('TINYBOARD') or exit; class Filter { @@ -149,17 +152,13 @@ class Filter { } } - public function action() { + public function action(Context $ctx) { global $board; $this->add_note = isset($this->add_note) ? $this->add_note : false; if ($this->add_note) { - $query = prepare('INSERT INTO ``ip_notes`` VALUES (NULL, :ip, :mod, :time, :body)'); - $query->bindValue(':ip', $_SERVER['REMOTE_ADDR']); - $query->bindValue(':mod', -1); - $query->bindValue(':time', time()); - $query->bindValue(':body', "Autoban message: ".$this->post['body']); - $query->execute() or error(db_error($query)); + $note_queries = $ctx->get(IpNoteQueries::class); + $note_queries->add($_SERVER['REMOTE_ADDR'], -1, 'Autoban message: ' . $this->post['body']); } if (isset($this->action)) { switch($this->action) { @@ -233,7 +232,7 @@ function purge_flood_table() { query("DELETE FROM ``flood`` WHERE `time` < $time") or error(db_error()); } -function do_filters(array $post) { +function do_filters(Context $ctx, array $post) { global $config; if (!isset($config['filters']) || empty($config['filters'])) { @@ -268,7 +267,7 @@ function do_filters(array $post) { $filter = new Filter($filter_array); $filter->flood_check = $flood_check; if ($filter->check($post)) { - $filter->action(); + $filter->action($ctx); } } From 4eeabe9d28f2fa25d3dbb1a7f9f0a33a76c29770 Mon Sep 17 00:00:00 2001 From: Zankaria Date: Fri, 21 Feb 2025 12:29:06 +0100 Subject: [PATCH 5/5] post.php: pass Context to filters --- post.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/post.php b/post.php index 17ee95c5..67ea932c 100644 --- a/post.php +++ b/post.php @@ -1062,7 +1062,7 @@ if (isset($_POST['delete'])) { if (!hasPermission($config['mod']['bypass_filters'], $board['uri']) && !$dropped_post) { require_once 'inc/filters.php'; - do_filters($post); + do_filters($context, $post); } if ($post['has_file']) { @@ -1245,7 +1245,7 @@ if (isset($_POST['delete'])) { // Do filters again if OCRing if ($config['tesseract_ocr'] && !hasPermission($config['mod']['bypass_filters'], $board['uri']) && !$dropped_post) { - do_filters($post); + do_filters($context, $post); } if (!hasPermission($config['mod']['postunoriginal'], $board['uri']) && $config['robot_enable'] && checkRobot($post['body_nomarkup']) && !$dropped_post) {