Passed
Pull Request — release-2.1 (#5585)
by John
04:24
created

load_lang_file()   C

Complexity

Conditions 14
Paths 20

Size

Total Lines 83
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 14
eloc 26
nc 20
nop 0
dl 0
loc 83
rs 6.2666
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
178
	}
179
	else
180
	{
181
		ob_start('ob_gzhandler');
182
183
		if (ini_get('session.save_handler') == 'user')
184
			@ini_set('session.save_handler', 'files');
185
		session_start();
186
187
		if (!headers_sent())
188
			echo '<!DOCTYPE html>
189
<html>
190
	<head>
191
		<title>', htmlspecialchars($_GET['pass_string']), '</title>
192
	</head>
193
	<body style="background-color: #d4d4d4; margin-top: 16%; text-align: center; font-size: 16pt;">
194
		<strong>', htmlspecialchars($_GET['pass_string']), '</strong>
195
	</body>
196
</html>';
197
		exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

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

Loading history...
198
	}
199
200
	// This is really quite simple; if ?delete is on the URL, delete the installer...
201
	if (isset($_GET['delete']))
202
	{
203
		if (isset($_SESSION['installer_temp_ftp']))
204
		{
205
			$ftp = new ftp_connection($_SESSION['installer_temp_ftp']['server'], $_SESSION['installer_temp_ftp']['port'], $_SESSION['installer_temp_ftp']['username'], $_SESSION['installer_temp_ftp']['password']);
206
			$ftp->chdir($_SESSION['installer_temp_ftp']['path']);
207
208
			$ftp->unlink('install.php');
209
210
			foreach ($databases as $key => $dummy)
211
			{
212
				$type = ($key == 'mysqli') ? 'mysql' : $key;
213
				$ftp->unlink('install_' . DB_SCRIPT_VERSION . '_' . $type . '.sql');
214
			}
215
216
			$ftp->close();
217
218
			unset($_SESSION['installer_temp_ftp']);
219
		}
220
		else
221
		{
222
			@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

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

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

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

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

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

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

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

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

1847
	if (count(/** @scrutinizer ignore-type */ $settingsArray) == 1)
Loading history...
1848
		$settingsArray = preg_split('~[\r\n]~', $settingsArray[0]);
1849
1850
	for ($i = 0, $n = count($settingsArray); $i < $n; $i++)
1851
	{
1852
		// Remove the redirect...
1853
		if (trim($settingsArray[$i]) == 'if (file_exists(dirname(__FILE__) . \'/install.php\'))' && trim($settingsArray[$i + 1]) == '{' && trim($settingsArray[$i + 10]) == '}')
1854
		{
1855
			// Set the ten lines to nothing.
1856
			for ($j = 0; $j < 11; $j++)
1857
				$settingsArray[$i++] = '';
1858
1859
			continue;
1860
		}
1861
1862
		if (trim($settingsArray[$i]) == '?' . '>')
1863
			$settingsArray[$i] = '';
1864
1865
		// Don't trim or bother with it if it's not a variable.
1866
		if (substr($settingsArray[$i], 0, 1) != '$')
1867
			continue;
1868
1869
		$settingsArray[$i] = rtrim($settingsArray[$i]) . "\n";
1870
1871
		foreach ($vars as $var => $val)
1872
			if (strncasecmp($settingsArray[$i], '$' . $var, 1 + strlen($var)) == 0)
1873
			{
1874
				$comment = strstr($settingsArray[$i], '#');
1875
				$settingsArray[$i] = '$' . $var . ' = \'' . $val . '\';' . ($comment != '' ? "\t\t" . $comment : "\n");
1876
				unset($vars[$var]);
1877
			}
1878
	}
1879
1880
	// Uh oh... the file wasn't empty... was it?
1881
	if (!empty($vars))
1882
	{
1883
		$settingsArray[$i++] = '';
1884
		foreach ($vars as $var => $val)
1885
			$settingsArray[$i++] = '$' . $var . ' = \'' . $val . '\';' . "\n";
1886
	}
1887
1888
	// Blank out the file - done to fix a oddity with some servers.
1889
	$fp = @fopen(dirname(__FILE__) . '/Settings.php', 'w');
1890
	if (!$fp)
0 ignored issues
show
introduced by
$fp is of type false|resource, thus it always evaluated to false.
Loading history...
1891
		return false;
1892
	fclose($fp);
1893
1894
	$fp = fopen(dirname(__FILE__) . '/Settings.php', 'r+');
1895
1896
	// Gotta have one of these ;)
1897
	if (trim($settingsArray[0]) != '<?php')
1898
		fwrite($fp, "<?php\n");
1899
1900
	$lines = count($settingsArray);
1901
	for ($i = 0; $i < $lines - 1; $i++)
1902
	{
1903
		// Don't just write a bunch of blank lines.
1904
		if ($settingsArray[$i] != '' || @$settingsArray[$i - 1] != '')
1905
			fwrite($fp, strtr($settingsArray[$i], "\r", ''));
1906
	}
1907
	fwrite($fp, $settingsArray[$i] . '?' . '>');
1908
	fclose($fp);
1909
1910
	// Even though on normal installations the filemtime should prevent this being used by the installer incorrectly
1911
	// it seems that there are times it might not. So let's MAKE it dump the cache.
1912
	if (function_exists('opcache_invalidate'))
1913
		opcache_invalidate(dirname(__FILE__) . '/Settings.php', true);
1914
1915
	return true;
1916
}
1917
1918
function updateDbLastError()
1919
{
1920
	global $cachedir;
1921
1922
	// Write out the db_last_error file with the error timestamp
1923
	if (!empty($cachedir) && is_writable($cachedir))
1924
		file_put_contents($cachedir . '/db_last_error.php', '<' . '?' . "php\n" . '$db_last_error = 0;' . "\n" . '?' . '>');
1925
	else
1926
		file_put_contents(dirname(__FILE__) . '/cache/db_last_error.php', '<' . '?' . "php\n" . '$db_last_error = 0;' . "\n" . '?' . '>');
1927
1928
	return true;
1929
}
1930
1931
// Create an .htaccess file to prevent mod_security. SMF has filtering built-in.
1932
function fixModSecurity()
1933
{
1934
	$htaccess_addition = '
1935
<IfModule mod_security.c>
1936
	# Turn off mod_security filtering.  SMF is a big boy, it doesn\'t need its hands held.
1937
	SecFilterEngine Off
1938
1939
	# The below probably isn\'t needed, but better safe than sorry.
1940
	SecFilterScanPOST Off
1941
</IfModule>';
1942
1943
	if (!function_exists('apache_get_modules') || !in_array('mod_security', apache_get_modules()))
1944
		return true;
1945
	elseif (file_exists(dirname(__FILE__) . '/.htaccess') && is_writable(dirname(__FILE__) . '/.htaccess'))
1946
	{
1947
		$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

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