Verzeichnisstruktur phpBB-3.3.15
- Veröffentlicht
- 28.08.2024
So funktioniert es
|
|
Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück |
Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis. |
|
|
(Beispiel Datei-Icons)
|
Auf das Icon klicken um den Quellcode anzuzeigen |
functions_user.php
0001 <?php
0002 /**
0003 *
0004 * This file is part of the phpBB Forum Software package.
0005 *
0006 * @copyright (c) phpBB Limited <https://www.phpbb.com>
0007 * @license GNU General Public License, version 2 (GPL-2.0)
0008 *
0009 * For full copyright and license information, please see
0010 * the docs/CREDITS.txt file.
0011 *
0012 */
0013
0014 /**
0015 * @ignore
0016 */
0017 if (!defined('IN_PHPBB'))
0018 {
0019 exit;
0020 }
0021
0022 /**
0023 * Obtain user_ids from usernames or vice versa. Returns false on
0024 * success else the error string
0025 *
0026 * @param array &$user_id_ary The user ids to check or empty if usernames used
0027 * @param array &$username_ary The usernames to check or empty if user ids used
0028 * @param mixed $user_type Array of user types to check, false if not restricting by user type
0029 * @param boolean $update_references If false, the supplied array is unset and appears unchanged from where it was called
0030 * @return boolean|string Returns false on success, error string on failure
0031 */
0032 function user_get_id_name(&$user_id_ary, &$username_ary, $user_type = false, $update_references = false)
0033 {
0034 global $db;
0035
0036 // Are both arrays already filled? Yep, return else
0037 // are neither array filled?
0038 if ($user_id_ary && $username_ary)
0039 {
0040 return false;
0041 }
0042 else if (!$user_id_ary && !$username_ary)
0043 {
0044 return 'NO_USERS';
0045 }
0046
0047 $which_ary = ($user_id_ary) ? 'user_id_ary' : 'username_ary';
0048
0049 if (${$which_ary} && !is_array(${$which_ary}))
0050 {
0051 ${$which_ary} = array(${$which_ary});
0052 }
0053
0054 $sql_in = ($which_ary == 'user_id_ary') ? array_map('intval', ${$which_ary}) : array_map('utf8_clean_string', ${$which_ary});
0055
0056 // By unsetting the array here, the values passed in at the point user_get_id_name() was called will be retained.
0057 // Otherwise, if we don't unset (as the array was passed by reference) the original array will be updated below.
0058 if ($update_references === false)
0059 {
0060 unset(${$which_ary});
0061 }
0062
0063 $user_id_ary = $username_ary = array();
0064
0065 // Grab the user id/username records
0066 $sql_where = ($which_ary == 'user_id_ary') ? 'user_id' : 'username_clean';
0067 $sql = 'SELECT user_id, username
0068 FROM ' . USERS_TABLE . '
0069 WHERE ' . $db->sql_in_set($sql_where, $sql_in);
0070
0071 if ($user_type !== false && !empty($user_type))
0072 {
0073 $sql .= ' AND ' . $db->sql_in_set('user_type', $user_type);
0074 }
0075
0076 $result = $db->sql_query($sql);
0077
0078 if (!($row = $db->sql_fetchrow($result)))
0079 {
0080 $db->sql_freeresult($result);
0081 return 'NO_USERS';
0082 }
0083
0084 do
0085 {
0086 $username_ary[$row['user_id']] = $row['username'];
0087 $user_id_ary[] = $row['user_id'];
0088 }
0089 while ($row = $db->sql_fetchrow($result));
0090 $db->sql_freeresult($result);
0091
0092 return false;
0093 }
0094
0095 /**
0096 * Get latest registered username and update database to reflect it
0097 */
0098 function update_last_username()
0099 {
0100 global $config, $db;
0101
0102 // Get latest username
0103 $sql = 'SELECT user_id, username, user_colour
0104 FROM ' . USERS_TABLE . '
0105 WHERE user_type IN (' . USER_NORMAL . ', ' . USER_FOUNDER . ')
0106 ORDER BY user_id DESC';
0107 $result = $db->sql_query_limit($sql, 1);
0108 $row = $db->sql_fetchrow($result);
0109 $db->sql_freeresult($result);
0110
0111 if ($row)
0112 {
0113 $config->set('newest_user_id', $row['user_id'], false);
0114 $config->set('newest_username', $row['username'], false);
0115 $config->set('newest_user_colour', $row['user_colour'], false);
0116 }
0117 }
0118
0119 /**
0120 * Updates a username across all relevant tables/fields
0121 *
0122 * @param string $old_name the old/current username
0123 * @param string $new_name the new username
0124 */
0125 function user_update_name($old_name, $new_name)
0126 {
0127 global $config, $db, $cache, $phpbb_dispatcher;
0128
0129 $update_ary = array(
0130 FORUMS_TABLE => array(
0131 'forum_last_poster_id' => 'forum_last_poster_name',
0132 ),
0133 MODERATOR_CACHE_TABLE => array(
0134 'user_id' => 'username',
0135 ),
0136 POSTS_TABLE => array(
0137 'poster_id' => 'post_username',
0138 ),
0139 TOPICS_TABLE => array(
0140 'topic_poster' => 'topic_first_poster_name',
0141 'topic_last_poster_id' => 'topic_last_poster_name',
0142 ),
0143 );
0144
0145 foreach ($update_ary as $table => $field_ary)
0146 {
0147 foreach ($field_ary as $id_field => $name_field)
0148 {
0149 $sql = "UPDATE $table
0150 SET $name_field = '" . $db->sql_escape($new_name) . "'
0151 WHERE $name_field = '" . $db->sql_escape($old_name) . "'
0152 AND $id_field <> " . ANONYMOUS;
0153 $db->sql_query($sql);
0154 }
0155 }
0156
0157 if ($config['newest_username'] == $old_name)
0158 {
0159 $config->set('newest_username', $new_name, false);
0160 }
0161
0162 /**
0163 * Update a username when it is changed
0164 *
0165 * @event core.update_username
0166 * @var string old_name The old username that is replaced
0167 * @var string new_name The new username
0168 * @since 3.1.0-a1
0169 */
0170 $vars = array('old_name', 'new_name');
0171 extract($phpbb_dispatcher->trigger_event('core.update_username', compact($vars)));
0172
0173 // Because some tables/caches use username-specific data we need to purge this here.
0174 $cache->destroy('sql', MODERATOR_CACHE_TABLE);
0175 }
0176
0177 /**
0178 * Adds an user
0179 *
0180 * @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.
0181 * @param array $cp_data custom profile fields, see custom_profile::build_insert_sql_array
0182 * @param array $notifications_data The notifications settings for the new user
0183 * @return the new user's ID.
0184 */
0185 function user_add($user_row, $cp_data = false, $notifications_data = null)
0186 {
0187 global $db, $config;
0188 global $phpbb_dispatcher, $phpbb_container;
0189
0190 if (empty($user_row['username']) || !isset($user_row['group_id']) || !isset($user_row['user_email']) || !isset($user_row['user_type']))
0191 {
0192 return false;
0193 }
0194
0195 $username_clean = utf8_clean_string($user_row['username']);
0196
0197 if (empty($username_clean))
0198 {
0199 return false;
0200 }
0201
0202 $sql_ary = array(
0203 'username' => $user_row['username'],
0204 'username_clean' => $username_clean,
0205 'user_password' => (isset($user_row['user_password'])) ? $user_row['user_password'] : '',
0206 'user_email' => strtolower($user_row['user_email']),
0207 'group_id' => $user_row['group_id'],
0208 'user_type' => $user_row['user_type'],
0209 );
0210
0211 // These are the additional vars able to be specified
0212 $additional_vars = array(
0213 'user_permissions' => '',
0214 'user_timezone' => $config['board_timezone'],
0215 'user_dateformat' => $config['default_dateformat'],
0216 'user_lang' => $config['default_lang'],
0217 'user_style' => (int) $config['default_style'],
0218 'user_actkey' => '',
0219 'user_ip' => '',
0220 'user_regdate' => time(),
0221 'user_passchg' => time(),
0222 'user_options' => 230271,
0223 // We do not set the new flag here - registration scripts need to specify it
0224 'user_new' => 0,
0225
0226 'user_inactive_reason' => 0,
0227 'user_inactive_time' => 0,
0228 'user_lastmark' => time(),
0229 'user_lastvisit' => 0,
0230 'user_lastpost_time' => 0,
0231 'user_lastpage' => '',
0232 'user_posts' => 0,
0233 'user_colour' => '',
0234 'user_avatar' => '',
0235 'user_avatar_type' => '',
0236 'user_avatar_width' => 0,
0237 'user_avatar_height' => 0,
0238 'user_new_privmsg' => 0,
0239 'user_unread_privmsg' => 0,
0240 'user_last_privmsg' => 0,
0241 'user_message_rules' => 0,
0242 'user_full_folder' => PRIVMSGS_NO_BOX,
0243 'user_emailtime' => 0,
0244
0245 'user_notify' => 0,
0246 'user_notify_pm' => 1,
0247 'user_notify_type' => NOTIFY_EMAIL,
0248 'user_allow_pm' => 1,
0249 'user_allow_viewonline' => 1,
0250 'user_allow_viewemail' => 1,
0251 'user_allow_massemail' => 1,
0252
0253 'user_sig' => '',
0254 'user_sig_bbcode_uid' => '',
0255 'user_sig_bbcode_bitfield' => '',
0256
0257 'user_form_salt' => unique_id(),
0258 );
0259
0260 // Now fill the sql array with not required variables
0261 foreach ($additional_vars as $key => $default_value)
0262 {
0263 $sql_ary[$key] = (isset($user_row[$key])) ? $user_row[$key] : $default_value;
0264 }
0265
0266 // Any additional variables in $user_row not covered above?
0267 $remaining_vars = array_diff(array_keys($user_row), array_keys($sql_ary));
0268
0269 // Now fill our sql array with the remaining vars
0270 if (count($remaining_vars))
0271 {
0272 foreach ($remaining_vars as $key)
0273 {
0274 $sql_ary[$key] = $user_row[$key];
0275 }
0276 }
0277
0278 /**
0279 * Use this event to modify the values to be inserted when a user is added
0280 *
0281 * @event core.user_add_modify_data
0282 * @var array user_row Array of user details submitted to user_add
0283 * @var array cp_data Array of Custom profile fields submitted to user_add
0284 * @var array sql_ary Array of data to be inserted when a user is added
0285 * @var array notifications_data Array of notification data to be inserted when a user is added
0286 * @since 3.1.0-a1
0287 * @changed 3.1.0-b5 Added user_row and cp_data
0288 * @changed 3.1.11-RC1 Added notifications_data
0289 */
0290 $vars = array('user_row', 'cp_data', 'sql_ary', 'notifications_data');
0291 extract($phpbb_dispatcher->trigger_event('core.user_add_modify_data', compact($vars)));
0292
0293 $sql = 'INSERT INTO ' . USERS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary);
0294 $db->sql_query($sql);
0295
0296 $user_id = $db->sql_nextid();
0297
0298 // Insert Custom Profile Fields
0299 if ($cp_data !== false && count($cp_data))
0300 {
0301 $cp_data['user_id'] = (int) $user_id;
0302
0303 /* @var $cp \phpbb\profilefields\manager */
0304 $cp = $phpbb_container->get('profilefields.manager');
0305 $sql = 'INSERT INTO ' . PROFILE_FIELDS_DATA_TABLE . ' ' .
0306 $db->sql_build_array('INSERT', $cp->build_insert_sql_array($cp_data));
0307 $db->sql_query($sql);
0308 }
0309
0310 // Place into appropriate group...
0311 $sql = 'INSERT INTO ' . USER_GROUP_TABLE . ' ' . $db->sql_build_array('INSERT', array(
0312 'user_id' => (int) $user_id,
0313 'group_id' => (int) $user_row['group_id'],
0314 'user_pending' => 0)
0315 );
0316 $db->sql_query($sql);
0317
0318 // Now make it the users default group...
0319 group_set_user_default($user_row['group_id'], array($user_id), false);
0320
0321 // Add to newly registered users group if user_new is 1
0322 if ($config['new_member_post_limit'] && $sql_ary['user_new'])
0323 {
0324 $sql = 'SELECT group_id
0325 FROM ' . GROUPS_TABLE . "
0326 WHERE group_name = 'NEWLY_REGISTERED'
0327 AND group_type = " . GROUP_SPECIAL;
0328 $result = $db->sql_query($sql);
0329 $add_group_id = (int) $db->sql_fetchfield('group_id');
0330 $db->sql_freeresult($result);
0331
0332 if ($add_group_id)
0333 {
0334 global $phpbb_log;
0335
0336 // Because these actions only fill the log unnecessarily, we disable it
0337 $phpbb_log->disable('admin');
0338
0339 // Add user to "newly registered users" group and set to default group if admin specified so.
0340 if ($config['new_member_group_default'])
0341 {
0342 group_user_add($add_group_id, $user_id, false, false, true);
0343 $user_row['group_id'] = $add_group_id;
0344 }
0345 else
0346 {
0347 group_user_add($add_group_id, $user_id);
0348 }
0349
0350 $phpbb_log->enable('admin');
0351 }
0352 }
0353
0354 // set the newest user and adjust the user count if the user is a normal user and no activation mail is sent
0355 if ($user_row['user_type'] == USER_NORMAL || $user_row['user_type'] == USER_FOUNDER)
0356 {
0357 $config->set('newest_user_id', $user_id, false);
0358 $config->set('newest_username', $user_row['username'], false);
0359 $config->increment('num_users', 1, false);
0360
0361 $sql = 'SELECT group_colour
0362 FROM ' . GROUPS_TABLE . '
0363 WHERE group_id = ' . (int) $user_row['group_id'];
0364 $result = $db->sql_query_limit($sql, 1);
0365 $row = $db->sql_fetchrow($result);
0366 $db->sql_freeresult($result);
0367
0368 $config->set('newest_user_colour', $row['group_colour'], false);
0369 }
0370
0371 // Use default notifications settings if notifications_data is not set
0372 if ($notifications_data === null)
0373 {
0374 $notifications_data = array(
0375 array(
0376 'item_type' => 'notification.type.post',
0377 'method' => 'notification.method.email',
0378 ),
0379 array(
0380 'item_type' => 'notification.type.topic',
0381 'method' => 'notification.method.email',
0382 ),
0383 );
0384 }
0385
0386 /**
0387 * Modify the notifications data to be inserted in the database when a user is added
0388 *
0389 * @event core.user_add_modify_notifications_data
0390 * @var array user_row Array of user details submitted to user_add
0391 * @var array cp_data Array of Custom profile fields submitted to user_add
0392 * @var array sql_ary Array of data to be inserted when a user is added
0393 * @var array notifications_data Array of notification data to be inserted when a user is added
0394 * @since 3.2.2-RC1
0395 */
0396 $vars = array('user_row', 'cp_data', 'sql_ary', 'notifications_data');
0397 extract($phpbb_dispatcher->trigger_event('core.user_add_modify_notifications_data', compact($vars)));
0398
0399 // Subscribe user to notifications if necessary
0400 if (!empty($notifications_data))
0401 {
0402 /* @var $phpbb_notifications \phpbb\notification\manager */
0403 $phpbb_notifications = $phpbb_container->get('notification_manager');
0404 foreach ($notifications_data as $subscription)
0405 {
0406 $phpbb_notifications->add_subscription($subscription['item_type'], 0, $subscription['method'], $user_id);
0407 }
0408 }
0409
0410 /**
0411 * Event that returns user id, user details and user CPF of newly registered user
0412 *
0413 * @event core.user_add_after
0414 * @var int user_id User id of newly registered user
0415 * @var array user_row Array of user details submitted to user_add
0416 * @var array cp_data Array of Custom profile fields submitted to user_add
0417 * @since 3.1.0-b5
0418 */
0419 $vars = array('user_id', 'user_row', 'cp_data');
0420 extract($phpbb_dispatcher->trigger_event('core.user_add_after', compact($vars)));
0421
0422 return $user_id;
0423 }
0424
0425 /**
0426 * Delete user(s) and their related data
0427 *
0428 * @param string $mode Mode of posts deletion (retain|remove)
0429 * @param mixed $user_ids Either an array of integers or an integer
0430 * @param bool $retain_username True if username should be retained, false otherwise
0431 * @return bool
0432 */
0433 function user_delete($mode, $user_ids, $retain_username = true)
0434 {
0435 global $cache, $config, $db, $user, $phpbb_dispatcher, $phpbb_container;
0436 global $phpbb_root_path, $phpEx;
0437
0438 $db->sql_transaction('begin');
0439
0440 $user_rows = array();
0441 if (!is_array($user_ids))
0442 {
0443 $user_ids = array($user_ids);
0444 }
0445
0446 $user_id_sql = $db->sql_in_set('user_id', $user_ids);
0447
0448 $sql = 'SELECT *
0449 FROM ' . USERS_TABLE . '
0450 WHERE ' . $user_id_sql;
0451 $result = $db->sql_query($sql);
0452 while ($row = $db->sql_fetchrow($result))
0453 {
0454 $user_rows[(int) $row['user_id']] = $row;
0455 }
0456 $db->sql_freeresult($result);
0457
0458 if (empty($user_rows))
0459 {
0460 return false;
0461 }
0462
0463 /**
0464 * Event before of the performing of the user(s) delete action
0465 *
0466 * @event core.delete_user_before
0467 * @var string mode Mode of posts deletion (retain|remove)
0468 * @var array user_ids ID(s) of the user(s) bound to be deleted
0469 * @var bool retain_username True if username should be retained, false otherwise
0470 * @var array user_rows Array containing data of the user(s) bound to be deleted
0471 * @since 3.1.0-a1
0472 * @changed 3.2.4-RC1 Added user_rows
0473 */
0474 $vars = array('mode', 'user_ids', 'retain_username', 'user_rows');
0475 extract($phpbb_dispatcher->trigger_event('core.delete_user_before', compact($vars)));
0476
0477 // Before we begin, we will remove the reports the user issued.
0478 $sql = 'SELECT r.post_id, p.topic_id
0479 FROM ' . REPORTS_TABLE . ' r, ' . POSTS_TABLE . ' p
0480 WHERE ' . $db->sql_in_set('r.user_id', $user_ids) . '
0481 AND p.post_id = r.post_id';
0482 $result = $db->sql_query($sql);
0483
0484 $report_posts = $report_topics = array();
0485 while ($row = $db->sql_fetchrow($result))
0486 {
0487 $report_posts[] = $row['post_id'];
0488 $report_topics[] = $row['topic_id'];
0489 }
0490 $db->sql_freeresult($result);
0491
0492 if (count($report_posts))
0493 {
0494 $report_posts = array_unique($report_posts);
0495 $report_topics = array_unique($report_topics);
0496
0497 // Get a list of topics that still contain reported posts
0498 $sql = 'SELECT DISTINCT topic_id
0499 FROM ' . POSTS_TABLE . '
0500 WHERE ' . $db->sql_in_set('topic_id', $report_topics) . '
0501 AND post_reported = 1
0502 AND ' . $db->sql_in_set('post_id', $report_posts, true);
0503 $result = $db->sql_query($sql);
0504
0505 $keep_report_topics = array();
0506 while ($row = $db->sql_fetchrow($result))
0507 {
0508 $keep_report_topics[] = $row['topic_id'];
0509 }
0510 $db->sql_freeresult($result);
0511
0512 if (count($keep_report_topics))
0513 {
0514 $report_topics = array_diff($report_topics, $keep_report_topics);
0515 }
0516 unset($keep_report_topics);
0517
0518 // Now set the flags back
0519 $sql = 'UPDATE ' . POSTS_TABLE . '
0520 SET post_reported = 0
0521 WHERE ' . $db->sql_in_set('post_id', $report_posts);
0522 $db->sql_query($sql);
0523
0524 if (count($report_topics))
0525 {
0526 $sql = 'UPDATE ' . TOPICS_TABLE . '
0527 SET topic_reported = 0
0528 WHERE ' . $db->sql_in_set('topic_id', $report_topics);
0529 $db->sql_query($sql);
0530 }
0531 }
0532
0533 // Remove reports
0534 $db->sql_query('DELETE FROM ' . REPORTS_TABLE . ' WHERE ' . $user_id_sql);
0535
0536 $num_users_delta = 0;
0537
0538 // Get auth provider collection in case accounts might need to be unlinked
0539 $provider_collection = $phpbb_container->get('auth.provider_collection');
0540
0541 // Some things need to be done in the loop (if the query changes based
0542 // on which user is currently being deleted)
0543 $added_guest_posts = 0;
0544 foreach ($user_rows as $user_id => $user_row)
0545 {
0546 if ($user_row['user_avatar'] && $user_row['user_avatar_type'] == 'avatar.driver.upload')
0547 {
0548 avatar_delete('user', $user_row);
0549 }
0550
0551 // Unlink accounts
0552 foreach ($provider_collection as $provider_name => $auth_provider)
0553 {
0554 $provider_data = $auth_provider->get_auth_link_data($user_id);
0555
0556 if ($provider_data !== null)
0557 {
0558 $link_data = array(
0559 'user_id' => $user_id,
0560 'link_method' => 'user_delete',
0561 );
0562
0563 // BLOCK_VARS might contain hidden fields necessary for unlinking accounts
0564 if (isset($provider_data['BLOCK_VARS']) && is_array($provider_data['BLOCK_VARS']))
0565 {
0566 foreach ($provider_data['BLOCK_VARS'] as $provider_service)
0567 {
0568 if (!array_key_exists('HIDDEN_FIELDS', $provider_service))
0569 {
0570 $provider_service['HIDDEN_FIELDS'] = array();
0571 }
0572
0573 $auth_provider->unlink_account(array_merge($link_data, $provider_service['HIDDEN_FIELDS']));
0574 }
0575 }
0576 else
0577 {
0578 $auth_provider->unlink_account($link_data);
0579 }
0580 }
0581 }
0582
0583 // Decrement number of users if this user is active
0584 if ($user_row['user_type'] != USER_INACTIVE && $user_row['user_type'] != USER_IGNORE)
0585 {
0586 --$num_users_delta;
0587 }
0588
0589 switch ($mode)
0590 {
0591 case 'retain':
0592 if ($retain_username === false)
0593 {
0594 $post_username = $user->lang['GUEST'];
0595 }
0596 else
0597 {
0598 $post_username = $user_row['username'];
0599 }
0600
0601 // If the user is inactive and newly registered
0602 // we assume no posts from the user, and save
0603 // the queries
0604 if ($user_row['user_type'] != USER_INACTIVE || $user_row['user_inactive_reason'] != INACTIVE_REGISTER || $user_row['user_posts'])
0605 {
0606 // When we delete these users and retain the posts, we must assign all the data to the guest user
0607 $sql = 'UPDATE ' . FORUMS_TABLE . '
0608 SET forum_last_poster_id = ' . ANONYMOUS . ", forum_last_poster_name = '" . $db->sql_escape($post_username) . "', forum_last_poster_colour = ''
0609 WHERE forum_last_poster_id = $user_id";
0610 $db->sql_query($sql);
0611
0612 $sql = 'UPDATE ' . POSTS_TABLE . '
0613 SET poster_id = ' . ANONYMOUS . ", post_username = '" . $db->sql_escape($post_username) . "'
0614 WHERE poster_id = $user_id";
0615 $db->sql_query($sql);
0616
0617 $sql = 'UPDATE ' . TOPICS_TABLE . '
0618 SET topic_poster = ' . ANONYMOUS . ", topic_first_poster_name = '" . $db->sql_escape($post_username) . "', topic_first_poster_colour = ''
0619 WHERE topic_poster = $user_id";
0620 $db->sql_query($sql);
0621
0622 $sql = 'UPDATE ' . TOPICS_TABLE . '
0623 SET topic_last_poster_id = ' . ANONYMOUS . ", topic_last_poster_name = '" . $db->sql_escape($post_username) . "', topic_last_poster_colour = ''
0624 WHERE topic_last_poster_id = $user_id";
0625 $db->sql_query($sql);
0626
0627 // Since we change every post by this author, we need to count this amount towards the anonymous user
0628
0629 if ($user_row['user_posts'])
0630 {
0631 $added_guest_posts += $user_row['user_posts'];
0632 }
0633 }
0634 break;
0635
0636 case 'remove':
0637 // there is nothing variant specific to deleting posts
0638 break;
0639 }
0640 }
0641
0642 if ($num_users_delta != 0)
0643 {
0644 $config->increment('num_users', $num_users_delta, false);
0645 }
0646
0647 // Now do the invariant tasks
0648 // all queries performed in one call of this function are in a single transaction
0649 // so this is kosher
0650 if ($mode == 'retain')
0651 {
0652 // Assign more data to the Anonymous user
0653 $sql = 'UPDATE ' . ATTACHMENTS_TABLE . '
0654 SET poster_id = ' . ANONYMOUS . '
0655 WHERE ' . $db->sql_in_set('poster_id', $user_ids);
0656 $db->sql_query($sql);
0657
0658 $sql = 'UPDATE ' . USERS_TABLE . '
0659 SET user_posts = user_posts + ' . $added_guest_posts . '
0660 WHERE user_id = ' . ANONYMOUS;
0661 $db->sql_query($sql);
0662 }
0663 else if ($mode == 'remove')
0664 {
0665 if (!function_exists('delete_posts'))
0666 {
0667 include($phpbb_root_path . 'includes/functions_admin.' . $phpEx);
0668 }
0669
0670 // Delete posts, attachments, etc.
0671 // delete_posts can handle any number of IDs in its second argument
0672 delete_posts('poster_id', $user_ids);
0673 }
0674
0675 $table_ary = [
0676 USERS_TABLE,
0677 USER_GROUP_TABLE,
0678 TOPICS_WATCH_TABLE,
0679 FORUMS_WATCH_TABLE,
0680 ACL_USERS_TABLE,
0681 TOPICS_TRACK_TABLE,
0682 TOPICS_POSTED_TABLE,
0683 FORUMS_TRACK_TABLE,
0684 PROFILE_FIELDS_DATA_TABLE,
0685 MODERATOR_CACHE_TABLE,
0686 DRAFTS_TABLE,
0687 BOOKMARKS_TABLE,
0688 SESSIONS_KEYS_TABLE,
0689 PRIVMSGS_FOLDER_TABLE,
0690 PRIVMSGS_RULES_TABLE,
0691 $phpbb_container->getParameter('tables.auth_provider_oauth_token_storage'),
0692 $phpbb_container->getParameter('tables.auth_provider_oauth_states'),
0693 $phpbb_container->getParameter('tables.auth_provider_oauth_account_assoc'),
0694 $phpbb_container->getParameter('tables.user_notifications')
0695 ];
0696
0697 // Ignore errors on deleting from non-existent tables, e.g. when migrating
0698 $db->sql_return_on_error(true);
0699 // Delete the miscellaneous (non-post) data for the user
0700 foreach ($table_ary as $table)
0701 {
0702 $sql = "DELETE FROM $table
0703 WHERE " . $user_id_sql;
0704 $db->sql_query($sql);
0705 }
0706 $db->sql_return_on_error();
0707
0708 $cache->destroy('sql', MODERATOR_CACHE_TABLE);
0709
0710 // Change user_id to anonymous for posts edited by this user
0711 $sql = 'UPDATE ' . POSTS_TABLE . '
0712 SET post_edit_user = ' . ANONYMOUS . '
0713 WHERE ' . $db->sql_in_set('post_edit_user', $user_ids);
0714 $db->sql_query($sql);
0715
0716 // Change user_id to anonymous for pms edited by this user
0717 $sql = 'UPDATE ' . PRIVMSGS_TABLE . '
0718 SET message_edit_user = ' . ANONYMOUS . '
0719 WHERE ' . $db->sql_in_set('message_edit_user', $user_ids);
0720 $db->sql_query($sql);
0721
0722 // Change user_id to anonymous for posts deleted by this user
0723 $sql = 'UPDATE ' . POSTS_TABLE . '
0724 SET post_delete_user = ' . ANONYMOUS . '
0725 WHERE ' . $db->sql_in_set('post_delete_user', $user_ids);
0726 $db->sql_query($sql);
0727
0728 // Change user_id to anonymous for topics deleted by this user
0729 $sql = 'UPDATE ' . TOPICS_TABLE . '
0730 SET topic_delete_user = ' . ANONYMOUS . '
0731 WHERE ' . $db->sql_in_set('topic_delete_user', $user_ids);
0732 $db->sql_query($sql);
0733
0734 // Delete user log entries about this user
0735 $sql = 'DELETE FROM ' . LOG_TABLE . '
0736 WHERE ' . $db->sql_in_set('reportee_id', $user_ids);
0737 $db->sql_query($sql);
0738
0739 // Change user_id to anonymous for this users triggered events
0740 $sql = 'UPDATE ' . LOG_TABLE . '
0741 SET user_id = ' . ANONYMOUS . '
0742 WHERE ' . $user_id_sql;
0743 $db->sql_query($sql);
0744
0745 // Delete the user_id from the zebra table
0746 $sql = 'DELETE FROM ' . ZEBRA_TABLE . '
0747 WHERE ' . $user_id_sql . '
0748 OR ' . $db->sql_in_set('zebra_id', $user_ids);
0749 $db->sql_query($sql);
0750
0751 // Delete the user_id from the banlist
0752 $sql = 'DELETE FROM ' . BANLIST_TABLE . '
0753 WHERE ' . $db->sql_in_set('ban_userid', $user_ids);
0754 $db->sql_query($sql);
0755
0756 // Delete the user_id from the session table
0757 $sql = 'DELETE FROM ' . SESSIONS_TABLE . '
0758 WHERE ' . $db->sql_in_set('session_user_id', $user_ids);
0759 $db->sql_query($sql);
0760
0761 // Clean the private messages tables from the user
0762 if (!function_exists('phpbb_delete_users_pms'))
0763 {
0764 include($phpbb_root_path . 'includes/functions_privmsgs.' . $phpEx);
0765 }
0766 phpbb_delete_users_pms($user_ids);
0767
0768 $phpbb_notifications = $phpbb_container->get('notification_manager');
0769 $phpbb_notifications->delete_notifications('notification.type.admin_activate_user', $user_ids);
0770
0771 $db->sql_transaction('commit');
0772
0773 /**
0774 * Event after the user(s) delete action has been performed
0775 *
0776 * @event core.delete_user_after
0777 * @var string mode Mode of posts deletion (retain|remove)
0778 * @var array user_ids ID(s) of the deleted user(s)
0779 * @var bool retain_username True if username should be retained, false otherwise
0780 * @var array user_rows Array containing data of the deleted user(s)
0781 * @since 3.1.0-a1
0782 * @changed 3.2.2-RC1 Added user_rows
0783 */
0784 $vars = array('mode', 'user_ids', 'retain_username', 'user_rows');
0785 extract($phpbb_dispatcher->trigger_event('core.delete_user_after', compact($vars)));
0786
0787 // Reset newest user info if appropriate
0788 if (in_array($config['newest_user_id'], $user_ids))
0789 {
0790 update_last_username();
0791 }
0792
0793 return false;
0794 }
0795
0796 /**
0797 * Flips user_type from active to inactive and vice versa, handles group membership updates
0798 *
0799 * @param string $mode can be flip for flipping from active/inactive, activate or deactivate
0800 * @param array $user_id_ary
0801 * @param int $reason
0802 */
0803 function user_active_flip($mode, $user_id_ary, $reason = INACTIVE_MANUAL)
0804 {
0805 global $config, $db, $user, $auth, $phpbb_dispatcher;
0806
0807 $deactivated = $activated = 0;
0808 $sql_statements = array();
0809
0810 if (!is_array($user_id_ary))
0811 {
0812 $user_id_ary = array($user_id_ary);
0813 }
0814
0815 if (!count($user_id_ary))
0816 {
0817 return;
0818 }
0819
0820 $sql = 'SELECT user_id, group_id, user_type, user_inactive_reason
0821 FROM ' . USERS_TABLE . '
0822 WHERE ' . $db->sql_in_set('user_id', $user_id_ary);
0823 $result = $db->sql_query($sql);
0824
0825 while ($row = $db->sql_fetchrow($result))
0826 {
0827 $sql_ary = array();
0828
0829 if ($row['user_type'] == USER_IGNORE || $row['user_type'] == USER_FOUNDER ||
0830 ($mode == 'activate' && $row['user_type'] != USER_INACTIVE) ||
0831 ($mode == 'deactivate' && $row['user_type'] == USER_INACTIVE))
0832 {
0833 continue;
0834 }
0835
0836 if ($row['user_type'] == USER_INACTIVE)
0837 {
0838 $activated++;
0839 }
0840 else
0841 {
0842 $deactivated++;
0843
0844 // Remove the users session key...
0845 $user->reset_login_keys($row['user_id']);
0846 }
0847
0848 $sql_ary += array(
0849 'user_type' => ($row['user_type'] == USER_NORMAL) ? USER_INACTIVE : USER_NORMAL,
0850 'user_inactive_time' => ($row['user_type'] == USER_NORMAL) ? time() : 0,
0851 'user_inactive_reason' => ($row['user_type'] == USER_NORMAL) ? $reason : 0,
0852 );
0853
0854 $sql_statements[$row['user_id']] = $sql_ary;
0855 }
0856 $db->sql_freeresult($result);
0857
0858 /**
0859 * Check or modify activated/deactivated users data before submitting it to the database
0860 *
0861 * @event core.user_active_flip_before
0862 * @var string mode User type changing mode, can be: flip|activate|deactivate
0863 * @var int reason Reason for changing user type, can be: INACTIVE_REGISTER|INACTIVE_PROFILE|INACTIVE_MANUAL|INACTIVE_REMIND
0864 * @var int activated The number of users to be activated
0865 * @var int deactivated The number of users to be deactivated
0866 * @var array user_id_ary Array with user ids to change user type
0867 * @var array sql_statements Array with users data to submit to the database, keys: user ids, values: arrays with user data
0868 * @since 3.1.4-RC1
0869 */
0870 $vars = array('mode', 'reason', 'activated', 'deactivated', 'user_id_ary', 'sql_statements');
0871 extract($phpbb_dispatcher->trigger_event('core.user_active_flip_before', compact($vars)));
0872
0873 if (count($sql_statements))
0874 {
0875 foreach ($sql_statements as $user_id => $sql_ary)
0876 {
0877 $sql = 'UPDATE ' . USERS_TABLE . '
0878 SET ' . $db->sql_build_array('UPDATE', $sql_ary) . '
0879 WHERE user_id = ' . $user_id;
0880 $db->sql_query($sql);
0881 }
0882
0883 $auth->acl_clear_prefetch(array_keys($sql_statements));
0884 }
0885
0886 /**
0887 * Perform additional actions after the users have been activated/deactivated
0888 *
0889 * @event core.user_active_flip_after
0890 * @var string mode User type changing mode, can be: flip|activate|deactivate
0891 * @var int reason Reason for changing user type, can be: INACTIVE_REGISTER|INACTIVE_PROFILE|INACTIVE_MANUAL|INACTIVE_REMIND
0892 * @var int activated The number of users to be activated
0893 * @var int deactivated The number of users to be deactivated
0894 * @var array user_id_ary Array with user ids to change user type
0895 * @var array sql_statements Array with users data to submit to the database, keys: user ids, values: arrays with user data
0896 * @since 3.1.4-RC1
0897 */
0898 $vars = array('mode', 'reason', 'activated', 'deactivated', 'user_id_ary', 'sql_statements');
0899 extract($phpbb_dispatcher->trigger_event('core.user_active_flip_after', compact($vars)));
0900
0901 if ($deactivated)
0902 {
0903 $config->increment('num_users', $deactivated * (-1), false);
0904 }
0905
0906 if ($activated)
0907 {
0908 $config->increment('num_users', $activated, false);
0909 }
0910
0911 // Update latest username
0912 update_last_username();
0913 }
0914
0915 /**
0916 * Add a ban or ban exclusion to the banlist. Bans either a user, an IP or an email address
0917 *
0918 * @param string $mode Type of ban. One of the following: user, ip, email
0919 * @param mixed $ban Banned entity. Either string or array with usernames, ips or email addresses
0920 * @param int $ban_len Ban length in minutes
0921 * @param string $ban_len_other Ban length as a date (YYYY-MM-DD)
0922 * @param boolean $ban_exclude Exclude these entities from banning?
0923 * @param string $ban_reason String describing the reason for this ban
0924 * @param string $ban_give_reason
0925 * @return boolean
0926 */
0927 function user_ban($mode, $ban, $ban_len, $ban_len_other, $ban_exclude, $ban_reason, $ban_give_reason = '')
0928 {
0929 global $db, $user, $cache, $phpbb_log;
0930
0931 // Delete stale bans
0932 $sql = 'DELETE FROM ' . BANLIST_TABLE . '
0933 WHERE ban_end < ' . time() . '
0934 AND ban_end <> 0';
0935 $db->sql_query($sql);
0936
0937 $ban_list = (!is_array($ban)) ? array_unique(explode("\n", $ban)) : $ban;
0938 $ban_list_log = implode(', ', $ban_list);
0939
0940 $current_time = time();
0941
0942 // Set $ban_end to the unix time when the ban should end. 0 is a permanent ban.
0943 if ($ban_len)
0944 {
0945 if ($ban_len != -1 || !$ban_len_other)
0946 {
0947 $ban_end = max($current_time, $current_time + ($ban_len) * 60);
0948 }
0949 else
0950 {
0951 $ban_other = explode('-', $ban_len_other);
0952 if (count($ban_other) == 3 && ((int) $ban_other[0] < 9999) &&
0953 (strlen($ban_other[0]) == 4) && (strlen($ban_other[1]) == 2) && (strlen($ban_other[2]) == 2))
0954 {
0955 $ban_end = max($current_time, $user->create_datetime()
0956 ->setDate((int) $ban_other[0], (int) $ban_other[1], (int) $ban_other[2])
0957 ->setTime(0, 0, 0)
0958 ->getTimestamp() + $user->timezone->getOffset(new DateTime('UTC')));
0959 }
0960 else
0961 {
0962 trigger_error('LENGTH_BAN_INVALID', E_USER_WARNING);
0963 }
0964 }
0965 }
0966 else
0967 {
0968 $ban_end = 0;
0969 }
0970
0971 $founder = $founder_names = array();
0972
0973 if (!$ban_exclude)
0974 {
0975 // Create a list of founder...
0976 $sql = 'SELECT user_id, user_email, username_clean
0977 FROM ' . USERS_TABLE . '
0978 WHERE user_type = ' . USER_FOUNDER;
0979 $result = $db->sql_query($sql);
0980
0981 while ($row = $db->sql_fetchrow($result))
0982 {
0983 $founder[$row['user_id']] = $row['user_email'];
0984 $founder_names[$row['user_id']] = $row['username_clean'];
0985 }
0986 $db->sql_freeresult($result);
0987 }
0988
0989 $banlist_ary = array();
0990
0991 switch ($mode)
0992 {
0993 case 'user':
0994 $type = 'ban_userid';
0995
0996 // At the moment we do not support wildcard username banning
0997
0998 // Select the relevant user_ids.
0999 $sql_usernames = array();
1000
1001 foreach ($ban_list as $username)
1002 {
1003 $username = trim($username);
1004 if ($username != '')
1005 {
1006 $clean_name = utf8_clean_string($username);
1007 if ($clean_name == $user->data['username_clean'])
1008 {
1009 trigger_error('CANNOT_BAN_YOURSELF', E_USER_WARNING);
1010 }
1011 if (in_array($clean_name, $founder_names))
1012 {
1013 trigger_error('CANNOT_BAN_FOUNDER', E_USER_WARNING);
1014 }
1015 $sql_usernames[] = $clean_name;
1016 }
1017 }
1018
1019 // Make sure we have been given someone to ban
1020 if (!count($sql_usernames))
1021 {
1022 trigger_error('NO_USER_SPECIFIED', E_USER_WARNING);
1023 }
1024
1025 $sql = 'SELECT user_id
1026 FROM ' . USERS_TABLE . '
1027 WHERE ' . $db->sql_in_set('username_clean', $sql_usernames);
1028
1029 // Do not allow banning yourself, the guest account, or founders.
1030 $non_bannable = array($user->data['user_id'], ANONYMOUS);
1031 if (count($founder))
1032 {
1033 $sql .= ' AND ' . $db->sql_in_set('user_id', array_merge(array_keys($founder), $non_bannable), true);
1034 }
1035 else
1036 {
1037 $sql .= ' AND ' . $db->sql_in_set('user_id', $non_bannable, true);
1038 }
1039
1040 $result = $db->sql_query($sql);
1041
1042 if ($row = $db->sql_fetchrow($result))
1043 {
1044 do
1045 {
1046 $banlist_ary[] = (int) $row['user_id'];
1047 }
1048 while ($row = $db->sql_fetchrow($result));
1049
1050 $db->sql_freeresult($result);
1051 }
1052 else
1053 {
1054 $db->sql_freeresult($result);
1055
1056 trigger_error('NO_USERS', E_USER_WARNING);
1057 }
1058 break;
1059
1060 case 'ip':
1061 $type = 'ban_ip';
1062
1063 foreach ($ban_list as $ban_item)
1064 {
1065 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))
1066 {
1067 // This is an IP range
1068 // Don't ask about all this, just don't ask ... !
1069 $ip_1_counter = $ip_range_explode[1];
1070 $ip_1_end = $ip_range_explode[5];
1071
1072 while ($ip_1_counter <= $ip_1_end)
1073 {
1074 $ip_2_counter = ($ip_1_counter == $ip_range_explode[1]) ? $ip_range_explode[2] : 0;
1075 $ip_2_end = ($ip_1_counter < $ip_1_end) ? 254 : $ip_range_explode[6];
1076
1077 if ($ip_2_counter == 0 && $ip_2_end == 254)
1078 {
1079 $ip_2_counter = 256;
1080
1081 $banlist_ary[] = "$ip_1_counter.*";
1082 }
1083
1084 while ($ip_2_counter <= $ip_2_end)
1085 {
1086 $ip_3_counter = ($ip_2_counter == $ip_range_explode[2] && $ip_1_counter == $ip_range_explode[1]) ? $ip_range_explode[3] : 0;
1087 $ip_3_end = ($ip_2_counter < $ip_2_end || $ip_1_counter < $ip_1_end) ? 254 : $ip_range_explode[7];
1088
1089 if ($ip_3_counter == 0 && $ip_3_end == 254)
1090 {
1091 $ip_3_counter = 256;
1092
1093 $banlist_ary[] = "$ip_1_counter.$ip_2_counter.*";
1094 }
1095
1096 while ($ip_3_counter <= $ip_3_end)
1097 {
1098 $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;
1099 $ip_4_end = ($ip_3_counter < $ip_3_end || $ip_2_counter < $ip_2_end) ? 254 : $ip_range_explode[8];
1100
1101 if ($ip_4_counter == 0 && $ip_4_end == 254)
1102 {
1103 $ip_4_counter = 256;
1104
1105 $banlist_ary[] = "$ip_1_counter.$ip_2_counter.$ip_3_counter.*";
1106 }
1107
1108 while ($ip_4_counter <= $ip_4_end)
1109 {
1110 $banlist_ary[] = "$ip_1_counter.$ip_2_counter.$ip_3_counter.$ip_4_counter";
1111 $ip_4_counter++;
1112 }
1113 $ip_3_counter++;
1114 }
1115 $ip_2_counter++;
1116 }
1117 $ip_1_counter++;
1118 }
1119 }
1120 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)))
1121 {
1122 // Normal IP address
1123 $banlist_ary[] = trim($ban_item);
1124 }
1125 else if (preg_match('#^\*$#', trim($ban_item)))
1126 {
1127 // Ban all IPs
1128 $banlist_ary[] = '*';
1129 }
1130 else if (preg_match('#^([\w\-_]\.?){2,}$#is', trim($ban_item)))
1131 {
1132 // hostname
1133 $ip_ary = gethostbynamel(trim($ban_item));
1134
1135 if (!empty($ip_ary))
1136 {
1137 foreach ($ip_ary as $ip)
1138 {
1139 if ($ip)
1140 {
1141 if (strlen($ip) > 40)
1142 {
1143 continue;
1144 }
1145
1146 $banlist_ary[] = $ip;
1147 }
1148 }
1149 }
1150 }
1151
1152 if (empty($banlist_ary))
1153 {
1154 trigger_error('NO_IPS_DEFINED', E_USER_WARNING);
1155 }
1156 }
1157 break;
1158
1159 case 'email':
1160 $type = 'ban_email';
1161
1162 foreach ($ban_list as $ban_item)
1163 {
1164 $ban_item = trim($ban_item);
1165
1166 if (preg_match('#^.*?@*|(([a-z0-9\-]+\.)+([a-z]{2,3}))$#i', $ban_item))
1167 {
1168 if (strlen($ban_item) > 100)
1169 {
1170 continue;
1171 }
1172
1173 if (!count($founder) || !in_array($ban_item, $founder))
1174 {
1175 $banlist_ary[] = $ban_item;
1176 }
1177 }
1178 }
1179
1180 if (count($ban_list) == 0)
1181 {
1182 trigger_error('NO_EMAILS_DEFINED', E_USER_WARNING);
1183 }
1184 break;
1185
1186 default:
1187 trigger_error('NO_MODE', E_USER_WARNING);
1188 break;
1189 }
1190
1191 // Fetch currently set bans of the specified type and exclude state. Prevent duplicate bans.
1192 $sql_where = ($type == 'ban_userid') ? 'ban_userid <> 0' : "$type <> ''";
1193
1194 $sql = "SELECT $type
1195 FROM " . BANLIST_TABLE . "
1196 WHERE $sql_where
1197 AND ban_exclude = " . (int) $ban_exclude;
1198 $result = $db->sql_query($sql);
1199
1200 // Reset $sql_where, because we use it later...
1201 $sql_where = '';
1202
1203 if ($row = $db->sql_fetchrow($result))
1204 {
1205 $banlist_ary_tmp = array();
1206 do
1207 {
1208 switch ($mode)
1209 {
1210 case 'user':
1211 $banlist_ary_tmp[] = $row['ban_userid'];
1212 break;
1213
1214 case 'ip':
1215 $banlist_ary_tmp[] = $row['ban_ip'];
1216 break;
1217
1218 case 'email':
1219 $banlist_ary_tmp[] = $row['ban_email'];
1220 break;
1221 }
1222 }
1223 while ($row = $db->sql_fetchrow($result));
1224
1225 $banlist_ary_tmp = array_intersect($banlist_ary, $banlist_ary_tmp);
1226
1227 if (count($banlist_ary_tmp))
1228 {
1229 // One or more entities are already banned/excluded, delete the existing bans, so they can be re-inserted with the given new length
1230 $sql = 'DELETE FROM ' . BANLIST_TABLE . '
1231 WHERE ' . $db->sql_in_set($type, $banlist_ary_tmp) . '
1232 AND ban_exclude = ' . (int) $ban_exclude;
1233 $db->sql_query($sql);
1234 }
1235
1236 unset($banlist_ary_tmp);
1237 }
1238 $db->sql_freeresult($result);
1239
1240 // We have some entities to ban
1241 if (count($banlist_ary))
1242 {
1243 $sql_ary = array();
1244
1245 foreach ($banlist_ary as $ban_entry)
1246 {
1247 $sql_ary[] = array(
1248 $type => $ban_entry,
1249 'ban_start' => (int) $current_time,
1250 'ban_end' => (int) $ban_end,
1251 'ban_exclude' => (int) $ban_exclude,
1252 'ban_reason' => (string) $ban_reason,
1253 'ban_give_reason' => (string) $ban_give_reason,
1254 );
1255 }
1256
1257 $db->sql_multi_insert(BANLIST_TABLE, $sql_ary);
1258
1259 // If we are banning we want to logout anyone matching the ban
1260 if (!$ban_exclude)
1261 {
1262 switch ($mode)
1263 {
1264 case 'user':
1265 $sql_where = 'WHERE ' . $db->sql_in_set('session_user_id', $banlist_ary);
1266 break;
1267
1268 case 'ip':
1269 $sql_where = 'WHERE ' . $db->sql_in_set('session_ip', $banlist_ary);
1270 break;
1271
1272 case 'email':
1273 $banlist_ary_sql = array();
1274
1275 foreach ($banlist_ary as $ban_entry)
1276 {
1277 $banlist_ary_sql[] = (string) str_replace('*', '%', $ban_entry);
1278 }
1279
1280 $sql = 'SELECT user_id
1281 FROM ' . USERS_TABLE . '
1282 WHERE ' . $db->sql_in_set('user_email', $banlist_ary_sql);
1283 $result = $db->sql_query($sql);
1284
1285 $sql_in = array();
1286
1287 if ($row = $db->sql_fetchrow($result))
1288 {
1289 do
1290 {
1291 $sql_in[] = $row['user_id'];
1292 }
1293 while ($row = $db->sql_fetchrow($result));
1294
1295 $sql_where = 'WHERE ' . $db->sql_in_set('session_user_id', $sql_in);
1296 }
1297 $db->sql_freeresult($result);
1298 break;
1299 }
1300
1301 if (isset($sql_where) && $sql_where)
1302 {
1303 $sql = 'DELETE FROM ' . SESSIONS_TABLE . "
1304 $sql_where";
1305 $db->sql_query($sql);
1306
1307 if ($mode == 'user')
1308 {
1309 $sql = 'DELETE FROM ' . SESSIONS_KEYS_TABLE . ' ' . ((in_array('*', $banlist_ary)) ? '' : 'WHERE ' . $db->sql_in_set('user_id', $banlist_ary));
1310 $db->sql_query($sql);
1311 }
1312 }
1313 }
1314
1315 // Update log
1316 $log_entry = ($ban_exclude) ? 'LOG_BAN_EXCLUDE_' : 'LOG_BAN_';
1317
1318 // Add to admin log, moderator log and user notes
1319 $phpbb_log->add('admin', $user->data['user_id'], $user->ip, $log_entry . strtoupper($mode), false, array($ban_reason, $ban_list_log));
1320 $phpbb_log->add('mod', $user->data['user_id'], $user->ip, $log_entry . strtoupper($mode), false, array(
1321 'forum_id' => 0,
1322 'topic_id' => 0,
1323 $ban_reason,
1324 $ban_list_log
1325 ));
1326 if ($mode == 'user')
1327 {
1328 foreach ($banlist_ary as $user_id)
1329 {
1330 $phpbb_log->add('user', $user->data['user_id'], $user->ip, $log_entry . strtoupper($mode), false, array(
1331 'reportee_id' => $user_id,
1332 $ban_reason,
1333 $ban_list_log
1334 ));
1335 }
1336 }
1337
1338 $cache->destroy('sql', BANLIST_TABLE);
1339
1340 return true;
1341 }
1342
1343 // There was nothing to ban/exclude. But destroying the cache because of the removal of stale bans.
1344 $cache->destroy('sql', BANLIST_TABLE);
1345
1346 return false;
1347 }
1348
1349 /**
1350 * Unban User
1351 */
1352 function user_unban($mode, $ban)
1353 {
1354 global $db, $user, $cache, $phpbb_log, $phpbb_dispatcher;
1355
1356 // Delete stale bans
1357 $sql = 'DELETE FROM ' . BANLIST_TABLE . '
1358 WHERE ban_end < ' . time() . '
1359 AND ban_end <> 0';
1360 $db->sql_query($sql);
1361
1362 if (!is_array($ban))
1363 {
1364 $ban = array($ban);
1365 }
1366
1367 $unban_sql = array_map('intval', $ban);
1368
1369 if (count($unban_sql))
1370 {
1371 // Grab details of bans for logging information later
1372 switch ($mode)
1373 {
1374 case 'user':
1375 $sql = 'SELECT u.username AS unban_info, u.user_id
1376 FROM ' . USERS_TABLE . ' u, ' . BANLIST_TABLE . ' b
1377 WHERE ' . $db->sql_in_set('b.ban_id', $unban_sql) . '
1378 AND u.user_id = b.ban_userid';
1379 break;
1380
1381 case 'email':
1382 $sql = 'SELECT ban_email AS unban_info
1383 FROM ' . BANLIST_TABLE . '
1384 WHERE ' . $db->sql_in_set('ban_id', $unban_sql);
1385 break;
1386
1387 case 'ip':
1388 $sql = 'SELECT ban_ip AS unban_info
1389 FROM ' . BANLIST_TABLE . '
1390 WHERE ' . $db->sql_in_set('ban_id', $unban_sql);
1391 break;
1392 }
1393 $result = $db->sql_query($sql);
1394
1395 $l_unban_list = '';
1396 $user_ids_ary = array();
1397 while ($row = $db->sql_fetchrow($result))
1398 {
1399 $l_unban_list .= (($l_unban_list != '') ? ', ' : '') . $row['unban_info'];
1400 if ($mode == 'user')
1401 {
1402 $user_ids_ary[] = $row['user_id'];
1403 }
1404 }
1405 $db->sql_freeresult($result);
1406
1407 $sql = 'DELETE FROM ' . BANLIST_TABLE . '
1408 WHERE ' . $db->sql_in_set('ban_id', $unban_sql);
1409 $db->sql_query($sql);
1410
1411 // Add to moderator log, admin log and user notes
1412 $phpbb_log->add('admin', $user->data['user_id'], $user->ip, 'LOG_UNBAN_' . strtoupper($mode), false, array($l_unban_list));
1413 $phpbb_log->add('mod', $user->data['user_id'], $user->ip, 'LOG_UNBAN_' . strtoupper($mode), false, array(
1414 'forum_id' => 0,
1415 'topic_id' => 0,
1416 $l_unban_list
1417 ));
1418 if ($mode == 'user')
1419 {
1420 foreach ($user_ids_ary as $user_id)
1421 {
1422 $phpbb_log->add('user', $user->data['user_id'], $user->ip, 'LOG_UNBAN_' . strtoupper($mode), false, array(
1423 'reportee_id' => $user_id,
1424 $l_unban_list
1425 ));
1426 }
1427 }
1428
1429 /**
1430 * Use this event to perform actions after the unban has been performed
1431 *
1432 * @event core.user_unban
1433 * @var string mode One of the following: user, ip, email
1434 * @var array user_ids_ary Array with user_ids
1435 * @since 3.1.11-RC1
1436 */
1437 $vars = array(
1438 'mode',
1439 'user_ids_ary',
1440 );
1441 extract($phpbb_dispatcher->trigger_event('core.user_unban', compact($vars)));
1442 }
1443
1444 $cache->destroy('sql', BANLIST_TABLE);
1445
1446 return false;
1447 }
1448
1449 /**
1450 * Internet Protocol Address Whois
1451 * RFC3912: WHOIS Protocol Specification
1452 *
1453 * @param string $ip Ip address, either IPv4 or IPv6.
1454 *
1455 * @return string Empty string if not a valid ip address.
1456 * Otherwise make_clickable()'ed whois result.
1457 */
1458 function user_ipwhois($ip)
1459 {
1460 if (!filter_var($ip, FILTER_VALIDATE_IP))
1461 {
1462 return '';
1463 }
1464
1465 // IPv4 & IPv6 addresses
1466 $whois_host = 'whois.arin.net.';
1467
1468 $ipwhois = '';
1469
1470 if (($fsk = @fsockopen($whois_host, 43)))
1471 {
1472 // CRLF as per RFC3912
1473 // Z to limit the query to all possible flags (whois.arin.net)
1474 fputs($fsk, "z $ip\r\n");
1475 while (!feof($fsk))
1476 {
1477 $ipwhois .= fgets($fsk, 1024);
1478 }
1479 @fclose($fsk);
1480 }
1481
1482 $match = array();
1483
1484 // Test for referrals from $whois_host to other whois databases, roll on rwhois
1485 if (preg_match('#ReferralServer:[\x20]*whois://(.+)#im', $ipwhois, $match))
1486 {
1487 if (strpos($match[1], ':') !== false)
1488 {
1489 $pos = strrpos($match[1], ':');
1490 $server = substr($match[1], 0, $pos);
1491 $port = (int) substr($match[1], $pos + 1);
1492 unset($pos);
1493 }
1494 else
1495 {
1496 $server = $match[1];
1497 $port = 43;
1498 }
1499
1500 $buffer = '';
1501
1502 if (($fsk = @fsockopen($server, $port)))
1503 {
1504 fputs($fsk, "$ip\r\n");
1505 while (!feof($fsk))
1506 {
1507 $buffer .= fgets($fsk, 1024);
1508 }
1509 @fclose($fsk);
1510 }
1511
1512 // Use the result from $whois_host if we don't get any result here
1513 $ipwhois = (empty($buffer)) ? $ipwhois : $buffer;
1514 }
1515
1516 $ipwhois = htmlspecialchars($ipwhois, ENT_COMPAT);
1517
1518 // Magic URL ;)
1519 return trim(make_clickable($ipwhois, false, ''));
1520 }
1521
1522 /**
1523 * Data validation ... used primarily but not exclusively by ucp modules
1524 *
1525 * "Master" function for validating a range of data types
1526 */
1527 function validate_data($data, $val_ary)
1528 {
1529 global $user;
1530
1531 $error = array();
1532
1533 foreach ($val_ary as $var => $val_seq)
1534 {
1535 if (!is_array($val_seq[0]))
1536 {
1537 $val_seq = array($val_seq);
1538 }
1539
1540 foreach ($val_seq as $validate)
1541 {
1542 $function = array_shift($validate);
1543 array_unshift($validate, $data[$var]);
1544
1545 if (is_array($function))
1546 {
1547 $result = call_user_func_array(array($function[0], 'validate_' . $function[1]), $validate);
1548 }
1549 else
1550 {
1551 $function_prefix = (function_exists('phpbb_validate_' . $function)) ? 'phpbb_validate_' : 'validate_';
1552 $result = call_user_func_array($function_prefix . $function, $validate);
1553 }
1554
1555 if ($result)
1556 {
1557 // Since errors are checked later for their language file existence, we need to make sure custom errors are not adjusted.
1558 $error[] = (empty($user->lang[$result . '_' . strtoupper($var)])) ? $result : $result . '_' . strtoupper($var);
1559 }
1560 }
1561 }
1562
1563 return $error;
1564 }
1565
1566 /**
1567 * Validate String
1568 *
1569 * @return boolean|string Either false if validation succeeded or a string which will be used as the error message (with the variable name appended)
1570 */
1571 function validate_string($string, $optional = false, $min = 0, $max = 0)
1572 {
1573 if (empty($string) && $optional)
1574 {
1575 return false;
1576 }
1577
1578 if ($min && utf8_strlen(html_entity_decode($string, ENT_COMPAT)) < $min)
1579 {
1580 return 'TOO_SHORT';
1581 }
1582 else if ($max && utf8_strlen(html_entity_decode($string, ENT_COMPAT)) > $max)
1583 {
1584 return 'TOO_LONG';
1585 }
1586
1587 return false;
1588 }
1589
1590 /**
1591 * Validate Number
1592 *
1593 * @return boolean|string Either false if validation succeeded or a string which will be used as the error message (with the variable name appended)
1594 */
1595 function validate_num($num, $optional = false, $min = 0, $max = 1E99)
1596 {
1597 if (empty($num) && $optional)
1598 {
1599 return false;
1600 }
1601
1602 if ($num < $min)
1603 {
1604 return 'TOO_SMALL';
1605 }
1606 else if ($num > $max)
1607 {
1608 return 'TOO_LARGE';
1609 }
1610
1611 return false;
1612 }
1613
1614 /**
1615 * Validate Date
1616 * @param string $date_string a date in the dd-mm-yyyy format
1617 * @param bool $optional
1618 * @return boolean
1619 */
1620 function validate_date($date_string, $optional = false)
1621 {
1622 $date = explode('-', $date_string);
1623 if ((empty($date) || count($date) != 3) && $optional)
1624 {
1625 return false;
1626 }
1627 else if ($optional)
1628 {
1629 for ($field = 0; $field <= 1; $field++)
1630 {
1631 $date[$field] = (int) $date[$field];
1632 if (empty($date[$field]))
1633 {
1634 $date[$field] = 1;
1635 }
1636 }
1637 $date[2] = (int) $date[2];
1638 // assume an arbitrary leap year
1639 if (empty($date[2]))
1640 {
1641 $date[2] = 1980;
1642 }
1643 }
1644
1645 if (count($date) != 3 || !checkdate($date[1], $date[0], $date[2]))
1646 {
1647 return 'INVALID';
1648 }
1649
1650 return false;
1651 }
1652
1653
1654 /**
1655 * Validate Match
1656 *
1657 * @return boolean|string Either false if validation succeeded or a string which will be used as the error message (with the variable name appended)
1658 */
1659 function validate_match($string, $optional = false, $match = '')
1660 {
1661 if (empty($string) && $optional)
1662 {
1663 return false;
1664 }
1665
1666 if (empty($match))
1667 {
1668 return false;
1669 }
1670
1671 if (!preg_match($match, $string))
1672 {
1673 return 'WRONG_DATA';
1674 }
1675
1676 return false;
1677 }
1678
1679 /**
1680 * Validate Language Pack ISO Name
1681 *
1682 * Tests whether a language name is valid and installed
1683 *
1684 * @param string $lang_iso The language string to test
1685 *
1686 * @return bool|string Either false if validation succeeded or
1687 * a string which will be used as the error message
1688 * (with the variable name appended)
1689 */
1690 function validate_language_iso_name($lang_iso)
1691 {
1692 global $db;
1693
1694 $sql = 'SELECT lang_id
1695 FROM ' . LANG_TABLE . "
1696 WHERE lang_iso = '" . $db->sql_escape($lang_iso) . "'";
1697 $result = $db->sql_query($sql);
1698 $lang_id = (int) $db->sql_fetchfield('lang_id');
1699 $db->sql_freeresult($result);
1700
1701 return ($lang_id) ? false : 'WRONG_DATA';
1702 }
1703
1704 /**
1705 * Validate Timezone Name
1706 *
1707 * Tests whether a timezone name is valid
1708 *
1709 * @param string $timezone The timezone string to test
1710 *
1711 * @return bool|string Either false if validation succeeded or
1712 * a string which will be used as the error message
1713 * (with the variable name appended)
1714 */
1715 function phpbb_validate_timezone($timezone)
1716 {
1717 return (in_array($timezone, phpbb_get_timezone_identifiers($timezone))) ? false : 'TIMEZONE_INVALID';
1718 }
1719
1720 /***
1721 * Validate Username
1722 *
1723 * Check to see if the username has been taken, or if it is disallowed.
1724 * Also checks if it includes the " character or the 4-bytes Unicode ones
1725 * (aka emojis) which we don't allow in usernames.
1726 * Used for registering, changing names, and posting anonymously with a username
1727 *
1728 * @param string $username The username to check
1729 * @param string $allowed_username An allowed username, default being $user->data['username']
1730 *
1731 * @return mixed Either false if validation succeeded or a string which will be
1732 * used as the error message (with the variable name appended)
1733 */
1734 function validate_username($username, $allowed_username = false, $allow_all_names = false)
1735 {
1736 global $config, $db, $user, $cache;
1737
1738 $clean_username = utf8_clean_string($username);
1739 $allowed_username = ($allowed_username === false) ? $user->data['username_clean'] : utf8_clean_string($allowed_username);
1740
1741 if ($allowed_username == $clean_username)
1742 {
1743 return false;
1744 }
1745
1746 // The very first check is for
1747 // out-of-bounds characters that are currently
1748 // not supported by utf8_bin in MySQL
1749 if (preg_match('/[\x{10000}-\x{10FFFF}]/u', $username))
1750 {
1751 return 'INVALID_EMOJIS';
1752 }
1753
1754 // ... fast checks first.
1755 if (strpos($username, '"') !== false || strpos($username, '"') !== false || empty($clean_username)
1756 || preg_match('/[\x{180E}\x{2005}-\x{200D}\x{202F}\x{205F}\x{2060}\x{FEFF}]/u', $username))
1757 {
1758 return 'INVALID_CHARS';
1759 }
1760
1761 switch ($config['allow_name_chars'])
1762 {
1763 case 'USERNAME_CHARS_ANY':
1764 $regex = '.+';
1765 break;
1766
1767 case 'USERNAME_ALPHA_ONLY':
1768 $regex = '[A-Za-z0-9]+';
1769 break;
1770
1771 case 'USERNAME_ALPHA_SPACERS':
1772 $regex = '[A-Za-z0-9-[\]_+ ]+';
1773 break;
1774
1775 case 'USERNAME_LETTER_NUM':
1776 $regex = '[\p{Lu}\p{Ll}\p{N}]+';
1777 break;
1778
1779 case 'USERNAME_LETTER_NUM_SPACERS':
1780 $regex = '[-\]_+ [\p{Lu}\p{Ll}\p{N}]+';
1781 break;
1782
1783 case 'USERNAME_ASCII':
1784 default:
1785 $regex = '[\x01-\x7F]+';
1786 break;
1787 }
1788
1789 if (!preg_match('#^' . $regex . '$#u', $username))
1790 {
1791 return 'INVALID_CHARS';
1792 }
1793
1794 $sql = 'SELECT username
1795 FROM ' . USERS_TABLE . "
1796 WHERE username_clean = '" . $db->sql_escape($clean_username) . "'";
1797 $result = $db->sql_query($sql);
1798 $row = $db->sql_fetchrow($result);
1799 $db->sql_freeresult($result);
1800
1801 if ($row)
1802 {
1803 return 'USERNAME_TAKEN';
1804 }
1805
1806 $sql = 'SELECT group_name
1807 FROM ' . GROUPS_TABLE . "
1808 WHERE LOWER(group_name) = '" . $db->sql_escape(utf8_strtolower($username)) . "'";
1809 $result = $db->sql_query($sql);
1810 $row = $db->sql_fetchrow($result);
1811 $db->sql_freeresult($result);
1812
1813 if ($row)
1814 {
1815 return 'USERNAME_TAKEN';
1816 }
1817
1818 if (!$allow_all_names)
1819 {
1820 $bad_usernames = $cache->obtain_disallowed_usernames();
1821
1822 foreach ($bad_usernames as $bad_username)
1823 {
1824 if (preg_match('#^' . $bad_username . '$#', $clean_username))
1825 {
1826 return 'USERNAME_DISALLOWED';
1827 }
1828 }
1829 }
1830
1831 return false;
1832 }
1833
1834 /**
1835 * Check to see if the password meets the complexity settings
1836 *
1837 * @return boolean|string Either false if validation succeeded or a string which will be used as the error message (with the variable name appended)
1838 */
1839 function validate_password($password)
1840 {
1841 global $config;
1842
1843 if ($password === '' || $config['pass_complex'] === 'PASS_TYPE_ANY')
1844 {
1845 // Password empty or no password complexity required.
1846 return false;
1847 }
1848
1849 $upp = '\p{Lu}';
1850 $low = '\p{Ll}';
1851 $num = '\p{N}';
1852 $sym = '[^\p{Lu}\p{Ll}\p{N}]';
1853 $chars = array();
1854
1855 switch ($config['pass_complex'])
1856 {
1857 // No break statements below ...
1858 // We require strong passwords in case pass_complex is not set or is invalid
1859 default:
1860
1861 // Require mixed case letters, numbers and symbols
1862 case 'PASS_TYPE_SYMBOL':
1863 $chars[] = $sym;
1864
1865 // Require mixed case letters and numbers
1866 case 'PASS_TYPE_ALPHA':
1867 $chars[] = $num;
1868
1869 // Require mixed case letters
1870 case 'PASS_TYPE_CASE':
1871 $chars[] = $low;
1872 $chars[] = $upp;
1873 }
1874
1875 foreach ($chars as $char)
1876 {
1877 if (!preg_match('#' . $char . '#u', $password))
1878 {
1879 return 'INVALID_CHARS';
1880 }
1881 }
1882
1883 return false;
1884 }
1885
1886 /**
1887 * Check to see if email address is a valid address and contains a MX record
1888 *
1889 * @param string $email The email to check
1890 * @param $config
1891 *
1892 * @return mixed Either false if validation succeeded or a string which will be used as the error message (with the variable name appended)
1893 */
1894 function phpbb_validate_email($email, $config = null)
1895 {
1896 if ($config === null)
1897 {
1898 global $config;
1899 }
1900
1901 $email = strtolower($email);
1902
1903 if (!preg_match('/^' . get_preg_expression('email') . '$/i', $email))
1904 {
1905 return 'EMAIL_INVALID';
1906 }
1907
1908 // Check MX record.
1909 // The idea for this is from reading the UseBB blog/announcement. :)
1910 if ($config['email_check_mx'])
1911 {
1912 list(, $domain) = explode('@', $email);
1913
1914 if (checkdnsrr($domain, 'A') === false && checkdnsrr($domain, 'MX') === false)
1915 {
1916 return 'DOMAIN_NO_MX_RECORD';
1917 }
1918 }
1919
1920 return false;
1921 }
1922
1923 /**
1924 * Check to see if email address is banned or already present in the DB
1925 *
1926 * @param string $email The email to check
1927 * @param string $allowed_email An allowed email, default being $user->data['user_email']
1928 *
1929 * @return mixed Either false if validation succeeded or a string which will be used as the error message (with the variable name appended)
1930 */
1931 function validate_user_email($email, $allowed_email = false)
1932 {
1933 global $config, $db, $user;
1934
1935 $email = strtolower($email);
1936 $allowed_email = ($allowed_email === false) ? strtolower($user->data['user_email']) : strtolower($allowed_email);
1937
1938 if ($allowed_email == $email)
1939 {
1940 return false;
1941 }
1942
1943 $validate_email = phpbb_validate_email($email, $config);
1944 if ($validate_email)
1945 {
1946 return $validate_email;
1947 }
1948
1949 $ban = $user->check_ban(false, false, $email, true);
1950 if (!empty($ban))
1951 {
1952 return !empty($ban['ban_give_reason']) ? $ban['ban_give_reason'] : 'EMAIL_BANNED';
1953 }
1954
1955 if (!$config['allow_emailreuse'])
1956 {
1957 $sql = 'SELECT user_email
1958 FROM ' . USERS_TABLE . "
1959 WHERE user_email = '" . $db->sql_escape($email) . "'";
1960 $result = $db->sql_query($sql);
1961 $row = $db->sql_fetchrow($result);
1962 $db->sql_freeresult($result);
1963
1964 if ($row)
1965 {
1966 return 'EMAIL_TAKEN';
1967 }
1968 }
1969
1970 return false;
1971 }
1972
1973 /**
1974 * Validate jabber address
1975 * Taken from the jabber class within flyspray (see author notes)
1976 *
1977 * @author flyspray.org
1978 */
1979 function validate_jabber($jid)
1980 {
1981 if (!$jid)
1982 {
1983 return false;
1984 }
1985
1986 $separator_pos = strpos($jid, '@');
1987
1988 if ($separator_pos === false)
1989 {
1990 return 'WRONG_DATA';
1991 }
1992
1993 $username = substr($jid, 0, $separator_pos);
1994 $realm = substr($jid, $separator_pos + 1);
1995
1996 if (strlen($username) == 0 || strlen($realm) < 3)
1997 {
1998 return 'WRONG_DATA';
1999 }
2000
2001 $arr = explode('.', $realm);
2002
2003 if (count($arr) == 0)
2004 {
2005 return 'WRONG_DATA';
2006 }
2007
2008 foreach ($arr as $part)
2009 {
2010 if (substr($part, 0, 1) == '-' || substr($part, -1, 1) == '-')
2011 {
2012 return 'WRONG_DATA';
2013 }
2014
2015 if (!preg_match("@^[a-zA-Z0-9-.]+$@", $part))
2016 {
2017 return 'WRONG_DATA';
2018 }
2019 }
2020
2021 $boundary = array(array(0, 127), array(192, 223), array(224, 239), array(240, 247), array(248, 251), array(252, 253));
2022
2023 // Prohibited Characters RFC3454 + RFC3920
2024 $prohibited = array(
2025 // Table C.1.1
2026 array(0x0020, 0x0020), // SPACE
2027 // Table C.1.2
2028 array(0x00A0, 0x00A0), // NO-BREAK SPACE
2029 array(0x1680, 0x1680), // OGHAM SPACE MARK
2030 array(0x2000, 0x2001), // EN QUAD
2031 array(0x2001, 0x2001), // EM QUAD
2032 array(0x2002, 0x2002), // EN SPACE
2033 array(0x2003, 0x2003), // EM SPACE
2034 array(0x2004, 0x2004), // THREE-PER-EM SPACE
2035 array(0x2005, 0x2005), // FOUR-PER-EM SPACE
2036 array(0x2006, 0x2006), // SIX-PER-EM SPACE
2037 array(0x2007, 0x2007), // FIGURE SPACE
2038 array(0x2008, 0x2008), // PUNCTUATION SPACE
2039 array(0x2009, 0x2009), // THIN SPACE
2040 array(0x200A, 0x200A), // HAIR SPACE
2041 array(0x200B, 0x200B), // ZERO WIDTH SPACE
2042 array(0x202F, 0x202F), // NARROW NO-BREAK SPACE
2043 array(0x205F, 0x205F), // MEDIUM MATHEMATICAL SPACE
2044 array(0x3000, 0x3000), // IDEOGRAPHIC SPACE
2045 // Table C.2.1
2046 array(0x0000, 0x001F), // [CONTROL CHARACTERS]
2047 array(0x007F, 0x007F), // DELETE
2048 // Table C.2.2
2049 array(0x0080, 0x009F), // [CONTROL CHARACTERS]
2050 array(0x06DD, 0x06DD), // ARABIC END OF AYAH
2051 array(0x070F, 0x070F), // SYRIAC ABBREVIATION MARK
2052 array(0x180E, 0x180E), // MONGOLIAN VOWEL SEPARATOR
2053 array(0x200C, 0x200C), // ZERO WIDTH NON-JOINER
2054 array(0x200D, 0x200D), // ZERO WIDTH JOINER
2055 array(0x2028, 0x2028), // LINE SEPARATOR
2056 array(0x2029, 0x2029), // PARAGRAPH SEPARATOR
2057 array(0x2060, 0x2060), // WORD JOINER
2058 array(0x2061, 0x2061), // FUNCTION APPLICATION
2059 array(0x2062, 0x2062), // INVISIBLE TIMES
2060 array(0x2063, 0x2063), // INVISIBLE SEPARATOR
2061 array(0x206A, 0x206F), // [CONTROL CHARACTERS]
2062 array(0xFEFF, 0xFEFF), // ZERO WIDTH NO-BREAK SPACE
2063 array(0xFFF9, 0xFFFC), // [CONTROL CHARACTERS]
2064 array(0x1D173, 0x1D17A), // [MUSICAL CONTROL CHARACTERS]
2065 // Table C.3
2066 array(0xE000, 0xF8FF), // [PRIVATE USE, PLANE 0]
2067 array(0xF0000, 0xFFFFD), // [PRIVATE USE, PLANE 15]
2068 array(0x100000, 0x10FFFD), // [PRIVATE USE, PLANE 16]
2069 // Table C.4
2070 array(0xFDD0, 0xFDEF), // [NONCHARACTER CODE POINTS]
2071 array(0xFFFE, 0xFFFF), // [NONCHARACTER CODE POINTS]
2072 array(0x1FFFE, 0x1FFFF), // [NONCHARACTER CODE POINTS]
2073 array(0x2FFFE, 0x2FFFF), // [NONCHARACTER CODE POINTS]
2074 array(0x3FFFE, 0x3FFFF), // [NONCHARACTER CODE POINTS]
2075 array(0x4FFFE, 0x4FFFF), // [NONCHARACTER CODE POINTS]
2076 array(0x5FFFE, 0x5FFFF), // [NONCHARACTER CODE POINTS]
2077 array(0x6FFFE, 0x6FFFF), // [NONCHARACTER CODE POINTS]
2078 array(0x7FFFE, 0x7FFFF), // [NONCHARACTER CODE POINTS]
2079 array(0x8FFFE, 0x8FFFF), // [NONCHARACTER CODE POINTS]
2080 array(0x9FFFE, 0x9FFFF), // [NONCHARACTER CODE POINTS]
2081 array(0xAFFFE, 0xAFFFF), // [NONCHARACTER CODE POINTS]
2082 array(0xBFFFE, 0xBFFFF), // [NONCHARACTER CODE POINTS]
2083 array(0xCFFFE, 0xCFFFF), // [NONCHARACTER CODE POINTS]
2084 array(0xDFFFE, 0xDFFFF), // [NONCHARACTER CODE POINTS]
2085 array(0xEFFFE, 0xEFFFF), // [NONCHARACTER CODE POINTS]
2086 array(0xFFFFE, 0xFFFFF), // [NONCHARACTER CODE POINTS]
2087 array(0x10FFFE, 0x10FFFF), // [NONCHARACTER CODE POINTS]
2088 // Table C.5
2089 array(0xD800, 0xDFFF), // [SURROGATE CODES]
2090 // Table C.6
2091 array(0xFFF9, 0xFFF9), // INTERLINEAR ANNOTATION ANCHOR
2092 array(0xFFFA, 0xFFFA), // INTERLINEAR ANNOTATION SEPARATOR
2093 array(0xFFFB, 0xFFFB), // INTERLINEAR ANNOTATION TERMINATOR
2094 array(0xFFFC, 0xFFFC), // OBJECT REPLACEMENT CHARACTER
2095 array(0xFFFD, 0xFFFD), // REPLACEMENT CHARACTER
2096 // Table C.7
2097 array(0x2FF0, 0x2FFB), // [IDEOGRAPHIC DESCRIPTION CHARACTERS]
2098 // Table C.8
2099 array(0x0340, 0x0340), // COMBINING GRAVE TONE MARK
2100 array(0x0341, 0x0341), // COMBINING ACUTE TONE MARK
2101 array(0x200E, 0x200E), // LEFT-TO-RIGHT MARK
2102 array(0x200F, 0x200F), // RIGHT-TO-LEFT MARK
2103 array(0x202A, 0x202A), // LEFT-TO-RIGHT EMBEDDING
2104 array(0x202B, 0x202B), // RIGHT-TO-LEFT EMBEDDING
2105 array(0x202C, 0x202C), // POP DIRECTIONAL FORMATTING
2106 array(0x202D, 0x202D), // LEFT-TO-RIGHT OVERRIDE
2107 array(0x202E, 0x202E), // RIGHT-TO-LEFT OVERRIDE
2108 array(0x206A, 0x206A), // INHIBIT SYMMETRIC SWAPPING
2109 array(0x206B, 0x206B), // ACTIVATE SYMMETRIC SWAPPING
2110 array(0x206C, 0x206C), // INHIBIT ARABIC FORM SHAPING
2111 array(0x206D, 0x206D), // ACTIVATE ARABIC FORM SHAPING
2112 array(0x206E, 0x206E), // NATIONAL DIGIT SHAPES
2113 array(0x206F, 0x206F), // NOMINAL DIGIT SHAPES
2114 // Table C.9
2115 array(0xE0001, 0xE0001), // LANGUAGE TAG
2116 array(0xE0020, 0xE007F), // [TAGGING CHARACTERS]
2117 // RFC3920
2118 array(0x22, 0x22), // "
2119 array(0x26, 0x26), // &
2120 array(0x27, 0x27), // '
2121 array(0x2F, 0x2F), // /
2122 array(0x3A, 0x3A), // :
2123 array(0x3C, 0x3C), // <
2124 array(0x3E, 0x3E), // >
2125 array(0x40, 0x40) // @
2126 );
2127
2128 $pos = 0;
2129 $result = true;
2130
2131 while ($pos < strlen($username))
2132 {
2133 $len = $uni = 0;
2134 for ($i = 0; $i <= 5; $i++)
2135 {
2136 if (ord($username[$pos]) >= $boundary[$i][0] && ord($username[$pos]) <= $boundary[$i][1])
2137 {
2138 $len = $i + 1;
2139 $uni = (ord($username[$pos]) - $boundary[$i][0]) * pow(2, $i * 6);
2140
2141 for ($k = 1; $k < $len; $k++)
2142 {
2143 $uni += (ord($username[$pos + $k]) - 128) * pow(2, ($i - $k) * 6);
2144 }
2145
2146 break;
2147 }
2148 }
2149
2150 if ($len == 0)
2151 {
2152 return 'WRONG_DATA';
2153 }
2154
2155 foreach ($prohibited as $pval)
2156 {
2157 if ($uni >= $pval[0] && $uni <= $pval[1])
2158 {
2159 $result = false;
2160 break 2;
2161 }
2162 }
2163
2164 $pos = $pos + $len;
2165 }
2166
2167 if (!$result)
2168 {
2169 return 'WRONG_DATA';
2170 }
2171
2172 return false;
2173 }
2174
2175 /**
2176 * Validate hex colour value
2177 *
2178 * @param string $colour The hex colour value
2179 * @param bool $optional Whether the colour value is optional. True if an empty
2180 * string will be accepted as correct input, false if not.
2181 * @return bool|string Error message if colour value is incorrect, false if it
2182 * fits the hex colour code
2183 */
2184 function phpbb_validate_hex_colour($colour, $optional = false)
2185 {
2186 if ($colour === '')
2187 {
2188 return (($optional) ? false : 'WRONG_DATA');
2189 }
2190
2191 if (!preg_match('/^([0-9a-fA-F]{6}|[0-9a-fA-F]{3})$/', $colour))
2192 {
2193 return 'WRONG_DATA';
2194 }
2195
2196 return false;
2197 }
2198
2199 /**
2200 * Verifies whether a style ID corresponds to an active style.
2201 *
2202 * @param int $style_id The style_id of a style which should be checked if activated or not.
2203 * @return boolean
2204 */
2205 function phpbb_style_is_active($style_id)
2206 {
2207 global $db;
2208
2209 $sql = 'SELECT style_active
2210 FROM ' . STYLES_TABLE . '
2211 WHERE style_id = '. (int) $style_id;
2212 $result = $db->sql_query($sql);
2213
2214 $style_is_active = (bool) $db->sql_fetchfield('style_active');
2215 $db->sql_freeresult($result);
2216
2217 return $style_is_active;
2218 }
2219
2220 /**
2221 * Remove avatar
2222 */
2223 function avatar_delete($mode, $row, $clean_db = false)
2224 {
2225 global $phpbb_root_path, $config;
2226
2227 // Check if the users avatar is actually *not* a group avatar
2228 if ($mode == 'user')
2229 {
2230 if (strpos($row['user_avatar'], 'g') === 0 || (((int) $row['user_avatar'] !== 0) && ((int) $row['user_avatar'] !== (int) $row['user_id'])))
2231 {
2232 return false;
2233 }
2234 }
2235
2236 if ($clean_db)
2237 {
2238 avatar_remove_db($row[$mode . '_avatar']);
2239 }
2240 $filename = get_avatar_filename($row[$mode . '_avatar']);
2241
2242 if (file_exists($phpbb_root_path . $config['avatar_path'] . '/' . $filename))
2243 {
2244 @unlink($phpbb_root_path . $config['avatar_path'] . '/' . $filename);
2245 return true;
2246 }
2247
2248 return false;
2249 }
2250
2251 /**
2252 * Generates avatar filename from the database entry
2253 */
2254 function get_avatar_filename($avatar_entry)
2255 {
2256 global $config;
2257
2258 if ($avatar_entry[0] === 'g')
2259 {
2260 $avatar_group = true;
2261 $avatar_entry = substr($avatar_entry, 1);
2262 }
2263 else
2264 {
2265 $avatar_group = false;
2266 }
2267 $ext = substr(strrchr($avatar_entry, '.'), 1);
2268 $avatar_entry = intval($avatar_entry);
2269 return $config['avatar_salt'] . '_' . (($avatar_group) ? 'g' : '') . $avatar_entry . '.' . $ext;
2270 }
2271
2272 /**
2273 * Returns an explanation string with maximum avatar settings
2274 *
2275 * @return string
2276 */
2277 function phpbb_avatar_explanation_string()
2278 {
2279 global $config, $user;
2280
2281 return $user->lang(($config['avatar_filesize'] == 0) ? 'AVATAR_EXPLAIN_NO_FILESIZE' : 'AVATAR_EXPLAIN',
2282 $user->lang('PIXELS', (int) $config['avatar_max_width']),
2283 $user->lang('PIXELS', (int) $config['avatar_max_height']),
2284 round($config['avatar_filesize'] / 1024));
2285 }
2286
2287 //
2288 // Usergroup functions
2289 //
2290
2291 /**
2292 * Add or edit a group. If we're editing a group we only update user
2293 * parameters such as rank, etc. if they are changed
2294 */
2295 function group_create(&$group_id, $type, $name, $desc, $group_attributes, $allow_desc_bbcode = false, $allow_desc_urls = false, $allow_desc_smilies = false)
2296 {
2297 global $db, $user, $phpbb_container, $phpbb_log;
2298
2299 /** @var \phpbb\group\helper $group_helper */
2300 $group_helper = $phpbb_container->get('group_helper');
2301
2302 $error = array();
2303
2304 // Attributes which also affect the users table
2305 $user_attribute_ary = array('group_colour', 'group_rank', 'group_avatar', 'group_avatar_type', 'group_avatar_width', 'group_avatar_height');
2306
2307 // Check data. Limit group name length.
2308 if (!utf8_strlen($name) || utf8_strlen($name) > 60)
2309 {
2310 $error[] = (!utf8_strlen($name)) ? $user->lang['GROUP_ERR_USERNAME'] : $user->lang['GROUP_ERR_USER_LONG'];
2311 }
2312
2313 $err = group_validate_groupname($group_id, $name);
2314 if (!empty($err))
2315 {
2316 $error[] = $user->lang[$err];
2317 }
2318
2319 if (!in_array($type, array(GROUP_OPEN, GROUP_CLOSED, GROUP_HIDDEN, GROUP_SPECIAL, GROUP_FREE)))
2320 {
2321 $error[] = $user->lang['GROUP_ERR_TYPE'];
2322 }
2323
2324 $group_teampage = !empty($group_attributes['group_teampage']);
2325 unset($group_attributes['group_teampage']);
2326
2327 if (!count($error))
2328 {
2329 $current_legend = \phpbb\groupposition\legend::GROUP_DISABLED;
2330 $current_teampage = \phpbb\groupposition\teampage::GROUP_DISABLED;
2331
2332 /* @var $legend \phpbb\groupposition\legend */
2333 $legend = $phpbb_container->get('groupposition.legend');
2334
2335 /* @var $teampage \phpbb\groupposition\teampage */
2336 $teampage = $phpbb_container->get('groupposition.teampage');
2337
2338 if ($group_id)
2339 {
2340 try
2341 {
2342 $current_legend = $legend->get_group_value($group_id);
2343 $current_teampage = $teampage->get_group_value($group_id);
2344 }
2345 catch (\phpbb\groupposition\exception $exception)
2346 {
2347 trigger_error($user->lang($exception->getMessage()));
2348 }
2349 }
2350
2351 if (!empty($group_attributes['group_legend']))
2352 {
2353 if (($group_id && ($current_legend == \phpbb\groupposition\legend::GROUP_DISABLED)) || !$group_id)
2354 {
2355 // Old group currently not in the legend or new group, add at the end.
2356 $group_attributes['group_legend'] = 1 + $legend->get_group_count();
2357 }
2358 else
2359 {
2360 // Group stayes in the legend
2361 $group_attributes['group_legend'] = $current_legend;
2362 }
2363 }
2364 else if ($group_id && ($current_legend != \phpbb\groupposition\legend::GROUP_DISABLED))
2365 {
2366 // Group is removed from the legend
2367 try
2368 {
2369 $legend->delete_group($group_id, true);
2370 }
2371 catch (\phpbb\groupposition\exception $exception)
2372 {
2373 trigger_error($user->lang($exception->getMessage()));
2374 }
2375 $group_attributes['group_legend'] = \phpbb\groupposition\legend::GROUP_DISABLED;
2376 }
2377 else
2378 {
2379 $group_attributes['group_legend'] = \phpbb\groupposition\legend::GROUP_DISABLED;
2380 }
2381
2382 // Unset the objects, we don't need them anymore.
2383 unset($legend);
2384
2385 $user_ary = array();
2386 $sql_ary = array(
2387 'group_name' => (string) $name,
2388 'group_desc' => (string) $desc,
2389 'group_desc_uid' => '',
2390 'group_desc_bitfield' => '',
2391 'group_type' => (int) $type,
2392 );
2393
2394 // Parse description
2395 if ($desc)
2396 {
2397 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);
2398 }
2399
2400 if (count($group_attributes))
2401 {
2402 // Merge them with $sql_ary to properly update the group
2403 $sql_ary = array_merge($sql_ary, $group_attributes);
2404 }
2405
2406 // Setting the log message before we set the group id (if group gets added)
2407 $log = ($group_id) ? 'LOG_GROUP_UPDATED' : 'LOG_GROUP_CREATED';
2408
2409 if ($group_id)
2410 {
2411 $sql = 'SELECT user_id
2412 FROM ' . USERS_TABLE . '
2413 WHERE group_id = ' . $group_id;
2414 $result = $db->sql_query($sql);
2415
2416 while ($row = $db->sql_fetchrow($result))
2417 {
2418 $user_ary[] = $row['user_id'];
2419 }
2420 $db->sql_freeresult($result);
2421
2422 if (isset($sql_ary['group_avatar']))
2423 {
2424 remove_default_avatar($group_id, $user_ary);
2425 }
2426
2427 if (isset($sql_ary['group_rank']))
2428 {
2429 remove_default_rank($group_id, $user_ary);
2430 }
2431
2432 $sql = 'UPDATE ' . GROUPS_TABLE . '
2433 SET ' . $db->sql_build_array('UPDATE', $sql_ary) . "
2434 WHERE group_id = $group_id";
2435 $db->sql_query($sql);
2436
2437 // Since we may update the name too, we need to do this on other tables too...
2438 $sql = 'UPDATE ' . MODERATOR_CACHE_TABLE . "
2439 SET group_name = '" . $db->sql_escape($sql_ary['group_name']) . "'
2440 WHERE group_id = $group_id";
2441 $db->sql_query($sql);
2442
2443 // One special case is the group skip auth setting. If this was changed we need to purge permissions for this group
2444 if (isset($group_attributes['group_skip_auth']))
2445 {
2446 // Get users within this group...
2447 $sql = 'SELECT user_id
2448 FROM ' . USER_GROUP_TABLE . '
2449 WHERE group_id = ' . $group_id . '
2450 AND user_pending = 0';
2451 $result = $db->sql_query($sql);
2452
2453 $user_id_ary = array();
2454 while ($row = $db->sql_fetchrow($result))
2455 {
2456 $user_id_ary[] = $row['user_id'];
2457 }
2458 $db->sql_freeresult($result);
2459
2460 if (!empty($user_id_ary))
2461 {
2462 global $auth;
2463
2464 // Clear permissions cache of relevant users
2465 $auth->acl_clear_prefetch($user_id_ary);
2466 }
2467 }
2468 }
2469 else
2470 {
2471 $sql = 'INSERT INTO ' . GROUPS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary);
2472 $db->sql_query($sql);
2473 }
2474
2475 // Remove the group from the teampage, only if unselected and we are editing a group,
2476 // which is currently displayed.
2477 if (!$group_teampage && $group_id && $current_teampage != \phpbb\groupposition\teampage::GROUP_DISABLED)
2478 {
2479 try
2480 {
2481 $teampage->delete_group($group_id);
2482 }
2483 catch (\phpbb\groupposition\exception $exception)
2484 {
2485 trigger_error($user->lang($exception->getMessage()));
2486 }
2487 }
2488
2489 if (!$group_id)
2490 {
2491 $group_id = $db->sql_nextid();
2492
2493 if (isset($sql_ary['group_avatar_type']) && $sql_ary['group_avatar_type'] == 'avatar.driver.upload')
2494 {
2495 group_correct_avatar($group_id, $sql_ary['group_avatar']);
2496 }
2497 }
2498
2499 try
2500 {
2501 if ($group_teampage && $current_teampage == \phpbb\groupposition\teampage::GROUP_DISABLED)
2502 {
2503 $teampage->add_group($group_id);
2504 }
2505
2506 if ($group_teampage)
2507 {
2508 if ($current_teampage == \phpbb\groupposition\teampage::GROUP_DISABLED)
2509 {
2510 $teampage->add_group($group_id);
2511 }
2512 }
2513 else if ($group_id && ($current_teampage != \phpbb\groupposition\teampage::GROUP_DISABLED))
2514 {
2515 $teampage->delete_group($group_id);
2516 }
2517 }
2518 catch (\phpbb\groupposition\exception $exception)
2519 {
2520 trigger_error($user->lang($exception->getMessage()));
2521 }
2522 unset($teampage);
2523
2524 // Set user attributes
2525 $sql_ary = array();
2526 if (count($group_attributes))
2527 {
2528 // Go through the user attributes array, check if a group attribute matches it and then set it. ;)
2529 foreach ($user_attribute_ary as $attribute)
2530 {
2531 if (!isset($group_attributes[$attribute]))
2532 {
2533 continue;
2534 }
2535
2536 // If we are about to set an avatar, we will not overwrite user avatars if no group avatar is set...
2537 if (strpos($attribute, 'group_avatar') === 0 && !$group_attributes[$attribute])
2538 {
2539 continue;
2540 }
2541
2542 $sql_ary[$attribute] = $group_attributes[$attribute];
2543 }
2544 }
2545
2546 if (count($sql_ary) && count($user_ary))
2547 {
2548 group_set_user_default($group_id, $user_ary, $sql_ary);
2549 }
2550
2551 $name = $group_helper->get_name($name);
2552 $phpbb_log->add('admin', $user->data['user_id'], $user->ip, $log, false, array($name));
2553
2554 group_update_listings($group_id);
2555 }
2556
2557 return (count($error)) ? $error : false;
2558 }
2559
2560
2561 /**
2562 * Changes a group avatar's filename to conform to the naming scheme
2563 */
2564 function group_correct_avatar($group_id, $old_entry)
2565 {
2566 global $config, $db, $phpbb_root_path;
2567
2568 $group_id = (int) $group_id;
2569 $ext = substr(strrchr($old_entry, '.'), 1);
2570 $old_filename = get_avatar_filename($old_entry);
2571 $new_filename = $config['avatar_salt'] . "_g$group_id.$ext";
2572 $new_entry = 'g' . $group_id . '_' . substr(time(), -5) . ".$ext";
2573
2574 $avatar_path = $phpbb_root_path . $config['avatar_path'];
2575 if (@rename($avatar_path . '/'. $old_filename, $avatar_path . '/' . $new_filename))
2576 {
2577 $sql = 'UPDATE ' . GROUPS_TABLE . '
2578 SET group_avatar = \'' . $db->sql_escape($new_entry) . "'
2579 WHERE group_id = $group_id";
2580 $db->sql_query($sql);
2581 }
2582 }
2583
2584
2585 /**
2586 * Remove avatar also for users not having the group as default
2587 */
2588 function avatar_remove_db($avatar_name)
2589 {
2590 global $db;
2591
2592 $sql = 'UPDATE ' . USERS_TABLE . "
2593 SET user_avatar = '',
2594 user_avatar_type = ''
2595 WHERE user_avatar = '" . $db->sql_escape($avatar_name) . '\'';
2596 $db->sql_query($sql);
2597 }
2598
2599
2600 /**
2601 * Group Delete
2602 */
2603 function group_delete($group_id, $group_name = false)
2604 {
2605 global $db, $cache, $auth, $user, $phpbb_root_path, $phpEx, $phpbb_dispatcher, $phpbb_container, $phpbb_log;
2606
2607 if (!$group_name)
2608 {
2609 $group_name = get_group_name($group_id);
2610 }
2611
2612 $start = 0;
2613
2614 do
2615 {
2616 $user_id_ary = $username_ary = array();
2617
2618 // Batch query for group members, call group_user_del
2619 $sql = 'SELECT u.user_id, u.username
2620 FROM ' . USER_GROUP_TABLE . ' ug, ' . USERS_TABLE . " u
2621 WHERE ug.group_id = $group_id
2622 AND u.user_id = ug.user_id";
2623 $result = $db->sql_query_limit($sql, 200, $start);
2624
2625 if ($row = $db->sql_fetchrow($result))
2626 {
2627 do
2628 {
2629 $user_id_ary[] = $row['user_id'];
2630 $username_ary[] = $row['username'];
2631
2632 $start++;
2633 }
2634 while ($row = $db->sql_fetchrow($result));
2635
2636 group_user_del($group_id, $user_id_ary, $username_ary, $group_name);
2637 }
2638 else
2639 {
2640 $start = 0;
2641 }
2642 $db->sql_freeresult($result);
2643 }
2644 while ($start);
2645
2646 // Delete group from legend and teampage
2647 try
2648 {
2649 /* @var $legend \phpbb\groupposition\legend */
2650 $legend = $phpbb_container->get('groupposition.legend');
2651 $legend->delete_group($group_id);
2652 unset($legend);
2653 }
2654 catch (\phpbb\groupposition\exception $exception)
2655 {
2656 // The group we want to delete does not exist.
2657 // No reason to worry, we just continue the deleting process.
2658 //trigger_error($user->lang($exception->getMessage()));
2659 }
2660
2661 try
2662 {
2663 /* @var $teampage \phpbb\groupposition\teampage */
2664 $teampage = $phpbb_container->get('groupposition.teampage');
2665 $teampage->delete_group($group_id);
2666 unset($teampage);
2667 }
2668 catch (\phpbb\groupposition\exception $exception)
2669 {
2670 // The group we want to delete does not exist.
2671 // No reason to worry, we just continue the deleting process.
2672 //trigger_error($user->lang($exception->getMessage()));
2673 }
2674
2675 // Delete group
2676 $sql = 'DELETE FROM ' . GROUPS_TABLE . "
2677 WHERE group_id = $group_id";
2678 $db->sql_query($sql);
2679
2680 // Delete auth entries from the groups table
2681 $sql = 'DELETE FROM ' . ACL_GROUPS_TABLE . "
2682 WHERE group_id = $group_id";
2683 $db->sql_query($sql);
2684
2685 /**
2686 * Event after a group is deleted
2687 *
2688 * @event core.delete_group_after
2689 * @var int group_id ID of the deleted group
2690 * @var string group_name Name of the deleted group
2691 * @since 3.1.0-a1
2692 */
2693 $vars = array('group_id', 'group_name');
2694 extract($phpbb_dispatcher->trigger_event('core.delete_group_after', compact($vars)));
2695
2696 // Re-cache moderators
2697 if (!function_exists('phpbb_cache_moderators'))
2698 {
2699 include($phpbb_root_path . 'includes/functions_admin.' . $phpEx);
2700 }
2701
2702 phpbb_cache_moderators($db, $cache, $auth);
2703
2704 $phpbb_log->add('admin', $user->data['user_id'], $user->ip, 'LOG_GROUP_DELETE', false, array($group_name));
2705
2706 // Return false - no error
2707 return false;
2708 }
2709
2710 /**
2711 * Add user(s) to group
2712 *
2713 * @return mixed false if no errors occurred, else the user lang string for the relevant error, for example 'NO_USER'
2714 */
2715 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)
2716 {
2717 global $db, $auth, $user, $phpbb_container, $phpbb_log, $phpbb_dispatcher;
2718
2719 // We need both username and user_id info
2720 $result = user_get_id_name($user_id_ary, $username_ary);
2721
2722 if (empty($user_id_ary) || $result !== false)
2723 {
2724 return 'NO_USER';
2725 }
2726
2727 // Because the item that gets passed into the previous function is unset, the reference is lost and our original
2728 // array is retained - so we know there's a problem if there's a different number of ids to usernames now.
2729 if (count($user_id_ary) != count($username_ary))
2730 {
2731 return 'GROUP_USERS_INVALID';
2732 }
2733
2734 // Remove users who are already members of this group
2735 $sql = 'SELECT user_id, group_leader
2736 FROM ' . USER_GROUP_TABLE . '
2737 WHERE ' . $db->sql_in_set('user_id', $user_id_ary) . "
2738 AND group_id = $group_id";
2739 $result = $db->sql_query($sql);
2740
2741 $add_id_ary = $update_id_ary = array();
2742 while ($row = $db->sql_fetchrow($result))
2743 {
2744 $add_id_ary[] = (int) $row['user_id'];
2745
2746 if ($leader && !$row['group_leader'])
2747 {
2748 $update_id_ary[] = (int) $row['user_id'];
2749 }
2750 }
2751 $db->sql_freeresult($result);
2752
2753 // Do all the users exist in this group?
2754 $add_id_ary = array_diff($user_id_ary, $add_id_ary);
2755
2756 // If we have no users
2757 if (!count($add_id_ary) && !count($update_id_ary))
2758 {
2759 return 'GROUP_USERS_EXIST';
2760 }
2761
2762 /**
2763 * Event before users are added to a group
2764 *
2765 * @event core.group_add_user_before
2766 * @var int group_id ID of the group to which users are added
2767 * @var string group_name Name of the group
2768 * @var array user_id_ary IDs of the users to be added
2769 * @var array username_ary Names of the users to be added
2770 * @var int pending Pending setting, 1 if user(s) added are pending
2771 * @var array add_id_ary IDs of the users to be added who are not members yet
2772 * @since 3.3.15-RC1
2773 */
2774 $vars = array(
2775 'group_id',
2776 'group_name',
2777 'user_id_ary',
2778 'username_ary',
2779 'pending',
2780 'add_id_ary',
2781 );
2782 extract($phpbb_dispatcher->trigger_event('core.group_add_user_before', compact($vars)));
2783
2784 $db->sql_transaction('begin');
2785
2786 // Insert the new users
2787 if (count($add_id_ary))
2788 {
2789 $sql_ary = array();
2790
2791 foreach ($add_id_ary as $user_id)
2792 {
2793 $sql_ary[] = array(
2794 'user_id' => (int) $user_id,
2795 'group_id' => (int) $group_id,
2796 'group_leader' => (int) $leader,
2797 'user_pending' => (int) $pending,
2798 );
2799 }
2800
2801 $db->sql_multi_insert(USER_GROUP_TABLE, $sql_ary);
2802 }
2803
2804 if (count($update_id_ary))
2805 {
2806 $sql = 'UPDATE ' . USER_GROUP_TABLE . '
2807 SET group_leader = 1
2808 WHERE ' . $db->sql_in_set('user_id', $update_id_ary) . "
2809 AND group_id = $group_id";
2810 $db->sql_query($sql);
2811 }
2812
2813 if ($default)
2814 {
2815 group_user_attributes('default', $group_id, $user_id_ary, false, $group_name, $group_attributes);
2816 }
2817
2818 $db->sql_transaction('commit');
2819
2820 // Clear permissions cache of relevant users
2821 $auth->acl_clear_prefetch($user_id_ary);
2822
2823 /**
2824 * Event after users are added to a group
2825 *
2826 * @event core.group_add_user_after
2827 * @var int group_id ID of the group to which users are added
2828 * @var string group_name Name of the group
2829 * @var array user_id_ary IDs of the users which are added
2830 * @var array username_ary names of the users which are added
2831 * @var int pending Pending setting, 1 if user(s) added are pending
2832 * @since 3.1.7-RC1
2833 */
2834 $vars = array(
2835 'group_id',
2836 'group_name',
2837 'user_id_ary',
2838 'username_ary',
2839 'pending',
2840 );
2841 extract($phpbb_dispatcher->trigger_event('core.group_add_user_after', compact($vars)));
2842
2843 if (!$group_name)
2844 {
2845 $group_name = get_group_name($group_id);
2846 }
2847
2848 $log = ($leader) ? 'LOG_MODS_ADDED' : (($pending) ? 'LOG_USERS_PENDING' : 'LOG_USERS_ADDED');
2849
2850 $phpbb_log->add('admin', $user->data['user_id'], $user->ip, $log, false, array($group_name, implode(', ', $username_ary)));
2851
2852 group_update_listings($group_id);
2853
2854 if ($pending)
2855 {
2856 /* @var $phpbb_notifications \phpbb\notification\manager */
2857 $phpbb_notifications = $phpbb_container->get('notification_manager');
2858
2859 foreach ($add_id_ary as $user_id)
2860 {
2861 $phpbb_notifications->add_notifications('notification.type.group_request', array(
2862 'group_id' => $group_id,
2863 'user_id' => $user_id,
2864 'group_name' => $group_name,
2865 ));
2866 }
2867 }
2868
2869 // Return false - no error
2870 return false;
2871 }
2872
2873 /**
2874 * Remove a user/s from a given group. When we remove users we update their
2875 * default group_id. We do this by examining which "special" groups they belong
2876 * to. The selection is made based on a reasonable priority system
2877 *
2878 * @return false if no errors occurred, else the user lang string for the relevant error, for example 'NO_USER'
2879 */
2880 function group_user_del($group_id, $user_id_ary = false, $username_ary = false, $group_name = false, $log_action = true)
2881 {
2882 global $db, $auth, $config, $user, $phpbb_dispatcher, $phpbb_container, $phpbb_log;
2883
2884 if ($config['coppa_enable'])
2885 {
2886 $group_order = array('ADMINISTRATORS', 'GLOBAL_MODERATORS', 'NEWLY_REGISTERED', 'REGISTERED_COPPA', 'REGISTERED', 'BOTS', 'GUESTS');
2887 }
2888 else
2889 {
2890 $group_order = array('ADMINISTRATORS', 'GLOBAL_MODERATORS', 'NEWLY_REGISTERED', 'REGISTERED', 'BOTS', 'GUESTS');
2891 }
2892
2893 // We need both username and user_id info
2894 $result = user_get_id_name($user_id_ary, $username_ary);
2895
2896 if (empty($user_id_ary) || $result !== false)
2897 {
2898 return 'NO_USER';
2899 }
2900
2901 $sql = 'SELECT *
2902 FROM ' . GROUPS_TABLE . '
2903 WHERE ' . $db->sql_in_set('group_name', $group_order);
2904 $result = $db->sql_query($sql);
2905
2906 $group_order_id = $special_group_data = array();
2907 while ($row = $db->sql_fetchrow($result))
2908 {
2909 $group_order_id[$row['group_name']] = $row['group_id'];
2910
2911 $special_group_data[$row['group_id']] = array(
2912 'group_colour' => $row['group_colour'],
2913 'group_rank' => $row['group_rank'],
2914 );
2915
2916 // Only set the group avatar if one is defined...
2917 if ($row['group_avatar'])
2918 {
2919 $special_group_data[$row['group_id']] = array_merge($special_group_data[$row['group_id']], array(
2920 'group_avatar' => $row['group_avatar'],
2921 'group_avatar_type' => $row['group_avatar_type'],
2922 'group_avatar_width' => $row['group_avatar_width'],
2923 'group_avatar_height' => $row['group_avatar_height'])
2924 );
2925 }
2926 }
2927 $db->sql_freeresult($result);
2928
2929 // 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
2930 $sql = 'SELECT user_id, group_id
2931 FROM ' . USERS_TABLE . '
2932 WHERE ' . $db->sql_in_set('user_id', $user_id_ary);
2933 $result = $db->sql_query($sql);
2934
2935 $default_groups = array();
2936 while ($row = $db->sql_fetchrow($result))
2937 {
2938 $default_groups[$row['user_id']] = $row['group_id'];
2939 }
2940 $db->sql_freeresult($result);
2941
2942 // What special group memberships exist for these users?
2943 $sql = 'SELECT g.group_id, g.group_name, ug.user_id
2944 FROM ' . USER_GROUP_TABLE . ' ug, ' . GROUPS_TABLE . ' g
2945 WHERE ' . $db->sql_in_set('ug.user_id', $user_id_ary) . "
2946 AND g.group_id = ug.group_id
2947 AND g.group_id <> $group_id
2948 AND g.group_type = " . GROUP_SPECIAL . '
2949 ORDER BY ug.user_id, g.group_id';
2950 $result = $db->sql_query($sql);
2951
2952 $temp_ary = array();
2953 while ($row = $db->sql_fetchrow($result))
2954 {
2955 if ($default_groups[$row['user_id']] == $group_id && (!isset($temp_ary[$row['user_id']]) || $group_order_id[$row['group_name']] < $temp_ary[$row['user_id']]))
2956 {
2957 $temp_ary[$row['user_id']] = $row['group_id'];
2958 }
2959 }
2960 $db->sql_freeresult($result);
2961
2962 // sql_where_ary holds the new default groups and their users
2963 $sql_where_ary = array();
2964 foreach ($temp_ary as $uid => $gid)
2965 {
2966 $sql_where_ary[$gid][] = $uid;
2967 }
2968 unset($temp_ary);
2969
2970 foreach ($special_group_data as $gid => $default_data_ary)
2971 {
2972 if (isset($sql_where_ary[$gid]) && count($sql_where_ary[$gid]))
2973 {
2974 remove_default_rank($group_id, $sql_where_ary[$gid]);
2975 remove_default_avatar($group_id, $sql_where_ary[$gid]);
2976 group_set_user_default($gid, $sql_where_ary[$gid], $default_data_ary);
2977 }
2978 }
2979 unset($special_group_data);
2980
2981 /**
2982 * Event before users are removed from a group
2983 *
2984 * @event core.group_delete_user_before
2985 * @var int group_id ID of the group from which users are deleted
2986 * @var string group_name Name of the group
2987 * @var array user_id_ary IDs of the users which are removed
2988 * @var array username_ary names of the users which are removed
2989 * @since 3.1.0-a1
2990 */
2991 $vars = array('group_id', 'group_name', 'user_id_ary', 'username_ary');
2992 extract($phpbb_dispatcher->trigger_event('core.group_delete_user_before', compact($vars)));
2993
2994 $sql = 'DELETE FROM ' . USER_GROUP_TABLE . "
2995 WHERE group_id = $group_id
2996 AND " . $db->sql_in_set('user_id', $user_id_ary);
2997 $db->sql_query($sql);
2998
2999 // Clear permissions cache of relevant users
3000 $auth->acl_clear_prefetch($user_id_ary);
3001
3002 /**
3003 * Event after users are removed from a group
3004 *
3005 * @event core.group_delete_user_after
3006 * @var int group_id ID of the group from which users are deleted
3007 * @var string group_name Name of the group
3008 * @var array user_id_ary IDs of the users which are removed
3009 * @var array username_ary names of the users which are removed
3010 * @since 3.1.7-RC1
3011 */
3012 $vars = array('group_id', 'group_name', 'user_id_ary', 'username_ary');
3013 extract($phpbb_dispatcher->trigger_event('core.group_delete_user_after', compact($vars)));
3014
3015 if ($log_action)
3016 {
3017 if (!$group_name)
3018 {
3019 $group_name = get_group_name($group_id);
3020 }
3021
3022 $log = 'LOG_GROUP_REMOVE';
3023
3024 if ($group_name)
3025 {
3026 $phpbb_log->add('admin', $user->data['user_id'], $user->ip, $log, false, array($group_name, implode(', ', $username_ary)));
3027 }
3028 }
3029
3030 group_update_listings($group_id);
3031
3032 /* @var $phpbb_notifications \phpbb\notification\manager */
3033 $phpbb_notifications = $phpbb_container->get('notification_manager');
3034
3035 $phpbb_notifications->delete_notifications('notification.type.group_request', $user_id_ary, $group_id);
3036
3037 // Return false - no error
3038 return false;
3039 }
3040
3041
3042 /**
3043 * Removes the group avatar of the default group from the users in user_ids who have that group as default.
3044 */
3045 function remove_default_avatar($group_id, $user_ids)
3046 {
3047 global $db;
3048
3049 if (!is_array($user_ids))
3050 {
3051 $user_ids = array($user_ids);
3052 }
3053 if (empty($user_ids))
3054 {
3055 return false;
3056 }
3057
3058 $user_ids = array_map('intval', $user_ids);
3059
3060 $sql = 'SELECT *
3061 FROM ' . GROUPS_TABLE . '
3062 WHERE group_id = ' . (int) $group_id;
3063 $result = $db->sql_query($sql);
3064 if (!$row = $db->sql_fetchrow($result))
3065 {
3066 $db->sql_freeresult($result);
3067 return false;
3068 }
3069 $db->sql_freeresult($result);
3070
3071 $sql = 'UPDATE ' . USERS_TABLE . "
3072 SET user_avatar = '',
3073 user_avatar_type = '',
3074 user_avatar_width = 0,
3075 user_avatar_height = 0
3076 WHERE group_id = " . (int) $group_id . "
3077 AND user_avatar = '" . $db->sql_escape($row['group_avatar']) . "'
3078 AND " . $db->sql_in_set('user_id', $user_ids);
3079
3080 $db->sql_query($sql);
3081 }
3082
3083 /**
3084 * Removes the group rank of the default group from the users in user_ids who have that group as default.
3085 */
3086 function remove_default_rank($group_id, $user_ids)
3087 {
3088 global $db;
3089
3090 if (!is_array($user_ids))
3091 {
3092 $user_ids = array($user_ids);
3093 }
3094 if (empty($user_ids))
3095 {
3096 return false;
3097 }
3098
3099 $user_ids = array_map('intval', $user_ids);
3100
3101 $sql = 'SELECT *
3102 FROM ' . GROUPS_TABLE . '
3103 WHERE group_id = ' . (int) $group_id;
3104 $result = $db->sql_query($sql);
3105 if (!$row = $db->sql_fetchrow($result))
3106 {
3107 $db->sql_freeresult($result);
3108 return false;
3109 }
3110 $db->sql_freeresult($result);
3111
3112 $sql = 'UPDATE ' . USERS_TABLE . '
3113 SET user_rank = 0
3114 WHERE group_id = ' . (int) $group_id . '
3115 AND user_rank <> 0
3116 AND user_rank = ' . (int) $row['group_rank'] . '
3117 AND ' . $db->sql_in_set('user_id', $user_ids);
3118 $db->sql_query($sql);
3119 }
3120
3121 /**
3122 * This is used to promote (to leader), demote or set as default a member/s
3123 */
3124 function group_user_attributes($action, $group_id, $user_id_ary = false, $username_ary = false, $group_name = false, $group_attributes = false)
3125 {
3126 global $db, $auth, $user, $phpbb_container, $phpbb_log, $phpbb_dispatcher;
3127
3128 // We need both username and user_id info
3129 $result = user_get_id_name($user_id_ary, $username_ary);
3130
3131 if (empty($user_id_ary) || $result !== false)
3132 {
3133 return 'NO_USERS';
3134 }
3135
3136 if (!$group_name)
3137 {
3138 $group_name = get_group_name($group_id);
3139 }
3140
3141 switch ($action)
3142 {
3143 case 'demote':
3144 case 'promote':
3145
3146 $sql = 'SELECT user_id
3147 FROM ' . USER_GROUP_TABLE . "
3148 WHERE group_id = $group_id
3149 AND user_pending = 1
3150 AND " . $db->sql_in_set('user_id', $user_id_ary);
3151 $result = $db->sql_query_limit($sql, 1);
3152 $not_empty = ($db->sql_fetchrow($result));
3153 $db->sql_freeresult($result);
3154 if ($not_empty)
3155 {
3156 return 'NO_VALID_USERS';
3157 }
3158
3159 $sql = 'UPDATE ' . USER_GROUP_TABLE . '
3160 SET group_leader = ' . (($action == 'promote') ? 1 : 0) . "
3161 WHERE group_id = $group_id
3162 AND user_pending = 0
3163 AND " . $db->sql_in_set('user_id', $user_id_ary);
3164 $db->sql_query($sql);
3165
3166 $log = ($action == 'promote') ? 'LOG_GROUP_PROMOTED' : 'LOG_GROUP_DEMOTED';
3167 break;
3168
3169 case 'approve':
3170 // Make sure we only approve those which are pending ;)
3171 $sql = 'SELECT u.user_id, u.user_email, u.username, u.username_clean, u.user_notify_type, u.user_jabber, u.user_lang
3172 FROM ' . USERS_TABLE . ' u, ' . USER_GROUP_TABLE . ' ug
3173 WHERE ug.group_id = ' . $group_id . '
3174 AND ug.user_pending = 1
3175 AND ug.user_id = u.user_id
3176 AND ' . $db->sql_in_set('ug.user_id', $user_id_ary);
3177 $result = $db->sql_query($sql);
3178
3179 $user_id_ary = array();
3180 while ($row = $db->sql_fetchrow($result))
3181 {
3182 $user_id_ary[] = $row['user_id'];
3183 }
3184 $db->sql_freeresult($result);
3185
3186 if (!count($user_id_ary))
3187 {
3188 return false;
3189 }
3190
3191 $sql = 'UPDATE ' . USER_GROUP_TABLE . "
3192 SET user_pending = 0
3193 WHERE group_id = $group_id
3194 AND " . $db->sql_in_set('user_id', $user_id_ary);
3195 $db->sql_query($sql);
3196
3197 /* @var $phpbb_notifications \phpbb\notification\manager */
3198 $phpbb_notifications = $phpbb_container->get('notification_manager');
3199
3200 $phpbb_notifications->add_notifications('notification.type.group_request_approved', array(
3201 'user_ids' => $user_id_ary,
3202 'group_id' => $group_id,
3203 'group_name' => $group_name,
3204 ));
3205 $phpbb_notifications->delete_notifications('notification.type.group_request', $user_id_ary, $group_id);
3206
3207 $log = 'LOG_USERS_APPROVED';
3208 break;
3209
3210 case 'default':
3211 // We only set default group for approved members of the group
3212 $sql = 'SELECT user_id
3213 FROM ' . USER_GROUP_TABLE . "
3214 WHERE group_id = $group_id
3215 AND user_pending = 0
3216 AND " . $db->sql_in_set('user_id', $user_id_ary);
3217 $result = $db->sql_query($sql);
3218
3219 $user_id_ary = $username_ary = array();
3220 while ($row = $db->sql_fetchrow($result))
3221 {
3222 $user_id_ary[] = $row['user_id'];
3223 }
3224 $db->sql_freeresult($result);
3225
3226 $result = user_get_id_name($user_id_ary, $username_ary);
3227 if (!count($user_id_ary) || $result !== false)
3228 {
3229 return 'NO_USERS';
3230 }
3231
3232 $sql = 'SELECT user_id, group_id
3233 FROM ' . USERS_TABLE . '
3234 WHERE ' . $db->sql_in_set('user_id', $user_id_ary, false, true);
3235 $result = $db->sql_query($sql);
3236
3237 $groups = array();
3238 while ($row = $db->sql_fetchrow($result))
3239 {
3240 if (!isset($groups[$row['group_id']]))
3241 {
3242 $groups[$row['group_id']] = array();
3243 }
3244 $groups[$row['group_id']][] = $row['user_id'];
3245 }
3246 $db->sql_freeresult($result);
3247
3248 foreach ($groups as $gid => $uids)
3249 {
3250 remove_default_rank($gid, $uids);
3251 remove_default_avatar($gid, $uids);
3252 }
3253 group_set_user_default($group_id, $user_id_ary, $group_attributes);
3254 $log = 'LOG_GROUP_DEFAULTS';
3255 break;
3256 }
3257
3258 /**
3259 * Event to perform additional actions on setting user group attributes
3260 *
3261 * @event core.user_set_group_attributes
3262 * @var int group_id ID of the group
3263 * @var string group_name Name of the group
3264 * @var array user_id_ary IDs of the users to set group attributes
3265 * @var array username_ary Names of the users to set group attributes
3266 * @var array group_attributes Group attributes which were changed
3267 * @var string action Action to perform over the group members
3268 * @since 3.1.10-RC1
3269 */
3270 $vars = array(
3271 'group_id',
3272 'group_name',
3273 'user_id_ary',
3274 'username_ary',
3275 'group_attributes',
3276 'action',
3277 );
3278 extract($phpbb_dispatcher->trigger_event('core.user_set_group_attributes', compact($vars)));
3279
3280 // Clear permissions cache of relevant users
3281 $auth->acl_clear_prefetch($user_id_ary);
3282
3283 $phpbb_log->add('admin', $user->data['user_id'], $user->ip, $log, false, array($group_name, implode(', ', $username_ary)));
3284
3285 group_update_listings($group_id);
3286
3287 return false;
3288 }
3289
3290 /**
3291 * A small version of validate_username to check for a group name's existence. To be called directly.
3292 */
3293 function group_validate_groupname($group_id, $group_name)
3294 {
3295 global $db;
3296
3297 $group_name = utf8_clean_string($group_name);
3298
3299 if (!empty($group_id))
3300 {
3301 $sql = 'SELECT group_name
3302 FROM ' . GROUPS_TABLE . '
3303 WHERE group_id = ' . (int) $group_id;
3304 $result = $db->sql_query($sql);
3305 $row = $db->sql_fetchrow($result);
3306 $db->sql_freeresult($result);
3307
3308 if (!$row)
3309 {
3310 return false;
3311 }
3312
3313 $allowed_groupname = utf8_clean_string($row['group_name']);
3314
3315 if ($allowed_groupname == $group_name)
3316 {
3317 return false;
3318 }
3319 }
3320
3321 $sql = 'SELECT group_name
3322 FROM ' . GROUPS_TABLE . "
3323 WHERE LOWER(group_name) = '" . $db->sql_escape(utf8_strtolower($group_name)) . "'";
3324 $result = $db->sql_query($sql);
3325 $row = $db->sql_fetchrow($result);
3326 $db->sql_freeresult($result);
3327
3328 if ($row)
3329 {
3330 return 'GROUP_NAME_TAKEN';
3331 }
3332
3333 return false;
3334 }
3335
3336 /**
3337 * Set users default group
3338 *
3339 * @access private
3340 */
3341 function group_set_user_default($group_id, $user_id_ary, $group_attributes = false, $update_listing = false)
3342 {
3343 global $config, $phpbb_container, $db, $phpbb_dispatcher;
3344
3345 if (empty($user_id_ary))
3346 {
3347 return;
3348 }
3349
3350 $attribute_ary = array(
3351 'group_colour' => 'string',
3352 'group_rank' => 'int',
3353 'group_avatar' => 'string',
3354 'group_avatar_type' => 'string',
3355 'group_avatar_width' => 'int',
3356 'group_avatar_height' => 'int',
3357 );
3358
3359 $sql_ary = array(
3360 'group_id' => $group_id
3361 );
3362
3363 // Were group attributes passed to the function? If not we need to obtain them
3364 if ($group_attributes === false)
3365 {
3366 $sql = 'SELECT ' . implode(', ', array_keys($attribute_ary)) . '
3367 FROM ' . GROUPS_TABLE . "
3368 WHERE group_id = $group_id";
3369 $result = $db->sql_query($sql);
3370 $group_attributes = $db->sql_fetchrow($result);
3371 $db->sql_freeresult($result);
3372 }
3373
3374 foreach ($attribute_ary as $attribute => $type)
3375 {
3376 if (isset($group_attributes[$attribute]))
3377 {
3378 // 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
3379 if ((strpos($attribute, 'group_avatar') === 0 || strpos($attribute, 'group_rank') === 0) && !$group_attributes[$attribute])
3380 {
3381 continue;
3382 }
3383
3384 settype($group_attributes[$attribute], $type);
3385 $sql_ary[str_replace('group_', 'user_', $attribute)] = $group_attributes[$attribute];
3386 }
3387 }
3388
3389 $updated_sql_ary = $sql_ary;
3390
3391 // Before we update the user attributes, we will update the rank for users that don't have a custom rank
3392 if (isset($sql_ary['user_rank']))
3393 {
3394 $sql = 'UPDATE ' . USERS_TABLE . '
3395 SET ' . $db->sql_build_array('UPDATE', array('user_rank' => $sql_ary['user_rank'])) . '
3396 WHERE user_rank = 0
3397 AND ' . $db->sql_in_set('user_id', $user_id_ary);
3398 $db->sql_query($sql);
3399 unset($sql_ary['user_rank']);
3400 }
3401
3402 // Before we update the user attributes, we will update the avatar for users that don't have a custom avatar
3403 $avatar_options = array('user_avatar', 'user_avatar_type', 'user_avatar_height', 'user_avatar_width');
3404
3405 if (isset($sql_ary['user_avatar']))
3406 {
3407 $avatar_sql_ary = array();
3408 foreach ($avatar_options as $avatar_option)
3409 {
3410 if (isset($sql_ary[$avatar_option]))
3411 {
3412 $avatar_sql_ary[$avatar_option] = $sql_ary[$avatar_option];
3413 }
3414 }
3415
3416 $sql = 'UPDATE ' . USERS_TABLE . '
3417 SET ' . $db->sql_build_array('UPDATE', $avatar_sql_ary) . "
3418 WHERE user_avatar = ''
3419 AND " . $db->sql_in_set('user_id', $user_id_ary);
3420 $db->sql_query($sql);
3421 }
3422
3423 // Remove the avatar options, as we already updated them
3424 foreach ($avatar_options as $avatar_option)
3425 {
3426 unset($sql_ary[$avatar_option]);
3427 }
3428
3429 if (!empty($sql_ary))
3430 {
3431 $sql = 'UPDATE ' . USERS_TABLE . '
3432 SET ' . $db->sql_build_array('UPDATE', $sql_ary) . '
3433 WHERE ' . $db->sql_in_set('user_id', $user_id_ary);
3434 $db->sql_query($sql);
3435 }
3436
3437 if (isset($sql_ary['user_colour']))
3438 {
3439 // Update any cached colour information for these users
3440 $sql = 'UPDATE ' . FORUMS_TABLE . "
3441 SET forum_last_poster_colour = '" . $db->sql_escape($sql_ary['user_colour']) . "'
3442 WHERE " . $db->sql_in_set('forum_last_poster_id', $user_id_ary);
3443 $db->sql_query($sql);
3444
3445 $sql = 'UPDATE ' . TOPICS_TABLE . "
3446 SET topic_first_poster_colour = '" . $db->sql_escape($sql_ary['user_colour']) . "'
3447 WHERE " . $db->sql_in_set('topic_poster', $user_id_ary);
3448 $db->sql_query($sql);
3449
3450 $sql = 'UPDATE ' . TOPICS_TABLE . "
3451 SET topic_last_poster_colour = '" . $db->sql_escape($sql_ary['user_colour']) . "'
3452 WHERE " . $db->sql_in_set('topic_last_poster_id', $user_id_ary);
3453 $db->sql_query($sql);
3454
3455 if (in_array($config['newest_user_id'], $user_id_ary))
3456 {
3457 $config->set('newest_user_colour', $sql_ary['user_colour'], false);
3458 }
3459 }
3460
3461 // Make all values available for the event
3462 $sql_ary = $updated_sql_ary;
3463
3464 /**
3465 * Event when the default group is set for an array of users
3466 *
3467 * @event core.user_set_default_group
3468 * @var int group_id ID of the group
3469 * @var array user_id_ary IDs of the users
3470 * @var array group_attributes Group attributes which were changed
3471 * @var array update_listing Update the list of moderators and foes
3472 * @var array sql_ary User attributes which were changed
3473 * @since 3.1.0-a1
3474 */
3475 $vars = array('group_id', 'user_id_ary', 'group_attributes', 'update_listing', 'sql_ary');
3476 extract($phpbb_dispatcher->trigger_event('core.user_set_default_group', compact($vars)));
3477
3478 if ($update_listing)
3479 {
3480 group_update_listings($group_id);
3481 }
3482
3483 // Because some tables/caches use usercolour-specific data we need to purge this here.
3484 $phpbb_container->get('cache.driver')->destroy('sql', MODERATOR_CACHE_TABLE);
3485 }
3486
3487 /**
3488 * Get group name
3489 */
3490 function get_group_name($group_id)
3491 {
3492 global $db, $phpbb_container;
3493
3494 $sql = 'SELECT group_name, group_type
3495 FROM ' . GROUPS_TABLE . '
3496 WHERE group_id = ' . (int) $group_id;
3497 $result = $db->sql_query($sql);
3498 $row = $db->sql_fetchrow($result);
3499 $db->sql_freeresult($result);
3500
3501 if (!$row)
3502 {
3503 return '';
3504 }
3505
3506 /** @var \phpbb\group\helper $group_helper */
3507 $group_helper = $phpbb_container->get('group_helper');
3508
3509 return $group_helper->get_name($row['group_name']);
3510 }
3511
3512 /**
3513 * Obtain either the members of a specified group, the groups the specified user is subscribed to
3514 * or checking if a specified user is in a specified group. This function does not return pending memberships.
3515 *
3516 * Note: Never use this more than once... first group your users/groups
3517 */
3518 function group_memberships($group_id_ary = false, $user_id_ary = false, $return_bool = false)
3519 {
3520 global $db;
3521
3522 if (!$group_id_ary && !$user_id_ary)
3523 {
3524 return true;
3525 }
3526
3527 if ($user_id_ary)
3528 {
3529 $user_id_ary = (!is_array($user_id_ary)) ? array($user_id_ary) : $user_id_ary;
3530 }
3531
3532 if ($group_id_ary)
3533 {
3534 $group_id_ary = (!is_array($group_id_ary)) ? array($group_id_ary) : $group_id_ary;
3535 }
3536
3537 $sql = 'SELECT ug.*, u.username, u.username_clean, u.user_email
3538 FROM ' . USER_GROUP_TABLE . ' ug, ' . USERS_TABLE . ' u
3539 WHERE ug.user_id = u.user_id
3540 AND ug.user_pending = 0 AND ';
3541
3542 if ($group_id_ary)
3543 {
3544 $sql .= ' ' . $db->sql_in_set('ug.group_id', $group_id_ary);
3545 }
3546
3547 if ($user_id_ary)
3548 {
3549 $sql .= ($group_id_ary) ? ' AND ' : ' ';
3550 $sql .= $db->sql_in_set('ug.user_id', $user_id_ary);
3551 }
3552
3553 $result = ($return_bool) ? $db->sql_query_limit($sql, 1) : $db->sql_query($sql);
3554
3555 $row = $db->sql_fetchrow($result);
3556
3557 if ($return_bool)
3558 {
3559 $db->sql_freeresult($result);
3560 return ($row) ? true : false;
3561 }
3562
3563 if (!$row)
3564 {
3565 return false;
3566 }
3567
3568 $return = array();
3569
3570 do
3571 {
3572 $return[] = $row;
3573 }
3574 while ($row = $db->sql_fetchrow($result));
3575
3576 $db->sql_freeresult($result);
3577
3578 return $return;
3579 }
3580
3581 /**
3582 * Re-cache moderators and foes if group has a_ or m_ permissions
3583 */
3584 function group_update_listings($group_id)
3585 {
3586 global $db, $cache, $auth;
3587
3588 $hold_ary = $auth->acl_group_raw_data($group_id, array('a_', 'm_'));
3589
3590 if (empty($hold_ary))
3591 {
3592 return;
3593 }
3594
3595 $mod_permissions = $admin_permissions = false;
3596
3597 foreach ($hold_ary as $g_id => $forum_ary)
3598 {
3599 foreach ($forum_ary as $forum_id => $auth_ary)
3600 {
3601 foreach ($auth_ary as $auth_option => $setting)
3602 {
3603 if ($mod_permissions && $admin_permissions)
3604 {
3605 break 3;
3606 }
3607
3608 if ($setting != ACL_YES)
3609 {
3610 continue;
3611 }
3612
3613 if ($auth_option == 'm_')
3614 {
3615 $mod_permissions = true;
3616 }
3617
3618 if ($auth_option == 'a_')
3619 {
3620 $admin_permissions = true;
3621 }
3622 }
3623 }
3624 }
3625
3626 if ($mod_permissions)
3627 {
3628 if (!function_exists('phpbb_cache_moderators'))
3629 {
3630 global $phpbb_root_path, $phpEx;
3631 include($phpbb_root_path . 'includes/functions_admin.' . $phpEx);
3632 }
3633 phpbb_cache_moderators($db, $cache, $auth);
3634 }
3635
3636 if ($mod_permissions || $admin_permissions)
3637 {
3638 if (!function_exists('phpbb_update_foes'))
3639 {
3640 global $phpbb_root_path, $phpEx;
3641 include($phpbb_root_path . 'includes/functions_admin.' . $phpEx);
3642 }
3643 phpbb_update_foes($db, $auth, array($group_id));
3644 }
3645 }
3646
3647
3648
3649 /**
3650 * Funtion to make a user leave the NEWLY_REGISTERED system group.
3651 * @access public
3652 * @param int $user_id The id of the user to remove from the group
3653 * @param mixed $user_data The id of the user to remove from the group
3654 */
3655 function remove_newly_registered($user_id, $user_data = false)
3656 {
3657 global $db;
3658
3659 if ($user_data === false)
3660 {
3661 $sql = 'SELECT *
3662 FROM ' . USERS_TABLE . '
3663 WHERE user_id = ' . $user_id;
3664 $result = $db->sql_query($sql);
3665 $user_row = $db->sql_fetchrow($result);
3666 $db->sql_freeresult($result);
3667
3668 if (!$user_row)
3669 {
3670 return false;
3671 }
3672 else
3673 {
3674 $user_data = $user_row;
3675 }
3676 }
3677
3678 $sql = 'SELECT group_id
3679 FROM ' . GROUPS_TABLE . "
3680 WHERE group_name = 'NEWLY_REGISTERED'
3681 AND group_type = " . GROUP_SPECIAL;
3682 $result = $db->sql_query($sql);
3683 $group_id = (int) $db->sql_fetchfield('group_id');
3684 $db->sql_freeresult($result);
3685
3686 if (!$group_id)
3687 {
3688 return false;
3689 }
3690
3691 // We need to call group_user_del here, because this function makes sure everything is correctly changed.
3692 // Force function to not log the removal of users from newly registered users group
3693 group_user_del($group_id, $user_id, false, false, false);
3694
3695 // Set user_new to 0 to let this not be triggered again
3696 $sql = 'UPDATE ' . USERS_TABLE . '
3697 SET user_new = 0
3698 WHERE user_id = ' . $user_id;
3699 $db->sql_query($sql);
3700
3701 // The new users group was the users default group?
3702 if ($user_data['group_id'] == $group_id)
3703 {
3704 // Which group is now the users default one?
3705 $sql = 'SELECT group_id
3706 FROM ' . USERS_TABLE . '
3707 WHERE user_id = ' . $user_id;
3708 $result = $db->sql_query($sql);
3709 $user_data['group_id'] = $db->sql_fetchfield('group_id');
3710 $db->sql_freeresult($result);
3711 }
3712
3713 return $user_data['group_id'];
3714 }
3715
3716 /**
3717 * Gets user ids of currently banned registered users.
3718 *
3719 * @param array $user_ids Array of users' ids to check for banning,
3720 * leave empty to get complete list of banned ids
3721 * @param bool|int $ban_end Bool True to get users currently banned
3722 * Bool False to only get permanently banned users
3723 * Int Unix timestamp to get users banned until that time
3724 * @return array Array of banned users' ids if any, empty array otherwise
3725 */
3726 function phpbb_get_banned_user_ids($user_ids = array(), $ban_end = true)
3727 {
3728 global $db;
3729
3730 $sql_user_ids = (!empty($user_ids)) ? $db->sql_in_set('ban_userid', $user_ids) : 'ban_userid <> 0';
3731
3732 // Get banned User ID's
3733 // Ignore stale bans which were not wiped yet
3734 $banned_ids_list = array();
3735 $sql = 'SELECT ban_userid
3736 FROM ' . BANLIST_TABLE . "
3737 WHERE $sql_user_ids
3738 AND ban_exclude <> 1";
3739
3740 if ($ban_end === true)
3741 {
3742 // Banned currently
3743 $sql .= " AND (ban_end > " . time() . '
3744 OR ban_end = 0)';
3745 }
3746 else if ($ban_end === false)
3747 {
3748 // Permanently banned
3749 $sql .= " AND ban_end = 0";
3750 }
3751 else
3752 {
3753 // Banned until a specified time
3754 $sql .= " AND (ban_end > " . (int) $ban_end . '
3755 OR ban_end = 0)';
3756 }
3757
3758 $result = $db->sql_query($sql);
3759 while ($row = $db->sql_fetchrow($result))
3760 {
3761 $user_id = (int) $row['ban_userid'];
3762 $banned_ids_list[$user_id] = $user_id;
3763 }
3764 $db->sql_freeresult($result);
3765
3766 return $banned_ids_list;
3767 }
3768
3769 /**
3770 * Function for assigning a template var if the zebra module got included
3771 */
3772 function phpbb_module_zebra($mode, &$module_row)
3773 {
3774 global $template;
3775
3776 $template->assign_var('S_ZEBRA_ENABLED', true);
3777
3778 if ($mode == 'friends')
3779 {
3780 $template->assign_var('S_ZEBRA_FRIENDS_ENABLED', true);
3781 }
3782
3783 if ($mode == 'foes')
3784 {
3785 $template->assign_var('S_ZEBRA_FOES_ENABLED', true);
3786 }
3787 }
3788