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