Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Warning feature #598

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
142 changes: 120 additions & 22 deletions inc/bans.php
Expand Up @@ -112,13 +112,58 @@ static public function parse_range($mask) {

return array($ipstart, $ipend);
}

static function findWarning($ip, $get_mod_info = false) {

$query = prepare('SELECT ``bans``.*' . ($get_mod_info ? ', `username`' : '') . ' FROM ``bans``
' . ($get_mod_info ? 'LEFT JOIN ``mods`` ON ``mods``.`id` = `creator`' : '') . '
WHERE `flg_warning` = 1 AND (`ipstart` = :ip OR (:ip >= `ipstart` AND :ip <= `ipend`))');

$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::updateBansTable($warning['id'], 'seen');
} else {
if ($warning['post'])
$warning['post'] = json_decode($warning['post'], true);
$warning_list[] = $warning;
}
}

return $warning_list;
}

static public function updateBansTable($id = null, $action = null) {

switch ($action) {
case 'seen':
$query = prepare("UPDATE ``bans`` SET `seen` = 1 WHERE `id` = :id");
$query->bindValue(':id', $id, PDO::PARAM_INT);
$query->execute() or error(db_error());
break;
case 'purge_warning':
query("DELETE FROM ``bans`` WHERE `seen` = 1 AND `flg_warning` = 1 AND `expires` IS NULL AND `created` < UNIX_TIMESTAMP(NOW() - INTERVAL '2' DAY)") or error(db_error());
break;
case 'purge_ban':
query("DELETE FROM ``bans`` WHERE `seen` = 1 AND `flg_warning` = 0 AND `expires` IS NOT NULL AND `expires` < UNIX_TIMESTAMP()") or error(db_error());
break;
default:
break;
}
rebuildThemes('bans');
}


static public function find($ip, $board = false, $get_mod_info = false, $banid = null) {
global $config;

$query = prepare('SELECT ``bans``.*' . ($get_mod_info ? ', `username`' : '') . ' FROM ``bans``
' . ($get_mod_info ? 'LEFT JOIN ``mods`` ON ``mods``.`id` = `creator`' : '') . '
WHERE
WHERE `flg_warning` = 0 AND
(' . ($board !== false ? '(`board` IS NULL OR `board` = :board) AND' : '') . '
(`ipstart` = :ip OR (:ip >= `ipstart` AND :ip <= `ipend`)) OR (``bans``.id = :id))
ORDER BY `expires` IS NULL, `expires` DESC');
Expand Down Expand Up @@ -151,6 +196,7 @@ static public function find($ip, $board = false, $get_mod_info = false, $banid =
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`
WHERE `flg_warning` = 0
ORDER BY `created` DESC") or error(db_error());
$bans = $query->fetchAll(PDO::FETCH_ASSOC);

Expand Down Expand Up @@ -204,16 +250,6 @@ static public function stream_json($out = false, $filter_ips = false, $filter_st

}

static public function seen($ban_id) {
$query = query("UPDATE ``bans`` SET `seen` = 1 WHERE `id` = " . (int)$ban_id) or error(db_error());
rebuildThemes('bans');
}

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');
}

static public function delete($ban_id, $modlog = false, $boards = false, $dont_rebuild = false) {
global $config;

Expand Down Expand Up @@ -243,37 +279,90 @@ static public function delete($ban_id, $modlog = false, $boards = false, $dont_r
return true;
}

static public function new_ban($cloaked_mask, $reason, $length = false, $ban_board = false, $mod_id = false, $post = false) {
static public function new_warning($cloaked_mask, $reason, $warning_board = false, $mod_id = false, $post = false) {
global $mod, $pdo, $board;

$mask = uncloak_mask($cloaked_mask);

global $mod, $pdo, $board;

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 ``bans`` VALUES (NULL, :ipstart, :ipend, :time, :expires, :board, :mod, :reason, 0, :post)");


$query = prepare("INSERT INTO ``bans`` (`ipstart`, `ipend`, `created`, `board`, `creator`, `reason`, `seen`, `post`, `flg_warning`)
VALUES (:ipstart, :ipend, UNIX_TIMESTAMP(), :board, :creator, :reason, 0, :post, 1)");

$query->bindValue(':ipstart', $range[0]);
if ($range[1] !== false && $range[1] != $range[0])
$query->bindValue(':ipend', $range[1]);
else
$query->bindValue(':ipend', null, PDO::PARAM_NULL);

$query->bindValue(':mod', $mod_id);
$query->bindValue(':time', time());


if ($warning_board)
$query->bindValue(':board', $warning_board);
else
$query->bindValue(':board', null, PDO::PARAM_NULL);

$query->bindValue(':creator', $mod_id);

if ($reason !== '') {
$reason = escape_markup_modifiers($reason);
markup($reason);
$query->bindValue(':reason', $reason);
} else
$query->bindValue(':reason', null, PDO::PARAM_NULL);

if ($post) {
if (!isset($board['uri']))
openBoard($post['board']);

$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 in '
. ($warning_board ? '/' . $warning_board . '/' : 'all boards') .
' for ' .
(filter_var($mask, FILTER_VALIDATE_IP) !== false ? "<a href=\"?/IP/$cloaked_mask\">$cloaked_mask</a>" : $cloaked_mask) .
' (<small>#' . $pdo->lastInsertId() . '</small>)' .
' with ' . ($reason ? 'reason: ' . utf8tohtml($reason) . '' : 'no reason'));
}

return $pdo->lastInsertId();
}

static public function new_ban($cloaked_mask, $reason, $length = false, $ban_board = false, $mod_id = false, $post = false) {
$mask = uncloak_mask($cloaked_mask);

global $mod, $pdo, $board;

if ($mod_id === false) {
$mod_id = isset($mod['id']) ? $mod['id'] : -1;
}

// idk where to put this. doesnt matter
self::updateBansTable(null, 'purge_warning');

$range = self::parse_range($mask);
$mask = self::range_to_string($range);
$cloaked_mask = cloak_mask($mask);

$query = prepare("INSERT INTO ``bans``(`ipstart`, `ipend`, `created`, `expires`, `board`, `creator`, `reason`, `seen`, `post`)
VALUES (:ipstart, :ipend, UNIX_TIMESTAMP(), :expires, :board, :creator, :reason, 0, :post)");

$query->bindValue(':ipstart', $range[0]);
if ($range[1] !== false && $range[1] != $range[0])
$query->bindValue(':ipend', $range[1]);
else
$query->bindValue(':ipend', null, PDO::PARAM_NULL);

if ($length) {
if (is_int($length) || ctype_digit($length)) {
$length = time() + $length;
Expand All @@ -289,6 +378,15 @@ static public function new_ban($cloaked_mask, $reason, $length = false, $ban_boa
$query->bindValue(':board', $ban_board);
else
$query->bindValue(':board', null, PDO::PARAM_NULL);

$query->bindValue(':creator', $mod_id);

if ($reason !== '') {
$reason = escape_markup_modifiers($reason);
markup($reason);
$query->bindValue(':reason', $reason);
} else
$query->bindValue(':reason', null, PDO::PARAM_NULL);

if ($post) {
if (!isset($board['uri']))
Expand Down
20 changes: 20 additions & 0 deletions inc/config.php
Expand Up @@ -671,6 +671,13 @@
// 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
Expand Down Expand Up @@ -1425,6 +1432,8 @@
$config['mod']['link_delete'] = '[D]';
$config['mod']['link_ban'] = '[B]';
$config['mod']['link_bandelete'] = '[B&amp;D]';
$config['mod']['link_warning'] = '[W]';
$config['mod']['link_warningdelete'] = '[W&amp;D]';
$config['mod']['link_deletefile'] = '[F]';
$config['mod']['link_spoilerimage'] = '[S]';
$config['mod']['link_deletebyip'] = '[D+]';
Expand Down Expand Up @@ -1496,6 +1505,13 @@
// HTML to append to post bodies for public bans messages (where "%s" is the message).
$config['mod']['ban_message'] = '<span class="public_ban">(%s)</span>';

// 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'] = '<span class="public_warning">(%s)</span>';

// 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)
Expand Down Expand Up @@ -1568,6 +1584,10 @@
$config['mod']['delete'] = JANITOR;
// Ban a user for a post
$config['mod']['ban'] = MOD;
// Warns an IP
$config['mod']['warning'] = JANITOR;
// Warning and delete (one click; instant)
$config['mod']['warningdelete'] = &$config['mod']['bandelete'];
// Ban and delete (one click; instant)
$config['mod']['bandelete'] = MOD;
// Remove bans
Expand Down
91 changes: 82 additions & 9 deletions inc/functions.php
Expand Up @@ -841,7 +841,7 @@ function displayBan($ban) {
global $config, $board;

if (!$ban['seen']) {
Bans::seen($ban['id']);
Bans::updateBansTable($ban['id'], 'seen');
}

$ban['ip'] = $_SERVER['REMOTE_ADDR'];
Expand Down Expand Up @@ -904,6 +904,8 @@ function checkBan($board = false) {
if (event('check-ban', $board))
return true;

checkWarning($board);

$ips = array();

$ips[] = $_SERVER['REMOTE_ADDR'];
Expand All @@ -923,15 +925,15 @@ function checkBan($board = false) {
displayBan($ban);
} else {
header('Content-Type: text/json');
die(json_encode(array('error' => true, 'banned' => true)));
die(json_encode(array('error' => true, 'banned' => true, 'warned' => false)));
}
}
} else {
if (!isset($_POST['json_response'])) {
displayBan($ban);
} else {
header('Content-Type: text/json');
die(json_encode(array('error' => true, 'banned' => true)));
die(json_encode(array('error' => true, 'banned' => true, 'warned' => false)));
}
}
}
Expand All @@ -940,14 +942,85 @@ function checkBan($board = false) {
// I'm not sure where else to put this. It doesn't really matter where; it just needs to be called every
// now and then to keep the ban list tidy.
if ($config['cache']['enabled'] && $last_time_purged = cache::get('purged_bans_last')) {
if (time() - $last_time_purged < $config['purge_bans'] )
return;
if (time() - $last_time_purged > $config['purge_bans'] )
Bans::updateBansTable(null, 'purge_ban');
cache::set('purged_bans_last', time());
}

Bans::purge();

if ($config['cache']['enabled'])
cache::set('purged_bans_last', time());
}

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' => false, 'warned' => true)));
}
}
}
}

function displayWarning($warning) {
global $config, $board;

// If Warning havent benseen before mark it as seen.
if (!$warning['seen']) {
Bans::updateBansTable($warning['id'], 'seen');
}

$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!'),
'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 threadLocked($id) {
Expand Down