1<?php 2/** 3 * Loads common functions used throughout the site. 4 * 5 * @copyright (C) 2008-2012 PunBB, partially based on code (C) 2008-2009 FluxBB.org 6 * @license http://www.gnu.org/licenses/gpl.html GPL version 2 or higher 7 * @package PunBB 8 */ 9 10// 11// Common helpers and forum's wrappers for PHP functions 12// 13 14// Encodes the contents of $str so that they are safe to output on an (X)HTML page 15function forum_htmlencode($str) 16{ 17 return htmlspecialchars($str, ENT_QUOTES, 'UTF-8'); 18} 19 20 21// Trim whitespace including non-breaking space 22function forum_trim($str, $charlist = " \t\n\r\0\x0B\xC2\xA0") 23{ 24 return utf8_trim($str, $charlist); 25} 26 27 28// Convert \r\n and \r to \n 29function forum_linebreaks($str) 30{ 31 return str_replace(array("\r\n", "\r"), "\n", $str); 32} 33 34 35// Start PHP session 36function forum_session_start() { 37 static $forum_session_started = FALSE; 38 39 $return = ($hook = get_hook('fn_forum_session_start_start')) ? eval($hook) : null; 40 if ($return !== null) 41 return; 42 43 // Check if session already started 44 if ($forum_session_started && session_id()) 45 return; 46 47 // Check session id 48 $forum_session_id = NULL; 49 if (isset($_COOKIE['PHPSESSID'])) 50 $forum_session_id = $_COOKIE['PHPSESSID']; 51 else if (isset($_GET['PHPSESSID'])) 52 $forum_session_id = $_GET['PHPSESSID']; 53 54 if (empty($forum_session_id) || !preg_match('/^[a-z0-9\-,]{16,32}$/i', $forum_session_id)) 55 { 56 // Create new session id 57 $forum_session_id = random_key(32, FALSE, TRUE); 58 session_id($forum_session_id); 59 } 60 61 if (!isset($_SESSION)) 62 { 63 session_start(); 64 } 65 66 if (!isset($_SESSION['initiated'])) 67 { 68 session_regenerate_id(); 69 $_SESSION['initiated'] = TRUE; 70 } 71 72 $forum_session_started = TRUE; 73} 74 75 76// Converts the CDATA end sequence ]]> into ]]> 77function escape_cdata($str) 78{ 79 return str_replace(']]>', ']]>', $str); 80} 81 82 83// Check the text is CAPSED 84function check_is_all_caps($text) 85{ 86 return (bool)/**/(utf8_strtoupper($text) == $text && utf8_strtolower($text) != $text); 87} 88 89 90// Return current timestamp (with microseconds) as a float 91function forum_microtime() 92{ 93 if (version_compare(PHP_VERSION, '5.0.0', '>=')) 94 { 95 $mt = microtime(true); 96 } 97 else 98 { 99 list($usec, $sec) = explode(' ', microtime()); 100 $mt = ((float)/**/$usec + (float)/**/$sec); 101 } 102 103 return $mt; 104} 105 106 107// Inserts $element into $input at $offset 108// $offset can be either a numerical offset to insert at (eg: 0 inserts at the beginning of the array) 109// or a string, which is the key that the new element should be inserted before 110// $key is optional: it's used when inserting a new key/value pair into an associative array 111function array_insert(&$input, $offset, $element, $key = null) 112{ 113 if ($key == null) 114 $key = $offset; 115 116 // Determine the proper offset if we're using a string 117 if (!is_int($offset)) 118 $offset = array_search($offset, array_keys($input), true); 119 120 // Out of bounds checks 121 if ($offset > count($input)) 122 $offset = count($input); 123 else if ($offset < 0) 124 $offset = 0; 125 126 $input = array_merge(array_slice($input, 0, $offset), array($key => $element), array_slice($input, $offset)); 127} 128 129 130// Unset any variables instantiated as a result of register_globals being enabled 131function forum_unregister_globals() 132{ 133 $register_globals = @ini_get('register_globals'); 134 if ($register_globals === '' || $register_globals === '0' || strtolower($register_globals) === 'off') 135 return; 136 137 // Prevent script.php?GLOBALS[foo]=bar 138 if (isset($_REQUEST['GLOBALS']) || isset($_FILES['GLOBALS'])) 139 exit('I\'ll have a steak sandwich and... a steak sandwich.'); 140 141 // Variables that shouldn't be unset 142 $no_unset = array('GLOBALS', '_GET', '_POST', '_COOKIE', '_REQUEST', '_SERVER', '_ENV', '_FILES'); 143 144 // Remove elements in $GLOBALS that are present in any of the superglobals 145 $input = array_merge($_GET, $_POST, $_COOKIE, $_SERVER, $_ENV, $_FILES, isset($_SESSION) && is_array($_SESSION) ? $_SESSION : array()); 146 foreach ($input as $k => $v) 147 if (!in_array($k, $no_unset) && isset($GLOBALS[$k])) 148 { 149 unset($GLOBALS[$k]); 150 unset($GLOBALS[$k]); // Double unset to circumvent the zend_hash_del_key_or_index hole in PHP <4.4.3 and <5.1.4 151 } 152} 153 154 155// Removes any "bad" characters (characters which mess with the display of a page, are invisible, etc) from user input 156function forum_remove_bad_characters() 157{ 158 global $bad_utf8_chars; 159 160 $bad_utf8_chars = array("\0", "\xc2\xad", "\xcc\xb7", "\xcc\xb8", "\xe1\x85\x9F", "\xe1\x85\xA0", "\xe2\x80\x80", "\xe2\x80\x81", "\xe2\x80\x82", "\xe2\x80\x83", "\xe2\x80\x84", "\xe2\x80\x85", "\xe2\x80\x86", "\xe2\x80\x87", "\xe2\x80\x88", "\xe2\x80\x89", "\xe2\x80\x8a", "\xe2\x80\x8b", "\xe2\x80\x8e", "\xe2\x80\x8f", "\xe2\x80\xaa", "\xe2\x80\xab", "\xe2\x80\xac", "\xe2\x80\xad", "\xe2\x80\xae", "\xe2\x80\xaf", "\xe2\x81\x9f", "\xe3\x80\x80", "\xe3\x85\xa4", "\xef\xbb\xbf", "\xef\xbe\xa0", "\xef\xbf\xb9", "\xef\xbf\xba", "\xef\xbf\xbb", "\xE2\x80\x8D"); 161 162 ($hook = get_hook('fn_remove_bad_characters_start')) ? eval($hook) : null; 163 164 function _forum_remove_bad_characters($array) 165 { 166 global $bad_utf8_chars; 167 return is_array($array) ? array_map('_forum_remove_bad_characters', $array) : str_replace($bad_utf8_chars, '', $array); 168 } 169 170 $_GET = _forum_remove_bad_characters($_GET); 171 $_POST = _forum_remove_bad_characters($_POST); 172 $_COOKIE = _forum_remove_bad_characters($_COOKIE); 173 $_REQUEST = _forum_remove_bad_characters($_REQUEST); 174} 175 176 177// Fix the REQUEST_URI if we can, since both IIS6 and IIS7 break it 178function forum_fix_request_uri() 179{ 180 if (defined('FORUM_IGNORE_REQUEST_URI')) 181 return; 182 183 global $forum_config; 184 185 if (!isset($_SERVER['REQUEST_URI']) || (isset($_SERVER['QUERY_STRING']) && !empty($_SERVER['QUERY_STRING']) && strpos($_SERVER['REQUEST_URI'], '?') === false)) 186 { 187 // Workaround for a bug in IIS7 188 if (isset($_SERVER['HTTP_X_ORIGINAL_URL'])) 189 $_SERVER['REQUEST_URI'] = $_SERVER['HTTP_X_ORIGINAL_URL']; 190 191 // IIS6 also doesn't set REQUEST_URI, If we are using the default SEF URL scheme then we can work around it 192 else if (!isset($forum_config) || $forum_config['o_sef'] == 'Default') 193 { 194 $requested_page = str_replace(array('%26', '%3D', '%2F', '%3F'), array('&', '=', '/', '?'), rawurlencode($_SERVER['PHP_SELF'])); 195 $_SERVER['REQUEST_URI'] = $requested_page.(isset($_SERVER['QUERY_STRING']) && !empty($_SERVER['QUERY_STRING']) ? '?'.$_SERVER['QUERY_STRING'] : ''); 196 } 197 198 // Otherwise I am not aware of a work around... 199 else 200 error('The web server you are using is not correctly setting the REQUEST_URI variable.<br />This usually means you are using IIS6, or an unpatched IIS7. Please either disable SEF URLs, upgrade to IIS7 and install any available patches or try a different web server.'); 201 } 202} 203 204 205// Set a cookie, PunBB style! 206// Like other headers, cookies must be sent before any output from your script. 207// Use headers_sent() to ckeck wether HTTP headers has been sent already. 208function forum_setcookie($name, $value, $expire) 209{ 210 global $cookie_path, $cookie_domain, $cookie_secure; 211 212 $return = ($hook = get_hook('fn_forum_setcookie_start')) ? eval($hook) : null; 213 if ($return !== null) 214 return; 215 216 // Enable sending of a P3P header 217 header('P3P: CP="CUR ADM"'); 218 219 if (version_compare(PHP_VERSION, '5.2.0', '>=')) 220 setcookie($name, $value, $expire, $cookie_path, $cookie_domain, $cookie_secure, true); 221 else 222 setcookie($name, $value, $expire, $cookie_path.'; HttpOnly', $cookie_domain, $cookie_secure); 223} 224 225 226// Attempts to fetch the provided URL using any available means 227function get_remote_file($url, $timeout, $head_only = false, $max_redirects = 10) 228{ 229 $result = null; 230 $parsed_url = parse_url($url); 231 $allow_url_fopen = strtolower(@ini_get('allow_url_fopen')); 232 233 // Quite unlikely that this will be allowed on a shared host, but it can't hurt 234 if (function_exists('ini_set')) 235 @ini_set('default_socket_timeout', $timeout); 236 237 // If we have cURL, we might as well use it 238 if (function_exists('curl_init')) 239 { 240 // Setup the transfer 241 $ch = curl_init(); 242 curl_setopt($ch, CURLOPT_URL, $url); 243 curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); 244 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 245 curl_setopt($ch, CURLOPT_HEADER, true); 246 curl_setopt($ch, CURLOPT_NOBODY, $head_only); 247 curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); 248 curl_setopt($ch, CURLOPT_USERAGENT, 'PunBB'); 249 250 // Grab the page 251 $content = @curl_exec($ch); 252 $responce_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); 253 curl_close($ch); 254 255 // Process 301/302 redirect 256 if ($content !== false && ($responce_code == '301' || $responce_code == '302') && $max_redirects > 0) 257 { 258 $headers = explode("\r\n", trim($content)); 259 foreach ($headers as $header) 260 if (substr($header, 0, 10) == 'Location: ') 261 { 262 $responce = get_remote_file(substr($header, 10), $timeout, $head_only, $max_redirects - 1); 263 if ($responce !== null) 264 $responce['headers'] = array_merge($headers, $responce['headers']); 265 return $responce; 266 } 267 } 268 269 // Ignore everything except a 200 response code 270 if ($content !== false && $responce_code == '200') 271 { 272 if ($head_only) 273 $result['headers'] = explode("\r\n", str_replace("\r\n\r\n", "\r\n", trim($content))); 274 else 275 { 276 preg_match('#HTTP/1.[01] 200 OK#', $content, $match, PREG_OFFSET_CAPTURE); 277 $last_content = substr($content, $match[0][1]); 278 $content_start = strpos($last_content, "\r\n\r\n"); 279 if ($content_start !== false) 280 { 281 $result['headers'] = explode("\r\n", str_replace("\r\n\r\n", "\r\n", substr($content, 0, $match[0][1] + $content_start))); 282 $result['content'] = substr($last_content, $content_start + 4); 283 } 284 } 285 } 286 } 287 // fsockopen() is the second best thing 288 else if (function_exists('fsockopen')) 289 { 290 $remote = @fsockopen($parsed_url['host'], !empty($parsed_url['port']) ? intval($parsed_url['port']) : 80, $errno, $errstr, $timeout); 291 if ($remote) 292 { 293 // Send a standard HTTP 1.0 request for the page 294 fwrite($remote, ($head_only ? 'HEAD' : 'GET').' '.(!empty($parsed_url['path']) ? $parsed_url['path'] : '/').(!empty($parsed_url['query']) ? '?'.$parsed_url['query'] : '').' HTTP/1.0'."\r\n"); 295 fwrite($remote, 'Host: '.$parsed_url['host']."\r\n"); 296 fwrite($remote, 'User-Agent: PunBB'."\r\n"); 297 fwrite($remote, 'Connection: Close'."\r\n\r\n"); 298 299 stream_set_timeout($remote, $timeout); 300 $stream_meta = stream_get_meta_data($remote); 301 302 // Fetch the response 1024 bytes at a time and watch out for a timeout 303 $content = false; 304 while (!feof($remote) && !$stream_meta['timed_out']) 305 { 306 $content .= fgets($remote, 1024); 307 $stream_meta = stream_get_meta_data($remote); 308 } 309 310 fclose($remote); 311 312 // Process 301/302 redirect 313 if ($content !== false && $max_redirects > 0 && preg_match('#^HTTP/1.[01] 30[12]#', $content)) 314 { 315 $headers = explode("\r\n", trim($content)); 316 foreach ($headers as $header) 317 if (substr($header, 0, 10) == 'Location: ') 318 { 319 $responce = get_remote_file(substr($header, 10), $timeout, $head_only, $max_redirects - 1); 320 if ($responce !== null) 321 $responce['headers'] = array_merge($headers, $responce['headers']); 322 return $responce; 323 } 324 } 325 326 // Ignore everything except a 200 response code 327 if ($content !== false && preg_match('#^HTTP/1.[01] 200 OK#', $content)) 328 { 329 if ($head_only) 330 $result['headers'] = explode("\r\n", trim($content)); 331 else 332 { 333 $content_start = strpos($content, "\r\n\r\n"); 334 if ($content_start !== false) 335 { 336 $result['headers'] = explode("\r\n", substr($content, 0, $content_start)); 337 $result['content'] = substr($content, $content_start + 4); 338 } 339 } 340 } 341 } 342 } 343 // Last case scenario, we use file_get_contents provided allow_url_fopen is enabled (any non 200 response results in a failure) 344 else if (in_array($allow_url_fopen, array('on', 'true', '1'))) 345 { 346 // PHP5's version of file_get_contents() supports stream options 347 if (version_compare(PHP_VERSION, '5.0.0', '>=')) 348 { 349 // Setup a stream context 350 $stream_context = stream_context_create( 351 array( 352 'http' => array( 353 'method' => $head_only ? 'HEAD' : 'GET', 354 'user_agent' => 'PunBB', 355 'max_redirects' => $max_redirects + 1, // PHP >=5.1.0 only 356 'timeout' => $timeout // PHP >=5.2.1 only 357 ) 358 ) 359 ); 360 361 $content = @file_get_contents($url, false, $stream_context); 362 } 363 else 364 $content = @file_get_contents($url); 365 366 // Did we get anything? 367 if ($content !== false) 368 { 369 // Gotta love the fact that $http_response_header just appears in the global scope (*cough* hack! *cough*) 370 $result['headers'] = $http_response_header; 371 if (!$head_only) 372 $result['content'] = $content; 373 } 374 } 375 376 return $result; 377} 378 379 380// Clean version string from trailing '.0's 381function clean_version($version) 382{ 383 return preg_replace('/(\.0)+(?!\.)|(\.0+$)/', '$2', $version); 384} 385 386 387// Dump contents of variable(s) for debug 388function forum_dump() 389{ 390 echo '<pre>'; 391 392 $num_args = func_num_args(); 393 394 for ($i = 0; $i < $num_args; ++$i) 395 { 396 print_r(func_get_arg($i)); 397 echo "\n\n"; 398 } 399 400 echo '</pre>'; 401 exit; 402} 403 404 405// 406// Markup helpers 407// 408 409// A wrapper for PHP's number_format function 410function forum_number_format($number, $decimals = 0) 411{ 412 global $lang_common; 413 414 $return = ($hook = get_hook('fn_forum_number_format_start')) ? eval($hook) : null; 415 if ($return !== null) 416 return $return; 417 418 return number_format($number, $decimals, $lang_common['lang_decimal_point'], $lang_common['lang_thousands_sep']); 419} 420 421 422// Format a time string according to $date_format, $time_format, and timezones 423define('FORUM_FT_DATETIME', 0); 424define('FORUM_FT_DATE', 1); 425define('FORUM_FT_TIME', 2); 426function format_time($timestamp, $type = FORUM_FT_DATETIME, $date_format = null, $time_format = null, $no_text = false) 427{ 428 global $forum_config, $lang_common, $forum_user, $forum_time_formats, $forum_date_formats; 429 430 $return = ($hook = get_hook('fn_format_time_start')) ? eval($hook) : null; 431 if ($return !== null) 432 return $return; 433 434 if ($timestamp == '') 435 return ($no_text ? '' : $lang_common['Never']); 436 437 if ($date_format == null) 438 $date_format = $forum_date_formats[$forum_user['date_format']]; 439 440 if ($time_format == null) 441 $time_format = $forum_time_formats[$forum_user['time_format']]; 442 443 $diff = ($forum_user['timezone'] + $forum_user['dst']) * 3600; 444 $timestamp += $diff; 445 $now = time(); 446 447 $formatted_time = ''; 448 449 if ($type == FORUM_FT_DATETIME || $type == FORUM_FT_DATE) 450 { 451 $formatted_time = gmdate($date_format, $timestamp); 452 453 if (!$no_text) 454 { 455 $base = gmdate('Y-m-d', $timestamp); 456 $today = gmdate('Y-m-d', $now + $diff); 457 $yesterday = gmdate('Y-m-d', $now + $diff - 86400); 458 459 if ($base == $today) 460 $formatted_time = $lang_common['Today']; 461 else if ($base == $yesterday) 462 $formatted_time = $lang_common['Yesterday']; 463 } 464 } 465 466 if ($type == FORUM_FT_DATETIME) 467 $formatted_time .= ' '; 468 469 if ($type == FORUM_FT_DATETIME || $type == FORUM_FT_TIME) 470 $formatted_time .= gmdate($time_format, $timestamp); 471 472 ($hook = get_hook('fn_format_time_end')) ? eval($hook) : null; 473 474 return $formatted_time; 475} 476 477 478 479// Generate the "navigator" that appears at the top of every page 480function generate_navlinks() 481{ 482 global $forum_config, $lang_common, $forum_url, $forum_user; 483 484 $return = ($hook = get_hook('fn_generate_navlinks_start')) ? eval($hook) : null; 485 if ($return !== null) 486 return $return; 487 488 // Index should always be displayed 489 $links['index'] = '<li id="navindex"'.((FORUM_PAGE == 'index') ? ' class="isactive"' : '').'><a href="'.forum_link($forum_url['index']).'">'.$lang_common['Index'].'</a></li>'; 490 491 if ($forum_user['g_read_board'] == '1' && $forum_user['g_view_users'] == '1') 492 $links['userlist'] = '<li id="navuserlist"'.((FORUM_PAGE == 'userlist') ? ' class="isactive"' : '').'><a href="'.forum_link($forum_url['users']).'">'.$lang_common['User list'].'</a></li>'; 493 494 if ($forum_config['o_rules'] == '1' && (!$forum_user['is_guest'] || $forum_user['g_read_board'] == '1' || $forum_config['o_regs_allow'] == '1')) 495 $links['rules'] = '<li id="navrules"'.((FORUM_PAGE == 'rules') ? ' class="isactive"' : '').'><a href="'.forum_link($forum_url['rules']).'">'.$lang_common['Rules'].'</a></li>'; 496 497 if ($forum_user['is_guest']) 498 { 499 if ($forum_user['g_read_board'] == '1' && $forum_user['g_search'] == '1') 500 $links['search'] = '<li id="navsearch"'.((FORUM_PAGE == 'search') ? ' class="isactive"' : '').'><a href="'.forum_link($forum_url['search']).'">'.$lang_common['Search'].'</a></li>'; 501 502 $links['register'] = '<li id="navregister"'.((FORUM_PAGE == 'register') ? ' class="isactive"' : '').'><a href="'.forum_link($forum_url['register']).'">'.$lang_common['Register'].'</a></li>'; 503 $links['login'] = '<li id="navlogin"'.((FORUM_PAGE == 'login') ? ' class="isactive"' : '').'><a href="'.forum_link($forum_url['login']).'">'.$lang_common['Login'].'</a></li>'; 504 } 505 else 506 { 507 if (!$forum_user['is_admmod']) 508 { 509 if ($forum_user['g_read_board'] == '1' && $forum_user['g_search'] == '1') 510 $links['search'] = '<li id="navsearch"'.((FORUM_PAGE == 'search') ? ' class="isactive"' : '').'><a href="'.forum_link($forum_url['search']).'">'.$lang_common['Search'].'</a></li>'; 511 512 $links['profile'] = '<li id="navprofile"'.((substr(FORUM_PAGE, 0, 7) == 'profile') ? ' class="isactive"' : '').'><a href="'.forum_link($forum_url['user'], $forum_user['id']).'">'.$lang_common['Profile'].'</a></li>'; 513 $links['logout'] = '<li id="navlogout"><a href="'.forum_link($forum_url['logout'], array($forum_user['id'], generate_form_token('logout'.$forum_user['id']))).'">'.$lang_common['Logout'].'</a></li>'; 514 } 515 else 516 { 517 $links['search'] = '<li id="navsearch"'.((FORUM_PAGE == 'search') ? ' class="isactive"' : '').'><a href="'.forum_link($forum_url['search']).'">'.$lang_common['Search'].'</a></li>'; 518 $links['profile'] = '<li id="navprofile"'.((substr(FORUM_PAGE, 0, 7) == 'profile') ? ' class="isactive"' : '').'><a href="'.forum_link($forum_url['user'], $forum_user['id']).'">'.$lang_common['Profile'].'</a></li>'; 519 $links['admin'] = '<li id="navadmin"'.((substr(FORUM_PAGE, 0, 5) == 'admin') ? ' class="isactive"' : '').'><a href="'.forum_link($forum_url['admin_index']).'">'.$lang_common['Admin'].'</a></li>'; 520 $links['logout'] = '<li id="navlogout"><a href="'.forum_link($forum_url['logout'], array($forum_user['id'], generate_form_token('logout'.$forum_user['id']))).'">'.$lang_common['Logout'].'</a></li>'; 521 } 522 } 523 524 // Are there any additional navlinks we should insert into the array before imploding it? 525 if ($forum_config['o_additional_navlinks'] != '' && preg_match_all('#([0-9]+)\s*=\s*(.*?)\n#s', $forum_config['o_additional_navlinks']."\n", $extra_links)) 526 { 527 // Insert any additional links into the $links array (at the correct index) 528 $num_links = count($extra_links[1]); 529 for ($i = 0; $i < $num_links; ++$i) 530 array_insert($links, (int)$extra_links[1][$i], '<li id="navextra'.($i + 1).'">'.$extra_links[2][$i].'</li>'); 531 } 532 533 ($hook = get_hook('fn_generate_navlinks_end')) ? eval($hook) : null; 534 535 return implode("\n\t\t", $links); 536} 537 538 539 540// Outputs markup to display a user's avatar 541function generate_avatar_markup($user_id, $avatar_type, $avatar_width, $avatar_height, $username = NULL, $drop_cache = FALSE) 542{ 543 global $forum_config, $base_url; 544 545 $avatar_markup = $avatar_filename = ''; 546 547 $return = ($hook = get_hook('fn_generate_avatar_markup_start')) ? eval($hook) : null; 548 if ($return !== null) 549 return $return; 550 551 552 // Create avatar filename 553 switch ($avatar_type) 554 { 555 case FORUM_AVATAR_GIF: 556 $avatar_filename = $user_id.'.gif'; 557 break; 558 559 case FORUM_AVATAR_JPG: 560 $avatar_filename = $user_id.'.jpg'; 561 break; 562 563 case FORUM_AVATAR_PNG: 564 $avatar_filename = $user_id.'.png'; 565 break; 566 567 case FORUM_AVATAR_NONE: 568 default: 569 break; 570 } 571 572 // Create markup 573 if ($avatar_filename && $avatar_width > 0 && $avatar_height > 0) 574 { 575 $path = $forum_config['o_avatars_dir'].'/'.$avatar_filename; 576 577 // 578 if ($drop_cache) 579 { 580 $path .= '?no_cache='.random_key(8, TRUE); 581 } 582 583 $alt_attr = ''; 584 if (is_string($username) && utf8_strlen($username) > 0) { 585 $alt_attr = forum_htmlencode($username); 586 } 587 588 $avatar_markup = '<img src="'.$base_url.'/'.$path.'" width="'.$avatar_width.'" height="'.$avatar_height.'" alt="'.$alt_attr.'" />'; 589 } 590 591 ($hook = get_hook('fn_generate_avatar_markup_end')) ? eval($hook) : null; 592 593 return $avatar_markup; 594} 595 596 597// Generate breadcrumb navigation 598function generate_crumbs($reverse) 599{ 600 global $lang_common, $forum_url, $forum_config, $forum_page; 601 602 $return = ($hook = get_hook('fn_generate_crumbs_start')) ? eval($hook) : null; 603 if ($return !== null) 604 return $return; 605 606 if (empty($forum_page['crumbs'])) 607 $forum_page['crumbs'][0] = $forum_config['o_board_title']; 608 609 $crumbs = ''; 610 $num_crumbs = count($forum_page['crumbs']); 611 612 if ($reverse) 613 { 614 for ($i = ($num_crumbs - 1); $i >= 0; --$i) 615 $crumbs .= (is_array($forum_page['crumbs'][$i]) ? forum_htmlencode($forum_page['crumbs'][$i][0]) : forum_htmlencode($forum_page['crumbs'][$i])).((isset($forum_page['page']) && $i == ($num_crumbs - 1)) ? ' ('.$lang_common['Page'].' '.forum_number_format($forum_page['page']).')' : '').($i > 0 ? $lang_common['Title separator'] : ''); 616 } 617 else 618 for ($i = 0; $i < $num_crumbs; ++$i) 619 { 620 if ($i < ($num_crumbs - 1)) 621 $crumbs .= '<span class="crumb'.(($i == 0) ? ' crumbfirst' : '').'">'.(($i >= 1) ? '<span>'.$lang_common['Crumb separator'].'</span>' : '').(is_array($forum_page['crumbs'][$i]) ? '<a href="'.$forum_page['crumbs'][$i][1].'">'.forum_htmlencode($forum_page['crumbs'][$i][0]).'</a>' : forum_htmlencode($forum_page['crumbs'][$i])).'</span> '; 622 else 623 $crumbs .= '<span class="crumb crumblast'.(($i == 0) ? ' crumbfirst' : '').'">'.(($i >= 1) ? '<span>'.$lang_common['Crumb separator'].'</span>' : '').(is_array($forum_page['crumbs'][$i]) ? '<a href="'.$forum_page['crumbs'][$i][1].'">'.forum_htmlencode($forum_page['crumbs'][$i][0]).'</a>' : forum_htmlencode($forum_page['crumbs'][$i])).'</span> '; 624 } 625 626 ($hook = get_hook('fn_generate_crumbs_end')) ? eval($hook) : null; 627 628 return $crumbs; 629} 630 631 632 633// Generate a string with page and item information for multipage headings 634function generate_items_info($label, $first, $total) 635{ 636 global $forum_page, $lang_common; 637 638 $return = ($hook = get_hook('fn_generate_page_info_start')) ? eval($hook) : null; 639 if ($return !== null) 640 return $return; 641 642 if ($forum_page['num_pages'] == 1) 643 $item_info = '<span class="item-info">'.sprintf($lang_common['Item info single'], $label, forum_number_format($total)).'</span>'; 644 else 645 $item_info = '<span class="item-info">'.sprintf($lang_common['Item info plural'], $label, forum_number_format($first), forum_number_format($forum_page['finish_at']), forum_number_format($total)).'</span>'; 646 647 ($hook = get_hook('fn_generate_page_info_end')) ? eval($hook) : null; 648 649 return $item_info; 650} 651 652 653// Generate a string with numbered links (for multipage scripts) 654function paginate($num_pages, $cur_page, $link, $separator, $args = null, $is_default_scheme = null) 655{ 656 global $forum_url, $lang_common; 657 658 if ($is_default_scheme == null) 659 $forum_url_page = $forum_url['page']; 660 else 661 { 662 $forum_url_page = '&p=$1'; 663 unset($forum_url['insertion_find']); 664 } 665 666 $pages = array(); 667 $link_to_all = false; 668 669 $return = ($hook = get_hook('fn_paginate_start')) ? eval($hook) : null; 670 if ($return !== null) 671 return $return; 672 673 // If $cur_page == -1, we link to all pages (used in viewforum.php) 674 if ($cur_page == -1) 675 { 676 $cur_page = 1; 677 $link_to_all = true; 678 } 679 680 if ($num_pages <= 1) 681 $pages = array('<strong class="first-item">'.forum_number_format(1).'</strong>'); 682 else 683 { 684 // Add a previous page link 685 if ($num_pages > 1 && $cur_page > 1) 686 $pages[] = '<a'.(empty($pages) ? ' class="first-item"' : '').' href="'.forum_sublink($link, $forum_url_page, ($cur_page - 1), $args).'">'.$lang_common['Previous'].'</a>'; 687 688 if ($cur_page > 3) 689 { 690 $pages[] = '<a'.(empty($pages) ? ' class="first-item"' : '').' href="'.forum_sublink($link, $forum_url_page, 1, $args).'">'.forum_number_format(1).'</a>'; 691 692 if ($cur_page > 5) 693 $pages[] = '<span>'.$lang_common['Spacer'].'</span>'; 694 } 695 696 // Don't ask me how the following works. It just does, OK? :-) 697 for ($current = ($cur_page == 5) ? $cur_page - 3 : $cur_page - 2, $stop = ($cur_page + 4 == $num_pages) ? $cur_page + 4 : $cur_page + 3; $current < $stop; ++$current) 698 if ($current < 1 || $current > $num_pages) 699 continue; 700 else if ($current != $cur_page || $link_to_all) 701 $pages[] = '<a'.(empty($pages) ? ' class="first-item"' : '').' href="'.forum_sublink($link, $forum_url_page, $current, $args).'">'.forum_number_format($current).'</a>'; 702 else 703 $pages[] = '<strong'.(empty($pages) ? ' class="first-item"' : '').'>'.forum_number_format($current).'</strong>'; 704 705 if ($cur_page <= ($num_pages-3)) 706 { 707 if ($cur_page != ($num_pages-3) && $cur_page != ($num_pages-4)) 708 $pages[] = '<span>'.$lang_common['Spacer'].'</span>'; 709 710 $pages[] = '<a'.(empty($pages) ? ' class="first-item"' : '').' href="'.forum_sublink($link, $forum_url_page, $num_pages, $args).'">'.forum_number_format($num_pages).'</a>'; 711 } 712 713 // Add a next page link 714 if ($num_pages > 1 && !$link_to_all && $cur_page < $num_pages) 715 $pages[] = '<a'.(empty($pages) ? ' class="first-item"' : '').' href="'.forum_sublink($link, $forum_url_page, ($cur_page + 1), $args).'">'.$lang_common['Next'].'</a>'; 716 } 717 718 ($hook = get_hook('fn_paginate_end')) ? eval($hook) : null; 719 720 return implode($separator, $pages); 721} 722 723 724// Display executed queries (if enabled) for debug 725function get_saved_queries() 726{ 727 global $forum_db, $lang_common; 728 729 // Get the queries so that we can print them out 730 $saved_queries = $forum_db->get_saved_queries(); 731 732 ob_start(); 733 734?> 735<div id="brd-debug" class="main"> 736 <div class="debug"> 737 <table> 738 <caption><?php echo $lang_common['Debug summary'] ?></caption> 739 <thead> 740 <tr> 741 <th class="tcl" scope="col"><?php echo $lang_common['Query times'] ?></th> 742 <th class="tcr" scope="col"><?php echo $lang_common['Query'] ?></th> 743 </tr> 744 </thead> 745 <tbody> 746<?php 747 748 $query_time_total = 0.0; 749 foreach ($saved_queries as $cur_query) 750 { 751 $query_time_total += $cur_query[1]; 752 753?> 754 <tr> 755 <td class="tcl"><?php echo (($cur_query[1] != 0) ? forum_number_format($cur_query[1], 5) : ' ') ?></td> 756 <td class="tcr"><?php echo forum_htmlencode($cur_query[0]) ?></td> 757 </tr> 758<?php 759 760 } 761 762?> 763 <tr> 764 <td class="tcl border-less"><?php echo forum_number_format($query_time_total, 5) ?></td> 765 <td class="tcr border-less"><?php echo $lang_common['Total query time'] ?></td> 766 </tr> 767 </tbody> 768 </table> 769 </div> 770</div> 771<?php 772 773 return ob_get_clean(); 774} 775 776 777// 778// Other special helpers 779// 780 781// Return all code blocks that hook into $hook_id 782function get_hook($hook_id) 783{ 784 global $forum_hooks; 785 786 return !defined('FORUM_DISABLE_HOOKS') && isset($forum_hooks[$hook_id]) ? implode("\n", $forum_hooks[$hook_id]) : false; 787} 788 789 790// Generate a hyperlink with parameters and anchor 791function forum_link($link, $args = null) 792{ 793 global $forum_config, $base_url; 794 795 $return = ($hook = get_hook('fn_forum_link_start')) ? eval($hook) : null; 796 if ($return !== null) 797 return $return; 798 799 $gen_link = $link; 800 if ($args == null) 801 $gen_link = $base_url.'/'.$link; 802 else if (!is_array($args)) 803 $gen_link = $base_url.'/'.str_replace('$1', $args, $link); 804 else 805 { 806 for ($i = 0; isset($args[$i]); ++$i) 807 $gen_link = str_replace('$'.($i + 1), $args[$i], $gen_link); 808 $gen_link = $base_url.'/'.$gen_link; 809 } 810 811 ($hook = get_hook('fn_forum_link_end')) ? eval($hook) : null; 812 813 return $gen_link; 814} 815 816 817// Generate a hyperlink with parameters and anchor and a subsection such as a subpage 818function forum_sublink($link, $sublink, $subarg, $args = null) 819{ 820 global $forum_config, $forum_url, $base_url; 821 822 $return = ($hook = get_hook('fn_forum_sublink_start')) ? eval($hook) : null; 823 if ($return !== null) 824 return $return; 825 826 if ($sublink == $forum_url['page'] && $subarg == 1) 827 return forum_link($link, $args); 828 829 $gen_link = $link; 830 if (!is_array($args) && $args != null) 831 $gen_link = str_replace('$1', $args, $link); 832 else 833 { 834 for ($i = 0; isset($args[$i]); ++$i) 835 $gen_link = str_replace('$'.($i + 1), $args[$i], $gen_link); 836 } 837 838 if (isset($forum_url['insertion_find'])) 839 $gen_link = $base_url.'/'.str_replace($forum_url['insertion_find'], str_replace('$1', str_replace('$1', $subarg, $sublink), $forum_url['insertion_replace']), $gen_link); 840 else 841 $gen_link = $base_url.'/'.$gen_link.str_replace('$1', $subarg, $sublink); 842 843 ($hook = get_hook('fn_forum_sublink_end')) ? eval($hook) : null; 844 845 return $gen_link; 846} 847 848 849// Make a string safe to use in a URL 850function sef_friendly($str) 851{ 852 global $forum_config, $forum_user; 853 static $lang_url_replace, $forum_reserved_strings; 854 855 if (!isset($lang_url_replace)) 856 require FORUM_ROOT.'lang/'.$forum_user['language'].'/url_replace.php'; 857 858 if (!isset($forum_reserved_strings)) 859 { 860 // Bring in any reserved strings 861 if (file_exists(FORUM_ROOT.'include/url/'.$forum_config['o_sef'].'/reserved_strings.php')) 862 require FORUM_ROOT.'include/url/'.$forum_config['o_sef'].'/reserved_strings.php'; 863 else 864 require FORUM_ROOT.'include/url/Default/reserved_strings.php'; 865 } 866 867 $return = ($hook = get_hook('fn_sef_friendly_start')) ? eval($hook) : null; 868 if ($return !== null) 869 return $return; 870 871 $str = strtr($str, $lang_url_replace); 872 $str = strtolower(utf8_decode($str)); 873 $str = forum_trim(preg_replace(array('/[^a-z0-9\s]/', '/[\s]+/'), array('', '-'), $str), '-'); 874 875 foreach ($forum_reserved_strings as $match => $replace) 876 if ($str == $match) 877 return $replace; 878 else if ($match != '') 879 $str = str_replace($match, $replace, $str); 880 881 return $str; 882} 883 884 885// Replace censored words in $text loader 886function censor_words($text) 887{ 888 global $forum_db, $forum_censors; 889 890 $return = ($hook = get_hook('fn_censor_words_start')) ? eval($hook) : null; 891 if ($return !== null) 892 return $return; 893 894 // If not already loaded in a previous call, load the cached censors 895 if (!defined('FORUM_CENSORS_LOADED')) 896 { 897 if (file_exists(FORUM_CACHE_DIR.'cache_censors.php')) 898 include FORUM_CACHE_DIR.'cache_censors.php'; 899 900 if (!defined('FORUM_CENSORS_LOADED')) 901 { 902 if (!defined('FORUM_CACHE_FUNCTIONS_LOADED')) 903 require FORUM_ROOT.'include/cache.php'; 904 905 generate_censors_cache(); 906 require FORUM_CACHE_DIR.'cache_censors.php'; 907 } 908 } 909 910 // Check Unicode support 911 $unicode = defined('FORUM_SUPPORT_PCRE_UNICODE'); 912 913 return (isset($forum_censors)) ? censor_words_do($forum_censors, $text, $unicode) : $text; 914} 915 916 917// Replace censored words in $text 918function censor_words_do($forum_censors, $text, $unicode) 919{ 920 static $search_for = NULL; 921 static $replace_with = NULL; 922 923 if (is_null($search_for)) 924 $search_for = array(); 925 926 if (is_null($replace_with)) 927 $replace_with = array(); 928 929 930 if (!empty($forum_censors)) 931 { 932 // Generate regexp`s 933 foreach ($forum_censors as $censor_key => $cur_word) 934 { 935 if ($unicode) 936 { 937 // Unescape * 938 $replace = str_replace('\*', '*', preg_quote($cur_word['search_for'], '#')); 939 $replace = preg_replace(array('#(?<=[\p{Nd}\p{L}_])\*(?=[\p{Nd}\p{L}_])#iu', '#^\*#', '#\*$#'), array('([\x20]*?|[\p{Nd}\p{L}_-]*?)', '[\p{Nd}\p{L}_-]*?', '[\p{Nd}\p{L}_-]*?'), $replace); 940 941 // Generate the final substitution 942 $search_for[$censor_key] = '#(?<![\p{Nd}\p{L}_-])('.$replace.')(?![\p{Nd}\p{L}_-])#iu'; 943 } 944 else 945 { 946 // Unescape * 947 $replace = str_replace('\*', '\w*?', preg_quote($cur_word['search_for'], '#')); 948 $search_for[$censor_key] = '#(?<=\W)('.$replace.')(?=\W)#iu'; // This better for ASCII than (?!</S) 949 } 950 951 $replace_with[$censor_key] = $cur_word['replace_with']; 952 953 ($hook = get_hook('fn_censor_words_setup_regex')) ? eval($hook) : null; 954 } 955 956 // Replace 957 if (!empty($search_for)) 958 { 959 $text = utf8_substr(preg_replace($search_for, $replace_with, ' '.$text.' '), 1, -1); 960 } 961 } 962 963 return $text; 964} 965 966 967// Verifies that the provided username is OK for insertion into the database 968function validate_username($username, $exclude_id = null) 969{ 970 global $lang_common, $lang_register, $lang_profile, $forum_config; 971 972 $errors = array(); 973 974 $return = ($hook = get_hook('fn_validate_username_start')) ? eval($hook) : null; 975 if ($return !== null) 976 return $return; 977 978 // Convert multiple whitespace characters into one (to prevent people from registering with indistinguishable usernames) 979 $username = preg_replace('#\s+#s', ' ', $username); 980 981 // Validate username 982 if (utf8_strlen($username) < 2) 983 $errors[] = $lang_profile['Username too short']; 984 else if (utf8_strlen($username) > 25) 985 $errors[] = $lang_profile['Username too long']; 986 else if (strtolower($username) == 'guest' || utf8_strtolower($username) == utf8_strtolower($lang_common['Guest'])) 987 $errors[] = $lang_profile['Username guest']; 988 else if (preg_match('/[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/', $username) || preg_match('/((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))/', $username)) 989 $errors[] = $lang_profile['Username IP']; 990 else if ((strpos($username, '[') !== false || strpos($username, ']') !== false) && strpos($username, '\'') !== false && strpos($username, '"') !== false) 991 $errors[] = $lang_profile['Username reserved chars']; 992 else if (preg_match('/(?:\[\/?(?:b|u|i|h|colou?r|quote|code|img|url|email|list)\]|\[(?:code|quote|list)=)/i', $username)) 993 $errors[] = $lang_profile['Username BBCode']; 994 995 // Check username for any censored words 996 if ($forum_config['o_censoring'] == '1' && censor_words($username) != $username) 997 $errors[] = $lang_profile['Username censor']; 998 999 // Check for username dupe 1000 $dupe = check_username_dupe($username, $exclude_id); 1001 if ($dupe !== false) 1002 $errors[] = sprintf($lang_profile['Username dupe'], forum_htmlencode($dupe)); 1003 1004 ($hook = get_hook('fn_validate_username_end')) ? eval($hook) : null; 1005 1006 return $errors; 1007} 1008 1009 1010 1011 1012// Determines the correct title for $user 1013// $user must contain the elements 'username', 'title', 'posts', 'g_id' and 'g_user_title' 1014function get_title($user) 1015{ 1016 global $forum_db, $forum_config, $forum_bans, $lang_common; 1017 static $ban_list, $forum_ranks; 1018 1019 $return = ($hook = get_hook('fn_get_title_start')) ? eval($hook) : null; 1020 if ($return !== null) 1021 return $return; 1022 1023 // If not already built in a previous call, build an array of lowercase banned usernames 1024 if (empty($ban_list)) 1025 { 1026 $ban_list = array(); 1027 1028 foreach ($forum_bans as $cur_ban) 1029 $ban_list[] = utf8_strtolower($cur_ban['username']); 1030 } 1031 1032 // If not already loaded in a previous call, load the cached ranks 1033 if ($forum_config['o_ranks'] == '1' && !defined('FORUM_RANKS_LOADED')) 1034 { 1035 if (file_exists(FORUM_CACHE_DIR.'cache_ranks.php')) 1036 include FORUM_CACHE_DIR.'cache_ranks.php'; 1037 1038 if (!defined('FORUM_RANKS_LOADED')) 1039 { 1040 if (!defined('FORUM_CACHE_FUNCTIONS_LOADED')) 1041 require FORUM_ROOT.'include/cache.php'; 1042 1043 generate_ranks_cache(); 1044 require FORUM_CACHE_DIR.'cache_ranks.php'; 1045 } 1046 } 1047 1048 // If the user has a custom title 1049 if ($user['title'] != '') 1050 $user_title = forum_htmlencode($forum_config['o_censoring'] == '1' ? censor_words($user['title']) : $user['title']); 1051 // If the user is banned 1052 else if (in_array(utf8_strtolower($user['username']), $ban_list)) 1053 $user_title = $lang_common['Banned']; 1054 // If the user group has a default user title 1055 else if ($user['g_user_title'] != '') 1056 $user_title = forum_htmlencode($user['g_user_title']); 1057 // If the user is a guest 1058 else if ($user['g_id'] == FORUM_GUEST) 1059 $user_title = $lang_common['Guest']; 1060 else 1061 { 1062 // Are there any ranks? 1063 if ($forum_config['o_ranks'] == '1' && !empty($forum_ranks)) 1064 foreach ($forum_ranks as $cur_rank) 1065 if (intval($user['num_posts']) >= $cur_rank['min_posts']) 1066 $user_title = forum_htmlencode($cur_rank['rank']); 1067 1068 // If the user didn't "reach" any rank (or if ranks are disabled), we assign the default 1069 if (!isset($user_title)) 1070 $user_title = $lang_common['Member']; 1071 } 1072 1073 ($hook = get_hook('fn_get_title_end')) ? eval($hook) : null; 1074 1075 return $user_title; 1076} 1077 1078 1079// Return a list of all URL schemes installed 1080function get_scheme_packs() 1081{ 1082 $schemes = array(); 1083 1084 if ($handle = opendir(FORUM_ROOT.'include/url')) 1085 { 1086 while (false !== ($dirname = readdir($handle))) 1087 { 1088 $dirname = FORUM_ROOT.'include/url/'.$dirname; 1089 if (is_dir($dirname) && file_exists($dirname.'/forum_urls.php')) 1090 $schemes[] = basename($dirname); 1091 } 1092 closedir($handle); 1093 } 1094 1095 ($hook = get_hook('fn_get_scheme_packs_end')) ? eval($hook) : null; 1096 1097 return $schemes; 1098} 1099 1100 1101// Return a list of all styles installed 1102function get_style_packs() 1103{ 1104 $styles = array(); 1105 1106 if ($handle = opendir(FORUM_ROOT.'style')) 1107 { 1108 while (false !== ($dirname = readdir($handle))) 1109 { 1110 $dirname = FORUM_ROOT.'style/'.$dirname; 1111 $tempname = basename($dirname); 1112 if (is_dir($dirname) && file_exists($dirname.'/'.$tempname.'.php')) 1113 $styles[] = $tempname; 1114 } 1115 closedir($handle); 1116 } 1117 1118 ($hook = get_hook('fn_get_style_packs_end')) ? eval($hook) : null; 1119 1120 return $styles; 1121} 1122 1123 1124// Return a list of all language packs installed 1125function get_language_packs() 1126{ 1127 $languages = array(); 1128 1129 if ($handle = opendir(FORUM_ROOT.'lang')) 1130 { 1131 while (false !== ($dirname = readdir($handle))) 1132 { 1133 $dirname = FORUM_ROOT.'lang/'.$dirname; 1134 if (is_dir($dirname) && file_exists($dirname.'/common.php')) 1135 $languages[] = basename($dirname); 1136 } 1137 closedir($handle); 1138 } 1139 1140 ($hook = get_hook('fn_get_language_packs_end')) ? eval($hook) : null; 1141 1142 return $languages; 1143} 1144 1145 1146// Try to determine the correct remote IP-address 1147function get_remote_address() 1148{ 1149 $return = ($hook = get_hook('fn_get_remote_address_start')) ? eval($hook) : null; 1150 if ($return !== null) 1151 return $return; 1152 1153 return $_SERVER['REMOTE_ADDR']; 1154} 1155 1156 1157// Try to determine the current URL 1158function get_current_url($max_length = 0) 1159{ 1160 $return = ($hook = get_hook('fn_get_current_url_start')) ? eval($hook) : null; 1161 if ($return !== null) 1162 return $return; 1163 1164 $protocol = (!isset($_SERVER['HTTPS']) || strtolower($_SERVER['HTTPS']) == 'off') ? 'http://' : 'https://'; 1165 $port = (isset($_SERVER['SERVER_PORT']) && (($_SERVER['SERVER_PORT'] != '80' && $protocol == 'http://') || ($_SERVER['SERVER_PORT'] != '443' && $protocol == 'https://')) && strpos($_SERVER['HTTP_HOST'], ':') === false) ? ':'.$_SERVER['SERVER_PORT'] : ''; 1166 1167 $url = $protocol.$_SERVER['HTTP_HOST'].$port.$_SERVER['REQUEST_URI']; 1168 1169 if (strlen($url) <= $max_length || $max_length == 0) 1170 return $url; 1171 1172 // We can't find a short enough url 1173 return null; 1174} 1175 1176 1177// Checks if a word is a valid searchable word 1178function validate_search_word($word) 1179{ 1180 global $forum_user; 1181 static $stopwords; 1182 1183 $return = ($hook = get_hook('fn_validate_search_word_start')) ? eval($hook) : null; 1184 if ($return !== null) 1185 return $return; 1186 1187 if (!isset($stopwords)) 1188 { 1189 if (file_exists(FORUM_ROOT.'lang/'.$forum_user['language'].'/stopwords.txt')) 1190 { 1191 $stopwords = file(FORUM_ROOT.'lang/'.$forum_user['language'].'/stopwords.txt'); 1192 $stopwords = array_map('forum_trim', $stopwords); 1193 $stopwords = array_filter($stopwords); 1194 } 1195 else 1196 $stopwords = array(); 1197 1198 ($hook = get_hook('fn_validate_search_word_modify_stopwords')) ? eval($hook) : null; 1199 } 1200 1201 $num_chars = utf8_strlen($word); 1202 1203 $return = ($hook = get_hook('fn_validate_search_word_end')) ? eval($hook) : null; 1204 if ($return !== null) 1205 return $return; 1206 1207 return $num_chars >= FORUM_SEARCH_MIN_WORD && $num_chars <= FORUM_SEARCH_MAX_WORD && !in_array($word, $stopwords); 1208} 1209 1210 1211// Generate a random key of length $len 1212function random_key($len, $readable = false, $hash = false) 1213{ 1214 $key = ''; 1215 1216 $return = ($hook = get_hook('fn_random_key_start')) ? eval($hook) : null; 1217 if ($return !== null) 1218 return $return; 1219 1220 if ($hash) 1221 $key = substr(sha1(uniqid(rand(), true)), 0, $len); 1222 else if ($readable) 1223 { 1224 $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; 1225 1226 for ($i = 0; $i < $len; ++$i) 1227 $key .= substr($chars, (mt_rand() % strlen($chars)), 1); 1228 } 1229 else 1230 for ($i = 0; $i < $len; ++$i) 1231 $key .= chr(mt_rand(33, 126)); 1232 1233 ($hook = get_hook('fn_random_key_end')) ? eval($hook) : null; 1234 1235 return $key; 1236} 1237 1238 1239// Generates a valid CSRF token for use when submitting a form to $target_url 1240// $target_url should be an absolute URL and it should be exactly the URL that the user is going to 1241// Alternately, if the form token is going to be used in GET (which would mean the token is going to be 1242// a part of the URL itself), $target_url may be a plain string containing information related to the URL. 1243function generate_form_token($target_url) 1244{ 1245 global $forum_user; 1246 1247 $return = ($hook = get_hook('fn_generate_form_token_start')) ? eval($hook) : null; 1248 if ($return !== null) 1249 return $return; 1250 1251 return sha1(str_replace('&', '&', $target_url).$forum_user['csrf_token']); 1252} 1253 1254 1255// Generates a salted, SHA-1 hash of $str 1256function forum_hash($str, $salt) 1257{ 1258 $return = ($hook = get_hook('fn_forum_hash_start')) ? eval($hook) : null; 1259 if ($return !== null) 1260 return $return; 1261 1262 return sha1($salt.sha1($str)); 1263} 1264 1265 1266// Delete every .php file in the forum's cache directory 1267function forum_clear_cache() 1268{ 1269 $return = ($hook = get_hook('fn_forum_clear_cache_start')) ? eval($hook) : null; 1270 if ($return !== null) 1271 return; 1272 1273 $d = dir(FORUM_CACHE_DIR); 1274 if ($d) 1275 { 1276 while (($entry = $d->read()) !== false) 1277 { 1278 if (substr($entry, strlen($entry)-4) == '.php') 1279 @unlink(FORUM_CACHE_DIR.$entry); 1280 } 1281 $d->close(); 1282 } 1283} 1284 1285 1286// 1287// General forum specific functions 1288// 1289 1290// Authenticates the provided username and password against the user database 1291// $user can be either a user ID (integer) or a username (string) 1292// $password can be either a plaintext password or a password hash including salt ($password_is_hash must be set accordingly) 1293function authenticate_user($user, $password, $password_is_hash = false) 1294{ 1295 global $forum_db, $forum_user; 1296 1297 $return = ($hook = get_hook('fn_authenticate_user_start')) ? eval($hook) : null; 1298 if ($return !== null) 1299 return; 1300 1301 // Check if there's a user matching $user and $password 1302 $query = array( 1303 'SELECT' => 'u.*, g.*, o.logged, o.idle, o.csrf_token, o.prev_url', 1304 'FROM' => 'users AS u', 1305 'JOINS' => array( 1306 array( 1307 'INNER JOIN' => 'groups AS g', 1308 'ON' => 'g.g_id=u.group_id' 1309 ), 1310 array( 1311 'LEFT JOIN' => 'online AS o', 1312 'ON' => 'o.user_id=u.id' 1313 ) 1314 ) 1315 ); 1316 1317 // Are we looking for a user ID or a username? 1318 $query['WHERE'] = is_int($user) ? 'u.id='.intval($user) : 'u.username=\''.$forum_db->escape($user).'\''; 1319 1320 ($hook = get_hook('fn_authenticate_user_qr_get_user')) ? eval($hook) : null; 1321 $result = $forum_db->query_build($query) or error(__FILE__, __LINE__); 1322 $forum_user = $forum_db->fetch_assoc($result); 1323 1324 if (!isset($forum_user['id']) || 1325 ($password_is_hash && $password != $forum_user['password']) || 1326 (!$password_is_hash && forum_hash($password, $forum_user['salt']) != $forum_user['password'])) 1327 set_default_user(); 1328 1329 ($hook = get_hook('fn_authenticate_user_end')) ? eval($hook) : null; 1330} 1331 1332 1333// Attempt to login with the user ID and password hash from the cookie 1334function cookie_login(&$forum_user) 1335{ 1336 global $forum_db, $db_type, $forum_config, $cookie_name, $cookie_path, $cookie_domain, $cookie_secure, $forum_time_formats, $forum_date_formats; 1337 1338 $now = time(); 1339 $expire = $now + 1209600; // The cookie expires after 14 days 1340 1341 // We assume it's a guest 1342 $cookie = array('user_id' => 1, 'password_hash' => 'Guest', 'expiration_time' => 0, 'expire_hash' => 'Guest'); 1343 1344 $return = ($hook = get_hook('fn_cookie_login_start')) ? eval($hook) : null; 1345 if ($return !== null) 1346 return; 1347 1348 // If a cookie is set, we get the user_id and password hash from it 1349 if (!empty($_COOKIE[$cookie_name])) 1350 { 1351 $cookie_data = explode('|', base64_decode($_COOKIE[$cookie_name])); 1352 1353 if (!empty($cookie_data) && count($cookie_data) == 4) 1354 list($cookie['user_id'], $cookie['password_hash'], $cookie['expiration_time'], $cookie['expire_hash']) = $cookie_data; 1355 } 1356 1357 ($hook = get_hook('fn_cookie_login_fetch_cookie')) ? eval($hook) : null; 1358 1359 // If this a cookie for a logged in user and it shouldn't have already expired 1360 if (intval($cookie['user_id']) > 1 && intval($cookie['expiration_time']) > $now) 1361 { 1362 authenticate_user(intval($cookie['user_id']), $cookie['password_hash'], true); 1363 1364 // We now validate the cookie hash 1365 if ($cookie['expire_hash'] !== sha1($forum_user['salt'].$forum_user['password'].forum_hash(intval($cookie['expiration_time']), $forum_user['salt']))) 1366 set_default_user(); 1367 1368 // If we got back the default user, the login failed 1369 if ($forum_user['id'] == '1') 1370 { 1371 forum_setcookie($cookie_name, base64_encode('1|'.random_key(8, false, true).'|'.$expire.'|'.random_key(8, false, true)), $expire); 1372 return; 1373 } 1374 1375 // Send a new, updated cookie with a new expiration timestamp 1376 $expire = (intval($cookie['expiration_time']) > $now + $forum_config['o_timeout_visit']) ? $now + 1209600 : $now + $forum_config['o_timeout_visit']; 1377 forum_setcookie($cookie_name, base64_encode($forum_user['id'].'|'.$forum_user['password'].'|'.$expire.'|'.sha1($forum_user['salt'].$forum_user['password'].forum_hash($expire, $forum_user['salt']))), $expire); 1378 1379 // Set a default language if the user selected language no longer exists 1380 if (!file_exists(FORUM_ROOT.'lang/'.$forum_user['language'].'/common.php')) 1381 $forum_user['language'] = $forum_config['o_default_lang']; 1382 1383 // Set a default style if the user selected style no longer exists 1384 if (!file_exists(FORUM_ROOT.'style/'.$forum_user['style'].'/'.$forum_user['style'].'.php')) 1385 $forum_user['style'] = $forum_config['o_default_style']; 1386 1387 if (!$forum_user['disp_topics']) 1388 $forum_user['disp_topics'] = $forum_config['o_disp_topics_default']; 1389 if (!$forum_user['disp_posts']) 1390 $forum_user['disp_posts'] = $forum_config['o_disp_posts_default']; 1391 1392 // Check user has a valid date and time format 1393 if (!isset($forum_time_formats[$forum_user['time_format']])) 1394 $forum_user['time_format'] = 0; 1395 if (!isset($forum_date_formats[$forum_user['date_format']])) 1396 $forum_user['date_format'] = 0; 1397 1398 // Define this if you want this visit to affect the online list and the users last visit data 1399 if (!defined('FORUM_QUIET_VISIT')) 1400 { 1401 // Update the online list 1402 if (!$forum_user['logged']) 1403 { 1404 $forum_user['logged'] = $now; 1405 $forum_user['csrf_token'] = random_key(40, false, true); 1406 $forum_user['prev_url'] = get_current_url(255); 1407 1408 // REPLACE INTO avoids a user having two rows in the online table 1409 $query = array( 1410 'REPLACE' => 'user_id, ident, logged, csrf_token', 1411 'INTO' => 'online', 1412 'VALUES' => $forum_user['id'].', \''.$forum_db->escape($forum_user['username']).'\', '.$forum_user['logged'].', \''.$forum_user['csrf_token'].'\'', 1413 'UNIQUE' => 'user_id='.$forum_user['id'] 1414 ); 1415 1416 if ($forum_user['prev_url'] != null) 1417 { 1418 $query['REPLACE'] .= ', prev_url'; 1419 $query['VALUES'] .= ', \''.$forum_db->escape($forum_user['prev_url']).'\''; 1420 } 1421 1422 ($hook = get_hook('fn_cookie_login_qr_add_online_user')) ? eval($hook) : null; 1423 $forum_db->query_build($query) or error(__FILE__, __LINE__); 1424 1425 // Reset tracked topics 1426 set_tracked_topics(null); 1427 } 1428 else 1429 { 1430 // Special case: We've timed out, but no other user has browsed the forums since we timed out 1431 if ($forum_user['logged'] < ($now-$forum_config['o_timeout_visit'])) 1432 { 1433 $query = array( 1434 'UPDATE' => 'users', 1435 'SET' => 'last_visit='.$forum_user['logged'], 1436 'WHERE' => 'id='.$forum_user['id'] 1437 ); 1438 1439 ($hook = get_hook('fn_cookie_login_qr_update_user_visit')) ? eval($hook) : null; 1440 $forum_db->query_build($query) or error(__FILE__, __LINE__); 1441 1442 $forum_user['last_visit'] = $forum_user['logged']; 1443 } 1444 1445 // Now update the logged time and save the current URL in the online list 1446 $query = array( 1447 'UPDATE' => 'online', 1448 'SET' => 'logged='.$now, 1449 'WHERE' => 'user_id='.$forum_user['id'] 1450 ); 1451 1452 $current_url = get_current_url(255); 1453 if ($current_url != null && !defined('FORUM_REQUEST_AJAX')) 1454 $query['SET'] .= ', prev_url=\''.$forum_db->escape($current_url).'\''; 1455 1456 if ($forum_user['idle'] == '1') 1457 $query['SET'] .= ', idle=0'; 1458 1459 ($hook = get_hook('fn_cookie_login_qr_update_online_user')) ? eval($hook) : null; 1460 $forum_db->query_build($query) or error(__FILE__, __LINE__); 1461 1462 // Update tracked topics with the current expire time 1463 if (isset($_COOKIE[$cookie_name.'_track'])) 1464 forum_setcookie($cookie_name.'_track', $_COOKIE[$cookie_name.'_track'], $now + $forum_config['o_timeout_visit']); 1465 } 1466 } 1467 1468 $forum_user['is_guest'] = false; 1469 $forum_user['is_admmod'] = $forum_user['g_id'] == FORUM_ADMIN || $forum_user['g_moderator'] == '1'; 1470 } 1471 else 1472 set_default_user(); 1473 1474 ($hook = get_hook('fn_cookie_login_end')) ? eval($hook) : null; 1475} 1476 1477 1478// Fill $forum_user with default values (for guests) 1479function set_default_user() 1480{ 1481 global $forum_db, $db_type, $forum_user, $forum_config; 1482 1483 $remote_addr = get_remote_address(); 1484 1485 $return = ($hook = get_hook('fn_set_default_user_start')) ? eval($hook) : null; 1486 if ($return !== null) 1487 return; 1488 1489 // Fetch guest user 1490 $query = array( 1491 'SELECT' => 'u.*, g.*, o.logged, o.csrf_token, o.prev_url, o.last_post, o.last_search', 1492 'FROM' => 'users AS u', 1493 'JOINS' => array( 1494 array( 1495 'INNER JOIN' => 'groups AS g', 1496 'ON' => 'g.g_id=u.group_id' 1497 ), 1498 array( 1499 'LEFT JOIN' => 'online AS o', 1500 'ON' => 'o.ident=\''.$forum_db->escape($remote_addr).'\'' 1501 ) 1502 ), 1503 'WHERE' => 'u.id=1' 1504 ); 1505 1506 ($hook = get_hook('fn_set_default_user_qr_get_default_user')) ? eval($hook) : null; 1507 $result = $forum_db->query_build($query) or error(__FILE__, __LINE__); 1508 $forum_user = $forum_db->fetch_assoc($result); 1509 1510 if (!$forum_user) 1511 exit('Unable to fetch guest information. The table \''.$forum_db->prefix.'users\' must contain an entry with id = 1 that represents anonymous users.'); 1512 1513 if (!defined('FORUM_QUIET_VISIT')) 1514 { 1515 // Update online list 1516 if (!$forum_user['logged']) 1517 { 1518 $forum_user['logged'] = time(); 1519 $forum_user['csrf_token'] = random_key(40, false, true); 1520 $forum_user['prev_url'] = get_current_url(255); 1521 1522 // REPLACE INTO avoids a user having two rows in the online table 1523 $query = array( 1524 'REPLACE' => 'user_id, ident, logged, csrf_token', 1525 'INTO' => 'online', 1526 'VALUES' => '1, \''.$forum_db->escape($remote_addr).'\', '.$forum_user['logged'].', \''.$forum_user['csrf_token'].'\'', 1527 'UNIQUE' => 'user_id=1 AND ident=\''.$forum_db->escape($remote_addr).'\'' 1528 ); 1529 1530 if ($forum_user['prev_url'] != null) 1531 { 1532 $query['REPLACE'] .= ', prev_url'; 1533 $query['VALUES'] .= ', \''.$forum_db->escape($forum_user['prev_url']).'\''; 1534 } 1535 1536 ($hook = get_hook('fn_set_default_user_qr_add_online_guest_user')) ? eval($hook) : null; 1537 $forum_db->query_build($query) or error(__FILE__, __LINE__); 1538 } 1539 else 1540 { 1541 $query = array( 1542 'UPDATE' => 'online', 1543 'SET' => 'logged='.time(), 1544 'WHERE' => 'ident=\''.$forum_db->escape($remote_addr).'\'' 1545 ); 1546 1547 $current_url = get_current_url(255); 1548 if ($current_url != null) 1549 $query['SET'] .= ', prev_url=\''.$forum_db->escape($current_url).'\''; 1550 1551 ($hook = get_hook('fn_set_default_user_qr_update_online_guest_user')) ? eval($hook) : null; 1552 $forum_db->query_build($query) or error(__FILE__, __LINE__); 1553 } 1554 } 1555 1556 $forum_user['disp_topics'] = $forum_config['o_disp_topics_default']; 1557 $forum_user['disp_posts'] = $forum_config['o_disp_posts_default']; 1558 $forum_user['timezone'] = $forum_config['o_default_timezone']; 1559 $forum_user['dst'] = $forum_config['o_default_dst']; 1560 $forum_user['language'] = $forum_config['o_default_lang']; 1561 $forum_user['style'] = $forum_config['o_default_style']; 1562 $forum_user['is_guest'] = true; 1563 $forum_user['is_admmod'] = false; 1564 1565 ($hook = get_hook('fn_set_default_user_end')) ? eval($hook) : null; 1566} 1567 1568 1569// Check whether the connecting user is banned (and delete any expired bans while we're at it) 1570function check_bans() 1571{ 1572 global $forum_db, $forum_config, $lang_common, $forum_user, $forum_bans; 1573 1574 $return = ($hook = get_hook('fn_check_bans_start')) ? eval($hook) : null; 1575 if ($return !== null) 1576 return; 1577 1578 // Admins aren't affected 1579 if (defined('FORUM_ADMIN') && $forum_user['g_id'] == FORUM_ADMIN || !$forum_bans) 1580 return; 1581 1582 // Add a dot or a colon (depending on IPv4/IPv6) at the end of the IP address to prevent banned address 1583 // 192.168.0.5 from matching e.g. 192.168.0.50 1584 $user_ip = get_remote_address(); 1585 $user_ip .= (strpos($user_ip, '.') !== false) ? '.' : ':'; 1586 1587 $bans_altered = false; 1588 $is_banned = false; 1589 1590 foreach ($forum_bans as $cur_ban) 1591 { 1592 // Has this ban expired? 1593 if ($cur_ban['expire'] != '' && $cur_ban['expire'] <= time()) 1594 { 1595 $query = array( 1596 'DELETE' => 'bans', 1597 'WHERE' => 'id='.$cur_ban['id'] 1598 ); 1599 1600 ($hook = get_hook('fn_check_bans_qr_delete_expired_ban')) ? eval($hook) : null; 1601 $forum_db->query_build($query) or error(__FILE__, __LINE__); 1602 1603 $bans_altered = true; 1604 continue; 1605 } 1606 1607 if ($cur_ban['username'] != '' && utf8_strtolower($forum_user['username']) == utf8_strtolower($cur_ban['username'])) 1608 $is_banned = true; 1609 1610 if ($cur_ban['email'] != '' && $forum_user['email'] == $cur_ban['email']) 1611 $is_banned = true; 1612 1613 if ($cur_ban['ip'] != '') 1614 { 1615 $cur_ban_ips = explode(' ', $cur_ban['ip']); 1616 1617 $num_ips = count($cur_ban_ips); 1618 for ($i = 0; $i < $num_ips; ++$i) 1619 { 1620 // Add the proper ending to the ban 1621 if (strpos($user_ip, '.') !== false) 1622 $cur_ban_ips[$i] = $cur_ban_ips[$i].'.'; 1623 else 1624 $cur_ban_ips[$i] = $cur_ban_ips[$i].':'; 1625 1626 if (substr($user_ip, 0, strlen($cur_ban_ips[$i])) == $cur_ban_ips[$i]) 1627 { 1628 $is_banned = true; 1629 break; 1630 } 1631 } 1632 } 1633 1634 if ($is_banned) 1635 { 1636 $query = array( 1637 'DELETE' => 'online', 1638 'WHERE' => 'ident=\''.$forum_db->escape($forum_user['username']).'\'' 1639 ); 1640 1641 ($hook = get_hook('fn_check_bans_qr_delete_online_user')) ? eval($hook) : null; 1642 $forum_db->query_build($query) or error(__FILE__, __LINE__); 1643 1644 message($lang_common['Ban message'].(($cur_ban['expire'] != '') ? ' '.sprintf($lang_common['Ban message 2'], format_time($cur_ban['expire'], 1, null, null, true)) : '').(($cur_ban['message'] != '') ? ' '.$lang_common['Ban message 3'].'</p><p><strong>'.forum_htmlencode($cur_ban['message']).'</strong></p>' : '</p>').'<p>'.sprintf($lang_common['Ban message 4'], '<a href="mailto:'.forum_htmlencode($forum_config['o_admin_email']).'">'.forum_htmlencode($forum_config['o_admin_email']).'</a>')); 1645 } 1646 } 1647 1648 // If we removed any expired bans during our run-through, we need to regenerate the bans cache 1649 if ($bans_altered) 1650 { 1651 if (!defined('FORUM_CACHE_FUNCTIONS_LOADED')) 1652 require FORUM_ROOT.'include/cache.php'; 1653 1654 generate_bans_cache(); 1655 } 1656} 1657 1658 1659// Update "Users online" 1660function update_users_online() 1661{ 1662 global $forum_db, $forum_config, $forum_user; 1663 1664 $now = time(); 1665 1666 $return = ($hook = get_hook('fn_update_users_online_start')) ? eval($hook) : null; 1667 if ($return !== null) 1668 return; 1669 1670 1671 // Fetch all online list entries that are older than "o_timeout_online" 1672 $query = array( 1673 'SELECT' => 'o.*', 1674 'FROM' => 'online AS o', 1675 'WHERE' => 'o.logged < '.($now - $forum_config['o_timeout_online']) 1676 ); 1677 1678 ($hook = get_hook('fn_update_users_online_qr_get_old_online_users')) ? eval($hook) : null; 1679 $result = $forum_db->query_build($query) or error(__FILE__, __LINE__); 1680 1681 $need_delete_expired_guest = false; 1682 $expired_users_id = $idle_users_id = array(); 1683 while ($cur_user = $forum_db->fetch_assoc($result)) 1684 { 1685 if ($cur_user['user_id'] != '1') 1686 { 1687 // If the entry is older than "o_timeout_visit", update last_visit for the user in question, then delete him/her from the online list 1688 if ($cur_user['logged'] < ($now - $forum_config['o_timeout_visit'])) 1689 { 1690 $query = array( 1691 'UPDATE' => 'users', 1692 'SET' => 'last_visit='.$cur_user['logged'], 1693 'WHERE' => 'id='.$cur_user['user_id'] 1694 ); 1695 1696 ($hook = get_hook('fn_update_users_online_qr_update_user_visit')) ? eval($hook) : null; 1697 $forum_db->query_build($query) or error(__FILE__, __LINE__); 1698 1699 // Add to expired list 1700 $expired_users_id[] = $cur_user['user_id']; 1701 } 1702 else 1703 { 1704 // Add to idle list 1705 if ($cur_user['idle'] == '0') 1706 { 1707 $idle_users_id[] = $cur_user['user_id']; 1708 } 1709 } 1710 } 1711 else 1712 { 1713 // We have expired guest — delete it later 1714 $need_delete_expired_guest = true; 1715 } 1716 } 1717 1718 // Remove all guest that are older than "o_timeout_online" 1719 if ($need_delete_expired_guest) 1720 { 1721 $query = array( 1722 'DELETE' => 'online', 1723 'WHERE' => 'user_id=1 AND logged < '.($now - $forum_config['o_timeout_online']) 1724 ); 1725 ($hook = get_hook('fn_update_users_online_qr_delete_online_guest_user')) ? eval($hook) : null; 1726 $forum_db->query_build($query) or error(__FILE__, __LINE__); 1727 } 1728 1729 1730 // Delete expired users 1731 if (!empty($expired_users_id)) 1732 { 1733 $query = array( 1734 'DELETE' => 'online', 1735 'WHERE' => 'user_id IN ('.implode(',', $expired_users_id).')' 1736 ); 1737 1738 ($hook = get_hook('fn_update_users_online_qr_delete_online_user')) ? eval($hook) : null; 1739 $forum_db->query_build($query) or error(__FILE__, __LINE__); 1740 } 1741 1742 // Update idle users 1743 if (!empty($idle_users_id)) 1744 { 1745 $query = array( 1746 'UPDATE' => 'online', 1747 'SET' => 'idle=1', 1748 'WHERE' => 'user_id IN ('.implode(',', $idle_users_id).')' 1749 ); 1750 1751 ($hook = get_hook('fn_update_users_online_qr_update_user_idle')) ? eval($hook) : null; 1752 $forum_db->query_build($query) or error(__FILE__, __LINE__); 1753 } 1754 1755 ($hook = get_hook('fn_update_users_online_end')) ? eval($hook) : null; 1756} 1757 1758 1759// Save array of tracked topics in cookie 1760function set_tracked_topics($tracked_topics) 1761{ 1762 global $cookie_name, $cookie_path, $cookie_domain, $cookie_secure, $forum_config; 1763 1764 $return = ($hook = get_hook('fn_set_tracked_topics_start')) ? eval($hook) : null; 1765 if ($return !== null) 1766 return; 1767 1768 $cookie_data = ''; 1769 if (!empty($tracked_topics)) 1770 { 1771 // Sort the arrays (latest read first) 1772 arsort($tracked_topics['topics'], SORT_NUMERIC); 1773 arsort($tracked_topics['forums'], SORT_NUMERIC); 1774 1775 // Homebrew serialization (to avoid having to run unserialize() on cookie data) 1776 foreach ($tracked_topics['topics'] as $id => $timestamp) 1777 $cookie_data .= 't'.$id.'='.$timestamp.';'; 1778 foreach ($tracked_topics['forums'] as $id => $timestamp) 1779 $cookie_data .= 'f'.$id.'='.$timestamp.';'; 1780 1781 // Enforce a 4048 byte size limit (4096 minus some space for the cookie name) 1782 if (strlen($cookie_data) > 4048) 1783 { 1784 $cookie_data = substr($cookie_data, 0, 4048); 1785 $cookie_data = substr($cookie_data, 0, strrpos($cookie_data, ';')).';'; 1786 } 1787 } 1788 1789 forum_setcookie($cookie_name.'_track', $cookie_data, time() + $forum_config['o_timeout_visit']); 1790 $_COOKIE[$cookie_name.'_track'] = $cookie_data; // Set it directly in $_COOKIE as well 1791} 1792 1793 1794// Extract array of tracked topics from cookie 1795function get_tracked_topics() 1796{ 1797 global $cookie_name; 1798 1799 $return = ($hook = get_hook('fn_get_tracked_topics_start')) ? eval($hook) : null; 1800 if ($return !== null) 1801 return $return; 1802 1803 $cookie_data = isset($_COOKIE[$cookie_name.'_track']) ? $_COOKIE[$cookie_name.'_track'] : false; 1804 if (!$cookie_data) 1805 return array('topics' => array(), 'forums' => array()); 1806 1807 if (strlen($cookie_data) > 4048) 1808 return array('topics' => array(), 'forums' => array()); 1809 1810 // Unserialize data from cookie 1811 $tracked_topics = array('topics' => array(), 'forums' => array()); 1812 foreach (explode(';', $cookie_data) as $id_data) 1813 { 1814 switch (substr($id_data, 0, 1)) 1815 { 1816 case 'f': $type = 'forums'; break; 1817 case 't': $type = 'topics'; break; 1818 default: continue; 1819 } 1820 1821 $id = intval(substr($id_data, 1)); 1822 1823 if (($pos = strpos($id_data, '=')) === false) 1824 continue; 1825 $timestamp = intval(substr($id_data, $pos + 1)); 1826 1827 if ($id > 0 && $timestamp > 0) 1828 $tracked_topics[$type][$id] = $timestamp; 1829 } 1830 1831 ($hook = get_hook('fn_get_tracked_topics_end')) ? eval($hook) : null; 1832 1833 return $tracked_topics; 1834} 1835 1836 1837// Adds a new user. The username must be passed through validate_username() first. 1838function add_user($user_info, &$new_uid) 1839{ 1840 global $forum_db, $base_url, $lang_common, $forum_config, $forum_user, $forum_url; 1841 1842 $return = ($hook = get_hook('fn_add_user_start')) ? eval($hook) : null; 1843 if ($return !== null) 1844 return; 1845 1846 // Add the user 1847 $query = array( 1848 'INSERT' => 'username, group_id, password, email, email_setting, timezone, dst, language, style, registered, registration_ip, last_visit, salt, activate_key', 1849 'INTO' => 'users', 1850 'VALUES' => '\''.$forum_db->escape($user_info['username']).'\', '.$user_info['group_id'].', \''.$forum_db->escape($user_info['password_hash']).'\', \''.$forum_db->escape($user_info['email']).'\', '.$user_info['email_setting'].', '.floatval($user_info['timezone']).', '.$user_info['dst'].', \''.$forum_db->escape($user_info['language']).'\', \''.$forum_db->escape($user_info['style']).'\', '.$user_info['registered'].', \''.$forum_db->escape($user_info['registration_ip']).'\', '.$user_info['registered'].', \''.$forum_db->escape($user_info['salt']).'\', '.$user_info['activate_key'].'' 1851 ); 1852 1853 ($hook = get_hook('fn_add_user_qr_insert_user')) ? eval($hook) : null; 1854 $forum_db->query_build($query) or error(__FILE__, __LINE__); 1855 $new_uid = $forum_db->insert_id(); 1856 1857 // Must the user verify the registration? 1858 if ($user_info['require_verification']) 1859 { 1860 // Load the "welcome" template 1861 $mail_tpl = forum_trim(file_get_contents(FORUM_ROOT.'lang/'.$forum_user['language'].'/mail_templates/welcome.tpl')); 1862 1863 // The first row contains the subject 1864 $first_crlf = strpos($mail_tpl, "\n"); 1865 $mail_subject = forum_trim(substr($mail_tpl, 8, $first_crlf-8)); 1866 $mail_message = forum_trim(substr($mail_tpl, $first_crlf)); 1867 1868 $mail_subject = str_replace('<board_title>', $forum_config['o_board_title'], $mail_subject); 1869 $mail_message = str_replace('<base_url>', $base_url.'/', $mail_message); 1870 $mail_message = str_replace('<username>', $user_info['username'], $mail_message); 1871 $mail_message = str_replace('<activation_url>', str_replace('&', '&', forum_link($forum_url['change_password_key'], array($new_uid, substr($user_info['activate_key'], 1, -1)))), $mail_message); 1872 $mail_message = str_replace('<board_mailer>', sprintf($lang_common['Forum mailer'], $forum_config['o_board_title']), $mail_message); 1873 1874 ($hook = get_hook('fn_add_user_send_verification')) ? eval($hook) : null; 1875 1876 forum_mail($user_info['email'], $mail_subject, $mail_message); 1877 } 1878 1879 // Should we alert people on the admin mailing list that a new user has registered? 1880 if ($user_info['notify_admins'] && $forum_config['o_mailing_list'] != '') 1881 { 1882 $mail_subject = 'Alert - New registration'; 1883 $mail_message = 'User \''.$user_info['username'].'\' registered in the forums at '.$base_url.'/'."\n\n".'User profile: '.forum_link($forum_url['user'], $new_uid)."\n\n".'-- '."\n".'Forum Mailer'."\n".'(Do not reply to this message)'; 1884 1885 forum_mail($forum_config['o_mailing_list'], $mail_subject, $mail_message); 1886 } 1887 1888 ($hook = get_hook('fn_add_user_end')) ? eval($hook) : null; 1889} 1890 1891 1892// Delete a user and all information associated with it 1893function delete_user($user_id, $delete_posts = false) 1894{ 1895 global $forum_db, $db_type, $forum_config; 1896 1897 $return = ($hook = get_hook('fn_delete_user_start')) ? eval($hook) : null; 1898 if ($return !== null) 1899 return; 1900 1901 // First we need to get some data on the user 1902 $query = array( 1903 'SELECT' => 'u.username, u.group_id, g.g_moderator', 1904 'FROM' => 'users AS u', 1905 'JOINS' => array( 1906 array( 1907 'INNER JOIN' => 'groups AS g', 1908 'ON' => 'g.g_id=u.group_id' 1909 ) 1910 ), 1911 'WHERE' => 'u.id='.$user_id 1912 ); 1913 1914 ($hook = get_hook('fn_delete_user_qr_get_user_data')) ? eval($hook) : null; 1915 $result = $forum_db->query_build($query) or error(__FILE__, __LINE__); 1916 $user = $forum_db->fetch_assoc($result); 1917 1918 // Delete any subscriptions 1919 $query = array( 1920 'DELETE' => 'subscriptions', 1921 'WHERE' => 'user_id='.$user_id 1922 ); 1923 1924 ($hook = get_hook('fn_delete_user_qr_delete_subscriptions')) ? eval($hook) : null; 1925 $forum_db->query_build($query) or error(__FILE__, __LINE__); 1926 1927 // Delete any subscriptions forum 1928 $query = array( 1929 'DELETE' => 'forum_subscriptions', 1930 'WHERE' => 'user_id='.$user_id 1931 ); 1932 1933 ($hook = get_hook('fn_delete_user_qr_delete_forum_subscriptions')) ? eval($hook) : null; 1934 $forum_db->query_build($query) or error(__FILE__, __LINE__); 1935 1936 // Remove him/her from the online list (if they happen to be logged in) 1937 $query = array( 1938 'DELETE' => 'online', 1939 'WHERE' => 'user_id='.$user_id 1940 ); 1941 1942 ($hook = get_hook('fn_delete_user_qr_delete_online')) ? eval($hook) : null; 1943 $forum_db->query_build($query) or error(__FILE__, __LINE__); 1944 1945 // Should we delete all posts made by this user? 1946 if ($delete_posts) 1947 { 1948 @set_time_limit(0); 1949 1950 // Find all posts made by this user 1951 $query = array( 1952 'SELECT' => 'p.id, p.topic_id, t.forum_id, t.first_post_id', 1953 'FROM' => 'posts AS p', 1954 'JOINS' => array( 1955 array( 1956 'INNER JOIN' => 'topics AS t', 1957 'ON' => 't.id=p.topic_id' 1958 ) 1959 ), 1960 'WHERE' => 'p.poster_id='.$user_id 1961 ); 1962 1963 ($hook = get_hook('fn_delete_user_qr_get_user_posts')) ? eval($hook) : null; 1964 $result = $forum_db->query_build($query) or error(__FILE__, __LINE__); 1965 while ($cur_post = $forum_db->fetch_assoc($result)) 1966 { 1967 if ($cur_post['first_post_id'] == $cur_post['id']) 1968 delete_topic($cur_post['topic_id'], $cur_post['forum_id']); 1969 else 1970 delete_post($cur_post['id'], $cur_post['topic_id'], $cur_post['forum_id']); 1971 } 1972 } 1973 else 1974 { 1975 // Set all his/her posts to guest 1976 $query = array( 1977 'UPDATE' => 'posts', 1978 'SET' => 'poster_id=1', 1979 'WHERE' => 'poster_id='.$user_id 1980 ); 1981 1982 ($hook = get_hook('fn_delete_user_qr_reset_user_posts')) ? eval($hook) : null; 1983 $forum_db->query_build($query) or error(__FILE__, __LINE__); 1984 } 1985 1986 // Delete the user 1987 $query = array( 1988 'DELETE' => 'users', 1989 'WHERE' => 'id='.$user_id 1990 ); 1991 1992 ($hook = get_hook('fn_delete_user_qr_delete_user')) ? eval($hook) : null; 1993 $forum_db->query_build($query) or error(__FILE__, __LINE__); 1994 1995 // Delete user avatar 1996 delete_avatar($user_id); 1997 1998 // If the user is a moderator or an administrator, we remove him/her from the moderator list in all forums 1999 // and regenerate the bans cache (in case he/she created any bans) 2000 if ($user['group_id'] == FORUM_ADMIN || $user['g_moderator'] == '1') 2001 { 2002 clean_forum_moderators(); 2003 2004 // Regenerate the bans cache 2005 if (!defined('FORUM_CACHE_FUNCTIONS_LOADED')) 2006 require FORUM_ROOT.'include/cache.php'; 2007 2008 generate_bans_cache(); 2009 } 2010 2011 ($hook = get_hook('fn_delete_user_end')) ? eval($hook) : null; 2012} 2013 2014 2015// Check if a username is occupied 2016function check_username_dupe($username, $exclude_id = null) 2017{ 2018 global $forum_db; 2019 2020 $return = ($hook = get_hook('fn_check_username_dupe_start')) ? eval($hook) : null; 2021 if ($return !== null) 2022 return $return; 2023 2024 $query = array( 2025 'SELECT' => 'u.username', 2026 'FROM' => 'users AS u', 2027 'WHERE' => '(UPPER(username)=UPPER(\''.$forum_db->escape($username).'\') OR UPPER(username)=UPPER(\''.$forum_db->escape(preg_replace('/[^\w]/u', '', $username)).'\')) AND id>1' 2028 ); 2029 2030 if ($exclude_id) 2031 $query['WHERE'] .= ' AND id!='.$exclude_id; 2032 2033 ($hook = get_hook('fn_check_username_dupe_qr_check_username_dupe')) ? eval($hook) : null; 2034 $result = $forum_db->query_build($query) or error(__FILE__, __LINE__); 2035 $dupe_name = $forum_db->result($result); 2036 2037 return (is_null($dupe_name) || $dupe_name === false) ? false : $dupe_name; 2038} 2039 2040 2041// Deletes any avatars owned by the specified user ID 2042function delete_avatar($user_id) 2043{ 2044 global $forum_db, $db_type, $forum_config; 2045 2046 $filetypes = array('jpg', 'gif', 'png'); 2047 2048 $return = ($hook = get_hook('fn_delete_avatar_start')) ? eval($hook) : null; 2049 if ($return !== null) 2050 return; 2051 2052 // Delete user avatar from FS 2053 foreach ($filetypes as $cur_type) 2054 { 2055 $avatar = FORUM_ROOT.$forum_config['o_avatars_dir'].'/'.$user_id.'.'.$cur_type; 2056 if (file_exists($avatar)) 2057 { 2058 @unlink($avatar); 2059 } 2060 } 2061 2062 // Delete user avatar from DB 2063 $query = array( 2064 'UPDATE' => 'users', 2065 'SET' => 'avatar=\''.FORUM_AVATAR_NONE.'\', avatar_height=\'0\', avatar_width=\'0\'', 2066 'WHERE' => 'id='.$user_id 2067 ); 2068 2069 ($hook = get_hook('fn_delete_avatar_qr_delete_avatar')) ? eval($hook) : null; 2070 $forum_db->query_build($query) or error(__FILE__, __LINE__); 2071} 2072 2073 2074// Creates a new topic with its first post 2075function add_topic($post_info, &$new_tid, &$new_pid) 2076{ 2077 global $forum_db, $db_type, $forum_config, $lang_common; 2078 2079 $return = ($hook = get_hook('fn_add_topic_start')) ? eval($hook) : null; 2080 if ($return !== null) 2081 return; 2082 2083 // Add the topic 2084 $query = array( 2085 'INSERT' => 'poster, subject, posted, last_post, last_poster, forum_id', 2086 'INTO' => 'topics', 2087 'VALUES' => '\''.$forum_db->escape($post_info['poster']).'\', \''.$forum_db->escape($post_info['subject']).'\', '.$post_info['posted'].', '.$post_info['posted'].', \''.$forum_db->escape($post_info['poster']).'\', '.$post_info['forum_id'] 2088 ); 2089 2090 ($hook = get_hook('fn_add_topic_qr_add_topic')) ? eval($hook) : null; 2091 $forum_db->query_build($query) or error(__FILE__, __LINE__); 2092 $new_tid = $forum_db->insert_id(); 2093 2094 // To subscribe or not to subscribe, that ... 2095 if (!$post_info['is_guest'] && $post_info['subscribe']) 2096 { 2097 $query = array( 2098 'INSERT' => 'user_id, topic_id', 2099 'INTO' => 'subscriptions', 2100 'VALUES' => $post_info['poster_id'].' ,'.$new_tid 2101 ); 2102 2103 ($hook = get_hook('fn_add_topic_qr_add_subscription')) ? eval($hook) : null; 2104 $forum_db->query_build($query) or error(__FILE__, __LINE__); 2105 } 2106 2107 // Create the post ("topic post") 2108 $query = array( 2109 'INSERT' => 'poster, poster_id, poster_ip, message, hide_smilies, posted, topic_id', 2110 'INTO' => 'posts', 2111 'VALUES' => '\''.$forum_db->escape($post_info['poster']).'\', '.$post_info['poster_id'].', \''.$forum_db->escape(get_remote_address()).'\', \''.$forum_db->escape($post_info['message']).'\', '.$post_info['hide_smilies'].', '.$post_info['posted'].', '.$new_tid 2112 ); 2113 2114 // If it's a guest post, there might be an e-mail address we need to include 2115 if ($post_info['is_guest'] && $post_info['poster_email'] != null) 2116 { 2117 $query['INSERT'] .= ', poster_email'; 2118 $query['VALUES'] .= ', \''.$forum_db->escape($post_info['poster_email']).'\''; 2119 } 2120 2121 ($hook = get_hook('fn_add_topic_qr_add_topic_post')) ? eval($hook) : null; 2122 $forum_db->query_build($query) or error(__FILE__, __LINE__); 2123 $new_pid = $forum_db->insert_id(); 2124 2125 // Update the topic with last_post_id and first_post_id 2126 $query = array( 2127 'UPDATE' => 'topics', 2128 'SET' => 'last_post_id='.$new_pid.', first_post_id='.$new_pid, 2129 'WHERE' => 'id='.$new_tid 2130 ); 2131 2132 ($hook = get_hook('fn_add_topic_qr_update_topic')) ? eval($hook) : null; 2133 $forum_db->query_build($query) or error(__FILE__, __LINE__); 2134 2135 if (!defined('FORUM_SEARCH_IDX_FUNCTIONS_LOADED')) 2136 require FORUM_ROOT.'include/search_idx.php'; 2137 2138 update_search_index('post', $new_pid, $post_info['message'], $post_info['subject']); 2139 2140 sync_forum($post_info['forum_id']); 2141 2142 send_forum_subscriptions($post_info, $new_tid); 2143 2144 // Increment user's post count & last post time 2145 if (isset($post_info['update_user']) && $post_info['update_user']) 2146 { 2147 if ($post_info['is_guest']) 2148 { 2149 $query = array( 2150 'UPDATE' => 'online', 2151 'SET' => 'last_post='.$post_info['posted'], 2152 'WHERE' => 'ident=\''.$forum_db->escape(get_remote_address()).'\'' 2153 ); 2154 } 2155 else 2156 { 2157 $query = array( 2158 'UPDATE' => 'users', 2159 'SET' => 'num_posts=num_posts+1, last_post='.$post_info['posted'], 2160 'WHERE' => 'id='.$post_info['poster_id'] 2161 ); 2162 } 2163 2164 ($hook = get_hook('fn_add_topic_qr_update_last_post')) ? eval($hook) : null; 2165 $forum_db->query_build($query) or error(__FILE__, __LINE__); 2166 } 2167 2168 // If the posting user is logged in update his/her unread indicator 2169 if (!$post_info['is_guest'] && isset($post_info['update_unread']) && $post_info['update_unread']) 2170 { 2171 $tracked_topics = get_tracked_topics(); 2172 $tracked_topics['topics'][$new_tid] = time(); 2173 set_tracked_topics($tracked_topics); 2174 } 2175 2176 ($hook = get_hook('fn_add_topic_end')) ? eval($hook) : null; 2177} 2178 2179 2180// Delete a topic and all of it's posts 2181function delete_topic($topic_id, $forum_id) 2182{ 2183 global $forum_db, $db_type; 2184 2185 $return = ($hook = get_hook('fn_delete_topic_start')) ? eval($hook) : null; 2186 if ($return !== null) 2187 return; 2188 2189 // Create an array of forum IDs that need to be synced 2190 $forum_ids = array($forum_id); 2191 $query = array( 2192 'SELECT' => 't.forum_id', 2193 'FROM' => 'topics AS t', 2194 'WHERE' => 't.moved_to='.$topic_id 2195 ); 2196 2197 ($hook = get_hook('fn_delete_topic_qr_get_forums_to_sync')) ? eval($hook) : null; 2198 $result = $forum_db->query_build($query) or error(__FILE__, __LINE__); 2199 while ($row = $forum_db->fetch_assoc($result)) 2200 $forum_ids[] = $row['forum_id']; 2201 2202 // Delete the topic and any redirect topics 2203 $query = array( 2204 'DELETE' => 'topics', 2205 'WHERE' => 'id='.$topic_id.' OR moved_to='.$topic_id 2206 ); 2207 2208 ($hook = get_hook('fn_delete_topic_qr_delete_topic')) ? eval($hook) : null; 2209 $forum_db->query_build($query) or error(__FILE__, __LINE__); 2210 2211 // Create a list of the post ID's in this topic 2212 $query = array( 2213 'SELECT' => 'p.id', 2214 'FROM' => 'posts AS p', 2215 'WHERE' => 'p.topic_id='.$topic_id 2216 ); 2217 2218 ($hook = get_hook('fn_delete_topic_qr_get_posts_to_delete')) ? eval($hook) : null; 2219 $result = $forum_db->query_build($query) or error(__FILE__, __LINE__); 2220 2221 $post_ids = array(); 2222 while ($row = $forum_db->fetch_assoc($result)) 2223 $post_ids[] = $row['id']; 2224 2225 // Make sure we have a list of post ID's 2226 if (!empty($post_ids)) 2227 { 2228 // Delete posts in topic 2229 $query = array( 2230 'DELETE' => 'posts', 2231 'WHERE' => 'topic_id='.$topic_id 2232 ); 2233 2234 ($hook = get_hook('fn_delete_topic_qr_delete_topic_posts')) ? eval($hook) : null; 2235 $forum_db->query_build($query) or error(__FILE__, __LINE__); 2236 2237 if (!defined('FORUM_SEARCH_IDX_FUNCTIONS_LOADED')) 2238 require_once FORUM_ROOT.'include/search_idx.php'; 2239 2240 strip_search_index($post_ids); 2241 } 2242 2243 // Delete any subscriptions for this topic 2244 $query = array( 2245 'DELETE' => 'subscriptions', 2246 'WHERE' => 'topic_id='.$topic_id 2247 ); 2248 2249 ($hook = get_hook('fn_delete_topic_qr_delete_topic_subscriptions')) ? eval($hook) : null; 2250 $forum_db->query_build($query) or error(__FILE__, __LINE__); 2251 2252 foreach ($forum_ids as $cur_forum_id) 2253 sync_forum($cur_forum_id); 2254 2255 ($hook = get_hook('fn_delete_topic_end')) ? eval($hook) : null; 2256} 2257 2258 2259// Locate and delete any orphaned redirect topics 2260function delete_orphans() 2261{ 2262 global $forum_db; 2263 2264 $return = ($hook = get_hook('fn_delete_orphans_start')) ? eval($hook) : null; 2265 if ($return !== null) 2266 return; 2267 2268 // Locate any orphaned redirect topics 2269 $query = array( 2270 'SELECT' => 't1.id', 2271 'FROM' => 'topics AS t1', 2272 'JOINS' => array( 2273 array( 2274 'LEFT JOIN' => 'topics AS t2', 2275 'ON' => 't1.moved_to=t2.id' 2276 ) 2277 ), 2278 'WHERE' => 't2.id IS NULL AND t1.moved_to IS NOT NULL' 2279 ); 2280 2281 ($hook = get_hook('fn_delete_orphans_qr_get_orphans')) ? eval($hook) : null; 2282 $result = $forum_db->query_build($query) or error(__FILE__, __LINE__); 2283 2284 $orphans = array(); 2285 while ($row = $forum_db->fetch_assoc($result)) 2286 { 2287 $orphans[] = $row['id']; 2288 } 2289 2290 if (!empty($orphans)) 2291 { 2292 // Delete the orphan 2293 $query = array( 2294 'DELETE' => 'topics', 2295 'WHERE' => 'id IN('.implode(',', $orphans).')' 2296 ); 2297 2298 ($hook = get_hook('fn_delete_orphans_qr_delete_orphan')) ? eval($hook) : null; 2299 $forum_db->query_build($query) or error(__FILE__, __LINE__); 2300 } 2301 2302 ($hook = get_hook('fn_delete_orphans_end')) ? eval($hook) : null; 2303} 2304 2305 2306// Creates a new post 2307function add_post($post_info, &$new_pid) 2308{ 2309 global $forum_db, $db_type, $forum_config, $lang_common; 2310 2311 $return = ($hook = get_hook('fn_add_post_start')) ? eval($hook) : null; 2312 if ($return !== null) 2313 return; 2314 2315 // Add the post 2316 $query = array( 2317 'INSERT' => 'poster, poster_id, poster_ip, message, hide_smilies, posted, topic_id', 2318 'INTO' => 'posts', 2319 'VALUES' => '\''.$forum_db->escape($post_info['poster']).'\', '.$post_info['poster_id'].', \''.$forum_db->escape(get_remote_address()).'\', \''.$forum_db->escape($post_info['message']).'\', '.$post_info['hide_smilies'].', '.$post_info['posted'].', '.$post_info['topic_id'] 2320 ); 2321 2322 // If it's a guest post, there might be an e-mail address we need to include 2323 if ($post_info['is_guest'] && $post_info['poster_email'] != null) 2324 { 2325 $query['INSERT'] .= ', poster_email'; 2326 $query['VALUES'] .= ', \''.$forum_db->escape($post_info['poster_email']).'\''; 2327 } 2328 2329 ($hook = get_hook('fn_add_post_qr_add_post')) ? eval($hook) : null; 2330 $forum_db->query_build($query) or error(__FILE__, __LINE__); 2331 $new_pid = $forum_db->insert_id(); 2332 2333 if (!$post_info['is_guest']) 2334 { 2335 // Subscribe or unsubscribe? 2336 if ($post_info['subscr_action'] == 1) 2337 { 2338 $query = array( 2339 'INSERT' => 'user_id, topic_id', 2340 'INTO' => 'subscriptions', 2341 'VALUES' => $post_info['poster_id'].' ,'.$post_info['topic_id'] 2342 ); 2343 2344 ($hook = get_hook('fn_add_post_qr_add_subscription')) ? eval($hook) : null; 2345 $forum_db->query_build($query) or error(__FILE__, __LINE__); 2346 } 2347 else if ($post_info['subscr_action'] == 2) 2348 { 2349 $query = array( 2350 'DELETE' => 'subscriptions', 2351 'WHERE' => 'topic_id='.$post_info['topic_id'].' AND user_id='.$post_info['poster_id'] 2352 ); 2353 2354 ($hook = get_hook('fn_add_post_qr_delete_subscription')) ? eval($hook) : null; 2355 $forum_db->query_build($query) or error(__FILE__, __LINE__); 2356 } 2357 } 2358 2359 // Count number of replies in the topic 2360 $query = array( 2361 'SELECT' => 'COUNT(p.id)', 2362 'FROM' => 'posts AS p', 2363 'WHERE' => 'p.topic_id='.$post_info['topic_id'] 2364 ); 2365 2366 ($hook = get_hook('fn_add_post_qr_get_topic_reply_count')) ? eval($hook) : null; 2367 $result = $forum_db->query_build($query) or error(__FILE__, __LINE__); 2368 $num_replies = $forum_db->result($result, 0) - 1; 2369 2370 // Update topic 2371 $query = array( 2372 'UPDATE' => 'topics', 2373 'SET' => 'num_replies='.$num_replies.', last_post='.$post_info['posted'].', last_post_id='.$new_pid.', last_poster=\''.$forum_db->escape($post_info['poster']).'\'', 2374 'WHERE' => 'id='.$post_info['topic_id'] 2375 ); 2376 2377 ($hook = get_hook('fn_add_post_qr_update_topic')) ? eval($hook) : null; 2378 $forum_db->query_build($query) or error(__FILE__, __LINE__); 2379 2380 sync_forum($post_info['forum_id']); 2381 2382 if (!defined('FORUM_SEARCH_IDX_FUNCTIONS_LOADED')) 2383 require FORUM_ROOT.'include/search_idx.php'; 2384 2385 update_search_index('post', $new_pid, $post_info['message']); 2386 2387 send_subscriptions($post_info, $new_pid); 2388 2389 // Increment user's post count & last post time 2390 if (isset($post_info['update_user'])) 2391 { 2392 if ($post_info['is_guest']) 2393 { 2394 $query = array( 2395 'UPDATE' => 'online', 2396 'SET' => 'last_post='.$post_info['posted'], 2397 'WHERE' => 'ident=\''.$forum_db->escape(get_remote_address()).'\'' 2398 ); 2399 } 2400 else 2401 { 2402 $query = array( 2403 'UPDATE' => 'users', 2404 'SET' => 'num_posts=num_posts+1, last_post='.$post_info['posted'], 2405 'WHERE' => 'id='.$post_info['poster_id'] 2406 ); 2407 } 2408 2409 ($hook = get_hook('fn_add_post_qr_update_last_post')) ? eval($hook) : null; 2410 $forum_db->query_build($query) or error(__FILE__, __LINE__); 2411 } 2412 2413 // If the posting user is logged in update his/her unread indicator 2414 if (!$post_info['is_guest'] && isset($post_info['update_unread']) && $post_info['update_unread']) 2415 { 2416 $tracked_topics = get_tracked_topics(); 2417 $tracked_topics['topics'][$post_info['topic_id']] = time(); 2418 set_tracked_topics($tracked_topics); 2419 } 2420 2421 ($hook = get_hook('fn_add_post_end')) ? eval($hook) : null; 2422} 2423 2424 2425// Delete a single post 2426function delete_post($post_id, $topic_id, $forum_id) 2427{ 2428 global $forum_db, $db_type; 2429 2430 $return = ($hook = get_hook('fn_delete_post_start')) ? eval($hook) : null; 2431 if ($return !== null) 2432 return; 2433 2434 $query = array( 2435 'SELECT' => 'p.id, p.poster, p.posted', 2436 'FROM' => 'posts AS p', 2437 'WHERE' => 'p.topic_id='.$topic_id, 2438 'ORDER BY' => 'p.id DESC', 2439 'LIMIT' => '2' 2440 ); 2441 2442 ($hook = get_hook('fn_qr_get_topic_lastposts_info')) ? eval($hook) : null; 2443 $result = $forum_db->query_build($query) or error(__FILE__, __LINE__); 2444 list($last_id, ,) = $forum_db->fetch_row($result); 2445 list($second_last_id, $second_poster, $second_posted) = $forum_db->fetch_row($result); 2446 2447 // Delete the post 2448 $query = array( 2449 'DELETE' => 'posts', 2450 'WHERE' => 'id='.$post_id 2451 ); 2452 2453 ($hook = get_hook('fn_delete_post_qr_delete_post')) ? eval($hook) : null; 2454 $forum_db->query_build($query) or error(__FILE__, __LINE__); 2455 2456 if (!defined('FORUM_SEARCH_IDX_FUNCTIONS_LOADED')) 2457 require FORUM_ROOT.'include/search_idx.php'; 2458 2459 strip_search_index($post_id); 2460 2461 // Count number of replies in the topic 2462 $query = array( 2463 'SELECT' => 'COUNT(p.id)', 2464 'FROM' => 'posts AS p', 2465 'WHERE' => 'p.topic_id='.$topic_id 2466 ); 2467 2468 ($hook = get_hook('fn_qr_get_topic_reply_count2')) ? eval($hook) : null; 2469 $result = $forum_db->query_build($query) or error(__FILE__, __LINE__); 2470 $num_replies = $forum_db->result($result) - 1; 2471 2472 // Update the topic now that a post has been deleted 2473 $query = array( 2474 'UPDATE' => 'topics', 2475 'SET' => 'num_replies='.$num_replies, 2476 'WHERE' => 'id='.$topic_id 2477 ); 2478 2479 // If we deleted the most recent post, we need to sync up last post data as wel 2480 if ($last_id == $post_id) 2481 $query['SET'] .= ', last_post='.$second_posted.', last_post_id='.$second_last_id.', last_poster=\''.$forum_db->escape($second_poster).'\''; 2482 2483 ($hook = get_hook('fn_qr_update_topic2')) ? eval($hook) : null; 2484 $forum_db->query_build($query) or error(__FILE__, __LINE__); 2485 2486 sync_forum($forum_id); 2487 2488 ($hook = get_hook('fn_delete_post_end')) ? eval($hook) : null; 2489} 2490 2491 2492// Update posts, topics, last_post, last_post_id and last_poster for a forum 2493function sync_forum($forum_id) 2494{ 2495 global $forum_db; 2496 2497 $return = ($hook = get_hook('fn_sync_forum_start')) ? eval($hook) : null; 2498 if ($return !== null) 2499 return; 2500 2501 // Get topic and post count for forum 2502 $query = array( 2503 'SELECT' => 'COUNT(t.id) AS num_topics, SUM(t.num_replies) AS num_posts', 2504 'FROM' => 'topics AS t', 2505 'WHERE' => 't.forum_id='.$forum_id 2506 ); 2507 2508 ($hook = get_hook('fn_sync_forum_qr_get_forum_stats')) ? eval($hook) : null; 2509 $result = $forum_db->query_build($query) or error(__FILE__, __LINE__); 2510 $forum_stats = $forum_db->fetch_assoc($result); 2511 2512 // $num_posts is only the sum of all replies (we have to add the topic posts) 2513 $forum_stats['num_posts'] = $forum_stats['num_posts'] + $forum_stats['num_topics']; 2514 2515 2516 // Get last_post, last_post_id and last_poster for forum (if any) 2517 $query = array( 2518 'SELECT' => 't.last_post, t.last_post_id, t.last_poster', 2519 'FROM' => 'topics AS t', 2520 'WHERE' => 't.forum_id='.$forum_id.' AND t.moved_to is NULL', 2521 'ORDER BY' => 't.last_post DESC', 2522 'LIMIT' => '1' 2523 ); 2524 2525 ($hook = get_hook('fn_sync_forum_qr_get_forum_last_post_data')) ? eval($hook) : null; 2526 $result = $forum_db->query_build($query) or error(__FILE__, __LINE__); 2527 $last_post_info = $forum_db->fetch_assoc($result); 2528 2529 if ($last_post_info) 2530 { 2531 $last_post_info['last_poster'] = '\''.$forum_db->escape($last_post_info['last_poster']).'\''; 2532 } 2533 else 2534 $last_post_info['last_post'] = $last_post_info['last_post_id'] = $last_post_info['last_poster'] = 'NULL'; 2535 2536 // Now update the forum 2537 $query = array( 2538 'UPDATE' => 'forums', 2539 'SET' => 'num_topics='.$forum_stats['num_topics'].', num_posts='.$forum_stats['num_posts'].', last_post='.$last_post_info['last_post'].', last_post_id='.$last_post_info['last_post_id'].', last_poster='.$last_post_info['last_poster'], 2540 'WHERE' => 'id='.$forum_id 2541 ); 2542 2543 ($hook = get_hook('fn_sync_forum_qr_update_forum')) ? eval($hook) : null; 2544 $forum_db->query_build($query) or error(__FILE__, __LINE__); 2545 2546 ($hook = get_hook('fn_sync_forum_end')) ? eval($hook) : null; 2547} 2548 2549 2550// Update replies, last_post, last_post_id and last_poster for a topic 2551function sync_topic($topic_id) 2552{ 2553 global $forum_db; 2554 2555 $return = ($hook = get_hook('fn_sync_topic_start')) ? eval($hook) : null; 2556 if ($return !== null) 2557 return; 2558 2559 // Count number of replies in the topic 2560 $query = array( 2561 'SELECT' => 'COUNT(p.id)', 2562 'FROM' => 'posts AS p', 2563 'WHERE' => 'p.topic_id='.$topic_id 2564 ); 2565 2566 ($hook = get_hook('fn_sync_topic_qr_get_topic_reply_count')) ? eval($hook) : null; 2567 $result = $forum_db->query_build($query) or error(__FILE__, __LINE__); 2568 $num_replies = $forum_db->result($result) - 1; 2569 2570 // Get last_post, last_post_id and last_poster 2571 $query = array( 2572 'SELECT' => 'p.posted, p.id, p.poster', 2573 'FROM' => 'posts AS p', 2574 'WHERE' => 'p.topic_id='.$topic_id, 2575 'ORDER BY' => 'p.id DESC', 2576 'LIMIT' => '1' 2577 ); 2578 2579 ($hook = get_hook('fn_sync_topic_qr_get_topic_last_post_data')) ? eval($hook) : null; 2580 $result = $forum_db->query_build($query) or error(__FILE__, __LINE__); 2581 $last_post_info = $forum_db->fetch_assoc($result); 2582 2583 // Now update the topic 2584 $query = array( 2585 'UPDATE' => 'topics', 2586 'SET' => 'num_replies='.$num_replies.', last_post='.$last_post_info['posted'].', last_post_id='.$last_post_info['id'].', last_poster=\''.$forum_db->escape($last_post_info['poster']).'\'', 2587 'WHERE' => 'id='.$topic_id 2588 ); 2589 2590 ($hook = get_hook('fn_sync_topic_qr_update_topic')) ? eval($hook) : null; 2591 $forum_db->query_build($query) or error(__FILE__, __LINE__); 2592 2593 ($hook = get_hook('fn_sync_topic_end')) ? eval($hook) : null; 2594} 2595 2596 2597// Iterates through all forum moderator lists and removes any erroneous entries 2598function clean_forum_moderators() 2599{ 2600 global $forum_db; 2601 2602 $return = ($hook = get_hook('fn_clean_forum_moderators_start')) ? eval($hook) : null; 2603 if ($return !== null) 2604 return; 2605 2606 // Get a list of forums and their respective lists of moderators 2607 $query = array( 2608 'SELECT' => 'f.id, f.moderators', 2609 'FROM' => 'forums AS f', 2610 'WHERE' => 'f.moderators IS NOT NULL' 2611 ); 2612 2613 ($hook = get_hook('fn_clean_forum_moderators_qr_get_forum_moderators')) ? eval($hook) : null; 2614 $result = $forum_db->query_build($query) or error(__FILE__, __LINE__); 2615 2616 $removed_moderators = array(); 2617 while ($cur_forum = $forum_db->fetch_assoc($result)) 2618 { 2619 $cur_moderators = unserialize($cur_forum['moderators']); 2620 $new_moderators = $cur_moderators; 2621 2622 // Iterate through each user in the list and check if he/she is in a moderator or admin group 2623 foreach ($cur_moderators as $username => $user_id) 2624 { 2625 if (in_array($user_id, $removed_moderators)) 2626 { 2627 unset($new_moderators[$username]); 2628 continue; 2629 } 2630 2631 $query = array( 2632 'SELECT' => 'COUNT(u.id)', 2633 'FROM' => 'users AS u', 2634 'JOINS' => array( 2635 array( 2636 'INNER JOIN' => 'groups AS g', 2637 'ON' => 'g.g_id=u.group_id' 2638 ) 2639 ), 2640 'WHERE' => '(g.g_moderator=1 OR u.group_id=1) AND u.id='.$user_id 2641 ); 2642 2643 ($hook = get_hook('fn_clean_forum_moderators_qr_check_user_in_moderator_group')) ? eval($hook) : null; 2644 $result2 = $forum_db->query_build($query) or error(__FILE__, __LINE__); 2645 if ($forum_db->result($result2) < 1) // If the user isn't in a moderator or admin group, remove him/her from the list 2646 { 2647 unset($new_moderators[$username]); 2648 $removed_moderators[] = $user_id; 2649 } 2650 } 2651 2652 // If we changed anything, update the forum 2653 if ($cur_moderators != $new_moderators) 2654 { 2655 $new_moderators = (!empty($new_moderators)) ? '\''.$forum_db->escape(serialize($new_moderators)).'\'' : 'NULL'; 2656 2657 $query = array( 2658 'UPDATE' => 'forums', 2659 'SET' => 'moderators='.$new_moderators, 2660 'WHERE' => 'id='.$cur_forum['id'] 2661 ); 2662 2663 ($hook = get_hook('fn_qr_clean_forum_moderators_set_forum_moderators')) ? eval($hook) : null; 2664 $forum_db->query_build($query) or error(__FILE__, __LINE__); 2665 } 2666 } 2667 2668 ($hook = get_hook('fn_clean_forum_moderators_end')) ? eval($hook) : null; 2669} 2670 2671 2672// Send out subscription emails 2673function send_subscriptions($post_info, $new_pid) 2674{ 2675 global $forum_config, $forum_db, $forum_url, $lang_common; 2676 2677 $return = ($hook = get_hook('fn_send_subscriptions_start')) ? eval($hook) : null; 2678 if ($return !== null) 2679 return; 2680 2681 if ($forum_config['o_subscriptions'] != '1') 2682 return; 2683 2684 // Get the post time for the previous post in this topic 2685 $query = array( 2686 'SELECT' => 'p.posted', 2687 'FROM' => 'posts AS p', 2688 'WHERE' => 'p.topic_id='.$post_info['topic_id'], 2689 'ORDER BY' => 'p.id DESC', 2690 'LIMIT' => '1, 1' 2691 ); 2692 2693 ($hook = get_hook('fn_send_subscriptions_qr_get_previous_post_time')) ? eval($hook) : null; 2694 $result = $forum_db->query_build($query) or error(__FILE__, __LINE__); 2695 $previous_post_time = $forum_db->result($result); 2696 2697 // Get any subscribed users that should be notified (banned users are excluded) 2698 $query = array( 2699 'SELECT' => 'u.id, u.email, u.notify_with_post, u.language', 2700 'FROM' => 'users AS u', 2701 'JOINS' => array( 2702 array( 2703 'INNER JOIN' => 'subscriptions AS s', 2704 'ON' => 'u.id=s.user_id' 2705 ), 2706 array( 2707 'LEFT JOIN' => 'forum_perms AS fp', 2708 'ON' => '(fp.forum_id='.$post_info['forum_id'].' AND fp.group_id=u.group_id)' 2709 ), 2710 array( 2711 'LEFT JOIN' => 'online AS o', 2712 'ON' => 'u.id=o.user_id' 2713 ), 2714 array( 2715 'LEFT JOIN' => 'bans AS b', 2716 'ON' => 'u.username=b.username' 2717 ), 2718 ), 2719 'WHERE' => 'b.username IS NULL AND COALESCE(o.logged, u.last_visit)>'.$previous_post_time.' AND (fp.read_forum IS NULL OR fp.read_forum=1) AND s.topic_id='.$post_info['topic_id'].' AND u.id!='.$post_info['poster_id'] 2720 ); 2721 2722 ($hook = get_hook('fn_send_subscriptions_qr_get_users_to_notify')) ? eval($hook) : null; 2723 $result = $forum_db->query_build($query) or error(__FILE__, __LINE__); 2724 2725 $subscribers = array(); 2726 while ($row = $forum_db->fetch_assoc($result)) 2727 { 2728 $subscribers[] = $row; 2729 } 2730 2731 if (!empty($subscribers)) 2732 { 2733 if (!defined('FORUM_EMAIL_FUNCTIONS_LOADED')) 2734 require FORUM_ROOT.'include/email.php'; 2735 2736 $notification_emails = array(); 2737 2738 // Loop through subscribed users and send e-mails 2739 foreach ($subscribers as $cur_subscriber) 2740 { 2741 // Is the subscription e-mail for $cur_subscriber['language'] cached or not? 2742 if (!isset($notification_emails[$cur_subscriber['language']]) && file_exists(FORUM_ROOT.'lang/'.$cur_subscriber['language'].'/mail_templates/new_reply.tpl')) 2743 { 2744 // Load the "new reply" template 2745 $mail_tpl = forum_trim(file_get_contents(FORUM_ROOT.'lang/'.$cur_subscriber['language'].'/mail_templates/new_reply.tpl')); 2746 2747 // Load the "new reply full" template (with post included) 2748 $mail_tpl_full = forum_trim(file_get_contents(FORUM_ROOT.'lang/'.$cur_subscriber['language'].'/mail_templates/new_reply_full.tpl')); 2749 2750 // The first row contains the subject (it also starts with "Subject:") 2751 $first_crlf = strpos($mail_tpl, "\n"); 2752 $mail_subject = forum_trim(substr($mail_tpl, 8, $first_crlf-8)); 2753 $mail_message = forum_trim(substr($mail_tpl, $first_crlf)); 2754 2755 $first_crlf = strpos($mail_tpl_full, "\n"); 2756 $mail_subject_full = forum_trim(substr($mail_tpl_full, 8, $first_crlf-8)); 2757 $mail_message_full = forum_trim(substr($mail_tpl_full, $first_crlf)); 2758 2759 $mail_subject = str_replace('<topic_subject>', '\''.$post_info['subject'].'\'', $mail_subject); 2760 $mail_message = str_replace('<topic_subject>', '\''.$post_info['subject'].'\'', $mail_message); 2761 $mail_message = str_replace('<replier>', $post_info['poster'], $mail_message); 2762 $mail_message = str_replace('<post_url>', forum_link($forum_url['post'], $new_pid), $mail_message); 2763 $mail_message = str_replace('<unsubscribe_url>', forum_link($forum_url['unsubscribe'], array($post_info['topic_id'], generate_form_token('unsubscribe'.$post_info['topic_id'].$cur_subscriber['id']))), $mail_message); 2764 $mail_message = str_replace('<board_mailer>', sprintf($lang_common['Forum mailer'], $forum_config['o_board_title']), $mail_message); 2765 2766 $mail_subject_full = str_replace('<topic_subject>', '\''.$post_info['subject'].'\'', $mail_subject_full); 2767 $mail_message_full = str_replace('<topic_subject>', '\''.$post_info['subject'].'\'', $mail_message_full); 2768 $mail_message_full = str_replace('<replier>', $post_info['poster'], $mail_message_full); 2769 $mail_message_full = str_replace('<message>', $post_info['message'], $mail_message_full); 2770 $mail_message_full = str_replace('<post_url>', forum_link($forum_url['post'], $new_pid), $mail_message_full); 2771 $mail_message_full = str_replace('<unsubscribe_url>', forum_link($forum_url['unsubscribe'], array($post_info['topic_id'], generate_form_token('unsubscribe'.$post_info['topic_id'].$cur_subscriber['id']))), $mail_message_full); 2772 $mail_message_full = str_replace('<board_mailer>', sprintf($lang_common['Forum mailer'], $forum_config['o_board_title']), $mail_message_full); 2773 2774 $notification_emails[$cur_subscriber['language']][0] = $mail_subject; 2775 $notification_emails[$cur_subscriber['language']][1] = $mail_message; 2776 $notification_emails[$cur_subscriber['language']][2] = $mail_subject_full; 2777 $notification_emails[$cur_subscriber['language']][3] = $mail_message_full; 2778 2779 $mail_subject = $mail_message = $mail_subject_full = $mail_message_full = null; 2780 } 2781 2782 // We have to double check here because the templates could be missing 2783 // Make sure the e-mail address format is valid before sending 2784 if (isset($notification_emails[$cur_subscriber['language']]) && is_valid_email($cur_subscriber['email'])) 2785 { 2786 if ($cur_subscriber['notify_with_post'] == '0') 2787 forum_mail($cur_subscriber['email'], $notification_emails[$cur_subscriber['language']][0], $notification_emails[$cur_subscriber['language']][1]); 2788 else 2789 forum_mail($cur_subscriber['email'], $notification_emails[$cur_subscriber['language']][2], $notification_emails[$cur_subscriber['language']][3]); 2790 } 2791 } 2792 } 2793 2794 ($hook = get_hook('fn_send_subscriptions_end')) ? eval($hook) : null; 2795} 2796 2797 2798// Send out subscription emails 2799function send_forum_subscriptions($topic_info, $new_tid) 2800{ 2801 global $forum_config, $forum_db, $forum_url, $lang_common; 2802 2803 $return = ($hook = get_hook('fn_send_forum_subscriptions_start')) ? eval($hook) : null; 2804 if ($return !== null) 2805 return; 2806 2807 if ($forum_config['o_subscriptions'] != '1') 2808 return; 2809 2810 // Get any subscribed users that should be notified (banned users are excluded) 2811 $query = array( 2812 'SELECT' => 'u.id, u.email, u.notify_with_post, u.language', 2813 'FROM' => 'users AS u', 2814 'JOINS' => array( 2815 array( 2816 'INNER JOIN' => 'forum_subscriptions AS fs', 2817 'ON' => 'u.id=fs.user_id' 2818 ), 2819 array( 2820 'LEFT JOIN' => 'forum_perms AS fp', 2821 'ON' => '(fp.forum_id='.$topic_info['forum_id'].' AND fp.group_id=u.group_id)' 2822 ), 2823 array( 2824 'LEFT JOIN' => 'online AS o', 2825 'ON' => 'u.id=o.user_id' 2826 ), 2827 array( 2828 'LEFT JOIN' => 'bans AS b', 2829 'ON' => 'u.username=b.username' 2830 ), 2831 ), 2832 'WHERE' => 'b.username IS NULL AND (fp.read_forum IS NULL OR fp.read_forum=1) AND fs.forum_id='.$topic_info['forum_id'].' AND u.id!='.$topic_info['poster_id'] 2833 ); 2834 2835 ($hook = get_hook('fn_send_forum_subscriptions_qr_get_users_to_notify')) ? eval($hook) : null; 2836 $result = $forum_db->query_build($query) or error(__FILE__, __LINE__); 2837 2838 $subscribers = array(); 2839 while ($row = $forum_db->fetch_assoc($result)) 2840 { 2841 $subscribers[] = $row; 2842 } 2843 2844 if (!empty($subscribers)) 2845 { 2846 if (!defined('FORUM_EMAIL_FUNCTIONS_LOADED')) 2847 require FORUM_ROOT.'include/email.php'; 2848 2849 $notification_emails = array(); 2850 2851 // Loop through subscribed users and send e-mails 2852 foreach ($subscribers as $cur_subscriber) 2853 { 2854 // Is the subscription e-mail for $cur_subscriber['language'] cached or not? 2855 if (!isset($notification_emails[$cur_subscriber['language']]) && file_exists(FORUM_ROOT.'lang/'.$cur_subscriber['language'].'/mail_templates/new_topic.tpl')) 2856 { 2857 // Load the "new topic" template 2858 $mail_tpl = forum_trim(file_get_contents(FORUM_ROOT.'lang/'.$cur_subscriber['language'].'/mail_templates/new_topic.tpl')); 2859 2860 // Load the "new topic full" template (with first post included) 2861 $mail_tpl_full = forum_trim(file_get_contents(FORUM_ROOT.'lang/'.$cur_subscriber['language'].'/mail_templates/new_topic_full.tpl')); 2862 2863 // The first row contains the subject (it also starts with "Subject:") 2864 $first_crlf = strpos($mail_tpl, "\n"); 2865 $mail_subject = forum_trim(substr($mail_tpl, 8, $first_crlf-8)); 2866 $mail_message = forum_trim(substr($mail_tpl, $first_crlf)); 2867 2868 $first_crlf = strpos($mail_tpl_full, "\n"); 2869 $mail_subject_full = forum_trim(substr($mail_tpl_full, 8, $first_crlf-8)); 2870 $mail_message_full = forum_trim(substr($mail_tpl_full, $first_crlf)); 2871 2872 $mail_subject = str_replace('<forum_name>', '\''.$topic_info['forum_name'].'\'', $mail_subject); 2873 $mail_message = str_replace('<forum_name>', '\''.$topic_info['forum_name'].'\'', $mail_message); 2874 $mail_message = str_replace('<topic_starter>', $topic_info['poster'], $mail_message); 2875 $mail_message = str_replace('<topic_subject>', '\''.$topic_info['subject'].'\'', $mail_message); 2876 $mail_message = str_replace('<topic_url>', forum_link($forum_url['topic'], array($new_tid, sef_friendly($topic_info['subject']))), $mail_message); 2877 $mail_message = str_replace('<unsubscribe_url>', forum_link($forum_url['forum_unsubscribe'], array($topic_info['forum_id'], generate_form_token('forum_unsubscribe'.$topic_info['forum_id'].$cur_subscriber['id']))), $mail_message); 2878 $mail_message = str_replace('<board_mailer>', sprintf($lang_common['Forum mailer'], $forum_config['o_board_title']), $mail_message); 2879 2880 $mail_subject_full = str_replace('<forum_name>', '\''.$topic_info['forum_name'].'\'', $mail_subject_full); 2881 $mail_message_full = str_replace('<forum_name>', '\''.$topic_info['forum_name'].'\'', $mail_message_full); 2882 $mail_message_full = str_replace('<topic_starter>', $topic_info['poster'], $mail_message_full); 2883 $mail_message_full = str_replace('<topic_subject>', '\''.$topic_info['subject'].'\'', $mail_message_full); 2884 $mail_message_full = str_replace('<message>', $topic_info['message'], $mail_message_full); 2885 $mail_message_full = str_replace('<topic_url>', forum_link($forum_url['topic'], $new_tid), $mail_message_full); 2886 $mail_message_full = str_replace('<unsubscribe_url>', forum_link($forum_url['forum_unsubscribe'], array($topic_info['forum_id'], generate_form_token('forum_unsubscribe'.$topic_info['forum_id'].$cur_subscriber['id']))), $mail_message_full); 2887 $mail_message_full = str_replace('<board_mailer>', sprintf($lang_common['Forum mailer'], $forum_config['o_board_title']), $mail_message_full); 2888 2889 $notification_emails[$cur_subscriber['language']][0] = $mail_subject; 2890 $notification_emails[$cur_subscriber['language']][1] = $mail_message; 2891 $notification_emails[$cur_subscriber['language']][2] = $mail_subject_full; 2892 $notification_emails[$cur_subscriber['language']][3] = $mail_message_full; 2893 2894 $mail_subject = $mail_message = $mail_subject_full = $mail_message_full = null; 2895 } 2896 2897 // We have to double check here because the templates could be missing 2898 // Make sure the e-mail address format is valid before sending 2899 if (isset($notification_emails[$cur_subscriber['language']]) && is_valid_email($cur_subscriber['email'])) 2900 { 2901 if ($cur_subscriber['notify_with_post'] == '0') 2902 forum_mail($cur_subscriber['email'], $notification_emails[$cur_subscriber['language']][0], $notification_emails[$cur_subscriber['language']][1]); 2903 else 2904 forum_mail($cur_subscriber['email'], $notification_emails[$cur_subscriber['language']][2], $notification_emails[$cur_subscriber['language']][3]); 2905 } 2906 } 2907 } 2908 2909 ($hook = get_hook('fn_send_forum_subscriptions_end')) ? eval($hook) : null; 2910} 2911 2912// 2913// Special pages 2914// 2915 2916// Display a form that the user can use to confirm that they want to undertake an action. 2917// Used when the CSRF token from the request does not match the token stored in the database. 2918function csrf_confirm_form() 2919{ 2920 global $forum_db, $forum_url, $lang_common, $forum_config, $base_url, $forum_start, $tpl_main, $forum_user, $forum_page, $forum_updates, $forum_flash, $forum_loader; 2921 2922 // If we've disabled the CSRF check for this page, we have nothing to do here. 2923 if (defined('FORUM_DISABLE_CSRF_CONFIRM')) 2924 return; 2925 2926 // User pressed the cancel button 2927 if (isset($_POST['confirm_cancel'])) 2928 redirect(forum_htmlencode($_POST['prev_url']), $lang_common['Cancel redirect']); 2929 2930 // A helper function for csrf_confirm_form. It takes a multi-dimensional array and returns it as a 2931 // single-dimensional array suitable for use in hidden fields. 2932 function _csrf_confirm_form($key, $values) 2933 { 2934 $fields = array(); 2935 2936 if (is_array($values)) 2937 { 2938 foreach ($values as $cur_key => $cur_values) 2939 $fields = array_merge($fields, _csrf_confirm_form($key.'['.$cur_key.']', $cur_values)); 2940 2941 return $fields; 2942 } 2943 else 2944 $fields[$key] = $values; 2945 2946 return $fields; 2947 } 2948 2949 $return = ($hook = get_hook('fn_csrf_confirm_form_start')) ? eval($hook) : null; 2950 if ($return !== null) 2951 return; 2952 2953 if (defined('FORUM_REQUEST_AJAX')) 2954 { 2955 $json_data = array( 2956 'code' => -3, 2957 'message' => $lang_common['CSRF token mismatch'], 2958 'csrf_token' => generate_form_token(get_current_url()), 2959 'prev_url' => forum_htmlencode($forum_user['prev_url']), 2960 ); 2961 2962 foreach ($_POST as $submitted_key => $submitted_val) 2963 { 2964 if ($submitted_key != 'csrf_token' && $submitted_key != 'prev_url') 2965 { 2966 $hidden_fields = _csrf_confirm_form($submitted_key, $submitted_val); 2967 foreach ($hidden_fields as $field_key => $field_val) 2968 { 2969 $json_data['post_data'][$field_key] = forum_htmlencode($field_val); 2970 } 2971 } 2972 } 2973 2974 ($hook = get_hook('fn_redirect_pre_send_json')) ? eval($hook) : null; 2975 2976 send_json($json_data); 2977 } 2978 2979 // Setup breadcrumbs 2980 $forum_page['crumbs'] = array( 2981 array($forum_config['o_board_title'], forum_link($forum_url['index'])), 2982 $lang_common['Confirm action'] 2983 ); 2984 2985 $forum_page['form_action'] = get_current_url(); 2986 2987 $forum_page['hidden_fields'] = array( 2988 'csrf_token' => '<input type="hidden" name="csrf_token" value="'.generate_form_token($forum_page['form_action']).'" />', 2989 'prev_url' => '<input type="hidden" name="prev_url" value="'.forum_htmlencode($forum_user['prev_url']).'" />' 2990 ); 2991 2992 foreach ($_POST as $submitted_key => $submitted_val) 2993 if ($submitted_key != 'csrf_token' && $submitted_key != 'prev_url') 2994 { 2995 $hidden_fields = _csrf_confirm_form($submitted_key, $submitted_val); 2996 foreach ($hidden_fields as $field_key => $field_val) 2997 $forum_page['hidden_fields'][$field_key] = '<input type="hidden" name="'.forum_htmlencode($field_key).'" value="'.forum_htmlencode($field_val).'" />'; 2998 } 2999 3000 define('FORUM_PAGE', 'dialogue'); 3001 require FORUM_ROOT.'header.php'; 3002 3003 // START SUBST - <!-- forum_main --> 3004 ob_start(); 3005 3006 ($hook = get_hook('fn_csrf_confirm_form_pre_header_load')) ? eval($hook) : null; 3007 3008?> 3009<div id="brd-main" class="main"> 3010 <div class="main-head"> 3011 <h2 class="hn"><span><?php echo $lang_common['Confirm action head'] ?></span></h2> 3012 </div> 3013 <div class="main-content main-frm"> 3014 <div class="ct-box info-box"> 3015 <p><?php echo $lang_common['CSRF token mismatch'] ?></p> 3016 </div> 3017 <form class="frm-form" method="post" accept-charset="utf-8" action="<?php echo forum_htmlencode($forum_page['form_action']) ?>"> 3018 <div class="hidden"> 3019 <?php echo implode("\n\t\t\t\t", $forum_page['hidden_fields'])."\n" ?> 3020 </div> 3021 <div class="frm-buttons"> 3022 <span class="submit primary"><input type="submit" value="<?php echo $lang_common['Confirm'] ?>" /></span> 3023 <span class="cancel"><input type="submit" name="confirm_cancel" value="<?php echo $lang_common['Cancel'] ?>" /></span> 3024 </div> 3025 </form> 3026 </div> 3027</div> 3028<?php 3029 3030 ($hook = get_hook('fn_csrf_confirm_form_end')) ? eval($hook) : null; 3031 3032 $tpl_temp = forum_trim(ob_get_contents()); 3033 $tpl_main = str_replace('<!-- forum_main -->', $tpl_temp, $tpl_main); 3034 ob_end_clean(); 3035 // END SUBST - <!-- forum_main --> 3036 3037 require FORUM_ROOT.'footer.php'; 3038} 3039 3040 3041// Display a message 3042function message($message, $link = '', $heading = '') 3043{ 3044 global $forum_db, $forum_url, $lang_common, $forum_config, $base_url, $forum_start, $tpl_main, $forum_user, $forum_page, $forum_updates, $forum_loader, $forum_flash; 3045 3046 ($hook = get_hook('fn_message_start')) ? eval($hook) : null; 3047 3048 if (defined('FORUM_REQUEST_AJAX')) 3049 { 3050 $json_data = array( 3051 'code' => -1, 3052 'message' => $message 3053 ); 3054 3055 ($hook = get_hook('fn_message_pre_send_json')) ? eval($hook) : null; 3056 3057 send_json($json_data); 3058 } 3059 3060 if (!defined('FORUM_HEADER')) 3061 { 3062 if ($heading == '') 3063 $heading = $lang_common['Forum message']; 3064 3065 // Setup breadcrumbs 3066 $forum_page['crumbs'] = array( 3067 array($forum_config['o_board_title'], forum_link($forum_url['index'])), 3068 $lang_common['Forum message'] 3069 ); 3070 3071 ($hook = get_hook('fn_message_pre_header_load')) ? eval($hook) : null; 3072 3073 define('FORUM_PAGE', 'message'); 3074 require FORUM_ROOT.'header.php'; 3075 3076 // START SUBST - <!-- forum_main --> 3077 ob_start(); 3078 3079 ($hook = get_hook('fn_message_output_start')) ? eval($hook) : null; 3080 } 3081 3082?> 3083 <div class="main-head"> 3084<?php 3085 3086 if (!empty($forum_page['main_head_options'])) 3087 echo "\n\t\t".'<p class="options">'.implode(' ', $forum_page['main_head_options']).'</p>'; 3088 3089?> 3090 <h2 class="hn"><span><?php echo $heading ?></span></h2> 3091 </div> 3092 3093 <div class="main-content main-message"> 3094 <p><?php echo $message ?><?php if ($link != '') echo ' <span>'.$link.'</span>' ?></p> 3095 </div> 3096<?php 3097 3098 ($hook = get_hook('fn_message_output_end')) ? eval($hook) : null; 3099 3100 $tpl_temp = forum_trim(ob_get_contents()); 3101 $tpl_main = str_replace('<!-- forum_main -->', "\t".$tpl_temp, $tpl_main); 3102 ob_end_clean(); 3103 // END SUBST - <!-- forum_main --> 3104 3105 require FORUM_ROOT.'footer.php'; 3106} 3107 3108 3109// Display a message when board is in maintenance mode 3110function maintenance_message() 3111{ 3112 global $forum_db, $forum_config, $lang_common, $forum_user, $base_url, $forum_loader; 3113 3114 $return = ($hook = get_hook('fn_maintenance_message_start')) ? eval($hook) : null; 3115 if ($return !== null) 3116 return $return; 3117 3118 // Deal with newlines, tabs and multiple spaces 3119 $pattern = array("\t\t", ' ', ' '); 3120 $replace = array('    ', '  ', '  '); 3121 $message = str_replace($pattern, $replace, $forum_config['o_maintenance_message']); 3122 3123 // Send the Content-type header in case the web server is setup to send something else 3124 header('Content-type: text/html; charset=utf-8'); 3125 3126 // Send a 503 HTTP response code to prevent search bots from indexing the maintenace message 3127 header('HTTP/1.1 503 Service Temporarily Unavailable'); 3128 3129 // Load the maintenance template 3130 if (file_exists(FORUM_ROOT.'style/'.$forum_user['style'].'/maintenance.tpl')) 3131 $tpl_path = FORUM_ROOT.'style/'.$forum_user['style'].'/maintenance.tpl'; 3132 else 3133 $tpl_path = FORUM_ROOT.'include/template/maintenance.tpl'; 3134 3135 ($hook = get_hook('fn_maintenance_message_pre_template_loaded')) ? eval($hook) : null; 3136 3137 $tpl_maint = forum_trim(file_get_contents($tpl_path)); 3138 3139 ($hook = get_hook('fn_maintenance_message_template_loaded')) ? eval($hook) : null; 3140 3141 // START SUBST - <!-- forum_local --> 3142 $tpl_maint = str_replace('<!-- forum_local -->', 'xml:lang="'.$lang_common['lang_identifier'].'" lang="'.$lang_common['lang_identifier'].'" dir="'.$lang_common['lang_direction'].'"', $tpl_maint); 3143 // END SUBST - <!-- forum_local --> 3144 3145 // START SUBST - <!-- forum_head --> 3146 ob_start(); 3147 3148 if (file_exists(FORUM_ROOT.'style/'.$forum_user['style'].'/'.$forum_user['style'].'.php')) 3149 require FORUM_ROOT.'style/'.$forum_user['style'].'/'.$forum_user['style'].'.php'; 3150 else 3151 $forum_loader->add_css($base_url.'/style/print.css', array('type' => 'url', 'group' => FORUM_CSS_GROUP_SYSTEM, 'media' => 'screen')); 3152 echo $forum_loader->render_css(); 3153 3154 $tpl_temp = forum_trim(ob_get_contents()); 3155 $tpl_maint = str_replace('<!-- forum_head -->', $tpl_temp, $tpl_maint); 3156 ob_end_clean(); 3157 // END SUBST - <!-- forum_head --> 3158 3159 3160 // START SUBST - <!-- forum_maint_main --> 3161 ob_start(); 3162 3163?> 3164 <div class="main-head"> 3165 <h1 class="hn"><span><?php echo $lang_common['Maintenance mode'] ?></span></h1> 3166 </div> 3167 <div class="main-content main-message"> 3168 <div class="ct-box user-box"> 3169 <?php echo $message."\n" ?> 3170 </div> 3171 </div> 3172<?php 3173 3174 $tpl_temp = "\t".forum_trim(ob_get_contents()); 3175 $tpl_maint = str_replace('<!-- forum_maint_main -->', $tpl_temp, $tpl_maint); 3176 ob_end_clean(); 3177 // END SUBST - <!-- forum_maint_main --> 3178 3179 3180 // End the transaction 3181 $forum_db->end_transaction(); 3182 3183 3184 // START SUBST - <!-- forum_include "*" --> 3185 while (preg_match('#<!-- ?forum_include "([^/\\\\]*?)" ?-->#', $tpl_maint, $cur_include)) 3186 { 3187 if (!file_exists(FORUM_ROOT.'include/user/'.$cur_include[1])) 3188 error('Unable to process user include <!-- forum_include "'.forum_htmlencode($cur_include[1]).'" --> from template maintenance.tpl.<br />There is no such file in folder /include/user/.'); 3189 3190 ob_start(); 3191 include FORUM_ROOT.'include/user/'.$cur_include[1]; 3192 $tpl_temp = ob_get_contents(); 3193 $tpl_maint = str_replace($cur_include[0], $tpl_temp, $tpl_maint); 3194 ob_end_clean(); 3195 } 3196 // END SUBST - <!-- forum_include "*" --> 3197 3198 3199 // Close the db connection (and free up any result data) 3200 $forum_db->close(); 3201 3202 exit($tpl_maint); 3203} 3204 3205 3206// Display $message and redirect user to $destination_url 3207function redirect($destination_url, $message) 3208{ 3209 global $forum_db, $forum_config, $lang_common, $forum_user, $base_url, $forum_loader; 3210 3211 define('FORUM_PAGE', 'redirect'); 3212 3213 ($hook = get_hook('fn_redirect_start')) ? eval($hook) : null; 3214 3215 // Prefix with base_url (unless it's there already) 3216 if (strpos($destination_url, 'http://') !== 0 && strpos($destination_url, 'https://') !== 0 && strpos($destination_url, '/') !== 0) 3217 $destination_url = $base_url.'/'.$destination_url; 3218 3219 // Do a little spring cleaning 3220 $destination_url = preg_replace('/([\r\n])|(%0[ad])|(;[\s]*data[\s]*:)/i', '', $destination_url); 3221 3222 if (defined('FORUM_REQUEST_AJAX')) 3223 { 3224 $json_data = array( 3225 'code' => -2, 3226 'message' => $message, 3227 'destination_url' => $destination_url 3228 ); 3229 3230 ($hook = get_hook('fn_redirect_pre_send_json')) ? eval($hook) : null; 3231 3232 send_json($json_data); 3233 } 3234 3235 // If the delay is 0 seconds, we might as well skip the redirect all together 3236 if ($forum_config['o_redirect_delay'] == '0') 3237 header('Location: '.str_replace('&', '&', $destination_url)); 3238 3239 // Send no-cache headers 3240 header('Expires: Thu, 21 Jul 1977 07:30:00 GMT'); // When yours truly first set eyes on this world! :) 3241 header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); 3242 header('Cache-Control: post-check=0, pre-check=0', false); 3243 header('Pragma: no-cache'); // For HTTP/1.0 compability 3244 3245 // Send the Content-type header in case the web server is setup to send something else 3246 header('Content-type: text/html; charset=utf-8'); 3247 3248 // Load the redirect template 3249 if (file_exists(FORUM_ROOT.'style/'.$forum_user['style'].'/redirect.tpl')) 3250 $tpl_path = FORUM_ROOT.'style/'.$forum_user['style'].'/redirect.tpl'; 3251 else 3252 $tpl_path = FORUM_ROOT.'include/template/redirect.tpl'; 3253 3254 ($hook = get_hook('fn_redirect_pre_template_loaded')) ? eval($hook) : null; 3255 3256 $tpl_redir = forum_trim(file_get_contents($tpl_path)); 3257 3258 ($hook = get_hook('fn_redirect_template_loaded')) ? eval($hook) : null; 3259 3260 // START SUBST - <!-- forum_local --> 3261 $tpl_redir = str_replace('<!-- forum_local -->', 'xml:lang="'.$lang_common['lang_identifier'].'" lang="'.$lang_common['lang_identifier'].'" dir="'.$lang_common['lang_direction'].'"', $tpl_redir); 3262 // END SUBST - <!-- forum_local --> 3263 3264 // START SUBST - <!-- forum_head --> 3265 $forum_head['refresh'] = '<meta http-equiv="refresh" content="'.$forum_config['o_redirect_delay'].';URL='.str_replace(array('<', '>', '"'), array('<', '>', '"'), $destination_url).'" />'; 3266 $forum_head['title'] = '<title>'.$lang_common['Redirecting'].$lang_common['Title separator'].forum_htmlencode($forum_config['o_board_title']).'</title>'; 3267 3268 ob_start(); 3269 3270 // Include stylesheets 3271 if (file_exists(FORUM_ROOT.'style/'.$forum_user['style'].'/'.$forum_user['style'].'.php')) 3272 require FORUM_ROOT.'style/'.$forum_user['style'].'/'.$forum_user['style'].'.php'; 3273 else 3274 $forum_loader->add_css($base_url.'/style/print.css', array('type' => 'url', 'group' => FORUM_CSS_GROUP_SYSTEM, 'media' => 'screen')); 3275 3276 $head_temp = forum_trim(ob_get_contents()); 3277 $num_temp = 0; 3278 foreach (explode("\n", $head_temp) as $style_temp) 3279 $forum_head['style'.$num_temp++] = $style_temp; 3280 3281 ob_end_clean(); 3282 3283 ($hook = get_hook('fn_redirect_head')) ? eval($hook) : null; 3284 3285 $tmp_head = implode("\n", $forum_head).$forum_loader->render_css(); 3286 3287 $tpl_redir = str_replace('<!-- forum_head -->', $tmp_head, $tpl_redir); 3288 unset($forum_head,$tmp_head); 3289 // END SUBST - <!-- forum_head --> 3290 3291 // START SUBST - <!-- forum_redir_main --> 3292 ob_start(); 3293?> 3294<div id="brd-main" class="main basic"> 3295 3296 <div class="main-head"> 3297 <h1 class="hn"><span><?php echo $message.$lang_common['Redirecting'] ?></span></h1> 3298 </div> 3299 3300 <div class="main-content main-message"> 3301 <p><?php printf($lang_common['Forwarding info'], $forum_config['o_redirect_delay'], intval($forum_config['o_redirect_delay']) == 1 ? $lang_common['second'] : $lang_common['seconds']) ?><span> <a href="<?php echo $destination_url ?>"><?php echo $lang_common['Click redirect'] ?></a></span></p> 3302 </div> 3303 3304</div> 3305<?php 3306 3307 $tpl_temp = "\t".forum_trim(ob_get_contents()); 3308 $tpl_redir = str_replace('<!-- forum_redir_main -->', $tpl_temp, $tpl_redir); 3309 ob_end_clean(); 3310 // END SUBST - <!-- forum_redir_main --> 3311 3312 3313 // START SUBST - <!-- forum_debug --> 3314 if (defined('FORUM_SHOW_QUERIES')) 3315 $tpl_redir = str_replace('<!-- forum_debug -->', get_saved_queries(), $tpl_redir); 3316 3317 // End the transaction 3318 $forum_db->end_transaction(); 3319 // END SUBST - <!-- forum_debug --> 3320 3321 3322 // START SUBST - <!-- forum_include "*" --> 3323 while (preg_match('#<!-- ?forum_include "([^/\\\\]*?)" ?-->#', $tpl_redir, $cur_include)) 3324 { 3325 if (!file_exists(FORUM_ROOT.'include/user/'.$cur_include[1])) 3326 error('Unable to process user include <!-- forum_include "'.forum_htmlencode($cur_include[1]).'" --> from template redirect.tpl.<br />There is no such file in folder /include/user/.'); 3327 3328 ob_start(); 3329 include FORUM_ROOT.'include/user/'.$cur_include[1]; 3330 $tpl_temp = ob_get_contents(); 3331 $tpl_redir = str_replace($cur_include[0], $tpl_temp, $tpl_redir); 3332 ob_end_clean(); 3333 } 3334 // END SUBST - <!-- forum_include "*" --> 3335 3336 3337 // Close the db connection (and free up any result data) 3338 $forum_db->close(); 3339 3340 exit($tpl_redir); 3341} 3342 3343 3344// Display a simple error message 3345function error() 3346{ 3347 global $forum_config, $lang_common; 3348 3349 if (!headers_sent()) 3350 { 3351 // if no HTTP responce code is set we send 503 3352 if (!defined('FORUM_HTTP_RESPONSE_CODE_SET')) 3353 header('HTTP/1.1 503 Service Temporarily Unavailable'); 3354 header('Content-type: text/html; charset=utf-8'); 3355 } 3356 3357 /* 3358 Parse input parameters. Possible function signatures: 3359 error('Error message.'); 3360 error(__FILE__, __LINE__); 3361 error('Error message.', __FILE__, __LINE__); 3362 */ 3363 $num_args = func_num_args(); 3364 if ($num_args == 3) 3365 { 3366 $message = func_get_arg(0); 3367 $file = func_get_arg(1); 3368 $line = func_get_arg(2); 3369 } 3370 else if ($num_args == 2) 3371 { 3372 $file = func_get_arg(0); 3373 $line = func_get_arg(1); 3374 } 3375 else if ($num_args == 1) 3376 $message = func_get_arg(0); 3377 3378 // Set a default title and gzip setting if the script failed before $forum_config could be populated 3379 if (empty($forum_config)) 3380 { 3381 $forum_config['o_board_title'] = 'PunBB'; 3382 $forum_config['o_gzip'] = '0'; 3383 } 3384 3385 // Set a default error messages string if the script failed before $common_lang loaded 3386 if (empty($lang_common['Forum error header'])) 3387 { 3388 $lang_common['Forum error header'] = 'Sorry! The page could not be loaded.'; 3389 } 3390 3391 if (empty($lang_common['Forum error description'])) 3392 { 3393 $lang_common['Forum error description'] = 'This is probably a temporary error. Just refresh the page and retry. If problem continues, please check back in 5-10 minutes.'; 3394 } 3395 3396 if (empty($lang_common['Forum error location'])) 3397 { 3398 $lang_common['Forum error location'] = 'The error occurred on line %1$s in %2$s'; 3399 } 3400 3401 if (empty($lang_common['Forum error db reported'])) 3402 { 3403 $lang_common['Forum error db reported'] = 'Database reported:'; 3404 } 3405 3406 if (empty($lang_common['Forum error db query'])) 3407 { 3408 $lang_common['Forum error db query'] = 'Failed query:'; 3409 } 3410 3411 3412 // Empty all output buffers and stop buffering 3413 while (@ob_end_clean()); 3414 3415 // "Restart" output buffering if we are using ob_gzhandler (since the gzip header is already sent) 3416 if (!empty($forum_config['o_gzip']) && extension_loaded('zlib') && !empty($_SERVER['HTTP_ACCEPT_ENCODING']) && (strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== false || strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'deflate') !== false)) 3417 ob_start('ob_gzhandler'); 3418 3419?> 3420<!DOCTYPE html> 3421<html lang="en" dir="ltr"> 3422<head> 3423 <meta charset="utf-8" /> 3424 <title>Error - <?php echo forum_htmlencode($forum_config['o_board_title']) ?></title> 3425 <style> 3426 strong { font-weight: bold; } 3427 body { margin: 50px; font: 85%/150% verdana, arial, sans-serif; color: #222; max-width: 55em; } 3428 h1 { color: #a00000; font-weight: normal; font-size: 1.45em; } 3429 code { font-family: monospace, sans-serif; } 3430 .error_line { color: #999; font-size: .95em; } 3431 </style> 3432</head> 3433<body> 3434 <h1><?php echo forum_htmlencode($lang_common['Forum error header']) ?></h1> 3435<?php 3436 if (isset($message)) 3437 echo '<p>'.$message.'</p>'."\n"; 3438 else 3439 echo '<p>'.forum_htmlencode($lang_common['Forum error description']).'</p>'."\n"; 3440 3441 if ($num_args > 1) 3442 { 3443 if (defined('FORUM_DEBUG')) 3444 { 3445 $db_error = isset($GLOBALS['forum_db']) ? $GLOBALS['forum_db']->error() : array(); 3446 if (!empty($db_error['error_msg'])) 3447 { 3448 echo '<p><strong>'.forum_htmlencode($lang_common['Forum error db reported']).'</strong> '.forum_htmlencode($db_error['error_msg']).(($db_error['error_no']) ? ' (Errno: '.$db_error['error_no'].')' : '').'.</p>'."\n"; 3449 3450 if ($db_error['error_sql'] != '') 3451 echo '<p><strong>'.forum_htmlencode($lang_common['Forum error db query']).'</strong> <code>'.forum_htmlencode($db_error['error_sql']).'</code></p>'."\n"; 3452 } 3453 3454 if (isset($file) && isset($line)) 3455 echo '<p class="error_line">'.forum_htmlencode(sprintf($lang_common['Forum error location'], $line, $file)).'</p>'."\n"; 3456 } 3457 } 3458?> 3459</body> 3460</html> 3461<?php 3462 // If a database connection was established (before this error) we close it 3463 if (isset($GLOBALS['forum_db'])) 3464 $GLOBALS['forum_db']->close(); 3465 3466 exit; 3467} 3468 3469function send_json($params) 3470{ 3471 header('Content-type: application/json; charset=utf-8'); 3472 if (!function_exists('json_encode')) 3473 { 3474 function json_encode($data) 3475 { 3476 switch ($type = gettype($data)) 3477 { 3478 case 'NULL': 3479 return 'null'; 3480 case 'boolean': 3481 return ($data ? 'true' : 'false'); 3482 case 'integer': 3483 case 'double': 3484 case 'float': 3485 return $data; 3486 case 'string': 3487 return '"' . addslashes($data) . '"'; 3488 case 'object': 3489 $data = get_object_vars($data); 3490 case 'array': 3491 $output_index_count = 0; 3492 $output_indexed = array(); 3493 $output_assoc = array(); 3494 foreach ($data as $key => $value) 3495 { 3496 $output_indexed[] = json_encode($value); 3497 $output_assoc[] = json_encode($key) . ':' . json_encode($value); 3498 if ($output_index_count !== NULL && $output_index_count++ !== $key) 3499 { 3500 $output_index_count = NULL; 3501 } 3502 } 3503 if ($output_index_count !== NULL) { 3504 return '[' . implode(',', $output_indexed) . ']'; 3505 } else { 3506 return '{' . implode(',', $output_assoc) . '}'; 3507 } 3508 default: 3509 return ''; // Not supported 3510 } 3511 } 3512 } 3513 echo json_encode($params); 3514 die; 3515} 3516