Guest User

Untitled

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