Completed
Pull Request — release-2.1 (#5439)
by John
04:33
created

initialize_inputs()   F

Complexity

Conditions 31
Paths 567

Size

Total Lines 119
Code Lines 60

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 31
eloc 60
nc 567
nop 0
dl 0
loc 119
rs 0.6012
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * Simple Machines Forum (SMF)
5
 *
6
 * @package SMF
7
 * @author Simple Machines http://www.simplemachines.org
8
 * @copyright 2019 Simple Machines and individual contributors
9
 * @license http://www.simplemachines.org/about/smf/license.php BSD
10
 *
11
 * @version 2.1 RC1
12
 */
13
14
define('SMF_VERSION', '2.1 RC1');
15
define('SMF_FULL_VERSION', 'SMF ' . SMF_VERSION);
16
define('SMF_SOFTWARE_YEAR', '2019');
17
define('DB_SCRIPT_VERSION', '2-1');
18
define('SMF_INSTALLING', 1);
19
20
$GLOBALS['required_php_version'] = '5.4.0';
21
22
// Don't have PHP support, do you?
23
// ><html dir="ltr"><head><title>Error!</title></head><body>Sorry, this installer requires PHP!<div style="display: none;">
24
25
// Let's pull in useful classes
26
if (!defined('SMF'))
27
	define('SMF', 1);
28
29
require_once('Sources/Class-Package.php');
30
31
// Database info.
32
$databases = array(
33
	'mysql' => array(
34
		'name' => 'MySQL',
35
		'version' => '5.0.22',
36
		'version_check' => 'return min(mysqli_get_server_info($db_connection), mysqli_get_client_info());',
37
		'supported' => function_exists('mysqli_connect'),
38
		'default_user' => 'mysql.default_user',
39
		'default_password' => 'mysql.default_password',
40
		'default_host' => 'mysql.default_host',
41
		'default_port' => 'mysql.default_port',
42
		'utf8_support' => function()
43
		{
44
			return true;
45
		},
46
		'utf8_version' => '5.0.22',
47
		'utf8_version_check' => 'return mysqli_get_server_info($db_connection);',
48
		'utf8_default' => true,
49
		'utf8_required' => true,
50
		'alter_support' => true,
51
		'validate_prefix' => function(&$value)
52
		{
53
			$value = preg_replace('~[^A-Za-z0-9_\$]~', '', $value);
54
			return true;
55
		},
56
	),
57
	'postgresql' => array(
58
		'name' => 'PostgreSQL',
59
		'version' => '9.4',
60
		'function_check' => 'pg_connect',
61
		'version_check' => '$request = pg_query(\'SELECT version()\'); list ($version) = pg_fetch_row($request); list($pgl, $version) = explode(" ", $version); return $version;',
62
		'supported' => function_exists('pg_connect'),
63
		'always_has_db' => true,
64
		'utf8_default' => true,
65
		'utf8_required' => true,
66
		'utf8_support' => function()
67
		{
68
			$request = pg_query('SHOW SERVER_ENCODING');
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

68
			$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...
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

68
			$request = pg_query(/** @scrutinizer ignore-type */ 'SHOW SERVER_ENCODING');
Loading history...
69
70
			list ($charcode) = pg_fetch_row($request);
71
72
			if ($charcode == 'UTF8')
73
				return true;
74
			else
75
				return false;
76
		},
77
		'utf8_version' => '8.0',
78
		'utf8_version_check' => '$request = pg_query(\'SELECT version()\'); list ($version) = pg_fetch_row($request); list($pgl, $version) = explode(" ", $version); return $version;',
79
		'validate_prefix' => function(&$value)
80
		{
81
			global $txt;
82
83
			$value = preg_replace('~[^A-Za-z0-9_\$]~', '', $value);
84
85
			// Is it reserved?
86
			if ($value == 'pg_')
87
				return $txt['error_db_prefix_reserved'];
88
89
			// Is the prefix numeric?
90
			if (preg_match('~^\d~', $value))
91
				return $txt['error_db_prefix_numeric'];
92
93
			return true;
94
		},
95
	),
96
);
97
98
global $txt;
99
100
// Initialize everything and load the language files.
101
initialize_inputs();
102
load_lang_file();
103
104
// This is what we are.
105
$installurl = $_SERVER['PHP_SELF'];
106
107
// All the steps in detail.
108
// Number,Name,Function,Progress Weight.
109
$incontext['steps'] = array(
110
	0 => array(1, $txt['install_step_welcome'], 'Welcome', 0),
111
	1 => array(2, $txt['install_step_writable'], 'CheckFilesWritable', 10),
112
	2 => array(3, $txt['install_step_databaseset'], 'DatabaseSettings', 15),
113
	3 => array(4, $txt['install_step_forum'], 'ForumSettings', 40),
114
	4 => array(5, $txt['install_step_databasechange'], 'DatabasePopulation', 15),
115
	5 => array(6, $txt['install_step_admin'], 'AdminAccount', 20),
116
	6 => array(7, $txt['install_step_delete'], 'DeleteInstall', 0),
117
);
118
119
// Default title...
120
$incontext['page_title'] = $txt['smf_installer'];
121
122
// What step are we on?
123
$incontext['current_step'] = isset($_GET['step']) ? (int) $_GET['step'] : 0;
124
125
// Loop through all the steps doing each one as required.
126
$incontext['overall_percent'] = 0;
127
128
foreach ($incontext['steps'] as $num => $step)
129
{
130
	if ($num >= $incontext['current_step'])
131
	{
132
		// The current weight of this step in terms of overall progress.
133
		$incontext['step_weight'] = $step[3];
134
		// Make sure we reset the skip button.
135
		$incontext['skip'] = false;
136
137
		// Call the step and if it returns false that means pause!
138
		if (function_exists($step[2]) && $step[2]() === false)
139
			break;
140
		elseif (function_exists($step[2]))
141
			$incontext['current_step']++;
142
143
		// No warnings pass on.
144
		$incontext['warning'] = '';
145
	}
146
	$incontext['overall_percent'] += $step[3];
147
}
148
149
// Actually do the template stuff.
150
installExit();
151
152
function initialize_inputs()
153
{
154
	global $databases;
155
156
	// Just so people using older versions of PHP aren't left in the cold.
157
	if (!isset($_SERVER['PHP_SELF']))
158
		$_SERVER['PHP_SELF'] = isset($GLOBALS['HTTP_SERVER_VARS']['PHP_SELF']) ? $GLOBALS['HTTP_SERVER_VARS']['PHP_SELF'] : 'install.php';
159
160
	// Enable error reporting for fatal errors.
161
	error_reporting(E_ERROR | E_PARSE);
162
163
	// Fun.  Low PHP version...
164
	if (!isset($_GET))
165
	{
166
		$GLOBALS['_GET']['step'] = 0;
167
		return;
168
	}
169
170
	if (!isset($_GET['obgz']))
171
	{
172
		ob_start();
173
174
		if (ini_get('session.save_handler') == 'user')
175
			@ini_set('session.save_handler', 'files');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for ini_set(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

175
			/** @scrutinizer ignore-unhandled */ @ini_set('session.save_handler', 'files');

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

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

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

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

177
			/** @scrutinizer ignore-unhandled */ @session_start();

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

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

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

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

228
			/** @scrutinizer ignore-unhandled */ @unlink(__FILE__);

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
229
230
			foreach ($databases as $key => $dummy)
231
			{
232
				$type = ($key == 'mysqli') ? 'mysql' : $key;
233
				@unlink(dirname(__FILE__) . '/install_' . DB_SCRIPT_VERSION . '_' . $type . '.sql');
234
			}
235
		}
236
237
		// Now just redirect to a blank.png...
238
		$secure = false;
239
240
		if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on')
241
			$secure = true;
242
		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...
243
			$secure = true;
244
245
		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');
246
		exit;
247
	}
248
249
	// PHP 5 might cry if we don't do this now.
250
	if (function_exists('date_default_timezone_set'))
251
	{
252
		// Get PHP's default timezone, if set
253
		$ini_tz = ini_get('date.timezone');
254
		if (!empty($ini_tz))
255
			$timezone_id = $ini_tz;
256
		else
257
			$timezone_id = '';
258
259
		// If date.timezone is unset, invalid, or just plain weird, make a best guess
260
		if (!in_array($timezone_id, timezone_identifiers_list()))
0 ignored issues
show
Bug introduced by
It seems like timezone_identifiers_list() can also be of type false; however, parameter $haystack of in_array() does only seem to accept array, 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

260
		if (!in_array($timezone_id, /** @scrutinizer ignore-type */ timezone_identifiers_list()))
Loading history...
261
		{
262
			$server_offset = @mktime(0, 0, 0, 1, 1, 1970);
263
			$timezone_id = timezone_name_from_abbr('', $server_offset, 0);
264
		}
265
266
		date_default_timezone_set($timezone_id);
267
	}
268
269
	// Force an integer step, defaulting to 0.
270
	$_GET['step'] = (int) @$_GET['step'];
271
}
272
273
// Load the list of language files, and the current language file.
274
function load_lang_file()
275
{
276
	global $incontext, $user_info, $txt;
277
278
	$incontext['detected_languages'] = array();
279
280
	// Make sure the languages directory actually exists.
281
	if (file_exists(dirname(__FILE__) . '/Themes/default/languages'))
282
	{
283
		// Find all the "Install" language files in the directory.
284
		$dir = dir(dirname(__FILE__) . '/Themes/default/languages');
285
		while ($entry = $dir->read())
286
		{
287
			if (substr($entry, 0, 8) == 'Install.' && substr($entry, -4) == '.php')
288
				$incontext['detected_languages'][$entry] = ucfirst(substr($entry, 8, strlen($entry) - 12));
289
		}
290
		$dir->close();
291
	}
292
293
	// Didn't find any, show an error message!
294
	if (empty($incontext['detected_languages']))
295
	{
296
		// Let's not cache this message, eh?
297
		header('expires: Mon, 26 Jul 1997 05:00:00 GMT');
298
		header('last-modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
299
		header('cache-control: no-cache');
300
301
		echo '<!DOCTYPE html>
302
<html>
303
	<head>
304
		<title>SMF Installer: Error!</title>
305
		<style>
306
			body {
307
				font-family: sans-serif;
308
				max-width: 700px; }
309
310
			h1 {
311
				font-size: 14pt; }
312
313
			.directory {
314
				margin: 0.3em;
315
				font-family: monospace;
316
				font-weight: bold; }
317
		</style>
318
	</head>
319
	<body>
320
		<h1>A critical error has occurred.</h1>
321
322
		<p>This installer was unable to find the installer\'s language file or files. They should be found under:</p>
323
324
		<div class="directory">', dirname($_SERVER['PHP_SELF']) != '/' ? dirname($_SERVER['PHP_SELF']) : '', '/Themes/default/languages</div>
325
326
		<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>
327
		<p>If that doesn\'t help, please make sure this install.php file is in the same place as the Themes folder.</p>
328
		<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>
329
	</div></body>
330
</html>';
331
		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...
332
	}
333
334
	// Override the language file?
335
	if (isset($_GET['lang_file']))
336
		$_SESSION['installer_temp_lang'] = $_GET['lang_file'];
337
	elseif (isset($GLOBALS['HTTP_GET_VARS']['lang_file']))
338
		$_SESSION['installer_temp_lang'] = $GLOBALS['HTTP_GET_VARS']['lang_file'];
339
340
	// Make sure it exists, if it doesn't reset it.
341
	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']))
342
	{
343
		// Use the first one...
344
		list ($_SESSION['installer_temp_lang']) = array_keys($incontext['detected_languages']);
345
346
		// If we have english and some other language, use the other language.  We Americans hate english :P.
347
		if ($_SESSION['installer_temp_lang'] == 'Install.english.php' && count($incontext['detected_languages']) > 1)
348
			list (, $_SESSION['installer_temp_lang']) = array_keys($incontext['detected_languages']);
349
	}
350
351
	// And now include the actual language file itself.
352
	require_once(dirname(__FILE__) . '/Themes/default/languages/' . $_SESSION['installer_temp_lang']);
353
354
	// Which language did we load? Assume that he likes his language.
355
	preg_match('~^Install\.(.+[^-utf8])\.php$~', $_SESSION['installer_temp_lang'], $matches);
356
	$user_info['language'] = $matches[1];
357
}
358
359
// This handy function loads some settings and the like.
360
function load_database()
361
{
362
	global $db_prefix, $db_connection, $sourcedir, $smcFunc, $modSettings, $db_port;
363
	global $db_server, $db_passwd, $db_type, $db_name, $db_user, $db_persist, $db_mb4;
364
365
	if (empty($sourcedir))
366
		$sourcedir = dirname(__FILE__) . '/Sources';
367
368
	// Need this to check whether we need the database password.
369
	require(dirname(__FILE__) . '/Settings.php');
370
	if (!defined('SMF'))
371
		define('SMF', 1);
372
	if (empty($smcFunc))
373
		$smcFunc = array();
374
375
	$modSettings['disableQueryCheck'] = true;
376
377
	// Connect the database.
378
	if (!$db_connection)
379
	{
380
		require_once($sourcedir . '/Subs-Db-' . $db_type . '.php');
381
382
		$options = array('persist' => $db_persist);
383
384
		if (!empty($db_port))
385
			$options['port'] = $db_port;
386
387
		if (!empty($db_mb4))
388
			$options['db_mb4'] = $db_mb4;
389
390
		if (!$db_connection)
391
			$db_connection = smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, $db_prefix, $options);
392
	}
393
}
394
395
// This is called upon exiting the installer, for template etc.
396
function installExit($fallThrough = false)
397
{
398
	global $incontext, $installurl, $txt;
399
400
	// Send character set.
401
	header('content-type: text/html; charset=' . (isset($txt['lang_character_set']) ? $txt['lang_character_set'] : 'UTF-8'));
402
403
	// We usually dump our templates out.
404
	if (!$fallThrough)
405
	{
406
		// The top install bit.
407
		template_install_above();
408
409
		// Call the template.
410
		if (isset($incontext['sub_template']))
411
		{
412
			$incontext['form_url'] = $installurl . '?step=' . $incontext['current_step'];
413
414
			call_user_func('template_' . $incontext['sub_template']);
415
		}
416
		// @todo REMOVE THIS!!
417
		else
418
		{
419
			if (function_exists('doStep' . $_GET['step']))
420
				call_user_func('doStep' . $_GET['step']);
421
		}
422
		// Show the footer.
423
		template_install_below();
424
	}
425
426
	// Bang - gone!
427
	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...
428
}
429
430
function Welcome()
431
{
432
	global $incontext, $txt, $databases, $installurl;
433
434
	$incontext['page_title'] = $txt['install_welcome'];
435
	$incontext['sub_template'] = 'welcome_message';
436
437
	// Done the submission?
438
	if (isset($_POST['contbutt']))
439
		return true;
440
441
	// See if we think they have already installed it?
442
	if (is_readable(dirname(__FILE__) . '/Settings.php'))
443
	{
444
		$probably_installed = 0;
445
		foreach (file(dirname(__FILE__) . '/Settings.php') as $line)
446
		{
447
			if (preg_match('~^\$db_passwd\s=\s\'([^\']+)\';$~', $line))
448
				$probably_installed++;
449
			if (preg_match('~^\$boardurl\s=\s\'([^\']+)\';~', $line) && !preg_match('~^\$boardurl\s=\s\'http://127\.0\.0\.1/smf\';~', $line))
450
				$probably_installed++;
451
		}
452
453
		if ($probably_installed == 2)
454
			$incontext['warning'] = $txt['error_already_installed'];
455
	}
456
457
	// Is some database support even compiled in?
458
	$incontext['supported_databases'] = array();
459
	foreach ($databases as $key => $db)
460
	{
461
		if ($db['supported'])
462
		{
463
			$type = ($key == 'mysqli') ? 'mysql' : $key;
464
			if (!file_exists(dirname(__FILE__) . '/install_' . DB_SCRIPT_VERSION . '_' . $type . '.sql'))
465
			{
466
				$databases[$key]['supported'] = false;
467
				$notFoundSQLFile = true;
468
				$txt['error_db_script_missing'] = sprintf($txt['error_db_script_missing'], 'install_' . DB_SCRIPT_VERSION . '_' . $type . '.sql');
469
			}
470
			else
471
				$incontext['supported_databases'][] = $db;
472
		}
473
	}
474
475
	// Check the PHP version.
476
	if ((!function_exists('version_compare') || version_compare($GLOBALS['required_php_version'], PHP_VERSION, '>=')))
477
		$error = 'error_php_too_low';
478
	// Make sure we have a supported database
479
	elseif (empty($incontext['supported_databases']))
480
		$error = empty($notFoundSQLFile) ? 'error_db_missing' : 'error_db_script_missing';
481
	// How about session support?  Some crazy sysadmin remove it?
482
	elseif (!function_exists('session_start'))
483
		$error = 'error_session_missing';
484
	// Make sure they uploaded all the files.
485
	elseif (!file_exists(dirname(__FILE__) . '/index.php'))
486
		$error = 'error_missing_files';
487
	// Very simple check on the session.save_path for Windows.
488
	// @todo Move this down later if they don't use database-driven sessions?
489
	elseif (@ini_get('session.save_path') == '/tmp' && substr(__FILE__, 1, 2) == ':\\')
490
		$error = 'error_session_save_path';
491
492
	// Since each of the three messages would look the same, anyway...
493
	if (isset($error))
494
		$incontext['error'] = $txt[$error];
495
496
	// Mod_security blocks everything that smells funny. Let SMF handle security.
497
	if (!fixModSecurity() && !isset($_GET['overmodsecurity']))
498
		$incontext['error'] = $txt['error_mod_security'] . '<br><br><a href="' . $installurl . '?overmodsecurity=true">' . $txt['error_message_click'] . '</a> ' . $txt['error_message_bad_try_again'];
499
500
	// Confirm mbstring is loaded...
501
	if (!extension_loaded('mbstring'))
502
		$incontext['error'] = $txt['install_no_mbstring'];
503
504
	// Check for https stream support.
505
	$supported_streams = stream_get_wrappers();
506
	if (!in_array('https', $supported_streams))
507
		$incontext['warning'] = $txt['install_no_https'];
508
509
	return false;
510
}
511
512
function CheckFilesWritable()
513
{
514
	global $txt, $incontext;
515
516
	$incontext['page_title'] = $txt['ftp_checking_writable'];
517
	$incontext['sub_template'] = 'chmod_files';
518
519
	$writable_files = array(
520
		'attachments',
521
		'avatars',
522
		'custom_avatar',
523
		'cache',
524
		'Packages',
525
		'Smileys',
526
		'Themes',
527
		'agreement.txt',
528
		'Settings.php',
529
		'Settings_bak.php',
530
	);
531
532
	foreach ($incontext['detected_languages'] as $lang => $temp)
533
		$extra_files[] = 'Themes/default/languages/' . $lang;
534
535
	// With mod_security installed, we could attempt to fix it with .htaccess.
536
	if (function_exists('apache_get_modules') && in_array('mod_security', apache_get_modules()))
537
		$writable_files[] = file_exists(dirname(__FILE__) . '/.htaccess') ? '.htaccess' : '.';
538
539
	$failed_files = array();
540
541
	// On linux, it's easy - just use is_writable!
542
	if (substr(__FILE__, 1, 2) != ':\\')
543
	{
544
		$incontext['systemos'] = 'linux';
545
546
		foreach ($writable_files as $file)
547
		{
548
			// Some files won't exist, try to address up front
549
			if (!file_exists(dirname(__FILE__) . '/' . $file))
550
				@touch(dirname(__FILE__) . '/' . $file);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for touch(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

550
				/** @scrutinizer ignore-unhandled */ @touch(dirname(__FILE__) . '/' . $file);

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

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

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

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

554
				/** @scrutinizer ignore-unhandled */ @chmod(dirname(__FILE__) . '/' . $file, 0755);

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
555
556
				// Well, 755 hopefully worked... if not, try 777.
557
				if (!is_writable(dirname(__FILE__) . '/' . $file) && !@chmod(dirname(__FILE__) . '/' . $file, 0777))
558
					$failed_files[] = $file;
559
			}
560
		}
561
		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 532. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
562
			@chmod(dirname(__FILE__) . (empty($file) ? '' : '/' . $file), 0777);
563
	}
564
	// Windows is trickier.  Let's try opening for r+...
565
	else
566
	{
567
		$incontext['systemos'] = 'windows';
568
569
		foreach ($writable_files as $file)
570
		{
571
			// Folders can't be opened for write... but the index.php in them can ;)
572
			if (is_dir(dirname(__FILE__) . '/' . $file))
573
				$file .= '/index.php';
574
575
			// Funny enough, chmod actually does do something on windows - it removes the read only attribute.
576
			@chmod(dirname(__FILE__) . '/' . $file, 0777);
577
			$fp = @fopen(dirname(__FILE__) . '/' . $file, 'r+');
578
579
			// Hmm, okay, try just for write in that case...
580
			if (!is_resource($fp))
581
				$fp = @fopen(dirname(__FILE__) . '/' . $file, 'w');
582
583
			if (!is_resource($fp))
584
				$failed_files[] = $file;
585
586
			@fclose($fp);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for fclose(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

586
			/** @scrutinizer ignore-unhandled */ @fclose($fp);

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
Bug introduced by
It seems like $fp can also be of type false; however, parameter $handle 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

586
			@fclose(/** @scrutinizer ignore-type */ $fp);
Loading history...
587
		}
588
		foreach ($extra_files as $file)
589
			@chmod(dirname(__FILE__) . (empty($file) ? '' : '/' . $file), 0777);
590
	}
591
592
	$failure = count($failed_files) >= 1;
593
594
	if (!isset($_SERVER))
595
		return !$failure;
596
597
	// Put the list into context.
598
	$incontext['failed_files'] = $failed_files;
599
600
	// It's not going to be possible to use FTP on windows to solve the problem...
601
	if ($failure && substr(__FILE__, 1, 2) == ':\\')
602
	{
603
		$incontext['error'] = $txt['error_windows_chmod'] . '
604
					<ul class="error_content">
605
						<li>' . implode('</li>
606
						<li>', $failed_files) . '</li>
607
					</ul>';
608
609
		return false;
610
	}
611
	// We're going to have to use... FTP!
612
	elseif ($failure)
613
	{
614
		// Load any session data we might have...
615
		if (!isset($_POST['ftp_username']) && isset($_SESSION['installer_temp_ftp']))
616
		{
617
			$_POST['ftp_server'] = $_SESSION['installer_temp_ftp']['server'];
618
			$_POST['ftp_port'] = $_SESSION['installer_temp_ftp']['port'];
619
			$_POST['ftp_username'] = $_SESSION['installer_temp_ftp']['username'];
620
			$_POST['ftp_password'] = $_SESSION['installer_temp_ftp']['password'];
621
			$_POST['ftp_path'] = $_SESSION['installer_temp_ftp']['path'];
622
		}
623
624
		$incontext['ftp_errors'] = array();
625
		require_once('Sources/Class-Package.php');
626
		if (isset($_POST['ftp_username']))
627
		{
628
			$ftp = new ftp_connection($_POST['ftp_server'], $_POST['ftp_port'], $_POST['ftp_username'], $_POST['ftp_password']);
629
630
			if ($ftp->error === false)
0 ignored issues
show
introduced by
The condition $ftp->error === false is always false.
Loading history...
631
			{
632
				// Try it without /home/abc just in case they messed up.
633
				if (!$ftp->chdir($_POST['ftp_path']))
634
				{
635
					$incontext['ftp_errors'][] = $ftp->last_message;
636
					$ftp->chdir(preg_replace('~^/home[2]?/[^/]+?~', '', $_POST['ftp_path']));
637
				}
638
			}
639
		}
640
641
		if (!isset($ftp) || $ftp->error !== false)
642
		{
643
			if (!isset($ftp))
644
				$ftp = new ftp_connection(null);
645
			// Save the error so we can mess with listing...
646
			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...
647
				$incontext['ftp_errors'][] = $ftp->last_message;
648
649
			list ($username, $detect_path, $found_path) = $ftp->detect_path(dirname(__FILE__));
650
651
			if (empty($_POST['ftp_path']) && $found_path)
652
				$_POST['ftp_path'] = $detect_path;
653
654
			if (!isset($_POST['ftp_username']))
655
				$_POST['ftp_username'] = $username;
656
657
			// Set the username etc, into context.
658
			$incontext['ftp'] = array(
659
				'server' => isset($_POST['ftp_server']) ? $_POST['ftp_server'] : 'localhost',
660
				'port' => isset($_POST['ftp_port']) ? $_POST['ftp_port'] : '21',
661
				'username' => isset($_POST['ftp_username']) ? $_POST['ftp_username'] : '',
662
				'path' => isset($_POST['ftp_path']) ? $_POST['ftp_path'] : '/',
663
				'path_msg' => !empty($found_path) ? $txt['ftp_path_found_info'] : $txt['ftp_path_info'],
664
			);
665
666
			return false;
667
		}
668
		else
669
		{
670
			$_SESSION['installer_temp_ftp'] = array(
671
				'server' => $_POST['ftp_server'],
672
				'port' => $_POST['ftp_port'],
673
				'username' => $_POST['ftp_username'],
674
				'password' => $_POST['ftp_password'],
675
				'path' => $_POST['ftp_path']
676
			);
677
678
			$failed_files_updated = array();
679
680
			foreach ($failed_files as $file)
681
			{
682
				if (!is_writable(dirname(__FILE__) . '/' . $file))
683
					$ftp->chmod($file, 0755);
684
				if (!is_writable(dirname(__FILE__) . '/' . $file))
685
					$ftp->chmod($file, 0777);
686
				if (!is_writable(dirname(__FILE__) . '/' . $file))
687
				{
688
					$failed_files_updated[] = $file;
689
					$incontext['ftp_errors'][] = rtrim($ftp->last_message) . ' -> ' . $file . "\n";
690
				}
691
			}
692
693
			$ftp->close();
694
695
			// Are there any errors left?
696
			if (count($failed_files_updated) >= 1)
697
			{
698
				// Guess there are...
699
				$incontext['failed_files'] = $failed_files_updated;
700
701
				// Set the username etc, into context.
702
				$incontext['ftp'] = $_SESSION['installer_temp_ftp'] += array(
703
					'path_msg' => $txt['ftp_path_info'],
704
				);
705
706
				return false;
707
			}
708
		}
709
	}
710
711
	return true;
712
}
713
714
function DatabaseSettings()
715
{
716
	global $txt, $databases, $incontext, $smcFunc, $sourcedir;
717
	global $db_server, $db_name, $db_user, $db_passwd, $db_port, $db_mb4;
718
719
	$incontext['sub_template'] = 'database_settings';
720
	$incontext['page_title'] = $txt['db_settings'];
721
	$incontext['continue'] = 1;
722
723
	// Set up the defaults.
724
	$incontext['db']['server'] = 'localhost';
725
	$incontext['db']['user'] = '';
726
	$incontext['db']['name'] = '';
727
	$incontext['db']['pass'] = '';
728
	$incontext['db']['type'] = '';
729
	$incontext['supported_databases'] = array();
730
731
	$foundOne = false;
732
	foreach ($databases as $key => $db)
733
	{
734
		// Override with the defaults for this DB if appropriate.
735
		if ($db['supported'])
736
		{
737
			$incontext['supported_databases'][$key] = $db;
738
739
			if (!$foundOne)
740
			{
741
				if (isset($db['default_host']))
742
					$incontext['db']['server'] = ini_get($db['default_host']) or $incontext['db']['server'] = 'localhost';
743
				if (isset($db['default_user']))
744
				{
745
					$incontext['db']['user'] = ini_get($db['default_user']);
746
					$incontext['db']['name'] = ini_get($db['default_user']);
747
				}
748
				if (isset($db['default_password']))
749
					$incontext['db']['pass'] = ini_get($db['default_password']);
750
751
				// For simplicity and less confusion, leave the port blank by default
752
				$incontext['db']['port'] = '';
753
754
				$incontext['db']['type'] = $key;
755
				$foundOne = true;
756
			}
757
		}
758
	}
759
760
	// Override for repost.
761
	if (isset($_POST['db_user']))
762
	{
763
		$incontext['db']['user'] = $_POST['db_user'];
764
		$incontext['db']['name'] = $_POST['db_name'];
765
		$incontext['db']['server'] = $_POST['db_server'];
766
		$incontext['db']['prefix'] = $_POST['db_prefix'];
767
768
		if (!empty($_POST['db_port']))
769
			$incontext['db']['port'] = $_POST['db_port'];
770
	}
771
	else
772
	{
773
		$incontext['db']['prefix'] = 'smf_';
774
	}
775
776
	// Are we submitting?
777
	if (isset($_POST['db_type']))
778
	{
779
		// What type are they trying?
780
		$db_type = preg_replace('~[^A-Za-z0-9]~', '', $_POST['db_type']);
781
		$db_prefix = $_POST['db_prefix'];
782
		// Validate the prefix.
783
		$valid_prefix = $databases[$db_type]['validate_prefix']($db_prefix);
784
785
		if ($valid_prefix !== true)
786
		{
787
			$incontext['error'] = $valid_prefix;
788
			return false;
789
		}
790
791
		// Take care of these variables...
792
		$vars = array(
793
			'db_type' => $db_type,
794
			'db_name' => $_POST['db_name'],
795
			'db_user' => $_POST['db_user'],
796
			'db_passwd' => isset($_POST['db_passwd']) ? $_POST['db_passwd'] : '',
797
			'db_server' => $_POST['db_server'],
798
			'db_prefix' => $db_prefix,
799
			// The cookiename is special; we want it to be the same if it ever needs to be reinstalled with the same info.
800
			'cookiename' => 'SMFCookie' . abs(crc32($_POST['db_name'] . preg_replace('~[^A-Za-z0-9_$]~', '', $_POST['db_prefix'])) % 1000),
801
		);
802
803
		// Only set the port if we're not using the default
804
		if (!empty($_POST['db_port']))
805
		{
806
			// For MySQL, we can get the "default port" from PHP. PostgreSQL has no such option though.
807
			if (($db_type == 'mysql' || $db_type == 'mysqli') && $_POST['db_port'] != ini_get($db_type . '.default_port'))
808
				$vars['db_port'] = (int) $_POST['db_port'];
809
			elseif ($db_type == 'postgresql' && $_POST['db_port'] != 5432)
810
				$vars['db_port'] = (int) $_POST['db_port'];
811
		}
812
813
		// God I hope it saved!
814
		if (!updateSettingsFile($vars) && substr(__FILE__, 1, 2) == ':\\')
815
		{
816
			$incontext['error'] = $txt['error_windows_chmod'];
817
			return false;
818
		}
819
820
		// Make sure it works.
821
		require(dirname(__FILE__) . '/Settings.php');
822
823
		if (empty($sourcedir))
824
			$sourcedir = dirname(__FILE__) . '/Sources';
825
826
		// Better find the database file!
827
		if (!file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php'))
828
		{
829
			$incontext['error'] = sprintf($txt['error_db_file'], 'Subs-Db-' . $db_type . '.php');
830
			return false;
831
		}
832
833
		// Now include it for database functions!
834
		if (!defined('SMF'))
835
			define('SMF', 1);
836
837
		$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...
838
		if (empty($smcFunc))
839
			$smcFunc = array();
840
841
		require_once($sourcedir . '/Subs-Db-' . $db_type . '.php');
842
843
		// Attempt a connection.
844
		$needsDB = !empty($databases[$db_type]['always_has_db']);
845
846
		$options = array('non_fatal' => true, 'dont_select_db' => !$needsDB);
847
		// Add in the port if needed
848
		if (!empty($db_port))
849
			$options['port'] = $db_port;
850
851
		if (!empty($db_mb4))
852
			$options['db_mb4'] = $db_mb4;
853
854
		$db_connection = smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, $db_prefix, $options);
855
856
		// No dice?  Let's try adding the prefix they specified, just in case they misread the instructions ;)
857
		if ($db_connection == null)
858
		{
859
			$db_error = @$smcFunc['db_error']();
860
861
			$db_connection = smf_db_initiate($db_server, $db_name, $_POST['db_prefix'] . $db_user, $db_passwd, $db_prefix, $options);
862
			if ($db_connection != null)
863
			{
864
				$db_user = $_POST['db_prefix'] . $db_user;
865
				updateSettingsFile(array('db_user' => $db_user));
866
			}
867
		}
868
869
		// Still no connection?  Big fat error message :P.
870
		if (!$db_connection)
0 ignored issues
show
introduced by
$db_connection is of type null|resource, thus it always evaluated to false.
Loading history...
871
		{
872
			$incontext['error'] = $txt['error_db_connect'] . '<div class="error_content"><strong>' . $db_error . '</strong></div>';
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $db_error does not seem to be defined for all execution paths leading up to this point.
Loading history...
873
			return false;
874
		}
875
876
		// Do they meet the install requirements?
877
		// @todo Old client, new server?
878
		if (version_compare($databases[$db_type]['version'], preg_replace('~^\D*|\-.+?$~', '', eval($databases[$db_type]['version_check']))) > 0)
0 ignored issues
show
introduced by
The use of eval() is discouraged.
Loading history...
879
		{
880
			$incontext['error'] = $txt['error_db_too_low'];
881
			return false;
882
		}
883
884
		// Let's try that database on for size... assuming we haven't already lost the opportunity.
885
		if ($db_name != '' && !$needsDB)
886
		{
887
			$smcFunc['db_query']('', "
888
				CREATE DATABASE IF NOT EXISTS `$db_name`",
889
				array(
890
					'security_override' => true,
891
					'db_error_skip' => true,
892
				),
893
				$db_connection
894
			);
895
896
			// Okay, let's try the prefix if it didn't work...
897
			if (!$smcFunc['db_select_db']($db_name, $db_connection) && $db_name != '')
898
			{
899
				$smcFunc['db_query']('', "
900
					CREATE DATABASE IF NOT EXISTS `$_POST[db_prefix]$db_name`",
901
					array(
902
						'security_override' => true,
903
						'db_error_skip' => true,
904
					),
905
					$db_connection
906
				);
907
908
				if ($smcFunc['db_select_db']($_POST['db_prefix'] . $db_name, $db_connection))
909
				{
910
					$db_name = $_POST['db_prefix'] . $db_name;
911
					updateSettingsFile(array('db_name' => $db_name));
912
				}
913
			}
914
915
			// Okay, now let's try to connect...
916
			if (!$smcFunc['db_select_db']($db_name, $db_connection))
917
			{
918
				$incontext['error'] = sprintf($txt['error_db_database'], $db_name);
919
				return false;
920
			}
921
		}
922
923
		return true;
924
	}
925
926
	return false;
927
}
928
929
// Let's start with basic forum type settings.
930
function ForumSettings()
931
{
932
	global $txt, $incontext, $databases, $db_type, $db_connection;
933
934
	$incontext['sub_template'] = 'forum_settings';
935
	$incontext['page_title'] = $txt['install_settings'];
936
937
	// Let's see if we got the database type correct.
938
	if (isset($_POST['db_type'], $databases[$_POST['db_type']]))
939
		$db_type = $_POST['db_type'];
940
941
	// Else we'd better be able to get the connection.
942
	else
943
		load_database();
944
945
	$db_type = isset($_POST['db_type']) ? $_POST['db_type'] : $db_type;
946
947
	// What host and port are we on?
948
	$host = empty($_SERVER['HTTP_HOST']) ? $_SERVER['SERVER_NAME'] . (empty($_SERVER['SERVER_PORT']) || $_SERVER['SERVER_PORT'] == '80' ? '' : ':' . $_SERVER['SERVER_PORT']) : $_SERVER['HTTP_HOST'];
949
950
	$secure = false;
951
952
	if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on')
953
		$secure = true;
954
	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...
955
		$secure = true;
956
957
	// Now, to put what we've learned together... and add a path.
958
	$incontext['detected_url'] = 'http' . ($secure ? 's' : '') . '://' . $host . substr($_SERVER['PHP_SELF'], 0, strrpos($_SERVER['PHP_SELF'], '/'));
959
960
	// Check if the database sessions will even work.
961
	$incontext['test_dbsession'] = (ini_get('session.auto_start') != 1);
962
	$incontext['utf8_default'] = $databases[$db_type]['utf8_default'];
963
	$incontext['utf8_required'] = $databases[$db_type]['utf8_required'];
964
965
	$incontext['continue'] = 1;
966
967
	// Setup the SSL checkbox...
968
	$incontext['ssl_chkbx_protected'] = false;
969
	$incontext['ssl_chkbx_checked'] = false;
970
971
	// If redirect in effect, force ssl ON
972
	require_once(dirname(__FILE__) . '/Sources/Subs.php');
973
	if (https_redirect_active($incontext['detected_url']))
974
	{
975
		$incontext['ssl_chkbx_protected'] = true;
976
		$incontext['ssl_chkbx_checked'] = true;
977
		$_POST['force_ssl'] = true;
978
	}
979
	// If no cert, make sure ssl stays OFF
980
	if (!ssl_cert_found($incontext['detected_url']))
981
	{
982
		$incontext['ssl_chkbx_protected'] = true;
983
		$incontext['ssl_chkbx_checked'] = false;
984
	}
985
986
	// Submitting?
987
	if (isset($_POST['boardurl']))
988
	{
989
		if (substr($_POST['boardurl'], -10) == '/index.php')
990
			$_POST['boardurl'] = substr($_POST['boardurl'], 0, -10);
991
		elseif (substr($_POST['boardurl'], -1) == '/')
992
			$_POST['boardurl'] = substr($_POST['boardurl'], 0, -1);
993
		if (substr($_POST['boardurl'], 0, 7) != 'http://' && substr($_POST['boardurl'], 0, 7) != 'file://' && substr($_POST['boardurl'], 0, 8) != 'https://')
994
			$_POST['boardurl'] = 'http://' . $_POST['boardurl'];
995
996
		//Make sure boardurl is aligned with ssl setting
997
		if (empty($_POST['force_ssl']))
998
			$_POST['boardurl'] = strtr($_POST['boardurl'], array('https://' => 'http://'));
999
		else
1000
			$_POST['boardurl'] = strtr($_POST['boardurl'], array('http://' => 'https://'));
1001
1002
		// Save these variables.
1003
		$vars = array(
1004
			'boardurl' => $_POST['boardurl'],
1005
			'boarddir' => addslashes(dirname(__FILE__)),
1006
			'sourcedir' => addslashes(dirname(__FILE__)) . '/Sources',
1007
			'cachedir' => addslashes(dirname(__FILE__)) . '/cache',
1008
			'packagesdir' => addslashes(dirname(__FILE__)) . '/Packages',
1009
			'tasksdir' => addslashes(dirname(__FILE__)) . '/Sources/tasks',
1010
			'mbname' => strtr($_POST['mbname'], array('\"' => '"')),
1011
			'language' => substr($_SESSION['installer_temp_lang'], 8, -4),
1012
			'image_proxy_secret' => substr(sha1(mt_rand()), 0, 20),
1013
			'image_proxy_enabled' => !empty($_POST['force_ssl']),
1014
		);
1015
1016
		// Must save!
1017
		if (!updateSettingsFile($vars) && substr(__FILE__, 1, 2) == ':\\')
1018
		{
1019
			$incontext['error'] = $txt['error_windows_chmod'];
1020
			return false;
1021
		}
1022
1023
		// Make sure it works.
1024
		require(dirname(__FILE__) . '/Settings.php');
1025
1026
		// UTF-8 requires a setting to override the language charset.
1027
		if ((!empty($databases[$db_type]['utf8_support']) && !empty($databases[$db_type]['utf8_required'])) || (empty($databases[$db_type]['utf8_required']) && !empty($databases[$db_type]['utf8_support']) && isset($_POST['utf8'])))
1028
		{
1029
			if (!$databases[$db_type]['utf8_support']())
1030
			{
1031
				$incontext['error'] = sprintf($txt['error_utf8_support']);
1032
				return false;
1033
			}
1034
1035
			if (!empty($databases[$db_type]['utf8_version_check']) && version_compare($databases[$db_type]['utf8_version'], preg_replace('~\-.+?$~', '', eval($databases[$db_type]['utf8_version_check'])), '>'))
0 ignored issues
show
introduced by
The use of eval() is discouraged.
Loading history...
1036
			{
1037
				$incontext['error'] = sprintf($txt['error_utf8_version'], $databases[$db_type]['utf8_version']);
1038
				return false;
1039
			}
1040
			else
1041
				// Set the character set here.
1042
				updateSettingsFile(array('db_character_set' => 'utf8'));
1043
		}
1044
1045
		// Good, skip on.
1046
		return true;
1047
	}
1048
1049
	return false;
1050
}
1051
1052
// Step one: Do the SQL thang.
1053
function DatabasePopulation()
1054
{
1055
	global $db_character_set, $txt, $db_connection, $smcFunc, $databases, $modSettings, $db_type, $db_prefix, $incontext, $db_name, $boardurl;
1056
1057
	$incontext['sub_template'] = 'populate_database';
1058
	$incontext['page_title'] = $txt['db_populate'];
1059
	$incontext['continue'] = 1;
1060
1061
	// Already done?
1062
	if (isset($_POST['pop_done']))
1063
		return true;
1064
1065
	// Reload settings.
1066
	require(dirname(__FILE__) . '/Settings.php');
1067
	load_database();
1068
1069
	// Before running any of the queries, let's make sure another version isn't already installed.
1070
	$result = $smcFunc['db_query']('', '
1071
		SELECT variable, value
1072
		FROM {db_prefix}settings',
1073
		array(
1074
			'db_error_skip' => true,
1075
		)
1076
	);
1077
	$newSettings = array();
1078
	$modSettings = array();
1079
	if ($result !== false)
1080
	{
1081
		while ($row = $smcFunc['db_fetch_assoc']($result))
1082
			$modSettings[$row['variable']] = $row['value'];
1083
		$smcFunc['db_free_result']($result);
1084
1085
		// Do they match?  If so, this is just a refresh so charge on!
1086
		if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] != SMF_VERSION)
1087
		{
1088
			$incontext['error'] = $txt['error_versions_do_not_match'];
1089
			return false;
1090
		}
1091
	}
1092
	$modSettings['disableQueryCheck'] = true;
1093
1094
	// If doing UTF8, select it. PostgreSQL requires passing it as a string...
1095
	if (!empty($db_character_set) && $db_character_set == 'utf8' && !empty($databases[$db_type]['utf8_support']))
1096
		$smcFunc['db_query']('', '
1097
			SET NAMES {string:utf8}',
1098
			array(
1099
				'db_error_skip' => true,
1100
				'utf8' => 'utf8',
1101
			)
1102
		);
1103
1104
	// Windows likes to leave the trailing slash, which yields to C:\path\to\SMF\/attachments...
1105
	if (substr(__DIR__, -1) == '\\')
1106
		$attachdir = __DIR__ . 'attachments';
1107
	else
1108
		$attachdir = __DIR__ . '/attachments';
1109
1110
	$replaces = array(
1111
		'{$db_prefix}' => $db_prefix,
1112
		'{$attachdir}' => json_encode(array(1 => $smcFunc['db_escape_string']($attachdir))),
1113
		'{$boarddir}' => $smcFunc['db_escape_string'](dirname(__FILE__)),
1114
		'{$boardurl}' => $boardurl,
1115
		'{$enableCompressedOutput}' => isset($_POST['compress']) ? '1' : '0',
1116
		'{$databaseSession_enable}' => isset($_POST['dbsession']) ? '1' : '0',
1117
		'{$smf_version}' => SMF_VERSION,
1118
		'{$current_time}' => time(),
1119
		'{$sched_task_offset}' => 82800 + mt_rand(0, 86399),
1120
		'{$registration_method}' => isset($_POST['reg_mode']) ? $_POST['reg_mode'] : 0,
1121
	);
1122
1123
	foreach ($txt as $key => $value)
1124
	{
1125
		if (substr($key, 0, 8) == 'default_')
1126
			$replaces['{$' . $key . '}'] = $smcFunc['db_escape_string']($value);
1127
	}
1128
	$replaces['{$default_reserved_names}'] = strtr($replaces['{$default_reserved_names}'], array('\\\\n' => '\\n'));
1129
1130
	// MySQL-specific stuff - storage engine and UTF8 handling
1131
	if (substr($db_type, 0, 5) == 'mysql')
1132
	{
1133
		// Just in case the query fails for some reason...
1134
		$engines = array();
1135
1136
		// Figure out storage engines - what do we have, etc.
1137
		$get_engines = $smcFunc['db_query']('', 'SHOW ENGINES', array());
1138
1139
		while ($row = $smcFunc['db_fetch_assoc']($get_engines))
1140
		{
1141
			if ($row['Support'] == 'YES' || $row['Support'] == 'DEFAULT')
1142
				$engines[] = $row['Engine'];
1143
		}
1144
1145
		// Done with this now
1146
		$smcFunc['db_free_result']($get_engines);
1147
1148
		// InnoDB is better, so use it if possible...
1149
		$has_innodb = in_array('InnoDB', $engines);
1150
		$replaces['{$engine}'] = $has_innodb ? 'InnoDB' : 'MyISAM';
1151
		$replaces['{$memory}'] = (!$has_innodb && in_array('MEMORY', $engines)) ? 'MEMORY' : $replaces['{$engine}'];
1152
1153
		// If the UTF-8 setting was enabled, add it to the table definitions.
1154
		if (!empty($databases[$db_type]['utf8_support']) && (!empty($databases[$db_type]['utf8_required']) || isset($_POST['utf8'])))
1155
		{
1156
			$replaces['{$engine}'] .= ' DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci';
1157
			$replaces['{$memory}'] .= ' DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci';
1158
		}
1159
1160
		// One last thing - if we don't have InnoDB, we can't do transactions...
1161
		if (!$has_innodb)
1162
		{
1163
			$replaces['START TRANSACTION;'] = '';
1164
			$replaces['COMMIT;'] = '';
1165
		}
1166
	}
1167
	else
1168
	{
1169
		$has_innodb = false;
1170
	}
1171
1172
	// Read in the SQL.  Turn this on and that off... internationalize... etc.
1173
	$type = ($db_type == 'mysqli' ? 'mysql' : $db_type);
1174
	$sql_lines = explode("\n", strtr(implode(' ', file(dirname(__FILE__) . '/install_' . DB_SCRIPT_VERSION . '_' . $type . '.sql')), $replaces));
0 ignored issues
show
Bug introduced by
It seems like file(dirname(__FILE__) ..... '_' . $type . '.sql') can also be of type false; however, parameter $pieces of implode() does only seem to accept array, 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

1174
	$sql_lines = explode("\n", strtr(implode(' ', /** @scrutinizer ignore-type */ file(dirname(__FILE__) . '/install_' . DB_SCRIPT_VERSION . '_' . $type . '.sql')), $replaces));
Loading history...
1175
1176
	// Execute the SQL.
1177
	$current_statement = '';
1178
	$exists = array();
1179
	$incontext['failures'] = array();
1180
	$incontext['sql_results'] = array(
1181
		'tables' => 0,
1182
		'inserts' => 0,
1183
		'table_dups' => 0,
1184
		'insert_dups' => 0,
1185
	);
1186
	foreach ($sql_lines as $count => $line)
1187
	{
1188
		// No comments allowed!
1189
		if (substr(trim($line), 0, 1) != '#')
1190
			$current_statement .= "\n" . rtrim($line);
1191
1192
		// Is this the end of the query string?
1193
		if (empty($current_statement) || (preg_match('~;[\s]*$~s', $line) == 0 && $count != count($sql_lines)))
1194
			continue;
1195
1196
		// Does this table already exist?  If so, don't insert more data into it!
1197
		if (preg_match('~^\s*INSERT INTO ([^\s\n\r]+?)~', $current_statement, $match) != 0 && in_array($match[1], $exists))
1198
		{
1199
			preg_match_all('~\)[,;]~', $current_statement, $matches);
1200
			if (!empty($matches[0]))
1201
				$incontext['sql_results']['insert_dups'] += count($matches[0]);
1202
			else
1203
				$incontext['sql_results']['insert_dups']++;
1204
1205
			$current_statement = '';
1206
			continue;
1207
		}
1208
1209
		if ($smcFunc['db_query']('', $current_statement, array('security_override' => true, 'db_error_skip' => true), $db_connection) === false)
1210
		{
1211
			// Error 1050: Table already exists!
1212
			// @todo Needs to be made better!
1213
			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...
1214
			{
1215
				$exists[] = $match[1];
1216
				$incontext['sql_results']['table_dups']++;
1217
			}
1218
			// Don't error on duplicate indexes (or duplicate operators in PostgreSQL.)
1219
			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)))
1220
			{
1221
				// MySQLi requires a connection object. It's optional with MySQL and Postgres
1222
				$incontext['failures'][$count] = $smcFunc['db_error']($db_connection);
1223
			}
1224
		}
1225
		else
1226
		{
1227
			if (preg_match('~^\s*CREATE TABLE ([^\s\n\r]+?)~', $current_statement, $match) == 1)
1228
				$incontext['sql_results']['tables']++;
1229
			elseif (preg_match('~^\s*INSERT INTO ([^\s\n\r]+?)~', $current_statement, $match) == 1)
1230
			{
1231
				preg_match_all('~\)[,;]~', $current_statement, $matches);
1232
				if (!empty($matches[0]))
1233
					$incontext['sql_results']['inserts'] += count($matches[0]);
1234
				else
1235
					$incontext['sql_results']['inserts']++;
1236
			}
1237
		}
1238
1239
		$current_statement = '';
1240
1241
		// Wait, wait, I'm still working here!
1242
		set_time_limit(60);
1243
	}
1244
1245
	// Sort out the context for the SQL.
1246
	foreach ($incontext['sql_results'] as $key => $number)
1247
	{
1248
		if ($number == 0)
1249
			unset($incontext['sql_results'][$key]);
1250
		else
1251
			$incontext['sql_results'][$key] = sprintf($txt['db_populate_' . $key], $number);
1252
	}
1253
1254
	// Make sure UTF will be used globally.
1255
	if ((!empty($databases[$db_type]['utf8_support']) && !empty($databases[$db_type]['utf8_required'])) || (empty($databases[$db_type]['utf8_required']) && !empty($databases[$db_type]['utf8_support']) && isset($_POST['utf8'])))
1256
		$newSettings[] = array('global_character_set', 'UTF-8');
1257
1258
	// Auto-detect local & global cookie settings
1259
	$url_parts = parse_url($boardurl);
1260
	if ($url_parts !== false)
1261
	{
1262
		unset($globalCookies, $globalCookiesDomain, $localCookies);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $globalCookiesDomain seems to be never defined.
Loading history...
Comprehensibility Best Practice introduced by
The variable $globalCookies does not exist. Did you maybe mean $globalCookiesDomain?
Loading history...
Comprehensibility Best Practice introduced by
The variable $localCookies seems to be never defined.
Loading history...
1263
1264
		// Look for subdomain, if found, set globalCookie settings
1265
		// Don't bother looking if you have an ip address for host
1266
		if (!empty($url_parts['host']) && (filter_var($url_parts['host'], FILTER_VALIDATE_IP) === false))
1267
		{
1268
			// www isn't really a subdomain in this sense, so strip it out
1269
			$url_parts['host'] = str_ireplace('www.', '', $url_parts['host']);
1270
			$pos1 = strrpos($url_parts['host'], '.');
1271
			if ($pos1 !== false)
1272
			{
1273
				// 2nd period from the right indicates you have a subdomain
1274
				$pos2 = strrpos(substr($url_parts['host'], 0, $pos1 - 1), '.');
1275
				if ($pos2 !== false)
1276
				{
1277
					$globalCookies = '1';
1278
					$globalCookiesDomain = substr($url_parts['host'], $pos2 + 1);
1279
				}
1280
			}
1281
		}
1282
1283
		// Look for subfolder, if found, set localCookie
1284
		// Checking for len > 1 ensures you don't have just a slash...
1285
		if (!empty($url_parts['path']) && strlen($url_parts['path']) > 1)
1286
			$localCookies = '1';
1287
1288
		if (isset($globalCookies))
1289
			$newSettings[] = array('globalCookies', $globalCookies);
1290
		if (isset($globalCookiesDomain))
1291
			$newSettings[] = array('globalCookiesDomain', $globalCookiesDomain);
1292
		if (isset($localCookies))
1293
			$newSettings[] = array('localCookies', $localCookies);
1294
	}
1295
1296
	// Are we allowing stat collection?
1297
	if (!empty($_POST['stats']) && substr($boardurl, 0, 16) != 'http://localhost' && empty($modSettings['allow_sm_stats']) && empty($modSettings['enable_sm_stats']))
1298
	{
1299
		$upcontext['allow_sm_stats'] = true;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$upcontext was never initialized. Although not strictly required by PHP, it is generally a good practice to add $upcontext = array(); before regardless.
Loading history...
1300
1301
		// Attempt to register the site etc.
1302
		$fp = @fsockopen('www.simplemachines.org', 80, $errno, $errstr);
1303
		if ($fp)
0 ignored issues
show
introduced by
$fp is of type resource, thus it always evaluated to false.
Loading history...
1304
		{
1305
			$out = 'GET /smf/stats/register_stats.php?site=' . base64_encode($boardurl) . ' HTTP/1.1' . "\r\n";
1306
			$out .= 'Host: www.simplemachines.org' . "\r\n";
1307
			$out .= 'Connection: Close' . "\r\n\r\n";
1308
			fwrite($fp, $out);
1309
1310
			$return_data = '';
1311
			while (!feof($fp))
1312
				$return_data .= fgets($fp, 128);
1313
1314
			fclose($fp);
1315
1316
			// Get the unique site ID.
1317
			preg_match('~SITE-ID:\s(\w{10})~', $return_data, $ID);
1318
1319
			if (!empty($ID[1]))
1320
				$smcFunc['db_insert']('replace',
1321
					$db_prefix . 'settings',
1322
					array('variable' => 'string', 'value' => 'string'),
1323
					array(
1324
						array('sm_stats_key', $ID[1]),
1325
						array('enable_sm_stats', 1),
1326
					),
1327
					array('variable')
1328
				);
1329
		}
1330
	}
1331
	// Don't remove stat collection unless we unchecked the box for real, not from the loop.
1332
	elseif (empty($_POST['stats']) && empty($upcontext['allow_sm_stats']))
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $upcontext seems to never exist and therefore empty should always be true.
Loading history...
1333
		$smcFunc['db_query']('', '
1334
			DELETE FROM {db_prefix}settings
1335
			WHERE variable = {string:enable_sm_stats}',
1336
			array(
1337
				'enable_sm_stats' => 'enable_sm_stats',
1338
				'db_error_skip' => true,
1339
			)
1340
		);
1341
1342
	// Are we enabling SSL?
1343
	if (!empty($_POST['force_ssl']))
1344
		$newSettings[] = array('force_ssl', 1);
1345
1346
	// Setting a timezone is required.
1347
	if (!isset($modSettings['default_timezone']) && function_exists('date_default_timezone_set'))
1348
	{
1349
		// Get PHP's default timezone, if set
1350
		$ini_tz = ini_get('date.timezone');
1351
		if (!empty($ini_tz))
1352
			$timezone_id = $ini_tz;
1353
		else
1354
			$timezone_id = '';
1355
1356
		// If date.timezone is unset, invalid, or just plain weird, make a best guess
1357
		if (!in_array($timezone_id, timezone_identifiers_list()))
0 ignored issues
show
Bug introduced by
It seems like timezone_identifiers_list() can also be of type false; however, parameter $haystack of in_array() does only seem to accept array, 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

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

1820
	if (count(/** @scrutinizer ignore-type */ $settingsArray) == 1)
Loading history...
1821
		$settingsArray = preg_split('~[\r\n]~', $settingsArray[0]);
1822
1823
	for ($i = 0, $n = count($settingsArray); $i < $n; $i++)
1824
	{
1825
		// Remove the redirect...
1826
		if (trim($settingsArray[$i]) == 'if (file_exists(dirname(__FILE__) . \'/install.php\'))' && trim($settingsArray[$i + 1]) == '{' && trim($settingsArray[$i + 10]) == '}')
1827
		{
1828
			// Set the ten lines to nothing.
1829
			for ($j = 0; $j < 11; $j++)
1830
				$settingsArray[$i++] = '';
1831
1832
			continue;
1833
		}
1834
1835
		if (trim($settingsArray[$i]) == '?' . '>')
1836
			$settingsArray[$i] = '';
1837
1838
		// Don't trim or bother with it if it's not a variable.
1839
		if (substr($settingsArray[$i], 0, 1) != '$')
1840
			continue;
1841
1842
		$settingsArray[$i] = rtrim($settingsArray[$i]) . "\n";
1843
1844
		foreach ($vars as $var => $val)
1845
			if (strncasecmp($settingsArray[$i], '$' . $var, 1 + strlen($var)) == 0)
1846
			{
1847
				$comment = strstr($settingsArray[$i], '#');
1848
				$settingsArray[$i] = '$' . $var . ' = \'' . $val . '\';' . ($comment != '' ? "\t\t" . $comment : "\n");
1849
				unset($vars[$var]);
1850
			}
1851
	}
1852
1853
	// Uh oh... the file wasn't empty... was it?
1854
	if (!empty($vars))
1855
	{
1856
		$settingsArray[$i++] = '';
1857
		foreach ($vars as $var => $val)
1858
			$settingsArray[$i++] = '$' . $var . ' = \'' . $val . '\';' . "\n";
1859
	}
1860
1861
	// Blank out the file - done to fix a oddity with some servers.
1862
	$fp = @fopen(dirname(__FILE__) . '/Settings.php', 'w');
1863
	if (!$fp)
0 ignored issues
show
introduced by
$fp is of type false|resource, thus it always evaluated to false.
Loading history...
1864
		return false;
1865
	fclose($fp);
1866
1867
	$fp = fopen(dirname(__FILE__) . '/Settings.php', 'r+');
1868
1869
	// Gotta have one of these ;)
1870
	if (trim($settingsArray[0]) != '<?php')
1871
		fwrite($fp, "<?php\n");
1872
1873
	$lines = count($settingsArray);
1874
	for ($i = 0; $i < $lines - 1; $i++)
1875
	{
1876
		// Don't just write a bunch of blank lines.
1877
		if ($settingsArray[$i] != '' || @$settingsArray[$i - 1] != '')
1878
			fwrite($fp, strtr($settingsArray[$i], "\r", ''));
1879
	}
1880
	fwrite($fp, $settingsArray[$i] . '?' . '>');
1881
	fclose($fp);
1882
1883
	// Even though on normal installations the filemtime should prevent this being used by the installer incorrectly
1884
	// it seems that there are times it might not. So let's MAKE it dump the cache.
1885
	if (function_exists('opcache_invalidate'))
1886
		opcache_invalidate(dirname(__FILE__) . '/Settings.php', true);
1887
1888
	return true;
1889
}
1890
1891
function updateDbLastError()
1892
{
1893
	global $cachedir;
1894
1895
	// Write out the db_last_error file with the error timestamp
1896
	if (!empty($cachedir) && is_writable($cachedir))
1897
		file_put_contents($cachedir . '/db_last_error.php', '<' . '?' . "php\n" . '$db_last_error = 0;' . "\n" . '?' . '>');
1898
	else
1899
		file_put_contents(dirname(__FILE__) . '/cache/db_last_error.php', '<' . '?' . "php\n" . '$db_last_error = 0;' . "\n" . '?' . '>');
1900
1901
	return true;
1902
}
1903
1904
// Create an .htaccess file to prevent mod_security. SMF has filtering built-in.
1905
function fixModSecurity()
1906
{
1907
	$htaccess_addition = '
1908
<IfModule mod_security.c>
1909
	# Turn off mod_security filtering.  SMF is a big boy, it doesn\'t need its hands held.
1910
	SecFilterEngine Off
1911
1912
	# The below probably isn\'t needed, but better safe than sorry.
1913
	SecFilterScanPOST Off
1914
</IfModule>';
1915
1916
	if (!function_exists('apache_get_modules') || !in_array('mod_security', apache_get_modules()))
1917
		return true;
1918
	elseif (file_exists(dirname(__FILE__) . '/.htaccess') && is_writable(dirname(__FILE__) . '/.htaccess'))
1919
	{
1920
		$current_htaccess = implode('', file(dirname(__FILE__) . '/.htaccess'));
0 ignored issues
show
Bug introduced by
It seems like file(dirname(__FILE__) . '/.htaccess') can also be of type false; however, parameter $pieces of implode() does only seem to accept array, 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

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