Completed
Push — release-2.1 ( 996344...c049e9 )
by Mert
16s queued 10s
created

ManageServer.php ➔ ModifyGeneralSettings()   D

Complexity

Conditions 9
Paths 16

Size

Total Lines 97
Code Lines 48

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 48
nc 16
nop 1
dl 0
loc 97
rs 4.9219
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 2018 Simple Machines and individual contributors
57
 * @license http://www.simplemachines.org/about/smf/license.php BSD
58
 *
59
 * @version 2.1 Beta 4
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;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
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
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string|array|array>|null.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
138
 */
139
function ModifyGeneralSettings($return_config = false)
140
{
141
	global $scripturl, $context, $txt, $modSettings, $boardurl, $sourcedir;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
142
143
	// If no cert, force_ssl must remain 0
144
	require_once($sourcedir . '/Subs.php');
145
	if (!ssl_cert_found($boardurl) && empty($modSettings['force_ssl']))
146
		$disable_force_ssl = true;
147
	else
148
		$disable_force_ssl = false;
149
150
	/* If you're writing a mod, it's a bad idea to add things here....
151
	For each option:
152
		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)
153
	OR	an empty string for a horizontal rule.
154
	OR	a string for a titled section. */
155
	$config_vars = array(
156
		array('mbname', $txt['admin_title'], 'file', 'text', 30),
157
		'',
158
		array('maintenance', $txt['admin_maintain'], 'file', 'check'),
159
		array('mtitle', $txt['maintenance_subject'], 'file', 'text', 36),
160
		array('mmessage', $txt['maintenance_message'], 'file', 'text', 36),
161
		'',
162
		array('webmaster_email', $txt['admin_webmaster_email'], 'file', 'text', 30),
163
		'',
164
		array('enableCompressedOutput', $txt['enableCompressedOutput'], 'db', 'check', null, 'enableCompressedOutput'),
165
		array('disableTemplateEval', $txt['disableTemplateEval'], 'db', 'check', null, 'disableTemplateEval'),
166
		array('disableHostnameLookup', $txt['disableHostnameLookup'], 'db', 'check', null, 'disableHostnameLookup'),
167
		'',
168
		array('force_ssl', $txt['force_ssl'], 'db', 'select', array($txt['force_ssl_off'], $txt['force_ssl_complete']), 'force_ssl', 'disabled' => $disable_force_ssl),
169
		array('image_proxy_enabled', $txt['image_proxy_enabled'], 'file', 'select', array($txt['image_proxy_off'], $txt['image_proxy_internal'], $txt['image_proxy_external']), 'image_proxy_enabled'),
170
		array('image_proxy_secret', $txt['image_proxy_secret'], 'file', 'text', 30, 'image_proxy_secret'),
171
		array('image_proxy_maxsize', $txt['image_proxy_maxsize'], 'file', 'int', null, 'image_proxy_maxsize'),
172
		array('image_proxy_url', $txt['image_proxy_url'], 'file', 'text', 60, 'image_proxy_url'),
173
		'',
174
		array('enable_sm_stats', $txt['sm_state_setting'], 'db', 'check', null, 'enable_sm_stats'),
175
	);
176
177
	call_integration_hook('integrate_general_settings', array(&$config_vars));
178
179
	if ($return_config)
180
		return $config_vars;
181
182
	// Setup the template stuff.
183
	$context['post_url'] = $scripturl . '?action=admin;area=serversettings;sa=general;save';
184
	$context['settings_title'] = $txt['general_settings'];
185
186
	// Saving settings?
187
	if (isset($_REQUEST['save']))
188
	{
189
		call_integration_hook('integrate_save_general_settings');
190
191
		// Are we saving the stat collection?
192
		if (!empty($_POST['enable_sm_stats']) && empty($modSettings['sm_stats_key']))
193
		{
194
			$registerSMStats = registerSMStats();
195
196
			// Failed to register, disable it again.
197
			if (empty($registerSMStats))
198
				$_POST['enable_sm_stats'] = 0;
199
		}
200
201
		// Ensure all URLs are aligned with the new force_ssl setting
202
		// Treat unset like 0
203
		if (isset($_POST['force_ssl']))
204
			AlignURLsWithSSLSetting($_POST['force_ssl']);
205
		else
206
			AlignURLsWithSSLSetting(0);
207
208
		saveSettings($config_vars);
209
		$_SESSION['adm-save'] = true;
210
		redirectexit('action=admin;area=serversettings;sa=general;' . $context['session_var'] . '=' . $context['session_id']);
211
	}
212
213
	// Fill the config array.
214
	prepareServerSettingsContext($config_vars);
215
216
	// Some javascript for SSL
217
	addInlineJavaScript('
218
$(function()
219
{
220
	$("#force_ssl").change(function()
221
	{
222
		var mode = $(this).val() == 1 ? false : true;
223
		$("#image_proxy_enabled").prop("disabled", mode);
224
		$("#image_proxy_enabled").trigger("change");
225
	}).change();
226
	$("#image_proxy_enabled").change(function()
227
	{
228
		var mode = $(this).val() == 1 && $("#force_ssl").val() == 1 ? false : true;
229
		$("#image_proxy_secret").prop("disabled", mode);
230
		$("#image_proxy_maxsize").prop("disabled", mode);
231
		var mode2 = $(this).val() == 2 && $("#force_ssl").val() == 1 ? false : true;
232
		$("#image_proxy_url").prop("disabled", mode2);
233
	}).change();
234
});', true);
235
}
236
237
/**
238
 * Align URLs with SSL Setting.
239
 *
240
 * If force_ssl has changed, ensure all URLs are aligned with the new setting.
241
 * This includes:
242
 *     - $boardurl
243
 *     - $modSettings['smileys_url']
244
 *     - $modSettings['avatar_url']
245
 *     - $modSettings['custom_avatar_url'] - if found
246
 *     - theme_url - all entries in the themes table
247
 *     - images_url - all entries in the themes table
248
 *
249
 * This function will NOT overwrite URLs that are not subfolders of $boardurl.
250
 * The admin must have pointed those somewhere else on purpose, so they must be updated manually.
251
 *
252
 * A word of caution: You can't trust the http/https scheme reflected for these URLs in $globals
253
 * (e.g., $boardurl) or in $modSettings.  This is because SMF may change them in memory to comply
254
 * with the force_ssl setting - a soft redirect may be in effect...  Thus, conditional updates
255
 * to these values do not work.  You gotta just brute force overwrite them based on force_ssl.
256
 *
257
 * @param int $new_force_ssl is the current force_ssl setting.
258
 * @return void Returns nothing, just does its job
259
 */
260
function AlignURLsWithSSLSetting($new_force_ssl = 0)
261
{
262
	global $boardurl, $modSettings, $sourcedir, $db_prefix, $smcFunc;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
263
	require_once($sourcedir . '/Subs-Admin.php');
264
265
	// Check $boardurl
266
	if (!empty($new_force_ssl))
267
		$newval = strtr($boardurl, array('http://' => 'https://'));
268
	else
269
		$newval = strtr($boardurl, array('https://' => 'http://'));
270
	updateSettingsFile(array('boardurl' => '\'' . addslashes($newval) . '\''));
271
272
	$new_settings = array();
273
274
	// Check $smileys_url, but only if it points to a subfolder of $boardurl
275 View Code Duplication
	if (BoardurlMatch($modSettings['smileys_url']))
276
	{
277
		if (!empty($new_force_ssl))
278
			$newval = strtr($modSettings['smileys_url'], array('http://' => 'https://'));
279
		else
280
			$newval = strtr($modSettings['smileys_url'], array('https://' => 'http://'));
281
		$new_settings['smileys_url'] = $newval;
282
	}
283
284
	// Check $avatar_url, but only if it points to a subfolder of $boardurl
285 View Code Duplication
	if (BoardurlMatch($modSettings['avatar_url']))
286
	{
287
		if (!empty($new_force_ssl))
288
			$newval = strtr($modSettings['avatar_url'], array('http://' => 'https://'));
289
		else
290
			$newval = strtr($modSettings['avatar_url'], array('https://' => 'http://'));
291
		$new_settings['avatar_url'] = $newval;
292
	}
293
294
	// Check $custom_avatar_url, but only if it points to a subfolder of $boardurl
295
	// This one had been optional in the past, make sure it is set first
296 View Code Duplication
	if (isset($modSettings['custom_avatar_url']) && BoardurlMatch($modSettings['custom_avatar_url']))
297
	{
298
		if (!empty($new_force_ssl))
299
			$newval = strtr($modSettings['custom_avatar_url'], array('http://' => 'https://'));
300
		else
301
			$newval = strtr($modSettings['custom_avatar_url'], array('https://' => 'http://'));
302
		$new_settings['custom_avatar_url'] = $newval;
303
	}
304
305
	// Save updates to the settings table
306
	if (!empty($new_settings))
307
		updateSettings($new_settings, true);
308
309
	// Now we move onto the themes.
310
	// First, get a list of theme URLs...
311
	$request = $smcFunc['db_query']('', '
312
		SELECT id_theme, variable, value
313
		  FROM {db_prefix}themes
314
		 WHERE variable in ({string:themeurl}, {string:imagesurl})
315
		   AND id_member = {int:zero}',
316
		array(
317
			'themeurl' => 'theme_url',
318
			'imagesurl' => 'images_url',
319
			'zero' => 0,
320
		)
321
	);
322
323
	while ($row = $smcFunc['db_fetch_assoc']($request))
324
	{
325
		// First check to see if it points to a subfolder of $boardurl
326
		if (BoardurlMatch($row['value']))
327
		{
328
			if (!empty($new_force_ssl))
329
				$newval = strtr($row['value'], array('http://' => 'https://'));
330
			else
331
				$newval = strtr($row['value'], array('https://' => 'http://'));
332
			$smcFunc['db_query']('', '
333
				UPDATE {db_prefix}themes
334
				   SET value = {string:theme_val}
335
				 WHERE variable = {string:theme_var}
336
				   AND id_theme = {string:theme_id}
337
				   AND id_member = {int:zero}',
338
				array(
339
					'theme_val' => $newval,
340
					'theme_var' => $row['variable'],
341
					'theme_id' => $row['id_theme'],
342
					'zero' => 0,
343
				)
344
			);
345
		}
346
	}
347
	$smcFunc['db_free_result']($request);
348
}
349
350
/**
351
 * $boardurl Match.
352
 *
353
 * Helper function to see if the url being checked is based off of $boardurl.
354
 * If not, it was overridden by the admin to some other value on purpose, and should not
355
 * be stepped on by SMF when aligning URLs with the force_ssl setting.
356
 * The site admin must change URLs that are not aligned with $boardurl manually.
357
 *
358
 * @param string $url is the url to check.
359
 * @return bool Returns true if the url is based off of $boardurl (without the scheme), false if not
360
 */
361
function BoardurlMatch($url = '')
362
{
363
	global $boardurl;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
364
365
	// Strip the schemes
366
	$urlpath = strtr($url, array('http://' => '', 'https://' => ''));
367
	$boardurlpath = strtr($boardurl, array('http://' => '', 'https://' => ''));
368
369
	// If leftmost portion of path matches boardurl, return true
370
	$result = strpos($urlpath, $boardurlpath);
371
	if ($result === false || $result != 0)
0 ignored issues
show
Coding Style introduced by
The if-else statement can be simplified to return !($result === false || $result != 0);.
Loading history...
372
		return false;
373
	else
374
		return true;
375
}
376
377
/**
378
 * Basic database and paths settings - database name, host, etc.
379
 *
380
 * - It shows an interface for the settings in Settings.php to be changed.
381
 * - It contains the actual array of settings to show from Settings.php.
382
 * - Requires the admin_forum permission.
383
 * - Uses the edit_settings administration area.
384
 * - Accessed from ?action=admin;area=serversettings;sa=database.
385
 *
386
 * @param bool $return_config Whether or not to return the config_vars array (used for admin search)
387
 * @return void|array Returns nothing or returns the $config_vars array if $return_config is true
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<array|string>|null.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
388
 */
389
function ModifyDatabaseSettings($return_config = false)
390
{
391
	global $scripturl, $context, $txt, $smcFunc;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
392
	db_extend('extra');
393
394
	/* If you're writing a mod, it's a bad idea to add things here....
395
		For each option:
396
		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)
397
		OR an empty string for a horizontal rule.
398
		OR a string for a titled section. */
399
	$config_vars = array(
400
		array('db_persist', $txt['db_persist'], 'file', 'check', null, 'db_persist'),
401
		array('db_error_send', $txt['db_error_send'], 'file', 'check'),
402
		array('ssi_db_user', $txt['ssi_db_user'], 'file', 'text', null, 'ssi_db_user'),
403
		array('ssi_db_passwd', $txt['ssi_db_passwd'], 'file', 'password'),
404
		'',
405
		array('autoFixDatabase', $txt['autoFixDatabase'], 'db', 'check', false, 'autoFixDatabase')
406
	);
407
408
	// Add PG Stuff
409
	if ($smcFunc['db_title'] == "PostgreSQL")
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal PostgreSQL does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
410
	{
411
		$request = $smcFunc['db_query']('', 'SELECT cfgname FROM pg_ts_config', array());
412
		$fts_language = array();
413
414
		while ($row = $smcFunc['db_fetch_assoc']($request))
415
			$fts_language[$row['cfgname']] = $row['cfgname'];
416
417
		$config_vars = array_merge ($config_vars, array(
418
				'',
419
				array('search_language', $txt['search_language'], 'db', 'select', $fts_language, 'pgFulltextSearch')
420
			)
421
		);
422
	}
423
424
425
	call_integration_hook('integrate_database_settings', array(&$config_vars));
426
427
	if ($return_config)
428
		return $config_vars;
429
430
	// Setup the template stuff.
431
	$context['post_url'] = $scripturl . '?action=admin;area=serversettings;sa=database;save';
432
	$context['settings_title'] = $txt['database_settings'];
433
	$context['save_disabled'] = $context['settings_not_writable'];
434
435
	if (!$smcFunc['db_allow_persistent'])
436
		addInlineJavaScript('
437
			$(function()
438
			{
439
				$("#db_persist").prop("disabled", true);
440
			});', true);
441
442
	// Saving settings?
443
	if (isset($_REQUEST['save']))
444
	{
445
		call_integration_hook('integrate_save_database_settings');
446
447
		saveSettings($config_vars);
448
		$_SESSION['adm-save'] = true;
449
		redirectexit('action=admin;area=serversettings;sa=database;' . $context['session_var'] . '=' . $context['session_id']);
450
	}
451
452
	// Fill the config array.
453
	prepareServerSettingsContext($config_vars);
454
}
455
456
/**
457
 * This function handles cookies settings modifications.
458
 *
459
 * @param bool $return_config Whether or not to return the config_vars array (used for admin search)
460
 * @return void|array Returns nothing or returns the $config_vars array if $return_config is true
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<array|array|array|array|string|array>|null.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
461
 */
462
function ModifyCookieSettings($return_config = false)
463
{
464
	global $context, $scripturl, $txt, $sourcedir, $modSettings, $cookiename, $user_settings, $boardurl, $smcFunc;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
465
466
	// Define the variables we want to edit.
467
	$config_vars = array(
468
		// Cookies...
469
		array('cookiename', $txt['cookie_name'], 'file', 'text', 20),
470
		array('cookieTime', $txt['cookieTime'], 'db', 'int', 'postinput' => $txt['minutes']),
471
		array('localCookies', $txt['localCookies'], 'db', 'check', false, 'localCookies'),
472
		array('globalCookies', $txt['globalCookies'], 'db', 'check', false, 'globalCookies'),
473
		array('globalCookiesDomain', $txt['globalCookiesDomain'], 'db', 'text', false, 'globalCookiesDomain'),
474
		array('secureCookies', $txt['secureCookies'], 'db', 'check', false, 'secureCookies', 'disabled' => !httpsOn()),
475
		array('httponlyCookies', $txt['httponlyCookies'], 'db', 'check', false, 'httponlyCookies'),
476
		'',
477
		// Sessions
478
		array('databaseSession_enable', $txt['databaseSession_enable'], 'db', 'check', false, 'databaseSession_enable'),
479
		array('databaseSession_loose', $txt['databaseSession_loose'], 'db', 'check', false, 'databaseSession_loose'),
480
		array('databaseSession_lifetime', $txt['databaseSession_lifetime'], 'db', 'int', false, 'databaseSession_lifetime', 'postinput' => $txt['seconds']),
481
		'',
482
		// 2FA
483
		array('tfa_mode', $txt['tfa_mode'], 'db', 'select', array(
484
			0 => $txt['tfa_mode_disabled'],
485
			1 => $txt['tfa_mode_enabled'],
486
		) + (empty($user_settings['tfa_secret']) ? array() : array(
487
			2 => $txt['tfa_mode_forced'],
488
		)) + (empty($user_settings['tfa_secret']) ? array() : array(
489
			3 => $txt['tfa_mode_forcedall'],
490
		)), 'subtext' => $txt['tfa_mode_subtext'] . (empty($user_settings['tfa_secret']) ? '<br><strong>' . $txt['tfa_mode_forced_help'] . '</strong>' : ''), 'tfa_mode'),
491
	);
492
493
	addInlineJavaScript('
494
	function hideGlobalCookies()
495
	{
496
		var usingLocal = $("#localCookies").prop("checked");
497
		$("#setting_globalCookies").closest("dt").toggle(!usingLocal);
498
		$("#globalCookies").closest("dd").toggle(!usingLocal);
499
500
		var usingGlobal = !usingLocal && $("#globalCookies").prop("checked");
501
		$("#setting_globalCookiesDomain").closest("dt").toggle(usingGlobal);
502
		$("#globalCookiesDomain").closest("dd").toggle(usingGlobal);
503
	};
504
	hideGlobalCookies();
505
506
	$("#localCookies, #globalCookies").click(function() {
507
		hideGlobalCookies();
508
	});', true);
509
510
	if (empty($user_settings['tfa_secret']))
511
		addInlineJavaScript('');
512
513
	call_integration_hook('integrate_cookie_settings', array(&$config_vars));
514
515
	if ($return_config)
516
		return $config_vars;
517
518
	$context['post_url'] = $scripturl . '?action=admin;area=serversettings;sa=cookie;save';
519
	$context['settings_title'] = $txt['cookies_sessions_settings'];
520
521
	// Saving settings?
522
	if (isset($_REQUEST['save']))
523
	{
524
		call_integration_hook('integrate_save_cookie_settings');
525
526
		// Local and global do not play nicely together.
527
		if (!empty($_POST['localCookies']) && empty($_POST['globalCookies']))
528
			unset ($_POST['globalCookies']);
529
530
		if (empty($modSettings['localCookies']) != empty($_POST['localCookies']) || empty($modSettings['globalCookies']) != empty($_POST['globalCookies']))
531
			$scope_changed = true;
532
533
		if (!empty($_POST['globalCookiesDomain']) && strpos($boardurl, $_POST['globalCookiesDomain']) === false)
534
			fatal_lang_error('invalid_cookie_domain', false);
535
536
		saveSettings($config_vars);
537
538
		// If the cookie name or scope were changed, reset the cookie.
539
		if ($cookiename != $_POST['cookiename'] || !empty($scope_changed))
540
		{
541
			$original_session_id = $context['session_id'];
542
			include_once($sourcedir . '/Subs-Auth.php');
543
544
			// Remove the old cookie.
545
			setLoginCookie(-3600, 0);
546
547
			// Set the new one.
548
			$cookiename = !empty($_POST['cookiename']) ? $_POST['cookiename'] : $cookiename;
549
			setLoginCookie(60 * $modSettings['cookieTime'], $user_settings['id_member'], hash_salt($user_settings['passwd'], $user_settings['password_salt']));
550
551
			redirectexit('action=admin;area=serversettings;sa=cookie;' . $context['session_var'] . '=' . $original_session_id, $context['server']['needs_login_fix']);
552
		}
553
554
		//If we disabled 2FA, reset all members and membergroups settings.
555
		if (isset($_POST['tfa_mode']) && empty($_POST['tfa_mode']))
556
		{
557
			$smcFunc['db_query']('', '
558
				UPDATE {db_prefix}membergroups
559
				SET tfa_required = {int:zero}',
560
				array(
561
					'zero' => 0,
562
				)
563
			);
564
			$smcFunc['db_query']('', '
565
				UPDATE {db_prefix}members
566
				SET tfa_secret = {string:empty}, tfa_backup = {string:empty}',
567
				array(
568
					'empty' => '',
569
				)
570
			);
571
		}
572
573
		$_SESSION['adm-save'] = true;
574
		redirectexit('action=admin;area=serversettings;sa=cookie;' . $context['session_var'] . '=' . $context['session_id']);
575
	}
576
577
	// Fill the config array.
578
	prepareServerSettingsContext($config_vars);
579
}
580
581
/**
582
 * Settings really associated with general security aspects.
583
 *
584
 * @param bool $return_config Whether or not to return the config_vars array (used for admin search)
585
 * @return void|array Returns nothing or returns the $config_vars array if $return_config is true
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string[]|array|str...ray<string|array>>|null.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
586
 */
587
function ModifyGeneralSecuritySettings($return_config = false)
588
{
589
	global $txt, $scripturl, $context;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
590
591
	$config_vars = array(
592
			array('int', 'failed_login_threshold'),
593
			array('int', 'loginHistoryDays', 'subtext' => $txt['zero_to_disable']),
594
		'',
595
			array('check', 'securityDisable'),
596
			array('check', 'securityDisable_moderate'),
597
		'',
598
			// Reactive on email, and approve on delete
599
			array('check', 'send_validation_onChange'),
600
			array('check', 'approveAccountDeletion'),
601
		'',
602
			// Password strength.
603
			array('select', 'password_strength', array($txt['setting_password_strength_low'], $txt['setting_password_strength_medium'], $txt['setting_password_strength_high'])),
604
			array('check', 'enable_password_conversion'),
605
		'',
606
			// Reporting of personal messages?
607
			array('check', 'enableReportPM'),
608
		'',
609
			array('select', 'frame_security', array('SAMEORIGIN' => $txt['setting_frame_security_SAMEORIGIN'], 'DENY' => $txt['setting_frame_security_DENY'], 'DISABLE' => $txt['setting_frame_security_DISABLE'])),
610
		'',
611
			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')),
612
			array('text', 'proxy_ip_servers'),
613
	);
614
615
	call_integration_hook('integrate_general_security_settings', array(&$config_vars));
616
617
	if ($return_config)
618
		return $config_vars;
619
620
	// Saving?
621 View Code Duplication
	if (isset($_GET['save']))
622
	{
623
		saveDBSettings($config_vars);
624
		$_SESSION['adm-save'] = true;
625
626
		call_integration_hook('integrate_save_general_security_settings');
627
628
		writeLog();
629
		redirectexit('action=admin;area=serversettings;sa=security;' . $context['session_var'] . '=' . $context['session_id']);
630
	}
631
632
	$context['post_url'] = $scripturl . '?action=admin;area=serversettings;save;sa=security';
633
	$context['settings_title'] = $txt['security_settings'];
634
635
	prepareDBSettingContext($config_vars);
636
}
637
638
/**
639
 * Simply modifying cache functions
640
 *
641
 * @param bool $return_config Whether or not to return the config_vars array (used for admin search)
642
 * @return void|array Returns nothing or returns the $config_vars array if $return_config is true
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array[]|null.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
643
 */
644
function ModifyCacheSettings($return_config = false)
645
{
646
	global $context, $scripturl, $txt;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
647
648
	// Detect all available optimizers
649
	$detected = loadCacheAPIs();
650
651
	// set our values to show what, if anything, we found
652
	if (empty($detected))
653
	{
654
		$txt['cache_settings_message'] = $txt['detected_no_caching'];
655
		$cache_level = array($txt['cache_off']);
656
		$detected['none'] = $txt['cache_off'];
657
	}
658
	else
659
	{
660
		$txt['cache_settings_message'] = sprintf($txt['detected_accelerators'], implode(', ', $detected));
661
		$cache_level = array($txt['cache_off'], $txt['cache_level1'], $txt['cache_level2'], $txt['cache_level3']);
662
	}
663
664
	// Define the variables we want to edit.
665
	$config_vars = array(
666
		// Only a few settings, but they are important
667
		array('', $txt['cache_settings_message'], '', 'desc'),
668
		array('cache_enable', $txt['cache_enable'], 'file', 'select', $cache_level, 'cache_enable'),
669
		array('cache_accelerator', $txt['cache_accelerator'], 'file', 'select', $detected),
670
	);
671
672
	// some javascript to enable / disable certain settings if the option is not selected
673
	$context['settings_post_javascript'] = '
674
		$(document).ready(function() {
675
			$("#cache_accelerator").change();
676
		});';
677
678
	call_integration_hook('integrate_modify_cache_settings', array(&$config_vars));
679
680
	// Maybe we have some additional settings from the selected accelerator.
681
	if (!empty($detected))
682
	{
683
		foreach ($detected as $tryCache => $dummy)
684
		{
685
			$cache_class_name = $tryCache . '_cache';
686
687
			// loadCacheAPIs has already included the file, just see if we can't add the settings in.
688
			if (is_callable(array($cache_class_name, 'cacheSettings')))
689
			{
690
				$testAPI = new $cache_class_name();
691
				call_user_func_array(array($testAPI, 'cacheSettings'), array(&$config_vars));
692
			}
693
		}
694
	}
695
	if ($return_config)
696
		return $config_vars;
697
698
	// Saving again?
699 View Code Duplication
	if (isset($_GET['save']))
700
	{
701
		call_integration_hook('integrate_save_cache_settings');
702
703
		saveSettings($config_vars);
704
		$_SESSION['adm-save'] = true;
705
706
		// We need to save the $cache_enable to $modSettings as well
707
		updatesettings(array('cache_enable' => (int) $_POST['cache_enable']));
708
709
		// exit so we reload our new settings on the page
710
		redirectexit('action=admin;area=serversettings;sa=cache;' . $context['session_var'] . '=' . $context['session_id']);
711
	}
712
713
	loadLanguage('ManageMaintenance');
714
	createToken('admin-maint');
715
	$context['template_layers'][] = 'clean_cache_button';
716
717
	$context['post_url'] = $scripturl . '?action=admin;area=serversettings;sa=cache;save';
718
	$context['settings_title'] = $txt['caching_settings'];
719
720
	// Changing cache settings won't have any effect if Settings.php is not writeable.
721
	$context['save_disabled'] = $context['settings_not_writable'];
722
723
	// Decide what message to show.
724
	if (!$context['save_disabled'])
725
		$context['settings_message'] = $txt['caching_information'];
726
727
	// Prepare the template.
728
	prepareServerSettingsContext($config_vars);
729
}
730
731
/**
732
 * Allows to edit load balancing settings.
733
 *
734
 * @param bool $return_config Whether or not to return the config_vars array
735
 * @return void|array Returns nothing or returns the $config_vars array if $return_config is true
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<*,string|boolean>[]|null.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
736
 */
737
function ModifyLoadBalancingSettings($return_config = false)
738
{
739
	global $txt, $scripturl, $context, $modSettings;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
740
741
	// Setup a warning message, but disabled by default.
742
	$disabled = true;
743
	$context['settings_message'] = $txt['loadavg_disabled_conf'];
744
745
	if (stripos(PHP_OS, 'win') === 0)
746
	{
747
		$context['settings_message'] = $txt['loadavg_disabled_windows'];
748
		if (isset($_GET['save']))
749
			$_SESSION['adm-save'] = $txt['loadavg_disabled_windows'];
750
	}
751
	elseif (stripos(PHP_OS, 'darwin') === 0)
752
	{
753
		$context['settings_message'] = $txt['loadavg_disabled_osx'];
754
		if (isset($_GET['save']))
755
			$_SESSION['adm-save'] = $txt['loadavg_disabled_osx'];
756
	}
757
	else
758
	{
759
		$modSettings['load_average'] = @file_get_contents('/proc/loadavg');
760
		if (!empty($modSettings['load_average']) && preg_match('~^([^ ]+?) ([^ ]+?) ([^ ]+)~', $modSettings['load_average'], $matches) !== 0)
761
			$modSettings['load_average'] = (float) $matches[1];
762 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)
763
			$modSettings['load_average'] = (float) $matches[1];
764
		else
765
			unset($modSettings['load_average']);
766
767
		if (!empty($modSettings['load_average']) || $modSettings['load_average'] === 0.0)
768
		{
769
			$context['settings_message'] = sprintf($txt['loadavg_warning'], $modSettings['load_average']);
770
			$disabled = false;
771
		}
772
	}
773
774
	// Start with a simple checkbox.
775
	$config_vars = array(
776
		array('check', 'loadavg_enable', 'disabled' => $disabled),
777
	);
778
779
	// Set the default values for each option.
780
	$default_values = array(
781
		'loadavg_auto_opt' => 1.0,
782
		'loadavg_search' => 2.5,
783
		'loadavg_allunread' => 2.0,
784
		'loadavg_unreadreplies' => 3.5,
785
		'loadavg_show_posts' => 2.0,
786
		'loadavg_userstats' => 10.0,
787
		'loadavg_bbc' => 30.0,
788
		'loadavg_forum' => 40.0,
789
	);
790
791
	// Loop through the settings.
792
	foreach ($default_values as $name => $value)
793
	{
794
		// Use the default value if the setting isn't set yet.
795
		$value = !isset($modSettings[$name]) ? $value : $modSettings[$name];
796
		$config_vars[] = array('float', $name, 'value' => $value, 'disabled' => $disabled);
797
	}
798
799
	call_integration_hook('integrate_loadavg_settings', array(&$config_vars));
800
801
	if ($return_config)
802
		return $config_vars;
803
804
	$context['post_url'] = $scripturl . '?action=admin;area=serversettings;sa=loads;save';
805
	$context['settings_title'] = $txt['load_balancing_settings'];
806
807
	// Saving?
808
	if (isset($_GET['save']))
809
	{
810
		// Stupidity is not allowed.
811
		foreach ($_POST as $key => $value)
812
		{
813
			if (strpos($key, 'loadavg') === 0 || $key === 'loadavg_enable' || !in_array($key, array_keys($default_values)))
814
				continue;
815
			else
816
				$_POST[$key] = (float) $value;
817
818
			if ($key == 'loadavg_auto_opt' && $value <= 1)
819
				$_POST['loadavg_auto_opt'] = 1.0;
820
			elseif ($key == 'loadavg_forum' && $value < 10)
821
				$_POST['loadavg_forum'] = 10.0;
822
			elseif ($value < 2)
823
				$_POST[$key] = 2.0;
824
		}
825
826
		call_integration_hook('integrate_save_loadavg_settings');
827
828
		saveDBSettings($config_vars);
829
		if (!isset($_SESSION['adm-save']))
830
			$_SESSION['adm-save'] = true;
831
		redirectexit('action=admin;area=serversettings;sa=loads;' . $context['session_var'] . '=' . $context['session_id']);
832
	}
833
834
	prepareDBSettingContext($config_vars);
835
}
836
837
/**
838
 * Helper function, it sets up the context for the manage server settings.
839
 * - The basic usage of the six numbered key fields are
840
 * - array (0 ,1, 2, 3, 4, 5
841
 *		0 variable name - the name of the saved variable
842
 *		1 label - the text to show on the settings page
843
 *		2 saveto - file or db, where to save the variable name - value pair
844
 *		3 type - type of data to save, int, float, text, check
845
 *		4 size - false or field size
846
 *		5 help - '' or helptxt variable name
847
 *	)
848
 *
849
 * the following named keys are also permitted
850
 * 'disabled' => A string of code that will determine whether or not the setting should be disabled
851
 * 'postinput' => Text to display after the input field
852
 * 'preinput' => Text to display before the input field
853
 * 'subtext' => Additional descriptive text to display under the field's label
854
 * 'min' => minimum allowed value (for int/float). Defaults to 0 if not set.
855
 * 'max' => maximum allowed value (for int/float)
856
 * 'step' => how much to increment/decrement the value by (only for int/float - mostly used for float values).
857
 *
858
 * @param array $config_vars An array of configuration variables
859
 */
860
function prepareServerSettingsContext(&$config_vars)
861
{
862
	global $context, $modSettings, $smcFunc;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
863
864 View Code Duplication
	if (isset($_SESSION['adm-save']))
865
	{
866
		if ($_SESSION['adm-save'] === true)
867
			$context['saved_successful'] = true;
868
		else
869
			$context['saved_failed'] = $_SESSION['adm-save'];
870
871
		unset($_SESSION['adm-save']);
872
	}
873
874
	$context['config_vars'] = array();
875
	foreach ($config_vars as $identifier => $config_var)
876
	{
877
		if (!is_array($config_var) || !isset($config_var[1]))
878
			$context['config_vars'][] = $config_var;
879
		else
880
		{
881
			$varname = $config_var[0];
882
			global $$varname;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
883
884
			// Set the subtext in case it's part of the label.
885
			// @todo Temporary. Preventing divs inside label tags.
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
886
			$divPos = strpos($config_var[1], '<div');
887
			$subtext = '';
888
			if ($divPos !== false)
889
			{
890
				$subtext = preg_replace('~</?div[^>]*>~', '', substr($config_var[1], $divPos));
891
				$config_var[1] = substr($config_var[1], 0, $divPos);
892
			}
893
894
			$context['config_vars'][$config_var[0]] = array(
895
				'label' => $config_var[1],
896
				'help' => isset($config_var[5]) ? $config_var[5] : '',
897
				'type' => $config_var[3],
898
				'size' => empty($config_var[4]) ? 0 : $config_var[4],
899
				'data' => isset($config_var[4]) && is_array($config_var[4]) && $config_var[3] != 'select' ? $config_var[4] : array(),
900
				'name' => $config_var[0],
901
				'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 : '')),
902
				'disabled' => !empty($context['settings_not_writable']) || !empty($config_var['disabled']),
903
				'invalid' => false,
904
				'subtext' => !empty($config_var['subtext']) ? $config_var['subtext'] : $subtext,
905
				'javascript' => '',
906
				'preinput' => !empty($config_var['preinput']) ? $config_var['preinput'] : '',
907
				'postinput' => !empty($config_var['postinput']) ? $config_var['postinput'] : '',
908
			);
909
910
			// Handle min/max/step if necessary
911 View Code Duplication
			if ($config_var[3] == 'int' || $config_var[3] == 'float')
912
			{
913
				// Default to a min of 0 if one isn't set
914
				if (isset($config_var['min']))
915
					$context['config_vars'][$config_var[0]]['min'] = $config_var['min'];
916
				else
917
					$context['config_vars'][$config_var[0]]['min'] = 0;
918
919
				if (isset($config_var['max']))
920
					$context['config_vars'][$config_var[0]]['max'] = $config_var['max'];
921
922
				if (isset($config_var['step']))
923
					$context['config_vars'][$config_var[0]]['step'] = $config_var['step'];
924
			}
925
926
			// If this is a select box handle any data.
927
			if (!empty($config_var[4]) && is_array($config_var[4]))
928
			{
929
				// If it's associative
930
				$config_values = array_values($config_var[4]);
931 View Code Duplication
				if (isset($config_values[0]) && is_array($config_values[0]))
932
					$context['config_vars'][$config_var[0]]['data'] = $config_var[4];
933
				else
934
				{
935
					foreach ($config_var[4] as $key => $item)
936
						$context['config_vars'][$config_var[0]]['data'][] = array($key, $item);
937
				}
938
			}
939
		}
940
	}
941
942
	// Two tokens because saving these settings requires both saveSettings and saveDBSettings
943
	createToken('admin-ssc');
944
	createToken('admin-dbsc');
945
}
946
947
/**
948
 * Helper function, it sets up the context for database settings.
949
 * @todo see rev. 10406 from 2.1-requests
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
950
 *
951
 * @param array $config_vars An array of configuration variables
952
 */
953
function prepareDBSettingContext(&$config_vars)
954
{
955
	global $txt, $helptxt, $context, $modSettings, $sourcedir, $smcFunc;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
956
957
	loadLanguage('Help');
958
959 View Code Duplication
	if (isset($_SESSION['adm-save']))
960
	{
961
		if ($_SESSION['adm-save'] === true)
962
			$context['saved_successful'] = true;
963
		else
964
			$context['saved_failed'] = $_SESSION['adm-save'];
965
966
		unset($_SESSION['adm-save']);
967
	}
968
969
	$context['config_vars'] = array();
970
	$inlinePermissions = array();
971
	$bbcChoice = array();
972
	$board_list = false;
973
	foreach ($config_vars as $config_var)
974
	{
975
		// HR?
976
		if (!is_array($config_var))
977
			$context['config_vars'][] = $config_var;
978
		else
979
		{
980
			// If it has no name it doesn't have any purpose!
981
			if (empty($config_var[1]))
982
				continue;
983
984
			// Special case for inline permissions
985
			if ($config_var[0] == 'permissions' && allowedTo('manage_permissions'))
986
				$inlinePermissions[] = $config_var[1];
987
			elseif ($config_var[0] == 'permissions')
988
				continue;
989
990
			if ($config_var[0] == 'boards')
991
				$board_list = true;
992
993
			// Are we showing the BBC selection box?
994
			if ($config_var[0] == 'bbc')
995
				$bbcChoice[] = $config_var[1];
996
997
			// We need to do some parsing of the value before we pass it in.
998
			if (isset($modSettings[$config_var[1]]))
999
			{
1000
				switch ($config_var[0])
1001
				{
1002
					case 'select':
1003
						$value = $modSettings[$config_var[1]];
1004
						break;
1005
					case 'json':
1006
						$value = $smcFunc['htmlspecialchars']($smcFunc['json_encode']($modSettings[$config_var[1]]));
1007
						break;
1008
					case 'boards':
1009
						$value = explode(',', $modSettings[$config_var[1]]);
1010
						break;
1011
					default:
1012
						$value = $smcFunc['htmlspecialchars']($modSettings[$config_var[1]]);
1013
				}
1014
			}
1015
			else
1016
			{
1017
				// Darn, it's empty. What type is expected?
1018
				switch ($config_var[0])
1019
				{
1020
					case 'int':
1021
					case 'float':
1022
						$value = 0;
1023
						break;
1024
					case 'select':
1025
						$value = !empty($config_var['multiple']) ? $smcFunc['json_encode'](array()) : '';
1026
						break;
1027
					case 'boards':
1028
						$value = array();
1029
						break;
1030
					default:
1031
						$value = '';
1032
				}
1033
			}
1034
1035
			$context['config_vars'][$config_var[1]] = array(
1036
				'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] : '')),
1037
				'help' => isset($helptxt[$config_var[1]]) ? $config_var[1] : '',
1038
				'type' => $config_var[0],
1039
				'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)),
1040
				'data' => array(),
1041
				'name' => $config_var[1],
1042
				'value' => $value,
1043
				'disabled' => false,
1044
				'invalid' => !empty($config_var['invalid']),
1045
				'javascript' => '',
1046
				'var_message' => !empty($config_var['message']) && isset($txt[$config_var['message']]) ? $txt[$config_var['message']] : '',
1047
				'preinput' => isset($config_var['preinput']) ? $config_var['preinput'] : '',
1048
				'postinput' => isset($config_var['postinput']) ? $config_var['postinput'] : '',
1049
			);
1050
1051
			// Handle min/max/step if necessary
1052 View Code Duplication
			if ($config_var[0] == 'int' || $config_var[0] == 'float')
1053
			{
1054
				// Default to a min of 0 if one isn't set
1055
				if (isset($config_var['min']))
1056
					$context['config_vars'][$config_var[1]]['min'] = $config_var['min'];
1057
				else
1058
					$context['config_vars'][$config_var[1]]['min'] = 0;
1059
1060
				if (isset($config_var['max']))
1061
					$context['config_vars'][$config_var[1]]['max'] = $config_var['max'];
1062
1063
				if (isset($config_var['step']))
1064
					$context['config_vars'][$config_var[1]]['step'] = $config_var['step'];
1065
			}
1066
1067
			// If this is a select box handle any data.
1068
			if (!empty($config_var[2]) && is_array($config_var[2]))
1069
			{
1070
				// If we allow multiple selections, we need to adjust a few things.
1071
				if ($config_var[0] == 'select' && !empty($config_var['multiple']))
1072
				{
1073
					$context['config_vars'][$config_var[1]]['name'] .= '[]';
1074
					$context['config_vars'][$config_var[1]]['value'] = !empty($context['config_vars'][$config_var[1]]['value']) ? $smcFunc['json_decode']($context['config_vars'][$config_var[1]]['value'], true) : array();
1075
				}
1076
1077
				// If it's associative
1078 View Code Duplication
				if (isset($config_var[2][0]) && is_array($config_var[2][0]))
1079
					$context['config_vars'][$config_var[1]]['data'] = $config_var[2];
1080
				else
1081
				{
1082
					foreach ($config_var[2] as $key => $item)
1083
						$context['config_vars'][$config_var[1]]['data'][] = array($key, $item);
1084
				}
1085
			}
1086
1087
			// Finally allow overrides - and some final cleanups.
1088
			foreach ($config_var as $k => $v)
1089
			{
1090
				if (!is_numeric($k))
1091
				{
1092
					if (substr($k, 0, 2) == 'on')
1093
						$context['config_vars'][$config_var[1]]['javascript'] .= ' ' . $k . '="' . $v . '"';
1094
					else
1095
						$context['config_vars'][$config_var[1]][$k] = $v;
1096
				}
1097
1098
				// See if there are any other labels that might fit?
1099
				if (isset($txt['setting_' . $config_var[1]]))
1100
					$context['config_vars'][$config_var[1]]['label'] = $txt['setting_' . $config_var[1]];
1101
				elseif (isset($txt['groups_' . $config_var[1]]))
1102
					$context['config_vars'][$config_var[1]]['label'] = $txt['groups_' . $config_var[1]];
1103
			}
1104
1105
			// Set the subtext in case it's part of the label.
1106
			// @todo Temporary. Preventing divs inside label tags.
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
1107
			$divPos = strpos($context['config_vars'][$config_var[1]]['label'], '<div');
1108
			if ($divPos !== false)
1109
			{
1110
				$context['config_vars'][$config_var[1]]['subtext'] = preg_replace('~</?div[^>]*>~', '', substr($context['config_vars'][$config_var[1]]['label'], $divPos));
1111
				$context['config_vars'][$config_var[1]]['label'] = substr($context['config_vars'][$config_var[1]]['label'], 0, $divPos);
1112
			}
1113
		}
1114
	}
1115
1116
	// If we have inline permissions we need to prep them.
1117
	if (!empty($inlinePermissions) && allowedTo('manage_permissions'))
1118
	{
1119
		require_once($sourcedir . '/ManagePermissions.php');
1120
		init_inline_permissions($inlinePermissions, isset($context['permissions_excluded']) ? $context['permissions_excluded'] : array());
1121
	}
1122
1123
	if ($board_list)
1124
	{
1125
		require_once($sourcedir . '/Subs-MessageIndex.php');
1126
		$context['board_list'] = getBoardList();
1127
	}
1128
1129
	// What about any BBC selection boxes?
1130
	if (!empty($bbcChoice))
1131
	{
1132
		// What are the options, eh?
1133
		$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...
1134
		$bbcTags = array();
1135
		foreach ($temp as $tag)
0 ignored issues
show
Bug introduced by
The expression $temp of type string is not traversable.
Loading history...
1136
			$bbcTags[] = $tag['tag'];
1137
1138
		$bbcTags = array_unique($bbcTags);
1139
		$totalTags = count($bbcTags);
1140
1141
		// The number of columns we want to show the BBC tags in.
1142
		$numColumns = isset($context['num_bbc_columns']) ? $context['num_bbc_columns'] : 3;
1143
1144
		// Start working out the context stuff.
1145
		$context['bbc_columns'] = array();
1146
		$tagsPerColumn = ceil($totalTags / $numColumns);
1147
1148
		$col = 0; $i = 0;
0 ignored issues
show
Coding Style introduced by
It is generally recommended to place each PHP statement on a line by itself.

Let’s take a look at an example:

// Bad
$a = 5; $b = 6; $c = 7;

// Good
$a = 5;
$b = 6;
$c = 7;
Loading history...
1149
		foreach ($bbcTags as $tag)
1150
		{
1151
			if ($i % $tagsPerColumn == 0 && $i != 0)
1152
				$col++;
1153
1154
			$context['bbc_columns'][$col][] = array(
1155
				'tag' => $tag,
1156
				// @todo  'tag_' . ?
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
1157
				'show_help' => isset($helptxt[$tag]),
1158
			);
1159
1160
			$i++;
1161
		}
1162
1163
		// Now put whatever BBC options we may have into context too!
1164
		$context['bbc_sections'] = array();
1165
		foreach ($bbcChoice as $bbc)
1166
		{
1167
			$context['bbc_sections'][$bbc] = array(
1168
				'title' => isset($txt['bbc_title_' . $bbc]) ? $txt['bbc_title_' . $bbc] : $txt['bbcTagsToUse_select'],
1169
				'disabled' => empty($modSettings['bbc_disabled_' . $bbc]) ? array() : $modSettings['bbc_disabled_' . $bbc],
1170
				'all_selected' => empty($modSettings['bbc_disabled_' . $bbc]),
1171
			);
1172
		}
1173
	}
1174
1175
	call_integration_hook('integrate_prepare_db_settings', array(&$config_vars));
1176
	createToken('admin-dbsc');
1177
}
1178
1179
/**
1180
 * Helper function. Saves settings by putting them in Settings.php or saving them in the settings table.
1181
 *
1182
 * - Saves those settings set from ?action=admin;area=serversettings.
1183
 * - Requires the admin_forum permission.
1184
 * - Contains arrays of the types of data to save into Settings.php.
1185
 *
1186
 * @param array $config_vars An array of configuration variables
1187
 */
1188
function saveSettings(&$config_vars)
1189
{
1190
	global $sourcedir, $context;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1191
1192
	validateToken('admin-ssc');
1193
1194
	// Fix the darn stupid cookiename! (more may not be allowed, but these for sure!)
1195
	if (isset($_POST['cookiename']))
1196
		$_POST['cookiename'] = preg_replace('~[,;\s\.$]+~' . ($context['utf8'] ? 'u' : ''), '', $_POST['cookiename']);
1197
1198
	// Fix the forum's URL if necessary.
1199
	if (isset($_POST['boardurl']))
1200
	{
1201 View Code Duplication
		if (substr($_POST['boardurl'], -10) == '/index.php')
1202
			$_POST['boardurl'] = substr($_POST['boardurl'], 0, -10);
1203
		elseif (substr($_POST['boardurl'], -1) == '/')
1204
			$_POST['boardurl'] = substr($_POST['boardurl'], 0, -1);
1205 View Code Duplication
		if (substr($_POST['boardurl'], 0, 7) != 'http://' && substr($_POST['boardurl'], 0, 7) != 'file://' && substr($_POST['boardurl'], 0, 8) != 'https://')
1206
			$_POST['boardurl'] = 'http://' . $_POST['boardurl'];
1207
	}
1208
1209
	// Any passwords?
1210
	$config_passwords = array(
1211
		'db_passwd',
1212
		'ssi_db_passwd',
1213
	);
1214
1215
	// All the strings to write.
1216
	$config_strs = array(
1217
		'mtitle', 'mmessage',
1218
		'language', 'mbname', 'boardurl',
1219
		'cookiename',
1220
		'webmaster_email',
1221
		'db_name', 'db_user', 'db_server', 'db_prefix', 'ssi_db_user',
1222
		'boarddir', 'sourcedir',
1223
		'cachedir', 'cachedir_sqlite', 'cache_accelerator', 'cache_memcached',
1224
		'image_proxy_secret',
1225
		'image_proxy_url',
1226
	);
1227
1228
	// All the numeric variables.
1229
	$config_ints = array(
1230
		'cache_enable',
1231
		'image_proxy_maxsize',
1232
		'image_proxy_enabled',
1233
	);
1234
1235
	// All the checkboxes
1236
	$config_bools = array('db_persist', 'db_error_send', 'maintenance');
1237
1238
	// Now sort everything into a big array, and figure out arrays and etc.
1239
	$new_settings = array();
1240
	// Figure out which config vars we're saving here...
1241
	foreach ($config_vars as $var)
1242
	{
1243
		if (!is_array($var) || $var[2] != 'file' || (!in_array($var[0], $config_bools) && !isset($_POST[$var[0]])))
1244
			continue;
1245
1246
		$config_var = $var[0];
1247
1248
		if (in_array($config_var, $config_passwords))
1249
		{
1250
			if (isset($_POST[$config_var][1]) && $_POST[$config_var][0] == $_POST[$config_var][1])
1251
				$new_settings[$config_var] = '\'' . addcslashes($_POST[$config_var][0], '\'\\') . '\'';
1252
		}
1253
		elseif (in_array($config_var, $config_strs))
1254
		{
1255
			$new_settings[$config_var] = '\'' . addcslashes($_POST[$config_var], '\'\\') . '\'';
1256
		}
1257
		elseif (in_array($config_var, $config_ints))
1258
		{
1259
			$new_settings[$config_var] = (int) $_POST[$config_var];
1260
1261
			// 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...
1262
			$min = isset($var['min']) ? $var['min'] : 0;
1263
			$new_settings[$config_var] = max($min, $new_settings[$config_var]);
1264
1265
			// Is there a max value for this as well?
1266
			if (isset($var['max']))
1267
				$new_settings[$config_var] = min($var['max'], $new_settings[$config_var]);
1268
		}
1269
		elseif (in_array($config_var, $config_bools))
1270
		{
1271
			if (!empty($_POST[$config_var]))
1272
				$new_settings[$config_var] = '1';
1273
			else
1274
				$new_settings[$config_var] = '0';
1275
		}
1276
		else
1277
		{
1278
			// This shouldn't happen, but it might...
1279
			fatal_error('Unknown config_var \'' . $config_var . '\'');
1280
		}
1281
	}
1282
1283
	// Save the relevant settings in the Settings.php file.
1284
	require_once($sourcedir . '/Subs-Admin.php');
1285
	updateSettingsFile($new_settings);
1286
1287
	// Now loop through the remaining (database-based) settings.
1288
	$new_settings = array();
1289
	foreach ($config_vars as $config_var)
1290
	{
1291
		// We just saved the file-based settings, so skip their definitions.
1292
		if (!is_array($config_var) || $config_var[2] == 'file')
1293
			continue;
1294
1295
		$new_setting = array($config_var[3], $config_var[0]);
1296
1297
		// Select options need carried over, too.
1298
		if (isset($config_var[4]))
1299
			$new_setting[] = $config_var[4];
1300
1301
		// Include min and max if necessary
1302
		if (isset($config_var['min']))
1303
			$new_setting['min'] = $config_var['min'];
1304
1305
		if (isset($config_var['max']))
1306
			$new_setting['max'] = $config_var['max'];
1307
1308
		// Rewrite the definition a bit.
1309
		$new_settings[] = $new_setting;
1310
	}
1311
1312
	// Save the new database-based settings, if any.
1313
	if (!empty($new_settings))
1314
		saveDBSettings($new_settings);
1315
}
1316
1317
/**
1318
 * Helper function for saving database settings.
1319
 * @todo see rev. 10406 from 2.1-requests
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
1320
 *
1321
 * @param array $config_vars An array of configuration variables
1322
 */
1323
function saveDBSettings(&$config_vars)
1324
{
1325
	global $sourcedir, $smcFunc;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1326
	static $board_list = null;
1327
1328
	validateToken('admin-dbsc');
1329
1330
	$inlinePermissions = array();
1331
	foreach ($config_vars as $var)
1332
	{
1333
		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']))))
1334
			continue;
1335
1336
		// Checkboxes!
1337
		elseif ($var[0] == 'check')
1338
			$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...
1339
		// Select boxes!
1340
		elseif ($var[0] == 'select' && in_array($_POST[$var[1]], array_keys($var[2])))
1341
			$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...
1342
		elseif ($var[0] == 'select' && !empty($var['multiple']) && array_intersect($_POST[$var[1]], array_keys($var[2])) != array())
1343
		{
1344
			// For security purposes we validate this line by line.
1345
			$lOptions = array();
1346
			foreach ($_POST[$var[1]] as $invar)
1347
				if (in_array($invar, array_keys($var[2])))
1348
					$lOptions[] = $invar;
1349
1350
			$setArray[$var[1]] = $smcFunc['json_encode']($lOptions);
1351
		}
1352
		// List of boards!
1353
		elseif ($var[0] == 'boards')
1354
		{
1355
			// We just need a simple list of valid boards, nothing more.
1356
			if ($board_list === null)
1357
			{
1358
				$board_list = array();
1359
				$request = $smcFunc['db_query']('', '
1360
					SELECT id_board
1361
					FROM {db_prefix}boards');
1362
				while ($row = $smcFunc['db_fetch_row']($request))
1363
					$board_list[$row[0]] = true;
1364
1365
				$smcFunc['db_free_result']($request);
1366
			}
1367
1368
			$lOptions = array();
1369
1370
			if (!empty($_POST[$var[1]]))
1371
				foreach ($_POST[$var[1]] as $invar => $dummy)
1372
					if (isset($board_list[$invar]))
1373
						$lOptions[] = $invar;
1374
1375
			$setArray[$var[1]] = !empty($lOptions) ? implode(',', $lOptions) : '';
1376
		}
1377
		// Integers!
1378 View Code Duplication
		elseif ($var[0] == 'int')
1379
		{
1380
			$setArray[$var[1]] = (int) $_POST[$var[1]];
1381
1382
			// 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...
1383
			$min = isset($var['min']) ? $var['min'] : 0;
1384
			$setArray[$var[1]] = max($min, $setArray[$var[1]]);
1385
1386
			// Do we have a max value for this as well?
1387
			if (isset($var['max']))
1388
				$setArray[$var[1]] = min($var['max'], $setArray[$var[1]]);
1389
		}
1390
		// Floating point!
1391 View Code Duplication
		elseif ($var[0] == 'float')
1392
		{
1393
			$setArray[$var[1]] = (float) $_POST[$var[1]];
1394
1395
			// 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...
1396
			$min = isset($var['min']) ? $var['min'] : 0;
1397
			$setArray[$var[1]] = max($min, $setArray[$var[1]]);
1398
1399
			// Do we have a max value for this as well?
1400
			if (isset($var['max']))
1401
				$setArray[$var[1]] = min($var['max'], $setArray[$var[1]]);
1402
		}
1403
		// Text!
1404
		elseif (in_array($var[0], array('text', 'large_text', 'color', 'date', 'datetime', 'datetime-local', 'email', 'month', 'time')))
1405
			$setArray[$var[1]] = $_POST[$var[1]];
1406
		// Passwords!
1407
		elseif ($var[0] == 'password')
1408
		{
1409
			if (isset($_POST[$var[1]][1]) && $_POST[$var[1]][0] == $_POST[$var[1]][1])
1410
				$setArray[$var[1]] = $_POST[$var[1]][0];
1411
		}
1412
		// BBC.
1413
		elseif ($var[0] == 'bbc')
1414
		{
1415
1416
			$bbcTags = array();
1417
			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...
1418
				$bbcTags[] = $tag['tag'];
1419
1420
			if (!isset($_POST[$var[1] . '_enabledTags']))
1421
				$_POST[$var[1] . '_enabledTags'] = array();
1422
			elseif (!is_array($_POST[$var[1] . '_enabledTags']))
1423
				$_POST[$var[1] . '_enabledTags'] = array($_POST[$var[1] . '_enabledTags']);
1424
1425
			$setArray[$var[1]] = implode(',', array_diff($bbcTags, $_POST[$var[1] . '_enabledTags']));
1426
		}
1427
		// Permissions?
1428
		elseif ($var[0] == 'permissions')
1429
			$inlinePermissions[] = $var[1];
1430
	}
1431
1432
	if (!empty($setArray))
1433
		updateSettings($setArray);
1434
1435
	// If we have inline permissions we need to save them.
1436
	if (!empty($inlinePermissions) && allowedTo('manage_permissions'))
1437
	{
1438
		require_once($sourcedir . '/ManagePermissions.php');
1439
		save_inline_permissions($inlinePermissions);
1440
	}
1441
}
1442
1443
/**
1444
 * Allows us to see the servers php settings
1445
 *
1446
 * - loads the settings into an array for display in a template
1447
 * - drops cookie values just in case
1448
 */
1449
function ShowPHPinfoSettings()
1450
{
1451
	global $context, $txt;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1452
1453
	$category = $txt['phpinfo_settings'];
1454
1455
	// get the data
1456
	ob_start();
1457
	phpinfo();
1458
1459
	// We only want it for its body, pigs that we are
1460
	$info_lines = preg_replace('~^.*<body>(.*)</body>.*$~', '$1', ob_get_contents());
1461
	$info_lines = explode("\n", strip_tags($info_lines, "<tr><td><h2>"));
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal <tr><td><h2> does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
1462
	ob_end_clean();
1463
1464
	// remove things that could be considered sensitive
1465
	$remove = '_COOKIE|Cookie|_GET|_REQUEST|REQUEST_URI|QUERY_STRING|REQUEST_URL|HTTP_REFERER';
1466
1467
	// put all of it into an array
1468
	foreach ($info_lines as $line)
1469
	{
1470
		if (preg_match('~(' . $remove . ')~', $line))
1471
			continue;
1472
1473
		// new category?
1474
		if (strpos($line, '<h2>') !== false)
1475
			$category = preg_match('~<h2>(.*)</h2>~', $line, $title) ? $category = $title[1] : $category;
1476
1477
		// load it as setting => value or the old setting local master
1478
		if (preg_match('~<tr><td[^>]+>([^<]*)</td><td[^>]+>([^<]*)</td></tr>~', $line, $val))
1479
			$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...
1480
		elseif (preg_match('~<tr><td[^>]+>([^<]*)</td><td[^>]+>([^<]*)</td><td[^>]+>([^<]*)</td></tr>~', $line, $val))
1481
			$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...
1482
	}
1483
1484
	// load it in to context and display it
1485
	$context['pinfo'] = $pinfo;
1486
	$context['page_title'] = $txt['admin_server_settings'];
1487
	$context['sub_template'] = 'php_info';
1488
	return;
1489
}
1490
1491
/**
1492
 * Get the installed Cache API implementations.
1493
 *
1494
 */
1495
function loadCacheAPIs()
1496
{
1497
	global $sourcedir, $txt;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1498
1499
	// Make sure our class is in session.
1500
	require_once($sourcedir . '/Class-CacheAPI.php');
1501
1502
	$apis = array();
1503
	if ($dh = opendir($sourcedir))
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $dh. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
1504
	{
1505
		while (($file = readdir($dh)) !== false)
1506
		{
1507
			if (is_file($sourcedir . '/' . $file) && preg_match('~^CacheAPI-([A-Za-z\d_]+)\.php$~', $file, $matches))
1508
			{
1509
				$tryCache = strtolower($matches[1]);
1510
1511
				require_once($sourcedir . '/' . $file);
1512
				$cache_class_name = $tryCache . '_cache';
1513
				$testAPI = new $cache_class_name();
1514
1515
				// No Support?  NEXT!
1516
				if (!$testAPI->isSupported(true))
1517
					continue;
1518
1519
				$apis[$tryCache] = isset($txt[$tryCache . '_cache']) ? $txt[$tryCache . '_cache'] : $tryCache;
1520
			}
1521
		}
1522
	}
1523
	closedir($dh);
1524
1525
	return $apis;
1526
}
1527
1528
/**
1529
 * Registers the site with the Simple Machines Stat collection. This function
1530
 * purposely does not use updateSettings.php as it will be called shortly after
1531
 * this process completes by the saveSettings() function.
1532
 *
1533
 * @see Stats.php SMStats() for more information.
1534
 * @link https://www.simplemachines.org/about/stats.php for more info.
1535
 *
1536
 */
1537
function registerSMStats()
1538
{
1539
	global $modSettings, $boardurl, $smcFunc;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1540
1541
	// Already have a key?  Can't register again.
1542
	if (!empty($modSettings['sm_stats_key']))
1543
		return true;
1544
1545
	$fp = @fsockopen('www.simplemachines.org', 80, $errno, $errstr);
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $fp. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
1546
	if ($fp)
1547
	{
1548
		$out = 'GET /smf/stats/register_stats.php?site=' . base64_encode($boardurl) . ' HTTP/1.1' . "\r\n";
1549
		$out .= 'Host: www.simplemachines.org' . "\r\n";
1550
		$out .= 'Connection: Close' . "\r\n\r\n";
1551
		fwrite($fp, $out);
1552
1553
		$return_data = '';
1554
		while (!feof($fp))
1555
			$return_data .= fgets($fp, 128);
1556
1557
		fclose($fp);
1558
1559
		// Get the unique site ID.
1560
		preg_match('~SITE-ID:\s(\w{10})~', $return_data, $ID);
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $ID. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
1561
1562
		if (!empty($ID[1]))
1563
		{
1564
			$smcFunc['db_insert']('replace',
1565
				'{db_prefix}settings',
1566
				array('variable' => 'string', 'value' => 'string'),
1567
				array('sm_stats_key', $ID[1]),
1568
				array('variable')
1569
			);
1570
			return true;
1571
		}
1572
	}
1573
1574
	return false;
1575
}
1576
1577
?>