|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
/** |
|
4
|
|
|
* This file does a lot of important stuff. Mainly, this means it handles |
|
5
|
|
|
* the query string, request variables, and session management. |
|
6
|
|
|
* |
|
7
|
|
|
* Simple Machines Forum (SMF) |
|
8
|
|
|
* |
|
9
|
|
|
* @package SMF |
|
10
|
|
|
* @author Simple Machines http://www.simplemachines.org |
|
11
|
|
|
* @copyright 2017 Simple Machines and individual contributors |
|
12
|
|
|
* @license http://www.simplemachines.org/about/smf/license.php BSD |
|
13
|
|
|
* |
|
14
|
|
|
* @version 2.1 Beta 4 |
|
15
|
|
|
*/ |
|
16
|
|
|
|
|
17
|
|
|
if (!defined('SMF')) |
|
18
|
|
|
die('No direct access...'); |
|
19
|
|
|
|
|
20
|
|
|
/** |
|
21
|
|
|
* Clean the request variables - add html entities to GET and slashes if magic_quotes_gpc is Off. |
|
22
|
|
|
* |
|
23
|
|
|
* What it does: |
|
24
|
|
|
* - cleans the request variables (ENV, GET, POST, COOKIE, SERVER) and |
|
25
|
|
|
* - makes sure the query string was parsed correctly. |
|
26
|
|
|
* - handles the URLs passed by the queryless URLs option. |
|
27
|
|
|
* - makes sure, regardless of php.ini, everything has slashes. |
|
28
|
|
|
* - sets up $board, $topic, and $scripturl and $_REQUEST['start']. |
|
29
|
|
|
* - determines, or rather tries to determine, the client's IP. |
|
30
|
|
|
*/ |
|
31
|
|
|
|
|
32
|
|
|
function cleanRequest() |
|
33
|
|
|
{ |
|
34
|
|
|
global $board, $topic, $boardurl, $scripturl, $modSettings, $smcFunc; |
|
35
|
|
|
|
|
36
|
|
|
// Makes it easier to refer to things this way. |
|
37
|
|
|
$scripturl = $boardurl . '/index.php'; |
|
38
|
|
|
|
|
39
|
|
|
// What function to use to reverse magic quotes - if sybase is on we assume that the database sensibly has the right unescape function! |
|
40
|
|
|
$removeMagicQuoteFunction = ini_get('magic_quotes_sybase') || strtolower(ini_get('magic_quotes_sybase')) == 'on' ? 'unescapestring__recursive' : 'stripslashes__recursive'; |
|
41
|
|
|
|
|
42
|
|
|
// Save some memory.. (since we don't use these anyway.) |
|
43
|
|
|
unset($GLOBALS['HTTP_POST_VARS'], $GLOBALS['HTTP_POST_VARS']); |
|
44
|
|
|
unset($GLOBALS['HTTP_POST_FILES'], $GLOBALS['HTTP_POST_FILES']); |
|
45
|
|
|
|
|
46
|
|
|
// These keys shouldn't be set...ever. |
|
47
|
|
|
if (isset($_REQUEST['GLOBALS']) || isset($_COOKIE['GLOBALS'])) |
|
48
|
|
|
die('Invalid request variable.'); |
|
49
|
|
|
|
|
50
|
|
|
// Same goes for numeric keys. |
|
51
|
|
|
foreach (array_merge(array_keys($_POST), array_keys($_GET), array_keys($_FILES)) as $key) |
|
52
|
|
|
if (is_numeric($key)) |
|
53
|
|
|
die('Numeric request keys are invalid.'); |
|
54
|
|
|
|
|
55
|
|
|
// Numeric keys in cookies are less of a problem. Just unset those. |
|
56
|
|
|
foreach ($_COOKIE as $key => $value) |
|
57
|
|
|
if (is_numeric($key)) |
|
58
|
|
|
unset($_COOKIE[$key]); |
|
59
|
|
|
|
|
60
|
|
|
// Get the correct query string. It may be in an environment variable... |
|
61
|
|
|
if (!isset($_SERVER['QUERY_STRING'])) |
|
62
|
|
|
$_SERVER['QUERY_STRING'] = getenv('QUERY_STRING'); |
|
63
|
|
|
|
|
64
|
|
|
// It seems that sticking a URL after the query string is mighty common, well, it's evil - don't. |
|
65
|
|
|
if (strpos($_SERVER['QUERY_STRING'], 'http') === 0) |
|
66
|
|
|
{ |
|
67
|
|
|
header('HTTP/1.1 400 Bad Request'); |
|
68
|
|
|
die; |
|
69
|
|
|
} |
|
70
|
|
|
|
|
71
|
|
|
// Are we going to need to parse the ; out? |
|
72
|
|
|
if (strpos(ini_get('arg_separator.input'), ';') === false && !empty($_SERVER['QUERY_STRING'])) |
|
73
|
|
|
{ |
|
74
|
|
|
// Get rid of the old one! You don't know where it's been! |
|
75
|
|
|
$_GET = array(); |
|
76
|
|
|
|
|
77
|
|
|
// Was this redirected? If so, get the REDIRECT_QUERY_STRING. |
|
78
|
|
|
// Do not urldecode() the querystring. |
|
79
|
|
|
$_SERVER['QUERY_STRING'] = substr($_SERVER['QUERY_STRING'], 0, 5) === 'url=/' ? $_SERVER['REDIRECT_QUERY_STRING'] : $_SERVER['QUERY_STRING']; |
|
80
|
|
|
|
|
81
|
|
|
// Replace ';' with '&' and '&something&' with '&something=&'. (this is done for compatibility...) |
|
82
|
|
|
// @todo smflib |
|
83
|
|
|
parse_str(preg_replace('/&(\w+)(?=&|$)/', '&$1=', strtr($_SERVER['QUERY_STRING'], array(';?' => '&', ';' => '&', '%00' => '', "\0" => ''))), $_GET); |
|
84
|
|
|
|
|
85
|
|
|
// Magic quotes still applies with parse_str - so clean it up. |
|
86
|
|
View Code Duplication |
if (function_exists('get_magic_quotes_gpc') && @get_magic_quotes_gpc() != 0 && empty($modSettings['integrate_magic_quotes'])) |
|
|
|
|
|
|
87
|
|
|
$_GET = $removeMagicQuoteFunction($_GET); |
|
88
|
|
|
} |
|
89
|
|
|
elseif (strpos(ini_get('arg_separator.input'), ';') !== false) |
|
90
|
|
|
{ |
|
91
|
|
View Code Duplication |
if (function_exists('get_magic_quotes_gpc') && @get_magic_quotes_gpc() != 0 && empty($modSettings['integrate_magic_quotes'])) |
|
|
|
|
|
|
92
|
|
|
$_GET = $removeMagicQuoteFunction($_GET); |
|
93
|
|
|
|
|
94
|
|
|
// Search engines will send action=profile%3Bu=1, which confuses PHP. |
|
95
|
|
|
foreach ($_GET as $k => $v) |
|
96
|
|
|
{ |
|
97
|
|
|
if ((string) $v === $v && strpos($k, ';') !== false) |
|
98
|
|
|
{ |
|
99
|
|
|
$temp = explode(';', $v); |
|
100
|
|
|
$_GET[$k] = $temp[0]; |
|
101
|
|
|
|
|
102
|
|
|
for ($i = 1, $n = count($temp); $i < $n; $i++) |
|
103
|
|
|
{ |
|
104
|
|
|
@list ($key, $val) = @explode('=', $temp[$i], 2); |
|
|
|
|
|
|
105
|
|
|
if (!isset($_GET[$key])) |
|
106
|
|
|
$_GET[$key] = $val; |
|
107
|
|
|
} |
|
108
|
|
|
} |
|
109
|
|
|
|
|
110
|
|
|
// This helps a lot with integration! |
|
111
|
|
|
if (strpos($k, '?') === 0) |
|
112
|
|
|
{ |
|
113
|
|
|
$_GET[substr($k, 1)] = $v; |
|
114
|
|
|
unset($_GET[$k]); |
|
115
|
|
|
} |
|
116
|
|
|
} |
|
117
|
|
|
} |
|
118
|
|
|
|
|
119
|
|
|
// There's no query string, but there is a URL... try to get the data from there. |
|
120
|
|
|
if (!empty($_SERVER['REQUEST_URI'])) |
|
121
|
|
|
{ |
|
122
|
|
|
// Remove the .html, assuming there is one. |
|
123
|
|
|
if (substr($_SERVER['REQUEST_URI'], strrpos($_SERVER['REQUEST_URI'], '.'), 4) == '.htm') |
|
124
|
|
|
$request = substr($_SERVER['REQUEST_URI'], 0, strrpos($_SERVER['REQUEST_URI'], '.')); |
|
125
|
|
|
else |
|
126
|
|
|
$request = $_SERVER['REQUEST_URI']; |
|
127
|
|
|
|
|
128
|
|
|
// @todo smflib. |
|
129
|
|
|
// Replace 'index.php/a,b,c/d/e,f' with 'a=b,c&d=&e=f' and parse it into $_GET. |
|
130
|
|
|
if (strpos($request, basename($scripturl) . '/') !== false) |
|
131
|
|
|
{ |
|
132
|
|
|
parse_str(substr(preg_replace('/&(\w+)(?=&|$)/', '&$1=', strtr(preg_replace('~/([^,/]+),~', '/$1=', substr($request, strpos($request, basename($scripturl)) + strlen(basename($scripturl)))), '/', '&')), 1), $temp); |
|
133
|
|
View Code Duplication |
if (function_exists('get_magic_quotes_gpc') && @get_magic_quotes_gpc() != 0 && empty($modSettings['integrate_magic_quotes'])) |
|
|
|
|
|
|
134
|
|
|
$temp = $removeMagicQuoteFunction($temp); |
|
135
|
|
|
$_GET += $temp; |
|
136
|
|
|
} |
|
137
|
|
|
} |
|
138
|
|
|
|
|
139
|
|
|
// If magic quotes is on we have some work... |
|
140
|
|
|
if (function_exists('get_magic_quotes_gpc') && @get_magic_quotes_gpc() != 0) |
|
141
|
|
|
{ |
|
142
|
|
|
$_ENV = $removeMagicQuoteFunction($_ENV); |
|
143
|
|
|
$_POST = $removeMagicQuoteFunction($_POST); |
|
144
|
|
|
$_COOKIE = $removeMagicQuoteFunction($_COOKIE); |
|
145
|
|
|
foreach ($_FILES as $k => $dummy) |
|
146
|
|
|
if (isset($_FILES[$k]['name'])) |
|
147
|
|
|
$_FILES[$k]['name'] = $removeMagicQuoteFunction($_FILES[$k]['name']); |
|
148
|
|
|
} |
|
149
|
|
|
|
|
150
|
|
|
// Add entities to GET. This is kinda like the slashes on everything else. |
|
151
|
|
|
$_GET = htmlspecialchars__recursive($_GET); |
|
152
|
|
|
|
|
153
|
|
|
// Let's not depend on the ini settings... why even have COOKIE in there, anyway? |
|
154
|
|
|
$_REQUEST = $_POST + $_GET; |
|
155
|
|
|
|
|
156
|
|
|
// Make sure $board and $topic are numbers. |
|
157
|
|
|
if (isset($_REQUEST['board'])) |
|
158
|
|
|
{ |
|
159
|
|
|
// Make sure its a string and not something else like an array |
|
160
|
|
|
$_REQUEST['board'] = (string) $_REQUEST['board']; |
|
161
|
|
|
|
|
162
|
|
|
// If there's a slash in it, we've got a start value! (old, compatible links.) |
|
163
|
|
|
if (strpos($_REQUEST['board'], '/') !== false) |
|
164
|
|
|
list ($_REQUEST['board'], $_REQUEST['start']) = explode('/', $_REQUEST['board']); |
|
165
|
|
|
// Same idea, but dots. This is the currently used format - ?board=1.0... |
|
166
|
|
|
elseif (strpos($_REQUEST['board'], '.') !== false) |
|
167
|
|
|
list ($_REQUEST['board'], $_REQUEST['start']) = explode('.', $_REQUEST['board']); |
|
168
|
|
|
// Now make absolutely sure it's a number. |
|
169
|
|
|
$board = (int) $_REQUEST['board']; |
|
170
|
|
|
$_REQUEST['start'] = isset($_REQUEST['start']) ? (int) $_REQUEST['start'] : 0; |
|
171
|
|
|
|
|
172
|
|
|
// This is for "Who's Online" because it might come via POST - and it should be an int here. |
|
173
|
|
|
$_GET['board'] = $board; |
|
174
|
|
|
} |
|
175
|
|
|
// Well, $board is going to be a number no matter what. |
|
176
|
|
|
else |
|
177
|
|
|
$board = 0; |
|
178
|
|
|
|
|
179
|
|
|
// If there's a threadid, it's probably an old YaBB SE link. Flow with it. |
|
180
|
|
|
if (isset($_REQUEST['threadid']) && !isset($_REQUEST['topic'])) |
|
181
|
|
|
$_REQUEST['topic'] = $_REQUEST['threadid']; |
|
182
|
|
|
|
|
183
|
|
|
// We've got topic! |
|
184
|
|
|
if (isset($_REQUEST['topic'])) |
|
185
|
|
|
{ |
|
186
|
|
|
// Make sure its a string and not something else like an array |
|
187
|
|
|
$_REQUEST['topic'] = (string) $_REQUEST['topic']; |
|
188
|
|
|
|
|
189
|
|
|
// Slash means old, beta style, formatting. That's okay though, the link should still work. |
|
190
|
|
|
if (strpos($_REQUEST['topic'], '/') !== false) |
|
191
|
|
|
list ($_REQUEST['topic'], $_REQUEST['start']) = explode('/', $_REQUEST['topic']); |
|
192
|
|
|
// Dots are useful and fun ;). This is ?topic=1.15. |
|
193
|
|
|
elseif (strpos($_REQUEST['topic'], '.') !== false) |
|
194
|
|
|
list ($_REQUEST['topic'], $_REQUEST['start']) = explode('.', $_REQUEST['topic']); |
|
195
|
|
|
|
|
196
|
|
|
// Topic should always be an integer |
|
197
|
|
|
$topic = $_GET['topic'] = $_REQUEST['topic'] = (int) $_REQUEST['topic']; |
|
198
|
|
|
|
|
199
|
|
|
// Start could be a lot of things... |
|
200
|
|
|
// ... a simple number ... |
|
201
|
|
|
if (is_numeric($_REQUEST['start'])) |
|
202
|
|
|
{ |
|
203
|
|
|
$_REQUEST['start'] = (int) $_REQUEST['start']; |
|
204
|
|
|
} |
|
205
|
|
|
// ... or a specific message ... |
|
206
|
|
View Code Duplication |
elseif (strpos($_REQUEST['start'], 'msg') === 0) |
|
|
|
|
|
|
207
|
|
|
{ |
|
208
|
|
|
$virtual_msg = (int) substr($_REQUEST['start'], 3); |
|
209
|
|
|
$_REQUEST['start'] = $virtual_msg === 0 ? 0 : 'msg' . $virtual_msg; |
|
210
|
|
|
} |
|
211
|
|
|
// ... or whatever is new ... |
|
212
|
|
|
elseif (strpos($_REQUEST['start'], 'new') === 0) |
|
213
|
|
|
{ |
|
214
|
|
|
$_REQUEST['start'] = 'new'; |
|
215
|
|
|
} |
|
216
|
|
|
// ... or since a certain time ... |
|
217
|
|
View Code Duplication |
elseif (strpos($_REQUEST['start'], 'from') === 0) |
|
|
|
|
|
|
218
|
|
|
{ |
|
219
|
|
|
$timestamp = (int) substr($_REQUEST['start'], 4); |
|
220
|
|
|
$_REQUEST['start'] = $timestamp === 0 ? 0 : 'from' . $timestamp; |
|
221
|
|
|
} |
|
222
|
|
|
// ... or something invalid, in which case we reset it to 0. |
|
223
|
|
|
else |
|
224
|
|
|
$_REQUEST['start'] = 0; |
|
225
|
|
|
} |
|
226
|
|
|
else |
|
227
|
|
|
$topic = 0; |
|
228
|
|
|
|
|
229
|
|
|
// There should be a $_REQUEST['start'], some at least. If you need to default to other than 0, use $_GET['start']. |
|
230
|
|
|
if (empty($_REQUEST['start']) || $_REQUEST['start'] < 0 || (int) $_REQUEST['start'] > 2147473647) |
|
231
|
|
|
$_REQUEST['start'] = 0; |
|
232
|
|
|
|
|
233
|
|
|
// The action needs to be a string and not an array or anything else |
|
234
|
|
|
if (isset($_REQUEST['action'])) |
|
235
|
|
|
$_REQUEST['action'] = (string) $_REQUEST['action']; |
|
236
|
|
|
if (isset($_GET['action'])) |
|
237
|
|
|
$_GET['action'] = (string) $_GET['action']; |
|
238
|
|
|
|
|
239
|
|
|
// Some mail providers like to encode semicolons in activation URLs... |
|
240
|
|
|
if (!empty($_REQUEST['action']) && substr($_SERVER['QUERY_STRING'], 0, 18) == 'action=activate%3b') |
|
241
|
|
|
{ |
|
242
|
|
|
header('Location: ' . $scripturl . '?' . str_replace('%3b', ';', $_SERVER['QUERY_STRING'])); |
|
243
|
|
|
exit; |
|
244
|
|
|
} |
|
245
|
|
|
|
|
246
|
|
|
// Make sure we have a valid REMOTE_ADDR. |
|
247
|
|
|
if (!isset($_SERVER['REMOTE_ADDR'])) |
|
248
|
|
|
{ |
|
249
|
|
|
$_SERVER['REMOTE_ADDR'] = ''; |
|
250
|
|
|
// A new magic variable to indicate we think this is command line. |
|
251
|
|
|
$_SERVER['is_cli'] = true; |
|
252
|
|
|
} |
|
253
|
|
|
// Perhaps we have a IPv6 address. |
|
254
|
|
|
elseif (isValidIP($_SERVER['REMOTE_ADDR'])) |
|
255
|
|
|
{ |
|
256
|
|
|
$_SERVER['REMOTE_ADDR'] = preg_replace('~^::ffff:(\d+\.\d+\.\d+\.\d+)~', '\1', $_SERVER['REMOTE_ADDR']); |
|
257
|
|
|
} |
|
258
|
|
|
|
|
259
|
|
|
// Try to calculate their most likely IP for those people behind proxies (And the like). |
|
260
|
|
|
$_SERVER['BAN_CHECK_IP'] = $_SERVER['REMOTE_ADDR']; |
|
261
|
|
|
|
|
262
|
|
|
// If we haven't specified how to handle Reverse Proxy IP headers, lets do what we always used to do. |
|
263
|
|
|
if (!isset($modSettings['proxy_ip_header'])) |
|
264
|
|
|
$modSettings['proxy_ip_header'] = 'autodetect'; |
|
265
|
|
|
|
|
266
|
|
|
// Which headers are we going to check for Reverse Proxy IP headers? |
|
267
|
|
|
if ($modSettings['proxy_ip_header'] == 'disabled') |
|
268
|
|
|
$reverseIPheaders = array(); |
|
269
|
|
|
elseif ($modSettings['proxy_ip_header'] == 'autodetect') |
|
270
|
|
|
$reverseIPheaders = array('HTTP_X_FORWARDED_FOR', 'HTTP_CLIENT_IP'); |
|
271
|
|
|
else |
|
272
|
|
|
$reverseIPheaders = array($modSettings['proxy_ip_header']); |
|
273
|
|
|
|
|
274
|
|
|
// Find the user's IP address. (but don't let it give you 'unknown'!) |
|
275
|
|
|
foreach ($reverseIPheaders as $proxyIPheader) |
|
276
|
|
|
{ |
|
277
|
|
|
// Ignore if this is not set. |
|
278
|
|
|
if (!isset($_SERVER[$proxyIPheader])) |
|
279
|
|
|
continue; |
|
280
|
|
|
|
|
281
|
|
|
if (!empty($modSettings['proxy_ip_servers'])) |
|
282
|
|
|
{ |
|
283
|
|
|
foreach (explode(',', $modSettings['proxy_ip_servers']) as $proxy) |
|
284
|
|
|
if ($proxy == $_SERVER['REMOTE_ADDR'] || matchIPtoCIDR($_SERVER['REMOTE_ADDR'], $proxy)) |
|
285
|
|
|
continue; |
|
286
|
|
|
} |
|
287
|
|
|
|
|
288
|
|
|
// If there are commas, get the last one.. probably. |
|
289
|
|
|
if (strpos($_SERVER[$proxyIPheader], ',') !== false) |
|
290
|
|
|
{ |
|
291
|
|
|
$ips = array_reverse(explode(', ', $_SERVER[$proxyIPheader])); |
|
292
|
|
|
|
|
293
|
|
|
// Go through each IP... |
|
294
|
|
|
foreach ($ips as $i => $ip) |
|
295
|
|
|
{ |
|
296
|
|
|
// Make sure it's in a valid range... |
|
297
|
|
|
if (preg_match('~^((0|10|172\.(1[6-9]|2[0-9]|3[01])|192\.168|255|127)\.|unknown|::1|fe80::|fc00::)~', $ip) != 0 && preg_match('~^((0|10|172\.(1[6-9]|2[0-9]|3[01])|192\.168|255|127)\.|unknown|::1|fe80::|fc00::)~', $_SERVER['REMOTE_ADDR']) == 0) |
|
298
|
|
|
{ |
|
299
|
|
View Code Duplication |
if (!isValidIPv6($_SERVER[$proxyIPheader]) || preg_match('~::ffff:\d+\.\d+\.\d+\.\d+~', $_SERVER[$proxyIPheader]) !== 0) |
|
|
|
|
|
|
300
|
|
|
{ |
|
301
|
|
|
$_SERVER[$proxyIPheader] = preg_replace('~^::ffff:(\d+\.\d+\.\d+\.\d+)~', '\1', $_SERVER[$proxyIPheader]); |
|
302
|
|
|
|
|
303
|
|
|
// Just incase we have a legacy IPv4 address. |
|
304
|
|
|
// @ TODO: Convert to IPv6. |
|
305
|
|
|
if (preg_match('~^((([1]?\d)?\d|2[0-4]\d|25[0-5])\.){3}(([1]?\d)?\d|2[0-4]\d|25[0-5])$~', $_SERVER[$proxyIPheader]) === 0) |
|
306
|
|
|
continue; |
|
307
|
|
|
} |
|
308
|
|
|
|
|
309
|
|
|
continue; |
|
310
|
|
|
} |
|
311
|
|
|
|
|
312
|
|
|
// Otherwise, we've got an IP! |
|
313
|
|
|
$_SERVER['BAN_CHECK_IP'] = trim($ip); |
|
314
|
|
|
break; |
|
315
|
|
|
} |
|
316
|
|
|
} |
|
317
|
|
|
// Otherwise just use the only one. |
|
318
|
|
|
elseif (preg_match('~^((0|10|172\.(1[6-9]|2[0-9]|3[01])|192\.168|255|127)\.|unknown|::1|fe80::|fc00::)~', $_SERVER[$proxyIPheader]) == 0 || preg_match('~^((0|10|172\.(1[6-9]|2[0-9]|3[01])|192\.168|255|127)\.|unknown|::1|fe80::|fc00::)~', $_SERVER['REMOTE_ADDR']) != 0) |
|
319
|
|
|
$_SERVER['BAN_CHECK_IP'] = $_SERVER[$proxyIPheader]; |
|
320
|
|
View Code Duplication |
elseif (!isValidIPv6($_SERVER[$proxyIPheader]) || preg_match('~::ffff:\d+\.\d+\.\d+\.\d+~', $_SERVER[$proxyIPheader]) !== 0) |
|
|
|
|
|
|
321
|
|
|
{ |
|
322
|
|
|
$_SERVER[$proxyIPheader] = preg_replace('~^::ffff:(\d+\.\d+\.\d+\.\d+)~', '\1', $_SERVER[$proxyIPheader]); |
|
323
|
|
|
|
|
324
|
|
|
// Just incase we have a legacy IPv4 address. |
|
325
|
|
|
// @ TODO: Convert to IPv6. |
|
326
|
|
|
if (preg_match('~^((([1]?\d)?\d|2[0-4]\d|25[0-5])\.){3}(([1]?\d)?\d|2[0-4]\d|25[0-5])$~', $_SERVER[$proxyIPheader]) === 0) |
|
327
|
|
|
continue; |
|
328
|
|
|
} |
|
329
|
|
|
} |
|
330
|
|
|
|
|
331
|
|
|
// Make sure we know the URL of the current request. |
|
332
|
|
|
if (empty($_SERVER['REQUEST_URI'])) |
|
333
|
|
|
$_SERVER['REQUEST_URL'] = $scripturl . (!empty($_SERVER['QUERY_STRING']) ? '?' . $_SERVER['QUERY_STRING'] : ''); |
|
334
|
|
|
elseif (preg_match('~^([^/]+//[^/]+)~', $scripturl, $match) == 1) |
|
335
|
|
|
$_SERVER['REQUEST_URL'] = $match[1] . $_SERVER['REQUEST_URI']; |
|
336
|
|
|
else |
|
337
|
|
|
$_SERVER['REQUEST_URL'] = $_SERVER['REQUEST_URI']; |
|
338
|
|
|
|
|
339
|
|
|
// And make sure HTTP_USER_AGENT is set. |
|
340
|
|
|
$_SERVER['HTTP_USER_AGENT'] = isset($_SERVER['HTTP_USER_AGENT']) ? (isset($smcFunc['htmlspecialchars']) ? $smcFunc['htmlspecialchars']($smcFunc['db_unescape_string']($_SERVER['HTTP_USER_AGENT']), ENT_QUOTES) : htmlspecialchars($smcFunc['db_unescape_string']($_SERVER['HTTP_USER_AGENT']), ENT_QUOTES)) : ''; |
|
341
|
|
|
|
|
342
|
|
|
// Some final checking. |
|
343
|
|
|
if (!isValidIP($_SERVER['BAN_CHECK_IP'])) |
|
344
|
|
|
$_SERVER['BAN_CHECK_IP'] = ''; |
|
345
|
|
|
if ($_SERVER['REMOTE_ADDR'] == 'unknown') |
|
346
|
|
|
$_SERVER['REMOTE_ADDR'] = ''; |
|
347
|
|
|
} |
|
348
|
|
|
|
|
349
|
|
|
/** |
|
350
|
|
|
* Validates a IPv6 address. returns true if it is ipv6. |
|
351
|
|
|
* |
|
352
|
|
|
* @param string $ip The ip address to be validated |
|
353
|
|
|
* @return boolean Whether the specified IP is a valid IPv6 address |
|
354
|
|
|
*/ |
|
355
|
|
|
function isValidIPv6($ip) |
|
356
|
|
|
{ |
|
357
|
|
|
//looking for : |
|
358
|
|
|
if (strpos($ip, ':') === false) |
|
359
|
|
|
return false; |
|
360
|
|
|
|
|
361
|
|
|
//check valid address |
|
362
|
|
|
return inet_pton($ip); |
|
363
|
|
|
} |
|
364
|
|
|
|
|
365
|
|
|
/** |
|
366
|
|
|
* Converts IPv6s to numbers. This makes ban checks much easier. |
|
367
|
|
|
* |
|
368
|
|
|
* @param string $ip The IP address to be converted |
|
369
|
|
|
* @return array An array containing the expanded IP parts |
|
370
|
|
|
*/ |
|
371
|
|
|
function convertIPv6toInts($ip) |
|
372
|
|
|
{ |
|
373
|
|
|
static $expanded = array(); |
|
374
|
|
|
|
|
375
|
|
|
// Check if we have done this already. |
|
376
|
|
|
if (isset($expanded[$ip])) |
|
377
|
|
|
return $expanded[$ip]; |
|
378
|
|
|
|
|
379
|
|
|
// Expand the IP out. |
|
380
|
|
|
$expanded_ip = explode(':', expandIPv6($ip)); |
|
381
|
|
|
|
|
382
|
|
|
$new_ip = array(); |
|
383
|
|
|
foreach ($expanded_ip as $int) |
|
384
|
|
|
$new_ip[] = hexdec($int); |
|
385
|
|
|
|
|
386
|
|
|
// Save this incase of repeated use. |
|
387
|
|
|
$expanded[$ip] = $new_ip; |
|
388
|
|
|
|
|
389
|
|
|
return $expanded[$ip]; |
|
390
|
|
|
} |
|
391
|
|
|
|
|
392
|
|
|
/** |
|
393
|
|
|
* Expands a IPv6 address to its full form. |
|
394
|
|
|
* |
|
395
|
|
|
* @param string $addr The IPv6 address |
|
396
|
|
|
* @param bool $strict_check Whether to check the length of the expanded address for compliance |
|
397
|
|
|
* @return string|bool The expanded IPv6 address or false if $strict_check is true and the result isn't valid |
|
398
|
|
|
*/ |
|
399
|
|
|
function expandIPv6($addr, $strict_check = true) |
|
400
|
|
|
{ |
|
401
|
|
|
static $converted = array(); |
|
402
|
|
|
|
|
403
|
|
|
// Check if we have done this already. |
|
404
|
|
|
if (isset($converted[$addr])) |
|
405
|
|
|
return $converted[$addr]; |
|
406
|
|
|
|
|
407
|
|
|
// Check if there are segments missing, insert if necessary. |
|
408
|
|
|
if (strpos($addr, '::') !== false) |
|
409
|
|
|
{ |
|
410
|
|
|
$part = explode('::', $addr); |
|
411
|
|
|
$part[0] = explode(':', $part[0]); |
|
412
|
|
|
$part[1] = explode(':', $part[1]); |
|
413
|
|
|
$missing = array(); |
|
414
|
|
|
|
|
415
|
|
|
for ($i = 0; $i < (8 - (count($part[0]) + count($part[1]))); $i++) |
|
416
|
|
|
array_push($missing, '0000'); |
|
417
|
|
|
|
|
418
|
|
|
$part = array_merge($part[0], $missing, $part[1]); |
|
419
|
|
|
} |
|
420
|
|
|
else |
|
421
|
|
|
$part = explode(':', $addr); |
|
422
|
|
|
|
|
423
|
|
|
// Pad each segment until it has 4 digits. |
|
424
|
|
|
foreach ($part as &$p) |
|
425
|
|
|
while (strlen($p) < 4) |
|
426
|
|
|
$p = '0' . $p; |
|
427
|
|
|
|
|
428
|
|
|
unset($p); |
|
429
|
|
|
|
|
430
|
|
|
// Join segments. |
|
431
|
|
|
$result = implode(':', $part); |
|
432
|
|
|
|
|
433
|
|
|
// Save this incase of repeated use. |
|
434
|
|
|
$converted[$addr] = $result; |
|
435
|
|
|
|
|
436
|
|
|
// Quick check to make sure the length is as expected. |
|
437
|
|
|
if (!$strict_check || strlen($result) == 39) |
|
438
|
|
|
return $result; |
|
439
|
|
|
else |
|
440
|
|
|
return false; |
|
441
|
|
|
} |
|
442
|
|
|
|
|
443
|
|
|
|
|
444
|
|
|
/** |
|
445
|
|
|
* Detect if a IP is in a CIDR address |
|
446
|
|
|
* - returns true or false |
|
447
|
|
|
* |
|
448
|
|
|
* @param string $ip_address IP address to check |
|
449
|
|
|
* @param string $cidr_address CIDR address to verify |
|
450
|
|
|
* @return bool Whether the IP matches the CIDR |
|
451
|
|
|
*/ |
|
452
|
|
|
function matchIPtoCIDR($ip_address, $cidr_address) |
|
453
|
|
|
{ |
|
454
|
|
|
list ($cidr_network, $cidr_subnetmask) = preg_split('/', $cidr_address); |
|
455
|
|
|
return (ip2long($ip_address) & (~((1 << (32 - $cidr_subnetmask)) - 1))) == ip2long($cidr_network); |
|
456
|
|
|
} |
|
457
|
|
|
|
|
458
|
|
|
/** |
|
459
|
|
|
* Adds slashes to the array/variable. |
|
460
|
|
|
* What it does: |
|
461
|
|
|
* - returns the var, as an array or string, with escapes as required. |
|
462
|
|
|
* - importantly escapes all keys and values! |
|
463
|
|
|
* - calls itself recursively if necessary. |
|
464
|
|
|
* |
|
465
|
|
|
* @param array|string $var A string or array of strings to escape |
|
466
|
|
|
* @return array|string The escaped string or array of escaped strings |
|
467
|
|
|
*/ |
|
468
|
|
View Code Duplication |
function escapestring__recursive($var) |
|
|
|
|
|
|
469
|
|
|
{ |
|
470
|
|
|
global $smcFunc; |
|
471
|
|
|
|
|
472
|
|
|
if (!is_array($var)) |
|
473
|
|
|
return $smcFunc['db_escape_string']($var); |
|
474
|
|
|
|
|
475
|
|
|
// Reindex the array with slashes. |
|
476
|
|
|
$new_var = array(); |
|
477
|
|
|
|
|
478
|
|
|
// Add slashes to every element, even the indexes! |
|
479
|
|
|
foreach ($var as $k => $v) |
|
480
|
|
|
$new_var[$smcFunc['db_escape_string']($k)] = escapestring__recursive($v); |
|
481
|
|
|
|
|
482
|
|
|
return $new_var; |
|
483
|
|
|
} |
|
484
|
|
|
|
|
485
|
|
|
/** |
|
486
|
|
|
* Adds html entities to the array/variable. Uses two underscores to guard against overloading. |
|
487
|
|
|
* What it does: |
|
488
|
|
|
* - adds entities (", <, >) to the array or string var. |
|
489
|
|
|
* - importantly, does not effect keys, only values. |
|
490
|
|
|
* - calls itself recursively if necessary. |
|
491
|
|
|
* |
|
492
|
|
|
* @param array|string $var The string or array of strings to add entites to |
|
493
|
|
|
* @param int $level Which level we're at within the array (if called recursively) |
|
494
|
|
|
* @return array|string The string or array of strings with entities added |
|
495
|
|
|
*/ |
|
496
|
|
|
function htmlspecialchars__recursive($var, $level = 0) |
|
497
|
|
|
{ |
|
498
|
|
|
global $smcFunc; |
|
499
|
|
|
|
|
500
|
|
|
if (!is_array($var)) |
|
501
|
|
|
return isset($smcFunc['htmlspecialchars']) ? $smcFunc['htmlspecialchars']($var, ENT_QUOTES) : htmlspecialchars($var, ENT_QUOTES); |
|
502
|
|
|
|
|
503
|
|
|
// Add the htmlspecialchars to every element. |
|
504
|
|
|
foreach ($var as $k => $v) |
|
505
|
|
|
$var[$k] = $level > 25 ? null : htmlspecialchars__recursive($v, $level + 1); |
|
506
|
|
|
|
|
507
|
|
|
return $var; |
|
508
|
|
|
} |
|
509
|
|
|
|
|
510
|
|
|
/** |
|
511
|
|
|
* Removes url stuff from the array/variable. Uses two underscores to guard against overloading. |
|
512
|
|
|
* What it does: |
|
513
|
|
|
* - takes off url encoding (%20, etc.) from the array or string var. |
|
514
|
|
|
* - importantly, does it to keys too! |
|
515
|
|
|
* - calls itself recursively if there are any sub arrays. |
|
516
|
|
|
* |
|
517
|
|
|
* @param array|string $var The string or array of strings to decode |
|
518
|
|
|
* @param int $level Which level we're at within the array (if called recursively) |
|
519
|
|
|
* @return array|string The decoded string or array of decoded strings |
|
520
|
|
|
*/ |
|
521
|
|
View Code Duplication |
function urldecode__recursive($var, $level = 0) |
|
|
|
|
|
|
522
|
|
|
{ |
|
523
|
|
|
if (!is_array($var)) |
|
524
|
|
|
return urldecode($var); |
|
525
|
|
|
|
|
526
|
|
|
// Reindex the array... |
|
527
|
|
|
$new_var = array(); |
|
528
|
|
|
|
|
529
|
|
|
// Add the htmlspecialchars to every element. |
|
530
|
|
|
foreach ($var as $k => $v) |
|
531
|
|
|
$new_var[urldecode($k)] = $level > 25 ? null : urldecode__recursive($v, $level + 1); |
|
532
|
|
|
|
|
533
|
|
|
return $new_var; |
|
534
|
|
|
} |
|
535
|
|
|
/** |
|
536
|
|
|
* Unescapes any array or variable. Uses two underscores to guard against overloading. |
|
537
|
|
|
* What it does: |
|
538
|
|
|
* - unescapes, recursively, from the array or string var. |
|
539
|
|
|
* - effects both keys and values of arrays. |
|
540
|
|
|
* - calls itself recursively to handle arrays of arrays. |
|
541
|
|
|
* |
|
542
|
|
|
* @param array|string $var The string or array of strings to unescape |
|
543
|
|
|
* @return array|string The unescaped string or array of unescaped strings |
|
544
|
|
|
*/ |
|
545
|
|
View Code Duplication |
function unescapestring__recursive($var) |
|
|
|
|
|
|
546
|
|
|
{ |
|
547
|
|
|
global $smcFunc; |
|
548
|
|
|
|
|
549
|
|
|
if (!is_array($var)) |
|
550
|
|
|
return $smcFunc['db_unescape_string']($var); |
|
551
|
|
|
|
|
552
|
|
|
// Reindex the array without slashes, this time. |
|
553
|
|
|
$new_var = array(); |
|
554
|
|
|
|
|
555
|
|
|
// Strip the slashes from every element. |
|
556
|
|
|
foreach ($var as $k => $v) |
|
557
|
|
|
$new_var[$smcFunc['db_unescape_string']($k)] = unescapestring__recursive($v); |
|
558
|
|
|
|
|
559
|
|
|
return $new_var; |
|
560
|
|
|
} |
|
561
|
|
|
|
|
562
|
|
|
/** |
|
563
|
|
|
* Remove slashes recursively. Uses two underscores to guard against overloading. |
|
564
|
|
|
* What it does: |
|
565
|
|
|
* - removes slashes, recursively, from the array or string var. |
|
566
|
|
|
* - effects both keys and values of arrays. |
|
567
|
|
|
* - calls itself recursively to handle arrays of arrays. |
|
568
|
|
|
* |
|
569
|
|
|
* @param array|string $var The string or array of strings to strip slashes from |
|
570
|
|
|
* @param int $level = 0 What level we're at within the array (if called recursively) |
|
571
|
|
|
* @return array|string The string or array of strings with slashes stripped |
|
572
|
|
|
*/ |
|
573
|
|
View Code Duplication |
function stripslashes__recursive($var, $level = 0) |
|
|
|
|
|
|
574
|
|
|
{ |
|
575
|
|
|
if (!is_array($var)) |
|
576
|
|
|
return stripslashes($var); |
|
577
|
|
|
|
|
578
|
|
|
// Reindex the array without slashes, this time. |
|
579
|
|
|
$new_var = array(); |
|
580
|
|
|
|
|
581
|
|
|
// Strip the slashes from every element. |
|
582
|
|
|
foreach ($var as $k => $v) |
|
583
|
|
|
$new_var[stripslashes($k)] = $level > 25 ? null : stripslashes__recursive($v, $level + 1); |
|
584
|
|
|
|
|
585
|
|
|
return $new_var; |
|
586
|
|
|
} |
|
587
|
|
|
|
|
588
|
|
|
/** |
|
589
|
|
|
* Trim a string including the HTML space, character 160. Uses two underscores to guard against overloading. |
|
590
|
|
|
* What it does: |
|
591
|
|
|
* - trims a string or an the var array using html characters as well. |
|
592
|
|
|
* - does not effect keys, only values. |
|
593
|
|
|
* - may call itself recursively if needed. |
|
594
|
|
|
* |
|
595
|
|
|
* @param array|string $var The string or array of strings to trim |
|
596
|
|
|
* @param int $level = 0 How deep we're at within the array (if called recursively) |
|
597
|
|
|
* @return array|string The trimmed string or array of trimmed strings |
|
598
|
|
|
*/ |
|
599
|
|
|
function htmltrim__recursive($var, $level = 0) |
|
600
|
|
|
{ |
|
601
|
|
|
global $smcFunc; |
|
602
|
|
|
|
|
603
|
|
|
// Remove spaces (32), tabs (9), returns (13, 10, and 11), nulls (0), and hard spaces. (160) |
|
|
|
|
|
|
604
|
|
|
if (!is_array($var)) |
|
605
|
|
|
return isset($smcFunc) ? $smcFunc['htmltrim']($var) : trim($var, ' ' . "\t\n\r\x0B" . '\0' . "\xA0"); |
|
606
|
|
|
|
|
607
|
|
|
// Go through all the elements and remove the whitespace. |
|
608
|
|
|
foreach ($var as $k => $v) |
|
609
|
|
|
$var[$k] = $level > 25 ? null : htmltrim__recursive($v, $level + 1); |
|
610
|
|
|
|
|
611
|
|
|
return $var; |
|
612
|
|
|
} |
|
613
|
|
|
|
|
614
|
|
|
/** |
|
615
|
|
|
* Clean up the XML to make sure it doesn't contain invalid characters. |
|
616
|
|
|
* What it does: |
|
617
|
|
|
* - removes invalid XML characters to assure the input string being |
|
618
|
|
|
* - parsed properly. |
|
619
|
|
|
* |
|
620
|
|
|
* @param string $string The string to clean |
|
621
|
|
|
* @return string The cleaned string |
|
622
|
|
|
*/ |
|
623
|
|
|
function cleanXml($string) |
|
624
|
|
|
{ |
|
625
|
|
|
global $context; |
|
626
|
|
|
|
|
627
|
|
|
// https://www.w3.org/TR/2000/REC-xml-20001006#NT-Char |
|
628
|
|
|
return preg_replace('~[\x00-\x08\x0B\x0C\x0E-\x19' . ($context['utf8'] ? '\x{FFFE}\x{FFFF}' : '') . ']~' . ($context['utf8'] ? 'u' : ''), '', $string); |
|
629
|
|
|
} |
|
630
|
|
|
|
|
631
|
|
|
/** |
|
632
|
|
|
* Escapes (replaces) characters in strings to make them safe for use in javascript |
|
633
|
|
|
* |
|
634
|
|
|
* @param string $string The string to escape |
|
635
|
|
|
* @return string The escaped string |
|
636
|
|
|
*/ |
|
637
|
|
|
function JavaScriptEscape($string) |
|
638
|
|
|
{ |
|
639
|
|
|
global $scripturl; |
|
640
|
|
|
|
|
641
|
|
|
return '\'' . strtr($string, array( |
|
642
|
|
|
"\r" => '', |
|
643
|
|
|
"\n" => '\\n', |
|
644
|
|
|
"\t" => '\\t', |
|
645
|
|
|
'\\' => '\\\\', |
|
646
|
|
|
'\'' => '\\\'', |
|
647
|
|
|
'</' => '<\' + \'/', |
|
648
|
|
|
'<script' => '<scri\'+\'pt', |
|
649
|
|
|
'<body>' => '<bo\'+\'dy>', |
|
650
|
|
|
'<a href' => '<a hr\'+\'ef', |
|
651
|
|
|
$scripturl => '\' + smf_scripturl + \'', |
|
652
|
|
|
)) . '\''; |
|
653
|
|
|
} |
|
654
|
|
|
|
|
655
|
|
|
/** |
|
656
|
|
|
* Rewrite URLs to include the session ID. |
|
657
|
|
|
* What it does: |
|
658
|
|
|
* - rewrites the URLs outputted to have the session ID, if the user |
|
659
|
|
|
* is not accepting cookies and is using a standard web browser. |
|
660
|
|
|
* - handles rewriting URLs for the queryless URLs option. |
|
661
|
|
|
* - can be turned off entirely by setting $scripturl to an empty |
|
662
|
|
|
* string, ''. (it wouldn't work well like that anyway.) |
|
663
|
|
|
* - because of bugs in certain builds of PHP, does not function in |
|
664
|
|
|
* versions lower than 4.3.0 - please upgrade if this hurts you. |
|
665
|
|
|
* |
|
666
|
|
|
* @param string $buffer The unmodified output buffer |
|
667
|
|
|
* @return string The modified buffer |
|
668
|
|
|
*/ |
|
669
|
|
|
function ob_sessrewrite($buffer) |
|
670
|
|
|
{ |
|
671
|
|
|
global $scripturl, $modSettings, $context; |
|
672
|
|
|
|
|
673
|
|
|
// If $scripturl is set to nothing, or the SID is not defined (SSI?) just quit. |
|
674
|
|
|
if ($scripturl == '' || !defined('SID')) |
|
675
|
|
|
return $buffer; |
|
676
|
|
|
|
|
677
|
|
|
// Do nothing if the session is cookied, or they are a crawler - guests are caught by redirectexit(). This doesn't work below PHP 4.3.0, because it makes the output buffer bigger. |
|
678
|
|
|
// @todo smflib |
|
679
|
|
|
if (empty($_COOKIE) && SID != '' && !isBrowser('possibly_robot')) |
|
680
|
|
|
$buffer = preg_replace('/(?<!<link rel="canonical" href=)"' . preg_quote($scripturl, '/') . '(?!\?' . preg_quote(SID, '/') . ')\\??/', '"' . $scripturl . '?' . SID . '&', $buffer); |
|
681
|
|
|
// Debugging templates, are we? |
|
682
|
|
View Code Duplication |
elseif (isset($_GET['debug'])) |
|
|
|
|
|
|
683
|
|
|
$buffer = preg_replace('/(?<!<link rel="canonical" href=)"' . preg_quote($scripturl, '/') . '\\??/', '"' . $scripturl . '?debug;', $buffer); |
|
684
|
|
|
|
|
685
|
|
|
// This should work even in 4.2.x, just not CGI without cgi.fix_pathinfo. |
|
686
|
|
|
if (!empty($modSettings['queryless_urls']) && (!$context['server']['is_cgi'] || ini_get('cgi.fix_pathinfo') == 1 || @get_cfg_var('cgi.fix_pathinfo') == 1) && ($context['server']['is_apache'] || $context['server']['is_lighttpd'] || $context['server']['is_litespeed'])) |
|
687
|
|
|
{ |
|
688
|
|
|
// Let's do something special for session ids! |
|
689
|
|
|
if (defined('SID') && SID != '') |
|
690
|
|
|
$buffer = preg_replace_callback('~"' . preg_quote($scripturl, '/') . '\?(?:' . SID . '(?:;|&|&))((?:board|topic)=[^#"]+?)(#[^"]*?)?"~', function($m) |
|
691
|
|
|
{ |
|
692
|
|
|
global $scripturl; return '"' . $scripturl . "/" . strtr("$m[1]", '&;=', '//,') . ".html?" . SID . (isset($m[2]) ? $m[2] : "") . '"'; |
|
693
|
|
|
}, $buffer); |
|
694
|
|
View Code Duplication |
else |
|
|
|
|
|
|
695
|
|
|
$buffer = preg_replace_callback('~"' . preg_quote($scripturl, '/') . '\?((?:board|topic)=[^#"]+?)(#[^"]*?)?"~', function($m) |
|
696
|
|
|
{ |
|
697
|
|
|
global $scripturl; return '"' . $scripturl . '/' . strtr("$m[1]", '&;=', '//,') . '.html' . (isset($m[2]) ? $m[2] : "") . '"'; |
|
698
|
|
|
}, $buffer); |
|
699
|
|
|
} |
|
700
|
|
|
|
|
701
|
|
|
// Return the changed buffer. |
|
702
|
|
|
return $buffer; |
|
703
|
|
|
} |
|
704
|
|
|
|
|
705
|
|
|
?> |
|
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.