Completed
Push — release-2.1 ( 5876a2...f30e04 )
by Mert
06:50
created

ManageServer.php ➔ saveSettings()   F

Complexity

Conditions 30
Paths 3360

Size

Total Lines 126
Code Lines 67

Duplication

Lines 6
Ratio 4.76 %
Metric Value
cc 30
eloc 67
nc 3360
nop 1
dl 6
loc 126
rs 2

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 http://www.simplemachines.org
56
 * @copyright 2016 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
167
	call_integration_hook('integrate_general_settings', array(&$config_vars));
168
169
	if ($return_config)
170
		return $config_vars;
171
172
	// Setup the template stuff.
173
	$context['post_url'] = $scripturl . '?action=admin;area=serversettings;sa=general;save';
174
	$context['settings_title'] = $txt['general_settings'];
175
176
	// Saving settings?
177 View Code Duplication
	if (isset($_REQUEST['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...
178
	{
179
		call_integration_hook('integrate_save_general_settings');
180
181
		saveSettings($config_vars);
182
		$_SESSION['adm-save'] = true;
183
		redirectexit('action=admin;area=serversettings;sa=general;' . $context['session_var'] . '=' . $context['session_id']);
184
	}
185
186
	// Fill the config array.
187
	prepareServerSettingsContext($config_vars);
188
189
	// Some javascript for SSL
190
	addInlineJavascript('
191
$(function()
192
{
193
	$("#force_ssl").change(function()
194
	{
195
		var mode = $(this).val() == 2 ? false : true;
196
		$("#image_proxy_enabled").prop("disabled", mode);
197
		$("#image_proxy_secret").prop("disabled", mode);
198
		$("#image_proxy_maxsize").prop("disabled", mode);
199
	}).change();
200
});');
201
}
202
203
/**
204
 * Basic database and paths settings - database name, host, etc.
205
 *
206
 * - It shows an interface for the settings in Settings.php to be changed.
207
 * - It contains the actual array of settings to show from Settings.php.
208
 * - Requires the admin_forum permission.
209
 * - Uses the edit_settings administration area.
210
 * - Accessed from ?action=admin;area=serversettings;sa=database.
211
 *
212
 * @param bool $return_config Whether or not to return the config_vars array (used for admin search)
213
 * @return void|array Returns nothing or returns the $config_vars array if $return_config is true
214
 */
215
function ModifyDatabaseSettings($return_config = false)
216
{
217
	global $scripturl, $context, $txt;
218
219
	/* If you're writing a mod, it's a bad idea to add things here....
220
		For each option:
221
		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)
222
		OR an empty string for a horizontal rule.
223
		OR a string for a titled section. */
224
	$config_vars = array(
225
		array('db_persist', $txt['db_persist'], 'file', 'check', null, 'db_persist'),
226
		array('db_error_send', $txt['db_error_send'], 'file', 'check'),
227
		array('ssi_db_user', $txt['ssi_db_user'], 'file', 'text', null, 'ssi_db_user'),
228
		array('ssi_db_passwd', $txt['ssi_db_passwd'], 'file', 'password'),
229
		'',
230
		array('autoFixDatabase', $txt['autoFixDatabase'], 'db', 'check', false, 'autoFixDatabase')
231
	);
232
233
	call_integration_hook('integrate_database_settings', array(&$config_vars));
234
235
	if ($return_config)
236
		return $config_vars;
237
238
	// Setup the template stuff.
239
	$context['post_url'] = $scripturl . '?action=admin;area=serversettings;sa=database;save';
240
	$context['settings_title'] = $txt['database_settings'];
241
	$context['save_disabled'] = $context['settings_not_writable'];
242
243
	// Saving settings?
244 View Code Duplication
	if (isset($_REQUEST['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...
245
	{
246
		call_integration_hook('integrate_save_database_settings');
247
248
		saveSettings($config_vars);
249
		$_SESSION['adm-save'] = true;
250
		redirectexit('action=admin;area=serversettings;sa=database;' . $context['session_var'] . '=' . $context['session_id'] );
251
	}
252
253
	// Fill the config array.
254
	prepareServerSettingsContext($config_vars);
255
}
256
257
/**
258
 * This function handles cookies settings modifications.
259
 *
260
 * @param bool $return_config Whether or not to return the config_vars array (used for admin search)
261
 * @return void|array Returns nothing or returns the $config_vars array if $return_config is true
262
 */
263
function ModifyCookieSettings($return_config = false)
264
{
265
	global $context, $scripturl, $txt, $sourcedir, $modSettings, $cookiename, $user_settings, $boardurl, $smcFunc;
266
267
	// Define the variables we want to edit.
268
	$config_vars = array(
269
		// Cookies...
270
		array('cookiename', $txt['cookie_name'], 'file', 'text', 20),
271
		array('cookieTime', $txt['cookieTime'], 'db', 'int', 'postinput' => $txt['minutes']),
272
		array('localCookies', $txt['localCookies'], 'db', 'check', false, 'localCookies'),
273
		array('globalCookies', $txt['globalCookies'], 'db', 'check', false, 'globalCookies'),
274
		array('globalCookiesDomain', $txt['globalCookiesDomain'], 'db', 'text', false, 'globalCookiesDomain'),
275
		array('secureCookies', $txt['secureCookies'], 'db', 'check', false, 'secureCookies',  'disabled' => !isset($_SERVER['HTTPS']) || !(strtolower($_SERVER['HTTPS']) == 'on' || strtolower($_SERVER['HTTPS']) == '1')),
276
		array('httponlyCookies', $txt['httponlyCookies'], 'db', 'check', false, 'httponlyCookies'),
277
		'',
278
		// Sessions
279
		array('databaseSession_enable', $txt['databaseSession_enable'], 'db', 'check', false, 'databaseSession_enable'),
280
		array('databaseSession_loose', $txt['databaseSession_loose'], 'db', 'check', false, 'databaseSession_loose'),
281
		array('databaseSession_lifetime', $txt['databaseSession_lifetime'], 'db', 'int', false, 'databaseSession_lifetime', 'postinput' => $txt['seconds']),
282
		'',
283
		// 2FA
284
		array('tfa_mode', $txt['tfa_mode'], 'db', 'select', array(
285
			0 => $txt['tfa_mode_disabled'],
286
			1 => $txt['tfa_mode_enabled'],
287
		) + (empty($user_settings['tfa_secret']) ? array() : array(
288
			2 => $txt['tfa_mode_forced'],
289
		)) + (empty($user_settings['tfa_secret']) ? array() : array(
290
			3 => $txt['tfa_mode_forcedall'],
291
		)), 'subtext' => $txt['tfa_mode_subtext'] . (empty($user_settings['tfa_secret']) ? '<br /><strong>' . $txt['tfa_mode_forced_help'] . '</strong>' : ''), 'tfa_mode'),
292
	);
293
294
	addInlineJavascript('
295
	function hideGlobalCookies()
296
	{
297
		var usingLocal = $("#localCookies").prop("checked");
298
		$("#setting_globalCookies").closest("dt").toggle(!usingLocal);
299
		$("#globalCookies").closest("dd").toggle(!usingLocal);
300
301
		var usingGlobal = !usingLocal && $("#globalCookies").prop("checked");
302
		$("#setting_globalCookiesDomain").closest("dt").toggle(usingGlobal);
303
		$("#globalCookiesDomain").closest("dd").toggle(usingGlobal);
304
	};
305
	hideGlobalCookies();
306
307
	$("#localCookies, #globalCookies").click(function() {
308
		hideGlobalCookies();
309
	});', true);
310
311
	if (empty($user_settings['tfa_secret']))
312
		addInlineJavascript('');
313
314
	call_integration_hook('integrate_cookie_settings', array(&$config_vars));
315
316
	if ($return_config)
317
		return $config_vars;
318
319
	$context['post_url'] = $scripturl . '?action=admin;area=serversettings;sa=cookie;save';
320
	$context['settings_title'] = $txt['cookies_sessions_settings'];
321
322
	// Saving settings?
323
	if (isset($_REQUEST['save']))
324
	{
325
		call_integration_hook('integrate_save_cookie_settings');
326
327
		// Local and global do not play nicely together.
328
		if (!empty($_POST['localCookies']) && empty($_POST['globalCookies']))
329
			unset ($_POST['globalCookies']);
330
331
		if (!empty($_POST['globalCookiesDomain']) && strpos($boardurl, $_POST['globalCookiesDomain']) === false)
332
			fatal_lang_error('invalid_cookie_domain', 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...
333
334
		saveSettings($config_vars);
335
336
		// If the cookie name was changed, reset the cookie.
337
		if ($cookiename != $_POST['cookiename'])
338
		{
339
			$original_session_id = $context['session_id'];
340
			include_once($sourcedir . '/Subs-Auth.php');
341
342
			// Remove the old cookie.
343
			setLoginCookie(-3600, 0);
344
345
			// Set the new one.
346
			$cookiename = $_POST['cookiename'];
347
			setLoginCookie(60 * $modSettings['cookieTime'], $user_settings['id_member'], hash_salt($user_settings['passwd'], $user_settings['password_salt']));
348
349
			redirectexit('action=admin;area=serversettings;sa=cookie;' . $context['session_var'] . '=' . $original_session_id, $context['server']['needs_login_fix']);
350
		}
351
352
		//If we disabled 2FA, reset all members and membergroups settings.
353
		if (isset($_POST['tfa_mode']) && empty($_POST['tfa_mode']))
354
		{
355
			$smcFunc['db_query']('', '
356
				UPDATE {db_prefix}membergroups
357
				SET tfa_required = {int:zero}',
358
				array(
359
					'zero' => 0,
360
				)
361
			);
362
			$smcFunc['db_query']('', '
363
				UPDATE {db_prefix}members
364
				SET tfa_secret = {string:empty}, tfa_backup = {string:empty}',
365
				array(
366
					'empty' => '',
367
				)
368
			);
369
		}
370
371
		$_SESSION['adm-save'] = true;
372
		redirectexit('action=admin;area=serversettings;sa=cookie;' . $context['session_var'] . '=' . $context['session_id']);
373
	}
374
375
	// Fill the config array.
376
	prepareServerSettingsContext($config_vars);
377
}
378
379
/**
380
 * Settings really associated with general security aspects.
381
 *
382
 * @param bool $return_config Whether or not to return the config_vars array (used for admin search)
383
 * @return void|array Returns nothing or returns the $config_vars array if $return_config is true
384
 */
385
function ModifyGeneralSecuritySettings($return_config = false)
386
{
387
	global $txt, $scripturl, $context;
388
389
	$config_vars = array(
390
			array('int', 'failed_login_threshold'),
391
			array('int', 'loginHistoryDays', 'subtext' => $txt['zero_to_disable']),
392
		'',
393
			array('check', 'securityDisable'),
394
			array('check', 'securityDisable_moderate'),
395
		'',
396
			// Reactive on email, and approve on delete
397
			array('check', 'send_validation_onChange'),
398
			array('check', 'approveAccountDeletion'),
399
		'',
400
			// Password strength.
401
			array('select', 'password_strength', array($txt['setting_password_strength_low'], $txt['setting_password_strength_medium'], $txt['setting_password_strength_high'])),
402
			array('check', 'enable_password_conversion'),
403
		'',
404
			// Reporting of personal messages?
405
			array('check', 'enableReportPM'),
406
		'',
407
			array('select', 'frame_security', array('SAMEORIGIN' => $txt['setting_frame_security_SAMEORIGIN'], 'DENY' => $txt['setting_frame_security_DENY'], 'DISABLE' => $txt['setting_frame_security_DISABLE'])),
408
		'',
409
			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_CLIENT_IP', 'HTTP_X_REAL_IP', 'CF-Connecting-IP')),
410
			array('text', 'proxy_ip_servers'),
411
	);
412
413
	call_integration_hook('integrate_general_security_settings', array(&$config_vars));
414
415
	if ($return_config)
416
		return $config_vars;
417
418
	// Saving?
419 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...
420
	{
421
		saveDBSettings($config_vars);
422
		$_SESSION['adm-save'] = true;
423
424
		call_integration_hook('integrate_save_general_security_settings');
425
426
		writeLog();
427
		redirectexit('action=admin;area=serversettings;sa=security;' . $context['session_var'] . '=' . $context['session_id']);
428
	}
429
430
	$context['post_url'] = $scripturl . '?action=admin;area=serversettings;save;sa=security';
431
	$context['settings_title'] = $txt['security_settings'];
432
433
	prepareDBSettingContext($config_vars);
434
}
435
436
/**
437
 * Simply modifying cache functions
438
 *
439
 * @param bool $return_config Whether or not to return the config_vars array (used for admin search)
440
 * @return void|array Returns nothing or returns the $config_vars array if $return_config is true
441
 */
442
function ModifyCacheSettings($return_config = false)
443
{
444
	global $context, $scripturl, $txt;
445
446
	// Detect all available optimizers
447
	$detected = array();
448
	if (function_exists('apc_store'))
449
		$detected['apc'] = $txt['apc_cache'];
450
	if (function_exists('output_cache_put') || function_exists('zend_shm_cache_store'))
451
		$detected['zend'] = $txt['zend_cache'];
452
	if (function_exists('memcache_set') || function_exists('memcached_set'))
453
		$detected['memcached'] = $txt['memcached_cache'];
454
	if (function_exists('xcache_set'))
455
		$detected['xcache'] = $txt['xcache_cache'];
456
	if (function_exists('file_put_contents'))
457
		$detected['smf'] = $txt['default_cache'];
458
459
	// set our values to show what, if anything, we found
460
	if (empty($detected))
461
	{
462
		$txt['cache_settings_message'] = $txt['detected_no_caching'];
463
		$cache_level = array($txt['cache_off']);
464
		$detected['none'] = $txt['cache_off'];
465
	}
466
	else
467
	{
468
		$txt['cache_settings_message'] = sprintf($txt['detected_accelerators'], implode(', ', $detected));
469
		$cache_level = array($txt['cache_off'], $txt['cache_level1'], $txt['cache_level2'], $txt['cache_level3']);
470
	}
471
472
	// Define the variables we want to edit.
473
	$config_vars = array(
474
		// Only a few settings, but they are important
475
		array('', $txt['cache_settings_message'], '', 'desc'),
476
		array('cache_enable', $txt['cache_enable'], 'file', 'select', $cache_level, 'cache_enable'),
477
		array('cache_accelerator', $txt['cache_accelerator'], 'file', 'select', $detected),
478
		array('cache_memcached', $txt['cache_memcached'], 'file', 'text', $txt['cache_memcached'], 'cache_memcached'),
479
		array('cachedir', $txt['cachedir'], 'file', 'text', 36, 'cache_cachedir'),
480
	);
481
482
	// some javascript to enable / disable certain settings if the option is not selected
483
	$context['settings_post_javascript'] = '
484
		var cache_type = document.getElementById(\'cache_accelerator\');
485
		createEventListener(cache_type);
486
		cache_type.addEventListener("change", toggleCache);
487
		toggleCache();';
488
489
	call_integration_hook('integrate_modify_cache_settings', array(&$config_vars));
490
491
	if ($return_config)
492
		return $config_vars;
493
494
	// Saving again?
495 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...
496
	{
497
		call_integration_hook('integrate_save_cache_settings');
498
499
		saveSettings($config_vars);
500
		$_SESSION['adm-save'] = true;
501
502
		// we need to save the $cache_enable to $modSettings as well
503
		updatesettings(array('cache_enable' => (int) $_POST['cache_enable']));
504
505
		// exit so we reload our new settings on the page
506
		redirectexit('action=admin;area=serversettings;sa=cache;' . $context['session_var'] . '=' . $context['session_id']);
507
	}
508
509
	loadLanguage('ManageMaintenance');
510
	createToken('admin-maint');
511
	$context['template_layers'][] = 'clean_cache_button';
512
513
	$context['post_url'] = $scripturl . '?action=admin;area=serversettings;sa=cache;save';
514
	$context['settings_title'] = $txt['caching_settings'];
515
516
	// Changing cache settings won't have any effect if Settings.php is not writeable.
517
	$context['save_disabled'] = $context['settings_not_writable'];
518
519
	// Decide what message to show.
520
	if (!$context['save_disabled'])
521
		$context['settings_message'] = $txt['caching_information'];
522
523
	// Prepare the template.
524
	prepareServerSettingsContext($config_vars);
525
}
526
527
/**
528
 * Allows to edit load balancing settings.
529
 *
530
 * @param bool $return_config Whether or not to return the config_vars array
531
 * @return void|array Returns nothing or returns the $config_vars array if $return_config is true
532
 */
533
function ModifyLoadBalancingSettings($return_config = false)
534
{
535
	global $txt, $scripturl, $context, $modSettings;
536
537
	// Setup a warning message, but disabled by default.
538
	$disabled = true;
539
	$context['settings_message'] = $txt['loadavg_disabled_conf'];
540
541
	if (stripos(PHP_OS, 'win') === 0)
542
	{
543
		$context['settings_message'] = $txt['loadavg_disabled_windows'];
544
		if (isset($_GET['save']))
545
			$_SESSION['adm-save'] = $txt['loadavg_disabled_windows'];
546
	}
547
	elseif (stripos(PHP_OS, 'darwin') === 0)
548
	{
549
		$context['settings_message'] = $txt['loadavg_disabled_osx'];
550
		if (isset($_GET['save']))
551
			$_SESSION['adm-save'] = $txt['loadavg_disabled_osx'];
552
	}
553
	else
554
	{
555
		$modSettings['load_average'] = @file_get_contents('/proc/loadavg');
556
		if (!empty($modSettings['load_average']) && preg_match('~^([^ ]+?) ([^ ]+?) ([^ ]+)~', $modSettings['load_average'], $matches) !== 0)
557
			$modSettings['load_average'] = (float) $matches[1];
558 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...
559
			$modSettings['load_average'] = (float) $matches[1];
560
		else
561
			unset($modSettings['load_average']);
562
563
		if (!empty($modSettings['load_average']) || $modSettings['load_average'] === 0.0)
564
		{
565
			$context['settings_message'] = sprintf($txt['loadavg_warning'], $modSettings['load_average']);
566
			$disabled = false;
567
		}
568
	}
569
570
	// Start with a simple checkbox.
571
	$config_vars = array(
572
		array('check', 'loadavg_enable', 'disabled' => $disabled),
573
	);
574
575
	// Set the default values for each option.
576
	$default_values = array(
577
		'loadavg_auto_opt' => 1.0,
578
		'loadavg_search' => 2.5,
579
		'loadavg_allunread' => 2.0,
580
		'loadavg_unreadreplies' => 3.5,
581
		'loadavg_show_posts' => 2.0,
582
		'loadavg_userstats' => 10.0,
583
		'loadavg_bbc' => 30.0,
584
		'loadavg_forum' => 40.0,
585
	);
586
587
	// Loop through the settings.
588
	foreach ($default_values as $name => $value)
589
	{
590
		// Use the default value if the setting isn't set yet.
591
		$value = !isset($modSettings[$name]) ? $value : $modSettings[$name];
592
		$config_vars[] = array('float', $name, 'value' => $value, 'disabled' => $disabled);
593
	}
594
595
	call_integration_hook('integrate_loadavg_settings', array(&$config_vars));
596
597
	if ($return_config)
598
		return $config_vars;
599
600
	$context['post_url'] = $scripturl . '?action=admin;area=serversettings;sa=loads;save';
601
	$context['settings_title'] = $txt['load_balancing_settings'];
602
603
	// Saving?
604
	if (isset($_GET['save']))
605
	{
606
		// Stupidity is not allowed.
607
		foreach ($_POST as $key => $value)
608
		{
609
			if (strpos($key, 'loadavg') === 0 || $key === 'loadavg_enable' || !in_array($key, array_keys($default_values)))
610
				continue;
611
			else
612
				$_POST[$key] = (float) $value;
613
614
			if ($key == 'loadavg_auto_opt' && $value <= 1)
615
				$_POST['loadavg_auto_opt'] = 1.0;
616
			elseif ($key == 'loadavg_forum' && $value < 10)
617
				$_POST['loadavg_forum'] = 10.0;
618
			elseif ($value < 2)
619
				$_POST[$key] = 2.0;
620
		}
621
622
		call_integration_hook('integrate_save_loadavg_settings');
623
624
		saveDBSettings($config_vars);
625
		if (!isset($_SESSION['adm-save']))
626
			$_SESSION['adm-save'] = true;
627
		redirectexit('action=admin;area=serversettings;sa=loads;' . $context['session_var'] . '=' . $context['session_id']);
628
	}
629
630
	prepareDBSettingContext($config_vars);
631
}
632
633
634
/**
635
 * Helper function, it sets up the context for the manage server settings.
636
 * - The basic usage of the six numbered key fields are
637
 * - array (0 ,1, 2, 3, 4, 5
638
 *		0 variable name - the name of the saved variable
639
 *		1 label - the text to show on the settings page
640
 *		2 saveto - file or db, where to save the variable name - value pair
641
 *		3 type - type of data to save, int, float, text, check
642
 *		4 size - false or field size
643
 *		5 help - '' or helptxt variable name
644
 *	)
645
 *
646
 * the following named keys are also permitted
647
 * 'disabled' => A string of code that will determine whether or not the setting should be disabled
648
 * 'postinput' => Text to display after the input field
649
 * 'preinput' => Text to display before the input field
650
 * 'subtext' => Additional descriptive text to display under the field's label
651
 * 'min' => minimum allowed value (for int/float). Defaults to 0 if not set.
652
 * 'max' => maximum allowed value (for int/float)
653
 * 'step' => how much to increment/decrement the value by (only for int/float - mostly used for float values).
654
 *
655
 * @param array $config_vars An array of configuration variables
656
 */
657
function prepareServerSettingsContext(&$config_vars)
658
{
659
	global $context, $modSettings, $smcFunc;
660
661 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...
662
	{
663
		if ($_SESSION['adm-save'] === true)
664
			$context['saved_successful'] = true;
665
		else
666
			$context['saved_failed'] = $_SESSION['adm-save'];
667
668
		unset($_SESSION['adm-save']);
669
	}
670
671
	$context['config_vars'] = array();
672
	foreach ($config_vars as $identifier => $config_var)
673
	{
674
		if (!is_array($config_var) || !isset($config_var[1]))
675
			$context['config_vars'][] = $config_var;
676
		else
677
		{
678
			$varname = $config_var[0];
679
			global $$varname;
680
681
			// Set the subtext in case it's part of the label.
682
			// @todo Temporary. Preventing divs inside label tags.
683
			$divPos = strpos($config_var[1], '<div');
684
			$subtext = '';
685
			if ($divPos !== false)
686
			{
687
				$subtext = preg_replace('~</?div[^>]*>~', '', substr($config_var[1], $divPos));
688
				$config_var[1] = substr($config_var[1], 0, $divPos);
689
			}
690
691
			$context['config_vars'][$config_var[0]] = array(
692
				'label' => $config_var[1],
693
				'help' => isset($config_var[5]) ? $config_var[5] : '',
694
				'type' => $config_var[3],
695
				'size' => empty($config_var[4]) ? 0 : $config_var[4],
696
				'data' => isset($config_var[4]) && is_array($config_var[4]) && $config_var[3] != 'select' ? $config_var[4] : array(),
697
				'name' => $config_var[0],
698
				'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 : '')),
699
				'disabled' => !empty($context['settings_not_writable']) || !empty($config_var['disabled']),
700
				'invalid' => false,
701
				'subtext' => !empty($config_var['subtext']) ? $config_var['subtext'] : $subtext,
702
				'javascript' => '',
703
				'preinput' => !empty($config_var['preinput']) ? $config_var['preinput'] : '',
704
				'postinput' => !empty($config_var['postinput']) ? $config_var['postinput'] : '',
705
			);
706
707
			// Handle min/max/step if necessary
708 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...
709
			{
710
				// Default to a min of 0 if one isn't set
711
				if (isset($config_var['min']))
712
					$context['config_vars'][$config_var[0]]['min'] = $config_var['min'];
713
				else
714
					$context['config_vars'][$config_var[0]]['min'] = 0;
715
716
				if (isset($config_var['max']))
717
					$context['config_vars'][$config_var[0]]['max'] = $config_var['max'];
718
719
				if (isset($config_var['step']))
720
					$context['config_vars'][$config_var[0]]['step'] = $config_var['step'];
721
			}
722
723
			// If this is a select box handle any data.
724
			if (!empty($config_var[4]) && is_array($config_var[4]))
725
			{
726
				// If it's associative
727
				$config_values = array_values($config_var[4]);
728 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...
729
					$context['config_vars'][$config_var[0]]['data'] = $config_var[4];
730
				else
731
				{
732
					foreach ($config_var[4] as $key => $item)
733
						$context['config_vars'][$config_var[0]]['data'][] = array($key, $item);
734
				}
735
			}
736
		}
737
	}
738
739
	// Two tokens because saving these settings requires both saveSettings and saveDBSettings
740
	createToken('admin-ssc');
741
	createToken('admin-dbsc');
742
}
743
744
/**
745
 * Helper function, it sets up the context for database settings.
746
 * @todo see rev. 10406 from 2.1-requests
747
 *
748
 * @param array $config_vars An array of configuration variables
749
 */
750
function prepareDBSettingContext(&$config_vars)
751
{
752
	global $txt, $helptxt, $context, $modSettings, $sourcedir, $smcFunc;
753
754
	loadLanguage('Help');
755
756 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...
757
	{
758
		if ($_SESSION['adm-save'] === true)
759
			$context['saved_successful'] = true;
760
		else
761
			$context['saved_failed'] = $_SESSION['adm-save'];
762
763
		unset($_SESSION['adm-save']);
764
	}
765
766
	$context['config_vars'] = array();
767
	$inlinePermissions = array();
768
	$bbcChoice = array();
769
	$board_list = false;
770
	foreach ($config_vars as $config_var)
771
	{
772
		// HR?
773
		if (!is_array($config_var))
774
			$context['config_vars'][] = $config_var;
775
		else
776
		{
777
			// If it has no name it doesn't have any purpose!
778
			if (empty($config_var[1]))
779
				continue;
780
781
			// Special case for inline permissions
782
			if ($config_var[0] == 'permissions' && allowedTo('manage_permissions'))
783
				$inlinePermissions[] = $config_var[1];
784
			elseif ($config_var[0] == 'permissions')
785
				continue;
786
787
			if ($config_var[0] == 'boards')
788
				$board_list = true;
789
790
			// Are we showing the BBC selection box?
791
			if ($config_var[0] == 'bbc')
792
				$bbcChoice[] = $config_var[1];
793
794
			// We need to do some parsing of the value before we pass it in.
795
			if (isset($modSettings[$config_var[1]]))
796
			{
797
				switch ($config_var[0])
798
				{
799
					case 'select':
800
						$value = $modSettings[$config_var[1]];
801
						break;
802
					case 'boards':
803
						$value = explode(',', $modSettings[$config_var[1]]);
804
						break;
805
					default:
806
						$value = $smcFunc['htmlspecialchars']($modSettings[$config_var[1]]);
807
				}
808
			}
809
			else
810
			{
811
				// Darn, it's empty. What type is expected?
812
				switch ($config_var[0])
813
				{
814
					case 'int':
815
					case 'float':
816
						$value = 0;
817
						break;
818
					case 'select':
819
						$value = !empty($config_var['multiple']) ? json_encode(array()) : '';
820
						break;
821
					case 'boards':
822
						$value = array();
823
						break;
824
					default:
825
						$value = '';
826
				}
827
			}
828
829
			$context['config_vars'][$config_var[1]] = array(
830
				'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] : '')),
831
				'help' => isset($helptxt[$config_var[1]]) ? $config_var[1] : '',
832
				'type' => $config_var[0],
833
				'size' => !empty($config_var[2]) && !is_array($config_var[2]) ? $config_var[2] : (in_array($config_var[0], array('int', 'float')) ? 6 : 0),
834
				'data' => array(),
835
				'name' => $config_var[1],
836
				'value' => $value,
837
				'disabled' => false,
838
				'invalid' => !empty($config_var['invalid']),
839
				'javascript' => '',
840
				'var_message' => !empty($config_var['message']) && isset($txt[$config_var['message']]) ? $txt[$config_var['message']] : '',
841
				'preinput' => isset($config_var['preinput']) ? $config_var['preinput'] : '',
842
				'postinput' => isset($config_var['postinput']) ? $config_var['postinput'] : '',
843
			);
844
845
			// Handle min/max/step if necessary
846 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...
847
			{
848
				// Default to a min of 0 if one isn't set
849
				if (isset($config_var['min']))
850
					$context['config_vars'][$config_var[1]]['min'] = $config_var['min'];
851
				else
852
					$context['config_vars'][$config_var[1]]['min'] = 0;
853
854
				if (isset($config_var['max']))
855
					$context['config_vars'][$config_var[1]]['max'] = $config_var['max'];
856
857
				if (isset($config_var['step']))
858
					$context['config_vars'][$config_var[1]]['step'] = $config_var['step'];
859
			}
860
861
			// If this is a select box handle any data.
862
			if (!empty($config_var[2]) && is_array($config_var[2]))
863
			{
864
				// If we allow multiple selections, we need to adjust a few things.
865
				if ($config_var[0] == 'select' && !empty($config_var['multiple']))
866
				{
867
					$context['config_vars'][$config_var[1]]['name'] .= '[]';
868
					$context['config_vars'][$config_var[1]]['value'] = !empty($context['config_vars'][$config_var[1]]['value']) ? json_decode($context['config_vars'][$config_var[1]]['value'], true) : array();
869
				}
870
871
				// If it's associative
872 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...
873
					$context['config_vars'][$config_var[1]]['data'] = $config_var[2];
874
				else
875
				{
876
					foreach ($config_var[2] as $key => $item)
877
						$context['config_vars'][$config_var[1]]['data'][] = array($key, $item);
878
				}
879
			}
880
881
			// Finally allow overrides - and some final cleanups.
882
			foreach ($config_var as $k => $v)
883
			{
884
				if (!is_numeric($k))
885
				{
886
					if (substr($k, 0, 2) == 'on')
887
						$context['config_vars'][$config_var[1]]['javascript'] .= ' ' . $k . '="' . $v . '"';
888
					else
889
						$context['config_vars'][$config_var[1]][$k] = $v;
890
				}
891
892
				// See if there are any other labels that might fit?
893
				if (isset($txt['setting_' . $config_var[1]]))
894
					$context['config_vars'][$config_var[1]]['label'] = $txt['setting_' . $config_var[1]];
895
				elseif (isset($txt['groups_' . $config_var[1]]))
896
					$context['config_vars'][$config_var[1]]['label'] = $txt['groups_' . $config_var[1]];
897
			}
898
899
			// Set the subtext in case it's part of the label.
900
			// @todo Temporary. Preventing divs inside label tags.
901
			$divPos = strpos($context['config_vars'][$config_var[1]]['label'], '<div');
902
			if ($divPos !== false)
903
			{
904
				$context['config_vars'][$config_var[1]]['subtext'] = preg_replace('~</?div[^>]*>~', '', substr($context['config_vars'][$config_var[1]]['label'], $divPos));
905
				$context['config_vars'][$config_var[1]]['label'] = substr($context['config_vars'][$config_var[1]]['label'], 0, $divPos);
906
			}
907
		}
908
	}
909
910
	// If we have inline permissions we need to prep them.
911
	if (!empty($inlinePermissions) && allowedTo('manage_permissions'))
912
	{
913
		require_once($sourcedir . '/ManagePermissions.php');
914
		init_inline_permissions($inlinePermissions, isset($context['permissions_excluded']) ? $context['permissions_excluded'] : array());
915
	}
916
917
	if ($board_list)
918
	{
919
		require_once($sourcedir . '/Subs-MessageIndex.php');
920
		$context['board_list'] = getBoardList();
921
	}
922
923
	// What about any BBC selection boxes?
924
	if (!empty($bbcChoice))
925
	{
926
		// What are the options, eh?
927
		$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...
928
		$bbcTags = array();
929
		foreach ($temp as $tag)
0 ignored issues
show
Bug introduced by
The expression $temp of type string is not traversable.
Loading history...
930
			$bbcTags[] = $tag['tag'];
931
932
		$bbcTags = array_unique($bbcTags);
933
		$totalTags = count($bbcTags);
934
935
		// The number of columns we want to show the BBC tags in.
936
		$numColumns = isset($context['num_bbc_columns']) ? $context['num_bbc_columns'] : 3;
937
938
		// Start working out the context stuff.
939
		$context['bbc_columns'] = array();
940
		$tagsPerColumn = ceil($totalTags / $numColumns);
941
942
		$col = 0; $i = 0;
943
		foreach ($bbcTags as $tag)
944
		{
945
			if ($i % $tagsPerColumn == 0 && $i != 0)
946
				$col++;
947
948
			$context['bbc_columns'][$col][] = array(
949
				'tag' => $tag,
950
				// @todo  'tag_' . ?
951
				'show_help' => isset($helptxt[$tag]),
952
			);
953
954
			$i++;
955
		}
956
957
		// Now put whatever BBC options we may have into context too!
958
		$context['bbc_sections'] = array();
959
		foreach ($bbcChoice as $bbc)
960
		{
961
			$context['bbc_sections'][$bbc] = array(
962
				'title' => isset($txt['bbc_title_' . $bbc]) ? $txt['bbc_title_' . $bbc] : $txt['bbcTagsToUse_select'],
963
				'disabled' => empty($modSettings['bbc_disabled_' . $bbc]) ? array() : $modSettings['bbc_disabled_' . $bbc],
964
				'all_selected' => empty($modSettings['bbc_disabled_' . $bbc]),
965
			);
966
		}
967
	}
968
969
	call_integration_hook('integrate_prepare_db_settings', array(&$config_vars));
970
	createToken('admin-dbsc');
971
}
972
973
/**
974
 * Helper function. Saves settings by putting them in Settings.php or saving them in the settings table.
975
 *
976
 * - Saves those settings set from ?action=admin;area=serversettings.
977
 * - Requires the admin_forum permission.
978
 * - Contains arrays of the types of data to save into Settings.php.
979
 *
980
 * @param $config_vars An array of configuration variables
981
 */
982
function saveSettings(&$config_vars)
983
{
984
	global $sourcedir, $context;
985
986
	validateToken('admin-ssc');
987
988
	// Fix the darn stupid cookiename! (more may not be allowed, but these for sure!)
989
	if (isset($_POST['cookiename']))
990
		$_POST['cookiename'] = preg_replace('~[,;\s\.$]+~' . ($context['utf8'] ? 'u' : ''), '', $_POST['cookiename']);
991
992
	// Fix the forum's URL if necessary.
993
	if (isset($_POST['boardurl']))
994
	{
995 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...
996
			$_POST['boardurl'] = substr($_POST['boardurl'], 0, -10);
997
		elseif (substr($_POST['boardurl'], -1) == '/')
998
			$_POST['boardurl'] = substr($_POST['boardurl'], 0, -1);
999 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...
1000
			$_POST['boardurl'] = 'http://' . $_POST['boardurl'];
1001
	}
1002
1003
	// Any passwords?
1004
	$config_passwords = array(
1005
		'db_passwd',
1006
		'ssi_db_passwd',
1007
	);
1008
1009
	// All the strings to write.
1010
	$config_strs = array(
1011
		'mtitle', 'mmessage',
1012
		'language', 'mbname', 'boardurl',
1013
		'cookiename',
1014
		'webmaster_email',
1015
		'db_name', 'db_user', 'db_server', 'db_prefix', 'ssi_db_user',
1016
		'boarddir', 'sourcedir',
1017
		'cachedir', 'cache_accelerator', 'cache_memcached',
1018
		'image_proxy_secret',
1019
	);
1020
1021
	// All the numeric variables.
1022
	$config_ints = array(
1023
		'cache_enable',
1024
		'image_proxy_maxsize',
1025
	);
1026
1027
	// All the checkboxes
1028
	$config_bools = array('db_persist', 'db_error_send', 'maintenance', 'image_proxy_enabled');
1029
1030
	// Now sort everything into a big array, and figure out arrays and etc.
1031
	$new_settings = array();
1032
	// Figure out which config vars we're saving here...
1033
	foreach ($config_vars as $var)
1034
	{
1035
		if (!is_array($var) || $var[2] != 'file' || (!in_array($var[0], $config_bools) && !isset($_POST[$var[0]])))
1036
			continue;
1037
1038
		$config_var = $var[0];
1039
1040
		if (in_array($config_var, $config_passwords))
1041
		{
1042
			if (isset($_POST[$config_var][1]) && $_POST[$config_var][0] == $_POST[$config_var][1])
1043
				$new_settings[$config_var] = '\'' . addcslashes($_POST[$config_var][0], '\'\\') . '\'';
1044
		}
1045
		elseif (in_array($config_var, $config_strs))
1046
		{
1047
			$new_settings[$config_var] = '\'' . addcslashes($_POST[$config_var], '\'\\') . '\'';
1048
		}
1049
		elseif (in_array($config_var, $config_ints))
1050
		{
1051
			$new_settings[$config_var] = (int) $_POST[$config_var];
1052
1053
			// 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...
1054
			$min = isset($var['min']) ? $var['min'] : 0;
1055
			$new_settings[$config_var] = max($min, $new_settings[$config_var]);
1056
1057
			// Is there a max value for this as well?
1058
			if (isset($var['max']))
1059
				$new_settings[$config_var] = min($var['max'], $new_settings[$config_var]);
1060
		}
1061
		elseif (in_array($config_var, $config_bools))
1062
		{
1063
			if (!empty($_POST[$config_var]))
1064
				$new_settings[$config_var] = '1';
1065
			else
1066
				$new_settings[$config_var] = '0';
1067
		}
1068
		else
1069
		{
1070
			// This shouldn't happen, but it might...
1071
			fatal_error('Unknown config_var \'' . $config_var . '\'');
1072
		}
1073
	}
1074
1075
	// Save the relevant settings in the Settings.php file.
1076
	require_once($sourcedir . '/Subs-Admin.php');
1077
	updateSettingsFile($new_settings);
1078
1079
	// Now loop through the remaining (database-based) settings.
1080
	$new_settings = array();
1081
	foreach ($config_vars as $config_var)
1082
	{
1083
		// We just saved the file-based settings, so skip their definitions.
1084
		if (!is_array($config_var) || $config_var[2] == 'file')
1085
			continue;
1086
1087
		$new_setting = array($config_var[3], $config_var[0]);
1088
1089
		// Select options need carried over, too.
1090
		if (isset($config_var[4]))
1091
			$new_setting[] = $config_var[4];
1092
1093
		// Include min and max if necessary
1094
		if (isset($config_var['min']))
1095
			$new_setting['min'] = $config_var['min'];
1096
1097
		if (isset($config_var['max']))
1098
			$new_setting['max'] = $config_var['max'];
1099
1100
		// Rewrite the definition a bit.
1101
		$new_settings[] = $new_setting;
1102
	}
1103
1104
	// Save the new database-based settings, if any.
1105
	if (!empty($new_settings))
1106
		saveDBSettings($new_settings);
1107
}
1108
1109
/**
1110
 * Helper function for saving database settings.
1111
 * @todo see rev. 10406 from 2.1-requests
1112
 *
1113
 * @param array $config_vars An array of configuration variables
1114
 */
1115
function saveDBSettings(&$config_vars)
1116
{
1117
	global $sourcedir, $smcFunc;
1118
	static $board_list = null;
1119
1120
	validateToken('admin-dbsc');
1121
1122
	$inlinePermissions = array();
1123
	foreach ($config_vars as $var)
1124
	{
1125
		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']))))
1126
			continue;
1127
1128
		// Checkboxes!
1129
		elseif ($var[0] == 'check')
1130
			$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...
1131
		// Select boxes!
1132
		elseif ($var[0] == 'select' && in_array($_POST[$var[1]], array_keys($var[2])))
1133
			$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...
1134
		elseif ($var[0] == 'select' && !empty($var['multiple']) && array_intersect($_POST[$var[1]], array_keys($var[2])) != array())
1135
		{
1136
			// For security purposes we validate this line by line.
1137
			$lOptions = array();
1138
			foreach ($_POST[$var[1]] as $invar)
1139
				if (in_array($invar, array_keys($var[2])))
1140
					$lOptions[] = $invar;
1141
1142
			$setArray[$var[1]] = json_encode($lOptions);
1143
		}
1144
		// List of boards!
1145
		elseif ($var[0] == 'boards')
1146
		{
1147
			// We just need a simple list of valid boards, nothing more.
1148
			if ($board_list === null)
1149
			{
1150
				$board_list = array();
1151
				$request = $smcFunc['db_query']('', '
1152
					SELECT id_board
1153
					FROM {db_prefix}boards');
1154
				while ($row = $smcFunc['db_fetch_row']($request))
1155
					$board_list[$row[0]] = true;
1156
1157
				$smcFunc['db_free_result']($request);
1158
			}
1159
1160
			$lOptions = array();
1161
1162
			if (!empty($_POST[$var[1]]))
1163
				foreach ($_POST[$var[1]] as $invar => $dummy)
1164
					if (isset($board_list[$invar]))
1165
						$lOptions[] = $invar;
1166
1167
			$setArray[$var[1]] = !empty($lOptions) ? implode(',', $lOptions) : '';
1168
		}
1169
		// Integers!
1170 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...
1171
		{
1172
			$setArray[$var[1]] = (int) $_POST[$var[1]];
1173
1174
			// 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...
1175
			$min = isset($var['min']) ? $var['min'] : 0;
1176
			$setArray[$var[1]] = max($min, $setArray[$var[1]]);
1177
1178
			// Do we have a max value for this as well?
1179
			if (isset($var['max']))
1180
				$setArray[$var[1]] = min($var['max'], $setArray[$var[1]]);
1181
		}
1182
		// Floating point!
1183 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...
1184
		{
1185
			$setArray[$var[1]] = (float) $_POST[$var[1]];
1186
1187
			// 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...
1188
			$min = isset($var['min']) ? $var['min'] : 0;
1189
			$setArray[$var[1]] = max($min, $setArray[$var[1]]);
1190
1191
			// Do we have a max value for this as well?
1192
			if (isset($var['max']))
1193
				$setArray[$var[1]] = min($var['max'], $setArray[$var[1]]);
1194
		}
1195
		// Text!
1196
		elseif (in_array($var[0], array('text', 'large_text', 'color', 'date', 'datetime', 'datetime-local', 'email', 'month', 'time')))
1197
			$setArray[$var[1]] = $_POST[$var[1]];
1198
		// Passwords!
1199
		elseif ($var[0] == 'password')
1200
		{
1201
			if (isset($_POST[$var[1]][1]) && $_POST[$var[1]][0] == $_POST[$var[1]][1])
1202
				$setArray[$var[1]] = $_POST[$var[1]][0];
1203
		}
1204
		// BBC.
1205
		elseif ($var[0] == 'bbc')
1206
		{
1207
1208
			$bbcTags = array();
1209
			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...
1210
				$bbcTags[] = $tag['tag'];
1211
1212
			if (!isset($_POST[$var[1] . '_enabledTags']))
1213
				$_POST[$var[1] . '_enabledTags'] = array();
1214
			elseif (!is_array($_POST[$var[1] . '_enabledTags']))
1215
				$_POST[$var[1] . '_enabledTags'] = array($_POST[$var[1] . '_enabledTags']);
1216
1217
			$setArray[$var[1]] = implode(',', array_diff($bbcTags, $_POST[$var[1] . '_enabledTags']));
1218
		}
1219
		// Permissions?
1220
		elseif ($var[0] == 'permissions')
1221
			$inlinePermissions[] = $var[1];
1222
	}
1223
1224
	if (!empty($setArray))
1225
		updateSettings($setArray);
1226
1227
	// If we have inline permissions we need to save them.
1228
	if (!empty($inlinePermissions) && allowedTo('manage_permissions'))
1229
	{
1230
		require_once($sourcedir . '/ManagePermissions.php');
1231
		save_inline_permissions($inlinePermissions);
1232
	}
1233
}
1234
1235
/**
1236
 * Allows us to see the servers php settings
1237
 *
1238
 * - loads the settings into an array for display in a template
1239
 * - drops cookie values just in case
1240
 */
1241
function ShowPHPinfoSettings()
1242
{
1243
	global $context, $txt;
1244
1245
	$info_lines = array();
0 ignored issues
show
Unused Code introduced by
$info_lines is not used, you could remove the assignment.

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

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

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

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

Loading history...
1246
	$category = $txt['phpinfo_settings'];
1247
1248
	// get the data
1249
	ob_start();
1250
	phpinfo();
1251
1252
	// We only want it for its body, pigs that we are
1253
	$info_lines = preg_replace('~^.*<body>(.*)</body>.*$~', '$1', ob_get_contents());
1254
	$info_lines = explode("\n", strip_tags($info_lines, "<tr><td><h2>"));
1255
	ob_end_clean();
1256
1257
	// remove things that could be considered sensitive
1258
	$remove = '_COOKIE|Cookie|_GET|_REQUEST|REQUEST_URI|QUERY_STRING|REQUEST_URL|HTTP_REFERER';
1259
1260
	// put all of it into an array
1261
	foreach ($info_lines as $line)
1262
	{
1263
		if (preg_match('~(' . $remove . ')~', $line))
1264
			continue;
1265
1266
		// new category?
1267
		if (strpos($line, '<h2>') !== false)
1268
			$category = preg_match('~<h2>(.*)</h2>~', $line, $title) ? $category = $title[1] : $category;
1269
1270
		// load it as setting => value or the old setting local master
1271
		if (preg_match('~<tr><td[^>]+>([^<]*)</td><td[^>]+>([^<]*)</td></tr>~', $line, $val))
1272
			$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...
1273
		elseif (preg_match('~<tr><td[^>]+>([^<]*)</td><td[^>]+>([^<]*)</td><td[^>]+>([^<]*)</td></tr>~', $line, $val))
1274
			$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...
1275
	}
1276
1277
	// load it in to context and display it
1278
	$context['pinfo'] = $pinfo;
1279
	$context['page_title'] = $txt['admin_server_settings'];
1280
	$context['sub_template'] = 'php_info';
1281
	return;
1282
}
1283
1284
?>
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...