Passed
Pull Request — release-2.1 (#7077)
by Jeremy
11:09
created

installer_updateSettingsFile()   A

Complexity

Conditions 6
Paths 7

Size

Total Lines 26
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 14
nc 7
nop 2
dl 0
loc 26
rs 9.2222
c 1
b 0
f 0
1
<?php
2
3
/**
4
 * Simple Machines Forum (SMF)
5
 *
6
 * @package SMF
7
 * @author Simple Machines https://www.simplemachines.org
8
 * @copyright 2021 Simple Machines and individual contributors
9
 * @license https://www.simplemachines.org/about/smf/license.php BSD
10
 *
11
 * @version 2.1 RC4
12
 */
13
14
define('SMF_VERSION', '2.1 RC4');
15
define('SMF_FULL_VERSION', 'SMF ' . SMF_VERSION);
16
define('SMF_SOFTWARE_YEAR', '2021');
17
define('DB_SCRIPT_VERSION', '2-1');
18
define('SMF_INSTALLING', 1);
19
20
define('JQUERY_VERSION', '3.6.0');
21
define('POSTGRE_TITLE', 'PostgreSQL');
22
define('MYSQL_TITLE', 'MySQL');
23
define('SMF_USER_AGENT', 'Mozilla/5.0 (' . php_uname('s') . ' ' . php_uname('m') . ') AppleWebKit/605.1.15 (KHTML, like Gecko)  SMF/' . strtr(SMF_VERSION, ' ', '.'));
24
if (!defined('TIME_START'))
25
	define('TIME_START', microtime(true));
26
27
$GLOBALS['required_php_version'] = '7.0.0';
28
29
// Don't have PHP support, do you?
30
// ><html dir="ltr"><head><title>Error!</title></head><body>Sorry, this installer requires PHP!<div style="display: none;">
31
32
// Let's pull in useful classes
33
if (!defined('SMF'))
34
	define('SMF', 1);
35
36
require_once('Sources/Class-Package.php');
37
38
// Database info.
39
$databases = array(
40
	'mysql' => array(
41
		'name' => 'MySQL',
42
		'version' => '5.6.0',
43
		'version_check' => function() {
44
			global $db_connection;
45
			if (!function_exists('mysqli_fetch_row'))
46
				return false;
47
			return mysqli_fetch_row(mysqli_query($db_connection, 'SELECT VERSION();'))[0];
0 ignored issues
show
Bug introduced by
It seems like mysqli_query($db_connection, 'SELECT VERSION();') can also be of type true; however, parameter $result of mysqli_fetch_row() does only seem to accept mysqli_result, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

47
			return mysqli_fetch_row(/** @scrutinizer ignore-type */ mysqli_query($db_connection, 'SELECT VERSION();'))[0];
Loading history...
48
		},
49
		'supported' => function_exists('mysqli_connect'),
50
		'default_user' => 'mysql.default_user',
51
		'default_password' => 'mysql.default_password',
52
		'default_host' => 'mysql.default_host',
53
		'default_port' => 'mysql.default_port',
54
		'utf8_support' => function()
55
		{
56
			return true;
57
		},
58
		'utf8_version' => '5.0.22',
59
		'utf8_version_check' => function() {
60
			global $db_connection;
61
			return mysqli_get_server_info($db_connection);
62
		},
63
		'alter_support' => true,
64
		'validate_prefix' => function(&$value)
65
		{
66
			$value = preg_replace('~[^A-Za-z0-9_\$]~', '', $value);
67
			return true;
68
		},
69
	),
70
	'postgresql' => array(
71
		'name' => 'PostgreSQL',
72
		'version' => '9.6',
73
		'version_check' => function() {
74
			$request = pg_query('SELECT version()');
0 ignored issues
show
Bug introduced by
The call to pg_query() has too few arguments starting with query. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

74
			$request = /** @scrutinizer ignore-call */ pg_query('SELECT version()');

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
Bug introduced by
'SELECT version()' of type string is incompatible with the type resource expected by parameter $connection of pg_query(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

74
			$request = pg_query(/** @scrutinizer ignore-type */ 'SELECT version()');
Loading history...
75
			list ($version) = pg_fetch_row($request);
76
			list($pgl, $version) = explode(' ', $version);
77
			return $version;
78
		},
79
		'supported' => function_exists('pg_connect'),
80
		'always_has_db' => true,
81
		'utf8_support' => function()
82
		{
83
			$request = pg_query('SHOW SERVER_ENCODING');
0 ignored issues
show
Bug introduced by
'SHOW SERVER_ENCODING' of type string is incompatible with the type resource expected by parameter $connection of pg_query(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

83
			$request = pg_query(/** @scrutinizer ignore-type */ 'SHOW SERVER_ENCODING');
Loading history...
Bug introduced by
The call to pg_query() has too few arguments starting with query. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

83
			$request = /** @scrutinizer ignore-call */ pg_query('SHOW SERVER_ENCODING');

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
84
85
			list ($charcode) = pg_fetch_row($request);
86
87
			if ($charcode == 'UTF8')
88
				return true;
89
			else
90
				return false;
91
		},
92
		'utf8_version' => '8.0',
93
		'utf8_version_check' => function (){
94
			$request = pg_query('SELECT version()');
0 ignored issues
show
Bug introduced by
The call to pg_query() has too few arguments starting with query. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

94
			$request = /** @scrutinizer ignore-call */ pg_query('SELECT version()');

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
Bug introduced by
'SELECT version()' of type string is incompatible with the type resource expected by parameter $connection of pg_query(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

94
			$request = pg_query(/** @scrutinizer ignore-type */ 'SELECT version()');
Loading history...
95
			list ($version) = pg_fetch_row($request);
96
			list($pgl, $version) = explode(' ', $version);
97
			return $version;
98
		},
99
		'validate_prefix' => function(&$value)
100
		{
101
			global $txt;
102
103
			$value = preg_replace('~[^A-Za-z0-9_\$]~', '', $value);
104
105
			// Is it reserved?
106
			if ($value == 'pg_')
107
				return $txt['error_db_prefix_reserved'];
108
109
			// Is the prefix numeric?
110
			if (preg_match('~^\d~', $value))
111
				return $txt['error_db_prefix_numeric'];
112
113
			return true;
114
		},
115
	),
116
);
117
118
global $txt;
119
120
// Initialize everything and load the language files.
121
initialize_inputs();
122
load_lang_file();
123
124
// This is what we are.
125
$installurl = $_SERVER['PHP_SELF'];
126
127
// All the steps in detail.
128
// Number,Name,Function,Progress Weight.
129
$incontext['steps'] = array(
130
	0 => array(1, $txt['install_step_welcome'], 'Welcome', 0),
131
	1 => array(2, $txt['install_step_writable'], 'CheckFilesWritable', 10),
132
	2 => array(3, $txt['install_step_databaseset'], 'DatabaseSettings', 15),
133
	3 => array(4, $txt['install_step_forum'], 'ForumSettings', 40),
134
	4 => array(5, $txt['install_step_databasechange'], 'DatabasePopulation', 15),
135
	5 => array(6, $txt['install_step_admin'], 'AdminAccount', 20),
136
	6 => array(7, $txt['install_step_delete'], 'DeleteInstall', 0),
137
);
138
139
// Default title...
140
$incontext['page_title'] = $txt['smf_installer'];
141
142
// What step are we on?
143
$incontext['current_step'] = isset($_GET['step']) ? (int) $_GET['step'] : 0;
144
145
// Loop through all the steps doing each one as required.
146
$incontext['overall_percent'] = 0;
147
148
foreach ($incontext['steps'] as $num => $step)
149
{
150
	if ($num >= $incontext['current_step'])
151
	{
152
		// The current weight of this step in terms of overall progress.
153
		$incontext['step_weight'] = $step[3];
154
		// Make sure we reset the skip button.
155
		$incontext['skip'] = false;
156
157
		// Call the step and if it returns false that means pause!
158
		if (function_exists($step[2]) && $step[2]() === false)
159
			break;
160
		elseif (function_exists($step[2]))
161
			$incontext['current_step']++;
162
163
		// No warnings pass on.
164
		$incontext['warning'] = '';
165
	}
166
	$incontext['overall_percent'] += $step[3];
167
}
168
169
// Actually do the template stuff.
170
installExit();
171
172
function initialize_inputs()
173
{
174
	global $databases;
175
176
	// Just so people using older versions of PHP aren't left in the cold.
177
	if (!isset($_SERVER['PHP_SELF']))
178
		$_SERVER['PHP_SELF'] = isset($GLOBALS['HTTP_SERVER_VARS']['PHP_SELF']) ? $GLOBALS['HTTP_SERVER_VARS']['PHP_SELF'] : 'install.php';
179
180
	// In pre-release versions, report all errors.
181
	if (strspn(SMF_VERSION, '1234567890.') !== strlen(SMF_VERSION))
182
		error_reporting(E_ALL);
183
	// Otherwise, report all errors except for deprecation notices.
184
	else
185
		error_reporting(E_ALL & ~E_DEPRECATED);
186
187
	// Fun.  Low PHP version...
188
	if (!isset($_GET))
189
	{
190
		$GLOBALS['_GET']['step'] = 0;
191
		return;
192
	}
193
194
	if (!isset($_GET['obgz']))
195
	{
196
		ob_start();
197
198
		if (ini_get('session.save_handler') == 'user')
199
			@ini_set('session.save_handler', 'files');
200
		if (function_exists('session_start'))
201
			@session_start();
202
	}
203
	else
204
	{
205
		ob_start('ob_gzhandler');
206
207
		if (ini_get('session.save_handler') == 'user')
208
			@ini_set('session.save_handler', 'files');
209
		session_start();
210
211
		if (!headers_sent())
212
			echo '<!DOCTYPE html>
213
<html>
214
	<head>
215
		<title>', htmlspecialchars($_GET['pass_string']), '</title>
216
	</head>
217
	<body style="background-color: #d4d4d4; margin-top: 16%; text-align: center; font-size: 16pt;">
218
		<strong>', htmlspecialchars($_GET['pass_string']), '</strong>
219
	</body>
220
</html>';
221
		exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
222
	}
223
224
	// This is really quite simple; if ?delete is on the URL, delete the installer...
225
	if (isset($_GET['delete']))
226
	{
227
		if (isset($_SESSION['installer_temp_ftp']))
228
		{
229
			$ftp = new ftp_connection($_SESSION['installer_temp_ftp']['server'], $_SESSION['installer_temp_ftp']['port'], $_SESSION['installer_temp_ftp']['username'], $_SESSION['installer_temp_ftp']['password']);
230
			$ftp->chdir($_SESSION['installer_temp_ftp']['path']);
231
232
			$ftp->unlink('install.php');
233
234
			foreach ($databases as $key => $dummy)
235
			{
236
				$type = ($key == 'mysqli') ? 'mysql' : $key;
237
				$ftp->unlink('install_' . DB_SCRIPT_VERSION . '_' . $type . '.sql');
238
			}
239
240
			$ftp->close();
241
242
			unset($_SESSION['installer_temp_ftp']);
243
		}
244
		else
245
		{
246
			@unlink(__FILE__);
247
248
			foreach ($databases as $key => $dummy)
249
			{
250
				$type = ($key == 'mysqli') ? 'mysql' : $key;
251
				@unlink(dirname(__FILE__) . '/install_' . DB_SCRIPT_VERSION . '_' . $type . '.sql');
252
			}
253
		}
254
255
		// Now just redirect to a blank.png...
256
		$secure = false;
257
258
		if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on')
259
			$secure = true;
260
		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')
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: (! empty($_SERVER['HTTP_...FORWARDED_SSL'] == 'on', Probably Intended Meaning: ! empty($_SERVER['HTTP_X...ORWARDED_SSL'] == 'on')
Loading history...
261
			$secure = true;
262
263
		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');
264
		exit;
265
	}
266
267
	// PHP 5 might cry if we don't do this now.
268
	if (function_exists('date_default_timezone_set'))
269
	{
270
		// Get PHP's default timezone, if set
271
		$ini_tz = ini_get('date.timezone');
272
		if (!empty($ini_tz))
273
			$timezone_id = $ini_tz;
274
		else
275
			$timezone_id = '';
276
277
		// If date.timezone is unset, invalid, or just plain weird, make a best guess
278
		if (!in_array($timezone_id, timezone_identifiers_list()))
0 ignored issues
show
Bug introduced by
Are you sure the usage of timezone_identifiers_list() is correct as it seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
Bug introduced by
timezone_identifiers_list() of type void is incompatible with the type array expected by parameter $haystack of in_array(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

278
		if (!in_array($timezone_id, /** @scrutinizer ignore-type */ timezone_identifiers_list()))
Loading history...
279
		{
280
			$server_offset = @mktime(0, 0, 0, 1, 1, 1970);
281
			$timezone_id = timezone_name_from_abbr('', $server_offset, 0);
0 ignored issues
show
Bug introduced by
It seems like $server_offset can also be of type false; however, parameter $utcOffset of timezone_name_from_abbr() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

281
			$timezone_id = timezone_name_from_abbr('', /** @scrutinizer ignore-type */ $server_offset, 0);
Loading history...
282
		}
283
284
		date_default_timezone_set($timezone_id);
285
	}
286
	header('X-Frame-Options: SAMEORIGIN');
287
	header('X-XSS-Protection: 1');
288
	header('X-Content-Type-Options: nosniff');
289
290
	// Force an integer step, defaulting to 0.
291
	$_GET['step'] = (int) @$_GET['step'];
292
}
293
294
// Load the list of language files, and the current language file.
295
function load_lang_file()
296
{
297
	global $incontext, $user_info, $txt;
298
299
	$incontext['detected_languages'] = array();
300
301
	// Make sure the languages directory actually exists.
302
	if (file_exists(dirname(__FILE__) . '/Themes/default/languages'))
303
	{
304
		// Find all the "Install" language files in the directory.
305
		$dir = dir(dirname(__FILE__) . '/Themes/default/languages');
306
		while ($entry = $dir->read())
307
		{
308
			if (substr($entry, 0, 8) == 'Install.' && substr($entry, -4) == '.php')
309
				$incontext['detected_languages'][$entry] = ucfirst(substr($entry, 8, strlen($entry) - 12));
310
		}
311
		$dir->close();
312
	}
313
314
	// Didn't find any, show an error message!
315
	if (empty($incontext['detected_languages']))
316
	{
317
		// Let's not cache this message, eh?
318
		header('expires: Mon, 26 Jul 1997 05:00:00 GMT');
319
		header('last-modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
320
		header('cache-control: no-cache');
321
322
		echo '<!DOCTYPE html>
323
<html>
324
	<head>
325
		<title>SMF Installer: Error!</title>
326
		<style>
327
			body {
328
				font-family: sans-serif;
329
				max-width: 700px; }
330
331
			h1 {
332
				font-size: 14pt; }
333
334
			.directory {
335
				margin: 0.3em;
336
				font-family: monospace;
337
				font-weight: bold; }
338
		</style>
339
	</head>
340
	<body>
341
		<h1>A critical error has occurred.</h1>
342
343
		<p>This installer was unable to find the installer\'s language file or files. They should be found under:</p>
344
345
		<div class="directory">', dirname($_SERVER['PHP_SELF']) != '/' ? dirname($_SERVER['PHP_SELF']) : '', '/Themes/default/languages</div>
346
347
		<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>
348
		<p>If that doesn\'t help, please make sure this install.php file is in the same place as the Themes folder.</p>
349
		<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>
350
	</div></body>
351
</html>';
352
		die;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
353
	}
354
355
	// Override the language file?
356
	if (isset($_GET['lang_file']))
357
		$_SESSION['installer_temp_lang'] = $_GET['lang_file'];
358
	elseif (isset($GLOBALS['HTTP_GET_VARS']['lang_file']))
359
		$_SESSION['installer_temp_lang'] = $GLOBALS['HTTP_GET_VARS']['lang_file'];
360
361
	// Make sure it exists, if it doesn't reset it.
362
	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']))
363
	{
364
		// Use the first one...
365
		list ($_SESSION['installer_temp_lang']) = array_keys($incontext['detected_languages']);
366
367
		// If we have english and some other language, use the other language.  We Americans hate english :P.
368
		if ($_SESSION['installer_temp_lang'] == 'Install.english.php' && count($incontext['detected_languages']) > 1)
369
			list (, $_SESSION['installer_temp_lang']) = array_keys($incontext['detected_languages']);
370
	}
371
372
	// And now include the actual language file itself.
373
	require_once(dirname(__FILE__) . '/Themes/default/languages/' . $_SESSION['installer_temp_lang']);
374
375
	// Which language did we load? Assume that he likes his language.
376
	preg_match('~^Install\.(.+[^-utf8])\.php$~', $_SESSION['installer_temp_lang'], $matches);
377
	$user_info['language'] = $matches[1];
378
}
379
380
// This handy function loads some settings and the like.
381
function load_database()
382
{
383
	global $db_prefix, $db_connection, $sourcedir, $smcFunc, $modSettings, $db_port;
384
	global $db_server, $db_passwd, $db_type, $db_name, $db_user, $db_persist, $db_mb4;
385
386
	if (empty($sourcedir))
387
		$sourcedir = dirname(__FILE__) . '/Sources';
388
389
	// Need this to check whether we need the database password.
390
	require(dirname(__FILE__) . '/Settings.php');
391
	if (!defined('SMF'))
392
		define('SMF', 1);
393
	if (empty($smcFunc))
394
		$smcFunc = array();
395
396
	$modSettings['disableQueryCheck'] = true;
397
398
	// Connect the database.
399
	if (!$db_connection)
400
	{
401
		require_once($sourcedir . '/Subs-Db-' . $db_type . '.php');
402
403
		$options = array('persist' => $db_persist);
404
405
		if (!empty($db_port))
406
			$options['port'] = $db_port;
407
408
		if (!empty($db_mb4))
409
			$options['db_mb4'] = $db_mb4;
410
411
		if (!$db_connection)
412
			$db_connection = smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, $db_prefix, $options);
413
	}
414
}
415
416
// This is called upon exiting the installer, for template etc.
417
function installExit($fallThrough = false)
418
{
419
	global $incontext, $installurl, $txt;
420
421
	// Send character set.
422
	header('content-type: text/html; charset=' . (isset($txt['lang_character_set']) ? $txt['lang_character_set'] : 'UTF-8'));
423
424
	// We usually dump our templates out.
425
	if (!$fallThrough)
426
	{
427
		// The top install bit.
428
		template_install_above();
429
430
		// Call the template.
431
		if (isset($incontext['sub_template']))
432
		{
433
			$incontext['form_url'] = $installurl . '?step=' . $incontext['current_step'];
434
435
			call_user_func('template_' . $incontext['sub_template']);
436
		}
437
		// @todo REMOVE THIS!!
438
		else
439
		{
440
			if (function_exists('doStep' . $_GET['step']))
441
				call_user_func('doStep' . $_GET['step']);
442
		}
443
		// Show the footer.
444
		template_install_below();
445
	}
446
447
	// Bang - gone!
448
	die();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
449
}
450
451
function Welcome()
452
{
453
	global $incontext, $txt, $databases, $installurl;
454
455
	$incontext['page_title'] = $txt['install_welcome'];
456
	$incontext['sub_template'] = 'welcome_message';
457
458
	// Done the submission?
459
	if (isset($_POST['contbutt']))
460
		return true;
461
462
	// See if we think they have already installed it?
463
	if (is_readable(dirname(__FILE__) . '/Settings.php'))
464
	{
465
		$probably_installed = 0;
466
		foreach (file(dirname(__FILE__) . '/Settings.php') as $line)
467
		{
468
			if (preg_match('~^\$db_passwd\s=\s\'([^\']+)\';$~', $line))
469
				$probably_installed++;
470
			if (preg_match('~^\$boardurl\s=\s\'([^\']+)\';~', $line) && !preg_match('~^\$boardurl\s=\s\'http://127\.0\.0\.1/smf\';~', $line))
471
				$probably_installed++;
472
		}
473
474
		if ($probably_installed == 2)
475
			$incontext['warning'] = $txt['error_already_installed'];
476
	}
477
478
	// Is some database support even compiled in?
479
	$incontext['supported_databases'] = array();
480
	foreach ($databases as $key => $db)
481
	{
482
		if ($db['supported'])
483
		{
484
			$type = ($key == 'mysqli') ? 'mysql' : $key;
485
			if (!file_exists(dirname(__FILE__) . '/install_' . DB_SCRIPT_VERSION . '_' . $type . '.sql'))
486
			{
487
				$databases[$key]['supported'] = false;
488
				$notFoundSQLFile = true;
489
				$txt['error_db_script_missing'] = sprintf($txt['error_db_script_missing'], 'install_' . DB_SCRIPT_VERSION . '_' . $type . '.sql');
490
			}
491
			else
492
				$incontext['supported_databases'][] = $db;
493
		}
494
	}
495
496
	// Check the PHP version.
497
	if ((!function_exists('version_compare') || version_compare($GLOBALS['required_php_version'], PHP_VERSION, '>=')))
498
		$error = 'error_php_too_low';
499
	// Make sure we have a supported database
500
	elseif (empty($incontext['supported_databases']))
501
		$error = empty($notFoundSQLFile) ? 'error_db_missing' : 'error_db_script_missing';
502
	// How about session support?  Some crazy sysadmin remove it?
503
	elseif (!function_exists('session_start'))
504
		$error = 'error_session_missing';
505
	// Make sure they uploaded all the files.
506
	elseif (!file_exists(dirname(__FILE__) . '/index.php'))
507
		$error = 'error_missing_files';
508
	// Very simple check on the session.save_path for Windows.
509
	// @todo Move this down later if they don't use database-driven sessions?
510
	elseif (@ini_get('session.save_path') == '/tmp' && substr(__FILE__, 1, 2) == ':\\')
511
		$error = 'error_session_save_path';
512
513
	// Since each of the three messages would look the same, anyway...
514
	if (isset($error))
515
		$incontext['error'] = $txt[$error];
516
517
	// Mod_security blocks everything that smells funny. Let SMF handle security.
518
	if (!fixModSecurity() && !isset($_GET['overmodsecurity']))
519
		$incontext['error'] = $txt['error_mod_security'] . '<br><br><a href="' . $installurl . '?overmodsecurity=true">' . $txt['error_message_click'] . '</a> ' . $txt['error_message_bad_try_again'];
520
521
	// Confirm mbstring is loaded...
522
	if (!extension_loaded('mbstring'))
523
		$incontext['error'] = $txt['install_no_mbstring'];
524
525
	// Check for https stream support.
526
	$supported_streams = stream_get_wrappers();
527
	if (!in_array('https', $supported_streams))
528
		$incontext['warning'] = $txt['install_no_https'];
529
530
	return false;
531
}
532
533
function CheckFilesWritable()
534
{
535
	global $txt, $incontext;
536
537
	$incontext['page_title'] = $txt['ftp_checking_writable'];
538
	$incontext['sub_template'] = 'chmod_files';
539
540
	$writable_files = array(
541
		'attachments',
542
		'avatars',
543
		'custom_avatar',
544
		'cache',
545
		'Packages',
546
		'Smileys',
547
		'Themes',
548
		'agreement.txt',
549
		'Settings.php',
550
		'Settings_bak.php',
551
		'cache/db_last_error.php',
552
	);
553
554
	foreach ($incontext['detected_languages'] as $lang => $temp)
555
		$extra_files[] = 'Themes/default/languages/' . $lang;
556
557
	// With mod_security installed, we could attempt to fix it with .htaccess.
558
	if (function_exists('apache_get_modules') && in_array('mod_security', apache_get_modules()))
559
		$writable_files[] = file_exists(dirname(__FILE__) . '/.htaccess') ? '.htaccess' : '.';
560
561
	$failed_files = array();
562
563
	// On linux, it's easy - just use is_writable!
564
	if (substr(__FILE__, 1, 2) != ':\\')
565
	{
566
		$incontext['systemos'] = 'linux';
567
568
		foreach ($writable_files as $file)
569
		{
570
			// Some files won't exist, try to address up front
571
			if (!file_exists(dirname(__FILE__) . '/' . $file))
572
				@touch(dirname(__FILE__) . '/' . $file);
573
			// NOW do the writable check...
574
			if (!is_writable(dirname(__FILE__) . '/' . $file))
575
			{
576
				@chmod(dirname(__FILE__) . '/' . $file, 0755);
577
578
				// Well, 755 hopefully worked... if not, try 777.
579
				if (!is_writable(dirname(__FILE__) . '/' . $file) && !@chmod(dirname(__FILE__) . '/' . $file, 0777))
580
					$failed_files[] = $file;
581
			}
582
		}
583
		foreach ($extra_files as $file)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $extra_files seems to be defined by a foreach iteration on line 554. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
584
			@chmod(dirname(__FILE__) . (empty($file) ? '' : '/' . $file), 0777);
585
	}
586
	// Windows is trickier.  Let's try opening for r+...
587
	else
588
	{
589
		$incontext['systemos'] = 'windows';
590
591
		foreach ($writable_files as $file)
592
		{
593
			// Folders can't be opened for write... but the index.php in them can ;)
594
			if (is_dir(dirname(__FILE__) . '/' . $file))
595
				$file .= '/index.php';
596
597
			// Funny enough, chmod actually does do something on windows - it removes the read only attribute.
598
			@chmod(dirname(__FILE__) . '/' . $file, 0777);
599
			$fp = @fopen(dirname(__FILE__) . '/' . $file, 'r+');
600
601
			// Hmm, okay, try just for write in that case...
602
			if (!is_resource($fp))
603
				$fp = @fopen(dirname(__FILE__) . '/' . $file, 'w');
604
605
			if (!is_resource($fp))
606
				$failed_files[] = $file;
607
608
			@fclose($fp);
0 ignored issues
show
Bug introduced by
It seems like $fp can also be of type false; however, parameter $stream of fclose() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

608
			@fclose(/** @scrutinizer ignore-type */ $fp);
Loading history...
609
		}
610
		foreach ($extra_files as $file)
611
			@chmod(dirname(__FILE__) . (empty($file) ? '' : '/' . $file), 0777);
612
	}
613
614
	$failure = count($failed_files) >= 1;
615
616
	if (!isset($_SERVER))
617
		return !$failure;
618
619
	// Put the list into context.
620
	$incontext['failed_files'] = $failed_files;
621
622
	// It's not going to be possible to use FTP on windows to solve the problem...
623
	if ($failure && substr(__FILE__, 1, 2) == ':\\')
624
	{
625
		$incontext['error'] = $txt['error_windows_chmod'] . '
626
					<ul class="error_content">
627
						<li>' . implode('</li>
628
						<li>', $failed_files) . '</li>
629
					</ul>';
630
631
		return false;
632
	}
633
	// We're going to have to use... FTP!
634
	elseif ($failure)
635
	{
636
		// Load any session data we might have...
637
		if (!isset($_POST['ftp_username']) && isset($_SESSION['installer_temp_ftp']))
638
		{
639
			$_POST['ftp_server'] = $_SESSION['installer_temp_ftp']['server'];
640
			$_POST['ftp_port'] = $_SESSION['installer_temp_ftp']['port'];
641
			$_POST['ftp_username'] = $_SESSION['installer_temp_ftp']['username'];
642
			$_POST['ftp_password'] = $_SESSION['installer_temp_ftp']['password'];
643
			$_POST['ftp_path'] = $_SESSION['installer_temp_ftp']['path'];
644
		}
645
646
		$incontext['ftp_errors'] = array();
647
		require_once('Sources/Class-Package.php');
648
		if (isset($_POST['ftp_username']))
649
		{
650
			$ftp = new ftp_connection($_POST['ftp_server'], $_POST['ftp_port'], $_POST['ftp_username'], $_POST['ftp_password']);
651
652
			if ($ftp->error === false)
0 ignored issues
show
introduced by
The condition $ftp->error === false is always false.
Loading history...
653
			{
654
				// Try it without /home/abc just in case they messed up.
655
				if (!$ftp->chdir($_POST['ftp_path']))
656
				{
657
					$incontext['ftp_errors'][] = $ftp->last_message;
658
					$ftp->chdir(preg_replace('~^/home[2]?/[^/]+?~', '', $_POST['ftp_path']));
659
				}
660
			}
661
		}
662
663
		if (!isset($ftp) || $ftp->error !== false)
664
		{
665
			if (!isset($ftp))
666
				$ftp = new ftp_connection(null);
667
			// Save the error so we can mess with listing...
668
			elseif ($ftp->error !== false && empty($incontext['ftp_errors']) && !empty($ftp->last_message))
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $ftp does not seem to be defined for all execution paths leading up to this point.
Loading history...
669
				$incontext['ftp_errors'][] = $ftp->last_message;
670
671
			list ($username, $detect_path, $found_path) = $ftp->detect_path(dirname(__FILE__));
672
673
			if (empty($_POST['ftp_path']) && $found_path)
674
				$_POST['ftp_path'] = $detect_path;
675
676
			if (!isset($_POST['ftp_username']))
677
				$_POST['ftp_username'] = $username;
678
679
			// Set the username etc, into context.
680
			$incontext['ftp'] = array(
681
				'server' => isset($_POST['ftp_server']) ? $_POST['ftp_server'] : 'localhost',
682
				'port' => isset($_POST['ftp_port']) ? $_POST['ftp_port'] : '21',
683
				'username' => isset($_POST['ftp_username']) ? $_POST['ftp_username'] : '',
684
				'path' => isset($_POST['ftp_path']) ? $_POST['ftp_path'] : '/',
685
				'path_msg' => !empty($found_path) ? $txt['ftp_path_found_info'] : $txt['ftp_path_info'],
686
			);
687
688
			return false;
689
		}
690
		else
691
		{
692
			$_SESSION['installer_temp_ftp'] = array(
693
				'server' => $_POST['ftp_server'],
694
				'port' => $_POST['ftp_port'],
695
				'username' => $_POST['ftp_username'],
696
				'password' => $_POST['ftp_password'],
697
				'path' => $_POST['ftp_path']
698
			);
699
700
			$failed_files_updated = array();
701
702
			foreach ($failed_files as $file)
703
			{
704
				if (!is_writable(dirname(__FILE__) . '/' . $file))
705
					$ftp->chmod($file, 0755);
706
				if (!is_writable(dirname(__FILE__) . '/' . $file))
707
					$ftp->chmod($file, 0777);
708
				if (!is_writable(dirname(__FILE__) . '/' . $file))
709
				{
710
					$failed_files_updated[] = $file;
711
					$incontext['ftp_errors'][] = rtrim($ftp->last_message) . ' -> ' . $file . "\n";
712
				}
713
			}
714
715
			$ftp->close();
716
717
			// Are there any errors left?
718
			if (count($failed_files_updated) >= 1)
719
			{
720
				// Guess there are...
721
				$incontext['failed_files'] = $failed_files_updated;
722
723
				// Set the username etc, into context.
724
				$incontext['ftp'] = $_SESSION['installer_temp_ftp'] += array(
725
					'path_msg' => $txt['ftp_path_info'],
726
				);
727
728
				return false;
729
			}
730
		}
731
	}
732
733
	return true;
734
}
735
736
function DatabaseSettings()
737
{
738
	global $txt, $databases, $incontext, $smcFunc, $sourcedir;
739
	global $db_server, $db_name, $db_user, $db_passwd, $db_port, $db_mb4, $db_connection;
740
741
	$incontext['sub_template'] = 'database_settings';
742
	$incontext['page_title'] = $txt['db_settings'];
743
	$incontext['continue'] = 1;
744
745
	// Set up the defaults.
746
	$incontext['db']['server'] = 'localhost';
747
	$incontext['db']['user'] = '';
748
	$incontext['db']['name'] = '';
749
	$incontext['db']['pass'] = '';
750
	$incontext['db']['type'] = '';
751
	$incontext['supported_databases'] = array();
752
753
	$foundOne = false;
754
	foreach ($databases as $key => $db)
755
	{
756
		// Override with the defaults for this DB if appropriate.
757
		if ($db['supported'])
758
		{
759
			$incontext['supported_databases'][$key] = $db;
760
761
			if (!$foundOne)
762
			{
763
				if (isset($db['default_host']))
764
					$incontext['db']['server'] = ini_get($db['default_host']) or $incontext['db']['server'] = 'localhost';
765
				if (isset($db['default_user']))
766
				{
767
					$incontext['db']['user'] = ini_get($db['default_user']);
768
					$incontext['db']['name'] = ini_get($db['default_user']);
769
				}
770
				if (isset($db['default_password']))
771
					$incontext['db']['pass'] = ini_get($db['default_password']);
772
773
				// For simplicity and less confusion, leave the port blank by default
774
				$incontext['db']['port'] = '';
775
776
				$incontext['db']['type'] = $key;
777
				$foundOne = true;
778
			}
779
		}
780
	}
781
782
	// Override for repost.
783
	if (isset($_POST['db_user']))
784
	{
785
		$incontext['db']['user'] = $_POST['db_user'];
786
		$incontext['db']['name'] = $_POST['db_name'];
787
		$incontext['db']['server'] = $_POST['db_server'];
788
		$incontext['db']['prefix'] = $_POST['db_prefix'];
789
790
		if (!empty($_POST['db_port']))
791
			$incontext['db']['port'] = $_POST['db_port'];
792
	}
793
	else
794
	{
795
		$incontext['db']['prefix'] = 'smf_';
796
	}
797
798
	// Are we submitting?
799
	if (isset($_POST['db_type']))
800
	{
801
		// What type are they trying?
802
		$db_type = preg_replace('~[^A-Za-z0-9]~', '', $_POST['db_type']);
803
		$db_prefix = $_POST['db_prefix'];
804
		// Validate the prefix.
805
		$valid_prefix = $databases[$db_type]['validate_prefix']($db_prefix);
806
807
		if ($valid_prefix !== true)
808
		{
809
			$incontext['error'] = $valid_prefix;
810
			return false;
811
		}
812
813
		// Take care of these variables...
814
		$vars = array(
815
			'db_type' => $db_type,
816
			'db_name' => $_POST['db_name'],
817
			'db_user' => $_POST['db_user'],
818
			'db_passwd' => isset($_POST['db_passwd']) ? $_POST['db_passwd'] : '',
819
			'db_server' => $_POST['db_server'],
820
			'db_prefix' => $db_prefix,
821
			// The cookiename is special; we want it to be the same if it ever needs to be reinstalled with the same info.
822
			'cookiename' => 'SMFCookie' . abs(crc32($_POST['db_name'] . preg_replace('~[^A-Za-z0-9_$]~', '', $_POST['db_prefix'])) % 1000),
823
		);
824
825
		// Only set the port if we're not using the default
826
		if (!empty($_POST['db_port']))
827
		{
828
			// For MySQL, we can get the "default port" from PHP. PostgreSQL has no such option though.
829
			if (($db_type == 'mysql' || $db_type == 'mysqli') && $_POST['db_port'] != ini_get($db_type . '.default_port'))
830
				$vars['db_port'] = (int) $_POST['db_port'];
831
			elseif ($db_type == 'postgresql' && $_POST['db_port'] != 5432)
832
				$vars['db_port'] = (int) $_POST['db_port'];
833
		}
834
835
		// God I hope it saved!
836
		if (!installer_updateSettingsFile($vars))
837
		{
838
			$incontext['error'] = $txt['settings_error'];
839
			return false;
840
		}
841
842
		// Make sure it works.
843
		require(dirname(__FILE__) . '/Settings.php');
844
845
		if (empty($sourcedir))
846
			$sourcedir = dirname(__FILE__) . '/Sources';
847
848
		// Better find the database file!
849
		if (!file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php'))
850
		{
851
			$incontext['error'] = sprintf($txt['error_db_file'], 'Subs-Db-' . $db_type . '.php');
852
			return false;
853
		}
854
855
		// Now include it for database functions!
856
		if (!defined('SMF'))
857
			define('SMF', 1);
858
859
		$modSettings['disableQueryCheck'] = true;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$modSettings was never initialized. Although not strictly required by PHP, it is generally a good practice to add $modSettings = array(); before regardless.
Loading history...
860
		if (empty($smcFunc))
861
			$smcFunc = array();
862
863
		require_once($sourcedir . '/Subs-Db-' . $db_type . '.php');
864
865
		// Attempt a connection.
866
		$needsDB = !empty($databases[$db_type]['always_has_db']);
867
868
		$options = array('non_fatal' => true, 'dont_select_db' => !$needsDB);
869
		// Add in the port if needed
870
		if (!empty($db_port))
871
			$options['port'] = $db_port;
872
873
		if (!empty($db_mb4))
874
			$options['db_mb4'] = $db_mb4;
875
876
		$db_connection = smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, $db_prefix, $options);
877
878
		// Still no connection?  Big fat error message :P.
879
		if (!$db_connection)
0 ignored issues
show
introduced by
$db_connection is of type null|resource, thus it always evaluated to false.
Loading history...
880
		{
881
			// Get error info...  Recast just in case we get false or 0...
882
			$error_message = $smcFunc['db_connect_error']();
883
			if (empty($error_message))
884
				$error_message = '';
885
			$error_number = $smcFunc['db_connect_errno']();
886
			if (empty($error_number))
887
				$error_number = '';
888
			$db_error = (!empty($error_number) ? $error_number . ': ' : '') . $error_message;
889
890
			$incontext['error'] = $txt['error_db_connect'] . '<div class="error_content"><strong>' . $db_error . '</strong></div>';
891
			return false;
892
		}
893
894
		// Do they meet the install requirements?
895
		// @todo Old client, new server?
896
		if (version_compare($databases[$db_type]['version'], preg_replace('~^\D*|\-.+?$~', '', $databases[$db_type]['version_check']())) > 0)
897
		{
898
			$incontext['error'] = $txt['error_db_too_low'];
899
			return false;
900
		}
901
902
		// Let's try that database on for size... assuming we haven't already lost the opportunity.
903
		if ($db_name != '' && !$needsDB)
904
		{
905
			$smcFunc['db_query']('', "
906
				CREATE DATABASE IF NOT EXISTS `$db_name`",
907
				array(
908
					'security_override' => true,
909
					'db_error_skip' => true,
910
				),
911
				$db_connection
912
			);
913
914
			// Okay, let's try the prefix if it didn't work...
915
			if (!$smcFunc['db_select_db']($db_name, $db_connection) && $db_name != '')
916
			{
917
				$smcFunc['db_query']('', "
918
					CREATE DATABASE IF NOT EXISTS `$_POST[db_prefix]$db_name`",
919
					array(
920
						'security_override' => true,
921
						'db_error_skip' => true,
922
					),
923
					$db_connection
924
				);
925
926
				if ($smcFunc['db_select_db']($_POST['db_prefix'] . $db_name, $db_connection))
927
				{
928
					$db_name = $_POST['db_prefix'] . $db_name;
929
					installer_updateSettingsFile(array('db_name' => $db_name));
930
				}
931
			}
932
933
			// Okay, now let's try to connect...
934
			if (!$smcFunc['db_select_db']($db_name, $db_connection))
935
			{
936
				$incontext['error'] = sprintf($txt['error_db_database'], $db_name);
937
				return false;
938
			}
939
		}
940
941
		return true;
942
	}
943
944
	return false;
945
}
946
947
// Let's start with basic forum type settings.
948
function ForumSettings()
949
{
950
	global $txt, $incontext, $databases, $db_type, $db_connection, $smcFunc;
951
952
	$incontext['sub_template'] = 'forum_settings';
953
	$incontext['page_title'] = $txt['install_settings'];
954
955
	// Let's see if we got the database type correct.
956
	if (isset($_POST['db_type'], $databases[$_POST['db_type']]))
957
		$db_type = $_POST['db_type'];
958
959
	// Else we'd better be able to get the connection.
960
	else
961
		load_database();
962
963
	$db_type = isset($_POST['db_type']) ? $_POST['db_type'] : $db_type;
964
965
	// What host and port are we on?
966
	$host = empty($_SERVER['HTTP_HOST']) ? $_SERVER['SERVER_NAME'] . (empty($_SERVER['SERVER_PORT']) || $_SERVER['SERVER_PORT'] == '80' ? '' : ':' . $_SERVER['SERVER_PORT']) : $_SERVER['HTTP_HOST'];
967
968
	$secure = false;
969
970
	if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on')
971
		$secure = true;
972
	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')
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: (! empty($_SERVER['HTTP_...FORWARDED_SSL'] == 'on', Probably Intended Meaning: ! empty($_SERVER['HTTP_X...ORWARDED_SSL'] == 'on')
Loading history...
973
		$secure = true;
974
975
	// Now, to put what we've learned together... and add a path.
976
	$incontext['detected_url'] = 'http' . ($secure ? 's' : '') . '://' . $host . substr($_SERVER['PHP_SELF'], 0, strrpos($_SERVER['PHP_SELF'], '/'));
977
978
	// Check if the database sessions will even work.
979
	$incontext['test_dbsession'] = (ini_get('session.auto_start') != 1);
980
981
	$incontext['continue'] = 1;
982
983
	// Check Postgres setting
984
	if ( $db_type === 'postgresql')
985
	{
986
		load_database();
987
		$result = $smcFunc['db_query']('', '
988
			show standard_conforming_strings',
989
			array(
990
				'db_error_skip' => true,
991
			)
992
		);
993
994
		if ($result !== false)
995
		{
996
			$row = $smcFunc['db_fetch_assoc']($result);
997
			if ($row['standard_conforming_strings'] !== 'on')
998
				{
999
					$incontext['continue'] = 0;
1000
					$incontext['error'] = $txt['error_pg_scs'];
1001
				}
1002
			$smcFunc['db_free_result']($result);
1003
		}
1004
	}
1005
1006
	// Setup the SSL checkbox...
1007
	$incontext['ssl_chkbx_protected'] = false;
1008
	$incontext['ssl_chkbx_checked'] = false;
1009
1010
	// If redirect in effect, force ssl ON
1011
	require_once(dirname(__FILE__) . '/Sources/Subs.php');
1012
	if (https_redirect_active($incontext['detected_url']))
1013
	{
1014
		$incontext['ssl_chkbx_protected'] = true;
1015
		$incontext['ssl_chkbx_checked'] = true;
1016
		$_POST['force_ssl'] = true;
1017
	}
1018
	// If no cert, make sure ssl stays OFF
1019
	if (!ssl_cert_found($incontext['detected_url']))
1020
	{
1021
		$incontext['ssl_chkbx_protected'] = true;
1022
		$incontext['ssl_chkbx_checked'] = false;
1023
	}
1024
1025
	// Submitting?
1026
	if (isset($_POST['boardurl']))
1027
	{
1028
		if (substr($_POST['boardurl'], -10) == '/index.php')
1029
			$_POST['boardurl'] = substr($_POST['boardurl'], 0, -10);
1030
		elseif (substr($_POST['boardurl'], -1) == '/')
1031
			$_POST['boardurl'] = substr($_POST['boardurl'], 0, -1);
1032
		if (substr($_POST['boardurl'], 0, 7) != 'http://' && substr($_POST['boardurl'], 0, 7) != 'file://' && substr($_POST['boardurl'], 0, 8) != 'https://')
1033
			$_POST['boardurl'] = 'http://' . $_POST['boardurl'];
1034
1035
		// Make sure boardurl is aligned with ssl setting
1036
		if (empty($_POST['force_ssl']))
1037
			$_POST['boardurl'] = strtr($_POST['boardurl'], array('https://' => 'http://'));
1038
		else
1039
			$_POST['boardurl'] = strtr($_POST['boardurl'], array('http://' => 'https://'));
1040
1041
		// Deal with different operating systems' directory structure...
1042
		$path = rtrim(str_replace(DIRECTORY_SEPARATOR, '/', __DIR__), '/');
1043
1044
		// Load the compatibility library if necessary.
1045
		if (!is_callable('random_bytes'))
1046
			require_once('Sources/random_compat/random.php');
1047
1048
		// Save these variables.
1049
		$vars = array(
1050
			'boardurl' => $_POST['boardurl'],
1051
			'boarddir' => $path,
1052
			'sourcedir' => $path . '/Sources',
1053
			'cachedir' => $path . '/cache',
1054
			'packagesdir' => $path . '/Packages',
1055
			'tasksdir' => $path . '/Sources/tasks',
1056
			'mbname' => strtr($_POST['mbname'], array('\"' => '"')),
1057
			'language' => substr($_SESSION['installer_temp_lang'], 8, -4),
1058
			'image_proxy_secret' => bin2hex(random_bytes(10)),
1059
			'image_proxy_enabled' => !empty($_POST['force_ssl']),
1060
			'auth_secret' => bin2hex(random_bytes(32)),
1061
		);
1062
1063
		// Must save!
1064
		if (!installer_updateSettingsFile($vars))
1065
		{
1066
			$incontext['error'] = $txt['settings_error'];
1067
			return false;
1068
		}
1069
1070
		// Make sure it works.
1071
		require(dirname(__FILE__) . '/Settings.php');
1072
1073
		// UTF-8 requires a setting to override the language charset.
1074
		if (!$databases[$db_type]['utf8_support']())
1075
		{
1076
			$incontext['error'] = sprintf($txt['error_utf8_support']);
1077
			return false;
1078
		}
1079
1080
		if (!empty($databases[$db_type]['utf8_version_check']) && version_compare($databases[$db_type]['utf8_version'], preg_replace('~\-.+?$~', '', $databases[$db_type]['utf8_version_check']()), '>'))
1081
		{
1082
			$incontext['error'] = sprintf($txt['error_utf8_version'], $databases[$db_type]['utf8_version']);
1083
			return false;
1084
		}
1085
1086
		// Set the character set here.
1087
		installer_updateSettingsFile(array('db_character_set' => 'utf8'));
1088
1089
		// Good, skip on.
1090
		return true;
1091
	}
1092
1093
	return false;
1094
}
1095
1096
// Step one: Do the SQL thang.
1097
function DatabasePopulation()
1098
{
1099
	global $db_character_set, $txt, $db_connection, $smcFunc, $databases, $modSettings, $db_type, $db_prefix, $incontext, $db_name, $boardurl;
1100
1101
	$incontext['sub_template'] = 'populate_database';
1102
	$incontext['page_title'] = $txt['db_populate'];
1103
	$incontext['continue'] = 1;
1104
1105
	// Already done?
1106
	if (isset($_POST['pop_done']))
1107
		return true;
1108
1109
	// Reload settings.
1110
	require(dirname(__FILE__) . '/Settings.php');
1111
	load_database();
1112
1113
	// Before running any of the queries, let's make sure another version isn't already installed.
1114
	$result = $smcFunc['db_query']('', '
1115
		SELECT variable, value
1116
		FROM {db_prefix}settings',
1117
		array(
1118
			'db_error_skip' => true,
1119
		)
1120
	);
1121
	$newSettings = array();
1122
	$modSettings = array();
1123
	if ($result !== false)
1124
	{
1125
		while ($row = $smcFunc['db_fetch_assoc']($result))
1126
			$modSettings[$row['variable']] = $row['value'];
1127
		$smcFunc['db_free_result']($result);
1128
1129
		// Do they match?  If so, this is just a refresh so charge on!
1130
		if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] != SMF_VERSION)
1131
		{
1132
			$incontext['error'] = $txt['error_versions_do_not_match'];
1133
			return false;
1134
		}
1135
	}
1136
	$modSettings['disableQueryCheck'] = true;
1137
1138
	// If doing UTF8, select it. PostgreSQL requires passing it as a string...
1139
	$smcFunc['db_query']('', '
1140
		SET NAMES {string:utf8}',
1141
		array(
1142
			'db_error_skip' => true,
1143
			'utf8' => 'utf8',
1144
		)
1145
	);
1146
1147
	// Windows likes to leave the trailing slash, which yields to C:\path\to\SMF\/attachments...
1148
	if (substr(__DIR__, -1) == '\\')
1149
		$attachdir = __DIR__ . 'attachments';
1150
	else
1151
		$attachdir = __DIR__ . '/attachments';
1152
1153
	$replaces = array(
1154
		'{$db_prefix}' => $db_prefix,
1155
		'{$attachdir}' => json_encode(array(1 => $smcFunc['db_escape_string']($attachdir))),
1156
		'{$boarddir}' => $smcFunc['db_escape_string'](dirname(__FILE__)),
1157
		'{$boardurl}' => $boardurl,
1158
		'{$enableCompressedOutput}' => isset($_POST['compress']) ? '1' : '0',
1159
		'{$databaseSession_enable}' => isset($_POST['dbsession']) ? '1' : '0',
1160
		'{$smf_version}' => SMF_VERSION,
1161
		'{$current_time}' => time(),
1162
		'{$sched_task_offset}' => 82800 + mt_rand(0, 86399),
1163
		'{$registration_method}' => isset($_POST['reg_mode']) ? $_POST['reg_mode'] : 0,
1164
	);
1165
1166
	foreach ($txt as $key => $value)
1167
	{
1168
		if (substr($key, 0, 8) == 'default_')
1169
			$replaces['{$' . $key . '}'] = $smcFunc['db_escape_string']($value);
1170
	}
1171
	$replaces['{$default_reserved_names}'] = strtr($replaces['{$default_reserved_names}'], array('\\\\n' => '\\n'));
1172
1173
	// MySQL-specific stuff - storage engine and UTF8 handling
1174
	if (substr($db_type, 0, 5) == 'mysql')
1175
	{
1176
		// Just in case the query fails for some reason...
1177
		$engines = array();
1178
1179
		// Figure out storage engines - what do we have, etc.
1180
		$get_engines = $smcFunc['db_query']('', 'SHOW ENGINES', array());
1181
1182
		while ($row = $smcFunc['db_fetch_assoc']($get_engines))
1183
		{
1184
			if ($row['Support'] == 'YES' || $row['Support'] == 'DEFAULT')
1185
				$engines[] = $row['Engine'];
1186
		}
1187
1188
		// Done with this now
1189
		$smcFunc['db_free_result']($get_engines);
1190
1191
		// InnoDB is better, so use it if possible...
1192
		$has_innodb = in_array('InnoDB', $engines);
1193
		$replaces['{$engine}'] = $has_innodb ? 'InnoDB' : 'MyISAM';
1194
		$replaces['{$memory}'] = (!$has_innodb && in_array('MEMORY', $engines)) ? 'MEMORY' : $replaces['{$engine}'];
1195
1196
		// UTF-8 is required.
1197
		$replaces['{$engine}'] .= ' DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci';
1198
		$replaces['{$memory}'] .= ' DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci';
1199
1200
		// One last thing - if we don't have InnoDB, we can't do transactions...
1201
		if (!$has_innodb)
1202
		{
1203
			$replaces['START TRANSACTION;'] = '';
1204
			$replaces['COMMIT;'] = '';
1205
		}
1206
	}
1207
	else
1208
	{
1209
		$has_innodb = false;
1210
	}
1211
1212
	// Read in the SQL.  Turn this on and that off... internationalize... etc.
1213
	$type = ($db_type == 'mysqli' ? 'mysql' : $db_type);
1214
	$sql_lines = explode("\n", strtr(implode(' ', file(dirname(__FILE__) . '/install_' . DB_SCRIPT_VERSION . '_' . $type . '.sql')), $replaces));
1215
1216
	// Execute the SQL.
1217
	$current_statement = '';
1218
	$exists = array();
1219
	$incontext['failures'] = array();
1220
	$incontext['sql_results'] = array(
1221
		'tables' => 0,
1222
		'inserts' => 0,
1223
		'table_dups' => 0,
1224
		'insert_dups' => 0,
1225
	);
1226
	foreach ($sql_lines as $count => $line)
1227
	{
1228
		// No comments allowed!
1229
		if (substr(trim($line), 0, 1) != '#')
1230
			$current_statement .= "\n" . rtrim($line);
1231
1232
		// Is this the end of the query string?
1233
		if (empty($current_statement) || (preg_match('~;[\s]*$~s', $line) == 0 && $count != count($sql_lines)))
1234
			continue;
1235
1236
		// Does this table already exist?  If so, don't insert more data into it!
1237
		if (preg_match('~^\s*INSERT INTO ([^\s\n\r]+?)~', $current_statement, $match) != 0 && in_array($match[1], $exists))
1238
		{
1239
			preg_match_all('~\)[,;]~', $current_statement, $matches);
1240
			if (!empty($matches[0]))
1241
				$incontext['sql_results']['insert_dups'] += count($matches[0]);
1242
			else
1243
				$incontext['sql_results']['insert_dups']++;
1244
1245
			$current_statement = '';
1246
			continue;
1247
		}
1248
1249
		if ($smcFunc['db_query']('', $current_statement, array('security_override' => true, 'db_error_skip' => true), $db_connection) === false)
1250
		{
1251
			// Error 1050: Table already exists!
1252
			// @todo Needs to be made better!
1253
			if ((($db_type != 'mysql' && $db_type != 'mysqli') || mysqli_errno($db_connection) == 1050) && preg_match('~^\s*CREATE TABLE ([^\s\n\r]+?)~', $current_statement, $match) == 1)
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: ($db_type != 'mysql' && ...statement, $match) == 1, Probably Intended Meaning: $db_type != 'mysql' && $...tatement, $match) == 1)
Loading history...
1254
			{
1255
				$exists[] = $match[1];
1256
				$incontext['sql_results']['table_dups']++;
1257
			}
1258
			// Don't error on duplicate indexes (or duplicate operators in PostgreSQL.)
1259
			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)))
1260
			{
1261
				// MySQLi requires a connection object. It's optional with MySQL and Postgres
1262
				$incontext['failures'][$count] = $smcFunc['db_error']($db_connection);
1263
			}
1264
		}
1265
		else
1266
		{
1267
			if (preg_match('~^\s*CREATE TABLE ([^\s\n\r]+?)~', $current_statement, $match) == 1)
1268
				$incontext['sql_results']['tables']++;
1269
			elseif (preg_match('~^\s*INSERT INTO ([^\s\n\r]+?)~', $current_statement, $match) == 1)
1270
			{
1271
				preg_match_all('~\)[,;]~', $current_statement, $matches);
1272
				if (!empty($matches[0]))
1273
					$incontext['sql_results']['inserts'] += count($matches[0]);
1274
				else
1275
					$incontext['sql_results']['inserts']++;
1276
			}
1277
		}
1278
1279
		$current_statement = '';
1280
1281
		// Wait, wait, I'm still working here!
1282
		set_time_limit(60);
1283
	}
1284
1285
	// Sort out the context for the SQL.
1286
	foreach ($incontext['sql_results'] as $key => $number)
1287
	{
1288
		if ($number == 0)
1289
			unset($incontext['sql_results'][$key]);
1290
		else
1291
			$incontext['sql_results'][$key] = sprintf($txt['db_populate_' . $key], $number);
1292
	}
1293
1294
	// Make sure UTF will be used globally.
1295
	$newSettings[] = array('global_character_set', 'UTF-8');
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
		$incontext['allow_sm_stats'] = true;
1301
1302
		// Attempt to register the site etc.
1303
		$fp = @fsockopen('www.simplemachines.org', 443, $errno, $errstr);
1304
		if (!$fp)
0 ignored issues
show
introduced by
$fp is of type false|resource, thus it always evaluated to false.
Loading history...
1305
			$fp = @fsockopen('www.simplemachines.org', 80, $errno, $errstr);
1306
		if ($fp)
0 ignored issues
show
introduced by
$fp is of type false|resource, thus it always evaluated to false.
Loading history...
1307
		{
1308
			$out = 'GET /smf/stats/register_stats.php?site=' . base64_encode($boardurl) . ' HTTP/1.1' . "\r\n";
1309
			$out .= 'Host: www.simplemachines.org' . "\r\n";
1310
			$out .= 'Connection: Close' . "\r\n\r\n";
1311
			fwrite($fp, $out);
1312
1313
			$return_data = '';
1314
			while (!feof($fp))
1315
				$return_data .= fgets($fp, 128);
1316
1317
			fclose($fp);
1318
1319
			// Get the unique site ID.
1320
			preg_match('~SITE-ID:\s(\w{10})~', $return_data, $ID);
1321
1322
			if (!empty($ID[1]))
1323
				$smcFunc['db_insert']('replace',
1324
					$db_prefix . 'settings',
1325
					array('variable' => 'string', 'value' => 'string'),
1326
					array(
1327
						array('sm_stats_key', $ID[1]),
1328
						array('enable_sm_stats', 1),
1329
					),
1330
					array('variable')
1331
				);
1332
		}
1333
	}
1334
	// Don't remove stat collection unless we unchecked the box for real, not from the loop.
1335
	elseif (empty($_POST['stats']) && empty($incontext['allow_sm_stats']))
1336
		$smcFunc['db_query']('', '
1337
			DELETE FROM {db_prefix}settings
1338
			WHERE variable = {string:enable_sm_stats}',
1339
			array(
1340
				'enable_sm_stats' => 'enable_sm_stats',
1341
				'db_error_skip' => true,
1342
			)
1343
		);
1344
1345
	// Are we enabling SSL?
1346
	if (!empty($_POST['force_ssl']))
1347
		$newSettings[] = array('force_ssl', 1);
1348
1349
	// Setting a timezone is required.
1350
	if (!isset($modSettings['default_timezone']) && function_exists('date_default_timezone_set'))
1351
	{
1352
		// Get PHP's default timezone, if set
1353
		$ini_tz = ini_get('date.timezone');
1354
		if (!empty($ini_tz))
1355
			$timezone_id = $ini_tz;
1356
		else
1357
			$timezone_id = '';
1358
1359
		// If date.timezone is unset, invalid, or just plain weird, make a best guess
1360
		if (!in_array($timezone_id, timezone_identifiers_list()))
0 ignored issues
show
Bug introduced by
timezone_identifiers_list() of type void is incompatible with the type array expected by parameter $haystack of in_array(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1360
		if (!in_array($timezone_id, /** @scrutinizer ignore-type */ timezone_identifiers_list()))
Loading history...
Bug introduced by
Are you sure the usage of timezone_identifiers_list() is correct as it seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
1361
		{
1362
			$server_offset = @mktime(0, 0, 0, 1, 1, 1970);
1363
			$timezone_id = timezone_name_from_abbr('', $server_offset, 0);
0 ignored issues
show
Bug introduced by
It seems like $server_offset can also be of type false; however, parameter $utcOffset of timezone_name_from_abbr() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1363
			$timezone_id = timezone_name_from_abbr('', /** @scrutinizer ignore-type */ $server_offset, 0);
Loading history...
1364
		}
1365
1366
		if (date_default_timezone_set($timezone_id))
1367
			$newSettings[] = array('default_timezone', $timezone_id);
1368
	}
1369
1370
	if (!empty($newSettings))
1371
	{
1372
		$smcFunc['db_insert']('replace',
1373
			'{db_prefix}settings',
1374
			array('variable' => 'string-255', 'value' => 'string-65534'),
1375
			$newSettings,
1376
			array('variable')
1377
		);
1378
	}
1379
1380
	// Populate the smiley_files table.
1381
	// Can't just dump this data in the SQL file because we need to know the id for each smiley.
1382
	$smiley_filenames = array(
1383
		':)' => 'smiley',
1384
		';)' => 'wink',
1385
		':D' => 'cheesy',
1386
		';D' => 'grin',
1387
		'>:(' => 'angry',
1388
		':(' => 'sad',
1389
		':o' => 'shocked',
1390
		'8)' => 'cool',
1391
		'???' => 'huh',
1392
		'::)' => 'rolleyes',
1393
		':P' => 'tongue',
1394
		':-[' => 'embarrassed',
1395
		':-X' => 'lipsrsealed',
1396
		':-\\' => 'undecided',
1397
		':-*' => 'kiss',
1398
		':\'(' => 'cry',
1399
		'>:D' => 'evil',
1400
		'^-^' => 'azn',
1401
		'O0' => 'afro',
1402
		':))' => 'laugh',
1403
		'C:-)' => 'police',
1404
		'O:-)' => 'angel'
1405
	);
1406
	$smiley_set_extensions = array('fugue' => '.png', 'alienine' => '.png');
1407
1408
	$smiley_inserts = array();
1409
	$request = $smcFunc['db_query']('', '
1410
		SELECT id_smiley, code
1411
		FROM {db_prefix}smileys',
1412
		array()
1413
	);
1414
	while ($row = $smcFunc['db_fetch_assoc']($request))
1415
	{
1416
		foreach ($smiley_set_extensions as $set => $ext)
1417
			$smiley_inserts[] = array($row['id_smiley'], $set, $smiley_filenames[$row['code']] . $ext);
1418
	}
1419
	$smcFunc['db_free_result']($request);
1420
1421
	$smcFunc['db_insert']('ignore',
1422
		'{db_prefix}smiley_files',
1423
		array('id_smiley' => 'int', 'smiley_set' => 'string-48', 'filename' => 'string-48'),
1424
		$smiley_inserts,
1425
		array('id_smiley', 'smiley_set')
1426
	);
1427
1428
	// Let's optimize those new tables, but not on InnoDB, ok?
1429
	if (!$has_innodb)
1430
	{
1431
		db_extend();
1432
		$tables = $smcFunc['db_list_tables']($db_name, $db_prefix . '%');
1433
		foreach ($tables as $table)
1434
		{
1435
			$smcFunc['db_optimize_table']($table) != -1 or $db_messed = true;
1436
1437
			if (!empty($db_messed))
1438
			{
1439
				$incontext['failures'][-1] = $smcFunc['db_error']();
1440
				break;
1441
			}
1442
		}
1443
	}
1444
1445
	// MySQL specific stuff
1446
	if (substr($db_type, 0, 5) != 'mysql')
1447
		return false;
1448
1449
	// Find database user privileges.
1450
	$privs = array();
1451
	$get_privs = $smcFunc['db_query']('', 'SHOW PRIVILEGES', array());
1452
	while ($row = $smcFunc['db_fetch_assoc']($get_privs))
1453
	{
1454
		if ($row['Privilege'] == 'Alter')
1455
			$privs[] = $row['Privilege'];
1456
	}
1457
	$smcFunc['db_free_result']($get_privs);
1458
1459
	// Check for the ALTER privilege.
1460
	if (!empty($databases[$db_type]['alter_support']) && !in_array('Alter', $privs))
1461
	{
1462
		$incontext['error'] = $txt['error_db_alter_priv'];
1463
		return false;
1464
	}
1465
1466
	if (!empty($exists))
1467
	{
1468
		$incontext['page_title'] = $txt['user_refresh_install'];
1469
		$incontext['was_refresh'] = true;
1470
	}
1471
1472
	return false;
1473
}
1474
1475
// Ask for the administrator login information.
1476
function AdminAccount()
1477
{
1478
	global $txt, $db_type, $smcFunc, $incontext, $db_prefix, $db_passwd, $sourcedir, $db_character_set;
1479
1480
	$incontext['sub_template'] = 'admin_account';
1481
	$incontext['page_title'] = $txt['user_settings'];
1482
	$incontext['continue'] = 1;
1483
1484
	// Skipping?
1485
	if (!empty($_POST['skip']))
1486
		return true;
1487
1488
	// Need this to check whether we need the database password.
1489
	require(dirname(__FILE__) . '/Settings.php');
1490
	load_database();
1491
1492
	require_once($sourcedir . '/Subs-Auth.php');
1493
1494
	require_once($sourcedir . '/Subs.php');
1495
1496
	// Reload settings & set some global funcs
1497
	require_once($sourcedir . '/Load.php');
1498
	reloadSettings();
1499
1500
	// We need this to properly hash the password for Admin
1501
	$smcFunc['strtolower'] = function($string)
1502
	{
1503
		global $sourcedir;
1504
		if (function_exists('mb_strtolower'))
1505
			return mb_strtolower($string, 'UTF-8');
1506
		require_once($sourcedir . '/Subs-Charset.php');
1507
		return utf8_strtolower($string);
1508
	};
1509
1510
	if (!isset($_POST['username']))
1511
		$_POST['username'] = '';
1512
	if (!isset($_POST['email']))
1513
		$_POST['email'] = '';
1514
	if (!isset($_POST['server_email']))
1515
		$_POST['server_email'] = '';
1516
1517
	$incontext['username'] = htmlspecialchars($_POST['username']);
1518
	$incontext['email'] = htmlspecialchars($_POST['email']);
1519
	$incontext['server_email'] = htmlspecialchars($_POST['server_email']);
1520
1521
	$incontext['require_db_confirm'] = empty($db_type);
1522
1523
	// Only allow skipping if we think they already have an account setup.
1524
	$request = $smcFunc['db_query']('', '
1525
		SELECT id_member
1526
		FROM {db_prefix}members
1527
		WHERE id_group = {int:admin_group} OR FIND_IN_SET({int:admin_group}, additional_groups) != 0
1528
		LIMIT 1',
1529
		array(
1530
			'db_error_skip' => true,
1531
			'admin_group' => 1,
1532
		)
1533
	);
1534
	if ($smcFunc['db_num_rows']($request) != 0)
1535
		$incontext['skip'] = 1;
1536
	$smcFunc['db_free_result']($request);
1537
1538
	// Trying to create an account?
1539
	if (isset($_POST['password1']) && !empty($_POST['contbutt']))
1540
	{
1541
		// Wrong password?
1542
		if ($incontext['require_db_confirm'] && $_POST['password3'] != $db_passwd)
1543
		{
1544
			$incontext['error'] = $txt['error_db_connect'];
1545
			return false;
1546
		}
1547
		// Not matching passwords?
1548
		if ($_POST['password1'] != $_POST['password2'])
1549
		{
1550
			$incontext['error'] = $txt['error_user_settings_again_match'];
1551
			return false;
1552
		}
1553
		// No password?
1554
		if (strlen($_POST['password1']) < 4)
1555
		{
1556
			$incontext['error'] = $txt['error_user_settings_no_password'];
1557
			return false;
1558
		}
1559
		if (!file_exists($sourcedir . '/Subs.php'))
1560
		{
1561
			$incontext['error'] = sprintf($txt['error_sourcefile_missing'], 'Subs.php');
1562
			return false;
1563
		}
1564
1565
		// Update the webmaster's email?
1566
		if (!empty($_POST['server_email']) && (empty($webmaster_email) || $webmaster_email == '[email protected]'))
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $webmaster_email seems to never exist and therefore empty should always be true.
Loading history...
1567
			installer_updateSettingsFile(array('webmaster_email' => $_POST['server_email']));
1568
1569
		// Work out whether we're going to have dodgy characters and remove them.
1570
		$invalid_characters = preg_match('~[<>&"\'=\\\]~', $_POST['username']) != 0;
1571
		$_POST['username'] = preg_replace('~[<>&"\'=\\\]~', '', $_POST['username']);
1572
1573
		$result = $smcFunc['db_query']('', '
1574
			SELECT id_member, password_salt
1575
			FROM {db_prefix}members
1576
			WHERE member_name = {string:username} OR email_address = {string:email}
1577
			LIMIT 1',
1578
			array(
1579
				'username' => $_POST['username'],
1580
				'email' => $_POST['email'],
1581
				'db_error_skip' => true,
1582
			)
1583
		);
1584
		if ($smcFunc['db_num_rows']($result) != 0)
1585
		{
1586
			list ($incontext['member_id'], $incontext['member_salt']) = $smcFunc['db_fetch_row']($result);
1587
			$smcFunc['db_free_result']($result);
1588
1589
			$incontext['account_existed'] = $txt['error_user_settings_taken'];
1590
		}
1591
		elseif ($_POST['username'] == '' || strlen($_POST['username']) > 25)
1592
		{
1593
			// Try the previous step again.
1594
			$incontext['error'] = $_POST['username'] == '' ? $txt['error_username_left_empty'] : $txt['error_username_too_long'];
1595
			return false;
1596
		}
1597
		elseif ($invalid_characters || $_POST['username'] == '_' || $_POST['username'] == '|' || strpos($_POST['username'], '[code') !== false || strpos($_POST['username'], '[/code') !== false)
1598
		{
1599
			// Try the previous step again.
1600
			$incontext['error'] = $txt['error_invalid_characters_username'];
1601
			return false;
1602
		}
1603
		elseif (empty($_POST['email']) || !filter_var($_POST['email'], FILTER_VALIDATE_EMAIL) || strlen($_POST['email']) > 255)
1604
		{
1605
			// One step back, this time fill out a proper admin email address.
1606
			$incontext['error'] = sprintf($txt['error_valid_admin_email_needed'], $_POST['username']);
1607
			return false;
1608
		}
1609
		elseif (empty($_POST['server_email']) || !filter_var($_POST['server_email'], FILTER_VALIDATE_EMAIL) || strlen($_POST['server_email']) > 255)
1610
		{
1611
			// One step back, this time fill out a proper admin email address.
1612
			$incontext['error'] = $txt['error_valid_server_email_needed'];
1613
			return false;
1614
		}
1615
		elseif ($_POST['username'] != '')
1616
		{
1617
			if (!is_callable('random_int'))
1618
				require_once('Sources/random_compat/random.php');
1619
1620
			$incontext['member_salt'] = bin2hex(random_bytes(16));
1621
1622
			// Format the username properly.
1623
			$_POST['username'] = preg_replace('~[\t\n\r\x0B\0\xA0]+~', ' ', $_POST['username']);
1624
			$ip = isset($_SERVER['REMOTE_ADDR']) ? substr($_SERVER['REMOTE_ADDR'], 0, 255) : '';
1625
1626
			$_POST['password1'] = hash_password($_POST['username'], $_POST['password1']);
1627
1628
			$incontext['member_id'] = $smcFunc['db_insert']('',
1629
				$db_prefix . 'members',
1630
				array(
1631
					'member_name' => 'string-25',
1632
					'real_name' => 'string-25',
1633
					'passwd' => 'string',
1634
					'email_address' => 'string',
1635
					'id_group' => 'int',
1636
					'posts' => 'int',
1637
					'date_registered' => 'int',
1638
					'password_salt' => 'string',
1639
					'lngfile' => 'string',
1640
					'personal_text' => 'string',
1641
					'avatar' => 'string',
1642
					'member_ip' => 'inet',
1643
					'member_ip2' => 'inet',
1644
					'buddy_list' => 'string',
1645
					'pm_ignore_list' => 'string',
1646
					'website_title' => 'string',
1647
					'website_url' => 'string',
1648
					'signature' => 'string',
1649
					'usertitle' => 'string',
1650
					'secret_question' => 'string',
1651
					'additional_groups' => 'string',
1652
					'ignore_boards' => 'string',
1653
				),
1654
				array(
1655
					$_POST['username'],
1656
					$_POST['username'],
1657
					$_POST['password1'],
1658
					$_POST['email'],
1659
					1,
1660
					0,
1661
					time(),
1662
					$incontext['member_salt'],
1663
					'',
1664
					'',
1665
					'',
1666
					$ip,
1667
					$ip,
1668
					'',
1669
					'',
1670
					'',
1671
					'',
1672
					'',
1673
					'',
1674
					'',
1675
					'',
1676
					'',
1677
				),
1678
				array('id_member'),
1679
				1
1680
			);
1681
		}
1682
1683
		// If we're here we're good.
1684
		return true;
1685
	}
1686
1687
	return false;
1688
}
1689
1690
// Final step, clean up and a complete message!
1691
function DeleteInstall()
1692
{
1693
	global $smcFunc, $db_character_set, $context, $txt, $incontext;
1694
	global $databases, $sourcedir, $modSettings, $user_info, $db_type, $boardurl;
1695
	global $auth_secret, $cookiename;
1696
1697
	$incontext['page_title'] = $txt['congratulations'];
1698
	$incontext['sub_template'] = 'delete_install';
1699
	$incontext['continue'] = 0;
1700
1701
	require(dirname(__FILE__) . '/Settings.php');
1702
	load_database();
1703
1704
	chdir(dirname(__FILE__));
1705
1706
	require_once($sourcedir . '/Errors.php');
1707
	require_once($sourcedir . '/Logging.php');
1708
	require_once($sourcedir . '/Subs.php');
1709
	require_once($sourcedir . '/Load.php');
1710
	require_once($sourcedir . '/Security.php');
1711
	require_once($sourcedir . '/Subs-Auth.php');
1712
1713
	// Reload settings & set some global funcs
1714
	reloadSettings();
1715
1716
	// Bring a warning over.
1717
	if (!empty($incontext['account_existed']))
1718
		$incontext['warning'] = $incontext['account_existed'];
1719
1720
	$smcFunc['db_query']('', '
1721
		SET NAMES {string:db_character_set}',
1722
		array(
1723
			'db_character_set' => $db_character_set,
1724
			'db_error_skip' => true,
1725
		)
1726
	);
1727
1728
	// As track stats is by default enabled let's add some activity.
1729
	$smcFunc['db_insert']('ignore',
1730
		'{db_prefix}log_activity',
1731
		array('date' => 'date', 'topics' => 'int', 'posts' => 'int', 'registers' => 'int'),
1732
		array(strftime('%Y-%m-%d', time()), 1, 1, (!empty($incontext['member_id']) ? 1 : 0)),
1733
		array('date')
1734
	);
1735
1736
	// We're going to want our lovely $modSettings now.
1737
	$request = $smcFunc['db_query']('', '
1738
		SELECT variable, value
1739
		FROM {db_prefix}settings',
1740
		array(
1741
			'db_error_skip' => true,
1742
		)
1743
	);
1744
	// Only proceed if we can load the data.
1745
	if ($request)
1746
	{
1747
		while ($row = $smcFunc['db_fetch_row']($request))
1748
			$modSettings[$row[0]] = $row[1];
1749
		$smcFunc['db_free_result']($request);
1750
	}
1751
1752
	// Automatically log them in ;)
1753
	if (isset($incontext['member_id']) && isset($incontext['member_salt']))
1754
		setLoginCookie(3153600 * 60, $incontext['member_id'], hash_salt($_POST['password1'], $incontext['member_salt']));
1755
1756
	$result = $smcFunc['db_query']('', '
1757
		SELECT value
1758
		FROM {db_prefix}settings
1759
		WHERE variable = {string:db_sessions}',
1760
		array(
1761
			'db_sessions' => 'databaseSession_enable',
1762
			'db_error_skip' => true,
1763
		)
1764
	);
1765
	if ($smcFunc['db_num_rows']($result) != 0)
1766
		list ($db_sessions) = $smcFunc['db_fetch_row']($result);
1767
	$smcFunc['db_free_result']($result);
1768
1769
	if (empty($db_sessions))
1770
		$_SESSION['admin_time'] = time();
1771
	else
1772
	{
1773
		$_SERVER['HTTP_USER_AGENT'] = substr($_SERVER['HTTP_USER_AGENT'], 0, 211);
1774
1775
		$smcFunc['db_insert']('replace',
1776
			'{db_prefix}sessions',
1777
			array(
1778
				'session_id' => 'string', 'last_update' => 'int', 'data' => 'string',
1779
			),
1780
			array(
1781
				session_id(), time(), 'USER_AGENT|s:' . strlen($_SERVER['HTTP_USER_AGENT']) . ':"' . $_SERVER['HTTP_USER_AGENT'] . '";admin_time|i:' . time() . ';',
1782
			),
1783
			array('session_id')
1784
		);
1785
	}
1786
1787
	updateStats('member');
1788
	updateStats('message');
1789
	updateStats('topic');
1790
1791
	// This function is needed to do the updateStats('subject') call.
1792
	$smcFunc['strtolower'] = function($string)
1793
	{
1794
		global $sourcedir;
1795
		if (function_exists('mb_strtolower'))
1796
			return mb_strtolower($string, 'UTF-8');
1797
		require_once($sourcedir . '/Subs-Charset.php');
1798
		return utf8_strtolower($string);
1799
	};
1800
1801
	$request = $smcFunc['db_query']('', '
1802
		SELECT id_msg
1803
		FROM {db_prefix}messages
1804
		WHERE id_msg = 1
1805
			AND modified_time = 0
1806
		LIMIT 1',
1807
		array(
1808
			'db_error_skip' => true,
1809
		)
1810
	);
1811
	$context['utf8'] = true;
1812
	if ($smcFunc['db_num_rows']($request) > 0)
1813
		updateStats('subject', 1, htmlspecialchars($txt['default_topic_subject']));
1814
	$smcFunc['db_free_result']($request);
1815
1816
	// Now is the perfect time to fetch the SM files.
1817
	require_once($sourcedir . '/ScheduledTasks.php');
1818
	// Sanity check that they loaded earlier!
1819
	if (isset($modSettings['recycle_board']))
1820
	{
1821
		scheduled_fetchSMfiles(); // Now go get those files!
1822
1823
		// We've just installed!
1824
		$user_info['ip'] = $_SERVER['REMOTE_ADDR'];
1825
		$user_info['id'] = isset($incontext['member_id']) ? $incontext['member_id'] : 0;
1826
		logAction('install', array('version' => SMF_FULL_VERSION), 'admin');
1827
	}
1828
1829
	// Disable the legacy BBC by default for new installs
1830
	updateSettings(array(
1831
		'disabledBBC' => implode(',', $context['legacy_bbc']),
1832
	));
1833
1834
	// Some final context for the template.
1835
	$incontext['dir_still_writable'] = is_writable(dirname(__FILE__)) && substr(__FILE__, 1, 2) != ':\\';
1836
	$incontext['probably_delete_install'] = isset($_SESSION['installer_temp_ftp']) || is_writable(dirname(__FILE__)) || is_writable(__FILE__);
1837
1838
	// Update hash's cost to an appropriate setting
1839
	updateSettings(array(
1840
		'bcrypt_hash_cost' => hash_benchmark(),
1841
	));
1842
1843
	return false;
1844
}
1845
1846
function installer_updateSettingsFile($vars, $rebuild = false)
1847
{
1848
	global $sourcedir, $context, $db_character_set, $txt;
1849
1850
	$context['utf8'] = true;
1851
1852
	if (empty($sourcedir))
1853
	{
1854
		if (file_exists(dirname(__FILE__) . '/Sources') && is_dir(dirname(__FILE__) . '/Sources'))
1855
			$sourcedir = dirname(__FILE__) . '/Sources';
1856
		else
1857
			return false;
1858
	}
1859
1860
	if (!is_writeable(dirname(__FILE__) . '/Settings.php'))
1861
	{
1862
		@chmod(dirname(__FILE__) . '/Settings.php', 0777);
1863
1864
		if (!is_writeable(dirname(__FILE__) . '/Settings.php'))
1865
			return false;
1866
	}
1867
1868
	require_once($sourcedir . '/Subs.php');
1869
	require_once($sourcedir . '/Subs-Admin.php');
1870
1871
	return updateSettingsFile($vars, false, $rebuild);
1872
}
1873
1874
// Create an .htaccess file to prevent mod_security. SMF has filtering built-in.
1875
function fixModSecurity()
1876
{
1877
	$htaccess_addition = '
1878
<IfModule mod_security.c>
1879
	# Turn off mod_security filtering.  SMF is a big boy, it doesn\'t need its hands held.
1880
	SecFilterEngine Off
1881
1882
	# The below probably isn\'t needed, but better safe than sorry.
1883
	SecFilterScanPOST Off
1884
</IfModule>';
1885
1886
	if (!function_exists('apache_get_modules') || !in_array('mod_security', apache_get_modules()))
1887
		return true;
1888
	elseif (file_exists(dirname(__FILE__) . '/.htaccess') && is_writable(dirname(__FILE__) . '/.htaccess'))
1889
	{
1890
		$current_htaccess = implode('', file(dirname(__FILE__) . '/.htaccess'));
1891
1892
		// Only change something if mod_security hasn't been addressed yet.
1893
		if (strpos($current_htaccess, '<IfModule mod_security.c>') === false)
1894
		{
1895
			if ($ht_handle = fopen(dirname(__FILE__) . '/.htaccess', 'a'))
1896
			{
1897
				fwrite($ht_handle, $htaccess_addition);
1898
				fclose($ht_handle);
1899
				return true;
1900
			}
1901
			else
1902
				return false;
1903
		}
1904
		else
1905
			return true;
1906
	}
1907
	elseif (file_exists(dirname(__FILE__) . '/.htaccess'))
1908
		return strpos(implode('', file(dirname(__FILE__) . '/.htaccess')), '<IfModule mod_security.c>') !== false;
1909
	elseif (is_writable(dirname(__FILE__)))
1910
	{
1911
		if ($ht_handle = fopen(dirname(__FILE__) . '/.htaccess', 'w'))
1912
		{
1913
			fwrite($ht_handle, $htaccess_addition);
1914
			fclose($ht_handle);
1915
			return true;
1916
		}
1917
		else
1918
			return false;
1919
	}
1920
	else
1921
		return false;
1922
}
1923
1924
function template_install_above()
1925
{
1926
	global $incontext, $txt, $installurl;
1927
1928
	echo '<!DOCTYPE html>
1929
<html', $txt['lang_rtl'] == true ? ' dir="rtl"' : '', '>
1930
<head>
1931
	<meta charset="', isset($txt['lang_character_set']) ? $txt['lang_character_set'] : 'UTF-8', '">
1932
	<meta name="robots" content="noindex">
1933
	<title>', $txt['smf_installer'], '</title>
1934
	<link rel="stylesheet" href="Themes/default/css/index.css">
1935
	<link rel="stylesheet" href="Themes/default/css/install.css">
1936
	', $txt['lang_rtl'] == true ? '<link rel="stylesheet" href="Themes/default/css/rtl.css">' : '', '
1937
1938
	<script src="Themes/default/scripts/jquery-' . JQUERY_VERSION . '.min.js"></script>
1939
	<script src="Themes/default/scripts/script.js"></script>
1940
</head>
1941
<body>
1942
	<div id="footerfix">
1943
	<div id="header">
1944
		<h1 class="forumtitle">', $txt['smf_installer'], '</h1>
1945
		<img id="smflogo" src="Themes/default/images/smflogo.svg" alt="Simple Machines Forum" title="Simple Machines Forum">
1946
	</div>
1947
	<div id="wrapper">';
1948
1949
	// Have we got a language drop down - if so do it on the first step only.
1950
	if (!empty($incontext['detected_languages']) && count($incontext['detected_languages']) > 1 && $incontext['current_step'] == 0)
1951
	{
1952
		echo '
1953
		<div id="upper_section">
1954
			<div id="inner_section">
1955
				<div id="inner_wrap">
1956
					<div class="news">
1957
						<form action="', $installurl, '" method="get">
1958
							<label for="installer_language">', $txt['installer_language'], ':</label>
1959
							<select id="installer_language" name="lang_file" onchange="location.href = \'', $installurl, '?lang_file=\' + this.options[this.selectedIndex].value;">';
1960
1961
		foreach ($incontext['detected_languages'] as $lang => $name)
1962
			echo '
1963
								<option', isset($_SESSION['installer_temp_lang']) && $_SESSION['installer_temp_lang'] == $lang ? ' selected' : '', ' value="', $lang, '">', $name, '</option>';
1964
1965
		echo '
1966
							</select>
1967
							<noscript><input type="submit" value="', $txt['installer_language_set'], '" class="button"></noscript>
1968
						</form>
1969
					</div><!-- .news -->
1970
					<hr class="clear">
1971
				</div><!-- #inner_wrap -->
1972
			</div><!-- #inner_section -->
1973
		</div><!-- #upper_section -->';
1974
	}
1975
1976
	echo '
1977
		<div id="content_section">
1978
			<div id="main_content_section">
1979
				<div id="main_steps">
1980
					<h2>', $txt['upgrade_progress'], '</h2>
1981
					<ul class="steps_list">';
1982
1983
	foreach ($incontext['steps'] as $num => $step)
1984
		echo '
1985
						<li', $num == $incontext['current_step'] ? ' class="stepcurrent"' : '', '>
1986
							', $txt['upgrade_step'], ' ', $step[0], ': ', $step[1], '
1987
						</li>';
1988
1989
	echo '
1990
					</ul>
1991
				</div>
1992
				<div id="install_progress">
1993
					<div id="progress_bar" class="progress_bar progress_green">
1994
						<h3>'. $txt['upgrade_overall_progress'], '</h3>
1995
						<span id="overall_text">', $incontext['overall_percent'], '%</span>
1996
						<div id="overall_progress" class="bar" style="width: ', $incontext['overall_percent'], '%;"></div>
1997
					</div>
1998
				</div>
1999
				<div id="main_screen" class="clear">
2000
					<h2>', $incontext['page_title'], '</h2>
2001
					<div class="panel">';
2002
}
2003
2004
function template_install_below()
2005
{
2006
	global $incontext, $txt;
2007
2008
	if (!empty($incontext['continue']) || !empty($incontext['skip']))
2009
	{
2010
		echo '
2011
							<div class="floatright">';
2012
2013
		if (!empty($incontext['continue']))
2014
			echo '
2015
								<input type="submit" id="contbutt" name="contbutt" value="', $txt['upgrade_continue'], '" onclick="return submitThisOnce(this);" class="button">';
2016
		if (!empty($incontext['skip']))
2017
			echo '
2018
								<input type="submit" id="skip" name="skip" value="', $txt['upgrade_skip'], '" onclick="return submitThisOnce(this);" class="button">';
2019
		echo '
2020
							</div>';
2021
	}
2022
2023
	// Show the closing form tag and other data only if not in the last step
2024
	if (count($incontext['steps']) - 1 !== (int) $incontext['current_step'])
2025
		echo '
2026
						</form>';
2027
2028
	echo '
2029
					</div><!-- .panel -->
2030
				</div><!-- #main_screen -->
2031
			</div><!-- #main_content_section -->
2032
		</div><!-- #content_section -->
2033
	</div><!-- #wrapper -->
2034
	</div><!-- #footerfix -->
2035
	<div id="footer">
2036
		<ul>
2037
			<li class="copyright"><a href="https://www.simplemachines.org/" title="Simple Machines Forum" target="_blank" rel="noopener">' . SMF_FULL_VERSION . ' &copy; ' . SMF_SOFTWARE_YEAR . ', Simple Machines</a></li>
2038
		</ul>
2039
	</div>
2040
</body>
2041
</html>';
2042
}
2043
2044
// Welcome them to the wonderful world of SMF!
2045
function template_welcome_message()
2046
{
2047
	global $incontext, $txt;
2048
2049
	echo '
2050
	<script src="https://www.simplemachines.org/smf/current-version.js?version=' . urlencode(SMF_VERSION) . '"></script>
2051
	<form action="', $incontext['form_url'], '" method="post">
2052
		<p>', sprintf($txt['install_welcome_desc'], SMF_VERSION), '</p>
2053
		<div id="version_warning" class="noticebox hidden">
2054
			<h3>', $txt['error_warning_notice'], '</h3>
2055
			', sprintf($txt['error_script_outdated'], '<em id="smfVersion" style="white-space: nowrap;">??</em>', '<em id="yourVersion" style="white-space: nowrap;">' . SMF_VERSION . '</em>'), '
2056
		</div>';
2057
2058
	// Show the warnings, or not.
2059
	if (template_warning_divs())
2060
		echo '
2061
		<h3>', $txt['install_all_lovely'], '</h3>';
2062
2063
	// Say we want the continue button!
2064
	if (empty($incontext['error']))
2065
		$incontext['continue'] = 1;
2066
2067
	// For the latest version stuff.
2068
	echo '
2069
		<script>
2070
			// Latest version?
2071
			function smfCurrentVersion()
2072
			{
2073
				var smfVer, yourVer;
2074
2075
				if (!(\'smfVersion\' in window))
2076
					return;
2077
2078
				window.smfVersion = window.smfVersion.replace(/SMF\s?/g, \'\');
2079
2080
				smfVer = document.getElementById("smfVersion");
2081
				yourVer = document.getElementById("yourVersion");
2082
2083
				setInnerHTML(smfVer, window.smfVersion);
2084
2085
				var currentVersion = getInnerHTML(yourVer);
2086
				if (currentVersion < window.smfVersion)
2087
					document.getElementById(\'version_warning\').classList.remove(\'hidden\');
2088
			}
2089
			addLoadEvent(smfCurrentVersion);
2090
		</script>';
2091
}
2092
2093
// A shortcut for any warning stuff.
2094
function template_warning_divs()
2095
{
2096
	global $txt, $incontext;
2097
2098
	// Errors are very serious..
2099
	if (!empty($incontext['error']))
2100
		echo '
2101
		<div class="errorbox">
2102
			<h3>', $txt['upgrade_critical_error'], '</h3>
2103
			', $incontext['error'], '
2104
		</div>';
2105
	// A warning message?
2106
	elseif (!empty($incontext['warning']))
2107
		echo '
2108
		<div class="errorbox">
2109
			<h3>', $txt['upgrade_warning'], '</h3>
2110
			', $incontext['warning'], '
2111
		</div>';
2112
2113
	return empty($incontext['error']) && empty($incontext['warning']);
2114
}
2115
2116
function template_chmod_files()
2117
{
2118
	global $txt, $incontext;
2119
2120
	echo '
2121
		<p>', $txt['ftp_setup_why_info'], '</p>
2122
		<ul class="error_content">
2123
			<li>', implode('</li>
2124
			<li>', $incontext['failed_files']), '</li>
2125
		</ul>';
2126
2127
	if (isset($incontext['systemos'], $incontext['detected_path']) && $incontext['systemos'] == 'linux')
2128
		echo '
2129
		<hr>
2130
		<p>', $txt['chmod_linux_info'], '</p>
2131
		<samp># chmod a+w ', implode(' ' . $incontext['detected_path'] . '/', $incontext['failed_files']), '</samp>';
2132
2133
	// This is serious!
2134
	if (!template_warning_divs())
2135
		return;
2136
2137
	echo '
2138
		<hr>
2139
		<p>', $txt['ftp_setup_info'], '</p>';
2140
2141
	if (!empty($incontext['ftp_errors']))
2142
		echo '
2143
		<div class="error_message">
2144
			', $txt['error_ftp_no_connect'], '<br><br>
2145
			<code>', implode('<br>', $incontext['ftp_errors']), '</code>
2146
		</div>';
2147
2148
	echo '
2149
		<form action="', $incontext['form_url'], '" method="post">
2150
			<dl class="settings">
2151
				<dt>
2152
					<label for="ftp_server">', $txt['ftp_server'], ':</label>
2153
				</dt>
2154
				<dd>
2155
					<div class="floatright">
2156
						<label for="ftp_port" class="textbox"><strong>', $txt['ftp_port'], ':&nbsp;</strong></label>
2157
						<input type="text" size="3" name="ftp_port" id="ftp_port" value="', $incontext['ftp']['port'], '">
2158
					</div>
2159
					<input type="text" size="30" name="ftp_server" id="ftp_server" value="', $incontext['ftp']['server'], '">
2160
					<div class="smalltext block">', $txt['ftp_server_info'], '</div>
2161
				</dd>
2162
				<dt>
2163
					<label for="ftp_username">', $txt['ftp_username'], ':</label>
2164
				</dt>
2165
				<dd>
2166
					<input type="text" size="30" name="ftp_username" id="ftp_username" value="', $incontext['ftp']['username'], '">
2167
					<div class="smalltext block">', $txt['ftp_username_info'], '</div>
2168
				</dd>
2169
				<dt>
2170
					<label for="ftp_password">', $txt['ftp_password'], ':</label>
2171
				</dt>
2172
				<dd>
2173
					<input type="password" size="30" name="ftp_password" id="ftp_password">
2174
					<div class="smalltext block">', $txt['ftp_password_info'], '</div>
2175
				</dd>
2176
				<dt>
2177
					<label for="ftp_path">', $txt['ftp_path'], ':</label>
2178
				</dt>
2179
				<dd>
2180
					<input type="text" size="30" name="ftp_path" id="ftp_path" value="', $incontext['ftp']['path'], '">
2181
					<div class="smalltext block">', $incontext['ftp']['path_msg'], '</div>
2182
				</dd>
2183
			</dl>
2184
			<div class="righttext buttons">
2185
				<input type="submit" value="', $txt['ftp_connect'], '" onclick="return submitThisOnce(this);" class="button">
2186
			</div>
2187
		</form>
2188
		<a href="', $incontext['form_url'], '">', $txt['error_message_click'], '</a> ', $txt['ftp_setup_again'];
2189
}
2190
2191
// Get the database settings prepared.
2192
function template_database_settings()
2193
{
2194
	global $incontext, $txt;
2195
2196
	echo '
2197
	<form action="', $incontext['form_url'], '" method="post">
2198
		<p>', $txt['db_settings_info'], '</p>';
2199
2200
	template_warning_divs();
2201
2202
	echo '
2203
		<dl class="settings">';
2204
2205
	// More than one database type?
2206
	if (count($incontext['supported_databases']) > 1)
2207
	{
2208
		echo '
2209
			<dt>
2210
				<label for="db_type_input">', $txt['db_settings_type'], ':</label>
2211
			</dt>
2212
			<dd>
2213
				<select name="db_type" id="db_type_input" onchange="toggleDBInput();">';
2214
2215
		foreach ($incontext['supported_databases'] as $key => $db)
2216
			echo '
2217
					<option value="', $key, '"', isset($_POST['db_type']) && $_POST['db_type'] == $key ? ' selected' : '', '>', $db['name'], '</option>';
2218
2219
		echo '
2220
				</select>
2221
				<div class="smalltext">', $txt['db_settings_type_info'], '</div>
2222
			</dd>';
2223
	}
2224
	else
2225
	{
2226
		echo '
2227
			<dd>
2228
				<input type="hidden" name="db_type" value="', $incontext['db']['type'], '">
2229
			</dd>';
2230
	}
2231
2232
	echo '
2233
			<dt>
2234
				<label for="db_server_input">', $txt['db_settings_server'], ':</label>
2235
			</dt>
2236
			<dd>
2237
				<input type="text" name="db_server" id="db_server_input" value="', $incontext['db']['server'], '" size="30">
2238
				<div class="smalltext">', $txt['db_settings_server_info'], '</div>
2239
			</dd>
2240
			<dt>
2241
				<label for="db_port_input">', $txt['db_settings_port'], ':</label>
2242
			</dt>
2243
			<dd>
2244
				<input type="text" name="db_port" id="db_port_input" value="', $incontext['db']['port'], '">
2245
				<div class="smalltext">', $txt['db_settings_port_info'], '</div>
2246
			</dd>
2247
			<dt>
2248
				<label for="db_user_input">', $txt['db_settings_username'], ':</label>
2249
			</dt>
2250
			<dd>
2251
				<input type="text" name="db_user" id="db_user_input" value="', $incontext['db']['user'], '" size="30">
2252
				<div class="smalltext">', $txt['db_settings_username_info'], '</div>
2253
			</dd>
2254
			<dt>
2255
				<label for="db_passwd_input">', $txt['db_settings_password'], ':</label>
2256
			</dt>
2257
			<dd>
2258
				<input type="password" name="db_passwd" id="db_passwd_input" value="', $incontext['db']['pass'], '" size="30">
2259
				<div class="smalltext">', $txt['db_settings_password_info'], '</div>
2260
			</dd>
2261
			<dt>
2262
				<label for="db_name_input">', $txt['db_settings_database'], ':</label>
2263
			</dt>
2264
			<dd>
2265
				<input type="text" name="db_name" id="db_name_input" value="', empty($incontext['db']['name']) ? 'smf' : $incontext['db']['name'], '" size="30">
2266
				<div class="smalltext">
2267
					', $txt['db_settings_database_info'], '
2268
					<span id="db_name_info_warning">', $txt['db_settings_database_info_note'], '</span>
2269
				</div>
2270
			</dd>
2271
			<dt>
2272
				<label for="db_prefix_input">', $txt['db_settings_prefix'], ':</label>
2273
			</dt>
2274
			<dd>
2275
				<input type="text" name="db_prefix" id="db_prefix_input" value="', $incontext['db']['prefix'], '" size="30">
2276
				<div class="smalltext">', $txt['db_settings_prefix_info'], '</div>
2277
			</dd>
2278
		</dl>';
2279
2280
	// Toggles a warning related to db names in PostgreSQL
2281
	echo '
2282
		<script>
2283
			function toggleDBInput()
2284
			{
2285
				if (document.getElementById(\'db_type_input\').value == \'postgresql\')
2286
					document.getElementById(\'db_name_info_warning\').classList.add(\'hidden\');
2287
				else
2288
					document.getElementById(\'db_name_info_warning\').classList.remove(\'hidden\');
2289
			}
2290
			toggleDBInput();
2291
		</script>';
2292
}
2293
2294
// Stick in their forum settings.
2295
function template_forum_settings()
2296
{
2297
	global $incontext, $txt;
2298
2299
	echo '
2300
	<form action="', $incontext['form_url'], '" method="post">
2301
		<h3>', $txt['install_settings_info'], '</h3>';
2302
2303
	template_warning_divs();
2304
2305
	echo '
2306
		<dl class="settings">
2307
			<dt>
2308
				<label for="mbname_input">', $txt['install_settings_name'], ':</label>
2309
			</dt>
2310
			<dd>
2311
				<input type="text" name="mbname" id="mbname_input" value="', $txt['install_settings_name_default'], '" size="65">
2312
				<div class="smalltext">', $txt['install_settings_name_info'], '</div>
2313
			</dd>
2314
			<dt>
2315
				<label for="boardurl_input">', $txt['install_settings_url'], ':</label>
2316
			</dt>
2317
			<dd>
2318
				<input type="text" name="boardurl" id="boardurl_input" value="', $incontext['detected_url'], '" size="65">
2319
				<div class="smalltext">', $txt['install_settings_url_info'], '</div>
2320
			</dd>
2321
			<dt>
2322
				<label for="reg_mode">', $txt['install_settings_reg_mode'], ':</label>
2323
			</dt>
2324
			<dd>
2325
				<select name="reg_mode" id="reg_mode">
2326
					<optgroup label="', $txt['install_settings_reg_modes'], ':">
2327
						<option value="0" selected>', $txt['install_settings_reg_immediate'], '</option>
2328
						<option value="1">', $txt['install_settings_reg_email'], '</option>
2329
						<option value="2">', $txt['install_settings_reg_admin'], '</option>
2330
						<option value="3">', $txt['install_settings_reg_disabled'], '</option>
2331
					</optgroup>
2332
				</select>
2333
				<div class="smalltext">', $txt['install_settings_reg_mode_info'], '</div>
2334
			</dd>
2335
			<dt>', $txt['install_settings_compress'], ':</dt>
2336
			<dd>
2337
				<input type="checkbox" name="compress" id="compress_check" checked>
2338
				<label for="compress_check">', $txt['install_settings_compress_title'], '</label>
2339
				<div class="smalltext">', $txt['install_settings_compress_info'], '</div>
2340
			</dd>
2341
			<dt>', $txt['install_settings_dbsession'], ':</dt>
2342
			<dd>
2343
				<input type="checkbox" name="dbsession" id="dbsession_check" checked>
2344
				<label for="dbsession_check">', $txt['install_settings_dbsession_title'], '</label>
2345
				<div class="smalltext">', $incontext['test_dbsession'] ? $txt['install_settings_dbsession_info1'] : $txt['install_settings_dbsession_info2'], '</div>
2346
			</dd>
2347
			<dt>', $txt['install_settings_stats'], ':</dt>
2348
			<dd>
2349
				<input type="checkbox" name="stats" id="stats_check" checked="checked">
2350
				<label for="stats_check">', $txt['install_settings_stats_title'], '</label>
2351
				<div class="smalltext">', $txt['install_settings_stats_info'], '</div>
2352
			</dd>
2353
			<dt>', $txt['force_ssl'], ':</dt>
2354
			<dd>
2355
				<input type="checkbox" name="force_ssl" id="force_ssl"', $incontext['ssl_chkbx_checked'] ? ' checked' : '',
2356
					$incontext['ssl_chkbx_protected'] ? ' disabled' : '', '>
2357
				<label for="force_ssl">', $txt['force_ssl_label'], '</label>
2358
				<div class="smalltext"><strong>', $txt['force_ssl_info'], '</strong></div>
2359
			</dd>
2360
		</dl>';
2361
}
2362
2363
// Show results of the database population.
2364
function template_populate_database()
2365
{
2366
	global $incontext, $txt;
2367
2368
	echo '
2369
	<form action="', $incontext['form_url'], '" method="post">
2370
		<p>', !empty($incontext['was_refresh']) ? $txt['user_refresh_install_desc'] : $txt['db_populate_info'], '</p>';
2371
2372
	if (!empty($incontext['sql_results']))
2373
	{
2374
		echo '
2375
		<ul>
2376
			<li>', implode('</li><li>', $incontext['sql_results']), '</li>
2377
		</ul>';
2378
	}
2379
2380
	if (!empty($incontext['failures']))
2381
	{
2382
		echo '
2383
		<div class="red">', $txt['error_db_queries'], '</div>
2384
		<ul>';
2385
2386
		foreach ($incontext['failures'] as $line => $fail)
2387
			echo '
2388
			<li><strong>', $txt['error_db_queries_line'], $line + 1, ':</strong> ', nl2br(htmlspecialchars($fail)), '</li>';
2389
2390
		echo '
2391
		</ul>';
2392
	}
2393
2394
	echo '
2395
		<p>', $txt['db_populate_info2'], '</p>';
2396
2397
	template_warning_divs();
2398
2399
	echo '
2400
		<input type="hidden" name="pop_done" value="1">';
2401
}
2402
2403
// Create the admin account.
2404
function template_admin_account()
2405
{
2406
	global $incontext, $txt;
2407
2408
	echo '
2409
	<form action="', $incontext['form_url'], '" method="post">
2410
		<p>', $txt['user_settings_info'], '</p>';
2411
2412
	template_warning_divs();
2413
2414
	echo '
2415
		<dl class="settings">
2416
			<dt>
2417
				<label for="username">', $txt['user_settings_username'], ':</label>
2418
			</dt>
2419
			<dd>
2420
				<input type="text" name="username" id="username" value="', $incontext['username'], '" size="40">
2421
				<div class="smalltext">', $txt['user_settings_username_info'], '</div>
2422
			</dd>
2423
			<dt>
2424
				<label for="password1">', $txt['user_settings_password'], ':</label>
2425
			</dt>
2426
			<dd>
2427
				<input type="password" name="password1" id="password1" size="40">
2428
				<div class="smalltext">', $txt['user_settings_password_info'], '</div>
2429
			</dd>
2430
			<dt>
2431
				<label for="password2">', $txt['user_settings_again'], ':</label>
2432
			</dt>
2433
			<dd>
2434
				<input type="password" name="password2" id="password2" size="40">
2435
				<div class="smalltext">', $txt['user_settings_again_info'], '</div>
2436
			</dd>
2437
			<dt>
2438
				<label for="email">', $txt['user_settings_admin_email'], ':</label>
2439
			</dt>
2440
			<dd>
2441
				<input type="email" name="email" id="email" value="', $incontext['email'], '" size="40">
2442
				<div class="smalltext">', $txt['user_settings_admin_email_info'], '</div>
2443
			</dd>
2444
			<dt>
2445
				<label for="server_email">', $txt['user_settings_server_email'], ':</label>
2446
			</dt>
2447
			<dd>
2448
				<input type="text" name="server_email" id="server_email" value="', $incontext['server_email'], '" size="40">
2449
				<div class="smalltext">', $txt['user_settings_server_email_info'], '</div>
2450
			</dd>
2451
		</dl>';
2452
2453
	if ($incontext['require_db_confirm'])
2454
		echo '
2455
		<h2>', $txt['user_settings_database'], '</h2>
2456
		<p>', $txt['user_settings_database_info'], '</p>
2457
2458
		<div class="lefttext">
2459
			<input type="password" name="password3" size="30">
2460
		</div>';
2461
}
2462
2463
// Tell them it's done, and to delete.
2464
function template_delete_install()
2465
{
2466
	global $incontext, $installurl, $txt, $boardurl;
2467
2468
	echo '
2469
		<p>', $txt['congratulations_help'], '</p>';
2470
2471
	template_warning_divs();
2472
2473
	// Install directory still writable?
2474
	if ($incontext['dir_still_writable'])
2475
		echo '
2476
		<p><em>', $txt['still_writable'], '</em></p>';
2477
2478
	// Don't show the box if it's like 99% sure it won't work :P.
2479
	if ($incontext['probably_delete_install'])
2480
		echo '
2481
		<label>
2482
			<input type="checkbox" id="delete_self" onclick="doTheDelete();">
2483
			<strong>', $txt['delete_installer'], !isset($_SESSION['installer_temp_ftp']) ? ' ' . $txt['delete_installer_maybe'] : '', '</strong>
2484
		</label>
2485
		<script>
2486
			function doTheDelete()
2487
			{
2488
				var theCheck = document.getElementById ? document.getElementById("delete_self") : document.all.delete_self;
2489
				var tempImage = new Image();
2490
2491
				tempImage.src = "', $installurl, '?delete=1&ts_" + (new Date().getTime());
2492
				tempImage.width = 0;
2493
				theCheck.disabled = true;
2494
			}
2495
		</script>';
2496
2497
	echo '
2498
		<p>', sprintf($txt['go_to_your_forum'], $boardurl . '/index.php'), '</p>
2499
		<br>
2500
		', $txt['good_luck'];
2501
}
2502
2503
?>