Completed
Push — release-2.1 ( af8dd6...5b3c87 )
by Michael
20:47 queued 10:04
created

Load.php ➔ cache_get_data()   F

Complexity

Conditions 16
Paths 353

Size

Total Lines 39
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 16
eloc 22
nc 353
nop 2
dl 0
loc 39
rs 3.7109
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 2017 Simple Machines and individual contributors
11
 * @license http://www.simplemachines.org/about/smf/license.php BSD
12
 *
13
 * @version 2.1 Beta 4
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']('', '
30
			SET NAMES {string:db_character_set}',
31
			array(
32
				'db_character_set' => $db_character_set,
33
			)
34
		);
35
36
	// We need some caching support, maybe.
37
	loadCacheAccelerator();
38
39
	// Try to load it from the cache first; it'll never get cached if the setting is off.
40
	if (($modSettings = cache_get_data('modSettings', 90)) == null)
41
	{
42
		$request = $smcFunc['db_query']('', '
43
			SELECT variable, value
44
			FROM {db_prefix}settings',
45
			array(
46
			)
47
		);
48
		$modSettings = array();
49
		if (!$request)
50
			display_db_error();
51
		while ($row = $smcFunc['db_fetch_row']($request))
52
			$modSettings[$row[0]] = $row[1];
53
		$smcFunc['db_free_result']($request);
54
55
		// Do a few things to protect against missing settings or settings with invalid values...
56 View Code Duplication
		if (empty($modSettings['defaultMaxTopics']) || $modSettings['defaultMaxTopics'] <= 0 || $modSettings['defaultMaxTopics'] > 999)
57
			$modSettings['defaultMaxTopics'] = 20;
58 View Code Duplication
		if (empty($modSettings['defaultMaxMessages']) || $modSettings['defaultMaxMessages'] <= 0 || $modSettings['defaultMaxMessages'] > 999)
59
			$modSettings['defaultMaxMessages'] = 15;
60 View Code Duplication
		if (empty($modSettings['defaultMaxMembers']) || $modSettings['defaultMaxMembers'] <= 0 || $modSettings['defaultMaxMembers'] > 999)
61
			$modSettings['defaultMaxMembers'] = 30;
62 View Code Duplication
		if (empty($modSettings['defaultMaxListItems']) || $modSettings['defaultMaxListItems'] <= 0 || $modSettings['defaultMaxListItems'] > 999)
63
			$modSettings['defaultMaxListItems'] = 15;
64
65
		// We excpiclity do not use $smcFunc['json_decode'] here yet, as $smcFunc is not fully loaded.
66
		if (!is_array($modSettings['attachmentUploadDir']))
67
			$modSettings['attachmentUploadDir'] = smf_json_decode($modSettings['attachmentUploadDir'], true);
68
69
		if (!empty($cache_enable))
70
			cache_put_data('modSettings', $modSettings, 90);
71
	}
72
73
	$modSettings['cache_enable'] = $cache_enable;
74
75
	// UTF-8 ?
76
	$utf8 = (empty($modSettings['global_character_set']) ? $txt['lang_character_set'] : $modSettings['global_character_set']) === 'UTF-8';
77
78
	// Set a list of common functions.
79
	$ent_list = empty($modSettings['disableEntityCheck']) ? '&(#\d{1,7}|quot|amp|lt|gt|nbsp);' : '&(#021|quot|amp|lt|gt|nbsp);';
80
	$ent_check = empty($modSettings['disableEntityCheck']) ? function($string)
81
		{
82
			$string = preg_replace_callback('~(&#(\d{1,7}|x[0-9a-fA-F]{1,6});)~', 'entity_fix__callback', $string);
83
			return $string;
84
		} : function($string)
85
		{
86
			return $string;
87
		};
88
	$fix_utf8mb4 = function($string) use ($utf8, $smcFunc)
89
	{
90
		if (!$utf8 || $smcFunc['db_mb4'])
91
			return $string;
92
93
		$i = 0;
94
		$len = strlen($string);
95
		$new_string = '';
96
		while ($i < $len)
97
		{
98
			$ord = ord($string[$i]);
99
			if ($ord < 128)
100
			{
101
				$new_string .= $string[$i];
102
				$i++;
103
			}
104 View Code Duplication
			elseif ($ord < 224)
105
			{
106
				$new_string .= $string[$i] . $string[$i + 1];
107
				$i += 2;
108
			}
109 View Code Duplication
			elseif ($ord < 240)
110
			{
111
				$new_string .= $string[$i] . $string[$i + 1] . $string[$i + 2];
112
				$i += 3;
113
			}
114
			elseif ($ord < 248)
115
			{
116
				// Magic happens.
117
				$val = (ord($string[$i]) & 0x07) << 18;
118
				$val += (ord($string[$i + 1]) & 0x3F) << 12;
119
				$val += (ord($string[$i + 2]) & 0x3F) << 6;
120
				$val += (ord($string[$i + 3]) & 0x3F);
121
				$new_string .= '&#' . $val . ';';
122
				$i += 4;
123
			}
124
		}
125
		return $new_string;
126
	};
127
128
	// Preg_replace space characters depend on the character set in use
129
	$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';
130
131
	// global array of anonymous helper functions, used mostly to properly handle multi byte strings
132
	$smcFunc += array(
133
		'entity_fix' => function($string)
134
		{
135
			$num = $string[0] === 'x' ? hexdec(substr($string, 1)) : (int) $string;
136
			return $num < 0x20 || $num > 0x10FFFF || ($num >= 0xD800 && $num <= 0xDFFF) || $num === 0x202E || $num === 0x202D ? '' : '&#' . $num . ';';
137
		},
138
		'htmlspecialchars' => function($string, $quote_style = ENT_COMPAT, $charset = 'ISO-8859-1') use ($ent_check, $utf8, $fix_utf8mb4)
139
		{
140
			return $fix_utf8mb4($ent_check(htmlspecialchars($string, $quote_style, $utf8 ? 'UTF-8' : $charset)));
141
		},
142
		'htmltrim' => function($string) use ($utf8, $space_chars, $ent_check)
143
		{
144
			return preg_replace('~^(?:[ \t\n\r\x0B\x00' . $space_chars . ']|&nbsp;)+|(?:[ \t\n\r\x0B\x00' . $space_chars . ']|&nbsp;)+$~' . ($utf8 ? 'u' : ''), '', $ent_check($string));
145
		},
146
		'strlen' => function($string) use ($ent_list, $utf8, $ent_check)
147
		{
148
			return strlen(preg_replace('~' . $ent_list . ($utf8 ? '|.~u' : '~'), '_', $ent_check($string)));
149
		},
150
		'strpos' => function($haystack, $needle, $offset = 0) use ($utf8, $ent_check, $modSettings)
151
		{
152
			$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);
153
154
			if (strlen($needle) === 1)
155
			{
156
				$result = array_search($needle, array_slice($haystack_arr, $offset));
157
				return is_int($result) ? $result + $offset : false;
158
			}
159
			else
160
			{
161
				$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);
162
				$needle_size = count($needle_arr);
163
164
				$result = array_search($needle_arr[0], array_slice($haystack_arr, $offset));
165
				while ((int) $result === $result)
166
				{
167
					$offset += $result;
168
					if (array_slice($haystack_arr, $offset, $needle_size) === $needle_arr)
169
						return $offset;
170
					$result = array_search($needle_arr[0], array_slice($haystack_arr, ++$offset));
171
				}
172
				return false;
173
			}
174
		},
175
		'substr' => function($string, $start, $length = null) use ($utf8, $ent_check, $modSettings)
176
		{
177
			$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);
178
			return $length === null ? implode('', array_slice($ent_arr, $start)) : implode('', array_slice($ent_arr, $start, $length));
179
		},
180 View Code Duplication
		'strtolower' => $utf8 ? function($string) use ($sourcedir)
181
		{
182
			if (!function_exists('mb_strtolower'))
183
			{
184
				require_once($sourcedir . '/Subs-Charset.php');
185
				return utf8_strtolower($string);
186
			}
187
188
			return mb_strtolower($string, 'UTF-8');
189
		} : 'strtolower',
190
		'strtoupper' => $utf8 ? function($string)
191
		{
192
			global $sourcedir;
193
194
			if (!function_exists('mb_strtolower'))
195
			{
196
				require_once($sourcedir . '/Subs-Charset.php');
197
				return utf8_strtoupper($string);
198
			}
199
200
			return mb_strtoupper($string, 'UTF-8');
201
		} : 'strtoupper',
202
		'truncate' => function($string, $length) use ($utf8, $ent_check, $ent_list, &$smcFunc)
203
		{
204
			$string = $ent_check($string);
205
			preg_match('~^(' . $ent_list . '|.){' . $smcFunc['strlen'](substr($string, 0, $length)) . '}~' . ($utf8 ? 'u' : ''), $string, $matches);
206
			$string = $matches[0];
207
			while (strlen($string) > $length)
208
				$string = preg_replace('~(?:' . $ent_list . '|.)$~' . ($utf8 ? 'u' : ''), '', $string);
209
			return $string;
210
		},
211
		'ucfirst' => $utf8 ? function($string) use (&$smcFunc)
212
		{
213
			return $smcFunc['strtoupper']($smcFunc['substr']($string, 0, 1)) . $smcFunc['substr']($string, 1);
214
		} : 'ucfirst',
215
		'ucwords' => $utf8 ? function($string) use (&$smcFunc)
216
		{
217
			$words = preg_split('~([\s\r\n\t]+)~', $string, -1, PREG_SPLIT_DELIM_CAPTURE);
218
			for ($i = 0, $n = count($words); $i < $n; $i += 2)
219
				$words[$i] = $smcFunc['ucfirst']($words[$i]);
220
			return implode('', $words);
221
		} : 'ucwords',
222
		'json_decode' => 'smf_json_decode',
223
		'json_encode' => 'json_encode',
224
	);
225
226
	// Setting the timezone is a requirement for some functions.
227
	if (isset($modSettings['default_timezone']) && in_array($modSettings['default_timezone'], timezone_identifiers_list()))
228
		date_default_timezone_set($modSettings['default_timezone']);
229
	else
230
	{
231
		// Get PHP's default timezone, if set
232
		$ini_tz = ini_get('date.timezone');
233
		if (!empty($ini_tz))
234
			$modSettings['default_timezone'] = $ini_tz;
235
		else
236
			$modSettings['default_timezone'] = '';
237
238
		// If date.timezone is unset, invalid, or just plain weird, make a best guess
239 View Code Duplication
		if (!in_array($modSettings['default_timezone'], timezone_identifiers_list()))
240
		{
241
			$server_offset = @mktime(0, 0, 0, 1, 1, 1970);
242
			$modSettings['default_timezone'] = timezone_name_from_abbr('', $server_offset, 0);
243
		}
244
245
		date_default_timezone_set($modSettings['default_timezone']);
246
	}
247
248
	// Check the load averages?
249
	if (!empty($modSettings['loadavg_enable']))
250
	{
251
		if (($modSettings['load_average'] = cache_get_data('loadavg', 90)) == null)
252
		{
253
			$modSettings['load_average'] = @file_get_contents('/proc/loadavg');
254
			if (!empty($modSettings['load_average']) && preg_match('~^([^ ]+?) ([^ ]+?) ([^ ]+)~', $modSettings['load_average'], $matches) != 0)
255
				$modSettings['load_average'] = (float) $matches[1];
256 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)
257
				$modSettings['load_average'] = (float) $matches[1];
258
			else
259
				unset($modSettings['load_average']);
260
261
			if (!empty($modSettings['load_average']) || $modSettings['load_average'] === 0.0)
262
				cache_put_data('loadavg', $modSettings['load_average'], 90);
263
		}
264
265
		if (!empty($modSettings['load_average']) || $modSettings['load_average'] === 0.0)
266
			call_integration_hook('integrate_load_average', array($modSettings['load_average']));
267
268
		if (!empty($modSettings['loadavg_forum']) && !empty($modSettings['load_average']) && $modSettings['load_average'] >= $modSettings['loadavg_forum'])
269
			display_loadavg_error();
270
	}
271
272
	// Is post moderation alive and well? Everywhere else assumes this has been defined, so let's make sure it is.
273
	$modSettings['postmod_active'] = !empty($modSettings['postmod_active']);
274
275
	// Here to justify the name of this function. :P
276
	// It should be added to the install and upgrade scripts.
277
	// But since the converters need to be updated also. This is easier.
278
	if (empty($modSettings['currentAttachmentUploadDir']))
279
	{
280
		updateSettings(array(
281
			'attachmentUploadDir' => $smcFunc['json_encode'](array(1 => $modSettings['attachmentUploadDir'])),
282
			'currentAttachmentUploadDir' => 1,
283
		));
284
	}
285
286
	// Integration is cool.
287
	if (defined('SMF_INTEGRATION_SETTINGS'))
288
	{
289
		$integration_settings = $smcFUnc['json_decode'](SMF_INTEGRATION_SETTINGS, true);
0 ignored issues
show
Bug introduced by
The variable $smcFUnc does not exist. Did you mean $smcFunc?

This check looks for variables that are accessed but have not been defined. It raises an issue if it finds another variable that has a similar name.

The variable may have been renamed without also renaming all references.

Loading history...
290
		foreach ($integration_settings as $hook => $function)
291
			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...
292
	}
293
294
	// Any files to pre include?
295 View Code Duplication
	if (!empty($modSettings['integrate_pre_include']))
296
	{
297
		$pre_includes = explode(',', $modSettings['integrate_pre_include']);
298
		foreach ($pre_includes as $include)
299
		{
300
			$include = strtr(trim($include), array('$boarddir' => $boarddir, '$sourcedir' => $sourcedir));
301
			if (file_exists($include))
302
				require_once($include);
303
		}
304
	}
305
306
	// This determines the server... not used in many places, except for login fixing.
307
	$context['server'] = array(
308
		'is_iis' => isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS') !== false,
309
		'is_apache' => isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'], 'Apache') !== false,
310
		'is_litespeed' => isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'], 'LiteSpeed') !== false,
311
		'is_lighttpd' => isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'], 'lighttpd') !== false,
312
		'is_nginx' => isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'], 'nginx') !== false,
313
		'is_cgi' => isset($_SERVER['SERVER_SOFTWARE']) && strpos(php_sapi_name(), 'cgi') !== false,
314
		'is_windows' => strpos(PHP_OS, 'WIN') === 0,
315
		'iso_case_folding' => ord(strtolower(chr(138))) === 154,
316
	);
317
	// A bug in some versions of IIS under CGI (older ones) makes cookie setting not work with Location: headers.
318
	$context['server']['needs_login_fix'] = $context['server']['is_cgi'] && $context['server']['is_iis'];
319
320
	// Define a list of icons used across multiple places.
321
	$context['stable_icons'] = array('xx', 'thumbup', 'thumbdown', 'exclamation', 'question', 'lamp', 'smiley', 'angry', 'cheesy', 'grin', 'sad', 'wink', 'poll', 'moved', 'recycled', 'clip');
322
323
	// Define an array for custom profile fields placements.
324
	$context['cust_profile_fields_placement'] = array(
325
		'standard',
326
		'icons',
327
		'above_signature',
328
		'below_signature',
329
		'below_avatar',
330
		'above_member',
331
		'bottom_poster',
332
	);
333
334
	// Define an array for content-related <meta> elements (e.g. description, keywords, Open Graph) for the HTML head.
335
	$context['meta_tags'] = array();
336
337
	// Define an array of allowed HTML tags.
338
	$context['allowed_html_tags'] = array(
339
		'<img>',
340
		'<div>',
341
	);
342
343
	// These are the only valid image types for SMF, by default anyway.
344
	$context['validImageTypes'] = array(
345
		1 => 'gif',
346
		2 => 'jpeg',
347
		3 => 'png',
348
		5 => 'psd',
349
		6 => 'bmp',
350
		7 => 'tiff',
351
		8 => 'tiff',
352
		9 => 'jpeg',
353
		14 => 'iff'
354
	);
355
356
	// Define a list of allowed tags for descriptions.
357
	$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',);
358
359
	// Get an error count, if necessary
360
	if (!isset($context['num_errors']))
361
	{
362
		$query = $smcFunc['db_query']('', '
363
			SELECT COUNT(id_error)
364
			FROM {db_prefix}log_errors',
365
			array()
366
		);
367
368
		list($context['num_errors']) = $smcFunc['db_fetch_row']($query);
369
		$smcFunc['db_free_result']($query);
370
	}
371
372
	// Call pre load integration functions.
373
	call_integration_hook('integrate_pre_load');
374
}
375
376
/**
377
 * Load all the important user information.
378
 * What it does:
379
 * 	- sets up the $user_info array
380
 * 	- assigns $user_info['query_wanna_see_board'] for what boards the user can see.
381
 * 	- first checks for cookie or integration validation.
382
 * 	- uses the current session if no integration function or cookie is found.
383
 * 	- checks password length, if member is activated and the login span isn't over.
384
 * 		- if validation fails for the user, $id_member is set to 0.
385
 * 		- updates the last visit time when needed.
386
 */
387
function loadUserSettings()
388
{
389
	global $modSettings, $user_settings, $sourcedir, $smcFunc;
390
	global $cookiename, $user_info, $language, $context, $image_proxy_enabled, $image_proxy_secret, $boardurl;
391
392
	// Check first the integration, then the cookie, and last the session.
393
	if (count($integration_ids = call_integration_hook('integrate_verify_user')) > 0)
394
	{
395
		$id_member = 0;
396
		foreach ($integration_ids as $integration_id)
397
		{
398
			$integration_id = (int) $integration_id;
399
			if ($integration_id > 0)
400
			{
401
				$id_member = $integration_id;
402
				$already_verified = true;
403
				break;
404
			}
405
		}
406
	}
407
	else
408
		$id_member = 0;
409
410
	if (empty($id_member) && isset($_COOKIE[$cookiename]))
411
	{
412
		$cookie_data = $smcFunc['json_decode']($_COOKIE[$cookiename], true, false);
413
414
		if (empty($cookie_data))
415
			$cookie_data = safe_unserialize($_COOKIE[$cookiename]);
416
417
		list ($id_member, $password) = $cookie_data;
418
		$id_member = !empty($id_member) && strlen($password) > 0 ? (int) $id_member : 0;
419
	}
420
	elseif (empty($id_member) && isset($_SESSION['login_' . $cookiename]) && ($_SESSION['USER_AGENT'] == $_SERVER['HTTP_USER_AGENT'] || !empty($modSettings['disableCheckUA'])))
421
	{
422
		// @todo Perhaps we can do some more checking on this, such as on the first octet of the IP?
423
		$cookie_data = $smcFunc['json_decode']($_SESSION['login_' . $cookiename]);
424
425
		if (empty($cookie_data))
426
			$cookie_data = safe_unserialize($_SESSION['login_' . $cookiename]);
427
428
		list ($id_member, $password, $login_span) = $cookie_data;
429
		$id_member = !empty($id_member) && strlen($password) == 128 && $login_span > time() ? (int) $id_member : 0;
430
	}
431
432
	// Only load this stuff if the user isn't a guest.
433
	if ($id_member != 0)
434
	{
435
		// Is the member data cached?
436
		if (empty($modSettings['cache_enable']) || $modSettings['cache_enable'] < 2 || ($user_settings = cache_get_data('user_settings-' . $id_member, 60)) == null)
437
		{
438
			$request = $smcFunc['db_query']('', '
439
				SELECT mem.*, COALESCE(a.id_attach, 0) AS id_attach, a.filename, a.attachment_type
440
				FROM {db_prefix}members AS mem
441
					LEFT JOIN {db_prefix}attachments AS a ON (a.id_member = {int:id_member})
442
				WHERE mem.id_member = {int:id_member}
443
				LIMIT 1',
444
				array(
445
					'id_member' => $id_member,
446
				)
447
			);
448
			$user_settings = $smcFunc['db_fetch_assoc']($request);
449
			$smcFunc['db_free_result']($request);
450
451 View Code Duplication
			if (!empty($modSettings['force_ssl']) && $image_proxy_enabled && stripos($user_settings['avatar'], 'http://') !== false)
452
				$user_settings['avatar'] = strtr($boardurl, array('http://' => 'https://')) . '/proxy.php?request=' . urlencode($user_settings['avatar']) . '&hash=' . md5($user_settings['avatar'] . $image_proxy_secret);
453
454
			if (!empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= 2)
455
				cache_put_data('user_settings-' . $id_member, $user_settings, 60);
456
		}
457
458
		// Did we find 'im?  If not, junk it.
459
		if (!empty($user_settings))
460
		{
461
			// As much as the password should be right, we can assume the integration set things up.
462
			if (!empty($already_verified) && $already_verified === true)
463
				$check = true;
464
			// SHA-512 hash should be 128 characters long.
465
			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...
466
				$check = hash_salt($user_settings['passwd'], $user_settings['password_salt']) == $password;
467
			else
468
				$check = false;
469
470
			// Wrong password or not activated - either way, you're going nowhere.
471
			$id_member = $check && ($user_settings['is_activated'] == 1 || $user_settings['is_activated'] == 11) ? (int) $user_settings['id_member'] : 0;
472
		}
473
		else
474
			$id_member = 0;
475
476
		// If we no longer have the member maybe they're being all hackey, stop brute force!
477
		if (!$id_member)
478
		{
479
			require_once($sourcedir . '/LogInOut.php');
480
			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);
481
		}
482
		// Validate for Two Factor Authentication
483
		elseif (!empty($modSettings['tfa_mode']) && $id_member && !empty($user_settings['tfa_secret']) && (empty($_REQUEST['action']) || !in_array($_REQUEST['action'], array('login2', 'logintfa'))))
484
		{
485
			$tfacookie = $cookiename . '_tfa';
486
			$tfasecret = null;
487
488
			$verified = call_integration_hook('integrate_verify_tfa', array($id_member, $user_settings));
489
490
			if (empty($verified) || !in_array(true, $verified))
491
			{
492
				if (!empty($_COOKIE[$tfacookie]))
493
				{
494
					$tfa_data = $smcFunc['json_decode']($_COOKIE[$tfacookie]);
495
496
					list ($tfamember, $tfasecret) = $tfa_data;
497
498
					if (!isset($tfamember, $tfasecret) || (int) $tfamember != $id_member)
499
						$tfasecret = null;
500
				}
501
502
				if (empty($tfasecret) || hash_salt($user_settings['tfa_backup'], $user_settings['password_salt']) != $tfasecret)
503
				{
504
					$id_member = 0;
505
					redirectexit('action=logintfa');
506
				}
507
			}
508
		}
509
		// When authenticating their two factor code, make sure to reset their ID for security
510
		elseif (!empty($modSettings['tfa_mode']) && $id_member && !empty($user_settings['tfa_secret']) && $_REQUEST['action'] == 'logintfa')
511
		{
512
			$id_member = 0;
513
			$context['tfa_member'] = $user_settings;
514
			$user_settings = array();
515
		}
516
		// Are we forcing 2FA? Need to check if the user groups actually require 2FA
517
		elseif (!empty($modSettings['tfa_mode']) && $modSettings['tfa_mode'] >= 2 && $id_member && empty($user_settings['tfa_secret']))
518
		{
519
			if ($modSettings['tfa_mode'] == 2) //only do this if we are just forcing SOME membergroups
520
			{
521
				//Build an array of ALL user membergroups.
522
				$full_groups = array($user_settings['id_group']);
523
				if (!empty($user_settings['additional_groups']))
524
				{
525
					$full_groups = array_merge($full_groups, explode(',', $user_settings['additional_groups']));
526
					$full_groups = array_unique($full_groups); //duplicates, maybe?
527
				}
528
529
				//Find out if any group requires 2FA
530
				$request = $smcFunc['db_query']('', '
531
					SELECT COUNT(id_group) AS total
532
					FROM {db_prefix}membergroups
533
					WHERE tfa_required = {int:tfa_required}
534
						AND id_group IN ({array_int:full_groups})',
535
					array(
536
						'tfa_required' => 1,
537
						'full_groups' => $full_groups,
538
					)
539
				);
540
				$row = $smcFunc['db_fetch_assoc']($request);
541
				$smcFunc['db_free_result']($request);
542
			}
543
			else
544
				$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...
545
546
			$area = !empty($_REQUEST['area']) ? $_REQUEST['area'] : '';
547
			$action = !empty($_REQUEST['action']) ? $_REQUEST['action'] : '';
548
549
			if ($row['total'] > 0 && !in_array($action, array('profile', 'logout')) || ($action == 'profile' && $area != 'tfasetup'))
550
				redirectexit('action=profile;area=tfasetup;forced');
551
		}
552
	}
553
554
	// Found 'im, let's set up the variables.
555
	if ($id_member != 0)
556
	{
557
		// Let's not update the last visit time in these cases...
558
		// 1. SSI doesn't count as visiting the forum.
559
		// 2. RSS feeds and XMLHTTP requests don't count either.
560
		// 3. If it was set within this session, no need to set it again.
561
		// 4. New session, yet updated < five hours ago? Maybe cache can help.
562
		// 5. We're still logging in or authenticating
563
		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))
564
		{
565
			// @todo can this be cached?
566
			// Do a quick query to make sure this isn't a mistake.
567
			$result = $smcFunc['db_query']('', '
568
				SELECT poster_time
569
				FROM {db_prefix}messages
570
				WHERE id_msg = {int:id_msg}
571
				LIMIT 1',
572
				array(
573
					'id_msg' => $user_settings['id_msg_last_visit'],
574
				)
575
			);
576
			list ($visitTime) = $smcFunc['db_fetch_row']($result);
577
			$smcFunc['db_free_result']($result);
578
579
			$_SESSION['id_msg_last_visit'] = $user_settings['id_msg_last_visit'];
580
581
			// If it was *at least* five hours ago...
582
			if ($visitTime < time() - 5 * 3600)
583
			{
584
				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']));
585
				$user_settings['last_login'] = time();
586
587
				if (!empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= 2)
588
					cache_put_data('user_settings-' . $id_member, $user_settings, 60);
589
590
				if (!empty($modSettings['cache_enable']))
591
					cache_put_data('user_last_visit-' . $id_member, $_SESSION['id_msg_last_visit'], 5 * 3600);
592
			}
593
		}
594
		elseif (empty($_SESSION['id_msg_last_visit']))
595
			$_SESSION['id_msg_last_visit'] = $user_settings['id_msg_last_visit'];
596
597
		$username = $user_settings['member_name'];
598
599
		if (empty($user_settings['additional_groups']))
600
			$user_info = array(
601
				'groups' => array($user_settings['id_group'], $user_settings['id_post_group'])
602
			);
603 View Code Duplication
		else
604
			$user_info = array(
605
				'groups' => array_merge(
606
					array($user_settings['id_group'], $user_settings['id_post_group']),
607
					explode(',', $user_settings['additional_groups'])
608
				)
609
			);
610
611
		// Because history has proven that it is possible for groups to go bad - clean up in case.
612
		foreach ($user_info['groups'] as $k => $v)
613
			$user_info['groups'][$k] = (int) $v;
614
615
		// This is a logged in user, so definitely not a spider.
616
		$user_info['possibly_robot'] = false;
617
618
		// Figure out the new time offset.
619
		if (!empty($user_settings['timezone']))
620
		{
621
			// Get the offsets from UTC for the server, then for the user.
622
			$tz_system = new DateTimeZone(@date_default_timezone_get());
623
			$tz_user = new DateTimeZone($user_settings['timezone']);
624
			$time_system = new DateTime('now', $tz_system);
625
			$time_user = new DateTime('now', $tz_user);
626
			$user_info['time_offset'] = ($tz_user->getOffset($time_user) - $tz_system->getOffset($time_system)) / 3600;
627
		}
628
		else
629
		{
630
			// !!! Compatibility.
0 ignored issues
show
Unused Code Comprehensibility introduced by
43% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
631
			$user_info['time_offset'] = empty($user_settings['time_offset']) ? 0 : $user_settings['time_offset'];
632
		}
633
	}
634
	// If the user is a guest, initialize all the critical user settings.
635
	else
636
	{
637
		// This is what a guest's variables should be.
638
		$username = '';
639
		$user_info = array('groups' => array(-1));
640
		$user_settings = array();
641
642
		if (isset($_COOKIE[$cookiename]) && empty($context['tfa_member']))
643
			$_COOKIE[$cookiename] = '';
644
645
		// Expire the 2FA cookie
646
		if (isset($_COOKIE[$cookiename . '_tfa']) && empty($context['tfa_member']))
647
		{
648
			$tfa_data = $smcFunc['json_decode']($_COOKIE[$cookiename . '_tfa'], true);
649
650
			list ($id, $user, $exp, $state, $preserve) = $tfa_data;
651
652
			if (!isset($id, $user, $exp, $state, $preserve) || !$preserve || time() > $exp)
653
			{
654
				$_COOKIE[$cookiename . '_tfa'] = '';
655
				setTFACookie(-3600, 0, '');
656
			}
657
		}
658
659
		// Create a login token if it doesn't exist yet.
660
		if (!isset($_SESSION['token']['post-login']))
661
			createToken('login');
662
		else
663
			list ($context['login_token_var'],,, $context['login_token']) = $_SESSION['token']['post-login'];
664
665
		// Do we perhaps think this is a search robot? Check every five minutes just in case...
666
		if ((!empty($modSettings['spider_mode']) || !empty($modSettings['spider_group'])) && (!isset($_SESSION['robot_check']) || $_SESSION['robot_check'] < time() - 300))
667
		{
668
			require_once($sourcedir . '/ManageSearchEngines.php');
669
			$user_info['possibly_robot'] = SpiderCheck();
670
		}
671
		elseif (!empty($modSettings['spider_mode']))
672
			$user_info['possibly_robot'] = isset($_SESSION['id_robot']) ? $_SESSION['id_robot'] : 0;
673
		// If we haven't turned on proper spider hunts then have a guess!
674
		else
675
		{
676
			$ci_user_agent = strtolower($_SERVER['HTTP_USER_AGENT']);
677
			$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;
678
		}
679
680
		// We don't know the offset...
681
		$user_info['time_offset'] = 0;
682
	}
683
684
	// Set up the $user_info array.
685
	$user_info += array(
686
		'id' => $id_member,
687
		'username' => $username,
688
		'name' => isset($user_settings['real_name']) ? $user_settings['real_name'] : '',
689
		'email' => isset($user_settings['email_address']) ? $user_settings['email_address'] : '',
690
		'passwd' => isset($user_settings['passwd']) ? $user_settings['passwd'] : '',
691
		'language' => empty($user_settings['lngfile']) || empty($modSettings['userLanguage']) ? $language : $user_settings['lngfile'],
692
		'is_guest' => $id_member == 0,
693
		'is_admin' => in_array(1, $user_info['groups']),
694
		'theme' => empty($user_settings['id_theme']) ? 0 : $user_settings['id_theme'],
695
		'last_login' => empty($user_settings['last_login']) ? 0 : $user_settings['last_login'],
696
		'ip' => $_SERVER['REMOTE_ADDR'],
697
		'ip2' => $_SERVER['BAN_CHECK_IP'],
698
		'posts' => empty($user_settings['posts']) ? 0 : $user_settings['posts'],
699
		'time_format' => empty($user_settings['time_format']) ? $modSettings['time_format'] : $user_settings['time_format'],
700
		'avatar' => array(
701
			'url' => isset($user_settings['avatar']) ? $user_settings['avatar'] : '',
702
			'filename' => empty($user_settings['filename']) ? '' : $user_settings['filename'],
703
			'custom_dir' => !empty($user_settings['attachment_type']) && $user_settings['attachment_type'] == 1,
704
			'id_attach' => isset($user_settings['id_attach']) ? $user_settings['id_attach'] : 0
705
		),
706
		'smiley_set' => isset($user_settings['smiley_set']) ? $user_settings['smiley_set'] : '',
707
		'messages' => empty($user_settings['instant_messages']) ? 0 : $user_settings['instant_messages'],
708
		'unread_messages' => empty($user_settings['unread_messages']) ? 0 : $user_settings['unread_messages'],
709
		'alerts' => empty($user_settings['alerts']) ? 0 : $user_settings['alerts'],
710
		'total_time_logged_in' => empty($user_settings['total_time_logged_in']) ? 0 : $user_settings['total_time_logged_in'],
711
		'buddies' => !empty($modSettings['enable_buddylist']) && !empty($user_settings['buddy_list']) ? explode(',', $user_settings['buddy_list']) : array(),
712
		'ignoreboards' => !empty($user_settings['ignore_boards']) && !empty($modSettings['allow_ignore_boards']) ? explode(',', $user_settings['ignore_boards']) : array(),
713
		'ignoreusers' => !empty($user_settings['pm_ignore_list']) ? explode(',', $user_settings['pm_ignore_list']) : array(),
714
		'warning' => isset($user_settings['warning']) ? $user_settings['warning'] : 0,
715
		'permissions' => array(),
716
	);
717
	$user_info['groups'] = array_unique($user_info['groups']);
718
719
	// 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.
720
	if (!empty($user_info['ignoreboards']) && empty($user_info['ignoreboards'][$tmp = count($user_info['ignoreboards']) - 1]))
721
		unset($user_info['ignoreboards'][$tmp]);
722
723
	// Allow the user to change their language.
724
	if (!empty($modSettings['userLanguage']))
725
	{
726
		$languages = getLanguages();
727
728
		// Is it valid?
729
		if (!empty($_GET['language']) && isset($languages[strtr($_GET['language'], './\\:', '____')]))
730
		{
731
			$user_info['language'] = strtr($_GET['language'], './\\:', '____');
732
733
			// Make it permanent for members.
734
			if (!empty($user_info['id']))
735
				updateMemberData($user_info['id'], array('lngfile' => $user_info['language']));
736
			else
737
				$_SESSION['language'] = $user_info['language'];
738
		}
739
		elseif (!empty($_SESSION['language']) && isset($languages[strtr($_SESSION['language'], './\\:', '____')]))
740
			$user_info['language'] = strtr($_SESSION['language'], './\\:', '____');
741
	}
742
743
	// Just build this here, it makes it easier to change/use - administrators can see all boards.
744
	if ($user_info['is_admin'])
745
		$user_info['query_see_board'] = '1=1';
746
	// Otherwise just the groups in $user_info['groups'].
747
	else
748
		$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'] : '') . ')';
749
750
	// Build the list of boards they WANT to see.
751
	// This will take the place of query_see_boards in certain spots, so it better include the boards they can see also
752
753
	// If they aren't ignoring any boards then they want to see all the boards they can see
754
	if (empty($user_info['ignoreboards']))
755
		$user_info['query_wanna_see_board'] = $user_info['query_see_board'];
756
	// Ok I guess they don't want to see all the boards
757
	else
758
		$user_info['query_wanna_see_board'] = '(' . $user_info['query_see_board'] . ' AND b.id_board NOT IN (' . implode(',', $user_info['ignoreboards']) . '))';
759
760
	call_integration_hook('integrate_user_info');
761
}
762
763
/**
764
 * Check for moderators and see if they have access to the board.
765
 * What it does:
766
 * - sets up the $board_info array for current board information.
767
 * - if cache is enabled, the $board_info array is stored in cache.
768
 * - redirects to appropriate post if only message id is requested.
769
 * - is only used when inside a topic or board.
770
 * - determines the local moderators for the board.
771
 * - adds group id 3 if the user is a local moderator for the board they are in.
772
 * - prevents access if user is not in proper group nor a local moderator of the board.
773
 */
774
function loadBoard()
775
{
776
	global $txt, $scripturl, $context, $modSettings;
777
	global $board_info, $board, $topic, $user_info, $smcFunc;
778
779
	// Assume they are not a moderator.
780
	$user_info['is_mod'] = false;
781
	$context['user']['is_mod'] = &$user_info['is_mod'];
782
783
	// Start the linktree off empty..
784
	$context['linktree'] = array();
785
786
	// Have they by chance specified a message id but nothing else?
787
	if (empty($_REQUEST['action']) && empty($topic) && empty($board) && !empty($_REQUEST['msg']))
788
	{
789
		// Make sure the message id is really an int.
790
		$_REQUEST['msg'] = (int) $_REQUEST['msg'];
791
792
		// Looking through the message table can be slow, so try using the cache first.
793
		if (($topic = cache_get_data('msg_topic-' . $_REQUEST['msg'], 120)) === null)
794
		{
795
			$request = $smcFunc['db_query']('', '
796
				SELECT id_topic
797
				FROM {db_prefix}messages
798
				WHERE id_msg = {int:id_msg}
799
				LIMIT 1',
800
				array(
801
					'id_msg' => $_REQUEST['msg'],
802
				)
803
			);
804
805
			// So did it find anything?
806
			if ($smcFunc['db_num_rows']($request))
807
			{
808
				list ($topic) = $smcFunc['db_fetch_row']($request);
809
				$smcFunc['db_free_result']($request);
810
				// Save save save.
811
				cache_put_data('msg_topic-' . $_REQUEST['msg'], $topic, 120);
812
			}
813
		}
814
815
		// Remember redirection is the key to avoiding fallout from your bosses.
816
		if (!empty($topic))
817
			redirectexit('topic=' . $topic . '.msg' . $_REQUEST['msg'] . '#msg' . $_REQUEST['msg']);
818
		else
819
		{
820
			loadPermissions();
821
			loadTheme();
822
			fatal_lang_error('topic_gone', false);
823
		}
824
	}
825
826
	// Load this board only if it is specified.
827
	if (empty($board) && empty($topic))
828
	{
829
		$board_info = array('moderators' => array(), 'moderator_groups' => array());
830
		return;
831
	}
832
833
	if (!empty($modSettings['cache_enable']) && (empty($topic) || $modSettings['cache_enable'] >= 3))
834
	{
835
		// @todo SLOW?
836
		if (!empty($topic))
837
			$temp = cache_get_data('topic_board-' . $topic, 120);
838
		else
839
			$temp = cache_get_data('board-' . $board, 120);
840
841
		if (!empty($temp))
842
		{
843
			$board_info = $temp;
844
			$board = $board_info['id'];
845
		}
846
	}
847
848
	if (empty($temp))
849
	{
850
		$request = $smcFunc['db_query']('load_board_info', '
851
			SELECT
852
				c.id_cat, b.name AS bname, b.description, b.num_topics, b.member_groups, b.deny_member_groups,
853
				b.id_parent, c.name AS cname, COALESCE(mg.id_group, 0) AS id_moderator_group, mg.group_name,
854
				COALESCE(mem.id_member, 0) AS id_moderator,
855
				mem.real_name' . (!empty($topic) ? ', b.id_board' : '') . ', b.child_level,
856
				b.id_theme, b.override_theme, b.count_posts, b.id_profile, b.redirect,
857
				b.unapproved_topics, b.unapproved_posts' . (!empty($topic) ? ', t.approved, t.id_member_started' : '') . '
858
			FROM {db_prefix}boards AS b' . (!empty($topic) ? '
859
				INNER JOIN {db_prefix}topics AS t ON (t.id_topic = {int:current_topic})' : '') . '
860
				LEFT JOIN {db_prefix}categories AS c ON (c.id_cat = b.id_cat)
861
				LEFT JOIN {db_prefix}moderator_groups AS modgs ON (modgs.id_board = {raw:board_link})
862
				LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = modgs.id_group)
863
				LEFT JOIN {db_prefix}moderators AS mods ON (mods.id_board = {raw:board_link})
864
				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = mods.id_member)
865
			WHERE b.id_board = {raw:board_link}',
866
			array(
867
				'current_topic' => $topic,
868
				'board_link' => empty($topic) ? $smcFunc['db_quote']('{int:current_board}', array('current_board' => $board)) : 't.id_board',
869
			)
870
		);
871
		// If there aren't any, skip.
872
		if ($smcFunc['db_num_rows']($request) > 0)
873
		{
874
			$row = $smcFunc['db_fetch_assoc']($request);
875
876
			// Set the current board.
877
			if (!empty($row['id_board']))
878
				$board = $row['id_board'];
879
880
			// Basic operating information. (globals... :/)
881
			$board_info = array(
882
				'id' => $board,
883
				'moderators' => array(),
884
				'moderator_groups' => array(),
885
				'cat' => array(
886
					'id' => $row['id_cat'],
887
					'name' => $row['cname']
888
				),
889
				'name' => $row['bname'],
890
				'description' => $row['description'],
891
				'num_topics' => $row['num_topics'],
892
				'unapproved_topics' => $row['unapproved_topics'],
893
				'unapproved_posts' => $row['unapproved_posts'],
894
				'unapproved_user_topics' => 0,
895
				'parent_boards' => getBoardParents($row['id_parent']),
896
				'parent' => $row['id_parent'],
897
				'child_level' => $row['child_level'],
898
				'theme' => $row['id_theme'],
899
				'override_theme' => !empty($row['override_theme']),
900
				'profile' => $row['id_profile'],
901
				'redirect' => $row['redirect'],
902
				'recycle' => !empty($modSettings['recycle_enable']) && !empty($modSettings['recycle_board']) && $modSettings['recycle_board'] == $board,
903
				'posts_count' => empty($row['count_posts']),
904
				'cur_topic_approved' => empty($topic) || $row['approved'],
905
				'cur_topic_starter' => empty($topic) ? 0 : $row['id_member_started'],
906
			);
907
908
			// Load the membergroups allowed, and check permissions.
909
			$board_info['groups'] = $row['member_groups'] == '' ? array() : explode(',', $row['member_groups']);
910
			$board_info['deny_groups'] = $row['deny_member_groups'] == '' ? array() : explode(',', $row['deny_member_groups']);
911
912
			do
913
			{
914 View Code Duplication
				if (!empty($row['id_moderator']))
915
					$board_info['moderators'][$row['id_moderator']] = array(
916
						'id' => $row['id_moderator'],
917
						'name' => $row['real_name'],
918
						'href' => $scripturl . '?action=profile;u=' . $row['id_moderator'],
919
						'link' => '<a href="' . $scripturl . '?action=profile;u=' . $row['id_moderator'] . '">' . $row['real_name'] . '</a>'
920
					);
921
922 View Code Duplication
				if (!empty($row['id_moderator_group']))
923
					$board_info['moderator_groups'][$row['id_moderator_group']] = array(
924
						'id' => $row['id_moderator_group'],
925
						'name' => $row['group_name'],
926
						'href' => $scripturl . '?action=groups;sa=members;group=' . $row['id_moderator_group'],
927
						'link' => '<a href="' . $scripturl . '?action=groups;sa=members;group=' . $row['id_moderator_group'] . '">' . $row['group_name'] . '</a>'
928
					);
929
			}
930
			while ($row = $smcFunc['db_fetch_assoc']($request));
931
932
			// If the board only contains unapproved posts and the user isn't an approver then they can't see any topics.
933
			// If that is the case do an additional check to see if they have any topics waiting to be approved.
934
			if ($board_info['num_topics'] == 0 && $modSettings['postmod_active'] && !allowedTo('approve_posts'))
935
			{
936
				// Free the previous result
937
				$smcFunc['db_free_result']($request);
938
939
				// @todo why is this using id_topic?
940
				// @todo Can this get cached?
941
				$request = $smcFunc['db_query']('', '
942
					SELECT COUNT(id_topic)
943
					FROM {db_prefix}topics
944
					WHERE id_member_started={int:id_member}
945
						AND approved = {int:unapproved}
946
						AND id_board = {int:board}',
947
					array(
948
						'id_member' => $user_info['id'],
949
						'unapproved' => 0,
950
						'board' => $board,
951
					)
952
				);
953
954
				list ($board_info['unapproved_user_topics']) = $smcFunc['db_fetch_row']($request);
955
			}
956
957
			if (!empty($modSettings['cache_enable']) && (empty($topic) || $modSettings['cache_enable'] >= 3))
958
			{
959
				// @todo SLOW?
960
				if (!empty($topic))
961
					cache_put_data('topic_board-' . $topic, $board_info, 120);
962
				cache_put_data('board-' . $board, $board_info, 120);
963
			}
964
		}
965
		else
966
		{
967
			// Otherwise the topic is invalid, there are no moderators, etc.
968
			$board_info = array(
969
				'moderators' => array(),
970
				'moderator_groups' => array(),
971
				'error' => 'exist'
972
			);
973
			$topic = null;
974
			$board = 0;
975
		}
976
		$smcFunc['db_free_result']($request);
977
	}
978
979
	if (!empty($topic))
980
		$_GET['board'] = (int) $board;
981
982
	if (!empty($board))
983
	{
984
		// Get this into an array of keys for array_intersect
985
		$moderator_groups = array_keys($board_info['moderator_groups']);
986
987
		// Now check if the user is a moderator.
988
		$user_info['is_mod'] = isset($board_info['moderators'][$user_info['id']]) || count(array_intersect($user_info['groups'], $moderator_groups)) != 0;
989
990 View Code Duplication
		if (count(array_intersect($user_info['groups'], $board_info['groups'])) == 0 && !$user_info['is_admin'])
991
			$board_info['error'] = 'access';
992 View Code Duplication
		if (!empty($modSettings['deny_boards_access']) && count(array_intersect($user_info['groups'], $board_info['deny_groups'])) != 0 && !$user_info['is_admin'])
993
			$board_info['error'] = 'access';
994
995
		// Build up the linktree.
996
		$context['linktree'] = array_merge(
997
			$context['linktree'],
998
			array(array(
999
				'url' => $scripturl . '#c' . $board_info['cat']['id'],
1000
				'name' => $board_info['cat']['name']
1001
			)),
1002
			array_reverse($board_info['parent_boards']),
1003
			array(array(
1004
				'url' => $scripturl . '?board=' . $board . '.0',
1005
				'name' => $board_info['name']
1006
			))
1007
		);
1008
	}
1009
1010
	// Set the template contextual information.
1011
	$context['user']['is_mod'] = &$user_info['is_mod'];
1012
	$context['current_topic'] = $topic;
1013
	$context['current_board'] = $board;
1014
1015
	// No posting in redirection boards!
1016
	if (!empty($_REQUEST['action']) && $_REQUEST['action'] == 'post' && !empty($board_info['redirect']))
1017
		$board_info['error'] == 'post_in_redirect';
1018
1019
	// Hacker... you can't see this topic, I'll tell you that. (but moderators can!)
1020
	if (!empty($board_info['error']) && (!empty($modSettings['deny_boards_access']) || $board_info['error'] != 'access' || !$user_info['is_mod']))
1021
	{
1022
		// The permissions and theme need loading, just to make sure everything goes smoothly.
1023
		loadPermissions();
1024
		loadTheme();
1025
1026
		$_GET['board'] = '';
1027
		$_GET['topic'] = '';
1028
1029
		// The linktree should not give the game away mate!
1030
		$context['linktree'] = array(
1031
			array(
1032
				'url' => $scripturl,
1033
				'name' => $context['forum_name_html_safe']
1034
			)
1035
		);
1036
1037
		// If it's a prefetching agent or we're requesting an attachment.
1038
		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...
1039
		{
1040
			ob_end_clean();
1041
			header('HTTP/1.1 403 Forbidden');
1042
			die;
1043
		}
1044
		elseif ($board_info['error'] == 'post_in_redirect')
1045
		{
1046
			// Slightly different error message here...
1047
			fatal_lang_error('cannot_post_redirect', false);
1048
		}
1049
		elseif ($user_info['is_guest'])
1050
		{
1051
			loadLanguage('Errors');
1052
			is_not_guest($txt['topic_gone']);
1053
		}
1054
		else
1055
			fatal_lang_error('topic_gone', false);
1056
	}
1057
1058
	if ($user_info['is_mod'])
1059
		$user_info['groups'][] = 3;
1060
}
1061
1062
/**
1063
 * Load this user's permissions.
1064
 */
1065
function loadPermissions()
1066
{
1067
	global $user_info, $board, $board_info, $modSettings, $smcFunc, $sourcedir;
1068
1069
	if ($user_info['is_admin'])
1070
	{
1071
		banPermissions();
1072
		return;
1073
	}
1074
1075
	if (!empty($modSettings['cache_enable']))
1076
	{
1077
		$cache_groups = $user_info['groups'];
1078
		asort($cache_groups);
1079
		$cache_groups = implode(',', $cache_groups);
1080
		// If it's a spider then cache it different.
1081
		if ($user_info['possibly_robot'])
1082
			$cache_groups .= '-spider';
1083
1084
		if ($modSettings['cache_enable'] >= 2 && !empty($board) && ($temp = cache_get_data('permissions:' . $cache_groups . ':' . $board, 240)) != null && time() - 240 > $modSettings['settings_updated'])
1085
		{
1086
			list ($user_info['permissions']) = $temp;
1087
			banPermissions();
1088
1089
			return;
1090
		}
1091
		elseif (($temp = cache_get_data('permissions:' . $cache_groups, 240)) != null && time() - 240 > $modSettings['settings_updated'])
1092
			list ($user_info['permissions'], $removals) = $temp;
1093
	}
1094
1095
	// If it is detected as a robot, and we are restricting permissions as a special group - then implement this.
1096
	$spider_restrict = $user_info['possibly_robot'] && !empty($modSettings['spider_group']) ? ' OR (id_group = {int:spider_group} AND add_deny = 0)' : '';
1097
1098
	if (empty($user_info['permissions']))
1099
	{
1100
		// Get the general permissions.
1101
		$request = $smcFunc['db_query']('', '
1102
			SELECT permission, add_deny
1103
			FROM {db_prefix}permissions
1104
			WHERE id_group IN ({array_int:member_groups})
1105
				' . $spider_restrict,
1106
			array(
1107
				'member_groups' => $user_info['groups'],
1108
				'spider_group' => !empty($modSettings['spider_group']) ? $modSettings['spider_group'] : 0,
1109
			)
1110
		);
1111
		$removals = array();
1112 View Code Duplication
		while ($row = $smcFunc['db_fetch_assoc']($request))
1113
		{
1114
			if (empty($row['add_deny']))
1115
				$removals[] = $row['permission'];
1116
			else
1117
				$user_info['permissions'][] = $row['permission'];
1118
		}
1119
		$smcFunc['db_free_result']($request);
1120
1121
		if (isset($cache_groups))
1122
			cache_put_data('permissions:' . $cache_groups, array($user_info['permissions'], $removals), 240);
1123
	}
1124
1125
	// Get the board permissions.
1126
	if (!empty($board))
1127
	{
1128
		// Make sure the board (if any) has been loaded by loadBoard().
1129
		if (!isset($board_info['profile']))
1130
			fatal_lang_error('no_board');
1131
1132
		$request = $smcFunc['db_query']('', '
1133
			SELECT permission, add_deny
1134
			FROM {db_prefix}board_permissions
1135
			WHERE (id_group IN ({array_int:member_groups})
1136
				' . $spider_restrict . ')
1137
				AND id_profile = {int:id_profile}',
1138
			array(
1139
				'member_groups' => $user_info['groups'],
1140
				'id_profile' => $board_info['profile'],
1141
				'spider_group' => !empty($modSettings['spider_group']) ? $modSettings['spider_group'] : 0,
1142
			)
1143
		);
1144 View Code Duplication
		while ($row = $smcFunc['db_fetch_assoc']($request))
1145
		{
1146
			if (empty($row['add_deny']))
1147
				$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...
1148
			else
1149
				$user_info['permissions'][] = $row['permission'];
1150
		}
1151
		$smcFunc['db_free_result']($request);
1152
	}
1153
1154
	// Remove all the permissions they shouldn't have ;).
1155
	if (!empty($modSettings['permission_enable_deny']))
1156
		$user_info['permissions'] = array_diff($user_info['permissions'], $removals);
1157
1158
	if (isset($cache_groups) && !empty($board) && $modSettings['cache_enable'] >= 2)
1159
		cache_put_data('permissions:' . $cache_groups . ':' . $board, array($user_info['permissions'], null), 240);
1160
1161
	// Banned?  Watch, don't touch..
1162
	banPermissions();
1163
1164
	// Load the mod cache so we can know what additional boards they should see, but no sense in doing it for guests
1165
	if (!$user_info['is_guest'])
1166
	{
1167
		if (!isset($_SESSION['mc']) || $_SESSION['mc']['time'] <= $modSettings['settings_updated'])
1168
		{
1169
			require_once($sourcedir . '/Subs-Auth.php');
1170
			rebuildModCache();
1171
		}
1172
		else
1173
			$user_info['mod_cache'] = $_SESSION['mc'];
1174
1175
		// This is a useful phantom permission added to the current user, and only the current user while they are logged in.
1176
		// For example this drastically simplifies certain changes to the profile area.
1177
		$user_info['permissions'][] = 'is_not_guest';
1178
		// And now some backwards compatibility stuff for mods and whatnot that aren't expecting the new permissions.
1179
		$user_info['permissions'][] = 'profile_view_own';
1180
		if (in_array('profile_view', $user_info['permissions']))
1181
			$user_info['permissions'][] = 'profile_view_any';
1182
	}
1183
}
1184
1185
/**
1186
 * Loads an array of users' data by ID or member_name.
1187
 *
1188
 * @param array|string $users An array of users by id or name or a single username/id
1189
 * @param bool $is_name Whether $users contains names
1190
 * @param string $set What kind of data to load (normal, profile, minimal)
1191
 * @return array The ids of the members loaded
1192
 */
1193
function loadMemberData($users, $is_name = false, $set = 'normal')
1194
{
1195
	global $user_profile, $modSettings, $board_info, $smcFunc, $context;
1196
	global $image_proxy_enabled, $image_proxy_secret, $boardurl;
1197
1198
	// Can't just look for no users :P.
1199
	if (empty($users))
1200
		return array();
1201
1202
	// Pass the set value
1203
	$context['loadMemberContext_set'] = $set;
1204
1205
	// Make sure it's an array.
1206
	$users = !is_array($users) ? array($users) : array_unique($users);
1207
	$loaded_ids = array();
1208
1209
	if (!$is_name && !empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= 3)
1210
	{
1211
		$users = array_values($users);
1212
		for ($i = 0, $n = count($users); $i < $n; $i++)
1213
		{
1214
			$data = cache_get_data('member_data-' . $set . '-' . $users[$i], 240);
1215
			if ($data == null)
1216
				continue;
1217
1218
			$loaded_ids[] = $data['id_member'];
1219
			$user_profile[$data['id_member']] = $data;
1220
			unset($users[$i]);
1221
		}
1222
	}
1223
1224
	// Used by default
1225
	$select_columns = '
1226
			COALESCE(lo.log_time, 0) AS is_online, COALESCE(a.id_attach, 0) AS id_attach, a.filename, a.attachment_type,
1227
			mem.signature, mem.personal_text, mem.avatar, mem.id_member, mem.member_name,
1228
			mem.real_name, mem.email_address, mem.date_registered, mem.website_title, mem.website_url,
1229
			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,
1230
			mg.online_color AS member_group_color, COALESCE(mg.group_name, {string:blank_string}) AS member_group,
1231
			pg.online_color AS post_group_color, COALESCE(pg.group_name, {string:blank_string}) AS post_group,
1232
			mem.is_activated, mem.warning, ' . (!empty($modSettings['titlesEnable']) ? 'mem.usertitle, ' : '') . '
1233
			CASE WHEN mem.id_group = 0 OR mg.icons = {string:blank_string} THEN pg.icons ELSE mg.icons END AS icons';
1234
	$select_tables = '
1235
			LEFT JOIN {db_prefix}log_online AS lo ON (lo.id_member = mem.id_member)
1236
			LEFT JOIN {db_prefix}attachments AS a ON (a.id_member = mem.id_member)
1237
			LEFT JOIN {db_prefix}membergroups AS pg ON (pg.id_group = mem.id_post_group)
1238
			LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = mem.id_group)';
1239
1240
	// We add or replace according the the set
1241
	switch ($set)
1242
	{
1243
		case 'normal':
1244
			$select_columns .= ', mem.buddy_list,  mem.additional_groups';
1245
			break;
1246
		case 'profile':
1247
			$select_columns .= ', mem.additional_groups, mem.id_theme, mem.pm_ignore_list, mem.pm_receive_from,
1248
			mem.time_format, mem.timezone, mem.secret_question, mem.smiley_set, mem.tfa_secret,
1249
			mem.total_time_logged_in, lo.url, mem.ignore_boards, mem.password_salt, mem.pm_prefs, mem.buddy_list, mem.alerts';
1250
			break;
1251
		case 'minimal':
1252
			$select_columns = '
1253
			mem.id_member, mem.member_name, mem.real_name, mem.email_address, mem.date_registered,
1254
			mem.posts, mem.last_login, mem.member_ip, mem.member_ip2, mem.lngfile, mem.id_group';
1255
			$select_tables = '';
1256
			break;
1257
		default:
1258
			trigger_error('loadMemberData(): Invalid member data set \'' . $set . '\'', E_USER_WARNING);
1259
	}
1260
1261
	// Allow mods to easily add to the selected member data
1262
	call_integration_hook('integrate_load_member_data', array(&$select_columns, &$select_tables, &$set));
1263
1264
	if (!empty($users))
1265
	{
1266
		// Load the member's data.
1267
		$request = $smcFunc['db_query']('', '
1268
			SELECT' . $select_columns . '
1269
			FROM {db_prefix}members AS mem' . $select_tables . '
1270
			WHERE mem.' . ($is_name ? 'member_name' : 'id_member') . ' IN ({' . ($is_name ? 'array_string' : 'array_int') . ':users})',
1271
			array(
1272
				'blank_string' => '',
1273
				'users' => $users,
1274
			)
1275
		);
1276
		$new_loaded_ids = array();
1277
		while ($row = $smcFunc['db_fetch_assoc']($request))
1278
		{
1279
			// If the image proxy is enabled, we still want the original URL when they're editing the profile...
1280
			$row['avatar_original'] = !empty($row['avatar']) ? $row['avatar'] : '';
1281
1282
			// Take care of proxying avatar if required, do this here for maximum reach
1283 View Code Duplication
			if ($image_proxy_enabled && !empty($row['avatar']) && stripos($row['avatar'], 'http://') !== false)
1284
				$row['avatar'] = $boardurl . '/proxy.php?request=' . urlencode($row['avatar']) . '&hash=' . md5($row['avatar'] . $image_proxy_secret);
1285
1286
			if (isset($row['member_ip']))
1287
				$row['member_ip'] = inet_dtop($row['member_ip']);
1288
			if (isset($row['member_ip2']))
1289
				$row['member_ip2'] = inet_dtop($row['member_ip2']);
1290
			$new_loaded_ids[] = $row['id_member'];
1291
			$loaded_ids[] = $row['id_member'];
1292
			$row['options'] = array();
1293
			$user_profile[$row['id_member']] = $row;
1294
		}
1295
		$smcFunc['db_free_result']($request);
1296
	}
1297
1298 View Code Duplication
	if (!empty($new_loaded_ids) && $set !== 'minimal')
1299
	{
1300
		$request = $smcFunc['db_query']('', '
1301
			SELECT id_member, variable, value
1302
			FROM {db_prefix}themes
1303
			WHERE id_member IN ({array_int:loaded_ids})',
1304
			array(
1305
				'loaded_ids' => $new_loaded_ids,
1306
			)
1307
		);
1308
		while ($row = $smcFunc['db_fetch_assoc']($request))
1309
			$user_profile[$row['id_member']]['options'][$row['variable']] = $row['value'];
1310
		$smcFunc['db_free_result']($request);
1311
	}
1312
1313
	$additional_mods = array();
1314
1315
	// Are any of these users in groups assigned to moderate this board?
1316
	if (!empty($loaded_ids) && !empty($board_info['moderator_groups']) && $set === 'normal')
1317
	{
1318
		foreach ($loaded_ids as $a_member)
1319
		{
1320
			if (!empty($user_profile[$a_member]['additional_groups']))
1321
				$groups = array_merge(array($user_profile[$a_member]['id_group']), explode(',', $user_profile[$a_member]['additional_groups']));
1322
			else
1323
				$groups = array($user_profile[$a_member]['id_group']);
1324
1325
			$temp = array_intersect($groups, array_keys($board_info['moderator_groups']));
1326
1327
			if (!empty($temp))
1328
			{
1329
				$additional_mods[] = $a_member;
1330
			}
1331
		}
1332
	}
1333
1334
	if (!empty($new_loaded_ids) && !empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= 3)
1335
	{
1336
		for ($i = 0, $n = count($new_loaded_ids); $i < $n; $i++)
1337
			cache_put_data('member_data-' . $set . '-' . $new_loaded_ids[$i], $user_profile[$new_loaded_ids[$i]], 240);
1338
	}
1339
1340
	// Are we loading any moderators?  If so, fix their group data...
1341
	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)
1342
	{
1343 View Code Duplication
		if (($row = cache_get_data('moderator_group_info', 480)) == null)
1344
		{
1345
			$request = $smcFunc['db_query']('', '
1346
				SELECT group_name AS member_group, online_color AS member_group_color, icons
1347
				FROM {db_prefix}membergroups
1348
				WHERE id_group = {int:moderator_group}
1349
				LIMIT 1',
1350
				array(
1351
					'moderator_group' => 3,
1352
				)
1353
			);
1354
			$row = $smcFunc['db_fetch_assoc']($request);
1355
			$smcFunc['db_free_result']($request);
1356
1357
			cache_put_data('moderator_group_info', $row, 480);
1358
		}
1359
1360
		foreach ($temp_mods as $id)
1361
		{
1362
			// By popular demand, don't show admins or global moderators as moderators.
1363
			if ($user_profile[$id]['id_group'] != 1 && $user_profile[$id]['id_group'] != 2)
1364
				$user_profile[$id]['member_group'] = $row['member_group'];
1365
1366
			// If the Moderator group has no color or icons, but their group does... don't overwrite.
1367
			if (!empty($row['icons']))
1368
				$user_profile[$id]['icons'] = $row['icons'];
1369
			if (!empty($row['member_group_color']))
1370
				$user_profile[$id]['member_group_color'] = $row['member_group_color'];
1371
		}
1372
	}
1373
1374
	return $loaded_ids;
1375
}
1376
1377
/**
1378
 * Loads the user's basic values... meant for template/theme usage.
1379
 *
1380
 * @param int $user The ID of a user previously loaded by {@link loadMemberData()}
1381
 * @param bool $display_custom_fields Whether or not to display custom profile fields
1382
 * @return boolean Whether or not the data was loaded successfully
1383
 */
1384
function loadMemberContext($user, $display_custom_fields = false)
1385
{
1386
	global $memberContext, $user_profile, $txt, $scripturl, $user_info;
1387
	global $context, $modSettings, $settings, $smcFunc;
1388
	static $dataLoaded = array();
1389
	static $loadedLanguages = array();
1390
1391
	// If this person's data is already loaded, skip it.
1392
	if (isset($dataLoaded[$user]))
1393
		return true;
1394
1395
	// We can't load guests or members not loaded by loadMemberData()!
1396
	if ($user == 0)
1397
		return false;
1398
	if (!isset($user_profile[$user]))
1399
	{
1400
		trigger_error('loadMemberContext(): member id ' . $user . ' not previously loaded by loadMemberData()', E_USER_WARNING);
1401
		return false;
1402
	}
1403
1404
	// Well, it's loaded now anyhow.
1405
	$dataLoaded[$user] = true;
1406
	$profile = $user_profile[$user];
1407
1408
	// Censor everything.
1409
	censorText($profile['signature']);
1410
	censorText($profile['personal_text']);
1411
1412
	// Set things up to be used before hand.
1413
	$profile['signature'] = str_replace(array("\n", "\r"), array('<br>', ''), $profile['signature']);
1414
	$profile['signature'] = parse_bbc($profile['signature'], true, 'sig' . $profile['id_member']);
1415
1416
	$profile['is_online'] = (!empty($profile['show_online']) || allowedTo('moderate_forum')) && $profile['is_online'] > 0;
1417
	$profile['icons'] = empty($profile['icons']) ? array('', '') : explode('#', $profile['icons']);
1418
	// Setup the buddy status here (One whole in_array call saved :P)
1419
	$profile['buddy'] = in_array($profile['id_member'], $user_info['buddies']);
1420
	$buddy_list = !empty($profile['buddy_list']) ? explode(',', $profile['buddy_list']) : array();
1421
1422
	//We need a little fallback for the membergroup icons. If it doesn't exist in the current theme, fallback to default theme
1423
	if (isset($profile['icons'][1]) && file_exists($settings['actual_theme_dir'] . '/images/membericons/' . $profile['icons'][1])) //icon is set and exists
1424
		$group_icon_url = $settings['images_url'] . '/membericons/' . $profile['icons'][1];
1425
	elseif (isset($profile['icons'][1])) //icon is set and doesn't exist, fallback to default
1426
		$group_icon_url = $settings['default_images_url'] . '/membericons/' . $profile['icons'][1];
1427
	else //not set, bye bye
1428
		$group_icon_url = '';
1429
1430
	// These minimal values are always loaded
1431
	$memberContext[$user] = array(
1432
		'username' => $profile['member_name'],
1433
		'name' => $profile['real_name'],
1434
		'id' => $profile['id_member'],
1435
		'href' => $scripturl . '?action=profile;u=' . $profile['id_member'],
1436
		'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>',
1437
		'email' => $profile['email_address'],
1438
		'show_email' => !$user_info['is_guest'] && ($user_info['id'] == $profile['id_member'] || allowedTo('moderate_forum')),
1439
		'registered' => empty($profile['date_registered']) ? $txt['not_applicable'] : timeformat($profile['date_registered']),
1440
		'registered_timestamp' => empty($profile['date_registered']) ? 0 : forum_time(true, $profile['date_registered']),
1441
	);
1442
1443
	// If the set isn't minimal then load the monstrous array.
1444
	if ($context['loadMemberContext_set'] != 'minimal')
1445
	{
1446
		// Go the extra mile and load the user's native language name.
1447
		if (empty($loadedLanguages))
1448
			$loadedLanguages = getLanguages();
1449
1450
		$memberContext[$user] += array(
1451
			'username_color' => '<span ' . (!empty($profile['member_group_color']) ? 'style="color:' . $profile['member_group_color'] . ';"' : '') . '>' . $profile['member_name'] . '</span>',
1452
			'name_color' => '<span ' . (!empty($profile['member_group_color']) ? 'style="color:' . $profile['member_group_color'] . ';"' : '') . '>' . $profile['real_name'] . '</span>',
1453
			'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>',
1454
			'is_buddy' => $profile['buddy'],
1455
			'is_reverse_buddy' => in_array($user_info['id'], $buddy_list),
1456
			'buddies' => $buddy_list,
1457
			'title' => !empty($modSettings['titlesEnable']) ? $profile['usertitle'] : '',
1458
			'blurb' => $profile['personal_text'],
1459
			'website' => array(
1460
				'title' => $profile['website_title'],
1461
				'url' => $profile['website_url'],
1462
			),
1463
			'birth_date' => empty($profile['birthdate']) ? '1004-01-01' : (substr($profile['birthdate'], 0, 4) === '0004' ? '1004' . substr($profile['birthdate'], 4) : $profile['birthdate']),
1464
			'signature' => $profile['signature'],
1465
			'real_posts' => $profile['posts'],
1466
			'posts' => $profile['posts'] > 500000 ? $txt['geek'] : comma_format($profile['posts']),
1467
			'last_login' => empty($profile['last_login']) ? $txt['never'] : timeformat($profile['last_login']),
1468
			'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...
1469
			'ip' => $smcFunc['htmlspecialchars']($profile['member_ip']),
1470
			'ip2' => $smcFunc['htmlspecialchars']($profile['member_ip2']),
1471
			'online' => array(
1472
				'is_online' => $profile['is_online'],
1473
				'text' => $smcFunc['htmlspecialchars']($txt[$profile['is_online'] ? 'online' : 'offline']),
1474
				'member_online_text' => sprintf($txt[$profile['is_online'] ? 'member_is_online' : 'member_is_offline'], $smcFunc['htmlspecialchars']($profile['real_name'])),
1475
				'href' => $scripturl . '?action=pm;sa=send;u=' . $profile['id_member'],
1476
				'link' => '<a href="' . $scripturl . '?action=pm;sa=send;u=' . $profile['id_member'] . '">' . $txt[$profile['is_online'] ? 'online' : 'offline'] . '</a>',
1477
				'label' => $txt[$profile['is_online'] ? 'online' : 'offline']
1478
			),
1479
			'language' => !empty($loadedLanguages[$profile['lngfile']]) && !empty($loadedLanguages[$profile['lngfile']]['name']) ? $loadedLanguages[$profile['lngfile']]['name'] : $smcFunc['ucwords'](strtr($profile['lngfile'], array('_' => ' ', '-utf8' => ''))),
1480
			'is_activated' => isset($profile['is_activated']) ? $profile['is_activated'] : 1,
1481
			'is_banned' => isset($profile['is_activated']) ? $profile['is_activated'] >= 10 : 0,
1482
			'options' => $profile['options'],
1483
			'is_guest' => false,
1484
			'group' => $profile['member_group'],
1485
			'group_color' => $profile['member_group_color'],
1486
			'group_id' => $profile['id_group'],
1487
			'post_group' => $profile['post_group'],
1488
			'post_group_color' => $profile['post_group_color'],
1489
			'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]),
1490
			'warning' => $profile['warning'],
1491
			'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' : (''))),
1492
			'local_time' => timeformat(time() + ($profile['time_offset'] - $user_info['time_offset']) * 3600, false),
1493
			'custom_fields' => array(),
1494
		);
1495
	}
1496
1497
	// If the set isn't minimal then load their avatar as well.
1498
	if ($context['loadMemberContext_set'] != 'minimal')
1499
	{
1500
		if (!empty($modSettings['gravatarOverride']) || (!empty($modSettings['gravatarEnabled']) && stristr($profile['avatar'], 'gravatar://')))
1501
		{
1502
			if (!empty($modSettings['gravatarAllowExtraEmail']) && stristr($profile['avatar'], 'gravatar://') && strlen($profile['avatar']) > 11)
1503
				$image = get_gravatar_url($smcFunc['substr']($profile['avatar'], 11));
1504
			else
1505
				$image = get_gravatar_url($profile['email_address']);
1506
		}
1507
		else
1508
		{
1509
			// So it's stored in the member table?
1510
			if (!empty($profile['avatar']))
1511
			{
1512
				$image = (stristr($profile['avatar'], 'http://') || stristr($profile['avatar'], 'https://')) ? $profile['avatar'] : $modSettings['avatar_url'] . '/' . $profile['avatar'];
1513
			}
1514
			elseif (!empty($profile['filename']))
1515
				$image = $modSettings['custom_avatar_url'] . '/' . $profile['filename'];
1516
			// Right... no avatar...use the default one
1517
			else
1518
				$image = $modSettings['avatar_url'] . '/default.png';
1519
		}
1520
		if (!empty($image))
1521
			$memberContext[$user]['avatar'] = array(
1522
				'name' => $profile['avatar'],
1523
				'image' => '<img class="avatar" src="' . $image . '" alt="avatar_' . $profile['member_name'] . '">',
1524
				'href' => $image,
1525
				'url' => $image,
1526
			);
1527
	}
1528
1529
	// Are we also loading the members custom fields into context?
1530
	if ($display_custom_fields && !empty($modSettings['displayFields']))
1531
	{
1532
		$memberContext[$user]['custom_fields'] = array();
1533
1534
		if (!isset($context['display_fields']))
1535
			$context['display_fields'] = $smcFunc['json_decode']($modSettings['displayFields'], true);
1536
1537
		foreach ($context['display_fields'] as $custom)
1538
		{
1539
			if (!isset($custom['col_name']) || trim($custom['col_name']) == '' || empty($profile['options'][$custom['col_name']]))
1540
				continue;
1541
1542
			$value = $profile['options'][$custom['col_name']];
1543
1544
			// Don't show the "disabled" option for the "gender" field.
1545
			if ($custom['col_name'] == 'cust_gender' && $value == 'Disabled')
1546
				continue;
1547
1548
			// BBC?
1549
			if ($custom['bbc'])
1550
				$value = parse_bbc($value);
1551
			// ... or checkbox?
0 ignored issues
show
Unused Code Comprehensibility introduced by
43% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
1552
			elseif (isset($custom['type']) && $custom['type'] == 'check')
1553
				$value = $value ? $txt['yes'] : $txt['no'];
1554
1555
			// Enclosing the user input within some other text?
1556 View Code Duplication
			if (!empty($custom['enclose']))
1557
				$value = strtr($custom['enclose'], array(
1558
					'{SCRIPTURL}' => $scripturl,
1559
					'{IMAGES_URL}' => $settings['images_url'],
1560
					'{DEFAULT_IMAGES_URL}' => $settings['default_images_url'],
1561
					'{INPUT}' => $value,
1562
				));
1563
1564
			$memberContext[$user]['custom_fields'][] = array(
1565
				'title' => !empty($custom['title']) ? $custom['title'] : $custom['col_name'],
1566
				'col_name' => $custom['col_name'],
1567
				'value' => un_htmlspecialchars($value),
1568
				'placement' => !empty($custom['placement']) ? $custom['placement'] : 0,
1569
			);
1570
		}
1571
	}
1572
1573
	call_integration_hook('integrate_member_context', array(&$memberContext[$user], $user, $display_custom_fields));
1574
	return true;
1575
}
1576
1577
/**
1578
 * Loads the user's custom profile fields
1579
 *
1580
 * @param integer|array $users A single user ID or an array of user IDs
1581
 * @param string|array $params Either a string or an array of strings with profile field names
1582
 * @return array|boolean An array of data about the fields and their values or false if nothing was loaded
1583
 */
1584
function loadMemberCustomFields($users, $params)
1585
{
1586
	global $smcFunc, $txt, $scripturl, $settings;
1587
1588
	// Do not waste my time...
1589
	if (empty($users) || empty($params))
1590
		return false;
1591
1592
	// Make sure it's an array.
1593
	$users = !is_array($users) ? array($users) : array_unique($users);
1594
	$params = !is_array($params) ? array($params) : array_unique($params);
1595
	$return = array();
1596
1597
	$request = $smcFunc['db_query']('', '
1598
		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,
1599
		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
1600
		FROM {db_prefix}themes AS t
1601
			LEFT JOIN {db_prefix}custom_fields AS c ON (c.col_name = t.variable)
1602
		WHERE id_member IN ({array_int:loaded_ids})
1603
			AND variable IN ({array_string:params})
1604
		ORDER BY field_order',
1605
		array(
1606
			'loaded_ids' => $users,
1607
			'params' => $params,
1608
		)
1609
	);
1610
1611
	while ($row = $smcFunc['db_fetch_assoc']($request))
1612
	{
1613
		// BBC?
1614
		if (!empty($row['bbc']))
1615
			$row['value'] = parse_bbc($row['value']);
1616
1617
		// ... or checkbox?
0 ignored issues
show
Unused Code Comprehensibility introduced by
43% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
1618
		elseif (isset($row['type']) && $row['type'] == 'check')
1619
			$row['value'] = !empty($row['value']) ? $txt['yes'] : $txt['no'];
1620
1621
		// Enclosing the user input within some other text?
1622
		if (!empty($row['enclose']))
1623
			$row['value'] = strtr($row['enclose'], array(
1624
				'{SCRIPTURL}' => $scripturl,
1625
				'{IMAGES_URL}' => $settings['images_url'],
1626
				'{DEFAULT_IMAGES_URL}' => $settings['default_images_url'],
1627
				'{INPUT}' => un_htmlspecialchars($row['value']),
1628
			));
1629
1630
		// Send a simple array if there is just 1 param
1631
		if (count($params) == 1)
1632
			$return[$row['id_member']] = $row;
1633
1634
		// More than 1? knock yourself out...
1635
		else
1636
		{
1637
			if (!isset($return[$row['id_member']]))
1638
				$return[$row['id_member']] = array();
1639
1640
			$return[$row['id_member']][$row['variable']] = $row;
1641
		}
1642
	}
1643
1644
	$smcFunc['db_free_result']($request);
1645
1646
	return !empty($return) ? $return : false;
1647
}
1648
1649
/**
1650
 * Loads information about what browser the user is viewing with and places it in $context
1651
 *  - uses the class from {@link Class-BrowserDetect.php}
1652
 */
1653
function detectBrowser()
1654
{
1655
	// Load the current user's browser of choice
1656
	$detector = new browser_detector;
1657
	$detector->detectBrowser();
1658
}
1659
1660
/**
1661
 * Are we using this browser?
1662
 *
1663
 * Wrapper function for detectBrowser
1664
 * @param string $browser The browser we are checking for.
1665
 * @return bool Whether or not the current browser is what we're looking for
1666
*/
1667
function isBrowser($browser)
1668
{
1669
	global $context;
1670
1671
	// Don't know any browser!
1672
	if (empty($context['browser']))
1673
		detectBrowser();
1674
1675
	return !empty($context['browser'][$browser]) || !empty($context['browser']['is_' . $browser]) ? true : false;
1676
}
1677
1678
/**
1679
 * Load a theme, by ID.
1680
 *
1681
 * @param int $id_theme The ID of the theme to load
1682
 * @param bool $initialize Whether or not to initialize a bunch of theme-related variables/settings
1683
 */
1684
function loadTheme($id_theme = 0, $initialize = true)
1685
{
1686
	global $user_info, $user_settings, $board_info, $boarddir, $maintenance;
1687
	global $txt, $boardurl, $scripturl, $mbname, $modSettings;
1688
	global $context, $settings, $options, $sourcedir, $ssi_theme, $smcFunc, $language, $board, $image_proxy_enabled;
1689
1690
	// The theme was specified by parameter.
1691
	if (!empty($id_theme))
1692
		$id_theme = (int) $id_theme;
1693
	// The theme was specified by REQUEST.
1694 View Code Duplication
	elseif (!empty($_REQUEST['theme']) && (!empty($modSettings['theme_allow']) || allowedTo('admin_forum')))
1695
	{
1696
		$id_theme = (int) $_REQUEST['theme'];
1697
		$_SESSION['id_theme'] = $id_theme;
1698
	}
1699
	// The theme was specified by REQUEST... previously.
1700 View Code Duplication
	elseif (!empty($_SESSION['id_theme']) && (!empty($modSettings['theme_allow']) || allowedTo('admin_forum')))
1701
		$id_theme = (int) $_SESSION['id_theme'];
1702
	// The theme is just the user's choice. (might use ?board=1;theme=0 to force board theme.)
1703
	elseif (!empty($user_info['theme']) && !isset($_REQUEST['theme']))
1704
		$id_theme = $user_info['theme'];
1705
	// The theme was specified by the board.
1706
	elseif (!empty($board_info['theme']))
1707
		$id_theme = $board_info['theme'];
1708
	// The theme is the forum's default.
1709
	else
1710
		$id_theme = $modSettings['theme_guests'];
1711
1712
	// Verify the id_theme... no foul play.
1713
	// Always allow the board specific theme, if they are overriding.
1714
	if (!empty($board_info['theme']) && $board_info['override_theme'])
1715
		$id_theme = $board_info['theme'];
1716
	// If they have specified a particular theme to use with SSI allow it to be used.
1717
	elseif (!empty($ssi_theme) && $id_theme == $ssi_theme)
1718
		$id_theme = (int) $id_theme;
1719
	elseif (!empty($modSettings['enableThemes']) && !allowedTo('admin_forum'))
1720
	{
1721
		$themes = explode(',', $modSettings['enableThemes']);
1722
		if (!in_array($id_theme, $themes))
1723
			$id_theme = $modSettings['theme_guests'];
1724
		else
1725
			$id_theme = (int) $id_theme;
1726
	}
1727
	else
1728
		$id_theme = (int) $id_theme;
1729
1730
	$member = empty($user_info['id']) ? -1 : $user_info['id'];
1731
1732
	// Disable image proxy if we don't have SSL enabled
1733
	if (empty($modSettings['force_ssl']) || $modSettings['force_ssl'] < 2)
1734
		$image_proxy_enabled = false;
1735
1736
	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'])
1737
	{
1738
		$themeData = $temp;
1739
		$flag = true;
1740
	}
1741
	elseif (($temp = cache_get_data('theme_settings-' . $id_theme, 90)) != null && time() - 60 > $modSettings['settings_updated'])
1742
		$themeData = $temp + array($member => array());
1743
	else
1744
		$themeData = array(-1 => array(), 0 => array(), $member => array());
1745
1746
	if (empty($flag))
1747
	{
1748
		// Load variables from the current or default theme, global or this user's.
1749
		$result = $smcFunc['db_query']('', '
1750
			SELECT variable, value, id_member, id_theme
1751
			FROM {db_prefix}themes
1752
			WHERE id_member' . (empty($themeData[0]) ? ' IN (-1, 0, {int:id_member})' : ' = {int:id_member}') . '
1753
				AND id_theme' . ($id_theme == 1 ? ' = {int:id_theme}' : ' IN ({int:id_theme}, 1)'),
1754
			array(
1755
				'id_theme' => $id_theme,
1756
				'id_member' => $member,
1757
			)
1758
		);
1759
		// Pick between $settings and $options depending on whose data it is.
1760
		while ($row = $smcFunc['db_fetch_assoc']($result))
1761
		{
1762
			// There are just things we shouldn't be able to change as members.
1763
			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')))
1764
				continue;
1765
1766
			// If this is the theme_dir of the default theme, store it.
1767
			if (in_array($row['variable'], array('theme_dir', 'theme_url', 'images_url')) && $row['id_theme'] == '1' && empty($row['id_member']))
1768
				$themeData[0]['default_' . $row['variable']] = $row['value'];
1769
1770
			// If this isn't set yet, is a theme option, or is not the default theme..
1771
			if (!isset($themeData[$row['id_member']][$row['variable']]) || $row['id_theme'] != '1')
1772
				$themeData[$row['id_member']][$row['variable']] = substr($row['variable'], 0, 5) == 'show_' ? $row['value'] == '1' : $row['value'];
1773
		}
1774
		$smcFunc['db_free_result']($result);
1775
1776
		if (!empty($themeData[-1]))
1777
			foreach ($themeData[-1] as $k => $v)
1778
			{
1779
				if (!isset($themeData[$member][$k]))
1780
					$themeData[$member][$k] = $v;
1781
			}
1782
1783
		if (!empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= 2)
1784
			cache_put_data('theme_settings-' . $id_theme . ':' . $member, $themeData, 60);
1785
		// Only if we didn't already load that part of the cache...
1786
		elseif (!isset($temp))
1787
			cache_put_data('theme_settings-' . $id_theme, array(-1 => $themeData[-1], 0 => $themeData[0]), 90);
1788
	}
1789
1790
	$settings = $themeData[0];
1791
	$options = $themeData[$member];
1792
1793
	$settings['theme_id'] = $id_theme;
1794
1795
	$settings['actual_theme_url'] = $settings['theme_url'];
1796
	$settings['actual_images_url'] = $settings['images_url'];
1797
	$settings['actual_theme_dir'] = $settings['theme_dir'];
1798
1799
	$settings['template_dirs'] = array();
1800
	// This theme first.
1801
	$settings['template_dirs'][] = $settings['theme_dir'];
1802
1803
	// Based on theme (if there is one).
1804
	if (!empty($settings['base_theme_dir']))
1805
		$settings['template_dirs'][] = $settings['base_theme_dir'];
1806
1807
	// Lastly the default theme.
1808 View Code Duplication
	if ($settings['theme_dir'] != $settings['default_theme_dir'])
1809
		$settings['template_dirs'][] = $settings['default_theme_dir'];
1810
1811
	if (!$initialize)
1812
		return;
1813
1814
	// Check to see if we're forcing SSL
1815
	if (!empty($modSettings['force_ssl']) && $modSettings['force_ssl'] == 2 && empty($maintenance) &&
1816
		(!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] == 'off') && SMF != 'SSI')
1817
		redirectexit(strtr($_SERVER['REQUEST_URL'], array('http://' => 'https://')));
1818
1819
	// Check to see if they're accessing it from the wrong place.
1820
	if (isset($_SERVER['HTTP_HOST']) || isset($_SERVER['SERVER_NAME']))
1821
	{
1822
		$detected_url = isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on' ? 'https://' : 'http://';
1823
		$detected_url .= empty($_SERVER['HTTP_HOST']) ? $_SERVER['SERVER_NAME'] . (empty($_SERVER['SERVER_PORT']) || $_SERVER['SERVER_PORT'] == '80' ? '' : ':' . $_SERVER['SERVER_PORT']) : $_SERVER['HTTP_HOST'];
1824
		$temp = preg_replace('~/' . basename($scripturl) . '(/.+)?$~', '', strtr(dirname($_SERVER['PHP_SELF']), '\\', '/'));
1825
		if ($temp != '/')
1826
			$detected_url .= $temp;
1827
	}
1828
	if (isset($detected_url) && $detected_url != $boardurl)
1829
	{
1830
		// Try #1 - check if it's in a list of alias addresses.
1831
		if (!empty($modSettings['forum_alias_urls']))
1832
		{
1833
			$aliases = explode(',', $modSettings['forum_alias_urls']);
1834
1835
			foreach ($aliases as $alias)
1836
			{
1837
				// Rip off all the boring parts, spaces, etc.
1838
				if ($detected_url == trim($alias) || strtr($detected_url, array('http://' => '', 'https://' => '')) == trim($alias))
1839
					$do_fix = true;
1840
			}
1841
		}
1842
1843
		// Hmm... check #2 - is it just different by a www?  Send them to the correct place!!
1844
		if (empty($do_fix) && strtr($detected_url, array('://' => '://www.')) == $boardurl && (empty($_GET) || count($_GET) == 1) && SMF != 'SSI')
1845
		{
1846
			// Okay, this seems weird, but we don't want an endless loop - this will make $_GET not empty ;).
1847
			if (empty($_GET))
1848
				redirectexit('wwwRedirect');
1849
			else
1850
			{
1851
				list ($k, $v) = each($_GET);
1852
1853
				if ($k != 'wwwRedirect')
1854
					redirectexit('wwwRedirect;' . $k . '=' . $v);
1855
			}
1856
		}
1857
1858
		// #3 is just a check for SSL...
1859
		if (strtr($detected_url, array('https://' => 'http://')) == $boardurl)
1860
			$do_fix = true;
1861
1862
		// Okay, #4 - perhaps it's an IP address?  We're gonna want to use that one, then. (assuming it's the IP or something...)
1863
		if (!empty($do_fix) || preg_match('~^http[s]?://(?:[\d\.:]+|\[[\d:]+\](?::\d+)?)(?:$|/)~', $detected_url) == 1)
1864
		{
1865
			// Caching is good ;).
1866
			$oldurl = $boardurl;
1867
1868
			// Fix $boardurl and $scripturl.
1869
			$boardurl = $detected_url;
1870
			$scripturl = strtr($scripturl, array($oldurl => $boardurl));
1871
			$_SERVER['REQUEST_URL'] = strtr($_SERVER['REQUEST_URL'], array($oldurl => $boardurl));
1872
1873
			// Fix the theme urls...
1874
			$settings['theme_url'] = strtr($settings['theme_url'], array($oldurl => $boardurl));
1875
			$settings['default_theme_url'] = strtr($settings['default_theme_url'], array($oldurl => $boardurl));
1876
			$settings['actual_theme_url'] = strtr($settings['actual_theme_url'], array($oldurl => $boardurl));
1877
			$settings['images_url'] = strtr($settings['images_url'], array($oldurl => $boardurl));
1878
			$settings['default_images_url'] = strtr($settings['default_images_url'], array($oldurl => $boardurl));
1879
			$settings['actual_images_url'] = strtr($settings['actual_images_url'], array($oldurl => $boardurl));
1880
1881
			// And just a few mod settings :).
1882
			$modSettings['smileys_url'] = strtr($modSettings['smileys_url'], array($oldurl => $boardurl));
1883
			$modSettings['avatar_url'] = strtr($modSettings['avatar_url'], array($oldurl => $boardurl));
1884
1885
			// Clean up after loadBoard().
1886
			if (isset($board_info['moderators']))
1887
			{
1888
				foreach ($board_info['moderators'] as $k => $dummy)
1889
				{
1890
					$board_info['moderators'][$k]['href'] = strtr($dummy['href'], array($oldurl => $boardurl));
1891
					$board_info['moderators'][$k]['link'] = strtr($dummy['link'], array('"' . $oldurl => '"' . $boardurl));
1892
				}
1893
			}
1894
			foreach ($context['linktree'] as $k => $dummy)
1895
				$context['linktree'][$k]['url'] = strtr($dummy['url'], array($oldurl => $boardurl));
1896
		}
1897
	}
1898
	// Set up the contextual user array.
1899
	if (!empty($user_info))
1900
	{
1901
		$context['user'] = array(
1902
			'id' => $user_info['id'],
1903
			'is_logged' => !$user_info['is_guest'],
1904
			'is_guest' => &$user_info['is_guest'],
1905
			'is_admin' => &$user_info['is_admin'],
1906
			'is_mod' => &$user_info['is_mod'],
1907
			// A user can mod if they have permission to see the mod center, or they are a board/group/approval moderator.
1908
			'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'])))),
1909
			'username' => $user_info['username'],
1910
			'language' => $user_info['language'],
1911
			'email' => $user_info['email'],
1912
			'ignoreusers' => $user_info['ignoreusers'],
1913
		);
1914
		if (!$context['user']['is_guest'])
1915
			$context['user']['name'] = $user_info['name'];
1916 View Code Duplication
		elseif ($context['user']['is_guest'] && !empty($txt['guest_title']))
1917
			$context['user']['name'] = $txt['guest_title'];
1918
1919
		// Determine the current smiley set.
1920
		$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'];
1921
		$context['user']['smiley_set'] = $user_info['smiley_set'];
1922
	}
1923
	else
1924
	{
1925
		$context['user'] = array(
1926
			'id' => -1,
1927
			'is_logged' => false,
1928
			'is_guest' => true,
1929
			'is_mod' => false,
1930
			'can_mod' => false,
1931
			'name' => $txt['guest_title'],
1932
			'language' => $language,
1933
			'email' => '',
1934
			'ignoreusers' => array(),
1935
		);
1936
	}
1937
1938
	// Some basic information...
1939
	if (!isset($context['html_headers']))
1940
		$context['html_headers'] = '';
1941
	if (!isset($context['javascript_files']))
1942
		$context['javascript_files'] = array();
1943
	if (!isset($context['css_files']))
1944
		$context['css_files'] = array();
1945
	if (!isset($context['css_header']))
1946
		$context['css_header'] = array();
1947
	if (!isset($context['javascript_inline']))
1948
		$context['javascript_inline'] = array('standard' => array(), 'defer' => array());
1949
	if (!isset($context['javascript_vars']))
1950
		$context['javascript_vars'] = array();
1951
1952
	$context['login_url'] = (!empty($modSettings['force_ssl']) && $modSettings['force_ssl'] < 2 ? strtr($scripturl, array('http://' => 'https://')) : $scripturl) . '?action=login2';
1953
	$context['menu_separator'] = !empty($settings['use_image_buttons']) ? ' ' : ' | ';
1954
	$context['session_var'] = $_SESSION['session_var'];
1955
	$context['session_id'] = $_SESSION['session_value'];
1956
	$context['forum_name'] = $mbname;
1957
	$context['forum_name_html_safe'] = $smcFunc['htmlspecialchars']($context['forum_name']);
1958
	$context['header_logo_url_html_safe'] = empty($settings['header_logo_url']) ? '' : $smcFunc['htmlspecialchars']($settings['header_logo_url']);
1959
	$context['current_action'] = isset($_REQUEST['action']) ? $smcFunc['htmlspecialchars']($_REQUEST['action']) : null;
1960
	$context['current_subaction'] = isset($_REQUEST['sa']) ? $_REQUEST['sa'] : null;
1961
	$context['can_register'] = empty($modSettings['registration_method']) || $modSettings['registration_method'] != 3;
1962
	if (isset($modSettings['load_average']))
1963
		$context['load_average'] = $modSettings['load_average'];
1964
1965
	// Detect the browser. This is separated out because it's also used in attachment downloads
1966
	detectBrowser();
1967
1968
	// Set the top level linktree up.
1969
	array_unshift($context['linktree'], array(
1970
		'url' => $scripturl,
1971
		'name' => $context['forum_name_html_safe']
1972
	));
1973
1974
	// This allows sticking some HTML on the page output - useful for controls.
1975
	$context['insert_after_template'] = '';
1976
1977
	if (!isset($txt))
1978
		$txt = array();
1979
1980
	$simpleActions = array(
1981
		'findmember',
1982
		'helpadmin',
1983
		'printpage',
1984
		'spellcheck',
1985
	);
1986
1987
	// Parent action => array of areas
1988
	$simpleAreas = array(
1989
		'profile' => array('popup', 'alerts_popup',),
1990
	);
1991
1992
	// Parent action => array of subactions
1993
	$simpleSubActions = array(
1994
		'pm' => array('popup',),
1995
		'signup' => array('usernamecheck'),
1996
	);
1997
1998
	// Extra params like ;preview ;js, etc.
1999
	$extraParams = array(
2000
		'preview',
2001
		'splitjs',
2002
	);
2003
2004
	// Actions that specifically uses XML output.
2005
	$xmlActions = array(
2006
		'quotefast',
2007
		'jsmodify',
2008
		'xmlhttp',
2009
		'post2',
2010
		'suggest',
2011
		'stats',
2012
		'notifytopic',
2013
		'notifyboard',
2014
	);
2015
2016
	call_integration_hook('integrate_simple_actions', array(&$simpleActions, &$simpleAreas, &$simpleSubActions, &$extraParams, &$xmlActions));
2017
2018
	$context['simple_action'] = in_array($context['current_action'], $simpleActions) ||
2019
	(isset($simpleAreas[$context['current_action']]) && isset($_REQUEST['area']) && in_array($_REQUEST['area'], $simpleAreas[$context['current_action']])) ||
2020
	(isset($simpleSubActions[$context['current_action']]) && in_array($context['current_subaction'], $simpleSubActions[$context['current_action']]));
2021
2022
	// See if theres any extra param to check.
2023
	$requiresXML = false;
2024
	foreach ($extraParams as $key => $extra)
2025
		if (isset($_REQUEST[$extra]))
2026
			$requiresXML = true;
2027
2028
	// Output is fully XML, so no need for the index template.
2029
	if (isset($_REQUEST['xml']) && (in_array($context['current_action'], $xmlActions) || $requiresXML))
2030
	{
2031
		loadLanguage('index+Modifications');
2032
		loadTemplate('Xml');
2033
		$context['template_layers'] = array();
2034
	}
2035
2036
	// These actions don't require the index template at all.
2037
	elseif (!empty($context['simple_action']))
2038
	{
2039
		loadLanguage('index+Modifications');
2040
		$context['template_layers'] = array();
2041
	}
2042
2043
	else
2044
	{
2045
		// Custom templates to load, or just default?
2046
		if (isset($settings['theme_templates']))
2047
			$templates = explode(',', $settings['theme_templates']);
2048
		else
2049
			$templates = array('index');
2050
2051
		// Load each template...
2052
		foreach ($templates as $template)
2053
			loadTemplate($template);
2054
2055
		// ...and attempt to load their associated language files.
2056
		$required_files = implode('+', array_merge($templates, array('Modifications')));
2057
		loadLanguage($required_files, '', false);
2058
2059
		// Custom template layers?
2060
		if (isset($settings['theme_layers']))
2061
			$context['template_layers'] = explode(',', $settings['theme_layers']);
2062
		else
2063
			$context['template_layers'] = array('html', 'body');
2064
	}
2065
2066
	// Initialize the theme.
2067
	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...
2068
2069
	// Allow overriding the board wide time/number formats.
2070
	if (empty($user_settings['time_format']) && !empty($txt['time_format']))
2071
		$user_info['time_format'] = $txt['time_format'];
2072
2073
	// Set the character set from the template.
2074
	$context['character_set'] = empty($modSettings['global_character_set']) ? $txt['lang_character_set'] : $modSettings['global_character_set'];
2075
	$context['utf8'] = $context['character_set'] === 'UTF-8';
2076
	$context['right_to_left'] = !empty($txt['lang_rtl']);
2077
2078
	// Guests may still need a name.
2079 View Code Duplication
	if ($context['user']['is_guest'] && empty($context['user']['name']))
2080
		$context['user']['name'] = $txt['guest_title'];
2081
2082
	// Any theme-related strings that need to be loaded?
2083
	if (!empty($settings['require_theme_strings']))
2084
		loadLanguage('ThemeStrings', '', false);
2085
2086
	// Make a special URL for the language.
2087
	$settings['lang_images_url'] = $settings['images_url'] . '/' . (!empty($txt['image_lang']) ? $txt['image_lang'] : $user_info['language']);
2088
2089
	// And of course, let's load the default CSS file.
2090
	loadCSSFile('index.css', array('minimize' => true), 'smf_index');
2091
2092
	// Here is my luvly Responsive CSS
2093
	loadCSSFile('responsive.css', array('force_current' => false, 'validate' => true, 'minimize' => true), 'smf_responsive');
2094
2095
	if ($context['right_to_left'])
2096
		loadCSSFile('rtl.css', array(), 'smf_rtl');
2097
2098
	// We allow theme variants, because we're cool.
2099
	$context['theme_variant'] = '';
2100
	$context['theme_variant_url'] = '';
2101
	if (!empty($settings['theme_variants']))
2102
	{
2103
		// Overriding - for previews and that ilk.
2104
		if (!empty($_REQUEST['variant']))
2105
			$_SESSION['id_variant'] = $_REQUEST['variant'];
2106
		// User selection?
2107
		if (empty($settings['disable_user_variant']) || allowedTo('admin_forum'))
2108
			$context['theme_variant'] = !empty($_SESSION['id_variant']) ? $_SESSION['id_variant'] : (!empty($options['theme_variant']) ? $options['theme_variant'] : '');
2109
		// If not a user variant, select the default.
2110
		if ($context['theme_variant'] == '' || !in_array($context['theme_variant'], $settings['theme_variants']))
2111
			$context['theme_variant'] = !empty($settings['default_variant']) && in_array($settings['default_variant'], $settings['theme_variants']) ? $settings['default_variant'] : $settings['theme_variants'][0];
2112
2113
		// Do this to keep things easier in the templates.
2114
		$context['theme_variant'] = '_' . $context['theme_variant'];
2115
		$context['theme_variant_url'] = $context['theme_variant'] . '/';
2116
2117
		if (!empty($context['theme_variant']))
2118
		{
2119
			loadCSSFile('index' . $context['theme_variant'] . '.css', array(), 'smf_index' . $context['theme_variant']);
2120
			if ($context['right_to_left'])
2121
				loadCSSFile('rtl' . $context['theme_variant'] . '.css', array(), 'smf_rtl' . $context['theme_variant']);
2122
		}
2123
	}
2124
2125
	// Let's be compatible with old themes!
2126
	if (!function_exists('template_html_above') && in_array('html', $context['template_layers']))
2127
		$context['template_layers'] = array('main');
2128
2129
	$context['tabindex'] = 1;
2130
2131
	// Compatibility.
2132
	if (!isset($settings['theme_version']))
2133
		$modSettings['memberCount'] = $modSettings['totalMembers'];
2134
2135
	// Default JS variables for use in every theme
2136
	$context['javascript_vars'] = array(
2137
		'smf_theme_url' => '"' . $settings['theme_url'] . '"',
2138
		'smf_default_theme_url' => '"' . $settings['default_theme_url'] . '"',
2139
		'smf_images_url' => '"' . $settings['images_url'] . '"',
2140
		'smf_smileys_url' => '"' . $modSettings['smileys_url'] . '"',
2141
		'smf_scripturl' => '"' . $scripturl . '"',
2142
		'smf_iso_case_folding' => $context['server']['iso_case_folding'] ? 'true' : 'false',
2143
		'smf_charset' => '"' . $context['character_set'] . '"',
2144
		'smf_session_id' => '"' . $context['session_id'] . '"',
2145
		'smf_session_var' => '"' . $context['session_var'] . '"',
2146
		'smf_member_id' => $context['user']['id'],
2147
		'ajax_notification_text' => JavaScriptEscape($txt['ajax_in_progress']),
2148
		'help_popup_heading_text' => JavaScriptEscape($txt['help_popup']),
2149
	);
2150
2151
	// Add the JQuery library to the list of files to load.
2152
	if (isset($modSettings['jquery_source']) && $modSettings['jquery_source'] == 'cdn')
2153
		loadJavaScriptFile('https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js', array('external' => true), 'smf_jquery');
2154
2155
	elseif (isset($modSettings['jquery_source']) && $modSettings['jquery_source'] == 'local')
2156
		loadJavaScriptFile('jquery-3.1.1.min.js', array('seed' => false), 'smf_jquery');
2157
2158
	elseif (isset($modSettings['jquery_source'], $modSettings['jquery_custom']) && $modSettings['jquery_source'] == 'custom')
2159
		loadJavaScriptFile($modSettings['jquery_custom'], array('external' => true), 'smf_jquery');
2160
2161
	// Auto loading? template_javascript() will take care of the local half of this.
2162
	else
2163
		loadJavaScriptFile('https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js', array('external' => true), 'smf_jquery');
2164
2165
	// Queue our JQuery plugins!
2166
	loadJavaScriptFile('smf_jquery_plugins.js', array('minimize' => true), 'smf_jquery_plugins');
2167
	if (!$user_info['is_guest'])
2168
	{
2169
		loadJavaScriptFile('jquery.custom-scrollbar.js', array(), 'smf_jquery_scrollbar');
2170
		loadCSSFile('jquery.custom-scrollbar.css', array('force_current' => false, 'validate' => true), 'smf_scrollbar');
2171
	}
2172
2173
	// script.js and theme.js, always required, so always add them! Makes index.template.php cleaner and all.
2174
	loadJavaScriptFile('script.js', array('defer' => false, 'minimize' => true), 'smf_script');
2175
	loadJavaScriptFile('theme.js', array('minimize' => true), 'smf_theme');
2176
2177
	// If we think we have mail to send, let's offer up some possibilities... robots get pain (Now with scheduled task support!)
2178
	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())
2179
	{
2180
		if (isBrowser('possibly_robot'))
2181
		{
2182
			// @todo Maybe move this somewhere better?!
2183
			require_once($sourcedir . '/ScheduledTasks.php');
2184
2185
			// What to do, what to do?!
2186
			if (empty($modSettings['next_task_time']) || $modSettings['next_task_time'] < time())
2187
				AutoTask();
2188
			else
2189
				ReduceMailQueue();
2190
		}
2191
		else
2192
		{
2193
			$type = empty($modSettings['next_task_time']) || $modSettings['next_task_time'] < time() ? 'task' : 'mailq';
2194
			$ts = $type == 'mailq' ? $modSettings['mail_next_send'] : $modSettings['next_task_time'];
2195
2196
			addInlineJavaScript('
2197
		function smfAutoTask()
2198
		{
2199
			$.get(smf_scripturl + "?scheduled=' . $type . ';ts=' . $ts . '");
2200
		}
2201
		window.setTimeout("smfAutoTask();", 1);');
2202
		}
2203
	}
2204
2205
	// And we should probably trigger the cron too.
2206
	if (empty($modSettings['cron_is_real_cron']))
2207
	{
2208
		$ts = time();
2209
		$ts -= $ts % 15;
2210
		addInlineJavaScript('
2211
	function triggerCron()
2212
	{
2213
		$.get(' . JavaScriptEscape($boardurl) . ' + "/cron.php?ts=' . $ts . '");
2214
	}
2215
	window.setTimeout(triggerCron, 1);', true);
2216
	}
2217
2218
	// Filter out the restricted boards from the linktree
2219
	if (!$user_info['is_admin'] && !empty($board))
2220
	{
2221
		foreach ($context['linktree'] as $k => $element)
2222
		{
2223
			if (!empty($element['groups']) &&
2224
				(count(array_intersect($user_info['groups'], $element['groups'])) == 0 ||
2225
				(!empty($modSettings['deny_boards_access']) && count(array_intersect($user_info['groups'], $element['deny_groups'])) != 0)))
2226
			{
2227
				$context['linktree'][$k]['name'] = $txt['restricted_board'];
2228
				$context['linktree'][$k]['extra_before'] = '<i>';
2229
				$context['linktree'][$k]['extra_after'] = '</i>';
2230
				unset($context['linktree'][$k]['url']);
2231
			}
2232
		}
2233
	}
2234
2235
	// Any files to include at this point?
2236 View Code Duplication
	if (!empty($modSettings['integrate_theme_include']))
2237
	{
2238
		$theme_includes = explode(',', $modSettings['integrate_theme_include']);
2239
		foreach ($theme_includes as $include)
2240
		{
2241
			$include = strtr(trim($include), array('$boarddir' => $boarddir, '$sourcedir' => $sourcedir, '$themedir' => $settings['theme_dir']));
2242
			if (file_exists($include))
2243
				require_once($include);
2244
		}
2245
	}
2246
2247
	// Call load theme integration functions.
2248
	call_integration_hook('integrate_load_theme');
2249
2250
	// We are ready to go.
2251
	$context['theme_loaded'] = true;
2252
}
2253
2254
/**
2255
 * Load a template - if the theme doesn't include it, use the default.
2256
 * What this function does:
2257
 *  - loads a template file with the name template_name from the current, default, or base theme.
2258
 *  - detects a wrong default theme directory and tries to work around it.
2259
 *
2260
 * @uses the template_include() function to include the file.
2261
 * @param string $template_name The name of the template to load
2262
 * @param array|string $style_sheets The name of a single stylesheet or an array of names of stylesheets to load
2263
 * @param bool $fatal If true, dies with an error message if the template cannot be found
2264
 * @return boolean Whether or not the template was loaded
2265
 */
2266
function loadTemplate($template_name, $style_sheets = array(), $fatal = true)
2267
{
2268
	global $context, $settings, $txt, $scripturl, $boarddir, $db_show_debug;
2269
2270
	// Do any style sheets first, cause we're easy with those.
2271
	if (!empty($style_sheets))
2272
	{
2273
		if (!is_array($style_sheets))
2274
			$style_sheets = array($style_sheets);
2275
2276
		foreach ($style_sheets as $sheet)
2277
			loadCSSFile($sheet . '.css', array(), $sheet);
2278
	}
2279
2280
	// No template to load?
2281
	if ($template_name === false)
2282
		return true;
2283
2284
	$loaded = false;
2285
	foreach ($settings['template_dirs'] as $template_dir)
2286
	{
2287
		if (file_exists($template_dir . '/' . $template_name . '.template.php'))
2288
		{
2289
			$loaded = true;
2290
			template_include($template_dir . '/' . $template_name . '.template.php', true);
2291
			break;
2292
		}
2293
	}
2294
2295
	if ($loaded)
2296
	{
2297
		if ($db_show_debug === true)
2298
			$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 2285. 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...
2299
2300
		// If they have specified an initialization function for this template, go ahead and call it now.
2301
		if (function_exists('template_' . $template_name . '_init'))
2302
			call_user_func('template_' . $template_name . '_init');
2303
	}
2304
	// Hmmm... doesn't exist?!  I don't suppose the directory is wrong, is it?
2305
	elseif (!file_exists($settings['default_theme_dir']) && file_exists($boarddir . '/Themes/default'))
2306
	{
2307
		$settings['default_theme_dir'] = $boarddir . '/Themes/default';
2308
		$settings['template_dirs'][] = $settings['default_theme_dir'];
2309
2310 View Code Duplication
		if (!empty($context['user']['is_admin']) && !isset($_GET['th']))
2311
		{
2312
			loadLanguage('Errors');
2313
			echo '
2314
<div class="alert errorbox">
2315
	<a href="', $scripturl . '?action=admin;area=theme;sa=list;' . $context['session_var'] . '=' . $context['session_id'], '" class="alert">', $txt['theme_dir_wrong'], '</a>
2316
</div>';
2317
		}
2318
2319
		loadTemplate($template_name);
2320
	}
2321
	// Cause an error otherwise.
2322
	elseif ($template_name != 'Errors' && $template_name != 'index' && $fatal)
2323
		fatal_lang_error('theme_template_error', 'template', array((string) $template_name));
2324
	elseif ($fatal)
2325
		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'));
2326
	else
2327
		return false;
2328
}
2329
2330
/**
2331
 * Load a sub-template.
2332
 * What it does:
2333
 * 	- loads the sub template specified by sub_template_name, which must be in an already-loaded template.
2334
 *  - if ?debug is in the query string, shows administrators a marker after every sub template
2335
 *	for debugging purposes.
2336
 *
2337
 * @todo get rid of reading $_REQUEST directly
2338
 *
2339
 * @param string $sub_template_name The name of the sub-template to load
2340
 * @param bool $fatal Whether to die with an error if the sub-template can't be loaded
2341
 */
2342
function loadSubTemplate($sub_template_name, $fatal = false)
2343
{
2344
	global $context, $txt, $db_show_debug;
2345
2346
	if ($db_show_debug === true)
2347
		$context['debug']['sub_templates'][] = $sub_template_name;
2348
2349
	// Figure out what the template function is named.
2350
	$theme_function = 'template_' . $sub_template_name;
2351
	if (function_exists($theme_function))
2352
		$theme_function();
2353
	elseif ($fatal === false)
2354
		fatal_lang_error('theme_template_error', 'template', array((string) $sub_template_name));
2355
	elseif ($fatal !== 'ignore')
2356
		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'));
2357
2358
	// Are we showing debugging for templates?  Just make sure not to do it before the doctype...
2359
	if (allowedTo('admin_forum') && isset($_REQUEST['debug']) && !in_array($sub_template_name, array('init', 'main_below')) && ob_get_length() > 0 && !isset($_REQUEST['xml']))
2360
	{
2361
		echo '
2362
<div class="warningbox">---- ', $sub_template_name, ' ends ----</div>';
2363
	}
2364
}
2365
2366
/**
2367
 * Add a CSS file for output later
2368
 *
2369
 * @param string $fileName The name of the file to load
2370
 * @param array $params An array of parameters
2371
 * Keys are the following:
2372
 * 	- ['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
2373
 * 	- ['default_theme'] (true/false): force use of default theme url
2374
 * 	- ['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
2375
 *  - ['validate'] (true/false): if true script will validate the local file exists
2376
 *  - ['rtl'] (string): additional file to load in RTL mode
2377
 *  - ['seed'] (true/false/string): if true or null, use cache stale, false do not, or used a supplied string
2378
 *  - ['minimize'] boolean to add your file to the main minimized file. Useful when you have a file thats loaded everywhere and for everyone.
2379
 * @param string $id An ID to stick on the end of the filename for caching purposes
2380
 */
2381
function loadCSSFile($fileName, $params = array(), $id = '')
2382
{
2383
	global $settings, $context, $modSettings;
2384
2385
	$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']) : '');
2386
	$params['force_current'] = isset($params['force_current']) ? $params['force_current'] : false;
2387
	$themeRef = !empty($params['default_theme']) ? 'default_theme' : 'theme';
2388
	$params['minimize'] = isset($params['minimize']) ? $params['minimize'] : false;
2389
	$params['external'] = isset($params['external']) ? $params['external'] : false;
2390
	$params['validate'] = isset($params['validate']) ? $params['validate'] : true;
2391
2392
	// If this is an external file, automatically set this to false.
2393
	if (!empty($params['external']))
2394
		$params['minimize'] = false;
2395
2396
	// Account for shorthand like admin.css?alp21 filenames
2397
	$has_seed = strpos($fileName, '.css?');
2398
	$id = empty($id) ? strtr(basename(str_replace('.css', '', $fileName)), '?', '_') : $id;
2399
2400
	// Is this a local file?
2401 View Code Duplication
	if (empty($params['external']))
2402
	{
2403
		// Are we validating the the file exists?
2404
		if (!empty($params['validate']) && !file_exists($settings[$themeRef . '_dir'] . '/css/' . $fileName))
2405
		{
2406
			// Maybe the default theme has it?
2407
			if ($themeRef === 'theme' && !$params['force_current'] && file_exists($settings['default_theme_dir'] . '/css/' . $fileName))
2408
			{
2409
				$fileUrl = $settings['default_theme_url'] . '/css/' . $fileName . ($has_seed ? '' : $params['seed']);
2410
				$filePath = $settings['default_theme_dir'] . '/css/' . $fileName . ($has_seed ? '' : $params['seed']);
2411
			}
2412
2413
			else
2414
				$fileUrl = false;
2415
		}
2416
2417
		else
2418
		{
2419
			$fileUrl = $settings[$themeRef . '_url'] . '/css/' . $fileName . ($has_seed ? '' : $params['seed']);
2420
			$filePath = $settings[$themeRef . '_dir'] . '/css/' . $fileName . ($has_seed ? '' : $params['seed']);
2421
		}
2422
	}
2423
2424
	// An external file doesn't have a filepath. Mock one for simplicity.
2425
	else
2426
	{
2427
		$fileUrl = $fileName;
2428
		$filePath = $fileName;
2429
	}
2430
2431
	// Add it to the array for use in the template
2432 View Code Duplication
	if (!empty($fileName))
2433
		$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...
2434
2435
	if (!empty($context['right_to_left']) && !empty($params['rtl']))
2436
		loadCSSFile($params['rtl'], array_diff_key($params, array('rtl' => 0)));
2437
}
2438
2439
/**
2440
 * Add a block of inline css code to be executed later
2441
 *
2442
 * - only use this if you have to, generally external css files are better, but for very small changes
2443
 *   or for scripts that require help from PHP/whatever, this can be useful.
2444
 * - all code added with this function is added to the same <style> tag so do make sure your css is valid!
2445
 *
2446
 * @param string $css Some css code
2447
 * @return void|bool Adds the CSS to the $context['css_header'] array or returns if no CSS is specified
2448
 */
2449
function addInlineCss($css)
2450
{
2451
	global $context;
2452
2453
	// Gotta add something...
2454
	if (empty($css))
2455
		return false;
2456
2457
	$context['css_header'][] = $css;
2458
}
2459
2460
/**
2461
 * Add a Javascript file for output later
2462
 *
2463
 * @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...
2464
 * @param array $params An array of parameter info
2465
 * Keys are the following:
2466
 * 	- ['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
2467
 * 	- ['default_theme'] (true/false): force use of default theme url
2468
 * 	- ['defer'] (true/false): define if the file should load in <head> or before the closing <html> tag
2469
 * 	- ['force_current'] (true/false): if this is false, we will attempt to load the file from the
2470
 *	default theme if not found in the current theme
2471
 *	- ['async'] (true/false): if the script should be loaded asynchronously (HTML5)
2472
 *  - ['validate'] (true/false): if true script will validate the local file exists
2473
 *  - ['seed'] (true/false/string): if true or null, use cache stale, false do not, or used a supplied string
2474
 *  - ['minimize'] boolean to add your file to the main minimized file. Useful when you have a file thats loaded everywhere and for everyone.
2475
 *
2476
 * @param string $id An ID to stick on the end of the filename
2477
 */
2478
function loadJavaScriptFile($fileName, $params = array(), $id = '')
2479
{
2480
	global $settings, $context, $modSettings;
2481
2482
	$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']) : '');
2483
	$params['force_current'] = isset($params['force_current']) ? $params['force_current'] : false;
2484
	$themeRef = !empty($params['default_theme']) ? 'default_theme' : 'theme';
2485
	$params['minimize'] = isset($params['minimize']) ? $params['minimize'] : false;
2486
	$params['external'] = isset($params['external']) ? $params['external'] : false;
2487
	$params['validate'] = isset($params['validate']) ? $params['validate'] : true;
2488
2489
	// If this is an external file, automatically set this to false.
2490
	if (!empty($params['external']))
2491
		$params['minimize'] = false;
2492
2493
	// Account for shorthand like admin.js?alp21 filenames
2494
	$has_seed = strpos($fileName, '.js?');
2495
	$id = empty($id) ? strtr(basename(str_replace('.js', '', $fileName)), '?', '_') : $id;
2496
2497
	// Is this a local file?
2498 View Code Duplication
	if (empty($params['external']))
2499
	{
2500
		// Are we validating it exists on disk?
2501
		if (!empty($params['validate']) && !file_exists($settings[$themeRef . '_dir'] . '/scripts/' . $fileName))
2502
		{
2503
			// Can't find it in this theme, how about the default?
2504
			if ($themeRef === 'theme' && !$params['force_current'] && file_exists($settings['default_theme_dir'] . '/scripts/' . $fileName))
2505
			{
2506
				$fileUrl = $settings['default_theme_url'] . '/scripts/' . $fileName . ($has_seed ? '' : $params['seed']);
2507
				$filePath = $settings['default_theme_dir'] . '/scripts/' . $fileName . ($has_seed ? '' : $params['seed']);
2508
			}
2509
2510
			else
2511
			{
2512
				$fileUrl = false;
2513
				$filePath = false;
2514
			}
2515
		}
2516
2517
		else
2518
		{
2519
			$fileUrl = $settings[$themeRef . '_url'] . '/scripts/' . $fileName . ($has_seed ? '' : $params['seed']);
2520
			$filePath = $settings[$themeRef . '_dir'] . '/scripts/' . $fileName . ($has_seed ? '' : $params['seed']);
2521
		}
2522
	}
2523
2524
	// An external file doesn't have a filepath. Mock one for simplicity.
2525
	else
2526
	{
2527
		$fileUrl = $fileName;
2528
		$filePath = $fileName;
2529
	}
2530
2531
	// Add it to the array for use in the template
2532 View Code Duplication
	if (!empty($fileName))
2533
		$context['javascript_files'][$id] = array('fileUrl' => $fileUrl, 'filePath' => $filePath, 'fileName' => $fileName, 'options' => $params);
2534
}
2535
2536
/**
2537
 * Add a Javascript variable for output later (for feeding text strings and similar to JS)
2538
 * Cleaner and easier (for modders) than to use the function below.
2539
 *
2540
 * @param string $key The key for this variable
2541
 * @param string $value The value
2542
 * @param bool $escape Whether or not to escape the value
2543
 */
2544
function addJavaScriptVar($key, $value, $escape = false)
2545
{
2546
	global $context;
2547
2548
	if (!empty($key) && (!empty($value) || $value === '0'))
2549
		$context['javascript_vars'][$key] = !empty($escape) ? JavaScriptEscape($value) : $value;
2550
}
2551
2552
/**
2553
 * Add a block of inline Javascript code to be executed later
2554
 *
2555
 * - only use this if you have to, generally external JS files are better, but for very small scripts
2556
 *   or for scripts that require help from PHP/whatever, this can be useful.
2557
 * - all code added with this function is added to the same <script> tag so do make sure your JS is clean!
2558
 *
2559
 * @param string $javascript Some JS code
2560
 * @param bool $defer Whether the script should load in <head> or before the closing <html> tag
2561
 * @return void|bool Adds the code to one of the $context['javascript_inline'] arrays or returns if no JS was specified
2562
 */
2563
function addInlineJavaScript($javascript, $defer = false)
2564
{
2565
	global $context;
2566
2567
	if (empty($javascript))
2568
		return false;
2569
2570
	$context['javascript_inline'][($defer === true ? 'defer' : 'standard')][] = $javascript;
2571
}
2572
2573
/**
2574
 * Load a language file.  Tries the current and default themes as well as the user and global languages.
2575
 *
2576
 * @param string $template_name The name of a template file
2577
 * @param string $lang A specific language to load this file from
2578
 * @param bool $fatal Whether to die with an error if it can't be loaded
2579
 * @param bool $force_reload Whether to load the file again if it's already loaded
2580
 * @return string The language actually loaded.
2581
 */
2582
function loadLanguage($template_name, $lang = '', $fatal = true, $force_reload = false)
2583
{
2584
	global $user_info, $language, $settings, $context, $modSettings;
2585
	global $db_show_debug, $sourcedir, $txt, $birthdayEmails, $txtBirthdayEmails;
2586
	static $already_loaded = array();
2587
2588
	// Default to the user's language.
2589
	if ($lang == '')
2590
		$lang = isset($user_info['language']) ? $user_info['language'] : $language;
2591
2592
	// Do we want the English version of language file as fallback?
2593
	if (empty($modSettings['disable_language_fallback']) && $lang != 'english')
2594
		loadLanguage($template_name, 'english', false);
2595
2596
	if (!$force_reload && isset($already_loaded[$template_name]) && $already_loaded[$template_name] == $lang)
2597
		return $lang;
2598
2599
	// Make sure we have $settings - if not we're in trouble and need to find it!
2600
	if (empty($settings['default_theme_dir']))
2601
	{
2602
		require_once($sourcedir . '/ScheduledTasks.php');
2603
		loadEssentialThemeData();
2604
	}
2605
2606
	// What theme are we in?
2607
	$theme_name = basename($settings['theme_url']);
2608
	if (empty($theme_name))
2609
		$theme_name = 'unknown';
2610
2611
	// For each file open it up and write it out!
2612
	foreach (explode('+', $template_name) as $template)
2613
	{
2614
		// Obviously, the current theme is most important to check.
2615
		$attempts = array(
2616
			array($settings['theme_dir'], $template, $lang, $settings['theme_url']),
2617
			array($settings['theme_dir'], $template, $language, $settings['theme_url']),
2618
		);
2619
2620
		// Do we have a base theme to worry about?
2621
		if (isset($settings['base_theme_dir']))
2622
		{
2623
			$attempts[] = array($settings['base_theme_dir'], $template, $lang, $settings['base_theme_url']);
2624
			$attempts[] = array($settings['base_theme_dir'], $template, $language, $settings['base_theme_url']);
2625
		}
2626
2627
		// Fall back on the default theme if necessary.
2628
		$attempts[] = array($settings['default_theme_dir'], $template, $lang, $settings['default_theme_url']);
2629
		$attempts[] = array($settings['default_theme_dir'], $template, $language, $settings['default_theme_url']);
2630
2631
		// Fall back on the English language if none of the preferred languages can be found.
2632
		if (!in_array('english', array($lang, $language)))
2633
		{
2634
			$attempts[] = array($settings['theme_dir'], $template, 'english', $settings['theme_url']);
2635
			$attempts[] = array($settings['default_theme_dir'], $template, 'english', $settings['default_theme_url']);
2636
		}
2637
2638
		// Try to find the language file.
2639
		$found = false;
2640
		foreach ($attempts as $k => $file)
2641
		{
2642
			if (file_exists($file[0] . '/languages/' . $file[1] . '.' . $file[2] . '.php'))
2643
			{
2644
				// Include it!
2645
				template_include($file[0] . '/languages/' . $file[1] . '.' . $file[2] . '.php');
2646
2647
				// Note that we found it.
2648
				$found = true;
2649
2650
				// setlocale is required for basename() & pathinfo() to work properly on the selected language
2651
				if (!empty($txt['lang_locale']) && !empty($modSettings['global_character_set']))
2652
					setlocale(LC_CTYPE, $txt['lang_locale'] . '.' . $modSettings['global_character_set']);
2653
2654
				break;
2655
			}
2656
		}
2657
2658
		// That couldn't be found!  Log the error, but *try* to continue normally.
2659
		if (!$found && $fatal)
2660
		{
2661
			log_error(sprintf($txt['theme_language_error'], $template_name . '.' . $lang, 'template'));
2662
			break;
2663
		}
2664
2665
		// For the sake of backward compatibility
2666
		if (!empty($txt['emails']))
2667
		{
2668
			foreach ($txt['emails'] as $key => $value)
2669
			{
2670
				$txt[$key . '_subject'] = $value['subject'];
2671
				$txt[$key . '_body'] = $value['body'];
2672
			}
2673
			$txt['emails'] = array();
2674
		}
2675
		// For sake of backward compatibility: $birthdayEmails is supposed to be
2676
		// empty in a normal install. If it isn't it means the forum is using
2677
		// something "old" (it may be the translation, it may be a mod) and this
2678
		// code (like the piece above) takes care of converting it to the new format
2679
		if (!empty($birthdayEmails))
2680
		{
2681
			foreach ($birthdayEmails as $key => $value)
2682
			{
2683
				$txtBirthdayEmails[$key . '_subject'] = $value['subject'];
2684
				$txtBirthdayEmails[$key . '_body'] = $value['body'];
2685
				$txtBirthdayEmails[$key . '_author'] = $value['author'];
2686
			}
2687
			$birthdayEmails = array();
2688
		}
2689
	}
2690
2691
	// Keep track of what we're up to soldier.
2692
	if ($db_show_debug === true)
2693
		$context['debug']['language_files'][] = $template_name . '.' . $lang . ' (' . $theme_name . ')';
2694
2695
	// Remember what we have loaded, and in which language.
2696
	$already_loaded[$template_name] = $lang;
2697
2698
	// Return the language actually loaded.
2699
	return $lang;
2700
}
2701
2702
/**
2703
 * Get all parent boards (requires first parent as parameter)
2704
 * It finds all the parents of id_parent, and that board itself.
2705
 * Additionally, it detects the moderators of said boards.
2706
 *
2707
 * @param int $id_parent The ID of the parent board
2708
 * @return array An array of information about the boards found.
2709
 */
2710
function getBoardParents($id_parent)
2711
{
2712
	global $scripturl, $smcFunc;
2713
2714
	// First check if we have this cached already.
2715
	if (($boards = cache_get_data('board_parents-' . $id_parent, 480)) === null)
2716
	{
2717
		$boards = array();
2718
		$original_parent = $id_parent;
2719
2720
		// Loop while the parent is non-zero.
2721
		while ($id_parent != 0)
2722
		{
2723
			$result = $smcFunc['db_query']('', '
2724
				SELECT
2725
					b.id_parent, b.name, {int:board_parent} AS id_board, b.member_groups, b.deny_member_groups,
2726
					b.child_level, COALESCE(mem.id_member, 0) AS id_moderator, mem.real_name,
2727
					COALESCE(mg.id_group, 0) AS id_moderator_group, mg.group_name
2728
				FROM {db_prefix}boards AS b
2729
					LEFT JOIN {db_prefix}moderators AS mods ON (mods.id_board = b.id_board)
2730
					LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = mods.id_member)
2731
					LEFT JOIN {db_prefix}moderator_groups AS modgs ON (modgs.id_board = b.id_board)
2732
					LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = modgs.id_group)
2733
				WHERE b.id_board = {int:board_parent}',
2734
				array(
2735
					'board_parent' => $id_parent,
2736
				)
2737
			);
2738
			// In the EXTREMELY unlikely event this happens, give an error message.
2739
			if ($smcFunc['db_num_rows']($result) == 0)
2740
				fatal_lang_error('parent_not_found', 'critical');
2741
			while ($row = $smcFunc['db_fetch_assoc']($result))
2742
			{
2743
				if (!isset($boards[$row['id_board']]))
2744
				{
2745
					$id_parent = $row['id_parent'];
2746
					$boards[$row['id_board']] = array(
2747
						'url' => $scripturl . '?board=' . $row['id_board'] . '.0',
2748
						'name' => $row['name'],
2749
						'level' => $row['child_level'],
2750
						'groups' => explode(',', $row['member_groups']),
2751
						'deny_groups' => explode(',', $row['deny_member_groups']),
2752
						'moderators' => array(),
2753
						'moderator_groups' => array()
2754
					);
2755
				}
2756
				// If a moderator exists for this board, add that moderator for all children too.
2757 View Code Duplication
				if (!empty($row['id_moderator']))
2758
					foreach ($boards as $id => $dummy)
2759
					{
2760
						$boards[$id]['moderators'][$row['id_moderator']] = array(
2761
							'id' => $row['id_moderator'],
2762
							'name' => $row['real_name'],
2763
							'href' => $scripturl . '?action=profile;u=' . $row['id_moderator'],
2764
							'link' => '<a href="' . $scripturl . '?action=profile;u=' . $row['id_moderator'] . '">' . $row['real_name'] . '</a>'
2765
						);
2766
					}
2767
2768
				// If a moderator group exists for this board, add that moderator group for all children too
2769 View Code Duplication
				if (!empty($row['id_moderator_group']))
2770
					foreach ($boards as $id => $dummy)
2771
					{
2772
						$boards[$id]['moderator_groups'][$row['id_moderator_group']] = array(
2773
							'id' => $row['id_moderator_group'],
2774
							'name' => $row['group_name'],
2775
							'href' => $scripturl . '?action=groups;sa=members;group=' . $row['id_moderator_group'],
2776
							'link' => '<a href="' . $scripturl . '?action=groups;sa=members;group=' . $row['id_moderator_group'] . '">' . $row['group_name'] . '</a>'
2777
						);
2778
					}
2779
			}
2780
			$smcFunc['db_free_result']($result);
2781
		}
2782
2783
		cache_put_data('board_parents-' . $original_parent, $boards, 480);
2784
	}
2785
2786
	return $boards;
2787
}
2788
2789
/**
2790
 * Attempt to reload our known languages.
2791
 * It will try to choose only utf8 or non-utf8 languages.
2792
 *
2793
 * @param bool $use_cache Whether or not to use the cache
2794
 * @return array An array of information about available languages
2795
 */
2796
function getLanguages($use_cache = true)
2797
{
2798
	global $context, $smcFunc, $settings, $modSettings;
2799
2800
	// Either we don't use the cache, or its expired.
2801
	if (!$use_cache || ($context['languages'] = cache_get_data('known_languages', !empty($modSettings['cache_enable']) && $modSettings['cache_enable'] < 1 ? 86400 : 3600)) == null)
2802
	{
2803
		// If we don't have our ucwords function defined yet, let's load the settings data.
2804
		if (empty($smcFunc['ucwords']))
2805
			reloadSettings();
2806
2807
		// If we don't have our theme information yet, let's get it.
2808
		if (empty($settings['default_theme_dir']))
2809
			loadTheme(0, false);
2810
2811
		// Default language directories to try.
2812
		$language_directories = array(
2813
			$settings['default_theme_dir'] . '/languages',
2814
		);
2815
		if (!empty($settings['actual_theme_dir']) && $settings['actual_theme_dir'] != $settings['default_theme_dir'])
2816
			$language_directories[] = $settings['actual_theme_dir'] . '/languages';
2817
2818
		// We possibly have a base theme directory.
2819
		if (!empty($settings['base_theme_dir']))
2820
			$language_directories[] = $settings['base_theme_dir'] . '/languages';
2821
2822
		// Remove any duplicates.
2823
		$language_directories = array_unique($language_directories);
2824
2825
		// Get a list of languages.
2826
		$langList = !empty($modSettings['langList']) ? $smcFunc['json_decode']($modSettings['langList'], true) : array();
2827
		$langList = is_array($langList) ? $langList : false;
2828
2829
		$catchLang = array();
2830
2831
		foreach ($language_directories as $language_dir)
2832
		{
2833
			// Can't look in here... doesn't exist!
2834
			if (!file_exists($language_dir))
2835
				continue;
2836
2837
			$dir = dir($language_dir);
2838
			while ($entry = $dir->read())
2839
			{
2840
				// Look for the index language file... For good measure skip any "index.language-utf8.php" files
2841
				if (!preg_match('~^index\.(.+[^-utf8])\.php$~', $entry, $matches))
2842
					continue;
2843
2844
				if (!empty($langList) && !empty($langList[$matches[1]]))
2845
					$langName = $langList[$matches[1]];
2846
2847
				else
2848
				{
2849
					$langName = $smcFunc['ucwords'](strtr($matches[1], array('_' => ' ')));
2850
2851
					// Get the line we need.
2852
					$fp = @fopen($language_dir . '/' . $entry);
2853
2854
					// Yay!
2855
					if ($fp)
2856
					{
2857
						while (($line = fgets($fp)) !== false)
2858
						{
2859
							preg_match('~\$txt\[\'native_name\'\] = \'(.+)\'\;~', $line, $matchNative);
2860
2861
							// Set the language's name.
2862
							if (!empty($matchNative) && !empty($matchNative[1]))
2863
							{
2864
								$langName = un_htmlspecialchars($matchNative[1]);
2865
								break;
2866
							}
2867
						}
2868
2869
						fclose($fp);
2870
					}
2871
2872
					// Catch the language name.
2873
					$catchLang[$matches[1]] = $langName;
2874
				}
2875
2876
				// Build this language entry.
2877
				$context['languages'][$matches[1]] = array(
2878
					'name' => $langName,
2879
					'selected' => false,
2880
					'filename' => $matches[1],
2881
					'location' => $language_dir . '/index.' . $matches[1] . '.php',
2882
				);
2883
			}
2884
			$dir->close();
2885
		}
2886
2887
		// Do we need to store the lang list?
2888
		if (empty($langList))
2889
			updateSettings(array('langList' => $smcFunc['json_encode']($catchLang)));
2890
2891
		// Let's cash in on this deal.
2892 View Code Duplication
		if (!empty($modSettings['cache_enable']))
2893
			cache_put_data('known_languages', $context['languages'], !empty($modSettings['cache_enable']) && $modSettings['cache_enable'] < 1 ? 86400 : 3600);
2894
	}
2895
2896
	return $context['languages'];
2897
}
2898
2899
/**
2900
 * Replace all vulgar words with respective proper words. (substring or whole words..)
2901
 * What this function does:
2902
 *  - it censors the passed string.
2903
 *  - if the theme setting allow_no_censored is on, and the theme option
2904
 *	show_no_censored is enabled, does not censor, unless force is also set.
2905
 *  - it caches the list of censored words to reduce parsing.
2906
 *
2907
 * @param string &$text The text to censor
2908
 * @param bool $force Whether to censor the text regardless of settings
2909
 * @return string The censored text
2910
 */
2911
function censorText(&$text, $force = false)
2912
{
2913
	global $modSettings, $options, $txt;
2914
	static $censor_vulgar = null, $censor_proper;
2915
2916
	if ((!empty($options['show_no_censored']) && !empty($modSettings['allow_no_censored']) && !$force) || empty($modSettings['censor_vulgar']) || trim($text) === '')
2917
		return $text;
2918
2919
	// If they haven't yet been loaded, load them.
2920
	if ($censor_vulgar == null)
2921
	{
2922
		$censor_vulgar = explode("\n", $modSettings['censor_vulgar']);
2923
		$censor_proper = explode("\n", $modSettings['censor_proper']);
2924
2925
		// Quote them for use in regular expressions.
2926
		if (!empty($modSettings['censorWholeWord']))
2927
		{
2928
			for ($i = 0, $n = count($censor_vulgar); $i < $n; $i++)
2929
			{
2930
				$censor_vulgar[$i] = str_replace(array('\\\\\\*', '\\*', '&', '\''), array('[*]', '[^\s]*?', '&amp;', '&#039;'), preg_quote($censor_vulgar[$i], '/'));
2931
				$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' : '');
2932
2933
				// @todo I'm thinking the old way is some kind of bug and this is actually fixing it.
2934
				//if (strpos($censor_vulgar[$i], '\'') !== false)
2935
					//$censor_vulgar[$i] = str_replace('\'', '&#039;', $censor_vulgar[$i]);
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
2936
			}
2937
		}
2938
	}
2939
2940
	// Censoring isn't so very complicated :P.
2941
	if (empty($modSettings['censorWholeWord']))
2942
	{
2943
		$func = !empty($modSettings['censorIgnoreCase']) ? 'str_ireplace' : 'str_replace';
2944
		$text = $func($censor_vulgar, $censor_proper, $text);
2945
	}
2946
	else
2947
		$text = preg_replace($censor_vulgar, $censor_proper, $text);
2948
2949
	return $text;
2950
}
2951
2952
/**
2953
 * Load the template/language file using eval or require? (with eval we can show an error message!)
2954
 * 	- loads the template or language file specified by filename.
2955
 * 	- uses eval unless disableTemplateEval is enabled.
2956
 * 	- outputs a parse error if the file did not exist or contained errors.
2957
 * 	- attempts to detect the error and line, and show detailed information.
2958
 *
2959
 * @param string $filename The name of the file to include
2960
 * @param bool $once If true only includes the file once (like include_once)
2961
 */
2962
function template_include($filename, $once = false)
2963
{
2964
	global $context, $settings, $txt, $scripturl, $modSettings;
2965
	global $boardurl, $boarddir, $sourcedir;
2966
	global $maintenance, $mtitle, $mmessage;
2967
	static $templates = array();
2968
2969
	// We want to be able to figure out any errors...
2970
	@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...
2971
2972
	// Don't include the file more than once, if $once is true.
2973
	if ($once && in_array($filename, $templates))
2974
		return;
2975
	// Add this file to the include list, whether $once is true or not.
2976
	else
2977
		$templates[] = $filename;
2978
2979
	// Are we going to use eval?
2980
	if (empty($modSettings['disableTemplateEval']))
2981
	{
2982
		$file_found = file_exists($filename) && eval('?' . '>' . rtrim(file_get_contents($filename))) !== false;
0 ignored issues
show
Coding Style introduced by
The function template_include() contains an eval expression.

On one hand, eval might be exploited by malicious users if they somehow manage to inject dynamic content. On the other hand, with the emergence of faster PHP runtimes like the HHVM, eval prevents some optimization that they perform.

Loading history...
2983
		$settings['current_include_filename'] = $filename;
2984
	}
2985
	else
2986
	{
2987
		$file_found = file_exists($filename);
2988
2989
		if ($once && $file_found)
2990
			require_once($filename);
2991
		elseif ($file_found)
2992
			require($filename);
2993
	}
2994
2995
	if ($file_found !== true)
2996
	{
2997
		ob_end_clean();
2998
		if (!empty($modSettings['enableCompressedOutput']))
2999
			@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...
3000
		else
3001
			ob_start();
3002
3003 View Code Duplication
		if (isset($_GET['debug']))
3004
			header('Content-Type: application/xhtml+xml; charset=' . (empty($context['character_set']) ? 'ISO-8859-1' : $context['character_set']));
3005
3006
		// Don't cache error pages!!
3007
		header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
3008
		header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
3009
		header('Cache-Control: no-cache');
3010
3011
		if (!isset($txt['template_parse_error']))
3012
		{
3013
			$txt['template_parse_error'] = 'Template Parse Error!';
3014
			$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>.';
3015
			$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>.';
3016
			$txt['template_parse_errmsg'] = 'Unfortunately more information is not available at this time as to exactly what is wrong.';
3017
		}
3018
3019
		// First, let's get the doctype and language information out of the way.
3020
		echo '<!DOCTYPE html>
3021
<html', !empty($context['right_to_left']) ? ' dir="rtl"' : '', '>
3022
	<head>';
3023
		if (isset($context['character_set']))
3024
			echo '
3025
		<meta charset="', $context['character_set'], '">';
3026
3027
		if (!empty($maintenance) && !allowedTo('admin_forum'))
3028
			echo '
3029
		<title>', $mtitle, '</title>
3030
	</head>
3031
	<body>
3032
		<h3>', $mtitle, '</h3>
3033
		', $mmessage, '
3034
	</body>
3035
</html>';
3036
		elseif (!allowedTo('admin_forum'))
3037
			echo '
3038
		<title>', $txt['template_parse_error'], '</title>
3039
	</head>
3040
	<body>
3041
		<h3>', $txt['template_parse_error'], '</h3>
3042
		', $txt['template_parse_error_message'], '
3043
	</body>
3044
</html>';
3045
		else
3046
		{
3047
			require_once($sourcedir . '/Subs-Package.php');
3048
3049
			$error = fetch_web_data($boardurl . strtr($filename, array($boarddir => '', strtr($boarddir, '\\', '/') => '')));
3050
			$error_array = error_get_last();
3051
			if (empty($error) && ini_get('track_errors') && !empty($error_array))
3052
				$error = $error_array['message'];
3053
			if (empty($error))
3054
				$error = $txt['template_parse_errmsg'];
3055
3056
			$error = strtr($error, array('<b>' => '<strong>', '</b>' => '</strong>'));
3057
3058
			echo '
3059
		<title>', $txt['template_parse_error'], '</title>
3060
	</head>
3061
	<body>
3062
		<h3>', $txt['template_parse_error'], '</h3>
3063
		', sprintf($txt['template_parse_error_details'], strtr($filename, array($boarddir => '', strtr($boarddir, '\\', '/') => '')));
3064
3065
			if (!empty($error))
3066
				echo '
3067
		<hr>
3068
3069
		<div style="margin: 0 20px;"><pre>', strtr(strtr($error, array('<strong>' . $boarddir => '<strong>...', '<strong>' . strtr($boarddir, '\\', '/') => '<strong>...')), '\\', '/'), '</pre></div>';
3070
3071
			// I know, I know... this is VERY COMPLICATED.  Still, it's good.
3072
			if (preg_match('~ <strong>(\d+)</strong><br( /)?' . '>$~i', $error, $match) != 0)
3073
			{
3074
				$data = file($filename);
3075
				$data2 = highlight_php_code(implode('', $data));
3076
				$data2 = preg_split('~\<br( /)?\>~', $data2);
3077
3078
				// Fix the PHP code stuff...
3079
				if (!isBrowser('gecko'))
3080
					$data2 = str_replace("\t", '<span style="white-space: pre;">' . "\t" . '</span>', $data2);
3081
				else
3082
					$data2 = str_replace('<pre style="display: inline;">' . "\t" . '</pre>', "\t", $data2);
3083
3084
				// Now we get to work around a bug in PHP where it doesn't escape <br>s!
3085
				$j = -1;
3086
				foreach ($data as $line)
3087
				{
3088
					$j++;
3089
3090
					if (substr_count($line, '<br>') == 0)
3091
						continue;
3092
3093
					$n = substr_count($line, '<br>');
3094
					for ($i = 0; $i < $n; $i++)
3095
					{
3096
						$data2[$j] .= '&lt;br /&gt;' . $data2[$j + $i + 1];
3097
						unset($data2[$j + $i + 1]);
3098
					}
3099
					$j += $n;
3100
				}
3101
				$data2 = array_values($data2);
3102
				array_unshift($data2, '');
3103
3104
				echo '
3105
		<div style="margin: 2ex 20px; width: 96%; overflow: auto;"><pre style="margin: 0;">';
3106
3107
				// Figure out what the color coding was before...
3108
				$line = max($match[1] - 9, 1);
3109
				$last_line = '';
3110
				for ($line2 = $line - 1; $line2 > 1; $line2--)
3111
					if (strpos($data2[$line2], '<') !== false)
3112
					{
3113
						if (preg_match('~(<[^/>]+>)[^<]*$~', $data2[$line2], $color_match) != 0)
3114
							$last_line = $color_match[1];
3115
						break;
3116
					}
3117
3118
				// Show the relevant lines...
3119
				for ($n = min($match[1] + 4, count($data2) + 1); $line <= $n; $line++)
3120
				{
3121
					if ($line == $match[1])
3122
						echo '</pre><div style="background-color: #ffb0b5;"><pre style="margin: 0;">';
3123
3124
					echo '<span style="color: black;">', sprintf('%' . strlen($n) . 's', $line), ':</span> ';
3125
					if (isset($data2[$line]) && $data2[$line] != '')
3126
						echo substr($data2[$line], 0, 2) == '</' ? preg_replace('~^</[^>]+>~', '', $data2[$line]) : $last_line . $data2[$line];
3127
3128
					if (isset($data2[$line]) && preg_match('~(<[^/>]+>)[^<]*$~', $data2[$line], $color_match) != 0)
3129
					{
3130
						$last_line = $color_match[1];
3131
						echo '</', substr($last_line, 1, 4), '>';
3132
					}
3133
					elseif ($last_line != '' && strpos($data2[$line], '<') !== false)
3134
						$last_line = '';
3135
					elseif ($last_line != '' && $data2[$line] != '')
3136
						echo '</', substr($last_line, 1, 4), '>';
3137
3138
					if ($line == $match[1])
3139
						echo '</pre></div><pre style="margin: 0;">';
3140
					else
3141
						echo "\n";
3142
				}
3143
3144
				echo '</pre></div>';
3145
			}
3146
3147
			echo '
3148
	</body>
3149
</html>';
3150
		}
3151
3152
		die;
3153
	}
3154
}
3155
3156
/**
3157
 * Initialize a database connection.
3158
 */
3159
function loadDatabase()
3160
{
3161
	global $db_persist, $db_connection, $db_server, $db_user, $db_passwd;
3162
	global $db_type, $db_name, $ssi_db_user, $ssi_db_passwd, $sourcedir, $db_prefix, $db_port;
3163
3164
	// Figure out what type of database we are using.
3165
	if (empty($db_type) || !file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php'))
3166
		$db_type = 'mysql';
3167
3168
	// Load the file for the database.
3169
	require_once($sourcedir . '/Subs-Db-' . $db_type . '.php');
3170
3171
	$db_options = array();
3172
3173
	// Add in the port if needed
3174
	if (!empty($db_port))
3175
		$db_options['port'] = $db_port;
3176
3177
	// 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.
3178
	if (SMF == 'SSI' && !empty($ssi_db_user) && !empty($ssi_db_passwd))
3179
	{
3180
		$options = array_merge($db_options, array('persist' => $db_persist, 'non_fatal' => true, 'dont_select_db' => true));
3181
3182
		$db_connection = smf_db_initiate($db_server, $db_name, $ssi_db_user, $ssi_db_passwd, $db_prefix, $options);
3183
	}
3184
3185
	// Either we aren't in SSI mode, or it failed.
3186
	if (empty($db_connection))
3187
	{
3188
		$options = array_merge($db_options, array('persist' => $db_persist, 'dont_select_db' => SMF == 'SSI'));
3189
3190
		$db_connection = smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, $db_prefix, $options);
3191
	}
3192
3193
	// Safe guard here, if there isn't a valid connection lets put a stop to it.
3194
	if (!$db_connection)
3195
		display_db_error();
3196
3197
	// If in SSI mode fix up the prefix.
3198
	if (SMF == 'SSI')
3199
		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...
3200
}
3201
3202
/**
3203
 * Try to load up a supported caching method. This is saved in $cacheAPI if we are not overriding it.
3204
 *
3205
 * @param string $overrideCache Try to use a different cache method other than that defined in $cache_accelerator.
3206
 * @param string $fallbackSMF Use the default SMF method if the accelerator fails.
3207
 * @return object A object of $cacheAPI.
3208
*/
3209
function loadCacheAccelerator($overrideCache = null, $fallbackSMF = true)
3210
{
3211
	global $sourcedir, $cacheAPI, $cache_accelerator;
3212
3213
	// Not overriding this and we have a cacheAPI, send it back.
3214
	if (empty($overrideCache) && is_object($cacheAPI))
3215
		return $cacheAPI;
3216
	elseif (is_null($cacheAPI))
3217
		$cacheAPI = false;
3218
3219
	// Make sure our class is in session.
3220
	require_once($sourcedir . '/Class-CacheAPI.php');
3221
3222
	// What accelerator we are going to try.
3223
	$tryAccelerator = !empty($overrideCache) ? $overrideCache : !empty($cache_accelerator) ? $cache_accelerator : 'smf';
3224
	$tryAccelerator = strtolower($tryAccelerator);
3225
3226
	// Do some basic tests.
3227
	if (file_exists($sourcedir . '/CacheAPI-' . $tryAccelerator . '.php'))
3228
	{
3229
		require_once($sourcedir . '/CacheAPI-' . $tryAccelerator . '.php');
3230
3231
		$cache_class_name = $tryAccelerator . '_cache';
3232
		$testAPI = new $cache_class_name();
3233
3234
		// No Support?  NEXT!
3235
		if (!$testAPI->isSupported())
3236
		{
3237
			// Can we save ourselves?
3238
			if (!empty($fallbackSMF) && is_null($overrideCache) && $tryAccelerator != 'smf')
3239
				return loadCacheAccelerator(null, false);
3240
			return false;
3241
		}
3242
3243
		// Connect up to the accelerator.
3244
		$testAPI->connect();
3245
3246
		// Don't set this if we are overriding the cache.
3247
		if (is_null($overrideCache))
3248
		{
3249
			$cacheAPI = $testAPI;
3250
			return $cacheAPI;
3251
		}
3252
		else
3253
			return $testAPI;
3254
	}
3255
}
3256
3257
/**
3258
 * Try to retrieve a cache entry. On failure, call the appropriate function.
3259
 *
3260
 * @param string $key The key for this entry
3261
 * @param string $file The file associated with this entry
3262
 * @param string $function The function to call
3263
 * @param array $params Parameters to be passed to the specified function
3264
 * @param int $level The cache level
3265
 * @return string The cached data
3266
 */
3267
function cache_quick_get($key, $file, $function, $params, $level = 1)
3268
{
3269
	global $modSettings, $sourcedir;
3270
3271
	// @todo Why are we doing this if caching is disabled?
3272
3273
	if (function_exists('call_integration_hook'))
3274
		call_integration_hook('pre_cache_quick_get', array(&$key, &$file, &$function, &$params, &$level));
3275
3276
	/* Refresh the cache if either:
3277
		1. Caching is disabled.
3278
		2. The cache level isn't high enough.
3279
		3. The item has not been cached or the cached item expired.
3280
		4. The cached item has a custom expiration condition evaluating to true.
3281
		5. The expire time set in the cache item has passed (needed for Zend).
3282
	*/
3283
	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()))
0 ignored issues
show
Coding Style introduced by
The function cache_quick_get() contains an eval expression.

On one hand, eval might be exploited by malicious users if they somehow manage to inject dynamic content. On the other hand, with the emergence of faster PHP runtimes like the HHVM, eval prevents some optimization that they perform.

Loading history...
3284
	{
3285
		require_once($sourcedir . '/' . $file);
3286
		$cache_block = call_user_func_array($function, $params);
3287
3288
		if (!empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= $level)
3289
			cache_put_data($key, $cache_block, $cache_block['expires'] - time());
3290
	}
3291
3292
	// Some cached data may need a freshening up after retrieval.
3293
	if (!empty($cache_block['post_retri_eval']))
3294
		eval($cache_block['post_retri_eval']);
0 ignored issues
show
Coding Style introduced by
The function cache_quick_get() contains an eval expression.

On one hand, eval might be exploited by malicious users if they somehow manage to inject dynamic content. On the other hand, with the emergence of faster PHP runtimes like the HHVM, eval prevents some optimization that they perform.

Loading history...
3295
3296
	if (function_exists('call_integration_hook'))
3297
		call_integration_hook('post_cache_quick_get', array(&$cache_block));
3298
3299
	return $cache_block['data'];
3300
}
3301
3302
/**
3303
 * Puts value in the cache under key for ttl seconds.
3304
 *
3305
 * - It may "miss" so shouldn't be depended on
3306
 * - Uses the cache engine chosen in the ACP and saved in settings.php
3307
 * - It supports:
3308
 *	 Xcache: https://xcache.lighttpd.net/wiki/XcacheApi
3309
 *	 memcache: https://php.net/memcache
3310
 *	 APC: https://php.net/apc
3311
 *   APCu: https://php.net/book.apcu
3312
 *	 Zend: http://files.zend.com/help/Zend-Platform/output_cache_functions.htm
3313
 *	 Zend: http://files.zend.com/help/Zend-Platform/zend_cache_functions.htm
3314
 *
3315
 * @param string $key A key for this value
3316
 * @param mixed $value The data to cache
3317
 * @param int $ttl How long (in seconds) the data should be cached for
3318
 */
3319
function cache_put_data($key, $value, $ttl = 120)
3320
{
3321
	global $smcFunc, $cache_enable, $cacheAPI;
3322
	global $cache_hits, $cache_count, $db_show_debug;
3323
3324
	if (empty($cache_enable) || empty($cacheAPI))
3325
		return;
3326
3327
	$cache_count = isset($cache_count) ? $cache_count + 1 : 1;
3328
	if (isset($db_show_debug) && $db_show_debug === true)
3329
	{
3330
		$cache_hits[$cache_count] = array('k' => $key, 'd' => 'put', 's' => $value === null ? 0 : strlen(isset($smcFunc['json_encode']) ? $smcFunc['json_encode']($value) : json_encode($value)));
3331
		$st = microtime();
3332
	}
3333
3334
	// The API will handle the rest.
3335
	$value = $value === null ? null : (isset($smcFunc['json_encode']) ? $smcFunc['json_encode']($value) : json_encode($value));
3336
	$cacheAPI->putData($key, $value, $ttl);
3337
3338
	if (function_exists('call_integration_hook'))
3339
		call_integration_hook('cache_put_data', array(&$key, &$value, &$ttl));
3340
3341 View Code Duplication
	if (isset($db_show_debug) && $db_show_debug === true)
3342
		$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...
3343
}
3344
3345
/**
3346
 * Gets the value from the cache specified by key, so long as it is not older than ttl seconds.
3347
 * - It may often "miss", so shouldn't be depended on.
3348
 * - It supports the same as cache_put_data().
3349
 *
3350
 * @param string $key The key for the value to retrieve
3351
 * @param int $ttl The maximum age of the cached data
3352
 * @return string The cached data or null if nothing was loaded
3353
 */
3354
function cache_get_data($key, $ttl = 120)
3355
{
3356
	global $smcFunc, $cache_enable, $cacheAPI;
3357
	global $cache_hits, $cache_count, $cache_misses, $cache_count_misses, $db_show_debug;
3358
3359
	if (empty($cache_enable) || empty($cacheAPI))
3360
		return;
3361
3362
	$cache_count = isset($cache_count) ? $cache_count + 1 : 1;
3363
	if (isset($db_show_debug) && $db_show_debug === true)
3364
	{
3365
		$cache_hits[$cache_count] = array('k' => $key, 'd' => 'get');
3366
		$st = microtime();
3367
		$original_key = $key;
3368
	}
3369
3370
	// Ask the API to get the data.
3371
	$value = $cacheAPI->getData($key, $ttl);
3372
3373
	if (isset($db_show_debug) && $db_show_debug === true)
3374
	{
3375
		$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...
3376
		$cache_hits[$cache_count]['s'] = isset($value) ? strlen($value) : 0;
3377
3378
		if (empty($value))
3379
		{
3380
			if (!is_array($cache_misses))
3381
				$cache_misses = array();
3382
3383
			$cache_count_misses = isset($cache_count_misses) ? $cache_count_misses + 1 : 1;
3384
			$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...
3385
		}
3386
	}
3387
3388
	if (function_exists('call_integration_hook') && isset($value))
3389
		call_integration_hook('cache_get_data', array(&$key, &$ttl, &$value));
3390
3391
	return empty($value) ? null : (isset($smcFunc['json_encode']) ? $smcFunc['json_decode']($value, true) : smf_json_decode($value, true));
3392
}
3393
3394
/**
3395
 * Empty out the cache in use as best it can
3396
 *
3397
 * It may only remove the files of a certain type (if the $type parameter is given)
3398
 * Type can be user, data or left blank
3399
 * 	- user clears out user data
3400
 *  - data clears out system / opcode data
3401
 *  - If no type is specified will perform a complete cache clearing
3402
 * For cache engines that do not distinguish on types, a full cache flush will be done
3403
 *
3404
 * @param string $type The cache type ('memcached', 'apc', 'xcache', 'zend' or something else for SMF's file cache)
3405
 */
3406
function clean_cache($type = '')
3407
{
3408
	global $cacheAPI;
3409
3410
	// If we can't get to the API, can't do this.
3411
	if (empty($cacheAPI))
3412
		return;
3413
3414
	// Ask the API to do the heavy lifting. cleanCache also calls invalidateCache to be sure.
3415
	$cacheAPI->cleanCache($type);
3416
3417
	call_integration_hook('integrate_clean_cache');
3418
	clearstatcache();
3419
}
3420
3421
/**
3422
 * Helper function to set an array of data for an user's avatar.
3423
 *
3424
 * Makes assumptions based on the data provided, the following keys are required:
3425
 * - avatar The raw "avatar" column in members table
3426
 * - email The user's email. Used to get the gravatar info
3427
 * - filename The attachment filename
3428
 *
3429
 * @param array $data An array of raw info
3430
 * @return array An array of avatar data
3431
 */
3432
function set_avatar_data($data = array())
3433
{
3434
	global $modSettings, $boardurl, $smcFunc, $image_proxy_enabled, $image_proxy_secret;
3435
3436
	// Come on!
3437
	if (empty($data))
3438
		return array();
3439
3440
	// Set a nice default var.
3441
	$image = '';
3442
3443
	// Gravatar has been set as mandatory!
3444
	if (!empty($modSettings['gravatarOverride']))
3445
	{
3446
		if (!empty($modSettings['gravatarAllowExtraEmail']) && !empty($data['avatar']) && stristr($data['avatar'], 'gravatar://'))
3447
			$image = get_gravatar_url($smcFunc['substr']($data['avatar'], 11));
3448
3449
		else if (!empty($data['email']))
3450
			$image = get_gravatar_url($data['email']);
3451
	}
3452
3453
	// Look if the user has a gravatar field or has set an external url as avatar.
3454
	else
3455
	{
3456
		// So it's stored in the member table?
3457
		if (!empty($data['avatar']))
3458
		{
3459
			// Gravatar.
3460
			if (stristr($data['avatar'], 'gravatar://'))
3461
			{
3462
				if ($data['avatar'] == 'gravatar://')
3463
					$image = get_gravatar_url($data['email']);
3464
3465
				elseif (!empty($modSettings['gravatarAllowExtraEmail']))
3466
					$image = get_gravatar_url($smcFunc['substr']($data['avatar'], 11));
3467
			}
3468
3469
			// External url.
3470
			else
3471
			{
3472
				// Using ssl?
3473
				if (!empty($modSettings['force_ssl']) && $image_proxy_enabled && stripos($data['avatar'], 'http://') !== false)
3474
					$image = strtr($boardurl, array('http://' => 'https://')) . '/proxy.php?request=' . urlencode($data['avatar']) . '&hash=' . md5($data['avatar'] . $image_proxy_secret);
3475
3476
				// Just a plain external url.
3477
				else
3478
					$image = (stristr($data['avatar'], 'http://') || stristr($data['avatar'], 'https://')) ? $data['avatar'] : $modSettings['avatar_url'] . '/' . $data['avatar'];
3479
			}
3480
		}
3481
3482
		// Perhaps this user has an attachment as avatar...
3483
		else if (!empty($data['filename']))
3484
			$image = $modSettings['custom_avatar_url'] . '/' . $data['filename'];
3485
3486
		// Right... no avatar... use our default image.
3487
		else
3488
			$image = $modSettings['avatar_url'] . '/default.png';
3489
	}
3490
3491
	call_integration_hook('integrate_set_avatar_data', array(&$image, &$data));
3492
3493
	// 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.
3494
	if (!empty($image))
3495
		return array(
3496
			'name' => !empty($data['avatar']) ? $data['avatar'] : '',
3497
			'image' => '<img class="avatar" src="' . $image . '" />',
3498
			'href' => $image,
3499
			'url' => $image,
3500
		);
3501
3502
	// Fallback to make life easier for everyone...
3503
	else
3504
		return array(
3505
			'name' => '',
3506
			'image' => '',
3507
			'href' => '',
3508
			'url' => '',
3509
		);
3510
}
3511
3512
?>
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...