diff --git a/inc/bans.php b/inc/bans.php
index 84d30cbf..57194d75 100644
--- a/inc/bans.php
+++ b/inc/bans.php
@@ -145,7 +145,32 @@ class Bans {
return $ban_list;
}
-
+
+ static public function findWarning($ip, $get_mod_info = false) {
+ global $config;
+
+ $query = prepare('SELECT ``warnings``.*' . ($get_mod_info ? ', `username`' : '') . ' FROM ``warnings``
+ ' . ($get_mod_info ? 'LEFT JOIN ``mods`` ON ``mods``.`id` = `creator`' : '') . '
+ WHERE `ip` = :ip');
+
+ $query->bindValue(':ip', inet_pton($ip));
+ $query->execute() or error(db_error($query));
+
+ $warning_list = array();
+
+ while ($warning = $query->fetch(PDO::FETCH_ASSOC)) {
+ if ($warning['seen']) {
+ // self::deleteWarning($warning['id']);
+ } else {
+ if ($warning['post'])
+ $warning['post'] = json_decode($warning['post'], true);
+ $warning_list[] = $warning;
+ }
+ }
+
+ return $warning_list;
+ }
+
static public function stream_json($out = false, $filter_ips = false, $filter_staff = false, $board_access = false) {
$query = query("SELECT ``bans``.*, `username` FROM ``bans``
LEFT JOIN ``mods`` ON ``mods``.`id` = `creator`
@@ -207,6 +232,10 @@ class Bans {
rebuildThemes('bans');
}
+ static public function seenWarning($warning_id) {
+ $query = query("UPDATE ``warnings`` SET `seen` = 1 WHERE `id` = " . (int)$warning_id) or error(db_error());
+ }
+
static public function purge() {
$query = query("DELETE FROM ``bans`` WHERE `expires` IS NOT NULL AND `expires` < " . time() . " AND `seen` = 1") or error(db_error());
rebuildThemes('bans');
@@ -310,4 +339,53 @@ class Bans {
return $pdo->lastInsertId();
}
+
+ static public function new_warning($cloaked_mask, $reason, $warning_board = false, $mod_id = false, $post = false) {
+ $mask = uncloak_mask($cloaked_mask);
+
+ global $mod, $pdo, $board, $config;
+
+ if ($mod_id === false) {
+ $mod_id = isset($mod['id']) ? $mod['id'] : -1;
+ }
+
+ $range = self::parse_range($mask);
+ $mask = self::range_to_string($range);
+ $cloaked_mask = cloak_mask($mask);
+
+ $query = prepare("INSERT INTO ``warnings`` VALUES (NULL, :ip, :time, :board, :mod, :reason, 0, :post)");
+
+ $query->bindValue(':ip', $range[0]);
+ $query->bindValue(':mod', $mod_id);
+ $query->bindValue(':time', time());
+
+ if ($reason !== '') {
+ $reason = escape_markup_modifiers($reason);
+ markup($reason);
+ $query->bindValue(':reason', $reason);
+ } else
+ $query->bindValue(':reason', null, PDO::PARAM_NULL);
+
+ if ($warning_board)
+ $query->bindValue(':board', $warning_board);
+ else
+ $query->bindValue(':board', null, PDO::PARAM_NULL);
+
+ if ($post) {
+ $post['board'] = $board['uri'];
+ $query->bindValue(':post', json_encode($post));
+ } else
+ $query->bindValue(':post', null, PDO::PARAM_NULL);
+
+ $query->execute() or error(db_error($query));
+
+ if (isset($mod['id']) && $mod['id'] == $mod_id) {
+ modLog('Issued a new warning for ' .
+ (filter_var($mask, FILTER_VALIDATE_IP) !== false ? "$cloaked_mask" : $cloaked_mask) .
+ ' (#' . $pdo->lastInsertId() . ')' .
+ ' with ' . ($reason ? 'reason: ' . utf8tohtml($reason) . '' : 'no reason'));
+ }
+
+ return $pdo->lastInsertId();
+ }
}
diff --git a/inc/config.php b/inc/config.php
index a0bdbf7d..0ecfc7b9 100644
--- a/inc/config.php
+++ b/inc/config.php
@@ -675,6 +675,15 @@
// Show moderator name on ban page.
$config['show_modname'] = false;
+ // Show the post the user was issued warning for on the "You were issued a warning" page.
+ $config['warning_show_post'] = &$config['ban_show_post'];
+
+ // Optional HTML to append to "You were issued a warning. For example, you could include instructions and/or
+ // a link to an email address or IRC chat room to appeal the ban.
+ $config['warning_page_extra'] = '';
+
+
+
/*
* ====================
* Markup settings
@@ -1377,6 +1386,7 @@
// Mod links (full HTML).
$config['mod']['link_delete'] = '[D]';
+ $config['mod']['link_warning'] = '[W]';
$config['mod']['link_ban'] = '[B]';
$config['mod']['link_bandelete'] = '[B&D]';
$config['mod']['link_deletefile'] = '[F]';
@@ -1451,6 +1461,13 @@
// HTML to append to post bodies for public bans messages (where "%s" is the message).
$config['mod']['ban_message'] = '(%s)';
+ // Check public warning message by default.
+ $config['mod']['check_warning_message'] = false;
+ // Default public warning message
+ $config['mod']['default_warning_message'] = _('User was warned for this post');
+ // HTML to append to post bodies for public warning messages (where "%s" is the message).
+ $config['mod']['warning_message'] = '(%s)';
+
// When moving a thread to another board and choosing to keep a "shadow thread", an automated post (with
// a capcode) will be made, linking to the new location for the thread. "%s" will be replaced with a
// standard cross-board post citation (>>>/board/xxx)
@@ -1521,6 +1538,8 @@
$config['mod']['show_ip'] = MOD;
// Delete a post
$config['mod']['delete'] = JANITOR;
+ // Warn a user for a post
+ $config['mod']['warning'] = JANITOR;
// Ban a user for a post
$config['mod']['ban'] = MOD;
// Ban and delete (one click; instant)
diff --git a/inc/functions.php b/inc/functions.php
index a5cb9f07..a82dcd64 100755
--- a/inc/functions.php
+++ b/inc/functions.php
@@ -909,10 +909,15 @@ function checkBan($board = false) {
if (!isset($_SERVER['REMOTE_ADDR'])) {
// Server misconfiguration
return;
- }
+ }
if (event('check-ban', $board))
return true;
+
+
+ // Check for Warnings
+ checkWarning($board);
+
$ips = array();
@@ -960,6 +965,89 @@ function checkBan($board = false) {
cache::set('purged_bans_last', time());
}
+
+
+
+function displayWarning($warning) {
+ global $config, $board;
+
+ if (!$warning['seen']) {
+ Bans::seenWarning($warning['id']);
+ }
+
+ $warning['ip'] = $_SERVER['REMOTE_ADDR'];
+
+ if ($warning['post'] && isset($warning['post']['board'], $warning['post']['id'])) {
+ if (openBoard($warning['post']['board'])) {
+ $query = query(sprintf("SELECT `files` FROM ``posts_%s`` WHERE `id` = " .
+ (int)$warning['post']['id'], $board['uri']));
+ if ($_post = $query->fetch(PDO::FETCH_ASSOC)) {
+ $warning['post'] = array_merge($warning['post'], $_post);
+ }
+ }
+ if ($warning['post']['thread']) {
+ $post = new Post($warning['post']);
+ } else {
+ $post = new Thread($warning['post'], null, false, false);
+ }
+ }
+
+ // Show warning page and exit
+ die(
+ Element('page.html', array(
+ 'title' => _('Warning Issued!'),
+ 'config' => $config,
+ 'boardlist' => createBoardlist(isset($mod) ? $mod : false),
+ 'body' => Element('warning.html', array(
+ 'config' => $config,
+ 'warning' => $warning,
+ 'board' => $board,
+ 'post' => isset($post) ? $post->build(true) : false
+ )
+ ))
+ ));
+}
+
+
+
+function checkWarning($board = false) {
+ global $config;
+
+ if (!isset($_SERVER['REMOTE_ADDR'])) {
+ // Server misconfiguration
+ return;
+ }
+
+
+ if (event('check-warning', $board))
+ return true;
+
+ $ips = array();
+ $ips[] = $_SERVER['REMOTE_ADDR'];
+
+ if ($config['proxy_check'] && isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
+ $ips = array_merge($ips, explode(", ", $_SERVER['HTTP_X_FORWARDED_FOR']));
+ }
+
+
+ foreach ($ips as $ip) {
+ $warnings = Bans::findWarning($ip, $config['show_modname']);
+
+ foreach ($warnings as &$warning) {
+ if (!isset($_POST['json_response'])) {
+ displayWarning($warning);
+ } else {
+ header('Content-Type: text/json');
+ die(json_encode(array('error' => true, 'banned' => true)));
+ }
+ }
+ }
+}
+
+
+
+
+
function threadLocked($id) {
global $board;
diff --git a/inc/mod/pages.php b/inc/mod/pages.php
index 07766fff..0666fd57 100644
--- a/inc/mod/pages.php
+++ b/inc/mod/pages.php
@@ -1630,6 +1630,70 @@ function mod_ban_post($board, $delete, $post, $token = false) {
mod_page(_('New ban'), 'mod/ban_form.html', $args);
}
+function mod_warning_post($board, $post, $token = false) {
+ global $config, $mod;
+
+ if (!openBoard($board))
+ error($config['error']['noboard']);
+
+ if (!hasPermission($config['mod']['warning'], $board))
+ error($config['error']['noaccess']);
+
+ $security_token = make_secure_link_token($board . '/warning/' . $post);
+
+ $query = prepare(sprintf('SELECT ' . ($config['warning_show_post'] ? '*' : '`ip`, `thread`') .
+ ' FROM ``posts_%s`` WHERE `id` = :id', $board));
+ $query->bindValue(':id', $post);
+ $query->execute() or error(db_error($query));
+ if (!$_post = $query->fetch(PDO::FETCH_ASSOC))
+ error($config['error']['404']);
+
+ $thread = $_post['thread'];
+ $ip = $_post['ip'];
+
+ if (isset($_POST['new_warning'], $_POST['reason'])) {
+ require_once 'inc/mod/ban.php';
+
+ if (isset($_POST['ip']))
+ $ip = $_POST['ip'];
+
+ Bans::new_warning($_POST['ip'], $_POST['reason'], $board,
+ false, $config['warning_show_post'] ? $_post : false);
+
+ if (isset($_POST['public_message'], $_POST['message'])) {
+ // public warning message
+ $_POST['message'] = strtolower(preg_replace('/[\r\n]/', '', $_POST['message']));
+ $query = prepare(sprintf('UPDATE ``posts_%s`` SET `body_nomarkup` = CONCAT(`body_nomarkup`, :body_nomarkup) WHERE `id` = :id', $board));
+ $query->bindValue(':id', $post);
+ $query->bindValue(':body_nomarkup', sprintf("\n
+ {% trans %}for the following reason:{% endtrans %} +
++ {{ warning.reason }} +
+ {% endif %} ++ {% trans %}Your warning was filed on{% endtrans %} + {{ warning.created|date(config.ban_date) }} + {% if config.show_modname %} + {% if warning.username %} + {% trans %}by{% endtrans %} {{ warning.username }} + {% else %} + {% trans %}by{% endtrans %} 'system' + {% endif %} + {% endif %} +
+{% trans %}Your IP address is{% endtrans %} {{ warning.ip }}.
+ + {% if config.warning_page_extra %} +{{ config.warning_page_extra }}
+ {% endif %} + + {% if post and config.warning_show_post %} +{% trans %}You were issued an warning for the following post on{% endtrans %} {{ board.url }}:
+ {{ post }} +