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;
+ }
+}
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)),
]);
}
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);
}
}
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);
}
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) {