Passed
Push — release-2.1 ( 0c2197...207d2d )
by Jeremy
05:47
created

Welcome()   F

Complexity

Conditions 25
Paths 2017

Size

Total Lines 80
Code Lines 44

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 25
eloc 44
c 0
b 0
f 0
nop 0
dl 0
loc 80
rs 0
nc 2017

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

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

62
			$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

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

168
			/** @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...
169
		if (function_exists('session_start'))
170
			@session_start();
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition 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

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

221
			/** @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...
222
223
			foreach ($databases as $key => $dummy)
224
			{
225
				$type = ($key == 'mysqli') ? 'mysql' : $key;
226
				@unlink(dirname(__FILE__) . '/install_' . $GLOBALS['db_script_version'] . '_' . $type . '.sql');
227
			}
228
		}
229
230
		// Now just redirect to a blank.png...
231
		$secure = false;
232
233
		if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on')
234
			$secure = true;
235
		elseif (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https' || !empty($_SERVER['HTTP_X_FORWARDED_SSL']) && $_SERVER['HTTP_X_FORWARDED_SSL'] == 'on')
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...
236
			$secure = true;
237
238
		header('location: http' . ($secure ? 's' : '') . '://' . (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : $_SERVER['SERVER_NAME'] . ':' . $_SERVER['SERVER_PORT']) . dirname($_SERVER['PHP_SELF']) . '/Themes/default/images/blank.png');
239
		exit;
240
	}
241
242
	// PHP 5 might cry if we don't do this now.
243
	if (function_exists('date_default_timezone_set'))
244
	{
245
		// Get PHP's default timezone, if set
246
		$ini_tz = ini_get('date.timezone');
247
		if (!empty($ini_tz))
248
			$timezone_id = $ini_tz;
249
		else
250
			$timezone_id = '';
251
252
		// If date.timezone is unset, invalid, or just plain weird, make a best guess
253
		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

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

543
				/** @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...
544
			// NOW do the writable check...
545
			if (!is_writable(dirname(__FILE__) . '/' . $file))
546
			{
547
				@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

547
				/** @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...
548
549
				// Well, 755 hopefully worked... if not, try 777.
550
				if (!is_writable(dirname(__FILE__) . '/' . $file) && !@chmod(dirname(__FILE__) . '/' . $file, 0777))
551
					$failed_files[] = $file;
552
			}
553
		}
554
		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 525. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
555
			@chmod(dirname(__FILE__) . (empty($file) ? '' : '/' . $file), 0777);
556
	}
557
	// Windows is trickier.  Let's try opening for r+...
558
	else
559
	{
560
		$incontext['systemos'] = 'windows';
561
562
		foreach ($writable_files as $file)
563
		{
564
			// Folders can't be opened for write... but the index.php in them can ;)
565
			if (is_dir(dirname(__FILE__) . '/' . $file))
566
				$file .= '/index.php';
567
568
			// Funny enough, chmod actually does do something on windows - it removes the read only attribute.
569
			@chmod(dirname(__FILE__) . '/' . $file, 0777);
570
			$fp = @fopen(dirname(__FILE__) . '/' . $file, 'r+');
571
572
			// Hmm, okay, try just for write in that case...
573
			if (!is_resource($fp))
574
				$fp = @fopen(dirname(__FILE__) . '/' . $file, 'w');
575
576
			if (!is_resource($fp))
577
				$failed_files[] = $file;
578
579
			@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

579
			/** @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

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

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

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

1759
	if (count(/** @scrutinizer ignore-type */ $settingsArray) == 1)
Loading history...
1760
		$settingsArray = preg_split('~[\r\n]~', $settingsArray[0]);
1761
1762
	for ($i = 0, $n = count($settingsArray); $i < $n; $i++)
1763
	{
1764
		// Remove the redirect...
1765
		if (trim($settingsArray[$i]) == 'if (file_exists(dirname(__FILE__) . \'/install.php\'))' && trim($settingsArray[$i + 1]) == '{' && trim($settingsArray[$i + 9]) == '}')
1766
		{
1767
			// Set the ten lines to nothing.
1768
			for ($j=0; $j < 10; $j++)
1769
				$settingsArray[$i++] = '';
1770
1771
			continue;
1772
		}
1773
1774
		if (trim($settingsArray[$i]) == '?' . '>')
1775
			$settingsArray[$i] = '';
1776
1777
		// Don't trim or bother with it if it's not a variable.
1778
		if (substr($settingsArray[$i], 0, 1) != '$')
1779
			continue;
1780
1781
		$settingsArray[$i] = rtrim($settingsArray[$i]) . "\n";
1782
1783
		foreach ($vars as $var => $val)
1784
			if (strncasecmp($settingsArray[$i], '$' . $var, 1 + strlen($var)) == 0)
1785
			{
1786
				$comment = strstr($settingsArray[$i], '#');
1787
				$settingsArray[$i] = '$' . $var . ' = \'' . $val . '\';' . ($comment != '' ? "\t\t" . $comment : "\n");
1788
				unset($vars[$var]);
1789
			}
1790
	}
1791
1792
	// Uh oh... the file wasn't empty... was it?
1793
	if (!empty($vars))
1794
	{
1795
		$settingsArray[$i++] = '';
1796
		foreach ($vars as $var => $val)
1797
			$settingsArray[$i++] = '$' . $var . ' = \'' . $val . '\';' . "\n";
1798
	}
1799
1800
	// Blank out the file - done to fix a oddity with some servers.
1801
	$fp = @fopen(dirname(__FILE__) . '/Settings.php', 'w');
1802
	if (!$fp)
0 ignored issues
show
introduced by
$fp is of type false|resource, thus it always evaluated to false.
Loading history...
1803
		return false;
1804
	fclose($fp);
1805
1806
	$fp = fopen(dirname(__FILE__) . '/Settings.php', 'r+');
1807
1808
	// Gotta have one of these ;)
1809
	if (trim($settingsArray[0]) != '<?php')
1810
		fwrite($fp, "<?php\n");
1811
1812
	$lines = count($settingsArray);
1813
	for ($i = 0; $i < $lines - 1; $i++)
1814
	{
1815
		// Don't just write a bunch of blank lines.
1816
		if ($settingsArray[$i] != '' || @$settingsArray[$i - 1] != '')
1817
			fwrite($fp, strtr($settingsArray[$i], "\r", ''));
1818
	}
1819
	fwrite($fp, $settingsArray[$i] . '?' . '>');
1820
	fclose($fp);
1821
1822
	// Even though on normal installations the filemtime should prevent this being used by the installer incorrectly
1823
	// it seems that there are times it might not. So let's MAKE it dump the cache.
1824
	if (function_exists('opcache_invalidate'))
1825
		opcache_invalidate(dirname(__FILE__) . '/Settings.php', true);
1826
1827
	return true;
1828
}
1829
1830
function updateDbLastError()
1831
{
1832
	global $cachedir;
1833
1834
	// Write out the db_last_error file with the error timestamp
1835
	if (!empty($cachedir) && is_writable($cachedir))
1836
		file_put_contents($cachedir . '/db_last_error.php', '<' . '?' . "php\n" . '$db_last_error = 0;' . "\n" . '?' . '>');
1837
	else
1838
		file_put_contents(dirname(__FILE__) . '/cache/db_last_error.php', '<' . '?' . "php\n" . '$db_last_error = 0;' . "\n" . '?' . '>');
1839
1840
	return true;
1841
}
1842
1843
// Create an .htaccess file to prevent mod_security. SMF has filtering built-in.
1844
function fixModSecurity()
1845
{
1846
	$htaccess_addition = '
1847
<IfModule mod_security.c>
1848
	# Turn off mod_security filtering.  SMF is a big boy, it doesn\'t need its hands held.
1849
	SecFilterEngine Off
1850
1851
	# The below probably isn\'t needed, but better safe than sorry.
1852
	SecFilterScanPOST Off
1853
</IfModule>';
1854
1855
	if (!function_exists('apache_get_modules') || !in_array('mod_security', apache_get_modules()))
1856
		return true;
1857
	elseif (file_exists(dirname(__FILE__) . '/.htaccess') && is_writable(dirname(__FILE__) . '/.htaccess'))
1858
	{
1859
		$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

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