Completed
Push — release-2.1 ( c58202...ffbace )
by Jeremy
16s
created

install.php ➔ load_lang_file()   D

Complexity

Conditions 16
Paths 62

Size

Total Lines 84

Duplication

Lines 44
Ratio 52.38 %

Importance

Changes 0
Metric Value
cc 16
nc 62
nop 0
dl 44
loc 84
rs 4.8023
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * Simple Machines Forum (SMF)
5
 *
6
 * @package SMF
7
 * @author Simple Machines http://www.simplemachines.org
8
 * @copyright 2018 Simple Machines and individual contributors
9
 * @license http://www.simplemachines.org/about/smf/license.php BSD
10
 *
11
 * @version 2.1 Beta 4
12
 */
13
14
$GLOBALS['current_smf_version'] = '2.1 Beta 4';
15
$GLOBALS['db_script_version'] = '2-1';
16
17
$GLOBALS['required_php_version'] = '5.4.0';
18
19
// Don't have PHP support, do you?
20
// ><html dir="ltr"><head><title>Error!</title></head><body>Sorry, this installer requires PHP!<div style="display: none;">
21
22
// Let's pull in useful classes
23
if (!defined('SMF'))
24
	define('SMF', 1);
25
26
require_once('Sources/Class-Package.php');
27
28
// Database info.
29
$databases = array(
30
	'mysql' => array(
31
		'name' => 'MySQL',
32
		'version' => '5.0.22',
33
		'version_check' => 'return min(mysqli_get_server_info($db_connection), mysqli_get_client_info());',
34
		'supported' => function_exists('mysqli_connect'),
35
		'default_user' => 'mysql.default_user',
36
		'default_password' => 'mysql.default_password',
37
		'default_host' => 'mysql.default_host',
38
		'default_port' => 'mysql.default_port',
39
		'utf8_support' => function() {
40
			return true;
41
		},
42
		'utf8_version' => '5.0.22',
43
		'utf8_version_check' => 'return mysqli_get_server_info($db_connection);',
44
		'utf8_default' => true,
45
		'utf8_required' => true,
46
		'alter_support' => true,
47
		'validate_prefix' => function(&$value) {
48
			$value = preg_replace('~[^A-Za-z0-9_\$]~', '', $value);
49
			return true;
50
		},
51
	),
52
	'postgresql' => array(
53
		'name' => 'PostgreSQL',
54
		'version' => '9.4',
55
		'function_check' => 'pg_connect',
56
		'version_check' => '$request = pg_query(\'SELECT version()\'); list ($version) = pg_fetch_row($request); list($pgl, $version) = explode(" ", $version); return $version;',
57
		'supported' => function_exists('pg_connect'),
58
		'always_has_db' => true,
59
		'utf8_default' => true,
60
		'utf8_required' => true,
61
		'utf8_support' => function() {
62
			$request = pg_query('SHOW SERVER_ENCODING');
63
64
			list ($charcode) = pg_fetch_row($request);
65
66
			if ($charcode == 'UTF8')
0 ignored issues
show
Coding Style introduced by
The if-else statement can be simplified to return $charcode == 'UTF8';.
Loading history...
67
				return true;
68
			else
69
				return false;
70
		},
71
		'utf8_version' => '8.0',
72
		'utf8_version_check' => '$request = pg_query(\'SELECT version()\'); list ($version) = pg_fetch_row($request); list($pgl, $version) = explode(" ", $version); return $version;',
73
		'validate_prefix' => function(&$value) {
74
			global $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...
75
76
			$value = preg_replace('~[^A-Za-z0-9_\$]~', '', $value);
77
78
			// Is it reserved?
79
			if ($value == 'pg_')
80
				return $txt['error_db_prefix_reserved'];
81
82
			// Is the prefix numeric?
83
			if (preg_match('~^\d~', $value))
84
				return $txt['error_db_prefix_numeric'];
85
86
			return true;
87
		},
88
	),
89
);
90
91
global $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...
92
93
// Initialize everything and load the language files.
94
initialize_inputs();
95
load_lang_file();
96
97
// This is what we are.
98
$installurl = $_SERVER['PHP_SELF'];
99
100
// All the steps in detail.
101
// Number,Name,Function,Progress Weight.
102
$incontext['steps'] = array(
103
	0 => array(1, $txt['install_step_welcome'], 'Welcome', 0),
104
	1 => array(2, $txt['install_step_writable'], 'CheckFilesWritable', 10),
105
	2 => array(3, $txt['install_step_databaseset'], 'DatabaseSettings', 15),
106
	3 => array(4, $txt['install_step_forum'], 'ForumSettings', 40),
107
	4 => array(5, $txt['install_step_databasechange'], 'DatabasePopulation', 15),
108
	5 => array(6, $txt['install_step_admin'], 'AdminAccount', 20),
109
	6 => array(7, $txt['install_step_delete'], 'DeleteInstall', 0),
110
);
111
112
// Default title...
113
$incontext['page_title'] = $txt['smf_installer'];
114
115
// What step are we on?
116
$incontext['current_step'] = isset($_GET['step']) ? (int) $_GET['step'] : 0;
117
118
// Loop through all the steps doing each one as required.
119
$incontext['overall_percent'] = 0;
120
121
foreach ($incontext['steps'] as $num => $step)
122
{
123
	if ($num >= $incontext['current_step'])
124
	{
125
		// The current weight of this step in terms of overall progress.
126
		$incontext['step_weight'] = $step[3];
127
		// Make sure we reset the skip button.
128
		$incontext['skip'] = false;
129
130
		// Call the step and if it returns false that means pause!
131
		if (function_exists($step[2]) && $step[2]() === false)
132
			break;
133
		elseif (function_exists($step[2]))
134
			$incontext['current_step']++;
135
136
		// No warnings pass on.
137
		$incontext['warning'] = '';
138
	}
139
	$incontext['overall_percent'] += $step[3];
140
}
141
142
// Actually do the template stuff.
143
installExit();
144
145
function initialize_inputs()
146
{
147
	global $databases;
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...
148
149
	// Just so people using older versions of PHP aren't left in the cold.
150
	if (!isset($_SERVER['PHP_SELF']))
151
		$_SERVER['PHP_SELF'] = isset($GLOBALS['HTTP_SERVER_VARS']['PHP_SELF']) ? $GLOBALS['HTTP_SERVER_VARS']['PHP_SELF'] : 'install.php';
152
153
	// Enable error reporting for fatal errors.
154
	error_reporting(E_ERROR | E_PARSE);
155
156
	// Fun.  Low PHP version...
157
	if (!isset($_GET))
158
	{
159
		$GLOBALS['_GET']['step'] = 0;
160
		return;
161
	}
162
163
	if (!isset($_GET['obgz']))
164
	{
165
		ob_start();
166
167
		if (ini_get('session.save_handler') == 'user')
168
			@ini_set('session.save_handler', 'files');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
169
		if (function_exists('session_start'))
170
			@session_start();
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
171
	}
172
	else
173
	{
174
		ob_start('ob_gzhandler');
175
176
		if (ini_get('session.save_handler') == 'user')
177
			@ini_set('session.save_handler', 'files');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
178
		session_start();
179
180
		if (!headers_sent())
181
			echo '<!DOCTYPE html>
182
<html>
183
	<head>
184
		<title>', htmlspecialchars($_GET['pass_string']), '</title>
185
	</head>
186
	<body style="background-color: #d4d4d4; margin-top: 16%; text-align: center; font-size: 16pt;">
187
		<strong>', htmlspecialchars($_GET['pass_string']), '</strong>
188
	</body>
189
</html>';
190
		exit;
191
	}
192
193
	// Add slashes, as long as they aren't already being added.
194
	if (!function_exists('get_magic_quotes_gpc') || @get_magic_quotes_gpc() == 0)
195
		foreach ($_POST as $k => $v)
196
			if (strpos($k, 'password') === false && strpos($k, 'db_passwd') === false)
197
				$_POST[$k] = addslashes($v);
198
199
	// This is really quite simple; if ?delete is on the URL, delete the installer...
200
	if (isset($_GET['delete']))
201
	{
202
		if (isset($_SESSION['installer_temp_ftp']))
203
		{
204
			$ftp = new ftp_connection($_SESSION['installer_temp_ftp']['server'], $_SESSION['installer_temp_ftp']['port'], $_SESSION['installer_temp_ftp']['username'], $_SESSION['installer_temp_ftp']['password']);
205
			$ftp->chdir($_SESSION['installer_temp_ftp']['path']);
206
207
			$ftp->unlink('install.php');
208
209
			foreach ($databases as $key => $dummy)
210
			{
211
				$type = ($key == 'mysqli') ? 'mysql' : $key;
212
				$ftp->unlink('install_' . $GLOBALS['db_script_version'] . '_' . $type . '.sql');
213
			}
214
215
			$ftp->close();
216
217
			unset($_SESSION['installer_temp_ftp']);
218
		}
219
		else
220
		{
221
			@unlink(__FILE__);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
222
223
			foreach ($databases as $key => $dummy)
224
			{
225
				$type = ($key == 'mysqli') ? 'mysql' : $key;
226
				@unlink(dirname(__FILE__) . '/install_' . $GLOBALS['db_script_version'] . '_' . $type . '.sql');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
227
			}
228
		}
229
230
		// Now just redirect to a blank.png...
231
		$secure = false;
232
233 View Code Duplication
		if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on')
234
			$secure = true;
235
		elseif (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https' || !empty($_SERVER['HTTP_X_FORWARDED_SSL']) && $_SERVER['HTTP_X_FORWARDED_SSL'] == 'on')
236
			$secure = true;
237
238
		header('location: http' . ($secure ? 's' : '') . '://' . (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : $_SERVER['SERVER_NAME'] . ':' . $_SERVER['SERVER_PORT']) . dirname($_SERVER['PHP_SELF']) . '/Themes/default/images/blank.png');
0 ignored issues
show
Security Response Splitting introduced by
'location: http' . ($sec...fault/images/blank.png' can contain request data and is used in response header context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Fetching key HTTP_HOST from $_SERVER
    in other/install.php on line 238

Response Splitting Attacks

Allowing an attacker to set a response header, opens your application to response splitting attacks; effectively allowing an attacker to send any response, he would like.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
239
		exit;
240
	}
241
242
	// PHP 5 might cry if we don't do this now.
243
	if (function_exists('date_default_timezone_set'))
244
	{
245
		// Get PHP's default timezone, if set
246
		$ini_tz = ini_get('date.timezone');
247
		if (!empty($ini_tz))
248
			$timezone_id = $ini_tz;
249
		else
250
			$timezone_id = '';
251
252
		// If date.timezone is unset, invalid, or just plain weird, make a best guess
253 View Code Duplication
		if (!in_array($timezone_id, timezone_identifiers_list()))
254
		{
255
			$server_offset = @mktime(0, 0, 0, 1, 1, 1970);
256
			$timezone_id = timezone_name_from_abbr('', $server_offset, 0);
257
		}
258
259
		date_default_timezone_set($timezone_id);
260
	}
261
262
	// Force an integer step, defaulting to 0.
263
	$_GET['step'] = (int) @$_GET['step'];
264
}
265
266
// Load the list of language files, and the current language file.
267
function load_lang_file()
268
{
269
	global $txt, $incontext, $user_info;
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...
270
271
	$incontext['detected_languages'] = array();
272
273
	// Make sure the languages directory actually exists.
274 View Code Duplication
	if (file_exists(dirname(__FILE__) . '/Themes/default/languages'))
275
	{
276
		// Find all the "Install" language files in the directory.
277
		$dir = dir(dirname(__FILE__) . '/Themes/default/languages');
278
		while ($entry = $dir->read())
279
		{
280
			if (substr($entry, 0, 8) == 'Install.' && substr($entry, -4) == '.php')
281
				$incontext['detected_languages'][$entry] = ucfirst(substr($entry, 8, strlen($entry) - 12));
282
		}
283
		$dir->close();
284
	}
285
286
	// Didn't find any, show an error message!
287 View Code Duplication
	if (empty($incontext['detected_languages']))
288
	{
289
		// Let's not cache this message, eh?
290
		header('expires: Mon, 26 Jul 1997 05:00:00 GMT');
291
		header('last-modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
292
		header('cache-control: no-cache');
293
294
		echo '<!DOCTYPE html>
295
<html>
296
	<head>
297
		<title>SMF Installer: Error!</title>
298
		<style>
299
			body {
300
				font-family: sans-serif;
301
				max-width: 700px; }
302
		
303
			h1 {
304
				font-size: 14pt; }
305
306
			.directory {
307
				margin: 0.3em;
308
				font-family: monospace;
309
				font-weight: bold; }
310
		</style>
311
	</head>
312
	<body>
313
		<h1>A critical error has occurred.</h1>
314
315
		<p>This installer was unable to find the installer\'s language file or files. They should be found under:</p>
316
317
		<div class="directory">', dirname($_SERVER['PHP_SELF']) != '/' ? dirname($_SERVER['PHP_SELF']) : '', '/Themes/default/languages</div>
0 ignored issues
show
Security Cross-Site Scripting introduced by
dirname($_SERVER['PHP_SE...ERVER['PHP_SELF']) : '' can contain request data and is used in output context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Fetching key PHP_SELF from $_SERVER, and $_SERVER['PHP_SELF'] is passed through dirname()
    in other/install.php on line 317

Preventing Cross-Site-Scripting Attacks

Cross-Site-Scripting allows an attacker to inject malicious code into your website - in particular Javascript code, and have that code executed with the privileges of a visiting user. This can be used to obtain data, or perform actions on behalf of that visiting user.

In order to prevent this, make sure to escape all user-provided data:

// for HTML
$sanitized = htmlentities($tainted, ENT_QUOTES);

// for URLs
$sanitized = urlencode($tainted);

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
318
319
		<p>In some cases, FTP clients do not properly upload files with this many folders. Please double check to make sure you <strong>have uploaded all the files in the distribution</strong>.</p>
320
		<p>If that doesn\'t help, please make sure this install.php file is in the same place as the Themes folder.</p>
321
		<p>If you continue to get this error message, feel free to <a href="https://support.simplemachines.org/">look to us for support</a>.</p>
322
	</div></body>
323
</html>';
324
		die;
325
	}
326
327
	// Override the language file?
328 View Code Duplication
	if (isset($_GET['lang_file']))
329
		$_SESSION['installer_temp_lang'] = $_GET['lang_file'];
330
	elseif (isset($GLOBALS['HTTP_GET_VARS']['lang_file']))
331
		$_SESSION['installer_temp_lang'] = $GLOBALS['HTTP_GET_VARS']['lang_file'];
332
333
	// Make sure it exists, if it doesn't reset it.
334
	if (!isset($_SESSION['installer_temp_lang']) || preg_match('~[^\\w_\\-.]~', $_SESSION['installer_temp_lang']) === 1 || !file_exists(dirname(__FILE__) . '/Themes/default/languages/' . $_SESSION['installer_temp_lang']))
335
	{
336
		// Use the first one...
337
		list ($_SESSION['installer_temp_lang']) = array_keys($incontext['detected_languages']);
338
339
		// If we have english and some other language, use the other language.  We Americans hate english :P.
340 View Code Duplication
		if ($_SESSION['installer_temp_lang'] == 'Install.english.php' && count($incontext['detected_languages']) > 1)
341
			list (, $_SESSION['installer_temp_lang']) = array_keys($incontext['detected_languages']);
342
	}
343
344
	// And now include the actual language file itself.
345
	require_once(dirname(__FILE__) . '/Themes/default/languages/' . $_SESSION['installer_temp_lang']);
346
347
	// Which language did we load? Assume that he likes his language.
348
	preg_match('~^Install\.(.+[^-utf8])\.php$~', $_SESSION['installer_temp_lang'], $matches);
349
	$user_info['language'] = $matches[1];
350
}
351
352
// This handy function loads some settings and the like.
353
function load_database()
354
{
355
	global $db_prefix, $db_connection, $sourcedir, $smcFunc, $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...
356
	global $db_server, $db_passwd, $db_type, $db_name, $db_user, $db_persist;
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...
357
358
	if (empty($sourcedir))
359
		$sourcedir = dirname(__FILE__) . '/Sources';
360
361
	// Need this to check whether we need the database password.
362
	require(dirname(__FILE__) . '/Settings.php');
363
	if (!defined('SMF'))
364
		define('SMF', 1);
365
	if (empty($smcFunc))
366
		$smcFunc = array();
367
368
	$modSettings['disableQueryCheck'] = true;
369
370
	// Connect the database.
371
	if (!$db_connection)
372
	{
373
		require_once($sourcedir . '/Subs-Db-' . $db_type . '.php');
374
		if (version_compare(PHP_VERSION, '5', '<'))
375
			require_once($sourcedir . '/Subs-Compat.php');
376
377
		$db_options = array('persist' => $db_persist);
378
		$port = '';
379
380
		// Figure out the port...
381
		if (!empty($_POST['db_port']))
382
		{
383
			if ($db_type == 'mysql')
384
			{
385
				$port = ((int) $_POST['db_port'] == ini_get($db_type . 'default_port')) ? '' : (int) $_POST['db_port'];
386
			}
387 View Code Duplication
			elseif ($db_type == 'postgresql')
388
			{
389
				// PostgreSQL doesn't have a default port setting in php.ini, so just check against the default
390
				$port = ((int) $_POST['db_port'] == 5432) ? '' : (int) $_POST['db_port'];
391
			}
392
		}
393
394
		if (!empty($port))
395
			$db_options['port'] = $port;
396
397
		if (!$db_connection)
398
			$db_connection = smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, $db_prefix, $db_options);
399
	}
400
}
401
402
// This is called upon exiting the installer, for template etc.
403
function installExit($fallThrough = false)
404
{
405
	global $incontext, $installurl, $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...
406
407
	// Send character set.
408
	header('content-type: text/html; charset=' . (isset($txt['lang_character_set']) ? $txt['lang_character_set'] : 'UTF-8'));
409
410
	// We usually dump our templates out.
411
	if (!$fallThrough)
412
	{
413
		// The top install bit.
414
		template_install_above();
415
416
		// Call the template.
417
		if (isset($incontext['sub_template']))
418
		{
419
			$incontext['form_url'] = $installurl . '?step=' . $incontext['current_step'];
420
421
			call_user_func('template_' . $incontext['sub_template']);
422
		}
423
		// @todo REMOVE THIS!!
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...
424
		else
425
		{
426
			if (function_exists('doStep' . $_GET['step']))
427
				call_user_func('doStep' . $_GET['step']);
0 ignored issues
show
Security Code Execution introduced by
'doStep' . $_GET['step'] can contain request data and is used in code execution context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Read from $_GET
    in other/install.php on line 427

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
428
		}
429
		// Show the footer.
430
		template_install_below();
431
	}
432
433
	// Bang - gone!
434
	die();
435
}
436
437
function Welcome()
438
{
439
	global $incontext, $txt, $databases, $installurl;
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...
440
441
	$incontext['page_title'] = $txt['install_welcome'];
442
	$incontext['sub_template'] = 'welcome_message';
443
444
	// Done the submission?
445
	if (isset($_POST['contbutt']))
446
		return true;
447
448
	// See if we think they have already installed it?
449
	if (is_readable(dirname(__FILE__) . '/Settings.php'))
450
	{
451
		$probably_installed = 0;
452
		foreach (file(dirname(__FILE__) . '/Settings.php') as $line)
453
		{
454
			if (preg_match('~^\$db_passwd\s=\s\'([^\']+)\';$~', $line))
455
				$probably_installed++;
456
			if (preg_match('~^\$boardurl\s=\s\'([^\']+)\';~', $line) && !preg_match('~^\$boardurl\s=\s\'http://127\.0\.0\.1/smf\';~', $line))
457
				$probably_installed++;
458
		}
459
460
		if ($probably_installed == 2)
461
			$incontext['warning'] = $txt['error_already_installed'];
462
	}
463
464
	// Is some database support even compiled in?
465
	$incontext['supported_databases'] = array();
466
	foreach ($databases as $key => $db)
467
	{
468
		if ($db['supported'])
469
		{
470
			$type = ($key == 'mysqli') ? 'mysql' : $key;
471
			if (!file_exists(dirname(__FILE__) . '/install_' . $GLOBALS['db_script_version'] . '_' . $type . '.sql'))
472
			{
473
				$databases[$key]['supported'] = false;
474
				$notFoundSQLFile = true;
475
				$txt['error_db_script_missing'] = sprintf($txt['error_db_script_missing'], 'install_' . $GLOBALS['db_script_version'] . '_' . $type . '.sql');
476
			}
477
			else
478
				$incontext['supported_databases'][] = $db;
479
		}
480
	}
481
482
	// Check the PHP version.
483
	if ((!function_exists('version_compare') || version_compare($GLOBALS['required_php_version'], PHP_VERSION, '>=')))
484
		$error = 'error_php_too_low';
485
	// Make sure we have a supported database
486
	elseif (empty($incontext['supported_databases']))
487
		$error = empty($notFoundSQLFile) ? 'error_db_missing' : 'error_db_script_missing';
488
	// How about session support?  Some crazy sysadmin remove it?
489
	elseif (!function_exists('session_start'))
490
		$error = 'error_session_missing';
491
	// Make sure they uploaded all the files.
492
	elseif (!file_exists(dirname(__FILE__) . '/index.php'))
493
		$error = 'error_missing_files';
494
	// Very simple check on the session.save_path for Windows.
495
	// @todo Move this down later if they don't use database-driven sessions?
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...
496
	elseif (@ini_get('session.save_path') == '/tmp' && substr(__FILE__, 1, 2) == ':\\')
497
		$error = 'error_session_save_path';
498
499
	// Since each of the three messages would look the same, anyway...
500
	if (isset($error))
501
		$incontext['error'] = $txt[$error];
502
503
	// Mod_security blocks everything that smells funny. Let SMF handle security.
504
	if (!fixModSecurity() && !isset($_GET['overmodsecurity']))
505
		$incontext['error'] = $txt['error_mod_security'] . '<br><br><a href="' . $installurl . '?overmodsecurity=true">' . $txt['error_message_click'] . '</a> ' . $txt['error_message_bad_try_again'];
506
507
	// Confirm mbstring is loaded...
508
	if (!extension_loaded('mbstring'))
509
		$incontext['error'] = $txt['install_no_mbstring'];
510
511
	// Check for https stream support.
512
	$supported_streams = stream_get_wrappers();
513
	if (!in_array('https', $supported_streams))
514
		$incontext['warning'] = $txt['install_no_https'];
515
516
	return false;
517
}
518
519
function CheckFilesWritable()
520
{
521
	global $txt, $incontext;
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...
522
523
	$incontext['page_title'] = $txt['ftp_checking_writable'];
524
	$incontext['sub_template'] = 'chmod_files';
525
526
	$writable_files = array(
527
		'attachments',
528
		'avatars',
529
		'custom_avatar',
530
		'cache',
531
		'Packages',
532
		'Smileys',
533
		'Themes',
534
		'agreement.txt',
535
		'Settings.php',
536
		'Settings_bak.php',
537
	);
538
539
	foreach ($incontext['detected_languages'] as $lang => $temp)
540
		$extra_files[] = 'Themes/default/languages/' . $lang;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$extra_files was never initialized. Although not strictly required by PHP, it is generally a good practice to add $extra_files = 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...
541
542
	// With mod_security installed, we could attempt to fix it with .htaccess.
543
	if (function_exists('apache_get_modules') && in_array('mod_security', apache_get_modules()))
544
		$writable_files[] = file_exists(dirname(__FILE__) . '/.htaccess') ? '.htaccess' : '.';
545
546
	$failed_files = array();
547
548
	// On linux, it's easy - just use is_writable!
549
	if (substr(__FILE__, 1, 2) != ':\\')
550
	{
551
		$incontext['systemos'] = 'linux';
552
553
		foreach ($writable_files as $file)
554
		{
555
			// Some files won't exist, try to address up front
556
			if (!file_exists(dirname(__FILE__) . '/' . $file))
557
				@touch(dirname(__FILE__) . '/' . $file);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
558
			// NOW do the writable check...
559
			if (!is_writable(dirname(__FILE__) . '/' . $file))
560
			{
561
				@chmod(dirname(__FILE__) . '/' . $file, 0755);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
562
563
				// Well, 755 hopefully worked... if not, try 777.
564
				if (!is_writable(dirname(__FILE__) . '/' . $file) && !@chmod(dirname(__FILE__) . '/' . $file, 0777))
565
					$failed_files[] = $file;
566
			}
567
		}
568 View Code Duplication
		foreach ($extra_files as $file)
0 ignored issues
show
Bug introduced by
The variable $extra_files 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...
569
			@chmod(dirname(__FILE__) . (empty($file) ? '' : '/' . $file), 0777);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
570
	}
571
	// Windows is trickier.  Let's try opening for r+...
572
	else
573
	{
574
		$incontext['systemos'] = 'windows';
575
576
		foreach ($writable_files as $file)
577
		{
578
			// Folders can't be opened for write... but the index.php in them can ;)
579
			if (is_dir(dirname(__FILE__) . '/' . $file))
580
				$file .= '/index.php';
581
582
			// Funny enough, chmod actually does do something on windows - it removes the read only attribute.
583
			@chmod(dirname(__FILE__) . '/' . $file, 0777);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
584
			$fp = @fopen(dirname(__FILE__) . '/' . $file, 'r+');
585
586
			// Hmm, okay, try just for write in that case...
587
			if (!is_resource($fp))
588
				$fp = @fopen(dirname(__FILE__) . '/' . $file, 'w');
589
590
			if (!is_resource($fp))
591
				$failed_files[] = $file;
592
593
			@fclose($fp);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
594
		}
595 View Code Duplication
		foreach ($extra_files as $file)
596
			@chmod(dirname(__FILE__) . (empty($file) ? '' : '/' . $file), 0777);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
597
	}
598
599
	$failure = count($failed_files) >= 1;
600
601
	if (!isset($_SERVER))
602
		return !$failure;
603
604
	// Put the list into context.
605
	$incontext['failed_files'] = $failed_files;
606
607
	// It's not going to be possible to use FTP on windows to solve the problem...
608
	if ($failure && substr(__FILE__, 1, 2) == ':\\')
609
	{
610
		$incontext['error'] = $txt['error_windows_chmod'] . '
611
					<ul class="error_content">
612
						<li>' . implode('</li>
613
						<li>', $failed_files) . '</li>
614
					</ul>';
615
616
		return false;
617
	}
618
	// We're going to have to use... FTP!
619
	elseif ($failure)
620
	{
621
		// Load any session data we might have...
622 View Code Duplication
		if (!isset($_POST['ftp_username']) && isset($_SESSION['installer_temp_ftp']))
623
		{
624
			$_POST['ftp_server'] = $_SESSION['installer_temp_ftp']['server'];
625
			$_POST['ftp_port'] = $_SESSION['installer_temp_ftp']['port'];
626
			$_POST['ftp_username'] = $_SESSION['installer_temp_ftp']['username'];
627
			$_POST['ftp_password'] = $_SESSION['installer_temp_ftp']['password'];
628
			$_POST['ftp_path'] = $_SESSION['installer_temp_ftp']['path'];
629
		}
630
631
		$incontext['ftp_errors'] = array();
632
		require_once('Sources/Class-Package.php');
633 View Code Duplication
		if (isset($_POST['ftp_username']))
634
		{
635
			$ftp = new ftp_connection($_POST['ftp_server'], $_POST['ftp_port'], $_POST['ftp_username'], $_POST['ftp_password']);
636
637
			if ($ftp->error === false)
638
			{
639
				// Try it without /home/abc just in case they messed up.
640
				if (!$ftp->chdir($_POST['ftp_path']))
641
				{
642
					$incontext['ftp_errors'][] = $ftp->last_message;
643
					$ftp->chdir(preg_replace('~^/home[2]?/[^/]+?~', '', $_POST['ftp_path']));
644
				}
645
			}
646
		}
647
648
		if (!isset($ftp) || $ftp->error !== false)
649
		{
650
			if (!isset($ftp))
651
				$ftp = new ftp_connection(null);
652
			// Save the error so we can mess with listing...
653
			elseif ($ftp->error !== false && empty($incontext['ftp_errors']) && !empty($ftp->last_message))
654
				$incontext['ftp_errors'][] = $ftp->last_message;
655
656
			list ($username, $detect_path, $found_path) = $ftp->detect_path(dirname(__FILE__));
657
658
			if (empty($_POST['ftp_path']) && $found_path)
659
				$_POST['ftp_path'] = $detect_path;
660
661
			if (!isset($_POST['ftp_username']))
662
				$_POST['ftp_username'] = $username;
663
664
			// Set the username etc, into context.
665
			$incontext['ftp'] = array(
666
				'server' => isset($_POST['ftp_server']) ? $_POST['ftp_server'] : 'localhost',
667
				'port' => isset($_POST['ftp_port']) ? $_POST['ftp_port'] : '21',
668
				'username' => isset($_POST['ftp_username']) ? $_POST['ftp_username'] : '',
669
				'path' => isset($_POST['ftp_path']) ? $_POST['ftp_path'] : '/',
670
				'path_msg' => !empty($found_path) ? $txt['ftp_path_found_info'] : $txt['ftp_path_info'],
671
			);
672
673
			return false;
674
		}
675
		else
676
		{
677
			$_SESSION['installer_temp_ftp'] = array(
678
				'server' => $_POST['ftp_server'],
679
				'port' => $_POST['ftp_port'],
680
				'username' => $_POST['ftp_username'],
681
				'password' => $_POST['ftp_password'],
682
				'path' => $_POST['ftp_path']
683
			);
684
685
			$failed_files_updated = array();
686
687
			foreach ($failed_files as $file)
688
			{
689
				if (!is_writable(dirname(__FILE__) . '/' . $file))
690
					$ftp->chmod($file, 0755);
691
				if (!is_writable(dirname(__FILE__) . '/' . $file))
692
					$ftp->chmod($file, 0777);
693
				if (!is_writable(dirname(__FILE__) . '/' . $file))
694
				{
695
					$failed_files_updated[] = $file;
696
					$incontext['ftp_errors'][] = rtrim($ftp->last_message) . ' -> ' . $file . "\n";
697
				}
698
			}
699
700
			$ftp->close();
701
702
			// Are there any errors left?
703
			if (count($failed_files_updated) >= 1)
704
			{
705
				// Guess there are...
706
				$incontext['failed_files'] = $failed_files_updated;
707
708
				// Set the username etc, into context.
709
				$incontext['ftp'] = $_SESSION['installer_temp_ftp'] += array(
710
					'path_msg' => $txt['ftp_path_info'],
711
				);
712
713
				return false;
714
			}
715
		}
716
	}
717
718
	return true;
719
}
720
721
function DatabaseSettings()
722
{
723
	global $txt, $databases, $incontext, $smcFunc, $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...
724
	global $db_server, $db_name, $db_user, $db_passwd;
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...
725
726
	$incontext['sub_template'] = 'database_settings';
727
	$incontext['page_title'] = $txt['db_settings'];
728
	$incontext['continue'] = 1;
729
730
	// Set up the defaults.
731
	$incontext['db']['server'] = 'localhost';
732
	$incontext['db']['user'] = '';
733
	$incontext['db']['name'] = '';
734
	$incontext['db']['pass'] = '';
735
	$incontext['db']['type'] = '';
736
	$incontext['supported_databases'] = array();
737
738
	$foundOne = false;
739
	foreach ($databases as $key => $db)
740
	{
741
		// Override with the defaults for this DB if appropriate.
742
		if ($db['supported'])
743
		{
744
			$incontext['supported_databases'][$key] = $db;
745
746
			if (!$foundOne)
747
			{
748
				if (isset($db['default_host']))
749
					$incontext['db']['server'] = ini_get($db['default_host']) or $incontext['db']['server'] = 'localhost';
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as or instead of || is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
750
				if (isset($db['default_user']))
751
				{
752
					$incontext['db']['user'] = ini_get($db['default_user']);
753
					$incontext['db']['name'] = ini_get($db['default_user']);
754
				}
755
				if (isset($db['default_password']))
756
					$incontext['db']['pass'] = ini_get($db['default_password']);
757
758
				// For simplicity and less confusion, leave the port blank by default
759
				$incontext['db']['port'] = '';
760
761
				$incontext['db']['type'] = $key;
762
				$foundOne = true;
763
			}
764
		}
765
	}
766
767
	// Override for repost.
768
	if (isset($_POST['db_user']))
769
	{
770
		$incontext['db']['user'] = $_POST['db_user'];
771
		$incontext['db']['name'] = $_POST['db_name'];
772
		$incontext['db']['server'] = $_POST['db_server'];
773
		$incontext['db']['prefix'] = $_POST['db_prefix'];
774
775
		if (!empty($_POST['db_port']))
776
			$incontext['db']['port'] = $_POST['db_port'];
777
	}
778
	else
779
	{
780
		$incontext['db']['prefix'] = 'smf_';
781
	}
782
783
	// Are we submitting?
784
	if (isset($_POST['db_type']))
785
	{
786
		// What type are they trying?
787
		$db_type = preg_replace('~[^A-Za-z0-9]~', '', $_POST['db_type']);
788
		$db_prefix = $_POST['db_prefix'];
789
		// Validate the prefix.
790
		$valid_prefix = $databases[$db_type]['validate_prefix']($db_prefix);
791
792
		if ($valid_prefix !== true)
793
		{
794
			$incontext['error'] = $valid_prefix;
795
			return false;
796
		}
797
798
		// Take care of these variables...
799
		$vars = array(
800
			'db_type' => $db_type,
801
			'db_name' => $_POST['db_name'],
802
			'db_user' => $_POST['db_user'],
803
			'db_passwd' => isset($_POST['db_passwd']) ? $_POST['db_passwd'] : '',
804
			'db_server' => $_POST['db_server'],
805
			'db_prefix' => $db_prefix,
806
			// The cookiename is special; we want it to be the same if it ever needs to be reinstalled with the same info.
807
			'cookiename' => 'SMFCookie' . abs(crc32($_POST['db_name'] . preg_replace('~[^A-Za-z0-9_$]~', '', $_POST['db_prefix'])) % 1000),
808
		);
809
810
		// Only set the port if we're not using the default
811
		if (!empty($_POST['db_port']))
812
		{
813
			// For MySQL, we can get the "default port" from PHP. PostgreSQL has no such option though.
814
			if (($db_type == 'mysql' || $db_type == 'mysqli') && $_POST['db_port'] != ini_get($db_type . '.default_port'))
815
				$vars['db_port'] = (int) $_POST['db_port'];
816 View Code Duplication
			elseif ($db_type == 'postgresql' && $_POST['db_port'] != 5432)
817
				$vars['db_port'] = (int) $_POST['db_port'];
818
		}
819
820
		// God I hope it saved!
821 View Code Duplication
		if (!updateSettingsFile($vars) && substr(__FILE__, 1, 2) == ':\\')
822
		{
823
			$incontext['error'] = $txt['error_windows_chmod'];
824
			return false;
825
		}
826
827
		// Make sure it works.
828
		require(dirname(__FILE__) . '/Settings.php');
829
830
		if (empty($sourcedir))
831
			$sourcedir = dirname(__FILE__) . '/Sources';
832
833
		// Better find the database file!
834
		if (!file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php'))
835
		{
836
			$incontext['error'] = sprintf($txt['error_db_file'], 'Subs-Db-' . $db_type . '.php');
837
			return false;
838
		}
839
840
		// Now include it for database functions!
841
		if (!defined('SMF'))
842
			define('SMF', 1);
843
844
		$modSettings['disableQueryCheck'] = true;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$modSettings was never initialized. Although not strictly required by PHP, it is generally a good practice to add $modSettings = 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...
845
		if (empty($smcFunc))
846
			$smcFunc = array();
847
848
			require_once($sourcedir . '/Subs-Db-' . $db_type . '.php');
849
850
		// What - running PHP4? The shame!
851
		if (version_compare(PHP_VERSION, '5', '<'))
852
			require_once($sourcedir . '/Subs-Compat.php');
853
854
		// Attempt a connection.
855
		$needsDB = !empty($databases[$db_type]['always_has_db']);
856
		$db_connection = smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, $db_prefix, array('non_fatal' => true, 'dont_select_db' => !$needsDB));
857
858
		// No dice?  Let's try adding the prefix they specified, just in case they misread the instructions ;)
859
		if ($db_connection == null)
860
		{
861
			$db_error = @$smcFunc['db_error']();
862
863
			$db_connection = smf_db_initiate($db_server, $db_name, $_POST['db_prefix'] . $db_user, $db_passwd, $db_prefix, array('non_fatal' => true, 'dont_select_db' => !$needsDB));
864
			if ($db_connection != null)
865
			{
866
				$db_user = $_POST['db_prefix'] . $db_user;
867
				updateSettingsFile(array('db_user' => $db_user));
868
			}
869
		}
870
871
		// Still no connection?  Big fat error message :P.
872
		if (!$db_connection)
873
		{
874
			$incontext['error'] = $txt['error_db_connect'] . '<div class="error_content"><strong>' . $db_error . '</strong></div>';
0 ignored issues
show
Bug introduced by
The variable $db_error 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...
875
			return false;
876
		}
877
878
		// Do they meet the install requirements?
879
		// @todo Old client, new server?
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...
880
		if (version_compare($databases[$db_type]['version'], preg_replace('~^\D*|\-.+?$~', '', eval($databases[$db_type]['version_check']))) > 0)
0 ignored issues
show
Coding Style introduced by
The function DatabaseSettings() contains an eval expression.

On one hand, eval might be exploited by malicious users if they somehow manage to inject dynamic content. On the other hand, with the emergence of faster PHP runtimes like the HHVM, eval prevents some optimization that they perform.

Loading history...
881
		{
882
			$incontext['error'] = $txt['error_db_too_low'];
883
			return false;
884
		}
885
886
		// Let's try that database on for size... assuming we haven't already lost the opportunity.
887
		if ($db_name != '' && !$needsDB)
888
		{
889
			$smcFunc['db_query']('', "
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $db_name instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
890
				CREATE DATABASE IF NOT EXISTS `$db_name`",
891
				array(
892
					'security_override' => true,
893
					'db_error_skip' => true,
894
				),
895
				$db_connection
896
			);
897
898
			// Okay, let's try the prefix if it didn't work...
899
			if (!$smcFunc['db_select_db']($db_name, $db_connection) && $db_name != '')
900
			{
901
				$smcFunc['db_query']('', "
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $_POST instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $db_name instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
902
					CREATE DATABASE IF NOT EXISTS `$_POST[db_prefix]$db_name`",
903
					array(
904
						'security_override' => true,
905
						'db_error_skip' => true,
906
					),
907
					$db_connection
908
				);
909
910
				if ($smcFunc['db_select_db']($_POST['db_prefix'] . $db_name, $db_connection))
911
				{
912
					$db_name = $_POST['db_prefix'] . $db_name;
913
					updateSettingsFile(array('db_name' => $db_name));
914
				}
915
			}
916
917
			// Okay, now let's try to connect...
918
			if (!$smcFunc['db_select_db']($db_name, $db_connection))
919
			{
920
				$incontext['error'] = sprintf($txt['error_db_database'], $db_name);
921
				return false;
922
			}
923
		}
924
925
		return true;
926
	}
927
928
	return false;
929
}
930
931
// Let's start with basic forum type settings.
932
function ForumSettings()
933
{
934
	global $txt, $incontext, $databases, $db_type, $db_connection;
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...
935
936
	$incontext['sub_template'] = 'forum_settings';
937
	$incontext['page_title'] = $txt['install_settings'];
938
939
	// Let's see if we got the database type correct.
940
	if (isset($_POST['db_type'], $databases[$_POST['db_type']]))
941
		$db_type = $_POST['db_type'];
942
943
	// Else we'd better be able to get the connection.
944
	else
945
		load_database();
946
947
	$db_type = isset($_POST['db_type']) ? $_POST['db_type'] : $db_type;
948
949
	// What host and port are we on?
950
	$host = empty($_SERVER['HTTP_HOST']) ? $_SERVER['SERVER_NAME'] . (empty($_SERVER['SERVER_PORT']) || $_SERVER['SERVER_PORT'] == '80' ? '' : ':' . $_SERVER['SERVER_PORT']) : $_SERVER['HTTP_HOST'];
951
952
		$secure = false;
953
954 View Code Duplication
		if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on')
955
			$secure = true;
956
		elseif (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https' || !empty($_SERVER['HTTP_X_FORWARDED_SSL']) && $_SERVER['HTTP_X_FORWARDED_SSL'] == 'on')
957
			$secure = true;
958
959
	// Now, to put what we've learned together... and add a path.
960
	$incontext['detected_url'] = 'http' . ($secure ? 's' : '') . '://' . $host . substr($_SERVER['PHP_SELF'], 0, strrpos($_SERVER['PHP_SELF'], '/'));
961
962
	// Check if the database sessions will even work.
963
	$incontext['test_dbsession'] = (ini_get('session.auto_start') != 1);
964
	$incontext['utf8_default'] = $databases[$db_type]['utf8_default'];
965
	$incontext['utf8_required'] = $databases[$db_type]['utf8_required'];
966
967
	$incontext['continue'] = 1;
968
969
	// Setup the SSL checkbox...
970
	$incontext['ssl_chkbx_protected'] = false;
971
	$incontext['ssl_chkbx_checked'] = false;
972
973
	// If redirect in effect, force ssl ON
974
	require_once(dirname(__FILE__) . '/Sources/Subs.php');
975
	if (https_redirect_active($incontext['detected_url'])) {
976
		$incontext['ssl_chkbx_protected'] = true;
977
		$incontext['ssl_chkbx_checked'] = true;
978
		$_POST['force_ssl'] = true;
979
	}
980
	// If no cert, make sure ssl stays OFF
981
	if (!ssl_cert_found($incontext['detected_url'])) {
982
		$incontext['ssl_chkbx_protected'] = true;
983
		$incontext['ssl_chkbx_checked'] = false;
984
	}
985
986
	// Submitting?
987
	if (isset($_POST['boardurl']))
988
	{
989 View Code Duplication
		if (substr($_POST['boardurl'], -10) == '/index.php')
990
			$_POST['boardurl'] = substr($_POST['boardurl'], 0, -10);
991
		elseif (substr($_POST['boardurl'], -1) == '/')
992
			$_POST['boardurl'] = substr($_POST['boardurl'], 0, -1);
993 View Code Duplication
		if (substr($_POST['boardurl'], 0, 7) != 'http://' && substr($_POST['boardurl'], 0, 7) != 'file://' && substr($_POST['boardurl'], 0, 8) != 'https://')
994
			$_POST['boardurl'] = 'http://' . $_POST['boardurl'];
995
996
		//Make sure boardurl is aligned with ssl setting
997
		if (empty($_POST['force_ssl']))
998
			$_POST['boardurl'] = strtr($_POST['boardurl'], array('https://' => 'http://'));
999
		else
1000
			$_POST['boardurl'] = strtr($_POST['boardurl'], array('http://' => 'https://'));
1001
1002
		// Save these variables.
1003
		$vars = array(
1004
			'boardurl' => $_POST['boardurl'],
1005
			'boarddir' => addslashes(dirname(__FILE__)),
1006
			'sourcedir' => addslashes(dirname(__FILE__)) . '/Sources',
1007
			'cachedir' => addslashes(dirname(__FILE__)) . '/cache',
1008
			'packagesdir' => addslashes(dirname(__FILE__)) . '/Packages',
1009
			'tasksdir' => addslashes(dirname(__FILE__)) . '/Sources/tasks',
1010
			'mbname' => strtr($_POST['mbname'], array('\"' => '"')),
1011
			'language' => substr($_SESSION['installer_temp_lang'], 8, -4),
1012
			'image_proxy_secret' => substr(sha1(mt_rand()), 0, 20),
1013
			'image_proxy_enabled' => !empty($_POST['force_ssl']),
1014
		);
1015
1016
		// Must save!
1017 View Code Duplication
		if (!updateSettingsFile($vars) && substr(__FILE__, 1, 2) == ':\\')
1018
		{
1019
			$incontext['error'] = $txt['error_windows_chmod'];
1020
			return false;
1021
		}
1022
1023
		// Make sure it works.
1024
		require(dirname(__FILE__) . '/Settings.php');
1025
1026
		// UTF-8 requires a setting to override the language charset.
1027
		if ((!empty($databases[$db_type]['utf8_support']) && !empty($databases[$db_type]['utf8_required'])) || (empty($databases[$db_type]['utf8_required']) && !empty($databases[$db_type]['utf8_support']) && isset($_POST['utf8'])))
1028
		{
1029
			if (!$databases[$db_type]['utf8_support']())
1030
			{
1031
				$incontext['error'] = sprintf($txt['error_utf8_support']);
1032
				return false;
1033
			}
1034
1035
			if (!empty($databases[$db_type]['utf8_version_check']) && version_compare($databases[$db_type]['utf8_version'], preg_replace('~\-.+?$~', '', eval($databases[$db_type]['utf8_version_check'])), '>'))
0 ignored issues
show
Coding Style introduced by
The function ForumSettings() contains an eval expression.

On one hand, eval might be exploited by malicious users if they somehow manage to inject dynamic content. On the other hand, with the emergence of faster PHP runtimes like the HHVM, eval prevents some optimization that they perform.

Loading history...
1036
			{
1037
				$incontext['error'] = sprintf($txt['error_utf8_version'], $databases[$db_type]['utf8_version']);
1038
				return false;
1039
			}
1040
			else
1041
				// Set the character set here.
1042
				updateSettingsFile(array('db_character_set' => 'utf8'));
1043
		}
1044
1045
		// Good, skip on.
1046
		return true;
1047
	}
1048
1049
	return false;
1050
}
1051
1052
// Step one: Do the SQL thang.
1053
function DatabasePopulation()
1054
{
1055
	global $db_character_set, $txt, $db_connection, $smcFunc, $databases, $modSettings, $db_type, $db_prefix, $incontext, $db_name, $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...
1056
1057
	$incontext['sub_template'] = 'populate_database';
1058
	$incontext['page_title'] = $txt['db_populate'];
1059
	$incontext['continue'] = 1;
1060
1061
	// Already done?
1062
	if (isset($_POST['pop_done']))
1063
		return true;
1064
1065
	// Reload settings.
1066
	require(dirname(__FILE__) . '/Settings.php');
1067
	load_database();
1068
1069
	// Before running any of the queries, let's make sure another version isn't already installed.
1070
	$result = $smcFunc['db_query']('', '
1071
		SELECT variable, value
1072
		FROM {db_prefix}settings',
1073
		array(
1074
			'db_error_skip' => true,
1075
		)
1076
	);
1077
	$newSettings = array();
1078
	$modSettings = array();
1079
	if ($result !== false)
1080
	{
1081 View Code Duplication
		while ($row = $smcFunc['db_fetch_assoc']($result))
1082
			$modSettings[$row['variable']] = $row['value'];
1083
		$smcFunc['db_free_result']($result);
1084
1085
		// Do they match?  If so, this is just a refresh so charge on!
1086
		if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] != $GLOBALS['current_smf_version'])
1087
		{
1088
			$incontext['error'] = $txt['error_versions_do_not_match'];
1089
			return false;
1090
		}
1091
	}
1092
	$modSettings['disableQueryCheck'] = true;
1093
1094
	// If doing UTF8, select it. PostgreSQL requires passing it as a string...
1095 View Code Duplication
	if (!empty($db_character_set) && $db_character_set == 'utf8' && !empty($databases[$db_type]['utf8_support']))
1096
		$smcFunc['db_query']('', '
1097
			SET NAMES {string:utf8}',
1098
			array(
1099
				'db_error_skip' => true,
1100
				'utf8' => 'utf8',
1101
			)
1102
		);
1103
1104
	// Windows likes to leave the trailing slash, which yields to C:\path\to\SMF\/attachments...
1105
	if (substr(__DIR__, -1) == '\\')
1106
		$attachdir = __DIR__ . 'attachments';
1107
	else
1108
		$attachdir = __DIR__ . '/attachments';
1109
1110
	$replaces = array(
1111
		'{$db_prefix}' => $db_prefix,
1112
		'{$attachdir}' => json_encode(array(1 => $smcFunc['db_escape_string']($attachdir))),
1113
		'{$boarddir}' => $smcFunc['db_escape_string'](dirname(__FILE__)),
1114
		'{$boardurl}' => $boardurl,
1115
		'{$enableCompressedOutput}' => isset($_POST['compress']) ? '1' : '0',
1116
		'{$databaseSession_enable}' => isset($_POST['dbsession']) ? '1' : '0',
1117
		'{$smf_version}' => $GLOBALS['current_smf_version'],
1118
		'{$current_time}' => time(),
1119
		'{$sched_task_offset}' => 82800 + mt_rand(0, 86399),
1120
		'{$registration_method}' => isset($_POST['reg_mode']) ? $_POST['reg_mode'] : 0,
1121
	);
1122
1123
	foreach ($txt as $key => $value)
1124
	{
1125
		if (substr($key, 0, 8) == 'default_')
1126
			$replaces['{$' . $key . '}'] = $smcFunc['db_escape_string']($value);
1127
	}
1128
	$replaces['{$default_reserved_names}'] = strtr($replaces['{$default_reserved_names}'], array('\\\\n' => '\\n'));
1129
1130
	// MySQL-specific stuff - storage engine and UTF8 handling
1131
	if (substr($db_type, 0, 5) == 'mysql')
1132
	{
1133
		// Just in case the query fails for some reason...
1134
		$engines = array();
1135
1136
		// Figure out storage engines - what do we have, etc.
1137
		$get_engines = $smcFunc['db_query']('', 'SHOW ENGINES', array());
1138
1139 View Code Duplication
		while ($row = $smcFunc['db_fetch_assoc']($get_engines))
1140
		{
1141
			if ($row['Support'] == 'YES' || $row['Support'] == 'DEFAULT')
1142
				$engines[] = $row['Engine'];
1143
		}
1144
1145
		// Done with this now
1146
		$smcFunc['db_free_result']($get_engines);
1147
1148
		// InnoDB is better, so use it if possible...
1149
		$has_innodb = in_array('InnoDB', $engines);
1150
		$replaces['{$engine}'] = $has_innodb ? 'InnoDB' : 'MyISAM';
1151
		$replaces['{$memory}'] = (!$has_innodb && in_array('MEMORY', $engines)) ? 'MEMORY' : $replaces['{$engine}'];
1152
1153
		// If the UTF-8 setting was enabled, add it to the table definitions.
1154
		if (!empty($databases[$db_type]['utf8_support']) && (!empty($databases[$db_type]['utf8_required']) || isset($_POST['utf8'])))
1155
		{
1156
			$replaces['{$engine}'] .= ' DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci';
1157
			$replaces['{$memory}'] .= ' DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci';
1158
		}
1159
1160
		// One last thing - if we don't have InnoDB, we can't do transactions...
1161
		if (!$has_innodb)
1162
		{
1163
			$replaces['START TRANSACTION;'] = '';
1164
			$replaces['COMMIT;'] = '';
1165
		}
1166
	}
1167
	else
1168
	{
1169
		$has_innodb = false;
1170
	}
1171
1172
	// Read in the SQL.  Turn this on and that off... internationalize... etc.
1173
	$type = ($db_type == 'mysqli' ? 'mysql' : $db_type);
1174
	$sql_lines = explode("\n", strtr(implode(' ', file(dirname(__FILE__) . '/install_' . $GLOBALS['db_script_version'] . '_' . $type . '.sql')), $replaces));
1175
1176
	// Execute the SQL.
1177
	$current_statement = '';
1178
	$exists = array();
1179
	$incontext['failures'] = array();
1180
	$incontext['sql_results'] = array(
1181
		'tables' => 0,
1182
		'inserts' => 0,
1183
		'table_dups' => 0,
1184
		'insert_dups' => 0,
1185
	);
1186
	foreach ($sql_lines as $count => $line)
1187
	{
1188
		// No comments allowed!
1189
		if (substr(trim($line), 0, 1) != '#')
1190
			$current_statement .= "\n" . rtrim($line);
1191
1192
		// Is this the end of the query string?
1193
		if (empty($current_statement) || (preg_match('~;[\s]*$~s', $line) == 0 && $count != count($sql_lines)))
1194
			continue;
1195
1196
		// Does this table already exist?  If so, don't insert more data into it!
1197
		if (preg_match('~^\s*INSERT INTO ([^\s\n\r]+?)~', $current_statement, $match) != 0 && in_array($match[1], $exists))
1198
		{
1199
			preg_match_all('~\)[,;]~', $current_statement, $matches);
1200 View Code Duplication
			if (!empty($matches[0]))
1201
				$incontext['sql_results']['insert_dups'] += count($matches[0]);
1202
			else
1203
				$incontext['sql_results']['insert_dups']++;
1204
1205
			$current_statement = '';
1206
			continue;
1207
		}
1208
1209
		if ($smcFunc['db_query']('', $current_statement, array('security_override' => true, 'db_error_skip' => true), $db_connection) === false)
1210
		{
1211
			// Use the appropriate function based on the DB type
1212
			if ($db_type == 'mysql' || $db_type == 'mysqli')
1213
				$db_errorno = $db_type . '_errno';
1214
1215
			// Error 1050: Table already exists!
1216
			// @todo Needs to be made better!
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...
1217
			if ((($db_type != 'mysql' && $db_type != 'mysqli') || $db_errorno($db_connection) == 1050) && preg_match('~^\s*CREATE TABLE ([^\s\n\r]+?)~', $current_statement, $match) == 1)
0 ignored issues
show
Bug introduced by
The variable $db_errorno 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...
1218
			{
1219
				$exists[] = $match[1];
1220
				$incontext['sql_results']['table_dups']++;
1221
			}
1222
			// Don't error on duplicate indexes (or duplicate operators in PostgreSQL.)
1223
			elseif (!preg_match('~^\s*CREATE( UNIQUE)? INDEX ([^\n\r]+?)~', $current_statement, $match) && !($db_type == 'postgresql' && preg_match('~^\s*CREATE OPERATOR (^\n\r]+?)~', $current_statement, $match)))
1224
			{
1225
				// MySQLi requires a connection object. It's optional with MySQL and Postgres
1226
				$incontext['failures'][$count] = $smcFunc['db_error']($db_connection);
1227
			}
1228
		}
1229
		else
1230
		{
1231
			if (preg_match('~^\s*CREATE TABLE ([^\s\n\r]+?)~', $current_statement, $match) == 1)
1232
				$incontext['sql_results']['tables']++;
1233
			elseif (preg_match('~^\s*INSERT INTO ([^\s\n\r]+?)~', $current_statement, $match) == 1)
1234
			{
1235
				preg_match_all('~\)[,;]~', $current_statement, $matches);
1236 View Code Duplication
				if (!empty($matches[0]))
1237
					$incontext['sql_results']['inserts'] += count($matches[0]);
1238
				else
1239
					$incontext['sql_results']['inserts']++;
1240
			}
1241
		}
1242
1243
		$current_statement = '';
1244
1245
		// Wait, wait, I'm still working here!
1246
		set_time_limit(60);
1247
	}
1248
1249
	// Sort out the context for the SQL.
1250
	foreach ($incontext['sql_results'] as $key => $number)
1251
	{
1252
		if ($number == 0)
1253
			unset($incontext['sql_results'][$key]);
1254
		else
1255
			$incontext['sql_results'][$key] = sprintf($txt['db_populate_' . $key], $number);
1256
	}
1257
1258
	// Make sure UTF will be used globally.
1259
	if ((!empty($databases[$db_type]['utf8_support']) && !empty($databases[$db_type]['utf8_required'])) || (empty($databases[$db_type]['utf8_required']) && !empty($databases[$db_type]['utf8_support']) && isset($_POST['utf8'])))
1260
		$newSettings[] = array('global_character_set', 'UTF-8');
1261
1262
	// Auto-detect local & global cookie settings
1263
	$url_parts = parse_url($boardurl);
1264
	if ($url_parts !== false)
1265
	{
1266
		unset($globalCookies, $globalCookiesDomain, $localCookies);
1267
1268
		// Look for subdomain, if found, set globalCookie settings
1269
		// Don't bother looking if you have an ip address for host
1270
		if (!empty($url_parts['host']) && (filter_var($url_parts['host'], FILTER_VALIDATE_IP) === false))
1271
		{
1272
			// www isn't really a subdomain in this sense, so strip it out
1273
			$url_parts['host'] = str_ireplace('www.', '', $url_parts['host']);
1274
			$pos1 = strrpos($url_parts['host'], '.');
1275
			if ($pos1 !== false)
1276
			{
1277
				// 2nd period from the right indicates you have a subdomain
1278
				$pos2 = strrpos(substr($url_parts['host'], 0, $pos1 - 1), '.');
1279
				if ($pos2 !== false)
1280
				{
1281
					$globalCookies = '1';
1282
					$globalCookiesDomain = substr($url_parts['host'], $pos2 + 1);
1283
				}
1284
			}
1285
		}
1286
1287
		// Look for subfolder, if found, set localCookie
1288
		// Checking for len > 1 ensures you don't have just a slash...
1289
		if (!empty($url_parts['path']) && strlen($url_parts['path']) > 1)
1290
			$localCookies = '1';
1291
1292
		if (isset($globalCookies))
1293
			$newSettings[] = array('globalCookies', $globalCookies);
1294
		if (isset($globalCookiesDomain))
1295
			$newSettings[] = array('globalCookiesDomain', $globalCookiesDomain);
1296
		if (isset($localCookies))
1297
			$newSettings[] = array('localCookies', $localCookies);
1298
	}
1299
1300
	// Are we allowing stat collection?
1301
	if (!empty($_POST['stats']) && substr($boardurl, 0, 16) != 'http://localhost' && empty($modSettings['allow_sm_stats']) && empty($modSettings['enable_sm_stats']))
1302
	{
1303
		$upcontext['allow_sm_stats'] = true;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$upcontext was never initialized. Although not strictly required by PHP, it is generally a good practice to add $upcontext = 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...
1304
1305
		// Attempt to register the site etc.
1306
		$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...
1307 View Code Duplication
		if ($fp)
1308
		{
1309
			$out = 'GET /smf/stats/register_stats.php?site=' . base64_encode($boardurl) . ' HTTP/1.1' . "\r\n";
1310
			$out .= 'Host: www.simplemachines.org' . "\r\n";
1311
			$out .= 'Connection: Close' . "\r\n\r\n";
1312
			fwrite($fp, $out);
1313
1314
			$return_data = '';
1315
			while (!feof($fp))
1316
				$return_data .= fgets($fp, 128);
1317
1318
			fclose($fp);
1319
1320
			// Get the unique site ID.
1321
			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...
1322
1323
			if (!empty($ID[1]))
1324
				$smcFunc['db_insert']('replace',
1325
					$db_prefix . 'settings',
1326
					array('variable' => 'string', 'value' => 'string'),
1327
					array(
1328
						array('sm_stats_key', $ID[1]),
1329
						array('enable_sm_stats', 1),
1330
					),
1331
					array('variable')
1332
				);
1333
		}
1334
	}
1335
	// Don't remove stat collection unless we unchecked the box for real, not from the loop.
1336 View Code Duplication
	elseif (empty($_POST['stats']) && empty($upcontext['allow_sm_stats']))
0 ignored issues
show
Bug introduced by
The variable $upcontext seems only to be defined at a later point. As such the call to empty() seems to always evaluate to true.

This check marks calls to isset(...) or empty(...) that are found before the variable itself is defined. These will always have the same result.

This is likely the result of code being shifted around. Consider removing these calls.

Loading history...
1337
		$smcFunc['db_query']('', '
1338
			DELETE FROM {db_prefix}settings
1339
			WHERE variable = {string:enable_sm_stats}',
1340
			array(
1341
				'enable_sm_stats' => 'enable_sm_stats',
1342
				'db_error_skip' => true,
1343
			)
1344
		);
1345
1346
	// Are we enabling SSL?
1347
	if (!empty($_POST['force_ssl']))
1348
		$newSettings[] = array('force_ssl', 1);
1349
1350
	// Setting a timezone is required.
1351
	if (!isset($modSettings['default_timezone']) && function_exists('date_default_timezone_set'))
1352
	{
1353
		// Get PHP's default timezone, if set
1354
		$ini_tz = ini_get('date.timezone');
1355
		if (!empty($ini_tz))
1356
			$timezone_id = $ini_tz;
1357
		else
1358
			$timezone_id = '';
1359
1360
		// If date.timezone is unset, invalid, or just plain weird, make a best guess
1361 View Code Duplication
		if (!in_array($timezone_id, timezone_identifiers_list()))
1362
		{
1363
			$server_offset = @mktime(0, 0, 0, 1, 1, 1970);
1364
			$timezone_id = timezone_name_from_abbr('', $server_offset, 0);
1365
		}
1366
1367
		if (date_default_timezone_set($timezone_id))
1368
			$newSettings[] = array('default_timezone', $timezone_id);
1369
	}
1370
1371
	if (!empty($newSettings))
1372
	{
1373
		$smcFunc['db_insert']('replace',
1374
			'{db_prefix}settings',
1375
			array('variable' => 'string-255', 'value' => 'string-65534'),
1376
			$newSettings,
1377
			array('variable')
1378
		);
1379
	}
1380
1381
	// Let's optimize those new tables, but not on InnoDB, ok?
1382
	if (!$has_innodb)
1383
	{
1384
		db_extend();
1385
		$tables = $smcFunc['db_list_tables']($db_name, $db_prefix . '%');
1386
		foreach ($tables as $table)
1387
		{
1388
			$smcFunc['db_optimize_table']($table) != -1 or $db_messed = true;
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as or instead of || is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
1389
1390
			if (!empty($db_messed))
1391
			{
1392
				$incontext['failures'][-1] = $smcFunc['db_error']();
1393
				break;
1394
			}
1395
		}
1396
	}
1397
1398
	// MySQL specific stuff
1399
	if (substr($db_type, 0, 5) != 'mysql')
1400
		return false;
1401
1402
	// Find database user privileges.
1403
	$privs = array();
1404
	$get_privs = $smcFunc['db_query']('', 'SHOW PRIVILEGES', array());
1405
	while ($row = $smcFunc['db_fetch_assoc']($get_privs))
1406
	{
1407
		if ($row['Privilege'] == 'Alter')
1408
			$privs[] = $row['Privilege'];
1409
	}
1410
	$smcFunc['db_free_result']($get_privs);
1411
1412
	// Check for the ALTER privilege.
1413
	if (!empty($databases[$db_type]['alter_support']) && !in_array('Alter', $privs))
1414
	{
1415
		$incontext['error'] = $txt['error_db_alter_priv'];
1416
		return false;
1417
	}
1418
1419
	if (!empty($exists))
1420
	{
1421
		$incontext['page_title'] = $txt['user_refresh_install'];
1422
		$incontext['was_refresh'] = true;
1423
	}
1424
1425
	return false;
1426
}
1427
1428
// Ask for the administrator login information.
1429
function AdminAccount()
1430
{
1431
	global $txt, $db_type, $smcFunc, $incontext, $db_prefix, $db_passwd, $sourcedir, $db_character_set, $boardurl, $cachedir;
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...
1432
1433
	$incontext['sub_template'] = 'admin_account';
1434
	$incontext['page_title'] = $txt['user_settings'];
1435
	$incontext['continue'] = 1;
1436
1437
	// Skipping?
1438
	if (!empty($_POST['skip']))
1439
		return true;
1440
1441
	// Need this to check whether we need the database password.
1442
	require(dirname(__FILE__) . '/Settings.php');
1443
	load_database();
1444
1445
	require_once($sourcedir . '/Subs-Auth.php');
1446
1447
	require_once($sourcedir . '/Subs.php');
1448
1449
	// Reload settings & set some global funcs
1450
	require_once($sourcedir . '/Load.php');
1451
	reloadSettings();
1452
1453
	// We need this to properly hash the password for Admin
1454 View Code Duplication
	$smcFunc['strtolower'] = $db_character_set != 'utf8' && $txt['lang_character_set'] != 'UTF-8' ? 'strtolower' : function($string) {
1455
			global $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...
1456
			if (function_exists('mb_strtolower'))
1457
				return mb_strtolower($string, 'UTF-8');
1458
			require_once($sourcedir . '/Subs-Charset.php');
1459
			return utf8_strtolower($string);
1460
		};
1461
1462
	if (!isset($_POST['username']))
1463
		$_POST['username'] = '';
1464
	if (!isset($_POST['email']))
1465
		$_POST['email'] = '';
1466
	if (!isset($_POST['server_email']))
1467
		$_POST['server_email'] = '';
1468
1469
	$incontext['username'] = htmlspecialchars(stripslashes($_POST['username']));
1470
	$incontext['email'] = htmlspecialchars(stripslashes($_POST['email']));
1471
	$incontext['server_email'] = htmlspecialchars(stripslashes($_POST['server_email']));
1472
1473
	$incontext['require_db_confirm'] = empty($db_type);
1474
1475
	// Only allow skipping if we think they already have an account setup.
1476
	$request = $smcFunc['db_query']('', '
1477
		SELECT id_member
1478
		FROM {db_prefix}members
1479
		WHERE id_group = {int:admin_group} OR FIND_IN_SET({int:admin_group}, additional_groups) != 0
1480
		LIMIT 1',
1481
		array(
1482
			'db_error_skip' => true,
1483
			'admin_group' => 1,
1484
		)
1485
	);
1486
	if ($smcFunc['db_num_rows']($request) != 0)
1487
		$incontext['skip'] = 1;
1488
	$smcFunc['db_free_result']($request);
1489
1490
	// Trying to create an account?
1491
	if (isset($_POST['password1']) && !empty($_POST['contbutt']))
1492
	{
1493
		// Wrong password?
1494
		if ($incontext['require_db_confirm'] && $_POST['password3'] != $db_passwd)
1495
		{
1496
			$incontext['error'] = $txt['error_db_connect'];
1497
			return false;
1498
		}
1499
		// Not matching passwords?
1500
		if ($_POST['password1'] != $_POST['password2'])
1501
		{
1502
			$incontext['error'] = $txt['error_user_settings_again_match'];
1503
			return false;
1504
		}
1505
		// No password?
1506
		if (strlen($_POST['password1']) < 4)
1507
		{
1508
			$incontext['error'] = $txt['error_user_settings_no_password'];
1509
			return false;
1510
		}
1511
		if (!file_exists($sourcedir . '/Subs.php'))
1512
		{
1513
			$incontext['error'] = $txt['error_subs_missing'];
1514
			return false;
1515
		}
1516
1517
		// Update the webmaster's email?
1518
		if (!empty($_POST['server_email']) && (empty($webmaster_email) || $webmaster_email == '[email protected]'))
0 ignored issues
show
Bug introduced by
The variable $webmaster_email seems to never exist, and therefore empty should always return true. Did you maybe rename this variable?

This check looks for calls to isset(...) or empty() on variables that are yet undefined. These calls will always produce the same result and can be removed.

This is most likely caused by the renaming of a variable or the removal of a function/method parameter.

Loading history...
Bug introduced by
The variable $webmaster_email does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
1519
			updateSettingsFile(array('webmaster_email' => $_POST['server_email']));
1520
1521
		// Work out whether we're going to have dodgy characters and remove them.
1522
		$invalid_characters = preg_match('~[<>&"\'=\\\]~', $_POST['username']) != 0;
1523
		$_POST['username'] = preg_replace('~[<>&"\'=\\\]~', '', $_POST['username']);
1524
1525
		$result = $smcFunc['db_query']('', '
1526
			SELECT id_member, password_salt
1527
			FROM {db_prefix}members
1528
			WHERE member_name = {string:username} OR email_address = {string:email}
1529
			LIMIT 1',
1530
			array(
1531
				'username' => stripslashes($_POST['username']),
1532
				'email' => stripslashes($_POST['email']),
1533
				'db_error_skip' => true,
1534
			)
1535
		);
1536
		if ($smcFunc['db_num_rows']($result) != 0)
1537
		{
1538
			list ($incontext['member_id'], $incontext['member_salt']) = $smcFunc['db_fetch_row']($result);
1539
			$smcFunc['db_free_result']($result);
1540
1541
			$incontext['account_existed'] = $txt['error_user_settings_taken'];
1542
		}
1543
		elseif ($_POST['username'] == '' || strlen($_POST['username']) > 25)
1544
		{
1545
			// Try the previous step again.
1546
			$incontext['error'] = $_POST['username'] == '' ? $txt['error_username_left_empty'] : $txt['error_username_too_long'];
1547
			return false;
1548
		}
1549
		elseif ($invalid_characters || $_POST['username'] == '_' || $_POST['username'] == '|' || strpos($_POST['username'], '[code') !== false || strpos($_POST['username'], '[/code') !== false)
1550
		{
1551
			// Try the previous step again.
1552
			$incontext['error'] = $txt['error_invalid_characters_username'];
1553
			return false;
1554
		}
1555 View Code Duplication
		elseif (empty($_POST['email']) || !filter_var(stripslashes($_POST['email']), FILTER_VALIDATE_EMAIL) || strlen(stripslashes($_POST['email'])) > 255)
1556
		{
1557
			// One step back, this time fill out a proper admin email address.
1558
			$incontext['error'] = sprintf($txt['error_valid_admin_email_needed'], $_POST['username']);
1559
			return false;
1560
		}
1561 View Code Duplication
		elseif (empty($_POST['server_email']) || !filter_var(stripslashes($_POST['server_email']), FILTER_VALIDATE_EMAIL) || strlen(stripslashes($_POST['server_email'])) > 255)
1562
		{
1563
			// One step back, this time fill out a proper admin email address.
1564
			$incontext['error'] = $txt['error_valid_server_email_needed'];
1565
			return false;
1566
		}
1567
		elseif ($_POST['username'] != '')
1568
		{
1569
			$incontext['member_salt'] = substr(md5(mt_rand()), 0, 4);
1570
1571
			// Format the username properly.
1572
			$_POST['username'] = preg_replace('~[\t\n\r\x0B\0\xA0]+~', ' ', $_POST['username']);
1573
			$ip = isset($_SERVER['REMOTE_ADDR']) ? substr($_SERVER['REMOTE_ADDR'], 0, 255) : '';
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $ip. 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...
1574
1575
			$_POST['password1'] = hash_password(stripslashes($_POST['username']), stripslashes($_POST['password1']));
1576
1577
			$incontext['member_id'] = $smcFunc['db_insert']('',
1578
				$db_prefix . 'members',
1579
				array(
1580
					'member_name' => 'string-25', 'real_name' => 'string-25', 'passwd' => 'string', 'email_address' => 'string',
1581
					'id_group' => 'int', 'posts' => 'int', 'date_registered' => 'int',
1582
					'password_salt' => 'string', 'lngfile' => 'string', 'personal_text' => 'string', 'avatar' => 'string',
1583
					'member_ip' => 'inet', 'member_ip2' => 'inet', 'buddy_list' => 'string', 'pm_ignore_list' => 'string',
1584
					'website_title' => 'string', 'website_url' => 'string',
1585
					'signature' => 'string', 'usertitle' => 'string', 'secret_question' => 'string',
1586
					'additional_groups' => 'string', 'ignore_boards' => 'string',
1587
				),
1588
				array(
1589
					stripslashes($_POST['username']), stripslashes($_POST['username']), $_POST['password1'], stripslashes($_POST['email']),
1590
					1, 0, time(),
1591
					$incontext['member_salt'], '', '', '',
1592
					$ip, $ip, '', '',
1593
					'', '',
1594
					'', '', '',
1595
					'', '',
1596
				),
1597
				array('id_member'),
1598
				1
1599
			);
1600
		}
1601
1602
		// If we're here we're good.
1603
		return true;
1604
	}
1605
1606
	return false;
1607
}
1608
1609
// Final step, clean up and a complete message!
1610
function DeleteInstall()
1611
{
1612
	global $smcFunc, $db_character_set, $context, $txt, $incontext;
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...
1613
	global $current_smf_version, $databases, $sourcedir, $forum_version, $modSettings, $user_info, $db_type, $boardurl, $cachedir, $cookiename;
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...
1614
1615
	$incontext['page_title'] = $txt['congratulations'];
1616
	$incontext['sub_template'] = 'delete_install';
1617
	$incontext['continue'] = 0;
1618
1619
	require(dirname(__FILE__) . '/Settings.php');
1620
	load_database();
1621
1622
	chdir(dirname(__FILE__));
1623
1624
	require_once($sourcedir . '/Errors.php');
1625
	require_once($sourcedir . '/Logging.php');
1626
	require_once($sourcedir . '/Subs.php');
1627
	require_once($sourcedir . '/Load.php');
1628
	require_once($sourcedir . '/Security.php');
1629
	require_once($sourcedir . '/Subs-Auth.php');
1630
1631
	// Reload settings & set some global funcs
1632
	reloadSettings();
1633
1634
	// Bring a warning over.
1635
	if (!empty($incontext['account_existed']))
1636
		$incontext['warning'] = $incontext['account_existed'];
1637
1638 View Code Duplication
	if (!empty($db_character_set) && !empty($databases[$db_type]['utf8_support']))
1639
		$smcFunc['db_query']('', '
1640
			SET NAMES {string:db_character_set}',
1641
			array(
1642
				'db_character_set' => $db_character_set,
1643
				'db_error_skip' => true,
1644
			)
1645
		);
1646
1647
	// As track stats is by default enabled let's add some activity.
1648
	$smcFunc['db_insert']('ignore',
1649
		'{db_prefix}log_activity',
1650
		array('date' => 'date', 'topics' => 'int', 'posts' => 'int', 'registers' => 'int'),
1651
		array(strftime('%Y-%m-%d', time()), 1, 1, (!empty($incontext['member_id']) ? 1 : 0)),
1652
		array('date')
1653
	);
1654
1655
	// We're going to want our lovely $modSettings now.
1656
	$request = $smcFunc['db_query']('', '
1657
		SELECT variable, value
1658
		FROM {db_prefix}settings',
1659
		array(
1660
			'db_error_skip' => true,
1661
		)
1662
	);
1663
	// Only proceed if we can load the data.
1664
	if ($request)
1665
	{
1666 View Code Duplication
		while ($row = $smcFunc['db_fetch_row']($request))
1667
			$modSettings[$row[0]] = $row[1];
1668
		$smcFunc['db_free_result']($request);
1669
	}
1670
1671
	// Automatically log them in ;)
1672
	if (isset($incontext['member_id']) && isset($incontext['member_salt']))
1673
		setLoginCookie(3153600 * 60, $incontext['member_id'], hash_salt($_POST['password1'], $incontext['member_salt']));
1674
1675
	$result = $smcFunc['db_query']('', '
1676
		SELECT value
1677
		FROM {db_prefix}settings
1678
		WHERE variable = {string:db_sessions}',
1679
		array(
1680
			'db_sessions' => 'databaseSession_enable',
1681
			'db_error_skip' => true,
1682
		)
1683
	);
1684 View Code Duplication
	if ($smcFunc['db_num_rows']($result) != 0)
1685
		list ($db_sessions) = $smcFunc['db_fetch_row']($result);
1686
	$smcFunc['db_free_result']($result);
1687
1688
	if (empty($db_sessions))
1689
		$_SESSION['admin_time'] = time();
1690
	else
1691
	{
1692
		$_SERVER['HTTP_USER_AGENT'] = substr($_SERVER['HTTP_USER_AGENT'], 0, 211);
1693
1694
		$smcFunc['db_insert']('replace',
1695
			'{db_prefix}sessions',
1696
			array(
1697
				'session_id' => 'string', 'last_update' => 'int', 'data' => 'string',
1698
			),
1699
			array(
1700
				session_id(), time(), 'USER_AGENT|s:' . strlen($_SERVER['HTTP_USER_AGENT']) . ':"' . $_SERVER['HTTP_USER_AGENT'] . '";admin_time|i:' . time() . ';',
1701
			),
1702
			array('session_id')
1703
		);
1704
	}
1705
1706
	updateStats('member');
1707
	updateStats('message');
1708
	updateStats('topic');
1709
1710
	// This function is needed to do the updateStats('subject') call.
1711
	$smcFunc['strtolower'] = $db_character_set != 'utf8' && $txt['lang_character_set'] != 'UTF-8' ? 'strtolower' :
1712 View Code Duplication
		function($string){
1713
			global $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...
1714
			if (function_exists('mb_strtolower'))
1715
				return mb_strtolower($string, 'UTF-8');
1716
			require_once($sourcedir . '/Subs-Charset.php');
1717
			return utf8_strtolower($string);
1718
		};
1719
1720
	$request = $smcFunc['db_query']('', '
1721
		SELECT id_msg
1722
		FROM {db_prefix}messages
1723
		WHERE id_msg = 1
1724
			AND modified_time = 0
1725
		LIMIT 1',
1726
		array(
1727
			'db_error_skip' => true,
1728
		)
1729
	);
1730
	$context['utf8'] = $db_character_set === 'utf8' || $txt['lang_character_set'] === 'UTF-8';
1731
	if ($smcFunc['db_num_rows']($request) > 0)
1732
		updateStats('subject', 1, htmlspecialchars($txt['default_topic_subject']));
1733
	$smcFunc['db_free_result']($request);
1734
1735
	// Now is the perfect time to fetch the SM files.
1736
	require_once($sourcedir . '/ScheduledTasks.php');
1737
	// Sanity check that they loaded earlier!
1738
	if (isset($modSettings['recycle_board']))
1739
	{
1740
		$forum_version = $current_smf_version; // The variable is usually defined in index.php so lets just use our variable to do it for us.
1741
		scheduled_fetchSMfiles(); // Now go get those files!
1742
1743
		// We've just installed!
1744
		$user_info['ip'] = $_SERVER['REMOTE_ADDR'];
1745
		$user_info['id'] = isset($incontext['member_id']) ? $incontext['member_id'] : 0;
1746
		logAction('install', array('version' => $forum_version), 'admin');
1747
	}
1748
1749
	// Check if we need some stupid MySQL fix.
1750
	$server_version = $smcFunc['db_server_info']();
1751 View Code Duplication
	if (($db_type == 'mysql' || $db_type == 'mysqli') && in_array(substr($server_version, 0, 6), array('5.0.50', '5.0.51')))
1752
		updateSettings(array('db_mysql_group_by_fix' => '1'));
1753
1754
	// Some final context for the template.
1755
	$incontext['dir_still_writable'] = is_writable(dirname(__FILE__)) && substr(__FILE__, 1, 2) != ':\\';
1756
	$incontext['probably_delete_install'] = isset($_SESSION['installer_temp_ftp']) || is_writable(dirname(__FILE__)) || is_writable(__FILE__);
1757
1758
	// Update hash's cost to an appropriate setting
1759
	updateSettings(array(
1760
		'bcrypt_hash_cost' => hash_benchmark(),
1761
	));
1762
1763
	return false;
1764
}
1765
1766
function updateSettingsFile($vars)
1767
{
1768
	// Modify Settings.php.
1769
	$settingsArray = file(dirname(__FILE__) . '/Settings.php');
1770
1771
	// @todo Do we just want to read the file in clean, and split it this way always?
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...
1772
	if (count($settingsArray) == 1)
1773
		$settingsArray = preg_split('~[\r\n]~', $settingsArray[0]);
1774
1775
	for ($i = 0, $n = count($settingsArray); $i < $n; $i++)
1776
	{
1777
		// Remove the redirect...
1778
		if (trim($settingsArray[$i]) == 'if (file_exists(dirname(__FILE__) . \'/install.php\'))' && trim($settingsArray[$i + 1]) == '{' && trim($settingsArray[$i + 9]) == '}')
1779
		{
1780
			// Set the ten lines to nothing.
1781
			for ($j=0; $j < 10; $j++)
1782
				$settingsArray[$i++] = '';
1783
1784
			continue;
1785
		}
1786
1787
		if (trim($settingsArray[$i]) == '?' . '>')
1788
			$settingsArray[$i] = '';
1789
1790
		// Don't trim or bother with it if it's not a variable.
1791
		if (substr($settingsArray[$i], 0, 1) != '$')
1792
			continue;
1793
1794
		$settingsArray[$i] = rtrim($settingsArray[$i]) . "\n";
1795
1796
		foreach ($vars as $var => $val)
1797
			if (strncasecmp($settingsArray[$i], '$' . $var, 1 + strlen($var)) == 0)
1798
			{
1799
				$comment = strstr($settingsArray[$i], '#');
1800
				$settingsArray[$i] = '$' . $var . ' = \'' . $val . '\';' . ($comment != '' ? "\t\t" . $comment : "\n");
1801
				unset($vars[$var]);
1802
			}
1803
	}
1804
1805
	// Uh oh... the file wasn't empty... was it?
1806
	if (!empty($vars))
1807
	{
1808
		$settingsArray[$i++] = '';
1809
		foreach ($vars as $var => $val)
1810
			$settingsArray[$i++] = '$' . $var . ' = \'' . $val . '\';' . "\n";
1811
	}
1812
1813
	// Blank out the file - done to fix a oddity with some servers.
1814
	$fp = @fopen(dirname(__FILE__) . '/Settings.php', 'w');
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...
1815
	if (!$fp)
1816
		return false;
1817
	fclose($fp);
1818
1819
	$fp = fopen(dirname(__FILE__) . '/Settings.php', 'r+');
1820
1821
	// Gotta have one of these ;)
1822
	if (trim($settingsArray[0]) != '<?php')
1823
		fwrite($fp, "<?php\n");
1824
1825
	$lines = count($settingsArray);
1826
	for ($i = 0; $i < $lines - 1; $i++)
1827
	{
1828
		// Don't just write a bunch of blank lines.
1829
		if ($settingsArray[$i] != '' || @$settingsArray[$i - 1] != '')
1830
			fwrite($fp, strtr($settingsArray[$i], "\r", ''));
0 ignored issues
show
Security File Manipulation introduced by
strtr($settingsArray[$i], ' ', '') can contain request data and is used in file manipulation context(s) leading to a potential security vulnerability.

5 paths for user data to reach this point

  1. Path: Read from $_GET, and $changes is assigned in other/upgrade.php on line 1195
  1. Read from $_GET, and $changes is assigned
    in other/upgrade.php on line 1195
  2. $changes is passed to updateSettingsFile()
    in other/upgrade.php on line 1268
  3. $var is assigned
    in other/install.php on line 1809
  4. $settingsArray is assigned
    in other/install.php on line 1810
  5. $settingsArray[$i] is passed through strtr()
    in other/install.php on line 1830
  2. Path: Read from $_POST, and $_POST['maintitle'] is escaped by addslashes() for sql, xpath context(s), and $changes is assigned in other/upgrade.php on line 1205
  1. Read from $_POST, and $_POST['maintitle'] is escaped by addslashes() for sql, xpath context(s), and $changes is assigned
    in other/upgrade.php on line 1205
  2. $changes is assigned
    in other/upgrade.php on line 1206
  3. $changes is passed to updateSettingsFile()
    in other/upgrade.php on line 1268
  4. $var is assigned
    in other/install.php on line 1809
  5. $settingsArray is assigned
    in other/install.php on line 1810
  6. $settingsArray[$i] is passed through strtr()
    in other/install.php on line 1830
  3. Path: Read from $_POST, and $_POST['mainmessage'] is escaped by addslashes() for sql, xpath context(s), and $changes is assigned in other/upgrade.php on line 1206
  1. Read from $_POST, and $_POST['mainmessage'] is escaped by addslashes() for sql, xpath context(s), and $changes is assigned
    in other/upgrade.php on line 1206
  2. $changes is passed to updateSettingsFile()
    in other/upgrade.php on line 1268
  3. $var is assigned
    in other/install.php on line 1809
  4. $settingsArray is assigned
    in other/install.php on line 1810
  5. $settingsArray[$i] is passed through strtr()
    in other/install.php on line 1830
  4. Path: Read from $_POST, and $_POST[$config_var][0] is passed through addcslashes(), and $new_settings is assigned in Sources/ManageServer.php on line 1240
  1. Read from $_POST, and $_POST[$config_var][0] is passed through addcslashes(), and $new_settings is assigned
    in Sources/ManageServer.php on line 1240
  2. $new_settings is passed to updateSettingsFile()
    in Sources/ManageServer.php on line 1274
  3. $var is assigned
    in other/install.php on line 1809
  4. $settingsArray is assigned
    in other/install.php on line 1810
  5. $settingsArray[$i] is passed through strtr()
    in other/install.php on line 1830
  5. Path: Read from $_POST, and $_POST[$config_var] is passed through addcslashes(), and $new_settings is assigned in Sources/ManageServer.php on line 1244
  1. Read from $_POST, and $_POST[$config_var] is passed through addcslashes(), and $new_settings is assigned
    in Sources/ManageServer.php on line 1244
  2. $new_settings is passed to updateSettingsFile()
    in Sources/ManageServer.php on line 1274
  3. $var is assigned
    in other/install.php on line 1809
  4. $settingsArray is assigned
    in other/install.php on line 1810
  5. $settingsArray[$i] is passed through strtr()
    in other/install.php on line 1830

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
1831
	}
1832
	fwrite($fp, $settingsArray[$i] . '?' . '>');
0 ignored issues
show
Security File Manipulation introduced by
$settingsArray[$i] . '?' . '>' can contain request data and is used in file manipulation context(s) leading to a potential security vulnerability.

5 paths for user data to reach this point

  1. Path: Read from $_GET, and $changes is assigned in other/upgrade.php on line 1195
  1. Read from $_GET, and $changes is assigned
    in other/upgrade.php on line 1195
  2. $changes is passed to updateSettingsFile()
    in other/upgrade.php on line 1268
  3. $var is assigned
    in other/install.php on line 1809
  4. $settingsArray is assigned
    in other/install.php on line 1810
  2. Path: Read from $_POST, and $_POST['maintitle'] is escaped by addslashes() for sql, xpath context(s), and $changes is assigned in other/upgrade.php on line 1205
  1. Read from $_POST, and $_POST['maintitle'] is escaped by addslashes() for sql, xpath context(s), and $changes is assigned
    in other/upgrade.php on line 1205
  2. $changes is assigned
    in other/upgrade.php on line 1206
  3. $changes is passed to updateSettingsFile()
    in other/upgrade.php on line 1268
  4. $var is assigned
    in other/install.php on line 1809
  5. $settingsArray is assigned
    in other/install.php on line 1810
  3. Path: Read from $_POST, and $_POST['mainmessage'] is escaped by addslashes() for sql, xpath context(s), and $changes is assigned in other/upgrade.php on line 1206
  1. Read from $_POST, and $_POST['mainmessage'] is escaped by addslashes() for sql, xpath context(s), and $changes is assigned
    in other/upgrade.php on line 1206
  2. $changes is passed to updateSettingsFile()
    in other/upgrade.php on line 1268
  3. $var is assigned
    in other/install.php on line 1809
  4. $settingsArray is assigned
    in other/install.php on line 1810
  4. Path: Read from $_POST, and $_POST[$config_var][0] is passed through addcslashes(), and $new_settings is assigned in Sources/ManageServer.php on line 1240
  1. Read from $_POST, and $_POST[$config_var][0] is passed through addcslashes(), and $new_settings is assigned
    in Sources/ManageServer.php on line 1240
  2. $new_settings is passed to updateSettingsFile()
    in Sources/ManageServer.php on line 1274
  3. $var is assigned
    in other/install.php on line 1809
  4. $settingsArray is assigned
    in other/install.php on line 1810
  5. Path: Read from $_POST, and $_POST[$config_var] is passed through addcslashes(), and $new_settings is assigned in Sources/ManageServer.php on line 1244
  1. Read from $_POST, and $_POST[$config_var] is passed through addcslashes(), and $new_settings is assigned
    in Sources/ManageServer.php on line 1244
  2. $new_settings is passed to updateSettingsFile()
    in Sources/ManageServer.php on line 1274
  3. $var is assigned
    in other/install.php on line 1809
  4. $settingsArray is assigned
    in other/install.php on line 1810

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
1833
	fclose($fp);
1834
1835
	// Even though on normal installations the filemtime should prevent this being used by the installer incorrectly
1836
	// it seems that there are times it might not. So let's MAKE it dump the cache.
1837
	if (function_exists('opcache_invalidate'))
1838
		opcache_invalidate(dirname(__FILE__) . '/Settings.php', true);
1839
1840
	return true;
1841
}
1842
1843
function updateDbLastError()
1844
{
1845
	global $cachedir;
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...
1846
1847
	// Write out the db_last_error file with the error timestamp
1848
	if (!empty($cachedir) && is_writable($cachedir))
1849
		file_put_contents($cachedir . '/db_last_error.php', '<' . '?' . "php\n" . '$db_last_error = 0;' . "\n" . '?' . '>');
1850
	else
1851
		file_put_contents(dirname(__FILE__) . '/cache/db_last_error.php', '<' . '?' . "php\n" . '$db_last_error = 0;' . "\n" . '?' . '>');
1852
1853
	return true;
1854
}
1855
1856
// Create an .htaccess file to prevent mod_security. SMF has filtering built-in.
1857
function fixModSecurity()
1858
{
1859
	$htaccess_addition = '
1860
<IfModule mod_security.c>
1861
	# Turn off mod_security filtering.  SMF is a big boy, it doesn\'t need its hands held.
1862
	SecFilterEngine Off
1863
1864
	# The below probably isn\'t needed, but better safe than sorry.
1865
	SecFilterScanPOST Off
1866
</IfModule>';
1867
1868
	if (!function_exists('apache_get_modules') || !in_array('mod_security', apache_get_modules()))
1869
		return true;
1870
	elseif (file_exists(dirname(__FILE__) . '/.htaccess') && is_writable(dirname(__FILE__) . '/.htaccess'))
1871
	{
1872
		$current_htaccess = implode('', file(dirname(__FILE__) . '/.htaccess'));
1873
1874
		// Only change something if mod_security hasn't been addressed yet.
1875
		if (strpos($current_htaccess, '<IfModule mod_security.c>') === false)
1876
		{
1877 View Code Duplication
			if ($ht_handle = fopen(dirname(__FILE__) . '/.htaccess', 'a'))
1878
			{
1879
				fwrite($ht_handle, $htaccess_addition);
1880
				fclose($ht_handle);
1881
				return true;
1882
			}
1883
			else
1884
				return false;
1885
		}
1886
		else
1887
			return true;
1888
	}
1889
	elseif (file_exists(dirname(__FILE__) . '/.htaccess'))
1890
		return strpos(implode('', file(dirname(__FILE__) . '/.htaccess')), '<IfModule mod_security.c>') !== false;
1891
	elseif (is_writable(dirname(__FILE__)))
1892
	{
1893 View Code Duplication
		if ($ht_handle = fopen(dirname(__FILE__) . '/.htaccess', 'w'))
1894
		{
1895
			fwrite($ht_handle, $htaccess_addition);
1896
			fclose($ht_handle);
1897
			return true;
1898
		}
1899
		else
1900
			return false;
1901
	}
1902
	else
1903
		return false;
1904
}
1905
1906
function template_install_above()
1907
{
1908
	global $incontext, $txt, $installurl;
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...
1909
1910
	echo '<!DOCTYPE html>
1911
<html', $txt['lang_rtl'] == true ? ' dir="rtl"' : '', '>
1912
<head>
1913
	<meta charset="', isset($txt['lang_character_set']) ? $txt['lang_character_set'] : 'UTF-8', '">
1914
	<meta name="robots" content="noindex">
1915
	<title>', $txt['smf_installer'], '</title>
1916
	<link rel="stylesheet" href="Themes/default/css/index.css?alp21">
1917
	<link rel="stylesheet" href="Themes/default/css/install.css?alp21">
1918
	', $txt['lang_rtl'] == true ? '<link rel="stylesheet" href="Themes/default/css/rtl.css?alp21">' : '', '
1919
1920
	<script src="Themes/default/scripts/jquery-3.2.1.min.js"></script>
1921
	<script src="Themes/default/scripts/script.js"></script>
1922
</head>
1923
<body>
1924
	<div id="footerfix">
1925
	<div id="header">
1926
		<h1 class="forumtitle">', $txt['smf_installer'], '</h1>
1927
		<img id="smflogo" src="Themes/default/images/smflogo.svg" alt="Simple Machines Forum" title="Simple Machines Forum">
1928
	</div>
1929
	<div id="wrapper">
1930
		<div id="upper_section">
1931
			<div id="inner_section">
1932
				<div id="inner_wrap">';
1933
1934
	// Have we got a language drop down - if so do it on the first step only.
1935
	if (!empty($incontext['detected_languages']) && count($incontext['detected_languages']) > 1 && $incontext['current_step'] == 0)
1936
	{
1937
		echo '
1938
					<div class="news">
1939
						<form action="', $installurl, '" method="get">
1940
							<label for="installer_language">', $txt['installer_language'], ':</label>
1941
							<select id="installer_language" name="lang_file" onchange="location.href = \'', $installurl, '?lang_file=\' + this.options[this.selectedIndex].value;">';
1942
1943
		foreach ($incontext['detected_languages'] as $lang => $name)
1944
			echo '
1945
								<option', isset($_SESSION['installer_temp_lang']) && $_SESSION['installer_temp_lang'] == $lang ? ' selected' : '', ' value="', $lang, '">', $name, '</option>';
1946
1947
		echo '
1948
							</select>
1949
							<noscript><input type="submit" value="', $txt['installer_language_set'], '" class="button"></noscript>
1950
						</form>
1951
					</div><!-- .news -->
1952
					<hr class="clear">';
1953
	}
1954
1955
	echo '
1956
				</div><!-- #inner_wrap -->
1957
			</div><!-- #inner_section -->
1958
		</div><!-- #upper_section -->
1959
		<div id="content_section">
1960
			<div id="main_content_section">
1961
				<div id="main_steps">
1962
					<h2>', $txt['upgrade_progress'], '</h2>
1963
					<ul>';
1964
1965 View Code Duplication
	foreach ($incontext['steps'] as $num => $step)
1966
		echo '
1967
						<li class="', $num < $incontext['current_step'] ? 'stepdone' : ($num == $incontext['current_step'] ? 'stepcurrent' : 'stepwaiting'), '">', $txt['upgrade_step'], ' ', $step[0], ': ', $step[1], '</li>';
1968
1969
	echo '
1970
					</ul>
1971
				</div>
1972
				<div id="install_progress">
1973
					<div id="progress_bar" class="progress_bar progress_green">
1974
						<h3>'. $txt['upgrade_overall_progress'], '</h3>
1975
						<span id="overall_text">', $incontext['overall_percent'], '%</span>
1976
						<div id="overall_progress" class="bar" style="width: ', $incontext['overall_percent'], '%;"></div>
1977
					</div>
1978
				</div>
1979
				<div id="main_screen" class="clear">
1980
					<h2>', $incontext['page_title'], '</h2>
1981
					<div class="panel">';
1982
}
1983
1984
function template_install_below()
1985
{
1986
	global $incontext, $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...
1987
1988
	if (!empty($incontext['continue']) || !empty($incontext['skip']))
1989
	{
1990
		echo '
1991
							<div class="floatright">';
1992
1993
		if (!empty($incontext['continue']))
1994
			echo '
1995
								<input type="submit" id="contbutt" name="contbutt" value="', $txt['upgrade_continue'], '" onclick="return submitThisOnce(this);" class="button">';
1996
		if (!empty($incontext['skip']))
1997
			echo '
1998
								<input type="submit" id="skip" name="skip" value="', $txt['upgrade_skip'], '" onclick="return submitThisOnce(this);" class="button">';
1999
		echo '
2000
							</div>';
2001
	}
2002
2003
	// Show the closing form tag and other data only if not in the last step
2004
	if (count($incontext['steps']) - 1 !== (int) $incontext['current_step'])
2005
		echo '
2006
						</form>';
2007
2008
	echo '
2009
					</div><!-- .panel -->
2010
				</div><!-- #main_screen -->
2011
			</div><!-- #main_content_section -->
2012
		</div><!-- #content_section -->
2013
	</div><!-- #wrapper -->
2014
	</div><!-- #footerfix -->
2015
	<div id="footer">
2016
		<ul>
2017
			<li class="copyright"><a href="https://www.simplemachines.org/" title="Simple Machines Forum" target="_blank" rel="noopener">SMF &copy; 2018, Simple Machines</a></li>
2018
		</ul>
2019
	</div>
2020
</body>
2021
</html>';
2022
}
2023
2024
// Welcome them to the wonderful world of SMF!
2025
function template_welcome_message()
2026
{
2027
	global $incontext, $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...
2028
2029
	echo '
2030
	<script src="https://www.simplemachines.org/smf/current-version.js?version=' . $GLOBALS['current_smf_version'] . '"></script>
2031
	<form action="', $incontext['form_url'], '" method="post">
2032
		<p>', sprintf($txt['install_welcome_desc'], $GLOBALS['current_smf_version']), '</p>
2033
		<div id="version_warning" class="noticebox" style="display: none;">
2034
			<h3>', $txt['error_warning_notice'], '</h3>
2035
			', sprintf($txt['error_script_outdated'], '<em id="smfVersion" style="white-space: nowrap;">??</em>', '<em id="yourVersion" style="white-space: nowrap;">' . $GLOBALS['current_smf_version'] . '</em>'), '
2036
		</div>';
2037
2038
	// Show the warnings, or not.
2039
	if (template_warning_divs())
2040
		echo '
2041
		<h3>', $txt['install_all_lovely'], '</h3>';
2042
2043
	// Say we want the continue button!
2044
	if (empty($incontext['error']))
2045
		$incontext['continue'] = 1;
2046
2047
	// For the latest version stuff.
2048
	echo '
2049
		<script>
2050
			// Latest version?
2051
			function smfCurrentVersion()
2052
			{
2053
				var smfVer, yourVer;
2054
2055
				if (!(\'smfVersion\' in window))
2056
					return;
2057
2058
				window.smfVersion = window.smfVersion.replace(/SMF\s?/g, \'\');
2059
2060
				smfVer = document.getElementById("smfVersion");
2061
				yourVer = document.getElementById("yourVersion");
2062
2063
				setInnerHTML(smfVer, window.smfVersion);
2064
2065
				var currentVersion = getInnerHTML(yourVer);
2066
				if (currentVersion < window.smfVersion)
2067
					document.getElementById(\'version_warning\').style.display = \'\';
2068
			}
2069
			addLoadEvent(smfCurrentVersion);
2070
		</script>';
2071
}
2072
2073
// A shortcut for any warning stuff.
2074
function template_warning_divs()
2075
{
2076
	global $txt, $incontext;
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...
2077
2078
	// Errors are very serious..
2079
	if (!empty($incontext['error']))
2080
		echo '
2081
		<div class="errorbox">
2082
			<h3>', $txt['upgrade_critical_error'], '</h3>
2083
			', $incontext['error'], '
2084
		</div>';
2085
	// A warning message?
2086
	elseif (!empty($incontext['warning']))
2087
		echo '
2088
		<div class="errorbox">
2089
			<h3>', $txt['upgrade_warning'], '</h3>
2090
			', $incontext['warning'], '
2091
		</div>';
2092
2093
	return empty($incontext['error']) && empty($incontext['warning']);
2094
}
2095
2096
function template_chmod_files()
2097
{
2098
	global $txt, $incontext;
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...
2099
2100
	echo '
2101
		<p>', $txt['ftp_setup_why_info'], '</p>
2102
		<ul class="error_content">
2103
			<li>', implode('</li>
2104
			<li>', $incontext['failed_files']), '</li>
2105
		</ul>';
2106
2107
	if (isset($incontext['systemos'], $incontext['detected_path']) && $incontext['systemos'] == 'linux')
2108
		echo '
2109
		<hr>
2110
		<p>', $txt['chmod_linux_info'], '</p>
2111
		<tt># chmod a+w ', implode(' ' . $incontext['detected_path'] . '/', $incontext['failed_files']), '</tt>';
2112
2113
	// This is serious!
2114
	if (!template_warning_divs())
2115
		return;
2116
2117
	echo '
2118
		<hr>
2119
		<p>', $txt['ftp_setup_info'], '</p>';
2120
2121
	if (!empty($incontext['ftp_errors']))
2122
		echo '
2123
		<div class="error_message">
2124
			', $txt['error_ftp_no_connect'], '<br><br>
2125
			<code>', implode('<br>', $incontext['ftp_errors']), '</code>
2126
		</div>';
2127
2128
	echo '
2129
		<form action="', $incontext['form_url'], '" method="post">
2130
			<dl class="settings">
2131
				<dt>
2132
					<label for="ftp_server">', $txt['ftp_server'], ':</label>
2133
				</dt>
2134
				<dd>
2135
					<div class="floatright">
2136
						<label for="ftp_port" class="textbox"><strong>', $txt['ftp_port'], ':&nbsp;</strong></label>
2137
						<input type="text" size="3" name="ftp_port" id="ftp_port" value="', $incontext['ftp']['port'], '">
2138
					</div>
2139
					<input type="text" size="30" name="ftp_server" id="ftp_server" value="', $incontext['ftp']['server'], '">
2140
					<div class="smalltext block">', $txt['ftp_server_info'], '</div>
2141
				</dd>
2142
				<dt>
2143
					<label for="ftp_username">', $txt['ftp_username'], ':</label>
2144
				</dt>
2145
				<dd>
2146
					<input type="text" size="30" name="ftp_username" id="ftp_username" value="', $incontext['ftp']['username'], '">
2147
					<div class="smalltext block">', $txt['ftp_username_info'], '</div>
2148
				</dd>
2149
				<dt>
2150
					<label for="ftp_password">', $txt['ftp_password'], ':</label>
2151
				</dt>
2152
				<dd>
2153
					<input type="password" size="30" name="ftp_password" id="ftp_password">
2154
					<div class="smalltext block">', $txt['ftp_password_info'], '</div>
2155
				</dd>
2156
				<dt>
2157
					<label for="ftp_path">', $txt['ftp_path'], ':</label>
2158
				</dt>
2159
				<dd>
2160
					<input type="text" size="30" name="ftp_path" id="ftp_path" value="', $incontext['ftp']['path'], '">
2161
					<div class="smalltext block">', $incontext['ftp']['path_msg'], '</div>
2162
				</dd>
2163
			</dl>
2164
			<div class="righttext buttons">
2165
				<input type="submit" value="', $txt['ftp_connect'], '" onclick="return submitThisOnce(this);" class="button">
2166
			</div>
2167
		</form>
2168
		<a href="', $incontext['form_url'], '">', $txt['error_message_click'], '</a> ', $txt['ftp_setup_again'];
2169
}
2170
2171
// Get the database settings prepared.
2172
function template_database_settings()
2173
{
2174
	global $incontext, $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...
2175
2176
	echo '
2177
	<form action="', $incontext['form_url'], '" method="post">
2178
		<p>', $txt['db_settings_info'], '</p>';
2179
2180
	template_warning_divs();
2181
2182
	echo '
2183
		<dl class="settings">';
2184
2185
	// More than one database type?
2186
	if (count($incontext['supported_databases']) > 1)
2187
	{
2188
		echo '
2189
			<dt>
2190
				<label for="db_type_input">', $txt['db_settings_type'], ':</label>
2191
			</dt>
2192
			<dd>
2193
				<select name="db_type" id="db_type_input" onchange="toggleDBInput();">';
2194
2195
	foreach ($incontext['supported_databases'] as $key => $db)
2196
			echo '
2197
					<option value="', $key, '"', isset($_POST['db_type']) && $_POST['db_type'] == $key ? ' selected' : '', '>', $db['name'], '</option>';
2198
2199
	echo '
2200
				</select>
2201
				<div class="smalltext">', $txt['db_settings_type_info'], '</div>
2202
			</dd>';
2203
	}
2204
	else
2205
	{
2206
		echo '
2207
			<dd>
2208
				<input type="hidden" name="db_type" value="', $incontext['db']['type'], '">
2209
			</dd>';
2210
	}
2211
2212
	echo '
2213
			<dt>
2214
				<label for="db_server_input">', $txt['db_settings_server'], ':</label>
2215
			</dt>
2216
			<dd>
2217
				<input type="text" name="db_server" id="db_server_input" value="', $incontext['db']['server'], '" size="30">
2218
				<div class="smalltext">', $txt['db_settings_server_info'], '</div>
2219
			</dd>
2220
			<dt>
2221
				<label for="db_port_input">', $txt['db_settings_port'], ':</label>
2222
			</dt>
2223
			<dd>
2224
				<input type="text" name="db_port" id="db_port_input" value="', $incontext['db']['port'], '">
2225
				<div class="smalltext">', $txt['db_settings_port_info'], '</div>
2226
			</dd>
2227
			<dt>
2228
				<label for="db_user_input">', $txt['db_settings_username'], ':</label>
2229
			</dt>
2230
			<dd>
2231
				<input type="text" name="db_user" id="db_user_input" value="', $incontext['db']['user'], '" size="30">
2232
				<div class="smalltext">', $txt['db_settings_username_info'], '</div>
2233
			</dd>
2234
			<dt>
2235
				<label for="db_passwd_input">', $txt['db_settings_password'], ':</label>
2236
			</dt>
2237
			<dd>
2238
				<input type="password" name="db_passwd" id="db_passwd_input" value="', $incontext['db']['pass'], '" size="30">
2239
				<div class="smalltext">', $txt['db_settings_password_info'], '</div>
2240
			</dd>
2241
			<dt>
2242
				<label for="db_name_input">', $txt['db_settings_database'], ':</label>
2243
			</dt>
2244
			<dd>
2245
				<input type="text" name="db_name" id="db_name_input" value="', empty($incontext['db']['name']) ? 'smf' : $incontext['db']['name'], '" size="30">
2246
				<div class="smalltext">
2247
					', $txt['db_settings_database_info'], '
2248
					<span id="db_name_info_warning">', $txt['db_settings_database_info_note'], '</span>
2249
				</div>
2250
			</dd>
2251
			<dt style="display: none;">
2252
				<label for="db_filename_input">', $txt['db_settings_database_file'], ':</label>
2253
			</dt>
2254
			<dd style="display: none;">
2255
				<input type="text" name="db_filename" id="db_filename_input" value="', empty($incontext['db']['name']) ? dirname(__FILE__) . '/smf_' . substr(md5(microtime()), 0, 10) : stripslashes($incontext['db']['name']), '" size="30">
2256
				<div class="smalltext">', $txt['db_settings_database_file_info'], '</div>
2257
			</dd>
2258
			<dt>
2259
				<label for="db_prefix_input">', $txt['db_settings_prefix'], ':</label>
2260
			</dt>
2261
			<dd>
2262
				<input type="text" name="db_prefix" id="db_prefix_input" value="', $incontext['db']['prefix'], '" size="30">
2263
				<div class="smalltext">', $txt['db_settings_prefix_info'], '</div>
2264
			</dd>
2265
		</dl>';
2266
2267
	// Toggles a warning related to db names in PostgreSQL
2268
	echo '
2269
		<script>
2270
			function toggleDBInput()
2271
			{
2272
				if (document.getElementById(\'db_type_input\').value == \'postgresql\')
2273
					document.getElementById(\'db_name_info_warning\').style.display = \'none\';
2274
				else
2275
					document.getElementById(\'db_name_info_warning\').style.display = \'\';
2276
			}
2277
			toggleDBInput();
2278
		</script>';
2279
}
2280
2281
// Stick in their forum settings.
2282
function template_forum_settings()
2283
{
2284
	global $incontext, $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...
2285
2286
	echo '
2287
	<form action="', $incontext['form_url'], '" method="post">
2288
		<h3>', $txt['install_settings_info'], '</h3>';
2289
2290
	template_warning_divs();
2291
2292
	echo '
2293
		<dl class="settings">
2294
			<dt>
2295
				<label for="mbname_input">', $txt['install_settings_name'], ':</label>
2296
			</dt>
2297
			<dd>
2298
				<input type="text" name="mbname" id="mbname_input" value="', $txt['install_settings_name_default'], '" size="65">
2299
				<div class="smalltext">', $txt['install_settings_name_info'], '</div>
2300
			</dd>
2301
			<dt>
2302
				<label for="boardurl_input">', $txt['install_settings_url'], ':</label>
2303
			</dt>
2304
			<dd>
2305
				<input type="text" name="boardurl" id="boardurl_input" value="', $incontext['detected_url'], '" size="65">
2306
				<div class="smalltext">', $txt['install_settings_url_info'], '</div>
2307
			</dd>
2308
			<dt>
2309
				<label for="reg_mode">', $txt['install_settings_reg_mode'], ':</label>
2310
			</dt>
2311
			<dd>
2312
				<select name="reg_mode" id="reg_mode">
2313
					<optgroup label="', $txt['install_settings_reg_modes'], ':">
2314
						<option value="0" selected>', $txt['install_settings_reg_immediate'], '</option>
2315
						<option value="1">', $txt['install_settings_reg_email'], '</option>
2316
						<option value="2">', $txt['install_settings_reg_admin'], '</option>
2317
						<option value="3">', $txt['install_settings_reg_disabled'], '</option>
2318
					</optgroup>
2319
				</select>
2320
				<div class="smalltext">', $txt['install_settings_reg_mode_info'], '</div>
2321
			</dd>
2322
			<dt>', $txt['install_settings_compress'], ':</dt>
2323
			<dd>
2324
				<input type="checkbox" name="compress" id="compress_check" checked>
2325
				<label for="compress_check">', $txt['install_settings_compress_title'], '</label>
2326
				<div class="smalltext">', $txt['install_settings_compress_info'], '</div>
2327
			</dd>
2328
			<dt>', $txt['install_settings_dbsession'], ':</dt>
2329
			<dd>
2330
				<input type="checkbox" name="dbsession" id="dbsession_check" checked>
2331
				<label for="dbsession_check">', $txt['install_settings_dbsession_title'], '</label>
2332
				<div class="smalltext">', $incontext['test_dbsession'] ? $txt['install_settings_dbsession_info1'] : $txt['install_settings_dbsession_info2'], '</div>
2333
			</dd>
2334
			<dt>', $txt['install_settings_utf8'], ':</dt>
2335
			<dd>
2336
				<input type="checkbox" name="utf8" id="utf8_check"', $incontext['utf8_default'] ? ' checked' : '', '', $incontext['utf8_required'] ? ' disabled' : '', '>
2337
				<label for="utf8_check">', $txt['install_settings_utf8_title'], '</label>
2338
				<div class="smalltext">', $txt['install_settings_utf8_info'], '</div>
2339
			</dd>
2340
			<dt>', $txt['install_settings_stats'], ':</dt>
2341
			<dd>
2342
				<input type="checkbox" name="stats" id="stats_check" checked="checked">
2343
				<label for="stats_check">', $txt['install_settings_stats_title'], '</label>
2344
				<div class="smalltext">', $txt['install_settings_stats_info'], '</div>
2345
			</dd>
2346
			<dt>', $txt['force_ssl'], ':</dt>
2347
			<dd>
2348
				<input type="checkbox" name="force_ssl" id="force_ssl"', $incontext['ssl_chkbx_checked'] ? ' checked' : '',
2349
					$incontext['ssl_chkbx_protected'] ? ' disabled' : '', '>
2350
				<label for="force_ssl">', $txt['force_ssl_label'], '</label>
2351
				<div class="smalltext"><strong>', $txt['force_ssl_info'], '</strong></div>
2352
			</dd>
2353
		</dl>';
2354
}
2355
2356
// Show results of the database population.
2357
function template_populate_database()
2358
{
2359
	global $incontext, $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...
2360
2361
	echo '
2362
	<form action="', $incontext['form_url'], '" method="post">
2363
		<p>', !empty($incontext['was_refresh']) ? $txt['user_refresh_install_desc'] : $txt['db_populate_info'], '</p>';
2364
2365
	if (!empty($incontext['sql_results']))
2366
	{
2367
		echo '
2368
		<ul>
2369
			<li>', implode('</li><li>', $incontext['sql_results']), '</li>
2370
		</ul>';
2371
	}
2372
2373
	if (!empty($incontext['failures']))
2374
	{
2375
		echo '
2376
		<div class="red">', $txt['error_db_queries'], '</div>
2377
		<ul>';
2378
2379
		foreach ($incontext['failures'] as $line => $fail)
2380
			echo '
2381
			<li><strong>', $txt['error_db_queries_line'], $line + 1, ':</strong> ', nl2br(htmlspecialchars($fail)), '</li>';
2382
2383
		echo '
2384
		</ul>';
2385
	}
2386
2387
	echo '
2388
		<p>', $txt['db_populate_info2'], '</p>';
2389
2390
	template_warning_divs();
2391
2392
	echo '
2393
		<input type="hidden" name="pop_done" value="1">';
2394
}
2395
2396
// Create the admin account.
2397
function template_admin_account()
2398
{
2399
	global $incontext, $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...
2400
2401
	echo '
2402
	<form action="', $incontext['form_url'], '" method="post">
2403
		<p>', $txt['user_settings_info'], '</p>';
2404
2405
	template_warning_divs();
2406
2407
	echo '
2408
		<dl class="settings">
2409
			<dt>
2410
				<label for="username">', $txt['user_settings_username'], ':</label>
2411
			</dt>
2412
			<dd>
2413
				<input type="text" name="username" id="username" value="', $incontext['username'], '" size="40">
2414
				<div class="smalltext">', $txt['user_settings_username_info'], '</div>
2415
			</dd>
2416
			<dt>
2417
				<label for="password1">', $txt['user_settings_password'], ':</label>
2418
			</dt>
2419
			<dd>
2420
				<input type="password" name="password1" id="password1" size="40">
2421
				<div class="smalltext">', $txt['user_settings_password_info'], '</div>
2422
			</dd>
2423
			<dt>
2424
				<label for="password2">', $txt['user_settings_again'], ':</label>
2425
			</dt>
2426
			<dd>
2427
				<input type="password" name="password2" id="password2" size="40">
2428
				<div class="smalltext">', $txt['user_settings_again_info'], '</div>
2429
			</dd>
2430
			<dt>
2431
				<label for="email">', $txt['user_settings_admin_email'], ':</label>
2432
			</dt>
2433
			<dd>
2434
				<input type="text" name="email" id="email" value="', $incontext['email'], '" size="40">
2435
				<div class="smalltext">', $txt['user_settings_admin_email_info'], '</div>
2436
			</dd>
2437
			<dt>
2438
				<label for="server_email">', $txt['user_settings_server_email'], ':</label>
2439
			</dt>
2440
			<dd>
2441
				<input type="text" name="server_email" id="server_email" value="', $incontext['server_email'], '" size="40">
2442
				<div class="smalltext">', $txt['user_settings_server_email_info'], '</div>
2443
			</dd>
2444
		</dl>';
2445
2446
	if ($incontext['require_db_confirm'])
2447
		echo '
2448
		<h2>', $txt['user_settings_database'], '</h2>
2449
		<p>', $txt['user_settings_database_info'], '</p>
2450
2451
		<div class="lefttext">
2452
			<input type="password" name="password3" size="30">
2453
		</div>';
2454
}
2455
2456
// Tell them it's done, and to delete.
2457
function template_delete_install()
2458
{
2459
	global $incontext, $installurl, $txt, $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...
2460
2461
	echo '
2462
		<p>', $txt['congratulations_help'], '</p>';
2463
2464
	template_warning_divs();
2465
2466
	// Install directory still writable?
2467
	if ($incontext['dir_still_writable'])
2468
		echo '
2469
		<p><em>', $txt['still_writable'], '</em></p>';
2470
2471
	// Don't show the box if it's like 99% sure it won't work :P.
2472
	if ($incontext['probably_delete_install'])
2473
		echo '
2474
		<label>
2475
			<input type="checkbox" id="delete_self" onclick="doTheDelete();">
2476
			<strong>', $txt['delete_installer'], !isset($_SESSION['installer_temp_ftp']) ? ' ' . $txt['delete_installer_maybe'] : '', '</strong>
2477
		</label>
2478
		<script>
2479
			function doTheDelete()
2480
			{
2481
				var theCheck = document.getElementById ? document.getElementById("delete_self") : document.all.delete_self;
2482
				var tempImage = new Image();
2483
2484
				tempImage.src = "', $installurl, '?delete=1&ts_" + (new Date().getTime());
2485
				tempImage.width = 0;
2486
				theCheck.disabled = true;
2487
			}
2488
		</script>';
2489
2490
	echo '
2491
		<p>', sprintf($txt['go_to_your_forum'], $boardurl . '/index.php'), '</p>
2492
		<br>
2493
		', $txt['good_luck'];
2494
}
2495
2496
?>
2497