789 lines
25 KiB
PHP
789 lines
25 KiB
PHP
<?php
|
|
|
|
/*
|
|
* This is the endboard software, version beta 0.80
|
|
* It is a textboard written for the use in the darknets.
|
|
*
|
|
* This file holds all the basic functions needed. It can be
|
|
* included without side effects.
|
|
*
|
|
* The writing of this code started some time ago with another software
|
|
* called smolBBS. Although there is almost no original code left now,
|
|
* I still regard endboard as a fork of smolBBS.
|
|
* The author of smolBBS has required that the following text be
|
|
* distributed with any redistribution, so here it goes.
|
|
* The license and other conditions apply to endboard as well.
|
|
*
|
|
* IRC: *dulm @ irc.rizon.net
|
|
*
|
|
* Copyright (C) 2020 sandlind
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are
|
|
* met:
|
|
*
|
|
* (1) Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
*
|
|
* (2) Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in
|
|
* the documentation and/or other materials provided with the
|
|
* distribution.
|
|
*
|
|
* (3)The name of the author may not be used to
|
|
* endorse or promote products derived from this software without
|
|
* specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
|
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
|
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
// Check if the maximum requests as defined in the config file are
|
|
// exhausted or not
|
|
function check_max_requests($db, $settings, $ip)
|
|
{
|
|
|
|
$current = time();
|
|
|
|
if ( ($settings['max_requests_ip'] > 0)
|
|
&& ($_SERVER['REMOTE_ADDR'] != '127.0.0.1') ) {
|
|
$max_age = $current - ($settings['max_requests_timeframe'] * 60);
|
|
// max age is in minutes, so times 60 to go to seconds
|
|
$max_visits = $settings['max_requests_ip'];
|
|
} elseif ( ($settings['max_requests_tor'] > 0)
|
|
&& ($_SERVER['REMOTE_ADDR'] == '127.0.0.1') ) {
|
|
$max_age = $current - ($settings['max_requests_tor_timeframe'] * 60);
|
|
// max age is in minutes, so times 60 to go to seconds
|
|
$max_visits = $settings['max_requests_tor'];
|
|
} else {
|
|
return;
|
|
}
|
|
|
|
$statement = $db->prepare("SELECT unix_timestamp
|
|
FROM logs
|
|
WHERE type = 'portal'
|
|
AND ip = '$ip'
|
|
AND event in ('visit')
|
|
AND unix_timestamp > '$max_age'");
|
|
|
|
$result = $statement->execute();
|
|
|
|
$visits = 0;
|
|
|
|
while ($row = $result->fetchArray(SQLITE3_NUM)) {
|
|
$visits++;
|
|
}
|
|
|
|
if ( ($visits > $max_visits) ) {
|
|
$block_message = '429';
|
|
log_event($db, $settings, 'user', $block_message, $ip);
|
|
header( 'HTTP/1.1 429 Too Many Requests' );
|
|
quit($db, '429');
|
|
}
|
|
}
|
|
|
|
// Check if a message exists already in the database when importing.
|
|
function check_post_exists($db, $sub, $post_id)
|
|
{
|
|
|
|
$statement = $db->prepare("SELECT post_id
|
|
FROM threads
|
|
WHERE sub = '$sub'
|
|
AND post_id = '$post_id'");
|
|
$result = $statement->execute();
|
|
|
|
$counter = 0;
|
|
|
|
while ($row = $result->fetchArray(SQLITE3_NUM)) {
|
|
$counter++;
|
|
}
|
|
|
|
if ( ($counter < 1) ) {
|
|
// if the counter is smaller 1, there is no match
|
|
return FALSE;
|
|
} else {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
// Check if a sub exists before display
|
|
// If the name does not exist, name* is searched
|
|
function check_sub_exists($db, $sub)
|
|
{
|
|
|
|
if ( ($sub == 'overboard') || ($sub == 'main') ) {
|
|
return $sub;
|
|
}
|
|
|
|
$statement = $db->prepare("SELECT sub
|
|
FROM threads
|
|
WHERE sub = '$sub'
|
|
AND shadow = 'no'");
|
|
|
|
$result = $statement->execute();
|
|
|
|
$counter = 0;
|
|
|
|
while ($row = $result->fetchArray(SQLITE3_NUM)) {
|
|
$counter++;
|
|
$sub = "{$row[0]}";
|
|
}
|
|
|
|
if ( ($counter < 1) ) {
|
|
// if the counter is smaller 1, there is no match
|
|
$statement = $db->prepare("SELECT sub
|
|
FROM threads
|
|
WHERE sub GLOB '$sub*'
|
|
AND shadow = 'no'
|
|
ORDER BY sub
|
|
ASC LIMIT 1");
|
|
|
|
$result = $statement->execute();
|
|
|
|
$counter_2 = 0;
|
|
|
|
while ($row = $result->fetchArray(SQLITE3_NUM)) {
|
|
$counter_2++;
|
|
$sub = "{$row[0]}";
|
|
}
|
|
|
|
if ( ($counter_2 < 1) ) {
|
|
// if the counter is smaller 1, there is no match
|
|
return FALSE;
|
|
} else {
|
|
return $sub;
|
|
}
|
|
|
|
} else {
|
|
return $sub;
|
|
}
|
|
}
|
|
|
|
// record $_SERVER info
|
|
function debug_server($db, $settings)
|
|
{
|
|
|
|
$filename = $settings['work_dir'] . 'debug_server.txt';
|
|
$content = print_r($_SERVER, TRUE);
|
|
file_put_contents($filename, $content);
|
|
|
|
}
|
|
|
|
// Dump the contents of a sub, a thread or the whole board to a json
|
|
// file and send it to the browser.
|
|
// If used on the overboard, it will dump everything, including the
|
|
// messages from subs that are not actually displayed on the overboard.
|
|
function dump($db, $sub, $org_id, $settings)
|
|
{
|
|
// rewrite to include ranges
|
|
header( 'Content-Type: application/json' );
|
|
|
|
$json_dump = array();
|
|
|
|
if ( (!empty($org_id)) ) {
|
|
$statement = $db->prepare("SELECT post_id, text
|
|
FROM threads
|
|
WHERE sub = '$sub'
|
|
AND shadow = 'no'
|
|
AND org_id = '$org_id'");
|
|
$result = $statement->execute();
|
|
$json_dump['sub'] = $sub;
|
|
$json_dump['org_id'] = $org_id;
|
|
|
|
while ($row = $result->fetchArray(SQLITE3_NUM)) {
|
|
$post = array();
|
|
$post['post_id'] = "{$row[0]}";
|
|
$post['text'] = "{$row[1]}";
|
|
array_push($json_dump, $post);
|
|
}
|
|
} elseif ($sub == 'overboard') {
|
|
$statement = $db->prepare("SELECT post_id, org_id,
|
|
sub, text, timestamp
|
|
FROM threads
|
|
WHERE shadow = 'no'");
|
|
$result = $statement->execute();
|
|
|
|
while ($row = $result->fetchArray(SQLITE3_NUM)) {
|
|
$post = array();
|
|
$post['post_id'] = "{$row[0]}";
|
|
$post['org_id'] = "{$row[1]}";
|
|
$post['sub'] = "{$row[2]}";
|
|
$post['text'] = "{$row[3]}";
|
|
$post['timestamp'] = "{$row[4]}";
|
|
array_push($json_dump, $post);
|
|
}
|
|
} else {
|
|
$statement = $db->prepare("SELECT post_id, org_id, text
|
|
FROM threads
|
|
WHERE sub = '$sub'
|
|
AND shadow = 'no'");
|
|
$result = $statement->execute();
|
|
$json_dump['sub'] = $sub;
|
|
|
|
while ($row = $result->fetchArray(SQLITE3_NUM)) {
|
|
$post = array();
|
|
$post['post_id'] = "{$row[0]}";
|
|
$post['org_id'] = "{$row[1]}";
|
|
$post['text'] = "{$row[2]}";
|
|
array_push($json_dump, $post);
|
|
}
|
|
}
|
|
|
|
echo json_encode($json_dump, JSON_PRETTY_PRINT
|
|
| JSON_NUMERIC_CHECK
|
|
| JSON_UNESCAPED_UNICODE);
|
|
|
|
}
|
|
|
|
// Prevents additional tries to log in or to edit a post after too many
|
|
// failed tries. To be configured in the config file.
|
|
// Note: if enabled, this function can be used to dos the access to the
|
|
// admin and mod panels by sending constant junk requests.
|
|
// Still better than someone trying millions of user/password
|
|
// combinations, right ?
|
|
// If really paranoid, enable the admin panel only if you use it or
|
|
// let it listen on another address altogether.
|
|
// Read your logfiles to check if attacks are ongoing.
|
|
function fail2ban($db, $settings)
|
|
{
|
|
|
|
if ($settings['enable_fail2ban'] != TRUE) {
|
|
return;
|
|
}
|
|
|
|
$current = time();
|
|
$max_age = $current - ($settings['auth_time_frame'] * 60);
|
|
// max age is in minutes, so times 60 to go to seconds
|
|
|
|
if ($settings['superstrict'] == TRUE) {
|
|
$statement = $db->prepare("SELECT unix_timestamp
|
|
FROM logs
|
|
WHERE type = 'auth'
|
|
AND event in (
|
|
'name#tripkey combination not valid',
|
|
'wrong combination user/password',
|
|
'invalid token used',
|
|
'wrong combination name/token',
|
|
'fail2ban triggered')
|
|
AND unix_timestamp > '$max_age'");
|
|
} else {
|
|
$statement = $db->prepare("SELECT unix_timestamp
|
|
FROM logs
|
|
WHERE type = 'auth'
|
|
AND event in (
|
|
'name#tripkey combination not valid',
|
|
'wrong combination user/password',
|
|
'invalid token used',
|
|
'wrong combination name/token')
|
|
AND unix_timestamp > '$max_age'");
|
|
}
|
|
|
|
$result = $statement->execute();
|
|
|
|
$counter = 0;
|
|
|
|
while ($row = $result->fetchArray(SQLITE3_NUM)) {
|
|
$counter++;
|
|
}
|
|
|
|
if ($counter > $settings['auth_max']) {
|
|
$fail2ban_message = 'fail2ban triggered';
|
|
log_event($db, $settings, 'auth', $fail2ban_message, '');
|
|
header( 'HTTP/1.1 429 Too Many Requests' );
|
|
quit($db, '429');
|
|
}
|
|
}
|
|
|
|
// filter a variable according to different parameters and return the
|
|
// result
|
|
function filter($text, $type, $length)
|
|
{
|
|
if ( ( $type == 'alnum') ) {
|
|
$filtered_text = substr(
|
|
preg_replace("/[^0-9a-zA-Z]/", "", $text),
|
|
0, $length);
|
|
} elseif ( ($type == 'num') ) {
|
|
$filtered_text = substr(
|
|
preg_replace("/[^0-9]/", "", $text),
|
|
0, $length);
|
|
} elseif ( ($type == 'email') ) {
|
|
$filtered_text = substr(
|
|
preg_replace("/[^0-9a-zA-Z@._]/", "", $text),
|
|
0, $length);
|
|
}
|
|
|
|
return $filtered_text;
|
|
}
|
|
|
|
// Check how many pretty vars have been sent
|
|
function get_pretty_vars_count()
|
|
{
|
|
|
|
$raw_vars = explode('/', $_SERVER['REQUEST_URI']);
|
|
$count = count($raw_vars);
|
|
|
|
return $count;
|
|
|
|
}
|
|
|
|
// Return number of posts in sub or overboard, with or without replies.
|
|
function give_total_posts($db, $sub, $original_only, $settings)
|
|
{
|
|
|
|
if ( ($original_only) && ($sub != 'overboard') ) {
|
|
$statement = $db->prepare("SELECT post_id
|
|
FROM threads
|
|
WHERE sub = '$sub'
|
|
AND org_id = original
|
|
AND shadow = 'no'");
|
|
$result = $statement->execute();
|
|
$counter = 0;
|
|
while ($row = $result->fetchArray(SQLITE3_NUM)) {
|
|
$counter++;
|
|
}
|
|
} elseif ( ($sub == 'overboard') ) {
|
|
|
|
$no_overboard = '';
|
|
$last = array_pop($settings['no_overboard']);
|
|
|
|
foreach($settings['no_overboard'] as $ex_sub) {
|
|
$str = "'" . $ex_sub . "', ";
|
|
$no_overboard .= $str;
|
|
}
|
|
|
|
$no_overboard .= "'" . $last . "'";
|
|
$statement = $db->prepare("SELECT post_id
|
|
FROM threads
|
|
WHERE org_id = original
|
|
AND sub NOT IN ($no_overboard)
|
|
AND shadow = 'no'");
|
|
$result = $statement->execute();
|
|
$counter = 0;
|
|
while ($row = $result->fetchArray(SQLITE3_NUM)) {
|
|
$counter++;
|
|
}
|
|
} else {
|
|
$statement = $db->prepare("SELECT post_id
|
|
FROM threads
|
|
WHERE sub = '$sub'
|
|
AND shadow = 'no'");
|
|
$result = $statement->execute();
|
|
$counter = 0;
|
|
while ($row = $result->fetchArray(SQLITE3_NUM)) {
|
|
$counter++;
|
|
}
|
|
}
|
|
|
|
return $counter;
|
|
|
|
}
|
|
|
|
// Log an event to the db. Also, delete the overflow of logs as
|
|
// defined in the config file.
|
|
function log_event($db, $settings, $type, $text, $ip)
|
|
{
|
|
|
|
if ( ($settings['enable_logging'] != TRUE) ) {
|
|
return;
|
|
}
|
|
|
|
$current = time();
|
|
$timestamp = date('Y-m-d H:i:s', $_SERVER['REQUEST_TIME']);
|
|
|
|
$statement = $db->prepare("INSERT INTO logs(event, type, timestamp,
|
|
unix_timestamp, ip)
|
|
VALUES (?, '$type', '$timestamp',
|
|
'$current', ?)");
|
|
$statement->bindParam(1, $text);
|
|
$statement->bindParam(2, $ip);
|
|
$statement->execute();
|
|
|
|
if ( ($settings['cap_logs'] > 0) ) {
|
|
$statement = $db->prepare("DELETE FROM logs
|
|
WHERE ROWID IN
|
|
(SELECT ROWID FROM logs
|
|
ORDER BY ROWID DESC
|
|
LIMIT -1 OFFSET ?)");
|
|
// to prevent the db from bloating (and to prevent attacks), we
|
|
// allow only so many lines of logs at any one time, and we check
|
|
// this with each call.
|
|
$statement->bindParam(1, $settings['cap_logs']);
|
|
$result = $statement->execute();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Make all tables that are needed (one each for posts, keys, hashes
|
|
// (captchas), hashes (passwords) and logs).
|
|
// Also, the hashes for the captchas are cropped to 20000.
|
|
function make_tables($db)
|
|
{
|
|
|
|
// make basic tables: threads, captchas, logs, keys
|
|
$db->exec('CREATE TABLE IF NOT EXISTS "captchas" (
|
|
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
|
"hash" TEXT UNIQUE,
|
|
"unix_timestamp" INTEGER
|
|
)');
|
|
|
|
$db->exec('CREATE TABLE IF NOT EXISTS "logs" (
|
|
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
|
"timestamp" TEXT,
|
|
"unix_timestamp" INTEGER,
|
|
"type" TEXT,
|
|
"event" TEXT,
|
|
"ip" TEXT
|
|
)');
|
|
|
|
$db->exec('CREATE TABLE IF NOT EXISTS "keys" (
|
|
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
|
"name" TEXT UNIQUE NOT NULL,
|
|
"type" INTEGER NOT NULL,
|
|
"email" TEXT,
|
|
"key" TEXT,
|
|
"subs" TEXT,
|
|
"token" TEXT,
|
|
"timestamp_token" INTEGER
|
|
)');
|
|
|
|
$db->exec('CREATE TABLE IF NOT EXISTS "threads" (
|
|
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
|
"post_id" INTEGER NOT NULL,
|
|
"shadow" TEXT NOT NULL,
|
|
"sub" TEXT NOT NULL,
|
|
"global_id" TEXT NOT NULL UNIQUE,
|
|
"text_id" TEXT NOT NULL,
|
|
"text" TEXT NOT NULL,
|
|
"org_id" INTEGER NOT NULL,
|
|
"timestamp" TEXT,
|
|
"name" TEXT,
|
|
"tripcode" TEXT,
|
|
"original" INTEGER NOT NULL,
|
|
"move_message" TEXT,
|
|
"edit_message" TEXT,
|
|
UNIQUE(post_id, sub) ON CONFLICT IGNORE
|
|
)');
|
|
|
|
$statement = $db->prepare("DELETE FROM captchas
|
|
WHERE ROWID IN
|
|
(SELECT ROWID FROM captchas
|
|
ORDER BY ROWID DESC
|
|
LIMIT -1 OFFSET 20000)");
|
|
// to prevent the db from bloating (and to prevent attacks), we
|
|
// allow only 20000 captchas at any one time, and we check this
|
|
// with each call. This should be enough if your site is getting
|
|
// less or equal to 100.000 visitors a day.
|
|
// Total combinations of captcha and token are ca. 2000 * 62^250.
|
|
$result = $statement->execute();
|
|
|
|
}
|
|
|
|
// Make a token to grant access to the admin panel or the mod panel for
|
|
// a limited time. A way of having sessions without cookies.
|
|
// Also used as a hidden field in the post form to prevent double
|
|
// posting by sending the same input twice.
|
|
function make_token($length, $mode)
|
|
{
|
|
if ( $mode == 'alnum' ) {
|
|
$characters = '0123456789'
|
|
. 'abcdefghijklmnopqrstuvwxyz'
|
|
. 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
|
} elseif ( $mode == 'alpha' ) {
|
|
$characters = 'abcdefghijklmnopqrstuvwxyz'
|
|
. 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
|
} elseif ( $mode == 'num' ) {
|
|
$characters = '0123456789';
|
|
}
|
|
|
|
$counter = strlen($characters) - 1;
|
|
|
|
$random_string = '';
|
|
|
|
for ($i = 0; $i < $length; $i++) {
|
|
$index = random_int(0, $counter);
|
|
$random_string .= $characters[$index];
|
|
}
|
|
|
|
return $random_string;
|
|
}
|
|
|
|
// Close the database, so to not let this work for the garbage collector
|
|
// and loose time.
|
|
// Also display a final message (optionally), and exit.
|
|
function quit($db, $text)
|
|
{
|
|
|
|
$db->close();
|
|
|
|
if ($text != '') {
|
|
echo "$text";
|
|
}
|
|
|
|
exit;
|
|
}
|
|
|
|
// Parse pretty vars at different positions and with different filters
|
|
function read_pretty_vars($position, $filter, $length)
|
|
{
|
|
|
|
$var = '';
|
|
|
|
$raw_vars = explode('/', $_SERVER['REQUEST_URI']);
|
|
$pretty_vars = array();
|
|
|
|
foreach($raw_vars as $raw_var) {
|
|
if ($filter == 'number') {
|
|
$pretty_var = substr(preg_replace("/[^0-9]/", "",
|
|
$raw_var), 0, $length);
|
|
} elseif ($filter == 'alnum') {
|
|
$pretty_var = substr(preg_replace("/[^0-9a-zA-Z]/", "",
|
|
$raw_var), 0, $length);
|
|
} elseif ($filter == 'alnumplus') {
|
|
$pretty_var = substr(preg_replace("/[^0-9a-zA-Z+-_]/", "",
|
|
$raw_var), 0, $length);
|
|
}
|
|
array_push($pretty_vars, $pretty_var);
|
|
}
|
|
|
|
if ($position == 'last') {
|
|
$var = array_pop($pretty_vars);
|
|
} elseif ( (isset($pretty_vars[$position])) ) {
|
|
$var = $pretty_vars[$position];
|
|
}
|
|
|
|
return $var;
|
|
|
|
}
|
|
|
|
// Find the original post to a reply
|
|
function reset_org_id($db, $sub, $post_id)
|
|
{
|
|
|
|
$statement = $db->prepare("SELECT org_id
|
|
FROM threads
|
|
WHERE sub = '$sub'
|
|
AND shadow = 'no'
|
|
AND post_id = '$post_id'");
|
|
$result = $statement->execute();
|
|
|
|
while ($row = $result->fetchArray(SQLITE3_NUM)) {
|
|
$post_id = "{$row[0]}";
|
|
}
|
|
|
|
return $post_id;
|
|
}
|
|
|
|
// Set the css that we use
|
|
function set_css($mode, $settings)
|
|
{
|
|
|
|
$admin_css = array(
|
|
'auth_admin', 'admin', 'delete_admin',
|
|
'delete_logs', 'logs', 'password',
|
|
'setup', 'shadow', 'view_mods',
|
|
'change_mods', 'unshadow_post', 'unshadow_sub',
|
|
'import', 'dump_full'
|
|
);
|
|
|
|
$mod_css = array(
|
|
'apply', 'auth_mod', 'mod',
|
|
'delete_mod'
|
|
);
|
|
|
|
if (in_array($mode, $admin_css)) {
|
|
return 'admin';
|
|
} elseif (in_array($mode, $mod_css)) {
|
|
return 'mod';
|
|
} elseif ( (!empty($_POST['css'])) ) {
|
|
$css = filter($_POST['css'], 'alnum', 20);
|
|
// 20 chars should be enough to name a css
|
|
} else {
|
|
$raw_vars = explode('/', $_SERVER['REQUEST_URI']);
|
|
|
|
foreach($raw_vars as $raw_var) {
|
|
if ( (mb_strtolower(substr( $raw_var, 0, 4 )) === 'css=') ) {
|
|
// css= is four letters
|
|
$css_vars = explode('=', $raw_var);
|
|
$css = filter($css_vars[1], 'alnum', 20);
|
|
// 20 chars should be enough to name a css
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( !empty($css)
|
|
&& ( file_exists($settings['server_dir'] . 'css/' . $css . '.css') ) ){
|
|
return "$css";
|
|
} else {
|
|
return $settings['default_css'];
|
|
}
|
|
|
|
}
|
|
|
|
// Check what is expected from the request, and set mode accordingly.
|
|
// The variable "$short_mode" is read from the request.
|
|
// The elements in the triggers array other than 'tr', capture requests
|
|
// that are standard scans for vulns + target discovery.
|
|
// Part of the bot trap, which can be disabled in the config file
|
|
function set_mode($short_mode, $settings)
|
|
{
|
|
$triggers = array (
|
|
'tr', 'login', 'wellknown', 'wplogin',
|
|
'wpjson', 'wp', 'products', 'wpusers',
|
|
'wpadmin', 'wpadminer', 'adminer', 'phpmyadmin',
|
|
'wpuploads', 'wpcontent', 'wpconfig', 'wpincludes',
|
|
'static', 'img', 'images', 'uploads',
|
|
'styles', 'style', 'serverinfo', 'privatekey',
|
|
'serverstatus'
|
|
);
|
|
|
|
$short_modes = array (
|
|
'cm', 'dim', 'pa', 'ush',
|
|
'dsh', 'usha', 'dsa', 'aa',
|
|
'am', 'dt', 'a', 'm',
|
|
'ap', 'sp', 'im', 'da',
|
|
'dm', 'dl', 'su', 'lo',
|
|
'sh', 'iv', 'p', 'r',
|
|
'v', 's', 'b', 'd',
|
|
'df', 'e', 'u', 'rss'
|
|
);
|
|
|
|
$long_modes = array (
|
|
'view_mods', 'change_mods', 'password', 'unshadow_post',
|
|
'delete_post', 'unshadow_sub', 'delete_sub', 'auth_admin',
|
|
'auth_mod', 'destroy_token', 'admin', 'mod',
|
|
'apply', 'setup', 'import', 'delete_admin',
|
|
'delete_mod', 'delete_logs', 'subs', 'logs',
|
|
'shadow', 'individual_view', 'post', 'reply',
|
|
'view', 'view', 'bot', 'dump',
|
|
'dump_full', 'edit', 'user', 'feed'
|
|
);
|
|
|
|
if ( (in_array($short_mode, $triggers))
|
|
&& ($settings['enable_bot_trap']) ) {
|
|
$mode = 'trap';
|
|
} elseif ( (!in_array($short_mode, $short_modes)) ) {
|
|
$mode = 'landing';
|
|
// if nothing fits, we display the landing page
|
|
} else {
|
|
for ($i = 0; $i < count($short_modes); $i++) {
|
|
if ( ($short_modes[$i] == $short_mode) ) {
|
|
$mode = $long_modes[$i];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return $mode;
|
|
}
|
|
|
|
// In case of replies, check which post we are replying to
|
|
// (based on the pretty vars in GET requests)
|
|
function set_org_id()
|
|
{
|
|
|
|
$org_id = read_pretty_vars(3, 'number', 10);
|
|
// we read from the third position, and a message does not need more
|
|
// than 10 digits (=max 9 999 999 999 messages)
|
|
return $org_id;
|
|
|
|
}
|
|
|
|
// Get the page if it is defined, otherwise set to 1
|
|
function set_page()
|
|
{
|
|
|
|
if (get_pretty_vars_count() == 5) {
|
|
// the page is only defined if there are five vars
|
|
$page = read_pretty_vars(3, 'number', 10);
|
|
// read from third position, and a page does not need more
|
|
// than 10 digits
|
|
} else {
|
|
$page = 0;
|
|
// if there is no page given, we set it to zero
|
|
}
|
|
|
|
if ( ($page < 1) ) {
|
|
// if the page is zero, it means we did not
|
|
// get a number before
|
|
|
|
$page = read_pretty_vars(3, 'alnum', 3);
|
|
// read from the third position, and a page does not need more
|
|
// than 10 digits, this time we allow letters, too.
|
|
|
|
if ( ($page != 'all') ) {
|
|
|
|
$page = 1;
|
|
// if page is not "all", we start with the first
|
|
}
|
|
}
|
|
|
|
return $page;
|
|
}
|
|
|
|
// Determine if there is an original post_id given in the poststream
|
|
// (in case of replies)
|
|
function set_post_org_id()
|
|
{
|
|
|
|
$org_id = '';
|
|
|
|
if ( (!empty($_POST['org_id'])) ) {
|
|
$org_id = filter($_POST['org_id'], 'num', 10);
|
|
// no post id needs more than 10 digits
|
|
// (= 9 999 999 999 messages)
|
|
|
|
}
|
|
|
|
return $org_id;
|
|
}
|
|
|
|
// Determine which sub we use currently, first read from POST,
|
|
// than from the pretty vars (GET)
|
|
function set_sub($settings)
|
|
{
|
|
|
|
$sub = '';
|
|
|
|
if ( (!empty($_POST['sub'])) ) {
|
|
$sub = filter($_POST['sub'], 'alnum', $settings['max_name_sub']);
|
|
} elseif (get_pretty_vars_count() > 1) {
|
|
$sub = read_pretty_vars(2, 'alnum', $settings['max_name_sub']);
|
|
// read the sub from the second position
|
|
}
|
|
|
|
if ($sub == '') {
|
|
$sub = 'main';
|
|
}
|
|
|
|
return $sub;
|
|
}
|
|
|
|
// Get the quote, if there is one.
|
|
function set_quote()
|
|
{
|
|
|
|
if (get_pretty_vars_count() == 6) {
|
|
$quote = read_pretty_vars(4, 'alnum', 10);
|
|
// read from fourth position, and a post id does not need more
|
|
// than 10 digits
|
|
} else {
|
|
$quote = '';
|
|
}
|
|
|
|
return $quote;
|
|
|
|
}
|
|
|
|
// EOF
|