diff --git a/inc/anti-bot.php b/inc/anti-bot.php index 81afca1b..716e301c 100644 --- a/inc/anti-bot.php +++ b/inc/anti-bot.php @@ -19,7 +19,7 @@ class AntiBot { if ($uppercase) $chars .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; if ($special_chars) - $chars .= ' ~!@#$%^&*()_+,./;\'[]\\{}|:"<>?=-` '; + $chars .= ' ~!@#$%^&*()_+,./;\'[]\\{}|:<>?=-` '; $chars = str_split($chars); @@ -48,8 +48,9 @@ class AntiBot { foreach ($chars as &$c) { if (rand(0, 2) != 0) - continue; - $c = mb_encode_numericentity($c, array(0, 0xffff, 0, 0xffff), 'UTF-8'); + $c = utf8tohtml($c); + else + $c = mb_encode_numericentity($c, array(0, 0xffff, 0, 0xffff), 'UTF-8'); } return implode('', $chars); diff --git a/inc/cache.php b/inc/cache.php index 19e6b356..0bb73589 100644 --- a/inc/cache.php +++ b/inc/cache.php @@ -48,7 +48,7 @@ class Cache { } // debug - if ($data && $config['debug']) { + if ($data !== false && $config['debug']) { $debug['cached'][] = $key; } diff --git a/inc/config.php b/inc/config.php index 866fefae..cd5896fc 100644 --- a/inc/config.php +++ b/inc/config.php @@ -38,7 +38,6 @@ $config['check_updates_time'] = 43200; // 12 hours // Shows some extra information at the bottom of pages. Good for debugging development. - // Also experimental. $config['debug'] = false; // For development purposes. Turns 'display_errors' on. Not recommended for production. $config['verbose_errors'] = true; @@ -362,7 +361,7 @@ $config['markup'][] = array("/'''(.+?)'''/", "\$1"); $config['markup'][] = array("/''(.+?)''/", "\$1"); $config['markup'][] = array("/\*\*(.+?)\*\*/", "\$1"); - $config['markup'][] = array("/^\s*==(.+?)==\s*$/m", "\$1"); + $config['markup'][] = array("/^[ |\t]*==(.+?)==[ |\t]*$/m", "\$1"); // Highlight PHP code wrapped in tags (PHP 5.3.0+) // $config['markup'][] = array( @@ -816,8 +815,6 @@ // Do a DNS lookup on IP addresses to get their hostname on the IP summary page $config['mod']['dns_lookup'] = true; - // Show ban form on the IP summary page - $config['mod']['ip_banform'] = true; // How many recent posts, per board, to show in the IP summary page $config['mod']['ip_recentposts'] = 5; @@ -826,12 +823,17 @@ // How many actions to show per page in the moderation log $config['mod']['modlog_page'] = 350; + // How many bans to show per page in the ban list + $config['mod']['banlist_page'] = 350; + + // Number of news entries to display per page + $config['mod']['news_page'] = 40; // Maximum number of results to display for a search, per board $config['mod']['search_results'] = 75; - // Maximum number of notices to display on the moderator noticeboard - $config['mod']['noticeboard_display'] = 50; + // How many entries to show per page in the moderator noticeboard + $config['mod']['noticeboard_page'] = 50; // Number of entries to summarize and display on the dashboard $config['mod']['noticeboard_dashboard'] = 5; @@ -867,7 +869,20 @@ * Mod permissions * ==================== */ - + + // Capcode permissions + $config['mod']['capcode'] = array( + // JANITOR => array('Janitor'), + MOD => array('Mod'), + ADMIN => true + ); + + // Example: Allow mods to post with "## Moderator" as well + // $config['mod']['capcode'][MOD][] = 'Moderator'; + + // Example: Allow janitors to post with any capcode + // $config['mod']['capcode'][JANITOR] = true; + // Set any of the below to "DISABLED" to make them unavailable for everyone. // Don't worry about per-board moderators. Let all mods moderate any board. @@ -1043,6 +1058,4 @@ // Complex regular expression to catch URLs $config['url_regex'] = '/' . '(https?|ftp):\/\/' . '(([\w\-]+\.)+[a-zA-Z]{2,6}|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' . '(:\d+)?' . '(\/([\w\-~.#\/?=&;:+%!*\[\]@$\'()+,|\^]+)?)?' . '/'; - // INSANE regular expression for IPv6 addresses - $config['ipv6_regex'] = '((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?'; diff --git a/inc/filters.php b/inc/filters.php index 7204614a..ab48598f 100644 --- a/inc/filters.php +++ b/inc/filters.php @@ -28,7 +28,7 @@ class Filter { case 'name': return preg_match($match, $post['name']); case 'trip': - return preg_match($match, $post['trip']); + return $match === $post['trip']; case 'email': return preg_match($match, $post['email']); case 'subject': diff --git a/inc/functions.php b/inc/functions.php index bd255b35..eedbf9b9 100644 --- a/inc/functions.php +++ b/inc/functions.php @@ -32,7 +32,30 @@ function loadConfig() { if (!isset($_SERVER['REMOTE_ADDR'])) $_SERVER['REMOTE_ADDR'] = '0.0.0.0'; - $arrays = array('db', 'cache', 'cookies', 'error', 'dir', 'mod', 'spam', 'flood_filters', 'wordfilters', 'custom_capcode', 'custom_tripcode', 'dnsbl', 'dnsbl_exceptions', 'remote', 'allowed_ext', 'allowed_ext_files', 'file_icons', 'footer', 'stylesheets', 'additional_javascript', 'markup'); + $arrays = array( + 'db', + 'cache', + 'cookies', + 'error', + 'dir', + 'mod', + 'spam', + 'flood_filters', + 'wordfilters', + 'custom_capcode', + 'custom_tripcode', + 'dnsbl', + 'dnsbl_exceptions', + 'remote', + 'allowed_ext', + 'allowed_ext_files', + 'file_icons', + 'footer', + 'stylesheets', + 'additional_javascript', + 'markup', + 'custom_pages' + ); $config = array(); foreach ($arrays as $key) { @@ -277,10 +300,13 @@ function setupBoard($array) { $board = array( 'uri' => $array['uri'], - 'name' => $array['title'], - 'title' => $array['subtitle'] + 'title' => $array['title'], + 'subtitle' => $array['subtitle'] ); + // older versions + $board['name'] = &$board['title']; + $board['dir'] = sprintf($config['board_path'], $board['uri']); $board['url'] = sprintf($config['board_abbreviation'], $board['uri']); @@ -690,13 +716,13 @@ function post(array $post) { $query->bindValue(':password', $post['password']); $query->bindValue(':ip', isset($post['ip']) ? $post['ip'] : $_SERVER['REMOTE_ADDR']); - if ($post['mod'] && $post['sticky']) { + if ($post['op'] && $post['mod'] && $post['sticky']) { $query->bindValue(':sticky', 1, PDO::PARAM_INT); } else { $query->bindValue(':sticky', 0, PDO::PARAM_INT); } - if ($post['mod'] && $post['locked']) { + if ($post['op'] && $post['mod'] && $post['locked']) { $query->bindValue(':locked', 1, PDO::PARAM_INT); } else { $query->bindValue(':locked', 0, PDO::PARAM_INT); @@ -777,12 +803,8 @@ function deleteFile($id, $remove_entirely_if_already=true) { $query = prepare(sprintf("SELECT `thread`,`thumb`,`file` FROM `posts_%s` WHERE `id` = :id LIMIT 1", $board['uri'])); $query->bindValue(':id', $id, PDO::PARAM_INT); $query->execute() or error(db_error($query)); - - if ($query->rowCount() < 1) { + if (!$post = $query->fetch()) error($config['error']['invalidpost']); - } - - $post = $query->fetch(); if ($post['file'] == 'deleted' && !$post['thread']) return; // Can't delete OP's image completely. @@ -801,13 +823,14 @@ function deleteFile($id, $remove_entirely_if_already=true) { // Set file to 'deleted' $query->bindValue(':file', 'deleted', PDO::PARAM_INT); } - // Update database $query->bindValue(':id', $id, PDO::PARAM_INT); $query->execute() or error(db_error($query)); if ($post['thread']) buildThread($post['thread']); + else + buildThread($id); } // rebuild post (markup) @@ -1179,6 +1202,7 @@ function buildIndex() { global $board, $config; $pages = getPages(); + $antibot = create_antibot($board['uri']); $page = 1; while ($page <= $config['max_pages'] && $content = index($page)) { @@ -1188,7 +1212,7 @@ function buildIndex() { $content['pages'] = $pages; $content['pages'][$page-1]['selected'] = true; $content['btn'] = getPageButtons($content['pages']); - $content['antibot'] = create_antibot($board['uri']); + $content['antibot'] = $antibot; file_write($filename, Element('index.html', $content)); if (isset($md5) && $md5 == md5_file($filename)) { @@ -1370,8 +1394,10 @@ function markup(&$body, $track_cites = false) { if ($config['auto_unicode']) { $body = unicodify($body); - foreach ($markup_urls as &$url) { - $body = str_replace(unicodify($url), $url, $body); + if ($config['markup_urls']) { + foreach ($markup_urls as &$url) { + $body = str_replace(unicodify($url), $url, $body); + } } } diff --git a/inc/lib/Twig/Extensions/Extension/Tinyboard.php b/inc/lib/Twig/Extensions/Extension/Tinyboard.php index b515e8d2..0a128e7b 100644 --- a/inc/lib/Twig/Extensions/Extension/Tinyboard.php +++ b/inc/lib/Twig/Extensions/Extension/Tinyboard.php @@ -10,19 +10,22 @@ class Twig_Extensions_Extension_Tinyboard extends Twig_Extension public function getFilters() { return Array( - 'filesize' => new Twig_Filter_Function('format_bytes', Array('needs_environment' => false)), - 'truncate' => new Twig_Filter_Function('twig_truncate_filter', array('needs_environment' => false)), - 'truncate_body' => new Twig_Filter_Function('truncate', array('needs_environment' => false)), - 'extension' => new Twig_Filter_Function('twig_extension_filter', array('needs_environment' => false)), - 'sprintf' => new Twig_Filter_Function('sprintf', array('needs_environment' => false)), - 'capcode' => new Twig_Filter_Function('capcode', array('needs_environment' => false)), - 'hasPermission' => new Twig_Filter_Function('twig_hasPermission_filter', array('needs_environment' => false)), - 'date' => new Twig_Filter_Function('twig_date_filter', array('needs_environment' => false)), - 'poster_id' => new Twig_Filter_Function('poster_id', array('needs_environment' => false)), - 'remove_whitespace' => new Twig_Filter_Function('twig_remove_whitespace_filter', array('needs_environment' => false)), - 'count' => new Twig_Filter_Function('count', array('needs_environment' => false)), - 'until' => new Twig_Filter_Function('until', array('needs_environment' => false)), - 'addslashes' => new Twig_Filter_Function('addslashes', array('needs_environment' => false)), + 'filesize' => new Twig_Filter_Function('format_bytes'), + 'truncate' => new Twig_Filter_Function('twig_truncate_filter'), + 'truncate_body' => new Twig_Filter_Function('truncate'), + 'extension' => new Twig_Filter_Function('twig_extension_filter'), + 'sprintf' => new Twig_Filter_Function('sprintf'), + 'capcode' => new Twig_Filter_Function('capcode'), + 'hasPermission' => new Twig_Filter_Function('twig_hasPermission_filter'), + 'date' => new Twig_Filter_Function('twig_date_filter'), + 'poster_id' => new Twig_Filter_Function('poster_id'), + 'remove_whitespace' => new Twig_Filter_Function('twig_remove_whitespace_filter'), + 'count' => new Twig_Filter_Function('count'), + 'ago' => new Twig_Filter_Function('ago'), + 'until' => new Twig_Filter_Function('until'), + 'split' => new Twig_Filter_Function('twig_split_filter'), + 'push' => new Twig_Filter_Function('twig_push_filter'), + 'addslashes' => new Twig_Filter_Function('addslashes') ); } @@ -34,10 +37,11 @@ class Twig_Extensions_Extension_Tinyboard extends Twig_Extension public function getFunctions() { return Array( - 'time' => new Twig_Filter_Function('time', array('needs_environment' => false)), - 'timezone' => new Twig_Filter_Function('twig_timezone_function', array('needs_environment' => false)), - 'hiddenInputs' => new Twig_Filter_Function('hiddenInputs', array('needs_environment' => false)), - 'hiddenInputsHash' => new Twig_Filter_Function('hiddenInputsHash', array('needs_environment' => false)) + 'time' => new Twig_Filter_Function('time'), + 'floor' => new Twig_Filter_Function('floor'), + 'timezone' => new Twig_Filter_Function('twig_timezone_function'), + 'hiddenInputs' => new Twig_Filter_Function('hiddenInputs'), + 'hiddenInputsHash' => new Twig_Filter_Function('hiddenInputsHash'), ); } @@ -57,6 +61,15 @@ function twig_timezone_function() { return sprintf("%s%02d", ($hr = (int)floor(($tz = date('Z')) / 3600)) > 0 ? '+' : '-', abs($hr)) . ':' . sprintf("%02d", (($tz / 3600) - $hr) * 60); } +function twig_split_filter($str, $delim) { + return explode($delim, $str); +} + +function twig_push_filter($array, $value) { + array_push($array, $value); + return $array; +} + function twig_remove_whitespace_filter($data) { return preg_replace('/[\t\r\n]/', '', $data); } @@ -65,7 +78,7 @@ function twig_date_filter($date, $format) { return strftime($format, $date); } -function twig_hasPermission_filter($mod, $permission, $board) { +function twig_hasPermission_filter($mod, $permission, $board = null) { return hasPermission($permission, $board, $mod); } diff --git a/inc/mod-old.php b/inc/mod-old.php new file mode 100644 index 00000000..9065bb82 --- /dev/null +++ b/inc/mod-old.php @@ -0,0 +1,287 @@ +bindValue(':username', $username); + $query->bindValue(':password', $password); + $query->execute() or error(db_error($query)); + + if ($user = $query->fetch()) { + return $mod = Array( + 'id' => $user['id'], + 'type' => $user['type'], + 'username' => $username, + 'hash' => mkhash($username, $password), + 'boards' => explode(',', $user['boards']) + ); + } else return false; +} + +function setCookies() { + global $mod, $config; + if (!$mod) + error('setCookies() was called for a non-moderator!'); + + setcookie($config['cookies']['mod'], + $mod['username'] . // username + ':' . + $mod['hash'][0] . // password + ':' . + $mod['hash'][1], // salt + time() + $config['cookies']['expire'], $config['cookies']['jail'] ? $config['cookies']['path'] : '/', null, false, true); +} + +function destroyCookies() { + global $config; + // Delete the cookies + setcookie($config['cookies']['mod'], 'deleted', time() - $config['cookies']['expire'], $config['cookies']['jail']?$config['cookies']['path'] : '/', null, false, true); +} + +function create_pm_header() { + global $mod; + $query = prepare("SELECT `id` FROM `pms` WHERE `to` = :id AND `unread` = 1"); + $query->bindValue(':id', $mod['id'], PDO::PARAM_INT); + $query->execute() or error(db_error($query)); + + if ($pm = $query->fetch()) { + return Array('id' => $pm['id'], 'waiting' => $query->rowCount() - 1); + } + + return false; +} + +function modLog($action, $_board=null) { + global $mod, $board, $config; + $query = prepare("INSERT INTO `modlogs` VALUES (:id, :ip, :board, :time, :text)"); + $query->bindValue(':id', $mod['id'], PDO::PARAM_INT); + $query->bindValue(':ip', $_SERVER['REMOTE_ADDR']); + $query->bindValue(':time', time(), PDO::PARAM_INT); + $query->bindValue(':text', $action); + if (isset($_board)) + $query->bindValue(':board', $_board); + elseif (isset($board)) + $query->bindValue(':board', $board['uri']); + else + $query->bindValue(':board', null, PDO::PARAM_NULL); + $query->execute() or error(db_error($query)); + + if ($config['syslog']) + _syslog(LOG_INFO, '[mod/' . $mod['username'] . ']: ' . $action); +} + +// Generates a