Passed
Push — release-2.1 ( 0c2197...207d2d )
by Jeremy
05:47
created

Load.php ➔ loadUserSettings()   F

Complexity

Conditions 132
Paths > 20000

Size

Total Lines 385

Duplication

Lines 2
Ratio 0.52 %

Importance

Changes 0
Metric Value
cc 132
nc 4294967295
nop 0
dl 2
loc 385
rs 0
c 0
b 0
f 0

How to fix   Long Method    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 2018 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
		if (empty($modSettings['defaultMaxTopics']) || $modSettings['defaultMaxTopics'] <= 0 || $modSettings['defaultMaxTopics'] > 999)
57
			$modSettings['defaultMaxTopics'] = 20;
58
		if (empty($modSettings['defaultMaxMessages']) || $modSettings['defaultMaxMessages'] <= 0 || $modSettings['defaultMaxMessages'] > 999)
59
			$modSettings['defaultMaxMessages'] = 15;
60
		if (empty($modSettings['defaultMaxMembers']) || $modSettings['defaultMaxMembers'] <= 0 || $modSettings['defaultMaxMembers'] > 999)
61
			$modSettings['defaultMaxMembers'] = 30;
62
		if (empty($modSettings['defaultMaxListItems']) || $modSettings['defaultMaxListItems'] <= 0 || $modSettings['defaultMaxListItems'] > 999)
63
			$modSettings['defaultMaxListItems'] = 15;
64
65
		// We explicitly 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}' : '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
			elseif ($ord < 224)
105
			{
106
				$new_string .= $string[$i] . $string[$i + 1];
107
				$i += 2;
108
			}
109
			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
	// global array of anonymous helper functions, used mostly to properly handle multi byte strings
129
	$smcFunc += array(
130
		'entity_fix' => function($string)
131
		{
132
			$num = $string[0] === 'x' ? hexdec(substr($string, 1)) : (int) $string;
133
			return $num < 0x20 || $num > 0x10FFFF || ($num >= 0xD800 && $num <= 0xDFFF) || $num === 0x202E || $num === 0x202D ? '' : '&#' . $num . ';';
134
		},
135
		'htmlspecialchars' => function($string, $quote_style = ENT_COMPAT, $charset = 'ISO-8859-1') use ($ent_check, $utf8, $fix_utf8mb4)
136
		{
137
			return $fix_utf8mb4($ent_check(htmlspecialchars($string, $quote_style, $utf8 ? 'UTF-8' : $charset)));
138
		},
139
		'htmltrim' => function($string) use ($utf8, $ent_check)
140
		{
141
			// Preg_replace space characters depend on the character set in use
142
			$space_chars = $utf8 ? '\p{Z}\p{C}' : '\x00-\x20\x80-\xA0';
143
144
			return preg_replace('~^(?:[' . $space_chars . ']|&nbsp;)+|(?:[' . $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, $ent_list, $modSettings)
0 ignored issues
show
Unused Code introduced by
The import $modSettings is not used and could be removed.

This check looks for imports that have been defined, but are not used in the scope.

Loading history...
151
		{
152
			$haystack_arr = preg_split('~(' . $ent_list . '|.)~' . ($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));
0 ignored issues
show
Bug introduced by
It seems like $haystack_arr can also be of type false; however, parameter $array of array_slice() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

156
				$result = array_search($needle, array_slice(/** @scrutinizer ignore-type */ $haystack_arr, $offset));
Loading history...
157
				return is_int($result) ? $result + $offset : false;
158
			}
159
			else
160
			{
161
				$needle_arr = preg_split('~(' . $ent_list . '|.)~' . ($utf8 ? 'u' : '') . '', $ent_check($needle), -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
162
				$needle_size = count($needle_arr);
0 ignored issues
show
Bug introduced by
It seems like $needle_arr can also be of type false; however, parameter $var of count() does only seem to accept Countable|array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

162
				$needle_size = count(/** @scrutinizer ignore-type */ $needle_arr);
Loading history...
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, $ent_list, $modSettings)
0 ignored issues
show
Unused Code introduced by
The import $modSettings is not used and could be removed.

This check looks for imports that have been defined, but are not used in the scope.

Loading history...
176
		{
177
			$ent_arr = preg_split('~(' . $ent_list . '|.)~' . ($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));
0 ignored issues
show
Bug introduced by
It seems like $ent_arr can also be of type false; however, parameter $array of array_slice() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

178
			return $length === null ? implode('', array_slice(/** @scrutinizer ignore-type */ $ent_arr, $start)) : implode('', array_slice($ent_arr, $start, $length));
Loading history...
179
		},
180
		'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)
0 ignored issues
show
Bug introduced by
It seems like $words can also be of type false; however, parameter $var of count() does only seem to accept Countable|array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

218
			for ($i = 0, $n = count(/** @scrutinizer ignore-type */ $words); $i < $n; $i += 2)
Loading history...
219
				$words[$i] = $smcFunc['ucfirst']($words[$i]);
220
			return implode('', $words);
0 ignored issues
show
Bug introduced by
It seems like $words can also be of type false; however, parameter $pieces of implode() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

220
			return implode('', /** @scrutinizer ignore-type */ $words);
Loading history...
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()))
0 ignored issues
show
Bug introduced by
It seems like timezone_identifiers_list() can also be of type false; however, parameter $haystack of in_array() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

227
	if (isset($modSettings['default_timezone']) && in_array($modSettings['default_timezone'], /** @scrutinizer ignore-type */ timezone_identifiers_list()))
Loading history...
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
		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
			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 constant SMF_INTEGRATION_SETTINGS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
290
		foreach ($integration_settings as $hook => $function)
291
			add_integration_function($hook, $function, '', false);
0 ignored issues
show
Bug introduced by
'' of type string is incompatible with the type boolean expected by parameter $permanent of add_integration_function(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

291
			add_integration_function($hook, $function, /** @scrutinizer ignore-type */ '', false);
Loading history...
Bug introduced by
false of type false is incompatible with the type string expected by parameter $file of add_integration_function(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

291
			add_integration_function($hook, $function, '', /** @scrutinizer ignore-type */ false);
Loading history...
292
	}
293
294
	// Any files to pre include?
295
	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' => DIRECTORY_SEPARATOR === '\\',
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
		'before_member',
333
		'after_member',
334
	);
335
336
	// Define an array for content-related <meta> elements (e.g. description, keywords, Open Graph) for the HTML head.
337
	$context['meta_tags'] = array();
338
339
	// Define an array of allowed HTML tags.
340
	$context['allowed_html_tags'] = array(
341
		'<img>',
342
		'<div>',
343
	);
344
345
	// These are the only valid image types for SMF, by default anyway.
346
	$context['valid_image_types'] = array(
347
		1 => 'gif',
348
		2 => 'jpeg',
349
		3 => 'png',
350
		5 => 'psd',
351
		6 => 'bmp',
352
		7 => 'tiff',
353
		8 => 'tiff',
354
		9 => 'jpeg',
355
		14 => 'iff'
356
	);
357
358
	// Define a list of allowed tags for descriptions.
359
	$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',);
360
361
	// Define a list of old BBC tags no longer parsed
362
	$context['legacy_bbc'] = array(
363
		'br',
364
		'tt',
365
		'flash',
366
		'bdo',
367
		'black',
368
		'white',
369
		'red',
370
		'green',
371
		'blue',
372
		'acronym',
373
		'ftp',
374
		'glow',
375
		'move',
376
		'shadow',
377
	);
378
379
	// Call pre load integration functions.
380
	call_integration_hook('integrate_pre_load');
381
}
382
383
/**
384
 * Load all the important user information.
385
 * What it does:
386
 * 	- sets up the $user_info array
387
 * 	- assigns $user_info['query_wanna_see_board'] for what boards the user can see.
388
 * 	- first checks for cookie or integration validation.
389
 * 	- uses the current session if no integration function or cookie is found.
390
 * 	- checks password length, if member is activated and the login span isn't over.
391
 * 		- if validation fails for the user, $id_member is set to 0.
392
 * 		- updates the last visit time when needed.
393
 */
394
function loadUserSettings()
395
{
396
	global $modSettings, $user_settings, $sourcedir, $smcFunc;
397
	global $cookiename, $user_info, $language, $context, $image_proxy_enabled;
398
399
	// Check first the integration, then the cookie, and last the session.
400
	if (count($integration_ids = call_integration_hook('integrate_verify_user')) > 0)
401
	{
402
		$id_member = 0;
403
		foreach ($integration_ids as $integration_id)
404
		{
405
			$integration_id = (int) $integration_id;
406
			if ($integration_id > 0)
407
			{
408
				$id_member = $integration_id;
409
				$already_verified = true;
410
				break;
411
			}
412
		}
413
	}
414
	else
415
		$id_member = 0;
416
417
	if (empty($id_member) && isset($_COOKIE[$cookiename]))
418
	{
419
		// First try 2.1 json-format cookie
420
		$cookie_data = $smcFunc['json_decode']($_COOKIE[$cookiename], true, false);
421
422
		// Legacy format (for recent 2.0 --> 2.1 upgrades)
423
		if (empty($cookie_data))
424
			$cookie_data = safe_unserialize($_COOKIE[$cookiename]);
425
426
		list($id_member, $password, $login_span, $cookie_domain, $cookie_path) = array_pad((array) $cookie_data, 5, '');
427
428
		$id_member = !empty($id_member) && strlen($password) > 0 ? (int) $id_member : 0;
429
430
		// Make sure the cookie is set to the correct domain and path
431
		require_once($sourcedir . '/Subs-Auth.php');
432
		if (array($cookie_domain, $cookie_path) !== url_parts(!empty($modSettings['localCookies']), !empty($modSettings['globalCookies'])))
433
			setLoginCookie((int) $login_span - time(), $id_member);
434
	}
435
	elseif (empty($id_member) && isset($_SESSION['login_' . $cookiename]) && ($_SESSION['USER_AGENT'] == $_SERVER['HTTP_USER_AGENT'] || !empty($modSettings['disableCheckUA'])))
436
	{
437
		// @todo Perhaps we can do some more checking on this, such as on the first octet of the IP?
438
		$cookie_data = $smcFunc['json_decode']($_SESSION['login_' . $cookiename], true);
439
440
		if (empty($cookie_data))
441
			$cookie_data = safe_unserialize($_SESSION['login_' . $cookiename]);
442
443
		list($id_member, $password, $login_span) = array_pad((array) $cookie_data, 3, '');
444
		$id_member = !empty($id_member) && strlen($password) == 128 && (int) $login_span > time() ? (int) $id_member : 0;
445
	}
446
447
	// Only load this stuff if the user isn't a guest.
448
	if ($id_member != 0)
449
	{
450
		// Is the member data cached?
451
		if (empty($modSettings['cache_enable']) || $modSettings['cache_enable'] < 2 || ($user_settings = cache_get_data('user_settings-' . $id_member, 60)) == null)
452
		{
453
			$request = $smcFunc['db_query']('', '
454
				SELECT mem.*, COALESCE(a.id_attach, 0) AS id_attach, a.filename, a.attachment_type
455
				FROM {db_prefix}members AS mem
456
					LEFT JOIN {db_prefix}attachments AS a ON (a.id_member = {int:id_member})
457
				WHERE mem.id_member = {int:id_member}
458
				LIMIT 1',
459
				array(
460
					'id_member' => $id_member,
461
				)
462
			);
463
			$user_settings = $smcFunc['db_fetch_assoc']($request);
464
			$smcFunc['db_free_result']($request);
465
466
			if (!empty($modSettings['force_ssl']) && $image_proxy_enabled && stripos($user_settings['avatar'], 'http://') !== false && empty($user_info['possibly_robot']))
467
				$user_settings['avatar'] = get_proxied_url($user_settings['avatar']);
468
469
			if (!empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= 2)
470
				cache_put_data('user_settings-' . $id_member, $user_settings, 60);
471
		}
472
473
		// Did we find 'im?  If not, junk it.
474
		if (!empty($user_settings))
475
		{
476
			// As much as the password should be right, we can assume the integration set things up.
477
			if (!empty($already_verified) && $already_verified === true)
478
				$check = true;
479
			// SHA-512 hash should be 128 characters long.
480
			elseif (strlen($password) == 128)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $password does not seem to be defined for all execution paths leading up to this point.
Loading history...
481
				$check = hash_salt($user_settings['passwd'], $user_settings['password_salt']) == $password;
482
			else
483
				$check = false;
484
485
			// Wrong password or not activated - either way, you're going nowhere.
486
			$id_member = $check && ($user_settings['is_activated'] == 1 || $user_settings['is_activated'] == 11) ? (int) $user_settings['id_member'] : 0;
487
		}
488
		else
489
			$id_member = 0;
490
491
		// If we no longer have the member maybe they're being all hackey, stop brute force!
492
		if (!$id_member)
493
		{
494
			require_once($sourcedir . '/LogInOut.php');
495
			validatePasswordFlood(
496
				!empty($user_settings['id_member']) ? $user_settings['id_member'] : $id_member,
497
				!empty($user_settings['member_name']) ? $user_settings['member_name'] : '',
498
				!empty($user_settings['passwd_flood']) ? $user_settings['passwd_flood'] : false,
499
				$id_member != 0
500
			);
501
		}
502
		// Validate for Two Factor Authentication
503
		elseif (!empty($modSettings['tfa_mode']) && $id_member && !empty($user_settings['tfa_secret']) && (empty($_REQUEST['action']) || !in_array($_REQUEST['action'], array('login2', 'logintfa'))))
504
		{
505
			$tfacookie = $cookiename . '_tfa';
506
			$tfasecret = null;
507
508
			$verified = call_integration_hook('integrate_verify_tfa', array($id_member, $user_settings));
509
510
			if (empty($verified) || !in_array(true, $verified))
511
			{
512
				if (!empty($_COOKIE[$tfacookie]))
513
				{
514
					$tfa_data = $smcFunc['json_decode']($_COOKIE[$tfacookie], true);
515
516
					list ($tfamember, $tfasecret) = array_pad((array) $tfa_data, 2, '');
517
518
					if (!isset($tfamember, $tfasecret) || (int) $tfamember != $id_member)
519
						$tfasecret = null;
520
				}
521
522
				// They didn't finish logging in before coming here? Then they're no one to us.
523
				if (empty($tfasecret) || hash_salt($user_settings['tfa_backup'], $user_settings['password_salt']) != $tfasecret)
524
				{
525
					setLoginCookie(-3600, $id_member);
526
					$id_member = 0;
527
					$user_settings = array();
528
				}
529
			}
530
		}
531
		// When authenticating their two factor code, make sure to reset their ID for security
532
		elseif (!empty($modSettings['tfa_mode']) && $id_member && !empty($user_settings['tfa_secret']) && $_REQUEST['action'] == 'logintfa')
533
		{
534
			$id_member = 0;
535
			$context['tfa_member'] = $user_settings;
536
			$user_settings = array();
537
		}
538
		// Are we forcing 2FA? Need to check if the user groups actually require 2FA
539
		elseif (!empty($modSettings['tfa_mode']) && $modSettings['tfa_mode'] >= 2 && $id_member && empty($user_settings['tfa_secret']))
540
		{
541
			if ($modSettings['tfa_mode'] == 2) //only do this if we are just forcing SOME membergroups
542
			{
543
				//Build an array of ALL user membergroups.
544
				$full_groups = array($user_settings['id_group']);
545
				if (!empty($user_settings['additional_groups']))
546
				{
547
					$full_groups = array_merge($full_groups, explode(',', $user_settings['additional_groups']));
548
					$full_groups = array_unique($full_groups); //duplicates, maybe?
549
				}
550
551
				//Find out if any group requires 2FA
552
				$request = $smcFunc['db_query']('', '
553
					SELECT COUNT(id_group) AS total
554
					FROM {db_prefix}membergroups
555
					WHERE tfa_required = {int:tfa_required}
556
						AND id_group IN ({array_int:full_groups})',
557
					array(
558
						'tfa_required' => 1,
559
						'full_groups' => $full_groups,
560
					)
561
				);
562
				$row = $smcFunc['db_fetch_assoc']($request);
563
				$smcFunc['db_free_result']($request);
564
			}
565
			else
566
				$row['total'] = 1; //simplifies logics in the next "if"
0 ignored issues
show
Comprehensibility Best Practice introduced by
$row was never initialized. Although not strictly required by PHP, it is generally a good practice to add $row = array(); before regardless.
Loading history...
567
568
			$area = !empty($_REQUEST['area']) ? $_REQUEST['area'] : '';
569
			$action = !empty($_REQUEST['action']) ? $_REQUEST['action'] : '';
570
571
			if ($row['total'] > 0 && !in_array($action, array('profile', 'logout')) || ($action == 'profile' && $area != 'tfasetup'))
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: ($row['total'] > 0 && ! ... && $area != 'tfasetup', Probably Intended Meaning: $row['total'] > 0 && (! ...&& $area != 'tfasetup')
Loading history...
572
				redirectexit('action=profile;area=tfasetup;forced');
573
		}
574
	}
575
576
	// Found 'im, let's set up the variables.
577
	if ($id_member != 0)
578
	{
579
		// Let's not update the last visit time in these cases...
580
		// 1. SSI doesn't count as visiting the forum.
581
		// 2. RSS feeds and XMLHTTP requests don't count either.
582
		// 3. If it was set within this session, no need to set it again.
583
		// 4. New session, yet updated < five hours ago? Maybe cache can help.
584
		// 5. We're still logging in or authenticating
585
		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))
0 ignored issues
show
introduced by
The condition SMF != 'SSI' is always false.
Loading history...
586
		{
587
			// @todo can this be cached?
588
			// Do a quick query to make sure this isn't a mistake.
589
			$result = $smcFunc['db_query']('', '
590
				SELECT poster_time
591
				FROM {db_prefix}messages
592
				WHERE id_msg = {int:id_msg}
593
				LIMIT 1',
594
				array(
595
					'id_msg' => $user_settings['id_msg_last_visit'],
596
				)
597
			);
598
			list ($visitTime) = $smcFunc['db_fetch_row']($result);
599
			$smcFunc['db_free_result']($result);
600
601
			$_SESSION['id_msg_last_visit'] = $user_settings['id_msg_last_visit'];
602
603
			// If it was *at least* five hours ago...
604
			if ($visitTime < time() - 5 * 3600)
605
			{
606
				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']));
607
				$user_settings['last_login'] = time();
608
609
				if (!empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= 2)
610
					cache_put_data('user_settings-' . $id_member, $user_settings, 60);
611
612
				if (!empty($modSettings['cache_enable']))
613
					cache_put_data('user_last_visit-' . $id_member, $_SESSION['id_msg_last_visit'], 5 * 3600);
614
			}
615
		}
616
		elseif (empty($_SESSION['id_msg_last_visit']))
617
			$_SESSION['id_msg_last_visit'] = $user_settings['id_msg_last_visit'];
618
619
		$username = $user_settings['member_name'];
620
621
		if (empty($user_settings['additional_groups']))
622
			$user_info = array(
623
				'groups' => array($user_settings['id_group'], $user_settings['id_post_group'])
624
			);
625
		else
626
			$user_info = array(
627
				'groups' => array_merge(
628
					array($user_settings['id_group'], $user_settings['id_post_group']),
629
					explode(',', $user_settings['additional_groups'])
630
				)
631
			);
632
633
		// Because history has proven that it is possible for groups to go bad - clean up in case.
634
		$user_info['groups'] = array_map('intval', $user_info['groups']);
635
636
		// This is a logged in user, so definitely not a spider.
637
		$user_info['possibly_robot'] = false;
638
639
		// Figure out the new time offset.
640
		if (!empty($user_settings['timezone']))
641
		{
642
			// Get the offsets from UTC for the server, then for the user.
643
			$tz_system = new DateTimeZone(@date_default_timezone_get());
644
			$tz_user = new DateTimeZone($user_settings['timezone']);
645
			$time_system = new DateTime('now', $tz_system);
646
			$time_user = new DateTime('now', $tz_user);
647
			$user_info['time_offset'] = ($tz_user->getOffset($time_user) - $tz_system->getOffset($time_system)) / 3600;
648
		}
649
		else
650
		{
651
			// !!! Compatibility.
652
			$user_info['time_offset'] = empty($user_settings['time_offset']) ? 0 : $user_settings['time_offset'];
653
		}
654
	}
655
	// If the user is a guest, initialize all the critical user settings.
656
	else
657
	{
658
		// This is what a guest's variables should be.
659
		$username = '';
660
		$user_info = array('groups' => array(-1));
661
		$user_settings = array();
662
663
		if (isset($_COOKIE[$cookiename]) && empty($context['tfa_member']))
664
			$_COOKIE[$cookiename] = '';
665
666
		// Expire the 2FA cookie
667
		if (isset($_COOKIE[$cookiename . '_tfa']) && empty($context['tfa_member']))
668
		{
669
			$tfa_data = $smcFunc['json_decode']($_COOKIE[$cookiename . '_tfa'], true);
670
671
			list (,, $exp) = array_pad((array) $tfa_data, 3, 0);
672
673
			if (time() > $exp)
674
			{
675
				$_COOKIE[$cookiename . '_tfa'] = '';
676
				setTFACookie(-3600, 0, '');
677
			}
678
		}
679
680
		// Create a login token if it doesn't exist yet.
681
		if (!isset($_SESSION['token']['post-login']))
682
			createToken('login');
683
		else
684
			list ($context['login_token_var'],,, $context['login_token']) = $_SESSION['token']['post-login'];
685
686
		// Do we perhaps think this is a search robot? Check every five minutes just in case...
687
		if ((!empty($modSettings['spider_mode']) || !empty($modSettings['spider_group'])) && (!isset($_SESSION['robot_check']) || $_SESSION['robot_check'] < time() - 300))
688
		{
689
			require_once($sourcedir . '/ManageSearchEngines.php');
690
			$user_info['possibly_robot'] = SpiderCheck();
691
		}
692
		elseif (!empty($modSettings['spider_mode']))
693
			$user_info['possibly_robot'] = isset($_SESSION['id_robot']) ? $_SESSION['id_robot'] : 0;
694
		// If we haven't turned on proper spider hunts then have a guess!
695
		else
696
		{
697
			$ci_user_agent = strtolower($_SERVER['HTTP_USER_AGENT']);
698
			$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;
699
		}
700
701
		// We don't know the offset...
702
		$user_info['time_offset'] = 0;
703
704
		// Login Cookie times. Format: time => txt
705
		$context['login_cookie_times'] = array(
706
			60 => 'one_hour',
707
			1440 => 'one_day',
708
			10080 => 'one_week',
709
			43200 => 'one_month',
710
			3153600 => 'always_logged_in',
711
		);
712
	}
713
714
	// Set up the $user_info array.
715
	$user_info += array(
716
		'id' => $id_member,
717
		'username' => $username,
718
		'name' => isset($user_settings['real_name']) ? $user_settings['real_name'] : '',
719
		'email' => isset($user_settings['email_address']) ? $user_settings['email_address'] : '',
720
		'passwd' => isset($user_settings['passwd']) ? $user_settings['passwd'] : '',
721
		'language' => empty($user_settings['lngfile']) || empty($modSettings['userLanguage']) ? $language : $user_settings['lngfile'],
722
		'is_guest' => $id_member == 0,
723
		'is_admin' => in_array(1, $user_info['groups']),
724
		'theme' => empty($user_settings['id_theme']) ? 0 : $user_settings['id_theme'],
725
		'last_login' => empty($user_settings['last_login']) ? 0 : $user_settings['last_login'],
726
		'ip' => $_SERVER['REMOTE_ADDR'],
727
		'ip2' => $_SERVER['BAN_CHECK_IP'],
728
		'posts' => empty($user_settings['posts']) ? 0 : $user_settings['posts'],
729
		'time_format' => empty($user_settings['time_format']) ? $modSettings['time_format'] : $user_settings['time_format'],
730
		'avatar' => array(
731
			'url' => isset($user_settings['avatar']) ? $user_settings['avatar'] : '',
732
			'filename' => empty($user_settings['filename']) ? '' : $user_settings['filename'],
733
			'custom_dir' => !empty($user_settings['attachment_type']) && $user_settings['attachment_type'] == 1,
734
			'id_attach' => isset($user_settings['id_attach']) ? $user_settings['id_attach'] : 0
735
		),
736
		'smiley_set' => isset($user_settings['smiley_set']) ? $user_settings['smiley_set'] : '',
737
		'messages' => empty($user_settings['instant_messages']) ? 0 : $user_settings['instant_messages'],
738
		'unread_messages' => empty($user_settings['unread_messages']) ? 0 : $user_settings['unread_messages'],
739
		'alerts' => empty($user_settings['alerts']) ? 0 : $user_settings['alerts'],
740
		'total_time_logged_in' => empty($user_settings['total_time_logged_in']) ? 0 : $user_settings['total_time_logged_in'],
741
		'buddies' => !empty($modSettings['enable_buddylist']) && !empty($user_settings['buddy_list']) ? explode(',', $user_settings['buddy_list']) : array(),
742
		'ignoreboards' => !empty($user_settings['ignore_boards']) && !empty($modSettings['allow_ignore_boards']) ? explode(',', $user_settings['ignore_boards']) : array(),
743
		'ignoreusers' => !empty($user_settings['pm_ignore_list']) ? explode(',', $user_settings['pm_ignore_list']) : array(),
744
		'warning' => isset($user_settings['warning']) ? $user_settings['warning'] : 0,
745
		'permissions' => array(),
746
	);
747
	$user_info['groups'] = array_unique($user_info['groups']);
748
749
	// 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.
750
	if (!empty($user_info['ignoreboards']) && empty($user_info['ignoreboards'][$tmp = count($user_info['ignoreboards']) - 1]))
751
		unset($user_info['ignoreboards'][$tmp]);
752
753
	// Allow the user to change their language.
754
	if (!empty($modSettings['userLanguage']))
755
	{
756
		$languages = getLanguages();
757
758
		// Is it valid?
759
		if (!empty($_GET['language']) && isset($languages[strtr($_GET['language'], './\\:', '____')]))
760
		{
761
			$user_info['language'] = strtr($_GET['language'], './\\:', '____');
762
763
			// Make it permanent for members.
764
			if (!empty($user_info['id']))
765
				updateMemberData($user_info['id'], array('lngfile' => $user_info['language']));
766
			else
767
				$_SESSION['language'] = $user_info['language'];
768
		}
769
		elseif (!empty($_SESSION['language']) && isset($languages[strtr($_SESSION['language'], './\\:', '____')]))
770
			$user_info['language'] = strtr($_SESSION['language'], './\\:', '____');
771
	}
772
773
	$temp = build_query_board($user_info['id']);
774
	$user_info['query_see_board'] = $temp['query_see_board'];
775
	$user_info['query_wanna_see_board'] = $temp['query_wanna_see_board'];
776
777
	call_integration_hook('integrate_user_info');
778
}
779
780
/**
781
 * Check for moderators and see if they have access to the board.
782
 * What it does:
783
 * - sets up the $board_info array for current board information.
784
 * - if cache is enabled, the $board_info array is stored in cache.
785
 * - redirects to appropriate post if only message id is requested.
786
 * - is only used when inside a topic or board.
787
 * - determines the local moderators for the board.
788
 * - adds group id 3 if the user is a local moderator for the board they are in.
789
 * - prevents access if user is not in proper group nor a local moderator of the board.
790
 */
791
function loadBoard()
792
{
793
	global $txt, $scripturl, $context, $modSettings;
794
	global $board_info, $board, $topic, $user_info, $smcFunc;
795
796
	// Assume they are not a moderator.
797
	$user_info['is_mod'] = false;
798
	$context['user']['is_mod'] = &$user_info['is_mod'];
799
800
	// Start the linktree off empty..
801
	$context['linktree'] = array();
802
803
	// Have they by chance specified a message id but nothing else?
804
	if (empty($_REQUEST['action']) && empty($topic) && empty($board) && !empty($_REQUEST['msg']))
805
	{
806
		// Make sure the message id is really an int.
807
		$_REQUEST['msg'] = (int) $_REQUEST['msg'];
808
809
		// Looking through the message table can be slow, so try using the cache first.
810
		if (($topic = cache_get_data('msg_topic-' . $_REQUEST['msg'], 120)) === null)
0 ignored issues
show
introduced by
The condition $topic = cache_get_data(...T['msg'], 120) === null is always false.
Loading history...
811
		{
812
			$request = $smcFunc['db_query']('', '
813
				SELECT id_topic
814
				FROM {db_prefix}messages
815
				WHERE id_msg = {int:id_msg}
816
				LIMIT 1',
817
				array(
818
					'id_msg' => $_REQUEST['msg'],
819
				)
820
			);
821
822
			// So did it find anything?
823
			if ($smcFunc['db_num_rows']($request))
824
			{
825
				list ($topic) = $smcFunc['db_fetch_row']($request);
826
				$smcFunc['db_free_result']($request);
827
				// Save save save.
828
				cache_put_data('msg_topic-' . $_REQUEST['msg'], $topic, 120);
829
			}
830
		}
831
832
		// Remember redirection is the key to avoiding fallout from your bosses.
833
		if (!empty($topic))
834
			redirectexit('topic=' . $topic . '.msg' . $_REQUEST['msg'] . '#msg' . $_REQUEST['msg']);
835
		else
836
		{
837
			loadPermissions();
838
			loadTheme();
839
			fatal_lang_error('topic_gone', false);
840
		}
841
	}
842
843
	// Load this board only if it is specified.
844
	if (empty($board) && empty($topic))
845
	{
846
		$board_info = array('moderators' => array(), 'moderator_groups' => array());
847
		return;
848
	}
849
850
	if (!empty($modSettings['cache_enable']) && (empty($topic) || $modSettings['cache_enable'] >= 3))
851
	{
852
		// @todo SLOW?
853
		if (!empty($topic))
854
			$temp = cache_get_data('topic_board-' . $topic, 120);
855
		else
856
			$temp = cache_get_data('board-' . $board, 120);
857
858
		if (!empty($temp))
859
		{
860
			$board_info = $temp;
861
			$board = $board_info['id'];
862
		}
863
	}
864
865
	if (empty($temp))
866
	{
867
		$request = $smcFunc['db_query']('load_board_info', '
868
			SELECT
869
				c.id_cat, b.name AS bname, b.description, b.num_topics, b.member_groups, b.deny_member_groups,
870
				b.id_parent, c.name AS cname, COALESCE(mg.id_group, 0) AS id_moderator_group, mg.group_name,
871
				COALESCE(mem.id_member, 0) AS id_moderator,
872
				mem.real_name' . (!empty($topic) ? ', b.id_board' : '') . ', b.child_level,
873
				b.id_theme, b.override_theme, b.count_posts, b.id_profile, b.redirect,
874
				b.unapproved_topics, b.unapproved_posts' . (!empty($topic) ? ', t.approved, t.id_member_started' : '') . '
875
			FROM {db_prefix}boards AS b' . (!empty($topic) ? '
876
				INNER JOIN {db_prefix}topics AS t ON (t.id_topic = {int:current_topic})' : '') . '
877
				LEFT JOIN {db_prefix}categories AS c ON (c.id_cat = b.id_cat)
878
				LEFT JOIN {db_prefix}moderator_groups AS modgs ON (modgs.id_board = {raw:board_link})
879
				LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = modgs.id_group)
880
				LEFT JOIN {db_prefix}moderators AS mods ON (mods.id_board = {raw:board_link})
881
				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = mods.id_member)
882
			WHERE b.id_board = {raw:board_link}',
883
			array(
884
				'current_topic' => $topic,
885
				'board_link' => empty($topic) ? $smcFunc['db_quote']('{int:current_board}', array('current_board' => $board)) : 't.id_board',
886
			)
887
		);
888
		// If there aren't any, skip.
889
		if ($smcFunc['db_num_rows']($request) > 0)
890
		{
891
			$row = $smcFunc['db_fetch_assoc']($request);
892
893
			// Set the current board.
894
			if (!empty($row['id_board']))
895
				$board = $row['id_board'];
896
897
			// Basic operating information. (globals... :/)
898
			$board_info = array(
899
				'id' => $board,
900
				'moderators' => array(),
901
				'moderator_groups' => array(),
902
				'cat' => array(
903
					'id' => $row['id_cat'],
904
					'name' => $row['cname']
905
				),
906
				'name' => $row['bname'],
907
				'description' => $row['description'],
908
				'num_topics' => $row['num_topics'],
909
				'unapproved_topics' => $row['unapproved_topics'],
910
				'unapproved_posts' => $row['unapproved_posts'],
911
				'unapproved_user_topics' => 0,
912
				'parent_boards' => getBoardParents($row['id_parent']),
913
				'parent' => $row['id_parent'],
914
				'child_level' => $row['child_level'],
915
				'theme' => $row['id_theme'],
916
				'override_theme' => !empty($row['override_theme']),
917
				'profile' => $row['id_profile'],
918
				'redirect' => $row['redirect'],
919
				'recycle' => !empty($modSettings['recycle_enable']) && !empty($modSettings['recycle_board']) && $modSettings['recycle_board'] == $board,
920
				'posts_count' => empty($row['count_posts']),
921
				'cur_topic_approved' => empty($topic) || $row['approved'],
922
				'cur_topic_starter' => empty($topic) ? 0 : $row['id_member_started'],
923
			);
924
925
			// Load the membergroups allowed, and check permissions.
926
			$board_info['groups'] = $row['member_groups'] == '' ? array() : explode(',', $row['member_groups']);
927
			$board_info['deny_groups'] = $row['deny_member_groups'] == '' ? array() : explode(',', $row['deny_member_groups']);
928
929
			do
930
			{
931
				if (!empty($row['id_moderator']))
932
					$board_info['moderators'][$row['id_moderator']] = array(
933
						'id' => $row['id_moderator'],
934
						'name' => $row['real_name'],
935
						'href' => $scripturl . '?action=profile;u=' . $row['id_moderator'],
936
						'link' => '<a href="' . $scripturl . '?action=profile;u=' . $row['id_moderator'] . '">' . $row['real_name'] . '</a>'
937
					);
938
939
				if (!empty($row['id_moderator_group']))
940
					$board_info['moderator_groups'][$row['id_moderator_group']] = array(
941
						'id' => $row['id_moderator_group'],
942
						'name' => $row['group_name'],
943
						'href' => $scripturl . '?action=groups;sa=members;group=' . $row['id_moderator_group'],
944
						'link' => '<a href="' . $scripturl . '?action=groups;sa=members;group=' . $row['id_moderator_group'] . '">' . $row['group_name'] . '</a>'
945
					);
946
			}
947
			while ($row = $smcFunc['db_fetch_assoc']($request));
948
949
			// If the board only contains unapproved posts and the user isn't an approver then they can't see any topics.
950
			// If that is the case do an additional check to see if they have any topics waiting to be approved.
951
			if ($board_info['num_topics'] == 0 && $modSettings['postmod_active'] && !allowedTo('approve_posts'))
952
			{
953
				// Free the previous result
954
				$smcFunc['db_free_result']($request);
955
956
				// @todo why is this using id_topic?
957
				// @todo Can this get cached?
958
				$request = $smcFunc['db_query']('', '
959
					SELECT COUNT(id_topic)
960
					FROM {db_prefix}topics
961
					WHERE id_member_started={int:id_member}
962
						AND approved = {int:unapproved}
963
						AND id_board = {int:board}',
964
					array(
965
						'id_member' => $user_info['id'],
966
						'unapproved' => 0,
967
						'board' => $board,
968
					)
969
				);
970
971
				list ($board_info['unapproved_user_topics']) = $smcFunc['db_fetch_row']($request);
972
			}
973
974
			if (!empty($modSettings['cache_enable']) && (empty($topic) || $modSettings['cache_enable'] >= 3))
975
			{
976
				// @todo SLOW?
977
				if (!empty($topic))
978
					cache_put_data('topic_board-' . $topic, $board_info, 120);
979
				cache_put_data('board-' . $board, $board_info, 120);
980
			}
981
		}
982
		else
983
		{
984
			// Otherwise the topic is invalid, there are no moderators, etc.
985
			$board_info = array(
986
				'moderators' => array(),
987
				'moderator_groups' => array(),
988
				'error' => 'exist'
989
			);
990
			$topic = null;
991
			$board = 0;
992
		}
993
		$smcFunc['db_free_result']($request);
994
	}
995
996
	if (!empty($topic))
997
		$_GET['board'] = (int) $board;
998
999
	if (!empty($board))
1000
	{
1001
		// Get this into an array of keys for array_intersect
1002
		$moderator_groups = array_keys($board_info['moderator_groups']);
1003
1004
		// Now check if the user is a moderator.
1005
		$user_info['is_mod'] = isset($board_info['moderators'][$user_info['id']]) || count(array_intersect($user_info['groups'], $moderator_groups)) != 0;
1006
1007
		if (count(array_intersect($user_info['groups'], $board_info['groups'])) == 0 && !$user_info['is_admin'])
1008
			$board_info['error'] = 'access';
1009
		if (!empty($modSettings['deny_boards_access']) && count(array_intersect($user_info['groups'], $board_info['deny_groups'])) != 0 && !$user_info['is_admin'])
1010
			$board_info['error'] = 'access';
1011
1012
		// Build up the linktree.
1013
		$context['linktree'] = array_merge(
1014
			$context['linktree'],
1015
			array(array(
1016
				'url' => $scripturl . '#c' . $board_info['cat']['id'],
1017
				'name' => $board_info['cat']['name']
1018
			)),
1019
			array_reverse($board_info['parent_boards']),
1020
			array(array(
1021
				'url' => $scripturl . '?board=' . $board . '.0',
1022
				'name' => $board_info['name']
1023
			))
1024
		);
1025
	}
1026
1027
	// Set the template contextual information.
1028
	$context['user']['is_mod'] = &$user_info['is_mod'];
1029
	$context['current_topic'] = $topic;
1030
	$context['current_board'] = $board;
1031
1032
	// No posting in redirection boards!
1033
	if (!empty($_REQUEST['action']) && $_REQUEST['action'] == 'post' && !empty($board_info['redirect']))
1034
		$board_info['error'] == 'post_in_redirect';
1035
1036
	// Hacker... you can't see this topic, I'll tell you that. (but moderators can!)
1037
	if (!empty($board_info['error']) && (!empty($modSettings['deny_boards_access']) || $board_info['error'] != 'access' || !$user_info['is_mod']))
1038
	{
1039
		// The permissions and theme need loading, just to make sure everything goes smoothly.
1040
		loadPermissions();
1041
		loadTheme();
1042
1043
		$_GET['board'] = '';
1044
		$_GET['topic'] = '';
1045
1046
		// The linktree should not give the game away mate!
1047
		$context['linktree'] = array(
1048
			array(
1049
				'url' => $scripturl,
1050
				'name' => $context['forum_name_html_safe']
1051
			)
1052
		);
1053
1054
		// If it's a prefetching agent or we're requesting an attachment.
1055
		if ((isset($_SERVER['HTTP_X_MOZ']) && $_SERVER['HTTP_X_MOZ'] == 'prefetch') || (!empty($_REQUEST['action']) && $_REQUEST['action'] === 'dlattach'))
1056
		{
1057
			ob_end_clean();
1058
			send_http_status(403);
1059
			die;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1060
		}
1061
		elseif ($board_info['error'] == 'post_in_redirect')
1062
		{
1063
			// Slightly different error message here...
1064
			fatal_lang_error('cannot_post_redirect', false);
1065
		}
1066
		elseif ($user_info['is_guest'])
1067
		{
1068
			loadLanguage('Errors');
1069
			is_not_guest($txt['topic_gone']);
1070
		}
1071
		else
1072
			fatal_lang_error('topic_gone', false);
1073
	}
1074
1075
	if ($user_info['is_mod'])
1076
		$user_info['groups'][] = 3;
1077
}
1078
1079
/**
1080
 * Load this user's permissions.
1081
 */
1082
function loadPermissions()
1083
{
1084
	global $user_info, $board, $board_info, $modSettings, $smcFunc, $sourcedir;
1085
1086
	if ($user_info['is_admin'])
1087
	{
1088
		banPermissions();
1089
		return;
1090
	}
1091
1092
	if (!empty($modSettings['cache_enable']))
1093
	{
1094
		$cache_groups = $user_info['groups'];
1095
		asort($cache_groups);
1096
		$cache_groups = implode(',', $cache_groups);
1097
		// If it's a spider then cache it different.
1098
		if ($user_info['possibly_robot'])
1099
			$cache_groups .= '-spider';
1100
1101
		if ($modSettings['cache_enable'] >= 2 && !empty($board) && ($temp = cache_get_data('permissions:' . $cache_groups . ':' . $board, 240)) != null && time() - 240 > $modSettings['settings_updated'])
1102
		{
1103
			list ($user_info['permissions']) = $temp;
1104
			banPermissions();
1105
1106
			return;
1107
		}
1108
		elseif (($temp = cache_get_data('permissions:' . $cache_groups, 240)) != null && time() - 240 > $modSettings['settings_updated'])
1109
			list ($user_info['permissions'], $removals) = $temp;
1110
	}
1111
1112
	// If it is detected as a robot, and we are restricting permissions as a special group - then implement this.
1113
	$spider_restrict = $user_info['possibly_robot'] && !empty($modSettings['spider_group']) ? ' OR (id_group = {int:spider_group} AND add_deny = 0)' : '';
1114
1115
	if (empty($user_info['permissions']))
1116
	{
1117
		// Get the general permissions.
1118
		$request = $smcFunc['db_query']('', '
1119
			SELECT permission, add_deny
1120
			FROM {db_prefix}permissions
1121
			WHERE id_group IN ({array_int:member_groups})
1122
				' . $spider_restrict,
1123
			array(
1124
				'member_groups' => $user_info['groups'],
1125
				'spider_group' => !empty($modSettings['spider_group']) ? $modSettings['spider_group'] : 0,
1126
			)
1127
		);
1128
		$removals = array();
1129
		while ($row = $smcFunc['db_fetch_assoc']($request))
1130
		{
1131
			if (empty($row['add_deny']))
1132
				$removals[] = $row['permission'];
1133
			else
1134
				$user_info['permissions'][] = $row['permission'];
1135
		}
1136
		$smcFunc['db_free_result']($request);
1137
1138
		if (isset($cache_groups))
1139
			cache_put_data('permissions:' . $cache_groups, array($user_info['permissions'], $removals), 240);
1140
	}
1141
1142
	// Get the board permissions.
1143
	if (!empty($board))
1144
	{
1145
		// Make sure the board (if any) has been loaded by loadBoard().
1146
		if (!isset($board_info['profile']))
1147
			fatal_lang_error('no_board');
1148
1149
		$request = $smcFunc['db_query']('', '
1150
			SELECT permission, add_deny
1151
			FROM {db_prefix}board_permissions
1152
			WHERE (id_group IN ({array_int:member_groups})
1153
				' . $spider_restrict . ')
1154
				AND id_profile = {int:id_profile}',
1155
			array(
1156
				'member_groups' => $user_info['groups'],
1157
				'id_profile' => $board_info['profile'],
1158
				'spider_group' => !empty($modSettings['spider_group']) ? $modSettings['spider_group'] : 0,
1159
			)
1160
		);
1161
		while ($row = $smcFunc['db_fetch_assoc']($request))
1162
		{
1163
			if (empty($row['add_deny']))
1164
				$removals[] = $row['permission'];
1165
			else
1166
				$user_info['permissions'][] = $row['permission'];
1167
		}
1168
		$smcFunc['db_free_result']($request);
1169
	}
1170
1171
	// Remove all the permissions they shouldn't have ;).
1172
	if (!empty($modSettings['permission_enable_deny']))
1173
		$user_info['permissions'] = array_diff($user_info['permissions'], $removals);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $removals does not seem to be defined for all execution paths leading up to this point.
Loading history...
1174
1175
	if (isset($cache_groups) && !empty($board) && $modSettings['cache_enable'] >= 2)
1176
		cache_put_data('permissions:' . $cache_groups . ':' . $board, array($user_info['permissions'], null), 240);
1177
1178
	// Banned?  Watch, don't touch..
1179
	banPermissions();
1180
1181
	// Load the mod cache so we can know what additional boards they should see, but no sense in doing it for guests
1182
	if (!$user_info['is_guest'])
1183
	{
1184
		if (!isset($_SESSION['mc']) || $_SESSION['mc']['time'] <= $modSettings['settings_updated'])
1185
		{
1186
			require_once($sourcedir . '/Subs-Auth.php');
1187
			rebuildModCache();
1188
		}
1189
		else
1190
			$user_info['mod_cache'] = $_SESSION['mc'];
1191
1192
		// This is a useful phantom permission added to the current user, and only the current user while they are logged in.
1193
		// For example this drastically simplifies certain changes to the profile area.
1194
		$user_info['permissions'][] = 'is_not_guest';
1195
		// And now some backwards compatibility stuff for mods and whatnot that aren't expecting the new permissions.
1196
		$user_info['permissions'][] = 'profile_view_own';
1197
		if (in_array('profile_view', $user_info['permissions']))
1198
			$user_info['permissions'][] = 'profile_view_any';
1199
	}
1200
}
1201
1202
/**
1203
 * Loads an array of users' data by ID or member_name.
1204
 *
1205
 * @param array|string $users An array of users by id or name or a single username/id
1206
 * @param bool $is_name Whether $users contains names
1207
 * @param string $set What kind of data to load (normal, profile, minimal)
1208
 * @return array The ids of the members loaded
1209
 */
1210
function loadMemberData($users, $is_name = false, $set = 'normal')
1211
{
1212
	global $user_profile, $modSettings, $board_info, $smcFunc, $context;
1213
	global $image_proxy_enabled, $user_info;
1214
1215
	// Can't just look for no users :P.
1216
	if (empty($users))
1217
		return array();
1218
1219
	// Pass the set value
1220
	$context['loadMemberContext_set'] = $set;
1221
1222
	// Make sure it's an array.
1223
	$users = !is_array($users) ? array($users) : array_unique($users);
1224
	$loaded_ids = array();
1225
1226
	if (!$is_name && !empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= 3)
1227
	{
1228
		$users = array_values($users);
1229
		for ($i = 0, $n = count($users); $i < $n; $i++)
1230
		{
1231
			$data = cache_get_data('member_data-' . $set . '-' . $users[$i], 240);
1232
			if ($data == null)
1233
				continue;
1234
1235
			$loaded_ids[] = $data['id_member'];
1236
			$user_profile[$data['id_member']] = $data;
1237
			unset($users[$i]);
1238
		}
1239
	}
1240
1241
	// Used by default
1242
	$select_columns = '
1243
			COALESCE(lo.log_time, 0) AS is_online, COALESCE(a.id_attach, 0) AS id_attach, a.filename, a.attachment_type,
1244
			mem.signature, mem.personal_text, mem.avatar, mem.id_member, mem.member_name,
1245
			mem.real_name, mem.email_address, mem.date_registered, mem.website_title, mem.website_url,
1246
			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,
1247
			mg.online_color AS member_group_color, COALESCE(mg.group_name, {string:blank_string}) AS member_group,
1248
			pg.online_color AS post_group_color, COALESCE(pg.group_name, {string:blank_string}) AS post_group,
1249
			mem.is_activated, mem.warning, ' . (!empty($modSettings['titlesEnable']) ? 'mem.usertitle, ' : '') . '
1250
			CASE WHEN mem.id_group = 0 OR mg.icons = {string:blank_string} THEN pg.icons ELSE mg.icons END AS icons';
1251
	$select_tables = '
1252
			LEFT JOIN {db_prefix}log_online AS lo ON (lo.id_member = mem.id_member)
1253
			LEFT JOIN {db_prefix}attachments AS a ON (a.id_member = mem.id_member)
1254
			LEFT JOIN {db_prefix}membergroups AS pg ON (pg.id_group = mem.id_post_group)
1255
			LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = mem.id_group)';
1256
1257
	// We add or replace according the the set
1258
	switch ($set)
1259
	{
1260
		case 'normal':
1261
			$select_columns .= ', mem.buddy_list,  mem.additional_groups';
1262
			break;
1263
		case 'profile':
1264
			$select_columns .= ', mem.additional_groups, mem.id_theme, mem.pm_ignore_list, mem.pm_receive_from,
1265
			mem.time_format, mem.timezone, mem.secret_question, mem.smiley_set, mem.tfa_secret,
1266
			mem.total_time_logged_in, lo.url, mem.ignore_boards, mem.password_salt, mem.pm_prefs, mem.buddy_list, mem.alerts';
1267
			break;
1268
		case 'minimal':
1269
			$select_columns = '
1270
			mem.id_member, mem.member_name, mem.real_name, mem.email_address, mem.date_registered,
1271
			mem.posts, mem.last_login, mem.member_ip, mem.member_ip2, mem.lngfile, mem.id_group';
1272
			$select_tables = '';
1273
			break;
1274
		default:
1275
			trigger_error('loadMemberData(): Invalid member data set \'' . $set . '\'', E_USER_WARNING);
1276
	}
1277
1278
	// Allow mods to easily add to the selected member data
1279
	call_integration_hook('integrate_load_member_data', array(&$select_columns, &$select_tables, &$set));
1280
1281
	if (!empty($users))
1282
	{
1283
		// Load the member's data.
1284
		$request = $smcFunc['db_query']('', '
1285
			SELECT' . $select_columns . '
1286
			FROM {db_prefix}members AS mem' . $select_tables . '
1287
			WHERE mem.' . ($is_name ? 'member_name' : 'id_member') . ' IN ({' . ($is_name ? 'array_string' : 'array_int') . ':users})',
1288
			array(
1289
				'blank_string' => '',
1290
				'users' => $users,
1291
			)
1292
		);
1293
		$new_loaded_ids = array();
1294
		while ($row = $smcFunc['db_fetch_assoc']($request))
1295
		{
1296
			// If the image proxy is enabled, we still want the original URL when they're editing the profile...
1297
			$row['avatar_original'] = !empty($row['avatar']) ? $row['avatar'] : '';
1298
1299
			// Take care of proxying avatar if required, do this here for maximum reach
1300
			if ($image_proxy_enabled && !empty($row['avatar']) && stripos($row['avatar'], 'http://') !== false && empty($user_info['possibly_robot']))
1301
				$row['avatar'] = get_proxied_url($row['avatar']);
1302
1303
			// Keep track of the member's normal member group
1304
			$row['primary_group'] = !empty($row['member_group']) ? $row['member_group'] : '';
1305
1306
			if (isset($row['member_ip']))
1307
				$row['member_ip'] = inet_dtop($row['member_ip']);
1308
			if (isset($row['member_ip2']))
1309
				$row['member_ip2'] = inet_dtop($row['member_ip2']);
1310
			$row['id_member'] = (int) $row['id_member'];
1311
			$new_loaded_ids[] = $row['id_member'];
1312
			$loaded_ids[] = $row['id_member'];
1313
			$row['options'] = array();
1314
			$user_profile[$row['id_member']] = $row;
1315
		}
1316
		$smcFunc['db_free_result']($request);
1317
	}
1318
1319
	if (!empty($new_loaded_ids) && $set !== 'minimal')
1320
	{
1321
		$request = $smcFunc['db_query']('', '
1322
			SELECT id_member, variable, value
1323
			FROM {db_prefix}themes
1324
			WHERE id_member IN ({array_int:loaded_ids})',
1325
			array(
1326
				'loaded_ids' => $new_loaded_ids,
1327
			)
1328
		);
1329
		while ($row = $smcFunc['db_fetch_assoc']($request))
1330
			$user_profile[$row['id_member']]['options'][$row['variable']] = $row['value'];
1331
		$smcFunc['db_free_result']($request);
1332
	}
1333
1334
	$additional_mods = array();
1335
1336
	// Are any of these users in groups assigned to moderate this board?
1337
	if (!empty($loaded_ids) && !empty($board_info['moderator_groups']) && $set === 'normal')
1338
	{
1339
		foreach ($loaded_ids as $a_member)
1340
		{
1341
			if (!empty($user_profile[$a_member]['additional_groups']))
1342
				$groups = array_merge(array($user_profile[$a_member]['id_group']), explode(',', $user_profile[$a_member]['additional_groups']));
1343
			else
1344
				$groups = array($user_profile[$a_member]['id_group']);
1345
1346
			$temp = array_intersect($groups, array_keys($board_info['moderator_groups']));
1347
1348
			if (!empty($temp))
1349
			{
1350
				$additional_mods[] = $a_member;
1351
			}
1352
		}
1353
	}
1354
1355
	if (!empty($new_loaded_ids) && !empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= 3)
1356
	{
1357
		for ($i = 0, $n = count($new_loaded_ids); $i < $n; $i++)
1358
			cache_put_data('member_data-' . $set . '-' . $new_loaded_ids[$i], $user_profile[$new_loaded_ids[$i]], 240);
1359
	}
1360
1361
	// Are we loading any moderators?  If so, fix their group data...
1362
	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)
1363
	{
1364
		if (($row = cache_get_data('moderator_group_info', 480)) == null)
1365
		{
1366
			$request = $smcFunc['db_query']('', '
1367
				SELECT group_name AS member_group, online_color AS member_group_color, icons
1368
				FROM {db_prefix}membergroups
1369
				WHERE id_group = {int:moderator_group}
1370
				LIMIT 1',
1371
				array(
1372
					'moderator_group' => 3,
1373
				)
1374
			);
1375
			$row = $smcFunc['db_fetch_assoc']($request);
1376
			$smcFunc['db_free_result']($request);
1377
1378
			cache_put_data('moderator_group_info', $row, 480);
1379
		}
1380
1381
		foreach ($temp_mods as $id)
1382
		{
1383
			// By popular demand, don't show admins or global moderators as moderators.
1384
			if ($user_profile[$id]['id_group'] != 1 && $user_profile[$id]['id_group'] != 2)
1385
				$user_profile[$id]['member_group'] = $row['member_group'];
1386
1387
			// If the Moderator group has no color or icons, but their group does... don't overwrite.
1388
			if (!empty($row['icons']))
1389
				$user_profile[$id]['icons'] = $row['icons'];
1390
			if (!empty($row['member_group_color']))
1391
				$user_profile[$id]['member_group_color'] = $row['member_group_color'];
1392
		}
1393
	}
1394
1395
	return $loaded_ids;
1396
}
1397
1398
/**
1399
 * Loads the user's basic values... meant for template/theme usage.
1400
 *
1401
 * @param int $user The ID of a user previously loaded by {@link loadMemberData()}
1402
 * @param bool $display_custom_fields Whether or not to display custom profile fields
1403
 * @return boolean Whether or not the data was loaded successfully
1404
 */
1405
function loadMemberContext($user, $display_custom_fields = false)
1406
{
1407
	global $memberContext, $user_profile, $txt, $scripturl, $user_info;
1408
	global $context, $modSettings, $settings, $smcFunc;
1409
	static $dataLoaded = array();
1410
	static $loadedLanguages = array();
1411
1412
	// If this person's data is already loaded, skip it.
1413
	if (isset($dataLoaded[$user]))
1414
		return true;
1415
1416
	// We can't load guests or members not loaded by loadMemberData()!
1417
	if ($user == 0)
1418
		return false;
1419
	if (!isset($user_profile[$user]))
1420
	{
1421
		trigger_error('loadMemberContext(): member id ' . $user . ' not previously loaded by loadMemberData()', E_USER_WARNING);
1422
		return false;
1423
	}
1424
1425
	// Well, it's loaded now anyhow.
1426
	$dataLoaded[$user] = true;
1427
	$profile = $user_profile[$user];
1428
1429
	// Censor everything.
1430
	censorText($profile['signature']);
1431
	censorText($profile['personal_text']);
1432
1433
	// Set things up to be used before hand.
1434
	$profile['signature'] = str_replace(array("\n", "\r"), array('<br>', ''), $profile['signature']);
1435
	$profile['signature'] = parse_bbc($profile['signature'], true, 'sig' . $profile['id_member']);
1436
1437
	$profile['is_online'] = (!empty($profile['show_online']) || allowedTo('moderate_forum')) && $profile['is_online'] > 0;
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: $profile['is_online'] = ...ofile['is_online'] > 0), Probably Intended Meaning: $profile['is_online'] = ...ofile['is_online'] > 0)
Loading history...
1438
	$profile['icons'] = empty($profile['icons']) ? array('', '') : explode('#', $profile['icons']);
1439
	// Setup the buddy status here (One whole in_array call saved :P)
1440
	$profile['buddy'] = in_array($profile['id_member'], $user_info['buddies']);
1441
	$buddy_list = !empty($profile['buddy_list']) ? explode(',', $profile['buddy_list']) : array();
1442
1443
	//We need a little fallback for the membergroup icons. If it doesn't exist in the current theme, fallback to default theme
1444
	if (isset($profile['icons'][1]) && file_exists($settings['actual_theme_dir'] . '/images/membericons/' . $profile['icons'][1])) //icon is set and exists
1445
		$group_icon_url = $settings['images_url'] . '/membericons/' . $profile['icons'][1];
1446
	elseif (isset($profile['icons'][1])) //icon is set and doesn't exist, fallback to default
1447
		$group_icon_url = $settings['default_images_url'] . '/membericons/' . $profile['icons'][1];
1448
	else //not set, bye bye
1449
		$group_icon_url = '';
1450
1451
	// These minimal values are always loaded
1452
	$memberContext[$user] = array(
1453
		'username' => $profile['member_name'],
1454
		'name' => $profile['real_name'],
1455
		'id' => $profile['id_member'],
1456
		'href' => $scripturl . '?action=profile;u=' . $profile['id_member'],
1457
		'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>',
1458
		'email' => $profile['email_address'],
1459
		'show_email' => !$user_info['is_guest'] && ($user_info['id'] == $profile['id_member'] || allowedTo('moderate_forum')),
1460
		'registered' => empty($profile['date_registered']) ? $txt['not_applicable'] : timeformat($profile['date_registered']),
1461
		'registered_timestamp' => empty($profile['date_registered']) ? 0 : forum_time(true, $profile['date_registered']),
1462
	);
1463
1464
	// If the set isn't minimal then load the monstrous array.
1465
	if ($context['loadMemberContext_set'] != 'minimal')
1466
	{
1467
		// Go the extra mile and load the user's native language name.
1468
		if (empty($loadedLanguages))
1469
			$loadedLanguages = getLanguages();
1470
1471
		$memberContext[$user] += array(
1472
			'username_color' => '<span ' . (!empty($profile['member_group_color']) ? 'style="color:' . $profile['member_group_color'] . ';"' : '') . '>' . $profile['member_name'] . '</span>',
1473
			'name_color' => '<span ' . (!empty($profile['member_group_color']) ? 'style="color:' . $profile['member_group_color'] . ';"' : '') . '>' . $profile['real_name'] . '</span>',
1474
			'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>',
1475
			'is_buddy' => $profile['buddy'],
1476
			'is_reverse_buddy' => in_array($user_info['id'], $buddy_list),
1477
			'buddies' => $buddy_list,
1478
			'title' => !empty($modSettings['titlesEnable']) ? $profile['usertitle'] : '',
1479
			'blurb' => $profile['personal_text'],
1480
			'website' => array(
1481
				'title' => $profile['website_title'],
1482
				'url' => $profile['website_url'],
1483
			),
1484
			'birth_date' => empty($profile['birthdate']) ? '1004-01-01' : (substr($profile['birthdate'], 0, 4) === '0004' ? '1004' . substr($profile['birthdate'], 4) : $profile['birthdate']),
1485
			'signature' => $profile['signature'],
1486
			'real_posts' => $profile['posts'],
1487
			'posts' => $profile['posts'] > 500000 ? $txt['geek'] : comma_format($profile['posts']),
1488
			'last_login' => empty($profile['last_login']) ? $txt['never'] : timeformat($profile['last_login']),
1489
			'last_login_timestamp' => empty($profile['last_login']) ? 0 : forum_time(0, $profile['last_login']),
1490
			'ip' => $smcFunc['htmlspecialchars']($profile['member_ip']),
1491
			'ip2' => $smcFunc['htmlspecialchars']($profile['member_ip2']),
1492
			'online' => array(
1493
				'is_online' => $profile['is_online'],
1494
				'text' => $smcFunc['htmlspecialchars']($txt[$profile['is_online'] ? 'online' : 'offline']),
1495
				'member_online_text' => sprintf($txt[$profile['is_online'] ? 'member_is_online' : 'member_is_offline'], $smcFunc['htmlspecialchars']($profile['real_name'])),
1496
				'href' => $scripturl . '?action=pm;sa=send;u=' . $profile['id_member'],
1497
				'link' => '<a href="' . $scripturl . '?action=pm;sa=send;u=' . $profile['id_member'] . '">' . $txt[$profile['is_online'] ? 'online' : 'offline'] . '</a>',
1498
				'label' => $txt[$profile['is_online'] ? 'online' : 'offline']
1499
			),
1500
			'language' => !empty($loadedLanguages[$profile['lngfile']]) && !empty($loadedLanguages[$profile['lngfile']]['name']) ? $loadedLanguages[$profile['lngfile']]['name'] : $smcFunc['ucwords'](strtr($profile['lngfile'], array('_' => ' ', '-utf8' => ''))),
1501
			'is_activated' => isset($profile['is_activated']) ? $profile['is_activated'] : 1,
1502
			'is_banned' => isset($profile['is_activated']) ? $profile['is_activated'] >= 10 : 0,
1503
			'options' => $profile['options'],
1504
			'is_guest' => false,
1505
			'primary_group' => $profile['primary_group'],
1506
			'group' => $profile['member_group'],
1507
			'group_color' => $profile['member_group_color'],
1508
			'group_id' => $profile['id_group'],
1509
			'post_group' => $profile['post_group'],
1510
			'post_group_color' => $profile['post_group_color'],
1511
			'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]),
1512
			'warning' => $profile['warning'],
1513
			'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' : (''))),
1514
			'local_time' => timeformat(time() + ($profile['time_offset'] - $user_info['time_offset']) * 3600, false),
1515
			'custom_fields' => array(),
1516
		);
1517
	}
1518
1519
	// If the set isn't minimal then load their avatar as well.
1520
	if ($context['loadMemberContext_set'] != 'minimal')
1521
	{
1522
		if (!empty($modSettings['gravatarOverride']) || (!empty($modSettings['gravatarEnabled']) && stristr($profile['avatar'], 'gravatar://')))
1523
		{
1524
			if (!empty($modSettings['gravatarAllowExtraEmail']) && stristr($profile['avatar'], 'gravatar://') && strlen($profile['avatar']) > 11)
1525
				$image = get_gravatar_url($smcFunc['substr']($profile['avatar'], 11));
1526
			else
1527
				$image = get_gravatar_url($profile['email_address']);
1528
		}
1529
		else
1530
		{
1531
			// So it's stored in the member table?
1532
			if (!empty($profile['avatar']))
1533
			{
1534
				$image = (stristr($profile['avatar'], 'http://') || stristr($profile['avatar'], 'https://')) ? $profile['avatar'] : $modSettings['avatar_url'] . '/' . $profile['avatar'];
1535
			}
1536
			elseif (!empty($profile['filename']))
1537
				$image = $modSettings['custom_avatar_url'] . '/' . $profile['filename'];
1538
			// Right... no avatar...use the default one
1539
			else
1540
				$image = $modSettings['avatar_url'] . '/default.png';
1541
		}
1542
		if (!empty($image))
1543
			$memberContext[$user]['avatar'] = array(
1544
				'name' => $profile['avatar'],
1545
				'image' => '<img class="avatar" src="' . $image . '" alt="avatar_' . $profile['member_name'] . '">',
1546
				'href' => $image,
1547
				'url' => $image,
1548
			);
1549
	}
1550
1551
	// Are we also loading the members custom fields into context?
1552
	if ($display_custom_fields && !empty($modSettings['displayFields']))
1553
	{
1554
		$memberContext[$user]['custom_fields'] = array();
1555
1556
		if (!isset($context['display_fields']))
1557
			$context['display_fields'] = $smcFunc['json_decode']($modSettings['displayFields'], true);
1558
1559
		foreach ($context['display_fields'] as $custom)
1560
		{
1561
			if (!isset($custom['col_name']) || trim($custom['col_name']) == '' || empty($profile['options'][$custom['col_name']]))
1562
				continue;
1563
1564
			$value = $profile['options'][$custom['col_name']];
1565
1566
			$fieldOptions = array();
1567
			$currentKey = 0;
1568
1569
			// Create a key => value array for multiple options fields
1570
			if (!empty($custom['options']))
1571
				foreach ($custom['options'] as $k => $v)
1572
				{
1573
					$fieldOptions[] = $v;
1574
					if (empty($currentKey))
1575
						$currentKey = $v == $value ? $k : 0;
1576
				}
1577
1578
			// BBC?
1579
			if ($custom['bbc'])
1580
				$value = parse_bbc($value);
1581
1582
			// ... or checkbox?
1583
			elseif (isset($custom['type']) && $custom['type'] == 'check')
1584
				$value = $value ? $txt['yes'] : $txt['no'];
1585
1586
			// Enclosing the user input within some other text?
1587
			if (!empty($custom['enclose']))
1588
				$value = strtr($custom['enclose'], array(
1589
					'{SCRIPTURL}' => $scripturl,
1590
					'{IMAGES_URL}' => $settings['images_url'],
1591
					'{DEFAULT_IMAGES_URL}' => $settings['default_images_url'],
1592
					'{INPUT}' => $value,
1593
					'{KEY}' => $currentKey,
1594
				));
1595
1596
			$memberContext[$user]['custom_fields'][] = array(
1597
				'title' => !empty($custom['title']) ? $custom['title'] : $custom['col_name'],
1598
				'col_name' => $custom['col_name'],
1599
				'value' => un_htmlspecialchars($value),
1600
				'placement' => !empty($custom['placement']) ? $custom['placement'] : 0,
1601
			);
1602
		}
1603
	}
1604
1605
	call_integration_hook('integrate_member_context', array(&$memberContext[$user], $user, $display_custom_fields));
1606
	return true;
1607
}
1608
1609
/**
1610
 * Loads the user's custom profile fields
1611
 *
1612
 * @param integer|array $users A single user ID or an array of user IDs
1613
 * @param string|array $params Either a string or an array of strings with profile field names
1614
 * @return array|boolean An array of data about the fields and their values or false if nothing was loaded
1615
 */
1616
function loadMemberCustomFields($users, $params)
1617
{
1618
	global $smcFunc, $txt, $scripturl, $settings;
1619
1620
	// Do not waste my time...
1621
	if (empty($users) || empty($params))
1622
		return false;
1623
1624
	// Make sure it's an array.
1625
	$users = !is_array($users) ? array($users) : array_unique($users);
1626
	$params = !is_array($params) ? array($params) : array_unique($params);
1627
	$return = array();
1628
1629
	$request = $smcFunc['db_query']('', '
1630
		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,
1631
		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
1632
		FROM {db_prefix}themes AS t
1633
			LEFT JOIN {db_prefix}custom_fields AS c ON (c.col_name = t.variable)
1634
		WHERE id_member IN ({array_int:loaded_ids})
1635
			AND variable IN ({array_string:params})
1636
		ORDER BY field_order',
1637
		array(
1638
			'loaded_ids' => $users,
1639
			'params' => $params,
1640
		)
1641
	);
1642
1643
	while ($row = $smcFunc['db_fetch_assoc']($request))
1644
	{
1645
		$fieldOptions = array();
1646
		$currentKey = 0;
1647
1648
		// Create a key => value array for multiple options fields
1649
		if (!empty($row['field_options']))
1650
			foreach (explode(',', $row['field_options']) as $k => $v)
1651
			{
1652
				$fieldOptions[] = $v;
1653
				if (empty($currentKey))
1654
					$currentKey = $v == $row['value'] ? $k : 0;
1655
			}
1656
1657
		// BBC?
1658
		if (!empty($row['bbc']))
1659
			$row['value'] = parse_bbc($row['value']);
1660
1661
		// ... or checkbox?
1662
		elseif (isset($row['type']) && $row['type'] == 'check')
1663
			$row['value'] = !empty($row['value']) ? $txt['yes'] : $txt['no'];
1664
1665
		// Enclosing the user input within some other text?
1666
		if (!empty($row['enclose']))
1667
			$row['value'] = strtr($row['enclose'], array(
1668
				'{SCRIPTURL}' => $scripturl,
1669
				'{IMAGES_URL}' => $settings['images_url'],
1670
				'{DEFAULT_IMAGES_URL}' => $settings['default_images_url'],
1671
				'{INPUT}' => un_htmlspecialchars($row['value']),
1672
				'{KEY}' => $currentKey,
1673
			));
1674
1675
		// Send a simple array if there is just 1 param
1676
		if (count($params) == 1)
1677
			$return[$row['id_member']] = $row;
1678
1679
		// More than 1? knock yourself out...
1680
		else
1681
		{
1682
			if (!isset($return[$row['id_member']]))
1683
				$return[$row['id_member']] = array();
1684
1685
			$return[$row['id_member']][$row['variable']] = $row;
1686
		}
1687
	}
1688
1689
	$smcFunc['db_free_result']($request);
1690
1691
	return !empty($return) ? $return : false;
1692
}
1693
1694
/**
1695
 * Loads information about what browser the user is viewing with and places it in $context
1696
 *  - uses the class from {@link Class-BrowserDetect.php}
1697
 */
1698
function detectBrowser()
1699
{
1700
	// Load the current user's browser of choice
1701
	$detector = new browser_detector;
1702
	$detector->detectBrowser();
1703
}
1704
1705
/**
1706
 * Are we using this browser?
1707
 *
1708
 * Wrapper function for detectBrowser
1709
 * @param string $browser The browser we are checking for.
1710
 * @return bool Whether or not the current browser is what we're looking for
1711
*/
1712
function isBrowser($browser)
1713
{
1714
	global $context;
1715
1716
	// Don't know any browser!
1717
	if (empty($context['browser']))
1718
		detectBrowser();
1719
1720
	return !empty($context['browser'][$browser]) || !empty($context['browser']['is_' . $browser]) ? true : false;
1721
}
1722
1723
/**
1724
 * Load a theme, by ID.
1725
 *
1726
 * @param int $id_theme The ID of the theme to load
1727
 * @param bool $initialize Whether or not to initialize a bunch of theme-related variables/settings
1728
 */
1729
function loadTheme($id_theme = 0, $initialize = true)
1730
{
1731
	global $user_info, $user_settings, $board_info, $boarddir, $maintenance;
1732
	global $txt, $boardurl, $scripturl, $mbname, $modSettings;
1733
	global $context, $settings, $options, $sourcedir, $ssi_theme, $smcFunc, $language, $board, $image_proxy_enabled;
1734
1735
	// The theme was specified by parameter.
1736
	if (!empty($id_theme))
1737
		$id_theme = (int) $id_theme;
1738
	// The theme was specified by REQUEST.
1739
	elseif (!empty($_REQUEST['theme']) && (!empty($modSettings['theme_allow']) || allowedTo('admin_forum')))
1740
	{
1741
		$id_theme = (int) $_REQUEST['theme'];
1742
		$_SESSION['id_theme'] = $id_theme;
1743
	}
1744
	// The theme was specified by REQUEST... previously.
1745
	elseif (!empty($_SESSION['id_theme']) && (!empty($modSettings['theme_allow']) || allowedTo('admin_forum')))
1746
		$id_theme = (int) $_SESSION['id_theme'];
1747
	// The theme is just the user's choice. (might use ?board=1;theme=0 to force board theme.)
1748
	elseif (!empty($user_info['theme']) && !isset($_REQUEST['theme']))
1749
		$id_theme = $user_info['theme'];
1750
	// The theme was specified by the board.
1751
	elseif (!empty($board_info['theme']))
1752
		$id_theme = $board_info['theme'];
1753
	// The theme is the forum's default.
1754
	else
1755
		$id_theme = $modSettings['theme_guests'];
1756
1757
	// Verify the id_theme... no foul play.
1758
	// Always allow the board specific theme, if they are overriding.
1759
	if (!empty($board_info['theme']) && $board_info['override_theme'])
1760
		$id_theme = $board_info['theme'];
1761
	// If they have specified a particular theme to use with SSI allow it to be used.
1762
	elseif (!empty($ssi_theme) && $id_theme == $ssi_theme)
1763
		$id_theme = (int) $id_theme;
1764
	elseif (!empty($modSettings['enableThemes']) && !allowedTo('admin_forum'))
1765
	{
1766
		$themes = explode(',', $modSettings['enableThemes']);
1767
		if (!in_array($id_theme, $themes))
1768
			$id_theme = $modSettings['theme_guests'];
1769
		else
1770
			$id_theme = (int) $id_theme;
1771
	}
1772
		
1773
	// Allow mod authors the option to override the theme id for custom page themes
1774
	call_integration_hook('integrate_pre_load_theme', array(&$id_theme));
1775
1776
	// We already load the basic stuff?
1777
	if (empty($settings['theme_id']) || $settings['theme_id'] != $id_theme )
1778
	{
1779
		$member = empty($user_info['id']) ? -1 : $user_info['id'];
1780
1781
		// Disable image proxy if we don't have SSL enabled
1782
		if (empty($modSettings['force_ssl']))
1783
			$image_proxy_enabled = false;
1784
1785
		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'])
1786
		{
1787
			$themeData = $temp;
1788
			$flag = true;
1789
		}
1790
		elseif (($temp = cache_get_data('theme_settings-' . $id_theme, 90)) != null && time() - 60 > $modSettings['settings_updated'])
1791
			$themeData = $temp + array($member => array());
1792
		else
1793
			$themeData = array(-1 => array(), 0 => array(), $member => array());
1794
1795
		if (empty($flag))
1796
		{
1797
			// Load variables from the current or default theme, global or this user's.
1798
			$result = $smcFunc['db_query']('', '
1799
				SELECT variable, value, id_member, id_theme
1800
				FROM {db_prefix}themes
1801
				WHERE id_member' . (empty($themeData[0]) ? ' IN (-1, 0, {int:id_member})' : ' = {int:id_member}') . '
1802
					AND id_theme' . ($id_theme == 1 ? ' = {int:id_theme}' : ' IN ({int:id_theme}, 1)') .'
1803
				ORDER BY id_theme asc',
1804
				array(
1805
					'id_theme' => $id_theme,
1806
					'id_member' => $member,
1807
				)
1808
			);
1809
			// Pick between $settings and $options depending on whose data it is.
1810
			while ($row = $smcFunc['db_fetch_assoc']($result))
1811
			{
1812
				// There are just things we shouldn't be able to change as members.
1813
				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')))
1814
					continue;
1815
1816
				// If this is the theme_dir of the default theme, store it.
1817
				if (in_array($row['variable'], array('theme_dir', 'theme_url', 'images_url')) && $row['id_theme'] == '1' && empty($row['id_member']))
1818
					$themeData[0]['default_' . $row['variable']] = $row['value'];
1819
1820
				// If this isn't set yet, is a theme option, or is not the default theme..
1821
				if (!isset($themeData[$row['id_member']][$row['variable']]) || $row['id_theme'] != '1')
1822
					$themeData[$row['id_member']][$row['variable']] = substr($row['variable'], 0, 5) == 'show_' ? $row['value'] == '1' : $row['value'];
1823
			}
1824
			$smcFunc['db_free_result']($result);
1825
1826
			if (!empty($themeData[-1]))
1827
				foreach ($themeData[-1] as $k => $v)
1828
				{
1829
					if (!isset($themeData[$member][$k]))
1830
						$themeData[$member][$k] = $v;
1831
				}
1832
1833
			if (!empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= 2)
1834
				cache_put_data('theme_settings-' . $id_theme . ':' . $member, $themeData, 60);
1835
			// Only if we didn't already load that part of the cache...
1836
			elseif (!isset($temp))
1837
				cache_put_data('theme_settings-' . $id_theme, array(-1 => $themeData[-1], 0 => $themeData[0]), 90);
1838
		}
1839
1840
		$settings = $themeData[0];
1841
		$options = $themeData[$member];
1842
1843
		$settings['theme_id'] = $id_theme;
1844
1845
		$settings['actual_theme_url'] = $settings['theme_url'];
1846
		$settings['actual_images_url'] = $settings['images_url'];
1847
		$settings['actual_theme_dir'] = $settings['theme_dir'];
1848
1849
		$settings['template_dirs'] = array();
1850
		// This theme first.
1851
		$settings['template_dirs'][] = $settings['theme_dir'];
1852
1853
		// Based on theme (if there is one).
1854
		if (!empty($settings['base_theme_dir']))
1855
			$settings['template_dirs'][] = $settings['base_theme_dir'];
1856
1857
		// Lastly the default theme.
1858
		if ($settings['theme_dir'] != $settings['default_theme_dir'])
1859
			$settings['template_dirs'][] = $settings['default_theme_dir'];
1860
	}
1861
1862
1863
	if (!$initialize)
1864
		return;
1865
1866
	// Check to see if we're forcing SSL
1867
	if (!empty($modSettings['force_ssl']) && empty($maintenance) &&
1868
		!httpsOn() && SMF != 'SSI')
0 ignored issues
show
introduced by
The condition SMF != 'SSI' is always false.
Loading history...
1869
	{
1870
		if (isset($_GET['sslRedirect']))
1871
		{
1872
			loadLanguage('Errors');
1873
			fatal_lang_error($txt['login_ssl_required']);
1874
		}
1875
1876
		redirectexit(strtr($_SERVER['REQUEST_URL'], array('http://' => 'https://')) . (strpos($_SERVER['REQUEST_URL'], '?') > 0 ? ';' : '?') . 'sslRedirect');
1877
	}
1878
1879
	// Check to see if they're accessing it from the wrong place.
1880
	if (isset($_SERVER['HTTP_HOST']) || isset($_SERVER['SERVER_NAME']))
1881
	{
1882
		$detected_url = httpsOn() ? 'https://' : 'http://';
1883
		$detected_url .= empty($_SERVER['HTTP_HOST']) ? $_SERVER['SERVER_NAME'] . (empty($_SERVER['SERVER_PORT']) || $_SERVER['SERVER_PORT'] == '80' ? '' : ':' . $_SERVER['SERVER_PORT']) : $_SERVER['HTTP_HOST'];
1884
		$temp = preg_replace('~/' . basename($scripturl) . '(/.+)?$~', '', strtr(dirname($_SERVER['PHP_SELF']), '\\', '/'));
1885
		if ($temp != '/')
1886
			$detected_url .= $temp;
1887
	}
1888
	if (isset($detected_url) && $detected_url != $boardurl)
1889
	{
1890
		// Try #1 - check if it's in a list of alias addresses.
1891
		if (!empty($modSettings['forum_alias_urls']))
1892
		{
1893
			$aliases = explode(',', $modSettings['forum_alias_urls']);
1894
1895
			foreach ($aliases as $alias)
1896
			{
1897
				// Rip off all the boring parts, spaces, etc.
1898
				if ($detected_url == trim($alias) || strtr($detected_url, array('http://' => '', 'https://' => '')) == trim($alias))
1899
					$do_fix = true;
1900
			}
1901
		}
1902
1903
		// Hmm... check #2 - is it just different by a www?  Send them to the correct place!!
1904
		if (empty($do_fix) && strtr($detected_url, array('://' => '://www.')) == $boardurl && (empty($_GET) || count($_GET) == 1) && SMF != 'SSI')
0 ignored issues
show
introduced by
The condition SMF != 'SSI' is always false.
Loading history...
1905
		{
1906
			// Okay, this seems weird, but we don't want an endless loop - this will make $_GET not empty ;).
1907
			if (empty($_GET))
1908
				redirectexit('wwwRedirect');
1909
			else
1910
			{
1911
				$k = key($_GET);
1912
				$v = current($_GET);
1913
1914
				if ($k != 'wwwRedirect')
1915
					redirectexit('wwwRedirect;' . $k . '=' . $v);
1916
			}
1917
		}
1918
1919
		// #3 is just a check for SSL...
1920
		if (strtr($detected_url, array('https://' => 'http://')) == $boardurl)
1921
			$do_fix = true;
1922
1923
		// Okay, #4 - perhaps it's an IP address?  We're gonna want to use that one, then. (assuming it's the IP or something...)
1924
		if (!empty($do_fix) || preg_match('~^http[s]?://(?:[\d\.:]+|\[[\d:]+\](?::\d+)?)(?:$|/)~', $detected_url) == 1)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $detected_url does not seem to be defined for all execution paths leading up to this point.
Loading history...
1925
		{
1926
			// Caching is good ;).
1927
			$oldurl = $boardurl;
1928
1929
			// Fix $boardurl and $scripturl.
1930
			$boardurl = $detected_url;
1931
			$scripturl = strtr($scripturl, array($oldurl => $boardurl));
1932
			$_SERVER['REQUEST_URL'] = strtr($_SERVER['REQUEST_URL'], array($oldurl => $boardurl));
1933
1934
			// Fix the theme urls...
1935
			$settings['theme_url'] = strtr($settings['theme_url'], array($oldurl => $boardurl));
1936
			$settings['default_theme_url'] = strtr($settings['default_theme_url'], array($oldurl => $boardurl));
1937
			$settings['actual_theme_url'] = strtr($settings['actual_theme_url'], array($oldurl => $boardurl));
1938
			$settings['images_url'] = strtr($settings['images_url'], array($oldurl => $boardurl));
1939
			$settings['default_images_url'] = strtr($settings['default_images_url'], array($oldurl => $boardurl));
1940
			$settings['actual_images_url'] = strtr($settings['actual_images_url'], array($oldurl => $boardurl));
1941
1942
			// And just a few mod settings :).
1943
			$modSettings['smileys_url'] = strtr($modSettings['smileys_url'], array($oldurl => $boardurl));
1944
			$modSettings['avatar_url'] = strtr($modSettings['avatar_url'], array($oldurl => $boardurl));
1945
			$modSettings['custom_avatar_url'] = strtr($modSettings['custom_avatar_url'], array($oldurl => $boardurl));
1946
1947
			// Clean up after loadBoard().
1948
			if (isset($board_info['moderators']))
1949
			{
1950
				foreach ($board_info['moderators'] as $k => $dummy)
1951
				{
1952
					$board_info['moderators'][$k]['href'] = strtr($dummy['href'], array($oldurl => $boardurl));
1953
					$board_info['moderators'][$k]['link'] = strtr($dummy['link'], array('"' . $oldurl => '"' . $boardurl));
1954
				}
1955
			}
1956
			foreach ($context['linktree'] as $k => $dummy)
1957
				$context['linktree'][$k]['url'] = strtr($dummy['url'], array($oldurl => $boardurl));
1958
		}
1959
	}
1960
	// Set up the contextual user array.
1961
	if (!empty($user_info))
1962
	{
1963
		$context['user'] = array(
1964
			'id' => $user_info['id'],
1965
			'is_logged' => !$user_info['is_guest'],
1966
			'is_guest' => &$user_info['is_guest'],
1967
			'is_admin' => &$user_info['is_admin'],
1968
			'is_mod' => &$user_info['is_mod'],
1969
			// A user can mod if they have permission to see the mod center, or they are a board/group/approval moderator.
1970
			'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'])))),
1971
			'name' => $user_info['username'],
1972
			'language' => $user_info['language'],
1973
			'email' => $user_info['email'],
1974
			'ignoreusers' => $user_info['ignoreusers'],
1975
		);
1976
		if (!$context['user']['is_guest'])
1977
			$context['user']['name'] = $user_info['name'];
1978
		elseif ($context['user']['is_guest'] && !empty($txt['guest_title']))
1979
			$context['user']['name'] = $txt['guest_title'];
1980
1981
		// Determine the current smiley set.
1982
		$smiley_sets_known = explode(',', $modSettings['smiley_sets_known']);
1983
		$user_info['smiley_set'] = (!in_array($user_info['smiley_set'], $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'];
1984
		$context['user']['smiley_set'] = $user_info['smiley_set'];
1985
1986
		// Determine current smiley set extension
1987
		$smiley_sets_exts = explode(',', $modSettings['smiley_sets_exts']);
1988
		$user_info['smiley_set_ext'] = $smiley_sets_exts[array_search($user_info['smiley_set'], $smiley_sets_known)];
1989
		$context['user']['smiley_set_ext'] = $user_info['smiley_set_ext'];
1990
1991
		// Determine global default smiley set extension
1992
		$context['user']['smiley_set_default_ext'] = $smiley_sets_exts[array_search($modSettings['smiley_sets_default'], $smiley_sets_known)];
1993
	}
1994
	else
1995
	{
1996
		// What to do when there is no $user_info (e.g., an error very early in the login process)
1997
		$context['user'] = array(
1998
			'id' => -1,
1999
			'is_logged' => false,
2000
			'is_guest' => true,
2001
			'is_mod' => false,
2002
			'can_mod' => false,
2003
			'name' => $txt['guest_title'],
2004
			'language' => $language,
2005
			'email' => '',
2006
			'ignoreusers' => array(),
2007
		);
2008
		// Note we should stuff $user_info with some guest values also...
2009
		$user_info = array(
2010
			'id' => 0,
2011
			'is_guest' => true,
2012
			'is_admin' => false,
2013
			'is_mod' => false,
2014
			'username' => $txt['guest_title'],
2015
			'language' => $language,
2016
			'email' => '',
2017
			'smiley_set' => '',
2018
			'permissions' => array(),
2019
			'groups' => array(),
2020
			'ignoreusers' => array(),
2021
			'possibly_robot' => true,
2022
			'time_offset' => 0,
2023
			'time_format' => $modSettings['time_format'],
2024
		);
2025
	}
2026
2027
	// Some basic information...
2028
	if (!isset($context['html_headers']))
2029
		$context['html_headers'] = '';
2030
	if (!isset($context['javascript_files']))
2031
		$context['javascript_files'] = array();
2032
	if (!isset($context['css_files']))
2033
		$context['css_files'] = array();
2034
	if (!isset($context['css_header']))
2035
		$context['css_header'] = array();
2036
	if (!isset($context['javascript_inline']))
2037
		$context['javascript_inline'] = array('standard' => array(), 'defer' => array());
2038
	if (!isset($context['javascript_vars']))
2039
		$context['javascript_vars'] = array();
2040
2041
	$context['login_url'] =  $scripturl . '?action=login2';
2042
	$context['menu_separator'] = !empty($settings['use_image_buttons']) ? ' ' : ' | ';
2043
	$context['session_var'] = $_SESSION['session_var'];
2044
	$context['session_id'] = $_SESSION['session_value'];
2045
	$context['forum_name'] = $mbname;
2046
	$context['forum_name_html_safe'] = $smcFunc['htmlspecialchars']($context['forum_name']);
2047
	$context['header_logo_url_html_safe'] = empty($settings['header_logo_url']) ? '' : $smcFunc['htmlspecialchars']($settings['header_logo_url']);
2048
	$context['current_action'] = isset($_REQUEST['action']) ? $smcFunc['htmlspecialchars']($_REQUEST['action']) : null;
2049
	$context['current_subaction'] = isset($_REQUEST['sa']) ? $_REQUEST['sa'] : null;
2050
	$context['can_register'] = empty($modSettings['registration_method']) || $modSettings['registration_method'] != 3;
2051
	if (isset($modSettings['load_average']))
2052
		$context['load_average'] = $modSettings['load_average'];
2053
2054
	// Detect the browser. This is separated out because it's also used in attachment downloads
2055
	detectBrowser();
2056
2057
	// Set the top level linktree up.
2058
	// Note that if we're dealing with certain very early errors (e.g., login) the linktree might not be set yet...
2059
	if (empty($context['linktree']))
2060
		$context['linktree'] = array();
2061
	array_unshift($context['linktree'], array(
2062
		'url' => $scripturl,
2063
		'name' => $context['forum_name_html_safe']
2064
	));
2065
2066
	// This allows sticking some HTML on the page output - useful for controls.
2067
	$context['insert_after_template'] = '';
2068
2069
	if (!isset($txt))
2070
		$txt = array();
2071
2072
	$simpleActions = array(
2073
		'findmember',
2074
		'helpadmin',
2075
		'printpage',
2076
		'spellcheck',
2077
	);
2078
2079
	// Parent action => array of areas
2080
	$simpleAreas = array(
2081
		'profile' => array('popup', 'alerts_popup',),
2082
	);
2083
2084
	// Parent action => array of subactions
2085
	$simpleSubActions = array(
2086
		'pm' => array('popup',),
2087
		'signup' => array('usernamecheck'),
2088
	);
2089
2090
	// Extra params like ;preview ;js, etc.
2091
	$extraParams = array(
2092
		'preview',
2093
		'splitjs',
2094
	);
2095
2096
	// Actions that specifically uses XML output.
2097
	$xmlActions = array(
2098
		'quotefast',
2099
		'jsmodify',
2100
		'xmlhttp',
2101
		'post2',
2102
		'suggest',
2103
		'stats',
2104
		'notifytopic',
2105
		'notifyboard',
2106
	);
2107
2108
	call_integration_hook('integrate_simple_actions', array(&$simpleActions, &$simpleAreas, &$simpleSubActions, &$extraParams, &$xmlActions));
2109
2110
	$context['simple_action'] = in_array($context['current_action'], $simpleActions) ||
2111
	(isset($simpleAreas[$context['current_action']]) && isset($_REQUEST['area']) && in_array($_REQUEST['area'], $simpleAreas[$context['current_action']])) ||
2112
	(isset($simpleSubActions[$context['current_action']]) && in_array($context['current_subaction'], $simpleSubActions[$context['current_action']]));
2113
2114
	// See if theres any extra param to check.
2115
	$requiresXML = false;
2116
	foreach ($extraParams as $key => $extra)
2117
		if (isset($_REQUEST[$extra]))
2118
			$requiresXML = true;
2119
2120
	// Output is fully XML, so no need for the index template.
2121
	if (isset($_REQUEST['xml']) && (in_array($context['current_action'], $xmlActions) || $requiresXML))
2122
	{
2123
		loadLanguage('index+Modifications');
2124
		loadTemplate('Xml');
2125
		$context['template_layers'] = array();
2126
	}
2127
2128
	// These actions don't require the index template at all.
2129
	elseif (!empty($context['simple_action']))
2130
	{
2131
		loadLanguage('index+Modifications');
2132
		$context['template_layers'] = array();
2133
	}
2134
2135
	else
2136
	{
2137
		// Custom templates to load, or just default?
2138
		if (isset($settings['theme_templates']))
2139
			$templates = explode(',', $settings['theme_templates']);
2140
		else
2141
			$templates = array('index');
2142
2143
		// Load each template...
2144
		foreach ($templates as $template)
2145
			loadTemplate($template);
2146
2147
		// ...and attempt to load their associated language files.
2148
		$required_files = implode('+', array_merge($templates, array('Modifications')));
2149
		loadLanguage($required_files, '', false);
2150
2151
		// Custom template layers?
2152
		if (isset($settings['theme_layers']))
2153
			$context['template_layers'] = explode(',', $settings['theme_layers']);
2154
		else
2155
			$context['template_layers'] = array('html', 'body');
2156
	}
2157
2158
	// Initialize the theme.
2159
	loadSubTemplate('init', 'ignore');
0 ignored issues
show
Bug introduced by
'ignore' of type string is incompatible with the type boolean expected by parameter $fatal of loadSubTemplate(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

2159
	loadSubTemplate('init', /** @scrutinizer ignore-type */ 'ignore');
Loading history...
2160
2161
	// Allow overriding the board wide time/number formats.
2162
	if (empty($user_settings['time_format']) && !empty($txt['time_format']))
2163
		$user_info['time_format'] = $txt['time_format'];
2164
2165
	// Set the character set from the template.
2166
	$context['character_set'] = empty($modSettings['global_character_set']) ? $txt['lang_character_set'] : $modSettings['global_character_set'];
2167
	$context['utf8'] = $context['character_set'] === 'UTF-8';
2168
	$context['right_to_left'] = !empty($txt['lang_rtl']);
2169
2170
	// Guests may still need a name.
2171
	if ($context['user']['is_guest'] && empty($context['user']['name']))
2172
		$context['user']['name'] = $txt['guest_title'];
2173
2174
	// Any theme-related strings that need to be loaded?
2175
	if (!empty($settings['require_theme_strings']))
2176
		loadLanguage('ThemeStrings', '', false);
2177
2178
	// Make a special URL for the language.
2179
	$settings['lang_images_url'] = $settings['images_url'] . '/' . (!empty($txt['image_lang']) ? $txt['image_lang'] : $user_info['language']);
2180
2181
	// And of course, let's load the default CSS file.
2182
	loadCSSFile('index.css', array('minimize' => true, 'order_pos' => 1), 'smf_index');
2183
2184
	// Here is my luvly Responsive CSS
2185
	loadCSSFile('responsive.css', array('force_current' => false, 'validate' => true, 'minimize' => true, 'order_pos' => 9000), 'smf_responsive');
2186
2187
	if ($context['right_to_left'])
2188
		loadCSSFile('rtl.css', array('order_pos' => 200), 'smf_rtl');
2189
2190
	// We allow theme variants, because we're cool.
2191
	$context['theme_variant'] = '';
2192
	$context['theme_variant_url'] = '';
2193
	if (!empty($settings['theme_variants']))
2194
	{
2195
		// Overriding - for previews and that ilk.
2196
		if (!empty($_REQUEST['variant']))
2197
			$_SESSION['id_variant'] = $_REQUEST['variant'];
2198
		// User selection?
2199
		if (empty($settings['disable_user_variant']) || allowedTo('admin_forum'))
2200
			$context['theme_variant'] = !empty($_SESSION['id_variant']) ? $_SESSION['id_variant'] : (!empty($options['theme_variant']) ? $options['theme_variant'] : '');
2201
		// If not a user variant, select the default.
2202
		if ($context['theme_variant'] == '' || !in_array($context['theme_variant'], $settings['theme_variants']))
2203
			$context['theme_variant'] = !empty($settings['default_variant']) && in_array($settings['default_variant'], $settings['theme_variants']) ? $settings['default_variant'] : $settings['theme_variants'][0];
2204
2205
		// Do this to keep things easier in the templates.
2206
		$context['theme_variant'] = '_' . $context['theme_variant'];
2207
		$context['theme_variant_url'] = $context['theme_variant'] . '/';
2208
2209
		if (!empty($context['theme_variant']))
2210
		{
2211
			loadCSSFile('index' . $context['theme_variant'] . '.css', array('order_pos' => 300), 'smf_index' . $context['theme_variant']);
2212
			if ($context['right_to_left'])
2213
				loadCSSFile('rtl' . $context['theme_variant'] . '.css', array('order_pos' => 400), 'smf_rtl' . $context['theme_variant']);
2214
		}
2215
	}
2216
2217
	// Let's be compatible with old themes!
2218
	if (!function_exists('template_html_above') && in_array('html', $context['template_layers']))
2219
		$context['template_layers'] = array('main');
2220
2221
	$context['tabindex'] = 1;
2222
2223
	// Compatibility.
2224
	if (!isset($settings['theme_version']))
2225
		$modSettings['memberCount'] = $modSettings['totalMembers'];
2226
2227
	// Default JS variables for use in every theme
2228
	$context['javascript_vars'] = array(
2229
		'smf_theme_url' => '"' . $settings['theme_url'] . '"',
2230
		'smf_default_theme_url' => '"' . $settings['default_theme_url'] . '"',
2231
		'smf_images_url' => '"' . $settings['images_url'] . '"',
2232
		'smf_smileys_url' => '"' . $modSettings['smileys_url'] . '"',
2233
		'smf_smiley_sets_default' => '"' . $modSettings['smiley_sets_default'] . '"',
2234
		'smf_smiley_sets_exts' => '"' . $modSettings['smiley_sets_exts'] . '"',
2235
		'smf_smiley_sets_default_ext' => '"' . $context['user']['smiley_set_default_ext'] . '"',
2236
		'smf_scripturl' => '"' . $scripturl . '"',
2237
		'smf_iso_case_folding' => $context['server']['iso_case_folding'] ? 'true' : 'false',
2238
		'smf_charset' => '"' . $context['character_set'] . '"',
2239
		'smf_session_id' => '"' . $context['session_id'] . '"',
2240
		'smf_session_var' => '"' . $context['session_var'] . '"',
2241
		'smf_member_id' => $context['user']['id'],
2242
		'ajax_notification_text' => JavaScriptEscape($txt['ajax_in_progress']),
2243
		'help_popup_heading_text' => JavaScriptEscape($txt['help_popup']),
2244
		'banned_text' => JavaScriptEscape(sprintf($txt['your_ban'], $context['user']['name'])),
2245
	);
2246
2247
	// Add the JQuery library to the list of files to load.
2248
	if (isset($modSettings['jquery_source']) && $modSettings['jquery_source'] == 'cdn')
2249
		loadJavaScriptFile('https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js', array('external' => true), 'smf_jquery');
2250
2251
	elseif (isset($modSettings['jquery_source']) && $modSettings['jquery_source'] == 'local')
2252
		loadJavaScriptFile('jquery-3.2.1.min.js', array('seed' => false), 'smf_jquery');
2253
2254
	elseif (isset($modSettings['jquery_source'], $modSettings['jquery_custom']) && $modSettings['jquery_source'] == 'custom')
2255
		loadJavaScriptFile($modSettings['jquery_custom'], array('external' => true), 'smf_jquery');
2256
2257
	// Auto loading? template_javascript() will take care of the local half of this.
2258
	else
2259
		loadJavaScriptFile('https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js', array('external' => true), 'smf_jquery');
2260
2261
	// Queue our JQuery plugins!
2262
	loadJavaScriptFile('smf_jquery_plugins.js', array('minimize' => true), 'smf_jquery_plugins');
2263
	if (!$user_info['is_guest'])
2264
	{
2265
		loadJavaScriptFile('jquery.custom-scrollbar.js', array('minimize' => true), 'smf_jquery_scrollbar');
2266
		loadCSSFile('jquery.custom-scrollbar.css', array('force_current' => false, 'validate' => true), 'smf_scrollbar');
2267
	}
2268
2269
	// script.js and theme.js, always required, so always add them! Makes index.template.php cleaner and all.
2270
	loadJavaScriptFile('script.js', array('defer' => false, 'minimize' => true), 'smf_script');
2271
	loadJavaScriptFile('theme.js', array('minimize' => true), 'smf_theme');
2272
2273
	// If we think we have mail to send, let's offer up some possibilities... robots get pain (Now with scheduled task support!)
2274
	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())
2275
	{
2276
		if (isBrowser('possibly_robot'))
2277
		{
2278
			// @todo Maybe move this somewhere better?!
2279
			require_once($sourcedir . '/ScheduledTasks.php');
2280
2281
			// What to do, what to do?!
2282
			if (empty($modSettings['next_task_time']) || $modSettings['next_task_time'] < time())
2283
				AutoTask();
2284
			else
2285
				ReduceMailQueue();
2286
		}
2287
		else
2288
		{
2289
			$type = empty($modSettings['next_task_time']) || $modSettings['next_task_time'] < time() ? 'task' : 'mailq';
2290
			$ts = $type == 'mailq' ? $modSettings['mail_next_send'] : $modSettings['next_task_time'];
2291
2292
			addInlineJavaScript('
2293
		function smfAutoTask()
2294
		{
2295
			$.get(smf_scripturl + "?scheduled=' . $type . ';ts=' . $ts . '");
2296
		}
2297
		window.setTimeout("smfAutoTask();", 1);');
2298
		}
2299
	}
2300
2301
	// And we should probably trigger the cron too.
2302
	if (empty($modSettings['cron_is_real_cron']))
2303
	{
2304
		$ts = time();
2305
		$ts -= $ts % 15;
2306
		addInlineJavaScript('
2307
	function triggerCron()
2308
	{
2309
		$.get(' . JavaScriptEscape($boardurl) . ' + "/cron.php?ts=' . $ts . '");
2310
	}
2311
	window.setTimeout(triggerCron, 1);', true);
2312
	}
2313
2314
	// Filter out the restricted boards from the linktree
2315
	if (!$user_info['is_admin'] && !empty($board))
2316
	{
2317
		foreach ($context['linktree'] as $k => $element)
2318
		{
2319
			if (!empty($element['groups']) &&
2320
				(count(array_intersect($user_info['groups'], $element['groups'])) == 0 ||
2321
				(!empty($modSettings['deny_boards_access']) && count(array_intersect($user_info['groups'], $element['deny_groups'])) != 0)))
2322
			{
2323
				$context['linktree'][$k]['name'] = $txt['restricted_board'];
2324
				$context['linktree'][$k]['extra_before'] = '<i>';
2325
				$context['linktree'][$k]['extra_after'] = '</i>';
2326
				unset($context['linktree'][$k]['url']);
2327
			}
2328
		}
2329
	}
2330
2331
	// Any files to include at this point?
2332
	if (!empty($modSettings['integrate_theme_include']))
2333
	{
2334
		$theme_includes = explode(',', $modSettings['integrate_theme_include']);
2335
		foreach ($theme_includes as $include)
2336
		{
2337
			$include = strtr(trim($include), array('$boarddir' => $boarddir, '$sourcedir' => $sourcedir, '$themedir' => $settings['theme_dir']));
2338
			if (file_exists($include))
2339
				require_once($include);
2340
		}
2341
	}
2342
2343
	// Call load theme integration functions.
2344
	call_integration_hook('integrate_load_theme');
2345
2346
	// We are ready to go.
2347
	$context['theme_loaded'] = true;
2348
}
2349
2350
/**
2351
 * Load a template - if the theme doesn't include it, use the default.
2352
 * What this function does:
2353
 *  - loads a template file with the name template_name from the current, default, or base theme.
2354
 *  - detects a wrong default theme directory and tries to work around it.
2355
 *
2356
 * @uses the template_include() function to include the file.
2357
 * @param string $template_name The name of the template to load
2358
 * @param array|string $style_sheets The name of a single stylesheet or an array of names of stylesheets to load
2359
 * @param bool $fatal If true, dies with an error message if the template cannot be found
2360
 * @return boolean Whether or not the template was loaded
2361
 */
2362
function loadTemplate($template_name, $style_sheets = array(), $fatal = true)
2363
{
2364
	global $context, $settings, $txt, $scripturl, $boarddir, $db_show_debug;
2365
2366
	// Do any style sheets first, cause we're easy with those.
2367
	if (!empty($style_sheets))
2368
	{
2369
		if (!is_array($style_sheets))
2370
			$style_sheets = array($style_sheets);
2371
2372
		foreach ($style_sheets as $sheet)
2373
			loadCSSFile($sheet . '.css', array(), $sheet);
2374
	}
2375
2376
	// No template to load?
2377
	if ($template_name === false)
0 ignored issues
show
introduced by
The condition $template_name === false is always false.
Loading history...
2378
		return true;
2379
2380
	$loaded = false;
2381
	foreach ($settings['template_dirs'] as $template_dir)
2382
	{
2383
		if (file_exists($template_dir . '/' . $template_name . '.template.php'))
2384
		{
2385
			$loaded = true;
2386
			template_include($template_dir . '/' . $template_name . '.template.php', true);
2387
			break;
2388
		}
2389
	}
2390
2391
	if ($loaded)
0 ignored issues
show
introduced by
The condition $loaded is always false.
Loading history...
2392
	{
2393
		if ($db_show_debug === true)
2394
			$context['debug']['templates'][] = $template_name . ' (' . basename($template_dir) . ')';
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $template_dir seems to be defined by a foreach iteration on line 2381. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
2395
2396
		// If they have specified an initialization function for this template, go ahead and call it now.
2397
		if (function_exists('template_' . $template_name . '_init'))
2398
			call_user_func('template_' . $template_name . '_init');
2399
	}
2400
	// Hmmm... doesn't exist?!  I don't suppose the directory is wrong, is it?
2401
	elseif (!file_exists($settings['default_theme_dir']) && file_exists($boarddir . '/Themes/default'))
2402
	{
2403
		$settings['default_theme_dir'] = $boarddir . '/Themes/default';
2404
		$settings['template_dirs'][] = $settings['default_theme_dir'];
2405
2406
		if (!empty($context['user']['is_admin']) && !isset($_GET['th']))
2407
		{
2408
			loadLanguage('Errors');
2409
			echo '
2410
<div class="alert errorbox">
2411
	<a href="', $scripturl . '?action=admin;area=theme;sa=list;' . $context['session_var'] . '=' . $context['session_id'], '" class="alert">', $txt['theme_dir_wrong'], '</a>
2412
</div>';
2413
		}
2414
2415
		loadTemplate($template_name);
2416
	}
2417
	// Cause an error otherwise.
2418
	elseif ($template_name != 'Errors' && $template_name != 'index' && $fatal)
2419
		fatal_lang_error('theme_template_error', 'template', array((string) $template_name));
2420
	elseif ($fatal)
2421
		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'));
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
2422
	else
2423
		return false;
2424
}
2425
2426
/**
2427
 * Load a sub-template.
2428
 * What it does:
2429
 * 	- loads the sub template specified by sub_template_name, which must be in an already-loaded template.
2430
 *  - if ?debug is in the query string, shows administrators a marker after every sub template
2431
 *	for debugging purposes.
2432
 *
2433
 * @todo get rid of reading $_REQUEST directly
2434
 *
2435
 * @param string $sub_template_name The name of the sub-template to load
2436
 * @param bool $fatal Whether to die with an error if the sub-template can't be loaded
2437
 */
2438
function loadSubTemplate($sub_template_name, $fatal = false)
2439
{
2440
	global $context, $txt, $db_show_debug;
2441
2442
	if ($db_show_debug === true)
2443
		$context['debug']['sub_templates'][] = $sub_template_name;
2444
2445
	// Figure out what the template function is named.
2446
	$theme_function = 'template_' . $sub_template_name;
2447
	if (function_exists($theme_function))
2448
		$theme_function();
2449
	elseif ($fatal === false)
2450
		fatal_lang_error('theme_template_error', 'template', array((string) $sub_template_name));
2451
	elseif ($fatal !== 'ignore')
0 ignored issues
show
introduced by
The condition $fatal !== 'ignore' is always true.
Loading history...
2452
		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'));
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
2453
2454
	// Are we showing debugging for templates?  Just make sure not to do it before the doctype...
2455
	if (allowedTo('admin_forum') && isset($_REQUEST['debug']) && !in_array($sub_template_name, array('init', 'main_below')) && ob_get_length() > 0 && !isset($_REQUEST['xml']))
2456
	{
2457
		echo '
2458
<div class="warningbox">---- ', $sub_template_name, ' ends ----</div>';
2459
	}
2460
}
2461
2462
/**
2463
 * Add a CSS file for output later
2464
 *
2465
 * @param string $fileName The name of the file to load
2466
 * @param array $params An array of parameters
2467
 * Keys are the following:
2468
 * 	- ['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
2469
 * 	- ['default_theme'] (true/false): force use of default theme url
2470
 * 	- ['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
2471
 *  - ['validate'] (true/false): if true script will validate the local file exists
2472
 *  - ['rtl'] (string): additional file to load in RTL mode
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
 *  - ['order_pos'] int define the load order, when not define it's loaded in the middle, before index.css = -500, after index.css = 500, middle = 3000, end (i.e. after responsive.css) = 10000
2476
 * @param string $id An ID to stick on the end of the filename for caching purposes
2477
 */
2478
function loadCSSFile($fileName, $params = array(), $id = '')
2479
{
2480
	global $settings, $context, $modSettings;
2481
2482
	if (empty($context['css_files_order']))
2483
		$context['css_files_order'] = array();
2484
2485
	$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']) : '');
2486
	$params['force_current'] = isset($params['force_current']) ? $params['force_current'] : false;
2487
	$themeRef = !empty($params['default_theme']) ? 'default_theme' : 'theme';
2488
	$params['minimize'] = isset($params['minimize']) ? $params['minimize'] : true;
2489
	$params['external'] = isset($params['external']) ? $params['external'] : false;
2490
	$params['validate'] = isset($params['validate']) ? $params['validate'] : true;
2491
	$params['order_pos'] = isset($params['order_pos']) ? (int) $params['order_pos'] : 3000;
2492
2493
	// If this is an external file, automatically set this to false.
2494
	if (!empty($params['external']))
2495
		$params['minimize'] = false;
2496
2497
	// Account for shorthand like admin.css?alp21 filenames
2498
	$has_seed = strpos($fileName, '.css?');
2499
	$id = empty($id) ? strtr(basename(str_replace('.css', '', $fileName)), '?', '_') : $id;
2500
2501
	// Is this a local file?
2502
	if (empty($params['external']))
2503
	{
2504
		// Are we validating the the file exists?
2505
		if (!empty($params['validate']) && !file_exists($settings[$themeRef . '_dir'] . '/css/' . $fileName))
2506
		{
2507
			// Maybe the default theme has it?
2508
			if ($themeRef === 'theme' && !$params['force_current'] && file_exists($settings['default_theme_dir'] . '/css/' . $fileName))
2509
			{
2510
				$fileUrl = $settings['default_theme_url'] . '/css/' . $fileName . ($has_seed ? '' : $params['seed']);
2511
				$filePath = $settings['default_theme_dir'] . '/css/' . $fileName . ($has_seed ? '' : $params['seed']);
2512
			}
2513
2514
			else
2515
			{
2516
				$fileUrl = false;
2517
				$filePath = false;
2518
			}
2519
		}
2520
2521
		else
2522
		{
2523
			$fileUrl = $settings[$themeRef . '_url'] . '/css/' . $fileName . ($has_seed ? '' : $params['seed']);
2524
			$filePath = $settings[$themeRef . '_dir'] . '/css/' . $fileName . ($has_seed ? '' : $params['seed']);
2525
		}
2526
	}
2527
2528
	// An external file doesn't have a filepath. Mock one for simplicity.
2529
	else
2530
	{
2531
		$fileUrl = $fileName;
2532
		$filePath = $fileName;
2533
	}
2534
2535
	// Add it to the array for use in the template
2536
	if (!empty($fileName))
2537
	{
2538
		// find a free number/position
2539
		while (isset($context['css_files_order'][$params['order_pos']]))
2540
			$params['order_pos']++;
2541
		$context['css_files_order'][$params['order_pos']] = $id;
2542
2543
		$context['css_files'][$id] = array('fileUrl' => $fileUrl, 'filePath' => $filePath, 'fileName' => $fileName, 'options' => $params);
2544
	}
2545
2546
	if (!empty($context['right_to_left']) && !empty($params['rtl']))
2547
		loadCSSFile($params['rtl'], array_diff_key($params, array('rtl' => 0)));
2548
}
2549
2550
/**
2551
 * Add a block of inline css code to be executed later
2552
 *
2553
 * - only use this if you have to, generally external css files are better, but for very small changes
2554
 *   or for scripts that require help from PHP/whatever, this can be useful.
2555
 * - all code added with this function is added to the same <style> tag so do make sure your css is valid!
2556
 *
2557
 * @param string $css Some css code
2558
 * @return void|bool Adds the CSS to the $context['css_header'] array or returns if no CSS is specified
2559
 */
2560
function addInlineCss($css)
2561
{
2562
	global $context;
2563
2564
	// Gotta add something...
2565
	if (empty($css))
2566
		return false;
2567
2568
	$context['css_header'][] = $css;
2569
}
2570
2571
/**
2572
 * Add a Javascript file for output later
2573
 *
2574
 * @param string $fileName The name of the file to load
2575
 * @param array $params An array of parameter info
2576
 * Keys are the following:
2577
 * 	- ['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
2578
 * 	- ['default_theme'] (true/false): force use of default theme url
2579
 * 	- ['defer'] (true/false): define if the file should load in <head> or before the closing <html> tag
2580
 * 	- ['force_current'] (true/false): if this is false, we will attempt to load the file from the
2581
 *	default theme if not found in the current theme
2582
 *	- ['async'] (true/false): if the script should be loaded asynchronously (HTML5)
2583
 *  - ['validate'] (true/false): if true script will validate the local file exists
2584
 *  - ['seed'] (true/false/string): if true or null, use cache stale, false do not, or used a supplied string
2585
 *  - ['minimize'] boolean to add your file to the main minimized file. Useful when you have a file thats loaded everywhere and for everyone.
2586
 *
2587
 * @param string $id An ID to stick on the end of the filename
2588
 */
2589
function loadJavaScriptFile($fileName, $params = array(), $id = '')
2590
{
2591
	global $settings, $context, $modSettings;
2592
2593
	$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']) : '');
2594
	$params['force_current'] = isset($params['force_current']) ? $params['force_current'] : false;
2595
	$themeRef = !empty($params['default_theme']) ? 'default_theme' : 'theme';
2596
	$params['async'] = isset($params['async']) ? $params['async'] : false;
2597
	$params['minimize'] = isset($params['minimize']) ? $params['minimize'] : false;
2598
	$params['external'] = isset($params['external']) ? $params['external'] : false;
2599
	$params['validate'] = isset($params['validate']) ? $params['validate'] : true;
2600
2601
	// If this is an external file, automatically set this to false.
2602
	if (!empty($params['external']))
2603
		$params['minimize'] = false;
2604
2605
	// Account for shorthand like admin.js?alp21 filenames
2606
	$has_seed = strpos($fileName, '.js?');
2607
	$id = empty($id) ? strtr(basename(str_replace('.js', '', $fileName)), '?', '_') : $id;
2608
2609
	// Is this a local file?
2610
	if (empty($params['external']))
2611
	{
2612
		// Are we validating it exists on disk?
2613
		if (!empty($params['validate']) && !file_exists($settings[$themeRef . '_dir'] . '/scripts/' . $fileName))
2614
		{
2615
			// Can't find it in this theme, how about the default?
2616
			if ($themeRef === 'theme' && !$params['force_current'] && file_exists($settings['default_theme_dir'] . '/scripts/' . $fileName))
2617
			{
2618
				$fileUrl = $settings['default_theme_url'] . '/scripts/' . $fileName . ($has_seed ? '' : $params['seed']);
2619
				$filePath = $settings['default_theme_dir'] . '/scripts/' . $fileName . ($has_seed ? '' : $params['seed']);
2620
			}
2621
2622
			else
2623
			{
2624
				$fileUrl = false;
2625
				$filePath = false;
2626
			}
2627
		}
2628
2629
		else
2630
		{
2631
			$fileUrl = $settings[$themeRef . '_url'] . '/scripts/' . $fileName . ($has_seed ? '' : $params['seed']);
2632
			$filePath = $settings[$themeRef . '_dir'] . '/scripts/' . $fileName . ($has_seed ? '' : $params['seed']);
2633
		}
2634
	}
2635
2636
	// An external file doesn't have a filepath. Mock one for simplicity.
2637
	else
2638
	{
2639
		$fileUrl = $fileName;
2640
		$filePath = $fileName;
2641
	}
2642
2643
	// Add it to the array for use in the template
2644
	if (!empty($fileName))
2645
		$context['javascript_files'][$id] = array('fileUrl' => $fileUrl, 'filePath' => $filePath, 'fileName' => $fileName, 'options' => $params);
2646
}
2647
2648
/**
2649
 * Add a Javascript variable for output later (for feeding text strings and similar to JS)
2650
 * Cleaner and easier (for modders) than to use the function below.
2651
 *
2652
 * @param string $key The key for this variable
2653
 * @param string $value The value
2654
 * @param bool $escape Whether or not to escape the value
2655
 */
2656
function addJavaScriptVar($key, $value, $escape = false)
2657
{
2658
	global $context;
2659
2660
	if (!empty($key) && (!empty($value) || $value === '0'))
2661
		$context['javascript_vars'][$key] = !empty($escape) ? JavaScriptEscape($value) : $value;
2662
}
2663
2664
/**
2665
 * Add a block of inline Javascript code to be executed later
2666
 *
2667
 * - only use this if you have to, generally external JS files are better, but for very small scripts
2668
 *   or for scripts that require help from PHP/whatever, this can be useful.
2669
 * - all code added with this function is added to the same <script> tag so do make sure your JS is clean!
2670
 *
2671
 * @param string $javascript Some JS code
2672
 * @param bool $defer Whether the script should load in <head> or before the closing <html> tag
2673
 * @return void|bool Adds the code to one of the $context['javascript_inline'] arrays or returns if no JS was specified
2674
 */
2675
function addInlineJavaScript($javascript, $defer = false)
2676
{
2677
	global $context;
2678
2679
	if (empty($javascript))
2680
		return false;
2681
2682
	$context['javascript_inline'][($defer === true ? 'defer' : 'standard')][] = $javascript;
2683
}
2684
2685
/**
2686
 * Load a language file.  Tries the current and default themes as well as the user and global languages.
2687
 *
2688
 * @param string $template_name The name of a template file
2689
 * @param string $lang A specific language to load this file from
2690
 * @param bool $fatal Whether to die with an error if it can't be loaded
2691
 * @param bool $force_reload Whether to load the file again if it's already loaded
2692
 * @return string The language actually loaded.
2693
 */
2694
function loadLanguage($template_name, $lang = '', $fatal = true, $force_reload = false)
2695
{
2696
	global $user_info, $language, $settings, $context, $modSettings;
2697
	global $db_show_debug, $sourcedir, $txt, $birthdayEmails, $txtBirthdayEmails;
2698
	static $already_loaded = array();
2699
2700
	// Default to the user's language.
2701
	if ($lang == '')
2702
		$lang = isset($user_info['language']) ? $user_info['language'] : $language;
2703
2704
	// Do we want the English version of language file as fallback?
2705
	if (empty($modSettings['disable_language_fallback']) && $lang != 'english')
2706
		loadLanguage($template_name, 'english', false);
2707
2708
	if (!$force_reload && isset($already_loaded[$template_name]) && $already_loaded[$template_name] == $lang)
2709
		return $lang;
2710
2711
	// Make sure we have $settings - if not we're in trouble and need to find it!
2712
	if (empty($settings['default_theme_dir']))
2713
	{
2714
		require_once($sourcedir . '/ScheduledTasks.php');
2715
		loadEssentialThemeData();
2716
	}
2717
2718
	// What theme are we in?
2719
	$theme_name = basename($settings['theme_url']);
2720
	if (empty($theme_name))
2721
		$theme_name = 'unknown';
2722
2723
	// For each file open it up and write it out!
2724
	foreach (explode('+', $template_name) as $template)
2725
	{
2726
		// Obviously, the current theme is most important to check.
2727
		$attempts = array(
2728
			array($settings['theme_dir'], $template, $lang, $settings['theme_url']),
2729
			array($settings['theme_dir'], $template, $language, $settings['theme_url']),
2730
		);
2731
2732
		// Do we have a base theme to worry about?
2733
		if (isset($settings['base_theme_dir']))
2734
		{
2735
			$attempts[] = array($settings['base_theme_dir'], $template, $lang, $settings['base_theme_url']);
2736
			$attempts[] = array($settings['base_theme_dir'], $template, $language, $settings['base_theme_url']);
2737
		}
2738
2739
		// Fall back on the default theme if necessary.
2740
		$attempts[] = array($settings['default_theme_dir'], $template, $lang, $settings['default_theme_url']);
2741
		$attempts[] = array($settings['default_theme_dir'], $template, $language, $settings['default_theme_url']);
2742
2743
		// Fall back on the English language if none of the preferred languages can be found.
2744
		if (!in_array('english', array($lang, $language)))
2745
		{
2746
			$attempts[] = array($settings['theme_dir'], $template, 'english', $settings['theme_url']);
2747
			$attempts[] = array($settings['default_theme_dir'], $template, 'english', $settings['default_theme_url']);
2748
		}
2749
2750
		// Try to find the language file.
2751
		$found = false;
2752
		foreach ($attempts as $k => $file)
2753
		{
2754
			if (file_exists($file[0] . '/languages/' . $file[1] . '.' . $file[2] . '.php'))
2755
			{
2756
				// Include it!
2757
				template_include($file[0] . '/languages/' . $file[1] . '.' . $file[2] . '.php');
2758
2759
				// Note that we found it.
2760
				$found = true;
2761
2762
				// setlocale is required for basename() & pathinfo() to work properly on the selected language
2763
				if (!empty($txt['lang_locale']) && !empty($modSettings['global_character_set']))
2764
					setlocale(LC_CTYPE, $txt['lang_locale'] . '.' . $modSettings['global_character_set']);
2765
2766
				break;
2767
			}
2768
		}
2769
2770
		// That couldn't be found!  Log the error, but *try* to continue normally.
2771
		if (!$found && $fatal)
2772
		{
2773
			log_error(sprintf($txt['theme_language_error'], $template_name . '.' . $lang, 'template'));
2774
			break;
2775
		}
2776
2777
		// For the sake of backward compatibility
2778
		if (!empty($txt['emails']))
2779
		{
2780
			foreach ($txt['emails'] as $key => $value)
2781
			{
2782
				$txt[$key . '_subject'] = $value['subject'];
2783
				$txt[$key . '_body'] = $value['body'];
2784
			}
2785
			$txt['emails'] = array();
2786
		}
2787
		// For sake of backward compatibility: $birthdayEmails is supposed to be
2788
		// empty in a normal install. If it isn't it means the forum is using
2789
		// something "old" (it may be the translation, it may be a mod) and this
2790
		// code (like the piece above) takes care of converting it to the new format
2791
		if (!empty($birthdayEmails))
2792
		{
2793
			foreach ($birthdayEmails as $key => $value)
2794
			{
2795
				$txtBirthdayEmails[$key . '_subject'] = $value['subject'];
2796
				$txtBirthdayEmails[$key . '_body'] = $value['body'];
2797
				$txtBirthdayEmails[$key . '_author'] = $value['author'];
2798
			}
2799
			$birthdayEmails = array();
2800
		}
2801
	}
2802
2803
	// Keep track of what we're up to soldier.
2804
	if ($db_show_debug === true)
2805
		$context['debug']['language_files'][] = $template_name . '.' . $lang . ' (' . $theme_name . ')';
2806
2807
	// Remember what we have loaded, and in which language.
2808
	$already_loaded[$template_name] = $lang;
2809
2810
	// Return the language actually loaded.
2811
	return $lang;
2812
}
2813
2814
/**
2815
 * Get all parent boards (requires first parent as parameter)
2816
 * It finds all the parents of id_parent, and that board itself.
2817
 * Additionally, it detects the moderators of said boards.
2818
 *
2819
 * @param int $id_parent The ID of the parent board
2820
 * @return array An array of information about the boards found.
2821
 */
2822
function getBoardParents($id_parent)
2823
{
2824
	global $scripturl, $smcFunc;
2825
2826
	// First check if we have this cached already.
2827
	if (($boards = cache_get_data('board_parents-' . $id_parent, 480)) === null)
0 ignored issues
show
introduced by
The condition $boards = cache_get_data...d_parent, 480) === null is always false.
Loading history...
2828
	{
2829
		$boards = array();
2830
		$original_parent = $id_parent;
2831
2832
		// Loop while the parent is non-zero.
2833
		while ($id_parent != 0)
2834
		{
2835
			$result = $smcFunc['db_query']('', '
2836
				SELECT
2837
					b.id_parent, b.name, {int:board_parent} AS id_board, b.member_groups, b.deny_member_groups,
2838
					b.child_level, COALESCE(mem.id_member, 0) AS id_moderator, mem.real_name,
2839
					COALESCE(mg.id_group, 0) AS id_moderator_group, mg.group_name
2840
				FROM {db_prefix}boards AS b
2841
					LEFT JOIN {db_prefix}moderators AS mods ON (mods.id_board = b.id_board)
2842
					LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = mods.id_member)
2843
					LEFT JOIN {db_prefix}moderator_groups AS modgs ON (modgs.id_board = b.id_board)
2844
					LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = modgs.id_group)
2845
				WHERE b.id_board = {int:board_parent}',
2846
				array(
2847
					'board_parent' => $id_parent,
2848
				)
2849
			);
2850
			// In the EXTREMELY unlikely event this happens, give an error message.
2851
			if ($smcFunc['db_num_rows']($result) == 0)
2852
				fatal_lang_error('parent_not_found', 'critical');
2853
			while ($row = $smcFunc['db_fetch_assoc']($result))
2854
			{
2855
				if (!isset($boards[$row['id_board']]))
2856
				{
2857
					$id_parent = $row['id_parent'];
2858
					$boards[$row['id_board']] = array(
2859
						'url' => $scripturl . '?board=' . $row['id_board'] . '.0',
2860
						'name' => $row['name'],
2861
						'level' => $row['child_level'],
2862
						'groups' => explode(',', $row['member_groups']),
2863
						'deny_groups' => explode(',', $row['deny_member_groups']),
2864
						'moderators' => array(),
2865
						'moderator_groups' => array()
2866
					);
2867
				}
2868
				// If a moderator exists for this board, add that moderator for all children too.
2869
				if (!empty($row['id_moderator']))
2870
					foreach ($boards as $id => $dummy)
2871
					{
2872
						$boards[$id]['moderators'][$row['id_moderator']] = array(
2873
							'id' => $row['id_moderator'],
2874
							'name' => $row['real_name'],
2875
							'href' => $scripturl . '?action=profile;u=' . $row['id_moderator'],
2876
							'link' => '<a href="' . $scripturl . '?action=profile;u=' . $row['id_moderator'] . '">' . $row['real_name'] . '</a>'
2877
						);
2878
					}
2879
2880
				// If a moderator group exists for this board, add that moderator group for all children too
2881
				if (!empty($row['id_moderator_group']))
2882
					foreach ($boards as $id => $dummy)
2883
					{
2884
						$boards[$id]['moderator_groups'][$row['id_moderator_group']] = array(
2885
							'id' => $row['id_moderator_group'],
2886
							'name' => $row['group_name'],
2887
							'href' => $scripturl . '?action=groups;sa=members;group=' . $row['id_moderator_group'],
2888
							'link' => '<a href="' . $scripturl . '?action=groups;sa=members;group=' . $row['id_moderator_group'] . '">' . $row['group_name'] . '</a>'
2889
						);
2890
					}
2891
			}
2892
			$smcFunc['db_free_result']($result);
2893
		}
2894
2895
		cache_put_data('board_parents-' . $original_parent, $boards, 480);
2896
	}
2897
2898
	return $boards;
2899
}
2900
2901
/**
2902
 * Attempt to reload our known languages.
2903
 * It will try to choose only utf8 or non-utf8 languages.
2904
 *
2905
 * @param bool $use_cache Whether or not to use the cache
2906
 * @return array An array of information about available languages
2907
 */
2908
function getLanguages($use_cache = true)
2909
{
2910
	global $context, $smcFunc, $settings, $modSettings;
2911
2912
	// Either we don't use the cache, or its expired.
2913
	if (!$use_cache || ($context['languages'] = cache_get_data('known_languages', !empty($modSettings['cache_enable']) && $modSettings['cache_enable'] < 1 ? 86400 : 3600)) == null)
2914
	{
2915
		// If we don't have our ucwords function defined yet, let's load the settings data.
2916
		if (empty($smcFunc['ucwords']))
2917
			reloadSettings();
2918
2919
		// If we don't have our theme information yet, let's get it.
2920
		if (empty($settings['default_theme_dir']))
2921
			loadTheme(0, false);
2922
2923
		// Default language directories to try.
2924
		$language_directories = array(
2925
			$settings['default_theme_dir'] . '/languages',
2926
		);
2927
		if (!empty($settings['actual_theme_dir']) && $settings['actual_theme_dir'] != $settings['default_theme_dir'])
2928
			$language_directories[] = $settings['actual_theme_dir'] . '/languages';
2929
2930
		// We possibly have a base theme directory.
2931
		if (!empty($settings['base_theme_dir']))
2932
			$language_directories[] = $settings['base_theme_dir'] . '/languages';
2933
2934
		// Remove any duplicates.
2935
		$language_directories = array_unique($language_directories);
2936
2937
		// Get a list of languages.
2938
		$langList = !empty($modSettings['langList']) ? $smcFunc['json_decode']($modSettings['langList'], true) : array();
2939
		$langList = is_array($langList) ? $langList : false;
2940
2941
		$catchLang = array();
2942
2943
		foreach ($language_directories as $language_dir)
2944
		{
2945
			// Can't look in here... doesn't exist!
2946
			if (!file_exists($language_dir))
2947
				continue;
2948
2949
			$dir = dir($language_dir);
2950
			while ($entry = $dir->read())
2951
			{
2952
				// Look for the index language file... For good measure skip any "index.language-utf8.php" files
2953
				if (!preg_match('~^index\.(.+[^-utf8])\.php$~', $entry, $matches))
2954
					continue;
2955
2956
				if (!empty($langList) && !empty($langList[$matches[1]]))
2957
					$langName = $langList[$matches[1]];
2958
2959
				else
2960
				{
2961
					$langName = $smcFunc['ucwords'](strtr($matches[1], array('_' => ' ')));
2962
2963
					// Get the line we need.
2964
					$fp = @fopen($language_dir . '/' . $entry);
0 ignored issues
show
Bug introduced by
The call to fopen() has too few arguments starting with mode. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2964
					$fp = @/** @scrutinizer ignore-call */ fopen($language_dir . '/' . $entry);

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
2965
2966
					// Yay!
2967
					if ($fp)
2968
					{
2969
						while (($line = fgets($fp)) !== false)
2970
						{
2971
							preg_match('~\$txt\[\'native_name\'\] = \'(.+)\'\;~', $line, $matchNative);
2972
2973
							// Set the language's name.
2974
							if (!empty($matchNative) && !empty($matchNative[1]))
2975
							{
2976
								$langName = un_htmlspecialchars($matchNative[1]);
2977
								break;
2978
							}
2979
						}
2980
2981
						fclose($fp);
2982
					}
2983
2984
					// Catch the language name.
2985
					$catchLang[$matches[1]] = $langName;
2986
				}
2987
2988
				// Build this language entry.
2989
				$context['languages'][$matches[1]] = array(
2990
					'name' => $langName,
2991
					'selected' => false,
2992
					'filename' => $matches[1],
2993
					'location' => $language_dir . '/index.' . $matches[1] . '.php',
2994
				);
2995
			}
2996
			$dir->close();
2997
		}
2998
2999
		// Do we need to store the lang list?
3000
		if (empty($langList))
3001
			updateSettings(array('langList' => $smcFunc['json_encode']($catchLang)));
3002
3003
		// Let's cash in on this deal.
3004
		if (!empty($modSettings['cache_enable']))
3005
			cache_put_data('known_languages', $context['languages'], !empty($modSettings['cache_enable']) && $modSettings['cache_enable'] < 1 ? 86400 : 3600);
3006
	}
3007
3008
	return $context['languages'];
3009
}
3010
3011
/**
3012
 * Replace all vulgar words with respective proper words. (substring or whole words..)
3013
 * What this function does:
3014
 *  - it censors the passed string.
3015
 *  - if the theme setting allow_no_censored is on, and the theme option
3016
 *	show_no_censored is enabled, does not censor, unless force is also set.
3017
 *  - it caches the list of censored words to reduce parsing.
3018
 *
3019
 * @param string &$text The text to censor
3020
 * @param bool $force Whether to censor the text regardless of settings
3021
 * @return string The censored text
3022
 */
3023
function censorText(&$text, $force = false)
3024
{
3025
	global $modSettings, $options, $txt;
3026
	static $censor_vulgar = null, $censor_proper;
3027
3028
	if ((!empty($options['show_no_censored']) && !empty($modSettings['allow_no_censored']) && !$force) || empty($modSettings['censor_vulgar']) || trim($text) === '')
3029
		return $text;
3030
3031
	// If they haven't yet been loaded, load them.
3032
	if ($censor_vulgar == null)
3033
	{
3034
		$censor_vulgar = explode("\n", $modSettings['censor_vulgar']);
3035
		$censor_proper = explode("\n", $modSettings['censor_proper']);
3036
3037
		// Quote them for use in regular expressions.
3038
		if (!empty($modSettings['censorWholeWord']))
3039
		{
3040
			$charset = empty($modSettings['global_character_set']) ? $txt['lang_character_set'] : $modSettings['global_character_set'];
3041
3042
			for ($i = 0, $n = count($censor_vulgar); $i < $n; $i++)
3043
			{
3044
				$censor_vulgar[$i] = str_replace(array('\\\\\\*', '\\*', '&', '\''), array('[*]', '[^\s]*?', '&amp;', '&#039;'), preg_quote($censor_vulgar[$i], '/'));
3045
3046
				// Use the faster \b if we can, or something more complex if we can't
3047
				$boundary_before = preg_match('/^\w/', $censor_vulgar[$i]) ? '\b' : ($charset === 'UTF-8' ? '(?<![\p{L}\p{M}\p{N}_])' : '(?<!\w)');
3048
				$boundary_after = preg_match('/\w$/', $censor_vulgar[$i]) ? '\b' : ($charset === 'UTF-8' ? '(?![\p{L}\p{M}\p{N}_])' : '(?!\w)');
3049
3050
				$censor_vulgar[$i] = '/' . $boundary_before . $censor_vulgar[$i] . $boundary_after . '/' . (empty($modSettings['censorIgnoreCase']) ? '' : 'i') . ($charset === 'UTF-8' ? 'u' : '');
3051
			}
3052
		}
3053
	}
3054
3055
	// Censoring isn't so very complicated :P.
3056
	if (empty($modSettings['censorWholeWord']))
3057
	{
3058
		$func = !empty($modSettings['censorIgnoreCase']) ? 'str_ireplace' : 'str_replace';
3059
		$text = $func($censor_vulgar, $censor_proper, $text);
3060
	}
3061
	else
3062
		$text = preg_replace($censor_vulgar, $censor_proper, $text);
3063
3064
	return $text;
3065
}
3066
3067
/**
3068
 * Load the template/language file using require
3069
 * 	- loads the template or language file specified by filename.
3070
 * 	- uses eval unless disableTemplateEval is enabled.
3071
 * 	- outputs a parse error if the file did not exist or contained errors.
3072
 * 	- attempts to detect the error and line, and show detailed information.
3073
 *
3074
 * @param string $filename The name of the file to include
3075
 * @param bool $once If true only includes the file once (like include_once)
3076
 */
3077
function template_include($filename, $once = false)
3078
{
3079
	global $context, $txt, $scripturl, $modSettings;
3080
	global $boardurl, $boarddir;
3081
	global $maintenance, $mtitle, $mmessage;
3082
	static $templates = array();
3083
3084
	// We want to be able to figure out any errors...
3085
	@ini_set('track_errors', '1');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for ini_set(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

3085
	/** @scrutinizer ignore-unhandled */ @ini_set('track_errors', '1');

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...
3086
3087
	// Don't include the file more than once, if $once is true.
3088
	if ($once && in_array($filename, $templates))
3089
		return;
3090
	// Add this file to the include list, whether $once is true or not.
3091
	else
3092
		$templates[] = $filename;
3093
3094
3095
	$file_found = file_exists($filename);
3096
3097
	if ($once && $file_found)
3098
		require_once($filename);
3099
	elseif ($file_found)
3100
		require($filename);
3101
3102
	if ($file_found !== true)
3103
	{
3104
		ob_end_clean();
3105
		if (!empty($modSettings['enableCompressedOutput']))
3106
			@ob_start('ob_gzhandler');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for ob_start(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

3106
			/** @scrutinizer ignore-unhandled */ @ob_start('ob_gzhandler');

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...
3107
		else
3108
			ob_start();
3109
3110
		if (isset($_GET['debug']))
3111
			header('content-type: application/xhtml+xml; charset=' . (empty($context['character_set']) ? 'ISO-8859-1' : $context['character_set']));
3112
3113
		// Don't cache error pages!!
3114
		header('expires: Mon, 26 Jul 1997 05:00:00 GMT');
3115
		header('last-modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
3116
		header('cache-control: no-cache');
3117
3118
		if (!isset($txt['template_parse_error']))
3119
		{
3120
			$txt['template_parse_error'] = 'Template Parse Error!';
3121
			$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>.';
3122
			$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>.';
3123
			$txt['template_parse_errmsg'] = 'Unfortunately more information is not available at this time as to exactly what is wrong.';
3124
		}
3125
3126
		// First, let's get the doctype and language information out of the way.
3127
		echo '<!DOCTYPE html>
3128
<html', !empty($context['right_to_left']) ? ' dir="rtl"' : '', '>
3129
	<head>';
3130
		if (isset($context['character_set']))
3131
			echo '
3132
		<meta charset="', $context['character_set'], '">';
3133
3134
		if (!empty($maintenance) && !allowedTo('admin_forum'))
3135
			echo '
3136
		<title>', $mtitle, '</title>
3137
	</head>
3138
	<body>
3139
		<h3>', $mtitle, '</h3>
3140
		', $mmessage, '
3141
	</body>
3142
</html>';
3143
		elseif (!allowedTo('admin_forum'))
3144
			echo '
3145
		<title>', $txt['template_parse_error'], '</title>
3146
	</head>
3147
	<body>
3148
		<h3>', $txt['template_parse_error'], '</h3>
3149
		', $txt['template_parse_error_message'], '
3150
	</body>
3151
</html>';
3152
		else
3153
		{
3154
			$error = fetch_web_data($boardurl . strtr($filename, array($boarddir => '', strtr($boarddir, '\\', '/') => '')));
3155
			$error_array = error_get_last();
3156
			if (empty($error) && ini_get('track_errors') && !empty($error_array))
3157
				$error = $error_array['message'];
3158
			if (empty($error))
3159
				$error = $txt['template_parse_errmsg'];
3160
3161
			$error = strtr($error, array('<b>' => '<strong>', '</b>' => '</strong>'));
3162
3163
			echo '
3164
		<title>', $txt['template_parse_error'], '</title>
3165
	</head>
3166
	<body>
3167
		<h3>', $txt['template_parse_error'], '</h3>
3168
		', sprintf($txt['template_parse_error_details'], strtr($filename, array($boarddir => '', strtr($boarddir, '\\', '/') => '')));
3169
3170
			if (!empty($error))
3171
				echo '
3172
		<hr>
3173
3174
		<div style="margin: 0 20px;"><pre>', strtr(strtr($error, array('<strong>' . $boarddir => '<strong>...', '<strong>' . strtr($boarddir, '\\', '/') => '<strong>...')), '\\', '/'), '</pre></div>';
3175
3176
			// I know, I know... this is VERY COMPLICATED.  Still, it's good.
3177
			if (preg_match('~ <strong>(\d+)</strong><br( /)?' . '>$~i', $error, $match) != 0)
3178
			{
3179
				$data = file($filename);
3180
				$data2 = highlight_php_code(implode('', $data));
0 ignored issues
show
Bug introduced by
It seems like $data can also be of type false; however, parameter $pieces of implode() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

3180
				$data2 = highlight_php_code(implode('', /** @scrutinizer ignore-type */ $data));
Loading history...
3181
				$data2 = preg_split('~\<br( /)?\>~', $data2);
3182
3183
				// Fix the PHP code stuff...
3184
				if (!isBrowser('gecko'))
3185
					$data2 = str_replace("\t", '<span style="white-space: pre;">' . "\t" . '</span>', $data2);
3186
				else
3187
					$data2 = str_replace('<pre style="display: inline;">' . "\t" . '</pre>', "\t", $data2);
3188
3189
				// Now we get to work around a bug in PHP where it doesn't escape <br>s!
3190
				$j = -1;
3191
				foreach ($data as $line)
3192
				{
3193
					$j++;
3194
3195
					if (substr_count($line, '<br>') == 0)
3196
						continue;
3197
3198
					$n = substr_count($line, '<br>');
3199
					for ($i = 0; $i < $n; $i++)
3200
					{
3201
						$data2[$j] .= '&lt;br /&gt;' . $data2[$j + $i + 1];
3202
						unset($data2[$j + $i + 1]);
3203
					}
3204
					$j += $n;
3205
				}
3206
				$data2 = array_values($data2);
3207
				array_unshift($data2, '');
3208
3209
				echo '
3210
		<div style="margin: 2ex 20px; width: 96%; overflow: auto;"><pre style="margin: 0;">';
3211
3212
				// Figure out what the color coding was before...
3213
				$line = max($match[1] - 9, 1);
3214
				$last_line = '';
3215
				for ($line2 = $line - 1; $line2 > 1; $line2--)
3216
					if (strpos($data2[$line2], '<') !== false)
3217
					{
3218
						if (preg_match('~(<[^/>]+>)[^<]*$~', $data2[$line2], $color_match) != 0)
3219
							$last_line = $color_match[1];
3220
						break;
3221
					}
3222
3223
				// Show the relevant lines...
3224
				for ($n = min($match[1] + 4, count($data2) + 1); $line <= $n; $line++)
3225
				{
3226
					if ($line == $match[1])
3227
						echo '</pre><div style="background-color: #ffb0b5;"><pre style="margin: 0;">';
3228
3229
					echo '<span style="color: black;">', sprintf('%' . strlen($n) . 's', $line), ':</span> ';
3230
					if (isset($data2[$line]) && $data2[$line] != '')
3231
						echo substr($data2[$line], 0, 2) == '</' ? preg_replace('~^</[^>]+>~', '', $data2[$line]) : $last_line . $data2[$line];
3232
3233
					if (isset($data2[$line]) && preg_match('~(<[^/>]+>)[^<]*$~', $data2[$line], $color_match) != 0)
3234
					{
3235
						$last_line = $color_match[1];
3236
						echo '</', substr($last_line, 1, 4), '>';
3237
					}
3238
					elseif ($last_line != '' && strpos($data2[$line], '<') !== false)
3239
						$last_line = '';
3240
					elseif ($last_line != '' && $data2[$line] != '')
3241
						echo '</', substr($last_line, 1, 4), '>';
3242
3243
					if ($line == $match[1])
3244
						echo '</pre></div><pre style="margin: 0;">';
3245
					else
3246
						echo "\n";
3247
				}
3248
3249
				echo '</pre></div>';
3250
			}
3251
3252
			echo '
3253
	</body>
3254
</html>';
3255
		}
3256
3257
		die;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
3258
	}
3259
}
3260
3261
/**
3262
 * Initialize a database connection.
3263
 */
3264
function loadDatabase()
3265
{
3266
	global $db_persist, $db_connection, $db_server, $db_user, $db_passwd;
3267
	global $db_type, $db_name, $ssi_db_user, $ssi_db_passwd, $sourcedir, $db_prefix, $db_port, $db_mb4;
3268
3269
	// Figure out what type of database we are using.
3270
	if (empty($db_type) || !file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php'))
3271
		$db_type = 'mysql';
3272
3273
	// Load the file for the database.
3274
	require_once($sourcedir . '/Subs-Db-' . $db_type . '.php');
3275
3276
	$db_options = array();
3277
3278
	// Add in the port if needed
3279
	if (!empty($db_port))
3280
		$db_options['port'] = $db_port;
3281
3282
	if (!empty($db_mb4))
3283
		$db_options['db_mb4'] = $db_mb4;
3284
3285
	// 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.
3286
	if (SMF == 'SSI' && !empty($ssi_db_user) && !empty($ssi_db_passwd))
3287
	{
3288
		$options = array_merge($db_options, array('persist' => $db_persist, 'non_fatal' => true, 'dont_select_db' => true));
3289
3290
		$db_connection = smf_db_initiate($db_server, $db_name, $ssi_db_user, $ssi_db_passwd, $db_prefix, $options);
3291
	}
3292
3293
	// Either we aren't in SSI mode, or it failed.
3294
	if (empty($db_connection))
3295
	{
3296
		$options = array_merge($db_options, array('persist' => $db_persist, 'dont_select_db' => SMF == 'SSI'));
3297
3298
		$db_connection = smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, $db_prefix, $options);
3299
	}
3300
3301
	// Safe guard here, if there isn't a valid connection lets put a stop to it.
3302
	if (!$db_connection)
3303
		display_db_error();
3304
3305
	// If in SSI mode fix up the prefix.
3306
	if (SMF == 'SSI')
0 ignored issues
show
introduced by
The condition SMF == 'SSI' is always true.
Loading history...
3307
		db_fix_prefix($db_prefix, $db_name);
3308
}
3309
3310
/**
3311
 * Try to load up a supported caching method. This is saved in $cacheAPI if we are not overriding it.
3312
 *
3313
 * @param string $overrideCache Try to use a different cache method other than that defined in $cache_accelerator.
3314
 * @param bool $fallbackSMF Use the default SMF method if the accelerator fails.
3315
 * @return object|false A object of $cacheAPI, or False on failure.
3316
*/
3317
function loadCacheAccelerator($overrideCache = null, $fallbackSMF = true)
3318
{
3319
	global $sourcedir, $cacheAPI, $cache_accelerator, $cache_enable;
3320
3321
	// is caching enabled?
3322
	if (empty($cache_enable) && empty($overrideCache))
3323
		return false;
3324
3325
	// Not overriding this and we have a cacheAPI, send it back.
3326
	if (empty($overrideCache) && is_object($cacheAPI))
3327
		return $cacheAPI;
3328
	elseif (is_null($cacheAPI))
3329
		$cacheAPI = false;
3330
3331
	// Make sure our class is in session.
3332
	require_once($sourcedir . '/Class-CacheAPI.php');
3333
3334
	// What accelerator we are going to try.
3335
	$tryAccelerator = !empty($overrideCache) ? $overrideCache : !empty($cache_accelerator) ? $cache_accelerator : 'smf';
3336
	$tryAccelerator = strtolower($tryAccelerator);
3337
3338
	// Do some basic tests.
3339
	if (file_exists($sourcedir . '/CacheAPI-' . $tryAccelerator . '.php'))
3340
	{
3341
		require_once($sourcedir . '/CacheAPI-' . $tryAccelerator . '.php');
3342
3343
		$cache_class_name = $tryAccelerator . '_cache';
3344
		$testAPI = new $cache_class_name();
3345
3346
		// No Support?  NEXT!
3347
		if (!$testAPI->isSupported())
3348
		{
3349
			// Can we save ourselves?
3350
			if (!empty($fallbackSMF) && is_null($overrideCache) && $tryAccelerator != 'smf')
3351
				return loadCacheAccelerator(null, false);
3352
			return false;
3353
		}
3354
3355
		// Connect up to the accelerator.
3356
		$testAPI->connect();
3357
3358
		// Don't set this if we are overriding the cache.
3359
		if (is_null($overrideCache))
3360
		{
3361
			$cacheAPI = $testAPI;
3362
			return $cacheAPI;
3363
		}
3364
		else
3365
			return $testAPI;
3366
	}
3367
}
3368
3369
/**
3370
 * Try to retrieve a cache entry. On failure, call the appropriate function.
3371
 *
3372
 * @param string $key The key for this entry
3373
 * @param string $file The file associated with this entry
3374
 * @param string $function The function to call
3375
 * @param array $params Parameters to be passed to the specified function
3376
 * @param int $level The cache level
3377
 * @return string The cached data
3378
 */
3379
function cache_quick_get($key, $file, $function, $params, $level = 1)
3380
{
3381
	global $modSettings, $sourcedir;
3382
3383
	// @todo Why are we doing this if caching is disabled?
3384
3385
	if (function_exists('call_integration_hook'))
3386
		call_integration_hook('pre_cache_quick_get', array(&$key, &$file, &$function, &$params, &$level));
3387
3388
	/* Refresh the cache if either:
3389
		1. Caching is disabled.
3390
		2. The cache level isn't high enough.
3391
		3. The item has not been cached or the cached item expired.
3392
		4. The cached item has a custom expiration condition evaluating to true.
3393
		5. The expire time set in the cache item has passed (needed for Zend).
3394
	*/
3395
	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
introduced by
The use of eval() is discouraged.
Loading history...
introduced by
The condition is_array($cache_block = ...e_get_data($key, 3600)) is always false.
Loading history...
3396
	{
3397
		require_once($sourcedir . '/' . $file);
3398
		$cache_block = call_user_func_array($function, $params);
3399
3400
		if (!empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= $level)
3401
			cache_put_data($key, $cache_block, $cache_block['expires'] - time());
3402
	}
3403
3404
	// Some cached data may need a freshening up after retrieval.
3405
	if (!empty($cache_block['post_retri_eval']))
3406
		eval($cache_block['post_retri_eval']);
0 ignored issues
show
introduced by
The use of eval() is discouraged.
Loading history...
3407
3408
	if (function_exists('call_integration_hook'))
3409
		call_integration_hook('post_cache_quick_get', array(&$cache_block));
3410
3411
	return $cache_block['data'];
3412
}
3413
3414
/**
3415
 * Puts value in the cache under key for ttl seconds.
3416
 *
3417
 * - It may "miss" so shouldn't be depended on
3418
 * - Uses the cache engine chosen in the ACP and saved in settings.php
3419
 * - It supports:
3420
 *	 Xcache: https://xcache.lighttpd.net/wiki/XcacheApi
3421
 *	 memcache: https://php.net/memcache
3422
 *	 APC: https://php.net/apc
3423
 *   APCu: https://php.net/book.apcu
3424
 *	 Zend: http://files.zend.com/help/Zend-Platform/output_cache_functions.htm
3425
 *	 Zend: http://files.zend.com/help/Zend-Platform/zend_cache_functions.htm
3426
 *
3427
 * @param string $key A key for this value
3428
 * @param mixed $value The data to cache
3429
 * @param int $ttl How long (in seconds) the data should be cached for
3430
 */
3431
function cache_put_data($key, $value, $ttl = 120)
3432
{
3433
	global $smcFunc, $cache_enable, $cacheAPI;
3434
	global $cache_hits, $cache_count, $db_show_debug;
3435
3436
	if (empty($cache_enable) || empty($cacheAPI))
3437
		return;
3438
3439
	$cache_count = isset($cache_count) ? $cache_count + 1 : 1;
3440
	if (isset($db_show_debug) && $db_show_debug === true)
3441
	{
3442
		$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)));
3443
		$st = microtime(true);
3444
	}
3445
3446
	// The API will handle the rest.
3447
	$value = $value === null ? null : (isset($smcFunc['json_encode']) ? $smcFunc['json_encode']($value) : json_encode($value));
3448
	$cacheAPI->putData($key, $value, $ttl);
3449
3450
	if (function_exists('call_integration_hook'))
3451
		call_integration_hook('cache_put_data', array(&$key, &$value, &$ttl));
3452
3453
	if (isset($db_show_debug) && $db_show_debug === true)
3454
		$cache_hits[$cache_count]['t'] = microtime(true) - $st;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $st does not seem to be defined for all execution paths leading up to this point.
Loading history...
3455
}
3456
3457
/**
3458
 * Gets the value from the cache specified by key, so long as it is not older than ttl seconds.
3459
 * - It may often "miss", so shouldn't be depended on.
3460
 * - It supports the same as cache_put_data().
3461
 *
3462
 * @param string $key The key for the value to retrieve
3463
 * @param int $ttl The maximum age of the cached data
3464
 * @return string The cached data or null if nothing was loaded
3465
 */
3466
function cache_get_data($key, $ttl = 120)
3467
{
3468
	global $smcFunc, $cache_enable, $cacheAPI;
3469
	global $cache_hits, $cache_count, $cache_misses, $cache_count_misses, $db_show_debug;
3470
3471
	if (empty($cache_enable) || empty($cacheAPI))
3472
		return;
3473
3474
	$cache_count = isset($cache_count) ? $cache_count + 1 : 1;
3475
	if (isset($db_show_debug) && $db_show_debug === true)
3476
	{
3477
		$cache_hits[$cache_count] = array('k' => $key, 'd' => 'get');
3478
		$st = microtime(true);
3479
		$original_key = $key;
3480
	}
3481
3482
	// Ask the API to get the data.
3483
	$value = $cacheAPI->getData($key, $ttl);
3484
3485
	if (isset($db_show_debug) && $db_show_debug === true)
3486
	{
3487
		$cache_hits[$cache_count]['t'] = microtime(true) - $st;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $st does not seem to be defined for all execution paths leading up to this point.
Loading history...
3488
		$cache_hits[$cache_count]['s'] = isset($value) ? strlen($value) : 0;
3489
3490
		if (empty($value))
3491
		{
3492
			if (!is_array($cache_misses))
3493
				$cache_misses = array();
3494
3495
			$cache_count_misses = isset($cache_count_misses) ? $cache_count_misses + 1 : 1;
3496
			$cache_misses[$cache_count_misses] = array('k' => $original_key, 'd' => 'get');
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $original_key does not seem to be defined for all execution paths leading up to this point.
Loading history...
3497
		}
3498
	}
3499
3500
	if (function_exists('call_integration_hook') && isset($value))
3501
		call_integration_hook('cache_get_data', array(&$key, &$ttl, &$value));
3502
3503
	return empty($value) ? null : (isset($smcFunc['json_decode']) ? $smcFunc['json_decode']($value, true) : smf_json_decode($value, true));
3504
}
3505
3506
/**
3507
 * Empty out the cache in use as best it can
3508
 *
3509
 * It may only remove the files of a certain type (if the $type parameter is given)
3510
 * Type can be user, data or left blank
3511
 * 	- user clears out user data
3512
 *  - data clears out system / opcode data
3513
 *  - If no type is specified will perform a complete cache clearing
3514
 * For cache engines that do not distinguish on types, a full cache flush will be done
3515
 *
3516
 * @param string $type The cache type ('memcached', 'apc', 'xcache', 'zend' or something else for SMF's file cache)
3517
 */
3518
function clean_cache($type = '')
3519
{
3520
	global $cacheAPI;
3521
3522
	// If we can't get to the API, can't do this.
3523
	if (empty($cacheAPI))
3524
		return;
3525
3526
	// Ask the API to do the heavy lifting. cleanCache also calls invalidateCache to be sure.
3527
	$cacheAPI->cleanCache($type);
3528
3529
	call_integration_hook('integrate_clean_cache');
3530
	clearstatcache();
3531
}
3532
3533
/**
3534
 * Helper function to set an array of data for an user's avatar.
3535
 *
3536
 * Makes assumptions based on the data provided, the following keys are required:
3537
 * - avatar The raw "avatar" column in members table
3538
 * - email The user's email. Used to get the gravatar info
3539
 * - filename The attachment filename
3540
 *
3541
 * @param array $data An array of raw info
3542
 * @return array An array of avatar data
3543
 */
3544
function set_avatar_data($data = array())
3545
{
3546
	global $modSettings, $smcFunc, $image_proxy_enabled, $user_info;
3547
3548
	// Come on!
3549
	if (empty($data))
3550
		return array();
3551
3552
	// Set a nice default var.
3553
	$image = '';
3554
3555
	// Gravatar has been set as mandatory!
3556
	if (!empty($modSettings['gravatarOverride']))
3557
	{
3558
		if (!empty($modSettings['gravatarAllowExtraEmail']) && !empty($data['avatar']) && stristr($data['avatar'], 'gravatar://'))
3559
			$image = get_gravatar_url($smcFunc['substr']($data['avatar'], 11));
3560
3561
		else if (!empty($data['email']))
3562
			$image = get_gravatar_url($data['email']);
3563
	}
3564
3565
	// Look if the user has a gravatar field or has set an external url as avatar.
3566
	else
3567
	{
3568
		// So it's stored in the member table?
3569
		if (!empty($data['avatar']))
3570
		{
3571
			// Gravatar.
3572
			if (stristr($data['avatar'], 'gravatar://'))
3573
			{
3574
				if ($data['avatar'] == 'gravatar://')
3575
					$image = get_gravatar_url($data['email']);
3576
3577
				elseif (!empty($modSettings['gravatarAllowExtraEmail']))
3578
					$image = get_gravatar_url($smcFunc['substr']($data['avatar'], 11));
3579
			}
3580
3581
			// External url.
3582
			else
3583
			{
3584
				// Using ssl?
3585
				if (!empty($modSettings['force_ssl']) && $image_proxy_enabled && stripos($data['avatar'], 'http://') !== false && empty($user_info['possibly_robot']))
3586
					$image = get_proxied_url($data['avatar']);
3587
3588
				// Just a plain external url.
3589
				else
3590
					$image = (stristr($data['avatar'], 'http://') || stristr($data['avatar'], 'https://')) ? $data['avatar'] : $modSettings['avatar_url'] . '/' . $data['avatar'];
3591
			}
3592
		}
3593
3594
		// Perhaps this user has an attachment as avatar...
3595
		else if (!empty($data['filename']))
3596
			$image = $modSettings['custom_avatar_url'] . '/' . $data['filename'];
3597
3598
		// Right... no avatar... use our default image.
3599
		else
3600
			$image = $modSettings['avatar_url'] . '/default.png';
3601
	}
3602
3603
	call_integration_hook('integrate_set_avatar_data', array(&$image, &$data));
3604
3605
	// 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.
3606
	if (!empty($image))
3607
		return array(
3608
			'name' => !empty($data['avatar']) ? $data['avatar'] : '',
3609
			'image' => '<img class="avatar" src="' . $image . '" />',
3610
			'href' => $image,
3611
			'url' => $image,
3612
		);
3613
3614
	// Fallback to make life easier for everyone...
3615
	else
3616
		return array(
3617
			'name' => '',
3618
			'image' => '',
3619
			'href' => '',
3620
			'url' => '',
3621
		);
3622
}
3623
3624
?>