Completed
Push — release-2.1 ( e55abf...f15ab1 )
by
unknown
08:31
created

install.php ➔ CheckFilesWritable()   F

Complexity

Conditions 47
Paths > 20000

Size

Total Lines 201
Code Lines 109

Duplication

Lines 18
Ratio 8.96 %

Importance

Changes 0
Metric Value
cc 47
eloc 109
nc 593472
nop 0
dl 18
loc 201
rs 2
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.2',
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
	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
	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
	</head>
299
	<body style="font-family: sans-serif;"><div style="width: 600px;">
300
		<h1 style="font-size: 14pt;">A critical error has occurred.</h1>
301
302
		<p>This installer was unable to find the installer\'s language file or files.  They should be found under:</p>
303
304
		<div style="margin: 1ex; font-family: monospace; font-weight: bold;">', 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 304

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

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...
416
		}
417
		// Show the footer.
418
		template_install_below();
419
	}
420
421
	// Bang - gone!
422
	die();
423
}
424
425
function Welcome()
426
{
427
	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...
428
429
	$incontext['page_title'] = $txt['install_welcome'];
430
	$incontext['sub_template'] = 'welcome_message';
431
432
	// Done the submission?
433
	if (isset($_POST['contbutt']))
434
		return true;
435
436
	// See if we think they have already installed it?
437
	if (is_readable(dirname(__FILE__) . '/Settings.php'))
438
	{
439
		$probably_installed = 0;
440
		foreach (file(dirname(__FILE__) . '/Settings.php') as $line)
441
		{
442
			if (preg_match('~^\$db_passwd\s=\s\'([^\']+)\';$~', $line))
443
				$probably_installed++;
444
			if (preg_match('~^\$boardurl\s=\s\'([^\']+)\';~', $line) && !preg_match('~^\$boardurl\s=\s\'http://127\.0\.0\.1/smf\';~', $line))
445
				$probably_installed++;
446
		}
447
448
		if ($probably_installed == 2)
449
			$incontext['warning'] = $txt['error_already_installed'];
450
	}
451
452
	// Is some database support even compiled in?
453
	$incontext['supported_databases'] = array();
454
	foreach ($databases as $key => $db)
455
	{
456
		if ($db['supported'])
457
		{
458
			$type = ($key == 'mysqli') ? 'mysql' : $key;
459
			if (!file_exists(dirname(__FILE__) . '/install_' . $GLOBALS['db_script_version'] . '_' . $type . '.sql'))
460
			{
461
				$databases[$key]['supported'] = false;
462
				$notFoundSQLFile = true;
463
				$txt['error_db_script_missing'] = sprintf($txt['error_db_script_missing'], 'install_' . $GLOBALS['db_script_version'] . '_' . $type . '.sql');
464
			}
465
			else
466
				$incontext['supported_databases'][] = $db;
467
		}
468
	}
469
470
	// Check the PHP version.
471
	if ((!function_exists('version_compare') || version_compare($GLOBALS['required_php_version'], PHP_VERSION, '>=')))
472
		$error = 'error_php_too_low';
473
	// Make sure we have a supported database
474
	elseif (empty($incontext['supported_databases']))
475
		$error = empty($notFoundSQLFile) ? 'error_db_missing' : 'error_db_script_missing';
476
	// How about session support?  Some crazy sysadmin remove it?
477
	elseif (!function_exists('session_start'))
478
		$error = 'error_session_missing';
479
	// Make sure they uploaded all the files.
480
	elseif (!file_exists(dirname(__FILE__) . '/index.php'))
481
		$error = 'error_missing_files';
482
	// Very simple check on the session.save_path for Windows.
483
	// @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...
484
	elseif (@ini_get('session.save_path') == '/tmp' && substr(__FILE__, 1, 2) == ':\\')
485
		$error = 'error_session_save_path';
486
487
	// Since each of the three messages would look the same, anyway...
488
	if (isset($error))
489
		$incontext['error'] = $txt[$error];
490
491
	// Mod_security blocks everything that smells funny. Let SMF handle security.
492
	if (!fixModSecurity() && !isset($_GET['overmodsecurity']))
493
		$incontext['error'] = $txt['error_mod_security'] . '<br><br><a href="' . $installurl . '?overmodsecurity=true">' . $txt['error_message_click'] . '</a> ' . $txt['error_message_bad_try_again'];
494
495
	// Confirm mbstring is loaded...
496
	if (!extension_loaded('mbstring'))
497
		$incontext['error'] = $txt['install_no_mbstring'];
498
499
	// Check for https stream support.
500
	$supported_streams = stream_get_wrappers();
501
	if (!in_array('https', $supported_streams))
502
		$incontext['warning'] = $txt['install_no_https'];
503
504
	return false;
505
}
506
507
function CheckFilesWritable()
508
{
509
	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...
510
511
	$incontext['page_title'] = $txt['ftp_checking_writable'];
512
	$incontext['sub_template'] = 'chmod_files';
513
514
	$writable_files = array(
515
		'attachments',
516
		'avatars',
517
		'custom_avatar',
518
		'cache',
519
		'Packages',
520
		'Smileys',
521
		'Themes',
522
		'agreement.txt',
523
		'Settings.php',
524
		'Settings_bak.php',
525
	);
526
527
	foreach ($incontext['detected_languages'] as $lang => $temp)
528
		$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...
529
530
	// With mod_security installed, we could attempt to fix it with .htaccess.
531
	if (function_exists('apache_get_modules') && in_array('mod_security', apache_get_modules()))
532
		$writable_files[] = file_exists(dirname(__FILE__) . '/.htaccess') ? '.htaccess' : '.';
533
534
	$failed_files = array();
535
536
	// On linux, it's easy - just use is_writable!
537
	if (substr(__FILE__, 1, 2) != ':\\')
538
	{
539
		$incontext['systemos'] = 'linux';
540
541
		foreach ($writable_files as $file)
542
		{
543
			// Some files won't exist, try to address up front
544
			if (!file_exists(dirname(__FILE__) . '/' . $file))
545
				@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...
546
			// NOW do the writable check...
547
			if (!is_writable(dirname(__FILE__) . '/' . $file))
548
			{
549
				@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...
550
551
				// Well, 755 hopefully worked... if not, try 777.
552
				if (!is_writable(dirname(__FILE__) . '/' . $file) && !@chmod(dirname(__FILE__) . '/' . $file, 0777))
553
					$failed_files[] = $file;
554
			}
555
		}
556 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...
557
			@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...
558
	}
559
	// Windows is trickier.  Let's try opening for r+...
560
	else
561
	{
562
		$incontext['systemos'] = 'windows';
563
564
		foreach ($writable_files as $file)
565
		{
566
			// Folders can't be opened for write... but the index.php in them can ;)
567
			if (is_dir(dirname(__FILE__) . '/' . $file))
568
				$file .= '/index.php';
569
570
			// Funny enough, chmod actually does do something on windows - it removes the read only attribute.
571
			@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...
572
			$fp = @fopen(dirname(__FILE__) . '/' . $file, 'r+');
573
574
			// Hmm, okay, try just for write in that case...
575
			if (!is_resource($fp))
576
				$fp = @fopen(dirname(__FILE__) . '/' . $file, 'w');
577
578
			if (!is_resource($fp))
579
				$failed_files[] = $file;
580
581
			@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...
582
		}
583 View Code Duplication
		foreach ($extra_files as $file)
584
			@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...
585
	}
586
587
	$failure = count($failed_files) >= 1;
588
589
	if (!isset($_SERVER))
590
		return !$failure;
591
592
	// Put the list into context.
593
	$incontext['failed_files'] = $failed_files;
594
595
	// It's not going to be possible to use FTP on windows to solve the problem...
596
	if ($failure && substr(__FILE__, 1, 2) == ':\\')
597
	{
598
		$incontext['error'] = $txt['error_windows_chmod'] . '
599
					<ul style="margin: 2.5ex; font-family: monospace;">
600
						<li>' . implode('</li>
601
						<li>', $failed_files) . '</li>
602
					</ul>';
603
604
		return false;
605
	}
606
	// We're going to have to use... FTP!
607
	elseif ($failure)
608
	{
609
		// Load any session data we might have...
610
		if (!isset($_POST['ftp_username']) && isset($_SESSION['installer_temp_ftp']))
611
		{
612
			$_POST['ftp_server'] = $_SESSION['installer_temp_ftp']['server'];
613
			$_POST['ftp_port'] = $_SESSION['installer_temp_ftp']['port'];
614
			$_POST['ftp_username'] = $_SESSION['installer_temp_ftp']['username'];
615
			$_POST['ftp_password'] = $_SESSION['installer_temp_ftp']['password'];
616
			$_POST['ftp_path'] = $_SESSION['installer_temp_ftp']['path'];
617
		}
618
619
		$incontext['ftp_errors'] = array();
620
		require_once('Sources/Class-Package.php');
621 View Code Duplication
		if (isset($_POST['ftp_username']))
622
		{
623
			$ftp = new ftp_connection($_POST['ftp_server'], $_POST['ftp_port'], $_POST['ftp_username'], $_POST['ftp_password']);
624
625
			if ($ftp->error === false)
626
			{
627
				// Try it without /home/abc just in case they messed up.
628
				if (!$ftp->chdir($_POST['ftp_path']))
629
				{
630
					$incontext['ftp_errors'][] = $ftp->last_message;
631
					$ftp->chdir(preg_replace('~^/home[2]?/[^/]+?~', '', $_POST['ftp_path']));
632
				}
633
			}
634
		}
635
636
		if (!isset($ftp) || $ftp->error !== false)
637
		{
638
			if (!isset($ftp))
639
				$ftp = new ftp_connection(null);
640
			// Save the error so we can mess with listing...
641
			elseif ($ftp->error !== false && empty($incontext['ftp_errors']) && !empty($ftp->last_message))
642
				$incontext['ftp_errors'][] = $ftp->last_message;
643
644
			list ($username, $detect_path, $found_path) = $ftp->detect_path(dirname(__FILE__));
645
646
			if (empty($_POST['ftp_path']) && $found_path)
647
				$_POST['ftp_path'] = $detect_path;
648
649
			if (!isset($_POST['ftp_username']))
650
				$_POST['ftp_username'] = $username;
651
652
			// Set the username etc, into context.
653
			$incontext['ftp'] = array(
654
				'server' => isset($_POST['ftp_server']) ? $_POST['ftp_server'] : 'localhost',
655
				'port' => isset($_POST['ftp_port']) ? $_POST['ftp_port'] : '21',
656
				'username' => isset($_POST['ftp_username']) ? $_POST['ftp_username'] : '',
657
				'path' => isset($_POST['ftp_path']) ? $_POST['ftp_path'] : '/',
658
				'path_msg' => !empty($found_path) ? $txt['ftp_path_found_info'] : $txt['ftp_path_info'],
659
			);
660
661
			return false;
662
		}
663
		else
664
		{
665
			$_SESSION['installer_temp_ftp'] = array(
666
				'server' => $_POST['ftp_server'],
667
				'port' => $_POST['ftp_port'],
668
				'username' => $_POST['ftp_username'],
669
				'password' => $_POST['ftp_password'],
670
				'path' => $_POST['ftp_path']
671
			);
672
673
			$failed_files_updated = array();
674
675
			foreach ($failed_files as $file)
676
			{
677
				if (!is_writable(dirname(__FILE__) . '/' . $file))
678
					$ftp->chmod($file, 0755);
679
				if (!is_writable(dirname(__FILE__) . '/' . $file))
680
					$ftp->chmod($file, 0777);
681
				if (!is_writable(dirname(__FILE__) . '/' . $file))
682
				{
683
					$failed_files_updated[] = $file;
684
					$incontext['ftp_errors'][] = rtrim($ftp->last_message) . ' -> ' . $file . "\n";
685
				}
686
			}
687
688
			$ftp->close();
689
690
			// Are there any errors left?
691
			if (count($failed_files_updated) >= 1)
692
			{
693
				// Guess there are...
694
				$incontext['failed_files'] = $failed_files_updated;
695
696
				// Set the username etc, into context.
697
				$incontext['ftp'] = $_SESSION['installer_temp_ftp'] += array(
698
					'path_msg' => $txt['ftp_path_info'],
699
				);
700
701
				return false;
702
			}
703
		}
704
	}
705
706
	return true;
707
}
708
709
function DatabaseSettings()
710
{
711
	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...
712
	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...
713
714
	$incontext['sub_template'] = 'database_settings';
715
	$incontext['page_title'] = $txt['db_settings'];
716
	$incontext['continue'] = 1;
717
718
	// Set up the defaults.
719
	$incontext['db']['server'] = 'localhost';
720
	$incontext['db']['user'] = '';
721
	$incontext['db']['name'] = '';
722
	$incontext['db']['pass'] = '';
723
	$incontext['db']['type'] = '';
724
	$incontext['supported_databases'] = array();
725
726
	$foundOne = false;
727
	foreach ($databases as $key => $db)
728
	{
729
		// Override with the defaults for this DB if appropriate.
730
		if ($db['supported'])
731
		{
732
			$incontext['supported_databases'][$key] = $db;
733
734
			if (!$foundOne)
735
			{
736
				if (isset($db['default_host']))
737
					$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...
738
				if (isset($db['default_user']))
739
				{
740
					$incontext['db']['user'] = ini_get($db['default_user']);
741
					$incontext['db']['name'] = ini_get($db['default_user']);
742
				}
743
				if (isset($db['default_password']))
744
					$incontext['db']['pass'] = ini_get($db['default_password']);
745
746
				// For simplicity and less confusion, leave the port blank by default
747
				$incontext['db']['port'] = '';
748
749
				$incontext['db']['type'] = $key;
750
				$foundOne = true;
751
			}
752
		}
753
	}
754
755
	// Override for repost.
756
	if (isset($_POST['db_user']))
757
	{
758
		$incontext['db']['user'] = $_POST['db_user'];
759
		$incontext['db']['name'] = $_POST['db_name'];
760
		$incontext['db']['server'] = $_POST['db_server'];
761
		$incontext['db']['prefix'] = $_POST['db_prefix'];
762
763
		if (!empty($_POST['db_port']))
764
			$incontext['db']['port'] = $_POST['db_port'];
765
	}
766
	else
767
	{
768
		$incontext['db']['prefix'] = 'smf_';
769
	}
770
771
	// Are we submitting?
772
	if (isset($_POST['db_type']))
773
	{
774
		// What type are they trying?
775
		$db_type = preg_replace('~[^A-Za-z0-9]~', '', $_POST['db_type']);
776
		$db_prefix = $_POST['db_prefix'];
777
		// Validate the prefix.
778
		$valid_prefix = $databases[$db_type]['validate_prefix']($db_prefix);
779
780
		if ($valid_prefix !== true)
781
		{
782
			$incontext['error'] = $valid_prefix;
783
			return false;
784
		}
785
786
		// Take care of these variables...
787
		$vars = array(
788
			'db_type' => $db_type,
789
			'db_name' => $_POST['db_name'],
790
			'db_user' => $_POST['db_user'],
791
			'db_passwd' => isset($_POST['db_passwd']) ? $_POST['db_passwd'] : '',
792
			'db_server' => $_POST['db_server'],
793
			'db_prefix' => $db_prefix,
794
			// The cookiename is special; we want it to be the same if it ever needs to be reinstalled with the same info.
795
			'cookiename' => 'SMFCookie' . abs(crc32($_POST['db_name'] . preg_replace('~[^A-Za-z0-9_$]~', '', $_POST['db_prefix'])) % 1000),
796
		);
797
798
		// Only set the port if we're not using the default
799
		if (!empty($_POST['db_port']))
800
		{
801
			// For MySQL, we can get the "default port" from PHP. PostgreSQL has no such option though.
802
			if (($db_type == 'mysql' || $db_type == 'mysqli') && $_POST['db_port'] != ini_get($db_type . '.default_port'))
803
				$vars['db_port'] = (int) $_POST['db_port'];
804 View Code Duplication
			elseif ($db_type == 'postgresql' && $_POST['db_port'] != 5432)
805
				$vars['db_port'] = (int) $_POST['db_port'];
806
		}
807
808
		// God I hope it saved!
809 View Code Duplication
		if (!updateSettingsFile($vars) && substr(__FILE__, 1, 2) == ':\\')
810
		{
811
			$incontext['error'] = $txt['error_windows_chmod'];
812
			return false;
813
		}
814
815
		// Make sure it works.
816
		require(dirname(__FILE__) . '/Settings.php');
817
818
		if (empty($sourcedir))
819
			$sourcedir = dirname(__FILE__) . '/Sources';
820
821
		// Better find the database file!
822
		if (!file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php'))
823
		{
824
			$incontext['error'] = sprintf($txt['error_db_file'], 'Subs-Db-' . $db_type . '.php');
825
			return false;
826
		}
827
828
		// Now include it for database functions!
829
		if (!defined('SMF'))
830
			define('SMF', 1);
831
832
		$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...
833
		if (empty($smcFunc))
834
			$smcFunc = array();
835
836
			require_once($sourcedir . '/Subs-Db-' . $db_type . '.php');
837
838
		// What - running PHP4? The shame!
839
		if (version_compare(PHP_VERSION, '5', '<'))
840
			require_once($sourcedir . '/Subs-Compat.php');
841
842
		// Attempt a connection.
843
		$needsDB = !empty($databases[$db_type]['always_has_db']);
844
		$db_connection = smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, $db_prefix, array('non_fatal' => true, 'dont_select_db' => !$needsDB));
845
846
		// No dice?  Let's try adding the prefix they specified, just in case they misread the instructions ;)
847
		if ($db_connection == null)
848
		{
849
			$db_error = @$smcFunc['db_error']();
850
851
			$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));
852
			if ($db_connection != null)
853
			{
854
				$db_user = $_POST['db_prefix'] . $db_user;
855
				updateSettingsFile(array('db_user' => $db_user));
856
			}
857
		}
858
859
		// Still no connection?  Big fat error message :P.
860
		if (!$db_connection)
861
		{
862
			$incontext['error'] = $txt['error_db_connect'] . '<div style="margin: 2.5ex; font-family: monospace;"><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...
863
			return false;
864
		}
865
866
		// Do they meet the install requirements?
867
		// @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...
868
		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...
869
		{
870
			$incontext['error'] = $txt['error_db_too_low'];
871
			return false;
872
		}
873
874
		// Let's try that database on for size... assuming we haven't already lost the opportunity.
875
		if ($db_name != '' && !$needsDB)
876
		{
877
			$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...
878
				CREATE DATABASE IF NOT EXISTS `$db_name`",
879
				array(
880
					'security_override' => true,
881
					'db_error_skip' => true,
882
				),
883
				$db_connection
884
			);
885
886
			// Okay, let's try the prefix if it didn't work...
887
			if (!$smcFunc['db_select_db']($db_name, $db_connection) && $db_name != '')
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 $_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...
890
					CREATE DATABASE IF NOT EXISTS `$_POST[db_prefix]$db_name`",
891
					array(
892
						'security_override' => true,
893
						'db_error_skip' => true,
894
					),
895
					$db_connection
896
				);
897
898
				if ($smcFunc['db_select_db']($_POST['db_prefix'] . $db_name, $db_connection))
899
				{
900
					$db_name = $_POST['db_prefix'] . $db_name;
901
					updateSettingsFile(array('db_name' => $db_name));
902
				}
903
			}
904
905
			// Okay, now let's try to connect...
906
			if (!$smcFunc['db_select_db']($db_name, $db_connection))
907
			{
908
				$incontext['error'] = sprintf($txt['error_db_database'], $db_name);
909
				return false;
910
			}
911
		}
912
913
		return true;
914
	}
915
916
	return false;
917
}
918
919
// Let's start with basic forum type settings.
920
function ForumSettings()
921
{
922
	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...
923
924
	$incontext['sub_template'] = 'forum_settings';
925
	$incontext['page_title'] = $txt['install_settings'];
926
927
	// Let's see if we got the database type correct.
928
	if (isset($_POST['db_type'], $databases[$_POST['db_type']]))
929
		$db_type = $_POST['db_type'];
930
931
	// Else we'd better be able to get the connection.
932
	else
933
		load_database();
934
935
	$db_type = isset($_POST['db_type']) ? $_POST['db_type'] : $db_type;
936
937
	// What host and port are we on?
938
	$host = empty($_SERVER['HTTP_HOST']) ? $_SERVER['SERVER_NAME'] . (empty($_SERVER['SERVER_PORT']) || $_SERVER['SERVER_PORT'] == '80' ? '' : ':' . $_SERVER['SERVER_PORT']) : $_SERVER['HTTP_HOST'];
939
940
		$secure = false;
941
942 View Code Duplication
		if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on')
943
			$secure = true;
944
		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')
945
			$secure = true;
946
947
	// Now, to put what we've learned together... and add a path.
948
	$incontext['detected_url'] = 'http' . ($secure ? 's' : '') . '://' . $host . substr($_SERVER['PHP_SELF'], 0, strrpos($_SERVER['PHP_SELF'], '/'));
949
950
	// Check if the database sessions will even work.
951
	$incontext['test_dbsession'] = (ini_get('session.auto_start') != 1);
952
	$incontext['utf8_default'] = $databases[$db_type]['utf8_default'];
953
	$incontext['utf8_required'] = $databases[$db_type]['utf8_required'];
954
955
	$incontext['continue'] = 1;
956
957
	// Setup the SSL checkbox...
958
	$incontext['ssl_chkbx_protected'] = false;
959
	$incontext['ssl_chkbx_checked'] = false;
960
961
	// If redirect in effect, force ssl ON
962
	require_once(dirname(__FILE__) . '/Sources/Subs.php');
963
	if (https_redirect_active($incontext['detected_url'])) {
964
		$incontext['ssl_chkbx_protected'] = true;
965
		$incontext['ssl_chkbx_checked'] = true;
966
		$_POST['force_ssl'] = true;
967
	}
968
	// If no cert, make sure ssl stays OFF
969
	if (!ssl_cert_found($incontext['detected_url'])) {
970
		$incontext['ssl_chkbx_protected'] = true;
971
		$incontext['ssl_chkbx_checked'] = false;
972
	}
973
974
	// Submitting?
975
	if (isset($_POST['boardurl']))
976
	{
977 View Code Duplication
		if (substr($_POST['boardurl'], -10) == '/index.php')
978
			$_POST['boardurl'] = substr($_POST['boardurl'], 0, -10);
979
		elseif (substr($_POST['boardurl'], -1) == '/')
980
			$_POST['boardurl'] = substr($_POST['boardurl'], 0, -1);
981 View Code Duplication
		if (substr($_POST['boardurl'], 0, 7) != 'http://' && substr($_POST['boardurl'], 0, 7) != 'file://' && substr($_POST['boardurl'], 0, 8) != 'https://')
982
			$_POST['boardurl'] = 'http://' . $_POST['boardurl'];
983
984
		//Make sure boardurl is aligned with ssl setting
985
		if (empty($_POST['force_ssl']))
986
			$_POST['boardurl'] = strtr($_POST['boardurl'], array('https://' => 'http://'));
987
		else
988
			$_POST['boardurl'] = strtr($_POST['boardurl'], array('http://' => 'https://'));
989
990
		// Save these variables.
991
		$vars = array(
992
			'boardurl' => $_POST['boardurl'],
993
			'boarddir' => addslashes(dirname(__FILE__)),
994
			'sourcedir' => addslashes(dirname(__FILE__)) . '/Sources',
995
			'cachedir' => addslashes(dirname(__FILE__)) . '/cache',
996
			'packagesdir' => addslashes(dirname(__FILE__)) . '/Packages',
997
			'tasksdir' => addslashes(dirname(__FILE__)) . '/Sources/tasks',
998
			'mbname' => strtr($_POST['mbname'], array('\"' => '"')),
999
			'language' => substr($_SESSION['installer_temp_lang'], 8, -4),
1000
			'image_proxy_secret' => substr(sha1(mt_rand()), 0, 20),
1001
			'image_proxy_enabled' => !empty($_POST['force_ssl']),
1002
		);
1003
1004
		// Must save!
1005 View Code Duplication
		if (!updateSettingsFile($vars) && substr(__FILE__, 1, 2) == ':\\')
1006
		{
1007
			$incontext['error'] = $txt['error_windows_chmod'];
1008
			return false;
1009
		}
1010
1011
		// Make sure it works.
1012
		require(dirname(__FILE__) . '/Settings.php');
1013
1014
		// UTF-8 requires a setting to override the language charset.
1015
		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'])))
1016
		{
1017
			if (!$databases[$db_type]['utf8_support']())
1018
			{
1019
				$incontext['error'] = sprintf($txt['error_utf8_support']);
1020
				return false;
1021
			}
1022
1023
			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...
1024
			{
1025
				$incontext['error'] = sprintf($txt['error_utf8_version'], $databases[$db_type]['utf8_version']);
1026
				return false;
1027
			}
1028
			else
1029
				// Set the character set here.
1030
				updateSettingsFile(array('db_character_set' => 'utf8'));
1031
		}
1032
1033
		// Good, skip on.
1034
		return true;
1035
	}
1036
1037
	return false;
1038
}
1039
1040
// Step one: Do the SQL thang.
1041
function DatabasePopulation()
1042
{
1043
	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...
1044
1045
	$incontext['sub_template'] = 'populate_database';
1046
	$incontext['page_title'] = $txt['db_populate'];
1047
	$incontext['continue'] = 1;
1048
1049
	// Already done?
1050
	if (isset($_POST['pop_done']))
1051
		return true;
1052
1053
	// Reload settings.
1054
	require(dirname(__FILE__) . '/Settings.php');
1055
	load_database();
1056
1057
	// Before running any of the queries, let's make sure another version isn't already installed.
1058
	$result = $smcFunc['db_query']('', '
1059
		SELECT variable, value
1060
		FROM {db_prefix}settings',
1061
		array(
1062
			'db_error_skip' => true,
1063
		)
1064
	);
1065
	$newSettings = array();
1066
	$modSettings = array();
1067
	if ($result !== false)
1068
	{
1069 View Code Duplication
		while ($row = $smcFunc['db_fetch_assoc']($result))
1070
			$modSettings[$row['variable']] = $row['value'];
1071
		$smcFunc['db_free_result']($result);
1072
1073
		// Do they match?  If so, this is just a refresh so charge on!
1074
		if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] != $GLOBALS['current_smf_version'])
1075
		{
1076
			$incontext['error'] = $txt['error_versions_do_not_match'];
1077
			return false;
1078
		}
1079
	}
1080
	$modSettings['disableQueryCheck'] = true;
1081
1082
	// If doing UTF8, select it. PostgreSQL requires passing it as a string...
1083 View Code Duplication
	if (!empty($db_character_set) && $db_character_set == 'utf8' && !empty($databases[$db_type]['utf8_support']))
1084
		$smcFunc['db_query']('', '
1085
			SET NAMES {string:utf8}',
1086
			array(
1087
				'db_error_skip' => true,
1088
				'utf8' => 'utf8',
1089
			)
1090
		);
1091
1092
	// Windows likes to leave the trailing slash, which yields to C:\path\to\SMF\/attachments...
1093
	if (substr(__DIR__, -1) == '\\')
1094
		$attachdir = __DIR__ . 'attachments';
1095
	else
1096
		$attachdir = __DIR__ . '/attachments';
1097
1098
	$replaces = array(
1099
		'{$db_prefix}' => $db_prefix,
1100
		'{$attachdir}' => json_encode(array(1 => $smcFunc['db_escape_string']($attachdir))),
1101
		'{$boarddir}' => $smcFunc['db_escape_string'](dirname(__FILE__)),
1102
		'{$boardurl}' => $boardurl,
1103
		'{$enableCompressedOutput}' => isset($_POST['compress']) ? '1' : '0',
1104
		'{$databaseSession_enable}' => isset($_POST['dbsession']) ? '1' : '0',
1105
		'{$smf_version}' => $GLOBALS['current_smf_version'],
1106
		'{$current_time}' => time(),
1107
		'{$sched_task_offset}' => 82800 + mt_rand(0, 86399),
1108
		'{$registration_method}' => isset($_POST['reg_mode']) ? $_POST['reg_mode'] : 0,
1109
	);
1110
1111
	foreach ($txt as $key => $value)
1112
	{
1113
		if (substr($key, 0, 8) == 'default_')
1114
			$replaces['{$' . $key . '}'] = $smcFunc['db_escape_string']($value);
1115
	}
1116
	$replaces['{$default_reserved_names}'] = strtr($replaces['{$default_reserved_names}'], array('\\\\n' => '\\n'));
1117
1118
	// MySQL-specific stuff - storage engine and UTF8 handling
1119
	if (substr($db_type, 0, 5) == 'mysql')
1120
	{
1121
		// Just in case the query fails for some reason...
1122
		$engines = array();
1123
1124
		// Figure out storage engines - what do we have, etc.
1125
		$get_engines = $smcFunc['db_query']('', 'SHOW ENGINES', array());
1126
1127 View Code Duplication
		while ($row = $smcFunc['db_fetch_assoc']($get_engines))
1128
		{
1129
			if ($row['Support'] == 'YES' || $row['Support'] == 'DEFAULT')
1130
				$engines[] = $row['Engine'];
1131
		}
1132
1133
		// Done with this now
1134
		$smcFunc['db_free_result']($get_engines);
1135
1136
		// InnoDB is better, so use it if possible...
1137
		$has_innodb = in_array('InnoDB', $engines);
1138
		$replaces['{$engine}'] = $has_innodb ? 'InnoDB' : 'MyISAM';
1139
		$replaces['{$memory}'] = (!$has_innodb && in_array('MEMORY', $engines)) ? 'MEMORY' : $replaces['{$engine}'];
1140
1141
		// If the UTF-8 setting was enabled, add it to the table definitions.
1142
		if (!empty($databases[$db_type]['utf8_support']) && (!empty($databases[$db_type]['utf8_required']) || isset($_POST['utf8'])))
1143
		{
1144
			$replaces['{$engine}'] .= ' DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci';
1145
			$replaces['{$memory}'] .= ' DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci';
1146
		}
1147
1148
		// One last thing - if we don't have InnoDB, we can't do transactions...
1149
		if (!$has_innodb)
1150
		{
1151
			$replaces['START TRANSACTION;'] = '';
1152
			$replaces['COMMIT;'] = '';
1153
		}
1154
	}
1155
	else
1156
	{
1157
		$has_innodb = false;
1158
	}
1159
1160
	// Read in the SQL.  Turn this on and that off... internationalize... etc.
1161
	$type = ($db_type == 'mysqli' ? 'mysql' : $db_type);
1162
	$sql_lines = explode("\n", strtr(implode(' ', file(dirname(__FILE__) . '/install_' . $GLOBALS['db_script_version'] . '_' . $type . '.sql')), $replaces));
1163
1164
	// Execute the SQL.
1165
	$current_statement = '';
1166
	$exists = array();
1167
	$incontext['failures'] = array();
1168
	$incontext['sql_results'] = array(
1169
		'tables' => 0,
1170
		'inserts' => 0,
1171
		'table_dups' => 0,
1172
		'insert_dups' => 0,
1173
	);
1174
	foreach ($sql_lines as $count => $line)
1175
	{
1176
		// No comments allowed!
1177
		if (substr(trim($line), 0, 1) != '#')
1178
			$current_statement .= "\n" . rtrim($line);
1179
1180
		// Is this the end of the query string?
1181
		if (empty($current_statement) || (preg_match('~;[\s]*$~s', $line) == 0 && $count != count($sql_lines)))
1182
			continue;
1183
1184
		// Does this table already exist?  If so, don't insert more data into it!
1185
		if (preg_match('~^\s*INSERT INTO ([^\s\n\r]+?)~', $current_statement, $match) != 0 && in_array($match[1], $exists))
1186
		{
1187
			preg_match_all('~\)[,;]~', $current_statement, $matches);
1188 View Code Duplication
			if (!empty($matches[0]))
1189
				$incontext['sql_results']['insert_dups'] += count($matches[0]);
1190
			else
1191
				$incontext['sql_results']['insert_dups']++;
1192
1193
			$current_statement = '';
1194
			continue;
1195
		}
1196
1197
		if ($smcFunc['db_query']('', $current_statement, array('security_override' => true, 'db_error_skip' => true), $db_connection) === false)
1198
		{
1199
			// Use the appropriate function based on the DB type
1200
			if ($db_type == 'mysql' || $db_type == 'mysqli')
1201
				$db_errorno = $db_type . '_errno';
1202
1203
			// Error 1050: Table already exists!
1204
			// @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...
1205
			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...
1206
			{
1207
				$exists[] = $match[1];
1208
				$incontext['sql_results']['table_dups']++;
1209
			}
1210
			// Don't error on duplicate indexes (or duplicate operators in PostgreSQL.)
1211
			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)))
1212
			{
1213
				// MySQLi requires a connection object. It's optional with MySQL and Postgres
1214
				$incontext['failures'][$count] = $smcFunc['db_error']($db_connection);
1215
			}
1216
		}
1217
		else
1218
		{
1219
			if (preg_match('~^\s*CREATE TABLE ([^\s\n\r]+?)~', $current_statement, $match) == 1)
1220
				$incontext['sql_results']['tables']++;
1221
			elseif (preg_match('~^\s*INSERT INTO ([^\s\n\r]+?)~', $current_statement, $match) == 1)
1222
			{
1223
				preg_match_all('~\)[,;]~', $current_statement, $matches);
1224 View Code Duplication
				if (!empty($matches[0]))
1225
					$incontext['sql_results']['inserts'] += count($matches[0]);
1226
				else
1227
					$incontext['sql_results']['inserts']++;
1228
			}
1229
		}
1230
1231
		$current_statement = '';
1232
1233
		// Wait, wait, I'm still working here!
1234
		set_time_limit(60);
1235
	}
1236
1237
	// Sort out the context for the SQL.
1238
	foreach ($incontext['sql_results'] as $key => $number)
1239
	{
1240
		if ($number == 0)
1241
			unset($incontext['sql_results'][$key]);
1242
		else
1243
			$incontext['sql_results'][$key] = sprintf($txt['db_populate_' . $key], $number);
1244
	}
1245
1246
	// Make sure UTF will be used globally.
1247
	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'])))
1248
		$newSettings[] = array('global_character_set', 'UTF-8');
1249
1250
	// Auto-detect local & global cookie settings
1251
	$url_parts = parse_url($boardurl);
1252
	if ($url_parts !== false)
1253
	{
1254
		unset($globalCookies, $globalCookiesDomain, $localCookies);
1255
1256
		// Look for subdomain, if found, set globalCookie settings
1257
		// Don't bother looking if you have an ip address for host
1258
		if (!empty($url_parts['host']) && (filter_var($url_parts['host'], FILTER_VALIDATE_IP) === false))
1259
		{
1260
			// www isn't really a subdomain in this sense, so strip it out
1261
			$url_parts['host'] = str_ireplace('www.', '', $url_parts['host']);
1262
			$pos1 = strrpos($url_parts['host'], '.');
1263
			if ($pos1 !== false)
1264
			{
1265
				// 2nd period from the right indicates you have a subdomain
1266
				$pos2 = strrpos(substr($url_parts['host'], 0, $pos1 - 1), '.');
1267
				if ($pos2 !== false)
1268
				{
1269
					$globalCookies = '1';
1270
					$globalCookiesDomain = substr($url_parts['host'], $pos2 + 1);
1271
				}
1272
			}
1273
		}
1274
1275
		// Look for subfolder, if found, set localCookie
1276
		// Checking for len > 1 ensures you don't have just a slash...
1277
		if (!empty($url_parts['path']) && strlen($url_parts['path']) > 1)
1278
			$localCookies = '1';
1279
1280
		if (isset($globalCookies))
1281
			$newSettings[] = array('globalCookies', $globalCookies);
1282
		if (isset($globalCookiesDomain))
1283
			$newSettings[] = array('globalCookiesDomain', $globalCookiesDomain);
1284
		if (isset($localCookies))
1285
			$newSettings[] = array('localCookies', $localCookies);
1286
	}
1287
1288
	// Are we allowing stat collection?
1289
	if (!empty($_POST['stats']) && substr($boardurl, 0, 16) != 'http://localhost' && empty($modSettings['allow_sm_stats']) && empty($modSettings['enable_sm_stats']))
1290
	{
1291
		$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...
1292
1293
		// Attempt to register the site etc.
1294
		$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...
1295 View Code Duplication
		if ($fp)
1296
		{
1297
			$out = 'GET /smf/stats/register_stats.php?site=' . base64_encode($boardurl) . ' HTTP/1.1' . "\r\n";
1298
			$out .= 'Host: www.simplemachines.org' . "\r\n";
1299
			$out .= 'Connection: Close' . "\r\n\r\n";
1300
			fwrite($fp, $out);
1301
1302
			$return_data = '';
1303
			while (!feof($fp))
1304
				$return_data .= fgets($fp, 128);
1305
1306
			fclose($fp);
1307
1308
			// Get the unique site ID.
1309
			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...
1310
1311
			if (!empty($ID[1]))
1312
				$smcFunc['db_insert']('replace',
1313
					$db_prefix . 'settings',
1314
					array('variable' => 'string', 'value' => 'string'),
1315
					array(
1316
						array('sm_stats_key', $ID[1]),
1317
						array('enable_sm_stats', 1),
1318
					),
1319
					array('variable')
1320
				);
1321
		}
1322
	}
1323
	// Don't remove stat collection unless we unchecked the box for real, not from the loop.
1324 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...
1325
		$smcFunc['db_query']('', '
1326
			DELETE FROM {db_prefix}settings
1327
			WHERE variable = {string:enable_sm_stats}',
1328
			array(
1329
				'enable_sm_stats' => 'enable_sm_stats',
1330
				'db_error_skip' => true,
1331
			)
1332
		);
1333
1334
	// Are we enabling SSL?
1335
	if (!empty($_POST['force_ssl']))
1336
		$newSettings[] = array('force_ssl', 1);
1337
1338
	// Setting a timezone is required.
1339
	if (!isset($modSettings['default_timezone']) && function_exists('date_default_timezone_set'))
1340
	{
1341
		// Get PHP's default timezone, if set
1342
		$ini_tz = ini_get('date.timezone');
1343
		if (!empty($ini_tz))
1344
			$timezone_id = $ini_tz;
1345
		else
1346
			$timezone_id = '';
1347
1348
		// If date.timezone is unset, invalid, or just plain weird, make a best guess
1349 View Code Duplication
		if (!in_array($timezone_id, timezone_identifiers_list()))
1350
		{
1351
			$server_offset = @mktime(0, 0, 0, 1, 1, 1970);
1352
			$timezone_id = timezone_name_from_abbr('', $server_offset, 0);
1353
		}
1354
1355
		if (date_default_timezone_set($timezone_id))
1356
			$newSettings[] = array('default_timezone', $timezone_id);
1357
	}
1358
1359
	if (!empty($newSettings))
1360
	{
1361
		$smcFunc['db_insert']('replace',
1362
			'{db_prefix}settings',
1363
			array('variable' => 'string-255', 'value' => 'string-65534'),
1364
			$newSettings,
1365
			array('variable')
1366
		);
1367
	}
1368
1369
	// Let's optimize those new tables, but not on InnoDB, ok?
1370
	if (!$has_innodb)
1371
	{
1372
		db_extend();
1373
		$tables = $smcFunc['db_list_tables']($db_name, $db_prefix . '%');
1374
		foreach ($tables as $table)
1375
		{
1376
			$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...
1377
1378
			if (!empty($db_messed))
1379
			{
1380
				$incontext['failures'][-1] = $smcFunc['db_error']();
1381
				break;
1382
			}
1383
		}
1384
	}
1385
1386
	// MySQL specific stuff
1387
	if (substr($db_type, 0, 5) != 'mysql')
1388
		return false;
1389
1390
	// Find database user privileges.
1391
	$privs = array();
1392
	$get_privs = $smcFunc['db_query']('', 'SHOW PRIVILEGES', array());
1393
	while ($row = $smcFunc['db_fetch_assoc']($get_privs))
1394
	{
1395
		if ($row['Privilege'] == 'Alter')
1396
			$privs[] = $row['Privilege'];
1397
	}
1398
	$smcFunc['db_free_result']($get_privs);
1399
1400
	// Check for the ALTER privilege.
1401
	if (!empty($databases[$db_type]['alter_support']) && !in_array('Alter', $privs))
1402
	{
1403
		$incontext['error'] = $txt['error_db_alter_priv'];
1404
		return false;
1405
	}
1406
1407
	if (!empty($exists))
1408
	{
1409
		$incontext['page_title'] = $txt['user_refresh_install'];
1410
		$incontext['was_refresh'] = true;
1411
	}
1412
1413
	return false;
1414
}
1415
1416
// Ask for the administrator login information.
1417
function AdminAccount()
1418
{
1419
	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...
1420
1421
	$incontext['sub_template'] = 'admin_account';
1422
	$incontext['page_title'] = $txt['user_settings'];
1423
	$incontext['continue'] = 1;
1424
1425
	// Skipping?
1426
	if (!empty($_POST['skip']))
1427
		return true;
1428
1429
	// Need this to check whether we need the database password.
1430
	require(dirname(__FILE__) . '/Settings.php');
1431
	load_database();
1432
1433
	require_once($sourcedir . '/Subs-Auth.php');
1434
1435
	require_once($sourcedir . '/Subs.php');
1436
1437
	// Reload settings & set some global funcs
1438
	require_once($sourcedir . '/Load.php');
1439
	reloadSettings();
1440
1441
	// We need this to properly hash the password for Admin
1442 View Code Duplication
	$smcFunc['strtolower'] = $db_character_set != 'utf8' && $txt['lang_character_set'] != 'UTF-8' ? 'strtolower' : function($string) {
1443
			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...
1444
			if (function_exists('mb_strtolower'))
1445
				return mb_strtolower($string, 'UTF-8');
1446
			require_once($sourcedir . '/Subs-Charset.php');
1447
			return utf8_strtolower($string);
1448
		};
1449
1450
	if (!isset($_POST['username']))
1451
		$_POST['username'] = '';
1452
	if (!isset($_POST['email']))
1453
		$_POST['email'] = '';
1454
	if (!isset($_POST['server_email']))
1455
		$_POST['server_email'] = '';
1456
1457
	$incontext['username'] = htmlspecialchars(stripslashes($_POST['username']));
1458
	$incontext['email'] = htmlspecialchars(stripslashes($_POST['email']));
1459
	$incontext['server_email'] = htmlspecialchars(stripslashes($_POST['server_email']));
1460
1461
	$incontext['require_db_confirm'] = empty($db_type);
1462
1463
	// Only allow skipping if we think they already have an account setup.
1464
	$request = $smcFunc['db_query']('', '
1465
		SELECT id_member
1466
		FROM {db_prefix}members
1467
		WHERE id_group = {int:admin_group} OR FIND_IN_SET({int:admin_group}, additional_groups) != 0
1468
		LIMIT 1',
1469
		array(
1470
			'db_error_skip' => true,
1471
			'admin_group' => 1,
1472
		)
1473
	);
1474
	if ($smcFunc['db_num_rows']($request) != 0)
1475
		$incontext['skip'] = 1;
1476
	$smcFunc['db_free_result']($request);
1477
1478
	// Trying to create an account?
1479
	if (isset($_POST['password1']) && !empty($_POST['contbutt']))
1480
	{
1481
		// Wrong password?
1482
		if ($incontext['require_db_confirm'] && $_POST['password3'] != $db_passwd)
1483
		{
1484
			$incontext['error'] = $txt['error_db_connect'];
1485
			return false;
1486
		}
1487
		// Not matching passwords?
1488
		if ($_POST['password1'] != $_POST['password2'])
1489
		{
1490
			$incontext['error'] = $txt['error_user_settings_again_match'];
1491
			return false;
1492
		}
1493
		// No password?
1494
		if (strlen($_POST['password1']) < 4)
1495
		{
1496
			$incontext['error'] = $txt['error_user_settings_no_password'];
1497
			return false;
1498
		}
1499
		if (!file_exists($sourcedir . '/Subs.php'))
1500
		{
1501
			$incontext['error'] = $txt['error_subs_missing'];
1502
			return false;
1503
		}
1504
1505
		// Update the webmaster's email?
1506
		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...
1507
			updateSettingsFile(array('webmaster_email' => $_POST['server_email']));
1508
1509
		// Work out whether we're going to have dodgy characters and remove them.
1510
		$invalid_characters = preg_match('~[<>&"\'=\\\]~', $_POST['username']) != 0;
1511
		$_POST['username'] = preg_replace('~[<>&"\'=\\\]~', '', $_POST['username']);
1512
1513
		$result = $smcFunc['db_query']('', '
1514
			SELECT id_member, password_salt
1515
			FROM {db_prefix}members
1516
			WHERE member_name = {string:username} OR email_address = {string:email}
1517
			LIMIT 1',
1518
			array(
1519
				'username' => stripslashes($_POST['username']),
1520
				'email' => stripslashes($_POST['email']),
1521
				'db_error_skip' => true,
1522
			)
1523
		);
1524
		if ($smcFunc['db_num_rows']($result) != 0)
1525
		{
1526
			list ($incontext['member_id'], $incontext['member_salt']) = $smcFunc['db_fetch_row']($result);
1527
			$smcFunc['db_free_result']($result);
1528
1529
			$incontext['account_existed'] = $txt['error_user_settings_taken'];
1530
		}
1531
		elseif ($_POST['username'] == '' || strlen($_POST['username']) > 25)
1532
		{
1533
			// Try the previous step again.
1534
			$incontext['error'] = $_POST['username'] == '' ? $txt['error_username_left_empty'] : $txt['error_username_too_long'];
1535
			return false;
1536
		}
1537
		elseif ($invalid_characters || $_POST['username'] == '_' || $_POST['username'] == '|' || strpos($_POST['username'], '[code') !== false || strpos($_POST['username'], '[/code') !== false)
1538
		{
1539
			// Try the previous step again.
1540
			$incontext['error'] = $txt['error_invalid_characters_username'];
1541
			return false;
1542
		}
1543 View Code Duplication
		elseif (empty($_POST['email']) || !filter_var(stripslashes($_POST['email']), FILTER_VALIDATE_EMAIL) || strlen(stripslashes($_POST['email'])) > 255)
1544
		{
1545
			// One step back, this time fill out a proper admin email address.
1546
			$incontext['error'] = sprintf($txt['error_valid_admin_email_needed'], $_POST['username']);
1547
			return false;
1548
		}
1549 View Code Duplication
		elseif (empty($_POST['server_email']) || !filter_var(stripslashes($_POST['server_email']), FILTER_VALIDATE_EMAIL) || strlen(stripslashes($_POST['server_email'])) > 255)
1550
		{
1551
			// One step back, this time fill out a proper admin email address.
1552
			$incontext['error'] = $txt['error_valid_server_email_needed'];
1553
			return false;
1554
		}
1555
		elseif ($_POST['username'] != '')
1556
		{
1557
			$incontext['member_salt'] = substr(md5(mt_rand()), 0, 4);
1558
1559
			// Format the username properly.
1560
			$_POST['username'] = preg_replace('~[\t\n\r\x0B\0\xA0]+~', ' ', $_POST['username']);
1561
			$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...
1562
1563
			$_POST['password1'] = hash_password(stripslashes($_POST['username']), stripslashes($_POST['password1']));
1564
1565
			$incontext['member_id'] = $smcFunc['db_insert']('',
1566
				$db_prefix . 'members',
1567
				array(
1568
					'member_name' => 'string-25', 'real_name' => 'string-25', 'passwd' => 'string', 'email_address' => 'string',
1569
					'id_group' => 'int', 'posts' => 'int', 'date_registered' => 'int',
1570
					'password_salt' => 'string', 'lngfile' => 'string', 'personal_text' => 'string', 'avatar' => 'string',
1571
					'member_ip' => 'inet', 'member_ip2' => 'inet', 'buddy_list' => 'string', 'pm_ignore_list' => 'string',
1572
					'website_title' => 'string', 'website_url' => 'string',
1573
					'signature' => 'string', 'usertitle' => 'string', 'secret_question' => 'string',
1574
					'additional_groups' => 'string', 'ignore_boards' => 'string',
1575
				),
1576
				array(
1577
					stripslashes($_POST['username']), stripslashes($_POST['username']), $_POST['password1'], stripslashes($_POST['email']),
1578
					1, 0, time(),
1579
					$incontext['member_salt'], '', '', '',
1580
					$ip, $ip, '', '',
1581
					'', '',
1582
					'', '', '',
1583
					'', '',
1584
				),
1585
				array('id_member'),
1586
				1
1587
			);
1588
		}
1589
1590
		// If we're here we're good.
1591
		return true;
1592
	}
1593
1594
	return false;
1595
}
1596
1597
// Final step, clean up and a complete message!
1598
function DeleteInstall()
1599
{
1600
	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...
1601
	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...
1602
1603
	$incontext['page_title'] = $txt['congratulations'];
1604
	$incontext['sub_template'] = 'delete_install';
1605
	$incontext['continue'] = 0;
1606
1607
	require(dirname(__FILE__) . '/Settings.php');
1608
	load_database();
1609
1610
	chdir(dirname(__FILE__));
1611
1612
	require_once($sourcedir . '/Errors.php');
1613
	require_once($sourcedir . '/Logging.php');
1614
	require_once($sourcedir . '/Subs.php');
1615
	require_once($sourcedir . '/Load.php');
1616
	require_once($sourcedir . '/Security.php');
1617
	require_once($sourcedir . '/Subs-Auth.php');
1618
1619
	// Reload settings & set some global funcs
1620
	reloadSettings();
1621
1622
	// Bring a warning over.
1623
	if (!empty($incontext['account_existed']))
1624
		$incontext['warning'] = $incontext['account_existed'];
1625
1626 View Code Duplication
	if (!empty($db_character_set) && !empty($databases[$db_type]['utf8_support']))
1627
		$smcFunc['db_query']('', '
1628
			SET NAMES {string:db_character_set}',
1629
			array(
1630
				'db_character_set' => $db_character_set,
1631
				'db_error_skip' => true,
1632
			)
1633
		);
1634
1635
	// As track stats is by default enabled let's add some activity.
1636
	$smcFunc['db_insert']('ignore',
1637
		'{db_prefix}log_activity',
1638
		array('date' => 'date', 'topics' => 'int', 'posts' => 'int', 'registers' => 'int'),
1639
		array(strftime('%Y-%m-%d', time()), 1, 1, (!empty($incontext['member_id']) ? 1 : 0)),
1640
		array('date')
1641
	);
1642
1643
	// We're going to want our lovely $modSettings now.
1644
	$request = $smcFunc['db_query']('', '
1645
		SELECT variable, value
1646
		FROM {db_prefix}settings',
1647
		array(
1648
			'db_error_skip' => true,
1649
		)
1650
	);
1651
	// Only proceed if we can load the data.
1652
	if ($request)
1653
	{
1654 View Code Duplication
		while ($row = $smcFunc['db_fetch_row']($request))
1655
			$modSettings[$row[0]] = $row[1];
1656
		$smcFunc['db_free_result']($request);
1657
	}
1658
1659
	// Automatically log them in ;)
1660
	if (isset($incontext['member_id']) && isset($incontext['member_salt']))
1661
		setLoginCookie(3153600 * 60, $incontext['member_id'], hash_salt($_POST['password1'], $incontext['member_salt']));
1662
1663
	$result = $smcFunc['db_query']('', '
1664
		SELECT value
1665
		FROM {db_prefix}settings
1666
		WHERE variable = {string:db_sessions}',
1667
		array(
1668
			'db_sessions' => 'databaseSession_enable',
1669
			'db_error_skip' => true,
1670
		)
1671
	);
1672 View Code Duplication
	if ($smcFunc['db_num_rows']($result) != 0)
1673
		list ($db_sessions) = $smcFunc['db_fetch_row']($result);
1674
	$smcFunc['db_free_result']($result);
1675
1676
	if (empty($db_sessions))
1677
		$_SESSION['admin_time'] = time();
1678
	else
1679
	{
1680
		$_SERVER['HTTP_USER_AGENT'] = substr($_SERVER['HTTP_USER_AGENT'], 0, 211);
1681
1682
		$smcFunc['db_insert']('replace',
1683
			'{db_prefix}sessions',
1684
			array(
1685
				'session_id' => 'string', 'last_update' => 'int', 'data' => 'string',
1686
			),
1687
			array(
1688
				session_id(), time(), 'USER_AGENT|s:' . strlen($_SERVER['HTTP_USER_AGENT']) . ':"' . $_SERVER['HTTP_USER_AGENT'] . '";admin_time|i:' . time() . ';',
1689
			),
1690
			array('session_id')
1691
		);
1692
	}
1693
1694
	updateStats('member');
1695
	updateStats('message');
1696
	updateStats('topic');
1697
1698
	// This function is needed to do the updateStats('subject') call.
1699
	$smcFunc['strtolower'] = $db_character_set != 'utf8' && $txt['lang_character_set'] != 'UTF-8' ? 'strtolower' :
1700 View Code Duplication
		function($string){
1701
			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...
1702
			if (function_exists('mb_strtolower'))
1703
				return mb_strtolower($string, 'UTF-8');
1704
			require_once($sourcedir . '/Subs-Charset.php');
1705
			return utf8_strtolower($string);
1706
		};
1707
1708
	$request = $smcFunc['db_query']('', '
1709
		SELECT id_msg
1710
		FROM {db_prefix}messages
1711
		WHERE id_msg = 1
1712
			AND modified_time = 0
1713
		LIMIT 1',
1714
		array(
1715
			'db_error_skip' => true,
1716
		)
1717
	);
1718
	$context['utf8'] = $db_character_set === 'utf8' || $txt['lang_character_set'] === 'UTF-8';
1719
	if ($smcFunc['db_num_rows']($request) > 0)
1720
		updateStats('subject', 1, htmlspecialchars($txt['default_topic_subject']));
1721
	$smcFunc['db_free_result']($request);
1722
1723
	// Now is the perfect time to fetch the SM files.
1724
	require_once($sourcedir . '/ScheduledTasks.php');
1725
	// Sanity check that they loaded earlier!
1726
	if (isset($modSettings['recycle_board']))
1727
	{
1728
		$forum_version = $current_smf_version; // The variable is usually defined in index.php so lets just use our variable to do it for us.
1729
		scheduled_fetchSMfiles(); // Now go get those files!
1730
1731
		// We've just installed!
1732
		$user_info['ip'] = $_SERVER['REMOTE_ADDR'];
1733
		$user_info['id'] = isset($incontext['member_id']) ? $incontext['member_id'] : 0;
1734
		logAction('install', array('version' => $forum_version), 'admin');
1735
	}
1736
1737
	// Check if we need some stupid MySQL fix.
1738
	$server_version = $smcFunc['db_server_info']();
1739 View Code Duplication
	if (($db_type == 'mysql' || $db_type == 'mysqli') && in_array(substr($server_version, 0, 6), array('5.0.50', '5.0.51')))
1740
		updateSettings(array('db_mysql_group_by_fix' => '1'));
1741
1742
	// Some final context for the template.
1743
	$incontext['dir_still_writable'] = is_writable(dirname(__FILE__)) && substr(__FILE__, 1, 2) != ':\\';
1744
	$incontext['probably_delete_install'] = isset($_SESSION['installer_temp_ftp']) || is_writable(dirname(__FILE__)) || is_writable(__FILE__);
1745
1746
	// Update hash's cost to an appropriate setting
1747
	updateSettings(array(
1748
		'bcrypt_hash_cost' => hash_benchmark(),
1749
	));
1750
1751
	return false;
1752
}
1753
1754
function updateSettingsFile($vars)
1755
{
1756
	// Modify Settings.php.
1757
	$settingsArray = file(dirname(__FILE__) . '/Settings.php');
1758
1759
	// @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...
1760
	if (count($settingsArray) == 1)
1761
		$settingsArray = preg_split('~[\r\n]~', $settingsArray[0]);
1762
1763
	for ($i = 0, $n = count($settingsArray); $i < $n; $i++)
1764
	{
1765
		// Remove the redirect...
1766
		if (trim($settingsArray[$i]) == 'if (file_exists(dirname(__FILE__) . \'/install.php\'))' && trim($settingsArray[$i + 1]) == '{' && trim($settingsArray[$i + 9]) == '}')
1767
		{
1768
			// Set the ten lines to nothing.
1769
			for ($j=0; $j < 10; $j++)
1770
				$settingsArray[$i++] = '';
1771
1772
			continue;
1773
		}
1774
1775
		if (trim($settingsArray[$i]) == '?' . '>')
1776
			$settingsArray[$i] = '';
1777
1778
		// Don't trim or bother with it if it's not a variable.
1779
		if (substr($settingsArray[$i], 0, 1) != '$')
1780
			continue;
1781
1782
		$settingsArray[$i] = rtrim($settingsArray[$i]) . "\n";
1783
1784
		foreach ($vars as $var => $val)
1785
			if (strncasecmp($settingsArray[$i], '$' . $var, 1 + strlen($var)) == 0)
1786
			{
1787
				$comment = strstr($settingsArray[$i], '#');
1788
				$settingsArray[$i] = '$' . $var . ' = \'' . $val . '\';' . ($comment != '' ? "\t\t" . $comment : "\n");
1789
				unset($vars[$var]);
1790
			}
1791
	}
1792
1793
	// Uh oh... the file wasn't empty... was it?
1794
	if (!empty($vars))
1795
	{
1796
		$settingsArray[$i++] = '';
1797
		foreach ($vars as $var => $val)
1798
			$settingsArray[$i++] = '$' . $var . ' = \'' . $val . '\';' . "\n";
1799
	}
1800
1801
	// Blank out the file - done to fix a oddity with some servers.
1802
	$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...
1803
	if (!$fp)
1804
		return false;
1805
	fclose($fp);
1806
1807
	$fp = fopen(dirname(__FILE__) . '/Settings.php', 'r+');
1808
1809
	// Gotta have one of these ;)
1810
	if (trim($settingsArray[0]) != '<?php')
1811
		fwrite($fp, "<?php\n");
1812
1813
	$lines = count($settingsArray);
1814
	for ($i = 0; $i < $lines - 1; $i++)
1815
	{
1816
		// Don't just write a bunch of blank lines.
1817
		if ($settingsArray[$i] != '' || @$settingsArray[$i - 1] != '')
1818
			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 1086
  1. Read from $_GET, and $changes is assigned
    in other/upgrade.php on line 1086
  2. $changes is passed to updateSettingsFile()
    in other/upgrade.php on line 1159
  3. $var is assigned
    in other/install.php on line 1797
  4. $settingsArray is assigned
    in other/install.php on line 1798
  5. $settingsArray[$i] is passed through strtr()
    in other/install.php on line 1818
  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 1096
  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 1096
  2. $changes is assigned
    in other/upgrade.php on line 1097
  3. $changes is passed to updateSettingsFile()
    in other/upgrade.php on line 1159
  4. $var is assigned
    in other/install.php on line 1797
  5. $settingsArray is assigned
    in other/install.php on line 1798
  6. $settingsArray[$i] is passed through strtr()
    in other/install.php on line 1818
  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 1097
  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 1097
  2. $changes is passed to updateSettingsFile()
    in other/upgrade.php on line 1159
  3. $var is assigned
    in other/install.php on line 1797
  4. $settingsArray is assigned
    in other/install.php on line 1798
  5. $settingsArray[$i] is passed through strtr()
    in other/install.php on line 1818
  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 1233
  1. Read from $_POST, and $_POST[$config_var][0] is passed through addcslashes(), and $new_settings is assigned
    in Sources/ManageServer.php on line 1233
  2. $new_settings is passed to updateSettingsFile()
    in Sources/ManageServer.php on line 1267
  3. $var is assigned
    in other/install.php on line 1797
  4. $settingsArray is assigned
    in other/install.php on line 1798
  5. $settingsArray[$i] is passed through strtr()
    in other/install.php on line 1818
  5. Path: Read from $_POST, and $_POST[$config_var] is passed through addcslashes(), and $new_settings is assigned in Sources/ManageServer.php on line 1237
  1. Read from $_POST, and $_POST[$config_var] is passed through addcslashes(), and $new_settings is assigned
    in Sources/ManageServer.php on line 1237
  2. $new_settings is passed to updateSettingsFile()
    in Sources/ManageServer.php on line 1267
  3. $var is assigned
    in other/install.php on line 1797
  4. $settingsArray is assigned
    in other/install.php on line 1798
  5. $settingsArray[$i] is passed through strtr()
    in other/install.php on line 1818

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...
1819
	}
1820
	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 1086
  1. Read from $_GET, and $changes is assigned
    in other/upgrade.php on line 1086
  2. $changes is passed to updateSettingsFile()
    in other/upgrade.php on line 1159
  3. $var is assigned
    in other/install.php on line 1797
  4. $settingsArray is assigned
    in other/install.php on line 1798
  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 1096
  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 1096
  2. $changes is assigned
    in other/upgrade.php on line 1097
  3. $changes is passed to updateSettingsFile()
    in other/upgrade.php on line 1159
  4. $var is assigned
    in other/install.php on line 1797
  5. $settingsArray is assigned
    in other/install.php on line 1798
  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 1097
  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 1097
  2. $changes is passed to updateSettingsFile()
    in other/upgrade.php on line 1159
  3. $var is assigned
    in other/install.php on line 1797
  4. $settingsArray is assigned
    in other/install.php on line 1798
  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 1233
  1. Read from $_POST, and $_POST[$config_var][0] is passed through addcslashes(), and $new_settings is assigned
    in Sources/ManageServer.php on line 1233
  2. $new_settings is passed to updateSettingsFile()
    in Sources/ManageServer.php on line 1267
  3. $var is assigned
    in other/install.php on line 1797
  4. $settingsArray is assigned
    in other/install.php on line 1798
  5. Path: Read from $_POST, and $_POST[$config_var] is passed through addcslashes(), and $new_settings is assigned in Sources/ManageServer.php on line 1237
  1. Read from $_POST, and $_POST[$config_var] is passed through addcslashes(), and $new_settings is assigned
    in Sources/ManageServer.php on line 1237
  2. $new_settings is passed to updateSettingsFile()
    in Sources/ManageServer.php on line 1267
  3. $var is assigned
    in other/install.php on line 1797
  4. $settingsArray is assigned
    in other/install.php on line 1798

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