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