get('file_id_ext')[$ext_id] ?? '';
return ($base_path ? "$base_path/" : '') . floor($id / $first_div) . '/' . ($id % $sec_div) . '/' . $id . ($ext ? ".$ext" : '');
}
function get_avatar_path($id, $ext_id, $base_path = null, $first_div = 10000, $sec_div = 100)
{
$base_path ??= config()->get('avatars.upload_path');
return get_path_from_id($id, $ext_id, $base_path, $first_div, $sec_div);
}
function get_attach_path($id, $ext_id = '', $base_path = null, $first_div = 10000, $sec_div = 100)
{
$base_path ??= config()->get('attach.upload_path');
return get_path_from_id($id, $ext_id, $base_path, $first_div, $sec_div);
}
function delete_avatar($user_id, $avatar_ext_id)
{
$avatar_file = $avatar_ext_id ? get_avatar_path($user_id, $avatar_ext_id) : false;
return ($avatar_file && is_file($avatar_file) && unlink($avatar_file));
}
function get_tracks($type)
{
switch ($type) {
case 'topic':
$c_name = COOKIE_TOPIC;
break;
case 'forum':
$c_name = COOKIE_FORUM;
break;
case 'pm':
$c_name = COOKIE_PM;
break;
default:
trigger_error(__FUNCTION__ . ": invalid type '$type'", E_USER_ERROR);
}
$tracks = !empty($_COOKIE[$c_name]) ? json_decode($_COOKIE[$c_name], true) : false;
return $tracks ?: [];
}
/**
* Returns array with all banned users
*
* @param bool $return_as_names
* @return array
*/
function get_banned_users(bool $return_as_names = false): array
{
$banned_users = [];
foreach (DB()->fetch_rowset("SELECT ban_userid FROM " . BB_BANLIST . " WHERE ban_userid != 0") as $user) {
$banned_users[] = $return_as_names ? get_username($user['ban_userid']) : $user['ban_userid'];
}
return $banned_users;
}
function set_tracks($cookie_name, &$tracking_ary, $tracks = null, $val = TIMENOW)
{
global $tracking_topics, $tracking_forums, $user;
if (IS_GUEST) {
return;
}
$prev_tracking_ary = $tracking_ary;
if ($tracks) {
if (!is_array($tracks)) {
$tracks = [$tracks => $val];
}
foreach ($tracks as $key => $val) {
$key = (int)$key;
$val++;
$curr_track_val = !empty($tracking_ary[$key]) ? $tracking_ary[$key] : 0;
if ($val > max($curr_track_val, $user->data['user_lastvisit'])) {
$tracking_ary[$key] = $val;
} elseif ($curr_track_val < $user->data['user_lastvisit']) {
unset($tracking_ary[$key]);
}
}
}
$overflow = count($tracking_topics) + count($tracking_forums) - COOKIE_MAX_TRACKS;
if ($overflow > 0) {
arsort($tracking_ary);
for ($i = 0; $i < $overflow; $i++) {
array_pop($tracking_ary);
}
}
if (array_diff($tracking_ary, $prev_tracking_ary)) {
bb_setcookie($cookie_name, json_encode($tracking_ary));
}
}
function get_last_read($topic_id = 0, $forum_id = 0)
{
global $tracking_topics, $tracking_forums, $user;
$t = $tracking_topics[$topic_id] ?? 0;
$f = $tracking_forums[$forum_id] ?? 0;
return max($t, $f, $user->data['user_lastvisit']);
}
function is_unread($ref, $topic_id = 0, $forum_id = 0): bool
{
return (!IS_GUEST && $ref > get_last_read($topic_id, $forum_id));
}
//
// Auth
//
define('AUTH_LIST_ALL', 0);
// forum's ACL types (bb_forums: auth_view, auth_read... values)
define('AUTH_REG', 1);
define('AUTH_ACL', 2);
define('AUTH_ADMIN', 5);
// forum_perm bitfields - backward compatible with auth($type)
define('AUTH_ALL', 0);
define('AUTH_VIEW', 1);
define('AUTH_READ', 2);
define('AUTH_MOD', 3);
define('AUTH_POST', 4);
define('AUTH_REPLY', 5);
define('AUTH_EDIT', 6);
define('AUTH_DELETE', 7);
define('AUTH_STICKY', 8);
define('AUTH_ANNOUNCE', 9);
define('AUTH_VOTE', 10);
define('AUTH_POLLCREATE', 11);
define('AUTH_ATTACH', 12);
define('AUTH_DOWNLOAD', 13);
define('BF_AUTH_MOD', bit2dec(AUTH_MOD));
// When defining user permissions, take into account:
define('UG_PERM_BOTH', 1); // both user and group
define('UG_PERM_USER_ONLY', 2); // only personal user permissions
define('UG_PERM_GROUP_ONLY', 3); // only group permissions
$bf['forum_perm'] = [
'auth_view' => AUTH_VIEW,
'auth_read' => AUTH_READ,
'auth_mod' => AUTH_MOD,
'auth_post' => AUTH_POST,
'auth_reply' => AUTH_REPLY,
'auth_edit' => AUTH_EDIT,
'auth_delete' => AUTH_DELETE,
'auth_sticky' => AUTH_STICKY,
'auth_announce' => AUTH_ANNOUNCE,
'auth_vote' => AUTH_VOTE,
'auth_pollcreate' => AUTH_POLLCREATE,
'auth_attachments' => AUTH_ATTACH,
'auth_download' => AUTH_DOWNLOAD,
];
$bf['user_opt'] = [
# 'dis_opt_name' => PROHIBITIONS used by administrators for users
# 'user_opt_name' => SETTINGS used by users
'user_viewemail' => 0, // [SETTINGS] Show my email for other users
'dis_sig' => 1, // [PROHIBITIONS] Block set signature
'dis_avatar' => 2, // [PROHIBITIONS] Block set avatar
'dis_pm' => 3, // [PROHIBITIONS] Block sending PMs
'user_viewonline' => 4, // [SETTINGS] Hide my online status (time of last activity)
'user_notify' => 5, // [SETTINGS] Report of replies in my "Watching topics" (Via email)
'user_notify_pm' => 6, // [SETTINGS] Report of new PMs in forum (Via email)
'dis_passkey' => 7, // [PROHIBITIONS] Block changing and adding passkey into torrents, also block downloading torrents
'user_porn_forums' => 8, // [SETTINGS] Hide 18+ content (Porn forums)
'user_callseed' => 9, // [SETTINGS] Allow to send "Callseed" PMs
'user_empty' => 10, // [SETTINGS] Block showing advertisements to me (Not used)
'dis_topic' => 11, // [PROHIBITIONS] Block to create new topics
'dis_post' => 12, // [PROHIBITIONS] Block to send replies in topics
'dis_post_edit' => 13, // [PROHIBITIONS] Block editing own posts / topics
'user_dls' => 14, // [SETTINGS] Hide list of "Current downloads" in my profile
'user_retracker' => 15, // [SETTINGS] Add my retracker into downloaded torrent files
'user_hide_torrent_client' => 16, // [SETTINGS] Option to hide user's torrent client in peer list
'user_hide_peer_country' => 17, // [SETTINGS] Option to hide user's country name in peer list
'user_hide_peer_username' => 18, // [SETTINGS] Option to hide peer username in peer list
];
function bit2dec($bit_num)
{
if (is_array($bit_num)) {
$dec = 0;
foreach ($bit_num as $bit) {
$dec |= (1 << $bit);
}
return $dec;
}
return (1 << $bit_num);
}
function bf_bit2dec($bf_array_name, $key)
{
global $bf;
if (!isset($bf[$bf_array_name][$key])) {
trigger_error(__FUNCTION__ . ": bitfield '$key' not found", E_USER_ERROR);
}
return (1 << $bf[$bf_array_name][$key]);
}
function bf($int, $bf_array_name, $key)
{
return (bf_bit2dec($bf_array_name, $key) & (int)$int);
}
function setbit(&$int, $bit_num, $on)
{
return ($on) ? $int |= (1 << $bit_num) : $int &= ~(1 << $bit_num);
}
/*
$type's accepted (pre-pend with AUTH_):
VIEW, READ, POST, REPLY, EDIT, DELETE, STICKY, ANNOUNCE, VOTE, POLLCREATE
Possible options ($type/forum_id combinations):
* If you include a type and forum_id then a specific lookup will be done and
the single result returned
* If you set type to AUTH_ALL and specify a forum_id an array of all auth types
will be returned
* If you provide a forum_id a specific lookup on that forum will be done
* If you set forum_id to AUTH_LIST_ALL and specify a type an array listing the
results for all forums will be returned
* If you set forum_id to AUTH_LIST_ALL and type to AUTH_ALL a multidimensional
array containing the auth permissions for all types and all forums for that
user is returned
All results are returned as associative arrays, even when a single auth type is
specified.
If available you can send an array (either one or two-dimensional) containing the
forum auth levels, this will prevent the auth function having to do its own
lookup
*/
function auth($type, $forum_id, $ug_data, array $f_access = [], $group_perm = UG_PERM_BOTH)
{
global $lang, $bf, $datastore;
$is_guest = true;
$is_admin = false;
$auth = $auth_fields = $u_access = [];
$add_auth_type_desc = ($forum_id != AUTH_LIST_ALL);
// Check forum existence
if (!forum_exists()) {
return [];
}
if ($add_auth_type_desc && !forum_exists($forum_id)) {
return [];
}
//
// Get $auth_fields
//
if ($type == AUTH_ALL) {
$auth_fields = array_keys($bf['forum_perm']);
} elseif ($auth_type = array_search($type, $bf['forum_perm'])) {
$auth_fields = [$auth_type];
}
if (empty($auth_fields)) {
trigger_error(__FUNCTION__ . '(): empty $auth_fields', E_USER_ERROR);
}
//
// Get $f_access
//
// If f_access has been passed, or auth is needed to return an array of forums
// then we need to pull the auth information on the given forum (or all forums)
if (empty($f_access)) {
if (!$forums = $datastore->get('cat_forums')) {
$datastore->update('cat_forums');
$forums = $datastore->get('cat_forums');
}
if ($forum_id == AUTH_LIST_ALL) {
$f_access = $forums['f'];
} elseif (isset($forums['f'][$forum_id])) {
$f_access[$forum_id] = $forums['f'][$forum_id];
}
} elseif (isset($f_access['forum_id'])) {
// Change passed $f_access format for later using in foreach()
$f_access = [$f_access['forum_id'] => $f_access];
}
if (empty($f_access)) {
trigger_error(__FUNCTION__ . '(): empty $f_access', E_USER_ERROR);
}
//
// Get user or group permissions
//
$forum_match_sql = ($forum_id != AUTH_LIST_ALL) ? "AND aa.forum_id = " . (int)$forum_id : '';
// GROUP mode
if (!empty($ug_data['group_id'])) {
$is_guest = false;
$is_admin = false;
$sql = "SELECT aa.forum_id, aa.forum_perm
FROM " . BB_AUTH_ACCESS . " aa
WHERE aa.group_id = " . (int)$ug_data['group_id'] . "
$forum_match_sql";
foreach (DB()->fetch_rowset($sql) as $row) {
$u_access[$row['forum_id']] = $row['forum_perm'];
}
} // USER mode
elseif (!empty($ug_data['user_id'])) {
$is_guest = empty($ug_data['session_logged_in']);
$is_admin = (!$is_guest && $ug_data['user_level'] == ADMIN);
if ($group_perm != UG_PERM_BOTH) {
$group_single_user = ($group_perm == UG_PERM_USER_ONLY) ? 1 : 0;
$sql = "
SELECT
aa.forum_id, BIT_OR(aa.forum_perm) AS forum_perm
FROM
" . BB_USER_GROUP . " ug,
" . BB_GROUPS . " g,
" . BB_AUTH_ACCESS . " aa
WHERE
ug.user_id = " . (int)$ug_data['user_id'] . "
AND ug.user_pending = 0
AND g.group_id = ug.group_id
AND g.group_single_user = $group_single_user
AND aa.group_id = g.group_id
$forum_match_sql
GROUP BY aa.forum_id
";
foreach (DB()->fetch_rowset($sql) as $row) {
$u_access[$row['forum_id']] = $row['forum_perm'];
}
} else {
if (!$is_guest && !$is_admin) {
$sql = "SELECT aa.forum_id, aa.forum_perm
FROM " . BB_AUTH_ACCESS_SNAP . " aa
WHERE aa.user_id = " . (int)$ug_data['user_id'] . "
$forum_match_sql";
foreach (DB()->fetch_rowset($sql) as $row) {
$u_access[$row['forum_id']] = $row['forum_perm'];
}
}
}
}
// If the user is logged on and the forum type is either ALL or REG then the user has access
//
// If the type if ACL, MOD or ADMIN then we need to see if the user has specific permissions
// to do whatever it is they want to do ... to do this we pull relevant information for the
// user (and any groups they belong to)
//
// Now we compare the users access level against the forums. We assume here that a moderator
// and admin automatically have access to an ACL forum, similarly we assume admins meet an
// auth requirement of MOD
//
foreach ($f_access as $f_id => $f_data) {
$auth[$f_id]['auth_mod'] = auth_check('forum_perm', 'auth_mod', $u_access, $f_id, $is_admin);
foreach ($auth_fields as $auth_type) {
if (!isset($f_data[$auth_type])) {
continue;
}
switch ($f_data[$auth_type]) {
case AUTH_ALL:
$auth[$f_id][$auth_type] = true;
break;
case AUTH_REG:
$auth[$f_id][$auth_type] = !$is_guest;
break;
case AUTH_ACL:
$auth[$f_id][$auth_type] = (auth_check('forum_perm', $auth_type, $u_access, $f_id, $is_admin) || $auth[$f_id]['auth_mod']);
break;
case AUTH_MOD:
$auth[$f_id][$auth_type] = $auth[$f_id]['auth_mod'];
break;
case AUTH_ADMIN:
$auth[$f_id][$auth_type] = $is_admin;
break;
default:
$auth[$f_id][$auth_type] = false;
}
if ($add_auth_type_desc) {
$auth[$f_id][$auth_type . '_type'] =& $lang['AUTH_TYPES'][$f_data[$auth_type]];
}
}
}
return ($forum_id == AUTH_LIST_ALL) ? $auth : $auth[$forum_id];
}
function auth_check($bf_ary, $bf_key, $perm_ary, $perm_key, $is_admin = false)
{
if ($is_admin) {
return true;
}
if (!isset($perm_ary[$perm_key])) {
return false;
}
return bf($perm_ary[$perm_key], $bf_ary, $bf_key);
}
function delta_time($timestamp_1, $timestamp_2 = TIMENOW, $granularity = 'auto')
{
return $GLOBALS['DeltaTime']->spellDelta($timestamp_1, $timestamp_2, $granularity);
}
function get_select($select, $selected = null, $return_as = 'html', $first_opt = '»» Выбрать ')
{
$select_name = null;
$select_ary = [];
switch ($select) {
case 'groups':
$sql = "SELECT group_id, group_name FROM " . BB_GROUPS . " WHERE group_single_user = 0 ORDER BY group_name";
foreach (DB()->fetch_rowset($sql) as $row) {
$select_ary[$row['group_name']] = $row['group_id'];
}
$select_name = 'g';
break;
case 'forum_tpl':
$sql = "SELECT tpl_id, tpl_name FROM " . BB_TOPIC_TPL . " ORDER BY tpl_name";
$select_ary[$first_opt] = 0;
foreach (DB()->fetch_rowset($sql) as $row) {
$select_ary[$row['tpl_name']] = $row['tpl_id'];
}
$select_name = 'forum_tpl_select';
break;
}
return ($return_as == 'html') ? build_select($select_name, $select_ary, $selected) : $select_ary;
}
function build_select($name, $params, $selected = null, $max_length = HTML_SELECT_MAX_LENGTH, $multiple_size = null, $js = '')
{
global $html;
return $html->build_select($name, $params, $selected, $max_length, $multiple_size, $js);
}
function build_checkbox($name, $title, $checked = false, $disabled = false, $class = null, $id = null, $value = 1)
{
global $html;
return $html->build_checkbox($name, $title, $checked, $disabled, $class, $id, $value);
}
function replace_quote($str, $double = true, $single = true)
{
if ($double) {
$str = str_replace('"', '"', $str);
}
if ($single) {
$str = str_replace("'", ''', $str);
}
return $str;
}
/**
* Build simple hidden fields from array
*/
function build_hidden_fields($fields_ary)
{
$out = "\n";
foreach ($fields_ary as $name => $val) {
if (is_array($val)) {
foreach ($val as $ary_key => $ary_val) {
$out .= '\n";
}
} else {
$out .= '\n";
}
}
return $out;
}
/**
* Choost russian word declension based on numeric [from dklab.ru]
* Example for $expressions: array("ответ", "ответа", "ответов")
*/
function declension($int, $expressions, $format = '%1$s %2$s')
{
if (!is_array($expressions)) {
$expressions = $GLOBALS['lang']['DECLENSION'][strtoupper($expressions)];
}
if (count($expressions) < 3) {
$expressions[2] = $expressions[1];
}
$count = (int)$int % 100;
if ($count >= 5 && $count <= 20) {
$result = $expressions['2'];
} else {
$count %= 10;
if ($count == 1) {
$result = $expressions['0'];
} elseif ($count >= 2 && $count <= 4) {
$result = $expressions['1'];
} else {
$result = $expressions['2'];
}
}
return ($format) ? sprintf($format, $int, $result) : $result;
}
// http://forum.dklab.ru/php/advises/UrlreplaceargChangesValueOfParameterInUrl.html
function url_arg($url, $arg, $value, $amp = '&')
{
$arg = preg_quote($arg, '/');
// separate URL and ANCHOR
$anchor = '';
if (preg_match('/(.*)(#.*)/s', $url, $m)) {
$url = $m[1];
$anchor = $m[2];
}
// replace the parameter if it exists
if (preg_match("/((\?|&|&)$arg=)[^&]*/s", $url, $m)) {
$cur = $m[0];
$new = null === $value ? '' : $m[1] . urlencode($value);
$url = str_replace($cur, $new, $url);
} // add a parameter
elseif (null !== $value) {
$div = str_contains($url, '?') ? $amp : '?';
$url = $url . $div . $arg . '=' . urlencode($value);
}
return $url . $anchor;
}
/**
* Returns a size formatted in a more human-friendly format, rounded to the nearest GB, MB, KB..
*/
function humn_size($size, $rounder = null, $min = null, $space = ' ')
{
static $sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
static $rounders = [0, 0, 0, 2, 3, 3, 3, 3, 3];
$size = (float)$size;
$ext = $sizes[0];
$rnd = $rounders[0];
if ($min == 'KB' && $size < 1024) {
$size /= 1024;
$ext = 'KB';
$rounder = 1;
} else {
for ($i = 1, $cnt = count($sizes); ($i < $cnt && $size >= 1024); $i++) {
$size /= 1024;
$ext = $sizes[$i];
$rnd = $rounders[$i];
}
}
if (!$rounder) {
$rounder = $rnd;
}
return round($size, $rounder) . $space . $ext;
}
function bt_show_ip($ip, $port = '')
{
if (IS_AM) {
$ip = \TorrentPier\Helpers\IPHelper::long2ip_extended($ip);
if (!empty($port)) {
if (\TorrentPier\Helpers\IPHelper::isValidv6($ip)) {
// Wrap IPv6 address in square brackets
$ip = "[$ip]:$port";
} else {
$ip = "$ip:$port";
}
}
return $ip;
}
return config()->get('bt_show_ip_only_moder') ? false : \TorrentPier\Helpers\IPHelper::anonymizeIP($ip);
}
function bt_show_port($port)
{
if (IS_AM) {
return $port;
}
return config()->get('bt_show_port_only_moder') ? false : $port;
}
function checkbox_get_val(&$key, &$val, $default = 1, $on = 1, $off = 0)
{
global $previous_settings, $search_id;
if (isset($_REQUEST[$key]) && is_string($_REQUEST[$key])) {
$val = (int)$_REQUEST[$key];
} elseif (!isset($_REQUEST[$key]) && isset($_REQUEST['prev_' . $key])) {
$val = $off;
} elseif (isset($previous_settings[$key]) && (!IS_GUEST || !empty($search_id))) {
$val = ($previous_settings[$key]) ? $on : $off;
} else {
$val = $default;
}
}
function select_get_val($key, &$val, $options_ary, $default, $num = true)
{
global $previous_settings;
if (isset($_REQUEST[$key]) && is_string($_REQUEST[$key])) {
if (isset($options_ary[$_REQUEST[$key]])) {
$val = ($num) ? (int)$_REQUEST[$key] : $_REQUEST[$key];
}
} elseif (isset($previous_settings[$key])) {
$val = $previous_settings[$key];
} else {
$val = $default;
}
}
/**
* set_var
*
* Set variable, used by {@link request_var the request_var function}
*
* @access private
*/
function set_var(&$result, $var, $type, $multibyte = false, $strip = true)
{
settype($var, $type);
$result = $var;
if ($type == 'string') {
$result = trim(htmlspecialchars(str_replace(["\r\n", "\r"], ["\n", "\n"], $result)));
if (!empty($result)) {
// Make sure multibyte characters are wellformed
if ($multibyte) {
if (!preg_match('/^./u', $result)) {
$result = '';
}
}
}
$result = ($strip) ? stripslashes($result) : $result;
}
}
/**
* request_var
*
* Used to get passed variable
*/
function request_var($var_name, $default, $multibyte = false, $cookie = false)
{
if (!$cookie && isset($_COOKIE[$var_name])) {
if (!isset($_GET[$var_name], $_POST[$var_name])) {
return (is_array($default)) ? [] : $default;
}
$_REQUEST[$var_name] = $_POST[$var_name] ?? $_GET[$var_name];
}
if (!isset($_REQUEST[$var_name]) || (is_array($_REQUEST[$var_name]) && !is_array($default)) || (is_array($default) && !is_array($_REQUEST[$var_name]))) {
return (is_array($default)) ? [] : $default;
}
$var = $_REQUEST[$var_name];
if (!is_array($default)) {
$type = gettype($default);
} else {
[$key_type, $type] = $default;
$type = gettype($type);
$key_type = gettype($key_type);
if ($type == 'array') {
reset($default);
$default = current($default);
[$sub_key_type, $sub_type] = $default;
$sub_type = gettype($sub_type);
$sub_type = ($sub_type == 'array') ? 'NULL' : $sub_type;
$sub_key_type = gettype($sub_key_type);
}
}
if (is_array($var)) {
$_var = $var;
$var = [];
foreach ($_var as $k => $v) {
set_var($k, $k, $key_type);
if ($type == 'array' && is_array($v)) {
foreach ($v as $_k => $_v) {
if (is_array($_v)) {
$_v = null;
}
set_var($_k, $_k, $sub_key_type);
set_var($var[$k][$_k], $_v, $sub_type, $multibyte);
}
} else {
if ($type == 'array' || is_array($v)) {
$v = null;
}
set_var($var[$k], $v, $type, $multibyte);
}
}
} else {
set_var($var, $var, $type, $multibyte);
}
return $var;
}
function get_username($user_id)
{
if (empty($user_id)) {
return is_array($user_id) ? [] : false;
}
if (is_array($user_id)) {
$usernames = [];
foreach (DB()->fetch_rowset("SELECT user_id, username FROM " . BB_USERS . " WHERE user_id IN(" . get_id_csv($user_id) . ")") as $row) {
$usernames[$row['user_id']] = $row['username'];
}
return $usernames;
}
$row = DB()->fetch_row("SELECT username FROM " . BB_USERS . " WHERE user_id = '" . DB()->escape($user_id) . "' LIMIT 1");
return $row['username'];
}
function get_user_id($username)
{
if (empty($username)) {
return false;
}
if ($row = DB()->fetch_row("SELECT user_id FROM " . BB_USERS . " WHERE username = '" . DB()->escape($username) . "' LIMIT 1")) {
return $row['user_id'];
}
return false;
}
function str_short($text, $max_length, $space = ' ')
{
if (!empty($max_length) && !empty($text) && (mb_strlen($text, DEFAULT_CHARSET) > $max_length)) {
$text = mb_substr($text, 0, $max_length, DEFAULT_CHARSET);
if ($last_space_pos = $max_length - (int)strpos(strrev($text), (string)$space)) {
if ($last_space_pos > round($max_length * 3 / 4)) {
$last_space_pos--;
$text = mb_substr($text, 0, $last_space_pos, DEFAULT_CHARSET);
}
}
$text .= '...';
$text = preg_replace('!?(\w+)?;?(\w{1,5})?\.\.\.$!', '...', $text);
}
return $text ?? '';
}
function generate_user_info($row, bool $have_auth = IS_ADMIN): array
{
global $userdata, $lang, $images;
$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 = config()->get('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 = (config()->get('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 = config()->get('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 clean_username($username)
{
$username = mb_substr(htmlspecialchars(str_replace("\'", "'", trim($username))), 0, 25, DEFAULT_CHARSET);
$username = rtrim($username, "\\");
$username = str_replace("'", "\'", $username);
return $username;
}
/**
* 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;
if (!config()->get('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 $template, $userdata;
// AdminCP works only with default template
$tpl_dir_name = defined('IN_ADMIN') ? 'default' : basename(config()->get('tpl_name'));
$stylesheet = defined('IN_ADMIN') ? 'main.css' : basename(config()->get('stylesheet'));
if (!IS_GUEST && !empty($userdata['tpl_name'])) {
foreach (config()->get('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([
'SPACER' => make_url('styles/images/spacer.gif'),
'STYLESHEET' => make_url($css_dir . $stylesheet),
'EXT_LINK_NEW_WIN' => config()->get('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 $lang, $userdata;
$gmepoch = (int)$gmepoch;
if (!$format) {
$format = config()->get('default_dateformat');
}
if (empty($lang)) {
lang()->initializeLanguage();
}
if (!defined('IS_GUEST') || IS_GUEST) {
$tz = config()->get('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 (config()->get('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
{
static $iconExtension = '.png';
$bestMatch = null;
$bestMatchLength = 0;
foreach (config()->get('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)
{
if (!$date) {
return '';
}
$tz = TIMENOW + (3600 * config()->get('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 = '' . $lang['GOTO_PAGE'] . ' : ' . $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, $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, initialize language singleton
if (empty($lang)) {
lang()->initializeLanguage();
}
// 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/" . config()->get('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)
{
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 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)
{
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 = (config()->get('cookie_secure')) ? 'https://' : 'http://';
$server_name = preg_replace('#^\/?(.*?)\/?$#', '\1', trim(config()->get('server_name')));
$server_port = (config()->get('server_port') <> 80) ? ':' . trim(config()->get('server_port')) : '';
$script_name = preg_replace('#^\/?(.*?)\/?$#', '\1', trim(config()->get('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 $images;
$t_hot = ($topic['topic_replies'] >= config()->get('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 $lang;
$text = str_compact($text);
$ltrim_chars = ($ltrim_star) ? ' *-!' : ' ';
$wrap_with_quotes = preg_match('#^"[^"]+"$#', $text);
$text = ' ' . str_compact(ltrim($text, $ltrim_chars)) . ' ';
if (config()->get('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 $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 (config()->get('search_engine_type') == 'sphinx') {
$sphinx = init_sphinx();
$where = $title_match ? 'topics' : 'posts';
$sphinx->setServer(config()->get('sphinx_topic_titles_host'), config()->get('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 (config()->get('search_engine_type') == 'mysql') {
$where_forum = ($forum_ids) ? "AND forum_id IN(" . implode(',', $forum_ids) . ")" : '';
$search_bool_mode = (config()->get('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 $images, $lang;
if (!config()->get('magnet_links_enabled')) {
return false;
}
// Only for registered users
if (!config()->get('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 'get('passkey_key') . "=$auth_key") . '&dn=' . urlencode($name) . '">
';
}
function set_die_append_msg($forum_id = null, $topic_id = null, $group_id = null)
{
global $lang, $template;
$msg = '';
$msg .= $topic_id ? '