Completed
Push — release-2.1 ( 15f485...ae1be0 )
by Mathias
08:27
created

ManageServer.php ➔ ModifyGeneralSettings()   B

Complexity

Conditions 6
Paths 5

Size

Total Lines 75
Code Lines 37

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 37
nc 5
nop 1
dl 0
loc 75
rs 8.4736
c 0
b 0
f 0

How to fix   Long Method   

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 http://www.simplemachines.org
56
 * @copyright 2017 Simple Machines and individual contributors
57
 * @license http://www.simplemachines.org/about/smf/license.php BSD
58
 *
59
 * @version 2.1 Beta 3
60
 */
61
62
if (!defined('SMF'))
63
	die('No direct access...');
64
65
/**
66
 * This is the main dispatcher. Sets up all the available sub-actions, all the tabs and selects
67
 * the appropriate one based on the sub-action.
68
 *
69
 * Requires the admin_forum permission.
70
 * Redirects to the appropriate function based on the sub-action.
71
 *
72
 * @uses edit_settings adminIndex.
73
 */
74
function ModifySettings()
75
{
76
	global $context, $txt, $boarddir;
77
78
	// This is just to keep the database password more secure.
79
	isAllowedTo('admin_forum');
80
81
	// Load up all the tabs...
82
	$context[$context['admin_menu_name']]['tab_data'] = array(
83
		'title' => $txt['admin_server_settings'],
84
		'help' => 'serversettings',
85
		'description' => $txt['admin_basic_settings'],
86
	);
87
88
	checkSession('request');
89
90
	// The settings are in here, I swear!
91
	loadLanguage('ManageSettings');
92
93
	$context['page_title'] = $txt['admin_server_settings'];
94
	$context['sub_template'] = 'show_settings';
95
96
	$subActions = array(
97
		'general' => 'ModifyGeneralSettings',
98
		'database' => 'ModifyDatabaseSettings',
99
		'cookie' => 'ModifyCookieSettings',
100
		'security' => 'ModifyGeneralSecuritySettings',
101
		'cache' => 'ModifyCacheSettings',
102
		'loads' => 'ModifyLoadBalancingSettings',
103
		'phpinfo' => 'ShowPHPinfoSettings',
104
	);
105
106
	// By default we're editing the core settings
107
	$_REQUEST['sa'] = isset($_REQUEST['sa']) && isset($subActions[$_REQUEST['sa']]) ? $_REQUEST['sa'] : 'general';
108
	$context['sub_action'] = $_REQUEST['sa'];
109
110
	// Warn the user if there's any relevant information regarding Settings.php.
111
	$settings_not_writable = !is_writable($boarddir . '/Settings.php');
112
	$settings_backup_fail = !@is_writable($boarddir . '/Settings_bak.php') || !@copy($boarddir . '/Settings.php', $boarddir . '/Settings_bak.php');
113
114 View Code Duplication
	if ($settings_not_writable)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
115
		$context['settings_message'] = '<div class="centertext"><strong>' . $txt['settings_not_writable'] . '</strong></div><br>';
116
	elseif ($settings_backup_fail)
117
		$context['settings_message'] = '<div class="centertext"><strong>' . $txt['admin_backup_fail'] . '</strong></div><br>';
118
119
	$context['settings_not_writable'] = $settings_not_writable;
120
121
	call_integration_hook('integrate_server_settings', array(&$subActions));
122
123
	// Call the right function for this sub-action.
124
	call_helper($subActions[$_REQUEST['sa']]);
125
}
126
127
/**
128
 * General forum settings - forum name, maintenance mode, etc.
129
 * Practically, this shows an interface for the settings in Settings.php to be changed.
130
 *
131
 * - Requires the admin_forum permission.
132
 * - Uses the edit_settings administration area.
133
 * - Contains the actual array of settings to show from Settings.php.
134
 * - Accessed from ?action=admin;area=serversettings;sa=general.
135
 *
136
 * @param bool $return_config Whether to return the $config_vars array (for pagination purposes)
137
 * @return void|array Returns nothing or returns the $config_vars array if $return_config is true
138
 */
139
function ModifyGeneralSettings($return_config = false)
140
{
141
	global $scripturl, $context, $txt;
142
143
	/* If you're writing a mod, it's a bad idea to add things here....
144
	For each option:
145
		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)
146
	OR	an empty string for a horizontal rule.
147
	OR	a string for a titled section. */
148
	$config_vars = array(
149
		array('mbname', $txt['admin_title'], 'file', 'text', 30),
150
		'',
151
		array('maintenance', $txt['admin_maintain'], 'file', 'check'),
152
		array('mtitle', $txt['maintenance_subject'], 'file', 'text', 36),
153
		array('mmessage', $txt['maintenance_message'], 'file', 'text', 36),
154
		'',
155
		array('webmaster_email', $txt['admin_webmaster_email'], 'file', 'text', 30),
156
		'',
157
		array('enableCompressedOutput', $txt['enableCompressedOutput'], 'db', 'check', null, 'enableCompressedOutput'),
158
		array('disableTemplateEval', $txt['disableTemplateEval'], 'db', 'check', null, 'disableTemplateEval'),
159
		array('disableHostnameLookup', $txt['disableHostnameLookup'], 'db', 'check', null, 'disableHostnameLookup'),
160
		'',
161
		array('force_ssl', $txt['force_ssl'], 'db', 'select', array($txt['force_ssl_off'], $txt['force_ssl_auth'], $txt['force_ssl_complete']), 'force_ssl'),
162
		array('image_proxy_enabled', $txt['image_proxy_enabled'], 'file', 'check', null, 'image_proxy_enabled'),
163
		array('image_proxy_secret', $txt['image_proxy_secret'], 'file', 'text', 30, 'image_proxy_secret'),
164
		array('image_proxy_maxsize', $txt['image_proxy_maxsize'], 'file', 'int', null, 'image_proxy_maxsize'),
165
		'',
166
		array('enable_sm_stats', $txt['sm_state_setting'], 'db', 'check', null, 'enable_sm_stats'),
167
	);
168
169
	call_integration_hook('integrate_general_settings', array(&$config_vars));
170
171
	if ($return_config)
172
		return $config_vars;
173
174
	// Setup the template stuff.
175
	$context['post_url'] = $scripturl . '?action=admin;area=serversettings;sa=general;save';
176
	$context['settings_title'] = $txt['general_settings'];
177
178
	// Saving settings?
179
	if (isset($_REQUEST['save']))
180
	{
181
		call_integration_hook('integrate_save_general_settings');
182
183
		// Are we saving the stat collection?
184
		if (!empty($_POST['enable_sm_stats']) && empty($modSettings['sm_stats_key']))
0 ignored issues
show
Bug introduced by
The variable $modSettings seems to never exist, and therefore empty should always return true. Did you maybe rename this variable?

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

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

Loading history...
185
		{
186
			$registerSMStats = registerSMStats();
187
188
			// Failed to register, disable it again.
189
			if (empty($registerSMStats))
190
				$_POST['enable_sm_stats'] = 0;
191
		}
192
193
		saveSettings($config_vars);
194
		$_SESSION['adm-save'] = true;
195
		redirectexit('action=admin;area=serversettings;sa=general;' . $context['session_var'] . '=' . $context['session_id']);
196
	}
197
198
	// Fill the config array.
199
	prepareServerSettingsContext($config_vars);
200
201
	// Some javascript for SSL
202
	addInlineJavaScript('
203
$(function()
204
{
205
	$("#force_ssl").change(function()
206
	{
207
		var mode = $(this).val() == 2 ? false : true;
208
		$("#image_proxy_enabled").prop("disabled", mode);
209
		$("#image_proxy_secret").prop("disabled", mode);
210
		$("#image_proxy_maxsize").prop("disabled", mode);
211
	}).change();
212
});');
213
}
214
215
/**
216
 * Basic database and paths settings - database name, host, etc.
217
 *
218
 * - It shows an interface for the settings in Settings.php to be changed.
219
 * - It contains the actual array of settings to show from Settings.php.
220
 * - Requires the admin_forum permission.
221
 * - Uses the edit_settings administration area.
222
 * - Accessed from ?action=admin;area=serversettings;sa=database.
223
 *
224
 * @param bool $return_config Whether or not to return the config_vars array (used for admin search)
225
 * @return void|array Returns nothing or returns the $config_vars array if $return_config is true
226
 */
227
function ModifyDatabaseSettings($return_config = false)
228
{
229
	global $scripturl, $context, $txt, $smcFunc;
230
231
	/* If you're writing a mod, it's a bad idea to add things here....
232
		For each option:
233
		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)
234
		OR an empty string for a horizontal rule.
235
		OR a string for a titled section. */
236
	$config_vars = array(
237
		array('db_persist', $txt['db_persist'], 'file', 'check', null, 'db_persist'),
238
		array('db_error_send', $txt['db_error_send'], 'file', 'check'),
239
		array('ssi_db_user', $txt['ssi_db_user'], 'file', 'text', null, 'ssi_db_user'),
240
		array('ssi_db_passwd', $txt['ssi_db_passwd'], 'file', 'password'),
241
		'',
242
		array('autoFixDatabase', $txt['autoFixDatabase'], 'db', 'check', false, 'autoFixDatabase')
243
	);
244
	
245
	// Add PG Stuff
246
	if ($smcFunc['db_title'] == "PostgreSQL")
247
	{
248
		$request = $smcFunc['db_query']('', 'SELECT cfgname FROM pg_ts_config', array());
249
		$fts_language = array();
250
		
251
		while ($row = $smcFunc['db_fetch_assoc']($request))
252
			$fts_language[$row['cfgname']] = $row['cfgname'];
253
		
254
		$config_vars = array_merge ($config_vars, array(
255
				'',
256
				array('search_language', $txt['search_language'], 'db', 'select', $fts_language, 'pgFulltextSearch')
257
			)
258
		);
259
	}
260
		
261
262
	call_integration_hook('integrate_database_settings', array(&$config_vars));
263
264
	if ($return_config)
265
		return $config_vars;
266
267
	// Setup the template stuff.
268
	$context['post_url'] = $scripturl . '?action=admin;area=serversettings;sa=database;save';
269
	$context['settings_title'] = $txt['database_settings'];
270
	$context['save_disabled'] = $context['settings_not_writable'];
271
272
	// Saving settings?
273
	if (isset($_REQUEST['save']))
274
	{
275
		call_integration_hook('integrate_save_database_settings');
276
277
		saveSettings($config_vars);
278
		$_SESSION['adm-save'] = true;
279
		redirectexit('action=admin;area=serversettings;sa=database;' . $context['session_var'] . '=' . $context['session_id']);
280
	}
281
282
	// Fill the config array.
283
	prepareServerSettingsContext($config_vars);
284
}
285
286
/**
287
 * This function handles cookies settings modifications.
288
 *
289
 * @param bool $return_config Whether or not to return the config_vars array (used for admin search)
290
 * @return void|array Returns nothing or returns the $config_vars array if $return_config is true
291
 */
292
function ModifyCookieSettings($return_config = false)
293
{
294
	global $context, $scripturl, $txt, $sourcedir, $modSettings, $cookiename, $user_settings, $boardurl, $smcFunc;
295
296
	// Define the variables we want to edit.
297
	$config_vars = array(
298
		// Cookies...
299
		array('cookiename', $txt['cookie_name'], 'file', 'text', 20),
300
		array('cookieTime', $txt['cookieTime'], 'db', 'int', 'postinput' => $txt['minutes']),
301
		array('localCookies', $txt['localCookies'], 'db', 'check', false, 'localCookies'),
302
		array('globalCookies', $txt['globalCookies'], 'db', 'check', false, 'globalCookies'),
303
		array('globalCookiesDomain', $txt['globalCookiesDomain'], 'db', 'text', false, 'globalCookiesDomain'),
304
		array('secureCookies', $txt['secureCookies'], 'db', 'check', false, 'secureCookies', 'disabled' => !isset($_SERVER['HTTPS']) || !(strtolower($_SERVER['HTTPS']) == 'on' || strtolower($_SERVER['HTTPS']) == '1')),
305
		array('httponlyCookies', $txt['httponlyCookies'], 'db', 'check', false, 'httponlyCookies'),
306
		'',
307
		// Sessions
308
		array('databaseSession_enable', $txt['databaseSession_enable'], 'db', 'check', false, 'databaseSession_enable'),
309
		array('databaseSession_loose', $txt['databaseSession_loose'], 'db', 'check', false, 'databaseSession_loose'),
310
		array('databaseSession_lifetime', $txt['databaseSession_lifetime'], 'db', 'int', false, 'databaseSession_lifetime', 'postinput' => $txt['seconds']),
311
		'',
312
		// 2FA
313
		array('tfa_mode', $txt['tfa_mode'], 'db', 'select', array(
314
			0 => $txt['tfa_mode_disabled'],
315
			1 => $txt['tfa_mode_enabled'],
316
		) + (empty($user_settings['tfa_secret']) ? array() : array(
317
			2 => $txt['tfa_mode_forced'],
318
		)) + (empty($user_settings['tfa_secret']) ? array() : array(
319
			3 => $txt['tfa_mode_forcedall'],
320
		)), 'subtext' => $txt['tfa_mode_subtext'] . (empty($user_settings['tfa_secret']) ? '<br /><strong>' . $txt['tfa_mode_forced_help'] . '</strong>' : ''), 'tfa_mode'),
321
	);
322
323
	addInlineJavaScript('
324
	function hideGlobalCookies()
325
	{
326
		var usingLocal = $("#localCookies").prop("checked");
327
		$("#setting_globalCookies").closest("dt").toggle(!usingLocal);
328
		$("#globalCookies").closest("dd").toggle(!usingLocal);
329
330
		var usingGlobal = !usingLocal && $("#globalCookies").prop("checked");
331
		$("#setting_globalCookiesDomain").closest("dt").toggle(usingGlobal);
332
		$("#globalCookiesDomain").closest("dd").toggle(usingGlobal);
333
	};
334
	hideGlobalCookies();
335
336
	$("#localCookies, #globalCookies").click(function() {
337
		hideGlobalCookies();
338
	});', true);
339
340
	if (empty($user_settings['tfa_secret']))
341
		addInlineJavaScript('');
342
343
	call_integration_hook('integrate_cookie_settings', array(&$config_vars));
344
345
	if ($return_config)
346
		return $config_vars;
347
348
	$context['post_url'] = $scripturl . '?action=admin;area=serversettings;sa=cookie;save';
349
	$context['settings_title'] = $txt['cookies_sessions_settings'];
350
351
	// Saving settings?
352
	if (isset($_REQUEST['save']))
353
	{
354
		call_integration_hook('integrate_save_cookie_settings');
355
356
		// Local and global do not play nicely together.
357
		if (!empty($_POST['localCookies']) && empty($_POST['globalCookies']))
358
			unset ($_POST['globalCookies']);
359
360
		if (!empty($_POST['globalCookiesDomain']) && strpos($boardurl, $_POST['globalCookiesDomain']) === false)
361
			fatal_lang_error('invalid_cookie_domain', false);
362
363
		saveSettings($config_vars);
364
365
		// If the cookie name was changed, reset the cookie.
366
		if ($cookiename != $_POST['cookiename'])
367
		{
368
			$original_session_id = $context['session_id'];
369
			include_once($sourcedir . '/Subs-Auth.php');
370
371
			// Remove the old cookie.
372
			setLoginCookie(-3600, 0);
373
374
			// Set the new one.
375
			$cookiename = $_POST['cookiename'];
376
			setLoginCookie(60 * $modSettings['cookieTime'], $user_settings['id_member'], hash_salt($user_settings['passwd'], $user_settings['password_salt']));
377
378
			redirectexit('action=admin;area=serversettings;sa=cookie;' . $context['session_var'] . '=' . $original_session_id, $context['server']['needs_login_fix']);
379
		}
380
381
		//If we disabled 2FA, reset all members and membergroups settings.
382
		if (isset($_POST['tfa_mode']) && empty($_POST['tfa_mode']))
383
		{
384
			$smcFunc['db_query']('', '
385
				UPDATE {db_prefix}membergroups
386
				SET tfa_required = {int:zero}',
387
				array(
388
					'zero' => 0,
389
				)
390
			);
391
			$smcFunc['db_query']('', '
392
				UPDATE {db_prefix}members
393
				SET tfa_secret = {string:empty}, tfa_backup = {string:empty}',
394
				array(
395
					'empty' => '',
396
				)
397
			);
398
		}
399
400
		$_SESSION['adm-save'] = true;
401
		redirectexit('action=admin;area=serversettings;sa=cookie;' . $context['session_var'] . '=' . $context['session_id']);
402
	}
403
404
	// Fill the config array.
405
	prepareServerSettingsContext($config_vars);
406
}
407
408
/**
409
 * Settings really associated with general security aspects.
410
 *
411
 * @param bool $return_config Whether or not to return the config_vars array (used for admin search)
412
 * @return void|array Returns nothing or returns the $config_vars array if $return_config is true
413
 */
414
function ModifyGeneralSecuritySettings($return_config = false)
415
{
416
	global $txt, $scripturl, $context;
417
418
	$config_vars = array(
419
			array('int', 'failed_login_threshold'),
420
			array('int', 'loginHistoryDays', 'subtext' => $txt['zero_to_disable']),
421
		'',
422
			array('check', 'securityDisable'),
423
			array('check', 'securityDisable_moderate'),
424
		'',
425
			// Reactive on email, and approve on delete
426
			array('check', 'send_validation_onChange'),
427
			array('check', 'approveAccountDeletion'),
428
		'',
429
			// Password strength.
430
			array('select', 'password_strength', array($txt['setting_password_strength_low'], $txt['setting_password_strength_medium'], $txt['setting_password_strength_high'])),
431
			array('check', 'enable_password_conversion'),
432
		'',
433
			// Reporting of personal messages?
434
			array('check', 'enableReportPM'),
435
		'',
436
			array('select', 'frame_security', array('SAMEORIGIN' => $txt['setting_frame_security_SAMEORIGIN'], 'DENY' => $txt['setting_frame_security_DENY'], 'DISABLE' => $txt['setting_frame_security_DISABLE'])),
437
		'',
438
			array('select', 'proxy_ip_header', array('disabled' => $txt['setting_proxy_ip_header_disabled'], 'autodetect' => $txt['setting_proxy_ip_header_autodetect'], 'HTTP_X_FORWARDED_FOR' => 'HTTP_X_FORWARDED_FOR', 'HTTP_CLIENT_IP' => 'HTTP_CLIENT_IP', 'HTTP_X_REAL_IP' => 'HTTP_X_REAL_IP', 'CF-Connecting-IP' => 'CF-Connecting-IP')),
439
			array('text', 'proxy_ip_servers'),
440
	);
441
442
	call_integration_hook('integrate_general_security_settings', array(&$config_vars));
443
444
	if ($return_config)
445
		return $config_vars;
446
447
	// Saving?
448 View Code Duplication
	if (isset($_GET['save']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
449
	{
450
		saveDBSettings($config_vars);
451
		$_SESSION['adm-save'] = true;
452
453
		call_integration_hook('integrate_save_general_security_settings');
454
455
		writeLog();
456
		redirectexit('action=admin;area=serversettings;sa=security;' . $context['session_var'] . '=' . $context['session_id']);
457
	}
458
459
	$context['post_url'] = $scripturl . '?action=admin;area=serversettings;save;sa=security';
460
	$context['settings_title'] = $txt['security_settings'];
461
462
	prepareDBSettingContext($config_vars);
463
}
464
465
/**
466
 * Simply modifying cache functions
467
 *
468
 * @param bool $return_config Whether or not to return the config_vars array (used for admin search)
469
 * @return void|array Returns nothing or returns the $config_vars array if $return_config is true
470
 */
471
function ModifyCacheSettings($return_config = false)
472
{
473
	global $context, $scripturl, $txt;
474
475
	// Detect all available optimizers
476
	$detected = loadCacheAPIs();
477
478
	// set our values to show what, if anything, we found
479
	if (empty($detected))
480
	{
481
		$txt['cache_settings_message'] = $txt['detected_no_caching'];
482
		$cache_level = array($txt['cache_off']);
483
		$detected['none'] = $txt['cache_off'];
484
	}
485
	else
486
	{
487
		$txt['cache_settings_message'] = sprintf($txt['detected_accelerators'], implode(', ', $detected));
488
		$cache_level = array($txt['cache_off'], $txt['cache_level1'], $txt['cache_level2'], $txt['cache_level3']);
489
	}
490
491
	// Define the variables we want to edit.
492
	$config_vars = array(
493
		// Only a few settings, but they are important
494
		array('', $txt['cache_settings_message'], '', 'desc'),
495
		array('cache_enable', $txt['cache_enable'], 'file', 'select', $cache_level, 'cache_enable'),
496
		array('cache_accelerator', $txt['cache_accelerator'], 'file', 'select', $detected),
497
	);
498
499
	// some javascript to enable / disable certain settings if the option is not selected
500
	$context['settings_post_javascript'] = '
501
		$(document).ready(function() {
502
			$("#cache_accelerator").change();
503
		});';
504
505
	call_integration_hook('integrate_modify_cache_settings', array(&$config_vars));
506
507
	// Maybe we have some additional settings from the selected accelerator.
508
	if (!empty($detected))
509
	{
510
		foreach ($detected as $tryCache => $dummy)
511
		{
512
			$cache_class_name = $tryCache . '_cache';
513
514
			// loadCacheAPIs has already included the file, just see if we can't add the settings in.
515
			if (is_callable(array($cache_class_name, 'cacheSettings')))
516
			{
517
				$testAPI = new $cache_class_name();
518
				call_user_func_array(array($testAPI, 'cacheSettings'), array(&$config_vars));
519
			}
520
		}
521
	}
522
	if ($return_config)
523
		return $config_vars;
524
525
	// Saving again?
526 View Code Duplication
	if (isset($_GET['save']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
527
	{
528
		call_integration_hook('integrate_save_cache_settings');
529
530
		saveSettings($config_vars);
531
		$_SESSION['adm-save'] = true;
532
533
		// We need to save the $cache_enable to $modSettings as well
534
		updatesettings(array('cache_enable' => (int) $_POST['cache_enable']));
535
536
		// exit so we reload our new settings on the page
537
		redirectexit('action=admin;area=serversettings;sa=cache;' . $context['session_var'] . '=' . $context['session_id']);
538
	}
539
540
	loadLanguage('ManageMaintenance');
541
	createToken('admin-maint');
542
	$context['template_layers'][] = 'clean_cache_button';
543
544
	$context['post_url'] = $scripturl . '?action=admin;area=serversettings;sa=cache;save';
545
	$context['settings_title'] = $txt['caching_settings'];
546
547
	// Changing cache settings won't have any effect if Settings.php is not writeable.
548
	$context['save_disabled'] = $context['settings_not_writable'];
549
550
	// Decide what message to show.
551
	if (!$context['save_disabled'])
552
		$context['settings_message'] = $txt['caching_information'];
553
554
	// Prepare the template.
555
	prepareServerSettingsContext($config_vars);
556
}
557
558
/**
559
 * Allows to edit load balancing settings.
560
 *
561
 * @param bool $return_config Whether or not to return the config_vars array
562
 * @return void|array Returns nothing or returns the $config_vars array if $return_config is true
563
 */
564
function ModifyLoadBalancingSettings($return_config = false)
565
{
566
	global $txt, $scripturl, $context, $modSettings;
567
568
	// Setup a warning message, but disabled by default.
569
	$disabled = true;
570
	$context['settings_message'] = $txt['loadavg_disabled_conf'];
571
572
	if (stripos(PHP_OS, 'win') === 0)
573
	{
574
		$context['settings_message'] = $txt['loadavg_disabled_windows'];
575
		if (isset($_GET['save']))
576
			$_SESSION['adm-save'] = $txt['loadavg_disabled_windows'];
577
	}
578
	elseif (stripos(PHP_OS, 'darwin') === 0)
579
	{
580
		$context['settings_message'] = $txt['loadavg_disabled_osx'];
581
		if (isset($_GET['save']))
582
			$_SESSION['adm-save'] = $txt['loadavg_disabled_osx'];
583
	}
584
	else
585
	{
586
		$modSettings['load_average'] = @file_get_contents('/proc/loadavg');
587
		if (!empty($modSettings['load_average']) && preg_match('~^([^ ]+?) ([^ ]+?) ([^ ]+)~', $modSettings['load_average'], $matches) !== 0)
588
			$modSettings['load_average'] = (float) $matches[1];
589 View Code Duplication
		elseif (($modSettings['load_average'] = @`uptime`) !== null && preg_match('~load averages?: (\d+\.\d+), (\d+\.\d+), (\d+\.\d+)~i', $modSettings['load_average'], $matches) !== 0)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
590
			$modSettings['load_average'] = (float) $matches[1];
591
		else
592
			unset($modSettings['load_average']);
593
594
		if (!empty($modSettings['load_average']) || $modSettings['load_average'] === 0.0)
595
		{
596
			$context['settings_message'] = sprintf($txt['loadavg_warning'], $modSettings['load_average']);
597
			$disabled = false;
598
		}
599
	}
600
601
	// Start with a simple checkbox.
602
	$config_vars = array(
603
		array('check', 'loadavg_enable', 'disabled' => $disabled),
604
	);
605
606
	// Set the default values for each option.
607
	$default_values = array(
608
		'loadavg_auto_opt' => 1.0,
609
		'loadavg_search' => 2.5,
610
		'loadavg_allunread' => 2.0,
611
		'loadavg_unreadreplies' => 3.5,
612
		'loadavg_show_posts' => 2.0,
613
		'loadavg_userstats' => 10.0,
614
		'loadavg_bbc' => 30.0,
615
		'loadavg_forum' => 40.0,
616
	);
617
618
	// Loop through the settings.
619
	foreach ($default_values as $name => $value)
620
	{
621
		// Use the default value if the setting isn't set yet.
622
		$value = !isset($modSettings[$name]) ? $value : $modSettings[$name];
623
		$config_vars[] = array('float', $name, 'value' => $value, 'disabled' => $disabled);
624
	}
625
626
	call_integration_hook('integrate_loadavg_settings', array(&$config_vars));
627
628
	if ($return_config)
629
		return $config_vars;
630
631
	$context['post_url'] = $scripturl . '?action=admin;area=serversettings;sa=loads;save';
632
	$context['settings_title'] = $txt['load_balancing_settings'];
633
634
	// Saving?
635
	if (isset($_GET['save']))
636
	{
637
		// Stupidity is not allowed.
638
		foreach ($_POST as $key => $value)
639
		{
640
			if (strpos($key, 'loadavg') === 0 || $key === 'loadavg_enable' || !in_array($key, array_keys($default_values)))
641
				continue;
642
			else
643
				$_POST[$key] = (float) $value;
644
645
			if ($key == 'loadavg_auto_opt' && $value <= 1)
646
				$_POST['loadavg_auto_opt'] = 1.0;
647
			elseif ($key == 'loadavg_forum' && $value < 10)
648
				$_POST['loadavg_forum'] = 10.0;
649
			elseif ($value < 2)
650
				$_POST[$key] = 2.0;
651
		}
652
653
		call_integration_hook('integrate_save_loadavg_settings');
654
655
		saveDBSettings($config_vars);
656
		if (!isset($_SESSION['adm-save']))
657
			$_SESSION['adm-save'] = true;
658
		redirectexit('action=admin;area=serversettings;sa=loads;' . $context['session_var'] . '=' . $context['session_id']);
659
	}
660
661
	prepareDBSettingContext($config_vars);
662
}
663
664
/**
665
 * Helper function, it sets up the context for the manage server settings.
666
 * - The basic usage of the six numbered key fields are
667
 * - array (0 ,1, 2, 3, 4, 5
668
 *		0 variable name - the name of the saved variable
669
 *		1 label - the text to show on the settings page
670
 *		2 saveto - file or db, where to save the variable name - value pair
671
 *		3 type - type of data to save, int, float, text, check
672
 *		4 size - false or field size
673
 *		5 help - '' or helptxt variable name
674
 *	)
675
 *
676
 * the following named keys are also permitted
677
 * 'disabled' => A string of code that will determine whether or not the setting should be disabled
678
 * 'postinput' => Text to display after the input field
679
 * 'preinput' => Text to display before the input field
680
 * 'subtext' => Additional descriptive text to display under the field's label
681
 * 'min' => minimum allowed value (for int/float). Defaults to 0 if not set.
682
 * 'max' => maximum allowed value (for int/float)
683
 * 'step' => how much to increment/decrement the value by (only for int/float - mostly used for float values).
684
 *
685
 * @param array $config_vars An array of configuration variables
686
 */
687
function prepareServerSettingsContext(&$config_vars)
688
{
689
	global $context, $modSettings, $smcFunc;
690
691 View Code Duplication
	if (isset($_SESSION['adm-save']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
692
	{
693
		if ($_SESSION['adm-save'] === true)
694
			$context['saved_successful'] = true;
695
		else
696
			$context['saved_failed'] = $_SESSION['adm-save'];
697
698
		unset($_SESSION['adm-save']);
699
	}
700
701
	$context['config_vars'] = array();
702
	foreach ($config_vars as $identifier => $config_var)
703
	{
704
		if (!is_array($config_var) || !isset($config_var[1]))
705
			$context['config_vars'][] = $config_var;
706
		else
707
		{
708
			$varname = $config_var[0];
709
			global $$varname;
710
711
			// Set the subtext in case it's part of the label.
712
			// @todo Temporary. Preventing divs inside label tags.
713
			$divPos = strpos($config_var[1], '<div');
714
			$subtext = '';
715
			if ($divPos !== false)
716
			{
717
				$subtext = preg_replace('~</?div[^>]*>~', '', substr($config_var[1], $divPos));
718
				$config_var[1] = substr($config_var[1], 0, $divPos);
719
			}
720
721
			$context['config_vars'][$config_var[0]] = array(
722
				'label' => $config_var[1],
723
				'help' => isset($config_var[5]) ? $config_var[5] : '',
724
				'type' => $config_var[3],
725
				'size' => empty($config_var[4]) ? 0 : $config_var[4],
726
				'data' => isset($config_var[4]) && is_array($config_var[4]) && $config_var[3] != 'select' ? $config_var[4] : array(),
727
				'name' => $config_var[0],
728
				'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 : '')),
729
				'disabled' => !empty($context['settings_not_writable']) || !empty($config_var['disabled']),
730
				'invalid' => false,
731
				'subtext' => !empty($config_var['subtext']) ? $config_var['subtext'] : $subtext,
732
				'javascript' => '',
733
				'preinput' => !empty($config_var['preinput']) ? $config_var['preinput'] : '',
734
				'postinput' => !empty($config_var['postinput']) ? $config_var['postinput'] : '',
735
			);
736
737
			// Handle min/max/step if necessary
738 View Code Duplication
			if ($config_var[3] == 'int' || $config_var[3] == 'float')
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
739
			{
740
				// Default to a min of 0 if one isn't set
741
				if (isset($config_var['min']))
742
					$context['config_vars'][$config_var[0]]['min'] = $config_var['min'];
743
				else
744
					$context['config_vars'][$config_var[0]]['min'] = 0;
745
746
				if (isset($config_var['max']))
747
					$context['config_vars'][$config_var[0]]['max'] = $config_var['max'];
748
749
				if (isset($config_var['step']))
750
					$context['config_vars'][$config_var[0]]['step'] = $config_var['step'];
751
			}
752
753
			// If this is a select box handle any data.
754
			if (!empty($config_var[4]) && is_array($config_var[4]))
755
			{
756
				// If it's associative
757
				$config_values = array_values($config_var[4]);
758 View Code Duplication
				if (isset($config_values[0]) && is_array($config_values[0]))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
759
					$context['config_vars'][$config_var[0]]['data'] = $config_var[4];
760
				else
761
				{
762
					foreach ($config_var[4] as $key => $item)
763
						$context['config_vars'][$config_var[0]]['data'][] = array($key, $item);
764
				}
765
			}
766
		}
767
	}
768
769
	// Two tokens because saving these settings requires both saveSettings and saveDBSettings
770
	createToken('admin-ssc');
771
	createToken('admin-dbsc');
772
}
773
774
/**
775
 * Helper function, it sets up the context for database settings.
776
 * @todo see rev. 10406 from 2.1-requests
777
 *
778
 * @param array $config_vars An array of configuration variables
779
 */
780
function prepareDBSettingContext(&$config_vars)
781
{
782
	global $txt, $helptxt, $context, $modSettings, $sourcedir, $smcFunc;
783
784
	loadLanguage('Help');
785
786 View Code Duplication
	if (isset($_SESSION['adm-save']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
787
	{
788
		if ($_SESSION['adm-save'] === true)
789
			$context['saved_successful'] = true;
790
		else
791
			$context['saved_failed'] = $_SESSION['adm-save'];
792
793
		unset($_SESSION['adm-save']);
794
	}
795
796
	$context['config_vars'] = array();
797
	$inlinePermissions = array();
798
	$bbcChoice = array();
799
	$board_list = false;
800
	foreach ($config_vars as $config_var)
801
	{
802
		// HR?
803
		if (!is_array($config_var))
804
			$context['config_vars'][] = $config_var;
805
		else
806
		{
807
			// If it has no name it doesn't have any purpose!
808
			if (empty($config_var[1]))
809
				continue;
810
811
			// Special case for inline permissions
812
			if ($config_var[0] == 'permissions' && allowedTo('manage_permissions'))
813
				$inlinePermissions[] = $config_var[1];
814
			elseif ($config_var[0] == 'permissions')
815
				continue;
816
817
			if ($config_var[0] == 'boards')
818
				$board_list = true;
819
820
			// Are we showing the BBC selection box?
821
			if ($config_var[0] == 'bbc')
822
				$bbcChoice[] = $config_var[1];
823
824
			// We need to do some parsing of the value before we pass it in.
825
			if (isset($modSettings[$config_var[1]]))
826
			{
827
				switch ($config_var[0])
828
				{
829
					case 'select':
830
						$value = $modSettings[$config_var[1]];
831
						break;
832
					case 'json':
833
						$value = $smcFunc['htmlspecialchars'](json_encode($modSettings[$config_var[1]]));
834
						break;
835
					case 'boards':
836
						$value = explode(',', $modSettings[$config_var[1]]);
837
						break;
838
					default:
839
						$value = $smcFunc['htmlspecialchars']($modSettings[$config_var[1]]);
840
				}
841
			}
842
			else
843
			{
844
				// Darn, it's empty. What type is expected?
845
				switch ($config_var[0])
846
				{
847
					case 'int':
848
					case 'float':
849
						$value = 0;
850
						break;
851
					case 'select':
852
						$value = !empty($config_var['multiple']) ? json_encode(array()) : '';
853
						break;
854
					case 'boards':
855
						$value = array();
856
						break;
857
					default:
858
						$value = '';
859
				}
860
			}
861
862
			$context['config_vars'][$config_var[1]] = array(
863
				'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] : '')),
864
				'help' => isset($helptxt[$config_var[1]]) ? $config_var[1] : '',
865
				'type' => $config_var[0],
866
				'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)),
867
				'data' => array(),
868
				'name' => $config_var[1],
869
				'value' => $value,
870
				'disabled' => false,
871
				'invalid' => !empty($config_var['invalid']),
872
				'javascript' => '',
873
				'var_message' => !empty($config_var['message']) && isset($txt[$config_var['message']]) ? $txt[$config_var['message']] : '',
874
				'preinput' => isset($config_var['preinput']) ? $config_var['preinput'] : '',
875
				'postinput' => isset($config_var['postinput']) ? $config_var['postinput'] : '',
876
			);
877
878
			// Handle min/max/step if necessary
879 View Code Duplication
			if ($config_var[0] == 'int' || $config_var[0] == 'float')
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
880
			{
881
				// Default to a min of 0 if one isn't set
882
				if (isset($config_var['min']))
883
					$context['config_vars'][$config_var[1]]['min'] = $config_var['min'];
884
				else
885
					$context['config_vars'][$config_var[1]]['min'] = 0;
886
887
				if (isset($config_var['max']))
888
					$context['config_vars'][$config_var[1]]['max'] = $config_var['max'];
889
890
				if (isset($config_var['step']))
891
					$context['config_vars'][$config_var[1]]['step'] = $config_var['step'];
892
			}
893
894
			// If this is a select box handle any data.
895
			if (!empty($config_var[2]) && is_array($config_var[2]))
896
			{
897
				// If we allow multiple selections, we need to adjust a few things.
898
				if ($config_var[0] == 'select' && !empty($config_var['multiple']))
899
				{
900
					$context['config_vars'][$config_var[1]]['name'] .= '[]';
901
					$context['config_vars'][$config_var[1]]['value'] = !empty($context['config_vars'][$config_var[1]]['value']) ? smf_json_decode($context['config_vars'][$config_var[1]]['value'], true) : array();
902
				}
903
904
				// If it's associative
905 View Code Duplication
				if (isset($config_var[2][0]) && is_array($config_var[2][0]))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
906
					$context['config_vars'][$config_var[1]]['data'] = $config_var[2];
907
				else
908
				{
909
					foreach ($config_var[2] as $key => $item)
910
						$context['config_vars'][$config_var[1]]['data'][] = array($key, $item);
911
				}
912
			}
913
914
			// Finally allow overrides - and some final cleanups.
915
			foreach ($config_var as $k => $v)
916
			{
917
				if (!is_numeric($k))
918
				{
919
					if (substr($k, 0, 2) == 'on')
920
						$context['config_vars'][$config_var[1]]['javascript'] .= ' ' . $k . '="' . $v . '"';
921
					else
922
						$context['config_vars'][$config_var[1]][$k] = $v;
923
				}
924
925
				// See if there are any other labels that might fit?
926
				if (isset($txt['setting_' . $config_var[1]]))
927
					$context['config_vars'][$config_var[1]]['label'] = $txt['setting_' . $config_var[1]];
928
				elseif (isset($txt['groups_' . $config_var[1]]))
929
					$context['config_vars'][$config_var[1]]['label'] = $txt['groups_' . $config_var[1]];
930
			}
931
932
			// Set the subtext in case it's part of the label.
933
			// @todo Temporary. Preventing divs inside label tags.
934
			$divPos = strpos($context['config_vars'][$config_var[1]]['label'], '<div');
935
			if ($divPos !== false)
936
			{
937
				$context['config_vars'][$config_var[1]]['subtext'] = preg_replace('~</?div[^>]*>~', '', substr($context['config_vars'][$config_var[1]]['label'], $divPos));
938
				$context['config_vars'][$config_var[1]]['label'] = substr($context['config_vars'][$config_var[1]]['label'], 0, $divPos);
939
			}
940
		}
941
	}
942
943
	// If we have inline permissions we need to prep them.
944
	if (!empty($inlinePermissions) && allowedTo('manage_permissions'))
945
	{
946
		require_once($sourcedir . '/ManagePermissions.php');
947
		init_inline_permissions($inlinePermissions, isset($context['permissions_excluded']) ? $context['permissions_excluded'] : array());
948
	}
949
950
	if ($board_list)
951
	{
952
		require_once($sourcedir . '/Subs-MessageIndex.php');
953
		$context['board_list'] = getBoardList();
954
	}
955
956
	// What about any BBC selection boxes?
957
	if (!empty($bbcChoice))
958
	{
959
		// What are the options, eh?
960
		$temp = parse_bbc(false);
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
961
		$bbcTags = array();
962
		foreach ($temp as $tag)
0 ignored issues
show
Bug introduced by
The expression $temp of type string is not traversable.
Loading history...
963
			$bbcTags[] = $tag['tag'];
964
965
		$bbcTags = array_unique($bbcTags);
966
		$totalTags = count($bbcTags);
967
968
		// The number of columns we want to show the BBC tags in.
969
		$numColumns = isset($context['num_bbc_columns']) ? $context['num_bbc_columns'] : 3;
970
971
		// Start working out the context stuff.
972
		$context['bbc_columns'] = array();
973
		$tagsPerColumn = ceil($totalTags / $numColumns);
974
975
		$col = 0; $i = 0;
976
		foreach ($bbcTags as $tag)
977
		{
978
			if ($i % $tagsPerColumn == 0 && $i != 0)
979
				$col++;
980
981
			$context['bbc_columns'][$col][] = array(
982
				'tag' => $tag,
983
				// @todo  'tag_' . ?
984
				'show_help' => isset($helptxt[$tag]),
985
			);
986
987
			$i++;
988
		}
989
990
		// Now put whatever BBC options we may have into context too!
991
		$context['bbc_sections'] = array();
992
		foreach ($bbcChoice as $bbc)
993
		{
994
			$context['bbc_sections'][$bbc] = array(
995
				'title' => isset($txt['bbc_title_' . $bbc]) ? $txt['bbc_title_' . $bbc] : $txt['bbcTagsToUse_select'],
996
				'disabled' => empty($modSettings['bbc_disabled_' . $bbc]) ? array() : $modSettings['bbc_disabled_' . $bbc],
997
				'all_selected' => empty($modSettings['bbc_disabled_' . $bbc]),
998
			);
999
		}
1000
	}
1001
1002
	call_integration_hook('integrate_prepare_db_settings', array(&$config_vars));
1003
	createToken('admin-dbsc');
1004
}
1005
1006
/**
1007
 * Helper function. Saves settings by putting them in Settings.php or saving them in the settings table.
1008
 *
1009
 * - Saves those settings set from ?action=admin;area=serversettings.
1010
 * - Requires the admin_forum permission.
1011
 * - Contains arrays of the types of data to save into Settings.php.
1012
 *
1013
 * @param array $config_vars An array of configuration variables
1014
 */
1015
function saveSettings(&$config_vars)
1016
{
1017
	global $sourcedir, $context;
1018
1019
	validateToken('admin-ssc');
1020
1021
	// Fix the darn stupid cookiename! (more may not be allowed, but these for sure!)
1022
	if (isset($_POST['cookiename']))
1023
		$_POST['cookiename'] = preg_replace('~[,;\s\.$]+~' . ($context['utf8'] ? 'u' : ''), '', $_POST['cookiename']);
1024
1025
	// Fix the forum's URL if necessary.
1026
	if (isset($_POST['boardurl']))
1027
	{
1028 View Code Duplication
		if (substr($_POST['boardurl'], -10) == '/index.php')
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1029
			$_POST['boardurl'] = substr($_POST['boardurl'], 0, -10);
1030
		elseif (substr($_POST['boardurl'], -1) == '/')
1031
			$_POST['boardurl'] = substr($_POST['boardurl'], 0, -1);
1032 View Code Duplication
		if (substr($_POST['boardurl'], 0, 7) != 'http://' && substr($_POST['boardurl'], 0, 7) != 'file://' && substr($_POST['boardurl'], 0, 8) != 'https://')
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1033
			$_POST['boardurl'] = 'http://' . $_POST['boardurl'];
1034
	}
1035
1036
	// Any passwords?
1037
	$config_passwords = array(
1038
		'db_passwd',
1039
		'ssi_db_passwd',
1040
	);
1041
1042
	// All the strings to write.
1043
	$config_strs = array(
1044
		'mtitle', 'mmessage',
1045
		'language', 'mbname', 'boardurl',
1046
		'cookiename',
1047
		'webmaster_email',
1048
		'db_name', 'db_user', 'db_server', 'db_prefix', 'ssi_db_user',
1049
		'boarddir', 'sourcedir',
1050
		'cachedir', 'cachedir_sqlite', 'cache_accelerator', 'cache_memcached',
1051
		'image_proxy_secret',
1052
	);
1053
1054
	// All the numeric variables.
1055
	$config_ints = array(
1056
		'cache_enable',
1057
		'image_proxy_maxsize',
1058
	);
1059
1060
	// All the checkboxes
1061
	$config_bools = array('db_persist', 'db_error_send', 'maintenance', 'image_proxy_enabled');
1062
1063
	// Now sort everything into a big array, and figure out arrays and etc.
1064
	$new_settings = array();
1065
	// Figure out which config vars we're saving here...
1066
	foreach ($config_vars as $var)
1067
	{
1068
		if (!is_array($var) || $var[2] != 'file' || (!in_array($var[0], $config_bools) && !isset($_POST[$var[0]])))
1069
			continue;
1070
1071
		$config_var = $var[0];
1072
1073
		if (in_array($config_var, $config_passwords))
1074
		{
1075
			if (isset($_POST[$config_var][1]) && $_POST[$config_var][0] == $_POST[$config_var][1])
1076
				$new_settings[$config_var] = '\'' . addcslashes($_POST[$config_var][0], '\'\\') . '\'';
1077
		}
1078
		elseif (in_array($config_var, $config_strs))
1079
		{
1080
			$new_settings[$config_var] = '\'' . addcslashes($_POST[$config_var], '\'\\') . '\'';
1081
		}
1082
		elseif (in_array($config_var, $config_ints))
1083
		{
1084
			$new_settings[$config_var] = (int) $_POST[$config_var];
1085
1086
			// 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...
1087
			$min = isset($var['min']) ? $var['min'] : 0;
1088
			$new_settings[$config_var] = max($min, $new_settings[$config_var]);
1089
1090
			// Is there a max value for this as well?
1091
			if (isset($var['max']))
1092
				$new_settings[$config_var] = min($var['max'], $new_settings[$config_var]);
1093
		}
1094
		elseif (in_array($config_var, $config_bools))
1095
		{
1096
			if (!empty($_POST[$config_var]))
1097
				$new_settings[$config_var] = '1';
1098
			else
1099
				$new_settings[$config_var] = '0';
1100
		}
1101
		else
1102
		{
1103
			// This shouldn't happen, but it might...
1104
			fatal_error('Unknown config_var \'' . $config_var . '\'');
1105
		}
1106
	}
1107
1108
	// Save the relevant settings in the Settings.php file.
1109
	require_once($sourcedir . '/Subs-Admin.php');
1110
	updateSettingsFile($new_settings);
1111
1112
	// Now loop through the remaining (database-based) settings.
1113
	$new_settings = array();
1114
	foreach ($config_vars as $config_var)
1115
	{
1116
		// We just saved the file-based settings, so skip their definitions.
1117
		if (!is_array($config_var) || $config_var[2] == 'file')
1118
			continue;
1119
1120
		$new_setting = array($config_var[3], $config_var[0]);
1121
1122
		// Select options need carried over, too.
1123
		if (isset($config_var[4]))
1124
			$new_setting[] = $config_var[4];
1125
1126
		// Include min and max if necessary
1127
		if (isset($config_var['min']))
1128
			$new_setting['min'] = $config_var['min'];
1129
1130
		if (isset($config_var['max']))
1131
			$new_setting['max'] = $config_var['max'];
1132
1133
		// Rewrite the definition a bit.
1134
		$new_settings[] = $new_setting;
1135
	}
1136
1137
	// Save the new database-based settings, if any.
1138
	if (!empty($new_settings))
1139
		saveDBSettings($new_settings);
1140
}
1141
1142
/**
1143
 * Helper function for saving database settings.
1144
 * @todo see rev. 10406 from 2.1-requests
1145
 *
1146
 * @param array $config_vars An array of configuration variables
1147
 */
1148
function saveDBSettings(&$config_vars)
1149
{
1150
	global $sourcedir, $smcFunc;
1151
	static $board_list = null;
1152
1153
	validateToken('admin-dbsc');
1154
1155
	$inlinePermissions = array();
1156
	foreach ($config_vars as $var)
1157
	{
1158
		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']))))
1159
			continue;
1160
1161
		// Checkboxes!
1162
		elseif ($var[0] == 'check')
1163
			$setArray[$var[1]] = !empty($_POST[$var[1]]) ? '1' : '0';
0 ignored issues
show
Coding Style Comprehensibility introduced by
$setArray was never initialized. Although not strictly required by PHP, it is generally a good practice to add $setArray = array(); before regardless.

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

Let’s take a look at an example:

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

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

    // do something with $myArray
}

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

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

Loading history...
1164
		// Select boxes!
1165
		elseif ($var[0] == 'select' && in_array($_POST[$var[1]], array_keys($var[2])))
1166
			$setArray[$var[1]] = $_POST[$var[1]];
0 ignored issues
show
Bug introduced by
The variable $setArray does not seem to be defined for all execution paths leading up to this point.

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

Let’s take a look at an example:

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

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

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

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

Available Fixes

  1. Check for existence of the variable explicitly:

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

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

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1167
		elseif ($var[0] == 'select' && !empty($var['multiple']) && array_intersect($_POST[$var[1]], array_keys($var[2])) != array())
1168
		{
1169
			// For security purposes we validate this line by line.
1170
			$lOptions = array();
1171
			foreach ($_POST[$var[1]] as $invar)
1172
				if (in_array($invar, array_keys($var[2])))
1173
					$lOptions[] = $invar;
1174
1175
			$setArray[$var[1]] = json_encode($lOptions);
1176
		}
1177
		// List of boards!
1178
		elseif ($var[0] == 'boards')
1179
		{
1180
			// We just need a simple list of valid boards, nothing more.
1181
			if ($board_list === null)
1182
			{
1183
				$board_list = array();
1184
				$request = $smcFunc['db_query']('', '
1185
					SELECT id_board
1186
					FROM {db_prefix}boards');
1187
				while ($row = $smcFunc['db_fetch_row']($request))
1188
					$board_list[$row[0]] = true;
1189
1190
				$smcFunc['db_free_result']($request);
1191
			}
1192
1193
			$lOptions = array();
1194
1195
			if (!empty($_POST[$var[1]]))
1196
				foreach ($_POST[$var[1]] as $invar => $dummy)
1197
					if (isset($board_list[$invar]))
1198
						$lOptions[] = $invar;
1199
1200
			$setArray[$var[1]] = !empty($lOptions) ? implode(',', $lOptions) : '';
1201
		}
1202
		// Integers!
1203 View Code Duplication
		elseif ($var[0] == 'int')
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1204
		{
1205
			$setArray[$var[1]] = (int) $_POST[$var[1]];
1206
1207
			// 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...
1208
			$min = isset($var['min']) ? $var['min'] : 0;
1209
			$setArray[$var[1]] = max($min, $setArray[$var[1]]);
1210
1211
			// Do we have a max value for this as well?
1212
			if (isset($var['max']))
1213
				$setArray[$var[1]] = min($var['max'], $setArray[$var[1]]);
1214
		}
1215
		// Floating point!
1216 View Code Duplication
		elseif ($var[0] == 'float')
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1217
		{
1218
			$setArray[$var[1]] = (float) $_POST[$var[1]];
1219
1220
			// 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...
1221
			$min = isset($var['min']) ? $var['min'] : 0;
1222
			$setArray[$var[1]] = max($min, $setArray[$var[1]]);
1223
1224
			// Do we have a max value for this as well?
1225
			if (isset($var['max']))
1226
				$setArray[$var[1]] = min($var['max'], $setArray[$var[1]]);
1227
		}
1228
		// Text!
1229
		elseif (in_array($var[0], array('text', 'large_text', 'color', 'date', 'datetime', 'datetime-local', 'email', 'month', 'time')))
1230
			$setArray[$var[1]] = $_POST[$var[1]];
1231
		// Passwords!
1232
		elseif ($var[0] == 'password')
1233
		{
1234
			if (isset($_POST[$var[1]][1]) && $_POST[$var[1]][0] == $_POST[$var[1]][1])
1235
				$setArray[$var[1]] = $_POST[$var[1]][0];
1236
		}
1237
		// BBC.
1238
		elseif ($var[0] == 'bbc')
1239
		{
1240
1241
			$bbcTags = array();
1242
			foreach (parse_bbc(false) as $tag)
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Bug introduced by
The expression parse_bbc(false) of type string is not traversable.
Loading history...
1243
				$bbcTags[] = $tag['tag'];
1244
1245
			if (!isset($_POST[$var[1] . '_enabledTags']))
1246
				$_POST[$var[1] . '_enabledTags'] = array();
1247
			elseif (!is_array($_POST[$var[1] . '_enabledTags']))
1248
				$_POST[$var[1] . '_enabledTags'] = array($_POST[$var[1] . '_enabledTags']);
1249
1250
			$setArray[$var[1]] = implode(',', array_diff($bbcTags, $_POST[$var[1] . '_enabledTags']));
1251
		}
1252
		// Permissions?
1253
		elseif ($var[0] == 'permissions')
1254
			$inlinePermissions[] = $var[1];
1255
	}
1256
1257
	if (!empty($setArray))
1258
		updateSettings($setArray);
1259
1260
	// If we have inline permissions we need to save them.
1261
	if (!empty($inlinePermissions) && allowedTo('manage_permissions'))
1262
	{
1263
		require_once($sourcedir . '/ManagePermissions.php');
1264
		save_inline_permissions($inlinePermissions);
1265
	}
1266
}
1267
1268
/**
1269
 * Allows us to see the servers php settings
1270
 *
1271
 * - loads the settings into an array for display in a template
1272
 * - drops cookie values just in case
1273
 */
1274
function ShowPHPinfoSettings()
1275
{
1276
	global $context, $txt;
1277
1278
	$category = $txt['phpinfo_settings'];
1279
1280
	// get the data
1281
	ob_start();
1282
	phpinfo();
1283
1284
	// We only want it for its body, pigs that we are
1285
	$info_lines = preg_replace('~^.*<body>(.*)</body>.*$~', '$1', ob_get_contents());
1286
	$info_lines = explode("\n", strip_tags($info_lines, "<tr><td><h2>"));
1287
	ob_end_clean();
1288
1289
	// remove things that could be considered sensitive
1290
	$remove = '_COOKIE|Cookie|_GET|_REQUEST|REQUEST_URI|QUERY_STRING|REQUEST_URL|HTTP_REFERER';
1291
1292
	// put all of it into an array
1293
	foreach ($info_lines as $line)
1294
	{
1295
		if (preg_match('~(' . $remove . ')~', $line))
1296
			continue;
1297
1298
		// new category?
1299
		if (strpos($line, '<h2>') !== false)
1300
			$category = preg_match('~<h2>(.*)</h2>~', $line, $title) ? $category = $title[1] : $category;
1301
1302
		// load it as setting => value or the old setting local master
1303
		if (preg_match('~<tr><td[^>]+>([^<]*)</td><td[^>]+>([^<]*)</td></tr>~', $line, $val))
1304
			$pinfo[$category][$val[1]] = $val[2];
0 ignored issues
show
Coding Style Comprehensibility introduced by
$pinfo was never initialized. Although not strictly required by PHP, it is generally a good practice to add $pinfo = array(); before regardless.

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

Let’s take a look at an example:

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

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

    // do something with $myArray
}

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

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

Loading history...
1305
		elseif (preg_match('~<tr><td[^>]+>([^<]*)</td><td[^>]+>([^<]*)</td><td[^>]+>([^<]*)</td></tr>~', $line, $val))
1306
			$pinfo[$category][$val[1]] = array($txt['phpinfo_localsettings'] => $val[2], $txt['phpinfo_defaultsettings'] => $val[3]);
0 ignored issues
show
Bug introduced by
The variable $pinfo does not seem to be defined for all execution paths leading up to this point.

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

Let’s take a look at an example:

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

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

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

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

Available Fixes

  1. Check for existence of the variable explicitly:

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

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

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1307
	}
1308
1309
	// load it in to context and display it
1310
	$context['pinfo'] = $pinfo;
1311
	$context['page_title'] = $txt['admin_server_settings'];
1312
	$context['sub_template'] = 'php_info';
1313
	return;
1314
}
1315
1316
/**
1317
 * Get the installed Cache API implementations.
1318
 *
1319
 */
1320
function loadCacheAPIs()
1321
{
1322
	global $sourcedir, $txt;
1323
1324
	// Make sure our class is in session.
1325
	require_once($sourcedir . '/Class-CacheAPI.php');
1326
1327
	$apis = array();
1328
	if ($dh = opendir($sourcedir))
1329
	{
1330
		while (($file = readdir($dh)) !== false)
1331
		{
1332
			if (is_file($sourcedir . '/' . $file) && preg_match('~^CacheAPI-([A-Za-z\d_]+)\.php$~', $file, $matches))
1333
			{
1334
				$tryCache = strtolower($matches[1]);
1335
1336
				require_once($sourcedir . '/' . $file);
1337
				$cache_class_name = $tryCache . '_cache';
1338
				$testAPI = new $cache_class_name();
1339
1340
				// No Support?  NEXT!
1341
				if (!$testAPI->isSupported(true))
1342
					continue;
1343
1344
				$apis[$tryCache] = isset($txt[$tryCache . '_cache']) ? $txt[$tryCache . '_cache'] : $tryCache;
1345
			}
1346
		}
1347
	}
1348
	closedir($dh);
1349
1350
	return $apis;
1351
}
1352
1353
/**
1354
 * Registers the site with the Simple Machines Stat collection. This function 
1355
 * purposely does not use updateSettings.php as it will be called shortly after
1356
 * this process completes by the saveSettings() function.
1357
 *
1358
 * @see Stats.php SMStats() for more information.
1359
 * @link https://www.simplemachines.org/about/stats.php for more info.
1360
 *
1361
 */
1362
function registerSMStats()
1363
{
1364
	global $modSettings, $boardurl, $smcFunc;
1365
1366
	// Already have a key?  Can't register again.
1367
	if (!empty($modSettings['sm_stats_key']))
1368
		return true;
1369
1370
	$fp = @fsockopen('www.simplemachines.org', 80, $errno, $errstr);
1371
	if ($fp)
1372
	{
1373
		$out = 'GET /smf/stats/register_stats.php?site=' . base64_encode($boardurl) . ' HTTP/1.1' . "\r\n";
1374
		$out .= 'Host: www.simplemachines.org' . "\r\n";
1375
		$out .= 'Connection: Close' . "\r\n\r\n";
1376
		fwrite($fp, $out);
1377
1378
		$return_data = '';
1379
		while (!feof($fp))
1380
			$return_data .= fgets($fp, 128);
1381
1382
		fclose($fp);
1383
1384
		// Get the unique site ID.
1385
		preg_match('~SITE-ID:\s(\w{10})~', $return_data, $ID);
1386
1387
		if (!empty($ID[1]))
1388
		{
1389
			$smcFunc['db_insert']('replace',
1390
				'{db_prefix}settings',
1391
				array('variable' => 'string', 'value' => 'string'),
1392
				array('sm_stats_key', $ID[1]),
1393
				array('variable')
1394
			);
1395
			return true;
1396
		}
1397
	}
1398
1399
	return false;
1400
}
1401
1402
?>
0 ignored issues
show
Best Practice introduced by
It is not recommended to use PHP's closing tag ?> in files other than templates.

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

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

Loading history...