[ Index ] |
PHP Cross Reference of MyBB 1.8.38 |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * MyBB 1.8 4 * Copyright 2014 MyBB Group, All Rights Reserved 5 * 6 * Website: http://www.mybb.com 7 * License: http://www.mybb.com/about/license 8 * 9 */ 10 11 /** 12 * Outputs a page directly to the browser, parsing anything which needs to be parsed. 13 * 14 * @param string $contents The contents of the page. 15 */ 16 function output_page($contents) 17 { 18 global $db, $lang, $theme, $templates, $plugins, $mybb; 19 global $debug, $templatecache, $templatelist, $maintimer, $globaltime, $parsetime; 20 21 $contents = $plugins->run_hooks("pre_parse_page", $contents); 22 $contents = parse_page($contents); 23 $totaltime = format_time_duration($maintimer->stop()); 24 $contents = $plugins->run_hooks("pre_output_page", $contents); 25 26 if($mybb->usergroup['cancp'] == 1 || $mybb->dev_mode == 1) 27 { 28 if($mybb->settings['extraadmininfo'] != 0) 29 { 30 $phptime = $maintimer->totaltime - $db->query_time; 31 $query_time = $db->query_time; 32 33 if($maintimer->totaltime > 0) 34 { 35 $percentphp = number_format((($phptime/$maintimer->totaltime) * 100), 2); 36 $percentsql = number_format((($query_time/$maintimer->totaltime) * 100), 2); 37 } 38 else 39 { 40 // if we've got a super fast script... all we can do is assume something 41 $percentphp = 0; 42 $percentsql = 0; 43 } 44 45 $serverload = get_server_load(); 46 47 if(my_strpos(getenv("REQUEST_URI"), "?")) 48 { 49 $debuglink = htmlspecialchars_uni(getenv("REQUEST_URI")) . "&debug=1"; 50 } 51 else 52 { 53 $debuglink = htmlspecialchars_uni(getenv("REQUEST_URI")) . "?debug=1"; 54 } 55 56 $memory_usage = get_memory_usage(); 57 58 if($memory_usage) 59 { 60 $memory_usage = $lang->sprintf($lang->debug_memory_usage, get_friendly_size($memory_usage)); 61 } 62 else 63 { 64 $memory_usage = ''; 65 } 66 // MySQLi is still MySQL, so present it that way to the user 67 $database_server = $db->short_title; 68 69 if($database_server == 'MySQLi') 70 { 71 $database_server = 'MySQL'; 72 } 73 $generated_in = $lang->sprintf($lang->debug_generated_in, $totaltime); 74 $debug_weight = $lang->sprintf($lang->debug_weight, $percentphp, $percentsql, $database_server); 75 $sql_queries = $lang->sprintf($lang->debug_sql_queries, $db->query_count); 76 $server_load = $lang->sprintf($lang->debug_server_load, $serverload); 77 78 eval("\$debugstuff = \"".$templates->get("debug_summary")."\";"); 79 $contents = str_replace("<debugstuff>", $debugstuff, $contents); 80 } 81 82 if($mybb->debug_mode == true) 83 { 84 debug_page(); 85 } 86 } 87 88 $contents = str_replace("<debugstuff>", "", $contents); 89 90 if($mybb->settings['gzipoutput'] == 1) 91 { 92 $contents = gzip_encode($contents, $mybb->settings['gziplevel']); 93 } 94 95 @header("Content-type: text/html; charset={$lang->settings['charset']}"); 96 97 echo $contents; 98 99 $plugins->run_hooks("post_output_page"); 100 } 101 102 /** 103 * Adds a function or class to the list of code to run on shutdown. 104 * 105 * @param string|array $name The name of the function. 106 * @param mixed $arguments Either an array of arguments for the function or one argument 107 * @return boolean True if function exists, otherwise false. 108 */ 109 function add_shutdown($name, $arguments=array()) 110 { 111 global $shutdown_functions; 112 113 if(!is_array($shutdown_functions)) 114 { 115 $shutdown_functions = array(); 116 } 117 118 if(!is_array($arguments)) 119 { 120 $arguments = array($arguments); 121 } 122 123 if(is_array($name) && method_exists($name[0], $name[1])) 124 { 125 $shutdown_functions[] = array('function' => $name, 'arguments' => $arguments); 126 return true; 127 } 128 else if(!is_array($name) && function_exists($name)) 129 { 130 $shutdown_functions[] = array('function' => $name, 'arguments' => $arguments); 131 return true; 132 } 133 134 return false; 135 } 136 137 /** 138 * Runs the shutdown items after the page has been sent to the browser. 139 * 140 */ 141 function run_shutdown() 142 { 143 global $config, $db, $cache, $plugins, $error_handler, $shutdown_functions, $shutdown_queries, $done_shutdown, $mybb; 144 145 if($done_shutdown == true || !$config || (isset($error_handler) && $error_handler->has_errors)) 146 { 147 return; 148 } 149 150 if(empty($shutdown_queries) && empty($shutdown_functions)) 151 { 152 // Nothing to do 153 return; 154 } 155 156 // Missing the core? Build 157 if(!is_object($mybb)) 158 { 159 require_once MYBB_ROOT."inc/class_core.php"; 160 $mybb = new MyBB; 161 162 // Load the settings 163 require MYBB_ROOT."inc/settings.php"; 164 $mybb->settings = &$settings; 165 } 166 167 // If our DB has been deconstructed already (bad PHP 5.2.0), reconstruct 168 if(!is_object($db)) 169 { 170 if(!isset($config) || empty($config['database']['type'])) 171 { 172 require MYBB_ROOT."inc/config.php"; 173 } 174 175 if(isset($config)) 176 { 177 // Load DB interface 178 require_once MYBB_ROOT."inc/db_base.php"; 179 require_once MYBB_ROOT . 'inc/AbstractPdoDbDriver.php'; 180 181 require_once MYBB_ROOT."inc/db_".$config['database']['type'].".php"; 182 switch($config['database']['type']) 183 { 184 case "sqlite": 185 $db = new DB_SQLite; 186 break; 187 case "pgsql": 188 $db = new DB_PgSQL; 189 break; 190 case "pgsql_pdo": 191 $db = new PostgresPdoDbDriver(); 192 break; 193 case "mysqli": 194 $db = new DB_MySQLi; 195 break; 196 case "mysql_pdo": 197 $db = new MysqlPdoDbDriver(); 198 break; 199 default: 200 $db = new DB_MySQL; 201 } 202 203 $db->connect($config['database']); 204 if(!defined("TABLE_PREFIX")) 205 { 206 define("TABLE_PREFIX", $config['database']['table_prefix']); 207 } 208 $db->set_table_prefix(TABLE_PREFIX); 209 } 210 } 211 212 // Cache object deconstructed? reconstruct 213 if(!is_object($cache)) 214 { 215 require_once MYBB_ROOT."inc/class_datacache.php"; 216 $cache = new datacache; 217 $cache->cache(); 218 } 219 220 // And finally.. plugins 221 if(!is_object($plugins) && !defined("NO_PLUGINS") && !($mybb->settings['no_plugins'] == 1)) 222 { 223 require_once MYBB_ROOT."inc/class_plugins.php"; 224 $plugins = new pluginSystem; 225 $plugins->load(); 226 } 227 228 // We have some shutdown queries needing to be run 229 if(is_array($shutdown_queries)) 230 { 231 // Loop through and run them all 232 foreach($shutdown_queries as $query) 233 { 234 $db->write_query($query); 235 } 236 } 237 238 // Run any shutdown functions if we have them 239 if(is_array($shutdown_functions)) 240 { 241 foreach($shutdown_functions as $function) 242 { 243 call_user_func_array($function['function'], $function['arguments']); 244 } 245 } 246 247 $done_shutdown = true; 248 } 249 250 /** 251 * Sends a specified amount of messages from the mail queue 252 * 253 * @param int $count The number of messages to send (Defaults to 10) 254 */ 255 function send_mail_queue($count=10) 256 { 257 global $db, $cache, $plugins; 258 259 $plugins->run_hooks("send_mail_queue_start"); 260 261 // Check to see if the mail queue has messages needing to be sent 262 $mailcache = $cache->read("mailqueue"); 263 if($mailcache !== false && $mailcache['queue_size'] > 0 && ($mailcache['locked'] == 0 || $mailcache['locked'] < TIME_NOW-300)) 264 { 265 // Lock the queue so no other messages can be sent whilst these are (for popular boards) 266 $cache->update_mailqueue(0, TIME_NOW); 267 268 // Fetch emails for this page view - and send them 269 $query = $db->simple_select("mailqueue", "*", "", array("order_by" => "mid", "order_dir" => "asc", "limit_start" => 0, "limit" => $count)); 270 271 while($email = $db->fetch_array($query)) 272 { 273 // Delete the message from the queue 274 $db->delete_query("mailqueue", "mid='{$email['mid']}'"); 275 276 if($db->affected_rows() == 1) 277 { 278 my_mail($email['mailto'], $email['subject'], $email['message'], $email['mailfrom'], "", $email['headers'], true); 279 } 280 } 281 // Update the mailqueue cache and remove the lock 282 $cache->update_mailqueue(TIME_NOW, 0); 283 } 284 285 $plugins->run_hooks("send_mail_queue_end"); 286 } 287 288 /** 289 * Parses the contents of a page before outputting it. 290 * 291 * @param string $contents The contents of the page. 292 * @return string The parsed page. 293 */ 294 function parse_page($contents) 295 { 296 global $lang, $theme, $mybb, $htmldoctype, $archive_url, $error_handler; 297 298 $contents = str_replace('<navigation>', build_breadcrumb(), $contents); 299 $contents = str_replace('<archive_url>', $archive_url, $contents); 300 301 if($htmldoctype) 302 { 303 $contents = $htmldoctype.$contents; 304 } 305 else 306 { 307 $contents = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n".$contents; 308 } 309 310 $contents = str_replace("<html", "<html xmlns=\"http://www.w3.org/1999/xhtml\"", $contents); 311 312 if($lang->settings['rtl'] == 1) 313 { 314 $contents = str_replace("<html", "<html dir=\"rtl\"", $contents); 315 } 316 317 if($lang->settings['htmllang']) 318 { 319 $contents = str_replace("<html", "<html xml:lang=\"".$lang->settings['htmllang']."\" lang=\"".$lang->settings['htmllang']."\"", $contents); 320 } 321 322 if($error_handler->warnings) 323 { 324 $contents = str_replace("<body>", "<body>\n".$error_handler->show_warnings(), $contents); 325 } 326 327 return $contents; 328 } 329 330 /** 331 * Turn a unix timestamp in to a "friendly" date/time format for the user. 332 * 333 * @param string $format A date format (either relative, normal or PHP's date() structure). 334 * @param int $stamp The unix timestamp the date should be generated for. 335 * @param int|string $offset The offset in hours that should be applied to times. (timezones) Or an empty string to determine that automatically 336 * @param int $ty Whether or not to use today/yesterday formatting. 337 * @param boolean $adodb Whether or not to use the adodb time class for < 1970 or > 2038 times 338 * @return string The formatted timestamp. 339 */ 340 function my_date($format, $stamp=0, $offset="", $ty=1, $adodb=false) 341 { 342 global $mybb, $lang, $plugins; 343 344 // If the stamp isn't set, use TIME_NOW 345 if(empty($stamp)) 346 { 347 $stamp = TIME_NOW; 348 } 349 350 if(!$offset && $offset != '0') 351 { 352 if(isset($mybb->user['uid']) && $mybb->user['uid'] != 0 && array_key_exists("timezone", $mybb->user)) 353 { 354 $offset = (float)$mybb->user['timezone']; 355 $dstcorrection = $mybb->user['dst']; 356 } 357 else 358 { 359 $offset = (float)$mybb->settings['timezoneoffset']; 360 $dstcorrection = $mybb->settings['dstcorrection']; 361 } 362 363 // If DST correction is enabled, add an additional hour to the timezone. 364 if($dstcorrection == 1) 365 { 366 ++$offset; 367 if(my_substr($offset, 0, 1) != "-") 368 { 369 $offset = "+".$offset; 370 } 371 } 372 } 373 374 if($offset == "-") 375 { 376 $offset = 0; 377 } 378 379 // Using ADOdb? 380 if($adodb == true && !function_exists('adodb_date')) 381 { 382 $adodb = false; 383 } 384 385 $todaysdate = $yesterdaysdate = ''; 386 if($ty && ($format == $mybb->settings['dateformat'] || $format == 'relative' || $format == 'normal')) 387 { 388 $_stamp = TIME_NOW; 389 if($adodb == true) 390 { 391 $date = adodb_date($mybb->settings['dateformat'], $stamp + ($offset * 3600)); 392 $todaysdate = adodb_date($mybb->settings['dateformat'], $_stamp + ($offset * 3600)); 393 $yesterdaysdate = adodb_date($mybb->settings['dateformat'], ($_stamp - 86400) + ($offset * 3600)); 394 } 395 else 396 { 397 $date = gmdate($mybb->settings['dateformat'], $stamp + ($offset * 3600)); 398 $todaysdate = gmdate($mybb->settings['dateformat'], $_stamp + ($offset * 3600)); 399 $yesterdaysdate = gmdate($mybb->settings['dateformat'], ($_stamp - 86400) + ($offset * 3600)); 400 } 401 } 402 403 if($format == 'relative') 404 { 405 // Relative formats both date and time 406 $real_date = $real_time = ''; 407 if($adodb == true) 408 { 409 $real_date = adodb_date($mybb->settings['dateformat'], $stamp + ($offset * 3600)); 410 $real_time = $mybb->settings['datetimesep']; 411 $real_time .= adodb_date($mybb->settings['timeformat'], $stamp + ($offset * 3600)); 412 } 413 else 414 { 415 $real_date = gmdate($mybb->settings['dateformat'], $stamp + ($offset * 3600)); 416 $real_time = $mybb->settings['datetimesep']; 417 $real_time .= gmdate($mybb->settings['timeformat'], $stamp + ($offset * 3600)); 418 } 419 420 if($ty != 2 && abs(TIME_NOW - $stamp) < 3600) 421 { 422 $diff = TIME_NOW - $stamp; 423 $relative = array('prefix' => '', 'minute' => 0, 'plural' => $lang->rel_minutes_plural, 'suffix' => $lang->rel_ago); 424 425 if($diff < 0) 426 { 427 $diff = abs($diff); 428 $relative['suffix'] = ''; 429 $relative['prefix'] = $lang->rel_in; 430 } 431 432 $relative['minute'] = floor($diff / 60); 433 434 if($relative['minute'] <= 1) 435 { 436 $relative['minute'] = 1; 437 $relative['plural'] = $lang->rel_minutes_single; 438 } 439 440 if($diff <= 60) 441 { 442 // Less than a minute 443 $relative['prefix'] = $lang->rel_less_than; 444 } 445 446 $date = $lang->sprintf($lang->rel_time, $relative['prefix'], $relative['minute'], $relative['plural'], $relative['suffix'], $real_date, $real_time); 447 } 448 elseif($ty != 2 && abs(TIME_NOW - $stamp) < 43200) 449 { 450 $diff = TIME_NOW - $stamp; 451 $relative = array('prefix' => '', 'hour' => 0, 'plural' => $lang->rel_hours_plural, 'suffix' => $lang->rel_ago); 452 453 if($diff < 0) 454 { 455 $diff = abs($diff); 456 $relative['suffix'] = ''; 457 $relative['prefix'] = $lang->rel_in; 458 } 459 460 $relative['hour'] = floor($diff / 3600); 461 462 if($relative['hour'] <= 1) 463 { 464 $relative['hour'] = 1; 465 $relative['plural'] = $lang->rel_hours_single; 466 } 467 468 $date = $lang->sprintf($lang->rel_time, $relative['prefix'], $relative['hour'], $relative['plural'], $relative['suffix'], $real_date, $real_time); 469 } 470 else 471 { 472 if($ty) 473 { 474 if($todaysdate == $date) 475 { 476 $date = $lang->sprintf($lang->today_rel, $real_date); 477 } 478 else if($yesterdaysdate == $date) 479 { 480 $date = $lang->sprintf($lang->yesterday_rel, $real_date); 481 } 482 } 483 484 $date .= $mybb->settings['datetimesep']; 485 if($adodb == true) 486 { 487 $date .= adodb_date($mybb->settings['timeformat'], $stamp + ($offset * 3600)); 488 } 489 else 490 { 491 $date .= gmdate($mybb->settings['timeformat'], $stamp + ($offset * 3600)); 492 } 493 } 494 } 495 elseif($format == 'normal') 496 { 497 // Normal format both date and time 498 if($ty != 2) 499 { 500 if($todaysdate == $date) 501 { 502 $date = $lang->today; 503 } 504 else if($yesterdaysdate == $date) 505 { 506 $date = $lang->yesterday; 507 } 508 } 509 510 $date .= $mybb->settings['datetimesep']; 511 if($adodb == true) 512 { 513 $date .= adodb_date($mybb->settings['timeformat'], $stamp + ($offset * 3600)); 514 } 515 else 516 { 517 $date .= gmdate($mybb->settings['timeformat'], $stamp + ($offset * 3600)); 518 } 519 } 520 else 521 { 522 if($ty && $format == $mybb->settings['dateformat']) 523 { 524 if($todaysdate == $date) 525 { 526 $date = $lang->today; 527 } 528 else if($yesterdaysdate == $date) 529 { 530 $date = $lang->yesterday; 531 } 532 } 533 else 534 { 535 if($adodb == true) 536 { 537 $date = adodb_date($format, $stamp + ($offset * 3600)); 538 } 539 else 540 { 541 $date = gmdate($format, $stamp + ($offset * 3600)); 542 } 543 } 544 } 545 546 if(is_object($plugins)) 547 { 548 $date = $plugins->run_hooks("my_date", $date); 549 } 550 551 return $date; 552 } 553 554 /** 555 * Get a mail handler instance, a MyBB's built-in SMTP / PHP mail hander or one created by a plugin. 556 * @param bool $use_buitlin Whether to use MyBB's built-in mail handler. 557 * 558 * @return object A MyBB built-in mail handler or one created by plugin(s). 559 */ 560 function &get_my_mailhandler($use_buitlin = false) 561 { 562 global $mybb, $plugins; 563 static $my_mailhandler; 564 static $my_mailhandler_builtin; 565 566 if($use_buitlin) 567 { 568 // If our built-in mail handler doesn't exist, create it. 569 if(!is_object($my_mailhandler_builtin)) 570 { 571 require_once MYBB_ROOT . "inc/class_mailhandler.php"; 572 573 // Using SMTP. 574 if(isset($mybb->settings['mail_handler']) && $mybb->settings['mail_handler'] == 'smtp') 575 { 576 require_once MYBB_ROOT . "inc/mailhandlers/smtp.php"; 577 $my_mailhandler_builtin = new SmtpMail(); 578 } 579 // Using PHP mail(). 580 else 581 { 582 require_once MYBB_ROOT . "inc/mailhandlers/php.php"; 583 $my_mailhandler_builtin = new PhpMail(); 584 if(!empty($mybb->config['mail_parameters'])) 585 { 586 $my_mailhandler_builtin->additional_parameters = $mybb->config['mail_parameters']; 587 } 588 } 589 } 590 591 if(isset($plugins) && is_object($plugins)) 592 { 593 $plugins->run_hooks('my_mailhandler_builtin_after_init', $my_mailhandler_builtin); 594 } 595 596 return $my_mailhandler_builtin; 597 } 598 599 // If our mail handler doesn't exist, create it. 600 if(!is_object($my_mailhandler)) 601 { 602 require_once MYBB_ROOT . "inc/class_mailhandler.php"; 603 604 if(isset($plugins) && is_object($plugins)) 605 { 606 $plugins->run_hooks('my_mailhandler_init', $my_mailhandler); 607 } 608 609 // If no plugin has ever created the mail handler, resort to use the built-in one. 610 if(!is_object($my_mailhandler) || !($my_mailhandler instanceof MailHandler)) 611 { 612 $my_mailhandler = &get_my_mailhandler(true); 613 } 614 } 615 616 return $my_mailhandler; 617 } 618 619 /** 620 * Sends an email using PHP's mail function, formatting it appropriately. 621 * 622 * @param string $to Address the email should be addressed to. 623 * @param string $subject The subject of the email being sent. 624 * @param string $message The message being sent. 625 * @param string $from The from address of the email, if blank, the board name will be used. 626 * @param string $charset The chracter set being used to send this email. 627 * @param string $headers 628 * @param boolean $keep_alive Do we wish to keep the connection to the mail server alive to send more than one message (SMTP only) 629 * @param string $format The format of the email to be sent (text or html). text is default 630 * @param string $message_text The text message of the email if being sent in html format, for email clients that don't support html 631 * @param string $return_email The email address to return to. Defaults to admin return email address. 632 * @return bool True if the mail is sent, false otherwise. 633 */ 634 function my_mail($to, $subject, $message, $from="", $charset="", $headers="", $keep_alive=false, $format="text", $message_text="", $return_email="") 635 { 636 global $mybb, $plugins; 637 638 // Get our mail handler. 639 $mail = &get_my_mailhandler(); 640 641 // If MyBB's built-in SMTP mail handler is used, set the keep alive bit accordingly. 642 if($keep_alive == true && isset($mail->keep_alive) && isset($mybb->settings['mail_handler']) && $mybb->settings['mail_handler'] == 'smtp') 643 { 644 require_once MYBB_ROOT . "inc/class_mailhandler.php"; 645 require_once MYBB_ROOT . "inc/mailhandlers/smtp.php"; 646 if($mail instanceof MailHandler && $mail instanceof SmtpMail) 647 { 648 $mail->keep_alive = true; 649 } 650 } 651 652 // Following variables will help sequential plugins to determine how to process plugin hooks. 653 // Mark this variable true if the hooked plugin has sent the mail, otherwise don't modify it. 654 $is_mail_sent = false; 655 // Mark this variable false if the hooked plugin doesn't suggest sequential plugins to continue processing. 656 $continue_process = true; 657 658 $my_mail_parameters = array( 659 'to' => &$to, 660 'subject' => &$subject, 661 'message' => &$message, 662 'from' => &$from, 663 'charset' => &$charset, 664 'headers' => &$headers, 665 'keep_alive' => &$keep_alive, 666 'format' => &$format, 667 'message_text' => &$message_text, 668 'return_email' => &$return_email, 669 'is_mail_sent' => &$is_mail_sent, 670 'continue_process' => &$continue_process, 671 ); 672 673 if(isset($plugins) && is_object($plugins)) 674 { 675 $plugins->run_hooks('my_mail_pre_build_message', $my_mail_parameters); 676 } 677 678 // Build the mail message. 679 $mail->build_message($to, $subject, $message, $from, $charset, $headers, $format, $message_text, $return_email); 680 681 if(isset($plugins) && is_object($plugins)) 682 { 683 $plugins->run_hooks('my_mail_pre_send', $my_mail_parameters); 684 } 685 686 // Check if the hooked plugins still suggest to send the mail. 687 if($continue_process) 688 { 689 $is_mail_sent = $mail->send(); 690 } 691 692 if(isset($plugins) && is_object($plugins)) 693 { 694 $plugins->run_hooks('my_mail_post_send', $my_mail_parameters); 695 } 696 697 return $is_mail_sent; 698 } 699 700 /** 701 * Generates a code for POST requests to prevent XSS/CSRF attacks. 702 * Unique for each user or guest session and rotated every 6 hours. 703 * 704 * @param int $rotation_shift Adjustment of the rotation number to generate a past/future code 705 * @return string The generated code 706 */ 707 function generate_post_check($rotation_shift=0) 708 { 709 global $mybb, $session; 710 711 $rotation_interval = 6 * 3600; 712 $rotation = floor(TIME_NOW / $rotation_interval) + $rotation_shift; 713 714 $seed = $rotation; 715 716 if($mybb->user['uid']) 717 { 718 $seed .= $mybb->user['loginkey'].$mybb->user['salt'].$mybb->user['regdate']; 719 } 720 else 721 { 722 $seed .= $session->sid; 723 } 724 725 if(defined('IN_ADMINCP')) 726 { 727 $seed .= 'ADMINCP'; 728 } 729 730 $seed .= $mybb->settings['internal']['encryption_key']; 731 732 return md5($seed); 733 } 734 735 /** 736 * Verifies a POST check code is valid (i.e. generated using a rotation number from the past 24 hours) 737 * 738 * @param string $code The incoming POST check code 739 * @param boolean $silent Don't show an error to the user 740 * @return bool|void Result boolean if $silent is true, otherwise shows an error to the user 741 */ 742 function verify_post_check($code, $silent=false) 743 { 744 global $lang; 745 if( 746 generate_post_check() !== $code && 747 generate_post_check(-1) !== $code && 748 generate_post_check(-2) !== $code && 749 generate_post_check(-3) !== $code 750 ) 751 { 752 if($silent == true) 753 { 754 return false; 755 } 756 else 757 { 758 if(defined("IN_ADMINCP")) 759 { 760 return false; 761 } 762 else 763 { 764 error($lang->invalid_post_code); 765 } 766 } 767 } 768 else 769 { 770 return true; 771 } 772 } 773 774 /** 775 * Return a parent list for the specified forum. 776 * 777 * @param int $fid The forum id to get the parent list for. 778 * @return string The comma-separated parent list. 779 */ 780 function get_parent_list($fid) 781 { 782 global $forum_cache; 783 static $forumarraycache; 784 785 if(!empty($forumarraycache[$fid])) 786 { 787 return $forumarraycache[$fid]['parentlist']; 788 } 789 elseif(!empty($forum_cache[$fid])) 790 { 791 return $forum_cache[$fid]['parentlist']; 792 } 793 else 794 { 795 cache_forums(); 796 return $forum_cache[$fid]['parentlist']; 797 } 798 } 799 800 /** 801 * Build a parent list of a specific forum, suitable for querying 802 * 803 * @param int $fid The forum ID 804 * @param string $column The column name to add to the query 805 * @param string $joiner The joiner for each forum for querying (OR | AND | etc) 806 * @param string $parentlist The parent list of the forum - if you have it 807 * @return string The query string generated 808 */ 809 function build_parent_list($fid, $column="fid", $joiner="OR", $parentlist="") 810 { 811 if(!$parentlist) 812 { 813 $parentlist = get_parent_list($fid); 814 } 815 816 $parentsexploded = explode(",", $parentlist); 817 $builtlist = "("; 818 $sep = ''; 819 820 foreach($parentsexploded as $key => $val) 821 { 822 $builtlist .= "$sep$column='$val'"; 823 $sep = " $joiner "; 824 } 825 826 $builtlist .= ")"; 827 828 return $builtlist; 829 } 830 831 /** 832 * Load the forum cache in to memory 833 * 834 * @param boolean $force True to force a reload of the cache 835 * @return array The forum cache 836 */ 837 function cache_forums($force=false) 838 { 839 global $forum_cache, $cache; 840 841 if($force == true) 842 { 843 $forum_cache = $cache->read("forums", 1); 844 return $forum_cache; 845 } 846 847 if(!$forum_cache) 848 { 849 $forum_cache = $cache->read("forums"); 850 if(!$forum_cache) 851 { 852 $cache->update_forums(); 853 $forum_cache = $cache->read("forums", 1); 854 } 855 } 856 return $forum_cache; 857 } 858 859 /** 860 * Generate an array of all child and descendant forums for a specific forum. 861 * 862 * @param int $fid The forum ID 863 * @return Array of descendants 864 */ 865 function get_child_list($fid) 866 { 867 static $forums_by_parent; 868 869 $forums = array(); 870 if(!is_array($forums_by_parent)) 871 { 872 $forum_cache = cache_forums(); 873 foreach($forum_cache as $forum) 874 { 875 if($forum['active'] != 0) 876 { 877 $forums_by_parent[$forum['pid']][$forum['fid']] = $forum; 878 } 879 } 880 } 881 if(isset($forums_by_parent[$fid])) 882 { 883 if(!is_array($forums_by_parent[$fid])) 884 { 885 return $forums; 886 } 887 888 foreach($forums_by_parent[$fid] as $forum) 889 { 890 $forums[] = (int)$forum['fid']; 891 $children = get_child_list($forum['fid']); 892 if(is_array($children)) 893 { 894 $forums = array_merge($forums, $children); 895 } 896 } 897 } 898 return $forums; 899 } 900 901 /** 902 * Produce a friendly error message page 903 * 904 * @param string $error The error message to be shown 905 * @param string $title The title of the message shown in the title of the page and the error table 906 */ 907 function error($error="", $title="") 908 { 909 global $header, $footer, $theme, $headerinclude, $db, $templates, $lang, $mybb, $plugins; 910 911 $error = $plugins->run_hooks("error", $error); 912 if(!$error) 913 { 914 $error = $lang->unknown_error; 915 } 916 917 // AJAX error message? 918 if($mybb->get_input('ajax', MyBB::INPUT_INT)) 919 { 920 // Send our headers. 921 @header("Content-type: application/json; charset={$lang->settings['charset']}"); 922 echo json_encode(array("errors" => array($error))); 923 exit; 924 } 925 926 if(!$title) 927 { 928 $title = $mybb->settings['bbname']; 929 } 930 931 $timenow = my_date('relative', TIME_NOW); 932 reset_breadcrumb(); 933 add_breadcrumb($lang->error); 934 935 eval("\$errorpage = \"".$templates->get("error")."\";"); 936 output_page($errorpage); 937 938 exit; 939 } 940 941 /** 942 * Produce an error message for displaying inline on a page 943 * 944 * @param array $errors Array of errors to be shown 945 * @param string $title The title of the error message 946 * @param array $json_data JSON data to be encoded (we may want to send more data; e.g. newreply.php uses this for CAPTCHA) 947 * @return string The inline error HTML 948 */ 949 function inline_error($errors, $title="", $json_data=array()) 950 { 951 global $theme, $mybb, $db, $lang, $templates; 952 953 if(!$title) 954 { 955 $title = $lang->please_correct_errors; 956 } 957 958 if(!is_array($errors)) 959 { 960 $errors = array($errors); 961 } 962 963 // AJAX error message? 964 if($mybb->get_input('ajax', MyBB::INPUT_INT)) 965 { 966 // Send our headers. 967 @header("Content-type: application/json; charset={$lang->settings['charset']}"); 968 969 if(empty($json_data)) 970 { 971 echo json_encode(array("errors" => $errors)); 972 } 973 else 974 { 975 echo json_encode(array_merge(array("errors" => $errors), $json_data)); 976 } 977 exit; 978 } 979 980 $errorlist = ''; 981 982 foreach($errors as $error) 983 { 984 eval("\$errorlist .= \"".$templates->get("error_inline_item")."\";"); 985 } 986 987 eval("\$errors = \"".$templates->get("error_inline")."\";"); 988 989 return $errors; 990 } 991 992 /** 993 * Presents the user with a "no permission" page 994 */ 995 function error_no_permission() 996 { 997 global $mybb, $theme, $templates, $db, $lang, $plugins, $session; 998 999 $time = TIME_NOW; 1000 $plugins->run_hooks("no_permission"); 1001 1002 $noperm_array = array ( 1003 "nopermission" => '1', 1004 "location1" => 0, 1005 "location2" => 0 1006 ); 1007 1008 $db->update_query("sessions", $noperm_array, "sid='{$session->sid}'"); 1009 1010 if($mybb->get_input('ajax', MyBB::INPUT_INT)) 1011 { 1012 // Send our headers. 1013 header("Content-type: application/json; charset={$lang->settings['charset']}"); 1014 echo json_encode(array("errors" => array($lang->error_nopermission_user_ajax))); 1015 exit; 1016 } 1017 1018 if($mybb->user['uid']) 1019 { 1020 $lang->error_nopermission_user_username = $lang->sprintf($lang->error_nopermission_user_username, htmlspecialchars_uni($mybb->user['username'])); 1021 eval("\$errorpage = \"".$templates->get("error_nopermission_loggedin")."\";"); 1022 } 1023 else 1024 { 1025 // Redirect to where the user came from 1026 $redirect_url = $_SERVER['PHP_SELF']; 1027 if($_SERVER['QUERY_STRING']) 1028 { 1029 $redirect_url .= '?'.$_SERVER['QUERY_STRING']; 1030 } 1031 1032 $redirect_url = htmlspecialchars_uni($redirect_url); 1033 1034 switch($mybb->settings['username_method']) 1035 { 1036 case 0: 1037 $lang_username = $lang->username; 1038 break; 1039 case 1: 1040 $lang_username = $lang->username1; 1041 break; 1042 case 2: 1043 $lang_username = $lang->username2; 1044 break; 1045 default: 1046 $lang_username = $lang->username; 1047 break; 1048 } 1049 eval("\$errorpage = \"".$templates->get("error_nopermission")."\";"); 1050 } 1051 1052 error($errorpage); 1053 } 1054 1055 /** 1056 * Redirect the user to a given URL with a given message 1057 * 1058 * @param string $url The URL to redirect the user to 1059 * @param string $message The redirection message to be shown 1060 * @param string $title The title of the redirection page 1061 * @param boolean $force_redirect Force the redirect page regardless of settings 1062 */ 1063 function redirect($url, $message="", $title="", $force_redirect=false) 1064 { 1065 global $header, $footer, $mybb, $theme, $headerinclude, $templates, $lang, $plugins; 1066 1067 $redirect_args = array('url' => &$url, 'message' => &$message, 'title' => &$title); 1068 1069 $plugins->run_hooks("redirect", $redirect_args); 1070 1071 if($mybb->get_input('ajax', MyBB::INPUT_INT)) 1072 { 1073 // Send our headers. 1074 //@header("Content-type: text/html; charset={$lang->settings['charset']}"); 1075 $data = "<script type=\"text/javascript\">\n"; 1076 if($message != "") 1077 { 1078 $data .= 'alert("'.addslashes($message).'");'; 1079 } 1080 $url = str_replace("#", "&#", $url); 1081 $url = htmlspecialchars_decode($url); 1082 $url = str_replace(array("\n","\r",";"), "", $url); 1083 $data .= 'window.location = "'.addslashes($url).'";'."\n"; 1084 $data .= "</script>\n"; 1085 //exit; 1086 1087 @header("Content-type: application/json; charset={$lang->settings['charset']}"); 1088 echo json_encode(array("data" => $data)); 1089 exit; 1090 } 1091 1092 if(!$message) 1093 { 1094 $message = $lang->redirect; 1095 } 1096 1097 $time = TIME_NOW; 1098 $timenow = my_date('relative', $time); 1099 1100 if(!$title) 1101 { 1102 $title = $mybb->settings['bbname']; 1103 } 1104 1105 // Show redirects only if both ACP and UCP settings are enabled, or ACP is enabled, and user is a guest, or they are forced. 1106 if($force_redirect == true || ($mybb->settings['redirects'] == 1 && (!$mybb->user['uid'] || $mybb->user['showredirect'] == 1))) 1107 { 1108 $url = str_replace("&", "&", $url); 1109 $url = htmlspecialchars_uni($url); 1110 1111 eval("\$redirectpage = \"".$templates->get("redirect")."\";"); 1112 output_page($redirectpage); 1113 } 1114 else 1115 { 1116 $url = htmlspecialchars_decode($url); 1117 $url = str_replace(array("\n","\r",";"), "", $url); 1118 1119 run_shutdown(); 1120 1121 if(!my_validate_url($url, true, true)) 1122 { 1123 header("Location: {$mybb->settings['bburl']}/{$url}"); 1124 } 1125 else 1126 { 1127 header("Location: {$url}"); 1128 } 1129 } 1130 1131 exit; 1132 } 1133 1134 /** 1135 * Generate a listing of page - pagination 1136 * 1137 * @param int $count The number of items 1138 * @param int $perpage The number of items to be shown per page 1139 * @param int $page The current page number 1140 * @param string $url The URL to have page numbers tacked on to (If {page} is specified, the value will be replaced with the page #) 1141 * @param boolean $breadcrumb Whether or not the multipage is being shown in the navigation breadcrumb 1142 * @return string The generated pagination 1143 */ 1144 function multipage($count, $perpage, $page, $url, $breadcrumb=false) 1145 { 1146 global $theme, $templates, $lang, $mybb, $plugins; 1147 1148 if($count <= $perpage) 1149 { 1150 return ''; 1151 } 1152 1153 $args = array( 1154 'count' => &$count, 1155 'perpage' => &$perpage, 1156 'page' => &$page, 1157 'url' => &$url, 1158 'breadcrumb' => &$breadcrumb, 1159 ); 1160 $plugins->run_hooks('multipage', $args); 1161 1162 $page = (int)$page; 1163 1164 $url = str_replace("&", "&", $url); 1165 $url = htmlspecialchars_uni($url); 1166 1167 $pages = ceil($count / $perpage); 1168 1169 $prevpage = ''; 1170 if($page > 1) 1171 { 1172 $prev = $page-1; 1173 $page_url = fetch_page_url($url, $prev); 1174 eval("\$prevpage = \"".$templates->get("multipage_prevpage")."\";"); 1175 } 1176 1177 // Maximum number of "page bits" to show 1178 if(!$mybb->settings['maxmultipagelinks']) 1179 { 1180 $mybb->settings['maxmultipagelinks'] = 5; 1181 } 1182 1183 $from = $page-floor($mybb->settings['maxmultipagelinks']/2); 1184 $to = $page+floor($mybb->settings['maxmultipagelinks']/2); 1185 1186 if($from <= 0) 1187 { 1188 $from = 1; 1189 $to = $from+$mybb->settings['maxmultipagelinks']-1; 1190 } 1191 1192 if($to > $pages) 1193 { 1194 $to = $pages; 1195 $from = $pages-$mybb->settings['maxmultipagelinks']+1; 1196 if($from <= 0) 1197 { 1198 $from = 1; 1199 } 1200 } 1201 1202 if($to == 0) 1203 { 1204 $to = $pages; 1205 } 1206 1207 $start = ''; 1208 if($from > 1) 1209 { 1210 if($from-1 == 1) 1211 { 1212 $lang->multipage_link_start = ''; 1213 } 1214 1215 $page_url = fetch_page_url($url, 1); 1216 eval("\$start = \"".$templates->get("multipage_start")."\";"); 1217 } 1218 1219 $mppage = ''; 1220 for($i = $from; $i <= $to; ++$i) 1221 { 1222 $page_url = fetch_page_url($url, $i); 1223 if($page == $i) 1224 { 1225 if($breadcrumb == true) 1226 { 1227 eval("\$mppage .= \"".$templates->get("multipage_page_link_current")."\";"); 1228 } 1229 else 1230 { 1231 eval("\$mppage .= \"".$templates->get("multipage_page_current")."\";"); 1232 } 1233 } 1234 else 1235 { 1236 eval("\$mppage .= \"".$templates->get("multipage_page")."\";"); 1237 } 1238 } 1239 1240 $end = ''; 1241 if($to < $pages) 1242 { 1243 if($to+1 == $pages) 1244 { 1245 $lang->multipage_link_end = ''; 1246 } 1247 1248 $page_url = fetch_page_url($url, $pages); 1249 eval("\$end = \"".$templates->get("multipage_end")."\";"); 1250 } 1251 1252 $nextpage = ''; 1253 if($page < $pages) 1254 { 1255 $next = $page+1; 1256 $page_url = fetch_page_url($url, $next); 1257 eval("\$nextpage = \"".$templates->get("multipage_nextpage")."\";"); 1258 } 1259 1260 $jumptopage = ''; 1261 if($pages > ($mybb->settings['maxmultipagelinks']+1) && $mybb->settings['jumptopagemultipage'] == 1) 1262 { 1263 // When the second parameter is set to 1, fetch_page_url thinks it's the first page and removes it from the URL as it's unnecessary 1264 $jump_url = fetch_page_url($url, 1); 1265 eval("\$jumptopage = \"".$templates->get("multipage_jump_page")."\";"); 1266 } 1267 1268 $multipage_pages = $lang->sprintf($lang->multipage_pages, $pages); 1269 1270 if($breadcrumb == true) 1271 { 1272 eval("\$multipage = \"".$templates->get("multipage_breadcrumb")."\";"); 1273 } 1274 else 1275 { 1276 eval("\$multipage = \"".$templates->get("multipage")."\";"); 1277 } 1278 1279 return $multipage; 1280 } 1281 1282 /** 1283 * Generate a page URL for use by the multipage function 1284 * 1285 * @param string $url The URL being passed 1286 * @param int $page The page number 1287 * @return string 1288 */ 1289 function fetch_page_url($url, $page) 1290 { 1291 if($page <= 1) 1292 { 1293 $find = array( 1294 "-page-{page}", 1295 "&page={page}", 1296 "{page}" 1297 ); 1298 1299 // Remove "Page 1" to the defacto URL 1300 $url = str_replace($find, array("", "", $page), $url); 1301 return $url; 1302 } 1303 else if(strpos($url, "{page}") === false) 1304 { 1305 // If no page identifier is specified we tack it on to the end of the URL 1306 if(strpos($url, "?") === false) 1307 { 1308 $url .= "?"; 1309 } 1310 else 1311 { 1312 $url .= "&"; 1313 } 1314 1315 $url .= "page=$page"; 1316 } 1317 else 1318 { 1319 $url = str_replace("{page}", $page, $url); 1320 } 1321 1322 return $url; 1323 } 1324 1325 /** 1326 * Fetch the permissions for a specific user 1327 * 1328 * @param int $uid The user ID, if no user ID is provided then current user's ID will be considered. 1329 * @return array Array of user permissions for the specified user 1330 */ 1331 function user_permissions($uid=null) 1332 { 1333 global $mybb, $cache, $groupscache, $user_cache; 1334 1335 // If no user id is specified, assume it is the current user 1336 if($uid === null) 1337 { 1338 $uid = $mybb->user['uid']; 1339 } 1340 1341 // Its a guest. Return the group permissions directly from cache 1342 if($uid == 0) 1343 { 1344 return $groupscache[1]; 1345 } 1346 1347 // User id does not match current user, fetch permissions 1348 if($uid != $mybb->user['uid']) 1349 { 1350 // We've already cached permissions for this user, return them. 1351 if(!empty($user_cache[$uid]['permissions'])) 1352 { 1353 return $user_cache[$uid]['permissions']; 1354 } 1355 1356 // This user was not already cached, fetch their user information. 1357 if(empty($user_cache[$uid])) 1358 { 1359 $user_cache[$uid] = get_user($uid); 1360 } 1361 1362 // Collect group permissions. 1363 $gid = $user_cache[$uid]['usergroup'].",".$user_cache[$uid]['additionalgroups']; 1364 $groupperms = usergroup_permissions($gid); 1365 1366 // Store group permissions in user cache. 1367 $user_cache[$uid]['permissions'] = $groupperms; 1368 return $groupperms; 1369 } 1370 // This user is the current user, return their permissions 1371 else 1372 { 1373 return $mybb->usergroup; 1374 } 1375 } 1376 1377 /** 1378 * Fetch the usergroup permissions for a specific group or series of groups combined 1379 * 1380 * @param int|string $gid A list of groups (Can be a single integer, or a list of groups separated by a comma) 1381 * @return array Array of permissions generated for the groups, containing also a list of comma-separated checked groups under 'all_usergroups' index 1382 */ 1383 function usergroup_permissions($gid=0) 1384 { 1385 global $cache, $groupscache, $grouppermignore, $groupzerogreater, $groupzerolesser, $groupxgreater, $grouppermbyswitch; 1386 1387 if(!is_array($groupscache)) 1388 { 1389 $groupscache = $cache->read("usergroups"); 1390 } 1391 1392 $groups = explode(",", $gid); 1393 1394 if(count($groups) == 1) 1395 { 1396 $groupscache[$gid]['all_usergroups'] = $gid; 1397 return $groupscache[$gid]; 1398 } 1399 1400 $usergroup = array(); 1401 $usergroup['all_usergroups'] = $gid; 1402 1403 // Get those switch permissions from the first valid group. 1404 $permswitches_usergroup = array(); 1405 $grouppermswitches = array(); 1406 foreach(array_values($grouppermbyswitch) as $permvalue) 1407 { 1408 if(is_array($permvalue)) 1409 { 1410 foreach($permvalue as $perm) 1411 { 1412 $grouppermswitches[] = $perm; 1413 } 1414 } 1415 else 1416 { 1417 $grouppermswitches[] = $permvalue; 1418 } 1419 } 1420 $grouppermswitches = array_unique($grouppermswitches); 1421 foreach($groups as $gid) 1422 { 1423 if(trim($gid) == "" || empty($groupscache[$gid])) 1424 { 1425 continue; 1426 } 1427 foreach($grouppermswitches as $perm) 1428 { 1429 $permswitches_usergroup[$perm] = $groupscache[$gid][$perm]; 1430 } 1431 break; // Only retieve the first available group's permissions as how following action does. 1432 } 1433 1434 foreach($groups as $gid) 1435 { 1436 if(trim($gid) == "" || empty($groupscache[$gid])) 1437 { 1438 continue; 1439 } 1440 1441 foreach($groupscache[$gid] as $perm => $access) 1442 { 1443 if(!in_array($perm, $grouppermignore)) 1444 { 1445 if(isset($usergroup[$perm])) 1446 { 1447 $permbit = $usergroup[$perm]; 1448 } 1449 else 1450 { 1451 $permbit = ""; 1452 } 1453 1454 // permission type: 0 not a numerical permission, otherwise a numerical permission. 1455 // Positive value is for `greater is more` permission, negative for `lesser is more`. 1456 $perm_is_numerical = 0; 1457 $perm_numerical_lowerbound = 0; 1458 1459 // 0 represents unlimited for most numerical group permissions (i.e. private message limit) so take that into account. 1460 if(in_array($perm, $groupzerogreater)) 1461 { 1462 // 1 means a `0 or greater` permission. Value 0 means unlimited. 1463 $perm_is_numerical = 1; 1464 } 1465 // Less is more for some numerical group permissions (i.e. post count required for using signature) so take that into account, too. 1466 else if(in_array($perm, $groupzerolesser)) 1467 { 1468 // -1 means a `0 or lesser` permission. Value 0 means unlimited. 1469 $perm_is_numerical = -1; 1470 } 1471 // Greater is more, but with a lower bound. 1472 else if(array_key_exists($perm, $groupxgreater)) 1473 { 1474 // 2 means a general `greater` permission. Value 0 just means 0. 1475 $perm_is_numerical = 2; 1476 $perm_numerical_lowerbound = $groupxgreater[$perm]; 1477 } 1478 1479 if($perm_is_numerical != 0) 1480 { 1481 $update_current_perm = true; 1482 1483 // Ensure it's an integer. 1484 $access = (int)$access; 1485 // Check if this permission should be activatived by another switch permission in current group. 1486 if(array_key_exists($perm, $grouppermbyswitch)) 1487 { 1488 if(!is_array($grouppermbyswitch[$perm])) 1489 { 1490 $grouppermbyswitch[$perm] = array($grouppermbyswitch[$perm]); 1491 } 1492 1493 $update_current_perm = $group_current_perm_enabled = $group_perm_enabled = false; 1494 foreach($grouppermbyswitch[$perm] as $permswitch) 1495 { 1496 if(!isset($groupscache[$gid][$permswitch])) 1497 { 1498 continue; 1499 } 1500 $permswitches_current = $groupscache[$gid][$permswitch]; 1501 1502 // Determin if the permission is enabled by switches from current group. 1503 if($permswitches_current == 1 || $permswitches_current == "yes") // Keep yes/no for compatibility? 1504 { 1505 $group_current_perm_enabled = true; 1506 } 1507 // Determin if the permission is enabled by switches from previously handled groups. 1508 if($permswitches_usergroup[$permswitch] == 1 || $permswitches_usergroup[$permswitch] == "yes") // Keep yes/no for compatibility? 1509 { 1510 $group_perm_enabled = true; 1511 } 1512 } 1513 1514 // Set this permission if not set yet. 1515 if(!isset($usergroup[$perm])) 1516 { 1517 $usergroup[$perm] = $access; 1518 } 1519 1520 // If current group's setting enables the permission, we may need to update the user's permission. 1521 if($group_current_perm_enabled) 1522 { 1523 // Only update this permission if both its switch and current group switch are on. 1524 if($group_perm_enabled) 1525 { 1526 $update_current_perm = true; 1527 } 1528 // Override old useless value with value from current group. 1529 else 1530 { 1531 $usergroup[$perm] = $access; 1532 } 1533 } 1534 } 1535 1536 // No switch controls this permission, or permission needs an update. 1537 if($update_current_perm) 1538 { 1539 switch($perm_is_numerical) 1540 { 1541 case 1: 1542 case -1: 1543 if($access == 0 || $permbit === 0) 1544 { 1545 $usergroup[$perm] = 0; 1546 break; 1547 } 1548 default: 1549 if($perm_is_numerical > 0 && $access > $permbit || $perm_is_numerical < 0 && $access < $permbit) 1550 { 1551 $usergroup[$perm] = $access; 1552 } 1553 break; 1554 } 1555 } 1556 1557 // Maybe oversubtle, database uses Unsigned on them, but enables usage of permission value with a lower bound. 1558 if($usergroup[$perm] < $perm_numerical_lowerbound) 1559 { 1560 $usergroup[$perm] = $perm_numerical_lowerbound; 1561 } 1562 1563 // Work is done for numerical permissions. 1564 continue; 1565 } 1566 1567 if($access > $permbit || ($access == "yes" && $permbit == "no") || !$permbit) // Keep yes/no for compatibility? 1568 { 1569 $usergroup[$perm] = $access; 1570 } 1571 } 1572 } 1573 1574 foreach($permswitches_usergroup as $perm => $value) 1575 { 1576 $permswitches_usergroup[$perm] = $usergroup[$perm]; 1577 } 1578 } 1579 1580 return $usergroup; 1581 } 1582 1583 /** 1584 * Fetch the display group properties for a specific display group 1585 * 1586 * @param int $gid The group ID to fetch the display properties for 1587 * @return array Array of display properties for the group 1588 */ 1589 function usergroup_displaygroup($gid) 1590 { 1591 global $cache, $groupscache, $displaygroupfields; 1592 1593 if(!is_array($groupscache)) 1594 { 1595 $groupscache = $cache->read("usergroups"); 1596 } 1597 1598 $displaygroup = array(); 1599 $group = $groupscache[$gid]; 1600 1601 foreach($displaygroupfields as $field) 1602 { 1603 $displaygroup[$field] = $group[$field]; 1604 } 1605 1606 return $displaygroup; 1607 } 1608 1609 /** 1610 * Build the forum permissions for a specific forum, user or group 1611 * 1612 * @param int $fid The forum ID to build permissions for (0 builds for all forums) 1613 * @param int $uid The user to build the permissions for (0 will select the uid automatically) 1614 * @param int $gid The group of the user to build permissions for (0 will fetch it) 1615 * @return array Forum permissions for the specific forum or forums 1616 */ 1617 function forum_permissions($fid=0, $uid=0, $gid=0) 1618 { 1619 global $db, $cache, $groupscache, $forum_cache, $fpermcache, $mybb, $cached_forum_permissions_permissions, $cached_forum_permissions; 1620 1621 if($uid == 0) 1622 { 1623 $uid = $mybb->user['uid']; 1624 } 1625 1626 if(!$gid || $gid == 0) // If no group, we need to fetch it 1627 { 1628 if($uid != 0 && $uid != $mybb->user['uid']) 1629 { 1630 $user = get_user($uid); 1631 1632 $gid = $user['usergroup'].",".$user['additionalgroups']; 1633 $groupperms = usergroup_permissions($gid); 1634 } 1635 else 1636 { 1637 $gid = $mybb->user['usergroup']; 1638 1639 if(isset($mybb->user['additionalgroups'])) 1640 { 1641 $gid .= ",".$mybb->user['additionalgroups']; 1642 } 1643 1644 $groupperms = $mybb->usergroup; 1645 } 1646 } 1647 1648 if(!is_array($forum_cache)) 1649 { 1650 $forum_cache = cache_forums(); 1651 1652 if(!$forum_cache) 1653 { 1654 return false; 1655 } 1656 } 1657 1658 if(!is_array($fpermcache)) 1659 { 1660 $fpermcache = $cache->read("forumpermissions"); 1661 } 1662 1663 if($fid) // Fetch the permissions for a single forum 1664 { 1665 if(empty($cached_forum_permissions_permissions[$gid][$fid])) 1666 { 1667 $cached_forum_permissions_permissions[$gid][$fid] = fetch_forum_permissions($fid, $gid, $groupperms); 1668 } 1669 return $cached_forum_permissions_permissions[$gid][$fid]; 1670 } 1671 else 1672 { 1673 if(empty($cached_forum_permissions[$gid])) 1674 { 1675 foreach($forum_cache as $forum) 1676 { 1677 $cached_forum_permissions[$gid][$forum['fid']] = fetch_forum_permissions($forum['fid'], $gid, $groupperms); 1678 } 1679 } 1680 return $cached_forum_permissions[$gid]; 1681 } 1682 } 1683 1684 /** 1685 * Fetches the permissions for a specific forum/group applying the inheritance scheme. 1686 * Called by forum_permissions() 1687 * 1688 * @param int $fid The forum ID 1689 * @param string $gid A comma separated list of usergroups 1690 * @param array $groupperms Group permissions 1691 * @return array Permissions for this forum 1692 */ 1693 function fetch_forum_permissions($fid, $gid, $groupperms) 1694 { 1695 global $groupscache, $forum_cache, $fpermcache, $mybb, $fpermfields; 1696 1697 if(isset($gid)) 1698 { 1699 $groups = explode(",", $gid); 1700 } 1701 else 1702 { 1703 $groups = array(); 1704 } 1705 1706 $current_permissions = array(); 1707 $only_view_own_threads = 1; 1708 $only_reply_own_threads = 1; 1709 1710 if(empty($fpermcache[$fid])) // This forum has no custom or inherited permissions so lets just return the group permissions 1711 { 1712 $current_permissions = $groupperms; 1713 } 1714 else 1715 { 1716 foreach($groups as $gid) 1717 { 1718 // If this forum has custom or inherited permissions for the currently looped group. 1719 if(!empty($fpermcache[$fid][$gid])) 1720 { 1721 $level_permissions = $fpermcache[$fid][$gid]; 1722 } 1723 // Or, use the group permission instead, if available. Some forum permissions not existing here will be added back later. 1724 else if(!empty($groupscache[$gid])) 1725 { 1726 $level_permissions = $groupscache[$gid]; 1727 } 1728 // No permission is available for the currently looped group, probably we have bad data here. 1729 else 1730 { 1731 continue; 1732 } 1733 1734 foreach($level_permissions as $permission => $access) 1735 { 1736 if(empty($current_permissions[$permission]) || $access >= $current_permissions[$permission] || ($access == "yes" && $current_permissions[$permission] == "no")) 1737 { 1738 $current_permissions[$permission] = $access; 1739 } 1740 } 1741 1742 if($level_permissions["canview"] && empty($level_permissions["canonlyviewownthreads"])) 1743 { 1744 $only_view_own_threads = 0; 1745 } 1746 1747 if($level_permissions["canpostreplys"] && empty($level_permissions["canonlyreplyownthreads"])) 1748 { 1749 $only_reply_own_threads = 0; 1750 } 1751 } 1752 1753 if(count($current_permissions) == 0) 1754 { 1755 $current_permissions = $groupperms; 1756 } 1757 } 1758 1759 // Figure out if we can view more than our own threads 1760 if($only_view_own_threads == 0 || !isset($current_permissions["canonlyviewownthreads"])) 1761 { 1762 $current_permissions["canonlyviewownthreads"] = 0; 1763 } 1764 1765 // Figure out if we can reply more than our own threads 1766 if($only_reply_own_threads == 0 || !isset($current_permissions["canonlyreplyownthreads"])) 1767 { 1768 $current_permissions["canonlyreplyownthreads"] = 0; 1769 } 1770 1771 return $current_permissions; 1772 } 1773 1774 /** 1775 * Check whether password for given forum was validated for the current user 1776 * 1777 * @param array $forum The forum data 1778 * @param bool $ignore_empty Whether to treat forum password configured as an empty string as validated 1779 * @param bool $check_parents Whether to check parent forums using `parentlist` 1780 * @return bool 1781 */ 1782 function forum_password_validated($forum, $ignore_empty=false, $check_parents=false) 1783 { 1784 global $mybb, $forum_cache; 1785 1786 if($check_parents && isset($forum['parentlist'])) 1787 { 1788 if(!is_array($forum_cache)) 1789 { 1790 $forum_cache = cache_forums(); 1791 if(!$forum_cache) 1792 { 1793 return false; 1794 } 1795 } 1796 1797 $parents = explode(',', $forum['parentlist']); 1798 rsort($parents); 1799 1800 foreach($parents as $parent_id) 1801 { 1802 if($parent_id != $forum['fid'] && !forum_password_validated($forum_cache[$parent_id], true)) 1803 { 1804 return false; 1805 } 1806 } 1807 } 1808 1809 return ($ignore_empty && $forum['password'] === '') || ( 1810 isset($mybb->cookies['forumpass'][$forum['fid']]) && 1811 my_hash_equals( 1812 md5($mybb->user['uid'].$forum['password']), 1813 $mybb->cookies['forumpass'][$forum['fid']] 1814 ) 1815 ); 1816 } 1817 1818 /** 1819 * Check the password given on a certain forum for validity 1820 * 1821 * @param int $fid The forum ID 1822 * @param int $pid The Parent ID 1823 * @param bool $return 1824 * @return bool 1825 */ 1826 function check_forum_password($fid, $pid=0, $return=false) 1827 { 1828 global $mybb, $header, $footer, $headerinclude, $theme, $templates, $lang, $forum_cache; 1829 1830 $showform = true; 1831 1832 if(!is_array($forum_cache)) 1833 { 1834 $forum_cache = cache_forums(); 1835 if(!$forum_cache) 1836 { 1837 return false; 1838 } 1839 } 1840 1841 // Loop through each of parent forums to ensure we have a password for them too 1842 if(isset($forum_cache[$fid]['parentlist'])) 1843 { 1844 $parents = explode(',', $forum_cache[$fid]['parentlist']); 1845 rsort($parents); 1846 } 1847 if(!empty($parents)) 1848 { 1849 foreach($parents as $parent_id) 1850 { 1851 if($parent_id == $fid || $parent_id == $pid) 1852 { 1853 continue; 1854 } 1855 1856 if($forum_cache[$parent_id]['password'] !== "") 1857 { 1858 check_forum_password($parent_id, $fid); 1859 } 1860 } 1861 } 1862 1863 if($forum_cache[$fid]['password'] !== '') 1864 { 1865 if(isset($mybb->input['pwverify']) && $pid == 0) 1866 { 1867 if(my_hash_equals($forum_cache[$fid]['password'], $mybb->get_input('pwverify'))) 1868 { 1869 my_setcookie("forumpass[$fid]", md5($mybb->user['uid'].$mybb->get_input('pwverify')), null, true); 1870 $showform = false; 1871 } 1872 else 1873 { 1874 eval("\$pwnote = \"".$templates->get("forumdisplay_password_wrongpass")."\";"); 1875 $showform = true; 1876 } 1877 } 1878 else 1879 { 1880 if(!forum_password_validated($forum_cache[$fid])) 1881 { 1882 $showform = true; 1883 } 1884 else 1885 { 1886 $showform = false; 1887 } 1888 } 1889 } 1890 else 1891 { 1892 $showform = false; 1893 } 1894 1895 if($return) 1896 { 1897 return $showform; 1898 } 1899 1900 if($showform) 1901 { 1902 if($pid) 1903 { 1904 header("Location: ".$mybb->settings['bburl']."/".get_forum_link($fid)); 1905 } 1906 else 1907 { 1908 $_SERVER['REQUEST_URI'] = htmlspecialchars_uni($_SERVER['REQUEST_URI']); 1909 eval("\$pwform = \"".$templates->get("forumdisplay_password")."\";"); 1910 output_page($pwform); 1911 } 1912 exit; 1913 } 1914 } 1915 1916 /** 1917 * Return the permissions for a moderator in a specific forum 1918 * 1919 * @param int $fid The forum ID 1920 * @param int $uid The user ID to fetch permissions for (0 assumes current logged in user) 1921 * @param string $parentslist The parent list for the forum (if blank, will be fetched) 1922 * @return array Array of moderator permissions for the specific forum 1923 */ 1924 function get_moderator_permissions($fid, $uid=0, $parentslist="") 1925 { 1926 global $mybb, $cache, $db; 1927 static $modpermscache; 1928 1929 if($uid < 1) 1930 { 1931 $uid = $mybb->user['uid']; 1932 } 1933 1934 if($uid == 0) 1935 { 1936 return false; 1937 } 1938 1939 if(isset($modpermscache[$fid][$uid])) 1940 { 1941 return $modpermscache[$fid][$uid]; 1942 } 1943 1944 if(!$parentslist) 1945 { 1946 $parentslist = explode(',', get_parent_list($fid)); 1947 } 1948 1949 // Get user groups 1950 $perms = array(); 1951 $user = get_user($uid); 1952 1953 $groups = array($user['usergroup']); 1954 1955 if(!empty($user['additionalgroups'])) 1956 { 1957 $extra_groups = explode(",", $user['additionalgroups']); 1958 1959 foreach($extra_groups as $extra_group) 1960 { 1961 $groups[] = $extra_group; 1962 } 1963 } 1964 1965 $mod_cache = $cache->read("moderators"); 1966 1967 foreach($mod_cache as $forumid => $forum) 1968 { 1969 if(empty($forum) || !is_array($forum) || !in_array($forumid, $parentslist)) 1970 { 1971 // No perms or we're not after this forum 1972 continue; 1973 } 1974 1975 // User settings override usergroup settings 1976 if(!empty($forum['users'][$uid])) 1977 { 1978 $perm = $forum['users'][$uid]; 1979 foreach($perm as $action => $value) 1980 { 1981 if(strpos($action, "can") === false) 1982 { 1983 continue; 1984 } 1985 1986 if(!isset($perms[$action])) 1987 { 1988 $perms[$action] = $value; 1989 } 1990 // Figure out the user permissions 1991 else if($value == 0) 1992 { 1993 // The user doesn't have permission to set this action 1994 $perms[$action] = 0; 1995 } 1996 else 1997 { 1998 $perms[$action] = max($perm[$action], $perms[$action]); 1999 } 2000 } 2001 } 2002 2003 foreach($groups as $group) 2004 { 2005 if(empty($forum['usergroups'][$group]) || !is_array($forum['usergroups'][$group])) 2006 { 2007 // There are no permissions set for this group 2008 continue; 2009 } 2010 2011 $perm = $forum['usergroups'][$group]; 2012 foreach($perm as $action => $value) 2013 { 2014 if(strpos($action, "can") === false) 2015 { 2016 continue; 2017 } 2018 2019 if(!isset($perms[$action])) 2020 { 2021 $perms[$action] = $value; 2022 } 2023 else 2024 { 2025 $perms[$action] = max($perm[$action], $perms[$action]); 2026 } 2027 } 2028 } 2029 } 2030 2031 $modpermscache[$fid][$uid] = $perms; 2032 2033 return $perms; 2034 } 2035 2036 /** 2037 * Checks if a moderator has permissions to perform an action in a specific forum 2038 * 2039 * @param int $fid The forum ID (0 assumes global) 2040 * @param string $action The action tyring to be performed. (blank assumes any action at all) 2041 * @param int $uid The user ID (0 assumes current user) 2042 * @return bool Returns true if the user has permission, false if they do not 2043 */ 2044 function is_moderator($fid=0, $action="", $uid=0) 2045 { 2046 global $mybb, $cache, $plugins; 2047 2048 if($uid == 0) 2049 { 2050 $uid = $mybb->user['uid']; 2051 } 2052 2053 if($uid == 0) 2054 { 2055 return false; 2056 } 2057 2058 $user_perms = user_permissions($uid); 2059 2060 $hook_args = array( 2061 'fid' => $fid, 2062 'action' => $action, 2063 'uid' => $uid, 2064 ); 2065 2066 $plugins->run_hooks("is_moderator", $hook_args); 2067 2068 if(isset($hook_args['is_moderator'])) 2069 { 2070 return (boolean) $hook_args['is_moderator']; 2071 } 2072 2073 if(!empty($user_perms['issupermod']) && $user_perms['issupermod'] == 1) 2074 { 2075 if($fid) 2076 { 2077 $forumpermissions = forum_permissions($fid); 2078 if(!empty($forumpermissions['canview']) && !empty($forumpermissions['canviewthreads']) && empty($forumpermissions['canonlyviewownthreads'])) 2079 { 2080 return true; 2081 } 2082 return false; 2083 } 2084 return true; 2085 } 2086 else 2087 { 2088 if(!$fid) 2089 { 2090 $modcache = $cache->read('moderators'); 2091 if(!empty($modcache)) 2092 { 2093 foreach($modcache as $modusers) 2094 { 2095 if(isset($modusers['users'][$uid]) && $modusers['users'][$uid]['mid'] && (!$action || !empty($modusers['users'][$uid][$action]))) 2096 { 2097 return true; 2098 } 2099 2100 $groups = explode(',', $user_perms['all_usergroups']); 2101 2102 foreach($groups as $group) 2103 { 2104 if(trim($group) != '' && isset($modusers['usergroups'][$group]) && (!$action || !empty($modusers['usergroups'][$group][$action]))) 2105 { 2106 return true; 2107 } 2108 } 2109 } 2110 } 2111 return false; 2112 } 2113 else 2114 { 2115 $modperms = get_moderator_permissions($fid, $uid); 2116 2117 if(!$action && $modperms) 2118 { 2119 return true; 2120 } 2121 else 2122 { 2123 if(isset($modperms[$action]) && $modperms[$action] == 1) 2124 { 2125 return true; 2126 } 2127 else 2128 { 2129 return false; 2130 } 2131 } 2132 } 2133 } 2134 } 2135 2136 /** 2137 * Get an array of fids that the forum moderator has access to. 2138 * Do not use for administraotrs or global moderators as they moderate any forum and the function will return false. 2139 * 2140 * @param int $uid The user ID (0 assumes current user) 2141 * @return array|bool an array of the fids the user has moderator access to or bool if called incorrectly. 2142 */ 2143 function get_moderated_fids($uid=0) 2144 { 2145 global $mybb, $cache; 2146 2147 if($uid == 0) 2148 { 2149 $uid = $mybb->user['uid']; 2150 } 2151 2152 if($uid == 0) 2153 { 2154 return array(); 2155 } 2156 2157 $user_perms = user_permissions($uid); 2158 2159 if($user_perms['issupermod'] == 1) 2160 { 2161 return false; 2162 } 2163 2164 $fids = array(); 2165 2166 $modcache = $cache->read('moderators'); 2167 if(!empty($modcache)) 2168 { 2169 $groups = explode(',', $user_perms['all_usergroups']); 2170 2171 foreach($modcache as $fid => $forum) 2172 { 2173 if(isset($forum['users'][$uid]) && $forum['users'][$uid]['mid']) 2174 { 2175 $fids[] = $fid; 2176 continue; 2177 } 2178 2179 foreach($groups as $group) 2180 { 2181 if(trim($group) != '' && isset($forum['usergroups'][$group])) 2182 { 2183 $fids[] = $fid; 2184 } 2185 } 2186 } 2187 } 2188 2189 return $fids; 2190 } 2191 2192 /** 2193 * Generate a list of the posticons. 2194 * 2195 * @return string The template of posticons. 2196 */ 2197 function get_post_icons() 2198 { 2199 global $mybb, $cache, $icon, $theme, $templates, $lang; 2200 2201 if(isset($mybb->input['icon'])) 2202 { 2203 $icon = $mybb->get_input('icon'); 2204 } 2205 2206 $iconlist = ''; 2207 $no_icons_checked = " checked=\"checked\""; 2208 // read post icons from cache, and sort them accordingly 2209 $posticons_cache = (array)$cache->read("posticons"); 2210 $posticons = array(); 2211 foreach($posticons_cache as $posticon) 2212 { 2213 $posticons[$posticon['name']] = $posticon; 2214 } 2215 krsort($posticons); 2216 2217 foreach($posticons as $dbicon) 2218 { 2219 $dbicon['path'] = str_replace("{theme}", $theme['imgdir'], $dbicon['path']); 2220 $dbicon['path'] = htmlspecialchars_uni($mybb->get_asset_url($dbicon['path'])); 2221 $dbicon['name'] = htmlspecialchars_uni($dbicon['name']); 2222 2223 if($icon == $dbicon['iid']) 2224 { 2225 $checked = " checked=\"checked\""; 2226 $no_icons_checked = ''; 2227 } 2228 else 2229 { 2230 $checked = ''; 2231 } 2232 2233 eval("\$iconlist .= \"".$templates->get("posticons_icon")."\";"); 2234 } 2235 2236 if(!empty($iconlist)) 2237 { 2238 eval("\$posticons = \"".$templates->get("posticons")."\";"); 2239 } 2240 else 2241 { 2242 $posticons = ''; 2243 } 2244 2245 return $posticons; 2246 } 2247 2248 /** 2249 * MyBB setcookie() wrapper. 2250 * 2251 * @param string $name The cookie identifier. 2252 * @param string $value The cookie value. 2253 * @param int|string $expires The timestamp of the expiry date. 2254 * @param boolean $httponly True if setting a HttpOnly cookie (supported by the majority of web browsers) 2255 * @param string $samesite The samesite attribute to prevent CSRF. 2256 */ 2257 function my_setcookie($name, $value="", $expires="", $httponly=false, $samesite="") 2258 { 2259 global $mybb; 2260 2261 if(!$mybb->settings['cookiepath']) 2262 { 2263 $mybb->settings['cookiepath'] = "/"; 2264 } 2265 2266 if($expires == -1) 2267 { 2268 $expires = 0; 2269 } 2270 elseif($expires == "" || $expires == null) 2271 { 2272 $expires = TIME_NOW + (60*60*24*365); // Make the cookie expire in a years time 2273 } 2274 else 2275 { 2276 $expires = TIME_NOW + (int)$expires; 2277 } 2278 2279 $mybb->settings['cookiepath'] = str_replace(array("\n","\r"), "", $mybb->settings['cookiepath']); 2280 $mybb->settings['cookiedomain'] = str_replace(array("\n","\r"), "", $mybb->settings['cookiedomain']); 2281 $mybb->settings['cookieprefix'] = str_replace(array("\n","\r", " "), "", $mybb->settings['cookieprefix']); 2282 2283 // Versions of PHP prior to 5.2 do not support HttpOnly cookies and IE is buggy when specifying a blank domain so set the cookie manually 2284 $cookie = "Set-Cookie: {$mybb->settings['cookieprefix']}{$name}=".urlencode($value); 2285 2286 if($expires > 0) 2287 { 2288 $cookie .= "; expires=".@gmdate('D, d-M-Y H:i:s \\G\\M\\T', $expires); 2289 } 2290 2291 if(!empty($mybb->settings['cookiepath'])) 2292 { 2293 $cookie .= "; path={$mybb->settings['cookiepath']}"; 2294 } 2295 2296 if(!empty($mybb->settings['cookiedomain'])) 2297 { 2298 $cookie .= "; domain={$mybb->settings['cookiedomain']}"; 2299 } 2300 2301 if($httponly == true) 2302 { 2303 $cookie .= "; HttpOnly"; 2304 } 2305 2306 if($samesite != "" && $mybb->settings['cookiesamesiteflag']) 2307 { 2308 $samesite = strtolower($samesite); 2309 2310 if($samesite == "lax" || $samesite == "strict") 2311 { 2312 $cookie .= "; SameSite=".$samesite; 2313 } 2314 } 2315 2316 if($mybb->settings['cookiesecureflag']) 2317 { 2318 $cookie .= "; Secure"; 2319 } 2320 2321 $mybb->cookies[$name] = $value; 2322 2323 header($cookie, false); 2324 } 2325 2326 /** 2327 * Unset a cookie set by MyBB. 2328 * 2329 * @param string $name The cookie identifier. 2330 */ 2331 function my_unsetcookie($name) 2332 { 2333 global $mybb; 2334 2335 $expires = -3600; 2336 my_setcookie($name, "", $expires); 2337 2338 unset($mybb->cookies[$name]); 2339 } 2340 2341 /** 2342 * Get the contents from a serialised cookie array. 2343 * 2344 * @param string $name The cookie identifier. 2345 * @param int $id The cookie content id. 2346 * @return array|boolean The cookie id's content array or false when non-existent. 2347 */ 2348 function my_get_array_cookie($name, $id) 2349 { 2350 global $mybb; 2351 2352 if(!isset($mybb->cookies['mybb'][$name])) 2353 { 2354 return false; 2355 } 2356 2357 $cookie = my_unserialize($mybb->cookies['mybb'][$name], false); 2358 2359 if(is_array($cookie) && isset($cookie[$id])) 2360 { 2361 return $cookie[$id]; 2362 } 2363 else 2364 { 2365 return 0; 2366 } 2367 } 2368 2369 /** 2370 * Set a serialised cookie array. 2371 * 2372 * @param string $name The cookie identifier. 2373 * @param int $id The cookie content id. 2374 * @param string $value The value to set the cookie to. 2375 * @param int|string $expires The timestamp of the expiry date. 2376 */ 2377 function my_set_array_cookie($name, $id, $value, $expires="") 2378 { 2379 global $mybb; 2380 2381 if(isset($mybb->cookies['mybb'][$name])) 2382 { 2383 $newcookie = my_unserialize($mybb->cookies['mybb'][$name], false); 2384 } 2385 else 2386 { 2387 $newcookie = array(); 2388 } 2389 2390 $newcookie[$id] = $value; 2391 $newcookie = my_serialize($newcookie); 2392 my_setcookie("mybb[$name]", addslashes($newcookie), $expires); 2393 2394 if(isset($mybb->cookies['mybb']) && !is_array($mybb->cookies['mybb'])) 2395 { 2396 $mybb->cookies['mybb'] = array(); 2397 } 2398 2399 // Make sure our current viarables are up-to-date as well 2400 $mybb->cookies['mybb'][$name] = $newcookie; 2401 } 2402 2403 /* 2404 * Arbitrary limits for _safe_unserialize() 2405 */ 2406 define('MAX_SERIALIZED_INPUT_LENGTH', 10240); 2407 define('MAX_SERIALIZED_ARRAY_LENGTH', 256); 2408 define('MAX_SERIALIZED_ARRAY_DEPTH', 5); 2409 2410 /** 2411 * Credits go to https://github.com/piwik 2412 * Safe unserialize() replacement 2413 * - accepts a strict subset of PHP's native my_serialized representation 2414 * - does not unserialize objects 2415 * 2416 * @param string $str 2417 * @param bool $unlimited Whether to apply limits defined in MAX_SERIALIZED_* constants 2418 * @return mixed 2419 * @throw Exception if $str is malformed or contains unsupported types (e.g., resources, objects) 2420 */ 2421 function _safe_unserialize($str, $unlimited = true) 2422 { 2423 if(!$unlimited && strlen($str) > MAX_SERIALIZED_INPUT_LENGTH) 2424 { 2425 // input exceeds MAX_SERIALIZED_INPUT_LENGTH 2426 return false; 2427 } 2428 2429 if(empty($str) || !is_string($str)) 2430 { 2431 return false; 2432 } 2433 2434 $stack = $list = $expected = array(); 2435 2436 /* 2437 * states: 2438 * 0 - initial state, expecting a single value or array 2439 * 1 - terminal state 2440 * 2 - in array, expecting end of array or a key 2441 * 3 - in array, expecting value or another array 2442 */ 2443 $state = 0; 2444 while($state != 1) 2445 { 2446 $type = isset($str[0]) ? $str[0] : ''; 2447 2448 if($type == '}') 2449 { 2450 $str = substr($str, 1); 2451 } 2452 else if($type == 'N' && $str[1] == ';') 2453 { 2454 $value = null; 2455 $str = substr($str, 2); 2456 } 2457 else if($type == 'b' && preg_match('/^b:([01]);/', $str, $matches)) 2458 { 2459 $value = $matches[1] == '1' ? true : false; 2460 $str = substr($str, 4); 2461 } 2462 else if($type == 'i' && preg_match('/^i:(-?[0-9]+);(.*)/s', $str, $matches)) 2463 { 2464 $value = (int)$matches[1]; 2465 $str = $matches[2]; 2466 } 2467 else if($type == 'd' && preg_match('/^d:(-?[0-9]+\.?[0-9]*(E[+-][0-9]+)?);(.*)/s', $str, $matches)) 2468 { 2469 $value = (float)$matches[1]; 2470 $str = $matches[3]; 2471 } 2472 else if($type == 's' && preg_match('/^s:([0-9]+):"(.*)/s', $str, $matches) && substr($matches[2], (int)$matches[1], 2) == '";') 2473 { 2474 $value = substr($matches[2], 0, (int)$matches[1]); 2475 $str = substr($matches[2], (int)$matches[1] + 2); 2476 } 2477 else if( 2478 $type == 'a' && 2479 preg_match('/^a:([0-9]+):{(.*)/s', $str, $matches) && 2480 ($unlimited || $matches[1] < MAX_SERIALIZED_ARRAY_LENGTH) 2481 ) 2482 { 2483 $expectedLength = (int)$matches[1]; 2484 $str = $matches[2]; 2485 } 2486 else 2487 { 2488 // object or unknown/malformed type 2489 return false; 2490 } 2491 2492 switch($state) 2493 { 2494 case 3: // in array, expecting value or another array 2495 if($type == 'a') 2496 { 2497 if(!$unlimited && count($stack) >= MAX_SERIALIZED_ARRAY_DEPTH) 2498 { 2499 // array nesting exceeds MAX_SERIALIZED_ARRAY_DEPTH 2500 return false; 2501 } 2502 2503 $stack[] = &$list; 2504 $list[$key] = array(); 2505 $list = &$list[$key]; 2506 $expected[] = $expectedLength; 2507 $state = 2; 2508 break; 2509 } 2510 if($type != '}') 2511 { 2512 $list[$key] = $value; 2513 $state = 2; 2514 break; 2515 } 2516 2517 // missing array value 2518 return false; 2519 2520 case 2: // in array, expecting end of array or a key 2521 if($type == '}') 2522 { 2523 if(count($list) < end($expected)) 2524 { 2525 // array size less than expected 2526 return false; 2527 } 2528 2529 unset($list); 2530 $list = &$stack[count($stack)-1]; 2531 array_pop($stack); 2532 2533 // go to terminal state if we're at the end of the root array 2534 array_pop($expected); 2535 if(count($expected) == 0) { 2536 $state = 1; 2537 } 2538 break; 2539 } 2540 if($type == 'i' || $type == 's') 2541 { 2542 if(!$unlimited && count($list) >= MAX_SERIALIZED_ARRAY_LENGTH) 2543 { 2544 // array size exceeds MAX_SERIALIZED_ARRAY_LENGTH 2545 return false; 2546 } 2547 if(count($list) >= end($expected)) 2548 { 2549 // array size exceeds expected length 2550 return false; 2551 } 2552 2553 $key = $value; 2554 $state = 3; 2555 break; 2556 } 2557 2558 // illegal array index type 2559 return false; 2560 2561 case 0: // expecting array or value 2562 if($type == 'a') 2563 { 2564 if(!$unlimited && count($stack) >= MAX_SERIALIZED_ARRAY_DEPTH) 2565 { 2566 // array nesting exceeds MAX_SERIALIZED_ARRAY_DEPTH 2567 return false; 2568 } 2569 2570 $data = array(); 2571 $list = &$data; 2572 $expected[] = $expectedLength; 2573 $state = 2; 2574 break; 2575 } 2576 if($type != '}') 2577 { 2578 $data = $value; 2579 $state = 1; 2580 break; 2581 } 2582 2583 // not in array 2584 return false; 2585 } 2586 } 2587 2588 if(!empty($str)) 2589 { 2590 // trailing data in input 2591 return false; 2592 } 2593 return $data; 2594 } 2595 2596 /** 2597 * Credits go to https://github.com/piwik 2598 * Wrapper for _safe_unserialize() that handles exceptions and multibyte encoding issue 2599 * 2600 * @param string $str 2601 * @param bool $unlimited 2602 * @return mixed 2603 */ 2604 function my_unserialize($str, $unlimited = true) 2605 { 2606 // Ensure we use the byte count for strings even when strlen() is overloaded by mb_strlen() 2607 if(function_exists('mb_internal_encoding') && (((int)ini_get('mbstring.func_overload')) & 2)) 2608 { 2609 $mbIntEnc = mb_internal_encoding(); 2610 mb_internal_encoding('ASCII'); 2611 } 2612 2613 $out = _safe_unserialize($str, $unlimited); 2614 2615 if(isset($mbIntEnc)) 2616 { 2617 mb_internal_encoding($mbIntEnc); 2618 } 2619 2620 return $out; 2621 } 2622 2623 /** 2624 * Unserializes data using PHP's `unserialize()`, and its safety options if possible. 2625 * This function should only be used for values from trusted sources. 2626 * 2627 * @param string $str 2628 * @return mixed 2629 */ 2630 function native_unserialize($str) 2631 { 2632 if(version_compare(PHP_VERSION, '7.0.0', '>=')) 2633 { 2634 return unserialize($str, array('allowed_classes' => false)); 2635 } 2636 else 2637 { 2638 return unserialize($str); 2639 } 2640 } 2641 2642 /** 2643 * Credits go to https://github.com/piwik 2644 * Safe serialize() replacement 2645 * - output a strict subset of PHP's native serialized representation 2646 * - does not my_serialize objects 2647 * 2648 * @param mixed $value 2649 * @return string 2650 * @throw Exception if $value is malformed or contains unsupported types (e.g., resources, objects) 2651 */ 2652 function _safe_serialize( $value ) 2653 { 2654 if(is_null($value)) 2655 { 2656 return 'N;'; 2657 } 2658 2659 if(is_bool($value)) 2660 { 2661 return 'b:'.(int)$value.';'; 2662 } 2663 2664 if(is_int($value)) 2665 { 2666 return 'i:'.$value.';'; 2667 } 2668 2669 if(is_float($value)) 2670 { 2671 return 'd:'.str_replace(',', '.', $value).';'; 2672 } 2673 2674 if(is_string($value)) 2675 { 2676 return 's:'.strlen($value).':"'.$value.'";'; 2677 } 2678 2679 if(is_array($value)) 2680 { 2681 $out = ''; 2682 foreach($value as $k => $v) 2683 { 2684 $out .= _safe_serialize($k) . _safe_serialize($v); 2685 } 2686 2687 return 'a:'.count($value).':{'.$out.'}'; 2688 } 2689 2690 // safe_serialize cannot my_serialize resources or objects 2691 return false; 2692 } 2693 2694 /** 2695 * Credits go to https://github.com/piwik 2696 * Wrapper for _safe_serialize() that handles exceptions and multibyte encoding issue 2697 * 2698 * @param mixed $value 2699 * @return string 2700 */ 2701 function my_serialize($value) 2702 { 2703 // ensure we use the byte count for strings even when strlen() is overloaded by mb_strlen() 2704 if(function_exists('mb_internal_encoding') && (((int)ini_get('mbstring.func_overload')) & 2)) 2705 { 2706 $mbIntEnc = mb_internal_encoding(); 2707 mb_internal_encoding('ASCII'); 2708 } 2709 2710 $out = _safe_serialize($value); 2711 if(isset($mbIntEnc)) 2712 { 2713 mb_internal_encoding($mbIntEnc); 2714 } 2715 2716 return $out; 2717 } 2718 2719 /** 2720 * Returns the serverload of the system. 2721 * 2722 * @return int The serverload of the system. 2723 */ 2724 function get_server_load() 2725 { 2726 global $mybb, $lang; 2727 2728 $serverload = array(); 2729 2730 // DIRECTORY_SEPARATOR checks if running windows 2731 if(DIRECTORY_SEPARATOR != '\\') 2732 { 2733 if(function_exists("sys_getloadavg")) 2734 { 2735 // sys_getloadavg() will return an array with [0] being load within the last minute. 2736 $serverload = sys_getloadavg(); 2737 2738 if(!is_array($serverload)) 2739 { 2740 return $lang->unknown; 2741 } 2742 2743 $serverload[0] = round($serverload[0], 4); 2744 } 2745 else if(@file_exists("/proc/loadavg") && $load = @file_get_contents("/proc/loadavg")) 2746 { 2747 $serverload = explode(" ", $load); 2748 $serverload[0] = round($serverload[0], 4); 2749 } 2750 if(!is_numeric($serverload[0])) 2751 { 2752 if($mybb->safemode) 2753 { 2754 return $lang->unknown; 2755 } 2756 2757 // Suhosin likes to throw a warning if exec is disabled then die - weird 2758 if($func_blacklist = @ini_get('suhosin.executor.func.blacklist')) 2759 { 2760 if(strpos(",".$func_blacklist.",", 'exec') !== false) 2761 { 2762 return $lang->unknown; 2763 } 2764 } 2765 // PHP disabled functions? 2766 if($func_blacklist = @ini_get('disable_functions')) 2767 { 2768 if(strpos(",".$func_blacklist.",", 'exec') !== false) 2769 { 2770 return $lang->unknown; 2771 } 2772 } 2773 2774 $load = @exec("uptime"); 2775 $load = explode("load average: ", $load); 2776 $serverload = explode(",", $load[1]); 2777 if(!is_array($serverload)) 2778 { 2779 return $lang->unknown; 2780 } 2781 } 2782 } 2783 else 2784 { 2785 return $lang->unknown; 2786 } 2787 2788 $returnload = trim($serverload[0]); 2789 2790 return $returnload; 2791 } 2792 2793 /** 2794 * Returns the amount of memory allocated to the script. 2795 * 2796 * @return int The amount of memory allocated to the script. 2797 */ 2798 function get_memory_usage() 2799 { 2800 if(function_exists('memory_get_peak_usage')) 2801 { 2802 return memory_get_peak_usage(true); 2803 } 2804 elseif(function_exists('memory_get_usage')) 2805 { 2806 return memory_get_usage(true); 2807 } 2808 return false; 2809 } 2810 2811 /** 2812 * Updates the forum statistics with specific values (or addition/subtraction of the previous value) 2813 * 2814 * @param array $changes Array of items being updated (numthreads,numposts,numusers,numunapprovedthreads,numunapprovedposts,numdeletedposts,numdeletedthreads) 2815 * @param boolean $force Force stats update? 2816 */ 2817 function update_stats($changes=array(), $force=false) 2818 { 2819 global $cache, $db; 2820 static $stats_changes; 2821 2822 if(empty($stats_changes)) 2823 { 2824 // Update stats after all changes are done 2825 add_shutdown('update_stats', array(array(), true)); 2826 } 2827 2828 if(empty($stats_changes) || $stats_changes['inserted']) 2829 { 2830 $stats_changes = array( 2831 'numthreads' => '+0', 2832 'numposts' => '+0', 2833 'numusers' => '+0', 2834 'numunapprovedthreads' => '+0', 2835 'numunapprovedposts' => '+0', 2836 'numdeletedposts' => '+0', 2837 'numdeletedthreads' => '+0', 2838 'inserted' => false // Reset after changes are inserted into cache 2839 ); 2840 $stats = $stats_changes; 2841 } 2842 2843 if($force) // Force writing to cache? 2844 { 2845 if(!empty($changes)) 2846 { 2847 // Calculate before writing to cache 2848 update_stats($changes); 2849 } 2850 $stats = $cache->read("stats"); 2851 $changes = $stats_changes; 2852 } 2853 else 2854 { 2855 $stats = $stats_changes; 2856 } 2857 2858 $new_stats = array(); 2859 $counters = array('numthreads', 'numunapprovedthreads', 'numposts', 'numunapprovedposts', 'numusers', 'numdeletedposts', 'numdeletedthreads'); 2860 foreach($counters as $counter) 2861 { 2862 if(array_key_exists($counter, $changes)) 2863 { 2864 if(substr($changes[$counter], 0, 2) == "+-") 2865 { 2866 $changes[$counter] = substr($changes[$counter], 1); 2867 } 2868 // Adding or subtracting from previous value? 2869 if(substr($changes[$counter], 0, 1) == "+" || substr($changes[$counter], 0, 1) == "-") 2870 { 2871 if((int)$changes[$counter] != 0) 2872 { 2873 $new_stats[$counter] = $stats[$counter] + $changes[$counter]; 2874 if(!$force && (substr($stats[$counter], 0, 1) == "+" || substr($stats[$counter], 0, 1) == "-")) 2875 { 2876 // We had relative values? Then it is still relative 2877 if($new_stats[$counter] >= 0) 2878 { 2879 $new_stats[$counter] = "+{$new_stats[$counter]}"; 2880 } 2881 } 2882 // Less than 0? That's bad 2883 elseif($new_stats[$counter] < 0) 2884 { 2885 $new_stats[$counter] = 0; 2886 } 2887 } 2888 } 2889 else 2890 { 2891 $new_stats[$counter] = $changes[$counter]; 2892 // Less than 0? That's bad 2893 if($new_stats[$counter] < 0) 2894 { 2895 $new_stats[$counter] = 0; 2896 } 2897 } 2898 } 2899 } 2900 2901 if(!$force) 2902 { 2903 $stats_changes = array_merge($stats, $new_stats); // Overwrite changed values 2904 return; 2905 } 2906 2907 // Fetch latest user if the user count is changing 2908 if(array_key_exists('numusers', $changes)) 2909 { 2910 $query = $db->simple_select("users", "uid, username", "", array('order_by' => 'regdate', 'order_dir' => 'DESC', 'limit' => 1)); 2911 $lastmember = $db->fetch_array($query); 2912 $new_stats['lastuid'] = $lastmember['uid']; 2913 $new_stats['lastusername'] = $lastmember['username'] = htmlspecialchars_uni($lastmember['username']); 2914 } 2915 2916 if(!empty($new_stats)) 2917 { 2918 if(is_array($stats)) 2919 { 2920 $stats = array_merge($stats, $new_stats); // Overwrite changed values 2921 } 2922 else 2923 { 2924 $stats = $new_stats; 2925 } 2926 } 2927 2928 // Update stats row for today in the database 2929 $todays_stats = array( 2930 "dateline" => mktime(0, 0, 0, date("m"), date("j"), date("Y")), 2931 "numusers" => (int)$stats['numusers'], 2932 "numthreads" => (int)$stats['numthreads'], 2933 "numposts" => (int)$stats['numposts'] 2934 ); 2935 $db->replace_query("stats", $todays_stats, "dateline"); 2936 2937 $cache->update("stats", $stats, "dateline"); 2938 $stats_changes['inserted'] = true; 2939 } 2940 2941 /** 2942 * Updates the forum counters with a specific value (or addition/subtraction of the previous value) 2943 * 2944 * @param int $fid The forum ID 2945 * @param array $changes Array of items being updated (threads, posts, unapprovedthreads, unapprovedposts, deletedposts, deletedthreads) and their value (ex, 1, +1, -1) 2946 */ 2947 function update_forum_counters($fid, $changes=array()) 2948 { 2949 global $db; 2950 2951 $update_query = array(); 2952 2953 $counters = array('threads', 'unapprovedthreads', 'posts', 'unapprovedposts', 'deletedposts', 'deletedthreads'); 2954 2955 // Fetch above counters for this forum 2956 $query = $db->simple_select("forums", implode(",", $counters), "fid='{$fid}'"); 2957 $forum = $db->fetch_array($query); 2958 2959 foreach($counters as $counter) 2960 { 2961 if(array_key_exists($counter, $changes)) 2962 { 2963 if(substr($changes[$counter], 0, 2) == "+-") 2964 { 2965 $changes[$counter] = substr($changes[$counter], 1); 2966 } 2967 // Adding or subtracting from previous value? 2968 if(substr($changes[$counter], 0, 1) == "+" || substr($changes[$counter], 0, 1) == "-") 2969 { 2970 if((int)$changes[$counter] != 0) 2971 { 2972 $update_query[$counter] = $forum[$counter] + $changes[$counter]; 2973 } 2974 } 2975 else 2976 { 2977 $update_query[$counter] = $changes[$counter]; 2978 } 2979 2980 // Less than 0? That's bad 2981 if(isset($update_query[$counter]) && $update_query[$counter] < 0) 2982 { 2983 $update_query[$counter] = 0; 2984 } 2985 } 2986 } 2987 2988 // Only update if we're actually doing something 2989 if(count($update_query) > 0) 2990 { 2991 $db->update_query("forums", $update_query, "fid='".(int)$fid."'"); 2992 } 2993 2994 // Guess we should update the statistics too? 2995 $new_stats = array(); 2996 if(array_key_exists('threads', $update_query)) 2997 { 2998 $threads_diff = $update_query['threads'] - $forum['threads']; 2999 if($threads_diff > -1) 3000 { 3001 $new_stats['numthreads'] = "+{$threads_diff}"; 3002 } 3003 else 3004 { 3005 $new_stats['numthreads'] = "{$threads_diff}"; 3006 } 3007 } 3008 3009 if(array_key_exists('unapprovedthreads', $update_query)) 3010 { 3011 $unapprovedthreads_diff = $update_query['unapprovedthreads'] - $forum['unapprovedthreads']; 3012 if($unapprovedthreads_diff > -1) 3013 { 3014 $new_stats['numunapprovedthreads'] = "+{$unapprovedthreads_diff}"; 3015 } 3016 else 3017 { 3018 $new_stats['numunapprovedthreads'] = "{$unapprovedthreads_diff}"; 3019 } 3020 } 3021 3022 if(array_key_exists('posts', $update_query)) 3023 { 3024 $posts_diff = $update_query['posts'] - $forum['posts']; 3025 if($posts_diff > -1) 3026 { 3027 $new_stats['numposts'] = "+{$posts_diff}"; 3028 } 3029 else 3030 { 3031 $new_stats['numposts'] = "{$posts_diff}"; 3032 } 3033 } 3034 3035 if(array_key_exists('unapprovedposts', $update_query)) 3036 { 3037 $unapprovedposts_diff = $update_query['unapprovedposts'] - $forum['unapprovedposts']; 3038 if($unapprovedposts_diff > -1) 3039 { 3040 $new_stats['numunapprovedposts'] = "+{$unapprovedposts_diff}"; 3041 } 3042 else 3043 { 3044 $new_stats['numunapprovedposts'] = "{$unapprovedposts_diff}"; 3045 } 3046 } 3047 3048 if(array_key_exists('deletedposts', $update_query)) 3049 { 3050 $deletedposts_diff = $update_query['deletedposts'] - $forum['deletedposts']; 3051 if($deletedposts_diff > -1) 3052 { 3053 $new_stats['numdeletedposts'] = "+{$deletedposts_diff}"; 3054 } 3055 else 3056 { 3057 $new_stats['numdeletedposts'] = "{$deletedposts_diff}"; 3058 } 3059 } 3060 3061 if(array_key_exists('deletedthreads', $update_query)) 3062 { 3063 $deletedthreads_diff = $update_query['deletedthreads'] - $forum['deletedthreads']; 3064 if($deletedthreads_diff > -1) 3065 { 3066 $new_stats['numdeletedthreads'] = "+{$deletedthreads_diff}"; 3067 } 3068 else 3069 { 3070 $new_stats['numdeletedthreads'] = "{$deletedthreads_diff}"; 3071 } 3072 } 3073 3074 if(!empty($new_stats)) 3075 { 3076 update_stats($new_stats); 3077 } 3078 } 3079 3080 /** 3081 * Update the last post information for a specific forum 3082 * 3083 * @param int $fid The forum ID 3084 */ 3085 function update_forum_lastpost($fid) 3086 { 3087 global $db; 3088 3089 // Fetch the last post for this forum 3090 $query = $db->query(" 3091 SELECT tid, lastpost, lastposter, lastposteruid, subject 3092 FROM ".TABLE_PREFIX."threads 3093 WHERE fid='{$fid}' AND visible='1' AND closed NOT LIKE 'moved|%' 3094 ORDER BY lastpost DESC 3095 LIMIT 0, 1 3096 "); 3097 3098 if($db->num_rows($query) > 0) 3099 { 3100 $lastpost = $db->fetch_array($query); 3101 3102 $updated_forum = array( 3103 "lastpost" => (int)$lastpost['lastpost'], 3104 "lastposter" => $db->escape_string($lastpost['lastposter']), 3105 "lastposteruid" => (int)$lastpost['lastposteruid'], 3106 "lastposttid" => (int)$lastpost['tid'], 3107 "lastpostsubject" => $db->escape_string($lastpost['subject']), 3108 ); 3109 } 3110 else { 3111 $updated_forum = array( 3112 "lastpost" => 0, 3113 "lastposter" => '', 3114 "lastposteruid" => 0, 3115 "lastposttid" => 0, 3116 "lastpostsubject" => '', 3117 ); 3118 } 3119 3120 $db->update_query("forums", $updated_forum, "fid='{$fid}'"); 3121 } 3122 3123 /** 3124 * Updates the thread counters with a specific value (or addition/subtraction of the previous value) 3125 * 3126 * @param int $tid The thread ID 3127 * @param array $changes Array of items being updated (replies, unapprovedposts, deletedposts, attachmentcount) and their value (ex, 1, +1, -1) 3128 */ 3129 function update_thread_counters($tid, $changes=array()) 3130 { 3131 global $db; 3132 3133 $update_query = array(); 3134 $tid = (int)$tid; 3135 3136 $counters = array('replies', 'unapprovedposts', 'attachmentcount', 'deletedposts', 'attachmentcount'); 3137 3138 // Fetch above counters for this thread 3139 $query = $db->simple_select("threads", implode(",", $counters), "tid='{$tid}'"); 3140 $thread = $db->fetch_array($query); 3141 3142 foreach($counters as $counter) 3143 { 3144 if(array_key_exists($counter, $changes)) 3145 { 3146 if(substr($changes[$counter], 0, 2) == "+-") 3147 { 3148 $changes[$counter] = substr($changes[$counter], 1); 3149 } 3150 // Adding or subtracting from previous value? 3151 if(substr($changes[$counter], 0, 1) == "+" || substr($changes[$counter], 0, 1) == "-") 3152 { 3153 if((int)$changes[$counter] != 0) 3154 { 3155 $update_query[$counter] = $thread[$counter] + $changes[$counter]; 3156 } 3157 } 3158 else 3159 { 3160 $update_query[$counter] = $changes[$counter]; 3161 } 3162 3163 // Less than 0? That's bad 3164 if(isset($update_query[$counter]) && $update_query[$counter] < 0) 3165 { 3166 $update_query[$counter] = 0; 3167 } 3168 } 3169 } 3170 3171 $db->free_result($query); 3172 3173 // Only update if we're actually doing something 3174 if(count($update_query) > 0) 3175 { 3176 $db->update_query("threads", $update_query, "tid='{$tid}'"); 3177 } 3178 } 3179 3180 /** 3181 * Update the first post and lastpost data for a specific thread 3182 * 3183 * @param int $tid The thread ID 3184 */ 3185 function update_thread_data($tid) 3186 { 3187 global $db; 3188 3189 $thread = get_thread($tid); 3190 3191 // If this is a moved thread marker, don't update it - we need it to stay as it is 3192 if(strpos($thread['closed'], 'moved|') !== false) 3193 { 3194 return; 3195 } 3196 3197 $query = $db->query(" 3198 SELECT u.uid, u.username, p.username AS postusername, p.dateline 3199 FROM ".TABLE_PREFIX."posts p 3200 LEFT JOIN ".TABLE_PREFIX."users u ON (u.uid=p.uid) 3201 WHERE p.tid='$tid' AND p.visible='1' 3202 ORDER BY p.dateline DESC, p.pid DESC 3203 LIMIT 1" 3204 ); 3205 $lastpost = $db->fetch_array($query); 3206 3207 $db->free_result($query); 3208 3209 $query = $db->query(" 3210 SELECT u.uid, u.username, p.pid, p.username AS postusername, p.dateline 3211 FROM ".TABLE_PREFIX."posts p 3212 LEFT JOIN ".TABLE_PREFIX."users u ON (u.uid=p.uid) 3213 WHERE p.tid='$tid' 3214 ORDER BY p.dateline ASC, p.pid ASC 3215 LIMIT 1 3216 "); 3217 $firstpost = $db->fetch_array($query); 3218 3219 $db->free_result($query); 3220 3221 if(empty($firstpost['username'])) 3222 { 3223 $firstpost['username'] = $firstpost['postusername']; 3224 } 3225 3226 if(empty($lastpost['username'])) 3227 { 3228 $lastpost['username'] = $lastpost['postusername']; 3229 } 3230 3231 if(empty($lastpost['dateline'])) 3232 { 3233 $lastpost['username'] = $firstpost['username']; 3234 $lastpost['uid'] = $firstpost['uid']; 3235 $lastpost['dateline'] = $firstpost['dateline']; 3236 } 3237 3238 $lastpost['username'] = $db->escape_string($lastpost['username']); 3239 $firstpost['username'] = $db->escape_string($firstpost['username']); 3240 3241 $update_array = array( 3242 'firstpost' => (int)$firstpost['pid'], 3243 'username' => $firstpost['username'], 3244 'uid' => (int)$firstpost['uid'], 3245 'dateline' => (int)$firstpost['dateline'], 3246 'lastpost' => (int)$lastpost['dateline'], 3247 'lastposter' => $lastpost['username'], 3248 'lastposteruid' => (int)$lastpost['uid'], 3249 ); 3250 $db->update_query("threads", $update_array, "tid='{$tid}'"); 3251 } 3252 3253 /** 3254 * Updates the user counters with a specific value (or addition/subtraction of the previous value) 3255 * 3256 * @param int $uid The user ID 3257 * @param array $changes Array of items being updated (postnum, threadnum) and their value (ex, 1, +1, -1) 3258 */ 3259 function update_user_counters($uid, $changes=array()) 3260 { 3261 global $db; 3262 3263 $update_query = array(); 3264 3265 $counters = array('postnum', 'threadnum'); 3266 $uid = (int)$uid; 3267 3268 // Fetch above counters for this user 3269 $query = $db->simple_select("users", implode(",", $counters), "uid='{$uid}'"); 3270 $user = $db->fetch_array($query); 3271 3272 if($user) 3273 { 3274 foreach($counters as $counter) 3275 { 3276 if(array_key_exists($counter, $changes)) 3277 { 3278 if(substr($changes[$counter], 0, 2) == "+-") 3279 { 3280 $changes[$counter] = substr($changes[$counter], 1); 3281 } 3282 // Adding or subtracting from previous value? 3283 if(substr($changes[$counter], 0, 1) == "+" || substr($changes[$counter], 0, 1) == "-") 3284 { 3285 if((int)$changes[$counter] != 0) 3286 { 3287 $update_query[$counter] = $user[$counter] + $changes[$counter]; 3288 } 3289 } 3290 else 3291 { 3292 $update_query[$counter] = $changes[$counter]; 3293 } 3294 3295 // Less than 0? That's bad 3296 if(isset($update_query[$counter]) && $update_query[$counter] < 0) 3297 { 3298 $update_query[$counter] = 0; 3299 } 3300 } 3301 } 3302 } 3303 3304 $db->free_result($query); 3305 3306 // Only update if we're actually doing something 3307 if(count($update_query) > 0) 3308 { 3309 $db->update_query("users", $update_query, "uid='{$uid}'"); 3310 } 3311 } 3312 3313 /** 3314 * Deletes a thread from the database 3315 * 3316 * @param int $tid The thread ID 3317 * @return bool 3318 */ 3319 function delete_thread($tid) 3320 { 3321 global $moderation; 3322 3323 if(!is_object($moderation)) 3324 { 3325 require_once MYBB_ROOT."inc/class_moderation.php"; 3326 $moderation = new Moderation; 3327 } 3328 3329 return $moderation->delete_thread($tid); 3330 } 3331 3332 /** 3333 * Deletes a post from the database 3334 * 3335 * @param int $pid The thread ID 3336 * @return bool 3337 */ 3338 function delete_post($pid) 3339 { 3340 global $moderation; 3341 3342 if(!is_object($moderation)) 3343 { 3344 require_once MYBB_ROOT."inc/class_moderation.php"; 3345 $moderation = new Moderation; 3346 } 3347 3348 return $moderation->delete_post($pid); 3349 } 3350 3351 /** 3352 * Builds a forum jump menu 3353 * 3354 * @param int $pid The parent forum to start with 3355 * @param int $selitem The selected item ID 3356 * @param int $addselect If we need to add select boxes to this cal or not 3357 * @param string $depth The current depth of forums we're at 3358 * @param int $showextras Whether or not to show extra items such as User CP, Forum home 3359 * @param boolean $showall Ignore the showinjump setting and show all forums (for moderation pages) 3360 * @param mixed $permissions deprecated 3361 * @param string $name The name of the forum jump 3362 * @return string Forum jump items 3363 */ 3364 function build_forum_jump($pid=0, $selitem=0, $addselect=1, $depth="", $showextras=1, $showall=false, $permissions="", $name="fid") 3365 { 3366 global $forum_cache, $jumpfcache, $permissioncache, $mybb, $forumjump, $forumjumpbits, $gobutton, $theme, $templates, $lang; 3367 3368 $pid = (int)$pid; 3369 3370 if(!is_array($jumpfcache)) 3371 { 3372 if(!is_array($forum_cache)) 3373 { 3374 cache_forums(); 3375 } 3376 3377 foreach($forum_cache as $fid => $forum) 3378 { 3379 if($forum['active'] != 0) 3380 { 3381 $jumpfcache[$forum['pid']][$forum['disporder']][$forum['fid']] = $forum; 3382 } 3383 } 3384 } 3385 3386 if(!is_array($permissioncache)) 3387 { 3388 $permissioncache = forum_permissions(); 3389 } 3390 3391 if(isset($jumpfcache[$pid]) && is_array($jumpfcache[$pid])) 3392 { 3393 foreach($jumpfcache[$pid] as $main) 3394 { 3395 foreach($main as $forum) 3396 { 3397 $perms = $permissioncache[$forum['fid']]; 3398 3399 if($forum['fid'] != "0" && ($perms['canview'] != 0 || $mybb->settings['hideprivateforums'] == 0) && $forum['linkto'] == '' && ($forum['showinjump'] != 0 || $showall == true)) 3400 { 3401 $optionselected = ""; 3402 3403 if($selitem == $forum['fid']) 3404 { 3405 $optionselected = 'selected="selected"'; 3406 } 3407 3408 $forum['name'] = htmlspecialchars_uni(strip_tags($forum['name'])); 3409 3410 eval("\$forumjumpbits .= \"".$templates->get("forumjump_bit")."\";"); 3411 3412 if($forum_cache[$forum['fid']]) 3413 { 3414 $newdepth = $depth."--"; 3415 $forumjumpbits .= build_forum_jump($forum['fid'], $selitem, 0, $newdepth, $showextras, $showall); 3416 } 3417 } 3418 } 3419 } 3420 } 3421 3422 if($addselect) 3423 { 3424 if($showextras == 0) 3425 { 3426 $template = "special"; 3427 } 3428 else 3429 { 3430 $template = "advanced"; 3431 3432 if(strpos(FORUM_URL, '.html') !== false) 3433 { 3434 $forum_link = "'".str_replace('{fid}', "'+option+'", FORUM_URL)."'"; 3435 } 3436 else 3437 { 3438 $forum_link = "'".str_replace('{fid}', "'+option", FORUM_URL); 3439 } 3440 } 3441 3442 eval("\$forumjump = \"".$templates->get("forumjump_".$template)."\";"); 3443 } 3444 3445 return $forumjump; 3446 } 3447 3448 /** 3449 * Returns the extension of a file. 3450 * 3451 * @param string $file The filename. 3452 * @return string The extension of the file. 3453 */ 3454 function get_extension($file) 3455 { 3456 return my_strtolower(my_substr(strrchr($file, "."), 1)); 3457 } 3458 3459 /** 3460 * Generates a random string. 3461 * 3462 * @param int $length The length of the string to generate. 3463 * @param bool $complex Whether to return complex string. Defaults to false 3464 * @return string The random string. 3465 */ 3466 function random_str($length=8, $complex=false) 3467 { 3468 $set = array_merge(range(0, 9), range('A', 'Z'), range('a', 'z')); 3469 $str = array(); 3470 3471 // Complex strings have always at least 3 characters, even if $length < 3 3472 if($complex == true) 3473 { 3474 // At least one number 3475 $str[] = $set[my_rand(0, 9)]; 3476 3477 // At least one big letter 3478 $str[] = $set[my_rand(10, 35)]; 3479 3480 // At least one small letter 3481 $str[] = $set[my_rand(36, 61)]; 3482 3483 $length -= 3; 3484 } 3485 3486 for($i = 0; $i < $length; ++$i) 3487 { 3488 $str[] = $set[my_rand(0, 61)]; 3489 } 3490 3491 // Make sure they're in random order and convert them to a string 3492 shuffle($str); 3493 3494 return implode($str); 3495 } 3496 3497 /** 3498 * Formats a username based on their display group 3499 * 3500 * @param string $username The username 3501 * @param int $usergroup The usergroup for the user 3502 * @param int $displaygroup The display group for the user 3503 * @return string The formatted username 3504 */ 3505 function format_name($username, $usergroup, $displaygroup=0) 3506 { 3507 global $groupscache, $cache, $plugins; 3508 3509 static $formattednames = array(); 3510 3511 if(!isset($formattednames[$username])) 3512 { 3513 if(!is_array($groupscache)) 3514 { 3515 $groupscache = $cache->read("usergroups"); 3516 } 3517 3518 if($displaygroup != 0) 3519 { 3520 $usergroup = $displaygroup; 3521 } 3522 3523 $format = "{username}"; 3524 3525 if(isset($groupscache[$usergroup])) 3526 { 3527 $ugroup = $groupscache[$usergroup]; 3528 3529 if(strpos($ugroup['namestyle'], "{username}") !== false) 3530 { 3531 $format = $ugroup['namestyle']; 3532 } 3533 } 3534 3535 $format = stripslashes($format); 3536 3537 $parameters = compact('username', 'usergroup', 'displaygroup', 'format'); 3538 3539 $parameters = $plugins->run_hooks('format_name', $parameters); 3540 3541 $format = $parameters['format']; 3542 3543 $formattednames[$username] = str_replace("{username}", $username, $format); 3544 } 3545 3546 return $formattednames[$username]; 3547 } 3548 3549 /** 3550 * Formats an avatar to a certain dimension 3551 * 3552 * @param string $avatar The avatar file name 3553 * @param string $dimensions Dimensions of the avatar, width x height (e.g. 44|44) 3554 * @param string $max_dimensions The maximum dimensions of the formatted avatar 3555 * @return array Information for the formatted avatar 3556 */ 3557 function format_avatar($avatar, $dimensions = '', $max_dimensions = '') 3558 { 3559 global $mybb, $theme; 3560 static $avatars; 3561 3562 if(!isset($avatars)) 3563 { 3564 $avatars = array(); 3565 } 3566 3567 if(my_strpos($avatar, '://') !== false && !$mybb->settings['allowremoteavatars']) 3568 { 3569 // Remote avatar, but remote avatars are disallowed. 3570 $avatar = null; 3571 } 3572 3573 if(!$avatar) 3574 { 3575 // Default avatar 3576 if(defined('IN_ADMINCP')) 3577 { 3578 $theme['imgdir'] = '../images'; 3579 } 3580 3581 $avatar = str_replace('{theme}', $theme['imgdir'], $mybb->settings['useravatar']); 3582 $dimensions = $mybb->settings['useravatardims']; 3583 } 3584 3585 if(!$max_dimensions) 3586 { 3587 $max_dimensions = $mybb->settings['maxavatardims']; 3588 } 3589 3590 // An empty key wouldn't work so we need to add a fall back 3591 $key = $dimensions; 3592 if(empty($key)) 3593 { 3594 $key = 'default'; 3595 } 3596 $key2 = $max_dimensions; 3597 if(empty($key2)) 3598 { 3599 $key2 = 'default'; 3600 } 3601 3602 if(isset($avatars[$avatar][$key][$key2])) 3603 { 3604 return $avatars[$avatar][$key][$key2]; 3605 } 3606 3607 $avatar_width_height = ''; 3608 3609 if($dimensions) 3610 { 3611 $dimensions = preg_split('/[|x]/', $dimensions); 3612 3613 if($dimensions[0] && $dimensions[1]) 3614 { 3615 list($max_width, $max_height) = preg_split('/[|x]/', $max_dimensions); 3616 3617 if(!empty($max_dimensions) && ($dimensions[0] > $max_width || $dimensions[1] > $max_height)) 3618 { 3619 require_once MYBB_ROOT."inc/functions_image.php"; 3620 $scaled_dimensions = scale_image($dimensions[0], $dimensions[1], $max_width, $max_height); 3621 $avatar_width_height = "width=\"{$scaled_dimensions['width']}\" height=\"{$scaled_dimensions['height']}\""; 3622 } 3623 else 3624 { 3625 $avatar_width_height = "width=\"{$dimensions[0]}\" height=\"{$dimensions[1]}\""; 3626 } 3627 } 3628 } 3629 3630 $avatars[$avatar][$key][$key2] = array( 3631 'image' => htmlspecialchars_uni($mybb->get_asset_url($avatar)), 3632 'width_height' => $avatar_width_height 3633 ); 3634 3635 return $avatars[$avatar][$key][$key2]; 3636 } 3637 3638 /** 3639 * Build the javascript based MyCode inserter. 3640 * 3641 * @param string $bind The ID of the textarea to bind to. Defaults to "message". 3642 * @param bool $smilies Whether to include smilies. Defaults to true. 3643 * 3644 * @return string The MyCode inserter 3645 */ 3646 function build_mycode_inserter($bind="message", $smilies = true) 3647 { 3648 global $db, $mybb, $theme, $templates, $lang, $plugins, $smiliecache, $cache; 3649 3650 if($mybb->settings['bbcodeinserter'] != 0) 3651 { 3652 $editor_lang_strings = array( 3653 "editor_bold" => "Bold", 3654 "editor_italic" => "Italic", 3655 "editor_underline" => "Underline", 3656 "editor_strikethrough" => "Strikethrough", 3657 "editor_subscript" => "Subscript", 3658 "editor_superscript" => "Superscript", 3659 "editor_alignleft" => "Align left", 3660 "editor_center" => "Center", 3661 "editor_alignright" => "Align right", 3662 "editor_justify" => "Justify", 3663 "editor_fontname" => "Font Name", 3664 "editor_fontsize" => "Font Size", 3665 "editor_fontcolor" => "Font Color", 3666 "editor_removeformatting" => "Remove Formatting", 3667 "editor_cut" => "Cut", 3668 "editor_cutnosupport" => "Your browser does not allow the cut command. Please use the keyboard shortcut Ctrl/Cmd-X", 3669 "editor_copy" => "Copy", 3670 "editor_copynosupport" => "Your browser does not allow the copy command. Please use the keyboard shortcut Ctrl/Cmd-C", 3671 "editor_paste" => "Paste", 3672 "editor_pastenosupport" => "Your browser does not allow the paste command. Please use the keyboard shortcut Ctrl/Cmd-V", 3673 "editor_pasteentertext" => "Paste your text inside the following box:", 3674 "editor_pastetext" => "PasteText", 3675 "editor_numlist" => "Numbered list", 3676 "editor_bullist" => "Bullet list", 3677 "editor_undo" => "Undo", 3678 "editor_redo" => "Redo", 3679 "editor_rows" => "Rows:", 3680 "editor_cols" => "Cols:", 3681 "editor_inserttable" => "Insert a table", 3682 "editor_inserthr" => "Insert a horizontal rule", 3683 "editor_code" => "Code", 3684 "editor_width" => "Width (optional):", 3685 "editor_height" => "Height (optional):", 3686 "editor_insertimg" => "Insert an image", 3687 "editor_email" => "E-mail:", 3688 "editor_insertemail" => "Insert an email", 3689 "editor_url" => "URL:", 3690 "editor_insertlink" => "Insert a link", 3691 "editor_unlink" => "Unlink", 3692 "editor_more" => "More", 3693 "editor_insertemoticon" => "Insert an emoticon", 3694 "editor_videourl" => "Video URL:", 3695 "editor_videotype" => "Video Type:", 3696 "editor_insert" => "Insert", 3697 "editor_insertyoutubevideo" => "Insert a YouTube video", 3698 "editor_currentdate" => "Insert current date", 3699 "editor_currenttime" => "Insert current time", 3700 "editor_print" => "Print", 3701 "editor_viewsource" => "View source", 3702 "editor_description" => "Description (optional):", 3703 "editor_enterimgurl" => "Enter the image URL:", 3704 "editor_enteremail" => "Enter the e-mail address:", 3705 "editor_enterdisplayedtext" => "Enter the displayed text:", 3706 "editor_enterurl" => "Enter URL:", 3707 "editor_enteryoutubeurl" => "Enter the YouTube video URL or ID:", 3708 "editor_insertquote" => "Insert a Quote", 3709 "editor_invalidyoutube" => "Invalid YouTube video", 3710 "editor_dailymotion" => "Dailymotion", 3711 "editor_metacafe" => "MetaCafe", 3712 "editor_mixer" => "Mixer", 3713 "editor_vimeo" => "Vimeo", 3714 "editor_youtube" => "Youtube", 3715 "editor_facebook" => "Facebook", 3716 "editor_liveleak" => "LiveLeak", 3717 "editor_insertvideo" => "Insert a video", 3718 "editor_php" => "PHP", 3719 "editor_maximize" => "Maximize" 3720 ); 3721 $editor_language = "(function ($) {\n$.sceditor.locale[\"mybblang\"] = {\n"; 3722 3723 $editor_lang_strings = $plugins->run_hooks("mycode_add_codebuttons", $editor_lang_strings); 3724 3725 $editor_languages_count = count($editor_lang_strings); 3726 $i = 0; 3727 foreach($editor_lang_strings as $lang_string => $key) 3728 { 3729 $i++; 3730 $js_lang_string = str_replace("\"", "\\\"", $key); 3731 $string = str_replace("\"", "\\\"", $lang->$lang_string); 3732 $editor_language .= "\t\"{$js_lang_string}\": \"{$string}\""; 3733 3734 if($i < $editor_languages_count) 3735 { 3736 $editor_language .= ","; 3737 } 3738 3739 $editor_language .= "\n"; 3740 } 3741 3742 $editor_language .= "}})(jQuery);"; 3743 3744 if(defined("IN_ADMINCP")) 3745 { 3746 global $page; 3747 $codeinsert = $page->build_codebuttons_editor($bind, $editor_language, $smilies); 3748 } 3749 else 3750 { 3751 // Smilies 3752 $emoticon = ""; 3753 $emoticons_enabled = "false"; 3754 if($smilies) 3755 { 3756 if(!$smiliecache) 3757 { 3758 if(!isset($smilie_cache) || !is_array($smilie_cache)) 3759 { 3760 $smilie_cache = $cache->read("smilies"); 3761 } 3762 foreach($smilie_cache as $smilie) 3763 { 3764 $smilie['image'] = str_replace("{theme}", $theme['imgdir'], $smilie['image']); 3765 $smiliecache[$smilie['sid']] = $smilie; 3766 } 3767 } 3768 3769 if($mybb->settings['smilieinserter'] && $mybb->settings['smilieinsertercols'] && $mybb->settings['smilieinsertertot'] && !empty($smiliecache)) 3770 { 3771 $emoticon = ",emoticon"; 3772 } 3773 $emoticons_enabled = "true"; 3774 3775 unset($smilie); 3776 3777 if(is_array($smiliecache)) 3778 { 3779 reset($smiliecache); 3780 3781 $dropdownsmilies = $moresmilies = $hiddensmilies = ""; 3782 $i = 0; 3783 3784 foreach($smiliecache as $smilie) 3785 { 3786 $finds = explode("\n", $smilie['find']); 3787 $finds_count = count($finds); 3788 3789 // Only show the first text to replace in the box 3790 $smilie['find'] = $finds[0]; 3791 3792 $find = str_replace(array('\\', '"'), array('\\\\', '\"'), htmlspecialchars_uni($smilie['find'])); 3793 $image = htmlspecialchars_uni($mybb->get_asset_url($smilie['image'])); 3794 $image = str_replace(array('\\', '"'), array('\\\\', '\"'), $image); 3795 3796 if(!$mybb->settings['smilieinserter'] || !$mybb->settings['smilieinsertercols'] || !$mybb->settings['smilieinsertertot'] || !$smilie['showclickable']) 3797 { 3798 $hiddensmilies .= '"'.$find.'": "'.$image.'",'; 3799 } 3800 elseif($i < $mybb->settings['smilieinsertertot']) 3801 { 3802 $dropdownsmilies .= '"'.$find.'": "'.$image.'",'; 3803 ++$i; 3804 } 3805 else 3806 { 3807 $moresmilies .= '"'.$find.'": "'.$image.'",'; 3808 } 3809 3810 for($j = 1; $j < $finds_count; ++$j) 3811 { 3812 $find = str_replace(array('\\', '"'), array('\\\\', '\"'), htmlspecialchars_uni($finds[$j])); 3813 $hiddensmilies .= '"'.$find.'": "'.$image.'",'; 3814 } 3815 } 3816 } 3817 } 3818 3819 $basic1 = $basic2 = $align = $font = $size = $color = $removeformat = $email = $link = $list = $code = $sourcemode = ""; 3820 3821 if($mybb->settings['allowbasicmycode'] == 1) 3822 { 3823 $basic1 = "bold,italic,underline,strike|"; 3824 $basic2 = "horizontalrule,"; 3825 } 3826 3827 if($mybb->settings['allowalignmycode'] == 1) 3828 { 3829 $align = "left,center,right,justify|"; 3830 } 3831 3832 if($mybb->settings['allowfontmycode'] == 1) 3833 { 3834 $font = "font,"; 3835 } 3836 3837 if($mybb->settings['allowsizemycode'] == 1) 3838 { 3839 $size = "size,"; 3840 } 3841 3842 if($mybb->settings['allowcolormycode'] == 1) 3843 { 3844 $color = "color,"; 3845 } 3846 3847 if($mybb->settings['allowfontmycode'] == 1 || $mybb->settings['allowsizemycode'] == 1 || $mybb->settings['allowcolormycode'] == 1) 3848 { 3849 $removeformat = "removeformat|"; 3850 } 3851 3852 if($mybb->settings['allowemailmycode'] == 1) 3853 { 3854 $email = "email,"; 3855 } 3856 3857 if($mybb->settings['allowlinkmycode'] == 1) 3858 { 3859 $link = "link,unlink"; 3860 } 3861 3862 if($mybb->settings['allowlistmycode'] == 1) 3863 { 3864 $list = "bulletlist,orderedlist|"; 3865 } 3866 3867 if($mybb->settings['allowcodemycode'] == 1) 3868 { 3869 $code = "code,php,"; 3870 } 3871 3872 if($mybb->user['sourceeditor'] == 1) 3873 { 3874 $sourcemode = "MyBBEditor.sourceMode(true);"; 3875 } 3876 3877 eval("\$codeinsert = \"".$templates->get("codebuttons")."\";"); 3878 } 3879 } 3880 3881 return $codeinsert; 3882 } 3883 3884 /** 3885 * @param int $tid 3886 * @param array $postoptions The options carried with form submit 3887 * 3888 * @return string Predefined / updated subscription method of the thread for the user 3889 */ 3890 function get_subscription_method($tid = 0, $postoptions = array()) 3891 { 3892 global $mybb; 3893 3894 $subscription_methods = array('', 'none', 'email', 'pm'); // Define methods 3895 $subscription_method = (int)$mybb->user['subscriptionmethod']; // Set user default 3896 3897 // If no user default method available then reset method 3898 if(!$subscription_method) 3899 { 3900 $subscription_method = 0; 3901 } 3902 3903 // Return user default if no thread id available, in case 3904 if(!(int)$tid || (int)$tid <= 0) 3905 { 3906 return $subscription_methods[$subscription_method]; 3907 } 3908 3909 // If method not predefined set using data from database 3910 if(isset($postoptions['subscriptionmethod'])) 3911 { 3912 $method = trim($postoptions['subscriptionmethod']); 3913 return (in_array($method, $subscription_methods)) ? $method : $subscription_methods[0]; 3914 } 3915 else 3916 { 3917 global $db; 3918 3919 $query = $db->simple_select("threadsubscriptions", "tid, notification", "tid='".(int)$tid."' AND uid='".$mybb->user['uid']."'", array('limit' => 1)); 3920 $subscription = $db->fetch_array($query); 3921 3922 if($subscription) 3923 { 3924 $subscription_method = (int)$subscription['notification'] + 1; 3925 } 3926 } 3927 3928 return $subscription_methods[$subscription_method]; 3929 } 3930 3931 /** 3932 * Build the javascript clickable smilie inserter 3933 * 3934 * @return string The clickable smilies list 3935 */ 3936 function build_clickable_smilies() 3937 { 3938 global $cache, $smiliecache, $theme, $templates, $lang, $mybb, $smiliecount; 3939 3940 if($mybb->settings['smilieinserter'] != 0 && $mybb->settings['smilieinsertercols'] && $mybb->settings['smilieinsertertot']) 3941 { 3942 if(!$smiliecount) 3943 { 3944 $smilie_cache = $cache->read("smilies"); 3945 $smiliecount = count($smilie_cache); 3946 } 3947 3948 if(!$smiliecache) 3949 { 3950 if(!is_array($smilie_cache)) 3951 { 3952 $smilie_cache = $cache->read("smilies"); 3953 } 3954 foreach($smilie_cache as $smilie) 3955 { 3956 $smilie['image'] = str_replace("{theme}", $theme['imgdir'], $smilie['image']); 3957 $smiliecache[$smilie['sid']] = $smilie; 3958 } 3959 } 3960 3961 unset($smilie); 3962 3963 if(is_array($smiliecache)) 3964 { 3965 reset($smiliecache); 3966 3967 $getmore = ''; 3968 if($mybb->settings['smilieinsertertot'] >= $smiliecount) 3969 { 3970 $mybb->settings['smilieinsertertot'] = $smiliecount; 3971 } 3972 else if($mybb->settings['smilieinsertertot'] < $smiliecount) 3973 { 3974 $smiliecount = $mybb->settings['smilieinsertertot']; 3975 eval("\$getmore = \"".$templates->get("smilieinsert_getmore")."\";"); 3976 } 3977 3978 $smilies = $smilie_icons = ''; 3979 $counter = 0; 3980 $i = 0; 3981 3982 $extra_class = ''; 3983 foreach($smiliecache as $smilie) 3984 { 3985 if($i < $mybb->settings['smilieinsertertot'] && $smilie['showclickable'] != 0) 3986 { 3987 $smilie['image'] = str_replace("{theme}", $theme['imgdir'], $smilie['image']); 3988 $smilie['image'] = htmlspecialchars_uni($mybb->get_asset_url($smilie['image'])); 3989 $smilie['name'] = htmlspecialchars_uni($smilie['name']); 3990 3991 // Only show the first text to replace in the box 3992 $temp = explode("\n", $smilie['find']); // assign to temporary variable for php 5.3 compatibility 3993 $smilie['find'] = $temp[0]; 3994 3995 $find = str_replace(array('\\', "'"), array('\\\\', "\'"), htmlspecialchars_uni($smilie['find'])); 3996 3997 $onclick = " onclick=\"MyBBEditor.insertText(' $find ');\""; 3998 $extra_class = ' smilie_pointer'; 3999 eval('$smilie = "'.$templates->get('smilie', 1, 0).'";'); 4000 eval("\$smilie_icons .= \"".$templates->get("smilieinsert_smilie")."\";"); 4001 ++$i; 4002 ++$counter; 4003 4004 if($counter == $mybb->settings['smilieinsertercols']) 4005 { 4006 $counter = 0; 4007 eval("\$smilies .= \"".$templates->get("smilieinsert_row")."\";"); 4008 $smilie_icons = ''; 4009 } 4010 } 4011 } 4012 4013 if($counter != 0) 4014 { 4015 $colspan = $mybb->settings['smilieinsertercols'] - $counter; 4016 eval("\$smilies .= \"".$templates->get("smilieinsert_row_empty")."\";"); 4017 } 4018 4019 eval("\$clickablesmilies = \"".$templates->get("smilieinsert")."\";"); 4020 } 4021 else 4022 { 4023 $clickablesmilies = ""; 4024 } 4025 } 4026 else 4027 { 4028 $clickablesmilies = ""; 4029 } 4030 4031 return $clickablesmilies; 4032 } 4033 4034 /** 4035 * Builds thread prefixes and returns a selected prefix (or all) 4036 * 4037 * @param int $pid The prefix ID (0 to return all) 4038 * @return array The thread prefix's values (or all thread prefixes) 4039 */ 4040 function build_prefixes($pid=0) 4041 { 4042 global $cache; 4043 static $prefixes_cache; 4044 4045 if(is_array($prefixes_cache)) 4046 { 4047 if($pid > 0 && is_array($prefixes_cache[$pid])) 4048 { 4049 return $prefixes_cache[$pid]; 4050 } 4051 4052 return $prefixes_cache; 4053 } 4054 4055 $prefix_cache = $cache->read("threadprefixes"); 4056 4057 if(!is_array($prefix_cache)) 4058 { 4059 // No cache 4060 $prefix_cache = $cache->read("threadprefixes", true); 4061 4062 if(!is_array($prefix_cache)) 4063 { 4064 return array(); 4065 } 4066 } 4067 4068 $prefixes_cache = array(); 4069 foreach($prefix_cache as $prefix) 4070 { 4071 $prefixes_cache[$prefix['pid']] = $prefix; 4072 } 4073 4074 if($pid != 0 && is_array($prefixes_cache[$pid])) 4075 { 4076 return $prefixes_cache[$pid]; 4077 } 4078 else if(!empty($prefixes_cache)) 4079 { 4080 return $prefixes_cache; 4081 } 4082 4083 return false; 4084 } 4085 4086 /** 4087 * Build the thread prefix selection menu for the current user 4088 * 4089 * @param int|string $fid The forum ID (integer ID or string all) 4090 * @param int|string $selected_pid The selected prefix ID (integer ID or string any) 4091 * @param int $multiple Allow multiple prefix selection 4092 * @param int $previous_pid The previously selected prefix ID 4093 * @return string The thread prefix selection menu 4094 */ 4095 function build_prefix_select($fid, $selected_pid=0, $multiple=0, $previous_pid=0) 4096 { 4097 global $cache, $db, $lang, $mybb, $templates; 4098 4099 if($fid != 'all') 4100 { 4101 $fid = (int)$fid; 4102 } 4103 4104 $prefix_cache = build_prefixes(0); 4105 if(empty($prefix_cache)) 4106 { 4107 // We've got no prefixes to show 4108 return ''; 4109 } 4110 4111 // Go through each of our prefixes and decide which ones we can use 4112 $prefixes = array(); 4113 foreach($prefix_cache as $prefix) 4114 { 4115 if($fid != "all" && $prefix['forums'] != "-1") 4116 { 4117 // Decide whether this prefix can be used in our forum 4118 $forums = explode(",", $prefix['forums']); 4119 4120 if(!in_array($fid, $forums) && $prefix['pid'] != $previous_pid) 4121 { 4122 // This prefix is not in our forum list 4123 continue; 4124 } 4125 } 4126 4127 if(is_member($prefix['groups']) || $prefix['pid'] == $previous_pid) 4128 { 4129 // The current user can use this prefix 4130 $prefixes[$prefix['pid']] = $prefix; 4131 } 4132 } 4133 4134 if(empty($prefixes)) 4135 { 4136 return ''; 4137 } 4138 4139 $prefixselect = $prefixselect_prefix = ''; 4140 4141 if($multiple == 1) 4142 { 4143 $any_selected = ""; 4144 if($selected_pid == 'any') 4145 { 4146 $any_selected = " selected=\"selected\""; 4147 } 4148 } 4149 4150 $default_selected = ""; 4151 if(((int)$selected_pid == 0) && $selected_pid != 'any') 4152 { 4153 $default_selected = " selected=\"selected\""; 4154 } 4155 4156 foreach($prefixes as $prefix) 4157 { 4158 $selected = ""; 4159 if($prefix['pid'] == $selected_pid) 4160 { 4161 $selected = " selected=\"selected\""; 4162 } 4163 4164 $prefix['prefix'] = htmlspecialchars_uni($prefix['prefix']); 4165 eval("\$prefixselect_prefix .= \"".$templates->get("post_prefixselect_prefix")."\";"); 4166 } 4167 4168 if($multiple != 0) 4169 { 4170 eval("\$prefixselect = \"".$templates->get("post_prefixselect_multiple")."\";"); 4171 } 4172 else 4173 { 4174 eval("\$prefixselect = \"".$templates->get("post_prefixselect_single")."\";"); 4175 } 4176 4177 return $prefixselect; 4178 } 4179 4180 /** 4181 * Build the thread prefix selection menu for a forum without group permission checks 4182 * 4183 * @param int $fid The forum ID (integer ID) 4184 * @param int $selected_pid The selected prefix ID (integer ID) 4185 * @return string The thread prefix selection menu 4186 */ 4187 function build_forum_prefix_select($fid, $selected_pid=0) 4188 { 4189 global $cache, $db, $lang, $mybb, $templates; 4190 4191 $fid = (int)$fid; 4192 4193 $prefix_cache = build_prefixes(0); 4194 if(empty($prefix_cache)) 4195 { 4196 // We've got no prefixes to show 4197 return ''; 4198 } 4199 4200 // Go through each of our prefixes and decide which ones we can use 4201 $prefixes = array(); 4202 foreach($prefix_cache as $prefix) 4203 { 4204 if($prefix['forums'] != "-1") 4205 { 4206 // Decide whether this prefix can be used in our forum 4207 $forums = explode(",", $prefix['forums']); 4208 4209 if(in_array($fid, $forums)) 4210 { 4211 // This forum can use this prefix! 4212 $prefixes[$prefix['pid']] = $prefix; 4213 } 4214 } 4215 else 4216 { 4217 // This prefix is for anybody to use... 4218 $prefixes[$prefix['pid']] = $prefix; 4219 } 4220 } 4221 4222 if(empty($prefixes)) 4223 { 4224 return ''; 4225 } 4226 4227 $default_selected = array('all' => '', 'none' => '', 'any' => ''); 4228 $selected_pid = (int)$selected_pid; 4229 4230 if($selected_pid == 0) 4231 { 4232 $default_selected['all'] = ' selected="selected"'; 4233 } 4234 else if($selected_pid == -1) 4235 { 4236 $default_selected['none'] = ' selected="selected"'; 4237 } 4238 else if($selected_pid == -2) 4239 { 4240 $default_selected['any'] = ' selected="selected"'; 4241 } 4242 4243 $prefixselect_prefix = ''; 4244 foreach($prefixes as $prefix) 4245 { 4246 $selected = ''; 4247 if($prefix['pid'] == $selected_pid) 4248 { 4249 $selected = ' selected="selected"'; 4250 } 4251 4252 $prefix['prefix'] = htmlspecialchars_uni($prefix['prefix']); 4253 eval('$prefixselect_prefix .= "'.$templates->get("forumdisplay_threadlist_prefixes_prefix").'";'); 4254 } 4255 4256 eval('$prefixselect = "'.$templates->get("forumdisplay_threadlist_prefixes").'";'); 4257 return $prefixselect; 4258 } 4259 4260 /** 4261 * Gzip encodes text to a specified level 4262 * 4263 * @param string $contents The string to encode 4264 * @param int $level The level (1-9) to encode at 4265 * @return string The encoded string 4266 */ 4267 function gzip_encode($contents, $level=1) 4268 { 4269 if(function_exists("gzcompress") && function_exists("crc32") && !headers_sent() && !(ini_get('output_buffering') && my_strpos(' '.ini_get('output_handler'), 'ob_gzhandler'))) 4270 { 4271 $httpaccept_encoding = ''; 4272 4273 if(isset($_SERVER['HTTP_ACCEPT_ENCODING'])) 4274 { 4275 $httpaccept_encoding = $_SERVER['HTTP_ACCEPT_ENCODING']; 4276 } 4277 4278 if(my_strpos(" ".$httpaccept_encoding, "x-gzip")) 4279 { 4280 $encoding = "x-gzip"; 4281 } 4282 4283 if(my_strpos(" ".$httpaccept_encoding, "gzip")) 4284 { 4285 $encoding = "gzip"; 4286 } 4287 4288 if(isset($encoding)) 4289 { 4290 header("Content-Encoding: $encoding"); 4291 4292 if(function_exists("gzencode")) 4293 { 4294 $contents = gzencode($contents, $level); 4295 } 4296 else 4297 { 4298 $size = strlen($contents); 4299 $crc = crc32($contents); 4300 $gzdata = "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff"; 4301 $gzdata .= my_substr(gzcompress($contents, $level), 2, -4); 4302 $gzdata .= pack("V", $crc); 4303 $gzdata .= pack("V", $size); 4304 $contents = $gzdata; 4305 } 4306 } 4307 } 4308 4309 return $contents; 4310 } 4311 4312 /** 4313 * Log the actions of a moderator. 4314 * 4315 * @param array $data The data of the moderator's action. 4316 * @param string $action The message to enter for the action the moderator performed. 4317 */ 4318 function log_moderator_action($data, $action="") 4319 { 4320 global $mybb, $db, $session; 4321 4322 $fid = 0; 4323 if(isset($data['fid'])) 4324 { 4325 $fid = (int)$data['fid']; 4326 unset($data['fid']); 4327 } 4328 4329 $tid = 0; 4330 if(isset($data['tid'])) 4331 { 4332 $tid = (int)$data['tid']; 4333 unset($data['tid']); 4334 } 4335 4336 $pid = 0; 4337 if(isset($data['pid'])) 4338 { 4339 $pid = (int)$data['pid']; 4340 unset($data['pid']); 4341 } 4342 4343 $tids = array(); 4344 if(isset($data['tids'])) 4345 { 4346 $tids = (array)$data['tids']; 4347 unset($data['tids']); 4348 } 4349 4350 // Any remaining extra data - we my_serialize and insert in to its own column 4351 if(is_array($data)) 4352 { 4353 $data = my_serialize($data); 4354 } 4355 4356 $sql_array = array( 4357 "uid" => (int)$mybb->user['uid'], 4358 "dateline" => TIME_NOW, 4359 "fid" => (int)$fid, 4360 "tid" => $tid, 4361 "pid" => $pid, 4362 "action" => $db->escape_string($action), 4363 "data" => $db->escape_string($data), 4364 "ipaddress" => $db->escape_binary($session->packedip) 4365 ); 4366 4367 if($tids) 4368 { 4369 $multiple_sql_array = array(); 4370 4371 foreach($tids as $tid) 4372 { 4373 $sql_array['tid'] = (int)$tid; 4374 $multiple_sql_array[] = $sql_array; 4375 } 4376 4377 $db->insert_query_multiple("moderatorlog", $multiple_sql_array); 4378 } 4379 else 4380 { 4381 $db->insert_query("moderatorlog", $sql_array); 4382 } 4383 } 4384 4385 /** 4386 * Get the formatted reputation for a user. 4387 * 4388 * @param int $reputation The reputation value 4389 * @param int $uid The user ID (if not specified, the generated reputation will not be a link) 4390 * @return string The formatted repuation 4391 */ 4392 function get_reputation($reputation, $uid=0) 4393 { 4394 global $theme, $templates; 4395 4396 $display_reputation = $reputation_class = ''; 4397 if($reputation < 0) 4398 { 4399 $reputation_class = "reputation_negative"; 4400 } 4401 elseif($reputation > 0) 4402 { 4403 $reputation_class = "reputation_positive"; 4404 } 4405 else 4406 { 4407 $reputation_class = "reputation_neutral"; 4408 } 4409 4410 $reputation = my_number_format($reputation); 4411 4412 if($uid != 0) 4413 { 4414 eval("\$display_reputation = \"".$templates->get("postbit_reputation_formatted_link")."\";"); 4415 } 4416 else 4417 { 4418 eval("\$display_reputation = \"".$templates->get("postbit_reputation_formatted")."\";"); 4419 } 4420 4421 return $display_reputation; 4422 } 4423 4424 /** 4425 * Fetch a color coded version of a warning level (based on it's percentage) 4426 * 4427 * @param int $level The warning level (percentage of 100) 4428 * @return string Formatted warning level 4429 */ 4430 function get_colored_warning_level($level) 4431 { 4432 global $templates; 4433 4434 $warning_class = ''; 4435 if($level >= 80) 4436 { 4437 $warning_class = "high_warning"; 4438 } 4439 else if($level >= 50) 4440 { 4441 $warning_class = "moderate_warning"; 4442 } 4443 else if($level >= 25) 4444 { 4445 $warning_class = "low_warning"; 4446 } 4447 else 4448 { 4449 $warning_class = "normal_warning"; 4450 } 4451 4452 eval("\$level = \"".$templates->get("postbit_warninglevel_formatted")."\";"); 4453 return $level; 4454 } 4455 4456 /** 4457 * Fetch the IP address of the current user. 4458 * 4459 * @return string The IP address. 4460 */ 4461 function get_ip() 4462 { 4463 global $mybb, $plugins; 4464 4465 $ip = strtolower($_SERVER['REMOTE_ADDR']); 4466 4467 if($mybb->settings['ip_forwarded_check']) 4468 { 4469 $addresses = array(); 4470 4471 if(isset($_SERVER['HTTP_X_FORWARDED_FOR'])) 4472 { 4473 $addresses = explode(',', strtolower($_SERVER['HTTP_X_FORWARDED_FOR'])); 4474 } 4475 elseif(isset($_SERVER['HTTP_X_REAL_IP'])) 4476 { 4477 $addresses = explode(',', strtolower($_SERVER['HTTP_X_REAL_IP'])); 4478 } 4479 4480 if(is_array($addresses)) 4481 { 4482 foreach($addresses as $val) 4483 { 4484 $val = trim($val); 4485 // Validate IP address and exclude private addresses 4486 if(my_inet_ntop(my_inet_pton($val)) == $val && !preg_match("#^(10\.|172\.(1[6-9]|2[0-9]|3[0-1])\.|192\.168\.|fe80:|fe[c-f][0-f]:|f[c-d][0-f]{2}:)#", $val)) 4487 { 4488 $ip = $val; 4489 break; 4490 } 4491 } 4492 } 4493 } 4494 4495 if(!$ip) 4496 { 4497 if(isset($_SERVER['HTTP_CLIENT_IP'])) 4498 { 4499 $ip = strtolower($_SERVER['HTTP_CLIENT_IP']); 4500 } 4501 } 4502 4503 if($plugins) 4504 { 4505 $ip_array = array("ip" => &$ip); // Used for backwards compatibility on this hook with the updated run_hooks() function. 4506 $plugins->run_hooks("get_ip", $ip_array); 4507 } 4508 4509 return $ip; 4510 } 4511 4512 /** 4513 * Fetch the friendly size (GB, MB, KB, B) for a specified file size. 4514 * 4515 * @param int $size The size in bytes 4516 * @return string The friendly file size 4517 */ 4518 function get_friendly_size($size) 4519 { 4520 global $lang; 4521 4522 if(!is_numeric($size)) 4523 { 4524 return $lang->na; 4525 } 4526 4527 // Yottabyte (1024 Zettabytes) 4528 if($size >= 1208925819614629174706176) 4529 { 4530 $size = my_number_format(round(($size / 1208925819614629174706176), 2))." ".$lang->size_yb; 4531 } 4532 // Zetabyte (1024 Exabytes) 4533 elseif($size >= 1180591620717411303424) 4534 { 4535 $size = my_number_format(round(($size / 1180591620717411303424), 2))." ".$lang->size_zb; 4536 } 4537 // Exabyte (1024 Petabytes) 4538 elseif($size >= 1152921504606846976) 4539 { 4540 $size = my_number_format(round(($size / 1152921504606846976), 2))." ".$lang->size_eb; 4541 } 4542 // Petabyte (1024 Terabytes) 4543 elseif($size >= 1125899906842624) 4544 { 4545 $size = my_number_format(round(($size / 1125899906842624), 2))." ".$lang->size_pb; 4546 } 4547 // Terabyte (1024 Gigabytes) 4548 elseif($size >= 1099511627776) 4549 { 4550 $size = my_number_format(round(($size / 1099511627776), 2))." ".$lang->size_tb; 4551 } 4552 // Gigabyte (1024 Megabytes) 4553 elseif($size >= 1073741824) 4554 { 4555 $size = my_number_format(round(($size / 1073741824), 2))." ".$lang->size_gb; 4556 } 4557 // Megabyte (1024 Kilobytes) 4558 elseif($size >= 1048576) 4559 { 4560 $size = my_number_format(round(($size / 1048576), 2))." ".$lang->size_mb; 4561 } 4562 // Kilobyte (1024 bytes) 4563 elseif($size >= 1024) 4564 { 4565 $size = my_number_format(round(($size / 1024), 2))." ".$lang->size_kb; 4566 } 4567 elseif($size == 0) 4568 { 4569 $size = "0 ".$lang->size_bytes; 4570 } 4571 else 4572 { 4573 $size = my_number_format($size)." ".$lang->size_bytes; 4574 } 4575 4576 return $size; 4577 } 4578 4579 /** 4580 * Format a decimal number in to microseconds, milliseconds, or seconds. 4581 * 4582 * @param int $time The time in microseconds 4583 * @return string The friendly time duration 4584 */ 4585 function format_time_duration($time) 4586 { 4587 global $lang; 4588 4589 if(!is_numeric($time)) 4590 { 4591 return $lang->na; 4592 } 4593 4594 if(round(1000000 * $time, 2) < 1000) 4595 { 4596 $time = number_format(round(1000000 * $time, 2))." μs"; 4597 } 4598 elseif(round(1000000 * $time, 2) >= 1000 && round(1000000 * $time, 2) < 1000000) 4599 { 4600 $time = number_format(round((1000 * $time), 2))." ms"; 4601 } 4602 else 4603 { 4604 $time = round($time, 3)." seconds"; 4605 } 4606 4607 return $time; 4608 } 4609 4610 /** 4611 * Get the attachment icon for a specific file extension 4612 * 4613 * @param string $ext The file extension 4614 * @return string The attachment icon 4615 */ 4616 function get_attachment_icon($ext) 4617 { 4618 global $cache, $attachtypes, $theme, $templates, $lang, $mybb; 4619 4620 if(!$attachtypes) 4621 { 4622 $attachtypes = $cache->read("attachtypes"); 4623 } 4624 4625 $ext = my_strtolower($ext); 4626 4627 if($attachtypes[$ext]['icon']) 4628 { 4629 static $attach_icons_schemes = array(); 4630 if(!isset($attach_icons_schemes[$ext])) 4631 { 4632 $attach_icons_schemes[$ext] = parse_url($attachtypes[$ext]['icon']); 4633 if(!empty($attach_icons_schemes[$ext]['scheme'])) 4634 { 4635 $attach_icons_schemes[$ext] = $attachtypes[$ext]['icon']; 4636 } 4637 elseif(defined("IN_ADMINCP")) 4638 { 4639 $attach_icons_schemes[$ext] = str_replace("{theme}", "", $attachtypes[$ext]['icon']); 4640 if(my_substr($attach_icons_schemes[$ext], 0, 1) != "/") 4641 { 4642 $attach_icons_schemes[$ext] = "../".$attach_icons_schemes[$ext]; 4643 } 4644 } 4645 elseif(defined("IN_PORTAL")) 4646 { 4647 global $change_dir; 4648 $attach_icons_schemes[$ext] = $change_dir."/".str_replace("{theme}", $theme['imgdir'], $attachtypes[$ext]['icon']); 4649 $attach_icons_schemes[$ext] = $mybb->get_asset_url($attach_icons_schemes[$ext]); 4650 } 4651 else 4652 { 4653 $attach_icons_schemes[$ext] = str_replace("{theme}", $theme['imgdir'], $attachtypes[$ext]['icon']); 4654 $attach_icons_schemes[$ext] = $mybb->get_asset_url($attach_icons_schemes[$ext]); 4655 } 4656 } 4657 4658 $icon = $attach_icons_schemes[$ext]; 4659 4660 $name = htmlspecialchars_uni($attachtypes[$ext]['name']); 4661 } 4662 else 4663 { 4664 if(defined("IN_ADMINCP")) 4665 { 4666 $theme['imgdir'] = "../images"; 4667 } 4668 else if(defined("IN_PORTAL")) 4669 { 4670 global $change_dir; 4671 $theme['imgdir'] = "{$change_dir}/images"; 4672 } 4673 4674 $icon = "{$theme['imgdir']}/attachtypes/unknown.png"; 4675 4676 $name = $lang->unknown; 4677 } 4678 4679 $icon = htmlspecialchars_uni($icon); 4680 eval("\$attachment_icon = \"".$templates->get("attachment_icon")."\";"); 4681 return $attachment_icon; 4682 } 4683 4684 /** 4685 * Get a list of the unviewable forums for the current user 4686 * 4687 * @param boolean $only_readable_threads Set to true to only fetch those forums for which users can actually read a thread in. 4688 * @return string Comma separated values list of the forum IDs which the user cannot view 4689 */ 4690 function get_unviewable_forums($only_readable_threads=false) 4691 { 4692 global $forum_cache, $permissioncache, $mybb; 4693 4694 if(!is_array($forum_cache)) 4695 { 4696 cache_forums(); 4697 } 4698 4699 if(!is_array($permissioncache)) 4700 { 4701 $permissioncache = forum_permissions(); 4702 } 4703 4704 $unviewable = array(); 4705 foreach($forum_cache as $fid => $forum) 4706 { 4707 if($permissioncache[$forum['fid']]) 4708 { 4709 $perms = $permissioncache[$forum['fid']]; 4710 } 4711 else 4712 { 4713 $perms = $mybb->usergroup; 4714 } 4715 4716 $pwverified = 1; 4717 4718 4719 if(!forum_password_validated($forum, true)) 4720 { 4721 $pwverified = 0; 4722 } 4723 else 4724 { 4725 // Check parents for passwords 4726 $parents = explode(",", $forum['parentlist']); 4727 foreach($parents as $parent) 4728 { 4729 if(!forum_password_validated($forum_cache[$parent], true)) 4730 { 4731 $pwverified = 0; 4732 break; 4733 } 4734 } 4735 } 4736 4737 if($perms['canview'] == 0 || $pwverified == 0 || ($only_readable_threads == true && $perms['canviewthreads'] == 0)) 4738 { 4739 $unviewable[] = $forum['fid']; 4740 } 4741 } 4742 4743 $unviewableforums = implode(',', $unviewable); 4744 4745 return $unviewableforums; 4746 } 4747 4748 /** 4749 * Fixes mktime for dates earlier than 1970 4750 * 4751 * @param string $format The date format to use 4752 * @param int $year The year of the date 4753 * @return string The correct date format 4754 */ 4755 function fix_mktime($format, $year) 4756 { 4757 // Our little work around for the date < 1970 thing. 4758 // -2 idea provided by Matt Light (http://www.mephex.com) 4759 $format = str_replace("Y", $year, $format); 4760 $format = str_replace("y", my_substr($year, -2), $format); 4761 4762 return $format; 4763 } 4764 4765 /** 4766 * Build the breadcrumb navigation trail from the specified items 4767 * 4768 * @return string The formatted breadcrumb navigation trail 4769 */ 4770 function build_breadcrumb() 4771 { 4772 global $nav, $navbits, $templates, $theme, $lang, $mybb; 4773 4774 eval("\$navsep = \"".$templates->get("nav_sep")."\";"); 4775 4776 $i = 0; 4777 $activesep = ''; 4778 4779 if(is_array($navbits)) 4780 { 4781 reset($navbits); 4782 foreach($navbits as $key => $navbit) 4783 { 4784 if(isset($navbits[$key+1])) 4785 { 4786 if(isset($navbits[$key+2])) 4787 { 4788 $sep = $navsep; 4789 } 4790 else 4791 { 4792 $sep = ""; 4793 } 4794 4795 $multipage = null; 4796 $multipage_dropdown = null; 4797 if(!empty($navbit['multipage'])) 4798 { 4799 if(!$mybb->settings['threadsperpage'] || (int)$mybb->settings['threadsperpage'] < 1) 4800 { 4801 $mybb->settings['threadsperpage'] = 20; 4802 } 4803 4804 $multipage = multipage($navbit['multipage']['num_threads'], $mybb->settings['threadsperpage'], $navbit['multipage']['current_page'], $navbit['multipage']['url'], true); 4805 if($multipage) 4806 { 4807 ++$i; 4808 eval("\$multipage_dropdown = \"".$templates->get("nav_dropdown")."\";"); 4809 $sep = $multipage_dropdown.$sep; 4810 } 4811 } 4812 4813 // Replace page 1 URLs 4814 $navbit['url'] = str_replace("-page-1.html", ".html", $navbit['url']); 4815 $navbit['url'] = preg_replace("/&page=1$/", "", $navbit['url']); 4816 4817 eval("\$nav .= \"".$templates->get("nav_bit")."\";"); 4818 } 4819 } 4820 $navsize = count($navbits); 4821 $navbit = $navbits[$navsize-1]; 4822 } 4823 4824 if($nav) 4825 { 4826 eval("\$activesep = \"".$templates->get("nav_sep_active")."\";"); 4827 } 4828 4829 eval("\$activebit = \"".$templates->get("nav_bit_active")."\";"); 4830 eval("\$donenav = \"".$templates->get("nav")."\";"); 4831 4832 return $donenav; 4833 } 4834 4835 /** 4836 * Add a breadcrumb menu item to the list. 4837 * 4838 * @param string $name The name of the item to add 4839 * @param string $url The URL of the item to add 4840 */ 4841 function add_breadcrumb($name, $url="") 4842 { 4843 global $navbits; 4844 4845 $navsize = count($navbits); 4846 $navbits[$navsize]['name'] = $name; 4847 $navbits[$navsize]['url'] = $url; 4848 } 4849 4850 /** 4851 * Build the forum breadcrumb nagiation (the navigation to a specific forum including all parent forums) 4852 * 4853 * @param int $fid The forum ID to build the navigation for 4854 * @param array $multipage The multipage drop down array of information 4855 * @return int Returns 1 in every case. Kept for compatibility 4856 */ 4857 function build_forum_breadcrumb($fid, $multipage=array()) 4858 { 4859 global $pforumcache, $currentitem, $forum_cache, $navbits, $lang, $base_url, $archiveurl; 4860 4861 if(!$pforumcache) 4862 { 4863 if(!is_array($forum_cache)) 4864 { 4865 cache_forums(); 4866 } 4867 4868 foreach($forum_cache as $key => $val) 4869 { 4870 $pforumcache[$val['fid']][$val['pid']] = $val; 4871 } 4872 } 4873 4874 if(is_array($pforumcache[$fid])) 4875 { 4876 foreach($pforumcache[$fid] as $key => $forumnav) 4877 { 4878 if($fid == $forumnav['fid']) 4879 { 4880 if(!empty($pforumcache[$forumnav['pid']])) 4881 { 4882 build_forum_breadcrumb($forumnav['pid']); 4883 } 4884 4885 $navsize = count($navbits); 4886 // Convert & to & 4887 $navbits[$navsize]['name'] = preg_replace("#&(?!\#[0-9]+;)#si", "&", $forumnav['name']); 4888 4889 if(defined("IN_ARCHIVE")) 4890 { 4891 // Set up link to forum in breadcrumb. 4892 if($pforumcache[$fid][$forumnav['pid']]['type'] == 'f' || $pforumcache[$fid][$forumnav['pid']]['type'] == 'c') 4893 { 4894 $navbits[$navsize]['url'] = "{$base_url}forum-".$forumnav['fid'].".html"; 4895 } 4896 else 4897 { 4898 $navbits[$navsize]['url'] = $archiveurl."/index.php"; 4899 } 4900 } 4901 elseif(!empty($multipage)) 4902 { 4903 $navbits[$navsize]['url'] = get_forum_link($forumnav['fid'], $multipage['current_page']); 4904 4905 $navbits[$navsize]['multipage'] = $multipage; 4906 $navbits[$navsize]['multipage']['url'] = str_replace('{fid}', $forumnav['fid'], FORUM_URL_PAGED); 4907 } 4908 else 4909 { 4910 $navbits[$navsize]['url'] = get_forum_link($forumnav['fid']); 4911 } 4912 } 4913 } 4914 } 4915 4916 return 1; 4917 } 4918 4919 /** 4920 * Resets the breadcrumb navigation to the first item, and clears the rest 4921 */ 4922 function reset_breadcrumb() 4923 { 4924 global $navbits; 4925 4926 $newnav[0]['name'] = $navbits[0]['name']; 4927 $newnav[0]['url'] = $navbits[0]['url']; 4928 if(!empty($navbits[0]['options'])) 4929 { 4930 $newnav[0]['options'] = $navbits[0]['options']; 4931 } 4932 4933 unset($GLOBALS['navbits']); 4934 $GLOBALS['navbits'] = $newnav; 4935 } 4936 4937 /** 4938 * Builds a URL to an archive mode page 4939 * 4940 * @param string $type The type of page (thread|announcement|forum) 4941 * @param int $id The ID of the item 4942 * @return string The URL 4943 */ 4944 function build_archive_link($type="", $id=0) 4945 { 4946 global $mybb; 4947 4948 // If the server OS is not Windows and not Apache or the PHP is running as a CGI or we have defined ARCHIVE_QUERY_STRINGS, use query strings - DIRECTORY_SEPARATOR checks if running windows 4949 //if((DIRECTORY_SEPARATOR == '\\' && is_numeric(stripos($_SERVER['SERVER_SOFTWARE'], "apache")) == false) || is_numeric(stripos(SAPI_NAME, "cgi")) !== false || defined("ARCHIVE_QUERY_STRINGS")) 4950 if($mybb->settings['seourls_archive'] == 1) 4951 { 4952 $base_url = $mybb->settings['bburl']."/archive/index.php/"; 4953 } 4954 else 4955 { 4956 $base_url = $mybb->settings['bburl']."/archive/index.php?"; 4957 } 4958 4959 switch($type) 4960 { 4961 case "thread": 4962 $url = "{$base_url}thread-{$id}.html"; 4963 break; 4964 case "announcement": 4965 $url = "{$base_url}announcement-{$id}.html"; 4966 break; 4967 case "forum": 4968 $url = "{$base_url}forum-{$id}.html"; 4969 break; 4970 default: 4971 $url = $mybb->settings['bburl']."/archive/index.php"; 4972 } 4973 4974 return $url; 4975 } 4976 4977 /** 4978 * Prints a debug information page 4979 */ 4980 function debug_page() 4981 { 4982 global $db, $debug, $templates, $templatelist, $mybb, $maintimer, $globaltime, $ptimer, $parsetime, $lang, $cache; 4983 4984 $totaltime = format_time_duration($maintimer->totaltime); 4985 $phptime = $maintimer->totaltime - $db->query_time; 4986 $query_time = $db->query_time; 4987 $globaltime = format_time_duration($globaltime); 4988 4989 $percentphp = number_format((($phptime/$maintimer->totaltime)*100), 2); 4990 $percentsql = number_format((($query_time/$maintimer->totaltime)*100), 2); 4991 4992 $phptime = format_time_duration($maintimer->totaltime - $db->query_time); 4993 $query_time = format_time_duration($db->query_time); 4994 4995 $call_time = format_time_duration($cache->call_time); 4996 4997 $phpversion = PHP_VERSION; 4998 4999 $serverload = get_server_load(); 5000 5001 if($mybb->settings['gzipoutput'] != 0) 5002 { 5003 $gzipen = "Enabled"; 5004 } 5005 else 5006 { 5007 $gzipen = "Disabled"; 5008 } 5009 5010 echo "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"; 5011 echo "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">"; 5012 echo "<head>"; 5013 echo "<meta name=\"robots\" content=\"noindex\" />"; 5014 echo "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />"; 5015 echo "<title>MyBB Debug Information</title>"; 5016 echo "</head>"; 5017 echo "<body>"; 5018 echo "<h1>MyBB Debug Information</h1>\n"; 5019 echo "<h2>Page Generation</h2>\n"; 5020 echo "<table bgcolor=\"#666666\" width=\"95%\" cellpadding=\"4\" cellspacing=\"1\" align=\"center\">\n"; 5021 echo "<tr>\n"; 5022 echo "<td bgcolor=\"#cccccc\" colspan=\"4\"><b><span style=\"size:2;\">Page Generation Statistics</span></b></td>\n"; 5023 echo "</tr>\n"; 5024 echo "<tr>\n"; 5025 echo "<td bgcolor=\"#efefef\" width=\"25%\"><b><span style=\"font-family: tahoma; font-size: 12px;\">Page Generation Time:</span></b></td>\n"; 5026 echo "<td bgcolor=\"#fefefe\" width=\"25%\"><span style=\"font-family: tahoma; font-size: 12px;\">$totaltime</span></td>\n"; 5027 echo "<td bgcolor=\"#efefef\" width=\"25%\"><b><span style=\"font-family: tahoma; font-size: 12px;\">No. DB Queries:</span></b></td>\n"; 5028 echo "<td bgcolor=\"#fefefe\" width=\"25%\"><span style=\"font-family: tahoma; font-size: 12px;\">$db->query_count</span></td>\n"; 5029 echo "</tr>\n"; 5030 echo "<tr>\n"; 5031 echo "<td bgcolor=\"#efefef\" width=\"25%\"><b><span style=\"font-family: tahoma; font-size: 12px;\">PHP Processing Time:</span></b></td>\n"; 5032 echo "<td bgcolor=\"#fefefe\" width=\"25%\"><span style=\"font-family: tahoma; font-size: 12px;\">$phptime ($percentphp%)</span></td>\n"; 5033 echo "<td bgcolor=\"#efefef\" width=\"25%\"><b><span style=\"font-family: tahoma; font-size: 12px;\">DB Processing Time:</span></b></td>\n"; 5034 echo "<td bgcolor=\"#fefefe\" width=\"25%\"><span style=\"font-family: tahoma; font-size: 12px;\">$query_time ($percentsql%)</span></td>\n"; 5035 echo "</tr>\n"; 5036 echo "<tr>\n"; 5037 echo "<td bgcolor=\"#efefef\" width=\"25%\"><b><span style=\"font-family: tahoma; font-size: 12px;\">Extensions Used:</span></b></td>\n"; 5038 echo "<td bgcolor=\"#fefefe\" width=\"25%\"><span style=\"font-family: tahoma; font-size: 12px;\">{$mybb->config['database']['type']}, xml</span></td>\n"; 5039 echo "<td bgcolor=\"#efefef\" width=\"25%\"><b><span style=\"font-family: tahoma; font-size: 12px;\">Global.php Processing Time:</span></b></td>\n"; 5040 echo "<td bgcolor=\"#fefefe\" width=\"25%\"><span style=\"font-family: tahoma; font-size: 12px;\">$globaltime</span></td>\n"; 5041 echo "</tr>\n"; 5042 echo "<tr>\n"; 5043 echo "<td bgcolor=\"#efefef\" width=\"25%\"><b><span style=\"font-family: tahoma; font-size: 12px;\">PHP Version:</span></b></td>\n"; 5044 echo "<td bgcolor=\"#fefefe\" width=\"25%\"><span style=\"font-family: tahoma; font-size: 12px;\">$phpversion</span></td>\n"; 5045 echo "<td bgcolor=\"#efefef\" width=\"25%\"><b><span style=\"font-family: tahoma; font-size: 12px;\">Server Load:</span></b></td>\n"; 5046 echo "<td bgcolor=\"#fefefe\" width=\"25%\"><span style=\"font-family: tahoma; font-size: 12px;\">$serverload</span></td>\n"; 5047 echo "</tr>\n"; 5048 echo "<tr>\n"; 5049 echo "<td bgcolor=\"#efefef\" width=\"25%\"><b><span style=\"font-family: tahoma; font-size: 12px;\">GZip Encoding Status:</span></b></td>\n"; 5050 echo "<td bgcolor=\"#fefefe\" width=\"25%\"><span style=\"font-family: tahoma; font-size: 12px;\">$gzipen</span></td>\n"; 5051 echo "<td bgcolor=\"#efefef\" width=\"25%\"><b><span style=\"font-family: tahoma; font-size: 12px;\">No. Templates Used:</span></b></td>\n"; 5052 echo "<td bgcolor=\"#fefefe\" width=\"25%\"><span style=\"font-family: tahoma; font-size: 12px;\">".count($templates->cache)." (".(int)count(explode(",", $templatelist))." Cached / ".(int)count($templates->uncached_templates)." Manually Loaded)</span></td>\n"; 5053 echo "</tr>\n"; 5054 5055 $memory_usage = get_memory_usage(); 5056 if(!$memory_usage) 5057 { 5058 $memory_usage = $lang->unknown; 5059 } 5060 else 5061 { 5062 $memory_usage = get_friendly_size($memory_usage)." ({$memory_usage} bytes)"; 5063 } 5064 $memory_limit = @ini_get("memory_limit"); 5065 echo "<tr>\n"; 5066 echo "<td bgcolor=\"#EFEFEF\" width=\"25%\"><b><span style=\"font-family: tahoma; font-size: 12px;\">Memory Usage:</span></b></td>\n"; 5067 echo "<td bgcolor=\"#FEFEFE\" width=\"25%\"><span style=\"font-family: tahoma; font-size: 12px;\">{$memory_usage}</span></td>\n"; 5068 echo "<td bgcolor=\"#EFEFEF\" width=\"25%\"><b><span style=\"font-family: tahoma; font-size: 12px;\">Memory Limit:</span></b></td>\n"; 5069 echo "<td bgcolor=\"#FEFEFE\" width=\"25%\"><span style=\"font-family: tahoma; font-size: 12px;\">{$memory_limit}</span></td>\n"; 5070 echo "</tr>\n"; 5071 5072 echo "</table>\n"; 5073 5074 echo "<h2>Database Connections (".count($db->connections)." Total) </h2>\n"; 5075 echo "<table style=\"background-color: #666;\" width=\"95%\" cellpadding=\"4\" cellspacing=\"1\" align=\"center\">\n"; 5076 echo "<tr>\n"; 5077 echo "<td style=\"background: #fff;\">".implode("<br />", $db->connections)."</td>\n"; 5078 echo "</tr>\n"; 5079 echo "</table>\n"; 5080 echo "<br />\n"; 5081 5082 echo "<h2>Database Queries (".$db->query_count." Total) </h2>\n"; 5083 echo $db->explain; 5084 5085 if($cache->call_count > 0) 5086 { 5087 echo "<h2>Cache Calls (".$cache->call_count." Total, ".$call_time.") </h2>\n"; 5088 echo $cache->cache_debug; 5089 } 5090 5091 echo "<h2>Template Statistics</h2>\n"; 5092 5093 if(count($templates->cache) > 0) 5094 { 5095 echo "<table style=\"background-color: #666;\" width=\"95%\" cellpadding=\"4\" cellspacing=\"1\" align=\"center\">\n"; 5096 echo "<tr>\n"; 5097 echo "<td style=\"background-color: #ccc;\"><strong>Templates Used (Loaded for this Page) - ".count($templates->cache)." Total</strong></td>\n"; 5098 echo "</tr>\n"; 5099 echo "<tr>\n"; 5100 echo "<td style=\"background: #fff;\">".implode(", ", array_keys($templates->cache))."</td>\n"; 5101 echo "</tr>\n"; 5102 echo "</table>\n"; 5103 echo "<br />\n"; 5104 } 5105 5106 if(count($templates->uncached_templates) > 0) 5107 { 5108 echo "<table style=\"background-color: #666;\" width=\"95%\" cellpadding=\"4\" cellspacing=\"1\" align=\"center\">\n"; 5109 echo "<tr>\n"; 5110 echo "<td style=\"background-color: #ccc;\"><strong>Templates Requiring Additional Calls (Not Cached at Startup) - ".count($templates->uncached_templates)." Total</strong></td>\n"; 5111 echo "</tr>\n"; 5112 echo "<tr>\n"; 5113 echo "<td style=\"background: #fff;\">".implode(", ", $templates->uncached_templates)."</td>\n"; 5114 echo "</tr>\n"; 5115 echo "</table>\n"; 5116 echo "<br />\n"; 5117 } 5118 echo "</body>"; 5119 echo "</html>"; 5120 exit; 5121 } 5122 5123 /** 5124 * Outputs the correct page headers. 5125 */ 5126 function send_page_headers() 5127 { 5128 global $mybb; 5129 5130 if($mybb->settings['nocacheheaders'] == 1) 5131 { 5132 header("Cache-Control: no-cache, private"); 5133 } 5134 } 5135 5136 /** 5137 * Mark specific reported posts of a certain type as dealt with 5138 * 5139 * @param array|int $id An array or int of the ID numbers you're marking as dealt with 5140 * @param string $type The type of item the above IDs are for - post, posts, thread, threads, forum, all 5141 */ 5142 function mark_reports($id, $type="post") 5143 { 5144 global $db, $cache, $plugins; 5145 5146 switch($type) 5147 { 5148 case "posts": 5149 if(is_array($id)) 5150 { 5151 $rids = implode("','", $id); 5152 $rids = "'0','$rids'"; 5153 $db->update_query("reportedcontent", array('reportstatus' => 1), "id IN($rids) AND reportstatus='0' AND (type = 'post' OR type = '')"); 5154 } 5155 break; 5156 case "post": 5157 $db->update_query("reportedcontent", array('reportstatus' => 1), "id='$id' AND reportstatus='0' AND (type = 'post' OR type = '')"); 5158 break; 5159 case "threads": 5160 if(is_array($id)) 5161 { 5162 $rids = implode("','", $id); 5163 $rids = "'0','$rids'"; 5164 $db->update_query("reportedcontent", array('reportstatus' => 1), "id2 IN($rids) AND reportstatus='0' AND (type = 'post' OR type = '')"); 5165 } 5166 break; 5167 case "thread": 5168 $db->update_query("reportedcontent", array('reportstatus' => 1), "id2='$id' AND reportstatus='0' AND (type = 'post' OR type = '')"); 5169 break; 5170 case "forum": 5171 $db->update_query("reportedcontent", array('reportstatus' => 1), "id3='$id' AND reportstatus='0' AND (type = 'post' OR type = '')"); 5172 break; 5173 case "all": 5174 $db->update_query("reportedcontent", array('reportstatus' => 1), "reportstatus='0' AND (type = 'post' OR type = '')"); 5175 break; 5176 } 5177 5178 $arguments = array('id' => $id, 'type' => $type); 5179 $plugins->run_hooks("mark_reports", $arguments); 5180 $cache->update_reportedcontent(); 5181 } 5182 5183 /** 5184 * Fetch a friendly x days, y months etc date stamp from a timestamp 5185 * 5186 * @param int $stamp The timestamp 5187 * @param array $options Array of options 5188 * @return string The friendly formatted timestamp 5189 */ 5190 function nice_time($stamp, $options=array()) 5191 { 5192 global $lang; 5193 5194 $ysecs = 365*24*60*60; 5195 $mosecs = 31*24*60*60; 5196 $wsecs = 7*24*60*60; 5197 $dsecs = 24*60*60; 5198 $hsecs = 60*60; 5199 $msecs = 60; 5200 5201 if(isset($options['short'])) 5202 { 5203 $lang_year = $lang->year_short; 5204 $lang_years = $lang->years_short; 5205 $lang_month = $lang->month_short; 5206 $lang_months = $lang->months_short; 5207 $lang_week = $lang->week_short; 5208 $lang_weeks = $lang->weeks_short; 5209 $lang_day = $lang->day_short; 5210 $lang_days = $lang->days_short; 5211 $lang_hour = $lang->hour_short; 5212 $lang_hours = $lang->hours_short; 5213 $lang_minute = $lang->minute_short; 5214 $lang_minutes = $lang->minutes_short; 5215 $lang_second = $lang->second_short; 5216 $lang_seconds = $lang->seconds_short; 5217 } 5218 else 5219 { 5220 $lang_year = " ".$lang->year; 5221 $lang_years = " ".$lang->years; 5222 $lang_month = " ".$lang->month; 5223 $lang_months = " ".$lang->months; 5224 $lang_week = " ".$lang->week; 5225 $lang_weeks = " ".$lang->weeks; 5226 $lang_day = " ".$lang->day; 5227 $lang_days = " ".$lang->days; 5228 $lang_hour = " ".$lang->hour; 5229 $lang_hours = " ".$lang->hours; 5230 $lang_minute = " ".$lang->minute; 5231 $lang_minutes = " ".$lang->minutes; 5232 $lang_second = " ".$lang->second; 5233 $lang_seconds = " ".$lang->seconds; 5234 } 5235 5236 $years = floor($stamp/$ysecs); 5237 $stamp %= $ysecs; 5238 $months = floor($stamp/$mosecs); 5239 $stamp %= $mosecs; 5240 $weeks = floor($stamp/$wsecs); 5241 $stamp %= $wsecs; 5242 $days = floor($stamp/$dsecs); 5243 $stamp %= $dsecs; 5244 $hours = floor($stamp/$hsecs); 5245 $stamp %= $hsecs; 5246 $minutes = floor($stamp/$msecs); 5247 $stamp %= $msecs; 5248 $seconds = $stamp; 5249 5250 // Prevent gross over accuracy ($options parameter will override these) 5251 if($years > 0) 5252 { 5253 $options = array_merge(array( 5254 'days' => false, 5255 'hours' => false, 5256 'minutes' => false, 5257 'seconds' => false 5258 ), $options); 5259 } 5260 elseif($months > 0) 5261 { 5262 $options = array_merge(array( 5263 'hours' => false, 5264 'minutes' => false, 5265 'seconds' => false 5266 ), $options); 5267 } 5268 elseif($weeks > 0) 5269 { 5270 $options = array_merge(array( 5271 'minutes' => false, 5272 'seconds' => false 5273 ), $options); 5274 } 5275 elseif($days > 0) 5276 { 5277 $options = array_merge(array( 5278 'seconds' => false 5279 ), $options); 5280 } 5281 5282 $nicetime = array(); 5283 5284 if(!isset($options['years']) || $options['years'] !== false) 5285 { 5286 if($years == 1) 5287 { 5288 $nicetime['years'] = "1".$lang_year; 5289 } 5290 else if($years > 1) 5291 { 5292 $nicetime['years'] = $years.$lang_years; 5293 } 5294 } 5295 5296 if(!isset($options['months']) || $options['months'] !== false) 5297 { 5298 if($months == 1) 5299 { 5300 $nicetime['months'] = "1".$lang_month; 5301 } 5302 else if($months > 1) 5303 { 5304 $nicetime['months'] = $months.$lang_months; 5305 } 5306 } 5307 5308 if(!isset($options['weeks']) || $options['weeks'] !== false) 5309 { 5310 if($weeks == 1) 5311 { 5312 $nicetime['weeks'] = "1".$lang_week; 5313 } 5314 else if($weeks > 1) 5315 { 5316 $nicetime['weeks'] = $weeks.$lang_weeks; 5317 } 5318 } 5319 5320 if(!isset($options['days']) || $options['days'] !== false) 5321 { 5322 if($days == 1) 5323 { 5324 $nicetime['days'] = "1".$lang_day; 5325 } 5326 else if($days > 1) 5327 { 5328 $nicetime['days'] = $days.$lang_days; 5329 } 5330 } 5331 5332 if(!isset($options['hours']) || $options['hours'] !== false) 5333 { 5334 if($hours == 1) 5335 { 5336 $nicetime['hours'] = "1".$lang_hour; 5337 } 5338 else if($hours > 1) 5339 { 5340 $nicetime['hours'] = $hours.$lang_hours; 5341 } 5342 } 5343 5344 if(!isset($options['minutes']) || $options['minutes'] !== false) 5345 { 5346 if($minutes == 1) 5347 { 5348 $nicetime['minutes'] = "1".$lang_minute; 5349 } 5350 else if($minutes > 1) 5351 { 5352 $nicetime['minutes'] = $minutes.$lang_minutes; 5353 } 5354 } 5355 5356 if(!isset($options['seconds']) || $options['seconds'] !== false) 5357 { 5358 if($seconds == 1) 5359 { 5360 $nicetime['seconds'] = "1".$lang_second; 5361 } 5362 else if($seconds > 1) 5363 { 5364 $nicetime['seconds'] = $seconds.$lang_seconds; 5365 } 5366 } 5367 5368 if(!empty($nicetime)) 5369 { 5370 return implode(", ", $nicetime); 5371 } 5372 } 5373 5374 /** 5375 * Select an alternating row colour based on the previous call to this function 5376 * 5377 * @param int $reset 1 to reset the row to trow1. 5378 * @return string trow1 or trow2 depending on the previous call 5379 */ 5380 function alt_trow($reset=0) 5381 { 5382 global $alttrow; 5383 5384 if($alttrow == "trow1" && !$reset) 5385 { 5386 $trow = "trow2"; 5387 } 5388 else 5389 { 5390 $trow = "trow1"; 5391 } 5392 5393 $alttrow = $trow; 5394 5395 return $trow; 5396 } 5397 5398 /** 5399 * Add a user to a specific additional user group. 5400 * 5401 * @param int $uid The user ID 5402 * @param int $joingroup The user group ID to join 5403 * @return bool 5404 */ 5405 function join_usergroup($uid, $joingroup) 5406 { 5407 global $db, $mybb; 5408 5409 if($uid == $mybb->user['uid']) 5410 { 5411 $user = $mybb->user; 5412 } 5413 else 5414 { 5415 $query = $db->simple_select("users", "additionalgroups, usergroup", "uid='".(int)$uid."'"); 5416 $user = $db->fetch_array($query); 5417 } 5418 5419 // Build the new list of additional groups for this user and make sure they're in the right format 5420 $groups = array_map( 5421 'intval', 5422 explode(',', $user['additionalgroups']) 5423 ); 5424 5425 if(!in_array((int)$joingroup, $groups)) 5426 { 5427 $groups[] = (int)$joingroup; 5428 $groups = array_diff($groups, array($user['usergroup'])); 5429 $groups = array_unique($groups); 5430 5431 $groupslist = implode(',', $groups); 5432 5433 $db->update_query("users", array('additionalgroups' => $groupslist), "uid='".(int)$uid."'"); 5434 return true; 5435 } 5436 else 5437 { 5438 return false; 5439 } 5440 } 5441 5442 /** 5443 * Remove a user from a specific additional user group 5444 * 5445 * @param int $uid The user ID 5446 * @param int $leavegroup The user group ID 5447 */ 5448 function leave_usergroup($uid, $leavegroup) 5449 { 5450 global $db, $mybb, $cache; 5451 5452 $user = get_user($uid); 5453 5454 if($user['usergroup'] == $leavegroup) 5455 { 5456 return false; 5457 } 5458 5459 $groups = array_map( 5460 'intval', 5461 explode(',', $user['additionalgroups']) 5462 ); 5463 $groups = array_diff($groups, array($leavegroup)); 5464 $groups = array_unique($groups); 5465 5466 $groupslist = implode(',', $groups); 5467 5468 $dispupdate = ""; 5469 if($leavegroup == $user['displaygroup']) 5470 { 5471 $dispupdate = ", displaygroup=usergroup"; 5472 } 5473 5474 $db->write_query(" 5475 UPDATE ".TABLE_PREFIX."users 5476 SET additionalgroups='$groupslist' $dispupdate 5477 WHERE uid='".(int)$uid."' 5478 "); 5479 5480 $cache->update_moderators(); 5481 } 5482 5483 /** 5484 * Get the current location taking in to account different web serves and systems 5485 * 5486 * @param boolean $fields True to return as "hidden" fields 5487 * @param array $ignore Array of fields to ignore for returning "hidden" fields or URL being accessed 5488 * @param boolean $quick True to skip all inputs and return only the file path part of the URL 5489 * @return string|array The current URL being accessed or form data if $fields is true 5490 */ 5491 function get_current_location($fields=false, $ignore=array(), $quick=false) 5492 { 5493 global $mybb; 5494 5495 if(defined("MYBB_LOCATION")) 5496 { 5497 return MYBB_LOCATION; 5498 } 5499 5500 if(!empty($_SERVER['SCRIPT_NAME'])) 5501 { 5502 $location = htmlspecialchars_uni($_SERVER['SCRIPT_NAME']); 5503 } 5504 elseif(!empty($_SERVER['PHP_SELF'])) 5505 { 5506 $location = htmlspecialchars_uni($_SERVER['PHP_SELF']); 5507 } 5508 elseif(!empty($_ENV['PHP_SELF'])) 5509 { 5510 $location = htmlspecialchars_uni($_ENV['PHP_SELF']); 5511 } 5512 elseif(!empty($_SERVER['PATH_INFO'])) 5513 { 5514 $location = htmlspecialchars_uni($_SERVER['PATH_INFO']); 5515 } 5516 else 5517 { 5518 $location = htmlspecialchars_uni($_ENV['PATH_INFO']); 5519 } 5520 5521 if($quick) 5522 { 5523 return $location; 5524 } 5525 5526 if(!is_array($ignore)) 5527 { 5528 $ignore = array($ignore); 5529 } 5530 5531 if($fields == true) 5532 { 5533 5534 $form_html = ''; 5535 if(!empty($mybb->input)) 5536 { 5537 foreach($mybb->input as $name => $value) 5538 { 5539 if(in_array($name, $ignore) || is_array($name) || is_array($value)) 5540 { 5541 continue; 5542 } 5543 5544 $form_html .= "<input type=\"hidden\" name=\"".htmlspecialchars_uni($name)."\" value=\"".htmlspecialchars_uni($value)."\" />\n"; 5545 } 5546 } 5547 5548 return array('location' => $location, 'form_html' => $form_html, 'form_method' => $mybb->request_method); 5549 } 5550 else 5551 { 5552 $parameters = array(); 5553 5554 if(isset($_SERVER['QUERY_STRING'])) 5555 { 5556 $current_query_string = $_SERVER['QUERY_STRING']; 5557 } 5558 else if(isset($_ENV['QUERY_STRING'])) 5559 { 5560 $current_query_string = $_ENV['QUERY_STRING']; 5561 } else 5562 { 5563 $current_query_string = ''; 5564 } 5565 5566 parse_str($current_query_string, $current_parameters); 5567 5568 foreach($current_parameters as $name => $value) 5569 { 5570 if(!in_array($name, $ignore)) 5571 { 5572 $parameters[$name] = $value; 5573 } 5574 } 5575 5576 if($mybb->request_method === 'post') 5577 { 5578 $post_array = array('action', 'fid', 'pid', 'tid', 'uid', 'eid'); 5579 5580 foreach($post_array as $var) 5581 { 5582 if(isset($_POST[$var]) && !in_array($var, $ignore)) 5583 { 5584 $parameters[$var] = $_POST[$var]; 5585 } 5586 } 5587 } 5588 5589 if(!empty($parameters)) 5590 { 5591 $location .= '?'.http_build_query($parameters, '', '&'); 5592 } 5593 5594 return $location; 5595 } 5596 } 5597 5598 /** 5599 * Build a theme selection menu 5600 * 5601 * @param string $name The name of the menu 5602 * @param int $selected The ID of the selected theme 5603 * @param int $tid The ID of the parent theme to select from 5604 * @param string $depth The current selection depth 5605 * @param boolean $usergroup_override Whether or not to override usergroup permissions (true to override) 5606 * @param boolean $footer Whether or not theme select is in the footer (true if it is) 5607 * @param boolean $count_override Whether or not to override output based on theme count (true to override) 5608 * @return string The theme selection list 5609 */ 5610 function build_theme_select($name, $selected=-1, $tid=0, $depth="", $usergroup_override=false, $footer=false, $count_override=false) 5611 { 5612 global $db, $themeselect, $tcache, $lang, $mybb, $limit, $templates, $num_themes, $themeselect_option; 5613 5614 if($tid == 0) 5615 { 5616 $tid = 1; 5617 $num_themes = 0; 5618 $themeselect_option = ''; 5619 } 5620 5621 if(!is_array($tcache)) 5622 { 5623 $query = $db->simple_select('themes', 'tid, name, pid, allowedgroups', "pid!='0'"); 5624 5625 while($theme = $db->fetch_array($query)) 5626 { 5627 $tcache[$theme['pid']][$theme['tid']] = $theme; 5628 } 5629 } 5630 5631 if(is_array($tcache[$tid])) 5632 { 5633 foreach($tcache[$tid] as $theme) 5634 { 5635 $sel = ""; 5636 // Show theme if allowed, or if override is on 5637 if(is_member($theme['allowedgroups']) || $theme['allowedgroups'] == "all" || $usergroup_override == true) 5638 { 5639 if($theme['tid'] == $selected) 5640 { 5641 $sel = " selected=\"selected\""; 5642 } 5643 5644 if($theme['pid'] != 0) 5645 { 5646 $theme['name'] = htmlspecialchars_uni($theme['name']); 5647 eval("\$themeselect_option .= \"".$templates->get("usercp_themeselector_option")."\";"); 5648 ++$num_themes; 5649 $depthit = $depth."--"; 5650 } 5651 5652 if(array_key_exists($theme['tid'], $tcache)) 5653 { 5654 build_theme_select($name, $selected, $theme['tid'], $depthit, $usergroup_override, $footer, $count_override); 5655 } 5656 } 5657 } 5658 } 5659 5660 if($tid == 1 && ($num_themes > 1 || $count_override == true)) 5661 { 5662 if($footer == true) 5663 { 5664 eval("\$themeselect = \"".$templates->get("footer_themeselector")."\";"); 5665 } 5666 else 5667 { 5668 eval("\$themeselect = \"".$templates->get("usercp_themeselector")."\";"); 5669 } 5670 5671 return $themeselect; 5672 } 5673 else 5674 { 5675 return false; 5676 } 5677 } 5678 5679 /** 5680 * Get the theme data of a theme id. 5681 * 5682 * @param int $tid The theme id of the theme. 5683 * @return boolean|array False if no valid theme, Array with the theme data otherwise 5684 */ 5685 function get_theme($tid) 5686 { 5687 global $tcache, $db; 5688 5689 if(!is_array($tcache)) 5690 { 5691 $query = $db->simple_select('themes', 'tid, name, pid, allowedgroups', "pid!='0'"); 5692 5693 while($theme = $db->fetch_array($query)) 5694 { 5695 $tcache[$theme['pid']][$theme['tid']] = $theme; 5696 } 5697 } 5698 5699 $s_theme = false; 5700 5701 foreach($tcache as $themes) 5702 { 5703 foreach($themes as $theme) 5704 { 5705 if($tid == $theme['tid']) 5706 { 5707 $s_theme = $theme; 5708 break 2; 5709 } 5710 } 5711 } 5712 5713 return $s_theme; 5714 } 5715 5716 /** 5717 * Custom function for htmlspecialchars which takes in to account unicode 5718 * 5719 * @param string $message The string to format 5720 * @return string The string with htmlspecialchars applied 5721 */ 5722 function htmlspecialchars_uni($message) 5723 { 5724 $message = preg_replace("#&(?!\#[0-9]+;)#si", "&", $message); // Fix & but allow unicode 5725 $message = str_replace("<", "<", $message); 5726 $message = str_replace(">", ">", $message); 5727 $message = str_replace("\"", """, $message); 5728 return $message; 5729 } 5730 5731 /** 5732 * Custom function for formatting numbers. 5733 * 5734 * @param int $number The number to format. 5735 * @return int The formatted number. 5736 */ 5737 function my_number_format($number) 5738 { 5739 global $mybb; 5740 5741 if($number == "-") 5742 { 5743 return $number; 5744 } 5745 5746 if(is_int($number)) 5747 { 5748 return number_format($number, 0, $mybb->settings['decpoint'], $mybb->settings['thousandssep']); 5749 } 5750 else 5751 { 5752 if(isset($number)) 5753 { 5754 $parts = explode('.', $number); 5755 } 5756 else 5757 { 5758 $parts = array(); 5759 } 5760 5761 if(isset($parts[1])) 5762 { 5763 $decimals = my_strlen($parts[1]); 5764 } 5765 else 5766 { 5767 $decimals = 0; 5768 } 5769 5770 return number_format((double)$number, $decimals, $mybb->settings['decpoint'], $mybb->settings['thousandssep']); 5771 } 5772 } 5773 5774 /** 5775 * Converts a string of text to or from UTF-8. 5776 * 5777 * @param string $str The string of text to convert 5778 * @param boolean $to Whether or not the string is being converted to or from UTF-8 (true if converting to) 5779 * @return string The converted string 5780 */ 5781 function convert_through_utf8($str, $to=true) 5782 { 5783 global $lang; 5784 static $charset; 5785 static $use_mb; 5786 static $use_iconv; 5787 5788 if(!isset($charset)) 5789 { 5790 $charset = my_strtolower($lang->settings['charset']); 5791 } 5792 5793 if($charset == "utf-8") 5794 { 5795 return $str; 5796 } 5797 5798 if(!isset($use_iconv)) 5799 { 5800 $use_iconv = function_exists("iconv"); 5801 } 5802 5803 if(!isset($use_mb)) 5804 { 5805 $use_mb = function_exists("mb_convert_encoding"); 5806 } 5807 5808 if($use_iconv || $use_mb) 5809 { 5810 if($to) 5811 { 5812 $from_charset = $lang->settings['charset']; 5813 $to_charset = "UTF-8"; 5814 } 5815 else 5816 { 5817 $from_charset = "UTF-8"; 5818 $to_charset = $lang->settings['charset']; 5819 } 5820 if($use_iconv) 5821 { 5822 return iconv($from_charset, $to_charset."//IGNORE", $str); 5823 } 5824 else 5825 { 5826 return @mb_convert_encoding($str, $to_charset, $from_charset); 5827 } 5828 } 5829 elseif($charset == "iso-8859-1" && function_exists("utf8_encode")) 5830 { 5831 if($to) 5832 { 5833 return utf8_encode($str); 5834 } 5835 else 5836 { 5837 return utf8_decode($str); 5838 } 5839 } 5840 else 5841 { 5842 return $str; 5843 } 5844 } 5845 5846 /** 5847 * DEPRECATED! Please use other alternatives. 5848 * 5849 * @deprecated 5850 * @param string $message 5851 * 5852 * @return string 5853 */ 5854 function my_wordwrap($message) 5855 { 5856 return $message; 5857 } 5858 5859 /** 5860 * Workaround for date limitation in PHP to establish the day of a birthday (Provided by meme) 5861 * 5862 * @param int $month The month of the birthday 5863 * @param int $day The day of the birthday 5864 * @param int $year The year of the bithday 5865 * @return int The numeric day of the week for the birthday 5866 */ 5867 function get_weekday($month, $day, $year) 5868 { 5869 $h = 4; 5870 5871 for($i = 1969; $i >= $year; $i--) 5872 { 5873 $j = get_bdays($i); 5874 5875 for($k = 11; $k >= 0; $k--) 5876 { 5877 $l = ($k + 1); 5878 5879 for($m = $j[$k]; $m >= 1; $m--) 5880 { 5881 $h--; 5882 5883 if($i == $year && $l == $month && $m == $day) 5884 { 5885 return $h; 5886 } 5887 5888 if($h == 0) 5889 { 5890 $h = 7; 5891 } 5892 } 5893 } 5894 } 5895 } 5896 5897 /** 5898 * Workaround for date limitation in PHP to establish the day of a birthday (Provided by meme) 5899 * 5900 * @param int $in The year. 5901 * @return array The number of days in each month of that year 5902 */ 5903 function get_bdays($in) 5904 { 5905 return array( 5906 31, 5907 ($in % 4 == 0 && ($in % 100 > 0 || $in % 400 == 0) ? 29 : 28), 5908 31, 5909 30, 5910 31, 5911 30, 5912 31, 5913 31, 5914 30, 5915 31, 5916 30, 5917 31 5918 ); 5919 } 5920 5921 /** 5922 * DEPRECATED! Please use mktime()! 5923 * Formats a birthday appropriately 5924 * 5925 * @deprecated 5926 * @param string $display The PHP date format string 5927 * @param int $bm The month of the birthday 5928 * @param int $bd The day of the birthday 5929 * @param int $by The year of the birthday 5930 * @param int $wd The weekday of the birthday 5931 * @return string The formatted birthday 5932 */ 5933 function format_bdays($display, $bm, $bd, $by, $wd) 5934 { 5935 global $lang; 5936 5937 $bdays = array( 5938 $lang->sunday, 5939 $lang->monday, 5940 $lang->tuesday, 5941 $lang->wednesday, 5942 $lang->thursday, 5943 $lang->friday, 5944 $lang->saturday 5945 ); 5946 5947 $bmonth = array( 5948 $lang->month_1, 5949 $lang->month_2, 5950 $lang->month_3, 5951 $lang->month_4, 5952 $lang->month_5, 5953 $lang->month_6, 5954 $lang->month_7, 5955 $lang->month_8, 5956 $lang->month_9, 5957 $lang->month_10, 5958 $lang->month_11, 5959 $lang->month_12 5960 ); 5961 5962 // This needs to be in this specific order 5963 $find = array( 5964 'm', 5965 'n', 5966 'd', 5967 'D', 5968 'y', 5969 'Y', 5970 'j', 5971 'S', 5972 'F', 5973 'l', 5974 'M', 5975 ); 5976 5977 $html = array( 5978 'm', 5979 'n', 5980 'c', 5981 'D', 5982 'y', 5983 'Y', 5984 'j', 5985 'S', 5986 'F', 5987 'l', 5988 'M', 5989 ); 5990 5991 $bdays = str_replace($find, $html, $bdays); 5992 $bmonth = str_replace($find, $html, $bmonth); 5993 5994 $replace = array( 5995 sprintf('%02s', $bm), 5996 $bm, 5997 sprintf('%02s', $bd), 5998 ($wd == 2 ? my_substr($bdays[$wd], 0, 4) : ($wd == 4 ? my_substr($bdays[$wd], 0, 5) : my_substr($bdays[$wd], 0, 3))), 5999 my_substr($by, 2), 6000 $by, 6001 ($bd[0] == 0 ? my_substr($bd, 1) : $bd), 6002 ($bd == 1 || $bd == 21 || $bd == 31 ? 'st' : ($bd == 2 || $bd == 22 ? 'nd' : ($bd == 3 || $bd == 23 ? 'rd' : 'th'))), 6003 $bmonth[$bm-1], 6004 $wd, 6005 ($bm == 9 ? my_substr($bmonth[$bm-1], 0, 4) : my_substr($bmonth[$bm-1], 0, 3)), 6006 ); 6007 6008 // Do we have the full month in our output? 6009 // If so there's no need for the short month 6010 if(strpos($display, 'F') !== false) 6011 { 6012 array_pop($find); 6013 array_pop($replace); 6014 } 6015 6016 return str_replace($find, $replace, $display); 6017 } 6018 6019 /** 6020 * Returns the age of a user with specified birthday. 6021 * 6022 * @param string $birthday The birthday of a user. 6023 * @return int The age of a user with that birthday. 6024 */ 6025 function get_age($birthday) 6026 { 6027 $bday = explode("-", $birthday); 6028 if(!$bday[2]) 6029 { 6030 return; 6031 } 6032 6033 list($day, $month, $year) = explode("-", my_date("j-n-Y", TIME_NOW, 0, 0)); 6034 6035 $age = $year-$bday[2]; 6036 6037 if(($month == $bday[1] && $day < $bday[0]) || $month < $bday[1]) 6038 { 6039 --$age; 6040 } 6041 return $age; 6042 } 6043 6044 /** 6045 * Updates the first posts in a thread. 6046 * 6047 * @param int $tid The thread id for which to update the first post id. 6048 */ 6049 function update_first_post($tid) 6050 { 6051 global $db; 6052 6053 $query = $db->query(" 6054 SELECT u.uid, u.username, p.pid, p.username AS postusername, p.dateline 6055 FROM ".TABLE_PREFIX."posts p 6056 LEFT JOIN ".TABLE_PREFIX."users u ON (u.uid=p.uid) 6057 WHERE p.tid='$tid' 6058 ORDER BY p.dateline ASC, p.pid ASC 6059 LIMIT 1 6060 "); 6061 $firstpost = $db->fetch_array($query); 6062 6063 if(empty($firstpost['username'])) 6064 { 6065 $firstpost['username'] = $firstpost['postusername']; 6066 } 6067 $firstpost['username'] = $db->escape_string($firstpost['username']); 6068 6069 $update_array = array( 6070 'firstpost' => (int)$firstpost['pid'], 6071 'username' => $firstpost['username'], 6072 'uid' => (int)$firstpost['uid'], 6073 'dateline' => (int)$firstpost['dateline'] 6074 ); 6075 $db->update_query("threads", $update_array, "tid='{$tid}'"); 6076 } 6077 6078 /** 6079 * Updates the last posts in a thread. 6080 * 6081 * @param int $tid The thread id for which to update the last post id. 6082 */ 6083 function update_last_post($tid) 6084 { 6085 global $db; 6086 6087 $query = $db->query(" 6088 SELECT u.uid, u.username, p.username AS postusername, p.dateline 6089 FROM ".TABLE_PREFIX."posts p 6090 LEFT JOIN ".TABLE_PREFIX."users u ON (u.uid=p.uid) 6091 WHERE p.tid='$tid' AND p.visible='1' 6092 ORDER BY p.dateline DESC, p.pid DESC 6093 LIMIT 1" 6094 ); 6095 $lastpost = $db->fetch_array($query); 6096 6097 if(!$lastpost) 6098 { 6099 return false; 6100 } 6101 6102 if(empty($lastpost['username'])) 6103 { 6104 $lastpost['username'] = $lastpost['postusername']; 6105 } 6106 6107 if(empty($lastpost['dateline'])) 6108 { 6109 $query = $db->query(" 6110 SELECT u.uid, u.username, p.pid, p.username AS postusername, p.dateline 6111 FROM ".TABLE_PREFIX."posts p 6112 LEFT JOIN ".TABLE_PREFIX."users u ON (u.uid=p.uid) 6113 WHERE p.tid='$tid' 6114 ORDER BY p.dateline ASC, p.pid ASC 6115 LIMIT 1 6116 "); 6117 $firstpost = $db->fetch_array($query); 6118 6119 $lastpost['username'] = $firstpost['username']; 6120 $lastpost['uid'] = $firstpost['uid']; 6121 $lastpost['dateline'] = $firstpost['dateline']; 6122 } 6123 6124 $lastpost['username'] = $db->escape_string($lastpost['username']); 6125 6126 $update_array = array( 6127 'lastpost' => (int)$lastpost['dateline'], 6128 'lastposter' => $lastpost['username'], 6129 'lastposteruid' => (int)$lastpost['uid'] 6130 ); 6131 $db->update_query("threads", $update_array, "tid='{$tid}'"); 6132 } 6133 6134 /** 6135 * Checks for the length of a string, mb strings accounted for 6136 * 6137 * @param string $string The string to check the length of. 6138 * @return int The length of the string. 6139 */ 6140 function my_strlen($string) 6141 { 6142 global $lang; 6143 6144 $string = preg_replace("#&\#([0-9]+);#", "-", $string); 6145 6146 if(isset($lang->settings['charset']) && strtolower($lang->settings['charset']) == "utf-8") 6147 { 6148 // Get rid of any excess RTL and LTR override for they are the workings of the devil 6149 $string = str_replace(dec_to_utf8(8238), "", $string); 6150 $string = str_replace(dec_to_utf8(8237), "", $string); 6151 6152 // Remove dodgy whitespaces 6153 $string = str_replace(chr(0xCA), "", $string); 6154 } 6155 $string = trim($string); 6156 6157 if(function_exists("mb_strlen")) 6158 { 6159 $string_length = mb_strlen($string); 6160 } 6161 else 6162 { 6163 $string_length = strlen($string); 6164 } 6165 6166 return $string_length; 6167 } 6168 6169 /** 6170 * Cuts a string at a specified point, mb strings accounted for 6171 * 6172 * @param string $string The string to cut. 6173 * @param int $start Where to cut 6174 * @param int $length (optional) How much to cut 6175 * @param bool $handle_entities (optional) Properly handle HTML entities? 6176 * @return string The cut part of the string. 6177 */ 6178 function my_substr($string, $start, $length=null, $handle_entities = false) 6179 { 6180 if($handle_entities) 6181 { 6182 $string = unhtmlentities($string); 6183 } 6184 if(function_exists("mb_substr")) 6185 { 6186 if($length != null) 6187 { 6188 $cut_string = mb_substr($string, $start, $length); 6189 } 6190 else 6191 { 6192 $cut_string = mb_substr($string, $start); 6193 } 6194 } 6195 else 6196 { 6197 if($length != null) 6198 { 6199 $cut_string = substr($string, $start, $length); 6200 } 6201 else 6202 { 6203 $cut_string = substr($string, $start); 6204 } 6205 } 6206 6207 if($handle_entities) 6208 { 6209 $cut_string = htmlspecialchars_uni($cut_string); 6210 } 6211 return $cut_string; 6212 } 6213 6214 /** 6215 * Lowers the case of a string, mb strings accounted for 6216 * 6217 * @param string $string The string to lower. 6218 * @return string The lowered string. 6219 */ 6220 function my_strtolower($string) 6221 { 6222 if(function_exists("mb_strtolower")) 6223 { 6224 $string = mb_strtolower($string); 6225 } 6226 else 6227 { 6228 $string = strtolower($string); 6229 } 6230 6231 return $string; 6232 } 6233 6234 /** 6235 * Finds a needle in a haystack and returns it position, mb strings accounted for, case insensitive 6236 * 6237 * @param string $haystack String to look in (haystack) 6238 * @param string $needle What to look for (needle) 6239 * @param int $offset (optional) How much to offset 6240 * @return int|bool false on needle not found, integer position if found 6241 */ 6242 function my_stripos($haystack, $needle, $offset=0) 6243 { 6244 if($needle == '') 6245 { 6246 return false; 6247 } 6248 6249 if(function_exists("mb_stripos")) 6250 { 6251 $position = mb_stripos($haystack, $needle, $offset); 6252 } 6253 else 6254 { 6255 $position = stripos($haystack, $needle, $offset); 6256 } 6257 6258 return $position; 6259 } 6260 6261 /** 6262 * Finds a needle in a haystack and returns it position, mb strings accounted for 6263 * 6264 * @param string $haystack String to look in (haystack) 6265 * @param string $needle What to look for (needle) 6266 * @param int $offset (optional) How much to offset 6267 * @return int|bool false on needle not found, integer position if found 6268 */ 6269 function my_strpos($haystack, $needle, $offset=0) 6270 { 6271 if($needle == '') 6272 { 6273 return false; 6274 } 6275 6276 if(function_exists("mb_strpos")) 6277 { 6278 $position = mb_strpos($haystack, $needle, $offset); 6279 } 6280 else 6281 { 6282 $position = strpos($haystack, $needle, $offset); 6283 } 6284 6285 return $position; 6286 } 6287 6288 /** 6289 * Ups the case of a string, mb strings accounted for 6290 * 6291 * @param string $string The string to up. 6292 * @return string The uped string. 6293 */ 6294 function my_strtoupper($string) 6295 { 6296 if(function_exists("mb_strtoupper")) 6297 { 6298 $string = mb_strtoupper($string); 6299 } 6300 else 6301 { 6302 $string = strtoupper($string); 6303 } 6304 6305 return $string; 6306 } 6307 6308 /** 6309 * Returns any html entities to their original character 6310 * 6311 * @param string $string The string to un-htmlentitize. 6312 * @return string The un-htmlentitied' string. 6313 */ 6314 function unhtmlentities($string) 6315 { 6316 // Replace numeric entities 6317 $string = preg_replace_callback('~&#x([0-9a-f]+);~i', 'unichr_callback1', $string); 6318 $string = preg_replace_callback('~&#([0-9]+);~', 'unichr_callback2', $string); 6319 6320 // Replace literal entities 6321 $trans_tbl = get_html_translation_table(HTML_ENTITIES); 6322 $trans_tbl = array_flip($trans_tbl); 6323 6324 return strtr($string, $trans_tbl); 6325 } 6326 6327 /** 6328 * Returns any ascii to it's character (utf-8 safe). 6329 * 6330 * @param int $c The ascii to characterize. 6331 * @return string|bool The characterized ascii. False on failure 6332 */ 6333 function unichr($c) 6334 { 6335 if($c <= 0x7F) 6336 { 6337 return chr($c); 6338 } 6339 else if($c <= 0x7FF) 6340 { 6341 return chr(0xC0 | $c >> 6) . chr(0x80 | $c & 0x3F); 6342 } 6343 else if($c <= 0xFFFF) 6344 { 6345 return chr(0xE0 | $c >> 12) . chr(0x80 | $c >> 6 & 0x3F) 6346 . chr(0x80 | $c & 0x3F); 6347 } 6348 else if($c <= 0x10FFFF) 6349 { 6350 return chr(0xF0 | $c >> 18) . chr(0x80 | $c >> 12 & 0x3F) 6351 . chr(0x80 | $c >> 6 & 0x3F) 6352 . chr(0x80 | $c & 0x3F); 6353 } 6354 else 6355 { 6356 return false; 6357 } 6358 } 6359 6360 /** 6361 * Returns any ascii to it's character (utf-8 safe). 6362 * 6363 * @param array $matches Matches. 6364 * @return string|bool The characterized ascii. False on failure 6365 */ 6366 function unichr_callback1($matches) 6367 { 6368 return unichr(hexdec($matches[1])); 6369 } 6370 6371 /** 6372 * Returns any ascii to it's character (utf-8 safe). 6373 * 6374 * @param array $matches Matches. 6375 * @return string|bool The characterized ascii. False on failure 6376 */ 6377 function unichr_callback2($matches) 6378 { 6379 return unichr($matches[1]); 6380 } 6381 6382 /** 6383 * Get the event poster. 6384 * 6385 * @param array $event The event data array. 6386 * @return string The link to the event poster. 6387 */ 6388 function get_event_poster($event) 6389 { 6390 $event['username'] = htmlspecialchars_uni($event['username']); 6391 $event['username'] = format_name($event['username'], $event['usergroup'], $event['displaygroup']); 6392 $event_poster = build_profile_link($event['username'], $event['author']); 6393 return $event_poster; 6394 } 6395 6396 /** 6397 * Get the event date. 6398 * 6399 * @param array $event The event data array. 6400 * @return string The event date. 6401 */ 6402 function get_event_date($event) 6403 { 6404 global $mybb; 6405 6406 $event_date = explode("-", $event['date']); 6407 $event_date = gmmktime(0, 0, 0, $event_date[1], $event_date[0], $event_date[2]); 6408 $event_date = my_date($mybb->settings['dateformat'], $event_date); 6409 6410 return $event_date; 6411 } 6412 6413 /** 6414 * Get the profile link. 6415 * 6416 * @param int $uid The user id of the profile. 6417 * @return string The url to the profile. 6418 */ 6419 function get_profile_link($uid=0) 6420 { 6421 $link = str_replace("{uid}", $uid, PROFILE_URL); 6422 return htmlspecialchars_uni($link); 6423 } 6424 6425 /** 6426 * Get the announcement link. 6427 * 6428 * @param int $aid The announement id of the announcement. 6429 * @return string The url to the announcement. 6430 */ 6431 function get_announcement_link($aid=0) 6432 { 6433 $link = str_replace("{aid}", $aid, ANNOUNCEMENT_URL); 6434 return htmlspecialchars_uni($link); 6435 } 6436 6437 /** 6438 * Build the profile link. 6439 * 6440 * @param string $username The Username of the profile. 6441 * @param int $uid The user id of the profile. 6442 * @param string $target The target frame 6443 * @param string $onclick Any onclick javascript. 6444 * @return string The complete profile link. 6445 */ 6446 function build_profile_link($username="", $uid=0, $target="", $onclick="") 6447 { 6448 global $mybb, $lang; 6449 6450 if(!$username && $uid == 0) 6451 { 6452 // Return Guest phrase for no UID, no guest nickname 6453 return htmlspecialchars_uni($lang->guest); 6454 } 6455 elseif($uid == 0) 6456 { 6457 // Return the guest's nickname if user is a guest but has a nickname 6458 return $username; 6459 } 6460 else 6461 { 6462 // Build the profile link for the registered user 6463 if(!empty($target)) 6464 { 6465 $target = " target=\"{$target}\""; 6466 } 6467 6468 if(!empty($onclick)) 6469 { 6470 $onclick = " onclick=\"{$onclick}\""; 6471 } 6472 6473 return "<a href=\"{$mybb->settings['bburl']}/".get_profile_link($uid)."\"{$target}{$onclick}>{$username}</a>"; 6474 } 6475 } 6476 6477 /** 6478 * Build the forum link. 6479 * 6480 * @param int $fid The forum id of the forum. 6481 * @param int $page (Optional) The page number of the forum. 6482 * @return string The url to the forum. 6483 */ 6484 function get_forum_link($fid, $page=0) 6485 { 6486 if($page > 0) 6487 { 6488 $link = str_replace("{fid}", $fid, FORUM_URL_PAGED); 6489 $link = str_replace("{page}", $page, $link); 6490 return htmlspecialchars_uni($link); 6491 } 6492 else 6493 { 6494 $link = str_replace("{fid}", $fid, FORUM_URL); 6495 return htmlspecialchars_uni($link); 6496 } 6497 } 6498 6499 /** 6500 * Build the thread link. 6501 * 6502 * @param int $tid The thread id of the thread. 6503 * @param int $page (Optional) The page number of the thread. 6504 * @param string $action (Optional) The action we're performing (ex, lastpost, newpost, etc) 6505 * @return string The url to the thread. 6506 */ 6507 function get_thread_link($tid, $page=0, $action='') 6508 { 6509 if($page > 1) 6510 { 6511 if($action) 6512 { 6513 $link = THREAD_URL_ACTION; 6514 $link = str_replace("{action}", $action, $link); 6515 } 6516 else 6517 { 6518 $link = THREAD_URL_PAGED; 6519 } 6520 $link = str_replace("{tid}", $tid, $link); 6521 $link = str_replace("{page}", $page, $link); 6522 return htmlspecialchars_uni($link); 6523 } 6524 else 6525 { 6526 if($action) 6527 { 6528 $link = THREAD_URL_ACTION; 6529 $link = str_replace("{action}", $action, $link); 6530 } 6531 else 6532 { 6533 $link = THREAD_URL; 6534 } 6535 $link = str_replace("{tid}", $tid, $link); 6536 return htmlspecialchars_uni($link); 6537 } 6538 } 6539 6540 /** 6541 * Build the post link. 6542 * 6543 * @param int $pid The post ID of the post 6544 * @param int $tid The thread id of the post. 6545 * @return string The url to the post. 6546 */ 6547 function get_post_link($pid, $tid=0) 6548 { 6549 if($tid > 0) 6550 { 6551 $link = str_replace("{tid}", $tid, THREAD_URL_POST); 6552 $link = str_replace("{pid}", $pid, $link); 6553 return htmlspecialchars_uni($link); 6554 } 6555 else 6556 { 6557 $link = str_replace("{pid}", $pid, POST_URL); 6558 return htmlspecialchars_uni($link); 6559 } 6560 } 6561 6562 /** 6563 * Build the event link. 6564 * 6565 * @param int $eid The event ID of the event 6566 * @return string The URL of the event 6567 */ 6568 function get_event_link($eid) 6569 { 6570 $link = str_replace("{eid}", $eid, EVENT_URL); 6571 return htmlspecialchars_uni($link); 6572 } 6573 6574 /** 6575 * Build the link to a specified date on the calendar 6576 * 6577 * @param int $calendar The ID of the calendar 6578 * @param int $year The year 6579 * @param int $month The month 6580 * @param int $day The day (optional) 6581 * @return string The URL of the calendar 6582 */ 6583 function get_calendar_link($calendar, $year=0, $month=0, $day=0) 6584 { 6585 if($day > 0) 6586 { 6587 $link = str_replace("{month}", $month, CALENDAR_URL_DAY); 6588 $link = str_replace("{year}", $year, $link); 6589 $link = str_replace("{day}", $day, $link); 6590 $link = str_replace("{calendar}", $calendar, $link); 6591 return htmlspecialchars_uni($link); 6592 } 6593 else if($month > 0) 6594 { 6595 $link = str_replace("{month}", $month, CALENDAR_URL_MONTH); 6596 $link = str_replace("{year}", $year, $link); 6597 $link = str_replace("{calendar}", $calendar, $link); 6598 return htmlspecialchars_uni($link); 6599 } 6600 /* Not implemented 6601 else if($year > 0) 6602 { 6603 }*/ 6604 else 6605 { 6606 $link = str_replace("{calendar}", $calendar, CALENDAR_URL); 6607 return htmlspecialchars_uni($link); 6608 } 6609 } 6610 6611 /** 6612 * Build the link to a specified week on the calendar 6613 * 6614 * @param int $calendar The ID of the calendar 6615 * @param int $week The week 6616 * @return string The URL of the calendar 6617 */ 6618 function get_calendar_week_link($calendar, $week) 6619 { 6620 if($week < 0) 6621 { 6622 $week = str_replace('-', "n", $week); 6623 } 6624 $link = str_replace("{week}", $week, CALENDAR_URL_WEEK); 6625 $link = str_replace("{calendar}", $calendar, $link); 6626 return htmlspecialchars_uni($link); 6627 } 6628 6629 /** 6630 * Get the user data of an user id. 6631 * 6632 * @param int $uid The user id of the user. 6633 * @return array The users data 6634 */ 6635 function get_user($uid) 6636 { 6637 global $mybb, $db; 6638 static $user_cache; 6639 6640 $uid = (int)$uid; 6641 6642 if(!empty($mybb->user) && $uid == $mybb->user['uid']) 6643 { 6644 return $mybb->user; 6645 } 6646 elseif(isset($user_cache[$uid])) 6647 { 6648 return $user_cache[$uid]; 6649 } 6650 elseif($uid > 0) 6651 { 6652 $query = $db->simple_select("users", "*", "uid = '{$uid}'"); 6653 $user_cache[$uid] = $db->fetch_array($query); 6654 6655 return $user_cache[$uid]; 6656 } 6657 return array(); 6658 } 6659 6660 /** 6661 * Get the user data of an user username. 6662 * 6663 * @param string $username The user username of the user. 6664 * @param array $options 6665 * @return array The users data 6666 */ 6667 function get_user_by_username($username, $options=array()) 6668 { 6669 global $mybb, $db; 6670 6671 $username = $db->escape_string(my_strtolower($username)); 6672 6673 if(!isset($options['username_method'])) 6674 { 6675 $options['username_method'] = 0; 6676 } 6677 6678 switch($db->type) 6679 { 6680 case 'mysql': 6681 case 'mysqli': 6682 $field = 'username'; 6683 $efield = 'email'; 6684 break; 6685 default: 6686 $field = 'LOWER(username)'; 6687 $efield = 'LOWER(email)'; 6688 break; 6689 } 6690 6691 switch($options['username_method']) 6692 { 6693 case 1: 6694 $sqlwhere = "{$efield}='{$username}'"; 6695 break; 6696 case 2: 6697 $sqlwhere = "{$field}='{$username}' OR {$efield}='{$username}'"; 6698 break; 6699 default: 6700 $sqlwhere = "{$field}='{$username}'"; 6701 break; 6702 } 6703 6704 $fields = array('uid'); 6705 if(isset($options['fields'])) 6706 { 6707 $fields = array_merge((array)$options['fields'], $fields); 6708 } 6709 6710 $query = $db->simple_select('users', implode(',', array_unique($fields)), $sqlwhere, array('limit' => 1)); 6711 6712 if(isset($options['exists'])) 6713 { 6714 return (bool)$db->num_rows($query); 6715 } 6716 6717 return $db->fetch_array($query); 6718 } 6719 6720 /** 6721 * Get the forum of a specific forum id. 6722 * 6723 * @param int $fid The forum id of the forum. 6724 * @param int $active_override (Optional) If set to 1, will override the active forum status 6725 * @return array|bool The database row of a forum. False on failure 6726 */ 6727 function get_forum($fid, $active_override=0) 6728 { 6729 global $cache; 6730 static $forum_cache; 6731 6732 if(!isset($forum_cache) || !is_array($forum_cache)) 6733 { 6734 $forum_cache = $cache->read("forums"); 6735 } 6736 6737 if(empty($forum_cache[$fid])) 6738 { 6739 return false; 6740 } 6741 6742 if($active_override != 1) 6743 { 6744 $parents = explode(",", $forum_cache[$fid]['parentlist']); 6745 if(is_array($parents)) 6746 { 6747 foreach($parents as $parent) 6748 { 6749 if($forum_cache[$parent]['active'] == 0) 6750 { 6751 return false; 6752 } 6753 } 6754 } 6755 } 6756 6757 return $forum_cache[$fid]; 6758 } 6759 6760 /** 6761 * Get the thread of a thread id. 6762 * 6763 * @param int $tid The thread id of the thread. 6764 * @param boolean $recache Whether or not to recache the thread. 6765 * @return array|bool The database row of the thread. False on failure 6766 */ 6767 function get_thread($tid, $recache = false) 6768 { 6769 global $db; 6770 static $thread_cache; 6771 6772 $tid = (int)$tid; 6773 6774 if(isset($thread_cache[$tid]) && !$recache) 6775 { 6776 return $thread_cache[$tid]; 6777 } 6778 else 6779 { 6780 $query = $db->simple_select("threads", "*", "tid = '{$tid}'"); 6781 $thread = $db->fetch_array($query); 6782 6783 if($thread) 6784 { 6785 $thread_cache[$tid] = $thread; 6786 return $thread; 6787 } 6788 else 6789 { 6790 $thread_cache[$tid] = false; 6791 return false; 6792 } 6793 } 6794 } 6795 6796 /** 6797 * Get the post of a post id. 6798 * 6799 * @param int $pid The post id of the post. 6800 * @return array|bool The database row of the post. False on failure 6801 */ 6802 function get_post($pid) 6803 { 6804 global $db; 6805 static $post_cache; 6806 6807 $pid = (int)$pid; 6808 6809 if(isset($post_cache[$pid])) 6810 { 6811 return $post_cache[$pid]; 6812 } 6813 else 6814 { 6815 $query = $db->simple_select("posts", "*", "pid = '{$pid}'"); 6816 $post = $db->fetch_array($query); 6817 6818 if($post) 6819 { 6820 $post_cache[$pid] = $post; 6821 return $post; 6822 } 6823 else 6824 { 6825 $post_cache[$pid] = false; 6826 return false; 6827 } 6828 } 6829 } 6830 6831 /** 6832 * Get inactivate forums. 6833 * 6834 * @return string The comma separated values of the inactivate forum. 6835 */ 6836 function get_inactive_forums() 6837 { 6838 global $forum_cache, $cache; 6839 6840 if(!$forum_cache) 6841 { 6842 cache_forums(); 6843 } 6844 6845 $inactive = array(); 6846 6847 foreach($forum_cache as $fid => $forum) 6848 { 6849 if($forum['active'] == 0) 6850 { 6851 $inactive[] = $fid; 6852 foreach($forum_cache as $fid1 => $forum1) 6853 { 6854 if(my_strpos(",".$forum1['parentlist'].",", ",".$fid.",") !== false && !in_array($fid1, $inactive)) 6855 { 6856 $inactive[] = $fid1; 6857 } 6858 } 6859 } 6860 } 6861 6862 $inactiveforums = implode(",", $inactive); 6863 6864 return $inactiveforums; 6865 } 6866 6867 /** 6868 * Checks to make sure a user has not tried to login more times than permitted 6869 * 6870 * @param bool $fatal (Optional) Stop execution if it finds an error with the login. Default is True 6871 * @return bool|int Number of logins when success, false if failed. 6872 */ 6873 function login_attempt_check($uid = 0, $fatal = true) 6874 { 6875 global $mybb, $lang, $db; 6876 6877 $attempts = array(); 6878 $uid = (int)$uid; 6879 $now = TIME_NOW; 6880 6881 // Get this user's login attempts and eventual lockout, if a uid is provided 6882 if($uid > 0) 6883 { 6884 $query = $db->simple_select("users", "loginattempts, loginlockoutexpiry", "uid='{$uid}'", 1); 6885 $attempts = $db->fetch_array($query); 6886 6887 if($attempts['loginattempts'] <= 0) 6888 { 6889 return 0; 6890 } 6891 } 6892 // This user has a cookie lockout, show waiting time 6893 elseif(!empty($mybb->cookies['lockoutexpiry']) && $mybb->cookies['lockoutexpiry'] > $now) 6894 { 6895 if($fatal) 6896 { 6897 $secsleft = (int)($mybb->cookies['lockoutexpiry'] - $now); 6898 $hoursleft = floor($secsleft / 3600); 6899 $minsleft = floor(($secsleft / 60) % 60); 6900 $secsleft = floor($secsleft % 60); 6901 6902 error($lang->sprintf($lang->failed_login_wait, $hoursleft, $minsleft, $secsleft)); 6903 } 6904 6905 return false; 6906 } 6907 6908 if($mybb->settings['failedlogincount'] > 0 && isset($attempts['loginattempts']) && $attempts['loginattempts'] >= $mybb->settings['failedlogincount']) 6909 { 6910 // Set the expiry dateline if not set yet 6911 if($attempts['loginlockoutexpiry'] == 0) 6912 { 6913 $attempts['loginlockoutexpiry'] = $now + ((int)$mybb->settings['failedlogintime'] * 60); 6914 6915 // Add a cookie lockout. This is used to prevent access to the login page immediately. 6916 // A deep lockout is issued if he tries to login into a locked out account 6917 my_setcookie('lockoutexpiry', $attempts['loginlockoutexpiry']); 6918 6919 $db->update_query("users", array( 6920 "loginlockoutexpiry" => $attempts['loginlockoutexpiry'] 6921 ), "uid='{$uid}'"); 6922 } 6923 6924 if(empty($mybb->cookies['lockoutexpiry'])) 6925 { 6926 $failedtime = $attempts['loginlockoutexpiry']; 6927 } 6928 else 6929 { 6930 $failedtime = $mybb->cookies['lockoutexpiry']; 6931 } 6932 6933 // Are we still locked out? 6934 if($attempts['loginlockoutexpiry'] > $now) 6935 { 6936 if($fatal) 6937 { 6938 $secsleft = (int)($attempts['loginlockoutexpiry'] - $now); 6939 $hoursleft = floor($secsleft / 3600); 6940 $minsleft = floor(($secsleft / 60) % 60); 6941 $secsleft = floor($secsleft % 60); 6942 6943 error($lang->sprintf($lang->failed_login_wait, $hoursleft, $minsleft, $secsleft)); 6944 } 6945 6946 return false; 6947 } 6948 // Unlock if enough time has passed 6949 else { 6950 6951 if($uid > 0) 6952 { 6953 $db->update_query("users", array( 6954 "loginattempts" => 0, 6955 "loginlockoutexpiry" => 0 6956 ), "uid='{$uid}'"); 6957 } 6958 6959 // Wipe the cookie, no matter if a guest or a member 6960 my_unsetcookie('lockoutexpiry'); 6961 6962 return 0; 6963 } 6964 } 6965 6966 if(!isset($attempts['loginattempts'])) 6967 { 6968 $attempts['loginattempts'] = 0; 6969 } 6970 6971 // User can attempt another login 6972 return $attempts['loginattempts']; 6973 } 6974 6975 /** 6976 * Validates the format of an email address. 6977 * 6978 * @param string $email The string to check. 6979 * @return boolean True when valid, false when invalid. 6980 */ 6981 function validate_email_format($email) 6982 { 6983 return filter_var($email, FILTER_VALIDATE_EMAIL) !== false; 6984 } 6985 6986 /** 6987 * Checks to see if the email is already in use by another 6988 * 6989 * @param string $email The email to check. 6990 * @param int $uid User ID of the user (updating only) 6991 * @return boolean True when in use, false when not. 6992 */ 6993 function email_already_in_use($email, $uid=0) 6994 { 6995 global $db; 6996 6997 $uid_string = ""; 6998 if($uid) 6999 { 7000 $uid_string = " AND uid != '".(int)$uid."'"; 7001 } 7002 $query = $db->simple_select("users", "COUNT(email) as emails", "email = '".$db->escape_string($email)."'{$uid_string}"); 7003 7004 if($db->fetch_field($query, "emails") > 0) 7005 { 7006 return true; 7007 } 7008 7009 return false; 7010 } 7011 7012 /** 7013 * Rebuilds settings.php 7014 * 7015 */ 7016 function rebuild_settings() 7017 { 7018 global $db, $mybb; 7019 7020 $query = $db->simple_select("settings", "value, name", "", array( 7021 'order_by' => 'title', 7022 'order_dir' => 'ASC', 7023 )); 7024 7025 $settings = ''; 7026 while($setting = $db->fetch_array($query)) 7027 { 7028 $mybb->settings[$setting['name']] = $setting['value']; 7029 7030 $setting['name'] = addcslashes($setting['name'], "\\'"); 7031 $setting['value'] = addcslashes($setting['value'], '\\"$'); 7032 $settings .= "\$settings['{$setting['name']}'] = \"{$setting['value']}\";\n"; 7033 } 7034 7035 $settings = "<"."?php\n/*********************************\ \n DO NOT EDIT THIS FILE, PLEASE USE\n THE SETTINGS EDITOR\n\*********************************/\n\n$settings\n"; 7036 7037 file_put_contents(MYBB_ROOT.'inc/settings.php', $settings, LOCK_EX); 7038 7039 $GLOBALS['settings'] = &$mybb->settings; 7040 } 7041 7042 /** 7043 * Build a PREG compatible array of search highlight terms to replace in posts. 7044 * 7045 * @param string $terms Incoming terms to highlight 7046 * @return array PREG compatible array of terms 7047 */ 7048 function build_highlight_array($terms) 7049 { 7050 global $mybb; 7051 7052 if($mybb->settings['minsearchword'] < 1) 7053 { 7054 $mybb->settings['minsearchword'] = 3; 7055 } 7056 7057 if(is_array($terms)) 7058 { 7059 $terms = implode(' ', $terms); 7060 } 7061 7062 // Strip out any characters that shouldn't be included 7063 $bad_characters = array( 7064 "(", 7065 ")", 7066 "+", 7067 "-", 7068 "~" 7069 ); 7070 $terms = str_replace($bad_characters, '', $terms); 7071 $words = array(); 7072 7073 // Check if this is a "series of words" - should be treated as an EXACT match 7074 if(my_strpos($terms, "\"") !== false) 7075 { 7076 $inquote = false; 7077 $terms = explode("\"", $terms); 7078 foreach($terms as $phrase) 7079 { 7080 $phrase = htmlspecialchars_uni($phrase); 7081 if($phrase != "") 7082 { 7083 if($inquote) 7084 { 7085 $words[] = trim($phrase); 7086 } 7087 else 7088 { 7089 $split_words = preg_split("#\s{1,}#", $phrase, -1); 7090 if(!is_array($split_words)) 7091 { 7092 continue; 7093 } 7094 foreach($split_words as $word) 7095 { 7096 if(!$word || strlen($word) < $mybb->settings['minsearchword']) 7097 { 7098 continue; 7099 } 7100 $words[] = trim($word); 7101 } 7102 } 7103 } 7104 $inquote = !$inquote; 7105 } 7106 } 7107 // Otherwise just a simple search query with no phrases 7108 else 7109 { 7110 $terms = htmlspecialchars_uni($terms); 7111 $split_words = preg_split("#\s{1,}#", $terms, -1); 7112 if(is_array($split_words)) 7113 { 7114 foreach($split_words as $word) 7115 { 7116 if(!$word || strlen($word) < $mybb->settings['minsearchword']) 7117 { 7118 continue; 7119 } 7120 $words[] = trim($word); 7121 } 7122 } 7123 } 7124 7125 // Sort the word array by length. Largest terms go first and work their way down to the smallest term. 7126 // This resolves problems like "test tes" where "tes" will be highlighted first, then "test" can't be highlighted because of the changed html 7127 usort($words, 'build_highlight_array_sort'); 7128 7129 $highlight_cache = array(); 7130 7131 // Loop through our words to build the PREG compatible strings 7132 foreach($words as $word) 7133 { 7134 $word = trim($word); 7135 7136 $word = my_strtolower($word); 7137 7138 // Special boolean operators should be stripped 7139 if($word == "" || $word == "or" || $word == "not" || $word == "and") 7140 { 7141 continue; 7142 } 7143 7144 // Now make PREG compatible 7145 $find = "/(?<!&|&#)\b([[:alnum:]]*)(".preg_quote($word, "/").")(?![^<>]*?>)/ui"; 7146 $replacement = "$1<span class=\"highlight\" style=\"padding-left: 0px; padding-right: 0px;\">$2</span>"; 7147 $highlight_cache[$find] = $replacement; 7148 } 7149 7150 return $highlight_cache; 7151 } 7152 7153 /** 7154 * Sort the word array by length. Largest terms go first and work their way down to the smallest term. 7155 * 7156 * @param string $a First word. 7157 * @param string $b Second word. 7158 * @return integer Result of comparison function. 7159 */ 7160 function build_highlight_array_sort($a, $b) 7161 { 7162 return strlen($b) - strlen($a); 7163 } 7164 7165 /** 7166 * Converts a decimal reference of a character to its UTF-8 equivalent 7167 * (Code by Anne van Kesteren, http://annevankesteren.nl/2005/05/character-references) 7168 * 7169 * @param int $src Decimal value of a character reference 7170 * @return string|bool 7171 */ 7172 function dec_to_utf8($src) 7173 { 7174 $dest = ''; 7175 7176 if($src < 0) 7177 { 7178 return false; 7179 } 7180 elseif($src <= 0x007f) 7181 { 7182 $dest .= chr($src); 7183 } 7184 elseif($src <= 0x07ff) 7185 { 7186 $dest .= chr(0xc0 | ($src >> 6)); 7187 $dest .= chr(0x80 | ($src & 0x003f)); 7188 } 7189 elseif($src <= 0xffff) 7190 { 7191 $dest .= chr(0xe0 | ($src >> 12)); 7192 $dest .= chr(0x80 | (($src >> 6) & 0x003f)); 7193 $dest .= chr(0x80 | ($src & 0x003f)); 7194 } 7195 elseif($src <= 0x10ffff) 7196 { 7197 $dest .= chr(0xf0 | ($src >> 18)); 7198 $dest .= chr(0x80 | (($src >> 12) & 0x3f)); 7199 $dest .= chr(0x80 | (($src >> 6) & 0x3f)); 7200 $dest .= chr(0x80 | ($src & 0x3f)); 7201 } 7202 else 7203 { 7204 // Out of range 7205 return false; 7206 } 7207 7208 return $dest; 7209 } 7210 7211 /** 7212 * Checks if a username has been disallowed for registration/use. 7213 * 7214 * @param string $username The username 7215 * @param boolean $update_lastuse True if the 'last used' dateline should be updated if a match is found. 7216 * @return boolean True if banned, false if not banned 7217 */ 7218 function is_banned_username($username, $update_lastuse=false) 7219 { 7220 global $db; 7221 $query = $db->simple_select('banfilters', 'filter, fid', "type='2'"); 7222 while($banned_username = $db->fetch_array($query)) 7223 { 7224 // Make regular expression * match 7225 $banned_username['filter'] = str_replace('\*', '(.*)', preg_quote($banned_username['filter'], '#')); 7226 if(preg_match("#(^|\b){$banned_username['filter']}($|\b)#i", $username)) 7227 { 7228 // Updating last use 7229 if($update_lastuse == true) 7230 { 7231 $db->update_query("banfilters", array("lastuse" => TIME_NOW), "fid='{$banned_username['fid']}'"); 7232 } 7233 return true; 7234 } 7235 } 7236 // Still here - good username 7237 return false; 7238 } 7239 7240 /** 7241 * Check if a specific email address has been banned. 7242 * 7243 * @param string $email The email address. 7244 * @param boolean $update_lastuse True if the 'last used' dateline should be updated if a match is found. 7245 * @return boolean True if banned, false if not banned 7246 */ 7247 function is_banned_email($email, $update_lastuse=false) 7248 { 7249 global $cache, $db; 7250 7251 $banned_cache = $cache->read("bannedemails"); 7252 7253 if($banned_cache === false) 7254 { 7255 // Failed to read cache, see if we can rebuild it 7256 $cache->update_bannedemails(); 7257 $banned_cache = $cache->read("bannedemails"); 7258 } 7259 7260 if(is_array($banned_cache) && !empty($banned_cache)) 7261 { 7262 foreach($banned_cache as $banned_email) 7263 { 7264 // Make regular expression * match 7265 $banned_email['filter'] = str_replace('\*', '(.*)', preg_quote($banned_email['filter'], '#')); 7266 7267 if(preg_match("#{$banned_email['filter']}#i", $email)) 7268 { 7269 // Updating last use 7270 if($update_lastuse == true) 7271 { 7272 $db->update_query("banfilters", array("lastuse" => TIME_NOW), "fid='{$banned_email['fid']}'"); 7273 } 7274 return true; 7275 } 7276 } 7277 } 7278 7279 // Still here - good email 7280 return false; 7281 } 7282 7283 /** 7284 * Checks if a specific IP address has been banned. 7285 * 7286 * @param string $ip_address The IP address. 7287 * @param boolean $update_lastuse True if the 'last used' dateline should be updated if a match is found. 7288 * @return boolean True if banned, false if not banned. 7289 */ 7290 function is_banned_ip($ip_address, $update_lastuse=false) 7291 { 7292 global $db, $cache; 7293 7294 $banned_ips = $cache->read("bannedips"); 7295 if(!is_array($banned_ips)) 7296 { 7297 return false; 7298 } 7299 7300 $ip_address = my_inet_pton($ip_address); 7301 foreach($banned_ips as $banned_ip) 7302 { 7303 if(!$banned_ip['filter']) 7304 { 7305 continue; 7306 } 7307 7308 $banned = false; 7309 7310 $ip_range = fetch_ip_range($banned_ip['filter']); 7311 if(is_array($ip_range)) 7312 { 7313 if(strcmp($ip_range[0], $ip_address) <= 0 && strcmp($ip_range[1], $ip_address) >= 0) 7314 { 7315 $banned = true; 7316 } 7317 } 7318 elseif($ip_address == $ip_range) 7319 { 7320 $banned = true; 7321 } 7322 if($banned) 7323 { 7324 // Updating last use 7325 if($update_lastuse == true) 7326 { 7327 $db->update_query("banfilters", array("lastuse" => TIME_NOW), "fid='{$banned_ip['fid']}'"); 7328 } 7329 return true; 7330 } 7331 } 7332 7333 // Still here - good ip 7334 return false; 7335 } 7336 7337 /** 7338 * Returns an array of supported timezones 7339 * 7340 * @return string[] Key is timezone offset, Value the language description 7341 */ 7342 function get_supported_timezones() 7343 { 7344 global $lang; 7345 $timezones = array( 7346 "-12" => $lang->timezone_gmt_minus_1200, 7347 "-11" => $lang->timezone_gmt_minus_1100, 7348 "-10" => $lang->timezone_gmt_minus_1000, 7349 "-9.5" => $lang->timezone_gmt_minus_950, 7350 "-9" => $lang->timezone_gmt_minus_900, 7351 "-8" => $lang->timezone_gmt_minus_800, 7352 "-7" => $lang->timezone_gmt_minus_700, 7353 "-6" => $lang->timezone_gmt_minus_600, 7354 "-5" => $lang->timezone_gmt_minus_500, 7355 "-4.5" => $lang->timezone_gmt_minus_450, 7356 "-4" => $lang->timezone_gmt_minus_400, 7357 "-3.5" => $lang->timezone_gmt_minus_350, 7358 "-3" => $lang->timezone_gmt_minus_300, 7359 "-2" => $lang->timezone_gmt_minus_200, 7360 "-1" => $lang->timezone_gmt_minus_100, 7361 "0" => $lang->timezone_gmt, 7362 "1" => $lang->timezone_gmt_100, 7363 "2" => $lang->timezone_gmt_200, 7364 "3" => $lang->timezone_gmt_300, 7365 "3.5" => $lang->timezone_gmt_350, 7366 "4" => $lang->timezone_gmt_400, 7367 "4.5" => $lang->timezone_gmt_450, 7368 "5" => $lang->timezone_gmt_500, 7369 "5.5" => $lang->timezone_gmt_550, 7370 "5.75" => $lang->timezone_gmt_575, 7371 "6" => $lang->timezone_gmt_600, 7372 "6.5" => $lang->timezone_gmt_650, 7373 "7" => $lang->timezone_gmt_700, 7374 "8" => $lang->timezone_gmt_800, 7375 "8.5" => $lang->timezone_gmt_850, 7376 "8.75" => $lang->timezone_gmt_875, 7377 "9" => $lang->timezone_gmt_900, 7378 "9.5" => $lang->timezone_gmt_950, 7379 "10" => $lang->timezone_gmt_1000, 7380 "10.5" => $lang->timezone_gmt_1050, 7381 "11" => $lang->timezone_gmt_1100, 7382 "11.5" => $lang->timezone_gmt_1150, 7383 "12" => $lang->timezone_gmt_1200, 7384 "12.75" => $lang->timezone_gmt_1275, 7385 "13" => $lang->timezone_gmt_1300, 7386 "14" => $lang->timezone_gmt_1400 7387 ); 7388 return $timezones; 7389 } 7390 7391 /** 7392 * Build a time zone selection list. 7393 * 7394 * @param string $name The name of the select 7395 * @param int $selected The selected time zone (defaults to GMT) 7396 * @param boolean $short True to generate a "short" list with just timezone and current time 7397 * @return string 7398 */ 7399 function build_timezone_select($name, $selected=0, $short=false) 7400 { 7401 global $mybb, $lang, $templates; 7402 7403 $timezones = get_supported_timezones(); 7404 7405 $selected = str_replace("+", "", $selected); 7406 $timezone_option = ''; 7407 foreach($timezones as $timezone => $label) 7408 { 7409 $selected_add = ""; 7410 if($selected == $timezone) 7411 { 7412 $selected_add = " selected=\"selected\""; 7413 } 7414 if($short == true) 7415 { 7416 $label = ''; 7417 if($timezone != 0) 7418 { 7419 $label = $timezone; 7420 if($timezone > 0) 7421 { 7422 $label = "+{$label}"; 7423 } 7424 if(strpos($timezone, ".") !== false) 7425 { 7426 $label = str_replace(".", ":", $label); 7427 $label = str_replace(":5", ":30", $label); 7428 $label = str_replace(":75", ":45", $label); 7429 } 7430 else 7431 { 7432 $label .= ":00"; 7433 } 7434 } 7435 $time_in_zone = my_date($mybb->settings['timeformat'], TIME_NOW, $timezone); 7436 $label = $lang->sprintf($lang->timezone_gmt_short, $label." ", $time_in_zone); 7437 } 7438 7439 eval("\$timezone_option .= \"".$templates->get("usercp_options_timezone_option")."\";"); 7440 } 7441 7442 eval("\$select = \"".$templates->get("usercp_options_timezone")."\";"); 7443 return $select; 7444 } 7445 7446 /** 7447 * Fetch the contents of a remote file. 7448 * 7449 * @param string $url The URL of the remote file 7450 * @param array $post_data The array of post data 7451 * @param int $max_redirects Number of maximum redirects 7452 * @return string|bool The remote file contents. False on failure 7453 */ 7454 function fetch_remote_file($url, $post_data=array(), $max_redirects=20) 7455 { 7456 global $mybb, $config; 7457 7458 if(!my_validate_url($url, true)) 7459 { 7460 return false; 7461 } 7462 7463 $url_components = @parse_url($url); 7464 7465 if(!isset($url_components['scheme'])) 7466 { 7467 $url_components['scheme'] = 'https'; 7468 } 7469 if(!isset($url_components['port'])) 7470 { 7471 $url_components['port'] = $url_components['scheme'] == 'https' ? 443 : 80; 7472 } 7473 7474 if( 7475 !$url_components || 7476 empty($url_components['host']) || 7477 (!empty($url_components['scheme']) && !in_array($url_components['scheme'], array('http', 'https'))) || 7478 (!in_array($url_components['port'], array(80, 8080, 443))) || 7479 (!empty($config['disallowed_remote_hosts']) && in_array($url_components['host'], $config['disallowed_remote_hosts'])) 7480 ) 7481 { 7482 return false; 7483 } 7484 7485 $addresses = get_ip_by_hostname($url_components['host']); 7486 $destination_address = $addresses[0]; 7487 7488 if(!empty($config['disallowed_remote_addresses'])) 7489 { 7490 foreach($config['disallowed_remote_addresses'] as $disallowed_address) 7491 { 7492 $ip_range = fetch_ip_range($disallowed_address); 7493 7494 $packed_address = my_inet_pton($destination_address); 7495 7496 if(is_array($ip_range)) 7497 { 7498 if(strcmp($ip_range[0], $packed_address) <= 0 && strcmp($ip_range[1], $packed_address) >= 0) 7499 { 7500 return false; 7501 } 7502 } 7503 elseif($destination_address == $disallowed_address) 7504 { 7505 return false; 7506 } 7507 } 7508 } 7509 7510 $post_body = ''; 7511 if(!empty($post_data)) 7512 { 7513 foreach($post_data as $key => $val) 7514 { 7515 $post_body .= '&'.urlencode($key).'='.urlencode($val); 7516 } 7517 $post_body = ltrim($post_body, '&'); 7518 } 7519 7520 if(function_exists("curl_init")) 7521 { 7522 $fetch_header = $max_redirects > 0; 7523 7524 $ch = curl_init(); 7525 7526 $curlopt = array( 7527 CURLOPT_URL => $url, 7528 CURLOPT_HEADER => $fetch_header, 7529 CURLOPT_TIMEOUT => 10, 7530 CURLOPT_RETURNTRANSFER => 1, 7531 CURLOPT_FOLLOWLOCATION => 0, 7532 ); 7533 7534 if($ca_bundle_path = get_ca_bundle_path()) 7535 { 7536 $curlopt[CURLOPT_SSL_VERIFYPEER] = 1; 7537 $curlopt[CURLOPT_CAINFO] = $ca_bundle_path; 7538 } 7539 else 7540 { 7541 $curlopt[CURLOPT_SSL_VERIFYPEER] = 0; 7542 } 7543 7544 $curl_version_info = curl_version(); 7545 $curl_version = $curl_version_info['version']; 7546 7547 if(version_compare(PHP_VERSION, '7.0.7', '>=') && version_compare($curl_version, '7.49', '>=')) 7548 { 7549 // CURLOPT_CONNECT_TO 7550 $curlopt[10243] = array( 7551 $url_components['host'].':'.$url_components['port'].':'.$destination_address 7552 ); 7553 } 7554 elseif(version_compare(PHP_VERSION, '5.5', '>=') && version_compare($curl_version, '7.21.3', '>=')) 7555 { 7556 // CURLOPT_RESOLVE 7557 $curlopt[10203] = array( 7558 $url_components['host'].':'.$url_components['port'].':'.$destination_address 7559 ); 7560 } 7561 7562 if(defined('CURLOPT_DISALLOW_USERNAME_IN_URL')) 7563 { 7564 $curlopt[CURLOPT_DISALLOW_USERNAME_IN_URL] = true; 7565 } 7566 7567 if(!empty($post_body)) 7568 { 7569 $curlopt[CURLOPT_POST] = 1; 7570 $curlopt[CURLOPT_POSTFIELDS] = $post_body; 7571 } 7572 7573 curl_setopt_array($ch, $curlopt); 7574 7575 $response = curl_exec($ch); 7576 7577 if($fetch_header) 7578 { 7579 $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE); 7580 $header = substr($response, 0, $header_size); 7581 $body = substr($response, $header_size); 7582 7583 if(in_array(curl_getinfo($ch, CURLINFO_HTTP_CODE), array(301, 302))) 7584 { 7585 preg_match('/^Location:(.*?)(?:\n|$)/im', $header, $matches); 7586 7587 if($matches) 7588 { 7589 $data = fetch_remote_file(trim(array_pop($matches)), $post_data, --$max_redirects); 7590 } 7591 } 7592 else 7593 { 7594 $data = $body; 7595 } 7596 } 7597 else 7598 { 7599 $data = $response; 7600 } 7601 7602 curl_close($ch); 7603 return $data; 7604 } 7605 else if(function_exists("fsockopen")) 7606 { 7607 if(!isset($url_components['path'])) 7608 { 7609 $url_components['path'] = "/"; 7610 } 7611 if(isset($url_components['query'])) 7612 { 7613 $url_components['path'] .= "?{$url_components['query']}"; 7614 } 7615 7616 $scheme = ''; 7617 7618 if($url_components['scheme'] == 'https') 7619 { 7620 $scheme = 'ssl://'; 7621 if($url_components['port'] == 80) 7622 { 7623 $url_components['port'] = 443; 7624 } 7625 } 7626 7627 if(function_exists('stream_context_create')) 7628 { 7629 if($url_components['scheme'] == 'https' && $ca_bundle_path = get_ca_bundle_path()) 7630 { 7631 $context = stream_context_create(array( 7632 'ssl' => array( 7633 'verify_peer' => true, 7634 'verify_peer_name' => true, 7635 'peer_name' => $url_components['host'], 7636 'cafile' => $ca_bundle_path, 7637 ), 7638 )); 7639 } 7640 else 7641 { 7642 $context = stream_context_create(array( 7643 'ssl' => array( 7644 'verify_peer' => false, 7645 'verify_peer_name' => false, 7646 'peer_name' => $url_components['host'], 7647 ), 7648 )); 7649 } 7650 7651 $fp = @stream_socket_client($scheme.$destination_address.':'.(int)$url_components['port'], $error_no, $error, 10, STREAM_CLIENT_CONNECT, $context); 7652 } 7653 else 7654 { 7655 $fp = @fsockopen($scheme.$url_components['host'], (int)$url_components['port'], $error_no, $error, 10); 7656 } 7657 7658 if(!$fp) 7659 { 7660 return false; 7661 } 7662 @stream_set_timeout($fp, 10); 7663 $headers = array(); 7664 if(!empty($post_body)) 7665 { 7666 $headers[] = "POST {$url_components['path']} HTTP/1.0"; 7667 $headers[] = "Content-Length: ".strlen($post_body); 7668 $headers[] = "Content-Type: application/x-www-form-urlencoded"; 7669 } 7670 else 7671 { 7672 $headers[] = "GET {$url_components['path']} HTTP/1.0"; 7673 } 7674 7675 $headers[] = "Host: {$url_components['host']}"; 7676 $headers[] = "Connection: Close"; 7677 $headers[] = ''; 7678 7679 if(!empty($post_body)) 7680 { 7681 $headers[] = $post_body; 7682 } 7683 else 7684 { 7685 // If we have no post body, we need to add an empty element to make sure we've got \r\n\r\n before the (non-existent) body starts 7686 $headers[] = ''; 7687 } 7688 7689 $headers = implode("\r\n", $headers); 7690 if(!@fwrite($fp, $headers)) 7691 { 7692 return false; 7693 } 7694 7695 $data = null; 7696 7697 while(!feof($fp)) 7698 { 7699 $data .= fgets($fp, 12800); 7700 } 7701 fclose($fp); 7702 7703 $data = explode("\r\n\r\n", $data, 2); 7704 7705 $header = $data[0]; 7706 $status_line = current(explode("\n\n", $header, 1)); 7707 $body = $data[1]; 7708 7709 if($max_redirects > 0 && (strstr($status_line, ' 301 ') || strstr($status_line, ' 302 '))) 7710 { 7711 preg_match('/^Location:(.*?)(?:\n|$)/im', $header, $matches); 7712 7713 if($matches) 7714 { 7715 $data = fetch_remote_file(trim(array_pop($matches)), $post_data, --$max_redirects); 7716 } 7717 } 7718 else 7719 { 7720 $data = $body; 7721 } 7722 7723 return $data; 7724 } 7725 else 7726 { 7727 return false; 7728 } 7729 } 7730 7731 /** 7732 * Resolves a hostname into a set of IP addresses. 7733 * 7734 * @param string $hostname The hostname to be resolved 7735 * @return array|bool The resulting IP addresses. False on failure 7736 */ 7737 function get_ip_by_hostname($hostname) 7738 { 7739 $addresses = @gethostbynamel($hostname); 7740 7741 if(!$addresses) 7742 { 7743 $result_set = @dns_get_record($hostname, DNS_A | DNS_AAAA); 7744 7745 if($result_set) 7746 { 7747 $addresses = array_column($result_set, 'ip'); 7748 } 7749 else 7750 { 7751 return false; 7752 } 7753 } 7754 7755 return $addresses; 7756 } 7757 7758 /** 7759 * Returns the location of the CA bundle defined in the PHP configuration. 7760 * 7761 * @return string|bool The location of the CA bundle, false if not set 7762 */ 7763 function get_ca_bundle_path() 7764 { 7765 if($path = ini_get('openssl.cafile')) 7766 { 7767 return $path; 7768 } 7769 if($path = ini_get('curl.cainfo')) 7770 { 7771 return $path; 7772 } 7773 7774 return false; 7775 } 7776 7777 /** 7778 * Checks if a particular user is a super administrator. 7779 * 7780 * @param int $uid The user ID to check against the list of super admins 7781 * @return boolean True if a super admin, false if not 7782 */ 7783 function is_super_admin($uid) 7784 { 7785 static $super_admins; 7786 7787 if(!isset($super_admins)) 7788 { 7789 global $mybb; 7790 $super_admins = str_replace(" ", "", $mybb->config['super_admins']); 7791 } 7792 7793 if(my_strpos(",{$super_admins},", ",{$uid},") === false) 7794 { 7795 return false; 7796 } 7797 else 7798 { 7799 return true; 7800 } 7801 } 7802 7803 /** 7804 * Checks if a user is a member of a particular group 7805 * Originates from frostschutz's PluginLibrary 7806 * github.com/frostschutz 7807 * 7808 * @param array|int|string A selection of groups (as array or comma seperated) to check or -1 for any group 7809 * @param bool|array|int False assumes the current user. Otherwise an user array or an id can be passed 7810 * @return array Array of groups specified in the first param to which the user belongs 7811 */ 7812 function is_member($groups, $user = false) 7813 { 7814 global $mybb; 7815 7816 if(empty($groups)) 7817 { 7818 return array(); 7819 } 7820 7821 if($user == false) 7822 { 7823 $user = $mybb->user; 7824 } 7825 else if(!is_array($user)) 7826 { 7827 // Assume it's a UID 7828 $user = get_user($user); 7829 } 7830 7831 $memberships = array_map('intval', explode(',', $user['additionalgroups'])); 7832 $memberships[] = $user['usergroup']; 7833 7834 if(!is_array($groups)) 7835 { 7836 if((int)$groups == -1) 7837 { 7838 return $memberships; 7839 } 7840 else 7841 { 7842 if(is_string($groups)) 7843 { 7844 $groups = explode(',', $groups); 7845 } 7846 else 7847 { 7848 $groups = (array)$groups; 7849 } 7850 } 7851 } 7852 7853 $groups = array_filter(array_map('intval', $groups)); 7854 7855 return array_intersect($groups, $memberships); 7856 } 7857 7858 /** 7859 * Split a string based on the specified delimeter, ignoring said delimeter in escaped strings. 7860 * Ex: the "quick brown fox" jumped, could return 1 => the, 2 => quick brown fox, 3 => jumped 7861 * 7862 * @param string $delimeter The delimeter to split by 7863 * @param string $string The string to split 7864 * @param string $escape The escape character or string if we have one. 7865 * @return array Array of split string 7866 */ 7867 function escaped_explode($delimeter, $string, $escape="") 7868 { 7869 $strings = array(); 7870 $original = $string; 7871 $in_escape = false; 7872 if($escape) 7873 { 7874 if(is_array($escape)) 7875 { 7876 function escaped_explode_escape($string) 7877 { 7878 return preg_quote($string, "#"); 7879 } 7880 $escape_preg = "(".implode("|", array_map("escaped_explode_escape", $escape)).")"; 7881 } 7882 else 7883 { 7884 $escape_preg = preg_quote($escape, "#"); 7885 } 7886 $quoted_strings = preg_split("#(?<!\\\){$escape_preg}#", $string); 7887 } 7888 else 7889 { 7890 $quoted_strings = array($string); 7891 } 7892 foreach($quoted_strings as $string) 7893 { 7894 if($string != "") 7895 { 7896 if($in_escape) 7897 { 7898 $strings[] = trim($string); 7899 } 7900 else 7901 { 7902 $split_strings = explode($delimeter, $string); 7903 foreach($split_strings as $string) 7904 { 7905 if($string == "") continue; 7906 $strings[] = trim($string); 7907 } 7908 } 7909 } 7910 $in_escape = !$in_escape; 7911 } 7912 if(!count($strings)) 7913 { 7914 return $original; 7915 } 7916 return $strings; 7917 } 7918 7919 /** 7920 * DEPRECATED! Please use IPv6 compatible fetch_ip_range! 7921 * Fetch an IPv4 long formatted range for searching IPv4 IP addresses. 7922 * 7923 * @deprecated 7924 * @param string $ip The IP address to convert to a range based LONG 7925 * @return string|array If a full IP address is provided, the ip2long equivalent, otherwise an array of the upper & lower extremities of the IP 7926 */ 7927 function fetch_longipv4_range($ip) 7928 { 7929 $ip_bits = explode(".", $ip); 7930 $ip_string1 = $ip_string2 = ""; 7931 7932 if($ip == "*") 7933 { 7934 return array(ip2long('0.0.0.0'), ip2long('255.255.255.255')); 7935 } 7936 7937 if(strpos($ip, ".*") === false) 7938 { 7939 $ip = str_replace("*", "", $ip); 7940 if(count($ip_bits) == 4) 7941 { 7942 return ip2long($ip); 7943 } 7944 else 7945 { 7946 return array(ip2long($ip.".0"), ip2long($ip.".255")); 7947 } 7948 } 7949 // Wildcard based IP provided 7950 else 7951 { 7952 $sep = ""; 7953 foreach($ip_bits as $piece) 7954 { 7955 if($piece == "*") 7956 { 7957 $ip_string1 .= $sep."0"; 7958 $ip_string2 .= $sep."255"; 7959 } 7960 else 7961 { 7962 $ip_string1 .= $sep.$piece; 7963 $ip_string2 .= $sep.$piece; 7964 } 7965 $sep = "."; 7966 } 7967 return array(ip2long($ip_string1), ip2long($ip_string2)); 7968 } 7969 } 7970 7971 /** 7972 * Fetch a list of ban times for a user account. 7973 * 7974 * @return array Array of ban times 7975 */ 7976 function fetch_ban_times() 7977 { 7978 global $plugins, $lang; 7979 7980 // Days-Months-Years 7981 $ban_times = array( 7982 "1-0-0" => "1 {$lang->day}", 7983 "2-0-0" => "2 {$lang->days}", 7984 "3-0-0" => "3 {$lang->days}", 7985 "4-0-0" => "4 {$lang->days}", 7986 "5-0-0" => "5 {$lang->days}", 7987 "6-0-0" => "6 {$lang->days}", 7988 "7-0-0" => "1 {$lang->week}", 7989 "14-0-0" => "2 {$lang->weeks}", 7990 "21-0-0" => "3 {$lang->weeks}", 7991 "0-1-0" => "1 {$lang->month}", 7992 "0-2-0" => "2 {$lang->months}", 7993 "0-3-0" => "3 {$lang->months}", 7994 "0-4-0" => "4 {$lang->months}", 7995 "0-5-0" => "5 {$lang->months}", 7996 "0-6-0" => "6 {$lang->months}", 7997 "0-0-1" => "1 {$lang->year}", 7998 "0-0-2" => "2 {$lang->years}" 7999 ); 8000 8001 $ban_times = $plugins->run_hooks("functions_fetch_ban_times", $ban_times); 8002 8003 $ban_times['---'] = $lang->permanent; 8004 return $ban_times; 8005 } 8006 8007 /** 8008 * Format a ban length in to a UNIX timestamp. 8009 * 8010 * @param string $date The ban length string 8011 * @param int $stamp The optional UNIX timestamp, if 0, current time is used. 8012 * @return int The UNIX timestamp when the ban will be lifted 8013 */ 8014 function ban_date2timestamp($date, $stamp=0) 8015 { 8016 if($stamp == 0) 8017 { 8018 $stamp = TIME_NOW; 8019 } 8020 $d = explode('-', $date); 8021 $nowdate = date("H-j-n-Y", $stamp); 8022 $n = explode('-', $nowdate); 8023 $n[1] += $d[0]; 8024 $n[2] += $d[1]; 8025 $n[3] += $d[2]; 8026 return mktime(date("G", $stamp), date("i", $stamp), 0, $n[2], $n[1], $n[3]); 8027 } 8028 8029 /** 8030 * Expire old warnings in the database. 8031 * 8032 * @return bool 8033 */ 8034 function expire_warnings() 8035 { 8036 global $warningshandler; 8037 8038 if(!is_object($warningshandler)) 8039 { 8040 require_once MYBB_ROOT.'inc/datahandlers/warnings.php'; 8041 $warningshandler = new WarningsHandler('update'); 8042 } 8043 8044 return $warningshandler->expire_warnings(); 8045 } 8046 8047 /** 8048 * Custom chmod function to fix problems with hosts who's server configurations screw up umasks 8049 * 8050 * @param string $file The file to chmod 8051 * @param string $mode The mode to chmod(i.e. 0666) 8052 * @return bool 8053 */ 8054 function my_chmod($file, $mode) 8055 { 8056 // Passing $mode as an octal number causes strlen and substr to return incorrect values. Instead pass as a string 8057 if(substr($mode, 0, 1) != '0' || strlen($mode) !== 4) 8058 { 8059 return false; 8060 } 8061 $old_umask = umask(0); 8062 8063 // We convert the octal string to a decimal number because passing a octal string doesn't work with chmod 8064 // and type casting subsequently removes the prepended 0 which is needed for octal numbers 8065 $result = chmod($file, octdec($mode)); 8066 umask($old_umask); 8067 return $result; 8068 } 8069 8070 /** 8071 * Custom rmdir function to loop through an entire directory and delete all files/folders within 8072 * 8073 * @param string $path The path to the directory 8074 * @param array $ignore Any files you wish to ignore (optional) 8075 * @return bool 8076 */ 8077 function my_rmdir_recursive($path, $ignore=array()) 8078 { 8079 global $orig_dir; 8080 8081 if(!isset($orig_dir)) 8082 { 8083 $orig_dir = $path; 8084 } 8085 8086 if(@is_dir($path) && !@is_link($path)) 8087 { 8088 if($dh = @opendir($path)) 8089 { 8090 while(($file = @readdir($dh)) !== false) 8091 { 8092 if($file == '.' || $file == '..' || $file == '.svn' || in_array($path.'/'.$file, $ignore) || !my_rmdir_recursive($path.'/'.$file)) 8093 { 8094 continue; 8095 } 8096 } 8097 @closedir($dh); 8098 } 8099 8100 // Are we done? Don't delete the main folder too and return true 8101 if($path == $orig_dir) 8102 { 8103 return true; 8104 } 8105 8106 return @rmdir($path); 8107 } 8108 8109 return @unlink($path); 8110 } 8111 8112 /** 8113 * Counts the number of subforums in a array([pid][disporder][fid]) starting from the pid 8114 * 8115 * @param array $array The array of forums 8116 * @return integer The number of sub forums 8117 */ 8118 function subforums_count($array=array()) 8119 { 8120 $count = 0; 8121 foreach($array as $array2) 8122 { 8123 $count += count($array2); 8124 } 8125 8126 return $count; 8127 } 8128 8129 /** 8130 * DEPRECATED! Please use IPv6 compatible my_inet_pton! 8131 * Fix for PHP's ip2long to guarantee a 32-bit signed integer value is produced (this is aimed 8132 * at 64-bit versions of PHP) 8133 * 8134 * @deprecated 8135 * @param string $ip The IP to convert 8136 * @return integer IP in 32-bit signed format 8137 */ 8138 function my_ip2long($ip) 8139 { 8140 $ip_long = ip2long($ip); 8141 8142 if(!$ip_long) 8143 { 8144 $ip_long = sprintf("%u", ip2long($ip)); 8145 8146 if(!$ip_long) 8147 { 8148 return 0; 8149 } 8150 } 8151 8152 if($ip_long >= 2147483648) // Won't occur on 32-bit PHP 8153 { 8154 $ip_long -= 4294967296; 8155 } 8156 8157 return $ip_long; 8158 } 8159 8160 /** 8161 * DEPRECATED! Please use IPv6 compatible my_inet_ntop! 8162 * As above, fix for PHP's long2ip on 64-bit versions 8163 * 8164 * @deprecated 8165 * @param integer $long The IP to convert (will accept 64-bit IPs as well) 8166 * @return string IP in IPv4 format 8167 */ 8168 function my_long2ip($long) 8169 { 8170 // On 64-bit machines is_int will return true. On 32-bit it will return false 8171 if($long < 0 && is_int(2147483648)) 8172 { 8173 // We have a 64-bit system 8174 $long += 4294967296; 8175 } 8176 return long2ip($long); 8177 } 8178 8179 /** 8180 * Converts a human readable IP address to its packed in_addr representation 8181 * 8182 * @param string $ip The IP to convert 8183 * @return string IP in 32bit or 128bit binary format 8184 */ 8185 function my_inet_pton($ip) 8186 { 8187 if(function_exists('inet_pton')) 8188 { 8189 return @inet_pton($ip); 8190 } 8191 else 8192 { 8193 /** 8194 * Replace inet_pton() 8195 * 8196 * @category PHP 8197 * @package PHP_Compat 8198 * @license LGPL - http://www.gnu.org/licenses/lgpl.html 8199 * @copyright 2004-2007 Aidan Lister <aidan@php.net>, Arpad Ray <arpad@php.net> 8200 * @link http://php.net/inet_pton 8201 * @author Arpad Ray <arpad@php.net> 8202 * @version $Revision: 269597 $ 8203 */ 8204 $r = ip2long($ip); 8205 if($r !== false && $r != -1) 8206 { 8207 return pack('N', $r); 8208 } 8209 8210 $delim_count = substr_count($ip, ':'); 8211 if($delim_count < 1 || $delim_count > 7) 8212 { 8213 return false; 8214 } 8215 8216 $r = explode(':', $ip); 8217 $rcount = count($r); 8218 if(($doub = array_search('', $r, 1)) !== false) 8219 { 8220 $length = (!$doub || $doub == $rcount - 1 ? 2 : 1); 8221 array_splice($r, $doub, $length, array_fill(0, 8 + $length - $rcount, 0)); 8222 } 8223 8224 $r = array_map('hexdec', $r); 8225 array_unshift($r, 'n*'); 8226 $r = call_user_func_array('pack', $r); 8227 8228 return $r; 8229 } 8230 } 8231 8232 /** 8233 * Converts a packed internet address to a human readable representation 8234 * 8235 * @param string $ip IP in 32bit or 128bit binary format 8236 * @return string IP in human readable format 8237 */ 8238 function my_inet_ntop($ip) 8239 { 8240 if(function_exists('inet_ntop')) 8241 { 8242 return @inet_ntop($ip); 8243 } 8244 else 8245 { 8246 /** 8247 * Replace inet_ntop() 8248 * 8249 * @category PHP 8250 * @package PHP_Compat 8251 * @license LGPL - http://www.gnu.org/licenses/lgpl.html 8252 * @copyright 2004-2007 Aidan Lister <aidan@php.net>, Arpad Ray <arpad@php.net> 8253 * @link http://php.net/inet_ntop 8254 * @author Arpad Ray <arpad@php.net> 8255 * @version $Revision: 269597 $ 8256 */ 8257 switch(strlen($ip)) 8258 { 8259 case 4: 8260 list(,$r) = unpack('N', $ip); 8261 return long2ip($r); 8262 case 16: 8263 $r = substr(chunk_split(bin2hex($ip), 4, ':'), 0, -1); 8264 $r = preg_replace( 8265 array('/(?::?\b0+\b:?){2,}/', '/\b0+([^0])/e'), 8266 array('::', '(int)"$1"?"$1":"0$1"'), 8267 $r); 8268 return $r; 8269 } 8270 return false; 8271 } 8272 } 8273 8274 /** 8275 * Fetch an binary formatted range for searching IPv4 and IPv6 IP addresses. 8276 * 8277 * @param string $ipaddress The IP address to convert to a range 8278 * @return string|array|bool If a full IP address is provided, the in_addr representation, otherwise an array of the upper & lower extremities of the IP. False on failure 8279 */ 8280 function fetch_ip_range($ipaddress) 8281 { 8282 // Wildcard 8283 if(strpos($ipaddress, '*') !== false) 8284 { 8285 if(strpos($ipaddress, ':') !== false) 8286 { 8287 // IPv6 8288 $upper = str_replace('*', 'ffff', $ipaddress); 8289 $lower = str_replace('*', '0', $ipaddress); 8290 } 8291 else 8292 { 8293 // IPv4 8294 $ip_bits = count(explode('.', $ipaddress)); 8295 if($ip_bits < 4) 8296 { 8297 // Support for 127.0.* 8298 $replacement = str_repeat('.*', 4-$ip_bits); 8299 $ipaddress = substr_replace($ipaddress, $replacement, strrpos($ipaddress, '*')+1, 0); 8300 } 8301 $upper = str_replace('*', '255', $ipaddress); 8302 $lower = str_replace('*', '0', $ipaddress); 8303 } 8304 $upper = my_inet_pton($upper); 8305 $lower = my_inet_pton($lower); 8306 if($upper === false || $lower === false) 8307 { 8308 return false; 8309 } 8310 return array($lower, $upper); 8311 } 8312 // CIDR notation 8313 elseif(strpos($ipaddress, '/') !== false) 8314 { 8315 $ipaddress = explode('/', $ipaddress); 8316 $ip_address = $ipaddress[0]; 8317 $ip_range = (int)$ipaddress[1]; 8318 8319 if(empty($ip_address) || empty($ip_range)) 8320 { 8321 // Invalid input 8322 return false; 8323 } 8324 else 8325 { 8326 $ip_address = my_inet_pton($ip_address); 8327 8328 if(!$ip_address) 8329 { 8330 // Invalid IP address 8331 return false; 8332 } 8333 } 8334 8335 /** 8336 * Taken from: https://github.com/NewEraCracker/php_work/blob/master/ipRangeCalculate.php 8337 * Author: NewEraCracker 8338 * License: Public Domain 8339 */ 8340 8341 // Pack IP, Set some vars 8342 $ip_pack = $ip_address; 8343 $ip_pack_size = strlen($ip_pack); 8344 $ip_bits_size = $ip_pack_size*8; 8345 8346 // IP bits (lots of 0's and 1's) 8347 $ip_bits = ''; 8348 for($i = 0; $i < $ip_pack_size; $i = $i+1) 8349 { 8350 $bit = decbin(ord($ip_pack[$i])); 8351 $bit = str_pad($bit, 8, '0', STR_PAD_LEFT); 8352 $ip_bits .= $bit; 8353 } 8354 8355 // Significative bits (from the ip range) 8356 $ip_bits = substr($ip_bits, 0, $ip_range); 8357 8358 // Some calculations 8359 $ip_lower_bits = str_pad($ip_bits, $ip_bits_size, '0', STR_PAD_RIGHT); 8360 $ip_higher_bits = str_pad($ip_bits, $ip_bits_size, '1', STR_PAD_RIGHT); 8361 8362 // Lower IP 8363 $ip_lower_pack = ''; 8364 for($i=0; $i < $ip_bits_size; $i=$i+8) 8365 { 8366 $chr = substr($ip_lower_bits, $i, 8); 8367 $chr = chr(bindec($chr)); 8368 $ip_lower_pack .= $chr; 8369 } 8370 8371 // Higher IP 8372 $ip_higher_pack = ''; 8373 for($i=0; $i < $ip_bits_size; $i=$i+8) 8374 { 8375 $chr = substr($ip_higher_bits, $i, 8); 8376 $chr = chr( bindec($chr) ); 8377 $ip_higher_pack .= $chr; 8378 } 8379 8380 return array($ip_lower_pack, $ip_higher_pack); 8381 } 8382 // Just on IP address 8383 else 8384 { 8385 return my_inet_pton($ipaddress); 8386 } 8387 } 8388 8389 /** 8390 * Time how long it takes for a particular piece of code to run. Place calls above & below the block of code. 8391 * 8392 * @return float The time taken 8393 */ 8394 function get_execution_time() 8395 { 8396 static $time_start; 8397 8398 $time = microtime(true); 8399 8400 // Just starting timer, init and return 8401 if(!$time_start) 8402 { 8403 $time_start = $time; 8404 return; 8405 } 8406 // Timer has run, return execution time 8407 else 8408 { 8409 $total = $time-$time_start; 8410 if($total < 0) $total = 0; 8411 $time_start = 0; 8412 return $total; 8413 } 8414 } 8415 8416 /** 8417 * Processes a checksum list on MyBB files and returns a result set 8418 * 8419 * @param string $path The base path 8420 * @param int $count The count of files 8421 * @return array The bad files 8422 */ 8423 function verify_files($path=MYBB_ROOT, $count=0) 8424 { 8425 global $mybb, $checksums, $bad_verify_files; 8426 8427 // We don't need to check these types of files 8428 $ignore = array(".", "..", ".svn", "config.php", "settings.php", "Thumb.db", "config.default.php", "lock", "htaccess.txt", "htaccess-nginx.txt", "logo.gif", "logo.png"); 8429 $ignore_ext = array("attach"); 8430 8431 if(substr($path, -1, 1) == "/") 8432 { 8433 $path = substr($path, 0, -1); 8434 } 8435 8436 if(!is_array($bad_verify_files)) 8437 { 8438 $bad_verify_files = array(); 8439 } 8440 8441 // Make sure that we're in a directory and it's not a symbolic link 8442 if(@is_dir($path) && !@is_link($path)) 8443 { 8444 if($dh = @opendir($path)) 8445 { 8446 // Loop through all the files/directories in this directory 8447 while(($file = @readdir($dh)) !== false) 8448 { 8449 if(in_array($file, $ignore) || in_array(get_extension($file), $ignore_ext)) 8450 { 8451 continue; 8452 } 8453 8454 // Recurse through the directory tree 8455 if(is_dir($path."/".$file)) 8456 { 8457 verify_files($path."/".$file, ($count+1)); 8458 continue; 8459 } 8460 8461 // We only need the last part of the path (from the MyBB directory to the file. i.e. inc/functions.php) 8462 $file_path = ".".str_replace(substr(MYBB_ROOT, 0, -1), "", $path)."/".$file; 8463 8464 // Does this file even exist in our official list? Perhaps it's a plugin 8465 if(array_key_exists($file_path, $checksums)) 8466 { 8467 $filename = $path."/".$file; 8468 $handle = fopen($filename, "rb"); 8469 $hashingContext = hash_init('sha512'); 8470 while(!feof($handle)) 8471 { 8472 hash_update($hashingContext, fread($handle, 8192)); 8473 } 8474 fclose($handle); 8475 8476 $checksum = hash_final($hashingContext); 8477 8478 // Does it match any of our hashes (unix/windows new lines taken into consideration with the hashes) 8479 if(!in_array($checksum, $checksums[$file_path])) 8480 { 8481 $bad_verify_files[] = array("status" => "changed", "path" => $file_path); 8482 } 8483 } 8484 unset($checksums[$file_path]); 8485 } 8486 @closedir($dh); 8487 } 8488 } 8489 8490 if($count == 0) 8491 { 8492 if(!empty($checksums)) 8493 { 8494 foreach($checksums as $file_path => $hashes) 8495 { 8496 if(in_array(basename($file_path), $ignore)) 8497 { 8498 continue; 8499 } 8500 $bad_verify_files[] = array("status" => "missing", "path" => $file_path); 8501 } 8502 } 8503 } 8504 8505 // uh oh 8506 if($count == 0) 8507 { 8508 return $bad_verify_files; 8509 } 8510 } 8511 8512 /** 8513 * Returns a signed value equal to an integer 8514 * 8515 * @param int $int The integer 8516 * @return string The signed equivalent 8517 */ 8518 function signed($int) 8519 { 8520 if($int < 0) 8521 { 8522 return "$int"; 8523 } 8524 else 8525 { 8526 return "+$int"; 8527 } 8528 } 8529 8530 /** 8531 * Returns a securely generated seed 8532 * 8533 * @return string A secure binary seed 8534 */ 8535 function secure_binary_seed_rng($bytes) 8536 { 8537 $output = null; 8538 8539 if(version_compare(PHP_VERSION, '7.0', '>=')) 8540 { 8541 try 8542 { 8543 $output = random_bytes($bytes); 8544 } catch (Exception $e) { 8545 } 8546 } 8547 8548 if(strlen($output) < $bytes) 8549 { 8550 if(@is_readable('/dev/urandom') && ($handle = @fopen('/dev/urandom', 'rb'))) 8551 { 8552 $output = @fread($handle, $bytes); 8553 @fclose($handle); 8554 } 8555 } 8556 else 8557 { 8558 return $output; 8559 } 8560 8561 if(strlen($output) < $bytes) 8562 { 8563 if(function_exists('mcrypt_create_iv')) 8564 { 8565 if (DIRECTORY_SEPARATOR == '/') 8566 { 8567 $source = MCRYPT_DEV_URANDOM; 8568 } 8569 else 8570 { 8571 $source = MCRYPT_RAND; 8572 } 8573 8574 $output = @mcrypt_create_iv($bytes, $source); 8575 } 8576 } 8577 else 8578 { 8579 return $output; 8580 } 8581 8582 if(strlen($output) < $bytes) 8583 { 8584 if(function_exists('openssl_random_pseudo_bytes')) 8585 { 8586 // PHP <5.3.4 had a bug which makes that function unusable on Windows 8587 if ((DIRECTORY_SEPARATOR == '/') || version_compare(PHP_VERSION, '5.3.4', '>=')) 8588 { 8589 $output = openssl_random_pseudo_bytes($bytes, $crypto_strong); 8590 if ($crypto_strong == false) 8591 { 8592 $output = null; 8593 } 8594 } 8595 } 8596 } 8597 else 8598 { 8599 return $output; 8600 } 8601 8602 if(strlen($output) < $bytes) 8603 { 8604 if(class_exists('COM')) 8605 { 8606 try 8607 { 8608 $CAPI_Util = new COM('CAPICOM.Utilities.1'); 8609 if(is_callable(array($CAPI_Util, 'GetRandom'))) 8610 { 8611 $output = $CAPI_Util->GetRandom($bytes, 0); 8612 } 8613 } catch (Exception $e) { 8614 } 8615 } 8616 } 8617 else 8618 { 8619 return $output; 8620 } 8621 8622 if(strlen($output) < $bytes) 8623 { 8624 // Close to what PHP basically uses internally to seed, but not quite. 8625 $unique_state = microtime().@getmypid(); 8626 8627 $rounds = ceil($bytes / 16); 8628 8629 for($i = 0; $i < $rounds; $i++) 8630 { 8631 $unique_state = md5(microtime().$unique_state); 8632 $output .= md5($unique_state); 8633 } 8634 8635 $output = substr($output, 0, ($bytes * 2)); 8636 8637 $output = pack('H*', $output); 8638 8639 return $output; 8640 } 8641 else 8642 { 8643 return $output; 8644 } 8645 } 8646 8647 /** 8648 * Returns a securely generated seed integer 8649 * 8650 * @return int An integer equivalent of a secure hexadecimal seed 8651 */ 8652 function secure_seed_rng() 8653 { 8654 $bytes = PHP_INT_SIZE; 8655 8656 do 8657 { 8658 8659 $output = secure_binary_seed_rng($bytes); 8660 8661 // convert binary data to a decimal number 8662 if ($bytes == 4) 8663 { 8664 $elements = unpack('i', $output); 8665 $output = abs($elements[1]); 8666 } 8667 else 8668 { 8669 $elements = unpack('N2', $output); 8670 $output = abs($elements[1] << 32 | $elements[2]); 8671 } 8672 8673 } while($output > PHP_INT_MAX); 8674 8675 return $output; 8676 } 8677 8678 /** 8679 * Generates a cryptographically secure random number. 8680 * 8681 * @param int $min Optional lowest value to be returned (default: 0) 8682 * @param int $max Optional highest value to be returned (default: PHP_INT_MAX) 8683 */ 8684 function my_rand($min=0, $max=PHP_INT_MAX) 8685 { 8686 // backward compatibility 8687 if($min === null || $max === null || $max < $min) 8688 { 8689 $min = 0; 8690 $max = PHP_INT_MAX; 8691 } 8692 8693 if(version_compare(PHP_VERSION, '7.0', '>=')) 8694 { 8695 try 8696 { 8697 $result = random_int($min, $max); 8698 } catch (Exception $e) { 8699 } 8700 8701 if(isset($result)) 8702 { 8703 return $result; 8704 } 8705 } 8706 8707 $seed = secure_seed_rng(); 8708 8709 $distance = $max - $min; 8710 return $min + floor($distance * ($seed / PHP_INT_MAX) ); 8711 } 8712 8713 /** 8714 * More robust version of PHP's trim() function. It includes a list of UTF-8 blank characters 8715 * from http://kb.mozillazine.org/Network.IDN.blacklist_chars 8716 * 8717 * @param string $string The string to trim from 8718 * @param string $charlist Optional. The stripped characters can also be specified using the charlist parameter 8719 * @return string The trimmed string 8720 */ 8721 function trim_blank_chrs($string, $charlist="") 8722 { 8723 $hex_chrs = array( 8724 0x09 => 1, // \x{0009} 8725 0x0A => 1, // \x{000A} 8726 0x0B => 1, // \x{000B} 8727 0x0D => 1, // \x{000D} 8728 0x20 => 1, // \x{0020} 8729 0xC2 => array(0x81 => 1, 0x8D => 1, 0x90 => 1, 0x9D => 1, 0xA0 => 1, 0xAD => 1), // \x{0081}, \x{008D}, \x{0090}, \x{009D}, \x{00A0}, \x{00AD} 8730 0xCC => array(0xB7 => 1, 0xB8 => 1), // \x{0337}, \x{0338} 8731 0xE1 => array(0x85 => array(0x9F => 1, 0xA0 => 1), 0x9A => array(0x80 => 1), 0xA0 => array(0x8E => 1)), // \x{115F}, \x{1160}, \x{1680}, \x{180E} 8732 0xE2 => array(0x80 => array(0x80 => 1, 0x81 => 1, 0x82 => 1, 0x83 => 1, 0x84 => 1, 0x85 => 1, 0x86 => 1, 0x87 => 1, 0x88 => 1, 0x89 => 1, 0x8A => 1, 0x8B => 1, 0x8C => 1, 0x8D => 1, 0x8E => 1, 0x8F => 1, // \x{2000} - \x{200F} 8733 0xA8 => 1, 0xA9 => 1, 0xAA => 1, 0xAB => 1, 0xAC => 1, 0xAD => 1, 0xAE => 1, 0xAF => 1), // \x{2028} - \x{202F} 8734 0x81 => array(0x9F => 1)), // \x{205F} 8735 0xE3 => array(0x80 => array(0x80 => 1), // \x{3000} 8736 0x85 => array(0xA4 => 1)), // \x{3164} 8737 0xEF => array(0xBB => array(0xBF => 1), // \x{FEFF} 8738 0xBE => array(0xA0 => 1), // \x{FFA0} 8739 0xBF => array(0xB9 => 1, 0xBA => 1, 0xBB => 1)), // \x{FFF9} - \x{FFFB} 8740 ); 8741 8742 $hex_chrs_rev = array( 8743 0x09 => 1, // \x{0009} 8744 0x0A => 1, // \x{000A} 8745 0x0B => 1, // \x{000B} 8746 0x0D => 1, // \x{000D} 8747 0x20 => 1, // \x{0020} 8748 0x81 => array(0xC2 => 1, 0x80 => array(0xE2 => 1)), // \x{0081}, \x{2001} 8749 0x8D => array(0xC2 => 1, 0x80 => array(0xE2 => 1)), // \x{008D}, \x{200D} 8750 0x90 => array(0xC2 => 1), // \x{0090} 8751 0x9D => array(0xC2 => 1), // \x{009D} 8752 0xA0 => array(0xC2 => 1, 0x85 => array(0xE1 => 1), 0x81 => array(0xE2 => 1), 0xBE => array(0xEF => 1)), // \x{00A0}, \x{1160}, \x{2060}, \x{FFA0} 8753 0xAD => array(0xC2 => 1, 0x80 => array(0xE2 => 1)), // \x{00AD}, \x{202D} 8754 0xB8 => array(0xCC => 1), // \x{0338} 8755 0xB7 => array(0xCC => 1), // \x{0337} 8756 0x9F => array(0x85 => array(0xE1 => 1), 0x81 => array(0xE2 => 1)), // \x{115F}, \x{205F} 8757 0x80 => array(0x9A => array(0xE1 => 1), 0x80 => array(0xE2 => 1, 0xE3 => 1)), // \x{1680}, \x{2000}, \x{3000} 8758 0x8E => array(0xA0 => array(0xE1 => 1), 0x80 => array(0xE2 => 1)), // \x{180E}, \x{200E} 8759 0x82 => array(0x80 => array(0xE2 => 1)), // \x{2002} 8760 0x83 => array(0x80 => array(0xE2 => 1)), // \x{2003} 8761 0x84 => array(0x80 => array(0xE2 => 1)), // \x{2004} 8762 0x85 => array(0x80 => array(0xE2 => 1)), // \x{2005} 8763 0x86 => array(0x80 => array(0xE2 => 1)), // \x{2006} 8764 0x87 => array(0x80 => array(0xE2 => 1)), // \x{2007} 8765 0x88 => array(0x80 => array(0xE2 => 1)), // \x{2008} 8766 0x89 => array(0x80 => array(0xE2 => 1)), // \x{2009} 8767 0x8A => array(0x80 => array(0xE2 => 1)), // \x{200A} 8768 0x8B => array(0x80 => array(0xE2 => 1)), // \x{200B} 8769 0x8C => array(0x80 => array(0xE2 => 1)), // \x{200C} 8770 0x8F => array(0x80 => array(0xE2 => 1)), // \x{200F} 8771 0xA8 => array(0x80 => array(0xE2 => 1)), // \x{2028} 8772 0xA9 => array(0x80 => array(0xE2 => 1)), // \x{2029} 8773 0xAA => array(0x80 => array(0xE2 => 1)), // \x{202A} 8774 0xAB => array(0x80 => array(0xE2 => 1)), // \x{202B} 8775 0xAC => array(0x80 => array(0xE2 => 1)), // \x{202C} 8776 0xAE => array(0x80 => array(0xE2 => 1)), // \x{202E} 8777 0xAF => array(0x80 => array(0xE2 => 1)), // \x{202F} 8778 0xA4 => array(0x85 => array(0xE3 => 1)), // \x{3164} 8779 0xBF => array(0xBB => array(0xEF => 1)), // \x{FEFF} 8780 0xB9 => array(0xBF => array(0xEF => 1)), // \x{FFF9} 8781 0xBA => array(0xBF => array(0xEF => 1)), // \x{FFFA} 8782 0xBB => array(0xBF => array(0xEF => 1)), // \x{FFFB} 8783 ); 8784 8785 // Start from the beginning and work our way in 8786 $i = 0; 8787 do 8788 { 8789 // Check to see if we have matched a first character in our utf-8 array 8790 $offset = match_sequence($string, $hex_chrs); 8791 if(!$offset) 8792 { 8793 // If not, then we must have a "good" character and we don't need to do anymore processing 8794 break; 8795 } 8796 $string = substr($string, $offset); 8797 } 8798 while(++$i); 8799 8800 // Start from the end and work our way in 8801 $string = strrev($string); 8802 $i = 0; 8803 do 8804 { 8805 // Check to see if we have matched a first character in our utf-8 array 8806 $offset = match_sequence($string, $hex_chrs_rev); 8807 if(!$offset) 8808 { 8809 // If not, then we must have a "good" character and we don't need to do anymore processing 8810 break; 8811 } 8812 $string = substr($string, $offset); 8813 } 8814 while(++$i); 8815 $string = strrev($string); 8816 8817 if($charlist) 8818 { 8819 $string = trim($string, $charlist); 8820 } 8821 else 8822 { 8823 $string = trim($string); 8824 } 8825 8826 return $string; 8827 } 8828 8829 /** 8830 * Match a sequence 8831 * 8832 * @param string $string The string to match from 8833 * @param array $array The array to match from 8834 * @param int $i Number in the string 8835 * @param int $n Number of matches 8836 * @return int The number matched 8837 */ 8838 function match_sequence($string, $array, $i=0, $n=0) 8839 { 8840 if($string === "") 8841 { 8842 return 0; 8843 } 8844 8845 $ord = ord($string[$i]); 8846 if(array_key_exists($ord, $array)) 8847 { 8848 $level = $array[$ord]; 8849 ++$n; 8850 if(is_array($level)) 8851 { 8852 ++$i; 8853 return match_sequence($string, $level, $i, $n); 8854 } 8855 return $n; 8856 } 8857 8858 return 0; 8859 } 8860 8861 /** 8862 * Obtain the version of GD installed. 8863 * 8864 * @return float|null Version of GD 8865 */ 8866 function gd_version() 8867 { 8868 static $gd_version; 8869 8870 if($gd_version) 8871 { 8872 return $gd_version; 8873 } 8874 8875 if(!extension_loaded('gd')) 8876 { 8877 return null; 8878 } 8879 8880 if(function_exists("gd_info")) 8881 { 8882 $gd_info = gd_info(); 8883 preg_match('/\d/', $gd_info['GD Version'], $gd); 8884 $gd_version = $gd[0]; 8885 } 8886 else 8887 { 8888 ob_start(); 8889 phpinfo(8); 8890 $info = ob_get_contents(); 8891 ob_end_clean(); 8892 $info = stristr($info, 'gd version'); 8893 preg_match('/\d/', $info, $gd); 8894 $gd_version = $gd[0]; 8895 } 8896 8897 return $gd_version; 8898 } 8899 8900 /* 8901 * Validates an UTF-8 string. 8902 * 8903 * @param string $input The string to be checked 8904 * @param boolean $allow_mb4 Allow 4 byte UTF-8 characters? 8905 * @param boolean $return Return the cleaned string? 8906 * @return string|boolean Cleaned string or boolean 8907 */ 8908 function validate_utf8_string($input, $allow_mb4=true, $return=true) 8909 { 8910 // Valid UTF-8 sequence? 8911 if(!preg_match('##u', $input)) 8912 { 8913 $string = ''; 8914 $len = strlen($input); 8915 for($i = 0; $i < $len; $i++) 8916 { 8917 $c = ord($input[$i]); 8918 if($c > 128) 8919 { 8920 if($c > 247 || $c <= 191) 8921 { 8922 if($return) 8923 { 8924 $string .= '?'; 8925 continue; 8926 } 8927 else 8928 { 8929 return false; 8930 } 8931 } 8932 elseif($c > 239) 8933 { 8934 $bytes = 4; 8935 } 8936 elseif($c > 223) 8937 { 8938 $bytes = 3; 8939 } 8940 elseif($c > 191) 8941 { 8942 $bytes = 2; 8943 } 8944 if(($i + $bytes) > $len) 8945 { 8946 if($return) 8947 { 8948 $string .= '?'; 8949 break; 8950 } 8951 else 8952 { 8953 return false; 8954 } 8955 } 8956 $valid = true; 8957 $multibytes = $input[$i]; 8958 while($bytes > 1) 8959 { 8960 $i++; 8961 $b = ord($input[$i]); 8962 if($b < 128 || $b > 191) 8963 { 8964 if($return) 8965 { 8966 $valid = false; 8967 $string .= '?'; 8968 break; 8969 } 8970 else 8971 { 8972 return false; 8973 } 8974 } 8975 else 8976 { 8977 $multibytes .= $input[$i]; 8978 } 8979 $bytes--; 8980 } 8981 if($valid) 8982 { 8983 $string .= $multibytes; 8984 } 8985 } 8986 else 8987 { 8988 $string .= $input[$i]; 8989 } 8990 } 8991 $input = $string; 8992 } 8993 if($return) 8994 { 8995 if($allow_mb4) 8996 { 8997 return $input; 8998 } 8999 else 9000 { 9001 return preg_replace("#[^\\x00-\\x7F][\\x80-\\xBF]{3,}#", '?', $input); 9002 } 9003 } 9004 else 9005 { 9006 if($allow_mb4) 9007 { 9008 return true; 9009 } 9010 else 9011 { 9012 return !preg_match("#[^\\x00-\\x7F][\\x80-\\xBF]{3,}#", $input); 9013 } 9014 } 9015 } 9016 9017 /** 9018 * Send a Private Message to a user. 9019 * 9020 * @param array $pm Array containing: 'subject', 'message', 'touid' and 'receivepms' (the latter should reflect the value found in the users table: receivepms and receivefrombuddy) 9021 * @param int $fromid Sender UID (0 if you want to use $mybb->user['uid'] or -1 to use MyBB Engine) 9022 * @param bool $admin_override Whether or not do override user defined options for receiving PMs 9023 * @return bool True if PM sent 9024 */ 9025 function send_pm($pm, $fromid = 0, $admin_override=false) 9026 { 9027 global $lang, $mybb, $db, $session; 9028 9029 if($mybb->settings['enablepms'] == 0) 9030 { 9031 return false; 9032 } 9033 9034 if(!is_array($pm)) 9035 { 9036 return false; 9037 } 9038 9039 if(isset($pm['language'])) 9040 { 9041 if($pm['language'] != $mybb->user['language'] && $lang->language_exists($pm['language'])) 9042 { 9043 // Load user language 9044 $lang->set_language($pm['language']); 9045 $lang->load($pm['language_file']); 9046 9047 $revert = true; 9048 } 9049 9050 foreach(array('subject', 'message') as $key) 9051 { 9052 if(is_array($pm[$key])) 9053 { 9054 $lang_string = $lang->{$pm[$key][0]}; 9055 $num_args = count($pm[$key]); 9056 9057 for($i = 1; $i < $num_args; $i++) 9058 { 9059 $lang_string = str_replace('{'.$i.'}', $pm[$key][$i], $lang_string); 9060 } 9061 } 9062 else 9063 { 9064 $lang_string = $lang->{$pm[$key]}; 9065 } 9066 9067 $pm[$key] = $lang_string; 9068 } 9069 9070 if(isset($revert)) 9071 { 9072 // Revert language 9073 $lang->set_language($mybb->user['language']); 9074 $lang->load($pm['language_file']); 9075 } 9076 } 9077 9078 if(empty($pm['subject']) || empty($pm['message']) || empty($pm['touid']) || (empty($pm['receivepms']) && !$admin_override)) 9079 { 9080 return false; 9081 } 9082 9083 require_once MYBB_ROOT."inc/datahandlers/pm.php"; 9084 9085 $pmhandler = new PMDataHandler(); 9086 9087 $subject = $pm['subject']; 9088 $message = $pm['message']; 9089 $toid = $pm['touid']; 9090 9091 // Our recipients 9092 if(is_array($toid)) 9093 { 9094 $recipients_to = $toid; 9095 } 9096 else 9097 { 9098 $recipients_to = array($toid); 9099 } 9100 9101 $recipients_bcc = array(); 9102 9103 // Workaround for eliminating PHP warnings in PHP 8. Ref: https://github.com/mybb/mybb/issues/4630#issuecomment-1369144163 9104 if(isset($pm['sender']['uid']) && $pm['sender']['uid'] === -1 && $fromid === -1) 9105 { 9106 $sender = array( 9107 "uid" => 0, 9108 "username" => '' 9109 ); 9110 } 9111 9112 // Determine user ID 9113 if((int)$fromid == 0) 9114 { 9115 $fromid = (int)$mybb->user['uid']; 9116 } 9117 elseif((int)$fromid < 0) 9118 { 9119 $fromid = 0; 9120 } 9121 9122 // Build our final PM array 9123 $pm = array( 9124 "subject" => $subject, 9125 "message" => $message, 9126 "icon" => -1, 9127 "fromid" => $fromid, 9128 "toid" => $recipients_to, 9129 "bccid" => $recipients_bcc, 9130 "do" => '', 9131 "pmid" => '' 9132 ); 9133 9134 // (continued) Workaround for eliminating PHP warnings in PHP 8. Ref: https://github.com/mybb/mybb/issues/4630#issuecomment-1369144163 9135 if(isset($sender)) 9136 { 9137 $pm['sender'] = $sender; 9138 } 9139 9140 if(isset($session)) 9141 { 9142 $pm['ipaddress'] = $session->packedip; 9143 } 9144 9145 $pm['options'] = array( 9146 "disablesmilies" => 0, 9147 "savecopy" => 0, 9148 "readreceipt" => 0 9149 ); 9150 9151 $pm['saveasdraft'] = 0; 9152 9153 // Admin override 9154 $pmhandler->admin_override = (int)$admin_override; 9155 9156 $pmhandler->set_data($pm); 9157 9158 if($pmhandler->validate_pm()) 9159 { 9160 $pmhandler->insert_pm(); 9161 return true; 9162 } 9163 9164 return false; 9165 } 9166 9167 /** 9168 * Log a user spam block from StopForumSpam (or other spam service providers...) 9169 * 9170 * @param string $username The username that the user was using. 9171 * @param string $email The email address the user was using. 9172 * @param string $ip_address The IP addres of the user. 9173 * @param array $data An array of extra data to go with the block (eg: confidence rating). 9174 * @return bool Whether the action was logged successfully. 9175 */ 9176 function log_spam_block($username = '', $email = '', $ip_address = '', $data = array()) 9177 { 9178 global $db, $session; 9179 9180 if(!is_array($data)) 9181 { 9182 $data = array($data); 9183 } 9184 9185 if(!$ip_address) 9186 { 9187 $ip_address = get_ip(); 9188 } 9189 9190 $ip_address = my_inet_pton($ip_address); 9191 9192 $insert_array = array( 9193 'username' => $db->escape_string($username), 9194 'email' => $db->escape_string($email), 9195 'ipaddress' => $db->escape_binary($ip_address), 9196 'dateline' => (int)TIME_NOW, 9197 'data' => $db->escape_string(@my_serialize($data)), 9198 ); 9199 9200 return (bool)$db->insert_query('spamlog', $insert_array); 9201 } 9202 9203 /** 9204 * Copy a file to the CDN. 9205 * 9206 * @param string $file_path The path to the file to upload to the CDN. 9207 * 9208 * @param string $uploaded_path The path the file was uploaded to, reference parameter for when this may be needed. 9209 * 9210 * @return bool Whether the file was copied successfully. 9211 */ 9212 function copy_file_to_cdn($file_path = '', &$uploaded_path = null) 9213 { 9214 global $mybb, $plugins; 9215 9216 $success = false; 9217 9218 $file_path = (string)$file_path; 9219 9220 $real_file_path = realpath($file_path); 9221 9222 $file_dir_path = dirname($real_file_path); 9223 $file_dir_path = str_replace(MYBB_ROOT, '', $file_dir_path); 9224 $file_dir_path = ltrim($file_dir_path, './\\'); 9225 9226 $file_name = basename($real_file_path); 9227 9228 if(file_exists($file_path)) 9229 { 9230 9231 if(is_object($plugins)) 9232 { 9233 $hook_args = array( 9234 'file_path' => &$file_path, 9235 'real_file_path' => &$real_file_path, 9236 'file_name' => &$file_name, 9237 'file_dir_path' => &$file_dir_path 9238 ); 9239 $plugins->run_hooks('copy_file_to_cdn_start', $hook_args); 9240 } 9241 9242 if(!empty($mybb->settings['usecdn']) && !empty($mybb->settings['cdnpath'])) 9243 { 9244 $cdn_path = rtrim($mybb->settings['cdnpath'], '/\\'); 9245 9246 if(substr($file_dir_path, 0, my_strlen(MYBB_ROOT)) == MYBB_ROOT) 9247 { 9248 $file_dir_path = str_replace(MYBB_ROOT, '', $file_dir_path); 9249 } 9250 9251 $cdn_upload_path = $cdn_path . DIRECTORY_SEPARATOR . $file_dir_path; 9252 9253 if(!($dir_exists = is_dir($cdn_upload_path))) 9254 { 9255 $dir_exists = @mkdir($cdn_upload_path, 0777, true); 9256 } 9257 9258 if($dir_exists) 9259 { 9260 if(($cdn_upload_path = realpath($cdn_upload_path)) !== false) 9261 { 9262 $success = @copy($file_path, $cdn_upload_path.DIRECTORY_SEPARATOR.$file_name); 9263 9264 if($success) 9265 { 9266 $uploaded_path = $cdn_upload_path; 9267 } 9268 } 9269 } 9270 } 9271 9272 if(is_object($plugins)) 9273 { 9274 $hook_args = array( 9275 'file_path' => &$file_path, 9276 'real_file_path' => &$real_file_path, 9277 'file_name' => &$file_name, 9278 'uploaded_path' => &$uploaded_path, 9279 'success' => &$success, 9280 ); 9281 9282 $plugins->run_hooks('copy_file_to_cdn_end', $hook_args); 9283 } 9284 } 9285 9286 return $success; 9287 } 9288 9289 /** 9290 * Validate an url 9291 * 9292 * @param string $url The url to validate. 9293 * @param bool $relative_path Whether or not the url could be a relative path. 9294 * @param bool $allow_local Whether or not the url could be pointing to local networks. 9295 * 9296 * @return bool Whether this is a valid url. 9297 */ 9298 function my_validate_url($url, $relative_path=false, $allow_local=false) 9299 { 9300 if($allow_local) 9301 { 9302 $regex = '_^(?:(?:https?|ftp)://)(?:\S+(?::\S*)?@)?(?:(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:localhost|(?:(?:[a-z\x{00a1}-\x{ffff}0-9]-*)*[a-z\x{00a1}-\x{ffff}0-9]+)(?:\.(?:[a-z\x{00a1}-\x{ffff}0-9]-*)*[a-z\x{00a1}-\x{ffff}0-9]+)*(?:\.(?:[a-z\x{00a1}-\x{ffff}]{2,}))\.?))(?::\d{2,5})?(?:[/?#]\S*)?$_iuS'; 9303 } 9304 else 9305 { 9306 $regex = '_^(?:(?:https?|ftp)://)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\x{00a1}-\x{ffff}0-9]-*)*[a-z\x{00a1}-\x{ffff}0-9]+)(?:\.(?:[a-z\x{00a1}-\x{ffff}0-9]-*)*[a-z\x{00a1}-\x{ffff}0-9]+)*(?:\.(?:[a-z\x{00a1}-\x{ffff}]{2,}))\.?)(?::\d{2,5})?(?:[/?#]\S*)?$_iuS'; 9307 } 9308 9309 if($relative_path && my_substr($url, 0, 1) == '/' || preg_match($regex, $url)) 9310 { 9311 return true; 9312 } 9313 return false; 9314 } 9315 9316 /** 9317 * Strip html tags from string, also removes <script> and <style> contents. 9318 * 9319 * @deprecated 9320 * @param string $string String to stripe 9321 * @param string $allowable_tags Allowed html tags 9322 * 9323 * @return string Striped string 9324 */ 9325 function my_strip_tags($string, $allowable_tags = '') 9326 { 9327 $pattern = array( 9328 '@(<)style[^(>)]*?(>).*?(<)/style(>)@siu', 9329 '@(<)script[^(>)]*?.*?(<)/script(>)@siu', 9330 '@<style[^>]*?>.*?</style>@siu', 9331 '@<script[^>]*?.*?</script>@siu', 9332 ); 9333 $string = preg_replace($pattern, '', $string); 9334 return strip_tags($string, $allowable_tags); 9335 } 9336 9337 /** 9338 * Escapes a RFC 4180-compliant CSV string. 9339 * Based on https://github.com/Automattic/camptix/blob/f80725094440bf09861383b8f11e96c177c45789/camptix.php#L2867 9340 * 9341 * @param string $string The string to be escaped 9342 * @param boolean $escape_active_content Whether or not to escape active content trigger characters 9343 * @return string The escaped string 9344 */ 9345 function my_escape_csv($string, $escape_active_content=true) 9346 { 9347 if($escape_active_content) 9348 { 9349 $active_content_triggers = array('=', '+', '-', '@'); 9350 $delimiters = array(',', ';', ':', '|', '^', "\n", "\t", " "); 9351 9352 $first_character = mb_substr($string, 0, 1); 9353 9354 if( 9355 in_array($first_character, $active_content_triggers, true) || 9356 in_array($first_character, $delimiters, true) 9357 ) 9358 { 9359 $string = "'".$string; 9360 } 9361 9362 foreach($delimiters as $delimiter) 9363 { 9364 foreach($active_content_triggers as $trigger) 9365 { 9366 $string = str_replace($delimiter.$trigger, $delimiter."'".$trigger, $string); 9367 } 9368 } 9369 } 9370 9371 $string = str_replace('"', '""', $string); 9372 9373 return $string; 9374 } 9375 9376 // Fallback function for 'array_column', PHP < 5.5.0 compatibility 9377 if(!function_exists('array_column')) 9378 { 9379 function array_column($input, $column_key) 9380 { 9381 $values = array(); 9382 if(!is_array($input)) 9383 { 9384 $input = array($input); 9385 } 9386 foreach($input as $val) 9387 { 9388 if(is_array($val) && isset($val[$column_key])) 9389 { 9390 $values[] = $val[$column_key]; 9391 } 9392 elseif(is_object($val) && isset($val->$column_key)) 9393 { 9394 $values[] = $val->$column_key; 9395 } 9396 } 9397 return $values; 9398 } 9399 } 9400 9401 /** 9402 * Performs a timing attack safe string comparison. 9403 * 9404 * @param string $known_string The first string to be compared. 9405 * @param string $user_string The second, user-supplied string to be compared. 9406 * @return bool Result of the comparison. 9407 */ 9408 function my_hash_equals($known_string, $user_string) 9409 { 9410 if(version_compare(PHP_VERSION, '5.6.0', '>=')) 9411 { 9412 return hash_equals($known_string, $user_string); 9413 } 9414 else 9415 { 9416 $known_string_length = my_strlen($known_string); 9417 $user_string_length = my_strlen($user_string); 9418 9419 if($user_string_length != $known_string_length) 9420 { 9421 return false; 9422 } 9423 9424 $result = 0; 9425 9426 for($i = 0; $i < $known_string_length; $i++) 9427 { 9428 $result |= ord($known_string[$i]) ^ ord($user_string[$i]); 9429 } 9430 9431 return $result === 0; 9432 } 9433 } 9434 9435 /** 9436 * Retrieves all referrals for a specified user 9437 * 9438 * @param int uid 9439 * @param int start position 9440 * @param int total entries 9441 * @param bool false (default) only return display info, true for all info 9442 * @return array 9443 */ 9444 function get_user_referrals($uid, $start=0, $limit=0, $full=false) 9445 { 9446 global $db; 9447 9448 $referrals = $query_options = array(); 9449 $uid = (int) $uid; 9450 9451 if($uid === 0) 9452 { 9453 return $referrals; 9454 } 9455 9456 if($start && $limit) 9457 { 9458 $query_options['limit_start'] = $start; 9459 } 9460 9461 if($limit) 9462 { 9463 $query_options['limit'] = $limit; 9464 } 9465 9466 $fields = 'uid, username, usergroup, displaygroup, regdate'; 9467 if($full === true) 9468 { 9469 $fields = '*'; 9470 } 9471 9472 $query = $db->simple_select('users', $fields, "referrer='{$uid}'", $query_options); 9473 9474 while($referral = $db->fetch_array($query)) 9475 { 9476 $referrals[] = $referral; 9477 } 9478 9479 return $referrals; 9480 } 9481 9482 /** 9483 * Initialize the parser and store the XML data to be parsed. 9484 * 9485 * @param string $data 9486 * @return MyBBXMLParser The constructed XML parser. 9487 */ 9488 function create_xml_parser($data) 9489 { 9490 if(version_compare(PHP_VERSION, '8.0', '>=')) 9491 { 9492 require_once MYBB_ROOT."inc/class_xmlparser.php"; 9493 9494 return new MyBBXMLParser($data); 9495 } 9496 else 9497 { 9498 require_once MYBB_ROOT."inc/class_xml.php"; 9499 9500 return new XMLParser($data); 9501 } 9502 } 9503 9504 /** 9505 * Make a filesystem path absolute. 9506 * 9507 * Returns as-is paths which are already absolute. 9508 * 9509 * @param string $path The input path. Can be either absolute or relative. 9510 * @param string $base The absolute base to which to append $path if $path is 9511 * relative. Must end in DIRECTORY_SEPARATOR or a forward 9512 * slash. 9513 * @return string An absolute filesystem path corresponding to the input path. 9514 */ 9515 function mk_path_abs($path, $base = MYBB_ROOT) 9516 { 9517 $iswin = strtoupper(substr(PHP_OS, 0, 3)) === 'WIN'; 9518 $char1 = my_substr($path, 0, 1); 9519 if($char1 != '/' && !($iswin && ($char1 == '\\' || preg_match('(^[a-zA-Z]:\\\\)', $path)))) 9520 { 9521 $path = $base.$path; 9522 } 9523 9524 return $path; 9525 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
2005 - 2021 © MyBB.de | Alle Rechte vorbehalten! | Sponsor: netcup | Cross-referenced by PHPXref |