Completed
Pull Request — release-2.1 (#4286)
by Mert
09:44
created

ManageServer.php ➔ ModifyGeneralSettings()   C

Complexity

Conditions 9
Paths 16

Size

Total Lines 89
Code Lines 46

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 46
nc 16
nop 1
dl 0
loc 89
rs 5.2228
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * Contains all the functionality required to be able to edit the core server
5
 * settings. This includes anything from which an error may result in the forum
6
 * destroying itself in a firey fury.
7
 *
8
 * Adding options to one of the setting screens isn't hard. Call prepareDBSettingsContext;
9
 * The basic format for a checkbox is:
10
 * 		array('check', 'nameInModSettingsAndSQL'),
11
 * And for a text box:
12
 * 		array('text', 'nameInModSettingsAndSQL')
13
 * (NOTE: You have to add an entry for this at the bottom!)
14
 *
15
 * In these cases, it will look for $txt['nameInModSettingsAndSQL'] as the description,
16
 * and $helptxt['nameInModSettingsAndSQL'] as the help popup description.
17
 *
18
 * Here's a quick explanation of how to add a new item:
19
 *
20
 * - A text input box.  For textual values.
21
 * 		array('text', 'nameInModSettingsAndSQL', 'OptionalInputBoxWidth'),
22
 * - A text input box.  For numerical values.
23
 * 		array('int', 'nameInModSettingsAndSQL', 'OptionalInputBoxWidth'),
24
 * - A text input box.  For floating point values.
25
 * 		array('float', 'nameInModSettingsAndSQL', 'OptionalInputBoxWidth'),
26
 * - A large text input box. Used for textual values spanning multiple lines.
27
 * 		array('large_text', 'nameInModSettingsAndSQL', 'OptionalNumberOfRows'),
28
 * - A check box.  Either one or zero. (boolean)
29
 * 		array('check', 'nameInModSettingsAndSQL'),
30
 * - A selection box.  Used for the selection of something from a list.
31
 * 		array('select', 'nameInModSettingsAndSQL', array('valueForSQL' => $txt['displayedValue'])),
32
 * 		Note that just saying array('first', 'second') will put 0 in the SQL for 'first'.
33
 * - A password input box. Used for passwords, no less!
34
 * 		array('password', 'nameInModSettingsAndSQL', 'OptionalInputBoxWidth'),
35
 * - A permission - for picking groups who have a permission.
36
 * 		array('permissions', 'manage_groups'),
37
 * - A BBC selection box.
38
 * 		array('bbc', 'sig_bbc'),
39
 * - A list of boards to choose from
40
 *  	array('boards', 'likes_boards'),
41
 *  	Note that the storage in the database is as 1,2,3,4
42
 *
43
 * For each option:
44
 * 	- type (see above), variable name, size/possible values.
45
 * 	  OR make type '' for an empty string for a horizontal rule.
46
 *  - SET preinput - to put some HTML prior to the input box.
47
 *  - SET postinput - to put some HTML following the input box.
48
 *  - SET invalid - to mark the data as invalid.
49
 *  - PLUS you can override label and help parameters by forcing their keys in the array, for example:
50
 *  	array('text', 'invalidlabel', 3, 'label' => 'Actual Label')
51
 *
52
 * Simple Machines Forum (SMF)
53
 *
54
 * @package SMF
55
 * @author Simple Machines http://www.simplemachines.org
56
 * @copyright 2017 Simple Machines and individual contributors
57
 * @license http://www.simplemachines.org/about/smf/license.php BSD
58
 *
59
 * @version 2.1 Beta 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_auth'], $txt['force_ssl_complete']), 'force_ssl', 'disabled' => $disable_force_ssl),
169
		array('image_proxy_enabled', $txt['image_proxy_enabled'], 'file', 'check', null, '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
		'',
173
		array('enable_sm_stats', $txt['sm_state_setting'], 'db', 'check', null, 'enable_sm_stats'),
174
	);
175
176
	call_integration_hook('integrate_general_settings', array(&$config_vars));
177
178
	if ($return_config)
179
		return $config_vars;
180
181
	// Setup the template stuff.
182
	$context['post_url'] = $scripturl . '?action=admin;area=serversettings;sa=general;save';
183
	$context['settings_title'] = $txt['general_settings'];
184
185
	// Saving settings?
186
	if (isset($_REQUEST['save']))
187
	{
188
		call_integration_hook('integrate_save_general_settings');
189
190
		// Are we saving the stat collection?
191
		if (!empty($_POST['enable_sm_stats']) && empty($modSettings['sm_stats_key']))
192
		{
193
			$registerSMStats = registerSMStats();
194
195
			// Failed to register, disable it again.
196
			if (empty($registerSMStats))
197
				$_POST['enable_sm_stats'] = 0;
198
		}
199
200
		// Ensure all URLs are aligned with the new force_ssl setting
201
		// Treat unset like 0
202
		if (isset($_POST['force_ssl']))
203
			AlignURLsWithSSLSetting($_POST['force_ssl']);
204
		else
205
			AlignURLsWithSSLSetting(0);
206
			
207
		saveSettings($config_vars);
208
		$_SESSION['adm-save'] = true;
209
		redirectexit('action=admin;area=serversettings;sa=general;' . $context['session_var'] . '=' . $context['session_id']);
210
	}
211
212
	// Fill the config array.
213
	prepareServerSettingsContext($config_vars);
214
215
	// Some javascript for SSL
216
	addInlineJavaScript('
217
$(function()
218
{
219
	$("#force_ssl").change(function()
220
	{
221
		var mode = $(this).val() == 2 ? false : true;
222
		$("#image_proxy_enabled").prop("disabled", mode);
223
		$("#image_proxy_secret").prop("disabled", mode);
224
		$("#image_proxy_maxsize").prop("disabled", mode);
225
	}).change();
226
});');
227
}
228
229
/**
230
 * Align URLs with SSL Setting.
231
 *
232
 * If force_ssl has changed, ensure all URLs are aligned with the new setting.
233
 * This includes:
234
 *     - $boardurl
235
 *     - $modSettings['smileys_url']
236
 *     - $modSettings['avatar_url']
237
 *     - $modSettings['custom_avatar_url'] - if found
238
 *     - theme_url - all entries in the themes table
239
 *     - images_url - all entries in the themes table
240
 *
241
 * This function will NOT overwrite URLs that are not subfolders of $boardurl.
242
 * The admin must have pointed those somewhere else on purpose, so they must be updated manually.
243
 * 
244
 * A word of caution: You can't trust the http/https scheme reflected for these URLs in $globals
245
 * (e.g., $boardurl) or in $modSettings.  This is because SMF may change them in memory to comply
246
 * with the force_ssl setting - a soft redirect may be in effect...  Thus, conditional updates
247
 * to these values do not work.  You gotta just brute force overwrite them based on force_ssl.
248
 *
249
 * @param int $new_force_ssl is the current force_ssl setting.
250
 * @return void Returns nothing, just does its job
251
 */
252
function AlignURLsWithSSLSetting($new_force_ssl = 0)
253
{
254
	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...
255
	require_once($sourcedir . '/Subs-Admin.php');
256
257
	// Check $boardurl
258
	if ($new_force_ssl == 2)
259
		$newval = strtr($boardurl, array('http://' => 'https://'));
260
	else
261
		$newval = strtr($boardurl, array('https://' => 'http://'));
262
	updateSettingsFile(array('boardurl' => '\'' . addslashes($newval) . '\''));
263
264
	$new_settings = array();
265
266
	// Check $smileys_url, but only if it points to a subfolder of $boardurl
267 View Code Duplication
	if (BoardurlMatch($modSettings['smileys_url']))
268
	{
269
		if ($new_force_ssl == 2)
270
			$newval = strtr($modSettings['smileys_url'], array('http://' => 'https://'));
271
		else
272
			$newval = strtr($modSettings['smileys_url'], array('https://' => 'http://'));
273
		$new_settings['smileys_url'] = $newval;
274
	}
275
276
	// Check $avatar_url, but only if it points to a subfolder of $boardurl
277 View Code Duplication
	if (BoardurlMatch($modSettings['avatar_url']))
278
	{
279
		if ($new_force_ssl == 2)
280
			$newval = strtr($modSettings['avatar_url'], array('http://' => 'https://'));
281
		else
282
			$newval = strtr($modSettings['avatar_url'], array('https://' => 'http://'));
283
		$new_settings['avatar_url'] = $newval;
284
	}
285
286
	// Check $custom_avatar_url, but only if it points to a subfolder of $boardurl
287
	// This one had been optional in the past, make sure it is set first
288 View Code Duplication
	if (isset($modSettings['custom_avatar_url']) && BoardurlMatch($modSettings['custom_avatar_url']))
289
	{
290
		if ($new_force_ssl == 2)
291
			$newval = strtr($modSettings['custom_avatar_url'], array('http://' => 'https://'));
292
		else
293
			$newval = strtr($modSettings['custom_avatar_url'], array('https://' => 'http://'));
294
		$new_settings['custom_avatar_url'] = $newval;
295
	}
296
297
	// Save updates to the settings table
298
	if (!empty($new_settings))
299
		updateSettings($new_settings, true);
300
301
	// Now we move onto the themes.
302
	// First, get a list of theme URLs...
303
	$request = $smcFunc['db_query']('', '
304
		SELECT id_theme, variable, value
305
		  FROM {db_prefix}themes
306
		 WHERE variable in ({string:themeurl}, {string:imagesurl})
307
		   AND id_member = {int:zero}',
308
		array(
309
			'themeurl' => 'theme_url',
310
			'imagesurl' => 'images_url',
311
			'zero' => 0,
312
		)
313
	);
314
315
	while ($row = $smcFunc['db_fetch_assoc']($request))
316
	{
317
		// First check to see if it points to a subfolder of $boardurl
318
		if (BoardurlMatch($row['value']))
319
		{
320
			if ($new_force_ssl == 2)
321
				$newval = strtr($row['value'], array('http://' => 'https://'));
322
			else
323
				$newval = strtr($row['value'], array('https://' => 'http://'));
324
			$smcFunc['db_query']('', '
325
				UPDATE {db_prefix}themes
326
				   SET value = {string:theme_val}
327
				 WHERE variable = {string:theme_var}
328
				   AND id_theme = {string:theme_id}
329
				   AND id_member = {int:zero}',
330
				array(
331
					'theme_val' => $newval,
332
					'theme_var' => $row['variable'],
333
					'theme_id' => $row['id_theme'],
334
					'zero' => 0,
335
				)
336
			);
337
		}
338
	}
339
	$smcFunc['db_free_result']($request);
340
}
341
342
/**
343
 * $boardurl Match.
344
 *
345
 * Helper function to see if the url being checked is based off of $boardurl.
346
 * If not, it was overridden by the admin to some other value on purpose, and should not
347
 * be stepped on by SMF when aligning URLs with the force_ssl setting.
348
 * The site admin must change URLs that are not aligned with $boardurl manually.
349
 *
350
 * @param string $url is the url to check.
351
 * @return bool Returns true if the url is based off of $boardurl (without the scheme), false if not
352
 */
353
function BoardurlMatch($url = '')
354
{
355
	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...
356
357
	// Strip the schemes
358
	$urlpath = strtr($url, array('http://' => '', 'https://' => ''));
359
	$boardurlpath = strtr($boardurl, array('http://' => '', 'https://' => ''));
360
361
	// If leftmost portion of path matches boardurl, return true
362
	$result = strpos($urlpath, $boardurlpath);
363
	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...
364
		return false;
365
	else
366
		return true;
367
}
368
369
/**
370
 * Basic database and paths settings - database name, host, etc.
371
 *
372
 * - It shows an interface for the settings in Settings.php to be changed.
373
 * - It contains the actual array of settings to show from Settings.php.
374
 * - Requires the admin_forum permission.
375
 * - Uses the edit_settings administration area.
376
 * - Accessed from ?action=admin;area=serversettings;sa=database.
377
 *
378
 * @param bool $return_config Whether or not to return the config_vars array (used for admin search)
379
 * @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...
380
 */
381
function ModifyDatabaseSettings($return_config = false)
382
{
383
	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...
384
385
	/* If you're writing a mod, it's a bad idea to add things here....
386
		For each option:
387
		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)
388
		OR an empty string for a horizontal rule.
389
		OR a string for a titled section. */
390
	$config_vars = array(
391
		array('db_persist', $txt['db_persist'], 'file', 'check', null, 'db_persist'),
392
		array('db_error_send', $txt['db_error_send'], 'file', 'check'),
393
		array('ssi_db_user', $txt['ssi_db_user'], 'file', 'text', null, 'ssi_db_user'),
394
		array('ssi_db_passwd', $txt['ssi_db_passwd'], 'file', 'password'),
395
		'',
396
		array('autoFixDatabase', $txt['autoFixDatabase'], 'db', 'check', false, 'autoFixDatabase')
397
	);
398
399
	// Add PG Stuff
400
	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...
401
	{
402
		$request = $smcFunc['db_query']('', 'SELECT cfgname FROM pg_ts_config', array());
403
		$fts_language = array();
404
405
		while ($row = $smcFunc['db_fetch_assoc']($request))
406
			$fts_language[$row['cfgname']] = $row['cfgname'];
407
408
		$config_vars = array_merge ($config_vars, array(
409
				'',
410
				array('search_language', $txt['search_language'], 'db', 'select', $fts_language, 'pgFulltextSearch')
411
			)
412
		);
413
	}
414
415
416
	call_integration_hook('integrate_database_settings', array(&$config_vars));
417
418
	if ($return_config)
419
		return $config_vars;
420
421
	// Setup the template stuff.
422
	$context['post_url'] = $scripturl . '?action=admin;area=serversettings;sa=database;save';
423
	$context['settings_title'] = $txt['database_settings'];
424
	$context['save_disabled'] = $context['settings_not_writable'];
425
426
	// Saving settings?
427
	if (isset($_REQUEST['save']))
428
	{
429
		call_integration_hook('integrate_save_database_settings');
430
431
		saveSettings($config_vars);
432
		$_SESSION['adm-save'] = true;
433
		redirectexit('action=admin;area=serversettings;sa=database;' . $context['session_var'] . '=' . $context['session_id']);
434
	}
435
436
	// Fill the config array.
437
	prepareServerSettingsContext($config_vars);
438
}
439
440
/**
441
 * This function handles cookies settings modifications.
442
 *
443
 * @param bool $return_config Whether or not to return the config_vars array (used for admin search)
444
 * @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...
445
 */
446
function ModifyCookieSettings($return_config = false)
447
{
448
	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...
449
450
	// Define the variables we want to edit.
451
	$config_vars = array(
452
		// Cookies...
453
		array('cookiename', $txt['cookie_name'], 'file', 'text', 20),
454
		array('cookieTime', $txt['cookieTime'], 'db', 'int', 'postinput' => $txt['minutes']),
455
		array('localCookies', $txt['localCookies'], 'db', 'check', false, 'localCookies'),
456
		array('globalCookies', $txt['globalCookies'], 'db', 'check', false, 'globalCookies'),
457
		array('globalCookiesDomain', $txt['globalCookiesDomain'], 'db', 'text', false, 'globalCookiesDomain'),
458
		array('secureCookies', $txt['secureCookies'], 'db', 'check', false, 'secureCookies', 'disabled' => !isset($_SERVER['HTTPS']) || strtolower($_SERVER['HTTPS']) != 'on'),
459
		array('httponlyCookies', $txt['httponlyCookies'], 'db', 'check', false, 'httponlyCookies'),
460
		'',
461
		// Sessions
462
		array('databaseSession_enable', $txt['databaseSession_enable'], 'db', 'check', false, 'databaseSession_enable'),
463
		array('databaseSession_loose', $txt['databaseSession_loose'], 'db', 'check', false, 'databaseSession_loose'),
464
		array('databaseSession_lifetime', $txt['databaseSession_lifetime'], 'db', 'int', false, 'databaseSession_lifetime', 'postinput' => $txt['seconds']),
465
		'',
466
		// 2FA
467
		array('tfa_mode', $txt['tfa_mode'], 'db', 'select', array(
468
			0 => $txt['tfa_mode_disabled'],
469
			1 => $txt['tfa_mode_enabled'],
470
		) + (empty($user_settings['tfa_secret']) ? array() : array(
471
			2 => $txt['tfa_mode_forced'],
472
		)) + (empty($user_settings['tfa_secret']) ? array() : array(
473
			3 => $txt['tfa_mode_forcedall'],
474
		)), 'subtext' => $txt['tfa_mode_subtext'] . (empty($user_settings['tfa_secret']) ? '<br><strong>' . $txt['tfa_mode_forced_help'] . '</strong>' : ''), 'tfa_mode'),
475
	);
476
477
	addInlineJavaScript('
478
	function hideGlobalCookies()
479
	{
480
		var usingLocal = $("#localCookies").prop("checked");
481
		$("#setting_globalCookies").closest("dt").toggle(!usingLocal);
482
		$("#globalCookies").closest("dd").toggle(!usingLocal);
483
484
		var usingGlobal = !usingLocal && $("#globalCookies").prop("checked");
485
		$("#setting_globalCookiesDomain").closest("dt").toggle(usingGlobal);
486
		$("#globalCookiesDomain").closest("dd").toggle(usingGlobal);
487
	};
488
	hideGlobalCookies();
489
490
	$("#localCookies, #globalCookies").click(function() {
491
		hideGlobalCookies();
492
	});', true);
493
494
	if (empty($user_settings['tfa_secret']))
495
		addInlineJavaScript('');
496
497
	call_integration_hook('integrate_cookie_settings', array(&$config_vars));
498
499
	if ($return_config)
500
		return $config_vars;
501
502
	$context['post_url'] = $scripturl . '?action=admin;area=serversettings;sa=cookie;save';
503
	$context['settings_title'] = $txt['cookies_sessions_settings'];
504
505
	// Saving settings?
506
	if (isset($_REQUEST['save']))
507
	{
508
		call_integration_hook('integrate_save_cookie_settings');
509
510
		// Local and global do not play nicely together.
511
		if (!empty($_POST['localCookies']) && empty($_POST['globalCookies']))
512
			unset ($_POST['globalCookies']);
513
514
		if (!empty($_POST['globalCookiesDomain']) && strpos($boardurl, $_POST['globalCookiesDomain']) === false)
515
			fatal_lang_error('invalid_cookie_domain', false);
516
517
		saveSettings($config_vars);
518
519
		// If the cookie name was changed, reset the cookie.
520
		if ($cookiename != $_POST['cookiename'])
521
		{
522
			$original_session_id = $context['session_id'];
523
			include_once($sourcedir . '/Subs-Auth.php');
524
525
			// Remove the old cookie.
526
			setLoginCookie(-3600, 0);
527
528
			// Set the new one.
529
			$cookiename = $_POST['cookiename'];
530
			setLoginCookie(60 * $modSettings['cookieTime'], $user_settings['id_member'], hash_salt($user_settings['passwd'], $user_settings['password_salt']));
531
532
			redirectexit('action=admin;area=serversettings;sa=cookie;' . $context['session_var'] . '=' . $original_session_id, $context['server']['needs_login_fix']);
533
		}
534
535
		//If we disabled 2FA, reset all members and membergroups settings.
536
		if (isset($_POST['tfa_mode']) && empty($_POST['tfa_mode']))
537
		{
538
			$smcFunc['db_query']('', '
539
				UPDATE {db_prefix}membergroups
540
				SET tfa_required = {int:zero}',
541
				array(
542
					'zero' => 0,
543
				)
544
			);
545
			$smcFunc['db_query']('', '
546
				UPDATE {db_prefix}members
547
				SET tfa_secret = {string:empty}, tfa_backup = {string:empty}',
548
				array(
549
					'empty' => '',
550
				)
551
			);
552
		}
553
554
		$_SESSION['adm-save'] = true;
555
		redirectexit('action=admin;area=serversettings;sa=cookie;' . $context['session_var'] . '=' . $context['session_id']);
556
	}
557
558
	// Fill the config array.
559
	prepareServerSettingsContext($config_vars);
560
}
561
562
/**
563
 * Settings really associated with general security aspects.
564
 *
565
 * @param bool $return_config Whether or not to return the config_vars array (used for admin search)
566
 * @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...
567
 */
568
function ModifyGeneralSecuritySettings($return_config = false)
569
{
570
	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...
571
572
	$config_vars = array(
573
			array('int', 'failed_login_threshold'),
574
			array('int', 'loginHistoryDays', 'subtext' => $txt['zero_to_disable']),
575
		'',
576
			array('check', 'securityDisable'),
577
			array('check', 'securityDisable_moderate'),
578
		'',
579
			// Reactive on email, and approve on delete
580
			array('check', 'send_validation_onChange'),
581
			array('check', 'approveAccountDeletion'),
582
		'',
583
			// Password strength.
584
			array('select', 'password_strength', array($txt['setting_password_strength_low'], $txt['setting_password_strength_medium'], $txt['setting_password_strength_high'])),
585
			array('check', 'enable_password_conversion'),
586
		'',
587
			// Reporting of personal messages?
588
			array('check', 'enableReportPM'),
589
		'',
590
			array('select', 'frame_security', array('SAMEORIGIN' => $txt['setting_frame_security_SAMEORIGIN'], 'DENY' => $txt['setting_frame_security_DENY'], 'DISABLE' => $txt['setting_frame_security_DISABLE'])),
591
		'',
592
			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')),
593
			array('text', 'proxy_ip_servers'),
594
	);
595
596
	call_integration_hook('integrate_general_security_settings', array(&$config_vars));
597
598
	if ($return_config)
599
		return $config_vars;
600
601
	// Saving?
602 View Code Duplication
	if (isset($_GET['save']))
603
	{
604
		saveDBSettings($config_vars);
605
		$_SESSION['adm-save'] = true;
606
607
		call_integration_hook('integrate_save_general_security_settings');
608
609
		writeLog();
610
		redirectexit('action=admin;area=serversettings;sa=security;' . $context['session_var'] . '=' . $context['session_id']);
611
	}
612
613
	$context['post_url'] = $scripturl . '?action=admin;area=serversettings;save;sa=security';
614
	$context['settings_title'] = $txt['security_settings'];
615
616
	prepareDBSettingContext($config_vars);
617
}
618
619
/**
620
 * Simply modifying cache functions
621
 *
622
 * @param bool $return_config Whether or not to return the config_vars array (used for admin search)
623
 * @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...
624
 */
625
function ModifyCacheSettings($return_config = false)
626
{
627
	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...
628
629
	// Detect all available optimizers
630
	$detected = loadCacheAPIs();
631
632
	// set our values to show what, if anything, we found
633
	if (empty($detected))
634
	{
635
		$txt['cache_settings_message'] = $txt['detected_no_caching'];
636
		$cache_level = array($txt['cache_off']);
637
		$detected['none'] = $txt['cache_off'];
638
	}
639
	else
640
	{
641
		$txt['cache_settings_message'] = sprintf($txt['detected_accelerators'], implode(', ', $detected));
642
		$cache_level = array($txt['cache_off'], $txt['cache_level1'], $txt['cache_level2'], $txt['cache_level3']);
643
	}
644
645
	// Define the variables we want to edit.
646
	$config_vars = array(
647
		// Only a few settings, but they are important
648
		array('', $txt['cache_settings_message'], '', 'desc'),
649
		array('cache_enable', $txt['cache_enable'], 'file', 'select', $cache_level, 'cache_enable'),
650
		array('cache_accelerator', $txt['cache_accelerator'], 'file', 'select', $detected),
651
	);
652
653
	// some javascript to enable / disable certain settings if the option is not selected
654
	$context['settings_post_javascript'] = '
655
		$(document).ready(function() {
656
			$("#cache_accelerator").change();
657
		});';
658
659
	call_integration_hook('integrate_modify_cache_settings', array(&$config_vars));
660
661
	// Maybe we have some additional settings from the selected accelerator.
662
	if (!empty($detected))
663
	{
664
		foreach ($detected as $tryCache => $dummy)
665
		{
666
			$cache_class_name = $tryCache . '_cache';
667
668
			// loadCacheAPIs has already included the file, just see if we can't add the settings in.
669
			if (is_callable(array($cache_class_name, 'cacheSettings')))
670
			{
671
				$testAPI = new $cache_class_name();
672
				call_user_func_array(array($testAPI, 'cacheSettings'), array(&$config_vars));
673
			}
674
		}
675
	}
676
	if ($return_config)
677
		return $config_vars;
678
679
	// Saving again?
680 View Code Duplication
	if (isset($_GET['save']))
681
	{
682
		call_integration_hook('integrate_save_cache_settings');
683
684
		saveSettings($config_vars);
685
		$_SESSION['adm-save'] = true;
686
687
		// We need to save the $cache_enable to $modSettings as well
688
		updatesettings(array('cache_enable' => (int) $_POST['cache_enable']));
689
690
		// exit so we reload our new settings on the page
691
		redirectexit('action=admin;area=serversettings;sa=cache;' . $context['session_var'] . '=' . $context['session_id']);
692
	}
693
694
	loadLanguage('ManageMaintenance');
695
	createToken('admin-maint');
696
	$context['template_layers'][] = 'clean_cache_button';
697
698
	$context['post_url'] = $scripturl . '?action=admin;area=serversettings;sa=cache;save';
699
	$context['settings_title'] = $txt['caching_settings'];
700
701
	// Changing cache settings won't have any effect if Settings.php is not writeable.
702
	$context['save_disabled'] = $context['settings_not_writable'];
703
704
	// Decide what message to show.
705
	if (!$context['save_disabled'])
706
		$context['settings_message'] = $txt['caching_information'];
707
708
	// Prepare the template.
709
	prepareServerSettingsContext($config_vars);
710
}
711
712
/**
713
 * Allows to edit load balancing settings.
714
 *
715
 * @param bool $return_config Whether or not to return the config_vars array
716
 * @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...
717
 */
718
function ModifyLoadBalancingSettings($return_config = false)
719
{
720
	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...
721
722
	// Setup a warning message, but disabled by default.
723
	$disabled = true;
724
	$context['settings_message'] = $txt['loadavg_disabled_conf'];
725
726
	if (stripos(PHP_OS, 'win') === 0)
727
	{
728
		$context['settings_message'] = $txt['loadavg_disabled_windows'];
729
		if (isset($_GET['save']))
730
			$_SESSION['adm-save'] = $txt['loadavg_disabled_windows'];
731
	}
732
	elseif (stripos(PHP_OS, 'darwin') === 0)
733
	{
734
		$context['settings_message'] = $txt['loadavg_disabled_osx'];
735
		if (isset($_GET['save']))
736
			$_SESSION['adm-save'] = $txt['loadavg_disabled_osx'];
737
	}
738
	else
739
	{
740
		$modSettings['load_average'] = @file_get_contents('/proc/loadavg');
741
		if (!empty($modSettings['load_average']) && preg_match('~^([^ ]+?) ([^ ]+?) ([^ ]+)~', $modSettings['load_average'], $matches) !== 0)
742
			$modSettings['load_average'] = (float) $matches[1];
743 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)
744
			$modSettings['load_average'] = (float) $matches[1];
745
		else
746
			unset($modSettings['load_average']);
747
748
		if (!empty($modSettings['load_average']) || $modSettings['load_average'] === 0.0)
749
		{
750
			$context['settings_message'] = sprintf($txt['loadavg_warning'], $modSettings['load_average']);
751
			$disabled = false;
752
		}
753
	}
754
755
	// Start with a simple checkbox.
756
	$config_vars = array(
757
		array('check', 'loadavg_enable', 'disabled' => $disabled),
758
	);
759
760
	// Set the default values for each option.
761
	$default_values = array(
762
		'loadavg_auto_opt' => 1.0,
763
		'loadavg_search' => 2.5,
764
		'loadavg_allunread' => 2.0,
765
		'loadavg_unreadreplies' => 3.5,
766
		'loadavg_show_posts' => 2.0,
767
		'loadavg_userstats' => 10.0,
768
		'loadavg_bbc' => 30.0,
769
		'loadavg_forum' => 40.0,
770
	);
771
772
	// Loop through the settings.
773
	foreach ($default_values as $name => $value)
774
	{
775
		// Use the default value if the setting isn't set yet.
776
		$value = !isset($modSettings[$name]) ? $value : $modSettings[$name];
777
		$config_vars[] = array('float', $name, 'value' => $value, 'disabled' => $disabled);
778
	}
779
780
	call_integration_hook('integrate_loadavg_settings', array(&$config_vars));
781
782
	if ($return_config)
783
		return $config_vars;
784
785
	$context['post_url'] = $scripturl . '?action=admin;area=serversettings;sa=loads;save';
786
	$context['settings_title'] = $txt['load_balancing_settings'];
787
788
	// Saving?
789
	if (isset($_GET['save']))
790
	{
791
		// Stupidity is not allowed.
792
		foreach ($_POST as $key => $value)
793
		{
794
			if (strpos($key, 'loadavg') === 0 || $key === 'loadavg_enable' || !in_array($key, array_keys($default_values)))
795
				continue;
796
			else
797
				$_POST[$key] = (float) $value;
798
799
			if ($key == 'loadavg_auto_opt' && $value <= 1)
800
				$_POST['loadavg_auto_opt'] = 1.0;
801
			elseif ($key == 'loadavg_forum' && $value < 10)
802
				$_POST['loadavg_forum'] = 10.0;
803
			elseif ($value < 2)
804
				$_POST[$key] = 2.0;
805
		}
806
807
		call_integration_hook('integrate_save_loadavg_settings');
808
809
		saveDBSettings($config_vars);
810
		if (!isset($_SESSION['adm-save']))
811
			$_SESSION['adm-save'] = true;
812
		redirectexit('action=admin;area=serversettings;sa=loads;' . $context['session_var'] . '=' . $context['session_id']);
813
	}
814
815
	prepareDBSettingContext($config_vars);
816
}
817
818
/**
819
 * Helper function, it sets up the context for the manage server settings.
820
 * - The basic usage of the six numbered key fields are
821
 * - array (0 ,1, 2, 3, 4, 5
822
 *		0 variable name - the name of the saved variable
823
 *		1 label - the text to show on the settings page
824
 *		2 saveto - file or db, where to save the variable name - value pair
825
 *		3 type - type of data to save, int, float, text, check
826
 *		4 size - false or field size
827
 *		5 help - '' or helptxt variable name
828
 *	)
829
 *
830
 * the following named keys are also permitted
831
 * 'disabled' => A string of code that will determine whether or not the setting should be disabled
832
 * 'postinput' => Text to display after the input field
833
 * 'preinput' => Text to display before the input field
834
 * 'subtext' => Additional descriptive text to display under the field's label
835
 * 'min' => minimum allowed value (for int/float). Defaults to 0 if not set.
836
 * 'max' => maximum allowed value (for int/float)
837
 * 'step' => how much to increment/decrement the value by (only for int/float - mostly used for float values).
838
 *
839
 * @param array $config_vars An array of configuration variables
840
 */
841
function prepareServerSettingsContext(&$config_vars)
842
{
843
	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...
844
845 View Code Duplication
	if (isset($_SESSION['adm-save']))
846
	{
847
		if ($_SESSION['adm-save'] === true)
848
			$context['saved_successful'] = true;
849
		else
850
			$context['saved_failed'] = $_SESSION['adm-save'];
851
852
		unset($_SESSION['adm-save']);
853
	}
854
855
	$context['config_vars'] = array();
856
	foreach ($config_vars as $identifier => $config_var)
857
	{
858
		if (!is_array($config_var) || !isset($config_var[1]))
859
			$context['config_vars'][] = $config_var;
860
		else
861
		{
862
			$varname = $config_var[0];
863
			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...
864
865
			// Set the subtext in case it's part of the label.
866
			// @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...
867
			$divPos = strpos($config_var[1], '<div');
868
			$subtext = '';
869
			if ($divPos !== false)
870
			{
871
				$subtext = preg_replace('~</?div[^>]*>~', '', substr($config_var[1], $divPos));
872
				$config_var[1] = substr($config_var[1], 0, $divPos);
873
			}
874
875
			$context['config_vars'][$config_var[0]] = array(
876
				'label' => $config_var[1],
877
				'help' => isset($config_var[5]) ? $config_var[5] : '',
878
				'type' => $config_var[3],
879
				'size' => empty($config_var[4]) ? 0 : $config_var[4],
880
				'data' => isset($config_var[4]) && is_array($config_var[4]) && $config_var[3] != 'select' ? $config_var[4] : array(),
881
				'name' => $config_var[0],
882
				'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 : '')),
883
				'disabled' => !empty($context['settings_not_writable']) || !empty($config_var['disabled']),
884
				'invalid' => false,
885
				'subtext' => !empty($config_var['subtext']) ? $config_var['subtext'] : $subtext,
886
				'javascript' => '',
887
				'preinput' => !empty($config_var['preinput']) ? $config_var['preinput'] : '',
888
				'postinput' => !empty($config_var['postinput']) ? $config_var['postinput'] : '',
889
			);
890
891
			// Handle min/max/step if necessary
892 View Code Duplication
			if ($config_var[3] == 'int' || $config_var[3] == 'float')
893
			{
894
				// Default to a min of 0 if one isn't set
895
				if (isset($config_var['min']))
896
					$context['config_vars'][$config_var[0]]['min'] = $config_var['min'];
897
				else
898
					$context['config_vars'][$config_var[0]]['min'] = 0;
899
900
				if (isset($config_var['max']))
901
					$context['config_vars'][$config_var[0]]['max'] = $config_var['max'];
902
903
				if (isset($config_var['step']))
904
					$context['config_vars'][$config_var[0]]['step'] = $config_var['step'];
905
			}
906
907
			// If this is a select box handle any data.
908
			if (!empty($config_var[4]) && is_array($config_var[4]))
909
			{
910
				// If it's associative
911
				$config_values = array_values($config_var[4]);
912 View Code Duplication
				if (isset($config_values[0]) && is_array($config_values[0]))
913
					$context['config_vars'][$config_var[0]]['data'] = $config_var[4];
914
				else
915
				{
916
					foreach ($config_var[4] as $key => $item)
917
						$context['config_vars'][$config_var[0]]['data'][] = array($key, $item);
918
				}
919
			}
920
		}
921
	}
922
923
	// Two tokens because saving these settings requires both saveSettings and saveDBSettings
924
	createToken('admin-ssc');
925
	createToken('admin-dbsc');
926
}
927
928
/**
929
 * Helper function, it sets up the context for database settings.
930
 * @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...
931
 *
932
 * @param array $config_vars An array of configuration variables
933
 */
934
function prepareDBSettingContext(&$config_vars)
935
{
936
	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...
937
938
	loadLanguage('Help');
939
940 View Code Duplication
	if (isset($_SESSION['adm-save']))
941
	{
942
		if ($_SESSION['adm-save'] === true)
943
			$context['saved_successful'] = true;
944
		else
945
			$context['saved_failed'] = $_SESSION['adm-save'];
946
947
		unset($_SESSION['adm-save']);
948
	}
949
950
	$context['config_vars'] = array();
951
	$inlinePermissions = array();
952
	$bbcChoice = array();
953
	$board_list = false;
954
	foreach ($config_vars as $config_var)
955
	{
956
		// HR?
957
		if (!is_array($config_var))
958
			$context['config_vars'][] = $config_var;
959
		else
960
		{
961
			// If it has no name it doesn't have any purpose!
962
			if (empty($config_var[1]))
963
				continue;
964
965
			// Special case for inline permissions
966
			if ($config_var[0] == 'permissions' && allowedTo('manage_permissions'))
967
				$inlinePermissions[] = $config_var[1];
968
			elseif ($config_var[0] == 'permissions')
969
				continue;
970
971
			if ($config_var[0] == 'boards')
972
				$board_list = true;
973
974
			// Are we showing the BBC selection box?
975
			if ($config_var[0] == 'bbc')
976
				$bbcChoice[] = $config_var[1];
977
978
			// We need to do some parsing of the value before we pass it in.
979
			if (isset($modSettings[$config_var[1]]))
980
			{
981
				switch ($config_var[0])
982
				{
983
					case 'select':
984
						$value = $modSettings[$config_var[1]];
985
						break;
986
					case 'json':
987
						$value = $smcFunc['htmlspecialchars']($smcFunc['json_encode']($modSettings[$config_var[1]]));
988
						break;
989
					case 'boards':
990
						$value = explode(',', $modSettings[$config_var[1]]);
991
						break;
992
					default:
993
						$value = $smcFunc['htmlspecialchars']($modSettings[$config_var[1]]);
994
				}
995
			}
996
			else
997
			{
998
				// Darn, it's empty. What type is expected?
999
				switch ($config_var[0])
1000
				{
1001
					case 'int':
1002
					case 'float':
1003
						$value = 0;
1004
						break;
1005
					case 'select':
1006
						$value = !empty($config_var['multiple']) ? $smcFunc['json_encode'](array()) : '';
1007
						break;
1008
					case 'boards':
1009
						$value = array();
1010
						break;
1011
					default:
1012
						$value = '';
1013
				}
1014
			}
1015
1016
			$context['config_vars'][$config_var[1]] = array(
1017
				'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] : '')),
1018
				'help' => isset($helptxt[$config_var[1]]) ? $config_var[1] : '',
1019
				'type' => $config_var[0],
1020
				'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)),
1021
				'data' => array(),
1022
				'name' => $config_var[1],
1023
				'value' => $value,
1024
				'disabled' => false,
1025
				'invalid' => !empty($config_var['invalid']),
1026
				'javascript' => '',
1027
				'var_message' => !empty($config_var['message']) && isset($txt[$config_var['message']]) ? $txt[$config_var['message']] : '',
1028
				'preinput' => isset($config_var['preinput']) ? $config_var['preinput'] : '',
1029
				'postinput' => isset($config_var['postinput']) ? $config_var['postinput'] : '',
1030
			);
1031
1032
			// Handle min/max/step if necessary
1033 View Code Duplication
			if ($config_var[0] == 'int' || $config_var[0] == 'float')
1034
			{
1035
				// Default to a min of 0 if one isn't set
1036
				if (isset($config_var['min']))
1037
					$context['config_vars'][$config_var[1]]['min'] = $config_var['min'];
1038
				else
1039
					$context['config_vars'][$config_var[1]]['min'] = 0;
1040
1041
				if (isset($config_var['max']))
1042
					$context['config_vars'][$config_var[1]]['max'] = $config_var['max'];
1043
1044
				if (isset($config_var['step']))
1045
					$context['config_vars'][$config_var[1]]['step'] = $config_var['step'];
1046
			}
1047
1048
			// If this is a select box handle any data.
1049
			if (!empty($config_var[2]) && is_array($config_var[2]))
1050
			{
1051
				// If we allow multiple selections, we need to adjust a few things.
1052
				if ($config_var[0] == 'select' && !empty($config_var['multiple']))
1053
				{
1054
					$context['config_vars'][$config_var[1]]['name'] .= '[]';
1055
					$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();
1056
				}
1057
1058
				// If it's associative
1059 View Code Duplication
				if (isset($config_var[2][0]) && is_array($config_var[2][0]))
1060
					$context['config_vars'][$config_var[1]]['data'] = $config_var[2];
1061
				else
1062
				{
1063
					foreach ($config_var[2] as $key => $item)
1064
						$context['config_vars'][$config_var[1]]['data'][] = array($key, $item);
1065
				}
1066
			}
1067
1068
			// Finally allow overrides - and some final cleanups.
1069
			foreach ($config_var as $k => $v)
1070
			{
1071
				if (!is_numeric($k))
1072
				{
1073
					if (substr($k, 0, 2) == 'on')
1074
						$context['config_vars'][$config_var[1]]['javascript'] .= ' ' . $k . '="' . $v . '"';
1075
					else
1076
						$context['config_vars'][$config_var[1]][$k] = $v;
1077
				}
1078
1079
				// See if there are any other labels that might fit?
1080
				if (isset($txt['setting_' . $config_var[1]]))
1081
					$context['config_vars'][$config_var[1]]['label'] = $txt['setting_' . $config_var[1]];
1082
				elseif (isset($txt['groups_' . $config_var[1]]))
1083
					$context['config_vars'][$config_var[1]]['label'] = $txt['groups_' . $config_var[1]];
1084
			}
1085
1086
			// Set the subtext in case it's part of the label.
1087
			// @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...
1088
			$divPos = strpos($context['config_vars'][$config_var[1]]['label'], '<div');
1089
			if ($divPos !== false)
1090
			{
1091
				$context['config_vars'][$config_var[1]]['subtext'] = preg_replace('~</?div[^>]*>~', '', substr($context['config_vars'][$config_var[1]]['label'], $divPos));
1092
				$context['config_vars'][$config_var[1]]['label'] = substr($context['config_vars'][$config_var[1]]['label'], 0, $divPos);
1093
			}
1094
		}
1095
	}
1096
1097
	// If we have inline permissions we need to prep them.
1098
	if (!empty($inlinePermissions) && allowedTo('manage_permissions'))
1099
	{
1100
		require_once($sourcedir . '/ManagePermissions.php');
1101
		init_inline_permissions($inlinePermissions, isset($context['permissions_excluded']) ? $context['permissions_excluded'] : array());
1102
	}
1103
1104
	if ($board_list)
1105
	{
1106
		require_once($sourcedir . '/Subs-MessageIndex.php');
1107
		$context['board_list'] = getBoardList();
1108
	}
1109
1110
	// What about any BBC selection boxes?
1111
	if (!empty($bbcChoice))
1112
	{
1113
		// What are the options, eh?
1114
		$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...
1115
		$bbcTags = array();
1116
		foreach ($temp as $tag)
0 ignored issues
show
Bug introduced by
The expression $temp of type string is not traversable.
Loading history...
1117
			$bbcTags[] = $tag['tag'];
1118
1119
		$bbcTags = array_unique($bbcTags);
1120
		$totalTags = count($bbcTags);
1121
1122
		// The number of columns we want to show the BBC tags in.
1123
		$numColumns = isset($context['num_bbc_columns']) ? $context['num_bbc_columns'] : 3;
1124
1125
		// Start working out the context stuff.
1126
		$context['bbc_columns'] = array();
1127
		$tagsPerColumn = ceil($totalTags / $numColumns);
1128
1129
		$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...
1130
		foreach ($bbcTags as $tag)
1131
		{
1132
			if ($i % $tagsPerColumn == 0 && $i != 0)
1133
				$col++;
1134
1135
			$context['bbc_columns'][$col][] = array(
1136
				'tag' => $tag,
1137
				// @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...
1138
				'show_help' => isset($helptxt[$tag]),
1139
			);
1140
1141
			$i++;
1142
		}
1143
1144
		// Now put whatever BBC options we may have into context too!
1145
		$context['bbc_sections'] = array();
1146
		foreach ($bbcChoice as $bbc)
1147
		{
1148
			$context['bbc_sections'][$bbc] = array(
1149
				'title' => isset($txt['bbc_title_' . $bbc]) ? $txt['bbc_title_' . $bbc] : $txt['bbcTagsToUse_select'],
1150
				'disabled' => empty($modSettings['bbc_disabled_' . $bbc]) ? array() : $modSettings['bbc_disabled_' . $bbc],
1151
				'all_selected' => empty($modSettings['bbc_disabled_' . $bbc]),
1152
			);
1153
		}
1154
	}
1155
1156
	call_integration_hook('integrate_prepare_db_settings', array(&$config_vars));
1157
	createToken('admin-dbsc');
1158
}
1159
1160
/**
1161
 * Helper function. Saves settings by putting them in Settings.php or saving them in the settings table.
1162
 *
1163
 * - Saves those settings set from ?action=admin;area=serversettings.
1164
 * - Requires the admin_forum permission.
1165
 * - Contains arrays of the types of data to save into Settings.php.
1166
 *
1167
 * @param array $config_vars An array of configuration variables
1168
 */
1169
function saveSettings(&$config_vars)
1170
{
1171
	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...
1172
1173
	validateToken('admin-ssc');
1174
1175
	// Fix the darn stupid cookiename! (more may not be allowed, but these for sure!)
1176
	if (isset($_POST['cookiename']))
1177
		$_POST['cookiename'] = preg_replace('~[,;\s\.$]+~' . ($context['utf8'] ? 'u' : ''), '', $_POST['cookiename']);
1178
1179
	// Fix the forum's URL if necessary.
1180
	if (isset($_POST['boardurl']))
1181
	{
1182 View Code Duplication
		if (substr($_POST['boardurl'], -10) == '/index.php')
1183
			$_POST['boardurl'] = substr($_POST['boardurl'], 0, -10);
1184
		elseif (substr($_POST['boardurl'], -1) == '/')
1185
			$_POST['boardurl'] = substr($_POST['boardurl'], 0, -1);
1186 View Code Duplication
		if (substr($_POST['boardurl'], 0, 7) != 'http://' && substr($_POST['boardurl'], 0, 7) != 'file://' && substr($_POST['boardurl'], 0, 8) != 'https://')
1187
			$_POST['boardurl'] = 'http://' . $_POST['boardurl'];
1188
	}
1189
1190
	// Any passwords?
1191
	$config_passwords = array(
1192
		'db_passwd',
1193
		'ssi_db_passwd',
1194
	);
1195
1196
	// All the strings to write.
1197
	$config_strs = array(
1198
		'mtitle', 'mmessage',
1199
		'language', 'mbname', 'boardurl',
1200
		'cookiename',
1201
		'webmaster_email',
1202
		'db_name', 'db_user', 'db_server', 'db_prefix', 'ssi_db_user',
1203
		'boarddir', 'sourcedir',
1204
		'cachedir', 'cachedir_sqlite', 'cache_accelerator', 'cache_memcached',
1205
		'image_proxy_secret',
1206
	);
1207
1208
	// All the numeric variables.
1209
	$config_ints = array(
1210
		'cache_enable',
1211
		'image_proxy_maxsize',
1212
	);
1213
1214
	// All the checkboxes
1215
	$config_bools = array('db_persist', 'db_error_send', 'maintenance', 'image_proxy_enabled');
1216
1217
	// Now sort everything into a big array, and figure out arrays and etc.
1218
	$new_settings = array();
1219
	// Figure out which config vars we're saving here...
1220
	foreach ($config_vars as $var)
1221
	{
1222
		if (!is_array($var) || $var[2] != 'file' || (!in_array($var[0], $config_bools) && !isset($_POST[$var[0]])))
1223
			continue;
1224
1225
		$config_var = $var[0];
1226
1227
		if (in_array($config_var, $config_passwords))
1228
		{
1229
			if (isset($_POST[$config_var][1]) && $_POST[$config_var][0] == $_POST[$config_var][1])
1230
				$new_settings[$config_var] = '\'' . addcslashes($_POST[$config_var][0], '\'\\') . '\'';
1231
		}
1232
		elseif (in_array($config_var, $config_strs))
1233
		{
1234
			$new_settings[$config_var] = '\'' . addcslashes($_POST[$config_var], '\'\\') . '\'';
1235
		}
1236
		elseif (in_array($config_var, $config_ints))
1237
		{
1238
			$new_settings[$config_var] = (int) $_POST[$config_var];
1239
1240
			// 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...
1241
			$min = isset($var['min']) ? $var['min'] : 0;
1242
			$new_settings[$config_var] = max($min, $new_settings[$config_var]);
1243
1244
			// Is there a max value for this as well?
1245
			if (isset($var['max']))
1246
				$new_settings[$config_var] = min($var['max'], $new_settings[$config_var]);
1247
		}
1248
		elseif (in_array($config_var, $config_bools))
1249
		{
1250
			if (!empty($_POST[$config_var]))
1251
				$new_settings[$config_var] = '1';
1252
			else
1253
				$new_settings[$config_var] = '0';
1254
		}
1255
		else
1256
		{
1257
			// This shouldn't happen, but it might...
1258
			fatal_error('Unknown config_var \'' . $config_var . '\'');
1259
		}
1260
	}
1261
1262
	// Save the relevant settings in the Settings.php file.
1263
	require_once($sourcedir . '/Subs-Admin.php');
1264
	updateSettingsFile($new_settings);
1265
1266
	// Now loop through the remaining (database-based) settings.
1267
	$new_settings = array();
1268
	foreach ($config_vars as $config_var)
1269
	{
1270
		// We just saved the file-based settings, so skip their definitions.
1271
		if (!is_array($config_var) || $config_var[2] == 'file')
1272
			continue;
1273
1274
		$new_setting = array($config_var[3], $config_var[0]);
1275
1276
		// Select options need carried over, too.
1277
		if (isset($config_var[4]))
1278
			$new_setting[] = $config_var[4];
1279
1280
		// Include min and max if necessary
1281
		if (isset($config_var['min']))
1282
			$new_setting['min'] = $config_var['min'];
1283
1284
		if (isset($config_var['max']))
1285
			$new_setting['max'] = $config_var['max'];
1286
1287
		// Rewrite the definition a bit.
1288
		$new_settings[] = $new_setting;
1289
	}
1290
1291
	// Save the new database-based settings, if any.
1292
	if (!empty($new_settings))
1293
		saveDBSettings($new_settings);
1294
}
1295
1296
/**
1297
 * Helper function for saving database settings.
1298
 * @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...
1299
 *
1300
 * @param array $config_vars An array of configuration variables
1301
 */
1302
function saveDBSettings(&$config_vars)
1303
{
1304
	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...
1305
	static $board_list = null;
1306
1307
	validateToken('admin-dbsc');
1308
1309
	$inlinePermissions = array();
1310
	foreach ($config_vars as $var)
1311
	{
1312
		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']))))
1313
			continue;
1314
1315
		// Checkboxes!
1316
		elseif ($var[0] == 'check')
1317
			$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...
1318
		// Select boxes!
1319
		elseif ($var[0] == 'select' && in_array($_POST[$var[1]], array_keys($var[2])))
1320
			$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...
1321
		elseif ($var[0] == 'select' && !empty($var['multiple']) && array_intersect($_POST[$var[1]], array_keys($var[2])) != array())
1322
		{
1323
			// For security purposes we validate this line by line.
1324
			$lOptions = array();
1325
			foreach ($_POST[$var[1]] as $invar)
1326
				if (in_array($invar, array_keys($var[2])))
1327
					$lOptions[] = $invar;
1328
1329
			$setArray[$var[1]] = $smcFunc['json_encode']($lOptions);
1330
		}
1331
		// List of boards!
1332
		elseif ($var[0] == 'boards')
1333
		{
1334
			// We just need a simple list of valid boards, nothing more.
1335
			if ($board_list === null)
1336
			{
1337
				$board_list = array();
1338
				$request = $smcFunc['db_query']('', '
1339
					SELECT id_board
1340
					FROM {db_prefix}boards');
1341
				while ($row = $smcFunc['db_fetch_row']($request))
1342
					$board_list[$row[0]] = true;
1343
1344
				$smcFunc['db_free_result']($request);
1345
			}
1346
1347
			$lOptions = array();
1348
1349
			if (!empty($_POST[$var[1]]))
1350
				foreach ($_POST[$var[1]] as $invar => $dummy)
1351
					if (isset($board_list[$invar]))
1352
						$lOptions[] = $invar;
1353
1354
			$setArray[$var[1]] = !empty($lOptions) ? implode(',', $lOptions) : '';
1355
		}
1356
		// Integers!
1357 View Code Duplication
		elseif ($var[0] == 'int')
1358
		{
1359
			$setArray[$var[1]] = (int) $_POST[$var[1]];
1360
1361
			// 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...
1362
			$min = isset($var['min']) ? $var['min'] : 0;
1363
			$setArray[$var[1]] = max($min, $setArray[$var[1]]);
1364
1365
			// Do we have a max value for this as well?
1366
			if (isset($var['max']))
1367
				$setArray[$var[1]] = min($var['max'], $setArray[$var[1]]);
1368
		}
1369
		// Floating point!
1370 View Code Duplication
		elseif ($var[0] == 'float')
1371
		{
1372
			$setArray[$var[1]] = (float) $_POST[$var[1]];
1373
1374
			// 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...
1375
			$min = isset($var['min']) ? $var['min'] : 0;
1376
			$setArray[$var[1]] = max($min, $setArray[$var[1]]);
1377
1378
			// Do we have a max value for this as well?
1379
			if (isset($var['max']))
1380
				$setArray[$var[1]] = min($var['max'], $setArray[$var[1]]);
1381
		}
1382
		// Text!
1383
		elseif (in_array($var[0], array('text', 'large_text', 'color', 'date', 'datetime', 'datetime-local', 'email', 'month', 'time')))
1384
			$setArray[$var[1]] = $_POST[$var[1]];
1385
		// Passwords!
1386
		elseif ($var[0] == 'password')
1387
		{
1388
			if (isset($_POST[$var[1]][1]) && $_POST[$var[1]][0] == $_POST[$var[1]][1])
1389
				$setArray[$var[1]] = $_POST[$var[1]][0];
1390
		}
1391
		// BBC.
1392
		elseif ($var[0] == 'bbc')
1393
		{
1394
1395
			$bbcTags = array();
1396
			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...
1397
				$bbcTags[] = $tag['tag'];
1398
1399
			if (!isset($_POST[$var[1] . '_enabledTags']))
1400
				$_POST[$var[1] . '_enabledTags'] = array();
1401
			elseif (!is_array($_POST[$var[1] . '_enabledTags']))
1402
				$_POST[$var[1] . '_enabledTags'] = array($_POST[$var[1] . '_enabledTags']);
1403
1404
			$setArray[$var[1]] = implode(',', array_diff($bbcTags, $_POST[$var[1] . '_enabledTags']));
1405
		}
1406
		// Permissions?
1407
		elseif ($var[0] == 'permissions')
1408
			$inlinePermissions[] = $var[1];
1409
	}
1410
1411
	if (!empty($setArray))
1412
		updateSettings($setArray);
1413
1414
	// If we have inline permissions we need to save them.
1415
	if (!empty($inlinePermissions) && allowedTo('manage_permissions'))
1416
	{
1417
		require_once($sourcedir . '/ManagePermissions.php');
1418
		save_inline_permissions($inlinePermissions);
1419
	}
1420
}
1421
1422
/**
1423
 * Allows us to see the servers php settings
1424
 *
1425
 * - loads the settings into an array for display in a template
1426
 * - drops cookie values just in case
1427
 */
1428
function ShowPHPinfoSettings()
1429
{
1430
	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...
1431
1432
	$category = $txt['phpinfo_settings'];
1433
1434
	// get the data
1435
	ob_start();
1436
	phpinfo();
1437
1438
	// We only want it for its body, pigs that we are
1439
	$info_lines = preg_replace('~^.*<body>(.*)</body>.*$~', '$1', ob_get_contents());
1440
	$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...
1441
	ob_end_clean();
1442
1443
	// remove things that could be considered sensitive
1444
	$remove = '_COOKIE|Cookie|_GET|_REQUEST|REQUEST_URI|QUERY_STRING|REQUEST_URL|HTTP_REFERER';
1445
1446
	// put all of it into an array
1447
	foreach ($info_lines as $line)
1448
	{
1449
		if (preg_match('~(' . $remove . ')~', $line))
1450
			continue;
1451
1452
		// new category?
1453
		if (strpos($line, '<h2>') !== false)
1454
			$category = preg_match('~<h2>(.*)</h2>~', $line, $title) ? $category = $title[1] : $category;
1455
1456
		// load it as setting => value or the old setting local master
1457
		if (preg_match('~<tr><td[^>]+>([^<]*)</td><td[^>]+>([^<]*)</td></tr>~', $line, $val))
1458
			$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...
1459
		elseif (preg_match('~<tr><td[^>]+>([^<]*)</td><td[^>]+>([^<]*)</td><td[^>]+>([^<]*)</td></tr>~', $line, $val))
1460
			$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...
1461
	}
1462
1463
	// load it in to context and display it
1464
	$context['pinfo'] = $pinfo;
1465
	$context['page_title'] = $txt['admin_server_settings'];
1466
	$context['sub_template'] = 'php_info';
1467
	return;
1468
}
1469
1470
/**
1471
 * Get the installed Cache API implementations.
1472
 *
1473
 */
1474
function loadCacheAPIs()
1475
{
1476
	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...
1477
1478
	// Make sure our class is in session.
1479
	require_once($sourcedir . '/Class-CacheAPI.php');
1480
1481
	$apis = array();
1482
	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...
1483
	{
1484
		while (($file = readdir($dh)) !== false)
1485
		{
1486
			if (is_file($sourcedir . '/' . $file) && preg_match('~^CacheAPI-([A-Za-z\d_]+)\.php$~', $file, $matches))
1487
			{
1488
				$tryCache = strtolower($matches[1]);
1489
1490
				require_once($sourcedir . '/' . $file);
1491
				$cache_class_name = $tryCache . '_cache';
1492
				$testAPI = new $cache_class_name();
1493
1494
				// No Support?  NEXT!
1495
				if (!$testAPI->isSupported(true))
1496
					continue;
1497
1498
				$apis[$tryCache] = isset($txt[$tryCache . '_cache']) ? $txt[$tryCache . '_cache'] : $tryCache;
1499
			}
1500
		}
1501
	}
1502
	closedir($dh);
1503
1504
	return $apis;
1505
}
1506
1507
/**
1508
 * Registers the site with the Simple Machines Stat collection. This function
1509
 * purposely does not use updateSettings.php as it will be called shortly after
1510
 * this process completes by the saveSettings() function.
1511
 *
1512
 * @see Stats.php SMStats() for more information.
1513
 * @link https://www.simplemachines.org/about/stats.php for more info.
1514
 *
1515
 */
1516
function registerSMStats()
1517
{
1518
	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...
1519
1520
	// Already have a key?  Can't register again.
1521
	if (!empty($modSettings['sm_stats_key']))
1522
		return true;
1523
1524
	$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...
1525
	if ($fp)
1526
	{
1527
		$out = 'GET /smf/stats/register_stats.php?site=' . base64_encode($boardurl) . ' HTTP/1.1' . "\r\n";
1528
		$out .= 'Host: www.simplemachines.org' . "\r\n";
1529
		$out .= 'Connection: Close' . "\r\n\r\n";
1530
		fwrite($fp, $out);
1531
1532
		$return_data = '';
1533
		while (!feof($fp))
1534
			$return_data .= fgets($fp, 128);
1535
1536
		fclose($fp);
1537
1538
		// Get the unique site ID.
1539
		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...
1540
1541
		if (!empty($ID[1]))
1542
		{
1543
			$smcFunc['db_insert']('replace',
1544
				'{db_prefix}settings',
1545
				array('variable' => 'string', 'value' => 'string'),
1546
				array('sm_stats_key', $ID[1]),
1547
				array('variable')
1548
			);
1549
			return true;
1550
		}
1551
	}
1552
1553
	return false;
1554
}
1555
1556
?>