From caaf741691c9d44a9a186d6ce924fecb04f4f347 Mon Sep 17 00:00:00 2001 From: czaks Date: Fri, 22 Apr 2016 05:35:43 +0200 Subject: [SECURITY] keep up with modern password hashing standards --- install.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'install.sql') diff --git a/install.sql b/install.sql index 969107a2..fbf220c1 100644 --- a/install.sql +++ b/install.sql @@ -131,8 +131,8 @@ CREATE TABLE IF NOT EXISTS `modlogs` ( CREATE TABLE IF NOT EXISTS `mods` ( `id` smallint(6) unsigned NOT NULL AUTO_INCREMENT, `username` varchar(30) NOT NULL, - `password` char(64) CHARACTER SET ascii NOT NULL COMMENT 'SHA256', - `salt` char(32) CHARACTER SET ascii NOT NULL, + `password` varchar(256) CHARACTER SET ascii NOT NULL COMMENT 'SHA256', + `salt` varchar(64) CHARACTER SET ascii NOT NULL, `type` smallint(2) NOT NULL, `boards` text CHARACTER SET utf8 NOT NULL, PRIMARY KEY (`id`), -- cgit v1.3 From 7c3126866cd291d515ab5cc83d4635c3c95c6d85 Mon Sep 17 00:00:00 2001 From: czaks Date: Thu, 5 May 2016 06:43:22 +0200 Subject: ease the migration process for the previous security patch (by introducing another migration); restore php 5.4 compatibility (introducing a polyfill system) --- inc/functions.php | 2 ++ inc/mod/auth.php | 10 +++++----- inc/mod/pages.php | 18 +++++++++--------- inc/polyfill.php | 28 ++++++++++++++++++++++++++++ install.php | 4 +++- install.sql | 2 +- 6 files changed, 48 insertions(+), 16 deletions(-) create mode 100644 inc/polyfill.php (limited to 'install.sql') diff --git a/inc/functions.php b/inc/functions.php index 515e3e55..3f5ed3b0 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -18,6 +18,8 @@ require_once 'inc/template.php'; require_once 'inc/database.php'; require_once 'inc/events.php'; require_once 'inc/api.php'; +require_once 'inc/polyfill.php'; + if (!extension_loaded('gettext')) { require_once 'inc/lib/gettext/gettext.inc'; } diff --git a/inc/mod/auth.php b/inc/mod/auth.php index fa1a0f4f..d877b89f 100644 --- a/inc/mod/auth.php +++ b/inc/mod/auth.php @@ -76,20 +76,20 @@ function generate_salt() { function login($username, $password) { global $mod, $config; - $query = prepare("SELECT `id`, `type`, `boards`, `password`, `salt` FROM ``mods`` WHERE `username` = :username"); + $query = prepare("SELECT `id`, `type`, `boards`, `password`, `version` FROM ``mods`` WHERE `username` = :username"); $query->bindValue(':username', $username); $query->execute() or error(db_error($query)); if ($user = $query->fetch(PDO::FETCH_ASSOC)) { - list($version, $ok) = test_password($user['password'], $user['salt'], $password); + list($version, $ok) = test_password($user['password'], $user['version'], $password); if ($ok) { if ($config['password_crypt_version'] > $version) { // It's time to upgrade the password hashing method! - list ($user['salt'], $user['password']) = crypt_password($password); - $query = prepare("UPDATE ``mods`` SET `password` = :password, `salt` = :salt WHERE `id` = :id"); + list ($user['version'], $user['password']) = crypt_password($password); + $query = prepare("UPDATE ``mods`` SET `password` = :password, `version` = :version WHERE `id` = :id"); $query->bindValue(':password', $user['password']); - $query->bindValue(':salt', $user['salt']); + $query->bindValue(':version', $user['version']); $query->bindValue(':id', $user['id']); $query->execute() or error(db_error($query)); } diff --git a/inc/mod/pages.php b/inc/mod/pages.php index a07de4c7..8b6f73c4 100644 --- a/inc/mod/pages.php +++ b/inc/mod/pages.php @@ -1734,12 +1734,12 @@ function mod_user($uid) { } if ($_POST['password'] != '') { - list($salt, $password) = crypt_password($_POST['password']); + list($version, $password) = crypt_password($_POST['password']); - $query = prepare('UPDATE ``mods`` SET `password` = :password, `salt` = :salt WHERE `id` = :id'); + $query = prepare('UPDATE ``mods`` SET `password` = :password, `version` = :version WHERE `id` = :id'); $query->bindValue(':id', $uid); $query->bindValue(':password', $password); - $query->bindValue(':salt', $salt); + $query->bindValue(':version', $version); $query->execute() or error(db_error($query)); modLog('Changed password for ' . utf8tohtml($_POST['username']) . ' (#' . $user['id'] . ')'); @@ -1760,12 +1760,12 @@ function mod_user($uid) { if (hasPermission($config['mod']['change_password']) && $uid == $mod['id'] && isset($_POST['password'])) { if ($_POST['password'] != '') { - list($salt, $password) = crypt_password($_POST['password']); + list($version, $password) = crypt_password($_POST['password']); - $query = prepare('UPDATE ``mods`` SET `password` = :password, `salt` = :salt WHERE `id` = :id'); + $query = prepare('UPDATE ``mods`` SET `password` = :password, `version` = :version WHERE `id` = :id'); $query->bindValue(':id', $uid); $query->bindValue(':password', $password); - $query->bindValue(':salt', $salt); + $query->bindValue(':version', $version); $query->execute() or error(db_error($query)); modLog('Changed own password'); @@ -1832,12 +1832,12 @@ function mod_user_new() { if (!isset($config['mod']['groups'][$type]) || $type == DISABLED) error(sprintf($config['error']['invalidfield'], 'type')); - list($salt, $password) = crypt_password($_POST['password']); + list($version, $password) = crypt_password($_POST['password']); - $query = prepare('INSERT INTO ``mods`` VALUES (NULL, :username, :password, :salt, :type, :boards)'); + $query = prepare('INSERT INTO ``mods`` VALUES (NULL, :username, :password, :version, :type, :boards)'); $query->bindValue(':username', $_POST['username']); $query->bindValue(':password', $password); - $query->bindValue(':salt', $salt); + $query->bindValue(':version', $version); $query->bindValue(':type', $type); $query->bindValue(':boards', implode(',', $boards)); $query->execute() or error(db_error($query)); diff --git a/inc/polyfill.php b/inc/polyfill.php new file mode 100644 index 00000000..ac40a00a --- /dev/null +++ b/inc/polyfill.php @@ -0,0 +1,28 @@ + $i ? $i : 0]) ^ ord($theirs[$i]); + } + + return $answer === 0 && $olen === $tlen; + } +} diff --git a/install.php b/install.php index 5a2d724a..968c41de 100644 --- a/install.php +++ b/install.php @@ -1,7 +1,7 @@ vichan upgrade path. query("CREATE TABLE IF NOT EXISTS ``search_queries`` ( `ip` varchar(39) NOT NULL, `time` int(11) NOT NULL, `query` text NOT NULL) ENGINE=MyISAM DEFAULT CHARSET=utf8;") or error(db_error()); diff --git a/install.sql b/install.sql index fbf220c1..7e661450 100644 --- a/install.sql +++ b/install.sql @@ -132,7 +132,7 @@ CREATE TABLE IF NOT EXISTS `mods` ( `id` smallint(6) unsigned NOT NULL AUTO_INCREMENT, `username` varchar(30) NOT NULL, `password` varchar(256) CHARACTER SET ascii NOT NULL COMMENT 'SHA256', - `salt` varchar(64) CHARACTER SET ascii NOT NULL, + `version` varchar(64) CHARACTER SET ascii NOT NULL, `type` smallint(2) NOT NULL, `boards` text CHARACTER SET utf8 NOT NULL, PRIMARY KEY (`id`), -- cgit v1.3 From 7911c374e86ef8fa69c4545867b9d9d3de12d5c6 Mon Sep 17 00:00:00 2001 From: 8chan Date: Wed, 25 Feb 2015 17:21:49 -0800 Subject: Public action logs commit (log.php) Note: In a previous commit, I began making inc/mod/auth.php more modular with the check_login() function. Including it does NOT check mod login by default anymore like it does on vichan. You have to call check_login(). I've finally included it in inc/functions.php. If you have any custom pages that use inc/mod/auth.php, just including functions.php is enough now. =================================== Also: backports 351375185e5 (early 404) --- inc/config.php | 22 +++++++++++++++++----- inc/functions.php | 22 ++++++++++++++++++++-- inc/mod/auth.php | 2 +- inc/mod/pages.php | 36 ++++++++++++++++++++++++++++++++++++ install.sql | 1 + log.php | 24 ++++++++++++++++++++++++ mod.php | 6 +++--- post.php | 5 +++-- templates/mod/log.html | 16 ++++++++++++++-- tools/inc/cli.php | 1 - 10 files changed, 119 insertions(+), 16 deletions(-) create mode 100644 log.php (limited to 'install.sql') diff --git a/inc/config.php b/inc/config.php index a3087162..b6bc3b69 100644 --- a/inc/config.php +++ b/inc/config.php @@ -511,6 +511,13 @@ // The timeout for the above, in seconds. $config['upload_by_url_timeout'] = 15; + // Enable early 404? With default settings, a thread would 404 if it was to leave page 3, if it had less + // than 3 replies. + $config['early_404'] = false; + + $config['early_404_page'] = 3; + $config['early_404_replies'] = 5; + // A wordfilter (sometimes referred to as just a "filter" or "censor") automatically scans users’ posts // as they are submitted and changes or censors particular words or phrases. @@ -1537,25 +1544,30 @@ /* * ==================== - * Public post search + * Public pages * ==================== */ + + // Public post search settings $config['search'] = array(); // Enable the search form $config['search']['enable'] = false; // Maximal number of queries per IP address per minutes - $config['search']['queries_per_minutes'] = Array(15, 2); + $config['search']['queries_per_minutes'] = Array(15, 2); // Global maximal number of queries per minutes - $config['search']['queries_per_minutes_all'] = Array(50, 2); + $config['search']['queries_per_minutes_all'] = Array(50, 2); // Limit of search results - $config['search']['search_limit'] = 100; + $config['search']['search_limit'] = 100; // Boards for searching - //$config['search']['boards'] = array('a', 'b', 'c', 'd', 'e'); + //$config['search']['boards'] = array('a', 'b', 'c', 'd', 'e'); + + // Enable public logs? 0: NO, 1: YES, 2: YES, but drop names + $config['public_logs'] = 0; /* * ==================== diff --git a/inc/functions.php b/inc/functions.php index 8f650828..378e40b3 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -18,6 +18,7 @@ require_once 'inc/template.php'; require_once 'inc/database.php'; require_once 'inc/events.php'; require_once 'inc/api.php'; +require_once 'inc/mod/auth.php'; require_once 'inc/polyfill.php'; if (!extension_loaded('gettext')) { @@ -524,7 +525,8 @@ function setupBoard($array) { $board = array( 'uri' => $array['uri'], 'title' => $array['title'], - 'subtitle' => $array['subtitle'] + 'subtitle' => $array['subtitle'], + #'indexed' => $array['indexed'], ); // older versions @@ -1270,7 +1272,7 @@ function deletePost($id, $error_if_doesnt_exist=true, $rebuild_after=true) { return true; } -function clean() { +function clean($pid = false) { global $board, $config; $offset = round($config['max_pages']*$config['threads_per_page']); @@ -1281,6 +1283,22 @@ function clean() { $query->execute() or error(db_error($query)); while ($post = $query->fetch(PDO::FETCH_ASSOC)) { deletePost($post['id'], false, false); + if ($pid) modLog("Automatically deleting thread #{$post['id']} due to new thread #{$pid}"); + } + + // Bump off threads with X replies earlier, spam prevention method + if ($config['early_404']) { + $offset = round($config['early_404_page']*$config['threads_per_page']); + $query = prepare(sprintf("SELECT `id` AS `thread_id`, (SELECT COUNT(`id`) FROM ``posts_%s`` WHERE `thread` = `thread_id`) AS `reply_count` FROM ``posts_%s`` WHERE `thread` IS NULL ORDER BY `sticky` DESC, `bump` DESC LIMIT :offset, 9001", $board['uri'], $board['uri'])); + $query->bindValue(':offset', $offset, PDO::PARAM_INT); + $query->execute() or error(db_error($query)); + + while ($post = $query->fetch(PDO::FETCH_ASSOC)) { + if ($post['reply_count'] < $config['early_404_replies']) { + deletePost($post['thread_id'], false, false); + if ($pid) modLog("Automatically deleting thread #{$post['thread_id']} due to new thread #{$pid} (early 404 is set, #{$post['thread_id']} had {$post['reply_count']} replies)"); + } + } } } diff --git a/inc/mod/auth.php b/inc/mod/auth.php index 009e93ad..42f34196 100644 --- a/inc/mod/auth.php +++ b/inc/mod/auth.php @@ -130,7 +130,7 @@ function destroyCookies() { 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(':id', (isset($mod['id']) ? $mod['id'] : -1), PDO::PARAM_INT); $query->bindValue(':ip', $_SERVER['REMOTE_ADDR']); $query->bindValue(':time', time(), PDO::PARAM_INT); $query->bindValue(':text', $action); diff --git a/inc/mod/pages.php b/inc/mod/pages.php index a90fbbd6..ca12eaf1 100644 --- a/inc/mod/pages.php +++ b/inc/mod/pages.php @@ -698,6 +698,42 @@ function mod_user_log($username, $page_no = 1) { mod_page(_('Moderation log'), 'mod/log.html', array('logs' => $logs, 'count' => $count, 'username' => $username)); } +function mod_board_log($board, $page_no = 1, $hide_names = false, $public = false) { + global $config; + + if ($page_no < 1) + error($config['error']['404']); + + if (!hasPermission($config['mod']['mod_board_log'], $board) && !$public) + error($config['error']['noaccess']); + + $query = prepare("SELECT `username`, `mod`, `ip`, `board`, `time`, `text` FROM ``modlogs`` LEFT JOIN ``mods`` ON `mod` = ``mods``.`id` WHERE `board` = :board ORDER BY `time` DESC LIMIT :offset, :limit"); + $query->bindValue(':board', $board); + $query->bindValue(':limit', $config['mod']['modlog_page'], PDO::PARAM_INT); + $query->bindValue(':offset', ($page_no - 1) * $config['mod']['modlog_page'], PDO::PARAM_INT); + $query->execute() or error(db_error($query)); + $logs = $query->fetchAll(PDO::FETCH_ASSOC); + + if (empty($logs) && $page_no > 1) + error($config['error']['404']); + + if (!hasPermission($config['mod']['show_ip'])) { + // Supports ipv4 only! + foreach ($logs as $i => &$log) { + $log['text'] = preg_replace_callback('/(?:)?(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})(?:<\/a>)?/', function($matches) { + return "xxxx";//less_ip($matches[1]); + }, $log['text']); + } + } + + $query = prepare("SELECT COUNT(*) FROM ``modlogs`` LEFT JOIN ``mods`` ON `mod` = ``mods``.`id` WHERE `board` = :board"); + $query->bindValue(':board', $board); + $query->execute() or error(db_error($query)); + $count = $query->fetchColumn(); + + mod_page(_('Board log'), 'mod/log.html', array('logs' => $logs, 'count' => $count, 'board' => $board, 'hide_names' => $hide_names, 'public' => $public)); +} + function mod_view_board($boardName, $page_no = 1) { global $config, $mod; diff --git a/install.sql b/install.sql index 7e661450..024cb349 100644 --- a/install.sql +++ b/install.sql @@ -65,6 +65,7 @@ CREATE TABLE IF NOT EXISTS `boards` ( `uri` varchar(58) CHARACTER SET utf8 NOT NULL, `title` tinytext NOT NULL, `subtitle` tinytext, + -- `indexed` boolean default true, PRIMARY KEY (`uri`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; diff --git a/log.php b/log.php new file mode 100644 index 00000000..1a660c4c --- /dev/null +++ b/log.php @@ -0,0 +1,24 @@ + {% if log.username %} - {{ log.username|e }} + {% if hide_names %} + hidden + {% else %} + {% if not mod|hasPermission(config.mod.modlog) %} + {{ log.username|e }} + {% else %} + {{ log.username|e }} + {% endif %} + {% endif %} {% elseif log.mod == -1 %} system {% else %} @@ -44,7 +52,11 @@ {% if count > logs|count %}

{% for i in range(0, (count - 1) / config.mod.modlog_page) %} - [{{ i + 1 }}] + {% if public %} + [{{ i + 1 }}] + {% else %} + [{{ i + 1 }}] + {% endif %} {% endfor %}

{% endif %} diff --git a/tools/inc/cli.php b/tools/inc/cli.php index f3e8824f..95d51573 100644 --- a/tools/inc/cli.php +++ b/tools/inc/cli.php @@ -39,7 +39,6 @@ if(!getenv('TINYBOARD_PATH')) { putenv('TINYBOARD_PATH=' . getcwd()); require 'inc/functions.php'; -require 'inc/mod/auth.php'; $mod = Array( 'id' => -1, -- cgit v1.3 From 95b1e103cb9f349d7d82dca071636d5cde855ead Mon Sep 17 00:00:00 2001 From: Fredrick Brennan Date: Sun, 29 Mar 2015 09:18:14 +0800 Subject: Edit static pages commit --- .gitmodules | 6 ++ inc/config.php | 6 ++ inc/functions.php | 43 ++++++++++++ inc/mod/pages.php | 162 +++++++++++++++++++++++++++++++++++++++++++ install.sql | 20 +++++- mod.php | 18 +++-- templates/mod/edit_page.html | 29 ++++++++ templates/mod/pages.html | 34 +++++++++ tools/import_rules.php | 16 +++++ 9 files changed, 328 insertions(+), 6 deletions(-) create mode 100644 templates/mod/edit_page.html create mode 100644 templates/mod/pages.html create mode 100644 tools/import_rules.php (limited to 'install.sql') diff --git a/.gitmodules b/.gitmodules index 73f90e0a..df07fdf3 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,9 @@ [submodule "js/wPaint"] path = js/wPaint url = https://github.com/vichan-devel/wPaint.git + branch = master + +[submodule "inc/lib/parsedown"] + path = inc/lib/parsedown + url = https://github.com/vichan-devel/parsedown + branch = master diff --git a/inc/config.php b/inc/config.php index b6bc3b69..3fdb8d2f 100644 --- a/inc/config.php +++ b/inc/config.php @@ -1498,6 +1498,9 @@ $config['mod']['ban_appeals'] = MOD; // View the recent posts page $config['mod']['recent'] = MOD; + // Create pages + $config['mod']['edit_pages'] = MOD; + $config['pages_max'] = 10; // Config editor permissions $config['mod']['config'] = array(); @@ -1702,3 +1705,6 @@ // Use CAPTCHA for reports? $config['report_captcha'] = false; + + // Allowed HTML tags in ?/edit_pages. + $config['allowed_html'] = 'a[href|title],p,br,li,ol,ul,strong,em,u,h2,b,i,tt,div,img[src|alt|title],hr'; diff --git a/inc/functions.php b/inc/functions.php index 378e40b3..7970d05a 100755 --- a/inc/functions.php +++ b/inc/functions.php @@ -20,6 +20,7 @@ require_once 'inc/events.php'; require_once 'inc/api.php'; require_once 'inc/mod/auth.php'; require_once 'inc/polyfill.php'; +//require_once 'inc/lib/parsedown/Parsedown.php'; // we don't need that right now, do we? if (!extension_loaded('gettext')) { require_once 'inc/lib/gettext/gettext.inc'; @@ -2739,3 +2740,45 @@ function link_for($post, $page50 = false, $foreignlink = false, $thread = false) return sprintf($tpl, $id, $slug); } + +function prettify_textarea($s){ + return str_replace("\t", ' ', str_replace("\n", ' ', htmlentities($s))); +} + +class HTMLPurifier_URIFilter_NoExternalImages extends HTMLPurifier_URIFilter { + public $name = 'NoExternalImages'; + public function filter(&$uri, $c, $context) { + global $config; + $ct = $context->get('CurrentToken'); + + if (!$ct || $ct->name !== 'img') return true; + + if (!isset($uri->host) && !isset($uri->scheme)) return true; + + if (!in_array($uri->scheme . '://' . $uri->host . '/', $config['allowed_offsite_urls'])) { + error('No off-site links in board announcement images.'); + } + + return true; + } +} + +function purify_html($s) { + global $config; + + $c = HTMLPurifier_Config::createDefault(); + $c->set('HTML.Allowed', $config['allowed_html']); + $uri = $c->getDefinition('URI'); + $uri->addFilter(new HTMLPurifier_URIFilter_NoExternalImages(), $c); + $purifier = new HTMLPurifier($c); + $clean_html = $purifier->purify($s); + return $clean_html; +} + +function markdown($s) { + $pd = new Parsedown(); + $pd->setMarkupEscaped(true); + $pd->setimagesEnabled(false); + + return $pd->text($s); +} diff --git a/inc/mod/pages.php b/inc/mod/pages.php index ca12eaf1..328380d8 100644 --- a/inc/mod/pages.php +++ b/inc/mod/pages.php @@ -2628,6 +2628,167 @@ function mod_theme_rebuild($theme_name) { )); } +// This needs to be done for `secure` CSRF prevention compatibility, otherwise the $board will be read in as the token if editing global pages. +function delete_page_base($page = '', $board = false) { + global $config, $mod; + + if (empty($board)) + $board = false; + + if (!$board && $mod['boards'][0] !== '*') + error($config['error']['noaccess']); + + if (!hasPermission($config['mod']['edit_pages'], $board)) + error($config['error']['noaccess']); + + if ($board !== FALSE && !openBoard($board)) + error($config['error']['noboard']); + + if ($board) { + $query = prepare('DELETE FROM ``pages`` WHERE `board` = :board AND `name` = :name'); + $query->bindValue(':board', ($board ? $board : NULL)); + } else { + $query = prepare('DELETE FROM ``pages`` WHERE `board` IS NULL AND `name` = :name'); + } + $query->bindValue(':name', $page); + $query->execute() or error(db_error($query)); + + header('Location: ?/edit_pages' . ($board ? ('/' . $board) : ''), true, $config['redirect_http']); +} + +function mod_delete_page($page = '') { + delete_page_base($page); +} + +function mod_delete_page_board($page = '', $board = false) { + delete_page_base($page, $board); +} + +function mod_edit_page($id) { + global $config, $mod, $board; + + $query = prepare('SELECT * FROM ``pages`` WHERE `id` = :id'); + $query->bindValue(':id', $id); + $query->execute() or error(db_error($query)); + $page = $query->fetch(); + + if (!$page) + error(_('Could not find the page you are trying to edit.')); + + if (!$page['board'] && $mod['boards'][0] !== '*') + error($config['error']['noaccess']); + + if (!hasPermission($config['mod']['edit_pages'], $page['board'])) + error($config['error']['noaccess']); + + if ($page['board'] && !openBoard($page['board'])) + error($config['error']['noboard']); + + if (isset($_POST['method'], $_POST['content'])) { + $content = $_POST['content']; + $method = $_POST['method']; + $page['type'] = $method; + + if (!in_array($method, array('markdown', 'html', 'infinity'))) + error(_('Unrecognized page markup method.')); + + switch ($method) { + case 'markdown': + $write = markdown($content); + break; + case 'html': + if (hasPermission($config['mod']['rawhtml'])) { + $write = $content; + } else { + $write = purify_html($content); + } + break; + case 'infinity': + $c = $content; + markup($content); + $write = $content; + $content = $c; + } + + if (!isset($write) or !$write) + error(_('Failed to mark up your input for some reason...')); + + $query = prepare('UPDATE ``pages`` SET `type` = :method, `content` = :content WHERE `id` = :id'); + $query->bindValue(':method', $method); + $query->bindValue(':content', $content); + $query->bindValue(':id', $id); + $query->execute() or error(db_error($query)); + + $fn = ($board['uri'] ? ($board['uri'] . '/') : '') . $page['name'] . '.html'; + $body = "
$write
"; + $html = Element('page.html', array('config' => $config, 'body' => $body, 'title' => utf8tohtml($page['title']))); + file_write($fn, $html); + } + + if (!isset($content)) { + $query = prepare('SELECT `content` FROM ``pages`` WHERE `id` = :id'); + $query->bindValue(':id', $id); + $query->execute() or error(db_error($query)); + $content = $query->fetchColumn(); + } + + mod_page(sprintf(_('Editing static page: %s'), $page['name']), 'mod/edit_page.html', array('page' => $page, 'token' => make_secure_link_token("edit_page/$id"), 'content' => prettify_textarea($content), 'board' => $board)); +} + +function mod_pages($board = false) { + global $config, $mod, $pdo; + + if (empty($board)) + $board = false; + + if (!$board && $mod['boards'][0] !== '*') + error($config['error']['noaccess']); + + if (!hasPermission($config['mod']['edit_pages'], $board)) + error($config['error']['noaccess']); + + if ($board !== FALSE && !openBoard($board)) + error($config['error']['noboard']); + + if ($board) { + $query = prepare('SELECT * FROM ``pages`` WHERE `board` = :board'); + $query->bindValue(':board', $board); + } else { + $query = query('SELECT * FROM ``pages`` WHERE `board` IS NULL'); + } + $query->execute() or error(db_error($query)); + $pages = $query->fetchAll(PDO::FETCH_ASSOC); + + if (isset($_POST['page'])) { + if ($board and sizeof($pages) > $config['pages_max']) + error(sprintf(_('Sorry, this site only allows %d pages per board.'), $config['pages_max'])); + + if (!preg_match('/^[a-z0-9]{1,255}$/', $_POST['page'])) + error(_('Page names must be < 255 chars and may only contain lowercase letters A-Z and digits 1-9.')); + + foreach ($pages as $i => $p) { + if ($_POST['page'] === $p['name']) + error(_('Refusing to create a new page with the same name as an existing one.')); + } + + $title = ($_POST['title'] ? $_POST['title'] : NULL); + + $query = prepare('INSERT INTO ``pages``(board, title, name) VALUES(:board, :title, :name)'); + $query->bindValue(':board', ($board ? $board : NULL)); + $query->bindValue(':title', $title); + $query->bindValue(':name', $_POST['page']); + $query->execute() or error(db_error($query)); + + $pages[] = array('id' => $pdo->lastInsertId(), 'name' => $_POST['page'], 'board' => $board, 'title' => $title); + } + + foreach ($pages as $i => &$p) { + $p['delete_token'] = make_secure_link_token('edit_pages/delete/' . $p['name'] . ($board ? ('/' . $board) : '')); + } + + mod_page(_('Pages'), 'mod/pages.html', array('pages' => $pages, 'token' => make_secure_link_token('edit_pages' . ($board ? ('/' . $board) : '')), 'board' => $board)); +} + function mod_debug_antispam() { global $pdo, $config; @@ -2744,3 +2905,4 @@ function mod_debug_apc() { mod_page(_('Debug: APC'), 'mod/debug/apc.html', array('cached_vars' => $cached_vars)); } + diff --git a/install.sql b/install.sql index 024cb349..720e4ed5 100644 --- a/install.sql +++ b/install.sql @@ -245,7 +245,7 @@ CREATE TABLE IF NOT EXISTS `search_queries` ( `ip` varchar(39) NOT NULL, `time` int(11) NOT NULL, `query` text NOT NULL -) ENGINE=MyISAM DEFAULT CHARSET=utf8; +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; -- -------------------------------------------------------- @@ -297,6 +297,24 @@ CREATE TABLE IF NOT EXISTS `ban_appeals` ( KEY `ban_id` (`ban_id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 AUTO_INCREMENT=1 ; +-- -------------------------------------------------------- + +-- +-- Table structure for table `pages` +-- + +CREATE TABLE `pages` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `board` varchar(255) DEFAULT NULL, + `name` varchar(255) NOT NULL, + `title` varchar(255) DEFAULT NULL, + `type` varchar(255) DEFAULT NULL, + `content` text, + PRIMARY KEY (`id`), + UNIQUE KEY `u_pages` (`name`,`board`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; + +>>>>>>> 12fa8ec... Edit static pages commit /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; diff --git a/mod.php b/mod.php index a6ff00cf..fbe679ac 100644 --- a/mod.php +++ b/mod.php @@ -33,11 +33,19 @@ $pages = array( '/log' => 'log', // modlog '/log/(\d+)' => 'log', // modlog - '/log:([^/]+)' => 'user_log', // modlog - '/log:([^/]+)/(\d+)' => 'user_log', // modlog - '/news' => 'secure_POST news', // view news - '/news/(\d+)' => 'secure_POST news', // view news - '/news/delete/(\d+)' => 'secure news_delete', // delete from news + '/log:([^/:]+)' => 'user_log', // modlog + '/log:([^/:]+)/(\d+)' => 'user_log', // modlog + '/log:b:([^/]+)' => 'board_log', // modlog + '/log:b:([^/]+)/(\d+)' => 'board_log', // modlog + + '/edit_news' => 'secure_POST news', // view news + '/edit_news/(\d+)' => 'secure_POST news', // view news + '/edit_news/delete/(\d+)' => 'secure news_delete', // delete from news + + '/edit_pages(?:/?(\%b)?)' => 'secure_POST pages', + '/edit_page/(\d+)' => 'secure_POST edit_page', + '/edit_pages/delete/([a-z0-9]+)' => 'secure delete_page', + '/edit_pages/delete/([a-z0-9]+)/(\%b)' => 'secure delete_page_board', '/noticeboard' => 'secure_POST noticeboard', // view noticeboard '/noticeboard/(\d+)' => 'secure_POST noticeboard', // view noticeboard diff --git a/templates/mod/edit_page.html b/templates/mod/edit_page.html new file mode 100644 index 00000000..3d132767 --- /dev/null +++ b/templates/mod/edit_page.html @@ -0,0 +1,29 @@ +
+
+ + + + + + +
{% trans %}Markup method{% endtrans %} + {% set allowed_html = config.allowed_html %} + {% trans %}

"markdown" is provided by parsedown. Note: images disabled.

+

"html" allows the following tags:
{{ allowed_html }}

+

"infinity" is the same as what is used in posts.

+

This page will not convert between formats,
choose it once or do the conversion yourself!

{% endtrans %} +
+ +
{% trans %}Page content{% endtrans %} +
+ {% trans %}Page will appear at:{% endtrans %} + {% if board %} {{ config.domain }}/{{ board.uri }}/{{ page.name }}.html + {% else %} {{ config.site }}/{{ page.name }}.html + {% endif %}
+ +
+
diff --git a/templates/mod/pages.html b/templates/mod/pages.html new file mode 100644 index 00000000..c2395c02 --- /dev/null +++ b/templates/mod/pages.html @@ -0,0 +1,34 @@ + +
+

+{% if board %} +{% set page_max = config.pages_max %} +{% trans %}This page allows you to create static pages for your board. The limit is {{ page_max }} pages per board. You will still have to link to your pages somewhere in your board, for example in a sticky or in the board's announcement. To make links in the board's announcement, use <a> HTML tags.{% endtrans %} +{% else %} +{% trans %}This page allows you to create static pages for your imageboard.{% endtrans %} +{% endif %} +

{% trans %}Existing pages{% endtrans %}

+{% if pages %} +
+ + +{% for page in pages %} + +{% endfor %} +{% else %} +No pages yet! +{% endif %} +
{% trans %}URL{% endtrans %}{% trans %}Title{% endtrans %}{% trans %}Edit{% endtrans %}{% trans %}Delete{% endtrans %}
{{ page.name }}{{ page.title }}{% trans %}Edit{% endtrans %}{% trans %}Delete{% endtrans %}
+
+
+

{% trans %}Create a new page{% endtrans %}

+
+ + + + +
{% trans %}URL{% endtrans %}{% trans %}Title{% endtrans %}
+ +
+ +
diff --git a/tools/import_rules.php b/tools/import_rules.php new file mode 100644 index 00000000..f583f035 --- /dev/null +++ b/tools/import_rules.php @@ -0,0 +1,16 @@ + $b) { + $rules = @file_get_contents($b.'/rules.txt'); + if ($rules && !empty(trim($rules))) { + $query = prepare('INSERT INTO ``pages``(name, title, type, board, content) VALUES("rules", "Rules", "html", :board, :content)'); + $query->bindValue(':board', $b); + $query->bindValue(':content', $rules); + $query->execute() or error(db_error($query)); + } +} -- cgit v1.3 From 356f46237c3281b09d1fcc1df4e19cb12947063e Mon Sep 17 00:00:00 2001 From: czaks Date: Sun, 19 Jun 2016 02:15:49 +0200 Subject: fix install.sql after a bad merge --- install.sql | 1 - 1 file changed, 1 deletion(-) (limited to 'install.sql') diff --git a/install.sql b/install.sql index 720e4ed5..bf9b92bf 100644 --- a/install.sql +++ b/install.sql @@ -314,7 +314,6 @@ CREATE TABLE `pages` ( UNIQUE KEY `u_pages` (`name`,`board`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; ->>>>>>> 12fa8ec... Edit static pages commit /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; -- cgit v1.3 From 5e335a8564a76f0449d47f0767b4baa8d2cb9e91 Mon Sep 17 00:00:00 2001 From: czaks Date: Sun, 14 Aug 2016 16:24:17 +0200 Subject: preliminary inbound nntpchan support --- inc/config.php | 17 +++ install.php | 15 ++- install.sql | 19 +++ post.php | 359 +++++++++++++++++++++++++++++++++++++++++++-------------- 4 files changed, 322 insertions(+), 88 deletions(-) (limited to 'install.sql') diff --git a/inc/config.php b/inc/config.php index ebe17572..a8afdf7a 100644 --- a/inc/config.php +++ b/inc/config.php @@ -1689,6 +1689,23 @@ // Example: Adding the pre-markup post body to the API as "com_nomarkup". // $config['api']['extra_fields'] = array('body_nomarkup' => 'com_nomarkup'); +/* + * ================== + * NNTPChan settings + * ================== + */ + + $config['nntpchan'] = array(); + + // Enable NNTPChan integration + $config['nntpchan']['enabled'] = false; + + // NNTP server + $config['nntpchan']['server'] = "localhost:1119"; + + // Global dispatch array. Add your boards to it to enable them. + $config['nntpchan']['dispatch'] = array(); // 'overchan.test' => 'test' + /* * ==================== * Other/uncategorized diff --git a/install.php b/install.php index de4d3dd0..6f1617a8 100644 --- a/install.php +++ b/install.php @@ -1,7 +1,7 @@ vichan upgrade path. diff --git a/install.sql b/install.sql index bf9b92bf..3b390645 100644 --- a/install.sql +++ b/install.sql @@ -314,6 +314,25 @@ CREATE TABLE `pages` ( UNIQUE KEY `u_pages` (`name`,`board`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; +-- -------------------------------------------------------- + +-- +-- Table structure for table `nntp_references` +-- + +CREATE TABLE `nntp_references` ( + `board` varchar(60) NOT NULL, + `id` int(11) unsigned NOT NULL, + `message_id` varchar(255) CHARACTER SET ascii NOT NULL, + `message_id_digest` varchar(40) CHARACTER SET ascii NOT NULL, + `own` tinyint(1) NOT NULL, + `headers` text, + PRIMARY KEY (`message_id_digest`), + UNIQUE KEY `message_id` (`message_id`), + UNIQUE KEY `u_board_id` (`board`, `id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; + + /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; diff --git a/post.php b/post.php index aa1b8235..4af09928 100644 --- a/post.php +++ b/post.php @@ -11,6 +11,160 @@ if ((!isset($_POST['mod']) || !$_POST['mod']) && $config['board_locked']) { error("Board is locked"); } +$dropped_post = false; + +if (isset($_GET['Newsgroups']) && $config['nntpchan']['enabled']) { + $_POST = array(); + $_POST['json_response'] = true; + + $headers = json_encode($_GET); + + if (!isset ($_GET['Message-Id'])) { + if (!isset ($_GET['Message-ID'])) { + error("NNTPChan: No message ID"); + } + else $msgid = $_GET['Message-ID']; + } + else $msgid = $_GET['Message-Id']; + + $groups = preg_split("/,\s*/", $_GET['Newsgroups']); + if (count($groups) != 1) { + error("NNTPChan: Messages can go to only one newsgroup"); + } + $group = $groups[0]; + + if (!isset($config['nntpchan']['dispatch'][$group])) { + error("NNTPChan: We don't synchronize $group"); + } + $xboard = $config['nntpchan']['dispatch'][$group]; + + $ref = null; + if (isset ($_GET['References'])) { + $refs = preg_split("/,\s*/", $_GET['References']); + + if (count($refs) > 1) { + error("NNTPChan: We don't support multiple references"); + } + + $ref = $refs[0]; + + $query = prepare("SELECT `board`,`id` FROM ``nntp_references`` WHERE `message_id` = :ref"); + $query->bindValue(':ref', $ref); + $query->execute() or error(db_error($query)); + + $ary = $query->fetchAll(PDO::FETCH_ASSOC); + + if (count($ary) == 0) { + error("NNTPChan: We don't have $ref that $msgid references"); + } + + $p_id = $ary[0]['id']; + $p_board = $ary[0]['board']; + + if ($p_board != $xboard) { + error("NNTPChan: Cross board references not allowed. Tried to reference $p_board on $xboard"); + } + + $_POST['thread'] = $p_id; + } + + $date = isset($_GET['Date']) ? strtotime($_GET['Date']) : time(); + + list($ct) = explode('; ', $_GET['Content-Type']); + + $query = prepare("SELECT COUNT(*) AS `c` FROM ``nntp_references`` WHERE `message_id` = :msgid"); + $query->bindValue(":msgid", $msgid); + $query->execute() or error(db_error($query)); + + $a = $query->fetch(PDO::FETCH_ASSOC); + if ($a['c'] > 0) { + error("NNTPChan: We already have this post. Post discarded."); + } + + if ($ct == 'text/plain') { + $content = file_get_contents("php://input"); + } + elseif ($ct == 'multipart/mixed' || $ct == 'multipart/form-data') { + _syslog(LOG_INFO, "MM: Files: ".print_r($GLOBALS, true)); + $content = ''; + + $tmpfiles = $_FILES['attachment']; + foreach ($tmpfiles as $id => $file) { + if ($file['type'] == 'text/plain') { + $content .= file_get_contents($file['tmp_name']); + unset($_FILES['attachment'][$id]); + } + elseif ($file['type'] == 'message/rfc822') { // Signed message, ignore for now + unset($_FILES['attachment'][$id]); + } + else { // A real attachment :^) + } + } + + $_FILES = $_FILES['attachment']; + + + } + else { + error("NNTPChan: Wrong mime type: $ct"); + } + + $_POST['subject'] = isset($_GET['Subject']) ? $_GET['Subject'] : ''; + $_POST['board'] = $xboard; + + if (isset ($_GET['From'])) { + list($name, $mail) = explode(" <", $_GET['From'], 2); + $mail = preg_replace('/>\s+$/', '', $mail); + + $_POST['name'] = $name; + //$_POST['email'] = $mail; + $_POST['email'] = ''; + } + + if (isset ($_GET['X_Sage'])) { + $_POST['email'] = 'sage'; + } + + $content = preg_replace_callback('/>>([0-9a-fA-F]{6,})/', function($id) use ($xboard) { + $id = $id[1]; + + $query = prepare("SELECT `board`,`id` FROM ``nntp_references`` WHERE `message_id_digest` LIKE :rule"); + $idx = $id . "%"; + $query->bindValue(':rule', $idx); + $query->execute() or error(db_error($query)); + + $ary = $query->fetchAll(PDO::FETCH_ASSOC); + if (count($ary) == 0) { + return ">>>>$id"; + } + else { + $ret = array(); + foreach ($ary as $v) { + if ($v['board'] != $xboard) { + $ret[] = ">>>/".$v['board']."/".$v['id']; + } + else { + $ret[] = ">>".$v['id']; + } + } + return implode($ret, ", "); + } + }, $content); + + $_POST['body'] = $content; + + $dropped_post = array( + 'date' => $date, + 'board' => $xboard, + 'msgid' => $msgid, + 'headers' => $headers, + 'from_nntp' => true, + ); +} +elseif (isset($_GET['Newsgroups'])) { + error("NNTPChan: NNTPChan support is disabled"); +} + if (isset($_POST['delete'])) { // Delete @@ -178,8 +332,8 @@ if (isset($_POST['delete'])) { header('Content-Type: text/json'); echo json_encode(array('success' => true)); } -} elseif (isset($_POST['post'])) { - if (!isset($_POST['body'], $_POST['board'])) +} elseif (isset($_POST['post']) || $dropped_post) { + if (!isset($_POST['body'], $_POST['board']) && !$dropped_post) error($config['error']['bot']); $post = array('board' => $_POST['board'], 'files' => array()); @@ -206,61 +360,67 @@ if (isset($_POST['delete'])) { } else $post['op'] = true; - // Check for CAPTCHA right after opening the board so the "return" link is in there - if ($config['recaptcha']) { - if (!isset($_POST['recaptcha_challenge_field']) || !isset($_POST['recaptcha_response_field'])) - error($config['error']['bot']); - // Check what reCAPTCHA has to say... - $resp = recaptcha_check_answer($config['recaptcha_private'], - $_SERVER['REMOTE_ADDR'], - $_POST['recaptcha_challenge_field'], - $_POST['recaptcha_response_field']); - if (!$resp->is_valid) { - error($config['error']['captcha']); + + if (!$dropped_post) { + // Check for CAPTCHA right after opening the board so the "return" link is in there + if ($config['recaptcha']) { + if (!isset($_POST['recaptcha_challenge_field']) || !isset($_POST['recaptcha_response_field'])) + error($config['error']['bot']); + // Check what reCAPTCHA has to say... + $resp = recaptcha_check_answer($config['recaptcha_private'], + $_SERVER['REMOTE_ADDR'], + $_POST['recaptcha_challenge_field'], + $_POST['recaptcha_response_field']); + if (!$resp->is_valid) { + error($config['error']['captcha']); + } } - } - if (!(($post['op'] && $_POST['post'] == $config['button_newtopic']) || - (!$post['op'] && $_POST['post'] == $config['button_reply']))) - error($config['error']['bot']); + if (!(($post['op'] && $_POST['post'] == $config['button_newtopic']) || + (!$post['op'] && $_POST['post'] == $config['button_reply']))) + error($config['error']['bot']); - // Check the referrer - if ($config['referer_match'] !== false && - (!isset($_SERVER['HTTP_REFERER']) || !preg_match($config['referer_match'], rawurldecode($_SERVER['HTTP_REFERER'])))) - error($config['error']['referer']); + // Check the referrer + if ($config['referer_match'] !== false && + (!isset($_SERVER['HTTP_REFERER']) || !preg_match($config['referer_match'], rawurldecode($_SERVER['HTTP_REFERER'])))) + error($config['error']['referer']); - checkDNSBL(); + checkDNSBL(); - // Check if banned - checkBan($board['uri']); + // Check if banned + checkBan($board['uri']); - if ($post['mod'] = isset($_POST['mod']) && $_POST['mod']) { - check_login(false); - if (!$mod) { - // Liar. You're not a mod. - error($config['error']['notamod']); - } + if ($post['mod'] = isset($_POST['mod']) && $_POST['mod']) { + check_login(false); + if (!$mod) { + // Liar. You're not a mod. + error($config['error']['notamod']); + } - $post['sticky'] = $post['op'] && isset($_POST['sticky']); - $post['locked'] = $post['op'] && isset($_POST['lock']); - $post['raw'] = isset($_POST['raw']); + $post['sticky'] = $post['op'] && isset($_POST['sticky']); + $post['locked'] = $post['op'] && isset($_POST['lock']); + $post['raw'] = isset($_POST['raw']); - if ($post['sticky'] && !hasPermission($config['mod']['sticky'], $board['uri'])) - error($config['error']['noaccess']); - if ($post['locked'] && !hasPermission($config['mod']['lock'], $board['uri'])) - error($config['error']['noaccess']); - if ($post['raw'] && !hasPermission($config['mod']['rawhtml'], $board['uri'])) - error($config['error']['noaccess']); - } + if ($post['sticky'] && !hasPermission($config['mod']['sticky'], $board['uri'])) + error($config['error']['noaccess']); + if ($post['locked'] && !hasPermission($config['mod']['lock'], $board['uri'])) + error($config['error']['noaccess']); + if ($post['raw'] && !hasPermission($config['mod']['rawhtml'], $board['uri'])) + error($config['error']['noaccess']); + } + + if (!$post['mod']) { + $post['antispam_hash'] = checkSpam(array($board['uri'], isset($post['thread']) ? $post['thread'] : ($config['try_smarter'] && isset($_POST['page']) ? 0 - (int)$_POST['page'] : null))); + if ($post['antispam_hash'] === true) + error($config['error']['spam']); + } - if (!$post['mod']) { - $post['antispam_hash'] = checkSpam(array($board['uri'], isset($post['thread']) ? $post['thread'] : ($config['try_smarter'] && isset($_POST['page']) ? 0 - (int)$_POST['page'] : null))); - if ($post['antispam_hash'] === true) - error($config['error']['spam']); + if ($config['robot_enable'] && $config['robot_mute']) { + checkMute(); + } } - - if ($config['robot_enable'] && $config['robot_mute']) { - checkMute(); + else { + $mod = $post['mod'] = false; } //Check if thread exists @@ -371,28 +531,36 @@ if (isset($_POST['delete'])) { $post['email'] = str_replace(' ', '%20', htmlspecialchars($_POST['email'])); $post['body'] = $_POST['body']; $post['password'] = $_POST['password']; - $post['has_file'] = (!isset($post['embed']) && (($post['op'] && !isset($post['no_longer_require_an_image_for_op']) && $config['force_image_op']) || !empty($_FILES['file']['name']))); + $post['has_file'] = (!isset($post['embed']) && (($post['op'] && !isset($post['no_longer_require_an_image_for_op']) && $config['force_image_op']) || count($_FILES) > 0)); - if (!($post['has_file'] || isset($post['embed'])) || (($post['op'] && $config['force_body_op']) || (!$post['op'] && $config['force_body']))) { - $stripped_whitespace = preg_replace('/[\s]/u', '', $post['body']); - if ($stripped_whitespace == '') { - error($config['error']['tooshort_body']); + if (!$dropped_post) { + + if (!($post['has_file'] || isset($post['embed'])) || (($post['op'] && $config['force_body_op']) || (!$post['op'] && $config['force_body']))) { + $stripped_whitespace = preg_replace('/[\s]/u', '', $post['body']); + if ($stripped_whitespace == '') { + error($config['error']['tooshort_body']); + } } - } - if (!$post['op']) { - // Check if thread is locked - // but allow mods to post - if ($thread['locked'] && !hasPermission($config['mod']['postinlocked'], $board['uri'])) - error($config['error']['locked']); + if (!$post['op']) { + // Check if thread is locked + // but allow mods to post + if ($thread['locked'] && !hasPermission($config['mod']['postinlocked'], $board['uri'])) + error($config['error']['locked']); - $numposts = numPosts($post['thread']); + $numposts = numPosts($post['thread']); - if ($config['reply_hard_limit'] != 0 && $config['reply_hard_limit'] <= $numposts['replies']) - error($config['error']['reply_hard_limit']); + if ($config['reply_hard_limit'] != 0 && $config['reply_hard_limit'] <= $numposts['replies']) + error($config['error']['reply_hard_limit']); - if ($post['has_file'] && $config['image_hard_limit'] != 0 && $config['image_hard_limit'] <= $numposts['images']) - error($config['error']['image_hard_limit']); + if ($post['has_file'] && $config['image_hard_limit'] != 0 && $config['image_hard_limit'] <= $numposts['images']) + error($config['error']['image_hard_limit']); + } + } + else { + if (!$post['op']) { + $numposts = numPosts($post['thread']); + } } if ($post['has_file']) { @@ -442,7 +610,7 @@ if (isset($_POST['delete'])) { $trip = generate_tripcode($post['name']); $post['name'] = $trip[0]; - $post['trip'] = isset($trip[1]) ? $trip[1] : ''; + $post['trip'] = isset($trip[1]) ? $trip[1] : ''; // XX: Dropped posts and tripcodes $noko = false; if (strtolower($post['email']) == 'noko') { @@ -477,15 +645,17 @@ if (isset($_POST['delete'])) { if (empty($post['files'])) $post['has_file'] = false; - // Check for a file - if ($post['op'] && !isset($post['no_longer_require_an_image_for_op'])) { - if (!$post['has_file'] && $config['force_image_op']) - error($config['error']['noimage']); - } + if (!$dropped_post) { + // Check for a file + if ($post['op'] && !isset($post['no_longer_require_an_image_for_op'])) { + if (!$post['has_file'] && $config['force_image_op']) + error($config['error']['noimage']); + } - // Check for too many files - if (sizeof($post['files']) > $config['max_images']) - error($config['error']['toomanyimages']); + // Check for too many files + if (sizeof($post['files']) > $config['max_images']) + error($config['error']['toomanyimages']); + } if ($config['strip_combining_chars']) { $post['name'] = strip_combining_chars($post['name']); @@ -494,18 +664,19 @@ if (isset($_POST['delete'])) { $post['body'] = strip_combining_chars($post['body']); } - // Check string lengths - if (mb_strlen($post['name']) > 35) - error(sprintf($config['error']['toolong'], 'name')); - if (mb_strlen($post['email']) > 40) - error(sprintf($config['error']['toolong'], 'email')); - if (mb_strlen($post['subject']) > 100) - error(sprintf($config['error']['toolong'], 'subject')); - if (!$mod && mb_strlen($post['body']) > $config['max_body']) - error($config['error']['toolong_body']); - if (mb_strlen($post['password']) > 20) - error(sprintf($config['error']['toolong'], 'password')); - + if (!$dropped_post) { + // Check string lengths + if (mb_strlen($post['name']) > 35) + error(sprintf($config['error']['toolong'], 'name')); + if (mb_strlen($post['email']) > 40) + error(sprintf($config['error']['toolong'], 'email')); + if (mb_strlen($post['subject']) > 100) + error(sprintf($config['error']['toolong'], 'subject')); + if (!$mod && mb_strlen($post['body']) > $config['max_body']) + error($config['error']['toolong_body']); + if (mb_strlen($post['password']) > 20) + error(sprintf($config['error']['toolong'], 'password')); + } wordfilters($post['body']); $post['body'] = escape_markup_modifiers($post['body']); @@ -514,6 +685,7 @@ if (isset($_POST['delete'])) { $post['body'] .= "\n1"; } + if (!$dropped_post) if (($config['country_flags'] && !$config['allow_no_country']) || ($config['country_flags'] && $config['allow_no_country'] && !isset($_POST['no_country']))) { require 'inc/lib/geoip/geoip.inc'; $gi=geoip\geoip_open('inc/lib/geoip/GeoIPv6.dat', GEOIP_STANDARD); @@ -555,6 +727,7 @@ if (isset($_POST['delete'])) { $post['body'] .= "\n" . $_POST['tag'] . ""; } + if (!$dropped_post) if ($config['proxy_save'] && isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { $proxy = preg_replace("/[^0-9a-fA-F.,: ]/", '', $_SERVER['HTTP_X_FORWARDED_FOR']); $post['body'] .= "\n".$proxy.""; @@ -627,7 +800,7 @@ if (isset($_POST['delete'])) { } } - if (!hasPermission($config['mod']['bypass_filters'], $board['uri'])) { + if (!hasPermission($config['mod']['bypass_filters'], $board['uri']) && !$dropped_post) { require_once 'inc/filters.php'; do_filters($post); @@ -830,11 +1003,11 @@ if (isset($_POST['delete'])) { } // Do filters again if OCRing - if ($config['tesseract_ocr'] && !hasPermission($config['mod']['bypass_filters'], $board['uri'])) { + if ($config['tesseract_ocr'] && !hasPermission($config['mod']['bypass_filters'], $board['uri']) && !$dropped_post) { do_filters($post); } - if (!hasPermission($config['mod']['postunoriginal'], $board['uri']) && $config['robot_enable'] && checkRobot($post['body_nomarkup'])) { + if (!hasPermission($config['mod']['postunoriginal'], $board['uri']) && $config['robot_enable'] && checkRobot($post['body_nomarkup']) && !$dropped_post) { undoImage($post); if ($config['robot_mute']) { error(sprintf($config['error']['muted'], mute())); @@ -873,6 +1046,18 @@ if (isset($_POST['delete'])) { $post['id'] = $id = post($post); $post['slug'] = slugify($post); + + if ($dropped_post && $dropped_post['from_nntp']) { + $query = prepare("INSERT INTO ``nntp_references`` (`board`, `id`, `message_id`, `message_id_digest`, `own`, `headers`) VALUES ". + "(:board , :id , :message_id , :message_id_digest , false, :headers)"); + + $query->bindValue(':board', $dropped_post['board']); + $query->bindValue(':id', $id); + $query->bindValue(':message_id', $dropped_post['msgid']); + $query->bindValue(':message_id_digest', sha1($dropped_post['msgid'])); + $query->bindValue(':headers', $dropped_post['headers']); + $query->execute() or error(db_error($query)); + } insertFloodPost($post); // Handle cyclical threads -- cgit v1.3