Completed
Push — release-2.1 ( 5a2bec...38950a )
by Jeremy
06:09
created

install.php ➔ load_database()   B

Complexity

Conditions 9
Paths 136

Size

Total Lines 36

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
nc 136
nop 0
dl 0
loc 36
rs 7.7555
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.4',
55
		'function_check' => 'pg_connect',
56
		'version_check' => '$request = pg_query(\'SELECT version()\'); list ($version) = pg_fetch_row($request); list($pgl, $version) = explode(" ", $version); return $version;',
57
		'supported' => function_exists('pg_connect'),
58
		'always_has_db' => true,
59
		'utf8_default' => true,
60
		'utf8_required' => true,
61
		'utf8_support' => function() {
62
			$request = pg_query('SHOW SERVER_ENCODING');
63
64
			list ($charcode) = pg_fetch_row($request);
65
66
			if ($charcode == 'UTF8')
0 ignored issues
show
Coding Style introduced by
The if-else statement can be simplified to return $charcode == 'UTF8';.
Loading history...
67
				return true;
68
			else
69
				return false;
70
		},
71
		'utf8_version' => '8.0',
72
		'utf8_version_check' => '$request = pg_query(\'SELECT version()\'); list ($version) = pg_fetch_row($request); list($pgl, $version) = explode(" ", $version); return $version;',
73
		'validate_prefix' => function(&$value) {
74
			global $txt;
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;
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;
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 $incontext, $user_info, $txt;
270
271
	$incontext['detected_languages'] = array();
272
273
	// Make sure the languages directory actually exists.
274 View Code Duplication
	if (file_exists(dirname(__FILE__) . '/Themes/default/languages'))
275
	{
276
		// Find all the "Install" language files in the directory.
277
		$dir = dir(dirname(__FILE__) . '/Themes/default/languages');
278
		while ($entry = $dir->read())
279
		{
280
			if (substr($entry, 0, 8) == 'Install.' && substr($entry, -4) == '.php')
281
				$incontext['detected_languages'][$entry] = ucfirst(substr($entry, 8, strlen($entry) - 12));
282
		}
283
		$dir->close();
284
	}
285
286
	// Didn't find any, show an error message!
287 View Code Duplication
	if (empty($incontext['detected_languages']))
288
	{
289
		// Let's not cache this message, eh?
290
		header('expires: Mon, 26 Jul 1997 05:00:00 GMT');
291
		header('last-modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
292
		header('cache-control: no-cache');
293
294
		echo '<!DOCTYPE html>
295
<html>
296
	<head>
297
		<title>SMF Installer: Error!</title>
298
		<style>
299
			body {
300
				font-family: sans-serif;
301
				max-width: 700px; }
302
		
303
			h1 {
304
				font-size: 14pt; }
305
306
			.directory {
307
				margin: 0.3em;
308
				font-family: monospace;
309
				font-weight: bold; }
310
		</style>
311
	</head>
312
	<body>
313
		<h1>A critical error has occurred.</h1>
314
315
		<p>This installer was unable to find the installer\'s language file or files. They should be found under:</p>
316
317
		<div class="directory">', dirname($_SERVER['PHP_SELF']) != '/' ? dirname($_SERVER['PHP_SELF']) : '', '/Themes/default/languages</div>
0 ignored issues
show
Security Cross-Site Scripting introduced by
dirname($_SERVER['PHP_SE...ERVER['PHP_SELF']) : '' can contain request data and is used in output context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

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

Preventing Cross-Site-Scripting Attacks

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

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

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

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

General Strategies to prevent injection

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

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

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

$sanitized = (integer) $tainted;
Loading history...
318
319
		<p>In some cases, FTP clients do not properly upload files with this many folders. Please double check to make sure you <strong>have uploaded all the files in the distribution</strong>.</p>
320
		<p>If that doesn\'t help, please make sure this install.php file is in the same place as the Themes folder.</p>
321
		<p>If you continue to get this error message, feel free to <a href="https://support.simplemachines.org/">look to us for support</a>.</p>
322
	</div></body>
323
</html>';
324
		die;
325
	}
326
327
	// Override the language file?
328 View Code Duplication
	if (isset($_GET['lang_file']))
329
		$_SESSION['installer_temp_lang'] = $_GET['lang_file'];
330
	elseif (isset($GLOBALS['HTTP_GET_VARS']['lang_file']))
331
		$_SESSION['installer_temp_lang'] = $GLOBALS['HTTP_GET_VARS']['lang_file'];
332
333
	// Make sure it exists, if it doesn't reset it.
334
	if (!isset($_SESSION['installer_temp_lang']) || preg_match('~[^\\w_\\-.]~', $_SESSION['installer_temp_lang']) === 1 || !file_exists(dirname(__FILE__) . '/Themes/default/languages/' . $_SESSION['installer_temp_lang']))
335
	{
336
		// Use the first one...
337
		list ($_SESSION['installer_temp_lang']) = array_keys($incontext['detected_languages']);
338
339
		// If we have english and some other language, use the other language.  We Americans hate english :P.
340 View Code Duplication
		if ($_SESSION['installer_temp_lang'] == 'Install.english.php' && count($incontext['detected_languages']) > 1)
341
			list (, $_SESSION['installer_temp_lang']) = array_keys($incontext['detected_languages']);
342
	}
343
344
	// And now include the actual language file itself.
345
	require_once(dirname(__FILE__) . '/Themes/default/languages/' . $_SESSION['installer_temp_lang']);
346
347
	// Which language did we load? Assume that he likes his language.
348
	preg_match('~^Install\.(.+[^-utf8])\.php$~', $_SESSION['installer_temp_lang'], $matches);
349
	$user_info['language'] = $matches[1];
350
}
351
352
// This handy function loads some settings and the like.
353
function load_database()
354
{
355
	global $db_prefix, $db_connection, $sourcedir, $smcFunc, $modSettings, $db_port;
356
	global $db_server, $db_passwd, $db_type, $db_name, $db_user, $db_persist, $db_mb4;
357
358
	if (empty($sourcedir))
359
		$sourcedir = dirname(__FILE__) . '/Sources';
360
361
	// Need this to check whether we need the database password.
362
	require(dirname(__FILE__) . '/Settings.php');
363
	if (!defined('SMF'))
364
		define('SMF', 1);
365
	if (empty($smcFunc))
366
		$smcFunc = array();
367
368
	$modSettings['disableQueryCheck'] = true;
369
370
	// Connect the database.
371
	if (!$db_connection)
372
	{
373
		require_once($sourcedir . '/Subs-Db-' . $db_type . '.php');
374
		if (version_compare(PHP_VERSION, '5', '<'))
375
			require_once($sourcedir . '/Subs-Compat.php');
376
377
		$options = array('persist' => $db_persist);
378
379
		if (!empty($db_port))
380
			$options['port'] = $db_port;
381
382
		if (!empty($db_mb4))
383
			$options['db_mb4'] = $db_mb4;
384
385
		if (!$db_connection)
386
			$db_connection = smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, $db_prefix, $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;
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!!
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;
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?
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;
510
511
	$incontext['page_title'] = $txt['ftp_checking_writable'];
512
	$incontext['sub_template'] = 'chmod_files';
513
514
	$writable_files = array(
515
		'attachments',
516
		'avatars',
517
		'custom_avatar',
518
		'cache',
519
		'Packages',
520
		'Smileys',
521
		'Themes',
522
		'agreement.txt',
523
		'Settings.php',
524
		'Settings_bak.php',
525
	);
526
527
	foreach ($incontext['detected_languages'] as $lang => $temp)
528
		$extra_files[] = 'Themes/default/languages/' . $lang;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$extra_files was never initialized. Although not strictly required by PHP, it is generally a good practice to add $extra_files = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
529
530
	// With mod_security installed, we could attempt to fix it with .htaccess.
531
	if (function_exists('apache_get_modules') && in_array('mod_security', apache_get_modules()))
532
		$writable_files[] = file_exists(dirname(__FILE__) . '/.htaccess') ? '.htaccess' : '.';
533
534
	$failed_files = array();
535
536
	// On linux, it's easy - just use is_writable!
537
	if (substr(__FILE__, 1, 2) != ':\\')
538
	{
539
		$incontext['systemos'] = 'linux';
540
541
		foreach ($writable_files as $file)
542
		{
543
			// Some files won't exist, try to address up front
544
			if (!file_exists(dirname(__FILE__) . '/' . $file))
545
				@touch(dirname(__FILE__) . '/' . $file);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

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

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
550
551
				// Well, 755 hopefully worked... if not, try 777.
552
				if (!is_writable(dirname(__FILE__) . '/' . $file) && !@chmod(dirname(__FILE__) . '/' . $file, 0777))
553
					$failed_files[] = $file;
554
			}
555
		}
556 View Code Duplication
		foreach ($extra_files as $file)
0 ignored issues
show
Bug introduced by
The variable $extra_files does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
557
			@chmod(dirname(__FILE__) . (empty($file) ? '' : '/' . $file), 0777);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

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

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

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

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

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

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

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

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
585
	}
586
587
	$failure = count($failed_files) >= 1;
588
589
	if (!isset($_SERVER))
590
		return !$failure;
591
592
	// Put the list into context.
593
	$incontext['failed_files'] = $failed_files;
594
595
	// It's not going to be possible to use FTP on windows to solve the problem...
596
	if ($failure && substr(__FILE__, 1, 2) == ':\\')
597
	{
598
		$incontext['error'] = $txt['error_windows_chmod'] . '
599
					<ul class="error_content">
600
						<li>' . implode('</li>
601
						<li>', $failed_files) . '</li>
602
					</ul>';
603
604
		return false;
605
	}
606
	// We're going to have to use... FTP!
607
	elseif ($failure)
608
	{
609
		// Load any session data we might have...
610 View Code Duplication
		if (!isset($_POST['ftp_username']) && isset($_SESSION['installer_temp_ftp']))
611
		{
612
			$_POST['ftp_server'] = $_SESSION['installer_temp_ftp']['server'];
613
			$_POST['ftp_port'] = $_SESSION['installer_temp_ftp']['port'];
614
			$_POST['ftp_username'] = $_SESSION['installer_temp_ftp']['username'];
615
			$_POST['ftp_password'] = $_SESSION['installer_temp_ftp']['password'];
616
			$_POST['ftp_path'] = $_SESSION['installer_temp_ftp']['path'];
617
		}
618
619
		$incontext['ftp_errors'] = array();
620
		require_once('Sources/Class-Package.php');
621 View Code Duplication
		if (isset($_POST['ftp_username']))
622
		{
623
			$ftp = new ftp_connection($_POST['ftp_server'], $_POST['ftp_port'], $_POST['ftp_username'], $_POST['ftp_password']);
624
625
			if ($ftp->error === false)
626
			{
627
				// Try it without /home/abc just in case they messed up.
628
				if (!$ftp->chdir($_POST['ftp_path']))
629
				{
630
					$incontext['ftp_errors'][] = $ftp->last_message;
631
					$ftp->chdir(preg_replace('~^/home[2]?/[^/]+?~', '', $_POST['ftp_path']));
632
				}
633
			}
634
		}
635
636
		if (!isset($ftp) || $ftp->error !== false)
637
		{
638
			if (!isset($ftp))
639
				$ftp = new ftp_connection(null);
640
			// Save the error so we can mess with listing...
641
			elseif ($ftp->error !== false && empty($incontext['ftp_errors']) && !empty($ftp->last_message))
642
				$incontext['ftp_errors'][] = $ftp->last_message;
643
644
			list ($username, $detect_path, $found_path) = $ftp->detect_path(dirname(__FILE__));
645
646
			if (empty($_POST['ftp_path']) && $found_path)
647
				$_POST['ftp_path'] = $detect_path;
648
649
			if (!isset($_POST['ftp_username']))
650
				$_POST['ftp_username'] = $username;
651
652
			// Set the username etc, into context.
653
			$incontext['ftp'] = array(
654
				'server' => isset($_POST['ftp_server']) ? $_POST['ftp_server'] : 'localhost',
655
				'port' => isset($_POST['ftp_port']) ? $_POST['ftp_port'] : '21',
656
				'username' => isset($_POST['ftp_username']) ? $_POST['ftp_username'] : '',
657
				'path' => isset($_POST['ftp_path']) ? $_POST['ftp_path'] : '/',
658
				'path_msg' => !empty($found_path) ? $txt['ftp_path_found_info'] : $txt['ftp_path_info'],
659
			);
660
661
			return false;
662
		}
663
		else
664
		{
665
			$_SESSION['installer_temp_ftp'] = array(
666
				'server' => $_POST['ftp_server'],
667
				'port' => $_POST['ftp_port'],
668
				'username' => $_POST['ftp_username'],
669
				'password' => $_POST['ftp_password'],
670
				'path' => $_POST['ftp_path']
671
			);
672
673
			$failed_files_updated = array();
674
675
			foreach ($failed_files as $file)
676
			{
677
				if (!is_writable(dirname(__FILE__) . '/' . $file))
678
					$ftp->chmod($file, 0755);
679
				if (!is_writable(dirname(__FILE__) . '/' . $file))
680
					$ftp->chmod($file, 0777);
681
				if (!is_writable(dirname(__FILE__) . '/' . $file))
682
				{
683
					$failed_files_updated[] = $file;
684
					$incontext['ftp_errors'][] = rtrim($ftp->last_message) . ' -> ' . $file . "\n";
685
				}
686
			}
687
688
			$ftp->close();
689
690
			// Are there any errors left?
691
			if (count($failed_files_updated) >= 1)
692
			{
693
				// Guess there are...
694
				$incontext['failed_files'] = $failed_files_updated;
695
696
				// Set the username etc, into context.
697
				$incontext['ftp'] = $_SESSION['installer_temp_ftp'] += array(
698
					'path_msg' => $txt['ftp_path_info'],
699
				);
700
701
				return false;
702
			}
703
		}
704
	}
705
706
	return true;
707
}
708
709
function DatabaseSettings()
710
{
711
	global $txt, $databases, $incontext, $smcFunc, $sourcedir;
712
	global $db_server, $db_name, $db_user, $db_passwd, $db_port, $db_mb4;
713
714
	$incontext['sub_template'] = 'database_settings';
715
	$incontext['page_title'] = $txt['db_settings'];
716
	$incontext['continue'] = 1;
717
718
	// Set up the defaults.
719
	$incontext['db']['server'] = 'localhost';
720
	$incontext['db']['user'] = '';
721
	$incontext['db']['name'] = '';
722
	$incontext['db']['pass'] = '';
723
	$incontext['db']['type'] = '';
724
	$incontext['supported_databases'] = array();
725
726
	$foundOne = false;
727
	foreach ($databases as $key => $db)
728
	{
729
		// Override with the defaults for this DB if appropriate.
730
		if ($db['supported'])
731
		{
732
			$incontext['supported_databases'][$key] = $db;
733
734
			if (!$foundOne)
735
			{
736
				if (isset($db['default_host']))
737
					$incontext['db']['server'] = ini_get($db['default_host']) or $incontext['db']['server'] = 'localhost';
738
				if (isset($db['default_user']))
739
				{
740
					$incontext['db']['user'] = ini_get($db['default_user']);
741
					$incontext['db']['name'] = ini_get($db['default_user']);
742
				}
743
				if (isset($db['default_password']))
744
					$incontext['db']['pass'] = ini_get($db['default_password']);
745
746
				// For simplicity and less confusion, leave the port blank by default
747
				$incontext['db']['port'] = '';
748
749
				$incontext['db']['type'] = $key;
750
				$foundOne = true;
751
			}
752
		}
753
	}
754
755
	// Override for repost.
756
	if (isset($_POST['db_user']))
757
	{
758
		$incontext['db']['user'] = $_POST['db_user'];
759
		$incontext['db']['name'] = $_POST['db_name'];
760
		$incontext['db']['server'] = $_POST['db_server'];
761
		$incontext['db']['prefix'] = $_POST['db_prefix'];
762
763
		if (!empty($_POST['db_port']))
764
			$incontext['db']['port'] = $_POST['db_port'];
765
	}
766
	else
767
	{
768
		$incontext['db']['prefix'] = 'smf_';
769
	}
770
771
	// Are we submitting?
772
	if (isset($_POST['db_type']))
773
	{
774
		// What type are they trying?
775
		$db_type = preg_replace('~[^A-Za-z0-9]~', '', $_POST['db_type']);
776
		$db_prefix = $_POST['db_prefix'];
777
		// Validate the prefix.
778
		$valid_prefix = $databases[$db_type]['validate_prefix']($db_prefix);
779
780
		if ($valid_prefix !== true)
781
		{
782
			$incontext['error'] = $valid_prefix;
783
			return false;
784
		}
785
786
		// Take care of these variables...
787
		$vars = array(
788
			'db_type' => $db_type,
789
			'db_name' => $_POST['db_name'],
790
			'db_user' => $_POST['db_user'],
791
			'db_passwd' => isset($_POST['db_passwd']) ? $_POST['db_passwd'] : '',
792
			'db_server' => $_POST['db_server'],
793
			'db_prefix' => $db_prefix,
794
			// The cookiename is special; we want it to be the same if it ever needs to be reinstalled with the same info.
795
			'cookiename' => 'SMFCookie' . abs(crc32($_POST['db_name'] . preg_replace('~[^A-Za-z0-9_$]~', '', $_POST['db_prefix'])) % 1000),
796
		);
797
798
		// Only set the port if we're not using the default
799
		if (!empty($_POST['db_port']))
800
		{
801
			// For MySQL, we can get the "default port" from PHP. PostgreSQL has no such option though.
802
			if (($db_type == 'mysql' || $db_type == 'mysqli') && $_POST['db_port'] != ini_get($db_type . '.default_port'))
803
				$vars['db_port'] = (int) $_POST['db_port'];
804
			elseif ($db_type == 'postgresql' && $_POST['db_port'] != 5432)
805
				$vars['db_port'] = (int) $_POST['db_port'];
806
		}
807
808
		// God I hope it saved!
809 View Code Duplication
		if (!updateSettingsFile($vars) && substr(__FILE__, 1, 2) == ':\\')
810
		{
811
			$incontext['error'] = $txt['error_windows_chmod'];
812
			return false;
813
		}
814
815
		// Make sure it works.
816
		require(dirname(__FILE__) . '/Settings.php');
817
818
		if (empty($sourcedir))
819
			$sourcedir = dirname(__FILE__) . '/Sources';
820
821
		// Better find the database file!
822
		if (!file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php'))
823
		{
824
			$incontext['error'] = sprintf($txt['error_db_file'], 'Subs-Db-' . $db_type . '.php');
825
			return false;
826
		}
827
828
		// Now include it for database functions!
829
		if (!defined('SMF'))
830
			define('SMF', 1);
831
832
		$modSettings['disableQueryCheck'] = true;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$modSettings was never initialized. Although not strictly required by PHP, it is generally a good practice to add $modSettings = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
833
		if (empty($smcFunc))
834
			$smcFunc = array();
835
836
			require_once($sourcedir . '/Subs-Db-' . $db_type . '.php');
837
838
		// What - running PHP4? The shame!
839
		if (version_compare(PHP_VERSION, '5', '<'))
840
			require_once($sourcedir . '/Subs-Compat.php');
841
842
		// Attempt a connection.
843
		$needsDB = !empty($databases[$db_type]['always_has_db']);
844
845
		$options = array('non_fatal' => true, 'dont_select_db' => !$needsDB);
846
		// Add in the port if needed
847
		if (!empty($db_port))
848
			$options['port'] = $db_port;
849
		
850
		if (!empty($db_mb4))
851
			$options['db_mb4'] = $db_mb4;
852
853
		$db_connection = smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, $db_prefix, $options);
854
855
		// No dice?  Let's try adding the prefix they specified, just in case they misread the instructions ;)
856
		if ($db_connection == null)
857
		{
858
			$db_error = @$smcFunc['db_error']();
859
860
			$db_connection = smf_db_initiate($db_server, $db_name, $_POST['db_prefix'] . $db_user, $db_passwd, $db_prefix, $options);
861
			if ($db_connection != null)
862
			{
863
				$db_user = $_POST['db_prefix'] . $db_user;
864
				updateSettingsFile(array('db_user' => $db_user));
865
			}
866
		}
867
868
		// Still no connection?  Big fat error message :P.
869
		if (!$db_connection)
870
		{
871
			$incontext['error'] = $txt['error_db_connect'] . '<div class="error_content"><strong>' . $db_error . '</strong></div>';
0 ignored issues
show
Bug introduced by
The variable $db_error does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
872
			return false;
873
		}
874
875
		// Do they meet the install requirements?
876
		// @todo Old client, new server?
877
		if (version_compare($databases[$db_type]['version'], preg_replace('~^\D*|\-.+?$~', '', eval($databases[$db_type]['version_check']))) > 0)
878
		{
879
			$incontext['error'] = $txt['error_db_too_low'];
880
			return false;
881
		}
882
883
		// Let's try that database on for size... assuming we haven't already lost the opportunity.
884
		if ($db_name != '' && !$needsDB)
885
		{
886
			$smcFunc['db_query']('', "
887
				CREATE DATABASE IF NOT EXISTS `$db_name`",
888
				array(
889
					'security_override' => true,
890
					'db_error_skip' => true,
891
				),
892
				$db_connection
893
			);
894
895
			// Okay, let's try the prefix if it didn't work...
896
			if (!$smcFunc['db_select_db']($db_name, $db_connection) && $db_name != '')
897
			{
898
				$smcFunc['db_query']('', "
899
					CREATE DATABASE IF NOT EXISTS `$_POST[db_prefix]$db_name`",
900
					array(
901
						'security_override' => true,
902
						'db_error_skip' => true,
903
					),
904
					$db_connection
905
				);
906
907
				if ($smcFunc['db_select_db']($_POST['db_prefix'] . $db_name, $db_connection))
908
				{
909
					$db_name = $_POST['db_prefix'] . $db_name;
910
					updateSettingsFile(array('db_name' => $db_name));
911
				}
912
			}
913
914
			// Okay, now let's try to connect...
915
			if (!$smcFunc['db_select_db']($db_name, $db_connection))
916
			{
917
				$incontext['error'] = sprintf($txt['error_db_database'], $db_name);
918
				return false;
919
			}
920
		}
921
922
		return true;
923
	}
924
925
	return false;
926
}
927
928
// Let's start with basic forum type settings.
929
function ForumSettings()
930
{
931
	global $txt, $incontext, $databases, $db_type, $db_connection;
932
933
	$incontext['sub_template'] = 'forum_settings';
934
	$incontext['page_title'] = $txt['install_settings'];
935
936
	// Let's see if we got the database type correct.
937
	if (isset($_POST['db_type'], $databases[$_POST['db_type']]))
938
		$db_type = $_POST['db_type'];
939
940
	// Else we'd better be able to get the connection.
941
	else
942
		load_database();
943
944
	$db_type = isset($_POST['db_type']) ? $_POST['db_type'] : $db_type;
945
946
	// What host and port are we on?
947
	$host = empty($_SERVER['HTTP_HOST']) ? $_SERVER['SERVER_NAME'] . (empty($_SERVER['SERVER_PORT']) || $_SERVER['SERVER_PORT'] == '80' ? '' : ':' . $_SERVER['SERVER_PORT']) : $_SERVER['HTTP_HOST'];
948
949
		$secure = false;
950
951 View Code Duplication
		if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on')
952
			$secure = true;
953
		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')
954
			$secure = true;
955
956
	// Now, to put what we've learned together... and add a path.
957
	$incontext['detected_url'] = 'http' . ($secure ? 's' : '') . '://' . $host . substr($_SERVER['PHP_SELF'], 0, strrpos($_SERVER['PHP_SELF'], '/'));
958
959
	// Check if the database sessions will even work.
960
	$incontext['test_dbsession'] = (ini_get('session.auto_start') != 1);
961
	$incontext['utf8_default'] = $databases[$db_type]['utf8_default'];
962
	$incontext['utf8_required'] = $databases[$db_type]['utf8_required'];
963
964
	$incontext['continue'] = 1;
965
966
	// Setup the SSL checkbox...
967
	$incontext['ssl_chkbx_protected'] = false;
968
	$incontext['ssl_chkbx_checked'] = false;
969
970
	// If redirect in effect, force ssl ON
971
	require_once(dirname(__FILE__) . '/Sources/Subs.php');
972
	if (https_redirect_active($incontext['detected_url'])) {
973
		$incontext['ssl_chkbx_protected'] = true;
974
		$incontext['ssl_chkbx_checked'] = true;
975
		$_POST['force_ssl'] = true;
976
	}
977
	// If no cert, make sure ssl stays OFF
978
	if (!ssl_cert_found($incontext['detected_url'])) {
979
		$incontext['ssl_chkbx_protected'] = true;
980
		$incontext['ssl_chkbx_checked'] = false;
981
	}
982
983
	// Submitting?
984
	if (isset($_POST['boardurl']))
985
	{
986 View Code Duplication
		if (substr($_POST['boardurl'], -10) == '/index.php')
987
			$_POST['boardurl'] = substr($_POST['boardurl'], 0, -10);
988
		elseif (substr($_POST['boardurl'], -1) == '/')
989
			$_POST['boardurl'] = substr($_POST['boardurl'], 0, -1);
990 View Code Duplication
		if (substr($_POST['boardurl'], 0, 7) != 'http://' && substr($_POST['boardurl'], 0, 7) != 'file://' && substr($_POST['boardurl'], 0, 8) != 'https://')
991
			$_POST['boardurl'] = 'http://' . $_POST['boardurl'];
992
993
		//Make sure boardurl is aligned with ssl setting
994
		if (empty($_POST['force_ssl']))
995
			$_POST['boardurl'] = strtr($_POST['boardurl'], array('https://' => 'http://'));
996
		else
997
			$_POST['boardurl'] = strtr($_POST['boardurl'], array('http://' => 'https://'));
998
999
		// Save these variables.
1000
		$vars = array(
1001
			'boardurl' => $_POST['boardurl'],
1002
			'boarddir' => addslashes(dirname(__FILE__)),
1003
			'sourcedir' => addslashes(dirname(__FILE__)) . '/Sources',
1004
			'cachedir' => addslashes(dirname(__FILE__)) . '/cache',
1005
			'packagesdir' => addslashes(dirname(__FILE__)) . '/Packages',
1006
			'tasksdir' => addslashes(dirname(__FILE__)) . '/Sources/tasks',
1007
			'mbname' => strtr($_POST['mbname'], array('\"' => '"')),
1008
			'language' => substr($_SESSION['installer_temp_lang'], 8, -4),
1009
			'image_proxy_secret' => substr(sha1(mt_rand()), 0, 20),
1010
			'image_proxy_enabled' => !empty($_POST['force_ssl']),
1011
		);
1012
1013
		// Must save!
1014 View Code Duplication
		if (!updateSettingsFile($vars) && substr(__FILE__, 1, 2) == ':\\')
1015
		{
1016
			$incontext['error'] = $txt['error_windows_chmod'];
1017
			return false;
1018
		}
1019
1020
		// Make sure it works.
1021
		require(dirname(__FILE__) . '/Settings.php');
1022
1023
		// UTF-8 requires a setting to override the language charset.
1024
		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'])))
1025
		{
1026
			if (!$databases[$db_type]['utf8_support']())
1027
			{
1028
				$incontext['error'] = sprintf($txt['error_utf8_support']);
1029
				return false;
1030
			}
1031
1032
			if (!empty($databases[$db_type]['utf8_version_check']) && version_compare($databases[$db_type]['utf8_version'], preg_replace('~\-.+?$~', '', eval($databases[$db_type]['utf8_version_check'])), '>'))
1033
			{
1034
				$incontext['error'] = sprintf($txt['error_utf8_version'], $databases[$db_type]['utf8_version']);
1035
				return false;
1036
			}
1037
			else
1038
				// Set the character set here.
1039
				updateSettingsFile(array('db_character_set' => 'utf8'));
1040
		}
1041
1042
		// Good, skip on.
1043
		return true;
1044
	}
1045
1046
	return false;
1047
}
1048
1049
// Step one: Do the SQL thang.
1050
function DatabasePopulation()
1051
{
1052
	global $db_character_set, $txt, $db_connection, $smcFunc, $databases, $modSettings, $db_type, $db_prefix, $incontext, $db_name, $boardurl;
1053
1054
	$incontext['sub_template'] = 'populate_database';
1055
	$incontext['page_title'] = $txt['db_populate'];
1056
	$incontext['continue'] = 1;
1057
1058
	// Already done?
1059
	if (isset($_POST['pop_done']))
1060
		return true;
1061
1062
	// Reload settings.
1063
	require(dirname(__FILE__) . '/Settings.php');
1064
	load_database();
1065
1066
	// Before running any of the queries, let's make sure another version isn't already installed.
1067
	$result = $smcFunc['db_query']('', '
1068
		SELECT variable, value
1069
		FROM {db_prefix}settings',
1070
		array(
1071
			'db_error_skip' => true,
1072
		)
1073
	);
1074
	$newSettings = array();
1075
	$modSettings = array();
1076
	if ($result !== false)
1077
	{
1078 View Code Duplication
		while ($row = $smcFunc['db_fetch_assoc']($result))
1079
			$modSettings[$row['variable']] = $row['value'];
1080
		$smcFunc['db_free_result']($result);
1081
1082
		// Do they match?  If so, this is just a refresh so charge on!
1083
		if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] != $GLOBALS['current_smf_version'])
1084
		{
1085
			$incontext['error'] = $txt['error_versions_do_not_match'];
1086
			return false;
1087
		}
1088
	}
1089
	$modSettings['disableQueryCheck'] = true;
1090
1091
	// If doing UTF8, select it. PostgreSQL requires passing it as a string...
1092 View Code Duplication
	if (!empty($db_character_set) && $db_character_set == 'utf8' && !empty($databases[$db_type]['utf8_support']))
1093
		$smcFunc['db_query']('', '
1094
			SET NAMES {string:utf8}',
1095
			array(
1096
				'db_error_skip' => true,
1097
				'utf8' => 'utf8',
1098
			)
1099
		);
1100
1101
	// Windows likes to leave the trailing slash, which yields to C:\path\to\SMF\/attachments...
1102
	if (substr(__DIR__, -1) == '\\')
1103
		$attachdir = __DIR__ . 'attachments';
1104
	else
1105
		$attachdir = __DIR__ . '/attachments';
1106
1107
	$replaces = array(
1108
		'{$db_prefix}' => $db_prefix,
1109
		'{$attachdir}' => json_encode(array(1 => $smcFunc['db_escape_string']($attachdir))),
1110
		'{$boarddir}' => $smcFunc['db_escape_string'](dirname(__FILE__)),
1111
		'{$boardurl}' => $boardurl,
1112
		'{$enableCompressedOutput}' => isset($_POST['compress']) ? '1' : '0',
1113
		'{$databaseSession_enable}' => isset($_POST['dbsession']) ? '1' : '0',
1114
		'{$smf_version}' => $GLOBALS['current_smf_version'],
1115
		'{$current_time}' => time(),
1116
		'{$sched_task_offset}' => 82800 + mt_rand(0, 86399),
1117
		'{$registration_method}' => isset($_POST['reg_mode']) ? $_POST['reg_mode'] : 0,
1118
	);
1119
1120
	foreach ($txt as $key => $value)
1121
	{
1122
		if (substr($key, 0, 8) == 'default_')
1123
			$replaces['{$' . $key . '}'] = $smcFunc['db_escape_string']($value);
1124
	}
1125
	$replaces['{$default_reserved_names}'] = strtr($replaces['{$default_reserved_names}'], array('\\\\n' => '\\n'));
1126
1127
	// MySQL-specific stuff - storage engine and UTF8 handling
1128
	if (substr($db_type, 0, 5) == 'mysql')
1129
	{
1130
		// Just in case the query fails for some reason...
1131
		$engines = array();
1132
1133
		// Figure out storage engines - what do we have, etc.
1134
		$get_engines = $smcFunc['db_query']('', 'SHOW ENGINES', array());
1135
1136 View Code Duplication
		while ($row = $smcFunc['db_fetch_assoc']($get_engines))
1137
		{
1138
			if ($row['Support'] == 'YES' || $row['Support'] == 'DEFAULT')
1139
				$engines[] = $row['Engine'];
1140
		}
1141
1142
		// Done with this now
1143
		$smcFunc['db_free_result']($get_engines);
1144
1145
		// InnoDB is better, so use it if possible...
1146
		$has_innodb = in_array('InnoDB', $engines);
1147
		$replaces['{$engine}'] = $has_innodb ? 'InnoDB' : 'MyISAM';
1148
		$replaces['{$memory}'] = (!$has_innodb && in_array('MEMORY', $engines)) ? 'MEMORY' : $replaces['{$engine}'];
1149
1150
		// If the UTF-8 setting was enabled, add it to the table definitions.
1151
		if (!empty($databases[$db_type]['utf8_support']) && (!empty($databases[$db_type]['utf8_required']) || isset($_POST['utf8'])))
1152
		{
1153
			$replaces['{$engine}'] .= ' DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci';
1154
			$replaces['{$memory}'] .= ' DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci';
1155
		}
1156
1157
		// One last thing - if we don't have InnoDB, we can't do transactions...
1158
		if (!$has_innodb)
1159
		{
1160
			$replaces['START TRANSACTION;'] = '';
1161
			$replaces['COMMIT;'] = '';
1162
		}
1163
	}
1164
	else
1165
	{
1166
		$has_innodb = false;
1167
	}
1168
1169
	// Read in the SQL.  Turn this on and that off... internationalize... etc.
1170
	$type = ($db_type == 'mysqli' ? 'mysql' : $db_type);
1171
	$sql_lines = explode("\n", strtr(implode(' ', file(dirname(__FILE__) . '/install_' . $GLOBALS['db_script_version'] . '_' . $type . '.sql')), $replaces));
1172
1173
	// Execute the SQL.
1174
	$current_statement = '';
1175
	$exists = array();
1176
	$incontext['failures'] = array();
1177
	$incontext['sql_results'] = array(
1178
		'tables' => 0,
1179
		'inserts' => 0,
1180
		'table_dups' => 0,
1181
		'insert_dups' => 0,
1182
	);
1183
	foreach ($sql_lines as $count => $line)
1184
	{
1185
		// No comments allowed!
1186
		if (substr(trim($line), 0, 1) != '#')
1187
			$current_statement .= "\n" . rtrim($line);
1188
1189
		// Is this the end of the query string?
1190
		if (empty($current_statement) || (preg_match('~;[\s]*$~s', $line) == 0 && $count != count($sql_lines)))
1191
			continue;
1192
1193
		// Does this table already exist?  If so, don't insert more data into it!
1194
		if (preg_match('~^\s*INSERT INTO ([^\s\n\r]+?)~', $current_statement, $match) != 0 && in_array($match[1], $exists))
1195
		{
1196
			preg_match_all('~\)[,;]~', $current_statement, $matches);
1197 View Code Duplication
			if (!empty($matches[0]))
1198
				$incontext['sql_results']['insert_dups'] += count($matches[0]);
1199
			else
1200
				$incontext['sql_results']['insert_dups']++;
1201
1202
			$current_statement = '';
1203
			continue;
1204
		}
1205
1206
		if ($smcFunc['db_query']('', $current_statement, array('security_override' => true, 'db_error_skip' => true), $db_connection) === false)
1207
		{
1208
			// Use the appropriate function based on the DB type
1209
			if ($db_type == 'mysql' || $db_type == 'mysqli')
1210
				$db_errorno = $db_type . '_errno';
1211
1212
			// Error 1050: Table already exists!
1213
			// @todo Needs to be made better!
1214
			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...
1215
			{
1216
				$exists[] = $match[1];
1217
				$incontext['sql_results']['table_dups']++;
1218
			}
1219
			// Don't error on duplicate indexes (or duplicate operators in PostgreSQL.)
1220
			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)))
1221
			{
1222
				// MySQLi requires a connection object. It's optional with MySQL and Postgres
1223
				$incontext['failures'][$count] = $smcFunc['db_error']($db_connection);
1224
			}
1225
		}
1226
		else
1227
		{
1228
			if (preg_match('~^\s*CREATE TABLE ([^\s\n\r]+?)~', $current_statement, $match) == 1)
1229
				$incontext['sql_results']['tables']++;
1230
			elseif (preg_match('~^\s*INSERT INTO ([^\s\n\r]+?)~', $current_statement, $match) == 1)
1231
			{
1232
				preg_match_all('~\)[,;]~', $current_statement, $matches);
1233 View Code Duplication
				if (!empty($matches[0]))
1234
					$incontext['sql_results']['inserts'] += count($matches[0]);
1235
				else
1236
					$incontext['sql_results']['inserts']++;
1237
			}
1238
		}
1239
1240
		$current_statement = '';
1241
1242
		// Wait, wait, I'm still working here!
1243
		set_time_limit(60);
1244
	}
1245
1246
	// Sort out the context for the SQL.
1247
	foreach ($incontext['sql_results'] as $key => $number)
1248
	{
1249
		if ($number == 0)
1250
			unset($incontext['sql_results'][$key]);
1251
		else
1252
			$incontext['sql_results'][$key] = sprintf($txt['db_populate_' . $key], $number);
1253
	}
1254
1255
	// Make sure UTF will be used globally.
1256
	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'])))
1257
		$newSettings[] = array('global_character_set', 'UTF-8');
1258
1259
	// Auto-detect local & global cookie settings
1260
	$url_parts = parse_url($boardurl);
1261
	if ($url_parts !== false)
1262
	{
1263
		unset($globalCookies, $globalCookiesDomain, $localCookies);
1264
1265
		// Look for subdomain, if found, set globalCookie settings
1266
		// Don't bother looking if you have an ip address for host
1267
		if (!empty($url_parts['host']) && (filter_var($url_parts['host'], FILTER_VALIDATE_IP) === false))
1268
		{
1269
			// www isn't really a subdomain in this sense, so strip it out
1270
			$url_parts['host'] = str_ireplace('www.', '', $url_parts['host']);
1271
			$pos1 = strrpos($url_parts['host'], '.');
1272
			if ($pos1 !== false)
1273
			{
1274
				// 2nd period from the right indicates you have a subdomain
1275
				$pos2 = strrpos(substr($url_parts['host'], 0, $pos1 - 1), '.');
1276
				if ($pos2 !== false)
1277
				{
1278
					$globalCookies = '1';
1279
					$globalCookiesDomain = substr($url_parts['host'], $pos2 + 1);
1280
				}
1281
			}
1282
		}
1283
1284
		// Look for subfolder, if found, set localCookie
1285
		// Checking for len > 1 ensures you don't have just a slash...
1286
		if (!empty($url_parts['path']) && strlen($url_parts['path']) > 1)
1287
			$localCookies = '1';
1288
1289
		if (isset($globalCookies))
1290
			$newSettings[] = array('globalCookies', $globalCookies);
1291
		if (isset($globalCookiesDomain))
1292
			$newSettings[] = array('globalCookiesDomain', $globalCookiesDomain);
1293
		if (isset($localCookies))
1294
			$newSettings[] = array('localCookies', $localCookies);
1295
	}
1296
1297
	// Are we allowing stat collection?
1298
	if (!empty($_POST['stats']) && substr($boardurl, 0, 16) != 'http://localhost' && empty($modSettings['allow_sm_stats']) && empty($modSettings['enable_sm_stats']))
1299
	{
1300
		$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...
1301
1302
		// Attempt to register the site etc.
1303
		$fp = @fsockopen('www.simplemachines.org', 80, $errno, $errstr);
1304 View Code Duplication
		if ($fp)
1305
		{
1306
			$out = 'GET /smf/stats/register_stats.php?site=' . base64_encode($boardurl) . ' HTTP/1.1' . "\r\n";
1307
			$out .= 'Host: www.simplemachines.org' . "\r\n";
1308
			$out .= 'Connection: Close' . "\r\n\r\n";
1309
			fwrite($fp, $out);
1310
1311
			$return_data = '';
1312
			while (!feof($fp))
1313
				$return_data .= fgets($fp, 128);
1314
1315
			fclose($fp);
1316
1317
			// Get the unique site ID.
1318
			preg_match('~SITE-ID:\s(\w{10})~', $return_data, $ID);
1319
1320
			if (!empty($ID[1]))
1321
				$smcFunc['db_insert']('replace',
1322
					$db_prefix . 'settings',
1323
					array('variable' => 'string', 'value' => 'string'),
1324
					array(
1325
						array('sm_stats_key', $ID[1]),
1326
						array('enable_sm_stats', 1),
1327
					),
1328
					array('variable')
1329
				);
1330
		}
1331
	}
1332
	// Don't remove stat collection unless we unchecked the box for real, not from the loop.
1333 View Code Duplication
	elseif (empty($_POST['stats']) && empty($upcontext['allow_sm_stats']))
0 ignored issues
show
Bug introduced by
The variable $upcontext seems only to be defined at a later point. As such the call to empty() seems to always evaluate to true.

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

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

Loading history...
1334
		$smcFunc['db_query']('', '
1335
			DELETE FROM {db_prefix}settings
1336
			WHERE variable = {string:enable_sm_stats}',
1337
			array(
1338
				'enable_sm_stats' => 'enable_sm_stats',
1339
				'db_error_skip' => true,
1340
			)
1341
		);
1342
1343
	// Are we enabling SSL?
1344
	if (!empty($_POST['force_ssl']))
1345
		$newSettings[] = array('force_ssl', 1);
1346
1347
	// Setting a timezone is required.
1348
	if (!isset($modSettings['default_timezone']) && function_exists('date_default_timezone_set'))
1349
	{
1350
		// Get PHP's default timezone, if set
1351
		$ini_tz = ini_get('date.timezone');
1352
		if (!empty($ini_tz))
1353
			$timezone_id = $ini_tz;
1354
		else
1355
			$timezone_id = '';
1356
1357
		// If date.timezone is unset, invalid, or just plain weird, make a best guess
1358 View Code Duplication
		if (!in_array($timezone_id, timezone_identifiers_list()))
1359
		{
1360
			$server_offset = @mktime(0, 0, 0, 1, 1, 1970);
1361
			$timezone_id = timezone_name_from_abbr('', $server_offset, 0);
1362
		}
1363
1364
		if (date_default_timezone_set($timezone_id))
1365
			$newSettings[] = array('default_timezone', $timezone_id);
1366
	}
1367
1368
	if (!empty($newSettings))
1369
	{
1370
		$smcFunc['db_insert']('replace',
1371
			'{db_prefix}settings',
1372
			array('variable' => 'string-255', 'value' => 'string-65534'),
1373
			$newSettings,
1374
			array('variable')
1375
		);
1376
	}
1377
1378
	// Let's optimize those new tables, but not on InnoDB, ok?
1379
	if (!$has_innodb)
1380
	{
1381
		db_extend();
1382
		$tables = $smcFunc['db_list_tables']($db_name, $db_prefix . '%');
1383
		foreach ($tables as $table)
1384
		{
1385
			$smcFunc['db_optimize_table']($table) != -1 or $db_messed = true;
1386
1387
			if (!empty($db_messed))
1388
			{
1389
				$incontext['failures'][-1] = $smcFunc['db_error']();
1390
				break;
1391
			}
1392
		}
1393
	}
1394
1395
	// MySQL specific stuff
1396
	if (substr($db_type, 0, 5) != 'mysql')
1397
		return false;
1398
1399
	// Find database user privileges.
1400
	$privs = array();
1401
	$get_privs = $smcFunc['db_query']('', 'SHOW PRIVILEGES', array());
1402
	while ($row = $smcFunc['db_fetch_assoc']($get_privs))
1403
	{
1404
		if ($row['Privilege'] == 'Alter')
1405
			$privs[] = $row['Privilege'];
1406
	}
1407
	$smcFunc['db_free_result']($get_privs);
1408
1409
	// Check for the ALTER privilege.
1410
	if (!empty($databases[$db_type]['alter_support']) && !in_array('Alter', $privs))
1411
	{
1412
		$incontext['error'] = $txt['error_db_alter_priv'];
1413
		return false;
1414
	}
1415
1416
	if (!empty($exists))
1417
	{
1418
		$incontext['page_title'] = $txt['user_refresh_install'];
1419
		$incontext['was_refresh'] = true;
1420
	}
1421
1422
	return false;
1423
}
1424
1425
// Ask for the administrator login information.
1426
function AdminAccount()
1427
{
1428
	global $txt, $db_type, $smcFunc, $incontext, $db_prefix, $db_passwd, $sourcedir, $db_character_set;
1429
1430
	$incontext['sub_template'] = 'admin_account';
1431
	$incontext['page_title'] = $txt['user_settings'];
1432
	$incontext['continue'] = 1;
1433
1434
	// Skipping?
1435
	if (!empty($_POST['skip']))
1436
		return true;
1437
1438
	// Need this to check whether we need the database password.
1439
	require(dirname(__FILE__) . '/Settings.php');
1440
	load_database();
1441
1442
	require_once($sourcedir . '/Subs-Auth.php');
1443
1444
	require_once($sourcedir . '/Subs.php');
1445
1446
	// Reload settings & set some global funcs
1447
	require_once($sourcedir . '/Load.php');
1448
	reloadSettings();
1449
1450
	// We need this to properly hash the password for Admin
1451 View Code Duplication
	$smcFunc['strtolower'] = $db_character_set != 'utf8' && $txt['lang_character_set'] != 'UTF-8' ? 'strtolower' : function($string) {
1452
			global $sourcedir;
1453
			if (function_exists('mb_strtolower'))
1454
				return mb_strtolower($string, 'UTF-8');
1455
			require_once($sourcedir . '/Subs-Charset.php');
1456
			return utf8_strtolower($string);
1457
		};
1458
1459
	if (!isset($_POST['username']))
1460
		$_POST['username'] = '';
1461
	if (!isset($_POST['email']))
1462
		$_POST['email'] = '';
1463
	if (!isset($_POST['server_email']))
1464
		$_POST['server_email'] = '';
1465
1466
	$incontext['username'] = htmlspecialchars(stripslashes($_POST['username']));
1467
	$incontext['email'] = htmlspecialchars(stripslashes($_POST['email']));
1468
	$incontext['server_email'] = htmlspecialchars(stripslashes($_POST['server_email']));
1469
1470
	$incontext['require_db_confirm'] = empty($db_type);
1471
1472
	// Only allow skipping if we think they already have an account setup.
1473
	$request = $smcFunc['db_query']('', '
1474
		SELECT id_member
1475
		FROM {db_prefix}members
1476
		WHERE id_group = {int:admin_group} OR FIND_IN_SET({int:admin_group}, additional_groups) != 0
1477
		LIMIT 1',
1478
		array(
1479
			'db_error_skip' => true,
1480
			'admin_group' => 1,
1481
		)
1482
	);
1483
	if ($smcFunc['db_num_rows']($request) != 0)
1484
		$incontext['skip'] = 1;
1485
	$smcFunc['db_free_result']($request);
1486
1487
	// Trying to create an account?
1488
	if (isset($_POST['password1']) && !empty($_POST['contbutt']))
1489
	{
1490
		// Wrong password?
1491
		if ($incontext['require_db_confirm'] && $_POST['password3'] != $db_passwd)
1492
		{
1493
			$incontext['error'] = $txt['error_db_connect'];
1494
			return false;
1495
		}
1496
		// Not matching passwords?
1497
		if ($_POST['password1'] != $_POST['password2'])
1498
		{
1499
			$incontext['error'] = $txt['error_user_settings_again_match'];
1500
			return false;
1501
		}
1502
		// No password?
1503
		if (strlen($_POST['password1']) < 4)
1504
		{
1505
			$incontext['error'] = $txt['error_user_settings_no_password'];
1506
			return false;
1507
		}
1508
		if (!file_exists($sourcedir . '/Subs.php'))
1509
		{
1510
			$incontext['error'] = $txt['error_subs_missing'];
1511
			return false;
1512
		}
1513
1514
		// Update the webmaster's email?
1515
		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...
1516
			updateSettingsFile(array('webmaster_email' => $_POST['server_email']));
1517
1518
		// Work out whether we're going to have dodgy characters and remove them.
1519
		$invalid_characters = preg_match('~[<>&"\'=\\\]~', $_POST['username']) != 0;
1520
		$_POST['username'] = preg_replace('~[<>&"\'=\\\]~', '', $_POST['username']);
1521
1522
		$result = $smcFunc['db_query']('', '
1523
			SELECT id_member, password_salt
1524
			FROM {db_prefix}members
1525
			WHERE member_name = {string:username} OR email_address = {string:email}
1526
			LIMIT 1',
1527
			array(
1528
				'username' => stripslashes($_POST['username']),
1529
				'email' => stripslashes($_POST['email']),
1530
				'db_error_skip' => true,
1531
			)
1532
		);
1533
		if ($smcFunc['db_num_rows']($result) != 0)
1534
		{
1535
			list ($incontext['member_id'], $incontext['member_salt']) = $smcFunc['db_fetch_row']($result);
1536
			$smcFunc['db_free_result']($result);
1537
1538
			$incontext['account_existed'] = $txt['error_user_settings_taken'];
1539
		}
1540
		elseif ($_POST['username'] == '' || strlen($_POST['username']) > 25)
1541
		{
1542
			// Try the previous step again.
1543
			$incontext['error'] = $_POST['username'] == '' ? $txt['error_username_left_empty'] : $txt['error_username_too_long'];
1544
			return false;
1545
		}
1546
		elseif ($invalid_characters || $_POST['username'] == '_' || $_POST['username'] == '|' || strpos($_POST['username'], '[code') !== false || strpos($_POST['username'], '[/code') !== false)
1547
		{
1548
			// Try the previous step again.
1549
			$incontext['error'] = $txt['error_invalid_characters_username'];
1550
			return false;
1551
		}
1552 View Code Duplication
		elseif (empty($_POST['email']) || !filter_var(stripslashes($_POST['email']), FILTER_VALIDATE_EMAIL) || strlen(stripslashes($_POST['email'])) > 255)
1553
		{
1554
			// One step back, this time fill out a proper admin email address.
1555
			$incontext['error'] = sprintf($txt['error_valid_admin_email_needed'], $_POST['username']);
1556
			return false;
1557
		}
1558 View Code Duplication
		elseif (empty($_POST['server_email']) || !filter_var(stripslashes($_POST['server_email']), FILTER_VALIDATE_EMAIL) || strlen(stripslashes($_POST['server_email'])) > 255)
1559
		{
1560
			// One step back, this time fill out a proper admin email address.
1561
			$incontext['error'] = $txt['error_valid_server_email_needed'];
1562
			return false;
1563
		}
1564
		elseif ($_POST['username'] != '')
1565
		{
1566
			$incontext['member_salt'] = substr(md5(mt_rand()), 0, 4);
1567
1568
			// Format the username properly.
1569
			$_POST['username'] = preg_replace('~[\t\n\r\x0B\0\xA0]+~', ' ', $_POST['username']);
1570
			$ip = isset($_SERVER['REMOTE_ADDR']) ? substr($_SERVER['REMOTE_ADDR'], 0, 255) : '';
1571
1572
			$_POST['password1'] = hash_password(stripslashes($_POST['username']), stripslashes($_POST['password1']));
1573
1574
			$incontext['member_id'] = $smcFunc['db_insert']('',
1575
				$db_prefix . 'members',
1576
				array(
1577
					'member_name' => 'string-25', 'real_name' => 'string-25', 'passwd' => 'string', 'email_address' => 'string',
1578
					'id_group' => 'int', 'posts' => 'int', 'date_registered' => 'int',
1579
					'password_salt' => 'string', 'lngfile' => 'string', 'personal_text' => 'string', 'avatar' => 'string',
1580
					'member_ip' => 'inet', 'member_ip2' => 'inet', 'buddy_list' => 'string', 'pm_ignore_list' => 'string',
1581
					'website_title' => 'string', 'website_url' => 'string',
1582
					'signature' => 'string', 'usertitle' => 'string', 'secret_question' => 'string',
1583
					'additional_groups' => 'string', 'ignore_boards' => 'string',
1584
				),
1585
				array(
1586
					stripslashes($_POST['username']), stripslashes($_POST['username']), $_POST['password1'], stripslashes($_POST['email']),
1587
					1, 0, time(),
1588
					$incontext['member_salt'], '', '', '',
1589
					$ip, $ip, '', '',
1590
					'', '',
1591
					'', '', '',
1592
					'', '',
1593
				),
1594
				array('id_member'),
1595
				1
1596
			);
1597
		}
1598
1599
		// If we're here we're good.
1600
		return true;
1601
	}
1602
1603
	return false;
1604
}
1605
1606
// Final step, clean up and a complete message!
1607
function DeleteInstall()
1608
{
1609
	global $smcFunc, $db_character_set, $context, $txt, $incontext;
1610
	global $current_smf_version, $databases, $sourcedir, $forum_version, $modSettings, $user_info, $db_type, $boardurl;
1611
1612
	$incontext['page_title'] = $txt['congratulations'];
1613
	$incontext['sub_template'] = 'delete_install';
1614
	$incontext['continue'] = 0;
1615
1616
	require(dirname(__FILE__) . '/Settings.php');
1617
	load_database();
1618
1619
	chdir(dirname(__FILE__));
1620
1621
	require_once($sourcedir . '/Errors.php');
1622
	require_once($sourcedir . '/Logging.php');
1623
	require_once($sourcedir . '/Subs.php');
1624
	require_once($sourcedir . '/Load.php');
1625
	require_once($sourcedir . '/Security.php');
1626
	require_once($sourcedir . '/Subs-Auth.php');
1627
1628
	// Reload settings & set some global funcs
1629
	reloadSettings();
1630
1631
	// Bring a warning over.
1632
	if (!empty($incontext['account_existed']))
1633
		$incontext['warning'] = $incontext['account_existed'];
1634
1635 View Code Duplication
	if (!empty($db_character_set) && !empty($databases[$db_type]['utf8_support']))
1636
		$smcFunc['db_query']('', '
1637
			SET NAMES {string:db_character_set}',
1638
			array(
1639
				'db_character_set' => $db_character_set,
1640
				'db_error_skip' => true,
1641
			)
1642
		);
1643
1644
	// As track stats is by default enabled let's add some activity.
1645
	$smcFunc['db_insert']('ignore',
1646
		'{db_prefix}log_activity',
1647
		array('date' => 'date', 'topics' => 'int', 'posts' => 'int', 'registers' => 'int'),
1648
		array(strftime('%Y-%m-%d', time()), 1, 1, (!empty($incontext['member_id']) ? 1 : 0)),
1649
		array('date')
1650
	);
1651
1652
	// We're going to want our lovely $modSettings now.
1653
	$request = $smcFunc['db_query']('', '
1654
		SELECT variable, value
1655
		FROM {db_prefix}settings',
1656
		array(
1657
			'db_error_skip' => true,
1658
		)
1659
	);
1660
	// Only proceed if we can load the data.
1661
	if ($request)
1662
	{
1663
		while ($row = $smcFunc['db_fetch_row']($request))
1664
			$modSettings[$row[0]] = $row[1];
1665
		$smcFunc['db_free_result']($request);
1666
	}
1667
1668
	// Automatically log them in ;)
1669
	if (isset($incontext['member_id']) && isset($incontext['member_salt']))
1670
		setLoginCookie(3153600 * 60, $incontext['member_id'], hash_salt($_POST['password1'], $incontext['member_salt']));
1671
1672
	$result = $smcFunc['db_query']('', '
1673
		SELECT value
1674
		FROM {db_prefix}settings
1675
		WHERE variable = {string:db_sessions}',
1676
		array(
1677
			'db_sessions' => 'databaseSession_enable',
1678
			'db_error_skip' => true,
1679
		)
1680
	);
1681 View Code Duplication
	if ($smcFunc['db_num_rows']($result) != 0)
1682
		list ($db_sessions) = $smcFunc['db_fetch_row']($result);
1683
	$smcFunc['db_free_result']($result);
1684
1685
	if (empty($db_sessions))
1686
		$_SESSION['admin_time'] = time();
1687
	else
1688
	{
1689
		$_SERVER['HTTP_USER_AGENT'] = substr($_SERVER['HTTP_USER_AGENT'], 0, 211);
1690
1691
		$smcFunc['db_insert']('replace',
1692
			'{db_prefix}sessions',
1693
			array(
1694
				'session_id' => 'string', 'last_update' => 'int', 'data' => 'string',
1695
			),
1696
			array(
1697
				session_id(), time(), 'USER_AGENT|s:' . strlen($_SERVER['HTTP_USER_AGENT']) . ':"' . $_SERVER['HTTP_USER_AGENT'] . '";admin_time|i:' . time() . ';',
1698
			),
1699
			array('session_id')
1700
		);
1701
	}
1702
1703
	updateStats('member');
1704
	updateStats('message');
1705
	updateStats('topic');
1706
1707
	// This function is needed to do the updateStats('subject') call.
1708
	$smcFunc['strtolower'] = $db_character_set != 'utf8' && $txt['lang_character_set'] != 'UTF-8' ? 'strtolower' :
1709 View Code Duplication
		function($string){
1710
			global $sourcedir;
1711
			if (function_exists('mb_strtolower'))
1712
				return mb_strtolower($string, 'UTF-8');
1713
			require_once($sourcedir . '/Subs-Charset.php');
1714
			return utf8_strtolower($string);
1715
		};
1716
1717
	$request = $smcFunc['db_query']('', '
1718
		SELECT id_msg
1719
		FROM {db_prefix}messages
1720
		WHERE id_msg = 1
1721
			AND modified_time = 0
1722
		LIMIT 1',
1723
		array(
1724
			'db_error_skip' => true,
1725
		)
1726
	);
1727
	$context['utf8'] = $db_character_set === 'utf8' || $txt['lang_character_set'] === 'UTF-8';
1728
	if ($smcFunc['db_num_rows']($request) > 0)
1729
		updateStats('subject', 1, htmlspecialchars($txt['default_topic_subject']));
1730
	$smcFunc['db_free_result']($request);
1731
1732
	// Now is the perfect time to fetch the SM files.
1733
	require_once($sourcedir . '/ScheduledTasks.php');
1734
	// Sanity check that they loaded earlier!
1735
	if (isset($modSettings['recycle_board']))
1736
	{
1737
		$forum_version = $current_smf_version; // The variable is usually defined in index.php so lets just use our variable to do it for us.
1738
		scheduled_fetchSMfiles(); // Now go get those files!
1739
1740
		// We've just installed!
1741
		$user_info['ip'] = $_SERVER['REMOTE_ADDR'];
1742
		$user_info['id'] = isset($incontext['member_id']) ? $incontext['member_id'] : 0;
1743
		logAction('install', array('version' => $forum_version), 'admin');
1744
	}
1745
1746
	// Check if we need some stupid MySQL fix.
1747
	$server_version = $smcFunc['db_server_info']();
1748 View Code Duplication
	if (($db_type == 'mysql' || $db_type == 'mysqli') && in_array(substr($server_version, 0, 6), array('5.0.50', '5.0.51')))
1749
		updateSettings(array('db_mysql_group_by_fix' => '1'));
1750
1751
	// Some final context for the template.
1752
	$incontext['dir_still_writable'] = is_writable(dirname(__FILE__)) && substr(__FILE__, 1, 2) != ':\\';
1753
	$incontext['probably_delete_install'] = isset($_SESSION['installer_temp_ftp']) || is_writable(dirname(__FILE__)) || is_writable(__FILE__);
1754
1755
	// Update hash's cost to an appropriate setting
1756
	updateSettings(array(
1757
		'bcrypt_hash_cost' => hash_benchmark(),
1758
	));
1759
1760
	return false;
1761
}
1762
1763
function updateSettingsFile($vars)
1764
{
1765
	// Modify Settings.php.
1766
	$settingsArray = file(dirname(__FILE__) . '/Settings.php');
1767
1768
	// @todo Do we just want to read the file in clean, and split it this way always?
1769
	if (count($settingsArray) == 1)
1770
		$settingsArray = preg_split('~[\r\n]~', $settingsArray[0]);
1771
1772
	for ($i = 0, $n = count($settingsArray); $i < $n; $i++)
1773
	{
1774
		// Remove the redirect...
1775
		if (trim($settingsArray[$i]) == 'if (file_exists(dirname(__FILE__) . \'/install.php\'))' && trim($settingsArray[$i + 1]) == '{' && trim($settingsArray[$i + 9]) == '}')
1776
		{
1777
			// Set the ten lines to nothing.
1778
			for ($j=0; $j < 10; $j++)
1779
				$settingsArray[$i++] = '';
1780
1781
			continue;
1782
		}
1783
1784
		if (trim($settingsArray[$i]) == '?' . '>')
1785
			$settingsArray[$i] = '';
1786
1787
		// Don't trim or bother with it if it's not a variable.
1788
		if (substr($settingsArray[$i], 0, 1) != '$')
1789
			continue;
1790
1791
		$settingsArray[$i] = rtrim($settingsArray[$i]) . "\n";
1792
1793
		foreach ($vars as $var => $val)
1794
			if (strncasecmp($settingsArray[$i], '$' . $var, 1 + strlen($var)) == 0)
1795
			{
1796
				$comment = strstr($settingsArray[$i], '#');
1797
				$settingsArray[$i] = '$' . $var . ' = \'' . $val . '\';' . ($comment != '' ? "\t\t" . $comment : "\n");
1798
				unset($vars[$var]);
1799
			}
1800
	}
1801
1802
	// Uh oh... the file wasn't empty... was it?
1803
	if (!empty($vars))
1804
	{
1805
		$settingsArray[$i++] = '';
1806
		foreach ($vars as $var => $val)
1807
			$settingsArray[$i++] = '$' . $var . ' = \'' . $val . '\';' . "\n";
1808
	}
1809
1810
	// Blank out the file - done to fix a oddity with some servers.
1811
	$fp = @fopen(dirname(__FILE__) . '/Settings.php', 'w');
1812
	if (!$fp)
1813
		return false;
1814
	fclose($fp);
1815
1816
	$fp = fopen(dirname(__FILE__) . '/Settings.php', 'r+');
1817
1818
	// Gotta have one of these ;)
1819
	if (trim($settingsArray[0]) != '<?php')
1820
		fwrite($fp, "<?php\n");
1821
1822
	$lines = count($settingsArray);
1823
	for ($i = 0; $i < $lines - 1; $i++)
1824
	{
1825
		// Don't just write a bunch of blank lines.
1826
		if ($settingsArray[$i] != '' || @$settingsArray[$i - 1] != '')
1827
			fwrite($fp, strtr($settingsArray[$i], "\r", ''));
0 ignored issues
show
Security File Manipulation introduced by
strtr($settingsArray[$i], ' ', '') can contain request data and is used in file manipulation context(s) leading to a potential security vulnerability.

5 paths for user data to reach this point

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

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...
1828
	}
1829
	fwrite($fp, $settingsArray[$i] . '?' . '>');
0 ignored issues
show
Security File Manipulation introduced by
$settingsArray[$i] . '?' . '>' can contain request data and is used in file manipulation context(s) leading to a potential security vulnerability.

5 paths for user data to reach this point

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

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