Failed Conditions
Branch release-2.1 (4e22cf)
by Rick
06:39
created

QueryString.php ➔ htmltrim__recursive()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 14
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 7
nc 5
nop 2
dl 0
loc 14
rs 8.8571
c 0
b 0
f 0
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
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
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);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
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.
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
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.
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
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.
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
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
0 ignored issues
show
Documentation introduced by
Should the return type not be false|string?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
354
 */
355
function isValidIPv6($ip)
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $ip. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
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)
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $ip. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
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)
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in your project.

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.

Loading history...
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 (&quot;, &lt;, &gt;) 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)
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in your project.

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.

Loading history...
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)
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in your project.

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.

Loading history...
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)
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in your project.

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.

Loading history...
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)
0 ignored issues
show
Unused Code Comprehensibility introduced by
52% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
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
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
679
	if (empty($_COOKIE) && SID != '' && !isBrowser('possibly_robot'))
680
		$buffer = preg_replace('/(?<!<link rel="canonical" href=)"' . preg_quote($scripturl, '/') . '(?!\?' . preg_quote(SID, '/') . ')\\??/', '"' . $scripturl . '?' . SID . '&amp;', $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 . '(?:;|&|&amp;))((?:board|topic)=[^#"]+?)(#[^"]*?)?"~', function($m)
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $m. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
691
			{
692
				global $scripturl; return '"' . $scripturl . "/" . strtr("$m[1]", '&;=', '//,') . ".html?" . SID . (isset($m[2]) ? $m[2] : "") . '"';
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal / does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $m instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
Coding Style Comprehensibility introduced by
The string literal .html? does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
Coding Style Comprehensibility introduced by
The string literal does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
Coding Style introduced by
It is generally recommended to place each PHP statement on a line by itself.

Let’s take a look at an example:

// Bad
$a = 5; $b = 6; $c = 7;

// Good
$a = 5;
$b = 6;
$c = 7;
Loading history...
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] : "") . '"';
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $m instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
Coding Style Comprehensibility introduced by
The string literal does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
Coding Style introduced by
It is generally recommended to place each PHP statement on a line by itself.

Let’s take a look at an example:

// Bad
$a = 5; $b = 6; $c = 7;

// Good
$a = 5;
$b = 6;
$c = 7;
Loading history...
698
			}, $buffer);
699
	}
700
701
	// Return the changed buffer.
702
	return $buffer;
703
}
704
705
?>