· 8 years ago · May 19, 2018, 11:42 AM
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 * Checks if a user with uid $uid exists in the database.
13 *
14 * @param int $uid The uid to check for.
15 * @return boolean True when exists, false when not.
16 */
17function user_exists($uid)
18{
19 global $db;
20
21 $query = $db->simple_select("users", "COUNT(*) as user", "uid='".(int)$uid."'", array('limit' => 1));
22 if($db->fetch_field($query, 'user') == 1)
23 {
24 return true;
25 }
26 else
27 {
28 return false;
29 }
30}
31
32/**
33 * Checks if $username already exists in the database.
34 *
35 * @param string $username The username for check for.
36 * @return boolean True when exists, false when not.
37 */
38function username_exists($username)
39{
40 $options = array(
41 'username_method' => 2
42 );
43
44 return (bool)get_user_by_username($username, $options);
45}
46
47/**
48 * Checks a password with a supplied username.
49 *
50 * @param string $username The username of the user.
51 * @param string $password The plain-text password.
52 * @return boolean|array False when no match, array with user info when match.
53 */
54function validate_password_from_username($username, $password)
55{
56 global $mybb;
57
58 $options = array(
59 'fields' => '*',
60 'username_method' => $mybb->settings['username_method'],
61 );
62
63 $user = get_user_by_username($username, $options);
64
65 if(!$user['uid'])
66 {
67 return false;
68 }
69
70 return validate_password_from_uid($user['uid'], $password, $user);
71}
72
73/**
74 * Checks a password with a supplied uid.
75 *
76 * @param int $uid The user id.
77 * @param string $password The plain-text password.
78 * @param array $user An optional user data array.
79 * @return boolean|array False when not valid, user data array when valid.
80 */
81function validate_password_from_uid($uid, $password, $user = array())
82{
83 global $db, $mybb;
84 if(isset($mybb->user['uid']) && $mybb->user['uid'] == $uid)
85 {
86 $user = $mybb->user;
87 }
88 if(!$user['password'])
89 {
90 $user = get_user($uid);
91 }
92 if(!$user['salt'])
93 {
94 // Generate a salt for this user and assume the password stored in db is a plain md5 password
95 $password_fields = create_password($user['password'], false, $user);
96 $db->update_query("users", $password_fields, "uid='".$user['uid']."'");
97 }
98
99 if(!$user['loginkey'])
100 {
101 $user['loginkey'] = generate_loginkey();
102 $sql_array = array(
103 "loginkey" => $user['loginkey']
104 );
105 $db->update_query("users", $sql_array, "uid = ".$user['uid']);
106 }
107 if(verify_user_password($user, $password))
108 {
109 return $user;
110 }
111 else
112 {
113 return false;
114 }
115}
116
117/**
118 * Updates a user's password.
119 *
120 * @param int $uid The user's id.
121 * @param string $password The md5()'ed password.
122 * @param string $salt (Optional) The salt of the user.
123 * @return array The new password.
124 * @deprecated deprecated since version 1.8.6 Please use other alternatives.
125 */
126function update_password($uid, $password, $salt="")
127{
128 global $db, $plugins;
129
130 $newpassword = array();
131
132 // If no salt was specified, check in database first, if still doesn't exist, create one
133 if(!$salt)
134 {
135 $query = $db->simple_select("users", "salt", "uid='$uid'");
136 $user = $db->fetch_array($query);
137 if($user['salt'])
138 {
139 $salt = $user['salt'];
140 }
141 else
142 {
143 $salt = generate_salt();
144 }
145 $newpassword['salt'] = $salt;
146 }
147
148 // Create new password based on salt
149 $saltedpw = salt_password($password, $salt);
150
151 // Generate new login key
152 $loginkey = generate_loginkey();
153
154 // Update password and login key in database
155 $newpassword['password'] = $saltedpw;
156 $newpassword['loginkey'] = $loginkey;
157 $db->update_query("users", $newpassword, "uid='$uid'");
158
159 $plugins->run_hooks("password_changed");
160
161 return $newpassword;
162}
163
164/**
165 * Salts a password based on a supplied salt.
166 *
167 * @param string $password The md5()'ed password.
168 * @param string $salt The salt.
169 * @return string The password hash.
170 * @deprecated deprecated since version 1.8.9 Please use other alternatives.
171 */
172function salt_password($password, $salt)
173{
174 return md5(md5($salt).$password);
175}
176
177/**
178 * Salts a password based on a supplied salt.
179 *
180 * @param string $password The input password.
181 * @param string $salt (Optional) The salt used by the MyBB algorithm.
182 * @param string $user (Optional) An array containing password-related data.
183 * @return array Password-related fields.
184 */
185function create_password($password, $salt = false, $user = false)
186{
187 global $plugins;
188
189 $fields = null;
190
191 $parameters = compact('password', 'salt', 'user', 'fields');
192
193 if(!defined('IN_INSTALL') && !defined('IN_UPGRADE'))
194 {
195 $plugins->run_hooks('create_password', $parameters);
196 }
197
198 if(!is_null($parameters['fields']))
199 {
200 $fields = $parameters['fields'];
201 }
202 else
203 {
204 if(!$salt)
205 {
206 $salt = generate_salt();
207 }
208
209 $hash = md5(md5($salt).md5($password));
210
211 $fields = array(
212 'salt' => $salt,
213 'password' => $hash,
214 );
215 }
216
217 return $fields;
218}
219
220/**
221 * Compares user's password data against provided input.
222 *
223 * @param array $user An array containing password-related data.
224 * @param string $password The plain-text input password.
225 * @return bool Result of the comparison.
226 */
227function verify_user_password($user, $password)
228{
229 global $plugins;
230
231 $result = null;
232
233 $parameters = compact('user', 'password', 'result');
234
235 if(!defined('IN_INSTALL') && !defined('IN_UPGRADE'))
236 {
237 $plugins->run_hooks('verify_user_password', $parameters);
238 }
239
240 if(!is_null($parameters['result']))
241 {
242 return $parameters['result'];
243 }
244 else
245 {
246 $password_fields = create_password($password, $user['salt'], $user);
247
248 return my_hash_equals($user['password'], $password_fields['password']);
249 }
250}
251
252/**
253 * Performs a timing attack safe string comparison.
254 *
255 * @param string $known_string The first string to be compared.
256 * @param string $user_string The second, user-supplied string to be compared.
257 * @return bool Result of the comparison.
258 */
259function my_hash_equals($known_string, $user_string)
260{
261 if(version_compare(PHP_VERSION, '5.6.0', '>='))
262 {
263 return hash_equals($known_string, $user_string);
264 }
265 else
266 {
267 $known_string_length = my_strlen($known_string);
268 $user_string_length = my_strlen($user_string);
269
270 if($user_string_length != $known_string_length)
271 {
272 return false;
273 }
274
275 $result = 0;
276
277 for($i = 0; $i < $known_string_length; $i++)
278 {
279 $result |= ord($known_string[$i]) ^ ord($user_string[$i]);
280 }
281
282 return $result === 0;
283 }
284}
285
286/**
287 * Generates a random salt
288 *
289 * @return string The salt.
290 */
291function generate_salt()
292{
293 return random_str(8);
294}
295
296/**
297 * Generates a 50 character random login key.
298 *
299 * @return string The login key.
300 */
301function generate_loginkey()
302{
303 return random_str(50);
304}
305
306/**
307 * Updates a user's salt in the database (does not update a password).
308 *
309 * @param int $uid The uid of the user to update.
310 * @return string The new salt.
311 */
312function update_salt($uid)
313{
314 global $db;
315
316 $salt = generate_salt();
317 $sql_array = array(
318 "salt" => $salt
319 );
320 $db->update_query("users", $sql_array, "uid='{$uid}'");
321
322 return $salt;
323}
324
325/**
326 * Generates a new login key for a user.
327 *
328 * @param int $uid The uid of the user to update.
329 * @return string The new login key.
330 */
331function update_loginkey($uid)
332{
333 global $db;
334
335 $loginkey = generate_loginkey();
336 $sql_array = array(
337 "loginkey" => $loginkey
338 );
339 $db->update_query("users", $sql_array, "uid='{$uid}'");
340
341 return $loginkey;
342
343}
344
345/**
346 * Adds a thread to a user's thread subscription list.
347 * If no uid is supplied, the currently logged in user's id will be used.
348 *
349 * @param int $tid The tid of the thread to add to the list.
350 * @param int $notification (Optional) The type of notification to receive for replies (0=none, 1=email, 2=pm)
351 * @param int $uid (Optional) The uid of the user who's list to update.
352 * @return boolean True when success, false when otherwise.
353 */
354function add_subscribed_thread($tid, $notification=1, $uid=0)
355{
356 global $mybb, $db;
357
358 if(!$uid)
359 {
360 $uid = $mybb->user['uid'];
361 }
362
363 if(!$uid)
364 {
365 return false;
366 }
367
368 $query = $db->simple_select("threadsubscriptions", "*", "tid='".(int)$tid."' AND uid='".(int)$uid."'");
369 $subscription = $db->fetch_array($query);
370 if(!$subscription['tid'])
371 {
372 $insert_array = array(
373 'uid' => (int)$uid,
374 'tid' => (int)$tid,
375 'notification' => (int)$notification,
376 'dateline' => TIME_NOW
377 );
378 $db->insert_query("threadsubscriptions", $insert_array);
379 }
380 else
381 {
382 // Subscription exists - simply update notification
383 $update_array = array(
384 "notification" => (int)$notification
385 );
386 $db->update_query("threadsubscriptions", $update_array, "uid='{$uid}' AND tid='{$tid}'");
387 }
388 return true;
389}
390
391/**
392 * Remove a thread from a user's thread subscription list.
393 * If no uid is supplied, the currently logged in user's id will be used.
394 *
395 * @param int $tid The tid of the thread to remove from the list.
396 * @param int $uid (Optional) The uid of the user who's list to update.
397 * @return boolean True when success, false when otherwise.
398 */
399function remove_subscribed_thread($tid, $uid=0)
400{
401 global $mybb, $db;
402
403 if(!$uid)
404 {
405 $uid = $mybb->user['uid'];
406 }
407
408 if(!$uid)
409 {
410 return false;
411 }
412 $db->delete_query("threadsubscriptions", "tid='".$tid."' AND uid='{$uid}'");
413
414 return true;
415}
416
417/**
418 * Adds a forum to a user's forum subscription list.
419 * If no uid is supplied, the currently logged in user's id will be used.
420 *
421 * @param int $fid The fid of the forum to add to the list.
422 * @param int $uid (Optional) The uid of the user who's list to update.
423 * @return boolean True when success, false when otherwise.
424 */
425function add_subscribed_forum($fid, $uid=0)
426{
427 global $mybb, $db;
428
429 if(!$uid)
430 {
431 $uid = $mybb->user['uid'];
432 }
433
434 if(!$uid)
435 {
436 return false;
437 }
438
439 $fid = (int)$fid;
440 $uid = (int)$uid;
441
442 $query = $db->simple_select("forumsubscriptions", "*", "fid='".$fid."' AND uid='{$uid}'", array('limit' => 1));
443 $fsubscription = $db->fetch_array($query);
444 if(!$fsubscription['fid'])
445 {
446 $insert_array = array(
447 'fid' => $fid,
448 'uid' => $uid
449 );
450 $db->insert_query("forumsubscriptions", $insert_array);
451 }
452
453 return true;
454}
455
456/**
457 * Removes a forum from a user's forum subscription list.
458 * If no uid is supplied, the currently logged in user's id will be used.
459 *
460 * @param int $fid The fid of the forum to remove from the list.
461 * @param int $uid (Optional) The uid of the user who's list to update.
462 * @return boolean True when success, false when otherwise.
463 */
464function remove_subscribed_forum($fid, $uid=0)
465{
466 global $mybb, $db;
467
468 if(!$uid)
469 {
470 $uid = $mybb->user['uid'];
471 }
472
473 if(!$uid)
474 {
475 return false;
476 }
477 $db->delete_query("forumsubscriptions", "fid='".$fid."' AND uid='{$uid}'");
478
479 return true;
480}
481
482/**
483 * Constructs the usercp navigation menu.
484 *
485 */
486function usercp_menu()
487{
488 global $mybb, $templates, $theme, $plugins, $lang, $usercpnav, $usercpmenu;
489
490 $lang->load("usercpnav");
491
492 // Add the default items as plugins with separated priorities of 10
493 if($mybb->settings['enablepms'] != 0 && $mybb->usergroup['canusepms'] == 1)
494 {
495 $plugins->add_hook("usercp_menu", "usercp_menu_messenger", 10);
496 }
497
498 if($mybb->usergroup['canusercp'] == 1)
499 {
500 $plugins->add_hook("usercp_menu", "usercp_menu_profile", 20);
501 $plugins->add_hook("usercp_menu", "usercp_menu_misc", 30);
502 }
503
504 // Run the plugin hooks
505 $plugins->run_hooks("usercp_menu");
506 global $usercpmenu;
507
508 if($mybb->usergroup['canusercp'] == 1)
509 {
510 eval("\$ucp_nav_home = \"".$templates->get("usercp_nav_home")."\";");
511 }
512
513 eval("\$usercpnav = \"".$templates->get("usercp_nav")."\";");
514
515 $plugins->run_hooks("usercp_menu_built");
516}
517
518/**
519 * Constructs the usercp messenger menu.
520 *
521 */
522function usercp_menu_messenger()
523{
524 global $db, $mybb, $templates, $theme, $usercpmenu, $lang, $collapsed, $collapsedimg;
525
526 $usercp_nav_messenger = $templates->get("usercp_nav_messenger");
527 // Hide tracking link if no permission
528 $tracking = '';
529 if($mybb->usergroup['cantrackpms'])
530 {
531 $tracking = $templates->get("usercp_nav_messenger_tracking");
532 }
533 eval("\$ucp_nav_tracking = \"". $tracking ."\";");
534
535 // Hide compose link if no permission
536 $ucp_nav_compose = '';
537 if($mybb->usergroup['cansendpms'] == 1)
538 {
539 eval("\$ucp_nav_compose = \"".$templates->get("usercp_nav_messenger_compose")."\";");
540 }
541
542 $folderlinks = $folder_id = $folder_name = '';
543 $foldersexploded = explode("$%%$", $mybb->user['pmfolders']);
544 foreach($foldersexploded as $key => $folders)
545 {
546 $folderinfo = explode("**", $folders, 2);
547 $folderinfo[1] = get_pm_folder_name($folderinfo[0], $folderinfo[1]);
548 if($folderinfo[0] == 4)
549 {
550 $class = "usercp_nav_trash_pmfolder";
551 }
552 else if($folderlinks)
553 {
554 $class = "usercp_nav_sub_pmfolder";
555 }
556 else
557 {
558 $class = "usercp_nav_pmfolder";
559 }
560
561 $folder_id = $folderinfo[0];
562 $folder_name = $folderinfo[1];
563
564 eval("\$folderlinks .= \"".$templates->get("usercp_nav_messenger_folder")."\";");
565 }
566
567 if(!isset($collapsedimg['usercppms']))
568 {
569 $collapsedimg['usercppms'] = '';
570 }
571
572 if(!isset($collapsed['usercppms_e']))
573 {
574 $collapsed['usercppms_e'] = '';
575 }
576
577 eval("\$usercpmenu .= \"".$usercp_nav_messenger."\";");
578}
579
580/**
581 * Constructs the usercp profile menu.
582 *
583 */
584function usercp_menu_profile()
585{
586 global $db, $mybb, $templates, $theme, $usercpmenu, $lang, $collapsed, $collapsedimg;
587
588 $changenameop = '';
589 if($mybb->usergroup['canchangename'] != 0)
590 {
591 eval("\$changenameop = \"".$templates->get("usercp_nav_changename")."\";");
592 }
593
594 $changesigop = '';
595 if($mybb->usergroup['canusesig'] == 1 && ($mybb->usergroup['canusesigxposts'] == 0 || $mybb->usergroup['canusesigxposts'] > 0 && $mybb->user['postnum'] > $mybb->usergroup['canusesigxposts']))
596 {
597 if($mybb->user['suspendsignature'] == 0 || $mybb->user['suspendsignature'] == 1 && $mybb->user['suspendsigtime'] > 0 && $mybb->user['suspendsigtime'] < TIME_NOW)
598 {
599 eval("\$changesigop = \"".$templates->get("usercp_nav_editsignature")."\";");
600 }
601 }
602
603 if(!isset($collapsedimg['usercpprofile']))
604 {
605 $collapsedimg['usercpprofile'] = '';
606 }
607
608 if(!isset($collapsed['usercpprofile_e']))
609 {
610 $collapsed['usercpprofile_e'] = '';
611 }
612
613 eval("\$usercpmenu .= \"".$templates->get("usercp_nav_profile")."\";");
614}
615
616/**
617 * Constructs the usercp misc menu.
618 *
619 */
620function usercp_menu_misc()
621{
622 global $db, $mybb, $templates, $theme, $usercpmenu, $lang, $collapsed, $collapsedimg;
623
624 $draftstart = $draftend = '';
625 $draftcount = $lang->ucp_nav_drafts;
626
627 $query = $db->simple_select("posts", "COUNT(pid) AS draftcount", "visible = '-2' AND uid = '{$mybb->user['uid']}'");
628 $count = $db->fetch_field($query, 'draftcount');
629
630 if($count > 0)
631 {
632 $draftcount = $lang->sprintf($lang->ucp_nav_drafts_active, my_number_format($count));
633 }
634
635 if($mybb->settings['enableattachments'] != 0)
636 {
637 eval("\$attachmentop = \"".$templates->get("usercp_nav_attachments")."\";");
638 }
639
640 if(!isset($collapsedimg['usercpmisc']))
641 {
642 $collapsedimg['usercpmisc'] = '';
643 }
644
645 if(!isset($collapsed['usercpmisc_e']))
646 {
647 $collapsed['usercpmisc_e'] = '';
648 }
649
650 $profile_link = get_profile_link($mybb->user['uid']);
651 eval("\$usercpmenu .= \"".$templates->get("usercp_nav_misc")."\";");
652}
653
654/**
655 * Gets the usertitle for a specific uid.
656 *
657 * @param int $uid The uid of the user to get the usertitle of.
658 * @return string The usertitle of the user.
659 */
660function get_usertitle($uid=0)
661{
662 global $db, $mybb;
663
664 if($mybb->user['uid'] == $uid)
665 {
666 $user = $mybb->user;
667 }
668 else
669 {
670 $query = $db->simple_select("users", "usertitle,postnum", "uid='$uid'", array('limit' => 1));
671 $user = $db->fetch_array($query);
672 }
673
674 if($user['usertitle'])
675 {
676 return $user['usertitle'];
677 }
678 else
679 {
680 $usertitles = $mybb->cache->read('usertitles');
681 foreach($usertitles as $title)
682 {
683 if($title['posts'] <= $user['postnum'])
684 {
685 $usertitle = $title;
686 break;
687 }
688 }
689
690 return $usertitle['title'];
691 }
692}
693
694/**
695 * Updates a users private message count in the users table with the number of pms they have.
696 *
697 * @param int $uid The user id to update the count for. If none, assumes currently logged in user.
698 * @param int $count_to_update Bitwise value for what to update. 1 = total, 2 = new, 4 = unread. Combinations accepted.
699 * @return array The updated counters
700 */
701function update_pm_count($uid=0, $count_to_update=7)
702{
703 global $db, $mybb;
704
705 // If no user id, assume that we mean the current logged in user.
706 if((int)$uid == 0)
707 {
708 $uid = $mybb->user['uid'];
709 }
710
711 $uid = (int)$uid;
712 $pmcount = array();
713 if($uid == 0)
714 {
715 return $pmcount;
716 }
717
718 // Update total number of messages.
719 if($count_to_update & 1)
720 {
721 $query = $db->simple_select("privatemessages", "COUNT(pmid) AS pms_total", "uid='".$uid."'");
722 $total = $db->fetch_array($query);
723 $pmcount['totalpms'] = $total['pms_total'];
724 }
725
726 // Update number of unread messages.
727 if($count_to_update & 2 && $db->field_exists("unreadpms", "users") == true)
728 {
729 $query = $db->simple_select("privatemessages", "COUNT(pmid) AS pms_unread", "uid='".$uid."' AND status='0' AND folder='1'");
730 $unread = $db->fetch_array($query);
731 $pmcount['unreadpms'] = $unread['pms_unread'];
732 }
733
734 if(!empty($pmcount))
735 {
736 $db->update_query("users", $pmcount, "uid='".$uid."'");
737 }
738 return $pmcount;
739}
740
741/**
742 * Return the language specific name for a PM folder.
743 *
744 * @param int $fid The ID of the folder.
745 * @param string $name The folder name - can be blank, will use language default.
746 * @return string The name of the folder.
747 */
748function get_pm_folder_name($fid, $name="")
749{
750 global $lang;
751
752 if($name != '')
753 {
754 return $name;
755 }
756
757 switch($fid)
758 {
759 case 1:
760 return $lang->folder_inbox;
761 break;
762 case 2:
763 return $lang->folder_sent_items;
764 break;
765 case 3:
766 return $lang->folder_drafts;
767 break;
768 case 4:
769 return $lang->folder_trash;
770 break;
771 default:
772 return $lang->folder_untitled;
773 }
774}
775
776/**
777 * Generates a security question for registration.
778 *
779 * @param int $old_qid Optional ID of the old question.
780 * @return string The question session id.
781 */
782function generate_question($old_qid=0)
783{
784 global $db;
785
786 if($db->type == 'pgsql' || $db->type == 'sqlite')
787 {
788 $order_by = 'RANDOM()';
789 }
790 else
791 {
792 $order_by = 'RAND()';
793 }
794
795 if($old_qid)
796 {
797 $excl_old = ' AND qid != '.(int)$old_qid;
798 }
799
800 $query = $db->simple_select('questions', 'qid, shown', "active=1{$excl_old}", array('limit' => 1, 'order_by' => $order_by));
801 $question = $db->fetch_array($query);
802
803 if(!$db->num_rows($query))
804 {
805 // No active questions exist
806 return false;
807 }
808 else
809 {
810 $sessionid = random_str(32);
811
812 $sql_array = array(
813 "sid" => $sessionid,
814 "qid" => $question['qid'],
815 "dateline" => TIME_NOW
816 );
817 $db->insert_query("questionsessions", $sql_array);
818
819 $update_question = array(
820 "shown" => $question['shown'] + 1
821 );
822 $db->update_query("questions", $update_question, "qid = '{$question['qid']}'");
823
824 return $sessionid;
825 }
826}
827
828/**
829 * Check whether we can show the Purge Spammer Feature
830 *
831 * @param int $post_count The users post count
832 * @param int $usergroup The usergroup of our user
833 * @param int $uid The uid of our user
834 * @return boolean Whether or not to show the feature
835 */
836function purgespammer_show($post_count, $usergroup, $uid)
837{
838 global $mybb, $cache;
839
840 // only show this if the current user has permission to use it and the user has less than the post limit for using this tool
841 $bangroup = $mybb->settings['purgespammerbangroup'];
842 $usergroups = $cache->read('usergroups');
843
844 return ($mybb->user['uid'] != $uid && is_member($mybb->settings['purgespammergroups']) && !is_super_admin($uid)
845 && !$usergroups[$usergroup]['cancp'] && !$usergroups[$usergroup]['canmodcp'] && !$usergroups[$usergroup]['issupermod']
846 && (str_replace($mybb->settings['thousandssep'], '', $post_count) <= $mybb->settings['purgespammerpostlimit'] || $mybb->settings['purgespammerpostlimit'] == 0)
847 && !is_member($bangroup, $uid) && !$usergroups[$usergroup]['isbannedgroup']);
848}