644 lines
21 KiB
PHP
644 lines
21 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 functions used to make posts. 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.
|
|
*/
|
|
|
|
// Give a new location to the browser.
|
|
// Does not work with lynx, unfortunately, but then the link can be used.
|
|
function answer_redirect($sub, $css, $post_id, $settings)
|
|
{
|
|
|
|
$random_string = make_token(10, 'alpha');
|
|
|
|
if ( ( $settings['enable_tripcodes'] == TRUE ) &&
|
|
( !empty($_POST['combination']) ) &&
|
|
( !empty($_POST['combination_hidden']) ) &&
|
|
( $_POST['combination'] == $_POST['combination_hidden'] ) ) {
|
|
header( "refresh:10;url=/s/$sub/css=$css/random=$random_string" );
|
|
// we wait 10 seconds with the redirection
|
|
$credentials = $_POST['combination'];
|
|
$html_string = '<title>wait for it...</title></head>'
|
|
. '<h1>Redirection in about 10 secs.'
|
|
. ' Save this string, if you '
|
|
. ' want to edit your post later on: <br>'
|
|
. "$credentials<br>"
|
|
. ' If the redirection does not work, click '
|
|
. "<a href='/s/$sub/css=$css/"
|
|
. "random=$random_string'>here</a>"
|
|
. ' to go back.';
|
|
} else {
|
|
header( "refresh:3;url=/s/$sub/css=$css/random=$random_string" );
|
|
// we wait 3 seconds with the redirection
|
|
|
|
$html_string = '<title>wait for it...</title></head>'
|
|
. '<h1>Redirection in about 3 secs.'
|
|
. ' If that does not work, go '
|
|
. "<a href='/s/$sub/css=$css/"
|
|
. "random=$random_string'>back</a>.";
|
|
}
|
|
|
|
echo "$html_string";
|
|
}
|
|
|
|
// If the post is a reply, put the original post on top.
|
|
function bump_message($db, $org_id, $sub, $settings)
|
|
{
|
|
if ( $settings['auto_sage'] > 0 ) {
|
|
|
|
$statement = $db->prepare("SELECT global_id
|
|
FROM threads
|
|
WHERE org_id = '$org_id'
|
|
AND sub = '$sub'");
|
|
$result = $statement->execute();
|
|
|
|
$counter = 0;
|
|
|
|
|
|
while ($row = $result->fetchArray(SQLITE3_NUM)) {
|
|
$counter++;
|
|
}
|
|
|
|
if ( $counter > $settings['auto_sage'] ) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
$statement = $db->prepare("SELECT post_id, text, global_id, text_id,
|
|
timestamp, name, tripcode, original,
|
|
edit_message, move_message
|
|
FROM threads
|
|
WHERE original = '$org_id'
|
|
AND shadow = 'no'
|
|
AND sub = '$sub'");
|
|
$result = $statement->execute();
|
|
|
|
while ($row = $result->fetchArray(SQLITE3_NUM)) {
|
|
$post_id = "{$row[0]}";
|
|
$text = "{$row[1]}";
|
|
$global_id = "{$row[2]}";
|
|
$text_id = "{$row[3]}";
|
|
$timestamp = "{$row[4]}";
|
|
$name = "{$row[5]}";
|
|
$tripcode = "{$row[6]}";
|
|
$original = "{$row[7]}";
|
|
$edit_message = "{$row[8]}";
|
|
$move_message = "{$row[9]}";
|
|
}
|
|
|
|
$statement = $db->prepare("DELETE FROM threads
|
|
WHERE original = '$org_id'
|
|
AND shadow = 'no'
|
|
AND sub = '$sub'");
|
|
$result = $statement->execute();
|
|
|
|
$statement = $db->prepare("INSERT INTO threads(post_id, sub, text,
|
|
org_id, shadow,
|
|
global_id, text_id,
|
|
timestamp, name, tripcode,
|
|
original,
|
|
edit_message,
|
|
move_message)
|
|
VALUES ('$post_id', '$sub', ?, '$org_id',
|
|
'no', '$global_id', '$text_id',
|
|
'$timestamp', '$name', '$tripcode',
|
|
'$original', ?, ?)");
|
|
$statement->bindParam(1, $text);
|
|
$statement->bindParam(2, $edit_message);
|
|
$statement->bindParam(3, $move_message);
|
|
$statement->execute();
|
|
|
|
}
|
|
|
|
// Check the hashed captcha against the hashed solutions in the db.
|
|
function check_captcha($db, $settings)
|
|
{
|
|
if ( (!isset($_POST['post_token'])) ) {
|
|
quit($db, '<h1>What are you up to ? Use the postform.</h1>');
|
|
}
|
|
|
|
if ( ($settings['use_captcha'] == FALSE) ){
|
|
$post_hash = hash('sha512', $_POST['post_token']);
|
|
} elseif ( (!isset($_POST['math_one']))
|
|
|| (!isset($_POST['math_two']))
|
|
|| (!isset($_POST['math_type']))
|
|
|| (!isset($_POST['math_answer'])) ) {
|
|
quit($db, '<h1>What are you up to ? Use the postform.</h1>');
|
|
} else {
|
|
$post_summary = ($_POST['math_one'] . $_POST['math_two'] .
|
|
$_POST['math_type'] . $_POST['math_answer'] .
|
|
$_POST['post_token']);
|
|
$post_hash = hash('sha512', $post_summary);
|
|
}
|
|
|
|
$current = time();
|
|
$max_age = $current - $settings['lifetime_captcha'] * 60 * 60;
|
|
// lifetime is in hours in the configfile,
|
|
// so times 60 * 60 to go to seconds
|
|
|
|
$statement = $db->prepare("SELECT hash, unix_timestamp
|
|
FROM captchas
|
|
WHERE hash = '$post_hash'
|
|
AND unix_timestamp > '$max_age'");
|
|
$result = $statement->execute();
|
|
|
|
$counter = 0;
|
|
|
|
while ($row = $result->fetchArray(SQLITE3_NUM)) {
|
|
$counter++;
|
|
}
|
|
|
|
if ( ($settings['use_captcha'] == FALSE) && ($counter < 1) ) {
|
|
$quit_message = '<h1>Unauthorized attempt to post.'
|
|
. 'Use a newly opened postform.</h1>';
|
|
quit($db, $quit_message);
|
|
} elseif ( ($counter < 1) ) {
|
|
quit($db, '<h1>wrong answer or captcha expired, try again</h1>');
|
|
} else {
|
|
$statement = $db->prepare("DELETE FROM captchas
|
|
WHERE hash = '$post_hash'");
|
|
$result = $statement->execute();
|
|
}
|
|
}
|
|
|
|
// Check if we have enough free space on the harddisk to allow new posts
|
|
function check_free_space($db, $settings)
|
|
{
|
|
|
|
$free_space = disk_free_space($settings['work_dir']);
|
|
|
|
if ($free_space < ($settings['min_space'] * 1024 * 1024)) {
|
|
// the setting is in Megabyte, free space operates in bytes
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// Check if a name exists already, and if yes, check credentials.
|
|
// If not, return false.
|
|
function check_name($db, $name, $tripkey)
|
|
{
|
|
$statement = $db->prepare("SELECT tripcode
|
|
FROM threads
|
|
WHERE name = '$name'");
|
|
$result = $statement->execute();
|
|
|
|
$tripcode = '';
|
|
|
|
while ($row = $result->fetchArray(SQLITE3_NUM)) {
|
|
$tripcode = "{$row[0]}";
|
|
|
|
if (!password_verify($name . $tripkey, $tripcode)) {
|
|
// add logging to prevent password spraying attacks
|
|
quit($db, "<h1>Name#tripkey combination is not valid.</h1>");
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if (empty($tripcode)) {
|
|
$tripcode = password_hash($name . $tripkey, PASSWORD_DEFAULT);
|
|
}
|
|
|
|
return $tripcode;
|
|
}
|
|
|
|
// checks if content has been posted before, according the config file.
|
|
function check_original_content($db, $settings, $sub, $text_id, $org_id)
|
|
{
|
|
$statement = $db->prepare("SELECT post_id, sub, org_id
|
|
FROM threads
|
|
WHERE text_id = '$text_id'");
|
|
$result = $statement->execute();
|
|
|
|
while ($row = $result->fetchArray(SQLITE3_NUM)) {
|
|
$result_post_id = "{$row[0]}";
|
|
$result_sub = "{$row[1]}";
|
|
$result_org_id = "{$row[2]}";
|
|
if ( ($settings['original_content_global'] == TRUE) ){
|
|
return FALSE;
|
|
} elseif ( ($settings['original_content_sub'] == TRUE)
|
|
&& ($sub == $result_sub) ){
|
|
return FALSE;
|
|
} elseif ( ($settings['original_content_thread'] == TRUE)
|
|
&& ($sub == $result_sub)
|
|
&& ($org_id == $result_org_id) ){
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// Check if a message actually exists in the database when replying.
|
|
// Note that if the links of the site are used for navigation, this is
|
|
// always the case (unless it was deleted meanwhile).
|
|
// This routine is mostly to prevent malicious users from creating
|
|
// ghost messages that are in the db but are never displayed
|
|
// (because the threadstart is missing).
|
|
function check_org_id_exists($db, $sub, $org_id)
|
|
{
|
|
|
|
$statement = $db->prepare("SELECT post_id
|
|
FROM threads
|
|
WHERE sub = '$sub'
|
|
AND shadow = 'no'
|
|
AND org_id = '$org_id'");
|
|
$result = $statement->execute();
|
|
|
|
$counter = 0;
|
|
|
|
while ($row = $result->fetchArray(SQLITE3_NUM)) {
|
|
$counter++;
|
|
break;
|
|
}
|
|
|
|
if ( ($counter < 1) ) {
|
|
// if the counter is smaller 1, there is no match
|
|
return FALSE;
|
|
} else {
|
|
return TRUE;
|
|
}
|
|
|
|
}
|
|
|
|
// A simple check if the post is spam or not, also if it is too long
|
|
// or too short.
|
|
// These are a few of the only original lines of code left from smolBBS.
|
|
function check_spam($db, $text, $settings)
|
|
{
|
|
|
|
if (preg_match('/^(.)\1*$/u ', $text)) {
|
|
quit($db, '<h1>Spam detected!</h1>');
|
|
}
|
|
|
|
$post_length = strlen($text);
|
|
|
|
if ($post_length < $settings['min_char']) {
|
|
quit($db, '<h1>Post too short!</h1>');
|
|
}
|
|
|
|
if ($post_length > $settings['max_char']) {
|
|
quit($db, '<h1>Post too long!</h1>');
|
|
}
|
|
|
|
$text = str_replace(array("\n","\r"), '', $text);
|
|
|
|
if (substr_count($text, ' ') === strlen($text)) {
|
|
quit($db, '<h1>Spam detected! Post contained only spaces!</h1>');
|
|
}
|
|
//rewrite, does not work for all cases
|
|
if (ctype_space($text)) {
|
|
$quit_message = '<h1>Spam detected! Post contained only spaces '
|
|
. '(yeah, Unicode...)!</h1>';
|
|
quit($db, $quit_message);
|
|
}
|
|
|
|
}
|
|
|
|
// Select a new post_id for a new post. It is one higher than the
|
|
// previous existing highest number.
|
|
// This means in theory that if a message is deleted the number could
|
|
// be assigned to a different one (if it was the latest message that
|
|
// was deleted).
|
|
// This behavior can be prevented when working with a moderators account.
|
|
// Note that in contrast to previous versions, replies are inside
|
|
// the same numbering system.
|
|
function get_new_post_id($db, $sub)
|
|
{
|
|
|
|
$largest = 0;
|
|
|
|
$statement = $db->prepare("SELECT post_id
|
|
FROM threads
|
|
WHERE sub = '$sub'
|
|
ORDER BY post_id DESC
|
|
LIMIT 1");
|
|
// we just want the highest element
|
|
$result = $statement->execute();
|
|
|
|
while ($row = $result->fetchArray(SQLITE3_NUM)) {
|
|
$largest = "{$row[0]}";
|
|
}
|
|
|
|
$largest++;
|
|
// we increase the largest number by one to get the new post_id
|
|
|
|
return $largest;
|
|
}
|
|
|
|
// Get the token from the post stream.
|
|
function get_post_token()
|
|
{
|
|
|
|
$token = '';
|
|
|
|
if ( (!empty($_POST['token'])) ) {
|
|
$token = filter($_POST['token'], 'alnum', 250);
|
|
// length of token is 250 characters
|
|
}
|
|
|
|
return $token;
|
|
}
|
|
|
|
// Make an edit to an existing post
|
|
function make_edit($db, $sub, $post_id, $ip, $settings)
|
|
{
|
|
|
|
if ( ( $settings['enable_tripcodes'] != TRUE ) ) {
|
|
quit($db, "<h1>Tripcodes need to be enabled for this to work.</h1>");
|
|
}
|
|
|
|
if ( (!check_post_exists($db, $sub, $post_id)) ) {
|
|
quit($db, "<h1>Post $post_id on sub $sub does not exist.</h1>");
|
|
}
|
|
|
|
if ( ( $settings['enable_timestamps'] == TRUE ) &&
|
|
( (!empty($_POST['edit_timestamp'])) ) ) {
|
|
$timestamp = make_timestamp($settings);
|
|
} else {
|
|
$timestamp = '';
|
|
}
|
|
|
|
if ( (!empty($_POST['edit_combination'])) ) {
|
|
$parts = explode('#', $_POST['edit_combination']);
|
|
|
|
if ( (empty($parts[0])) || (empty($parts[1])) ) {
|
|
quit($db, "<h1>Name#tripkey not found.</h1>");
|
|
}
|
|
|
|
$name = filter($parts[0], 'email', 20);
|
|
$tripkey = filter($parts[1], 'alnum', 50);
|
|
} else {
|
|
quit($db, "<h1>Name#tripkey are needed for this.</h1>");
|
|
}
|
|
|
|
$statement = $db->prepare("SELECT tripcode, org_id, original,
|
|
move_message
|
|
FROM threads
|
|
WHERE sub = '$sub'
|
|
AND shadow = 'no'
|
|
AND post_id = '$post_id'
|
|
ORDER BY ROWID DESC");
|
|
$result = $statement->execute();
|
|
|
|
while ($row = $result->fetchArray(SQLITE3_NUM)) {
|
|
$tripcode_post = "{$row[0]}";
|
|
$org_id = "{$row[1]}";
|
|
$original = "{$row[2]}";
|
|
$move_message = "{$row[3]}";
|
|
}
|
|
|
|
if (!password_verify($name . $tripkey, $tripcode_post)) {
|
|
$auth_message = 'name#tripkey combination not valid';
|
|
log_event($db, $settings, 'auth', $auth_message, $ip);
|
|
sleep(10);
|
|
quit($db, "<h1>Name#tripkey combination is not valid.</h1>");
|
|
}
|
|
|
|
$statement = $db->prepare("UPDATE threads
|
|
SET shadow = 'yes'
|
|
WHERE post_id = '$post_id'
|
|
AND sub = '$sub'");
|
|
$result = $statement->execute();
|
|
|
|
$new_post_id = get_new_post_id($db, $sub);
|
|
|
|
$text = strip_tags($_POST['edit_text']);
|
|
$text_id = hash('sha512', $text);
|
|
$global_id = hash('sha512', $sub . $new_post_id . $org_id . $text);
|
|
$edit_message = 'edited by user, click "edit" to see the history';
|
|
|
|
$statement = $db->prepare("INSERT INTO threads(post_id, sub, text,
|
|
org_id, shadow,
|
|
global_id, text_id,
|
|
timestamp, name,
|
|
tripcode, original,
|
|
move_message,
|
|
edit_message)
|
|
VALUES ('$new_post_id', '$sub', ?, '$org_id',
|
|
'no', '$global_id', '$text_id',
|
|
'$timestamp', '$name',
|
|
'$tripcode_post',
|
|
'$original', ?, ?)");
|
|
$statement->bindParam(1, $text);
|
|
$statement->bindParam(2, $move_message);
|
|
$statement->bindParam(3, $edit_message);
|
|
$statement->execute();
|
|
|
|
return $post_id;
|
|
}
|
|
|
|
// Make a timestamp, precision is set in the config file
|
|
function make_timestamp($settings){
|
|
|
|
$month = gmdate("F");
|
|
$year = gmdate("Y");
|
|
|
|
if ( $settings['precision_timestamps'] == 'middle' ) {
|
|
$timestamp = $month . '/' . $year;
|
|
} elseif ( $settings['precision_timestamps'] == 'high' ) {
|
|
$timestamp = gmdate("Y-m-d");
|
|
} elseif ( $settings['precision_timestamps'] == 'insane' ) {
|
|
$timestamp = gmdate("Y-m-d H:i:s");
|
|
} else {
|
|
$quarter_1 = array('January', 'February', 'March');
|
|
$quarter_2 = array('April', 'May', 'June');
|
|
$quarter_3 = array('July', 'August', 'September');
|
|
$quarter_4 = array('October', 'November', 'December');
|
|
|
|
if ( in_array($month, $quarter_1 ) ){
|
|
$timestamp = 'Q1';
|
|
} elseif ( in_array($month, $quarter_2 ) ){
|
|
$timestamp = 'Q2';
|
|
} elseif ( in_array($month, $quarter_3 ) ){
|
|
$timestamp = 'Q3';
|
|
} elseif ( in_array($month, $quarter_4 ) ){
|
|
$timestamp = 'Q4';
|
|
}
|
|
|
|
$timestamp .= '/' . $year;
|
|
}
|
|
|
|
return $timestamp;
|
|
}
|
|
|
|
// Make a new post to a sub
|
|
function make_post($db, $sub, $settings, $text, $org_id)
|
|
{
|
|
|
|
$post_id = get_new_post_id($db, $sub);
|
|
|
|
if ($org_id == '') {
|
|
$org_id = $post_id;
|
|
} elseif ( (!check_org_id_exists($db, $sub, $org_id)) ) {
|
|
quit($db, "<h1>Post $org_id on sub $sub does not exist.</h1>");
|
|
}
|
|
|
|
$global_id = hash('sha512', $sub . $post_id . $org_id . $text);
|
|
$text_id = hash('sha512', $text);
|
|
|
|
if ( ( $settings['enable_timestamps'] == TRUE ) &&
|
|
( (!empty($_POST['timestamp'])) ) ) {
|
|
$timestamp = make_timestamp($settings);
|
|
} else {
|
|
$timestamp = '';
|
|
}
|
|
|
|
if ( ( $settings['enable_tripcodes'] == TRUE ) &&
|
|
(!empty($_POST['combination'])) ) {
|
|
$parts = explode('#', $_POST['combination']);
|
|
$name = filter($parts[0], 'email', 20);
|
|
$tripkey = filter($parts[1], 'alnum', 50);
|
|
$tripcode = check_name($db, $name, $tripkey);
|
|
} elseif ( ( $settings['enable_tripcodes'] == TRUE ) &&
|
|
(!empty($_POST['mod'])) &&
|
|
(!empty($_POST['pass'])) ) {
|
|
$name = filter($_POST['mod'], 'email', 20);
|
|
$tripkey = filter($_POST['pass'], 'alnum', 50);
|
|
$tripcode = check_name($db, $name, $tripkey);
|
|
} else {
|
|
$name = '';
|
|
$tripcode = '';
|
|
}
|
|
|
|
$statement = $db->prepare("INSERT INTO threads(post_id, sub, text,
|
|
org_id, shadow,
|
|
global_id, text_id,
|
|
timestamp, name,
|
|
tripcode, original)
|
|
VALUES ('$post_id', '$sub', ?, '$org_id',
|
|
'no', '$global_id', '$text_id',
|
|
'$timestamp', '$name', '$tripcode',
|
|
'$post_id')");
|
|
$statement->bindParam(1, $text);
|
|
$statement->execute();
|
|
|
|
if ( ($org_id != $post_id) &&
|
|
($settings['enable_bumping'] == TRUE) &&
|
|
(!isset($_POST['sage'])) ){
|
|
bump_message($db, $org_id, $sub, $settings);
|
|
}
|
|
|
|
return $post_id;
|
|
}
|
|
|
|
function make_tripcode($settings)
|
|
{
|
|
$tripkey = make_token(25, 'alnum');
|
|
$differ = make_token(6, 'alnum');
|
|
$name = $settings['prefix_autogen'] . $differ;
|
|
$combination = $name . '#' . $tripkey;
|
|
|
|
return $combination;
|
|
}
|
|
|
|
// checks if posts from users can be received or not
|
|
function post_block_user($db, $settings, $visitor_ip)
|
|
{
|
|
|
|
$current = time();
|
|
$max_age = $current - ($settings['max_post_timeframe'] * 60);
|
|
// the number from settings is in minutes, so times 60 for secs
|
|
|
|
if ( ($settings['max_post_global'] > 0) ) {
|
|
$statement = $db->prepare("SELECT unix_timestamp
|
|
FROM logs
|
|
WHERE type in ('bot', 'user')
|
|
AND event = 'post attempt'
|
|
AND unix_timestamp > '$max_age'");
|
|
|
|
$result = $statement->execute();
|
|
|
|
$counter = 0;
|
|
|
|
while ($row = $result->fetchArray(SQLITE3_NUM)) {
|
|
$counter++;
|
|
}
|
|
|
|
if ( ($counter > $settings['max_post_global']) ) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if ( ($settings['max_post_ip'] > 0) ) {
|
|
$statement = $db->prepare("SELECT unix_timestamp
|
|
FROM logs
|
|
WHERE type in ('bot', 'user')
|
|
AND event = 'post attempt'
|
|
AND ip = '$visitor_ip'
|
|
AND unix_timestamp > '$max_age'");
|
|
|
|
$result = $statement->execute();
|
|
|
|
$counter = 0;
|
|
|
|
while ($row = $result->fetchArray(SQLITE3_NUM)) {
|
|
$counter++;
|
|
}
|
|
|
|
if ( ($counter > $settings['max_post_ip']) ) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// register a new sub in the table "subs"
|
|
function register_sub($db, $name, $sub, $motto, $css, $botkey)
|
|
{
|
|
$statement = $db->prepare("INSERT INTO subs(name, type, moderator,
|
|
botkey, css, motto)
|
|
VALUES ('$sub', 'public', '$name',
|
|
'$botkey', '$css', ?)");
|
|
$statement->bindParam(1, $motto);
|
|
$statement->execute();
|
|
}
|
|
|
|
// EOF
|