Guest User

Untitled

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