PageRenderTime 72ms CodeModel.GetById 3ms app.highlight 53ms RepoModel.GetById 2ms app.codeStats 0ms

/upload/includes/functions.php

Relevant Search: With Applications for Solr and Elasticsearch

For more in depth reading about search, ranking and generally everything you could ever want to know about how lucene, elasticsearch or solr work under the hood I highly suggest this book. Easily one of the most interesting technical books I have read in a long time. If you are tasked with solving search relevance problems even if not in Solr or Elasticsearch it should be your first reference. Amazon Affiliate Link
http://torrentpier2.googlecode.com/
PHP | 2954 lines | 2350 code | 343 blank | 261 comment | 394 complexity | 549746820059d5ae3ae502a7fff17e21 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1<?php
   2
   3if (!defined('BB_ROOT')) die(basename(__FILE__));
   4
   5function get_path_from_id ($id, $ext_id, $base_path, $first_div, $sec_div)
   6{
   7	global $bb_cfg;
   8	$ext = isset($bb_cfg['file_id_ext'][$ext_id]) ? $bb_cfg['file_id_ext'][$ext_id] : '';
   9	return ($base_path ? "$base_path/" : '') . floor($id/$first_div) .'/'. ($id % $sec_div) .'/'. $id . ($ext ? ".$ext" : '');
  10}
  11
  12function get_avatar_path ($id, $ext_id, $base_path = '')
  13{
  14	return get_path_from_id($id, $ext_id, $base_path, 5000000, 100);
  15}
  16
  17function delete_avatar ($user_id, $avatar_ext_id)
  18{
  19	global $bb_cfg;
  20	$avatar_file = ($avatar_ext_id) ? get_avatar_path($user_id, $avatar_ext_id, $bb_cfg['avatars']['upload_path']) : '';
  21	return ($avatar_file && file_exists($avatar_file)) ? @unlink($avatar_file) : false;
  22}
  23
  24function get_attach_path ($id)
  25{
  26	global $bb_cfg;
  27	return get_path_from_id($id, '', $bb_cfg['attach']['upload_path'], 1000000, 100);
  28}
  29
  30function get_tracks ($type)
  31{
  32	static $pattern = '#^a:\d+:{[i:;\d]+}$#';
  33
  34	switch ($type)
  35	{
  36		case 'topic':
  37			$c_name = COOKIE_TOPIC;
  38			break;
  39		case 'forum':
  40			$c_name = COOKIE_FORUM;
  41			break;
  42		case 'pm':
  43			$c_name = COOKIE_PM;
  44			break;
  45		default:
  46			trigger_error(__FUNCTION__ .": invalid type '$type'", E_USER_ERROR);
  47	}
  48	$tracks = !empty($_COOKIE[$c_name]) ? @unserialize($_COOKIE[$c_name]) : false;
  49	return ($tracks) ? $tracks : array();
  50}
  51
  52function set_tracks ($cookie_name, &$tracking_ary, $tracks = null, $val = TIMENOW)
  53{
  54	global $tracking_topics, $tracking_forums, $user;
  55
  56	if (IS_GUEST) return;
  57
  58	$prev_tracking_ary = $tracking_ary;
  59
  60	if ($tracks)
  61	{
  62		if (!is_array($tracks))
  63		{
  64			$tracks = array($tracks => $val);
  65		}
  66		foreach ($tracks as $key => $val)
  67		{
  68			$key = (int) $key;
  69			$val++;
  70			$curr_track_val = !empty($tracking_ary[$key]) ? $tracking_ary[$key] : 0;
  71
  72			if ($val > max($curr_track_val, $user->data['user_lastvisit']))
  73			{
  74				$tracking_ary[$key] = $val;
  75			}
  76			elseif ($curr_track_val < $user->data['user_lastvisit'])
  77			{
  78				unset($tracking_ary[$key]);
  79			}
  80		}
  81	}
  82
  83	$overflow = count($tracking_topics) + count($tracking_forums) - COOKIE_MAX_TRACKS;
  84
  85	if ($overflow > 0)
  86	{
  87		arsort($tracking_ary);
  88		for ($i=0; $i < $overflow; $i++)
  89		{
  90			array_pop($tracking_ary);
  91		}
  92	}
  93
  94	if (array_diff($tracking_ary, $prev_tracking_ary))
  95	{
  96		bb_setcookie($cookie_name, serialize($tracking_ary));
  97	}
  98}
  99
 100function get_last_read ($topic_id = 0, $forum_id = 0)
 101{
 102	global $tracking_topics, $tracking_forums, $user;
 103
 104	$t = isset($tracking_topics[$topic_id]) ? $tracking_topics[$topic_id] : 0;
 105	$f = isset($tracking_forums[$forum_id]) ? $tracking_forums[$forum_id] : 0;
 106	return max($t, $f, $user->data['user_lastvisit']);
 107}
 108
 109function is_unread ($ref, $topic_id = 0, $forum_id = 0)
 110{
 111	return (!IS_GUEST && $ref > get_last_read($topic_id, $forum_id));
 112}
 113
 114//
 115// Ads
 116//
 117class ads_common
 118{
 119	var $ad_blocks  = array();
 120	var $active_ads = array();
 121
 122	/**
 123	*  Constructor
 124	*/
 125	function ads_common ()
 126	{
 127		global $bb_cfg;
 128
 129		$this->ad_blocks  =& $bb_cfg['ad_blocks'];
 130		$this->active_ads = !empty($bb_cfg['active_ads']) ? unserialize($bb_cfg['active_ads']) : array();
 131	}
 132
 133	/**
 134	*  Get ads to show for each block
 135	*/
 136	function get ($block_types)
 137	{
 138		$ads = array();
 139
 140		if ($this->active_ads)
 141		{
 142			$block_ids = $this->get_block_ids($block_types);
 143
 144			if ($ad_ids = $this->get_ad_ids($block_ids))
 145			{
 146				$ad_html = $this->get_ads_html();
 147
 148				foreach ($ad_ids as $block_id => $ad_id)
 149				{
 150					$ads[$block_id] =& $ad_html[$ad_id];
 151				}
 152			}
 153		}
 154
 155		return $ads;
 156	}
 157
 158	/**
 159	*  Get ads html
 160	*/
 161	function get_ads_html ()
 162	{
 163		global $datastore;
 164		if (!$ads_html = $datastore->get('ads'))
 165		{
 166			$datastore->update('ads');
 167			$ads_html = $datastore->get('ads');
 168		}
 169
 170		return $ads_html;
 171	}
 172
 173	/**
 174	*  Get block_ids for specified block_types
 175	*/
 176	function get_block_ids ($block_types)
 177	{
 178		$block_ids = array();
 179
 180		foreach ($block_types as $block_type)
 181		{
 182			if ($blocks =& $this->ad_blocks[$block_type])
 183			{
 184				$block_ids = array_merge($block_ids, array_keys($blocks));
 185			}
 186		}
 187
 188		return $block_ids;
 189	}
 190
 191	/**
 192	*  Get ad_ids for specified blocks
 193	*/
 194	function get_ad_ids ($block_ids)
 195	{
 196		$ad_ids = array();
 197
 198		foreach ($block_ids as $block_id)
 199		{
 200			if ($ads =& $this->active_ads[$block_id])
 201			{
 202				shuffle($ads);
 203				$ad_ids[$block_id] = $ads[0];
 204			}
 205		}
 206
 207		return $ad_ids;
 208	}
 209}
 210
 211//
 212// Auth
 213//
 214define('AUTH_LIST_ALL', 0);
 215
 216// forum's ACL types (bb_forums: auth_view, auth_read... values)
 217define('AUTH_REG',   1);
 218define('AUTH_ACL',   2);
 219define('AUTH_ADMIN', 5);
 220
 221// forum_perm bitfields - backward compatible with auth($type)
 222define('AUTH_ALL',        0);
 223define('AUTH_VIEW',       1);
 224define('AUTH_READ',       2);
 225define('AUTH_MOD',        3);
 226define('AUTH_POST',       4);
 227define('AUTH_REPLY',      5);
 228define('AUTH_EDIT',       6);
 229define('AUTH_DELETE',     7);
 230define('AUTH_STICKY',     8);
 231define('AUTH_ANNOUNCE',   9);
 232define('AUTH_VOTE',       10);
 233define('AUTH_POLLCREATE', 11);
 234define('AUTH_ATTACH',     12);
 235define('AUTH_DOWNLOAD',   13);
 236
 237define('BF_AUTH_MOD', bit2dec(AUTH_MOD));
 238
 239// When defining user permissions, take into account:
 240define('UG_PERM_BOTH',       1);  // both user and group
 241define('UG_PERM_USER_ONLY',  2);  // only personal user permissions
 242define('UG_PERM_GROUP_ONLY', 3);  // only group permissions
 243
 244$bf['forum_perm'] = array(
 245	'auth_view'        => AUTH_VIEW,
 246	'auth_read'        => AUTH_READ,
 247	'auth_mod'         => AUTH_MOD,
 248	'auth_post'        => AUTH_POST,
 249	'auth_reply'       => AUTH_REPLY,
 250	'auth_edit'        => AUTH_EDIT,
 251	'auth_delete'      => AUTH_DELETE,
 252	'auth_sticky'      => AUTH_STICKY,
 253	'auth_announce'    => AUTH_ANNOUNCE,
 254	'auth_vote'        => AUTH_VOTE,
 255	'auth_pollcreate'  => AUTH_POLLCREATE,
 256	'auth_attachments' => AUTH_ATTACH,
 257	'auth_download'    => AUTH_DOWNLOAD,
 258);
 259
 260$bf['user_opt'] = array(
 261	'viewemail'        => 0,  // ?????????? e-mail
 262	'allow_sig'        => 1,  // ?????? ?? ???????
 263	'allow_avatar'     => 2,  // ?????? ?? ??????
 264	'allow_pm'         => 3,  // ?????? ?? ???????? ??
 265	'allow_viewonline' => 4,  // ???????? ?????????? ????????????
 266	'notify'           => 5,  // ???????? ?? ??????? ? ????????????? ?????
 267	'notify_pm'        => 6,  // ???????? ? ????? ??
 268	'allow_passkey'    => 7,  // ?????? ?? ?????????? passkey, ?? ?? ?????? ?? ?????????? ?????????
 269	'hide_porn_forums' => 8,  // ???????? pron ??????
 270	'allow_gallery'    => 9,  // ?????????????? (?????? ????????????? ???????)
 271	'hide_ads'         => 10, // ?????? ?? ????? ???????
 272	'allow_topic'      => 11, // ?????? ?? ???????? ????? ???
 273	'allow_post'       => 12, // ?????? ?? ???????? ?????????
 274	'allow_post_edit'  => 13, // ?????? ?? ?????????????? ?????????
 275	'allow_dls'        => 14, // ?????? ?? ?????? ??????? ??????? ? ???????
 276);
 277
 278function bit2dec ($bit_num)
 279{
 280	if (is_array($bit_num))
 281	{
 282		$dec = 0;
 283		foreach ($bit_num as $bit)
 284		{
 285			$dec |= (1 << $bit);
 286		}
 287		return $dec;
 288	}
 289	return (1 << $bit_num);
 290}
 291
 292function bf_bit2dec ($bf_array_name, $key)
 293{
 294	global $bf;
 295	if (!isset($bf[$bf_array_name][$key]))
 296	{
 297		trigger_error(__FUNCTION__ .": bitfield '$key' not found", E_USER_ERROR);
 298	}
 299	return (1 << $bf[$bf_array_name][$key]);
 300}
 301
 302function bf ($int, $bf_array_name, $key)
 303{
 304	return (bf_bit2dec($bf_array_name, $key) & (int) $int);
 305}
 306
 307function setbit (&$int, $bit_num, $on)
 308{
 309	return ($on) ? $int |= (1 << $bit_num) : $int &= ~(1 << $bit_num);
 310}
 311
 312/*
 313	$type's accepted (pre-pend with AUTH_):
 314	VIEW, READ, POST, REPLY, EDIT, DELETE, STICKY, ANNOUNCE, VOTE, POLLCREATE
 315
 316	Possible options ($type/forum_id combinations):
 317
 318	* If you include a type and forum_id then a specific lookup will be done and
 319	the single result returned
 320
 321	* If you set type to AUTH_ALL and specify a forum_id an array of all auth types
 322	will be returned
 323
 324	* If you provide a forum_id a specific lookup on that forum will be done
 325
 326	* If you set forum_id to AUTH_LIST_ALL and specify a type an array listing the
 327	results for all forums will be returned
 328
 329	* If you set forum_id to AUTH_LIST_ALL and type to AUTH_ALL a multidimensional
 330	array containing the auth permissions for all types and all forums for that
 331	user is returned
 332
 333	All results are returned as associative arrays, even when a single auth type is
 334	specified.
 335
 336	If available you can send an array (either one or two dimensional) containing the
 337	forum auth levels, this will prevent the auth function having to do its own
 338	lookup
 339*/
 340function auth ($type, $forum_id, $ug_data, $f_access = array(), $group_perm = UG_PERM_BOTH)
 341{
 342	global $lang, $bf, $datastore;
 343
 344	$is_guest = true;
 345	$is_admin = false;
 346	$auth = $auth_fields = $u_access = array();
 347	$add_auth_type_desc = ($forum_id != AUTH_LIST_ALL);
 348
 349	//
 350	// Get $auth_fields
 351	//
 352	if ($type == AUTH_ALL)
 353	{
 354		$auth_fields = array_keys($bf['forum_perm']);
 355	}
 356	else if ($auth_type = array_search($type, $bf['forum_perm']))
 357	{
 358		$auth_fields = array($auth_type);
 359	}
 360
 361	if (empty($auth_fields))
 362	{
 363		trigger_error(__FUNCTION__ .'(): empty $auth_fields', E_USER_ERROR);
 364	}
 365
 366	//
 367	// Get $f_access
 368	//
 369	// If f_access has been passed, or auth is needed to return an array of forums
 370	// then we need to pull the auth information on the given forum (or all forums)
 371	if (empty($f_access))
 372	{
 373		if (!$forums = $datastore->get('cat_forums'))
 374		{
 375			$datastore->update('cat_forums');
 376			$forums = $datastore->get('cat_forums');
 377		}
 378
 379		if ($forum_id == AUTH_LIST_ALL)
 380		{
 381			$f_access = $forums['f'];
 382		}
 383		else if (isset($forums['f'][$forum_id]))
 384		{
 385			$f_access[$forum_id] = $forums['f'][$forum_id];
 386		}
 387	}
 388	else if (isset($f_access['forum_id']))
 389	{
 390		// Change passed $f_access format for later using in foreach()
 391		$f_access = array($f_access['forum_id'] => $f_access);
 392	}
 393
 394	if (empty($f_access))
 395	{
 396		trigger_error(__FUNCTION__ .'(): empty $f_access', E_USER_ERROR);
 397	}
 398
 399	//
 400	// Get user or group permissions
 401	//
 402	$forum_match_sql = ($forum_id != AUTH_LIST_ALL) ? "AND aa.forum_id = ". (int) $forum_id : '';
 403
 404	// GROUP mode
 405	if (!empty($ug_data['group_id']))
 406	{
 407		$is_guest = false;
 408		$is_admin = false;
 409
 410		$sql = "SELECT aa.forum_id, aa.forum_perm
 411			FROM ". BB_AUTH_ACCESS ." aa
 412			WHERE aa.group_id = ". (int) $ug_data['group_id'] ."
 413				$forum_match_sql";
 414
 415		foreach (DB()->fetch_rowset($sql) as $row)
 416		{
 417			$u_access[$row['forum_id']] = $row['forum_perm'];
 418		}
 419	}
 420	// USER mode
 421	else if (!empty($ug_data['user_id']))
 422	{
 423		$is_guest = empty($ug_data['session_logged_in']);
 424		$is_admin = (!$is_guest && $ug_data['user_level'] == ADMIN);
 425
 426		if ($group_perm != UG_PERM_BOTH)
 427		{
 428			$group_single_user = ($group_perm == UG_PERM_USER_ONLY) ? 1 : 0;
 429
 430			$sql = "
 431				SELECT
 432					aa.forum_id, BIT_OR(aa.forum_perm) AS forum_perm
 433				FROM
 434					". BB_USER_GROUP  ." ug,
 435					". BB_GROUPS      ." g,
 436					". BB_AUTH_ACCESS ." aa
 437				WHERE
 438					    ug.user_id = ". (int) $ug_data['user_id'] ."
 439					AND ug.user_pending = 0
 440					AND g.group_id = ug.group_id
 441					AND g.group_single_user = $group_single_user
 442					AND aa.group_id = g.group_id
 443						$forum_match_sql
 444					GROUP BY aa.forum_id
 445			";
 446
 447			foreach (DB()->fetch_rowset($sql) as $row)
 448			{
 449				$u_access[$row['forum_id']] = $row['forum_perm'];
 450			}
 451		}
 452		else
 453		{
 454			if (!$is_guest && !$is_admin)
 455			{
 456				$sql = "SELECT SQL_CACHE aa.forum_id, aa.forum_perm
 457					FROM ". BB_AUTH_ACCESS_SNAP ." aa
 458					WHERE aa.user_id = ". (int) $ug_data['user_id'] ."
 459						$forum_match_sql";
 460
 461				foreach (DB()->fetch_rowset($sql) as $row)
 462				{
 463					$u_access[$row['forum_id']] = $row['forum_perm'];
 464				}
 465			}
 466		}
 467	}
 468
 469	// If the user is logged on and the forum type is either ALL or REG then the user has access
 470	//
 471	// If the type if ACL, MOD or ADMIN then we need to see if the user has specific permissions
 472	// to do whatever it is they want to do ... to do this we pull relevant information for the
 473	// user (and any groups they belong to)
 474	//
 475	// Now we compare the users access level against the forums. We assume here that a moderator
 476	// and admin automatically have access to an ACL forum, similarly we assume admins meet an
 477	// auth requirement of MOD
 478	//
 479	foreach ($f_access as $f_id => $f_data)
 480	{
 481		$auth[$f_id]['auth_mod'] = auth_check('forum_perm', 'auth_mod', $u_access, $f_id, $is_admin);
 482
 483		foreach ($auth_fields as $auth_type)
 484		{
 485			if (!isset($f_data[$auth_type]))
 486			{
 487				continue;
 488			}
 489			switch ($f_data[$auth_type])
 490			{
 491				case AUTH_ALL:
 492					$auth[$f_id][$auth_type] = true;
 493					break;
 494
 495				case AUTH_REG:
 496					$auth[$f_id][$auth_type] = !$is_guest;
 497					break;
 498
 499				case AUTH_ACL:
 500					$auth[$f_id][$auth_type] = (auth_check('forum_perm', $auth_type, $u_access, $f_id, $is_admin) || $auth[$f_id]['auth_mod']);
 501					break;
 502
 503				case AUTH_MOD:
 504					$auth[$f_id][$auth_type] = $auth[$f_id]['auth_mod'];
 505					break;
 506
 507				case AUTH_ADMIN:
 508					$auth[$f_id][$auth_type] = $is_admin;
 509					break;
 510
 511				default:
 512					$auth[$f_id][$auth_type] = false;
 513			}
 514			if ($add_auth_type_desc)
 515			{
 516				$auth[$f_id][$auth_type .'_type'] =& $lang['AUTH_TYPES'][$f_data[$auth_type]];
 517			}
 518		}
 519	}
 520
 521	return ($forum_id == AUTH_LIST_ALL) ? $auth : $auth[$forum_id];
 522}
 523
 524function auth_check ($bf_ary, $bf_key, $perm_ary, $perm_key, $is_admin = false)
 525{
 526	if ($is_admin) return true;
 527	if (!isset($perm_ary[$perm_key])) return false;
 528
 529	return bf($perm_ary[$perm_key], $bf_ary, $bf_key);
 530}
 531
 532class Date_Delta
 533{
 534	var $auto_granularity = array(
 535		60        => 'seconds',   // set granularity to "seconds" if delta less then 1 minute
 536		10800     => 'minutes',   // 3 hours
 537		259200    => 'hours',     // 3 days
 538		31363200  => 'mday',      // 12 months
 539		311040000 => 'mon',       // 10 years
 540	);
 541	var $intervals = array();
 542	var $format    = '';
 543
 544	// Creates new object.
 545	function Date_Delta()
 546	{
 547		global $lang;
 548
 549		$this->intervals = $lang['DELTA_TIME']['INTERVALS'];
 550		$this->format = $lang['DELTA_TIME']['FORMAT'];
 551	}
 552
 553	// Makes the spellable phrase.
 554	function spellDelta($first, $last, $from = 'auto')
 555	{
 556		if ($last < $first)
 557		{
 558			$old_first = $first;
 559			$first = $last;
 560			$last = $old_first;
 561		}
 562
 563		if ($from == 'auto')
 564		{
 565			$from = 'year';
 566			$diff = $last - $first;
 567			foreach ($this->auto_granularity as $seconds_count => $granule)
 568			{
 569				if ($diff < $seconds_count)
 570				{
 571					$from = $granule;
 572					break;
 573				}
 574			}
 575		}
 576
 577		// Solve data delta.
 578		$delta = $this->getDelta($first, $last);
 579		if (!$delta) return false;
 580
 581		// Make spellable phrase.
 582		$parts = array();
 583		$intervals = $GLOBALS['lang']['DELTA_TIME']['INTERVALS'];
 584
 585		foreach (array_reverse($delta) as $k => $n)
 586		{
 587			if (!$n)
 588			{
 589				if ($k == $from)
 590				{
 591					if (!$parts)
 592					{
 593						$parts[] = declension($n, $this->intervals[$k], $this->format);
 594					}
 595					break;
 596				}
 597				continue;
 598			}
 599			$parts[] = declension($n, $this->intervals[$k], $this->format);
 600			if ($k == $from) break;
 601		}
 602		return join(' ', $parts);
 603	}
 604
 605	// returns the associative array with date deltas.
 606	function getDelta($first, $last)
 607	{
 608		if ($last < $first) return false;
 609
 610		// Solve H:M:S part.
 611		$hms = ($last - $first) % (3600 * 24);
 612		$delta['seconds'] = $hms % 60;
 613		$delta['minutes'] = floor($hms/60) % 60;
 614		$delta['hours']   = floor($hms/3600) % 60;
 615
 616		// Now work only with date, delta time = 0.
 617		$last -= $hms;
 618		$f = getdate($first);
 619		$l = getdate($last); // the same daytime as $first!
 620
 621		$dYear = $dMon = $dDay = 0;
 622
 623		// Delta day. Is negative, month overlapping.
 624		$dDay += $l['mday'] - $f['mday'];
 625		if ($dDay < 0) {
 626			$monlen = $this->monthLength(date('Y', $first), date('m', $first));
 627			$dDay += $monlen;
 628			$dMon--;
 629		}
 630		$delta['mday'] = $dDay;
 631
 632		// Delta month. If negative, year overlapping.
 633		$dMon += $l['mon'] - $f['mon'];
 634		if ($dMon < 0) {
 635			$dMon += 12;
 636			$dYear --;
 637		}
 638		$delta['mon'] = $dMon;
 639
 640		// Delta year.
 641		$dYear += $l['year'] - $f['year'];
 642		$delta['year'] = $dYear;
 643
 644		return $delta;
 645	}
 646
 647	// Returns the length (in days) of the specified month.
 648	function monthLength($year, $mon)
 649	{
 650		$l = 28;
 651		while (checkdate($mon, $l+1, $year)) $l++;
 652		return $l;
 653	}
 654}
 655
 656function delta_time ($timestamp_1, $timestamp_2 = TIMENOW, $granularity = 'auto')
 657{
 658	return $GLOBALS['DeltaTime']->spellDelta($timestamp_1, $timestamp_2, $granularity);
 659}
 660
 661function get_select ($select, $selected = null, $return_as = 'html', $first_opt = '&raquo;&raquo; ??????? ')
 662{
 663	$select_ary = array();
 664
 665	switch ($select)
 666	{
 667		case 'groups':
 668			$sql = "SELECT group_id, group_name FROM ". BB_GROUPS ." WHERE group_single_user = 0 ORDER BY group_name";
 669			foreach (DB()->fetch_rowset($sql) as $row)
 670			{
 671				$select_ary[$row['group_name']] = $row['group_id'];
 672			}
 673			$select_name = 'g';
 674			break;
 675
 676		case 'forum_tpl':
 677			$sql = "SELECT tpl_id, tpl_name FROM ". BB_TOPIC_TPL ." ORDER BY tpl_name";
 678			$select_ary[$first_opt] = 0;
 679			foreach (DB()->fetch_rowset($sql) as $row)
 680			{
 681				$select_ary[$row['tpl_name']] = $row['tpl_id'];
 682			}
 683			$select_name = 'forum_tpl_select';
 684			break;
 685	}
 686
 687	return ($return_as == 'html') ? build_select($select_name, $select_ary, $selected) : $select_ary;
 688}
 689
 690class html_common
 691{
 692	var $options    = '';
 693	var $attr       = array();
 694	var $cur_attr   = null;
 695	var $max_length = HTML_SELECT_MAX_LENGTH;
 696	var $selected   = array();
 697
 698	function build_select ($name, $params, $selected = null, $max_length = HTML_SELECT_MAX_LENGTH, $multiple_size = null, $js = '')
 699	{
 700		if (empty($params)) return '';
 701
 702		$this->options = '';
 703		$this->selected = array_flip((array) $selected);
 704		$this->max_length = $max_length;
 705
 706		$this->attr = array();
 707		$this->cur_attr =& $this->attr;
 708
 709		if (isset($params['__attributes']))
 710		{
 711			$this->attr = $params['__attributes'];
 712			unset($params['__attributes']);
 713		}
 714
 715		$this->_build_select_rec($params);
 716
 717		$select_params  = ($js) ? " $js" : '';
 718		$select_params .= ($multiple_size) ? ' multiple="multiple" size="'. $multiple_size .'"' : '';
 719		$select_params .= ' name="'. htmlCHR($name) .'"';
 720		$select_params .= ' id="'. htmlCHR($name) .'"';
 721
 722		return "\n<select $select_params>\n". $this->options ."</select>\n";
 723	}
 724
 725	function _build_select_rec ($params)
 726	{
 727		foreach ($params as $opt_name => $opt_val)
 728		{
 729			$opt_name = rtrim($opt_name);
 730
 731			if (is_array($opt_val))
 732			{
 733				$this->cur_attr =& $this->cur_attr[$opt_name];
 734
 735				$label = htmlCHR(str_short($opt_name, $this->max_length));
 736
 737				$this->options .= "\t<optgroup label=\"&nbsp;". $label ."\">\n";
 738				$this->_build_select_rec($opt_val);
 739				$this->options .= "\t</optgroup>\n";
 740
 741				$this->cur_attr =& $this->attr;
 742			}
 743			else
 744			{
 745				$text  = htmlCHR(str_short($opt_name, $this->max_length));
 746				$value = ' value="'. htmlCHR($opt_val) .'"';
 747
 748				$class = isset($this->cur_attr[$opt_name]['class']) ? ' class="'. $this->cur_attr[$opt_name]['class'] .'"' : '';
 749				$style = isset($this->cur_attr[$opt_name]['style']) ? ' style="'. $this->cur_attr[$opt_name]['style'] .'"' : '';
 750
 751				$selected = isset($this->selected[$opt_val]) ? HTML_SELECTED : '';
 752				$disabled = isset($this->cur_attr[$opt_name]['disabled']) ? HTML_DISABLED : '';
 753
 754				$this->options .= "\t\t<option". $class . $style . $selected . $disabled . $value .'>&nbsp;'. $text ."&nbsp;</option>\n";
 755			}
 756		}
 757	}
 758
 759	function array2html ($array, $ul = 'ul', $li = 'li')
 760	{
 761		$this->out = '';
 762		$this->_array2html_rec($array, $ul, $li);
 763		return "<$ul class=\"tree-root\">{$this->out}</$ul>";
 764	}
 765
 766	function _array2html_rec ($array, $ul, $li)
 767	{
 768		foreach ($array as $k => $v)
 769		{
 770			if (is_array($v))
 771			{
 772				$this->out .= "<$li><span class=\"b\">$k</span><$ul>";
 773				$this->_array2html_rec($v, $ul, $li);
 774				$this->out .= "</$ul></$li>";
 775			}
 776			else
 777			{
 778				$this->out .= "<$li><span>$v</span></$li>";
 779			}
 780		}
 781	}
 782
 783	// all arguments should be already htmlspecialchar()d (if needed)
 784	function build_checkbox ($name, $title, $checked = false, $disabled = false, $class = null, $id = null, $value = 1)
 785	{
 786		$name     = ' name="'. $name .'" ';
 787		$value    = ' value="'. $value .'" ';
 788		$title    = ($class) ? '<span class="'. $class .'">'. $title .'</span>' : $title;
 789		$id       = ($id) ? " id=\"$id\" " : '';
 790		$checked  = ($checked) ? HTML_CHECKED : '';
 791		$disabled = ($disabled) ? HTML_DISABLED : '';
 792
 793		return '<label><input type="checkbox" '. $id . $name . $value . $checked . $disabled .' />&nbsp;'. $title .'&nbsp;</label>';
 794	}
 795
 796#	function build_option ($opt_name, $opt_val, $selected = null, $max_length = false)
 797#	{
 798#		return "\t\t<option value=\"". htmlCHR($opt_val) .'"'. (($selected) ? ' selected="selected"' : '') .'>'. htmlCHR(str_short($opt_name, $max_length)) ."</option>\n";
 799#	}
 800
 801#	function build_optgroup ($label, $contents, $max_length = false)
 802#	{
 803#		return "\t<optgroup label=\"&nbsp;". htmlCHR(str_short($label, $max_length)) ."\">\n". $contents ."\t</optgroup>\n";
 804#	}
 805}
 806
 807function build_select ($name, $params, $selected = null, $max_length = HTML_SELECT_MAX_LENGTH, $multiple_size = null, $js = '')
 808{
 809	global $html;
 810	return $html->build_select($name, $params, $selected, $max_length, $multiple_size, $js);
 811}
 812
 813function build_checkbox ($name, $title, $checked = false, $disabled = false, $class = null, $id = null, $value = 1)
 814{
 815	global $html;
 816	return $html->build_checkbox($name, $title, $checked, $disabled, $class, $id, $value);
 817}
 818
 819function replace_quote ($str, $double = true, $single = true)
 820{
 821	if ($double) $str = str_replace('"', '&quot;', $str);
 822	if ($single) $str = str_replace("'", '&#039;', $str);
 823	return $str;
 824}
 825
 826/**
 827* Build simple hidden fields from array
 828*/
 829function build_hidden_fields ($fields_ary)
 830{
 831	$out = "\n";
 832
 833	foreach ($fields_ary as $name => $val)
 834	{
 835		if (is_array($val))
 836		{
 837			foreach ($val as $ary_key => $ary_val)
 838			{
 839				$out .= '<input type="hidden" name="'. $name .'['. $ary_key .']" value="'. $ary_val ."\" />\n";
 840			}
 841		}
 842		else
 843		{
 844			$out .= '<input type="hidden" name="'. $name .'" value="'. $val ."\" />\n";
 845		}
 846	}
 847
 848	return $out;
 849}
 850
 851/**
 852 * Choost russian word declension based on numeric [from dklab.ru]
 853 * Example for $expressions: array("?????", "??????", "???????")
 854 */
 855function declension ($int, $expressions, $format = '%1$s %2$s')
 856{
 857	if (!is_array($expressions))
 858	{
 859		$expressions = $GLOBALS['lang']['DECLENSION'][strtoupper($expressions)];
 860	}
 861
 862	if (count($expressions) < 3)
 863	{
 864		$expressions[2] = $expressions[1];
 865	}
 866	$count = intval($int) % 100;
 867
 868	if ($count >= 5 && $count <= 20)
 869	{
 870		$result = $expressions['2'];
 871	}
 872	else
 873	{
 874		$count = $count % 10;
 875		if ($count == 1)
 876		{
 877			$result = $expressions['0'];
 878		}
 879		elseif ($count >= 2 && $count <= 4)
 880		{
 881			$result = $expressions['1'];
 882		}
 883		else
 884		{
 885			$result = $expressions['2'];
 886		}
 887	}
 888
 889	return ($format) ? sprintf($format, $int, $result) : $result;
 890}
 891
 892// http://forum.dklab.ru/php/advises/UrlreplaceargChangesValueOfParameterInUrl.html
 893function url_arg ($url, $arg, $value, $amp = '&amp;')
 894{
 895	$arg = preg_quote($arg, '/');
 896
 897	// ????????? URL ? ANCHOR
 898	$anchor = '';
 899	if (preg_match('/(.*)(#.*)/s', $url, $m))
 900	{
 901		$url    = $m[1];
 902		$anchor = $m[2];
 903	}
 904	// ???????? ????????, ???? ?? ??????????
 905	if (preg_match("/((\?|&|&amp;)$arg=)[^&]*/s", $url, $m))
 906	{
 907		$cur = $m[0];
 908		$new = is_null($value) ? '' : $m[1] . urlencode($value);
 909		$url = str_replace($cur, $new, $url);
 910	}
 911	// ????????? ????????
 912	else if (!is_null($value))
 913	{
 914		$div = (strpos($url, '?') !== false) ? $amp : '?';
 915		$url = $url . $div . $arg .'='. urlencode($value);
 916	}
 917	return $url . $anchor;
 918}
 919
 920/**
 921 * Adds commas between every group of thousands
 922 */
 923function commify ($number)
 924{
 925	return number_format($number);
 926}
 927
 928/**
 929 * Returns a size formatted in a more human-friendly format, rounded to the nearest GB, MB, KB..
 930 */
 931function humn_size ($size, $rounder = null, $min = null, $space = '&nbsp;')
 932{
 933	static $sizes   = array('B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB');
 934	static $rounders = array(0,   0,    0,    2,    3,    3,    3,    3,    3);
 935
 936	$size = (float) $size;
 937	$ext = $sizes[0];
 938	$rnd = $rounders[0];
 939
 940	if ($min == 'KB' && $size < 1024)
 941	{
 942		$size = $size / 1024;
 943		$ext  = 'KB';
 944		$rounder = 1;
 945	}
 946	else
 947	{
 948		for ($i=1, $cnt=count($sizes); ($i < $cnt && $size >= 1024); $i++)
 949		{
 950			$size = $size / 1024;
 951			$ext  = $sizes[$i];
 952			$rnd  = $rounders[$i];
 953		}
 954	}
 955	if (!$rounder)
 956	{
 957		$rounder = $rnd;
 958	}
 959
 960	return round($size, $rounder) . $space . $ext;
 961}
 962
 963function bt_show_ip ($ip, $port = '')
 964{
 965	global $bb_cfg;
 966
 967	if (IS_AM)
 968	{
 969		$ip = decode_ip($ip);
 970		$ip .= ($port) ? ":$port" : '';
 971		return $ip;
 972	}
 973	else
 974	{
 975		return ($bb_cfg['bt_show_ip_only_moder']) ? false : decode_ip_xx($ip);
 976	}
 977}
 978
 979function bt_show_port ($port)
 980{
 981	global $bb_cfg;
 982
 983	if (IS_AM)
 984	{
 985		return $port;
 986	}
 987	else
 988	{
 989		return ($bb_cfg['bt_show_port_only_moder']) ? false : $port;
 990	}
 991}
 992
 993function decode_ip_xx ($ip)
 994{
 995	$h = explode('.', chunk_split($ip, 2, '.'));
 996	return hexdec($h[0]) .'.'. hexdec($h[1]) .'.'. hexdec($h[2]) .'.xx';
 997}
 998
 999function checkbox_get_val (&$key, &$val, $default = 1, $on = 1, $off = 0)
1000{
1001	global $previous_settings, $search_id;
1002
1003	if (isset($_REQUEST[$key]) && is_string($_REQUEST[$key]))
1004	{
1005		$val = (int) $_REQUEST[$key];
1006	}
1007	else if (!isset($_REQUEST[$key]) && isset($_REQUEST['prev_'. $key]))
1008	{
1009		$val = $off;
1010	}
1011	else if (isset($previous_settings[$key]) && (!IS_GUEST || !empty($search_id)))
1012	{
1013		$val = ($previous_settings[$key]) ? $on : $off;
1014	}
1015	else
1016	{
1017		$val = $default;
1018	}
1019}
1020
1021function select_get_val ($key, &$val, $options_ary, $default, $num = true)
1022{
1023	global $previous_settings;
1024
1025	if (isset($_REQUEST[$key]) && is_string($_REQUEST[$key]))
1026	{
1027		if (isset($options_ary[$_REQUEST[$key]]))
1028		{
1029			$val = ($num) ? intval($_REQUEST[$key]) : $_REQUEST[$key];
1030		}
1031	}
1032	else if (isset($previous_settings[$key]))
1033	{
1034		$val = $previous_settings[$key];
1035	}
1036	else
1037	{
1038		$val = $default;
1039	}
1040}
1041
1042/**
1043* set_var
1044*
1045* Set variable, used by {@link request_var the request_var function}
1046*
1047* @access private
1048*/
1049function set_var(&$result, $var, $type, $multibyte = false, $strip = true)
1050{
1051	settype($var, $type);
1052	$result = $var;
1053
1054	if ($type == 'string')
1055	{
1056		$result = trim(htmlspecialchars(str_replace(array("\r\n", "\r"), array("\n", "\n"), $result)));
1057
1058		if (!empty($result))
1059		{
1060			// Make sure multibyte characters are wellformed
1061			if ($multibyte)
1062			{
1063				if (!preg_match('/^./u', $result))
1064				{
1065					$result = '';
1066				}
1067			}
1068		}
1069
1070		$result = ($strip) ? stripslashes($result) : $result;
1071	}
1072}
1073/**
1074* request_var
1075*
1076* Used to get passed variable
1077*/
1078function request_var($var_name, $default, $multibyte = false, $cookie = false)
1079{
1080	if (!$cookie && isset($_COOKIE[$var_name]))
1081	{
1082		if (!isset($_GET[$var_name]) && !isset($_POST[$var_name]))
1083		{
1084			return (is_array($default)) ? array() : $default;
1085		}
1086		$_REQUEST[$var_name] = isset($_POST[$var_name]) ? $_POST[$var_name] : $_GET[$var_name];
1087	}
1088
1089	if (!isset($_REQUEST[$var_name]) || (is_array($_REQUEST[$var_name]) && !is_array($default)) || (is_array($default) && !is_array($_REQUEST[$var_name])))
1090	{
1091		return (is_array($default)) ? array() : $default;
1092	}
1093
1094	$var = $_REQUEST[$var_name];
1095	if (!is_array($default))
1096	{
1097		$type = gettype($default);
1098	}
1099	else
1100	{
1101		list($key_type, $type) = each($default);
1102		$type = gettype($type);
1103		$key_type = gettype($key_type);
1104		if ($type == 'array')
1105		{
1106			reset($default);
1107			$default = current($default);
1108			list($sub_key_type, $sub_type) = each($default);
1109			$sub_type = gettype($sub_type);
1110			$sub_type = ($sub_type == 'array') ? 'NULL' : $sub_type;
1111			$sub_key_type = gettype($sub_key_type);
1112		}
1113	}
1114
1115	if (is_array($var))
1116	{
1117		$_var = $var;
1118		$var = array();
1119
1120		foreach ($_var as $k => $v)
1121		{
1122			set_var($k, $k, $key_type);
1123			if ($type == 'array' && is_array($v))
1124			{
1125				foreach ($v as $_k => $_v)
1126				{
1127					if (is_array($_v))
1128					{
1129						$_v = null;
1130					}
1131					set_var($_k, $_k, $sub_key_type);
1132					set_var($var[$k][$_k], $_v, $sub_type, $multibyte);
1133				}
1134			}
1135			else
1136			{
1137				if ($type == 'array' || is_array($v))
1138				{
1139					$v = null;
1140				}
1141				set_var($var[$k], $v, $type, $multibyte);
1142			}
1143		}
1144	}
1145	else
1146	{
1147		set_var($var, $var, $type, $multibyte);
1148	}
1149
1150	return $var;
1151}
1152
1153function get_username ($user_id)
1154{
1155	if (empty($user_id))
1156	{
1157		return is_array($user_id) ? array() : false;
1158	}
1159	if (is_array($user_id))
1160	{
1161		$usernames = array();
1162		foreach (DB()->fetch_rowset("SELECT user_id, username FROM ". BB_USERS ." WHERE user_id IN(". get_id_csv($user_id) .")") as $row)
1163		{
1164			$usernames[$row['user_id']] = $row['username'];
1165		}
1166		return $usernames;
1167	}
1168	else
1169	{
1170		$row = DB()->fetch_row("SELECT username FROM ". BB_USERS ." WHERE user_id = $user_id LIMIT 1");
1171		return $row['username'];
1172	}
1173}
1174
1175function get_user_id ($username)
1176{
1177	if (empty($username)) return false;
1178	$row = DB()->fetch_row("SELECT user_id FROM ". BB_USERS ." WHERE username = '". DB()->escape($username) ."' LIMIT 1");
1179	return $row['user_id'];
1180}
1181
1182function str_short ($text, $max_length, $space = ' ')
1183{
1184	if ($max_length && mb_strlen($text, 'UTF-8') > $max_length)
1185	{
1186		$text = mb_substr($text, 0, $max_length, 'UTF-8');
1187
1188		if ($last_space_pos = $max_length - intval(strpos(strrev($text), $space)))
1189		{
1190			if ($last_space_pos > round($max_length * 3/4))
1191			{
1192				$last_space_pos--;
1193				$text = mb_substr($text, 0, $last_space_pos, 'UTF-8');
1194			}
1195		}
1196		$text .= '...';
1197		$text = preg_replace('!&#?(\w+)?;?(\w{1,5})?\.\.\.$!', '...', $text);
1198	}
1199
1200	return $text;
1201}
1202
1203function wbr ($text, $max_word_length = HTML_WBR_LENGTH)
1204{
1205	return preg_replace("/([\w\->;:.,~!?(){}@#$%^*\/\\\\]{". $max_word_length ."})/ui", '$1<wbr>', $text);
1206}
1207
1208function get_bt_userdata ($user_id)
1209{
1210	return DB()->fetch_row("SELECT bt.*, SUM(tr.speed_up) as speed_up, SUM(tr.speed_down) as speed_down
1211                            FROM      ". BB_BT_USERS   ." bt
1212                            LEFT JOIN ". BB_BT_TRACKER ." tr ON (bt.user_id = tr.user_id)
1213                            WHERE bt.user_id = ". (int) $user_id ."
1214                            GROUP BY bt.user_id");
1215}
1216
1217function get_bt_ratio ($btu)
1218{
1219	return
1220		(!empty($btu['u_down_total']) && $btu['u_down_total'] > MIN_DL_FOR_RATIO)
1221		? round((($btu['u_up_total'] + $btu['u_up_release'] + $btu['u_up_bonus']) / $btu['u_down_total']), 2)
1222		: null
1223	;
1224}
1225
1226function show_bt_userdata ($user_id)
1227{
1228	global $lang, $template;
1229
1230	$btu = get_bt_userdata($user_id);
1231
1232	$template->assign_vars(array(
1233		'SHOW_BT_USERDATA' => true,
1234		'UP_TOTAL'         => humn_size($btu['u_up_total']),
1235		'UP_BONUS'         => humn_size($btu['u_up_bonus']),
1236		'RELEASED'         => humn_size($btu['u_up_release']),
1237		'DOWN_TOTAL'       => humn_size($btu['u_down_total']),
1238		'DOWN_TOTAL_BYTES' => $btu['u_down_total'],
1239		'USER_RATIO'       => get_bt_ratio($btu),
1240		'MIN_DL_FOR_RATIO' => humn_size(MIN_DL_FOR_RATIO),
1241		'MIN_DL_BYTES'     => MIN_DL_FOR_RATIO,
1242		'AUTH_KEY'         => ($btu['auth_key']) ? $btu['auth_key'] : $lang['NONE'],
1243
1244		'TD_DL'            => humn_size($btu['down_today']),
1245		'TD_UL'            => humn_size($btu['up_today']),
1246		'TD_REL'           => humn_size($btu['up_release_today']),
1247		'TD_BONUS'         => humn_size($btu['up_bonus_today']),
1248		'TD_POINTS'        => ($btu['auth_key']) ? $btu['points_today'] : '0.00',
1249
1250		'YS_DL'            => humn_size($btu['down_yesterday']),
1251		'YS_UL'            => humn_size($btu['up_yesterday']),
1252		'YS_REL'           => humn_size($btu['up_release_yesterday']),
1253		'YS_BONUS'         => humn_size($btu['up_bonus_yesterday']),
1254		'YS_POINTS'        => ($btu['auth_key']) ? $btu['points_yesterday'] : '0.00',
1255
1256		'SPEED_UP'         => humn_size($btu['speed_up'], 0, 'KB') .'/s',
1257		'SPEED_DOWN'       => humn_size($btu['speed_down'], 0, 'KB') .'/s',
1258	));
1259}
1260
1261function get_attachments_dir ($cfg = null)
1262{
1263	if (!$cfg AND !$cfg = $GLOBALS['attach_config'])
1264	{
1265		$cfg = bb_get_config(BB_ATTACH_CONFIG, true, false);
1266	}
1267
1268	if (!$cfg['allow_ftp_upload'])
1269	{
1270		if ($cfg['upload_dir'][0] == '/' || ($cfg['upload_dir'][0] != '/' && $cfg['upload_dir'][1] == ':'))
1271		{
1272			return $cfg['upload_dir'];
1273		}
1274		else
1275		{
1276			return BB_ROOT . $cfg['upload_dir'];
1277		}
1278	}
1279	else
1280	{
1281		return $cfg['download_path'];
1282	}
1283}
1284
1285function bb_get_config ($table, $from_db = false, $update_cache = true)
1286{
1287	if ($from_db OR !$cfg = CACHE('bb_cache')->get("config_{$table}"))
1288	{
1289		$cfg = array();
1290		foreach (DB()->fetch_rowset("SELECT * FROM $table") as $row)
1291		{
1292			$cfg[$row['config_name']] = $row['config_value'];
1293		}
1294		if ($update_cache)
1295		{
1296			CACHE('bb_cache')->set("config_{$table}", $cfg);
1297		}
1298	}
1299	return $cfg;
1300}
1301
1302function bb_update_config ($params, $table = BB_CONFIG)
1303{
1304	$updates = array();
1305	foreach ($params as $name => $val)
1306	{
1307		$updates[] = array(
1308			'config_name'  => $name,
1309			'config_value' => $val,
1310		);
1311	}
1312	$updates = DB()->build_array('MULTI_INSERT', $updates);
1313
1314	DB()->query("REPLACE INTO $table $updates");
1315	// Update cache
1316	bb_get_config($table, true, true);
1317}
1318
1319function get_db_stat($mode)
1320{
1321	switch( $mode )
1322	{
1323		case 'usercount':
1324			$sql = "SELECT COUNT(user_id) AS total
1325				FROM " . BB_USERS;
1326			break;
1327
1328		case 'newestuser':
1329			$sql = "SELECT user_id, username
1330				FROM " . BB_USERS . "
1331				WHERE user_id <> " . GUEST_UID . "
1332				ORDER BY user_id DESC
1333				LIMIT 1";
1334			break;
1335
1336		case 'postcount':
1337		case 'topiccount':
1338			$sql = "SELECT SUM(forum_topics) AS topic_total, SUM(forum_posts) AS post_total
1339				FROM " . BB_FORUMS;
1340			break;
1341	}
1342
1343	if ( !($result = DB()->sql_query($sql)) )
1344	{
1345		return false;
1346	}
1347
1348	$row = DB()->sql_fetchrow($result);
1349
1350	switch ( $mode )
1351	{
1352		case 'usercount':
1353			return $row['total'];
1354			break;
1355		case 'newestuser':
1356			return $row;
1357			break;
1358		case 'postcount':
1359			return $row['post_total'];
1360			break;
1361		case 'topiccount':
1362			return $row['topic_total'];
1363			break;
1364	}
1365
1366	return false;
1367}
1368
1369function clean_username($username)
1370{
1371	$username = mb_substr(htmlspecialchars(str_replace("\'", "'", trim($username))), 0, 25, 'UTF-8');
1372	$username = bb_rtrim($username, "\\");
1373	$username = str_replace("'", "\'", $username);
1374
1375	return $username;
1376}
1377
1378function bb_ltrim($str, $charlist = false)
1379{
1380	if ($charlist === false)
1381	{
1382		return ltrim($str);
1383	}
1384
1385	$str = ltrim($str, $charlist);
1386
1387	return $str;
1388}
1389
1390function bb_rtrim($str, $charlist = false)
1391{
1392	if ($charlist === false)
1393	{
1394		return rtrim($str);
1395	}
1396
1397	$str = rtrim($str, $charlist);
1398
1399	return $str;
1400}
1401
1402// Get Userdata, $u can be username or user_id. If $force_name is true, the username will be forced.
1403function get_userdata ($u, $force_name = false, $allow_guest = false)
1404{
1405	if (!$u) return false;
1406
1407	if (intval($u) == GUEST_UID && $allow_guest)
1408	{
1409		if ($u_data = CACHE('bb_cache')->get('guest_userdata'))
1410		{
1411			return $u_data;
1412		}
1413	}
1414
1415	$u_data = array();
1416	$name_search = false;
1417	$exclude_anon_sql = (!$allow_guest) ? "AND user_id != ". GUEST_UID : '';
1418
1419	if ($force_name || !is_numeric($u))
1420	{
1421		$name_search = true;
1422		$where_sql = "WHERE username = '". DB()->escape(clean_username($u)) ."'";
1423	}
1424	else
1425	{
1426		$where_sql = "WHERE user_id = ". (int) $u;
1427	}
1428
1429	$sql = "SELECT * FROM ". BB_USERS ." $where_sql $exclude_anon_sql LIMIT 1";
1430
1431	if (!$u_data = DB()->fetch_row($sql))
1432	{
1433		if (!is_int($u) && !$name_search)
1434		{
1435			$where_sql = "WHERE username = '". DB()->escape(clean_username($u)) ."'";
1436			$sql = "SELECT * FROM ". BB_USERS ." $where_sql $exclude_anon_sql LIMIT 1";
1437			$u_data = DB()->fetch_row($sql);
1438		}
1439	}
1440
1441	if ($u_data['user_id'] == GUEST_UID)
1442	{
1443		CACHE('bb_cache')->set('guest_userdata', $u_data);
1444	}
1445
1446	return $u_data;
1447}
1448
1449function make_jumpbox ($selected = 0)
1450{
1451	global $datastore, $template;
1452
1453	if (!$jumpbox = $datastore->get('jumpbox'))
1454	{
1455		$datastore->update('jumpbox');
1456		$jumpbox = $datastore->get('jumpbox');
1457	}
1458
1459	$template->assign_vars(array(
1460		'JUMPBOX' => (IS_GUEST) ? $jumpbox['guest'] : $jumpbox['user'],
1461	));
1462}
1463
1464// $mode: array(not_auth_forum1,not_auth_forum2,..) or (string) 'mode'
1465function 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)
1466{
1467	global $lang, $datastore;
1468
1469	if (is_array($mode))
1470	{
1471		$not_auth_forums_fary = array_flip($mode);
1472		$mode = 'not_auth_forums';
1473	}
1474	if (is_null($max_length))
1475	{
1476		$max_length = HTML_SELECT_MAX_LENGTH;
1477	}
1478	$select = is_null($all_forums_option) ? array() : array($lang['ALL_AVAILABLE'] => $all_forums_option);
1479	if (!$forums = $datastore->get('cat_forums'))
1480	{
1481		$datastore->update('cat_forums');
1482		$forums = $datastore->get('cat_forums');
1483	}
1484
1485	foreach ($forums['f'] as $fid => $f)
1486	{
1487		switch ($mode)
1488		{
1489			case 'guest':
1490				if ($f['auth_view'] != AUTH_ALL) continue 2;
1491				break;
1492
1493			case 'user':
1494				if ($f['auth_view'] != AUTH_ALL && $f['auth_view'] != AUTH_REG) continue 2;
1495				break;
1496
1497			case 'not_auth_forums':
1498				if (isset($not_auth_forums_fary[$f['forum_id']])) continue 2;
1499				break;
1500
1501			case 'admin':
1502				break;
1503
1504			default:
1505				trigger_error(__FUNCTION__ .": invalid mode '$mode'", E_USER_ERROR);
1506		}
1507		$cat_title = $forums['c'][$f['cat_id']]['cat_title'];
1508		$f_name = ($f['forum_parent']) ? ' |- ' : '';
1509		$f_name .= $f['forum_name'];
1510
1511		while (isset($select[$cat_title][$f_name]))
1512		{
1513			$f_name .= ' ';
1514		}
1515
1516		$select[$cat_title][$f_name] = $fid;
1517
1518		if (!$f['forum_parent'])
1519		{
1520			$class = 'root_forum';
1521			$class .= isset($f['subforums']) ? ' has_sf' : '';
1522			$select['__attributes'][$cat_title][$f_name]['class'] = $class;
1523		}
1524	}
1525
1526	return build_select($name, $select, $selected, $max_length, $multiple_size, $js);
1527}
1528
1529function setup_style ()
1530{
1531	global $bb_cfg, $template, $userdata;
1532
1533	// AdminCP works only with default template
1534	$tpl_dir_name = defined('IN_ADMIN') ? 'default'   : basename($bb_cfg['tpl_name']);
1535	$stylesheet   = defined('IN_ADMIN') ? 'main.css'  : basename($bb_cfg['stylesheet']);
1536
1537	if (!IS_GUEST && !empty($userdata['tpl_name']))
1538	{
1539		foreach ($bb_cfg['templates'] as $folder => $name)
1540		{
1541			if ($userdata['tpl_name'] == $folder) $tpl_dir_name = basename($userdata['tpl_name']);
1542		}
1543	}
1544
1545	$template = new Template(TEMPLATES_DIR . $tpl_dir_name);
1546	$css_dir = basename(TEMPLATES_DIR) ."/$tpl_dir_name/css/";
1547
1548	$template->assign_vars(array(
1549		'BB_ROOT'          => BB_ROOT,
1550		'SPACER'           => make_url('/images/spacer.gif'),
1551		'STYLESHEET'       => make_url($css_dir . $stylesheet),
1552		'EXT_LINK_NEW_WIN' => $bb_cfg['ext_link_new_win'],
1553		'TPL_DIR'          => make_url($css_dir),
1554		'SITE_URL'         => make_url('/'),
1555	));
1556
1557	require(TEMPLATES_DIR . $tpl_dir_name .'/tpl_config.php');
1558
1559	$theme = array('template_name' => $tpl_dir_name);
1560
1561	return $theme;
1562}
1563
1564// Create date/time from format and timezone
1565function bb_date ($gmepoch, $format = false, $tz = null)
1566{
1567	global $bb_cfg, $lang, $userdata;
1568
1569	if (!$format) $format = $bb_cfg['default_dateformat'];
1570	if (empty($lang)) require_once($bb_cfg['default_lang_dir'] .'lang_main.php');
1571
1572	if (is_null($tz) || $tz == 'false')
1573	{
1574		if (empty($userdata['session_logged_in']))
1575		{
1576			$tz2 = $bb_cfg['board_timezone'];
1577		}
1578		else $tz2 = $userdata['user_timezone'];
1579	}
1580	elseif (is_numeric($tz)) $tz2 = $tz;
1581
1582	$date = gmdate($format, $gmepoch + (3600 * $tz2));
1583
1584	if ($tz != 'false')
1585	{
1586		$time_format = " H:i";
1587
1588		$today = gmdate("d", TIMENOW + (3600 * $tz2));
1589		$month = gmdate("m", TIMENOW + (3600 * $tz2));
1590		$year  = gmdate("Y", TIMENOW + (3600 * $tz2));
1591
1592		$date_today = gmdate("d", $gmepoch + (3600 * $tz2));
1593		$date_month = gmdate("m", $gmepoch + (3600 * $tz2));
1594		$date_year  = gmdate("Y", $gmepoch + (3600 * $tz2));
1595
1596		if ($date_today == $today && $date_month == $month && $date_year == $year)
1597		{
1598			$date = 'today' . gmdate($time_format, $gmepoch + (3600 * $tz2));
1599		}
1600		elseif ($today != 1 && $date_today == ($today-1) && $date_month == $month && $date_year == $year)
1601		{
1602			$date = 'yesterday' . gmdate($time_format, $gmepoch + (3600 * $tz2));
1603		}
1604		elseif ($today == 1 && $month != 1)
1605		{
1606			$yesterday = date ("t", mktime(0, 0, 0, ($month-1), 1, $year));
1607			if ($date_today == $yesterday && $date_month == ($month-1) && $date_year == $year)
1608				$date = 'yesterday' . gmdate($time_format, $gmepoch + (3600 * $tz2));
1609		}
1610		elseif ($today == 1 && $month == 1)
1611		{
1612			$yesterday = date ("t", mktime(0, 0, 0, 12, 1, ($year -1)));
1613			if ($date_today == $yesterday && $date_month == 12 && $date_year == ($year-1))
1614				$date = 'yesterday' . gmdate($time_format, $gmepoch + (3600 * $tz));
1615		}
1616	}
1617
1618	return ($bb_cfg['translate_dates']) ? strtr(strtoupper($date), $lang['DATETIME']) : $date;
1619}
1620
1621// Birthday
1622// Add function mkrealdate for Birthday MOD
1623// the originate php "mktime()", does not work proberly on all OS, especially when going back in time
1624// before year 1970 (year 0), this function "mkrealtime()", has a mutch larger valid date range,
1625// from 1901 - 2099. it returns a "like" UNIX timestamp divided by 86400, so
1626// calculation from the originate php date and mktime is easy.
1627// mkrealdate, returns the number of day (with sign) from 1.1.1970.
1628function mkrealdate ($day, $month, $birth_year)
1629{
1630	// define epoch
1631	$epoch = 0;
1632	// range check months
1633	if ($month < 1 || $month > 12) return "error";
1634	// range check days
1635	switch ($month)
1636	{
1637		case 1: if ($day > 31) return "error"; break;
1638		case 2: if ($day > 29) return "error";
1639			$epoch = $epoch+31; break;
1640		case 3: if ($day > 31) return "error";
1641			$epoch = $epoch+59; break;
1642		case 4: if ($day > 30) return "error" ;
1643			$epoch = $epoch+90; break;
1644		case 5: if ($day > 31) return "error";
1645			$epoch = $epoch+120; break;
1646		case 6: if ($day > 30) return "error";
1647			$epoch = $epoch+151; break;
1648		case 7: if ($day > 31) return "error";
1649			$epoch = $epoch+181; break;
1650		case 8: if ($day > 31) return "error";
1651			$epoch = $epoch+212; break;
1652		case 9: if ($day > 30) return "error";
1653			$epoch = $epoch+243; break;
1654		case 10: if ($day > 31) return "error";
1655			$epoch = $epoch+273; break;
1656		case 11: if ($day > 30) return "error";
1657			$epoch = $epoch+304; break;
1658		case 12: if ($day > 31) return "error";
1659			$epoch = $epoch+334; break;
1660	}
1661	$epoch = $epoch+$day;
1662	$epoch_Y = sqrt(($birth_year-1970)*($birth_year-1970));
1663	$leapyear = round((($epoch_Y+2) / 4)-.5);
1664	if (($epoch_Y+2)%4 == 0)
1665	{// curent year is leapyear
1666		$leapyear--;
1667		if ($birth_year > 1970 && $month >= 3) $epoch = $epoch+1;
1668		if ($birth_year < 1970 && $month < 3) $epoch = $epoch-1;
1669	}
1670	else if ($month == 2 && $day > 28) return "error";//only 28 days in feb.
1671	//year
1672	if ($birth_year > 1970)
1673	{
1674		$epoch = $epoch + $epoch_Y*365-1 + $leapyear;
1675	}
1676	else
1677	{
1678		$epoch = $epoch - $epoch_Y*365-1 - $leapyear;
1679	}
1680	return $epoch;
1681}
1682
1683// Add function realdate for Birthday MOD
1684// the originate php "date()", does not work proberly on all OS, especially when going back in time
1685// before year 1970 (year 0), this function "realdate()", has a mutch larger valid date range,
1686// from 1901 - 2099. it returns a "like" UNIX date format (only date, related letters may be used, due to the fact that
1687// the given date value should already be divided by 86400 - leaving no time information left)
1688// a input like a UNIX timestamp divided by 86400 is expected, so
1689// calculation from the originate php date and mktime is easy.
1690// e.g. realdate ("m d Y", 3) returns the string "1 3 1970"
1691
1692// UNIX users should replace this function with the below code, since this should be faster
1693//
1694
1695function realdate ($date, $format = "Ymd")
1696{
1697	if (!$date) return;
1698	return bb_date($date*86400 + 1, $format, 0);
1699}
1700
1701function birthday_age ($date, $list = 0)
1702{
1703	if (!$date) return;
1704	return delta_time(mktime(11, 0, 0, date('m', strtotime($date)), date('d', strtotime($date)), (date('Y', strtotime($date)) - $list)));
1705}
1706
1707//
1708// Pagination routine, generates
1709// page number sequence
1710//
1711function generate_pagination ($base_url, $num_items, $per_page, $start_item, $add_prevnext_text = TRUE)
1712{
1713	global $lang, $template;
1714
1715// Pagination Mod
1716	$begin_end = 3;
1717	$from_middle = 1;
1718/*
1719	By default, $begin_end is 3, and $from_middle is 1, so on page 6 in a 12 page view, it will look like this:
1720
1721	a, d = $begin_end = 3
1722	b, c = $from_middle = 1
1723
1724 "begin"        "middle"           "end"
1725    |              |                 |
1726    |     a     b  |  c     d        |
1727    |     |     |  |  |     |        |
1728    v     v     v  v  v     v        v
1729    1, 2, 3 ... 5, 6, 7 ... 10, 11, 12
1730
1731	Change $begin_end and $from_middle to suit your needs appropriately
1732*/
1733
1734	$total_pages = ceil($num_items/$per_page);
1735
1736	if ( $total_pages == 1 || $num_items == 0 )
1737	{
1738		return '';
1739	}
1740
1741	$on_page = floor($start_item / $per_page) + 1;
1742
1743	$page_string = '';
1744	if ( $total_pages > ((2*($begin_end + $from_middle)) + 2) )
1745	{
1746		$init_page_max = ( $total_pages > $begin_end ) ? $begin_end : $total_pages;
1747		for($i = 1; $i < $init_page_max + 1; $i++)
1748		{
1749			$page_string .= ( $i == $on_page ) ? '<b>' . $i . '</b>' : '<a href="' . $base_url . "&amp;start=" . ( ( $i - 1 ) * $per_page ) . '">' . $i . '</a>';
1750			if ( $i <  $init_page_max )
1751			{
1752				$page_string .= ", ";
1753			}
1754		}
1755		if ( $total_pages > $begin_end )
1756		{
1757			if ( $on_page > 1  && $on_page < $total_pages )
1758			{
1759				$page_string .= ( $on_page > ($begin_end + $from_middle + 1) ) ? ' ... ' : ', ';
1760
1761				$init_page_min = ( $on_page > ($begin_end + $from_middle) ) ? $on_page : ($begin_end + $from_middle + 1);
1762
1763				$init_page_max = ( $on_page < $total_pages - ($begin_end + $from_middle) ) ? $on_page : $total_pages - ($begin_end + $from_middle);
1764
1765				for($i = $init_page_min - $from_middle; $i < $init_page_max + ($from_middle + 1); $i++)
1766				{
1767					$page_string .= ($i == $on_page) ? '<b>' . $i . '</b>' : '<a href="' . $base_url . "&amp;start=" . ( ( $i - 1 ) * $per_page ) . '">' . $i . '</a>';
1768					if ( $i <  $init_page_max + $from_middle )
1769					{
1770						$page_string .= ', ';
1771					}
1772				}
1773				$page_string .= ( $on_page < $total_pages - ($begin_end + $from_middle) ) ? ' ... ' : ', ';
1774			}
1775			else
1776			{
1777				$page_string .= '&nbsp;...&nbsp;';
1778			}
1779			for($i = $total_pages - ($begin_end - 1); $i < $total_pages + 1; $i++)
1780			{
1781				$page_string .= ( $i == $on_page ) ? '<b>' . $i . '</b>'  : '<a href="' . $base_url . "&amp;start=" . ( ( $i - 1 ) * $per_page ) . '">' . $i . '</a>';
1782				if( $i <  $total_pages )
1783				{
1784					$page_string .= ", ";
1785				}
1786			}
1787		}
1788	}
1789	else
1790	{
1791		for($i = 1; $i < $total_pages + 1; $i++)
1792		{
1793			$page_string .= ( $i == $on_page ) ? '<b>' . $i . '</b>' : '<a href="' . $base_url . "&amp;start=" . ( ( $i - 1 ) * $per_page ) . '">' . $i . '</a>';
1794			if ( $i <  $total_pages )
1795			{
1796				$page_string .= ', ';
1797			}
1798		}
1799	}
1800
1801	if ( $add_prevnext_text )
1802	{
1803		if ( $on_page > 1 )
1804		{
1805			$page_string = ' <a href="' . $base_url . "&amp;start=" . ( ( $on_page - 2 ) * $per_page ) . '">' . $lang['PREVIOUS_PAGE'] . '</a>&nbsp;&nbsp;' . $page_string;
1806		}
1807
1808		if ( $on_page < $total_pages )
1809		{
1810			$page_string .= '&nbsp;&nbsp;<a href="' . $base_url . "&amp;start=" . ( $on_page * $per_page ) . '">' . $lang['NEXT_PAGE'] . '</a>';
1811		}
1812
1813	}
1814
1815	$pagination = ($page_string) ? '<a class="menu-root" href="#pg-jump">'. $lang['GOTO_PAGE'] .'</a> :&nbsp;&nbsp;'. $page_string : '';
1816	$pagination = str_replace('&amp;start=0', '', $pagination);
1817
1818	$template->assign_vars(array(
1819		'PAGINATION'   => $pagination,
1820		'PAGE_NUMBER'  => sprintf($lang['PAGE_OF'], ( floor($start_item/$per_page) + 1 ), ceil( $num_items / $per_page )),
1821		'PG_BASE_URL'  => $base_url,
1822		'PG_PER_PAGE'  => $per_page,
1823	));
1824
1825	return $pagination;
1826}
1827
1828//
1829// This does exactly what preg_quote() does in PHP 4-ish
1830// If you just need the 1-parameter preg_quote call, then don't bother using this.
1831//
1832function bb_preg_quote($str, $delimiter)
1833{
1834	$text = preg_quote($str);
1835	$text = str_replace($delimiter, '\\' . $delimiter, $text);
1836
1837	return $text;
1838}
1839
1840//
1841// Obtain list of naughty words and build preg style replacement arrays for use by the
1842// calling script, note that the vars are passed as references this just makes it easier
1843// to return both sets of arrays
1844//
1845function obtain_word_list(&$orig_word, &$replacement_word)
1846{
1847	global $bb_cfg;
1848
1849	if (!$bb_cfg['use_word_censor']) return;
1850
1851	if (!$sql = CACHE('bb_cache')->get('censored'))
1852	{
1853		$sql = DB()->fetch_rowset("SELECT word, replacement FROM ". BB_WORDS);
1854		if(!$sql) $sql = array(array('word' => 1, 'replacement' => 1));
1855		CACHE('bb_cache')->set('censored', $sql, 7200);
1856	}
1857
1858	foreach($sql as $row)
1859	{
1860		//$orig_word[] = '#(?<!\S)(' . str_replace('\*', '\S*?', preg_quote($row['word'], '#')) . ')(?!\S)#iu';
1861		$orig_word[] = '#(?<![\p{Nd}\p{L}_])(' . str_replace('\*', '[\p{Nd}\p{L}_]*?', preg_quote($row['word'], '#')) . ')(?![\p{Nd}\p{L}_])#iu';
1862		$replacement_word[] = $row['replacement'];
1863	}
1864
1865	return true;
1866}
1867
1868function smiley_sort ($a, $b)
1869{
1870	if (strlen($a['code']) == strlen($b['code']))
1871	{
1872		return 0;
1873	}
1874
1875	return (strlen($a['code']) > strlen($b['code'])) ? -1 : 1;
1876}
1877
1878function bb_die ($msg_text)
1879{
1880	if (defined('IN_AJAX'))
1881	{
1882		$GLOBALS['ajax']->ajax_die($msg…

Large files files are truncated, but you can click here to view the full file