From 9494e0185c8661ed18c373ce46cbf9a66cb4d008 Mon Sep 17 00:00:00 2001 From: Michael Foster Date: Mon, 2 Sep 2013 09:46:42 +1000 Subject: new index on bans table --- install.sql | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'install.sql') diff --git a/install.sql b/install.sql index 1980f8bb..2978b5c3 100644 --- a/install.sql +++ b/install.sql @@ -49,7 +49,8 @@ CREATE TABLE IF NOT EXISTS `bans` ( `board` varchar(58) CHARACTER SET utf8 DEFAULT NULL, `seen` tinyint(1) NOT NULL, PRIMARY KEY (`id`), - KEY `ip` (`ip`) + KEY `ip` (`ip`), + KEY `seen` (`seen`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 AUTO_INCREMENT=1 ; -- -------------------------------------------------------- -- cgit v1.3 From af357b566133a176935eff7490f23183fb897826 Mon Sep 17 00:00:00 2001 From: Michael Foster Date: Mon, 2 Sep 2013 11:41:17 +1000 Subject: another index change --- install.php | 6 +++++- install.sql | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'install.sql') diff --git a/install.php b/install.php index 1732aa94..e8bdda8b 100644 --- a/install.php +++ b/install.php @@ -1,7 +1,7 @@ Date: Fri, 6 Sep 2013 23:09:18 +1000 Subject: Better and faster basic flood prevention, while merging it into $config['filters']. --- inc/config.php | 177 +++++++++++++++++++++++++++++++++++------------------- inc/filters.php | 109 +++++++++++++++++++++++++++++++-- inc/functions.php | 58 ++++++++---------- install.php | 17 +++++- install.sql | 21 +++++++ post.php | 23 ++++--- 6 files changed, 294 insertions(+), 111 deletions(-) (limited to 'install.sql') diff --git a/inc/config.php b/inc/config.php index e8b65967..81cae233 100644 --- a/inc/config.php +++ b/inc/config.php @@ -168,13 +168,6 @@ * ==================== */ - // Minimum time between between each post by the same IP address. - $config['flood_time'] = 10; - // Minimum time between between each post with the exact same content AND same IP address. - $config['flood_time_ip'] = 120; - // Same as above but by a different IP address. (Same content, not necessarily same IP address.) - $config['flood_time_same'] = 30; - /* * To further prevent spam and abuse, you can use DNS blacklists (DNSBL). A DNSBL is a list of IP * addresses published through the Internet Domain Name Service (DNS) either as a zone file that can be @@ -283,6 +276,118 @@ $config['recaptcha_public'] = '6LcXTcUSAAAAAKBxyFWIt2SO8jwx4W7wcSMRoN3f'; $config['recaptcha_private'] = '6LcXTcUSAAAAAOGVbVdhmEM1_SyRF4xTKe8jbzf_'; + /* + * Custom filters detect certain posts and reject/ban accordingly. They are made up of a condition and an + * action (for when ALL conditions are met). As every single post has to be put through each filter, + * having hundreds probably isn't ideal as it could slow things down. + * + * By default, the custom filters array is populated with basic flood prevention conditions. This + * includes forcing users to wait at least 5 seconds between posts. To disable (or amend) these flood + * prevention settings, you will need to empty the $config['filters'] array first. You can do so by + * adding "$config['filters'] = array();" to inc/instance-config.php. Basic flood prevention used to be + * controlled solely by config variables such as $config['flood_time'] and $config['flood_time_ip'], and + * it still is, as long as you leave the relevant $config['filters'] intact. These old config variables + * still exist for backwards-compatability and general convenience. + * + * Read more: http://tinyboard.org/docs/index.php?p=Config/Filters + */ + + // Minimum time between between each post by the same IP address. + $config['flood_time'] = 10; + // Minimum time between between each post with the exact same content AND same IP address. + $config['flood_time_ip'] = 120; + // Same as above but by a different IP address. (Same content, not necessarily same IP address.) + $config['flood_time_same'] = 30; + + // Minimum time between posts by the same IP address (all boards). + $config['filters'][] = array( + 'condition' => array( + 'flood-match' => array('ip'), // Only match IP address + 'flood-time' => &$config['flood_time'] // 10 seconds minimum + ), + 'action' => 'reject', + 'message' => &$config['error']['flood'] + ); + + // Minimum time between posts by the same IP address with the same text. + $config['filters'][] = array( + 'condition' => array( + 'flood-match' => array('ip', 'body'), // Match IP address and post body + 'flood-time' => &$config['flood_time_ip'] // 2 minutes minimum + ), + 'action' => 'reject', + 'message' => &$config['error']['flood'] + ); + + // Minimum time between posts with the same text. (Same content, but not always the same IP address.) + $config['filters'][] = array( + 'condition' => array( + 'flood-match' => array('body'), // Match IP address and post body + 'flood-time' => &$config['flood_time_same'] // 30 seconds minimum + ), + 'action' => 'reject', + 'message' => &$config['error']['flood'] + ); + + // Example: Minimum time between posts with the same file hash. + // $config['filters'][] = array( + // 'condition' => array( + // 'flood-match' => array('file'), // Match file hash + // 'flood-time' => 60 * 2 // 2 minutes minimum + // ), + // 'action' => 'reject', + // 'message' => &$config['error']['flood'] + // ); + + // An example of blocking an imaginary known spammer, who keeps posting a reply with the name "surgeon", + // ending his posts with "regards, the surgeon" or similar. + // $config['filters'][] = array( + // 'condition' => array( + // 'name' => '/^surgeon$/', + // 'body' => '/regards,\s+(the )?surgeon$/i', + // 'OP' => false + // ), + // 'action' => 'reject', + // 'message' => 'Go away, spammer.' + // ); + + // Same as above, but issuing a 3-hour ban instead of just reject the post. + // $config['filters'][] = array( + // 'condition' => array( + // 'name' => '/^surgeon$/', + // 'body' => '/regards,\s+(the )?surgeon$/i', + // 'OP' => false + // ), + // 'action' => 'ban', + // 'expires' => 60 * 60 * 3, // 3 hours + // 'reason' => 'Go away, spammer.' + // ); + + // PHP 5.3+ (anonymous functions) + // There is also a "custom" condition, making the possibilities of this feature pretty much endless. + // This is a bad example, because there is already a "name" condition built-in. + // $config['filters'][] = array( + // 'condition' => array( + // 'body' => '/h$/i', + // 'OP' => false, + // 'custom' => function($post) { + // if($post['name'] == 'Anonymous') + // return true; + // else + // return false; + // } + // ), + // 'action' => 'reject' + // ); + + // Filter flood prevention conditions ("flood-match") depend on a table which contains a cache of recent + // posts across all boards. This table is automatically purged of older posts, determining the maximum + // "age" by looking at each filter. However, when determining the maximum age, Tinyboard does not look + // outside the current board. This means that if you have a special flood condition for a specific board + // (contained in a board configuration file) which has a flood-time greater than any of those in the + // global configuration, you need to set the following variable to the maximum flood-time condition value. + // $config['flood_cache'] = 60 * 60 * 24; // 24 hours + /* * ==================== * Post settings @@ -401,57 +506,6 @@ // Require users to see the ban page at least once for a ban even if it has since expired. $config['require_ban_view'] = true; - /* - * Custom filters detect certain posts and reject/ban accordingly. They are made up of a - * condition and an action (for when ALL conditions are met). As every single post has to - * be put through each filter, having hundreds probably isn’t ideal as it could slow things down. - * - * Read more: http://tinyboard.org/docs/index.php?p=Config/Filters - * - * This used to be named $config['flood_filters'] (still exists as an alias). - */ - - // An example of blocking an imaginary known spammer, who keeps posting a reply with the name "surgeon", - // ending his posts with "regards, the surgeon" or similar. - // $config['filters'][] = array( - // 'condition' => array( - // 'name' => '/^surgeon$/', - // 'body' => '/regards,\s+(the )?surgeon$/i', - // 'OP' => false - // ), - // 'action' => 'reject', - // 'message' => 'Go away, spammer.' - // ); - - // Same as above, but issuing a 3-hour ban instead of just reject the post. - // $config['filters'][] = array( - // 'condition' => array( - // 'name' => '/^surgeon$/', - // 'body' => '/regards,\s+(the )?surgeon$/i', - // 'OP' => false - // ), - // 'action' => 'ban', - // 'expires' => 60 * 60 * 3, // 3 hours - // 'reason' => 'Go away, spammer.' - // ); - - // PHP 5.3+ (anonymous functions) - // There is also a "custom" condition, making the possibilities of this feature pretty much endless. - // This is a bad example, because there is already a "name" condition built-in. - // $config['filters'][] = array( - // 'condition' => array( - // 'body' => '/h$/i', - // 'OP' => false, - // 'custom' => function($post) { - // if($post['name'] == 'Anonymous') - // return true; - // else - // return false; - // } - // ), - // 'action' => 'reject' - // ); - /* * ==================== * Markup settings @@ -593,10 +647,6 @@ // that as a thumbnail instead of resizing/redrawing. $config['minimum_copy_resize'] = false; - // Image hashing function. There's really no reason to change this. - // sha1_file, md5_file, etc. You can also define your own similar function. - $config['file_hash'] = 'sha1_file'; - // Maximum image upload size in bytes. $config['max_filesize'] = 10 * 1024 * 1024; // 10MB // Maximum image dimensions. @@ -1156,7 +1206,8 @@ // Post bypass unoriginal content check on robot-enabled boards $config['mod']['postunoriginal'] = ADMIN; // Bypass flood check - $config['mod']['flood'] = ADMIN; + $config['mod']['bypass_filters'] = ADMIN; + $config['mod']['flood'] = &$config['mod']['bypass_filters']; // Raw HTML posting $config['mod']['rawhtml'] = ADMIN; diff --git a/inc/filters.php b/inc/filters.php index 253a86b6..81a33116 100644 --- a/inc/filters.php +++ b/inc/filters.php @@ -7,6 +7,7 @@ defined('TINYBOARD') or exit; class Filter { + public $flood_check; private $condition; public function __construct(array $arr) { @@ -22,6 +23,56 @@ class Filter { if (!is_callable($match)) error('Custom condition for filter is not callable!'); return $match($post); + case 'flood-match': + if (!is_array($match)) + error('Filter condition "flood-match" must be an array.'); + + // Filter out "flood" table entries which do not match this filter. + + $flood_check_matched = array(); + + foreach ($this->flood_check as $flood_post) { + foreach ($match as $flood_match_arg) { + switch ($flood_match_arg) { + case 'ip': + if ($flood_post['ip'] != $_SERVER['REMOTE_ADDR']) + continue 3; + break; + case 'body': + if ($flood_post['posthash'] != md5($post['body_nomarkup'])) + continue 3; + break; + case 'file': + if (!isset($post['filehash'])) + return false; + if ($flood_post['filehash'] != $post['filehash']) + continue 3; + break; + case 'board': + if ($flood_post['board'] != $post['board']) + continue 3; + break; + case 'isreply': + if ($flood_post['isreply'] == $post['op']) + continue 3; + break; + default: + error('Invalid filter flood condition: ' . $flood_match_arg); + } + } + $flood_check_matched[] = $flood_post; + } + + $this->flood_check = $flood_check_matched; + + return !empty($this->flood_check); + case 'flood-time': + foreach ($this->flood_check as $flood_post) { + if (time() - $flood_post['time'] <= $match) { + return true; + } + } + return false; case 'name': return preg_match($match, $post['name']); case 'trip': @@ -31,7 +82,7 @@ class Filter { case 'subject': return preg_match($match, $post['subject']); case 'body': - return preg_match($match, $post['body']); + return preg_match($match, $post['body_nomarkup']); case 'filename': if (!$post['has_file']) return false; @@ -126,14 +177,64 @@ class Filter { } } +function purge_flood_table() { + global $config; + + // Determine how long we need to keep a cache of posts for flood prevention. Unfortunately, it is not + // aware of flood filters in other board configurations. You can solve this problem by settings the + // config variable $config['flood_cache'] (seconds). + + if (isset($config['flood_cache'])) { + $max_time = &$config['flood_cache']; + } else { + $max_time = 0; + foreach ($config['filters'] as $filter) { + if (isset($filter['condition']['flood-time'])) + $max_time = max($max_time, $filter['condition']['flood-time']); + } + } + + $time = time() - $max_time; + + query("DELETE FROM ``flood`` WHERE ``time`` < $time") or error(db_error()); +} + function do_filters(array $post) { global $config; - if (!isset($config['filters'])) + if (!isset($config['filters']) || empty($config['filters'])) return; - foreach ($config['filters'] as $arr) { - $filter = new Filter($arr); + foreach ($config['filters'] as $filter) { + if (isset($filter['condition']['flood-match'])) { + $has_flood = true; + break; + } + } + + purge_flood_table(); + + if (isset($has_flood)) { + if ($post['has_file']) { + $query = prepare("SELECT * FROM ``flood`` WHERE `ip` = :ip OR `posthash` = :posthash OR `filehash` = :filehash"); + $query->bindValue(':ip', $_SERVER['REMOTE_ADDR']); + $query->bindValue(':posthash', md5($post['body_nomarkup'])); + $query->bindValue(':filehash', $post['filehash']); + } else { + $query = prepare("SELECT * FROM ``flood`` WHERE `ip` = :ip OR `posthash` = :posthash"); + $query->bindValue(':ip', $_SERVER['REMOTE_ADDR']); + $query->bindValue(':posthash', md5($post['body_nomarkup'])); + + } + $query->execute() or error(db_error($query)); + $flood_check = $query->fetchAll(PDO::FETCH_ASSOC); + } else { + $flood_check = false; + } + + foreach ($config['filters'] as $filter_array) { + $filter = new Filter($filter_array); + $filter->flood_check = $flood_check; if ($filter->check($post)) $filter->action(); } diff --git a/inc/functions.php b/inc/functions.php index 9601c0f8..a6963c91 100644 --- a/inc/functions.php +++ b/inc/functions.php @@ -572,30 +572,6 @@ function listBoards() { return $boards; } -function checkFlood($post) { - global $board, $config; - - $query = prepare(sprintf("SELECT COUNT(*) FROM ``posts_%s`` WHERE - (`ip` = :ip AND `time` >= :floodtime) - OR - (`ip` = :ip AND :body != '' AND `body_nomarkup` = :body AND `time` >= :floodsameiptime) - OR - (:body != '' AND `body_nomarkup` = :body AND `time` >= :floodsametime) LIMIT 1", $board['uri'])); - $query->bindValue(':ip', $_SERVER['REMOTE_ADDR']); - $query->bindValue(':body', $post['body']); - $query->bindValue(':floodtime', time()-$config['flood_time'], PDO::PARAM_INT); - $query->bindValue(':floodsameiptime', time()-$config['flood_time_ip'], PDO::PARAM_INT); - $query->bindValue(':floodsametime', time()-$config['flood_time_same'], PDO::PARAM_INT); - $query->execute() or error(db_error($query)); - - $flood = (bool) $query->fetchColumn(); - - if (event('check-flood', $post)) - return true; - - return $flood; -} - function until($timestamp) { $difference = $timestamp - time(); if ($difference < 60) { @@ -780,6 +756,22 @@ function threadExists($id) { return false; } +function insertFloodPost(array $post) { + global $board; + + $query = prepare("INSERT INTO ``flood`` VALUES (NULL, :ip, :board, :time, :posthash, :filehash, :isreply)"); + $query->bindValue(':ip', $_SERVER['REMOTE_ADDR']); + $query->bindValue(':board', $board['uri']); + $query->bindValue(':time', time()); + $query->bindValue(':posthash', md5($post['body_nomarkup'])); + if ($post['has_file']) + $query->bindValue(':filehash', $post['filehash']); + else + $query->bindValue(':filehash', null, PDO::PARAM_NULL); + $query->bindValue(':isreply', !$post['op']); + $query->execute() or error(db_error($query)); +} + function post(array $post) { global $pdo, $board; $query = prepare(sprintf("INSERT INTO ``posts_%s`` VALUES ( NULL, :thread, :subject, :email, :name, :trip, :capcode, :body, :body_nomarkup, :time, :time, :thumb, :thumbwidth, :thumbheight, :file, :width, :height, :filesize, :filename, :filehash, :password, :ip, :sticky, :locked, 0, :embed)", $board['uri'])); @@ -788,19 +780,19 @@ function post(array $post) { if (!empty($post['subject'])) { $query->bindValue(':subject', $post['subject']); } else { - $query->bindValue(':subject', NULL, PDO::PARAM_NULL); + $query->bindValue(':subject', null, PDO::PARAM_NULL); } if (!empty($post['email'])) { $query->bindValue(':email', $post['email']); } else { - $query->bindValue(':email', NULL, PDO::PARAM_NULL); + $query->bindValue(':email', null, PDO::PARAM_NULL); } if (!empty($post['trip'])) { $query->bindValue(':trip', $post['trip']); } else { - $query->bindValue(':trip', NULL, PDO::PARAM_NULL); + $query->bindValue(':trip', null, PDO::PARAM_NULL); } $query->bindValue(':name', $post['name']); @@ -811,27 +803,27 @@ function post(array $post) { $query->bindValue(':ip', isset($post['ip']) ? $post['ip'] : $_SERVER['REMOTE_ADDR']); if ($post['op'] && $post['mod'] && isset($post['sticky']) && $post['sticky']) { - $query->bindValue(':sticky', 1, PDO::PARAM_INT); + $query->bindValue(':sticky', true, PDO::PARAM_INT); } else { - $query->bindValue(':sticky', 0, PDO::PARAM_INT); + $query->bindValue(':sticky', false, PDO::PARAM_INT); } if ($post['op'] && $post['mod'] && isset($post['locked']) && $post['locked']) { - $query->bindValue(':locked', 1, PDO::PARAM_INT); + $query->bindValue(':locked', true, PDO::PARAM_INT); } else { - $query->bindValue(':locked', 0, PDO::PARAM_INT); + $query->bindValue(':locked', false, PDO::PARAM_INT); } if ($post['mod'] && isset($post['capcode']) && $post['capcode']) { $query->bindValue(':capcode', $post['capcode'], PDO::PARAM_INT); } else { - $query->bindValue(':capcode', NULL, PDO::PARAM_NULL); + $query->bindValue(':capcode', null, PDO::PARAM_NULL); } if (!empty($post['embed'])) { $query->bindValue(':embed', $post['embed']); } else { - $query->bindValue(':embed', NULL, PDO::PARAM_NULL); + $query->bindValue(':embed', null, PDO::PARAM_NULL); } if ($post['op']) { diff --git a/install.php b/install.php index e8bdda8b..0011a274 100644 --- a/install.php +++ b/install.php @@ -1,7 +1,7 @@ Date: Fri, 6 Sep 2013 23:10:25 +1000 Subject: CHARACTER SET not needed here --- install.php | 2 +- install.sql | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'install.sql') diff --git a/install.php b/install.php index 0011a274..45244b90 100644 --- a/install.php +++ b/install.php @@ -395,7 +395,7 @@ if (file_exists($config['has_installed'])) { case 'v0.9.6-dev-18': query("CREATE TABLE IF NOT EXISTS ``flood`` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, - `ip` varchar(39) CHARACTER SET ascii NOT NULL, + `ip` varchar(39) NOT NULL, `board` varchar(58) CHARACTER SET utf8 NOT NULL, `time` int(11) NOT NULL, `posthash` char(32) NOT NULL, diff --git a/install.sql b/install.sql index 3c8cc886..25c0a0f1 100644 --- a/install.sql +++ b/install.sql @@ -253,7 +253,7 @@ CREATE TABLE IF NOT EXISTS `theme_settings` ( CREATE TABLE IF NOT EXISTS `flood` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, - `ip` varchar(39) CHARACTER SET ascii NOT NULL, + `ip` varchar(39) NOT NULL, `board` varchar(58) CHARACTER SET utf8 NOT NULL, `time` int(11) NOT NULL, `posthash` char(32) NOT NULL, -- cgit v1.3 From eea4e42609e319b38f14bca9f31cc3327ffaf175 Mon Sep 17 00:00:00 2001 From: Michael Foster Date: Mon, 9 Sep 2013 01:33:51 +1000 Subject: Add ability to create custom user/permissions groups --- inc/config.php | 34 ++++++++++++++++++-------------- inc/functions.php | 9 +++++++++ inc/mod/config-editor.php | 11 ++++++----- inc/mod/pages.php | 42 ++++++++++++++++++++++++++++++---------- install.php | 7 ++++++- install.sql | 4 ++-- templates/mod/config-editor.html | 9 +++++---- templates/mod/user.html | 20 +++++++------------ templates/mod/users.html | 11 ++++++----- 9 files changed, 93 insertions(+), 54 deletions(-) (limited to 'install.sql') diff --git a/inc/config.php b/inc/config.php index 84222009..897c8a42 100644 --- a/inc/config.php +++ b/inc/config.php @@ -1162,18 +1162,28 @@ * ==================== */ - // Probably best not to change these: - if (!defined('JANITOR')) { - define('JANITOR', 0, true); - define('MOD', 1, true); - define('ADMIN', 2, true); - define('DISABLED', 3, true); - } + // Probably best not to change this unless you are smart enough to figure out what you're doing. If you + // decide to change it, remember that it is impossible to redefinite/overwrite groups; you may only add + // new ones. + $config['mod']['groups'] = array( + 10 => 'Janitor', + 20 => 'Mod', + 30 => 'Admin', + // 98 => 'God', + 99 => 'Disabled' + ); + + // If you add stuff to the above, you'll need to call this function immediately after. + define_groups(); + + // Example: Adding a new permissions group. + // $config['mod']['groups'][0] = 'NearlyPowerless'; + // define_groups(); // Capcode permissions. $config['mod']['capcode'] = array( // JANITOR => array('Janitor'), - MOD => array('Mod'), + MOD => array('Mod'), ADMIN => true ); @@ -1312,18 +1322,14 @@ $config['mod']['edit_config'] = ADMIN; // Config editor permissions - $config['mod']['config'] = array( - JANITOR => false, - MOD => false, - ADMIN => false, - DISABLED => false, - ); + $config['mod']['config'] = array(); // Disable the following configuration variables from being changed via ?/config. The following default // banned variables are considered somewhat dangerous. $config['mod']['config'][DISABLED] = array( 'mod>config', 'mod>config_editor_php', + 'mod>groups', 'convert_args', 'db>password', ); diff --git a/inc/functions.php b/inc/functions.php index 819a935b..e5e33449 100644 --- a/inc/functions.php +++ b/inc/functions.php @@ -265,6 +265,15 @@ function verbose_error_handler($errno, $errstr, $errfile, $errline) { )); } +function define_groups() { + global $config; + + foreach ($config['mod']['groups'] as $group_value => $group_name) + defined($group_name) or define($group_name, $group_value, true); + + ksort($config['mod']['groups']); +} + function create_antibot($board, $thread = null) { require_once dirname(__FILE__) . '/anti-bot.php'; diff --git a/inc/mod/config-editor.php b/inc/mod/config-editor.php index 3f15ebe5..4493fd2b 100644 --- a/inc/mod/config-editor.php +++ b/inc/mod/config-editor.php @@ -3,7 +3,7 @@ function permission_to_edit_config_var($varname) { global $config, $mod; - if (is_array($config['mod']['config'][DISABLED])) { + if (isset($config['mod']['config'][DISABLED])) { foreach ($config['mod']['config'][DISABLED] as $disabled_var_name) { $disabled_var_name = explode('>', $disabled_var_name); if (count($disabled_var_name) == 1) @@ -14,10 +14,11 @@ function permission_to_edit_config_var($varname) { } $allow_only = false; - // for ($perm = (int)$mod['type']; $perm >= JANITOR; $perm --) { - for ($perm = JANITOR; $perm <= (int)$mod['type']; $perm ++) { + foreach ($config['mod']['groups'] as $perm => $perm_name) { + if ($perm > $mod['type']) + break; $allow_only = false; - if (is_array($config['mod']['config'][$perm])) { + if (isset($config['mod']['config'][$perm]) && is_array($config['mod']['config'][$perm])) { foreach ($config['mod']['config'][$perm] as $perm_var_name) { if ($perm_var_name == '!') { $allow_only = true; @@ -92,7 +93,7 @@ function config_vars() { continue; // This is just an alias. if (!preg_match('/^array|\[\]|function/', $var['default']) && !preg_match('/^Example: /', trim(implode(' ', $var['comment'])))) { $syntax_error = true; - $temp = eval('$syntax_error = false;return ' . $var['default'] . ';'); + $temp = eval('$syntax_error = false;return @' . $var['default'] . ';'); if ($syntax_error && $temp === false) { error('Error parsing config.php (line ' . $line_no . ')!', null, $var); } elseif (!isset($temp)) { diff --git a/inc/mod/pages.php b/inc/mod/pages.php index 5e45cdae..af8f7466 100644 --- a/inc/mod/pages.php +++ b/inc/mod/pages.php @@ -1674,11 +1674,39 @@ function mod_user_promote($uid, $action) { if (!hasPermission($config['mod']['promoteusers'])) error($config['error']['noaccess']); - $query = prepare("UPDATE ``mods`` SET `type` = `type` " . ($action == 'promote' ? "+1 WHERE `type` < " . (int)ADMIN : "-1 WHERE `type` > " . (int)JANITOR) . " AND `id` = :id"); + $query = prepare("SELECT `type`, `username` FROM ``mods`` WHERE `id` = :id"); $query->bindValue(':id', $uid); $query->execute() or error(db_error($query)); - modLog(($action == 'promote' ? 'Promoted' : 'Demoted') . " user #{$uid}"); + if (!$mod = $query->fetch(PDO::FETCH_ASSOC)) + error($config['error']['404']); + + $new_group = false; + + $groups = $config['mod']['groups']; + if ($action == 'demote') + $groups = array_reverse($groups, true); + + foreach ($groups as $group_value => $group_name) { + if ($action == 'promote' && $group_value > $mod['type']) { + $new_group = $group_value; + break; + } elseif ($action == 'demote' && $group_value < $mod['type']) { + $new_group = $group_value; + break; + } + } + + if ($new_group === false || $new_group == DISABLED) + error(_('Impossible to promote/demote user.')); + + $query = prepare("UPDATE ``mods`` SET `type` = :group_value WHERE `id` = :id"); + $query->bindValue(':id', $uid); + $query->bindValue(':group_value', $new_group); + $query->execute() or error(db_error($query)); + + modLog(($action == 'promote' ? 'Promoted' : 'Demoted') . ' user "' . + utf8tohtml($mod['username']) . '" to ' . $config['mod']['groups'][$new_group]); header('Location: ?/users', true, $config['redirect_http']); } @@ -2069,14 +2097,8 @@ function mod_config($board_config = false) { $config_append .= ' = '; - if (@$var['permissions'] && in_array($value, array(JANITOR, MOD, ADMIN, DISABLED))) { - $perm_array = array( - JANITOR => 'JANITOR', - MOD => 'MOD', - ADMIN => 'ADMIN', - DISABLED => 'DISABLED' - ); - $config_append .= $perm_array[$value]; + if (@$var['permissions'] && isset($config['mod']['groups'][$value])) { + $config_append .= $config['mod']['groups'][$value]; } else { $config_append .= var_export($value, true); } diff --git a/install.php b/install.php index 45244b90..639a90c9 100644 --- a/install.php +++ b/install.php @@ -1,7 +1,7 @@ {% elseif var.permissions %} {% elseif var.type == 'integer' %} diff --git a/templates/mod/user.html b/templates/mod/user.html index 1bb297af..89240824 100644 --- a/templates/mod/user.html +++ b/templates/mod/user.html @@ -28,21 +28,15 @@ {% if new %} - {% trans 'Class' %} + {% trans 'Group' %}
    -
  • - - -
  • -
  • - - -
  • -
  • - - -
  • + {% for group_value, group_name in config.mod.groups if group_name != 'Disabled' %} +
  • + + +
  • + {% endfor %}
diff --git a/templates/mod/users.html b/templates/mod/users.html index fbd43b32..984c48e1 100644 --- a/templates/mod/users.html +++ b/templates/mod/users.html @@ -15,9 +15,10 @@ {{ user.id }} {{ user.username|e }} - {% if user.type == constant('JANITOR') %}{% trans 'Janitor' %} - {% elseif user.type == constant('MOD') %}{% trans 'Mod' %} - {% elseif user.type == constant('ADMIN') %}{% trans 'Admin' %} + {% if config.mod.groups[user.type] %} + {{ config.mod.groups[user.type] }} + {% else %} + {% trans 'Unknown' %} ({{ user.type }}) {% endif %} @@ -46,10 +47,10 @@ {% endif %} - {% if mod|hasPermission(config.mod.promoteusers) and user.type < constant('ADMIN') %} + {% if mod|hasPermission(config.mod.promoteusers) and user.type < constant(config.mod.groups[0:-1]|last) %} ▲ {% endif %} - {% if mod|hasPermission(config.mod.promoteusers) and user.type > constant('JANITOR') %} + {% if mod|hasPermission(config.mod.promoteusers) and user.type > constant(config.mod.groups|first) %} ▼ {% endif %} {% if mod|hasPermission(config.mod.modlog) %} -- cgit v1.3 From 288988e6bd6bbff69509e0c6f15c810479041cc7 Mon Sep 17 00:00:00 2001 From: Michael Foster Date: Mon, 9 Sep 2013 15:40:55 +1000 Subject: Bugfix with new installations. Reported by a raging retard named Kraid, who refuses to pay for support --- install.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'install.sql') diff --git a/install.sql b/install.sql index 1e7a5011..22bdeb55 100644 --- a/install.sql +++ b/install.sql @@ -135,14 +135,14 @@ CREATE TABLE IF NOT EXISTS `mods` ( `boards` text CHARACTER SET utf8 NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `id` (`id`,`username`) -) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 AUTO_INCREMENT=5 ; +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 AUTO_INCREMENT=1 ; -- -- Dumping data for table `mods` -- INSERT INTO `mods` VALUES -(1, 'admin', 'cedad442efeef7112fed0f50b011b2b9bf83f6898082f995f69dd7865ca19fb7', '4a44c6c55df862ae901b413feecb0d49', 20, '*'); +(1, 'admin', 'cedad442efeef7112fed0f50b011b2b9bf83f6898082f995f69dd7865ca19fb7', '4a44c6c55df862ae901b413feecb0d49', 30, '*'); -- -------------------------------------------------------- -- cgit v1.3 From c7c297b8a6f526a467f0f3bd8044e02d481db63a Mon Sep 17 00:00:00 2001 From: Michael Foster Date: Tue, 17 Sep 2013 09:23:50 +1000 Subject: Finish upgrade to new bans table --- install.php | 5 +++++ install.sql | 20 +++++++++++--------- 2 files changed, 16 insertions(+), 9 deletions(-) (limited to 'install.sql') diff --git a/install.php b/install.php index 69be6300..c39f62dd 100644 --- a/install.php +++ b/install.php @@ -467,6 +467,11 @@ if (file_exists($config['has_installed'])) { $query->bindValue(':seen', $ban['seen']); $query->execute() or error(db_error($query)); } + + // Drop old bans table + query("DROP TABLE ``bans``") or error(db_error()); + // Replace with new table + query("RENAME TABLE ``bans_new_temp`` TO ``bans``") or error(db_error()); case false: // Update version number file_write($config['has_installed'], VERSION); diff --git a/install.sql b/install.sql index 22bdeb55..462cd2f0 100644 --- a/install.sql +++ b/install.sql @@ -40,18 +40,20 @@ CREATE TABLE IF NOT EXISTS `antispam` ( -- CREATE TABLE IF NOT EXISTS `bans` ( - `id` int(11) unsigned NOT NULL AUTO_INCREMENT, - `ip` varchar(39) CHARACTER SET ascii NOT NULL, - `mod` int(11) NOT NULL COMMENT 'which mod made the ban', - `set` int(11) NOT NULL, - `expires` int(11) DEFAULT NULL, + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `ipstart` varbinary(16) NOT NULL, + `ipend` varbinary(16) DEFAULT NULL, + `created` int(10) unsigned NOT NULL, + `expires` int(10) unsigned DEFAULT NULL, + `board` varchar(58) DEFAULT NULL, + `creator` int(10) NOT NULL, `reason` text, - `board` varchar(58) CHARACTER SET utf8 DEFAULT NULL, `seen` tinyint(1) NOT NULL, + `post` blob, PRIMARY KEY (`id`), - KEY `ip` (`ip`), - KEY `seen` (`seen`) -) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 AUTO_INCREMENT=1 ; + KEY `expires` (`expires`), + KEY `ipstart` (`ipstart`,`ipend`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 AUTO_INCREMENT=1 ; -- -------------------------------------------------------- -- cgit v1.3