', $text);
}
function generate_user_info($row, bool $have_auth = IS_ADMIN): array
{
global $userdata, $lang, $images, $bb_cfg;
$from = !empty($row['user_from']) ? render_flag($row['user_from'], false) : $lang['NOSELECT'];
$joined = bb_date($row['user_regdate'], 'Y-m-d H:i', false);
$user_time = !empty($row['user_time']) ? sprintf('%s (%s)', bb_date($row['user_time']), delta_time($row['user_time'])) : $lang['NOSELECT'];
$posts = '' . $row['user_posts'] ?: 0 . '';
$pm = $bb_cfg['text_buttons'] ? '' . $lang['SEND_PM_TXTB'] . '' : '
';
$avatar = get_avatar($row['user_id'], $row['avatar_ext_id'], !bf($row['user_opt'], 'user_opt', 'dis_avatar'), 50, 50);
if (bf($row['user_opt'], 'user_opt', 'user_viewemail') || $have_auth || ($row['user_id'] == $userdata['user_id'])) {
$email_uri = ($bb_cfg['board_email_form']) ? ("profile.php?mode=email&" . POST_USERS_URL . "=" . $row['user_id']) : 'mailto:' . $row['user_email'];
$email = '' . $row['user_email'] . '';
} else {
$email = $lang['HIDDEN_USER'];
}
if ($row['user_website']) {
$www = $bb_cfg['text_buttons'] ? '' . $lang['VISIT_WEBSITE_TXTB'] . '' : '
';
} else {
$www = $lang['NOSELECT'];
}
return [
'from' => $from,
'joined' => $joined,
'joined_raw' => $row['user_regdate'],
'posts' => $posts,
'pm' => $pm,
'avatar' => $avatar,
'user_time' => $user_time,
'user_time_raw' => ($row['user_time'] ?? ''),
'email' => $email,
'www' => $www
];
}
function get_bt_userdata($user_id)
{
if (!$btu = CACHE('bb_cache')->get('btu_' . $user_id)) {
$btu = DB()->fetch_row("
SELECT bt.*, SUM(tr.speed_up) AS speed_up, SUM(tr.speed_down) AS speed_down
FROM " . BB_BT_USERS . " bt
LEFT JOIN " . BB_BT_TRACKER . " tr ON (bt.user_id = tr.user_id)
WHERE bt.user_id = " . (int)$user_id . "
GROUP BY bt.user_id
LIMIT 1
");
CACHE('bb_cache')->set('btu_' . $user_id, $btu, 300);
}
return $btu;
}
function show_bt_userdata($user_id): void
{
global $template;
if (!$btu = get_bt_userdata($user_id)) {
return;
}
$template->assign_vars([
'SHOW_BT_USERDATA' => true,
'UP_TOTAL' => humn_size($btu['u_up_total']),
'UP_BONUS' => humn_size($btu['u_up_bonus']),
'RELEASED' => humn_size($btu['u_up_release']),
'DOWN_TOTAL' => humn_size($btu['u_down_total']),
'DOWN_TOTAL_BYTES' => $btu['u_down_total'],
'USER_RATIO' => get_bt_ratio($btu),
'MIN_DL_FOR_RATIO' => humn_size((int)MIN_DL_FOR_RATIO),
'MIN_DL_BYTES' => (int)MIN_DL_FOR_RATIO,
'AUTH_KEY' => $btu['auth_key'],
'TD_DL' => humn_size($btu['down_today']),
'TD_UL' => humn_size($btu['up_today']),
'TD_REL' => humn_size($btu['up_release_today']),
'TD_BONUS' => humn_size($btu['up_bonus_today']),
'TD_POINTS' => $btu['auth_key'] ? $btu['points_today'] : '0.00',
'YS_DL' => humn_size($btu['down_yesterday']),
'YS_UL' => humn_size($btu['up_yesterday']),
'YS_REL' => humn_size($btu['up_release_yesterday']),
'YS_BONUS' => humn_size($btu['up_bonus_yesterday']),
'YS_POINTS' => $btu['auth_key'] ? $btu['points_yesterday'] : '0.00',
'SPEED_UP' => humn_size($btu['speed_up'], min: 'KB') . '/s',
'SPEED_DOWN' => humn_size($btu['speed_down'], min: 'KB') . '/s',
]);
}
function get_attachments_dir($cfg = null)
{
if (!$cfg and !$cfg = $GLOBALS['attach_config']) {
$cfg = bb_get_config(BB_ATTACH_CONFIG, true, false);
}
if ($cfg['upload_dir'][0] == '/' || ($cfg['upload_dir'][0] != '/' && $cfg['upload_dir'][1] == ':')) {
return $cfg['upload_dir'];
}
return BB_ROOT . $cfg['upload_dir'];
}
function bb_get_config($table, $from_db = false, $update_cache = true)
{
if ($from_db or !$cfg = CACHE('bb_config')->get("config_{$table}")) {
$cfg = [];
foreach (DB()->fetch_rowset("SELECT * FROM $table") as $row) {
$cfg[$row['config_name']] = $row['config_value'];
}
if ($update_cache) {
CACHE('bb_config')->set("config_{$table}", $cfg);
}
}
return $cfg;
}
function bb_update_config($params, $table = BB_CONFIG)
{
$updates = [];
foreach ($params as $name => $val) {
$updates[] = [
'config_name' => $name,
'config_value' => $val
];
}
$updates = DB()->build_array('MULTI_INSERT', $updates);
DB()->query("REPLACE INTO $table $updates");
// Update cache
bb_get_config($table, true, true);
}
function get_db_stat($mode)
{
switch ($mode) {
case 'usercount':
$sql = "SELECT COUNT(user_id) AS total FROM " . BB_USERS;
break;
case 'newestuser':
$sql = "SELECT user_id, username FROM " . BB_USERS . " WHERE user_id <> " . GUEST_UID . " ORDER BY user_id DESC LIMIT 1";
break;
case 'postcount':
case 'topiccount':
$sql = "SELECT SUM(forum_topics) AS topic_total, SUM(forum_posts) AS post_total FROM " . BB_FORUMS;
break;
}
if (!($result = DB()->sql_query($sql))) {
return false;
}
$row = DB()->sql_fetchrow($result);
switch ($mode) {
case 'usercount':
return $row['total'];
break;
case 'newestuser':
return $row;
break;
case 'postcount':
return $row['post_total'];
break;
case 'topiccount':
return $row['topic_total'];
break;
}
return false;
}
function clean_username($username)
{
$username = mb_substr(htmlspecialchars(str_replace("\'", "'", trim($username))), 0, 25, DEFAULT_CHARSET);
$username = rtrim($username, "\\");
$username = str_replace("'", "\'", $username);
return $username;
}
function bb_ltrim($str, $charlist = false)
{
if ($charlist === false) {
return ltrim($str);
}
$str = ltrim($str, $charlist);
return $str;
}
function bb_rtrim($str, $charlist = false)
{
if ($charlist === false) {
return rtrim($str);
}
$str = rtrim($str, $charlist);
return $str;
}
/**
* Get Userdata
*
* @param int|string $u
* @param bool $is_name
* @param bool $allow_guest
* @return mixed
*/
function get_userdata(int|string $u, bool $is_name = false, bool $allow_guest = false, bool $profile_view = false)
{
if (empty($u)) {
return false;
}
if (!$is_name) {
$u = (int)$u;
if ($u === GUEST_UID && $allow_guest) {
if ($u_data = CACHE('bb_cache')->get('guest_userdata')) {
return $u_data;
}
}
$where_sql = "WHERE user_id = " . $u;
} else {
$where_sql = "WHERE username = '" . DB()->escape(clean_username($u)) . "'";
}
if ($profile_view) {
$where_sql = "WHERE user_id = " . (int)$u . " OR username = '" . DB()->escape(clean_username($u)) . "'";
}
$exclude_anon_sql = (!$allow_guest) ? "AND user_id != " . GUEST_UID : '';
$sql = "SELECT * FROM " . BB_USERS . " $where_sql $exclude_anon_sql LIMIT 1";
if (!$u_data = DB()->fetch_row($sql)) {
return false;
}
if ((int)$u_data['user_id'] === GUEST_UID) {
CACHE('bb_cache')->set('guest_userdata', $u_data);
}
return $u_data;
}
function make_jumpbox(): void
{
global $datastore, $template, $bb_cfg;
if (!$bb_cfg['show_jumpbox']) {
return;
}
if (!$jumpbox = $datastore->get('jumpbox')) {
$datastore->update('jumpbox');
$jumpbox = $datastore->get('jumpbox');
}
$template->assign_vars(['JUMPBOX' => IS_GUEST ? DB()->escape($jumpbox['guest']) : DB()->escape($jumpbox['user'])]);
}
// $mode: array(not_auth_forum1,not_auth_forum2,..) or (string) 'mode'
function get_forum_select($mode = 'guest', $name = POST_FORUM_URL, $selected = null, $max_length = HTML_SELECT_MAX_LENGTH, $multiple_size = null, $js = '', $all_forums_option = null)
{
global $lang, $datastore;
if (is_array($mode)) {
$not_auth_forums_fary = array_flip($mode);
$mode = 'not_auth_forums';
}
if (null === $max_length) {
$max_length = HTML_SELECT_MAX_LENGTH;
}
$select = null === $all_forums_option ? [] : [$lang['ALL_AVAILABLE'] => $all_forums_option];
if (!$forums = $datastore->get('cat_forums')) {
$datastore->update('cat_forums');
$forums = $datastore->get('cat_forums');
}
foreach ($forums['f'] as $fid => $f) {
switch ($mode) {
case 'guest':
if ($f['auth_view'] != AUTH_ALL) {
continue 2;
}
break;
case 'user':
if ($f['auth_view'] != AUTH_ALL && $f['auth_view'] != AUTH_REG) {
continue 2;
}
break;
case 'not_auth_forums':
if (isset($not_auth_forums_fary[$f['forum_id']])) {
continue 2;
}
break;
case 'admin':
break;
default:
trigger_error(__FUNCTION__ . ": invalid mode '$mode'", E_USER_ERROR);
}
$cat_title = $forums['c'][$f['cat_id']]['cat_title'];
$f_name = ($f['forum_parent']) ? ' |- ' : '';
$f_name .= $f['forum_name'];
while (isset($select[$cat_title][$f_name])) {
$f_name .= ' ';
}
$select[$cat_title][$f_name] = $fid;
if (!$f['forum_parent']) {
$class = 'root_forum';
$class .= isset($f['subforums']) ? ' has_sf' : '';
$select['__attributes'][$cat_title][$f_name]['class'] = $class;
}
}
return build_select($name, $select, $selected, $max_length, $multiple_size, $js);
}
function setup_style()
{
global $bb_cfg, $template, $userdata;
// AdminCP works only with default template
$tpl_dir_name = defined('IN_ADMIN') ? 'default' : basename($bb_cfg['tpl_name']);
$stylesheet = defined('IN_ADMIN') ? 'main.css' : basename($bb_cfg['stylesheet']);
if (!IS_GUEST && !empty($userdata['tpl_name'])) {
foreach ($bb_cfg['templates'] as $folder => $name) {
if ($userdata['tpl_name'] == $folder) {
$tpl_dir_name = basename($userdata['tpl_name']);
}
}
}
$template = new TorrentPier\Legacy\Template(TEMPLATES_DIR . '/' . $tpl_dir_name);
$css_dir = 'styles/' . basename(TEMPLATES_DIR) . '/' . $tpl_dir_name . '/css/';
$template->assign_vars([
'BB_ROOT' => BB_ROOT,
'SPACER' => make_url('styles/images/spacer.gif'),
'STYLESHEET' => make_url($css_dir . $stylesheet),
'EXT_LINK_NEW_WIN' => $bb_cfg['ext_link_new_win'],
'TPL_DIR' => make_url($css_dir),
'SITE_URL' => make_url('/')
]);
require_once TEMPLATES_DIR . '/' . $tpl_dir_name . '/tpl_config.php';
return ['template_name' => $tpl_dir_name];
}
// Create date / time with format and friendly date
function bb_date($gmepoch, $format = false, $friendly_date = true)
{
global $bb_cfg, $lang, $userdata;
$gmepoch = (int)$gmepoch;
if (!$format) {
$format = $bb_cfg['default_dateformat'];
}
if (empty($lang)) {
require_once($bb_cfg['default_lang_dir'] . 'main.php');
}
if (!defined('IS_GUEST') || IS_GUEST) {
$tz = $bb_cfg['board_timezone'];
} else {
$tz = $userdata['user_timezone'];
}
$date = gmdate($format, $gmepoch + (3600 * $tz));
if ($friendly_date) {
$time_format = ' H:i';
$today = gmdate('d', TIMENOW + (3600 * $tz));
$month = gmdate('m', TIMENOW + (3600 * $tz));
$year = gmdate('Y', TIMENOW + (3600 * $tz));
$date_today = gmdate('d', $gmepoch + (3600 * $tz));
$date_month = gmdate('m', $gmepoch + (3600 * $tz));
$date_year = gmdate('Y', $gmepoch + (3600 * $tz));
if ($date_today == $today && $date_month == $month && $date_year == $year) {
$date = 'today' . gmdate($time_format, $gmepoch + (3600 * $tz));
} elseif ($today != 1 && $date_today == ($today - 1) && $date_month == $month && $date_year == $year) {
$date = 'yesterday' . gmdate($time_format, $gmepoch + (3600 * $tz));
} elseif ($today == 1 && $month != 1) {
$yesterday = date('t', mktime(0, 0, 0, ($month - 1), 1, $year));
if ($date_today == $yesterday && $date_month == ($month - 1) && $date_year == $year) {
$date = 'yesterday' . gmdate($time_format, $gmepoch + (3600 * $tz));
}
} elseif ($today == 1 && $month == 1) {
$yesterday = date('t', mktime(0, 0, 0, 12, 1, ($year - 1)));
if ($date_today == $yesterday && $date_month == 12 && $date_year == ($year - 1)) {
$date = 'yesterday' . gmdate($time_format, $gmepoch + (3600 * $tz));
}
}
}
return ($bb_cfg['translate_dates']) ? strtr(strtoupper($date), $lang['DATETIME']) : $date;
}
/**
* Get user's torrent client string
*
* @param string $peer_id
* @return string
*/
function get_user_torrent_client(string $peer_id): string
{
global $bb_cfg;
static $iconExtension = '.png';
$bestMatch = null;
$bestMatchLength = 0;
foreach ($bb_cfg['tor_clients'] as $key => $clientName) {
if (str_starts_with($peer_id, $key) !== false && strlen($key) > $bestMatchLength) {
$bestMatch = $clientName;
$bestMatchLength = strlen($key);
}
}
$clientIconPath = BB_ROOT . 'styles/images/clients/' . $bestMatch . $iconExtension;
if (!empty($bestMatch) && is_file($clientIconPath)) {
return '
';
}
return $peer_id;
}
/**
* Returns country flag by country code
*
* @param string $code
* @param bool $showName
* @return string
*/
function render_flag(string $code, bool $showName = true): string
{
global $lang;
static $iconExtension = '.svg';
$nameIgnoreList = [
'WBW',
'PACE',
'LGBT'
];
if (isset($lang['COUNTRIES'][$code])) {
if ($code === '0') {
return ''; // No selected
} else {
$flagIconPath = BB_ROOT . 'styles/images/flags/' . $code . $iconExtension;
if (is_file($flagIconPath)) {
$langName = $lang['COUNTRIES'][$code];
$countryName = ($showName && !in_array($code, $nameIgnoreList)) ? ' ' . str_short($langName, 20) : '';
return '
' . $countryName . '';
}
}
}
return $code;
}
function birthday_age($date)
{
global $bb_cfg;
if (!$date) {
return '';
}
$tz = TIMENOW + (3600 * $bb_cfg['board_timezone']);
return delta_time(strtotime($date, $tz));
}
//
// Pagination routine, generates
// page number sequence
//
function generate_pagination($base_url, $num_items, $per_page, $start_item, $add_prevnext_text = true)
{
global $lang, $template;
$begin_end = 3;
$from_middle = 1;
$total_pages = ceil($num_items / $per_page);
$on_page = floor($start_item / $per_page) + 1;
$query_separator = '&';
if (!str_contains($base_url, '?')) {
$query_separator = '?';
}
$page_string = '';
if ($total_pages > ((2 * ($begin_end + $from_middle)) + 2)) {
$init_page_max = ($total_pages > $begin_end) ? $begin_end : $total_pages;
for ($i = 1; $i < $init_page_max + 1; $i++) {
$page_string .= ($i == $on_page) ? '' . $i . '' : '' . $i . '';
if ($i < $init_page_max) {
$page_string .= ", ";
}
}
if ($total_pages > $begin_end) {
if ($on_page > 1 && $on_page < $total_pages) {
$page_string .= ($on_page > ($begin_end + $from_middle + 1)) ? ' ... ' : ', ';
$init_page_min = ($on_page > ($begin_end + $from_middle)) ? $on_page : ($begin_end + $from_middle + 1);
$init_page_max = ($on_page < $total_pages - ($begin_end + $from_middle)) ? $on_page : $total_pages - ($begin_end + $from_middle);
for ($i = $init_page_min - $from_middle; $i < $init_page_max + ($from_middle + 1); $i++) {
$page_string .= ($i == $on_page) ? '' . $i . '' : '' . $i . '';
if ($i < $init_page_max + $from_middle) {
$page_string .= ', ';
}
}
$page_string .= ($on_page < $total_pages - ($begin_end + $from_middle)) ? ' ... ' : ', ';
} else {
$page_string .= ' ... ';
}
for ($i = $total_pages - ($begin_end - 1); $i < $total_pages + 1; $i++) {
$page_string .= ($i == $on_page) ? '' . $i . '' : '' . $i . '';
if ($i < $total_pages) {
$page_string .= ", ";
}
}
}
} else {
for ($i = 1; $i < $total_pages + 1; $i++) {
$page_string .= ($i == $on_page) ? '' . $i . '' : '' . $i . '';
if ($i < $total_pages) {
$page_string .= ', ';
}
}
}
if ($add_prevnext_text) {
if ($on_page > 1) {
$page_string = ' ' . $lang['PREVIOUS_PAGE'] . ' ' . $page_string;
$meta_prev_link = FULL_URL . $base_url . "{$query_separator}start=" . (($on_page - 2) * $per_page);
}
if ($on_page < $total_pages) {
$page_string .= ' ' . $lang['NEXT_PAGE'] . '';
$meta_next_link = FULL_URL . $base_url . "{$query_separator}start=" . ($on_page * $per_page);
}
}
$pagination = false;
if ($page_string && $total_pages > 1) {
$pagination = ' : ' . $page_string;
$pagination = str_replace("{$query_separator}start=0", '', $pagination);
}
$template->assign_vars([
'PAGINATION' => $pagination,
'PAGE_NUMBER' => sprintf($lang['PAGE_OF'], (floor($start_item / $per_page) + 1), ceil($num_items / $per_page)),
'PG_BASE_URL' => $base_url,
'PG_PER_PAGE' => $per_page,
// Assign meta
'META_PREV_PAGE' => $meta_prev_link ?? '',
'META_NEXT_PAGE' => $meta_next_link ?? '',
]);
return $pagination;
}
//
// This does exactly what preg_quote() does in PHP 4-ish
// If you just need the 1-parameter preg_quote call, then don't bother using this.
//
function bb_preg_quote($str, $delimiter)
{
$text = preg_quote($str);
$text = str_replace($delimiter, '\\' . $delimiter, $text);
return $text;
}
function bb_die($msg_text, $status_code = null)
{
global $ajax, $bb_cfg, $lang, $template, $theme, $userdata, $user;
if (isset($status_code)) {
http_response_code($status_code);
}
if (defined('IN_AJAX')) {
$ajax->ajax_die($msg_text);
}
// Check
if (defined('HAS_DIED')) {
trigger_error(__FUNCTION__ . ' was called multiple times', E_USER_ERROR);
}
define('HAS_DIED', 1);
define('DISABLE_CACHING_OUTPUT', true);
// If empty lang
if (empty($lang)) {
require($bb_cfg['default_lang_dir'] . 'main.php');
}
// If empty session
if (empty($userdata)) {
$userdata = $user->session_start();
}
// If the header hasn't been output then do it
if (!defined('PAGE_HEADER_SENT')) {
if (empty($template)) {
$template = new TorrentPier\Legacy\Template(BB_ROOT . "templates/{$bb_cfg['tpl_name']}");
}
if (empty($theme)) {
$theme = setup_style();
}
require(PAGE_HEADER);
}
// Check for lang variable
if (!empty($lang[$msg_text])) {
$msg_text = $lang[$msg_text];
}
$template->assign_vars([
'TPL_BB_DIE' => true,
'MESSAGE_TEXT' => $msg_text
]);
$template->set_filenames(['bb_die' => 'common.tpl']);
$template->pparse('bb_die');
require(PAGE_FOOTER);
exit;
}
function bb_simple_die($txt, $status_code = null)
{
global $bb_cfg;
header('Content-Type: text/plain; charset=' . DEFAULT_CHARSET);
if (isset($status_code)) {
http_response_code($status_code);
}
if (!empty($_COOKIE['explain'])) {
bb_die("bb_simple_die:
$txt");
}
die($txt);
}
function bb_realpath($path)
{
return realpath($path);
}
function login_redirect($url = '')
{
redirect(LOGIN_URL . '?redirect=' . (($url) ?: ($_SERVER['REQUEST_URI'] ?? '/')));
}
function meta_refresh($url, $time = 5)
{
global $template;
$template->assign_var('META', '');
}
function redirect($url)
{
global $bb_cfg;
if (headers_sent($filename, $linenum)) {
trigger_error("Headers already sent in $filename($linenum)", E_USER_ERROR);
}
if (str_contains(urldecode($url), "\n") || str_contains(urldecode($url), "\r") || str_contains(urldecode($url), ';url')) {
bb_die('Tried to redirect to potentially insecure url');
}
$url = trim($url);
$server_protocol = ($bb_cfg['cookie_secure']) ? 'https://' : 'http://';
$server_name = preg_replace('#^\/?(.*?)\/?$#', '\1', trim($bb_cfg['server_name']));
$server_port = ($bb_cfg['server_port'] <> 80) ? ':' . trim($bb_cfg['server_port']) : '';
$script_name = preg_replace('#^\/?(.*?)\/?$#', '\1', trim($bb_cfg['script_path']));
if ($script_name) {
$script_name = "/$script_name";
$url = preg_replace("#^$script_name#", '', $url);
}
$redirect_url = $server_protocol . $server_name . $server_port . $script_name . preg_replace('#^\/?(.*?)\/?$#', '/\1', $url);
// Send no-cache headers to prevent browsers from caching redirects
send_no_cache_headers();
// Behave as per HTTP/1.1 spec for others
header('Location: ' . $redirect_url, response_code: 301);
exit;
}
// build a list of the sortable fields or return field name
function get_forum_display_sort_option($selected_row = 0, $action = 'list', $list = 'sort')
{
global $lang;
$forum_display_sort = [
'lang_key' => ['LASTPOST', 'SORT_TOPIC_TITLE', 'SORT_TIME'],
'fields' => ['t.topic_last_post_time', 't.topic_title', 't.topic_time']
];
$forum_display_order = [
'lang_key' => ['DESC', 'ASC'],
'fields' => ['DESC', 'ASC']
];
// get the good list
$list_name = 'forum_display_' . $list;
$listrow = ${$list_name};
// init the result
$res = '';
if ($selected_row > count($listrow['lang_key'])) {
$selected_row = 0;
}
// build list
if ($action == 'list') {
foreach ($listrow['lang_key'] as $i => $iValue) {
$selected = ($i == $selected_row) ? ' selected' : '';
$l_value = $lang[$listrow['lang_key'][$i]] ?? $iValue;
$res .= '';
}
} else {
// field
$res = $listrow['fields'][$selected_row];
}
return $res;
}
function topic_attachment_image($switch_attachment)
{
global $is_auth;
if (!$switch_attachment || !($is_auth['auth_download'] && $is_auth['auth_view'])) {
return '';
}
return '
';
}
function clear_dl_list($topics_csv)
{
DB()->query("DELETE FROM " . BB_BT_DLSTATUS . " WHERE topic_id IN($topics_csv)");
DB()->query("DELETE FROM " . BB_BT_DLSTATUS_SNAP . " WHERE topic_id IN($topics_csv)");
}
// $ids - array(id1,id2,..) or (string) id
function get_id_csv($ids)
{
$ids = array_values((array)$ids);
array_deep($ids, 'intval', 'one-dimensional');
return (string)implode(',', $ids);
}
// $ids - array(id1,id2,..) or (string) id1,id2,..
function get_id_ary($ids)
{
$ids = is_string($ids) ? explode(',', $ids) : array_values((array)$ids);
array_deep($ids, 'intval', 'one-dimensional');
return (array)$ids;
}
function get_topic_title($topic_id)
{
$row = DB()->fetch_row("
SELECT topic_title FROM " . BB_TOPICS . " WHERE topic_id = " . (int)$topic_id . "
");
return $row['topic_title'];
}
function forum_exists($forum_id = null): bool
{
if (!isset($forum_id)) {
return (bool)DB()->fetch_row("SELECT * FROM " . BB_FORUMS . " LIMIT 1");
}
return (bool)DB()->fetch_row("SELECT forum_id FROM " . BB_FORUMS . " WHERE forum_id = $forum_id LIMIT 1");
}
function cat_exists($cat_id): bool
{
return (bool)DB()->fetch_row("SELECT cat_id FROM " . BB_CATEGORIES . " WHERE cat_id = $cat_id LIMIT 1");
}
function get_topic_icon($topic, $is_unread = null)
{
global $bb_cfg, $images;
$t_hot = ($topic['topic_replies'] >= $bb_cfg['hot_threshold']);
$is_unread ??= is_unread($topic['topic_last_post_time'], $topic['topic_id'], $topic['forum_id']);
if ($topic['topic_status'] == TOPIC_MOVED) {
$folder_image = $images['folder'];
} else {
$folder = ($t_hot) ? $images['folder_hot'] : $images['folder'];
$folder_new = ($t_hot) ? $images['folder_hot_new'] : $images['folder_new'];
if ($topic['topic_type'] == POST_ANNOUNCE) {
$folder = $images['folder_announce'];
$folder_new = $images['folder_announce_new'];
} elseif ($topic['topic_type'] == POST_STICKY) {
$folder = $images['folder_sticky'];
$folder_new = $images['folder_sticky_new'];
} elseif ($topic['topic_status'] == TOPIC_LOCKED) {
$folder = $images['folder_locked'];
$folder_new = $images['folder_locked_new'];
} elseif ($topic['topic_dl_type'] == TOPIC_DL_TYPE_DL) {
$folder = ($t_hot) ? $images['folder_dl_hot'] : $images['folder_dl'];
$folder_new = ($t_hot) ? $images['folder_dl_hot_new'] : $images['folder_dl_new'];
}
$folder_image = ($is_unread) ? $folder_new : $folder;
}
return $folder_image;
}
function build_topic_pagination($url, $replies, $per_page)
{
$pg = '';
if (++$replies > $per_page) {
$total_pages = ceil($replies / $per_page);
for ($j = 0, $page = 1; $j < $replies; $j += $per_page, $page++) {
$href = ($j) ? "$url&start=$j" : $url;
$pg .= '' . $page . '';
if ($page == 1 && $total_pages > 3) {
$pg .= ' .. ';
$page = $total_pages - 2;
$j += ($total_pages - 3) * $per_page;
} elseif ($page < $total_pages) {
$pg .= ', ';
}
}
}
return $pg;
}
function print_confirmation($tpl_vars): void
{
global $template, $lang;
$template->assign_vars([
'TPL_CONFIRM' => true,
'CONFIRM_TITLE' => $lang['CONFIRM'],
'FORM_METHOD' => 'post'
]);
if (!isset($tpl_vars['QUESTION'])) {
$tpl_vars['QUESTION'] = $lang['QUESTION'];
}
$template->assign_vars($tpl_vars);
print_page('common.tpl');
}
/**
* $args = array(
* 'tpl' => 'template file name',
* 'simple' => $gen_simple_header,
* );
* OR (string) 'template_file_name'
*
* $type = '' (common forum page)
* 'admin' (adminCP page)
* 'simple' (simple page without common header)
*
* $mode = 'no_header'
* 'no_footer'
*/
function print_page($args, $type = '', $mode = '')
{
global $template, $gen_simple_header;
$tpl = (is_array($args) && !empty($args['tpl'])) ? $args['tpl'] : $args;
$tpl = ($type === 'admin') ? ADMIN_TPL_DIR . $tpl : $tpl;
$gen_simple_header = (is_array($args) && !empty($args['simple']) or $type === 'simple') ? true : $gen_simple_header;
if ($mode !== 'no_header') {
require(PAGE_HEADER);
}
$template->set_filenames(['body' => $tpl]);
$template->pparse('body');
if ($mode !== 'no_footer') {
require(PAGE_FOOTER);
}
}
function caching_output($enabled, $mode, $cache_var_name, $ttl = 300)
{
if (!$enabled || !CACHE('bb_cache')->used) {
return;
}
if ($mode == 'send') {
if ($cached_contents = CACHE('bb_cache')->get($cache_var_name)) {
exit($cached_contents);
}
} elseif ($mode == 'store') {
if ($output = ob_get_contents()) {
CACHE('bb_cache')->set($cache_var_name, $output, $ttl);
}
}
}
function clean_title($str, $replace_underscore = false)
{
$str = ($replace_underscore) ? str_replace('_', ' ', $str) : $str;
$str = htmlCHR(str_compact($str));
return $str;
}
function clean_text_match($text, $ltrim_star = true, $die_if_empty = false)
{
global $bb_cfg, $lang;
$text = str_compact($text);
$ltrim_chars = ($ltrim_star) ? ' *-!' : ' ';
$wrap_with_quotes = preg_match('#^"[^"]+"$#', $text);
$text = ' ' . str_compact(ltrim($text, $ltrim_chars)) . ' ';
if ($bb_cfg['search_engine_type'] == 'sphinx') {
$text = preg_replace('#(?<=\S)\-#u', ' ', $text); // "1-2-3" -> "1 2 3"
$text = preg_replace('#[^0-9a-zA-Zа-яА-ЯёЁ\-_*|]#u', ' ', $text); // valid characters (except '"' which are separate)
$text = str_replace(['-', '*'], [' -', '* '], $text); // only at the beginning/end of a word
$text = preg_replace('#\s*\|\s*#u', '|', $text); // "| " -> "|"
$text = preg_replace('#\|+#u', ' | ', $text); // "||" -> "|"
$text = preg_replace('#(?<=\s)[\-*]+\s#u', ' ', $text); // single " - ", " * "
$text = trim($text, ' -|');
$text = str_compact($text);
$text_match_sql = ($wrap_with_quotes && $text != '') ? '"' . $text . '"' : $text;
} else {
$text_match_sql = DB()->escape(trim($text));
}
if (!$text_match_sql && $die_if_empty) {
bb_die($lang['NO_SEARCH_MATCH']);
}
return $text_match_sql;
}
function init_sphinx()
{
global $sphinx;
if (!isset($sphinx)) {
$sphinx = \Sphinx\SphinxClient::create();
$sphinx->setConnectTimeout(5);
$sphinx->setRankingMode($sphinx::SPH_RANK_NONE);
$sphinx->setMatchMode($sphinx::SPH_MATCH_BOOLEAN);
}
return $sphinx;
}
function log_sphinx_error($err_type, $err_msg, $query = '')
{
$ignore_err_txt = [
'negation on top level',
'Query word length is less than min prefix length'
];
if (!count($ignore_err_txt) || !preg_match('#' . implode('|', $ignore_err_txt) . '#i', $err_msg)) {
$orig_query = strtr($_REQUEST['nm'], ["\n" => '\n']);
bb_log(date('m-d H:i:s') . " | $err_type | $err_msg | $orig_query | $query" . LOG_LF, 'sphinx_error');
}
}
function get_title_match_topics($title_match_sql, array $forum_ids = [])
{
global $bb_cfg, $sphinx, $userdata, $title_match, $lang;
$where_ids = [];
if ($forum_ids) {
$forum_ids = array_diff($forum_ids, [0 => 0]);
}
$title_match_sql = encode_text_match($title_match_sql);
if ($bb_cfg['search_engine_type'] == 'sphinx') {
$sphinx = init_sphinx();
$where = $title_match ? 'topics' : 'posts';
$sphinx->setServer($bb_cfg['sphinx_topic_titles_host'], $bb_cfg['sphinx_topic_titles_port']);
if ($forum_ids) {
$sphinx->setFilter('forum_id', $forum_ids, false);
}
if (preg_match('#^"[^"]+"$#u', $title_match_sql)) {
$sphinx->setMatchMode($sphinx::SPH_MATCH_PHRASE);
}
if ($result = $sphinx->query($title_match_sql, $where, $userdata['username'] . ' (' . CLIENT_IP . ')')) {
if (!empty($result['matches'])) {
$where_ids = array_keys($result['matches']);
}
} elseif ($error = $sphinx->getLastError()) {
if (strpos($error, 'errno=110')) {
bb_die($lang['SEARCH_ERROR']);
}
log_sphinx_error('ERR', $error, $title_match_sql);
}
if ($warning = $sphinx->getLastWarning()) {
log_sphinx_error('wrn', $warning, $title_match_sql);
}
} elseif ($bb_cfg['search_engine_type'] == 'mysql') {
$where_forum = ($forum_ids) ? "AND forum_id IN(" . implode(',', $forum_ids) . ")" : '';
$search_bool_mode = ($bb_cfg['allow_search_in_bool_mode']) ? ' IN BOOLEAN MODE' : '';
if ($title_match) {
$where_id = 'topic_id';
$sql = "SELECT topic_id FROM " . BB_TOPICS . "
WHERE MATCH (topic_title) AGAINST ('$title_match_sql'$search_bool_mode)
$where_forum";
} else {
$where_id = 'post_id';
$sql = "SELECT p.post_id FROM " . BB_POSTS . " p, " . BB_POSTS_SEARCH . " ps
WHERE ps.post_id = p.post_id
AND MATCH (ps.search_words) AGAINST ('$title_match_sql'$search_bool_mode)
$where_forum";
}
foreach (DB()->fetch_rowset($sql) as $row) {
$where_ids[] = $row[$where_id];
}
} else {
bb_die($lang['SEARCH_OFF']);
}
return $where_ids;
}
/**
* Encodes text match
*
* Desc: for a more correct search for words containing a single quote
*
* @param $txt
* @return array|string|string[]
*/
function encode_text_match($txt)
{
return str_replace("'", ''', $txt);
}
function decode_text_match($txt)
{
return str_replace(''', "'", $txt);
}
/**
* Create magnet link
*
* @param string $infohash (xt=urn:btih)
* @param string $infohash_v2 (xt=urn:btmh:1220)
* @param string $auth_key (tr)
* @param string $name (dn)
* @param int|string $length (xl)
* @return string
*/
function create_magnet(string $infohash, string $infohash_v2, string $auth_key, string $name, int|string $length = 0): string
{
global $bb_cfg, $images, $lang;
if (!$bb_cfg['magnet_links_enabled']) {
return false;
}
// Only for registered users
if (!$bb_cfg['magnet_links_for_guests'] && IS_GUEST) {
return false;
}
$v1_support = !empty($infohash);
$v2_support = !empty($infohash_v2);
$magnet = 'magnet:?';
if ($v1_support) {
$magnet .= 'xt=urn:btih:' . bin2hex($infohash);
}
if ($v2_support) {
if ($v1_support) {
$magnet .= '&';
}
$magnet .= 'xt=urn:btmh:1220' . bin2hex($infohash_v2);
}
$length = (int)$length;
if ($length > 0) {
$magnet .= '&xl=' . $length;
}
return '
';
}
function set_die_append_msg($forum_id = null, $topic_id = null, $group_id = null)
{
global $lang, $template;
$msg = '';
$msg .= $topic_id ? '' . $lang['TOPIC_RETURN'] . '
' : '';
$msg .= $forum_id ? '' . $lang['FORUM_RETURN'] . '
' : '';
$msg .= $group_id ? '' . $lang['GROUP_RETURN'] . '
' : '';
$msg .= '' . $lang['INDEX_RETURN'] . '
';
$template->assign_var('BB_DIE_APPEND_MSG', $msg);
}
function set_pr_die_append_msg($pr_uid)
{
global $lang, $template;
$template->assign_var('BB_DIE_APPEND_MSG', '
' . $lang['PROFILE_RETURN'] . '
' . $lang['PROFILE_EDIT_RETURN'] . '
' . $lang['INDEX_RETURN'] . '
');
}
function send_pm($user_id, $subject, $message, $poster_id = BOT_UID)
{
global $userdata;
$subject = DB()->escape($subject);
$message = DB()->escape($message);
if ($poster_id == BOT_UID) {
$poster_ip = '0';
} elseif ($row = DB()->fetch_row("SELECT user_reg_ip FROM " . BB_USERS . " WHERE user_id = $poster_id")) {
$poster_ip = $row['user_reg_ip'];
} else {
$poster_id = $userdata['user_id'];
$poster_ip = USER_IP;
}
DB()->query("INSERT INTO " . BB_PRIVMSGS . " (privmsgs_type, privmsgs_subject, privmsgs_from_userid, privmsgs_to_userid, privmsgs_date, privmsgs_ip) VALUES (" . PRIVMSGS_NEW_MAIL . ", '$subject', {$poster_id}, $user_id, " . TIMENOW . ", '$poster_ip')");
$pm_id = DB()->sql_nextid();
DB()->query("INSERT INTO " . BB_PRIVMSGS_TEXT . " (privmsgs_text_id, privmsgs_text) VALUES ($pm_id, '$message')");
DB()->query("UPDATE " . BB_USERS . " SET user_new_privmsg = user_new_privmsg + 1, user_last_privmsg = " . TIMENOW . ", user_newest_pm_id = $pm_id WHERE user_id = $user_id");
}
/**
* Generates link to profile
*
* @param array $data
* @param bool $target_blank
* @param bool $no_link
* @return string
*/
function profile_url(array $data, bool $target_blank = false, bool $no_link = false): string
{
global $bb_cfg, $lang, $datastore;
if (!$ranks = $datastore->get('ranks')) {
$datastore->update('ranks');
$ranks = $datastore->get('ranks');
}
$username = !empty($data['username']) ? $data['username'] : $lang['GUEST'];
$user_id = !empty($data['user_id']) ? (int)$data['user_id'] : GUEST_UID;
$user_rank = !empty($data['user_rank']) ? $data['user_rank'] : 0;
$title = '';
$style = 'colorUser';
if (isset($ranks[$user_rank])) {
$title = $ranks[$user_rank]['rank_title'];
if ($bb_cfg['color_nick']) {
$style = $ranks[$user_rank]['rank_style'];
}
}
if (empty($title)) {
$title = match ($user_id) {
GUEST_UID => $lang['GUEST'],
BOT_UID => $username,
default => $lang['USER'],
};
}
$profile = '' . $username . '';
if (!in_array($user_id, explode(',', EXCLUDED_USERS)) && !$no_link) {
$target_blank = $target_blank ? ' target="_blank" ' : '';
$profile = '' . $profile . '';
}
if (getBanInfo($user_id)) {
return '' . $profile . '';
}
return $profile;
}
function get_avatar($user_id, $ext_id, $allow_avatar = true, $height = '', $width = '')
{
global $bb_cfg;
$height = $height ? 'height="' . $height . '"' : '';
$width = $width ? 'width="' . $width . '"' : '';
$user_avatar = '
';
if ($user_id == BOT_UID && $bb_cfg['avatars']['bot_avatar']) {
$user_avatar = '
';
} elseif ($allow_avatar && $ext_id) {
if (is_file(get_avatar_path($user_id, $ext_id))) {
$user_avatar = '
';
}
}
return $user_avatar;
}
/**
* Returns gender image
*
* @param int $gender
* @return string|null
*/
function genderImage(int $gender): ?string
{
global $bb_cfg, $lang, $images;
if (!$bb_cfg['gender']) {
return false;
}
return match ($gender) {
MALE => '
',
FEMALE => '
',
default => '
',
};
}
function is_gold($type): string
{
global $lang, $bb_cfg, $images;
$type = (int)$type;
$is_gold = '';
if (!$bb_cfg['tracker']['gold_silver_enabled']) {
return $is_gold;
}
switch ($type) {
case TOR_TYPE_GOLD:
$is_gold = '
';
break;
case TOR_TYPE_SILVER:
$is_gold = '
';
break;
default:
break;
}
return $is_gold;
}
function update_atom($type, $id)
{
switch ($type) {
case 'user':
\TorrentPier\Legacy\Atom::update_user_feed($id, get_username($id));
break;
case 'topic':
$topic_poster = (int)DB()->fetch_row("SELECT topic_poster FROM " . BB_TOPICS . " WHERE topic_id = $id LIMIT 1", 'topic_poster');
\TorrentPier\Legacy\Atom::update_user_feed($topic_poster, get_username($topic_poster));
break;
}
}
function hash_search($hash)
{
global $lang;
$hash = htmlCHR(trim($hash));
$info_hash_where = null;
if (!isset($hash) || !ctype_xdigit($hash)) {
bb_die(sprintf($lang['HASH_INVALID'], $hash));
}
$info_hash = DB()->escape(pack('H*', $hash));
// Check info_hash version
if (mb_strlen($hash, DEFAULT_CHARSET) == 40) {
$info_hash_where = "WHERE info_hash = '$info_hash'";
} elseif (mb_strlen($hash, DEFAULT_CHARSET) == 64) {
$info_hash_where = "WHERE info_hash_v2 = '$info_hash'";
} else {
bb_die(sprintf($lang['HASH_INVALID'], $hash));
}
if ($row = DB()->fetch_row("SELECT topic_id FROM " . BB_BT_TORRENTS . " $info_hash_where")) {
redirect(TOPIC_URL . $row['topic_id']);
} else {
bb_die(sprintf($lang['HASH_NOT_FOUND'], $hash));
}
}
/**
* Function for checking captcha answer
*
* @param string $mode
* @return bool|string
*/
function bb_captcha(string $mode): bool|string
{
global $bb_cfg, $lang;
$settings = $bb_cfg['captcha'];
$settings['language'] = $bb_cfg['default_lang'];
// Checking captcha settings
if (!$settings['disabled'] && $settings['service'] !== 'text') {
if (empty($settings['public_key']) || empty($settings['secret_key'])) {
bb_die($lang['CAPTCHA_SETTINGS']);
}
}
// Selecting captcha service
$captchaClasses = [
'googleV2' => \TorrentPier\Captcha\GoogleCaptchaV2::class,
'googleV3' => \TorrentPier\Captcha\GoogleCaptchaV3::class,
'hCaptcha' => \TorrentPier\Captcha\HCaptcha::class,
'yandex' => \TorrentPier\Captcha\YandexSmartCaptcha::class,
'cloudflare' => \TorrentPier\Captcha\CloudflareTurnstileCaptcha::class,
'text' => \TorrentPier\Captcha\TextCaptcha::class
];
if (!isset($captchaClasses[$settings['service']])) {
bb_die(sprintf('Captcha service (%s) not supported', $settings['service']));
}
$captchaClass = $captchaClasses[$settings['service']];
$captcha = new $captchaClass($settings);
// Selection mode
if (isset($captcha)) {
switch ($mode) {
case 'get':
case 'check':
return $captcha->$mode();
default:
bb_die(sprintf('Invalid mode: %s', $mode));
}
}
return false;
}
function clean_tor_dirname($dirname)
{
return str_replace(['[', ']', '<', '>', "'"], ['[', ']', '<', '>', '''], $dirname);
}
/**
* Get birthday icon
*
* @param $user_birthday
* @param $user_id
* @return string
*/
function user_birthday_icon($user_birthday, $user_id): string
{
global $bb_cfg, $images, $lang;
$current_date = bb_date(TIMENOW, 'md', false);
$user_birthday = ($user_id != GUEST_UID && !empty($user_birthday) && $user_birthday != '1900-01-01')
? bb_date(strtotime($user_birthday), 'md', false) : false;
return ($bb_cfg['birthday_enabled'] && $current_date == $user_birthday) ? '
' : '';
}
/**
* Returns information about user ban
*
* @param int|null $userId
* @return array|null
*/
function getBanInfo(?int $userId = null): ?array
{
global $datastore;
// Get bans info from datastore
$bans = $datastore->get('ban_list');
if (!isset($userId)) {
return $bans;
}
return $bans[$userId] ?? [];
}
/**
* Read updater file
*
* @return array|bool
*/
function readUpdaterFile(): array|bool
{
if (!is_file(UPDATER_FILE)) {
return false;
}
return json_decode(file_get_contents(UPDATER_FILE), true);
}
/**
* IP Geolocation API
*
* @param string $ipAddress
* @param int $port
* @return array
*/
function infoByIP(string $ipAddress, int $port = 0): array
{
global $bb_cfg;
if (!$bb_cfg['ip2country_settings']['enabled']) {
return [];
}
$ipAddress = \TorrentPier\Helpers\IPHelper::long2ip_extended($ipAddress);
$cacheName = hash('xxh128', ($ipAddress . '_' . $port));
if (!$data = CACHE('bb_ip2countries')->get($cacheName)) {
$data = [];
$contextOptions = [];
if (!empty($bb_cfg['ip2country_settings']['api_token'])) {
$contextOptions['http'] = [
'header' => "Authorization: Bearer " . $bb_cfg['ip2country_settings']['api_token'] . "\r\n"
];
}
$context = stream_context_create($contextOptions);
try {
$response = file_get_contents($bb_cfg['ip2country_settings']['endpoint'] . $ipAddress, context: $context);
if ($response !== false) {
$json = json_decode($response, true);
if (is_array($json) && !empty($json)) {
$data = [
'ipVersion' => $json['ipVersion'],
'countryCode' => $json['countryCode'],
'continent' => $json['continent'],
'continentCode' => $json['continentCode']
];
}
} else {
bb_log("[FreeIPAPI] Failed to get IP info for: $ipAddress" . LOG_LF);
}
} catch (Exception $e) {
bb_log("[FreeIPAPI] " . $e->getMessage() . LOG_LF);
}
if (empty($data)) {
$data = [
'response' => false,
'timestamp' => TIMENOW
];
}
CACHE('bb_ip2countries')->set($cacheName, $data, 1200);
}
return $data;
}