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