Issues (1014)

other/upgrade.php (44 issues)

1
<?php
2
3
/**
4
 * Simple Machines Forum (SMF)
5
 *
6
 * @package SMF
7
 * @author Simple Machines https://www.simplemachines.org
8
 * @copyright 2022 Simple Machines and individual contributors
9
 * @license https://www.simplemachines.org/about/smf/license.php BSD
10
 *
11
 * @version 2.1.2
12
 */
13
14
// Version information...
15
define('SMF_VERSION', '2.1.2');
16
define('SMF_FULL_VERSION', 'SMF ' . SMF_VERSION);
17
define('SMF_SOFTWARE_YEAR', '2022');
18
define('SMF_LANG_VERSION', '2.1.2');
19
define('SMF_INSTALLING', 1);
20
21
define('JQUERY_VERSION', '3.6.0');
22
define('POSTGRE_TITLE', 'PostgreSQL');
23
define('MYSQL_TITLE', 'MySQL');
24
define('SMF_USER_AGENT', 'Mozilla/5.0 (' . php_uname('s') . ' ' . php_uname('m') . ') AppleWebKit/605.1.15 (KHTML, like Gecko)  SMF/' . strtr(SMF_VERSION, ' ', '.'));
25
if (!defined('TIME_START'))
26
	define('TIME_START', microtime(true));
27
28
/**
29
 * The minimum required PHP version.
30
 *
31
 * @var string
32
 */
33
$GLOBALS['required_php_version'] = '7.0.0';
34
35
/**
36
 * A list of supported database systems.
37
 *
38
 * @var array
39
 */
40
$databases = array(
41
	'mysql' => array(
42
		'name' => 'MySQL',
43
		'version' => '5.6.0',
44
		'version_check' => function() {
45
			global $db_connection;
46
			if (!function_exists('mysqli_fetch_row'))
47
				return false;
48
			return mysqli_fetch_row(mysqli_query($db_connection, 'SELECT VERSION();'))[0];
0 ignored issues
show
It seems like mysqli_query($db_connection, 'SELECT VERSION();') can also be of type true; however, parameter $result of mysqli_fetch_row() does only seem to accept mysqli_result, maybe add an additional type check? ( Ignorable by Annotation )

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

48
			return mysqli_fetch_row(/** @scrutinizer ignore-type */ mysqli_query($db_connection, 'SELECT VERSION();'))[0];
Loading history...
49
		},
50
		'alter_support' => true,
51
	),
52
	'postgresql' => array(
53
		'name' => 'PostgreSQL',
54
		'version' => '9.6',
55
		'version_check' => function() {
56
			if (!function_exists('pg_version'))
57
				return false;
58
			$version = pg_version();
59
			return $version['client'];
60
		},
61
		'always_has_db' => true,
62
	),
63
);
64
65
/**
66
 * The maximum time a single substep may take, in seconds.
67
 *
68
 * @var int
69
 */
70
$timeLimitThreshold = 3;
71
72
/**
73
 * The current path to the upgrade.php file.
74
 *
75
 * @var string
76
 */
77
$upgrade_path = dirname(__FILE__);
78
79
/**
80
 * The URL of the current page.
81
 *
82
 * @var string
83
 */
84
$upgradeurl = $_SERVER['PHP_SELF'];
85
86
/**
87
 * Flag to disable the required administrator login.
88
 *
89
 * @var bool
90
 */
91
$disable_security = false;
92
93
/**
94
 * The amount of seconds allowed between logins.
95
 * If the first user to login is inactive for this amount of seconds, a second login is allowed.
96
 *
97
 * @var int
98
 */
99
$upcontext['inactive_timeout'] = 10;
100
101
global $txt;
102
103
// All the steps in detail.
104
// Number,Name,Function,Progress Weight.
105
$upcontext['steps'] = array(
106
	0 => array(1, 'upgrade_step_login', 'WelcomeLogin', 2),
107
	1 => array(2, 'upgrade_step_options', 'UpgradeOptions', 2),
108
	2 => array(3, 'upgrade_step_backup', 'BackupDatabase', 10),
109
	3 => array(4, 'upgrade_step_database', 'DatabaseChanges', 50),
110
	4 => array(5, 'upgrade_step_convertjson', 'serialize_to_json', 10),
111
	5 => array(6, 'upgrade_step_convertutf', 'ConvertUtf8', 20),
112
	6 => array(7, 'upgrade_step_delete', 'DeleteUpgrade', 1),
113
);
114
// Just to remember which one has files in it.
115
$upcontext['database_step'] = 3;
116
117
// Secure some resources
118
@ini_set('mysql.connect_timeout', -1);
119
@ini_set('default_socket_timeout', 900);
120
@ini_set('memory_limit', '512M');
121
122
// Clean the upgrade path if this is from the client.
123
if (!empty($_SERVER['argv']) && php_sapi_name() == 'cli' && empty($_SERVER['REMOTE_ADDR']))
124
	for ($i = 1; $i < $_SERVER['argc']; $i++)
125
	{
126
		// Provide the help without possible errors if the enviornment isn't sane.
127
		if (in_array($_SERVER['argv'][$i], array('-h', '--help')))
128
		{
129
			cmdStep0();
130
			exit;
131
		}
132
133
		if (preg_match('~^--path=(.+)$~', $_SERVER['argv'][$i], $match) != 0)
134
			$upgrade_path = realpath(substr($match[1], -1) == '/' ? substr($match[1], 0, -1) : $match[1]);
135
136
		// Cases where we do php other/upgrade.php --path=./
137
		if ($upgrade_path == './' && isset($_SERVER['PWD']))
138
			$upgrade_path = realpath($_SERVER['PWD']);
139
		// Cases where we do php upgrade.php --path=../
140
		elseif ($upgrade_path == '../' && isset($_SERVER['PWD']))
141
			$upgrade_path = dirname(realpath($_SERVER['PWD']));
142
	}
143
144
// Are we from the client?
145
if (php_sapi_name() == 'cli' && empty($_SERVER['REMOTE_ADDR']))
146
{
147
	$command_line = true;
148
	$disable_security = true;
149
}
150
else
151
	$command_line = false;
152
153
// We can't do anything without these files.
154
foreach (array(
155
	dirname(__FILE__) . '/upgrade-helper.php',
156
	$upgrade_path . '/Settings.php'
157
) as $required_file)
158
{
159
	if (!file_exists($required_file))
160
		die(basename($required_file) . ' was not found where it was expected: ' . $required_file . '! Make sure you have uploaded ALL files from the upgrade package to your forum\'s root directory. The upgrader cannot continue.');
161
162
	require_once($required_file);
163
}
164
165
// We don't use "-utf8" anymore...  Tweak the entry that may have been loaded by Settings.php
166
if (isset($language))
167
	$language = str_ireplace('-utf8', '', basename($language, '.lng'));
168
169
// Figure out a valid language request (if any)
170
// Can't use $_GET until it's been cleaned, so do this manually and VERY restrictively! This even strips off those '-utf8' bits that we don't want.
171
if (isset($_SERVER['QUERY_STRING']) && preg_match('~\blang=(\w+)~', $_SERVER['QUERY_STRING'], $matches))
172
	$upcontext['lang'] = $matches[1];
173
174
// Are we logged in?
175
if (isset($upgradeData))
176
{
177
	$upcontext['user'] = json_decode(base64_decode($upgradeData), true);
178
179
	// Check for sensible values.
180
	if (empty($upcontext['user']['started']) || $upcontext['user']['started'] < time() - 86400)
181
		$upcontext['user']['started'] = time();
182
	if (empty($upcontext['user']['updated']) || $upcontext['user']['updated'] < time() - 86400)
183
		$upcontext['user']['updated'] = 0;
184
185
	$upcontext['started'] = $upcontext['user']['started'];
186
	$upcontext['updated'] = $upcontext['user']['updated'];
187
188
	$is_debug = !empty($upcontext['user']['debug']) ? true : false;
189
190
	$upcontext['skip_db_substeps'] = !empty($upcontext['user']['skip_db_substeps']);
191
}
192
193
// Nothing sensible?
194
if (empty($upcontext['updated']))
195
{
196
	$upcontext['started'] = time();
197
	$upcontext['updated'] = 0;
198
	$upcontext['skip_db_substeps'] = false;
199
	$upcontext['user'] = array(
200
		'id' => 0,
201
		'name' => 'Guest',
202
		'pass' => 0,
203
		'started' => $upcontext['started'],
204
		'updated' => $upcontext['updated'],
205
	);
206
}
207
208
// Try to load the language file... or at least define a few necessary strings for now.
209
load_lang_file();
210
211
// Load up some essential data...
212
loadEssentialData();
213
214
// Are we going to be mimic'ing SSI at this point?
215
if (isset($_GET['ssi']))
216
{
217
	require_once($sourcedir . '/Errors.php');
218
	require_once($sourcedir . '/Logging.php');
219
	require_once($sourcedir . '/Load.php');
220
	require_once($sourcedir . '/Security.php');
221
	require_once($sourcedir . '/Subs-Package.php');
222
223
	// SMF isn't started up properly, but loadUserSettings calls our cookies.
224
	if (!isset($smcFunc['json_encode']))
225
	{
226
		$smcFunc['json_encode'] = 'json_encode';
227
		$smcFunc['json_decode'] = 'smf_json_decode';
228
	}
229
230
	loadUserSettings();
231
	loadPermissions();
232
	reloadSettings();
233
}
234
235
// Include our helper functions.
236
require_once($sourcedir . '/Subs.php');
237
require_once($sourcedir . '/LogInOut.php');
238
require_once($sourcedir . '/Subs-Editor.php');
239
240
// Don't do security check if on Yabbse
241
if (!isset($modSettings['smfVersion']))
242
	$disable_security = true;
243
244
// This only exists if we're on SMF ;)
245
if (isset($modSettings['smfVersion']))
246
{
247
	$request = $smcFunc['db_query']('', '
248
		SELECT variable, value
249
		FROM {db_prefix}themes
250
		WHERE id_theme = {int:id_theme}
251
			AND variable IN ({string:theme_url}, {string:theme_dir}, {string:images_url})',
252
		array(
253
			'id_theme' => 1,
254
			'theme_url' => 'theme_url',
255
			'theme_dir' => 'theme_dir',
256
			'images_url' => 'images_url',
257
			'db_error_skip' => true,
258
		)
259
	);
260
	while ($row = $smcFunc['db_fetch_assoc']($request))
261
		$modSettings[$row['variable']] = $row['value'];
262
	$smcFunc['db_free_result']($request);
263
}
264
265
if (!isset($modSettings['theme_url']))
266
{
267
	$modSettings['theme_dir'] = $boarddir . '/Themes/default';
268
	$modSettings['theme_url'] = 'Themes/default';
269
	$modSettings['images_url'] = 'Themes/default/images';
270
}
271
if (!isset($settings['default_theme_url']))
272
	$settings['default_theme_url'] = $modSettings['theme_url'];
273
if (!isset($settings['default_theme_dir']))
274
	$settings['default_theme_dir'] = $modSettings['theme_dir'];
275
276
// Old DBs won't have this
277
if (!isset($modSettings['rand_seed']))
278
{
279
	if (!function_exists('cache_put_data'))
280
		require_once($sourcedir . '/Load.php');
281
	smf_seed_generator();
282
}
283
284
// This is needed in case someone invokes the upgrader using https when upgrading an http forum
285
if (httpsOn())
286
	$settings['default_theme_url'] = strtr($settings['default_theme_url'], array('http://' => 'https://'));
287
288
$upcontext['is_large_forum'] = (empty($modSettings['smfVersion']) || $modSettings['smfVersion'] <= '1.1 RC1') && !empty($modSettings['totalMessages']) && $modSettings['totalMessages'] > 75000;
289
290
// Have we got tracking data - if so use it (It will be clean!)
291
if (isset($_GET['data']))
292
{
293
	global $is_debug;
294
295
	$upcontext['upgrade_status'] = json_decode(base64_decode($_GET['data']), true);
296
	$upcontext['current_step'] = $upcontext['upgrade_status']['curstep'];
297
	$upcontext['language'] = $upcontext['upgrade_status']['lang'];
298
	$upcontext['rid'] = $upcontext['upgrade_status']['rid'];
299
	$support_js = $upcontext['upgrade_status']['js'];
300
301
	// Only set this if the upgrader status says so.
302
	if (empty($is_debug))
303
		$is_debug = $upcontext['upgrade_status']['debug'];
304
}
305
// Set the defaults.
306
else
307
{
308
	$upcontext['current_step'] = 0;
309
	$upcontext['rid'] = mt_rand(0, 5000);
310
	$upcontext['upgrade_status'] = array(
311
		'curstep' => 0,
312
		'lang' => isset($upcontext['lang']) ? $upcontext['lang'] : basename($language, '.lng'),
313
		'rid' => $upcontext['rid'],
314
		'pass' => 0,
315
		'debug' => 0,
316
		'js' => 0,
317
	);
318
	$upcontext['language'] = $upcontext['upgrade_status']['lang'];
319
}
320
321
// Now that we have the necessary info, make sure we loaded the right language file.
322
load_lang_file();
323
324
// Default title...
325
$upcontext['page_title'] = $txt['updating_smf_installation'];
326
327
// If this isn't the first stage see whether they are logging in and resuming.
328
if ($upcontext['current_step'] != 0 || !empty($upcontext['user']['step']))
329
	checkLogin();
330
331
if ($command_line)
332
	cmdStep0();
333
334
// Don't error if we're using xml.
335
if (isset($_GET['xml']))
336
	$upcontext['return_error'] = true;
337
338
// Loop through all the steps doing each one as required.
339
$upcontext['overall_percent'] = 0;
340
foreach ($upcontext['steps'] as $num => $step)
341
{
342
	if ($num >= $upcontext['current_step'])
343
	{
344
		// The current weight of this step in terms of overall progress.
345
		$upcontext['step_weight'] = $step[3];
346
		// Make sure we reset the skip button.
347
		$upcontext['skip'] = false;
348
349
		// We cannot proceed if we're not logged in.
350
		if ($num != 0 && !$disable_security && $upcontext['user']['pass'] != $upcontext['upgrade_status']['pass'])
351
		{
352
			$upcontext['steps'][0][2]();
353
			break;
354
		}
355
356
		// Call the step and if it returns false that means pause!
357
		if (function_exists($step[2]) && $step[2]() === false)
358
			break;
359
		elseif (function_exists($step[2]))
360
		{
361
			//Start each new step with this unset, so the 'normal' template is called first
362
			unset($_GET['xml']);
363
			//Clear out warnings at the start of each step
364
			unset($upcontext['custom_warning']);
365
			$_GET['substep'] = 0;
366
			$upcontext['current_step']++;
367
		}
368
	}
369
	$upcontext['overall_percent'] += $step[3];
370
}
371
372
upgradeExit();
373
374
// Exit the upgrade script.
375
function upgradeExit($fallThrough = false)
376
{
377
	global $upcontext, $upgradeurl, $sourcedir, $command_line, $is_debug, $txt;
378
379
	// Save where we are...
380
	if (!empty($upcontext['current_step']) && !empty($upcontext['user']['id']))
381
	{
382
		$upcontext['user']['step'] = $upcontext['current_step'];
383
		$upcontext['user']['substep'] = $_GET['substep'];
384
		$upcontext['user']['updated'] = time();
385
		$upcontext['user']['skip_db_substeps'] = !empty($upcontext['skip_db_substeps']);
386
		$upcontext['debug'] = $is_debug;
387
		$upgradeData = base64_encode(json_encode($upcontext['user']));
388
		require_once($sourcedir . '/Subs.php');
389
		require_once($sourcedir . '/Subs-Admin.php');
390
		updateSettingsFile(array('upgradeData' => $upgradeData));
391
		updateDbLastError(0);
392
	}
393
394
	// Handle the progress of the step, if any.
395
	if (!empty($upcontext['step_progress']) && isset($upcontext['steps'][$upcontext['current_step']]))
396
	{
397
		$upcontext['step_progress'] = round($upcontext['step_progress'], 1);
398
		$upcontext['overall_percent'] += $upcontext['step_progress'] * ($upcontext['steps'][$upcontext['current_step']][3] / 100);
399
	}
400
	$upcontext['overall_percent'] = (int) $upcontext['overall_percent'];
401
402
	// We usually dump our templates out.
403
	if (!$fallThrough)
404
	{
405
		// This should not happen my dear... HELP ME DEVELOPERS!!
406
		if (!empty($command_line))
407
		{
408
			if (function_exists('debug_print_backtrace'))
409
				debug_print_backtrace();
410
411
			printf("\n" . $txt['error_unexpected_template_call'], isset($upcontext['sub_template']) ? $upcontext['sub_template'] : '');
412
			flush();
413
			die();
0 ignored issues
show
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...
414
		}
415
416
		if (!isset($_GET['xml']))
417
			template_upgrade_above();
418
		else
419
		{
420
			header('content-type: text/xml; charset=UTF-8');
421
			// Sadly we need to retain the $_GET data thanks to the old upgrade scripts.
422
			$upcontext['get_data'] = array();
423
			foreach ($_GET as $k => $v)
424
			{
425
				if (substr($k, 0, 3) != 'amp' && !in_array($k, array('xml', 'substep', 'lang', 'data', 'step', 'filecount')))
426
				{
427
					$upcontext['get_data'][$k] = $v;
428
				}
429
			}
430
			template_xml_above();
431
		}
432
433
		// Call the template.
434
		if (isset($upcontext['sub_template']))
435
		{
436
			$upcontext['upgrade_status']['curstep'] = $upcontext['current_step'];
437
			$upcontext['form_url'] = $upgradeurl . '?step=' . $upcontext['current_step'] . '&amp;substep=' . $_GET['substep'] . '&amp;data=' . base64_encode(json_encode($upcontext['upgrade_status']));
438
439
			// Custom stuff to pass back?
440
			if (!empty($upcontext['query_string']))
441
				$upcontext['form_url'] .= $upcontext['query_string'];
442
443
			// Call the appropriate subtemplate
444
			if (is_callable('template_' . $upcontext['sub_template']))
445
				call_user_func('template_' . $upcontext['sub_template']);
446
			else
447
				die(sprintf($txt['error_invalid_template'], $upcontext['sub_template']));
0 ignored issues
show
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...
448
		}
449
450
		// Was there an error?
451
		if (!empty($upcontext['forced_error_message']))
452
			echo $upcontext['forced_error_message'];
453
454
		// Show the footer.
455
		if (!isset($_GET['xml']))
456
			template_upgrade_below();
457
		else
458
			template_xml_below();
459
	}
460
461
	// Show the upgrade time for CLI when we are completely done, if in debug mode.
462
	if (!empty($command_line) && $is_debug)
463
	{
464
		$active = time() - $upcontext['started'];
465
		$hours = floor($active / 3600);
466
		$minutes = intval(($active / 60) % 60);
467
		$seconds = intval($active % 60);
468
469
		if ($hours > 0)
470
			echo "\n" . '', sprintf($txt['upgrade_completed_time_hms'], $hours, $minutes, $seconds), '' . "\n";
471
		elseif ($minutes > 0)
472
			echo "\n" . '', sprintf($txt['upgrade_completed_time_ms'], $minutes, $seconds), '' . "\n";
473
		elseif ($seconds > 0)
474
			echo "\n" . '', sprintf($txt['upgrade_completed_time_s'], $seconds), '' . "\n";
475
	}
476
477
	// Bang - gone!
478
	die();
0 ignored issues
show
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...
479
}
480
481
// Load the list of language files, and the current language file.
482
function load_lang_file()
483
{
484
	global $txt, $upcontext, $language, $modSettings, $upgrade_path, $command_line;
485
486
	static $lang_dir = '', $detected_languages = array(), $loaded_langfile = '';
487
488
	// Do we know where to look for the language files, or shall we just guess for now?
489
	$temp = isset($modSettings['theme_dir']) ? $modSettings['theme_dir'] . '/languages' : $upgrade_path . '/Themes/default/languages';
490
491
	if ($lang_dir != $temp)
492
	{
493
		$lang_dir = $temp;
494
		$detected_languages = array();
495
	}
496
497
	// Override the language file?
498
	if (isset($upcontext['language']))
499
		$_SESSION['upgrader_langfile'] = 'Install.' . $upcontext['language'] . '.php';
500
	elseif (isset($upcontext['lang']))
501
		$_SESSION['upgrader_langfile'] = 'Install.' . $upcontext['lang'] . '.php';
502
	elseif (isset($language))
503
		$_SESSION['upgrader_langfile'] = 'Install.' . $language . '.php';
504
505
	// Avoid pointless repetition
506
	if (isset($_SESSION['upgrader_langfile']) && $loaded_langfile == $lang_dir . '/' . $_SESSION['upgrader_langfile'])
507
		return;
508
509
	// Now try to find the language files
510
	if (empty($detected_languages))
511
	{
512
		// Make sure the languages directory actually exists.
513
		if (file_exists($lang_dir))
514
		{
515
			// Find all the "Install" language files in the directory.
516
			$dir = dir($lang_dir);
517
			while ($entry = $dir->read())
518
			{
519
				// Skip any old '-utf8' language files that might be lying around
520
				if (strpos($entry, '-utf8') !== false)
521
					continue;
522
523
				if (substr($entry, 0, 8) == 'Install.' && substr($entry, -4) == '.php')
524
					$detected_languages[$entry] = ucfirst(substr($entry, 8, strlen($entry) - 12));
525
			}
526
			$dir->close();
527
		}
528
		// Our guess was wrong, but that's fine. We'll try again after $modSettings['theme_dir'] is defined.
529
		elseif (!isset($modSettings['theme_dir']))
530
		{
531
			// Define a few essential strings for now.
532
			$txt['error_db_connect_settings'] = 'Cannot connect to the database server.<br><br>Please check that the database info variables are correct in Settings.php.';
533
			$txt['error_sourcefile_missing'] = 'Unable to find the Sources/%1$s file. Please make sure it was uploaded properly, and then try again.';
534
535
			$txt['warning_lang_old'] = 'The language files for your selected language, %1$s, have not been updated to the latest version. Upgrade will continue with the forum default, %2$s.';
536
			$txt['warning_lang_missing'] = 'The upgrader could not find the &quot;Install&quot; language file for your selected language, %1$s. Upgrade will continue with the forum default, %2$s.';
537
538
			return;
539
		}
540
	}
541
542
	// Didn't find any, show an error message!
543
	if (empty($detected_languages))
544
	{
545
		$from = explode('/', $command_line ? $upgrade_path : $_SERVER['PHP_SELF']);
546
		$to = explode('/', $lang_dir);
547
		$relPath = $to;
548
549
		foreach($from as $depth => $dir)
550
		{
551
			if ($dir === $to[$depth])
552
				array_shift($relPath);
553
			else
554
			{
555
				$remaining = count($from) - $depth;
556
				if ($remaining > 1)
557
				{
558
					$padLength = (count($relPath) + $remaining - 1) * -1;
559
					$relPath = array_pad($relPath, $padLength, '..');
560
					break;
561
				}
562
				else
563
					$relPath[0] = './' . $relPath[0];
564
			}
565
		}
566
		$relPath = implode(DIRECTORY_SEPARATOR, $relPath);
567
568
		// Command line?
569
		if ($command_line)
570
		{
571
			echo 'This upgrader was unable to find the upgrader\'s language file or files.  They should be found under:', "\n",
572
				$relPath, "\n",
573
				'In some cases, FTP clients do not properly upload files with this many folders. Please double check to make sure you have uploaded all the files in the distribution', "\n",
574
				'If that doesn\'t help, please make sure this upgrade.php file is in the same place as the Themes folder.', "\n";
575
			die;
0 ignored issues
show
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...
576
		}
577
578
		// Let's not cache this message, eh?
579
		header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
580
		header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
581
		header('Cache-Control: no-cache');
582
583
		echo '<!DOCTYPE html>
584
			<html>
585
				<head>
586
					<title>SMF Upgrader: Error!</title>
587
						<style>
588
							body {
589
								font-family: sans-serif;
590
								max-width: 700px; }
591
592
								h1 {
593
									font-size: 14pt; }
594
595
								.directory {
596
									margin: 0.3em;
597
									font-family: monospace;
598
									font-weight: bold; }
599
						</style>
600
				</head>
601
				<body>
602
					<h1>A critical error has occurred.</h1>
603
						<p>This upgrader was unable to find the upgrader\'s language file or files.  They should be found under:</p>
604
						<div class="directory">', $relPath, '</div>
605
						<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>
606
						<p>If that doesn\'t help, please make sure this upgrade.php file is in the same place as the Themes folder.</p>
607
						<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>
608
				</body>
609
			</html>';
610
		die;
0 ignored issues
show
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...
611
	}
612
613
	// Make sure it exists. If it doesn't, reset it.
614
	if (!isset($_SESSION['upgrader_langfile']) || preg_match('~[^\w.-]~', $_SESSION['upgrader_langfile']) === 1 || !file_exists($lang_dir . '/' . $_SESSION['upgrader_langfile']))
615
	{
616
		// Use the first one...
617
		list ($_SESSION['upgrader_langfile']) = array_keys($detected_languages);
618
619
		// If we have English and some other language, use the other language.
620
		if ($_SESSION['upgrader_langfile'] == 'Install.english.php' && count($detected_languages) > 1)
621
			list (, $_SESSION['upgrader_langfile']) = array_keys($detected_languages);
622
	}
623
624
	// For backup we load English at first, then the second language will overwrite it.
625
	if ($_SESSION['upgrader_langfile'] != 'Install.english.php')
626
	{
627
		require_once($lang_dir . '/index.english.php');
628
		require_once($lang_dir . '/Install.english.php');
629
	}
630
631
	// And now include the actual language file itself.
632
	require_once($lang_dir . '/' . str_replace('Install.', 'index.', $_SESSION['upgrader_langfile']));
633
	require_once($lang_dir . '/' . $_SESSION['upgrader_langfile']);
634
635
	// Remember what we've done
636
	$loaded_langfile = $lang_dir . '/' . $_SESSION['upgrader_langfile'];
637
}
638
639
// Used to direct the user to another location.
640
function redirectLocation($location, $addForm = true)
641
{
642
	global $upgradeurl, $upcontext, $command_line;
643
644
	// Command line users can't be redirected.
645
	if ($command_line)
646
		upgradeExit(true);
647
648
	// Are we providing the core info?
649
	if ($addForm)
650
	{
651
		$upcontext['upgrade_status']['curstep'] = $upcontext['current_step'];
652
		$location = $upgradeurl . '?step=' . $upcontext['current_step'] . '&substep=' . $_GET['substep'] . '&data=' . base64_encode(json_encode($upcontext['upgrade_status'])) . $location;
653
	}
654
655
	while (@ob_end_clean())
656
		header('location: ' . strtr($location, array('&amp;' => '&')));
657
658
	// Exit - saving status as we go.
659
	upgradeExit(true);
660
}
661
662
// Load all essential data and connect to the DB as this is pre SSI.php
663
function loadEssentialData()
664
{
665
	global $db_server, $db_user, $db_passwd, $db_name, $db_connection;
666
	global $db_prefix, $db_character_set, $db_type, $db_port, $db_show_debug;
667
	global $db_mb4, $modSettings, $sourcedir, $smcFunc, $txt, $utf8;
668
669
	// Report all errors if admin wants them or this is a pre-release version.
670
	if (!empty($db_show_debug) || strspn(SMF_VERSION, '1234567890.') !== strlen(SMF_VERSION))
671
		error_reporting(E_ALL);
672
	// Otherwise, report all errors except for deprecation notices.
673
	else
674
		error_reporting(E_ALL & ~E_DEPRECATED);
675
676
	define('SMF', 1);
677
	header('X-Frame-Options: SAMEORIGIN');
678
	header('X-XSS-Protection: 1');
679
	header('X-Content-Type-Options: nosniff');
680
681
	// Start the session.
682
	if (@ini_get('session.save_handler') == 'user')
683
		@ini_set('session.save_handler', 'files');
684
	@session_start();
685
686
	if (empty($smcFunc))
687
		$smcFunc = array();
688
689
	require_once($sourcedir . '/Subs.php');
690
691
	if (version_compare(PHP_VERSION, '8.0.0', '>='))
692
		require_once($sourcedir . '/Subs-Compat.php');
693
694
	@set_time_limit(600);
695
696
	$smcFunc['random_int'] = function($min = 0, $max = PHP_INT_MAX)
697
	{
698
		global $sourcedir;
699
700
		// Oh, wouldn't it be great if I *was* crazy? Then the world would be okay.
701
		if (!is_callable('random_int'))
702
			require_once($sourcedir . '/random_compat/random.php');
703
704
		return random_int($min, $max);
705
	};
706
707
	// This is now needed for loadUserSettings()
708
	$smcFunc['random_bytes'] = function($bytes)
709
	{
710
		global $sourcedir;
711
712
		if (!is_callable('random_bytes'))
713
			require_once($sourcedir . '/random_compat/random.php');
714
715
		return random_bytes($bytes);
716
	};
717
718
	// We need this for authentication and some upgrade code
719
	require_once($sourcedir . '/Subs-Auth.php');
720
	require_once($sourcedir . '/Class-Package.php');
721
722
	$smcFunc['strtolower'] = 'smf_strtolower';
723
724
	// Initialize everything...
725
	initialize_inputs();
726
727
	$utf8 = (empty($modSettings['global_character_set']) ? $txt['lang_character_set'] : $modSettings['global_character_set']) === 'UTF-8';
728
729
	$smcFunc['normalize'] = function($string, $form = 'c') use ($utf8)
730
	{
731
		global $sourcedir;
732
733
		if (!$utf8)
734
			return $string;
735
736
		require_once($sourcedir . '/Subs-Charset.php');
737
738
		$normalize_func = 'utf8_normalize_' . strtolower((string) $form);
739
740
		if (!function_exists($normalize_func))
741
			return false;
742
743
		return $normalize_func($string);
744
	};
745
746
	// Get the database going!
747
	if (empty($db_type) || $db_type == 'mysqli')
748
	{
749
		$db_type = 'mysql';
750
		// If overriding $db_type, need to set its settings.php entry too
751
		$changes = array();
752
		$changes['db_type'] = 'mysql';
753
		require_once($sourcedir . '/Subs-Admin.php');
754
		updateSettingsFile($changes);
755
	}
756
757
	if (file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php'))
758
	{
759
		require_once($sourcedir . '/Subs-Db-' . $db_type . '.php');
760
761
		// Make the connection...
762
		if (empty($db_connection))
763
		{
764
			$options = array('non_fatal' => true);
765
			// Add in the port if needed
766
			if (!empty($db_port))
767
				$options['port'] = $db_port;
768
769
			if (!empty($db_mb4))
770
				$options['db_mb4'] = $db_mb4;
771
772
			$db_connection = smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, $db_prefix, $options);
773
		}
774
		else
775
			// If we've returned here, ping/reconnect to be safe
776
			$smcFunc['db_ping']($db_connection);
777
778
		// Oh dear god!!
779
		if ($db_connection === null)
780
		{
781
			// Get error info...  Recast just in case we get false or 0...
782
			$error_message = $smcFunc['db_connect_error']();
783
			if (empty($error_message))
784
				$error_message = '';
785
			$error_number = $smcFunc['db_connect_errno']();
786
			if (empty($error_number))
787
				$error_number = '';
788
			$db_error = (!empty($error_number) ? $error_number . ': ' : '') . $error_message;
789
790
			die($txt['error_db_connect_settings'] . '<br><br>' . $db_error);
0 ignored issues
show
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...
791
		}
792
793
		if ($db_type == 'mysql' && isset($db_character_set) && preg_match('~^\w+$~', $db_character_set) === 1)
794
			$smcFunc['db_query']('', '
795
				SET NAMES {string:db_character_set}',
796
				array(
797
					'db_error_skip' => true,
798
					'db_character_set' => $db_character_set,
799
				)
800
			);
801
802
		// Load the modSettings data...
803
		$request = $smcFunc['db_query']('', '
804
			SELECT variable, value
805
			FROM {db_prefix}settings',
806
			array(
807
				'db_error_skip' => true,
808
			)
809
		);
810
		$modSettings = array();
811
		while ($row = $smcFunc['db_fetch_assoc']($request))
812
			$modSettings[$row['variable']] = $row['value'];
813
		$smcFunc['db_free_result']($request);
814
	}
815
	else
816
		return throw_error(sprintf($txt['error_sourcefile_missing'], 'Subs-Db-' . $db_type . '.php'));
0 ignored issues
show
The function throw_error was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

816
		return /** @scrutinizer ignore-call */ throw_error(sprintf($txt['error_sourcefile_missing'], 'Subs-Db-' . $db_type . '.php'));
Loading history...
817
818
	// If they don't have the file, they're going to get a warning anyway so we won't need to clean request vars.
819
	if (file_exists($sourcedir . '/QueryString.php') && php_version_check())
820
	{
821
		require_once($sourcedir . '/QueryString.php');
822
		cleanRequest();
823
	}
824
825
	if (!isset($_GET['substep']))
826
		$_GET['substep'] = 0;
827
}
828
829
function initialize_inputs()
830
{
831
	global $start_time, $db_type, $upgrade_path;
832
833
	$start_time = time();
834
835
	umask(0);
836
837
	ob_start();
838
839
	// Better to upgrade cleanly and fall apart than to screw everything up if things take too long.
840
	ignore_user_abort(true);
841
842
	// This is really quite simple; if ?delete is on the URL, delete the upgrader...
843
	if (isset($_GET['delete']))
844
	{
845
		deleteFile(__FILE__);
0 ignored issues
show
The function deleteFile was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

845
		/** @scrutinizer ignore-call */ 
846
  deleteFile(__FILE__);
Loading history...
846
847
		// And the extra little files ;).
848
		deleteFile(dirname(__FILE__) . '/upgrade_1-0.sql');
849
		deleteFile(dirname(__FILE__) . '/upgrade_1-1.sql');
850
		deleteFile(dirname(__FILE__) . '/upgrade_2-0_' . $db_type . '.sql');
851
		deleteFile(dirname(__FILE__) . '/upgrade_2-1_' . $db_type . '.sql');
852
		deleteFile(dirname(__FILE__) . '/upgrade-helper.php');
853
854
		$dh = opendir(dirname(__FILE__));
855
		while ($file = readdir($dh))
856
		{
857
			if (preg_match('~upgrade_\d-\d_([A-Za-z])+\.sql~i', $file, $matches) && isset($matches[1]))
858
				deleteFile(dirname(__FILE__) . '/' . $file);
859
		}
860
		closedir($dh);
861
862
		// Legacy files while we're at it. NOTE: We only touch files we KNOW shouldn't be there.
863
		// 1.1 Sources files not in 2.0+
864
		deleteFile($upgrade_path . '/Sources/ModSettings.php');
865
		// 1.1 Templates that don't exist any more (e.g. renamed)
866
		deleteFile($upgrade_path . '/Themes/default/Combat.template.php');
867
		deleteFile($upgrade_path . '/Themes/default/Modlog.template.php');
868
		// 1.1 JS files were stored in the main theme folder, but in 2.0+ are in the scripts/ folder
869
		deleteFile($upgrade_path . '/Themes/default/fader.js');
870
		deleteFile($upgrade_path . '/Themes/default/script.js');
871
		deleteFile($upgrade_path . '/Themes/default/spellcheck.js');
872
		deleteFile($upgrade_path . '/Themes/default/xml_board.js');
873
		deleteFile($upgrade_path . '/Themes/default/xml_topic.js');
874
875
		// 2.0 Sources files not in 2.1+
876
		deleteFile($upgrade_path . '/Sources/DumpDatabase.php');
877
		deleteFile($upgrade_path . '/Sources/LockTopic.php');
878
879
		header('location: http' . (httpsOn() ? 's' : '') . '://' . (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : $_SERVER['SERVER_NAME'] . ':' . $_SERVER['SERVER_PORT']) . dirname($_SERVER['PHP_SELF']) . '/Themes/default/images/blank.png');
880
		exit;
881
	}
882
883
	// Something is causing this to happen, and it's annoying.  Stop it.
884
	$temp = 'upgrade_php?step';
885
	while (strlen($temp) > 4)
886
	{
887
		if (isset($_GET[$temp]))
888
			unset($_GET[$temp]);
889
		$temp = substr($temp, 1);
890
	}
891
892
	// Force a step, defaulting to 0.
893
	$_GET['step'] = (int) @$_GET['step'];
894
	$_GET['substep'] = (int) @$_GET['substep'];
895
}
896
897
// Step 0 - Let's welcome them in and ask them to login!
898
function WelcomeLogin()
899
{
900
	global $boarddir, $sourcedir, $modSettings, $cachedir, $upgradeurl, $upcontext;
901
	global $smcFunc, $db_type, $databases, $boardurl, $upgrade_path;
902
903
	// We global $txt here so that the language files can add to them. This variable is NOT unused.
904
	global $txt;
905
906
	$upcontext['sub_template'] = 'welcome_message';
907
908
	// Check for some key files - one template, one language, and a new and an old source file.
909
	$check = @file_exists($modSettings['theme_dir'] . '/index.template.php')
910
		&& @file_exists($sourcedir . '/QueryString.php')
911
		&& @file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php')
912
		&& @file_exists(dirname(__FILE__) . '/upgrade_2-1_' . $db_type . '.sql');
913
914
	// Need legacy scripts?
915
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 2.1)
916
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_2-0_' . $db_type . '.sql');
917
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 2.0)
918
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_1-1.sql');
919
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 1.1)
920
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_1-0.sql');
921
922
	// We don't need "-utf8" files anymore...
923
	$upcontext['language'] = str_ireplace('-utf8', '', $upcontext['language']);
924
925
	if (!$check)
926
		// Don't tell them what files exactly because it's a spot check - just like teachers don't tell which problems they are spot checking, that's dumb.
927
		return throw_error($txt['error_upgrade_files_missing']);
0 ignored issues
show
The function throw_error was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

927
		return /** @scrutinizer ignore-call */ throw_error($txt['error_upgrade_files_missing']);
Loading history...
928
929
	// Do they meet the install requirements?
930
	if (!php_version_check())
931
		return throw_error($txt['error_php_too_low']);
932
933
	if (!db_version_check())
934
		return throw_error(sprintf($txt['error_db_too_low'], $databases[$db_type]['name']));
935
936
	// Do some checks to make sure they have proper privileges
937
	db_extend('packages');
938
939
	// CREATE
940
	$create = $smcFunc['db_create_table']('{db_prefix}priv_check', array(array('name' => 'id_test', 'type' => 'int', 'size' => 10, 'unsigned' => true, 'auto' => true)), array(array('columns' => array('id_test'), 'type' => 'primary')), array(), 'overwrite');
941
942
	// ALTER
943
	$alter = $smcFunc['db_add_column']('{db_prefix}priv_check', array('name' => 'txt', 'type' => 'varchar', 'size' => 4, 'null' => false, 'default' => ''));
944
945
	// DROP
946
	$drop = $smcFunc['db_drop_table']('{db_prefix}priv_check');
947
948
	// Sorry... we need CREATE, ALTER and DROP
949
	if (!$create || !$alter || !$drop)
950
		return throw_error(sprintf($txt['error_db_privileges'], $databases[$db_type]['name']));
951
952
	// Do a quick version spot check.
953
	$temp = substr(@implode('', @file($boarddir . '/index.php')), 0, 4096);
0 ignored issues
show
It seems like @file($boarddir . '/index.php') 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

953
	$temp = substr(@implode('', /** @scrutinizer ignore-type */ @file($boarddir . '/index.php')), 0, 4096);
Loading history...
954
	preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $temp, $match);
955
	if (empty($match[1]) || (trim($match[1]) != SMF_VERSION))
956
		return throw_error($txt['error_upgrade_old_files']);
957
958
	// What absolutely needs to be writable?
959
	$writable_files = array(
960
		$boarddir . '/Settings.php',
961
		$boarddir . '/Settings_bak.php',
962
	);
963
964
	// Only check for minified writable files if we have it enabled or not set.
965
	if (!empty($modSettings['minimize_files']) || !isset($modSettings['minimize_files']))
966
		$writable_files += array(
967
			$modSettings['theme_dir'] . '/css/minified.css',
968
			$modSettings['theme_dir'] . '/scripts/minified.js',
969
			$modSettings['theme_dir'] . '/scripts/minified_deferred.js',
970
		);
971
972
	// Do we need to add this setting?
973
	$need_settings_update = empty($modSettings['custom_avatar_dir']);
974
975
	$custom_av_dir = !empty($modSettings['custom_avatar_dir']) ? $modSettings['custom_avatar_dir'] : $GLOBALS['boarddir'] . '/custom_avatar';
976
	$custom_av_url = !empty($modSettings['custom_avatar_url']) ? $modSettings['custom_avatar_url'] : $boardurl . '/custom_avatar';
977
978
	// This little fellow has to cooperate...
979
	quickFileWritable($custom_av_dir);
0 ignored issues
show
The function quickFileWritable was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

979
	/** @scrutinizer ignore-call */ 
980
 quickFileWritable($custom_av_dir);
Loading history...
980
981
	// Are we good now?
982
	if (!is_writable($custom_av_dir))
983
		return throw_error(sprintf($txt['error_dir_not_writable'], $custom_av_dir));
984
	elseif ($need_settings_update)
985
	{
986
		if (!function_exists('cache_put_data'))
987
			require_once($sourcedir . '/Load.php');
988
989
		updateSettings(array('custom_avatar_dir' => $custom_av_dir));
990
		updateSettings(array('custom_avatar_url' => $custom_av_url));
991
	}
992
993
	require_once($sourcedir . '/Security.php');
994
995
	// Check the cache directory.
996
	$cachedir_temp = empty($cachedir) ? $boarddir . '/cache' : $cachedir;
997
	if (!file_exists($cachedir_temp))
998
		@mkdir($cachedir_temp);
999
1000
	if (!file_exists($cachedir_temp))
1001
		return throw_error($txt['error_cache_not_found']);
1002
1003
	quickFileWritable($cachedir_temp . '/db_last_error.php');
1004
1005
	if (!file_exists($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php'))
1006
		return throw_error(sprintf($txt['error_lang_index_missing'], $upcontext['language'], $upgradeurl));
1007
	elseif (!isset($_GET['skiplang']))
1008
	{
1009
		$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php')), 0, 4096);
1010
		preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
1011
1012
		if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
1013
			return throw_error(sprintf($txt['error_upgrade_old_lang_files'], $upcontext['language'], $upgradeurl));
1014
	}
1015
1016
	if (!makeFilesWritable($writable_files))
0 ignored issues
show
The function makeFilesWritable was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

1016
	if (!/** @scrutinizer ignore-call */ makeFilesWritable($writable_files))
Loading history...
1017
		return false;
1018
1019
	// Check agreement.txt. (it may not exist, in which case $boarddir must be writable.)
1020
	if (isset($modSettings['agreement']) && (!is_writable($boarddir) || file_exists($boarddir . '/agreement.txt')) && !is_writable($boarddir . '/agreement.txt'))
1021
		return throw_error($txt['error_agreement_not_writable']);
1022
1023
	// Upgrade the agreement.
1024
	elseif (isset($modSettings['agreement']))
1025
	{
1026
		$fp = fopen($boarddir . '/agreement.txt', 'w');
1027
		fwrite($fp, $modSettings['agreement']);
1028
		fclose($fp);
1029
	}
1030
1031
	// We're going to check that their board dir setting is right in case they've been moving stuff around.
1032
	if (strtr($boarddir, array('/' => '', '\\' => '')) != strtr($upgrade_path, array('/' => '', '\\' => '')))
1033
		$upcontext['warning'] = '
1034
			' . sprintf($txt['upgrade_forumdir_settings'], $boarddir, $upgrade_path) . '<br>
1035
			<ul>
1036
				<li>' . $txt['upgrade_forumdir'] . '  ' . $boarddir . '</li>
1037
				<li>' . $txt['upgrade_sourcedir'] . '  ' . $boarddir . '</li>
1038
				<li>' . $txt['upgrade_cachedir'] . '  ' . $cachedir_temp . '</li>
1039
			</ul>
1040
			' . $txt['upgrade_incorrect_settings'] . '';
1041
1042
	// Confirm mbstring is loaded...
1043
	if (!extension_loaded('mbstring'))
1044
		return throw_error($txt['install_no_mbstring']);
1045
1046
	// Confirm fileinfo is loaded...
1047
	if (!extension_loaded('fileinfo'))
1048
		return throw_error($txt['install_no_fileinfo']);
1049
1050
	// Check for https stream support.
1051
	$supported_streams = stream_get_wrappers();
1052
	if (!in_array('https', $supported_streams))
1053
		$upcontext['custom_warning'] = $txt['install_no_https'];
1054
1055
	// Either we're logged in or we're going to present the login.
1056
	if (checkLogin())
1057
		return true;
1058
1059
	$upcontext += createToken('login');
1060
1061
	return false;
1062
}
1063
1064
// Step 0.5: Does the login work?
1065
function checkLogin()
1066
{
1067
	global $modSettings, $upcontext, $disable_security;
1068
	global $smcFunc, $db_type, $support_js, $sourcedir, $txt;
1069
1070
	// Are we trying to login?
1071
	if (isset($_POST['contbutt']) && (!empty($_POST['user']) || $disable_security))
1072
	{
1073
		// If we've disabled security pick a suitable name!
1074
		if (empty($_POST['user']))
1075
			$_POST['user'] = 'Administrator';
1076
1077
		// Before 2.0 these column names were different!
1078
		$oldDB = false;
1079
		if (empty($db_type) || $db_type == 'mysql')
1080
		{
1081
			$request = $smcFunc['db_query']('', '
1082
				SHOW COLUMNS
1083
				FROM {db_prefix}members
1084
				LIKE {string:member_name}',
1085
				array(
1086
					'member_name' => 'memberName',
1087
					'db_error_skip' => true,
1088
				)
1089
			);
1090
			if ($smcFunc['db_num_rows']($request) != 0)
1091
				$oldDB = true;
1092
			$smcFunc['db_free_result']($request);
1093
		}
1094
1095
		// Get what we believe to be their details.
1096
		if (!$disable_security)
1097
		{
1098
			if ($oldDB)
1099
				$request = $smcFunc['db_query']('', '
1100
					SELECT id_member, memberName AS member_name, passwd, id_group,
1101
						additionalGroups AS additional_groups, lngfile
1102
					FROM {db_prefix}members
1103
					WHERE memberName = {string:member_name}',
1104
					array(
1105
						'member_name' => $_POST['user'],
1106
						'db_error_skip' => true,
1107
					)
1108
				);
1109
			else
1110
				$request = $smcFunc['db_query']('', '
1111
					SELECT id_member, member_name, passwd, id_group, additional_groups, lngfile
1112
					FROM {db_prefix}members
1113
					WHERE member_name = {string:member_name}',
1114
					array(
1115
						'member_name' => $_POST['user'],
1116
						'db_error_skip' => true,
1117
					)
1118
				);
1119
			if ($smcFunc['db_num_rows']($request) != 0)
1120
			{
1121
				list ($id_member, $name, $password, $id_group, $addGroups, $user_language) = $smcFunc['db_fetch_row']($request);
1122
1123
				$groups = explode(',', $addGroups);
1124
				$groups[] = $id_group;
1125
1126
				foreach ($groups as $k => $v)
1127
					$groups[$k] = (int) $v;
1128
1129
				$sha_passwd = sha1(strtolower($name) . $_REQUEST['passwrd']);
1130
1131
				// We don't use "-utf8" anymore...
1132
				$user_language = str_ireplace('-utf8', '', $user_language);
1133
			}
1134
			else
1135
				$upcontext['username_incorrect'] = true;
1136
1137
			$smcFunc['db_free_result']($request);
1138
		}
1139
		$upcontext['username'] = $_POST['user'];
1140
1141
		// Track whether javascript works!
1142
		if (isset($_POST['js_works']))
1143
		{
1144
			if (!empty($_POST['js_works']))
1145
			{
1146
				$upcontext['upgrade_status']['js'] = 1;
1147
				$support_js = 1;
1148
			}
1149
			else
1150
				$support_js = 0;
1151
		}
1152
1153
		// Note down the version we are coming from.
1154
		if (!empty($modSettings['smfVersion']) && empty($upcontext['user']['version']))
1155
			$upcontext['user']['version'] = $modSettings['smfVersion'];
1156
1157
		// Didn't get anywhere?
1158
		if (!$disable_security && (empty($sha_passwd) || (!empty($password) ? $password : '') != $sha_passwd) && !hash_verify_password((!empty($name) ? $name : ''), $_REQUEST['passwrd'], (!empty($password) ? $password : '')) && empty($upcontext['username_incorrect']))
1159
		{
1160
			// MD5?
1161
			$md5pass = md5_hmac($_REQUEST['passwrd'], strtolower($_POST['user']));
1162
			if ($md5pass != $password)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $password does not seem to be defined for all execution paths leading up to this point.
Loading history...
1163
			{
1164
				$upcontext['password_failed'] = true;
1165
				// Disable the hashing this time.
1166
				$upcontext['disable_login_hashing'] = true;
1167
			}
1168
		}
1169
1170
		if ((empty($upcontext['password_failed']) && !empty($name)) || $disable_security)
1171
		{
1172
			// Set the password.
1173
			if (!$disable_security)
1174
			{
1175
				// Do we actually have permission?
1176
				if (!in_array(1, $groups))
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $groups does not seem to be defined for all execution paths leading up to this point.
Loading history...
1177
				{
1178
					$request = $smcFunc['db_query']('', '
1179
						SELECT permission
1180
						FROM {db_prefix}permissions
1181
						WHERE id_group IN ({array_int:groups})
1182
							AND permission = {string:admin_forum}',
1183
						array(
1184
							'groups' => $groups,
1185
							'admin_forum' => 'admin_forum',
1186
							'db_error_skip' => true,
1187
						)
1188
					);
1189
					if ($smcFunc['db_num_rows']($request) == 0)
1190
						return throw_error($txt['error_not_admin']);
0 ignored issues
show
The function throw_error was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

1190
						return /** @scrutinizer ignore-call */ throw_error($txt['error_not_admin']);
Loading history...
1191
					$smcFunc['db_free_result']($request);
1192
				}
1193
1194
				$upcontext['user']['id'] = $id_member;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $id_member does not seem to be defined for all execution paths leading up to this point.
Loading history...
1195
				$upcontext['user']['name'] = $name;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $name does not seem to be defined for all execution paths leading up to this point.
Loading history...
1196
			}
1197
			else
1198
			{
1199
				$upcontext['user']['id'] = 1;
1200
				$upcontext['user']['name'] = 'Administrator';
1201
			}
1202
1203
			if (!is_callable('random_int'))
1204
				require_once('Sources/random_compat/random.php');
1205
1206
			$upcontext['user']['pass'] = random_int(0, 60000);
1207
			// This basically is used to match the GET variables to Settings.php.
1208
			$upcontext['upgrade_status']['pass'] = $upcontext['user']['pass'];
1209
1210
			// Set the language to that of the user?
1211
			if (isset($user_language) && $user_language != $upcontext['language'] && file_exists($modSettings['theme_dir'] . '/languages/index.' . basename($user_language, '.lng') . '.php'))
1212
			{
1213
				$user_language = basename($user_language, '.lng');
1214
				$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $user_language . '.php')), 0, 4096);
0 ignored issues
show
It seems like @file($modSettings['them...user_language . '.php') 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

1214
				$temp = substr(@implode('', /** @scrutinizer ignore-type */ @file($modSettings['theme_dir'] . '/languages/index.' . $user_language . '.php')), 0, 4096);
Loading history...
1215
				preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
1216
1217
				if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
1218
					$upcontext['upgrade_options_warning'] = sprintf($txt['warning_lang_old'], $user_language, $upcontext['language']);
1219
				elseif (!file_exists($modSettings['theme_dir'] . '/languages/Install.' . $user_language . '.php'))
1220
					$upcontext['upgrade_options_warning'] = sprintf($txt['warning_lang_missing'], $user_language, $upcontext['language']);
1221
				else
1222
				{
1223
					// Set this as the new language.
1224
					$upcontext['language'] = $user_language;
1225
					$upcontext['upgrade_status']['lang'] = $upcontext['language'];
1226
1227
					// Include the file.
1228
					load_lang_file();
1229
				}
1230
			}
1231
1232
			// If we're resuming set the step and substep to be correct.
1233
			if (isset($_POST['cont']))
1234
			{
1235
				$upcontext['current_step'] = $upcontext['user']['step'];
1236
				$_GET['substep'] = $upcontext['user']['substep'];
1237
			}
1238
1239
			return true;
1240
		}
1241
	}
1242
1243
	return false;
1244
}
1245
1246
// Step 1: Do the maintenance and backup.
1247
function UpgradeOptions()
1248
{
1249
	global $db_prefix, $command_line, $modSettings, $is_debug, $smcFunc, $packagesdir, $tasksdir, $language, $txt, $db_port;
1250
	global $boarddir, $boardurl, $sourcedir, $maintenance, $cachedir, $upcontext, $db_type, $db_server, $image_proxy_enabled;
1251
	global $auth_secret;
1252
1253
	$upcontext['sub_template'] = 'upgrade_options';
1254
	$upcontext['page_title'] = $txt['upgrade_options'];
1255
1256
	db_extend('packages');
1257
	$upcontext['karma_installed'] = array('good' => false, 'bad' => false);
1258
	$member_columns = $smcFunc['db_list_columns']('{db_prefix}members');
1259
1260
	$upcontext['karma_installed']['good'] = in_array('karma_good', $member_columns);
1261
	$upcontext['karma_installed']['bad'] = in_array('karma_bad', $member_columns);
1262
1263
	$upcontext['migrate_settings_recommended'] = empty($modSettings['smfVersion']) || version_compare(strtolower($modSettings['smfVersion']), substr(SMF_VERSION, 0, strpos(SMF_VERSION, '.') + 1 + strspn(SMF_VERSION, '1234567890', strpos(SMF_VERSION, '.') + 1)) . ' foo', '<');
1264
1265
	unset($member_columns);
1266
1267
	// If we've not submitted then we're done.
1268
	if (empty($_POST['upcont']))
1269
		return false;
1270
1271
	// We cannot execute this step in strict mode - strict mode data fixes are not applied yet
1272
	setSqlMode(false);
1273
1274
	// Firstly, if they're enabling SM stat collection just do it.
1275
	if (!empty($_POST['stats']) && substr($boardurl, 0, 16) != 'http://localhost' && empty($modSettings['allow_sm_stats']) && empty($modSettings['enable_sm_stats']))
1276
	{
1277
		$upcontext['allow_sm_stats'] = true;
1278
1279
		// Don't register if we still have a key.
1280
		if (empty($modSettings['sm_stats_key']))
1281
		{
1282
			// Attempt to register the site etc.
1283
			$fp = @fsockopen('www.simplemachines.org', 443, $errno, $errstr);
1284
			if (!$fp)
0 ignored issues
show
$fp is of type false|resource, thus it always evaluated to false.
Loading history...
1285
				$fp = @fsockopen('www.simplemachines.org', 80, $errno, $errstr);
1286
			if ($fp)
0 ignored issues
show
$fp is of type false|resource, thus it always evaluated to false.
Loading history...
1287
			{
1288
				$out = 'GET /smf/stats/register_stats.php?site=' . base64_encode($boardurl) . ' HTTP/1.1' . "\r\n";
1289
				$out .= 'Host: www.simplemachines.org' . "\r\n";
1290
				$out .= 'Connection: Close' . "\r\n\r\n";
1291
				fwrite($fp, $out);
1292
1293
				$return_data = '';
1294
				while (!feof($fp))
1295
					$return_data .= fgets($fp, 128);
1296
1297
				fclose($fp);
1298
1299
				// Get the unique site ID.
1300
				preg_match('~SITE-ID:\s(\w{10})~', $return_data, $ID);
1301
1302
				if (!empty($ID[1]))
1303
					$smcFunc['db_insert']('replace',
1304
						$db_prefix . 'settings',
1305
						array('variable' => 'string', 'value' => 'string'),
1306
						array(
1307
							array('sm_stats_key', $ID[1]),
1308
							array('enable_sm_stats', 1),
1309
						),
1310
						array('variable')
1311
					);
1312
			}
1313
		}
1314
		else
1315
		{
1316
			$smcFunc['db_insert']('replace',
1317
				$db_prefix . 'settings',
1318
				array('variable' => 'string', 'value' => 'string'),
1319
				array('enable_sm_stats', 1),
1320
				array('variable')
1321
			);
1322
		}
1323
	}
1324
	// Don't remove stat collection unless we unchecked the box for real, not from the loop.
1325
	elseif (empty($_POST['stats']) && empty($upcontext['allow_sm_stats']))
1326
		$smcFunc['db_query']('', '
1327
			DELETE FROM {db_prefix}settings
1328
			WHERE variable = {string:enable_sm_stats}',
1329
			array(
1330
				'enable_sm_stats' => 'enable_sm_stats',
1331
				'db_error_skip' => true,
1332
			)
1333
		);
1334
1335
	// Deleting old karma stuff?
1336
	$_SESSION['delete_karma'] = !empty($_POST['delete_karma']);
1337
1338
	// Emptying the error log?
1339
	$_SESSION['empty_error'] = !empty($_POST['empty_error']);
1340
1341
	$changes = array();
1342
1343
	// Add proxy settings.
1344
	if (!isset($GLOBALS['image_proxy_secret']) || $GLOBALS['image_proxy_secret'] == 'smfisawesome')
1345
		$changes['image_proxy_secret'] = substr(sha1(mt_rand()), 0, 20);
1346
	if (!isset($GLOBALS['image_proxy_maxsize']))
1347
		$changes['image_proxy_maxsize'] = 5190;
1348
	if (!isset($GLOBALS['image_proxy_enabled']))
1349
		$changes['image_proxy_enabled'] = false;
1350
1351
	// If $boardurl reflects https, set force_ssl
1352
	if (!function_exists('cache_put_data'))
1353
		require_once($sourcedir . '/Load.php');
1354
	if (stripos($boardurl, 'https://') !== false && !isset($modSettings['force_ssl']))
1355
		updateSettings(array('force_ssl' => '1'));
1356
1357
	// If we're overriding the language follow it through.
1358
	if (isset($upcontext['lang']) && file_exists($modSettings['theme_dir'] . '/languages/index.' . $upcontext['lang'] . '.php'))
1359
		$changes['language'] = $upcontext['lang'];
1360
1361
	if (!empty($_POST['maint']))
1362
	{
1363
		$changes['maintenance'] = 2;
1364
		// Remember what it was...
1365
		$upcontext['user']['main'] = $maintenance;
1366
1367
		if (!empty($_POST['maintitle']))
1368
		{
1369
			$changes['mtitle'] = $_POST['maintitle'];
1370
			$changes['mmessage'] = $_POST['mainmessage'];
1371
		}
1372
		else
1373
		{
1374
			$changes['mtitle'] = $txt['mtitle'];
1375
			$changes['mmessage'] = $txt['mmessage'];
1376
		}
1377
	}
1378
1379
	if ($command_line)
1380
		echo ' * Updating Settings.php...';
1381
1382
	// Fix some old paths.
1383
	if (substr($boarddir, 0, 1) == '.')
1384
		$changes['boarddir'] = fixRelativePath($boarddir);
1385
1386
	if (substr($sourcedir, 0, 1) == '.')
1387
		$changes['sourcedir'] = fixRelativePath($sourcedir);
1388
1389
	if (empty($cachedir) || substr($cachedir, 0, 1) == '.')
1390
		$changes['cachedir'] = fixRelativePath($boarddir) . '/cache';
1391
1392
	// Migrate cache settings.
1393
	// Accelerator setting didn't exist previously; use 'smf' file based caching as default if caching had been enabled.
1394
	if (!isset($GLOBALS['cache_enable']))
1395
		$changes += array(
1396
			'cache_accelerator' => upgradeCacheSettings(),
0 ignored issues
show
The function upgradeCacheSettings was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

1396
			'cache_accelerator' => /** @scrutinizer ignore-call */ upgradeCacheSettings(),
Loading history...
1397
			'cache_enable' => !empty($modSettings['cache_enable']) ? $modSettings['cache_enable'] : 0,
1398
			'cache_memcached' => !empty($modSettings['cache_memcached']) ? $modSettings['cache_memcached'] : '',
1399
		);
1400
1401
	// If they have a "host:port" setup for the host, split that into separate values
1402
	// You should never have a : in the hostname if you're not on MySQL, but better safe than sorry
1403
	if (strpos($db_server, ':') !== false && $db_type == 'mysql')
1404
	{
1405
		list ($db_server, $db_port) = explode(':', $db_server);
1406
1407
		$changes['db_server'] = $db_server;
1408
1409
		// Only set this if we're not using the default port
1410
		if ($db_port != ini_get('mysqli.default_port'))
1411
			$changes['db_port'] = (int) $db_port;
1412
	}
1413
1414
	// If db_port is set and is the same as the default, set it to 0.
1415
	if (!empty($db_port))
1416
	{
1417
		if ($db_type == 'mysql' && $db_port == ini_get('mysqli.default_port'))
1418
			$changes['db_port'] = 0;
1419
1420
		elseif ($db_type == 'postgresql' && $db_port == 5432)
1421
			$changes['db_port'] = 0;
1422
	}
1423
1424
	// Maybe we haven't had this option yet?
1425
	if (empty($packagesdir))
1426
		$changes['packagesdir'] = fixRelativePath($boarddir) . '/Packages';
1427
1428
	// Add support for $tasksdir var.
1429
	if (empty($tasksdir))
1430
		$changes['tasksdir'] = fixRelativePath($sourcedir) . '/tasks';
1431
1432
	// Make sure we fix the language as well.
1433
	if (stristr($language, '-utf8'))
1434
		$changes['language'] = str_ireplace('-utf8', '', $language);
1435
1436
	// @todo Maybe change the cookie name if going to 1.1, too?
1437
1438
	// Ensure this doesn't get lost in translation.
1439
	$changes['upgradeData'] = base64_encode(json_encode($upcontext['user']));
1440
1441
	// Update Settings.php with the new settings, and rebuild if they selected that option.
1442
	require_once($sourcedir . '/Subs.php');
1443
	require_once($sourcedir . '/Subs-Admin.php');
1444
	$res = updateSettingsFile($changes, false, !empty($_POST['migrateSettings']));
1445
1446
	if ($command_line && $res)
1447
		echo ' Successful.' . "\n";
1448
	elseif ($command_line && !$res)
1449
	{
1450
		echo ' FAILURE.' . "\n";
1451
		die;
0 ignored issues
show
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...
1452
	}
1453
1454
	// Are we doing debug?
1455
	if (isset($_POST['debug']))
1456
	{
1457
		$upcontext['upgrade_status']['debug'] = true;
1458
		$is_debug = true;
1459
	}
1460
1461
	// If we're not backing up then jump one.
1462
	if (empty($_POST['backup']))
1463
		$upcontext['current_step']++;
1464
1465
	// If we've got here then let's proceed to the next step!
1466
	return true;
1467
}
1468
1469
// Backup the database - why not...
1470
function BackupDatabase()
1471
{
1472
	global $upcontext, $db_prefix, $command_line, $support_js, $file_steps, $smcFunc, $txt;
1473
1474
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'backup_xml' : 'backup_database';
1475
	$upcontext['page_title'] = $txt['backup_database'];
1476
1477
	// Done it already - js wise?
1478
	if (!empty($_POST['backup_done']))
1479
		return true;
1480
1481
	// We cannot execute this step in strict mode - strict mode data fixes are not applied yet
1482
	setSqlMode(false);
1483
1484
	// Some useful stuff here.
1485
	db_extend();
1486
1487
	// Might need this as well
1488
	db_extend('packages');
1489
1490
	// Get all the table names.
1491
	$filter = str_replace('_', '\_', preg_match('~^`(.+?)`\.(.+?)$~', $db_prefix, $match) != 0 ? $match[2] : $db_prefix) . '%';
1492
	$db = preg_match('~^`(.+?)`\.(.+?)$~', $db_prefix, $match) != 0 ? strtr($match[1], array('`' => '')) : false;
1493
	$tables = $smcFunc['db_list_tables']($db, $filter);
1494
1495
	$table_names = array();
1496
	foreach ($tables as $table)
1497
		if (substr($table, 0, 7) !== 'backup_')
1498
			$table_names[] = $table;
1499
1500
	$upcontext['table_count'] = count($table_names);
1501
	$upcontext['cur_table_num'] = $_GET['substep'];
1502
	$upcontext['cur_table_name'] = str_replace($db_prefix, '', isset($table_names[$_GET['substep']]) ? $table_names[$_GET['substep']] : $table_names[0]);
1503
	$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
1504
	// For non-java auto submit...
1505
	$file_steps = $upcontext['table_count'];
1506
1507
	// What ones have we already done?
1508
	foreach ($table_names as $id => $table)
1509
		if ($id < $_GET['substep'])
1510
			$upcontext['previous_tables'][] = $table;
1511
1512
	if ($command_line)
1513
		echo 'Backing Up Tables.';
1514
1515
	// If we don't support javascript we backup here.
1516
	if (!$support_js || isset($_GET['xml']))
1517
	{
1518
		// Backup each table!
1519
		for ($substep = $_GET['substep'], $n = count($table_names); $substep < $n; $substep++)
1520
		{
1521
			$upcontext['cur_table_name'] = str_replace($db_prefix, '', (isset($table_names[$substep + 1]) ? $table_names[$substep + 1] : $table_names[$substep]));
1522
			$upcontext['cur_table_num'] = $substep + 1;
1523
1524
			$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
1525
1526
			// Do we need to pause?
1527
			nextSubstep($substep);
1528
1529
			backupTable($table_names[$substep]);
1530
1531
			// If this is XML to keep it nice for the user do one table at a time anyway!
1532
			if (isset($_GET['xml']))
1533
				return upgradeExit();
0 ignored issues
show
Are you sure the usage of upgradeExit() is correct as it seems to always return null.

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

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

}

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

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

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

Loading history...
1534
		}
1535
1536
		if ($command_line)
1537
		{
1538
			echo "\n" . ' Successful.\'' . "\n";
1539
			flush();
1540
		}
1541
		$upcontext['step_progress'] = 100;
1542
1543
		$_GET['substep'] = 0;
1544
		// Make sure we move on!
1545
		return true;
1546
	}
1547
1548
	// Either way next place to post will be database changes!
1549
	$_GET['substep'] = 0;
1550
	return false;
1551
}
1552
1553
// Backup one table...
1554
function backupTable($table)
1555
{
1556
	global $command_line, $db_prefix, $smcFunc;
1557
1558
	if ($command_line)
1559
	{
1560
		echo "\n" . ' +++ Backing up \"' . str_replace($db_prefix, '', $table) . '"...';
1561
		flush();
1562
	}
1563
1564
	$smcFunc['db_backup_table']($table, 'backup_' . $table);
1565
1566
	if ($command_line)
1567
		echo ' done.';
1568
}
1569
1570
// Step 2: Everything.
1571
function DatabaseChanges()
1572
{
1573
	global $db_prefix, $modSettings, $smcFunc, $txt;
1574
	global $upcontext, $support_js, $db_type, $boarddir;
1575
1576
	// Have we just completed this?
1577
	if (!empty($_POST['database_done']))
1578
		return true;
1579
1580
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'database_xml' : 'database_changes';
1581
	$upcontext['page_title'] = $txt['database_changes'];
1582
1583
	$upcontext['delete_karma'] = !empty($_SESSION['delete_karma']);
1584
	$upcontext['empty_error'] = !empty($_SESSION['empty_error']);
1585
1586
	// All possible files.
1587
	// Name, < version, insert_on_complete
1588
	// Last entry in array indicates whether to use sql_mode of STRICT or not.
1589
	$files = array(
1590
		array('upgrade_1-0.sql', '1.1', '1.1 RC0', false),
1591
		array('upgrade_1-1.sql', '2.0', '2.0 a', false),
1592
		array('upgrade_2-0_' . $db_type . '.sql', '2.1', '2.1 dev0', false),
1593
		array('upgrade_2-1_' . $db_type . '.sql', '3.0', SMF_VERSION, true),
1594
	);
1595
1596
	// How many files are there in total?
1597
	if (isset($_GET['filecount']))
1598
		$upcontext['file_count'] = (int) $_GET['filecount'];
1599
	else
1600
	{
1601
		$upcontext['file_count'] = 0;
1602
		foreach ($files as $file)
1603
		{
1604
			if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < $file[1])
1605
				$upcontext['file_count']++;
1606
		}
1607
	}
1608
1609
	// Do each file!
1610
	$did_not_do = count($files) - $upcontext['file_count'];
1611
	$upcontext['step_progress'] = 0;
1612
	$upcontext['cur_file_num'] = 0;
1613
	foreach ($files as $file)
1614
	{
1615
		if ($did_not_do)
1616
			$did_not_do--;
1617
		else
1618
		{
1619
			$upcontext['cur_file_num']++;
1620
			$upcontext['cur_file_name'] = $file[0];
1621
			// Do we actually need to do this still?
1622
			if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < $file[1])
1623
			{
1624
				// Use STRICT mode on more recent steps
1625
				setSqlMode($file[3]);
1626
1627
				// Reload modSettings to capture any adds/updates made along the way
1628
				$request = $smcFunc['db_query']('', '
1629
					SELECT variable, value
1630
					FROM {db_prefix}settings',
1631
					array(
1632
						'db_error_skip' => true,
1633
					)
1634
				);
1635
1636
				$modSettings = array();
1637
				while ($row = $smcFunc['db_fetch_assoc']($request))
1638
					$modSettings[$row['variable']] = $row['value'];
1639
1640
				$smcFunc['db_free_result']($request);
1641
1642
				// Some theme settings are in $modSettings
1643
				// Note we still might be doing yabbse (no smf ver)
1644
				if (isset($modSettings['smfVersion']))
1645
				{
1646
					$request = $smcFunc['db_query']('', '
1647
						SELECT variable, value
1648
						FROM {db_prefix}themes
1649
						WHERE id_theme = {int:id_theme}
1650
							AND variable IN ({string:theme_url}, {string:theme_dir}, {string:images_url})',
1651
						array(
1652
							'id_theme' => 1,
1653
							'theme_url' => 'theme_url',
1654
							'theme_dir' => 'theme_dir',
1655
							'images_url' => 'images_url',
1656
							'db_error_skip' => true,
1657
						)
1658
					);
1659
1660
					while ($row = $smcFunc['db_fetch_assoc']($request))
1661
						$modSettings[$row['variable']] = $row['value'];
1662
1663
					$smcFunc['db_free_result']($request);
1664
				}
1665
1666
				if (!isset($modSettings['theme_url']))
1667
				{
1668
					$modSettings['theme_dir'] = $boarddir . '/Themes/default';
1669
					$modSettings['theme_url'] = 'Themes/default';
1670
					$modSettings['images_url'] = 'Themes/default/images';
1671
				}
1672
1673
				// Now process the file...
1674
				$nextFile = parse_sql(dirname(__FILE__) . '/' . $file[0]);
1675
				if ($nextFile)
1676
				{
1677
					// Only update the version of this if complete.
1678
					$smcFunc['db_insert']('replace',
1679
						$db_prefix . 'settings',
1680
						array('variable' => 'string', 'value' => 'string'),
1681
						array('smfVersion', $file[2]),
1682
						array('variable')
1683
					);
1684
1685
					$modSettings['smfVersion'] = $file[2];
1686
				}
1687
1688
				// If this is XML we only do this stuff once.
1689
				if (isset($_GET['xml']))
1690
				{
1691
					// Flag to move on to the next.
1692
					$upcontext['completed_step'] = true;
1693
					// Did we complete the whole file?
1694
					if ($nextFile)
1695
						$upcontext['current_debug_item_num'] = -1;
1696
					return upgradeExit();
0 ignored issues
show
Are you sure the usage of upgradeExit() is correct as it seems to always return null.

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

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

}

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

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

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

Loading history...
1697
				}
1698
				elseif ($support_js)
1699
					break;
1700
			}
1701
			// Set the progress bar to be right as if we had - even if we hadn't...
1702
			$upcontext['step_progress'] = ($upcontext['cur_file_num'] / $upcontext['file_count']) * 100;
1703
		}
1704
	}
1705
1706
	$_GET['substep'] = 0;
1707
	// So the template knows we're done.
1708
	if (!$support_js)
1709
	{
1710
		$upcontext['changes_complete'] = true;
1711
1712
		return true;
1713
	}
1714
	return false;
1715
}
1716
1717
// Different versions of the files use different sql_modes
1718
function setSqlMode($strict = true)
1719
{
1720
	global $db_type, $db_connection;
1721
1722
	if ($db_type != 'mysql')
1723
		return;
1724
1725
	if ($strict)
1726
		$mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION,PIPES_AS_CONCAT';
1727
	else
1728
		$mode = '';
1729
1730
	mysqli_query($db_connection, 'SET SESSION sql_mode = \'' . $mode . '\'');
1731
1732
	return;
1733
}
1734
1735
// Delete the damn thing!
1736
function DeleteUpgrade()
1737
{
1738
	global $command_line, $language, $upcontext, $sourcedir;
1739
	global $user_info, $maintenance, $smcFunc, $db_type, $txt, $settings;
1740
1741
	// Now it's nice to have some of the basic SMF source files.
1742
	if (!isset($_GET['ssi']) && !$command_line)
1743
		redirectLocation('&ssi=1');
1744
1745
	$upcontext['sub_template'] = 'upgrade_complete';
1746
	$upcontext['page_title'] = $txt['upgrade_complete'];
1747
1748
	$endl = $command_line ? "\n" : '<br>' . "\n";
1749
1750
	$changes = array(
1751
		'language' => (substr($language, -4) == '.lng' ? substr($language, 0, -4) : $language),
1752
		'db_error_send' => true,
1753
		'upgradeData' => null,
1754
	);
1755
1756
	// Are we in maintenance mode?
1757
	if (isset($upcontext['user']['main']))
1758
	{
1759
		if ($command_line)
1760
			echo ' * ';
1761
		$upcontext['removed_maintenance'] = true;
1762
		$changes['maintenance'] = $upcontext['user']['main'];
1763
	}
1764
	// Otherwise if somehow we are in 2 let's go to 1.
1765
	elseif (!empty($maintenance) && $maintenance == 2)
1766
		$changes['maintenance'] = 1;
1767
1768
	// Wipe this out...
1769
	$upcontext['user'] = array();
1770
1771
	require_once($sourcedir . '/Subs.php');
1772
	require_once($sourcedir . '/Subs-Admin.php');
1773
	updateSettingsFile($changes);
1774
1775
	// Clean any old cache files away.
1776
	upgrade_clean_cache();
0 ignored issues
show
The function upgrade_clean_cache was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

1776
	/** @scrutinizer ignore-call */ 
1777
 upgrade_clean_cache();
Loading history...
1777
1778
	// Can we delete the file?
1779
	$upcontext['can_delete_script'] = is_writable(dirname(__FILE__)) || is_writable(__FILE__);
1780
1781
	// Now is the perfect time to fetch the SM files.
1782
	if ($command_line)
1783
		cli_scheduled_fetchSMfiles();
1784
	else
1785
	{
1786
		require_once($sourcedir . '/ScheduledTasks.php');
1787
		scheduled_fetchSMfiles(); // Now go get those files!
1788
		// This is needed in case someone invokes the upgrader using https when upgrading an http forum
1789
		if (httpsOn())
1790
			$settings['default_theme_url'] = strtr($settings['default_theme_url'], array('http://' => 'https://'));
1791
	}
1792
1793
	// Log what we've done.
1794
	if (empty($user_info['id']))
1795
		$user_info['id'] = !empty($upcontext['user']['id']) ? $upcontext['user']['id'] : 0;
1796
1797
	// Log the action manually, so CLI still works.
1798
	$smcFunc['db_insert']('',
1799
		'{db_prefix}log_actions',
1800
		array(
1801
			'log_time' => 'int', 'id_log' => 'int', 'id_member' => 'int', 'ip' => 'inet', 'action' => 'string',
1802
			'id_board' => 'int', 'id_topic' => 'int', 'id_msg' => 'int', 'extra' => 'string-65534',
1803
		),
1804
		array(
1805
			time(), 3, $user_info['id'], $command_line ? '127.0.0.1' : $user_info['ip'], 'upgrade',
1806
			0, 0, 0, json_encode(array('version' => SMF_FULL_VERSION, 'member' => $user_info['id'])),
1807
		),
1808
		array('id_action')
1809
	);
1810
	$user_info['id'] = 0;
1811
1812
	if ($command_line)
1813
	{
1814
		echo $endl;
1815
		echo 'Upgrade Complete!', $endl;
1816
		echo 'Please delete this file as soon as possible for security reasons.', $endl;
1817
		exit;
0 ignored issues
show
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...
1818
	}
1819
1820
	// Make sure it says we're done.
1821
	$upcontext['overall_percent'] = 100;
1822
	if (isset($upcontext['step_progress']))
1823
		unset($upcontext['step_progress']);
1824
1825
	$_GET['substep'] = 0;
1826
	return false;
1827
}
1828
1829
// Just like the built in one, but setup for CLI to not use themes.
1830
function cli_scheduled_fetchSMfiles()
1831
{
1832
	global $sourcedir, $language, $modSettings, $smcFunc;
1833
1834
	if (empty($modSettings['time_format']))
1835
		$modSettings['time_format'] = '%B %d, %Y, %I:%M:%S %p';
1836
1837
	// What files do we want to get
1838
	$request = $smcFunc['db_query']('', '
1839
		SELECT id_file, filename, path, parameters
1840
		FROM {db_prefix}admin_info_files',
1841
		array(
1842
		)
1843
	);
1844
1845
	$js_files = array();
1846
	while ($row = $smcFunc['db_fetch_assoc']($request))
1847
	{
1848
		$js_files[$row['id_file']] = array(
1849
			'filename' => $row['filename'],
1850
			'path' => $row['path'],
1851
			'parameters' => sprintf($row['parameters'], $language, urlencode($modSettings['time_format']), urlencode(SMF_FULL_VERSION)),
1852
		);
1853
	}
1854
	$smcFunc['db_free_result']($request);
1855
1856
	// We're gonna need fetch_web_data() to pull this off.
1857
	require_once($sourcedir . '/Subs.php');
1858
1859
	foreach ($js_files as $ID_FILE => $file)
1860
	{
1861
		// Create the url
1862
		$server = empty($file['path']) || substr($file['path'], 0, 7) != 'http://' ? 'https://www.simplemachines.org' : '';
1863
		$url = $server . (!empty($file['path']) ? $file['path'] : $file['path']) . $file['filename'] . (!empty($file['parameters']) ? '?' . $file['parameters'] : '');
1864
1865
		// Get the file
1866
		$file_data = fetch_web_data($url);
1867
1868
		// If we got an error - give up - the site might be down.
1869
		if ($file_data === false)
1870
			return throw_error(sprintf('Could not retrieve the file %1$s.', $url));
0 ignored issues
show
The function throw_error was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

1870
			return /** @scrutinizer ignore-call */ throw_error(sprintf('Could not retrieve the file %1$s.', $url));
Loading history...
1871
1872
		// Save the file to the database.
1873
		$smcFunc['db_query']('substring', '
1874
			UPDATE {db_prefix}admin_info_files
1875
			SET data = SUBSTRING({string:file_data}, 1, 65534)
1876
			WHERE id_file = {int:id_file}',
1877
			array(
1878
				'id_file' => $ID_FILE,
1879
				'file_data' => $file_data,
1880
			)
1881
		);
1882
	}
1883
	return true;
1884
}
1885
1886
function convertSettingsToTheme()
1887
{
1888
	global $db_prefix, $modSettings, $smcFunc;
1889
1890
	$values = array(
1891
		'show_latest_member' => @$GLOBALS['showlatestmember'],
1892
		'show_bbc' => isset($GLOBALS['showyabbcbutt']) ? $GLOBALS['showyabbcbutt'] : @$GLOBALS['showbbcbutt'],
1893
		'show_modify' => @$GLOBALS['showmodify'],
1894
		'show_user_images' => @$GLOBALS['showuserpic'],
1895
		'show_blurb' => @$GLOBALS['showusertext'],
1896
		'show_gender' => @$GLOBALS['showgenderimage'],
1897
		'show_newsfader' => @$GLOBALS['shownewsfader'],
1898
		'display_recent_bar' => @$GLOBALS['Show_RecentBar'],
1899
		'show_member_bar' => @$GLOBALS['Show_MemberBar'],
1900
		'linktree_link' => @$GLOBALS['curposlinks'],
1901
		'show_profile_buttons' => @$GLOBALS['profilebutton'],
1902
		'show_mark_read' => @$GLOBALS['showmarkread'],
1903
		'show_board_desc' => @$GLOBALS['ShowBDescrip'],
1904
		'newsfader_time' => @$GLOBALS['fadertime'],
1905
		'use_image_buttons' => empty($GLOBALS['MenuType']) ? 1 : 0,
1906
		'enable_news' => @$GLOBALS['enable_news'],
1907
		'linktree_inline' => @$modSettings['enableInlineLinks'],
1908
		'return_to_post' => @$modSettings['returnToPost'],
1909
	);
1910
1911
	$themeData = array();
1912
	foreach ($values as $variable => $value)
1913
	{
1914
		if (!isset($value) || $value === null)
1915
			$value = 0;
1916
1917
		$themeData[] = array(0, 1, $variable, $value);
1918
	}
1919
	if (!empty($themeData))
1920
	{
1921
		$smcFunc['db_insert']('ignore',
1922
			$db_prefix . 'themes',
1923
			array('id_member' => 'int', 'id_theme' => 'int', 'variable' => 'string', 'value' => 'string'),
1924
			$themeData,
1925
			array('id_member', 'id_theme', 'variable')
1926
		);
1927
	}
1928
}
1929
1930
// This function only works with MySQL but that's fine as it is only used for v1.0.
1931
function convertSettingstoOptions()
1932
{
1933
	global $modSettings, $smcFunc;
1934
1935
	// Format: new_setting -> old_setting_name.
1936
	$values = array(
1937
		'calendar_start_day' => 'cal_startmonday',
1938
		'view_newest_first' => 'viewNewestFirst',
1939
		'view_newest_pm_first' => 'viewNewestFirst',
1940
	);
1941
1942
	foreach ($values as $variable => $value)
1943
	{
1944
		if (empty($modSettings[$value[0]]))
1945
			continue;
1946
1947
		$smcFunc['db_query']('', '
1948
			INSERT IGNORE INTO {db_prefix}themes
1949
				(id_member, id_theme, variable, value)
1950
			SELECT id_member, 1, {string:variable}, {string:value}
1951
			FROM {db_prefix}members',
1952
			array(
1953
				'variable' => $variable,
1954
				'value' => $modSettings[$value[0]],
1955
				'db_error_skip' => true,
1956
			)
1957
		);
1958
1959
		$smcFunc['db_query']('', '
1960
			INSERT IGNORE INTO {db_prefix}themes
1961
				(id_member, id_theme, variable, value)
1962
			VALUES (-1, 1, {string:variable}, {string:value})',
1963
			array(
1964
				'variable' => $variable,
1965
				'value' => $modSettings[$value[0]],
1966
				'db_error_skip' => true,
1967
			)
1968
		);
1969
	}
1970
}
1971
1972
function php_version_check()
1973
{
1974
	return version_compare(PHP_VERSION, $GLOBALS['required_php_version'], '>=');
1975
}
1976
1977
function db_version_check()
1978
{
1979
	global $db_type, $databases;
1980
1981
	$curver = $databases[$db_type]['version_check']();
1982
	$curver = preg_replace('~\-.+?$~', '', $curver);
1983
1984
	return version_compare($databases[$db_type]['version'], $curver, '<=');
1985
}
1986
1987
function fixRelativePath($path)
1988
{
1989
	global $install_path;
1990
1991
	// Fix the . at the start, clear any duplicate slashes, and fix any trailing slash...
1992
	return addslashes(preg_replace(array('~^\.([/\\\]|$)~', '~[/]+~', '~[\\\]+~', '~[/\\\]$~'), array($install_path . '$1', '/', '\\', ''), $path));
1993
}
1994
1995
function parse_sql($filename)
1996
{
1997
	global $db_prefix, $db_collation, $boarddir, $boardurl, $command_line, $file_steps, $step_progress, $custom_warning;
1998
	global $upcontext, $support_js, $is_debug, $db_type, $db_character_set, $smcFunc;
1999
2000
/*
2001
	Failure allowed on:
2002
		- INSERT INTO but not INSERT IGNORE INTO.
2003
		- UPDATE IGNORE but not UPDATE.
2004
		- ALTER TABLE and ALTER IGNORE TABLE.
2005
		- DROP TABLE.
2006
	Yes, I realize that this is a bit confusing... maybe it should be done differently?
2007
2008
	If a comment...
2009
		- begins with --- it is to be output, with a break only in debug mode. (and say successful\n\n if there was one before.)
2010
		- begins with ---# it is a debugging statement, no break - only shown at all in debug.
2011
		- is only ---#, it is "done." and then a break - only shown in debug.
2012
		- begins with ---{ it is a code block terminating at ---}.
2013
2014
	Every block of between "--- ..."s is a step.  Every "---#" section represents a substep.
2015
2016
	Replaces the following variables:
2017
		- {$boarddir}
2018
		- {$boardurl}
2019
		- {$db_prefix}
2020
		- {$db_collation}
2021
*/
2022
2023
	// May want to use extended functionality.
2024
	db_extend();
2025
	db_extend('packages');
2026
2027
	// Our custom error handler - does nothing but does stop public errors from XML!
2028
	// Note that php error suppression - @ - used heavily in the upgrader, calls the error handler
2029
	// but error_reporting() will return 0 as it does so (pre php8).
2030
	// Note error handling in php8+ no longer fails silently on many errors, but error_reporting()
2031
	// will return 4437 (E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR | E_RECOVERABLE_ERROR | E_PARSE)
2032
	// as it does so.
2033
	set_error_handler(
2034
		function($errno, $errstr, $errfile, $errline) use ($support_js)
2035
		{
2036
			if ($support_js)
2037
				return true;
2038
			elseif ((error_reporting() != 0) && (error_reporting() != (E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR | E_RECOVERABLE_ERROR | E_PARSE)))
2039
				echo 'Error: ' . $errstr . ' File: ' . $errfile . ' Line: ' . $errline;
2040
		}
2041
	);
2042
2043
	// If we're on MySQL, set {db_collation}; this approach is used throughout upgrade_2-0_mysql.php to set new tables to utf8
2044
	// Note it is expected to be in the format: ENGINE=MyISAM{$db_collation};
2045
	if ($db_type == 'mysql')
2046
		$db_collation = ' DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci';
2047
	else
2048
		$db_collation = '';
2049
2050
	$endl = $command_line ? "\n" : '<br>' . "\n";
2051
2052
	$lines = file($filename);
2053
2054
	$current_type = 'sql';
2055
	$current_data = '';
2056
	$substep = 0;
2057
	$last_step = '';
2058
2059
	// Make sure all newly created tables will have the proper characters set; this approach is used throughout upgrade_2-1_mysql.php
2060
	$lines = str_replace(') ENGINE=MyISAM;', ') ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;', $lines);
2061
2062
	// Count the total number of steps within this file - for progress.
2063
	$file_steps = substr_count(implode('', $lines), '---#');
2064
	$upcontext['total_items'] = substr_count(implode('', $lines), '--- ');
2065
	$upcontext['debug_items'] = $file_steps;
2066
	$upcontext['current_item_num'] = 0;
2067
	$upcontext['current_item_name'] = '';
2068
	$upcontext['current_debug_item_num'] = 0;
2069
	$upcontext['current_debug_item_name'] = '';
2070
	// This array keeps a record of what we've done in case java is dead...
2071
	$upcontext['actioned_items'] = array();
2072
2073
	$done_something = false;
2074
2075
	foreach ($lines as $line_number => $line)
2076
	{
2077
		$do_current = $substep >= $_GET['substep'];
2078
2079
		// Get rid of any comments in the beginning of the line...
2080
		if (substr(trim($line), 0, 2) === '/*')
2081
			$line = preg_replace('~/\*.+?\*/~', '', $line);
2082
2083
		// Always flush.  Flush, flush, flush.  Flush, flush, flush, flush!  FLUSH!
2084
		if ($is_debug && !$support_js && $command_line)
2085
			flush();
2086
2087
		if (trim($line) === '')
2088
			continue;
2089
2090
		if (trim(substr($line, 0, 3)) === '---')
2091
		{
2092
			$type = substr($line, 3, 1);
2093
2094
			// An error??
2095
			if (trim($current_data) != '' && $type !== '}')
2096
			{
2097
				$upcontext['error_message'] = 'Error in upgrade script - line ' . $line_number . '!' . $endl;
2098
				if ($command_line)
2099
					echo $upcontext['error_message'];
2100
			}
2101
2102
			if ($type == ' ')
2103
			{
2104
				if (!$support_js && $do_current && $_GET['substep'] != 0 && $command_line)
2105
				{
2106
					echo ' Successful.', $endl;
2107
					flush();
2108
				}
2109
2110
				$last_step = htmlspecialchars(rtrim(substr($line, 4)));
2111
				$upcontext['current_item_num']++;
2112
				$upcontext['current_item_name'] = $last_step;
2113
2114
				if ($do_current)
2115
				{
2116
					$upcontext['actioned_items'][] = $last_step;
2117
					if ($command_line)
2118
						echo ' * ';
2119
2120
					// Starting a new main step in our DB changes, so it's time to reset this.
2121
					$upcontext['skip_db_substeps'] = false;
2122
				}
2123
			}
2124
			elseif ($type == '#')
2125
			{
2126
				$upcontext['step_progress'] += (100 / $upcontext['file_count']) / $file_steps;
2127
2128
				$upcontext['current_debug_item_num']++;
2129
				if (trim($line) != '---#')
2130
					$upcontext['current_debug_item_name'] = htmlspecialchars(rtrim(substr($line, 4)));
2131
2132
				// Have we already done something?
2133
				if (isset($_GET['xml']) && $done_something)
2134
				{
2135
					restore_error_handler();
2136
					return $upcontext['current_debug_item_num'] >= $upcontext['debug_items'] ? true : false;
2137
				}
2138
2139
				if ($do_current)
2140
				{
2141
					if (trim($line) == '---#' && $command_line)
2142
						echo ' done.', $endl;
2143
					elseif ($command_line)
2144
						echo ' +++ ', rtrim(substr($line, 4));
2145
					elseif (trim($line) != '---#')
2146
					{
2147
						if ($is_debug)
2148
							$upcontext['actioned_items'][] = $upcontext['current_debug_item_name'];
2149
					}
2150
				}
2151
2152
				if ($substep < $_GET['substep'] && $substep + 1 >= $_GET['substep'])
2153
				{
2154
					if ($command_line)
2155
						echo ' * ';
2156
					else
2157
						$upcontext['actioned_items'][] = $last_step;
2158
				}
2159
2160
				// Small step - only if we're actually doing stuff.
2161
				if ($do_current)
2162
					nextSubstep(++$substep);
2163
				else
2164
					$substep++;
2165
			}
2166
			elseif ($type == '{')
2167
				$current_type = 'code';
2168
			elseif ($type == '}')
2169
			{
2170
				$current_type = 'sql';
2171
2172
				if (!$do_current || !empty($upcontext['skip_db_substeps']))
2173
				{
2174
					$current_data = '';
2175
2176
					// Avoid confusion when skipping something we normally would have done
2177
					if ($do_current)
2178
						$done_something = true;
2179
2180
					continue;
2181
				}
2182
2183
				// @todo Update this to a try/catch for PHP 7+, because eval() now throws an exception for parse errors instead of returning false
2184
				if (eval('global $db_prefix, $modSettings, $smcFunc, $txt, $upcontext, $db_name; ' . $current_data) === false)
0 ignored issues
show
The use of eval() is discouraged.
Loading history...
2185
				{
2186
					$upcontext['error_message'] = 'Error in upgrade script ' . basename($filename) . ' on line ' . $line_number . '!' . $endl;
2187
					if ($command_line)
2188
						echo $upcontext['error_message'];
2189
				}
2190
2191
				// Done with code!
2192
				$current_data = '';
2193
				$done_something = true;
2194
			}
2195
2196
			continue;
2197
		}
2198
2199
		$current_data .= $line;
2200
		if (substr(rtrim($current_data), -1) === ';' && $current_type === 'sql')
2201
		{
2202
			if ((!$support_js || isset($_GET['xml'])))
2203
			{
2204
				if (!$do_current || !empty($upcontext['skip_db_substeps']))
2205
				{
2206
					$current_data = '';
2207
2208
					if ($do_current)
2209
						$done_something = true;
2210
2211
					continue;
2212
				}
2213
2214
				// {$sboarddir} is deprecated, but blah blah backward compatibility blah...
2215
				$current_data = strtr(substr(rtrim($current_data), 0, -1), array('{$db_prefix}' => $db_prefix, '{$boarddir}' => $smcFunc['db_escape_string']($boarddir), '{$sboarddir}' => $smcFunc['db_escape_string']($boarddir), '{$boardurl}' => $boardurl, '{$db_collation}' => $db_collation));
2216
2217
				upgrade_query($current_data);
2218
2219
				// @todo This will be how it kinda does it once mysql all stripped out - needed for postgre (etc).
2220
				/*
2221
				$result = $smcFunc['db_query']('', $current_data, false, false);
2222
				// Went wrong?
2223
				if (!$result)
2224
				{
2225
					// Bit of a bodge - do we want the error?
2226
					if (!empty($upcontext['return_error']))
2227
					{
2228
						$upcontext['error_message'] = $smcFunc['db_error']($db_connection);
2229
						return false;
2230
					}
2231
				}*/
2232
				$done_something = true;
2233
			}
2234
			$current_data = '';
2235
		}
2236
		// If this is xml based and we're just getting the item name then that's grand.
2237
		elseif ($support_js && !isset($_GET['xml']) && $upcontext['current_debug_item_name'] != '' && $do_current)
2238
		{
2239
			restore_error_handler();
2240
			return false;
2241
		}
2242
2243
		// Clean up by cleaning any step info.
2244
		$step_progress = array();
2245
		$custom_warning = '';
2246
	}
2247
2248
	// Put back the error handler.
2249
	restore_error_handler();
2250
2251
	if ($command_line)
2252
	{
2253
		echo ' Successful.' . "\n";
2254
		flush();
2255
	}
2256
2257
	$_GET['substep'] = 0;
2258
	return true;
2259
}
2260
2261
function upgrade_query($string, $unbuffered = false)
2262
{
2263
	global $db_connection, $db_server, $db_user, $db_passwd, $db_type;
2264
	global $command_line, $upcontext, $upgradeurl, $modSettings;
2265
	global $db_name, $db_unbuffered, $smcFunc, $txt;
2266
2267
	// Get the query result - working around some SMF specific security - just this once!
2268
	$modSettings['disableQueryCheck'] = true;
2269
	$db_unbuffered = $unbuffered;
2270
	$ignore_insert_error = false;
2271
2272
	$result = $smcFunc['db_query']('', $string, array('security_override' => true, 'db_error_skip' => true));
2273
	$db_unbuffered = false;
2274
2275
	// Failure?!
2276
	if ($result !== false)
2277
		return $result;
2278
2279
	$db_error_message = $smcFunc['db_error']($db_connection);
2280
	// If MySQL we do something more clever.
2281
	if ($db_type == 'mysql')
2282
	{
2283
		$mysqli_errno = mysqli_errno($db_connection);
2284
		$error_query = in_array(substr(trim($string), 0, 11), array('INSERT INTO', 'UPDATE IGNO', 'ALTER TABLE', 'DROP TABLE ', 'ALTER IGNOR', 'INSERT IGNO'));
2285
2286
		// Error numbers:
2287
		//    1016: Can't open file '....MYI'
2288
		//    1050: Table already exists.
2289
		//    1054: Unknown column name.
2290
		//    1060: Duplicate column name.
2291
		//    1061: Duplicate key name.
2292
		//    1062: Duplicate entry for unique key.
2293
		//    1068: Multiple primary keys.
2294
		//    1072: Key column '%s' doesn't exist in table.
2295
		//    1091: Can't drop key, doesn't exist.
2296
		//    1146: Table doesn't exist.
2297
		//    2013: Lost connection to server during query.
2298
2299
		if ($mysqli_errno == 1016)
2300
		{
2301
			if (preg_match('~\'([^\.\']+)~', $db_error_message, $match) != 0 && !empty($match[1]))
2302
			{
2303
				mysqli_query($db_connection, 'REPAIR TABLE `' . $match[1] . '`');
2304
				$result = mysqli_query($db_connection, $string);
2305
				if ($result !== false)
2306
					return $result;
2307
			}
2308
		}
2309
		elseif ($mysqli_errno == 2013)
2310
		{
2311
			$db_connection = mysqli_connect($db_server, $db_user, $db_passwd);
0 ignored issues
show
The call to mysqli_connect() has too few arguments starting with database. ( Ignorable by Annotation )

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

2311
			$db_connection = /** @scrutinizer ignore-call */ mysqli_connect($db_server, $db_user, $db_passwd);

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...
2312
			mysqli_select_db($db_connection, $db_name);
2313
			if ($db_connection)
2314
			{
2315
				$result = mysqli_query($db_connection, $string);
2316
				if ($result !== false)
2317
					return $result;
2318
			}
2319
		}
2320
		// Duplicate column name... should be okay ;).
2321
		elseif (in_array($mysqli_errno, array(1060, 1061, 1068, 1091)))
2322
			return false;
2323
		// Duplicate insert... make sure it's the proper type of query ;).
2324
		elseif (in_array($mysqli_errno, array(1054, 1062, 1146)) && $error_query)
2325
			return false;
2326
		// Creating an index on a non-existent column.
2327
		elseif ($mysqli_errno == 1072)
2328
			return false;
2329
		elseif ($mysqli_errno == 1050 && substr(trim($string), 0, 12) == 'RENAME TABLE')
2330
			return false;
2331
		// Testing for legacy tables or columns? Needed for 1.0 & 1.1 scripts.
2332
		elseif (in_array($mysqli_errno, array(1054, 1146)) && in_array(substr(trim($string), 0, 7), array('SELECT ', 'SHOW CO')))
2333
			return false;
2334
	}
2335
	// If a table already exists don't go potty.
2336
	else
2337
	{
2338
		if (in_array(substr(trim($string), 0, 8), array('CREATE T', 'CREATE S', 'DROP TABL', 'ALTER TA', 'CREATE I', 'CREATE U')))
2339
		{
2340
			if (strpos($db_error_message, 'exist') !== false)
2341
				return true;
2342
		}
2343
		elseif (strpos(trim($string), 'INSERT ') !== false)
2344
		{
2345
			if (strpos($db_error_message, 'duplicate') !== false || $ignore_insert_error)
2346
				return true;
2347
		}
2348
	}
2349
2350
	// Get the query string so we pass everything.
2351
	$query_string = '';
2352
	foreach ($_GET as $k => $v)
2353
		$query_string .= ';' . $k . '=' . $v;
2354
	if (strlen($query_string) != 0)
2355
		$query_string = '?' . substr($query_string, 1);
2356
2357
	if ($command_line)
2358
	{
2359
		echo 'Unsuccessful!  Database error message:', "\n", $db_error_message, "\n";
2360
		die;
0 ignored issues
show
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...
2361
	}
2362
2363
	// Bit of a bodge - do we want the error?
2364
	if (!empty($upcontext['return_error']))
2365
	{
2366
		$upcontext['error_message'] = $db_error_message;
2367
		$upcontext['error_string'] = $string;
2368
		return false;
2369
	}
2370
2371
	// Otherwise we have to display this somewhere appropriate if possible.
2372
	$upcontext['forced_error_message'] = '
2373
			<strong>' . $txt['upgrade_unsuccessful'] . '</strong><br>
2374
2375
			<div style="margin: 2ex;">
2376
				' . $txt['upgrade_thisquery'] . '
2377
				<blockquote><pre>' . nl2br(htmlspecialchars(trim($string))) . ';</pre></blockquote>
2378
2379
				' . $txt['upgrade_causerror'] . '
2380
				<blockquote>' . nl2br(htmlspecialchars($db_error_message)) . '</blockquote>
2381
			</div>
2382
2383
			<form action="' . $upgradeurl . $query_string . '" method="post">
2384
				<input type="submit" value="' . $txt['upgrade_respondtime_clickhere'] . '" class="button">
2385
			</form>
2386
		</div>';
2387
2388
	upgradeExit();
2389
}
2390
2391
// This performs a table alter, but does it unbuffered so the script can time out professionally.
2392
function protected_alter($change, $substep, $is_test = false)
2393
{
2394
	global $db_prefix, $smcFunc;
2395
2396
	db_extend('packages');
2397
2398
	// Firstly, check whether the current index/column exists.
2399
	$found = false;
2400
	if ($change['type'] === 'column')
2401
	{
2402
		$columns = $smcFunc['db_list_columns']('{db_prefix}' . $change['table'], true);
2403
		foreach ($columns as $column)
2404
		{
2405
			// Found it?
2406
			if ($column['name'] === $change['name'])
2407
			{
2408
				$found |= true;
2409
				// Do some checks on the data if we have it set.
2410
				if (isset($change['col_type']))
2411
					$found &= $change['col_type'] === $column['type'];
2412
				if (isset($change['null_allowed']))
2413
					$found &= $column['null'] == $change['null_allowed'];
2414
				if (isset($change['default']))
2415
					$found &= $change['default'] === $column['default'];
2416
			}
2417
		}
2418
	}
2419
	elseif ($change['type'] === 'index')
2420
	{
2421
		$request = upgrade_query('
2422
			SHOW INDEX
2423
			FROM ' . $db_prefix . $change['table']);
2424
		if ($request !== false)
2425
		{
2426
			$cur_index = array();
2427
2428
			while ($row = $smcFunc['db_fetch_assoc']($request))
2429
				if ($row['Key_name'] === $change['name'])
2430
					$cur_index[(int) $row['Seq_in_index']] = $row['Column_name'];
2431
2432
			ksort($cur_index, SORT_NUMERIC);
2433
			$found = array_values($cur_index) === $change['target_columns'];
2434
2435
			$smcFunc['db_free_result']($request);
2436
		}
2437
	}
2438
2439
	// If we're trying to add and it's added, we're done.
2440
	if ($found && in_array($change['method'], array('add', 'change')))
0 ignored issues
show
Bug Best Practice introduced by
The expression $found of type false|integer is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
2441
		return true;
2442
	// Otherwise if we're removing and it wasn't found we're also done.
2443
	elseif (!$found && in_array($change['method'], array('remove', 'change_remove')))
0 ignored issues
show
Bug Best Practice introduced by
The expression $found of type false|integer is loosely compared to false; this is ambiguous if the integer can be 0. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
2444
		return true;
2445
	// Otherwise is it just a test?
2446
	elseif ($is_test)
2447
		return false;
2448
2449
	// Not found it yet? Bummer! How about we see if we're currently doing it?
2450
	$running = false;
2451
	$found = false;
2452
	while (1 == 1)
2453
	{
2454
		$request = upgrade_query('
2455
			SHOW FULL PROCESSLIST');
2456
		while ($row = $smcFunc['db_fetch_assoc']($request))
2457
		{
2458
			if (strpos($row['Info'], 'ALTER TABLE ' . $db_prefix . $change['table']) !== false && strpos($row['Info'], $change['text']) !== false)
2459
				$found = true;
2460
		}
2461
2462
		// Can't find it? Then we need to run it fools!
2463
		if (!$found && !$running)
2464
		{
2465
			$smcFunc['db_free_result']($request);
2466
2467
			$success = upgrade_query('
2468
				ALTER TABLE ' . $db_prefix . $change['table'] . '
2469
				' . $change['text'], true) !== false;
2470
2471
			if (!$success)
2472
				return false;
2473
2474
			// Return
2475
			$running = true;
2476
		}
2477
		// What if we've not found it, but we'd ran it already? Must of completed.
2478
		elseif (!$found)
2479
		{
2480
			$smcFunc['db_free_result']($request);
2481
			return true;
2482
		}
2483
2484
		// Pause execution for a sec or three.
2485
		sleep(3);
2486
2487
		// Can never be too well protected.
2488
		nextSubstep($substep);
2489
	}
2490
2491
	// Protect it.
2492
	nextSubstep($substep);
2493
}
2494
2495
/**
2496
 * Alter a text column definition preserving its character set.
2497
 *
2498
 * @param array $change
2499
 * @param int $substep
2500
 */
2501
function textfield_alter($change, $substep)
2502
{
2503
	global $db_prefix, $smcFunc;
2504
2505
	$request = $smcFunc['db_query']('', '
2506
		SHOW FULL COLUMNS
2507
		FROM {db_prefix}' . $change['table'] . '
2508
		LIKE {string:column}',
2509
		array(
2510
			'column' => $change['column'],
2511
			'db_error_skip' => true,
2512
		)
2513
	);
2514
	if ($smcFunc['db_num_rows']($request) === 0)
2515
		die('Unable to find column ' . $change['column'] . ' inside table ' . $db_prefix . $change['table']);
0 ignored issues
show
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...
2516
	$table_row = $smcFunc['db_fetch_assoc']($request);
2517
	$smcFunc['db_free_result']($request);
2518
2519
	// If something of the current column definition is different, fix it.
2520
	$column_fix = $table_row['Type'] !== $change['type'] || (strtolower($table_row['Null']) === 'yes') !== $change['null_allowed'] || ($table_row['Default'] === null) !== !isset($change['default']) || (isset($change['default']) && $change['default'] !== $table_row['Default']);
2521
2522
	// Columns that previously allowed null, need to be converted first.
2523
	$null_fix = strtolower($table_row['Null']) === 'yes' && !$change['null_allowed'];
2524
2525
	// Get the character set that goes with the collation of the column.
2526
	if ($column_fix && !empty($table_row['Collation']))
2527
	{
2528
		$request = $smcFunc['db_query']('', '
2529
			SHOW COLLATION
2530
			LIKE {string:collation}',
2531
			array(
2532
				'collation' => $table_row['Collation'],
2533
				'db_error_skip' => true,
2534
			)
2535
		);
2536
		// No results? Just forget it all together.
2537
		if ($smcFunc['db_num_rows']($request) === 0)
2538
			unset($table_row['Collation']);
2539
		else
2540
			$collation_info = $smcFunc['db_fetch_assoc']($request);
2541
		$smcFunc['db_free_result']($request);
2542
	}
2543
2544
	if ($column_fix)
2545
	{
2546
		// Make sure there are no NULL's left.
2547
		if ($null_fix)
2548
			$smcFunc['db_query']('', '
2549
				UPDATE {db_prefix}' . $change['table'] . '
2550
				SET ' . $change['column'] . ' = {string:default}
2551
				WHERE ' . $change['column'] . ' IS NULL',
2552
				array(
2553
					'default' => isset($change['default']) ? $change['default'] : '',
2554
					'db_error_skip' => true,
2555
				)
2556
			);
2557
2558
		// Do the actual alteration.
2559
		$smcFunc['db_query']('', '
2560
			ALTER TABLE {db_prefix}' . $change['table'] . '
2561
			CHANGE COLUMN ' . $change['column'] . ' ' . $change['column'] . ' ' . $change['type'] . (isset($collation_info['Charset']) ? ' CHARACTER SET ' . $collation_info['Charset'] . ' COLLATE ' . $collation_info['Collation'] : '') . ($change['null_allowed'] ? '' : ' NOT NULL') . (isset($change['default']) ? ' default {string:default}' : ''),
2562
			array(
2563
				'default' => isset($change['default']) ? $change['default'] : '',
2564
				'db_error_skip' => true,
2565
			)
2566
		);
2567
	}
2568
	nextSubstep($substep);
2569
}
2570
2571
// The next substep.
2572
function nextSubstep($substep)
2573
{
2574
	global $start_time, $timeLimitThreshold, $command_line, $custom_warning;
2575
	global $step_progress, $is_debug, $upcontext;
2576
2577
	if ($_GET['substep'] < $substep)
2578
		$_GET['substep'] = $substep;
2579
2580
	if ($command_line)
2581
	{
2582
		if (time() - $start_time > 1 && empty($is_debug))
2583
		{
2584
			echo '.';
2585
			$start_time = time();
2586
		}
2587
		return;
2588
	}
2589
2590
	@set_time_limit(300);
2591
	if (function_exists('apache_reset_timeout'))
2592
		@apache_reset_timeout();
2593
2594
	if (time() - $start_time <= $timeLimitThreshold)
2595
		return;
2596
2597
	// Do we have some custom step progress stuff?
2598
	if (!empty($step_progress))
2599
	{
2600
		$upcontext['substep_progress'] = 0;
2601
		$upcontext['substep_progress_name'] = $step_progress['name'];
2602
		if ($step_progress['current'] > $step_progress['total'])
2603
			$upcontext['substep_progress'] = 99.9;
2604
		else
2605
			$upcontext['substep_progress'] = ($step_progress['current'] / $step_progress['total']) * 100;
2606
2607
		// Make it nicely rounded.
2608
		$upcontext['substep_progress'] = round($upcontext['substep_progress'], 1);
2609
	}
2610
2611
	// If this is XML we just exit right away!
2612
	if (isset($_GET['xml']))
2613
		return upgradeExit();
0 ignored issues
show
Are you sure the usage of upgradeExit() is correct as it seems to always return null.

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

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

}

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

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

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

Loading history...
2614
2615
	// We're going to pause after this!
2616
	$upcontext['pause'] = true;
2617
2618
	$upcontext['query_string'] = '';
2619
	foreach ($_GET as $k => $v)
2620
	{
2621
		if ($k != 'data' && $k != 'substep' && $k != 'step')
2622
			$upcontext['query_string'] .= ';' . $k . '=' . $v;
2623
	}
2624
2625
	// Custom warning?
2626
	if (!empty($custom_warning))
2627
		$upcontext['custom_warning'] = $custom_warning;
2628
2629
	upgradeExit();
2630
}
2631
2632
function cmdStep0()
2633
{
2634
	global $boarddir, $sourcedir, $modSettings, $start_time, $cachedir, $databases, $db_type, $smcFunc, $upcontext;
2635
	global $is_debug, $boardurl, $txt;
2636
	$start_time = time();
2637
2638
	while (ob_get_level() > 0)
2639
		ob_end_clean();
2640
	ob_implicit_flush(1);
2641
2642
	if (!isset($_SERVER['argv']))
2643
		$_SERVER['argv'] = array();
2644
	$_GET['maint'] = 1;
2645
2646
	foreach ($_SERVER['argv'] as $i => $arg)
2647
	{
2648
		if (preg_match('~^--language=(.+)$~', $arg, $match) != 0)
2649
			$upcontext['lang'] = $match[1];
2650
		elseif (preg_match('~^--path=(.+)$~', $arg) != 0)
2651
			continue;
2652
		elseif ($arg == '--no-maintenance')
2653
			$_GET['maint'] = 0;
2654
		elseif ($arg == '--debug')
2655
			$is_debug = true;
2656
		elseif ($arg == '--backup')
2657
			$_POST['backup'] = 1;
2658
		elseif ($arg == '--rebuild-settings')
2659
			$_POST['migrateSettings'] = 1;
2660
		elseif ($arg == '--allow-stats')
2661
			$_POST['stats'] = 1;
2662
		elseif ($arg == '--template' && (file_exists($boarddir . '/template.php') || file_exists($boarddir . '/template.html') && !file_exists($modSettings['theme_dir'] . '/converted')))
2663
			$_GET['conv'] = 1;
2664
		elseif ($i != 0)
2665
		{
2666
			echo 'SMF Command-line Upgrader
2667
Usage: /path/to/php -f ' . basename(__FILE__) . ' -- [OPTION]...
2668
2669
	--path=/path/to/SMF     Specify custom SMF root directory.
2670
	--language=LANG         Reset the forum\'s language to LANG.
2671
	--no-maintenance        Don\'t put the forum into maintenance mode.
2672
	--debug                 Output debugging information.
2673
	--backup                Create backups of tables with "backup_" prefix.
2674
	--allow-stats           Allow Simple Machines stat collection
2675
	--rebuild-settings      Rebuild the Settings.php file';
2676
			echo "\n";
2677
			exit;
0 ignored issues
show
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...
2678
		}
2679
	}
2680
2681
	if (!php_version_check())
2682
		print_error('Error: PHP ' . PHP_VERSION . ' does not match version requirements.', true);
0 ignored issues
show
The function print_error was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

2682
		/** @scrutinizer ignore-call */ 
2683
  print_error('Error: PHP ' . PHP_VERSION . ' does not match version requirements.', true);
Loading history...
2683
	if (!db_version_check())
2684
		print_error('Error: ' . $databases[$db_type]['name'] . ' ' . $databases[$db_type]['version'] . ' does not match minimum requirements.', true);
2685
2686
	// Do some checks to make sure they have proper privileges
2687
	db_extend('packages');
2688
2689
	// CREATE
2690
	$create = $smcFunc['db_create_table']('{db_prefix}priv_check', array(array('name' => 'id_test', 'type' => 'int', 'size' => 10, 'unsigned' => true, 'auto' => true)), array(array('columns' => array('id_test'), 'primary' => true)), array(), 'overwrite');
2691
2692
	// ALTER
2693
	$alter = $smcFunc['db_add_column']('{db_prefix}priv_check', array('name' => 'txt', 'type' => 'varchar', 'size' => 4, 'null' => false, 'default' => ''));
2694
2695
	// DROP
2696
	$drop = $smcFunc['db_drop_table']('{db_prefix}priv_check');
2697
2698
	// Sorry... we need CREATE, ALTER and DROP
2699
	if (!$create || !$alter || !$drop)
2700
		print_error("The " . $databases[$db_type]['name'] . " user you have set in Settings.php does not have proper privileges.\n\nPlease ask your host to give this user the ALTER, CREATE, and DROP privileges.", true);
2701
2702
	$check = @file_exists($modSettings['theme_dir'] . '/index.template.php')
2703
		&& @file_exists($sourcedir . '/QueryString.php')
2704
		&& @file_exists($sourcedir . '/ManageBoards.php');
2705
	if (!$check && !isset($modSettings['smfVersion']))
2706
		print_error('Error: Some files are missing or out-of-date.', true);
2707
2708
	// Do a quick version spot check.
2709
	$temp = substr(@implode('', @file($boarddir . '/index.php')), 0, 4096);
0 ignored issues
show
It seems like @file($boarddir . '/index.php') 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

2709
	$temp = substr(@implode('', /** @scrutinizer ignore-type */ @file($boarddir . '/index.php')), 0, 4096);
Loading history...
2710
	preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $temp, $match);
2711
	if (empty($match[1]) || (trim($match[1]) != SMF_VERSION))
2712
		print_error('Error: Some files have not yet been updated properly.');
2713
2714
	// Make sure Settings.php is writable.
2715
	quickFileWritable($boarddir . '/Settings.php');
0 ignored issues
show
The function quickFileWritable was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

2715
	/** @scrutinizer ignore-call */ 
2716
 quickFileWritable($boarddir . '/Settings.php');
Loading history...
2716
	if (!is_writable($boarddir . '/Settings.php'))
2717
		print_error('Error: Unable to obtain write access to "Settings.php".', true);
2718
2719
	// Make sure Settings_bak.php is writable.
2720
	quickFileWritable($boarddir . '/Settings_bak.php');
2721
	if (!is_writable($boarddir . '/Settings_bak.php'))
2722
		print_error('Error: Unable to obtain write access to "Settings_bak.php".');
2723
2724
	if (isset($modSettings['agreement']) && (!is_writable($boarddir) || file_exists($boarddir . '/agreement.txt')) && !is_writable($boarddir . '/agreement.txt'))
2725
		print_error('Error: Unable to obtain write access to "agreement.txt".');
2726
	elseif (isset($modSettings['agreement']))
2727
	{
2728
		$fp = fopen($boarddir . '/agreement.txt', 'w');
2729
		fwrite($fp, $modSettings['agreement']);
2730
		fclose($fp);
2731
	}
2732
2733
	// Make sure Themes is writable.
2734
	quickFileWritable($modSettings['theme_dir']);
2735
2736
	if (!is_writable($modSettings['theme_dir']) && !isset($modSettings['smfVersion']))
2737
		print_error('Error: Unable to obtain write access to "Themes".');
2738
2739
	// Make sure cache directory exists and is writable!
2740
	$cachedir_temp = empty($cachedir) ? $boarddir . '/cache' : $cachedir;
2741
	if (!file_exists($cachedir_temp))
2742
		@mkdir($cachedir_temp);
2743
2744
	// Make sure the cache temp dir is writable.
2745
	quickFileWritable($cachedir_temp);
2746
2747
	if (!is_writable($cachedir_temp))
2748
		print_error('Error: Unable to obtain write access to "cache".', true);
2749
2750
	// Make sure db_last_error.php is writable.
2751
	quickFileWritable($cachedir_temp . '/db_last_error.php');
2752
	if (!is_writable($cachedir_temp . '/db_last_error.php'))
2753
		print_error('Error: Unable to obtain write access to "db_last_error.php".');
2754
2755
	if (!file_exists($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php'))
2756
		print_error('Error: Unable to find language files!', true);
2757
	else
2758
	{
2759
		$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php')), 0, 4096);
2760
		preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
2761
2762
		if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
2763
			print_error('Error: Language files out of date.', true);
2764
		if (!file_exists($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php'))
2765
			print_error('Error: Install language is missing for selected language.', true);
2766
2767
		// Otherwise include it!
2768
		require_once($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php');
2769
	}
2770
2771
	// Do we need to add this setting?
2772
	$need_settings_update = empty($modSettings['custom_avatar_dir']);
2773
2774
	$custom_av_dir = !empty($modSettings['custom_avatar_dir']) ? $modSettings['custom_avatar_dir'] : $boarddir . '/custom_avatar';
2775
	$custom_av_url = !empty($modSettings['custom_avatar_url']) ? $modSettings['custom_avatar_url'] : $boardurl . '/custom_avatar';
2776
2777
	// This little fellow has to cooperate...
2778
	quickFileWritable($custom_av_dir);
2779
2780
	// Are we good now?
2781
	if (!is_writable($custom_av_dir))
2782
		print_error(sprintf($txt['error_dir_not_writable'], $custom_av_dir));
2783
	elseif ($need_settings_update)
2784
	{
2785
		if (!function_exists('cache_put_data'))
2786
			require_once($sourcedir . '/Load.php');
2787
2788
		updateSettings(array('custom_avatar_dir' => $custom_av_dir));
2789
		updateSettings(array('custom_avatar_url' => $custom_av_url));
2790
	}
2791
2792
	// Make sure we skip the HTML for login.
2793
	$_POST['upcont'] = true;
2794
	$upcontext['current_step'] = 1;
2795
}
2796
2797
/**
2798
 * Handles converting your database to UTF-8
2799
 */
2800
function ConvertUtf8()
2801
{
2802
	global $upcontext, $db_character_set, $sourcedir, $smcFunc, $modSettings, $language;
2803
	global $db_prefix, $db_type, $command_line, $support_js, $txt;
2804
2805
	// Done it already?
2806
	if (!empty($_POST['utf8_done']))
2807
	{
2808
		if ($command_line)
2809
			return DeleteUpgrade();
2810
		else
2811
			return true;
2812
	}
2813
	// First make sure they aren't already on UTF-8 before we go anywhere...
2814
	if ($db_type == 'postgresql' || ($db_character_set === 'utf8' && !empty($modSettings['global_character_set']) && $modSettings['global_character_set'] === 'UTF-8'))
2815
	{
2816
		$smcFunc['db_insert']('replace',
2817
			'{db_prefix}settings',
2818
			array('variable' => 'string', 'value' => 'string'),
2819
			array(array('global_character_set', 'UTF-8')),
2820
			array('variable')
2821
		);
2822
2823
		if ($command_line)
2824
			return DeleteUpgrade();
2825
		else
2826
			return true;
2827
	}
2828
	else
2829
	{
2830
		$upcontext['page_title'] = $txt['converting_utf8'];
2831
		$upcontext['sub_template'] = isset($_GET['xml']) ? 'convert_xml' : 'convert_utf8';
2832
2833
		// The character sets used in SMF's language files with their db equivalent.
2834
		$charsets = array(
2835
			// Armenian
2836
			'armscii8' => 'armscii8',
2837
			// Chinese-traditional.
2838
			'big5' => 'big5',
2839
			// Chinese-simplified.
2840
			'gbk' => 'gbk',
2841
			// West European.
2842
			'ISO-8859-1' => 'latin1',
2843
			// Romanian.
2844
			'ISO-8859-2' => 'latin2',
2845
			// Turkish.
2846
			'ISO-8859-9' => 'latin5',
2847
			// Latvian
2848
			'ISO-8859-13' => 'latin7',
2849
			// West European with Euro sign.
2850
			'ISO-8859-15' => 'latin9',
2851
			// Thai.
2852
			'tis-620' => 'tis620',
2853
			// Persian, Chinese, etc.
2854
			'UTF-8' => 'utf8',
2855
			// Russian.
2856
			'windows-1251' => 'cp1251',
2857
			// Greek.
2858
			'windows-1253' => 'utf8',
2859
			// Hebrew.
2860
			'windows-1255' => 'utf8',
2861
			// Arabic.
2862
			'windows-1256' => 'cp1256',
2863
		);
2864
2865
		// Get a list of character sets supported by your MySQL server.
2866
		$request = $smcFunc['db_query']('', '
2867
			SHOW CHARACTER SET',
2868
			array(
2869
			)
2870
		);
2871
		$db_charsets = array();
2872
		while ($row = $smcFunc['db_fetch_assoc']($request))
2873
			$db_charsets[] = $row['Charset'];
2874
2875
		$smcFunc['db_free_result']($request);
2876
2877
		// Character sets supported by both MySQL and SMF's language files.
2878
		$charsets = array_intersect($charsets, $db_charsets);
2879
2880
		// Use the messages.body column as indicator for the database charset.
2881
		$request = $smcFunc['db_query']('', '
2882
			SHOW FULL COLUMNS
2883
			FROM {db_prefix}messages
2884
			LIKE {string:body_like}',
2885
			array(
2886
				'body_like' => 'body',
2887
			)
2888
		);
2889
		$column_info = $smcFunc['db_fetch_assoc']($request);
2890
		$smcFunc['db_free_result']($request);
2891
2892
		// A collation looks like latin1_swedish. We only need the character set.
2893
		list($upcontext['database_charset']) = explode('_', $column_info['Collation']);
2894
		$upcontext['database_charset'] = in_array($upcontext['database_charset'], $charsets) ? array_search($upcontext['database_charset'], $charsets) : $upcontext['database_charset'];
2895
2896
		// Detect whether a fulltext index is set.
2897
		$request = $smcFunc['db_query']('', '
2898
			SHOW INDEX
2899
			FROM {db_prefix}messages',
2900
			array(
2901
			)
2902
		);
2903
2904
		$upcontext['dropping_index'] = false;
2905
2906
		// If there's a fulltext index, we need to drop it first...
2907
		if ($request !== false || $smcFunc['db_num_rows']($request) != 0)
2908
		{
2909
			while ($row = $smcFunc['db_fetch_assoc']($request))
2910
				if ($row['Column_name'] == 'body' && (isset($row['Index_type']) && $row['Index_type'] == 'FULLTEXT' || isset($row['Comment']) && $row['Comment'] == 'FULLTEXT'))
2911
					$upcontext['fulltext_index'][] = $row['Key_name'];
2912
			$smcFunc['db_free_result']($request);
2913
2914
			if (isset($upcontext['fulltext_index']))
2915
				$upcontext['fulltext_index'] = array_unique($upcontext['fulltext_index']);
2916
		}
2917
2918
		// Drop it and make a note...
2919
		if (!empty($upcontext['fulltext_index']))
2920
		{
2921
			$upcontext['dropping_index'] = true;
2922
2923
			$smcFunc['db_query']('', '
2924
				ALTER TABLE {db_prefix}messages
2925
				DROP INDEX ' . implode(',
2926
				DROP INDEX ', $upcontext['fulltext_index']),
2927
				array(
2928
					'db_error_skip' => true,
2929
				)
2930
			);
2931
2932
			// Update the settings table
2933
			$smcFunc['db_insert']('replace',
2934
				'{db_prefix}settings',
2935
				array('variable' => 'string', 'value' => 'string'),
2936
				array('db_search_index', ''),
2937
				array('variable')
2938
			);
2939
		}
2940
2941
		// Figure out what charset we should be converting from...
2942
		$lang_charsets = array(
2943
			'arabic' => 'windows-1256',
2944
			'armenian_east' => 'armscii-8',
2945
			'armenian_west' => 'armscii-8',
2946
			'azerbaijani_latin' => 'ISO-8859-9',
2947
			'bangla' => 'UTF-8',
2948
			'belarusian' => 'ISO-8859-5',
2949
			'bulgarian' => 'windows-1251',
2950
			'cambodian' => 'UTF-8',
2951
			'chinese_simplified' => 'gbk',
2952
			'chinese_traditional' => 'big5',
2953
			'croation' => 'ISO-8859-2',
2954
			'czech' => 'ISO-8859-2',
2955
			'czech_informal' => 'ISO-8859-2',
2956
			'english_pirate' => 'UTF-8',
2957
			'esperanto' => 'ISO-8859-3',
2958
			'estonian' => 'ISO-8859-15',
2959
			'filipino_tagalog' => 'UTF-8',
2960
			'filipino_vasayan' => 'UTF-8',
2961
			'georgian' => 'UTF-8',
2962
			'greek' => 'ISO-8859-3',
2963
			'hebrew' => 'windows-1255',
2964
			'hungarian' => 'ISO-8859-2',
2965
			'irish' => 'UTF-8',
2966
			'japanese' => 'UTF-8',
2967
			'khmer' => 'UTF-8',
2968
			'korean' => 'UTF-8',
2969
			'kurdish_kurmanji' => 'ISO-8859-9',
2970
			'kurdish_sorani' => 'windows-1256',
2971
			'lao' => 'tis-620',
2972
			'latvian' => 'ISO-8859-13',
2973
			'lithuanian' => 'ISO-8859-4',
2974
			'macedonian' => 'UTF-8',
2975
			'malayalam' => 'UTF-8',
2976
			'mongolian' => 'UTF-8',
2977
			'nepali' => 'UTF-8',
2978
			'persian' => 'UTF-8',
2979
			'polish' => 'ISO-8859-2',
2980
			'romanian' => 'ISO-8859-2',
2981
			'russian' => 'windows-1252',
2982
			'sakha' => 'UTF-8',
2983
			'serbian_cyrillic' => 'ISO-8859-5',
2984
			'serbian_latin' => 'ISO-8859-2',
2985
			'sinhala' => 'UTF-8',
2986
			'slovak' => 'ISO-8859-2',
2987
			'slovenian' => 'ISO-8859-2',
2988
			'telugu' => 'UTF-8',
2989
			'thai' => 'tis-620',
2990
			'turkish' => 'ISO-8859-9',
2991
			'turkmen' => 'ISO-8859-9',
2992
			'ukranian' => 'windows-1251',
2993
			'urdu' => 'UTF-8',
2994
			'uzbek_cyrillic' => 'ISO-8859-5',
2995
			'uzbek_latin' => 'ISO-8859-5',
2996
			'vietnamese' => 'UTF-8',
2997
			'yoruba' => 'UTF-8'
2998
		);
2999
3000
		// Default to ISO-8859-1 unless we detected another supported charset
3001
		$upcontext['charset_detected'] = (isset($lang_charsets[$language]) && isset($charsets[strtr(strtolower($upcontext['charset_detected']), array('utf' => 'UTF', 'iso' => 'ISO'))])) ? $lang_charsets[$language] : 'ISO-8859-1';
3002
3003
		$upcontext['charset_list'] = array_keys($charsets);
3004
3005
		// Translation table for the character sets not native for MySQL.
3006
		$translation_tables = array(
3007
			'windows-1255' => array(
3008
				'0x81' => '\'\'',		'0x8A' => '\'\'',		'0x8C' => '\'\'',
3009
				'0x8D' => '\'\'',		'0x8E' => '\'\'',		'0x8F' => '\'\'',
3010
				'0x90' => '\'\'',		'0x9A' => '\'\'',		'0x9C' => '\'\'',
3011
				'0x9D' => '\'\'',		'0x9E' => '\'\'',		'0x9F' => '\'\'',
3012
				'0xCA' => '\'\'',		'0xD9' => '\'\'',		'0xDA' => '\'\'',
3013
				'0xDB' => '\'\'',		'0xDC' => '\'\'',		'0xDD' => '\'\'',
3014
				'0xDE' => '\'\'',		'0xDF' => '\'\'',		'0xFB' => '0xD792',
3015
				'0xFC' => '0xE282AC',		'0xFF' => '0xD6B2',		'0xC2' => '0xFF',
3016
				'0x80' => '0xFC',		'0xE2' => '0xFB',		'0xA0' => '0xC2A0',
3017
				'0xA1' => '0xC2A1',		'0xA2' => '0xC2A2',		'0xA3' => '0xC2A3',
3018
				'0xA5' => '0xC2A5',		'0xA6' => '0xC2A6',		'0xA7' => '0xC2A7',
3019
				'0xA8' => '0xC2A8',		'0xA9' => '0xC2A9',		'0xAB' => '0xC2AB',
3020
				'0xAC' => '0xC2AC',		'0xAD' => '0xC2AD',		'0xAE' => '0xC2AE',
3021
				'0xAF' => '0xC2AF',		'0xB0' => '0xC2B0',		'0xB1' => '0xC2B1',
3022
				'0xB2' => '0xC2B2',		'0xB3' => '0xC2B3',		'0xB4' => '0xC2B4',
3023
				'0xB5' => '0xC2B5',		'0xB6' => '0xC2B6',		'0xB7' => '0xC2B7',
3024
				'0xB8' => '0xC2B8',		'0xB9' => '0xC2B9',		'0xBB' => '0xC2BB',
3025
				'0xBC' => '0xC2BC',		'0xBD' => '0xC2BD',		'0xBE' => '0xC2BE',
3026
				'0xBF' => '0xC2BF',		'0xD7' => '0xD7B3',		'0xD1' => '0xD781',
3027
				'0xD4' => '0xD7B0',		'0xD5' => '0xD7B1',		'0xD6' => '0xD7B2',
3028
				'0xE0' => '0xD790',		'0xEA' => '0xD79A',		'0xEC' => '0xD79C',
3029
				'0xED' => '0xD79D',		'0xEE' => '0xD79E',		'0xEF' => '0xD79F',
3030
				'0xF0' => '0xD7A0',		'0xF1' => '0xD7A1',		'0xF2' => '0xD7A2',
3031
				'0xF3' => '0xD7A3',		'0xF5' => '0xD7A5',		'0xF6' => '0xD7A6',
3032
				'0xF7' => '0xD7A7',		'0xF8' => '0xD7A8',		'0xF9' => '0xD7A9',
3033
				'0x82' => '0xE2809A',	'0x84' => '0xE2809E',	'0x85' => '0xE280A6',
3034
				'0x86' => '0xE280A0',	'0x87' => '0xE280A1',	'0x89' => '0xE280B0',
3035
				'0x8B' => '0xE280B9',	'0x93' => '0xE2809C',	'0x94' => '0xE2809D',
3036
				'0x95' => '0xE280A2',	'0x97' => '0xE28094',	'0x99' => '0xE284A2',
3037
				'0xC0' => '0xD6B0',		'0xC1' => '0xD6B1',		'0xC3' => '0xD6B3',
3038
				'0xC4' => '0xD6B4',		'0xC5' => '0xD6B5',		'0xC6' => '0xD6B6',
3039
				'0xC7' => '0xD6B7',		'0xC8' => '0xD6B8',		'0xC9' => '0xD6B9',
3040
				'0xCB' => '0xD6BB',		'0xCC' => '0xD6BC',		'0xCD' => '0xD6BD',
3041
				'0xCE' => '0xD6BE',		'0xCF' => '0xD6BF',		'0xD0' => '0xD780',
3042
				'0xD2' => '0xD782',		'0xE3' => '0xD793',		'0xE4' => '0xD794',
3043
				'0xE5' => '0xD795',		'0xE7' => '0xD797',		'0xE9' => '0xD799',
3044
				'0xFD' => '0xE2808E',	'0xFE' => '0xE2808F',	'0x92' => '0xE28099',
3045
				'0x83' => '0xC692',		'0xD3' => '0xD783',		'0x88' => '0xCB86',
3046
				'0x98' => '0xCB9C',		'0x91' => '0xE28098',	'0x96' => '0xE28093',
3047
				'0xBA' => '0xC3B7',		'0x9B' => '0xE280BA',	'0xAA' => '0xC397',
3048
				'0xA4' => '0xE282AA',	'0xE1' => '0xD791',		'0xE6' => '0xD796',
3049
				'0xE8' => '0xD798',		'0xEB' => '0xD79B',		'0xF4' => '0xD7A4',
3050
				'0xFA' => '0xD7AA',
3051
			),
3052
			'windows-1253' => array(
3053
				'0x81' => '\'\'',			'0x88' => '\'\'',			'0x8A' => '\'\'',
3054
				'0x8C' => '\'\'',			'0x8D' => '\'\'',			'0x8E' => '\'\'',
3055
				'0x8F' => '\'\'',			'0x90' => '\'\'',			'0x98' => '\'\'',
3056
				'0x9A' => '\'\'',			'0x9C' => '\'\'',			'0x9D' => '\'\'',
3057
				'0x9E' => '\'\'',			'0x9F' => '\'\'',			'0xAA' => '\'\'',
3058
				'0xD2' => '0xE282AC',			'0xFF' => '0xCE92',			'0xCE' => '0xCE9E',
3059
				'0xB8' => '0xCE88',		'0xBA' => '0xCE8A',		'0xBC' => '0xCE8C',
3060
				'0xBE' => '0xCE8E',		'0xBF' => '0xCE8F',		'0xC0' => '0xCE90',
3061
				'0xC8' => '0xCE98',		'0xCA' => '0xCE9A',		'0xCC' => '0xCE9C',
3062
				'0xCD' => '0xCE9D',		'0xCF' => '0xCE9F',		'0xDA' => '0xCEAA',
3063
				'0xE8' => '0xCEB8',		'0xEA' => '0xCEBA',		'0xEC' => '0xCEBC',
3064
				'0xEE' => '0xCEBE',		'0xEF' => '0xCEBF',		'0xC2' => '0xFF',
3065
				'0xBD' => '0xC2BD',		'0xED' => '0xCEBD',		'0xB2' => '0xC2B2',
3066
				'0xA0' => '0xC2A0',		'0xA3' => '0xC2A3',		'0xA4' => '0xC2A4',
3067
				'0xA5' => '0xC2A5',		'0xA6' => '0xC2A6',		'0xA7' => '0xC2A7',
3068
				'0xA8' => '0xC2A8',		'0xA9' => '0xC2A9',		'0xAB' => '0xC2AB',
3069
				'0xAC' => '0xC2AC',		'0xAD' => '0xC2AD',		'0xAE' => '0xC2AE',
3070
				'0xB0' => '0xC2B0',		'0xB1' => '0xC2B1',		'0xB3' => '0xC2B3',
3071
				'0xB5' => '0xC2B5',		'0xB6' => '0xC2B6',		'0xB7' => '0xC2B7',
3072
				'0xBB' => '0xC2BB',		'0xE2' => '0xCEB2',		'0x80' => '0xD2',
3073
				'0x82' => '0xE2809A',	'0x84' => '0xE2809E',	'0x85' => '0xE280A6',
3074
				'0x86' => '0xE280A0',	'0xA1' => '0xCE85',		'0xA2' => '0xCE86',
3075
				'0x87' => '0xE280A1',	'0x89' => '0xE280B0',	'0xB9' => '0xCE89',
3076
				'0x8B' => '0xE280B9',	'0x91' => '0xE28098',	'0x99' => '0xE284A2',
3077
				'0x92' => '0xE28099',	'0x93' => '0xE2809C',	'0x94' => '0xE2809D',
3078
				'0x95' => '0xE280A2',	'0x96' => '0xE28093',	'0x97' => '0xE28094',
3079
				'0x9B' => '0xE280BA',	'0xAF' => '0xE28095',	'0xB4' => '0xCE84',
3080
				'0xC1' => '0xCE91',		'0xC3' => '0xCE93',		'0xC4' => '0xCE94',
3081
				'0xC5' => '0xCE95',		'0xC6' => '0xCE96',		'0x83' => '0xC692',
3082
				'0xC7' => '0xCE97',		'0xC9' => '0xCE99',		'0xCB' => '0xCE9B',
3083
				'0xD0' => '0xCEA0',		'0xD1' => '0xCEA1',		'0xD3' => '0xCEA3',
3084
				'0xD4' => '0xCEA4',		'0xD5' => '0xCEA5',		'0xD6' => '0xCEA6',
3085
				'0xD7' => '0xCEA7',		'0xD8' => '0xCEA8',		'0xD9' => '0xCEA9',
3086
				'0xDB' => '0xCEAB',		'0xDC' => '0xCEAC',		'0xDD' => '0xCEAD',
3087
				'0xDE' => '0xCEAE',		'0xDF' => '0xCEAF',		'0xE0' => '0xCEB0',
3088
				'0xE1' => '0xCEB1',		'0xE3' => '0xCEB3',		'0xE4' => '0xCEB4',
3089
				'0xE5' => '0xCEB5',		'0xE6' => '0xCEB6',		'0xE7' => '0xCEB7',
3090
				'0xE9' => '0xCEB9',		'0xEB' => '0xCEBB',		'0xF0' => '0xCF80',
3091
				'0xF1' => '0xCF81',		'0xF2' => '0xCF82',		'0xF3' => '0xCF83',
3092
				'0xF4' => '0xCF84',		'0xF5' => '0xCF85',		'0xF6' => '0xCF86',
3093
				'0xF7' => '0xCF87',		'0xF8' => '0xCF88',		'0xF9' => '0xCF89',
3094
				'0xFA' => '0xCF8A',		'0xFB' => '0xCF8B',		'0xFC' => '0xCF8C',
3095
				'0xFD' => '0xCF8D',		'0xFE' => '0xCF8E',
3096
			),
3097
		);
3098
3099
		// Make some preparations.
3100
		if (isset($translation_tables[$upcontext['charset_detected']]))
3101
		{
3102
			$replace = '%field%';
3103
3104
			// Build a huge REPLACE statement...
3105
			foreach ($translation_tables[$upcontext['charset_detected']] as $from => $to)
3106
				$replace = 'REPLACE(' . $replace . ', ' . $from . ', ' . $to . ')';
3107
		}
3108
3109
		// Get a list of table names ahead of time... This makes it easier to set our substep and such
3110
		db_extend();
3111
		$queryTables = $smcFunc['db_list_tables'](false, $db_prefix . '%');
3112
3113
		$upcontext['table_count'] = count($queryTables);
3114
3115
		// What ones have we already done?
3116
		foreach ($queryTables as $id => $table)
3117
			if ($id < $_GET['substep'])
3118
				$upcontext['previous_tables'][] = $table;
3119
3120
		$upcontext['cur_table_num'] = $_GET['substep'];
3121
		$upcontext['cur_table_name'] = str_replace($db_prefix, '', $queryTables[$_GET['substep']]);
3122
		$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
3123
3124
		// Make sure we're ready & have painted the template before proceeding
3125
		if ($support_js && !isset($_GET['xml']))
3126
		{
3127
			$_GET['substep'] = 0;
3128
			return false;
3129
		}
3130
3131
		// We want to start at the first table.
3132
		for ($substep = $_GET['substep'], $n = count($queryTables); $substep < $n; $substep++)
3133
		{
3134
			$table = $queryTables[$substep];
3135
3136
			$getTableStatus = $smcFunc['db_query']('', '
3137
				SHOW TABLE STATUS
3138
				LIKE {string:table_name}',
3139
				array(
3140
					'table_name' => str_replace('_', '\_', $table)
3141
				)
3142
			);
3143
3144
			// Only one row so we can just fetch_assoc and free the result...
3145
			$table_info = $smcFunc['db_fetch_assoc']($getTableStatus);
3146
			$smcFunc['db_free_result']($getTableStatus);
3147
3148
			$upcontext['cur_table_name'] = str_replace($db_prefix, '', (isset($queryTables[$substep + 1]) ? $queryTables[$substep + 1] : $queryTables[$substep]));
3149
			$upcontext['cur_table_num'] = $substep + 1;
3150
			$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
3151
3152
			// Do we need to pause?
3153
			nextSubstep($substep);
3154
3155
			// Just to make sure it doesn't time out.
3156
			if (function_exists('apache_reset_timeout'))
3157
				@apache_reset_timeout();
3158
3159
			$table_charsets = array();
3160
3161
			// Loop through each column.
3162
			$queryColumns = $smcFunc['db_query']('', '
3163
				SHOW FULL COLUMNS
3164
				FROM ' . $table_info['Name'],
3165
				array(
3166
				)
3167
			);
3168
			while ($column_info = $smcFunc['db_fetch_assoc']($queryColumns))
3169
			{
3170
				// Only text'ish columns have a character set and need converting.
3171
				if (strpos($column_info['Type'], 'text') !== false || strpos($column_info['Type'], 'char') !== false)
3172
				{
3173
					$collation = empty($column_info['Collation']) || $column_info['Collation'] === 'NULL' ? $table_info['Collation'] : $column_info['Collation'];
3174
					if (!empty($collation) && $collation !== 'NULL')
3175
					{
3176
						list($charset) = explode('_', $collation);
3177
3178
						// Build structure of columns to operate on organized by charset; only operate on columns not yet utf8
3179
						if ($charset != 'utf8')
3180
						{
3181
							if (!isset($table_charsets[$charset]))
3182
								$table_charsets[$charset] = array();
3183
3184
							$table_charsets[$charset][] = $column_info;
3185
						}
3186
					}
3187
				}
3188
			}
3189
			$smcFunc['db_free_result']($queryColumns);
3190
3191
			// Only change the non-utf8 columns identified above
3192
			if (count($table_charsets) > 0)
3193
			{
3194
				$updates_blob = '';
3195
				$updates_text = '';
3196
				foreach ($table_charsets as $charset => $columns)
3197
				{
3198
					if ($charset !== $charsets[$upcontext['charset_detected']])
3199
					{
3200
						foreach ($columns as $column)
3201
						{
3202
							$updates_blob .= '
3203
								CHANGE COLUMN `' . $column['Field'] . '` `' . $column['Field'] . '` ' . strtr($column['Type'], array('text' => 'blob', 'char' => 'binary')) . ($column['Null'] === 'YES' ? ' NULL' : ' NOT NULL') . (strpos($column['Type'], 'char') === false ? '' : ' default \'' . $column['Default'] . '\'') . ',';
3204
							$updates_text .= '
3205
								CHANGE COLUMN `' . $column['Field'] . '` `' . $column['Field'] . '` ' . $column['Type'] . ' CHARACTER SET ' . $charsets[$upcontext['charset_detected']] . ($column['Null'] === 'YES' ? '' : ' NOT NULL') . (strpos($column['Type'], 'char') === false ? '' : ' default \'' . $column['Default'] . '\'') . ',';
3206
						}
3207
					}
3208
				}
3209
3210
				// Change the columns to binary form.
3211
				$smcFunc['db_query']('', '
3212
					ALTER TABLE {raw:table_name}{raw:updates_blob}',
3213
					array(
3214
						'table_name' => $table_info['Name'],
3215
						'updates_blob' => substr($updates_blob, 0, -1),
3216
					)
3217
				);
3218
3219
				// Convert the character set if MySQL has no native support for it.
3220
				if (isset($translation_tables[$upcontext['charset_detected']]))
3221
				{
3222
					$update = '';
3223
					foreach ($table_charsets as $charset => $columns)
3224
						foreach ($columns as $column)
3225
							$update .= '
3226
								' . $column['Field'] . ' = ' . strtr($replace, array('%field%' => $column['Field'])) . ',';
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $replace does not seem to be defined for all execution paths leading up to this point.
Loading history...
3227
3228
					$smcFunc['db_query']('', '
3229
						UPDATE {raw:table_name}
3230
						SET {raw:updates}',
3231
						array(
3232
							'table_name' => $table_info['Name'],
3233
							'updates' => substr($update, 0, -1),
3234
						)
3235
					);
3236
				}
3237
3238
				// Change the columns back, but with the proper character set.
3239
				$smcFunc['db_query']('', '
3240
					ALTER TABLE {raw:table_name}{raw:updates_text}',
3241
					array(
3242
						'table_name' => $table_info['Name'],
3243
						'updates_text' => substr($updates_text, 0, -1),
3244
					)
3245
				);
3246
			}
3247
3248
			// Now do the actual conversion (if still needed).
3249
			if ($charsets[$upcontext['charset_detected']] !== 'utf8')
3250
			{
3251
				if ($command_line)
3252
					echo 'Converting table ' . $table_info['Name'] . ' to UTF-8...';
3253
3254
				$smcFunc['db_query']('', '
3255
					ALTER TABLE {raw:table_name}
3256
					CONVERT TO CHARACTER SET utf8',
3257
					array(
3258
						'table_name' => $table_info['Name'],
3259
					)
3260
				);
3261
3262
				if ($command_line)
3263
					echo " done.\n";
3264
			}
3265
			// If this is XML to keep it nice for the user do one table at a time anyway!
3266
			if (isset($_GET['xml']) && $upcontext['cur_table_num'] < $upcontext['table_count'])
3267
				return upgradeExit();
0 ignored issues
show
Are you sure the usage of upgradeExit() is correct as it seems to always return null.

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

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

}

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

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

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

Loading history...
3268
		}
3269
3270
		$prev_charset = empty($translation_tables[$upcontext['charset_detected']]) ? $charsets[$upcontext['charset_detected']] : $translation_tables[$upcontext['charset_detected']];
3271
3272
		$smcFunc['db_insert']('replace',
3273
			'{db_prefix}settings',
3274
			array('variable' => 'string', 'value' => 'string'),
3275
			array(array('global_character_set', 'UTF-8'), array('previousCharacterSet', $prev_charset)),
3276
			array('variable')
3277
		);
3278
3279
		// Store it in Settings.php too because it's needed before db connection.
3280
		// Hopefully this works...
3281
		require_once($sourcedir . '/Subs.php');
3282
		require_once($sourcedir . '/Subs-Admin.php');
3283
		updateSettingsFile(array('db_character_set' => 'utf8'));
3284
3285
		// The conversion might have messed up some serialized strings. Fix them!
3286
		$request = $smcFunc['db_query']('', '
3287
			SELECT id_action, extra
3288
			FROM {db_prefix}log_actions
3289
			WHERE action IN ({string:remove}, {string:delete})',
3290
			array(
3291
				'remove' => 'remove',
3292
				'delete' => 'delete',
3293
			)
3294
		);
3295
		while ($row = $smcFunc['db_fetch_assoc']($request))
3296
		{
3297
			if (@safe_unserialize($row['extra']) === false && preg_match('~^(a:3:{s:5:"topic";i:\d+;s:7:"subject";s:)(\d+):"(.+)"(;s:6:"member";s:5:"\d+";})$~', $row['extra'], $matches) === 1)
3298
				$smcFunc['db_query']('', '
3299
					UPDATE {db_prefix}log_actions
3300
					SET extra = {string:extra}
3301
					WHERE id_action = {int:current_action}',
3302
					array(
3303
						'current_action' => $row['id_action'],
3304
						'extra' => $matches[1] . strlen($matches[3]) . ':"' . $matches[3] . '"' . $matches[4],
3305
					)
3306
				);
3307
		}
3308
		$smcFunc['db_free_result']($request);
3309
3310
		if ($upcontext['dropping_index'] && $command_line)
3311
		{
3312
			echo "\n" . '', $txt['upgrade_fulltext_error'], '';
3313
			flush();
3314
		}
3315
	}
3316
3317
	// Make sure we move on!
3318
	if ($command_line)
3319
		return DeleteUpgrade();
3320
3321
	$_GET['substep'] = 0;
3322
	return false;
3323
}
3324
3325
/**
3326
 * Wrapper for unserialize that attempts to repair corrupted serialized data strings
3327
 *
3328
 * @param string $string Serialized data that may or may not have been corrupted
3329
 * @return string|bool The unserialized data, or false if the repair failed
3330
 */
3331
function upgrade_unserialize($string)
3332
{
3333
	if (!is_string($string))
0 ignored issues
show
The condition is_string($string) is always true.
Loading history...
3334
	{
3335
		$data = false;
3336
	}
3337
	// Might be JSON already.
3338
	elseif (strpos($string, '{') === 0)
3339
	{
3340
		$data = @json_decode($string, true);
3341
3342
		if (is_null($data))
3343
			$data = false;
3344
	}
3345
	elseif (in_array(substr($string, 0, 2), array('b:', 'i:', 'd:', 's:', 'a:', 'N;')))
3346
	{
3347
		$data = @safe_unserialize($string);
3348
3349
		// The serialized data is broken.
3350
		if ($data === false)
3351
		{
3352
			// This bit fixes incorrect string lengths, which can happen if the character encoding was changed (e.g. conversion to UTF-8)
3353
			$new_string = preg_replace_callback(
0 ignored issues
show
The assignment to $new_string is dead and can be removed.
Loading history...
3354
				'~\bs:(\d+):"(.*?)";(?=$|[bidsaO]:|[{}}]|N;)~s',
3355
				function ($matches)
3356
				{
3357
					return 's:' . strlen($matches[2]) . ':"' . $matches[2] . '";';
3358
				},
3359
				$string
3360
			);
3361
3362
			// @todo Add more possible fixes here. For example, fix incorrect array lengths, try to handle truncated strings gracefully, etc.
3363
3364
			// Did it work?
3365
			$data = @safe_unserialize($string);
3366
		}
3367
	}
3368
	// Just a plain string, then.
3369
	else
3370
		$data = false;
3371
3372
	return $data;
3373
}
3374
3375
function serialize_to_json()
3376
{
3377
	global $command_line, $smcFunc, $modSettings, $sourcedir, $upcontext, $support_js, $txt;
3378
3379
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'serialize_json_xml' : 'serialize_json';
3380
	// First thing's first - did we already do this?
3381
	if (!empty($modSettings['json_done']))
3382
	{
3383
		if ($command_line)
3384
			return ConvertUtf8();
3385
		else
3386
			return true;
3387
	}
3388
3389
	// Needed when writing settings
3390
	if (!function_exists('cache_put_data'))
3391
		require_once($sourcedir . '/Load.php');
3392
3393
	// Done it already - js wise?
3394
	if (!empty($_POST['json_done']))
3395
	{
3396
		updateSettings(array('json_done' => true));
3397
		return true;
3398
	}
3399
3400
	// List of tables affected by this function
3401
	// name => array('key', col1[,col2|true[,col3]])
3402
	// If 3rd item in array is true, it indicates that col1 could be empty...
3403
	$tables = array(
3404
		'background_tasks' => array('id_task', 'task_data'),
3405
		'log_actions' => array('id_action', 'extra'),
3406
		'log_online' => array('session', 'url'),
3407
		'log_packages' => array('id_install', 'db_changes', 'failed_steps', 'credits'),
3408
		'log_spider_hits' => array('id_hit', 'url'),
3409
		'log_subscribed' => array('id_sublog', 'pending_details'),
3410
		'pm_rules' => array('id_rule', 'criteria', 'actions'),
3411
		'qanda' => array('id_question', 'answers'),
3412
		'subscriptions' => array('id_subscribe', 'cost'),
3413
		'user_alerts' => array('id_alert', 'extra', true),
3414
		'user_drafts' => array('id_draft', 'to_list', true),
3415
		// These last two are a bit different - we'll handle those separately
3416
		'settings' => array(),
3417
		'themes' => array()
3418
	);
3419
3420
	// Set up some context stuff...
3421
	// Because we're not using numeric indices, we need this to figure out the current table name...
3422
	$keys = array_keys($tables);
3423
3424
	$upcontext['page_title'] = $txt['converting_json'];
3425
	$upcontext['table_count'] = count($keys);
3426
	$upcontext['cur_table_num'] = $_GET['substep'];
3427
	$upcontext['cur_table_name'] = isset($keys[$_GET['substep']]) ? $keys[$_GET['substep']] : $keys[0];
3428
	$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
3429
3430
	foreach ($keys as $id => $table)
3431
		if ($id < $_GET['substep'])
3432
			$upcontext['previous_tables'][] = $table;
3433
3434
	if ($command_line)
3435
		echo 'Converting data from serialize() to json_encode().';
3436
3437
	if (!$support_js || isset($_GET['xml']))
3438
	{
3439
		// Fix the data in each table
3440
		for ($substep = $_GET['substep']; $substep < $upcontext['table_count']; $substep++)
3441
		{
3442
			$upcontext['cur_table_name'] = isset($keys[$substep + 1]) ? $keys[$substep + 1] : $keys[$substep];
3443
			$upcontext['cur_table_num'] = $substep + 1;
3444
3445
			$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
3446
3447
			// Do we need to pause?
3448
			nextSubstep($substep);
3449
3450
			// Initialize a few things...
3451
			$where = '';
3452
			$vars = array();
3453
			$table = $keys[$substep];
3454
			$info = $tables[$table];
3455
3456
			// Now the fun - build our queries and all that fun stuff
3457
			if ($table == 'settings')
3458
			{
3459
				// Now a few settings...
3460
				$serialized_settings = array(
3461
					'attachment_basedirectories',
3462
					'attachmentUploadDir',
3463
					'cal_today_birthday',
3464
					'cal_today_event',
3465
					'cal_today_holiday',
3466
					'displayFields',
3467
					'last_attachments_directory',
3468
					'memberlist_cache',
3469
					'search_custom_index_config',
3470
					'spider_name_cache'
3471
				);
3472
3473
				// Loop through and fix these...
3474
				$new_settings = array();
3475
				if ($command_line)
3476
					echo "\n" . 'Fixing some settings...';
3477
3478
				foreach ($serialized_settings as $var)
3479
				{
3480
					if (isset($modSettings[$var]))
3481
					{
3482
						// Attempt to unserialize the setting
3483
						$temp = upgrade_unserialize($modSettings[$var]);
3484
3485
						if (!$temp && $command_line)
3486
							echo "\n - Failed to unserialize the '" . $var . "' setting. Skipping.";
3487
						elseif ($temp !== false)
3488
							$new_settings[$var] = json_encode($temp);
3489
					}
3490
				}
3491
3492
				// Update everything at once
3493
				updateSettings($new_settings, true);
3494
3495
				if ($command_line)
3496
					echo ' done.';
3497
			}
3498
			elseif ($table == 'themes')
3499
			{
3500
				// Finally, fix the admin prefs. Unfortunately this is stored per theme, but hopefully they only have one theme installed at this point...
3501
				$query = $smcFunc['db_query']('', '
3502
					SELECT id_member, id_theme, value FROM {db_prefix}themes
3503
					WHERE variable = {string:admin_prefs}',
3504
					array(
3505
						'admin_prefs' => 'admin_preferences'
3506
					)
3507
				);
3508
3509
				if ($smcFunc['db_num_rows']($query) != 0)
3510
				{
3511
					while ($row = $smcFunc['db_fetch_assoc']($query))
3512
					{
3513
						$temp = upgrade_unserialize($row['value']);
3514
3515
						if ($command_line)
3516
						{
3517
							if ($temp === false)
3518
								echo "\n" . 'Unserialize of admin_preferences for user ' . $row['id_member'] . ' failed. Skipping.';
3519
							else
3520
								echo "\n" . 'Fixing admin preferences...';
3521
						}
3522
3523
						if ($temp !== false)
3524
						{
3525
							$row['value'] = json_encode($temp);
3526
3527
							// Even though we have all values from the table, UPDATE is still faster than REPLACE
3528
							$smcFunc['db_query']('', '
3529
								UPDATE {db_prefix}themes
3530
								SET value = {string:prefs}
3531
								WHERE id_theme = {int:theme}
3532
									AND id_member = {int:member}
3533
									AND variable = {string:admin_prefs}',
3534
								array(
3535
									'prefs' => $row['value'],
3536
									'theme' => $row['id_theme'],
3537
									'member' => $row['id_member'],
3538
									'admin_prefs' => 'admin_preferences'
3539
								)
3540
							);
3541
3542
							if ($command_line)
3543
								echo ' done.';
3544
						}
3545
					}
3546
3547
					$smcFunc['db_free_result']($query);
3548
				}
3549
			}
3550
			else
3551
			{
3552
				// First item is always the key...
3553
				$key = $info[0];
3554
				unset($info[0]);
3555
3556
				// Now we know what columns we have and such...
3557
				if (count($info) == 2 && $info[2] === true)
3558
				{
3559
					$col_select = $info[1];
3560
					$where = ' WHERE ' . $info[1] . ' != {empty}';
3561
				}
3562
				else
3563
				{
3564
					$col_select = implode(', ', $info);
3565
				}
3566
3567
				$query = $smcFunc['db_query']('', '
3568
					SELECT ' . $key . ', ' . $col_select . '
3569
					FROM {db_prefix}' . $table . $where,
3570
					array()
3571
				);
3572
3573
				if ($smcFunc['db_num_rows']($query) != 0)
3574
				{
3575
					if ($command_line)
3576
					{
3577
						echo "\n" . ' +++ Fixing the "' . $table . '" table...';
3578
						flush();
3579
					}
3580
3581
					while ($row = $smcFunc['db_fetch_assoc']($query))
3582
					{
3583
						$update = '';
3584
3585
						// We already know what our key is...
3586
						foreach ($info as $col)
3587
						{
3588
							if ($col !== true && $row[$col] != '')
3589
							{
3590
								$temp = upgrade_unserialize($row[$col]);
3591
3592
								// Oh well...
3593
								if ($temp === false)
3594
								{
3595
									$temp = array();
3596
3597
									if ($command_line)
3598
										echo "\nFailed to unserialize " . $row[$col] . ". Setting to empty value.\n";
3599
								}
3600
3601
								$row[$col] = json_encode($temp);
3602
3603
								// Build our SET string and variables array
3604
								$update .= (empty($update) ? '' : ', ') . $col . ' = {string:' . $col . '}';
3605
								$vars[$col] = $row[$col];
3606
							}
3607
						}
3608
3609
						$vars[$key] = $row[$key];
3610
3611
						// In a few cases, we might have empty data, so don't try to update in those situations...
3612
						if (!empty($update))
3613
						{
3614
							$smcFunc['db_query']('', '
3615
								UPDATE {db_prefix}' . $table . '
3616
								SET ' . $update . '
3617
								WHERE ' . $key . ' = {' . ($key == 'session' ? 'string' : 'int') . ':' . $key . '}',
3618
								$vars
3619
							);
3620
						}
3621
					}
3622
3623
					if ($command_line)
3624
						echo ' done.';
3625
3626
					// Free up some memory...
3627
					$smcFunc['db_free_result']($query);
3628
				}
3629
			}
3630
			// If this is XML to keep it nice for the user do one table at a time anyway!
3631
			if (isset($_GET['xml']))
3632
				return upgradeExit();
0 ignored issues
show
Are you sure the usage of upgradeExit() is correct as it seems to always return null.

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

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

}

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

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

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

Loading history...
3633
		}
3634
3635
		if ($command_line)
3636
		{
3637
			echo "\n" . 'Successful.' . "\n";
3638
			flush();
3639
		}
3640
		$upcontext['step_progress'] = 100;
3641
3642
		// Last but not least, insert a dummy setting so we don't have to do this again in the future...
3643
		updateSettings(array('json_done' => true));
3644
3645
		$_GET['substep'] = 0;
3646
		// Make sure we move on!
3647
		if ($command_line)
3648
			return ConvertUtf8();
3649
3650
		return true;
3651
	}
3652
3653
	// If this fails we just move on to deleting the upgrade anyway...
3654
	$_GET['substep'] = 0;
3655
	return false;
3656
}
3657
3658
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3659
						Templates are below this point
3660
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
3661
3662
// This is what is displayed if there's any chmod to be done. If not it returns nothing...
3663
function template_chmod()
3664
{
3665
	global $upcontext, $txt, $settings;
3666
3667
	// Don't call me twice!
3668
	if (!empty($upcontext['chmod_called']))
3669
		return;
3670
3671
	$upcontext['chmod_called'] = true;
3672
3673
	// Nothing?
3674
	if (empty($upcontext['chmod']['files']) && empty($upcontext['chmod']['ftp_error']))
3675
		return;
3676
3677
	// Was it a problem with Windows?
3678
	if (!empty($upcontext['chmod']['ftp_error']) && $upcontext['chmod']['ftp_error'] == 'total_mess')
3679
	{
3680
		echo '
3681
		<div class="error">
3682
			<p>', $txt['upgrade_writable_files'], '</p>
3683
			<ul class="error_content">
3684
				<li>' . implode('</li>
3685
				<li>', $upcontext['chmod']['files']) . '</li>
3686
			</ul>
3687
		</div>';
3688
3689
		return false;
3690
	}
3691
3692
	echo '
3693
		<div class="panel">
3694
			<h2>', $txt['upgrade_ftp_login'], '</h2>
3695
			<h3>', $txt['upgrade_ftp_perms'], '</h3>
3696
			<script>
3697
				function warning_popup()
3698
				{
3699
					popup = window.open(\'\',\'popup\',\'height=150,width=400,scrollbars=yes\');
3700
					var content = popup.document;
3701
					content.write(\'<!DOCTYPE html>\n\');
3702
					content.write(\'<html', $txt['lang_rtl'] == true ? ' dir="rtl"' : '', '>\n\t<head>\n\t\t<meta name="robots" content="noindex">\n\t\t\');
3703
					content.write(\'<title>', $txt['upgrade_ftp_warning'], '</title>\n\t\t<link rel="stylesheet" href="', $settings['default_theme_url'], '/css/index.css">\n\t</head>\n\t<body id="popup">\n\t\t\');
3704
					content.write(\'<div class="windowbg description">\n\t\t\t<h4>', $txt['upgrade_ftp_files'], '</h4>\n\t\t\t\');
3705
					content.write(\'<p>', implode('<br>\n\t\t\t', $upcontext['chmod']['files']), '</p>\n\t\t\t\');';
3706
3707
	if (isset($upcontext['systemos']) && $upcontext['systemos'] == 'linux')
3708
		echo '
3709
					content.write(\'<hr>\n\t\t\t\');
3710
					content.write(\'<p>', $txt['upgrade_ftp_shell'], '</p>\n\t\t\t\');
3711
					content.write(\'<tt># chmod a+w ', implode(' ', $upcontext['chmod']['files']), '</tt>\n\t\t\t\');';
3712
3713
	echo '
3714
					content.write(\'<a href="javascript:self.close();">close</a>\n\t\t</div>\n\t</body>\n</html>\');
3715
					content.close();
3716
				}
3717
			</script>';
3718
3719
	if (!empty($upcontext['chmod']['ftp_error']))
3720
		echo '
3721
			<div class="error">
3722
				<p>', $txt['upgrade_ftp_error'], '<p>
3723
				<code>', $upcontext['chmod']['ftp_error'], '</code>
3724
			</div>';
3725
3726
	if (empty($upcontext['chmod_in_form']))
3727
		echo '
3728
			<form action="', $upcontext['form_url'], '" method="post">';
3729
3730
	echo '
3731
				<dl class="settings">
3732
					<dt>
3733
						<label for="ftp_server">', $txt['ftp_server'], ':</label>
3734
					</dt>
3735
					<dd>
3736
						<div class="floatright">
3737
							<label for="ftp_port" class="textbox"><strong>', $txt['ftp_port'], ':</strong></label>
3738
							<input type="text" size="3" name="ftp_port" id="ftp_port" value="', isset($upcontext['chmod']['port']) ? $upcontext['chmod']['port'] : '21', '">
3739
						</div>
3740
						<input type="text" size="30" name="ftp_server" id="ftp_server" value="', isset($upcontext['chmod']['server']) ? $upcontext['chmod']['server'] : 'localhost', '">
3741
						<div class="smalltext">', $txt['ftp_server_info'], '</div>
3742
					</dd>
3743
					<dt>
3744
						<label for="ftp_username">', $txt['ftp_username'], ':</label>
3745
					</dt>
3746
					<dd>
3747
						<input type="text" size="30" name="ftp_username" id="ftp_username" value="', isset($upcontext['chmod']['username']) ? $upcontext['chmod']['username'] : '', '">
3748
						<div class="smalltext">', $txt['ftp_username_info'], '</div>
3749
					</dd>
3750
					<dt>
3751
						<label for="ftp_password">', $txt['ftp_password'], ':</label>
3752
					</dt>
3753
					<dd>
3754
						<input type="password" size="30" name="ftp_password" id="ftp_password">
3755
						<div class="smalltext">', $txt['ftp_password_info'], '</div>
3756
					</dd>
3757
					<dt>
3758
						<label for="ftp_path">', $txt['ftp_path'], ':</label>
3759
					</dt>
3760
					<dd>
3761
						<input type="text" size="30" name="ftp_path" id="ftp_path" value="', isset($upcontext['chmod']['path']) ? $upcontext['chmod']['path'] : '', '">
3762
						<div class="smalltext">', !empty($upcontext['chmod']['path']) ? $txt['ftp_path_found_info'] : $txt['ftp_path_info'], '</div>
3763
					</dd>
3764
				</dl>
3765
3766
				<div class="righttext buttons">
3767
					<input type="submit" value="', $txt['ftp_connect'], '" class="button">
3768
				</div>';
3769
3770
	if (empty($upcontext['chmod_in_form']))
3771
		echo '
3772
			</form>';
3773
3774
	echo '
3775
		</div><!-- .panel -->';
3776
}
3777
3778
function template_upgrade_above()
3779
{
3780
	global $modSettings, $txt, $settings, $upcontext, $upgradeurl;
3781
3782
	echo '<!DOCTYPE html>
3783
<html', $txt['lang_rtl'] == true ? ' dir="rtl"' : '', '>
3784
<head>
3785
	<meta charset="', isset($txt['lang_character_set']) ? $txt['lang_character_set'] : 'UTF-8', '">
3786
	<meta name="robots" content="noindex">
3787
	<title>', $txt['upgrade_upgrade_utility'], '</title>
3788
	<link rel="stylesheet" href="', $settings['default_theme_url'], '/css/index.css">
3789
	<link rel="stylesheet" href="', $settings['default_theme_url'], '/css/install.css">
3790
	', $txt['lang_rtl'] == true ? '<link rel="stylesheet" href="' . $settings['default_theme_url'] . '/css/rtl.css">' : '', '
3791
	<script src="https://ajax.googleapis.com/ajax/libs/jquery/', JQUERY_VERSION, '/jquery.min.js"></script>
3792
	<script src="', $settings['default_theme_url'], '/scripts/script.js"></script>
3793
	<script>
3794
		var smf_scripturl = \'', $upgradeurl, '\';
3795
		var smf_charset = \'', (empty($modSettings['global_character_set']) ? (empty($txt['lang_character_set']) ? 'UTF-8' : $txt['lang_character_set']) : $modSettings['global_character_set']), '\';
3796
		var startPercent = ', $upcontext['overall_percent'], ';
3797
		var allow_xhjr_credentials = false;
3798
3799
		// This function dynamically updates the step progress bar - and overall one as required.
3800
		function updateStepProgress(current, max, overall_weight)
3801
		{
3802
			// What out the actual percent.
3803
			var width = parseInt((current / max) * 100);
3804
			if (document.getElementById(\'step_progress\'))
3805
			{
3806
				document.getElementById(\'step_progress\').style.width = width + "%";
3807
				setInnerHTML(document.getElementById(\'step_text\'), width + "%");
3808
			}
3809
			if (overall_weight && document.getElementById(\'overall_progress\'))
3810
			{
3811
				overall_width = parseInt(startPercent + width * (overall_weight / 100));
3812
				document.getElementById(\'overall_progress\').style.width = overall_width + "%";
3813
				setInnerHTML(document.getElementById(\'overall_text\'), overall_width + "%");
3814
			}
3815
		}
3816
	</script>
3817
</head>
3818
<body>
3819
	<div id="footerfix">
3820
	<div id="header">
3821
		<h1 class="forumtitle">', $txt['upgrade_upgrade_utility'], '</h1>
3822
		<img id="smflogo" src="', $settings['default_theme_url'], '/images/smflogo.svg" alt="Simple Machines Forum" title="Simple Machines Forum">
3823
	</div>
3824
	<div id="wrapper">
3825
		<div id="content_section">
3826
			<div id="main_content_section">
3827
				<div id="main_steps">
3828
					<h2>', $txt['upgrade_progress'], '</h2>
3829
					<ul class="steps_list">';
3830
3831
	foreach ($upcontext['steps'] as $num => $step)
3832
		echo '
3833
						<li', $num == $upcontext['current_step'] ? ' class="stepcurrent"' : '', '>
3834
							', $txt['upgrade_step'], ' ', $step[0], ': ', $txt[$step[1]], '
3835
						</li>';
3836
3837
	echo '
3838
					</ul>
3839
				</div><!-- #main_steps -->
3840
3841
				<div id="install_progress">
3842
					<div id="progress_bar" class="progress_bar progress_green">
3843
						<h3>', $txt['upgrade_overall_progress'], '</h3>
3844
						<div id="overall_progress" class="bar" style="width: ', $upcontext['overall_percent'], '%;"></div>
3845
						<span id="overall_text">', $upcontext['overall_percent'], '%</span>
3846
					</div>';
3847
3848
	if (isset($upcontext['step_progress']))
3849
		echo '
3850
					<div id="progress_bar_step" class="progress_bar progress_yellow">
3851
						<h3>', $txt['upgrade_step_progress'], '</h3>
3852
						<div id="step_progress" class="bar" style="width: ', $upcontext['step_progress'], '%;"></div>
3853
						<span id="step_text">', $upcontext['step_progress'], '%</span>
3854
					</div>';
3855
3856
	echo '
3857
					<div id="substep_bar_div" class="progress_bar ', isset($upcontext['substep_progress']) ? '' : 'hidden', '">
3858
						<h3 id="substep_name">', isset($upcontext['substep_progress_name']) ? trim(strtr($upcontext['substep_progress_name'], array('.' => ''))) : '', '</h3>
3859
						<div id="substep_progress" class="bar" style="width: ', isset($upcontext['substep_progress']) ? $upcontext['substep_progress'] : 0, '%;"></div>
3860
						<span id="substep_text">', isset($upcontext['substep_progress']) ? $upcontext['substep_progress'] : 0, '%</span>
3861
					</div>';
3862
3863
	// How long have we been running this?
3864
	$elapsed = time() - $upcontext['started'];
3865
	$mins = (int) ($elapsed / 60);
3866
	$seconds = $elapsed - $mins * 60;
3867
	echo '
3868
					<div class="smalltext time_elapsed">
3869
						', $txt['upgrade_time_elapsed'], ':
3870
						<span id="mins_elapsed">', $mins, '</span> ', $txt['upgrade_time_mins'], ', <span id="secs_elapsed">', $seconds, '</span> ', $txt['upgrade_time_secs'], '.
3871
					</div>';
3872
	echo '
3873
				</div><!-- #install_progress -->
3874
				<div id="main_screen" class="clear">
3875
					<h2>', $upcontext['page_title'], '</h2>
3876
					<div class="panel">';
3877
}
3878
3879
function template_upgrade_below()
3880
{
3881
	global $upcontext, $txt;
3882
3883
	if (!empty($upcontext['pause']))
3884
		echo '
3885
							<em>', $txt['upgrade_incomplete'], '.</em><br>
3886
3887
							<h2 style="margin-top: 2ex;">', $txt['upgrade_not_quite_done'], '</h2>
3888
							<h3>
3889
								', $txt['upgrade_paused_overload'], '
3890
							</h3>';
3891
3892
	if (!empty($upcontext['custom_warning']))
3893
		echo '
3894
							<div class="errorbox">
3895
								<h3>', $txt['upgrade_note'], '</h3>
3896
								', $upcontext['custom_warning'], '
3897
							</div>';
3898
3899
	echo '
3900
							<div class="righttext buttons">';
3901
3902
	if (!empty($upcontext['continue']))
3903
		echo '
3904
								<input type="submit" id="contbutt" name="contbutt" value="', $txt['upgrade_continue'], '"', $upcontext['continue'] == 2 ? ' disabled' : '', ' class="button">';
3905
	if (!empty($upcontext['skip']))
3906
		echo '
3907
								<input type="submit" id="skip" name="skip" value="', $txt['upgrade_skip'], '" onclick="dontSubmit = true; document.getElementById(\'contbutt\').disabled = \'disabled\'; return true;" class="button">';
3908
3909
	echo '
3910
							</div>
3911
						</form>
3912
					</div><!-- .panel -->
3913
				</div><!-- #main_screen -->
3914
			</div><!-- #main_content_section -->
3915
		</div><!-- #content_section -->
3916
	</div><!-- #wrapper -->
3917
	</div><!-- #footerfix -->
3918
	<div id="footer">
3919
		<ul>
3920
			<li class="copyright"><a href="https://www.simplemachines.org/" title="Simple Machines Forum" target="_blank" rel="noopener">SMF &copy; ' . SMF_SOFTWARE_YEAR . ', Simple Machines</a></li>
3921
		</ul>
3922
	</div>';
3923
3924
	// Are we on a pause?
3925
	if (!empty($upcontext['pause']))
3926
	{
3927
		echo '
3928
	<script>
3929
		window.onload = doAutoSubmit;
3930
		var countdown = 3;
3931
		var dontSubmit = false;
3932
3933
		function doAutoSubmit()
3934
		{
3935
			if (countdown == 0 && !dontSubmit)
3936
				document.upform.submit();
3937
			else if (countdown == -1)
3938
				return;
3939
3940
			document.getElementById(\'contbutt\').value = "', $txt['upgrade_continue'], ' (" + countdown + ")";
3941
			countdown--;
3942
3943
			setTimeout("doAutoSubmit();", 1000);
3944
		}
3945
	</script>';
3946
	}
3947
3948
	echo '
3949
</body>
3950
</html>';
3951
}
3952
3953
function template_xml_above()
3954
{
3955
	global $upcontext;
3956
3957
	echo '<', '?xml version="1.0" encoding="UTF-8"?', '>
3958
	<smf>';
3959
3960
	if (!empty($upcontext['get_data']))
3961
		foreach ($upcontext['get_data'] as $k => $v)
3962
			echo '
3963
		<get key="', $k, '">', $v, '</get>';
3964
}
3965
3966
function template_xml_below()
3967
{
3968
	echo '
3969
	</smf>';
3970
}
3971
3972
function template_error_message()
3973
{
3974
	global $upcontext, $txt;
3975
3976
	echo '
3977
	<div class="error">
3978
		', $upcontext['error_msg'], '
3979
		<br>
3980
		<a href="', $_SERVER['PHP_SELF'], '">', $txt['upgrade_respondtime_clickhere'], '</a>
3981
	</div>';
3982
}
3983
3984
function template_welcome_message()
3985
{
3986
	global $upcontext, $disable_security, $settings, $txt;
3987
3988
	echo '
3989
				<script src="https://www.simplemachines.org/smf/current-version.js?version=' . SMF_VERSION . '"></script>
3990
3991
				<h3>', sprintf($txt['upgrade_ready_proceed'], SMF_VERSION), '</h3>
3992
				<form action="', $upcontext['form_url'], '" method="post" name="upform" id="upform">
3993
					<input type="hidden" name="', $upcontext['login_token_var'], '" value="', $upcontext['login_token'], '">
3994
3995
					<div id="version_warning" class="noticebox hidden">
3996
						<h3>', $txt['upgrade_warning'], '</h3>
3997
						', sprintf($txt['upgrade_warning_out_of_date'], SMF_VERSION, 'https://www.simplemachines.org'), '
3998
					</div>';
3999
4000
	$upcontext['chmod_in_form'] = true;
4001
	template_chmod();
4002
4003
	// For large, pre 1.1 RC2 forums give them a warning about the possible impact of this upgrade!
4004
	if ($upcontext['is_large_forum'])
4005
		echo '
4006
					<div class="errorbox">
4007
						<h3>', $txt['upgrade_warning'], '</h3>
4008
						', $txt['upgrade_warning_lots_data'], '
4009
					</div>';
4010
4011
	// A warning message?
4012
	if (!empty($upcontext['warning']))
4013
		echo '
4014
					<div class="errorbox">
4015
						<h3>', $txt['upgrade_warning'], '</h3>
4016
						', $upcontext['warning'], '
4017
					</div>';
4018
4019
	// Paths are incorrect?
4020
	echo '
4021
					<div class="errorbox', (file_exists($settings['default_theme_dir'] . '/scripts/script.js') ? ' hidden' : ''), '" id="js_script_missing_error">
4022
						<h3>', $txt['upgrade_critical_error'], '</h3>
4023
						', sprintf($txt['upgrade_error_script_js'], 'https://download.simplemachines.org/?tools'), '
4024
					</div>';
4025
4026
	// Is there someone already doing this?
4027
	if (!empty($upcontext['user']['id']) && (time() - $upcontext['started'] < 72600 || time() - $upcontext['updated'] < 3600))
4028
	{
4029
		$ago = time() - $upcontext['started'];
4030
		$ago_hours = floor($ago / 3600);
4031
		$ago_minutes = intval(($ago / 60) % 60);
4032
		$ago_seconds = intval($ago % 60);
4033
		$agoTxt = $ago < 60 ? 'upgrade_time_s' : ($ago < 3600 ? 'upgrade_time_ms' : 'upgrade_time_hms');
4034
4035
		$updated = time() - $upcontext['updated'];
4036
		$updated_hours = floor($updated / 3600);
4037
		$updated_minutes = intval(($updated / 60) % 60);
4038
		$updated_seconds = intval($updated % 60);
4039
		$updatedTxt = $updated < 60 ? 'upgrade_time_updated_s' : ($updated < 3600 ? 'upgrade_time_updated_hm' : 'upgrade_time_updated_hms');
4040
4041
		echo '
4042
					<div class="errorbox">
4043
						<h3>', $txt['upgrade_warning'], '</h3>
4044
						<p>', sprintf($txt['upgrade_time_user'], $upcontext['user']['name']), '</p>
4045
						<p>', sprintf($txt[$agoTxt], $ago_seconds, $ago_minutes, $ago_hours), '</p>
4046
						<p>', sprintf($txt[$updatedTxt], $updated_seconds, $updated_minutes, $updated_hours), '</p>';
4047
4048
		if ($updated < 600)
4049
			echo '
4050
						<p>', $txt['upgrade_run_script'], ' ', $upcontext['user']['name'], ' ', $txt['upgrade_run_script2'], '</p>';
4051
4052
		if ($updated > $upcontext['inactive_timeout'])
4053
			echo '
4054
						<p>', $txt['upgrade_run'], '</p>';
4055
		elseif ($upcontext['inactive_timeout'] > 120)
4056
			echo '
4057
						<p>', sprintf($txt['upgrade_script_timeout_minutes'], $upcontext['user']['name'], round($upcontext['inactive_timeout'] / 60, 1)), '</p>';
4058
		else
4059
			echo '
4060
						<p>', sprintf($txt['upgrade_script_timeout_seconds'], $upcontext['user']['name'], $upcontext['inactive_timeout']), '</p>';
4061
4062
		echo '
4063
					</div>';
4064
	}
4065
4066
	echo '
4067
					<strong>', $txt['upgrade_admin_login'], ' ', $disable_security ? $txt['upgrade_admin_disabled'] : '', '</strong>
4068
					<h3>', $txt['upgrade_sec_login'], '</h3>
4069
					<dl class="settings adminlogin">
4070
						<dt>
4071
							<label for="user"', $disable_security ? ' disabled' : '', '>', $txt['upgrade_username'], '</label>
4072
						</dt>
4073
						<dd>
4074
							<input type="text" name="user" value="', !empty($upcontext['username']) ? $upcontext['username'] : '', '"', $disable_security ? ' disabled' : '', '>';
4075
4076
	if (!empty($upcontext['username_incorrect']))
4077
		echo '
4078
							<div class="smalltext red">', $txt['upgrade_wrong_username'], '</div>';
4079
4080
	echo '
4081
						</dd>
4082
						<dt>
4083
							<label for="passwrd"', $disable_security ? ' disabled' : '', '>', $txt['upgrade_password'], '</label>
4084
						</dt>
4085
						<dd>
4086
							<input type="password" name="passwrd" value=""', $disable_security ? ' disabled' : '', '>';
4087
4088
	if (!empty($upcontext['password_failed']))
4089
		echo '
4090
							<div class="smalltext red">', $txt['upgrade_wrong_password'], '</div>';
4091
4092
	echo '
4093
						</dd>';
4094
4095
	// Can they continue?
4096
	if (!empty($upcontext['user']['id']) && time() - $upcontext['user']['updated'] >= $upcontext['inactive_timeout'] && $upcontext['user']['step'] > 1)
4097
	{
4098
		echo '
4099
						<dd>
4100
							<label for="cont"><input type="checkbox" id="cont" name="cont" checked>', $txt['upgrade_continue_step'], '</label>
4101
						</dd>';
4102
	}
4103
4104
	echo '
4105
					</dl>
4106
					<span class="smalltext">
4107
						', $txt['upgrade_bypass'], '
4108
					</span>
4109
					<input type="hidden" name="login_attempt" id="login_attempt" value="1">
4110
					<input type="hidden" name="js_works" id="js_works" value="0">';
4111
4112
	// Say we want the continue button!
4113
	$upcontext['continue'] = !empty($upcontext['user']['id']) && time() - $upcontext['user']['updated'] < $upcontext['inactive_timeout'] ? 2 : 1;
4114
4115
	// This defines whether javascript is going to work elsewhere :D
4116
	echo '
4117
					<script>
4118
						if (\'XMLHttpRequest\' in window && document.getElementById(\'js_works\'))
4119
							document.getElementById(\'js_works\').value = 1;
4120
4121
						// Latest version?
4122
						function smfCurrentVersion()
4123
						{
4124
							var smfVer, yourVer;
4125
4126
							if (!(\'smfVersion\' in window))
4127
								return;
4128
4129
							window.smfVersion = window.smfVersion.replace(/SMF\s?/g, \'\');
4130
4131
							smfVer = document.getElementById(\'smfVersion\');
4132
							yourVer = document.getElementById(\'yourVersion\');
4133
4134
							setInnerHTML(smfVer, window.smfVersion);
4135
4136
							var currentVersion = getInnerHTML(yourVer);
4137
							if (currentVersion < window.smfVersion)
4138
								document.getElementById(\'version_warning\').classList.remove(\'hidden\');
4139
						}
4140
						addLoadEvent(smfCurrentVersion);
4141
4142
						// This checks that the script file even exists!
4143
						if (typeof(smfSelectText) == \'undefined\')
4144
							document.getElementById(\'js_script_missing_error\').classList.remove(\'hidden\');
4145
4146
					</script>';
4147
}
4148
4149
function template_upgrade_options()
4150
{
4151
	global $upcontext, $modSettings, $db_prefix, $mmessage, $mtitle, $txt;
4152
4153
	echo '
4154
				<h3>', $txt['upgrade_areyouready'], '</h3>
4155
				<form action="', $upcontext['form_url'], '" method="post" name="upform" id="upform">';
4156
4157
	// Warning message?
4158
	if (!empty($upcontext['upgrade_options_warning']))
4159
		echo '
4160
				<div class="errorbox">
4161
					<h3>', $txt['upgrade_warning'], '</h3>
4162
					', $upcontext['upgrade_options_warning'], '
4163
				</div>';
4164
4165
	echo '
4166
				<ul class="upgrade_settings">
4167
					<li>
4168
						<input type="checkbox" name="backup" id="backup" value="1" checked>
4169
						<label for="backup">', $txt['upgrade_backup_table'], ' &quot;backup_' . $db_prefix . '&quot;.</label>
4170
						(', $txt['upgrade_recommended'], ')
4171
					</li>
4172
					<li>
4173
						<input type="checkbox" name="maint" id="maint" value="1" checked>
4174
						<label for="maint">', $txt['upgrade_maintenance'], '</label>
4175
						<span class="smalltext">(<a href="javascript:void(0)" onclick="document.getElementById(\'mainmess\').classList.toggle(\'hidden\')">', $txt['upgrade_customize'], '</a>)</span>
4176
						<div id="mainmess" class="hidden">
4177
							<strong class="smalltext">', $txt['upgrade_maintenance_title'], ' </strong><br>
4178
							<input type="text" name="maintitle" size="30" value="', htmlspecialchars($mtitle), '"><br>
4179
							<strong class="smalltext">', $txt['upgrade_maintenance_message'], ' </strong><br>
4180
							<textarea name="mainmessage" rows="3" cols="50">', htmlspecialchars($mmessage), '</textarea>
4181
						</div>
4182
					</li>
4183
					<li>
4184
						<input type="checkbox" name="debug" id="debug" value="1">
4185
						<label for="debug">'.$txt['upgrade_debug_info'], '</label>
4186
					</li>
4187
					<li>
4188
						<input type="checkbox" name="empty_error" id="empty_error" value="1">
4189
						<label for="empty_error">', $txt['upgrade_empty_errorlog'], '</label>
4190
					</li>';
4191
4192
	if (!empty($upcontext['karma_installed']['good']) || !empty($upcontext['karma_installed']['bad']))
4193
		echo '
4194
					<li>
4195
						<input type="checkbox" name="delete_karma" id="delete_karma" value="1">
4196
						<label for="delete_karma">', $txt['upgrade_delete_karma'], '</label>
4197
					</li>';
4198
4199
	echo '
4200
					<li>
4201
						<input type="checkbox" name="stats" id="stats" value="1"', empty($modSettings['allow_sm_stats']) && empty($modSettings['enable_sm_stats']) ? '' : ' checked="checked"', '>
4202
						<label for="stat">
4203
							', $txt['upgrade_stats_collection'], '<br>
4204
							<span class="smalltext">', sprintf($txt['upgrade_stats_info'], 'https://www.simplemachines.org/about/stats.php'), '</a></span>
4205
						</label>
4206
					</li>
4207
					<li>
4208
						<input type="checkbox" name="migrateSettings" id="migrateSettings" value="1"', empty($upcontext['migrate_settings_recommended']) ? '' : ' checked="checked"', '>
4209
						<label for="migrateSettings">
4210
							', $txt['upgrade_migrate_settings_file'], '
4211
						</label>
4212
					</li>
4213
				</ul>
4214
				<input type="hidden" name="upcont" value="1">';
4215
4216
	// We need a normal continue button here!
4217
	$upcontext['continue'] = 1;
4218
}
4219
4220
// Template for the database backup tool/
4221
function template_backup_database()
4222
{
4223
	global $upcontext, $support_js, $is_debug, $txt;
4224
4225
	echo '
4226
				<h3>', $txt['upgrade_wait'], '</h3>';
4227
4228
	echo '
4229
				<form action="', $upcontext['form_url'], '" name="upform" id="upform" method="post">
4230
					<input type="hidden" name="backup_done" id="backup_done" value="0">
4231
					<strong>', sprintf($txt['upgrade_completedtables_outof'], $upcontext['cur_table_num'], $upcontext['table_count']), '</strong>
4232
					<div id="debug_section">
4233
						<span id="debuginfo"></span>
4234
					</div>';
4235
4236
	// Dont any tables so far?
4237
	if (!empty($upcontext['previous_tables']))
4238
		foreach ($upcontext['previous_tables'] as $table)
4239
			echo '
4240
					<br>', $txt['upgrade_completed_table'], ' &quot;', $table, '&quot;.';
4241
4242
	echo '
4243
					<h3 id="current_tab">
4244
						', $txt['upgrade_current_table'], ' &quot;<span id="current_table">', $upcontext['cur_table_name'], '</span>&quot;
4245
					</h3>
4246
					<p id="commess" class="', $upcontext['cur_table_num'] == $upcontext['table_count'] ? 'inline_block' : 'hidden', '">', $txt['upgrade_backup_complete'], '</p>';
4247
4248
	// Continue please!
4249
	$upcontext['continue'] = $support_js ? 2 : 1;
4250
4251
	// If javascript allows we want to do this using XML.
4252
	if ($support_js)
4253
	{
4254
		echo '
4255
					<script>
4256
						var lastTable = ', $upcontext['cur_table_num'], ';
4257
						function getNextTables()
4258
						{
4259
							getXMLDocument(\'', $upcontext['form_url'], '&xml&substep=\' + lastTable, onBackupUpdate);
4260
						}
4261
4262
						// Got an update!
4263
						function onBackupUpdate(oXMLDoc)
4264
						{
4265
							var sCurrentTableName = "";
4266
							var iTableNum = 0;
4267
							var sCompletedTableName = getInnerHTML(document.getElementById(\'current_table\'));
4268
							for (var i = 0; i < oXMLDoc.getElementsByTagName("table")[0].childNodes.length; i++)
4269
								sCurrentTableName += oXMLDoc.getElementsByTagName("table")[0].childNodes[i].nodeValue;
4270
							iTableNum = oXMLDoc.getElementsByTagName("table")[0].getAttribute("num");
4271
4272
							// Update the page.
4273
							setInnerHTML(document.getElementById(\'tab_done\'), iTableNum);
4274
							setInnerHTML(document.getElementById(\'current_table\'), sCurrentTableName);
4275
							lastTable = iTableNum;
4276
							updateStepProgress(iTableNum, ', $upcontext['table_count'], ', ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');';
4277
4278
		// If debug flood the screen.
4279
		if ($is_debug)
4280
			echo '
4281
							setOuterHTML(document.getElementById(\'debuginfo\'), \'<br>', $txt['upgrade_completed_table'], ' &quot;\' + sCompletedTableName + \'&quot;.<span id="debuginfo"><\' + \'/span>\');
4282
4283
							if (document.getElementById(\'debug_section\').scrollHeight)
4284
								document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
4285
4286
		echo '
4287
							// Get the next update...
4288
							if (iTableNum == ', $upcontext['table_count'], ')
4289
							{
4290
								document.getElementById(\'commess\').classList.remove("hidden");
4291
								document.getElementById(\'current_tab\').classList.add("hidden");
4292
								document.getElementById(\'contbutt\').disabled = 0;
4293
								document.getElementById(\'backup_done\').value = 1;
4294
							}
4295
							else
4296
								getNextTables();
4297
						}
4298
						getNextTables();
4299
					//# sourceURL=dynamicScript-bkup.js
4300
					</script>';
4301
	}
4302
}
4303
4304
function template_backup_xml()
4305
{
4306
	global $upcontext;
4307
4308
	echo '
4309
		<table num="', $upcontext['cur_table_num'], '">', $upcontext['cur_table_name'], '</table>';
4310
}
4311
4312
// Here is the actual "make the changes" template!
4313
function template_database_changes()
4314
{
4315
	global $upcontext, $support_js, $is_debug, $timeLimitThreshold, $txt;
4316
4317
	if (empty($is_debug) && !empty($upcontext['upgrade_status']['debug']))
4318
		$is_debug = true;
4319
4320
	echo '
4321
				<h3>', $txt['upgrade_db_changes'], '</h3>
4322
				<h4><em>', $txt['upgrade_db_patient'], '</em></h4>';
4323
4324
	echo '
4325
				<form action="', $upcontext['form_url'], '&amp;filecount=', $upcontext['file_count'], '" name="upform" id="upform" method="post">
4326
					<input type="hidden" name="database_done" id="database_done" value="0">';
4327
4328
	// No javascript looks rubbish!
4329
	if (!$support_js)
4330
	{
4331
		foreach ($upcontext['actioned_items'] as $num => $item)
4332
		{
4333
			if ($num != 0)
4334
				echo ' Successful!';
4335
			echo '<br>' . $item;
4336
		}
4337
4338
		// Only tell deubbers how much time they wasted waiting for the upgrade because they don't have javascript.
4339
		if (!empty($upcontext['changes_complete']))
4340
		{
4341
			if ($is_debug)
4342
			{
4343
				$active = time() - $upcontext['started'];
4344
				$hours = floor($active / 3600);
4345
				$minutes = intval(($active / 60) % 60);
4346
				$seconds = intval($active % 60);
4347
4348
				echo '', sprintf($txt['upgrade_success_time_db'], $seconds, $minutes, $hours), '<br>';
4349
			}
4350
			else
4351
				echo '', $txt['upgrade_success'], '<br>';
4352
4353
			echo '
4354
					<p id="commess">', $txt['upgrade_db_complete'], '</p>';
4355
		}
4356
	}
4357
	else
4358
	{
4359
		// Tell them how many files we have in total.
4360
		if ($upcontext['file_count'] > 1)
4361
			echo '
4362
					<strong id="info1">', $txt['upgrade_script'], ' <span id="file_done">', $upcontext['cur_file_num'], '</span> of ', $upcontext['file_count'], '.</strong>';
4363
4364
		echo '
4365
					<h3 id="info2">
4366
						<strong>', $txt['upgrade_executing'], '</strong> &quot;<span id="cur_item_name">', $upcontext['current_item_name'], '</span>&quot; (<span id="item_num">', $upcontext['current_item_num'], '</span> ', $txt['upgrade_of'], ' <span id="total_items"><span id="item_count">', $upcontext['total_items'], '</span>', $upcontext['file_count'] > 1 ? ' - of this script' : '', ')</span>
4367
					</h3>
4368
					<p id="commess" class="', !empty($upcontext['changes_complete']) || $upcontext['current_debug_item_num'] == $upcontext['debug_items'] ? 'inline_block' : 'hidden', '">', $txt['upgrade_db_complete2'], '</p>';
4369
4370
		if ($is_debug)
4371
		{
4372
			// Let our debuggers know how much time was spent, but not wasted since JS handled refreshing the page!
4373
			if ($upcontext['current_debug_item_num'] == $upcontext['debug_items'])
4374
			{
4375
				$active = time() - $upcontext['started'];
4376
				$hours = floor($active / 3600);
4377
				$minutes = intval(($active / 60) % 60);
4378
				$seconds = intval($active % 60);
4379
4380
				echo '
4381
					<p id="upgradeCompleted">', sprintf($txt['upgrade_success_time_db'], $seconds, $minutes, $hours), '</p>';
4382
			}
4383
			else
4384
				echo '
4385
					<p id="upgradeCompleted"></p>';
4386
4387
			echo '
4388
					<div id="debug_section">
4389
						<span id="debuginfo"></span>
4390
					</div>';
4391
		}
4392
	}
4393
4394
	// Place for the XML error message.
4395
	echo '
4396
					<div id="error_block" class="errorbox', empty($upcontext['error_message']) ? ' hidden' : '', '">
4397
						<h3>', $txt['upgrade_error'], '</h3>
4398
						<div id="error_message">', isset($upcontext['error_message']) ? $upcontext['error_message'] : $txt['upgrade_unknown_error'], '</div>
4399
					</div>';
4400
4401
	// We want to continue at some point!
4402
	$upcontext['continue'] = $support_js ? 2 : 1;
4403
4404
	// If javascript allows we want to do this using XML.
4405
	if ($support_js)
4406
	{
4407
		echo '
4408
					<script>
4409
						var lastItem = ', $upcontext['current_debug_item_num'], ';
4410
						var sLastString = "', strtr($upcontext['current_debug_item_name'], array('"' => '&quot;')), '";
4411
						var iLastSubStepProgress = -1;
4412
						var curFile = ', $upcontext['cur_file_num'], ';
4413
						var totalItems = 0;
4414
						var prevFile = 0;
4415
						var retryCount = 0;
4416
						var testvar = 0;
4417
						var timeOutID = 0;
4418
						var getData = "";
4419
						var debugItems = ', $upcontext['debug_items'], ';';
4420
4421
		if ($is_debug)
4422
			echo '
4423
						var upgradeStartTime = ' . $upcontext['started'] . ';';
4424
4425
		echo '
4426
						function getNextItem()
4427
						{
4428
							// We want to track this...
4429
							if (timeOutID)
4430
								clearTimeout(timeOutID);
4431
							timeOutID = window.setTimeout("retTimeout()", ', (10 * $timeLimitThreshold), '000);
4432
4433
							getXMLDocument(\'', $upcontext['form_url'], '&xml&filecount=', $upcontext['file_count'], '&substep=\' + lastItem + getData, onItemUpdate);
4434
						}
4435
4436
						// Got an update!
4437
						function onItemUpdate(oXMLDoc)
4438
						{
4439
							var sItemName = "";
4440
							var sDebugName = "";
4441
							var iItemNum = 0;
4442
							var iSubStepProgress = -1;
4443
							var iDebugNum = 0;
4444
							var bIsComplete = 0;
4445
							var bSkipped = 0;
4446
							getData = "";
4447
4448
							// We\'ve got something - so reset the timeout!
4449
							if (timeOutID)
4450
								clearTimeout(timeOutID);
4451
4452
							// Assume no error at this time...
4453
							document.getElementById("error_block").classList.add("hidden");
4454
4455
							// Are we getting some duff info?
4456
							if (!oXMLDoc.getElementsByTagName("item")[0])
4457
							{
4458
								// Too many errors?
4459
								if (retryCount > 15)
4460
								{
4461
									document.getElementById("error_block").classList.remove("hidden");
4462
									setInnerHTML(document.getElementById("error_message"), "Error retrieving information on step: " + (sDebugName == "" ? sLastString : sDebugName));';
4463
4464
		if ($is_debug)
4465
			echo '
4466
									setOuterHTML(document.getElementById(\'debuginfo\'), \'<span class="red">failed<\' + \'/span><span id="debuginfo"><\' + \'/span>\');';
4467
4468
		echo '
4469
								}
4470
								else
4471
								{
4472
									retryCount++;
4473
									getNextItem();
4474
								}
4475
								return false;
4476
							}
4477
4478
							// Never allow loops.
4479
							if (curFile == prevFile)
4480
							{
4481
								retryCount++;
4482
								if (retryCount > 10)
4483
								{
4484
									document.getElementById("error_block").classList.remove("hidden");
4485
									setInnerHTML(document.getElementById("error_message"), "', $txt['upgrade_loop'], '" + sDebugName);';
4486
4487
		if ($is_debug)
4488
			echo '
4489
									setOuterHTML(document.getElementById(\'debuginfo\'), \'<span class="red">failed<\' + \'/span><span id="debuginfo"><\' + \'/span>\');';
4490
4491
		echo '
4492
								}
4493
							}
4494
							retryCount = 0;
4495
4496
							for (var i = 0; i < oXMLDoc.getElementsByTagName("item")[0].childNodes.length; i++)
4497
								sItemName += oXMLDoc.getElementsByTagName("item")[0].childNodes[i].nodeValue;
4498
							for (var i = 0; i < oXMLDoc.getElementsByTagName("debug")[0].childNodes.length; i++)
4499
								sDebugName += oXMLDoc.getElementsByTagName("debug")[0].childNodes[i].nodeValue;
4500
							for (var i = 0; i < oXMLDoc.getElementsByTagName("get").length; i++)
4501
							{
4502
								getData += "&" + oXMLDoc.getElementsByTagName("get")[i].getAttribute("key") + "=";
4503
								for (var j = 0; j < oXMLDoc.getElementsByTagName("get")[i].childNodes.length; j++)
4504
								{
4505
									getData += oXMLDoc.getElementsByTagName("get")[i].childNodes[j].nodeValue;
4506
								}
4507
							}
4508
4509
							iItemNum = oXMLDoc.getElementsByTagName("item")[0].getAttribute("num");
4510
							iDebugNum = parseInt(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("num"));
4511
							bIsComplete = parseInt(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("complete"));
4512
							bSkipped = parseInt(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("skipped"));
4513
							iSubStepProgress = parseFloat(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("percent"));
4514
							sLastString = sDebugName + " (Item: " + iDebugNum + ")";
4515
4516
							curFile = parseInt(oXMLDoc.getElementsByTagName("file")[0].getAttribute("num"));
4517
							debugItems = parseInt(oXMLDoc.getElementsByTagName("file")[0].getAttribute("debug_items"));
4518
							totalItems = parseInt(oXMLDoc.getElementsByTagName("file")[0].getAttribute("items"));
4519
4520
							// If we have an error we haven\'t completed!
4521
							if (oXMLDoc.getElementsByTagName("error")[0] && bIsComplete)
4522
								iDebugNum = lastItem;
4523
4524
							// Do we have the additional progress bar?
4525
							if (iSubStepProgress != -1)
4526
							{
4527
								document.getElementById("substep_bar_div").classList.remove("hidden");
4528
								document.getElementById("substep_progress").style.width = iSubStepProgress + "%";
4529
								setInnerHTML(document.getElementById("substep_text"), iSubStepProgress + "%");
4530
								setInnerHTML(document.getElementById("substep_name"), sDebugName.replace(/\./g, ""));
4531
							}
4532
							else
4533
							{
4534
								document.getElementById("substep_bar_div").classList.add("hidden");
4535
							}
4536
4537
							// Move onto the next item?
4538
							if (bIsComplete)
4539
								lastItem = iDebugNum;
4540
							else
4541
								lastItem = iDebugNum - 1;
4542
4543
							// Are we finished?
4544
							if (bIsComplete && iDebugNum == -1 && curFile >= ', $upcontext['file_count'], ')
4545
							{';
4546
4547
		// Database Changes, tell us how much time we spen to do this.  If this gets updated via JS.
4548
		if ($is_debug)
4549
			echo '
4550
								document.getElementById(\'debug_section\').classList.add("hidden");
4551
4552
								var upgradeFinishedTime = parseInt(oXMLDoc.getElementsByTagName("curtime")[0].childNodes[0].nodeValue);
4553
								var diffTime = upgradeFinishedTime - upgradeStartTime;
4554
								var diffHours = Math.floor(diffTime / 3600);
4555
								var diffMinutes = parseInt((diffTime / 60) % 60);
4556
								var diffSeconds = parseInt(diffTime % 60);
4557
4558
								var completedTxt = "', $txt['upgrade_success_time_db'], '";
4559
console.log(completedTxt, upgradeFinishedTime, diffTime, diffHours, diffMinutes, diffSeconds);
4560
4561
								completedTxt = completedTxt.replace("%1$d", diffSeconds).replace("%2$d", diffMinutes).replace("%3$d", diffHours);
4562
console.log(completedTxt, upgradeFinishedTime, diffTime, diffHours, diffMinutes, diffSeconds);
4563
								setInnerHTML(document.getElementById("upgradeCompleted"), completedTxt);';
4564
4565
		echo '
4566
4567
								document.getElementById(\'commess\').classList.remove("hidden");
4568
								document.getElementById(\'contbutt\').disabled = 0;
4569
								document.getElementById(\'database_done\').value = 1;';
4570
4571
		if ($upcontext['file_count'] > 1)
4572
			echo '
4573
								document.getElementById(\'info1\').classList.add(\'hidden\');';
4574
4575
		echo '
4576
								document.getElementById(\'info2\').classList.add(\'hidden\');
4577
								updateStepProgress(100, 100, ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');
4578
								return true;
4579
							}
4580
							// Was it the last step in the file?
4581
							else if (bIsComplete && iDebugNum == -1)
4582
							{
4583
								lastItem = 0;
4584
								prevFile = curFile;';
4585
4586
		if ($is_debug)
4587
			echo '
4588
								setOuterHTML(document.getElementById(\'debuginfo\'), \'Moving to next script file...done<br><span id="debuginfo"><\' + \'/span>\');';
4589
4590
		echo '
4591
								getNextItem();
4592
								return true;
4593
							}';
4594
4595
		// If debug scroll the screen.
4596
		if ($is_debug)
4597
			echo '
4598
							if (iLastSubStepProgress == -1)
4599
							{
4600
								// Give it consistent dots.
4601
								dots = sDebugName.match(/\./g);
4602
								numDots = dots ? dots.length : 0;
4603
								for (var i = numDots; i < 3; i++)
4604
									sDebugName += ".";
4605
								setOuterHTML(document.getElementById(\'debuginfo\'), sDebugName + \'<span id="debuginfo"><\' + \'/span>\');
4606
							}
4607
							iLastSubStepProgress = iSubStepProgress;
4608
4609
							if (bIsComplete && bSkipped)
4610
								setOuterHTML(document.getElementById(\'debuginfo\'), \'skipped<br><span id="debuginfo"><\' + \'/span>\');
4611
							else if (bIsComplete)
4612
								setOuterHTML(document.getElementById(\'debuginfo\'), \'done<br><span id="debuginfo"><\' + \'/span>\');
4613
							else
4614
								setOuterHTML(document.getElementById(\'debuginfo\'), \'...<span id="debuginfo"><\' + \'/span>\');
4615
4616
							if (document.getElementById(\'debug_section\').scrollHeight)
4617
								document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
4618
4619
		echo '
4620
							// Update the page.
4621
							setInnerHTML(document.getElementById(\'item_num\'), iItemNum);
4622
							setInnerHTML(document.getElementById(\'cur_item_name\'), sItemName);';
4623
4624
		if ($upcontext['file_count'] > 1)
4625
		{
4626
			echo '
4627
							setInnerHTML(document.getElementById(\'file_done\'), curFile);
4628
							setInnerHTML(document.getElementById(\'item_count\'), totalItems);';
4629
		}
4630
4631
		echo '
4632
							// Is there an error?
4633
							if (oXMLDoc.getElementsByTagName("error")[0])
4634
							{
4635
								var sErrorMsg = "";
4636
								for (var i = 0; i < oXMLDoc.getElementsByTagName("error")[0].childNodes.length; i++)
4637
									sErrorMsg += oXMLDoc.getElementsByTagName("error")[0].childNodes[i].nodeValue;
4638
								document.getElementById("error_block").classList.remove("hidden");
4639
								setInnerHTML(document.getElementById("error_message"), sErrorMsg);
4640
								return false;
4641
							}
4642
4643
							// Get the progress bar right.
4644
							barTotal = debugItems * ', $upcontext['file_count'], ';
4645
							barDone = (debugItems * (curFile - 1)) + lastItem;
4646
4647
							updateStepProgress(barDone, barTotal, ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');
4648
4649
							// Finally - update the time here as it shows the server is responding!
4650
							curTime = new Date();
4651
							iElapsed = (curTime.getTime() / 1000 - ', $upcontext['started'], ');
4652
							mins = parseInt(iElapsed / 60);
4653
							secs = parseInt(iElapsed - mins * 60);
4654
							setInnerHTML(document.getElementById("mins_elapsed"), mins);
4655
							setInnerHTML(document.getElementById("secs_elapsed"), secs);
4656
4657
							getNextItem();
4658
							return true;
4659
						}
4660
4661
						// What if we timeout?!
4662
						function retTimeout(attemptAgain)
4663
						{
4664
							// Oh noes...
4665
							if (!attemptAgain)
4666
							{
4667
								document.getElementById("error_block").classList.remove("hidden");
4668
								setInnerHTML(document.getElementById("error_message"), "', sprintf($txt['upgrade_respondtime'], ($timeLimitThreshold * 10)), '" + "<a href=\"#\" onclick=\"retTimeout(true); return false;\">', $txt['upgrade_respondtime_clickhere'], '</a>");
4669
							}
4670
							else
4671
							{
4672
								document.getElementById("error_block").classList.add("hidden");
4673
								getNextItem();
4674
							}
4675
						}';
4676
4677
		// Start things off assuming we've not errored.
4678
		if (empty($upcontext['error_message']))
4679
			echo '
4680
						getNextItem();';
4681
4682
		echo '
4683
					//# sourceURL=dynamicScript-dbch.js
4684
					</script>';
4685
	}
4686
	return;
4687
}
4688
4689
function template_database_xml()
4690
{
4691
	global $is_debug, $upcontext;
4692
4693
	echo '
4694
	<file num="', $upcontext['cur_file_num'], '" items="', $upcontext['total_items'], '" debug_items="', $upcontext['debug_items'], '">', $upcontext['cur_file_name'], '</file>
4695
	<item num="', $upcontext['current_item_num'], '">', $upcontext['current_item_name'], '</item>
4696
	<debug num="', $upcontext['current_debug_item_num'], '" percent="', isset($upcontext['substep_progress']) ? $upcontext['substep_progress'] : '-1', '" complete="', empty($upcontext['completed_step']) ? 0 : 1, '" skipped="', empty($upcontext['skip_db_substeps']) ? 0 : 1, '">', $upcontext['current_debug_item_name'], '</debug>';
4697
4698
	if (!empty($upcontext['error_message']))
4699
		echo '
4700
	<error>', $upcontext['error_message'], '</error>';
4701
4702
	if (!empty($upcontext['error_string']))
4703
		echo '
4704
	<sql>', $upcontext['error_string'], '</sql>';
4705
4706
	if ($is_debug)
4707
		echo '
4708
	<curtime>', time(), '</curtime>';
4709
}
4710
4711
// Template for the UTF-8 conversion step. Basically a copy of the backup stuff with slight modifications....
4712
function template_convert_utf8()
4713
{
4714
	global $upcontext, $support_js, $is_debug, $txt;
4715
4716
	echo '
4717
				<h3>', $txt['upgrade_wait2'], '</h3>
4718
				<form action="', $upcontext['form_url'], '" name="upform" id="upform" method="post">
4719
					<input type="hidden" name="utf8_done" id="utf8_done" value="0">
4720
					<strong>', $txt['upgrade_completed'], ' <span id="tab_done">', $upcontext['cur_table_num'], '</span> ', $txt['upgrade_outof'], ' ', $upcontext['table_count'], ' ', $txt['upgrade_tables'], '</strong>
4721
					<div id="debug_section">
4722
						<span id="debuginfo"></span>
4723
					</div>';
4724
4725
	// Done any tables so far?
4726
	if (!empty($upcontext['previous_tables']))
4727
		foreach ($upcontext['previous_tables'] as $table)
4728
			echo '
4729
					<br>', $txt['upgrade_completed_table'], ' &quot;', $table, '&quot;.';
4730
4731
	echo '
4732
					<h3 id="current_tab">
4733
						', $txt['upgrade_current_table'], ' &quot;<span id="current_table">', $upcontext['cur_table_name'], '</span>&quot;
4734
					</h3>';
4735
4736
	// If we dropped their index, let's let them know
4737
	if ($upcontext['dropping_index'])
4738
		echo '
4739
					<p id="indexmsg" class="', $upcontext['cur_table_num'] == $upcontext['table_count'] ? 'inline_block' : 'hidden', '>', $txt['upgrade_fulltext'], '</p>';
4740
4741
	// Completion notification
4742
	echo '
4743
					<p id="commess" class="', $upcontext['cur_table_num'] == $upcontext['table_count'] ? 'inline_block' : 'hidden', '">', $txt['upgrade_conversion_proceed'], '</p>';
4744
4745
	// Continue please!
4746
	$upcontext['continue'] = $support_js ? 2 : 1;
4747
4748
	// If javascript allows we want to do this using XML.
4749
	if ($support_js)
4750
	{
4751
		echo '
4752
					<script>
4753
						var lastTable = ', $upcontext['cur_table_num'], ';
4754
						function getNextTables()
4755
						{
4756
							getXMLDocument(\'', $upcontext['form_url'], '&xml&substep=\' + lastTable, onConversionUpdate);
4757
						}
4758
4759
						// Got an update!
4760
						function onConversionUpdate(oXMLDoc)
4761
						{
4762
							var sCurrentTableName = "";
4763
							var iTableNum = 0;
4764
							var sCompletedTableName = getInnerHTML(document.getElementById(\'current_table\'));
4765
							for (var i = 0; i < oXMLDoc.getElementsByTagName("table")[0].childNodes.length; i++)
4766
								sCurrentTableName += oXMLDoc.getElementsByTagName("table")[0].childNodes[i].nodeValue;
4767
							iTableNum = oXMLDoc.getElementsByTagName("table")[0].getAttribute("num");
4768
4769
							// Update the page.
4770
							setInnerHTML(document.getElementById(\'tab_done\'), iTableNum);
4771
							setInnerHTML(document.getElementById(\'current_table\'), sCurrentTableName);
4772
							lastTable = iTableNum;
4773
							updateStepProgress(iTableNum, ', $upcontext['table_count'], ', ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');';
4774
4775
		// If debug flood the screen.
4776
		if ($is_debug)
4777
			echo '
4778
						setOuterHTML(document.getElementById(\'debuginfo\'), \'<br>', $txt['upgrade_completed_table'], ' &quot;\' + sCompletedTableName + \'&quot;.<span id="debuginfo"><\' + \'/span>\');
4779
4780
						if (document.getElementById(\'debug_section\').scrollHeight)
4781
							document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
4782
4783
		echo '
4784
						// Get the next update...
4785
						if (iTableNum == ', $upcontext['table_count'], ')
4786
						{
4787
							document.getElementById(\'commess\').classList.remove(\'hidden\');
4788
							if (document.getElementById(\'indexmsg\') != null) {
4789
								document.getElementById(\'indexmsg\').classList.remove(\'hidden\');
4790
							}
4791
							document.getElementById(\'current_tab\').classList.add(\'hidden\');
4792
							document.getElementById(\'contbutt\').disabled = 0;
4793
							document.getElementById(\'utf8_done\').value = 1;
4794
						}
4795
						else
4796
							getNextTables();
4797
					}
4798
					getNextTables();
4799
				//# sourceURL=dynamicScript-conv.js
4800
				</script>';
4801
	}
4802
}
4803
4804
function template_convert_xml()
4805
{
4806
	global $upcontext;
4807
4808
	echo '
4809
	<table num="', $upcontext['cur_table_num'], '">', $upcontext['cur_table_name'], '</table>';
4810
}
4811
4812
// Template for the database backup tool/
4813
function template_serialize_json()
4814
{
4815
	global $upcontext, $support_js, $is_debug, $txt;
4816
4817
	echo '
4818
				<h3>', $txt['upgrade_convert_datajson'], '</h3>
4819
				<form action="', $upcontext['form_url'], '" name="upform" id="upform" method="post">
4820
					<input type="hidden" name="json_done" id="json_done" value="0">
4821
					<strong>', $txt['upgrade_completed'], ' <span id="tab_done">', $upcontext['cur_table_num'], '</span> ', $txt['upgrade_outof'], ' ', $upcontext['table_count'], ' ', $txt['upgrade_tables'], '</strong>
4822
					<div id="debug_section">
4823
						<span id="debuginfo"></span>
4824
					</div>';
4825
4826
	// Dont any tables so far?
4827
	if (!empty($upcontext['previous_tables']))
4828
		foreach ($upcontext['previous_tables'] as $table)
4829
			echo '
4830
					<br>', $txt['upgrade_completed_table'], ' &quot;', $table, '&quot;.';
4831
4832
	echo '
4833
					<h3 id="current_tab">
4834
						', $txt['upgrade_current_table'], ' &quot;<span id="current_table">', $upcontext['cur_table_name'], '</span>&quot;
4835
					</h3>
4836
					<p id="commess" class="', $upcontext['cur_table_num'] == $upcontext['table_count'] ? 'inline_block' : 'hidden', '">', $txt['upgrade_json_completed'], '</p>';
4837
4838
	// Try to make sure substep was reset.
4839
	if ($upcontext['cur_table_num'] == $upcontext['table_count'])
4840
		echo '
4841
					<input type="hidden" name="substep" id="substep" value="0">';
4842
4843
	// Continue please!
4844
	$upcontext['continue'] = $support_js ? 2 : 1;
4845
4846
	// If javascript allows we want to do this using XML.
4847
	if ($support_js)
4848
	{
4849
		echo '
4850
					<script>
4851
						var lastTable = ', $upcontext['cur_table_num'], ';
4852
						function getNextTables()
4853
						{
4854
							getXMLDocument(\'', $upcontext['form_url'], '&xml&substep=\' + lastTable, onBackupUpdate);
4855
						}
4856
4857
						// Got an update!
4858
						function onBackupUpdate(oXMLDoc)
4859
						{
4860
							var sCurrentTableName = "";
4861
							var iTableNum = 0;
4862
							var sCompletedTableName = getInnerHTML(document.getElementById(\'current_table\'));
4863
							for (var i = 0; i < oXMLDoc.getElementsByTagName("table")[0].childNodes.length; i++)
4864
								sCurrentTableName += oXMLDoc.getElementsByTagName("table")[0].childNodes[i].nodeValue;
4865
							iTableNum = oXMLDoc.getElementsByTagName("table")[0].getAttribute("num");
4866
4867
							// Update the page.
4868
							setInnerHTML(document.getElementById(\'tab_done\'), iTableNum);
4869
							setInnerHTML(document.getElementById(\'current_table\'), sCurrentTableName);
4870
							lastTable = iTableNum;
4871
							updateStepProgress(iTableNum, ', $upcontext['table_count'], ', ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');';
4872
4873
		// If debug flood the screen.
4874
		if ($is_debug)
4875
			echo '
4876
							setOuterHTML(document.getElementById(\'debuginfo\'), \'<br>', $txt['upgrade_completed_table'], ' &quot;\' + sCompletedTableName + \'&quot;.<span id="debuginfo"><\' + \'/span>\');
4877
4878
							if (document.getElementById(\'debug_section\').scrollHeight)
4879
								document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
4880
4881
		echo '
4882
							// Get the next update...
4883
							if (iTableNum == ', $upcontext['table_count'], ')
4884
							{
4885
								document.getElementById(\'commess\').classList.remove("hidden");
4886
								document.getElementById(\'current_tab\').classList.add("hidden");
4887
								document.getElementById(\'contbutt\').disabled = 0;
4888
								document.getElementById(\'json_done\').value = 1;
4889
							}
4890
							else
4891
								getNextTables();
4892
						}
4893
						getNextTables();
4894
					//# sourceURL=dynamicScript-json.js
4895
					</script>';
4896
	}
4897
}
4898
4899
function template_serialize_json_xml()
4900
{
4901
	global $upcontext;
4902
4903
	echo '
4904
	<table num="', $upcontext['cur_table_num'], '">', $upcontext['cur_table_name'], '</table>';
4905
}
4906
4907
function template_upgrade_complete()
4908
{
4909
	global $upcontext, $upgradeurl, $settings, $boardurl, $is_debug, $txt;
4910
4911
	echo '
4912
				<h3>', sprintf($txt['upgrade_done'], $boardurl), '</h3>
4913
				<form action="', $boardurl, '/index.php">';
4914
4915
	if (!empty($upcontext['can_delete_script']))
4916
		echo '
4917
					<label>
4918
						<input type="checkbox" id="delete_self" onclick="doTheDelete(this);"> ', $txt['upgrade_delete_now'], '
4919
					</label>
4920
					<em>', $txt['upgrade_delete_server'], '</em>
4921
					<script>
4922
						function doTheDelete(theCheck)
4923
						{
4924
							var theImage = document.getElementById ? document.getElementById("delete_upgrader") : document.all.delete_upgrader;
4925
							theImage.src = "', $upgradeurl, '?delete=1&ts_" + (new Date().getTime());
4926
							theCheck.disabled = true;
4927
						}
4928
					</script>
4929
					<img src="', $settings['default_theme_url'], '/images/blank.png" alt="" id="delete_upgrader"><br>';
4930
4931
	// Show Upgrade time in debug mode when we completed the upgrade process totally
4932
	if ($is_debug)
4933
	{
4934
		$active = time() - $upcontext['started'];
4935
		$hours = floor($active / 3600);
4936
		$minutes = intval(($active / 60) % 60);
4937
		$seconds = intval($active % 60);
4938
4939
		if ($hours > 0)
4940
			echo '', sprintf($txt['upgrade_completed_time_hms'], $seconds, $minutes, $hours), '';
4941
		elseif ($minutes > 0)
4942
			echo '', sprintf($txt['upgrade_completed_time_ms'], $seconds, $minutes), '';
4943
		elseif ($seconds > 0)
4944
			echo '', sprintf($txt['upgrade_completed_time_s'], $seconds), '';
4945
	}
4946
4947
	echo '
4948
					<p>
4949
						', sprintf($txt['upgrade_problems'], 'https://www.simplemachines.org'), '
4950
						<br>
4951
						', $txt['upgrade_luck'], '<br>
4952
						Simple Machines
4953
					</p>';
4954
}
4955
4956
/**
4957
 * Convert MySQL (var)char ip col to binary
4958
 *
4959
 * newCol needs to be a varbinary(16) null able field
4960
 *
4961
 * @param string $targetTable The table to perform the operation on
4962
 * @param string $oldCol The old column to gather data from
4963
 * @param string $newCol The new column to put data in
4964
 * @param int $limit The amount of entries to handle at once.
4965
 * @param int $setSize The amount of entries after which to update the database.
4966
 * @return bool
4967
 */
4968
function MySQLConvertOldIp($targetTable, $oldCol, $newCol, $limit = 50000, $setSize = 100)
4969
{
4970
	global $smcFunc, $step_progress;
4971
4972
	$current_substep = !isset($_GET['substep']) ? 0 : (int) $_GET['substep'];
4973
4974
	// Skip this if we don't have the column
4975
	$request = $smcFunc['db_query']('', '
4976
		SHOW FIELDS
4977
		FROM {db_prefix}{raw:table}
4978
		WHERE Field = {string:name}',
4979
		array(
4980
			'table' => $targetTable,
4981
			'name' => $oldCol,
4982
		)
4983
	);
4984
	if ($smcFunc['db_num_rows']($request) !== 1)
4985
	{
4986
		$smcFunc['db_free_result']($request);
4987
		return;
4988
	}
4989
	$smcFunc['db_free_result']($request);
4990
4991
	// Setup progress bar
4992
	if (!isset($_GET['total_fixes']) || !isset($_GET['a']))
4993
	{
4994
		$request = $smcFunc['db_query']('', '
4995
			SELECT COUNT(DISTINCT {raw:old_col})
4996
			FROM {db_prefix}{raw:table_name}',
4997
			array(
4998
				'old_col' => $oldCol,
4999
				'table_name' => $targetTable,
5000
			)
5001
		);
5002
		list ($step_progress['total']) = $smcFunc['db_fetch_row']($request);
5003
		$_GET['total_fixes'] = $step_progress['total'];
5004
		$smcFunc['db_free_result']($request);
5005
5006
		$_GET['a'] = 0;
5007
	}
5008
5009
	$step_progress['name'] = 'Converting ips';
5010
	$step_progress['current'] = $_GET['a'];
5011
	$step_progress['total'] = $_GET['total_fixes'];
5012
5013
	// Main process loop
5014
	$is_done = false;
5015
	while (!$is_done)
5016
	{
5017
		// Keep looping at the current step.
5018
		nextSubstep($current_substep);
5019
5020
		// mysql default max length is 1mb https://dev.mysql.com/doc/refman/5.1/en/packet-too-large.html
5021
		$arIp = array();
5022
5023
		$request = $smcFunc['db_query']('', '
5024
			SELECT DISTINCT {raw:old_col}
5025
			FROM {db_prefix}{raw:table_name}
5026
			WHERE {raw:new_col} = {string:empty}
5027
			LIMIT {int:limit}',
5028
			array(
5029
				'old_col' => $oldCol,
5030
				'new_col' => $newCol,
5031
				'table_name' => $targetTable,
5032
				'empty' => '',
5033
				'limit' => $limit,
5034
			)
5035
		);
5036
		while ($row = $smcFunc['db_fetch_assoc']($request))
5037
			$arIp[] = $row[$oldCol];
5038
5039
		$smcFunc['db_free_result']($request);
5040
5041
		if (empty($arIp))
5042
			$is_done = true;
5043
5044
		$updates = array();
5045
		$new_ips = array();
5046
		$cases = array();
5047
		$count = count($arIp);
5048
		for ($i = 0; $i < $count; $i++)
5049
		{
5050
			$new_ip = trim($arIp[$i]);
5051
5052
			$new_ip = filter_var($new_ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6);
5053
			if ($new_ip === false)
5054
				$new_ip = '';
5055
5056
			$updates['ip' . $i] = $arIp[$i];
5057
			$new_ips['newip' . $i] = $new_ip;
5058
			$cases[$arIp[$i]] = 'WHEN ' . $oldCol . ' = {string:ip' . $i . '} THEN {inet:newip' . $i . '}';
5059
5060
			// Execute updates every $setSize & also when done with contents of $arIp
5061
			if ((($i + 1) == $count) || (($i + 1) % $setSize === 0))
5062
			{
5063
				$updates['whereSet'] = array_values($updates);
5064
				$smcFunc['db_query']('', '
5065
					UPDATE {db_prefix}' . $targetTable . '
5066
					SET ' . $newCol . ' = CASE ' .
5067
					implode('
5068
						', $cases) . '
5069
						ELSE NULL
5070
					END
5071
					WHERE ' . $oldCol . ' IN ({array_string:whereSet})',
5072
					array_merge($updates, $new_ips)
5073
				);
5074
5075
				$updates = array();
5076
				$new_ips = array();
5077
				$cases = array();
5078
			}
5079
		}
5080
5081
		$_GET['a'] += $limit;
5082
		$step_progress['current'] = $_GET['a'];
5083
	}
5084
5085
	$step_progress = array();
5086
	unset($_GET['a']);
5087
	unset($_GET['total_fixes']);
5088
}
5089
5090
/**
5091
 * Get the column info. This is basically the same as smf_db_list_columns but we get 1 column, force detail and other checks.
5092
 *
5093
 * @param string $targetTable The table to perform the operation on
5094
 * @param string $column The column we are looking for.
5095
 *
5096
 * @return array Info on the table.
5097
 */
5098
function upgradeGetColumnInfo($targetTable, $column)
5099
{
5100
	global $smcFunc;
5101
5102
	// This should already be here, but be safe.
5103
	db_extend('packages');
5104
5105
	$columns = $smcFunc['db_list_columns']($targetTable, true);
5106
5107
	if (isset($columns[$column]))
5108
		return $columns[$column];
5109
	else
5110
		return null;
5111
}
5112
5113
?>