ninechan/index.php

1028 lines
38 KiB
PHP

<?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('&', '&amp;', $cap);
$cap = str_replace('"', '&quot;', $cap);
$cap = str_replace("'", '&#39;', $cap);
$cap = str_replace('<', '&lt;', $cap);
$cap = str_replace('>', '&gt;', $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') ? '&nbsp;<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 : '') .'
&copy; <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();