dev_endboard/srv/mob/index.php

408 lines
14 KiB
PHP

<?php
/*
* This is the endboard software, version beta 0.80, index file for mobiles
* It is a textboard, written for the use in the darknets.
*
* 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.
*/
$config_file = '/etc/opt/endboard/config.php';
// edit in case of custom paths
if ( file_exists($config_file) ) {
require_once $config_file;
// include the config file with the parameters and
// the landing page. No side effects.
$settings['config_file'] = $config_file;
} else {
echo "config file $config_file not found - exiting";
exit;
}
$db_file = $settings['work_dir'] . $settings['board_file'];
// initialize db
$db = new SQLite3($db_file, SQLITE3_OPEN_CREATE | SQLITE3_OPEN_READWRITE);
$db->busyTimeout(50000);
// set a long timeout
// if this condition is fulfilled, we have most likely crashed before
// need to do real error handling (rewrite)
if ( (!file_exists($db_file)) ) {
echo "<h3>Cannot find file $db_file, are the settings correct ?</h3>";
exit;
}
require_once('/opt/endboard/base.php');
// get the basic functions used in most cases
require_once('/opt/endboard/bot.php');
// get the functions used to deal with bots
require_once('/opt/endboard/display.php');
// get the functions needed for display
require_once('/opt/endboard/post.php');
// get the functions needed to make posts
require_once('/opt/endboard/search.php');
// get the functions needed to perform fulltext searches
require_once('/opt/endboard/show.php');
// get the functions needed to show forms, subs and post history
require_once('/opt/endboard/mobile.php');
// get the functions needed specifically for smartphones and the like
// Note: these are _almost_ all files which are sourced, with one exception:
// the haikubot in display.php needs one big file with an array of words,
// which is not always needed and therefore not sourced here.
//make tables if needed
make_tables($db);
// checks with which mode we are called
$short_mode = read_pretty_vars(2, 'alnum', 20);
// we read from the first position. Mode descriptions need 20 characters max.
// try to check the visitors (local) ip. On success, check if this ip should
// be blocked or not (according to the settings in the config file)
// also check if the portal has been passed or not, and display it
// if not (and it is enabled).
// Updated with more variables than just the (local) ip, it's now working
// surprisingly well, also on tor. The variable is still called 'ip'.
if ( (!empty($_SERVER['REMOTE_ADDR']))
&& ($settings['enable_logging'] == TRUE) ) {
$visitor_ip = $_SERVER['REMOTE_ADDR'];
$check_server = array (
'HTTP_USER_AGENT',
'HTTP_ACCEPT_ENCODING',
'HTTP_ACCEPT_LANGUAGE',
'HTTP_ACCEPT',
'HTTP_HOST',
'HTTP_X_REQUESTED_WITH',
'HTTP_X_I2P_DESTB64',
'HTTP_X_I2P_DESTB32',
'HTTP_X_I2P_DESTHASH',
'HTTP_UPGRADE_INSECURE_REQUESTS',
'HTTP_SEC_GPC',
'HTTP_PRIORITY',
'HTTP_SEC_FETCH_MODE',
'HTTP_SEC_FETCH_DEST'
);
foreach ($check_server as $id) {
if ( (!empty($_SERVER[$id])) ) {
$visitor_ip .= $_SERVER[$id];
}
}
$visitor_ip = hash('sha512', $visitor_ip);
if ( (empty($_POST['portal'])) && ($short_mode != 'b') ) {
check_portal_mob($db, $settings, $visitor_ip);
} elseif ( (!empty($_POST['portal'])) ) {
$portal_message = 'pass';
log_event($db, $settings, 'portal', $portal_message, $visitor_ip);
redirect_target();
quit($db, '');
}
check_max_requests($db, $settings, $visitor_ip);
bot_block($db, $settings, $visitor_ip);
} else {
$visitor_ip = '';
}
$mode = set_mode($short_mode, $settings);
// First actions that are common to many modes: check which sub is
// requested and if it exists
$sub_wanted = array(
'dump', 'post', 'reply', 'view'
);
if ( (in_array($mode, $sub_wanted)) ) {
$sub = set_sub_mob($settings);
}
$check_sub = array(
'dump', 'reply', 'view'
);
if ( (in_array($mode, $check_sub)) ) {
$sub_check = check_sub_exists($db, $sub);
if ($sub_check == FALSE) {
print_header_mob();
quit($db, "<h3>Sub '$sub' does not exist.</h3>");
} else {
$sub = $sub_check;
}
}
// Show the header, this is for almost all modes
$show_header = array(
'individual_view', 'landing', 'post', 'reply',
'subs', 'view'
);
if ( (in_array($mode, $show_header)) ) {
print_header_mob();
}
// Do particular actions for the mode we have been called with and exit.
// Note: there are no breaks in any of these cases, as there usually would.
// Instead, each case calls the quit function at some point, which exits
// the script.
switch($mode) {
// exports a thread, a sub or the whole board to a json file
// and sents it to the user
case 'dump':
$org_id = read_pretty_vars(3, 'number', 10);
// we read from the third postion, ten digits is enough
dump($db, $sub, $org_id, $settings);
quit($db, "");
// shows an individual feed
case 'individual_view':
$ex_subs = array();
$in_subs = array();
$subs = read_pretty_vars(2, 'alnumplus', 1500);
// we read from the second position. 1500 characters
// should be enough to describe the subs (at least ~70 subs).
$title = '';
if (strpos($subs, '-') !== FALSE) {
$ex_subs = explode('-', $subs);
} elseif (strpos($subs, '+') !== FALSE) {
$in_subs = explode('+', $subs);
} elseif ( (!empty($_POST['ex_subs'])) ) {
$ex_subs = explode(' ', $_POST['ex_subs']);
} elseif ( (!empty($_POST['in_subs'])) ) {
$in_subs = explode(' ', $_POST['in_subs']);
} else {
quit($db, '<h3>Please choose subs to include or exclude.</h3>');
}
if (count($ex_subs) > 0) {
// if we have at least one exsub
foreach($ex_subs as $ex_sub) {
$ex_sub = filter($ex_sub, 'alnum', $settings['max_name_sub']);
if ( ($ex_sub != '')
&& (check_sub_exists($db, $ex_sub) == TRUE) ) {
$title .= "-" . $ex_sub;
}
}
$description = "ex";
} elseif (count($in_subs) > 0) {
// otherwise, if we have at least one insub
foreach($in_subs as $in_sub) {
$in_sub = filter($in_sub, 'alnum', $settings['max_name_sub']);
if ( ($in_sub != '')
&& (check_sub_exists($db, $in_sub) == TRUE) ) {
$title .= "+" . $in_sub;
}
}
$description = "inc";
}
if ( (empty($title)) ) {
$quit_message = '<h3>The subs you chose do not exist. Please '
. 'choose existing subs to include or exclude.</h3>';
quit($db, $quit_message);
}
$html_string = "<title>$title</title></head>"
. '<body><div class="site-container">';
echo $html_string;
print_hamburger_menu($db, $settings, 'overboard', '0', '0');
$total_posts = print_individual_feed_mob($db, $settings,
$ex_subs, $in_subs);
quit($db, "");
// displays the landing page as defined in the config file
// also: check if there was a request URI, if yes, mark as bot
case 'landing':
if ( ( (!empty($_SERVER['REQUEST_URI'])) )
&& ( (strlen($_SERVER['REQUEST_URI']) > 6) ) ) {
// if more than 6 chars to go to the landing page,
// this could be a bot, especially if repeated
$bot_message = 'landing page bot request';
log_event($db, $settings, 'bot', $bot_message, $visitor_ip);
}
$html_string = "<title>landing</title></head>"
. '<body><div class="site-container">'
. '<div class="posts" id="posts">';
echo "$html_string";
print_hamburger_menu($db, $settings, 'overboard', '', '');
echo '<div class="posts" id="posts">';
echo '<div class="message first">';
show_landing_page('');
show_subs_no_count_mob($db);
echo '</div>';
quit($db, "");
// makes a new post
case 'post':
$org_id = set_post_org_id();
$text = strip_tags($_POST['text']);
$text_id = hash('sha512', $text);
check_spam($db, $text, $settings);
if (check_free_space($db, $settings) == FALSE) {
$quit_message = '<h3>Filesystem is almost full, '
. 'no posting possible!</h3>';
quit($db, $quit_message);
} elseif ( (check_original_content($db, $settings, $sub,
$text_id, $org_id) == FALSE) ) {
$quit_message = '<h3>This text has been posted before, '
. 'the admin requests original content.</h3>';
quit($db, $quit_message);
} elseif ( (mb_strtolower(substr( $sub, 0, 9 )) === 'overboard') ) {
// overboard has nine characters
$quit_message = '<h3>Subs cannot be named \'overboard\', also'
. ' their names cannot start with it.</h3>';
quit($db, $quit_message);
} elseif ( (mb_strtolower(substr( $sub, 0, 4 )) === 'main')
&& ($sub != 'main') ) {
// main has four characters
quit($db, '<h3>Subs names cannot start with \'main\'.</h3>');
} elseif ( (post_block_user($db, $settings, $visitor_ip) != TRUE) ) {
$post_block_message = '429 - too many user posts';
log_event($db, $settings, 'user', $post_block_message, $visitor_ip);
header( 'HTTP/1.1 429 Too Many Requests' );
quit($db, '429');
}
check_captcha($db, $settings);
$post_message = "post attempt";
log_event($db, $settings, 'user', $post_message, $visitor_ip);
make_post($db, $sub, $settings, $text, $org_id);
answer_redirect_mob($sub);
quit($db, "");
// shows a thread
case 'reply':
$org_id = set_org_id_mob();
if ( (!check_org_id_exists($db, $sub, $org_id)) ) {
quit($db, "<h3>Post $org_id on sub $sub does not exist.</h3>");
}
$quote = set_quote_mob();
$msg = ($sub . '/' . $org_id);
$html_string = "<title>$msg</title></head>"
. '<body><div class="site-container">';
echo "$html_string";
print_thread_mob($db, $sub, $settings, $org_id);
show_post_form_mob($db, $msg, $sub, $settings, $org_id,
$quote, $visitor_ip);
quit($db, '');
// displays all subs, including their message counts
case 'subs':
$html_string = "<title>available subs</title></head>"
. '<body><div class="site-container">'
. '<div class="posts" id="posts">';
echo $html_string;
show_subs_count_mob($db, $settings);
echo '</div></div></body></html>';
quit($db, "");
// shows a sub or the overboard
case 'view':
$page = set_page_mob();
if ( $sub == 'overboard' ) {
$title = $settings['title'];
} else {
$title = "sub/$sub";
}
$html_string = "<title>$title</title></head>"
. '<body><div class="site-container">';
echo $html_string;
$total_posts = give_total_posts($db, $sub, TRUE, $settings);
if ( ($page != 'all')
&& ((($settings['pagination'] * $page) >
($total_posts + $settings['pagination']))) ) {
quit($db, "<h3>Not enough posts to display page = $page...!</h3>");
}
print_hamburger_menu($db, $settings, $sub, $total_posts, $page);
if ( ($sub != 'overboard') ) {
print_sub_mob($db, $sub, $settings, $page);
show_post_form_mob($db, '', $sub, $settings, '', '', $visitor_ip);
} else {
print_overboard_mob($db, $settings, $page);
}
echo '</div></div></body></html>';
quit($db, "");
}
// EOF