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