Completed
Pull Request — release-2.1 (#4470)
by Fran
08:53
created

install.php ➔ template_welcome_message()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 50
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

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

2 paths for user data to reach this point

  1. 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 1798
  4. $settingsArray is assigned
    in other/install.php on line 1799
  5. $settingsArray[$i] is passed through strtr()
    in other/install.php on line 1819
  2. 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 1798
  4. $settingsArray is assigned
    in other/install.php on line 1799
  5. $settingsArray[$i] is passed through strtr()
    in other/install.php on line 1819

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...
1820
	}
1821
	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.

2 paths for user data to reach this point

  1. 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 1798
  4. $settingsArray is assigned
    in other/install.php on line 1799
  2. 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 1798
  4. $settingsArray is assigned
    in other/install.php on line 1799

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