diff --git a/post.php b/post.php index d39d7bc2..6b41c5f3 100644 --- a/post.php +++ b/post.php @@ -64,6 +64,105 @@ function strip_markup($post_body) } } +/** + * Checks if the user at the remote ip passed the captcha. + * + * @param string $secret Server side secret. + * @param string $response User provided response. + * @param string $remote_ip User ip. + * @return bool Returns true if the use passed the captcha. + */ +function check_recaptcha($secret, $response, $remote_ip) +{ + $resp = json_decode(file_get_contents( + sprintf( + 'https://www.google.com/recaptcha/api/siteverify?secret=%s&response=%s&remoteip=%s', + $secret, + urlencode($response), + $remote_ip + ) + ), true); + return !!$resp['success']; +} + +/** + * Deletes the (single) captcha associated with the ip and code. + * + * @param string $ip + * @param string $code + * @return bool Returns true on success. + */ +function db_delete_captcha($ip, $code) +{ + $query = prepare('DELETE FROM captchas WHERE ip=:ip AND code=:code LIMIT 1'); + $query->bindValue(':ip', $ip); + $query->bindValue(':code', $code); + $query->execute(); + return $query->rowCount() != 0; +} + +/** + * Get the (single) thread with the given id in the given board. + * + * @param string $board Board to search in. MUST ALREADY BE SANITIZED. + * @param int $thread_id Id of the thread. + * @return false|array Returns false if the thread does not exist. Otherwise, an array with the thread's 'sticky', 'locked', + * 'cycle', 'sage', and 'slug' properties. + */ +function db_select_thread_with_attributes($board, $thread_id) +{ + $query = prepare(sprintf("SELECT `sticky`, `locked`, `cycle`, `sage`, `slug` FROM ``posts_%s`` WHERE `id` = :id AND `thread` IS NULL LIMIT 1", $board)); + $query->bindValue(':id', $thread_id, PDO::PARAM_INT); + $query->execute() or error(db_error()); + $thread = $query->fetch(PDO::FETCH_ASSOC); + + if (!$thread) { + return false; + } + return $thread; +} + +/** + * Get the threads with the given id in the given board. + * + * @param string $board Board to search in. MUST ALREADY BE SANITIZED. + * @param int $thread_id Id of the thread. + * @return false|array Returns false if no thread exists. Otherwise, an array of arrays with the threads 'id', 'thread' + * and 'body_nomarkup' properties. + */ +function db_select_threads_minimal($board, $thread_id) +{ + $query = prepare(sprintf("SELECT `id`, `thread`, `body_nomarkup` FROM ``posts_%s`` WHERE `id` = :id", $board)); + $query->bindValue(':id', $thread_id, PDO::PARAM_INT); + $query->execute() or error(db_error($query)); + $threads = $query->fetch(PDO::FETCH_ASSOC); + + if (!$threads) { + return false; + } + return $threads; +} + +/** + * Inserts a new report. + * + * @param string $ip Ip of the user sending the report. + * @param [type] $board Board of the reported thread. MUST ALREADY BE SANITIZED. + * @param [type] $post_id Post reported. + * @param [type] $reason Reason of the report. + * @return void + */ +function db_insert_report($ip, $board, $post_id, $reason) +{ + $query = prepare("INSERT INTO ``reports`` VALUES (NULL, :time, :ip, :board, :post, :reason)"); + $query->bindValue(':time', time(), PDO::PARAM_INT); + $query->bindValue(':ip', $ip, PDO::PARAM_STR); + $query->bindValue(':board', $board, PDO::PARAM_STR); + $query->bindValue(':post', $post_id, PDO::PARAM_INT); + $query->bindValue(':reason', $reason, PDO::PARAM_STR); + $query->execute() or error(db_error($query)); +} + /** * Inserts a new ban appeal into the database. * @@ -71,7 +170,7 @@ function strip_markup($post_body) * @param string $appeal_message Appeal message. * @return void */ -function query_insert_ban_appeal($ban_id, $appeal_message) +function db_insert_ban_appeal($ban_id, $appeal_message) { $query = prepare("INSERT INTO ``ban_appeals`` VALUES (NULL, :ban_id, :time, :message, 0)"); $query->bindValue(':ban_id', $ban_id, PDO::PARAM_INT); @@ -80,6 +179,18 @@ function query_insert_ban_appeal($ban_id, $appeal_message) $query->execute() or error(db_error($query)); } +/** + * Fetches the appeals to a ban. + * + * @param int $ban_id MUST ALREADY BE SANITIZED. + * @return array + */ +function db_select_ban_appeals($ban_id) +{ + $query = query("SELECT `denied` FROM ``ban_appeals`` WHERE `ban_id` = $ban_id") or error(db_error()); + return $query->fetchAll(PDO::FETCH_COLUMN); +} + /** * Method handling functions */ @@ -407,6 +518,7 @@ function handle_report() } if ($config['report_captcha']) { + // What is this? $resp = file_get_contents($config['captcha']['provider_check'] . "?" . http_build_query([ 'mode' => 'check', 'text' => $_POST['captcha_text'], @@ -423,11 +535,7 @@ function handle_report() markup($reason); foreach ($report as $id) { - $query = prepare(sprintf("SELECT `id`,`thread`, `body_nomarkup` FROM ``posts_%s`` WHERE `id` = :id", $board['uri'])); - $query->bindValue(':id', $id, PDO::PARAM_INT); - $query->execute() or error(db_error($query)); - - $thread = $query->fetch(PDO::FETCH_ASSOC); + $thread = db_select_threads_minimal($board['uri'], $id); $error = event('report', array('ip' => $_SERVER['REMOTE_ADDR'], 'board' => $board['uri'], 'post' => $post, 'reason' => $reason, 'link' => link_for($thread))); if ($error) { @@ -441,13 +549,8 @@ function handle_report() '/' . $board['dir'] . $config['dir']['res'] . link_for($thread) . ($thread['thread'] ? '#' . $id : '') . ' for "' . $reason . '"' ); - $query = prepare("INSERT INTO ``reports`` VALUES (NULL, :time, :ip, :board, :post, :reason)"); - $query->bindValue(':time', time(), PDO::PARAM_INT); - $query->bindValue(':ip', $_SERVER['REMOTE_ADDR'], PDO::PARAM_STR); - $query->bindValue(':board', $board['uri'], PDO::PARAM_STR); - $query->bindValue(':post', $id, PDO::PARAM_INT); - $query->bindValue(':reason', $reason, PDO::PARAM_STR); - $query->execute() or error(db_error($query)); + + db_insert_report($_SERVER['REMOTE_ADDR'], $board['uri'], $id, $reason); if ($config['slack']) { function slack($message, $room = "reports", $icon = ":no_entry_sign:") @@ -508,7 +611,7 @@ function handle_report() if (!isset($_POST['json_response'])) { $index = $root . $board['dir'] . $config['file_index']; - $reported_post = $root . $board['dir'] . $config['dir']['res'] . ($thread['thread'] ? $thread['thread'] : $id) . ".html" . ($thread['thread'] ? '#' . $id : ''); + //$reported_post = $root . $board['dir'] . $config['dir']['res'] . ($thread['thread'] ? $thread['thread'] : $id) . ".html" . ($thread['thread'] ? '#' . $id : ''); //header('Location: ' . $reported_post); echo Element('page.html', array('config' => $config, 'body' => '
[ ' . _('Close window') . " ] [ " . _('Return') . ' ]
', 'title' => _('Report submitted!'))); @@ -568,19 +671,10 @@ function handle_post() if (!$dropped_post) { // Check for CAPTCHA right after opening the board so the "return" link is in there. if ($config['recaptcha']) { - if (!isset($_POST['g-recaptcha-response'])) + if (!isset($_POST['g-recaptcha-response'])) { error($config['error']['bot']); - // Check what reCAPTCHA has to say... - $resp = json_decode(file_get_contents( - sprintf( - 'https://www.google.com/recaptcha/api/siteverify?secret=%s&response=%s&remoteip=%s', - $config['recaptcha_private'], - urlencode($_POST['g-recaptcha-response']), - $_SERVER['REMOTE_ADDR'] - ) - ), true); - - if (!$resp['success']) { + } + if (!check_recaptcha($config['recaptcha_private'], $_POST['g-recaptcha-response'], $_SERVER['REMOTE_ADDR'])) { error($config['error']['captcha']); } } @@ -600,13 +694,7 @@ function handle_post() error($config['error']['securimage']['empty']); } - $query = prepare('DELETE FROM captchas WHERE timebindValue(':ip', $_SERVER['REMOTE_ADDR']); - $query->bindValue(':code', $_POST['captcha']); - $query->execute(); - - if ($query->rowCount() == 0) { + if (!db_delete_captcha($_SERVER['REMOTE_ADDR'], $_POST['captcha'])) { error($config['error']['securimage']['bad']); } } @@ -634,7 +722,7 @@ function handle_post() if ($post['mod'] = isset($_POST['mod']) && $_POST['mod']) { check_login(false); if (!$mod) { - // Liar. You're not a mod. + // Liar. You're not a mod >:-[ error($config['error']['notamod']); } @@ -674,14 +762,11 @@ function handle_post() $mod = $post['mod'] = false; } - //Check if thread exists + // Check if thread exists. if (!$post['op']) { - $query = prepare(sprintf("SELECT `sticky`,`locked`,`cycle`,`sage`,`slug` FROM ``posts_%s`` WHERE `id` = :id AND `thread` IS NULL LIMIT 1", $board['uri'])); - $query->bindValue(':id', $post['thread'], PDO::PARAM_INT); - $query->execute() or error(db_error()); + $thread = db_select_thread_with_attributes($board['uri'], $post['thread']); - if (!$thread = $query->fetch(PDO::FETCH_ASSOC)) { - // Non-existant + if ($thread === false) { error($config['error']['nonexistant']); } } else { @@ -844,7 +929,7 @@ function handle_post() $post['name'] = $name; } } - } else if ($config['joke_capcode']) { + } elseif ($config['joke_capcode']) { if (strtolower($post['email']) == 'joke') { if (isset($config['joke_capcode_default'])) { $cap = $config['joke_capcode_default']; @@ -1570,6 +1655,7 @@ function handle_appeal() error($config['error']['bot']); } + // Doubles as sanitization against SQL injection. $ban_id = (int) $_POST['ban_id']; $bans = Bans::find($_SERVER['REMOTE_ADDR']); @@ -1588,8 +1674,7 @@ function handle_appeal() error($config['error']['tooshortban']); } - $query = query("SELECT `denied` FROM ``ban_appeals`` WHERE `ban_id` = $ban_id") or error(db_error()); - $ban_appeals = $query->fetchAll(PDO::FETCH_COLUMN); + $ban_appeals = db_select_ban_appeals($ban_id); if (count($ban_appeals) >= $config['ban_appeals_max']) { error($config['error']['toomanyappeals']); @@ -1607,7 +1692,7 @@ function handle_appeal() // Sanitize away eventual Cross Site Scripting funkyness. $appeal_msg = htmlspecialchars($_POST['appeal']); - query_insert_ban_appeal($ban_id, $appeal_msg); + db_insert_ban_appeal($ban_id, $appeal_msg); displayBan($ban); }