Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
65.30% |
954 / 1461 |
|
20.93% |
9 / 43 |
CRAP | n/a |
0 / 0 |
|
user_get_id_name | |
81.48% |
22 / 27 |
|
0.00% |
0 / 1 |
15.24 | |||
update_last_username | |
100.00% |
12 / 12 |
|
100.00% |
1 / 1 |
2 | |||
user_update_name | |
0.00% |
0 / 28 |
|
0.00% |
0 / 1 |
20 | |||
user_add | |
81.89% |
104 / 127 |
|
0.00% |
0 / 1 |
24.87 | |||
user_delete | |
95.14% |
176 / 185 |
|
0.00% |
0 / 1 |
34 | |||
user_active_flip | |
95.45% |
42 / 44 |
|
0.00% |
0 / 1 |
18 | |||
user_ban | |
0.00% |
0 / 21 |
|
0.00% |
0 / 1 |
110 | |||
user_unban | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
user_ipwhois | |
44.44% |
12 / 27 |
|
0.00% |
0 / 1 |
22.89 | |||
validate_data | |
100.00% |
15 / 15 |
|
100.00% |
1 / 1 |
8 | |||
validate_string | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
7 | |||
validate_num | |
85.71% |
6 / 7 |
|
0.00% |
0 / 1 |
5.07 | |||
validate_date | |
50.00% |
7 / 14 |
|
0.00% |
0 / 1 |
22.50 | |||
validate_match | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
5 | |||
validate_language_iso_name | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
2 | |||
phpbb_validate_timezone | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
6 | |||
validate_username | |
100.00% |
53 / 53 |
|
100.00% |
1 / 1 |
21 | |||
validate_password | |
100.00% |
20 / 20 |
|
100.00% |
1 / 1 |
9 | |||
phpbb_validate_email | |
70.00% |
7 / 10 |
|
0.00% |
0 / 1 |
6.97 | |||
validate_user_email | |
100.00% |
21 / 21 |
|
100.00% |
1 / 1 |
8 | |||
validate_jabber | |
97.69% |
127 / 130 |
|
0.00% |
0 / 1 |
20 | |||
phpbb_validate_hex_colour | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
4 | |||
phpbb_style_is_active | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
2 | |||
avatar_delete | |
0.00% |
0 / 12 |
|
0.00% |
0 / 1 |
56 | |||
get_avatar_filename | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
12 | |||
phpbb_avatar_explanation_string | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
6 | |||
group_create | |
48.25% |
55 / 114 |
|
0.00% |
0 / 1 |
367.39 | |||
group_correct_avatar | |
0.00% |
0 / 13 |
|
0.00% |
0 / 1 |
6 | |||
avatar_remove_db | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 | |||
group_delete | |
0.00% |
0 / 40 |
|
0.00% |
0 / 1 |
42 | |||
group_user_add | |
82.26% |
51 / 62 |
|
0.00% |
0 / 1 |
19.81 | |||
group_user_del | |
0.00% |
0 / 75 |
|
0.00% |
0 / 1 |
342 | |||
remove_default_avatar | |
78.95% |
15 / 19 |
|
0.00% |
0 / 1 |
4.15 | |||
remove_default_rank | |
80.00% |
16 / 20 |
|
0.00% |
0 / 1 |
4.13 | |||
group_user_attributes | |
79.12% |
72 / 91 |
|
0.00% |
0 / 1 |
22.29 | |||
group_validate_groupname | |
47.83% |
11 / 23 |
|
0.00% |
0 / 1 |
8.55 | |||
group_set_user_default | |
95.83% |
69 / 72 |
|
0.00% |
0 / 1 |
17 | |||
get_group_name | |
90.91% |
10 / 11 |
|
0.00% |
0 / 1 |
2.00 | |||
group_memberships | |
0.00% |
0 / 27 |
|
0.00% |
0 / 1 |
210 | |||
group_update_listings | |
14.81% |
4 / 27 |
|
0.00% |
0 / 1 |
154.08 | |||
remove_newly_registered | |
0.00% |
0 / 31 |
|
0.00% |
0 / 1 |
30 | |||
phpbb_get_banned_user_ids | |
0.00% |
0 / 20 |
|
0.00% |
0 / 1 |
72 | |||
phpbb_module_zebra | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
12 |
1 | <?php |
2 | /** |
3 | * |
4 | * This file is part of the phpBB Forum Software package. |
5 | * |
6 | * @copyright (c) phpBB Limited <https://www.phpbb.com> |
7 | * @license GNU General Public License, version 2 (GPL-2.0) |
8 | * |
9 | * For full copyright and license information, please see |
10 | * the docs/CREDITS.txt file. |
11 | * |
12 | */ |
13 | |
14 | /** |
15 | * @ignore |
16 | */ |
17 | if (!defined('IN_PHPBB')) |
18 | { |
19 | exit; |
20 | } |
21 | |
22 | /** |
23 | * Obtain user_ids from usernames or vice versa. Returns false on |
24 | * success else the error string |
25 | * |
26 | * @param array &$user_id_ary The user ids to check or empty if usernames used |
27 | * @param array &$username_ary The usernames to check or empty if user ids used |
28 | * @param mixed $user_type Array of user types to check, false if not restricting by user type |
29 | * @param boolean $update_references If false, the supplied array is unset and appears unchanged from where it was called |
30 | * @return boolean|string Returns false on success, error string on failure |
31 | */ |
32 | function user_get_id_name(&$user_id_ary, &$username_ary, $user_type = false, $update_references = false) |
33 | { |
34 | global $db; |
35 | |
36 | // Are both arrays already filled? Yep, return else |
37 | // are neither array filled? |
38 | if ($user_id_ary && $username_ary) |
39 | { |
40 | return false; |
41 | } |
42 | else if (!$user_id_ary && !$username_ary) |
43 | { |
44 | return 'NO_USERS'; |
45 | } |
46 | |
47 | $which_ary = ($user_id_ary) ? 'user_id_ary' : 'username_ary'; |
48 | |
49 | if (${$which_ary} && !is_array(${$which_ary})) |
50 | { |
51 | ${$which_ary} = array(${$which_ary}); |
52 | } |
53 | |
54 | $sql_in = ($which_ary == 'user_id_ary') ? array_map('intval', ${$which_ary}) : array_map('utf8_clean_string', ${$which_ary}); |
55 | |
56 | // By unsetting the array here, the values passed in at the point user_get_id_name() was called will be retained. |
57 | // Otherwise, if we don't unset (as the array was passed by reference) the original array will be updated below. |
58 | if ($update_references === false) |
59 | { |
60 | unset(${$which_ary}); |
61 | } |
62 | |
63 | $user_id_ary = $username_ary = array(); |
64 | |
65 | // Grab the user id/username records |
66 | $sql_where = ($which_ary == 'user_id_ary') ? 'user_id' : 'username_clean'; |
67 | $sql = 'SELECT user_id, username |
68 | FROM ' . USERS_TABLE . ' |
69 | WHERE ' . $db->sql_in_set($sql_where, $sql_in); |
70 | |
71 | if ($user_type !== false && !empty($user_type)) |
72 | { |
73 | $sql .= ' AND ' . $db->sql_in_set('user_type', $user_type); |
74 | } |
75 | |
76 | $result = $db->sql_query($sql); |
77 | |
78 | if (!($row = $db->sql_fetchrow($result))) |
79 | { |
80 | $db->sql_freeresult($result); |
81 | return 'NO_USERS'; |
82 | } |
83 | |
84 | do |
85 | { |
86 | $username_ary[$row['user_id']] = $row['username']; |
87 | $user_id_ary[] = $row['user_id']; |
88 | } |
89 | while ($row = $db->sql_fetchrow($result)); |
90 | $db->sql_freeresult($result); |
91 | |
92 | return false; |
93 | } |
94 | |
95 | /** |
96 | * Get latest registered username and update database to reflect it |
97 | */ |
98 | function update_last_username() |
99 | { |
100 | global $config, $db; |
101 | |
102 | // Get latest username |
103 | $sql = 'SELECT user_id, username, user_colour |
104 | FROM ' . USERS_TABLE . ' |
105 | WHERE user_type IN (' . USER_NORMAL . ', ' . USER_FOUNDER . ') |
106 | ORDER BY user_id DESC'; |
107 | $result = $db->sql_query_limit($sql, 1); |
108 | $row = $db->sql_fetchrow($result); |
109 | $db->sql_freeresult($result); |
110 | |
111 | if ($row) |
112 | { |
113 | $config->set('newest_user_id', $row['user_id'], false); |
114 | $config->set('newest_username', $row['username'], false); |
115 | $config->set('newest_user_colour', $row['user_colour'], false); |
116 | } |
117 | } |
118 | |
119 | /** |
120 | * Updates a username across all relevant tables/fields |
121 | * |
122 | * @param string $old_name the old/current username |
123 | * @param string $new_name the new username |
124 | */ |
125 | function user_update_name($old_name, $new_name) |
126 | { |
127 | global $config, $db, $cache, $phpbb_dispatcher; |
128 | |
129 | $update_ary = array( |
130 | FORUMS_TABLE => array( |
131 | 'forum_last_poster_id' => 'forum_last_poster_name', |
132 | ), |
133 | MODERATOR_CACHE_TABLE => array( |
134 | 'user_id' => 'username', |
135 | ), |
136 | POSTS_TABLE => array( |
137 | 'poster_id' => 'post_username', |
138 | ), |
139 | TOPICS_TABLE => array( |
140 | 'topic_poster' => 'topic_first_poster_name', |
141 | 'topic_last_poster_id' => 'topic_last_poster_name', |
142 | ), |
143 | ); |
144 | |
145 | foreach ($update_ary as $table => $field_ary) |
146 | { |
147 | foreach ($field_ary as $id_field => $name_field) |
148 | { |
149 | $sql = "UPDATE $table |
150 | SET $name_field = '" . $db->sql_escape($new_name) . "' |
151 | WHERE $name_field = '" . $db->sql_escape($old_name) . "' |
152 | AND $id_field <> " . ANONYMOUS; |
153 | $db->sql_query($sql); |
154 | } |
155 | } |
156 | |
157 | if ($config['newest_username'] == $old_name) |
158 | { |
159 | $config->set('newest_username', $new_name, false); |
160 | } |
161 | |
162 | /** |
163 | * Update a username when it is changed |
164 | * |
165 | * @event core.update_username |
166 | * @var string old_name The old username that is replaced |
167 | * @var string new_name The new username |
168 | * @since 3.1.0-a1 |
169 | */ |
170 | $vars = array('old_name', 'new_name'); |
171 | extract($phpbb_dispatcher->trigger_event('core.update_username', compact($vars))); |
172 | |
173 | // Because some tables/caches use username-specific data we need to purge this here. |
174 | $cache->destroy('sql', MODERATOR_CACHE_TABLE); |
175 | } |
176 | |
177 | /** |
178 | * Adds an user |
179 | * |
180 | * @param mixed $user_row An array containing the following keys (and the appropriate values): username, group_id (the group to place the user in), user_email and the user_type(usually 0). Additional entries not overridden by defaults will be forwarded. |
181 | * @param array $cp_data custom profile fields, see custom_profile::build_insert_sql_array |
182 | * @param array $notifications_data The notifications settings for the new user |
183 | * @return int The new user's ID. |
184 | */ |
185 | function user_add($user_row, $cp_data = false, $notifications_data = null) |
186 | { |
187 | global $db, $config; |
188 | global $phpbb_dispatcher, $phpbb_container; |
189 | |
190 | if (empty($user_row['username']) || !isset($user_row['group_id']) || !isset($user_row['user_email']) || !isset($user_row['user_type'])) |
191 | { |
192 | return false; |
193 | } |
194 | |
195 | $username_clean = utf8_clean_string($user_row['username']); |
196 | |
197 | if (empty($username_clean)) |
198 | { |
199 | return false; |
200 | } |
201 | |
202 | $sql_ary = array( |
203 | 'username' => $user_row['username'], |
204 | 'username_clean' => $username_clean, |
205 | 'user_password' => (isset($user_row['user_password'])) ? $user_row['user_password'] : '', |
206 | 'user_email' => strtolower($user_row['user_email']), |
207 | 'group_id' => $user_row['group_id'], |
208 | 'user_type' => $user_row['user_type'], |
209 | ); |
210 | |
211 | // These are the additional vars able to be specified |
212 | $additional_vars = array( |
213 | 'user_permissions' => '', |
214 | 'user_timezone' => $config['board_timezone'], |
215 | 'user_dateformat' => $config['default_dateformat'], |
216 | 'user_lang' => $config['default_lang'], |
217 | 'user_style' => (int) $config['default_style'], |
218 | 'user_actkey' => '', |
219 | 'user_ip' => '', |
220 | 'user_regdate' => time(), |
221 | 'user_passchg' => time(), |
222 | 'user_options' => 230271, |
223 | // We do not set the new flag here - registration scripts need to specify it |
224 | 'user_new' => 0, |
225 | |
226 | 'user_inactive_reason' => 0, |
227 | 'user_inactive_time' => 0, |
228 | 'user_lastmark' => time(), |
229 | 'user_lastvisit' => 0, |
230 | 'user_lastpost_time' => 0, |
231 | 'user_lastpage' => '', |
232 | 'user_posts' => 0, |
233 | 'user_colour' => '', |
234 | 'user_avatar' => '', |
235 | 'user_avatar_type' => '', |
236 | 'user_avatar_width' => 0, |
237 | 'user_avatar_height' => 0, |
238 | 'user_new_privmsg' => 0, |
239 | 'user_unread_privmsg' => 0, |
240 | 'user_last_privmsg' => 0, |
241 | 'user_message_rules' => 0, |
242 | 'user_full_folder' => PRIVMSGS_NO_BOX, |
243 | 'user_emailtime' => 0, |
244 | |
245 | 'user_notify' => 0, |
246 | 'user_notify_pm' => 1, |
247 | 'user_notify_type' => NOTIFY_EMAIL, |
248 | 'user_allow_pm' => 1, |
249 | 'user_allow_viewonline' => 1, |
250 | 'user_allow_viewemail' => 1, |
251 | 'user_allow_massemail' => 1, |
252 | |
253 | 'user_sig' => '', |
254 | 'user_sig_bbcode_uid' => '', |
255 | 'user_sig_bbcode_bitfield' => '', |
256 | |
257 | 'user_form_salt' => unique_id(), |
258 | ); |
259 | |
260 | // Now fill the sql array with not required variables |
261 | foreach ($additional_vars as $key => $default_value) |
262 | { |
263 | $sql_ary[$key] = (isset($user_row[$key])) ? $user_row[$key] : $default_value; |
264 | } |
265 | |
266 | // Any additional variables in $user_row not covered above? |
267 | $remaining_vars = array_diff(array_keys($user_row), array_keys($sql_ary)); |
268 | |
269 | // Now fill our sql array with the remaining vars |
270 | if (count($remaining_vars)) |
271 | { |
272 | foreach ($remaining_vars as $key) |
273 | { |
274 | $sql_ary[$key] = $user_row[$key]; |
275 | } |
276 | } |
277 | |
278 | /** |
279 | * Use this event to modify the values to be inserted when a user is added |
280 | * |
281 | * @event core.user_add_modify_data |
282 | * @var array user_row Array of user details submitted to user_add |
283 | * @var array cp_data Array of Custom profile fields submitted to user_add |
284 | * @var array sql_ary Array of data to be inserted when a user is added |
285 | * @var array notifications_data Array of notification data to be inserted when a user is added |
286 | * @since 3.1.0-a1 |
287 | * @changed 3.1.0-b5 Added user_row and cp_data |
288 | * @changed 3.1.11-RC1 Added notifications_data |
289 | */ |
290 | $vars = array('user_row', 'cp_data', 'sql_ary', 'notifications_data'); |
291 | extract($phpbb_dispatcher->trigger_event('core.user_add_modify_data', compact($vars))); |
292 | |
293 | $sql = 'INSERT INTO ' . USERS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary); |
294 | $db->sql_query($sql); |
295 | |
296 | $user_id = $db->sql_nextid(); |
297 | |
298 | // Insert Custom Profile Fields |
299 | if ($cp_data !== false && count($cp_data)) |
300 | { |
301 | $cp_data['user_id'] = (int) $user_id; |
302 | |
303 | /* @var $cp \phpbb\profilefields\manager */ |
304 | $cp = $phpbb_container->get('profilefields.manager'); |
305 | $sql = 'INSERT INTO ' . PROFILE_FIELDS_DATA_TABLE . ' ' . |
306 | $db->sql_build_array('INSERT', $cp->build_insert_sql_array($cp_data)); |
307 | $db->sql_query($sql); |
308 | } |
309 | |
310 | // Place into appropriate group... |
311 | $sql = 'INSERT INTO ' . USER_GROUP_TABLE . ' ' . $db->sql_build_array('INSERT', array( |
312 | 'user_id' => (int) $user_id, |
313 | 'group_id' => (int) $user_row['group_id'], |
314 | 'user_pending' => 0) |
315 | ); |
316 | $db->sql_query($sql); |
317 | |
318 | // Now make it the users default group... |
319 | group_set_user_default($user_row['group_id'], array($user_id), false); |
320 | |
321 | // Add to newly registered users group if user_new is 1 |
322 | if ($config['new_member_post_limit'] && $sql_ary['user_new']) |
323 | { |
324 | $sql = 'SELECT group_id |
325 | FROM ' . GROUPS_TABLE . " |
326 | WHERE group_name = 'NEWLY_REGISTERED' |
327 | AND group_type = " . GROUP_SPECIAL; |
328 | $result = $db->sql_query($sql); |
329 | $add_group_id = (int) $db->sql_fetchfield('group_id'); |
330 | $db->sql_freeresult($result); |
331 | |
332 | if ($add_group_id) |
333 | { |
334 | global $phpbb_log; |
335 | |
336 | // Because these actions only fill the log unnecessarily, we disable it |
337 | $phpbb_log->disable('admin'); |
338 | |
339 | // Add user to "newly registered users" group and set to default group if admin specified so. |
340 | if ($config['new_member_group_default']) |
341 | { |
342 | group_user_add($add_group_id, $user_id, false, false, true); |
343 | $user_row['group_id'] = $add_group_id; |
344 | } |
345 | else |
346 | { |
347 | group_user_add($add_group_id, $user_id); |
348 | } |
349 | |
350 | $phpbb_log->enable('admin'); |
351 | } |
352 | } |
353 | |
354 | // set the newest user and adjust the user count if the user is a normal user and no activation mail is sent |
355 | if ($user_row['user_type'] == USER_NORMAL || $user_row['user_type'] == USER_FOUNDER) |
356 | { |
357 | $config->set('newest_user_id', $user_id, false); |
358 | $config->set('newest_username', $user_row['username'], false); |
359 | $config->increment('num_users', 1, false); |
360 | |
361 | $sql = 'SELECT group_colour |
362 | FROM ' . GROUPS_TABLE . ' |
363 | WHERE group_id = ' . (int) $user_row['group_id']; |
364 | $result = $db->sql_query_limit($sql, 1); |
365 | $row = $db->sql_fetchrow($result); |
366 | $db->sql_freeresult($result); |
367 | |
368 | $config->set('newest_user_colour', $row['group_colour'], false); |
369 | } |
370 | |
371 | // Use default notifications settings if notifications_data is not set |
372 | if ($notifications_data === null) |
373 | { |
374 | $notifications_data = array( |
375 | array( |
376 | 'item_type' => 'notification.type.post', |
377 | 'method' => 'notification.method.email', |
378 | ), |
379 | array( |
380 | 'item_type' => 'notification.type.topic', |
381 | 'method' => 'notification.method.email', |
382 | ), |
383 | ); |
384 | } |
385 | |
386 | /** |
387 | * Modify the notifications data to be inserted in the database when a user is added |
388 | * |
389 | * @event core.user_add_modify_notifications_data |
390 | * @var array user_row Array of user details submitted to user_add |
391 | * @var array cp_data Array of Custom profile fields submitted to user_add |
392 | * @var array sql_ary Array of data to be inserted when a user is added |
393 | * @var array notifications_data Array of notification data to be inserted when a user is added |
394 | * @since 3.2.2-RC1 |
395 | */ |
396 | $vars = array('user_row', 'cp_data', 'sql_ary', 'notifications_data'); |
397 | extract($phpbb_dispatcher->trigger_event('core.user_add_modify_notifications_data', compact($vars))); |
398 | |
399 | // Subscribe user to notifications if necessary |
400 | if (!empty($notifications_data)) |
401 | { |
402 | /* @var $phpbb_notifications \phpbb\notification\manager */ |
403 | $phpbb_notifications = $phpbb_container->get('notification_manager'); |
404 | foreach ($notifications_data as $subscription) |
405 | { |
406 | $phpbb_notifications->add_subscription($subscription['item_type'], 0, $subscription['method'], $user_id); |
407 | } |
408 | } |
409 | |
410 | /** |
411 | * Event that returns user id, user details and user CPF of newly registered user |
412 | * |
413 | * @event core.user_add_after |
414 | * @var int user_id User id of newly registered user |
415 | * @var array user_row Array of user details submitted to user_add |
416 | * @var array cp_data Array of Custom profile fields submitted to user_add |
417 | * @since 3.1.0-b5 |
418 | */ |
419 | $vars = array('user_id', 'user_row', 'cp_data'); |
420 | extract($phpbb_dispatcher->trigger_event('core.user_add_after', compact($vars))); |
421 | |
422 | return $user_id; |
423 | } |
424 | |
425 | /** |
426 | * Delete user(s) and their related data |
427 | * |
428 | * @param string $mode Mode of posts deletion (retain|remove) |
429 | * @param mixed $user_ids Either an array of integers or an integer |
430 | * @param bool $retain_username True if username should be retained, false otherwise |
431 | * @return bool |
432 | */ |
433 | function user_delete($mode, $user_ids, $retain_username = true) |
434 | { |
435 | global $cache, $config, $db, $user, $phpbb_dispatcher, $phpbb_container; |
436 | global $phpbb_root_path, $phpEx; |
437 | |
438 | $db->sql_transaction('begin'); |
439 | |
440 | $user_rows = array(); |
441 | if (!is_array($user_ids)) |
442 | { |
443 | $user_ids = array($user_ids); |
444 | } |
445 | |
446 | $user_id_sql = $db->sql_in_set('user_id', $user_ids); |
447 | |
448 | $sql = 'SELECT * |
449 | FROM ' . USERS_TABLE . ' |
450 | WHERE ' . $user_id_sql; |
451 | $result = $db->sql_query($sql); |
452 | while ($row = $db->sql_fetchrow($result)) |
453 | { |
454 | $user_rows[(int) $row['user_id']] = $row; |
455 | } |
456 | $db->sql_freeresult($result); |
457 | |
458 | if (empty($user_rows)) |
459 | { |
460 | return false; |
461 | } |
462 | |
463 | /** |
464 | * Event before of the performing of the user(s) delete action |
465 | * |
466 | * @event core.delete_user_before |
467 | * @var string mode Mode of posts deletion (retain|remove) |
468 | * @var array user_ids ID(s) of the user(s) bound to be deleted |
469 | * @var bool retain_username True if username should be retained, false otherwise |
470 | * @var array user_rows Array containing data of the user(s) bound to be deleted |
471 | * @since 3.1.0-a1 |
472 | * @changed 3.2.4-RC1 Added user_rows |
473 | */ |
474 | $vars = array('mode', 'user_ids', 'retain_username', 'user_rows'); |
475 | extract($phpbb_dispatcher->trigger_event('core.delete_user_before', compact($vars))); |
476 | |
477 | // Before we begin, we will remove the reports the user issued. |
478 | $sql = 'SELECT r.post_id, p.topic_id |
479 | FROM ' . REPORTS_TABLE . ' r, ' . POSTS_TABLE . ' p |
480 | WHERE ' . $db->sql_in_set('r.user_id', $user_ids) . ' |
481 | AND p.post_id = r.post_id'; |
482 | $result = $db->sql_query($sql); |
483 | |
484 | $report_posts = $report_topics = array(); |
485 | while ($row = $db->sql_fetchrow($result)) |
486 | { |
487 | $report_posts[] = $row['post_id']; |
488 | $report_topics[] = $row['topic_id']; |
489 | } |
490 | $db->sql_freeresult($result); |
491 | |
492 | if (count($report_posts)) |
493 | { |
494 | $report_posts = array_unique($report_posts); |
495 | $report_topics = array_unique($report_topics); |
496 | |
497 | // Get a list of topics that still contain reported posts |
498 | $sql = 'SELECT DISTINCT topic_id |
499 | FROM ' . POSTS_TABLE . ' |
500 | WHERE ' . $db->sql_in_set('topic_id', $report_topics) . ' |
501 | AND post_reported = 1 |
502 | AND ' . $db->sql_in_set('post_id', $report_posts, true); |
503 | $result = $db->sql_query($sql); |
504 | |
505 | $keep_report_topics = array(); |
506 | while ($row = $db->sql_fetchrow($result)) |
507 | { |
508 | $keep_report_topics[] = $row['topic_id']; |
509 | } |
510 | $db->sql_freeresult($result); |
511 | |
512 | if (count($keep_report_topics)) |
513 | { |
514 | $report_topics = array_diff($report_topics, $keep_report_topics); |
515 | } |
516 | unset($keep_report_topics); |
517 | |
518 | // Now set the flags back |
519 | $sql = 'UPDATE ' . POSTS_TABLE . ' |
520 | SET post_reported = 0 |
521 | WHERE ' . $db->sql_in_set('post_id', $report_posts); |
522 | $db->sql_query($sql); |
523 | |
524 | if (count($report_topics)) |
525 | { |
526 | $sql = 'UPDATE ' . TOPICS_TABLE . ' |
527 | SET topic_reported = 0 |
528 | WHERE ' . $db->sql_in_set('topic_id', $report_topics); |
529 | $db->sql_query($sql); |
530 | } |
531 | } |
532 | |
533 | // Remove reports |
534 | $db->sql_query('DELETE FROM ' . REPORTS_TABLE . ' WHERE ' . $user_id_sql); |
535 | |
536 | $num_users_delta = 0; |
537 | |
538 | // Get auth provider collection in case accounts might need to be unlinked |
539 | $provider_collection = $phpbb_container->get('auth.provider_collection'); |
540 | |
541 | // Some things need to be done in the loop (if the query changes based |
542 | // on which user is currently being deleted) |
543 | $added_guest_posts = 0; |
544 | foreach ($user_rows as $user_id => $user_row) |
545 | { |
546 | if ($user_row['user_avatar'] && $user_row['user_avatar_type'] == 'avatar.driver.upload') |
547 | { |
548 | avatar_delete('user', $user_row); |
549 | } |
550 | |
551 | // Unlink accounts |
552 | foreach ($provider_collection as $provider_name => $auth_provider) |
553 | { |
554 | $provider_data = $auth_provider->get_auth_link_data($user_id); |
555 | |
556 | if ($provider_data !== null) |
557 | { |
558 | $link_data = array( |
559 | 'user_id' => $user_id, |
560 | 'link_method' => 'user_delete', |
561 | ); |
562 | |
563 | // BLOCK_VARS might contain hidden fields necessary for unlinking accounts |
564 | if (isset($provider_data['BLOCK_VARS']) && is_array($provider_data['BLOCK_VARS'])) |
565 | { |
566 | foreach ($provider_data['BLOCK_VARS'] as $provider_service) |
567 | { |
568 | if (!array_key_exists('HIDDEN_FIELDS', $provider_service)) |
569 | { |
570 | $provider_service['HIDDEN_FIELDS'] = array(); |
571 | } |
572 | |
573 | $auth_provider->unlink_account(array_merge($link_data, $provider_service['HIDDEN_FIELDS'])); |
574 | } |
575 | } |
576 | else |
577 | { |
578 | $auth_provider->unlink_account($link_data); |
579 | } |
580 | } |
581 | } |
582 | |
583 | // Decrement number of users if this user is active |
584 | if ($user_row['user_type'] != USER_INACTIVE && $user_row['user_type'] != USER_IGNORE) |
585 | { |
586 | --$num_users_delta; |
587 | } |
588 | |
589 | switch ($mode) |
590 | { |
591 | case 'retain': |
592 | if ($retain_username === false) |
593 | { |
594 | $post_username = $user->lang['GUEST']; |
595 | } |
596 | else |
597 | { |
598 | $post_username = $user_row['username']; |
599 | } |
600 | |
601 | // If the user is inactive and newly registered |
602 | // we assume no posts from the user, and save |
603 | // the queries |
604 | if ($user_row['user_type'] != USER_INACTIVE || $user_row['user_inactive_reason'] != INACTIVE_REGISTER || $user_row['user_posts']) |
605 | { |
606 | // When we delete these users and retain the posts, we must assign all the data to the guest user |
607 | $sql = 'UPDATE ' . FORUMS_TABLE . ' |
608 | SET forum_last_poster_id = ' . ANONYMOUS . ", forum_last_poster_name = '" . $db->sql_escape($post_username) . "', forum_last_poster_colour = '' |
609 | WHERE forum_last_poster_id = $user_id"; |
610 | $db->sql_query($sql); |
611 | |
612 | $sql = 'UPDATE ' . POSTS_TABLE . ' |
613 | SET poster_id = ' . ANONYMOUS . ", post_username = '" . $db->sql_escape($post_username) . "' |
614 | WHERE poster_id = $user_id"; |
615 | $db->sql_query($sql); |
616 | |
617 | $sql = 'UPDATE ' . TOPICS_TABLE . ' |
618 | SET topic_poster = ' . ANONYMOUS . ", topic_first_poster_name = '" . $db->sql_escape($post_username) . "', topic_first_poster_colour = '' |
619 | WHERE topic_poster = $user_id"; |
620 | $db->sql_query($sql); |
621 | |
622 | $sql = 'UPDATE ' . TOPICS_TABLE . ' |
623 | SET topic_last_poster_id = ' . ANONYMOUS . ", topic_last_poster_name = '" . $db->sql_escape($post_username) . "', topic_last_poster_colour = '' |
624 | WHERE topic_last_poster_id = $user_id"; |
625 | $db->sql_query($sql); |
626 | |
627 | // Since we change every post by this author, we need to count this amount towards the anonymous user |
628 | |
629 | if ($user_row['user_posts']) |
630 | { |
631 | $added_guest_posts += $user_row['user_posts']; |
632 | } |
633 | } |
634 | break; |
635 | |
636 | case 'remove': |
637 | // there is nothing variant specific to deleting posts |
638 | break; |
639 | } |
640 | } |
641 | |
642 | if ($num_users_delta != 0) |
643 | { |
644 | $config->increment('num_users', $num_users_delta, false); |
645 | } |
646 | |
647 | // Now do the invariant tasks |
648 | // all queries performed in one call of this function are in a single transaction |
649 | // so this is kosher |
650 | if ($mode == 'retain') |
651 | { |
652 | // Assign more data to the Anonymous user |
653 | $sql = 'UPDATE ' . ATTACHMENTS_TABLE . ' |
654 | SET poster_id = ' . ANONYMOUS . ' |
655 | WHERE ' . $db->sql_in_set('poster_id', $user_ids); |
656 | $db->sql_query($sql); |
657 | |
658 | $sql = 'UPDATE ' . USERS_TABLE . ' |
659 | SET user_posts = user_posts + ' . $added_guest_posts . ' |
660 | WHERE user_id = ' . ANONYMOUS; |
661 | $db->sql_query($sql); |
662 | } |
663 | else if ($mode == 'remove') |
664 | { |
665 | if (!function_exists('delete_posts')) |
666 | { |
667 | include($phpbb_root_path . 'includes/functions_admin.' . $phpEx); |
668 | } |
669 | |
670 | // Delete posts, attachments, etc. |
671 | // delete_posts can handle any number of IDs in its second argument |
672 | delete_posts('poster_id', $user_ids); |
673 | } |
674 | |
675 | $table_ary = [ |
676 | USERS_TABLE, |
677 | USER_GROUP_TABLE, |
678 | TOPICS_WATCH_TABLE, |
679 | FORUMS_WATCH_TABLE, |
680 | ACL_USERS_TABLE, |
681 | TOPICS_TRACK_TABLE, |
682 | TOPICS_POSTED_TABLE, |
683 | FORUMS_TRACK_TABLE, |
684 | PROFILE_FIELDS_DATA_TABLE, |
685 | MODERATOR_CACHE_TABLE, |
686 | DRAFTS_TABLE, |
687 | BOOKMARKS_TABLE, |
688 | SESSIONS_KEYS_TABLE, |
689 | PRIVMSGS_FOLDER_TABLE, |
690 | PRIVMSGS_RULES_TABLE, |
691 | $phpbb_container->getParameter('tables.auth_provider_oauth_token_storage'), |
692 | $phpbb_container->getParameter('tables.auth_provider_oauth_states'), |
693 | $phpbb_container->getParameter('tables.auth_provider_oauth_account_assoc'), |
694 | $phpbb_container->getParameter('tables.user_notifications') |
695 | ]; |
696 | |
697 | // Ignore errors on deleting from non-existent tables, e.g. when migrating |
698 | $db->sql_return_on_error(true); |
699 | // Delete the miscellaneous (non-post) data for the user |
700 | foreach ($table_ary as $table) |
701 | { |
702 | $sql = "DELETE FROM $table |
703 | WHERE " . $user_id_sql; |
704 | $db->sql_query($sql); |
705 | } |
706 | $db->sql_return_on_error(); |
707 | |
708 | $cache->destroy('sql', MODERATOR_CACHE_TABLE); |
709 | |
710 | // Change user_id to anonymous for posts edited by this user |
711 | $sql = 'UPDATE ' . POSTS_TABLE . ' |
712 | SET post_edit_user = ' . ANONYMOUS . ' |
713 | WHERE ' . $db->sql_in_set('post_edit_user', $user_ids); |
714 | $db->sql_query($sql); |
715 | |
716 | // Change user_id to anonymous for pms edited by this user |
717 | $sql = 'UPDATE ' . PRIVMSGS_TABLE . ' |
718 | SET message_edit_user = ' . ANONYMOUS . ' |
719 | WHERE ' . $db->sql_in_set('message_edit_user', $user_ids); |
720 | $db->sql_query($sql); |
721 | |
722 | // Change user_id to anonymous for posts deleted by this user |
723 | $sql = 'UPDATE ' . POSTS_TABLE . ' |
724 | SET post_delete_user = ' . ANONYMOUS . ' |
725 | WHERE ' . $db->sql_in_set('post_delete_user', $user_ids); |
726 | $db->sql_query($sql); |
727 | |
728 | // Change user_id to anonymous for topics deleted by this user |
729 | $sql = 'UPDATE ' . TOPICS_TABLE . ' |
730 | SET topic_delete_user = ' . ANONYMOUS . ' |
731 | WHERE ' . $db->sql_in_set('topic_delete_user', $user_ids); |
732 | $db->sql_query($sql); |
733 | |
734 | // Delete user log entries about this user |
735 | $sql = 'DELETE FROM ' . LOG_TABLE . ' |
736 | WHERE ' . $db->sql_in_set('reportee_id', $user_ids); |
737 | $db->sql_query($sql); |
738 | |
739 | // Change user_id to anonymous for this users triggered events |
740 | $sql = 'UPDATE ' . LOG_TABLE . ' |
741 | SET user_id = ' . ANONYMOUS . ' |
742 | WHERE ' . $user_id_sql; |
743 | $db->sql_query($sql); |
744 | |
745 | // Delete the user_id from the zebra table |
746 | $sql = 'DELETE FROM ' . ZEBRA_TABLE . ' |
747 | WHERE ' . $user_id_sql . ' |
748 | OR ' . $db->sql_in_set('zebra_id', $user_ids); |
749 | $db->sql_query($sql); |
750 | |
751 | // Delete the user_id from the banlist |
752 | $sql = 'DELETE FROM ' . BANS_TABLE . " |
753 | WHERE ban_mode = 'user' |
754 | AND " . $db->sql_in_set('ban_userid', $user_ids); |
755 | $db->sql_query($sql); |
756 | |
757 | // Delete the user_id from the session table |
758 | $sql = 'DELETE FROM ' . SESSIONS_TABLE . ' |
759 | WHERE ' . $db->sql_in_set('session_user_id', $user_ids); |
760 | $db->sql_query($sql); |
761 | |
762 | // Clean the private messages tables from the user |
763 | if (!function_exists('phpbb_delete_users_pms')) |
764 | { |
765 | include($phpbb_root_path . 'includes/functions_privmsgs.' . $phpEx); |
766 | } |
767 | phpbb_delete_users_pms($user_ids); |
768 | |
769 | $phpbb_notifications = $phpbb_container->get('notification_manager'); |
770 | $phpbb_notifications->delete_notifications('notification.type.admin_activate_user', $user_ids); |
771 | |
772 | $db->sql_transaction('commit'); |
773 | |
774 | /** |
775 | * Event after the user(s) delete action has been performed |
776 | * |
777 | * @event core.delete_user_after |
778 | * @var string mode Mode of posts deletion (retain|remove) |
779 | * @var array user_ids ID(s) of the deleted user(s) |
780 | * @var bool retain_username True if username should be retained, false otherwise |
781 | * @var array user_rows Array containing data of the deleted user(s) |
782 | * @since 3.1.0-a1 |
783 | * @changed 3.2.2-RC1 Added user_rows |
784 | */ |
785 | $vars = array('mode', 'user_ids', 'retain_username', 'user_rows'); |
786 | extract($phpbb_dispatcher->trigger_event('core.delete_user_after', compact($vars))); |
787 | |
788 | // Reset newest user info if appropriate |
789 | if (in_array($config['newest_user_id'], $user_ids)) |
790 | { |
791 | update_last_username(); |
792 | } |
793 | |
794 | return false; |
795 | } |
796 | |
797 | /** |
798 | * Flips user_type from active to inactive and vice versa, handles group membership updates |
799 | * |
800 | * @param string $mode can be flip for flipping from active/inactive, activate or deactivate |
801 | * @param array $user_id_ary |
802 | * @param int $reason |
803 | */ |
804 | function user_active_flip($mode, $user_id_ary, $reason = INACTIVE_MANUAL) |
805 | { |
806 | global $config, $db, $user, $auth, $phpbb_dispatcher; |
807 | |
808 | $deactivated = $activated = 0; |
809 | $sql_statements = array(); |
810 | |
811 | if (!is_array($user_id_ary)) |
812 | { |
813 | $user_id_ary = array($user_id_ary); |
814 | } |
815 | |
816 | if (!count($user_id_ary)) |
817 | { |
818 | return; |
819 | } |
820 | |
821 | $sql = 'SELECT user_id, group_id, user_type, user_inactive_reason |
822 | FROM ' . USERS_TABLE . ' |
823 | WHERE ' . $db->sql_in_set('user_id', $user_id_ary); |
824 | $result = $db->sql_query($sql); |
825 | |
826 | while ($row = $db->sql_fetchrow($result)) |
827 | { |
828 | $sql_ary = array(); |
829 | |
830 | if ($row['user_type'] == USER_IGNORE || $row['user_type'] == USER_FOUNDER || |
831 | ($mode == 'activate' && $row['user_type'] != USER_INACTIVE) || |
832 | ($mode == 'deactivate' && $row['user_type'] == USER_INACTIVE)) |
833 | { |
834 | continue; |
835 | } |
836 | |
837 | if ($row['user_type'] == USER_INACTIVE) |
838 | { |
839 | $activated++; |
840 | } |
841 | else |
842 | { |
843 | $deactivated++; |
844 | |
845 | // Remove the users session key... |
846 | $user->reset_login_keys($row['user_id']); |
847 | } |
848 | |
849 | $sql_ary += array( |
850 | 'user_type' => ($row['user_type'] == USER_NORMAL) ? USER_INACTIVE : USER_NORMAL, |
851 | 'user_inactive_time' => ($row['user_type'] == USER_NORMAL) ? time() : 0, |
852 | 'user_inactive_reason' => ($row['user_type'] == USER_NORMAL) ? $reason : 0, |
853 | ); |
854 | |
855 | $sql_statements[$row['user_id']] = $sql_ary; |
856 | } |
857 | $db->sql_freeresult($result); |
858 | |
859 | /** |
860 | * Check or modify activated/deactivated users data before submitting it to the database |
861 | * |
862 | * @event core.user_active_flip_before |
863 | * @var string mode User type changing mode, can be: flip|activate|deactivate |
864 | * @var int reason Reason for changing user type, can be: INACTIVE_REGISTER|INACTIVE_PROFILE|INACTIVE_MANUAL|INACTIVE_REMIND |
865 | * @var int activated The number of users to be activated |
866 | * @var int deactivated The number of users to be deactivated |
867 | * @var array user_id_ary Array with user ids to change user type |
868 | * @var array sql_statements Array with users data to submit to the database, keys: user ids, values: arrays with user data |
869 | * @since 3.1.4-RC1 |
870 | */ |
871 | $vars = array('mode', 'reason', 'activated', 'deactivated', 'user_id_ary', 'sql_statements'); |
872 | extract($phpbb_dispatcher->trigger_event('core.user_active_flip_before', compact($vars))); |
873 | |
874 | if (count($sql_statements)) |
875 | { |
876 | foreach ($sql_statements as $user_id => $sql_ary) |
877 | { |
878 | $sql = 'UPDATE ' . USERS_TABLE . ' |
879 | SET ' . $db->sql_build_array('UPDATE', $sql_ary) . ' |
880 | WHERE user_id = ' . $user_id; |
881 | $db->sql_query($sql); |
882 | } |
883 | |
884 | $auth->acl_clear_prefetch(array_keys($sql_statements)); |
885 | } |
886 | |
887 | /** |
888 | * Perform additional actions after the users have been activated/deactivated |
889 | * |
890 | * @event core.user_active_flip_after |
891 | * @var string mode User type changing mode, can be: flip|activate|deactivate |
892 | * @var int reason Reason for changing user type, can be: INACTIVE_REGISTER|INACTIVE_PROFILE|INACTIVE_MANUAL|INACTIVE_REMIND |
893 | * @var int activated The number of users to be activated |
894 | * @var int deactivated The number of users to be deactivated |
895 | * @var array user_id_ary Array with user ids to change user type |
896 | * @var array sql_statements Array with users data to submit to the database, keys: user ids, values: arrays with user data |
897 | * @since 3.1.4-RC1 |
898 | */ |
899 | $vars = array('mode', 'reason', 'activated', 'deactivated', 'user_id_ary', 'sql_statements'); |
900 | extract($phpbb_dispatcher->trigger_event('core.user_active_flip_after', compact($vars))); |
901 | |
902 | if ($deactivated) |
903 | { |
904 | $config->increment('num_users', $deactivated * (-1), false); |
905 | } |
906 | |
907 | if ($activated) |
908 | { |
909 | $config->increment('num_users', $activated, false); |
910 | } |
911 | |
912 | // Update latest username |
913 | update_last_username(); |
914 | } |
915 | |
916 | /** |
917 | * Add a ban or ban exclusion to the banlist. Bans either a user, an IP or an email address |
918 | * |
919 | * @deprecated 4.0.0-a1 (To be removed: 4.1.0) |
920 | * |
921 | * @param string $mode Type of ban. One of the following: user, ip, email |
922 | * @param mixed $ban Banned entity. Either string or array with usernames, ips or email addresses |
923 | * @param int $ban_len Ban length in minutes |
924 | * @param string $ban_len_other Ban length as a date (YYYY-MM-DD) |
925 | * @param string $ban_reason String describing the reason for this ban |
926 | * @param string $ban_give_reason |
927 | * @return boolean |
928 | */ |
929 | function user_ban($mode, $ban, $ban_len, $ban_len_other, $ban_reason, $ban_give_reason = '') |
930 | { |
931 | global $phpbb_container, $user; |
932 | |
933 | /** @var \phpbb\ban\manager $ban_manager */ |
934 | $ban_manager = $phpbb_container->get('ban.manager'); |
935 | |
936 | $items = is_array($ban) ? $ban : [$ban]; |
937 | |
938 | $current_time = time(); |
939 | // Set $ban_end to the unix time when the ban should end. 0 is a permanent ban. |
940 | if ($ban_len) |
941 | { |
942 | if ($ban_len != -1 || !$ban_len_other) |
943 | { |
944 | $ban_end = max($current_time, $current_time + ($ban_len) * 60); |
945 | } |
946 | else |
947 | { |
948 | $ban_other = explode('-', $ban_len_other); |
949 | if (count($ban_other) == 3 && ((int) $ban_other[0] < 9999) && |
950 | (strlen($ban_other[0]) == 4) && (strlen($ban_other[1]) == 2) && (strlen($ban_other[2]) == 2)) |
951 | { |
952 | $ban_end = max($current_time, $user->create_datetime() |
953 | ->setDate((int) $ban_other[0], (int) $ban_other[1], (int) $ban_other[2]) |
954 | ->setTime(0, 0, 0) |
955 | ->getTimestamp() + $user->timezone->getOffset(new DateTime('UTC'))); |
956 | } |
957 | else |
958 | { |
959 | trigger_error('LENGTH_BAN_INVALID', E_USER_WARNING); |
960 | } |
961 | } |
962 | } |
963 | else |
964 | { |
965 | $ban_end = 0; |
966 | } |
967 | |
968 | $start = new \DateTime(); |
969 | $start->setTimestamp($current_time); |
970 | $end = new \DateTime(); |
971 | $end->setTimestamp($ban_end); |
972 | |
973 | return $ban_manager->ban($mode, $items, $start, $end, $ban_reason, $ban_give_reason); |
974 | } |
975 | |
976 | /** |
977 | * Unban User |
978 | * |
979 | * @deprecated 4.0.0-a1 (To be removed: 4.1.0) |
980 | */ |
981 | function user_unban($mode, $ban) |
982 | { |
983 | global $phpbb_container; |
984 | |
985 | $items = is_array($ban) ? $ban : [$ban]; |
986 | |
987 | /** @var \phpbb\ban\manager $ban_manager */ |
988 | $ban_manager = $phpbb_container->get('ban.manager'); |
989 | $ban_manager->unban($mode, $items); |
990 | } |
991 | |
992 | /** |
993 | * Internet Protocol Address Whois |
994 | * RFC3912: WHOIS Protocol Specification |
995 | * |
996 | * @param string $ip Ip address, either IPv4 or IPv6. |
997 | * |
998 | * @return string Empty string if not a valid ip address. |
999 | * Otherwise make_clickable()'ed whois result. |
1000 | */ |
1001 | function user_ipwhois($ip) |
1002 | { |
1003 | if (!filter_var($ip, FILTER_VALIDATE_IP)) |
1004 | { |
1005 | return ''; |
1006 | } |
1007 | |
1008 | // IPv4 & IPv6 addresses |
1009 | $whois_host = 'whois.arin.net.'; |
1010 | |
1011 | $ipwhois = ''; |
1012 | |
1013 | if (($fsk = @fsockopen($whois_host, 43))) |
1014 | { |
1015 | // CRLF as per RFC3912 |
1016 | // Z to limit the query to all possible flags (whois.arin.net) |
1017 | fputs($fsk, "z $ip\r\n"); |
1018 | while (!feof($fsk)) |
1019 | { |
1020 | $ipwhois .= fgets($fsk, 1024); |
1021 | } |
1022 | @fclose($fsk); |
1023 | } |
1024 | |
1025 | $match = array(); |
1026 | |
1027 | // Test for referrals from $whois_host to other whois databases, roll on rwhois |
1028 | if (preg_match('#ReferralServer:[\x20]*whois://(.+)#im', $ipwhois, $match)) |
1029 | { |
1030 | if (strpos($match[1], ':') !== false) |
1031 | { |
1032 | $pos = strrpos($match[1], ':'); |
1033 | $server = substr($match[1], 0, $pos); |
1034 | $port = (int) substr($match[1], $pos + 1); |
1035 | unset($pos); |
1036 | } |
1037 | else |
1038 | { |
1039 | $server = $match[1]; |
1040 | $port = 43; |
1041 | } |
1042 | |
1043 | $buffer = ''; |
1044 | |
1045 | if (($fsk = @fsockopen($server, $port))) |
1046 | { |
1047 | fputs($fsk, "$ip\r\n"); |
1048 | while (!feof($fsk)) |
1049 | { |
1050 | $buffer .= fgets($fsk, 1024); |
1051 | } |
1052 | @fclose($fsk); |
1053 | } |
1054 | |
1055 | // Use the result from $whois_host if we don't get any result here |
1056 | $ipwhois = (empty($buffer)) ? $ipwhois : $buffer; |
1057 | } |
1058 | |
1059 | $ipwhois = htmlspecialchars($ipwhois, ENT_COMPAT); |
1060 | |
1061 | // Magic URL ;) |
1062 | return trim(make_clickable($ipwhois, false, '')); |
1063 | } |
1064 | |
1065 | /** |
1066 | * Data validation ... used primarily but not exclusively by ucp modules |
1067 | * |
1068 | * "Master" function for validating a range of data types |
1069 | */ |
1070 | function validate_data($data, $val_ary) |
1071 | { |
1072 | global $user; |
1073 | |
1074 | $error = array(); |
1075 | |
1076 | foreach ($val_ary as $var => $val_seq) |
1077 | { |
1078 | if (!is_array($val_seq[0])) |
1079 | { |
1080 | $val_seq = array($val_seq); |
1081 | } |
1082 | |
1083 | foreach ($val_seq as $validate) |
1084 | { |
1085 | $function = array_shift($validate); |
1086 | array_unshift($validate, $data[$var]); |
1087 | |
1088 | if (is_array($function)) |
1089 | { |
1090 | $result = call_user_func_array(array($function[0], 'validate_' . $function[1]), $validate); |
1091 | } |
1092 | else |
1093 | { |
1094 | $function_prefix = (function_exists('phpbb_validate_' . $function)) ? 'phpbb_validate_' : 'validate_'; |
1095 | $result = call_user_func_array($function_prefix . $function, $validate); |
1096 | } |
1097 | |
1098 | if ($result) |
1099 | { |
1100 | // Since errors are checked later for their language file existence, we need to make sure custom errors are not adjusted. |
1101 | $error[] = (empty($user->lang[$result . '_' . strtoupper($var)])) ? $result : $result . '_' . strtoupper($var); |
1102 | } |
1103 | } |
1104 | } |
1105 | |
1106 | return $error; |
1107 | } |
1108 | |
1109 | /** |
1110 | * Validate String |
1111 | * |
1112 | * @return boolean|string Either false if validation succeeded or a string which will be used as the error message (with the variable name appended) |
1113 | */ |
1114 | function validate_string($string, $optional = false, $min = 0, $max = 0) |
1115 | { |
1116 | if (empty($string) && $optional) |
1117 | { |
1118 | return false; |
1119 | } |
1120 | |
1121 | if ($min && utf8_strlen(html_entity_decode($string, ENT_COMPAT)) < $min) |
1122 | { |
1123 | return 'TOO_SHORT'; |
1124 | } |
1125 | else if ($max && utf8_strlen(html_entity_decode($string, ENT_COMPAT)) > $max) |
1126 | { |
1127 | return 'TOO_LONG'; |
1128 | } |
1129 | |
1130 | return false; |
1131 | } |
1132 | |
1133 | /** |
1134 | * Validate Number |
1135 | * |
1136 | * @return boolean|string Either false if validation succeeded or a string which will be used as the error message (with the variable name appended) |
1137 | */ |
1138 | function validate_num($num, $optional = false, $min = 0, $max = 1E99) |
1139 | { |
1140 | if (empty($num) && $optional) |
1141 | { |
1142 | return false; |
1143 | } |
1144 | |
1145 | if ($num < $min) |
1146 | { |
1147 | return 'TOO_SMALL'; |
1148 | } |
1149 | else if ($num > $max) |
1150 | { |
1151 | return 'TOO_LARGE'; |
1152 | } |
1153 | |
1154 | return false; |
1155 | } |
1156 | |
1157 | /** |
1158 | * Validate Date |
1159 | * @param string $date_string a date in the dd-mm-yyyy format |
1160 | * @param bool $optional |
1161 | * @return boolean |
1162 | */ |
1163 | function validate_date($date_string, $optional = false) |
1164 | { |
1165 | $date = explode('-', $date_string); |
1166 | if ((empty($date) || count($date) != 3) && $optional) |
1167 | { |
1168 | return false; |
1169 | } |
1170 | else if ($optional) |
1171 | { |
1172 | for ($field = 0; $field <= 1; $field++) |
1173 | { |
1174 | $date[$field] = (int) $date[$field]; |
1175 | if (empty($date[$field])) |
1176 | { |
1177 | $date[$field] = 1; |
1178 | } |
1179 | } |
1180 | $date[2] = (int) $date[2]; |
1181 | // assume an arbitrary leap year |
1182 | if (empty($date[2])) |
1183 | { |
1184 | $date[2] = 1980; |
1185 | } |
1186 | } |
1187 | |
1188 | if (count($date) != 3 || !checkdate($date[1], $date[0], $date[2])) |
1189 | { |
1190 | return 'INVALID'; |
1191 | } |
1192 | |
1193 | return false; |
1194 | } |
1195 | |
1196 | /** |
1197 | * Validate Match |
1198 | * |
1199 | * @return boolean|string Either false if validation succeeded or a string which will be used as the error message (with the variable name appended) |
1200 | */ |
1201 | function validate_match($string, $optional = false, $match = '') |
1202 | { |
1203 | if (empty($string) && $optional) |
1204 | { |
1205 | return false; |
1206 | } |
1207 | |
1208 | if (empty($match)) |
1209 | { |
1210 | return false; |
1211 | } |
1212 | |
1213 | if (!preg_match($match, $string)) |
1214 | { |
1215 | return 'WRONG_DATA'; |
1216 | } |
1217 | |
1218 | return false; |
1219 | } |
1220 | |
1221 | /** |
1222 | * Validate Language Pack ISO Name |
1223 | * |
1224 | * Tests whether a language name is valid and installed |
1225 | * |
1226 | * @param string $lang_iso The language string to test |
1227 | * |
1228 | * @return bool|string Either false if validation succeeded or |
1229 | * a string which will be used as the error message |
1230 | * (with the variable name appended) |
1231 | */ |
1232 | function validate_language_iso_name($lang_iso) |
1233 | { |
1234 | global $db; |
1235 | |
1236 | $sql = 'SELECT lang_id |
1237 | FROM ' . LANG_TABLE . " |
1238 | WHERE lang_iso = '" . $db->sql_escape($lang_iso) . "'"; |
1239 | $result = $db->sql_query($sql); |
1240 | $lang_id = (int) $db->sql_fetchfield('lang_id'); |
1241 | $db->sql_freeresult($result); |
1242 | |
1243 | return ($lang_id) ? false : 'WRONG_DATA'; |
1244 | } |
1245 | |
1246 | /** |
1247 | * Validate Timezone Name |
1248 | * |
1249 | * Tests whether a timezone name is valid |
1250 | * |
1251 | * @param string $timezone The timezone string to test |
1252 | * |
1253 | * @return bool|string Either false if validation succeeded or |
1254 | * a string which will be used as the error message |
1255 | * (with the variable name appended) |
1256 | */ |
1257 | function phpbb_validate_timezone($timezone) |
1258 | { |
1259 | return (in_array($timezone, phpbb_get_timezone_identifiers($timezone))) ? false : 'TIMEZONE_INVALID'; |
1260 | } |
1261 | |
1262 | /*** |
1263 | * Validate Username |
1264 | * |
1265 | * Check to see if the username has been taken, or if it is disallowed. |
1266 | * Also checks if it includes the " character or the 4-bytes Unicode ones |
1267 | * (aka emojis) which we don't allow in usernames. |
1268 | * Used for registering, changing names, and posting anonymously with a username |
1269 | * |
1270 | * @param string $username The username to check |
1271 | * @param string $allowed_username An allowed username, default being $user->data['username'] |
1272 | * |
1273 | * @return string|false Either false if validation succeeded or a string which will be |
1274 | * used as the error message (with the variable name appended) |
1275 | */ |
1276 | function validate_username($username, $allowed_username = false, $allow_all_names = false) |
1277 | { |
1278 | global $config, $db, $user, $cache; |
1279 | |
1280 | $clean_username = utf8_clean_string($username); |
1281 | $allowed_username = ($allowed_username === false) ? $user->data['username_clean'] : utf8_clean_string($allowed_username); |
1282 | |
1283 | if ($allowed_username == $clean_username) |
1284 | { |
1285 | return false; |
1286 | } |
1287 | |
1288 | // The very first check is for |
1289 | // out-of-bounds characters that are currently |
1290 | // not supported by utf8_bin in MySQL |
1291 | if (preg_match('/[\x{10000}-\x{10FFFF}]/u', $username)) |
1292 | { |
1293 | return 'INVALID_EMOJIS'; |
1294 | } |
1295 | |
1296 | // ... fast checks first. |
1297 | if (strpos($username, '"') !== false || strpos($username, '"') !== false || empty($clean_username) |
1298 | || preg_match('/[\x{180E}\x{2005}-\x{200D}\x{202F}\x{205F}\x{2060}\x{FEFF}]/u', $username)) |
1299 | { |
1300 | return 'INVALID_CHARS'; |
1301 | } |
1302 | |
1303 | switch ($config['allow_name_chars']) |
1304 | { |
1305 | case 'USERNAME_CHARS_ANY': |
1306 | $regex = '.+'; |
1307 | break; |
1308 | |
1309 | case 'USERNAME_ALPHA_ONLY': |
1310 | $regex = '[A-Za-z0-9]+'; |
1311 | break; |
1312 | |
1313 | case 'USERNAME_ALPHA_SPACERS': |
1314 | $regex = '[A-Za-z0-9-[\]_+ ]+'; |
1315 | break; |
1316 | |
1317 | case 'USERNAME_LETTER_NUM': |
1318 | $regex = '[\p{Lu}\p{Ll}\p{N}]+'; |
1319 | break; |
1320 | |
1321 | case 'USERNAME_LETTER_NUM_SPACERS': |
1322 | $regex = '[-\]_+ [\p{Lu}\p{Ll}\p{N}]+'; |
1323 | break; |
1324 | |
1325 | case 'USERNAME_ASCII': |
1326 | default: |
1327 | $regex = '[\x01-\x7F]+'; |
1328 | break; |
1329 | } |
1330 | |
1331 | if (!preg_match('#^' . $regex . '$#u', $username)) |
1332 | { |
1333 | return 'INVALID_CHARS'; |
1334 | } |
1335 | |
1336 | $sql = 'SELECT username |
1337 | FROM ' . USERS_TABLE . " |
1338 | WHERE username_clean = '" . $db->sql_escape($clean_username) . "'"; |
1339 | $result = $db->sql_query($sql); |
1340 | $row = $db->sql_fetchrow($result); |
1341 | $db->sql_freeresult($result); |
1342 | |
1343 | if ($row) |
1344 | { |
1345 | return 'USERNAME_TAKEN'; |
1346 | } |
1347 | |
1348 | $sql = 'SELECT group_name |
1349 | FROM ' . GROUPS_TABLE . " |
1350 | WHERE LOWER(group_name) = '" . $db->sql_escape(utf8_strtolower($username)) . "'"; |
1351 | $result = $db->sql_query($sql); |
1352 | $row = $db->sql_fetchrow($result); |
1353 | $db->sql_freeresult($result); |
1354 | |
1355 | if ($row) |
1356 | { |
1357 | return 'USERNAME_TAKEN'; |
1358 | } |
1359 | |
1360 | if (!$allow_all_names) |
1361 | { |
1362 | $bad_usernames = $cache->obtain_disallowed_usernames(); |
1363 | |
1364 | foreach ($bad_usernames as $bad_username) |
1365 | { |
1366 | if (preg_match('#^' . $bad_username . '$#', $clean_username)) |
1367 | { |
1368 | return 'USERNAME_DISALLOWED'; |
1369 | } |
1370 | } |
1371 | } |
1372 | |
1373 | return false; |
1374 | } |
1375 | |
1376 | /** |
1377 | * Check to see if the password meets the complexity settings |
1378 | * |
1379 | * @return boolean|string Either false if validation succeeded or a string which will be used as the error message (with the variable name appended) |
1380 | */ |
1381 | function validate_password($password) |
1382 | { |
1383 | global $config; |
1384 | |
1385 | if ($password === '' || $config['pass_complex'] === 'PASS_TYPE_ANY') |
1386 | { |
1387 | // Password empty or no password complexity required. |
1388 | return false; |
1389 | } |
1390 | |
1391 | $upp = '\p{Lu}'; |
1392 | $low = '\p{Ll}'; |
1393 | $num = '\p{N}'; |
1394 | $sym = '[^\p{Lu}\p{Ll}\p{N}]'; |
1395 | $chars = array(); |
1396 | |
1397 | switch ($config['pass_complex']) |
1398 | { |
1399 | // No break statements below ... |
1400 | // We require strong passwords in case pass_complex is not set or is invalid |
1401 | default: |
1402 | |
1403 | // Require mixed case letters, numbers and symbols |
1404 | case 'PASS_TYPE_SYMBOL': |
1405 | $chars[] = $sym; |
1406 | |
1407 | // Require mixed case letters and numbers |
1408 | case 'PASS_TYPE_ALPHA': |
1409 | $chars[] = $num; |
1410 | |
1411 | // Require mixed case letters |
1412 | case 'PASS_TYPE_CASE': |
1413 | $chars[] = $low; |
1414 | $chars[] = $upp; |
1415 | } |
1416 | |
1417 | foreach ($chars as $char) |
1418 | { |
1419 | if (!preg_match('#' . $char . '#u', $password)) |
1420 | { |
1421 | return 'INVALID_CHARS'; |
1422 | } |
1423 | } |
1424 | |
1425 | return false; |
1426 | } |
1427 | |
1428 | /** |
1429 | * Check to see if email address is a valid address and contains a MX record |
1430 | * |
1431 | * @param string $email The email to check |
1432 | * @param $config |
1433 | * |
1434 | * @return mixed Either false if validation succeeded or a string which will be used as the error message (with the variable name appended) |
1435 | */ |
1436 | function phpbb_validate_email($email, $config = null) |
1437 | { |
1438 | if ($config === null) |
1439 | { |
1440 | global $config; |
1441 | } |
1442 | |
1443 | $email = strtolower($email); |
1444 | |
1445 | if (!preg_match('/^' . get_preg_expression('email') . '$/i', $email)) |
1446 | { |
1447 | return 'EMAIL_INVALID'; |
1448 | } |
1449 | |
1450 | // Check MX record. |
1451 | // The idea for this is from reading the UseBB blog/announcement. :) |
1452 | if ($config['email_check_mx']) |
1453 | { |
1454 | list(, $domain) = explode('@', $email); |
1455 | |
1456 | if (checkdnsrr($domain, 'A') === false && checkdnsrr($domain, 'MX') === false) |
1457 | { |
1458 | return 'DOMAIN_NO_MX_RECORD'; |
1459 | } |
1460 | } |
1461 | |
1462 | return false; |
1463 | } |
1464 | |
1465 | /** |
1466 | * Check to see if email address is banned or already present in the DB |
1467 | * |
1468 | * @param string $email The email to check |
1469 | * @param string $allowed_email An allowed email, default being $user->data['user_email'] |
1470 | * |
1471 | * @return mixed Either false if validation succeeded or a string which will be used as the error message (with the variable name appended) |
1472 | */ |
1473 | function validate_user_email($email, $allowed_email = false) |
1474 | { |
1475 | global $config, $db, $user; |
1476 | |
1477 | $email = strtolower($email); |
1478 | $allowed_email = ($allowed_email === false) ? strtolower($user->data['user_email']) : strtolower($allowed_email); |
1479 | |
1480 | if ($allowed_email == $email) |
1481 | { |
1482 | return false; |
1483 | } |
1484 | |
1485 | $validate_email = phpbb_validate_email($email, $config); |
1486 | if ($validate_email) |
1487 | { |
1488 | return $validate_email; |
1489 | } |
1490 | |
1491 | $ban = $user->check_ban(false, false, $email, true); |
1492 | if (!empty($ban)) |
1493 | { |
1494 | return !empty($ban['reason']) ? $ban['reason'] : 'EMAIL_BANNED'; |
1495 | } |
1496 | |
1497 | if (!$config['allow_emailreuse']) |
1498 | { |
1499 | $sql = 'SELECT user_email |
1500 | FROM ' . USERS_TABLE . " |
1501 | WHERE user_email = '" . $db->sql_escape($email) . "'"; |
1502 | $result = $db->sql_query($sql); |
1503 | $row = $db->sql_fetchrow($result); |
1504 | $db->sql_freeresult($result); |
1505 | |
1506 | if ($row) |
1507 | { |
1508 | return 'EMAIL_TAKEN'; |
1509 | } |
1510 | } |
1511 | |
1512 | return false; |
1513 | } |
1514 | |
1515 | /** |
1516 | * Validate jabber address |
1517 | * Taken from the jabber class within flyspray (see author notes) |
1518 | * |
1519 | * @author flyspray.org |
1520 | */ |
1521 | function validate_jabber($jid) |
1522 | { |
1523 | if (!$jid) |
1524 | { |
1525 | return false; |
1526 | } |
1527 | |
1528 | $separator_pos = strpos($jid, '@'); |
1529 | |
1530 | if ($separator_pos === false) |
1531 | { |
1532 | return 'WRONG_DATA'; |
1533 | } |
1534 | |
1535 | $username = substr($jid, 0, $separator_pos); |
1536 | $realm = substr($jid, $separator_pos + 1); |
1537 | |
1538 | if (strlen($username) == 0 || strlen($realm) < 3) |
1539 | { |
1540 | return 'WRONG_DATA'; |
1541 | } |
1542 | |
1543 | $arr = explode('.', $realm); |
1544 | |
1545 | if (count($arr) == 0) |
1546 | { |
1547 | return 'WRONG_DATA'; |
1548 | } |
1549 | |
1550 | foreach ($arr as $part) |
1551 | { |
1552 | if (substr($part, 0, 1) == '-' || substr($part, -1, 1) == '-') |
1553 | { |
1554 | return 'WRONG_DATA'; |
1555 | } |
1556 | |
1557 | if (!preg_match("@^[a-zA-Z0-9-.]+$@", $part)) |
1558 | { |
1559 | return 'WRONG_DATA'; |
1560 | } |
1561 | } |
1562 | |
1563 | $boundary = array(array(0, 127), array(192, 223), array(224, 239), array(240, 247), array(248, 251), array(252, 253)); |
1564 | |
1565 | // Prohibited Characters RFC3454 + RFC3920 |
1566 | $prohibited = array( |
1567 | // Table C.1.1 |
1568 | array(0x0020, 0x0020), // SPACE |
1569 | // Table C.1.2 |
1570 | array(0x00A0, 0x00A0), // NO-BREAK SPACE |
1571 | array(0x1680, 0x1680), // OGHAM SPACE MARK |
1572 | array(0x2000, 0x2001), // EN QUAD |
1573 | array(0x2001, 0x2001), // EM QUAD |
1574 | array(0x2002, 0x2002), // EN SPACE |
1575 | array(0x2003, 0x2003), // EM SPACE |
1576 | array(0x2004, 0x2004), // THREE-PER-EM SPACE |
1577 | array(0x2005, 0x2005), // FOUR-PER-EM SPACE |
1578 | array(0x2006, 0x2006), // SIX-PER-EM SPACE |
1579 | array(0x2007, 0x2007), // FIGURE SPACE |
1580 | array(0x2008, 0x2008), // PUNCTUATION SPACE |
1581 | array(0x2009, 0x2009), // THIN SPACE |
1582 | array(0x200A, 0x200A), // HAIR SPACE |
1583 | array(0x200B, 0x200B), // ZERO WIDTH SPACE |
1584 | array(0x202F, 0x202F), // NARROW NO-BREAK SPACE |
1585 | array(0x205F, 0x205F), // MEDIUM MATHEMATICAL SPACE |
1586 | array(0x3000, 0x3000), // IDEOGRAPHIC SPACE |
1587 | // Table C.2.1 |
1588 | array(0x0000, 0x001F), // [CONTROL CHARACTERS] |
1589 | array(0x007F, 0x007F), // DELETE |
1590 | // Table C.2.2 |
1591 | array(0x0080, 0x009F), // [CONTROL CHARACTERS] |
1592 | array(0x06DD, 0x06DD), // ARABIC END OF AYAH |
1593 | array(0x070F, 0x070F), // SYRIAC ABBREVIATION MARK |
1594 | array(0x180E, 0x180E), // MONGOLIAN VOWEL SEPARATOR |
1595 | array(0x200C, 0x200C), // ZERO WIDTH NON-JOINER |
1596 | array(0x200D, 0x200D), // ZERO WIDTH JOINER |
1597 | array(0x2028, 0x2028), // LINE SEPARATOR |
1598 | array(0x2029, 0x2029), // PARAGRAPH SEPARATOR |
1599 | array(0x2060, 0x2060), // WORD JOINER |
1600 | array(0x2061, 0x2061), // FUNCTION APPLICATION |
1601 | array(0x2062, 0x2062), // INVISIBLE TIMES |
1602 | array(0x2063, 0x2063), // INVISIBLE SEPARATOR |
1603 | array(0x206A, 0x206F), // [CONTROL CHARACTERS] |
1604 | array(0xFEFF, 0xFEFF), // ZERO WIDTH NO-BREAK SPACE |
1605 | array(0xFFF9, 0xFFFC), // [CONTROL CHARACTERS] |
1606 | array(0x1D173, 0x1D17A), // [MUSICAL CONTROL CHARACTERS] |
1607 | // Table C.3 |
1608 | array(0xE000, 0xF8FF), // [PRIVATE USE, PLANE 0] |
1609 | array(0xF0000, 0xFFFFD), // [PRIVATE USE, PLANE 15] |
1610 | array(0x100000, 0x10FFFD), // [PRIVATE USE, PLANE 16] |
1611 | // Table C.4 |
1612 | array(0xFDD0, 0xFDEF), // [NONCHARACTER CODE POINTS] |
1613 | array(0xFFFE, 0xFFFF), // [NONCHARACTER CODE POINTS] |
1614 | array(0x1FFFE, 0x1FFFF), // [NONCHARACTER CODE POINTS] |
1615 | array(0x2FFFE, 0x2FFFF), // [NONCHARACTER CODE POINTS] |
1616 | array(0x3FFFE, 0x3FFFF), // [NONCHARACTER CODE POINTS] |
1617 | array(0x4FFFE, 0x4FFFF), // [NONCHARACTER CODE POINTS] |
1618 | array(0x5FFFE, 0x5FFFF), // [NONCHARACTER CODE POINTS] |
1619 | array(0x6FFFE, 0x6FFFF), // [NONCHARACTER CODE POINTS] |
1620 | array(0x7FFFE, 0x7FFFF), // [NONCHARACTER CODE POINTS] |
1621 | array(0x8FFFE, 0x8FFFF), // [NONCHARACTER CODE POINTS] |
1622 | array(0x9FFFE, 0x9FFFF), // [NONCHARACTER CODE POINTS] |
1623 | array(0xAFFFE, 0xAFFFF), // [NONCHARACTER CODE POINTS] |
1624 | array(0xBFFFE, 0xBFFFF), // [NONCHARACTER CODE POINTS] |
1625 | array(0xCFFFE, 0xCFFFF), // [NONCHARACTER CODE POINTS] |
1626 | array(0xDFFFE, 0xDFFFF), // [NONCHARACTER CODE POINTS] |
1627 | array(0xEFFFE, 0xEFFFF), // [NONCHARACTER CODE POINTS] |
1628 | array(0xFFFFE, 0xFFFFF), // [NONCHARACTER CODE POINTS] |
1629 | array(0x10FFFE, 0x10FFFF), // [NONCHARACTER CODE POINTS] |
1630 | // Table C.5 |
1631 | array(0xD800, 0xDFFF), // [SURROGATE CODES] |
1632 | // Table C.6 |
1633 | array(0xFFF9, 0xFFF9), // INTERLINEAR ANNOTATION ANCHOR |
1634 | array(0xFFFA, 0xFFFA), // INTERLINEAR ANNOTATION SEPARATOR |
1635 | array(0xFFFB, 0xFFFB), // INTERLINEAR ANNOTATION TERMINATOR |
1636 | array(0xFFFC, 0xFFFC), // OBJECT REPLACEMENT CHARACTER |
1637 | array(0xFFFD, 0xFFFD), // REPLACEMENT CHARACTER |
1638 | // Table C.7 |
1639 | array(0x2FF0, 0x2FFB), // [IDEOGRAPHIC DESCRIPTION CHARACTERS] |
1640 | // Table C.8 |
1641 | array(0x0340, 0x0340), // COMBINING GRAVE TONE MARK |
1642 | array(0x0341, 0x0341), // COMBINING ACUTE TONE MARK |
1643 | array(0x200E, 0x200E), // LEFT-TO-RIGHT MARK |
1644 | array(0x200F, 0x200F), // RIGHT-TO-LEFT MARK |
1645 | array(0x202A, 0x202A), // LEFT-TO-RIGHT EMBEDDING |
1646 | array(0x202B, 0x202B), // RIGHT-TO-LEFT EMBEDDING |
1647 | array(0x202C, 0x202C), // POP DIRECTIONAL FORMATTING |
1648 | array(0x202D, 0x202D), // LEFT-TO-RIGHT OVERRIDE |
1649 | array(0x202E, 0x202E), // RIGHT-TO-LEFT OVERRIDE |
1650 | array(0x206A, 0x206A), // INHIBIT SYMMETRIC SWAPPING |
1651 | array(0x206B, 0x206B), // ACTIVATE SYMMETRIC SWAPPING |
1652 | array(0x206C, 0x206C), // INHIBIT ARABIC FORM SHAPING |
1653 | array(0x206D, 0x206D), // ACTIVATE ARABIC FORM SHAPING |
1654 | array(0x206E, 0x206E), // NATIONAL DIGIT SHAPES |
1655 | array(0x206F, 0x206F), // NOMINAL DIGIT SHAPES |
1656 | // Table C.9 |
1657 | array(0xE0001, 0xE0001), // LANGUAGE TAG |
1658 | array(0xE0020, 0xE007F), // [TAGGING CHARACTERS] |
1659 | // RFC3920 |
1660 | array(0x22, 0x22), // " |
1661 | array(0x26, 0x26), // & |
1662 | array(0x27, 0x27), // ' |
1663 | array(0x2F, 0x2F), // / |
1664 | array(0x3A, 0x3A), // : |
1665 | array(0x3C, 0x3C), // < |
1666 | array(0x3E, 0x3E), // > |
1667 | array(0x40, 0x40) // @ |
1668 | ); |
1669 | |
1670 | $pos = 0; |
1671 | $result = true; |
1672 | |
1673 | while ($pos < strlen($username)) |
1674 | { |
1675 | $len = $uni = 0; |
1676 | for ($i = 0; $i <= 5; $i++) |
1677 | { |
1678 | if (ord($username[$pos]) >= $boundary[$i][0] && ord($username[$pos]) <= $boundary[$i][1]) |
1679 | { |
1680 | $len = $i + 1; |
1681 | $uni = (ord($username[$pos]) - $boundary[$i][0]) * pow(2, $i * 6); |
1682 | |
1683 | for ($k = 1; $k < $len; $k++) |
1684 | { |
1685 | $uni += (ord($username[$pos + $k]) - 128) * pow(2, ($i - $k) * 6); |
1686 | } |
1687 | |
1688 | break; |
1689 | } |
1690 | } |
1691 | |
1692 | if ($len == 0) |
1693 | { |
1694 | return 'WRONG_DATA'; |
1695 | } |
1696 | |
1697 | foreach ($prohibited as $pval) |
1698 | { |
1699 | if ($uni >= $pval[0] && $uni <= $pval[1]) |
1700 | { |
1701 | $result = false; |
1702 | break 2; |
1703 | } |
1704 | } |
1705 | |
1706 | $pos = $pos + $len; |
1707 | } |
1708 | |
1709 | if (!$result) |
1710 | { |
1711 | return 'WRONG_DATA'; |
1712 | } |
1713 | |
1714 | return false; |
1715 | } |
1716 | |
1717 | /** |
1718 | * Validate hex colour value |
1719 | * |
1720 | * @param string $colour The hex colour value |
1721 | * @param bool $optional Whether the colour value is optional. True if an empty |
1722 | * string will be accepted as correct input, false if not. |
1723 | * @return bool|string Error message if colour value is incorrect, false if it |
1724 | * fits the hex colour code |
1725 | */ |
1726 | function phpbb_validate_hex_colour($colour, $optional = false) |
1727 | { |
1728 | if ($colour === '') |
1729 | { |
1730 | return (($optional) ? false : 'WRONG_DATA'); |
1731 | } |
1732 | |
1733 | if (!preg_match('/^([0-9a-fA-F]{6}|[0-9a-fA-F]{3})$/', $colour)) |
1734 | { |
1735 | return 'WRONG_DATA'; |
1736 | } |
1737 | |
1738 | return false; |
1739 | } |
1740 | |
1741 | /** |
1742 | * Verifies whether a style ID corresponds to an active style. |
1743 | * |
1744 | * @param int $style_id The style_id of a style which should be checked if activated or not. |
1745 | * @return boolean |
1746 | */ |
1747 | function phpbb_style_is_active($style_id) |
1748 | { |
1749 | global $db; |
1750 | |
1751 | $sql = 'SELECT style_active |
1752 | FROM ' . STYLES_TABLE . ' |
1753 | WHERE style_id = '. (int) $style_id; |
1754 | $result = $db->sql_query($sql); |
1755 | |
1756 | $style_is_active = (bool) $db->sql_fetchfield('style_active'); |
1757 | $db->sql_freeresult($result); |
1758 | |
1759 | return $style_is_active; |
1760 | } |
1761 | |
1762 | /** |
1763 | * Remove avatar |
1764 | */ |
1765 | function avatar_delete($mode, $row, $clean_db = false) |
1766 | { |
1767 | global $phpbb_container; |
1768 | |
1769 | $storage = $phpbb_container->get('storage.avatar'); |
1770 | |
1771 | // Check if the users avatar is actually *not* a group avatar |
1772 | if ($mode == 'user') |
1773 | { |
1774 | if (strpos($row['user_avatar'], 'g') === 0 || (((int) $row['user_avatar'] !== 0) && ((int) $row['user_avatar'] !== (int) $row['user_id']))) |
1775 | { |
1776 | return false; |
1777 | } |
1778 | } |
1779 | |
1780 | if ($clean_db) |
1781 | { |
1782 | avatar_remove_db($row[$mode . '_avatar']); |
1783 | } |
1784 | $filename = get_avatar_filename($row[$mode . '_avatar']); |
1785 | |
1786 | try |
1787 | { |
1788 | $storage->delete($filename); |
1789 | |
1790 | return true; |
1791 | } |
1792 | catch (\phpbb\storage\exception\storage_exception $e) |
1793 | { |
1794 | // Fail is covered by return statement below |
1795 | } |
1796 | |
1797 | return false; |
1798 | } |
1799 | |
1800 | /** |
1801 | * Generates avatar filename from the database entry |
1802 | */ |
1803 | function get_avatar_filename($avatar_entry) |
1804 | { |
1805 | global $config; |
1806 | |
1807 | if ($avatar_entry[0] === 'g') |
1808 | { |
1809 | $avatar_group = true; |
1810 | $avatar_entry = substr($avatar_entry, 1); |
1811 | } |
1812 | else |
1813 | { |
1814 | $avatar_group = false; |
1815 | } |
1816 | $ext = substr(strrchr($avatar_entry, '.'), 1); |
1817 | $avatar_entry = intval($avatar_entry); |
1818 | return $config['avatar_salt'] . '_' . (($avatar_group) ? 'g' : '') . $avatar_entry . '.' . $ext; |
1819 | } |
1820 | |
1821 | /** |
1822 | * Returns an explanation string with maximum avatar settings |
1823 | * |
1824 | * @return string |
1825 | */ |
1826 | function phpbb_avatar_explanation_string() |
1827 | { |
1828 | global $config, $user; |
1829 | |
1830 | return $user->lang(($config['avatar_filesize'] == 0) ? 'AVATAR_EXPLAIN_NO_FILESIZE' : 'AVATAR_EXPLAIN', |
1831 | $user->lang('PIXELS', (int) $config['avatar_max_width']), |
1832 | $user->lang('PIXELS', (int) $config['avatar_max_height']), |
1833 | round($config['avatar_filesize'] / 1024)); |
1834 | } |
1835 | |
1836 | // |
1837 | // Usergroup functions |
1838 | // |
1839 | |
1840 | /** |
1841 | * Add or edit a group. If we're editing a group we only update user |
1842 | * parameters such as rank, etc. if they are changed |
1843 | */ |
1844 | function group_create(&$group_id, $type, $name, $desc, $group_attributes, $allow_desc_bbcode = false, $allow_desc_urls = false, $allow_desc_smilies = false) |
1845 | { |
1846 | global $db, $user, $phpbb_container, $phpbb_log; |
1847 | |
1848 | /** @var \phpbb\group\helper $group_helper */ |
1849 | $group_helper = $phpbb_container->get('group_helper'); |
1850 | |
1851 | $error = array(); |
1852 | |
1853 | // Attributes which also affect the users table |
1854 | $user_attribute_ary = array('group_colour', 'group_rank', 'group_avatar', 'group_avatar_type', 'group_avatar_width', 'group_avatar_height'); |
1855 | |
1856 | // Check data. Limit group name length. |
1857 | if (!utf8_strlen($name) || utf8_strlen($name) > 60) |
1858 | { |
1859 | $error[] = (!utf8_strlen($name)) ? $user->lang['GROUP_ERR_USERNAME'] : $user->lang['GROUP_ERR_USER_LONG']; |
1860 | } |
1861 | |
1862 | $err = group_validate_groupname($group_id, $name); |
1863 | if (!empty($err)) |
1864 | { |
1865 | $error[] = $user->lang[$err]; |
1866 | } |
1867 | |
1868 | if (!in_array($type, array(GROUP_OPEN, GROUP_CLOSED, GROUP_HIDDEN, GROUP_SPECIAL, GROUP_FREE))) |
1869 | { |
1870 | $error[] = $user->lang['GROUP_ERR_TYPE']; |
1871 | } |
1872 | |
1873 | $group_teampage = !empty($group_attributes['group_teampage']); |
1874 | unset($group_attributes['group_teampage']); |
1875 | |
1876 | if (!count($error)) |
1877 | { |
1878 | $current_legend = \phpbb\groupposition\legend::GROUP_DISABLED; |
1879 | $current_teampage = \phpbb\groupposition\teampage::GROUP_DISABLED; |
1880 | |
1881 | /* @var $legend \phpbb\groupposition\legend */ |
1882 | $legend = $phpbb_container->get('groupposition.legend'); |
1883 | |
1884 | /* @var $teampage \phpbb\groupposition\teampage */ |
1885 | $teampage = $phpbb_container->get('groupposition.teampage'); |
1886 | |
1887 | if ($group_id) |
1888 | { |
1889 | try |
1890 | { |
1891 | $current_legend = $legend->get_group_value($group_id); |
1892 | $current_teampage = $teampage->get_group_value($group_id); |
1893 | } |
1894 | catch (\phpbb\groupposition\exception $exception) |
1895 | { |
1896 | trigger_error($user->lang($exception->getMessage())); |
1897 | } |
1898 | } |
1899 | |
1900 | if (!empty($group_attributes['group_legend'])) |
1901 | { |
1902 | if (($group_id && ($current_legend == \phpbb\groupposition\legend::GROUP_DISABLED)) || !$group_id) |
1903 | { |
1904 | // Old group currently not in the legend or new group, add at the end. |
1905 | $group_attributes['group_legend'] = 1 + $legend->get_group_count(); |
1906 | } |
1907 | else |
1908 | { |
1909 | // Group stayes in the legend |
1910 | $group_attributes['group_legend'] = $current_legend; |
1911 | } |
1912 | } |
1913 | else if ($group_id && ($current_legend != \phpbb\groupposition\legend::GROUP_DISABLED)) |
1914 | { |
1915 | // Group is removed from the legend |
1916 | try |
1917 | { |
1918 | $legend->delete_group($group_id, true); |
1919 | } |
1920 | catch (\phpbb\groupposition\exception $exception) |
1921 | { |
1922 | trigger_error($user->lang($exception->getMessage())); |
1923 | } |
1924 | $group_attributes['group_legend'] = \phpbb\groupposition\legend::GROUP_DISABLED; |
1925 | } |
1926 | else |
1927 | { |
1928 | $group_attributes['group_legend'] = \phpbb\groupposition\legend::GROUP_DISABLED; |
1929 | } |
1930 | |
1931 | // Unset the objects, we don't need them anymore. |
1932 | unset($legend); |
1933 | |
1934 | $user_ary = array(); |
1935 | $sql_ary = array( |
1936 | 'group_name' => (string) $name, |
1937 | 'group_desc' => (string) $desc, |
1938 | 'group_desc_uid' => '', |
1939 | 'group_desc_bitfield' => '', |
1940 | 'group_type' => (int) $type, |
1941 | ); |
1942 | |
1943 | // Parse description |
1944 | if ($desc) |
1945 | { |
1946 | 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); |
1947 | } |
1948 | |
1949 | if (count($group_attributes)) |
1950 | { |
1951 | // Merge them with $sql_ary to properly update the group |
1952 | $sql_ary = array_merge($sql_ary, $group_attributes); |
1953 | } |
1954 | |
1955 | // Setting the log message before we set the group id (if group gets added) |
1956 | $log = ($group_id) ? 'LOG_GROUP_UPDATED' : 'LOG_GROUP_CREATED'; |
1957 | |
1958 | if ($group_id) |
1959 | { |
1960 | $sql = 'SELECT user_id |
1961 | FROM ' . USERS_TABLE . ' |
1962 | WHERE group_id = ' . $group_id; |
1963 | $result = $db->sql_query($sql); |
1964 | |
1965 | while ($row = $db->sql_fetchrow($result)) |
1966 | { |
1967 | $user_ary[] = $row['user_id']; |
1968 | } |
1969 | $db->sql_freeresult($result); |
1970 | |
1971 | if (isset($sql_ary['group_avatar'])) |
1972 | { |
1973 | remove_default_avatar($group_id, $user_ary); |
1974 | } |
1975 | |
1976 | if (isset($sql_ary['group_rank'])) |
1977 | { |
1978 | remove_default_rank($group_id, $user_ary); |
1979 | } |
1980 | |
1981 | $sql = 'UPDATE ' . GROUPS_TABLE . ' |
1982 | SET ' . $db->sql_build_array('UPDATE', $sql_ary) . " |
1983 | WHERE group_id = $group_id"; |
1984 | $db->sql_query($sql); |
1985 | |
1986 | // Since we may update the name too, we need to do this on other tables too... |
1987 | $sql = 'UPDATE ' . MODERATOR_CACHE_TABLE . " |
1988 | SET group_name = '" . $db->sql_escape($sql_ary['group_name']) . "' |
1989 | WHERE group_id = $group_id"; |
1990 | $db->sql_query($sql); |
1991 | |
1992 | // One special case is the group skip auth setting. If this was changed we need to purge permissions for this group |
1993 | if (isset($group_attributes['group_skip_auth'])) |
1994 | { |
1995 | // Get users within this group... |
1996 | $sql = 'SELECT user_id |
1997 | FROM ' . USER_GROUP_TABLE . ' |
1998 | WHERE group_id = ' . $group_id . ' |
1999 | AND user_pending = 0'; |
2000 | $result = $db->sql_query($sql); |
2001 | |
2002 | $user_id_ary = array(); |
2003 | while ($row = $db->sql_fetchrow($result)) |
2004 | { |
2005 | $user_id_ary[] = $row['user_id']; |
2006 | } |
2007 | $db->sql_freeresult($result); |
2008 | |
2009 | if (!empty($user_id_ary)) |
2010 | { |
2011 | global $auth; |
2012 | |
2013 | // Clear permissions cache of relevant users |
2014 | $auth->acl_clear_prefetch($user_id_ary); |
2015 | } |
2016 | } |
2017 | } |
2018 | else |
2019 | { |
2020 | $sql = 'INSERT INTO ' . GROUPS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary); |
2021 | $db->sql_query($sql); |
2022 | } |
2023 | |
2024 | // Remove the group from the teampage, only if unselected and we are editing a group, |
2025 | // which is currently displayed. |
2026 | if (!$group_teampage && $group_id && $current_teampage != \phpbb\groupposition\teampage::GROUP_DISABLED) |
2027 | { |
2028 | try |
2029 | { |
2030 | $teampage->delete_group($group_id); |
2031 | } |
2032 | catch (\phpbb\groupposition\exception $exception) |
2033 | { |
2034 | trigger_error($user->lang($exception->getMessage())); |
2035 | } |
2036 | } |
2037 | |
2038 | if (!$group_id) |
2039 | { |
2040 | $group_id = $db->sql_nextid(); |
2041 | |
2042 | if (isset($sql_ary['group_avatar_type']) && $sql_ary['group_avatar_type'] == 'avatar.driver.upload') |
2043 | { |
2044 | group_correct_avatar($group_id, $sql_ary['group_avatar']); |
2045 | } |
2046 | } |
2047 | |
2048 | try |
2049 | { |
2050 | if ($group_teampage && $current_teampage == \phpbb\groupposition\teampage::GROUP_DISABLED) |
2051 | { |
2052 | $teampage->add_group($group_id); |
2053 | } |
2054 | |
2055 | if ($group_teampage) |
2056 | { |
2057 | if ($current_teampage == \phpbb\groupposition\teampage::GROUP_DISABLED) |
2058 | { |
2059 | $teampage->add_group($group_id); |
2060 | } |
2061 | } |
2062 | else if ($group_id && ($current_teampage != \phpbb\groupposition\teampage::GROUP_DISABLED)) |
2063 | { |
2064 | $teampage->delete_group($group_id); |
2065 | } |
2066 | } |
2067 | catch (\phpbb\groupposition\exception $exception) |
2068 | { |
2069 | trigger_error($user->lang($exception->getMessage())); |
2070 | } |
2071 | unset($teampage); |
2072 | |
2073 | // Set user attributes |
2074 | $sql_ary = array(); |
2075 | if (count($group_attributes)) |
2076 | { |
2077 | // Go through the user attributes array, check if a group attribute matches it and then set it. ;) |
2078 | foreach ($user_attribute_ary as $attribute) |
2079 | { |
2080 | if (!isset($group_attributes[$attribute])) |
2081 | { |
2082 | continue; |
2083 | } |
2084 | |
2085 | // If we are about to set an avatar, we will not overwrite user avatars if no group avatar is set... |
2086 | if (strpos($attribute, 'group_avatar') === 0 && !$group_attributes[$attribute]) |
2087 | { |
2088 | continue; |
2089 | } |
2090 | |
2091 | $sql_ary[$attribute] = $group_attributes[$attribute]; |
2092 | } |
2093 | } |
2094 | |
2095 | if (count($sql_ary) && count($user_ary)) |
2096 | { |
2097 | group_set_user_default($group_id, $user_ary, $sql_ary); |
2098 | } |
2099 | |
2100 | $name = $group_helper->get_name($name); |
2101 | $phpbb_log->add('admin', $user->data['user_id'], $user->ip, $log, false, array($name)); |
2102 | |
2103 | group_update_listings($group_id); |
2104 | } |
2105 | |
2106 | return (count($error)) ? $error : false; |
2107 | } |
2108 | |
2109 | |
2110 | /** |
2111 | * Changes a group avatar's filename to conform to the naming scheme |
2112 | */ |
2113 | function group_correct_avatar($group_id, $old_entry) |
2114 | { |
2115 | global $config, $db, $phpbb_container; |
2116 | |
2117 | $storage = $phpbb_container->get('storage.avatar'); |
2118 | |
2119 | $group_id = (int) $group_id; |
2120 | $ext = substr(strrchr($old_entry, '.'), 1); |
2121 | $old_filename = get_avatar_filename($old_entry); |
2122 | $new_filename = $config['avatar_salt'] . "_g$group_id.$ext"; |
2123 | $new_entry = 'g' . $group_id . '_' . substr(time(), -5) . ".$ext"; |
2124 | |
2125 | try |
2126 | { |
2127 | $storage->rename($old_filename, $new_filename); |
2128 | |
2129 | $sql = 'UPDATE ' . GROUPS_TABLE . ' |
2130 | SET group_avatar = \'' . $db->sql_escape($new_entry) . "' |
2131 | WHERE group_id = $group_id"; |
2132 | $db->sql_query($sql); |
2133 | } |
2134 | catch (\phpbb\storage\exception\storage_exception $e) |
2135 | { |
2136 | // If rename fail, dont execute the query |
2137 | } |
2138 | } |
2139 | |
2140 | |
2141 | /** |
2142 | * Remove avatar also for users not having the group as default |
2143 | */ |
2144 | function avatar_remove_db($avatar_name) |
2145 | { |
2146 | global $db; |
2147 | |
2148 | $sql = 'UPDATE ' . USERS_TABLE . " |
2149 | SET user_avatar = '', |
2150 | user_avatar_type = '' |
2151 | WHERE user_avatar = '" . $db->sql_escape($avatar_name) . '\''; |
2152 | $db->sql_query($sql); |
2153 | } |
2154 | |
2155 | |
2156 | /** |
2157 | * Group Delete |
2158 | */ |
2159 | function group_delete($group_id, $group_name = false) |
2160 | { |
2161 | global $db, $cache, $auth, $user, $phpbb_root_path, $phpEx, $phpbb_dispatcher, $phpbb_container, $phpbb_log; |
2162 | |
2163 | if (!$group_name) |
2164 | { |
2165 | $group_name = get_group_name($group_id); |
2166 | } |
2167 | |
2168 | $start = 0; |
2169 | |
2170 | do |
2171 | { |
2172 | $user_id_ary = $username_ary = array(); |
2173 | |
2174 | // Batch query for group members, call group_user_del |
2175 | $sql = 'SELECT u.user_id, u.username |
2176 | FROM ' . USER_GROUP_TABLE . ' ug, ' . USERS_TABLE . " u |
2177 | WHERE ug.group_id = $group_id |
2178 | AND u.user_id = ug.user_id"; |
2179 | $result = $db->sql_query_limit($sql, 200, $start); |
2180 | |
2181 | if ($row = $db->sql_fetchrow($result)) |
2182 | { |
2183 | do |
2184 | { |
2185 | $user_id_ary[] = $row['user_id']; |
2186 | $username_ary[] = $row['username']; |
2187 | |
2188 | $start++; |
2189 | } |
2190 | while ($row = $db->sql_fetchrow($result)); |
2191 | |
2192 | group_user_del($group_id, $user_id_ary, $username_ary, $group_name); |
2193 | } |
2194 | else |
2195 | { |
2196 | $start = 0; |
2197 | } |
2198 | $db->sql_freeresult($result); |
2199 | } |
2200 | while ($start); |
2201 | |
2202 | // Delete group from legend and teampage |
2203 | try |
2204 | { |
2205 | /* @var $legend \phpbb\groupposition\legend */ |
2206 | $legend = $phpbb_container->get('groupposition.legend'); |
2207 | $legend->delete_group($group_id); |
2208 | unset($legend); |
2209 | } |
2210 | catch (\phpbb\groupposition\exception $exception) |
2211 | { |
2212 | // The group we want to delete does not exist. |
2213 | // No reason to worry, we just continue the deleting process. |
2214 | //trigger_error($user->lang($exception->getMessage())); |
2215 | } |
2216 | |
2217 | try |
2218 | { |
2219 | /* @var $teampage \phpbb\groupposition\teampage */ |
2220 | $teampage = $phpbb_container->get('groupposition.teampage'); |
2221 | $teampage->delete_group($group_id); |
2222 | unset($teampage); |
2223 | } |
2224 | catch (\phpbb\groupposition\exception $exception) |
2225 | { |
2226 | // The group we want to delete does not exist. |
2227 | // No reason to worry, we just continue the deleting process. |
2228 | //trigger_error($user->lang($exception->getMessage())); |
2229 | } |
2230 | |
2231 | // Delete group |
2232 | $sql = 'DELETE FROM ' . GROUPS_TABLE . " |
2233 | WHERE group_id = $group_id"; |
2234 | $db->sql_query($sql); |
2235 | |
2236 | // Delete auth entries from the groups table |
2237 | $sql = 'DELETE FROM ' . ACL_GROUPS_TABLE . " |
2238 | WHERE group_id = $group_id"; |
2239 | $db->sql_query($sql); |
2240 | |
2241 | /** |
2242 | * Event after a group is deleted |
2243 | * |
2244 | * @event core.delete_group_after |
2245 | * @var int group_id ID of the deleted group |
2246 | * @var string group_name Name of the deleted group |
2247 | * @since 3.1.0-a1 |
2248 | */ |
2249 | $vars = array('group_id', 'group_name'); |
2250 | extract($phpbb_dispatcher->trigger_event('core.delete_group_after', compact($vars))); |
2251 | |
2252 | // Re-cache moderators |
2253 | if (!function_exists('phpbb_cache_moderators')) |
2254 | { |
2255 | include($phpbb_root_path . 'includes/functions_admin.' . $phpEx); |
2256 | } |
2257 | |
2258 | phpbb_cache_moderators($db, $phpbb_container->get('dbal.tools'), $cache, $auth); |
2259 | |
2260 | $phpbb_log->add('admin', $user->data['user_id'], $user->ip, 'LOG_GROUP_DELETE', false, array($group_name)); |
2261 | |
2262 | // Return false - no error |
2263 | return false; |
2264 | } |
2265 | |
2266 | /** |
2267 | * Add user(s) to group |
2268 | * |
2269 | * @return string|false false if no errors occurred, else the user lang string for the relevant error, for example 'NO_USER' |
2270 | */ |
2271 | 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) |
2272 | { |
2273 | global $db, $auth, $user, $phpbb_container, $phpbb_log, $phpbb_dispatcher; |
2274 | |
2275 | // We need both username and user_id info |
2276 | $result = user_get_id_name($user_id_ary, $username_ary); |
2277 | |
2278 | if (empty($user_id_ary) || $result !== false) |
2279 | { |
2280 | return 'NO_USER'; |
2281 | } |
2282 | |
2283 | // Because the item that gets passed into the previous function is unset, the reference is lost and our original |
2284 | // array is retained - so we know there's a problem if there's a different number of ids to usernames now. |
2285 | if (count($user_id_ary) != count($username_ary)) |
2286 | { |
2287 | return 'GROUP_USERS_INVALID'; |
2288 | } |
2289 | |
2290 | // Remove users who are already members of this group |
2291 | $sql = 'SELECT user_id, group_leader |
2292 | FROM ' . USER_GROUP_TABLE . ' |
2293 | WHERE ' . $db->sql_in_set('user_id', $user_id_ary) . " |
2294 | AND group_id = $group_id"; |
2295 | $result = $db->sql_query($sql); |
2296 | |
2297 | $add_id_ary = $update_id_ary = array(); |
2298 | while ($row = $db->sql_fetchrow($result)) |
2299 | { |
2300 | $add_id_ary[] = (int) $row['user_id']; |
2301 | |
2302 | if ($leader && !$row['group_leader']) |
2303 | { |
2304 | $update_id_ary[] = (int) $row['user_id']; |
2305 | } |
2306 | } |
2307 | $db->sql_freeresult($result); |
2308 | |
2309 | // Do all the users exist in this group? |
2310 | $add_id_ary = array_diff($user_id_ary, $add_id_ary); |
2311 | |
2312 | // If we have no users |
2313 | if (!count($add_id_ary) && !count($update_id_ary)) |
2314 | { |
2315 | return 'GROUP_USERS_EXIST'; |
2316 | } |
2317 | |
2318 | $db->sql_transaction('begin'); |
2319 | |
2320 | // Insert the new users |
2321 | if (count($add_id_ary)) |
2322 | { |
2323 | $sql_ary = array(); |
2324 | |
2325 | foreach ($add_id_ary as $user_id) |
2326 | { |
2327 | $sql_ary[] = array( |
2328 | 'user_id' => (int) $user_id, |
2329 | 'group_id' => (int) $group_id, |
2330 | 'group_leader' => (int) $leader, |
2331 | 'user_pending' => (int) $pending, |
2332 | ); |
2333 | } |
2334 | |
2335 | $db->sql_multi_insert(USER_GROUP_TABLE, $sql_ary); |
2336 | } |
2337 | |
2338 | if (count($update_id_ary)) |
2339 | { |
2340 | $sql = 'UPDATE ' . USER_GROUP_TABLE . ' |
2341 | SET group_leader = 1 |
2342 | WHERE ' . $db->sql_in_set('user_id', $update_id_ary) . " |
2343 | AND group_id = $group_id"; |
2344 | $db->sql_query($sql); |
2345 | } |
2346 | |
2347 | if ($default) |
2348 | { |
2349 | group_user_attributes('default', $group_id, $user_id_ary, false, $group_name, $group_attributes); |
2350 | } |
2351 | |
2352 | $db->sql_transaction('commit'); |
2353 | |
2354 | // Clear permissions cache of relevant users |
2355 | $auth->acl_clear_prefetch($user_id_ary); |
2356 | |
2357 | /** |
2358 | * Event after users are added to a group |
2359 | * |
2360 | * @event core.group_add_user_after |
2361 | * @var int group_id ID of the group to which users are added |
2362 | * @var string group_name Name of the group |
2363 | * @var array user_id_ary IDs of the users which are added |
2364 | * @var array username_ary names of the users which are added |
2365 | * @var int pending Pending setting, 1 if user(s) added are pending |
2366 | * @since 3.1.7-RC1 |
2367 | */ |
2368 | $vars = array( |
2369 | 'group_id', |
2370 | 'group_name', |
2371 | 'user_id_ary', |
2372 | 'username_ary', |
2373 | 'pending', |
2374 | ); |
2375 | extract($phpbb_dispatcher->trigger_event('core.group_add_user_after', compact($vars))); |
2376 | |
2377 | if (!$group_name) |
2378 | { |
2379 | $group_name = get_group_name($group_id); |
2380 | } |
2381 | |
2382 | $log = ($leader) ? 'LOG_MODS_ADDED' : (($pending) ? 'LOG_USERS_PENDING' : 'LOG_USERS_ADDED'); |
2383 | |
2384 | $phpbb_log->add('admin', $user->data['user_id'], $user->ip, $log, false, array($group_name, implode(', ', $username_ary))); |
2385 | |
2386 | group_update_listings($group_id); |
2387 | |
2388 | if ($pending) |
2389 | { |
2390 | /* @var $phpbb_notifications \phpbb\notification\manager */ |
2391 | $phpbb_notifications = $phpbb_container->get('notification_manager'); |
2392 | |
2393 | foreach ($add_id_ary as $user_id) |
2394 | { |
2395 | $phpbb_notifications->add_notifications('notification.type.group_request', array( |
2396 | 'group_id' => $group_id, |
2397 | 'user_id' => $user_id, |
2398 | 'group_name' => $group_name, |
2399 | )); |
2400 | } |
2401 | } |
2402 | |
2403 | // Return false - no error |
2404 | return false; |
2405 | } |
2406 | |
2407 | /** |
2408 | * Remove a user/s from a given group. When we remove users we update their |
2409 | * default group_id. We do this by examining which "special" groups they belong |
2410 | * to. The selection is made based on a reasonable priority system |
2411 | * |
2412 | * @return false if no errors occurred, else the user lang string for the relevant error, for example 'NO_USER' |
2413 | */ |
2414 | function group_user_del($group_id, $user_id_ary = false, $username_ary = false, $group_name = false, $log_action = true) |
2415 | { |
2416 | global $db, $auth, $config, $user, $phpbb_dispatcher, $phpbb_container, $phpbb_log; |
2417 | |
2418 | if ($config['coppa_enable']) |
2419 | { |
2420 | $group_order = array('ADMINISTRATORS', 'GLOBAL_MODERATORS', 'NEWLY_REGISTERED', 'REGISTERED_COPPA', 'REGISTERED', 'BOTS', 'GUESTS'); |
2421 | } |
2422 | else |
2423 | { |
2424 | $group_order = array('ADMINISTRATORS', 'GLOBAL_MODERATORS', 'NEWLY_REGISTERED', 'REGISTERED', 'BOTS', 'GUESTS'); |
2425 | } |
2426 | |
2427 | // We need both username and user_id info |
2428 | $result = user_get_id_name($user_id_ary, $username_ary); |
2429 | |
2430 | if (empty($user_id_ary) || $result !== false) |
2431 | { |
2432 | return 'NO_USER'; |
2433 | } |
2434 | |
2435 | $sql = 'SELECT * |
2436 | FROM ' . GROUPS_TABLE . ' |
2437 | WHERE ' . $db->sql_in_set('group_name', $group_order); |
2438 | $result = $db->sql_query($sql); |
2439 | |
2440 | $group_order_id = $special_group_data = array(); |
2441 | while ($row = $db->sql_fetchrow($result)) |
2442 | { |
2443 | $group_order_id[$row['group_name']] = $row['group_id']; |
2444 | |
2445 | $special_group_data[$row['group_id']] = array( |
2446 | 'group_colour' => $row['group_colour'], |
2447 | 'group_rank' => $row['group_rank'], |
2448 | ); |
2449 | |
2450 | // Only set the group avatar if one is defined... |
2451 | if ($row['group_avatar']) |
2452 | { |
2453 | $special_group_data[$row['group_id']] = array_merge($special_group_data[$row['group_id']], array( |
2454 | 'group_avatar' => $row['group_avatar'], |
2455 | 'group_avatar_type' => $row['group_avatar_type'], |
2456 | 'group_avatar_width' => $row['group_avatar_width'], |
2457 | 'group_avatar_height' => $row['group_avatar_height']) |
2458 | ); |
2459 | } |
2460 | } |
2461 | $db->sql_freeresult($result); |
2462 | |
2463 | // 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 |
2464 | $sql = 'SELECT user_id, group_id |
2465 | FROM ' . USERS_TABLE . ' |
2466 | WHERE ' . $db->sql_in_set('user_id', $user_id_ary); |
2467 | $result = $db->sql_query($sql); |
2468 | |
2469 | $default_groups = array(); |
2470 | while ($row = $db->sql_fetchrow($result)) |
2471 | { |
2472 | $default_groups[$row['user_id']] = $row['group_id']; |
2473 | } |
2474 | $db->sql_freeresult($result); |
2475 | |
2476 | // What special group memberships exist for these users? |
2477 | $sql = 'SELECT g.group_id, g.group_name, ug.user_id |
2478 | FROM ' . USER_GROUP_TABLE . ' ug, ' . GROUPS_TABLE . ' g |
2479 | WHERE ' . $db->sql_in_set('ug.user_id', $user_id_ary) . " |
2480 | AND g.group_id = ug.group_id |
2481 | AND g.group_id <> $group_id |
2482 | AND g.group_type = " . GROUP_SPECIAL . ' |
2483 | ORDER BY ug.user_id, g.group_id'; |
2484 | $result = $db->sql_query($sql); |
2485 | |
2486 | $temp_ary = array(); |
2487 | while ($row = $db->sql_fetchrow($result)) |
2488 | { |
2489 | if ($default_groups[$row['user_id']] == $group_id && (!isset($temp_ary[$row['user_id']]) || $group_order_id[$row['group_name']] < $temp_ary[$row['user_id']])) |
2490 | { |
2491 | $temp_ary[$row['user_id']] = $row['group_id']; |
2492 | } |
2493 | } |
2494 | $db->sql_freeresult($result); |
2495 | |
2496 | // sql_where_ary holds the new default groups and their users |
2497 | $sql_where_ary = array(); |
2498 | foreach ($temp_ary as $uid => $gid) |
2499 | { |
2500 | $sql_where_ary[$gid][] = $uid; |
2501 | } |
2502 | unset($temp_ary); |
2503 | |
2504 | foreach ($special_group_data as $gid => $default_data_ary) |
2505 | { |
2506 | if (isset($sql_where_ary[$gid]) && count($sql_where_ary[$gid])) |
2507 | { |
2508 | remove_default_rank($group_id, $sql_where_ary[$gid]); |
2509 | remove_default_avatar($group_id, $sql_where_ary[$gid]); |
2510 | group_set_user_default($gid, $sql_where_ary[$gid], $default_data_ary); |
2511 | } |
2512 | } |
2513 | unset($special_group_data); |
2514 | |
2515 | /** |
2516 | * Event before users are removed from a group |
2517 | * |
2518 | * @event core.group_delete_user_before |
2519 | * @var int group_id ID of the group from which users are deleted |
2520 | * @var string group_name Name of the group |
2521 | * @var array user_id_ary IDs of the users which are removed |
2522 | * @var array username_ary names of the users which are removed |
2523 | * @since 3.1.0-a1 |
2524 | */ |
2525 | $vars = array('group_id', 'group_name', 'user_id_ary', 'username_ary'); |
2526 | extract($phpbb_dispatcher->trigger_event('core.group_delete_user_before', compact($vars))); |
2527 | |
2528 | $sql = 'DELETE FROM ' . USER_GROUP_TABLE . " |
2529 | WHERE group_id = $group_id |
2530 | AND " . $db->sql_in_set('user_id', $user_id_ary); |
2531 | $db->sql_query($sql); |
2532 | |
2533 | // Clear permissions cache of relevant users |
2534 | $auth->acl_clear_prefetch($user_id_ary); |
2535 | |
2536 | /** |
2537 | * Event after users are removed from a group |
2538 | * |
2539 | * @event core.group_delete_user_after |
2540 | * @var int group_id ID of the group from which users are deleted |
2541 | * @var string group_name Name of the group |
2542 | * @var array user_id_ary IDs of the users which are removed |
2543 | * @var array username_ary names of the users which are removed |
2544 | * @since 3.1.7-RC1 |
2545 | */ |
2546 | $vars = array('group_id', 'group_name', 'user_id_ary', 'username_ary'); |
2547 | extract($phpbb_dispatcher->trigger_event('core.group_delete_user_after', compact($vars))); |
2548 | |
2549 | if ($log_action) |
2550 | { |
2551 | if (!$group_name) |
2552 | { |
2553 | $group_name = get_group_name($group_id); |
2554 | } |
2555 | |
2556 | $log = 'LOG_GROUP_REMOVE'; |
2557 | |
2558 | if ($group_name) |
2559 | { |
2560 | $phpbb_log->add('admin', $user->data['user_id'], $user->ip, $log, false, array($group_name, implode(', ', $username_ary))); |
2561 | } |
2562 | } |
2563 | |
2564 | group_update_listings($group_id); |
2565 | |
2566 | /* @var $phpbb_notifications \phpbb\notification\manager */ |
2567 | $phpbb_notifications = $phpbb_container->get('notification_manager'); |
2568 | |
2569 | $phpbb_notifications->delete_notifications('notification.type.group_request', $user_id_ary, $group_id); |
2570 | |
2571 | // Return false - no error |
2572 | return false; |
2573 | } |
2574 | |
2575 | |
2576 | /** |
2577 | * Removes the group avatar of the default group from the users in user_ids who have that group as default. |
2578 | */ |
2579 | function remove_default_avatar($group_id, $user_ids) |
2580 | { |
2581 | global $db; |
2582 | |
2583 | if (!is_array($user_ids)) |
2584 | { |
2585 | $user_ids = array($user_ids); |
2586 | } |
2587 | if (empty($user_ids)) |
2588 | { |
2589 | return false; |
2590 | } |
2591 | |
2592 | $user_ids = array_map('intval', $user_ids); |
2593 | |
2594 | $sql = 'SELECT * |
2595 | FROM ' . GROUPS_TABLE . ' |
2596 | WHERE group_id = ' . (int) $group_id; |
2597 | $result = $db->sql_query($sql); |
2598 | if (!$row = $db->sql_fetchrow($result)) |
2599 | { |
2600 | $db->sql_freeresult($result); |
2601 | return false; |
2602 | } |
2603 | $db->sql_freeresult($result); |
2604 | |
2605 | $sql = 'UPDATE ' . USERS_TABLE . " |
2606 | SET user_avatar = '', |
2607 | user_avatar_type = '', |
2608 | user_avatar_width = 0, |
2609 | user_avatar_height = 0 |
2610 | WHERE group_id = " . (int) $group_id . " |
2611 | AND user_avatar = '" . $db->sql_escape($row['group_avatar']) . "' |
2612 | AND " . $db->sql_in_set('user_id', $user_ids); |
2613 | |
2614 | $db->sql_query($sql); |
2615 | } |
2616 | |
2617 | /** |
2618 | * Removes the group rank of the default group from the users in user_ids who have that group as default. |
2619 | * @return bool true if successful, false if not |
2620 | */ |
2621 | function remove_default_rank($group_id, $user_ids) |
2622 | { |
2623 | global $db; |
2624 | |
2625 | if (!is_array($user_ids)) |
2626 | { |
2627 | $user_ids = array($user_ids); |
2628 | } |
2629 | if (empty($user_ids)) |
2630 | { |
2631 | return false; |
2632 | } |
2633 | |
2634 | $user_ids = array_map('intval', $user_ids); |
2635 | |
2636 | $sql = 'SELECT * |
2637 | FROM ' . GROUPS_TABLE . ' |
2638 | WHERE group_id = ' . (int) $group_id; |
2639 | $result = $db->sql_query($sql); |
2640 | if (!$row = $db->sql_fetchrow($result)) |
2641 | { |
2642 | $db->sql_freeresult($result); |
2643 | return false; |
2644 | } |
2645 | $db->sql_freeresult($result); |
2646 | |
2647 | $sql = 'UPDATE ' . USERS_TABLE . ' |
2648 | SET user_rank = 0 |
2649 | WHERE group_id = ' . (int) $group_id . ' |
2650 | AND user_rank <> 0 |
2651 | AND user_rank = ' . (int) $row['group_rank'] . ' |
2652 | AND ' . $db->sql_in_set('user_id', $user_ids); |
2653 | $db->sql_query($sql); |
2654 | |
2655 | return true; |
2656 | } |
2657 | |
2658 | /** |
2659 | * This is used to promote (to leader), demote or set as default a member/s |
2660 | */ |
2661 | function group_user_attributes($action, $group_id, $user_id_ary = false, $username_ary = false, $group_name = false, $group_attributes = false) |
2662 | { |
2663 | global $db, $auth, $user, $phpbb_container, $phpbb_log, $phpbb_dispatcher; |
2664 | |
2665 | // We need both username and user_id info |
2666 | $result = user_get_id_name($user_id_ary, $username_ary); |
2667 | |
2668 | if (empty($user_id_ary) || $result !== false) |
2669 | { |
2670 | return 'NO_USERS'; |
2671 | } |
2672 | |
2673 | if (!$group_name) |
2674 | { |
2675 | $group_name = get_group_name($group_id); |
2676 | } |
2677 | |
2678 | switch ($action) |
2679 | { |
2680 | case 'demote': |
2681 | case 'promote': |
2682 | |
2683 | $sql = 'SELECT user_id |
2684 | FROM ' . USER_GROUP_TABLE . " |
2685 | WHERE group_id = $group_id |
2686 | AND user_pending = 1 |
2687 | AND " . $db->sql_in_set('user_id', $user_id_ary); |
2688 | $result = $db->sql_query_limit($sql, 1); |
2689 | $not_empty = ($db->sql_fetchrow($result)); |
2690 | $db->sql_freeresult($result); |
2691 | if ($not_empty) |
2692 | { |
2693 | return 'NO_VALID_USERS'; |
2694 | } |
2695 | |
2696 | $sql = 'UPDATE ' . USER_GROUP_TABLE . ' |
2697 | SET group_leader = ' . (($action == 'promote') ? 1 : 0) . " |
2698 | WHERE group_id = $group_id |
2699 | AND user_pending = 0 |
2700 | AND " . $db->sql_in_set('user_id', $user_id_ary); |
2701 | $db->sql_query($sql); |
2702 | |
2703 | $log = ($action == 'promote') ? 'LOG_GROUP_PROMOTED' : 'LOG_GROUP_DEMOTED'; |
2704 | break; |
2705 | |
2706 | case 'approve': |
2707 | // Make sure we only approve those which are pending ;) |
2708 | $sql = 'SELECT u.user_id, u.user_email, u.username, u.username_clean, u.user_notify_type, u.user_jabber, u.user_lang |
2709 | FROM ' . USERS_TABLE . ' u, ' . USER_GROUP_TABLE . ' ug |
2710 | WHERE ug.group_id = ' . $group_id . ' |
2711 | AND ug.user_pending = 1 |
2712 | AND ug.user_id = u.user_id |
2713 | AND ' . $db->sql_in_set('ug.user_id', $user_id_ary); |
2714 | $result = $db->sql_query($sql); |
2715 | |
2716 | $user_id_ary = array(); |
2717 | while ($row = $db->sql_fetchrow($result)) |
2718 | { |
2719 | $user_id_ary[] = $row['user_id']; |
2720 | } |
2721 | $db->sql_freeresult($result); |
2722 | |
2723 | if (!count($user_id_ary)) |
2724 | { |
2725 | return false; |
2726 | } |
2727 | |
2728 | $sql = 'UPDATE ' . USER_GROUP_TABLE . " |
2729 | SET user_pending = 0 |
2730 | WHERE group_id = $group_id |
2731 | AND " . $db->sql_in_set('user_id', $user_id_ary); |
2732 | $db->sql_query($sql); |
2733 | |
2734 | /* @var $phpbb_notifications \phpbb\notification\manager */ |
2735 | $phpbb_notifications = $phpbb_container->get('notification_manager'); |
2736 | |
2737 | $phpbb_notifications->add_notifications('notification.type.group_request_approved', array( |
2738 | 'user_ids' => $user_id_ary, |
2739 | 'group_id' => $group_id, |
2740 | 'group_name' => $group_name, |
2741 | )); |
2742 | $phpbb_notifications->delete_notifications('notification.type.group_request', $user_id_ary, $group_id); |
2743 | |
2744 | $log = 'LOG_USERS_APPROVED'; |
2745 | break; |
2746 | |
2747 | case 'default': |
2748 | // We only set default group for approved members of the group |
2749 | $sql = 'SELECT user_id |
2750 | FROM ' . USER_GROUP_TABLE . " |
2751 | WHERE group_id = $group_id |
2752 | AND user_pending = 0 |
2753 | AND " . $db->sql_in_set('user_id', $user_id_ary); |
2754 | $result = $db->sql_query($sql); |
2755 | |
2756 | $user_id_ary = $username_ary = array(); |
2757 | while ($row = $db->sql_fetchrow($result)) |
2758 | { |
2759 | $user_id_ary[] = $row['user_id']; |
2760 | } |
2761 | $db->sql_freeresult($result); |
2762 | |
2763 | $result = user_get_id_name($user_id_ary, $username_ary); |
2764 | if (!count($user_id_ary) || $result !== false) |
2765 | { |
2766 | return 'NO_USERS'; |
2767 | } |
2768 | |
2769 | $sql = 'SELECT user_id, group_id |
2770 | FROM ' . USERS_TABLE . ' |
2771 | WHERE ' . $db->sql_in_set('user_id', $user_id_ary, false, true); |
2772 | $result = $db->sql_query($sql); |
2773 | |
2774 | $groups = array(); |
2775 | while ($row = $db->sql_fetchrow($result)) |
2776 | { |
2777 | if (!isset($groups[$row['group_id']])) |
2778 | { |
2779 | $groups[$row['group_id']] = array(); |
2780 | } |
2781 | $groups[$row['group_id']][] = $row['user_id']; |
2782 | } |
2783 | $db->sql_freeresult($result); |
2784 | |
2785 | foreach ($groups as $gid => $uids) |
2786 | { |
2787 | remove_default_rank($gid, $uids); |
2788 | remove_default_avatar($gid, $uids); |
2789 | } |
2790 | group_set_user_default($group_id, $user_id_ary, $group_attributes); |
2791 | $log = 'LOG_GROUP_DEFAULTS'; |
2792 | break; |
2793 | } |
2794 | |
2795 | /** |
2796 | * Event to perform additional actions on setting user group attributes |
2797 | * |
2798 | * @event core.user_set_group_attributes |
2799 | * @var int group_id ID of the group |
2800 | * @var string group_name Name of the group |
2801 | * @var array user_id_ary IDs of the users to set group attributes |
2802 | * @var array username_ary Names of the users to set group attributes |
2803 | * @var array group_attributes Group attributes which were changed |
2804 | * @var string action Action to perform over the group members |
2805 | * @since 3.1.10-RC1 |
2806 | */ |
2807 | $vars = array( |
2808 | 'group_id', |
2809 | 'group_name', |
2810 | 'user_id_ary', |
2811 | 'username_ary', |
2812 | 'group_attributes', |
2813 | 'action', |
2814 | ); |
2815 | extract($phpbb_dispatcher->trigger_event('core.user_set_group_attributes', compact($vars))); |
2816 | |
2817 | // Clear permissions cache of relevant users |
2818 | $auth->acl_clear_prefetch($user_id_ary); |
2819 | |
2820 | $phpbb_log->add('admin', $user->data['user_id'], $user->ip, $log, false, array($group_name, implode(', ', $username_ary))); |
2821 | |
2822 | group_update_listings($group_id); |
2823 | |
2824 | return false; |
2825 | } |
2826 | |
2827 | /** |
2828 | * A small version of validate_username to check for a group name's existence. To be called directly. |
2829 | */ |
2830 | function group_validate_groupname($group_id, $group_name) |
2831 | { |
2832 | global $db; |
2833 | |
2834 | $group_name = utf8_clean_string($group_name); |
2835 | |
2836 | if (!empty($group_id)) |
2837 | { |
2838 | $sql = 'SELECT group_name |
2839 | FROM ' . GROUPS_TABLE . ' |
2840 | WHERE group_id = ' . (int) $group_id; |
2841 | $result = $db->sql_query($sql); |
2842 | $row = $db->sql_fetchrow($result); |
2843 | $db->sql_freeresult($result); |
2844 | |
2845 | if (!$row) |
2846 | { |
2847 | return false; |
2848 | } |
2849 | |
2850 | $allowed_groupname = utf8_clean_string($row['group_name']); |
2851 | |
2852 | if ($allowed_groupname == $group_name) |
2853 | { |
2854 | return false; |
2855 | } |
2856 | } |
2857 | |
2858 | $sql = 'SELECT group_name |
2859 | FROM ' . GROUPS_TABLE . " |
2860 | WHERE LOWER(group_name) = '" . $db->sql_escape(utf8_strtolower($group_name)) . "'"; |
2861 | $result = $db->sql_query($sql); |
2862 | $row = $db->sql_fetchrow($result); |
2863 | $db->sql_freeresult($result); |
2864 | |
2865 | if ($row) |
2866 | { |
2867 | return 'GROUP_NAME_TAKEN'; |
2868 | } |
2869 | |
2870 | return false; |
2871 | } |
2872 | |
2873 | /** |
2874 | * Set users default group |
2875 | * |
2876 | * @access private |
2877 | */ |
2878 | function group_set_user_default($group_id, $user_id_ary, $group_attributes = false, $update_listing = false) |
2879 | { |
2880 | global $config, $phpbb_container, $db, $phpbb_dispatcher; |
2881 | |
2882 | if (empty($user_id_ary)) |
2883 | { |
2884 | return; |
2885 | } |
2886 | |
2887 | $attribute_ary = array( |
2888 | 'group_colour' => 'string', |
2889 | 'group_rank' => 'int', |
2890 | 'group_avatar' => 'string', |
2891 | 'group_avatar_type' => 'string', |
2892 | 'group_avatar_width' => 'int', |
2893 | 'group_avatar_height' => 'int', |
2894 | ); |
2895 | |
2896 | $sql_ary = array( |
2897 | 'group_id' => $group_id |
2898 | ); |
2899 | |
2900 | // Were group attributes passed to the function? If not we need to obtain them |
2901 | if ($group_attributes === false) |
2902 | { |
2903 | $sql = 'SELECT ' . implode(', ', array_keys($attribute_ary)) . ' |
2904 | FROM ' . GROUPS_TABLE . " |
2905 | WHERE group_id = $group_id"; |
2906 | $result = $db->sql_query($sql); |
2907 | $group_attributes = $db->sql_fetchrow($result); |
2908 | $db->sql_freeresult($result); |
2909 | } |
2910 | |
2911 | foreach ($attribute_ary as $attribute => $type) |
2912 | { |
2913 | if (isset($group_attributes[$attribute])) |
2914 | { |
2915 | // 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 |
2916 | if ((strpos($attribute, 'group_avatar') === 0 || strpos($attribute, 'group_rank') === 0) && !$group_attributes[$attribute]) |
2917 | { |
2918 | continue; |
2919 | } |
2920 | |
2921 | settype($group_attributes[$attribute], $type); |
2922 | $sql_ary[str_replace('group_', 'user_', $attribute)] = $group_attributes[$attribute]; |
2923 | } |
2924 | } |
2925 | |
2926 | $updated_sql_ary = $sql_ary; |
2927 | |
2928 | // Before we update the user attributes, we will update the rank for users that don't have a custom rank |
2929 | if (isset($sql_ary['user_rank'])) |
2930 | { |
2931 | $sql = 'UPDATE ' . USERS_TABLE . ' |
2932 | SET ' . $db->sql_build_array('UPDATE', array('user_rank' => $sql_ary['user_rank'])) . ' |
2933 | WHERE user_rank = 0 |
2934 | AND ' . $db->sql_in_set('user_id', $user_id_ary); |
2935 | $db->sql_query($sql); |
2936 | unset($sql_ary['user_rank']); |
2937 | } |
2938 | |
2939 | // Before we update the user attributes, we will update the avatar for users that don't have a custom avatar |
2940 | $avatar_options = array('user_avatar', 'user_avatar_type', 'user_avatar_height', 'user_avatar_width'); |
2941 | |
2942 | if (isset($sql_ary['user_avatar'])) |
2943 | { |
2944 | $avatar_sql_ary = array(); |
2945 | foreach ($avatar_options as $avatar_option) |
2946 | { |
2947 | if (isset($sql_ary[$avatar_option])) |
2948 | { |
2949 | $avatar_sql_ary[$avatar_option] = $sql_ary[$avatar_option]; |
2950 | } |
2951 | } |
2952 | |
2953 | $sql = 'UPDATE ' . USERS_TABLE . ' |
2954 | SET ' . $db->sql_build_array('UPDATE', $avatar_sql_ary) . " |
2955 | WHERE user_avatar = '' |
2956 | AND " . $db->sql_in_set('user_id', $user_id_ary); |
2957 | $db->sql_query($sql); |
2958 | } |
2959 | |
2960 | // Remove the avatar options, as we already updated them |
2961 | foreach ($avatar_options as $avatar_option) |
2962 | { |
2963 | unset($sql_ary[$avatar_option]); |
2964 | } |
2965 | |
2966 | if (!empty($sql_ary)) |
2967 | { |
2968 | $sql = 'UPDATE ' . USERS_TABLE . ' |
2969 | SET ' . $db->sql_build_array('UPDATE', $sql_ary) . ' |
2970 | WHERE ' . $db->sql_in_set('user_id', $user_id_ary); |
2971 | $db->sql_query($sql); |
2972 | } |
2973 | |
2974 | if (isset($sql_ary['user_colour'])) |
2975 | { |
2976 | // Update any cached colour information for these users |
2977 | $sql = 'UPDATE ' . FORUMS_TABLE . " |
2978 | SET forum_last_poster_colour = '" . $db->sql_escape($sql_ary['user_colour']) . "' |
2979 | WHERE " . $db->sql_in_set('forum_last_poster_id', $user_id_ary); |
2980 | $db->sql_query($sql); |
2981 | |
2982 | $sql = 'UPDATE ' . TOPICS_TABLE . " |
2983 | SET topic_first_poster_colour = '" . $db->sql_escape($sql_ary['user_colour']) . "' |
2984 | WHERE " . $db->sql_in_set('topic_poster', $user_id_ary); |
2985 | $db->sql_query($sql); |
2986 | |
2987 | $sql = 'UPDATE ' . TOPICS_TABLE . " |
2988 | SET topic_last_poster_colour = '" . $db->sql_escape($sql_ary['user_colour']) . "' |
2989 | WHERE " . $db->sql_in_set('topic_last_poster_id', $user_id_ary); |
2990 | $db->sql_query($sql); |
2991 | |
2992 | if (in_array($config['newest_user_id'], $user_id_ary)) |
2993 | { |
2994 | $config->set('newest_user_colour', $sql_ary['user_colour'], false); |
2995 | } |
2996 | } |
2997 | |
2998 | // Make all values available for the event |
2999 | $sql_ary = $updated_sql_ary; |
3000 | |
3001 | /** |
3002 | * Event when the default group is set for an array of users |
3003 | * |
3004 | * @event core.user_set_default_group |
3005 | * @var int group_id ID of the group |
3006 | * @var array user_id_ary IDs of the users |
3007 | * @var array group_attributes Group attributes which were changed |
3008 | * @var array update_listing Update the list of moderators and foes |
3009 | * @var array sql_ary User attributes which were changed |
3010 | * @since 3.1.0-a1 |
3011 | */ |
3012 | $vars = array('group_id', 'user_id_ary', 'group_attributes', 'update_listing', 'sql_ary'); |
3013 | extract($phpbb_dispatcher->trigger_event('core.user_set_default_group', compact($vars))); |
3014 | |
3015 | if ($update_listing) |
3016 | { |
3017 | group_update_listings($group_id); |
3018 | } |
3019 | |
3020 | // Because some tables/caches use usercolour-specific data we need to purge this here. |
3021 | $phpbb_container->get('cache.driver')->destroy('sql', MODERATOR_CACHE_TABLE); |
3022 | } |
3023 | |
3024 | /** |
3025 | * Get group name |
3026 | */ |
3027 | function get_group_name($group_id) |
3028 | { |
3029 | global $db, $phpbb_container; |
3030 | |
3031 | $sql = 'SELECT group_name, group_type |
3032 | FROM ' . GROUPS_TABLE . ' |
3033 | WHERE group_id = ' . (int) $group_id; |
3034 | $result = $db->sql_query($sql); |
3035 | $row = $db->sql_fetchrow($result); |
3036 | $db->sql_freeresult($result); |
3037 | |
3038 | if (!$row) |
3039 | { |
3040 | return ''; |
3041 | } |
3042 | |
3043 | /** @var \phpbb\group\helper $group_helper */ |
3044 | $group_helper = $phpbb_container->get('group_helper'); |
3045 | |
3046 | return $group_helper->get_name($row['group_name']); |
3047 | } |
3048 | |
3049 | /** |
3050 | * Obtain either the members of a specified group, the groups the specified user is subscribed to |
3051 | * or checking if a specified user is in a specified group. This function does not return pending memberships. |
3052 | * |
3053 | * Note: Never use this more than once... first group your users/groups |
3054 | */ |
3055 | function group_memberships($group_id_ary = false, $user_id_ary = false, $return_bool = false) |
3056 | { |
3057 | global $db; |
3058 | |
3059 | if (!$group_id_ary && !$user_id_ary) |
3060 | { |
3061 | return true; |
3062 | } |
3063 | |
3064 | if ($user_id_ary) |
3065 | { |
3066 | $user_id_ary = (!is_array($user_id_ary)) ? array($user_id_ary) : $user_id_ary; |
3067 | } |
3068 | |
3069 | if ($group_id_ary) |
3070 | { |
3071 | $group_id_ary = (!is_array($group_id_ary)) ? array($group_id_ary) : $group_id_ary; |
3072 | } |
3073 | |
3074 | $sql = 'SELECT ug.*, u.username, u.username_clean, u.user_email |
3075 | FROM ' . USER_GROUP_TABLE . ' ug, ' . USERS_TABLE . ' u |
3076 | WHERE ug.user_id = u.user_id |
3077 | AND ug.user_pending = 0 AND '; |
3078 | |
3079 | if ($group_id_ary) |
3080 | { |
3081 | $sql .= ' ' . $db->sql_in_set('ug.group_id', $group_id_ary); |
3082 | } |
3083 | |
3084 | if ($user_id_ary) |
3085 | { |
3086 | $sql .= ($group_id_ary) ? ' AND ' : ' '; |
3087 | $sql .= $db->sql_in_set('ug.user_id', $user_id_ary); |
3088 | } |
3089 | |
3090 | $result = ($return_bool) ? $db->sql_query_limit($sql, 1) : $db->sql_query($sql); |
3091 | |
3092 | $row = $db->sql_fetchrow($result); |
3093 | |
3094 | if ($return_bool) |
3095 | { |
3096 | $db->sql_freeresult($result); |
3097 | return ($row) ? true : false; |
3098 | } |
3099 | |
3100 | if (!$row) |
3101 | { |
3102 | return false; |
3103 | } |
3104 | |
3105 | $return = array(); |
3106 | |
3107 | do |
3108 | { |
3109 | $return[] = $row; |
3110 | } |
3111 | while ($row = $db->sql_fetchrow($result)); |
3112 | |
3113 | $db->sql_freeresult($result); |
3114 | |
3115 | return $return; |
3116 | } |
3117 | |
3118 | /** |
3119 | * Re-cache moderators and foes if group has a_ or m_ permissions |
3120 | */ |
3121 | function group_update_listings($group_id) |
3122 | { |
3123 | global $db, $cache, $auth; |
3124 | |
3125 | $hold_ary = $auth->acl_group_raw_data($group_id, array('a_', 'm_')); |
3126 | |
3127 | if (empty($hold_ary)) |
3128 | { |
3129 | return; |
3130 | } |
3131 | |
3132 | $mod_permissions = $admin_permissions = false; |
3133 | |
3134 | foreach ($hold_ary as $g_id => $forum_ary) |
3135 | { |
3136 | foreach ($forum_ary as $forum_id => $auth_ary) |
3137 | { |
3138 | foreach ($auth_ary as $auth_option => $setting) |
3139 | { |
3140 | if ($mod_permissions && $admin_permissions) |
3141 | { |
3142 | break 3; |
3143 | } |
3144 | |
3145 | if ($setting != ACL_YES) |
3146 | { |
3147 | continue; |
3148 | } |
3149 | |
3150 | if ($auth_option == 'm_') |
3151 | { |
3152 | $mod_permissions = true; |
3153 | } |
3154 | |
3155 | if ($auth_option == 'a_') |
3156 | { |
3157 | $admin_permissions = true; |
3158 | } |
3159 | } |
3160 | } |
3161 | } |
3162 | |
3163 | if ($mod_permissions) |
3164 | { |
3165 | if (!function_exists('phpbb_cache_moderators')) |
3166 | { |
3167 | global $phpbb_root_path, $phpEx; |
3168 | include($phpbb_root_path . 'includes/functions_admin.' . $phpEx); |
3169 | } |
3170 | |
3171 | global $phpbb_container; |
3172 | |
3173 | phpbb_cache_moderators($db, $phpbb_container->get('dbal.tools'), $cache, $auth); |
3174 | } |
3175 | |
3176 | if ($mod_permissions || $admin_permissions) |
3177 | { |
3178 | if (!function_exists('phpbb_update_foes')) |
3179 | { |
3180 | global $phpbb_root_path, $phpEx; |
3181 | include($phpbb_root_path . 'includes/functions_admin.' . $phpEx); |
3182 | } |
3183 | phpbb_update_foes($db, $auth, array($group_id)); |
3184 | } |
3185 | } |
3186 | |
3187 | |
3188 | |
3189 | /** |
3190 | * Funtion to make a user leave the NEWLY_REGISTERED system group. |
3191 | * @access public |
3192 | * @param int $user_id The id of the user to remove from the group |
3193 | * @param mixed $user_data The id of the user to remove from the group |
3194 | */ |
3195 | function remove_newly_registered($user_id, $user_data = false) |
3196 | { |
3197 | global $db; |
3198 | |
3199 | if ($user_data === false) |
3200 | { |
3201 | $sql = 'SELECT * |
3202 | FROM ' . USERS_TABLE . ' |
3203 | WHERE user_id = ' . $user_id; |
3204 | $result = $db->sql_query($sql); |
3205 | $user_row = $db->sql_fetchrow($result); |
3206 | $db->sql_freeresult($result); |
3207 | |
3208 | if (!$user_row) |
3209 | { |
3210 | return false; |
3211 | } |
3212 | else |
3213 | { |
3214 | $user_data = $user_row; |
3215 | } |
3216 | } |
3217 | |
3218 | $sql = 'SELECT group_id |
3219 | FROM ' . GROUPS_TABLE . " |
3220 | WHERE group_name = 'NEWLY_REGISTERED' |
3221 | AND group_type = " . GROUP_SPECIAL; |
3222 | $result = $db->sql_query($sql); |
3223 | $group_id = (int) $db->sql_fetchfield('group_id'); |
3224 | $db->sql_freeresult($result); |
3225 | |
3226 | if (!$group_id) |
3227 | { |
3228 | return false; |
3229 | } |
3230 | |
3231 | // We need to call group_user_del here, because this function makes sure everything is correctly changed. |
3232 | // Force function to not log the removal of users from newly registered users group |
3233 | group_user_del($group_id, $user_id, false, false, false); |
3234 | |
3235 | // Set user_new to 0 to let this not be triggered again |
3236 | $sql = 'UPDATE ' . USERS_TABLE . ' |
3237 | SET user_new = 0 |
3238 | WHERE user_id = ' . $user_id; |
3239 | $db->sql_query($sql); |
3240 | |
3241 | // The new users group was the users default group? |
3242 | if ($user_data['group_id'] == $group_id) |
3243 | { |
3244 | // Which group is now the users default one? |
3245 | $sql = 'SELECT group_id |
3246 | FROM ' . USERS_TABLE . ' |
3247 | WHERE user_id = ' . $user_id; |
3248 | $result = $db->sql_query($sql); |
3249 | $user_data['group_id'] = $db->sql_fetchfield('group_id'); |
3250 | $db->sql_freeresult($result); |
3251 | } |
3252 | |
3253 | return $user_data['group_id']; |
3254 | } |
3255 | |
3256 | /** |
3257 | * Gets user ids of currently banned registered users. |
3258 | * |
3259 | * @param array $user_ids Array of users' ids to check for banning, |
3260 | * leave empty to get complete list of banned ids |
3261 | * @param bool|int $ban_end Bool True to get users currently banned |
3262 | * Bool False to only get permanently banned users |
3263 | * Int Unix timestamp to get users banned until that time |
3264 | * @return array Array of banned users' ids if any, empty array otherwise |
3265 | */ |
3266 | function phpbb_get_banned_user_ids($user_ids = array(), $ban_end = true) |
3267 | { |
3268 | global $phpbb_container; |
3269 | |
3270 | /** @var \phpbb\ban\manager $ban_manager */ |
3271 | $ban_manager = $phpbb_container->get('ban.manager'); |
3272 | $banned_users = $ban_manager->get_banned_users(); |
3273 | |
3274 | if ($ban_end === false) |
3275 | { |
3276 | $banned_users = array_filter($banned_users, function ($end) { |
3277 | return $end <= 0; |
3278 | }); |
3279 | } |
3280 | else if ($ban_end !== true) |
3281 | { |
3282 | $banned_users = array_filter($banned_users, function ($end) use ($ban_end) { |
3283 | return $end <= 0 || $end > (int) $ban_end; |
3284 | }); |
3285 | } |
3286 | else |
3287 | { |
3288 | $banned_users = array_filter($banned_users, function ($end) { |
3289 | return $end <= 0 || $end > time(); |
3290 | }); |
3291 | } |
3292 | |
3293 | $result_array = []; |
3294 | foreach ($banned_users as $user_id => $_) |
3295 | { |
3296 | if (count($user_ids) && !in_array($user_id, $user_ids)) |
3297 | { |
3298 | continue; |
3299 | } |
3300 | |
3301 | $result_array[$user_id] = $user_id; |
3302 | } |
3303 | |
3304 | return $result_array; |
3305 | } |
3306 | |
3307 | /** |
3308 | * Function for assigning a template var if the zebra module got included |
3309 | */ |
3310 | function phpbb_module_zebra($mode, &$module_row) |
3311 | { |
3312 | global $template; |
3313 | |
3314 | $template->assign_var('S_ZEBRA_ENABLED', true); |
3315 | |
3316 | if ($mode == 'friends') |
3317 | { |
3318 | $template->assign_var('S_ZEBRA_FRIENDS_ENABLED', true); |
3319 | } |
3320 | |
3321 | if ($mode == 'foes') |
3322 | { |
3323 | $template->assign_var('S_ZEBRA_FOES_ENABLED', true); |
3324 | } |
3325 | } |