[ Index ]

PHP Cross Reference of MyBB 1.8.21

title

Body

[close]

/inc/ -> functions.php (source)

   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")) . "&amp;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  
 180              require_once MYBB_ROOT."inc/db_".$config['database']['type'].".php";
 181              switch($config['database']['type'])
 182              {
 183                  case "sqlite":
 184                      $db = new DB_SQLite;
 185                      break;
 186                  case "pgsql":
 187                      $db = new DB_PgSQL;
 188                      break;
 189                  case "mysqli":
 190                      $db = new DB_MySQLi;
 191                      break;
 192                  default:
 193                      $db = new DB_MySQL;
 194              }
 195  
 196              $db->connect($config['database']);
 197              if(!defined("TABLE_PREFIX"))
 198              {
 199                  define("TABLE_PREFIX", $config['database']['table_prefix']);
 200              }
 201              $db->set_table_prefix(TABLE_PREFIX);
 202          }
 203      }
 204  
 205      // Cache object deconstructed? reconstruct
 206      if(!is_object($cache))
 207      {
 208          require_once  MYBB_ROOT."inc/class_datacache.php";
 209          $cache = new datacache;
 210          $cache->cache();
 211      }
 212  
 213      // And finally.. plugins
 214      if(!is_object($plugins) && !defined("NO_PLUGINS") && !($mybb->settings['no_plugins'] == 1))
 215      {
 216          require_once  MYBB_ROOT."inc/class_plugins.php";
 217          $plugins = new pluginSystem;
 218          $plugins->load();
 219      }
 220  
 221      // We have some shutdown queries needing to be run
 222      if(is_array($shutdown_queries))
 223      {
 224          // Loop through and run them all
 225          foreach($shutdown_queries as $query)
 226          {
 227              $db->query($query);
 228          }
 229      }
 230  
 231      // Run any shutdown functions if we have them
 232      if(is_array($shutdown_functions))
 233      {
 234          foreach($shutdown_functions as $function)
 235          {
 236              call_user_func_array($function['function'], $function['arguments']);
 237          }
 238      }
 239  
 240      $done_shutdown = true;
 241  }
 242  
 243  /**
 244   * Sends a specified amount of messages from the mail queue
 245   *
 246   * @param int $count The number of messages to send (Defaults to 10)
 247   */
 248  function send_mail_queue($count=10)
 249  {
 250      global $db, $cache, $plugins;
 251  
 252      $plugins->run_hooks("send_mail_queue_start");
 253  
 254      // Check to see if the mail queue has messages needing to be sent
 255      $mailcache = $cache->read("mailqueue");
 256      if($mailcache['queue_size'] > 0 && ($mailcache['locked'] == 0 || $mailcache['locked'] < TIME_NOW-300))
 257      {
 258          // Lock the queue so no other messages can be sent whilst these are (for popular boards)
 259          $cache->update_mailqueue(0, TIME_NOW);
 260  
 261          // Fetch emails for this page view - and send them
 262          $query = $db->simple_select("mailqueue", "*", "", array("order_by" => "mid", "order_dir" => "asc", "limit_start" => 0, "limit" => $count));
 263  
 264          while($email = $db->fetch_array($query))
 265          {
 266              // Delete the message from the queue
 267              $db->delete_query("mailqueue", "mid='{$email['mid']}'");
 268  
 269              if($db->affected_rows() == 1)
 270              {
 271                  my_mail($email['mailto'], $email['subject'], $email['message'], $email['mailfrom'], "", $email['headers'], true);
 272              }
 273          }
 274          // Update the mailqueue cache and remove the lock
 275          $cache->update_mailqueue(TIME_NOW, 0);
 276      }
 277  
 278      $plugins->run_hooks("send_mail_queue_end");
 279  }
 280  
 281  /**
 282   * Parses the contents of a page before outputting it.
 283   *
 284   * @param string $contents The contents of the page.
 285   * @return string The parsed page.
 286   */
 287  function parse_page($contents)
 288  {
 289      global $lang, $theme, $mybb, $htmldoctype, $archive_url, $error_handler;
 290  
 291      $contents = str_replace('<navigation>', build_breadcrumb(), $contents);
 292      $contents = str_replace('<archive_url>', $archive_url, $contents);
 293  
 294      if($htmldoctype)
 295      {
 296          $contents = $htmldoctype.$contents;
 297      }
 298      else
 299      {
 300          $contents = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n".$contents;
 301      }
 302  
 303      $contents = str_replace("<html", "<html xmlns=\"http://www.w3.org/1999/xhtml\"", $contents);
 304  
 305      if($lang->settings['rtl'] == 1)
 306      {
 307          $contents = str_replace("<html", "<html dir=\"rtl\"", $contents);
 308      }
 309  
 310      if($lang->settings['htmllang'])
 311      {
 312          $contents = str_replace("<html", "<html xml:lang=\"".$lang->settings['htmllang']."\" lang=\"".$lang->settings['htmllang']."\"", $contents);
 313      }
 314  
 315      if($error_handler->warnings)
 316      {
 317          $contents = str_replace("<body>", "<body>\n".$error_handler->show_warnings(), $contents);
 318      }
 319  
 320      return $contents;
 321  }
 322  
 323  /**
 324   * Turn a unix timestamp in to a "friendly" date/time format for the user.
 325   *
 326   * @param string $format A date format (either relative, normal or PHP's date() structure).
 327   * @param int $stamp The unix timestamp the date should be generated for.
 328   * @param int|string $offset The offset in hours that should be applied to times. (timezones) Or an empty string to determine that automatically
 329   * @param int $ty Whether or not to use today/yesterday formatting.
 330   * @param boolean $adodb Whether or not to use the adodb time class for < 1970 or > 2038 times
 331   * @return string The formatted timestamp.
 332   */
 333  function my_date($format, $stamp=0, $offset="", $ty=1, $adodb=false)
 334  {
 335      global $mybb, $lang, $mybbadmin, $plugins;
 336  
 337      // If the stamp isn't set, use TIME_NOW
 338      if(empty($stamp))
 339      {
 340          $stamp = TIME_NOW;
 341      }
 342  
 343      if(!$offset && $offset != '0')
 344      {
 345          if(isset($mybb->user['uid']) && $mybb->user['uid'] != 0 && array_key_exists("timezone", $mybb->user))
 346          {
 347              $offset = (float)$mybb->user['timezone'];
 348              $dstcorrection = $mybb->user['dst'];
 349          }
 350          elseif(defined("IN_ADMINCP"))
 351          {
 352              $offset = (float)$mybbadmin['timezone'];
 353              $dstcorrection = $mybbadmin['dst'];
 354          }
 355          else
 356          {
 357              $offset = (float)$mybb->settings['timezoneoffset'];
 358              $dstcorrection = $mybb->settings['dstcorrection'];
 359          }
 360  
 361          // If DST correction is enabled, add an additional hour to the timezone.
 362          if($dstcorrection == 1)
 363          {
 364              ++$offset;
 365              if(my_substr($offset, 0, 1) != "-")
 366              {
 367                  $offset = "+".$offset;
 368              }
 369          }
 370      }
 371  
 372      if($offset == "-")
 373      {
 374          $offset = 0;
 375      }
 376  
 377      // Using ADOdb?
 378      if($adodb == true && !function_exists('adodb_date'))
 379      {
 380          $adodb = false;
 381      }
 382  
 383      $todaysdate = $yesterdaysdate = '';
 384      if($ty && ($format == $mybb->settings['dateformat'] || $format == 'relative' || $format == 'normal'))
 385      {
 386          $_stamp = TIME_NOW;
 387          if($adodb == true)
 388          {
 389              $date = adodb_date($mybb->settings['dateformat'], $stamp + ($offset * 3600));
 390              $todaysdate = adodb_date($mybb->settings['dateformat'], $_stamp + ($offset * 3600));
 391              $yesterdaysdate = adodb_date($mybb->settings['dateformat'], ($_stamp - 86400) + ($offset * 3600));
 392          }
 393          else
 394          {
 395              $date = gmdate($mybb->settings['dateformat'], $stamp + ($offset * 3600));
 396              $todaysdate = gmdate($mybb->settings['dateformat'], $_stamp + ($offset * 3600));
 397              $yesterdaysdate = gmdate($mybb->settings['dateformat'], ($_stamp - 86400) + ($offset * 3600));
 398          }
 399      }
 400  
 401      if($format == 'relative')
 402      {
 403          // Relative formats both date and time
 404          $real_date = $real_time = '';
 405          if($adodb == true)
 406          {
 407              $real_date = adodb_date($mybb->settings['dateformat'], $stamp + ($offset * 3600));
 408              $real_time = $mybb->settings['datetimesep'];
 409              $real_time .= adodb_date($mybb->settings['timeformat'], $stamp + ($offset * 3600));
 410          }
 411          else
 412          {
 413              $real_date = gmdate($mybb->settings['dateformat'], $stamp + ($offset * 3600));
 414              $real_time = $mybb->settings['datetimesep'];
 415              $real_time .= gmdate($mybb->settings['timeformat'], $stamp + ($offset * 3600));
 416          }
 417  
 418          if($ty != 2 && abs(TIME_NOW - $stamp) < 3600)
 419          {
 420              $diff = TIME_NOW - $stamp;
 421              $relative = array('prefix' => '', 'minute' => 0, 'plural' => $lang->rel_minutes_plural, 'suffix' => $lang->rel_ago);
 422  
 423              if($diff < 0)
 424              {
 425                  $diff = abs($diff);
 426                  $relative['suffix'] = '';
 427                  $relative['prefix'] = $lang->rel_in;
 428              }
 429  
 430              $relative['minute'] = floor($diff / 60);
 431  
 432              if($relative['minute'] <= 1)
 433              {
 434                  $relative['minute'] = 1;
 435                  $relative['plural'] = $lang->rel_minutes_single;
 436              }
 437  
 438              if($diff <= 60)
 439              {
 440                  // Less than a minute
 441                  $relative['prefix'] = $lang->rel_less_than;
 442              }
 443  
 444              $date = $lang->sprintf($lang->rel_time, $relative['prefix'], $relative['minute'], $relative['plural'], $relative['suffix'], $real_date, $real_time);
 445          }
 446          elseif($ty != 2 && abs(TIME_NOW - $stamp) < 43200)
 447          {
 448              $diff = TIME_NOW - $stamp;
 449              $relative = array('prefix' => '', 'hour' => 0, 'plural' => $lang->rel_hours_plural, 'suffix' => $lang->rel_ago);
 450  
 451              if($diff < 0)
 452              {
 453                  $diff = abs($diff);
 454                  $relative['suffix'] = '';
 455                  $relative['prefix'] = $lang->rel_in;
 456              }
 457  
 458              $relative['hour'] = floor($diff / 3600);
 459  
 460              if($relative['hour'] <= 1)
 461              {
 462                  $relative['hour'] = 1;
 463                  $relative['plural'] = $lang->rel_hours_single;
 464              }
 465  
 466              $date = $lang->sprintf($lang->rel_time, $relative['prefix'], $relative['hour'], $relative['plural'], $relative['suffix'], $real_date, $real_time);
 467          }
 468          else
 469          {
 470              if($ty)
 471              {
 472                  if($todaysdate == $date)
 473                  {
 474                      $date = $lang->sprintf($lang->today_rel, $real_date);
 475                  }
 476                  else if($yesterdaysdate == $date)
 477                  {
 478                      $date = $lang->sprintf($lang->yesterday_rel, $real_date);
 479                  }
 480              }
 481  
 482              $date .= $mybb->settings['datetimesep'];
 483              if($adodb == true)
 484              {
 485                  $date .= adodb_date($mybb->settings['timeformat'], $stamp + ($offset * 3600));
 486              }
 487              else
 488              {
 489                  $date .= gmdate($mybb->settings['timeformat'], $stamp + ($offset * 3600));
 490              }
 491          }
 492      }
 493      elseif($format == 'normal')
 494      {
 495          // Normal format both date and time
 496          if($ty != 2)
 497          {
 498              if($todaysdate == $date)
 499              {
 500                  $date = $lang->today;
 501              }
 502              else if($yesterdaysdate == $date)
 503              {
 504                  $date = $lang->yesterday;
 505              }
 506          }
 507  
 508          $date .= $mybb->settings['datetimesep'];
 509          if($adodb == true)
 510          {
 511              $date .= adodb_date($mybb->settings['timeformat'], $stamp + ($offset * 3600));
 512          }
 513          else
 514          {
 515              $date .= gmdate($mybb->settings['timeformat'], $stamp + ($offset * 3600));
 516          }
 517      }
 518      else
 519      {
 520          if($ty && $format == $mybb->settings['dateformat'])
 521          {
 522              if($todaysdate == $date)
 523              {
 524                  $date = $lang->today;
 525              }
 526              else if($yesterdaysdate == $date)
 527              {
 528                  $date = $lang->yesterday;
 529              }
 530          }
 531          else
 532          {
 533              if($adodb == true)
 534              {
 535                  $date = adodb_date($format, $stamp + ($offset * 3600));
 536              }
 537              else
 538              {
 539                  $date = gmdate($format, $stamp + ($offset * 3600));
 540              }
 541          }
 542      }
 543  
 544      if(is_object($plugins))
 545      {
 546          $date = $plugins->run_hooks("my_date", $date);
 547      }
 548  
 549      return $date;
 550  }
 551  
 552  /**
 553   * Sends an email using PHP's mail function, formatting it appropriately.
 554   *
 555   * @param string $to Address the email should be addressed to.
 556   * @param string $subject The subject of the email being sent.
 557   * @param string $message The message being sent.
 558   * @param string $from The from address of the email, if blank, the board name will be used.
 559   * @param string $charset The chracter set being used to send this email.
 560   * @param string $headers
 561   * @param boolean $keep_alive Do we wish to keep the connection to the mail server alive to send more than one message (SMTP only)
 562   * @param string $format The format of the email to be sent (text or html). text is default
 563   * @param string $message_text The text message of the email if being sent in html format, for email clients that don't support html
 564   * @param string $return_email The email address to return to. Defaults to admin return email address.
 565   * @return bool
 566   */
 567  function my_mail($to, $subject, $message, $from="", $charset="", $headers="", $keep_alive=false, $format="text", $message_text="", $return_email="")
 568  {
 569      global $mybb;
 570      static $mail;
 571  
 572      // Does our object not exist? Create it
 573      if(!is_object($mail))
 574      {
 575          require_once  MYBB_ROOT."inc/class_mailhandler.php";
 576  
 577          if($mybb->settings['mail_handler'] == 'smtp')
 578          {
 579              require_once  MYBB_ROOT."inc/mailhandlers/smtp.php";
 580              $mail = new SmtpMail();
 581          }
 582          else
 583          {
 584              require_once  MYBB_ROOT."inc/mailhandlers/php.php";
 585              $mail = new PhpMail();
 586          }
 587      }
 588  
 589      // Using SMTP based mail
 590      if($mybb->settings['mail_handler'] == 'smtp')
 591      {
 592          if($keep_alive == true)
 593          {
 594              $mail->keep_alive = true;
 595          }
 596      }
 597  
 598      // Using PHP based mail()
 599      else
 600      {
 601          if($mybb->settings['mail_parameters'] != '')
 602          {
 603              $mail->additional_parameters = $mybb->settings['mail_parameters'];
 604          }
 605      }
 606  
 607      // Build and send
 608      $mail->build_message($to, $subject, $message, $from, $charset, $headers, $format, $message_text, $return_email);
 609      return $mail->send();
 610  }
 611  
 612  /**
 613   * Generates a unique code for POST requests to prevent XSS/CSRF attacks
 614   *
 615   * @return string The generated code
 616   */
 617  function generate_post_check()
 618  {
 619      global $mybb, $session;
 620      if($mybb->user['uid'])
 621      {
 622          return md5($mybb->user['loginkey'].$mybb->user['salt'].$mybb->user['regdate']);
 623      }
 624      // Guests get a special string
 625      else
 626      {
 627          return md5($session->sid.$mybb->config['database']['username'].$mybb->settings['internal']['encryption_key']);
 628      }
 629  }
 630  
 631  /**
 632   * Verifies a POST check code is valid, if not shows an error (silently returns false on silent parameter)
 633   *
 634   * @param string $code The incoming POST check code
 635   * @param boolean $silent Silent mode or not (silent mode will not show the error to the user but returns false)
 636   * @return bool
 637   */
 638  function verify_post_check($code, $silent=false)
 639  {
 640      global $lang;
 641      if(generate_post_check() !== $code)
 642      {
 643          if($silent == true)
 644          {
 645              return false;
 646          }
 647          else
 648          {
 649              if(defined("IN_ADMINCP"))
 650              {
 651                  return false;
 652              }
 653              else
 654              {
 655                  error($lang->invalid_post_code);
 656              }
 657          }
 658      }
 659      else
 660      {
 661          return true;
 662      }
 663  }
 664  
 665  /**
 666   * Return a parent list for the specified forum.
 667   *
 668   * @param int $fid The forum id to get the parent list for.
 669   * @return string The comma-separated parent list.
 670   */
 671  function get_parent_list($fid)
 672  {
 673      global $forum_cache;
 674      static $forumarraycache;
 675  
 676      if($forumarraycache[$fid])
 677      {
 678          return $forumarraycache[$fid]['parentlist'];
 679      }
 680      elseif($forum_cache[$fid])
 681      {
 682          return $forum_cache[$fid]['parentlist'];
 683      }
 684      else
 685      {
 686          cache_forums();
 687          return $forum_cache[$fid]['parentlist'];
 688      }
 689  }
 690  
 691  /**
 692   * Build a parent list of a specific forum, suitable for querying
 693   *
 694   * @param int $fid The forum ID
 695   * @param string $column The column name to add to the query
 696   * @param string $joiner The joiner for each forum for querying (OR | AND | etc)
 697   * @param string $parentlist The parent list of the forum - if you have it
 698   * @return string The query string generated
 699   */
 700  function build_parent_list($fid, $column="fid", $joiner="OR", $parentlist="")
 701  {
 702      if(!$parentlist)
 703      {
 704          $parentlist = get_parent_list($fid);
 705      }
 706  
 707      $parentsexploded = explode(",", $parentlist);
 708      $builtlist = "(";
 709      $sep = '';
 710  
 711      foreach($parentsexploded as $key => $val)
 712      {
 713          $builtlist .= "$sep$column='$val'";
 714          $sep = " $joiner ";
 715      }
 716  
 717      $builtlist .= ")";
 718  
 719      return $builtlist;
 720  }
 721  
 722  /**
 723   * Load the forum cache in to memory
 724   *
 725   * @param boolean $force True to force a reload of the cache
 726   * @return array The forum cache
 727   */
 728  function cache_forums($force=false)
 729  {
 730      global $forum_cache, $cache;
 731  
 732      if($force == true)
 733      {
 734          $forum_cache = $cache->read("forums", 1);
 735          return $forum_cache;
 736      }
 737  
 738      if(!$forum_cache)
 739      {
 740          $forum_cache = $cache->read("forums");
 741          if(!$forum_cache)
 742          {
 743              $cache->update_forums();
 744              $forum_cache = $cache->read("forums", 1);
 745          }
 746      }
 747      return $forum_cache;
 748  }
 749  
 750  /**
 751   * Generate an array of all child and descendant forums for a specific forum.
 752   *
 753   * @param int $fid The forum ID
 754   * @return Array of descendants
 755   */
 756  function get_child_list($fid)
 757  {
 758      static $forums_by_parent;
 759  
 760      $forums = array();
 761      if(!is_array($forums_by_parent))
 762      {
 763          $forum_cache = cache_forums();
 764          foreach($forum_cache as $forum)
 765          {
 766              if($forum['active'] != 0)
 767              {
 768                  $forums_by_parent[$forum['pid']][$forum['fid']] = $forum;
 769              }
 770          }
 771      }
 772      if(!is_array($forums_by_parent[$fid]))
 773      {
 774          return $forums;
 775      }
 776  
 777      foreach($forums_by_parent[$fid] as $forum)
 778      {
 779          $forums[] = $forum['fid'];
 780          $children = get_child_list($forum['fid']);
 781          if(is_array($children))
 782          {
 783              $forums = array_merge($forums, $children);
 784          }
 785      }
 786      return $forums;
 787  }
 788  
 789  /**
 790   * Produce a friendly error message page
 791   *
 792   * @param string $error The error message to be shown
 793   * @param string $title The title of the message shown in the title of the page and the error table
 794   */
 795  function error($error="", $title="")
 796  {
 797      global $header, $footer, $theme, $headerinclude, $db, $templates, $lang, $mybb, $plugins;
 798  
 799      $error = $plugins->run_hooks("error", $error);
 800      if(!$error)
 801      {
 802          $error = $lang->unknown_error;
 803      }
 804  
 805      // AJAX error message?
 806      if($mybb->get_input('ajax', MyBB::INPUT_INT))
 807      {
 808          // Send our headers.
 809          @header("Content-type: application/json; charset={$lang->settings['charset']}");
 810          echo json_encode(array("errors" => array($error)));
 811          exit;
 812      }
 813  
 814      if(!$title)
 815      {
 816          $title = $mybb->settings['bbname'];
 817      }
 818  
 819      $timenow = my_date('relative', TIME_NOW);
 820      reset_breadcrumb();
 821      add_breadcrumb($lang->error);
 822  
 823      eval("\$errorpage = \"".$templates->get("error")."\";");
 824      output_page($errorpage);
 825  
 826      exit;
 827  }
 828  
 829  /**
 830   * Produce an error message for displaying inline on a page
 831   *
 832   * @param array $errors Array of errors to be shown
 833   * @param string $title The title of the error message
 834   * @param array $json_data JSON data to be encoded (we may want to send more data; e.g. newreply.php uses this for CAPTCHA)
 835   * @return string The inline error HTML
 836   */
 837  function inline_error($errors, $title="", $json_data=array())
 838  {
 839      global $theme, $mybb, $db, $lang, $templates;
 840  
 841      if(!$title)
 842      {
 843          $title = $lang->please_correct_errors;
 844      }
 845  
 846      if(!is_array($errors))
 847      {
 848          $errors = array($errors);
 849      }
 850  
 851      // AJAX error message?
 852      if($mybb->get_input('ajax', MyBB::INPUT_INT))
 853      {
 854          // Send our headers.
 855          @header("Content-type: application/json; charset={$lang->settings['charset']}");
 856  
 857          if(empty($json_data))
 858          {
 859              echo json_encode(array("errors" => $errors));
 860          }
 861          else
 862          {
 863              echo json_encode(array_merge(array("errors" => $errors), $json_data));
 864          }
 865          exit;
 866      }
 867  
 868      $errorlist = '';
 869  
 870      foreach($errors as $error)
 871      {
 872          eval("\$errorlist .= \"".$templates->get("error_inline_item")."\";");
 873      }
 874  
 875      eval("\$errors = \"".$templates->get("error_inline")."\";");
 876  
 877      return $errors;
 878  }
 879  
 880  /**
 881   * Presents the user with a "no permission" page
 882   */
 883  function error_no_permission()
 884  {
 885      global $mybb, $theme, $templates, $db, $lang, $plugins, $session;
 886  
 887      $time = TIME_NOW;
 888      $plugins->run_hooks("no_permission");
 889  
 890      $noperm_array = array (
 891          "nopermission" => '1',
 892          "location1" => 0,
 893          "location2" => 0
 894      );
 895  
 896      $db->update_query("sessions", $noperm_array, "sid='{$session->sid}'");
 897  
 898      if($mybb->get_input('ajax', MyBB::INPUT_INT))
 899      {
 900          // Send our headers.
 901          header("Content-type: application/json; charset={$lang->settings['charset']}");
 902          echo json_encode(array("errors" => array($lang->error_nopermission_user_ajax)));
 903          exit;
 904      }
 905  
 906      if($mybb->user['uid'])
 907      {
 908          $lang->error_nopermission_user_username = $lang->sprintf($lang->error_nopermission_user_username, htmlspecialchars_uni($mybb->user['username']));
 909          eval("\$errorpage = \"".$templates->get("error_nopermission_loggedin")."\";");
 910      }
 911      else
 912      {
 913          // Redirect to where the user came from
 914          $redirect_url = $_SERVER['PHP_SELF'];
 915          if($_SERVER['QUERY_STRING'])
 916          {
 917              $redirect_url .= '?'.$_SERVER['QUERY_STRING'];
 918          }
 919  
 920          $redirect_url = htmlspecialchars_uni($redirect_url);
 921  
 922          switch($mybb->settings['username_method'])
 923          {
 924              case 0:
 925                  $lang_username = $lang->username;
 926                  break;
 927              case 1:
 928                  $lang_username = $lang->username1;
 929                  break;
 930              case 2:
 931                  $lang_username = $lang->username2;
 932                  break;
 933              default:
 934                  $lang_username = $lang->username;
 935                  break;
 936          }
 937          eval("\$errorpage = \"".$templates->get("error_nopermission")."\";");
 938      }
 939  
 940      error($errorpage);
 941  }
 942  
 943  /**
 944   * Redirect the user to a given URL with a given message
 945   *
 946   * @param string $url The URL to redirect the user to
 947   * @param string $message The redirection message to be shown
 948   * @param string $title The title of the redirection page
 949   * @param boolean $force_redirect Force the redirect page regardless of settings
 950   */
 951  function redirect($url, $message="", $title="", $force_redirect=false)
 952  {
 953      global $header, $footer, $mybb, $theme, $headerinclude, $templates, $lang, $plugins;
 954  
 955      $redirect_args = array('url' => &$url, 'message' => &$message, 'title' => &$title);
 956  
 957      $plugins->run_hooks("redirect", $redirect_args);
 958  
 959      if($mybb->get_input('ajax', MyBB::INPUT_INT))
 960      {
 961          // Send our headers.
 962          //@header("Content-type: text/html; charset={$lang->settings['charset']}");
 963          $data = "<script type=\"text/javascript\">\n";
 964          if($message != "")
 965          {
 966              $data .=  'alert("'.addslashes($message).'");';
 967          }
 968          $url = str_replace("#", "&#", $url);
 969          $url = htmlspecialchars_decode($url);
 970          $url = str_replace(array("\n","\r",";"), "", $url);
 971          $data .=  'window.location = "'.addslashes($url).'";'."\n";
 972          $data .= "</script>\n";
 973          //exit;
 974  
 975          @header("Content-type: application/json; charset={$lang->settings['charset']}");
 976          echo json_encode(array("data" => $data));
 977          exit;
 978      }
 979  
 980      if(!$message)
 981      {
 982          $message = $lang->redirect;
 983      }
 984  
 985      $time = TIME_NOW;
 986      $timenow = my_date('relative', $time);
 987  
 988      if(!$title)
 989      {
 990          $title = $mybb->settings['bbname'];
 991      }
 992  
 993      // Show redirects only if both ACP and UCP settings are enabled, or ACP is enabled, and user is a guest, or they are forced.
 994      if($force_redirect == true || ($mybb->settings['redirects'] == 1 && ($mybb->user['showredirect'] == 1 || !$mybb->user['uid'])))
 995      {
 996          $url = str_replace("&amp;", "&", $url);
 997          $url = htmlspecialchars_uni($url);
 998  
 999          eval("\$redirectpage = \"".$templates->get("redirect")."\";");
1000          output_page($redirectpage);
1001      }
1002      else
1003      {
1004          $url = htmlspecialchars_decode($url);
1005          $url = str_replace(array("\n","\r",";"), "", $url);
1006  
1007          run_shutdown();
1008  
1009          if(!my_validate_url($url, true, true))
1010          {
1011              header("Location: {$mybb->settings['bburl']}/{$url}");
1012          }
1013          else
1014          {
1015              header("Location: {$url}");
1016          }
1017      }
1018  
1019      exit;
1020  }
1021  
1022  /**
1023   * Generate a listing of page - pagination
1024   *
1025   * @param int $count The number of items
1026   * @param int $perpage The number of items to be shown per page
1027   * @param int $page The current page number
1028   * @param string $url The URL to have page numbers tacked on to (If {page} is specified, the value will be replaced with the page #)
1029   * @param boolean $breadcrumb Whether or not the multipage is being shown in the navigation breadcrumb
1030   * @return string The generated pagination
1031   */
1032  function multipage($count, $perpage, $page, $url, $breadcrumb=false)
1033  {
1034      global $theme, $templates, $lang, $mybb;
1035  
1036      if($count <= $perpage)
1037      {
1038          return '';
1039      }
1040  
1041      $page = (int)$page;
1042  
1043      $url = str_replace("&amp;", "&", $url);
1044      $url = htmlspecialchars_uni($url);
1045  
1046      $pages = ceil($count / $perpage);
1047  
1048      $prevpage = '';
1049      if($page > 1)
1050      {
1051          $prev = $page-1;
1052          $page_url = fetch_page_url($url, $prev);
1053          eval("\$prevpage = \"".$templates->get("multipage_prevpage")."\";");
1054      }
1055  
1056      // Maximum number of "page bits" to show
1057      if(!$mybb->settings['maxmultipagelinks'])
1058      {
1059          $mybb->settings['maxmultipagelinks'] = 5;
1060      }
1061  
1062      $from = $page-floor($mybb->settings['maxmultipagelinks']/2);
1063      $to = $page+floor($mybb->settings['maxmultipagelinks']/2);
1064  
1065      if($from <= 0)
1066      {
1067          $from = 1;
1068          $to = $from+$mybb->settings['maxmultipagelinks']-1;
1069      }
1070  
1071      if($to > $pages)
1072      {
1073          $to = $pages;
1074          $from = $pages-$mybb->settings['maxmultipagelinks']+1;
1075          if($from <= 0)
1076          {
1077              $from = 1;
1078          }
1079      }
1080  
1081      if($to == 0)
1082      {
1083          $to = $pages;
1084      }
1085  
1086      $start = '';
1087      if($from > 1)
1088      {
1089          if($from-1 == 1)
1090          {
1091              $lang->multipage_link_start = '';
1092          }
1093  
1094          $page_url = fetch_page_url($url, 1);
1095          eval("\$start = \"".$templates->get("multipage_start")."\";");
1096      }
1097  
1098      $mppage = '';
1099      for($i = $from; $i <= $to; ++$i)
1100      {
1101          $page_url = fetch_page_url($url, $i);
1102          if($page == $i)
1103          {
1104              if($breadcrumb == true)
1105              {
1106                  eval("\$mppage .= \"".$templates->get("multipage_page_link_current")."\";");
1107              }
1108              else
1109              {
1110                  eval("\$mppage .= \"".$templates->get("multipage_page_current")."\";");
1111              }
1112          }
1113          else
1114          {
1115              eval("\$mppage .= \"".$templates->get("multipage_page")."\";");
1116          }
1117      }
1118  
1119      $end = '';
1120      if($to < $pages)
1121      {
1122          if($to+1 == $pages)
1123          {
1124              $lang->multipage_link_end = '';
1125          }
1126  
1127          $page_url = fetch_page_url($url, $pages);
1128          eval("\$end = \"".$templates->get("multipage_end")."\";");
1129      }
1130  
1131      $nextpage = '';
1132      if($page < $pages)
1133      {
1134          $next = $page+1;
1135          $page_url = fetch_page_url($url, $next);
1136          eval("\$nextpage = \"".$templates->get("multipage_nextpage")."\";");
1137      }
1138  
1139      $jumptopage = '';
1140      if($pages > ($mybb->settings['maxmultipagelinks']+1) && $mybb->settings['jumptopagemultipage'] == 1)
1141      {
1142          // 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
1143          $jump_url = fetch_page_url($url, 1);
1144          eval("\$jumptopage = \"".$templates->get("multipage_jump_page")."\";");
1145      }
1146  
1147      $multipage_pages = $lang->sprintf($lang->multipage_pages, $pages);
1148  
1149      if($breadcrumb == true)
1150      {
1151          eval("\$multipage = \"".$templates->get("multipage_breadcrumb")."\";");
1152      }
1153      else
1154      {
1155          eval("\$multipage = \"".$templates->get("multipage")."\";");
1156      }
1157  
1158      return $multipage;
1159  }
1160  
1161  /**
1162   * Generate a page URL for use by the multipage function
1163   *
1164   * @param string $url The URL being passed
1165   * @param int $page The page number
1166   * @return string
1167   */
1168  function fetch_page_url($url, $page)
1169  {
1170      if($page <= 1)
1171      {
1172          $find = array(
1173              "-page-{page}",
1174              "&amp;page={page}",
1175              "{page}"
1176          );
1177  
1178          // Remove "Page 1" to the defacto URL
1179          $url = str_replace($find, array("", "", $page), $url);
1180          return $url;
1181      }
1182      else if(strpos($url, "{page}") === false)
1183      {
1184          // If no page identifier is specified we tack it on to the end of the URL
1185          if(strpos($url, "?") === false)
1186          {
1187              $url .= "?";
1188          }
1189          else
1190          {
1191              $url .= "&amp;";
1192          }
1193  
1194          $url .= "page=$page";
1195      }
1196      else
1197      {
1198          $url = str_replace("{page}", $page, $url);
1199      }
1200  
1201      return $url;
1202  }
1203  
1204  /**
1205   * Fetch the permissions for a specific user
1206   *
1207   * @param int $uid The user ID, if no user ID is provided then current user's ID will be considered.
1208   * @return array Array of user permissions for the specified user
1209   */
1210  function user_permissions($uid=null)
1211  {
1212      global $mybb, $cache, $groupscache, $user_cache;
1213  
1214      // If no user id is specified, assume it is the current user
1215      if($uid === null)
1216      {
1217          $uid = $mybb->user['uid'];
1218      }
1219  
1220      // Its a guest. Return the group permissions directly from cache
1221      if($uid == 0)
1222      {
1223          return $groupscache[1];
1224      }
1225  
1226      // User id does not match current user, fetch permissions
1227      if($uid != $mybb->user['uid'])
1228      {
1229          // We've already cached permissions for this user, return them.
1230          if(!empty($user_cache[$uid]['permissions']))
1231          {
1232              return $user_cache[$uid]['permissions'];
1233          }
1234  
1235          // This user was not already cached, fetch their user information.
1236          if(empty($user_cache[$uid]))
1237          {
1238              $user_cache[$uid] = get_user($uid);
1239          }
1240  
1241          // Collect group permissions.
1242          $gid = $user_cache[$uid]['usergroup'].",".$user_cache[$uid]['additionalgroups'];
1243          $groupperms = usergroup_permissions($gid);
1244  
1245          // Store group permissions in user cache.
1246          $user_cache[$uid]['permissions'] = $groupperms;
1247          return $groupperms;
1248      }
1249      // This user is the current user, return their permissions
1250      else
1251      {
1252          return $mybb->usergroup;
1253      }
1254  }
1255  
1256  /**
1257   * Fetch the usergroup permissions for a specific group or series of groups combined
1258   *
1259   * @param int|string $gid A list of groups (Can be a single integer, or a list of groups separated by a comma)
1260   * @return array Array of permissions generated for the groups, containing also a list of comma-separated checked groups under 'all_usergroups' index
1261   */
1262  function usergroup_permissions($gid=0)
1263  {
1264      global $cache, $groupscache, $grouppermignore, $groupzerogreater;
1265  
1266      if(!is_array($groupscache))
1267      {
1268          $groupscache = $cache->read("usergroups");
1269      }
1270  
1271      $groups = explode(",", $gid);
1272  
1273      if(count($groups) == 1)
1274      {
1275          $groupscache[$gid]['all_usergroups'] = $gid;
1276          return $groupscache[$gid];
1277      }
1278  
1279      $usergroup = array();
1280      $usergroup['all_usergroups'] = $gid;
1281  
1282      foreach($groups as $gid)
1283      {
1284          if(trim($gid) == "" || empty($groupscache[$gid]))
1285          {
1286              continue;
1287          }
1288  
1289          foreach($groupscache[$gid] as $perm => $access)
1290          {
1291              if(!in_array($perm, $grouppermignore))
1292              {
1293                  if(isset($usergroup[$perm]))
1294                  {
1295                      $permbit = $usergroup[$perm];
1296                  }
1297                  else
1298                  {
1299                      $permbit = "";
1300                  }
1301  
1302                  // 0 represents unlimited for numerical group permissions (i.e. private message limit) so take that into account.
1303                  if(in_array($perm, $groupzerogreater) && ($access == 0 || $permbit === 0))
1304                  {
1305                      $usergroup[$perm] = 0;
1306                      continue;
1307                  }
1308  
1309                  if($access > $permbit || ($access == "yes" && $permbit == "no") || !$permbit) // Keep yes/no for compatibility?
1310                  {
1311                      $usergroup[$perm] = $access;
1312                  }
1313              }
1314          }
1315      }
1316  
1317      return $usergroup;
1318  }
1319  
1320  /**
1321   * Fetch the display group properties for a specific display group
1322   *
1323   * @param int $gid The group ID to fetch the display properties for
1324   * @return array Array of display properties for the group
1325   */
1326  function usergroup_displaygroup($gid)
1327  {
1328      global $cache, $groupscache, $displaygroupfields;
1329  
1330      if(!is_array($groupscache))
1331      {
1332          $groupscache = $cache->read("usergroups");
1333      }
1334  
1335      $displaygroup = array();
1336      $group = $groupscache[$gid];
1337  
1338      foreach($displaygroupfields as $field)
1339      {
1340          $displaygroup[$field] = $group[$field];
1341      }
1342  
1343      return $displaygroup;
1344  }
1345  
1346  /**
1347   * Build the forum permissions for a specific forum, user or group
1348   *
1349   * @param int $fid The forum ID to build permissions for (0 builds for all forums)
1350   * @param int $uid The user to build the permissions for (0 will select the uid automatically)
1351   * @param int $gid The group of the user to build permissions for (0 will fetch it)
1352   * @return array Forum permissions for the specific forum or forums
1353   */
1354  function forum_permissions($fid=0, $uid=0, $gid=0)
1355  {
1356      global $db, $cache, $groupscache, $forum_cache, $fpermcache, $mybb, $cached_forum_permissions_permissions, $cached_forum_permissions;
1357  
1358      if($uid == 0)
1359      {
1360          $uid = $mybb->user['uid'];
1361      }
1362  
1363      if(!$gid || $gid == 0) // If no group, we need to fetch it
1364      {
1365          if($uid != 0 && $uid != $mybb->user['uid'])
1366          {
1367              $user = get_user($uid);
1368  
1369              $gid = $user['usergroup'].",".$user['additionalgroups'];
1370              $groupperms = usergroup_permissions($gid);
1371          }
1372          else
1373          {
1374              $gid = $mybb->user['usergroup'];
1375  
1376              if(isset($mybb->user['additionalgroups']))
1377              {
1378                  $gid .= ",".$mybb->user['additionalgroups'];
1379              }
1380  
1381              $groupperms = $mybb->usergroup;
1382          }
1383      }
1384  
1385      if(!is_array($forum_cache))
1386      {
1387          $forum_cache = cache_forums();
1388  
1389          if(!$forum_cache)
1390          {
1391              return false;
1392          }
1393      }
1394  
1395      if(!is_array($fpermcache))
1396      {
1397          $fpermcache = $cache->read("forumpermissions");
1398      }
1399  
1400      if($fid) // Fetch the permissions for a single forum
1401      {
1402          if(empty($cached_forum_permissions_permissions[$gid][$fid]))
1403          {
1404              $cached_forum_permissions_permissions[$gid][$fid] = fetch_forum_permissions($fid, $gid, $groupperms);
1405          }
1406          return $cached_forum_permissions_permissions[$gid][$fid];
1407      }
1408      else
1409      {
1410          if(empty($cached_forum_permissions[$gid]))
1411          {
1412              foreach($forum_cache as $forum)
1413              {
1414                  $cached_forum_permissions[$gid][$forum['fid']] = fetch_forum_permissions($forum['fid'], $gid, $groupperms);
1415              }
1416          }
1417          return $cached_forum_permissions[$gid];
1418      }
1419  }
1420  
1421  /**
1422   * Fetches the permissions for a specific forum/group applying the inheritance scheme.
1423   * Called by forum_permissions()
1424   *
1425   * @param int $fid The forum ID
1426   * @param string $gid A comma separated list of usergroups
1427   * @param array $groupperms Group permissions
1428   * @return array Permissions for this forum
1429  */
1430  function fetch_forum_permissions($fid, $gid, $groupperms)
1431  {
1432      global $groupscache, $forum_cache, $fpermcache, $mybb, $fpermfields;
1433  
1434      $groups = explode(",", $gid);
1435  
1436      if(empty($fpermcache[$fid])) // This forum has no custom or inherited permissions so lets just return the group permissions
1437      {
1438          return $groupperms;
1439      }
1440  
1441      $current_permissions = array();
1442      $only_view_own_threads = 1;
1443      $only_reply_own_threads = 1;
1444  
1445      foreach($groups as $gid)
1446      {
1447          if(!empty($groupscache[$gid]))
1448          {
1449              $level_permissions = $fpermcache[$fid][$gid];
1450  
1451              // If our permissions arn't inherited we need to figure them out
1452              if(empty($fpermcache[$fid][$gid]))
1453              {
1454                  $parents = explode(',', $forum_cache[$fid]['parentlist']);
1455                  rsort($parents);
1456                  if(!empty($parents))
1457                  {
1458                      foreach($parents as $parent_id)
1459                      {
1460                          if(!empty($fpermcache[$parent_id][$gid]))
1461                          {
1462                              $level_permissions = $fpermcache[$parent_id][$gid];
1463                              break;
1464                          }
1465                      }
1466                  }
1467              }
1468  
1469              // If we STILL don't have forum permissions we use the usergroup itself
1470              if(empty($level_permissions))
1471              {
1472                  $level_permissions = $groupscache[$gid];
1473              }
1474  
1475              foreach($level_permissions as $permission => $access)
1476              {
1477                  if(empty($current_permissions[$permission]) || $access >= $current_permissions[$permission] || ($access == "yes" && $current_permissions[$permission] == "no"))
1478                  {
1479                      $current_permissions[$permission] = $access;
1480                  }
1481              }
1482  
1483              if($level_permissions["canview"] && empty($level_permissions["canonlyviewownthreads"]))
1484              {
1485                  $only_view_own_threads = 0;
1486              }
1487  
1488              if($level_permissions["canpostreplys"] && empty($level_permissions["canonlyreplyownthreads"]))
1489              {
1490                  $only_reply_own_threads = 0;
1491              }
1492          }
1493      }
1494  
1495      // Figure out if we can view more than our own threads
1496      if($only_view_own_threads == 0)
1497      {
1498          $current_permissions["canonlyviewownthreads"] = 0;
1499      }
1500  
1501      // Figure out if we can reply more than our own threads
1502      if($only_reply_own_threads == 0)
1503      {
1504          $current_permissions["canonlyreplyownthreads"] = 0;
1505      }
1506  
1507      if(count($current_permissions) == 0)
1508      {
1509          $current_permissions = $groupperms;
1510      }
1511      return $current_permissions;
1512  }
1513  
1514  /**
1515   * Check the password given on a certain forum for validity
1516   *
1517   * @param int $fid The forum ID
1518   * @param int $pid The Parent ID
1519   * @param bool $return
1520   * @return bool
1521   */
1522  function check_forum_password($fid, $pid=0, $return=false)
1523  {
1524      global $mybb, $header, $footer, $headerinclude, $theme, $templates, $lang, $forum_cache;
1525  
1526      $showform = true;
1527  
1528      if(!is_array($forum_cache))
1529      {
1530          $forum_cache = cache_forums();
1531          if(!$forum_cache)
1532          {
1533              return false;
1534          }
1535      }
1536  
1537      // Loop through each of parent forums to ensure we have a password for them too
1538      if(isset($forum_cache[$fid]['parentlist']))
1539      {
1540          $parents = explode(',', $forum_cache[$fid]['parentlist']);
1541          rsort($parents);
1542      }
1543      if(!empty($parents))
1544      {
1545          foreach($parents as $parent_id)
1546          {
1547              if($parent_id == $fid || $parent_id == $pid)
1548              {
1549                  continue;
1550              }
1551  
1552              if($forum_cache[$parent_id]['password'] != "")
1553              {
1554                  check_forum_password($parent_id, $fid);
1555              }
1556          }
1557      }
1558  
1559      if(!empty($forum_cache[$fid]['password']))
1560      {
1561          $password = $forum_cache[$fid]['password'];
1562          if(isset($mybb->input['pwverify']) && $pid == 0)
1563          {
1564              if($password === $mybb->get_input('pwverify'))
1565              {
1566                  my_setcookie("forumpass[$fid]", md5($mybb->user['uid'].$mybb->get_input('pwverify')), null, true);
1567                  $showform = false;
1568              }
1569              else
1570              {
1571                  eval("\$pwnote = \"".$templates->get("forumdisplay_password_wrongpass")."\";");
1572                  $showform = true;
1573              }
1574          }
1575          else
1576          {
1577              if(!$mybb->cookies['forumpass'][$fid] || ($mybb->cookies['forumpass'][$fid] && md5($mybb->user['uid'].$password) !== $mybb->cookies['forumpass'][$fid]))
1578              {
1579                  $showform = true;
1580              }
1581              else
1582              {
1583                  $showform = false;
1584              }
1585          }
1586      }
1587      else
1588      {
1589          $showform = false;
1590      }
1591  
1592      if($return)
1593      {
1594          return $showform;
1595      }
1596  
1597      if($showform)
1598      {
1599          if($pid)
1600          {
1601              header("Location: ".$mybb->settings['bburl']."/".get_forum_link($fid));
1602          }
1603          else
1604          {
1605              $_SERVER['REQUEST_URI'] = htmlspecialchars_uni($_SERVER['REQUEST_URI']);
1606              eval("\$pwform = \"".$templates->get("forumdisplay_password")."\";");
1607              output_page($pwform);
1608          }
1609          exit;
1610      }
1611  }
1612  
1613  /**
1614   * Return the permissions for a moderator in a specific forum
1615   *
1616   * @param int $fid The forum ID
1617   * @param int $uid The user ID to fetch permissions for (0 assumes current logged in user)
1618   * @param string $parentslist The parent list for the forum (if blank, will be fetched)
1619   * @return array Array of moderator permissions for the specific forum
1620   */
1621  function get_moderator_permissions($fid, $uid=0, $parentslist="")
1622  {
1623      global $mybb, $cache, $db;
1624      static $modpermscache;
1625  
1626      if($uid < 1)
1627      {
1628          $uid = $mybb->user['uid'];
1629      }
1630  
1631      if($uid == 0)
1632      {
1633          return false;
1634      }
1635  
1636      if(isset($modpermscache[$fid][$uid]))
1637      {
1638          return $modpermscache[$fid][$uid];
1639      }
1640  
1641      if(!$parentslist)
1642      {
1643          $parentslist = explode(',', get_parent_list($fid));
1644      }
1645  
1646      // Get user groups
1647      $perms = array();
1648      $user = get_user($uid);
1649  
1650      $groups = array($user['usergroup']);
1651  
1652      if(!empty($user['additionalgroups']))
1653      {
1654          $extra_groups = explode(",", $user['additionalgroups']);
1655  
1656          foreach($extra_groups as $extra_group)
1657          {
1658              $groups[] = $extra_group;
1659          }
1660      }
1661  
1662      $mod_cache = $cache->read("moderators");
1663  
1664      foreach($mod_cache as $forumid => $forum)
1665      {
1666          if(!is_array($forum) || !in_array($forumid, $parentslist))
1667          {
1668              // No perms or we're not after this forum
1669              continue;
1670          }
1671  
1672          // User settings override usergroup settings
1673          if(is_array($forum['users'][$uid]))
1674          {
1675              $perm = $forum['users'][$uid];
1676              foreach($perm as $action => $value)
1677              {
1678                  if(strpos($action, "can") === false)
1679                  {
1680                      continue;
1681                  }
1682  
1683                  // Figure out the user permissions
1684                  if($value == 0)
1685                  {
1686                      // The user doesn't have permission to set this action
1687                      $perms[$action] = 0;
1688                  }
1689                  else
1690                  {
1691                      $perms[$action] = max($perm[$action], $perms[$action]);
1692                  }
1693              }
1694          }
1695  
1696          foreach($groups as $group)
1697          {
1698              if(!is_array($forum['usergroups'][$group]))
1699              {
1700                  // There are no permissions set for this group
1701                  continue;
1702              }
1703  
1704              $perm = $forum['usergroups'][$group];
1705              foreach($perm as $action => $value)
1706              {
1707                  if(strpos($action, "can") === false)
1708                  {
1709                      continue;
1710                  }
1711  
1712                  $perms[$action] = max($perm[$action], $perms[$action]);
1713              }
1714          }
1715      }
1716  
1717      $modpermscache[$fid][$uid] = $perms;
1718  
1719      return $perms;
1720  }
1721  
1722  /**
1723   * Checks if a moderator has permissions to perform an action in a specific forum
1724   *
1725   * @param int $fid The forum ID (0 assumes global)
1726   * @param string $action The action tyring to be performed. (blank assumes any action at all)
1727   * @param int $uid The user ID (0 assumes current user)
1728   * @return bool Returns true if the user has permission, false if they do not
1729   */
1730  function is_moderator($fid=0, $action="", $uid=0)
1731  {
1732      global $mybb, $cache;
1733  
1734      if($uid == 0)
1735      {
1736          $uid = $mybb->user['uid'];
1737      }
1738  
1739      if($uid == 0)
1740      {
1741          return false;
1742      }
1743  
1744      $user_perms = user_permissions($uid);
1745      if($user_perms['issupermod'] == 1)
1746      {
1747          if($fid)
1748          {
1749              $forumpermissions = forum_permissions($fid);
1750              if($forumpermissions['canview'] && $forumpermissions['canviewthreads'] && !$forumpermissions['canonlyviewownthreads'])
1751              {
1752                  return true;
1753              }
1754              return false;
1755          }
1756          return true;
1757      }
1758      else
1759      {
1760          if(!$fid)
1761          {
1762              $modcache = $cache->read('moderators');
1763              if(!empty($modcache))
1764              {
1765                  foreach($modcache as $modusers)
1766                  {
1767                      if(isset($modusers['users'][$uid]) && $modusers['users'][$uid]['mid'] && (!$action || !empty($modusers['users'][$uid][$action])))
1768                      {
1769                          return true;
1770                      }
1771  
1772                      $groups = explode(',', $user_perms['all_usergroups']);
1773  
1774                      foreach($groups as $group)
1775                      {
1776                          if(trim($group) != '' && isset($modusers['usergroups'][$group]) && (!$action || !empty($modusers['usergroups'][$group][$action])))
1777                          {
1778                              return true;
1779                          }
1780                      }
1781                  }
1782              }
1783              return false;
1784          }
1785          else
1786          {
1787              $modperms = get_moderator_permissions($fid, $uid);
1788  
1789              if(!$action && $modperms)
1790              {
1791                  return true;
1792              }
1793              else
1794              {
1795                  if(isset($modperms[$action]) && $modperms[$action] == 1)
1796                  {
1797                      return true;
1798                  }
1799                  else
1800                  {
1801                      return false;
1802                  }
1803              }
1804          }
1805      }
1806  }
1807  
1808  /**
1809   * Generate a list of the posticons.
1810   *
1811   * @return string The template of posticons.
1812   */
1813  function get_post_icons()
1814  {
1815      global $mybb, $cache, $icon, $theme, $templates, $lang;
1816  
1817      if(isset($mybb->input['icon']))
1818      {
1819          $icon = $mybb->get_input('icon');
1820      }
1821  
1822      $iconlist = '';
1823      $no_icons_checked = " checked=\"checked\"";
1824      // read post icons from cache, and sort them accordingly
1825      $posticons_cache = (array)$cache->read("posticons");
1826      $posticons = array();
1827      foreach($posticons_cache as $posticon)
1828      {
1829          $posticons[$posticon['name']] = $posticon;
1830      }
1831      krsort($posticons);
1832  
1833      foreach($posticons as $dbicon)
1834      {
1835          $dbicon['path'] = str_replace("{theme}", $theme['imgdir'], $dbicon['path']);
1836          $dbicon['path'] = htmlspecialchars_uni($mybb->get_asset_url($dbicon['path']));
1837          $dbicon['name'] = htmlspecialchars_uni($dbicon['name']);
1838  
1839          if($icon == $dbicon['iid'])
1840          {
1841              $checked = " checked=\"checked\"";
1842              $no_icons_checked = '';
1843          }
1844          else
1845          {
1846              $checked = '';
1847          }
1848  
1849          eval("\$iconlist .= \"".$templates->get("posticons_icon")."\";");
1850      }
1851  
1852      if(!empty($iconlist))
1853      {
1854          eval("\$posticons = \"".$templates->get("posticons")."\";");
1855      }
1856      else
1857      {
1858          $posticons = '';
1859      }
1860  
1861      return $posticons;
1862  }
1863  
1864  /**
1865   * MyBB setcookie() wrapper.
1866   *
1867   * @param string $name The cookie identifier.
1868   * @param string $value The cookie value.
1869   * @param int|string $expires The timestamp of the expiry date.
1870   * @param boolean $httponly True if setting a HttpOnly cookie (supported by the majority of web browsers)
1871   * @param string $samesite The samesite attribute to prevent CSRF.
1872   */
1873  function my_setcookie($name, $value="", $expires="", $httponly=false, $samesite="")
1874  {
1875      global $mybb;
1876  
1877      if(!$mybb->settings['cookiepath'])
1878      {
1879          $mybb->settings['cookiepath'] = "/";
1880      }
1881  
1882      if($expires == -1)
1883      {
1884          $expires = 0;
1885      }
1886      elseif($expires == "" || $expires == null)
1887      {
1888          $expires = TIME_NOW + (60*60*24*365); // Make the cookie expire in a years time
1889      }
1890      else
1891      {
1892          $expires = TIME_NOW + (int)$expires;
1893      }
1894  
1895      $mybb->settings['cookiepath'] = str_replace(array("\n","\r"), "", $mybb->settings['cookiepath']);
1896      $mybb->settings['cookiedomain'] = str_replace(array("\n","\r"), "", $mybb->settings['cookiedomain']);
1897      $mybb->settings['cookieprefix'] = str_replace(array("\n","\r", " "), "", $mybb->settings['cookieprefix']);
1898  
1899      // 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
1900      $cookie = "Set-Cookie: {$mybb->settings['cookieprefix']}{$name}=".urlencode($value);
1901  
1902      if($expires > 0)
1903      {
1904          $cookie .= "; expires=".@gmdate('D, d-M-Y H:i:s \\G\\M\\T', $expires);
1905      }
1906  
1907      if(!empty($mybb->settings['cookiepath']))
1908      {
1909          $cookie .= "; path={$mybb->settings['cookiepath']}";
1910      }
1911  
1912      if(!empty($mybb->settings['cookiedomain']))
1913      {
1914          $cookie .= "; domain={$mybb->settings['cookiedomain']}";
1915      }
1916  
1917      if($httponly == true)
1918      {
1919          $cookie .= "; HttpOnly";
1920      }
1921  
1922      if($samesite != "" && $mybb->settings['cookiesamesiteflag'])
1923      {
1924          $samesite = strtolower($samesite);
1925  
1926          if($samesite == "lax" || $samesite == "strict")
1927          {
1928              $cookie .= "; SameSite=".$samesite;
1929          }
1930      }
1931  
1932      if($mybb->settings['cookiesecureflag'])
1933      {
1934          $cookie .= "; Secure";
1935      }
1936  
1937      $mybb->cookies[$name] = $value;
1938  
1939      header($cookie, false);
1940  }
1941  
1942  /**
1943   * Unset a cookie set by MyBB.
1944   *
1945   * @param string $name The cookie identifier.
1946   */
1947  function my_unsetcookie($name)
1948  {
1949      global $mybb;
1950  
1951      $expires = -3600;
1952      my_setcookie($name, "", $expires);
1953  
1954      unset($mybb->cookies[$name]);
1955  }
1956  
1957  /**
1958   * Get the contents from a serialised cookie array.
1959   *
1960   * @param string $name The cookie identifier.
1961   * @param int $id The cookie content id.
1962   * @return array|boolean The cookie id's content array or false when non-existent.
1963   */
1964  function my_get_array_cookie($name, $id)
1965  {
1966      global $mybb;
1967  
1968      if(!isset($mybb->cookies['mybb'][$name]))
1969      {
1970          return false;
1971      }
1972  
1973      $cookie = my_unserialize($mybb->cookies['mybb'][$name]);
1974  
1975      if(is_array($cookie) && isset($cookie[$id]))
1976      {
1977          return $cookie[$id];
1978      }
1979      else
1980      {
1981          return 0;
1982      }
1983  }
1984  
1985  /**
1986   * Set a serialised cookie array.
1987   *
1988   * @param string $name The cookie identifier.
1989   * @param int $id The cookie content id.
1990   * @param string $value The value to set the cookie to.
1991   * @param int|string $expires The timestamp of the expiry date.
1992   */
1993  function my_set_array_cookie($name, $id, $value, $expires="")
1994  {
1995      global $mybb;
1996  
1997      $cookie = $mybb->cookies['mybb'];
1998      if(isset($cookie[$name]))
1999      {
2000          $newcookie = my_unserialize($cookie[$name]);
2001      }
2002      else
2003      {
2004          $newcookie = array();
2005      }
2006  
2007      $newcookie[$id] = $value;
2008      $newcookie = my_serialize($newcookie);
2009      my_setcookie("mybb[$name]", addslashes($newcookie), $expires);
2010  
2011      // Make sure our current viarables are up-to-date as well
2012      $mybb->cookies['mybb'][$name] = $newcookie;
2013  }
2014  
2015  /*
2016   * Arbitrary limits for _safe_unserialize()
2017   */
2018  define('MAX_SERIALIZED_INPUT_LENGTH', 10240);
2019  define('MAX_SERIALIZED_ARRAY_LENGTH', 256);
2020  define('MAX_SERIALIZED_ARRAY_DEPTH', 5);
2021  
2022  /**
2023   * Credits go to https://github.com/piwik
2024   * Safe unserialize() replacement
2025   * - accepts a strict subset of PHP's native my_serialized representation
2026   * - does not unserialize objects
2027   *
2028   * @param string $str
2029   * @return mixed
2030   * @throw Exception if $str is malformed or contains unsupported types (e.g., resources, objects)
2031   */
2032  function _safe_unserialize($str)
2033  {
2034      if(strlen($str) > MAX_SERIALIZED_INPUT_LENGTH)
2035      {
2036          // input exceeds MAX_SERIALIZED_INPUT_LENGTH
2037          return false;
2038      }
2039  
2040      if(empty($str) || !is_string($str))
2041      {
2042          return false;
2043      }
2044  
2045      $stack = $list = $expected = array();
2046  
2047      /*
2048       * states:
2049       *   0 - initial state, expecting a single value or array
2050       *   1 - terminal state
2051       *   2 - in array, expecting end of array or a key
2052       *   3 - in array, expecting value or another array
2053       */
2054      $state = 0;
2055      while($state != 1)
2056      {
2057          $type = isset($str[0]) ? $str[0] : '';
2058  
2059          if($type == '}')
2060          {
2061              $str = substr($str, 1);
2062          }
2063          else if($type == 'N' && $str[1] == ';')
2064          {
2065              $value = null;
2066              $str = substr($str, 2);
2067          }
2068          else if($type == 'b' && preg_match('/^b:([01]);/', $str, $matches))
2069          {
2070              $value = $matches[1] == '1' ? true : false;
2071              $str = substr($str, 4);
2072          }
2073          else if($type == 'i' && preg_match('/^i:(-?[0-9]+);(.*)/s', $str, $matches))
2074          {
2075              $value = (int)$matches[1];
2076              $str = $matches[2];
2077          }
2078          else if($type == 'd' && preg_match('/^d:(-?[0-9]+\.?[0-9]*(E[+-][0-9]+)?);(.*)/s', $str, $matches))
2079          {
2080              $value = (float)$matches[1];
2081              $str = $matches[3];
2082          }
2083          else if($type == 's' && preg_match('/^s:([0-9]+):"(.*)/s', $str, $matches) && substr($matches[2], (int)$matches[1], 2) == '";')
2084          {
2085              $value = substr($matches[2], 0, (int)$matches[1]);
2086              $str = substr($matches[2], (int)$matches[1] + 2);
2087          }
2088          else if($type == 'a' && preg_match('/^a:([0-9]+):{(.*)/s', $str, $matches) && $matches[1] < MAX_SERIALIZED_ARRAY_LENGTH)
2089          {
2090              $expectedLength = (int)$matches[1];
2091              $str = $matches[2];
2092          }
2093          else
2094          {
2095              // object or unknown/malformed type
2096              return false;
2097          }
2098  
2099          switch($state)
2100          {
2101              case 3: // in array, expecting value or another array
2102                  if($type == 'a')
2103                  {
2104                      if(count($stack) >= MAX_SERIALIZED_ARRAY_DEPTH)
2105                      {
2106                          // array nesting exceeds MAX_SERIALIZED_ARRAY_DEPTH
2107                          return false;
2108                      }
2109  
2110                      $stack[] = &$list;
2111                      $list[$key] = array();
2112                      $list = &$list[$key];
2113                      $expected[] = $expectedLength;
2114                      $state = 2;
2115                      break;
2116                  }
2117                  if($type != '}')
2118                  {
2119                      $list[$key] = $value;
2120                      $state = 2;
2121                      break;
2122                  }
2123  
2124                  // missing array value
2125                  return false;
2126  
2127              case 2: // in array, expecting end of array or a key
2128                  if($type == '}')
2129                  {
2130                      if(count($list) < end($expected))
2131                      {
2132                          // array size less than expected
2133                          return false;
2134                      }
2135  
2136                      unset($list);
2137                      $list = &$stack[count($stack)-1];
2138                      array_pop($stack);
2139  
2140                      // go to terminal state if we're at the end of the root array
2141                      array_pop($expected);
2142                      if(count($expected) == 0) {
2143                          $state = 1;
2144                      }
2145                      break;
2146                  }
2147                  if($type == 'i' || $type == 's')
2148                  {
2149                      if(count($list) >= MAX_SERIALIZED_ARRAY_LENGTH)
2150                      {
2151                          // array size exceeds MAX_SERIALIZED_ARRAY_LENGTH
2152                          return false;
2153                      }
2154                      if(count($list) >= end($expected))
2155                      {
2156                          // array size exceeds expected length
2157                          return false;
2158                      }
2159  
2160                      $key = $value;
2161                      $state = 3;
2162                      break;
2163                  }
2164  
2165                  // illegal array index type
2166                  return false;
2167  
2168              case 0: // expecting array or value
2169                  if($type == 'a')
2170                  {
2171                      if(count($stack) >= MAX_SERIALIZED_ARRAY_DEPTH)
2172                      {
2173                          // array nesting exceeds MAX_SERIALIZED_ARRAY_DEPTH
2174                          return false;
2175                      }
2176  
2177                      $data = array();
2178                      $list = &$data;
2179                      $expected[] = $expectedLength;
2180                      $state = 2;
2181                      break;
2182                  }
2183                  if($type != '}')
2184                  {
2185                      $data = $value;
2186                      $state = 1;
2187                      break;
2188                  }
2189  
2190                  // not in array
2191                  return false;
2192          }
2193      }
2194  
2195      if(!empty($str))
2196      {
2197          // trailing data in input
2198          return false;
2199      }
2200      return $data;
2201  }
2202  
2203  /**
2204   * Credits go to https://github.com/piwik
2205   * Wrapper for _safe_unserialize() that handles exceptions and multibyte encoding issue
2206   *
2207   * @param string $str
2208   * @return mixed
2209   */
2210  function my_unserialize($str)
2211  {
2212      // Ensure we use the byte count for strings even when strlen() is overloaded by mb_strlen()
2213      if(function_exists('mb_internal_encoding') && (((int)ini_get('mbstring.func_overload')) & 2))
2214      {
2215          $mbIntEnc = mb_internal_encoding();
2216          mb_internal_encoding('ASCII');
2217      }
2218  
2219      $out = _safe_unserialize($str);
2220  
2221      if(isset($mbIntEnc))
2222      {
2223          mb_internal_encoding($mbIntEnc);
2224      }
2225  
2226      return $out;
2227  }
2228  
2229  /**
2230   * Credits go to https://github.com/piwik
2231   * Safe serialize() replacement
2232   * - output a strict subset of PHP's native serialized representation
2233   * - does not my_serialize objects
2234   *
2235   * @param mixed $value
2236   * @return string
2237   * @throw Exception if $value is malformed or contains unsupported types (e.g., resources, objects)
2238   */
2239  function _safe_serialize( $value )
2240  {
2241      if(is_null($value))
2242      {
2243          return 'N;';
2244      }
2245  
2246      if(is_bool($value))
2247      {
2248          return 'b:'.(int)$value.';';
2249      }
2250  
2251      if(is_int($value))
2252      {
2253          return 'i:'.$value.';';
2254      }
2255  
2256      if(is_float($value))
2257      {
2258          return 'd:'.str_replace(',', '.', $value).';';
2259      }
2260  
2261      if(is_string($value))
2262      {
2263          return 's:'.strlen($value).':"'.$value.'";';
2264      }
2265  
2266      if(is_array($value))
2267      {
2268          $out = '';
2269          foreach($value as $k => $v)
2270          {
2271              $out .= _safe_serialize($k) . _safe_serialize($v);
2272          }
2273  
2274          return 'a:'.count($value).':{'.$out.'}';
2275      }
2276  
2277      // safe_serialize cannot my_serialize resources or objects
2278      return false;
2279  }
2280  
2281  /**
2282   * Credits go to https://github.com/piwik
2283   * Wrapper for _safe_serialize() that handles exceptions and multibyte encoding issue
2284   *
2285   * @param mixed $value
2286   * @return string
2287  */
2288  function my_serialize($value)
2289  {
2290      // ensure we use the byte count for strings even when strlen() is overloaded by mb_strlen()
2291      if(function_exists('mb_internal_encoding') && (((int)ini_get('mbstring.func_overload')) & 2))
2292      {
2293          $mbIntEnc = mb_internal_encoding();
2294          mb_internal_encoding('ASCII');
2295      }
2296  
2297      $out = _safe_serialize($value);
2298      if(isset($mbIntEnc))
2299      {
2300          mb_internal_encoding($mbIntEnc);
2301      }
2302  
2303      return $out;
2304  }
2305  
2306  /**
2307   * Returns the serverload of the system.
2308   *
2309   * @return int The serverload of the system.
2310   */
2311  function get_server_load()
2312  {
2313      global $mybb, $lang;
2314  
2315      $serverload = array();
2316  
2317      // DIRECTORY_SEPARATOR checks if running windows
2318      if(DIRECTORY_SEPARATOR != '\\')
2319      {
2320          if(function_exists("sys_getloadavg"))
2321          {
2322              // sys_getloadavg() will return an array with [0] being load within the last minute.
2323              $serverload = sys_getloadavg();
2324              $serverload[0] = round($serverload[0], 4);
2325          }
2326          else if(@file_exists("/proc/loadavg") && $load = @file_get_contents("/proc/loadavg"))
2327          {
2328              $serverload = explode(" ", $load);
2329              $serverload[0] = round($serverload[0], 4);
2330          }
2331          if(!is_numeric($serverload[0]))
2332          {
2333              if($mybb->safemode)
2334              {
2335                  return $lang->unknown;
2336              }
2337  
2338              // Suhosin likes to throw a warning if exec is disabled then die - weird
2339              if($func_blacklist = @ini_get('suhosin.executor.func.blacklist'))
2340              {
2341                  if(strpos(",".$func_blacklist.",", 'exec') !== false)
2342                  {
2343                      return $lang->unknown;
2344                  }
2345              }
2346              // PHP disabled functions?
2347              if($func_blacklist = @ini_get('disable_functions'))
2348              {
2349                  if(strpos(",".$func_blacklist.",", 'exec') !== false)
2350                  {
2351                      return $lang->unknown;
2352                  }
2353              }
2354  
2355              $load = @exec("uptime");
2356              $load = explode("load average: ", $load);
2357              $serverload = explode(",", $load[1]);
2358              if(!is_array($serverload))
2359              {
2360                  return $lang->unknown;
2361              }
2362          }
2363      }
2364      else
2365      {
2366          return $lang->unknown;
2367      }
2368  
2369      $returnload = trim($serverload[0]);
2370  
2371      return $returnload;
2372  }
2373  
2374  /**
2375   * Returns the amount of memory allocated to the script.
2376   *
2377   * @return int The amount of memory allocated to the script.
2378   */
2379  function get_memory_usage()
2380  {
2381      if(function_exists('memory_get_peak_usage'))
2382      {
2383          return memory_get_peak_usage(true);
2384      }
2385      elseif(function_exists('memory_get_usage'))
2386      {
2387          return memory_get_usage(true);
2388      }
2389      return false;
2390  }
2391  
2392  /**
2393   * Updates the forum statistics with specific values (or addition/subtraction of the previous value)
2394   *
2395   * @param array $changes Array of items being updated (numthreads,numposts,numusers,numunapprovedthreads,numunapprovedposts,numdeletedposts,numdeletedthreads)
2396   * @param boolean $force Force stats update?
2397   */
2398  function update_stats($changes=array(), $force=false)
2399  {
2400      global $cache, $db;
2401      static $stats_changes;
2402  
2403      if(empty($stats_changes))
2404      {
2405          // Update stats after all changes are done
2406          add_shutdown('update_stats', array(array(), true));
2407      }
2408  
2409      if(empty($stats_changes) || $stats_changes['inserted'])
2410      {
2411          $stats_changes = array(
2412              'numthreads' => '+0',
2413              'numposts' => '+0',
2414              'numusers' => '+0',
2415              'numunapprovedthreads' => '+0',
2416              'numunapprovedposts' => '+0',
2417              'numdeletedposts' => '+0',
2418              'numdeletedthreads' => '+0',
2419              'inserted' => false // Reset after changes are inserted into cache
2420          );
2421          $stats = $stats_changes;
2422      }
2423  
2424      if($force) // Force writing to cache?
2425      {
2426          if(!empty($changes))
2427          {
2428              // Calculate before writing to cache
2429              update_stats($changes);
2430          }
2431          $stats = $cache->read("stats");
2432          $changes = $stats_changes;
2433      }
2434      else
2435      {
2436          $stats = $stats_changes;
2437      }
2438  
2439      $new_stats = array();
2440      $counters = array('numthreads', 'numunapprovedthreads', 'numposts', 'numunapprovedposts', 'numusers', 'numdeletedposts', 'numdeletedthreads');
2441      foreach($counters as $counter)
2442      {
2443          if(array_key_exists($counter, $changes))
2444          {
2445              if(substr($changes[$counter], 0, 2) == "+-")
2446              {
2447                  $changes[$counter] = substr($changes[$counter], 1);
2448              }
2449              // Adding or subtracting from previous value?
2450              if(substr($changes[$counter], 0, 1) == "+" || substr($changes[$counter], 0, 1) == "-")
2451              {
2452                  if((int)$changes[$counter] != 0)
2453                  {
2454                      $new_stats[$counter] = $stats[$counter] + $changes[$counter];
2455                      if(!$force && (substr($stats[$counter], 0, 1) == "+" || substr($stats[$counter], 0, 1) == "-"))
2456                      {
2457                          // We had relative values? Then it is still relative
2458                          if($new_stats[$counter] >= 0)
2459                          {
2460                              $new_stats[$counter] = "+{$new_stats[$counter]}";
2461                          }
2462                      }
2463                      // Less than 0? That's bad
2464                      elseif($new_stats[$counter] < 0)
2465                      {
2466                          $new_stats[$counter] = 0;
2467                      }
2468                  }
2469              }
2470              else
2471              {
2472                  $new_stats[$counter] = $changes[$counter];
2473                  // Less than 0? That's bad
2474                  if($new_stats[$counter] < 0)
2475                  {
2476                      $new_stats[$counter] = 0;
2477                  }
2478              }
2479          }
2480      }
2481  
2482      if(!$force)
2483      {
2484          $stats_changes = array_merge($stats, $new_stats); // Overwrite changed values
2485          return;
2486      }
2487  
2488      // Fetch latest user if the user count is changing
2489      if(array_key_exists('numusers', $changes))
2490      {
2491          $query = $db->simple_select("users", "uid, username", "", array('order_by' => 'regdate', 'order_dir' => 'DESC', 'limit' => 1));
2492          $lastmember = $db->fetch_array($query);
2493          $new_stats['lastuid'] = $lastmember['uid'];
2494          $new_stats['lastusername'] = $lastmember['username'] = htmlspecialchars_uni($lastmember['username']);
2495      }
2496  
2497      if(!empty($new_stats))
2498      {
2499          if(is_array($stats))
2500          {
2501              $stats = array_merge($stats, $new_stats); // Overwrite changed values
2502          }
2503          else
2504          {
2505              $stats = $new_stats;
2506          }
2507      }
2508  
2509      // Update stats row for today in the database
2510      $todays_stats = array(
2511          "dateline" => mktime(0, 0, 0, date("m"), date("j"), date("Y")),
2512          "numusers" => (int)$stats['numusers'],
2513          "numthreads" => (int)$stats['numthreads'],
2514          "numposts" => (int)$stats['numposts']
2515      );
2516      $db->replace_query("stats", $todays_stats, "dateline");
2517  
2518      $cache->update("stats", $stats, "dateline");
2519      $stats_changes['inserted'] = true;
2520  }
2521  
2522  /**
2523   * Updates the forum counters with a specific value (or addition/subtraction of the previous value)
2524   *
2525   * @param int $fid The forum ID
2526   * @param array $changes Array of items being updated (threads, posts, unapprovedthreads, unapprovedposts, deletedposts, deletedthreads) and their value (ex, 1, +1, -1)
2527   */
2528  function update_forum_counters($fid, $changes=array())
2529  {
2530      global $db;
2531  
2532      $update_query = array();
2533  
2534      $counters = array('threads', 'unapprovedthreads', 'posts', 'unapprovedposts', 'deletedposts', 'deletedthreads');
2535  
2536      // Fetch above counters for this forum
2537      $query = $db->simple_select("forums", implode(",", $counters), "fid='{$fid}'");
2538      $forum = $db->fetch_array($query);
2539  
2540      foreach($counters as $counter)
2541      {
2542          if(array_key_exists($counter, $changes))
2543          {
2544              if(substr($changes[$counter], 0, 2) == "+-")
2545              {
2546                  $changes[$counter] = substr($changes[$counter], 1);
2547              }
2548              // Adding or subtracting from previous value?
2549              if(substr($changes[$counter], 0, 1) == "+" || substr($changes[$counter], 0, 1) == "-")
2550              {
2551                  if((int)$changes[$counter] != 0)
2552                  {
2553                      $update_query[$counter] = $forum[$counter] + $changes[$counter];
2554                  }
2555              }
2556              else
2557              {
2558                  $update_query[$counter] = $changes[$counter];
2559              }
2560  
2561              // Less than 0? That's bad
2562              if(isset($update_query[$counter]) && $update_query[$counter] < 0)
2563              {
2564                  $update_query[$counter] = 0;
2565              }
2566          }
2567      }
2568  
2569      // Only update if we're actually doing something
2570      if(count($update_query) > 0)
2571      {
2572          $db->update_query("forums", $update_query, "fid='".(int)$fid."'");
2573      }
2574  
2575      // Guess we should update the statistics too?
2576      $new_stats = array();
2577      if(array_key_exists('threads', $update_query))
2578      {
2579          $threads_diff = $update_query['threads'] - $forum['threads'];
2580          if($threads_diff > -1)
2581          {
2582              $new_stats['numthreads'] = "+{$threads_diff}";
2583          }
2584          else
2585          {
2586              $new_stats['numthreads'] = "{$threads_diff}";
2587          }
2588      }
2589  
2590      if(array_key_exists('unapprovedthreads', $update_query))
2591      {
2592          $unapprovedthreads_diff = $update_query['unapprovedthreads'] - $forum['unapprovedthreads'];
2593          if($unapprovedthreads_diff > -1)
2594          {
2595              $new_stats['numunapprovedthreads'] = "+{$unapprovedthreads_diff}";
2596          }
2597          else
2598          {
2599              $new_stats['numunapprovedthreads'] = "{$unapprovedthreads_diff}";
2600          }
2601      }
2602  
2603      if(array_key_exists('posts', $update_query))
2604      {
2605          $posts_diff = $update_query['posts'] - $forum['posts'];
2606          if($posts_diff > -1)
2607          {
2608              $new_stats['numposts'] = "+{$posts_diff}";
2609          }
2610          else
2611          {
2612              $new_stats['numposts'] = "{$posts_diff}";
2613          }
2614      }
2615  
2616      if(array_key_exists('unapprovedposts', $update_query))
2617      {
2618          $unapprovedposts_diff = $update_query['unapprovedposts'] - $forum['unapprovedposts'];
2619          if($unapprovedposts_diff > -1)
2620          {
2621              $new_stats['numunapprovedposts'] = "+{$unapprovedposts_diff}";
2622          }
2623          else
2624          {
2625              $new_stats['numunapprovedposts'] = "{$unapprovedposts_diff}";
2626          }
2627      }
2628  
2629      if(array_key_exists('deletedposts', $update_query))
2630      {
2631          $deletedposts_diff = $update_query['deletedposts'] - $forum['deletedposts'];
2632          if($deletedposts_diff > -1)
2633          {
2634              $new_stats['numdeletedposts'] = "+{$deletedposts_diff}";
2635          }
2636          else
2637          {
2638              $new_stats['numdeletedposts'] = "{$deletedposts_diff}";
2639          }
2640      }
2641  
2642      if(array_key_exists('deletedthreads', $update_query))
2643      {
2644          $deletedthreads_diff = $update_query['deletedthreads'] - $forum['deletedthreads'];
2645          if($deletedthreads_diff > -1)
2646          {
2647              $new_stats['numdeletedthreads'] = "+{$deletedthreads_diff}";
2648          }
2649          else
2650          {
2651              $new_stats['numdeletedthreads'] = "{$deletedthreads_diff}";
2652          }
2653      }
2654  
2655      if(!empty($new_stats))
2656      {
2657          update_stats($new_stats);
2658      }
2659  }
2660  
2661  /**
2662   * Update the last post information for a specific forum
2663   *
2664   * @param int $fid The forum ID
2665   */
2666  function update_forum_lastpost($fid)
2667  {
2668      global $db;
2669  
2670      // Fetch the last post for this forum
2671      $query = $db->query("
2672          SELECT tid, lastpost, lastposter, lastposteruid, subject
2673          FROM ".TABLE_PREFIX."threads
2674          WHERE fid='{$fid}' AND visible='1' AND closed NOT LIKE 'moved|%'
2675          ORDER BY lastpost DESC
2676          LIMIT 0, 1
2677      ");
2678      $lastpost = $db->fetch_array($query);
2679  
2680      $updated_forum = array(
2681          "lastpost" => (int)$lastpost['lastpost'],
2682          "lastposter" => $db->escape_string($lastpost['lastposter']),
2683          "lastposteruid" => (int)$lastpost['lastposteruid'],
2684          "lastposttid" => (int)$lastpost['tid'],
2685          "lastpostsubject" => $db->escape_string($lastpost['subject'])
2686      );
2687  
2688      $db->update_query("forums", $updated_forum, "fid='{$fid}'");
2689  }
2690  
2691  /**
2692   * Updates the thread counters with a specific value (or addition/subtraction of the previous value)
2693   *
2694   * @param int $tid The thread ID
2695   * @param array $changes Array of items being updated (replies, unapprovedposts, deletedposts, attachmentcount) and their value (ex, 1, +1, -1)
2696   */
2697  function update_thread_counters($tid, $changes=array())
2698  {
2699      global $db;
2700  
2701      $update_query = array();
2702      $tid = (int)$tid;
2703  
2704      $counters = array('replies', 'unapprovedposts', 'attachmentcount', 'deletedposts', 'attachmentcount');
2705  
2706      // Fetch above counters for this thread
2707      $query = $db->simple_select("threads", implode(",", $counters), "tid='{$tid}'");
2708      $thread = $db->fetch_array($query);
2709  
2710      foreach($counters as $counter)
2711      {
2712          if(array_key_exists($counter, $changes))
2713          {
2714              if(substr($changes[$counter], 0, 2) == "+-")
2715              {
2716                  $changes[$counter] = substr($changes[$counter], 1);
2717              }
2718              // Adding or subtracting from previous value?
2719              if(substr($changes[$counter], 0, 1) == "+" || substr($changes[$counter], 0, 1) == "-")
2720              {
2721                  if((int)$changes[$counter] != 0)
2722                  {
2723                      $update_query[$counter] = $thread[$counter] + $changes[$counter];
2724                  }
2725              }
2726              else
2727              {
2728                  $update_query[$counter] = $changes[$counter];
2729              }
2730  
2731              // Less than 0? That's bad
2732              if(isset($update_query[$counter]) && $update_query[$counter] < 0)
2733              {
2734                  $update_query[$counter] = 0;
2735              }
2736          }
2737      }
2738  
2739      $db->free_result($query);
2740  
2741      // Only update if we're actually doing something
2742      if(count($update_query) > 0)
2743      {
2744          $db->update_query("threads", $update_query, "tid='{$tid}'");
2745      }
2746  }
2747  
2748  /**
2749   * Update the first post and lastpost data for a specific thread
2750   *
2751   * @param int $tid The thread ID
2752   */
2753  function update_thread_data($tid)
2754  {
2755      global $db;
2756  
2757      $thread = get_thread($tid);
2758  
2759      // If this is a moved thread marker, don't update it - we need it to stay as it is
2760      if(strpos($thread['closed'], 'moved|') !== false)
2761      {
2762          return;
2763      }
2764  
2765      $query = $db->query("
2766          SELECT u.uid, u.username, p.username AS postusername, p.dateline
2767          FROM ".TABLE_PREFIX."posts p
2768          LEFT JOIN ".TABLE_PREFIX."users u ON (u.uid=p.uid)
2769          WHERE p.tid='$tid' AND p.visible='1'
2770          ORDER BY p.dateline DESC
2771          LIMIT 1"
2772      );
2773      $lastpost = $db->fetch_array($query);
2774  
2775      $db->free_result($query);
2776  
2777      $query = $db->query("
2778          SELECT u.uid, u.username, p.pid, p.username AS postusername, p.dateline
2779          FROM ".TABLE_PREFIX."posts p
2780          LEFT JOIN ".TABLE_PREFIX."users u ON (u.uid=p.uid)
2781          WHERE p.tid='$tid'
2782          ORDER BY p.dateline ASC
2783          LIMIT 1
2784      ");
2785      $firstpost = $db->fetch_array($query);
2786  
2787      $db->free_result($query);
2788  
2789      if(empty($firstpost['username']))
2790      {
2791          $firstpost['username'] = $firstpost['postusername'];
2792      }
2793  
2794      if(empty($lastpost['username']))
2795      {
2796          $lastpost['username'] = $lastpost['postusername'];
2797      }
2798  
2799      if(empty($lastpost['dateline']))
2800      {
2801          $lastpost['username'] = $firstpost['username'];
2802          $lastpost['uid'] = $firstpost['uid'];
2803          $lastpost['dateline'] = $firstpost['dateline'];
2804      }
2805  
2806      $lastpost['username'] = $db->escape_string($lastpost['username']);
2807      $firstpost['username'] = $db->escape_string($firstpost['username']);
2808  
2809      $update_array = array(
2810          'firstpost' => (int)$firstpost['pid'],
2811          'username' => $firstpost['username'],
2812          'uid' => (int)$firstpost['uid'],
2813          'dateline' => (int)$firstpost['dateline'],
2814          'lastpost' => (int)$lastpost['dateline'],
2815          'lastposter' => $lastpost['username'],
2816          'lastposteruid' => (int)$lastpost['uid'],
2817      );
2818      $db->update_query("threads", $update_array, "tid='{$tid}'");
2819  }
2820  
2821  /**
2822   * Updates the user counters with a specific value (or addition/subtraction of the previous value)
2823   *
2824   * @param int $uid The user ID
2825   * @param array $changes Array of items being updated (postnum, threadnum) and their value (ex, 1, +1, -1)
2826   */
2827  function update_user_counters($uid, $changes=array())
2828  {
2829      global $db;
2830  
2831      $update_query = array();
2832  
2833      $counters = array('postnum', 'threadnum');
2834      $uid = (int)$uid;
2835  
2836      // Fetch above counters for this user
2837      $query = $db->simple_select("users", implode(",", $counters), "uid='{$uid}'");
2838      $user = $db->fetch_array($query);
2839  
2840      foreach($counters as $counter)
2841      {
2842          if(array_key_exists($counter, $changes))
2843          {
2844              if(substr($changes[$counter], 0, 2) == "+-")
2845              {
2846                  $changes[$counter] = substr($changes[$counter], 1);
2847              }
2848              // Adding or subtracting from previous value?
2849              if(substr($changes[$counter], 0, 1) == "+" || substr($changes[$counter], 0, 1) == "-")
2850              {
2851                  if((int)$changes[$counter] != 0)
2852                  {
2853                      $update_query[$counter] = $user[$counter] + $changes[$counter];
2854                  }
2855              }
2856              else
2857              {
2858                  $update_query[$counter] = $changes[$counter];
2859              }
2860  
2861              // Less than 0? That's bad
2862              if(isset($update_query[$counter]) && $update_query[$counter] < 0)
2863              {
2864                  $update_query[$counter] = 0;
2865              }
2866          }
2867      }
2868  
2869      $db->free_result($query);
2870  
2871      // Only update if we're actually doing something
2872      if(count($update_query) > 0)
2873      {
2874          $db->update_query("users", $update_query, "uid='{$uid}'");
2875      }
2876  }
2877  
2878  /**
2879   * Deletes a thread from the database
2880   *
2881   * @param int $tid The thread ID
2882   * @return bool
2883   */
2884  function delete_thread($tid)
2885  {
2886      global $moderation;
2887  
2888      if(!is_object($moderation))
2889      {
2890          require_once  MYBB_ROOT."inc/class_moderation.php";
2891          $moderation = new Moderation;
2892      }
2893  
2894      return $moderation->delete_thread($tid);
2895  }
2896  
2897  /**
2898   * Deletes a post from the database
2899   *
2900   * @param int $pid The thread ID
2901   * @return bool
2902   */
2903  function delete_post($pid)
2904  {
2905      global $moderation;
2906  
2907      if(!is_object($moderation))
2908      {
2909          require_once  MYBB_ROOT."inc/class_moderation.php";
2910          $moderation = new Moderation;
2911      }
2912  
2913      return $moderation->delete_post($pid);
2914  }
2915  
2916  /**
2917   * Builds a forum jump menu
2918   *
2919   * @param int $pid The parent forum to start with
2920   * @param int $selitem The selected item ID
2921   * @param int $addselect If we need to add select boxes to this cal or not
2922   * @param string $depth The current depth of forums we're at
2923   * @param int $showextras Whether or not to show extra items such as User CP, Forum home
2924   * @param boolean $showall Ignore the showinjump setting and show all forums (for moderation pages)
2925   * @param mixed $permissions deprecated
2926   * @param string $name The name of the forum jump
2927   * @return string Forum jump items
2928   */
2929  function build_forum_jump($pid=0, $selitem=0, $addselect=1, $depth="", $showextras=1, $showall=false, $permissions="", $name="fid")
2930  {
2931      global $forum_cache, $jumpfcache, $permissioncache, $mybb, $forumjump, $forumjumpbits, $gobutton, $theme, $templates, $lang;
2932  
2933      $pid = (int)$pid;
2934  
2935      if(!is_array($jumpfcache))
2936      {
2937          if(!is_array($forum_cache))
2938          {
2939              cache_forums();
2940          }
2941  
2942          foreach($forum_cache as $fid => $forum)
2943          {
2944              if($forum['active'] != 0)
2945              {
2946                  $jumpfcache[$forum['pid']][$forum['disporder']][$forum['fid']] = $forum;
2947              }
2948          }
2949      }
2950  
2951      if(!is_array($permissioncache))
2952      {
2953          $permissioncache = forum_permissions();
2954      }
2955  
2956      if(isset($jumpfcache[$pid]) && is_array($jumpfcache[$pid]))
2957      {
2958          foreach($jumpfcache[$pid] as $main)
2959          {
2960              foreach($main as $forum)
2961              {
2962                  $perms = $permissioncache[$forum['fid']];
2963  
2964                  if($forum['fid'] != "0" && ($perms['canview'] != 0 || $mybb->settings['hideprivateforums'] == 0) && $forum['linkto'] == '' && ($forum['showinjump'] != 0 || $showall == true))
2965                  {
2966                      $optionselected = "";
2967  
2968                      if($selitem == $forum['fid'])
2969                      {
2970                          $optionselected = 'selected="selected"';
2971                      }
2972  
2973                      $forum['name'] = htmlspecialchars_uni(strip_tags($forum['name']));
2974  
2975                      eval("\$forumjumpbits .= \"".$templates->get("forumjump_bit")."\";");
2976  
2977                      if($forum_cache[$forum['fid']])
2978                      {
2979                          $newdepth = $depth."--";
2980                          $forumjumpbits .= build_forum_jump($forum['fid'], $selitem, 0, $newdepth, $showextras, $showall);
2981                      }
2982                  }
2983              }
2984          }
2985      }
2986  
2987      if($addselect)
2988      {
2989          if($showextras == 0)
2990          {
2991              $template = "special";
2992          }
2993          else
2994          {
2995              $template = "advanced";
2996  
2997              if(strpos(FORUM_URL, '.html') !== false)
2998              {
2999                  $forum_link = "'".str_replace('{fid}', "'+option+'", FORUM_URL)."'";
3000              }
3001              else
3002              {
3003                  $forum_link = "'".str_replace('{fid}', "'+option", FORUM_URL);
3004              }
3005          }
3006  
3007          eval("\$forumjump = \"".$templates->get("forumjump_".$template)."\";");
3008      }
3009  
3010      return $forumjump;
3011  }
3012  
3013  /**
3014   * Returns the extension of a file.
3015   *
3016   * @param string $file The filename.
3017   * @return string The extension of the file.
3018   */
3019  function get_extension($file)
3020  {
3021      return my_strtolower(my_substr(strrchr($file, "."), 1));
3022  }
3023  
3024  /**
3025   * Generates a random string.
3026   *
3027   * @param int $length The length of the string to generate.
3028   * @param bool $complex Whether to return complex string. Defaults to false
3029   * @return string The random string.
3030   */
3031  function random_str($length=8, $complex=false)
3032  {
3033      $set = array_merge(range(0, 9), range('A', 'Z'), range('a', 'z'));
3034      $str = array();
3035  
3036      // Complex strings have always at least 3 characters, even if $length < 3
3037      if($complex == true)
3038      {
3039          // At least one number
3040          $str[] = $set[my_rand(0, 9)];
3041  
3042          // At least one big letter
3043          $str[] = $set[my_rand(10, 35)];
3044  
3045          // At least one small letter
3046          $str[] = $set[my_rand(36, 61)];
3047  
3048          $length -= 3;
3049      }
3050  
3051      for($i = 0; $i < $length; ++$i)
3052      {
3053          $str[] = $set[my_rand(0, 61)];
3054      }
3055  
3056      // Make sure they're in random order and convert them to a string
3057      shuffle($str);
3058  
3059      return implode($str);
3060  }
3061  
3062  /**
3063   * Formats a username based on their display group
3064   *
3065   * @param string $username The username
3066   * @param int $usergroup The usergroup for the user
3067   * @param int $displaygroup The display group for the user
3068   * @return string The formatted username
3069   */
3070  function format_name($username, $usergroup, $displaygroup=0)
3071  {
3072      global $groupscache, $cache, $plugins;
3073  
3074      static $formattednames = array();
3075  
3076      if(!isset($formattednames[$username]))
3077      {
3078          if(!is_array($groupscache))
3079          {
3080              $groupscache = $cache->read("usergroups");
3081          }
3082  
3083          if($displaygroup != 0)
3084          {
3085              $usergroup = $displaygroup;
3086          }
3087  
3088          $format = "{username}";
3089  
3090          if(isset($groupscache[$usergroup]))
3091          {
3092              $ugroup = $groupscache[$usergroup];
3093  
3094              if(strpos($ugroup['namestyle'], "{username}") !== false)
3095              {
3096                  $format = $ugroup['namestyle'];
3097              }
3098          }
3099  
3100          $format = stripslashes($format);
3101  
3102          $parameters = compact('username', 'usergroup', 'displaygroup', 'format');
3103  
3104          $parameters = $plugins->run_hooks('format_name', $parameters);
3105  
3106          $format = $parameters['format'];
3107  
3108          $formattednames[$username] = str_replace("{username}", $username, $format);
3109      }
3110  
3111      return $formattednames[$username];
3112  }
3113  
3114  /**
3115   * Formats an avatar to a certain dimension
3116   *
3117   * @param string $avatar The avatar file name
3118   * @param string $dimensions Dimensions of the avatar, width x height (e.g. 44|44)
3119   * @param string $max_dimensions The maximum dimensions of the formatted avatar
3120   * @return array Information for the formatted avatar
3121   */
3122  function format_avatar($avatar, $dimensions = '', $max_dimensions = '')
3123  {
3124      global $mybb, $theme;
3125      static $avatars;
3126  
3127      if(!isset($avatars))
3128      {
3129          $avatars = array();
3130      }
3131  
3132      if(my_strpos($avatar, '://') !== false && !$mybb->settings['allowremoteavatars'])
3133      {
3134          // Remote avatar, but remote avatars are disallowed.
3135          $avatar = null;
3136      }
3137  
3138      if(!$avatar)
3139      {
3140          // Default avatar
3141          if(defined('IN_ADMINCP'))
3142          {
3143              $theme['imgdir'] = '../images';
3144          }
3145  
3146          $avatar = str_replace('{theme}', $theme['imgdir'], $mybb->settings['useravatar']);
3147          $dimensions = $mybb->settings['useravatardims'];
3148      }
3149  
3150      if(!$max_dimensions)
3151      {
3152          $max_dimensions = $mybb->settings['maxavatardims'];
3153      }
3154  
3155      // An empty key wouldn't work so we need to add a fall back
3156      $key = $dimensions;
3157      if(empty($key))
3158      {
3159          $key = 'default';
3160      }
3161      $key2 = $max_dimensions;
3162      if(empty($key2))
3163      {
3164          $key2 = 'default';
3165      }
3166  
3167      if(isset($avatars[$avatar][$key][$key2]))
3168      {
3169          return $avatars[$avatar][$key][$key2];
3170      }
3171  
3172      $avatar_width_height = '';
3173  
3174      if($dimensions)
3175      {
3176          $dimensions = preg_split('/[|x]/', $dimensions);
3177  
3178          if($dimensions[0] && $dimensions[1])
3179          {
3180              list($max_width, $max_height) = preg_split('/[|x]/', $max_dimensions);
3181  
3182              if(!empty($max_dimensions) && ($dimensions[0] > $max_width || $dimensions[1] > $max_height))
3183              {
3184                  require_once  MYBB_ROOT."inc/functions_image.php";
3185                  $scaled_dimensions = scale_image($dimensions[0], $dimensions[1], $max_width, $max_height);
3186                  $avatar_width_height = "width=\"{$scaled_dimensions['width']}\" height=\"{$scaled_dimensions['height']}\"";
3187              }
3188              else
3189              {
3190                  $avatar_width_height = "width=\"{$dimensions[0]}\" height=\"{$dimensions[1]}\"";
3191              }
3192          }
3193      }
3194  
3195      $avatars[$avatar][$key][$key2] = array(
3196          'image' => htmlspecialchars_uni($mybb->get_asset_url($avatar)),
3197          'width_height' => $avatar_width_height
3198      );
3199  
3200      return $avatars[$avatar][$key][$key2];
3201  }
3202  
3203  /**
3204   * Build the javascript based MyCode inserter.
3205   *
3206   * @param string $bind The ID of the textarea to bind to. Defaults to "message".
3207   * @param bool $smilies Whether to include smilies. Defaults to true.
3208   *
3209   * @return string The MyCode inserter
3210   */
3211  function build_mycode_inserter($bind="message", $smilies = true)
3212  {
3213      global $db, $mybb, $theme, $templates, $lang, $plugins, $smiliecache, $cache;
3214  
3215      if($mybb->settings['bbcodeinserter'] != 0)
3216      {
3217          $editor_lang_strings = array(
3218              "editor_bold" => "Bold",
3219              "editor_italic" => "Italic",
3220              "editor_underline" => "Underline",
3221              "editor_strikethrough" => "Strikethrough",
3222              "editor_subscript" => "Subscript",
3223              "editor_superscript" => "Superscript",
3224              "editor_alignleft" => "Align left",
3225              "editor_center" => "Center",
3226              "editor_alignright" => "Align right",
3227              "editor_justify" => "Justify",
3228              "editor_fontname" => "Font Name",
3229              "editor_fontsize" => "Font Size",
3230              "editor_fontcolor" => "Font Color",
3231              "editor_removeformatting" => "Remove Formatting",
3232              "editor_cut" => "Cut",
3233              "editor_cutnosupport" => "Your browser does not allow the cut command. Please use the keyboard shortcut Ctrl/Cmd-X",
3234              "editor_copy" => "Copy",
3235              "editor_copynosupport" => "Your browser does not allow the copy command. Please use the keyboard shortcut Ctrl/Cmd-C",
3236              "editor_paste" => "Paste",
3237              "editor_pastenosupport" => "Your browser does not allow the paste command. Please use the keyboard shortcut Ctrl/Cmd-V",
3238              "editor_pasteentertext" => "Paste your text inside the following box:",
3239              "editor_pastetext" => "PasteText",
3240              "editor_numlist" => "Numbered list",
3241              "editor_bullist" => "Bullet list",
3242              "editor_undo" => "Undo",
3243              "editor_redo" => "Redo",
3244              "editor_rows" => "Rows:",
3245              "editor_cols" => "Cols:",
3246              "editor_inserttable" => "Insert a table",
3247              "editor_inserthr" => "Insert a horizontal rule",
3248              "editor_code" => "Code",
3249              "editor_width" => "Width (optional):",
3250              "editor_height" => "Height (optional):",
3251              "editor_insertimg" => "Insert an image",
3252              "editor_email" => "E-mail:",
3253              "editor_insertemail" => "Insert an email",
3254              "editor_url" => "URL:",
3255              "editor_insertlink" => "Insert a link",
3256              "editor_unlink" => "Unlink",
3257              "editor_more" => "More",
3258              "editor_insertemoticon" => "Insert an emoticon",
3259              "editor_videourl" => "Video URL:",
3260              "editor_videotype" => "Video Type:",
3261              "editor_insert" => "Insert",
3262              "editor_insertyoutubevideo" => "Insert a YouTube video",
3263              "editor_currentdate" => "Insert current date",
3264              "editor_currenttime" => "Insert current time",
3265              "editor_print" => "Print",
3266              "editor_viewsource" => "View source",
3267              "editor_description" => "Description (optional):",
3268              "editor_enterimgurl" => "Enter the image URL:",
3269              "editor_enteremail" => "Enter the e-mail address:",
3270              "editor_enterdisplayedtext" => "Enter the displayed text:",
3271              "editor_enterurl" => "Enter URL:",
3272              "editor_enteryoutubeurl" => "Enter the YouTube video URL or ID:",
3273              "editor_insertquote" => "Insert a Quote",
3274              "editor_invalidyoutube" => "Invalid YouTube video",
3275              "editor_dailymotion" => "Dailymotion",
3276              "editor_metacafe" => "MetaCafe",
3277              "editor_mixer" => "Mixer",
3278              "editor_vimeo" => "Vimeo",
3279              "editor_youtube" => "Youtube",
3280              "editor_facebook" => "Facebook",
3281              "editor_liveleak" => "LiveLeak",
3282              "editor_insertvideo" => "Insert a video",
3283              "editor_php" => "PHP",
3284              "editor_maximize" => "Maximize"
3285          );
3286          $editor_language = "(function ($) {\n$.sceditor.locale[\"mybblang\"] = {\n";
3287  
3288          $editor_lang_strings = $plugins->run_hooks("mycode_add_codebuttons", $editor_lang_strings);
3289  
3290          $editor_languages_count = count($editor_lang_strings);
3291          $i = 0;
3292          foreach($editor_lang_strings as $lang_string => $key)
3293          {
3294              $i++;
3295              $js_lang_string = str_replace("\"", "\\\"", $key);
3296              $string = str_replace("\"", "\\\"", $lang->$lang_string);
3297              $editor_language .= "\t\"{$js_lang_string}\": \"{$string}\"";
3298  
3299              if($i < $editor_languages_count)
3300              {
3301                  $editor_language .= ",";
3302              }
3303  
3304              $editor_language .= "\n";
3305          }
3306  
3307          $editor_language .= "}})(jQuery);";
3308  
3309          if(defined("IN_ADMINCP"))
3310          {
3311              global $page;
3312              $codeinsert = $page->build_codebuttons_editor($bind, $editor_language, $smilies);
3313          }
3314          else
3315          {
3316              // Smilies
3317              $emoticon = "";
3318              $emoticons_enabled = "false";
3319              if($smilies)
3320              {
3321                  if(!$smiliecache)
3322                  {
3323                      if(!isset($smilie_cache) || !is_array($smilie_cache))
3324                      {
3325                          $smilie_cache = $cache->read("smilies");
3326                      }
3327                      foreach($smilie_cache as $smilie)
3328                      {
3329                          $smilie['image'] = str_replace("{theme}", $theme['imgdir'], $smilie['image']);
3330                          $smiliecache[$smilie['sid']] = $smilie;
3331                      }
3332                  }
3333  
3334                  if($mybb->settings['smilieinserter'] && $mybb->settings['smilieinsertercols'] && $mybb->settings['smilieinsertertot'] && !empty($smiliecache))
3335                  {
3336                      $emoticon = ",emoticon";
3337                  }
3338                  $emoticons_enabled = "true";
3339  
3340                  unset($smilie);
3341  
3342                  if(is_array($smiliecache))
3343                  {
3344                      reset($smiliecache);
3345  
3346                      $dropdownsmilies = $moresmilies = $hiddensmilies = "";
3347                      $i = 0;
3348  
3349                      foreach($smiliecache as $smilie)
3350                      {
3351                          $finds = explode("\n", $smilie['find']);
3352                          $finds_count = count($finds);
3353  
3354                          // Only show the first text to replace in the box
3355                          $smilie['find'] = $finds[0];
3356  
3357                          $find = str_replace(array('\\', '"'), array('\\\\', '\"'), htmlspecialchars_uni($smilie['find']));
3358                          $image = htmlspecialchars_uni($mybb->get_asset_url($smilie['image']));
3359                          $image = str_replace(array('\\', '"'), array('\\\\', '\"'), $image);
3360  
3361                          if(!$mybb->settings['smilieinserter'] || !$mybb->settings['smilieinsertercols'] || !$mybb->settings['smilieinsertertot'] || !$smilie['showclickable'])
3362                          {
3363                              $hiddensmilies .= '"'.$find.'": "'.$image.'",';
3364                          }
3365                          elseif($i < $mybb->settings['smilieinsertertot'])
3366                          {
3367                              $dropdownsmilies .= '"'.$find.'": "'.$image.'",';
3368                              ++$i;
3369                          }
3370                          else
3371                          {
3372                              $moresmilies .= '"'.$find.'": "'.$image.'",';
3373                          }
3374  
3375                          for($j = 1; $j < $finds_count; ++$j)
3376                          {
3377                              $find = str_replace(array('\\', '"'), array('\\\\', '\"'), htmlspecialchars_uni($finds[$j]));
3378                              $hiddensmilies .= '"'.$find.'": "'.$image.'",';
3379                          }
3380                      }
3381                  }
3382              }
3383  
3384              $basic1 = $basic2 = $align = $font = $size = $color = $removeformat = $email = $link = $list = $code = $sourcemode = "";
3385  
3386              if($mybb->settings['allowbasicmycode'] == 1)
3387              {
3388                  $basic1 = "bold,italic,underline,strike|";
3389                  $basic2 = "horizontalrule,";
3390              }
3391  
3392              if($mybb->settings['allowalignmycode'] == 1)
3393              {
3394                  $align = "left,center,right,justify|";
3395              }
3396  
3397              if($mybb->settings['allowfontmycode'] == 1)
3398              {
3399                  $font = "font,";
3400              }
3401  
3402              if($mybb->settings['allowsizemycode'] == 1)
3403              {
3404                  $size = "size,";
3405              }
3406  
3407              if($mybb->settings['allowcolormycode'] == 1)
3408              {
3409                  $color = "color,";
3410              }
3411  
3412              if($mybb->settings['allowfontmycode'] == 1 || $mybb->settings['allowsizemycode'] == 1 || $mybb->settings['allowcolormycode'] == 1)
3413              {
3414                  $removeformat = "removeformat|";
3415              }
3416  
3417              if($mybb->settings['allowemailmycode'] == 1)
3418              {
3419                  $email = "email,";
3420              }
3421  
3422              if($mybb->settings['allowlinkmycode'] == 1)
3423              {
3424                  $link = "link,unlink";
3425              }
3426  
3427              if($mybb->settings['allowlistmycode'] == 1)
3428              {
3429                  $list = "bulletlist,orderedlist|";
3430              }
3431  
3432              if($mybb->settings['allowcodemycode'] == 1)
3433              {
3434                  $code = "code,php,";
3435              }
3436  
3437              if($mybb->user['sourceeditor'] == 1)
3438              {
3439                  $sourcemode = "MyBBEditor.sourceMode(true);";
3440              }
3441  
3442              eval("\$codeinsert = \"".$templates->get("codebuttons")."\";");
3443          }
3444      }
3445  
3446      return $codeinsert;
3447  }
3448  
3449  /**
3450   * @param int $tid
3451   * @param array $postoptions The options carried with form submit
3452   *
3453   * @return string Predefined / updated subscription method of the thread for the user
3454   */
3455  function get_subscription_method($tid = 0, $postoptions = array())
3456  {
3457      global $mybb;
3458  
3459      $subscription_methods = array('', 'none', 'email', 'pm'); // Define methods
3460      $subscription_method = (int)$mybb->user['subscriptionmethod']; // Set user default
3461  
3462      // If no user default method available then reset method
3463      if(!$subscription_method)
3464      {
3465          $subscription_method = 0;
3466      }
3467  
3468      // Return user default if no thread id available, in case
3469      if(!(int)$tid || (int)$tid <= 0)
3470      {
3471          return $subscription_methods[$subscription_method];
3472      }
3473  
3474      // If method not predefined set using data from database
3475      if(isset($postoptions['subscriptionmethod']))
3476      {
3477          $method = trim($postoptions['subscriptionmethod']);
3478          return (in_array($method, $subscription_methods)) ? $method : $subscription_methods[0];
3479      }
3480      else
3481      {
3482          global $db;
3483  
3484          $query = $db->simple_select("threadsubscriptions", "tid, notification", "tid='".(int)$tid."' AND uid='".$mybb->user['uid']."'", array('limit' => 1));
3485          $subscription = $db->fetch_array($query);
3486  
3487          if($subscription['tid'])
3488          {
3489              $subscription_method = (int)$subscription['notification'] + 1;
3490          }
3491      }
3492  
3493      return $subscription_methods[$subscription_method];
3494  }
3495  
3496  /**
3497   * Build the javascript clickable smilie inserter
3498   *
3499   * @return string The clickable smilies list
3500   */
3501  function build_clickable_smilies()
3502  {
3503      global $cache, $smiliecache, $theme, $templates, $lang, $mybb, $smiliecount;
3504  
3505      if($mybb->settings['smilieinserter'] != 0 && $mybb->settings['smilieinsertercols'] && $mybb->settings['smilieinsertertot'])
3506      {
3507          if(!$smiliecount)
3508          {
3509              $smilie_cache = $cache->read("smilies");
3510              $smiliecount = count($smilie_cache);
3511          }
3512  
3513          if(!$smiliecache)
3514          {
3515              if(!is_array($smilie_cache))
3516              {
3517                  $smilie_cache = $cache->read("smilies");
3518              }
3519              foreach($smilie_cache as $smilie)
3520              {
3521                  $smilie['image'] = str_replace("{theme}", $theme['imgdir'], $smilie['image']);
3522                  $smiliecache[$smilie['sid']] = $smilie;
3523              }
3524          }
3525  
3526          unset($smilie);
3527  
3528          if(is_array($smiliecache))
3529          {
3530              reset($smiliecache);
3531  
3532              $getmore = '';
3533              if($mybb->settings['smilieinsertertot'] >= $smiliecount)
3534              {
3535                  $mybb->settings['smilieinsertertot'] = $smiliecount;
3536              }
3537              else if($mybb->settings['smilieinsertertot'] < $smiliecount)
3538              {
3539                  $smiliecount = $mybb->settings['smilieinsertertot'];
3540                  eval("\$getmore = \"".$templates->get("smilieinsert_getmore")."\";");
3541              }
3542  
3543              $smilies = '';
3544              $counter = 0;
3545              $i = 0;
3546  
3547              $extra_class = '';
3548              foreach($smiliecache as $smilie)
3549              {
3550                  if($i < $mybb->settings['smilieinsertertot'] && $smilie['showclickable'] != 0)
3551                  {
3552                      $smilie['image'] = str_replace("{theme}", $theme['imgdir'], $smilie['image']);
3553                      $smilie['image'] = htmlspecialchars_uni($mybb->get_asset_url($smilie['image']));
3554                      $smilie['name'] = htmlspecialchars_uni($smilie['name']);
3555  
3556                      // Only show the first text to replace in the box
3557                      $temp = explode("\n", $smilie['find']); // assign to temporary variable for php 5.3 compatibility
3558                      $smilie['find'] = $temp[0];
3559  
3560                      $find = str_replace(array('\\', "'"), array('\\\\', "\'"), htmlspecialchars_uni($smilie['find']));
3561  
3562                      $onclick = " onclick=\"MyBBEditor.insertText(' $find ');\"";
3563                      $extra_class = ' smilie_pointer';
3564                      eval('$smilie = "'.$templates->get('smilie', 1, 0).'";');
3565                      eval("\$smilie_icons .= \"".$templates->get("smilieinsert_smilie")."\";");
3566                      ++$i;
3567                      ++$counter;
3568  
3569                      if($counter == $mybb->settings['smilieinsertercols'])
3570                      {
3571                          $counter = 0;
3572                          eval("\$smilies .= \"".$templates->get("smilieinsert_row")."\";");
3573                          $smilie_icons = '';
3574                      }
3575                  }
3576              }
3577  
3578              if($counter != 0)
3579              {
3580                  $colspan = $mybb->settings['smilieinsertercols'] - $counter;
3581                  eval("\$smilies .= \"".$templates->get("smilieinsert_row_empty")."\";");
3582              }
3583  
3584              eval("\$clickablesmilies = \"".$templates->get("smilieinsert")."\";");
3585          }
3586          else
3587          {
3588              $clickablesmilies = "";
3589          }
3590      }
3591      else
3592      {
3593          $clickablesmilies = "";
3594      }
3595  
3596      return $clickablesmilies;
3597  }
3598  
3599  /**
3600   * Builds thread prefixes and returns a selected prefix (or all)
3601   *
3602   *  @param int $pid The prefix ID (0 to return all)
3603   *  @return array The thread prefix's values (or all thread prefixes)
3604   */
3605  function build_prefixes($pid=0)
3606  {
3607      global $cache;
3608      static $prefixes_cache;
3609  
3610      if(is_array($prefixes_cache))
3611      {
3612          if($pid > 0 && is_array($prefixes_cache[$pid]))
3613          {
3614              return $prefixes_cache[$pid];
3615          }
3616  
3617          return $prefixes_cache;
3618      }
3619  
3620      $prefix_cache = $cache->read("threadprefixes");
3621  
3622      if(!is_array($prefix_cache))
3623      {
3624          // No cache
3625          $prefix_cache = $cache->read("threadprefixes", true);
3626  
3627          if(!is_array($prefix_cache))
3628          {
3629              return array();
3630          }
3631      }
3632  
3633      $prefixes_cache = array();
3634      foreach($prefix_cache as $prefix)
3635      {
3636          $prefixes_cache[$prefix['pid']] = $prefix;
3637      }
3638  
3639      if($pid != 0 && is_array($prefixes_cache[$pid]))
3640      {
3641          return $prefixes_cache[$pid];
3642      }
3643      else if(!empty($prefixes_cache))
3644      {
3645          return $prefixes_cache;
3646      }
3647  
3648      return false;
3649  }
3650  
3651  /**
3652   * Build the thread prefix selection menu for the current user
3653   *
3654   *  @param int|string $fid The forum ID (integer ID or string all)
3655   *  @param int|string $selected_pid The selected prefix ID (integer ID or string any)
3656   *  @param int $multiple Allow multiple prefix selection
3657   *  @param int $previous_pid The previously selected prefix ID
3658   *  @return string The thread prefix selection menu
3659   */
3660  function build_prefix_select($fid, $selected_pid=0, $multiple=0, $previous_pid=0)
3661  {
3662      global $cache, $db, $lang, $mybb, $templates;
3663  
3664      if($fid != 'all')
3665      {
3666          $fid = (int)$fid;
3667      }
3668  
3669      $prefix_cache = build_prefixes(0);
3670      if(empty($prefix_cache))
3671      {
3672          // We've got no prefixes to show
3673          return '';
3674      }
3675  
3676      // Go through each of our prefixes and decide which ones we can use
3677      $prefixes = array();
3678      foreach($prefix_cache as $prefix)
3679      {
3680          if($fid != "all" && $prefix['forums'] != "-1")
3681          {
3682              // Decide whether this prefix can be used in our forum
3683              $forums = explode(",", $prefix['forums']);
3684  
3685              if(!in_array($fid, $forums) && $prefix['pid'] != $previous_pid)
3686              {
3687                  // This prefix is not in our forum list
3688                  continue;
3689              }
3690          }
3691  
3692          if(is_member($prefix['groups']) || $prefix['pid'] == $previous_pid)
3693          {
3694              // The current user can use this prefix
3695              $prefixes[$prefix['pid']] = $prefix;
3696          }
3697      }
3698  
3699      if(empty($prefixes))
3700      {
3701          return '';
3702      }
3703  
3704      $prefixselect = $prefixselect_prefix = '';
3705  
3706      if($multiple == 1)
3707      {
3708          $any_selected = "";
3709          if($selected_pid == 'any')
3710          {
3711              $any_selected = " selected=\"selected\"";
3712          }
3713      }
3714  
3715      $default_selected = "";
3716      if(((int)$selected_pid == 0) && $selected_pid != 'any')
3717      {
3718          $default_selected = " selected=\"selected\"";
3719      }
3720  
3721      foreach($prefixes as $prefix)
3722      {
3723          $selected = "";
3724          if($prefix['pid'] == $selected_pid)
3725          {
3726              $selected = " selected=\"selected\"";
3727          }
3728  
3729          $prefix['prefix'] = htmlspecialchars_uni($prefix['prefix']);
3730          eval("\$prefixselect_prefix .= \"".$templates->get("post_prefixselect_prefix")."\";");
3731      }
3732  
3733      if($multiple != 0)
3734      {
3735          eval("\$prefixselect = \"".$templates->get("post_prefixselect_multiple")."\";");
3736      }
3737      else
3738      {
3739          eval("\$prefixselect = \"".$templates->get("post_prefixselect_single")."\";");
3740      }
3741  
3742      return $prefixselect;
3743  }
3744  
3745  /**
3746   * Build the thread prefix selection menu for a forum without group permission checks
3747   *
3748   *  @param int $fid The forum ID (integer ID)
3749   *  @param int $selected_pid The selected prefix ID (integer ID)
3750   *  @return string The thread prefix selection menu
3751   */
3752  function build_forum_prefix_select($fid, $selected_pid=0)
3753  {
3754      global $cache, $db, $lang, $mybb, $templates;
3755  
3756      $fid = (int)$fid;
3757  
3758      $prefix_cache = build_prefixes(0);
3759      if(empty($prefix_cache))
3760      {
3761          // We've got no prefixes to show
3762          return '';
3763      }
3764  
3765      // Go through each of our prefixes and decide which ones we can use
3766      $prefixes = array();
3767      foreach($prefix_cache as $prefix)
3768      {
3769          if($prefix['forums'] != "-1")
3770          {
3771              // Decide whether this prefix can be used in our forum
3772              $forums = explode(",", $prefix['forums']);
3773  
3774              if(in_array($fid, $forums))
3775              {
3776                  // This forum can use this prefix!
3777                  $prefixes[$prefix['pid']] = $prefix;
3778              }
3779          }
3780          else
3781          {
3782              // This prefix is for anybody to use...
3783              $prefixes[$prefix['pid']] = $prefix;
3784          }
3785      }
3786  
3787      if(empty($prefixes))
3788      {
3789          return '';
3790      }
3791  
3792      $default_selected = array();
3793      $selected_pid = (int)$selected_pid;
3794  
3795      if($selected_pid == 0)
3796      {
3797          $default_selected['all'] = ' selected="selected"';
3798      }
3799      else if($selected_pid == -1)
3800      {
3801          $default_selected['none'] = ' selected="selected"';
3802      }
3803      else if($selected_pid == -2)
3804      {
3805          $default_selected['any'] = ' selected="selected"';
3806      }
3807  
3808      foreach($prefixes as $prefix)
3809      {
3810          $selected = '';
3811          if($prefix['pid'] == $selected_pid)
3812          {
3813              $selected = ' selected="selected"';
3814          }
3815  
3816          $prefix['prefix'] = htmlspecialchars_uni($prefix['prefix']);
3817          eval('$prefixselect_prefix .= "'.$templates->get("forumdisplay_threadlist_prefixes_prefix").'";');
3818      }
3819  
3820      eval('$prefixselect = "'.$templates->get("forumdisplay_threadlist_prefixes").'";');
3821      return $prefixselect;
3822  }
3823  
3824  /**
3825   * Gzip encodes text to a specified level
3826   *
3827   * @param string $contents The string to encode
3828   * @param int $level The level (1-9) to encode at
3829   * @return string The encoded string
3830   */
3831  function gzip_encode($contents, $level=1)
3832  {
3833      if(function_exists("gzcompress") && function_exists("crc32") && !headers_sent() && !(ini_get('output_buffering') && my_strpos(' '.ini_get('output_handler'), 'ob_gzhandler')))
3834      {
3835          $httpaccept_encoding = '';
3836  
3837          if(isset($_SERVER['HTTP_ACCEPT_ENCODING']))
3838          {
3839              $httpaccept_encoding = $_SERVER['HTTP_ACCEPT_ENCODING'];
3840          }
3841  
3842          if(my_strpos(" ".$httpaccept_encoding, "x-gzip"))
3843          {
3844              $encoding = "x-gzip";
3845          }
3846  
3847          if(my_strpos(" ".$httpaccept_encoding, "gzip"))
3848          {
3849              $encoding = "gzip";
3850          }
3851  
3852          if(isset($encoding))
3853          {
3854              header("Content-Encoding: $encoding");
3855  
3856              if(function_exists("gzencode"))
3857              {
3858                  $contents = gzencode($contents, $level);
3859              }
3860              else
3861              {
3862                  $size = strlen($contents);
3863                  $crc = crc32($contents);
3864                  $gzdata = "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff";
3865                  $gzdata .= my_substr(gzcompress($contents, $level), 2, -4);
3866                  $gzdata .= pack("V", $crc);
3867                  $gzdata .= pack("V", $size);
3868                  $contents = $gzdata;
3869              }
3870          }
3871      }
3872  
3873      return $contents;
3874  }
3875  
3876  /**
3877   * Log the actions of a moderator.
3878   *
3879   * @param array $data The data of the moderator's action.
3880   * @param string $action The message to enter for the action the moderator performed.
3881   */
3882  function log_moderator_action($data, $action="")
3883  {
3884      global $mybb, $db, $session;
3885  
3886      $fid = 0;
3887      if(isset($data['fid']))
3888      {
3889          $fid = (int)$data['fid'];
3890          unset($data['fid']);
3891      }
3892  
3893      $tid = 0;
3894      if(isset($data['tid']))
3895      {
3896          $tid = (int)$data['tid'];
3897          unset($data['tid']);
3898      }
3899  
3900      $pid = 0;
3901      if(isset($data['pid']))
3902      {
3903          $pid = (int)$data['pid'];
3904          unset($data['pid']);
3905      }
3906  
3907      $tids = array();
3908      if(isset($data['tids']))
3909      {
3910          $tids = (array)$data['tids'];
3911          unset($data['tids']);
3912      }
3913  
3914      // Any remaining extra data - we my_serialize and insert in to its own column
3915      if(is_array($data))
3916      {
3917          $data = my_serialize($data);
3918      }
3919  
3920      $sql_array = array(
3921          "uid" => (int)$mybb->user['uid'],
3922          "dateline" => TIME_NOW,
3923          "fid" => (int)$fid,
3924          "tid" => $tid,
3925          "pid" => $pid,
3926          "action" => $db->escape_string($action),
3927          "data" => $db->escape_string($data),
3928          "ipaddress" => $db->escape_binary($session->packedip)
3929      );
3930  
3931      if($tids)
3932      {
3933          $multiple_sql_array = array();
3934  
3935          foreach($tids as $tid)
3936          {
3937              $sql_array['tid'] = (int)$tid;
3938              $multiple_sql_array[] = $sql_array;
3939          }
3940  
3941          $db->insert_query_multiple("moderatorlog", $multiple_sql_array);
3942      }
3943      else
3944      {
3945          $db->insert_query("moderatorlog", $sql_array);
3946      }
3947  }
3948  
3949  /**
3950   * Get the formatted reputation for a user.
3951   *
3952   * @param int $reputation The reputation value
3953   * @param int $uid The user ID (if not specified, the generated reputation will not be a link)
3954   * @return string The formatted repuation
3955   */
3956  function get_reputation($reputation, $uid=0)
3957  {
3958      global $theme, $templates;
3959  
3960      $display_reputation = $reputation_class = '';
3961      if($reputation < 0)
3962      {
3963          $reputation_class = "reputation_negative";
3964      }
3965      elseif($reputation > 0)
3966      {
3967          $reputation_class = "reputation_positive";
3968      }
3969      else
3970      {
3971          $reputation_class = "reputation_neutral";
3972      }
3973  
3974      $reputation = my_number_format($reputation);
3975  
3976      if($uid != 0)
3977      {
3978          eval("\$display_reputation = \"".$templates->get("postbit_reputation_formatted_link")."\";");
3979      }
3980      else
3981      {
3982          eval("\$display_reputation = \"".$templates->get("postbit_reputation_formatted")."\";");
3983      }
3984  
3985      return $display_reputation;
3986  }
3987  
3988  /**
3989   * Fetch a color coded version of a warning level (based on it's percentage)
3990   *
3991   * @param int $level The warning level (percentage of 100)
3992   * @return string Formatted warning level
3993   */
3994  function get_colored_warning_level($level)
3995  {
3996      global $templates;
3997  
3998      $warning_class = '';
3999      if($level >= 80)
4000      {
4001          $warning_class = "high_warning";
4002      }
4003      else if($level >= 50)
4004      {
4005          $warning_class = "moderate_warning";
4006      }
4007      else if($level >= 25)
4008      {
4009          $warning_class = "low_warning";
4010      }
4011      else
4012      {
4013          $warning_class = "normal_warning";
4014      }
4015  
4016      eval("\$level = \"".$templates->get("postbit_warninglevel_formatted")."\";");
4017      return $level;
4018  }
4019  
4020  /**
4021   * Fetch the IP address of the current user.
4022   *
4023   * @return string The IP address.
4024   */
4025  function get_ip()
4026  {
4027      global $mybb, $plugins;
4028  
4029      $ip = strtolower($_SERVER['REMOTE_ADDR']);
4030  
4031      if($mybb->settings['ip_forwarded_check'])
4032      {
4033          $addresses = array();
4034  
4035          if(isset($_SERVER['HTTP_X_FORWARDED_FOR']))
4036          {
4037              $addresses = explode(',', strtolower($_SERVER['HTTP_X_FORWARDED_FOR']));
4038          }
4039          elseif(isset($_SERVER['HTTP_X_REAL_IP']))
4040          {
4041              $addresses = explode(',', strtolower($_SERVER['HTTP_X_REAL_IP']));
4042          }
4043  
4044          if(is_array($addresses))
4045          {
4046              foreach($addresses as $val)
4047              {
4048                  $val = trim($val);
4049                  // Validate IP address and exclude private addresses
4050                  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))
4051                  {
4052                      $ip = $val;
4053                      break;
4054                  }
4055              }
4056          }
4057      }
4058  
4059      if(!$ip)
4060      {
4061          if(isset($_SERVER['HTTP_CLIENT_IP']))
4062          {
4063              $ip = strtolower($_SERVER['HTTP_CLIENT_IP']);
4064          }
4065      }
4066  
4067      if($plugins)
4068      {
4069          $ip_array = array("ip" => &$ip); // Used for backwards compatibility on this hook with the updated run_hooks() function.
4070          $plugins->run_hooks("get_ip", $ip_array);
4071      }
4072  
4073      return $ip;
4074  }
4075  
4076  /**
4077   * Fetch the friendly size (GB, MB, KB, B) for a specified file size.
4078   *
4079   * @param int $size The size in bytes
4080   * @return string The friendly file size
4081   */
4082  function get_friendly_size($size)
4083  {
4084      global $lang;
4085  
4086      if(!is_numeric($size))
4087      {
4088          return $lang->na;
4089      }
4090  
4091      // Yottabyte (1024 Zettabytes)
4092      if($size >= 1208925819614629174706176)
4093      {
4094          $size = my_number_format(round(($size / 1208925819614629174706176), 2))." ".$lang->size_yb;
4095      }
4096      // Zetabyte (1024 Exabytes)
4097      elseif($size >= 1180591620717411303424)
4098      {
4099          $size = my_number_format(round(($size / 1180591620717411303424), 2))." ".$lang->size_zb;
4100      }
4101      // Exabyte (1024 Petabytes)
4102      elseif($size >= 1152921504606846976)
4103      {
4104          $size = my_number_format(round(($size / 1152921504606846976), 2))." ".$lang->size_eb;
4105      }
4106      // Petabyte (1024 Terabytes)
4107      elseif($size >= 1125899906842624)
4108      {
4109          $size = my_number_format(round(($size / 1125899906842624), 2))." ".$lang->size_pb;
4110      }
4111      // Terabyte (1024 Gigabytes)
4112      elseif($size >= 1099511627776)
4113      {
4114          $size = my_number_format(round(($size / 1099511627776), 2))." ".$lang->size_tb;
4115      }
4116      // Gigabyte (1024 Megabytes)
4117      elseif($size >= 1073741824)
4118      {
4119          $size = my_number_format(round(($size / 1073741824), 2))." ".$lang->size_gb;
4120      }
4121      // Megabyte (1024 Kilobytes)
4122      elseif($size >= 1048576)
4123      {
4124          $size = my_number_format(round(($size / 1048576), 2))." ".$lang->size_mb;
4125      }
4126      // Kilobyte (1024 bytes)
4127      elseif($size >= 1024)
4128      {
4129          $size = my_number_format(round(($size / 1024), 2))." ".$lang->size_kb;
4130      }
4131      elseif($size == 0)
4132      {
4133          $size = "0 ".$lang->size_bytes;
4134      }
4135      else
4136      {
4137          $size = my_number_format($size)." ".$lang->size_bytes;
4138      }
4139  
4140      return $size;
4141  }
4142  
4143  /**
4144   * Format a decimal number in to microseconds, milliseconds, or seconds.
4145   *
4146   * @param int $time The time in microseconds
4147   * @return string The friendly time duration
4148   */
4149  function format_time_duration($time)
4150  {
4151      global $lang;
4152  
4153      if(!is_numeric($time))
4154      {
4155          return $lang->na;
4156      }
4157  
4158      if(round(1000000 * $time, 2) < 1000)
4159      {
4160          $time = number_format(round(1000000 * $time, 2))." μs";
4161      }
4162      elseif(round(1000000 * $time, 2) >= 1000 && round(1000000 * $time, 2) < 1000000)
4163      {
4164          $time = number_format(round((1000 * $time), 2))." ms";
4165      }
4166      else
4167      {
4168          $time = round($time, 3)." seconds";
4169      }
4170  
4171      return $time;
4172  }
4173  
4174  /**
4175   * Get the attachment icon for a specific file extension
4176   *
4177   * @param string $ext The file extension
4178   * @return string The attachment icon
4179   */
4180  function get_attachment_icon($ext)
4181  {
4182      global $cache, $attachtypes, $theme, $templates, $lang, $mybb;
4183  
4184      if(!$attachtypes)
4185      {
4186          $attachtypes = $cache->read("attachtypes");
4187      }
4188  
4189      $ext = my_strtolower($ext);
4190  
4191      if($attachtypes[$ext]['icon'])
4192      {
4193          static $attach_icons_schemes = array();
4194          if(!isset($attach_icons_schemes[$ext]))
4195          {
4196              $attach_icons_schemes[$ext] = parse_url($attachtypes[$ext]['icon']);
4197              if(!empty($attach_icons_schemes[$ext]['scheme']))
4198              {
4199                  $attach_icons_schemes[$ext] = $attachtypes[$ext]['icon'];
4200              }
4201              elseif(defined("IN_ADMINCP"))
4202              {
4203                  $attach_icons_schemes[$ext] = str_replace("{theme}", "", $attachtypes[$ext]['icon']);
4204                  if(my_substr($attach_icons_schemes[$ext], 0, 1) != "/")
4205                  {
4206                      $attach_icons_schemes[$ext] = "../".$attach_icons_schemes[$ext];
4207                  }
4208              }
4209              elseif(defined("IN_PORTAL"))
4210              {
4211                  global $change_dir;
4212                  $attach_icons_schemes[$ext] = $change_dir."/".str_replace("{theme}", $theme['imgdir'], $attachtypes[$ext]['icon']);
4213                  $attach_icons_schemes[$ext] = $mybb->get_asset_url($attach_icons_schemes[$ext]);
4214              }
4215              else
4216              {
4217                  $attach_icons_schemes[$ext] = str_replace("{theme}", $theme['imgdir'], $attachtypes[$ext]['icon']);
4218                  $attach_icons_schemes[$ext] = $mybb->get_asset_url($attach_icons_schemes[$ext]);
4219              }
4220          }
4221  
4222          $icon = $attach_icons_schemes[$ext];
4223  
4224          $name = htmlspecialchars_uni($attachtypes[$ext]['name']);
4225      }
4226      else
4227      {
4228          if(defined("IN_ADMINCP"))
4229          {
4230              $theme['imgdir'] = "../images";
4231          }
4232          else if(defined("IN_PORTAL"))
4233          {
4234              global $change_dir;
4235              $theme['imgdir'] = "{$change_dir}/images";
4236          }
4237  
4238          $icon = "{$theme['imgdir']}/attachtypes/unknown.png";
4239  
4240          $name = $lang->unknown;
4241      }
4242  
4243      $icon = htmlspecialchars_uni($icon);
4244      eval("\$attachment_icon = \"".$templates->get("attachment_icon")."\";");
4245      return $attachment_icon;
4246  }
4247  
4248  /**
4249   * Get a list of the unviewable forums for the current user
4250   *
4251   * @param boolean $only_readable_threads Set to true to only fetch those forums for which users can actually read a thread in.
4252   * @return string Comma separated values list of the forum IDs which the user cannot view
4253   */
4254  function get_unviewable_forums($only_readable_threads=false)
4255  {
4256      global $forum_cache, $permissioncache, $mybb;
4257  
4258      if(!is_array($forum_cache))
4259      {
4260          cache_forums();
4261      }
4262  
4263      if(!is_array($permissioncache))
4264      {
4265          $permissioncache = forum_permissions();
4266      }
4267  
4268      $password_forums = $unviewable = array();
4269      foreach($forum_cache as $fid => $forum)
4270      {
4271          if($permissioncache[$forum['fid']])
4272          {
4273              $perms = $permissioncache[$forum['fid']];
4274          }
4275          else
4276          {
4277              $perms = $mybb->usergroup;
4278          }
4279  
4280          $pwverified = 1;
4281  
4282          if($forum['password'] != "")
4283          {
4284              if($mybb->cookies['forumpass'][$forum['fid']] !== md5($mybb->user['uid'].$forum['password']))
4285              {
4286                  $pwverified = 0;
4287              }
4288  
4289              $password_forums[$forum['fid']] = $forum['password'];
4290          }
4291          else
4292          {
4293              // Check parents for passwords
4294              $parents = explode(",", $forum['parentlist']);
4295              foreach($parents as $parent)
4296              {
4297                  if(isset($password_forums[$parent]) && $mybb->cookies['forumpass'][$parent] !== md5($mybb->user['uid'].$password_forums[$parent]))
4298                  {
4299                      $pwverified = 0;
4300                  }
4301              }
4302          }
4303  
4304          if($perms['canview'] == 0 || $pwverified == 0 || ($only_readable_threads == true && $perms['canviewthreads'] == 0))
4305          {
4306              $unviewable[] = $forum['fid'];
4307          }
4308      }
4309  
4310      $unviewableforums = implode(',', $unviewable);
4311  
4312      return $unviewableforums;
4313  }
4314  
4315  /**
4316   * Fixes mktime for dates earlier than 1970
4317   *
4318   * @param string $format The date format to use
4319   * @param int $year The year of the date
4320   * @return string The correct date format
4321   */
4322  function fix_mktime($format, $year)
4323  {
4324      // Our little work around for the date < 1970 thing.
4325      // -2 idea provided by Matt Light (http://www.mephex.com)
4326      $format = str_replace("Y", $year, $format);
4327      $format = str_replace("y", my_substr($year, -2), $format);
4328  
4329      return $format;
4330  }
4331  
4332  /**
4333   * Build the breadcrumb navigation trail from the specified items
4334   *
4335   * @return string The formatted breadcrumb navigation trail
4336   */
4337  function build_breadcrumb()
4338  {
4339      global $nav, $navbits, $templates, $theme, $lang, $mybb;
4340  
4341      eval("\$navsep = \"".$templates->get("nav_sep")."\";");
4342  
4343      $i = 0;
4344      $activesep = '';
4345  
4346      if(is_array($navbits))
4347      {
4348          reset($navbits);
4349          foreach($navbits as $key => $navbit)
4350          {
4351              if(isset($navbits[$key+1]))
4352              {
4353                  if(isset($navbits[$key+2]))
4354                  {
4355                      $sep = $navsep;
4356                  }
4357                  else
4358                  {
4359                      $sep = "";
4360                  }
4361  
4362                  $multipage = null;
4363                  $multipage_dropdown = null;
4364                  if(!empty($navbit['multipage']))
4365                  {
4366                      if(!$mybb->settings['threadsperpage'] || (int)$mybb->settings['threadsperpage'] < 1)
4367                      {
4368                          $mybb->settings['threadsperpage'] = 20;
4369                      }
4370  
4371                      $multipage = multipage($navbit['multipage']['num_threads'], $mybb->settings['threadsperpage'], $navbit['multipage']['current_page'], $navbit['multipage']['url'], true);
4372                      if($multipage)
4373                      {
4374                          ++$i;
4375                          eval("\$multipage_dropdown = \"".$templates->get("nav_dropdown")."\";");
4376                          $sep = $multipage_dropdown.$sep;
4377                      }
4378                  }
4379  
4380                  // Replace page 1 URLs
4381                  $navbit['url'] = str_replace("-page-1.html", ".html", $navbit['url']);
4382                  $navbit['url'] = preg_replace("/&amp;page=1$/", "", $navbit['url']);
4383  
4384                  eval("\$nav .= \"".$templates->get("nav_bit")."\";");
4385              }
4386          }
4387          $navsize = count($navbits);
4388          $navbit = $navbits[$navsize-1];
4389      }
4390  
4391      if($nav)
4392      {
4393          eval("\$activesep = \"".$templates->get("nav_sep_active")."\";");
4394      }
4395  
4396      eval("\$activebit = \"".$templates->get("nav_bit_active")."\";");
4397      eval("\$donenav = \"".$templates->get("nav")."\";");
4398  
4399      return $donenav;
4400  }
4401  
4402  /**
4403   * Add a breadcrumb menu item to the list.
4404   *
4405   * @param string $name The name of the item to add
4406   * @param string $url The URL of the item to add
4407   */
4408  function add_breadcrumb($name, $url="")
4409  {
4410      global $navbits;
4411  
4412      $navsize = count($navbits);
4413      $navbits[$navsize]['name'] = $name;
4414      $navbits[$navsize]['url'] = $url;
4415  }
4416  
4417  /**
4418   * Build the forum breadcrumb nagiation (the navigation to a specific forum including all parent forums)
4419   *
4420   * @param int $fid The forum ID to build the navigation for
4421   * @param array $multipage The multipage drop down array of information
4422   * @return int Returns 1 in every case. Kept for compatibility
4423   */
4424  function build_forum_breadcrumb($fid, $multipage=array())
4425  {
4426      global $pforumcache, $currentitem, $forum_cache, $navbits, $lang, $base_url, $archiveurl;
4427  
4428      if(!$pforumcache)
4429      {
4430          if(!is_array($forum_cache))
4431          {
4432              cache_forums();
4433          }
4434  
4435          foreach($forum_cache as $key => $val)
4436          {
4437              $pforumcache[$val['fid']][$val['pid']] = $val;
4438          }
4439      }
4440  
4441      if(is_array($pforumcache[$fid]))
4442      {
4443          foreach($pforumcache[$fid] as $key => $forumnav)
4444          {
4445              if($fid == $forumnav['fid'])
4446              {
4447                  if(!empty($pforumcache[$forumnav['pid']]))
4448                  {
4449                      build_forum_breadcrumb($forumnav['pid']);
4450                  }
4451  
4452                  $navsize = count($navbits);
4453                  // Convert & to &amp;
4454                  $navbits[$navsize]['name'] = preg_replace("#&(?!\#[0-9]+;)#si", "&amp;", $forumnav['name']);
4455  
4456                  if(defined("IN_ARCHIVE"))
4457                  {
4458                      // Set up link to forum in breadcrumb.
4459                      if($pforumcache[$fid][$forumnav['pid']]['type'] == 'f' || $pforumcache[$fid][$forumnav['pid']]['type'] == 'c')
4460                      {
4461                          $navbits[$navsize]['url'] = "{$base_url}forum-".$forumnav['fid'].".html";
4462                      }
4463                      else
4464                      {
4465                          $navbits[$navsize]['url'] = $archiveurl."/index.php";
4466                      }
4467                  }
4468                  elseif(!empty($multipage))
4469                  {
4470                      $navbits[$navsize]['url'] = get_forum_link($forumnav['fid'], $multipage['current_page']);
4471  
4472                      $navbits[$navsize]['multipage'] = $multipage;
4473                      $navbits[$navsize]['multipage']['url'] = str_replace('{fid}', $forumnav['fid'], FORUM_URL_PAGED);
4474                  }
4475                  else
4476                  {
4477                      $navbits[$navsize]['url'] = get_forum_link($forumnav['fid']);
4478                  }
4479              }
4480          }
4481      }
4482  
4483      return 1;
4484  }
4485  
4486  /**
4487   * Resets the breadcrumb navigation to the first item, and clears the rest
4488   */
4489  function reset_breadcrumb()
4490  {
4491      global $navbits;
4492  
4493      $newnav[0]['name'] = $navbits[0]['name'];
4494      $newnav[0]['url'] = $navbits[0]['url'];
4495      if(!empty($navbits[0]['options']))
4496      {
4497          $newnav[0]['options'] = $navbits[0]['options'];
4498      }
4499  
4500      unset($GLOBALS['navbits']);
4501      $GLOBALS['navbits'] = $newnav;
4502  }
4503  
4504  /**
4505   * Builds a URL to an archive mode page
4506   *
4507   * @param string $type The type of page (thread|announcement|forum)
4508   * @param int $id The ID of the item
4509   * @return string The URL
4510   */
4511  function build_archive_link($type="", $id=0)
4512  {
4513      global $mybb;
4514  
4515      // 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
4516      //if((DIRECTORY_SEPARATOR == '\\' && is_numeric(stripos($_SERVER['SERVER_SOFTWARE'], "apache")) == false) || is_numeric(stripos(SAPI_NAME, "cgi")) !== false || defined("ARCHIVE_QUERY_STRINGS"))
4517      if($mybb->settings['seourls_archive'] == 1)
4518      {
4519          $base_url = $mybb->settings['bburl']."/archive/index.php/";
4520      }
4521      else
4522      {
4523          $base_url = $mybb->settings['bburl']."/archive/index.php?";
4524      }
4525  
4526      switch($type)
4527      {
4528          case "thread":
4529              $url = "{$base_url}thread-{$id}.html";
4530              break;
4531          case "announcement":
4532              $url = "{$base_url}announcement-{$id}.html";
4533              break;
4534          case "forum":
4535              $url = "{$base_url}forum-{$id}.html";
4536              break;
4537          default:
4538              $url = $mybb->settings['bburl']."/archive/index.php";
4539      }
4540  
4541      return $url;
4542  }
4543  
4544  /**
4545   * Prints a debug information page
4546   */
4547  function debug_page()
4548  {
4549      global $db, $debug, $templates, $templatelist, $mybb, $maintimer, $globaltime, $ptimer, $parsetime, $lang, $cache;
4550  
4551      $totaltime = format_time_duration($maintimer->totaltime);
4552      $phptime = $maintimer->totaltime - $db->query_time;
4553      $query_time = $db->query_time;
4554      $globaltime = format_time_duration($globaltime);
4555  
4556      $percentphp = number_format((($phptime/$maintimer->totaltime)*100), 2);
4557      $percentsql = number_format((($query_time/$maintimer->totaltime)*100), 2);
4558  
4559      $phptime = format_time_duration($maintimer->totaltime - $db->query_time);
4560      $query_time = format_time_duration($db->query_time);
4561  
4562      $call_time = format_time_duration($cache->call_time);
4563  
4564      $phpversion = PHP_VERSION;
4565  
4566      $serverload = get_server_load();
4567  
4568      if($mybb->settings['gzipoutput'] != 0)
4569      {
4570          $gzipen = "Enabled";
4571      }
4572      else
4573      {
4574          $gzipen = "Disabled";
4575      }
4576  
4577      echo "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n";
4578      echo "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">";
4579      echo "<head>";
4580      echo "<meta name=\"robots\" content=\"noindex\" />";
4581      echo "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />";
4582      echo "<title>MyBB Debug Information</title>";
4583      echo "</head>";
4584      echo "<body>";
4585      echo "<h1>MyBB Debug Information</h1>\n";
4586      echo "<h2>Page Generation</h2>\n";
4587      echo "<table bgcolor=\"#666666\" width=\"95%\" cellpadding=\"4\" cellspacing=\"1\" align=\"center\">\n";
4588      echo "<tr>\n";
4589      echo "<td bgcolor=\"#cccccc\" colspan=\"4\"><b><span style=\"size:2;\">Page Generation Statistics</span></b></td>\n";
4590      echo "</tr>\n";
4591      echo "<tr>\n";
4592      echo "<td bgcolor=\"#efefef\" width=\"25%\"><b><span style=\"font-family: tahoma; font-size: 12px;\">Page Generation Time:</span></b></td>\n";
4593      echo "<td bgcolor=\"#fefefe\" width=\"25%\"><span style=\"font-family: tahoma; font-size: 12px;\">$totaltime</span></td>\n";
4594      echo "<td bgcolor=\"#efefef\" width=\"25%\"><b><span style=\"font-family: tahoma; font-size: 12px;\">No. DB Queries:</span></b></td>\n";