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)
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
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.
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
Thank you for your interest in ninechan :)

View file

@ -119,3 +119,14 @@ $ninechan['reCaptchaPublic'] = "6LdQwAYTAAAAAKSW9Q7U6qS6HFTwotccCMr1Ejri";
// reCAPTCHA Private key
$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.
$version = '2.1';
$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) {
@ -219,7 +227,7 @@ function nHead() {
foreach($styles as $url => $name) {
// Get the key of the first entry
$main = key($styles);
$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 ') .'/>';
@ -232,7 +240,14 @@ function nHead() {
<body>
<h1><a href="./">'. getConfig('title') .'</a></h1>'.
(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;
@ -269,9 +284,9 @@ function nFoot() {
*/
$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 : '') .'
&copy; <a href="http://flash.moe/" target="_blank">Flashwave</a>
&copy; <a href="https://flash.moe" target="_blank">flashwave</a>
</h6>
</body>
</html>';
@ -413,6 +428,15 @@ CREATE TABLE IF NOT EXISTS `". $sql['table'] ."` (
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();
@ -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
if(checkBan($_SERVER['REMOTE_ADDR']))
print '<div class="banmsg">'. getLang('USERBANNEDMSG') .'</div><hr />';
@ -458,7 +486,7 @@ if(isset($_GET['v'])) {
print '<h2>'. getLang('THREADS') .'</h2>';
// 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
$threads = array_chunk(getPosts(), getConfig('threadsPerPage'), true);
@ -469,7 +497,7 @@ if(isset($_GET['v'])) {
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&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>';
@ -489,7 +517,7 @@ if(isset($_GET['v'])) {
}
// 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;
// Thread view
@ -518,7 +546,7 @@ if(isset($_GET['v'])) {
if($thread[0]['locked'])
print '<h3>'. getLang('LOCKED') .'</h3>';
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
if($auth == $ninechan['modPass']) {
@ -526,12 +554,12 @@ if(isset($_GET['v'])) {
print '<h6>';
// 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'])
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
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>';
@ -595,15 +623,15 @@ if(isset($_GET['v'])) {
if($auth == $ninechan['modPass']) {
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&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&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=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>';
@ -615,12 +643,12 @@ if(isset($_GET['v'])) {
print '<h6>';
// 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'])
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
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>';
@ -630,7 +658,7 @@ if(isset($_GET['v'])) {
if($thread[0]['locked'])
print '<h3>'. getLang('LOCKED') .'</h3>';
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 {
@ -642,7 +670,11 @@ if(isset($_GET['v'])) {
break;
// 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
if(checkBan($_SERVER['REMOTE_ADDR'])) {
@ -651,7 +683,7 @@ if(isset($_GET['v'])) {
}
// 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">';
// Predefine that we're creating a new thread
@ -700,7 +732,7 @@ if(isset($_GET['v'])) {
if($locked) {
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 {
@ -749,7 +781,11 @@ if(isset($_GET['v'])) {
break;
// Submitting posts
case 'submit':
case 'boat':
if($remoteAddrState >= .6) {
header('Location: /bot.html');
exit;
}
// Check if IP banned
if(checkBan($_SERVER['REMOTE_ADDR'])) {
@ -858,10 +894,14 @@ if(isset($_GET['v'])) {
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;
case 'del':
if($remoteAddrState >= .6) {
header('Location: /bot.html');
exit;
}
// Check if IP banned
if(checkBan($_SERVER['REMOTE_ADDR'])) {
@ -963,103 +1003,10 @@ if(isset($_GET['v'])) {
}
break;
// Moderator Authentication
case 'mod':
if($auth == getConfig('modPass')) { // Check if authenticated
// 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;
case 'destroy':
header('Location: ./');
session_destroy();
exit;
// Default action
default:

View file

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