2010-11-02 12:54:50 +01:00
< ? php
2012-04-11 18:49:22 +02:00
/*
2014-06-30 09:50:20 +02:00
* Copyright ( c ) 2010 - 2014 Tinyboard Development Group
2012-04-11 18:49:22 +02:00
*/
2020-01-20 03:01:23 +01:00
2012-04-12 16:18:19 +02:00
if ( realpath ( $_SERVER [ 'SCRIPT_FILENAME' ]) == str_replace ( '\\' , '/' , __FILE__ )) {
2012-04-11 18:49:22 +02:00
// You cannot request this file directly.
exit ;
}
2013-08-29 00:25:15 +02:00
$microtime_start = microtime ( true );
2012-04-11 18:49:22 +02:00
// the user is not currently logged in as a moderator
$mod = false ;
register_shutdown_function ( 'fatal_error_handler' );
mb_internal_encoding ( 'UTF-8' );
loadConfig ();
2014-02-23 18:49:04 +01:00
function init_locale ( $locale , $error = 'error' ) {
if ( extension_loaded ( 'gettext' )) {
2024-04-30 12:46:44 +02:00
setlocale ( LC_ALL , $locale );
2014-02-23 18:49:04 +01:00
bindtextdomain ( 'tinyboard' , './inc/locale' );
bind_textdomain_codeset ( 'tinyboard' , 'UTF-8' );
textdomain ( 'tinyboard' );
} else {
2015-04-05 16:26:32 +02:00
if ( _setlocale ( LC_ALL , $locale ) === false ) {
$error ( 'The specified locale (' . $locale . ') does not exist on your platform!' );
}
2014-02-23 18:49:04 +01:00
_bindtextdomain ( 'tinyboard' , './inc/locale' );
_bind_textdomain_codeset ( 'tinyboard' , 'UTF-8' );
_textdomain ( 'tinyboard' );
}
}
$current_locale = 'en' ;
2012-04-11 18:49:22 +02:00
function loadConfig () {
2015-04-05 18:48:53 +02:00
global $board , $config , $__ip , $debug , $__version , $microtime_start , $current_locale , $events ;
2013-08-03 13:50:25 +02:00
2012-04-11 18:49:22 +02:00
$error = function_exists ( 'error' ) ? 'error' : 'basic_error_function_because_the_other_isnt_loaded_yet' ;
2013-08-03 13:50:25 +02:00
2015-04-05 18:48:53 +02:00
$boardsuffix = isset ( $board [ 'uri' ]) ? $board [ 'uri' ] : '' ;
2013-08-03 13:50:25 +02:00
2024-02-04 14:53:06 +01:00
if ( ! isset ( $_SERVER [ 'REMOTE_ADDR' ]))
$_SERVER [ 'REMOTE_ADDR' ] = '0.0.0.0' ;
2013-08-03 13:50:25 +02:00
2015-04-05 18:48:53 +02:00
if ( file_exists ( 'tmp/cache/cache_config.php' )) {
require_once ( 'tmp/cache/cache_config.php' );
}
2024-03-14 10:01:13 +01:00
if ( isset ( $config [ 'cache_config' ]) &&
2024-04-30 12:46:44 +02:00
$config [ 'cache_config' ] &&
$config = Cache :: get ( 'config_' . $boardsuffix ))
{
2015-04-05 18:48:53 +02:00
$events = Cache :: get ( 'events_' . $boardsuffix );
2015-04-05 20:25:57 +02:00
define_groups ();
2015-04-05 18:48:53 +02:00
if ( file_exists ( 'inc/instance-functions.php' )) {
require_once ( 'inc/instance-functions.php' );
}
if ( $config [ 'locale' ] != $current_locale ) {
2024-04-30 12:46:44 +02:00
$current_locale = $config [ 'locale' ];
init_locale ( $config [ 'locale' ], $error );
}
} else {
2015-04-05 18:48:53 +02:00
$config = array ();
2013-08-03 13:50:25 +02:00
2024-03-14 10:01:13 +01:00
reset_events ();
2015-04-05 19:12:41 +02:00
$arrays = array (
'db' ,
'api' ,
'cache' ,
2016-05-08 13:20:00 +02:00
'lock' ,
'queue' ,
2015-04-05 19:12:41 +02:00
'cookies' ,
'error' ,
'dir' ,
'mod' ,
'spam' ,
'filters' ,
'wordfilters' ,
'custom_capcode' ,
'custom_tripcode' ,
'dnsbl' ,
'dnsbl_exceptions' ,
'remote' ,
'allowed_ext' ,
'allowed_ext_files' ,
'file_icons' ,
'footer' ,
'stylesheets' ,
'additional_javascript' ,
'markup' ,
'custom_pages' ,
'dashboard_links'
);
foreach ( $arrays as $key ) {
$config [ $key ] = array ();
}
2014-02-23 18:49:04 +01:00
2024-02-04 14:53:06 +01:00
if ( ! file_exists ( 'inc/instance-config.php' ))
2022-09-27 14:56:29 +02:00
$error ( 'vichan is not configured! Create inc/instance-config.php.' );
2014-02-23 18:49:04 +01:00
2015-04-05 19:12:41 +02:00
// Initialize locale as early as possible
2015-04-05 16:52:35 +02:00
2015-04-05 19:12:41 +02:00
// Those calls are expensive. Unfortunately, our cache system is not initialized at this point.
// So, we may store the locale in a tmp/ filesystem.
2014-02-23 18:49:04 +01:00
2015-04-05 19:12:41 +02:00
if ( file_exists ( $fn = 'tmp/cache/locale_' . $boardsuffix ) ) {
2016-05-05 08:43:34 +02:00
$config [ 'locale' ] = @ file_get_contents ( $fn );
2014-04-27 15:48:47 +02:00
}
2015-04-05 19:12:41 +02:00
else {
$config [ 'locale' ] = 'en' ;
2022-09-27 14:50:46 +02:00
$configstr = file_get_contents ( 'inc/secrets.php' );
2015-04-05 19:12:41 +02:00
if ( isset ( $board [ 'dir' ]) && file_exists ( $board [ 'dir' ] . '/config.php' )) {
$configstr .= file_get_contents ( $board [ 'dir' ] . '/config.php' );
}
$matches = array ();
2016-05-05 08:40:13 +02:00
preg_match_all ( '/[^\/#*]\$config\s*\[\s*[\'"]locale[\'"]\s*\]\s*=\s*([\'"])(.*?)\1/' , $configstr , $matches );
2015-04-05 19:12:41 +02:00
if ( $matches && isset ( $matches [ 2 ]) && $matches [ 2 ]) {
$matches = $matches [ 2 ];
$config [ 'locale' ] = $matches [ count ( $matches ) - 1 ];
}
2016-05-05 08:43:34 +02:00
@ file_put_contents ( $fn , $config [ 'locale' ]);
2015-04-05 16:52:35 +02:00
}
2015-04-05 19:12:41 +02:00
if ( $config [ 'locale' ] != $current_locale ) {
$current_locale = $config [ 'locale' ];
init_locale ( $config [ 'locale' ], $error );
}
2014-02-23 18:49:04 +01:00
2015-04-05 19:12:41 +02:00
require 'inc/config.php' ;
2013-08-03 13:50:25 +02:00
2015-04-05 19:12:41 +02:00
require 'inc/instance-config.php' ;
2013-08-03 13:50:25 +02:00
2015-04-05 19:12:41 +02:00
if ( isset ( $board [ 'dir' ]) && file_exists ( $board [ 'dir' ] . '/config.php' )) {
require $board [ 'dir' ] . '/config.php' ;
}
2014-02-23 18:49:04 +01:00
2015-04-05 19:12:41 +02:00
if ( $config [ 'locale' ] != $current_locale ) {
$current_locale = $config [ 'locale' ];
init_locale ( $config [ 'locale' ], $error );
2012-04-11 18:49:22 +02:00
}
2015-04-05 18:48:53 +02:00
2024-02-04 14:53:06 +01:00
if ( ! isset ( $config [ 'global_message' ]))
$config [ 'global_message' ] = false ;
if ( ! isset ( $config [ 'post_url' ]))
$config [ 'post_url' ] = $config [ 'root' ] . $config [ 'file_post' ];
2015-04-05 19:12:41 +02:00
2024-02-04 14:53:06 +01:00
if ( ! isset ( $config [ 'referer_match' ]))
2015-04-05 19:12:41 +02:00
if ( isset ( $_SERVER [ 'HTTP_HOST' ])) {
$config [ 'referer_match' ] = '/^' .
( preg_match ( '@^https?://@' , $config [ 'root' ]) ? '' :
'https?:\/\/' . $_SERVER [ 'HTTP_HOST' ]) .
preg_quote ( $config [ 'root' ], '/' ) .
'(' .
str_replace ( '%s' , $config [ 'board_regex' ], preg_quote ( $config [ 'board_path' ], '/' )) .
'(' .
preg_quote ( $config [ 'file_index' ], '/' ) . '|' .
str_replace ( '%d' , '\d+' , preg_quote ( $config [ 'file_page' ])) .
')?' .
'|' .
str_replace ( '%s' , $config [ 'board_regex' ], preg_quote ( $config [ 'board_path' ], '/' )) .
preg_quote ( $config [ 'dir' ][ 'res' ], '/' ) .
'(' .
str_replace ( '%d' , '\d+' , preg_quote ( $config [ 'file_page' ], '/' )) . '|' .
str_replace ( '%d' , '\d+' , preg_quote ( $config [ 'file_page50' ], '/' )) . '|' .
2024-04-30 12:46:44 +02:00
str_replace ( array ( '%d' , '%s' ), array ( '\d+' , '[a-z0-9-]+' ), preg_quote ( $config [ 'file_page_slug' ], '/' )) . '|' .
str_replace ( array ( '%d' , '%s' ), array ( '\d+' , '[a-z0-9-]+' ), preg_quote ( $config [ 'file_page50_slug' ], '/' )) .
2015-04-05 19:12:41 +02:00
')' .
'|' .
preg_quote ( $config [ 'file_mod' ], '/' ) . '\?\/.+' .
')([#?](.+)?)?$/ui' ;
} else {
// CLI mode
$config [ 'referer_match' ] = '//' ;
}
2024-02-04 14:53:06 +01:00
if ( ! isset ( $config [ 'cookies' ][ 'path' ]))
2015-04-05 19:12:41 +02:00
$config [ 'cookies' ][ 'path' ] = & $config [ 'root' ];
2024-02-04 14:53:06 +01:00
if ( ! isset ( $config [ 'dir' ][ 'static' ]))
2015-04-05 19:12:41 +02:00
$config [ 'dir' ][ 'static' ] = $config [ 'root' ] . 'static/' ;
2024-02-04 14:53:06 +01:00
if ( ! isset ( $config [ 'image_blank' ]))
$config [ 'image_blank' ] = $config [ 'dir' ][ 'static' ] . 'blank.gif' ;
if ( ! isset ( $config [ 'image_sticky' ]))
$config [ 'image_sticky' ] = $config [ 'dir' ][ 'static' ] . 'sticky.gif' ;
if ( ! isset ( $config [ 'image_locked' ]))
$config [ 'image_locked' ] = $config [ 'dir' ][ 'static' ] . 'locked.gif' ;
if ( ! isset ( $config [ 'image_bumplocked' ]))
$config [ 'image_bumplocked' ] = $config [ 'dir' ][ 'static' ] . 'sage.gif' ;
if ( ! isset ( $config [ 'image_deleted' ]))
$config [ 'image_deleted' ] = $config [ 'dir' ][ 'static' ] . 'deleted.png' ;
if ( ! isset ( $config [ 'image_cyclical' ]))
$config [ 'image_cyclical' ] = $config [ 'dir' ][ 'static' ] . 'cycle.png' ;
2015-04-05 19:12:41 +02:00
2020-01-19 07:06:01 +01:00
if ( isset ( $board )) {
2024-02-04 14:53:06 +01:00
if ( ! isset ( $config [ 'uri_thumb' ]))
2020-01-19 07:06:01 +01:00
$config [ 'uri_thumb' ] = $config [ 'root' ] . $board [ 'dir' ] . $config [ 'dir' ][ 'thumb' ];
2024-02-04 14:53:06 +01:00
elseif ( isset ( $board [ 'dir' ]))
2020-01-19 07:06:01 +01:00
$config [ 'uri_thumb' ] = sprintf ( $config [ 'uri_thumb' ], $board [ 'dir' ]);
2024-02-04 14:53:06 +01:00
if ( ! isset ( $config [ 'uri_img' ]))
2020-01-19 07:06:01 +01:00
$config [ 'uri_img' ] = $config [ 'root' ] . $board [ 'dir' ] . $config [ 'dir' ][ 'img' ];
2024-02-04 14:53:06 +01:00
elseif ( isset ( $board [ 'dir' ]))
2020-01-19 07:06:01 +01:00
$config [ 'uri_img' ] = sprintf ( $config [ 'uri_img' ], $board [ 'dir' ]);
}
2015-04-05 19:12:41 +02:00
2024-02-04 14:53:06 +01:00
if ( ! isset ( $config [ 'uri_stylesheets' ]))
$config [ 'uri_stylesheets' ] = $config [ 'root' ] . 'stylesheets/' ;
if ( ! isset ( $config [ 'url_stylesheet' ]))
$config [ 'url_stylesheet' ] = $config [ 'uri_stylesheets' ] . 'style.css' ;
if ( ! isset ( $config [ 'url_javascript' ]))
$config [ 'url_javascript' ] = $config [ 'root' ] . $config [ 'file_script' ];
if ( ! isset ( $config [ 'additional_javascript_url' ]))
$config [ 'additional_javascript_url' ] = $config [ 'root' ];
if ( ! isset ( $config [ 'uri_flags' ]))
$config [ 'uri_flags' ] = $config [ 'root' ] . 'static/flags/%s.png' ;
if ( ! isset ( $config [ 'user_flag' ]))
$config [ 'user_flag' ] = false ;
if ( ! isset ( $config [ 'user_flags' ]))
$config [ 'user_flags' ] = array ();
2015-04-05 19:12:41 +02:00
2024-02-04 14:53:06 +01:00
if ( ! isset ( $__version ))
2015-04-05 19:12:41 +02:00
$__version = file_exists ( '.installed' ) ? trim ( file_get_contents ( '.installed' )) : false ;
$config [ 'version' ] = $__version ;
2024-04-30 12:46:44 +02:00
if ( $config [ 'allow_roll' ]) {
2015-04-05 19:12:41 +02:00
event_handler ( 'post' , 'diceRoller' );
2024-04-30 12:46:44 +02:00
}
2015-04-05 19:12:41 +02:00
2024-04-30 12:46:44 +02:00
if ( in_array ( 'webm' , $config [ 'allowed_ext_files' ]) || in_array ( 'mp4' , $config [ 'allowed_ext_files' ])) {
2015-08-11 04:51:27 +02:00
event_handler ( 'post' , 'postHandler' );
2024-04-30 12:46:44 +02:00
}
2015-04-05 18:48:53 +02:00
}
2024-02-04 14:53:06 +01:00
// Effectful config processing below:
2015-02-27 21:16:03 +01:00
2015-04-05 18:48:53 +02:00
date_default_timezone_set ( $config [ 'timezone' ]);
2012-04-12 16:18:19 +02:00
if ( $config [ 'root_file' ]) {
2012-04-11 18:49:22 +02:00
chdir ( $config [ 'root_file' ]);
2011-11-18 13:39:13 +01:00
}
2012-04-10 17:18:38 +02:00
2015-02-27 21:16:03 +01:00
// Keep the original address to properly comply with other board configurations
2024-02-04 14:53:06 +01:00
if ( ! isset ( $__ip ))
2015-02-27 21:16:03 +01:00
$__ip = $_SERVER [ 'REMOTE_ADDR' ];
// ::ffff:0.0.0.0
2024-02-04 14:53:06 +01:00
if ( preg_match ( '/^\:\:(ffff\:)?(\d+\.\d+\.\d+\.\d+)$/' , $__ip , $m ))
2015-02-27 21:16:03 +01:00
$_SERVER [ 'REMOTE_ADDR' ] = $m [ 2 ];
2012-04-12 16:18:19 +02:00
if ( $config [ 'verbose_errors' ]) {
2013-08-03 08:01:52 +02:00
set_error_handler ( 'verbose_error_handler' );
2020-01-20 03:01:23 +01:00
error_reporting ( $config [ 'deprecation_errors' ] ? E_ALL : E_ALL & ~ E_DEPRECATED );
2013-08-03 11:21:02 +02:00
ini_set ( 'display_errors' , true );
2013-08-03 06:22:28 +02:00
ini_set ( 'html_errors' , false );
2014-03-31 21:12:04 +02:00
} else {
ini_set ( 'display_errors' , false );
2012-04-10 17:18:38 +02:00
}
2013-08-03 06:22:28 +02:00
2024-02-04 14:53:06 +01:00
if ( $config [ 'syslog' ])
openlog ( 'tinyboard' , LOG_ODELAY , LOG_SYSLOG ); // open a connection to sysem logger
2013-08-03 13:50:25 +02:00
2024-02-04 14:53:06 +01:00
if ( $config [ 'cache' ][ 'enabled' ])
2012-04-11 18:49:22 +02:00
require_once 'inc/cache.php' ;
2014-04-06 21:54:22 +02:00
2024-04-30 12:46:44 +02:00
if ( in_array ( 'webm' , $config [ 'allowed_ext_files' ]) || in_array ( 'mp4' , $config [ 'allowed_ext_files' ]))
2014-04-06 21:54:22 +02:00
require_once 'inc/lib/webm/posthandler.php' ;
2015-04-05 18:48:53 +02:00
event ( 'load-config' );
2014-07-03 02:55:33 +02:00
2015-04-05 18:48:53 +02:00
if ( $config [ 'cache_config' ] && ! isset ( $config [ 'cache_config_loaded' ])) {
file_put_contents ( 'tmp/cache/cache_config.php' , '<?php ' .
'$config = array();' .
2015-04-05 20:04:27 +02:00
'$config[\'cache\'] = ' . var_export ( $config [ 'cache' ], true ) . ';' .
2015-04-05 18:48:53 +02:00
'$config[\'cache_config\'] = true;' .
2015-04-05 20:04:27 +02:00
'$config[\'debug\'] = ' . var_export ( $config [ 'debug' ], true ) . ';' .
2015-04-05 18:48:53 +02:00
'require_once(\'inc/cache.php\');'
);
2014-08-10 16:51:45 +02:00
2015-04-05 18:48:53 +02:00
$config [ 'cache_config_loaded' ] = true ;
Cache :: set ( 'config_' . $boardsuffix , $config );
Cache :: set ( 'events_' . $boardsuffix , $events );
}
2015-08-11 04:51:27 +02:00
2024-02-04 14:53:06 +01:00
if ( is_array ( $config [ 'anonymous' ]))
2015-08-11 04:51:27 +02:00
$config [ 'anonymous' ] = $config [ 'anonymous' ][ array_rand ( $config [ 'anonymous' ])];
2024-03-14 10:01:13 +01:00
2013-08-30 00:39:11 +02:00
if ( $config [ 'debug' ]) {
if ( ! isset ( $debug )) {
$debug = array (
'sql' => array (),
'exec' => array (),
'purge' => array (),
'cached' => array (),
'write' => array (),
'time' => array (
'db_queries' => 0 ,
'exec' => 0 ,
),
'start' => $microtime_start ,
'start_debug' => microtime ( true )
);
$debug [ 'start' ] = $microtime_start ;
}
}
2012-04-11 18:49:22 +02:00
}
2024-02-04 14:53:06 +01:00
function basic_error_function_because_the_other_isnt_loaded_yet ( $message , $priority = true ) {
global $config ;
if ( $config [ 'syslog' ] && $priority !== false ) {
// Use LOG_NOTICE instead of LOG_ERR or LOG_WARNING because most error message are not significant.
_syslog ( $priority !== true ? $priority : LOG_NOTICE , $message );
}
// Yes, this is horrible.
die ( '<!DOCTYPE html><html><head><title>Error</title>' .
'<style type="text/css">' .
'body{text-align:center;font-family:arial, helvetica, sans-serif;font-size:10pt;}' .
'p{padding:0;margin:20px 0;}' .
'p.c{font-size:11px;}' .
'</style></head>' .
'<body><h2>Error</h2>' . $message . '<hr/>' .
'<p class="c">This alternative error page is being displayed because the other couldn\'t be found or hasn\'t loaded yet.</p></body></html>' );
}
2024-03-14 10:01:13 +01:00
function fatal_error_handler () {
2024-02-04 14:53:06 +01:00
if ( $error = error_get_last ()) {
if ( $error [ 'type' ] == E_ERROR ) {
if ( function_exists ( 'error' )) {
error ( 'Caught fatal error: ' . $error [ 'message' ] . ' in <strong>' . $error [ 'file' ] . '</strong> on line ' . $error [ 'line' ], LOG_ERR );
} else {
basic_error_function_because_the_other_isnt_loaded_yet ( 'Caught fatal error: ' . $error [ 'message' ] . ' in ' . $error [ 'file' ] . ' on line ' . $error [ 'line' ], LOG_ERR );
}
}
}
}
function _syslog ( $priority , $message ) {
if ( isset ( $_SERVER [ 'REMOTE_ADDR' ], $_SERVER [ 'REQUEST_METHOD' ], $_SERVER [ 'REQUEST_URI' ])) {
// CGI
syslog ( $priority , $message . ' - client: ' . $_SERVER [ 'REMOTE_ADDR' ] . ', request: "' . $_SERVER [ 'REQUEST_METHOD' ] . ' ' . $_SERVER [ 'REQUEST_URI' ] . '"' );
} else {
syslog ( $priority , $message );
}
}
function verbose_error_handler ( $errno , $errstr , $errfile , $errline ) {
global $config ;
if ( error_reporting () == 0 )
return false ; // Looks like this warning was suppressed by the @ operator.
if ( $errno == E_DEPRECATED && ! $config [ 'deprecation_errors' ])
return false ;
error ( utf8tohtml ( $errstr ), true , array (
'file' => $errfile . ':' . $errline ,
'errno' => $errno ,
'error' => $errstr ,
'backtrace' => array_slice ( debug_backtrace (), 1 )
));
}
2013-09-08 17:33:51 +02:00
function define_groups () {
global $config ;
2015-04-05 12:44:58 +02:00
foreach ( $config [ 'mod' ][ 'groups' ] as $group_value => $group_name ) {
$group_name = strtoupper ( $group_name );
if ( ! defined ( $group_name )) {
2020-10-31 02:56:49 +01:00
define ( $group_name , $group_value );
2015-04-05 12:44:58 +02:00
}
}
2024-03-14 10:01:13 +01:00
2013-09-08 17:33:51 +02:00
ksort ( $config [ 'mod' ][ 'groups' ]);
}
2012-04-12 13:56:01 +02:00
function create_antibot ( $board , $thread = null ) {
2012-04-17 00:16:36 +02:00
require_once dirname ( __FILE__ ) . '/anti-bot.php' ;
2013-08-03 13:50:25 +02:00
2024-02-04 14:53:06 +01:00
return _create_antibot ( $board , $thread );
2012-04-12 13:56:01 +02:00
}
2012-04-11 18:49:22 +02:00
2014-04-21 15:38:18 +02:00
function rebuildThemes ( $action , $boardname = false ) {
2014-10-09 04:09:30 +02:00
global $config , $board , $current_locale ;
2014-04-21 15:38:18 +02:00
// Save the global variables
$_config = $config ;
$_board = $board ;
2012-04-11 18:49:22 +02:00
// List themes
2015-04-05 16:38:16 +02:00
if ( $themes = Cache :: get ( " themes " )) {
// OK, we already have themes loaded
}
else {
$query = query ( " SELECT `theme` FROM ``theme_settings`` WHERE `name` IS NULL AND `value` IS NULL " ) or error ( db_error ());
$themes = array ();
2012-08-30 17:35:27 +02:00
2015-04-05 16:38:16 +02:00
while ( $theme = $query -> fetch ( PDO :: FETCH_ASSOC )) {
$themes [] = $theme ;
}
Cache :: set ( " themes " , $themes );
}
foreach ( $themes as $theme ) {
2014-10-09 03:57:18 +02:00
// Restore them
$config = $_config ;
2014-10-09 04:09:30 +02:00
$board = $_board ;
2024-03-14 10:01:13 +01:00
// Reload the locale
2024-03-11 10:31:19 +01:00
if ( $config [ 'locale' ] != $current_locale ) {
$current_locale = $config [ 'locale' ];
init_locale ( $config [ 'locale' ]);
}
2014-10-09 04:09:30 +02:00
2015-02-26 21:44:39 +01:00
if ( PHP_SAPI === 'cli' ) {
echo " Rebuilding theme " . $theme [ 'theme' ] . " ... " ;
}
2014-10-09 03:57:18 +02:00
2014-04-21 15:38:18 +02:00
rebuildTheme ( $theme [ 'theme' ], $action , $boardname );
2015-02-26 21:44:39 +01:00
if ( PHP_SAPI === 'cli' ) {
echo " done \n " ;
}
2012-04-11 18:49:22 +02:00
}
2014-04-21 15:38:18 +02:00
2014-10-09 03:57:18 +02:00
// Restore them again
2014-04-21 15:38:18 +02:00
$config = $_config ;
$board = $_board ;
2014-10-09 04:09:30 +02:00
2024-03-14 10:01:13 +01:00
// Reload the locale
2014-10-09 04:09:30 +02:00
if ( $config [ 'locale' ] != $current_locale ) {
2024-03-11 10:31:19 +01:00
$current_locale = $config [ 'locale' ];
init_locale ( $config [ 'locale' ]);
2014-10-09 04:09:30 +02:00
}
2012-04-11 18:49:22 +02:00
}
function loadThemeConfig ( $_theme ) {
global $config ;
2012-04-12 16:18:19 +02:00
if ( ! file_exists ( $config [ 'dir' ][ 'themes' ] . '/' . $_theme . '/info.php' ))
2012-04-11 18:49:22 +02:00
return false ;
// Load theme information into $theme
include $config [ 'dir' ][ 'themes' ] . '/' . $_theme . '/info.php' ;
return $theme ;
}
2013-07-18 18:06:26 +02:00
function rebuildTheme ( $theme , $action , $board = false ) {
2012-04-11 18:49:22 +02:00
global $config , $_theme ;
$_theme = $theme ;
$theme = loadThemeConfig ( $_theme );
2012-04-12 16:18:19 +02:00
if ( file_exists ( $config [ 'dir' ][ 'themes' ] . '/' . $_theme . '/theme.php' )) {
2012-04-11 18:49:22 +02:00
require_once $config [ 'dir' ][ 'themes' ] . '/' . $_theme . '/theme.php' ;
2013-08-03 13:50:25 +02:00
2013-07-18 18:06:26 +02:00
$theme [ 'build_function' ]( $action , themeSettings ( $_theme ), $board );
2012-04-11 18:49:22 +02:00
}
}
function themeSettings ( $theme ) {
2015-04-05 16:38:16 +02:00
if ( $settings = Cache :: get ( " theme_settings_ " . $theme )) {
return $settings ;
}
2013-08-01 04:14:26 +02:00
$query = prepare ( " SELECT `name`, `value` FROM ``theme_settings`` WHERE `theme` = :theme AND `name` IS NOT NULL " );
2012-04-11 18:49:22 +02:00
$query -> bindValue ( ':theme' , $theme );
$query -> execute () or error ( db_error ( $query ));
$settings = array ();
2013-08-01 02:51:43 +02:00
while ( $s = $query -> fetch ( PDO :: FETCH_ASSOC )) {
2012-04-11 18:49:22 +02:00
$settings [ $s [ 'name' ]] = $s [ 'value' ];
}
2015-04-05 16:38:16 +02:00
Cache :: set ( " theme_settings_ " . $theme , $settings );
2012-04-11 18:49:22 +02:00
return $settings ;
}
2024-02-04 14:53:06 +01:00
function sprintf3 ( $str , $vars , $delim = '%' ) {
$replaces = array ();
foreach ( $vars as $k => $v ) {
$replaces [ $delim . $k . $delim ] = $v ;
}
return str_replace ( array_keys ( $replaces ),
array_values ( $replaces ), $str );
}
function mb_substr_replace ( $string , $replacement , $start , $length ) {
return mb_substr ( $string , 0 , $start ) . $replacement . mb_substr ( $string , $start + $length );
}
2013-07-31 09:12:06 +02:00
2012-04-11 18:49:22 +02:00
function setupBoard ( $array ) {
global $board , $config ;
2013-08-03 13:50:25 +02:00
2024-04-30 12:46:44 +02:00
$board = [
2012-04-12 15:23:47 +02:00
'uri' => $array [ 'uri' ],
2012-04-12 18:11:41 +02:00
'title' => $array [ 'title' ],
2015-02-26 02:21:49 +01:00
'subtitle' => $array [ 'subtitle' ],
2024-04-30 12:46:44 +02:00
];
2013-08-03 13:50:25 +02:00
2012-04-12 18:11:41 +02:00
// older versions
$board [ 'name' ] = & $board [ 'title' ];
2013-08-03 13:50:25 +02:00
2012-04-11 18:49:22 +02:00
$board [ 'dir' ] = sprintf ( $config [ 'board_path' ], $board [ 'uri' ]);
$board [ 'url' ] = sprintf ( $config [ 'board_abbreviation' ], $board [ 'uri' ]);
2013-08-03 13:50:25 +02:00
2012-04-11 18:49:22 +02:00
loadConfig ();
2013-08-03 13:50:25 +02:00
2012-04-12 16:18:19 +02:00
if ( ! file_exists ( $board [ 'dir' ]))
2012-04-12 15:23:47 +02:00
@ mkdir ( $board [ 'dir' ], 0777 ) or error ( " Couldn't create " . $board [ 'dir' ] . " . Check permissions. " , true );
2012-04-12 16:18:19 +02:00
if ( ! file_exists ( $board [ 'dir' ] . $config [ 'dir' ][ 'img' ]))
2012-04-12 15:23:47 +02:00
@ mkdir ( $board [ 'dir' ] . $config [ 'dir' ][ 'img' ], 0777 )
or error ( " Couldn't create " . $board [ 'dir' ] . $config [ 'dir' ][ 'img' ] . " . Check permissions. " , true );
2012-04-12 16:18:19 +02:00
if ( ! file_exists ( $board [ 'dir' ] . $config [ 'dir' ][ 'thumb' ]))
2012-04-12 15:23:47 +02:00
@ mkdir ( $board [ 'dir' ] . $config [ 'dir' ][ 'thumb' ], 0777 )
or error ( " Couldn't create " . $board [ 'dir' ] . $config [ 'dir' ][ 'img' ] . " . Check permissions. " , true );
2012-04-12 16:18:19 +02:00
if ( ! file_exists ( $board [ 'dir' ] . $config [ 'dir' ][ 'res' ]))
2012-04-12 15:23:47 +02:00
@ mkdir ( $board [ 'dir' ] . $config [ 'dir' ][ 'res' ], 0777 )
or error ( " Couldn't create " . $board [ 'dir' ] . $config [ 'dir' ][ 'img' ] . " . Check permissions. " , true );
2012-04-11 18:49:22 +02:00
}
function openBoard ( $uri ) {
2016-05-08 14:01:55 +02:00
global $config , $build_pages , $board ;
2013-08-03 13:50:25 +02:00
if ( $config [ 'try_smarter' ])
$build_pages = array ();
2016-05-08 14:01:55 +02:00
// And what if we don't really need to change a board we have opened?
2024-02-04 14:53:06 +01:00
if ( isset ( $board ) && isset ( $board [ 'uri' ]) && $board [ 'uri' ] == $uri ) {
2016-05-08 14:01:55 +02:00
return true ;
}
$b = getBoardInfo ( $uri );
if ( $b ) {
setupBoard ( $b );
2015-04-01 18:13:32 +02:00
if ( function_exists ( 'after_open_board' )) {
after_open_board ();
}
2013-03-01 00:12:17 +01:00
return true ;
}
return false ;
}
function getBoardInfo ( $uri ) {
2012-04-11 18:49:22 +02:00
global $config ;
2012-08-30 17:35:27 +02:00
2012-04-12 16:18:19 +02:00
if ( $config [ 'cache' ][ 'enabled' ] && ( $board = cache :: get ( 'board_' . $uri ))) {
2013-03-01 00:12:17 +01:00
return $board ;
2011-04-13 15:47:47 +02:00
}
2013-08-03 13:50:25 +02:00
2013-08-01 04:14:26 +02:00
$query = prepare ( " SELECT * FROM ``boards`` WHERE `uri` = :uri LIMIT 1 " );
2012-04-11 18:49:22 +02:00
$query -> bindValue ( ':uri' , $uri );
$query -> execute () or error ( db_error ( $query ));
2013-08-03 13:50:25 +02:00
2013-08-01 02:51:43 +02:00
if ( $board = $query -> fetch ( PDO :: FETCH_ASSOC )) {
2012-04-12 16:18:19 +02:00
if ( $config [ 'cache' ][ 'enabled' ])
2012-04-11 18:49:22 +02:00
cache :: set ( 'board_' . $uri , $board );
2013-03-01 00:12:17 +01:00
return $board ;
2012-08-30 17:35:27 +02:00
}
return false ;
2012-04-11 18:49:22 +02:00
}
2012-04-10 17:18:38 +02:00
2012-04-11 18:49:22 +02:00
function boardTitle ( $uri ) {
2013-03-01 00:12:17 +01:00
$board = getBoardInfo ( $uri );
if ( $board )
2012-04-11 18:49:22 +02:00
return $board [ 'title' ];
2012-08-30 17:35:27 +02:00
return false ;
2012-04-11 18:49:22 +02:00
}
2024-02-04 14:53:06 +01:00
function purge ( $uri ) {
global $config , $debug ;
// Fix for Unicode
2024-03-14 10:01:13 +01:00
$uri = rawurlencode ( $uri );
2024-02-04 14:53:06 +01:00
$noescape = " /!~*()+: " ;
$noescape = preg_split ( '//' , $noescape );
$noescape_url = array_map ( " rawurlencode " , $noescape );
$uri = str_replace ( $noescape_url , $noescape , $uri );
if ( preg_match ( $config [ 'referer_match' ], $config [ 'root' ]) && isset ( $_SERVER [ 'REQUEST_URI' ])) {
$uri = ( str_replace ( '\\' , '/' , dirname ( $_SERVER [ 'REQUEST_URI' ])) == '/' ? '/' : str_replace ( '\\' , '/' , dirname ( $_SERVER [ 'REQUEST_URI' ])) . '/' ) . $uri ;
} else {
$uri = $config [ 'root' ] . $uri ;
}
if ( $config [ 'debug' ]) {
$debug [ 'purge' ][] = $uri ;
}
foreach ( $config [ 'purge' ] as & $purge ) {
$host = & $purge [ 0 ];
$port = & $purge [ 1 ];
$http_host = isset ( $purge [ 2 ]) ? $purge [ 2 ] : ( isset ( $_SERVER [ 'HTTP_HOST' ]) ? $_SERVER [ 'HTTP_HOST' ] : 'localhost' );
$request = " PURGE { $uri } HTTP/1.1 \r \n Host: { $http_host } \r \n User-Agent: Tinyboard \r \n Connection: Close \r \n \r \n " ;
if ( $fp = fsockopen ( $host , $port , $errno , $errstr , $config [ 'purge_timeout' ])) {
fwrite ( $fp , $request );
fclose ( $fp );
} else {
// Cannot connect?
error ( 'Could not PURGE for ' . $host );
}
}
}
function file_write ( $path , $data , $simple = false , $skip_purge = false ) {
global $config , $debug ;
if ( preg_match ( '/^remote:\/\/(.+)\:(.+)$/' , $path , $m )) {
2024-02-14 23:00:52 +01:00
if ( isset ( $config [ 'remote' ])) {
error ( 'Remote server support has been removed' );
2024-02-04 14:53:06 +01:00
}
}
if ( ! $fp = fopen ( $path , $simple ? 'w' : 'c' ))
error ( 'Unable to open file for writing: ' . $path );
// File locking
if ( ! $simple && ! flock ( $fp , LOCK_EX )) {
error ( 'Unable to lock file: ' . $path );
}
// Truncate file
if ( ! $simple && ! ftruncate ( $fp , 0 ))
error ( 'Unable to truncate file: ' . $path );
// Write data
if (( $bytes = fwrite ( $fp , $data )) === false )
error ( 'Unable to write to file: ' . $path );
// Unlock
if ( ! $simple )
flock ( $fp , LOCK_UN );
// Close
if ( ! fclose ( $fp ))
error ( 'Unable to close file: ' . $path );
/**
* Create gzipped file .
*
* When writing into a file foo . bar and the size is larger or equal to 1
* KiB , this also produces the gzipped version foo . bar . gz
*
* This is useful with nginx with gzip_static on .
*/
if ( $config [ 'gzip_static' ]) {
$gzpath = " $path .gz " ;
if ( $bytes & ~ 0x3ff ) { // if ($bytes >= 1024)
if ( file_put_contents ( $gzpath , gzencode ( $data ), $simple ? 0 : LOCK_EX ) === false )
error ( " Unable to write to file: $gzpath " );
//if (!touch($gzpath, filemtime($path), fileatime($path)))
// error("Unable to touch file: $gzpath");
}
else {
@ unlink ( $gzpath );
}
}
if ( ! $skip_purge && isset ( $config [ 'purge' ])) {
// Purge cache
if ( basename ( $path ) == $config [ 'file_index' ]) {
// Index file (/index.html); purge "/" as well
$uri = dirname ( $path );
// root
if ( $uri == '.' )
$uri = '' ;
else
$uri .= '/' ;
purge ( $uri );
}
purge ( $path );
}
if ( $config [ 'debug' ]) {
$debug [ 'write' ][] = $path . ': ' . $bytes . ' bytes' ;
}
event ( 'write' , $path );
}
function file_unlink ( $path ) {
global $config , $debug ;
if ( $config [ 'debug' ]) {
if ( ! isset ( $debug [ 'unlink' ]))
$debug [ 'unlink' ] = array ();
$debug [ 'unlink' ][] = $path ;
}
$ret = @ unlink ( $path );
2024-04-30 12:46:44 +02:00
if ( $config [ 'gzip_static' ]) {
$gzpath = " $path .gz " ;
2024-02-04 14:53:06 +01:00
@ unlink ( $gzpath );
}
if ( isset ( $config [ 'purge' ]) && $path [ 0 ] != '/' && isset ( $_SERVER [ 'HTTP_HOST' ])) {
// Purge cache
if ( basename ( $path ) == $config [ 'file_index' ]) {
// Index file (/index.html); purge "/" as well
$uri = dirname ( $path );
// root
if ( $uri == '.' )
$uri = '' ;
else
$uri .= '/' ;
purge ( $uri );
}
purge ( $path );
}
event ( 'unlink' , $path );
return $ret ;
}
2012-04-11 18:49:22 +02:00
function hasPermission ( $action = null , $board = null , $_mod = null ) {
global $config ;
2013-08-03 13:50:25 +02:00
2012-04-12 16:18:19 +02:00
if ( isset ( $_mod ))
2012-04-11 18:49:22 +02:00
$mod = & $_mod ;
else
global $mod ;
2013-08-03 13:50:25 +02:00
2012-04-12 16:18:19 +02:00
if ( ! is_array ( $mod ))
2012-04-11 18:49:22 +02:00
return false ;
2013-08-03 13:50:25 +02:00
2012-04-12 16:18:19 +02:00
if ( isset ( $action ) && $mod [ 'type' ] < $action )
2012-04-11 18:49:22 +02:00
return false ;
2013-08-03 13:50:25 +02:00
2012-04-12 16:18:19 +02:00
if ( ! isset ( $board ) || $config [ 'mod' ][ 'skip_per_board' ])
2012-04-11 18:49:22 +02:00
return true ;
2013-08-03 13:50:25 +02:00
2012-04-12 16:18:19 +02:00
if ( ! isset ( $mod [ 'boards' ]))
2012-04-11 18:49:22 +02:00
return false ;
2013-08-03 13:50:25 +02:00
2012-04-12 16:18:19 +02:00
if ( ! in_array ( '*' , $mod [ 'boards' ]) && ! in_array ( $board , $mod [ 'boards' ]))
2012-04-11 18:49:22 +02:00
return false ;
2013-08-03 13:50:25 +02:00
2012-04-11 18:49:22 +02:00
return true ;
}
2014-04-19 23:02:42 +02:00
function listBoards ( $just_uri = false ) {
2012-04-11 18:49:22 +02:00
global $config ;
2013-08-03 13:50:25 +02:00
2014-04-19 23:02:42 +02:00
$just_uri ? $cache_name = 'all_boards_uri' : $cache_name = 'all_boards' ;
2013-08-03 13:50:25 +02:00
2014-04-19 23:02:42 +02:00
if ( $config [ 'cache' ][ 'enabled' ] && ( $boards = cache :: get ( $cache_name )))
return $boards ;
2013-08-03 13:50:25 +02:00
2014-04-19 23:02:42 +02:00
if ( ! $just_uri ) {
2014-04-19 23:06:59 +02:00
$query = query ( " SELECT * FROM ``boards`` ORDER BY `uri` " ) or error ( db_error ());
2014-04-19 23:02:42 +02:00
$boards = $query -> fetchAll ();
} else {
$boards = array ();
$query = query ( " SELECT `uri` FROM ``boards`` " ) or error ( db_error ());
while ( $board = $query -> fetchColumn ()) {
$boards [] = $board ;
}
}
2024-03-14 10:01:13 +01:00
2012-04-12 16:18:19 +02:00
if ( $config [ 'cache' ][ 'enabled' ])
2014-04-19 23:02:42 +02:00
cache :: set ( $cache_name , $boards );
2013-08-03 13:50:25 +02:00
2012-04-11 18:49:22 +02:00
return $boards ;
}
function displayBan ( $ban ) {
2013-09-18 00:47:34 +02:00
global $config , $board ;
2013-08-03 13:50:25 +02:00
2013-07-16 12:33:37 +02:00
if ( ! $ban [ 'seen' ]) {
2013-09-17 01:15:24 +02:00
Bans :: seen ( $ban [ 'id' ]);
2013-07-16 12:33:37 +02:00
}
2013-08-03 13:50:25 +02:00
2012-04-11 18:49:22 +02:00
$ban [ 'ip' ] = $_SERVER [ 'REMOTE_ADDR' ];
2014-05-14 19:51:49 +02:00
2013-09-18 00:47:34 +02:00
if ( $ban [ 'post' ] && isset ( $ban [ 'post' ][ 'board' ], $ban [ 'post' ][ 'id' ])) {
2013-09-21 04:51:23 +02:00
if ( openBoard ( $ban [ 'post' ][ 'board' ])) {
2014-05-14 19:51:49 +02:00
$query = query ( sprintf ( " SELECT `files` FROM ``posts_%s`` WHERE `id` = " .
2013-09-21 04:51:23 +02:00
( int ) $ban [ 'post' ][ 'id' ], $board [ 'uri' ]));
if ( $_post = $query -> fetch ( PDO :: FETCH_ASSOC )) {
$ban [ 'post' ] = array_merge ( $ban [ 'post' ], $_post );
}
2013-09-18 00:47:34 +02:00
}
2013-09-18 00:56:04 +02:00
if ( $ban [ 'post' ][ 'thread' ]) {
$post = new Post ( $ban [ 'post' ]);
} else {
$post = new Thread ( $ban [ 'post' ], null , false , false );
}
2013-09-18 00:47:34 +02:00
}
2024-03-14 10:01:13 +01:00
2013-09-21 04:51:23 +02:00
$denied_appeals = array ();
$pending_appeal = false ;
2024-03-14 10:01:13 +01:00
2013-09-21 04:51:23 +02:00
if ( $config [ 'ban_appeals' ]) {
2014-09-25 22:59:24 +02:00
$query = query ( " SELECT `time`, `denied` FROM ``ban_appeals`` WHERE `ban_id` = " . ( int ) $ban [ 'id' ]) or error ( db_error ());
2013-09-21 04:51:23 +02:00
while ( $ban_appeal = $query -> fetch ( PDO :: FETCH_ASSOC )) {
if ( $ban_appeal [ 'denied' ]) {
$denied_appeals [] = $ban_appeal [ 'time' ];
} else {
$pending_appeal = $ban_appeal [ 'time' ];
}
}
}
2024-03-14 10:01:13 +01:00
2012-04-11 18:49:22 +02:00
// Show banned page and exit
die (
2022-08-29 16:50:45 +02:00
Element ( $config [ 'file_page_template' ], array (
2013-07-27 07:21:30 +02:00
'title' => _ ( 'Banned!' ),
2012-04-11 18:49:22 +02:00
'config' => $config ,
2016-08-15 04:13:26 +02:00
'boardlist' => createBoardlist ( isset ( $mod ) ? $mod : false ),
2022-08-29 16:50:45 +02:00
'body' => Element ( $config [ 'file_banned' ], array (
2011-04-22 16:24:15 +02:00
'config' => $config ,
2013-09-18 00:47:34 +02:00
'ban' => $ban ,
2013-09-18 00:56:04 +02:00
'board' => $board ,
2013-09-21 04:51:23 +02:00
'post' => isset ( $post ) ? $post -> build ( true ) : false ,
'denied_appeals' => $denied_appeals ,
'pending_appeal' => $pending_appeal
2012-04-11 18:49:22 +02:00
)
))
));
}
2013-09-10 08:20:54 +02:00
function checkBan ( $board = false ) {
2012-04-11 18:49:22 +02:00
global $config ;
2013-08-03 13:50:25 +02:00
2012-04-12 16:18:19 +02:00
if ( ! isset ( $_SERVER [ 'REMOTE_ADDR' ])) {
2012-04-11 18:49:22 +02:00
// Server misconfiguration
return ;
2024-03-14 10:01:13 +01:00
}
2013-08-03 13:50:25 +02:00
2012-04-12 16:18:19 +02:00
if ( event ( 'check-ban' , $board ))
2012-04-11 18:49:22 +02:00
return true ;
2014-09-01 00:30:33 +02:00
2015-03-24 05:19:25 +01:00
$ips = array ();
$ips [] = $_SERVER [ 'REMOTE_ADDR' ];
if ( $config [ 'proxy_check' ] && isset ( $_SERVER [ 'HTTP_X_FORWARDED_FOR' ])) {
$ips = array_merge ( $ips , explode ( " , " , $_SERVER [ 'HTTP_X_FORWARDED_FOR' ]));
}
foreach ( $ips as $ip ) {
2020-08-10 03:31:50 +02:00
$bans = Bans :: find ( $ip , $board , $config [ 'show_modname' ]);
2024-03-14 10:01:13 +01:00
2015-03-24 05:19:25 +01:00
foreach ( $bans as & $ban ) {
if ( $ban [ 'expires' ] && $ban [ 'expires' ] < time ()) {
Bans :: delete ( $ban [ 'id' ]);
if ( $config [ 'require_ban_view' ] && ! $ban [ 'seen' ]) {
if ( ! isset ( $_POST [ 'json_response' ])) {
displayBan ( $ban );
} else {
header ( 'Content-Type: text/json' );
die ( json_encode ( array ( 'error' => true , 'banned' => true )));
}
}
} else {
2013-09-17 01:15:24 +02:00
if ( ! isset ( $_POST [ 'json_response' ])) {
displayBan ( $ban );
} else {
header ( 'Content-Type: text/json' );
die ( json_encode ( array ( 'error' => true , 'banned' => true )));
}
2013-07-16 12:33:37 +02:00
}
2011-10-10 13:37:39 +02:00
}
}
2013-08-03 13:50:25 +02:00
2013-09-17 01:15:24 +02:00
// I'm not sure where else to put this. It doesn't really matter where; it just needs to be called every
// now and then to keep the ban list tidy.
2013-08-04 10:48:13 +02:00
if ( $config [ 'cache' ][ 'enabled' ] && $last_time_purged = cache :: get ( 'purged_bans_last' )) {
2013-08-04 10:49:21 +02:00
if ( time () - $last_time_purged < $config [ 'purge_bans' ] )
2013-08-04 10:48:13 +02:00
return ;
}
2024-03-14 10:01:13 +01:00
2013-09-17 01:15:24 +02:00
Bans :: purge ();
2024-03-14 10:01:13 +01:00
2013-08-04 10:48:13 +02:00
if ( $config [ 'cache' ][ 'enabled' ])
cache :: set ( 'purged_bans_last' , time ());
2012-04-11 18:49:22 +02:00
}
function threadLocked ( $id ) {
global $board ;
2013-08-03 13:50:25 +02:00
2012-04-12 16:18:19 +02:00
if ( event ( 'check-locked' , $id ))
2012-04-11 18:49:22 +02:00
return true ;
2013-08-03 13:50:25 +02:00
2013-08-01 04:14:26 +02:00
$query = prepare ( sprintf ( " SELECT `locked` FROM ``posts_%s`` WHERE `id` = :id AND `thread` IS NULL LIMIT 1 " , $board [ 'uri' ]));
2012-04-11 18:49:22 +02:00
$query -> bindValue ( ':id' , $id , PDO :: PARAM_INT );
$query -> execute () or error ( db_error ());
2013-08-03 13:50:25 +02:00
2013-08-01 02:51:43 +02:00
if (( $locked = $query -> fetchColumn ()) === false ) {
2012-04-11 18:49:22 +02:00
// Non-existant, so it can't be locked...
return false ;
2010-11-30 13:12:54 +01:00
}
2013-08-03 13:50:25 +02:00
2013-08-01 02:51:43 +02:00
return ( bool ) $locked ;
2012-04-11 18:49:22 +02:00
}
function threadSageLocked ( $id ) {
global $board ;
2013-08-03 13:50:25 +02:00
2012-04-12 16:18:19 +02:00
if ( event ( 'check-sage-locked' , $id ))
2012-04-11 18:49:22 +02:00
return true ;
2013-08-03 13:50:25 +02:00
2013-08-01 04:14:26 +02:00
$query = prepare ( sprintf ( " SELECT `sage` FROM ``posts_%s`` WHERE `id` = :id AND `thread` IS NULL LIMIT 1 " , $board [ 'uri' ]));
2012-04-11 18:49:22 +02:00
$query -> bindValue ( ':id' , $id , PDO :: PARAM_INT );
$query -> execute () or error ( db_error ());
2013-08-03 13:50:25 +02:00
2013-08-01 02:51:43 +02:00
if (( $sagelocked = $query -> fetchColumn ()) === false ) {
2012-04-11 18:49:22 +02:00
// Non-existant, so it can't be locked...
return false ;
}
2013-08-03 13:50:25 +02:00
2013-08-01 02:51:43 +02:00
return ( bool ) $sagelocked ;
2012-04-11 18:49:22 +02:00
}
function threadExists ( $id ) {
global $board ;
2013-08-03 13:50:25 +02:00
2013-08-01 04:14:26 +02:00
$query = prepare ( sprintf ( " SELECT 1 FROM ``posts_%s`` WHERE `id` = :id AND `thread` IS NULL LIMIT 1 " , $board [ 'uri' ]));
2012-04-11 18:49:22 +02:00
$query -> bindValue ( ':id' , $id , PDO :: PARAM_INT );
$query -> execute () or error ( db_error ());
2013-08-03 13:50:25 +02:00
2012-04-12 16:18:19 +02:00
if ( $query -> rowCount ()) {
2012-04-11 18:49:22 +02:00
return true ;
2012-08-30 17:35:27 +02:00
}
return false ;
2012-04-11 18:49:22 +02:00
}
2013-09-06 15:09:18 +02:00
function insertFloodPost ( array $post ) {
global $board ;
2024-03-14 10:01:13 +01:00
2013-09-06 15:09:18 +02:00
$query = prepare ( " INSERT INTO ``flood`` VALUES (NULL, :ip, :board, :time, :posthash, :filehash, :isreply) " );
$query -> bindValue ( ':ip' , $_SERVER [ 'REMOTE_ADDR' ]);
$query -> bindValue ( ':board' , $board [ 'uri' ]);
$query -> bindValue ( ':time' , time ());
2013-09-07 04:50:32 +02:00
$query -> bindValue ( ':posthash' , make_comment_hex ( $post [ 'body_nomarkup' ]));
2013-09-06 15:09:18 +02:00
if ( $post [ 'has_file' ])
$query -> bindValue ( ':filehash' , $post [ 'filehash' ]);
else
$query -> bindValue ( ':filehash' , null , PDO :: PARAM_NULL );
2013-09-06 16:04:22 +02:00
$query -> bindValue ( ':isreply' , ! $post [ 'op' ], PDO :: PARAM_INT );
2013-09-06 15:09:18 +02:00
$query -> execute () or error ( db_error ( $query ));
}
2012-04-11 18:49:22 +02:00
function post ( array $post ) {
global $pdo , $board ;
2015-04-03 08:56:28 +02:00
$query = prepare ( sprintf ( " INSERT INTO ``posts_%s`` VALUES ( NULL, :thread, :subject, :email, :name, :trip, :capcode, :body, :body_nomarkup, :time, :time, :files, :num_files, :filehash, :password, :ip, :sticky, :locked, :cycle, 0, :embed, :slug) " , $board [ 'uri' ]));
2013-08-03 13:50:25 +02:00
2012-04-11 18:49:22 +02:00
// Basic stuff
2012-04-12 16:18:19 +02:00
if ( ! empty ( $post [ 'subject' ])) {
2012-04-11 18:49:22 +02:00
$query -> bindValue ( ':subject' , $post [ 'subject' ]);
} else {
2013-09-06 15:09:18 +02:00
$query -> bindValue ( ':subject' , null , PDO :: PARAM_NULL );
2012-04-11 18:49:22 +02:00
}
2013-08-03 13:50:25 +02:00
2012-04-12 16:18:19 +02:00
if ( ! empty ( $post [ 'email' ])) {
2012-04-11 18:49:22 +02:00
$query -> bindValue ( ':email' , $post [ 'email' ]);
} else {
2013-09-06 15:09:18 +02:00
$query -> bindValue ( ':email' , null , PDO :: PARAM_NULL );
2012-04-11 18:49:22 +02:00
}
2013-08-03 13:50:25 +02:00
2012-04-12 16:18:19 +02:00
if ( ! empty ( $post [ 'trip' ])) {
2012-04-11 18:49:22 +02:00
$query -> bindValue ( ':trip' , $post [ 'trip' ]);
} else {
2013-09-06 15:09:18 +02:00
$query -> bindValue ( ':trip' , null , PDO :: PARAM_NULL );
2012-04-11 18:49:22 +02:00
}
2013-08-03 13:50:25 +02:00
2012-04-11 18:49:22 +02:00
$query -> bindValue ( ':name' , $post [ 'name' ]);
$query -> bindValue ( ':body' , $post [ 'body' ]);
$query -> bindValue ( ':body_nomarkup' , $post [ 'body_nomarkup' ]);
$query -> bindValue ( ':time' , isset ( $post [ 'time' ]) ? $post [ 'time' ] : time (), PDO :: PARAM_INT );
2024-03-14 10:01:13 +01:00
$query -> bindValue ( ':password' , $post [ 'password' ]);
2012-04-11 18:49:22 +02:00
$query -> bindValue ( ':ip' , isset ( $post [ 'ip' ]) ? $post [ 'ip' ] : $_SERVER [ 'REMOTE_ADDR' ]);
2013-08-03 13:50:25 +02:00
2013-06-08 12:55:13 +02:00
if ( $post [ 'op' ] && $post [ 'mod' ] && isset ( $post [ 'sticky' ]) && $post [ 'sticky' ]) {
2013-09-06 15:09:18 +02:00
$query -> bindValue ( ':sticky' , true , PDO :: PARAM_INT );
2012-04-11 18:49:22 +02:00
} else {
2013-09-06 15:09:18 +02:00
$query -> bindValue ( ':sticky' , false , PDO :: PARAM_INT );
2012-04-11 18:49:22 +02:00
}
2013-08-03 13:50:25 +02:00
2013-06-08 12:55:13 +02:00
if ( $post [ 'op' ] && $post [ 'mod' ] && isset ( $post [ 'locked' ]) && $post [ 'locked' ]) {
2013-09-06 15:09:18 +02:00
$query -> bindValue ( ':locked' , true , PDO :: PARAM_INT );
2012-04-11 18:49:22 +02:00
} else {
2013-09-06 15:09:18 +02:00
$query -> bindValue ( ':locked' , false , PDO :: PARAM_INT );
2012-04-11 18:49:22 +02:00
}
2013-08-03 13:50:25 +02:00
2015-04-03 08:56:28 +02:00
if ( $post [ 'op' ] && $post [ 'mod' ] && isset ( $post [ 'cycle' ]) && $post [ 'cycle' ]) {
$query -> bindValue ( ':cycle' , true , PDO :: PARAM_INT );
} else {
$query -> bindValue ( ':cycle' , false , PDO :: PARAM_INT );
}
2012-04-12 16:18:19 +02:00
if ( $post [ 'mod' ] && isset ( $post [ 'capcode' ]) && $post [ 'capcode' ]) {
2018-07-27 14:08:03 +02:00
$query -> bindValue ( ':capcode' , $post [ 'capcode' ], PDO :: PARAM_STR );
2012-04-11 18:49:22 +02:00
} else {
2013-09-06 15:09:18 +02:00
$query -> bindValue ( ':capcode' , null , PDO :: PARAM_NULL );
2012-04-11 18:49:22 +02:00
}
2013-08-03 13:50:25 +02:00
2012-04-12 16:18:19 +02:00
if ( ! empty ( $post [ 'embed' ])) {
2012-04-11 18:49:22 +02:00
$query -> bindValue ( ':embed' , $post [ 'embed' ]);
} else {
2013-09-06 15:09:18 +02:00
$query -> bindValue ( ':embed' , null , PDO :: PARAM_NULL );
2012-04-11 18:49:22 +02:00
}
2013-08-03 13:50:25 +02:00
2012-04-12 16:18:19 +02:00
if ( $post [ 'op' ]) {
2012-04-11 18:49:22 +02:00
// No parent thread, image
$query -> bindValue ( ':thread' , null , PDO :: PARAM_NULL );
} else {
$query -> bindValue ( ':thread' , $post [ 'thread' ], PDO :: PARAM_INT );
}
2013-08-03 13:50:25 +02:00
2012-04-12 16:18:19 +02:00
if ( $post [ 'has_file' ]) {
2014-04-27 15:48:47 +02:00
$query -> bindValue ( ':files' , json_encode ( $post [ 'files' ]));
$query -> bindValue ( ':num_files' , $post [ 'num_files' ]);
2012-04-11 18:49:22 +02:00
$query -> bindValue ( ':filehash' , $post [ 'filehash' ]);
} else {
2014-04-27 15:48:47 +02:00
$query -> bindValue ( ':files' , null , PDO :: PARAM_NULL );
$query -> bindValue ( ':num_files' , 0 );
2012-04-11 18:49:22 +02:00
$query -> bindValue ( ':filehash' , null , PDO :: PARAM_NULL );
2010-12-17 15:18:03 +01:00
}
2013-08-03 13:50:25 +02:00
2015-03-10 12:48:59 +01:00
if ( $post [ 'op' ]) {
$query -> bindValue ( ':slug' , slugify ( $post ));
}
2015-03-10 12:57:06 +01:00
else {
$query -> bindValue ( ':slug' , NULL );
}
2015-03-10 12:48:59 +01:00
2012-04-12 16:18:19 +02:00
if ( ! $query -> execute ()) {
2012-04-11 18:49:22 +02:00
undoImage ( $post );
error ( db_error ( $query ));
}
2013-08-03 13:50:25 +02:00
2012-04-11 18:49:22 +02:00
return $pdo -> lastInsertId ();
}
function bumpThread ( $id ) {
2013-08-03 23:42:34 +02:00
global $config , $board , $build_pages ;
2013-08-03 13:50:25 +02:00
2012-04-12 16:18:19 +02:00
if ( event ( 'bump' , $id ))
2012-04-11 18:49:22 +02:00
return true ;
2013-08-03 13:50:25 +02:00
2015-04-06 18:59:33 +02:00
if ( $config [ 'try_smarter' ]) {
$build_pages = array_merge ( range ( 1 , thread_find_page ( $id )), $build_pages );
}
2013-08-04 02:34:59 +02:00
2013-08-01 04:14:26 +02:00
$query = prepare ( sprintf ( " UPDATE ``posts_%s`` SET `bump` = :time WHERE `id` = :id AND `thread` IS NULL " , $board [ 'uri' ]));
2012-04-11 18:49:22 +02:00
$query -> bindValue ( ':time' , time (), PDO :: PARAM_INT );
$query -> bindValue ( ':id' , $id , PDO :: PARAM_INT );
$query -> execute () or error ( db_error ( $query ));
}
// Remove file from post
2014-04-30 23:18:35 +02:00
function deleteFile ( $id , $remove_entirely_if_already = true , $file = null ) {
2012-04-11 18:49:22 +02:00
global $board , $config ;
2013-08-03 13:50:25 +02:00
2014-04-30 23:18:35 +02:00
$query = prepare ( sprintf ( " SELECT `thread`, `files`, `num_files` FROM ``posts_%s`` WHERE `id` = :id LIMIT 1 " , $board [ 'uri' ]));
2012-04-11 18:49:22 +02:00
$query -> bindValue ( ':id' , $id , PDO :: PARAM_INT );
$query -> execute () or error ( db_error ( $query ));
2013-08-01 02:51:43 +02:00
if ( ! $post = $query -> fetch ( PDO :: FETCH_ASSOC ))
2012-04-11 18:49:22 +02:00
error ( $config [ 'error' ][ 'invalidpost' ]);
2014-04-30 23:18:35 +02:00
$files = json_decode ( $post [ 'files' ]);
$file_to_delete = $file !== false ? $files [( int ) $file ] : ( object ) array ( 'file' => false );
2013-08-03 13:50:25 +02:00
2015-04-20 13:20:17 +02:00
if ( ! $files [ 0 ]) error ( _ ( 'That post has no files.' ));
2014-04-30 23:18:35 +02:00
if ( $files [ 0 ] -> file == 'deleted' && $post [ 'num_files' ] == 1 && ! $post [ 'thread' ])
2012-04-11 18:49:22 +02:00
return ; // Can't delete OP's image completely.
2013-08-03 13:50:25 +02:00
2014-04-27 15:48:47 +02:00
$query = prepare ( sprintf ( " UPDATE ``posts_%s`` SET `files` = :file WHERE `id` = :id " , $board [ 'uri' ]));
2014-04-30 23:18:35 +02:00
if (( $file && $file_to_delete -> file == 'deleted' ) && $remove_entirely_if_already ) {
2012-04-11 18:49:22 +02:00
// Already deleted; remove file fully
2014-04-30 23:18:35 +02:00
$files [ $file ] = null ;
2012-04-11 18:49:22 +02:00
} else {
2014-04-30 23:18:35 +02:00
foreach ( $files as $i => $f ) {
if (( $file !== false && $i == $file ) || $file === null ) {
// Delete thumbnail
2016-05-17 09:53:09 +02:00
if ( isset ( $f -> thumb ) && $f -> thumb ) {
file_unlink ( $board [ 'dir' ] . $config [ 'dir' ][ 'thumb' ] . $f -> thumb );
unset ( $files [ $i ] -> thumb );
}
2013-08-03 13:50:25 +02:00
2014-04-30 23:18:35 +02:00
// Delete file
file_unlink ( $board [ 'dir' ] . $config [ 'dir' ][ 'img' ] . $f -> file );
$files [ $i ] -> file = 'deleted' ;
}
}
2012-04-11 18:49:22 +02:00
}
2013-08-03 13:50:25 +02:00
2014-04-30 23:18:35 +02:00
$query -> bindValue ( ':file' , json_encode ( $files ), PDO :: PARAM_STR );
2012-04-11 18:49:22 +02:00
$query -> bindValue ( ':id' , $id , PDO :: PARAM_INT );
$query -> execute () or error ( db_error ( $query ));
2013-08-03 13:50:25 +02:00
2012-04-12 16:18:19 +02:00
if ( $post [ 'thread' ])
2012-04-11 18:49:22 +02:00
buildThread ( $post [ 'thread' ]);
2012-05-28 09:08:09 +02:00
else
buildThread ( $id );
2012-04-11 18:49:22 +02:00
}
// rebuild post (markup)
function rebuildPost ( $id ) {
2015-02-17 01:21:30 +01:00
global $board , $mod ;
2013-08-03 13:50:25 +02:00
2015-02-17 01:21:30 +01:00
$query = prepare ( sprintf ( " SELECT * FROM ``posts_%s`` WHERE `id` = :id " , $board [ 'uri' ]));
2012-04-11 18:49:22 +02:00
$query -> bindValue ( ':id' , $id , PDO :: PARAM_INT );
$query -> execute () or error ( db_error ( $query ));
2013-08-03 13:50:25 +02:00
2013-08-01 02:51:43 +02:00
if (( ! $post = $query -> fetch ( PDO :: FETCH_ASSOC )) || ! $post [ 'body_nomarkup' ])
2012-04-11 18:49:22 +02:00
return false ;
2013-08-03 13:50:25 +02:00
2015-02-17 01:21:30 +01:00
markup ( $post [ 'body' ] = & $post [ 'body_nomarkup' ]);
$post = ( object ) $post ;
event ( 'rebuildpost' , $post );
$post = ( array ) $post ;
2013-08-03 13:50:25 +02:00
2013-08-01 04:14:26 +02:00
$query = prepare ( sprintf ( " UPDATE ``posts_%s`` SET `body` = :body WHERE `id` = :id " , $board [ 'uri' ]));
2015-02-17 01:21:30 +01:00
$query -> bindValue ( ':body' , $post [ 'body' ]);
2012-04-11 18:49:22 +02:00
$query -> bindValue ( ':id' , $id , PDO :: PARAM_INT );
$query -> execute () or error ( db_error ( $query ));
2013-08-03 13:50:25 +02:00
2012-04-11 18:49:22 +02:00
buildThread ( $post [ 'thread' ] ? $post [ 'thread' ] : $id );
2013-08-03 13:50:25 +02:00
2012-04-11 18:49:22 +02:00
return true ;
}
// Delete a post (reply or thread)
function deletePost ( $id , $error_if_doesnt_exist = true , $rebuild_after = true ) {
global $board , $config ;
2013-08-03 13:50:25 +02:00
2012-04-11 18:49:22 +02:00
// Select post and replies (if thread) in one query
2015-03-10 13:52:31 +01:00
$query = prepare ( sprintf ( " SELECT `id`,`thread`,`files`,`slug` FROM ``posts_%s`` WHERE `id` = :id OR `thread` = :id " , $board [ 'uri' ]));
2012-04-11 18:49:22 +02:00
$query -> bindValue ( ':id' , $id , PDO :: PARAM_INT );
$query -> execute () or error ( db_error ( $query ));
2013-08-03 13:50:25 +02:00
2012-04-12 16:18:19 +02:00
if ( $query -> rowCount () < 1 ) {
if ( $error_if_doesnt_exist )
2011-02-12 07:25:15 +01:00
error ( $config [ 'error' ][ 'invalidpost' ]);
2012-04-11 18:49:22 +02:00
else return false ;
}
2013-08-03 13:50:25 +02:00
2012-04-11 18:49:22 +02:00
$ids = array ();
2013-08-03 13:50:25 +02:00
2012-04-11 18:49:22 +02:00
// Delete posts and maybe replies
2013-08-01 02:51:43 +02:00
while ( $post = $query -> fetch ( PDO :: FETCH_ASSOC )) {
2013-08-17 08:06:45 +02:00
event ( 'delete' , $post );
2024-03-14 10:01:13 +01:00
2019-10-14 08:45:39 +02:00
$thread_id = $post [ 'thread' ];
2012-04-12 16:18:19 +02:00
if ( ! $post [ 'thread' ]) {
2012-04-11 18:49:22 +02:00
// Delete thread HTML page
2015-03-10 12:48:59 +01:00
file_unlink ( $board [ 'dir' ] . $config [ 'dir' ][ 'res' ] . link_for ( $post ) );
file_unlink ( $board [ 'dir' ] . $config [ 'dir' ][ 'res' ] . link_for ( $post , true ) ); // noko50
2013-08-20 21:53:11 +02:00
file_unlink ( $board [ 'dir' ] . $config [ 'dir' ][ 'res' ] . sprintf ( '%d.json' , $post [ 'id' ]));
2013-08-03 13:50:25 +02:00
2013-08-01 04:14:26 +02:00
$antispam_query = prepare ( 'DELETE FROM ``antispam`` WHERE `board` = :board AND `thread` = :thread' );
2012-04-18 17:19:10 +02:00
$antispam_query -> bindValue ( ':board' , $board [ 'uri' ]);
$antispam_query -> bindValue ( ':thread' , $post [ 'id' ]);
$antispam_query -> execute () or error ( db_error ( $antispam_query ));
2012-04-12 16:18:19 +02:00
} elseif ( $query -> rowCount () == 1 ) {
2012-04-11 18:49:22 +02:00
// Rebuild thread
$rebuild = & $post [ 'thread' ];
2011-01-21 03:14:55 +01:00
}
2014-04-27 15:48:47 +02:00
if ( $post [ 'files' ]) {
2011-01-21 03:14:55 +01:00
// Delete file
2014-04-27 15:48:47 +02:00
foreach ( json_decode ( $post [ 'files' ]) as $i => $f ) {
2014-04-30 23:18:35 +02:00
if ( $f -> file !== 'deleted' ) {
file_unlink ( $board [ 'dir' ] . $config [ 'dir' ][ 'img' ] . $f -> file );
file_unlink ( $board [ 'dir' ] . $config [ 'dir' ][ 'thumb' ] . $f -> thumb );
}
2014-04-27 15:48:47 +02:00
}
2011-01-21 03:14:55 +01:00
}
2013-08-03 13:50:25 +02:00
2012-04-11 18:49:22 +02:00
$ids [] = ( int ) $post [ 'id' ];
2013-08-03 13:50:25 +02:00
2011-01-21 03:14:55 +01:00
}
2013-08-03 13:50:25 +02:00
2013-08-01 04:14:26 +02:00
$query = prepare ( sprintf ( " DELETE FROM ``posts_%s`` WHERE `id` = :id OR `thread` = :id " , $board [ 'uri' ]));
2012-04-11 18:49:22 +02:00
$query -> bindValue ( ':id' , $id , PDO :: PARAM_INT );
$query -> execute () or error ( db_error ( $query ));
2013-08-03 13:50:25 +02:00
2013-08-30 07:00:33 +02:00
$query = prepare ( " SELECT `board`, `post` FROM ``cites`` WHERE `target_board` = :board AND (`target` = " . implode ( ' OR `target` = ' , $ids ) . " ) ORDER BY `board` " );
2012-04-11 18:49:22 +02:00
$query -> bindValue ( ':board' , $board [ 'uri' ]);
$query -> execute () or error ( db_error ( $query ));
2013-08-01 02:51:43 +02:00
while ( $cite = $query -> fetch ( PDO :: FETCH_ASSOC )) {
2012-04-12 16:18:19 +02:00
if ( $board [ 'uri' ] != $cite [ 'board' ]) {
if ( ! isset ( $tmp_board ))
2012-04-11 18:49:22 +02:00
$tmp_board = $board [ 'uri' ];
openBoard ( $cite [ 'board' ]);
2011-01-18 14:41:43 +01:00
}
2012-04-11 18:49:22 +02:00
rebuildPost ( $cite [ 'post' ]);
}
2013-08-03 13:50:25 +02:00
2012-04-12 16:18:19 +02:00
if ( isset ( $tmp_board ))
2012-04-11 18:49:22 +02:00
openBoard ( $tmp_board );
2013-08-03 13:50:25 +02:00
2013-08-30 07:00:33 +02:00
$query = prepare ( " DELETE FROM ``cites`` WHERE (`target_board` = :board AND (`target` = " . implode ( ' OR `target` = ' , $ids ) . " )) OR (`board` = :board AND (`post` = " . implode ( ' OR `post` = ' , $ids ) . " )) " );
2012-04-11 18:49:22 +02:00
$query -> bindValue ( ':board' , $board [ 'uri' ]);
$query -> execute () or error ( db_error ( $query ));
2019-10-14 08:45:39 +02:00
2024-04-30 12:46:44 +02:00
// No need to run on OPs
if ( $config [ 'anti_bump_flood' ] && isset ( $thread_id )) {
$query = prepare ( sprintf ( " SELECT `sage` FROM ``posts_%s`` WHERE `id` = :thread " , $board [ 'uri' ]));
$query -> bindValue ( ':thread' , $thread_id );
$query -> execute () or error ( db_error ( $query ));
$bumplocked = ( bool ) $query -> fetchColumn ();
if ( ! $bumplocked ) {
$query = prepare ( sprintf ( " SELECT `time` FROM ``posts_%s`` WHERE (`thread` = :thread AND NOT email <=> 'sage') OR `id` = :thread ORDER BY `time` DESC LIMIT 1 " , $board [ 'uri' ]));
$query -> bindValue ( ':thread' , $thread_id );
$query -> execute () or error ( db_error ( $query ));
$bump = $query -> fetchColumn ();
$query = prepare ( sprintf ( " UPDATE ``posts_%s`` SET `bump` = :bump WHERE `id` = :thread " , $board [ 'uri' ]));
$query -> bindValue ( ':bump' , $bump );
$query -> bindValue ( ':thread' , $thread_id );
$query -> execute () or error ( db_error ( $query ));
}
}
2022-01-22 04:31:52 +01:00
2012-04-12 16:18:19 +02:00
if ( isset ( $rebuild ) && $rebuild_after ) {
2012-04-11 18:49:22 +02:00
buildThread ( $rebuild );
2013-08-22 19:08:26 +02:00
buildIndex ();
2012-04-11 18:49:22 +02:00
}
2013-08-03 13:50:25 +02:00
2012-04-11 18:49:22 +02:00
return true ;
}
2015-02-26 02:21:49 +01:00
function clean ( $pid = false ) {
2012-04-11 18:49:22 +02:00
global $board , $config ;
$offset = round ( $config [ 'max_pages' ] * $config [ 'threads_per_page' ]);
2013-08-03 13:50:25 +02:00
2012-04-11 18:49:22 +02:00
// I too wish there was an easier way of doing this...
2013-08-01 04:14:26 +02:00
$query = prepare ( sprintf ( " SELECT `id` FROM ``posts_%s`` WHERE `thread` IS NULL ORDER BY `sticky` DESC, `bump` DESC LIMIT :offset, 9001 " , $board [ 'uri' ]));
2012-04-11 18:49:22 +02:00
$query -> bindValue ( ':offset' , $offset , PDO :: PARAM_INT );
2013-08-03 13:50:25 +02:00
2012-04-11 18:49:22 +02:00
$query -> execute () or error ( db_error ( $query ));
2013-08-01 02:51:43 +02:00
while ( $post = $query -> fetch ( PDO :: FETCH_ASSOC )) {
2014-02-18 20:42:19 +01:00
deletePost ( $post [ 'id' ], false , false );
2015-02-26 02:21:49 +01:00
if ( $pid ) modLog ( " Automatically deleting thread # { $post [ 'id' ] } due to new thread # { $pid } " );
}
// Bump off threads with X replies earlier, spam prevention method
if ( $config [ 'early_404' ]) {
$offset = round ( $config [ 'early_404_page' ] * $config [ 'threads_per_page' ]);
$query = prepare ( sprintf ( " SELECT `id` AS `thread_id`, (SELECT COUNT(`id`) FROM ``posts_%s`` WHERE `thread` = `thread_id`) AS `reply_count` FROM ``posts_%s`` WHERE `thread` IS NULL ORDER BY `sticky` DESC, `bump` DESC LIMIT :offset, 9001 " , $board [ 'uri' ], $board [ 'uri' ]));
$query -> bindValue ( ':offset' , $offset , PDO :: PARAM_INT );
$query -> execute () or error ( db_error ( $query ));
2024-03-14 10:01:13 +01:00
2017-05-17 20:54:35 +02:00
if ( $config [ 'early_404_staged' ]) {
$page = $config [ 'early_404_page' ];
$iter = 0 ;
}
else {
$page = 1 ;
}
2015-02-26 02:21:49 +01:00
while ( $post = $query -> fetch ( PDO :: FETCH_ASSOC )) {
2017-05-17 20:54:35 +02:00
if ( $post [ 'reply_count' ] < $page * $config [ 'early_404_replies' ]) {
2015-02-26 02:21:49 +01:00
deletePost ( $post [ 'thread_id' ], false , false );
if ( $pid ) modLog ( " Automatically deleting thread # { $post [ 'thread_id' ] } due to new thread # { $pid } (early 404 is set, # { $post [ 'thread_id' ] } had { $post [ 'reply_count' ] } replies) " );
}
2017-05-17 20:54:35 +02:00
if ( $config [ 'early_404_staged' ]) {
$iter ++ ;
if ( $iter == $config [ 'threads_per_page' ]) {
$page ++ ;
$iter = 0 ;
}
}
2015-02-26 02:21:49 +01:00
}
2012-04-11 18:49:22 +02:00
}
}
2013-08-03 13:50:25 +02:00
function thread_find_page ( $thread ) {
global $config , $board ;
$query = query ( sprintf ( " SELECT `id` FROM ``posts_%s`` WHERE `thread` IS NULL ORDER BY `sticky` DESC, `bump` DESC " , $board [ 'uri' ])) or error ( db_error ( $query ));
$threads = $query -> fetchAll ( PDO :: FETCH_COLUMN );
if (( $index = array_search ( $thread , $threads )) === false )
return false ;
return floor (( $config [ 'threads_per_page' ] + $index ) / $config [ 'threads_per_page' ]);
}
2016-05-08 10:54:30 +02:00
// $brief means that we won't need to generate anything yet
function index ( $page , $mod = false , $brief = false ) {
2012-04-11 18:49:22 +02:00
global $board , $config , $debug ;
$body = '' ;
$offset = round ( $page * $config [ 'threads_per_page' ] - $config [ 'threads_per_page' ]);
2013-08-03 13:50:25 +02:00
2013-08-01 04:14:26 +02:00
$query = prepare ( sprintf ( " SELECT * FROM ``posts_%s`` WHERE `thread` IS NULL ORDER BY `sticky` DESC, `bump` DESC LIMIT :offset,:threads_per_page " , $board [ 'uri' ]));
2012-04-11 18:49:22 +02:00
$query -> bindValue ( ':offset' , $offset , PDO :: PARAM_INT );
$query -> bindValue ( ':threads_per_page' , $config [ 'threads_per_page' ], PDO :: PARAM_INT );
$query -> execute () or error ( db_error ( $query ));
2013-08-03 13:50:25 +02:00
2013-07-31 23:04:36 +02:00
if ( $page == 1 && $query -> rowCount () < $config [ 'threads_per_page' ])
2013-07-31 23:02:42 +02:00
$board [ 'thread_count' ] = $query -> rowCount ();
2013-08-03 13:50:25 +02:00
2013-07-31 23:02:42 +02:00
if ( $query -> rowCount () < 1 && $page > 1 )
2012-04-11 18:49:22 +02:00
return false ;
2013-07-02 19:52:29 +02:00
2013-08-05 12:09:15 +02:00
$threads = array ();
2024-03-14 10:01:13 +01:00
2013-08-01 02:51:43 +02:00
while ( $th = $query -> fetch ( PDO :: FETCH_ASSOC )) {
2013-08-16 13:08:01 +02:00
$thread = new Thread ( $th , $mod ? '?/' : $config [ 'root' ], $mod );
2013-08-03 13:50:25 +02:00
2013-09-08 05:35:02 +02:00
if ( $config [ 'cache' ][ 'enabled' ]) {
$cached = cache :: get ( " thread_index_ { $board [ 'uri' ] } _ { $th [ 'id' ] } " );
if ( isset ( $cached [ 'replies' ], $cached [ 'omitted' ])) {
$replies = $cached [ 'replies' ];
$omitted = $cached [ 'omitted' ];
} else {
2013-09-09 11:19:11 +02:00
unset ( $cached );
2013-09-08 05:35:02 +02:00
}
}
2016-05-08 10:54:30 +02:00
2013-09-09 11:19:11 +02:00
if ( ! isset ( $cached )) {
2013-08-01 04:14:26 +02:00
$posts = prepare ( sprintf ( " SELECT * FROM ``posts_%s`` WHERE `thread` = :id ORDER BY `id` DESC LIMIT :limit " , $board [ 'uri' ]));
2012-09-27 20:46:20 +02:00
$posts -> bindValue ( ':id' , $th [ 'id' ]);
$posts -> bindValue ( ':limit' , ( $th [ 'sticky' ] ? $config [ 'threads_preview_sticky' ] : $config [ 'threads_preview' ]), PDO :: PARAM_INT );
$posts -> execute () or error ( db_error ( $posts ));
2013-08-03 13:50:25 +02:00
2013-01-28 04:27:47 +01:00
$replies = array_reverse ( $posts -> fetchAll ( PDO :: FETCH_ASSOC ));
2013-08-03 13:50:25 +02:00
2013-01-28 04:27:47 +01:00
if ( count ( $replies ) == ( $th [ 'sticky' ] ? $config [ 'threads_preview_sticky' ] : $config [ 'threads_preview' ])) {
2013-06-18 19:21:41 +02:00
$count = numPosts ( $th [ 'id' ]);
$omitted = array ( 'post_count' => $count [ 'replies' ], 'image_count' => $count [ 'images' ]);
2013-01-28 04:27:47 +01:00
} else {
$omitted = false ;
}
2013-08-03 13:50:25 +02:00
2013-01-23 02:43:46 +01:00
if ( $config [ 'cache' ][ 'enabled' ])
2013-01-28 04:27:47 +01:00
cache :: set ( " thread_index_ { $board [ 'uri' ] } _ { $th [ 'id' ] } " , array (
'replies' => $replies ,
'omitted' => $omitted ,
));
2013-01-23 02:43:46 +01:00
}
2013-08-03 13:50:25 +02:00
2013-01-23 02:43:46 +01:00
$num_images = 0 ;
foreach ( $replies as $po ) {
2014-04-27 15:48:47 +02:00
if ( $po [ 'num_files' ])
$num_images += $po [ 'num_files' ];
2013-08-03 13:50:25 +02:00
2013-08-16 13:08:01 +02:00
$thread -> add ( new Post ( $po , $mod ? '?/' : $config [ 'root' ], $mod ));
2013-01-23 02:43:46 +01:00
}
2013-08-03 13:50:25 +02:00
2013-12-28 19:46:50 +01:00
$thread -> images = $num_images ;
$thread -> replies = isset ( $omitted [ 'post_count' ]) ? $omitted [ 'post_count' ] : count ( $replies );
2013-01-28 04:27:47 +01:00
if ( $omitted ) {
$thread -> omitted = $omitted [ 'post_count' ] - ( $th [ 'sticky' ] ? $config [ 'threads_preview_sticky' ] : $config [ 'threads_preview' ]);
$thread -> omitted_images = $omitted [ 'image_count' ] - $num_images ;
2012-04-11 18:49:22 +02:00
}
2024-03-14 10:01:13 +01:00
2013-07-02 19:52:29 +02:00
$threads [] = $thread ;
2016-05-08 10:54:30 +02:00
if ( ! $brief ) {
$body .= $thread -> build ( true );
}
2011-01-18 14:41:43 +01:00
}
2013-08-03 13:50:25 +02:00
2015-04-22 05:54:48 +02:00
if ( $config [ 'file_board' ]) {
2022-08-29 16:50:45 +02:00
$body = Element ( $config [ 'file_fileboard' ], array ( 'body' => $body , 'mod' => $mod ));
2015-04-22 05:54:48 +02:00
}
2012-04-11 18:49:22 +02:00
return array (
2012-09-27 20:46:20 +02:00
'board' => $board ,
'body' => $body ,
2012-04-11 18:49:22 +02:00
'post_url' => $config [ 'post_url' ],
'config' => $config ,
2013-07-02 19:52:29 +02:00
'boardlist' => createBoardlist ( $mod ),
2014-04-30 23:18:35 +02:00
'threads' => $threads ,
2012-04-11 18:49:22 +02:00
);
}
2010-11-04 15:39:02 +01:00
2012-04-11 18:49:22 +02:00
function getPageButtons ( $pages , $mod = false ) {
global $config , $board ;
2013-08-03 13:50:25 +02:00
2012-04-11 18:49:22 +02:00
$btn = array ();
$root = ( $mod ? '?/' : $config [ 'root' ]) . $board [ 'dir' ];
2013-08-03 13:50:25 +02:00
2012-04-12 16:18:19 +02:00
foreach ( $pages as $num => $page ) {
if ( isset ( $page [ 'selected' ])) {
2012-04-11 18:49:22 +02:00
// Previous button
2012-04-12 16:18:19 +02:00
if ( $num == 0 ) {
2012-04-11 18:49:22 +02:00
// There is no previous page.
$btn [ 'prev' ] = _ ( 'Previous' );
} else {
$loc = ( $mod ? '?/' . $board [ 'uri' ] . '/' : '' ) .
( $num == 1 ?
$config [ 'file_index' ]
:
sprintf ( $config [ 'file_page' ], $num )
);
2013-08-03 13:50:25 +02:00
2012-04-11 18:49:22 +02:00
$btn [ 'prev' ] = '<form action="' . ( $mod ? '' : $root . $loc ) . '" method="get">' .
( $mod ?
'<input type="hidden" name="status" value="301" />' .
'<input type="hidden" name="r" value="' . htmlentities ( $loc ) . '" />'
: '' ) .
'<input type="submit" value="' . _ ( 'Previous' ) . '" /></form>' ;
2011-02-17 07:07:36 +01:00
}
2013-08-03 13:50:25 +02:00
2012-04-12 16:18:19 +02:00
if ( $num == count ( $pages ) - 1 ) {
2012-04-11 18:49:22 +02:00
// There is no next page.
$btn [ 'next' ] = _ ( 'Next' );
} else {
$loc = ( $mod ? '?/' . $board [ 'uri' ] . '/' : '' ) . sprintf ( $config [ 'file_page' ], $num + 2 );
2013-08-03 13:50:25 +02:00
2012-04-11 18:49:22 +02:00
$btn [ 'next' ] = '<form action="' . ( $mod ? '' : $root . $loc ) . '" method="get">' .
( $mod ?
'<input type="hidden" name="status" value="301" />' .
'<input type="hidden" name="r" value="' . htmlentities ( $loc ) . '" />'
: '' ) .
'<input type="submit" value="' . _ ( 'Next' ) . '" /></form>' ;
2010-11-02 11:57:33 +01:00
}
}
}
2013-08-03 13:50:25 +02:00
2012-04-11 18:49:22 +02:00
return $btn ;
}
function getPages ( $mod = false ) {
global $board , $config ;
2013-08-03 13:50:25 +02:00
2013-07-31 23:02:42 +02:00
if ( isset ( $board [ 'thread_count' ])) {
$count = $board [ 'thread_count' ];
} else {
// Count threads
2013-08-01 04:14:26 +02:00
$query = query ( sprintf ( " SELECT COUNT(*) FROM ``posts_%s`` WHERE `thread` IS NULL " , $board [ 'uri' ])) or error ( db_error ());
2013-07-31 23:02:42 +02:00
$count = $query -> fetchColumn ();
}
2012-04-11 18:49:22 +02:00
$count = floor (( $config [ 'threads_per_page' ] + $count - 1 ) / $config [ 'threads_per_page' ]);
2013-08-03 13:50:25 +02:00
2012-04-12 16:18:19 +02:00
if ( $count < 1 ) $count = 1 ;
2013-08-03 13:50:25 +02:00
2012-04-11 18:49:22 +02:00
$pages = array ();
2012-04-12 16:18:19 +02:00
for ( $x = 0 ; $x < $count && $x < $config [ 'max_pages' ]; $x ++ ) {
2012-04-11 18:49:22 +02:00
$pages [] = array (
'num' => $x + 1 ,
'link' => $x == 0 ? ( $mod ? '?/' : $config [ 'root' ]) . $board [ 'dir' ] . $config [ 'file_index' ] : ( $mod ? '?/' : $config [ 'root' ]) . $board [ 'dir' ] . sprintf ( $config [ 'file_page' ], $x + 1 )
);
2011-01-18 14:41:43 +01:00
}
2013-08-03 13:50:25 +02:00
2012-04-11 18:49:22 +02:00
return $pages ;
}
2013-09-07 04:50:32 +02:00
// Stolen with permission from PlainIB (by Frank Usrs)
function make_comment_hex ( $str ) {
// remove cross-board citations
// the numbers don't matter
$str = preg_replace ( '!>>>/[A-Za-z0-9]+/!' , '' , $str );
if ( function_exists ( 'iconv' )) {
// remove diacritics and other noise
// FIXME: this removes cyrillic entirely
2014-04-18 14:38:05 +02:00
$oldstr = $str ;
$str = @ iconv ( 'UTF-8' , 'ASCII//TRANSLIT//IGNORE' , $str );
if ( ! $str ) $str = $oldstr ;
2013-09-07 04:50:32 +02:00
}
$str = strtolower ( $str );
// strip all non-alphabet characters
$str = preg_replace ( '/[^a-z]/' , '' , $str );
return md5 ( $str );
}
2012-04-11 18:49:22 +02:00
function makerobot ( $body ) {
global $config ;
$body = strtolower ( $body );
2013-08-03 13:50:25 +02:00
2012-04-11 18:49:22 +02:00
// Leave only letters
$body = preg_replace ( '/[^a-z]/i' , '' , $body );
// Remove repeating characters
2012-04-12 16:18:19 +02:00
if ( $config [ 'robot_strip_repeating' ])
2012-04-11 18:49:22 +02:00
$body = preg_replace ( '/(.)\\1+/' , '$1' , $body );
2013-08-03 13:50:25 +02:00
2012-04-11 18:49:22 +02:00
return sha1 ( $body );
}
function checkRobot ( $body ) {
2012-08-30 17:35:27 +02:00
if ( empty ( $body ) || event ( 'check-robot' , $body ))
2012-04-11 18:49:22 +02:00
return true ;
2013-08-03 13:50:25 +02:00
2012-04-11 18:49:22 +02:00
$body = makerobot ( $body );
2013-08-01 04:14:26 +02:00
$query = prepare ( " SELECT 1 FROM ``robot`` WHERE `hash` = :hash LIMIT 1 " );
2012-04-11 18:49:22 +02:00
$query -> bindValue ( ':hash' , $body );
$query -> execute () or error ( db_error ( $query ));
2013-08-03 13:50:25 +02:00
2013-08-01 02:51:43 +02:00
if ( $query -> fetchColumn ()) {
2012-04-11 18:49:22 +02:00
return true ;
2011-01-20 09:24:23 +01:00
}
2012-08-30 17:35:27 +02:00
// Insert new hash
2013-08-01 04:14:26 +02:00
$query = prepare ( " INSERT INTO ``robot`` VALUES (:hash) " );
2012-08-30 17:35:27 +02:00
$query -> bindValue ( ':hash' , $body );
$query -> execute () or error ( db_error ( $query ));
2013-08-03 13:50:25 +02:00
2012-08-30 17:35:27 +02:00
return false ;
2012-04-11 18:49:22 +02:00
}
2013-06-18 19:21:41 +02:00
// Returns an associative array with 'replies' and 'images' keys
2012-04-11 18:49:22 +02:00
function numPosts ( $id ) {
global $board ;
2014-04-27 15:48:47 +02:00
$query = prepare ( sprintf ( " SELECT COUNT(*) AS `replies`, SUM(`num_files`) AS `images` FROM ``posts_%s`` WHERE `thread` = :thread " , $board [ 'uri' ], $board [ 'uri' ]));
2012-04-11 18:49:22 +02:00
$query -> bindValue ( ':thread' , $id , PDO :: PARAM_INT );
$query -> execute () or error ( db_error ( $query ));
2013-08-03 13:50:25 +02:00
2013-08-31 18:04:42 +02:00
return $query -> fetch ( PDO :: FETCH_ASSOC );
2012-04-11 18:49:22 +02:00
}
function muteTime () {
global $config ;
2013-08-03 13:50:25 +02:00
2012-04-12 16:18:19 +02:00
if ( $time = event ( 'mute-time' ))
2012-04-11 18:49:22 +02:00
return $time ;
2013-08-03 13:50:25 +02:00
2012-04-11 18:49:22 +02:00
// Find number of mutes in the past X hours
2013-08-01 04:14:26 +02:00
$query = prepare ( " SELECT COUNT(*) FROM ``mutes`` WHERE `time` >= :time AND `ip` = :ip " );
2012-04-11 18:49:22 +02:00
$query -> bindValue ( ':time' , time () - ( $config [ 'robot_mute_hour' ] * 3600 ), PDO :: PARAM_INT );
$query -> bindValue ( ':ip' , $_SERVER [ 'REMOTE_ADDR' ]);
$query -> execute () or error ( db_error ( $query ));
2013-08-03 13:50:25 +02:00
2013-08-01 02:51:43 +02:00
if ( ! $result = $query -> fetchColumn ())
return 0 ;
return pow ( $config [ 'robot_mute_multiplier' ], $result );
2012-04-11 18:49:22 +02:00
}
function mute () {
// Insert mute
2013-08-01 04:14:26 +02:00
$query = prepare ( " INSERT INTO ``mutes`` VALUES (:ip, :time) " );
2012-04-11 18:49:22 +02:00
$query -> bindValue ( ':time' , time (), PDO :: PARAM_INT );
$query -> bindValue ( ':ip' , $_SERVER [ 'REMOTE_ADDR' ]);
$query -> execute () or error ( db_error ( $query ));
2013-08-03 13:50:25 +02:00
2012-04-11 18:49:22 +02:00
return muteTime ();
}
function checkMute () {
2024-02-04 14:53:06 +01:00
global $config , $debug ;
2013-08-03 13:50:25 +02:00
2012-04-12 16:18:19 +02:00
if ( $config [ 'cache' ][ 'enabled' ]) {
2012-04-11 18:49:22 +02:00
// Cached mute?
2012-04-12 16:18:19 +02:00
if (( $mute = cache :: get ( " mute_ ${ _SERVER['REMOTE_ADDR'] } " )) && ( $mutetime = cache :: get ( " mutetime_ ${ _SERVER['REMOTE_ADDR'] } " ))) {
2012-04-11 18:49:22 +02:00
error ( sprintf ( $config [ 'error' ][ 'youaremuted' ], $mute [ 'time' ] + $mutetime - time ()));
}
2011-01-20 09:24:23 +01:00
}
2013-08-03 13:50:25 +02:00
2012-04-11 18:49:22 +02:00
$mutetime = muteTime ();
2012-04-12 16:18:19 +02:00
if ( $mutetime > 0 ) {
2012-04-11 18:49:22 +02:00
// Find last mute time
2013-08-01 04:14:26 +02:00
$query = prepare ( " SELECT `time` FROM ``mutes`` WHERE `ip` = :ip ORDER BY `time` DESC LIMIT 1 " );
2011-01-20 09:24:23 +01:00
$query -> bindValue ( ':ip' , $_SERVER [ 'REMOTE_ADDR' ]);
$query -> execute () or error ( db_error ( $query ));
2013-08-03 13:50:25 +02:00
2013-08-01 02:51:43 +02:00
if ( ! $mute = $query -> fetch ( PDO :: FETCH_ASSOC )) {
2012-04-11 18:49:22 +02:00
// What!? He's muted but he's not muted...
return ;
2011-05-19 13:24:17 +02:00
}
2013-08-03 13:50:25 +02:00
2012-04-12 16:18:19 +02:00
if ( $mute [ 'time' ] + $mutetime > time ()) {
if ( $config [ 'cache' ][ 'enabled' ]) {
2012-04-11 18:49:22 +02:00
cache :: set ( " mute_ ${ _SERVER['REMOTE_ADDR'] } " , $mute , $mute [ 'time' ] + $mutetime - time ());
cache :: set ( " mutetime_ ${ _SERVER['REMOTE_ADDR'] } " , $mutetime , $mute [ 'time' ] + $mutetime - time ());
2011-01-20 09:24:23 +01:00
}
2012-04-11 18:49:22 +02:00
// Not expired yet
error ( sprintf ( $config [ 'error' ][ 'youaremuted' ], $mute [ 'time' ] + $mutetime - time ()));
} else {
2024-03-14 10:01:13 +01:00
// Already expired
2012-04-11 18:49:22 +02:00
return ;
2011-01-20 09:24:23 +01:00
}
}
2012-04-11 18:49:22 +02:00
}
2024-02-04 14:53:06 +01:00
function _create_antibot ( $board , $thread ) {
global $config , $purged_old_antispam ;
$antibot = new AntiBot ( array ( $board , $thread ));
if ( ! isset ( $purged_old_antispam )) {
$purged_old_antispam = true ;
query ( 'DELETE FROM ``antispam`` WHERE `expires` < UNIX_TIMESTAMP()' ) or error ( db_error ());
}
if ( $thread )
$query = prepare ( 'UPDATE ``antispam`` SET `expires` = UNIX_TIMESTAMP() + :expires WHERE `board` = :board AND `thread` = :thread AND `expires` IS NULL' );
else
$query = prepare ( 'UPDATE ``antispam`` SET `expires` = UNIX_TIMESTAMP() + :expires WHERE `board` = :board AND `thread` IS NULL AND `expires` IS NULL' );
$query -> bindValue ( ':board' , $board );
if ( $thread )
$query -> bindValue ( ':thread' , $thread );
$query -> bindValue ( ':expires' , $config [ 'spam' ][ 'hidden_inputs_expire' ]);
$query -> execute () or error ( db_error ( $query ));
$query = prepare ( 'INSERT INTO ``antispam`` VALUES (:board, :thread, :hash, UNIX_TIMESTAMP(), NULL, 0)' );
$query -> bindValue ( ':board' , $board );
$query -> bindValue ( ':thread' , $thread );
$query -> bindValue ( ':hash' , $antibot -> hash ());
$query -> execute () or error ( db_error ( $query ));
return $antibot ;
}
2021-02-13 21:14:06 +01:00
function checkSpam ( array $extra_salt = array ()) {
global $config , $pdo ;
if ( ! isset ( $_POST [ 'hash' ]))
return true ;
$hash = $_POST [ 'hash' ];
if ( ! empty ( $extra_salt )) {
// create a salted hash of the "extra salt"
$extra_salt = implode ( ':' , $extra_salt );
} else {
$extra_salt = '' ;
}
// Reconsturct the $inputs array
$inputs = array ();
foreach ( $_POST as $name => $value ) {
if ( in_array ( $name , $config [ 'spam' ][ 'valid_inputs' ]))
continue ;
$inputs [ $name ] = $value ;
}
// Sort the inputs in alphabetical order (A-Z)
ksort ( $inputs );
$_hash = '' ;
// Iterate through each input
foreach ( $inputs as $name => $value ) {
$_hash .= $name . '=' . $value ;
}
// Add a salt to the hash
$_hash .= $config [ 'cookies' ][ 'salt' ];
// Use SHA1 for the hash
$_hash = sha1 ( $_hash . $extra_salt );
if ( $hash != $_hash )
return true ;
$query = prepare ( 'SELECT `passed` FROM ``antispam`` WHERE `hash` = :hash' );
$query -> bindValue ( ':hash' , $hash );
$query -> execute () or error ( db_error ( $query ));
if ((( $passed = $query -> fetchColumn ( 0 )) === false ) || ( $passed > $config [ 'spam' ][ 'hidden_inputs_max_pass' ])) {
// there was no database entry for this hash. most likely expired.
return true ;
}
return $hash ;
}
function incrementSpamHash ( $hash ) {
$query = prepare ( 'UPDATE ``antispam`` SET `passed` = `passed` + 1 WHERE `hash` = :hash' );
$query -> bindValue ( ':hash' , $hash );
$query -> execute () or error ( db_error ( $query ));
}
2015-04-01 17:07:24 +02:00
function buildIndex ( $global_api = " yes " ) {
2013-08-03 13:50:25 +02:00
global $board , $config , $build_pages ;
2016-05-08 10:54:30 +02:00
$catalog_api_action = generation_strategy ( 'sb_api' , array ( $board [ 'uri' ]));
2012-04-11 18:49:22 +02:00
2016-05-08 10:54:30 +02:00
$pages = null ;
$antibot = null ;
if ( $config [ 'api' ][ 'enabled' ]) {
$api = new Api ();
$catalog = array ();
2013-08-20 18:53:05 +02:00
}
2013-07-02 19:52:29 +02:00
2013-08-03 13:50:25 +02:00
for ( $page = 1 ; $page <= $config [ 'max_pages' ]; $page ++ ) {
2013-01-23 18:56:06 +01:00
$filename = $board [ 'dir' ] . ( $page == 1 ? $config [ 'file_index' ] : sprintf ( $config [ 'file_page' ], $page ));
2015-04-02 20:29:43 +02:00
$jsonFilename = $board [ 'dir' ] . ( $page - 1 ) . '.json' ; // pages should start from 0
2013-08-04 02:34:59 +02:00
2016-05-08 10:54:30 +02:00
$wont_build_this_page = $config [ 'try_smarter' ] && isset ( $build_pages ) && ! empty ( $build_pages ) && ! in_array ( $page , $build_pages );
if (( ! $config [ 'api' ][ 'enabled' ] || $global_api == " skip " ) && $wont_build_this_page )
2013-08-03 13:50:25 +02:00
continue ;
2016-05-08 10:54:30 +02:00
$action = generation_strategy ( 'sb_board' , array ( $board [ 'uri' ], $page ));
if ( $action == 'rebuild' || $catalog_api_action == 'rebuild' ) {
$content = index ( $page , false , $wont_build_this_page );
2015-04-02 20:29:43 +02:00
if ( ! $content )
break ;
2013-12-28 18:03:49 +01:00
2022-03-31 01:59:21 +02:00
// Tries to avoid rebuilding if the body is the same as the one in cache.
if ( $config [ 'cache' ][ 'enabled' ]) {
$contentHash = md5 ( json_encode ( $content [ 'body' ]));
$contentHashKey = '_index_hashed_' . $board [ 'uri' ] . '_' . $page ;
$cachedHash = cache :: get ( $contentHashKey );
if ( $cachedHash == $contentHash ){
if ( $config [ 'api' ][ 'enabled' ]) {
// this is needed for the thread.json and catalog.json rebuilding below, which includes all pages.
$catalog [ $page - 1 ] = $content [ 'threads' ];
}
continue ;
}
cache :: set ( $contentHashKey , $contentHash , 3600 );
}
2015-04-02 20:29:43 +02:00
// json api
if ( $config [ 'api' ][ 'enabled' ]) {
$threads = $content [ 'threads' ];
$json = json_encode ( $api -> translatePage ( $threads ));
file_write ( $jsonFilename , $json );
2015-04-01 18:11:08 +02:00
2015-04-02 20:29:43 +02:00
$catalog [ $page - 1 ] = $threads ;
2013-01-23 18:56:06 +01:00
2016-05-08 10:54:30 +02:00
if ( $wont_build_this_page ) continue ;
}
2015-04-02 20:29:43 +02:00
if ( $config [ 'try_smarter' ]) {
$antibot = create_antibot ( $board [ 'uri' ], 0 - $page );
$content [ 'current_page' ] = $page ;
}
2016-05-08 10:54:30 +02:00
elseif ( ! $antibot ) {
2016-05-08 10:59:36 +02:00
$antibot = create_antibot ( $board [ 'uri' ]);
2016-05-08 10:54:30 +02:00
}
2015-04-02 20:29:43 +02:00
$antibot -> reset ();
2016-05-08 10:54:30 +02:00
if ( ! $pages ) {
$pages = getPages ();
}
2015-04-02 20:29:43 +02:00
$content [ 'pages' ] = $pages ;
$content [ 'pages' ][ $page - 1 ][ 'selected' ] = true ;
$content [ 'btn' ] = getPageButtons ( $content [ 'pages' ]);
$content [ 'antibot' ] = $antibot ;
2015-04-02 19:36:55 +02:00
2022-08-29 16:50:45 +02:00
file_write ( $filename , Element ( $config [ 'file_board_index' ], $content ));
2015-04-02 20:29:43 +02:00
}
2016-05-08 10:54:30 +02:00
elseif ( $action == 'delete' || $catalog_api_action == 'delete' ) {
2015-04-02 20:29:43 +02:00
file_unlink ( $filename );
file_unlink ( $jsonFilename );
}
2011-02-17 12:03:52 +01:00
}
2013-07-02 19:52:29 +02:00
2016-05-08 10:54:30 +02:00
// $action is an action for our last page
if (( $catalog_api_action == 'rebuild' || $action == 'rebuild' || $action == 'delete' ) && $page < $config [ 'max_pages' ]) {
2012-04-12 16:18:19 +02:00
for (; $page <= $config [ 'max_pages' ]; $page ++ ) {
2011-02-12 07:25:15 +01:00
$filename = $board [ 'dir' ] . ( $page == 1 ? $config [ 'file_index' ] : sprintf ( $config [ 'file_page' ], $page ));
2012-04-11 18:49:22 +02:00
file_unlink ( $filename );
2013-07-02 19:52:29 +02:00
2013-08-20 20:59:20 +02:00
if ( $config [ 'api' ][ 'enabled' ]) {
2013-08-28 18:41:36 +02:00
$jsonFilename = $board [ 'dir' ] . ( $page - 1 ) . '.json' ;
2013-08-20 20:59:20 +02:00
file_unlink ( $jsonFilename );
}
2010-11-02 11:57:33 +01:00
}
}
2013-07-02 19:52:29 +02:00
// json api catalog
2015-04-01 17:07:24 +02:00
if ( $config [ 'api' ][ 'enabled' ] && $global_api != " skip " ) {
2016-05-08 10:54:30 +02:00
if ( $catalog_api_action == 'delete' ) {
2015-04-02 20:29:43 +02:00
$jsonFilename = $board [ 'dir' ] . 'catalog.json' ;
file_unlink ( $jsonFilename );
$jsonFilename = $board [ 'dir' ] . 'threads.json' ;
file_unlink ( $jsonFilename );
}
2016-05-08 10:54:30 +02:00
elseif ( $catalog_api_action == 'rebuild' ) {
2015-04-02 20:29:43 +02:00
$json = json_encode ( $api -> translateCatalog ( $catalog ));
$jsonFilename = $board [ 'dir' ] . 'catalog.json' ;
file_write ( $jsonFilename , $json );
2013-12-28 19:46:50 +01:00
2015-04-02 20:29:43 +02:00
$json = json_encode ( $api -> translateCatalog ( $catalog , true ));
$jsonFilename = $board [ 'dir' ] . 'threads.json' ;
file_write ( $jsonFilename , $json );
}
2013-08-20 18:53:05 +02:00
}
2013-08-29 10:55:25 +02:00
if ( $config [ 'try_smarter' ])
$build_pages = array ();
2012-04-11 18:49:22 +02:00
}
function buildJavascript () {
global $config ;
2013-08-03 13:50:25 +02:00
2012-04-11 18:49:22 +02:00
$stylesheets = array ();
2012-04-12 16:18:19 +02:00
foreach ( $config [ 'stylesheets' ] as $name => $uri ) {
2012-04-11 18:49:22 +02:00
$stylesheets [] = array (
'name' => addslashes ( $name ),
'uri' => addslashes (( ! empty ( $uri ) ? $config [ 'uri_stylesheets' ] : '' ) . $uri ));
}
2013-07-03 06:25:32 +02:00
2012-04-11 18:49:22 +02:00
$script = Element ( 'main.js' , array (
'config' => $config ,
'stylesheets' => $stylesheets
));
2013-08-03 13:50:25 +02:00
2013-07-31 12:54:53 +02:00
// Check if we have translation for the javascripts; if yes, we add it to additional javascripts
list ( $pure_locale ) = explode ( " . " , $config [ 'locale' ]);
if ( file_exists ( $jsloc = " inc/locale/ $pure_locale /LC_MESSAGES/javascript.js " )) {
$script = file_get_contents ( $jsloc ) . " \n \n " . $script ;
}
2013-08-03 13:50:25 +02:00
2012-04-12 16:18:19 +02:00
if ( $config [ 'additional_javascript_compile' ]) {
foreach ( $config [ 'additional_javascript' ] as $file ) {
2012-04-11 18:49:22 +02:00
$script .= file_get_contents ( $file );
2012-03-14 11:44:41 +01:00
}
2011-03-26 16:36:32 +01:00
}
2013-08-03 13:50:25 +02:00
2012-04-12 16:18:19 +02:00
if ( $config [ 'minify_js' ]) {
2012-04-11 18:49:22 +02:00
$script = JSMin :: minify ( $script );
}
2013-08-03 13:50:25 +02:00
2012-04-11 18:49:22 +02:00
file_write ( $config [ 'file_script' ], $script );
}
function checkDNSBL () {
global $config ;
2013-08-03 13:50:25 +02:00
2012-04-12 16:18:19 +02:00
if ( isIPv6 ())
2012-04-11 18:49:22 +02:00
return ; // No IPv6 support yet.
2013-08-03 13:50:25 +02:00
2012-04-12 16:18:19 +02:00
if ( ! isset ( $_SERVER [ 'REMOTE_ADDR' ]))
2012-04-11 18:49:22 +02:00
return ; // Fix your web server configuration
2013-08-03 13:50:25 +02:00
2016-05-08 14:01:55 +02:00
if ( preg_match ( " /^(::(ffff:)?)?(127 \ .|192 \ .168 \ .|10 \ .|172 \ .(1[6-9]|2[0-9]|3[0-1]) \ .|0 \ .|255 \ .)/ " , $_SERVER [ 'REMOTE_ADDR' ]))
return ; // It's pointless to check for local IP addresses in dnsbls, isn't it?
2012-04-12 16:18:19 +02:00
if ( in_array ( $_SERVER [ 'REMOTE_ADDR' ], $config [ 'dnsbl_exceptions' ]))
2012-04-11 18:49:22 +02:00
return ;
2013-08-03 13:50:25 +02:00
2012-04-15 02:21:43 +02:00
$ipaddr = ReverseIPOctets ( $_SERVER [ 'REMOTE_ADDR' ]);
2013-08-03 13:50:25 +02:00
2012-04-12 16:18:19 +02:00
foreach ( $config [ 'dnsbl' ] as $blacklist ) {
2012-04-15 02:21:43 +02:00
if ( ! is_array ( $blacklist ))
2012-04-11 18:49:22 +02:00
$blacklist = array ( $blacklist );
2013-08-03 13:50:25 +02:00
2012-04-15 02:21:43 +02:00
if (( $lookup = str_replace ( '%' , $ipaddr , $blacklist [ 0 ])) == $blacklist [ 0 ])
$lookup = $ipaddr . '.' . $blacklist [ 0 ];
2013-08-03 13:50:25 +02:00
2012-04-12 16:18:19 +02:00
if ( ! $ip = DNS ( $lookup ))
2012-04-11 18:49:22 +02:00
continue ; // not in list
2013-08-03 13:50:25 +02:00
2012-04-11 18:49:22 +02:00
$blacklist_name = isset ( $blacklist [ 2 ]) ? $blacklist [ 2 ] : $blacklist [ 0 ];
2013-08-03 13:50:25 +02:00
2012-04-12 16:18:19 +02:00
if ( ! isset ( $blacklist [ 1 ])) {
2012-04-11 18:49:22 +02:00
// If you're listed at all, you're blocked.
error ( sprintf ( $config [ 'error' ][ 'dnsbl' ], $blacklist_name ));
2012-04-12 16:18:19 +02:00
} elseif ( is_array ( $blacklist [ 1 ])) {
foreach ( $blacklist [ 1 ] as $octet ) {
if ( $ip == $octet || $ip == '127.0.0.' . $octet )
2012-03-17 20:34:34 +01:00
error ( sprintf ( $config [ 'error' ][ 'dnsbl' ], $blacklist_name ));
2011-05-19 09:37:23 +02:00
}
2012-04-12 16:18:19 +02:00
} elseif ( is_callable ( $blacklist [ 1 ])) {
if ( $blacklist [ 1 ]( $ip ))
2012-04-11 18:49:22 +02:00
error ( sprintf ( $config [ 'error' ][ 'dnsbl' ], $blacklist_name ));
} else {
2012-04-12 16:18:19 +02:00
if ( $ip == $blacklist [ 1 ] || $ip == '127.0.0.' . $blacklist [ 1 ])
2012-04-11 18:49:22 +02:00
error ( sprintf ( $config [ 'error' ][ 'dnsbl' ], $blacklist_name ));
2011-05-19 09:37:23 +02:00
}
2011-01-19 02:37:31 +01:00
}
2012-04-11 18:49:22 +02:00
}
2024-02-04 14:53:06 +01:00
function isIPv6 () {
return strstr ( $_SERVER [ 'REMOTE_ADDR' ], ':' ) !== false ;
}
function ReverseIPOctets ( $ip ) {
return implode ( '.' , array_reverse ( explode ( '.' , $ip )));
}
2012-04-11 18:49:22 +02:00
function wordfilters ( & $body ) {
global $config ;
2013-08-03 13:50:25 +02:00
2012-04-12 16:18:19 +02:00
foreach ( $config [ 'wordfilters' ] as $filter ) {
if ( isset ( $filter [ 2 ]) && $filter [ 2 ]) {
2013-08-12 17:53:23 +02:00
if ( is_callable ( $filter [ 1 ]))
$body = preg_replace_callback ( $filter [ 0 ], $filter [ 1 ], $body );
else
$body = preg_replace ( $filter [ 0 ], $filter [ 1 ], $body );
2012-04-11 18:49:22 +02:00
} else {
$body = str_ireplace ( $filter [ 0 ], $filter [ 1 ], $body );
}
2011-02-17 10:32:11 +01:00
}
2012-04-11 18:49:22 +02:00
}
function quote ( $body , $quote = true ) {
global $config ;
2013-08-03 13:50:25 +02:00
2012-04-11 18:49:22 +02:00
$body = str_replace ( '<br/>' , " \n " , $body );
2013-08-03 13:50:25 +02:00
2012-04-11 18:49:22 +02:00
$body = strip_tags ( $body );
2013-08-03 13:50:25 +02:00
2012-04-11 18:49:22 +02:00
$body = preg_replace ( " /(^| \n )/ " , '$1>' , $body );
2013-08-03 13:50:25 +02:00
2012-04-11 18:49:22 +02:00
$body .= " \n " ;
2013-08-03 13:50:25 +02:00
2012-04-12 16:18:19 +02:00
if ( $config [ 'minify_html' ])
2012-04-11 18:49:22 +02:00
$body = str_replace ( " \n " , '
' , $body );
2013-08-03 13:50:25 +02:00
2012-04-11 18:49:22 +02:00
return $body ;
}
function markup_url ( $matches ) {
2013-01-16 19:49:26 +01:00
global $config , $markup_urls ;
2013-08-03 13:50:25 +02:00
2012-04-11 18:49:22 +02:00
$url = $matches [ 1 ];
$after = $matches [ 2 ];
2013-08-03 13:50:25 +02:00
2012-04-11 18:49:22 +02:00
$markup_urls [] = $url ;
2013-08-03 13:50:25 +02:00
2013-10-04 12:13:58 +02:00
$link = ( object ) array (
2014-03-13 22:39:32 +01:00
'href' => $config [ 'link_prefix' ] . $url ,
2013-10-04 12:13:58 +02:00
'text' => $url ,
'rel' => 'nofollow' ,
'target' => '_blank' ,
);
2024-03-14 10:01:13 +01:00
2013-10-04 12:13:58 +02:00
event ( 'markup-url' , $link );
$link = ( array ) $link ;
$parts = array ();
foreach ( $link as $attr => $value ) {
if ( $attr == 'text' || $attr == 'after' )
continue ;
2014-01-17 00:30:04 +01:00
$parts [] = $attr . '="' . $value . '"' ;
2013-10-04 12:13:58 +02:00
}
if ( isset ( $link [ 'after' ]))
$after = $link [ 'after' ] . $after ;
2014-01-17 00:30:04 +01:00
return '<a ' . implode ( ' ' , $parts ) . '>' . $link [ 'text' ] . '</a>' . $after ;
2012-04-11 18:49:22 +02:00
}
function unicodify ( $body ) {
$body = str_replace ( '...' , '…' , $body );
$body = str_replace ( '<--' , '←' , $body );
$body = str_replace ( '-->' , '→' , $body );
2013-08-03 13:50:25 +02:00
2012-04-11 18:49:22 +02:00
// En and em- dashes are rendered exactly the same in
// most monospace fonts (they look the same in code
// editors).
2013-01-23 17:37:15 +01:00
$body = str_replace ( '---' , '—' , $body ); // em dash
2013-02-12 18:25:56 +01:00
$body = str_replace ( '--' , '–' , $body ); // en dash
2013-08-03 13:50:25 +02:00
2012-04-11 18:49:22 +02:00
return $body ;
}
2013-08-16 13:08:01 +02:00
function extract_modifiers ( $body ) {
$modifiers = array ();
2024-03-14 10:01:13 +01:00
2014-08-09 05:12:36 +02:00
if ( preg_match_all ( '@<tinyboard ([\w\s]+)>(.*?)</tinyboard>@us' , $body , $matches , PREG_SET_ORDER )) {
2013-08-16 13:08:01 +02:00
foreach ( $matches as $match ) {
2013-08-16 13:25:56 +02:00
if ( preg_match ( '/^escape /' , $match [ 1 ]))
continue ;
$modifiers [ $match [ 1 ]] = html_entity_decode ( $match [ 2 ]);
2013-08-16 13:08:01 +02:00
}
}
2024-03-14 10:01:13 +01:00
2013-08-16 13:08:01 +02:00
return $modifiers ;
}
2014-12-24 15:08:19 +01:00
function remove_modifiers ( $body ) {
return preg_replace ( '@<tinyboard ([\w\s]+)>(.+?)</tinyboard>@usm' , '' , $body );
}
function markup ( & $body , $track_cites = false , $op = false ) {
2012-04-11 18:49:22 +02:00
global $board , $config , $markup_urls ;
2024-03-14 10:01:13 +01:00
2013-08-16 13:25:56 +02:00
$modifiers = extract_modifiers ( $body );
2024-03-14 10:01:13 +01:00
2013-08-17 09:53:39 +02:00
$body = preg_replace ( '@<tinyboard (?!escape )([\w\s]+)>(.+?)</tinyboard>@us' , '' , $body );
2013-08-17 09:56:38 +02:00
$body = preg_replace ( '@<(tinyboard) escape ([\w\s]+)>@i' , '<$1 $2>' , $body );
2024-03-14 10:01:13 +01:00
2013-08-17 20:49:56 +02:00
if ( isset ( $modifiers [ 'raw html' ]) && $modifiers [ 'raw html' ] == '1' ) {
return array ();
}
2013-08-03 13:50:25 +02:00
2012-04-11 18:49:22 +02:00
$body = str_replace ( " \r " , '' , $body );
$body = utf8tohtml ( $body );
2013-08-03 13:50:25 +02:00
2013-07-31 04:08:56 +02:00
if ( mysql_version () < 50503 )
$body = mb_encode_numericentity ( $body , array ( 0x010000 , 0xffffff , 0 , 0xffffff ), 'UTF-8' );
2013-08-03 13:50:25 +02:00
2015-04-12 00:32:58 +02:00
if ( $config [ 'markup_code' ]) {
$code_markup = array ();
$body = preg_replace_callback ( $config [ 'markup_code' ], function ( $matches ) use ( & $code_markup ) {
$d = count ( $code_markup );
$code_markup [] = $matches ;
return " <code $d > " ;
}, $body );
}
2012-04-12 16:18:19 +02:00
foreach ( $config [ 'markup' ] as $markup ) {
if ( is_string ( $markup [ 1 ])) {
2012-04-11 18:49:22 +02:00
$body = preg_replace ( $markup [ 0 ], $markup [ 1 ], $body );
2012-04-12 16:18:19 +02:00
} elseif ( is_callable ( $markup [ 1 ])) {
2012-04-11 18:49:22 +02:00
$body = preg_replace_callback ( $markup [ 0 ], $markup [ 1 ], $body );
2011-04-06 11:18:36 +02:00
}
}
2013-08-03 13:50:25 +02:00
2012-04-12 16:18:19 +02:00
if ( $config [ 'markup_urls' ]) {
2012-04-11 18:49:22 +02:00
$markup_urls = array ();
2013-08-03 13:50:25 +02:00
2012-04-11 18:49:22 +02:00
$body = preg_replace_callback (
'/((?:https?:\/\/|ftp:\/\/|irc:\/\/)[^\s<>()"]+?(?:\([^\s<>()"]*?\)[^\s<>()"]*?)*)((?:\s|<|>|"|\.||\]|!|\?|,|,|")*(?:[\s<>()"]|$))/' ,
'markup_url' ,
$body ,
- 1 ,
$num_links );
2013-08-03 13:50:25 +02:00
2012-04-12 16:18:19 +02:00
if ( $num_links > $config [ 'max_links' ])
2012-04-11 18:49:22 +02:00
error ( $config [ 'error' ][ 'toomanylinks' ]);
2011-04-12 11:56:37 +02:00
}
2021-02-16 17:56:54 +01:00
2013-08-29 13:24:38 +02:00
if ( $config [ 'markup_repair_tidy' ])
$body = str_replace ( ' ' , ' ' , $body );
2013-08-03 13:50:25 +02:00
2012-04-12 16:18:19 +02:00
if ( $config [ 'auto_unicode' ]) {
2012-04-11 18:49:22 +02:00
$body = unicodify ( $body );
2013-08-03 13:50:25 +02:00
2012-05-08 15:59:54 +02:00
if ( $config [ 'markup_urls' ]) {
foreach ( $markup_urls as & $url ) {
$body = str_replace ( unicodify ( $url ), $url , $body );
}
2012-04-11 18:49:22 +02:00
}
2012-01-30 19:57:25 +01:00
}
2013-08-03 13:50:25 +02:00
2012-04-11 18:49:22 +02:00
$tracked_cites = array ();
2013-08-03 13:50:25 +02:00
2012-04-11 18:49:22 +02:00
// Cites
2023-01-12 11:16:15 +01:00
if ( isset ( $board ) && preg_match_all ( '/(^|[\s(])>>(\d+?)((?=[\s,.)?!])|$)/m' , $body , $cites , PREG_SET_ORDER | PREG_OFFSET_CAPTURE )) {
2012-04-12 16:18:19 +02:00
if ( count ( $cites [ 0 ]) > $config [ 'max_cites' ]) {
2012-04-11 18:49:22 +02:00
error ( $config [ 'error' ][ 'toomanycites' ]);
2010-11-02 11:57:33 +01:00
}
2013-08-03 13:50:25 +02:00
2013-07-26 17:01:13 +02:00
$skip_chars = 0 ;
2013-07-31 09:12:06 +02:00
$body_tmp = $body ;
2021-02-16 17:56:54 +01:00
2013-08-29 04:52:31 +02:00
$search_cites = array ();
foreach ( $cites as $matches ) {
$search_cites [] = '`id` = ' . $matches [ 2 ][ 0 ];
}
$search_cites = array_unique ( $search_cites );
2024-03-14 10:01:13 +01:00
2013-08-29 04:52:31 +02:00
$query = query ( sprintf ( 'SELECT `thread`, `id` FROM ``posts_%s`` WHERE ' .
implode ( ' OR ' , $search_cites ), $board [ 'uri' ])) or error ( db_error ());
2021-02-16 17:56:54 +01:00
2013-08-29 04:52:31 +02:00
$cited_posts = array ();
while ( $cited = $query -> fetch ( PDO :: FETCH_ASSOC )) {
$cited_posts [ $cited [ 'id' ]] = $cited [ 'thread' ] ? $cited [ 'thread' ] : false ;
}
2024-03-14 10:01:13 +01:00
2013-07-26 17:01:13 +02:00
foreach ( $cites as $matches ) {
$cite = $matches [ 2 ][ 0 ];
2013-08-03 13:50:25 +02:00
2013-07-31 09:13:51 +02:00
// preg_match_all is not multibyte-safe
2013-07-31 09:12:06 +02:00
foreach ( $matches as & $match ) {
$match [ 1 ] = mb_strlen ( substr ( $body_tmp , 0 , $match [ 1 ]));
}
2013-08-03 13:50:25 +02:00
2013-08-29 04:52:31 +02:00
if ( isset ( $cited_posts [ $cite ])) {
2015-02-19 01:55:03 +01:00
$replacement = '<a onclick="highlightReply(\'' . $cite . '\', event);" href="' .
2013-08-29 04:52:31 +02:00
$config [ 'root' ] . $board [ 'dir' ] . $config [ 'dir' ][ 'res' ] .
2015-03-10 14:19:36 +01:00
link_for ( array ( 'id' => $cite , 'thread' => $cited_posts [ $cite ])) . '#' . $cite . '">' .
2013-08-29 04:52:31 +02:00
'>>' . $cite .
'</a>' ;
2013-08-03 13:50:25 +02:00
2013-07-31 09:12:06 +02:00
$body = mb_substr_replace ( $body , $matches [ 1 ][ 0 ] . $replacement . $matches [ 3 ][ 0 ], $matches [ 0 ][ 1 ] + $skip_chars , mb_strlen ( $matches [ 0 ][ 0 ]));
$skip_chars += mb_strlen ( $matches [ 1 ][ 0 ] . $replacement . $matches [ 3 ][ 0 ]) - mb_strlen ( $matches [ 0 ][ 0 ]);
2013-08-03 13:50:25 +02:00
2012-04-12 16:18:19 +02:00
if ( $track_cites && $config [ 'track_cites' ])
2013-08-29 04:52:31 +02:00
$tracked_cites [] = array ( $board [ 'uri' ], $cite );
2011-04-06 11:18:36 +02:00
}
}
2012-04-11 18:49:22 +02:00
}
2013-08-03 13:50:25 +02:00
2012-04-11 18:49:22 +02:00
// Cross-board linking
2023-01-12 11:16:15 +01:00
if ( preg_match_all ( '/(^|[\s(])>>>\/(' . $config [ 'board_regex' ] . 'f?)\/(\d+)?((?=[\s,.)?!])|$)/um' , $body , $cites , PREG_SET_ORDER | PREG_OFFSET_CAPTURE )) {
2012-04-12 16:18:19 +02:00
if ( count ( $cites [ 0 ]) > $config [ 'max_cites' ]) {
2012-04-11 18:49:22 +02:00
error ( $config [ 'error' ][ 'toomanycross' ]);
}
2013-08-03 13:50:25 +02:00
2013-07-26 17:01:13 +02:00
$skip_chars = 0 ;
2013-07-31 09:12:06 +02:00
$body_tmp = $body ;
2021-02-16 17:56:54 +01:00
2013-08-29 05:31:02 +02:00
if ( isset ( $cited_posts )) {
// Carry found posts from local board >>X links
foreach ( $cited_posts as $cite => $thread ) {
$cited_posts [ $cite ] = $config [ 'root' ] . $board [ 'dir' ] . $config [ 'dir' ][ 'res' ] .
( $thread ? $thread : $cite ) . '.html#' . $cite ;
}
2021-02-16 17:56:54 +01:00
2013-08-29 05:31:02 +02:00
$cited_posts = array (
$board [ 'uri' ] => $cited_posts
);
} else
$cited_posts = array ();
2024-03-14 10:01:13 +01:00
2013-08-29 05:31:02 +02:00
$crossboard_indexes = array ();
$search_cites_boards = array ();
2024-03-14 10:01:13 +01:00
2013-08-29 05:31:02 +02:00
foreach ( $cites as $matches ) {
$_board = $matches [ 2 ][ 0 ];
$cite = @ $matches [ 3 ][ 0 ];
2024-03-14 10:01:13 +01:00
2013-08-29 05:31:02 +02:00
if ( ! isset ( $search_cites_boards [ $_board ]))
$search_cites_boards [ $_board ] = array ();
$search_cites_boards [ $_board ][] = $cite ;
}
2024-03-14 10:01:13 +01:00
2013-08-29 05:31:02 +02:00
$tmp_board = $board [ 'uri' ];
2024-03-14 10:01:13 +01:00
2013-08-29 05:31:02 +02:00
foreach ( $search_cites_boards as $_board => $search_cites ) {
$clauses = array ();
foreach ( $search_cites as $cite ) {
if ( ! $cite || isset ( $cited_posts [ $_board ][ $cite ]))
continue ;
$clauses [] = '`id` = ' . $cite ;
}
$clauses = array_unique ( $clauses );
2024-03-14 10:01:13 +01:00
2013-08-29 05:31:02 +02:00
if ( $board [ 'uri' ] != $_board ) {
if ( ! openBoard ( $_board ))
continue ; // Unknown board
}
2024-03-14 10:01:13 +01:00
2013-08-29 05:31:02 +02:00
if ( ! empty ( $clauses )) {
$cited_posts [ $_board ] = array ();
2024-03-14 10:01:13 +01:00
2015-03-10 14:16:27 +01:00
$query = query ( sprintf ( 'SELECT `thread`, `id`, `slug` FROM ``posts_%s`` WHERE ' .
2013-08-29 05:31:02 +02:00
implode ( ' OR ' , $clauses ), $board [ 'uri' ])) or error ( db_error ());
2024-03-14 10:01:13 +01:00
2013-08-29 05:31:02 +02:00
while ( $cite = $query -> fetch ( PDO :: FETCH_ASSOC )) {
$cited_posts [ $_board ][ $cite [ 'id' ]] = $config [ 'root' ] . $board [ 'dir' ] . $config [ 'dir' ][ 'res' ] .
2015-03-10 14:16:27 +01:00
link_for ( $cite ) . '#' . $cite [ 'id' ];
2013-08-29 05:31:02 +02:00
}
}
2024-03-14 10:01:13 +01:00
2013-08-29 05:31:02 +02:00
$crossboard_indexes [ $_board ] = $config [ 'root' ] . $board [ 'dir' ] . $config [ 'file_index' ];
}
2024-03-14 10:01:13 +01:00
2013-08-29 05:31:02 +02:00
// Restore old board
if ( $board [ 'uri' ] != $tmp_board )
openBoard ( $tmp_board );
2013-08-03 13:50:25 +02:00
2013-07-26 17:01:13 +02:00
foreach ( $cites as $matches ) {
$_board = $matches [ 2 ][ 0 ];
$cite = @ $matches [ 3 ][ 0 ];
2013-08-03 13:50:25 +02:00
2013-07-31 09:13:51 +02:00
// preg_match_all is not multibyte-safe
2013-07-31 09:12:06 +02:00
foreach ( $matches as & $match ) {
$match [ 1 ] = mb_strlen ( substr ( $body_tmp , 0 , $match [ 1 ]));
}
2013-08-03 13:50:25 +02:00
2013-08-29 05:31:02 +02:00
if ( $cite ) {
if ( isset ( $cited_posts [ $_board ][ $cite ])) {
$link = $cited_posts [ $_board ][ $cite ];
2024-03-14 10:01:13 +01:00
2013-08-29 05:31:02 +02:00
$replacement = '<a ' .
( $_board == $board [ 'uri' ] ?
2015-02-19 01:55:03 +01:00
'onclick="highlightReply(\'' . $cite . '\', event);" '
2013-08-29 05:31:02 +02:00
: '' ) . 'href="' . $link . '">' .
'>>>/' . $_board . '/' . $cite .
'</a>' ;
2013-07-31 09:12:06 +02:00
$body = mb_substr_replace ( $body , $matches [ 1 ][ 0 ] . $replacement . $matches [ 4 ][ 0 ], $matches [ 0 ][ 1 ] + $skip_chars , mb_strlen ( $matches [ 0 ][ 0 ]));
2013-07-26 17:01:13 +02:00
$skip_chars += mb_strlen ( $matches [ 1 ][ 0 ] . $replacement . $matches [ 4 ][ 0 ]) - mb_strlen ( $matches [ 0 ][ 0 ]);
2013-08-29 05:31:02 +02:00
if ( $track_cites && $config [ 'track_cites' ])
$tracked_cites [] = array ( $_board , $cite );
2011-04-06 11:18:36 +02:00
}
2013-08-29 05:31:02 +02:00
} elseif ( isset ( $crossboard_indexes [ $_board ])) {
$replacement = '<a href="' . $crossboard_indexes [ $_board ] . '">' .
'>>>/' . $_board . '/' .
'</a>' ;
$body = mb_substr_replace ( $body , $matches [ 1 ][ 0 ] . $replacement . $matches [ 4 ][ 0 ], $matches [ 0 ][ 1 ] + $skip_chars , mb_strlen ( $matches [ 0 ][ 0 ]));
$skip_chars += mb_strlen ( $matches [ 1 ][ 0 ] . $replacement . $matches [ 4 ][ 0 ]) - mb_strlen ( $matches [ 0 ][ 0 ]);
2010-11-02 11:57:33 +01:00
}
}
}
2024-03-14 10:01:13 +01:00
2013-08-29 05:31:02 +02:00
$tracked_cites = array_unique ( $tracked_cites , SORT_REGULAR );
2013-08-03 13:50:25 +02:00
2012-04-11 18:49:22 +02:00
$body = preg_replace ( " /^ \ s*>.* $ /m " , '<span class="quote">$0</span>' , $body );
2013-08-03 13:50:25 +02:00
2012-04-12 16:18:19 +02:00
if ( $config [ 'strip_superfluous_returns' ])
2012-04-11 18:49:22 +02:00
$body = preg_replace ( '/\s+$/' , '' , $body );
2013-08-03 13:50:25 +02:00
2012-04-11 18:49:22 +02:00
$body = preg_replace ( " / \n / " , '<br/>' , $body );
2015-04-12 00:32:58 +02:00
// Fix code markup
if ( $config [ 'markup_code' ]) {
foreach ( $code_markup as $id => $val ) {
$code = isset ( $val [ 2 ]) ? $val [ 2 ] : $val [ 1 ];
$code_lang = isset ( $val [ 2 ]) ? $val [ 1 ] : " " ;
$code = " <pre class='code lang- $code_lang '> " . str_replace ( array ( " \n " , " \t " ), array ( " " , " 	 " ), htmlspecialchars ( $code )) . " </pre> " ;
$body = str_replace ( " <code $id > " , $code , $body );
}
}
2013-08-29 07:56:36 +02:00
if ( $config [ 'markup_repair_tidy' ]) {
$tidy = new tidy ();
2013-08-31 05:23:29 +02:00
$body = str_replace ( " \t " , '	' , $body );
2013-08-29 13:24:38 +02:00
$body = $tidy -> repairString ( $body , array (
2013-08-29 13:05:03 +02:00
'doctype' => 'omit' ,
2019-11-12 10:34:23 +01:00
'bare' => $config [ 'markup_repair_tidy_bare' ],
2013-08-29 13:05:03 +02:00
'literal-attributes' => true ,
'indent' => false ,
'show-body-only' => true ,
'wrap' => 0 ,
'output-bom' => false ,
2013-08-29 13:24:38 +02:00
'output-html' => true ,
'newline' => 'LF' ,
'quiet' => true ,
2013-08-29 13:05:03 +02:00
), 'utf8' );
2013-08-29 07:56:36 +02:00
$body = str_replace ( " \n " , '' , $body );
}
2015-04-12 00:32:58 +02:00
2013-08-31 05:23:29 +02:00
// replace tabs with 8 spaces
2014-04-27 15:48:47 +02:00
$body = str_replace ( " \t " , ' ' , $body );
2015-04-12 00:32:58 +02:00
2012-04-11 18:49:22 +02:00
return $tracked_cites ;
}
2010-11-04 15:39:02 +01:00
2013-08-01 00:59:54 +02:00
function escape_markup_modifiers ( $string ) {
2013-08-17 09:56:38 +02:00
return preg_replace ( '@<(tinyboard) ([\w\s]+)>@mi' , '<$1 escape $2>' , $string );
2013-08-01 00:59:54 +02:00
}
2022-09-15 20:42:00 +02:00
function defined_flags_accumulate ( $desired_flags ) {
$output_flags = 0x0 ;
foreach ( $desired_flags as $flagname ) {
if ( defined ( $flagname )) {
$flag = constant ( $flagname );
if ( gettype ( $flag ) != 'integer' )
error ( sprintf ( $config [ 'error' ][ 'flag_wrongtype' ], $flagname ));
$output_flags |= $flag ;
} else {
if ( $config [ 'deprecation_errors' ])
error ( sprintf ( $config [ 'error' ][ 'flag_undefined' ], $flagname ));
}
}
return $output_flags ;
}
2012-04-11 18:49:22 +02:00
function utf8tohtml ( $utf8 ) {
2023-08-03 03:04:49 +02:00
$flags = defined_flags_accumulate ([ 'ENT_NOQUOTES' , 'ENT_SUBSTITUTE' , 'ENT_DISALLOWED' ]);
2024-05-11 00:28:51 +02:00
return $utf8 ? htmlspecialchars ( $utf8 , $flags , 'UTF-8' ) : '' ;
2012-04-11 18:49:22 +02:00
}
2010-11-04 15:39:02 +01:00
2013-07-29 02:33:26 +02:00
function ordutf8 ( $string , & $offset ) {
2024-03-14 10:01:13 +01:00
$code = ord ( substr ( $string , $offset , 1 ));
2013-07-29 02:33:26 +02:00
if ( $code >= 128 ) { // otherwise 0xxxxxxx
if ( $code < 224 )
$bytesnumber = 2 ; // 110xxxxx
else if ( $code < 240 )
$bytesnumber = 3 ; // 1110xxxx
else if ( $code < 248 )
$bytesnumber = 4 ; // 11110xxx
$codetemp = $code - 192 - ( $bytesnumber > 2 ? 32 : 0 ) - ( $bytesnumber > 3 ? 16 : 0 );
for ( $i = 2 ; $i <= $bytesnumber ; $i ++ ) {
$offset ++ ;
$code2 = ord ( substr ( $string , $offset , 1 )) - 128 ; //10xxxxxx
$codetemp = $codetemp * 64 + $code2 ;
}
$code = $codetemp ;
}
$offset += 1 ;
if ( $offset >= strlen ( $string ))
$offset = - 1 ;
return $code ;
}
2023-03-28 17:01:12 +02:00
// Limit Non_Spacing_Mark and Enclosing_Mark characters
2013-07-29 02:33:26 +02:00
function strip_combining_chars ( $str ) {
2023-03-28 17:01:12 +02:00
global $config ;
$limit = strval ( $config [ 'max_combining_chars' ] + 1 );
return preg_replace ( '/(\p{Me}|\p{Mn}){' . $limit . ',}/u' , '' , $str );
2013-07-29 02:33:26 +02:00
}
function buildThread ( $id , $return = false , $mod = false ) {
2013-08-03 13:50:25 +02:00
global $board , $config , $build_pages ;
2012-04-11 18:49:22 +02:00
$id = round ( $id );
2013-08-03 13:50:25 +02:00
2012-04-12 16:18:19 +02:00
if ( event ( 'build-thread' , $id ))
2012-04-11 18:49:22 +02:00
return ;
2013-08-03 13:50:25 +02:00
2012-04-12 16:18:19 +02:00
if ( $config [ 'cache' ][ 'enabled' ] && ! $mod ) {
2012-04-11 18:49:22 +02:00
// Clear cache
cache :: delete ( " thread_index_ { $board [ 'uri' ] } _ { $id } " );
cache :: delete ( " thread_ { $board [ 'uri' ] } _ { $id } " );
2011-04-13 12:02:58 +02:00
}
2013-08-03 13:50:25 +02:00
2015-04-06 18:59:33 +02:00
if ( $config [ 'try_smarter' ] && ! $mod )
$build_pages [] = thread_find_page ( $id );
2016-05-08 10:54:30 +02:00
$action = generation_strategy ( 'sb_thread' , array ( $board [ 'uri' ], $id ));
if ( $action == 'rebuild' || $return || $mod ) {
2015-04-02 20:30:57 +02:00
$query = prepare ( sprintf ( " SELECT * FROM ``posts_%s`` WHERE (`thread` IS NULL AND `id` = :id) OR `thread` = :id ORDER BY `thread`,`id` " , $board [ 'uri' ]));
$query -> bindValue ( ':id' , $id , PDO :: PARAM_INT );
$query -> execute () or error ( db_error ( $query ));
2013-08-03 13:50:25 +02:00
2015-04-02 20:30:57 +02:00
while ( $post = $query -> fetch ( PDO :: FETCH_ASSOC )) {
if ( ! isset ( $thread )) {
$thread = new Thread ( $post , $mod ? '?/' : $config [ 'root' ], $mod );
} else {
$thread -> add ( new Post ( $post , $mod ? '?/' : $config [ 'root' ], $mod ));
}
2010-11-02 11:57:33 +01:00
}
2013-08-03 13:50:25 +02:00
2015-04-02 20:30:57 +02:00
// Check if any posts were found
if ( ! isset ( $thread ))
error ( $config [ 'error' ][ 'nonexistant' ]);
2024-03-14 10:01:13 +01:00
2015-04-02 20:30:57 +02:00
$hasnoko50 = $thread -> postCount () >= $config [ 'noko50_min' ];
$antibot = $mod || $return ? false : create_antibot ( $board [ 'uri' ], $id );
2013-08-03 13:50:25 +02:00
2022-08-29 16:50:45 +02:00
$body = Element ( $config [ 'file_thread' ], array (
2015-04-02 20:30:57 +02:00
'board' => $board ,
'thread' => $thread ,
'body' => $thread -> build (),
'config' => $config ,
'id' => $id ,
'mod' => $mod ,
'hasnoko50' => $hasnoko50 ,
'isnoko50' => false ,
'antibot' => $antibot ,
'boardlist' => createBoardlist ( $mod ),
'return' => ( $mod ? '?' . $board [ 'url' ] . $config [ 'file_index' ] : $config [ 'root' ] . $board [ 'dir' ] . $config [ 'file_index' ])
));
// json api
2016-05-08 10:54:30 +02:00
if ( $config [ 'api' ][ 'enabled' ] && ! $mod ) {
2015-04-02 20:30:57 +02:00
$api = new Api ();
$json = json_encode ( $api -> translateThread ( $thread ));
$jsonFilename = $board [ 'dir' ] . $config [ 'dir' ][ 'res' ] . $id . '.json' ;
file_write ( $jsonFilename , $json );
}
}
2016-05-08 10:54:30 +02:00
elseif ( $action == 'delete' ) {
2013-08-21 14:43:35 +02:00
$jsonFilename = $board [ 'dir' ] . $config [ 'dir' ][ 'res' ] . $id . '.json' ;
2015-04-02 20:30:57 +02:00
file_unlink ( $jsonFilename );
2013-08-20 18:53:05 +02:00
}
2013-08-11 23:49:29 +02:00
2016-05-08 10:54:30 +02:00
if ( $action == 'delete' && ! $return && ! $mod ) {
2015-04-02 20:30:57 +02:00
$noko50fn = $board [ 'dir' ] . $config [ 'dir' ][ 'res' ] . link_for ( array ( 'id' => $id ), true );
file_unlink ( $noko50fn );
file_unlink ( $board [ 'dir' ] . $config [ 'dir' ][ 'res' ] . link_for ( array ( 'id' => $id )));
2016-05-08 10:54:30 +02:00
} elseif ( $return ) {
2012-04-11 18:49:22 +02:00
return $body ;
2016-05-08 10:54:30 +02:00
} elseif ( $action == 'rebuild' ) {
2015-03-10 12:48:59 +01:00
$noko50fn = $board [ 'dir' ] . $config [ 'dir' ][ 'res' ] . link_for ( $thread , true );
2013-08-10 23:16:30 +02:00
if ( $hasnoko50 || file_exists ( $noko50fn )) {
2013-08-28 01:44:59 +02:00
buildThread50 ( $id , $return , $mod , $thread , $antibot );
2013-08-10 23:16:30 +02:00
}
2015-03-10 12:48:59 +01:00
file_write ( $board [ 'dir' ] . $config [ 'dir' ][ 'res' ] . link_for ( $thread ), $body );
2013-08-10 23:16:30 +02:00
}
}
2013-08-28 01:44:59 +02:00
function buildThread50 ( $id , $return = false , $mod = false , $thread = null , $antibot = false ) {
2013-08-10 23:16:30 +02:00
global $board , $config , $build_pages ;
$id = round ( $id );
2024-03-14 10:01:13 +01:00
2013-08-28 01:44:59 +02:00
if ( $antibot )
$antibot -> reset ();
2024-03-14 10:01:13 +01:00
2013-08-10 23:16:30 +02:00
if ( ! $thread ) {
$query = prepare ( sprintf ( " SELECT * FROM ``posts_%s`` WHERE (`thread` IS NULL AND `id` = :id) OR `thread` = :id ORDER BY `thread`,`id` DESC LIMIT :limit " , $board [ 'uri' ]));
$query -> bindValue ( ':id' , $id , PDO :: PARAM_INT );
$query -> bindValue ( ':limit' , $config [ 'noko50_count' ] + 1 , PDO :: PARAM_INT );
$query -> execute () or error ( db_error ( $query ));
2024-03-14 10:01:13 +01:00
2013-08-10 23:16:30 +02:00
$num_images = 0 ;
while ( $post = $query -> fetch ( PDO :: FETCH_ASSOC )) {
if ( ! isset ( $thread )) {
2013-08-16 16:09:37 +02:00
$thread = new Thread ( $post , $mod ? '?/' : $config [ 'root' ], $mod );
2013-08-10 23:16:30 +02:00
} else {
2014-05-02 19:32:18 +02:00
if ( $post [ 'files' ])
$num_images += $post [ 'num_files' ];
2024-03-14 10:01:13 +01:00
2013-08-16 16:10:18 +02:00
$thread -> add ( new Post ( $post , $mod ? '?/' : $config [ 'root' ], $mod ));
2013-08-10 23:16:30 +02:00
}
}
// Check if any posts were found
if ( ! isset ( $thread ))
error ( $config [ 'error' ][ 'nonexistant' ]);
if ( $query -> rowCount () == $config [ 'noko50_count' ] + 1 ) {
2014-06-14 18:33:55 +02:00
$count = prepare ( sprintf ( " SELECT COUNT(`id`) as `num` FROM ``posts_%s`` WHERE `thread` = :thread UNION ALL
SELECT SUM ( `num_files` ) FROM `` posts_ % s `` WHERE `files` IS NOT NULL AND `thread` = : thread " , $board['uri'] , $board['uri'] ));
2013-08-10 23:16:30 +02:00
$count -> bindValue ( ':thread' , $id , PDO :: PARAM_INT );
$count -> execute () or error ( db_error ( $count ));
2024-03-14 10:01:13 +01:00
2013-08-10 23:16:30 +02:00
$c = $count -> fetch ();
$thread -> omitted = $c [ 'num' ] - $config [ 'noko50_count' ];
2024-03-14 10:01:13 +01:00
2013-08-10 23:16:30 +02:00
$c = $count -> fetch ();
$thread -> omitted_images = $c [ 'num' ] - $num_images ;
}
$thread -> posts = array_reverse ( $thread -> posts );
} else {
$allPosts = $thread -> posts ;
$thread -> posts = array_slice ( $allPosts , - $config [ 'noko50_count' ]);
$thread -> omitted += count ( $allPosts ) - count ( $thread -> posts );
foreach ( $allPosts as $index => $post ) {
if ( $index == count ( $allPosts ) - count ( $thread -> posts ))
2024-03-14 10:01:13 +01:00
break ;
2014-05-02 18:58:21 +02:00
if ( $post -> files )
2014-05-02 19:32:18 +02:00
$thread -> omitted_images += $post -> num_files ;
2013-08-10 23:16:30 +02:00
}
}
2012-08-30 17:35:27 +02:00
2024-03-14 10:01:13 +01:00
$hasnoko50 = $thread -> postCount () >= $config [ 'noko50_min' ];
2013-08-10 23:16:30 +02:00
2022-08-29 16:50:45 +02:00
$body = Element ( $config [ 'file_thread' ], array (
2013-08-10 23:16:30 +02:00
'board' => $board ,
'thread' => $thread ,
'body' => $thread -> build ( false , true ),
'config' => $config ,
'id' => $id ,
'mod' => $mod ,
'hasnoko50' => $hasnoko50 ,
'isnoko50' => true ,
2013-08-28 01:44:59 +02:00
'antibot' => $mod ? false : ( $antibot ? $antibot : create_antibot ( $board [ 'uri' ], $id )),
2013-08-10 23:16:30 +02:00
'boardlist' => createBoardlist ( $mod ),
'return' => ( $mod ? '?' . $board [ 'url' ] . $config [ 'file_index' ] : $config [ 'root' ] . $board [ 'dir' ] . $config [ 'file_index' ])
2024-03-14 10:01:13 +01:00
));
2013-08-10 23:16:30 +02:00
if ( $return ) {
return $body ;
} else {
2015-03-10 12:48:59 +01:00
file_write ( $board [ 'dir' ] . $config [ 'dir' ][ 'res' ] . link_for ( $thread , true ), $body );
2013-08-10 23:16:30 +02:00
}
2012-04-11 18:49:22 +02:00
}
2024-02-04 14:53:06 +01:00
function rrmdir ( $dir ) {
if ( is_dir ( $dir )) {
$objects = scandir ( $dir );
foreach ( $objects as $object ) {
if ( $object != " . " && $object != " .. " ) {
if ( filetype ( $dir . " / " . $object ) == " dir " )
rrmdir ( $dir . " / " . $object );
else
file_unlink ( $dir . " / " . $object );
}
}
reset ( $objects );
rmdir ( $dir );
}
}
2012-04-11 18:49:22 +02:00
function poster_id ( $ip , $thread ) {
global $config ;
2013-08-03 13:50:25 +02:00
2012-04-12 16:18:19 +02:00
if ( $id = event ( 'poster-id' , $ip , $thread ))
2012-04-11 18:49:22 +02:00
return $id ;
2013-08-03 13:50:25 +02:00
2012-04-11 18:49:22 +02:00
// Confusing, hard to brute-force, but simple algorithm
return substr ( sha1 ( sha1 ( $ip . $config [ 'secure_trip_salt' ] . $thread ) . $config [ 'secure_trip_salt' ]), 0 , $config [ 'poster_id_length' ]);
}
2010-11-02 11:57:33 +01:00
2012-04-11 18:49:22 +02:00
function generate_tripcode ( $name ) {
global $config ;
2013-08-03 13:50:25 +02:00
2012-04-12 16:18:19 +02:00
if ( $trip = event ( 'tripcode' , $name ))
2012-04-11 18:49:22 +02:00
return $trip ;
2013-08-03 13:50:25 +02:00
2012-04-12 16:18:19 +02:00
if ( ! preg_match ( '/^([^#]+)?(##|#)(.+)$/' , $name , $match ))
2012-04-11 18:49:22 +02:00
return array ( $name );
2013-08-03 13:50:25 +02:00
2012-04-11 18:49:22 +02:00
$name = $match [ 1 ];
$secure = $match [ 2 ] == '##' ;
$trip = $match [ 3 ];
2013-08-03 13:50:25 +02:00
2012-04-11 18:49:22 +02:00
// convert to SHIT_JIS encoding
$trip = mb_convert_encoding ( $trip , 'Shift_JIS' , 'UTF-8' );
2013-08-03 13:50:25 +02:00
2012-04-11 18:49:22 +02:00
// generate salt
$salt = substr ( $trip . 'H..' , 1 , 2 );
2013-08-03 09:36:20 +02:00
$salt = preg_replace ( '/[^.-z]/' , '.' , $salt );
2012-04-11 18:49:22 +02:00
$salt = strtr ( $salt , ':;<=>?@[\]^_`' , 'ABCDEFGabcdef' );
2013-08-03 13:50:25 +02:00
2012-04-12 16:18:19 +02:00
if ( $secure ) {
if ( isset ( $config [ 'custom_tripcode' ][ " ## { $trip } " ]))
2012-04-11 18:49:22 +02:00
$trip = $config [ 'custom_tripcode' ][ " ## { $trip } " ];
else
2015-02-17 01:24:54 +01:00
$trip = '!!' . substr ( crypt ( $trip , str_replace ( '+' , '.' , '_..A.' . substr ( base64_encode ( sha1 ( $trip . $config [ 'secure_trip_salt' ], true )), 0 , 4 ))), - 10 );
2012-04-11 18:49:22 +02:00
} else {
2012-04-12 16:18:19 +02:00
if ( isset ( $config [ 'custom_tripcode' ][ " # { $trip } " ]))
2012-04-11 18:49:22 +02:00
$trip = $config [ 'custom_tripcode' ][ " # { $trip } " ];
else
$trip = '!' . substr ( crypt ( $trip , $salt ), - 10 );
}
2013-08-03 13:50:25 +02:00
2012-04-11 18:49:22 +02:00
return array ( $name , $trip );
}
2013-08-03 13:50:25 +02:00
2012-04-11 18:49:22 +02:00
function getPostByHash ( $hash ) {
global $board ;
2013-08-01 04:14:26 +02:00
$query = prepare ( sprintf ( " SELECT `id`,`thread` FROM ``posts_%s`` WHERE `filehash` = :hash " , $board [ 'uri' ]));
2012-04-11 18:49:22 +02:00
$query -> bindValue ( ':hash' , $hash , PDO :: PARAM_STR );
$query -> execute () or error ( db_error ( $query ));
2013-08-03 13:50:25 +02:00
2013-08-01 02:51:43 +02:00
if ( $post = $query -> fetch ( PDO :: FETCH_ASSOC )) {
2012-04-11 18:49:22 +02:00
return $post ;
2011-02-19 09:28:07 +01:00
}
2013-08-03 13:50:25 +02:00
2012-04-11 18:49:22 +02:00
return false ;
}
2012-11-14 21:33:27 +01:00
function getPostByHashInThread ( $hash , $thread ) {
global $board ;
2013-08-01 04:14:26 +02:00
$query = prepare ( sprintf ( " SELECT `id`,`thread` FROM ``posts_%s`` WHERE `filehash` = :hash AND ( `thread` = :thread OR `id` = :thread ) " , $board [ 'uri' ]));
2012-11-14 21:33:27 +01:00
$query -> bindValue ( ':hash' , $hash , PDO :: PARAM_STR );
$query -> bindValue ( ':thread' , $thread , PDO :: PARAM_INT );
$query -> execute () or error ( db_error ( $query ));
2013-08-03 13:50:25 +02:00
2013-08-01 02:51:43 +02:00
if ( $post = $query -> fetch ( PDO :: FETCH_ASSOC )) {
2012-11-14 21:33:27 +01:00
return $post ;
}
2013-08-03 13:50:25 +02:00
2012-11-14 21:33:27 +01:00
return false ;
}
2012-04-11 18:49:22 +02:00
function undoImage ( array $post ) {
2014-04-27 15:48:47 +02:00
if ( ! $post [ 'has_file' ] || ! isset ( $post [ 'files' ]))
2012-04-11 18:49:22 +02:00
return ;
2013-08-03 13:50:25 +02:00
2014-04-27 15:48:47 +02:00
foreach ( $post [ 'files' ] as $key => $file ) {
if ( isset ( $file [ 'file_path' ]))
file_unlink ( $file [ 'file_path' ]);
if ( isset ( $file [ 'thumb_path' ]))
file_unlink ( $file [ 'thumb_path' ]);
}
2012-04-11 18:49:22 +02:00
}
2024-02-04 14:53:06 +01:00
function rDNS ( $ip_addr ) {
global $config ;
if ( $config [ 'cache' ][ 'enabled' ] && ( $host = cache :: get ( 'rdns_' . $ip_addr ))) {
return $host ;
}
if ( ! $config [ 'dns_system' ]) {
$host = gethostbyaddr ( $ip_addr );
} else {
$resp = shell_exec_error ( 'host -W 3 ' . $ip_addr );
if ( preg_match ( '/domain name pointer ([^\s]+)$/' , $resp , $m ))
$host = $m [ 1 ];
else
$host = $ip_addr ;
}
$isip = filter_var ( $host , FILTER_VALIDATE_IP );
if ( $config [ 'fcrdns' ] && ! $isip && DNS ( $host ) != $ip_addr ) {
$host = $ip_addr ;
}
if ( $config [ 'cache' ][ 'enabled' ])
cache :: set ( 'rdns_' . $ip_addr , $host );
return $host ;
}
function DNS ( $host ) {
global $config ;
if ( $config [ 'cache' ][ 'enabled' ] && ( $ip_addr = cache :: get ( 'dns_' . $host ))) {
return $ip_addr != '?' ? $ip_addr : false ;
}
if ( ! $config [ 'dns_system' ]) {
$ip_addr = gethostbyname ( $host );
if ( $ip_addr == $host )
$ip_addr = false ;
} else {
$resp = shell_exec_error ( 'host -W 1 ' . $host );
if ( preg_match ( '/has address ([^\s]+)$/' , $resp , $m ))
$ip_addr = $m [ 1 ];
else
$ip_addr = false ;
}
if ( $config [ 'cache' ][ 'enabled' ])
cache :: set ( 'dns_' . $host , $ip_addr !== false ? $ip_addr : '?' );
return $ip_addr ;
}
function shell_exec_error ( $command , $suppress_stdout = false ) {
global $config , $debug ;
if ( $config [ 'debug' ])
$start = microtime ( true );
$return = trim ( shell_exec ( 'PATH="' . escapeshellcmd ( $config [ 'shell_path' ]) . ':$PATH";' .
$command . ' 2>&1 ' . ( $suppress_stdout ? '> /dev/null ' : '' ) . '&& echo "TB_SUCCESS"' ));
$return = preg_replace ( '/TB_SUCCESS$/' , '' , $return );
if ( $config [ 'debug' ]) {
$time = microtime ( true ) - $start ;
$debug [ 'exec' ][] = array (
'command' => $command ,
'time' => '~' . round ( $time * 1000 , 2 ) . 'ms' ,
'response' => $return ? $return : null
);
$debug [ 'time' ][ 'exec' ] += $time ;
}
return $return === 'TB_SUCCESS' ? false : $return ;
}
2014-08-10 16:51:45 +02:00
/* Die rolling :
* If " dice XdY+/-Z " is in the email field ( where X or +/- Z may be
* missing ), X Y - sided dice are rolled and summed , with the modifier Z
* added on . The result is displayed at the top of the post .
*/
function diceRoller ( $post ) {
2014-08-10 17:14:11 +02:00
global $config ;
2014-08-10 16:51:45 +02:00
if ( strpos ( strtolower ( $post -> email ), 'dice%20' ) === 0 ) {
$dicestr = str_split ( substr ( $post -> email , strlen ( 'dice%20' )));
// Get params
$diceX = '' ;
$diceY = '' ;
$diceZ = '' ;
$curd = 'diceX' ;
for ( $i = 0 ; $i < count ( $dicestr ); $i ++ ) {
if ( is_numeric ( $dicestr [ $i ])) {
$$curd .= $dicestr [ $i ];
} else if ( $dicestr [ $i ] == 'd' ) {
$curd = 'diceY' ;
} else if ( $dicestr [ $i ] == '-' || $dicestr [ $i ] == '+' ) {
$curd = 'diceZ' ;
$$curd = $dicestr [ $i ];
}
}
// Default values for X and Z
if ( $diceX == '' ) {
$diceX = '1' ;
}
if ( $diceZ == '' ) {
$diceZ = '+0' ;
}
// Intify them
$diceX = intval ( $diceX );
$diceY = intval ( $diceY );
$diceZ = intval ( $diceZ );
// Continue only if we have valid values
if ( $diceX > 0 && $diceY > 0 ) {
$dicerolls = array ();
$dicesum = $diceZ ;
for ( $i = 0 ; $i < $diceX ; $i ++ ) {
$roll = rand ( 1 , $diceY );
$dicerolls [] = $roll ;
$dicesum += $roll ;
}
// Prepend the result to the post body
$modifier = ( $diceZ != 0 ) ? ((( $diceZ < 0 ) ? ' - ' : ' + ' ) . abs ( $diceZ )) : '' ;
$dicesum = ( $diceX > 1 ) ? ' = ' . $dicesum : '' ;
2014-08-10 17:14:11 +02:00
$post -> body = '<table class="diceroll"><tr><td><img src="' . $config [ 'dir' ][ 'static' ] . 'd10.svg" alt="Dice roll" width="24"></td><td>Rolled ' . implode ( ', ' , $dicerolls ) . $modifier . $dicesum . '</td></tr></table><br/>' . $post -> body ;
2014-08-10 16:51:45 +02:00
}
}
}
2015-03-10 12:48:59 +01:00
function slugify ( $post ) {
2015-03-12 00:03:22 +01:00
global $config ;
2015-03-10 12:48:59 +01:00
$slug = " " ;
2015-03-10 13:09:53 +01:00
if ( isset ( $post [ 'subject' ]) && $post [ 'subject' ])
$slug = $post [ 'subject' ];
2015-03-10 12:48:59 +01:00
elseif ( isset ( $post [ 'body_nomarkup' ]) && $post [ 'body_nomarkup' ])
$slug = $post [ 'body_nomarkup' ];
elseif ( isset ( $post [ 'body' ]) && $post [ 'body' ])
2016-06-09 11:08:29 +02:00
$slug = strip_tags ( $post [ 'body' ]);
2015-03-10 12:48:59 +01:00
// Fix UTF-8 first
$slug = mb_convert_encoding ( $slug , " UTF-8 " , " UTF-8 " );
// Transliterate local characters like ü, I wonder how would it work for weird alphabets :^)
$slug = iconv ( " UTF-8 " , " ASCII//TRANSLIT//IGNORE " , $slug );
2015-03-30 06:26:53 +02:00
// Remove Tinyboard custom markup
$slug = preg_replace ( " /<tinyboard [^>]+>.*?< \ /tinyboard>/s " , '' , $slug );
2015-03-10 12:48:59 +01:00
// Downcase everything
$slug = strtolower ( $slug );
// Strip bad characters, alphanumerics should suffice
$slug = preg_replace ( '/[^a-zA-Z0-9]/' , '-' , $slug );
// Replace multiple dashes with single ones
$slug = preg_replace ( '/-+/' , '-' , $slug );
// Strip dashes at the beginning and at the end
$slug = preg_replace ( '/^-|-$/' , '' , $slug );
2015-03-12 00:03:22 +01:00
// Slug should be X characters long, at max (80?)
$slug = substr ( $slug , 0 , $config [ 'slug_max_size' ]);
2015-03-10 12:48:59 +01:00
// Slug is now ready
return $slug ;
}
function link_for ( $post , $page50 = false , $foreignlink = false , $thread = false ) {
global $config , $board ;
$post = ( array ) $post ;
// Where do we need to look for OP?
2015-03-10 13:02:38 +01:00
$b = $foreignlink ? $foreignlink : ( isset ( $post [ 'board' ]) ? array ( 'uri' => $post [ 'board' ]) : $board );
2015-03-10 12:48:59 +01:00
$id = ( isset ( $post [ 'thread' ]) && $post [ 'thread' ]) ? $post [ 'thread' ] : $post [ 'id' ];
$slug = false ;
2015-03-10 14:16:27 +01:00
if ( $config [ 'slugify' ] && ( ( isset ( $post [ 'thread' ]) && $post [ 'thread' ]) || ! isset ( $post [ 'slug' ]) ) ) {
2015-03-10 13:42:10 +01:00
$cvar = " slug_ " . $b [ 'uri' ] . " _ " . $id ;
2015-03-10 12:48:59 +01:00
if ( ! $thread ) {
2015-03-10 13:42:10 +01:00
$slug = Cache :: get ( $cvar );
2015-03-10 12:48:59 +01:00
2015-03-10 13:42:10 +01:00
if ( $slug === false ) {
$query = prepare ( sprintf ( " SELECT `slug` FROM ``posts_%s`` WHERE `id` = :id " , $b [ 'uri' ]));
2024-04-30 12:46:44 +02:00
$query -> bindValue ( ':id' , $id , PDO :: PARAM_INT );
$query -> execute () or error ( db_error ( $query ));
2015-03-10 12:48:59 +01:00
2024-04-30 12:46:44 +02:00
$thread = $query -> fetch ( PDO :: FETCH_ASSOC );
2015-03-10 13:42:10 +01:00
$slug = $thread [ 'slug' ];
Cache :: set ( $cvar , $slug );
}
}
else {
$slug = $thread [ 'slug' ];
2015-03-10 12:48:59 +01:00
}
}
elseif ( $config [ 'slugify' ]) {
$slug = $post [ 'slug' ];
}
2024-04-30 12:46:44 +02:00
if ( $page50 && $slug ) $tpl = $config [ 'file_page50_slug' ];
2015-03-10 12:48:59 +01:00
else if ( ! $page50 && $slug ) $tpl = $config [ 'file_page_slug' ];
else if ( $page50 && ! $slug ) $tpl = $config [ 'file_page50' ];
else if ( ! $page50 && ! $slug ) $tpl = $config [ 'file_page' ];
return sprintf ( $tpl , $id , $slug );
}
2015-03-29 03:18:14 +02:00
function prettify_textarea ( $s ){
return str_replace ( " \t " , '	' , str_replace ( " \n " , ' ' , htmlentities ( $s )));
}
function purify_html ( $s ) {
global $config ;
$c = HTMLPurifier_Config :: createDefault ();
$c -> set ( 'HTML.Allowed' , $config [ 'allowed_html' ]);
$uri = $c -> getDefinition ( 'URI' );
$uri -> addFilter ( new HTMLPurifier_URIFilter_NoExternalImages (), $c );
$purifier = new HTMLPurifier ( $c );
$clean_html = $purifier -> purify ( $s );
return $clean_html ;
}
function markdown ( $s ) {
$pd = new Parsedown ();
$pd -> setMarkupEscaped ( true );
$pd -> setimagesEnabled ( false );
return $pd -> text ( $s );
}
2016-05-08 10:54:30 +02:00
2024-02-04 14:53:06 +01:00
function generation_strategy ( $fun , $array = array ()) { global $config ;
$action = false ;
foreach ( $config [ 'generation_strategies' ] as $s ) {
if ( $action = $s ( $fun , $array )) {
break ;
}
}
switch ( $action [ 0 ]) {
case 'immediate' :
return 'rebuild' ;
case 'defer' :
// Ok, it gets interesting here :)
2024-02-16 15:18:17 +01:00
$queue = Queues :: get_queue ( $config , 'generate' );
if ( $queue === false ) {
if ( $config [ 'syslog' ]) {
_syslog ( LOG_ERR , " Could not initialize generate queue, falling back to immediate rebuild strategy " );
}
return 'rebuild' ;
}
2024-02-16 15:48:18 +01:00
$ret = $queue -> push ( serialize ( array ( 'build' , $fun , $array , $action )));
if ( $ret === false ) {
if ( $config [ 'syslog' ]) {
_syslog ( LOG_ERR , " Could not push item in the queue, falling back to immediate rebuild strategy " );
}
return 'rebuild' ;
}
return 'ignore' ;
2024-02-04 14:53:06 +01:00
case 'build_on_load' :
return 'delete' ;
}
}
function strategy_immediate ( $fun , $array ) {
return array ( 'immediate' );
}
function strategy_smart_build ( $fun , $array ) {
return array ( 'build_on_load' );
}
function strategy_sane ( $fun , $array ) { global $config ;
if ( php_sapi_name () == 'cli' ) return false ;
else if ( isset ( $_POST [ 'mod' ])) return false ;
// Thread needs to be done instantly. Same with a board page, but only if posting a new thread.
else if ( $fun == 'sb_thread' || ( $fun == 'sb_board' && $array [ 1 ] == 1 && isset ( $_POST [ 'page' ]))) return array ( 'immediate' );
else return false ;
}
// My first, test strategy.
function strategy_first ( $fun , $array ) {
switch ( $fun ) {
case 'sb_thread' :
return array ( 'defer' );
case 'sb_board' :
if ( $array [ 1 ] > 8 ) return array ( 'build_on_load' );
else return array ( 'defer' );
case 'sb_api' :
return array ( 'defer' );
case 'sb_catalog' :
return array ( 'defer' );
case 'sb_recent' :
return array ( 'build_on_load' );
case 'sb_sitemap' :
return array ( 'build_on_load' );
case 'sb_ukko' :
return array ( 'defer' );
}
}
function base32_decode ( $d ) {
$charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567' ;
$d = str_split ( $d );
$l = array_pop ( $d );
$b = '' ;
foreach ( $d as $c ) {
$b .= sprintf ( " %05b " , strpos ( $charset , $c ));
}
$padding = 8 - strlen ( $b ) % 8 ;
$b .= str_pad ( decbin ( strpos ( $charset , $l )), $padding , '0' , STR_PAD_LEFT );
return implode ( '' , array_map ( function ( $c ) { return chr ( bindec ( $c )); }, str_split ( $b , 8 )));
}
function base32_encode ( $d ) {
$charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567' ;
$b = implode ( '' , array_map ( function ( $c ) { return sprintf ( " %08b " , ord ( $c )); }, str_split ( $d )));
return implode ( '' , array_map ( function ( $c ) use ( $charset ) { return $charset [ bindec ( $c )]; }, str_split ( $b , 5 )));
}
2021-02-13 14:11:41 +01:00
function cloak_ip ( $ip ) {
global $config ;
$ipcrypt_key = $config [ 'ipcrypt_key' ] ? : null ;
if ( empty ( $ipcrypt_key ))
return $ip ;
$ip_dec = inet_pton ( $ip );
if ( $config [ 'ipcrypt_dns' ]) {
$host = gethostbyaddr ( $ip );
if ( $host !== $ip ) {
$segments = explode ( '.' , $host );
$tld = [];
$tld [] = array_pop ( $segments );
if ( count ( $segments ) >= 2 ) {
$tld [] = array_pop ( $segments );
}
$tld = implode ( '.' , array_reverse ( $tld ));
}
}
if ( is_numeric ( $ip ))
$ipbytes = pack ( 'N' , $ip );
else if ( $ip_dec !== false )
$ipbytes = $ip_dec ;
else
return " #ERROR " ;
if ( strlen ( $ipbytes ) >= 16 )
$ipbytes = substr ( $ipbytes , 0 , 16 );
2023-09-02 07:16:05 +02:00
$cyphertext = openssl_encrypt ( $ipbytes , 'aes-256-ctr' , $ipcrypt_key , OPENSSL_RAW_DATA );
2021-02-13 14:11:41 +01:00
$ret = $config [ 'ipcrypt_prefix' ] . ':' . base32_encode ( $cyphertext );
if ( isset ( $tld ) && ! empty ( $tld )) {
$ret .= '.' . $tld ;
}
return $ret ;
}
function uncloak_ip ( $ip ) {
global $config ;
$ipcrypt_key = ( $config [ 'ipcrypt_key' ]);
if ( empty ( $ipcrypt_key ))
return $ip ;
$juice = substr ( $ip , strlen ( $config [ 'ipcrypt_prefix' ]) + 1 );
if ( $delimiter = strpos ( $juice , '.' )) {
$juice = substr ( $juice , 0 , $delimiter );
}
if ( substr ( $ip , 0 , strlen ( $config [ 'ipcrypt_prefix' ]) + 1 ) === $config [ 'ipcrypt_prefix' ] . ':' ) {
2023-09-02 07:16:05 +02:00
$plaintext = openssl_decrypt ( base32_decode ( $juice ), 'aes-256-ctr' , $ipcrypt_key , OPENSSL_RAW_DATA );
2021-02-13 14:11:41 +01:00
if ( $plaintext === false || strlen ( $plaintext ) == 0 )
return '#ERROR' ;
if ( strlen ( $ip ) >= 16 )
return inet_ntop ( $plaintext );
else
return long2ip ( unpack ( 'N' , $plaintext )[ 1 ]);
}
return '#ERROR' ;
}
function cloak_mask ( $mask ) {
2021-04-01 22:30:22 +02:00
list ( $net , $block ) = array_pad ( explode ( '/' , $mask , 2 ), 2 , null );
2021-02-13 14:11:41 +01:00
$mask = cloak_ip ( $net );
if ( $block ) {
$mask .= '/' . $block ;
}
return $mask ;
}
function uncloak_mask ( $mask ) {
2021-04-01 22:30:22 +02:00
list ( $addr , $block ) = array_pad ( explode ( '/' , $mask , 2 ), 2 , null );
2021-02-13 14:11:41 +01:00
$mask = uncloak_ip ( $addr );
if ( $mask === '#ERROR' ) {
$mask = $addr ;
}
if ( $block ) {
$mask .= '/' . $block ;
}
return $mask ;
}
2023-08-14 08:14:41 +02:00
function check_thread_limit ( $post ) {
global $config , $board ;
if ( ! isset ( $config [ 'max_threads_per_hour' ]) || ! $config [ 'max_threads_per_hour' ]) return false ;
if ( $post [ 'op' ]) {
$query = prepare ( sprintf ( 'SELECT COUNT(*) AS `count` FROM ``posts_%s`` WHERE `thread` IS NULL AND FROM_UNIXTIME(`time`) > DATE_SUB(NOW(), INTERVAL 1 HOUR);' , $board [ 'uri' ]));
$query -> execute () or error ( db_error ( $query ));
$r = $query -> fetch ( PDO :: FETCH_ASSOC );
return $r [ 'count' ] >= $config [ 'max_threads_per_hour' ];
}
}