prepareDBSettingContext()   F
last analyzed

Complexity

Conditions 68
Paths 144

Size

Total Lines 235
Code Lines 139

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 68
eloc 139
nc 144
nop 1
dl 0
loc 235
rs 3.0399
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
 * Contains all the functionality required to be able to edit the core server
5
 * settings. This includes anything from which an error may result in the forum
6
 * destroying itself in a firey fury.
7
 *
8
 * Adding options to one of the setting screens isn't hard. Call prepareDBSettingsContext;
9
 * The basic format for a checkbox is:
10
 * 		array('check', 'nameInModSettingsAndSQL'),
11
 * And for a text box:
12
 * 		array('text', 'nameInModSettingsAndSQL')
13
 * (NOTE: You have to add an entry for this at the bottom!)
14
 *
15
 * In these cases, it will look for $txt['nameInModSettingsAndSQL'] as the description,
16
 * and $helptxt['nameInModSettingsAndSQL'] as the help popup description.
17
 *
18
 * Here's a quick explanation of how to add a new item:
19
 *
20
 * - A text input box.  For textual values.
21
 * 		array('text', 'nameInModSettingsAndSQL', 'OptionalInputBoxWidth'),
22
 * - A text input box.  For numerical values.
23
 * 		array('int', 'nameInModSettingsAndSQL', 'OptionalInputBoxWidth'),
24
 * - A text input box.  For floating point values.
25
 * 		array('float', 'nameInModSettingsAndSQL', 'OptionalInputBoxWidth'),
26
 * - A large text input box. Used for textual values spanning multiple lines.
27
 * 		array('large_text', 'nameInModSettingsAndSQL', 'OptionalNumberOfRows'),
28
 * - A check box.  Either one or zero. (boolean)
29
 * 		array('check', 'nameInModSettingsAndSQL'),
30
 * - A selection box.  Used for the selection of something from a list.
31
 * 		array('select', 'nameInModSettingsAndSQL', array('valueForSQL' => $txt['displayedValue'])),
32
 * 		Note that just saying array('first', 'second') will put 0 in the SQL for 'first'.
33
 * - A password input box. Used for passwords, no less!
34
 * 		array('password', 'nameInModSettingsAndSQL', 'OptionalInputBoxWidth'),
35
 * - A permission - for picking groups who have a permission.
36
 * 		array('permissions', 'manage_groups'),
37
 * - A BBC selection box.
38
 * 		array('bbc', 'sig_bbc'),
39
 * - A list of boards to choose from
40
 *  	array('boards', 'likes_boards'),
41
 *  	Note that the storage in the database is as 1,2,3,4
42
 *
43
 * For each option:
44
 * 	- type (see above), variable name, size/possible values.
45
 * 	  OR make type '' for an empty string for a horizontal rule.
46
 *  - SET preinput - to put some HTML prior to the input box.
47
 *  - SET postinput - to put some HTML following the input box.
48
 *  - SET invalid - to mark the data as invalid.
49
 *  - PLUS you can override label and help parameters by forcing their keys in the array, for example:
50
 *  	array('text', 'invalidlabel', 3, 'label' => 'Actual Label')
51
 *
52
 * Simple Machines Forum (SMF)
53
 *
54
 * @package SMF
55
 * @author Simple Machines https://www.simplemachines.org
56
 * @copyright 2022 Simple Machines and individual contributors
57
 * @license https://www.simplemachines.org/about/smf/license.php BSD
58
 *
59
 * @version 2.1.2
60
 */
61
62
use SMF\Cache\CacheApi;
63
use SMF\Cache\CacheApiInterface;
64
65
if (!defined('SMF'))
66
	die('No direct access...');
67
68
/**
69
 * This is the main dispatcher. Sets up all the available sub-actions, all the tabs and selects
70
 * the appropriate one based on the sub-action.
71
 *
72
 * Requires the admin_forum permission.
73
 * Redirects to the appropriate function based on the sub-action.
74
 *
75
 * Uses edit_settings adminIndex.
76
 */
77
function ModifySettings()
78
{
79
	global $context, $txt, $boarddir;
80
81
	// This is just to keep the database password more secure.
82
	isAllowedTo('admin_forum');
83
84
	// Load up all the tabs...
85
	$context[$context['admin_menu_name']]['tab_data'] = array(
86
		'title' => $txt['admin_server_settings'],
87
		'help' => 'serversettings',
88
		'description' => $txt['admin_basic_settings'],
89
	);
90
91
	checkSession('request');
92
93
	// The settings are in here, I swear!
94
	loadLanguage('ManageSettings');
95
96
	$context['page_title'] = $txt['admin_server_settings'];
97
	$context['sub_template'] = 'show_settings';
98
99
	$subActions = array(
100
		'general' => 'ModifyGeneralSettings',
101
		'database' => 'ModifyDatabaseSettings',
102
		'cookie' => 'ModifyCookieSettings',
103
		'security' => 'ModifyGeneralSecuritySettings',
104
		'cache' => 'ModifyCacheSettings',
105
		'export' => 'ModifyExportSettings',
106
		'loads' => 'ModifyLoadBalancingSettings',
107
		'phpinfo' => 'ShowPHPinfoSettings',
108
	);
109
110
	// By default we're editing the core settings
111
	$_REQUEST['sa'] = isset($_REQUEST['sa']) && isset($subActions[$_REQUEST['sa']]) ? $_REQUEST['sa'] : 'general';
112
	$context['sub_action'] = $_REQUEST['sa'];
113
114
	// Warn the user if there's any relevant information regarding Settings.php.
115
	$settings_not_writable = !is_writable($boarddir . '/Settings.php');
116
	$settings_backup_fail = !@is_writable($boarddir . '/Settings_bak.php') || !@copy($boarddir . '/Settings.php', $boarddir . '/Settings_bak.php');
117
118
	if ($settings_backup_fail)
119
		$context['settings_message'] = array(
120
			'label' => $txt['admin_backup_fail'],
121
			'tag' => 'div',
122
			'class' => 'centertext strong'
123
		);
124
125
	$context['settings_not_writable'] = $settings_not_writable;
126
127
	call_integration_hook('integrate_server_settings', array(&$subActions));
128
129
	// Call the right function for this sub-action.
130
	call_helper($subActions[$_REQUEST['sa']]);
131
}
132
133
/**
134
 * General forum settings - forum name, maintenance mode, etc.
135
 * Practically, this shows an interface for the settings in Settings.php to be changed.
136
 *
137
 * - Requires the admin_forum permission.
138
 * - Uses the edit_settings administration area.
139
 * - Contains the actual array of settings to show from Settings.php.
140
 * - Accessed from ?action=admin;area=serversettings;sa=general.
141
 *
142
 * @param bool $return_config Whether to return the $config_vars array (for pagination purposes)
143
 * @return void|array Returns nothing or returns the $config_vars array if $return_config is true
144
 */
145
function ModifyGeneralSettings($return_config = false)
146
{
147
	global $scripturl, $context, $txt, $modSettings, $boardurl, $sourcedir, $smcFunc;
148
149
	/* If you're writing a mod, it's a bad idea to add things here....
150
	For each option:
151
		variable name, description, type (constant), size/possible values, helptext, optional 'min' (minimum value for float/int, defaults to 0), optional 'max' (maximum value for float/int), optional 'step' (amount to increment/decrement value for float/int)
152
	OR	an empty string for a horizontal rule.
153
	OR	a string for a titled section. */
154
	$config_vars = array(
155
		array('mbname', $txt['admin_title'], 'file', 'text', 30),
156
		'',
157
		array('maintenance', $txt['admin_maintain'], 'file', 'check'),
158
		array('mtitle', $txt['maintenance_subject'], 'file', 'text', 36),
159
		array('mmessage', $txt['maintenance_message'], 'file', 'text', 36),
160
		'',
161
		array('webmaster_email', $txt['admin_webmaster_email'], 'file', 'text', 30),
162
		'',
163
		array('enableCompressedOutput', $txt['enableCompressedOutput'], 'db', 'check', null, 'enableCompressedOutput'),
164
		array('disableHostnameLookup', $txt['disableHostnameLookup'], 'db', 'check', null, 'disableHostnameLookup'),
165
		'',
166
		'force_ssl' => array('force_ssl', $txt['force_ssl'], 'db', 'select', array($txt['force_ssl_off'], $txt['force_ssl_complete']), 'force_ssl'),
167
		array('image_proxy_enabled', $txt['image_proxy_enabled'], 'file', 'check', null, 'image_proxy_enabled'),
168
		array('image_proxy_secret', $txt['image_proxy_secret'], 'file', 'text', 30, 'image_proxy_secret'),
169
		array('image_proxy_maxsize', $txt['image_proxy_maxsize'], 'file', 'int', null, 'image_proxy_maxsize'),
170
		'',
171
		array('enable_sm_stats', $txt['enable_sm_stats'], 'db', 'check', null, 'enable_sm_stats'),
172
	);
173
174
	call_integration_hook('integrate_general_settings', array(&$config_vars));
175
176
	if ($return_config)
177
		return $config_vars;
178
179
	// If no cert, force_ssl must remain 0 (The admin search doesn't require this)
180
	$config_vars['force_ssl']['disabled'] = empty($modSettings['force_ssl']) && !ssl_cert_found($boardurl);
181
182
	// Setup the template stuff.
183
	$context['post_url'] = $scripturl . '?action=admin;area=serversettings;sa=general;save';
184
	$context['settings_title'] = $txt['general_settings'];
185
	$context['save_disabled'] = $context['settings_not_writable'];
186
187
	// Saving settings?
188
	if (isset($_REQUEST['save']))
189
	{
190
		call_integration_hook('integrate_save_general_settings');
191
192
		foreach ($config_vars as $config_var)
193
		{
194
			if (is_array($config_var) && isset($config_var[3]) && $config_var[3] == 'text' && !empty($_POST[$config_var[0]]))
195
				$_POST[$config_var[0]] = $smcFunc['normalize']($_POST[$config_var[0]]);
196
		}
197
198
		// Are we saving the stat collection?
199
		if (!empty($_POST['enable_sm_stats']) && empty($modSettings['sm_stats_key']))
200
		{
201
			$registerSMStats = registerSMStats();
202
203
			// Failed to register, disable it again.
204
			if (empty($registerSMStats))
205
				$_POST['enable_sm_stats'] = 0;
206
		}
207
208
		// Ensure all URLs are aligned with the new force_ssl setting
209
		// Treat unset like 0
210
		if (isset($_POST['force_ssl']))
211
			AlignURLsWithSSLSetting($_POST['force_ssl']);
212
		else
213
			AlignURLsWithSSLSetting(0);
214
215
		saveSettings($config_vars);
216
		$_SESSION['adm-save'] = true;
217
		redirectexit('action=admin;area=serversettings;sa=general;' . $context['session_var'] . '=' . $context['session_id']);
218
	}
219
220
	// Fill the config array.
221
	prepareServerSettingsContext($config_vars);
222
223
	// Some javascript for SSL
224
	if (empty($context['settings_not_writable']))
225
		addInlineJavaScript('
226
$(function()
227
{
228
	$("#force_ssl").change(function()
229
	{
230
		var mode = $(this).val() == 1 ? false : true;
231
		$("#image_proxy_enabled").prop("disabled", mode);
232
		$("#image_proxy_secret").prop("disabled", mode);
233
		$("#image_proxy_maxsize").prop("disabled", mode);
234
	}).change();
235
});', true);
236
}
237
238
/**
239
 * Align URLs with SSL Setting.
240
 *
241
 * If force_ssl has changed, ensure all URLs are aligned with the new setting.
242
 * This includes:
243
 *     - $boardurl
244
 *     - $modSettings['smileys_url']
245
 *     - $modSettings['avatar_url']
246
 *     - $modSettings['custom_avatar_url'] - if found
247
 *     - theme_url - all entries in the themes table
248
 *     - images_url - all entries in the themes table
249
 *
250
 * This function will NOT overwrite URLs that are not subfolders of $boardurl.
251
 * The admin must have pointed those somewhere else on purpose, so they must be updated manually.
252
 *
253
 * A word of caution: You can't trust the http/https scheme reflected for these URLs in $globals
254
 * (e.g., $boardurl) or in $modSettings.  This is because SMF may change them in memory to comply
255
 * with the force_ssl setting - a soft redirect may be in effect...  Thus, conditional updates
256
 * to these values do not work.  You gotta just brute force overwrite them based on force_ssl.
257
 *
258
 * @param int $new_force_ssl is the current force_ssl setting.
259
 * @return void Returns nothing, just does its job
260
 */
261
function AlignURLsWithSSLSetting($new_force_ssl = 0)
262
{
263
	global $boardurl, $modSettings, $sourcedir, $smcFunc;
264
	require_once($sourcedir . '/Subs-Admin.php');
265
266
	// Check $boardurl
267
	if (!empty($new_force_ssl))
268
		$newval = strtr($boardurl, array('http://' => 'https://'));
269
	else
270
		$newval = strtr($boardurl, array('https://' => 'http://'));
271
	updateSettingsFile(array('boardurl' => $newval));
272
273
	$new_settings = array();
274
275
	// Check $smileys_url, but only if it points to a subfolder of $boardurl
276
	if (BoardurlMatch($modSettings['smileys_url']))
277
	{
278
		if (!empty($new_force_ssl))
279
			$newval = strtr($modSettings['smileys_url'], array('http://' => 'https://'));
280
		else
281
			$newval = strtr($modSettings['smileys_url'], array('https://' => 'http://'));
282
		$new_settings['smileys_url'] = $newval;
283
	}
284
285
	// Check $avatar_url, but only if it points to a subfolder of $boardurl
286
	if (BoardurlMatch($modSettings['avatar_url']))
287
	{
288
		if (!empty($new_force_ssl))
289
			$newval = strtr($modSettings['avatar_url'], array('http://' => 'https://'));
290
		else
291
			$newval = strtr($modSettings['avatar_url'], array('https://' => 'http://'));
292
		$new_settings['avatar_url'] = $newval;
293
	}
294
295
	// Check $custom_avatar_url, but only if it points to a subfolder of $boardurl
296
	// This one had been optional in the past, make sure it is set first
297
	if (isset($modSettings['custom_avatar_url']) && BoardurlMatch($modSettings['custom_avatar_url']))
298
	{
299
		if (!empty($new_force_ssl))
300
			$newval = strtr($modSettings['custom_avatar_url'], array('http://' => 'https://'));
301
		else
302
			$newval = strtr($modSettings['custom_avatar_url'], array('https://' => 'http://'));
303
		$new_settings['custom_avatar_url'] = $newval;
304
	}
305
306
	// Save updates to the settings table
307
	if (!empty($new_settings))
308
		updateSettings($new_settings, true);
309
310
	// Now we move onto the themes.
311
	// First, get a list of theme URLs...
312
	$request = $smcFunc['db_query']('', '
313
		SELECT id_theme, variable, value
314
		FROM {db_prefix}themes
315
		WHERE variable in ({string:themeurl}, {string:imagesurl})
316
			AND id_member = {int:zero}',
317
		array(
318
			'themeurl' => 'theme_url',
319
			'imagesurl' => 'images_url',
320
			'zero' => 0,
321
		)
322
	);
323
324
	while ($row = $smcFunc['db_fetch_assoc']($request))
325
	{
326
		// First check to see if it points to a subfolder of $boardurl
327
		if (BoardurlMatch($row['value']))
328
		{
329
			if (!empty($new_force_ssl))
330
				$newval = strtr($row['value'], array('http://' => 'https://'));
331
			else
332
				$newval = strtr($row['value'], array('https://' => 'http://'));
333
334
			$smcFunc['db_query']('', '
335
				UPDATE {db_prefix}themes
336
				SET value = {string:theme_val}
337
				WHERE variable = {string:theme_var}
338
					AND id_theme = {string:theme_id}
339
					AND id_member = {int:zero}',
340
				array(
341
					'theme_val' => $newval,
342
					'theme_var' => $row['variable'],
343
					'theme_id' => $row['id_theme'],
344
					'zero' => 0,
345
				)
346
			);
347
		}
348
	}
349
	$smcFunc['db_free_result']($request);
350
}
351
352
/**
353
 * $boardurl Match.
354
 *
355
 * Helper function to see if the url being checked is based off of $boardurl.
356
 * If not, it was overridden by the admin to some other value on purpose, and should not
357
 * be stepped on by SMF when aligning URLs with the force_ssl setting.
358
 * The site admin must change URLs that are not aligned with $boardurl manually.
359
 *
360
 * @param string $url is the url to check.
361
 * @return bool Returns true if the url is based off of $boardurl (without the scheme), false if not
362
 */
363
function BoardurlMatch($url = '')
364
{
365
	global $boardurl;
366
367
	// Strip the schemes
368
	$urlpath = strtr($url, array('http://' => '', 'https://' => ''));
369
	$boardurlpath = strtr($boardurl, array('http://' => '', 'https://' => ''));
370
371
	// If leftmost portion of path matches boardurl, return true
372
	$result = strpos($urlpath, $boardurlpath);
373
	if ($result === false || $result != 0)
374
		return false;
375
	else
376
		return true;
377
}
378
379
/**
380
 * Basic database and paths settings - database name, host, etc.
381
 *
382
 * - It shows an interface for the settings in Settings.php to be changed.
383
 * - It contains the actual array of settings to show from Settings.php.
384
 * - Requires the admin_forum permission.
385
 * - Uses the edit_settings administration area.
386
 * - Accessed from ?action=admin;area=serversettings;sa=database.
387
 *
388
 * @param bool $return_config Whether or not to return the config_vars array (used for admin search)
389
 * @return void|array Returns nothing or returns the $config_vars array if $return_config is true
390
 */
391
function ModifyDatabaseSettings($return_config = false)
392
{
393
	global $scripturl, $context, $txt, $smcFunc;
394
	db_extend('extra');
395
396
	/* If you're writing a mod, it's a bad idea to add things here....
397
		For each option:
398
		variable name, description, type (constant), size/possible values, helptext, optional 'min' (minimum value for float/int, defaults to 0), optional 'max' (maximum value for float/int), optional 'step' (amount to increment/decrement value for float/int)
399
		OR an empty string for a horizontal rule.
400
		OR a string for a titled section. */
401
	$config_vars = array(
402
		array('db_persist', $txt['db_persist'], 'file', 'check', null, 'db_persist'),
403
		array('db_error_send', $txt['db_error_send'], 'file', 'check'),
404
		array('ssi_db_user', $txt['ssi_db_user'], 'file', 'text', null, 'ssi_db_user'),
405
		array('ssi_db_passwd', $txt['ssi_db_passwd'], 'file', 'password'),
406
		'',
407
		array('autoFixDatabase', $txt['autoFixDatabase'], 'db', 'check', false, 'autoFixDatabase')
408
	);
409
410
	// Add PG Stuff
411
	if ($smcFunc['db_title'] === POSTGRE_TITLE)
412
	{
413
		$request = $smcFunc['db_query']('', 'SELECT cfgname FROM pg_ts_config', array());
414
		$fts_language = array();
415
416
		while ($row = $smcFunc['db_fetch_assoc']($request))
417
			$fts_language[$row['cfgname']] = $row['cfgname'];
418
419
		$config_vars = array_merge($config_vars, array(
420
				'',
421
				array('search_language', $txt['search_language'], 'db', 'select', $fts_language, 'pgFulltextSearch')
422
			)
423
		);
424
	}
425
426
	call_integration_hook('integrate_database_settings', array(&$config_vars));
427
428
	if ($return_config)
429
		return $config_vars;
430
431
	// Setup the template stuff.
432
	$context['post_url'] = $scripturl . '?action=admin;area=serversettings;sa=database;save';
433
	$context['settings_title'] = $txt['database_settings'];
434
	$context['save_disabled'] = $context['settings_not_writable'];
435
436
	if (!$smcFunc['db_allow_persistent']())
437
		addInlineJavaScript('
438
			$(function()
439
			{
440
				$("#db_persist").prop("disabled", true);
441
			});', true);
442
443
	// Saving settings?
444
	if (isset($_REQUEST['save']))
445
	{
446
		call_integration_hook('integrate_save_database_settings');
447
448
		saveSettings($config_vars);
449
		$_SESSION['adm-save'] = true;
450
		redirectexit('action=admin;area=serversettings;sa=database;' . $context['session_var'] . '=' . $context['session_id']);
451
	}
452
453
	// Fill the config array.
454
	prepareServerSettingsContext($config_vars);
455
}
456
457
/**
458
 * This function handles cookies settings modifications.
459
 *
460
 * @param bool $return_config Whether or not to return the config_vars array (used for admin search)
461
 * @return void|array Returns nothing or returns the $config_vars array if $return_config is true
462
 */
463
function ModifyCookieSettings($return_config = false)
464
{
465
	global $context, $scripturl, $txt, $sourcedir, $modSettings, $cookiename, $user_settings, $boardurl, $smcFunc;
466
467
	// Define the variables we want to edit.
468
	$config_vars = array(
469
		// Cookies...
470
		array('cookiename', $txt['cookie_name'], 'file', 'text', 20),
471
		array('cookieTime', $txt['cookieTime'], 'db', 'select', array_filter(array_map(
472
			function ($str) use ($txt)
473
			{
474
				return isset($txt[$str]) ? $txt[$str] : '';
475
			},
476
			$context['login_cookie_times']
477
		))),
478
		array('localCookies', $txt['localCookies'], 'db', 'check', false, 'localCookies'),
479
		array('globalCookies', $txt['globalCookies'], 'db', 'check', false, 'globalCookies'),
480
		array('globalCookiesDomain', $txt['globalCookiesDomain'], 'db', 'text', false, 'globalCookiesDomain'),
481
		array('secureCookies', $txt['secureCookies'], 'db', 'check', false, 'secureCookies', 'disabled' => !httpsOn()),
482
		array('httponlyCookies', $txt['httponlyCookies'], 'db', 'check', false, 'httponlyCookies'),
483
		array('samesiteCookies', $txt['samesiteCookies'], 'db', 'select', array(
484
				'none' 		=> $txt['samesiteNone'],
485
				'lax' 		=> $txt['samesiteLax'],
486
				'strict' 	=> $txt['samesiteStrict']
487
			),
488
			'samesiteCookies'),
489
		'',
490
		// Sessions
491
		array('databaseSession_enable', $txt['databaseSession_enable'], 'db', 'check', false, 'databaseSession_enable'),
492
		array('databaseSession_loose', $txt['databaseSession_loose'], 'db', 'check', false, 'databaseSession_loose'),
493
		array('databaseSession_lifetime', $txt['databaseSession_lifetime'], 'db', 'int', false, 'databaseSession_lifetime', 'postinput' => $txt['seconds']),
494
		'',
495
		// 2FA
496
		array('tfa_mode', $txt['tfa_mode'], 'db', 'select', array(
497
			0 => $txt['tfa_mode_disabled'],
498
			1 => $txt['tfa_mode_enabled'],
499
		) + (empty($user_settings['tfa_secret']) ? array() : array(
500
			2 => $txt['tfa_mode_forced'],
501
		)) + (empty($user_settings['tfa_secret']) ? array() : array(
502
			3 => $txt['tfa_mode_forcedall'],
503
		)), 'subtext' => $txt['tfa_mode_subtext'] . (empty($user_settings['tfa_secret']) ? '<br><strong>' . $txt['tfa_mode_forced_help'] . '</strong>' : ''), 'tfa_mode'),
504
	);
505
506
	addInlineJavaScript('
507
	function hideGlobalCookies()
508
	{
509
		var usingLocal = $("#localCookies").prop("checked");
510
		$("#setting_globalCookies").closest("dt").toggle(!usingLocal);
511
		$("#globalCookies").closest("dd").toggle(!usingLocal);
512
513
		var usingGlobal = !usingLocal && $("#globalCookies").prop("checked");
514
		$("#setting_globalCookiesDomain").closest("dt").toggle(usingGlobal);
515
		$("#globalCookiesDomain").closest("dd").toggle(usingGlobal);
516
	};
517
	hideGlobalCookies();
518
519
	$("#localCookies, #globalCookies").click(function() {
520
		hideGlobalCookies();
521
	});
522
	', true);
523
524
	if (empty($user_settings['tfa_secret']))
525
		addInlineJavaScript('');
526
527
	call_integration_hook('integrate_cookie_settings', array(&$config_vars));
528
529
	if ($return_config)
530
		return $config_vars;
531
532
	$context['post_url'] = $scripturl . '?action=admin;area=serversettings;sa=cookie;save';
533
	$context['settings_title'] = $txt['cookies_sessions_settings'];
534
	$context['save_disabled'] = $context['settings_not_writable'];
535
536
	// Saving settings?
537
	if (isset($_REQUEST['save']))
538
	{
539
		call_integration_hook('integrate_save_cookie_settings');
540
541
		$_POST['cookiename'] = $smcFunc['normalize']($_POST['cookiename']);
542
543
		// Local and global do not play nicely together.
544
		if (!empty($_POST['localCookies']) && empty($_POST['globalCookies']))
545
			unset ($_POST['globalCookies']);
546
547
		if (empty($modSettings['localCookies']) != empty($_POST['localCookies']) || empty($modSettings['globalCookies']) != empty($_POST['globalCookies']))
548
			$scope_changed = true;
549
550
		if (!empty($_POST['globalCookiesDomain']))
551
		{
552
			$_POST['globalCookiesDomain'] = parse_iri(normalize_iri((strpos($_POST['globalCookiesDomain'], '//') === false ? 'http://' : '') . ltrim($_POST['globalCookiesDomain'], '.')), PHP_URL_HOST);
553
554
			if (!preg_match('/(?:^|\.)' . preg_quote($_POST['globalCookiesDomain'], '/') . '$/u', parse_iri($boardurl, PHP_URL_HOST)))
555
				fatal_lang_error('invalid_cookie_domain', false);
556
		}
557
558
		// Per spec, if samesite setting is 'none', cookies MUST be secure. Thems the rules. Else you lock everyone out...
559
		if (!empty($_POST['samesiteCookies']) && ($_POST['samesiteCookies'] === 'none') && empty($_POST['secureCookies']))
560
			fatal_lang_error('samesiteSecureRequired', false);
561
562
		saveSettings($config_vars);
563
564
		// If the cookie name or scope were changed, reset the cookie.
565
		if ($cookiename != $_POST['cookiename'] || !empty($scope_changed))
566
		{
567
			$original_session_id = $context['session_id'];
568
			include_once($sourcedir . '/Subs-Auth.php');
569
570
			// Remove the old cookie.
571
			setLoginCookie(-3600, 0);
572
573
			// Set the new one.
574
			$cookiename = !empty($_POST['cookiename']) ? $_POST['cookiename'] : $cookiename;
575
			setLoginCookie(60 * $modSettings['cookieTime'], $user_settings['id_member'], hash_salt($user_settings['passwd'], $user_settings['password_salt']));
576
577
			redirectexit('action=admin;area=serversettings;sa=cookie;' . $context['session_var'] . '=' . $original_session_id, $context['server']['needs_login_fix']);
578
		}
579
580
		//If we disabled 2FA, reset all members and membergroups settings.
581
		if (isset($_POST['tfa_mode']) && empty($_POST['tfa_mode']))
582
		{
583
			$smcFunc['db_query']('', '
584
				UPDATE {db_prefix}membergroups
585
				SET tfa_required = {int:zero}',
586
				array(
587
					'zero' => 0,
588
				)
589
			);
590
			$smcFunc['db_query']('', '
591
				UPDATE {db_prefix}members
592
				SET tfa_secret = {string:empty}, tfa_backup = {string:empty}',
593
				array(
594
					'empty' => '',
595
				)
596
			);
597
		}
598
599
		$_SESSION['adm-save'] = true;
600
		redirectexit('action=admin;area=serversettings;sa=cookie;' . $context['session_var'] . '=' . $context['session_id']);
601
	}
602
603
	// Fill the config array.
604
	prepareServerSettingsContext($config_vars);
605
}
606
607
/**
608
 * Settings really associated with general security aspects.
609
 *
610
 * @param bool $return_config Whether or not to return the config_vars array (used for admin search)
611
 * @return void|array Returns nothing or returns the $config_vars array if $return_config is true
612
 */
613
function ModifyGeneralSecuritySettings($return_config = false)
614
{
615
	global $txt, $scripturl, $context;
616
617
	$config_vars = array(
618
		array('int', 'failed_login_threshold'),
619
		array('int', 'loginHistoryDays', 'subtext' => $txt['zero_to_disable']),
620
		'',
621
622
		array('check', 'securityDisable'),
623
		array('check', 'securityDisable_moderate'),
624
		'',
625
626
		// Reactive on email, and approve on delete
627
		array('check', 'send_validation_onChange'),
628
		array('check', 'approveAccountDeletion'),
629
		'',
630
631
		// Password strength.
632
		array(
633
			'select',
634
			'password_strength',
635
			array(
636
				$txt['setting_password_strength_low'],
637
				$txt['setting_password_strength_medium'],
638
				$txt['setting_password_strength_high']
639
			)
640
		),
641
		array('check', 'enable_password_conversion'),
642
		'',
643
644
		// Reporting of personal messages?
645
		array('check', 'enableReportPM'),
646
		'',
647
648
		array('check', 'allow_cors'),
649
		array('check', 'allow_cors_credentials'),
650
		array('text', 'cors_domains'),
651
		array('text', 'cors_headers'),
652
		'',
653
654
		array(
655
			'select',
656
			'frame_security',
657
			array(
658
				'SAMEORIGIN' => $txt['setting_frame_security_SAMEORIGIN'],
659
				'DENY' => $txt['setting_frame_security_DENY'],
660
				'DISABLE' => $txt['setting_frame_security_DISABLE']
661
			)
662
		),
663
		'',
664
665
		array(
666
			'select',
667
			'proxy_ip_header',
668
			array(
669
				'disabled' => $txt['setting_proxy_ip_header_disabled'],
670
				'autodetect' => $txt['setting_proxy_ip_header_autodetect'],
671
				'HTTP_X_FORWARDED_FOR' => 'X-Forwarded-For',
672
				'HTTP_CLIENT_IP' => 'Client-IP',
673
				'HTTP_X_REAL_IP' => 'X-Real-IP',
674
				'HTTP_CF_CONNECTING_IP' => 'CF-Connecting-IP'
675
			)
676
		),
677
		array('text', 'proxy_ip_servers'),
678
	);
679
680
	call_integration_hook('integrate_general_security_settings', array(&$config_vars));
681
682
	if ($return_config)
683
		return $config_vars;
684
685
	// Saving?
686
	if (isset($_GET['save']))
687
	{
688
		if (!empty($_POST['cors_domains']))
689
		{
690
			$cors_domains = explode(',', $_POST['cors_domains']);
691
692
			foreach ($cors_domains as &$cors_domain)
693
			{
694
				if (strpos($cors_domain, '//') === false)
695
					$cors_domain = '//' . $cors_domain;
696
697
				$temp = parse_iri(normalize_iri($cors_domain));
698
699
				if (strpos($temp['host'], '*') !== false)
700
					$temp['host'] = substr($temp['host'], strrpos($temp['host'], '*'));
701
702
				$cors_domain = (!empty($temp['scheme']) ? $temp['scheme'] . '://' : '') . $temp['host'] . (!empty($temp['port']) ? ':' . $temp['port'] : '');
703
			}
704
705
			$_POST['cors_domains'] = implode(',', $cors_domains);
706
		}
707
708
		saveDBSettings($config_vars);
709
		$_SESSION['adm-save'] = true;
710
711
		call_integration_hook('integrate_save_general_security_settings');
712
713
		writeLog();
714
		redirectexit('action=admin;area=serversettings;sa=security;' . $context['session_var'] . '=' . $context['session_id']);
715
	}
716
717
	$context['post_url'] = $scripturl . '?action=admin;area=serversettings;save;sa=security';
718
	$context['settings_title'] = $txt['security_settings'];
719
720
	prepareDBSettingContext($config_vars);
721
}
722
723
/**
724
 * Simply modifying cache functions
725
 *
726
 * @param bool $return_config Whether or not to return the config_vars array (used for admin search)
727
 * @return void|array Returns nothing or returns the $config_vars array if $return_config is true
728
 */
729
function ModifyCacheSettings($return_config = false)
730
{
731
	global $context, $scripturl, $txt, $cacheAPI, $cache_enable, $cache_accelerator;
732
733
	// Detect all available optimizers
734
	$detectedCacheApis = loadCacheAPIs();
735
	$apis_names = array();
736
737
	/* @var CacheApiInterface $cache_api */
738
	foreach ($detectedCacheApis as $class_name => $cache_api)
739
	{
740
		$class_name_txt_key = strtolower($cache_api->getImplementationClassKeyName());
741
742
		$apis_names[$class_name] = isset($txt[$class_name_txt_key . '_cache']) ?
743
			$txt[$class_name_txt_key . '_cache'] : $class_name;
744
	}
745
746
	// set our values to show what, if anything, we found
747
	if (empty($detectedCacheApis))
748
	{
749
		$txt['cache_settings_message'] = '<strong class="alert">' . $txt['detected_no_caching'] . '</strong>';
750
		$cache_level = array($txt['cache_off']);
751
		$apis_names['none'] = $txt['cache_off'];
752
	}
753
754
	else
755
	{
756
		$txt['cache_settings_message'] = '<strong class="success">' .
757
			sprintf($txt['detected_accelerators'], implode(', ', $apis_names)) . '</strong>';
758
759
		$cache_level = array($txt['cache_off'], $txt['cache_level1'], $txt['cache_level2'], $txt['cache_level3']);
760
	}
761
762
	// Define the variables we want to edit.
763
	$config_vars = array(
764
		// Only a few settings, but they are important
765
		array('', $txt['cache_settings_message'], '', 'desc'),
766
		array('cache_enable', $txt['cache_enable'], 'file', 'select', $cache_level, 'cache_enable'),
767
		array('cache_accelerator', $txt['cache_accelerator'], 'file', 'select', $apis_names),
768
	);
769
770
	// some javascript to enable / disable certain settings if the option is not selected
771
	$context['settings_post_javascript'] = '
772
		$(document).ready(function() {
773
			$("#cache_accelerator").change();
774
		});';
775
776
	call_integration_hook('integrate_modify_cache_settings', array(&$config_vars));
777
778
	// Maybe we have some additional settings from the selected accelerator.
779
	if (!empty($detectedCacheApis))
780
		/* @var CacheApiInterface $cache_api */
781
		foreach ($detectedCacheApis as $class_name_txt_key => $cache_api)
782
			if (is_callable(array($cache_api, 'cacheSettings')))
783
				$cache_api->cacheSettings($config_vars);
784
785
	if ($return_config)
786
		return $config_vars;
787
788
	// Saving again?
789
	if (isset($_GET['save']))
790
	{
791
		call_integration_hook('integrate_save_cache_settings');
792
793
		if (is_callable(array($cacheAPI, 'cleanCache')) && ((int) $_POST['cache_enable'] < $cache_enable || $_POST['cache_accelerator'] != $cache_accelerator))
794
		{
795
			$cacheAPI->cleanCache();
796
		}
797
798
		saveSettings($config_vars);
799
		$_SESSION['adm-save'] = true;
800
801
		// We need to save the $cache_enable to $modSettings as well
802
		updateSettings(array('cache_enable' => (int) $_POST['cache_enable']));
803
804
		// exit so we reload our new settings on the page
805
		redirectexit('action=admin;area=serversettings;sa=cache;' . $context['session_var'] . '=' . $context['session_id']);
806
	}
807
808
	loadLanguage('ManageMaintenance');
809
	createToken('admin-maint');
810
	$context['template_layers'][] = 'clean_cache_button';
811
812
	$context['post_url'] = $scripturl . '?action=admin;area=serversettings;sa=cache;save';
813
	$context['settings_title'] = $txt['caching_settings'];
814
815
	// Changing cache settings won't have any effect if Settings.php is not writable.
816
	$context['save_disabled'] = $context['settings_not_writable'];
817
818
	// Decide what message to show.
819
	if (!$context['save_disabled'])
820
		$context['settings_message'] = $txt['caching_information'];
821
822
	// Prepare the template.
823
	prepareServerSettingsContext($config_vars);
824
}
825
826
/**
827
 * Controls settings for data export functionality
828
 *
829
 * @param bool $return_config Whether or not to return the config_vars array (used for admin search)
830
 * @return void|array Returns nothing or returns the $config_vars array if $return_config is true
831
 */
832
function ModifyExportSettings($return_config = false)
833
{
834
	global $context, $scripturl, $txt, $modSettings, $boarddir, $sourcedir;
835
836
	// Fill in a default value for this if it is missing.
837
	if (empty($modSettings['export_dir']))
838
		$modSettings['export_dir'] = $boarddir . DIRECTORY_SEPARATOR . 'exports';
839
840
	/*
841
		Some paranoid hosts worry that the disk space functions pose a security
842
		risk. Usually these hosts just disable the functions and move on, which
843
		is fine. A rare few, however, are not only paranoid, but also think it'd
844
		be a "clever" security move to overload the disk space functions with
845
		custom code that intentionally delivers false information, which is
846
		idiotic and evil. At any rate, if the functions are unavailable or if
847
		they report obviously insane values, it's not possible to track disk
848
		usage correctly.
849
	 */
850
	$diskspace_disabled = (!function_exists('disk_free_space') || !function_exists('disk_total_space') || intval(@disk_total_space(file_exists($modSettings['export_dir']) ? $modSettings['export_dir'] : $boarddir)) < 1440);
851
852
	$context['settings_message'] = $txt['export_settings_description'];
853
854
	$config_vars = array(
855
		array('text', 'export_dir', 40),
856
		array('int', 'export_expiry', 'subtext' => $txt['zero_to_disable'], 'postinput' => $txt['days_word']),
857
		array('int', 'export_min_diskspace_pct', 'postinput' => '%', 'max' => 80, 'disabled' => $diskspace_disabled),
858
		array('int', 'export_rate', 'min' => 5, 'max' => 500, 'step' => 5, 'subtext' => $txt['export_rate_desc']),
859
	);
860
861
	call_integration_hook('integrate_export_settings', array(&$config_vars));
862
863
	if ($return_config)
864
		return $config_vars;
865
866
	if (isset($_REQUEST['save']))
867
	{
868
		$prev_export_dir = is_dir($modSettings['export_dir']) ? rtrim($modSettings['export_dir'], '/\\') : '';
869
870
		if (!empty($_POST['export_dir']))
871
			$_POST['export_dir'] = rtrim($_POST['export_dir'], '/\\');
872
873
		if ($diskspace_disabled)
874
			$_POST['export_min_diskspace_pct'] = 0;
875
876
		$_POST['export_rate'] = max(5, min($_POST['export_rate'], 500));
877
878
		saveDBSettings($config_vars);
879
880
		// Create the new directory, but revert to the previous one if anything goes wrong.
881
		require_once($sourcedir . '/Profile-Export.php');
882
		create_export_dir($prev_export_dir);
883
884
		// Ensure we don't lose track of any existing export files.
885
		if (!empty($prev_export_dir) && $prev_export_dir != $modSettings['export_dir'])
886
		{
887
			$export_files = glob($prev_export_dir . DIRECTORY_SEPARATOR . '*');
888
889
			foreach ($export_files as $export_file)
890
			{
891
				if (!in_array(basename($export_file), array('index.php', '.htaccess')))
892
				{
893
					rename($export_file, $modSettings['export_dir'] . DIRECTORY_SEPARATOR . basename($export_file));
894
				}
895
			}
896
		}
897
898
		call_integration_hook('integrate_save_export_settings');
899
900
		$_SESSION['adm-save'] = true;
901
		redirectexit('action=admin;area=serversettings;sa=export;' . $context['session_var'] . '=' . $context['session_id']);
902
	}
903
904
	$context['post_url'] = $scripturl . '?action=admin;area=serversettings;sa=export;save';
905
	$context['settings_title'] = $txt['export_settings'];
906
907
	prepareDBSettingContext($config_vars);
908
}
909
910
/**
911
 * Allows to edit load balancing settings.
912
 *
913
 * @param bool $return_config Whether or not to return the config_vars array
914
 * @return void|array Returns nothing or returns the $config_vars array if $return_config is true
915
 */
916
function ModifyLoadBalancingSettings($return_config = false)
917
{
918
	global $txt, $scripturl, $context, $modSettings;
919
920
	// Setup a warning message, but disabled by default.
921
	$disabled = true;
922
	$context['settings_message'] = array('label' => $txt['loadavg_disabled_conf'], 'class' => 'error');
923
924
	if (DIRECTORY_SEPARATOR === '\\')
925
	{
926
		$context['settings_message']['label'] = $txt['loadavg_disabled_windows'];
927
		if (isset($_GET['save']))
928
			$_SESSION['adm-save'] = $context['settings_message']['label'];
929
	}
930
	elseif (stripos(PHP_OS, 'darwin') === 0)
931
	{
932
		$context['settings_message']['label'] = $txt['loadavg_disabled_osx'];
933
		if (isset($_GET['save']))
934
			$_SESSION['adm-save'] = $context['settings_message']['label'];
935
	}
936
	else
937
	{
938
		$modSettings['load_average'] = @file_get_contents('/proc/loadavg');
939
		if (!empty($modSettings['load_average']) && preg_match('~^([^ ]+?) ([^ ]+?) ([^ ]+)~', $modSettings['load_average'], $matches) !== 0)
940
			$modSettings['load_average'] = (float) $matches[1];
941
		elseif (($modSettings['load_average'] = @`uptime`) !== null && preg_match('~load averages?: (\d+\.\d+), (\d+\.\d+), (\d+\.\d+)~i', $modSettings['load_average'], $matches) !== 0)
942
			$modSettings['load_average'] = (float) $matches[1];
943
		else
944
			unset($modSettings['load_average']);
945
946
		if (!empty($modSettings['load_average']) || (isset($modSettings['load_average']) && $modSettings['load_average'] === 0.0))
947
		{
948
			$context['settings_message']['label'] = sprintf($txt['loadavg_warning'], $modSettings['load_average']);
949
			$disabled = false;
950
		}
951
	}
952
953
	// Start with a simple checkbox.
954
	$config_vars = array(
955
		array('check', 'loadavg_enable', 'disabled' => $disabled),
956
	);
957
958
	// Set the default values for each option.
959
	$default_values = array(
960
		'loadavg_auto_opt' => 1.0,
961
		'loadavg_search' => 2.5,
962
		'loadavg_allunread' => 2.0,
963
		'loadavg_unreadreplies' => 3.5,
964
		'loadavg_show_posts' => 2.0,
965
		'loadavg_userstats' => 10.0,
966
		'loadavg_bbc' => 30.0,
967
		'loadavg_forum' => 40.0,
968
	);
969
970
	// Loop through the settings.
971
	foreach ($default_values as $name => $value)
972
	{
973
		// Use the default value if the setting isn't set yet.
974
		$value = !isset($modSettings[$name]) ? $value : $modSettings[$name];
975
		$config_vars[] = array('float', $name, 'value' => $value, 'disabled' => $disabled);
976
	}
977
978
	call_integration_hook('integrate_loadavg_settings', array(&$config_vars));
979
980
	if ($return_config)
981
		return $config_vars;
982
983
	$context['post_url'] = $scripturl . '?action=admin;area=serversettings;sa=loads;save';
984
	$context['settings_title'] = $txt['load_balancing_settings'];
985
986
	// Saving?
987
	if (isset($_GET['save']))
988
	{
989
		// Stupidity is not allowed.
990
		foreach ($_POST as $key => $value)
991
		{
992
			if (strpos($key, 'loadavg') === 0 || $key === 'loadavg_enable' || !in_array($key, array_keys($default_values)))
993
				continue;
994
			else
995
				$_POST[$key] = (float) $value;
996
997
			if ($key == 'loadavg_auto_opt' && $value <= 1)
998
				$_POST['loadavg_auto_opt'] = 1.0;
999
			elseif ($key == 'loadavg_forum' && $value < 10)
1000
				$_POST['loadavg_forum'] = 10.0;
1001
			elseif ($value < 2)
1002
				$_POST[$key] = 2.0;
1003
		}
1004
1005
		call_integration_hook('integrate_save_loadavg_settings');
1006
1007
		saveDBSettings($config_vars);
1008
		if (!isset($_SESSION['adm-save']))
1009
			$_SESSION['adm-save'] = true;
1010
		redirectexit('action=admin;area=serversettings;sa=loads;' . $context['session_var'] . '=' . $context['session_id']);
1011
	}
1012
1013
	prepareDBSettingContext($config_vars);
1014
}
1015
1016
/**
1017
 * Helper function, it sets up the context for the manage server settings.
1018
 * - The basic usage of the six numbered key fields are
1019
 * - array (0 ,1, 2, 3, 4, 5
1020
 *		0 variable name - the name of the saved variable
1021
 *		1 label - the text to show on the settings page
1022
 *		2 saveto - file or db, where to save the variable name - value pair
1023
 *		3 type - type of data to save, int, float, text, check
1024
 *		4 size - false or field size
1025
 *		5 help - '' or helptxt variable name
1026
 *	)
1027
 *
1028
 * the following named keys are also permitted
1029
 * 'disabled' => A string of code that will determine whether or not the setting should be disabled
1030
 * 'postinput' => Text to display after the input field
1031
 * 'preinput' => Text to display before the input field
1032
 * 'subtext' => Additional descriptive text to display under the field's label
1033
 * 'min' => minimum allowed value (for int/float). Defaults to 0 if not set.
1034
 * 'max' => maximum allowed value (for int/float)
1035
 * 'step' => how much to increment/decrement the value by (only for int/float - mostly used for float values).
1036
 *
1037
 * @param array $config_vars An array of configuration variables
1038
 */
1039
function prepareServerSettingsContext(&$config_vars)
1040
{
1041
	global $context, $modSettings, $smcFunc, $txt;
1042
1043
	if (!empty($context['settings_not_writable']))
1044
		$context['settings_message'] = array(
1045
			'label' => $txt['settings_not_writable'],
1046
			'tag' => 'div',
1047
			'class' => 'centertext strong'
1048
		);
1049
1050
	if (isset($_SESSION['adm-save']))
1051
	{
1052
		if ($_SESSION['adm-save'] === true)
1053
			$context['saved_successful'] = true;
1054
		else
1055
			$context['saved_failed'] = $_SESSION['adm-save'];
1056
1057
		unset($_SESSION['adm-save']);
1058
	}
1059
1060
	$context['config_vars'] = array();
1061
	foreach ($config_vars as $identifier => $config_var)
1062
	{
1063
		if (!is_array($config_var) || !isset($config_var[1]))
1064
			$context['config_vars'][] = $config_var;
1065
		else
1066
		{
1067
			$varname = $config_var[0];
1068
			global $$varname;
1069
1070
			// Set the subtext in case it's part of the label.
1071
			// @todo Temporary. Preventing divs inside label tags.
1072
			$divPos = strpos($config_var[1], '<div');
1073
			$subtext = '';
1074
			if ($divPos !== false)
1075
			{
1076
				$subtext = preg_replace('~</?div[^>]*>~', '', substr($config_var[1], $divPos));
1077
				$config_var[1] = substr($config_var[1], 0, $divPos);
1078
			}
1079
1080
			$context['config_vars'][$config_var[0]] = array(
1081
				'label' => $config_var[1],
1082
				'help' => isset($config_var[5]) ? $config_var[5] : '',
1083
				'type' => $config_var[3],
1084
				'size' => !empty($config_var[4]) && !is_array($config_var[4]) ? $config_var[4] : 0,
1085
				'data' => isset($config_var[4]) && is_array($config_var[4]) && $config_var[3] != 'select' ? $config_var[4] : array(),
1086
				'name' => $config_var[0],
1087
				'value' => $config_var[2] == 'file' ? $smcFunc['htmlspecialchars']($$varname) : (isset($modSettings[$config_var[0]]) ? $smcFunc['htmlspecialchars']($modSettings[$config_var[0]]) : (in_array($config_var[3], array('int', 'float')) ? 0 : '')),
1088
				'disabled' => !empty($context['settings_not_writable']) || !empty($config_var['disabled']),
1089
				'invalid' => false,
1090
				'subtext' => !empty($config_var['subtext']) ? $config_var['subtext'] : $subtext,
1091
				'javascript' => '',
1092
				'preinput' => !empty($config_var['preinput']) ? $config_var['preinput'] : '',
1093
				'postinput' => !empty($config_var['postinput']) ? $config_var['postinput'] : '',
1094
			);
1095
1096
			// Handle min/max/step if necessary
1097
			if ($config_var[3] == 'int' || $config_var[3] == 'float')
1098
			{
1099
				// Default to a min of 0 if one isn't set
1100
				if (isset($config_var['min']))
1101
					$context['config_vars'][$config_var[0]]['min'] = $config_var['min'];
1102
				else
1103
					$context['config_vars'][$config_var[0]]['min'] = 0;
1104
1105
				if (isset($config_var['max']))
1106
					$context['config_vars'][$config_var[0]]['max'] = $config_var['max'];
1107
1108
				if (isset($config_var['step']))
1109
					$context['config_vars'][$config_var[0]]['step'] = $config_var['step'];
1110
			}
1111
1112
			// If this is a select box handle any data.
1113
			if (!empty($config_var[4]) && is_array($config_var[4]))
1114
			{
1115
				// If it's associative
1116
				$config_values = array_values($config_var[4]);
1117
				if (isset($config_values[0]) && is_array($config_values[0]))
1118
					$context['config_vars'][$config_var[0]]['data'] = $config_var[4];
1119
				else
1120
				{
1121
					foreach ($config_var[4] as $key => $item)
1122
						$context['config_vars'][$config_var[0]]['data'][] = array($key, $item);
1123
				}
1124
			}
1125
		}
1126
	}
1127
1128
	// Two tokens because saving these settings requires both saveSettings and saveDBSettings
1129
	createToken('admin-ssc');
1130
	createToken('admin-dbsc');
1131
}
1132
1133
/**
1134
 * Helper function, it sets up the context for database settings.
1135
 *
1136
 * @todo see rev. 10406 from 2.1-requests
1137
 *
1138
 * @param array $config_vars An array of configuration variables
1139
 */
1140
function prepareDBSettingContext(&$config_vars)
1141
{
1142
	global $txt, $helptxt, $context, $modSettings, $sourcedir, $smcFunc;
1143
1144
	loadLanguage('Help');
1145
1146
	if (isset($_SESSION['adm-save']))
1147
	{
1148
		if ($_SESSION['adm-save'] === true)
1149
			$context['saved_successful'] = true;
1150
		else
1151
			$context['saved_failed'] = $_SESSION['adm-save'];
1152
1153
		unset($_SESSION['adm-save']);
1154
	}
1155
1156
	$context['config_vars'] = array();
1157
	$inlinePermissions = array();
1158
	$bbcChoice = array();
1159
	$board_list = false;
1160
	foreach ($config_vars as $config_var)
1161
	{
1162
		// HR?
1163
		if (!is_array($config_var))
1164
			$context['config_vars'][] = $config_var;
1165
		else
1166
		{
1167
			// If it has no name it doesn't have any purpose!
1168
			if (empty($config_var[1]))
1169
				continue;
1170
1171
			// Special case for inline permissions
1172
			if ($config_var[0] == 'permissions' && allowedTo('manage_permissions'))
1173
				$inlinePermissions[] = $config_var[1];
1174
1175
			elseif ($config_var[0] == 'permissions')
1176
				continue;
1177
1178
			if ($config_var[0] == 'boards')
1179
				$board_list = true;
1180
1181
			// Are we showing the BBC selection box?
1182
			if ($config_var[0] == 'bbc')
1183
				$bbcChoice[] = $config_var[1];
1184
1185
			// We need to do some parsing of the value before we pass it in.
1186
			if (isset($modSettings[$config_var[1]]))
1187
			{
1188
				switch ($config_var[0])
1189
				{
1190
					case 'select':
1191
						$value = $modSettings[$config_var[1]];
1192
						break;
1193
					case 'json':
1194
						$value = $smcFunc['htmlspecialchars']($smcFunc['json_encode']($modSettings[$config_var[1]]));
1195
						break;
1196
					case 'boards':
1197
						$value = explode(',', $modSettings[$config_var[1]]);
1198
						break;
1199
					default:
1200
						$value = $smcFunc['htmlspecialchars']($modSettings[$config_var[1]]);
1201
				}
1202
			}
1203
			else
1204
			{
1205
				// Darn, it's empty. What type is expected?
1206
				switch ($config_var[0])
1207
				{
1208
					case 'int':
1209
					case 'float':
1210
						$value = 0;
1211
						break;
1212
					case 'select':
1213
						$value = !empty($config_var['multiple']) ? $smcFunc['json_encode'](array()) : '';
1214
						break;
1215
					case 'boards':
1216
						$value = array();
1217
						break;
1218
					default:
1219
						$value = '';
1220
				}
1221
			}
1222
1223
			$context['config_vars'][$config_var[1]] = array(
1224
				'label' => isset($config_var['text_label']) ? $config_var['text_label'] : (isset($txt[$config_var[1]]) ? $txt[$config_var[1]] : (isset($config_var[3]) && !is_array($config_var[3]) ? $config_var[3] : '')),
1225
				'help' => isset($helptxt[$config_var[1]]) ? $config_var[1] : '',
1226
				'type' => $config_var[0],
1227
				'size' => !empty($config_var['size']) ? $config_var['size'] : (!empty($config_var[2]) && !is_array($config_var[2]) ? $config_var[2] : (in_array($config_var[0], array('int', 'float')) ? 6 : 0)),
1228
				'data' => array(),
1229
				'name' => $config_var[1],
1230
				'value' => $value,
1231
				'disabled' => false,
1232
				'invalid' => !empty($config_var['invalid']),
1233
				'javascript' => '',
1234
				'var_message' => !empty($config_var['message']) && isset($txt[$config_var['message']]) ? $txt[$config_var['message']] : '',
1235
				'preinput' => isset($config_var['preinput']) ? $config_var['preinput'] : '',
1236
				'postinput' => isset($config_var['postinput']) ? $config_var['postinput'] : '',
1237
			);
1238
1239
			// Handle min/max/step if necessary
1240
			if ($config_var[0] == 'int' || $config_var[0] == 'float')
1241
			{
1242
				// Default to a min of 0 if one isn't set
1243
				if (isset($config_var['min']))
1244
					$context['config_vars'][$config_var[1]]['min'] = $config_var['min'];
1245
1246
				else
1247
					$context['config_vars'][$config_var[1]]['min'] = 0;
1248
1249
				if (isset($config_var['max']))
1250
					$context['config_vars'][$config_var[1]]['max'] = $config_var['max'];
1251
1252
				if (isset($config_var['step']))
1253
					$context['config_vars'][$config_var[1]]['step'] = $config_var['step'];
1254
			}
1255
1256
			// If this is a select box handle any data.
1257
			if (!empty($config_var[2]) && is_array($config_var[2]))
1258
			{
1259
				// If we allow multiple selections, we need to adjust a few things.
1260
				if ($config_var[0] == 'select' && !empty($config_var['multiple']))
1261
				{
1262
					$context['config_vars'][$config_var[1]]['name'] .= '[]';
1263
					$context['config_vars'][$config_var[1]]['value'] = !empty($context['config_vars'][$config_var[1]]['value']) ? $smcFunc['json_decode']($context['config_vars'][$config_var[1]]['value'], true) : array();
1264
				}
1265
1266
				// If it's associative
1267
				if (isset($config_var[2][0]) && is_array($config_var[2][0]))
1268
					$context['config_vars'][$config_var[1]]['data'] = $config_var[2];
1269
1270
				else
1271
				{
1272
					foreach ($config_var[2] as $key => $item)
1273
						$context['config_vars'][$config_var[1]]['data'][] = array($key, $item);
1274
				}
1275
				if (empty($config_var['size']) && !empty($config_var['multiple']))
1276
					$context['config_vars'][$config_var[1]]['size'] = max(4, count($config_var[2]));
1277
			}
1278
1279
			// Finally allow overrides - and some final cleanups.
1280
			foreach ($config_var as $k => $v)
1281
			{
1282
				if (!is_numeric($k))
1283
				{
1284
					if (substr($k, 0, 2) == 'on')
1285
						$context['config_vars'][$config_var[1]]['javascript'] .= ' ' . $k . '="' . $v . '"';
1286
					else
1287
						$context['config_vars'][$config_var[1]][$k] = $v;
1288
				}
1289
1290
				// See if there are any other labels that might fit?
1291
				if (isset($txt['setting_' . $config_var[1]]))
1292
					$context['config_vars'][$config_var[1]]['label'] = $txt['setting_' . $config_var[1]];
1293
1294
				elseif (isset($txt['groups_' . $config_var[1]]))
1295
					$context['config_vars'][$config_var[1]]['label'] = $txt['groups_' . $config_var[1]];
1296
			}
1297
1298
			// Set the subtext in case it's part of the label.
1299
			// @todo Temporary. Preventing divs inside label tags.
1300
			$divPos = strpos($context['config_vars'][$config_var[1]]['label'], '<div');
1301
			if ($divPos !== false)
1302
			{
1303
				$context['config_vars'][$config_var[1]]['subtext'] = preg_replace('~</?div[^>]*>~', '', substr($context['config_vars'][$config_var[1]]['label'], $divPos));
1304
				$context['config_vars'][$config_var[1]]['label'] = substr($context['config_vars'][$config_var[1]]['label'], 0, $divPos);
1305
			}
1306
		}
1307
	}
1308
1309
	// If we have inline permissions we need to prep them.
1310
	if (!empty($inlinePermissions) && allowedTo('manage_permissions'))
1311
	{
1312
		require_once($sourcedir . '/ManagePermissions.php');
1313
		init_inline_permissions($inlinePermissions);
1314
	}
1315
1316
	if ($board_list)
1317
	{
1318
		require_once($sourcedir . '/Subs-MessageIndex.php');
1319
		$context['board_list'] = getBoardList();
1320
	}
1321
1322
	// What about any BBC selection boxes?
1323
	if (!empty($bbcChoice))
1324
	{
1325
		// What are the options, eh?
1326
		$temp = parse_bbc(false);
1327
		$bbcTags = array();
1328
		foreach ($temp as $tag)
0 ignored issues
show
Bug introduced by
The expression $temp of type string is not traversable.
Loading history...
1329
			if (!isset($tag['require_parents']))
1330
				$bbcTags[] = $tag['tag'];
1331
1332
		$bbcTags = array_unique($bbcTags);
1333
1334
		// The number of columns we want to show the BBC tags in.
1335
		$numColumns = isset($context['num_bbc_columns']) ? $context['num_bbc_columns'] : 3;
1336
1337
		// Now put whatever BBC options we may have into context too!
1338
		$context['bbc_sections'] = array();
1339
		foreach ($bbcChoice as $bbcSection)
1340
		{
1341
			$context['bbc_sections'][$bbcSection] = array(
1342
				'title' => isset($txt['bbc_title_' . $bbcSection]) ? $txt['bbc_title_' . $bbcSection] : $txt['enabled_bbc_select'],
1343
				'disabled' => empty($modSettings['bbc_disabled_' . $bbcSection]) ? array() : $modSettings['bbc_disabled_' . $bbcSection],
1344
				'all_selected' => empty($modSettings['bbc_disabled_' . $bbcSection]),
1345
				'columns' => array(),
1346
			);
1347
1348
			if ($bbcSection == 'legacyBBC')
1349
				$sectionTags = array_intersect($context['legacy_bbc'], $bbcTags);
1350
			else
1351
				$sectionTags = array_diff($bbcTags, $context['legacy_bbc']);
1352
1353
			$totalTags = count($sectionTags);
1354
			$tagsPerColumn = ceil($totalTags / $numColumns);
1355
1356
			$col = 0;
1357
			$i = 0;
1358
			foreach ($sectionTags as $tag)
1359
			{
1360
				if ($i % $tagsPerColumn == 0 && $i != 0)
1361
					$col++;
1362
1363
				$context['bbc_sections'][$bbcSection]['columns'][$col][] = array(
1364
					'tag' => $tag,
1365
					'show_help' => isset($helptxt['tag_' . $tag]),
1366
				);
1367
1368
				$i++;
1369
			}
1370
		}
1371
	}
1372
1373
	call_integration_hook('integrate_prepare_db_settings', array(&$config_vars));
1374
	createToken('admin-dbsc');
1375
}
1376
1377
/**
1378
 * Helper function. Saves settings by putting them in Settings.php or saving them in the settings table.
1379
 *
1380
 * - Saves those settings set from ?action=admin;area=serversettings.
1381
 * - Requires the admin_forum permission.
1382
 * - Contains arrays of the types of data to save into Settings.php.
1383
 *
1384
 * @param array $config_vars An array of configuration variables
1385
 */
1386
function saveSettings(&$config_vars)
1387
{
1388
	global $sourcedir, $context;
1389
1390
	validateToken('admin-ssc');
1391
1392
	// Fix the darn stupid cookiename! (more may not be allowed, but these for sure!)
1393
	if (isset($_POST['cookiename']))
1394
		$_POST['cookiename'] = preg_replace('~[,;\s\.$]+~' . ($context['utf8'] ? 'u' : ''), '', $_POST['cookiename']);
1395
1396
	// Fix the forum's URL if necessary.
1397
	if (isset($_POST['boardurl']))
1398
	{
1399
		if (substr($_POST['boardurl'], -10) == '/index.php')
1400
			$_POST['boardurl'] = substr($_POST['boardurl'], 0, -10);
1401
		elseif (substr($_POST['boardurl'], -1) == '/')
1402
			$_POST['boardurl'] = substr($_POST['boardurl'], 0, -1);
1403
		if (substr($_POST['boardurl'], 0, 7) != 'http://' && substr($_POST['boardurl'], 0, 7) != 'file://' && substr($_POST['boardurl'], 0, 8) != 'https://')
1404
			$_POST['boardurl'] = 'http://' . $_POST['boardurl'];
1405
1406
		$_POST['boardurl'] = normalize_iri($_POST['boardurl']);
1407
	}
1408
1409
	require_once($sourcedir . '/Subs-Admin.php');
1410
1411
	// Any passwords?
1412
	$config_passwords = array();
1413
1414
	// All the numeric variables.
1415
	$config_nums = array();
1416
1417
	// All the checkboxes
1418
	$config_bools = array();
1419
1420
	// Ones that accept multiple types (should be rare)
1421
	$config_multis = array();
1422
1423
	// Get all known setting definitions and assign them to our groups above.
1424
	$settings_defs = get_settings_defs();
1425
	foreach ($settings_defs as $var => $def)
1426
	{
1427
		if (!is_string($var))
1428
			continue;
1429
1430
		if (!empty($def['is_password']))
1431
		{
1432
			$config_passwords[] = $var;
1433
		}
1434
		else
1435
		{
1436
			// Special handling if multiple types are allowed.
1437
			if (is_array($def['type']))
1438
			{
1439
				// Obviously, we don't need null here.
1440
				$def['type'] = array_filter(
1441
					$def['type'],
1442
					function ($type)
1443
					{
1444
						return $type !== 'NULL';
1445
					}
1446
				);
1447
1448
				$type = count($def['type']) == 1 ? reset($def['type']) : 'multiple';
1449
			}
1450
			else
1451
				$type = $def['type'];
1452
1453
			switch ($type)
1454
			{
1455
				case 'multiple':
1456
					$config_multis[$var] = $def['type'];
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment if this fall-through is intended.
Loading history...
1457
1458
				case 'double':
1459
					$config_nums[] = $var;
1460
					break;
1461
1462
				case 'integer':
1463
					// Some things saved as integers are presented as booleans
1464
					foreach ($config_vars as $config_var)
1465
					{
1466
						if (is_array($config_var) && $config_var[0] == $var)
1467
						{
1468
							if ($config_var[3] == 'check')
1469
							{
1470
								$config_bools[] = $var;
1471
								break 2;
1472
							}
1473
							else
1474
								break;
1475
						}
1476
					}
1477
					$config_nums[] = $var;
1478
					break;
1479
1480
				case 'boolean':
1481
					$config_bools[] = $var;
1482
					break;
1483
1484
				default:
1485
					break;
1486
			}
1487
		}
1488
	}
1489
1490
	// Now sort everything into a big array, and figure out arrays and etc.
1491
	$new_settings = array();
1492
	// Figure out which config vars we're saving here...
1493
	foreach ($config_vars as $config_var)
1494
	{
1495
		if (!is_array($config_var) || $config_var[2] != 'file')
1496
			continue;
1497
1498
		$var_name = $config_var[0];
1499
1500
		// Unknown setting?
1501
		if (!isset($settings_defs[$var_name]) && isset($config_var[3]))
1502
		{
1503
			switch ($config_var[3])
1504
			{
1505
				case 'int':
1506
				case 'float':
1507
					$config_nums[] = $var_name;
1508
					break;
1509
1510
				case 'check':
1511
					$config_bools[] = $var_name;
1512
					break;
1513
1514
				default:
1515
					break;
1516
			}
1517
		}
1518
1519
		if (!in_array($var_name, $config_bools) && !isset($_POST[$var_name]))
1520
			continue;
1521
1522
		if (in_array($var_name, $config_passwords))
1523
		{
1524
			if (isset($_POST[$var_name][1]) && $_POST[$var_name][0] == $_POST[$var_name][1])
1525
				$new_settings[$var_name] = $_POST[$var_name][0];
1526
		}
1527
		elseif (in_array($var_name, $config_nums))
1528
		{
1529
			$new_settings[$var_name] = (int) $_POST[$var_name];
1530
1531
			// If no min is specified, assume 0. This is done to avoid having to specify 'min => 0' for all settings where 0 is the min...
1532
			$min = isset($config_var['min']) ? $config_var['min'] : 0;
1533
			$new_settings[$var_name] = max($min, $new_settings[$var_name]);
1534
1535
			// Is there a max value for this as well?
1536
			if (isset($config_var['max']))
1537
				$new_settings[$var_name] = min($config_var['max'], $new_settings[$var_name]);
1538
		}
1539
		elseif (in_array($var_name, $config_bools))
1540
		{
1541
			$new_settings[$var_name] = !empty($_POST[$var_name]);
1542
		}
1543
		elseif (isset($config_multis[$var_name]))
1544
		{
1545
			$is_acceptable_type = false;
1546
1547
			foreach ($config_multis[$var_name] as $type)
1548
			{
1549
				$temp = $_POST[$var_name];
1550
				settype($temp, $type);
1551
1552
				if ($temp == $_POST[$var_name])
1553
				{
1554
					$new_settings[$var_name] = $temp;
1555
					$is_acceptable_type = true;
1556
					break;
1557
				}
1558
			}
1559
1560
			if (!$is_acceptable_type)
1561
				fatal_error('Invalid config_var \'' . $var_name . '\'');
1562
		}
1563
		else
1564
		{
1565
			$new_settings[$var_name] = $_POST[$var_name];
1566
		}
1567
	}
1568
1569
	// Save the relevant settings in the Settings.php file.
1570
	updateSettingsFile($new_settings);
1571
1572
	// Now loop through the remaining (database-based) settings.
1573
	$new_settings = array();
1574
	foreach ($config_vars as $config_var)
1575
	{
1576
		// We just saved the file-based settings, so skip their definitions.
1577
		if (!is_array($config_var) || $config_var[2] == 'file')
1578
			continue;
1579
1580
		$new_setting = array($config_var[3], $config_var[0]);
1581
1582
		// Select options need carried over, too.
1583
		if (isset($config_var[4]))
1584
			$new_setting[] = $config_var[4];
1585
1586
		// Include min and max if necessary
1587
		if (isset($config_var['min']))
1588
			$new_setting['min'] = $config_var['min'];
1589
1590
		if (isset($config_var['max']))
1591
			$new_setting['max'] = $config_var['max'];
1592
1593
		// Rewrite the definition a bit.
1594
		$new_settings[] = $new_setting;
1595
	}
1596
1597
	// Save the new database-based settings, if any.
1598
	if (!empty($new_settings))
1599
		saveDBSettings($new_settings);
1600
}
1601
1602
/**
1603
 * Helper function for saving database settings.
1604
 *
1605
 * @todo see rev. 10406 from 2.1-requests
1606
 *
1607
 * @param array $config_vars An array of configuration variables
1608
 */
1609
function saveDBSettings(&$config_vars)
1610
{
1611
	global $sourcedir, $smcFunc;
1612
	static $board_list = null;
1613
1614
	validateToken('admin-dbsc');
1615
1616
	$inlinePermissions = array();
1617
	foreach ($config_vars as $var)
1618
	{
1619
		if (!isset($var[1]) || (!isset($_POST[$var[1]]) && $var[0] != 'check' && $var[0] != 'permissions' && $var[0] != 'boards' && ($var[0] != 'bbc' || !isset($_POST[$var[1] . '_enabledTags']))))
1620
			continue;
1621
1622
		// Checkboxes!
1623
		elseif ($var[0] == 'check')
1624
			$setArray[$var[1]] = !empty($_POST[$var[1]]) ? '1' : '0';
1625
		// Select boxes!
1626
		elseif ($var[0] == 'select' && in_array($_POST[$var[1]], array_keys($var[2])))
1627
			$setArray[$var[1]] = $_POST[$var[1]];
1628
		elseif ($var[0] == 'select' && !empty($var['multiple']) && array_intersect($_POST[$var[1]], array_keys($var[2])) != array())
1629
		{
1630
			// For security purposes we validate this line by line.
1631
			$lOptions = array();
1632
			foreach ($_POST[$var[1]] as $invar)
1633
				if (in_array($invar, array_keys($var[2])))
1634
					$lOptions[] = $invar;
1635
1636
			$setArray[$var[1]] = $smcFunc['json_encode']($lOptions);
1637
		}
1638
		// List of boards!
1639
		elseif ($var[0] == 'boards')
1640
		{
1641
			// We just need a simple list of valid boards, nothing more.
1642
			if ($board_list === null)
1643
			{
1644
				$board_list = array();
1645
				$request = $smcFunc['db_query']('', '
1646
					SELECT id_board
1647
					FROM {db_prefix}boards');
1648
1649
				while ($row = $smcFunc['db_fetch_row']($request))
1650
					$board_list[$row[0]] = true;
1651
1652
				$smcFunc['db_free_result']($request);
1653
			}
1654
1655
			$lOptions = array();
1656
1657
			if (!empty($_POST[$var[1]]))
1658
				foreach ($_POST[$var[1]] as $invar => $dummy)
1659
					if (isset($board_list[$invar]))
1660
						$lOptions[] = $invar;
1661
1662
			$setArray[$var[1]] = !empty($lOptions) ? implode(',', $lOptions) : '';
1663
		}
1664
		// Integers!
1665
		elseif ($var[0] == 'int')
1666
		{
1667
			$setArray[$var[1]] = (int) $_POST[$var[1]];
1668
1669
			// If no min is specified, assume 0. This is done to avoid having to specify 'min => 0' for all settings where 0 is the min...
1670
			$min = isset($var['min']) ? $var['min'] : 0;
1671
			$setArray[$var[1]] = max($min, $setArray[$var[1]]);
1672
1673
			// Do we have a max value for this as well?
1674
			if (isset($var['max']))
1675
				$setArray[$var[1]] = min($var['max'], $setArray[$var[1]]);
1676
		}
1677
		// Floating point!
1678
		elseif ($var[0] == 'float')
1679
		{
1680
			$setArray[$var[1]] = (float) $_POST[$var[1]];
1681
1682
			// If no min is specified, assume 0. This is done to avoid having to specify 'min => 0' for all settings where 0 is the min...
1683
			$min = isset($var['min']) ? $var['min'] : 0;
1684
			$setArray[$var[1]] = max($min, $setArray[$var[1]]);
1685
1686
			// Do we have a max value for this as well?
1687
			if (isset($var['max']))
1688
				$setArray[$var[1]] = min($var['max'], $setArray[$var[1]]);
1689
		}
1690
		// Text!
1691
		elseif (in_array($var[0], array('text', 'large_text', 'color', 'date', 'datetime', 'datetime-local', 'email', 'month', 'time')))
1692
			$setArray[$var[1]] = $_POST[$var[1]];
1693
		// Passwords!
1694
		elseif ($var[0] == 'password')
1695
		{
1696
			if (isset($_POST[$var[1]][1]) && $_POST[$var[1]][0] == $_POST[$var[1]][1])
1697
				$setArray[$var[1]] = $_POST[$var[1]][0];
1698
		}
1699
		// BBC.
1700
		elseif ($var[0] == 'bbc')
1701
		{
1702
			$bbcTags = array();
1703
			foreach (parse_bbc(false) as $tag)
0 ignored issues
show
Bug introduced by
The expression parse_bbc(false) of type string is not traversable.
Loading history...
1704
				$bbcTags[] = $tag['tag'];
1705
1706
			if (!isset($_POST[$var[1] . '_enabledTags']))
1707
				$_POST[$var[1] . '_enabledTags'] = array();
1708
			elseif (!is_array($_POST[$var[1] . '_enabledTags']))
1709
				$_POST[$var[1] . '_enabledTags'] = array($_POST[$var[1] . '_enabledTags']);
1710
1711
			$setArray[$var[1]] = implode(',', array_diff($bbcTags, $_POST[$var[1] . '_enabledTags']));
1712
		}
1713
		// Permissions?
1714
		elseif ($var[0] == 'permissions')
1715
			$inlinePermissions[] = $var[1];
1716
	}
1717
1718
	if (!empty($setArray))
1719
		updateSettings($setArray);
1720
1721
	// If we have inline permissions we need to save them.
1722
	if (!empty($inlinePermissions) && allowedTo('manage_permissions'))
1723
	{
1724
		require_once($sourcedir . '/ManagePermissions.php');
1725
		save_inline_permissions($inlinePermissions);
1726
	}
1727
}
1728
1729
/**
1730
 * Allows us to see the servers php settings
1731
 *
1732
 * - loads the settings into an array for display in a template
1733
 * - drops cookie values just in case
1734
 */
1735
function ShowPHPinfoSettings()
1736
{
1737
	global $context, $txt;
1738
1739
	$category = $txt['phpinfo_settings'];
1740
1741
	// get the data
1742
	ob_start();
1743
	phpinfo();
1744
1745
	// We only want it for its body, pigs that we are
1746
	$info_lines = preg_replace('~^.*<body>(.*)</body>.*$~', '$1', ob_get_contents());
1747
	$info_lines = explode("\n", strip_tags($info_lines, "<tr><td><h2>"));
1748
	ob_end_clean();
1749
1750
	// remove things that could be considered sensitive
1751
	$remove = '_COOKIE|Cookie|_GET|_REQUEST|REQUEST_URI|QUERY_STRING|REQUEST_URL|HTTP_REFERER';
1752
1753
	// put all of it into an array
1754
	foreach ($info_lines as $line)
1755
	{
1756
		if (preg_match('~(' . $remove . ')~', $line))
1757
			continue;
1758
1759
		// new category?
1760
		if (strpos($line, '<h2>') !== false)
1761
			$category = preg_match('~<h2>(.*)</h2>~', $line, $title) ? $category = $title[1] : $category;
0 ignored issues
show
Unused Code introduced by
The assignment to $category is dead and can be removed.
Loading history...
1762
1763
		// load it as setting => value or the old setting local master
1764
		if (preg_match('~<tr><td[^>]+>([^<]*)</td><td[^>]+>([^<]*)</td></tr>~', $line, $val))
1765
			$pinfo[$category][$val[1]] = $val[2];
1766
		elseif (preg_match('~<tr><td[^>]+>([^<]*)</td><td[^>]+>([^<]*)</td><td[^>]+>([^<]*)</td></tr>~', $line, $val))
1767
			$pinfo[$category][$val[1]] = array($txt['phpinfo_localsettings'] => $val[2], $txt['phpinfo_defaultsettings'] => $val[3]);
1768
	}
1769
1770
	// load it in to context and display it
1771
	$context['pinfo'] = $pinfo;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $pinfo does not seem to be defined for all execution paths leading up to this point.
Loading history...
1772
	$context['page_title'] = $txt['admin_server_settings'];
1773
	$context['sub_template'] = 'php_info';
1774
	return;
1775
}
1776
1777
/**
1778
 * Get the installed Cache API implementations.
1779
 *
1780
 */
1781
function loadCacheAPIs()
1782
{
1783
	global $sourcedir;
1784
1785
	$cacheAPIdir = $sourcedir . '/Cache';
1786
1787
	$loadedApis = array();
1788
	$apis_dir = $cacheAPIdir .'/'. CacheApi::APIS_FOLDER;
1789
1790
	$api_classes = new GlobIterator($apis_dir . '/*.php', FilesystemIterator::NEW_CURRENT_AND_KEY);
1791
1792
	foreach ($api_classes as $file_path => $file_info)
1793
	{
1794
		require_once($apis_dir . '/' . $file_path);
1795
1796
		$class_name = $file_info->getBasename('.php');
1797
		$fully_qualified_class_name = CacheApi::APIS_NAMESPACE . $class_name;
1798
1799
		/* @var CacheApiInterface $cache_api */
1800
		$cache_api = new $fully_qualified_class_name();
1801
1802
		// Deal with it!
1803
		if (!($cache_api instanceof CacheApiInterface) || !($cache_api instanceof CacheApi))
1804
			continue;
1805
1806
		// No Support?  NEXT!
1807
		if (!$cache_api->isSupported(true))
1808
			continue;
1809
1810
		$loadedApis[$class_name] = $cache_api;
1811
	}
1812
1813
	call_integration_hook('integrate_load_cache_apis', array(&$loadedApis));
1814
1815
	return $loadedApis;
1816
}
1817
1818
/**
1819
 * Registers the site with the Simple Machines Stat collection. This function
1820
 * purposely does not use updateSettings.php as it will be called shortly after
1821
 * this process completes by the saveSettings() function.
1822
 *
1823
 * @see SMStats() for more information.
1824
 * @link https://www.simplemachines.org/about/stats.php for more info.
1825
 *
1826
 */
1827
function registerSMStats()
1828
{
1829
	global $modSettings, $boardurl, $smcFunc;
1830
1831
	// Already have a key?  Can't register again.
1832
	if (!empty($modSettings['sm_stats_key']))
1833
		return true;
1834
1835
	$fp = @fsockopen('www.simplemachines.org', 443, $errno, $errstr);
1836
	if (!$fp)
0 ignored issues
show
introduced by
$fp is of type false|resource, thus it always evaluated to false.
Loading history...
1837
		$fp = @fsockopen('www.simplemachines.org', 80, $errno, $errstr);
1838
	if ($fp)
0 ignored issues
show
introduced by
$fp is of type false|resource, thus it always evaluated to false.
Loading history...
1839
	{
1840
		$out = 'GET /smf/stats/register_stats.php?site=' . base64_encode($boardurl) . ' HTTP/1.1' . "\r\n";
1841
		$out .= 'Host: www.simplemachines.org' . "\r\n";
1842
		$out .= 'Connection: Close' . "\r\n\r\n";
1843
		fwrite($fp, $out);
1844
1845
		$return_data = '';
1846
		while (!feof($fp))
1847
			$return_data .= fgets($fp, 128);
1848
1849
		fclose($fp);
1850
1851
		// Get the unique site ID.
1852
		preg_match('~SITE-ID:\s(\w{10})~', $return_data, $ID);
1853
1854
		if (!empty($ID[1]))
1855
		{
1856
			$smcFunc['db_insert']('replace',
1857
				'{db_prefix}settings',
1858
				array('variable' => 'string', 'value' => 'string'),
1859
				array('sm_stats_key', $ID[1]),
1860
				array('variable')
1861
			);
1862
			return true;
1863
		}
1864
	}
1865
1866
	return false;
1867
}
1868
1869
?>