<?php /* * Ninechan Board Script * by Flashwave <http://flash.moe> * Distributed under the MIT-License */ // Set ninechan version, don't change this or it'll probably break things. $version = '2.1x'; // Language file versions this version is compatible with $langCompat = [ '2.1' ]; function ncCheckRemoteAddr(string $addr): float { $addr = rawurlencode($addr); $mail = rawurlencode('ninechan-ipintel@flash.moe'); // banned from the system, bother with this at some point //$result = file_get_contents("https://check.getipintel.net/check.php?ip={$addr}&contact={$mail}"); return 0; } // Error messages function error($data) { global $version; print '<!DOCTYPE html><html><head><meta charset="utf-8" /><title>ninechan error</title></head><body>'; print $data; print '<hr />ninechan '. $version .'</body></html>'; exit; } // Include Configuration if(@!include 'config.php') error('Failed to load configuration file.'); // Getting configuration values function getConfig($key) { // Make $ninechan global global $ninechan; // Check if the key exists and return the proper string return array_key_exists($key, $ninechan) ? $ninechan[$key] : null; } // Include language file if(@!include 'lang/'. getConfig('lang') .'.php') error('Failed to load language file.'); // Error Reporting error_reporting(getConfig('exposeErrors') ? -1 : 0); // Getting language string function getLang($key) { // Make $language global global $language; // Check if the key exists and return the proper string return array_key_exists($key, $language) ? $language[$key] : 'Undefined index'; } // Cleaning strings function cleanString($string, $lower = false, $nospecial = false) { // Run common sanitisation function over string $string = htmlentities($string, ENT_QUOTES | ENT_IGNORE, getConfig('charset')); $string = stripslashes($string); $string = strip_tags($string); // If set also make the string lowercase if($lower) $string = strtolower($string); // If set remove all characters that aren't a-z or 0-9 if($nospecial) $string = preg_replace('/[^a-z0-9]/', '', $string); // Return clean string return $string; } // Parsing tripcodes function parseTrip($name) { // Match ! or # and everything after it if(preg_match("/(#|!)(.*)/", $name, $matches)) { // Get the cap code $cap = $matches[2]; $cap = mb_convert_encoding($cap, 'SJIS', 'UTF-8'); $cap = str_replace('#', '', $cap); $cap = str_replace('&', '&', $cap); $cap = str_replace('"', '"', $cap); $cap = str_replace("'", ''', $cap); $cap = str_replace('<', '<', $cap); $cap = str_replace('>', '>', $cap); // Create the salt $salt = substr($cap .'H.', 1, 2); $salt = preg_replace('/[^.\/0-9:;<=>?@A-Z\[\\\]\^_`a-z]/', '.', $salt); $salt = strtr($salt, ':;<=>?@[\]^_`', 'ABCDEFGabcdef'); // Generate tripcode $trip = substr(crypt($cap, $salt), -10); // Return tripcode return $trip; } } // Parsing post tags function parsePost($content){ // Get bbcodes file $bbcodes = json_decode(file_get_contents(getConfig('bbCodes')), true); // Parse bbcodes $content = preg_replace(array_flip($bbcodes), $bbcodes, $content); // Add newlines and return return nl2br($content); } // Generating Random Password function generatePassword() { // Set characters to work with $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_@#$!*\/[]{}=+'; // Generate the string for($i = 0, $pass = ''; $i < 34; $i++) { $index = rand(0, mb_strlen($chars) - 1); $pass .= mb_substr($chars, $index, 1); } // Return it return $pass; } // Moderation function nMod($mode, $id, $action) { // Make SQL variables global global $sql, $sqldb; // Switch to the proper mode switch($mode) { // Banning a poster case 'ban': $prepare = "UPDATE `". $sql['table'] ."` SET `ban` = :action WHERE `id` = :id"; break; // Deleting a post case 'del': $prepare = "DELETE FROM `". $sql['table'] ."` WHERE `id` = :id"; break; // Prune an entire thread case 'prune': $prepare = "DELETE FROM `". $sql['table'] ."` WHERE `tid` = :id"; break; // Lock a thread case 'lock': $prepare = "UPDATE `". $sql['table'] ."` SET `locked` = :action WHERE `tid` = :id"; break; // Return false if there's no proper option default: return false; } $query = $sqldb->prepare($prepare); $action = $action ? '1' : '0'; $query->bindParam(':id', $id, PDO::PARAM_INT); $query->bindParam(':action', $action, PDO::PARAM_INT); $query->execute(); // Return true if successful return true; } // Board HTML header function nHead() { $header = '<!DOCTYPE html> <html> <head> <meta charset="'. getConfig('charset') .'" /> <title>'. getConfig('title') .'</title>'. (getConfig('desc') ? '<meta name="description" content="'. getConfig('desc') .'" />' : '') .'<script type="text/javascript" charset="'. getConfig('charset') .'" src="ninechan.js"></script> <script type="text/javascript"> ninechan.cookiePrefix = "'. getConfig('cookiePrefix') .'"; window.onload = function() { ninechan.init(); } </script>'; // Check if styles are enabled if(getConfig('styles')) { // Take the array $styles = getConfig('styles'); // Iterate over all styles foreach($styles as $url => $name) { // Get the key of the first entry $main = key($styles); // Append styles to the header $header .= '<link rel="'. ($url == $main ? null : 'alternate ') .'stylesheet" type="text/css" href="'. $url .'" title="'. $name .'" '. ($url == $main ? null : 'disabled ') .'/>'; } } $header .= '</head> <body> <h1><a href="./">'. getConfig('title') .'</a></h1>'. (getConfig('desc') ? ' <i>'. getConfig('desc') .'</i>' : '') .'<hr />'; $header .= '<h5>'; foreach($GLOBALS['navLinks'] as $title => $url) $header .= " [<a href='{$url}'>{$title}</a>]"; foreach($GLOBALS['navBoards'] as $link => $board) $header .= " [<a href='/{$link}/'>{$board[2]}</a>]"; $header .= '</h5><hr/><h6>[<a href="?v=index">'. getLang('INDEX') .'</a>] [<a href="?v=soup">'. getLang('NEWTHREAD') .'</a>]</h6><hr />'; return $header; } // Board HTML footer function nFoot() { // Make $version global global $version; $footer = '<hr />'; // Check if styles are enabled if(getConfig('styles')) { // Take the array $styles = getConfig('styles'); $footer .= '<h6>'; // List every style foreach($styles as $name) $footer .= '[<a href="javascript:void(0);" onclick="ninechan.setStyle(\''.$name.'\');">'.$name.'</a>]'."\r\n"; $footer .= '</h6>'; } /* Please retain the full copyright notice below including the link to flash.moe. This not only gives respect to the amount of time given freely by the developer but also helps build interest, traffic and use of ninechan and other projects. */ $footer .= '<h6> <a href="https://patchii.net/flash/ninechan" target="_blank">ninechan</a> '. (getConfig('showVersion') ? $version : '') .' © <a href="https://flash.moe" target="_blank">flashwave</a> </h6> </body> </html>'; // Return the footer return $footer; } // Setting cookies function nCookie($name, $content) { // Execute setcookie() setcookie( getConfig('cookiePrefix') . $name, $content, time() + getConfig('cookieLifetime'), getConfig('cookiePath'), $_SERVER['SERVER_NAME'] ); } // Check if the IP is banned function checkBan($ip) { // Make SQL variables global global $sql, $sqldb; // Base64 encode IP $ip = base64_encode($ip); // Prepare the statement $banCheck = $sqldb->prepare("SELECT * FROM `". $sql['table'] ."` WHERE `ip` = :ip AND `ban` = '1'"); // Append ip the statement $banCheck->bindParam(':ip', $ip, PDO::PARAM_STR); // Execute statement $banCheck->execute(); // Fetch array $return = $banCheck->fetchAll(PDO::FETCH_ASSOC); // Return the array return $return; } // Getting thread data function getPosts($id = 0) { // Make SQL variables global global $sql, $sqldb; // Prepare the statement $getPosts = $sqldb->prepare("SELECT * FROM `". $sql['table'] ."` WHERE ". ($id ? "`tid` = :tid" : "`op` = '1'") ." ORDER BY `". ($id ? "id`" : "lastreply` DESC")); // Bind the ID to the statement if($id) $getPosts->bindParam(':tid', $id, PDO::PARAM_INT); // Execute statement $getPosts->execute(); // Fetch array $return = $getPosts->fetchAll(PDO::FETCH_BOTH); // Return the array return $return; } function verifyCaptcha($response) { // Attempt to get the response $resp = @file_get_contents('https://www.google.com/recaptcha/api/siteverify?secret='. getConfig('reCaptchaPrivate') .'&response='. $response); // In the highly unlikely case that it failed to get anything forge a false if(!$resp) error('Could not connect to the ReCAPTCHA server.'); // Decode the response JSON from the servers $resp = json_decode($resp, true); // Return shit return $resp; } // Check language version if(!in_array($langVersion, $langCompat)) { print '<h2>The selected language file is incompatible with this version!</h2>'; print 'The version your language file was created for is <b>'. $langVersion .'</b>.<br />'; print 'Your version of ninechan is compatible with the following language file versions:<ul>'; foreach($langCompat as $ver) print '<li>'. $ver; print '</ul>'; exit; } // Check dependencies if(version_compare(phpversion(), '5.3.0', '<')) // PHP 5.3 or higher error('<h2>'. getLang('PHP_OUTDATED') .'</h2>'); if(!extension_loaded('PDO')) // Check if PHP Data Objects is available error('<h2>'. getLang('SQL_FUNCTION') .'</h2>'); // Connect to SQL using PDO try { $sqldb = new PDO($sql['dsn'], $sql['user'], $sql['pass']); } catch(PDOException $e) { // Catch connection error error('<h2>'. getLang('SQL_CONNECT') .'</h2>'. $e->getMessage()); } // Initialise Database $sqldb->query(" CREATE TABLE IF NOT EXISTS `". $sql['table'] ."` ( `id` int(16) unsigned NOT NULL AUTO_INCREMENT, `title` varchar(255) COLLATE utf8_bin NOT NULL, `name` varchar(255) COLLATE utf8_bin DEFAULT NULL, `trip` varchar(255) COLLATE utf8_bin DEFAULT NULL, `email` varchar(255) COLLATE utf8_bin DEFAULT NULL, `date` int(11) unsigned NOT NULL, `content` text COLLATE utf8_bin NOT NULL, `password` varchar(255) COLLATE utf8_bin NOT NULL, `ip` varchar(255) COLLATE utf8_bin NOT NULL, `op` tinyint(1) unsigned NOT NULL DEFAULT '1', `tid` int(16) unsigned NOT NULL DEFAULT '0', `locked` tinyint(1) unsigned NOT NULL DEFAULT '0', `ban` tinyint(1) unsigned NOT NULL DEFAULT '0', `lastreply` int(11) unsigned NOT NULL DEFAULT '0', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin; "); // Session session_start(); // Start a session $auth = @$_SESSION['mod']; // Set an alias for mod $remoteAddr = $_SERVER['REMOTE_ADDR']; $remoteAddrKey = 'ip_' . $remoteAddr; if(true || !isset($_SESSION[$remoteAddrKey]) || $_SESSION[$remoteAddrKey] < 0) { $_SESSION[$remoteAddrKey] = $remoteAddrState = ncCheckRemoteAddr($remoteAddr); if($remoteAddrState >= .8) file_put_contents(__DIR__ . '/../shame.txt', sprintf('[%s] %s - %F - %s%s', date('Y-m-d H:i:s'), $remoteAddr, $remoteAddrState, htmlspecialchars($_SERVER['HTTP_USER_AGENT'] ?? '(none)'), PHP_EOL), FILE_APPEND | LOCK_EX); } else $remoteAddrState = $_SESSION[$remoteAddrKey]; // Print the header print nHead(); // Exit if board is set as closed in the config file if($ninechan['closed']) { print '<h3>'. getLang('BOARD_CLOSED') .'</h3>'. getLang('REASON') .': '. getConfig('closedReason') .''; print nFoot(); exit; } // Catch &t= and redirect it properly if(isset($_GET['t'])) { // Get the url and replace &t= with &id= $newUrl = str_replace('t=', 'id=', $_SERVER['REQUEST_URI']); // Redirect and exit header('Location: '. $newUrl); print '<meta http-equiv="refresh" content="2; URL="'. $newUrl .'" />'; exit; } // Check if the current IP is banned if($remoteAddrState >= .6) print '<div class="banmsg">You have been automatically banned from posting on this board. <a href="/bot.html">Click here for more info</a>.</div><hr />'; // Check if the current IP is banned if(checkBan($_SERVER['REMOTE_ADDR'])) print '<div class="banmsg">'. getLang('USERBANNEDMSG') .'</div><hr />'; // Check if pass cookie is set and set it if it isn't if(!isset($_COOKIE[getConfig('cookiePrefix') .'pass'])) nCookie('pass', generatePassword()); // Check if view variable is set if(isset($_GET['v'])) { // Switch to proper view switch($_GET['v']) { // Main index case 'index': // Section title print '<h2>'. getLang('THREADS') .'</h2>'; // New thread link print '<h3><a href="'. $_SERVER['PHP_SELF'] .'?v=soup">'. getLang('NEWTHREAD') .'</a></h3>'; // Get threads $threads = array_chunk(getPosts(), getConfig('threadsPerPage'), true); // If at least one post was returned print the list if($threads) { print '<ol>'; foreach($threads[(isset($_GET['p']) && ($_GET['p'] - 1) >= 0 && array_key_exists(($_GET['p'] - 1), $threads)) ? $_GET['p'] - 1 : 0] as $thread) print '<li><a href="'. $_SERVER['PHP_SELF'] .'?v=thread&id='. $thread['tid'] .'">'. $thread['title'] .'</a> <span style="font-size: x-small;">[<b title="'. date(getConfig('dateFormat'), $thread['lastreply']) .'">'. (count(getPosts($thread['tid'])) - 1) .'</b>]</span>'; print '</ol>'; } else // Else return EMPTY print '<h3>'. getLang('EMPTY') .'</h3>'; // Pagination if(count($threads) > 1) { print '<h5 style="margin-bottom: 10px;">['; foreach($threads as $page => $pthreads) print '<a href="?v=index&p='. ($page + 1) .'"> '. ($page + 1) .' </a>'. ($page == key(array_reverse($threads, true)) ? '' : '/'); print ']</h5>'; } // New thread link print '<h3><a href="'. $_SERVER['PHP_SELF'] .'?v=soup">'. getLang('NEWTHREAD') .'</a></h3>'; break; // Thread view case 'thread': // Just return NONEXISTENT if t is invalid if(!isset($_GET['id']) || !is_numeric($_GET['id'])) { print('<h3>'. getLang('NONEXISTENT') .'</h3>'); break; } // Strip all non-numeric characters from the string and assign it to $threadId $threadId = preg_replace('/\D/', '', $_GET['id']); // Get thread data $thread = getPosts($threadId); // Check if the thread exists if($thread) { // Print post title print '<h2>'. getLang('THREAD') .': '. $thread[0]['title'] .'</h2>'; // Check if thread is locked and if true display message if($thread[0]['locked']) print '<h3>'. getLang('LOCKED') .'</h3>'; else print '<h3><a href='. $_SERVER['PHP_SELF'] .'?v=soup&id='. $thread[0]['tid'] .'>'. getLang('NEWREPLY') .'</a></h3>'; // Moderator tools if($auth == $ninechan['modPass']) { print '<h6>'; // Purge button print '[<a href="'. $_SERVER['PHP_SELF'] .'?v=mod&del=purge&id='. $thread[0]['tid'] .'">'. getLang('PURGE') .'</a>]'."\r\n"; if($thread[0]['locked']) print '[<a href="'. $_SERVER['PHP_SELF'] .'?v=mod&lock=false&id='. $thread[0]['tid'] .'">'. getLang('UNLOCK') .'</a>]'; else print '[<a href="'. $_SERVER['PHP_SELF'] .'?v=mod&lock=true&id='. $thread[0]['tid'] .'">'. getLang('LOCK') .'</a>]'; print '</h6>'; } // Print every post foreach($thread as $post) { // Set poster name if(getConfig('forcedAnon')) { $posterName = getConfig('anonName'); $posterTrip = null; // Check if forced anon for mods or admins is enabled } elseif((getConfig('modsAreAnon') === 1 && in_array($post['trip'], getConfig('modTrip'))) || (getConfig('adminsAreAnon') === 1 && in_array($post['trip'], getConfig('adminTrip')))) { $posterName = getConfig('anonName'); $posterTrip = null; // Check if forced trip anon for mods or admins is enabled } elseif((getConfig('modsAreAnon') === 2 && in_array($post['trip'], getConfig('modTrip'))) || (getConfig('adminsAreAnon') === 2 && in_array($post['trip'], getConfig('adminTrip')))) { $posterName = $post['name']; $posterTrip = null; } else { // Check if name is set $posterName = (empty($post['name']) ? getConfig('anonName') : $post['name']); // Check if trip isset $posterTrip = (empty($post['trip']) ? '' : ' <span class="trip">!'. $post['trip'] .'</span>'); } print '<fieldset id="'. $post['id'] .'">'; print '<legend><b>'. $post['title'] .'</b> <a href="#'. $post['id'].'">'. getLang('BY') .'</a> <b>'; if(empty($post['email'])) print $posterName . $posterTrip; else print '<a href="mailto:'. $post['email'] .'">'. $posterName . $posterTrip .'</a>'; if(in_array($post['trip'], getConfig('adminTrip'))) // Check if tripcode is Admin print ' <span class="admincap">## Admin</span>'; elseif(in_array($post['trip'], getConfig('modTrip'))) // Check if tripcode is Mod print ' <span class="modcap">## Mod</span>'; print '</b></legend>'; // Parse BBcode and other things in the post content print '<div class="postContent">'. parsePost($post['content']) .'</div><br />'; // Check if (USER WAS BANNED FOR THIS POST) if($post['ban']) print '<h6 class="ban">'. getLang('POSTBANNED') .'</h6>'; // Moderator tools if($auth == $ninechan['modPass']) { print '<h6>'; print '[<a href="'. $_SERVER['PHP_SELF'] .'?v=mod&del=true&id='. $post['id'] .'&tid='. $post['tid'] .'">'. getLang('DELETE') .'</a>]'."\r\n"; print '[<a href="'. $_SERVER['PHP_SELF'] .'?v=mod&ban='. ($post['ban'] ? 'false' : 'true') .'&id='. $post['id'] .'&tid='. $post['tid'] .'">'. getLang($post['ban'] ? 'UNBAN' : 'BAN') .'</a>]'."\r\n"; print '[IP: '.base64_decode($post['ip']).']'; print '</h6>'; } // Date and ID print '<h6><i>'. date(getConfig('dateFormat'), $post['date']) .' <a href="#'. $post['id'] .'">No.</a> <a href="'. $_SERVER['PHP_SELF'] .'?v=soup&id='. $post['tid'] .'&text=>>'. $post['id'] .'">'. $post['id'] .'</a> [<a href="'. $_SERVER['PHP_SELF'] .'?v=del&id='. $post['id'] .'" title="'. getLang('DELPOST') .'">X</a>]</i></h6>'; print '</fieldset>'; } // Moderator tools if($auth == $ninechan['modPass']) { print '<h6>'; // Purge button print '[<a href="'. $_SERVER['PHP_SELF'] .'?v=mod&del=purge&id='. $thread[0]['tid'] .'">'. getLang('PURGE') .'</a>]'."\r\n"; if($thread[0]['locked']) print '[<a href="'. $_SERVER['PHP_SELF'] .'?v=mod&lock=false&id='. $thread[0]['tid'] .'">'. getLang('UNLOCK') .'</a>]'; else print '[<a href="'. $_SERVER['PHP_SELF'] .'?v=mod&lock=true&id='. $thread[0]['tid'] .'">'. getLang('LOCK') .'</a>]'; print '</h6>'; } // Check if thread is locked and if true display message if($thread[0]['locked']) print '<h3>'. getLang('LOCKED') .'</h3>'; else print '<h3><a href="'. $_SERVER['PHP_SELF'] .'?v=soup&id='. $thread[0]['tid'] .'">'. getLang('NEWREPLY') .'</a></h3>'; } else { // If not return NONEXISTENT and stop print getLang('NONEXISTENT'); break; } break; // Posting case 'soup': if($remoteAddrState >= .6) { header('Location: /bot.html'); exit; } // Check if user is banned and if so don't display the form at all if(checkBan($_SERVER['REMOTE_ADDR'])) { print '<h2>'. getLang('USERBANNED') .'</h2>'; break; } // Print "global" form elements print '<form method="post" action="'. $_SERVER['PHP_SELF'] .'?v=boat">'; print '<table id="postForm" class="postForm">'; // Predefine that we're creating a new thread $newThread = true; // Predefine that the thread isn't locked $locked = false; // Predefine $threadTitle to avoid E_WARNINGs $threadTitle = ''; // Check if a thread ID is set if(isset($_GET['id'])) { // If so make sure it's numeric if(!is_numeric($_GET['id'])) { print '<h2>'. getLang('NOTNUMERIC') .'</h2>'; print '<a href="'. $_SERVER['PHP_SELF'] .'?v=index">'. getLang('RETURN') .'</a>'; break; } // Strip non-numerical characters from the string $threadId = preg_replace('/\D/', '', $_GET['id']); // Get the thread data $thread = getPosts($threadId); if(!$thread) { print '<h2>'. getLang('NONEXISTENT') .'</h2>'; print '<a href="'. $_SERVER['PHP_SELF'] .'?v=index">'. getLang('RETURN') .'</a>'; break; } // Reassign $thread $thread = $thread[0]; // Since we're not creating a thread set newThread to false $newThread = false; // Reassign $locked $locked = $thread['locked']; // Don't display posting form if thread is locked if($locked) { print '<h2>'. getLang('LOCKEDMSG') .'</h2>'; print '<meta http-equiv="refresh" content="2; URL="'. $_SERVER['PHP_SELF'] .'?v=thread&id='. $thread['tid'] .'" />'; } else { print '<h2>'. getLang('RETO') .' '. $thread['title'] .' ['. $thread['tid'] .']</h2>'; print '<input type="hidden" name="tid" value="'. $thread['tid'] .'" />'; $threadTitle = 'Re: '. $thread['title']; } } else print '<h2>'. getLang('NEWTHREAD') .'</h2>'; // Predefine the comment $comment = isset($_GET['text']) ? $_GET['text']."\r\n" : ''; if(!$locked) { //-- Only display post page if thread isn't locked print '<tr><td>'. getLang('NAME') .'</td><td><input name="name" type="text" value="'. @$_COOKIE[getConfig('cookiePrefix') .'name'] .'" /></td></tr>'; print '<tr><td>'. getLang('EMAIL') .'</td><td><input name="email" type="text" value="'. @$_COOKIE[getConfig('cookiePrefix') .'email'] .'" /></td></tr>'; print '<tr><td>'. getLang('TITLE') .'</td><td><input name="title" type="text" value="'. $threadTitle .'" /></td></tr>'; print '<tr><td>'. getLang('COMMENT') .'</td><td><textarea name="content" rows="6" cols="48">'. $comment .'</textarea></td></tr>'; if(getConfig('reCaptcha')) { // Display reCaptcha if enabled in config print '<tr><td>'. getLang('VERIFICATION') .'</td><td> <script src="https://www.google.com/recaptcha/api.js" async defer></script> <div class="g-recaptcha" data-sitekey="'. getConfig('reCaptchaPublic') .'" style="margin: auto; display: inline-block;"></div> <noscript> <div style="width: 302px; height: 352px; margin: auto; display: inline-block;"> <div style="width: 302px; height: 352px; position: relative;"> <div style="width: 302px; height: 352px; position: absolute;"> <iframe src="https://www.google.com/recaptcha/api/fallback?k='. getConfig('reCaptchaPublic') .'" frameborder="0" scrolling="no" style="width: 302px; height:352px; border-style: none;"></iframe> </div> <div style="width: 250px; height: 80px; position: absolute; border-style: none; bottom: 21px; left: 25px; margin: 0px; padding: 0px; right: 25px;"> <textarea id="g-recaptcha-response" name="g-recaptcha-response" class="g-recaptcha-response" style="width: 250px; height: 80px; border: 1px solid #c1c1c1; margin: 0px; padding: 0px; resize: none;" value=""></textarea> </div> </div> </div> </noscript></td></tr>'; } print '<tr><td>'. getLang('PASSWORD') .'</td><td><input name="password" type="password" placeholder="'. getLang('PASSWORDCONTEXT') .'" value="'. @$_COOKIE[getConfig('cookiePrefix') .'pass'] .'" /> <input value="'. getLang('SUBMIT') .'" type="submit" /></td></tr>'; print '</table></form>'; } break; // Submitting posts case 'boat': if($remoteAddrState >= .6) { header('Location: /bot.html'); exit; } // Check if IP banned if(checkBan($_SERVER['REMOTE_ADDR'])) { print '<h2>'. getLang('USERBANNED') .'</h2>'; break; } // Check reCaptcha if(getConfig('reCaptcha')) { // Verify the captcha $reCaptcha = verifyCaptcha($_POST['g-recaptcha-response']); // If reCaptcha is invalid die and display error message if(!$reCaptcha['success']) { print '<h2>'. getLang('INVALIDCAPTCHA') .'</h2><meta http-equiv="refresh" content="2; URL='. $_SERVER['PHP_SELF'] .'" />'; print nFoot(); exit; } } // Assign variables $submitData = [ 'title' => cleanString($_POST['title']), 'content' => cleanString($_POST['content']), 'name' => ($_N_NAME = cleanString($_POST['name'])), 'nameNT' => (strlen(strstr($_N_NAME, "#", true)) ? strstr($_N_NAME, "#", true) : $_N_NAME), 'trip' => parseTrip($_POST['name']), 'email' => ($_N_MAIL = ($_POST['email'] == 'noko' ? null : cleanString($_POST['email']))), 'date' => time(), 'password' => md5(strlen($_POST['password']) ? $_POST['password'] : generatePassword()), 'ip' => base64_encode($_SERVER['REMOTE_ADDR']), 'op' => ($_N_OP = (isset($_POST['tid']) ? 0 : 1)), 'id' => ($_N_OP ? ($sqldb->query("SELECT MAX(tid) AS `tid` FROM `". $sql['table'] ."` LIMIT 1")->fetch()['tid'] + 1) : cleanString($_POST['tid'], true, true)), 'noredir' => ($_N_MAIL == 'noko' ? true : false) ]; // Update last time replied if(isset($_POST['tid'])) { // Prepare the statement $updateLast = $sqldb->prepare("UPDATE `". $sql['table'] ."` SET `lastreply` = :lastreply WHERE `tid` = :tid AND `op` = 1"); // Bind the parameters $updateLast->bindParam(':lastreply', $submitData['date'], PDO::PARAM_INT); $updateLast->bindParam(':tid', $_POST['tid'], PDO::PARAM_INT); // Execute statement $updateLast->execute(); } // Assign cookies nCookie('name', $submitData['name']); nCookie('email', $submitData['email']); nCookie('pass', $submitData['password']); // Check if title is too short if(empty($submitData['title']) || strlen($submitData['title']) < getConfig('titleMinLength')) { print '<h2>'. getLang('TITLETOOSHORT') .'</h2>'; print '<meta http-equiv="refresh" content="2; URL='. $_SERVER['PHP_SELF'] .'" />'; break; } // Check if title is too long if(strlen($submitData['title']) > getConfig('titleMaxLength')) { print '<h2>'. getLang('TITLETOOLONG') .'</h2>'; print '<meta http-equiv="refresh" content="2; URL='. $_SERVER['PHP_SELF'] .'" />'; break; } // Check if comment is too short if(empty($submitData['title']) || strlen($submitData['content']) < getConfig('commentMinLength')) { print '<h2>'. getLang('COMMENTTOOSHORT') .'</h2>'; print '<meta http-equiv="refresh" content="2; URL='. $_SERVER['PHP_SELF'] .'" />'; break; } // Check if comment is too long if(strlen($submitData['content']) > getConfig('commentMaxLength')) { print '<h2>'. getLang('COMMENTTOOLONG') .'</h2>'; print '<meta http-equiv="refresh" content="2; URL='. $_SERVER['PHP_SELF'] .'" />'; break; } $submitPost = $sqldb->prepare("INSERT INTO `". $sql['table'] ."` (`title`, `name`, `trip`, `email`, `date`, `content`, `password`, `ip`, `op`, `tid`, `lastreply`) VALUES (:title, :name, :trip, :email, :date, :content, :password, :ipaddr, :op, :threadid, :lastrep)"); $submitPost->bindParam(':title', $submitData['title']); $submitPost->bindParam(':name', $submitData['nameNT']); $submitPost->bindParam(':trip', $submitData['trip']); $submitPost->bindParam(':email', $submitData['email']); $submitPost->bindParam(':date', $submitData['date']); $submitPost->bindParam(':content', $submitData['content']); $submitPost->bindParam(':password', $submitData['password']); $submitPost->bindParam(':ipaddr', $submitData['ip']); $submitPost->bindParam(':op', $submitData['op']); $submitPost->bindParam(':threadid', $submitData['id']); $submitPost->bindParam(':lastrep', $submitData['date']); $submitPost->execute(); print '<h1>'. getLang('POSTED') .'</h1>'; print '<meta http-equiv="refresh" content="1; URL='. $_SERVER['PHP_SELF'] . ($submitData['noredir'] ? '?v=index' : '?v=thread&id='. $submitData['id']) .'" />'; break; case 'del': if($remoteAddrState >= .6) { header('Location: /bot.html'); exit; } // Check if IP banned if(checkBan($_SERVER['REMOTE_ADDR'])) { print '<h2>'. getLang('USERBANNED') .'</h2>'; break; } // Assign array to variable so we can store things in it later $deletionData = array(); // If we're in _POST mode begin deletion preparation if(isset($_POST['id'])) { // Prepare statement to get the post's data $getPostData = $sqldb->prepare("SELECT * FROM `". $sql['table'] ." WHERE `id` = :pid LIMIT 1"); // Bind the ID $getPostData->bindParam(':pid', $_POST['id'], PDO::PARAM_INT); // Execute the statement $getPostData->execute(); // Fetch the data $getData = $getPostData->fetch(); // Error if the post couldn't be found if(!count($getData)) error(getLang('NONEXISTENT') .'<meta http-equiv="refresh" content="0; url='. $_SERVER['PHP_SELF'] .'?v=index" />'); // Check if the post is locked if($getData['locked']) { print '<h2>'. getLang('LOCKEDMSGDEL') .'</h2>'; print '<meta http-equiv="refresh" content="2; URL='. $_SERVER['PHP_SELF'] .'?v=index" />'; } else { // Match the password if($getData['password'] == md5($_POST['password'])) { // Use the moderator function to delete the post nMod('del', $getData['id'], true); print '<h2>'. getLang('DEL_SUCCEED') .'</h2>'; print '<meta http-equiv="refresh" content="2; URL='. $_SERVER['PHP_SELF'] .'?v=index" />'; } else { // Else display an error message print '<h2>'. getLang('DEL_FAILED') .'</h2>'; print '<meta http-equiv="refresh" content="2; URL='. $_SERVER['PHP_SELF'] .'?v=index" />'; } } // Else enter _GET mode } elseif(isset($_GET['id'])) { // Check if the post exists if(!is_numeric($_GET['id'])) error(getLang('NONEXISTENT') .'<meta http-equiv="refresh" content="0; url='. $_SERVER['PHP_SELF'] .'?v=index" />'); // Prepare statement to get post data $getPostData = $sqldb->prepare("SELECT * FROM `". $sql['table'] ." WHERE `id` = :pid LIMIT 1"); // Bind ID $getPostData->bindParam(':pid', $_GET['id'], PDO::PARAM_INT); // Execute statement $getPostData->execute(); // Fetch data $getData = $getPostData->fetch(); // Error if the post couldn't be found if(!count($getData)) error(getLang('NONEXISTENT') .'<meta http-equiv="refresh" content="0; url='. $_SERVER['PHP_SELF'] .'?v=index" />'); // Create form print '<form method="post" action="'. $_SERVER['PHP_SELF'] .'?v=del">'; if($getData['locked']) { print '<h2>'. getLang('LOCKEDMSGDEL') .'</h2>'; print '<meta http-equiv="refresh" content="2; URL='. $_SERVER['PHP_SELF'] .'?v=index" />'; } else { print '<h2>'. getLang('DELPOST') .' '. $getData['id'] .'</h2>'; print '<input type="hidden" name="id" value="'. $_GET['id'] .'" />'; } print '<table id="postForm" class="postForm">'; print '<tr><td>'. getLang('PASSWORD') .'</td>'; print '<td><input name="password" type="password" placeholder="'. getLang('PASSWORDCONTEXT') .'" value="'.@$_COOKIE[getConfig('cookiePrefix') .'pass'].'" /> '; print '<input value="'. getLang('SUBMIT') .'" type="submit" /></td></tr>'; print '</table>'; print '</form>'; } break; case 'destroy': header('Location: ./'); session_destroy(); exit; // Default action default: header('Location: '. $_SERVER['PHP_SELF'] .'?v=index'); // If invalid option is set redirect to index print '<meta http-equiv="refresh" content="0; url='. $_SERVER['PHP_SELF'] .'?v=index" />'; // Fallback because I've had experiences where header() didn't work properly break; } } else { header('Location: '. $_SERVER['PHP_SELF'] .'?v=index'); // If invalid option is set redirect to index print '<meta http-equiv="refresh" content="0; url='. $_SERVER['PHP_SELF'] .'?v=index" />'; // Fallback because I've had experiences where header() didn't work properly exit; } // Print footer print nFoot();