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