Advertisement
Serpius5005

Untitled

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