merge? merge.
[phpbb.git] / phpBB / includes / functions_user.php
blobba77a56562bbfb06dff288b19ccb5586b9459f7a
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 an 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 $db->sql_transaction('begin');
319 // Before we begin, we will remove the reports the user issued.
320 $sql = 'SELECT r.post_id, p.topic_id
321 FROM ' . REPORTS_TABLE . ' r, ' . POSTS_TABLE . ' p
322 WHERE r.user_id = ' . $user_id . '
323 AND p.post_id = r.post_id';
324 $result = $db->sql_query($sql);
326 $report_posts = $report_topics = array();
327 while ($row = $db->sql_fetchrow($result))
329 $report_posts[] = $row['post_id'];
330 $report_topics[] = $row['topic_id'];
332 $db->sql_freeresult($result);
334 if (sizeof($report_posts))
336 $report_posts = array_unique($report_posts);
337 $report_topics = array_unique($report_topics);
339 // Get a list of topics that still contain reported posts
340 $sql = 'SELECT DISTINCT topic_id
341 FROM ' . POSTS_TABLE . '
342 WHERE ' . $db->sql_in_set('topic_id', $report_topics) . '
343 AND post_reported = 1
344 AND ' . $db->sql_in_set('post_id', $report_posts, true);
345 $result = $db->sql_query($sql);
347 $keep_report_topics = array();
348 while ($row = $db->sql_fetchrow($result))
350 $keep_report_topics[] = $row['topic_id'];
352 $db->sql_freeresult($result);
354 if (sizeof($keep_report_topics))
356 $report_topics = array_diff($report_topics, $keep_report_topics);
358 unset($keep_report_topics);
360 // Now set the flags back
361 $sql = 'UPDATE ' . POSTS_TABLE . '
362 SET post_reported = 0
363 WHERE ' . $db->sql_in_set('post_id', $report_posts);
364 $db->sql_query($sql);
366 if (sizeof($report_topics))
368 $sql = 'UPDATE ' . TOPICS_TABLE . '
369 SET topic_reported = 0
370 WHERE ' . $db->sql_in_set('topic_id', $report_topics);
371 $db->sql_query($sql);
375 // Remove reports
376 $db->sql_query('DELETE FROM ' . REPORTS_TABLE . ' WHERE user_id = ' . $user_id);
378 if ($user_row['user_avatar'] && $user_row['user_avatar_type'] == AVATAR_UPLOAD)
380 avatar_delete('user', $user_row);
383 switch ($mode)
385 case 'retain':
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);
434 break;
436 case 'remove':
438 if (!function_exists('delete_posts'))
440 include(PHPBB_ROOT_PATH . 'includes/functions_admin.' . PHP_EXT);
443 $sql = 'SELECT topic_id, COUNT(post_id) AS total_posts
444 FROM ' . POSTS_TABLE . "
445 WHERE poster_id = $user_id
446 GROUP BY topic_id";
447 $result = $db->sql_query($sql);
449 $topic_id_ary = array();
450 while ($row = $db->sql_fetchrow($result))
452 $topic_id_ary[$row['topic_id']] = $row['total_posts'];
454 $db->sql_freeresult($result);
456 if (sizeof($topic_id_ary))
458 $sql = 'SELECT topic_id, topic_replies, topic_replies_real
459 FROM ' . TOPICS_TABLE . '
460 WHERE ' . $db->sql_in_set('topic_id', array_keys($topic_id_ary));
461 $result = $db->sql_query($sql);
463 $del_topic_ary = array();
464 while ($row = $db->sql_fetchrow($result))
466 if (max($row['topic_replies'], $row['topic_replies_real']) + 1 == $topic_id_ary[$row['topic_id']])
468 $del_topic_ary[] = $row['topic_id'];
471 $db->sql_freeresult($result);
473 if (sizeof($del_topic_ary))
475 $sql = 'DELETE FROM ' . TOPICS_TABLE . '
476 WHERE ' . $db->sql_in_set('topic_id', $del_topic_ary);
477 $db->sql_query($sql);
481 // Delete posts, attachments, etc.
482 delete_posts('poster_id', $user_id);
484 break;
487 $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);
489 foreach ($table_ary as $table)
491 $sql = "DELETE FROM $table
492 WHERE user_id = $user_id";
493 $db->sql_query($sql);
496 $cache->destroy('sql', MODERATOR_CACHE_TABLE);
498 // Remove any undelivered mails...
499 $sql = 'SELECT msg_id, user_id
500 FROM ' . PRIVMSGS_TO_TABLE . '
501 WHERE author_id = ' . $user_id . '
502 AND folder_id = ' . PRIVMSGS_NO_BOX;
503 $result = $db->sql_query($sql);
505 $undelivered_msg = $undelivered_user = array();
506 while ($row = $db->sql_fetchrow($result))
508 $undelivered_msg[] = $row['msg_id'];
509 $undelivered_user[$row['user_id']][] = true;
511 $db->sql_freeresult($result);
513 if (sizeof($undelivered_msg))
515 $sql = 'DELETE FROM ' . PRIVMSGS_TABLE . '
516 WHERE ' . $db->sql_in_set('msg_id', $undelivered_msg);
517 $db->sql_query($sql);
520 $sql = 'DELETE FROM ' . PRIVMSGS_TO_TABLE . '
521 WHERE author_id = ' . $user_id . '
522 AND folder_id = ' . PRIVMSGS_NO_BOX;
523 $db->sql_query($sql);
525 // Delete all to-information
526 $sql = 'DELETE FROM ' . PRIVMSGS_TO_TABLE . '
527 WHERE user_id = ' . $user_id;
528 $db->sql_query($sql);
530 // Set the remaining author id to anonymous - this way users are still able to read messages from users being removed
531 $sql = 'UPDATE ' . PRIVMSGS_TO_TABLE . '
532 SET author_id = ' . ANONYMOUS . '
533 WHERE author_id = ' . $user_id;
534 $db->sql_query($sql);
536 $sql = 'UPDATE ' . PRIVMSGS_TABLE . '
537 SET author_id = ' . ANONYMOUS . '
538 WHERE author_id = ' . $user_id;
539 $db->sql_query($sql);
541 foreach ($undelivered_user as $_user_id => $ary)
543 if ($_user_id == $user_id)
545 continue;
548 $sql = 'UPDATE ' . USERS_TABLE . '
549 SET user_new_privmsg = user_new_privmsg - ' . sizeof($ary) . ',
550 user_unread_privmsg = user_unread_privmsg - ' . sizeof($ary) . '
551 WHERE user_id = ' . $_user_id;
552 $db->sql_query($sql);
555 // Reset newest user info if appropriate
556 if ($config['newest_user_id'] == $user_id)
558 update_last_username();
561 // Decrement number of users if this user is active
562 if ($user_row['user_type'] != USER_INACTIVE && $user_row['user_type'] != USER_IGNORE)
564 set_config('num_users', $config['num_users'] - 1, true);
567 $db->sql_transaction('commit');
569 return false;
573 * Flips user_type from active to inactive and vice versa, handles group membership updates
575 * @param string $mode can be flip for flipping from active/inactive, activate or deactivate
577 function user_active_flip($mode, $user_id_ary, $reason = INACTIVE_MANUAL)
579 global $config, $db, $user, $auth;
581 $deactivated = $activated = 0;
582 $sql_statements = array();
584 if (!is_array($user_id_ary))
586 $user_id_ary = array($user_id_ary);
589 if (!sizeof($user_id_ary))
591 return;
594 $sql = 'SELECT user_id, group_id, user_type, user_inactive_reason
595 FROM ' . USERS_TABLE . '
596 WHERE ' . $db->sql_in_set('user_id', $user_id_ary);
597 $result = $db->sql_query($sql);
599 while ($row = $db->sql_fetchrow($result))
601 $sql_ary = array();
603 if ($row['user_type'] == USER_IGNORE || $row['user_type'] == USER_FOUNDER ||
604 ($mode == 'activate' && $row['user_type'] != USER_INACTIVE) ||
605 ($mode == 'deactivate' && $row['user_type'] == USER_INACTIVE))
607 continue;
610 if ($row['user_type'] == USER_INACTIVE)
612 $activated++;
614 else
616 $deactivated++;
618 // Remove the users session key...
619 $user->reset_login_keys($row['user_id']);
622 $sql_ary += array(
623 'user_type' => ($row['user_type'] == USER_NORMAL) ? USER_INACTIVE : USER_NORMAL,
624 'user_inactive_time' => ($row['user_type'] == USER_NORMAL) ? time() : 0,
625 'user_inactive_reason' => ($row['user_type'] == USER_NORMAL) ? $reason : 0,
628 $sql_statements[$row['user_id']] = $sql_ary;
630 $db->sql_freeresult($result);
632 if (sizeof($sql_statements))
634 foreach ($sql_statements as $user_id => $sql_ary)
636 $sql = 'UPDATE ' . USERS_TABLE . '
637 SET ' . $db->sql_build_array('UPDATE', $sql_ary) . '
638 WHERE user_id = ' . $user_id;
639 $db->sql_query($sql);
642 $auth->acl_clear_prefetch(array_keys($sql_statements));
645 if ($deactivated)
647 set_config('num_users', $config['num_users'] - $deactivated, true);
650 if ($activated)
652 set_config('num_users', $config['num_users'] + $activated, true);
655 // Update latest username
656 update_last_username();
660 * Add a ban or ban exclusion to the banlist. Bans either a user, an IP or an email address
662 * @param string $mode Type of ban. One of the following: user, ip, email
663 * @param mixed $ban Banned entity. Either string or array with usernames, ips or email addresses
664 * @param int $ban_len Ban length in minutes
665 * @param string $ban_len_other Ban length as a date (YYYY-MM-DD)
666 * @param boolean $ban_exclude Exclude these entities from banning?
667 * @param string $ban_reason String describing the reason for this ban
668 * @return boolean
670 function user_ban($mode, $ban, $ban_len, $ban_len_other, $ban_exclude, $ban_reason, $ban_give_reason = '')
672 global $db, $user, $auth, $cache;
674 // Delete stale bans
675 $sql = 'DELETE FROM ' . BANLIST_TABLE . '
676 WHERE ban_end < ' . time() . '
677 AND ban_end <> 0';
678 $db->sql_query($sql);
680 $ban_list = (!is_array($ban)) ? array_unique(explode("\n", $ban)) : $ban;
681 $ban_list_log = implode(', ', $ban_list);
683 $current_time = time();
685 // Set $ban_end to the unix time when the ban should end. 0 is a permanent ban.
686 if ($ban_len)
688 if ($ban_len != -1 || !$ban_len_other)
690 $ban_end = max($current_time, $current_time + ($ban_len) * 60);
692 else
694 $ban_other = explode('-', $ban_len_other);
695 if (sizeof($ban_other) == 3 && ((int)$ban_other[0] < 9999) &&
696 (strlen($ban_other[0]) == 4) && (strlen($ban_other[1]) == 2) && (strlen($ban_other[2]) == 2))
698 $ban_end = max($current_time, gmmktime(0, 0, 0, (int)$ban_other[1], (int)$ban_other[2], (int)$ban_other[0]));
700 else
702 trigger_error('LENGTH_BAN_INVALID');
706 else
708 $ban_end = 0;
711 $founder = $founder_names = array();
713 if (!$ban_exclude)
715 // Create a list of founder...
716 $sql = 'SELECT user_id, user_email, username_clean
717 FROM ' . USERS_TABLE . '
718 WHERE user_type = ' . USER_FOUNDER;
719 $result = $db->sql_query($sql);
721 while ($row = $db->sql_fetchrow($result))
723 $founder[$row['user_id']] = $row['user_email'];
724 $founder_names[$row['user_id']] = $row['username_clean'];
726 $db->sql_freeresult($result);
729 $banlist_ary = array();
731 switch ($mode)
733 case 'user':
734 $type = 'ban_userid';
736 // At the moment we do not support wildcard username banning
738 // Select the relevant user_ids.
739 $sql_usernames = array();
741 foreach ($ban_list as $username)
743 $username = trim($username);
744 if ($username != '')
746 $clean_name = utf8_clean_string($username);
747 if ($clean_name == $user->data['username_clean'])
749 trigger_error('CANNOT_BAN_YOURSELF', E_USER_WARNING);
751 if (in_array($clean_name, $founder_names))
753 trigger_error('CANNOT_BAN_FOUNDER', E_USER_WARNING);
755 $sql_usernames[] = $clean_name;
759 // Make sure we have been given someone to ban
760 if (!sizeof($sql_usernames))
762 trigger_error('NO_USER_SPECIFIED');
765 $sql = 'SELECT user_id
766 FROM ' . USERS_TABLE . '
767 WHERE ' . $db->sql_in_set('username_clean', $sql_usernames);
769 // Do not allow banning yourself
770 if (sizeof($founder))
772 $sql .= ' AND ' . $db->sql_in_set('user_id', array_merge(array_keys($founder), array($user->data['user_id'])), true);
774 else
776 $sql .= ' AND user_id <> ' . $user->data['user_id'];
779 $result = $db->sql_query($sql);
781 if ($row = $db->sql_fetchrow($result))
785 $banlist_ary[] = (int) $row['user_id'];
787 while ($row = $db->sql_fetchrow($result));
789 else
791 $db->sql_freeresult($result);
792 trigger_error('NO_USERS');
794 $db->sql_freeresult($result);
795 break;
797 case 'ip':
798 $type = 'ban_ip';
800 foreach ($ban_list as $ban_item)
802 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))
804 // This is an IP range
805 // Don't ask about all this, just don't ask ... !
806 $ip_1_counter = $ip_range_explode[1];
807 $ip_1_end = $ip_range_explode[5];
809 while ($ip_1_counter <= $ip_1_end)
811 $ip_2_counter = ($ip_1_counter == $ip_range_explode[1]) ? $ip_range_explode[2] : 0;
812 $ip_2_end = ($ip_1_counter < $ip_1_end) ? 254 : $ip_range_explode[6];
814 if ($ip_2_counter == 0 && $ip_2_end == 254)
816 $ip_2_counter = 256;
817 $ip_2_fragment = 256;
819 $banlist_ary[] = "$ip_1_counter.*";
822 while ($ip_2_counter <= $ip_2_end)
824 $ip_3_counter = ($ip_2_counter == $ip_range_explode[2] && $ip_1_counter == $ip_range_explode[1]) ? $ip_range_explode[3] : 0;
825 $ip_3_end = ($ip_2_counter < $ip_2_end || $ip_1_counter < $ip_1_end) ? 254 : $ip_range_explode[7];
827 if ($ip_3_counter == 0 && $ip_3_end == 254)
829 $ip_3_counter = 256;
830 $ip_3_fragment = 256;
832 $banlist_ary[] = "$ip_1_counter.$ip_2_counter.*";
835 while ($ip_3_counter <= $ip_3_end)
837 $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;
838 $ip_4_end = ($ip_3_counter < $ip_3_end || $ip_2_counter < $ip_2_end) ? 254 : $ip_range_explode[8];
840 if ($ip_4_counter == 0 && $ip_4_end == 254)
842 $ip_4_counter = 256;
843 $ip_4_fragment = 256;
845 $banlist_ary[] = "$ip_1_counter.$ip_2_counter.$ip_3_counter.*";
848 while ($ip_4_counter <= $ip_4_end)
850 $banlist_ary[] = "$ip_1_counter.$ip_2_counter.$ip_3_counter.$ip_4_counter";
851 $ip_4_counter++;
853 $ip_3_counter++;
855 $ip_2_counter++;
857 $ip_1_counter++;
860 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)))
862 // Normal IP address
863 $banlist_ary[] = trim($ban_item);
865 else if (preg_match('#^\*$#', trim($ban_item)))
867 // Ban all IPs
868 $banlist_ary[] = '*';
870 else if (preg_match('#^([\w\-_]\.?){2,}$#is', trim($ban_item)))
872 // hostname
873 $ip_ary = gethostbynamel(trim($ban_item));
875 if (!empty($ip_ary))
877 foreach ($ip_ary as $ip)
879 if ($ip)
881 if (strlen($ip) > 40)
883 continue;
886 $banlist_ary[] = $ip;
891 else
893 trigger_error('NO_IPS_DEFINED');
896 break;
898 case 'email':
899 $type = 'ban_email';
901 foreach ($ban_list as $ban_item)
903 $ban_item = trim($ban_item);
905 if (preg_match('#^.*?@*|(([a-z0-9\-]+\.)+([a-z]{2,3}))$#i', $ban_item))
907 if (strlen($ban_item) > 100)
909 continue;
912 if (!sizeof($founder) || !in_array($ban_item, $founder))
914 $banlist_ary[] = $ban_item;
919 if (sizeof($ban_list) == 0)
921 trigger_error('NO_EMAILS_DEFINED');
923 break;
925 default:
926 trigger_error('NO_MODE');
927 break;
930 // Fetch currently set bans of the specified type and exclude state. Prevent duplicate bans.
931 $sql_where = ($type == 'ban_userid') ? 'ban_userid <> 0' : "$type <> ''";
933 $sql = "SELECT $type
934 FROM " . BANLIST_TABLE . "
935 WHERE $sql_where
936 AND ban_exclude = $ban_exclude";
937 $result = $db->sql_query($sql);
939 // Reset $sql_where, because we use it later...
940 $sql_where = '';
942 if ($row = $db->sql_fetchrow($result))
944 $banlist_ary_tmp = array();
947 switch ($mode)
949 case 'user':
950 $banlist_ary_tmp[] = $row['ban_userid'];
951 break;
953 case 'ip':
954 $banlist_ary_tmp[] = $row['ban_ip'];
955 break;
957 case 'email':
958 $banlist_ary_tmp[] = $row['ban_email'];
959 break;
962 while ($row = $db->sql_fetchrow($result));
964 $banlist_ary = array_unique(array_diff($banlist_ary, $banlist_ary_tmp));
965 unset($banlist_ary_tmp);
967 $db->sql_freeresult($result);
969 // We have some entities to ban
970 if (sizeof($banlist_ary))
972 $sql_ary = array();
974 foreach ($banlist_ary as $ban_entry)
976 $sql_ary[] = array(
977 $type => $ban_entry,
978 'ban_start' => (int) $current_time,
979 'ban_end' => (int) $ban_end,
980 'ban_exclude' => (int) $ban_exclude,
981 'ban_reason' => (string) $ban_reason,
982 'ban_give_reason' => (string) $ban_give_reason,
986 $db->sql_multi_insert(BANLIST_TABLE, $sql_ary);
988 // If we are banning we want to logout anyone matching the ban
989 if (!$ban_exclude)
991 switch ($mode)
993 case 'user':
994 $sql_where = 'WHERE ' . $db->sql_in_set('session_user_id', $banlist_ary);
995 break;
997 case 'ip':
998 $sql_where = 'WHERE ' . $db->sql_in_set('session_ip', $banlist_ary);
999 break;
1001 case 'email':
1002 $banlist_ary_sql = array();
1004 foreach ($banlist_ary as $ban_entry)
1006 $banlist_ary_sql[] = (string) str_replace('*', '%', $ban_entry);
1009 $sql = 'SELECT user_id
1010 FROM ' . USERS_TABLE . '
1011 WHERE ' . $db->sql_in_set('user_email', $banlist_ary_sql);
1012 $result = $db->sql_query($sql);
1014 $sql_in = array();
1016 if ($row = $db->sql_fetchrow($result))
1020 $sql_in[] = $row['user_id'];
1022 while ($row = $db->sql_fetchrow($result));
1024 $sql_where = 'WHERE ' . $db->sql_in_set('session_user_id', $sql_in);
1026 $db->sql_freeresult($result);
1027 break;
1030 if (isset($sql_where) && $sql_where)
1032 $sql = 'DELETE FROM ' . SESSIONS_TABLE . "
1033 $sql_where";
1034 $db->sql_query($sql);
1036 if ($mode == 'user')
1038 $sql = 'DELETE FROM ' . SESSIONS_KEYS_TABLE . ' ' . ((in_array('*', $banlist_ary)) ? '' : 'WHERE ' . $db->sql_in_set('user_id', $banlist_ary));
1039 $db->sql_query($sql);
1044 // Update log
1045 $log_entry = ($ban_exclude) ? 'LOG_BAN_EXCLUDE_' : 'LOG_BAN_';
1047 // Add to moderator and admin log
1048 add_log('admin', $log_entry . strtoupper($mode), $ban_reason, $ban_list_log);
1049 add_log('mod', 0, 0, $log_entry . strtoupper($mode), $ban_reason, $ban_list_log);
1051 $cache->destroy('sql', BANLIST_TABLE);
1053 return true;
1056 // There was nothing to ban/exclude. But destroying the cache because of the removal of stale bans.
1057 $cache->destroy('sql', BANLIST_TABLE);
1059 return false;
1063 * Unban User
1065 function user_unban($mode, $ban)
1067 global $db, $user, $auth, $cache;
1069 // Delete stale bans
1070 $sql = 'DELETE FROM ' . BANLIST_TABLE . '
1071 WHERE ban_end < ' . time() . '
1072 AND ban_end <> 0';
1073 $db->sql_query($sql);
1075 if (!is_array($ban))
1077 $ban = array($ban);
1080 $unban_sql = array_map('intval', $ban);
1082 if (sizeof($unban_sql))
1084 // Grab details of bans for logging information later
1085 switch ($mode)
1087 case 'user':
1088 $sql = 'SELECT u.username AS unban_info
1089 FROM ' . USERS_TABLE . ' u, ' . BANLIST_TABLE . ' b
1090 WHERE ' . $db->sql_in_set('b.ban_id', $unban_sql) . '
1091 AND u.user_id = b.ban_userid';
1092 break;
1094 case 'email':
1095 $sql = 'SELECT ban_email AS unban_info
1096 FROM ' . BANLIST_TABLE . '
1097 WHERE ' . $db->sql_in_set('ban_id', $unban_sql);
1098 break;
1100 case 'ip':
1101 $sql = 'SELECT ban_ip AS unban_info
1102 FROM ' . BANLIST_TABLE . '
1103 WHERE ' . $db->sql_in_set('ban_id', $unban_sql);
1104 break;
1106 $result = $db->sql_query($sql);
1108 $l_unban_list = '';
1109 while ($row = $db->sql_fetchrow($result))
1111 $l_unban_list .= (($l_unban_list != '') ? ', ' : '') . $row['unban_info'];
1113 $db->sql_freeresult($result);
1115 $sql = 'DELETE FROM ' . BANLIST_TABLE . '
1116 WHERE ' . $db->sql_in_set('ban_id', $unban_sql);
1117 $db->sql_query($sql);
1119 // Add to moderator and admin log
1120 add_log('admin', 'LOG_UNBAN_' . strtoupper($mode), $l_unban_list);
1121 add_log('mod', 0, 0, 'LOG_UNBAN_' . strtoupper($mode), $l_unban_list);
1124 $cache->destroy('sql', BANLIST_TABLE);
1126 return false;
1130 * Whois facility
1132 function user_ipwhois($ip)
1134 $ipwhois = '';
1136 // Check IP
1137 // Only supporting IPv4 at the moment...
1138 if (empty($ip) || !preg_match(get_preg_expression('ipv4'), $ip))
1140 return '';
1143 $match = array(
1144 '#RIPE\.NET#is' => 'whois.ripe.net',
1145 '#whois\.apnic\.net#is' => 'whois.apnic.net',
1146 '#nic\.ad\.jp#is' => 'whois.nic.ad.jp',
1147 '#whois\.registro\.br#is' => 'whois.registro.br'
1150 if (($fsk = @fsockopen('whois.arin.net', 43)))
1152 fputs($fsk, "$ip\n");
1153 while (!feof($fsk))
1155 $ipwhois .= fgets($fsk, 1024);
1157 @fclose($fsk);
1160 foreach (array_keys($match) as $server)
1162 if (preg_match($server, $ipwhois))
1164 $ipwhois = '';
1165 if (($fsk = @fsockopen($match[$server], 43)))
1167 fputs($fsk, "$ip\n");
1168 while (!feof($fsk))
1170 $ipwhois .= fgets($fsk, 1024);
1172 @fclose($fsk);
1174 break;
1178 $ipwhois = htmlspecialchars($ipwhois);
1180 // Magic URL ;)
1181 return trim(make_clickable($ipwhois, false, ''));
1185 * Data validation ... used primarily but not exclusively by ucp modules
1187 * "Master" function for validating a range of data types
1189 function validate_data($data, $val_ary)
1191 global $user;
1193 $error = array();
1195 foreach ($val_ary as $var => $val_seq)
1197 if (!is_array($val_seq[0]))
1199 $val_seq = array($val_seq);
1202 foreach ($val_seq as $validate)
1204 $function = array_shift($validate);
1205 array_unshift($validate, $data[$var]);
1207 if ($result = call_user_func_array('validate_' . $function, $validate))
1209 // Since errors are checked later for their language file existence, we need to make sure custom errors are not adjusted.
1210 $error[] = (empty($user->lang[$result . '_' . strtoupper($var)])) ? $result : $result . '_' . strtoupper($var);
1215 return $error;
1219 * Validate String
1221 * @return boolean|string Either false if validation succeeded or a string which will be used as the error message (with the variable name appended)
1223 function validate_string($string, $optional = false, $min = 0, $max = 0)
1225 if (empty($string) && $optional)
1227 return false;
1230 if ($min && utf8_strlen(htmlspecialchars_decode($string)) < $min)
1232 return 'TOO_SHORT';
1234 else if ($max && utf8_strlen(htmlspecialchars_decode($string)) > $max)
1236 return 'TOO_LONG';
1239 return false;
1243 * Validate Number
1245 * @return boolean|string Either false if validation succeeded or a string which will be used as the error message (with the variable name appended)
1247 function validate_num($num, $optional = false, $min = 0, $max = 1E99)
1249 if (empty($num) && $optional)
1251 return false;
1254 if ($num < $min)
1256 return 'TOO_SMALL';
1258 else if ($num > $max)
1260 return 'TOO_LARGE';
1263 return false;
1267 * Validate Date
1268 * @param String $string a date in the dd-mm-yyyy format
1269 * @return boolean
1271 function validate_date($date_string, $optional = false)
1273 $date = explode('-', $date_string);
1274 if ((empty($date) || sizeof($date) != 3) && $optional)
1276 return false;
1278 else if ($optional)
1280 for ($field = 0; $field <= 1; $field++)
1282 $date[$field] = (int) $date[$field];
1283 if (empty($date[$field]))
1285 $date[$field] = 1;
1288 $date[2] = (int) $date[2];
1289 // assume an arbitrary leap year
1290 if (empty($date[2]))
1292 $date[2] = 1980;
1296 if (sizeof($date) != 3 || !checkdate($date[1], $date[0], $date[2]))
1298 return 'INVALID';
1301 return false;
1306 * Validate Match
1308 * @return boolean|string Either false if validation succeeded or a string which will be used as the error message (with the variable name appended)
1310 function validate_match($string, $optional = false, $match = '')
1312 if (empty($string) && $optional)
1314 return false;
1317 if (empty($match))
1319 return false;
1322 if (!preg_match($match, $string))
1324 return 'WRONG_DATA';
1327 return false;
1331 * Check to see if the username has been taken, or if it is disallowed.
1332 * Also checks if it includes the " character, which we don't allow in usernames.
1333 * Used for registering, changing names, and posting anonymously with a username
1335 * @param string $username The username to check
1336 * @param string $allowed_username An allowed username, default being $user->data['username']
1338 * @return mixed Either false if validation succeeded or a string which will be used as the error message (with the variable name appended)
1340 function validate_username($username, $allowed_username = false)
1342 global $config, $db, $user, $cache;
1344 $clean_username = utf8_clean_string($username);
1345 $allowed_username = ($allowed_username === false) ? $user->data['username_clean'] : utf8_clean_string($allowed_username);
1347 if ($allowed_username == $clean_username)
1349 return false;
1352 // ... fast checks first.
1353 if (strpos($username, '&quot;') !== false || strpos($username, '"') !== false || empty($clean_username))
1355 return 'INVALID_CHARS';
1358 $mbstring = $pcre = false;
1360 // generic UTF-8 character types supported
1361 switch ($config['allow_name_chars'])
1363 case 'USERNAME_CHARS_ANY':
1364 $regex = '.+';
1365 break;
1367 case 'USERNAME_ALPHA_ONLY':
1368 $regex = '[A-Za-z0-9]+';
1369 break;
1371 case 'USERNAME_ALPHA_SPACERS':
1372 $regex = '[A-Za-z0-9-[\]_+ ]+';
1373 break;
1375 case 'USERNAME_LETTER_NUM':
1376 $regex = '[\p{Lu}\p{Ll}\p{N}]+';
1377 break;
1379 case 'USERNAME_LETTER_NUM_SPACERS':
1380 $regex = '[-\]_+ [\p{Lu}\p{Ll}\p{N}]+';
1381 break;
1383 case 'USERNAME_ASCII':
1384 default:
1385 $regex = '[\x01-\x7F]+';
1386 break;
1389 if (!preg_match('#^' . $regex . '$#u', $username))
1391 return 'INVALID_CHARS';
1394 $sql = 'SELECT username
1395 FROM ' . USERS_TABLE . "
1396 WHERE username_clean = '" . $db->sql_escape($clean_username) . "'";
1397 $result = $db->sql_query($sql);
1398 $row = $db->sql_fetchrow($result);
1399 $db->sql_freeresult($result);
1401 if ($row)
1403 return 'USERNAME_TAKEN';
1406 $sql = 'SELECT group_name
1407 FROM ' . GROUPS_TABLE . "
1408 WHERE group_name = '" . $db->sql_escape($clean_username) . "'";
1409 $result = $db->sql_query($sql);
1410 $row = $db->sql_fetchrow($result);
1411 $db->sql_freeresult($result);
1413 if ($row)
1415 return 'USERNAME_TAKEN';
1418 $bad_usernames = cache::obtain_disallowed_usernames();
1420 foreach ($bad_usernames as $bad_username)
1422 if (preg_match('#^' . $bad_username . '$#', $clean_username))
1424 return 'USERNAME_DISALLOWED';
1428 return false;
1432 * Check to see if the password meets the complexity settings
1434 * @return boolean|string Either false if validation succeeded or a string which will be used as the error message (with the variable name appended)
1436 function validate_password($password)
1438 global $config, $db, $user;
1440 if (!$password)
1442 return false;
1445 // generic UTF-8 character types supported
1446 $upp = '\p{Lu}';
1447 $low = '\p{Ll}';
1448 $let = '\p{L}';
1449 $num = '\p{N}';
1450 $sym = '[^\p{Lu}\p{Ll}\p{N}]';
1452 $chars = array();
1454 switch ($config['pass_complex'])
1456 case 'PASS_TYPE_CASE':
1457 $chars[] = $low;
1458 $chars[] = $upp;
1459 break;
1461 case 'PASS_TYPE_ALPHA':
1462 $chars[] = $let;
1463 $chars[] = $num;
1464 break;
1466 case 'PASS_TYPE_SYMBOL':
1467 $chars[] = $low;
1468 $chars[] = $upp;
1469 $chars[] = $num;
1470 $chars[] = $sym;
1471 break;
1474 if ($pcre)
1476 foreach ($chars as $char)
1478 if (!preg_match('#' . $char . '#u', $password))
1480 return 'INVALID_CHARS';
1484 else if ($mbstring)
1486 foreach ($chars as $char)
1488 if (mb_ereg($char, $password) === false)
1490 return 'INVALID_CHARS';
1495 return false;
1499 * Check to see if email address is banned or already present in the DB
1501 * @param string $email The email to check
1502 * @param string $allowed_email An allowed email, default being $user->data['user_email']
1504 * @return mixed Either false if validation succeeded or a string which will be used as the error message (with the variable name appended)
1506 function validate_email($email, $allowed_email = false)
1508 global $config, $db, $user;
1510 $email = strtolower($email);
1511 $allowed_email = ($allowed_email === false) ? strtolower($user->data['user_email']) : strtolower($allowed_email);
1513 if ($allowed_email == $email)
1515 return false;
1518 if (!preg_match('/^' . get_preg_expression('email') . '$/i', $email))
1520 return 'EMAIL_INVALID';
1523 // Check MX record.
1524 // The idea for this is from reading the UseBB blog/announcement. :)
1525 if ($config['email_check_mx'])
1527 list(, $domain) = explode('@', $email);
1529 if (phpbb_checkdnsrr($domain, 'A') === false && phpbb_checkdnsrr($domain, 'MX') === false)
1531 return 'DOMAIN_NO_MX_RECORD';
1535 if (($ban_reason = $user->check_ban(false, false, $email, true)) !== false)
1537 return ($ban_reason === true) ? 'EMAIL_BANNED' : $ban_reason;
1540 if (!$config['allow_emailreuse'])
1542 $sql = 'SELECT user_email_hash
1543 FROM ' . USERS_TABLE . "
1544 WHERE user_email_hash = " . hexdec(crc32($email) . strlen($email));
1545 $result = $db->sql_query($sql);
1546 $row = $db->sql_fetchrow($result);
1547 $db->sql_freeresult($result);
1549 if ($row)
1551 return 'EMAIL_TAKEN';
1555 return false;
1559 * Validate jabber address
1560 * Taken from the jabber class within flyspray (see author notes)
1562 * @author flyspray.org
1564 function validate_jabber($jid)
1566 if (!$jid)
1568 return false;
1571 $seperator_pos = strpos($jid, '@');
1573 if ($seperator_pos === false)
1575 return 'WRONG_DATA';
1578 $username = substr($jid, 0, $seperator_pos);
1579 $realm = substr($jid, $seperator_pos + 1);
1581 if (strlen($username) == 0 || strlen($realm) < 3)
1583 return 'WRONG_DATA';
1586 $arr = explode('.', $realm);
1588 if (sizeof($arr) == 0)
1590 return 'WRONG_DATA';
1593 foreach ($arr as $part)
1595 if (substr($part, 0, 1) == '-' || substr($part, -1, 1) == '-')
1597 return 'WRONG_DATA';
1600 if (!preg_match("@^[a-zA-Z0-9-.]+$@", $part))
1602 return 'WRONG_DATA';
1606 $boundary = array(array(0, 127), array(192, 223), array(224, 239), array(240, 247), array(248, 251), array(252, 253));
1608 // Prohibited Characters RFC3454 + RFC3920
1609 $prohibited = array(
1610 // Table C.1.1
1611 array(0x0020, 0x0020), // SPACE
1612 // Table C.1.2
1613 array(0x00A0, 0x00A0), // NO-BREAK SPACE
1614 array(0x1680, 0x1680), // OGHAM SPACE MARK
1615 array(0x2000, 0x2001), // EN QUAD
1616 array(0x2001, 0x2001), // EM QUAD
1617 array(0x2002, 0x2002), // EN SPACE
1618 array(0x2003, 0x2003), // EM SPACE
1619 array(0x2004, 0x2004), // THREE-PER-EM SPACE
1620 array(0x2005, 0x2005), // FOUR-PER-EM SPACE
1621 array(0x2006, 0x2006), // SIX-PER-EM SPACE
1622 array(0x2007, 0x2007), // FIGURE SPACE
1623 array(0x2008, 0x2008), // PUNCTUATION SPACE
1624 array(0x2009, 0x2009), // THIN SPACE
1625 array(0x200A, 0x200A), // HAIR SPACE
1626 array(0x200B, 0x200B), // ZERO WIDTH SPACE
1627 array(0x202F, 0x202F), // NARROW NO-BREAK SPACE
1628 array(0x205F, 0x205F), // MEDIUM MATHEMATICAL SPACE
1629 array(0x3000, 0x3000), // IDEOGRAPHIC SPACE
1630 // Table C.2.1
1631 array(0x0000, 0x001F), // [CONTROL CHARACTERS]
1632 array(0x007F, 0x007F), // DELETE
1633 // Table C.2.2
1634 array(0x0080, 0x009F), // [CONTROL CHARACTERS]
1635 array(0x06DD, 0x06DD), // ARABIC END OF AYAH
1636 array(0x070F, 0x070F), // SYRIAC ABBREVIATION MARK
1637 array(0x180E, 0x180E), // MONGOLIAN VOWEL SEPARATOR
1638 array(0x200C, 0x200C), // ZERO WIDTH NON-JOINER
1639 array(0x200D, 0x200D), // ZERO WIDTH JOINER
1640 array(0x2028, 0x2028), // LINE SEPARATOR
1641 array(0x2029, 0x2029), // PARAGRAPH SEPARATOR
1642 array(0x2060, 0x2060), // WORD JOINER
1643 array(0x2061, 0x2061), // FUNCTION APPLICATION
1644 array(0x2062, 0x2062), // INVISIBLE TIMES
1645 array(0x2063, 0x2063), // INVISIBLE SEPARATOR
1646 array(0x206A, 0x206F), // [CONTROL CHARACTERS]
1647 array(0xFEFF, 0xFEFF), // ZERO WIDTH NO-BREAK SPACE
1648 array(0xFFF9, 0xFFFC), // [CONTROL CHARACTERS]
1649 array(0x1D173, 0x1D17A), // [MUSICAL CONTROL CHARACTERS]
1650 // Table C.3
1651 array(0xE000, 0xF8FF), // [PRIVATE USE, PLANE 0]
1652 array(0xF0000, 0xFFFFD), // [PRIVATE USE, PLANE 15]
1653 array(0x100000, 0x10FFFD), // [PRIVATE USE, PLANE 16]
1654 // Table C.4
1655 array(0xFDD0, 0xFDEF), // [NONCHARACTER CODE POINTS]
1656 array(0xFFFE, 0xFFFF), // [NONCHARACTER CODE POINTS]
1657 array(0x1FFFE, 0x1FFFF), // [NONCHARACTER CODE POINTS]
1658 array(0x2FFFE, 0x2FFFF), // [NONCHARACTER CODE POINTS]
1659 array(0x3FFFE, 0x3FFFF), // [NONCHARACTER CODE POINTS]
1660 array(0x4FFFE, 0x4FFFF), // [NONCHARACTER CODE POINTS]
1661 array(0x5FFFE, 0x5FFFF), // [NONCHARACTER CODE POINTS]
1662 array(0x6FFFE, 0x6FFFF), // [NONCHARACTER CODE POINTS]
1663 array(0x7FFFE, 0x7FFFF), // [NONCHARACTER CODE POINTS]
1664 array(0x8FFFE, 0x8FFFF), // [NONCHARACTER CODE POINTS]
1665 array(0x9FFFE, 0x9FFFF), // [NONCHARACTER CODE POINTS]
1666 array(0xAFFFE, 0xAFFFF), // [NONCHARACTER CODE POINTS]
1667 array(0xBFFFE, 0xBFFFF), // [NONCHARACTER CODE POINTS]
1668 array(0xCFFFE, 0xCFFFF), // [NONCHARACTER CODE POINTS]
1669 array(0xDFFFE, 0xDFFFF), // [NONCHARACTER CODE POINTS]
1670 array(0xEFFFE, 0xEFFFF), // [NONCHARACTER CODE POINTS]
1671 array(0xFFFFE, 0xFFFFF), // [NONCHARACTER CODE POINTS]
1672 array(0x10FFFE, 0x10FFFF), // [NONCHARACTER CODE POINTS]
1673 // Table C.5
1674 array(0xD800, 0xDFFF), // [SURROGATE CODES]
1675 // Table C.6
1676 array(0xFFF9, 0xFFF9), // INTERLINEAR ANNOTATION ANCHOR
1677 array(0xFFFA, 0xFFFA), // INTERLINEAR ANNOTATION SEPARATOR
1678 array(0xFFFB, 0xFFFB), // INTERLINEAR ANNOTATION TERMINATOR
1679 array(0xFFFC, 0xFFFC), // OBJECT REPLACEMENT CHARACTER
1680 array(0xFFFD, 0xFFFD), // REPLACEMENT CHARACTER
1681 // Table C.7
1682 array(0x2FF0, 0x2FFB), // [IDEOGRAPHIC DESCRIPTION CHARACTERS]
1683 // Table C.8
1684 array(0x0340, 0x0340), // COMBINING GRAVE TONE MARK
1685 array(0x0341, 0x0341), // COMBINING ACUTE TONE MARK
1686 array(0x200E, 0x200E), // LEFT-TO-RIGHT MARK
1687 array(0x200F, 0x200F), // RIGHT-TO-LEFT MARK
1688 array(0x202A, 0x202A), // LEFT-TO-RIGHT EMBEDDING
1689 array(0x202B, 0x202B), // RIGHT-TO-LEFT EMBEDDING
1690 array(0x202C, 0x202C), // POP DIRECTIONAL FORMATTING
1691 array(0x202D, 0x202D), // LEFT-TO-RIGHT OVERRIDE
1692 array(0x202E, 0x202E), // RIGHT-TO-LEFT OVERRIDE
1693 array(0x206A, 0x206A), // INHIBIT SYMMETRIC SWAPPING
1694 array(0x206B, 0x206B), // ACTIVATE SYMMETRIC SWAPPING
1695 array(0x206C, 0x206C), // INHIBIT ARABIC FORM SHAPING
1696 array(0x206D, 0x206D), // ACTIVATE ARABIC FORM SHAPING
1697 array(0x206E, 0x206E), // NATIONAL DIGIT SHAPES
1698 array(0x206F, 0x206F), // NOMINAL DIGIT SHAPES
1699 // Table C.9
1700 array(0xE0001, 0xE0001), // LANGUAGE TAG
1701 array(0xE0020, 0xE007F), // [TAGGING CHARACTERS]
1702 // RFC3920
1703 array(0x22, 0x22), // "
1704 array(0x26, 0x26), // &
1705 array(0x27, 0x27), // '
1706 array(0x2F, 0x2F), // /
1707 array(0x3A, 0x3A), // :
1708 array(0x3C, 0x3C), // <
1709 array(0x3E, 0x3E), // >
1710 array(0x40, 0x40) // @
1713 $pos = 0;
1714 $result = true;
1716 // @todo: rewrite this!
1717 while ($pos < strlen($username))
1719 $len = $uni = 0;
1720 for ($i = 0; $i <= 5; $i++)
1722 if (ord($username[$pos]) >= $boundary[$i][0] && ord($username[$pos]) <= $boundary[$i][1])
1724 $len = $i + 1;
1725 $uni = (ord($username[$pos]) - $boundary[$i][0]) * pow(2, $i * 6);
1727 for ($k = 1; $k < $len; $k++)
1729 $uni += (ord($username[$pos + $k]) - 128) * pow(2, ($i - $k) * 6);
1732 break;
1736 if ($len == 0)
1738 return 'WRONG_DATA';
1741 foreach ($prohibited as $pval)
1743 if ($uni >= $pval[0] && $uni <= $pval[1])
1745 $result = false;
1746 break 2;
1750 $pos = $pos + $len;
1753 if (!$result)
1755 return 'WRONG_DATA';
1758 return false;
1762 * Remove avatar
1764 function avatar_delete($mode, $row, $clean_db = false)
1766 global $config, $db, $user;
1768 // Check if the users avatar is actually *not* a group avatar
1769 if ($mode == 'user')
1771 if (strpos($row['user_avatar'], 'g') === 0 || (((int)$row['user_avatar'] !== 0) && ((int)$row['user_avatar'] !== (int)$row['user_id'])))
1773 return false;
1777 if ($clean_db)
1779 avatar_remove_db($row[$mode . '_avatar']);
1781 $filename = get_avatar_filename($row[$mode . '_avatar']);
1782 if (file_exists(PHPBB_ROOT_PATH . $config['avatar_path'] . '/' . $filename))
1784 @unlink(PHPBB_ROOT_PATH . $config['avatar_path'] . '/' . $filename);
1785 return true;
1788 return false;
1792 * Remote avatar linkage
1794 function avatar_remote($data, &$error)
1796 global $config, $db, $user;
1798 if (!preg_match('#^(http|https|ftp)://#i', $data['remotelink']))
1800 $data['remotelink'] = 'http://' . $data['remotelink'];
1802 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']))
1804 $error[] = $user->lang['AVATAR_URL_INVALID'];
1805 return false;
1808 // Make sure getimagesize works...
1809 if (($image_data = @getimagesize($data['remotelink'])) === false && (empty($data['width']) || empty($data['height'])))
1811 $error[] = $user->lang['UNABLE_GET_IMAGE_SIZE'];
1812 return false;
1815 if (!empty($image_data) && ($image_data[0] < 2 || $image_data[1] < 2))
1817 $error[] = $user->lang['AVATAR_NO_SIZE'];
1818 return false;
1821 $width = ($data['width'] && $data['height']) ? $data['width'] : $image_data[0];
1822 $height = ($data['width'] && $data['height']) ? $data['height'] : $image_data[1];
1824 if ($width < 2 || $height < 2)
1826 $error[] = $user->lang['AVATAR_NO_SIZE'];
1827 return false;
1830 // Check image type
1831 include_once(PHPBB_ROOT_PATH . 'includes/functions_upload.' . PHP_EXT);
1832 $types = fileupload::image_types();
1833 $extension = strtolower(filespec::get_extension($data['remotelink']));
1835 if (!empty($image_data) && (!isset($types[$image_data[2]]) || !in_array($extension, $types[$image_data[2]])))
1837 if (!isset($types[$image_data[2]]))
1839 $error[] = $user->lang['UNABLE_GET_IMAGE_SIZE'];
1841 else
1843 $error[] = sprintf($user->lang['IMAGE_FILETYPE_MISMATCH'], $types[$image_data[2]][0], $extension);
1845 return false;
1848 if ($config['avatar_max_width'] || $config['avatar_max_height'])
1850 if ($width > $config['avatar_max_width'] || $height > $config['avatar_max_height'])
1852 $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);
1853 return false;
1857 if ($config['avatar_min_width'] || $config['avatar_min_height'])
1859 if ($width < $config['avatar_min_width'] || $height < $config['avatar_min_height'])
1861 $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);
1862 return false;
1866 return array(AVATAR_REMOTE, $data['remotelink'], $width, $height);
1870 * Avatar upload using the upload class
1872 function avatar_upload($data, &$error)
1874 global $config, $db, $user;
1876 // Init upload class
1877 include_once(PHPBB_ROOT_PATH . 'includes/functions_upload.' . PHP_EXT);
1878 $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']));
1880 if (!empty($_FILES['uploadfile']['name']))
1882 $file = $upload->form_upload('uploadfile');
1884 else
1886 $file = $upload->remote_upload($data['uploadurl']);
1889 $prefix = $config['avatar_salt'] . '_';
1890 $file->clean_filename('avatar', $prefix, $data['user_id']);
1892 $destination = $config['avatar_path'];
1894 // Adjust destination path (no trailing slash)
1895 if (substr($destination, -1, 1) == '/' || substr($destination, -1, 1) == '\\')
1897 $destination = substr($destination, 0, -1);
1900 $destination = str_replace(array('../', '..\\', './', '.\\'), '', $destination);
1901 if ($destination && ($destination[0] == '/' || $destination[0] == "\\"))
1903 $destination = '';
1906 // Move file and overwrite any existing image
1907 $file->move_file($destination, true);
1909 if (sizeof($file->error))
1911 $file->remove();
1912 $error = array_merge($error, $file->error);
1915 return array(AVATAR_UPLOAD, $data['user_id'] . '_' . time() . '.' . $file->get('extension'), $file->get('width'), $file->get('height'));
1919 * Generates avatar filename from the database entry
1921 function get_avatar_filename($avatar_entry)
1923 global $config;
1926 if ($avatar_entry[0] === 'g')
1928 $avatar_group = true;
1929 $avatar_entry = substr($avatar_entry, 1);
1931 else
1933 $avatar_group = false;
1935 $ext = substr(strrchr($avatar_entry, '.'), 1);
1936 $avatar_entry = intval($avatar_entry);
1937 return $config['avatar_salt'] . '_' . (($avatar_group) ? 'g' : '') . $avatar_entry . '.' . $ext;
1941 * Avatar Gallery
1943 function avatar_gallery($category, $avatar_select, $items_per_column, $block_var = 'avatar_row')
1945 global $user, $cache, $template, $config;
1947 $avatar_list = array();
1949 $path = PHPBB_ROOT_PATH . $config['avatar_gallery_path'];
1951 if (!file_exists($path) || !is_dir($path))
1953 $avatar_list = array($user->lang['NO_AVATAR_CATEGORY'] => array());
1955 else
1957 // Collect images
1958 $dp = @opendir($path);
1960 if (!$dp)
1962 return array($user->lang['NO_AVATAR_CATEGORY'] => array());
1965 while (($file = readdir($dp)) !== false)
1967 if ($file[0] != '.' && preg_match('#^[^&"\'<>]+$#i', $file) && is_dir("$path/$file"))
1969 $avatar_row_count = $avatar_col_count = 0;
1971 if ($dp2 = @opendir("$path/$file"))
1973 while (($sub_file = readdir($dp2)) !== false)
1975 if (preg_match('#^[^&\'"<>]+\.(?:gif|png|jpe?g)$#i', $sub_file))
1977 $avatar_list[$file][$avatar_row_count][$avatar_col_count] = array(
1978 'file' => "$file/$sub_file",
1979 'filename' => $sub_file,
1980 'name' => ucfirst(str_replace('_', ' ', preg_replace('#^(.*)\..*$#', '\1', $sub_file))),
1982 $avatar_col_count++;
1983 if ($avatar_col_count == $items_per_column)
1985 $avatar_row_count++;
1986 $avatar_col_count = 0;
1990 closedir($dp2);
1994 closedir($dp);
1997 if (!sizeof($avatar_list))
1999 $avatar_list = array($user->lang['NO_AVATAR_CATEGORY'] => array());
2002 @ksort($avatar_list);
2004 $category = (!$category) ? key($avatar_list) : $category;
2005 $avatar_categories = array_keys($avatar_list);
2007 $s_category_options = '';
2008 foreach ($avatar_categories as $cat)
2010 $s_category_options .= '<option value="' . $cat . '"' . (($cat == $category) ? ' selected="selected"' : '') . '>' . $cat . '</option>';
2013 $template->assign_vars(array(
2014 'S_AVATARS_ENABLED' => true,
2015 'S_IN_AVATAR_GALLERY' => true,
2016 'S_CAT_OPTIONS' => $s_category_options)
2019 $avatar_list = (isset($avatar_list[$category])) ? $avatar_list[$category] : array();
2021 foreach ($avatar_list as $avatar_row_ary)
2023 $template->assign_block_vars($block_var, array());
2025 foreach ($avatar_row_ary as $avatar_col_ary)
2027 $template->assign_block_vars($block_var . '.avatar_column', array(
2028 'AVATAR_IMAGE' => PHPBB_ROOT_PATH . $config['avatar_gallery_path'] . '/' . $avatar_col_ary['file'],
2029 'AVATAR_NAME' => $avatar_col_ary['name'],
2030 'AVATAR_FILE' => $avatar_col_ary['filename'])
2033 $template->assign_block_vars($block_var . '.avatar_option_column', array(
2034 'AVATAR_IMAGE' => PHPBB_ROOT_PATH . $config['avatar_gallery_path'] . '/' . $avatar_col_ary['file'],
2035 'S_OPTIONS_AVATAR' => $avatar_col_ary['filename'])
2040 return $avatar_list;
2045 * Tries to (re-)establish avatar dimensions
2047 function avatar_get_dimensions($avatar, $avatar_type, &$error, $current_x = 0, $current_y = 0)
2049 global $config, $user;
2051 switch ($avatar_type)
2053 case AVATAR_REMOTE :
2054 break;
2056 case AVATAR_UPLOAD :
2057 $avatar = PHPBB_ROOT_PATH . $config['avatar_path'] . '/' . get_avatar_filename($avatar);
2058 break;
2060 case AVATAR_GALLERY :
2061 $avatar = PHPBB_ROOT_PATH . $config['avatar_gallery_path'] . '/' . $avatar ;
2062 break;
2065 // Make sure getimagesize works...
2066 if (($image_data = @getimagesize($avatar)) === false)
2068 $error[] = $user->lang['UNABLE_GET_IMAGE_SIZE'];
2069 return false;
2072 if ($image_data[0] < 2 || $image_data[1] < 2)
2074 $error[] = $user->lang['AVATAR_NO_SIZE'];
2075 return false;
2078 // try to maintain ratio
2079 if (!(empty($current_x) && empty($current_y)))
2081 if ($current_x != 0)
2083 $image_data[1] = (int) floor(($current_x / $image_data[0]) * $image_data[1]);
2084 $image_data[1] = min($config['avatar_max_height'], $image_data[1]);
2085 $image_data[1] = max($config['avatar_min_height'], $image_data[1]);
2087 if ($current_y != 0)
2089 $image_data[0] = (int) floor(($current_y / $image_data[1]) * $image_data[0]);
2090 $image_data[0] = min($config['avatar_max_width'], $image_data[1]);
2091 $image_data[0] = max($config['avatar_min_width'], $image_data[1]);
2094 return array($image_data[0], $image_data[1]);
2098 * Uploading/Changing user avatar
2100 function avatar_process_user(&$error, $custom_userdata = false)
2102 global $config, $auth, $user, $db;
2104 $data = array(
2105 'uploadurl' => request_var('uploadurl', ''),
2106 'remotelink' => request_var('remotelink', ''),
2107 'width' => request_var('width', 0),
2108 'height' => request_var('height', 0),
2111 $error = validate_data($data, array(
2112 'uploadurl' => array('string', true, 5, 255),
2113 'remotelink' => array('string', true, 5, 255),
2114 'width' => array('string', true, 1, 3),
2115 'height' => array('string', true, 1, 3),
2118 if (sizeof($error))
2120 return false;
2123 $sql_ary = array();
2125 if ($custom_userdata === false)
2127 $userdata = &$user->data;
2129 else
2131 $userdata = &$custom_userdata;
2134 $data['user_id'] = $userdata['user_id'];
2135 $change_avatar = ($custom_userdata === false) ? $auth->acl_get('u_chgavatar') : true;
2136 $avatar_select = basename(request_var('avatar_select', ''));
2138 // Can we upload?
2139 $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;
2141 if ((!empty($_FILES['uploadfile']['name']) || $data['uploadurl']) && $can_upload)
2143 list($sql_ary['user_avatar_type'], $sql_ary['user_avatar'], $sql_ary['user_avatar_width'], $sql_ary['user_avatar_height']) = avatar_upload($data, $error);
2145 else if ($data['remotelink'] && $change_avatar && $config['allow_avatar_remote'])
2147 list($sql_ary['user_avatar_type'], $sql_ary['user_avatar'], $sql_ary['user_avatar_width'], $sql_ary['user_avatar_height']) = avatar_remote($data, $error);
2149 else if ($avatar_select && $change_avatar && $config['allow_avatar_local'])
2151 $category = basename(request_var('category', ''));
2153 $sql_ary['user_avatar_type'] = AVATAR_GALLERY;
2154 $sql_ary['user_avatar'] = $avatar_select;
2156 // check avatar gallery
2157 if (!is_dir(PHPBB_ROOT_PATH . $config['avatar_gallery_path'] . '/' . $category))
2159 $sql_ary['user_avatar'] = '';
2160 $sql_ary['user_avatar_type'] = $sql_ary['user_avatar_width'] = $sql_ary['user_avatar_height'] = 0;
2162 else
2164 list($sql_ary['user_avatar_width'], $sql_ary['user_avatar_height']) = getimagesize(PHPBB_ROOT_PATH . $config['avatar_gallery_path'] . '/' . $category . '/' . $sql_ary['user_avatar']);
2165 $sql_ary['user_avatar'] = $category . '/' . $sql_ary['user_avatar'];
2168 else if (isset($_POST['delete']) && $change_avatar)
2170 $sql_ary['user_avatar'] = '';
2171 $sql_ary['user_avatar_type'] = $sql_ary['user_avatar_width'] = $sql_ary['user_avatar_height'] = 0;
2173 else if (!empty($userdata['user_avatar']))
2175 // Only update the dimensions
2177 if (empty($data['width']) || empty($data['height']))
2179 if ($dims = avatar_get_dimensions($userdata['user_avatar'], $userdata['user_avatar_type'], $error, $data['width'], $data['height']))
2181 list($guessed_x, $guessed_y) = $dims;
2182 if (empty($data['width']))
2184 $data['width'] = $guessed_x;
2186 if (empty($data['height']))
2188 $data['height'] = $guessed_y;
2192 if (($config['avatar_max_width'] || $config['avatar_max_height']) &&
2193 (($data['width'] != $userdata['user_avatar_width']) || $data['height'] != $userdata['user_avatar_height']))
2195 if ($data['width'] > $config['avatar_max_width'] || $data['height'] > $config['avatar_max_height'])
2197 $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']);
2201 if (!sizeof($error))
2203 if ($config['avatar_min_width'] || $config['avatar_min_height'])
2205 if ($data['width'] < $config['avatar_min_width'] || $data['height'] < $config['avatar_min_height'])
2207 $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']);
2212 if (!sizeof($error))
2214 $sql_ary['user_avatar_width'] = $data['width'];
2215 $sql_ary['user_avatar_height'] = $data['height'];
2219 if (!sizeof($error))
2221 // Do we actually have any data to update?
2222 if (sizeof($sql_ary))
2224 $sql = 'UPDATE ' . USERS_TABLE . '
2225 SET ' . $db->sql_build_array('UPDATE', $sql_ary) . '
2226 WHERE user_id = ' . (($custom_userdata === false) ? $user->data['user_id'] : $custom_userdata['user_id']);
2227 $db->sql_query($sql);
2229 if (isset($sql_ary['user_avatar']))
2231 $userdata = ($custom_userdata === false) ? $user->data : $custom_userdata;
2233 // Delete old avatar if present
2234 if ((!empty($userdata['user_avatar']) && empty($sql_ary['user_avatar']) && $userdata['user_avatar_type'] == AVATAR_UPLOAD)
2235 || ( !empty($userdata['user_avatar']) && !empty($sql_ary['user_avatar']) && $userdata['user_avatar_type'] == AVATAR_UPLOAD && $sql_ary['user_avatar_type'] != AVATAR_UPLOAD))
2237 avatar_delete('user', $userdata);
2243 return (sizeof($error)) ? false : true;
2247 // Usergroup functions
2251 * Add or edit a group. If we're editing a group we only update user
2252 * parameters such as rank, etc. if they are changed
2254 function group_create(&$group_id, $type, $name, $desc, $group_attributes, $allow_desc_bbcode = false, $allow_desc_urls = false, $allow_desc_smilies = false)
2256 global $config, $db, $user, $file_upload;
2258 $error = array();
2259 $attribute_ary = array(
2260 'group_colour' => 'string',
2261 'group_rank' => 'int',
2262 'group_avatar' => 'string',
2263 'group_avatar_type' => 'int',
2264 'group_avatar_width' => 'int',
2265 'group_avatar_height' => 'int',
2267 'group_receive_pm' => 'int',
2268 'group_legend' => 'int',
2269 'group_message_limit' => 'int',
2271 'group_founder_manage' => 'int',
2274 // Those are group-only attributes
2275 $group_only_ary = array('group_receive_pm', 'group_legend', 'group_message_limit', 'group_founder_manage');
2277 // Check data. Limit group name length.
2278 if (!utf8_strlen($name) || utf8_strlen($name) > 60)
2280 $error[] = (!utf8_strlen($name)) ? $user->lang['GROUP_ERR_USERNAME'] : $user->lang['GROUP_ERR_USER_LONG'];
2283 $err = group_validate_groupname($group_id, $name);
2284 if (!empty($err))
2286 $error[] = $user->lang[$err];
2289 if (!in_array($type, array(GROUP_OPEN, GROUP_CLOSED, GROUP_HIDDEN, GROUP_SPECIAL, GROUP_FREE)))
2291 $error[] = $user->lang['GROUP_ERR_TYPE'];
2294 if (!sizeof($error))
2296 $user_ary = array();
2297 $sql_ary = array(
2298 'group_name' => (string) $name,
2299 'group_name_clean' => (string) utf8_clean_string($name),
2300 'group_desc' => (string) $desc,
2301 'group_desc_uid' => '',
2302 'group_desc_bitfield' => '',
2303 'group_type' => (int) $type,
2306 // Parse description
2307 if ($desc)
2309 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);
2312 if (sizeof($group_attributes))
2314 foreach ($attribute_ary as $attribute => $_type)
2316 if (isset($group_attributes[$attribute]))
2318 settype($group_attributes[$attribute], $_type);
2319 $sql_ary[$attribute] = $group_attributes[$attribute];
2324 // Setting the log message before we set the group id (if group gets added)
2325 $log = ($group_id) ? 'LOG_GROUP_UPDATED' : 'LOG_GROUP_CREATED';
2327 $query = '';
2329 if ($group_id)
2331 $sql = 'SELECT user_id
2332 FROM ' . USERS_TABLE . '
2333 WHERE group_id = ' . $group_id;
2334 $result = $db->sql_query($sql);
2336 while ($row = $db->sql_fetchrow($result))
2338 $user_ary[] = $row['user_id'];
2340 $db->sql_freeresult($result);
2342 if (isset($sql_ary['group_avatar']) && !$sql_ary['group_avatar'])
2344 remove_default_avatar($group_id, $user_ary);
2346 if (isset($sql_ary['group_rank']) && !$sql_ary['group_rank'])
2348 remove_default_rank($group_id, $user_ary);
2351 $sql = 'UPDATE ' . GROUPS_TABLE . '
2352 SET ' . $db->sql_build_array('UPDATE', $sql_ary) . "
2353 WHERE group_id = $group_id";
2354 $db->sql_query($sql);
2356 // Since we may update the name too, we need to do this on other tables too...
2357 $sql = 'UPDATE ' . MODERATOR_CACHE_TABLE . "
2358 SET group_name = '" . $db->sql_escape($sql_ary['group_name']) . "'
2359 WHERE group_id = $group_id";
2360 $db->sql_query($sql);
2362 else
2364 $sql = 'INSERT INTO ' . GROUPS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary);
2365 $db->sql_query($sql);
2368 if (!$group_id)
2370 $group_id = $db->sql_nextid();
2371 if (isset($sql_ary['group_avatar_type']) && $sql_ary['group_avatar_type'] == AVATAR_UPLOAD)
2373 group_correct_avatar($group_id, $sql_ary['group_avatar']);
2377 // Set user attributes
2378 $sql_ary = array();
2379 if (sizeof($group_attributes))
2381 foreach ($attribute_ary as $attribute => $_type)
2383 if (isset($group_attributes[$attribute]) && !in_array($attribute, $group_only_ary))
2385 // If we are about to set an avatar, we will not overwrite user avatars if no group avatar is set...
2386 if (strpos($attribute, 'group_avatar') === 0 && !$group_attributes[$attribute])
2388 continue;
2391 $sql_ary[$attribute] = $group_attributes[$attribute];
2396 if (sizeof($sql_ary) && sizeof($user_ary))
2398 group_set_user_default($group_id, $user_ary, $sql_ary);
2401 $name = ($type == GROUP_SPECIAL) ? $user->lang['G_' . $name] : $name;
2402 add_log('admin', $log, $name);
2404 group_update_listings($group_id);
2407 return (sizeof($error)) ? $error : false;
2412 * Changes a group avatar's filename to conform to the naming scheme
2414 function group_correct_avatar($group_id, $old_entry)
2416 global $config, $db;
2418 $group_id = (int)$group_id;
2419 $ext = substr(strrchr($old_entry, '.'), 1);
2420 $old_filename = get_avatar_filename($old_entry);
2421 $new_filename = $config['avatar_salt'] . "_g$group_id.$ext";
2422 $new_entry = 'g' . $group_id . '_' . substr(time(), -5) . ".$ext";
2424 $avatar_path = PHPBB_ROOT_PATH . $config['avatar_path'];
2425 if (@rename($avatar_path . '/'. $old_filename, $avatar_path . '/' . $new_filename))
2427 $sql = 'UPDATE ' . GROUPS_TABLE . '
2428 SET group_avatar = \'' . $db->sql_escape($new_entry) . "'
2429 WHERE group_id = $group_id";
2430 $db->sql_query($sql);
2436 * Remove avatar also for users not having the group as default
2438 function avatar_remove_db($avatar_name)
2440 global $config, $db;
2442 $sql = 'UPDATE ' . USERS_TABLE . "
2443 SET user_avatar = '',
2444 user_avatar_type = 0
2445 WHERE user_avatar = '" . $db->sql_escape($avatar_name) . '\'';
2446 $db->sql_query($sql);
2451 * Group Delete
2453 function group_delete($group_id, $group_name = false)
2455 global $db;
2457 if (!$group_name)
2459 $group_name = get_group_name($group_id);
2462 $start = 0;
2466 $user_id_ary = $username_ary = array();
2468 // Batch query for group members, call group_user_del
2469 $sql = 'SELECT u.user_id, u.username
2470 FROM ' . USER_GROUP_TABLE . ' ug, ' . USERS_TABLE . " u
2471 WHERE ug.group_id = $group_id
2472 AND u.user_id = ug.user_id";
2473 $result = $db->sql_query_limit($sql, 200, $start);
2475 if ($row = $db->sql_fetchrow($result))
2479 $user_id_ary[] = $row['user_id'];
2480 $username_ary[] = $row['username'];
2482 $start++;
2484 while ($row = $db->sql_fetchrow($result));
2486 group_user_del($group_id, $user_id_ary, $username_ary, $group_name);
2488 else
2490 $start = 0;
2492 $db->sql_freeresult($result);
2494 while ($start);
2496 // Delete group
2497 $sql = 'DELETE FROM ' . GROUPS_TABLE . "
2498 WHERE group_id = $group_id";
2499 $db->sql_query($sql);
2501 // Delete auth entries from the groups table
2502 $sql = 'DELETE FROM ' . ACL_GROUPS_TABLE . "
2503 WHERE group_id = $group_id";
2504 $db->sql_query($sql);
2506 // Re-cache moderators
2507 if (!function_exists('cache_moderators'))
2509 include(PHPBB_ROOT_PATH . 'includes/functions_admin.' . PHP_EXT);
2512 cache_moderators();
2514 add_log('admin', 'LOG_GROUP_DELETE', $group_name);
2516 // Return false - no error
2517 return false;
2521 * Add user(s) to group
2523 * @return mixed false if no errors occurred, else the user lang string for the relevant error, for example 'NO_USER'
2525 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)
2527 global $db, $auth;
2529 // We need both username and user_id info
2530 $result = user_get_id_name($user_id_ary, $username_ary);
2532 if (!sizeof($user_id_ary) || $result !== false)
2534 return 'NO_USER';
2537 // Remove users who are already members of this group
2538 $sql = 'SELECT user_id, group_leader
2539 FROM ' . USER_GROUP_TABLE . '
2540 WHERE ' . $db->sql_in_set('user_id', $user_id_ary) . "
2541 AND group_id = $group_id";
2542 $result = $db->sql_query($sql);
2544 $add_id_ary = $update_id_ary = array();
2545 while ($row = $db->sql_fetchrow($result))
2547 $add_id_ary[] = (int) $row['user_id'];
2549 if ($leader && !$row['group_leader'])
2551 $update_id_ary[] = (int) $row['user_id'];
2554 $db->sql_freeresult($result);
2556 // Do all the users exist in this group?
2557 $add_id_ary = array_diff($user_id_ary, $add_id_ary);
2559 // If we have no users
2560 if (!sizeof($add_id_ary) && !sizeof($update_id_ary))
2562 return 'GROUP_USERS_EXIST';
2565 $db->sql_transaction('begin');
2567 // Insert the new users
2568 if (sizeof($add_id_ary))
2570 $sql_ary = array();
2572 foreach ($add_id_ary as $user_id)
2574 $sql_ary[] = array(
2575 'user_id' => (int) $user_id,
2576 'group_id' => (int) $group_id,
2577 'group_leader' => (int) $leader,
2578 'user_pending' => (int) $pending,
2582 $db->sql_multi_insert(USER_GROUP_TABLE, $sql_ary);
2585 if (sizeof($update_id_ary))
2587 $sql = 'UPDATE ' . USER_GROUP_TABLE . '
2588 SET group_leader = 1
2589 WHERE ' . $db->sql_in_set('user_id', $update_id_ary) . "
2590 AND group_id = $group_id";
2591 $db->sql_query($sql);
2594 if ($default)
2596 group_set_user_default($group_id, $user_id_ary, $group_attributes);
2599 $db->sql_transaction('commit');
2601 // Clear permissions cache of relevant users
2602 $auth->acl_clear_prefetch($user_id_ary);
2604 if (!$group_name)
2606 $group_name = get_group_name($group_id);
2609 $log = ($leader) ? 'LOG_MODS_ADDED' : 'LOG_USERS_ADDED';
2611 add_log('admin', $log, $group_name, implode(', ', $username_ary));
2613 group_update_listings($group_id);
2615 // Return false - no error
2616 return false;
2620 * Remove a user/s from a given group. When we remove users we update their
2621 * default group_id. We do this by examining which "special" groups they belong
2622 * to. The selection is made based on a reasonable priority system
2624 * @return false if no errors occurred, else the user lang string for the relevant error, for example 'NO_USER'
2626 function group_user_del($group_id, $user_id_ary = false, $username_ary = false, $group_name = false)
2628 global $db, $auth;
2630 $group_order = array('ADMINISTRATORS', 'GLOBAL_MODERATORS', 'REGISTERED_COPPA', 'REGISTERED', 'BOTS', 'GUESTS');
2632 // We need both username and user_id info
2633 $result = user_get_id_name($user_id_ary, $username_ary);
2635 if (!sizeof($user_id_ary) || $result !== false)
2637 return 'NO_USER';
2640 $clean_group_order = array_map('utf8_clean_string', $group_order);
2642 $sql = 'SELECT *
2643 FROM ' . GROUPS_TABLE . '
2644 WHERE ' . $db->sql_in_set('group_name_clean', $clean_group_order);
2645 $result = $db->sql_query($sql);
2647 $group_order_id = $special_group_data = array();
2648 while ($row = $db->sql_fetchrow($result))
2650 $group_order_id[$row['group_name']] = $row['group_id'];
2652 $special_group_data[$row['group_id']] = array(
2653 'group_colour' => $row['group_colour'],
2654 'group_rank' => $row['group_rank'],
2657 // Only set the group avatar if one is defined...
2658 if ($row['group_avatar'])
2660 $special_group_data[$row['group_id']] = array_merge($special_group_data[$row['group_id']], array(
2661 'group_avatar' => $row['group_avatar'],
2662 'group_avatar_type' => $row['group_avatar_type'],
2663 'group_avatar_width' => $row['group_avatar_width'],
2664 'group_avatar_height' => $row['group_avatar_height'])
2668 $db->sql_freeresult($result);
2670 // 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
2671 $sql = 'SELECT user_id, group_id
2672 FROM ' . USERS_TABLE . '
2673 WHERE ' . $db->sql_in_set('user_id', $user_id_ary);
2674 $result = $db->sql_query($sql);
2676 $default_groups = array();
2677 while ($row = $db->sql_fetchrow($result))
2679 $default_groups[$row['user_id']] = $row['group_id'];
2681 $db->sql_freeresult($result);
2683 // What special group memberships exist for these users?
2684 $sql = 'SELECT g.group_id, g.group_name_clean, ug.user_id
2685 FROM ' . USER_GROUP_TABLE . ' ug, ' . GROUPS_TABLE . ' g
2686 WHERE ' . $db->sql_in_set('ug.user_id', $user_id_ary) . "
2687 AND g.group_id = ug.group_id
2688 AND g.group_id <> $group_id
2689 AND g.group_type = " . GROUP_SPECIAL . '
2690 ORDER BY ug.user_id, g.group_id';
2691 $result = $db->sql_query($sql);
2693 $temp_ary = array();
2694 while ($row = $db->sql_fetchrow($result))
2696 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']]))
2698 $temp_ary[$row['user_id']] = $row['group_id'];
2701 $db->sql_freeresult($result);
2703 $sql_where_ary = array();
2704 foreach ($temp_ary as $uid => $gid)
2706 $sql_where_ary[$gid][] = $uid;
2708 unset($temp_ary);
2710 foreach ($special_group_data as $gid => $default_data_ary)
2712 if (isset($sql_where_ary[$gid]) && sizeof($sql_where_ary[$gid]))
2714 remove_default_rank($group_id, $sql_where_ary[$gid]);
2715 remove_default_avatar($group_id, $sql_where_ary[$gid]);
2716 group_set_user_default($gid, $sql_where_ary[$gid], $default_data_ary);
2719 unset($special_group_data);
2721 $sql = 'DELETE FROM ' . USER_GROUP_TABLE . "
2722 WHERE group_id = $group_id
2723 AND " . $db->sql_in_set('user_id', $user_id_ary);
2724 $db->sql_query($sql);
2726 // Clear permissions cache of relevant users
2727 $auth->acl_clear_prefetch($user_id_ary);
2729 if (!$group_name)
2731 $group_name = get_group_name($group_id);
2734 $log = 'LOG_GROUP_REMOVE';
2736 add_log('admin', $log, $group_name, implode(', ', $username_ary));
2738 group_update_listings($group_id);
2740 // Return false - no error
2741 return false;
2746 * Removes the group avatar of the default group from the users in user_ids who have that group as default.
2748 function remove_default_avatar($group_id, $user_ids)
2750 global $db;
2752 if (!is_array($user_ids))
2754 $user_ids = array($user_ids);
2756 if (empty($user_ids))
2758 return false;
2761 $user_ids = array_map('intval', $user_ids);
2763 $sql = 'SELECT *
2764 FROM ' . GROUPS_TABLE . '
2765 WHERE group_id = ' . (int)$group_id;
2766 $result = $db->sql_query($sql);
2767 if (!$row = $db->sql_fetchrow($result))
2769 $db->sql_freeresult($result);
2770 return false;
2772 $db->sql_freeresult($result);
2774 $sql = 'UPDATE ' . USERS_TABLE . "
2775 SET user_avatar = '',
2776 user_avatar_type = 0,
2777 user_avatar_width = 0,
2778 user_avatar_height = 0
2779 WHERE group_id = " . (int) $group_id . "
2780 AND user_avatar = '" . $db->sql_escape($row['group_avatar']) . "'
2781 AND " . $db->sql_in_set('user_id', $user_ids);
2783 $db->sql_query($sql);
2787 * Removes the group rank of the default group from the users in user_ids who have that group as default.
2789 function remove_default_rank($group_id, $user_ids)
2791 global $db;
2793 if (!is_array($user_ids))
2795 $user_ids = array($user_ids);
2797 if (empty($user_ids))
2799 return false;
2802 $user_ids = array_map('intval', $user_ids);
2804 $sql = 'SELECT *
2805 FROM ' . GROUPS_TABLE . '
2806 WHERE group_id = ' . (int)$group_id;
2807 $result = $db->sql_query($sql);
2808 if (!$row = $db->sql_fetchrow($result))
2810 $db->sql_freeresult($result);
2811 return false;
2813 $db->sql_freeresult($result);
2815 $sql = 'UPDATE ' . USERS_TABLE . '
2816 SET user_rank = 0
2817 WHERE group_id = ' . (int)$group_id . '
2818 AND user_rank <> 0
2819 AND user_rank = ' . (int)$row['group_rank'] . '
2820 AND ' . $db->sql_in_set('user_id', $user_ids);
2821 $db->sql_query($sql);
2825 * This is used to promote (to leader), demote or set as default a member/s
2827 function group_user_attributes($action, $group_id, $user_id_ary = false, $username_ary = false, $group_name = false, $group_attributes = false)
2829 global $db, $auth, $config;
2831 // We need both username and user_id info
2832 $result = user_get_id_name($user_id_ary, $username_ary);
2834 if (!sizeof($user_id_ary) || $result !== false)
2836 return 'NO_USERS';
2839 if (!$group_name)
2841 $group_name = get_group_name($group_id);
2844 switch ($action)
2846 case 'demote':
2847 case 'promote':
2849 $sql = 'SELECT user_id FROM ' . USER_GROUP_TABLE . "
2850 WHERE group_id = $group_id
2851 AND user_pending = 1
2852 AND " . $db->sql_in_set('user_id', $user_id_ary);
2853 $result = $db->sql_query_limit($sql, 1);
2854 $not_empty = ($db->sql_fetchrow($result));
2855 $db->sql_freeresult($result);
2856 if ($not_empty)
2858 return 'NO_VALID_USERS';
2861 $sql = 'UPDATE ' . USER_GROUP_TABLE . '
2862 SET group_leader = ' . (($action == 'promote') ? 1 : 0) . "
2863 WHERE group_id = $group_id
2864 AND user_pending = 0
2865 AND " . $db->sql_in_set('user_id', $user_id_ary);
2866 $db->sql_query($sql);
2868 $log = ($action == 'promote') ? 'LOG_GROUP_PROMOTED' : 'LOG_GROUP_DEMOTED';
2869 break;
2871 case 'approve':
2872 // Make sure we only approve those which are pending ;)
2873 $sql = 'SELECT u.user_id, u.user_email, u.username, u.username_clean, u.user_notify_type, u.user_jabber, u.user_lang
2874 FROM ' . USERS_TABLE . ' u, ' . USER_GROUP_TABLE . ' ug
2875 WHERE ug.group_id = ' . $group_id . '
2876 AND ug.user_pending = 1
2877 AND ug.user_id = u.user_id
2878 AND ' . $db->sql_in_set('ug.user_id', $user_id_ary);
2879 $result = $db->sql_query($sql);
2881 $user_id_ary = $email_users = array();
2882 while ($row = $db->sql_fetchrow($result))
2884 $user_id_ary[] = $row['user_id'];
2885 $email_users[] = $row;
2887 $db->sql_freeresult($result);
2889 if (!sizeof($user_id_ary))
2891 return false;
2894 $sql = 'UPDATE ' . USER_GROUP_TABLE . "
2895 SET user_pending = 0
2896 WHERE group_id = $group_id
2897 AND " . $db->sql_in_set('user_id', $user_id_ary);
2898 $db->sql_query($sql);
2900 // Send approved email to users...
2901 include_once(PHPBB_ROOT_PATH . 'includes/functions_messenger.' . PHP_EXT);
2902 $messenger = new messenger();
2904 foreach ($email_users as $row)
2906 $messenger->template('group_approved', $row['user_lang']);
2908 $messenger->to($row['user_email'], $row['username']);
2909 $messenger->im($row['user_jabber'], $row['username']);
2911 $messenger->assign_vars(array(
2912 'USERNAME' => htmlspecialchars_decode($row['username']),
2913 'GROUP_NAME' => htmlspecialchars_decode($group_name),
2914 'U_GROUP' => generate_board_url() . '/ucp.' . PHP_EXT . '?i=groups&mode=membership',
2917 $messenger->send($row['user_notify_type']);
2920 $messenger->save_queue();
2922 $log = 'LOG_USERS_APPROVED';
2923 break;
2925 case 'default':
2926 $sql = 'SELECT user_id, group_id FROM ' . USERS_TABLE . '
2927 WHERE ' . $db->sql_in_set('user_id', $user_id_ary, false, true);
2928 $result = $db->sql_query($sql);
2930 $groups = array();
2931 while ($row = $db->sql_fetchrow($result))
2933 if (!isset($groups[$row['group_id']]))
2935 $groups[$row['group_id']] = array();
2937 $groups[$row['group_id']][] = $row['user_id'];
2939 $db->sql_freeresult($result);
2941 foreach ($groups as $gid => $uids)
2943 remove_default_rank($gid, $uids);
2944 remove_default_avatar($gid, $uids);
2946 group_set_user_default($group_id, $user_id_ary, $group_attributes);
2947 $log = 'LOG_GROUP_DEFAULTS';
2948 break;
2951 // Clear permissions cache of relevant users
2952 $auth->acl_clear_prefetch($user_id_ary);
2954 add_log('admin', $log, $group_name, implode(', ', $username_ary));
2956 group_update_listings($group_id);
2958 return false;
2962 * A small version of validate_username to check for a group name's existence. To be called directly.
2964 function group_validate_groupname($group_id, $group_name)
2966 global $config, $db;
2968 $group_name = utf8_clean_string($group_name);
2970 if (!empty($group_id))
2972 $sql = 'SELECT group_name_clean
2973 FROM ' . GROUPS_TABLE . '
2974 WHERE group_id = ' . (int) $group_id;
2975 $result = $db->sql_query($sql);
2976 $row = $db->sql_fetchrow($result);
2977 $db->sql_freeresult($result);
2979 if (!$row)
2981 return false;
2984 $allowed_groupname = utf8_clean_string($row['group_name']);
2986 if ($allowed_groupname == $group_name)
2988 return false;
2992 $sql = 'SELECT group_name
2993 FROM ' . GROUPS_TABLE . "
2994 WHERE group_name_clean = '" . $db->sql_escape(utf8_clean_string($group_name)) . "'";
2995 $result = $db->sql_query($sql);
2996 $row = $db->sql_fetchrow($result);
2997 $db->sql_freeresult($result);
2999 if ($row)
3001 return 'GROUP_NAME_TAKEN';
3004 return false;
3008 * Set users default group
3010 * @private
3012 function group_set_user_default($group_id, $user_id_ary, $group_attributes = false, $update_listing = false)
3014 global $db;
3016 if (empty($user_id_ary))
3018 return;
3021 $attribute_ary = array(
3022 'group_colour' => 'string',
3023 'group_rank' => 'int',
3024 'group_avatar' => 'string',
3025 'group_avatar_type' => 'int',
3026 'group_avatar_width' => 'int',
3027 'group_avatar_height' => 'int',
3030 $sql_ary = array(
3031 'group_id' => $group_id
3034 // Were group attributes passed to the function? If not we need to obtain them
3035 if ($group_attributes === false)
3037 $sql = 'SELECT ' . implode(', ', array_keys($attribute_ary)) . '
3038 FROM ' . GROUPS_TABLE . "
3039 WHERE group_id = $group_id";
3040 $result = $db->sql_query($sql);
3041 $group_attributes = $db->sql_fetchrow($result);
3042 $db->sql_freeresult($result);
3045 foreach ($attribute_ary as $attribute => $type)
3047 if (isset($group_attributes[$attribute]))
3049 // 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
3050 if ((strpos($attribute, 'group_avatar') === 0 || strpos($attribute, 'group_rank') === 0) && !$group_attributes[$attribute])
3052 continue;
3055 settype($group_attributes[$attribute], $type);
3056 $sql_ary[str_replace('group_', 'user_', $attribute)] = $group_attributes[$attribute];
3060 // Before we update the user attributes, we will make a list of those having now the group avatar assigned
3061 if (isset($sql_ary['user_avatar']))
3063 // Ok, get the original avatar data from users having an uploaded one (we need to remove these from the filesystem)
3064 $sql = 'SELECT user_id, group_id, user_avatar
3065 FROM ' . USERS_TABLE . '
3066 WHERE ' . $db->sql_in_set('user_id', $user_id_ary) . '
3067 AND user_avatar_type = ' . AVATAR_UPLOAD;
3068 $result = $db->sql_query($sql);
3070 while ($row = $db->sql_fetchrow($result))
3072 avatar_delete('user', $row);
3074 $db->sql_freeresult($result);
3076 else
3078 unset($sql_ary['user_avatar_type']);
3079 unset($sql_ary['user_avatar_height']);
3080 unset($sql_ary['user_avatar_width']);
3083 $sql = 'UPDATE ' . USERS_TABLE . ' SET ' . $db->sql_build_array('UPDATE', $sql_ary) . '
3084 WHERE ' . $db->sql_in_set('user_id', $user_id_ary);
3085 $db->sql_query($sql);
3087 if (isset($sql_ary['user_colour']))
3089 // Update any cached colour information for these users
3090 $sql = 'UPDATE ' . FORUMS_TABLE . " SET forum_last_poster_colour = '" . $db->sql_escape($sql_ary['user_colour']) . "'
3091 WHERE " . $db->sql_in_set('forum_last_poster_id', $user_id_ary);
3092 $db->sql_query($sql);
3094 $sql = 'UPDATE ' . TOPICS_TABLE . " SET topic_first_poster_colour = '" . $db->sql_escape($sql_ary['user_colour']) . "'
3095 WHERE " . $db->sql_in_set('topic_poster', $user_id_ary);
3096 $db->sql_query($sql);
3098 $sql = 'UPDATE ' . TOPICS_TABLE . " SET topic_last_poster_colour = '" . $db->sql_escape($sql_ary['user_colour']) . "'
3099 WHERE " . $db->sql_in_set('topic_last_poster_id', $user_id_ary);
3100 $db->sql_query($sql);
3102 global $config;
3104 if (in_array($config['newest_user_id'], $user_id_ary))
3106 set_config('newest_user_colour', $sql_ary['user_colour'], true);
3110 if ($update_listing)
3112 group_update_listings($group_id);
3117 * Get group name
3119 function get_group_name($group_id)
3121 global $db, $user;
3123 $sql = 'SELECT group_name, group_type
3124 FROM ' . GROUPS_TABLE . '
3125 WHERE group_id = ' . (int) $group_id;
3126 $result = $db->sql_query($sql);
3127 $row = $db->sql_fetchrow($result);
3128 $db->sql_freeresult($result);
3130 if (!$row)
3132 return '';
3135 return ($row['group_type'] == GROUP_SPECIAL) ? $user->lang['G_' . $row['group_name']] : $row['group_name'];
3139 * Obtain either the members of a specified group, the groups the specified user is subscribed to
3140 * or checking if a specified user is in a specified group. This function does not return pending memberships.
3142 * Note: Never use this more than once... first group your users/groups
3144 function group_memberships($group_id_ary = false, $user_id_ary = false, $return_bool = false)
3146 global $db;
3148 if (!$group_id_ary && !$user_id_ary)
3150 return true;
3153 if ($user_id_ary)
3155 $user_id_ary = (!is_array($user_id_ary)) ? array($user_id_ary) : $user_id_ary;
3158 if ($group_id_ary)
3160 $group_id_ary = (!is_array($group_id_ary)) ? array($group_id_ary) : $group_id_ary;
3163 $sql = 'SELECT ug.*, u.username, u.username_clean, u.user_email
3164 FROM ' . USER_GROUP_TABLE . ' ug, ' . USERS_TABLE . ' u
3165 WHERE ug.user_id = u.user_id
3166 AND ug.user_pending = 0 AND ';
3168 if ($group_id_ary)
3170 $sql .= ' ' . $db->sql_in_set('ug.group_id', $group_id_ary);
3173 if ($user_id_ary)
3175 $sql .= ($group_id_ary) ? ' AND ' : ' ';
3176 $sql .= $db->sql_in_set('ug.user_id', $user_id_ary);
3179 $result = ($return_bool) ? $db->sql_query_limit($sql, 1) : $db->sql_query($sql);
3181 $row = $db->sql_fetchrow($result);
3183 if ($return_bool)
3185 $db->sql_freeresult($result);
3186 return ($row) ? true : false;
3189 if (!$row)
3191 return false;
3194 $return = array();
3198 $return[] = $row;
3200 while ($row = $db->sql_fetchrow($result));
3202 $db->sql_freeresult($result);
3204 return $return;
3208 * Re-cache moderators and foes if group has a_ or m_ permissions
3210 function group_update_listings($group_id)
3212 global $auth;
3214 $hold_ary = $auth->acl_group_raw_data($group_id, array('a_', 'm_'));
3216 if (!sizeof($hold_ary))
3218 return;
3221 $mod_permissions = $admin_permissions = false;
3223 foreach ($hold_ary as $g_id => $forum_ary)
3225 foreach ($forum_ary as $forum_id => $auth_ary)
3227 foreach ($auth_ary as $auth_option => $setting)
3229 if ($mod_permissions && $admin_permissions)
3231 break 3;
3234 if ($setting != ACL_YES)
3236 continue;
3239 if ($auth_option == 'm_')
3241 $mod_permissions = true;
3244 if ($auth_option == 'a_')
3246 $admin_permissions = true;
3252 if ($mod_permissions)
3254 if (!function_exists('cache_moderators'))
3256 include(PHPBB_ROOT_PATH . 'includes/functions_admin.' . PHP_EXT);
3258 cache_moderators();
3261 if ($mod_permissions || $admin_permissions)
3263 if (!function_exists('update_foes'))
3265 include(PHPBB_ROOT_PATH . 'includes/functions_admin.' . PHP_EXT);
3267 update_foes(array($group_id));