1 | <?php |
||
2 | |||
3 | /** |
||
4 | * This file has the very important job of ensuring forum security. |
||
5 | * This task includes banning and permissions, namely. |
||
6 | * |
||
7 | * Simple Machines Forum (SMF) |
||
8 | * |
||
9 | * @package SMF |
||
10 | * @author Simple Machines https://www.simplemachines.org |
||
11 | * @copyright 2022 Simple Machines and individual contributors |
||
12 | * @license https://www.simplemachines.org/about/smf/license.php BSD |
||
13 | * |
||
14 | * @version 2.1.0 |
||
15 | */ |
||
16 | |||
17 | if (!defined('SMF')) |
||
18 | die('No direct access...'); |
||
19 | |||
20 | /** |
||
21 | * Check if the user is who he/she says he is |
||
22 | * Makes sure the user is who they claim to be by requiring a password to be typed in every hour. |
||
23 | * Is turned on and off by the securityDisable setting. |
||
24 | * Uses the adminLogin() function of Subs-Auth.php if they need to login, which saves all request (post and get) data. |
||
25 | * |
||
26 | * @param string $type What type of session this is |
||
27 | * @param string $force When true, require a password even if we normally wouldn't |
||
28 | * @return void|string Returns 'session_verify_fail' if verification failed |
||
29 | */ |
||
30 | function validateSession($type = 'admin', $force = false) |
||
31 | { |
||
32 | global $modSettings, $sourcedir, $user_info; |
||
33 | |||
34 | // We don't care if the option is off, because Guests should NEVER get past here. |
||
35 | is_not_guest(); |
||
36 | |||
37 | // Validate what type of session check this is. |
||
38 | $types = array(); |
||
39 | call_integration_hook('integrate_validateSession', array(&$types)); |
||
40 | $type = in_array($type, $types) || $type == 'moderate' ? $type : 'admin'; |
||
41 | |||
42 | // If we're using XML give an additional ten minutes grace as an admin can't log on in XML mode. |
||
43 | $refreshTime = isset($_GET['xml']) ? 4200 : 3600; |
||
44 | |||
45 | if (empty($force)) |
||
46 | { |
||
47 | // Is the security option off? |
||
48 | if (!empty($modSettings['securityDisable' . ($type != 'admin' ? '_' . $type : '')])) |
||
49 | return; |
||
50 | |||
51 | // Or are they already logged in?, Moderator or admin session is need for this area |
||
52 | if ((!empty($_SESSION[$type . '_time']) && $_SESSION[$type . '_time'] + $refreshTime >= time()) || (!empty($_SESSION['admin_time']) && $_SESSION['admin_time'] + $refreshTime >= time())) |
||
53 | return; |
||
54 | } |
||
55 | |||
56 | require_once($sourcedir . '/Subs-Auth.php'); |
||
57 | |||
58 | // Posting the password... check it. |
||
59 | if (isset($_POST[$type . '_pass'])) |
||
60 | { |
||
61 | // Check to ensure we're forcing SSL for authentication |
||
62 | if (!empty($modSettings['force_ssl']) && empty($maintenance) && !httpsOn()) |
||
63 | fatal_lang_error('login_ssl_required'); |
||
64 | |||
65 | checkSession(); |
||
66 | |||
67 | $good_password = in_array(true, call_integration_hook('integrate_verify_password', array($user_info['username'], $_POST[$type . '_pass'], false)), true); |
||
68 | |||
69 | // Password correct? |
||
70 | if ($good_password || hash_verify_password($user_info['username'], $_POST[$type . '_pass'], $user_info['passwd'])) |
||
71 | { |
||
72 | $_SESSION[$type . '_time'] = time(); |
||
73 | unset($_SESSION['request_referer']); |
||
74 | return; |
||
75 | } |
||
76 | } |
||
77 | |||
78 | // Better be sure to remember the real referer |
||
79 | if (empty($_SESSION['request_referer'])) |
||
80 | $_SESSION['request_referer'] = isset($_SERVER['HTTP_REFERER']) ? @parse_iri($_SERVER['HTTP_REFERER']) : array(); |
||
81 | elseif (empty($_POST)) |
||
82 | unset($_SESSION['request_referer']); |
||
83 | |||
84 | // Need to type in a password for that, man. |
||
85 | if (!isset($_GET['xml'])) |
||
86 | adminLogin($type); |
||
87 | else |
||
88 | return 'session_verify_fail'; |
||
89 | } |
||
90 | |||
91 | /** |
||
92 | * Require a user who is logged in. (not a guest.) |
||
93 | * Checks if the user is currently a guest, and if so asks them to login with a message telling them why. |
||
94 | * Message is what to tell them when asking them to login. |
||
95 | * |
||
96 | * @param string $message The message to display to the guest |
||
97 | */ |
||
98 | function is_not_guest($message = '') |
||
99 | { |
||
100 | global $user_info, $txt, $context, $scripturl, $modSettings; |
||
101 | |||
102 | // Luckily, this person isn't a guest. |
||
103 | if (!$user_info['is_guest']) |
||
104 | return; |
||
105 | |||
106 | // Log what they were trying to do didn't work) |
||
107 | if (!empty($modSettings['who_enabled'])) |
||
108 | $_GET['error'] = 'guest_login'; |
||
109 | writeLog(true); |
||
110 | |||
111 | // Just die. |
||
112 | if (isset($_REQUEST['xml'])) |
||
113 | obExit(false); |
||
114 | |||
115 | // Attempt to detect if they came from dlattach. |
||
116 | if (SMF != 'SSI' && empty($context['theme_loaded'])) |
||
0 ignored issues
–
show
introduced
by
![]() |
|||
117 | loadTheme(); |
||
118 | |||
119 | // Never redirect to an attachment |
||
120 | if (strpos($_SERVER['REQUEST_URL'], 'dlattach') === false) |
||
121 | $_SESSION['login_url'] = $_SERVER['REQUEST_URL']; |
||
122 | |||
123 | // Load the Login template and language file. |
||
124 | loadLanguage('Login'); |
||
125 | |||
126 | // Apparently we're not in a position to handle this now. Let's go to a safer location for now. |
||
127 | if (empty($context['template_layers'])) |
||
128 | { |
||
129 | $_SESSION['login_url'] = $scripturl . '?' . $_SERVER['QUERY_STRING']; |
||
130 | redirectexit('action=login'); |
||
131 | } |
||
132 | else |
||
133 | { |
||
134 | loadTemplate('Login'); |
||
135 | $context['sub_template'] = 'kick_guest'; |
||
136 | $context['robot_no_index'] = true; |
||
137 | } |
||
138 | |||
139 | // Use the kick_guest sub template... |
||
140 | $context['kick_message'] = $message; |
||
141 | $context['page_title'] = $txt['login']; |
||
142 | |||
143 | obExit(); |
||
144 | |||
145 | // We should never get to this point, but if we did we wouldn't know the user isn't a guest. |
||
146 | trigger_error('No direct access...', E_USER_ERROR); |
||
147 | } |
||
148 | |||
149 | /** |
||
150 | * Do banning related stuff. (ie. disallow access....) |
||
151 | * Checks if the user is banned, and if so dies with an error. |
||
152 | * Caches this information for optimization purposes. |
||
153 | * |
||
154 | * @param bool $forceCheck Whether to force a recheck |
||
155 | */ |
||
156 | function is_not_banned($forceCheck = false) |
||
157 | { |
||
158 | global $txt, $modSettings, $context, $user_info; |
||
159 | global $sourcedir, $cookiename, $user_settings, $smcFunc; |
||
160 | |||
161 | // You cannot be banned if you are an admin - doesn't help if you log out. |
||
162 | if ($user_info['is_admin']) |
||
163 | return; |
||
164 | |||
165 | // Only check the ban every so often. (to reduce load.) |
||
166 | if ($forceCheck || !isset($_SESSION['ban']) || empty($modSettings['banLastUpdated']) || ($_SESSION['ban']['last_checked'] < $modSettings['banLastUpdated']) || $_SESSION['ban']['id_member'] != $user_info['id'] || $_SESSION['ban']['ip'] != $user_info['ip'] || $_SESSION['ban']['ip2'] != $user_info['ip2'] || (isset($user_info['email'], $_SESSION['ban']['email']) && $_SESSION['ban']['email'] != $user_info['email'])) |
||
167 | { |
||
168 | // Innocent until proven guilty. (but we know you are! :P) |
||
169 | $_SESSION['ban'] = array( |
||
170 | 'last_checked' => time(), |
||
171 | 'id_member' => $user_info['id'], |
||
172 | 'ip' => $user_info['ip'], |
||
173 | 'ip2' => $user_info['ip2'], |
||
174 | 'email' => $user_info['email'], |
||
175 | ); |
||
176 | |||
177 | $ban_query = array(); |
||
178 | $ban_query_vars = array('current_time' => time()); |
||
179 | $flag_is_activated = false; |
||
180 | |||
181 | // Check both IP addresses. |
||
182 | foreach (array('ip', 'ip2') as $ip_number) |
||
183 | { |
||
184 | if ($ip_number == 'ip2' && $user_info['ip2'] == $user_info['ip']) |
||
185 | continue; |
||
186 | $ban_query[] = ' {inet:' . $ip_number . '} BETWEEN bi.ip_low and bi.ip_high'; |
||
187 | $ban_query_vars[$ip_number] = $user_info[$ip_number]; |
||
188 | // IP was valid, maybe there's also a hostname... |
||
189 | if (empty($modSettings['disableHostnameLookup']) && $user_info[$ip_number] != 'unknown') |
||
190 | { |
||
191 | $hostname = host_from_ip($user_info[$ip_number]); |
||
192 | if (strlen($hostname) > 0) |
||
193 | { |
||
194 | $ban_query[] = '({string:hostname' . $ip_number . '} LIKE bi.hostname)'; |
||
195 | $ban_query_vars['hostname' . $ip_number] = $hostname; |
||
196 | } |
||
197 | } |
||
198 | } |
||
199 | |||
200 | // Is their email address banned? |
||
201 | if (strlen($user_info['email']) != 0) |
||
202 | { |
||
203 | $ban_query[] = '({string:email} LIKE bi.email_address)'; |
||
204 | $ban_query_vars['email'] = $user_info['email']; |
||
205 | } |
||
206 | |||
207 | // How about this user? |
||
208 | if (!$user_info['is_guest'] && !empty($user_info['id'])) |
||
209 | { |
||
210 | $ban_query[] = 'bi.id_member = {int:id_member}'; |
||
211 | $ban_query_vars['id_member'] = $user_info['id']; |
||
212 | } |
||
213 | |||
214 | // Check the ban, if there's information. |
||
215 | if (!empty($ban_query)) |
||
216 | { |
||
217 | $restrictions = array( |
||
218 | 'cannot_access', |
||
219 | 'cannot_login', |
||
220 | 'cannot_post', |
||
221 | 'cannot_register', |
||
222 | ); |
||
223 | $request = $smcFunc['db_query']('', ' |
||
224 | SELECT bi.id_ban, bi.email_address, bi.id_member, bg.cannot_access, bg.cannot_register, |
||
225 | bg.cannot_post, bg.cannot_login, bg.reason, COALESCE(bg.expire_time, 0) AS expire_time |
||
226 | FROM {db_prefix}ban_items AS bi |
||
227 | INNER JOIN {db_prefix}ban_groups AS bg ON (bg.id_ban_group = bi.id_ban_group AND (bg.expire_time IS NULL OR bg.expire_time > {int:current_time})) |
||
228 | WHERE |
||
229 | (' . implode(' OR ', $ban_query) . ')', |
||
230 | $ban_query_vars |
||
231 | ); |
||
232 | // Store every type of ban that applies to you in your session. |
||
233 | while ($row = $smcFunc['db_fetch_assoc']($request)) |
||
234 | { |
||
235 | foreach ($restrictions as $restriction) |
||
236 | if (!empty($row[$restriction])) |
||
237 | { |
||
238 | $_SESSION['ban'][$restriction]['reason'] = $row['reason']; |
||
239 | $_SESSION['ban'][$restriction]['ids'][] = $row['id_ban']; |
||
240 | if (!isset($_SESSION['ban']['expire_time']) || ($_SESSION['ban']['expire_time'] != 0 && ($row['expire_time'] == 0 || $row['expire_time'] > $_SESSION['ban']['expire_time']))) |
||
241 | $_SESSION['ban']['expire_time'] = $row['expire_time']; |
||
242 | |||
243 | if (!$user_info['is_guest'] && $restriction == 'cannot_access' && ($row['id_member'] == $user_info['id'] || $row['email_address'] == $user_info['email'])) |
||
244 | $flag_is_activated = true; |
||
245 | } |
||
246 | } |
||
247 | $smcFunc['db_free_result']($request); |
||
248 | } |
||
249 | |||
250 | // Mark the cannot_access and cannot_post bans as being 'hit'. |
||
251 | if (isset($_SESSION['ban']['cannot_access']) || isset($_SESSION['ban']['cannot_post']) || isset($_SESSION['ban']['cannot_login'])) |
||
252 | log_ban(array_merge(isset($_SESSION['ban']['cannot_access']) ? $_SESSION['ban']['cannot_access']['ids'] : array(), isset($_SESSION['ban']['cannot_post']) ? $_SESSION['ban']['cannot_post']['ids'] : array(), isset($_SESSION['ban']['cannot_login']) ? $_SESSION['ban']['cannot_login']['ids'] : array())); |
||
253 | |||
254 | // If for whatever reason the is_activated flag seems wrong, do a little work to clear it up. |
||
255 | if ($user_info['id'] && (($user_settings['is_activated'] >= 10 && !$flag_is_activated) |
||
256 | || ($user_settings['is_activated'] < 10 && $flag_is_activated))) |
||
257 | { |
||
258 | require_once($sourcedir . '/ManageBans.php'); |
||
259 | updateBanMembers(); |
||
260 | } |
||
261 | } |
||
262 | |||
263 | // Hey, I know you! You're ehm... |
||
264 | if (!isset($_SESSION['ban']['cannot_access']) && !empty($_COOKIE[$cookiename . '_'])) |
||
265 | { |
||
266 | $bans = explode(',', $_COOKIE[$cookiename . '_']); |
||
267 | foreach ($bans as $key => $value) |
||
268 | $bans[$key] = (int) $value; |
||
269 | $request = $smcFunc['db_query']('', ' |
||
270 | SELECT bi.id_ban, bg.reason, COALESCE(bg.expire_time, 0) AS expire_time |
||
271 | FROM {db_prefix}ban_items AS bi |
||
272 | INNER JOIN {db_prefix}ban_groups AS bg ON (bg.id_ban_group = bi.id_ban_group) |
||
273 | WHERE bi.id_ban IN ({array_int:ban_list}) |
||
274 | AND (bg.expire_time IS NULL OR bg.expire_time > {int:current_time}) |
||
275 | AND bg.cannot_access = {int:cannot_access} |
||
276 | LIMIT {int:limit}', |
||
277 | array( |
||
278 | 'cannot_access' => 1, |
||
279 | 'ban_list' => $bans, |
||
280 | 'current_time' => time(), |
||
281 | 'limit' => count($bans), |
||
282 | ) |
||
283 | ); |
||
284 | while ($row = $smcFunc['db_fetch_assoc']($request)) |
||
285 | { |
||
286 | $_SESSION['ban']['cannot_access']['ids'][] = $row['id_ban']; |
||
287 | $_SESSION['ban']['cannot_access']['reason'] = $row['reason']; |
||
288 | $_SESSION['ban']['expire_time'] = $row['expire_time']; |
||
289 | } |
||
290 | $smcFunc['db_free_result']($request); |
||
291 | |||
292 | // My mistake. Next time better. |
||
293 | if (!isset($_SESSION['ban']['cannot_access'])) |
||
294 | { |
||
295 | require_once($sourcedir . '/Subs-Auth.php'); |
||
296 | $cookie_url = url_parts(!empty($modSettings['localCookies']), !empty($modSettings['globalCookies'])); |
||
297 | smf_setcookie($cookiename . '_', '', time() - 3600, $cookie_url[1], $cookie_url[0], false, false); |
||
298 | } |
||
299 | } |
||
300 | |||
301 | // If you're fully banned, it's end of the story for you. |
||
302 | if (isset($_SESSION['ban']['cannot_access'])) |
||
303 | { |
||
304 | // We don't wanna see you! |
||
305 | if (!$user_info['is_guest']) |
||
306 | $smcFunc['db_query']('', ' |
||
307 | DELETE FROM {db_prefix}log_online |
||
308 | WHERE id_member = {int:current_member}', |
||
309 | array( |
||
310 | 'current_member' => $user_info['id'], |
||
311 | ) |
||
312 | ); |
||
313 | |||
314 | // 'Log' the user out. Can't have any funny business... (save the name!) |
||
315 | $old_name = isset($user_info['name']) && $user_info['name'] != '' ? $user_info['name'] : $txt['guest_title']; |
||
316 | $user_info['name'] = ''; |
||
317 | $user_info['username'] = ''; |
||
318 | $user_info['is_guest'] = true; |
||
319 | $user_info['is_admin'] = false; |
||
320 | $user_info['permissions'] = array(); |
||
321 | $user_info['id'] = 0; |
||
322 | $context['user'] = array( |
||
323 | 'id' => 0, |
||
324 | 'username' => '', |
||
325 | 'name' => $txt['guest_title'], |
||
326 | 'is_guest' => true, |
||
327 | 'is_logged' => false, |
||
328 | 'is_admin' => false, |
||
329 | 'is_mod' => false, |
||
330 | 'can_mod' => false, |
||
331 | 'language' => $user_info['language'], |
||
332 | ); |
||
333 | |||
334 | // A goodbye present. |
||
335 | require_once($sourcedir . '/Subs-Auth.php'); |
||
336 | require_once($sourcedir . '/LogInOut.php'); |
||
337 | $cookie_url = url_parts(!empty($modSettings['localCookies']), !empty($modSettings['globalCookies'])); |
||
338 | smf_setcookie($cookiename . '_', implode(',', $_SESSION['ban']['cannot_access']['ids']), time() + 3153600, $cookie_url[1], $cookie_url[0], false, false); |
||
339 | |||
340 | // Don't scare anyone, now. |
||
341 | $_GET['action'] = ''; |
||
342 | $_GET['board'] = ''; |
||
343 | $_GET['topic'] = ''; |
||
344 | writeLog(true); |
||
345 | Logout(true, false); |
||
346 | |||
347 | // You banned, sucka! |
||
348 | fatal_error(sprintf($txt['your_ban'], $old_name) . (empty($_SESSION['ban']['cannot_access']['reason']) ? '' : '<br>' . $_SESSION['ban']['cannot_access']['reason']) . '<br>' . (!empty($_SESSION['ban']['expire_time']) ? sprintf($txt['your_ban_expires'], timeformat($_SESSION['ban']['expire_time'], false)) : $txt['your_ban_expires_never']), false, 403); |
||
349 | |||
350 | // If we get here, something's gone wrong.... but let's try anyway. |
||
351 | trigger_error('No direct access...', E_USER_ERROR); |
||
352 | } |
||
353 | // You're not allowed to log in but yet you are. Let's fix that. |
||
354 | elseif (isset($_SESSION['ban']['cannot_login']) && !$user_info['is_guest']) |
||
355 | { |
||
356 | // We don't wanna see you! |
||
357 | $smcFunc['db_query']('', ' |
||
358 | DELETE FROM {db_prefix}log_online |
||
359 | WHERE id_member = {int:current_member}', |
||
360 | array( |
||
361 | 'current_member' => $user_info['id'], |
||
362 | ) |
||
363 | ); |
||
364 | |||
365 | // 'Log' the user out. Can't have any funny business... (save the name!) |
||
366 | $old_name = isset($user_info['name']) && $user_info['name'] != '' ? $user_info['name'] : $txt['guest_title']; |
||
367 | $user_info['name'] = ''; |
||
368 | $user_info['username'] = ''; |
||
369 | $user_info['is_guest'] = true; |
||
370 | $user_info['is_admin'] = false; |
||
371 | $user_info['permissions'] = array(); |
||
372 | $user_info['id'] = 0; |
||
373 | $context['user'] = array( |
||
374 | 'id' => 0, |
||
375 | 'username' => '', |
||
376 | 'name' => $txt['guest_title'], |
||
377 | 'is_guest' => true, |
||
378 | 'is_logged' => false, |
||
379 | 'is_admin' => false, |
||
380 | 'is_mod' => false, |
||
381 | 'can_mod' => false, |
||
382 | 'language' => $user_info['language'], |
||
383 | ); |
||
384 | |||
385 | // SMF's Wipe 'n Clean(r) erases all traces. |
||
386 | $_GET['action'] = ''; |
||
387 | $_GET['board'] = ''; |
||
388 | $_GET['topic'] = ''; |
||
389 | writeLog(true); |
||
390 | |||
391 | require_once($sourcedir . '/LogInOut.php'); |
||
392 | Logout(true, false); |
||
393 | |||
394 | fatal_error(sprintf($txt['your_ban'], $old_name) . (empty($_SESSION['ban']['cannot_login']['reason']) ? '' : '<br>' . $_SESSION['ban']['cannot_login']['reason']) . '<br>' . (!empty($_SESSION['ban']['expire_time']) ? sprintf($txt['your_ban_expires'], timeformat($_SESSION['ban']['expire_time'], false)) : $txt['your_ban_expires_never']) . '<br>' . $txt['ban_continue_browse'], false, 403); |
||
395 | } |
||
396 | |||
397 | // Fix up the banning permissions. |
||
398 | if (isset($user_info['permissions'])) |
||
399 | banPermissions(); |
||
400 | } |
||
401 | |||
402 | /** |
||
403 | * Fix permissions according to ban status. |
||
404 | * Applies any states of banning by removing permissions the user cannot have. |
||
405 | */ |
||
406 | function banPermissions() |
||
407 | { |
||
408 | global $user_info, $sourcedir, $modSettings, $context; |
||
409 | |||
410 | // Somehow they got here, at least take away all permissions... |
||
411 | if (isset($_SESSION['ban']['cannot_access'])) |
||
412 | $user_info['permissions'] = array(); |
||
413 | // Okay, well, you can watch, but don't touch a thing. |
||
414 | elseif (isset($_SESSION['ban']['cannot_post']) || (!empty($modSettings['warning_mute']) && $modSettings['warning_mute'] <= $user_info['warning'])) |
||
415 | { |
||
416 | $denied_permissions = array( |
||
417 | 'pm_send', |
||
418 | 'calendar_post', 'calendar_edit_own', 'calendar_edit_any', |
||
419 | 'poll_post', |
||
420 | 'poll_add_own', 'poll_add_any', |
||
421 | 'poll_edit_own', 'poll_edit_any', |
||
422 | 'poll_lock_own', 'poll_lock_any', |
||
423 | 'poll_remove_own', 'poll_remove_any', |
||
424 | 'manage_attachments', 'manage_smileys', 'manage_boards', 'admin_forum', 'manage_permissions', |
||
425 | 'moderate_forum', 'manage_membergroups', 'manage_bans', 'send_mail', 'edit_news', |
||
426 | 'profile_identity_any', 'profile_extra_any', 'profile_title_any', |
||
427 | 'profile_forum_any', 'profile_other_any', 'profile_signature_any', |
||
428 | 'post_new', 'post_reply_own', 'post_reply_any', |
||
429 | 'delete_own', 'delete_any', 'delete_replies', |
||
430 | 'make_sticky', |
||
431 | 'merge_any', 'split_any', |
||
432 | 'modify_own', 'modify_any', 'modify_replies', |
||
433 | 'move_any', |
||
434 | 'lock_own', 'lock_any', |
||
435 | 'remove_own', 'remove_any', |
||
436 | 'post_unapproved_topics', 'post_unapproved_replies_own', 'post_unapproved_replies_any', |
||
437 | ); |
||
438 | call_integration_hook('integrate_post_ban_permissions', array(&$denied_permissions)); |
||
439 | $user_info['permissions'] = array_diff($user_info['permissions'], $denied_permissions); |
||
440 | } |
||
441 | // Are they absolutely under moderation? |
||
442 | elseif (!empty($modSettings['warning_moderate']) && $modSettings['warning_moderate'] <= $user_info['warning']) |
||
443 | { |
||
444 | // Work out what permissions should change... |
||
445 | $permission_change = array( |
||
446 | 'post_new' => 'post_unapproved_topics', |
||
447 | 'post_reply_own' => 'post_unapproved_replies_own', |
||
448 | 'post_reply_any' => 'post_unapproved_replies_any', |
||
449 | 'post_attachment' => 'post_unapproved_attachments', |
||
450 | ); |
||
451 | call_integration_hook('integrate_warn_permissions', array(&$permission_change)); |
||
452 | foreach ($permission_change as $old => $new) |
||
453 | { |
||
454 | if (!in_array($old, $user_info['permissions'])) |
||
455 | unset($permission_change[$old]); |
||
456 | else |
||
457 | $user_info['permissions'][] = $new; |
||
458 | } |
||
459 | $user_info['permissions'] = array_diff($user_info['permissions'], array_keys($permission_change)); |
||
460 | } |
||
461 | |||
462 | // @todo Find a better place to call this? Needs to be after permissions loaded! |
||
463 | // Finally, some bits we cache in the session because it saves queries. |
||
464 | if (isset($_SESSION['mc']) && $_SESSION['mc']['time'] > $modSettings['settings_updated'] && $_SESSION['mc']['id'] == $user_info['id']) |
||
465 | $user_info['mod_cache'] = $_SESSION['mc']; |
||
466 | else |
||
467 | { |
||
468 | require_once($sourcedir . '/Subs-Auth.php'); |
||
469 | rebuildModCache(); |
||
470 | } |
||
471 | |||
472 | // Now that we have the mod cache taken care of lets setup a cache for the number of mod reports still open |
||
473 | if (isset($_SESSION['rc']['reports']) && isset($_SESSION['rc']['member_reports']) && $_SESSION['rc']['time'] > $modSettings['last_mod_report_action'] && $_SESSION['rc']['id'] == $user_info['id']) |
||
474 | { |
||
475 | $context['open_mod_reports'] = $_SESSION['rc']['reports']; |
||
476 | $context['open_member_reports'] = $_SESSION['rc']['member_reports']; |
||
477 | } |
||
478 | elseif ($_SESSION['mc']['bq'] != '0=1') |
||
479 | { |
||
480 | require_once($sourcedir . '/Subs-ReportedContent.php'); |
||
481 | $context['open_mod_reports'] = recountOpenReports('posts'); |
||
482 | $context['open_member_reports'] = recountOpenReports('members'); |
||
483 | } |
||
484 | else |
||
485 | { |
||
486 | $context['open_mod_reports'] = 0; |
||
487 | $context['open_member_reports'] = 0; |
||
488 | } |
||
489 | } |
||
490 | |||
491 | /** |
||
492 | * Log a ban in the database. |
||
493 | * Log the current user in the ban logs. |
||
494 | * Increment the hit counters for the specified ban ID's (if any.) |
||
495 | * |
||
496 | * @param array $ban_ids The IDs of the bans |
||
497 | * @param string $email The email address associated with the user that triggered this hit |
||
498 | */ |
||
499 | function log_ban($ban_ids = array(), $email = null) |
||
500 | { |
||
501 | global $user_info, $smcFunc; |
||
502 | |||
503 | // Don't log web accelerators, it's very confusing... |
||
504 | if (isset($_SERVER['HTTP_X_MOZ']) && $_SERVER['HTTP_X_MOZ'] == 'prefetch') |
||
505 | return; |
||
506 | |||
507 | $smcFunc['db_insert']('', |
||
508 | '{db_prefix}log_banned', |
||
509 | array('id_member' => 'int', 'ip' => 'inet', 'email' => 'string', 'log_time' => 'int'), |
||
510 | array($user_info['id'], $user_info['ip'], ($email === null ? ($user_info['is_guest'] ? '' : $user_info['email']) : $email), time()), |
||
511 | array('id_ban_log') |
||
512 | ); |
||
513 | |||
514 | // One extra point for these bans. |
||
515 | if (!empty($ban_ids)) |
||
516 | $smcFunc['db_query']('', ' |
||
517 | UPDATE {db_prefix}ban_items |
||
518 | SET hits = hits + 1 |
||
519 | WHERE id_ban IN ({array_int:ban_ids})', |
||
520 | array( |
||
521 | 'ban_ids' => $ban_ids, |
||
522 | ) |
||
523 | ); |
||
524 | } |
||
525 | |||
526 | /** |
||
527 | * Checks if a given email address might be banned. |
||
528 | * Check if a given email is banned. |
||
529 | * Performs an immediate ban if the turns turns out positive. |
||
530 | * |
||
531 | * @param string $email The email to check |
||
532 | * @param string $restriction What type of restriction (cannot_post, cannot_register, etc.) |
||
533 | * @param string $error The error message to display if they are indeed banned |
||
534 | */ |
||
535 | function isBannedEmail($email, $restriction, $error) |
||
536 | { |
||
537 | global $txt, $smcFunc; |
||
538 | |||
539 | // Can't ban an empty email |
||
540 | if (empty($email) || trim($email) == '') |
||
541 | return; |
||
542 | |||
543 | // Let's start with the bans based on your IP/hostname/memberID... |
||
544 | $ban_ids = isset($_SESSION['ban'][$restriction]) ? $_SESSION['ban'][$restriction]['ids'] : array(); |
||
545 | $ban_reason = isset($_SESSION['ban'][$restriction]) ? $_SESSION['ban'][$restriction]['reason'] : ''; |
||
546 | |||
547 | // ...and add to that the email address you're trying to register. |
||
548 | $request = $smcFunc['db_query']('', ' |
||
549 | SELECT bi.id_ban, bg.' . $restriction . ', bg.cannot_access, bg.reason |
||
550 | FROM {db_prefix}ban_items AS bi |
||
551 | INNER JOIN {db_prefix}ban_groups AS bg ON (bg.id_ban_group = bi.id_ban_group) |
||
552 | WHERE {string:email} LIKE bi.email_address |
||
553 | AND (bg.' . $restriction . ' = {int:cannot_access} OR bg.cannot_access = {int:cannot_access}) |
||
554 | AND (bg.expire_time IS NULL OR bg.expire_time >= {int:now})', |
||
555 | array( |
||
556 | 'email' => $email, |
||
557 | 'cannot_access' => 1, |
||
558 | 'now' => time(), |
||
559 | ) |
||
560 | ); |
||
561 | while ($row = $smcFunc['db_fetch_assoc']($request)) |
||
562 | { |
||
563 | if (!empty($row['cannot_access'])) |
||
564 | { |
||
565 | $_SESSION['ban']['cannot_access']['ids'][] = $row['id_ban']; |
||
566 | $_SESSION['ban']['cannot_access']['reason'] = $row['reason']; |
||
567 | } |
||
568 | if (!empty($row[$restriction])) |
||
569 | { |
||
570 | $ban_ids[] = $row['id_ban']; |
||
571 | $ban_reason = $row['reason']; |
||
572 | } |
||
573 | } |
||
574 | $smcFunc['db_free_result']($request); |
||
575 | |||
576 | // You're in biiig trouble. Banned for the rest of this session! |
||
577 | if (isset($_SESSION['ban']['cannot_access'])) |
||
578 | { |
||
579 | log_ban($_SESSION['ban']['cannot_access']['ids']); |
||
580 | $_SESSION['ban']['last_checked'] = time(); |
||
581 | |||
582 | fatal_error(sprintf($txt['your_ban'], $txt['guest_title']) . $_SESSION['ban']['cannot_access']['reason'], false); |
||
583 | } |
||
584 | |||
585 | if (!empty($ban_ids)) |
||
586 | { |
||
587 | // Log this ban for future reference. |
||
588 | log_ban($ban_ids, $email); |
||
589 | fatal_error($error . $ban_reason, false); |
||
590 | } |
||
591 | } |
||
592 | |||
593 | /** |
||
594 | * Make sure the user's correct session was passed, and they came from here. |
||
595 | * Checks the current session, verifying that the person is who he or she should be. |
||
596 | * Also checks the referrer to make sure they didn't get sent here. |
||
597 | * Depends on the disableCheckUA setting, which is usually missing. |
||
598 | * Will check GET, POST, or REQUEST depending on the passed type. |
||
599 | * Also optionally checks the referring action if passed. (note that the referring action must be by GET.) |
||
600 | * |
||
601 | * @param string $type The type of check (post, get, request) |
||
602 | * @param string $from_action The action this is coming from |
||
603 | * @param bool $is_fatal Whether to die with a fatal error if the check fails |
||
604 | * @return string The error message if is_fatal is false. |
||
605 | */ |
||
606 | function checkSession($type = 'post', $from_action = '', $is_fatal = true) |
||
607 | { |
||
608 | global $context, $sc, $modSettings, $boardurl; |
||
609 | |||
610 | // Is it in as $_POST['sc']? |
||
611 | if ($type == 'post') |
||
612 | { |
||
613 | $check = isset($_POST[$_SESSION['session_var']]) ? $_POST[$_SESSION['session_var']] : (empty($modSettings['strictSessionCheck']) && isset($_POST['sc']) ? $_POST['sc'] : null); |
||
614 | if ($check !== $sc) |
||
615 | $error = 'session_timeout'; |
||
616 | } |
||
617 | |||
618 | // How about $_GET['sesc']? |
||
619 | elseif ($type == 'get') |
||
620 | { |
||
621 | $check = isset($_GET[$_SESSION['session_var']]) ? $_GET[$_SESSION['session_var']] : (empty($modSettings['strictSessionCheck']) && isset($_GET['sesc']) ? $_GET['sesc'] : null); |
||
622 | if ($check !== $sc) |
||
623 | $error = 'session_verify_fail'; |
||
624 | } |
||
625 | |||
626 | // Or can it be in either? |
||
627 | elseif ($type == 'request') |
||
628 | { |
||
629 | $check = isset($_GET[$_SESSION['session_var']]) ? $_GET[$_SESSION['session_var']] : (empty($modSettings['strictSessionCheck']) && isset($_GET['sesc']) ? $_GET['sesc'] : (isset($_POST[$_SESSION['session_var']]) ? $_POST[$_SESSION['session_var']] : (empty($modSettings['strictSessionCheck']) && isset($_POST['sc']) ? $_POST['sc'] : null))); |
||
630 | |||
631 | if ($check !== $sc) |
||
632 | $error = 'session_verify_fail'; |
||
633 | } |
||
634 | |||
635 | // Verify that they aren't changing user agents on us - that could be bad. |
||
636 | if ((!isset($_SESSION['USER_AGENT']) || $_SESSION['USER_AGENT'] != $_SERVER['HTTP_USER_AGENT']) && empty($modSettings['disableCheckUA'])) |
||
637 | $error = 'session_verify_fail'; |
||
638 | |||
639 | // Make sure a page with session check requirement is not being prefetched. |
||
640 | if (isset($_SERVER['HTTP_X_MOZ']) && $_SERVER['HTTP_X_MOZ'] == 'prefetch') |
||
641 | { |
||
642 | ob_end_clean(); |
||
643 | send_http_status(403); |
||
644 | die; |
||
645 | } |
||
646 | |||
647 | // Check the referring site - it should be the same server at least! |
||
648 | if (isset($_SESSION['request_referer'])) |
||
649 | $referrer = $_SESSION['request_referer']; |
||
650 | else |
||
651 | $referrer = isset($_SERVER['HTTP_REFERER']) ? @parse_url($_SERVER['HTTP_REFERER']) : array(); |
||
652 | |||
653 | // Check the refer but if we have CORS enabled and it came from a trusted source, we can skip this check. |
||
654 | if (!empty($referrer['host']) && (empty($modSettings['allow_cors']) || empty($context['valid_cors_found']) || !in_array($context['valid_cors_found'], array('same', 'subdomain')))) |
||
655 | { |
||
656 | if (strpos($_SERVER['HTTP_HOST'], ':') !== false) |
||
657 | $real_host = substr($_SERVER['HTTP_HOST'], 0, strpos($_SERVER['HTTP_HOST'], ':')); |
||
658 | else |
||
659 | $real_host = $_SERVER['HTTP_HOST']; |
||
660 | |||
661 | $parsed_url = parse_iri($boardurl); |
||
662 | |||
663 | // Are global cookies on? If so, let's check them ;). |
||
664 | if (!empty($modSettings['globalCookies'])) |
||
665 | { |
||
666 | if (preg_match('~(?:[^\.]+\.)?([^\.]{3,}\..+)\z~i', $parsed_url['host'], $parts) == 1) |
||
667 | $parsed_url['host'] = $parts[1]; |
||
668 | |||
669 | if (preg_match('~(?:[^\.]+\.)?([^\.]{3,}\..+)\z~i', $referrer['host'], $parts) == 1) |
||
670 | $referrer['host'] = $parts[1]; |
||
671 | |||
672 | if (preg_match('~(?:[^\.]+\.)?([^\.]{3,}\..+)\z~i', $real_host, $parts) == 1) |
||
673 | $real_host = $parts[1]; |
||
674 | } |
||
675 | |||
676 | // Okay: referrer must either match parsed_url or real_host. |
||
677 | if (isset($parsed_url['host']) && strtolower($referrer['host']) != strtolower($parsed_url['host']) && strtolower($referrer['host']) != strtolower($real_host)) |
||
678 | { |
||
679 | $error = 'verify_url_fail'; |
||
680 | $log_error = true; |
||
681 | } |
||
682 | } |
||
683 | |||
684 | // Well, first of all, if a from_action is specified you'd better have an old_url. |
||
685 | if (!empty($from_action) && (!isset($_SESSION['old_url']) || preg_match('~[?;&]action=' . $from_action . '([;&]|$)~', $_SESSION['old_url']) == 0)) |
||
686 | { |
||
687 | $error = 'verify_url_fail'; |
||
688 | $log_error = true; |
||
689 | } |
||
690 | |||
691 | if (strtolower($_SERVER['HTTP_USER_AGENT']) == 'hacker') |
||
692 | fatal_error('Sound the alarm! It\'s a hacker! Close the castle gates!!', false); |
||
693 | |||
694 | // Everything is ok, return an empty string. |
||
695 | if (!isset($error)) |
||
696 | return ''; |
||
697 | // A session error occurred, show the error. |
||
698 | elseif ($is_fatal) |
||
699 | { |
||
700 | if (isset($_GET['xml'])) |
||
701 | { |
||
702 | ob_end_clean(); |
||
703 | send_http_status(403, 'Forbidden - Session timeout'); |
||
704 | die; |
||
705 | } |
||
706 | else |
||
707 | fatal_lang_error($error, isset($log_error) ? 'user' : false); |
||
708 | } |
||
709 | // A session error occurred, return the error to the calling function. |
||
710 | else |
||
711 | return $error; |
||
712 | |||
713 | // We really should never fall through here, for very important reasons. Let's make sure. |
||
714 | trigger_error('No direct access...', E_USER_ERROR); |
||
715 | } |
||
716 | |||
717 | /** |
||
718 | * Check if a specific confirm parameter was given. |
||
719 | * |
||
720 | * @param string $action The action we want to check against |
||
721 | * @return bool|string True if the check passed or a token |
||
722 | */ |
||
723 | function checkConfirm($action) |
||
724 | { |
||
725 | global $modSettings, $smcFunc; |
||
726 | |||
727 | if (isset($_GET['confirm']) && isset($_SESSION['confirm_' . $action]) && md5($_GET['confirm'] . $_SERVER['HTTP_USER_AGENT']) == $_SESSION['confirm_' . $action]) |
||
728 | return true; |
||
729 | |||
730 | else |
||
731 | { |
||
732 | $token = md5($smcFunc['random_int']() . session_id() . (string) microtime() . $modSettings['rand_seed']); |
||
733 | $_SESSION['confirm_' . $action] = md5($token . $_SERVER['HTTP_USER_AGENT']); |
||
734 | |||
735 | return $token; |
||
736 | } |
||
737 | } |
||
738 | |||
739 | /** |
||
740 | * Lets give you a token of our appreciation. |
||
741 | * |
||
742 | * @param string $action The action to create the token for |
||
743 | * @param string $type The type of token ('post', 'get' or 'request') |
||
744 | * @return array An array containing the name of the token var and the actual token |
||
745 | */ |
||
746 | function createToken($action, $type = 'post') |
||
747 | { |
||
748 | global $modSettings, $context, $smcFunc; |
||
749 | |||
750 | $token = md5($smcFunc['random_int']() . session_id() . (string) microtime() . $modSettings['rand_seed'] . $type); |
||
751 | $token_var = substr(preg_replace('~^\d+~', '', md5($smcFunc['random_int']() . (string) microtime() . $smcFunc['random_int']())), 0, $smcFunc['random_int'](7, 12)); |
||
752 | |||
753 | $_SESSION['token'][$type . '-' . $action] = array($token_var, md5($token . $_SERVER['HTTP_USER_AGENT']), time(), $token); |
||
754 | |||
755 | $context[$action . '_token'] = $token; |
||
756 | $context[$action . '_token_var'] = $token_var; |
||
757 | |||
758 | return array($action . '_token_var' => $token_var, $action . '_token' => $token); |
||
759 | } |
||
760 | |||
761 | /** |
||
762 | * Only patrons with valid tokens can ride this ride. |
||
763 | * |
||
764 | * @param string $action The action to validate the token for |
||
765 | * @param string $type The type of request (get, request, or post) |
||
766 | * @param bool $reset Whether to reset the token and display an error if validation fails |
||
767 | * @return bool returns whether the validation was successful |
||
768 | */ |
||
769 | function validateToken($action, $type = 'post', $reset = true) |
||
770 | { |
||
771 | $type = $type == 'get' || $type == 'request' ? $type : 'post'; |
||
772 | |||
773 | // This nasty piece of code validates a token. |
||
774 | /* |
||
775 | 1. The token exists in session. |
||
776 | 2. The {$type} variable should exist. |
||
777 | 3. We concat the variable we received with the user agent |
||
778 | 4. Match that result against what is in the session. |
||
779 | 5. If it matches, success, otherwise we fallout. |
||
780 | */ |
||
781 | if (isset($_SESSION['token'][$type . '-' . $action], $GLOBALS['_' . strtoupper($type)][$_SESSION['token'][$type . '-' . $action][0]]) && md5($GLOBALS['_' . strtoupper($type)][$_SESSION['token'][$type . '-' . $action][0]] . $_SERVER['HTTP_USER_AGENT']) === $_SESSION['token'][$type . '-' . $action][1]) |
||
782 | { |
||
783 | // Invalidate this token now. |
||
784 | unset($_SESSION['token'][$type . '-' . $action]); |
||
785 | |||
786 | return true; |
||
787 | } |
||
788 | |||
789 | // Patrons with invalid tokens get the boot. |
||
790 | if ($reset) |
||
791 | { |
||
792 | // Might as well do some cleanup on this. |
||
793 | cleanTokens(); |
||
794 | |||
795 | // I'm back baby. |
||
796 | createToken($action, $type); |
||
797 | |||
798 | fatal_lang_error('token_verify_fail', false); |
||
799 | } |
||
800 | // Remove this token as its useless |
||
801 | else |
||
802 | unset($_SESSION['token'][$type . '-' . $action]); |
||
803 | |||
804 | // Randomly check if we should remove some older tokens. |
||
805 | if (mt_rand(0, 138) == 23) |
||
806 | cleanTokens(); |
||
807 | |||
808 | return false; |
||
809 | } |
||
810 | |||
811 | /** |
||
812 | * Removes old unused tokens from session |
||
813 | * defaults to 3 hours before a token is considered expired |
||
814 | * if $complete = true will remove all tokens |
||
815 | * |
||
816 | * @param bool $complete Whether to remove all tokens or only expired ones |
||
817 | */ |
||
818 | function cleanTokens($complete = false) |
||
819 | { |
||
820 | // We appreciate cleaning up after yourselves. |
||
821 | if (!isset($_SESSION['token'])) |
||
822 | return; |
||
823 | |||
824 | // Clean up tokens, trying to give enough time still. |
||
825 | foreach ($_SESSION['token'] as $key => $data) |
||
826 | if ($data[2] + 10800 < time() || $complete) |
||
827 | unset($_SESSION['token'][$key]); |
||
828 | } |
||
829 | |||
830 | /** |
||
831 | * Check whether a form has been submitted twice. |
||
832 | * Registers a sequence number for a form. |
||
833 | * Checks whether a submitted sequence number is registered in the current session. |
||
834 | * Depending on the value of is_fatal shows an error or returns true or false. |
||
835 | * Frees a sequence number from the stack after it's been checked. |
||
836 | * Frees a sequence number without checking if action == 'free'. |
||
837 | * |
||
838 | * @param string $action The action - can be 'register', 'check' or 'free' |
||
839 | * @param bool $is_fatal Whether to die with a fatal error |
||
840 | * @return void|bool If the action isn't check, returns nothing, otherwise returns whether the check was successful |
||
841 | */ |
||
842 | function checkSubmitOnce($action, $is_fatal = true) |
||
843 | { |
||
844 | global $context, $txt; |
||
845 | |||
846 | if (!isset($_SESSION['forms'])) |
||
847 | $_SESSION['forms'] = array(); |
||
848 | |||
849 | // Register a form number and store it in the session stack. (use this on the page that has the form.) |
||
850 | if ($action == 'register') |
||
851 | { |
||
852 | $context['form_sequence_number'] = 0; |
||
853 | while (empty($context['form_sequence_number']) || in_array($context['form_sequence_number'], $_SESSION['forms'])) |
||
854 | $context['form_sequence_number'] = mt_rand(1, 16000000); |
||
855 | } |
||
856 | // Check whether the submitted number can be found in the session. |
||
857 | elseif ($action == 'check') |
||
858 | { |
||
859 | if (!isset($_REQUEST['seqnum'])) |
||
860 | return true; |
||
861 | elseif (!in_array($_REQUEST['seqnum'], $_SESSION['forms'])) |
||
862 | { |
||
863 | $_SESSION['forms'][] = (int) $_REQUEST['seqnum']; |
||
864 | return true; |
||
865 | } |
||
866 | elseif ($is_fatal) |
||
867 | fatal_lang_error('error_form_already_submitted', false); |
||
868 | else |
||
869 | return false; |
||
870 | } |
||
871 | // Don't check, just free the stack number. |
||
872 | elseif ($action == 'free' && isset($_REQUEST['seqnum']) && in_array($_REQUEST['seqnum'], $_SESSION['forms'])) |
||
873 | $_SESSION['forms'] = array_diff($_SESSION['forms'], array($_REQUEST['seqnum'])); |
||
874 | elseif ($action != 'free') |
||
875 | { |
||
876 | loadLanguage('Errors'); |
||
877 | trigger_error(sprintf($txt['check_submit_once_invalid_action'], $action), E_USER_WARNING); |
||
878 | } |
||
879 | } |
||
880 | |||
881 | /** |
||
882 | * Check the user's permissions. |
||
883 | * checks whether the user is allowed to do permission. (ie. post_new.) |
||
884 | * If boards is specified, checks those boards instead of the current one. |
||
885 | * If any is true, will return true if the user has the permission on any of the specified boards |
||
886 | * Always returns true if the user is an administrator. |
||
887 | * |
||
888 | * @param string|array $permission A single permission to check or an array of permissions to check |
||
889 | * @param int|array $boards The ID of a board or an array of board IDs if we want to check board-level permissions |
||
890 | * @param bool $any Whether to check for permission on at least one board instead of all boards |
||
891 | * @return bool Whether the user has the specified permission |
||
892 | */ |
||
893 | function allowedTo($permission, $boards = null, $any = false) |
||
894 | { |
||
895 | global $user_info, $smcFunc; |
||
896 | static $perm_cache = array(); |
||
897 | |||
898 | // You're always allowed to do nothing. (unless you're a working man, MR. LAZY :P!) |
||
899 | if (empty($permission)) |
||
900 | return true; |
||
901 | |||
902 | // You're never allowed to do something if your data hasn't been loaded yet! |
||
903 | if (empty($user_info) || !isset($user_info['permissions'])) |
||
904 | return false; |
||
905 | |||
906 | // Administrators are supermen :P. |
||
907 | if ($user_info['is_admin']) |
||
908 | return true; |
||
909 | |||
910 | // Let's ensure this is an array. |
||
911 | $permission = (array) $permission; |
||
912 | |||
913 | // This should be a boolean. |
||
914 | $any = (bool) $any; |
||
915 | |||
916 | // Are we checking the _current_ board, or some other boards? |
||
917 | if ($boards === null) |
||
918 | { |
||
919 | $user_permissions = $user_info['permissions']; |
||
920 | |||
921 | // Allow temporary overrides for general permissions? |
||
922 | call_integration_hook('integrate_allowed_to_general', array(&$user_permissions, $permission)); |
||
923 | |||
924 | if (count(array_intersect($permission, $user_permissions)) != 0) |
||
925 | return true; |
||
926 | // You aren't allowed, by default. |
||
927 | else |
||
928 | return false; |
||
929 | } |
||
930 | elseif (!is_array($boards)) |
||
931 | $boards = array($boards); |
||
932 | |||
933 | $cache_key = hash('md5', $user_info['id'] . '-' . implode(',', $permission) . '-' . implode(',', $boards) . '-' . (int) $any); |
||
934 | |||
935 | if (isset($perm_cache[$cache_key])) |
||
936 | return $perm_cache[$cache_key]; |
||
937 | |||
938 | $request = $smcFunc['db_query']('', ' |
||
939 | SELECT MIN(bp.add_deny) AS add_deny |
||
940 | FROM {db_prefix}boards AS b |
||
941 | INNER JOIN {db_prefix}board_permissions AS bp ON (bp.id_profile = b.id_profile) |
||
942 | LEFT JOIN {db_prefix}moderators AS mods ON (mods.id_board = b.id_board AND mods.id_member = {int:current_member}) |
||
943 | LEFT JOIN {db_prefix}moderator_groups AS modgs ON (modgs.id_board = b.id_board AND modgs.id_group IN ({array_int:group_list})) |
||
944 | WHERE b.id_board IN ({array_int:board_list}) |
||
945 | AND bp.id_group IN ({array_int:group_list}, {int:moderator_group}) |
||
946 | AND bp.permission IN ({array_string:permission_list}) |
||
947 | AND (mods.id_member IS NOT NULL OR modgs.id_group IS NOT NULL OR bp.id_group != {int:moderator_group}) |
||
948 | GROUP BY b.id_board', |
||
949 | array( |
||
950 | 'current_member' => $user_info['id'], |
||
951 | 'board_list' => $boards, |
||
952 | 'group_list' => $user_info['groups'], |
||
953 | 'moderator_group' => 3, |
||
954 | 'permission_list' => $permission, |
||
955 | ) |
||
956 | ); |
||
957 | |||
958 | if ($any) |
||
959 | { |
||
960 | $result = false; |
||
961 | while ($row = $smcFunc['db_fetch_assoc']($request)) |
||
962 | { |
||
963 | $result = !empty($row['add_deny']); |
||
964 | if ($result == true) |
||
965 | break; |
||
966 | } |
||
967 | $smcFunc['db_free_result']($request); |
||
968 | $return = $result; |
||
969 | } |
||
970 | |||
971 | // Make sure they can do it on all of the boards. |
||
972 | elseif ($smcFunc['db_num_rows']($request) != count($boards)) |
||
973 | $return = false; |
||
974 | |||
975 | else |
||
976 | { |
||
977 | $result = true; |
||
978 | while ($row = $smcFunc['db_fetch_assoc']($request)) |
||
979 | $result &= !empty($row['add_deny']); |
||
980 | $smcFunc['db_free_result']($request); |
||
981 | $return = $result; |
||
982 | } |
||
983 | |||
984 | // Allow temporary overrides for board permissions? |
||
985 | call_integration_hook('integrate_allowed_to_board', array(&$return, $permission, $boards, $any)); |
||
986 | |||
987 | $perm_cache[$cache_key] = $return; |
||
988 | |||
989 | // If the query returned 1, they can do it... otherwise, they can't. |
||
990 | return $return; |
||
991 | } |
||
992 | |||
993 | /** |
||
994 | * Fatal error if they cannot. |
||
995 | * Uses allowedTo() to check if the user is allowed to do permission. |
||
996 | * Checks the passed boards or current board for the permission. |
||
997 | * If $any is true, the user only needs permission on at least one of the boards to pass |
||
998 | * If they are not, it loads the Errors language file and shows an error using $txt['cannot_' . $permission]. |
||
999 | * If they are a guest and cannot do it, this calls is_not_guest(). |
||
1000 | * |
||
1001 | * @param string|array $permission A single permission to check or an array of permissions to check |
||
1002 | * @param int|array $boards The ID of a single board or an array of board IDs if we're checking board-level permissions (null otherwise) |
||
1003 | * @param bool $any Whether to check for permission on at least one board instead of all boards |
||
1004 | */ |
||
1005 | function isAllowedTo($permission, $boards = null, $any = false) |
||
1006 | { |
||
1007 | global $user_info, $txt; |
||
1008 | |||
1009 | $heavy_permissions = array( |
||
1010 | 'admin_forum', |
||
1011 | 'manage_attachments', |
||
1012 | 'manage_smileys', |
||
1013 | 'manage_boards', |
||
1014 | 'edit_news', |
||
1015 | 'moderate_forum', |
||
1016 | 'manage_bans', |
||
1017 | 'manage_membergroups', |
||
1018 | 'manage_permissions', |
||
1019 | ); |
||
1020 | |||
1021 | // Make it an array, even if a string was passed. |
||
1022 | $permission = (array) $permission; |
||
1023 | |||
1024 | call_integration_hook('integrate_heavy_permissions_session', array(&$heavy_permissions)); |
||
1025 | |||
1026 | // Check the permission and return an error... |
||
1027 | if (!allowedTo($permission, $boards, $any)) |
||
1028 | { |
||
1029 | // Pick the last array entry as the permission shown as the error. |
||
1030 | $error_permission = array_shift($permission); |
||
1031 | |||
1032 | // If they are a guest, show a login. (because the error might be gone if they do!) |
||
1033 | if ($user_info['is_guest']) |
||
1034 | { |
||
1035 | loadLanguage('Errors'); |
||
1036 | is_not_guest($txt['cannot_' . $error_permission]); |
||
1037 | } |
||
1038 | |||
1039 | // Clear the action because they aren't really doing that! |
||
1040 | $_GET['action'] = ''; |
||
1041 | $_GET['board'] = ''; |
||
1042 | $_GET['topic'] = ''; |
||
1043 | writeLog(true); |
||
1044 | |||
1045 | fatal_lang_error('cannot_' . $error_permission, false); |
||
1046 | |||
1047 | // Getting this far is a really big problem, but let's try our best to prevent any cases... |
||
1048 | trigger_error('No direct access...', E_USER_ERROR); |
||
1049 | } |
||
1050 | |||
1051 | // If you're doing something on behalf of some "heavy" permissions, validate your session. |
||
1052 | // (take out the heavy permissions, and if you can't do anything but those, you need a validated session.) |
||
1053 | if (!allowedTo(array_diff($permission, $heavy_permissions), $boards)) |
||
1054 | validateSession(); |
||
1055 | } |
||
1056 | |||
1057 | /** |
||
1058 | * Return the boards a user has a certain (board) permission on. (array(0) if all.) |
||
1059 | * - returns a list of boards on which the user is allowed to do the specified permission. |
||
1060 | * - returns an array with only a 0 in it if the user has permission to do this on every board. |
||
1061 | * - returns an empty array if he or she cannot do this on any board. |
||
1062 | * If check_access is true will also make sure the group has proper access to that board. |
||
1063 | * |
||
1064 | * @param string|array $permissions A single permission to check or an array of permissions to check |
||
1065 | * @param bool $check_access Whether to check only the boards the user has access to |
||
1066 | * @param bool $simple Whether to return a simple array of board IDs or one with permissions as the keys |
||
1067 | * @return array An array of board IDs or an array containing 'permission' => 'board,board2,...' pairs |
||
1068 | */ |
||
1069 | function boardsAllowedTo($permissions, $check_access = true, $simple = true) |
||
1070 | { |
||
1071 | global $user_info, $smcFunc; |
||
1072 | |||
1073 | // Arrays are nice, most of the time. |
||
1074 | $permissions = (array) $permissions; |
||
1075 | |||
1076 | /* |
||
1077 | * Set $simple to true to use this function as it were in SMF 2.0.x. |
||
1078 | * Otherwise, the resultant array becomes split into the multiple |
||
1079 | * permissions that were passed. Other than that, it's just the normal |
||
1080 | * state of play that you're used to. |
||
1081 | */ |
||
1082 | |||
1083 | // Administrators are all powerful, sorry. |
||
1084 | if ($user_info['is_admin']) |
||
1085 | { |
||
1086 | if ($simple) |
||
1087 | return array(0); |
||
1088 | else |
||
1089 | { |
||
1090 | $boards = array(); |
||
1091 | foreach ($permissions as $permission) |
||
1092 | $boards[$permission] = array(0); |
||
1093 | |||
1094 | return $boards; |
||
1095 | } |
||
1096 | } |
||
1097 | |||
1098 | // All groups the user is in except 'moderator'. |
||
1099 | $groups = array_diff($user_info['groups'], array(3)); |
||
1100 | |||
1101 | $request = $smcFunc['db_query']('', ' |
||
1102 | SELECT b.id_board, bp.add_deny' . ($simple ? '' : ', bp.permission') . ' |
||
1103 | FROM {db_prefix}board_permissions AS bp |
||
1104 | INNER JOIN {db_prefix}boards AS b ON (b.id_profile = bp.id_profile) |
||
1105 | LEFT JOIN {db_prefix}moderators AS mods ON (mods.id_board = b.id_board AND mods.id_member = {int:current_member}) |
||
1106 | LEFT JOIN {db_prefix}moderator_groups AS modgs ON (modgs.id_board = b.id_board AND modgs.id_group IN ({array_int:group_list})) |
||
1107 | WHERE bp.id_group IN ({array_int:group_list}, {int:moderator_group}) |
||
1108 | AND bp.permission IN ({array_string:permissions}) |
||
1109 | AND (mods.id_member IS NOT NULL OR modgs.id_group IS NOT NULL OR bp.id_group != {int:moderator_group})' . |
||
1110 | ($check_access ? ' AND {query_see_board}' : ''), |
||
1111 | array( |
||
1112 | 'current_member' => $user_info['id'], |
||
1113 | 'group_list' => $groups, |
||
1114 | 'moderator_group' => 3, |
||
1115 | 'permissions' => $permissions, |
||
1116 | ) |
||
1117 | ); |
||
1118 | $boards = array(); |
||
1119 | $deny_boards = array(); |
||
1120 | while ($row = $smcFunc['db_fetch_assoc']($request)) |
||
1121 | { |
||
1122 | if ($simple) |
||
1123 | { |
||
1124 | if (empty($row['add_deny'])) |
||
1125 | $deny_boards[] = $row['id_board']; |
||
1126 | else |
||
1127 | $boards[] = $row['id_board']; |
||
1128 | } |
||
1129 | else |
||
1130 | { |
||
1131 | if (empty($row['add_deny'])) |
||
1132 | $deny_boards[$row['permission']][] = $row['id_board']; |
||
1133 | else |
||
1134 | $boards[$row['permission']][] = $row['id_board']; |
||
1135 | } |
||
1136 | } |
||
1137 | $smcFunc['db_free_result']($request); |
||
1138 | |||
1139 | if ($simple) |
||
1140 | $boards = array_unique(array_values(array_diff($boards, $deny_boards))); |
||
1141 | else |
||
1142 | { |
||
1143 | foreach ($permissions as $permission) |
||
1144 | { |
||
1145 | // never had it to start with |
||
1146 | if (empty($boards[$permission])) |
||
1147 | $boards[$permission] = array(); |
||
1148 | else |
||
1149 | { |
||
1150 | // Or it may have been removed |
||
1151 | $deny_boards[$permission] = isset($deny_boards[$permission]) ? $deny_boards[$permission] : array(); |
||
1152 | $boards[$permission] = array_unique(array_values(array_diff($boards[$permission], $deny_boards[$permission]))); |
||
1153 | } |
||
1154 | } |
||
1155 | } |
||
1156 | |||
1157 | // Maybe a mod needs to tweak the list of allowed boards on the fly? |
||
1158 | call_integration_hook('integrate_boards_allowed_to', array(&$boards, $deny_boards, $permissions, $check_access, $simple)); |
||
1159 | |||
1160 | return $boards; |
||
1161 | } |
||
1162 | |||
1163 | /** |
||
1164 | * This function attempts to protect from spammed messages and the like. |
||
1165 | * The time taken depends on error_type - generally uses the modSetting. |
||
1166 | * |
||
1167 | * @param string $error_type The error type. Also used as a $txt index (not an actual string). |
||
1168 | * @param boolean $only_return_result Whether you want the function to die with a fatal_lang_error. |
||
1169 | * @return bool Whether they've posted within the limit |
||
1170 | */ |
||
1171 | function spamProtection($error_type, $only_return_result = false) |
||
1172 | { |
||
1173 | global $modSettings, $user_info, $smcFunc; |
||
1174 | |||
1175 | // Certain types take less/more time. |
||
1176 | $timeOverrides = array( |
||
1177 | 'login' => 2, |
||
1178 | 'register' => 2, |
||
1179 | 'remind' => 30, |
||
1180 | 'sendmail' => $modSettings['spamWaitTime'] * 5, |
||
1181 | 'reporttm' => $modSettings['spamWaitTime'] * 4, |
||
1182 | 'search' => !empty($modSettings['search_floodcontrol_time']) ? $modSettings['search_floodcontrol_time'] : 1, |
||
1183 | ); |
||
1184 | |||
1185 | call_integration_hook('integrate_spam_protection', array(&$timeOverrides)); |
||
1186 | |||
1187 | // Moderators are free... |
||
1188 | if (!allowedTo('moderate_board')) |
||
1189 | $timeLimit = isset($timeOverrides[$error_type]) ? $timeOverrides[$error_type] : $modSettings['spamWaitTime']; |
||
1190 | else |
||
1191 | $timeLimit = 2; |
||
1192 | |||
1193 | // Delete old entries... |
||
1194 | $smcFunc['db_query']('', ' |
||
1195 | DELETE FROM {db_prefix}log_floodcontrol |
||
1196 | WHERE log_time < {int:log_time} |
||
1197 | AND log_type = {string:log_type}', |
||
1198 | array( |
||
1199 | 'log_time' => time() - $timeLimit, |
||
1200 | 'log_type' => $error_type, |
||
1201 | ) |
||
1202 | ); |
||
1203 | |||
1204 | // Add a new entry, deleting the old if necessary. |
||
1205 | $smcFunc['db_insert']('replace', |
||
1206 | '{db_prefix}log_floodcontrol', |
||
1207 | array('ip' => 'inet', 'log_time' => 'int', 'log_type' => 'string'), |
||
1208 | array($user_info['ip'], time(), $error_type), |
||
1209 | array('ip', 'log_type') |
||
1210 | ); |
||
1211 | |||
1212 | // If affected is 0 or 2, it was there already. |
||
1213 | if ($smcFunc['db_affected_rows']() != 1) |
||
1214 | { |
||
1215 | // Spammer! You only have to wait a *few* seconds! |
||
1216 | if (!$only_return_result) |
||
1217 | fatal_lang_error($error_type . '_WaitTime_broken', false, array($timeLimit)); |
||
1218 | |||
1219 | return true; |
||
1220 | } |
||
1221 | |||
1222 | // They haven't posted within the limit. |
||
1223 | return false; |
||
1224 | } |
||
1225 | |||
1226 | /** |
||
1227 | * A generic function to create a pair of index.php and .htaccess files in a directory |
||
1228 | * |
||
1229 | * @param string|array $paths The (absolute) directory path |
||
1230 | * @param boolean $attachments Whether this is an attachment directory |
||
1231 | * @return bool|array True on success an array of errors if anything fails |
||
1232 | */ |
||
1233 | function secureDirectory($paths, $attachments = false) |
||
1234 | { |
||
1235 | $errors = array(); |
||
1236 | |||
1237 | // Work with arrays |
||
1238 | $paths = (array) $paths; |
||
1239 | |||
1240 | if (empty($paths)) |
||
1241 | $errors[] = 'empty_path'; |
||
1242 | |||
1243 | if (!empty($errors)) |
||
1244 | return $errors; |
||
1245 | |||
1246 | foreach ($paths as $path) |
||
1247 | { |
||
1248 | if (!is_writable($path)) |
||
1249 | { |
||
1250 | $errors[] = 'path_not_writable'; |
||
1251 | |||
1252 | continue; |
||
1253 | } |
||
1254 | |||
1255 | $directory_name = basename($path); |
||
1256 | |||
1257 | $close = empty($attachments) ? ' |
||
1258 | </Files>' : ' |
||
1259 | Allow from localhost |
||
1260 | </Files> |
||
1261 | |||
1262 | RemoveHandler .php .php3 .phtml .cgi .fcgi .pl .fpl .shtml'; |
||
1263 | |||
1264 | if (file_exists($path . '/.htaccess')) |
||
1265 | { |
||
1266 | $errors[] = 'htaccess_exists'; |
||
1267 | |||
1268 | continue; |
||
1269 | } |
||
1270 | |||
1271 | else |
||
1272 | { |
||
1273 | $fh = @fopen($path . '/.htaccess', 'w'); |
||
1274 | |||
1275 | if ($fh) |
||
1276 | { |
||
1277 | fwrite($fh, '<Files *> |
||
1278 | Order Deny,Allow |
||
1279 | Deny from all' . $close); |
||
1280 | fclose($fh); |
||
1281 | } |
||
1282 | |||
1283 | else |
||
1284 | $errors[] = 'htaccess_cannot_create_file'; |
||
1285 | } |
||
1286 | |||
1287 | if (file_exists($path . '/index.php')) |
||
1288 | { |
||
1289 | $errors[] = 'index-php_exists'; |
||
1290 | |||
1291 | continue; |
||
1292 | } |
||
1293 | |||
1294 | else |
||
1295 | { |
||
1296 | $fh = @fopen($path . '/index.php', 'w'); |
||
1297 | |||
1298 | if ($fh) |
||
1299 | { |
||
1300 | fwrite($fh, '<' . '?php |
||
1301 | |||
1302 | /** |
||
1303 | * This file is here solely to protect your ' . $directory_name . ' directory. |
||
1304 | */ |
||
1305 | |||
1306 | // Look for Settings.php.... |
||
1307 | if (file_exists(dirname(dirname(__FILE__)) . \'/Settings.php\')) |
||
1308 | { |
||
1309 | // Found it! |
||
1310 | require(dirname(dirname(__FILE__)) . \'/Settings.php\'); |
||
1311 | header(\'location: \' . $boardurl); |
||
1312 | } |
||
1313 | // Can\'t find it... just forget it. |
||
1314 | else |
||
1315 | exit; |
||
1316 | |||
1317 | ?' . '>'); |
||
1318 | fclose($fh); |
||
1319 | } |
||
1320 | |||
1321 | else |
||
1322 | $errors[] = 'index-php_cannot_create_file'; |
||
1323 | } |
||
1324 | } |
||
1325 | |||
1326 | if (!empty($errors)) |
||
1327 | return $errors; |
||
1328 | |||
1329 | else |
||
1330 | return true; |
||
1331 | } |
||
1332 | |||
1333 | /** |
||
1334 | * This sets the X-Frame-Options header. |
||
1335 | * |
||
1336 | * @param string $override An option to override (either 'SAMEORIGIN' or 'DENY') |
||
1337 | * @since 2.1 |
||
1338 | */ |
||
1339 | function frameOptionsHeader($override = null) |
||
1340 | { |
||
1341 | global $modSettings; |
||
1342 | |||
1343 | $option = 'SAMEORIGIN'; |
||
1344 | if (is_null($override) && !empty($modSettings['frame_security'])) |
||
1345 | $option = $modSettings['frame_security']; |
||
1346 | elseif (in_array($override, array('SAMEORIGIN', 'DENY'))) |
||
1347 | $option = $override; |
||
1348 | |||
1349 | // Don't bother setting the header if we have disabled it. |
||
1350 | if ($option == 'DISABLE') |
||
1351 | return; |
||
1352 | |||
1353 | // Finally set it. |
||
1354 | header('x-frame-options: ' . $option); |
||
1355 | |||
1356 | // And some other useful ones. |
||
1357 | header('x-xss-protection: 1'); |
||
1358 | header('x-content-type-options: nosniff'); |
||
1359 | } |
||
1360 | |||
1361 | /** |
||
1362 | * This sets the Access-Control-Allow-Origin header. |
||
1363 | * @link https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS |
||
1364 | * |
||
1365 | * @param bool $set_header (Default: true): When false, we will do the logic, but not send the headers. The relevant logic is still saved in the $context and can be sent manually. |
||
1366 | * |
||
1367 | * @since 2.1 |
||
1368 | */ |
||
1369 | function corsPolicyHeader($set_header = true) |
||
1370 | { |
||
1371 | global $boardurl, $modSettings, $context; |
||
1372 | |||
1373 | if (empty($modSettings['allow_cors']) || empty($_SERVER['HTTP_ORIGIN'])) |
||
1374 | return; |
||
1375 | |||
1376 | foreach (array('origin' => $_SERVER['HTTP_ORIGIN'], 'boardurl_parts' => $boardurl) as $var => $url) |
||
1377 | { |
||
1378 | // Convert any Punycode to Unicode for the sake of comparision, then parse. |
||
1379 | $$var = parse_iri(url_to_iri((string) validate_iri(normalize_iri(trim($url))))); |
||
1380 | } |
||
1381 | |||
1382 | // The admin wants weak security... :( |
||
1383 | if (!empty($modSettings['cors_domains']) && $modSettings['cors_domains'] === '*') |
||
1384 | { |
||
1385 | $context['cors_domain'] = '*'; |
||
1386 | $context['valid_cors_found'] = 'wildcard'; |
||
1387 | } |
||
1388 | |||
1389 | // Oh good, the admin cares about security. :) |
||
1390 | else |
||
1391 | { |
||
1392 | $i = 0; |
||
1393 | |||
1394 | // Build our list of allowed CORS origins. |
||
1395 | $allowed_origins = array(); |
||
1396 | |||
1397 | // If subdomain-independent cookies are on, allow CORS requests from subdomains. |
||
1398 | if (!empty($modSettings['globalCookies']) && !empty($modSettings['globalCookiesDomain'])) |
||
1399 | { |
||
1400 | $allowed_origins[++$i] = array_merge(parse_iri('//*.' . trim($modSettings['globalCookiesDomain'])), array('type' => 'subdomain')); |
||
1401 | } |
||
1402 | |||
1403 | // Support forum_alias_urls as well, since those are supported by our login cookie. |
||
1404 | if (!empty($modSettings['forum_alias_urls'])) |
||
1405 | { |
||
1406 | foreach (explode(',', $modSettings['forum_alias_urls']) as $alias) |
||
1407 | $allowed_origins[++$i] = array_merge(parse_iri((strpos($alias, '//') === false ? '//' : '') . trim($alias)), array('type' => 'alias')); |
||
1408 | } |
||
1409 | |||
1410 | // Additional CORS domains. |
||
1411 | if (!empty($modSettings['cors_domains'])) |
||
1412 | { |
||
1413 | foreach (explode(',', $modSettings['cors_domains']) as $cors_domain) |
||
1414 | { |
||
1415 | $allowed_origins[++$i] = array_merge(parse_iri((strpos($cors_domain, '//') === false ? '//' : '') . trim($cors_domain)), array('type' => 'additional')); |
||
1416 | |||
1417 | if (strpos($allowed_origins[$i]['host'], '*') === 0) |
||
1418 | $allowed_origins[$i]['type'] .= '_wildcard'; |
||
1419 | } |
||
1420 | } |
||
1421 | |||
1422 | // Does the origin match any of our allowed domains? |
||
1423 | foreach ($allowed_origins as $allowed_origin) |
||
1424 | { |
||
1425 | // If a specific scheme is required, it must match. |
||
1426 | if (!empty($allowed_origin['scheme']) && $allowed_origin['scheme'] !== $origin['scheme']) |
||
1427 | continue; |
||
1428 | |||
1429 | // If a specific port is required, it must match. |
||
1430 | if (!empty($allowed_origin['port'])) |
||
1431 | { |
||
1432 | // Automatically supply the default port for the "special" schemes. |
||
1433 | // See https://url.spec.whatwg.org/#special-scheme |
||
1434 | if (empty($origin['port'])) |
||
1435 | { |
||
1436 | switch ($origin['scheme']) |
||
1437 | { |
||
1438 | case 'http': |
||
1439 | case 'ws': |
||
1440 | $origin['port'] = 80; |
||
1441 | break; |
||
1442 | |||
1443 | case 'https': |
||
1444 | case 'wss': |
||
1445 | $origin['port'] = 443; |
||
1446 | break; |
||
1447 | |||
1448 | case 'ftp': |
||
1449 | $origin['port'] = 21; |
||
1450 | break; |
||
1451 | |||
1452 | case 'file': |
||
1453 | default: |
||
1454 | $origin['port'] = null; |
||
1455 | break; |
||
1456 | } |
||
1457 | } |
||
1458 | |||
1459 | if ((int) $allowed_origin['port'] !== (int) $origin['port']) |
||
1460 | continue; |
||
1461 | } |
||
1462 | |||
1463 | // Wildcard can only be the first character. |
||
1464 | if (strrpos($allowed_origin['host'], '*') > 0) |
||
1465 | continue; |
||
1466 | |||
1467 | // Wildcard means allow the domain or any subdomains. |
||
1468 | if (strpos($allowed_origin['host'], '*') === 0) |
||
1469 | $host_regex = '(?:^|\.)' . preg_quote(ltrim($allowed_origin['host'], '*.'), '~') . '$'; |
||
1470 | |||
1471 | // No wildcard means allow the domain only. |
||
1472 | else |
||
1473 | $host_regex = '^' . preg_quote($allowed_origin['host'], '~') . '$'; |
||
1474 | |||
1475 | if (preg_match('~' . $host_regex . '~u', $origin['host'])) |
||
1476 | { |
||
1477 | $context['cors_domain'] = trim($_SERVER['HTTP_ORIGIN']); |
||
1478 | $context['valid_cors_found'] = $allowed_origin['type']; |
||
1479 | break; |
||
1480 | } |
||
1481 | } |
||
1482 | } |
||
1483 | |||
1484 | // The default is just to place the root URL of the forum into the policy. |
||
1485 | if (empty($context['cors_domain'])) |
||
1486 | { |
||
1487 | $context['cors_domain'] = iri_to_url($boardurl_parts['scheme'] . '://' . $boardurl_parts['host']); |
||
1488 | |||
1489 | // Attach the port if needed. |
||
1490 | if (!empty($boardurl_parts['port'])) |
||
1491 | $context['cors_domain'] .= ':' . $boardurl_parts['port']; |
||
1492 | |||
1493 | $context['valid_cors_found'] = 'same'; |
||
1494 | } |
||
1495 | |||
1496 | $context['cors_headers'] = 'X-SMF-AJAX'; |
||
1497 | |||
1498 | // Any additional headers? |
||
1499 | if (!empty($modSettings['cors_headers'])) |
||
1500 | { |
||
1501 | // Cleanup any typos. |
||
1502 | $cors_headers = explode(',', $modSettings['cors_headers']); |
||
1503 | foreach ($cors_headers as &$ch) |
||
1504 | $ch = str_replace(' ', '-', trim($ch)); |
||
1505 | |||
1506 | $context['cors_headers'] += implode(',', $cors_headers); |
||
1507 | } |
||
1508 | |||
1509 | // Allowing Cross-Origin Resource Sharing (CORS). |
||
1510 | if ($set_header && !empty($context['valid_cors_found']) && !empty($context['cors_domain'])) |
||
1511 | { |
||
1512 | header('Access-Control-Allow-Origin: ' . $context['cors_domain']); |
||
1513 | header('Access-Control-Allow-Headers: ' . $context['cors_headers']); |
||
1514 | |||
1515 | // Be careful with this, you're allowing an external site to allow the browser to send cookies with this. |
||
1516 | if (!empty($modSettings['allow_cors_credentials'])) |
||
1517 | header('Access-Control-Allow-Credentials: true'); |
||
1518 | } |
||
1519 | } |
||
1520 | |||
1521 | ?> |