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