The 2.1x 2025 source code dump update.

This commit is contained in:
flash 2025-04-17 23:13:15 +02:00
parent 85a1260259
commit 332e119613
5 changed files with 88 additions and 154 deletions

View file

@ -1,6 +1,6 @@
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2014-2015, flashwave <me@flash.moe> Copyright (c) 2014-2025, flashwave <me@flash.moe>
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View file

@ -1,33 +1,9 @@
ninechan # ninechan
========
A simple text based discussion board of which you can find the demo, live development, suggestions and support boards on http://ninechan.flash.moe/ After the release of the final ninechan package/version, I've done a bunch of maintenance.
This repository makes all of that public.
So far this is not a continuation of the project and 2.1x isn't a drop-in update since it has a bunch of changes specific to my environment, most of which being an attempt to stop the flood of spam...
The version on GitHub is _usually_ in sync with the one on the development board. The source code is still licenced under the MIT licence.
When downloading ninechan make sure to download it from the Releases tab thing and not master to ensure the stability of your board. Thank you for your interest in ninechan :)
Requirements
------------
| *Server-Side* | *Client-Side* |
| ---------------------------------- | ---------------------------- |
| PHP >= 5.3 | Enabled JavaScript |
| Any PDO compatible Database Engine | CSS Compatibility |
| Short PHP Tags | Cookies enabled |
Features
--------
- Easy Installation and Upgrading
- Moderation
- Tripcodes
- Youtube Embedding
- Easy language changing and adding
- Quoting/"greentexting"
- Multiple styles
- reCAPTCHAs
- Password based post deletion
Help
----
Although I don't really see when you need help with the script just send me an email (email address is on github profile) or ask about it in the issues section

View file

@ -119,3 +119,14 @@ $ninechan['reCaptchaPublic'] = "6LdQwAYTAAAAAKSW9Q7U6qS6HFTwotccCMr1Ejri";
// reCAPTCHA Private key // reCAPTCHA Private key
$ninechan['reCaptchaPrivate'] = "6LdQwAYTAAAAAJ3NUhUmFyc814B7AM1LGbVwWKp2"; $ninechan['reCaptchaPrivate'] = "6LdQwAYTAAAAAJ3NUhUmFyc814B7AM1LGbVwWKp2";
$navBoards = [
'dev' => ['ninechan', 'dev', 'Board'],
//'sup' => ['ninechan', 'sup', 'Support'],
//'sug' => ['ninechan', 'sug', 'Suggestions'],
];
$navLinks = [
'Home' => '/',
'Downloads' => '/dl',
];

189
index.php
View file

@ -6,13 +6,21 @@
*/ */
// Set ninechan version, don't change this or it'll probably break things. // Set ninechan version, don't change this or it'll probably break things.
$version = '2.1'; $version = '2.1x';
// Language file versions this version is compatible with // Language file versions this version is compatible with
$langCompat = [ $langCompat = [
'2.1' '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 // Error messages
function error($data) { function error($data) {
@ -219,7 +227,7 @@ function nHead() {
foreach($styles as $url => $name) { foreach($styles as $url => $name) {
// Get the key of the first entry // Get the key of the first entry
$main = key($styles); $main = key($styles);
// Append styles to the header // 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 .= '<link rel="'. ($url == $main ? null : 'alternate ') .'stylesheet" type="text/css" href="'. $url .'" title="'. $name .'" '. ($url == $main ? null : 'disabled ') .'/>';
@ -232,7 +240,14 @@ function nHead() {
<body> <body>
<h1><a href="./">'. getConfig('title') .'</a></h1>'. <h1><a href="./">'. getConfig('title') .'</a></h1>'.
(getConfig('desc') ? '&nbsp;<i>'. getConfig('desc') .'</i>' : '') (getConfig('desc') ? '&nbsp;<i>'. getConfig('desc') .'</i>' : '')
.'<hr /><h6>[<a href="?v=index">'. getLang('INDEX') .'</a>] [<a href="?v=post">'. getLang('NEWTHREAD') .'</a>] [<a href="?v=mod">'. getLang('MANAGE') .'</a>]</h6><hr />'; .'<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; return $header;
@ -269,9 +284,9 @@ function nFoot() {
*/ */
$footer .= '<h6> $footer .= '<h6>
<a href="http://ninechan.flash.moe/" target="_blank">ninechan</a> <a href="https://patchii.net/flash/ninechan" target="_blank">ninechan</a>
'. (getConfig('showVersion') ? $version : '') .' '. (getConfig('showVersion') ? $version : '') .'
&copy; <a href="http://flash.moe/" target="_blank">Flashwave</a> &copy; <a href="https://flash.moe" target="_blank">flashwave</a>
</h6> </h6>
</body> </body>
</html>'; </html>';
@ -413,6 +428,15 @@ CREATE TABLE IF NOT EXISTS `". $sql['table'] ."` (
session_start(); // Start a session session_start(); // Start a session
$auth = @$_SESSION['mod']; // Set an alias for mod $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 the header
print nHead(); print nHead();
@ -438,6 +462,10 @@ if(isset($_GET['t'])) {
} }
// 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 // Check if the current IP is banned
if(checkBan($_SERVER['REMOTE_ADDR'])) if(checkBan($_SERVER['REMOTE_ADDR']))
print '<div class="banmsg">'. getLang('USERBANNEDMSG') .'</div><hr />'; print '<div class="banmsg">'. getLang('USERBANNEDMSG') .'</div><hr />';
@ -458,7 +486,7 @@ if(isset($_GET['v'])) {
print '<h2>'. getLang('THREADS') .'</h2>'; print '<h2>'. getLang('THREADS') .'</h2>';
// New thread link // New thread link
print '<h3><a href="'. $_SERVER['PHP_SELF'] .'?v=post">'. getLang('NEWTHREAD') .'</a></h3>'; print '<h3><a href="'. $_SERVER['PHP_SELF'] .'?v=soup">'. getLang('NEWTHREAD') .'</a></h3>';
// Get threads // Get threads
$threads = array_chunk(getPosts(), getConfig('threadsPerPage'), true); $threads = array_chunk(getPosts(), getConfig('threadsPerPage'), true);
@ -469,7 +497,7 @@ if(isset($_GET['v'])) {
print '<ol>'; print '<ol>';
foreach($threads[(isset($_GET['p']) && ($_GET['p'] - 1) >= 0 && array_key_exists(($_GET['p'] - 1), $threads)) ? $_GET['p'] - 1 : 0] as $thread) 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&amp;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 '<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>'; print '</ol>';
@ -489,7 +517,7 @@ if(isset($_GET['v'])) {
} }
// New thread link // New thread link
print '<h3><a href="'. $_SERVER['PHP_SELF'] .'?v=post">'. getLang('NEWTHREAD') .'</a></h3>'; print '<h3><a href="'. $_SERVER['PHP_SELF'] .'?v=soup">'. getLang('NEWTHREAD') .'</a></h3>';
break; break;
// Thread view // Thread view
@ -518,7 +546,7 @@ if(isset($_GET['v'])) {
if($thread[0]['locked']) if($thread[0]['locked'])
print '<h3>'. getLang('LOCKED') .'</h3>'; print '<h3>'. getLang('LOCKED') .'</h3>';
else else
print '<h3><a href='. $_SERVER['PHP_SELF'] .'?v=post&amp;id='. $thread[0]['tid'] .'>'. getLang('NEWREPLY') .'</a></h3>'; print '<h3><a href='. $_SERVER['PHP_SELF'] .'?v=soup&id='. $thread[0]['tid'] .'>'. getLang('NEWREPLY') .'</a></h3>';
// Moderator tools // Moderator tools
if($auth == $ninechan['modPass']) { if($auth == $ninechan['modPass']) {
@ -526,12 +554,12 @@ if(isset($_GET['v'])) {
print '<h6>'; print '<h6>';
// Purge button // Purge button
print '[<a href="'. $_SERVER['PHP_SELF'] .'?v=mod&amp;del=purge&amp;id='. $thread[0]['tid'] .'">'. getLang('PURGE') .'</a>]'."\r\n"; print '[<a href="'. $_SERVER['PHP_SELF'] .'?v=mod&del=purge&id='. $thread[0]['tid'] .'">'. getLang('PURGE') .'</a>]'."\r\n";
if($thread[0]['locked']) if($thread[0]['locked'])
print '[<a href="'. $_SERVER['PHP_SELF'] .'?v=mod&amp;lock=false&amp;id='. $thread[0]['tid'] .'">'. getLang('UNLOCK') .'</a>]'; print '[<a href="'. $_SERVER['PHP_SELF'] .'?v=mod&lock=false&id='. $thread[0]['tid'] .'">'. getLang('UNLOCK') .'</a>]';
else else
print '[<a href="'. $_SERVER['PHP_SELF'] .'?v=mod&amp;lock=true&amp;id='. $thread[0]['tid'] .'">'. getLang('LOCK') .'</a>]'; print '[<a href="'. $_SERVER['PHP_SELF'] .'?v=mod&lock=true&id='. $thread[0]['tid'] .'">'. getLang('LOCK') .'</a>]';
print '</h6>'; print '</h6>';
@ -595,15 +623,15 @@ if(isset($_GET['v'])) {
if($auth == $ninechan['modPass']) { if($auth == $ninechan['modPass']) {
print '<h6>'; print '<h6>';
print '[<a href="'. $_SERVER['PHP_SELF'] .'?v=mod&amp;del=true&id='. $post['id'] .'&amp;id='. $post['tid'] .'">'. getLang('DELETE') .'</a>]'."\r\n"; 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&amp;ban='. ($post['ban'] ? 'false' : 'true') .'&amp;id='. $post['id'] .'&amp;id='. $post['tid'] .'">'. getLang($post['ban'] ? 'UNBAN' : 'BAN') .'</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 '[IP: '.base64_decode($post['ip']).']';
print '</h6>'; print '</h6>';
} }
// Date and ID // Date and ID
print '<h6><i>'. date(getConfig('dateFormat'), $post['date']) .' <a href="#'. $post['id'] .'">No.</a> <a href="'. $_SERVER['PHP_SELF'] .'?v=post&amp;id='. $post['tid'] .'&amp;text=>>'. $post['id'] .'">'. $post['id'] .'</a> [<a href="'. $_SERVER['PHP_SELF'] .'?v=del&amp;id='. $post['id'] .'" title="'. getLang('DELPOST') .'">X</a>]</i></h6>'; 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>'; print '</fieldset>';
@ -615,12 +643,12 @@ if(isset($_GET['v'])) {
print '<h6>'; print '<h6>';
// Purge button // Purge button
print '[<a href="'. $_SERVER['PHP_SELF'] .'?v=mod&amp;del=purge&amp;id='. $thread[0]['tid'] .'">'. getLang('PURGE') .'</a>]'."\r\n"; print '[<a href="'. $_SERVER['PHP_SELF'] .'?v=mod&del=purge&id='. $thread[0]['tid'] .'">'. getLang('PURGE') .'</a>]'."\r\n";
if($thread[0]['locked']) if($thread[0]['locked'])
print '[<a href="'. $_SERVER['PHP_SELF'] .'?v=mod&amp;lock=false&amp;id='. $thread[0]['tid'] .'">'. getLang('UNLOCK') .'</a>]'; print '[<a href="'. $_SERVER['PHP_SELF'] .'?v=mod&lock=false&id='. $thread[0]['tid'] .'">'. getLang('UNLOCK') .'</a>]';
else else
print '[<a href="'. $_SERVER['PHP_SELF'] .'?v=mod&amp;lock=true&amp;id='. $thread[0]['tid'] .'">'. getLang('LOCK') .'</a>]'; print '[<a href="'. $_SERVER['PHP_SELF'] .'?v=mod&lock=true&id='. $thread[0]['tid'] .'">'. getLang('LOCK') .'</a>]';
print '</h6>'; print '</h6>';
@ -630,7 +658,7 @@ if(isset($_GET['v'])) {
if($thread[0]['locked']) if($thread[0]['locked'])
print '<h3>'. getLang('LOCKED') .'</h3>'; print '<h3>'. getLang('LOCKED') .'</h3>';
else else
print '<h3><a href="'. $_SERVER['PHP_SELF'] .'?v=post&amp;id='. $thread[0]['tid'] .'">'. getLang('NEWREPLY') .'</a></h3>'; print '<h3><a href="'. $_SERVER['PHP_SELF'] .'?v=soup&id='. $thread[0]['tid'] .'">'. getLang('NEWREPLY') .'</a></h3>';
} else { } else {
@ -642,7 +670,11 @@ if(isset($_GET['v'])) {
break; break;
// Posting // Posting
case 'post': 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 // Check if user is banned and if so don't display the form at all
if(checkBan($_SERVER['REMOTE_ADDR'])) { if(checkBan($_SERVER['REMOTE_ADDR'])) {
@ -651,7 +683,7 @@ if(isset($_GET['v'])) {
} }
// Print "global" form elements // Print "global" form elements
print '<form method="post" action="'. $_SERVER['PHP_SELF'] .'?v=submit">'; print '<form method="post" action="'. $_SERVER['PHP_SELF'] .'?v=boat">';
print '<table id="postForm" class="postForm">'; print '<table id="postForm" class="postForm">';
// Predefine that we're creating a new thread // Predefine that we're creating a new thread
@ -700,7 +732,7 @@ if(isset($_GET['v'])) {
if($locked) { if($locked) {
print '<h2>'. getLang('LOCKEDMSG') .'</h2>'; print '<h2>'. getLang('LOCKEDMSG') .'</h2>';
print '<meta http-equiv="refresh" content="2; URL="'. $_SERVER['PHP_SELF'] .'?v=thread&amp;id='. $thread['tid'] .'" />'; print '<meta http-equiv="refresh" content="2; URL="'. $_SERVER['PHP_SELF'] .'?v=thread&id='. $thread['tid'] .'" />';
} else { } else {
@ -749,7 +781,11 @@ if(isset($_GET['v'])) {
break; break;
// Submitting posts // Submitting posts
case 'submit': case 'boat':
if($remoteAddrState >= .6) {
header('Location: /bot.html');
exit;
}
// Check if IP banned // Check if IP banned
if(checkBan($_SERVER['REMOTE_ADDR'])) { if(checkBan($_SERVER['REMOTE_ADDR'])) {
@ -858,10 +894,14 @@ if(isset($_GET['v'])) {
print '<h1>'. getLang('POSTED') .'</h1>'; print '<h1>'. getLang('POSTED') .'</h1>';
print '<meta http-equiv="refresh" content="1; URL='. $_SERVER['PHP_SELF'] . ($submitData['noredir'] ? '?v=index' : '?v=thread&amp;id='. $submitData['id']) .'" />'; print '<meta http-equiv="refresh" content="1; URL='. $_SERVER['PHP_SELF'] . ($submitData['noredir'] ? '?v=index' : '?v=thread&id='. $submitData['id']) .'" />';
break; break;
case 'del': case 'del':
if($remoteAddrState >= .6) {
header('Location: /bot.html');
exit;
}
// Check if IP banned // Check if IP banned
if(checkBan($_SERVER['REMOTE_ADDR'])) { if(checkBan($_SERVER['REMOTE_ADDR'])) {
@ -963,103 +1003,10 @@ if(isset($_GET['v'])) {
} }
break; break;
// Moderator Authentication case 'destroy':
case 'mod': header('Location: ./');
if($auth == getConfig('modPass')) { // Check if authenticated session_destroy();
exit;
// Page title
print '<h2>'. getLang('MODLOGOUT') .'</h2>';
if(isset($_POST['modkill'])) { // POST request modkill is set...
session_destroy(); // ...kill moderator session...
header('Location: '. $_SERVER['PHP_SELF'] .'?v=mod'); // ...and redirect to ?v=mod
print '<meta http-equiv="refresh" content="0; url='. $_SERVER['PHP_SELF'] .'?v=mod" />'; // fallback
exit;
}
// Print logout form
print '<form method="post" action="'.$_SERVER['PHP_SELF'].'?v=mod">';
print getLang('MODTOOLS') .'<br />';
print '<input type="submit" value="'. getLang('LOGOUT') .'" name="modkill" />';
print '</form>';
// Ban handler
if(isset($_GET['ban']) && isset($_GET['id']) && isset($_GET['id'])) {
if($_GET['ban'] == "true")
nMod('ban', $_GET['id'], true);
else
nMod('ban', $_GET['id'], false);
header('Location: '. $_SERVER['PHP_SELF'] .'?v=thread&amp;id='.$_GET['id']);
print '<meta http-equiv="refresh" content="0; url='. $_SERVER['PHP_SELF'] .'?v=thread&amp;id='.$_GET['id'].'" />'; // fallback
exit;
}
// Deletion handler
if(isset($_GET['del']) && isset($_GET['id'])) {
if($_GET['del'] == "purge") {
nMod('prune', $_GET['id'], true);
header('Location: '. $_SERVER['PHP_SELF'] .'?v=index');
print '<meta http-equiv="refresh" content="0; url='. $_SERVER['PHP_SELF'] .'?v=index" />'; // fallback
exit;
} else {
if($_GET['del'] == "true")
nMod('del', $_GET['id'], true);
else
nMod('del', $_GET['id'], false);
header('Location: '. $_SERVER['PHP_SELF'] .'?v=thread&amp;id='.$_GET['id']);
print '<meta http-equiv="refresh" content="0; url='. $_SERVER['PHP_SELF'] .'?v=thread&amp;id='.$_GET['id'].'" />'; // fallback
exit;
}
}
// Lock handler
if(isset($_GET['lock']) && isset($_GET['id'])) {
if($_GET['lock'] == "true")
nMod('lock', $_GET['id'], true);
else
nMod('lock', $_GET['id'], false);
header('Location: '. $_SERVER['PHP_SELF'] .'?v=thread&amp;id='.$_GET['id']);
print '<meta http-equiv="refresh" content="0; url='. $_SERVER['PHP_SELF'] .'?v=thread&amp;id='.$_GET['id'].'" />'; // fallback
exit;
}
} else {
// Else display login screen
if(isset($_POST['modPass'])) {
if($_POST['modPass'] == $ninechan['modPass'])
$_SESSION['mod'] = $ninechan['modPass'];
header('Location: '. $_SERVER['PHP_SELF'] .'?v=mod');
print '<meta http-equiv="refresh" content="0; url='. $_SERVER['PHP_SELF'] .'?v=mod" />'; // fallback
exit;
}
print '<h2>'. getLang('MODLOGIN') .'</h2>';
print '<form method="post" action="'. $_SERVER['PHP_SELF'] .'?v=mod">';
print '<input type="password" name="modPass" /><input type="submit" value="'. getLang('LOGIN') .'" />';
print '</form>';
}
break;
// Default action // Default action
default: default:

View file

@ -12,8 +12,8 @@ $language = [
// Board // Board
'THREADS' => 'Threads', 'THREADS' => 'Threads',
'THREAD' => 'Thread', 'THREAD' => 'Thread',
'NEWTHREAD' => 'New Thread', 'NEWTHREAD' => '&#8288;N&#8288;e&#8288;w&#8288; &#8288;T&#8288;h&#8288;r&#8288;e&#8288;a&#8288;d&#8288;',
'NEWREPLY' => 'New Reply', 'NEWREPLY' => '&#8288;N&#8288;e&#8288;w&#8288; &#8288;R&#8288;e&#8288;p&#8288;l&#8288;y&#8288;',
'LOCKED' => 'Locked', 'LOCKED' => 'Locked',
'BY' => 'by', 'BY' => 'by',
'RETURN' => 'Return to Index', 'RETURN' => 'Return to Index',