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