- Automatically add users/groups to the PM recipient list, if entered or selected.
[phpbb.git] / phpBB / includes / functions_user.php
bloba39638bbf226ccc2e35aede6fdc078de21e829c7
1 <?php
2 /**
4 * @package phpBB3
5 * @version $Id$
6 * @copyright (c) 2005 phpBB Group
7 * @license http://opensource.org/licenses/gpl-license.php GNU Public License
9 */
11 /**
12 * @ignore
14 if (!defined('IN_PHPBB'))
16 exit;
19 /**
20 * Obtain user_ids from usernames or vice versa. Returns false on
21 * success else the error string
23 * @param array &$user_id_ary The user ids to check or empty if usernames used
24 * @param array &$username_ary The usernames to check or empty if user ids used
25 * @param mixed $user_type Array of user types to check, false if not restricting by user type
27 function user_get_id_name(&$user_id_ary, &$username_ary, $user_type = false)
29 global $db;
31 // Are both arrays already filled? Yep, return else
32 // are neither array filled?
33 if ($user_id_ary && $username_ary)
35 return false;
37 else if (!$user_id_ary && !$username_ary)
39 return 'NO_USERS';
42 $which_ary = ($user_id_ary) ? 'user_id_ary' : 'username_ary';
44 if ($$which_ary && !is_array($$which_ary))
46 $$which_ary = array($$which_ary);
49 $sql_in = ($which_ary == 'user_id_ary') ? array_map('intval', $$which_ary) : array_map('utf8_clean_string', $$which_ary);
50 unset($$which_ary);
52 $user_id_ary = $username_ary = array();
54 // Grab the user id/username records
55 $sql_where = ($which_ary == 'user_id_ary') ? 'user_id' : 'username_clean';
56 $sql = 'SELECT user_id, username
57 FROM ' . USERS_TABLE . '
58 WHERE ' . $db->sql_in_set($sql_where, $sql_in);
60 if ($user_type !== false && !empty($user_type))
62 $sql .= ' AND ' . $db->sql_in_set('user_type', $user_type);
65 $result = $db->sql_query($sql);
67 if (!($row = $db->sql_fetchrow($result)))
69 $db->sql_freeresult($result);
70 return 'NO_USERS';
75 $username_ary[$row['user_id']] = $row['username'];
76 $user_id_ary[] = $row['user_id'];
78 while ($row = $db->sql_fetchrow($result));
79 $db->sql_freeresult($result);
81 return false;
84 /**
85 * Get latest registered username and update database to reflect it
87 function update_last_username()
89 global $db;
91 // Get latest username
92 $sql = 'SELECT user_id, username, user_colour
93 FROM ' . USERS_TABLE . '
94 WHERE user_type IN (' . USER_NORMAL . ', ' . USER_FOUNDER . ')
95 ORDER BY user_id DESC';
96 $result = $db->sql_query_limit($sql, 1);
97 $row = $db->sql_fetchrow($result);
98 $db->sql_freeresult($result);
100 if ($row)
102 set_config('newest_user_id', $row['user_id'], true);
103 set_config('newest_username', $row['username'], true);
104 set_config('newest_user_colour', $row['user_colour'], true);
109 * Updates a username across all relevant tables/fields
111 * @param string $old_name the old/current username
112 * @param string $new_name the new username
114 function user_update_name($old_name, $new_name)
116 global $config, $db, $cache;
118 $update_ary = array(
119 FORUMS_TABLE => array('forum_last_poster_name'),
120 MODERATOR_CACHE_TABLE => array('username'),
121 POSTS_TABLE => array('post_username'),
122 TOPICS_TABLE => array('topic_first_poster_name', 'topic_last_poster_name'),
125 foreach ($update_ary as $table => $field_ary)
127 foreach ($field_ary as $field)
129 $sql = "UPDATE $table
130 SET $field = '" . $db->sql_escape($new_name) . "'
131 WHERE $field = '" . $db->sql_escape($old_name) . "'";
132 $db->sql_query($sql);
136 if ($config['newest_username'] == $old_name)
138 set_config('newest_username', $new_name, true);
141 // Because some tables/caches use username-specific data we need to purge this here.
142 $cache->destroy('sql', MODERATOR_CACHE_TABLE);
146 * Adds a user
148 * @param mixed $user_row An array containing the following keys (and the appropriate values): username, group_id (the group to place the user in), user_email and the user_type(usually 0). Additional entries not overridden by defaults will be forwarded.
149 * @param string $cp_data custom profile fields, see custom_profile::build_insert_sql_array
150 * @return the new user's ID.
152 function user_add($user_row, $cp_data = false)
154 global $db, $user, $auth, $config;
156 if (empty($user_row['username']) || !isset($user_row['group_id']) || !isset($user_row['user_email']) || !isset($user_row['user_type']))
158 return false;
161 $username_clean = utf8_clean_string($user_row['username']);
163 if (empty($username_clean))
165 return false;
168 $sql_ary = array(
169 'username' => $user_row['username'],
170 'username_clean' => $username_clean,
171 'user_password' => (isset($user_row['user_password'])) ? $user_row['user_password'] : '',
172 'user_pass_convert' => 0,
173 'user_email' => strtolower($user_row['user_email']),
174 'user_email_hash' => hexdec(crc32(strtolower($user_row['user_email'])) . strlen($user_row['user_email'])),
175 'group_id' => $user_row['group_id'],
176 'user_type' => $user_row['user_type'],
179 // These are the additional vars able to be specified
180 $additional_vars = array(
181 'user_permissions' => '',
182 'user_timezone' => $config['board_timezone'],
183 'user_dateformat' => $config['default_dateformat'],
184 'user_lang' => $config['default_lang'],
185 'user_style' => $config['default_style'],
186 'user_actkey' => '',
187 'user_ip' => '',
188 'user_regdate' => time(),
189 'user_passchg' => time(),
190 'user_options' => 895,
192 'user_inactive_reason' => 0,
193 'user_inactive_time' => 0,
194 'user_lastmark' => time(),
195 'user_lastvisit' => 0,
196 'user_lastpost_time' => 0,
197 'user_lastpage' => '',
198 'user_posts' => 0,
199 'user_dst' => (int) $config['board_dst'],
200 'user_colour' => '',
201 'user_occ' => '',
202 'user_interests' => '',
203 'user_avatar' => '',
204 'user_avatar_type' => 0,
205 'user_avatar_width' => 0,
206 'user_avatar_height' => 0,
207 'user_new_privmsg' => 0,
208 'user_unread_privmsg' => 0,
209 'user_last_privmsg' => 0,
210 'user_message_rules' => 0,
211 'user_full_folder' => PRIVMSGS_NO_BOX,
212 'user_emailtime' => 0,
214 'user_notify' => 0,
215 'user_notify_pm' => 1,
216 'user_notify_type' => NOTIFY_EMAIL,
217 'user_allow_pm' => 1,
218 'user_allow_viewonline' => 1,
219 'user_allow_viewemail' => 1,
220 'user_allow_massemail' => 1,
222 'user_sig' => '',
223 'user_sig_bbcode_uid' => '',
224 'user_sig_bbcode_bitfield' => '',
226 'user_form_salt' => unique_id(),
229 // Now fill the sql array with not required variables
230 foreach ($additional_vars as $key => $default_value)
232 $sql_ary[$key] = (isset($user_row[$key])) ? $user_row[$key] : $default_value;
235 // Any additional variables in $user_row not covered above?
236 $remaining_vars = array_diff(array_keys($user_row), array_keys($sql_ary));
238 // Now fill our sql array with the remaining vars
239 if (sizeof($remaining_vars))
241 foreach ($remaining_vars as $key)
243 $sql_ary[$key] = $user_row[$key];
247 $sql = 'INSERT INTO ' . USERS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary);
248 $db->sql_query($sql);
250 $user_id = $db->sql_nextid();
252 // Insert Custom Profile Fields
253 if ($cp_data !== false && sizeof($cp_data))
255 $cp_data['user_id'] = (int) $user_id;
257 if (!class_exists('custom_profile'))
259 include_once(PHPBB_ROOT_PATH . 'includes/functions_profile_fields.' . PHP_EXT);
262 $sql = 'INSERT INTO ' . PROFILE_FIELDS_DATA_TABLE . ' ' .
263 $db->sql_build_array('INSERT', custom_profile::build_insert_sql_array($cp_data));
264 $db->sql_query($sql);
267 // Place into appropriate group...
268 $sql = 'INSERT INTO ' . USER_GROUP_TABLE . ' ' . $db->sql_build_array('INSERT', array(
269 'user_id' => (int) $user_id,
270 'group_id' => (int) $user_row['group_id'],
271 'user_pending' => 0)
273 $db->sql_query($sql);
275 // Now make it the users default group...
276 group_set_user_default($user_row['group_id'], array($user_id), false);
278 // set the newest user and adjust the user count if the user is a normal user and no activation mail is sent
279 if ($user_row['user_type'] == USER_NORMAL)
281 set_config('newest_user_id', $user_id, true);
282 set_config('newest_username', $user_row['username'], true);
283 set_config('num_users', $config['num_users'] + 1, true);
285 $sql = 'SELECT group_colour
286 FROM ' . GROUPS_TABLE . '
287 WHERE group_id = ' . (int) $user_row['group_id'];
288 $result = $db->sql_query_limit($sql, 1);
289 $row = $db->sql_fetchrow($result);
290 $db->sql_freeresult($result);
292 set_config('newest_user_colour', $row['group_colour'], true);
295 return $user_id;
299 * Remove User
301 function user_delete($mode, $user_id, $post_username = false)
303 global $cache, $config, $db, $user, $auth;
305 $sql = 'SELECT *
306 FROM ' . USERS_TABLE . '
307 WHERE user_id = ' . $user_id;
308 $result = $db->sql_query($sql);
309 $user_row = $db->sql_fetchrow($result);
310 $db->sql_freeresult($result);
312 if (!$user_row)
314 return false;
317 // Before we begin, we will remove the reports the user issued.
318 $sql = 'SELECT r.post_id, p.topic_id
319 FROM ' . REPORTS_TABLE . ' r, ' . POSTS_TABLE . ' p
320 WHERE r.user_id = ' . $user_id . '
321 AND p.post_id = r.post_id';
322 $result = $db->sql_query($sql);
324 $report_posts = $report_topics = array();
325 while ($row = $db->sql_fetchrow($result))
327 $report_posts[] = $row['post_id'];
328 $report_topics[] = $row['topic_id'];
330 $db->sql_freeresult($result);
332 if (sizeof($report_posts))
334 $report_posts = array_unique($report_posts);
335 $report_topics = array_unique($report_topics);
337 // Get a list of topics that still contain reported posts
338 $sql = 'SELECT DISTINCT topic_id
339 FROM ' . POSTS_TABLE . '
340 WHERE ' . $db->sql_in_set('topic_id', $report_topics) . '
341 AND post_reported = 1
342 AND ' . $db->sql_in_set('post_id', $report_posts, true);
343 $result = $db->sql_query($sql);
345 $keep_report_topics = array();
346 while ($row = $db->sql_fetchrow($result))
348 $keep_report_topics[] = $row['topic_id'];
350 $db->sql_freeresult($result);
352 if (sizeof($keep_report_topics))
354 $report_topics = array_diff($report_topics, $keep_report_topics);
356 unset($keep_report_topics);
358 // Now set the flags back
359 $sql = 'UPDATE ' . POSTS_TABLE . '
360 SET post_reported = 0
361 WHERE ' . $db->sql_in_set('post_id', $report_posts);
362 $db->sql_query($sql);
364 if (sizeof($report_topics))
366 $sql = 'UPDATE ' . TOPICS_TABLE . '
367 SET topic_reported = 0
368 WHERE ' . $db->sql_in_set('topic_id', $report_topics);
369 $db->sql_query($sql);
373 // Remove reports
374 $db->sql_query('DELETE FROM ' . REPORTS_TABLE . ' WHERE user_id = ' . $user_id);
376 if ($user_row['user_avatar'] && $user_row['user_avatar_type'] == AVATAR_UPLOAD)
378 avatar_delete('user', $user_row);
381 switch ($mode)
383 case 'retain':
385 $db->sql_transaction('begin');
387 if ($post_username === false)
389 $post_username = $user->lang['GUEST'];
392 // If the user is inactive and newly registered we assume no posts from this user being there...
393 if ($user_row['user_type'] == USER_INACTIVE && $user_row['user_inactive_reason'] == INACTIVE_REGISTER && !$user_row['user_posts'])
396 else
398 $sql = 'UPDATE ' . FORUMS_TABLE . '
399 SET forum_last_poster_id = ' . ANONYMOUS . ", forum_last_poster_name = '" . $db->sql_escape($post_username) . "', forum_last_poster_colour = ''
400 WHERE forum_last_poster_id = $user_id";
401 $db->sql_query($sql);
403 $sql = 'UPDATE ' . POSTS_TABLE . '
404 SET poster_id = ' . ANONYMOUS . ", post_username = '" . $db->sql_escape($post_username) . "'
405 WHERE poster_id = $user_id";
406 $db->sql_query($sql);
408 $sql = 'UPDATE ' . POSTS_TABLE . '
409 SET post_edit_user = ' . ANONYMOUS . "
410 WHERE post_edit_user = $user_id";
411 $db->sql_query($sql);
413 $sql = 'UPDATE ' . TOPICS_TABLE . '
414 SET topic_poster = ' . ANONYMOUS . ", topic_first_poster_name = '" . $db->sql_escape($post_username) . "', topic_first_poster_colour = ''
415 WHERE topic_poster = $user_id";
416 $db->sql_query($sql);
418 $sql = 'UPDATE ' . TOPICS_TABLE . '
419 SET topic_last_poster_id = ' . ANONYMOUS . ", topic_last_poster_name = '" . $db->sql_escape($post_username) . "', topic_last_poster_colour = ''
420 WHERE topic_last_poster_id = $user_id";
421 $db->sql_query($sql);
423 // Since we change every post by this author, we need to count this amount towards the anonymous user
425 // Update the post count for the anonymous user
426 if ($user_row['user_posts'])
428 $sql = 'UPDATE ' . USERS_TABLE . '
429 SET user_posts = user_posts + ' . $user_row['user_posts'] . '
430 WHERE user_id = ' . ANONYMOUS;
431 $db->sql_query($sql);
435 $db->sql_transaction('commit');
437 break;
439 case 'remove':
441 if (!function_exists('delete_posts'))
443 include(PHPBB_ROOT_PATH . 'includes/functions_admin.' . PHP_EXT);
446 $sql = 'SELECT topic_id, COUNT(post_id) AS total_posts
447 FROM ' . POSTS_TABLE . "
448 WHERE poster_id = $user_id
449 GROUP BY topic_id";
450 $result = $db->sql_query($sql);
452 $topic_id_ary = array();
453 while ($row = $db->sql_fetchrow($result))
455 $topic_id_ary[$row['topic_id']] = $row['total_posts'];
457 $db->sql_freeresult($result);
459 if (sizeof($topic_id_ary))
461 $sql = 'SELECT topic_id, topic_replies, topic_replies_real
462 FROM ' . TOPICS_TABLE . '
463 WHERE ' . $db->sql_in_set('topic_id', array_keys($topic_id_ary));
464 $result = $db->sql_query($sql);
466 $del_topic_ary = array();
467 while ($row = $db->sql_fetchrow($result))
469 if (max($row['topic_replies'], $row['topic_replies_real']) + 1 == $topic_id_ary[$row['topic_id']])
471 $del_topic_ary[] = $row['topic_id'];
474 $db->sql_freeresult($result);
476 if (sizeof($del_topic_ary))
478 $sql = 'DELETE FROM ' . TOPICS_TABLE . '
479 WHERE ' . $db->sql_in_set('topic_id', $del_topic_ary);
480 $db->sql_query($sql);
484 // Delete posts, attachments, etc.
485 delete_posts('poster_id', $user_id);
487 break;
490 $db->sql_transaction('begin');
492 $table_ary = array(USERS_TABLE, USER_GROUP_TABLE, TOPICS_WATCH_TABLE, FORUMS_WATCH_TABLE, ACL_USERS_TABLE, TOPICS_TRACK_TABLE, TOPICS_POSTED_TABLE, FORUMS_TRACK_TABLE, PROFILE_FIELDS_DATA_TABLE, MODERATOR_CACHE_TABLE, DRAFTS_TABLE, BOOKMARKS_TABLE);
494 foreach ($table_ary as $table)
496 $sql = "DELETE FROM $table
497 WHERE user_id = $user_id";
498 $db->sql_query($sql);
501 $cache->destroy('sql', MODERATOR_CACHE_TABLE);
503 // Remove any undelivered mails...
504 $sql = 'SELECT msg_id, user_id
505 FROM ' . PRIVMSGS_TO_TABLE . '
506 WHERE author_id = ' . $user_id . '
507 AND folder_id = ' . PRIVMSGS_NO_BOX;
508 $result = $db->sql_query($sql);
510 $undelivered_msg = $undelivered_user = array();
511 while ($row = $db->sql_fetchrow($result))
513 $undelivered_msg[] = $row['msg_id'];
514 $undelivered_user[$row['user_id']][] = true;
516 $db->sql_freeresult($result);
518 if (sizeof($undelivered_msg))
520 $sql = 'DELETE FROM ' . PRIVMSGS_TABLE . '
521 WHERE ' . $db->sql_in_set('msg_id', $undelivered_msg);
522 $db->sql_query($sql);
525 $sql = 'DELETE FROM ' . PRIVMSGS_TO_TABLE . '
526 WHERE author_id = ' . $user_id . '
527 AND folder_id = ' . PRIVMSGS_NO_BOX;
528 $db->sql_query($sql);
530 // Delete all to-information
531 $sql = 'DELETE FROM ' . PRIVMSGS_TO_TABLE . '
532 WHERE user_id = ' . $user_id;
533 $db->sql_query($sql);
535 // Set the remaining author id to anonymous - this way users are still able to read messages from users being removed
536 $sql = 'UPDATE ' . PRIVMSGS_TO_TABLE . '
537 SET author_id = ' . ANONYMOUS . '
538 WHERE author_id = ' . $user_id;
539 $db->sql_query($sql);
541 $sql = 'UPDATE ' . PRIVMSGS_TABLE . '
542 SET author_id = ' . ANONYMOUS . '
543 WHERE author_id = ' . $user_id;
544 $db->sql_query($sql);
546 foreach ($undelivered_user as $_user_id => $ary)
548 if ($_user_id == $user_id)
550 continue;
553 $sql = 'UPDATE ' . USERS_TABLE . '
554 SET user_new_privmsg = user_new_privmsg - ' . sizeof($ary) . ',
555 user_unread_privmsg = user_unread_privmsg - ' . sizeof($ary) . '
556 WHERE user_id = ' . $_user_id;
557 $db->sql_query($sql);
560 $db->sql_transaction('commit');
562 // Reset newest user info if appropriate
563 if ($config['newest_user_id'] == $user_id)
565 update_last_username();
568 // Decrement number of users if this user is active
569 if ($user_row['user_type'] != USER_INACTIVE && $user_row['user_type'] != USER_IGNORE)
571 set_config('num_users', $config['num_users'] - 1, true);
574 return false;
578 * Flips user_type from active to inactive and vice versa, handles group membership updates
580 * @param string $mode can be flip for flipping from active/inactive, activate or deactivate
582 function user_active_flip($mode, $user_id_ary, $reason = INACTIVE_MANUAL)
584 global $config, $db, $user, $auth;
586 $deactivated = $activated = 0;
587 $sql_statements = array();
589 if (!is_array($user_id_ary))
591 $user_id_ary = array($user_id_ary);
594 if (!sizeof($user_id_ary))
596 return;
599 $sql = 'SELECT user_id, group_id, user_type, user_inactive_reason
600 FROM ' . USERS_TABLE . '
601 WHERE ' . $db->sql_in_set('user_id', $user_id_ary);
602 $result = $db->sql_query($sql);
604 while ($row = $db->sql_fetchrow($result))
606 $sql_ary = array();
608 if ($row['user_type'] == USER_IGNORE || $row['user_type'] == USER_FOUNDER ||
609 ($mode == 'activate' && $row['user_type'] != USER_INACTIVE) ||
610 ($mode == 'deactivate' && $row['user_type'] == USER_INACTIVE))
612 continue;
615 if ($row['user_type'] == USER_INACTIVE)
617 $activated++;
619 else
621 $deactivated++;
623 // Remove the users session key...
624 $user->reset_login_keys($row['user_id']);
627 $sql_ary += array(
628 'user_type' => ($row['user_type'] == USER_NORMAL) ? USER_INACTIVE : USER_NORMAL,
629 'user_inactive_time' => ($row['user_type'] == USER_NORMAL) ? time() : 0,
630 'user_inactive_reason' => ($row['user_type'] == USER_NORMAL) ? $reason : 0,
633 $sql_statements[$row['user_id']] = $sql_ary;
635 $db->sql_freeresult($result);
637 if (sizeof($sql_statements))
639 foreach ($sql_statements as $user_id => $sql_ary)
641 $sql = 'UPDATE ' . USERS_TABLE . '
642 SET ' . $db->sql_build_array('UPDATE', $sql_ary) . '
643 WHERE user_id = ' . $user_id;
644 $db->sql_query($sql);
647 $auth->acl_clear_prefetch(array_keys($sql_statements));
650 if ($deactivated)
652 set_config('num_users', $config['num_users'] - $deactivated, true);
655 if ($activated)
657 set_config('num_users', $config['num_users'] + $activated, true);
660 // Update latest username
661 update_last_username();
665 * Add a ban or ban exclusion to the banlist. Bans either a user, an IP or an email address
667 * @param string $mode Type of ban. One of the following: user, ip, email
668 * @param mixed $ban Banned entity. Either string or array with usernames, ips or email addresses
669 * @param int $ban_len Ban length in minutes
670 * @param string $ban_len_other Ban length as a date (YYYY-MM-DD)
671 * @param boolean $ban_exclude Exclude these entities from banning?
672 * @param string $ban_reason String describing the reason for this ban
673 * @return boolean
675 function user_ban($mode, $ban, $ban_len, $ban_len_other, $ban_exclude, $ban_reason, $ban_give_reason = '')
677 global $db, $user, $auth, $cache;
679 // Delete stale bans
680 $sql = 'DELETE FROM ' . BANLIST_TABLE . '
681 WHERE ban_end < ' . time() . '
682 AND ban_end <> 0';
683 $db->sql_query($sql);
685 $ban_list = (!is_array($ban)) ? array_unique(explode("\n", $ban)) : $ban;
686 $ban_list_log = implode(', ', $ban_list);
688 $current_time = time();
690 // Set $ban_end to the unix time when the ban should end. 0 is a permanent ban.
691 if ($ban_len)
693 if ($ban_len != -1 || !$ban_len_other)
695 $ban_end = max($current_time, $current_time + ($ban_len) * 60);
697 else
699 $ban_other = explode('-', $ban_len_other);
700 if (sizeof($ban_other) == 3 && ((int)$ban_other[0] < 9999) &&
701 (strlen($ban_other[0]) == 4) && (strlen($ban_other[1]) == 2) && (strlen($ban_other[2]) == 2))
703 $ban_end = max($current_time, gmmktime(0, 0, 0, (int)$ban_other[1], (int)$ban_other[2], (int)$ban_other[0]));
705 else
707 trigger_error('LENGTH_BAN_INVALID');
711 else
713 $ban_end = 0;
716 $founder = $founder_names = array();
718 if (!$ban_exclude)
720 // Create a list of founder...
721 $sql = 'SELECT user_id, user_email, username_clean
722 FROM ' . USERS_TABLE . '
723 WHERE user_type = ' . USER_FOUNDER;
724 $result = $db->sql_query($sql);
726 while ($row = $db->sql_fetchrow($result))
728 $founder[$row['user_id']] = $row['user_email'];
729 $founder_names[$row['user_id']] = $row['username_clean'];
731 $db->sql_freeresult($result);
734 $banlist_ary = array();
736 switch ($mode)
738 case 'user':
739 $type = 'ban_userid';
741 // At the moment we do not support wildcard username banning
743 // Select the relevant user_ids.
744 $sql_usernames = array();
746 foreach ($ban_list as $username)
748 $username = trim($username);
749 if ($username != '')
751 $clean_name = utf8_clean_string($username);
752 if ($clean_name == $user->data['username_clean'])
754 trigger_error('CANNOT_BAN_YOURSELF', E_USER_WARNING);
756 if (in_array($clean_name, $founder_names))
758 trigger_error('CANNOT_BAN_FOUNDER', E_USER_WARNING);
760 $sql_usernames[] = $clean_name;
764 // Make sure we have been given someone to ban
765 if (!sizeof($sql_usernames))
767 trigger_error('NO_USER_SPECIFIED');
770 $sql = 'SELECT user_id
771 FROM ' . USERS_TABLE . '
772 WHERE ' . $db->sql_in_set('username_clean', $sql_usernames);
774 // Do not allow banning yourself
775 if (sizeof($founder))
777 $sql .= ' AND ' . $db->sql_in_set('user_id', array_merge(array_keys($founder), array($user->data['user_id'])), true);
779 else
781 $sql .= ' AND user_id <> ' . $user->data['user_id'];
784 $result = $db->sql_query($sql);
786 if ($row = $db->sql_fetchrow($result))
790 $banlist_ary[] = (int) $row['user_id'];
792 while ($row = $db->sql_fetchrow($result));
794 else
796 $db->sql_freeresult($result);
797 trigger_error('NO_USERS');
799 $db->sql_freeresult($result);
800 break;
802 case 'ip':
803 $type = 'ban_ip';
805 foreach ($ban_list as $ban_item)
807 if (preg_match('#^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})[ ]*\-[ ]*([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$#', trim($ban_item), $ip_range_explode))
809 // This is an IP range
810 // Don't ask about all this, just don't ask ... !
811 $ip_1_counter = $ip_range_explode[1];
812 $ip_1_end = $ip_range_explode[5];
814 while ($ip_1_counter <= $ip_1_end)
816 $ip_2_counter = ($ip_1_counter == $ip_range_explode[1]) ? $ip_range_explode[2] : 0;
817 $ip_2_end = ($ip_1_counter < $ip_1_end) ? 254 : $ip_range_explode[6];
819 if ($ip_2_counter == 0 && $ip_2_end == 254)
821 $ip_2_counter = 256;
822 $ip_2_fragment = 256;
824 $banlist_ary[] = "$ip_1_counter.*";
827 while ($ip_2_counter <= $ip_2_end)
829 $ip_3_counter = ($ip_2_counter == $ip_range_explode[2] && $ip_1_counter == $ip_range_explode[1]) ? $ip_range_explode[3] : 0;
830 $ip_3_end = ($ip_2_counter < $ip_2_end || $ip_1_counter < $ip_1_end) ? 254 : $ip_range_explode[7];
832 if ($ip_3_counter == 0 && $ip_3_end == 254)
834 $ip_3_counter = 256;
835 $ip_3_fragment = 256;
837 $banlist_ary[] = "$ip_1_counter.$ip_2_counter.*";
840 while ($ip_3_counter <= $ip_3_end)
842 $ip_4_counter = ($ip_3_counter == $ip_range_explode[3] && $ip_2_counter == $ip_range_explode[2] && $ip_1_counter == $ip_range_explode[1]) ? $ip_range_explode[4] : 0;
843 $ip_4_end = ($ip_3_counter < $ip_3_end || $ip_2_counter < $ip_2_end) ? 254 : $ip_range_explode[8];
845 if ($ip_4_counter == 0 && $ip_4_end == 254)
847 $ip_4_counter = 256;
848 $ip_4_fragment = 256;
850 $banlist_ary[] = "$ip_1_counter.$ip_2_counter.$ip_3_counter.*";
853 while ($ip_4_counter <= $ip_4_end)
855 $banlist_ary[] = "$ip_1_counter.$ip_2_counter.$ip_3_counter.$ip_4_counter";
856 $ip_4_counter++;
858 $ip_3_counter++;
860 $ip_2_counter++;
862 $ip_1_counter++;
865 else if (preg_match('#^([0-9]{1,3})\.([0-9\*]{1,3})\.([0-9\*]{1,3})\.([0-9\*]{1,3})$#', trim($ban_item)) || preg_match('#^[a-f0-9:]+\*?$#i', trim($ban_item)))
867 // Normal IP address
868 $banlist_ary[] = trim($ban_item);
870 else if (preg_match('#^\*$#', trim($ban_item)))
872 // Ban all IPs
873 $banlist_ary[] = '*';
875 else if (preg_match('#^([\w\-_]\.?){2,}$#is', trim($ban_item)))
877 // hostname
878 $ip_ary = gethostbynamel(trim($ban_item));
880 if (!empty($ip_ary))
882 foreach ($ip_ary as $ip)
884 if ($ip)
886 if (strlen($ip) > 40)
888 continue;
891 $banlist_ary[] = $ip;
896 else
898 trigger_error('NO_IPS_DEFINED');
901 break;
903 case 'email':
904 $type = 'ban_email';
906 foreach ($ban_list as $ban_item)
908 $ban_item = trim($ban_item);
910 if (preg_match('#^.*?@*|(([a-z0-9\-]+\.)+([a-z]{2,3}))$#i', $ban_item))
912 if (strlen($ban_item) > 100)
914 continue;
917 if (!sizeof($founder) || !in_array($ban_item, $founder))
919 $banlist_ary[] = $ban_item;
924 if (sizeof($ban_list) == 0)
926 trigger_error('NO_EMAILS_DEFINED');
928 break;
930 default:
931 trigger_error('NO_MODE');
932 break;
935 // Fetch currently set bans of the specified type and exclude state. Prevent duplicate bans.
936 $sql_where = ($type == 'ban_userid') ? 'ban_userid <> 0' : "$type <> ''";
938 $sql = "SELECT $type
939 FROM " . BANLIST_TABLE . "
940 WHERE $sql_where
941 AND ban_exclude = $ban_exclude";
942 $result = $db->sql_query($sql);
944 // Reset $sql_where, because we use it later...
945 $sql_where = '';
947 if ($row = $db->sql_fetchrow($result))
949 $banlist_ary_tmp = array();
952 switch ($mode)
954 case 'user':
955 $banlist_ary_tmp[] = $row['ban_userid'];
956 break;
958 case 'ip':
959 $banlist_ary_tmp[] = $row['ban_ip'];
960 break;
962 case 'email':
963 $banlist_ary_tmp[] = $row['ban_email'];
964 break;
967 while ($row = $db->sql_fetchrow($result));
969 $banlist_ary = array_unique(array_diff($banlist_ary, $banlist_ary_tmp));
970 unset($banlist_ary_tmp);
972 $db->sql_freeresult($result);
974 // We have some entities to ban
975 if (sizeof($banlist_ary))
977 $sql_ary = array();
979 foreach ($banlist_ary as $ban_entry)
981 $sql_ary[] = array(
982 $type => $ban_entry,
983 'ban_start' => (int) $current_time,
984 'ban_end' => (int) $ban_end,
985 'ban_exclude' => (int) $ban_exclude,
986 'ban_reason' => (string) $ban_reason,
987 'ban_give_reason' => (string) $ban_give_reason,
991 $db->sql_multi_insert(BANLIST_TABLE, $sql_ary);
993 // If we are banning we want to logout anyone matching the ban
994 if (!$ban_exclude)
996 switch ($mode)
998 case 'user':
999 $sql_where = 'WHERE ' . $db->sql_in_set('session_user_id', $banlist_ary);
1000 break;
1002 case 'ip':
1003 $sql_where = 'WHERE ' . $db->sql_in_set('session_ip', $banlist_ary);
1004 break;
1006 case 'email':
1007 $banlist_ary_sql = array();
1009 foreach ($banlist_ary as $ban_entry)
1011 $banlist_ary_sql[] = (string) str_replace('*', '%', $ban_entry);
1014 $sql = 'SELECT user_id
1015 FROM ' . USERS_TABLE . '
1016 WHERE ' . $db->sql_in_set('user_email', $banlist_ary_sql);
1017 $result = $db->sql_query($sql);
1019 $sql_in = array();
1021 if ($row = $db->sql_fetchrow($result))
1025 $sql_in[] = $row['user_id'];
1027 while ($row = $db->sql_fetchrow($result));
1029 $sql_where = 'WHERE ' . $db->sql_in_set('session_user_id', $sql_in);
1031 $db->sql_freeresult($result);
1032 break;
1035 if (isset($sql_where) && $sql_where)
1037 $sql = 'DELETE FROM ' . SESSIONS_TABLE . "
1038 $sql_where";
1039 $db->sql_query($sql);
1041 if ($mode == 'user')
1043 $sql = 'DELETE FROM ' . SESSIONS_KEYS_TABLE . ' ' . ((in_array('*', $banlist_ary)) ? '' : 'WHERE ' . $db->sql_in_set('user_id', $banlist_ary));
1044 $db->sql_query($sql);
1049 // Update log
1050 $log_entry = ($ban_exclude) ? 'LOG_BAN_EXCLUDE_' : 'LOG_BAN_';
1052 // Add to moderator and admin log
1053 add_log('admin', $log_entry . strtoupper($mode), $ban_reason, $ban_list_log);
1054 add_log('mod', 0, 0, $log_entry . strtoupper($mode), $ban_reason, $ban_list_log);
1056 $cache->destroy('sql', BANLIST_TABLE);
1058 return true;
1061 // There was nothing to ban/exclude. But destroying the cache because of the removal of stale bans.
1062 $cache->destroy('sql', BANLIST_TABLE);
1064 return false;
1068 * Unban User
1070 function user_unban($mode, $ban)
1072 global $db, $user, $auth, $cache;
1074 // Delete stale bans
1075 $sql = 'DELETE FROM ' . BANLIST_TABLE . '
1076 WHERE ban_end < ' . time() . '
1077 AND ban_end <> 0';
1078 $db->sql_query($sql);
1080 if (!is_array($ban))
1082 $ban = array($ban);
1085 $unban_sql = array_map('intval', $ban);
1087 if (sizeof($unban_sql))
1089 // Grab details of bans for logging information later
1090 switch ($mode)
1092 case 'user':
1093 $sql = 'SELECT u.username AS unban_info
1094 FROM ' . USERS_TABLE . ' u, ' . BANLIST_TABLE . ' b
1095 WHERE ' . $db->sql_in_set('b.ban_id', $unban_sql) . '
1096 AND u.user_id = b.ban_userid';
1097 break;
1099 case 'email':
1100 $sql = 'SELECT ban_email AS unban_info
1101 FROM ' . BANLIST_TABLE . '
1102 WHERE ' . $db->sql_in_set('ban_id', $unban_sql);
1103 break;
1105 case 'ip':
1106 $sql = 'SELECT ban_ip AS unban_info
1107 FROM ' . BANLIST_TABLE . '
1108 WHERE ' . $db->sql_in_set('ban_id', $unban_sql);
1109 break;
1111 $result = $db->sql_query($sql);
1113 $l_unban_list = '';
1114 while ($row = $db->sql_fetchrow($result))
1116 $l_unban_list .= (($l_unban_list != '') ? ', ' : '') . $row['unban_info'];
1118 $db->sql_freeresult($result);
1120 $sql = 'DELETE FROM ' . BANLIST_TABLE . '
1121 WHERE ' . $db->sql_in_set('ban_id', $unban_sql);
1122 $db->sql_query($sql);
1124 // Add to moderator and admin log
1125 add_log('admin', 'LOG_UNBAN_' . strtoupper($mode), $l_unban_list);
1126 add_log('mod', 0, 0, 'LOG_UNBAN_' . strtoupper($mode), $l_unban_list);
1129 $cache->destroy('sql', BANLIST_TABLE);
1131 return false;
1135 * Whois facility
1137 function user_ipwhois($ip)
1139 $ipwhois = '';
1141 // Check IP
1142 // Only supporting IPv4 at the moment...
1143 if (empty($ip) || !preg_match(get_preg_expression('ipv4'), $ip))
1145 return '';
1148 $match = array(
1149 '#RIPE\.NET#is' => 'whois.ripe.net',
1150 '#whois\.apnic\.net#is' => 'whois.apnic.net',
1151 '#nic\.ad\.jp#is' => 'whois.nic.ad.jp',
1152 '#whois\.registro\.br#is' => 'whois.registro.br'
1155 if (($fsk = @fsockopen('whois.arin.net', 43)))
1157 fputs($fsk, "$ip\n");
1158 while (!feof($fsk))
1160 $ipwhois .= fgets($fsk, 1024);
1162 @fclose($fsk);
1165 foreach (array_keys($match) as $server)
1167 if (preg_match($server, $ipwhois))
1169 $ipwhois = '';
1170 if (($fsk = @fsockopen($match[$server], 43)))
1172 fputs($fsk, "$ip\n");
1173 while (!feof($fsk))
1175 $ipwhois .= fgets($fsk, 1024);
1177 @fclose($fsk);
1179 break;
1183 $ipwhois = htmlspecialchars($ipwhois);
1185 // Magic URL ;)
1186 return trim(make_clickable($ipwhois, false, ''));
1190 * Data validation ... used primarily but not exclusively by ucp modules
1192 * "Master" function for validating a range of data types
1194 function validate_data($data, $val_ary)
1196 global $user;
1198 $error = array();
1200 foreach ($val_ary as $var => $val_seq)
1202 if (!is_array($val_seq[0]))
1204 $val_seq = array($val_seq);
1207 foreach ($val_seq as $validate)
1209 $function = array_shift($validate);
1210 array_unshift($validate, $data[$var]);
1212 if ($result = call_user_func_array('validate_' . $function, $validate))
1214 // Since errors are checked later for their language file existence, we need to make sure custom errors are not adjusted.
1215 $error[] = (empty($user->lang[$result . '_' . strtoupper($var)])) ? $result : $result . '_' . strtoupper($var);
1220 return $error;
1224 * Validate String
1226 * @return boolean|string Either false if validation succeeded or a string which will be used as the error message (with the variable name appended)
1228 function validate_string($string, $optional = false, $min = 0, $max = 0)
1230 if (empty($string) && $optional)
1232 return false;
1235 if ($min && utf8_strlen(htmlspecialchars_decode($string)) < $min)
1237 return 'TOO_SHORT';
1239 else if ($max && utf8_strlen(htmlspecialchars_decode($string)) > $max)
1241 return 'TOO_LONG';
1244 return false;
1248 * Validate Number
1250 * @return boolean|string Either false if validation succeeded or a string which will be used as the error message (with the variable name appended)
1252 function validate_num($num, $optional = false, $min = 0, $max = 1E99)
1254 if (empty($num) && $optional)
1256 return false;
1259 if ($num < $min)
1261 return 'TOO_SMALL';
1263 else if ($num > $max)
1265 return 'TOO_LARGE';
1268 return false;
1272 * Validate Date
1273 * @param String $string a date in the dd-mm-yyyy format
1274 * @return boolean
1276 function validate_date($date_string, $optional = false)
1278 $date = explode('-', $date_string);
1279 if ((empty($date) || sizeof($date) != 3) && $optional)
1281 return false;
1283 else if ($optional)
1285 for ($field = 0; $field <= 1; $field++)
1287 $date[$field] = (int) $date[$field];
1288 if (empty($date[$field]))
1290 $date[$field] = 1;
1293 $date[2] = (int) $date[2];
1294 // assume an arbitrary leap year
1295 if (empty($date[2]))
1297 $date[2] = 1980;
1301 if (sizeof($date) != 3 || !checkdate($date[1], $date[0], $date[2]))
1303 return 'INVALID';
1306 return false;
1311 * Validate Match
1313 * @return boolean|string Either false if validation succeeded or a string which will be used as the error message (with the variable name appended)
1315 function validate_match($string, $optional = false, $match = '')
1317 if (empty($string) && $optional)
1319 return false;
1322 if (empty($match))
1324 return false;
1327 if (!preg_match($match, $string))
1329 return 'WRONG_DATA';
1332 return false;
1336 * Check to see if the username has been taken, or if it is disallowed.
1337 * Also checks if it includes the " character, which we don't allow in usernames.
1338 * Used for registering, changing names, and posting anonymously with a username
1340 * @param string $username The username to check
1341 * @param string $allowed_username An allowed username, default being $user->data['username']
1343 * @return mixed Either false if validation succeeded or a string which will be used as the error message (with the variable name appended)
1345 function validate_username($username, $allowed_username = false)
1347 global $config, $db, $user, $cache;
1349 $clean_username = utf8_clean_string($username);
1350 $allowed_username = ($allowed_username === false) ? $user->data['username_clean'] : utf8_clean_string($allowed_username);
1352 if ($allowed_username == $clean_username)
1354 return false;
1357 // ... fast checks first.
1358 if (strpos($username, '&quot;') !== false || strpos($username, '"') !== false || empty($clean_username))
1360 return 'INVALID_CHARS';
1363 $mbstring = $pcre = false;
1365 // generic UTF-8 character types supported
1366 switch ($config['allow_name_chars'])
1368 case 'USERNAME_CHARS_ANY':
1369 $regex = '.+';
1370 break;
1372 case 'USERNAME_ALPHA_ONLY':
1373 $regex = '[A-Za-z0-9]+';
1374 break;
1376 case 'USERNAME_ALPHA_SPACERS':
1377 $regex = '[A-Za-z0-9-[\]_+ ]+';
1378 break;
1380 case 'USERNAME_LETTER_NUM':
1381 $regex = '[\p{Lu}\p{Ll}\p{N}]+';
1382 break;
1384 case 'USERNAME_LETTER_NUM_SPACERS':
1385 $regex = '[-\]_+ [\p{Lu}\p{Ll}\p{N}]+';
1386 break;
1388 case 'USERNAME_ASCII':
1389 default:
1390 $regex = '[\x01-\x7F]+';
1391 break;
1394 if (!preg_match('#^' . $regex . '$#u', $username))
1396 return 'INVALID_CHARS';
1399 $sql = 'SELECT username
1400 FROM ' . USERS_TABLE . "
1401 WHERE username_clean = '" . $db->sql_escape($clean_username) . "'";
1402 $result = $db->sql_query($sql);
1403 $row = $db->sql_fetchrow($result);
1404 $db->sql_freeresult($result);
1406 if ($row)
1408 return 'USERNAME_TAKEN';
1411 $sql = 'SELECT group_name
1412 FROM ' . GROUPS_TABLE . "
1413 WHERE group_name = '" . $db->sql_escape($clean_username) . "'";
1414 $result = $db->sql_query($sql);
1415 $row = $db->sql_fetchrow($result);
1416 $db->sql_freeresult($result);
1418 if ($row)
1420 return 'USERNAME_TAKEN';
1423 $bad_usernames = cache::obtain_disallowed_usernames();
1425 foreach ($bad_usernames as $bad_username)
1427 if (preg_match('#^' . $bad_username . '$#', $clean_username))
1429 return 'USERNAME_DISALLOWED';
1433 return false;
1437 * Check to see if the password meets the complexity settings
1439 * @return boolean|string Either false if validation succeeded or a string which will be used as the error message (with the variable name appended)
1441 function validate_password($password)
1443 global $config, $db, $user;
1445 if (!$password)
1447 return false;
1450 // generic UTF-8 character types supported
1451 $upp = '\p{Lu}';
1452 $low = '\p{Ll}';
1453 $let = '\p{L}';
1454 $num = '\p{N}';
1455 $sym = '[^\p{Lu}\p{Ll}\p{N}]';
1457 $chars = array();
1459 switch ($config['pass_complex'])
1461 case 'PASS_TYPE_CASE':
1462 $chars[] = $low;
1463 $chars[] = $upp;
1464 break;
1466 case 'PASS_TYPE_ALPHA':
1467 $chars[] = $let;
1468 $chars[] = $num;
1469 break;
1471 case 'PASS_TYPE_SYMBOL':
1472 $chars[] = $low;
1473 $chars[] = $upp;
1474 $chars[] = $num;
1475 $chars[] = $sym;
1476 break;
1479 if ($pcre)
1481 foreach ($chars as $char)
1483 if (!preg_match('#' . $char . '#u', $password))
1485 return 'INVALID_CHARS';
1489 else if ($mbstring)
1491 foreach ($chars as $char)
1493 if (mb_ereg($char, $password) === false)
1495 return 'INVALID_CHARS';
1500 return false;
1504 * Check to see if email address is banned or already present in the DB
1506 * @param string $email The email to check
1507 * @param string $allowed_email An allowed email, default being $user->data['user_email']
1509 * @return mixed Either false if validation succeeded or a string which will be used as the error message (with the variable name appended)
1511 function validate_email($email, $allowed_email = false)
1513 global $config, $db, $user;
1515 $email = strtolower($email);
1516 $allowed_email = ($allowed_email === false) ? strtolower($user->data['user_email']) : strtolower($allowed_email);
1518 if ($allowed_email == $email)
1520 return false;
1523 if (!preg_match('/^' . get_preg_expression('email') . '$/i', $email))
1525 return 'EMAIL_INVALID';
1528 // Check MX record.
1529 // The idea for this is from reading the UseBB blog/announcement. :)
1530 if ($config['email_check_mx'])
1532 list(, $domain) = explode('@', $email);
1534 if (phpbb_checkdnsrr($domain, 'A') === false && phpbb_checkdnsrr($domain, 'MX') === false)
1536 return 'DOMAIN_NO_MX_RECORD';
1540 if (($ban_reason = $user->check_ban(false, false, $email, true)) !== false)
1542 return ($ban_reason === true) ? 'EMAIL_BANNED' : $ban_reason;
1545 if (!$config['allow_emailreuse'])
1547 $sql = 'SELECT user_email_hash
1548 FROM ' . USERS_TABLE . "
1549 WHERE user_email_hash = " . hexdec(crc32($email) . strlen($email));
1550 $result = $db->sql_query($sql);
1551 $row = $db->sql_fetchrow($result);
1552 $db->sql_freeresult($result);
1554 if ($row)
1556 return 'EMAIL_TAKEN';
1560 return false;
1564 * Validate jabber address
1565 * Taken from the jabber class within flyspray (see author notes)
1567 * @author flyspray.org
1569 function validate_jabber($jid)
1571 if (!$jid)
1573 return false;
1576 $seperator_pos = strpos($jid, '@');
1578 if ($seperator_pos === false)
1580 return 'WRONG_DATA';
1583 $username = substr($jid, 0, $seperator_pos);
1584 $realm = substr($jid, $seperator_pos + 1);
1586 if (strlen($username) == 0 || strlen($realm) < 3)
1588 return 'WRONG_DATA';
1591 $arr = explode('.', $realm);
1593 if (sizeof($arr) == 0)
1595 return 'WRONG_DATA';
1598 foreach ($arr as $part)
1600 if (substr($part, 0, 1) == '-' || substr($part, -1, 1) == '-')
1602 return 'WRONG_DATA';
1605 if (!preg_match("@^[a-zA-Z0-9-.]+$@", $part))
1607 return 'WRONG_DATA';
1611 $boundary = array(array(0, 127), array(192, 223), array(224, 239), array(240, 247), array(248, 251), array(252, 253));
1613 // Prohibited Characters RFC3454 + RFC3920
1614 $prohibited = array(
1615 // Table C.1.1
1616 array(0x0020, 0x0020), // SPACE
1617 // Table C.1.2
1618 array(0x00A0, 0x00A0), // NO-BREAK SPACE
1619 array(0x1680, 0x1680), // OGHAM SPACE MARK
1620 array(0x2000, 0x2001), // EN QUAD
1621 array(0x2001, 0x2001), // EM QUAD
1622 array(0x2002, 0x2002), // EN SPACE
1623 array(0x2003, 0x2003), // EM SPACE
1624 array(0x2004, 0x2004), // THREE-PER-EM SPACE
1625 array(0x2005, 0x2005), // FOUR-PER-EM SPACE
1626 array(0x2006, 0x2006), // SIX-PER-EM SPACE
1627 array(0x2007, 0x2007), // FIGURE SPACE
1628 array(0x2008, 0x2008), // PUNCTUATION SPACE
1629 array(0x2009, 0x2009), // THIN SPACE
1630 array(0x200A, 0x200A), // HAIR SPACE
1631 array(0x200B, 0x200B), // ZERO WIDTH SPACE
1632 array(0x202F, 0x202F), // NARROW NO-BREAK SPACE
1633 array(0x205F, 0x205F), // MEDIUM MATHEMATICAL SPACE
1634 array(0x3000, 0x3000), // IDEOGRAPHIC SPACE
1635 // Table C.2.1
1636 array(0x0000, 0x001F), // [CONTROL CHARACTERS]
1637 array(0x007F, 0x007F), // DELETE
1638 // Table C.2.2
1639 array(0x0080, 0x009F), // [CONTROL CHARACTERS]
1640 array(0x06DD, 0x06DD), // ARABIC END OF AYAH
1641 array(0x070F, 0x070F), // SYRIAC ABBREVIATION MARK
1642 array(0x180E, 0x180E), // MONGOLIAN VOWEL SEPARATOR
1643 array(0x200C, 0x200C), // ZERO WIDTH NON-JOINER
1644 array(0x200D, 0x200D), // ZERO WIDTH JOINER
1645 array(0x2028, 0x2028), // LINE SEPARATOR
1646 array(0x2029, 0x2029), // PARAGRAPH SEPARATOR
1647 array(0x2060, 0x2060), // WORD JOINER
1648 array(0x2061, 0x2061), // FUNCTION APPLICATION
1649 array(0x2062, 0x2062), // INVISIBLE TIMES
1650 array(0x2063, 0x2063), // INVISIBLE SEPARATOR
1651 array(0x206A, 0x206F), // [CONTROL CHARACTERS]
1652 array(0xFEFF, 0xFEFF), // ZERO WIDTH NO-BREAK SPACE
1653 array(0xFFF9, 0xFFFC), // [CONTROL CHARACTERS]
1654 array(0x1D173, 0x1D17A), // [MUSICAL CONTROL CHARACTERS]
1655 // Table C.3
1656 array(0xE000, 0xF8FF), // [PRIVATE USE, PLANE 0]
1657 array(0xF0000, 0xFFFFD), // [PRIVATE USE, PLANE 15]
1658 array(0x100000, 0x10FFFD), // [PRIVATE USE, PLANE 16]
1659 // Table C.4
1660 array(0xFDD0, 0xFDEF), // [NONCHARACTER CODE POINTS]
1661 array(0xFFFE, 0xFFFF), // [NONCHARACTER CODE POINTS]
1662 array(0x1FFFE, 0x1FFFF), // [NONCHARACTER CODE POINTS]
1663 array(0x2FFFE, 0x2FFFF), // [NONCHARACTER CODE POINTS]
1664 array(0x3FFFE, 0x3FFFF), // [NONCHARACTER CODE POINTS]
1665 array(0x4FFFE, 0x4FFFF), // [NONCHARACTER CODE POINTS]
1666 array(0x5FFFE, 0x5FFFF), // [NONCHARACTER CODE POINTS]
1667 array(0x6FFFE, 0x6FFFF), // [NONCHARACTER CODE POINTS]
1668 array(0x7FFFE, 0x7FFFF), // [NONCHARACTER CODE POINTS]
1669 array(0x8FFFE, 0x8FFFF), // [NONCHARACTER CODE POINTS]
1670 array(0x9FFFE, 0x9FFFF), // [NONCHARACTER CODE POINTS]
1671 array(0xAFFFE, 0xAFFFF), // [NONCHARACTER CODE POINTS]
1672 array(0xBFFFE, 0xBFFFF), // [NONCHARACTER CODE POINTS]
1673 array(0xCFFFE, 0xCFFFF), // [NONCHARACTER CODE POINTS]
1674 array(0xDFFFE, 0xDFFFF), // [NONCHARACTER CODE POINTS]
1675 array(0xEFFFE, 0xEFFFF), // [NONCHARACTER CODE POINTS]
1676 array(0xFFFFE, 0xFFFFF), // [NONCHARACTER CODE POINTS]
1677 array(0x10FFFE, 0x10FFFF), // [NONCHARACTER CODE POINTS]
1678 // Table C.5
1679 array(0xD800, 0xDFFF), // [SURROGATE CODES]
1680 // Table C.6
1681 array(0xFFF9, 0xFFF9), // INTERLINEAR ANNOTATION ANCHOR
1682 array(0xFFFA, 0xFFFA), // INTERLINEAR ANNOTATION SEPARATOR
1683 array(0xFFFB, 0xFFFB), // INTERLINEAR ANNOTATION TERMINATOR
1684 array(0xFFFC, 0xFFFC), // OBJECT REPLACEMENT CHARACTER
1685 array(0xFFFD, 0xFFFD), // REPLACEMENT CHARACTER
1686 // Table C.7
1687 array(0x2FF0, 0x2FFB), // [IDEOGRAPHIC DESCRIPTION CHARACTERS]
1688 // Table C.8
1689 array(0x0340, 0x0340), // COMBINING GRAVE TONE MARK
1690 array(0x0341, 0x0341), // COMBINING ACUTE TONE MARK
1691 array(0x200E, 0x200E), // LEFT-TO-RIGHT MARK
1692 array(0x200F, 0x200F), // RIGHT-TO-LEFT MARK
1693 array(0x202A, 0x202A), // LEFT-TO-RIGHT EMBEDDING
1694 array(0x202B, 0x202B), // RIGHT-TO-LEFT EMBEDDING
1695 array(0x202C, 0x202C), // POP DIRECTIONAL FORMATTING
1696 array(0x202D, 0x202D), // LEFT-TO-RIGHT OVERRIDE
1697 array(0x202E, 0x202E), // RIGHT-TO-LEFT OVERRIDE
1698 array(0x206A, 0x206A), // INHIBIT SYMMETRIC SWAPPING
1699 array(0x206B, 0x206B), // ACTIVATE SYMMETRIC SWAPPING
1700 array(0x206C, 0x206C), // INHIBIT ARABIC FORM SHAPING
1701 array(0x206D, 0x206D), // ACTIVATE ARABIC FORM SHAPING
1702 array(0x206E, 0x206E), // NATIONAL DIGIT SHAPES
1703 array(0x206F, 0x206F), // NOMINAL DIGIT SHAPES
1704 // Table C.9
1705 array(0xE0001, 0xE0001), // LANGUAGE TAG
1706 array(0xE0020, 0xE007F), // [TAGGING CHARACTERS]
1707 // RFC3920
1708 array(0x22, 0x22), // "
1709 array(0x26, 0x26), // &
1710 array(0x27, 0x27), // '
1711 array(0x2F, 0x2F), // /
1712 array(0x3A, 0x3A), // :
1713 array(0x3C, 0x3C), // <
1714 array(0x3E, 0x3E), // >
1715 array(0x40, 0x40) // @
1718 $pos = 0;
1719 $result = true;
1721 // @todo: rewrite this!
1722 while ($pos < strlen($username))
1724 $len = $uni = 0;
1725 for ($i = 0; $i <= 5; $i++)
1727 if (ord($username[$pos]) >= $boundary[$i][0] && ord($username[$pos]) <= $boundary[$i][1])
1729 $len = $i + 1;
1730 $uni = (ord($username[$pos]) - $boundary[$i][0]) * pow(2, $i * 6);
1732 for ($k = 1; $k < $len; $k++)
1734 $uni += (ord($username[$pos + $k]) - 128) * pow(2, ($i - $k) * 6);
1737 break;
1741 if ($len == 0)
1743 return 'WRONG_DATA';
1746 foreach ($prohibited as $pval)
1748 if ($uni >= $pval[0] && $uni <= $pval[1])
1750 $result = false;
1751 break 2;
1755 $pos = $pos + $len;
1758 if (!$result)
1760 return 'WRONG_DATA';
1763 return false;
1767 * Remove avatar
1769 function avatar_delete($mode, $row, $clean_db = false)
1771 global $config, $db, $user;
1773 // Check if the users avatar is actually *not* a group avatar
1774 if ($mode == 'user')
1776 if (strpos($row['user_avatar'], 'g') === 0 || (((int)$row['user_avatar'] !== 0) && ((int)$row['user_avatar'] !== (int)$row['user_id'])))
1778 return false;
1782 if ($clean_db)
1784 avatar_remove_db($row[$mode . '_avatar']);
1786 $filename = get_avatar_filename($row[$mode . '_avatar']);
1787 if (file_exists(PHPBB_ROOT_PATH . $config['avatar_path'] . '/' . $filename))
1789 @unlink(PHPBB_ROOT_PATH . $config['avatar_path'] . '/' . $filename);
1790 return true;
1793 return false;
1797 * Remote avatar linkage
1799 function avatar_remote($data, &$error)
1801 global $config, $db, $user;
1803 if (!preg_match('#^(http|https|ftp)://#i', $data['remotelink']))
1805 $data['remotelink'] = 'http://' . $data['remotelink'];
1807 if (!preg_match('#^(http|https|ftp)://(?:(.*?\.)*?[a-z0-9\-]+?\.[a-z]{2,4}|(?:\d{1,3}\.){3,5}\d{1,3}):?([0-9]*?).*?\.(gif|jpg|jpeg|png)$#i', $data['remotelink']))
1809 $error[] = $user->lang['AVATAR_URL_INVALID'];
1810 return false;
1813 // Make sure getimagesize works...
1814 if (($image_data = @getimagesize($data['remotelink'])) === false && (empty($data['width']) || empty($data['height'])))
1816 $error[] = $user->lang['UNABLE_GET_IMAGE_SIZE'];
1817 return false;
1820 if (!empty($image_data) && ($image_data[0] < 2 || $image_data[1] < 2))
1822 $error[] = $user->lang['AVATAR_NO_SIZE'];
1823 return false;
1826 $width = ($data['width'] && $data['height']) ? $data['width'] : $image_data[0];
1827 $height = ($data['width'] && $data['height']) ? $data['height'] : $image_data[1];
1829 if ($width < 2 || $height < 2)
1831 $error[] = $user->lang['AVATAR_NO_SIZE'];
1832 return false;
1835 // Check image type
1836 include_once(PHPBB_ROOT_PATH . 'includes/functions_upload.' . PHP_EXT);
1837 $types = fileupload::image_types();
1838 $extension = strtolower(filespec::get_extension($data['remotelink']));
1840 if (!empty($image_data) && (!isset($types[$image_data[2]]) || !in_array($extension, $types[$image_data[2]])))
1842 if (!isset($types[$image_data[2]]))
1844 $error[] = $user->lang['UNABLE_GET_IMAGE_SIZE'];
1846 else
1848 $error[] = sprintf($user->lang['IMAGE_FILETYPE_MISMATCH'], $types[$image_data[2]][0], $extension);
1850 return false;
1853 if ($config['avatar_max_width'] || $config['avatar_max_height'])
1855 if ($width > $config['avatar_max_width'] || $height > $config['avatar_max_height'])
1857 $error[] = sprintf($user->lang['AVATAR_WRONG_SIZE'], $config['avatar_min_width'], $config['avatar_min_height'], $config['avatar_max_width'], $config['avatar_max_height'], $width, $height);
1858 return false;
1862 if ($config['avatar_min_width'] || $config['avatar_min_height'])
1864 if ($width < $config['avatar_min_width'] || $height < $config['avatar_min_height'])
1866 $error[] = sprintf($user->lang['AVATAR_WRONG_SIZE'], $config['avatar_min_width'], $config['avatar_min_height'], $config['avatar_max_width'], $config['avatar_max_height'], $width, $height);
1867 return false;
1871 return array(AVATAR_REMOTE, $data['remotelink'], $width, $height);
1875 * Avatar upload using the upload class
1877 function avatar_upload($data, &$error)
1879 global $config, $db, $user;
1881 // Init upload class
1882 include_once(PHPBB_ROOT_PATH . 'includes/functions_upload.' . PHP_EXT);
1883 $upload = new fileupload('AVATAR_', array('jpg', 'jpeg', 'gif', 'png'), $config['avatar_filesize'], $config['avatar_min_width'], $config['avatar_min_height'], $config['avatar_max_width'], $config['avatar_max_height'], explode('|', $config['mime_triggers']));
1885 if (!empty($_FILES['uploadfile']['name']))
1887 $file = $upload->form_upload('uploadfile');
1889 else
1891 $file = $upload->remote_upload($data['uploadurl']);
1894 $prefix = $config['avatar_salt'] . '_';
1895 $file->clean_filename('avatar', $prefix, $data['user_id']);
1897 $destination = $config['avatar_path'];
1899 // Adjust destination path (no trailing slash)
1900 if (substr($destination, -1, 1) == '/' || substr($destination, -1, 1) == '\\')
1902 $destination = substr($destination, 0, -1);
1905 $destination = str_replace(array('../', '..\\', './', '.\\'), '', $destination);
1906 if ($destination && ($destination[0] == '/' || $destination[0] == "\\"))
1908 $destination = '';
1911 // Move file and overwrite any existing image
1912 $file->move_file($destination, true);
1914 if (sizeof($file->error))
1916 $file->remove();
1917 $error = array_merge($error, $file->error);
1920 return array(AVATAR_UPLOAD, $data['user_id'] . '_' . time() . '.' . $file->get('extension'), $file->get('width'), $file->get('height'));
1924 * Generates avatar filename from the database entry
1926 function get_avatar_filename($avatar_entry)
1928 global $config;
1931 if ($avatar_entry[0] === 'g')
1933 $avatar_group = true;
1934 $avatar_entry = substr($avatar_entry, 1);
1936 else
1938 $avatar_group = false;
1940 $ext = substr(strrchr($avatar_entry, '.'), 1);
1941 $avatar_entry = intval($avatar_entry);
1942 return $config['avatar_salt'] . '_' . (($avatar_group) ? 'g' : '') . $avatar_entry . '.' . $ext;
1946 * Avatar Gallery
1948 function avatar_gallery($category, $avatar_select, $items_per_column, $block_var = 'avatar_row')
1950 global $user, $cache, $template, $config;
1952 $avatar_list = array();
1954 $path = PHPBB_ROOT_PATH . $config['avatar_gallery_path'];
1956 if (!file_exists($path) || !is_dir($path))
1958 $avatar_list = array($user->lang['NO_AVATAR_CATEGORY'] => array());
1960 else
1962 // Collect images
1963 $dp = @opendir($path);
1965 if (!$dp)
1967 return array($user->lang['NO_AVATAR_CATEGORY'] => array());
1970 while (($file = readdir($dp)) !== false)
1972 if ($file[0] != '.' && preg_match('#^[^&"\'<>]+$#i', $file) && is_dir("$path/$file"))
1974 $avatar_row_count = $avatar_col_count = 0;
1976 if ($dp2 = @opendir("$path/$file"))
1978 while (($sub_file = readdir($dp2)) !== false)
1980 if (preg_match('#^[^&\'"<>]+\.(?:gif|png|jpe?g)$#i', $sub_file))
1982 $avatar_list[$file][$avatar_row_count][$avatar_col_count] = array(
1983 'file' => "$file/$sub_file",
1984 'filename' => $sub_file,
1985 'name' => ucfirst(str_replace('_', ' ', preg_replace('#^(.*)\..*$#', '\1', $sub_file))),
1987 $avatar_col_count++;
1988 if ($avatar_col_count == $items_per_column)
1990 $avatar_row_count++;
1991 $avatar_col_count = 0;
1995 closedir($dp2);
1999 closedir($dp);
2002 if (!sizeof($avatar_list))
2004 $avatar_list = array($user->lang['NO_AVATAR_CATEGORY'] => array());
2007 @ksort($avatar_list);
2009 $category = (!$category) ? key($avatar_list) : $category;
2010 $avatar_categories = array_keys($avatar_list);
2012 $s_category_options = '';
2013 foreach ($avatar_categories as $cat)
2015 $s_category_options .= '<option value="' . $cat . '"' . (($cat == $category) ? ' selected="selected"' : '') . '>' . $cat . '</option>';
2018 $template->assign_vars(array(
2019 'S_AVATARS_ENABLED' => true,
2020 'S_IN_AVATAR_GALLERY' => true,
2021 'S_CAT_OPTIONS' => $s_category_options)
2024 $avatar_list = (isset($avatar_list[$category])) ? $avatar_list[$category] : array();
2026 foreach ($avatar_list as $avatar_row_ary)
2028 $template->assign_block_vars($block_var, array());
2030 foreach ($avatar_row_ary as $avatar_col_ary)
2032 $template->assign_block_vars($block_var . '.avatar_column', array(
2033 'AVATAR_IMAGE' => PHPBB_ROOT_PATH . $config['avatar_gallery_path'] . '/' . $avatar_col_ary['file'],
2034 'AVATAR_NAME' => $avatar_col_ary['name'],
2035 'AVATAR_FILE' => $avatar_col_ary['filename'])
2038 $template->assign_block_vars($block_var . '.avatar_option_column', array(
2039 'AVATAR_IMAGE' => PHPBB_ROOT_PATH . $config['avatar_gallery_path'] . '/' . $avatar_col_ary['file'],
2040 'S_OPTIONS_AVATAR' => $avatar_col_ary['filename'])
2045 return $avatar_list;
2050 * Tries to (re-)establish avatar dimensions
2052 function avatar_get_dimensions($avatar, $avatar_type, &$error, $current_x = 0, $current_y = 0)
2054 global $config, $user;
2056 switch ($avatar_type)
2058 case AVATAR_REMOTE :
2059 break;
2061 case AVATAR_UPLOAD :
2062 $avatar = PHPBB_ROOT_PATH . $config['avatar_path'] . '/' . get_avatar_filename($avatar);
2063 break;
2065 case AVATAR_GALLERY :
2066 $avatar = PHPBB_ROOT_PATH . $config['avatar_gallery_path'] . '/' . $avatar ;
2067 break;
2070 // Make sure getimagesize works...
2071 if (($image_data = @getimagesize($avatar)) === false)
2073 $error[] = $user->lang['UNABLE_GET_IMAGE_SIZE'];
2074 return false;
2077 if ($image_data[0] < 2 || $image_data[1] < 2)
2079 $error[] = $user->lang['AVATAR_NO_SIZE'];
2080 return false;
2083 // try to maintain ratio
2084 if (!(empty($current_x) && empty($current_y)))
2086 if ($current_x != 0)
2088 $image_data[1] = (int) floor(($current_x / $image_data[0]) * $image_data[1]);
2089 $image_data[1] = min($config['avatar_max_height'], $image_data[1]);
2090 $image_data[1] = max($config['avatar_min_height'], $image_data[1]);
2092 if ($current_y != 0)
2094 $image_data[0] = (int) floor(($current_y / $image_data[1]) * $image_data[0]);
2095 $image_data[0] = min($config['avatar_max_width'], $image_data[1]);
2096 $image_data[0] = max($config['avatar_min_width'], $image_data[1]);
2099 return array($image_data[0], $image_data[1]);
2103 * Uploading/Changing user avatar
2105 function avatar_process_user(&$error, $custom_userdata = false)
2107 global $config, $auth, $user, $db;
2109 $data = array(
2110 'uploadurl' => request_var('uploadurl', ''),
2111 'remotelink' => request_var('remotelink', ''),
2112 'width' => request_var('width', 0),
2113 'height' => request_var('height', 0),
2116 $error = validate_data($data, array(
2117 'uploadurl' => array('string', true, 5, 255),
2118 'remotelink' => array('string', true, 5, 255),
2119 'width' => array('string', true, 1, 3),
2120 'height' => array('string', true, 1, 3),
2123 if (sizeof($error))
2125 return false;
2128 $sql_ary = array();
2130 if ($custom_userdata === false)
2132 $userdata = &$user->data;
2134 else
2136 $userdata = &$custom_userdata;
2139 $data['user_id'] = $userdata['user_id'];
2140 $change_avatar = ($custom_userdata === false) ? $auth->acl_get('u_chgavatar') : true;
2141 $avatar_select = basename(request_var('avatar_select', ''));
2143 // Can we upload?
2144 $can_upload = ($config['allow_avatar_upload'] && file_exists(PHPBB_ROOT_PATH . $config['avatar_path']) && @is_writable(PHPBB_ROOT_PATH . $config['avatar_path']) && $change_avatar && (@ini_get('file_uploads') || strtolower(@ini_get('file_uploads')) == 'on')) ? true : false;
2146 if ((!empty($_FILES['uploadfile']['name']) || $data['uploadurl']) && $can_upload)
2148 list($sql_ary['user_avatar_type'], $sql_ary['user_avatar'], $sql_ary['user_avatar_width'], $sql_ary['user_avatar_height']) = avatar_upload($data, $error);
2150 else if ($data['remotelink'] && $change_avatar && $config['allow_avatar_remote'])
2152 list($sql_ary['user_avatar_type'], $sql_ary['user_avatar'], $sql_ary['user_avatar_width'], $sql_ary['user_avatar_height']) = avatar_remote($data, $error);
2154 else if ($avatar_select && $change_avatar && $config['allow_avatar_local'])
2156 $category = basename(request_var('category', ''));
2158 $sql_ary['user_avatar_type'] = AVATAR_GALLERY;
2159 $sql_ary['user_avatar'] = $avatar_select;
2161 // check avatar gallery
2162 if (!is_dir(PHPBB_ROOT_PATH . $config['avatar_gallery_path'] . '/' . $category))
2164 $sql_ary['user_avatar'] = '';
2165 $sql_ary['user_avatar_type'] = $sql_ary['user_avatar_width'] = $sql_ary['user_avatar_height'] = 0;
2167 else
2169 list($sql_ary['user_avatar_width'], $sql_ary['user_avatar_height']) = getimagesize(PHPBB_ROOT_PATH . $config['avatar_gallery_path'] . '/' . $category . '/' . $sql_ary['user_avatar']);
2170 $sql_ary['user_avatar'] = $category . '/' . $sql_ary['user_avatar'];
2173 else if (isset($_POST['delete']) && $change_avatar)
2175 $sql_ary['user_avatar'] = '';
2176 $sql_ary['user_avatar_type'] = $sql_ary['user_avatar_width'] = $sql_ary['user_avatar_height'] = 0;
2178 else if (!empty($userdata['user_avatar']))
2180 // Only update the dimensions
2182 if (empty($data['width']) || empty($data['height']))
2184 if ($dims = avatar_get_dimensions($userdata['user_avatar'], $userdata['user_avatar_type'], $error, $data['width'], $data['height']))
2186 list($guessed_x, $guessed_y) = $dims;
2187 if (empty($data['width']))
2189 $data['width'] = $guessed_x;
2191 if (empty($data['height']))
2193 $data['height'] = $guessed_y;
2197 if (($config['avatar_max_width'] || $config['avatar_max_height']) &&
2198 (($data['width'] != $userdata['user_avatar_width']) || $data['height'] != $userdata['user_avatar_height']))
2200 if ($data['width'] > $config['avatar_max_width'] || $data['height'] > $config['avatar_max_height'])
2202 $error[] = sprintf($user->lang['AVATAR_WRONG_SIZE'], $config['avatar_min_width'], $config['avatar_min_height'], $config['avatar_max_width'], $config['avatar_max_height'], $data['width'], $data['height']);
2206 if (!sizeof($error))
2208 if ($config['avatar_min_width'] || $config['avatar_min_height'])
2210 if ($data['width'] < $config['avatar_min_width'] || $data['height'] < $config['avatar_min_height'])
2212 $error[] = sprintf($user->lang['AVATAR_WRONG_SIZE'], $config['avatar_min_width'], $config['avatar_min_height'], $config['avatar_max_width'], $config['avatar_max_height'], $data['width'], $data['height']);
2217 if (!sizeof($error))
2219 $sql_ary['user_avatar_width'] = $data['width'];
2220 $sql_ary['user_avatar_height'] = $data['height'];
2224 if (!sizeof($error))
2226 // Do we actually have any data to update?
2227 if (sizeof($sql_ary))
2229 $ext_new = $ext_old = '';
2230 if (isset($sql_ary['user_avatar']))
2232 $userdata = ($custom_userdata === false) ? $user->data : $custom_userdata;
2233 $ext_new = (empty($sql_ary['user_avatar'])) ? '' : substr(strrchr($sql_ary['user_avatar'], '.'), 1);
2234 $ext_old = (empty($userdata['user_avatar'])) ? '' : substr(strrchr($userdata['user_avatar'], '.'), 1);
2236 if ($userdata['user_avatar_type'] == AVATAR_UPLOAD)
2238 // Delete old avatar if present
2239 if ((!empty($userdata['user_avatar']) && empty($sql_ary['user_avatar']))
2240 || ( !empty($userdata['user_avatar']) && !empty($sql_ary['user_avatar']) && $ext_new !== $ext_old))
2242 avatar_delete('user', $userdata);
2247 $sql = 'UPDATE ' . USERS_TABLE . '
2248 SET ' . $db->sql_build_array('UPDATE', $sql_ary) . '
2249 WHERE user_id = ' . (($custom_userdata === false) ? $user->data['user_id'] : $custom_userdata['user_id']);
2250 $db->sql_query($sql);
2255 return (sizeof($error)) ? false : true;
2259 // Usergroup functions
2263 * Add or edit a group. If we're editing a group we only update user
2264 * parameters such as rank, etc. if they are changed
2266 function group_create(&$group_id, $type, $name, $desc, $group_attributes, $allow_desc_bbcode = false, $allow_desc_urls = false, $allow_desc_smilies = false)
2268 global $config, $db, $user, $file_upload;
2270 $error = array();
2271 $attribute_ary = array(
2272 'group_colour' => 'string',
2273 'group_rank' => 'int',
2274 'group_avatar' => 'string',
2275 'group_avatar_type' => 'int',
2276 'group_avatar_width' => 'int',
2277 'group_avatar_height' => 'int',
2279 'group_receive_pm' => 'int',
2280 'group_legend' => 'int',
2281 'group_message_limit' => 'int',
2282 'group_max_recipients' => 'int',
2284 'group_founder_manage' => 'int',
2287 // Those are group-only attributes
2288 $group_only_ary = array('group_receive_pm', 'group_legend', 'group_message_limit', 'group_max_recipients', 'group_founder_manage');
2290 // Check data. Limit group name length.
2291 if (!utf8_strlen($name) || utf8_strlen($name) > 60)
2293 $error[] = (!utf8_strlen($name)) ? $user->lang['GROUP_ERR_USERNAME'] : $user->lang['GROUP_ERR_USER_LONG'];
2296 $err = group_validate_groupname($group_id, $name);
2297 if (!empty($err))
2299 $error[] = $user->lang[$err];
2302 if (!in_array($type, array(GROUP_OPEN, GROUP_CLOSED, GROUP_HIDDEN, GROUP_SPECIAL, GROUP_FREE)))
2304 $error[] = $user->lang['GROUP_ERR_TYPE'];
2307 if (!sizeof($error))
2309 $user_ary = array();
2310 $sql_ary = array(
2311 'group_name' => (string) $name,
2312 'group_name_clean' => (string) utf8_clean_string($name),
2313 'group_desc' => (string) $desc,
2314 'group_desc_uid' => '',
2315 'group_desc_bitfield' => '',
2316 'group_type' => (int) $type,
2319 // Parse description
2320 if ($desc)
2322 generate_text_for_storage($sql_ary['group_desc'], $sql_ary['group_desc_uid'], $sql_ary['group_desc_bitfield'], $sql_ary['group_desc_options'], $allow_desc_bbcode, $allow_desc_urls, $allow_desc_smilies);
2325 if (sizeof($group_attributes))
2327 foreach ($attribute_ary as $attribute => $_type)
2329 if (isset($group_attributes[$attribute]))
2331 settype($group_attributes[$attribute], $_type);
2332 $sql_ary[$attribute] = $group_attributes[$attribute];
2337 // Setting the log message before we set the group id (if group gets added)
2338 $log = ($group_id) ? 'LOG_GROUP_UPDATED' : 'LOG_GROUP_CREATED';
2340 $query = '';
2342 if ($group_id)
2344 $sql = 'SELECT user_id
2345 FROM ' . USERS_TABLE . '
2346 WHERE group_id = ' . $group_id;
2347 $result = $db->sql_query($sql);
2349 while ($row = $db->sql_fetchrow($result))
2351 $user_ary[] = $row['user_id'];
2353 $db->sql_freeresult($result);
2355 if (isset($sql_ary['group_avatar']) && !$sql_ary['group_avatar'])
2357 remove_default_avatar($group_id, $user_ary);
2359 if (isset($sql_ary['group_rank']) && !$sql_ary['group_rank'])
2361 remove_default_rank($group_id, $user_ary);
2364 $sql = 'UPDATE ' . GROUPS_TABLE . '
2365 SET ' . $db->sql_build_array('UPDATE', $sql_ary) . "
2366 WHERE group_id = $group_id";
2367 $db->sql_query($sql);
2369 // Since we may update the name too, we need to do this on other tables too...
2370 $sql = 'UPDATE ' . MODERATOR_CACHE_TABLE . "
2371 SET group_name = '" . $db->sql_escape($sql_ary['group_name']) . "'
2372 WHERE group_id = $group_id";
2373 $db->sql_query($sql);
2375 else
2377 $sql = 'INSERT INTO ' . GROUPS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary);
2378 $db->sql_query($sql);
2381 if (!$group_id)
2383 $group_id = $db->sql_nextid();
2384 if (isset($sql_ary['group_avatar_type']) && $sql_ary['group_avatar_type'] == AVATAR_UPLOAD)
2386 group_correct_avatar($group_id, $sql_ary['group_avatar']);
2390 // Set user attributes
2391 $sql_ary = array();
2392 if (sizeof($group_attributes))
2394 foreach ($attribute_ary as $attribute => $_type)
2396 if (isset($group_attributes[$attribute]) && !in_array($attribute, $group_only_ary))
2398 // If we are about to set an avatar, we will not overwrite user avatars if no group avatar is set...
2399 if (strpos($attribute, 'group_avatar') === 0 && !$group_attributes[$attribute])
2401 continue;
2404 $sql_ary[$attribute] = $group_attributes[$attribute];
2409 if (sizeof($sql_ary) && sizeof($user_ary))
2411 group_set_user_default($group_id, $user_ary, $sql_ary);
2414 $name = ($type == GROUP_SPECIAL) ? $user->lang['G_' . $name] : $name;
2415 add_log('admin', $log, $name);
2417 group_update_listings($group_id);
2420 return (sizeof($error)) ? $error : false;
2425 * Changes a group avatar's filename to conform to the naming scheme
2427 function group_correct_avatar($group_id, $old_entry)
2429 global $config, $db;
2431 $group_id = (int)$group_id;
2432 $ext = substr(strrchr($old_entry, '.'), 1);
2433 $old_filename = get_avatar_filename($old_entry);
2434 $new_filename = $config['avatar_salt'] . "_g$group_id.$ext";
2435 $new_entry = 'g' . $group_id . '_' . substr(time(), -5) . ".$ext";
2437 $avatar_path = PHPBB_ROOT_PATH . $config['avatar_path'];
2438 if (@rename($avatar_path . '/'. $old_filename, $avatar_path . '/' . $new_filename))
2440 $sql = 'UPDATE ' . GROUPS_TABLE . '
2441 SET group_avatar = \'' . $db->sql_escape($new_entry) . "'
2442 WHERE group_id = $group_id";
2443 $db->sql_query($sql);
2449 * Remove avatar also for users not having the group as default
2451 function avatar_remove_db($avatar_name)
2453 global $config, $db;
2455 $sql = 'UPDATE ' . USERS_TABLE . "
2456 SET user_avatar = '',
2457 user_avatar_type = 0
2458 WHERE user_avatar = '" . $db->sql_escape($avatar_name) . '\'';
2459 $db->sql_query($sql);
2464 * Group Delete
2466 function group_delete($group_id, $group_name = false)
2468 global $db;
2470 if (!$group_name)
2472 $group_name = get_group_name($group_id);
2475 $start = 0;
2479 $user_id_ary = $username_ary = array();
2481 // Batch query for group members, call group_user_del
2482 $sql = 'SELECT u.user_id, u.username
2483 FROM ' . USER_GROUP_TABLE . ' ug, ' . USERS_TABLE . " u
2484 WHERE ug.group_id = $group_id
2485 AND u.user_id = ug.user_id";
2486 $result = $db->sql_query_limit($sql, 200, $start);
2488 if ($row = $db->sql_fetchrow($result))
2492 $user_id_ary[] = $row['user_id'];
2493 $username_ary[] = $row['username'];
2495 $start++;
2497 while ($row = $db->sql_fetchrow($result));
2499 group_user_del($group_id, $user_id_ary, $username_ary, $group_name);
2501 else
2503 $start = 0;
2505 $db->sql_freeresult($result);
2507 while ($start);
2509 // Delete group
2510 $sql = 'DELETE FROM ' . GROUPS_TABLE . "
2511 WHERE group_id = $group_id";
2512 $db->sql_query($sql);
2514 // Delete auth entries from the groups table
2515 $sql = 'DELETE FROM ' . ACL_GROUPS_TABLE . "
2516 WHERE group_id = $group_id";
2517 $db->sql_query($sql);
2519 // Re-cache moderators
2520 if (!function_exists('cache_moderators'))
2522 include(PHPBB_ROOT_PATH . 'includes/functions_admin.' . PHP_EXT);
2525 cache_moderators();
2527 add_log('admin', 'LOG_GROUP_DELETE', $group_name);
2529 // Return false - no error
2530 return false;
2534 * Add user(s) to group
2536 * @return mixed false if no errors occurred, else the user lang string for the relevant error, for example 'NO_USER'
2538 function group_user_add($group_id, $user_id_ary = false, $username_ary = false, $group_name = false, $default = false, $leader = 0, $pending = 0, $group_attributes = false)
2540 global $db, $auth;
2542 // We need both username and user_id info
2543 $result = user_get_id_name($user_id_ary, $username_ary);
2545 if (!sizeof($user_id_ary) || $result !== false)
2547 return 'NO_USER';
2550 // Remove users who are already members of this group
2551 $sql = 'SELECT user_id, group_leader
2552 FROM ' . USER_GROUP_TABLE . '
2553 WHERE ' . $db->sql_in_set('user_id', $user_id_ary) . "
2554 AND group_id = $group_id";
2555 $result = $db->sql_query($sql);
2557 $add_id_ary = $update_id_ary = array();
2558 while ($row = $db->sql_fetchrow($result))
2560 $add_id_ary[] = (int) $row['user_id'];
2562 if ($leader && !$row['group_leader'])
2564 $update_id_ary[] = (int) $row['user_id'];
2567 $db->sql_freeresult($result);
2569 // Do all the users exist in this group?
2570 $add_id_ary = array_diff($user_id_ary, $add_id_ary);
2572 // If we have no users
2573 if (!sizeof($add_id_ary) && !sizeof($update_id_ary))
2575 return 'GROUP_USERS_EXIST';
2578 $db->sql_transaction('begin');
2580 // Insert the new users
2581 if (sizeof($add_id_ary))
2583 $sql_ary = array();
2585 foreach ($add_id_ary as $user_id)
2587 $sql_ary[] = array(
2588 'user_id' => (int) $user_id,
2589 'group_id' => (int) $group_id,
2590 'group_leader' => (int) $leader,
2591 'user_pending' => (int) $pending,
2595 $db->sql_multi_insert(USER_GROUP_TABLE, $sql_ary);
2598 if (sizeof($update_id_ary))
2600 $sql = 'UPDATE ' . USER_GROUP_TABLE . '
2601 SET group_leader = 1
2602 WHERE ' . $db->sql_in_set('user_id', $update_id_ary) . "
2603 AND group_id = $group_id";
2604 $db->sql_query($sql);
2607 if ($default)
2609 group_set_user_default($group_id, $user_id_ary, $group_attributes);
2612 $db->sql_transaction('commit');
2614 // Clear permissions cache of relevant users
2615 $auth->acl_clear_prefetch($user_id_ary);
2617 if (!$group_name)
2619 $group_name = get_group_name($group_id);
2622 $log = ($leader) ? 'LOG_MODS_ADDED' : 'LOG_USERS_ADDED';
2624 add_log('admin', $log, $group_name, implode(', ', $username_ary));
2626 group_update_listings($group_id);
2628 // Return false - no error
2629 return false;
2633 * Remove a user/s from a given group. When we remove users we update their
2634 * default group_id. We do this by examining which "special" groups they belong
2635 * to. The selection is made based on a reasonable priority system
2637 * @return false if no errors occurred, else the user lang string for the relevant error, for example 'NO_USER'
2639 function group_user_del($group_id, $user_id_ary = false, $username_ary = false, $group_name = false)
2641 global $db, $auth;
2643 $group_order = array('ADMINISTRATORS', 'GLOBAL_MODERATORS', 'REGISTERED_COPPA', 'REGISTERED', 'BOTS', 'GUESTS');
2645 // We need both username and user_id info
2646 $result = user_get_id_name($user_id_ary, $username_ary);
2648 if (!sizeof($user_id_ary) || $result !== false)
2650 return 'NO_USER';
2653 $clean_group_order = array_map('utf8_clean_string', $group_order);
2655 $sql = 'SELECT *
2656 FROM ' . GROUPS_TABLE . '
2657 WHERE ' . $db->sql_in_set('group_name_clean', $clean_group_order);
2658 $result = $db->sql_query($sql);
2660 $group_order_id = $special_group_data = array();
2661 while ($row = $db->sql_fetchrow($result))
2663 $group_order_id[$row['group_name']] = $row['group_id'];
2665 $special_group_data[$row['group_id']] = array(
2666 'group_colour' => $row['group_colour'],
2667 'group_rank' => $row['group_rank'],
2670 // Only set the group avatar if one is defined...
2671 if ($row['group_avatar'])
2673 $special_group_data[$row['group_id']] = array_merge($special_group_data[$row['group_id']], array(
2674 'group_avatar' => $row['group_avatar'],
2675 'group_avatar_type' => $row['group_avatar_type'],
2676 'group_avatar_width' => $row['group_avatar_width'],
2677 'group_avatar_height' => $row['group_avatar_height'])
2681 $db->sql_freeresult($result);
2683 // Get users default groups - we only need to reset default group membership if the group from which the user gets removed is set as default
2684 $sql = 'SELECT user_id, group_id
2685 FROM ' . USERS_TABLE . '
2686 WHERE ' . $db->sql_in_set('user_id', $user_id_ary);
2687 $result = $db->sql_query($sql);
2689 $default_groups = array();
2690 while ($row = $db->sql_fetchrow($result))
2692 $default_groups[$row['user_id']] = $row['group_id'];
2694 $db->sql_freeresult($result);
2696 // What special group memberships exist for these users?
2697 $sql = 'SELECT g.group_id, g.group_name_clean, ug.user_id
2698 FROM ' . USER_GROUP_TABLE . ' ug, ' . GROUPS_TABLE . ' g
2699 WHERE ' . $db->sql_in_set('ug.user_id', $user_id_ary) . "
2700 AND g.group_id = ug.group_id
2701 AND g.group_id <> $group_id
2702 AND g.group_type = " . GROUP_SPECIAL . '
2703 ORDER BY ug.user_id, g.group_id';
2704 $result = $db->sql_query($sql);
2706 $temp_ary = array();
2707 while ($row = $db->sql_fetchrow($result))
2709 if ($default_groups[$row['user_id']] == $group_id && (!isset($temp_ary[$row['user_id']]) || array_search($row['group_name_clean'], $clean_group_order) < $temp_ary[$row['user_id']]))
2711 $temp_ary[$row['user_id']] = $row['group_id'];
2714 $db->sql_freeresult($result);
2716 $sql_where_ary = array();
2717 foreach ($temp_ary as $uid => $gid)
2719 $sql_where_ary[$gid][] = $uid;
2721 unset($temp_ary);
2723 foreach ($special_group_data as $gid => $default_data_ary)
2725 if (isset($sql_where_ary[$gid]) && sizeof($sql_where_ary[$gid]))
2727 remove_default_rank($group_id, $sql_where_ary[$gid]);
2728 remove_default_avatar($group_id, $sql_where_ary[$gid]);
2729 group_set_user_default($gid, $sql_where_ary[$gid], $default_data_ary);
2732 unset($special_group_data);
2734 $sql = 'DELETE FROM ' . USER_GROUP_TABLE . "
2735 WHERE group_id = $group_id
2736 AND " . $db->sql_in_set('user_id', $user_id_ary);
2737 $db->sql_query($sql);
2739 // Clear permissions cache of relevant users
2740 $auth->acl_clear_prefetch($user_id_ary);
2742 if (!$group_name)
2744 $group_name = get_group_name($group_id);
2747 $log = 'LOG_GROUP_REMOVE';
2749 add_log('admin', $log, $group_name, implode(', ', $username_ary));
2751 group_update_listings($group_id);
2753 // Return false - no error
2754 return false;
2759 * Removes the group avatar of the default group from the users in user_ids who have that group as default.
2761 function remove_default_avatar($group_id, $user_ids)
2763 global $db;
2765 if (!is_array($user_ids))
2767 $user_ids = array($user_ids);
2769 if (empty($user_ids))
2771 return false;
2774 $user_ids = array_map('intval', $user_ids);
2776 $sql = 'SELECT *
2777 FROM ' . GROUPS_TABLE . '
2778 WHERE group_id = ' . (int)$group_id;
2779 $result = $db->sql_query($sql);
2780 if (!$row = $db->sql_fetchrow($result))
2782 $db->sql_freeresult($result);
2783 return false;
2785 $db->sql_freeresult($result);
2787 $sql = 'UPDATE ' . USERS_TABLE . "
2788 SET user_avatar = '',
2789 user_avatar_type = 0,
2790 user_avatar_width = 0,
2791 user_avatar_height = 0
2792 WHERE group_id = " . (int) $group_id . "
2793 AND user_avatar = '" . $db->sql_escape($row['group_avatar']) . "'
2794 AND " . $db->sql_in_set('user_id', $user_ids);
2796 $db->sql_query($sql);
2800 * Removes the group rank of the default group from the users in user_ids who have that group as default.
2802 function remove_default_rank($group_id, $user_ids)
2804 global $db;
2806 if (!is_array($user_ids))
2808 $user_ids = array($user_ids);
2810 if (empty($user_ids))
2812 return false;
2815 $user_ids = array_map('intval', $user_ids);
2817 $sql = 'SELECT *
2818 FROM ' . GROUPS_TABLE . '
2819 WHERE group_id = ' . (int)$group_id;
2820 $result = $db->sql_query($sql);
2821 if (!$row = $db->sql_fetchrow($result))
2823 $db->sql_freeresult($result);
2824 return false;
2826 $db->sql_freeresult($result);
2828 $sql = 'UPDATE ' . USERS_TABLE . '
2829 SET user_rank = 0
2830 WHERE group_id = ' . (int)$group_id . '
2831 AND user_rank <> 0
2832 AND user_rank = ' . (int)$row['group_rank'] . '
2833 AND ' . $db->sql_in_set('user_id', $user_ids);
2834 $db->sql_query($sql);
2838 * This is used to promote (to leader), demote or set as default a member/s
2840 function group_user_attributes($action, $group_id, $user_id_ary = false, $username_ary = false, $group_name = false, $group_attributes = false)
2842 global $db, $auth, $config;
2844 // We need both username and user_id info
2845 $result = user_get_id_name($user_id_ary, $username_ary);
2847 if (!sizeof($user_id_ary) || $result !== false)
2849 return 'NO_USERS';
2852 if (!$group_name)
2854 $group_name = get_group_name($group_id);
2857 switch ($action)
2859 case 'demote':
2860 case 'promote':
2862 $sql = 'SELECT user_id FROM ' . USER_GROUP_TABLE . "
2863 WHERE group_id = $group_id
2864 AND user_pending = 1
2865 AND " . $db->sql_in_set('user_id', $user_id_ary);
2866 $result = $db->sql_query_limit($sql, 1);
2867 $not_empty = ($db->sql_fetchrow($result));
2868 $db->sql_freeresult($result);
2869 if ($not_empty)
2871 return 'NO_VALID_USERS';
2874 $sql = 'UPDATE ' . USER_GROUP_TABLE . '
2875 SET group_leader = ' . (($action == 'promote') ? 1 : 0) . "
2876 WHERE group_id = $group_id
2877 AND user_pending = 0
2878 AND " . $db->sql_in_set('user_id', $user_id_ary);
2879 $db->sql_query($sql);
2881 $log = ($action == 'promote') ? 'LOG_GROUP_PROMOTED' : 'LOG_GROUP_DEMOTED';
2882 break;
2884 case 'approve':
2885 // Make sure we only approve those which are pending ;)
2886 $sql = 'SELECT u.user_id, u.user_email, u.username, u.username_clean, u.user_notify_type, u.user_jabber, u.user_lang
2887 FROM ' . USERS_TABLE . ' u, ' . USER_GROUP_TABLE . ' ug
2888 WHERE ug.group_id = ' . $group_id . '
2889 AND ug.user_pending = 1
2890 AND ug.user_id = u.user_id
2891 AND ' . $db->sql_in_set('ug.user_id', $user_id_ary);
2892 $result = $db->sql_query($sql);
2894 $user_id_ary = $email_users = array();
2895 while ($row = $db->sql_fetchrow($result))
2897 $user_id_ary[] = $row['user_id'];
2898 $email_users[] = $row;
2900 $db->sql_freeresult($result);
2902 if (!sizeof($user_id_ary))
2904 return false;
2907 $sql = 'UPDATE ' . USER_GROUP_TABLE . "
2908 SET user_pending = 0
2909 WHERE group_id = $group_id
2910 AND " . $db->sql_in_set('user_id', $user_id_ary);
2911 $db->sql_query($sql);
2913 // Send approved email to users...
2914 include_once(PHPBB_ROOT_PATH . 'includes/functions_messenger.' . PHP_EXT);
2915 $messenger = new messenger();
2917 foreach ($email_users as $row)
2919 $messenger->template('group_approved', $row['user_lang']);
2921 $messenger->to($row['user_email'], $row['username']);
2922 $messenger->im($row['user_jabber'], $row['username']);
2924 $messenger->assign_vars(array(
2925 'USERNAME' => htmlspecialchars_decode($row['username']),
2926 'GROUP_NAME' => htmlspecialchars_decode($group_name),
2927 'U_GROUP' => generate_board_url() . '/ucp.' . PHP_EXT . '?i=groups&mode=membership',
2930 $messenger->send($row['user_notify_type']);
2933 $messenger->save_queue();
2935 $log = 'LOG_USERS_APPROVED';
2936 break;
2938 case 'default':
2939 $sql = 'SELECT user_id, group_id FROM ' . USERS_TABLE . '
2940 WHERE ' . $db->sql_in_set('user_id', $user_id_ary, false, true);
2941 $result = $db->sql_query($sql);
2943 $groups = array();
2944 while ($row = $db->sql_fetchrow($result))
2946 if (!isset($groups[$row['group_id']]))
2948 $groups[$row['group_id']] = array();
2950 $groups[$row['group_id']][] = $row['user_id'];
2952 $db->sql_freeresult($result);
2954 foreach ($groups as $gid => $uids)
2956 remove_default_rank($gid, $uids);
2957 remove_default_avatar($gid, $uids);
2959 group_set_user_default($group_id, $user_id_ary, $group_attributes);
2960 $log = 'LOG_GROUP_DEFAULTS';
2961 break;
2964 // Clear permissions cache of relevant users
2965 $auth->acl_clear_prefetch($user_id_ary);
2967 add_log('admin', $log, $group_name, implode(', ', $username_ary));
2969 group_update_listings($group_id);
2971 return false;
2975 * A small version of validate_username to check for a group name's existence. To be called directly.
2977 function group_validate_groupname($group_id, $group_name)
2979 global $config, $db;
2981 $group_name = utf8_clean_string($group_name);
2983 if (!empty($group_id))
2985 $sql = 'SELECT group_name_clean
2986 FROM ' . GROUPS_TABLE . '
2987 WHERE group_id = ' . (int) $group_id;
2988 $result = $db->sql_query($sql);
2989 $row = $db->sql_fetchrow($result);
2990 $db->sql_freeresult($result);
2992 if (!$row)
2994 return false;
2997 $allowed_groupname = utf8_clean_string($row['group_name']);
2999 if ($allowed_groupname == $group_name)
3001 return false;
3005 $sql = 'SELECT group_name
3006 FROM ' . GROUPS_TABLE . "
3007 WHERE group_name_clean = '" . $db->sql_escape(utf8_clean_string($group_name)) . "'";
3008 $result = $db->sql_query($sql);
3009 $row = $db->sql_fetchrow($result);
3010 $db->sql_freeresult($result);
3012 if ($row)
3014 return 'GROUP_NAME_TAKEN';
3017 return false;
3021 * Set users default group
3023 * @access private
3025 function group_set_user_default($group_id, $user_id_ary, $group_attributes = false, $update_listing = false)
3027 global $db;
3029 if (empty($user_id_ary))
3031 return;
3034 $attribute_ary = array(
3035 'group_colour' => 'string',
3036 'group_rank' => 'int',
3037 'group_avatar' => 'string',
3038 'group_avatar_type' => 'int',
3039 'group_avatar_width' => 'int',
3040 'group_avatar_height' => 'int',
3043 $sql_ary = array(
3044 'group_id' => $group_id
3047 // Were group attributes passed to the function? If not we need to obtain them
3048 if ($group_attributes === false)
3050 $sql = 'SELECT ' . implode(', ', array_keys($attribute_ary)) . '
3051 FROM ' . GROUPS_TABLE . "
3052 WHERE group_id = $group_id";
3053 $result = $db->sql_query($sql);
3054 $group_attributes = $db->sql_fetchrow($result);
3055 $db->sql_freeresult($result);
3058 foreach ($attribute_ary as $attribute => $type)
3060 if (isset($group_attributes[$attribute]))
3062 // If we are about to set an avatar or rank, we will not overwrite with empty, unless we are not actually changing the default group
3063 if ((strpos($attribute, 'group_avatar') === 0 || strpos($attribute, 'group_rank') === 0) && !$group_attributes[$attribute])
3065 continue;
3068 settype($group_attributes[$attribute], $type);
3069 $sql_ary[str_replace('group_', 'user_', $attribute)] = $group_attributes[$attribute];
3073 // Before we update the user attributes, we will make a list of those having now the group avatar assigned
3074 if (isset($sql_ary['user_avatar']))
3076 // Ok, get the original avatar data from users having an uploaded one (we need to remove these from the filesystem)
3077 $sql = 'SELECT user_id, group_id, user_avatar
3078 FROM ' . USERS_TABLE . '
3079 WHERE ' . $db->sql_in_set('user_id', $user_id_ary) . '
3080 AND user_avatar_type = ' . AVATAR_UPLOAD;
3081 $result = $db->sql_query($sql);
3083 while ($row = $db->sql_fetchrow($result))
3085 avatar_delete('user', $row);
3087 $db->sql_freeresult($result);
3089 else
3091 unset($sql_ary['user_avatar_type']);
3092 unset($sql_ary['user_avatar_height']);
3093 unset($sql_ary['user_avatar_width']);
3096 $sql = 'UPDATE ' . USERS_TABLE . ' SET ' . $db->sql_build_array('UPDATE', $sql_ary) . '
3097 WHERE ' . $db->sql_in_set('user_id', $user_id_ary);
3098 $db->sql_query($sql);
3100 if (isset($sql_ary['user_colour']))
3102 // Update any cached colour information for these users
3103 $sql = 'UPDATE ' . FORUMS_TABLE . " SET forum_last_poster_colour = '" . $db->sql_escape($sql_ary['user_colour']) . "'
3104 WHERE " . $db->sql_in_set('forum_last_poster_id', $user_id_ary);
3105 $db->sql_query($sql);
3107 $sql = 'UPDATE ' . TOPICS_TABLE . " SET topic_first_poster_colour = '" . $db->sql_escape($sql_ary['user_colour']) . "'
3108 WHERE " . $db->sql_in_set('topic_poster', $user_id_ary);
3109 $db->sql_query($sql);
3111 $sql = 'UPDATE ' . TOPICS_TABLE . " SET topic_last_poster_colour = '" . $db->sql_escape($sql_ary['user_colour']) . "'
3112 WHERE " . $db->sql_in_set('topic_last_poster_id', $user_id_ary);
3113 $db->sql_query($sql);
3115 global $config;
3117 if (in_array($config['newest_user_id'], $user_id_ary))
3119 set_config('newest_user_colour', $sql_ary['user_colour'], true);
3123 if ($update_listing)
3125 group_update_listings($group_id);
3130 * Get group name
3132 function get_group_name($group_id)
3134 global $db, $user;
3136 $sql = 'SELECT group_name, group_type
3137 FROM ' . GROUPS_TABLE . '
3138 WHERE group_id = ' . (int) $group_id;
3139 $result = $db->sql_query($sql);
3140 $row = $db->sql_fetchrow($result);
3141 $db->sql_freeresult($result);
3143 if (!$row)
3145 return '';
3148 return ($row['group_type'] == GROUP_SPECIAL) ? $user->lang['G_' . $row['group_name']] : $row['group_name'];
3152 * Obtain either the members of a specified group, the groups the specified user is subscribed to
3153 * or checking if a specified user is in a specified group. This function does not return pending memberships.
3155 * Note: Never use this more than once... first group your users/groups
3157 function group_memberships($group_id_ary = false, $user_id_ary = false, $return_bool = false)
3159 global $db;
3161 if (!$group_id_ary && !$user_id_ary)
3163 return true;
3166 if ($user_id_ary)
3168 $user_id_ary = (!is_array($user_id_ary)) ? array($user_id_ary) : $user_id_ary;
3171 if ($group_id_ary)
3173 $group_id_ary = (!is_array($group_id_ary)) ? array($group_id_ary) : $group_id_ary;
3176 $sql = 'SELECT ug.*, u.username, u.username_clean, u.user_email
3177 FROM ' . USER_GROUP_TABLE . ' ug, ' . USERS_TABLE . ' u
3178 WHERE ug.user_id = u.user_id
3179 AND ug.user_pending = 0 AND ';
3181 if ($group_id_ary)
3183 $sql .= ' ' . $db->sql_in_set('ug.group_id', $group_id_ary);
3186 if ($user_id_ary)
3188 $sql .= ($group_id_ary) ? ' AND ' : ' ';
3189 $sql .= $db->sql_in_set('ug.user_id', $user_id_ary);
3192 $result = ($return_bool) ? $db->sql_query_limit($sql, 1) : $db->sql_query($sql);
3194 $row = $db->sql_fetchrow($result);
3196 if ($return_bool)
3198 $db->sql_freeresult($result);
3199 return ($row) ? true : false;
3202 if (!$row)
3204 return false;
3207 $return = array();
3211 $return[] = $row;
3213 while ($row = $db->sql_fetchrow($result));
3215 $db->sql_freeresult($result);
3217 return $return;
3221 * Re-cache moderators and foes if group has a_ or m_ permissions
3223 function group_update_listings($group_id)
3225 global $auth;
3227 $hold_ary = $auth->acl_group_raw_data($group_id, array('a_', 'm_'));
3229 if (!sizeof($hold_ary))
3231 return;
3234 $mod_permissions = $admin_permissions = false;
3236 foreach ($hold_ary as $g_id => $forum_ary)
3238 foreach ($forum_ary as $forum_id => $auth_ary)
3240 foreach ($auth_ary as $auth_option => $setting)
3242 if ($mod_permissions && $admin_permissions)
3244 break 3;
3247 if ($setting != ACL_YES)
3249 continue;
3252 if ($auth_option == 'm_')
3254 $mod_permissions = true;
3257 if ($auth_option == 'a_')
3259 $admin_permissions = true;
3265 if ($mod_permissions)
3267 if (!function_exists('cache_moderators'))
3269 include(PHPBB_ROOT_PATH . 'includes/functions_admin.' . PHP_EXT);
3271 cache_moderators();
3274 if ($mod_permissions || $admin_permissions)
3276 if (!function_exists('update_foes'))
3278 include(PHPBB_ROOT_PATH . 'includes/functions_admin.' . PHP_EXT);
3280 update_foes(array($group_id));