Introducing IP-less mode, global new thread delay

(Not fully tested)
This commit is contained in:
Juribiyan 2018-05-28 20:46:07 +05:00
parent 2e65dd183a
commit 86d23fc4d5
9 changed files with 152 additions and 42 deletions

View File

@ -273,6 +273,7 @@ CREATE TABLE `PREFIX_posts` (
`IS_DELETED` tinyint(1) NOT NULL DEFAULT '0',
`bumped` int(20) unsigned NOT NULL DEFAULT '0',
`country` varchar(10) NOT NULL DEFAULT 'xx',
`by_new_user` TINYINT(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`boardid`,`id`),
KEY `parentid` (`parentid`),
KEY `bumped` (`bumped`),

View File

@ -0,0 +1,2 @@
ALTER TABLE `posts`
ADD COLUMN `by_new_user` TINYINT(1) NOT NULL DEFAULT '0' AFTER `country`;

View File

@ -30,11 +30,15 @@
require 'config.php';
require KU_ROOTDIR . 'inc/functions.php';
require KU_ROOTDIR . 'inc/classes/bans.class.php';
require KU_ROOTDIR . 'inc/classes/posting.class.php';
session_start();
$bans_class = new Bans();
$posting_class = new Posting();
if (isset($_POST['appealmessage']) && KU_APPEAL != '') {
$results = $tc_db->GetAll("SELECT * FROM `".KU_DBPREFIX."banlist` WHERE `type` = '0' AND `ipmd5` = '" . md5($_SERVER['REMOTE_ADDR']) . "' AND `id` = " . $tc_db->qstr($_POST['banid']) . "LIMIT 1");
$results = $tc_db->GetAll("SELECT * FROM `".KU_DBPREFIX."banlist` WHERE `type` = '0' AND `ipmd5` = '" . $posting_class->user_id_md5 . "' AND `id` = " . $tc_db->qstr($_POST['banid']) . "LIMIT 1");
if (count($results)>0) {
foreach($results AS $line) {
if ($line['appealat'] > 0 && $line['appealat'] < time()) {
@ -50,6 +54,6 @@ if (isset($_POST['appealmessage']) && KU_APPEAL != '') {
}
}
$bans_class->BanCheck($_SERVER['REMOTE_ADDR'], '', true);
$bans_class->BanCheck($posting_class->user_id, '', true);
?>

View File

@ -49,7 +49,6 @@ require KU_ROOTDIR . 'inc/classes/cloud20.class.php';
$bans_class = new Bans();
$parse_class = new Parse();
$posting_class = new Posting();
function notify($id="???", $newthreadid = '') {
$cl20 = new Cloud20();
@ -140,11 +139,11 @@ if (isset($_POST['board'])) {
// A board being supplied is required for this script to function
error_redirect(KU_WEBPATH, _gettext('No board provided'));
}
// Must be declared after board class
$posting_class = new Posting();
// Expired ban removal, and then existing ban check on the current user
$ban_result = $bans_class->BanCheck($_SERVER['REMOTE_ADDR'], $board_class->board['name']);
$ban_result = $bans_class->BanCheck($posting_class->user_id, $board_class->board['name']);
if ($ban_result && is_array($ban_result) && $_POST['AJAX']) {
exit(json_encode(array(
'error' => _gettext('YOU ARE BANNED'),
@ -195,7 +194,6 @@ if (isset($_POST['makepost'])) { // A more evident way to identify post action,
$parse_class->id = $nextid;
$ua = ($board_class->board['useragent']) ? htmlspecialchars($_SERVER['HTTP_USER_AGENT']) : false;
$dice = ($board_class->board['dice']) ? true : false;
$ipmd5 = md5($_SERVER['REMOTE_ADDR']);
// If they are just a normal user, or vip...
if ($user_authority <= 0) {
// If the thread is locked
@ -204,7 +202,7 @@ if (isset($_POST['makepost'])) { // A more evident way to identify post action,
exitWithErrorPage(_gettext('Sorry, this thread is locked and can not be replied to.'));
}
$post_message = $parse_class->ParsePost($_POST['message'], $board_class->board['name'], $thread_replyto, $board_class->board['id'], false, $ua, $dice, $ipmd5);
$post_message = $parse_class->ParsePost($_POST['message'], $board_class->board['name'], $thread_replyto, $board_class->board['id'], false, $ua, $dice, $posting_class->user_id_md5);
// Or, if they are a moderator/administrator...
}
else {
@ -219,7 +217,7 @@ if (isset($_POST['makepost'])) { // A more evident way to identify post action,
// Otherwise, parse it as usual...
}
else {
$post_message = $parse_class->ParsePost($_POST['message'], $board_class->board['name'], $thread_replyto, $board_class->board['id'], false, $ua, $dice, $ipmd5);
$post_message = $parse_class->ParsePost($_POST['message'], $board_class->board['name'], $thread_replyto, $board_class->board['id'], false, $ua, $dice, $posting_class->user_id_md5);
// (Moved) check against blacklist and detect flood
}
@ -293,7 +291,7 @@ if (isset($_POST['makepost'])) { // A more evident way to identify post action,
$lock = 0;
}
if (!$post_displaystaffstatus && $user_authority > 0) {
if (!$post_displaystaffstatus && $user_authority > 0/* && $user_authority != 3*/) {
$user_authority_display = 0;
} elseif ($user_authority > 0) {
$user_authority_display = $user_authority;
@ -385,7 +383,7 @@ if (isset($_POST['makepost'])) { // A more evident way to identify post action,
// $upload_class->file_name, $upload_class->original_file_name, $filetype_withoutdot, $upload_class->file_md5, $upload_class->imgWidth, $upload_class->imgHeight, $upload_class->file_size, $upload_class->imgWidth_thumb, $upload_class->imgHeight_thumb
$post_class = new Post(0, $board_class->board['name'], $board_class->board['id'], true);
$post_id = $post_class->Insert($thread_replyto, $post['name'], $post['tripcode'], $post['email'], $post['subject'], addslashes($post['message']), $upload_class->attachments, $post_passwordmd5, time(), time(), $_SERVER['REMOTE_ADDR'], $user_authority_display, $sticky, $lock, $board_class->board['id'], $post['country']);
$post_id = $post_class->Insert($thread_replyto, $post['name'], $post['tripcode'], $post['email'], $post['subject'], addslashes($post['message']), $upload_class->attachments, $post_passwordmd5, time(), time(), $posting_class->user_id, $user_authority_display, $sticky, $lock, $board_class->board['id'], $post['country'], $posting_class->is_new_user);
if ($user_authority > 0 && $user_authority != 3) {
$modpost_message = 'Modposted #<a href="' . KU_BOARDSFOLDER . $board_class->board['name'] . '/res/';
@ -398,6 +396,10 @@ if (isset($_POST['makepost'])) { // A more evident way to identify post action,
management_addlogentry($modpost_message, 1, md5_decrypt($_POST['modpassword'], KU_RANDOMSEED));
}
// Give persistent cookie
if ($posting_class->ipless_mode && $posting_class->need_cookie)
setcookie('I0_persistent_id', $posting_class->user_id, time() + 31556926, '/'/*, KU_DOMAIN*/);
if ($post['name_save'] && isset($_POST['name'])) {
setcookie('name', $_POST['name'], time() + 31556926, '/', KU_DOMAIN);
}
@ -408,7 +410,6 @@ if (isset($_POST['makepost'])) { // A more evident way to identify post action,
setcookie('postpassword', $_POST['postpassword'], time() + 31556926, '/');
// Determine the page from which post is getting bumbed →
$startpage = -1;
if ($thread_replyto != '0') {

View File

@ -87,7 +87,8 @@ if (!$cache_loaded) {
$cf['I0_SMILES_ENABLED'] = true;
$cf['I0_USERSMILES_ENABLED'] = true;
$cf['I0_SMILEDIR'] = 'images/smileys/'; // Make sure to set proper write rights to this directory
$cf['I0_FORCE_HTML_NOCACHE'] = true; // When AJAX-ing HTML, force no-cache (default: off (server must handle this, recommended for local debugging only))
$cf['I0_FORCE_HTML_NOCACHE'] = false; // When AJAX-ing HTML, force no-cache (default: off (server must handle this, recommended for local debugging only))
$cf['I0_IPLESS_MODE'] = 'auto'; // Whether or not cookies should be used instead of IP's (typically for TOR hidden services). Supported values: true, false and 'auto'. In auto mode, cookies will be used when client IP is 127.0.0.1
// Database
$cf['KU_DBTYPE'] = 'mysqli'; // Database type. Valid values are mysql and mysqli (reccomended for mysql).
@ -148,10 +149,12 @@ if (!$cache_loaded) {
$cf['KU_MENUSTYLESWITCHER'] = true; // Whether or not to display the different styles in a clickable switcher in the menu
// Limitations
$cf['KU_NEWTHREADDELAY'] = 30; // Minimum time in seconds a user must wait before posting a new thread again
$cf['KU_NEWTHREADDELAY'] = 300; // Minimum time in seconds a user must wait before posting a new thread again
$cf['KU_REPLYDELAY'] = 7; // Minimum time in seconds a user must wait before posting a reply again
$cf['KU_LINELENGTH'] = 150; // Used when cutting long post messages on pages and placing the message too long notification
$cf['KU_CUTPOSTS'] = false;
$cf['I0_GLOBAL_NEWTHREADDELAY'] = 0; // Minimum time in seconds a NEW user must wait before posting a new thread. Usable for limiting the speed of wipe
$cf['I0_REPLIES_TO_RECOGNIZE'] = 10; // Minimum number of replies a user must post to be recognized as not “New”. 0 means a user can lose the “New” status only through creating a new thread. Usable only if I0_GLOBAL_NEWTHREADDELAY is enabled
// Image handling
$cf['KU_THUMBWIDTH'] = 200; // Maximum thumbnail width

View File

@ -55,7 +55,7 @@ class Bans {
return $bans;
}
else {
echo $this->DisplayBannedMessage($bans);
echo $this->DisplayBannedMessage($bans, $ip);
}
die();
}
@ -99,9 +99,9 @@ class Bans {
}
/* Return the page which will inform the user a quite unfortunate message */
function DisplayBannedMessage($bans, $board='') {
function DisplayBannedMessage($bans, $ip) {
/* Set a cookie with the users current IP address in case they use a proxy to attempt to make another post */
setcookie('tc_previousip', $_SERVER['REMOTE_ADDR'], (time() + 604800), KU_BOARDSFOLDER);
setcookie('tc_previousip', $ip, (time() + 604800), KU_BOARDSFOLDER);
require_once KU_ROOTDIR . 'lib/dwoo.php';

View File

@ -441,7 +441,7 @@ class Board {
function RegenerateThreads($id = 0) {
global $tc_db, $CURRENTLOCALE;
require_once(KU_ROOTDIR."lib/dwoo.php");
if (!isset($this->dwoo)) { $this->dwoo = New Dwoo; $this->dwoo_data = new Dwoo_Data(); $this->InitializeDwoo(); }
if (!isset($this->dwoo)) { $this->dwoo = New Dwoo(); $this->dwoo_data = new Dwoo_Data(); $this->InitializeDwoo(); }
// $embeds = Array();
$numimages = 0;
$embeds = $tc_db->GetAll("SELECT * FROM `" . KU_DBPREFIX . "embeds`");
@ -1014,11 +1014,33 @@ class Post extends Board {
}
}
function Insert($parentid, $name, $tripcode, $email, $subject, $message, $attachments, $password, $timestamp, $bumped, $ip, $posterauthority, $stickied, $locked, $boardid, $country) {
function Insert($parentid, $name, $tripcode, $email, $subject, $message, $attachments, $password, $timestamp, $bumped, $ip, $posterauthority, $stickied, $locked, $boardid, $country, $is_new_user) {
global $tc_db;
$post_fields = array(
$parentid,
$boardid,
$name,
$tripcode,
$mail,
$subject,
$message,
$password,
$timestamp,
$bumped,
md5_encrypt($ip, KU_RANDOMSEED),
md5($ip),
$posterauthority,
$stickied,
$locked,
$country,
$is_new_user
);
foreach($post_fields as &$pf) {
$pf = $tc_db->qstr($pf);
}
$query = "INSERT INTO `".KU_DBPREFIX."posts`
( `parentid` , `boardid`, `name` , `tripcode` , `email` , `subject` , `message` , `password` , `timestamp` , `bumped` , `ip` , `ipmd5` , `posterauthority` , `stickied` , `locked`, `country` )
VALUES ( ".$tc_db->qstr($parentid).", ".$tc_db->qstr($boardid).", ".$tc_db->qstr($name).", ".$tc_db->qstr($tripcode).", ".$tc_db->qstr($email).", ".$tc_db->qstr($subject).", ".$tc_db->qstr($message).", ".$tc_db->qstr($password).", ".$tc_db->qstr($timestamp).", ".$tc_db->qstr($bumped).", ".$tc_db->qstr(md5_encrypt($ip, KU_RANDOMSEED)).", '".md5($ip)."', ".$tc_db->qstr($posterauthority).", ".$tc_db->qstr($stickied).", ".$tc_db->qstr($locked).", ".$tc_db->qstr($country)." )";
(`parentid` , `boardid`, `name` , `tripcode` , `email` , `subject` , `message` , `password` , `timestamp` , `bumped` , `ip` , `ipmd5` , `posterauthority` , `stickied` , `locked`, `country`, `by_new_user`)
VALUES ( ".implode(', ', $post_fields)." )";
$tc_db->Execute($query);
$id = $tc_db->Insert_Id();
$sqlerr = $tc_db->ErrorNo();

View File

@ -20,28 +20,102 @@
*/
class Posting {
function __construct() {
global $tc_db, $board_class;
$this->ipless_mode = (I0_IPLESS_MODE==true || (I0_IPLESS_MODE=='auto' && $_SERVER['REMOTE_ADDR']=='127.0.0.1'));
// Set user ID
if ($this->ipless_mode) {
if (isset($_COOKIE['I0_persistent_id'])) {
$this->user_id = $_COOKIE['I0_persistent_id'];
}
else {
$this->user_id = session_id();
$this->need_cookie = true;
$this->is_new_user = true;
}
}
else {
$this->user_id = $_SERVER['REMOTE_ADDR'];
}
$this->user_id_md5 = md5($this->user_id);
if (I0_GLOBAL_NEWTHREADDELAY > 0 && !$this->is_new_user) {
// Check if the user has created any threads on this board
$any_threads = $tc_db->GetOne("SELECT COUNT(*) FROM `posts` WHERE `boardid`=? AND `parentid`='0' AND `ipmd5`=?",
array($board_class->board['id'], $this->user_id_md5));
if ($any_threads)
$this->is_new_user = false;
elseif (I0_REPLIES_TO_RECOGNIZE) { // Check if the user has created a sufficient number of posts on this board
$post_count = $tc_db->GetOne("SELECT COUNT(*) FROM `posts` WHERE `boardid`=? AND `ipmd5`=? LIMIT ?",
array($board_class->board['id'], $this->user_id_md5, I0_REPLIES_TO_RECOGNIZE));
$this->is_new_user = ($post_count < I0_REPLIES_TO_RECOGNIZE);
}
else
$this->is_new_user = true;
}
$this->now = time();
}
function CheckReplyTime() {
global $tc_db, $board_class;
/* Get the timestamp of the last time a reply was made by this IP address */
$result = $tc_db->GetOne("SELECT MAX(timestamp) FROM `" . KU_DBPREFIX . "posts` WHERE `boardid` = " . $board_class->board['id'] . " AND `parentid` != 0 AND `ipmd5` = '" . md5($_SERVER['REMOTE_ADDR']) . "' AND `timestamp` > " . (time() - KU_REPLYDELAY));
/* If they have posted before and it was recorded... */
if (isset($result)) {
/* If the time was shorter than the minimum time distance */
if (time() - $result <= KU_REPLYDELAY) {
exitWithErrorPage(_gettext('Please wait a moment before posting again.'), _gettext('You are currently posting faster than the configured minimum post delay allows.'));
}
$result = $tc_db->GetOne("SELECT MAX(timestamp)
FROM `" . KU_DBPREFIX . "posts`
WHERE
`boardid` = " . $board_class->board['id'] . "
AND
`ipmd5` = '" . $this->user_id_md5 . "'
AND
NOT `IS_DELETED`
AND
`timestamp` > " . ($this->now - KU_REPLYDELAY));
if ($result) {
exitWithErrorPage(sprintf(_gettext('Please wait %d s before posting again.'), KU_REPLYDELAY - ($this->now - $result)),
_gettext('You are currently posting faster than the configured minimum post delay allows.'));
}
}
function CheckNewThreadTime() {
global $tc_db, $board_class;
/* Get the timestamp of the last time a new thread was made by this IP address */
$result = $tc_db->GetOne("SELECT MAX(timestamp) FROM `" . KU_DBPREFIX . "posts` WHERE `boardid` = " . $board_class->board['id'] . " AND `parentid` = 0 AND `ipmd5` = '" . md5($_SERVER['REMOTE_ADDR']) . "' AND `timestamp` > " . (time() - KU_NEWTHREADDELAY));
/* If they have posted before and it was recorded... */
if (isset($result)) {
/* If the time was shorter than the minimum time distance */
if (time() - $result <= KU_NEWTHREADDELAY) {
exitWithErrorPage(_gettext('Please wait a moment before posting again.'), _gettext('You are currently posting faster than the configured minimum post delay allows.'));
/* Check the global new thread delay */
if (I0_GLOBAL_NEWTHREADDELAY > 0 && $this->is_new_user) {
$result = $tc_db->GetOne("SELECT MAX(timestamp)
FROM `" . KU_DBPREFIX . "posts`
WHERE
`boardid` = " . $board_class->board['id'] . "
AND
`parentid` = 0
AND
`by_new_user` = '1'
AND
NOT `IS_DELETED`
AND
`timestamp` > " . ($this->now - I0_GLOBAL_NEWTHREADDELAY));
if ($result) {
exitWithErrorPage(sprintf(_gettext('Please wait %d s before posting again.'), I0_GLOBAL_NEWTHREADDELAY - ($this->now - $result)),
_gettext('Global new thread delay for new users is imposed.'));
}
}
else {
/* Get the timestamp of the last time a new thread was made by this IP address */
$result = $tc_db->GetOne("SELECT MAX(timestamp)
FROM `" . KU_DBPREFIX . "posts`
WHERE
`boardid` = " . $board_class->board['id'] . "
AND
`parentid` = 0
AND
`ipmd5` = '" . $this->user_id_md5 . "'
AND
NOT `IS_DELETED`
AND
`timestamp` > " . ($this->now - KU_NEWTHREADDELAY));
if ($result) {
exitWithErrorPage(sprintf(_gettext('Please wait %d s before posting again.'), KU_NEWTHREADDELAY - ($this->now - $result)),
_gettext('You are currently posting faster than the configured minimum post delay allows.'));
}
}
}
@ -140,8 +214,8 @@ class Posting {
WHERE `md5` = " . $tc_db->qstr($attachment['file_md5']) . "
LIMIT 1");
if (count($results) > 0) {
$bans_class->BanUser($_SERVER['REMOTE_ADDR'], 'SERVER', '1', $results[0]['bantime'], '', 'Posting a banned file.<br />' . $results[0]['description'], 0, 0, 1);
$bans_class->BanCheck($_SERVER['REMOTE_ADDR'], $board_class->board['name']);
$bans_class->BanUser($this->user_id, 'SERVER', '1', $results[0]['bantime'], '', 'Posting a banned file.<br />' . $results[0]['description'], 0, 0, 1);
$bans_class->BanCheck($this->user_id, $board_class->board['name']);
die();
// TODO: AJAX response
}
@ -308,7 +382,7 @@ class Posting {
foreach ($badlinks as $badlink) {
if (stripos($_POST['message'], $badlink) !== false) {
/* They included a blacklisted link in their post. Ban them for an hour */
$bans_class->BanUser($_SERVER['REMOTE_ADDR'], 'board.php', 1, 3600, '', _gettext('Posting a blacklisted link.') . ' (' . $badlink . ')', $_POST['message']);
$bans_class->BanUser($this->user_id, 'board.php', 1, 3600, '', _gettext('Posting a blacklisted link.') . ' (' . $badlink . ')', $_POST['message']);
exitWithErrorPage(sprintf(_gettext('Blacklisted link ( %s ) detected.'), $badlink));
}
}
@ -335,7 +409,7 @@ class Posting {
if(!strlen($msg)) return;
$lastmsg = $tc_db->GetAll("SELECT `message` FROM `".KU_DBPREFIX."posts` WHERE `ipmd5` = '" . md5($_SERVER['REMOTE_ADDR']) . "' AND `boardid`='".$boardid."' ORDER BY `timestamp` DESC LIMIT 1");
$lastmsg = $tc_db->GetAll("SELECT `message` FROM `".KU_DBPREFIX."posts` WHERE `ipmd5` = '" . $this->user_id_md5 . "' AND `boardid`='".$boardid."' ORDER BY `timestamp` DESC LIMIT 1");
$lastmsg = mb_strtolower(strip_tags(str_replace($cyr, $lat, $lastmsg[0][0])));
if($msg == $lastmsg) exitWithErrorPage(_gettext('Flood Detected'), _gettext('You are posting the same message again.'));
@ -357,7 +431,7 @@ class Posting {
foreach ($badlinks as $badlink) {
if (stripos($msg, mb_strtolower(str_replace($cyr, $lat, $badlink))) !== false) {
/* They included a blacklisted link in their post. Ban them for an hour */
$bans_class->BanUser($_SERVER['REMOTE_ADDR'], 'board.php', 0, 3600, $board, _gettext('Posting a blacklisted link.') . ' (' . $badlink . ')', $_POST['message']);
$bans_class->BanUser($this->user_id, 'board.php', 0, 3600, $board, _gettext('Posting a blacklisted link.') . ' (' . $badlink . ')', $_POST['message']);
exitWithErrorPage(sprintf(_gettext('Blacklisted link ( %s ) detected.'), $badlink));
}
}

View File

@ -862,8 +862,11 @@ msgstr "Выберите доску."
msgid "Please select only one image to upload."
msgstr "Выберите только изображение для загрузки."
msgid "Please wait a moment before posting again."
msgstr "Подождите перед отправкой нового сообщения."
msgid "Please wait %d s before posting again."
msgstr "Подождите %d с перед отправкой нового сообщения."
msgid "Global new thread delay for new users is imposed."
msgid "Действует режим глобального ограничения на создание тредов новыми пользователями."
msgid "Popular"
msgstr "Популярные"