509 lines
17 KiB
PHP
509 lines
17 KiB
PHP
<?php
|
|
|
|
/*
|
|
* This is the endboard software, version beta 0.73
|
|
* It is a textboard written for the use in the darknets.
|
|
*
|
|
* This file holds the functions used to show subs, forms and post history.
|
|
* 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.
|
|
*/
|
|
|
|
function get_first_post_text($db, $sub, $settings)
|
|
{
|
|
if ( ($sub == 'main') ){
|
|
return $settings['hover_title_main'];
|
|
}
|
|
|
|
$statement = $db->prepare("SELECT text
|
|
FROM threads
|
|
WHERE shadow = 'no'
|
|
AND sub = '$sub'
|
|
ORDER BY post_id ASC
|
|
LIMIT 1");
|
|
$result = $statement->execute();
|
|
|
|
while ($row = $result->fetchArray(SQLITE3_NUM)) {
|
|
$title = "{$row[0]}";
|
|
$title = htmlspecialchars($title, ENT_QUOTES, 'UTF-8');
|
|
}
|
|
|
|
return $title;
|
|
}
|
|
|
|
// Show the form to edit a post, prefilled with the original post
|
|
function show_edit_form($db, $sub, $post_id, $ip, $css, $settings)
|
|
{
|
|
if ( (check_free_space($db, $settings) == FALSE) ) {
|
|
echo '<h1>No editing possible, no space on filesystem</h1>';
|
|
return;
|
|
}
|
|
|
|
if ( (post_block_user($db, $settings, $ip) != TRUE) ) {
|
|
echo '<h1>Max posts exhausted. Retry later.</h1>';
|
|
return;
|
|
}
|
|
|
|
$statement = $db->prepare("SELECT text, original
|
|
FROM threads
|
|
WHERE sub = '$sub'
|
|
AND shadow = 'no'
|
|
AND post_id = '$post_id'
|
|
");
|
|
$result = $statement->execute();
|
|
|
|
while ($row = $result->fetchArray(SQLITE3_NUM)) {
|
|
$post_text = "{$row[0]}";
|
|
$original = "{$row[1]}";
|
|
}
|
|
|
|
$html_string = "<div class='form'><form action='/e' method='post'>"
|
|
. "<table class='newpost'>"
|
|
. "<input type='hidden' name='css' value='$css'>"
|
|
. "<input type='hidden' name='sub' value='$sub'>"
|
|
. '<tr><td><textarea rows=\'10\' cols=\'56\' name=\'edit_text\' '
|
|
. "required >$post_text</textarea></td></tr>"
|
|
. "<input type='hidden' name='original' value='$original'>"
|
|
. "<input type='hidden' name='post_id' value='$post_id'>";
|
|
|
|
$token = make_token(250);
|
|
$current = time();
|
|
|
|
$hash = hash('sha512', $token);
|
|
|
|
$html_string .= "<input type='hidden' name='post_token' "
|
|
. "value='$token'><tr><td>"
|
|
. "<textarea "
|
|
. "name='edit_combination' "
|
|
. "placeholder='Enter name#tripkey'></textarea>";
|
|
|
|
if ($settings['enable_timestamps']) {
|
|
$html_string .= "<input type='checkbox' value='timestamp' name='edit_timestamp'"
|
|
. "id='timestamp'><span class=\"label timestamp\">timestamp</span>";
|
|
}
|
|
|
|
$html_string .= "<input type='submit' value='Edit post'><br></td>";
|
|
$html_string .= '</table></form></div><hr>';
|
|
|
|
$statement = $db->prepare("INSERT OR IGNORE
|
|
INTO captchas(hash, unix_timestamp)
|
|
VALUES ('$hash', '$current')");
|
|
$statement->execute();
|
|
|
|
echo "$html_string";
|
|
}
|
|
|
|
// Show the postform with or without the captcha (according to setting),
|
|
// if on main show also the field to create new subs
|
|
function show_post_form($db, $msg, $sub, $settings, $org_id, $css, $quote, $ip)
|
|
{
|
|
|
|
if ( (check_free_space($db, $settings) == FALSE) ) {
|
|
echo '<h1>No posting possible, no space on filesystem</h1>';
|
|
return;
|
|
}
|
|
|
|
if ( (post_block_user($db, $settings, $ip) != TRUE) ) {
|
|
echo '<h1>Max posts exhausted. Retry later.</h1>';
|
|
return;
|
|
}
|
|
|
|
$html_string = "<div class='form'><form action='/p' method='post'>"
|
|
. "<table class='newpost'>"
|
|
. "<input type='hidden' name='css' value='$css'>";
|
|
|
|
if ( (!$org_id) &&
|
|
($sub == 'main') &&
|
|
($settings['enable_sub_creation'] == TRUE) ) {
|
|
$html_string .= "<tr><td><input type='text' name='sub' value='$sub' "
|
|
. "placeholder='name of sub'>";
|
|
}
|
|
|
|
if ( ($sub != 'main') ) {
|
|
$html_string .= "<input type='hidden' name='sub' value='$sub'>";
|
|
}
|
|
|
|
$html_string .= '<tr><td><textarea rows=\'10\' cols=\'56\' name=\'text\' ';
|
|
|
|
if ( (!empty($quote)) ) {
|
|
$html_string .= "required >>> $quote</textarea></td></tr>";
|
|
} else {
|
|
$html_string .= 'required placeholder=\'[b bold], [i italic],'
|
|
. '[li list point], [s strike through], [sp spoiler],'
|
|
. '[h headline], [u underlined], [url link], '
|
|
. '>> $post_id: local reference,'
|
|
. ' s/$sub/$post_id: global reference\'>'
|
|
. '</textarea></td></tr>';
|
|
}
|
|
|
|
if ( (!empty($org_id)) ) {
|
|
$html_string .= "<input type='hidden' name='org_id' value='$org_id'>";
|
|
}
|
|
|
|
$token = make_token(250);
|
|
$current = time();
|
|
|
|
if ($settings['use_captcha']) {
|
|
$math_one = rand(20,80);
|
|
$math_two = rand(1,19);
|
|
// the first number should be bigger than the next, to avoid
|
|
// negative results. Also, results should be below 100.
|
|
$math_type = rand(0,1);
|
|
// 0 means +, 1 means -
|
|
if ($math_type == 0) {
|
|
$answer = $math_one + $math_two;
|
|
$math_type = '+';
|
|
} elseif ($math_type == 1) {
|
|
$answer = $math_one - $math_two;
|
|
$math_type = '-';
|
|
}
|
|
|
|
$summary = ($math_one . $math_two . $math_type . $answer . $token);
|
|
$hash = hash('sha512', $summary);
|
|
|
|
$html_string .= "<tr><td><input type='hidden' name='math_one'"
|
|
. " value='$math_one'><input type='hidden' "
|
|
. "name='math_two' value='$math_two'>"
|
|
. "<input type='hidden' name='math_type' "
|
|
. "value='$math_type'><input type='hidden' "
|
|
. "name='post_token' value='$token'>$math_one "
|
|
. "$math_type $math_two <input type='text' "
|
|
. "required name='math_answer' maxlength=3 size=3 "
|
|
. "placeholder='???'>";
|
|
} else {
|
|
$hash = hash('sha512', $token);
|
|
|
|
$html_string .= "<input type='hidden' name='post_token' "
|
|
. "value='$token'><tr><td>";
|
|
}
|
|
|
|
if ( ($settings['enable_tripcodes']) ) {
|
|
$combination = make_tripcode($settings);
|
|
|
|
$html_string .= "<textarea "
|
|
. "name='combination' "
|
|
. ">$combination</textarea>"
|
|
. "<input type='hidden' name='combination_hidden'"
|
|
. " value='$combination'>";
|
|
}
|
|
|
|
|
|
if ( ($settings['enable_sage']) &&
|
|
(!empty($org_id)) ){
|
|
$html_string .= "<input type='checkbox' value='sage' name='sage'"
|
|
. "id='sage'><span class=\"label sage\">sage</span>";
|
|
}
|
|
|
|
if ($settings['enable_timestamps']) {
|
|
$html_string .= "<input type='checkbox' value='timestamp' name='timestamp'"
|
|
. "id='timestamp'><span class=\"label timestamp\">timestamp</span>";
|
|
}
|
|
|
|
$html_string .= "<input type='submit' value='Post this'><br></td>";
|
|
$html_string .= '</table></form></div><hr>';
|
|
|
|
$statement = $db->prepare("INSERT OR IGNORE
|
|
INTO captchas(hash, unix_timestamp)
|
|
VALUES ('$hash', '$current')");
|
|
$statement->execute();
|
|
|
|
echo "$html_string";
|
|
}
|
|
|
|
// Shows all versions of a specific post
|
|
function show_post_history($db, $sub, $post_id, $settings)
|
|
{
|
|
$html_string = '<div class=\'postcontainer\'>';
|
|
|
|
$statement = $db->prepare("SELECT original
|
|
FROM threads
|
|
WHERE sub = '$sub'
|
|
AND post_id = '$post_id'
|
|
");
|
|
$result = $statement->execute();
|
|
|
|
while ($row = $result->fetchArray(SQLITE3_NUM)) {
|
|
$original = "{$row[0]}";
|
|
}
|
|
|
|
$statement = $db->prepare("SELECT post_id, org_id, sub, text, timestamp,
|
|
name, tripcode
|
|
FROM threads
|
|
WHERE sub = '$sub'
|
|
AND original = '$original'
|
|
ORDER BY post_id");
|
|
$result = $statement->execute();
|
|
|
|
$posts = array();
|
|
$counter = 0;
|
|
|
|
while ($row = $result->fetchArray(SQLITE3_NUM)) {
|
|
$counter++;
|
|
$post = array();
|
|
$post_id = "{$row[0]}";
|
|
$org_id = "{$row[1]}";
|
|
$post_text = "{$row[3]}";
|
|
$post_text = break_text(bbcode_to_html($post_text, $settings, $sub),
|
|
$settings);
|
|
$id_text = make_id_text($post_id);
|
|
$timestamp = "{$row[4]}";
|
|
$name = "{$row[5]}";
|
|
$tripcode = "{$row[6]}";
|
|
array_push($post, $post_id);
|
|
array_push($post, $org_id);
|
|
array_push($post, $post_text);
|
|
array_push($post, $id_text);
|
|
array_push($post, $timestamp);
|
|
array_push($post, $name);
|
|
array_push($post, $tripcode);
|
|
array_push($posts, $post);
|
|
}
|
|
|
|
$display = array_reverse($posts);
|
|
|
|
foreach ($display as $old_post) {
|
|
|
|
$post_id = "$old_post[0]";
|
|
$org_id = "$old_post[1]";
|
|
$post_text = "$old_post[2]";
|
|
$id_text = $old_post[3];
|
|
$timestamp = "$old_post[4]";
|
|
$name = "$old_post[5]";
|
|
$tripcode = "$old_post[6]";
|
|
$html_string .= "<div><p id=\"$post_id" . "_" . "$sub\"></p>"
|
|
. "<div class='post'>#$id_text <br><br>"
|
|
. "$post_text<br><br></div><br></div>";
|
|
}
|
|
|
|
$html_string .= '</div>';
|
|
|
|
echo "$html_string";
|
|
|
|
}
|
|
|
|
// Show the existing subs to a user, including their count
|
|
// Differentiates between subs with > 10 posts (high-traffic)
|
|
// and lower (low-traffic). Also shows the last ten subs that
|
|
// were posted to.
|
|
function show_subs_count($db, $css, $settings)
|
|
{
|
|
$out = '';
|
|
|
|
if ( (!empty($settings['no_overboard'])) ) {
|
|
$last = array_pop($settings['no_overboard']);
|
|
|
|
foreach($settings['no_overboard'] as $no_overboard) {
|
|
$str = "'" . $no_overboard . "', ";
|
|
$out .= $str;
|
|
}
|
|
|
|
$out .= "'" . $last . "'";
|
|
|
|
}
|
|
|
|
$statement = $db->prepare("SELECT post_id
|
|
FROM threads
|
|
WHERE sub NOT IN ($out)
|
|
AND shadow = 'no'");
|
|
$result = $statement->execute();
|
|
$counter = 0;
|
|
|
|
while ($row = $result->fetchArray(SQLITE3_NUM)) {
|
|
$counter++;
|
|
}
|
|
|
|
$statement = $db->prepare("SELECT post_id
|
|
FROM threads
|
|
WHERE sub NOT IN ($out)
|
|
AND shadow = 'no'
|
|
AND post_id = org_id");
|
|
$result = $statement->execute();
|
|
$counter_org = 0;
|
|
|
|
while ($row = $result->fetchArray(SQLITE3_NUM)) {
|
|
$counter_org++;
|
|
}
|
|
|
|
$replies = $counter - $counter_org;
|
|
|
|
$title = $settings['hover_title_overboard'];
|
|
|
|
$html_string = "<h1> Subs with some traffic:</h1><h1>"
|
|
. "<a href=/s/overboard/$css"
|
|
. " title=\"$title\">overboard"
|
|
. "($counter_org/$replies)</a>";
|
|
|
|
$statement = $db->prepare("SELECT DISTINCT sub
|
|
FROM threads
|
|
WHERE shadow = 'no'
|
|
ORDER BY sub
|
|
COLLATE NOCASE");
|
|
$result = $statement->execute();
|
|
|
|
$high_traffic_subs = array();
|
|
$low_traffic_subs = array();
|
|
|
|
while ($row = $result->fetchArray(SQLITE3_NUM)) {
|
|
$display_sub = array();
|
|
$sub = "{$row[0]}";
|
|
$total_posts = give_total_posts($db, $sub, FALSE, $settings);
|
|
$total_org_posts = give_total_posts($db, $sub, TRUE, $settings);
|
|
$replies = $total_posts - $total_org_posts;
|
|
|
|
array_push($display_sub, $sub);
|
|
array_push($display_sub, $total_org_posts);
|
|
array_push($display_sub, $replies);
|
|
|
|
$title = get_first_post_text($db, $sub, $settings);
|
|
array_push($display_sub, $title);
|
|
|
|
if ( ($total_posts > 10) ) {
|
|
// we define any sub with more than ten messages as high traffic
|
|
// anything below as low traffic
|
|
array_push($high_traffic_subs, $display_sub);
|
|
} else {
|
|
array_push($low_traffic_subs, $display_sub);
|
|
}
|
|
}
|
|
|
|
|
|
foreach($high_traffic_subs as $display_sub) {
|
|
$title = $display_sub[3];
|
|
|
|
$html_string .= " | <a href=/s/$display_sub[0]/$css"
|
|
. " title=\"$title\">$display_sub[0]"
|
|
. "($display_sub[1]/$display_sub[2])</a>";
|
|
}
|
|
|
|
$html_string .= '</h1><br><br><br><h1>Other subs:</h1><h1>';
|
|
|
|
$temp = array_reverse($low_traffic_subs);
|
|
$first_display_sub = array_pop($temp);
|
|
$low_traffic_subs = array_reverse($temp);
|
|
|
|
$title = $first_display_sub[3];
|
|
|
|
$html_string .= "<a href=/s/$first_display_sub[0]/$css"
|
|
. " title=\"$title\">$first_display_sub[0]"
|
|
. "($first_display_sub[1]/$first_display_sub[2])</a>";
|
|
|
|
foreach($low_traffic_subs as $display_sub) {
|
|
$title = $display_sub[3];
|
|
|
|
$html_string .= " | <a href=/s/$display_sub[0]/$css"
|
|
. " title=\"$title\">$display_sub[0]"
|
|
. "($display_sub[1]/$display_sub[2])</a>";
|
|
}
|
|
|
|
$statement = $db->prepare("SELECT DISTINCT sub
|
|
FROM threads
|
|
WHERE shadow = 'no'
|
|
ORDER BY ROWID DESC
|
|
LIMIT 10");
|
|
$result = $statement->execute();
|
|
|
|
$html_string .= '</h1><br>'
|
|
. '<br><br><h1>Subs with recent posts:</h1><h1>';
|
|
|
|
while ($row = $result->fetchArray(SQLITE3_NUM)) {
|
|
$sub = "{$row[0]}";
|
|
$title = get_first_post_text($db, $sub, $settings);
|
|
$html_string .= "<a href=/s/$sub/$css title=\"$title\">$sub</a> ";
|
|
}
|
|
|
|
$html_string .= '</h1>';
|
|
|
|
echo "$html_string";
|
|
}
|
|
|
|
// Show the existing subs to a user, without the count
|
|
function show_subs_no_count($db, $css, $settings)
|
|
{
|
|
$title = $settings['hover_title_overboard'];
|
|
|
|
$html_string = "<h1><a href=/s/overboard/$css"
|
|
. " title=\"$title\">overboard</a>";
|
|
|
|
$statement = $db->prepare("SELECT DISTINCT sub
|
|
FROM threads
|
|
WHERE shadow = 'no'
|
|
ORDER BY sub
|
|
COLLATE NOCASE");
|
|
$result = $statement->execute();
|
|
|
|
while ($row = $result->fetchArray(SQLITE3_NUM)) {
|
|
$sub = "{$row[0]}";
|
|
|
|
if ( ($sub != '') ) {
|
|
$title = get_first_post_text($db, $sub, $settings);
|
|
$html_string .= " | <a href=/s/$sub/$css title=\"$title\">$sub</a>";
|
|
}
|
|
}
|
|
|
|
$html_string .= '</h1>';
|
|
|
|
echo "$html_string";
|
|
}
|
|
|
|
// Show the form that allows to set individual feeds.
|
|
function show_set_feeds_form($db, $settings, $css)
|
|
{
|
|
|
|
$html_string = '<br><br><br><h1>Set your multifeed:<br></h1>'
|
|
. '<p id=\'page\'>'
|
|
. '<div class=\'form\'><form action=\'/iv\' method=\'post\'>'
|
|
. '<table class=\'newpost\'>'
|
|
. '<tr><td>Show everything except: </td><td>'
|
|
. '<input type=\'text\' name=\'ex_subs\' placeholder=\''
|
|
. 'subs you do not want to see\' style=\'width: 500px;\'>'
|
|
. '<tr><td>OR Show nothing but: </td><td>'
|
|
. '<input type=\'text\' name=\'in_subs\' placeholder=\''
|
|
. 'the only subs you want to see\' style=\'width: 500px;\'>'
|
|
. '<input type=\'hidden\' name=\'css\' value=\'$css\'>'
|
|
. '<tr><td></td><td><input type=\'submit\' '
|
|
. 'value=\'Get my feeds\'>'
|
|
. '<br></td></tr></table></form></div></h1><hr>';
|
|
|
|
echo "$html_string";
|
|
}
|
|
|
|
// EOF
|