Completed
Push — release-2.1 ( 48ac11...7dbd50 )
by Mert
05:22
created

Load.php ➔ addJavaScriptVar()   B

Complexity

Conditions 5
Paths 3

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 4
nc 3
nop 3
dl 0
loc 7
rs 8.8571
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * This file has the hefty job of loading information for the forum.
5
 *
6
 * Simple Machines Forum (SMF)
7
 *
8
 * @package SMF
9
 * @author Simple Machines http://www.simplemachines.org
10
 * @copyright 2016 Simple Machines and individual contributors
11
 * @license http://www.simplemachines.org/about/smf/license.php BSD
12
 *
13
 * @version 2.1 Beta 3
14
 */
15
16
if (!defined('SMF'))
17
	die('No direct access...');
18
19
/**
20
 * Load the $modSettings array.
21
 */
22
function reloadSettings()
23
{
24
	global $modSettings, $boarddir, $smcFunc, $txt, $db_character_set;
25
	global $cache_enable, $sourcedir, $context;
26
27
	// Most database systems have not set UTF-8 as their default input charset.
28
	if (!empty($db_character_set))
29
		$smcFunc['db_query']('set_character_set', '
30
			SET NAMES ' . $db_character_set,
31
			array(
32
			)
33
		);
34
35
	// Try to load it from the cache first; it'll never get cached if the setting is off.
36
	if (($modSettings = cache_get_data('modSettings', 90)) == null)
37
	{
38
		$request = $smcFunc['db_query']('', '
39
			SELECT variable, value
40
			FROM {db_prefix}settings',
41
			array(
42
			)
43
		);
44
		$modSettings = array();
45
		if (!$request)
46
			display_db_error();
47 View Code Duplication
		while ($row = $smcFunc['db_fetch_row']($request))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
48
			$modSettings[$row[0]] = $row[1];
49
		$smcFunc['db_free_result']($request);
50
51
		// Do a few things to protect against missing settings or settings with invalid values...
52 View Code Duplication
		if (empty($modSettings['defaultMaxTopics']) || $modSettings['defaultMaxTopics'] <= 0 || $modSettings['defaultMaxTopics'] > 999)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
53
			$modSettings['defaultMaxTopics'] = 20;
54 View Code Duplication
		if (empty($modSettings['defaultMaxMessages']) || $modSettings['defaultMaxMessages'] <= 0 || $modSettings['defaultMaxMessages'] > 999)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
55
			$modSettings['defaultMaxMessages'] = 15;
56 View Code Duplication
		if (empty($modSettings['defaultMaxMembers']) || $modSettings['defaultMaxMembers'] <= 0 || $modSettings['defaultMaxMembers'] > 999)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
57
			$modSettings['defaultMaxMembers'] = 30;
58 View Code Duplication
		if (empty($modSettings['defaultMaxListItems']) || $modSettings['defaultMaxListItems'] <= 0 || $modSettings['defaultMaxListItems'] > 999)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
59
			$modSettings['defaultMaxListItems'] = 15;
60
61
		if (!is_array($modSettings['attachmentUploadDir']))
62
			$modSettings['attachmentUploadDir'] = smf_json_decode($modSettings['attachmentUploadDir'], true);
63
64
		if (!empty($cache_enable))
65
			cache_put_data('modSettings', $modSettings, 90);
66
	}
67
68
	$modSettings['cache_enable'] = $cache_enable;
69
70
	// UTF-8 ?
71
	$utf8 = (empty($modSettings['global_character_set']) ? $txt['lang_character_set'] : $modSettings['global_character_set']) === 'UTF-8';
72
73
	// Set a list of common functions.
74
	$ent_list = empty($modSettings['disableEntityCheck']) ? '&(#\d{1,7}|quot|amp|lt|gt|nbsp);' : '&(#021|quot|amp|lt|gt|nbsp);';
75
	$ent_check = empty($modSettings['disableEntityCheck']) ? function ($string)
76
		{
77
			$string = preg_replace_callback('~(&#(\d{1,7}|x[0-9a-fA-F]{1,6});)~', 'entity_fix__callback', $string);
78
			return $string;
79
		} : function ($string)
80
		{
81
			return $string;
82
		};
83
	$fix_utf8mb4 = function ($string) use ($utf8)
84
	{
85
		if (!$utf8)
86
			return $string;
87
88
		$i = 0;
89
		$len = strlen($string);
90
		$new_string = '';
91
		while ($i < $len)
92
		{
93
			$ord = ord($string[$i]);
94
			if ($ord < 128)
95
			{
96
				$new_string .= $string[$i];
97
				$i++;
98
			}
99 View Code Duplication
			elseif ($ord < 224)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
100
			{
101
				$new_string .= $string[$i] . $string[$i+1];
102
				$i += 2;
103
			}
104 View Code Duplication
			elseif ($ord < 240)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
105
			{
106
				$new_string .= $string[$i] . $string[$i+1] . $string[$i+2];
107
				$i += 3;
108
			}
109
			elseif ($ord < 248)
110
			{
111
				// Magic happens.
112
				$val = (ord($string[$i]) & 0x07) << 18;
113
				$val += (ord($string[$i+1]) & 0x3F) << 12;
114
				$val += (ord($string[$i+2]) & 0x3F) << 6;
115
				$val += (ord($string[$i+3]) & 0x3F);
116
				$new_string .= '&#' . $val . ';';
117
				$i += 4;
118
			}
119
		}
120
		return $new_string;
121
	};
122
123
	// Preg_replace space characters depend on the character set in use
124
	$space_chars = $utf8 ? '\x{A0}\x{AD}\x{2000}-\x{200F}\x{201F}\x{202F}\x{3000}\x{FEFF}' : '\x00-\x08\x0B\x0C\x0E-\x19\xA0';
125
126
	// global array of anonymous helper functions, used mostly to properly handle multi byte strings
127
	$smcFunc += array(
128
		'entity_fix' => function ($string)
129
		{
130
			$num = $string[0] === 'x' ? hexdec(substr($string, 1)) : (int) $string;
131
			return $num < 0x20 || $num > 0x10FFFF || ($num >= 0xD800 && $num <= 0xDFFF) || $num === 0x202E || $num === 0x202D ? '' : '&#' . $num . ';';
132
		},
133
		'htmlspecialchars' => function ($string, $quote_style = ENT_COMPAT, $charset = 'ISO-8859-1') use ($ent_check, $utf8, $fix_utf8mb4)
134
		{
135
			return $fix_utf8mb4($ent_check(htmlspecialchars($string, $quote_style, $utf8 ? 'UTF-8' : $charset)));
136
		},
137
		'htmltrim' => function ($string) use ($utf8, $space_chars, $ent_check)
138
		{
139
			return preg_replace('~^(?:[ \t\n\r\x0B\x00' . $space_chars . ']|&nbsp;)+|(?:[ \t\n\r\x0B\x00' . $space_chars . ']|&nbsp;)+$~' . ($utf8 ? 'u' : ''), '', $ent_check($string));
140
		},
141
		'strlen' => function ($string) use ($ent_list, $utf8, $ent_check)
142
		{
143
			return strlen(preg_replace('~' . $ent_list . ($utf8 ? '|.~u' : '~'), '_', $ent_check($string)));
144
		},
145
		'strpos' => function ($haystack, $needle, $offset = 0) use ($utf8, $ent_check, $modSettings)
146
		{
147
			$haystack_arr = preg_split('~(&#' . (empty($modSettings['disableEntityCheck']) ? '\d{1,7}' : '021') . ';|&quot;|&amp;|&lt;|&gt;|&nbsp;|.)~' . ($utf8 ? 'u' : ''), $ent_check($haystack), -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
148
149
			if (strlen($needle) === 1)
150
			{
151
				$result = array_search($needle, array_slice($haystack_arr, $offset));
152
				return is_int($result) ? $result + $offset : false;
153
			}
154
			else
155
			{
156
				$needle_arr = preg_split('~(&#' . (empty($modSettings['disableEntityCheck']) ? '\d{1,7}' : '021') . ';|&quot;|&amp;|&lt;|&gt;|&nbsp;|.)~' . ($utf8 ? 'u' : '') . '', $ent_check($needle), -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
157
				$needle_size = count($needle_arr);
158
159
				$result = array_search($needle_arr[0], array_slice($haystack_arr, $offset));
160
				while ((int) $result === $result)
161
				{
162
					$offset += $result;
163
					if (array_slice($haystack_arr, $offset, $needle_size) === $needle_arr)
164
						return $offset;
165
					$result = array_search($needle_arr[0], array_slice($haystack_arr, ++$offset));
166
				}
167
				return false;
168
			}
169
		},
170
		'substr' => function ($string, $start, $length = null) use ($utf8, $ent_check, $modSettings)
171
		{
172
			$ent_arr = preg_split('~(&#' . (empty($modSettings['disableEntityCheck']) ? '\d{1,7}' : '021') . ';|&quot;|&amp;|&lt;|&gt;|&nbsp;|.)~' . ($utf8 ? 'u' : '') . '', $ent_check($string), -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
173
			return $length === null ? implode('', array_slice($ent_arr, $start)) : implode('', array_slice($ent_arr, $start, $length));
174
		},
175 View Code Duplication
		'strtolower' => $utf8 ? function ($string) use ($sourcedir)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
176
		{
177
			if (!function_exists('mb_strtolower'))
178
			{
179
				require_once($sourcedir . '/Subs-Charset.php');
180
				return utf8_strtolower($string);
181
			}
182
183
			return mb_strtolower($string, 'UTF-8');
184
		} : 'strtolower',
185
		'strtoupper' => $utf8 ? function ($string)
186
		{
187
			global $sourcedir;
188
189
			if (!function_exists('mb_strtolower'))
190
			{
191
				require_once($sourcedir . '/Subs-Charset.php');
192
				return utf8_strtoupper($string);
193
			}
194
195
			return mb_strtoupper($string, 'UTF-8');
196
		} : 'strtoupper',
197
		'truncate' => function($string, $length) use ($utf8, $ent_check, $ent_list, &$smcFunc)
198
		{
199
			$string = $ent_check($string);
200
			preg_match('~^(' . $ent_list . '|.){' . $smcFunc['strlen'](substr($string, 0, $length)) . '}~'.  ($utf8 ? 'u' : ''), $string, $matches);
201
			$string = $matches[0];
202
			while (strlen($string) > $length)
203
				$string = preg_replace('~(?:' . $ent_list . '|.)$~'.  ($utf8 ? 'u' : ''), '', $string);
204
			return $string;
205
		},
206
		'ucfirst' => $utf8 ? function ($string) use (&$smcFunc)
207
		{
208
			return $smcFunc['strtoupper']($smcFunc['substr']($string, 0, 1)) . $smcFunc['substr']($string, 1);
209
		} : 'ucfirst',
210
		'ucwords' => $utf8 ? function ($string) use (&$smcFunc)
211
		{
212
			$words = preg_split('~([\s\r\n\t]+)~', $string, -1, PREG_SPLIT_DELIM_CAPTURE);
213
			for ($i = 0, $n = count($words); $i < $n; $i += 2)
214
				$words[$i] = $smcFunc['ucfirst']($words[$i]);
215
			return implode('', $words);
216
		} : 'ucwords',
217
	);
218
219
	// Setting the timezone is a requirement for some functions.
220
	if (isset($modSettings['default_timezone']))
221
		date_default_timezone_set($modSettings['default_timezone']);
222
223
	// Check the load averages?
224
	if (!empty($modSettings['loadavg_enable']))
225
	{
226
		if (($modSettings['load_average'] = cache_get_data('loadavg', 90)) == null)
227
		{
228
			$modSettings['load_average'] = @file_get_contents('/proc/loadavg');
229
			if (!empty($modSettings['load_average']) && preg_match('~^([^ ]+?) ([^ ]+?) ([^ ]+)~', $modSettings['load_average'], $matches) != 0)
230
				$modSettings['load_average'] = (float) $matches[1];
231 View Code Duplication
			elseif (($modSettings['load_average'] = @`uptime`) != null && preg_match('~load average[s]?: (\d+\.\d+), (\d+\.\d+), (\d+\.\d+)~i', $modSettings['load_average'], $matches) != 0)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
232
				$modSettings['load_average'] = (float) $matches[1];
233
			else
234
				unset($modSettings['load_average']);
235
236
			if (!empty($modSettings['load_average']) || $modSettings['load_average'] === 0.0)
237
				cache_put_data('loadavg', $modSettings['load_average'], 90);
238
		}
239
240
		if (!empty($modSettings['load_average']) || $modSettings['load_average'] === 0.0)
241
			call_integration_hook('integrate_load_average', array($modSettings['load_average']));
242
243
		if (!empty($modSettings['loadavg_forum']) && !empty($modSettings['load_average']) && $modSettings['load_average'] >= $modSettings['loadavg_forum'])
244
			display_loadavg_error();
245
	}
246
247
	// Is post moderation alive and well? Everywhere else assumes this has been defined, so let's make sure it is.
248
	$modSettings['postmod_active'] = !empty($modSettings['postmod_active']);
249
250
	// Here to justify the name of this function. :P
251
	// It should be added to the install and upgrade scripts.
252
	// But since the converters need to be updated also. This is easier.
253
	if (empty($modSettings['currentAttachmentUploadDir']))
254
	{
255
		updateSettings(array(
256
			'attachmentUploadDir' => json_encode(array(1 => $modSettings['attachmentUploadDir'])),
257
			'currentAttachmentUploadDir' => 1,
258
		));
259
	}
260
261
	// Integration is cool.
262
	if (defined('SMF_INTEGRATION_SETTINGS'))
263
	{
264
		$integration_settings = smf_json_decode(SMF_INTEGRATION_SETTINGS, true);
265
		foreach ($integration_settings as $hook => $function)
266
			add_integration_function($hook, $function, '', false);
0 ignored issues
show
Documentation introduced by
'' is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
false is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
267
	}
268
269
	// Any files to pre include?
270 View Code Duplication
	if (!empty($modSettings['integrate_pre_include']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
271
	{
272
		$pre_includes = explode(',', $modSettings['integrate_pre_include']);
273
		foreach ($pre_includes as $include)
274
		{
275
			$include = strtr(trim($include), array('$boarddir' => $boarddir, '$sourcedir' => $sourcedir));
276
			if (file_exists($include))
277
				require_once($include);
278
		}
279
	}
280
281
	// This determines the server... not used in many places, except for login fixing.
282
	$context['server'] = array(
283
		'is_iis' => isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS') !== false,
284
		'is_apache' => isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'], 'Apache') !== false,
285
		'is_litespeed' => isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'], 'LiteSpeed') !== false,
286
		'is_lighttpd' => isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'], 'lighttpd') !== false,
287
		'is_nginx' => isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'], 'nginx') !== false,
288
		'is_cgi' => isset($_SERVER['SERVER_SOFTWARE']) && strpos(php_sapi_name(), 'cgi') !== false,
289
		'is_windows' => strpos(PHP_OS, 'WIN') === 0,
290
		'iso_case_folding' => ord(strtolower(chr(138))) === 154,
291
	);
292
	// A bug in some versions of IIS under CGI (older ones) makes cookie setting not work with Location: headers.
293
	$context['server']['needs_login_fix'] = $context['server']['is_cgi'] && $context['server']['is_iis'];
294
295
	// Define a list of icons used across multiple places.
296
	$context['stable_icons'] = array('xx', 'thumbup', 'thumbdown', 'exclamation', 'question', 'lamp', 'smiley', 'angry', 'cheesy', 'grin', 'sad', 'wink', 'poll', 'moved', 'recycled', 'clip');
297
298
	// Define an array for custom profile fields placements.
299
	$context['cust_profile_fields_placement'] = array(
300
		'standard',
301
		'icons',
302
		'above_signature',
303
		'below_signature',
304
		'below_avatar',
305
		'above_member',
306
		'bottom_poster',
307
	);
308
309
	// Define an array for content-related <meta> elements (e.g. description, keywords, Open Graph) for the HTML head.
310
	$context['meta_tags'] = array();
311
312
	// Define an array of allowed HTML tags.
313
	$context['allowed_html_tags'] = array(
314
		'<img>',
315
		'<div>',
316
	);
317
318
	// These are the only valid image types for SMF, by default anyway.
319
	$context['validImageTypes'] = array(
320
		1 => 'gif',
321
		2 => 'jpeg',
322
		3 => 'png',
323
		5 => 'psd',
324
		6 => 'bmp',
325
		7 => 'tiff',
326
		8 => 'tiff',
327
		9 => 'jpeg',
328
		14 => 'iff'
329
	);
330
331
	// Define a list of allowed tags for descriptions.
332
	$context['description_allowed_tags'] = array('abbr', 'anchor', 'b', 'center', 'color', 'font', 'hr', 'i', 'img', 'iurl', 'left', 'li', 'list', 'ltr', 'pre', 'right', 's', 'sub', 'sup', 'table', 'td', 'tr', 'u', 'url',);
333
334
	// Get an error count, if necessary
335
	if (!isset($context['num_errors']))
336
	{
337
		$query = $smcFunc['db_query']('', '
338
			SELECT COUNT(id_error)
339
			FROM {db_prefix}log_errors',
340
			array()
341
		);
342
343
		list($context['num_errors']) = $smcFunc['db_fetch_row']($query);
344
		$smcFunc['db_free_result']($query);
345
	}
346
347
	// Call pre load integration functions.
348
	call_integration_hook('integrate_pre_load');
349
}
350
351
/**
352
 * Load all the important user information.
353
 * What it does:
354
 * 	- sets up the $user_info array
355
 * 	- assigns $user_info['query_wanna_see_board'] for what boards the user can see.
356
 * 	- first checks for cookie or integration validation.
357
 * 	- uses the current session if no integration function or cookie is found.
358
 * 	- checks password length, if member is activated and the login span isn't over.
359
 * 		- if validation fails for the user, $id_member is set to 0.
360
 * 		- updates the last visit time when needed.
361
 */
362
function loadUserSettings()
363
{
364
	global $modSettings, $user_settings, $sourcedir, $smcFunc;
365
	global $cookiename, $user_info, $language, $context, $image_proxy_enabled, $image_proxy_secret, $boardurl;
366
367
	// Check first the integration, then the cookie, and last the session.
368
	if (count($integration_ids = call_integration_hook('integrate_verify_user')) > 0)
369
	{
370
		$id_member = 0;
371
		foreach ($integration_ids as $integration_id)
372
		{
373
			$integration_id = (int) $integration_id;
374
			if ($integration_id > 0)
375
			{
376
				$id_member = $integration_id;
377
				$already_verified = true;
378
				break;
379
			}
380
		}
381
	}
382
	else
383
		$id_member = 0;
384
385
	if (empty($id_member) && isset($_COOKIE[$cookiename]))
386
	{
387
		$cookie_data = smf_json_decode($_COOKIE[$cookiename], true, false);
388
389
		if (empty($cookie_data))
390
			$cookie_data = safe_unserialize($_COOKIE[$cookiename]);
391
392
		list ($id_member, $password) = $cookie_data;
393
		$id_member = !empty($id_member) && strlen($password) > 0 ? (int) $id_member : 0;
394
	}
395
	elseif (empty($id_member) && isset($_SESSION['login_' . $cookiename]) && ($_SESSION['USER_AGENT'] == $_SERVER['HTTP_USER_AGENT'] || !empty($modSettings['disableCheckUA'])))
396
	{
397
		// @todo Perhaps we can do some more checking on this, such as on the first octet of the IP?
398
		$cookie_data = smf_json_decode($_SESSION['login_' . $cookiename]);
399
400
		if (empty($cookie_data))
401
			$cookie_data = safe_unserialize($_SESSION['login_' . $cookiename]);
402
403
		list ($id_member, $password, $login_span) = $cookie_data;
404
		$id_member = !empty($id_member) && strlen($password) == 128 && $login_span > time() ? (int) $id_member : 0;
405
	}
406
407
	// Only load this stuff if the user isn't a guest.
408
	if ($id_member != 0)
409
	{
410
		// Is the member data cached?
411
		if (empty($modSettings['cache_enable']) || $modSettings['cache_enable'] < 2 || ($user_settings = cache_get_data('user_settings-' . $id_member, 60)) == null)
412
		{
413
			$request = $smcFunc['db_query']('', '
414
				SELECT mem.*, COALESCE(a.id_attach, 0) AS id_attach, a.filename, a.attachment_type
415
				FROM {db_prefix}members AS mem
416
					LEFT JOIN {db_prefix}attachments AS a ON (a.id_member = {int:id_member})
417
				WHERE mem.id_member = {int:id_member}
418
				LIMIT 1',
419
				array(
420
					'id_member' => $id_member,
421
				)
422
			);
423
			$user_settings = $smcFunc['db_fetch_assoc']($request);
424
			$smcFunc['db_free_result']($request);
425
426 View Code Duplication
			if (!empty($modSettings['force_ssl']) && $image_proxy_enabled && stripos($user_settings['avatar'], 'http://') !== false)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
427
				$user_settings['avatar'] = strtr($boardurl, array('http://' => 'https://')) . '/proxy.php?request=' . urlencode($user_settings['avatar']) . '&hash=' . md5($user_settings['avatar'] . $image_proxy_secret);
428
429
			if (!empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= 2)
430
				cache_put_data('user_settings-' . $id_member, $user_settings, 60);
431
		}
432
433
		// Did we find 'im?  If not, junk it.
434
		if (!empty($user_settings))
435
		{
436
			// As much as the password should be right, we can assume the integration set things up.
437
			if (!empty($already_verified) && $already_verified === true)
438
				$check = true;
439
			// SHA-512 hash should be 128 characters long.
440
			elseif (strlen($password) == 128)
0 ignored issues
show
Bug introduced by
The variable $password does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
441
				$check = hash_salt($user_settings['passwd'], $user_settings['password_salt']) == $password;
442
			else
443
				$check = false;
444
445
			// Wrong password or not activated - either way, you're going nowhere.
446
			$id_member = $check && ($user_settings['is_activated'] == 1 || $user_settings['is_activated'] == 11) ? (int) $user_settings['id_member'] : 0;
447
		}
448
		else
449
			$id_member = 0;
450
451
		// If we no longer have the member maybe they're being all hackey, stop brute force!
452
		if (!$id_member)
453
		{
454
			require_once($sourcedir . '/LogInOut.php');
455
			validatePasswordFlood(!empty($user_settings['id_member']) ? $user_settings['id_member'] : $id_member, !empty($user_settings['passwd_flood']) ? $user_settings['passwd_flood'] : false, $id_member != 0);
456
		}
457
		// Validate for Two Factor Authentication
458
		elseif (!empty($modSettings['tfa_mode']) && $id_member && !empty($user_settings['tfa_secret']) && (empty($_REQUEST['action']) || !in_array($_REQUEST['action'], array('login2', 'logintfa'))))
459
		{
460
			$tfacookie = $cookiename . '_tfa';
461
			$tfasecret = null;
462
463
			$verified = call_integration_hook('integrate_verify_tfa', array($id_member, $user_settings));
464
465
			if (empty($verified) || !in_array(true, $verified))
466
			{
467
				if (!empty($_COOKIE[$tfacookie]))
468
				{
469
					$tfa_data = smf_json_decode($_COOKIE[$tfacookie]);
470
471
					if (is_null($tfa_data))
472
						$tfa_data = safe_unserialize($_COOKIE[$tfacookie]);
473
474
					list ($tfamember, $tfasecret) = $tfa_data;
475
476
					if ((int) $tfamember != $id_member)
477
						$tfasecret = null;
478
				}
479
480
				if (empty($tfasecret) || hash_salt($user_settings['tfa_backup'], $user_settings['password_salt']) != $tfasecret)
481
				{
482
					$id_member = 0;
483
					redirectexit('action=logintfa');
484
				}
485
			}
486
		}
487
		// When authenticating their two factor code, make sure to reset their ID for security
488
		elseif (!empty($modSettings['tfa_mode']) && $id_member && !empty($user_settings['tfa_secret']) && $_REQUEST['action'] == 'logintfa')
489
		{
490
			$id_member = 0;
491
			$context['tfa_member'] = $user_settings;
492
			$user_settings = array();
493
		}
494
		// Are we forcing 2FA? Need to check if the user groups actually require 2FA
495
		elseif (!empty($modSettings['tfa_mode']) && $modSettings['tfa_mode'] >= 2 && $id_member && empty($user_settings['tfa_secret']))
496
		{
497
			if ($modSettings['tfa_mode'] == 2) //only do this if we are just forcing SOME membergroups
498
			{
499
				//Build an array of ALL user membergroups.
500
				$full_groups = array($user_settings['id_group']);
501
				if (!empty($user_settings['additional_groups']))
502
				{
503
					$full_groups = array_merge($full_groups, explode(',', $user_settings['additional_groups']));
504
					$full_groups = array_unique($full_groups); //duplicates, maybe?
505
				}
506
507
				//Find out if any group requires 2FA
508
				$request = $smcFunc['db_query']('', '
509
					SELECT COUNT(id_group) AS total
510
					FROM {db_prefix}membergroups
511
					WHERE tfa_required = {int:tfa_required}
512
						AND id_group IN ({array_int:full_groups})',
513
					array(
514
						'tfa_required' => 1,
515
						'full_groups' => $full_groups,
516
					)
517
				);
518
				$row = $smcFunc['db_fetch_assoc']($request);
519
				$smcFunc['db_free_result']($request);
520
			}
521
			else
522
				$row['total'] = 1; //simplifies logics in the next "if"
0 ignored issues
show
Coding Style Comprehensibility introduced by
$row was never initialized. Although not strictly required by PHP, it is generally a good practice to add $row = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
523
524
			$area = !empty($_REQUEST['area']) ? $_REQUEST['area'] : '';
525
			$action = !empty($_REQUEST['action']) ? $_REQUEST['action'] : '';
526
527
			if ($row['total'] > 0 && !in_array($action, array('profile', 'logout')) || ($action == 'profile' && $area != 'tfasetup'))
528
				redirectexit('action=profile;area=tfasetup;forced');
529
		}
530
	}
531
532
	// Found 'im, let's set up the variables.
533
	if ($id_member != 0)
534
	{
535
		// Let's not update the last visit time in these cases...
536
		// 1. SSI doesn't count as visiting the forum.
537
		// 2. RSS feeds and XMLHTTP requests don't count either.
538
		// 3. If it was set within this session, no need to set it again.
539
		// 4. New session, yet updated < five hours ago? Maybe cache can help.
540
		// 5. We're still logging in or authenticating
541
		if (SMF != 'SSI' && !isset($_REQUEST['xml']) && (!isset($_REQUEST['action']) || !in_array($_REQUEST['action'], array('.xml', 'login2', 'logintfa'))) && empty($_SESSION['id_msg_last_visit']) && (empty($modSettings['cache_enable']) || ($_SESSION['id_msg_last_visit'] = cache_get_data('user_last_visit-' . $id_member, 5 * 3600)) === null))
542
		{
543
			// @todo can this be cached?
544
			// Do a quick query to make sure this isn't a mistake.
545
			$result = $smcFunc['db_query']('', '
546
				SELECT poster_time
547
				FROM {db_prefix}messages
548
				WHERE id_msg = {int:id_msg}
549
				LIMIT 1',
550
				array(
551
					'id_msg' => $user_settings['id_msg_last_visit'],
552
				)
553
			);
554
			list ($visitTime) = $smcFunc['db_fetch_row']($result);
555
			$smcFunc['db_free_result']($result);
556
557
			$_SESSION['id_msg_last_visit'] = $user_settings['id_msg_last_visit'];
558
559
			// If it was *at least* five hours ago...
560
			if ($visitTime < time() - 5 * 3600)
561
			{
562
				updateMemberData($id_member, array('id_msg_last_visit' => (int) $modSettings['maxMsgID'], 'last_login' => time(), 'member_ip' => $_SERVER['REMOTE_ADDR'], 'member_ip2' => $_SERVER['BAN_CHECK_IP']));
563
				$user_settings['last_login'] = time();
564
565
				if (!empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= 2)
566
					cache_put_data('user_settings-' . $id_member, $user_settings, 60);
567
568
				if (!empty($modSettings['cache_enable']))
569
					cache_put_data('user_last_visit-' . $id_member, $_SESSION['id_msg_last_visit'], 5 * 3600);
570
			}
571
		}
572
		elseif (empty($_SESSION['id_msg_last_visit']))
573
			$_SESSION['id_msg_last_visit'] = $user_settings['id_msg_last_visit'];
574
575
		$username = $user_settings['member_name'];
576
577
		if (empty($user_settings['additional_groups']))
578
			$user_info = array(
579
				'groups' => array($user_settings['id_group'], $user_settings['id_post_group'])
580
			);
581 View Code Duplication
		else
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
582
			$user_info = array(
583
				'groups' => array_merge(
584
					array($user_settings['id_group'], $user_settings['id_post_group']),
585
					explode(',', $user_settings['additional_groups'])
586
				)
587
			);
588
589
		// Because history has proven that it is possible for groups to go bad - clean up in case.
590
		foreach ($user_info['groups'] as $k => $v)
591
			$user_info['groups'][$k] = (int) $v;
592
593
		// This is a logged in user, so definitely not a spider.
594
		$user_info['possibly_robot'] = false;
595
596
		// Figure out the new time offset.
597
		if (!empty($user_settings['timezone']))
598
		{
599
			// Get the offsets from UTC for the server, then for the user.
600
			$tz_system = new DateTimeZone(@date_default_timezone_get());
601
			$tz_user = new DateTimeZone($user_settings['timezone']);
602
			$time_system = new DateTime('now', $tz_system);
603
			$time_user = new DateTime('now', $tz_user);
604
			$user_info['time_offset'] = ($tz_user->getOffset($time_user) - $tz_system->getOffset($time_system)) / 3600;
605
		}
606
		else
607
		{
608
			// !!! Compatibility.
609
			$user_info['time_offset'] = empty($user_settings['time_offset']) ? 0 :$user_settings['time_offset'];
610
		}
611
	}
612
	// If the user is a guest, initialize all the critical user settings.
613
	else
614
	{
615
		// This is what a guest's variables should be.
616
		$username = '';
617
		$user_info = array('groups' => array(-1));
618
		$user_settings = array();
619
620
		if (isset($_COOKIE[$cookiename]) && empty($context['tfa_member']))
621
			$_COOKIE[$cookiename] = '';
622
623
		// Expire the 2FA cookie
624
		if (isset($_COOKIE[$cookiename . '_tfa']) && empty($context['tfa_member']))
625
		{
626
			$tfa_data = smf_json_decode($_COOKIE[$cookiename . '_tfa'], true);
627
628
			if (is_null($tfa_data))
629
				$tfa_data = safe_unserialize($_COOKIE[$cookiename . '_tfa']);
630
631
			list ($id, $user, $exp, $state, $preserve) = $tfa_data;
0 ignored issues
show
Unused Code introduced by
The assignment to $id is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
Unused Code introduced by
The assignment to $user is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
Unused Code introduced by
The assignment to $state is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
632
633
			if (!$preserve || time() > $exp)
634
			{
635
				$_COOKIE[$cookiename . '_tfa'] = '';
636
				setTFACookie(-3600, 0, '');
637
			}
638
		}
639
640
		// Create a login token if it doesn't exist yet.
641
		if (!isset($_SESSION['token']['post-login']))
642
			createToken('login');
643
		else
644
			list ($context['login_token_var'],,, $context['login_token']) = $_SESSION['token']['post-login'];
645
646
		// Do we perhaps think this is a search robot? Check every five minutes just in case...
647
		if ((!empty($modSettings['spider_mode']) || !empty($modSettings['spider_group'])) && (!isset($_SESSION['robot_check']) || $_SESSION['robot_check'] < time() - 300))
648
		{
649
			require_once($sourcedir . '/ManageSearchEngines.php');
650
			$user_info['possibly_robot'] = SpiderCheck();
651
		}
652
		elseif (!empty($modSettings['spider_mode']))
653
			$user_info['possibly_robot'] = isset($_SESSION['id_robot']) ? $_SESSION['id_robot'] : 0;
654
		// If we haven't turned on proper spider hunts then have a guess!
655
		else
656
		{
657
			$ci_user_agent = strtolower($_SERVER['HTTP_USER_AGENT']);
658
			$user_info['possibly_robot'] = (strpos($_SERVER['HTTP_USER_AGENT'], 'Mozilla') === false && strpos($_SERVER['HTTP_USER_AGENT'], 'Opera') === false) || strpos($ci_user_agent, 'googlebot') !== false || strpos($ci_user_agent, 'slurp') !== false || strpos($ci_user_agent, 'crawl') !== false || strpos($ci_user_agent, 'msnbot') !== false;
659
		}
660
661
		// We don't know the offset...
662
		$user_info['time_offset'] = 0;
663
	}
664
665
	// Set up the $user_info array.
666
	$user_info += array(
667
		'id' => $id_member,
668
		'username' => $username,
669
		'name' => isset($user_settings['real_name']) ? $user_settings['real_name'] : '',
670
		'email' => isset($user_settings['email_address']) ? $user_settings['email_address'] : '',
671
		'passwd' => isset($user_settings['passwd']) ? $user_settings['passwd'] : '',
672
		'language' => empty($user_settings['lngfile']) || empty($modSettings['userLanguage']) ? $language : $user_settings['lngfile'],
673
		'is_guest' => $id_member == 0,
674
		'is_admin' => in_array(1, $user_info['groups']),
675
		'theme' => empty($user_settings['id_theme']) ? 0 : $user_settings['id_theme'],
676
		'last_login' => empty($user_settings['last_login']) ? 0 : $user_settings['last_login'],
677
		'ip' => $_SERVER['REMOTE_ADDR'],
678
		'ip2' => $_SERVER['BAN_CHECK_IP'],
679
		'posts' => empty($user_settings['posts']) ? 0 : $user_settings['posts'],
680
		'time_format' => empty($user_settings['time_format']) ? $modSettings['time_format'] : $user_settings['time_format'],
681
		'avatar' => array(
682
			'url' => isset($user_settings['avatar']) ? $user_settings['avatar'] : '',
683
			'filename' => empty($user_settings['filename']) ? '' : $user_settings['filename'],
684
			'custom_dir' => !empty($user_settings['attachment_type']) && $user_settings['attachment_type'] == 1,
685
			'id_attach' => isset($user_settings['id_attach']) ? $user_settings['id_attach'] : 0
686
		),
687
		'smiley_set' => isset($user_settings['smiley_set']) ? $user_settings['smiley_set'] : '',
688
		'messages' => empty($user_settings['instant_messages']) ? 0 : $user_settings['instant_messages'],
689
		'unread_messages' => empty($user_settings['unread_messages']) ? 0 : $user_settings['unread_messages'],
690
		'alerts' => empty($user_settings['alerts']) ? 0 : $user_settings['alerts'],
691
		'total_time_logged_in' => empty($user_settings['total_time_logged_in']) ? 0 : $user_settings['total_time_logged_in'],
692
		'buddies' => !empty($modSettings['enable_buddylist']) && !empty($user_settings['buddy_list']) ? explode(',', $user_settings['buddy_list']) : array(),
693
		'ignoreboards' => !empty($user_settings['ignore_boards']) && !empty($modSettings['allow_ignore_boards']) ? explode(',', $user_settings['ignore_boards']) : array(),
694
		'ignoreusers' => !empty($user_settings['pm_ignore_list']) ? explode(',', $user_settings['pm_ignore_list']) : array(),
695
		'warning' => isset($user_settings['warning']) ? $user_settings['warning'] : 0,
696
		'permissions' => array(),
697
	);
698
	$user_info['groups'] = array_unique($user_info['groups']);
699
700
	// Make sure that the last item in the ignore boards array is valid. If the list was too long it could have an ending comma that could cause problems.
701
	if (!empty($user_info['ignoreboards']) && empty($user_info['ignoreboards'][$tmp = count($user_info['ignoreboards']) - 1]))
702
		unset($user_info['ignoreboards'][$tmp]);
703
704
	// Allow the user to change their language.
705
	if (!empty($modSettings['userLanguage']))
706
	{
707
		$languages = getLanguages();
708
709
		// Is it valid?
710
		if (!empty($_GET['language']) && isset($languages[strtr($_GET['language'], './\\:', '____')]))
711
		{
712
			$user_info['language'] = strtr($_GET['language'], './\\:', '____');
713
714
			// Make it permanent for members.
715
			if (!empty($user_info['id']))
716
				updateMemberData($user_info['id'], array('lngfile' => $user_info['language']));
717
			else
718
				$_SESSION['language'] = $user_info['language'];
719
		}
720
		elseif (!empty($_SESSION['language']) && isset($languages[strtr($_SESSION['language'], './\\:', '____')]))
721
			$user_info['language'] = strtr($_SESSION['language'], './\\:', '____');
722
	}
723
724
	// Just build this here, it makes it easier to change/use - administrators can see all boards.
725
	if ($user_info['is_admin'])
726
		$user_info['query_see_board'] = '1=1';
727
	// Otherwise just the groups in $user_info['groups'].
728
	else
729
		$user_info['query_see_board'] = '((FIND_IN_SET(' . implode(', b.member_groups) != 0 OR FIND_IN_SET(', $user_info['groups']) . ', b.member_groups) != 0)' . (!empty($modSettings['deny_boards_access']) ? ' AND (FIND_IN_SET(' . implode(', b.deny_member_groups) = 0 AND FIND_IN_SET(', $user_info['groups']) . ', b.deny_member_groups) = 0)' : '') . (isset($user_info['mod_cache']) ? ' OR ' . $user_info['mod_cache']['mq'] : '') . ')';
730
731
	// Build the list of boards they WANT to see.
732
	// This will take the place of query_see_boards in certain spots, so it better include the boards they can see also
733
734
	// If they aren't ignoring any boards then they want to see all the boards they can see
735
	if (empty($user_info['ignoreboards']))
736
		$user_info['query_wanna_see_board'] = $user_info['query_see_board'];
737
	// Ok I guess they don't want to see all the boards
738
	else
739
		$user_info['query_wanna_see_board'] = '(' . $user_info['query_see_board'] . ' AND b.id_board NOT IN (' . implode(',', $user_info['ignoreboards']) . '))';
740
741
	call_integration_hook('integrate_user_info');
742
}
743
744
/**
745
 * Check for moderators and see if they have access to the board.
746
 * What it does:
747
 * - sets up the $board_info array for current board information.
748
 * - if cache is enabled, the $board_info array is stored in cache.
749
 * - redirects to appropriate post if only message id is requested.
750
 * - is only used when inside a topic or board.
751
 * - determines the local moderators for the board.
752
 * - adds group id 3 if the user is a local moderator for the board they are in.
753
 * - prevents access if user is not in proper group nor a local moderator of the board.
754
 */
755
function loadBoard()
756
{
757
	global $txt, $scripturl, $context, $modSettings;
758
	global $board_info, $board, $topic, $user_info, $smcFunc;
759
760
	// Assume they are not a moderator.
761
	$user_info['is_mod'] = false;
762
	$context['user']['is_mod'] = &$user_info['is_mod'];
763
764
	// Start the linktree off empty..
765
	$context['linktree'] = array();
766
767
	// Have they by chance specified a message id but nothing else?
768
	if (empty($_REQUEST['action']) && empty($topic) && empty($board) && !empty($_REQUEST['msg']))
769
	{
770
		// Make sure the message id is really an int.
771
		$_REQUEST['msg'] = (int) $_REQUEST['msg'];
772
773
		// Looking through the message table can be slow, so try using the cache first.
774
		if (($topic = cache_get_data('msg_topic-' . $_REQUEST['msg'], 120)) === null)
775
		{
776
			$request = $smcFunc['db_query']('', '
777
				SELECT id_topic
778
				FROM {db_prefix}messages
779
				WHERE id_msg = {int:id_msg}
780
				LIMIT 1',
781
				array(
782
					'id_msg' => $_REQUEST['msg'],
783
				)
784
			);
785
786
			// So did it find anything?
787
			if ($smcFunc['db_num_rows']($request))
788
			{
789
				list ($topic) = $smcFunc['db_fetch_row']($request);
790
				$smcFunc['db_free_result']($request);
791
				// Save save save.
792
				cache_put_data('msg_topic-' . $_REQUEST['msg'], $topic, 120);
793
			}
794
		}
795
796
		// Remember redirection is the key to avoiding fallout from your bosses.
797
		if (!empty($topic))
798
			redirectexit('topic=' . $topic . '.msg' . $_REQUEST['msg'] . '#msg' . $_REQUEST['msg']);
799
		else
800
		{
801
			loadPermissions();
802
			loadTheme();
803
			fatal_lang_error('topic_gone', false);
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
804
		}
805
	}
806
807
	// Load this board only if it is specified.
808
	if (empty($board) && empty($topic))
809
	{
810
		$board_info = array('moderators' => array(), 'moderator_groups' => array());
811
		return;
812
	}
813
814
	if (!empty($modSettings['cache_enable']) && (empty($topic) || $modSettings['cache_enable'] >= 3))
815
	{
816
		// @todo SLOW?
817
		if (!empty($topic))
818
			$temp = cache_get_data('topic_board-' . $topic, 120);
819
		else
820
			$temp = cache_get_data('board-' . $board, 120);
821
822
		if (!empty($temp))
823
		{
824
			$board_info = $temp;
825
			$board = $board_info['id'];
826
		}
827
	}
828
829
	if (empty($temp))
830
	{
831
		$request = $smcFunc['db_query']('', '
832
			SELECT
833
				c.id_cat, b.name AS bname, b.description, b.num_topics, b.member_groups, b.deny_member_groups,
834
				b.id_parent, c.name AS cname, COALESCE(mg.id_group, 0) AS id_moderator_group, mg.group_name,
835
				COALESCE(mem.id_member, 0) AS id_moderator,
836
				mem.real_name' . (!empty($topic) ? ', b.id_board' : '') . ', b.child_level,
837
				b.id_theme, b.override_theme, b.count_posts, b.id_profile, b.redirect,
838
				b.unapproved_topics, b.unapproved_posts' . (!empty($topic) ? ', t.approved, t.id_member_started' : '') . '
839
			FROM {db_prefix}boards AS b' . (!empty($topic) ? '
840
				INNER JOIN {db_prefix}topics AS t ON (t.id_topic = {int:current_topic})' : '') . '
841
				LEFT JOIN {db_prefix}categories AS c ON (c.id_cat = b.id_cat)
842
				LEFT JOIN {db_prefix}moderator_groups AS modgs ON (modgs.id_board = {raw:board_link})
843
				LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = modgs.id_group)
844
				LEFT JOIN {db_prefix}moderators AS mods ON (mods.id_board = {raw:board_link})
845
				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = mods.id_member)
846
			WHERE b.id_board = {raw:board_link}',
847
			array(
848
				'current_topic' => $topic,
849
				'board_link' => empty($topic) ? $smcFunc['db_quote']('{int:current_board}', array('current_board' => $board)) : 't.id_board',
850
			)
851
		);
852
		// If there aren't any, skip.
853
		if ($smcFunc['db_num_rows']($request) > 0)
854
		{
855
			$row = $smcFunc['db_fetch_assoc']($request);
856
857
			// Set the current board.
858
			if (!empty($row['id_board']))
859
				$board = $row['id_board'];
860
861
			// Basic operating information. (globals... :/)
862
			$board_info = array(
863
				'id' => $board,
864
				'moderators' => array(),
865
				'moderator_groups' => array(),
866
				'cat' => array(
867
					'id' => $row['id_cat'],
868
					'name' => $row['cname']
869
				),
870
				'name' => $row['bname'],
871
				'description' => $row['description'],
872
				'num_topics' => $row['num_topics'],
873
				'unapproved_topics' => $row['unapproved_topics'],
874
				'unapproved_posts' => $row['unapproved_posts'],
875
				'unapproved_user_topics' => 0,
876
				'parent_boards' => getBoardParents($row['id_parent']),
877
				'parent' => $row['id_parent'],
878
				'child_level' => $row['child_level'],
879
				'theme' => $row['id_theme'],
880
				'override_theme' => !empty($row['override_theme']),
881
				'profile' => $row['id_profile'],
882
				'redirect' => $row['redirect'],
883
				'recycle' => !empty($modSettings['recycle_enable']) && !empty($modSettings['recycle_board']) && $modSettings['recycle_board'] == $board,
884
				'posts_count' => empty($row['count_posts']),
885
				'cur_topic_approved' => empty($topic) || $row['approved'],
886
				'cur_topic_starter' => empty($topic) ? 0 : $row['id_member_started'],
887
			);
888
889
			// Load the membergroups allowed, and check permissions.
890
			$board_info['groups'] = $row['member_groups'] == '' ? array() : explode(',', $row['member_groups']);
891
			$board_info['deny_groups'] = $row['deny_member_groups'] == '' ? array() : explode(',', $row['deny_member_groups']);
892
893
			do
894
			{
895 View Code Duplication
				if (!empty($row['id_moderator']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
896
					$board_info['moderators'][$row['id_moderator']] = array(
897
						'id' => $row['id_moderator'],
898
						'name' => $row['real_name'],
899
						'href' => $scripturl . '?action=profile;u=' . $row['id_moderator'],
900
						'link' => '<a href="' . $scripturl . '?action=profile;u=' . $row['id_moderator'] . '">' . $row['real_name'] . '</a>'
901
					);
902
903 View Code Duplication
				if (!empty($row['id_moderator_group']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
904
					$board_info['moderator_groups'][$row['id_moderator_group']] = array(
905
						'id' => $row['id_moderator_group'],
906
						'name' => $row['group_name'],
907
						'href' => $scripturl . '?action=groups;sa=members;group=' . $row['id_moderator_group'],
908
						'link' => '<a href="' . $scripturl . '?action=groups;sa=members;group=' . $row['id_moderator_group'] . '">' . $row['group_name'] . '</a>'
909
					);
910
			}
911
			while ($row = $smcFunc['db_fetch_assoc']($request));
912
913
			// If the board only contains unapproved posts and the user isn't an approver then they can't see any topics.
914
			// If that is the case do an additional check to see if they have any topics waiting to be approved.
915
			if ($board_info['num_topics'] == 0 && $modSettings['postmod_active'] && !allowedTo('approve_posts'))
916
			{
917
				// Free the previous result
918
				$smcFunc['db_free_result']($request);
919
920
				// @todo why is this using id_topic?
921
				// @todo Can this get cached?
922
				$request = $smcFunc['db_query']('', '
923
					SELECT COUNT(id_topic)
924
					FROM {db_prefix}topics
925
					WHERE id_member_started={int:id_member}
926
						AND approved = {int:unapproved}
927
						AND id_board = {int:board}',
928
					array(
929
						'id_member' => $user_info['id'],
930
						'unapproved' => 0,
931
						'board' => $board,
932
					)
933
				);
934
935
				list ($board_info['unapproved_user_topics']) = $smcFunc['db_fetch_row']($request);
936
			}
937
938
			if (!empty($modSettings['cache_enable']) && (empty($topic) || $modSettings['cache_enable'] >= 3))
939
			{
940
				// @todo SLOW?
941
				if (!empty($topic))
942
					cache_put_data('topic_board-' . $topic, $board_info, 120);
943
				cache_put_data('board-' . $board, $board_info, 120);
944
			}
945
		}
946
		else
947
		{
948
			// Otherwise the topic is invalid, there are no moderators, etc.
949
			$board_info = array(
950
				'moderators' => array(),
951
				'moderator_groups' => array(),
952
				'error' => 'exist'
953
			);
954
			$topic = null;
955
			$board = 0;
956
		}
957
		$smcFunc['db_free_result']($request);
958
	}
959
960
	if (!empty($topic))
961
		$_GET['board'] = (int) $board;
962
963
	if (!empty($board))
964
	{
965
		// Get this into an array of keys for array_intersect
966
		$moderator_groups = array_keys($board_info['moderator_groups']);
967
968
		// Now check if the user is a moderator.
969
		$user_info['is_mod'] = isset($board_info['moderators'][$user_info['id']]) || count(array_intersect($user_info['groups'], $moderator_groups)) != 0;
970
971 View Code Duplication
		if (count(array_intersect($user_info['groups'], $board_info['groups'])) == 0 && !$user_info['is_admin'])
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
972
			$board_info['error'] = 'access';
973 View Code Duplication
		if (!empty($modSettings['deny_boards_access']) && count(array_intersect($user_info['groups'], $board_info['deny_groups'])) != 0 && !$user_info['is_admin'])
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
974
			$board_info['error'] = 'access';
975
976
		// Build up the linktree.
977
		$context['linktree'] = array_merge(
978
			$context['linktree'],
979
			array(array(
980
				'url' => $scripturl . '#c' . $board_info['cat']['id'],
981
				'name' => $board_info['cat']['name']
982
			)),
983
			array_reverse($board_info['parent_boards']),
984
			array(array(
985
				'url' => $scripturl . '?board=' . $board . '.0',
986
				'name' => $board_info['name']
987
			))
988
		);
989
	}
990
991
	// Set the template contextual information.
992
	$context['user']['is_mod'] = &$user_info['is_mod'];
993
	$context['current_topic'] = $topic;
994
	$context['current_board'] = $board;
995
996
	// No posting in redirection boards!
997
	if (!empty($_REQUEST['action']) && $_REQUEST['action'] == 'post' && !empty($board_info['redirect']))
998
		$board_info['error'] == 'post_in_redirect';
999
1000
	// Hacker... you can't see this topic, I'll tell you that. (but moderators can!)
1001
	if (!empty($board_info['error']) && (!empty($modSettings['deny_boards_access']) || $board_info['error'] != 'access' || !$user_info['is_mod']))
1002
	{
1003
		// The permissions and theme need loading, just to make sure everything goes smoothly.
1004
		loadPermissions();
1005
		loadTheme();
1006
1007
		$_GET['board'] = '';
1008
		$_GET['topic'] = '';
1009
1010
		// The linktree should not give the game away mate!
1011
		$context['linktree'] = array(
1012
			array(
1013
				'url' => $scripturl,
1014
				'name' => $context['forum_name_html_safe']
1015
			)
1016
		);
1017
1018
		// If it's a prefetching agent or we're requesting an attachment.
1019
		if ((isset($_SERVER['HTTP_X_MOZ']) && $_SERVER['HTTP_X_MOZ'] == 'prefetch') || (!empty($_REQUEST['action']) && $_REQUEST['action'] === 'dlattach'))
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison === seems to always evaluate to false as the types of $_REQUEST['action'] (integer) and 'dlattach' (string) can never be identical. Maybe you want to use a loose comparison == instead?
Loading history...
1020
		{
1021
			ob_end_clean();
1022
			header('HTTP/1.1 403 Forbidden');
1023
			die;
1024
		}
1025
		elseif ($board_info['error'] == 'post_in_redirect')
1026
		{
1027
			// Slightly different error message here...
1028
			fatal_lang_error('cannot_post_redirect', false);
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1029
		}
1030
		elseif ($user_info['is_guest'])
1031
		{
1032
			loadLanguage('Errors');
1033
			is_not_guest($txt['topic_gone']);
1034
		}
1035
		else
1036
			fatal_lang_error('topic_gone', false);
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1037
	}
1038
1039
	if ($user_info['is_mod'])
1040
		$user_info['groups'][] = 3;
1041
}
1042
1043
/**
1044
 * Load this user's permissions.
1045
 */
1046
function loadPermissions()
1047
{
1048
	global $user_info, $board, $board_info, $modSettings, $smcFunc, $sourcedir;
1049
1050
	if ($user_info['is_admin'])
1051
	{
1052
		banPermissions();
1053
		return;
1054
	}
1055
1056
	if (!empty($modSettings['cache_enable']))
1057
	{
1058
		$cache_groups = $user_info['groups'];
1059
		asort($cache_groups);
1060
		$cache_groups = implode(',', $cache_groups);
1061
		// If it's a spider then cache it different.
1062
		if ($user_info['possibly_robot'])
1063
			$cache_groups .= '-spider';
1064
1065
		if ($modSettings['cache_enable'] >= 2 && !empty($board) && ($temp = cache_get_data('permissions:' . $cache_groups . ':' . $board, 240)) != null && time() - 240 > $modSettings['settings_updated'])
1066
		{
1067
			list ($user_info['permissions']) = $temp;
1068
			banPermissions();
1069
1070
			return;
1071
		}
1072
		elseif (($temp = cache_get_data('permissions:' . $cache_groups, 240)) != null && time() - 240 > $modSettings['settings_updated'])
1073
			list ($user_info['permissions'], $removals) = $temp;
1074
	}
1075
1076
	// If it is detected as a robot, and we are restricting permissions as a special group - then implement this.
1077
	$spider_restrict = $user_info['possibly_robot'] && !empty($modSettings['spider_group']) ? ' OR (id_group = {int:spider_group} AND add_deny = 0)' : '';
1078
1079
	if (empty($user_info['permissions']))
1080
	{
1081
		// Get the general permissions.
1082
		$request = $smcFunc['db_query']('', '
1083
			SELECT permission, add_deny
1084
			FROM {db_prefix}permissions
1085
			WHERE id_group IN ({array_int:member_groups})
1086
				' . $spider_restrict,
1087
			array(
1088
				'member_groups' => $user_info['groups'],
1089
				'spider_group' => !empty($modSettings['spider_group']) ? $modSettings['spider_group'] : 0,
1090
			)
1091
		);
1092
		$removals = array();
1093 View Code Duplication
		while ($row = $smcFunc['db_fetch_assoc']($request))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1094
		{
1095
			if (empty($row['add_deny']))
1096
				$removals[] = $row['permission'];
1097
			else
1098
				$user_info['permissions'][] = $row['permission'];
1099
		}
1100
		$smcFunc['db_free_result']($request);
1101
1102
		if (isset($cache_groups))
1103
			cache_put_data('permissions:' . $cache_groups, array($user_info['permissions'], $removals), 240);
1104
	}
1105
1106
	// Get the board permissions.
1107
	if (!empty($board))
1108
	{
1109
		// Make sure the board (if any) has been loaded by loadBoard().
1110
		if (!isset($board_info['profile']))
1111
			fatal_lang_error('no_board');
1112
1113
		$request = $smcFunc['db_query']('', '
1114
			SELECT permission, add_deny
1115
			FROM {db_prefix}board_permissions
1116
			WHERE (id_group IN ({array_int:member_groups})
1117
				' . $spider_restrict . ')
1118
				AND id_profile = {int:id_profile}',
1119
			array(
1120
				'member_groups' => $user_info['groups'],
1121
				'id_profile' => $board_info['profile'],
1122
				'spider_group' => !empty($modSettings['spider_group']) ? $modSettings['spider_group'] : 0,
1123
			)
1124
		);
1125 View Code Duplication
		while ($row = $smcFunc['db_fetch_assoc']($request))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1126
		{
1127
			if (empty($row['add_deny']))
1128
				$removals[] = $row['permission'];
0 ignored issues
show
Bug introduced by
The variable $removals does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1129
			else
1130
				$user_info['permissions'][] = $row['permission'];
1131
		}
1132
		$smcFunc['db_free_result']($request);
1133
	}
1134
1135
	// Remove all the permissions they shouldn't have ;).
1136
	if (!empty($modSettings['permission_enable_deny']))
1137
		$user_info['permissions'] = array_diff($user_info['permissions'], $removals);
1138
1139
	if (isset($cache_groups) && !empty($board) && $modSettings['cache_enable'] >= 2)
1140
		cache_put_data('permissions:' . $cache_groups . ':' . $board, array($user_info['permissions'], null), 240);
1141
1142
	// Banned?  Watch, don't touch..
1143
	banPermissions();
1144
1145
	// Load the mod cache so we can know what additional boards they should see, but no sense in doing it for guests
1146
	if (!$user_info['is_guest'])
1147
	{
1148
		if (!isset($_SESSION['mc']) || $_SESSION['mc']['time'] <= $modSettings['settings_updated'])
1149
		{
1150
			require_once($sourcedir . '/Subs-Auth.php');
1151
			rebuildModCache();
1152
		}
1153
		else
1154
			$user_info['mod_cache'] = $_SESSION['mc'];
1155
1156
		// This is a useful phantom permission added to the current user, and only the current user while they are logged in.
1157
		// For example this drastically simplifies certain changes to the profile area.
1158
		$user_info['permissions'][] = 'is_not_guest';
1159
		// And now some backwards compatibility stuff for mods and whatnot that aren't expecting the new permissions.
1160
		$user_info['permissions'][] = 'profile_view_own';
1161
		if (in_array('profile_view', $user_info['permissions']))
1162
			$user_info['permissions'][] = 'profile_view_any';
1163
	}
1164
}
1165
1166
/**
1167
 * Loads an array of users' data by ID or member_name.
1168
 *
1169
 * @param array|string $users An array of users by id or name or a single username/id
1170
 * @param bool $is_name Whether $users contains names
1171
 * @param string $set What kind of data to load (normal, profile, minimal)
1172
 * @return array The ids of the members loaded
1173
 */
1174
function loadMemberData($users, $is_name = false, $set = 'normal')
1175
{
1176
	global $user_profile, $modSettings, $board_info, $smcFunc, $context;
1177
	global $image_proxy_enabled, $image_proxy_secret, $boardurl;
1178
1179
	// Can't just look for no users :P.
1180
	if (empty($users))
1181
		return array();
1182
1183
	// Pass the set value
1184
	$context['loadMemberContext_set'] = $set;
1185
1186
	// Make sure it's an array.
1187
	$users = !is_array($users) ? array($users) : array_unique($users);
1188
	$loaded_ids = array();
1189
1190
	if (!$is_name && !empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= 3)
1191
	{
1192
		$users = array_values($users);
1193
		for ($i = 0, $n = count($users); $i < $n; $i++)
1194
		{
1195
			$data = cache_get_data('member_data-' . $set . '-' . $users[$i], 240);
1196
			if ($data == null)
1197
				continue;
1198
1199
			$loaded_ids[] = $data['id_member'];
1200
			$user_profile[$data['id_member']] = $data;
1201
			unset($users[$i]);
1202
		}
1203
	}
1204
1205
	// Used by default
1206
	$select_columns = '
1207
			COALESCE(lo.log_time, 0) AS is_online, COALESCE(a.id_attach, 0) AS id_attach, a.filename, a.attachment_type,
1208
			mem.signature, mem.personal_text, mem.avatar, mem.id_member, mem.member_name,
1209
			mem.real_name, mem.email_address, mem.date_registered, mem.website_title, mem.website_url,
1210
			mem.birthdate, mem.member_ip, mem.member_ip2, mem.posts, mem.last_login, mem.id_post_group, mem.lngfile, mem.id_group, mem.time_offset, mem.show_online,
1211
			mg.online_color AS member_group_color, COALESCE(mg.group_name, {string:blank_string}) AS member_group,
1212
			pg.online_color AS post_group_color, COALESCE(pg.group_name, {string:blank_string}) AS post_group,
1213
			mem.is_activated, mem.warning, ' . (!empty($modSettings['titlesEnable']) ? 'mem.usertitle, ' : '') . '
1214
			CASE WHEN mem.id_group = 0 OR mg.icons = {string:blank_string} THEN pg.icons ELSE mg.icons END AS icons';
1215
	$select_tables = '
1216
			LEFT JOIN {db_prefix}log_online AS lo ON (lo.id_member = mem.id_member)
1217
			LEFT JOIN {db_prefix}attachments AS a ON (a.id_member = mem.id_member)
1218
			LEFT JOIN {db_prefix}membergroups AS pg ON (pg.id_group = mem.id_post_group)
1219
			LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = mem.id_group)';
1220
1221
	// We add or replace according the the set
1222
	switch ($set)
1223
	{
1224
		case 'normal':
1225
			$select_columns .= ', mem.buddy_list,  mem.additional_groups';
1226
			break;
1227
		case 'profile':
1228
			$select_columns .= ', mem.additional_groups, mem.id_theme, mem.pm_ignore_list, mem.pm_receive_from,
1229
			mem.time_format, mem.timezone, mem.secret_question, mem.smiley_set, mem.tfa_secret,
1230
			mem.total_time_logged_in, lo.url, mem.ignore_boards, mem.password_salt, mem.pm_prefs, mem.buddy_list, mem.alerts';
1231
			break;
1232
		case 'minimal':
1233
			$select_columns = '
1234
			mem.id_member, mem.member_name, mem.real_name, mem.email_address, mem.date_registered,
1235
			mem.posts, mem.last_login, mem.member_ip, mem.member_ip2, mem.lngfile, mem.id_group';
1236
			$select_tables = '';
1237
			break;
1238
		default:
1239
			trigger_error('loadMemberData(): Invalid member data set \'' . $set . '\'', E_USER_WARNING);
1240
	}
1241
1242
	// Allow mods to easily add to the selected member data
1243
	call_integration_hook('integrate_load_member_data', array(&$select_columns, &$select_tables, &$set));
1244
1245
	if (!empty($users))
1246
	{
1247
		// Load the member's data.
1248
		$request = $smcFunc['db_query']('', '
1249
			SELECT' . $select_columns . '
1250
			FROM {db_prefix}members AS mem' . $select_tables . '
1251
			WHERE mem.' . ($is_name ? 'member_name' : 'id_member') . ' IN ({' . ($is_name ? 'array_string' : 'array_int') . ':users})',
1252
			array(
1253
				'blank_string' => '',
1254
				'users' => $users,
1255
			)
1256
		);
1257
		$new_loaded_ids = array();
1258
		while ($row = $smcFunc['db_fetch_assoc']($request))
1259
		{
1260
			// Take care of proxying avatar if required, do this here for maximum reach
1261 View Code Duplication
			if ($image_proxy_enabled && !empty($row['avatar']) && stripos($row['avatar'], 'http://') !== false)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1262
				$row['avatar'] = $boardurl . '/proxy.php?request=' . urlencode($row['avatar']) . '&hash=' . md5($row['avatar'] . $image_proxy_secret);
1263
1264
			if ( isset($row['member_ip']) )
1265
				$row['member_ip'] = inet_dtop($row['member_ip']);
1266
			if ( isset($row['member_ip2']) )
1267
				$row['member_ip2'] = inet_dtop($row['member_ip2']);
1268
			$new_loaded_ids[] = $row['id_member'];
1269
			$loaded_ids[] = $row['id_member'];
1270
			$row['options'] = array();
1271
			$user_profile[$row['id_member']] = $row;
1272
		}
1273
		$smcFunc['db_free_result']($request);
1274
	}
1275
1276 View Code Duplication
	if (!empty($new_loaded_ids) && $set !== 'minimal')
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1277
	{
1278
		$request = $smcFunc['db_query']('', '
1279
			SELECT *
1280
			FROM {db_prefix}themes
1281
			WHERE id_member IN ({array_int:loaded_ids})',
1282
			array(
1283
				'loaded_ids' => $new_loaded_ids,
1284
			)
1285
		);
1286
		while ($row = $smcFunc['db_fetch_assoc']($request))
1287
			$user_profile[$row['id_member']]['options'][$row['variable']] = $row['value'];
1288
		$smcFunc['db_free_result']($request);
1289
	}
1290
1291
	$additional_mods = array();
1292
1293
	// Are any of these users in groups assigned to moderate this board?
1294
	if (!empty($loaded_ids) && !empty($board_info['moderator_groups']) && $set === 'normal')
1295
	{
1296
		foreach ($loaded_ids as $a_member)
1297
		{
1298
			if (!empty($user_profile[$a_member]['additional_groups']))
1299
				$groups = array_merge(array($user_profile[$a_member]['id_group']), explode(',', $user_profile[$a_member]['additional_groups']));
1300
			else
1301
				$groups = array($user_profile[$a_member]['id_group']);
1302
1303
			$temp = array_intersect($groups, array_keys($board_info['moderator_groups']));
1304
1305
			if (!empty($temp))
1306
			{
1307
				$additional_mods[] = $a_member;
1308
			}
1309
		}
1310
	}
1311
1312
	if (!empty($new_loaded_ids) && !empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= 3)
1313
	{
1314
		for ($i = 0, $n = count($new_loaded_ids); $i < $n; $i++)
1315
			cache_put_data('member_data-' . $set . '-' . $new_loaded_ids[$i], $user_profile[$new_loaded_ids[$i]], 240);
1316
	}
1317
1318
	// Are we loading any moderators?  If so, fix their group data...
1319
	if (!empty($loaded_ids) && (!empty($board_info['moderators']) || !empty($board_info['moderator_groups'])) && $set === 'normal' && count($temp_mods = array_merge(array_intersect($loaded_ids, array_keys($board_info['moderators'])), $additional_mods)) !== 0)
1320
	{
1321 View Code Duplication
		if (($row = cache_get_data('moderator_group_info', 480)) == null)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1322
		{
1323
			$request = $smcFunc['db_query']('', '
1324
				SELECT group_name AS member_group, online_color AS member_group_color, icons
1325
				FROM {db_prefix}membergroups
1326
				WHERE id_group = {int:moderator_group}
1327
				LIMIT 1',
1328
				array(
1329
					'moderator_group' => 3,
1330
				)
1331
			);
1332
			$row = $smcFunc['db_fetch_assoc']($request);
1333
			$smcFunc['db_free_result']($request);
1334
1335
			cache_put_data('moderator_group_info', $row, 480);
1336
		}
1337
1338
		foreach ($temp_mods as $id)
1339
		{
1340
			// By popular demand, don't show admins or global moderators as moderators.
1341
			if ($user_profile[$id]['id_group'] != 1 && $user_profile[$id]['id_group'] != 2)
1342
				$user_profile[$id]['member_group'] = $row['member_group'];
1343
1344
			// If the Moderator group has no color or icons, but their group does... don't overwrite.
1345
			if (!empty($row['icons']))
1346
				$user_profile[$id]['icons'] = $row['icons'];
1347
			if (!empty($row['member_group_color']))
1348
				$user_profile[$id]['member_group_color'] = $row['member_group_color'];
1349
		}
1350
	}
1351
1352
	return $loaded_ids;
1353
}
1354
1355
/**
1356
 * Loads the user's basic values... meant for template/theme usage.
1357
 *
1358
 * @param int $user The ID of a user previously loaded by {@link loadMemberData()}
1359
 * @param bool $display_custom_fields Whether or not to display custom profile fields
1360
 * @return boolean Whether or not the data was loaded successfully
1361
 */
1362
function loadMemberContext($user, $display_custom_fields = false)
1363
{
1364
	global $memberContext, $user_profile, $txt, $scripturl, $user_info;
1365
	global $context, $modSettings, $settings, $smcFunc;
1366
	static $dataLoaded = array();
1367
	static $loadedLanguages = array();
1368
1369
	// If this person's data is already loaded, skip it.
1370
	if (isset($dataLoaded[$user]))
1371
		return true;
1372
1373
	// We can't load guests or members not loaded by loadMemberData()!
1374
	if ($user == 0)
1375
		return false;
1376
	if (!isset($user_profile[$user]))
1377
	{
1378
		trigger_error('loadMemberContext(): member id ' . $user . ' not previously loaded by loadMemberData()', E_USER_WARNING);
1379
		return false;
1380
	}
1381
1382
	// Well, it's loaded now anyhow.
1383
	$dataLoaded[$user] = true;
1384
	$profile = $user_profile[$user];
1385
1386
	// Censor everything.
1387
	censorText($profile['signature']);
1388
	censorText($profile['personal_text']);
1389
1390
	// Set things up to be used before hand.
1391
	$profile['signature'] = str_replace(array("\n", "\r"), array('<br>', ''), $profile['signature']);
1392
	$profile['signature'] = parse_bbc($profile['signature'], true, 'sig' . $profile['id_member']);
1393
1394
	$profile['is_online'] = (!empty($profile['show_online']) || allowedTo('moderate_forum')) && $profile['is_online'] > 0;
1395
	$profile['icons'] = empty($profile['icons']) ? array('', '') : explode('#', $profile['icons']);
1396
	// Setup the buddy status here (One whole in_array call saved :P)
1397
	$profile['buddy'] = in_array($profile['id_member'], $user_info['buddies']);
1398
	$buddy_list = !empty($profile['buddy_list']) ? explode(',', $profile['buddy_list']) : array();
1399
1400
	//We need a little fallback for the membergroup icons. If it doesn't exist in the current theme, fallback to default theme
1401
	if (isset($profile['icons'][1]) && file_exists($settings['actual_theme_dir'] . '/images/membericons/' . $profile['icons'][1])) //icon is set and exists
1402
		$group_icon_url = $settings['images_url'] . '/membericons/' . $profile['icons'][1];
1403
	elseif (isset($profile['icons'][1])) //icon is set and doesn't exist, fallback to default
1404
		$group_icon_url = $settings['default_images_url'] . '/membericons/' . $profile['icons'][1];
1405
	else //not set, bye bye
1406
		$group_icon_url = '';
1407
1408
	// These minimal values are always loaded
1409
	$memberContext[$user] = array(
1410
		'username' => $profile['member_name'],
1411
		'name' => $profile['real_name'],
1412
		'id' => $profile['id_member'],
1413
		'href' => $scripturl . '?action=profile;u=' . $profile['id_member'],
1414
		'link' => '<a href="' . $scripturl . '?action=profile;u=' . $profile['id_member'] . '" title="' . $txt['profile_of'] . ' ' . $profile['real_name'] . '" '. (!empty($modSettings['onlineEnable']) ? 'class="pm_icon"' : '').'>' . $profile['real_name'] . '</a>',
1415
		'email' => $profile['email_address'],
1416
		'show_email' => !$user_info['is_guest'] && ($user_info['id'] == $profile['id_member'] || allowedTo('moderate_forum')),
1417
		'registered' => empty($profile['date_registered']) ? $txt['not_applicable'] : timeformat($profile['date_registered']),
1418
		'registered_timestamp' => empty($profile['date_registered']) ? 0 : forum_time(true, $profile['date_registered']),
1419
	);
1420
1421
	// If the set isn't minimal then load the monstrous array.
1422
	if ($context['loadMemberContext_set'] != 'minimal')
1423
	{
1424
		// Go the extra mile and load the user's native language name.
1425
		if (empty($loadedLanguages))
1426
			$loadedLanguages = getLanguages();
1427
1428
		$memberContext[$user] += array(
1429
			'username_color' => '<span '. (!empty($profile['member_group_color']) ? 'style="color:'. $profile['member_group_color'] .';"' : '') .'>'. $profile['member_name'] .'</span>',
1430
			'name_color' => '<span '. (!empty($profile['member_group_color']) ? 'style="color:'. $profile['member_group_color'] .';"' : '') .'>'. $profile['real_name'] .'</span>',
1431
			'link_color' => '<a href="' . $scripturl . '?action=profile;u=' . $profile['id_member'] . '" title="' . $txt['profile_of'] . ' ' . $profile['real_name'] . '" '. (!empty($profile['member_group_color']) ? 'style="color:'. $profile['member_group_color'] .';"' : '') .'>' . $profile['real_name'] . '</a>',
1432
			'is_buddy' => $profile['buddy'],
1433
			'is_reverse_buddy' => in_array($user_info['id'], $buddy_list),
1434
			'buddies' => $buddy_list,
1435
			'title' => !empty($modSettings['titlesEnable']) ? $profile['usertitle'] : '',
1436
			'blurb' => $profile['personal_text'],
1437
			'website' => array(
1438
				'title' => $profile['website_title'],
1439
				'url' => $profile['website_url'],
1440
			),
1441
			'birth_date' => empty($profile['birthdate']) || $profile['birthdate'] === '0001-01-01' ? '0000-00-00' : (substr($profile['birthdate'], 0, 4) === '0004' ? '0000' . substr($profile['birthdate'], 4) : $profile['birthdate']),
1442
			'signature' => $profile['signature'],
1443
			'real_posts' => $profile['posts'],
1444
			'posts' => $profile['posts'] > 500000 ? $txt['geek'] : comma_format($profile['posts']),
1445
			'last_login' => empty($profile['last_login']) ? $txt['never'] : timeformat($profile['last_login']),
1446
			'last_login_timestamp' => empty($profile['last_login']) ? 0 : forum_time(0, $profile['last_login']),
0 ignored issues
show
Documentation introduced by
0 is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1447
			'ip' => $smcFunc['htmlspecialchars']($profile['member_ip']),
1448
			'ip2' => $smcFunc['htmlspecialchars']($profile['member_ip2']),
1449
			'online' => array(
1450
				'is_online' => $profile['is_online'],
1451
				'text' => $smcFunc['htmlspecialchars']($txt[$profile['is_online'] ? 'online' : 'offline']),
1452
				'member_online_text' => sprintf($txt[$profile['is_online'] ? 'member_is_online' : 'member_is_offline'], $smcFunc['htmlspecialchars']($profile['real_name'])),
1453
				'href' => $scripturl . '?action=pm;sa=send;u=' . $profile['id_member'],
1454
				'link' => '<a href="' . $scripturl . '?action=pm;sa=send;u=' . $profile['id_member'] . '">' . $txt[$profile['is_online'] ? 'online' : 'offline'] . '</a>',
1455
				'label' => $txt[$profile['is_online'] ? 'online' : 'offline']
1456
			),
1457
			'language' => !empty($loadedLanguages[$profile['lngfile']]) && !empty($loadedLanguages[$profile['lngfile']]['name']) ? $loadedLanguages[$profile['lngfile']]['name'] : $smcFunc['ucwords'](strtr($profile['lngfile'], array('_' => ' ', '-utf8' => ''))),
1458
			'is_activated' => isset($profile['is_activated']) ? $profile['is_activated'] : 1,
1459
			'is_banned' => isset($profile['is_activated']) ? $profile['is_activated'] >= 10 : 0,
1460
			'options' => $profile['options'],
1461
			'is_guest' => false,
1462
			'group' => $profile['member_group'],
1463
			'group_color' => $profile['member_group_color'],
1464
			'group_id' => $profile['id_group'],
1465
			'post_group' => $profile['post_group'],
1466
			'post_group_color' => $profile['post_group_color'],
1467
			'group_icons' => str_repeat('<img src="' . str_replace('$language', $context['user']['language'], isset($profile['icons'][1]) ? $group_icon_url : '') . '" alt="*">', empty($profile['icons'][0]) || empty($profile['icons'][1]) ? 0 : $profile['icons'][0]),
1468
			'warning' => $profile['warning'],
1469
			'warning_status' => !empty($modSettings['warning_mute']) && $modSettings['warning_mute'] <= $profile['warning'] ? 'mute' : (!empty($modSettings['warning_moderate']) && $modSettings['warning_moderate'] <= $profile['warning'] ? 'moderate' : (!empty($modSettings['warning_watch']) && $modSettings['warning_watch'] <= $profile['warning'] ? 'watch' : (''))),
1470
			'local_time' => timeformat(time() + ($profile['time_offset'] - $user_info['time_offset']) * 3600, false),
1471
			'custom_fields' => array(),
1472
		);
1473
	}
1474
1475
	// If the set isn't minimal then load their avatar as well.
1476
	if ($context['loadMemberContext_set'] != 'minimal')
1477
	{
1478
		if (!empty($modSettings['gravatarOverride']) || (!empty($modSettings['gravatarEnabled']) && stristr($profile['avatar'], 'gravatar://')))
1479
		{
1480
			if (!empty($modSettings['gravatarAllowExtraEmail']) && stristr($profile['avatar'], 'gravatar://') && strlen($profile['avatar']) > 11)
1481
				$image = get_gravatar_url($smcFunc['substr']($profile['avatar'], 11));
1482
			else
1483
				$image = get_gravatar_url($profile['email_address']);
1484
		}
1485
		else
1486
		{
1487
			// So it's stored in the member table?
1488
			if (!empty($profile['avatar']))
1489
			{
1490
				$image = (stristr($profile['avatar'], 'http://') || stristr($profile['avatar'], 'https://')) ? $profile['avatar'] : $modSettings['avatar_url'] . '/' . $profile['avatar'];
1491
			}
1492
			elseif (!empty($profile['filename']))
1493
				$image = $modSettings['custom_avatar_url'] . '/' . $profile['filename'];
1494
			// Right... no avatar...use the default one
1495
			else
1496
				$image = $modSettings['avatar_url'] . '/default.png';
1497
		}
1498
		if (!empty($image))
1499
			$memberContext[$user]['avatar'] = array(
1500
				'name' => $profile['avatar'],
1501
				'image' => '<img class="avatar" src="' . $image . '" alt="avatar_'. $profile['member_name'].'">',
1502
				'href' => $image,
1503
				'url' => $image,
1504
			);
1505
	}
1506
1507
	// Are we also loading the members custom fields into context?
1508
	if ($display_custom_fields && !empty($modSettings['displayFields']))
1509
	{
1510
		$memberContext[$user]['custom_fields'] = array();
1511
1512
		if (!isset($context['display_fields']))
1513
			$context['display_fields'] = smf_json_decode($modSettings['displayFields'], true);
1514
1515
		foreach ($context['display_fields'] as $custom)
1516
		{
1517
			if (!isset($custom['col_name']) || trim($custom['col_name']) == '' || empty($profile['options'][$custom['col_name']]))
1518
				continue;
1519
1520
			$value = $profile['options'][$custom['col_name']];
1521
1522
			// Don't show the "disabled" option for the "gender" field.
1523
			if ($custom['col_name'] == 'cust_gender' && $value == 'Disabled')
1524
				continue;
1525
1526
			// BBC?
1527
			if ($custom['bbc'])
1528
				$value = parse_bbc($value);
1529
			// ... or checkbox?
1530
			elseif (isset($custom['type']) && $custom['type'] == 'check')
1531
				$value = $value ? $txt['yes'] : $txt['no'];
1532
1533
			// Enclosing the user input within some other text?
1534 View Code Duplication
			if (!empty($custom['enclose']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1535
				$value = strtr($custom['enclose'], array(
1536
					'{SCRIPTURL}' => $scripturl,
1537
					'{IMAGES_URL}' => $settings['images_url'],
1538
					'{DEFAULT_IMAGES_URL}' => $settings['default_images_url'],
1539
					'{INPUT}' => $value,
1540
				));
1541
1542
			$memberContext[$user]['custom_fields'][] = array(
1543
				'title' => !empty($custom['title']) ? $custom['title'] : $custom['col_name'],
1544
				'col_name' => $custom['col_name'],
1545
				'value' => un_htmlspecialchars($value),
1546
				'placement' => !empty($custom['placement']) ? $custom['placement'] : 0,
1547
			);
1548
		}
1549
	}
1550
1551
	call_integration_hook('integrate_member_context', array(&$memberContext[$user], $user, $display_custom_fields));
1552
	return true;
1553
}
1554
1555
/**
1556
 * Loads the user's custom profile fields
1557
 *
1558
 * @param integer|array $users A single user ID or an array of user IDs
1559
 * @param string|array $params Either a string or an array of strings with profile field names
1560
 * @return array|boolean An array of data about the fields and their values or false if nothing was loaded
1561
 */
1562
function loadMemberCustomFields($users, $params)
1563
{
1564
	global $smcFunc, $txt, $scripturl, $settings;
1565
1566
	// Do not waste my time...
1567
	if (empty($users) || empty($params))
1568
		return false;
1569
1570
	// Make sure it's an array.
1571
	$users = !is_array($users) ? array($users) : array_unique($users);
1572
	$params = !is_array($params) ? array($params) : array_unique($params);
1573
	$return = array();
1574
1575
	$request = $smcFunc['db_query']('', '
1576
		SELECT c.id_field, c.col_name, c.field_name, c.field_desc, c.field_type, c.field_order, c.field_length, c.field_options, c.mask, show_reg,
1577
		c.show_display, c.show_profile, c.private, c.active, c.bbc, c.can_search, c.default_value, c.enclose, c.placement, t.variable, t.value, t.id_member
1578
		FROM {db_prefix}themes AS t
1579
			LEFT JOIN {db_prefix}custom_fields AS c ON (c.col_name = t.variable)
1580
		WHERE id_member IN ({array_int:loaded_ids})
1581
			AND variable IN ({array_string:params})
1582
		ORDER BY field_order',
1583
		array(
1584
			'loaded_ids' => $users,
1585
			'params' => $params,
1586
		)
1587
	);
1588
1589
	while ($row = $smcFunc['db_fetch_assoc']($request))
1590
	{
1591
		// BBC?
1592
		if (!empty($row['bbc']))
1593
			$row['value'] = parse_bbc($row['value']);
1594
1595
		// ... or checkbox?
1596
		elseif (isset($row['type']) && $row['type'] == 'check')
1597
			$row['value'] = !empty($row['value']) ? $txt['yes'] : $txt['no'];
1598
1599
		// Enclosing the user input within some other text?
1600
		if (!empty($row['enclose']))
1601
			$row['value'] = strtr($row['enclose'], array(
1602
				'{SCRIPTURL}' => $scripturl,
1603
				'{IMAGES_URL}' => $settings['images_url'],
1604
				'{DEFAULT_IMAGES_URL}' => $settings['default_images_url'],
1605
				'{INPUT}' => un_htmlspecialchars($row['value']),
1606
			));
1607
1608
		// Send a simple array if there is just 1 param
1609
		if (count($params) == 1)
1610
			$return[$row['id_member']] = $row;
1611
1612
		// More than 1? knock yourself out...
1613
		else
1614
		{
1615
			if (!isset($return[$row['id_member']]))
1616
				$return[$row['id_member']] = array();
1617
1618
			$return[$row['id_member']][$row['variable']] = $row;
1619
		}
1620
	}
1621
1622
	$smcFunc['db_free_result']($request);
1623
1624
	return !empty($return) ? $return : false;
1625
}
1626
1627
/**
1628
 * Loads information about what browser the user is viewing with and places it in $context
1629
 *  - uses the class from {@link Class-BrowserDetect.php}
1630
 */
1631
function detectBrowser()
1632
{
1633
	// Load the current user's browser of choice
1634
	$detector = new browser_detector;
1635
	$detector->detectBrowser();
1636
}
1637
1638
/**
1639
 * Are we using this browser?
1640
 *
1641
 * Wrapper function for detectBrowser
1642
 * @param string $browser The browser we are checking for.
1643
 * @return bool Whether or not the current browser is what we're looking for
1644
*/
1645
function isBrowser($browser)
1646
{
1647
	global $context;
1648
1649
	// Don't know any browser!
1650
	if (empty($context['browser']))
1651
		detectBrowser();
1652
1653
	return !empty($context['browser'][$browser]) || !empty($context['browser']['is_' . $browser]) ? true : false;
1654
}
1655
1656
/**
1657
 * Load a theme, by ID.
1658
 *
1659
 * @param int $id_theme The ID of the theme to load
1660
 * @param bool $initialize Whether or not to initialize a bunch of theme-related variables/settings
1661
 */
1662
function loadTheme($id_theme = 0, $initialize = true)
1663
{
1664
	global $user_info, $user_settings, $board_info, $boarddir, $maintenance;
1665
	global $txt, $boardurl, $scripturl, $mbname, $modSettings;
1666
	global $context, $settings, $options, $sourcedir, $ssi_theme, $smcFunc, $language, $board, $image_proxy_enabled;
1667
1668
	// The theme was specified by parameter.
1669
	if (!empty($id_theme))
1670
		$id_theme = (int) $id_theme;
1671
	// The theme was specified by REQUEST.
1672 View Code Duplication
	elseif (!empty($_REQUEST['theme']) && (!empty($modSettings['theme_allow']) || allowedTo('admin_forum')))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1673
	{
1674
		$id_theme = (int) $_REQUEST['theme'];
1675
		$_SESSION['id_theme'] = $id_theme;
1676
	}
1677
	// The theme was specified by REQUEST... previously.
1678 View Code Duplication
	elseif (!empty($_SESSION['id_theme']) && (!empty($modSettings['theme_allow']) || allowedTo('admin_forum')))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1679
		$id_theme = (int) $_SESSION['id_theme'];
1680
	// The theme is just the user's choice. (might use ?board=1;theme=0 to force board theme.)
1681
	elseif (!empty($user_info['theme']) && !isset($_REQUEST['theme']))
1682
		$id_theme = $user_info['theme'];
1683
	// The theme was specified by the board.
1684
	elseif (!empty($board_info['theme']))
1685
		$id_theme = $board_info['theme'];
1686
	// The theme is the forum's default.
1687
	else
1688
		$id_theme = $modSettings['theme_guests'];
1689
1690
	// Verify the id_theme... no foul play.
1691
	// Always allow the board specific theme, if they are overriding.
1692
	if (!empty($board_info['theme']) && $board_info['override_theme'])
1693
		$id_theme = $board_info['theme'];
1694
	// If they have specified a particular theme to use with SSI allow it to be used.
1695
	elseif (!empty($ssi_theme) && $id_theme == $ssi_theme)
1696
		$id_theme = (int) $id_theme;
1697
	elseif (!empty($modSettings['enableThemes']) && !allowedTo('admin_forum'))
1698
	{
1699
		$themes = explode(',', $modSettings['enableThemes']);
1700
		if (!in_array($id_theme, $themes))
1701
			$id_theme = $modSettings['theme_guests'];
1702
		else
1703
			$id_theme = (int) $id_theme;
1704
	}
1705
	else
1706
		$id_theme = (int) $id_theme;
1707
1708
	$member = empty($user_info['id']) ? -1 : $user_info['id'];
1709
1710
	// Disable image proxy if we don't have SSL enabled
1711
	if (empty($modSettings['force_ssl']) || $modSettings['force_ssl'] < 2)
1712
		$image_proxy_enabled = false;
1713
1714
	if (!empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= 2 && ($temp = cache_get_data('theme_settings-' . $id_theme . ':' . $member, 60)) != null && time() - 60 > $modSettings['settings_updated'])
1715
	{
1716
		$themeData = $temp;
1717
		$flag = true;
1718
	}
1719
	elseif (($temp = cache_get_data('theme_settings-' . $id_theme, 90)) != null && time() - 60 > $modSettings['settings_updated'])
1720
		$themeData = $temp + array($member => array());
1721
	else
1722
		$themeData = array(-1 => array(), 0 => array(), $member => array());
1723
1724
	if (empty($flag))
1725
	{
1726
		// Load variables from the current or default theme, global or this user's.
1727
		$result = $smcFunc['db_query']('', '
1728
			SELECT variable, value, id_member, id_theme
1729
			FROM {db_prefix}themes
1730
			WHERE id_member' . (empty($themeData[0]) ? ' IN (-1, 0, {int:id_member})' : ' = {int:id_member}') . '
1731
				AND id_theme' . ($id_theme == 1 ? ' = {int:id_theme}' : ' IN ({int:id_theme}, 1)'),
1732
			array(
1733
				'id_theme' => $id_theme,
1734
				'id_member' => $member,
1735
			)
1736
		);
1737
		// Pick between $settings and $options depending on whose data it is.
1738
		while ($row = $smcFunc['db_fetch_assoc']($result))
1739
		{
1740
			// There are just things we shouldn't be able to change as members.
1741
			if ($row['id_member'] != 0 && in_array($row['variable'], array('actual_theme_url', 'actual_images_url', 'base_theme_dir', 'base_theme_url', 'default_images_url', 'default_theme_dir', 'default_theme_url', 'default_template', 'images_url', 'number_recent_posts', 'smiley_sets_default', 'theme_dir', 'theme_id', 'theme_layers', 'theme_templates', 'theme_url')))
1742
				continue;
1743
1744
			// If this is the theme_dir of the default theme, store it.
1745
			if (in_array($row['variable'], array('theme_dir', 'theme_url', 'images_url')) && $row['id_theme'] == '1' && empty($row['id_member']))
1746
				$themeData[0]['default_' . $row['variable']] = $row['value'];
1747
1748
			// If this isn't set yet, is a theme option, or is not the default theme..
1749
			if (!isset($themeData[$row['id_member']][$row['variable']]) || $row['id_theme'] != '1')
1750
				$themeData[$row['id_member']][$row['variable']] = substr($row['variable'], 0, 5) == 'show_' ? $row['value'] == '1' : $row['value'];
1751
		}
1752
		$smcFunc['db_free_result']($result);
1753
1754
		if (!empty($themeData[-1]))
1755
			foreach ($themeData[-1] as $k => $v)
1756
			{
1757
				if (!isset($themeData[$member][$k]))
1758
					$themeData[$member][$k] = $v;
1759
			}
1760
1761
		if (!empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= 2)
1762
			cache_put_data('theme_settings-' . $id_theme . ':' . $member, $themeData, 60);
1763
		// Only if we didn't already load that part of the cache...
1764
		elseif (!isset($temp))
1765
			cache_put_data('theme_settings-' . $id_theme, array(-1 => $themeData[-1], 0 => $themeData[0]), 90);
1766
	}
1767
1768
	$settings = $themeData[0];
1769
	$options = $themeData[$member];
1770
1771
	$settings['theme_id'] = $id_theme;
1772
1773
	$settings['actual_theme_url'] = $settings['theme_url'];
1774
	$settings['actual_images_url'] = $settings['images_url'];
1775
	$settings['actual_theme_dir'] = $settings['theme_dir'];
1776
1777
	$settings['template_dirs'] = array();
1778
	// This theme first.
1779
	$settings['template_dirs'][] = $settings['theme_dir'];
1780
1781
	// Based on theme (if there is one).
1782
	if (!empty($settings['base_theme_dir']))
1783
		$settings['template_dirs'][] = $settings['base_theme_dir'];
1784
1785
	// Lastly the default theme.
1786 View Code Duplication
	if ($settings['theme_dir'] != $settings['default_theme_dir'])
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1787
		$settings['template_dirs'][] = $settings['default_theme_dir'];
1788
1789
	if (!$initialize)
1790
		return;
1791
1792
	// Check to see if we're forcing SSL
1793
	if (!empty($modSettings['force_ssl']) && $modSettings['force_ssl'] == 2 && empty($maintenance) &&
1794
		(!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] == 'off') && SMF != 'SSI')
1795
		redirectexit(strtr($_SERVER['REQUEST_URL'], array('http://' => 'https://')));
1796
1797
	// Check to see if they're accessing it from the wrong place.
1798
	if (isset($_SERVER['HTTP_HOST']) || isset($_SERVER['SERVER_NAME']))
1799
	{
1800
		$detected_url = isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on' ? 'https://' : 'http://';
1801
		$detected_url .= empty($_SERVER['HTTP_HOST']) ? $_SERVER['SERVER_NAME'] . (empty($_SERVER['SERVER_PORT']) || $_SERVER['SERVER_PORT'] == '80' ? '' : ':' . $_SERVER['SERVER_PORT']) : $_SERVER['HTTP_HOST'];
1802
		$temp = preg_replace('~/' . basename($scripturl) . '(/.+)?$~', '', strtr(dirname($_SERVER['PHP_SELF']), '\\', '/'));
1803
		if ($temp != '/')
1804
			$detected_url .= $temp;
1805
	}
1806
	if (isset($detected_url) && $detected_url != $boardurl)
1807
	{
1808
		// Try #1 - check if it's in a list of alias addresses.
1809
		if (!empty($modSettings['forum_alias_urls']))
1810
		{
1811
			$aliases = explode(',', $modSettings['forum_alias_urls']);
1812
1813
			foreach ($aliases as $alias)
1814
			{
1815
				// Rip off all the boring parts, spaces, etc.
1816
				if ($detected_url == trim($alias) || strtr($detected_url, array('http://' => '', 'https://' => '')) == trim($alias))
1817
					$do_fix = true;
1818
			}
1819
		}
1820
1821
		// Hmm... check #2 - is it just different by a www?  Send them to the correct place!!
1822
		if (empty($do_fix) && strtr($detected_url, array('://' => '://www.')) == $boardurl && (empty($_GET) || count($_GET) == 1) && SMF != 'SSI')
1823
		{
1824
			// Okay, this seems weird, but we don't want an endless loop - this will make $_GET not empty ;).
1825
			if (empty($_GET))
1826
				redirectexit('wwwRedirect');
1827
			else
1828
			{
1829
				list ($k, $v) = each($_GET);
1830
1831
				if ($k != 'wwwRedirect')
1832
					redirectexit('wwwRedirect;' . $k . '=' . $v);
1833
			}
1834
		}
1835
1836
		// #3 is just a check for SSL...
1837
		if (strtr($detected_url, array('https://' => 'http://')) == $boardurl)
1838
			$do_fix = true;
1839
1840
		// Okay, #4 - perhaps it's an IP address?  We're gonna want to use that one, then. (assuming it's the IP or something...)
1841
		if (!empty($do_fix) || preg_match('~^http[s]?://(?:[\d\.:]+|\[[\d:]+\](?::\d+)?)(?:$|/)~', $detected_url) == 1)
1842
		{
1843
			// Caching is good ;).
1844
			$oldurl = $boardurl;
1845
1846
			// Fix $boardurl and $scripturl.
1847
			$boardurl = $detected_url;
1848
			$scripturl = strtr($scripturl, array($oldurl => $boardurl));
1849
			$_SERVER['REQUEST_URL'] = strtr($_SERVER['REQUEST_URL'], array($oldurl => $boardurl));
1850
1851
			// Fix the theme urls...
1852
			$settings['theme_url'] = strtr($settings['theme_url'], array($oldurl => $boardurl));
1853
			$settings['default_theme_url'] = strtr($settings['default_theme_url'], array($oldurl => $boardurl));
1854
			$settings['actual_theme_url'] = strtr($settings['actual_theme_url'], array($oldurl => $boardurl));
1855
			$settings['images_url'] = strtr($settings['images_url'], array($oldurl => $boardurl));
1856
			$settings['default_images_url'] = strtr($settings['default_images_url'], array($oldurl => $boardurl));
1857
			$settings['actual_images_url'] = strtr($settings['actual_images_url'], array($oldurl => $boardurl));
1858
1859
			// And just a few mod settings :).
1860
			$modSettings['smileys_url'] = strtr($modSettings['smileys_url'], array($oldurl => $boardurl));
1861
			$modSettings['avatar_url'] = strtr($modSettings['avatar_url'], array($oldurl => $boardurl));
1862
1863
			// Clean up after loadBoard().
1864
			if (isset($board_info['moderators']))
1865
			{
1866
				foreach ($board_info['moderators'] as $k => $dummy)
1867
				{
1868
					$board_info['moderators'][$k]['href'] = strtr($dummy['href'], array($oldurl => $boardurl));
1869
					$board_info['moderators'][$k]['link'] = strtr($dummy['link'], array('"' . $oldurl => '"' . $boardurl));
1870
				}
1871
			}
1872
			foreach ($context['linktree'] as $k => $dummy)
1873
				$context['linktree'][$k]['url'] = strtr($dummy['url'], array($oldurl => $boardurl));
1874
		}
1875
	}
1876
	// Set up the contextual user array.
1877
	if (!empty($user_info))
1878
	{
1879
		$context['user'] = array(
1880
			'id' => $user_info['id'],
1881
			'is_logged' => !$user_info['is_guest'],
1882
			'is_guest' => &$user_info['is_guest'],
1883
			'is_admin' => &$user_info['is_admin'],
1884
			'is_mod' => &$user_info['is_mod'],
1885
			// A user can mod if they have permission to see the mod center, or they are a board/group/approval moderator.
1886
			'can_mod' => allowedTo('access_mod_center') || (!$user_info['is_guest'] && ($user_info['mod_cache']['gq'] != '0=1' || $user_info['mod_cache']['bq'] != '0=1' || ($modSettings['postmod_active'] && !empty($user_info['mod_cache']['ap'])))),
1887
			'username' => $user_info['username'],
1888
			'language' => $user_info['language'],
1889
			'email' => $user_info['email'],
1890
			'ignoreusers' => $user_info['ignoreusers'],
1891
		);
1892
		if (!$context['user']['is_guest'])
1893
			$context['user']['name'] = $user_info['name'];
1894 View Code Duplication
		elseif ($context['user']['is_guest'] && !empty($txt['guest_title']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1895
			$context['user']['name'] = $txt['guest_title'];
1896
1897
		// Determine the current smiley set.
1898
		$user_info['smiley_set'] = (!in_array($user_info['smiley_set'], explode(',', $modSettings['smiley_sets_known'])) && $user_info['smiley_set'] != 'none') || empty($modSettings['smiley_sets_enable']) ? (!empty($settings['smiley_sets_default']) ? $settings['smiley_sets_default'] : $modSettings['smiley_sets_default']) : $user_info['smiley_set'];
1899
		$context['user']['smiley_set'] = $user_info['smiley_set'];
1900
	}
1901
	else
1902
	{
1903
		$context['user'] = array(
1904
			'id' => -1,
1905
			'is_logged' => false,
1906
			'is_guest' => true,
1907
			'is_mod' => false,
1908
			'can_mod' => false,
1909
			'name' => $txt['guest_title'],
1910
			'language' => $language,
1911
			'email' => '',
1912
			'ignoreusers' => array(),
1913
		);
1914
	}
1915
1916
	// Some basic information...
1917
	if (!isset($context['html_headers']))
1918
		$context['html_headers'] = '';
1919
	if (!isset($context['javascript_files']))
1920
		$context['javascript_files'] = array();
1921
	if (!isset($context['css_files']))
1922
		$context['css_files'] = array();
1923
	if (!isset($context['css_header']))
1924
		$context['css_header'] = array();
1925
	if (!isset($context['javascript_inline']))
1926
		$context['javascript_inline'] = array('standard' => array(), 'defer' => array());
1927
	if (!isset($context['javascript_vars']))
1928
		$context['javascript_vars'] = array();
1929
1930
	$context['login_url'] = (!empty($modSettings['force_ssl']) && $modSettings['force_ssl'] < 2 ? strtr($scripturl, array('http://' => 'https://')) : $scripturl) . '?action=login2';
1931
	$context['menu_separator'] = !empty($settings['use_image_buttons']) ? ' ' : ' | ';
1932
	$context['session_var'] = $_SESSION['session_var'];
1933
	$context['session_id'] = $_SESSION['session_value'];
1934
	$context['forum_name'] = $mbname;
1935
	$context['forum_name_html_safe'] = $smcFunc['htmlspecialchars']($context['forum_name']);
1936
	$context['header_logo_url_html_safe'] = empty($settings['header_logo_url']) ? '' : $smcFunc['htmlspecialchars']($settings['header_logo_url']);
1937
	$context['current_action'] = isset($_REQUEST['action']) ? $smcFunc['htmlspecialchars']($_REQUEST['action']) : null;
1938
	$context['current_subaction'] = isset($_REQUEST['sa']) ? $_REQUEST['sa'] : null;
1939
	$context['can_register'] = empty($modSettings['registration_method']) || $modSettings['registration_method'] != 3;
1940
	if (isset($modSettings['load_average']))
1941
		$context['load_average'] = $modSettings['load_average'];
1942
1943
	// Detect the browser. This is separated out because it's also used in attachment downloads
1944
	detectBrowser();
1945
1946
	// Set the top level linktree up.
1947
	array_unshift($context['linktree'], array(
1948
		'url' => $scripturl,
1949
		'name' => $context['forum_name_html_safe']
1950
	));
1951
1952
	// This allows sticking some HTML on the page output - useful for controls.
1953
	$context['insert_after_template'] = '';
1954
1955
	if (!isset($txt))
1956
		$txt = array();
1957
1958
	$simpleActions = array(
1959
		'findmember',
1960
		'helpadmin',
1961
		'printpage',
1962
		'spellcheck',
1963
	);
1964
1965
	// Parent action => array of areas
1966
	$simpleAreas = array(
1967
		'profile' => array('popup', 'alerts_popup',),
1968
	);
1969
1970
	// Parent action => array of subactions
1971
	$simpleSubActions = array(
1972
		'pm' => array('popup',),
1973
		'signup' => array('usernamecheck'),
1974
	);
1975
1976
	// Actions that specifically uses XML output.
1977
	$xmlActions = array(
1978
		'quotefast',
1979
		'jsmodify',
1980
		'xmlhttp',
1981
		'post2',
1982
		'suggest',
1983
		'stats',
1984
		'notifytopic',
1985
		'notifyboard',
1986
	);
1987
1988
	call_integration_hook('integrate_simple_actions', array(&$simpleActions, &$simpleAreas, &$simpleSubActions, &$xmlActions));
1989
1990
	$context['simple_action'] = in_array($context['current_action'], $simpleActions) ||
1991
	(isset($simpleAreas[$context['current_action']]) && isset($_REQUEST['area']) && in_array($_REQUEST['area'], $simpleAreas[$context['current_action']])) ||
1992
	(isset($simpleSubActions[$context['current_action']]) && in_array($context['current_subaction'], $simpleSubActions[$context['current_action']]));
1993
1994
	// Output is fully XML, so no need for the index template.
1995
	if (isset($_REQUEST['xml']) && in_array($context['current_action'], $xmlActions))
1996
	{
1997
		loadLanguage('index+Modifications');
1998
		loadTemplate('Xml');
1999
		$context['template_layers'] = array();
2000
	}
2001
2002
	// These actions don't require the index template at all.
2003
	elseif (!empty($context['simple_action']))
2004
	{
2005
		loadLanguage('index+Modifications');
2006
		$context['template_layers'] = array();
2007
	}
2008
2009
	else
2010
	{
2011
		// Custom templates to load, or just default?
2012
		if (isset($settings['theme_templates']))
2013
			$templates = explode(',', $settings['theme_templates']);
2014
		else
2015
			$templates = array('index');
2016
2017
		// Load each template...
2018
		foreach ($templates as $template)
2019
			loadTemplate($template);
2020
2021
		// ...and attempt to load their associated language files.
2022
		$required_files = implode('+', array_merge($templates, array('Modifications')));
2023
		loadLanguage($required_files, '', false);
2024
2025
		// Custom template layers?
2026
		if (isset($settings['theme_layers']))
2027
			$context['template_layers'] = explode(',', $settings['theme_layers']);
2028
		else
2029
			$context['template_layers'] = array('html', 'body');
2030
	}
2031
2032
	// Initialize the theme.
2033
	loadSubTemplate('init', 'ignore');
0 ignored issues
show
Documentation introduced by
'ignore' is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
2034
2035
	// Allow overriding the board wide time/number formats.
2036
	if (empty($user_settings['time_format']) && !empty($txt['time_format']))
2037
		$user_info['time_format'] = $txt['time_format'];
2038
2039
	// Set the character set from the template.
2040
	$context['character_set'] = empty($modSettings['global_character_set']) ? $txt['lang_character_set'] : $modSettings['global_character_set'];
2041
	$context['utf8'] = $context['character_set'] === 'UTF-8';
2042
	$context['right_to_left'] = !empty($txt['lang_rtl']);
2043
2044
	// Guests may still need a name.
2045 View Code Duplication
	if ($context['user']['is_guest'] && empty($context['user']['name']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
2046
		$context['user']['name'] = $txt['guest_title'];
2047
2048
	// Any theme-related strings that need to be loaded?
2049
	if (!empty($settings['require_theme_strings']))
2050
		loadLanguage('ThemeStrings', '', false);
2051
2052
	// Make a special URL for the language.
2053
	$settings['lang_images_url'] = $settings['images_url'] . '/' . (!empty($txt['image_lang']) ? $txt['image_lang'] : $user_info['language']);
2054
2055
	// And of course, let's load the default CSS file.
2056
	loadCSSFile('index.css', array('minimize' => true), 'smf_index');
2057
2058
	// Here is my luvly Responsive CSS
2059
	loadCSSFile('responsive.css', array('force_current' => false, 'validate' => true, 'minimize' => true), 'smf_responsive');
2060
2061
	if ($context['right_to_left'])
2062
		loadCSSFile('rtl.css', array(), 'smf_rtl');
2063
2064
	// We allow theme variants, because we're cool.
2065
	$context['theme_variant'] = '';
2066
	$context['theme_variant_url'] = '';
2067
	if (!empty($settings['theme_variants']))
2068
	{
2069
		// Overriding - for previews and that ilk.
2070
		if (!empty($_REQUEST['variant']))
2071
			$_SESSION['id_variant'] = $_REQUEST['variant'];
2072
		// User selection?
2073
		if (empty($settings['disable_user_variant']) || allowedTo('admin_forum'))
2074
			$context['theme_variant'] = !empty($_SESSION['id_variant']) ? $_SESSION['id_variant'] : (!empty($options['theme_variant']) ? $options['theme_variant'] : '');
2075
		// If not a user variant, select the default.
2076
		if ($context['theme_variant'] == '' || !in_array($context['theme_variant'], $settings['theme_variants']))
2077
			$context['theme_variant'] = !empty($settings['default_variant']) && in_array($settings['default_variant'], $settings['theme_variants']) ? $settings['default_variant'] : $settings['theme_variants'][0];
2078
2079
		// Do this to keep things easier in the templates.
2080
		$context['theme_variant'] = '_' . $context['theme_variant'];
2081
		$context['theme_variant_url'] = $context['theme_variant'] . '/';
2082
2083
		if (!empty($context['theme_variant']))
2084
		{
2085
			loadCSSFile('index' . $context['theme_variant'] . '.css', array(), 'smf_index' . $context['theme_variant']);
2086
			if ($context['right_to_left'])
2087
				loadCSSFile('rtl' . $context['theme_variant'] . '.css', array(), 'smf_rtl' . $context['theme_variant']);
2088
		}
2089
	}
2090
2091
	// Let's be compatible with old themes!
2092
	if (!function_exists('template_html_above') && in_array('html', $context['template_layers']))
2093
		$context['template_layers'] = array('main');
2094
2095
	$context['tabindex'] = 1;
2096
2097
	// Compatibility.
2098
	if (!isset($settings['theme_version']))
2099
		$modSettings['memberCount'] = $modSettings['totalMembers'];
2100
2101
	// Default JS variables for use in every theme
2102
	$context['javascript_vars'] = array(
2103
		'smf_theme_url' => '"' . $settings['theme_url'] . '"',
2104
		'smf_default_theme_url' => '"' . $settings['default_theme_url'] . '"',
2105
		'smf_images_url' => '"' . $settings['images_url'] . '"',
2106
		'smf_smileys_url' => '"' . $modSettings['smileys_url'] . '"',
2107
		'smf_scripturl' => '"' . $scripturl . '"',
2108
		'smf_iso_case_folding' => $context['server']['iso_case_folding'] ? 'true' : 'false',
2109
		'smf_charset' => '"' . $context['character_set'] . '"',
2110
		'smf_session_id' => '"' . $context['session_id'] . '"',
2111
		'smf_session_var' => '"' . $context['session_var'] . '"',
2112
		'smf_member_id' => $context['user']['id'],
2113
		'ajax_notification_text' => JavaScriptEscape($txt['ajax_in_progress']),
2114
		'help_popup_heading_text' => JavaScriptEscape($txt['help_popup']),
2115
	);
2116
2117
	// Add the JQuery library to the list of files to load.
2118
	if (isset($modSettings['jquery_source']) && $modSettings['jquery_source'] == 'cdn')
2119
		loadJavaScriptFile('https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js', array('external' => true), 'smf_jquery');
2120
2121
	elseif (isset($modSettings['jquery_source']) && $modSettings['jquery_source'] == 'local')
2122
		loadJavaScriptFile('jquery-2.1.4.min.js', array('seed' => false), 'smf_jquery');
2123
2124
	elseif (isset($modSettings['jquery_source'], $modSettings['jquery_custom']) && $modSettings['jquery_source'] == 'custom')
2125
		loadJavaScriptFile($modSettings['jquery_custom'], array(), 'smf_jquery');
2126
2127
	// Auto loading? template_javascript() will take care of the local half of this.
2128
	else
2129
		loadJavaScriptFile('https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js', array('external' => true), 'smf_jquery');
2130
2131
	// Queue our JQuery plugins!
2132
	loadJavaScriptFile('smf_jquery_plugins.js', array('minimize' => true), 'smf_jquery_plugins');
2133
	if (!$user_info['is_guest'])
2134
	{
2135
		loadJavaScriptFile('jquery.custom-scrollbar.js', array(), 'smf_jquery_scrollbar');
2136
		loadCSSFile('jquery.custom-scrollbar.css', array('force_current' => false, 'validate' => true), 'smf_scrollbar');
2137
	}
2138
2139
	// script.js and theme.js, always required, so always add them! Makes index.template.php cleaner and all.
2140
	loadJavaScriptFile('script.js', array('defer' => false, 'minimize' => true), 'smf_script');
2141
	loadJavaScriptFile('theme.js', array('minimize' => true), 'smf_theme');
2142
2143
	// If we think we have mail to send, let's offer up some possibilities... robots get pain (Now with scheduled task support!)
2144
	if ((!empty($modSettings['mail_next_send']) && $modSettings['mail_next_send'] < time() && empty($modSettings['mail_queue_use_cron'])) || empty($modSettings['next_task_time']) || $modSettings['next_task_time'] < time())
2145
	{
2146
		if (isBrowser('possibly_robot'))
2147
		{
2148
			// @todo Maybe move this somewhere better?!
2149
			require_once($sourcedir . '/ScheduledTasks.php');
2150
2151
			// What to do, what to do?!
2152
			if (empty($modSettings['next_task_time']) || $modSettings['next_task_time'] < time())
2153
				AutoTask();
2154
			else
2155
				ReduceMailQueue();
2156
		}
2157
		else
2158
		{
2159
			$type = empty($modSettings['next_task_time']) || $modSettings['next_task_time'] < time() ? 'task' : 'mailq';
2160
			$ts = $type == 'mailq' ? $modSettings['mail_next_send'] : $modSettings['next_task_time'];
2161
2162
			addInlineJavaScript('
2163
		function smfAutoTask()
2164
		{
2165
			$.get(smf_scripturl + "?scheduled=' . $type . ';ts=' . $ts . '");
2166
		}
2167
		window.setTimeout("smfAutoTask();", 1);');
2168
		}
2169
	}
2170
2171
	// And we should probably trigger the cron too.
2172
	if (empty($modSettings['cron_is_real_cron']))
2173
	{
2174
		$ts = time();
2175
		$ts -= $ts % 15;
2176
		addInlineJavaScript('
2177
	function triggerCron()
2178
	{
2179
		$.get(' . JavaScriptEscape($boardurl) . ' + "/cron.php?ts=' . $ts . '");
2180
	}
2181
	window.setTimeout(triggerCron, 1);', true);
2182
	}
2183
2184
	// Filter out the restricted boards from the linktree
2185
	if (!$user_info['is_admin'] && !empty($board))
2186
	{
2187
		foreach ($context['linktree'] as $k => $element)
2188
		{
2189
			if (!empty($element['groups']) &&
2190
				(count(array_intersect($user_info['groups'], $element['groups'])) == 0 ||
2191
				(!empty($modSettings['deny_boards_access']) && count(array_intersect($user_info['groups'], $element['deny_groups'])) != 0)))
2192
			{
2193
				$context['linktree'][$k]['name'] = $txt['restricted_board'];
2194
				$context['linktree'][$k]['extra_before'] = '<i>';
2195
				$context['linktree'][$k]['extra_after'] = '</i>';
2196
				unset($context['linktree'][$k]['url']);
2197
			}
2198
		}
2199
	}
2200
2201
	// Any files to include at this point?
2202 View Code Duplication
	if (!empty($modSettings['integrate_theme_include']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
2203
	{
2204
		$theme_includes = explode(',', $modSettings['integrate_theme_include']);
2205
		foreach ($theme_includes as $include)
2206
		{
2207
			$include = strtr(trim($include), array('$boarddir' => $boarddir, '$sourcedir' => $sourcedir, '$themedir' => $settings['theme_dir']));
2208
			if (file_exists($include))
2209
				require_once($include);
2210
		}
2211
	}
2212
2213
	// Call load theme integration functions.
2214
	call_integration_hook('integrate_load_theme');
2215
2216
	// We are ready to go.
2217
	$context['theme_loaded'] = true;
2218
}
2219
2220
/**
2221
 * Load a template - if the theme doesn't include it, use the default.
2222
 * What this function does:
2223
 *  - loads a template file with the name template_name from the current, default, or base theme.
2224
 *  - detects a wrong default theme directory and tries to work around it.
2225
 *
2226
 * @uses the template_include() function to include the file.
2227
 * @param string $template_name The name of the template to load
2228
 * @param array|string $style_sheets The name of a single stylesheet or an array of names of stylesheets to load
2229
 * @param bool $fatal If true, dies with an error message if the template cannot be found
2230
 * @return boolean Whether or not the template was loaded
2231
 */
2232
function loadTemplate($template_name, $style_sheets = array(), $fatal = true)
2233
{
2234
	global $context, $settings, $txt, $scripturl, $boarddir, $db_show_debug;
2235
2236
	// Do any style sheets first, cause we're easy with those.
2237
	if (!empty($style_sheets))
2238
	{
2239
		if (!is_array($style_sheets))
2240
			$style_sheets = array($style_sheets);
2241
2242
		foreach ($style_sheets as $sheet)
2243
			loadCSSFile($sheet . '.css', array(), $sheet);
2244
	}
2245
2246
	// No template to load?
2247
	if ($template_name === false)
2248
		return true;
2249
2250
	$loaded = false;
2251
	foreach ($settings['template_dirs'] as $template_dir)
2252
	{
2253
		if (file_exists($template_dir . '/' . $template_name . '.template.php'))
2254
		{
2255
			$loaded = true;
2256
			template_include($template_dir . '/' . $template_name . '.template.php', true);
2257
			break;
2258
		}
2259
	}
2260
2261
	if ($loaded)
2262
	{
2263
		if ($db_show_debug === true)
2264
			$context['debug']['templates'][] = $template_name . ' (' . basename($template_dir) . ')';
0 ignored issues
show
Bug introduced by
The variable $template_dir seems to be defined by a foreach iteration on line 2251. Are you sure the iterator is never empty, otherwise this variable is not defined?

It seems like you are relying on a variable being defined by an iteration:

foreach ($a as $b) {
}

// $b is defined here only if $a has elements, for example if $a is array()
// then $b would not be defined here. To avoid that, we recommend to set a
// default value for $b.


// Better
$b = 0; // or whatever default makes sense in your context
foreach ($a as $b) {
}

// $b is now guaranteed to be defined here.
Loading history...
2265
2266
		// If they have specified an initialization function for this template, go ahead and call it now.
2267
		if (function_exists('template_' . $template_name . '_init'))
2268
			call_user_func('template_' . $template_name . '_init');
2269
	}
2270
	// Hmmm... doesn't exist?!  I don't suppose the directory is wrong, is it?
2271
	elseif (!file_exists($settings['default_theme_dir']) && file_exists($boarddir . '/Themes/default'))
2272
	{
2273
		$settings['default_theme_dir'] = $boarddir . '/Themes/default';
2274
		$settings['template_dirs'][] = $settings['default_theme_dir'];
2275
2276 View Code Duplication
		if (!empty($context['user']['is_admin']) && !isset($_GET['th']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
2277
		{
2278
			loadLanguage('Errors');
2279
			echo '
2280
<div class="alert errorbox">
2281
	<a href="', $scripturl . '?action=admin;area=theme;sa=list;' . $context['session_var'] . '=' . $context['session_id'], '" class="alert">', $txt['theme_dir_wrong'], '</a>
2282
</div>';
2283
		}
2284
2285
		loadTemplate($template_name);
2286
	}
2287
	// Cause an error otherwise.
2288
	elseif ($template_name != 'Errors' && $template_name != 'index' && $fatal)
2289
		fatal_lang_error('theme_template_error', 'template', array((string) $template_name));
2290
	elseif ($fatal)
2291
		die(log_error(sprintf(isset($txt['theme_template_error']) ? $txt['theme_template_error'] : 'Unable to load Themes/default/%s.template.php!', (string) $template_name), 'template'));
2292
	else
2293
		return false;
2294
}
2295
2296
2297
/**
2298
 * Load a sub-template.
2299
 * What it does:
2300
 * 	- loads the sub template specified by sub_template_name, which must be in an already-loaded template.
2301
 *  - if ?debug is in the query string, shows administrators a marker after every sub template
2302
 *	for debugging purposes.
2303
 *
2304
 * @todo get rid of reading $_REQUEST directly
2305
 *
2306
 * @param string $sub_template_name The name of the sub-template to load
2307
 * @param bool $fatal Whether to die with an error if the sub-template can't be loaded
2308
 */
2309
function loadSubTemplate($sub_template_name, $fatal = false)
2310
{
2311
	global $context, $txt, $db_show_debug;
2312
2313
	if ($db_show_debug === true)
2314
		$context['debug']['sub_templates'][] = $sub_template_name;
2315
2316
	// Figure out what the template function is named.
2317
	$theme_function = 'template_' . $sub_template_name;
2318
	if (function_exists($theme_function))
2319
		$theme_function();
2320
	elseif ($fatal === false)
2321
		fatal_lang_error('theme_template_error', 'template', array((string) $sub_template_name));
2322
	elseif ($fatal !== 'ignore')
2323
		die(log_error(sprintf(isset($txt['theme_template_error']) ? $txt['theme_template_error'] : 'Unable to load the %s sub template!', (string) $sub_template_name), 'template'));
2324
2325
	// Are we showing debugging for templates?  Just make sure not to do it before the doctype...
2326
	if (allowedTo('admin_forum') && isset($_REQUEST['debug']) && !in_array($sub_template_name, array('init', 'main_below')) && ob_get_length() > 0 && !isset($_REQUEST['xml']))
2327
	{
2328
		echo '
2329
<div class="warningbox">---- ', $sub_template_name, ' ends ----</div>';
2330
	}
2331
}
2332
2333
/**
2334
 * Add a CSS file for output later
2335
 *
2336
 * @param string $fileName The name of the file to load
2337
 * @param array $params An array of parameters
2338
 * Keys are the following:
2339
 * 	- ['external'] (true/false): define if the file is a externally located file. Needs to be set to true if you are loading an external file
2340
 * 	- ['default_theme'] (true/false): force use of default theme url
2341
 * 	- ['force_current'] (true/false): if this is false, we will attempt to load the file from the default theme if not found in the current theme
2342
 *  - ['validate'] (true/false): if true script will validate the local file exists
2343
 *  - ['rtl'] (string): additional file to load in RTL mode
2344
 *  - ['seed'] (true/false/string): if true or null, use cache stale, false do not, or used a supplied string
2345
 *  - ['minimize'] boolean to add your file to the main minimized file. Useful when you have a file thats loaded everywhere and for everyone.
2346
 * @param string $id An ID to stick on the end of the filename for caching purposes
2347
 */
2348
function loadCSSFile($fileName, $params = array(), $id = '')
2349
{
2350
	global $settings, $context, $modSettings;
2351
2352
	$params['seed'] = (!array_key_exists('seed', $params) || (array_key_exists('seed', $params) && $params['seed'] === true)) ? (array_key_exists('browser_cache', $modSettings) ? $modSettings['browser_cache'] : '') : (is_string($params['seed']) ? ($params['seed'] = $params['seed'][0] === '?' ? $params['seed'] : '?' . $params['seed']) : '');
2353
	$params['force_current'] = isset($params['force_current']) ? $params['force_current'] : false;
2354
	$themeRef = !empty($params['default_theme']) ? 'default_theme' : 'theme';
2355
	$params['minimize'] = isset($params['minimize']) ? $params['minimize'] : false;
2356
	$params['external'] = isset($params['external']) ? $params['external'] : false;
2357
	$params['validate'] = isset($params['validate']) ? $params['validate'] : true;
2358
2359
	// If this is an external file, automatically set this to false.
2360
	if (!empty($params['external']))
2361
		$params['minimize'] = false;
2362
2363
	// Account for shorthand like admin.css?alp21 filenames
2364
	$has_seed = strpos($fileName, '.css?');
2365
	$id = empty($id) ? strtr(basename(str_replace('.css', '', $fileName)), '?', '_') : $id;
2366
2367
	// Is this a local file?
2368 View Code Duplication
	if (empty($params['external']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
2369
	{
2370
		// Are we validating the the file exists?
2371
		if (!empty($params['validate']) && !file_exists($settings[$themeRef . '_dir'] . '/css/' . $fileName))
2372
		{
2373
			// Maybe the default theme has it?
2374
			if ($themeRef === 'theme' && !$params['force_current'] && file_exists($settings['default_theme_dir'] . '/css/' . $fileName))
2375
			{
2376
				$fileUrl = $settings['default_theme_url'] . '/css/' . $fileName . ($has_seed ? '' : $params['seed']);
2377
				$filePath = $settings['default_theme_dir'] . '/css/' . $fileName . ($has_seed ? '' : $params['seed']);
2378
			}
2379
2380
			else
2381
				$fileUrl = false;
2382
		}
2383
2384
		else
2385
		{
2386
			$fileUrl = $settings[$themeRef . '_url'] . '/css/' . $fileName . ($has_seed ? '' : $params['seed']);
2387
			$filePath = $settings[$themeRef . '_dir'] . '/css/' . $fileName . ($has_seed ? '' : $params['seed']);
2388
		}
2389
	}
2390
2391
	// An external file doesn't have a filepath. Mock one for simplicity.
2392
	else
2393
	{
2394
		$fileUrl = $fileName;
2395
		$filePath = $fileName;
2396
	}
2397
2398
	// Add it to the array for use in the template
2399 View Code Duplication
	if (!empty($fileName))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
2400
		$context['css_files'][$id] = array('fileUrl' => $fileUrl, 'filePath' => $filePath, 'fileName' => $fileName, 'options' => $params);
0 ignored issues
show
Bug introduced by
The variable $filePath does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
2401
2402
	if (!empty($context['right_to_left']) && !empty($params['rtl']))
2403
		loadCSSFile($params['rtl'], array_diff_key($params, array('rtl' => 0)));
2404
}
2405
2406
/**
2407
 * Add a block of inline css code to be executed later
2408
 *
2409
 * - only use this if you have to, generally external css files are better, but for very small changes
2410
 *   or for scripts that require help from PHP/whatever, this can be useful.
2411
 * - all code added with this function is added to the same <style> tag so do make sure your css is valid!
2412
 *
2413
 * @param string $css Some css code
2414
 * @return void|bool Adds the CSS to the $context['css_header'] array or returns if no CSS is specified
2415
 */
2416
function addInlineCss($css)
2417
{
2418
	global $context;
2419
2420
	// Gotta add something...
2421
	if (empty($css))
2422
		return false;
2423
2424
	$context['css_header'][] = $css;
2425
}
2426
2427
/**
2428
 * Add a Javascript file for output later
2429
2430
 * @param string $filename The name of the file to load
0 ignored issues
show
Documentation introduced by
There is no parameter named $filename. Did you maybe mean $fileName?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
2431
 * @param array $params An array of parameter info
2432
 * Keys are the following:
2433
 * 	- ['external'] (true/false): define if the file is a externally located file. Needs to be set to true if you are loading an external file
2434
 * 	- ['default_theme'] (true/false): force use of default theme url
2435
 * 	- ['defer'] (true/false): define if the file should load in <head> or before the closing <html> tag
2436
 * 	- ['force_current'] (true/false): if this is false, we will attempt to load the file from the
2437
 *	default theme if not found in the current theme
2438
 *	- ['async'] (true/false): if the script should be loaded asynchronously (HTML5)
2439
 *  - ['validate'] (true/false): if true script will validate the local file exists
2440
 *  - ['seed'] (true/false/string): if true or null, use cache stale, false do not, or used a supplied string
2441
 *  - ['minimize'] boolean to add your file to the main minimized file. Useful when you have a file thats loaded everywhere and for everyone.
2442
 *
2443
 * @param string $id An ID to stick on the end of the filename
2444
 */
2445
function loadJavaScriptFile($fileName, $params = array(), $id = '')
2446
{
2447
	global $settings, $context, $modSettings;
2448
2449
	$params['seed'] = (!array_key_exists('seed', $params) || (array_key_exists('seed', $params) && $params['seed'] === true)) ? (array_key_exists('browser_cache', $modSettings) ? $modSettings['browser_cache'] : '') : (is_string($params['seed']) ? ($params['seed'] = $params['seed'][0] === '?' ? $params['seed'] : '?' . $params['seed']) : '');
2450
	$params['force_current'] = isset($params['force_current']) ? $params['force_current'] : false;
2451
	$themeRef = !empty($params['default_theme']) ? 'default_theme' : 'theme';
2452
	$params['minimize'] = isset($params['minimize']) ? $params['minimize'] : false;
2453
	$params['external'] = isset($params['external']) ? $params['external'] : false;
2454
	$params['validate'] = isset($params['validate']) ? $params['validate'] : true;
2455
2456
	// If this is an external file, automatically set this to false.
2457
	if (!empty($params['external']))
2458
		$params['minimize'] = false;
2459
2460
	// Account for shorthand like admin.js?alp21 filenames
2461
	$has_seed = strpos($fileName, '.js?');
2462
	$id = empty($id) ? strtr(basename(str_replace('.js', '', $fileName)), '?', '_') : $id;
2463
2464
	// Is this a local file?
2465 View Code Duplication
	if (empty($params['external']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
2466
	{
2467
		// Are we validating it exists on disk?
2468
		if (!empty($params['validate']) && !file_exists($settings[$themeRef . '_dir'] . '/scripts/' . $fileName))
2469
		{
2470
			// Can't find it in this theme, how about the default?
2471
			if ($themeRef === 'theme' && !$params['force_current'] && file_exists($settings['default_theme_dir'] . '/scripts/' . $fileName))
2472
			{
2473
				$fileUrl = $settings['default_theme_url'] . '/scripts/' . $fileName . ($has_seed ? '' : $params['seed']);
2474
				$filePath = $settings['default_theme_dir'] . '/scripts/' . $fileName . ($has_seed ? '' : $params['seed']);
2475
			}
2476
2477
			else
2478
			{
2479
				$fileUrl = false;
2480
				$filePath = false;
2481
			}
2482
		}
2483
2484
		else
2485
		{
2486
			$fileUrl = $settings[$themeRef . '_url'] . '/scripts/' . $fileName . ($has_seed ? '' : $params['seed']);
2487
			$filePath = $settings[$themeRef . '_dir'] . '/scripts/' . $fileName . ($has_seed ? '' : $params['seed']);
2488
		}
2489
	}
2490
2491
	// An external file doesn't have a filepath. Mock one for simplicity.
2492
	else
2493
	{
2494
		$fileUrl = $fileName;
2495
		$filePath = $fileName;
2496
	}
2497
2498
	// Add it to the array for use in the template
2499 View Code Duplication
	if (!empty($fileName))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
2500
		$context['javascript_files'][$id] = array('fileUrl' => $fileUrl, 'filePath' => $filePath, 'fileName' => $fileName, 'options' => $params);
2501
}
2502
2503
/**
2504
 * Add a Javascript variable for output later (for feeding text strings and similar to JS)
2505
 * Cleaner and easier (for modders) than to use the function below.
2506
 *
2507
 * @param string $key The key for this variable
2508
 * @param string $value The value
2509
 * @param bool $escape Whether or not to escape the value
2510
 */
2511
function addJavaScriptVar($key, $value, $escape = false)
2512
{
2513
	global $context;
2514
2515
	if (!empty($key) && (!empty($value) || $value === '0'))
2516
		$context['javascript_vars'][$key] = !empty($escape) ? JavaScriptEscape($value) : $value;
2517
}
2518
2519
/**
2520
 * Add a block of inline Javascript code to be executed later
2521
 *
2522
 * - only use this if you have to, generally external JS files are better, but for very small scripts
2523
 *   or for scripts that require help from PHP/whatever, this can be useful.
2524
 * - all code added with this function is added to the same <script> tag so do make sure your JS is clean!
2525
 *
2526
 * @param string $javascript Some JS code
2527
 * @param bool $defer Whether the script should load in <head> or before the closing <html> tag
2528
 * @return void|bool Adds the code to one of the $context['javascript_inline'] arrays or returns if no JS was specified
2529
 */
2530
function addInlineJavaScript($javascript, $defer = false)
2531
{
2532
	global $context;
2533
2534
	if (empty($javascript))
2535
		return false;
2536
2537
	$context['javascript_inline'][($defer === true ? 'defer' : 'standard')][] = $javascript;
2538
}
2539
2540
/**
2541
 * Load a language file.  Tries the current and default themes as well as the user and global languages.
2542
 *
2543
 * @param string $template_name The name of a template file
2544
 * @param string $lang A specific language to load this file from
2545
 * @param bool $fatal Whether to die with an error if it can't be loaded
2546
 * @param bool $force_reload Whether to load the file again if it's already loaded
2547
 * @return string The language actually loaded.
2548
 */
2549
function loadLanguage($template_name, $lang = '', $fatal = true, $force_reload = false)
2550
{
2551
	global $user_info, $language, $settings, $context, $modSettings;
2552
	global $db_show_debug, $sourcedir, $txt, $birthdayEmails, $txtBirthdayEmails;
2553
	static $already_loaded = array();
2554
2555
	// Default to the user's language.
2556
	if ($lang == '')
2557
		$lang = isset($user_info['language']) ? $user_info['language'] : $language;
2558
2559
	// Do we want the English version of language file as fallback?
2560
	if (empty($modSettings['disable_language_fallback']) && $lang != 'english')
2561
		loadLanguage($template_name, 'english', false);
2562
2563
	if (!$force_reload && isset($already_loaded[$template_name]) && $already_loaded[$template_name] == $lang)
2564
		return $lang;
2565
2566
	// Make sure we have $settings - if not we're in trouble and need to find it!
2567
	if (empty($settings['default_theme_dir']))
2568
	{
2569
		require_once($sourcedir . '/ScheduledTasks.php');
2570
		loadEssentialThemeData();
2571
	}
2572
2573
	// What theme are we in?
2574
	$theme_name = basename($settings['theme_url']);
2575
	if (empty($theme_name))
2576
		$theme_name = 'unknown';
2577
2578
	// For each file open it up and write it out!
2579
	foreach (explode('+', $template_name) as $template)
2580
	{
2581
		// Obviously, the current theme is most important to check.
2582
		$attempts = array(
2583
			array($settings['theme_dir'], $template, $lang, $settings['theme_url']),
2584
			array($settings['theme_dir'], $template, $language, $settings['theme_url']),
2585
		);
2586
2587
		// Do we have a base theme to worry about?
2588
		if (isset($settings['base_theme_dir']))
2589
		{
2590
			$attempts[] = array($settings['base_theme_dir'], $template, $lang, $settings['base_theme_url']);
2591
			$attempts[] = array($settings['base_theme_dir'], $template, $language, $settings['base_theme_url']);
2592
		}
2593
2594
		// Fall back on the default theme if necessary.
2595
		$attempts[] = array($settings['default_theme_dir'], $template, $lang, $settings['default_theme_url']);
2596
		$attempts[] = array($settings['default_theme_dir'], $template, $language, $settings['default_theme_url']);
2597
2598
		// Fall back on the English language if none of the preferred languages can be found.
2599
		if (!in_array('english', array($lang, $language)))
2600
		{
2601
			$attempts[] = array($settings['theme_dir'], $template, 'english', $settings['theme_url']);
2602
			$attempts[] = array($settings['default_theme_dir'], $template, 'english', $settings['default_theme_url']);
2603
		}
2604
2605
		// Try to find the language file.
2606
		$found = false;
2607
		foreach ($attempts as $k => $file)
2608
		{
2609
			if (file_exists($file[0] . '/languages/' . $file[1] . '.' . $file[2] . '.php'))
2610
			{
2611
				// Include it!
2612
				template_include($file[0] . '/languages/' . $file[1] . '.' . $file[2] . '.php');
2613
2614
				// Note that we found it.
2615
				$found = true;
2616
2617
				break;
2618
			}
2619
		}
2620
2621
		// That couldn't be found!  Log the error, but *try* to continue normally.
2622
		if (!$found && $fatal)
2623
		{
2624
			log_error(sprintf($txt['theme_language_error'], $template_name . '.' . $lang, 'template'));
2625
			break;
2626
		}
2627
2628
		// For the sake of backward compatibility
2629
		if (!empty($txt['emails']))
2630
		{
2631
			foreach ($txt['emails'] as $key => $value)
2632
			{
2633
				$txt[$key . '_subject'] = $value['subject'];
2634
				$txt[$key . '_body'] = $value['body'];
2635
			}
2636
			$txt['emails'] = array();
2637
		}
2638
		// For sake of backward compatibility: $birthdayEmails is supposed to be
2639
		// empty in a normal install. If it isn't it means the forum is using
2640
		// something "old" (it may be the translation, it may be a mod) and this
2641
		// code (like the piece above) takes care of converting it to the new format
2642
		if (!empty($birthdayEmails))
2643
		{
2644
			foreach ($birthdayEmails as $key => $value)
2645
			{
2646
				$txtBirthdayEmails[$key . '_subject'] = $value['subject'];
2647
				$txtBirthdayEmails[$key . '_body'] = $value['body'];
2648
				$txtBirthdayEmails[$key . '_author'] = $value['author'];
2649
			}
2650
			$birthdayEmails = array();
2651
		}
2652
	}
2653
2654
	// Keep track of what we're up to soldier.
2655
	if ($db_show_debug === true)
2656
		$context['debug']['language_files'][] = $template_name . '.' . $lang . ' (' . $theme_name . ')';
2657
2658
	// Remember what we have loaded, and in which language.
2659
	$already_loaded[$template_name] = $lang;
2660
2661
	// Return the language actually loaded.
2662
	return $lang;
2663
}
2664
2665
/**
2666
 * Get all parent boards (requires first parent as parameter)
2667
 * It finds all the parents of id_parent, and that board itself.
2668
 * Additionally, it detects the moderators of said boards.
2669
 *
2670
 * @param int $id_parent The ID of the parent board
2671
 * @return array An array of information about the boards found.
2672
 */
2673
function getBoardParents($id_parent)
2674
{
2675
	global $scripturl, $smcFunc;
2676
2677
	// First check if we have this cached already.
2678
	if (($boards = cache_get_data('board_parents-' . $id_parent, 480)) === null)
2679
	{
2680
		$boards = array();
2681
		$original_parent = $id_parent;
2682
2683
		// Loop while the parent is non-zero.
2684
		while ($id_parent != 0)
2685
		{
2686
			$result = $smcFunc['db_query']('', '
2687
				SELECT
2688
					b.id_parent, b.name, {int:board_parent} AS id_board, b.member_groups, b.deny_member_groups,
2689
					b.child_level, COALESCE(mem.id_member, 0) AS id_moderator, mem.real_name,
2690
					COALESCE(mg.id_group, 0) AS id_moderator_group, mg.group_name
2691
				FROM {db_prefix}boards AS b
2692
					LEFT JOIN {db_prefix}moderators AS mods ON (mods.id_board = b.id_board)
2693
					LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = mods.id_member)
2694
					LEFT JOIN {db_prefix}moderator_groups AS modgs ON (modgs.id_board = b.id_board)
2695
					LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = modgs.id_group)
2696
				WHERE b.id_board = {int:board_parent}',
2697
				array(
2698
					'board_parent' => $id_parent,
2699
				)
2700
			);
2701
			// In the EXTREMELY unlikely event this happens, give an error message.
2702
			if ($smcFunc['db_num_rows']($result) == 0)
2703
				fatal_lang_error('parent_not_found', 'critical');
2704
			while ($row = $smcFunc['db_fetch_assoc']($result))
2705
			{
2706
				if (!isset($boards[$row['id_board']]))
2707
				{
2708
					$id_parent = $row['id_parent'];
2709
					$boards[$row['id_board']] = array(
2710
						'url' => $scripturl . '?board=' . $row['id_board'] . '.0',
2711
						'name' => $row['name'],
2712
						'level' => $row['child_level'],
2713
						'groups' => explode(',', $row['member_groups']),
2714
						'deny_groups' => explode(',', $row['deny_member_groups']),
2715
						'moderators' => array(),
2716
						'moderator_groups' => array()
2717
					);
2718
				}
2719
				// If a moderator exists for this board, add that moderator for all children too.
2720 View Code Duplication
				if (!empty($row['id_moderator']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
2721
					foreach ($boards as $id => $dummy)
2722
					{
2723
						$boards[$id]['moderators'][$row['id_moderator']] = array(
2724
							'id' => $row['id_moderator'],
2725
							'name' => $row['real_name'],
2726
							'href' => $scripturl . '?action=profile;u=' . $row['id_moderator'],
2727
							'link' => '<a href="' . $scripturl . '?action=profile;u=' . $row['id_moderator'] . '">' . $row['real_name'] . '</a>'
2728
						);
2729
					}
2730
2731
				// If a moderator group exists for this board, add that moderator group for all children too
2732 View Code Duplication
				if (!empty($row['id_moderator_group']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
2733
					foreach ($boards as $id => $dummy)
2734
					{
2735
						$boards[$id]['moderator_groups'][$row['id_moderator_group']] = array(
2736
							'id' => $row['id_moderator_group'],
2737
							'name' => $row['group_name'],
2738
							'href' => $scripturl . '?action=groups;sa=members;group=' . $row['id_moderator_group'],
2739
							'link' => '<a href="' . $scripturl . '?action=groups;sa=members;group=' . $row['id_moderator_group'] . '">' . $row['group_name'] . '</a>'
2740
						);
2741
					}
2742
			}
2743
			$smcFunc['db_free_result']($result);
2744
		}
2745
2746
		cache_put_data('board_parents-' . $original_parent, $boards, 480);
2747
	}
2748
2749
	return $boards;
2750
}
2751
2752
/**
2753
 * Attempt to reload our known languages.
2754
 * It will try to choose only utf8 or non-utf8 languages.
2755
 *
2756
 * @param bool $use_cache Whether or not to use the cache
2757
 * @param bool $favor_utf8 Whether or not to favor UTF-8 files
2758
 * @return array An array of information about available languages
2759
 */
2760
function getLanguages($use_cache = true, $favor_utf8 = true)
2761
{
2762
	global $context, $smcFunc, $settings, $modSettings;
2763
2764
	// Either we don't use the cache, or its expired.
2765
	if (!$use_cache || ($context['languages'] = cache_get_data('known_languages' . ($favor_utf8 ? '' : '_all'), !empty($modSettings['cache_enable']) && $modSettings['cache_enable'] < 1 ? 86400 : 3600)) == null)
2766
	{
2767
		// If we don't have our ucwords function defined yet, let's load the settings data.
2768
		if (empty($smcFunc['ucwords']))
2769
			reloadSettings();
2770
2771
		// If we don't have our theme information yet, let's get it.
2772
		if (empty($settings['default_theme_dir']))
2773
			loadTheme(0, false);
2774
2775
		// Default language directories to try.
2776
		$language_directories = array(
2777
			$settings['default_theme_dir'] . '/languages',
2778
		);
2779
		if (!empty($settings['actual_theme_dir']) && $settings['actual_theme_dir'] != $settings['default_theme_dir'])
2780
			$language_directories[] = $settings['actual_theme_dir'] . '/languages';
2781
2782
		// We possibly have a base theme directory.
2783
		if (!empty($settings['base_theme_dir']))
2784
			$language_directories[] = $settings['base_theme_dir'] . '/languages';
2785
2786
		// Remove any duplicates.
2787
		$language_directories = array_unique($language_directories);
2788
2789
		// Get a list of languages.
2790
		$langList = !empty($modSettings['langList']) ? json_decode($modSettings['langList'], true) : array();
2791
		$langList = is_array($langList) ? $langList : false;
2792
2793
		$catchLang = array();
2794
2795
		foreach ($language_directories as $language_dir)
2796
		{
2797
			// Can't look in here... doesn't exist!
2798
			if (!file_exists($language_dir))
2799
				continue;
2800
2801
			$dir = dir($language_dir);
2802
			while ($entry = $dir->read())
2803
			{
2804
				// Look for the index language file....
2805
				if (!preg_match('~^index\.(.+)\.php$~', $entry, $matches))
2806
					continue;
2807
2808
				if (!empty($langList) && !empty($langList[$matches[1]]))
2809
					$langName = $langList[$matches[1]];
2810
2811
				else
2812
				{
2813
					$langName = $smcFunc['ucwords'](strtr($matches[1], array('_' => ' ')));
2814
2815
					// Get the line we need.
2816
					$fp = @fopen($language_dir .'/'. $entry);
2817
2818
					// Yay!
2819
					if ($fp)
2820
					{
2821
						while (($line = fgets($fp)) !== false)
2822
						{
2823
							preg_match('~\$txt\[\'native_name\'\] = \'(.+)\'\;~', $line, $matchNative);
2824
2825
							// Set the language's name.
2826
							if (!empty($matchNative) && !empty($matchNative[1]))
2827
							{
2828
								$langName = un_htmlspecialchars($matchNative[1]);
2829
								break;
2830
							}
2831
						}
2832
2833
						fclose($fp);
2834
					}
2835
2836
					// Catch the language name.
2837
					$catchLang[$matches[1]] = $langName;
2838
				}
2839
2840
				// Build this language entry.
2841
				$context['languages'][$matches[1]] = array(
2842
					'name' => $langName,
2843
					'selected' => false,
2844
					'filename' => $matches[1],
2845
					'location' => $language_dir . '/index.' . $matches[1] . '.php',
2846
				);
2847
2848
				$indexFile = '';
0 ignored issues
show
Unused Code introduced by
$indexFile is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
2849
			}
2850
			$dir->close();
2851
		}
2852
2853
		// Do we need to store the lang list?
2854
		if (empty($langList))
2855
			updateSettings(array('langList' => json_encode($catchLang)));
2856
2857
		// Favoring UTF8? Then prevent us from selecting non-UTF8 versions.
2858
		if ($favor_utf8)
2859
		{
2860
			foreach ($context['languages'] as $lang)
0 ignored issues
show
Bug introduced by
The expression $context['languages'] of type null|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
2861
				if (substr($lang['filename'], strlen($lang['filename']) - 5, 5) != '-utf8' && isset($context['languages'][$lang['filename'] . '-utf8']))
2862
					unset($context['languages'][$lang['filename']]);
2863
		}
2864
2865
		// Let's cash in on this deal.
2866
		if (!empty($modSettings['cache_enable']))
2867
			cache_put_data('known_languages' . ($favor_utf8 ? '' : '_all'), $context['languages'], !empty($modSettings['cache_enable']) && $modSettings['cache_enable'] < 1 ? 86400 : 3600);
2868
	}
2869
2870
	return $context['languages'];
2871
}
2872
2873
/**
2874
 * Replace all vulgar words with respective proper words. (substring or whole words..)
2875
 * What this function does:
2876
 *  - it censors the passed string.
2877
 *  - if the theme setting allow_no_censored is on, and the theme option
2878
 *	show_no_censored is enabled, does not censor, unless force is also set.
2879
 *  - it caches the list of censored words to reduce parsing.
2880
 *
2881
 * @param string &$text The text to censor
2882
 * @param bool $force Whether to censor the text regardless of settings
2883
 * @return string The censored text
2884
 */
2885
function censorText(&$text, $force = false)
2886
{
2887
	global $modSettings, $options, $txt;
2888
	static $censor_vulgar = null, $censor_proper;
2889
2890
	if ((!empty($options['show_no_censored']) && !empty($modSettings['allow_no_censored']) && !$force) || empty($modSettings['censor_vulgar']) || trim($text) === '')
2891
		return $text;
2892
2893
	// If they haven't yet been loaded, load them.
2894
	if ($censor_vulgar == null)
2895
	{
2896
		$censor_vulgar = explode("\n", $modSettings['censor_vulgar']);
2897
		$censor_proper = explode("\n", $modSettings['censor_proper']);
2898
2899
		// Quote them for use in regular expressions.
2900
		if (!empty($modSettings['censorWholeWord']))
2901
		{
2902
			for ($i = 0, $n = count($censor_vulgar); $i < $n; $i++)
2903
			{
2904
				$censor_vulgar[$i] = str_replace(array('\\\\\\*', '\\*', '&', '\''), array('[*]', '[^\s]*?', '&amp;', '&#039;'), preg_quote($censor_vulgar[$i], '/'));
2905
				$censor_vulgar[$i] = '/(?<=^|\W)' . $censor_vulgar[$i] . '(?=$|\W)/' . (empty($modSettings['censorIgnoreCase']) ? '' : 'i') . ((empty($modSettings['global_character_set']) ? $txt['lang_character_set'] : $modSettings['global_character_set']) === 'UTF-8' ? 'u' : '');
2906
2907
				// @todo I'm thinking the old way is some kind of bug and this is actually fixing it.
2908
				//if (strpos($censor_vulgar[$i], '\'') !== false)
2909
					//$censor_vulgar[$i] = str_replace('\'', '&#039;', $censor_vulgar[$i]);
2910
			}
2911
		}
2912
	}
2913
2914
	// Censoring isn't so very complicated :P.
2915
	if (empty($modSettings['censorWholeWord']))
2916
	{
2917
		$func = !empty($modSettings['censorIgnoreCase']) ? 'str_ireplace' : 'str_replace';
2918
		$text = $func($censor_vulgar, $censor_proper, $text);
2919
	}
2920
	else
2921
		$text = preg_replace($censor_vulgar, $censor_proper, $text);
2922
2923
	return $text;
2924
}
2925
2926
/**
2927
 * Load the template/language file using eval or require? (with eval we can show an error message!)
2928
 * 	- loads the template or language file specified by filename.
2929
 * 	- uses eval unless disableTemplateEval is enabled.
2930
 * 	- outputs a parse error if the file did not exist or contained errors.
2931
 * 	- attempts to detect the error and line, and show detailed information.
2932
 *
2933
 * @param string $filename The name of the file to include
2934
 * @param bool $once If true only includes the file once (like include_once)
2935
 */
2936
function template_include($filename, $once = false)
2937
{
2938
	global $context, $settings, $txt, $scripturl, $modSettings;
2939
	global $boardurl, $boarddir, $sourcedir;
2940
	global $maintenance, $mtitle, $mmessage;
2941
	static $templates = array();
2942
2943
	// We want to be able to figure out any errors...
2944
	@ini_set('track_errors', '1');
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...
2945
2946
	// Don't include the file more than once, if $once is true.
2947
	if ($once && in_array($filename, $templates))
2948
		return;
2949
	// Add this file to the include list, whether $once is true or not.
2950
	else
2951
		$templates[] = $filename;
2952
2953
	// Are we going to use eval?
2954
	if (empty($modSettings['disableTemplateEval']))
2955
	{
2956
		$file_found = file_exists($filename) && eval('?' . '>' . rtrim(file_get_contents($filename))) !== false;
2957
		$settings['current_include_filename'] = $filename;
2958
	}
2959
	else
2960
	{
2961
		$file_found = file_exists($filename);
2962
2963
		if ($once && $file_found)
2964
			require_once($filename);
2965
		elseif ($file_found)
2966
			require($filename);
2967
	}
2968
2969
	if ($file_found !== true)
2970
	{
2971
		ob_end_clean();
2972
		if (!empty($modSettings['enableCompressedOutput']))
2973
			@ob_start('ob_gzhandler');
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...
2974
		else
2975
			ob_start();
2976
2977 View Code Duplication
		if (isset($_GET['debug']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
2978
			header('Content-Type: application/xhtml+xml; charset=' . (empty($context['character_set']) ? 'ISO-8859-1' : $context['character_set']));
2979
2980
		// Don't cache error pages!!
2981
		header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
2982
		header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
2983
		header('Cache-Control: no-cache');
2984
2985
		if (!isset($txt['template_parse_error']))
2986
		{
2987
			$txt['template_parse_error'] = 'Template Parse Error!';
2988
			$txt['template_parse_error_message'] = 'It seems something has gone sour on the forum with the template system.  This problem should only be temporary, so please come back later and try again.  If you continue to see this message, please contact the administrator.<br><br>You can also try <a href="javascript:location.reload();">refreshing this page</a>.';
2989
			$txt['template_parse_error_details'] = 'There was a problem loading the <pre><strong>%1$s</strong></pre> template or language file.  Please check the syntax and try again - remember, single quotes (<pre>\'</pre>) often have to be escaped with a slash (<pre>\\</pre>).  To see more specific error information from PHP, try <a href="' . $boardurl . '%1$s" class="extern">accessing the file directly</a>.<br><br>You may want to try to <a href="javascript:location.reload();">refresh this page</a> or <a href="' . $scripturl . '?theme=1">use the default theme</a>.';
2990
			$txt['template_parse_errmsg'] = 'Unfortunately more information is not available at this time as to exactly what is wrong.';
2991
		}
2992
2993
		// First, let's get the doctype and language information out of the way.
2994
		echo '<!DOCTYPE html>
2995
<html', !empty($context['right_to_left']) ? ' dir="rtl"' : '', '>
2996
	<head>';
2997
		if (isset($context['character_set']))
2998
			echo '
2999
		<meta charset="', $context['character_set'], '">';
3000
3001
		if (!empty($maintenance) && !allowedTo('admin_forum'))
3002
			echo '
3003
		<title>', $mtitle, '</title>
3004
	</head>
3005
	<body>
3006
		<h3>', $mtitle, '</h3>
3007
		', $mmessage, '
3008
	</body>
3009
</html>';
3010
		elseif (!allowedTo('admin_forum'))
3011
			echo '
3012
		<title>', $txt['template_parse_error'], '</title>
3013
	</head>
3014
	<body>
3015
		<h3>', $txt['template_parse_error'], '</h3>
3016
		', $txt['template_parse_error_message'], '
3017
	</body>
3018
</html>';
3019
		else
3020
		{
3021
			require_once($sourcedir . '/Subs-Package.php');
3022
3023
			$error = fetch_web_data($boardurl . strtr($filename, array($boarddir => '', strtr($boarddir, '\\', '/') => '')));
3024
			if (empty($error) && ini_get('track_errors') && !empty($php_errormsg))
3025
				$error = $php_errormsg;
3026
			if (empty($error))
3027
				$error = $txt['template_parse_errmsg'];
3028
3029
			$error = strtr($error, array('<b>' => '<strong>', '</b>' => '</strong>'));
3030
3031
			echo '
3032
		<title>', $txt['template_parse_error'], '</title>
3033
	</head>
3034
	<body>
3035
		<h3>', $txt['template_parse_error'], '</h3>
3036
		', sprintf($txt['template_parse_error_details'], strtr($filename, array($boarddir => '', strtr($boarddir, '\\', '/') => '')));
3037
3038
			if (!empty($error))
3039
				echo '
3040
		<hr>
3041
3042
		<div style="margin: 0 20px;"><pre>', strtr(strtr($error, array('<strong>' . $boarddir => '<strong>...', '<strong>' . strtr($boarddir, '\\', '/') => '<strong>...')), '\\', '/'), '</pre></div>';
3043
3044
			// I know, I know... this is VERY COMPLICATED.  Still, it's good.
3045
			if (preg_match('~ <strong>(\d+)</strong><br( /)?' . '>$~i', $error, $match) != 0)
3046
			{
3047
				$data = file($filename);
3048
				$data2 = highlight_php_code(implode('', $data));
3049
				$data2 = preg_split('~\<br( /)?\>~', $data2);
3050
3051
				// Fix the PHP code stuff...
3052
				if (!isBrowser('gecko'))
3053
					$data2 = str_replace("\t", '<span style="white-space: pre;">' . "\t" . '</span>', $data2);
3054
				else
3055
					$data2 = str_replace('<pre style="display: inline;">' . "\t" . '</pre>', "\t", $data2);
3056
3057
				// Now we get to work around a bug in PHP where it doesn't escape <br>s!
3058
				$j = -1;
3059
				foreach ($data as $line)
3060
				{
3061
					$j++;
3062
3063
					if (substr_count($line, '<br>') == 0)
3064
						continue;
3065
3066
					$n = substr_count($line, '<br>');
3067
					for ($i = 0; $i < $n; $i++)
3068
					{
3069
						$data2[$j] .= '&lt;br /&gt;' . $data2[$j + $i + 1];
3070
						unset($data2[$j + $i + 1]);
3071
					}
3072
					$j += $n;
3073
				}
3074
				$data2 = array_values($data2);
3075
				array_unshift($data2, '');
3076
3077
				echo '
3078
		<div style="margin: 2ex 20px; width: 96%; overflow: auto;"><pre style="margin: 0;">';
3079
3080
				// Figure out what the color coding was before...
3081
				$line = max($match[1] - 9, 1);
3082
				$last_line = '';
3083
				for ($line2 = $line - 1; $line2 > 1; $line2--)
3084
					if (strpos($data2[$line2], '<') !== false)
3085
					{
3086
						if (preg_match('~(<[^/>]+>)[^<]*$~', $data2[$line2], $color_match) != 0)
3087
							$last_line = $color_match[1];
3088
						break;
3089
					}
3090
3091
				// Show the relevant lines...
3092
				for ($n = min($match[1] + 4, count($data2) + 1); $line <= $n; $line++)
3093
				{
3094
					if ($line == $match[1])
3095
						echo '</pre><div style="background-color: #ffb0b5;"><pre style="margin: 0;">';
3096
3097
					echo '<span style="color: black;">', sprintf('%' . strlen($n) . 's', $line), ':</span> ';
3098
					if (isset($data2[$line]) && $data2[$line] != '')
3099
						echo substr($data2[$line], 0, 2) == '</' ? preg_replace('~^</[^>]+>~', '', $data2[$line]) : $last_line . $data2[$line];
3100
3101
					if (isset($data2[$line]) && preg_match('~(<[^/>]+>)[^<]*$~', $data2[$line], $color_match) != 0)
3102
					{
3103
						$last_line = $color_match[1];
3104
						echo '</', substr($last_line, 1, 4), '>';
3105
					}
3106
					elseif ($last_line != '' && strpos($data2[$line], '<') !== false)
3107
						$last_line = '';
3108
					elseif ($last_line != '' && $data2[$line] != '')
3109
						echo '</', substr($last_line, 1, 4), '>';
3110
3111
					if ($line == $match[1])
3112
						echo '</pre></div><pre style="margin: 0;">';
3113
					else
3114
						echo "\n";
3115
				}
3116
3117
				echo '</pre></div>';
3118
			}
3119
3120
			echo '
3121
	</body>
3122
</html>';
3123
		}
3124
3125
		die;
3126
	}
3127
}
3128
3129
/**
3130
 * Initialize a database connection.
3131
 */
3132
function loadDatabase()
3133
{
3134
	global $db_persist, $db_connection, $db_server, $db_user, $db_passwd;
3135
	global $db_type, $db_name, $ssi_db_user, $ssi_db_passwd, $sourcedir, $db_prefix, $db_port;
3136
3137
	// Figure out what type of database we are using.
3138
	if (empty($db_type) || !file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php'))
3139
		$db_type = 'mysql';
3140
3141
	// Load the file for the database.
3142
	require_once($sourcedir . '/Subs-Db-' . $db_type . '.php');
3143
3144
	$db_options = array();
3145
3146
	// Add in the port if needed
3147
	if (!empty($db_port))
3148
		$db_options['port'] = $db_port;
3149
3150
	// If we are in SSI try them first, but don't worry if it doesn't work, we have the normal username and password we can use.
3151
	if (SMF == 'SSI' && !empty($ssi_db_user) && !empty($ssi_db_passwd))
3152
	{
3153
		$options = array_merge($db_options, array('persist' => $db_persist, 'non_fatal' => true, 'dont_select_db' => true));
3154
3155
		$db_connection = smf_db_initiate($db_server, $db_name, $ssi_db_user, $ssi_db_passwd, $db_prefix, $options);
3156
	}
3157
3158
	// Either we aren't in SSI mode, or it failed.
3159
	if (empty($db_connection))
3160
	{
3161
		$options = array_merge($db_options, array('persist' => $db_persist, 'dont_select_db' => SMF == 'SSI'));
3162
3163
		$db_connection = smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, $db_prefix, $options);
3164
	}
3165
3166
	// Safe guard here, if there isn't a valid connection lets put a stop to it.
3167
	if (!$db_connection)
3168
		display_db_error();
3169
3170
	// If in SSI mode fix up the prefix.
3171
	if (SMF == 'SSI')
3172
		db_fix_prefix($db_prefix, $db_name);
0 ignored issues
show
Unused Code introduced by
The call to the function db_fix_prefix() seems unnecessary as the function has no side-effects.
Loading history...
3173
}
3174
3175
/**
3176
 * Try to retrieve a cache entry. On failure, call the appropriate function.
3177
 *
3178
 * @param string $key The key for this entry
3179
 * @param string $file The file associated with this entry
3180
 * @param string $function The function to call
3181
 * @param array $params Parameters to be passed to the specified function
3182
 * @param int $level The cache level
3183
 * @return string The cached data
3184
 */
3185
function cache_quick_get($key, $file, $function, $params, $level = 1)
3186
{
3187
	global $modSettings, $sourcedir;
3188
3189
	// @todo Why are we doing this if caching is disabled?
3190
3191
	if (function_exists('call_integration_hook'))
3192
		call_integration_hook('pre_cache_quick_get', array(&$key, &$file, &$function, &$params, &$level));
3193
3194
	/* Refresh the cache if either:
3195
		1. Caching is disabled.
3196
		2. The cache level isn't high enough.
3197
		3. The item has not been cached or the cached item expired.
3198
		4. The cached item has a custom expiration condition evaluating to true.
3199
		5. The expire time set in the cache item has passed (needed for Zend).
3200
	*/
3201
	if (empty($modSettings['cache_enable']) || $modSettings['cache_enable'] < $level || !is_array($cache_block = cache_get_data($key, 3600)) || (!empty($cache_block['refresh_eval']) && eval($cache_block['refresh_eval'])) || (!empty($cache_block['expires']) && $cache_block['expires'] < time()))
3202
	{
3203
		require_once($sourcedir . '/' . $file);
3204
		$cache_block = call_user_func_array($function, $params);
3205
3206
		if (!empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= $level)
3207
			cache_put_data($key, $cache_block, $cache_block['expires'] - time());
3208
	}
3209
3210
	// Some cached data may need a freshening up after retrieval.
3211
	if (!empty($cache_block['post_retri_eval']))
3212
		eval($cache_block['post_retri_eval']);
3213
3214
	if (function_exists('call_integration_hook'))
3215
		call_integration_hook('post_cache_quick_get', array(&$cache_block));
3216
3217
	return $cache_block['data'];
3218
}
3219
3220
/**
3221
 * Puts value in the cache under key for ttl seconds.
3222
 *
3223
 * - It may "miss" so shouldn't be depended on
3224
 * - Uses the cache engine chosen in the ACP and saved in settings.php
3225
 * - It supports:
3226
 *	 Xcache: http://xcache.lighttpd.net/wiki/XcacheApi
3227
 *	 memcache: http://www.php.net/memcache
3228
 *	 APC: http://www.php.net/apc
3229
 *   APCu: http://www.php.net/book.apcu
3230
 *	 Zend: http://files.zend.com/help/Zend-Platform/output_cache_functions.htm
3231
 *	 Zend: http://files.zend.com/help/Zend-Platform/zend_cache_functions.htm
3232
 *
3233
 * @param string $key A key for this value
3234
 * @param mixed $value The data to cache
3235
 * @param int $ttl How long (in seconds) the data should be cached for
3236
 */
3237
function cache_put_data($key, $value, $ttl = 120)
3238
{
3239
	global $boardurl, $modSettings, $memcached;
3240
	global $cache_hits, $cache_count, $db_show_debug, $cachedir;
3241
	global $cache_accelerator, $cache_enable, $cache_memcached;
3242
3243
	if (empty($cache_enable))
3244
		return;
3245
3246
	$cache_count = isset($cache_count) ? $cache_count + 1 : 1;
3247
	if (isset($db_show_debug) && $db_show_debug === true)
3248
	{
3249
		$cache_hits[$cache_count] = array('k' => $key, 'd' => 'put', 's' => $value === null ? 0 : strlen(json_encode($value)));
3250
		$st = microtime();
3251
	}
3252
3253
	$key = md5($boardurl . filemtime($cachedir . '/' . 'index.php')) . '-SMF-' . strtr($key, ':/', '-_');
3254
	$value = $value === null ? null : json_encode($value);
3255
3256
	switch ($cache_accelerator)
3257
	{
3258
		case 'memcached':
3259
			// The simple yet efficient memcached.
3260
			if ((function_exists('memcached_set') || function_exists('memcache_set')) && isset($cache_memcached) && trim($cache_memcached) != '')
3261
			{
3262
				// Not connected yet?
3263
				if (empty($memcached))
3264
					get_memcached_server();
3265
				if (!$memcached)
3266
					return;
3267
3268
				memcache_set($memcached, $key, $value, 0, $ttl);
3269
			}
3270
			break;
3271
		case 'apc':
3272
			// Alternative PHP Cache, ahoy!
3273
			if (function_exists('apc_store'))
3274
			{
3275
				// An extended key is needed to counteract a bug in APC.
3276
				if ($value === null)
3277
					apc_delete($key . 'smf');
3278
				else
3279
					apc_store($key . 'smf', $value, $ttl);
3280
			}
3281
			break;
3282
		case 'apcu':
3283
			// APC User Cache
3284
			if (function_exists('apcu_store'))
3285
			{
3286
				// Not sure if this bug exists in APCu or not?
3287
				if ($value === null)
3288
					apcu_delete($key . 'smf');
3289
				else
3290
					apcu_store($key . 'smf', $value, $ttl);
3291
			}
3292
			break;
3293 View Code Duplication
		case 'zend':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
3294
			// Zend Platform/ZPS/etc.
3295
			if (function_exists('zend_shm_cache_store'))
3296
				zend_shm_cache_store('SMF::' . $key, $value, $ttl);
3297
			elseif (function_exists('output_cache_put'))
3298
				output_cache_put($key, $value);
3299
			break;
3300
		case 'xcache':
3301
			if (function_exists('xcache_set') && ini_get('xcache.var_size') > 0)
3302
			{
3303
				if ($value === null)
3304
					xcache_unset($key);
3305
				else
3306
					xcache_set($key, $value, $ttl);
3307
			}
3308
			break;
3309
		default:
3310
			// Otherwise custom cache?
3311
			if ($value === null)
3312
				@unlink($cachedir . '/data_' . $key . '.php');
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...
3313
			else
3314
			{
3315
				$cache_data = '<' . '?' . 'php if (!defined(\'SMF\')) die; if (' . (time() + $ttl) . ' < time()) $expired = true; else{$expired = false; $value = \'' . addcslashes($value, '\\\'') . '\';}' . '?' . '>';
3316
3317
				// Write out the cache file, check that the cache write was successful; all the data must be written
3318
				// If it fails due to low diskspace, or other, remove the cache file
3319
				if (file_put_contents($cachedir . '/data_' . $key . '.php', $cache_data, LOCK_EX) !== strlen($cache_data))
3320
					@unlink($cachedir . '/data_' . $key . '.php');
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...
3321
			}
3322
			break;
3323
	}
3324
3325
	if (function_exists('call_integration_hook'))
3326
		call_integration_hook('cache_put_data', array(&$key, &$value, &$ttl));
3327
3328 View Code Duplication
	if (isset($db_show_debug) && $db_show_debug === true)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
3329
		$cache_hits[$cache_count]['t'] = array_sum(explode(' ', microtime())) - array_sum(explode(' ', $st));
0 ignored issues
show
Bug introduced by
The variable $st does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
3330
3331
	// Invalidate the opcode cache
3332
	if (function_exists('opcache_invalidate'))
3333
   		opcache_invalidate($cachedir . '/data_' . $key . '.php', true);
3334
3335
	if (function_exists('apc_delete_file'))
3336
   		@apc_delete_file($cachedir . '/data_' . $key . '.php');
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...
3337
}
3338
3339
/**
3340
 * Gets the value from the cache specified by key, so long as it is not older than ttl seconds.
3341
 * - It may often "miss", so shouldn't be depended on.
3342
 * - It supports the same as cache_put_data().
3343
 *
3344
 * @param string $key The key for the value to retrieve
3345
 * @param int $ttl The maximum age of the cached data
3346
 * @return string The cached data or null if nothing was loaded
3347
 */
3348
function cache_get_data($key, $ttl = 120)
3349
{
3350
	global $boardurl, $modSettings, $memcached;
3351
	global $cache_hits, $cache_count, $cache_misses, $cache_count_misses, $db_show_debug, $cachedir;
3352
	global $cache_accelerator, $cache_enable, $cache_memcached;
3353
3354
	if (empty($cache_enable))
3355
		return;
3356
3357
	$cache_count = isset($cache_count) ? $cache_count + 1 : 1;
3358
	if (isset($db_show_debug) && $db_show_debug === true)
3359
	{
3360
		$cache_hits[$cache_count] = array('k' => $key, 'd' => 'get');
3361
		$st = microtime();
3362
		$original_key = $key;
3363
	}
3364
3365
	$key = md5($boardurl . filemtime($cachedir . '/' . 'index.php')) . '-SMF-' . strtr($key, ':/', '-_');
3366
3367
	switch ($cache_accelerator)
3368
	{
3369
		case 'memcached':
3370
			// Okay, let's go for it memcached!
3371
			if ((function_exists('memcache_get') || function_exists('memcached_get')) && isset($cache_memcached) && trim($cache_memcached) != '')
3372
			{
3373
				// Not connected yet?
3374
				if (empty($memcached))
3375
					get_memcached_server();
3376
				if (!$memcached)
3377
				{
3378
					$cache_misses_count = isset($cache_misses) ? $cache_misses + 1 : 1;
0 ignored issues
show
Unused Code introduced by
$cache_misses_count is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
3379
					$value = null;
3380
				}
3381
				else
3382
					$value = (function_exists('memcache_get')) ? memcache_get($memcached, $key) : memcached_get($memcached, $key);
3383
			}
3384
			break;
3385
		case 'apc':
3386
			// This is the free APC from PECL.
3387
			if (function_exists('apc_fetch'))
3388
				$value = apc_fetch($key . 'smf');
3389
			break;
3390
		case 'apcu':
3391
			// APC User Cache. A continuation of the now-unsupported APC but without opcode cache
3392
			if (function_exists('apcu_fetch'))
3393
				$value = apcu_fetch($key . 'smf');
3394
			break;
3395 View Code Duplication
		case 'zend':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
3396
			// Zend's pricey stuff.
3397
			if (function_exists('zend_shm_cache_fetch'))
3398
				$value = zend_shm_cache_fetch('SMF::' . $key);
3399
			elseif (function_exists('output_cache_get'))
3400
				$value = output_cache_get($key, $ttl);
3401
			break;
3402
		case 'xcache':
3403
			if (function_exists('xcache_get') && ini_get('xcache.var_size') > 0)
3404
				$value = xcache_get($key);
3405
			break;
3406
		default:
3407
			// Otherwise it's SMF data!
3408
			if (file_exists($cachedir . '/data_' . $key . '.php') && filesize($cachedir . '/data_' . $key . '.php') > 10)
3409
			{
3410
				// Work around Zend's opcode caching (PHP 5.5+), they would cache older files for a couple of seconds
3411
				// causing newer files to take effect a while later.
3412
				if (function_exists('opcache_invalidate'))
3413
					opcache_invalidate($cachedir . '/data_' . $key . '.php', true);
3414
3415
				// php will cache file_exists et all, we can't 100% depend on its results so proceed with caution
3416
				@include($cachedir . '/data_' . $key . '.php');
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...
3417
				if (!empty($expired) && isset($value))
0 ignored issues
show
Bug introduced by
The variable $expired seems to never exist, and therefore empty should always return true. Did you maybe rename this variable?

This check looks for calls to isset(...) or empty() on variables that are yet undefined. These calls will always produce the same result and can be removed.

This is most likely caused by the renaming of a variable or the removal of a function/method parameter.

Loading history...
Bug introduced by
The variable $value seems only to be defined at a later point. As such the call to isset() seems to always evaluate to false.

This check marks calls to isset(...) or empty(...) that are found before the variable itself is defined. These will always have the same result.

This is likely the result of code being shifted around. Consider removing these calls.

Loading history...
3418
				{
3419
					@unlink($cachedir . '/data_' . $key . '.php');
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...
3420
					unset($value);
3421
				}
3422
			}
3423
			break;
3424
	}
3425
3426
	if (isset($db_show_debug) && $db_show_debug === true)
3427
	{
3428
		$cache_hits[$cache_count]['t'] = array_sum(explode(' ', microtime())) - array_sum(explode(' ', $st));
0 ignored issues
show
Bug introduced by
The variable $st does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
3429
		$cache_hits[$cache_count]['s'] = isset($value) ? strlen($value) : 0;
3430
3431
		if (empty($value))
3432
		{
3433
			if (!isset($cache_misses))
3434
				$cache_misses = array();
3435
3436
			$cache_count_misses = isset($cache_count_misses) ? $cache_count_misses + 1 : 1;
3437
			$cache_misses[$cache_count_misses] = array('k' => $original_key, 'd' => 'get');
0 ignored issues
show
Bug introduced by
The variable $original_key does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
3438
		}
3439
	}
3440
3441
	if (function_exists('call_integration_hook') && isset($value))
3442
		call_integration_hook('cache_get_data', array(&$key, &$ttl, &$value));
3443
3444
	return empty($value) ? null : smf_json_decode($value, true);
3445
}
3446
3447
/**
3448
 * Get memcache servers.
3449
 *
3450
 * - This function is used by cache_get_data() and cache_put_data().
3451
 * - It attempts to connect to a random server in the cache_memcached setting.
3452
 * - It recursively calls itself up to $level times.
3453
 *
3454
 * @param int $level The maximum number of times to call this function recursively
3455
 */
3456
function get_memcached_server($level = 3)
3457
{
3458
	global $memcached, $db_persist, $cache_memcached;
3459
3460
	$servers = explode(',', $cache_memcached);
3461
	$server = trim($servers[array_rand($servers)]);
3462
3463
	$port = 0;
3464
3465
	// Normal host names do not contain slashes, while e.g. unix sockets do. Assume alternative transport pipe with port 0.
3466
	if(strpos($server,'/') !== false)
3467
		$host = $server;
3468
	else
3469
	{
3470
		$server = explode(':', $server);
3471
		$host = $server[0];
3472
		$port = isset($server[1]) ? $server[1] : 11211;
3473
	}
3474
3475
	$cache = (function_exists('memcache_get')) ? 'memcache' : ((function_exists('memcached_get') ? 'memcached' : ''));
3476
3477
	// Don't try more times than we have servers!
3478
	$level = min(count($servers), $level);
3479
3480
	// Don't wait too long: yes, we want the server, but we might be able to run the query faster!
3481
	if (empty($db_persist))
3482
	{
3483
		if ($cache === 'memcached')
3484
			$memcached = memcached_connect($host, $port);
3485
		if ($cache === 'memcache')
3486
			$memcached = memcache_connect($host, $port);
3487
	}
3488
	else
3489
	{
3490
		if ($cache === 'memcached')
3491
			$memcached = memcached_pconnect($host, $port);
3492
		if ($cache === 'memcache')
3493
			$memcached = memcache_pconnect($host, $port);
3494
	}
3495
3496
	if (!$memcached && $level > 0)
3497
		get_memcached_server($level - 1);
3498
}
3499
3500
/**
3501
 * Helper function to set an array of data for an user's avatar.
3502
 *
3503
 * Makes assumptions based on the data provided, the following keys are required:
3504
 * - avatar The raw "avatar" column in members table
3505
 * - email The user's email. Used to get the gravatar info
3506
 * - filename The attachment filename
3507
 *
3508
 * @param array $data An array of raw info
3509
 * @return array An array of avatar data
3510
 */
3511
function set_avatar_data($data = array())
3512
{
3513
	global $modSettings, $boardurl, $smcFunc, $image_proxy_enabled, $image_proxy_secret;
3514
3515
	// Come on!
3516
	if (empty($data))
3517
		return array();
3518
3519
	// Set a nice default var.
3520
	$image = '';
3521
3522
	// Gravatar has been set as mandatory!
3523
	if (!empty($modSettings['gravatarOverride']))
3524
	{
3525
		if (!empty($modSettings['gravatarAllowExtraEmail']) && !empty($data['avatar']) && stristr($data['avatar'], 'gravatar://'))
3526
			$image = get_gravatar_url($smcFunc['substr']($data['avatar'], 11));
3527
3528
		else if (!empty($data['email']))
3529
			$image = get_gravatar_url($data['email']);
3530
	}
3531
3532
	// Look if the user has a gravatar field or has set an external url as avatar.
3533
	else
3534
	{
3535
		// So it's stored in the member table?
3536
		if (!empty($data['avatar']))
3537
		{
3538
			// Gravatar.
3539
			if (stristr($data['avatar'], 'gravatar://'))
3540
			{
3541
				if ($data['avatar'] == 'gravatar://')
3542
					$image = get_gravatar_url($data['email']);
3543
3544
				elseif (!empty($modSettings['gravatarAllowExtraEmail']))
3545
					$image = get_gravatar_url($smcFunc['substr']($data['avatar'], 11));
3546
			}
3547
3548
			// External url.
3549
			else
3550
			{
3551
				// Using ssl?
3552
				if (!empty($modSettings['force_ssl']) && $image_proxy_enabled && stripos($data['avatar'], 'http://') !== false)
3553
					$image = strtr($boardurl, array('http://' => 'https://')) . '/proxy.php?request=' . urlencode($data['avatar']) . '&hash=' . md5($data['avatar'] . $image_proxy_secret);
3554
3555
				// Just a plain external url.
3556
				else
3557
					$image = (stristr($data['avatar'], 'http://') || stristr($data['avatar'], 'https://')) ? $data['avatar'] : $modSettings['avatar_url'] . '/' . $data['avatar'];
3558
			}
3559
		}
3560
3561
		// Perhaps this user has an attachment as avatar...
3562
		else if (!empty($data['filename']))
3563
			$image = $modSettings['custom_avatar_url'] . '/' . $data['filename'];
3564
3565
		// Right... no avatar... use our default image.
3566
		else
3567
			$image = $modSettings['avatar_url'] . '/default.png';
3568
	}
3569
3570
	call_integration_hook('integrate_set_avatar_data', array(&$image, &$data));
3571
3572
	// At this point in time $image has to be filled unless you chose to force gravatar and the user doesn't have the needed data to retrieve it... thus a check for !empty() is still needed.
3573
	if (!empty($image))
3574
		return array(
3575
			'name' => !empty($data['avatar']) ? $data['avatar'] : '',
3576
			'image' => '<img class="avatar" src="' . $image . '" />',
3577
			'href' => $image,
3578
			'url' => $image,
3579
		);
3580
3581
	// Fallback to make life easier for everyone...
3582
	else
3583
		return array(
3584
			'name' => '',
3585
			'image' => '',
3586
			'href' => '',
3587
			'url' => '',
3588
		);
3589
}
3590
3591
?>
0 ignored issues
show
Best Practice introduced by
It is not recommended to use PHP's closing tag ?> in files other than templates.

Using a closing tag in PHP files that only contain PHP code is not recommended as you might accidentally add whitespace after the closing tag which would then be output by PHP. This can cause severe problems, for example headers cannot be sent anymore.

A simple precaution is to leave off the closing tag as it is not required, and it also has no negative effects whatsoever.

Loading history...