Advertisement
Guest User

Untitled

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