Completed
Branch release-2.1 (e49a83)
by Mert
04:10
created

ManageServer.php ➔ loadCacheAPIs()   C

Complexity

Conditions 7
Paths 2

Size

Total Lines 32
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 16
nc 2
nop 0
dl 0
loc 32
rs 6.7272
c 0
b 0
f 0
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_X_FORWARDED_FOR', 'HTTP_CLIENT_IP' => 'HTTP_CLIENT_IP', 'HTTP_X_REAL_IP' => 'HTTP_X_REAL_IP', 'CF-Connecting-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, $cacheAPI;
445
446
	// Detect all available optimizers
447
	$detected = loadCacheAPIs();
448
449
	// set our values to show what, if anything, we found
450
	if (empty($detected))
451
	{
452
		$txt['cache_settings_message'] = $txt['detected_no_caching'];
453
		$cache_level = array($txt['cache_off']);
454
		$detected['none'] = $txt['cache_off'];
455
	}
456
	else
457
	{
458
		$txt['cache_settings_message'] = sprintf($txt['detected_accelerators'], implode(', ', $detected));
459
		$cache_level = array($txt['cache_off'], $txt['cache_level1'], $txt['cache_level2'], $txt['cache_level3']);
460
	}
461
462
	// Define the variables we want to edit.
463
	$config_vars = array(
464
		// Only a few settings, but they are important
465
		array('', $txt['cache_settings_message'], '', 'desc'),
466
		array('cache_enable', $txt['cache_enable'], 'file', 'select', $cache_level, 'cache_enable'),
467
		array('cache_accelerator', $txt['cache_accelerator'], 'file', 'select', $detected),
468
	);
469
470
	// some javascript to enable / disable certain settings if the option is not selected
471
	$context['settings_post_javascript'] = '
472
		$(document).ready(function() {
473
			$("#cache_accelerator").change();
474
		});';
475
476
	call_integration_hook('integrate_modify_cache_settings', array(&$config_vars));
477
478
	// Maybe we have some additional settings from the selected accelerator.
479
	if (!empty($detected))
480
	{
481
		foreach ($detected as $tryCache => $dummy)
482
		{
483
			$cache_class_name = $tryCache . '_cache';
484
485
			// loadCacheAPIs has already included the file, just see if we can't add the settings in.
486
			if (is_callable(array($cache_class_name, 'cacheSettings')))
487
			{
488
				$testAPI = new $cache_class_name();
489
				call_user_func_array(array($testAPI, 'cacheSettings'), array(&$config_vars));
490
			}
491
		}
492
	}
493
	if ($return_config)
494
		return $config_vars;
495
496
	// Saving again?
497 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...
498
	{
499
		call_integration_hook('integrate_save_cache_settings');
500
501
		saveSettings($config_vars);
502
		$_SESSION['adm-save'] = true;
503
504
		// We need to save the $cache_enable to $modSettings as well
505
		updatesettings(array('cache_enable' => (int) $_POST['cache_enable']));
506
507
		// exit so we reload our new settings on the page
508
		redirectexit('action=admin;area=serversettings;sa=cache;' . $context['session_var'] . '=' . $context['session_id']);
509
	}
510
511
	loadLanguage('ManageMaintenance');
512
	createToken('admin-maint');
513
	$context['template_layers'][] = 'clean_cache_button';
514
515
	$context['post_url'] = $scripturl . '?action=admin;area=serversettings;sa=cache;save';
516
	$context['settings_title'] = $txt['caching_settings'];
517
518
	// Changing cache settings won't have any effect if Settings.php is not writeable.
519
	$context['save_disabled'] = $context['settings_not_writable'];
520
521
	// Decide what message to show.
522
	if (!$context['save_disabled'])
523
		$context['settings_message'] = $txt['caching_information'];
524
525
	// Prepare the template.
526
	prepareServerSettingsContext($config_vars);
527
}
528
529
/**
530
 * Allows to edit load balancing settings.
531
 *
532
 * @param bool $return_config Whether or not to return the config_vars array
533
 * @return void|array Returns nothing or returns the $config_vars array if $return_config is true
534
 */
535
function ModifyLoadBalancingSettings($return_config = false)
536
{
537
	global $txt, $scripturl, $context, $modSettings;
538
539
	// Setup a warning message, but disabled by default.
540
	$disabled = true;
541
	$context['settings_message'] = $txt['loadavg_disabled_conf'];
542
543
	if (stripos(PHP_OS, 'win') === 0)
544
	{
545
		$context['settings_message'] = $txt['loadavg_disabled_windows'];
546
		if (isset($_GET['save']))
547
			$_SESSION['adm-save'] = $txt['loadavg_disabled_windows'];
548
	}
549
	elseif (stripos(PHP_OS, 'darwin') === 0)
550
	{
551
		$context['settings_message'] = $txt['loadavg_disabled_osx'];
552
		if (isset($_GET['save']))
553
			$_SESSION['adm-save'] = $txt['loadavg_disabled_osx'];
554
	}
555
	else
556
	{
557
		$modSettings['load_average'] = @file_get_contents('/proc/loadavg');
558
		if (!empty($modSettings['load_average']) && preg_match('~^([^ ]+?) ([^ ]+?) ([^ ]+)~', $modSettings['load_average'], $matches) !== 0)
559
			$modSettings['load_average'] = (float) $matches[1];
560 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...
561
			$modSettings['load_average'] = (float) $matches[1];
562
		else
563
			unset($modSettings['load_average']);
564
565
		if (!empty($modSettings['load_average']) || $modSettings['load_average'] === 0.0)
566
		{
567
			$context['settings_message'] = sprintf($txt['loadavg_warning'], $modSettings['load_average']);
568
			$disabled = false;
569
		}
570
	}
571
572
	// Start with a simple checkbox.
573
	$config_vars = array(
574
		array('check', 'loadavg_enable', 'disabled' => $disabled),
575
	);
576
577
	// Set the default values for each option.
578
	$default_values = array(
579
		'loadavg_auto_opt' => 1.0,
580
		'loadavg_search' => 2.5,
581
		'loadavg_allunread' => 2.0,
582
		'loadavg_unreadreplies' => 3.5,
583
		'loadavg_show_posts' => 2.0,
584
		'loadavg_userstats' => 10.0,
585
		'loadavg_bbc' => 30.0,
586
		'loadavg_forum' => 40.0,
587
	);
588
589
	// Loop through the settings.
590
	foreach ($default_values as $name => $value)
591
	{
592
		// Use the default value if the setting isn't set yet.
593
		$value = !isset($modSettings[$name]) ? $value : $modSettings[$name];
594
		$config_vars[] = array('float', $name, 'value' => $value, 'disabled' => $disabled);
595
	}
596
597
	call_integration_hook('integrate_loadavg_settings', array(&$config_vars));
598
599
	if ($return_config)
600
		return $config_vars;
601
602
	$context['post_url'] = $scripturl . '?action=admin;area=serversettings;sa=loads;save';
603
	$context['settings_title'] = $txt['load_balancing_settings'];
604
605
	// Saving?
606
	if (isset($_GET['save']))
607
	{
608
		// Stupidity is not allowed.
609
		foreach ($_POST as $key => $value)
610
		{
611
			if (strpos($key, 'loadavg') === 0 || $key === 'loadavg_enable' || !in_array($key, array_keys($default_values)))
612
				continue;
613
			else
614
				$_POST[$key] = (float) $value;
615
616
			if ($key == 'loadavg_auto_opt' && $value <= 1)
617
				$_POST['loadavg_auto_opt'] = 1.0;
618
			elseif ($key == 'loadavg_forum' && $value < 10)
619
				$_POST['loadavg_forum'] = 10.0;
620
			elseif ($value < 2)
621
				$_POST[$key] = 2.0;
622
		}
623
624
		call_integration_hook('integrate_save_loadavg_settings');
625
626
		saveDBSettings($config_vars);
627
		if (!isset($_SESSION['adm-save']))
628
			$_SESSION['adm-save'] = true;
629
		redirectexit('action=admin;area=serversettings;sa=loads;' . $context['session_var'] . '=' . $context['session_id']);
630
	}
631
632
	prepareDBSettingContext($config_vars);
633
}
634
635
/**
636
 * Helper function, it sets up the context for the manage server settings.
637
 * - The basic usage of the six numbered key fields are
638
 * - array (0 ,1, 2, 3, 4, 5
639
 *		0 variable name - the name of the saved variable
640
 *		1 label - the text to show on the settings page
641
 *		2 saveto - file or db, where to save the variable name - value pair
642
 *		3 type - type of data to save, int, float, text, check
643
 *		4 size - false or field size
644
 *		5 help - '' or helptxt variable name
645
 *	)
646
 *
647
 * the following named keys are also permitted
648
 * 'disabled' => A string of code that will determine whether or not the setting should be disabled
649
 * 'postinput' => Text to display after the input field
650
 * 'preinput' => Text to display before the input field
651
 * 'subtext' => Additional descriptive text to display under the field's label
652
 * 'min' => minimum allowed value (for int/float). Defaults to 0 if not set.
653
 * 'max' => maximum allowed value (for int/float)
654
 * 'step' => how much to increment/decrement the value by (only for int/float - mostly used for float values).
655
 *
656
 * @param array $config_vars An array of configuration variables
657
 */
658
function prepareServerSettingsContext(&$config_vars)
659
{
660
	global $context, $modSettings, $smcFunc;
661
662 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...
663
	{
664
		if ($_SESSION['adm-save'] === true)
665
			$context['saved_successful'] = true;
666
		else
667
			$context['saved_failed'] = $_SESSION['adm-save'];
668
669
		unset($_SESSION['adm-save']);
670
	}
671
672
	$context['config_vars'] = array();
673
	foreach ($config_vars as $identifier => $config_var)
674
	{
675
		if (!is_array($config_var) || !isset($config_var[1]))
676
			$context['config_vars'][] = $config_var;
677
		else
678
		{
679
			$varname = $config_var[0];
680
			global $$varname;
681
682
			// Set the subtext in case it's part of the label.
683
			// @todo Temporary. Preventing divs inside label tags.
684
			$divPos = strpos($config_var[1], '<div');
685
			$subtext = '';
686
			if ($divPos !== false)
687
			{
688
				$subtext = preg_replace('~</?div[^>]*>~', '', substr($config_var[1], $divPos));
689
				$config_var[1] = substr($config_var[1], 0, $divPos);
690
			}
691
692
			$context['config_vars'][$config_var[0]] = array(
693
				'label' => $config_var[1],
694
				'help' => isset($config_var[5]) ? $config_var[5] : '',
695
				'type' => $config_var[3],
696
				'size' => empty($config_var[4]) ? 0 : $config_var[4],
697
				'data' => isset($config_var[4]) && is_array($config_var[4]) && $config_var[3] != 'select' ? $config_var[4] : array(),
698
				'name' => $config_var[0],
699
				'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 : '')),
700
				'disabled' => !empty($context['settings_not_writable']) || !empty($config_var['disabled']),
701
				'invalid' => false,
702
				'subtext' => !empty($config_var['subtext']) ? $config_var['subtext'] : $subtext,
703
				'javascript' => '',
704
				'preinput' => !empty($config_var['preinput']) ? $config_var['preinput'] : '',
705
				'postinput' => !empty($config_var['postinput']) ? $config_var['postinput'] : '',
706
			);
707
708
			// Handle min/max/step if necessary
709 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...
710
			{
711
				// Default to a min of 0 if one isn't set
712
				if (isset($config_var['min']))
713
					$context['config_vars'][$config_var[0]]['min'] = $config_var['min'];
714
				else
715
					$context['config_vars'][$config_var[0]]['min'] = 0;
716
717
				if (isset($config_var['max']))
718
					$context['config_vars'][$config_var[0]]['max'] = $config_var['max'];
719
720
				if (isset($config_var['step']))
721
					$context['config_vars'][$config_var[0]]['step'] = $config_var['step'];
722
			}
723
724
			// If this is a select box handle any data.
725
			if (!empty($config_var[4]) && is_array($config_var[4]))
726
			{
727
				// If it's associative
728
				$config_values = array_values($config_var[4]);
729 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...
730
					$context['config_vars'][$config_var[0]]['data'] = $config_var[4];
731
				else
732
				{
733
					foreach ($config_var[4] as $key => $item)
734
						$context['config_vars'][$config_var[0]]['data'][] = array($key, $item);
735
				}
736
			}
737
		}
738
	}
739
740
	// Two tokens because saving these settings requires both saveSettings and saveDBSettings
741
	createToken('admin-ssc');
742
	createToken('admin-dbsc');
743
}
744
745
/**
746
 * Helper function, it sets up the context for database settings.
747
 * @todo see rev. 10406 from 2.1-requests
748
 *
749
 * @param array $config_vars An array of configuration variables
750
 */
751
function prepareDBSettingContext(&$config_vars)
752
{
753
	global $txt, $helptxt, $context, $modSettings, $sourcedir, $smcFunc;
754
755
	loadLanguage('Help');
756
757 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...
758
	{
759
		if ($_SESSION['adm-save'] === true)
760
			$context['saved_successful'] = true;
761
		else
762
			$context['saved_failed'] = $_SESSION['adm-save'];
763
764
		unset($_SESSION['adm-save']);
765
	}
766
767
	$context['config_vars'] = array();
768
	$inlinePermissions = array();
769
	$bbcChoice = array();
770
	$board_list = false;
771
	foreach ($config_vars as $config_var)
772
	{
773
		// HR?
774
		if (!is_array($config_var))
775
			$context['config_vars'][] = $config_var;
776
		else
777
		{
778
			// If it has no name it doesn't have any purpose!
779
			if (empty($config_var[1]))
780
				continue;
781
782
			// Special case for inline permissions
783
			if ($config_var[0] == 'permissions' && allowedTo('manage_permissions'))
784
				$inlinePermissions[] = $config_var[1];
785
			elseif ($config_var[0] == 'permissions')
786
				continue;
787
788
			if ($config_var[0] == 'boards')
789
				$board_list = true;
790
791
			// Are we showing the BBC selection box?
792
			if ($config_var[0] == 'bbc')
793
				$bbcChoice[] = $config_var[1];
794
795
			// We need to do some parsing of the value before we pass it in.
796
			if (isset($modSettings[$config_var[1]]))
797
			{
798
				switch ($config_var[0])
799
				{
800
					case 'select':
801
						$value = $modSettings[$config_var[1]];
802
						break;
803
					case 'json':
804
						$value = $smcFunc['htmlspecialchars'](json_encode($modSettings[$config_var[1]]));
805
						break;
806
					case 'boards':
807
						$value = explode(',', $modSettings[$config_var[1]]);
808
						break;
809
					default:
810
						$value = $smcFunc['htmlspecialchars']($modSettings[$config_var[1]]);
811
				}
812
			}
813
			else
814
			{
815
				// Darn, it's empty. What type is expected?
816
				switch ($config_var[0])
817
				{
818
					case 'int':
819
					case 'float':
820
						$value = 0;
821
						break;
822
					case 'select':
823
						$value = !empty($config_var['multiple']) ? json_encode(array()) : '';
824
						break;
825
					case 'boards':
826
						$value = array();
827
						break;
828
					default:
829
						$value = '';
830
				}
831
			}
832
833
			$context['config_vars'][$config_var[1]] = array(
834
				'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] : '')),
835
				'help' => isset($helptxt[$config_var[1]]) ? $config_var[1] : '',
836
				'type' => $config_var[0],
837
				'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)),
838
				'data' => array(),
839
				'name' => $config_var[1],
840
				'value' => $value,
841
				'disabled' => false,
842
				'invalid' => !empty($config_var['invalid']),
843
				'javascript' => '',
844
				'var_message' => !empty($config_var['message']) && isset($txt[$config_var['message']]) ? $txt[$config_var['message']] : '',
845
				'preinput' => isset($config_var['preinput']) ? $config_var['preinput'] : '',
846
				'postinput' => isset($config_var['postinput']) ? $config_var['postinput'] : '',
847
			);
848
849
			// Handle min/max/step if necessary
850 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...
851
			{
852
				// Default to a min of 0 if one isn't set
853
				if (isset($config_var['min']))
854
					$context['config_vars'][$config_var[1]]['min'] = $config_var['min'];
855
				else
856
					$context['config_vars'][$config_var[1]]['min'] = 0;
857
858
				if (isset($config_var['max']))
859
					$context['config_vars'][$config_var[1]]['max'] = $config_var['max'];
860
861
				if (isset($config_var['step']))
862
					$context['config_vars'][$config_var[1]]['step'] = $config_var['step'];
863
			}
864
865
			// If this is a select box handle any data.
866
			if (!empty($config_var[2]) && is_array($config_var[2]))
867
			{
868
				// If we allow multiple selections, we need to adjust a few things.
869
				if ($config_var[0] == 'select' && !empty($config_var['multiple']))
870
				{
871
					$context['config_vars'][$config_var[1]]['name'] .= '[]';
872
					$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();
873
				}
874
875
				// If it's associative
876 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...
877
					$context['config_vars'][$config_var[1]]['data'] = $config_var[2];
878
				else
879
				{
880
					foreach ($config_var[2] as $key => $item)
881
						$context['config_vars'][$config_var[1]]['data'][] = array($key, $item);
882
				}
883
			}
884
885
			// Finally allow overrides - and some final cleanups.
886
			foreach ($config_var as $k => $v)
887
			{
888
				if (!is_numeric($k))
889
				{
890
					if (substr($k, 0, 2) == 'on')
891
						$context['config_vars'][$config_var[1]]['javascript'] .= ' ' . $k . '="' . $v . '"';
892
					else
893
						$context['config_vars'][$config_var[1]][$k] = $v;
894
				}
895
896
				// See if there are any other labels that might fit?
897
				if (isset($txt['setting_' . $config_var[1]]))
898
					$context['config_vars'][$config_var[1]]['label'] = $txt['setting_' . $config_var[1]];
899
				elseif (isset($txt['groups_' . $config_var[1]]))
900
					$context['config_vars'][$config_var[1]]['label'] = $txt['groups_' . $config_var[1]];
901
			}
902
903
			// Set the subtext in case it's part of the label.
904
			// @todo Temporary. Preventing divs inside label tags.
905
			$divPos = strpos($context['config_vars'][$config_var[1]]['label'], '<div');
906
			if ($divPos !== false)
907
			{
908
				$context['config_vars'][$config_var[1]]['subtext'] = preg_replace('~</?div[^>]*>~', '', substr($context['config_vars'][$config_var[1]]['label'], $divPos));
909
				$context['config_vars'][$config_var[1]]['label'] = substr($context['config_vars'][$config_var[1]]['label'], 0, $divPos);
910
			}
911
		}
912
	}
913
914
	// If we have inline permissions we need to prep them.
915
	if (!empty($inlinePermissions) && allowedTo('manage_permissions'))
916
	{
917
		require_once($sourcedir . '/ManagePermissions.php');
918
		init_inline_permissions($inlinePermissions, isset($context['permissions_excluded']) ? $context['permissions_excluded'] : array());
919
	}
920
921
	if ($board_list)
922
	{
923
		require_once($sourcedir . '/Subs-MessageIndex.php');
924
		$context['board_list'] = getBoardList();
925
	}
926
927
	// What about any BBC selection boxes?
928
	if (!empty($bbcChoice))
929
	{
930
		// What are the options, eh?
931
		$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...
932
		$bbcTags = array();
933
		foreach ($temp as $tag)
0 ignored issues
show
Bug introduced by
The expression $temp of type string is not traversable.
Loading history...
934
			$bbcTags[] = $tag['tag'];
935
936
		$bbcTags = array_unique($bbcTags);
937
		$totalTags = count($bbcTags);
938
939
		// The number of columns we want to show the BBC tags in.
940
		$numColumns = isset($context['num_bbc_columns']) ? $context['num_bbc_columns'] : 3;
941
942
		// Start working out the context stuff.
943
		$context['bbc_columns'] = array();
944
		$tagsPerColumn = ceil($totalTags / $numColumns);
945
946
		$col = 0; $i = 0;
947
		foreach ($bbcTags as $tag)
948
		{
949
			if ($i % $tagsPerColumn == 0 && $i != 0)
950
				$col++;
951
952
			$context['bbc_columns'][$col][] = array(
953
				'tag' => $tag,
954
				// @todo  'tag_' . ?
955
				'show_help' => isset($helptxt[$tag]),
956
			);
957
958
			$i++;
959
		}
960
961
		// Now put whatever BBC options we may have into context too!
962
		$context['bbc_sections'] = array();
963
		foreach ($bbcChoice as $bbc)
964
		{
965
			$context['bbc_sections'][$bbc] = array(
966
				'title' => isset($txt['bbc_title_' . $bbc]) ? $txt['bbc_title_' . $bbc] : $txt['bbcTagsToUse_select'],
967
				'disabled' => empty($modSettings['bbc_disabled_' . $bbc]) ? array() : $modSettings['bbc_disabled_' . $bbc],
968
				'all_selected' => empty($modSettings['bbc_disabled_' . $bbc]),
969
			);
970
		}
971
	}
972
973
	call_integration_hook('integrate_prepare_db_settings', array(&$config_vars));
974
	createToken('admin-dbsc');
975
}
976
977
/**
978
 * Helper function. Saves settings by putting them in Settings.php or saving them in the settings table.
979
 *
980
 * - Saves those settings set from ?action=admin;area=serversettings.
981
 * - Requires the admin_forum permission.
982
 * - Contains arrays of the types of data to save into Settings.php.
983
 *
984
 * @param $config_vars An array of configuration variables
985
 */
986
function saveSettings(&$config_vars)
987
{
988
	global $sourcedir, $context;
989
990
	validateToken('admin-ssc');
991
992
	// Fix the darn stupid cookiename! (more may not be allowed, but these for sure!)
993
	if (isset($_POST['cookiename']))
994
		$_POST['cookiename'] = preg_replace('~[,;\s\.$]+~' . ($context['utf8'] ? 'u' : ''), '', $_POST['cookiename']);
995
996
	// Fix the forum's URL if necessary.
997
	if (isset($_POST['boardurl']))
998
	{
999 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...
1000
			$_POST['boardurl'] = substr($_POST['boardurl'], 0, -10);
1001
		elseif (substr($_POST['boardurl'], -1) == '/')
1002
			$_POST['boardurl'] = substr($_POST['boardurl'], 0, -1);
1003 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...
1004
			$_POST['boardurl'] = 'http://' . $_POST['boardurl'];
1005
	}
1006
1007
	// Any passwords?
1008
	$config_passwords = array(
1009
		'db_passwd',
1010
		'ssi_db_passwd',
1011
	);
1012
1013
	// All the strings to write.
1014
	$config_strs = array(
1015
		'mtitle', 'mmessage',
1016
		'language', 'mbname', 'boardurl',
1017
		'cookiename',
1018
		'webmaster_email',
1019
		'db_name', 'db_user', 'db_server', 'db_prefix', 'ssi_db_user',
1020
		'boarddir', 'sourcedir',
1021
		'cachedir', 'cache_accelerator', 'cache_memcached',
1022
		'image_proxy_secret',
1023
	);
1024
1025
	// All the numeric variables.
1026
	$config_ints = array(
1027
		'cache_enable',
1028
		'image_proxy_maxsize',
1029
	);
1030
1031
	// All the checkboxes
1032
	$config_bools = array('db_persist', 'db_error_send', 'maintenance', 'image_proxy_enabled');
1033
1034
	// Now sort everything into a big array, and figure out arrays and etc.
1035
	$new_settings = array();
1036
	// Figure out which config vars we're saving here...
1037
	foreach ($config_vars as $var)
1038
	{
1039
		if (!is_array($var) || $var[2] != 'file' || (!in_array($var[0], $config_bools) && !isset($_POST[$var[0]])))
1040
			continue;
1041
1042
		$config_var = $var[0];
1043
1044
		if (in_array($config_var, $config_passwords))
1045
		{
1046
			if (isset($_POST[$config_var][1]) && $_POST[$config_var][0] == $_POST[$config_var][1])
1047
				$new_settings[$config_var] = '\'' . addcslashes($_POST[$config_var][0], '\'\\') . '\'';
1048
		}
1049
		elseif (in_array($config_var, $config_strs))
1050
		{
1051
			$new_settings[$config_var] = '\'' . addcslashes($_POST[$config_var], '\'\\') . '\'';
1052
		}
1053
		elseif (in_array($config_var, $config_ints))
1054
		{
1055
			$new_settings[$config_var] = (int) $_POST[$config_var];
1056
1057
			// 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...
1058
			$min = isset($var['min']) ? $var['min'] : 0;
1059
			$new_settings[$config_var] = max($min, $new_settings[$config_var]);
1060
1061
			// Is there a max value for this as well?
1062
			if (isset($var['max']))
1063
				$new_settings[$config_var] = min($var['max'], $new_settings[$config_var]);
1064
		}
1065
		elseif (in_array($config_var, $config_bools))
1066
		{
1067
			if (!empty($_POST[$config_var]))
1068
				$new_settings[$config_var] = '1';
1069
			else
1070
				$new_settings[$config_var] = '0';
1071
		}
1072
		else
1073
		{
1074
			// This shouldn't happen, but it might...
1075
			fatal_error('Unknown config_var \'' . $config_var . '\'');
1076
		}
1077
	}
1078
1079
	// Save the relevant settings in the Settings.php file.
1080
	require_once($sourcedir . '/Subs-Admin.php');
1081
	updateSettingsFile($new_settings);
1082
1083
	// Now loop through the remaining (database-based) settings.
1084
	$new_settings = array();
1085
	foreach ($config_vars as $config_var)
1086
	{
1087
		// We just saved the file-based settings, so skip their definitions.
1088
		if (!is_array($config_var) || $config_var[2] == 'file')
1089
			continue;
1090
1091
		$new_setting = array($config_var[3], $config_var[0]);
1092
1093
		// Select options need carried over, too.
1094
		if (isset($config_var[4]))
1095
			$new_setting[] = $config_var[4];
1096
1097
		// Include min and max if necessary
1098
		if (isset($config_var['min']))
1099
			$new_setting['min'] = $config_var['min'];
1100
1101
		if (isset($config_var['max']))
1102
			$new_setting['max'] = $config_var['max'];
1103
1104
		// Rewrite the definition a bit.
1105
		$new_settings[] = $new_setting;
1106
	}
1107
1108
	// Save the new database-based settings, if any.
1109
	if (!empty($new_settings))
1110
		saveDBSettings($new_settings);
1111
}
1112
1113
/**
1114
 * Helper function for saving database settings.
1115
 * @todo see rev. 10406 from 2.1-requests
1116
 *
1117
 * @param array $config_vars An array of configuration variables
1118
 */
1119
function saveDBSettings(&$config_vars)
1120
{
1121
	global $sourcedir, $smcFunc;
1122
	static $board_list = null;
1123
1124
	validateToken('admin-dbsc');
1125
1126
	$inlinePermissions = array();
1127
	foreach ($config_vars as $var)
1128
	{
1129
		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']))))
1130
			continue;
1131
1132
		// Checkboxes!
1133
		elseif ($var[0] == 'check')
1134
			$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...
1135
		// Select boxes!
1136
		elseif ($var[0] == 'select' && in_array($_POST[$var[1]], array_keys($var[2])))
1137
			$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...
1138
		elseif ($var[0] == 'select' && !empty($var['multiple']) && array_intersect($_POST[$var[1]], array_keys($var[2])) != array())
1139
		{
1140
			// For security purposes we validate this line by line.
1141
			$lOptions = array();
1142
			foreach ($_POST[$var[1]] as $invar)
1143
				if (in_array($invar, array_keys($var[2])))
1144
					$lOptions[] = $invar;
1145
1146
			$setArray[$var[1]] = json_encode($lOptions);
1147
		}
1148
		// List of boards!
1149
		elseif ($var[0] == 'boards')
1150
		{
1151
			// We just need a simple list of valid boards, nothing more.
1152
			if ($board_list === null)
1153
			{
1154
				$board_list = array();
1155
				$request = $smcFunc['db_query']('', '
1156
					SELECT id_board
1157
					FROM {db_prefix}boards');
1158
				while ($row = $smcFunc['db_fetch_row']($request))
1159
					$board_list[$row[0]] = true;
1160
1161
				$smcFunc['db_free_result']($request);
1162
			}
1163
1164
			$lOptions = array();
1165
1166
			if (!empty($_POST[$var[1]]))
1167
				foreach ($_POST[$var[1]] as $invar => $dummy)
1168
					if (isset($board_list[$invar]))
1169
						$lOptions[] = $invar;
1170
1171
			$setArray[$var[1]] = !empty($lOptions) ? implode(',', $lOptions) : '';
1172
		}
1173
		// Integers!
1174 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...
1175
		{
1176
			$setArray[$var[1]] = (int) $_POST[$var[1]];
1177
1178
			// 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...
1179
			$min = isset($var['min']) ? $var['min'] : 0;
1180
			$setArray[$var[1]] = max($min, $setArray[$var[1]]);
1181
1182
			// Do we have a max value for this as well?
1183
			if (isset($var['max']))
1184
				$setArray[$var[1]] = min($var['max'], $setArray[$var[1]]);
1185
		}
1186
		// Floating point!
1187 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...
1188
		{
1189
			$setArray[$var[1]] = (float) $_POST[$var[1]];
1190
1191
			// 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...
1192
			$min = isset($var['min']) ? $var['min'] : 0;
1193
			$setArray[$var[1]] = max($min, $setArray[$var[1]]);
1194
1195
			// Do we have a max value for this as well?
1196
			if (isset($var['max']))
1197
				$setArray[$var[1]] = min($var['max'], $setArray[$var[1]]);
1198
		}
1199
		// Text!
1200
		elseif (in_array($var[0], array('text', 'large_text', 'color', 'date', 'datetime', 'datetime-local', 'email', 'month', 'time')))
1201
			$setArray[$var[1]] = $_POST[$var[1]];
1202
		// Passwords!
1203
		elseif ($var[0] == 'password')
1204
		{
1205
			if (isset($_POST[$var[1]][1]) && $_POST[$var[1]][0] == $_POST[$var[1]][1])
1206
				$setArray[$var[1]] = $_POST[$var[1]][0];
1207
		}
1208
		// BBC.
1209
		elseif ($var[0] == 'bbc')
1210
		{
1211
1212
			$bbcTags = array();
1213
			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...
1214
				$bbcTags[] = $tag['tag'];
1215
1216
			if (!isset($_POST[$var[1] . '_enabledTags']))
1217
				$_POST[$var[1] . '_enabledTags'] = array();
1218
			elseif (!is_array($_POST[$var[1] . '_enabledTags']))
1219
				$_POST[$var[1] . '_enabledTags'] = array($_POST[$var[1] . '_enabledTags']);
1220
1221
			$setArray[$var[1]] = implode(',', array_diff($bbcTags, $_POST[$var[1] . '_enabledTags']));
1222
		}
1223
		// Permissions?
1224
		elseif ($var[0] == 'permissions')
1225
			$inlinePermissions[] = $var[1];
1226
	}
1227
1228
	if (!empty($setArray))
1229
		updateSettings($setArray);
1230
1231
	// If we have inline permissions we need to save them.
1232
	if (!empty($inlinePermissions) && allowedTo('manage_permissions'))
1233
	{
1234
		require_once($sourcedir . '/ManagePermissions.php');
1235
		save_inline_permissions($inlinePermissions);
1236
	}
1237
}
1238
1239
/**
1240
 * Allows us to see the servers php settings
1241
 *
1242
 * - loads the settings into an array for display in a template
1243
 * - drops cookie values just in case
1244
 */
1245
function ShowPHPinfoSettings()
1246
{
1247
	global $context, $txt;
1248
1249
	$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...
1250
	$category = $txt['phpinfo_settings'];
1251
1252
	// get the data
1253
	ob_start();
1254
	phpinfo();
1255
1256
	// We only want it for its body, pigs that we are
1257
	$info_lines = preg_replace('~^.*<body>(.*)</body>.*$~', '$1', ob_get_contents());
1258
	$info_lines = explode("\n", strip_tags($info_lines, "<tr><td><h2>"));
1259
	ob_end_clean();
1260
1261
	// remove things that could be considered sensitive
1262
	$remove = '_COOKIE|Cookie|_GET|_REQUEST|REQUEST_URI|QUERY_STRING|REQUEST_URL|HTTP_REFERER';
1263
1264
	// put all of it into an array
1265
	foreach ($info_lines as $line)
1266
	{
1267
		if (preg_match('~(' . $remove . ')~', $line))
1268
			continue;
1269
1270
		// new category?
1271
		if (strpos($line, '<h2>') !== false)
1272
			$category = preg_match('~<h2>(.*)</h2>~', $line, $title) ? $category = $title[1] : $category;
1273
1274
		// load it as setting => value or the old setting local master
1275
		if (preg_match('~<tr><td[^>]+>([^<]*)</td><td[^>]+>([^<]*)</td></tr>~', $line, $val))
1276
			$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...
1277
		elseif (preg_match('~<tr><td[^>]+>([^<]*)</td><td[^>]+>([^<]*)</td><td[^>]+>([^<]*)</td></tr>~', $line, $val))
1278
			$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...
1279
	}
1280
1281
	// load it in to context and display it
1282
	$context['pinfo'] = $pinfo;
1283
	$context['page_title'] = $txt['admin_server_settings'];
1284
	$context['sub_template'] = 'php_info';
1285
	return;
1286
}
1287
1288
/**
1289
 * Get the installed Cache API implementations.
1290
 *
1291
 */
1292
function loadCacheAPIs()
1293
{
1294
	global $sourcedir, $txt;
1295
1296
	// Make sure our class is in session.
1297
	require_once($sourcedir . '/Class-CacheAPI.php');
1298
1299
	$apis = array();
1300
	if ($dh = opendir($sourcedir))
1301
	{
1302
		while (($file = readdir($dh)) !== false)
1303
		{
1304
			if (is_file($sourcedir . '/' . $file) && preg_match('~^CacheAPI-([A-Za-z\d_]+)\.php$~', $file, $matches))
1305
			{
1306
				$tryCache = strtolower($matches[1]);
1307
1308
				require_once($sourcedir . '/' . $file);
1309
				$cache_class_name = $tryCache . '_cache';
1310
				$testAPI = new $cache_class_name();
1311
1312
				// No Support?  NEXT!
1313
				if (!$testAPI->isSupported(true))
1314
					continue;
1315
1316
				$apis[$tryCache] = isset($txt[$tryCache . '_cache']) ? $txt[$tryCache . '_cache'] : $tryCache;
1317
			}
1318
		}
1319
	}
1320
	closedir($dh);
1321
1322
	return $apis;
1323
}
1324
1325
?>
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...