diff options
| author | Michael Foster <savetheinternet@tinyboard.org> | 2013-09-06 23:09:18 +1000 |
|---|---|---|
| committer | Michael Foster <savetheinternet@tinyboard.org> | 2013-09-06 23:09:18 +1000 |
| commit | f309e4037c11197e4392e402992882358e4678c7 (patch) | |
| tree | c0b7bbc65801ba693db55b847db3bddcbcc894a4 /inc/filters.php | |
| parent | 14ff0fbeb3fd93e94a804d647971b8a9551c327f (diff) | |
Better and faster basic flood prevention, while merging it into $config['filters'].
Diffstat (limited to 'inc/filters.php')
| -rw-r--r-- | inc/filters.php | 109 |
1 files changed, 105 insertions, 4 deletions
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(); } |
