Passed
Push — release-2.1 ( 908430...ab1855 )
by Mathias
11:01 queued 11s
created

fix_serialized_data()   A

Complexity

Conditions 5
Paths 3

Size

Total Lines 23
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 11
nc 3
nop 1
dl 0
loc 23
rs 9.6111
c 1
b 0
f 0
1
<?php
2
3
/**
4
 * Simple Machines Forum (SMF)
5
 *
6
 * @package SMF
7
 * @author Simple Machines https://www.simplemachines.org
8
 * @copyright 2021 Simple Machines and individual contributors
9
 * @license https://www.simplemachines.org/about/smf/license.php BSD
10
 *
11
 * @version 2.1 RC4
12
 */
13
14
// Version information...
15
define('SMF_VERSION', '2.1 RC4');
16
define('SMF_FULL_VERSION', 'SMF ' . SMF_VERSION);
17
define('SMF_SOFTWARE_YEAR', '2021');
18
define('SMF_LANG_VERSION', '2.1 RC4');
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
Bug introduced by
It seems like mysqli_query($db_connection, 'SELECT VERSION();') can also be of type true; however, parameter $result of mysqli_fetch_row() does only seem to accept mysqli_result, maybe add an additional type check? ( Ignorable by Annotation )

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

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
@set_time_limit(600);
117
if (!ini_get('safe_mode'))
118
{
119
	ini_set('mysql.connect_timeout', -1);
120
	ini_set('default_socket_timeout', 900);
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
Best Practice introduced by
Using exit here is not recommended.

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

Loading history...
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
Best Practice introduced by
Using exit here is not recommended.

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

Loading history...
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
Best Practice introduced by
Using exit here is not recommended.

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

Loading history...
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
Best Practice introduced by
Using exit here is not recommended.

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

Loading history...
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
Best Practice introduced by
Using exit here is not recommended.

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

Loading history...
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
	$smcFunc['random_int'] = function($min = 0, $max = PHP_INT_MAX)
692
	{
693
		global $sourcedir;
694
695
		// Oh, wouldn't it be great if I *was* crazy? Then the world would be okay.
696
		if (!is_callable('random_int'))
697
			require_once($sourcedir . '/random_compat/random.php');
698
699
		return random_int($min, $max);
700
	};
701
702
	// This is now needed for loadUserSettings()
703
	$smcFunc['random_bytes'] = function($bytes)
704
	{
705
		global $sourcedir;
706
707
		if (!is_callable('random_bytes'))
708
			require_once($sourcedir . '/random_compat/random.php');
709
710
		return random_bytes($bytes);
711
	};
712
713
	// We need this for authentication and some upgrade code
714
	require_once($sourcedir . '/Subs-Auth.php');
715
	require_once($sourcedir . '/Class-Package.php');
716
717
	$smcFunc['strtolower'] = 'smf_strtolower';
718
719
	// Initialize everything...
720
	initialize_inputs();
721
722
	$utf8 = (empty($modSettings['global_character_set']) ? $txt['lang_character_set'] : $modSettings['global_character_set']) === 'UTF-8';
723
724
	$smcFunc['normalize'] = function($string, $form = 'c') use ($utf8)
725
	{
726
		global $sourcedir;
727
728
		if (!$utf8)
729
			return $string;
730
731
		require_once($sourcedir . '/Subs-Charset.php');
732
733
		$normalize_func = 'utf8_normalize_' . strtolower((string) $form);
734
735
		if (!function_exists($normalize_func))
736
			return false;
737
738
		return $normalize_func($string);
739
	};
740
741
	// Get the database going!
742
	if (empty($db_type) || $db_type == 'mysqli')
743
	{
744
		$db_type = 'mysql';
745
		// If overriding $db_type, need to set its settings.php entry too
746
		$changes = array();
747
		$changes['db_type'] = 'mysql';
748
		require_once($sourcedir . '/Subs-Admin.php');
749
		updateSettingsFile($changes);
750
	}
751
752
	if (file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php'))
753
	{
754
		require_once($sourcedir . '/Subs-Db-' . $db_type . '.php');
755
756
		// Make the connection...
757
		if (empty($db_connection))
758
		{
759
			$options = array('non_fatal' => true);
760
			// Add in the port if needed
761
			if (!empty($db_port))
762
				$options['port'] = $db_port;
763
764
			if (!empty($db_mb4))
765
				$options['db_mb4'] = $db_mb4;
766
767
			$db_connection = smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, $db_prefix, $options);
768
		}
769
		else
770
			// If we've returned here, ping/reconnect to be safe
771
			$smcFunc['db_ping']($db_connection);
772
773
		// Oh dear god!!
774
		if ($db_connection === null)
775
		{
776
			// Get error info...  Recast just in case we get false or 0...
777
			$error_message = $smcFunc['db_connect_error']();
778
			if (empty($error_message))
779
				$error_message = '';
780
			$error_number = $smcFunc['db_connect_errno']();
781
			if (empty($error_number))
782
				$error_number = '';
783
			$db_error = (!empty($error_number) ? $error_number . ': ' : '') . $error_message;
784
785
			die($txt['error_db_connect_settings'] . '<br><br>' . $db_error);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

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

Loading history...
786
		}
787
788
		if ($db_type == 'mysql' && isset($db_character_set) && preg_match('~^\w+$~', $db_character_set) === 1)
789
			$smcFunc['db_query']('', '
790
				SET NAMES {string:db_character_set}',
791
				array(
792
					'db_error_skip' => true,
793
					'db_character_set' => $db_character_set,
794
				)
795
			);
796
797
		// Load the modSettings data...
798
		$request = $smcFunc['db_query']('', '
799
			SELECT variable, value
800
			FROM {db_prefix}settings',
801
			array(
802
				'db_error_skip' => true,
803
			)
804
		);
805
		$modSettings = array();
806
		while ($row = $smcFunc['db_fetch_assoc']($request))
807
			$modSettings[$row['variable']] = $row['value'];
808
		$smcFunc['db_free_result']($request);
809
	}
810
	else
811
		return throw_error(sprintf($txt['error_sourcefile_missing'], 'Subs-Db-' . $db_type . '.php'));
0 ignored issues
show
Bug introduced by
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

811
		return /** @scrutinizer ignore-call */ throw_error(sprintf($txt['error_sourcefile_missing'], 'Subs-Db-' . $db_type . '.php'));
Loading history...
812
813
	// If they don't have the file, they're going to get a warning anyway so we won't need to clean request vars.
814
	if (file_exists($sourcedir . '/QueryString.php') && php_version_check())
815
	{
816
		require_once($sourcedir . '/QueryString.php');
817
		cleanRequest();
818
	}
819
820
	if (!isset($_GET['substep']))
821
		$_GET['substep'] = 0;
822
}
823
824
function initialize_inputs()
825
{
826
	global $start_time, $db_type, $upgrade_path;
827
828
	$start_time = time();
829
830
	umask(0);
831
832
	ob_start();
833
834
	// Better to upgrade cleanly and fall apart than to screw everything up if things take too long.
835
	ignore_user_abort(true);
836
837
	// This is really quite simple; if ?delete is on the URL, delete the upgrader...
838
	if (isset($_GET['delete']))
839
	{
840
		deleteFile(__FILE__);
0 ignored issues
show
Bug introduced by
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

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

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

948
	$temp = substr(@implode('', /** @scrutinizer ignore-type */ @file($boarddir . '/index.php')), 0, 4096);
Loading history...
949
	preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $temp, $match);
950
	if (empty($match[1]) || (trim($match[1]) != SMF_VERSION))
951
		return throw_error($txt['error_upgrade_old_files']);
952
953
	// What absolutely needs to be writable?
954
	$writable_files = array(
955
		$boarddir . '/Settings.php',
956
		$boarddir . '/Settings_bak.php',
957
	);
958
959
	// Only check for minified writable files if we have it enabled or not set.
960
	if (!empty($modSettings['minimize_files']) || !isset($modSettings['minimize_files']))
961
		$writable_files += array(
962
			$modSettings['theme_dir'] . '/css/minified.css',
963
			$modSettings['theme_dir'] . '/scripts/minified.js',
964
			$modSettings['theme_dir'] . '/scripts/minified_deferred.js',
965
		);
966
967
	// Do we need to add this setting?
968
	$need_settings_update = empty($modSettings['custom_avatar_dir']);
969
970
	$custom_av_dir = !empty($modSettings['custom_avatar_dir']) ? $modSettings['custom_avatar_dir'] : $GLOBALS['boarddir'] . '/custom_avatar';
971
	$custom_av_url = !empty($modSettings['custom_avatar_url']) ? $modSettings['custom_avatar_url'] : $boardurl . '/custom_avatar';
972
973
	// This little fellow has to cooperate...
974
	quickFileWritable($custom_av_dir);
0 ignored issues
show
Bug introduced by
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

974
	/** @scrutinizer ignore-call */ 
975
 quickFileWritable($custom_av_dir);
Loading history...
975
976
	// Are we good now?
977
	if (!is_writable($custom_av_dir))
978
		return throw_error(sprintf($txt['error_dir_not_writable'], $custom_av_dir));
979
	elseif ($need_settings_update)
980
	{
981
		if (!function_exists('cache_put_data'))
982
			require_once($sourcedir . '/Load.php');
983
984
		updateSettings(array('custom_avatar_dir' => $custom_av_dir));
985
		updateSettings(array('custom_avatar_url' => $custom_av_url));
986
	}
987
988
	require_once($sourcedir . '/Security.php');
989
990
	// Check the cache directory.
991
	$cachedir_temp = empty($cachedir) ? $boarddir . '/cache' : $cachedir;
992
	if (!file_exists($cachedir_temp))
993
		@mkdir($cachedir_temp);
994
995
	if (!file_exists($cachedir_temp))
996
		return throw_error($txt['error_cache_not_found']);
997
998
	quickFileWritable($cachedir_temp . '/db_last_error.php');
999
1000
	if (!file_exists($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php'))
1001
		return throw_error(sprintf($txt['error_lang_index_missing'], $upcontext['language'], $upgradeurl));
1002
	elseif (!isset($_GET['skiplang']))
1003
	{
1004
		$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php')), 0, 4096);
1005
		preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
1006
1007
		if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
1008
			return throw_error(sprintf($txt['error_upgrade_old_lang_files'], $upcontext['language'], $upgradeurl));
1009
	}
1010
1011
	if (!makeFilesWritable($writable_files))
0 ignored issues
show
Bug introduced by
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

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

1181
						return /** @scrutinizer ignore-call */ throw_error($txt['error_not_admin']);
Loading history...
1182
					$smcFunc['db_free_result']($request);
1183
				}
1184
1185
				$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...
1186
				$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...
1187
			}
1188
			else
1189
			{
1190
				$upcontext['user']['id'] = 1;
1191
				$upcontext['user']['name'] = 'Administrator';
1192
			}
1193
1194
			if (!is_callable('random_int'))
1195
				require_once('Sources/random_compat/random.php');
1196
1197
			$upcontext['user']['pass'] = random_int(0, 60000);
1198
			// This basically is used to match the GET variables to Settings.php.
1199
			$upcontext['upgrade_status']['pass'] = $upcontext['user']['pass'];
1200
1201
			// Set the language to that of the user?
1202
			if (isset($user_language) && $user_language != $upcontext['language'] && file_exists($modSettings['theme_dir'] . '/languages/index.' . basename($user_language, '.lng') . '.php'))
1203
			{
1204
				$user_language = basename($user_language, '.lng');
1205
				$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $user_language . '.php')), 0, 4096);
0 ignored issues
show
Bug introduced by
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

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

1387
			'cache_accelerator' => /** @scrutinizer ignore-call */ upgradeCacheSettings(),
Loading history...
1388
			'cache_enable' => !empty($modSettings['cache_enable']) ? $modSettings['cache_enable'] : 0,
1389
			'cache_memcached' => !empty($modSettings['cache_memcached']) ? $modSettings['cache_memcached'] : '',
1390
		);
1391
1392
	// If they have a "host:port" setup for the host, split that into separate values
1393
	// You should never have a : in the hostname if you're not on MySQL, but better safe than sorry
1394
	if (strpos($db_server, ':') !== false && $db_type == 'mysql')
1395
	{
1396
		list ($db_server, $db_port) = explode(':', $db_server);
1397
1398
		$changes['db_server'] = $db_server;
1399
1400
		// Only set this if we're not using the default port
1401
		if ($db_port != ini_get('mysqli.default_port'))
1402
			$changes['db_port'] = (int) $db_port;
1403
	}
1404
1405
	// If db_port is set and is the same as the default, set it to 0.
1406
	if (!empty($db_port))
1407
	{
1408
		if ($db_type == 'mysql' && $db_port == ini_get('mysqli.default_port'))
1409
			$changes['db_port'] = 0;
1410
1411
		elseif ($db_type == 'postgresql' && $db_port == 5432)
1412
			$changes['db_port'] = 0;
1413
	}
1414
1415
	// Maybe we haven't had this option yet?
1416
	if (empty($packagesdir))
1417
		$changes['packagesdir'] = fixRelativePath($boarddir) . '/Packages';
1418
1419
	// Add support for $tasksdir var.
1420
	if (empty($tasksdir))
1421
		$changes['tasksdir'] = fixRelativePath($sourcedir) . '/tasks';
1422
1423
	// Make sure we fix the language as well.
1424
	if (stristr($language, '-utf8'))
1425
		$changes['language'] = str_ireplace('-utf8', '', $language);
1426
1427
	// @todo Maybe change the cookie name if going to 1.1, too?
1428
1429
	// Ensure this doesn't get lost in translation.
1430
	$changes['upgradeData'] = base64_encode(json_encode($upcontext['user']));
1431
1432
	// Update Settings.php with the new settings, and rebuild if they selected that option.
1433
	require_once($sourcedir . '/Subs.php');
1434
	require_once($sourcedir . '/Subs-Admin.php');
1435
	$res = updateSettingsFile($changes, false, !empty($_POST['migrateSettings']));
1436
1437
	if ($command_line && $res)
1438
		echo ' Successful.' . "\n";
1439
	elseif ($command_line && !$res)
1440
	{
1441
		echo ' FAILURE.' . "\n";
1442
		die;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

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

Loading history...
1443
	}
1444
1445
	// Are we doing debug?
1446
	if (isset($_POST['debug']))
1447
	{
1448
		$upcontext['upgrade_status']['debug'] = true;
1449
		$is_debug = true;
1450
	}
1451
1452
	// If we're not backing up then jump one.
1453
	if (empty($_POST['backup']))
1454
		$upcontext['current_step']++;
1455
1456
	// If we've got here then let's proceed to the next step!
1457
	return true;
1458
}
1459
1460
// Backup the database - why not...
1461
function BackupDatabase()
1462
{
1463
	global $upcontext, $db_prefix, $command_line, $support_js, $file_steps, $smcFunc, $txt;
1464
1465
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'backup_xml' : 'backup_database';
1466
	$upcontext['page_title'] = $txt['backup_database'];
1467
1468
	// Done it already - js wise?
1469
	if (!empty($_POST['backup_done']))
1470
		return true;
1471
1472
	// We cannot execute this step in strict mode - strict mode data fixes are not applied yet
1473
	setSqlMode(false);
1474
1475
	// Some useful stuff here.
1476
	db_extend();
1477
1478
	// Might need this as well
1479
	db_extend('packages');
1480
1481
	// Get all the table names.
1482
	$filter = str_replace('_', '\_', preg_match('~^`(.+?)`\.(.+?)$~', $db_prefix, $match) != 0 ? $match[2] : $db_prefix) . '%';
1483
	$db = preg_match('~^`(.+?)`\.(.+?)$~', $db_prefix, $match) != 0 ? strtr($match[1], array('`' => '')) : false;
1484
	$tables = $smcFunc['db_list_tables']($db, $filter);
1485
1486
	$table_names = array();
1487
	foreach ($tables as $table)
1488
		if (substr($table, 0, 7) !== 'backup_')
1489
			$table_names[] = $table;
1490
1491
	$upcontext['table_count'] = count($table_names);
1492
	$upcontext['cur_table_num'] = $_GET['substep'];
1493
	$upcontext['cur_table_name'] = str_replace($db_prefix, '', isset($table_names[$_GET['substep']]) ? $table_names[$_GET['substep']] : $table_names[0]);
1494
	$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
1495
	// For non-java auto submit...
1496
	$file_steps = $upcontext['table_count'];
1497
1498
	// What ones have we already done?
1499
	foreach ($table_names as $id => $table)
1500
		if ($id < $_GET['substep'])
1501
			$upcontext['previous_tables'][] = $table;
1502
1503
	if ($command_line)
1504
		echo 'Backing Up Tables.';
1505
1506
	// If we don't support javascript we backup here.
1507
	if (!$support_js || isset($_GET['xml']))
1508
	{
1509
		// Backup each table!
1510
		for ($substep = $_GET['substep'], $n = count($table_names); $substep < $n; $substep++)
1511
		{
1512
			$upcontext['cur_table_name'] = str_replace($db_prefix, '', (isset($table_names[$substep + 1]) ? $table_names[$substep + 1] : $table_names[$substep]));
1513
			$upcontext['cur_table_num'] = $substep + 1;
1514
1515
			$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
1516
1517
			// Do we need to pause?
1518
			nextSubstep($substep);
1519
1520
			backupTable($table_names[$substep]);
1521
1522
			// If this is XML to keep it nice for the user do one table at a time anyway!
1523
			if (isset($_GET['xml']))
1524
				return upgradeExit();
0 ignored issues
show
Bug introduced by
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...
1525
		}
1526
1527
		if ($command_line)
1528
		{
1529
			echo "\n" . ' Successful.\'' . "\n";
1530
			flush();
1531
		}
1532
		$upcontext['step_progress'] = 100;
1533
1534
		$_GET['substep'] = 0;
1535
		// Make sure we move on!
1536
		return true;
1537
	}
1538
1539
	// Either way next place to post will be database changes!
1540
	$_GET['substep'] = 0;
1541
	return false;
1542
}
1543
1544
// Backup one table...
1545
function backupTable($table)
1546
{
1547
	global $command_line, $db_prefix, $smcFunc;
1548
1549
	if ($command_line)
1550
	{
1551
		echo "\n" . ' +++ Backing up \"' . str_replace($db_prefix, '', $table) . '"...';
1552
		flush();
1553
	}
1554
1555
	$smcFunc['db_backup_table']($table, 'backup_' . $table);
1556
1557
	if ($command_line)
1558
		echo ' done.';
1559
}
1560
1561
// Step 2: Everything.
1562
function DatabaseChanges()
1563
{
1564
	global $db_prefix, $modSettings, $smcFunc, $txt;
1565
	global $upcontext, $support_js, $db_type, $boarddir;
1566
1567
	// Have we just completed this?
1568
	if (!empty($_POST['database_done']))
1569
		return true;
1570
1571
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'database_xml' : 'database_changes';
1572
	$upcontext['page_title'] = $txt['database_changes'];
1573
1574
	$upcontext['delete_karma'] = !empty($_SESSION['delete_karma']);
1575
	$upcontext['empty_error'] = !empty($_SESSION['empty_error']);
1576
1577
	// All possible files.
1578
	// Name, < version, insert_on_complete
1579
	// Last entry in array indicates whether to use sql_mode of STRICT or not.
1580
	$files = array(
1581
		array('upgrade_1-0.sql', '1.1', '1.1 RC0', false),
1582
		array('upgrade_1-1.sql', '2.0', '2.0 a', false),
1583
		array('upgrade_2-0_' . $db_type . '.sql', '2.1', '2.1 dev0', false),
1584
		array('upgrade_2-1_' . $db_type . '.sql', '3.0', SMF_VERSION, true),
1585
	);
1586
1587
	// How many files are there in total?
1588
	if (isset($_GET['filecount']))
1589
		$upcontext['file_count'] = (int) $_GET['filecount'];
1590
	else
1591
	{
1592
		$upcontext['file_count'] = 0;
1593
		foreach ($files as $file)
1594
		{
1595
			if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < $file[1])
1596
				$upcontext['file_count']++;
1597
		}
1598
	}
1599
1600
	// Do each file!
1601
	$did_not_do = count($files) - $upcontext['file_count'];
1602
	$upcontext['step_progress'] = 0;
1603
	$upcontext['cur_file_num'] = 0;
1604
	foreach ($files as $file)
1605
	{
1606
		if ($did_not_do)
1607
			$did_not_do--;
1608
		else
1609
		{
1610
			$upcontext['cur_file_num']++;
1611
			$upcontext['cur_file_name'] = $file[0];
1612
			// Do we actually need to do this still?
1613
			if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < $file[1])
1614
			{
1615
				// Use STRICT mode on more recent steps
1616
				setSqlMode($file[3]);
1617
1618
				// Reload modSettings to capture any adds/updates made along the way
1619
				$request = $smcFunc['db_query']('', '
1620
					SELECT variable, value
1621
					FROM {db_prefix}settings',
1622
					array(
1623
						'db_error_skip' => true,
1624
					)
1625
				);
1626
1627
				$modSettings = array();
1628
				while ($row = $smcFunc['db_fetch_assoc']($request))
1629
					$modSettings[$row['variable']] = $row['value'];
1630
1631
				$smcFunc['db_free_result']($request);
1632
1633
				// Some theme settings are in $modSettings
1634
				// Note we still might be doing yabbse (no smf ver)
1635
				if (isset($modSettings['smfVersion']))
1636
				{
1637
					$request = $smcFunc['db_query']('', '
1638
						SELECT variable, value
1639
						FROM {db_prefix}themes
1640
						WHERE id_theme = {int:id_theme}
1641
							AND variable IN ({string:theme_url}, {string:theme_dir}, {string:images_url})',
1642
						array(
1643
							'id_theme' => 1,
1644
							'theme_url' => 'theme_url',
1645
							'theme_dir' => 'theme_dir',
1646
							'images_url' => 'images_url',
1647
							'db_error_skip' => true,
1648
						)
1649
					);
1650
1651
					while ($row = $smcFunc['db_fetch_assoc']($request))
1652
						$modSettings[$row['variable']] = $row['value'];
1653
1654
					$smcFunc['db_free_result']($request);
1655
				}
1656
1657
				if (!isset($modSettings['theme_url']))
1658
				{
1659
					$modSettings['theme_dir'] = $boarddir . '/Themes/default';
1660
					$modSettings['theme_url'] = 'Themes/default';
1661
					$modSettings['images_url'] = 'Themes/default/images';
1662
				}
1663
1664
				// Now process the file...
1665
				$nextFile = parse_sql(dirname(__FILE__) . '/' . $file[0]);
1666
				if ($nextFile)
1667
				{
1668
					// Only update the version of this if complete.
1669
					$smcFunc['db_insert']('replace',
1670
						$db_prefix . 'settings',
1671
						array('variable' => 'string', 'value' => 'string'),
1672
						array('smfVersion', $file[2]),
1673
						array('variable')
1674
					);
1675
1676
					$modSettings['smfVersion'] = $file[2];
1677
				}
1678
1679
				// If this is XML we only do this stuff once.
1680
				if (isset($_GET['xml']))
1681
				{
1682
					// Flag to move on to the next.
1683
					$upcontext['completed_step'] = true;
1684
					// Did we complete the whole file?
1685
					if ($nextFile)
1686
						$upcontext['current_debug_item_num'] = -1;
1687
					return upgradeExit();
0 ignored issues
show
Bug introduced by
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...
1688
				}
1689
				elseif ($support_js)
1690
					break;
1691
			}
1692
			// Set the progress bar to be right as if we had - even if we hadn't...
1693
			$upcontext['step_progress'] = ($upcontext['cur_file_num'] / $upcontext['file_count']) * 100;
1694
		}
1695
	}
1696
1697
	$_GET['substep'] = 0;
1698
	// So the template knows we're done.
1699
	if (!$support_js)
1700
	{
1701
		$upcontext['changes_complete'] = true;
1702
1703
		return true;
1704
	}
1705
	return false;
1706
}
1707
1708
// Different versions of the files use different sql_modes
1709
function setSqlMode($strict = true)
1710
{
1711
	global $db_type, $db_connection;
1712
1713
	if ($db_type != 'mysql')
1714
		return;
1715
1716
	if ($strict)
1717
		$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';
1718
	else
1719
		$mode = '';
1720
1721
	mysqli_query($db_connection, 'SET SESSION sql_mode = \'' . $mode . '\'');
1722
1723
	return;
1724
}
1725
1726
// Delete the damn thing!
1727
function DeleteUpgrade()
1728
{
1729
	global $command_line, $language, $upcontext, $sourcedir;
1730
	global $user_info, $maintenance, $smcFunc, $db_type, $txt, $settings;
1731
1732
	// Now it's nice to have some of the basic SMF source files.
1733
	if (!isset($_GET['ssi']) && !$command_line)
1734
		redirectLocation('&ssi=1');
1735
1736
	$upcontext['sub_template'] = 'upgrade_complete';
1737
	$upcontext['page_title'] = $txt['upgrade_complete'];
1738
1739
	$endl = $command_line ? "\n" : '<br>' . "\n";
1740
1741
	$changes = array(
1742
		'language' => (substr($language, -4) == '.lng' ? substr($language, 0, -4) : $language),
1743
		'db_error_send' => true,
1744
		'upgradeData' => null,
1745
	);
1746
1747
	// Are we in maintenance mode?
1748
	if (isset($upcontext['user']['main']))
1749
	{
1750
		if ($command_line)
1751
			echo ' * ';
1752
		$upcontext['removed_maintenance'] = true;
1753
		$changes['maintenance'] = $upcontext['user']['main'];
1754
	}
1755
	// Otherwise if somehow we are in 2 let's go to 1.
1756
	elseif (!empty($maintenance) && $maintenance == 2)
1757
		$changes['maintenance'] = 1;
1758
1759
	// Wipe this out...
1760
	$upcontext['user'] = array();
1761
1762
	require_once($sourcedir . '/Subs.php');
1763
	require_once($sourcedir . '/Subs-Admin.php');
1764
	updateSettingsFile($changes);
1765
1766
	// Clean any old cache files away.
1767
	upgrade_clean_cache();
0 ignored issues
show
Bug introduced by
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

1767
	/** @scrutinizer ignore-call */ 
1768
 upgrade_clean_cache();
Loading history...
1768
1769
	// Can we delete the file?
1770
	$upcontext['can_delete_script'] = is_writable(dirname(__FILE__)) || is_writable(__FILE__);
1771
1772
	// Now is the perfect time to fetch the SM files.
1773
	if ($command_line)
1774
		cli_scheduled_fetchSMfiles();
1775
	else
1776
	{
1777
		require_once($sourcedir . '/ScheduledTasks.php');
1778
		scheduled_fetchSMfiles(); // Now go get those files!
1779
		// This is needed in case someone invokes the upgrader using https when upgrading an http forum
1780
		if (httpsOn())
1781
			$settings['default_theme_url'] = strtr($settings['default_theme_url'], array('http://' => 'https://'));
1782
	}
1783
1784
	// Log what we've done.
1785
	if (empty($user_info['id']))
1786
		$user_info['id'] = !empty($upcontext['user']['id']) ? $upcontext['user']['id'] : 0;
1787
1788
	// Log the action manually, so CLI still works.
1789
	$smcFunc['db_insert']('',
1790
		'{db_prefix}log_actions',
1791
		array(
1792
			'log_time' => 'int', 'id_log' => 'int', 'id_member' => 'int', 'ip' => 'inet', 'action' => 'string',
1793
			'id_board' => 'int', 'id_topic' => 'int', 'id_msg' => 'int', 'extra' => 'string-65534',
1794
		),
1795
		array(
1796
			time(), 3, $user_info['id'], $command_line ? '127.0.0.1' : $user_info['ip'], 'upgrade',
1797
			0, 0, 0, json_encode(array('version' => SMF_FULL_VERSION, 'member' => $user_info['id'])),
1798
		),
1799
		array('id_action')
1800
	);
1801
	$user_info['id'] = 0;
1802
1803
	if ($command_line)
1804
	{
1805
		echo $endl;
1806
		echo 'Upgrade Complete!', $endl;
1807
		echo 'Please delete this file as soon as possible for security reasons.', $endl;
1808
		exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

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

Loading history...
1809
	}
1810
1811
	// Make sure it says we're done.
1812
	$upcontext['overall_percent'] = 100;
1813
	if (isset($upcontext['step_progress']))
1814
		unset($upcontext['step_progress']);
1815
1816
	$_GET['substep'] = 0;
1817
	return false;
1818
}
1819
1820
// Just like the built in one, but setup for CLI to not use themes.
1821
function cli_scheduled_fetchSMfiles()
1822
{
1823
	global $sourcedir, $language, $modSettings, $smcFunc;
1824
1825
	if (empty($modSettings['time_format']))
1826
		$modSettings['time_format'] = '%B %d, %Y, %I:%M:%S %p';
1827
1828
	// What files do we want to get
1829
	$request = $smcFunc['db_query']('', '
1830
		SELECT id_file, filename, path, parameters
1831
		FROM {db_prefix}admin_info_files',
1832
		array(
1833
		)
1834
	);
1835
1836
	$js_files = array();
1837
	while ($row = $smcFunc['db_fetch_assoc']($request))
1838
	{
1839
		$js_files[$row['id_file']] = array(
1840
			'filename' => $row['filename'],
1841
			'path' => $row['path'],
1842
			'parameters' => sprintf($row['parameters'], $language, urlencode($modSettings['time_format']), urlencode(SMF_FULL_VERSION)),
1843
		);
1844
	}
1845
	$smcFunc['db_free_result']($request);
1846
1847
	// We're gonna need fetch_web_data() to pull this off.
1848
	require_once($sourcedir . '/Subs.php');
1849
1850
	foreach ($js_files as $ID_FILE => $file)
1851
	{
1852
		// Create the url
1853
		$server = empty($file['path']) || substr($file['path'], 0, 7) != 'http://' ? 'https://www.simplemachines.org' : '';
1854
		$url = $server . (!empty($file['path']) ? $file['path'] : $file['path']) . $file['filename'] . (!empty($file['parameters']) ? '?' . $file['parameters'] : '');
1855
1856
		// Get the file
1857
		$file_data = fetch_web_data($url);
1858
1859
		// If we got an error - give up - the site might be down.
1860
		if ($file_data === false)
1861
			return throw_error(sprintf('Could not retrieve the file %1$s.', $url));
0 ignored issues
show
Bug introduced by
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

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

2299
			$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...
2300
			mysqli_select_db($db_connection, $db_name);
2301
			if ($db_connection)
2302
			{
2303
				$result = mysqli_query($db_connection, $string);
2304
				if ($result !== false)
2305
					return $result;
2306
			}
2307
		}
2308
		// Duplicate column name... should be okay ;).
2309
		elseif (in_array($mysqli_errno, array(1060, 1061, 1068, 1091)))
2310
			return false;
2311
		// Duplicate insert... make sure it's the proper type of query ;).
2312
		elseif (in_array($mysqli_errno, array(1054, 1062, 1146)) && $error_query)
2313
			return false;
2314
		// Creating an index on a non-existent column.
2315
		elseif ($mysqli_errno == 1072)
2316
			return false;
2317
		elseif ($mysqli_errno == 1050 && substr(trim($string), 0, 12) == 'RENAME TABLE')
2318
			return false;
2319
		// Testing for legacy tables or columns? Needed for 1.0 & 1.1 scripts.
2320
		elseif (in_array($mysqli_errno, array(1054, 1146)) && in_array(substr(trim($string), 0, 7), array('SELECT ', 'SHOW CO')))
2321
			return false;
2322
	}
2323
	// If a table already exists don't go potty.
2324
	else
2325
	{
2326
		if (in_array(substr(trim($string), 0, 8), array('CREATE T', 'CREATE S', 'DROP TABL', 'ALTER TA', 'CREATE I', 'CREATE U')))
2327
		{
2328
			if (strpos($db_error_message, 'exist') !== false)
2329
				return true;
2330
		}
2331
		elseif (strpos(trim($string), 'INSERT ') !== false)
2332
		{
2333
			if (strpos($db_error_message, 'duplicate') !== false || $ignore_insert_error)
2334
				return true;
2335
		}
2336
	}
2337
2338
	// Get the query string so we pass everything.
2339
	$query_string = '';
2340
	foreach ($_GET as $k => $v)
2341
		$query_string .= ';' . $k . '=' . $v;
2342
	if (strlen($query_string) != 0)
2343
		$query_string = '?' . substr($query_string, 1);
2344
2345
	if ($command_line)
2346
	{
2347
		echo 'Unsuccessful!  Database error message:', "\n", $db_error_message, "\n";
2348
		die;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

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

Loading history...
2349
	}
2350
2351
	// Bit of a bodge - do we want the error?
2352
	if (!empty($upcontext['return_error']))
2353
	{
2354
		$upcontext['error_message'] = $db_error_message;
2355
		$upcontext['error_string'] = $string;
2356
		return false;
2357
	}
2358
2359
	// Otherwise we have to display this somewhere appropriate if possible.
2360
	$upcontext['forced_error_message'] = '
2361
			<strong>' . $txt['upgrade_unsuccessful'] . '</strong><br>
2362
2363
			<div style="margin: 2ex;">
2364
				' . $txt['upgrade_thisquery'] . '
2365
				<blockquote><pre>' . nl2br(htmlspecialchars(trim($string))) . ';</pre></blockquote>
2366
2367
				' . $txt['upgrade_causerror'] . '
2368
				<blockquote>' . nl2br(htmlspecialchars($db_error_message)) . '</blockquote>
2369
			</div>
2370
2371
			<form action="' . $upgradeurl . $query_string . '" method="post">
2372
				<input type="submit" value="' . $txt['upgrade_respondtime_clickhere'] . '" class="button">
2373
			</form>
2374
		</div>';
2375
2376
	upgradeExit();
2377
}
2378
2379
// This performs a table alter, but does it unbuffered so the script can time out professionally.
2380
function protected_alter($change, $substep, $is_test = false)
2381
{
2382
	global $db_prefix, $smcFunc;
2383
2384
	db_extend('packages');
2385
2386
	// Firstly, check whether the current index/column exists.
2387
	$found = false;
2388
	if ($change['type'] === 'column')
2389
	{
2390
		$columns = $smcFunc['db_list_columns']('{db_prefix}' . $change['table'], true);
2391
		foreach ($columns as $column)
2392
		{
2393
			// Found it?
2394
			if ($column['name'] === $change['name'])
2395
			{
2396
				$found |= true;
2397
				// Do some checks on the data if we have it set.
2398
				if (isset($change['col_type']))
2399
					$found &= $change['col_type'] === $column['type'];
2400
				if (isset($change['null_allowed']))
2401
					$found &= $column['null'] == $change['null_allowed'];
2402
				if (isset($change['default']))
2403
					$found &= $change['default'] === $column['default'];
2404
			}
2405
		}
2406
	}
2407
	elseif ($change['type'] === 'index')
2408
	{
2409
		$request = upgrade_query('
2410
			SHOW INDEX
2411
			FROM ' . $db_prefix . $change['table']);
2412
		if ($request !== false)
2413
		{
2414
			$cur_index = array();
2415
2416
			while ($row = $smcFunc['db_fetch_assoc']($request))
2417
				if ($row['Key_name'] === $change['name'])
2418
					$cur_index[(int) $row['Seq_in_index']] = $row['Column_name'];
2419
2420
			ksort($cur_index, SORT_NUMERIC);
2421
			$found = array_values($cur_index) === $change['target_columns'];
2422
2423
			$smcFunc['db_free_result']($request);
2424
		}
2425
	}
2426
2427
	// If we're trying to add and it's added, we're done.
2428
	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...
2429
		return true;
2430
	// Otherwise if we're removing and it wasn't found we're also done.
2431
	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...
2432
		return true;
2433
	// Otherwise is it just a test?
2434
	elseif ($is_test)
2435
		return false;
2436
2437
	// Not found it yet? Bummer! How about we see if we're currently doing it?
2438
	$running = false;
2439
	$found = false;
2440
	while (1 == 1)
2441
	{
2442
		$request = upgrade_query('
2443
			SHOW FULL PROCESSLIST');
2444
		while ($row = $smcFunc['db_fetch_assoc']($request))
2445
		{
2446
			if (strpos($row['Info'], 'ALTER TABLE ' . $db_prefix . $change['table']) !== false && strpos($row['Info'], $change['text']) !== false)
2447
				$found = true;
2448
		}
2449
2450
		// Can't find it? Then we need to run it fools!
2451
		if (!$found && !$running)
2452
		{
2453
			$smcFunc['db_free_result']($request);
2454
2455
			$success = upgrade_query('
2456
				ALTER TABLE ' . $db_prefix . $change['table'] . '
2457
				' . $change['text'], true) !== false;
2458
2459
			if (!$success)
2460
				return false;
2461
2462
			// Return
2463
			$running = true;
2464
		}
2465
		// What if we've not found it, but we'd ran it already? Must of completed.
2466
		elseif (!$found)
2467
		{
2468
			$smcFunc['db_free_result']($request);
2469
			return true;
2470
		}
2471
2472
		// Pause execution for a sec or three.
2473
		sleep(3);
2474
2475
		// Can never be too well protected.
2476
		nextSubstep($substep);
2477
	}
2478
2479
	// Protect it.
2480
	nextSubstep($substep);
2481
}
2482
2483
/**
2484
 * Alter a text column definition preserving its character set.
2485
 *
2486
 * @param array $change
2487
 * @param int $substep
2488
 */
2489
function textfield_alter($change, $substep)
2490
{
2491
	global $db_prefix, $smcFunc;
2492
2493
	$request = $smcFunc['db_query']('', '
2494
		SHOW FULL COLUMNS
2495
		FROM {db_prefix}' . $change['table'] . '
2496
		LIKE {string:column}',
2497
		array(
2498
			'column' => $change['column'],
2499
			'db_error_skip' => true,
2500
		)
2501
	);
2502
	if ($smcFunc['db_num_rows']($request) === 0)
2503
		die('Unable to find column ' . $change['column'] . ' inside table ' . $db_prefix . $change['table']);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

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

Loading history...
2504
	$table_row = $smcFunc['db_fetch_assoc']($request);
2505
	$smcFunc['db_free_result']($request);
2506
2507
	// If something of the current column definition is different, fix it.
2508
	$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']);
2509
2510
	// Columns that previously allowed null, need to be converted first.
2511
	$null_fix = strtolower($table_row['Null']) === 'yes' && !$change['null_allowed'];
2512
2513
	// Get the character set that goes with the collation of the column.
2514
	if ($column_fix && !empty($table_row['Collation']))
2515
	{
2516
		$request = $smcFunc['db_query']('', '
2517
			SHOW COLLATION
2518
			LIKE {string:collation}',
2519
			array(
2520
				'collation' => $table_row['Collation'],
2521
				'db_error_skip' => true,
2522
			)
2523
		);
2524
		// No results? Just forget it all together.
2525
		if ($smcFunc['db_num_rows']($request) === 0)
2526
			unset($table_row['Collation']);
2527
		else
2528
			$collation_info = $smcFunc['db_fetch_assoc']($request);
2529
		$smcFunc['db_free_result']($request);
2530
	}
2531
2532
	if ($column_fix)
2533
	{
2534
		// Make sure there are no NULL's left.
2535
		if ($null_fix)
2536
			$smcFunc['db_query']('', '
2537
				UPDATE {db_prefix}' . $change['table'] . '
2538
				SET ' . $change['column'] . ' = {string:default}
2539
				WHERE ' . $change['column'] . ' IS NULL',
2540
				array(
2541
					'default' => isset($change['default']) ? $change['default'] : '',
2542
					'db_error_skip' => true,
2543
				)
2544
			);
2545
2546
		// Do the actual alteration.
2547
		$smcFunc['db_query']('', '
2548
			ALTER TABLE {db_prefix}' . $change['table'] . '
2549
			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}' : ''),
2550
			array(
2551
				'default' => isset($change['default']) ? $change['default'] : '',
2552
				'db_error_skip' => true,
2553
			)
2554
		);
2555
	}
2556
	nextSubstep($substep);
2557
}
2558
2559
// Check if we need to alter this query.
2560
function checkChange(&$change)
2561
{
2562
	global $smcFunc, $db_type, $databases;
2563
	static $database_version, $where_field_support;
2564
2565
	// Attempt to find a database_version.
2566
	if (empty($database_version))
2567
	{
2568
		$database_version = $databases[$db_type]['version_check']();
2569
		$where_field_support = $db_type == 'mysql' && version_compare('5.0', $database_version, '<=');
2570
	}
2571
2572
	// Not a column we need to check on?
2573
	if (!in_array($change['name'], array('memberGroups', 'passwordSalt')))
2574
		return;
2575
2576
	// Break it up you (six|seven).
2577
	$temp = explode(' ', str_replace('NOT NULL', 'NOT_NULL', $change['text']));
2578
2579
	// Can we support a shortcut method?
2580
	if ($where_field_support)
2581
	{
2582
		// Get the details about this change.
2583
		$request = $smcFunc['db_query']('', '
2584
			SHOW FIELDS
2585
			FROM {db_prefix}{raw:table}
2586
			WHERE Field = {string:old_name} OR Field = {string:new_name}',
2587
			array(
2588
				'table' => $change['table'],
2589
				'old_name' => $temp[1],
2590
				'new_name' => $temp[2],
2591
			)
2592
		);
2593
		// !!! This doesn't technically work because we don't pass request into it, but it hasn't broke anything yet.
2594
		if ($smcFunc['db_num_rows'] != 1)
2595
			return;
2596
2597
		list (, $current_type) = $smcFunc['db_fetch_assoc']($request);
2598
		$smcFunc['db_free_result']($request);
2599
	}
2600
	else
2601
	{
2602
		// Do this the old fashion, sure method way.
2603
		$request = $smcFunc['db_query']('', '
2604
			SHOW FIELDS
2605
			FROM {db_prefix}{raw:table}',
2606
			array(
2607
				'table' => $change['table'],
2608
			)
2609
		);
2610
		// Mayday!
2611
		// !!! This doesn't technically work because we don't pass request into it, but it hasn't broke anything yet.
2612
		if ($smcFunc['db_num_rows'] == 0)
2613
			return;
2614
2615
		// Oh where, oh where has my little field gone. Oh where can it be...
2616
		while ($row = $smcFunc['db_query']($request))
2617
			if ($row['Field'] == $temp[1] || $row['Field'] == $temp[2])
2618
			{
2619
				$current_type = $row['Type'];
2620
				break;
2621
			}
2622
	}
2623
2624
	// If this doesn't match, the column may of been altered for a reason.
2625
	if (trim($current_type) != trim($temp[3]))
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $current_type does not seem to be defined for all execution paths leading up to this point.
Loading history...
2626
		$temp[3] = $current_type;
2627
2628
	// Piece this back together.
2629
	$change['text'] = str_replace('NOT_NULL', 'NOT NULL', implode(' ', $temp));
2630
}
2631
2632
// The next substep.
2633
function nextSubstep($substep)
2634
{
2635
	global $start_time, $timeLimitThreshold, $command_line, $custom_warning;
2636
	global $step_progress, $is_debug, $upcontext;
2637
2638
	if ($_GET['substep'] < $substep)
2639
		$_GET['substep'] = $substep;
2640
2641
	if ($command_line)
2642
	{
2643
		if (time() - $start_time > 1 && empty($is_debug))
2644
		{
2645
			echo '.';
2646
			$start_time = time();
2647
		}
2648
		return;
2649
	}
2650
2651
	@set_time_limit(300);
2652
	if (function_exists('apache_reset_timeout'))
2653
		@apache_reset_timeout();
2654
2655
	if (time() - $start_time <= $timeLimitThreshold)
2656
		return;
2657
2658
	// Do we have some custom step progress stuff?
2659
	if (!empty($step_progress))
2660
	{
2661
		$upcontext['substep_progress'] = 0;
2662
		$upcontext['substep_progress_name'] = $step_progress['name'];
2663
		if ($step_progress['current'] > $step_progress['total'])
2664
			$upcontext['substep_progress'] = 99.9;
2665
		else
2666
			$upcontext['substep_progress'] = ($step_progress['current'] / $step_progress['total']) * 100;
2667
2668
		// Make it nicely rounded.
2669
		$upcontext['substep_progress'] = round($upcontext['substep_progress'], 1);
2670
	}
2671
2672
	// If this is XML we just exit right away!
2673
	if (isset($_GET['xml']))
2674
		return upgradeExit();
0 ignored issues
show
Bug introduced by
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...
2675
2676
	// We're going to pause after this!
2677
	$upcontext['pause'] = true;
2678
2679
	$upcontext['query_string'] = '';
2680
	foreach ($_GET as $k => $v)
2681
	{
2682
		if ($k != 'data' && $k != 'substep' && $k != 'step')
2683
			$upcontext['query_string'] .= ';' . $k . '=' . $v;
2684
	}
2685
2686
	// Custom warning?
2687
	if (!empty($custom_warning))
2688
		$upcontext['custom_warning'] = $custom_warning;
2689
2690
	upgradeExit();
2691
}
2692
2693
function cmdStep0()
2694
{
2695
	global $boarddir, $sourcedir, $modSettings, $start_time, $cachedir, $databases, $db_type, $smcFunc, $upcontext;
2696
	global $is_debug, $boardurl, $txt;
2697
	$start_time = time();
2698
2699
	while (ob_get_level() > 0)
2700
		ob_end_clean();
2701
	ob_implicit_flush(1);
2702
	@set_time_limit(600);
2703
2704
	if (!isset($_SERVER['argv']))
2705
		$_SERVER['argv'] = array();
2706
	$_GET['maint'] = 1;
2707
2708
	foreach ($_SERVER['argv'] as $i => $arg)
2709
	{
2710
		if (preg_match('~^--language=(.+)$~', $arg, $match) != 0)
2711
			$upcontext['lang'] = $match[1];
2712
		elseif (preg_match('~^--path=(.+)$~', $arg) != 0)
2713
			continue;
2714
		elseif ($arg == '--no-maintenance')
2715
			$_GET['maint'] = 0;
2716
		elseif ($arg == '--debug')
2717
			$is_debug = true;
2718
		elseif ($arg == '--backup')
2719
			$_POST['backup'] = 1;
2720
		elseif ($arg == '--rebuild-settings')
2721
			$_POST['migrateSettings'] = 1;
2722
		elseif ($arg == '--allow-stats')
2723
			$_POST['stats'] = 1;
2724
		elseif ($arg == '--template' && (file_exists($boarddir . '/template.php') || file_exists($boarddir . '/template.html') && !file_exists($modSettings['theme_dir'] . '/converted')))
2725
			$_GET['conv'] = 1;
2726
		elseif ($i != 0)
2727
		{
2728
			echo 'SMF Command-line Upgrader
2729
Usage: /path/to/php -f ' . basename(__FILE__) . ' -- [OPTION]...
2730
2731
	--path=/path/to/SMF     Specify custom SMF root directory.
2732
	--language=LANG         Reset the forum\'s language to LANG.
2733
	--no-maintenance        Don\'t put the forum into maintenance mode.
2734
	--debug                 Output debugging information.
2735
	--backup                Create backups of tables with "backup_" prefix.
2736
	--allow-stats           Allow Simple Machines stat collection
2737
	--rebuild-settings      Rebuild the Settings.php file';
2738
			echo "\n";
2739
			exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

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

Loading history...
2740
		}
2741
	}
2742
2743
	if (!php_version_check())
2744
		print_error('Error: PHP ' . PHP_VERSION . ' does not match version requirements.', true);
0 ignored issues
show
Bug introduced by
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

2744
		/** @scrutinizer ignore-call */ 
2745
  print_error('Error: PHP ' . PHP_VERSION . ' does not match version requirements.', true);
Loading history...
2745
	if (!db_version_check())
2746
		print_error('Error: ' . $databases[$db_type]['name'] . ' ' . $databases[$db_type]['version'] . ' does not match minimum requirements.', true);
2747
2748
	// Do some checks to make sure they have proper privileges
2749
	db_extend('packages');
2750
2751
	// CREATE
2752
	$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');
2753
2754
	// ALTER
2755
	$alter = $smcFunc['db_add_column']('{db_prefix}priv_check', array('name' => 'txt', 'type' => 'varchar', 'size' => 4, 'null' => false, 'default' => ''));
2756
2757
	// DROP
2758
	$drop = $smcFunc['db_drop_table']('{db_prefix}priv_check');
2759
2760
	// Sorry... we need CREATE, ALTER and DROP
2761
	if (!$create || !$alter || !$drop)
2762
		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);
2763
2764
	$check = @file_exists($modSettings['theme_dir'] . '/index.template.php')
2765
		&& @file_exists($sourcedir . '/QueryString.php')
2766
		&& @file_exists($sourcedir . '/ManageBoards.php');
2767
	if (!$check && !isset($modSettings['smfVersion']))
2768
		print_error('Error: Some files are missing or out-of-date.', true);
2769
2770
	// Do a quick version spot check.
2771
	$temp = substr(@implode('', @file($boarddir . '/index.php')), 0, 4096);
0 ignored issues
show
Bug introduced by
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

2771
	$temp = substr(@implode('', /** @scrutinizer ignore-type */ @file($boarddir . '/index.php')), 0, 4096);
Loading history...
2772
	preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $temp, $match);
2773
	if (empty($match[1]) || (trim($match[1]) != SMF_VERSION))
2774
		print_error('Error: Some files have not yet been updated properly.');
2775
2776
	// Make sure Settings.php is writable.
2777
	quickFileWritable($boarddir . '/Settings.php');
0 ignored issues
show
Bug introduced by
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

2777
	/** @scrutinizer ignore-call */ 
2778
 quickFileWritable($boarddir . '/Settings.php');
Loading history...
2778
	if (!is_writable($boarddir . '/Settings.php'))
2779
		print_error('Error: Unable to obtain write access to "Settings.php".', true);
2780
2781
	// Make sure Settings_bak.php is writable.
2782
	quickFileWritable($boarddir . '/Settings_bak.php');
2783
	if (!is_writable($boarddir . '/Settings_bak.php'))
2784
		print_error('Error: Unable to obtain write access to "Settings_bak.php".');
2785
2786
	if (isset($modSettings['agreement']) && (!is_writable($boarddir) || file_exists($boarddir . '/agreement.txt')) && !is_writable($boarddir . '/agreement.txt'))
2787
		print_error('Error: Unable to obtain write access to "agreement.txt".');
2788
	elseif (isset($modSettings['agreement']))
2789
	{
2790
		$fp = fopen($boarddir . '/agreement.txt', 'w');
2791
		fwrite($fp, $modSettings['agreement']);
2792
		fclose($fp);
2793
	}
2794
2795
	// Make sure Themes is writable.
2796
	quickFileWritable($modSettings['theme_dir']);
2797
2798
	if (!is_writable($modSettings['theme_dir']) && !isset($modSettings['smfVersion']))
2799
		print_error('Error: Unable to obtain write access to "Themes".');
2800
2801
	// Make sure cache directory exists and is writable!
2802
	$cachedir_temp = empty($cachedir) ? $boarddir . '/cache' : $cachedir;
2803
	if (!file_exists($cachedir_temp))
2804
		@mkdir($cachedir_temp);
2805
2806
	// Make sure the cache temp dir is writable.
2807
	quickFileWritable($cachedir_temp);
2808
2809
	if (!is_writable($cachedir_temp))
2810
		print_error('Error: Unable to obtain write access to "cache".', true);
2811
2812
	// Make sure db_last_error.php is writable.
2813
	quickFileWritable($cachedir_temp . '/db_last_error.php');
2814
	if (!is_writable($cachedir_temp . '/db_last_error.php'))
2815
		print_error('Error: Unable to obtain write access to "db_last_error.php".');
2816
2817
	if (!file_exists($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php'))
2818
		print_error('Error: Unable to find language files!', true);
2819
	else
2820
	{
2821
		$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php')), 0, 4096);
2822
		preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
2823
2824
		if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
2825
			print_error('Error: Language files out of date.', true);
2826
		if (!file_exists($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php'))
2827
			print_error('Error: Install language is missing for selected language.', true);
2828
2829
		// Otherwise include it!
2830
		require_once($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php');
2831
	}
2832
2833
	// Do we need to add this setting?
2834
	$need_settings_update = empty($modSettings['custom_avatar_dir']);
2835
2836
	$custom_av_dir = !empty($modSettings['custom_avatar_dir']) ? $modSettings['custom_avatar_dir'] : $boarddir . '/custom_avatar';
2837
	$custom_av_url = !empty($modSettings['custom_avatar_url']) ? $modSettings['custom_avatar_url'] : $boardurl . '/custom_avatar';
2838
2839
	// This little fellow has to cooperate...
2840
	quickFileWritable($custom_av_dir);
2841
2842
	// Are we good now?
2843
	if (!is_writable($custom_av_dir))
2844
		print_error(sprintf($txt['error_dir_not_writable'], $custom_av_dir));
2845
	elseif ($need_settings_update)
2846
	{
2847
		if (!function_exists('cache_put_data'))
2848
			require_once($sourcedir . '/Load.php');
2849
2850
		updateSettings(array('custom_avatar_dir' => $custom_av_dir));
2851
		updateSettings(array('custom_avatar_url' => $custom_av_url));
2852
	}
2853
2854
	// Make sure we skip the HTML for login.
2855
	$_POST['upcont'] = true;
2856
	$upcontext['current_step'] = 1;
2857
}
2858
2859
/**
2860
 * Handles converting your database to UTF-8
2861
 */
2862
function ConvertUtf8()
2863
{
2864
	global $upcontext, $db_character_set, $sourcedir, $smcFunc, $modSettings, $language;
2865
	global $db_prefix, $db_type, $command_line, $support_js, $txt;
2866
2867
	// Done it already?
2868
	if (!empty($_POST['utf8_done']))
2869
	{
2870
		if ($command_line)
2871
			return DeleteUpgrade();
2872
		else
2873
			return true;
2874
	}
2875
	// First make sure they aren't already on UTF-8 before we go anywhere...
2876
	if ($db_type == 'postgresql' || ($db_character_set === 'utf8' && !empty($modSettings['global_character_set']) && $modSettings['global_character_set'] === 'UTF-8'))
2877
	{
2878
		$smcFunc['db_insert']('replace',
2879
			'{db_prefix}settings',
2880
			array('variable' => 'string', 'value' => 'string'),
2881
			array(array('global_character_set', 'UTF-8')),
2882
			array('variable')
2883
		);
2884
2885
		if ($command_line)
2886
			return DeleteUpgrade();
2887
		else
2888
			return true;
2889
	}
2890
	else
2891
	{
2892
		$upcontext['page_title'] = $txt['converting_utf8'];
2893
		$upcontext['sub_template'] = isset($_GET['xml']) ? 'convert_xml' : 'convert_utf8';
2894
2895
		// The character sets used in SMF's language files with their db equivalent.
2896
		$charsets = array(
2897
			// Armenian
2898
			'armscii8' => 'armscii8',
2899
			// Chinese-traditional.
2900
			'big5' => 'big5',
2901
			// Chinese-simplified.
2902
			'gbk' => 'gbk',
2903
			// West European.
2904
			'ISO-8859-1' => 'latin1',
2905
			// Romanian.
2906
			'ISO-8859-2' => 'latin2',
2907
			// Turkish.
2908
			'ISO-8859-9' => 'latin5',
2909
			// Latvian
2910
			'ISO-8859-13' => 'latin7',
2911
			// West European with Euro sign.
2912
			'ISO-8859-15' => 'latin9',
2913
			// Thai.
2914
			'tis-620' => 'tis620',
2915
			// Persian, Chinese, etc.
2916
			'UTF-8' => 'utf8',
2917
			// Russian.
2918
			'windows-1251' => 'cp1251',
2919
			// Greek.
2920
			'windows-1253' => 'utf8',
2921
			// Hebrew.
2922
			'windows-1255' => 'utf8',
2923
			// Arabic.
2924
			'windows-1256' => 'cp1256',
2925
		);
2926
2927
		// Get a list of character sets supported by your MySQL server.
2928
		$request = $smcFunc['db_query']('', '
2929
			SHOW CHARACTER SET',
2930
			array(
2931
			)
2932
		);
2933
		$db_charsets = array();
2934
		while ($row = $smcFunc['db_fetch_assoc']($request))
2935
			$db_charsets[] = $row['Charset'];
2936
2937
		$smcFunc['db_free_result']($request);
2938
2939
		// Character sets supported by both MySQL and SMF's language files.
2940
		$charsets = array_intersect($charsets, $db_charsets);
2941
2942
		// Use the messages.body column as indicator for the database charset.
2943
		$request = $smcFunc['db_query']('', '
2944
			SHOW FULL COLUMNS
2945
			FROM {db_prefix}messages
2946
			LIKE {string:body_like}',
2947
			array(
2948
				'body_like' => 'body',
2949
			)
2950
		);
2951
		$column_info = $smcFunc['db_fetch_assoc']($request);
2952
		$smcFunc['db_free_result']($request);
2953
2954
		// A collation looks like latin1_swedish. We only need the character set.
2955
		list($upcontext['database_charset']) = explode('_', $column_info['Collation']);
2956
		$upcontext['database_charset'] = in_array($upcontext['database_charset'], $charsets) ? array_search($upcontext['database_charset'], $charsets) : $upcontext['database_charset'];
2957
2958
		// Detect whether a fulltext index is set.
2959
		$request = $smcFunc['db_query']('', '
2960
			SHOW INDEX
2961
			FROM {db_prefix}messages',
2962
			array(
2963
			)
2964
		);
2965
2966
		$upcontext['dropping_index'] = false;
2967
2968
		// If there's a fulltext index, we need to drop it first...
2969
		if ($request !== false || $smcFunc['db_num_rows']($request) != 0)
2970
		{
2971
			while ($row = $smcFunc['db_fetch_assoc']($request))
2972
				if ($row['Column_name'] == 'body' && (isset($row['Index_type']) && $row['Index_type'] == 'FULLTEXT' || isset($row['Comment']) && $row['Comment'] == 'FULLTEXT'))
2973
					$upcontext['fulltext_index'][] = $row['Key_name'];
2974
			$smcFunc['db_free_result']($request);
2975
2976
			if (isset($upcontext['fulltext_index']))
2977
				$upcontext['fulltext_index'] = array_unique($upcontext['fulltext_index']);
2978
		}
2979
2980
		// Drop it and make a note...
2981
		if (!empty($upcontext['fulltext_index']))
2982
		{
2983
			$upcontext['dropping_index'] = true;
2984
2985
			$smcFunc['db_query']('', '
2986
				ALTER TABLE {db_prefix}messages
2987
				DROP INDEX ' . implode(',
2988
				DROP INDEX ', $upcontext['fulltext_index']),
2989
				array(
2990
					'db_error_skip' => true,
2991
				)
2992
			);
2993
2994
			// Update the settings table
2995
			$smcFunc['db_insert']('replace',
2996
				'{db_prefix}settings',
2997
				array('variable' => 'string', 'value' => 'string'),
2998
				array('db_search_index', ''),
2999
				array('variable')
3000
			);
3001
		}
3002
3003
		// Figure out what charset we should be converting from...
3004
		$lang_charsets = array(
3005
			'arabic' => 'windows-1256',
3006
			'armenian_east' => 'armscii-8',
3007
			'armenian_west' => 'armscii-8',
3008
			'azerbaijani_latin' => 'ISO-8859-9',
3009
			'bangla' => 'UTF-8',
3010
			'belarusian' => 'ISO-8859-5',
3011
			'bulgarian' => 'windows-1251',
3012
			'cambodian' => 'UTF-8',
3013
			'chinese_simplified' => 'gbk',
3014
			'chinese_traditional' => 'big5',
3015
			'croation' => 'ISO-8859-2',
3016
			'czech' => 'ISO-8859-2',
3017
			'czech_informal' => 'ISO-8859-2',
3018
			'english_pirate' => 'UTF-8',
3019
			'esperanto' => 'ISO-8859-3',
3020
			'estonian' => 'ISO-8859-15',
3021
			'filipino_tagalog' => 'UTF-8',
3022
			'filipino_vasayan' => 'UTF-8',
3023
			'georgian' => 'UTF-8',
3024
			'greek' => 'ISO-8859-3',
3025
			'hebrew' => 'windows-1255',
3026
			'hungarian' => 'ISO-8859-2',
3027
			'irish' => 'UTF-8',
3028
			'japanese' => 'UTF-8',
3029
			'khmer' => 'UTF-8',
3030
			'korean' => 'UTF-8',
3031
			'kurdish_kurmanji' => 'ISO-8859-9',
3032
			'kurdish_sorani' => 'windows-1256',
3033
			'lao' => 'tis-620',
3034
			'latvian' => 'ISO-8859-13',
3035
			'lithuanian' => 'ISO-8859-4',
3036
			'macedonian' => 'UTF-8',
3037
			'malayalam' => 'UTF-8',
3038
			'mongolian' => 'UTF-8',
3039
			'nepali' => 'UTF-8',
3040
			'persian' => 'UTF-8',
3041
			'polish' => 'ISO-8859-2',
3042
			'romanian' => 'ISO-8859-2',
3043
			'russian' => 'windows-1252',
3044
			'sakha' => 'UTF-8',
3045
			'serbian_cyrillic' => 'ISO-8859-5',
3046
			'serbian_latin' => 'ISO-8859-2',
3047
			'sinhala' => 'UTF-8',
3048
			'slovak' => 'ISO-8859-2',
3049
			'slovenian' => 'ISO-8859-2',
3050
			'telugu' => 'UTF-8',
3051
			'thai' => 'tis-620',
3052
			'turkish' => 'ISO-8859-9',
3053
			'turkmen' => 'ISO-8859-9',
3054
			'ukranian' => 'windows-1251',
3055
			'urdu' => 'UTF-8',
3056
			'uzbek_cyrillic' => 'ISO-8859-5',
3057
			'uzbek_latin' => 'ISO-8859-5',
3058
			'vietnamese' => 'UTF-8',
3059
			'yoruba' => 'UTF-8'
3060
		);
3061
3062
		// Default to ISO-8859-1 unless we detected another supported charset
3063
		$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';
3064
3065
		$upcontext['charset_list'] = array_keys($charsets);
3066
3067
		// Translation table for the character sets not native for MySQL.
3068
		$translation_tables = array(
3069
			'windows-1255' => array(
3070
				'0x81' => '\'\'',		'0x8A' => '\'\'',		'0x8C' => '\'\'',
3071
				'0x8D' => '\'\'',		'0x8E' => '\'\'',		'0x8F' => '\'\'',
3072
				'0x90' => '\'\'',		'0x9A' => '\'\'',		'0x9C' => '\'\'',
3073
				'0x9D' => '\'\'',		'0x9E' => '\'\'',		'0x9F' => '\'\'',
3074
				'0xCA' => '\'\'',		'0xD9' => '\'\'',		'0xDA' => '\'\'',
3075
				'0xDB' => '\'\'',		'0xDC' => '\'\'',		'0xDD' => '\'\'',
3076
				'0xDE' => '\'\'',		'0xDF' => '\'\'',		'0xFB' => '0xD792',
3077
				'0xFC' => '0xE282AC',		'0xFF' => '0xD6B2',		'0xC2' => '0xFF',
3078
				'0x80' => '0xFC',		'0xE2' => '0xFB',		'0xA0' => '0xC2A0',
3079
				'0xA1' => '0xC2A1',		'0xA2' => '0xC2A2',		'0xA3' => '0xC2A3',
3080
				'0xA5' => '0xC2A5',		'0xA6' => '0xC2A6',		'0xA7' => '0xC2A7',
3081
				'0xA8' => '0xC2A8',		'0xA9' => '0xC2A9',		'0xAB' => '0xC2AB',
3082
				'0xAC' => '0xC2AC',		'0xAD' => '0xC2AD',		'0xAE' => '0xC2AE',
3083
				'0xAF' => '0xC2AF',		'0xB0' => '0xC2B0',		'0xB1' => '0xC2B1',
3084
				'0xB2' => '0xC2B2',		'0xB3' => '0xC2B3',		'0xB4' => '0xC2B4',
3085
				'0xB5' => '0xC2B5',		'0xB6' => '0xC2B6',		'0xB7' => '0xC2B7',
3086
				'0xB8' => '0xC2B8',		'0xB9' => '0xC2B9',		'0xBB' => '0xC2BB',
3087
				'0xBC' => '0xC2BC',		'0xBD' => '0xC2BD',		'0xBE' => '0xC2BE',
3088
				'0xBF' => '0xC2BF',		'0xD7' => '0xD7B3',		'0xD1' => '0xD781',
3089
				'0xD4' => '0xD7B0',		'0xD5' => '0xD7B1',		'0xD6' => '0xD7B2',
3090
				'0xE0' => '0xD790',		'0xEA' => '0xD79A',		'0xEC' => '0xD79C',
3091
				'0xED' => '0xD79D',		'0xEE' => '0xD79E',		'0xEF' => '0xD79F',
3092
				'0xF0' => '0xD7A0',		'0xF1' => '0xD7A1',		'0xF2' => '0xD7A2',
3093
				'0xF3' => '0xD7A3',		'0xF5' => '0xD7A5',		'0xF6' => '0xD7A6',
3094
				'0xF7' => '0xD7A7',		'0xF8' => '0xD7A8',		'0xF9' => '0xD7A9',
3095
				'0x82' => '0xE2809A',	'0x84' => '0xE2809E',	'0x85' => '0xE280A6',
3096
				'0x86' => '0xE280A0',	'0x87' => '0xE280A1',	'0x89' => '0xE280B0',
3097
				'0x8B' => '0xE280B9',	'0x93' => '0xE2809C',	'0x94' => '0xE2809D',
3098
				'0x95' => '0xE280A2',	'0x97' => '0xE28094',	'0x99' => '0xE284A2',
3099
				'0xC0' => '0xD6B0',		'0xC1' => '0xD6B1',		'0xC3' => '0xD6B3',
3100
				'0xC4' => '0xD6B4',		'0xC5' => '0xD6B5',		'0xC6' => '0xD6B6',
3101
				'0xC7' => '0xD6B7',		'0xC8' => '0xD6B8',		'0xC9' => '0xD6B9',
3102
				'0xCB' => '0xD6BB',		'0xCC' => '0xD6BC',		'0xCD' => '0xD6BD',
3103
				'0xCE' => '0xD6BE',		'0xCF' => '0xD6BF',		'0xD0' => '0xD780',
3104
				'0xD2' => '0xD782',		'0xE3' => '0xD793',		'0xE4' => '0xD794',
3105
				'0xE5' => '0xD795',		'0xE7' => '0xD797',		'0xE9' => '0xD799',
3106
				'0xFD' => '0xE2808E',	'0xFE' => '0xE2808F',	'0x92' => '0xE28099',
3107
				'0x83' => '0xC692',		'0xD3' => '0xD783',		'0x88' => '0xCB86',
3108
				'0x98' => '0xCB9C',		'0x91' => '0xE28098',	'0x96' => '0xE28093',
3109
				'0xBA' => '0xC3B7',		'0x9B' => '0xE280BA',	'0xAA' => '0xC397',
3110
				'0xA4' => '0xE282AA',	'0xE1' => '0xD791',		'0xE6' => '0xD796',
3111
				'0xE8' => '0xD798',		'0xEB' => '0xD79B',		'0xF4' => '0xD7A4',
3112
				'0xFA' => '0xD7AA',
3113
			),
3114
			'windows-1253' => array(
3115
				'0x81' => '\'\'',			'0x88' => '\'\'',			'0x8A' => '\'\'',
3116
				'0x8C' => '\'\'',			'0x8D' => '\'\'',			'0x8E' => '\'\'',
3117
				'0x8F' => '\'\'',			'0x90' => '\'\'',			'0x98' => '\'\'',
3118
				'0x9A' => '\'\'',			'0x9C' => '\'\'',			'0x9D' => '\'\'',
3119
				'0x9E' => '\'\'',			'0x9F' => '\'\'',			'0xAA' => '\'\'',
3120
				'0xD2' => '0xE282AC',			'0xFF' => '0xCE92',			'0xCE' => '0xCE9E',
3121
				'0xB8' => '0xCE88',		'0xBA' => '0xCE8A',		'0xBC' => '0xCE8C',
3122
				'0xBE' => '0xCE8E',		'0xBF' => '0xCE8F',		'0xC0' => '0xCE90',
3123
				'0xC8' => '0xCE98',		'0xCA' => '0xCE9A',		'0xCC' => '0xCE9C',
3124
				'0xCD' => '0xCE9D',		'0xCF' => '0xCE9F',		'0xDA' => '0xCEAA',
3125
				'0xE8' => '0xCEB8',		'0xEA' => '0xCEBA',		'0xEC' => '0xCEBC',
3126
				'0xEE' => '0xCEBE',		'0xEF' => '0xCEBF',		'0xC2' => '0xFF',
3127
				'0xBD' => '0xC2BD',		'0xED' => '0xCEBD',		'0xB2' => '0xC2B2',
3128
				'0xA0' => '0xC2A0',		'0xA3' => '0xC2A3',		'0xA4' => '0xC2A4',
3129
				'0xA5' => '0xC2A5',		'0xA6' => '0xC2A6',		'0xA7' => '0xC2A7',
3130
				'0xA8' => '0xC2A8',		'0xA9' => '0xC2A9',		'0xAB' => '0xC2AB',
3131
				'0xAC' => '0xC2AC',		'0xAD' => '0xC2AD',		'0xAE' => '0xC2AE',
3132
				'0xB0' => '0xC2B0',		'0xB1' => '0xC2B1',		'0xB3' => '0xC2B3',
3133
				'0xB5' => '0xC2B5',		'0xB6' => '0xC2B6',		'0xB7' => '0xC2B7',
3134
				'0xBB' => '0xC2BB',		'0xE2' => '0xCEB2',		'0x80' => '0xD2',
3135
				'0x82' => '0xE2809A',	'0x84' => '0xE2809E',	'0x85' => '0xE280A6',
3136
				'0x86' => '0xE280A0',	'0xA1' => '0xCE85',		'0xA2' => '0xCE86',
3137
				'0x87' => '0xE280A1',	'0x89' => '0xE280B0',	'0xB9' => '0xCE89',
3138
				'0x8B' => '0xE280B9',	'0x91' => '0xE28098',	'0x99' => '0xE284A2',
3139
				'0x92' => '0xE28099',	'0x93' => '0xE2809C',	'0x94' => '0xE2809D',
3140
				'0x95' => '0xE280A2',	'0x96' => '0xE28093',	'0x97' => '0xE28094',
3141
				'0x9B' => '0xE280BA',	'0xAF' => '0xE28095',	'0xB4' => '0xCE84',
3142
				'0xC1' => '0xCE91',		'0xC3' => '0xCE93',		'0xC4' => '0xCE94',
3143
				'0xC5' => '0xCE95',		'0xC6' => '0xCE96',		'0x83' => '0xC692',
3144
				'0xC7' => '0xCE97',		'0xC9' => '0xCE99',		'0xCB' => '0xCE9B',
3145
				'0xD0' => '0xCEA0',		'0xD1' => '0xCEA1',		'0xD3' => '0xCEA3',
3146
				'0xD4' => '0xCEA4',		'0xD5' => '0xCEA5',		'0xD6' => '0xCEA6',
3147
				'0xD7' => '0xCEA7',		'0xD8' => '0xCEA8',		'0xD9' => '0xCEA9',
3148
				'0xDB' => '0xCEAB',		'0xDC' => '0xCEAC',		'0xDD' => '0xCEAD',
3149
				'0xDE' => '0xCEAE',		'0xDF' => '0xCEAF',		'0xE0' => '0xCEB0',
3150
				'0xE1' => '0xCEB1',		'0xE3' => '0xCEB3',		'0xE4' => '0xCEB4',
3151
				'0xE5' => '0xCEB5',		'0xE6' => '0xCEB6',		'0xE7' => '0xCEB7',
3152
				'0xE9' => '0xCEB9',		'0xEB' => '0xCEBB',		'0xF0' => '0xCF80',
3153
				'0xF1' => '0xCF81',		'0xF2' => '0xCF82',		'0xF3' => '0xCF83',
3154
				'0xF4' => '0xCF84',		'0xF5' => '0xCF85',		'0xF6' => '0xCF86',
3155
				'0xF7' => '0xCF87',		'0xF8' => '0xCF88',		'0xF9' => '0xCF89',
3156
				'0xFA' => '0xCF8A',		'0xFB' => '0xCF8B',		'0xFC' => '0xCF8C',
3157
				'0xFD' => '0xCF8D',		'0xFE' => '0xCF8E',
3158
			),
3159
		);
3160
3161
		// Make some preparations.
3162
		if (isset($translation_tables[$upcontext['charset_detected']]))
3163
		{
3164
			$replace = '%field%';
3165
3166
			// Build a huge REPLACE statement...
3167
			foreach ($translation_tables[$upcontext['charset_detected']] as $from => $to)
3168
				$replace = 'REPLACE(' . $replace . ', ' . $from . ', ' . $to . ')';
3169
		}
3170
3171
		// Get a list of table names ahead of time... This makes it easier to set our substep and such
3172
		db_extend();
3173
		$queryTables = $smcFunc['db_list_tables'](false, $db_prefix . '%');
3174
3175
		$upcontext['table_count'] = count($queryTables);
3176
3177
		// What ones have we already done?
3178
		foreach ($queryTables as $id => $table)
3179
			if ($id < $_GET['substep'])
3180
				$upcontext['previous_tables'][] = $table;
3181
3182
		$upcontext['cur_table_num'] = $_GET['substep'];
3183
		$upcontext['cur_table_name'] = str_replace($db_prefix, '', $queryTables[$_GET['substep']]);
3184
		$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
3185
3186
		// Make sure we're ready & have painted the template before proceeding
3187
		if ($support_js && !isset($_GET['xml']))
3188
		{
3189
			$_GET['substep'] = 0;
3190
			return false;
3191
		}
3192
3193
		// We want to start at the first table.
3194
		for ($substep = $_GET['substep'], $n = count($queryTables); $substep < $n; $substep++)
3195
		{
3196
			$table = $queryTables[$substep];
3197
3198
			$getTableStatus = $smcFunc['db_query']('', '
3199
				SHOW TABLE STATUS
3200
				LIKE {string:table_name}',
3201
				array(
3202
					'table_name' => str_replace('_', '\_', $table)
3203
				)
3204
			);
3205
3206
			// Only one row so we can just fetch_assoc and free the result...
3207
			$table_info = $smcFunc['db_fetch_assoc']($getTableStatus);
3208
			$smcFunc['db_free_result']($getTableStatus);
3209
3210
			$upcontext['cur_table_name'] = str_replace($db_prefix, '', (isset($queryTables[$substep + 1]) ? $queryTables[$substep + 1] : $queryTables[$substep]));
3211
			$upcontext['cur_table_num'] = $substep + 1;
3212
			$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
3213
3214
			// Do we need to pause?
3215
			nextSubstep($substep);
3216
3217
			// Just to make sure it doesn't time out.
3218
			if (function_exists('apache_reset_timeout'))
3219
				@apache_reset_timeout();
3220
3221
			$table_charsets = array();
3222
3223
			// Loop through each column.
3224
			$queryColumns = $smcFunc['db_query']('', '
3225
				SHOW FULL COLUMNS
3226
				FROM ' . $table_info['Name'],
3227
				array(
3228
				)
3229
			);
3230
			while ($column_info = $smcFunc['db_fetch_assoc']($queryColumns))
3231
			{
3232
				// Only text'ish columns have a character set and need converting.
3233
				if (strpos($column_info['Type'], 'text') !== false || strpos($column_info['Type'], 'char') !== false)
3234
				{
3235
					$collation = empty($column_info['Collation']) || $column_info['Collation'] === 'NULL' ? $table_info['Collation'] : $column_info['Collation'];
3236
					if (!empty($collation) && $collation !== 'NULL')
3237
					{
3238
						list($charset) = explode('_', $collation);
3239
3240
						// Build structure of columns to operate on organized by charset; only operate on columns not yet utf8
3241
						if ($charset != 'utf8')
3242
						{
3243
							if (!isset($table_charsets[$charset]))
3244
								$table_charsets[$charset] = array();
3245
3246
							$table_charsets[$charset][] = $column_info;
3247
						}
3248
					}
3249
				}
3250
			}
3251
			$smcFunc['db_free_result']($queryColumns);
3252
3253
			// Only change the non-utf8 columns identified above
3254
			if (count($table_charsets) > 0)
3255
			{
3256
				$updates_blob = '';
3257
				$updates_text = '';
3258
				foreach ($table_charsets as $charset => $columns)
3259
				{
3260
					if ($charset !== $charsets[$upcontext['charset_detected']])
3261
					{
3262
						foreach ($columns as $column)
3263
						{
3264
							$updates_blob .= '
3265
								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'] . '\'') . ',';
3266
							$updates_text .= '
3267
								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'] . '\'') . ',';
3268
						}
3269
					}
3270
				}
3271
3272
				// Change the columns to binary form.
3273
				$smcFunc['db_query']('', '
3274
					ALTER TABLE {raw:table_name}{raw:updates_blob}',
3275
					array(
3276
						'table_name' => $table_info['Name'],
3277
						'updates_blob' => substr($updates_blob, 0, -1),
3278
					)
3279
				);
3280
3281
				// Convert the character set if MySQL has no native support for it.
3282
				if (isset($translation_tables[$upcontext['charset_detected']]))
3283
				{
3284
					$update = '';
3285
					foreach ($table_charsets as $charset => $columns)
3286
						foreach ($columns as $column)
3287
							$update .= '
3288
								' . $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...
3289
3290
					$smcFunc['db_query']('', '
3291
						UPDATE {raw:table_name}
3292
						SET {raw:updates}',
3293
						array(
3294
							'table_name' => $table_info['Name'],
3295
							'updates' => substr($update, 0, -1),
3296
						)
3297
					);
3298
				}
3299
3300
				// Change the columns back, but with the proper character set.
3301
				$smcFunc['db_query']('', '
3302
					ALTER TABLE {raw:table_name}{raw:updates_text}',
3303
					array(
3304
						'table_name' => $table_info['Name'],
3305
						'updates_text' => substr($updates_text, 0, -1),
3306
					)
3307
				);
3308
			}
3309
3310
			// Now do the actual conversion (if still needed).
3311
			if ($charsets[$upcontext['charset_detected']] !== 'utf8')
3312
			{
3313
				if ($command_line)
3314
					echo 'Converting table ' . $table_info['Name'] . ' to UTF-8...';
3315
3316
				$smcFunc['db_query']('', '
3317
					ALTER TABLE {raw:table_name}
3318
					CONVERT TO CHARACTER SET utf8',
3319
					array(
3320
						'table_name' => $table_info['Name'],
3321
					)
3322
				);
3323
3324
				if ($command_line)
3325
					echo " done.\n";
3326
			}
3327
			// If this is XML to keep it nice for the user do one table at a time anyway!
3328
			if (isset($_GET['xml']) && $upcontext['cur_table_num'] < $upcontext['table_count'])
3329
				return upgradeExit();
0 ignored issues
show
Bug introduced by
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...
3330
		}
3331
3332
		$prev_charset = empty($translation_tables[$upcontext['charset_detected']]) ? $charsets[$upcontext['charset_detected']] : $translation_tables[$upcontext['charset_detected']];
3333
3334
		$smcFunc['db_insert']('replace',
3335
			'{db_prefix}settings',
3336
			array('variable' => 'string', 'value' => 'string'),
3337
			array(array('global_character_set', 'UTF-8'), array('previousCharacterSet', $prev_charset)),
3338
			array('variable')
3339
		);
3340
3341
		// Store it in Settings.php too because it's needed before db connection.
3342
		// Hopefully this works...
3343
		require_once($sourcedir . '/Subs.php');
3344
		require_once($sourcedir . '/Subs-Admin.php');
3345
		updateSettingsFile(array('db_character_set' => 'utf8'));
3346
3347
		// The conversion might have messed up some serialized strings. Fix them!
3348
		$request = $smcFunc['db_query']('', '
3349
			SELECT id_action, extra
3350
			FROM {db_prefix}log_actions
3351
			WHERE action IN ({string:remove}, {string:delete})',
3352
			array(
3353
				'remove' => 'remove',
3354
				'delete' => 'delete',
3355
			)
3356
		);
3357
		while ($row = $smcFunc['db_fetch_assoc']($request))
3358
		{
3359
			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)
3360
				$smcFunc['db_query']('', '
3361
					UPDATE {db_prefix}log_actions
3362
					SET extra = {string:extra}
3363
					WHERE id_action = {int:current_action}',
3364
					array(
3365
						'current_action' => $row['id_action'],
3366
						'extra' => $matches[1] . strlen($matches[3]) . ':"' . $matches[3] . '"' . $matches[4],
3367
					)
3368
				);
3369
		}
3370
		$smcFunc['db_free_result']($request);
3371
3372
		if ($upcontext['dropping_index'] && $command_line)
3373
		{
3374
			echo "\n" . '', $txt['upgrade_fulltext_error'], '';
3375
			flush();
3376
		}
3377
	}
3378
3379
	// Make sure we move on!
3380
	if ($command_line)
3381
		return DeleteUpgrade();
3382
3383
	$_GET['substep'] = 0;
3384
	return false;
3385
}
3386
3387
/**
3388
 * Wrapper for unserialize that attempts to repair corrupted serialized data strings
3389
 *
3390
 * @param string $string Serialized data that may or may not have been corrupted
3391
 * @return string|bool The unserialized data, or false if the repair failed
3392
 */
3393
function upgrade_unserialize($string)
3394
{
3395
	if (!is_string($string))
0 ignored issues
show
introduced by
The condition is_string($string) is always true.
Loading history...
3396
	{
3397
		$data = false;
3398
	}
3399
	// Might be JSON already.
3400
	elseif (strpos($string, '{') === 0)
3401
	{
3402
		$data = @json_decode($string, true);
3403
3404
		if (is_null($data))
3405
			$data = false;
3406
	}
3407
	elseif (in_array(substr($string, 0, 2), array('b:', 'i:', 'd:', 's:', 'a:', 'N;')))
3408
	{
3409
		$data = @safe_unserialize($string);
3410
3411
		// The serialized data is broken.
3412
		if ($data === false)
3413
		{
3414
			// This bit fixes incorrect string lengths, which can happen if the character encoding was changed (e.g. conversion to UTF-8)
3415
			$new_string = preg_replace_callback(
0 ignored issues
show
Unused Code introduced by
The assignment to $new_string is dead and can be removed.
Loading history...
3416
				'~\bs:(\d+):"(.*?)";(?=$|[bidsaO]:|[{}}]|N;)~s',
3417
				function ($matches)
3418
				{
3419
					return 's:' . strlen($matches[2]) . ':"' . $matches[2] . '";';
3420
				},
3421
				$string
3422
			);
3423
3424
			// @todo Add more possible fixes here. For example, fix incorrect array lengths, try to handle truncated strings gracefully, etc.
3425
3426
			// Did it work?
3427
			$data = @safe_unserialize($string);
3428
		}
3429
	}
3430
	// Just a plain string, then.
3431
	else
3432
		$data = false;
3433
3434
	return $data;
3435
}
3436
3437
function serialize_to_json()
3438
{
3439
	global $command_line, $smcFunc, $modSettings, $sourcedir, $upcontext, $support_js, $txt;
3440
3441
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'serialize_json_xml' : 'serialize_json';
3442
	// First thing's first - did we already do this?
3443
	if (!empty($modSettings['json_done']))
3444
	{
3445
		if ($command_line)
3446
			return ConvertUtf8();
3447
		else
3448
			return true;
3449
	}
3450
3451
	// Needed when writing settings
3452
	if (!function_exists('cache_put_data'))
3453
		require_once($sourcedir . '/Load.php');
3454
3455
	// Done it already - js wise?
3456
	if (!empty($_POST['json_done']))
3457
	{
3458
		updateSettings(array('json_done' => true));
3459
		return true;
3460
	}
3461
3462
	// List of tables affected by this function
3463
	// name => array('key', col1[,col2|true[,col3]])
3464
	// If 3rd item in array is true, it indicates that col1 could be empty...
3465
	$tables = array(
3466
		'background_tasks' => array('id_task', 'task_data'),
3467
		'log_actions' => array('id_action', 'extra'),
3468
		'log_online' => array('session', 'url'),
3469
		'log_packages' => array('id_install', 'db_changes', 'failed_steps', 'credits'),
3470
		'log_spider_hits' => array('id_hit', 'url'),
3471
		'log_subscribed' => array('id_sublog', 'pending_details'),
3472
		'pm_rules' => array('id_rule', 'criteria', 'actions'),
3473
		'qanda' => array('id_question', 'answers'),
3474
		'subscriptions' => array('id_subscribe', 'cost'),
3475
		'user_alerts' => array('id_alert', 'extra', true),
3476
		'user_drafts' => array('id_draft', 'to_list', true),
3477
		// These last two are a bit different - we'll handle those separately
3478
		'settings' => array(),
3479
		'themes' => array()
3480
	);
3481
3482
	// Set up some context stuff...
3483
	// Because we're not using numeric indices, we need this to figure out the current table name...
3484
	$keys = array_keys($tables);
3485
3486
	$upcontext['page_title'] = $txt['converting_json'];
3487
	$upcontext['table_count'] = count($keys);
3488
	$upcontext['cur_table_num'] = $_GET['substep'];
3489
	$upcontext['cur_table_name'] = isset($keys[$_GET['substep']]) ? $keys[$_GET['substep']] : $keys[0];
3490
	$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
3491
3492
	foreach ($keys as $id => $table)
3493
		if ($id < $_GET['substep'])
3494
			$upcontext['previous_tables'][] = $table;
3495
3496
	if ($command_line)
3497
		echo 'Converting data from serialize() to json_encode().';
3498
3499
	if (!$support_js || isset($_GET['xml']))
3500
	{
3501
		// Fix the data in each table
3502
		for ($substep = $_GET['substep']; $substep < $upcontext['table_count']; $substep++)
3503
		{
3504
			$upcontext['cur_table_name'] = isset($keys[$substep + 1]) ? $keys[$substep + 1] : $keys[$substep];
3505
			$upcontext['cur_table_num'] = $substep + 1;
3506
3507
			$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
3508
3509
			// Do we need to pause?
3510
			nextSubstep($substep);
3511
3512
			// Initialize a few things...
3513
			$where = '';
3514
			$vars = array();
3515
			$table = $keys[$substep];
3516
			$info = $tables[$table];
3517
3518
			// Now the fun - build our queries and all that fun stuff
3519
			if ($table == 'settings')
3520
			{
3521
				// Now a few settings...
3522
				$serialized_settings = array(
3523
					'attachment_basedirectories',
3524
					'attachmentUploadDir',
3525
					'cal_today_birthday',
3526
					'cal_today_event',
3527
					'cal_today_holiday',
3528
					'displayFields',
3529
					'last_attachments_directory',
3530
					'memberlist_cache',
3531
					'search_custom_index_config',
3532
					'spider_name_cache'
3533
				);
3534
3535
				// Loop through and fix these...
3536
				$new_settings = array();
3537
				if ($command_line)
3538
					echo "\n" . 'Fixing some settings...';
3539
3540
				foreach ($serialized_settings as $var)
3541
				{
3542
					if (isset($modSettings[$var]))
3543
					{
3544
						// Attempt to unserialize the setting
3545
						$temp = upgrade_unserialize($modSettings[$var]);
3546
3547
						if (!$temp && $command_line)
3548
							echo "\n - Failed to unserialize the '" . $var . "' setting. Skipping.";
3549
						elseif ($temp !== false)
3550
							$new_settings[$var] = json_encode($temp);
3551
					}
3552
				}
3553
3554
				// Update everything at once
3555
				updateSettings($new_settings, true);
3556
3557
				if ($command_line)
3558
					echo ' done.';
3559
			}
3560
			elseif ($table == 'themes')
3561
			{
3562
				// Finally, fix the admin prefs. Unfortunately this is stored per theme, but hopefully they only have one theme installed at this point...
3563
				$query = $smcFunc['db_query']('', '
3564
					SELECT id_member, id_theme, value FROM {db_prefix}themes
3565
					WHERE variable = {string:admin_prefs}',
3566
					array(
3567
						'admin_prefs' => 'admin_preferences'
3568
					)
3569
				);
3570
3571
				if ($smcFunc['db_num_rows']($query) != 0)
3572
				{
3573
					while ($row = $smcFunc['db_fetch_assoc']($query))
3574
					{
3575
						$temp = upgrade_unserialize($row['value']);
3576
3577
						if ($command_line)
3578
						{
3579
							if ($temp === false)
3580
								echo "\n" . 'Unserialize of admin_preferences for user ' . $row['id_member'] . ' failed. Skipping.';
3581
							else
3582
								echo "\n" . 'Fixing admin preferences...';
3583
						}
3584
3585
						if ($temp !== false)
3586
						{
3587
							$row['value'] = json_encode($temp);
3588
3589
							// Even though we have all values from the table, UPDATE is still faster than REPLACE
3590
							$smcFunc['db_query']('', '
3591
								UPDATE {db_prefix}themes
3592
								SET value = {string:prefs}
3593
								WHERE id_theme = {int:theme}
3594
									AND id_member = {int:member}
3595
									AND variable = {string:admin_prefs}',
3596
								array(
3597
									'prefs' => $row['value'],
3598
									'theme' => $row['id_theme'],
3599
									'member' => $row['id_member'],
3600
									'admin_prefs' => 'admin_preferences'
3601
								)
3602
							);
3603
3604
							if ($command_line)
3605
								echo ' done.';
3606
						}
3607
					}
3608
3609
					$smcFunc['db_free_result']($query);
3610
				}
3611
			}
3612
			else
3613
			{
3614
				// First item is always the key...
3615
				$key = $info[0];
3616
				unset($info[0]);
3617
3618
				// Now we know what columns we have and such...
3619
				if (count($info) == 2 && $info[2] === true)
3620
				{
3621
					$col_select = $info[1];
3622
					$where = ' WHERE ' . $info[1] . ' != {empty}';
3623
				}
3624
				else
3625
				{
3626
					$col_select = implode(', ', $info);
3627
				}
3628
3629
				$query = $smcFunc['db_query']('', '
3630
					SELECT ' . $key . ', ' . $col_select . '
3631
					FROM {db_prefix}' . $table . $where,
3632
					array()
3633
				);
3634
3635
				if ($smcFunc['db_num_rows']($query) != 0)
3636
				{
3637
					if ($command_line)
3638
					{
3639
						echo "\n" . ' +++ Fixing the "' . $table . '" table...';
3640
						flush();
3641
					}
3642
3643
					while ($row = $smcFunc['db_fetch_assoc']($query))
3644
					{
3645
						$update = '';
3646
3647
						// We already know what our key is...
3648
						foreach ($info as $col)
3649
						{
3650
							if ($col !== true && $row[$col] != '')
3651
							{
3652
								$temp = upgrade_unserialize($row[$col]);
3653
3654
								// Oh well...
3655
								if ($temp === false)
3656
								{
3657
									$temp = array();
3658
3659
									if ($command_line)
3660
										echo "\nFailed to unserialize " . $row[$col] . ". Setting to empty value.\n";
3661
								}
3662
3663
								$row[$col] = json_encode($temp);
3664
3665
								// Build our SET string and variables array
3666
								$update .= (empty($update) ? '' : ', ') . $col . ' = {string:' . $col . '}';
3667
								$vars[$col] = $row[$col];
3668
							}
3669
						}
3670
3671
						$vars[$key] = $row[$key];
3672
3673
						// In a few cases, we might have empty data, so don't try to update in those situations...
3674
						if (!empty($update))
3675
						{
3676
							$smcFunc['db_query']('', '
3677
								UPDATE {db_prefix}' . $table . '
3678
								SET ' . $update . '
3679
								WHERE ' . $key . ' = {' . ($key == 'session' ? 'string' : 'int') . ':' . $key . '}',
3680
								$vars
3681
							);
3682
						}
3683
					}
3684
3685
					if ($command_line)
3686
						echo ' done.';
3687
3688
					// Free up some memory...
3689
					$smcFunc['db_free_result']($query);
3690
				}
3691
			}
3692
			// If this is XML to keep it nice for the user do one table at a time anyway!
3693
			if (isset($_GET['xml']))
3694
				return upgradeExit();
0 ignored issues
show
Bug introduced by
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...
3695
		}
3696
3697
		if ($command_line)
3698
		{
3699
			echo "\n" . 'Successful.' . "\n";
3700
			flush();
3701
		}
3702
		$upcontext['step_progress'] = 100;
3703
3704
		// Last but not least, insert a dummy setting so we don't have to do this again in the future...
3705
		updateSettings(array('json_done' => true));
3706
3707
		$_GET['substep'] = 0;
3708
		// Make sure we move on!
3709
		if ($command_line)
3710
			return ConvertUtf8();
3711
3712
		return true;
3713
	}
3714
3715
	// If this fails we just move on to deleting the upgrade anyway...
3716
	$_GET['substep'] = 0;
3717
	return false;
3718
}
3719
3720
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3721
						Templates are below this point
3722
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
3723
3724
// This is what is displayed if there's any chmod to be done. If not it returns nothing...
3725
function template_chmod()
3726
{
3727
	global $upcontext, $txt, $settings;
3728
3729
	// Don't call me twice!
3730
	if (!empty($upcontext['chmod_called']))
3731
		return;
3732
3733
	$upcontext['chmod_called'] = true;
3734
3735
	// Nothing?
3736
	if (empty($upcontext['chmod']['files']) && empty($upcontext['chmod']['ftp_error']))
3737
		return;
3738
3739
	// Was it a problem with Windows?
3740
	if (!empty($upcontext['chmod']['ftp_error']) && $upcontext['chmod']['ftp_error'] == 'total_mess')
3741
	{
3742
		echo '
3743
		<div class="error">
3744
			<p>', $txt['upgrade_writable_files'], '</p>
3745
			<ul class="error_content">
3746
				<li>' . implode('</li>
3747
				<li>', $upcontext['chmod']['files']) . '</li>
3748
			</ul>
3749
		</div>';
3750
3751
		return false;
3752
	}
3753
3754
	echo '
3755
		<div class="panel">
3756
			<h2>', $txt['upgrade_ftp_login'], '</h2>
3757
			<h3>', $txt['upgrade_ftp_perms'], '</h3>
3758
			<script>
3759
				function warning_popup()
3760
				{
3761
					popup = window.open(\'\',\'popup\',\'height=150,width=400,scrollbars=yes\');
3762
					var content = popup.document;
3763
					content.write(\'<!DOCTYPE html>\n\');
3764
					content.write(\'<html', $txt['lang_rtl'] == true ? ' dir="rtl"' : '', '>\n\t<head>\n\t\t<meta name="robots" content="noindex">\n\t\t\');
3765
					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\');
3766
					content.write(\'<div class="windowbg description">\n\t\t\t<h4>', $txt['upgrade_ftp_files'], '</h4>\n\t\t\t\');
3767
					content.write(\'<p>', implode('<br>\n\t\t\t', $upcontext['chmod']['files']), '</p>\n\t\t\t\');';
3768
3769
	if (isset($upcontext['systemos']) && $upcontext['systemos'] == 'linux')
3770
		echo '
3771
					content.write(\'<hr>\n\t\t\t\');
3772
					content.write(\'<p>', $txt['upgrade_ftp_shell'], '</p>\n\t\t\t\');
3773
					content.write(\'<tt># chmod a+w ', implode(' ', $upcontext['chmod']['files']), '</tt>\n\t\t\t\');';
3774
3775
	echo '
3776
					content.write(\'<a href="javascript:self.close();">close</a>\n\t\t</div>\n\t</body>\n</html>\');
3777
					content.close();
3778
				}
3779
			</script>';
3780
3781
	if (!empty($upcontext['chmod']['ftp_error']))
3782
		echo '
3783
			<div class="error">
3784
				<p>', $txt['upgrade_ftp_error'], '<p>
3785
				<code>', $upcontext['chmod']['ftp_error'], '</code>
3786
			</div>';
3787
3788
	if (empty($upcontext['chmod_in_form']))
3789
		echo '
3790
			<form action="', $upcontext['form_url'], '" method="post">';
3791
3792
	echo '
3793
				<dl class="settings">
3794
					<dt>
3795
						<label for="ftp_server">', $txt['ftp_server'], ':</label>
3796
					</dt>
3797
					<dd>
3798
						<div class="floatright">
3799
							<label for="ftp_port" class="textbox"><strong>', $txt['ftp_port'], ':</strong></label>
3800
							<input type="text" size="3" name="ftp_port" id="ftp_port" value="', isset($upcontext['chmod']['port']) ? $upcontext['chmod']['port'] : '21', '">
3801
						</div>
3802
						<input type="text" size="30" name="ftp_server" id="ftp_server" value="', isset($upcontext['chmod']['server']) ? $upcontext['chmod']['server'] : 'localhost', '">
3803
						<div class="smalltext">', $txt['ftp_server_info'], '</div>
3804
					</dd>
3805
					<dt>
3806
						<label for="ftp_username">', $txt['ftp_username'], ':</label>
3807
					</dt>
3808
					<dd>
3809
						<input type="text" size="30" name="ftp_username" id="ftp_username" value="', isset($upcontext['chmod']['username']) ? $upcontext['chmod']['username'] : '', '">
3810
						<div class="smalltext">', $txt['ftp_username_info'], '</div>
3811
					</dd>
3812
					<dt>
3813
						<label for="ftp_password">', $txt['ftp_password'], ':</label>
3814
					</dt>
3815
					<dd>
3816
						<input type="password" size="30" name="ftp_password" id="ftp_password">
3817
						<div class="smalltext">', $txt['ftp_password_info'], '</div>
3818
					</dd>
3819
					<dt>
3820
						<label for="ftp_path">', $txt['ftp_path'], ':</label>
3821
					</dt>
3822
					<dd>
3823
						<input type="text" size="30" name="ftp_path" id="ftp_path" value="', isset($upcontext['chmod']['path']) ? $upcontext['chmod']['path'] : '', '">
3824
						<div class="smalltext">', !empty($upcontext['chmod']['path']) ? $txt['ftp_path_found_info'] : $txt['ftp_path_info'], '</div>
3825
					</dd>
3826
				</dl>
3827
3828
				<div class="righttext buttons">
3829
					<input type="submit" value="', $txt['ftp_connect'], '" class="button">
3830
				</div>';
3831
3832
	if (empty($upcontext['chmod_in_form']))
3833
		echo '
3834
			</form>';
3835
3836
	echo '
3837
		</div><!-- .panel -->';
3838
}
3839
3840
function template_upgrade_above()
3841
{
3842
	global $modSettings, $txt, $settings, $upcontext, $upgradeurl;
3843
3844
	echo '<!DOCTYPE html>
3845
<html', $txt['lang_rtl'] == true ? ' dir="rtl"' : '', '>
3846
<head>
3847
	<meta charset="', isset($txt['lang_character_set']) ? $txt['lang_character_set'] : 'UTF-8', '">
3848
	<meta name="robots" content="noindex">
3849
	<title>', $txt['upgrade_upgrade_utility'], '</title>
3850
	<link rel="stylesheet" href="', $settings['default_theme_url'], '/css/index.css">
3851
	<link rel="stylesheet" href="', $settings['default_theme_url'], '/css/install.css">
3852
	', $txt['lang_rtl'] == true ? '<link rel="stylesheet" href="' . $settings['default_theme_url'] . '/css/rtl.css">' : '', '
3853
	<script src="https://ajax.googleapis.com/ajax/libs/jquery/', JQUERY_VERSION, '/jquery.min.js"></script>
3854
	<script src="', $settings['default_theme_url'], '/scripts/script.js"></script>
3855
	<script>
3856
		var smf_scripturl = \'', $upgradeurl, '\';
3857
		var smf_charset = \'', (empty($modSettings['global_character_set']) ? (empty($txt['lang_character_set']) ? 'UTF-8' : $txt['lang_character_set']) : $modSettings['global_character_set']), '\';
3858
		var startPercent = ', $upcontext['overall_percent'], ';
3859
		var allow_xhjr_credentials = false;
3860
3861
		// This function dynamically updates the step progress bar - and overall one as required.
3862
		function updateStepProgress(current, max, overall_weight)
3863
		{
3864
			// What out the actual percent.
3865
			var width = parseInt((current / max) * 100);
3866
			if (document.getElementById(\'step_progress\'))
3867
			{
3868
				document.getElementById(\'step_progress\').style.width = width + "%";
3869
				setInnerHTML(document.getElementById(\'step_text\'), width + "%");
3870
			}
3871
			if (overall_weight && document.getElementById(\'overall_progress\'))
3872
			{
3873
				overall_width = parseInt(startPercent + width * (overall_weight / 100));
3874
				document.getElementById(\'overall_progress\').style.width = overall_width + "%";
3875
				setInnerHTML(document.getElementById(\'overall_text\'), overall_width + "%");
3876
			}
3877
		}
3878
	</script>
3879
</head>
3880
<body>
3881
	<div id="footerfix">
3882
	<div id="header">
3883
		<h1 class="forumtitle">', $txt['upgrade_upgrade_utility'], '</h1>
3884
		<img id="smflogo" src="', $settings['default_theme_url'], '/images/smflogo.svg" alt="Simple Machines Forum" title="Simple Machines Forum">
3885
	</div>
3886
	<div id="wrapper">
3887
		<div id="content_section">
3888
			<div id="main_content_section">
3889
				<div id="main_steps">
3890
					<h2>', $txt['upgrade_progress'], '</h2>
3891
					<ul class="steps_list">';
3892
3893
	foreach ($upcontext['steps'] as $num => $step)
3894
		echo '
3895
						<li', $num == $upcontext['current_step'] ? ' class="stepcurrent"' : '', '>
3896
							', $txt['upgrade_step'], ' ', $step[0], ': ', $txt[$step[1]], '
3897
						</li>';
3898
3899
	echo '
3900
					</ul>
3901
				</div><!-- #main_steps -->
3902
3903
				<div id="install_progress">
3904
					<div id="progress_bar" class="progress_bar progress_green">
3905
						<h3>', $txt['upgrade_overall_progress'], '</h3>
3906
						<div id="overall_progress" class="bar" style="width: ', $upcontext['overall_percent'], '%;"></div>
3907
						<span id="overall_text">', $upcontext['overall_percent'], '%</span>
3908
					</div>';
3909
3910
	if (isset($upcontext['step_progress']))
3911
		echo '
3912
					<div id="progress_bar_step" class="progress_bar progress_yellow">
3913
						<h3>', $txt['upgrade_step_progress'], '</h3>
3914
						<div id="step_progress" class="bar" style="width: ', $upcontext['step_progress'], '%;"></div>
3915
						<span id="step_text">', $upcontext['step_progress'], '%</span>
3916
					</div>';
3917
3918
	echo '
3919
					<div id="substep_bar_div" class="progress_bar ', isset($upcontext['substep_progress']) ? '' : 'hidden', '">
3920
						<h3 id="substep_name">', isset($upcontext['substep_progress_name']) ? trim(strtr($upcontext['substep_progress_name'], array('.' => ''))) : '', '</h3>
3921
						<div id="substep_progress" class="bar" style="width: ', isset($upcontext['substep_progress']) ? $upcontext['substep_progress'] : 0, '%;"></div>
3922
						<span id="substep_text">', isset($upcontext['substep_progress']) ? $upcontext['substep_progress'] : 0, '%</span>
3923
					</div>';
3924
3925
	// How long have we been running this?
3926
	$elapsed = time() - $upcontext['started'];
3927
	$mins = (int) ($elapsed / 60);
3928
	$seconds = $elapsed - $mins * 60;
3929
	echo '
3930
					<div class="smalltext time_elapsed">
3931
						', $txt['upgrade_time_elapsed'], ':
3932
						<span id="mins_elapsed">', $mins, '</span> ', $txt['upgrade_time_mins'], ', <span id="secs_elapsed">', $seconds, '</span> ', $txt['upgrade_time_secs'], '.
3933
					</div>';
3934
	echo '
3935
				</div><!-- #install_progress -->
3936
				<div id="main_screen" class="clear">
3937
					<h2>', $upcontext['page_title'], '</h2>
3938
					<div class="panel">';
3939
}
3940
3941
function template_upgrade_below()
3942
{
3943
	global $upcontext, $txt;
3944
3945
	if (!empty($upcontext['pause']))
3946
		echo '
3947
							<em>', $txt['upgrade_incomplete'], '.</em><br>
3948
3949
							<h2 style="margin-top: 2ex;">', $txt['upgrade_not_quite_done'], '</h2>
3950
							<h3>
3951
								', $txt['upgrade_paused_overload'], '
3952
							</h3>';
3953
3954
	if (!empty($upcontext['custom_warning']))
3955
		echo '
3956
							<div class="errorbox">
3957
								<h3>', $txt['upgrade_note'], '</h3>
3958
								', $upcontext['custom_warning'], '
3959
							</div>';
3960
3961
	echo '
3962
							<div class="righttext buttons">';
3963
3964
	if (!empty($upcontext['continue']))
3965
		echo '
3966
								<input type="submit" id="contbutt" name="contbutt" value="', $txt['upgrade_continue'], '"', $upcontext['continue'] == 2 ? ' disabled' : '', ' class="button">';
3967
	if (!empty($upcontext['skip']))
3968
		echo '
3969
								<input type="submit" id="skip" name="skip" value="', $txt['upgrade_skip'], '" onclick="dontSubmit = true; document.getElementById(\'contbutt\').disabled = \'disabled\'; return true;" class="button">';
3970
3971
	echo '
3972
							</div>
3973
						</form>
3974
					</div><!-- .panel -->
3975
				</div><!-- #main_screen -->
3976
			</div><!-- #main_content_section -->
3977
		</div><!-- #content_section -->
3978
	</div><!-- #wrapper -->
3979
	</div><!-- #footerfix -->
3980
	<div id="footer">
3981
		<ul>
3982
			<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>
3983
		</ul>
3984
	</div>';
3985
3986
	// Are we on a pause?
3987
	if (!empty($upcontext['pause']))
3988
	{
3989
		echo '
3990
	<script>
3991
		window.onload = doAutoSubmit;
3992
		var countdown = 3;
3993
		var dontSubmit = false;
3994
3995
		function doAutoSubmit()
3996
		{
3997
			if (countdown == 0 && !dontSubmit)
3998
				document.upform.submit();
3999
			else if (countdown == -1)
4000
				return;
4001
4002
			document.getElementById(\'contbutt\').value = "', $txt['upgrade_continue'], ' (" + countdown + ")";
4003
			countdown--;
4004
4005
			setTimeout("doAutoSubmit();", 1000);
4006
		}
4007
	</script>';
4008
	}
4009
4010
	echo '
4011
</body>
4012
</html>';
4013
}
4014
4015
function template_xml_above()
4016
{
4017
	global $upcontext;
4018
4019
	echo '<', '?xml version="1.0" encoding="UTF-8"?', '>
4020
	<smf>';
4021
4022
	if (!empty($upcontext['get_data']))
4023
		foreach ($upcontext['get_data'] as $k => $v)
4024
			echo '
4025
		<get key="', $k, '">', $v, '</get>';
4026
}
4027
4028
function template_xml_below()
4029
{
4030
	echo '
4031
	</smf>';
4032
}
4033
4034
function template_error_message()
4035
{
4036
	global $upcontext, $txt;
4037
4038
	echo '
4039
	<div class="error">
4040
		', $upcontext['error_msg'], '
4041
		<br>
4042
		<a href="', $_SERVER['PHP_SELF'], '">', $txt['upgrade_respondtime_clickhere'], '</a>
4043
	</div>';
4044
}
4045
4046
function template_welcome_message()
4047
{
4048
	global $upcontext, $disable_security, $settings, $txt;
4049
4050
	echo '
4051
				<script src="https://www.simplemachines.org/smf/current-version.js?version=' . SMF_VERSION . '"></script>
4052
4053
				<h3>', sprintf($txt['upgrade_ready_proceed'], SMF_VERSION), '</h3>
4054
				<form action="', $upcontext['form_url'], '" method="post" name="upform" id="upform">
4055
					<input type="hidden" name="', $upcontext['login_token_var'], '" value="', $upcontext['login_token'], '">
4056
4057
					<div id="version_warning" class="noticebox hidden">
4058
						<h3>', $txt['upgrade_warning'], '</h3>
4059
						', sprintf($txt['upgrade_warning_out_of_date'], SMF_VERSION, 'https://www.simplemachines.org'), '
4060
					</div>';
4061
4062
	$upcontext['chmod_in_form'] = true;
4063
	template_chmod();
4064
4065
	// For large, pre 1.1 RC2 forums give them a warning about the possible impact of this upgrade!
4066
	if ($upcontext['is_large_forum'])
4067
		echo '
4068
					<div class="errorbox">
4069
						<h3>', $txt['upgrade_warning'], '</h3>
4070
						', $txt['upgrade_warning_lots_data'], '
4071
					</div>';
4072
4073
	// A warning message?
4074
	if (!empty($upcontext['warning']))
4075
		echo '
4076
					<div class="errorbox">
4077
						<h3>', $txt['upgrade_warning'], '</h3>
4078
						', $upcontext['warning'], '
4079
					</div>';
4080
4081
	// Paths are incorrect?
4082
	echo '
4083
					<div class="errorbox', (file_exists($settings['default_theme_dir'] . '/scripts/script.js') ? ' hidden' : ''), '" id="js_script_missing_error">
4084
						<h3>', $txt['upgrade_critical_error'], '</h3>
4085
						', sprintf($txt['upgrade_error_script_js'], 'https://download.simplemachines.org/?tools'), '
4086
					</div>';
4087
4088
	// Is there someone already doing this?
4089
	if (!empty($upcontext['user']['id']) && (time() - $upcontext['started'] < 72600 || time() - $upcontext['updated'] < 3600))
4090
	{
4091
		$ago = time() - $upcontext['started'];
4092
		$ago_hours = floor($ago / 3600);
4093
		$ago_minutes = intval(($ago / 60) % 60);
4094
		$ago_seconds = intval($ago % 60);
4095
		$agoTxt = $ago < 60 ? 'upgrade_time_s' : ($ago < 3600 ? 'upgrade_time_ms' : 'upgrade_time_hms');
4096
4097
		$updated = time() - $upcontext['updated'];
4098
		$updated_hours = floor($updated / 3600);
4099
		$updated_minutes = intval(($updated / 60) % 60);
4100
		$updated_seconds = intval($updated % 60);
4101
		$updatedTxt = $updated < 60 ? 'upgrade_time_updated_s' : ($updated < 3600 ? 'upgrade_time_updated_hm' : 'upgrade_time_updated_hms');
4102
4103
		echo '
4104
					<div class="errorbox">
4105
						<h3>', $txt['upgrade_warning'], '</h3>
4106
						<p>', sprintf($txt['upgrade_time_user'], $upcontext['user']['name']), '</p>
4107
						<p>', sprintf($txt[$agoTxt], $ago_seconds, $ago_minutes, $ago_hours), '</p>
4108
						<p>', sprintf($txt[$updatedTxt], $updated_seconds, $updated_minutes, $updated_hours), '</p>';
4109
4110
		if ($updated < 600)
4111
			echo '
4112
						<p>', $txt['upgrade_run_script'], ' ', $upcontext['user']['name'], ' ', $txt['upgrade_run_script2'], '</p>';
4113
4114
		if ($updated > $upcontext['inactive_timeout'])
4115
			echo '
4116
						<p>', $txt['upgrade_run'], '</p>';
4117
		elseif ($upcontext['inactive_timeout'] > 120)
4118
			echo '
4119
						<p>', sprintf($txt['upgrade_script_timeout_minutes'], $upcontext['user']['name'], round($upcontext['inactive_timeout'] / 60, 1)), '</p>';
4120
		else
4121
			echo '
4122
						<p>', sprintf($txt['upgrade_script_timeout_seconds'], $upcontext['user']['name'], $upcontext['inactive_timeout']), '</p>';
4123
4124
		echo '
4125
					</div>';
4126
	}
4127
4128
	echo '
4129
					<strong>', $txt['upgrade_admin_login'], ' ', $disable_security ? $txt['upgrade_admin_disabled'] : '', '</strong>
4130
					<h3>', $txt['upgrade_sec_login'], '</h3>
4131
					<dl class="settings adminlogin">
4132
						<dt>
4133
							<label for="user"', $disable_security ? ' disabled' : '', '>', $txt['upgrade_username'], '</label>
4134
						</dt>
4135
						<dd>
4136
							<input type="text" name="user" value="', !empty($upcontext['username']) ? $upcontext['username'] : '', '"', $disable_security ? ' disabled' : '', '>';
4137
4138
	if (!empty($upcontext['username_incorrect']))
4139
		echo '
4140
							<div class="smalltext red">', $txt['upgrade_wrong_username'], '</div>';
4141
4142
	echo '
4143
						</dd>
4144
						<dt>
4145
							<label for="passwrd"', $disable_security ? ' disabled' : '', '>', $txt['upgrade_password'], '</label>
4146
						</dt>
4147
						<dd>
4148
							<input type="password" name="passwrd" value=""', $disable_security ? ' disabled' : '', '>';
4149
4150
	if (!empty($upcontext['password_failed']))
4151
		echo '
4152
							<div class="smalltext red">', $txt['upgrade_wrong_password'], '</div>';
4153
4154
	echo '
4155
						</dd>';
4156
4157
	// Can they continue?
4158
	if (!empty($upcontext['user']['id']) && time() - $upcontext['user']['updated'] >= $upcontext['inactive_timeout'] && $upcontext['user']['step'] > 1)
4159
	{
4160
		echo '
4161
						<dd>
4162
							<label for="cont"><input type="checkbox" id="cont" name="cont" checked>', $txt['upgrade_continue_step'], '</label>
4163
						</dd>';
4164
	}
4165
4166
	echo '
4167
					</dl>
4168
					<span class="smalltext">
4169
						', $txt['upgrade_bypass'], '
4170
					</span>
4171
					<input type="hidden" name="login_attempt" id="login_attempt" value="1">
4172
					<input type="hidden" name="js_works" id="js_works" value="0">';
4173
4174
	// Say we want the continue button!
4175
	$upcontext['continue'] = !empty($upcontext['user']['id']) && time() - $upcontext['user']['updated'] < $upcontext['inactive_timeout'] ? 2 : 1;
4176
4177
	// This defines whether javascript is going to work elsewhere :D
4178
	echo '
4179
					<script>
4180
						if (\'XMLHttpRequest\' in window && document.getElementById(\'js_works\'))
4181
							document.getElementById(\'js_works\').value = 1;
4182
4183
						// Latest version?
4184
						function smfCurrentVersion()
4185
						{
4186
							var smfVer, yourVer;
4187
4188
							if (!(\'smfVersion\' in window))
4189
								return;
4190
4191
							window.smfVersion = window.smfVersion.replace(/SMF\s?/g, \'\');
4192
4193
							smfVer = document.getElementById(\'smfVersion\');
4194
							yourVer = document.getElementById(\'yourVersion\');
4195
4196
							setInnerHTML(smfVer, window.smfVersion);
4197
4198
							var currentVersion = getInnerHTML(yourVer);
4199
							if (currentVersion < window.smfVersion)
4200
								document.getElementById(\'version_warning\').classList.remove(\'hidden\');
4201
						}
4202
						addLoadEvent(smfCurrentVersion);
4203
4204
						// This checks that the script file even exists!
4205
						if (typeof(smfSelectText) == \'undefined\')
4206
							document.getElementById(\'js_script_missing_error\').classList.remove(\'hidden\');
4207
4208
					</script>';
4209
}
4210
4211
function template_upgrade_options()
4212
{
4213
	global $upcontext, $modSettings, $db_prefix, $mmessage, $mtitle, $txt;
4214
4215
	echo '
4216
				<h3>', $txt['upgrade_areyouready'], '</h3>
4217
				<form action="', $upcontext['form_url'], '" method="post" name="upform" id="upform">';
4218
4219
	// Warning message?
4220
	if (!empty($upcontext['upgrade_options_warning']))
4221
		echo '
4222
				<div class="errorbox">
4223
					<h3>', $txt['upgrade_warning'], '</h3>
4224
					', $upcontext['upgrade_options_warning'], '
4225
				</div>';
4226
4227
	echo '
4228
				<ul class="upgrade_settings">
4229
					<li>
4230
						<input type="checkbox" name="backup" id="backup" value="1" checked>
4231
						<label for="backup">', $txt['upgrade_backup_table'], ' &quot;backup_' . $db_prefix . '&quot;.</label>
4232
						(', $txt['upgrade_recommended'], ')
4233
					</li>
4234
					<li>
4235
						<input type="checkbox" name="maint" id="maint" value="1" checked>
4236
						<label for="maint">', $txt['upgrade_maintenance'], '</label>
4237
						<span class="smalltext">(<a href="javascript:void(0)" onclick="document.getElementById(\'mainmess\').classList.toggle(\'hidden\')">', $txt['upgrade_customize'], '</a>)</span>
4238
						<div id="mainmess" class="hidden">
4239
							<strong class="smalltext">', $txt['upgrade_maintenance_title'], ' </strong><br>
4240
							<input type="text" name="maintitle" size="30" value="', htmlspecialchars($mtitle), '"><br>
4241
							<strong class="smalltext">', $txt['upgrade_maintenance_message'], ' </strong><br>
4242
							<textarea name="mainmessage" rows="3" cols="50">', htmlspecialchars($mmessage), '</textarea>
4243
						</div>
4244
					</li>
4245
					<li>
4246
						<input type="checkbox" name="debug" id="debug" value="1">
4247
						<label for="debug">'.$txt['upgrade_debug_info'], '</label>
4248
					</li>
4249
					<li>
4250
						<input type="checkbox" name="empty_error" id="empty_error" value="1">
4251
						<label for="empty_error">', $txt['upgrade_empty_errorlog'], '</label>
4252
					</li>';
4253
4254
	if (!empty($upcontext['karma_installed']['good']) || !empty($upcontext['karma_installed']['bad']))
4255
		echo '
4256
					<li>
4257
						<input type="checkbox" name="delete_karma" id="delete_karma" value="1">
4258
						<label for="delete_karma">', $txt['upgrade_delete_karma'], '</label>
4259
					</li>';
4260
4261
	echo '
4262
					<li>
4263
						<input type="checkbox" name="stats" id="stats" value="1"', empty($modSettings['allow_sm_stats']) && empty($modSettings['enable_sm_stats']) ? '' : ' checked="checked"', '>
4264
						<label for="stat">
4265
							', $txt['upgrade_stats_collection'], '<br>
4266
							<span class="smalltext">', sprintf($txt['upgrade_stats_info'], 'https://www.simplemachines.org/about/stats.php'), '</a></span>
4267
						</label>
4268
					</li>
4269
					<li>
4270
						<input type="checkbox" name="migrateSettings" id="migrateSettings" value="1"', empty($upcontext['migrate_settings_recommended']) ? '' : ' checked="checked"', '>
4271
						<label for="migrateSettings">
4272
							', $txt['upgrade_migrate_settings_file'], '
4273
						</label>
4274
					</li>
4275
				</ul>
4276
				<input type="hidden" name="upcont" value="1">';
4277
4278
	// We need a normal continue button here!
4279
	$upcontext['continue'] = 1;
4280
}
4281
4282
// Template for the database backup tool/
4283
function template_backup_database()
4284
{
4285
	global $upcontext, $support_js, $is_debug, $txt;
4286
4287
	echo '
4288
				<h3>', $txt['upgrade_wait'], '</h3>';
4289
4290
	echo '
4291
				<form action="', $upcontext['form_url'], '" name="upform" id="upform" method="post">
4292
					<input type="hidden" name="backup_done" id="backup_done" value="0">
4293
					<strong>', sprintf($txt['upgrade_completedtables_outof'], $upcontext['cur_table_num'], $upcontext['table_count']), '</strong>
4294
					<div id="debug_section">
4295
						<span id="debuginfo"></span>
4296
					</div>';
4297
4298
	// Dont any tables so far?
4299
	if (!empty($upcontext['previous_tables']))
4300
		foreach ($upcontext['previous_tables'] as $table)
4301
			echo '
4302
					<br>', $txt['upgrade_completed_table'], ' &quot;', $table, '&quot;.';
4303
4304
	echo '
4305
					<h3 id="current_tab">
4306
						', $txt['upgrade_current_table'], ' &quot;<span id="current_table">', $upcontext['cur_table_name'], '</span>&quot;
4307
					</h3>
4308
					<p id="commess" class="', $upcontext['cur_table_num'] == $upcontext['table_count'] ? 'inline_block' : 'hidden', '">', $txt['upgrade_backup_complete'], '</p>';
4309
4310
	// Continue please!
4311
	$upcontext['continue'] = $support_js ? 2 : 1;
4312
4313
	// If javascript allows we want to do this using XML.
4314
	if ($support_js)
4315
	{
4316
		echo '
4317
					<script>
4318
						var lastTable = ', $upcontext['cur_table_num'], ';
4319
						function getNextTables()
4320
						{
4321
							getXMLDocument(\'', $upcontext['form_url'], '&xml&substep=\' + lastTable, onBackupUpdate);
4322
						}
4323
4324
						// Got an update!
4325
						function onBackupUpdate(oXMLDoc)
4326
						{
4327
							var sCurrentTableName = "";
4328
							var iTableNum = 0;
4329
							var sCompletedTableName = getInnerHTML(document.getElementById(\'current_table\'));
4330
							for (var i = 0; i < oXMLDoc.getElementsByTagName("table")[0].childNodes.length; i++)
4331
								sCurrentTableName += oXMLDoc.getElementsByTagName("table")[0].childNodes[i].nodeValue;
4332
							iTableNum = oXMLDoc.getElementsByTagName("table")[0].getAttribute("num");
4333
4334
							// Update the page.
4335
							setInnerHTML(document.getElementById(\'tab_done\'), iTableNum);
4336
							setInnerHTML(document.getElementById(\'current_table\'), sCurrentTableName);
4337
							lastTable = iTableNum;
4338
							updateStepProgress(iTableNum, ', $upcontext['table_count'], ', ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');';
4339
4340
		// If debug flood the screen.
4341
		if ($is_debug)
4342
			echo '
4343
							setOuterHTML(document.getElementById(\'debuginfo\'), \'<br>', $txt['upgrade_completed_table'], ' &quot;\' + sCompletedTableName + \'&quot;.<span id="debuginfo"><\' + \'/span>\');
4344
4345
							if (document.getElementById(\'debug_section\').scrollHeight)
4346
								document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
4347
4348
		echo '
4349
							// Get the next update...
4350
							if (iTableNum == ', $upcontext['table_count'], ')
4351
							{
4352
								document.getElementById(\'commess\').classList.remove("hidden");
4353
								document.getElementById(\'current_tab\').classList.add("hidden");
4354
								document.getElementById(\'contbutt\').disabled = 0;
4355
								document.getElementById(\'backup_done\').value = 1;
4356
							}
4357
							else
4358
								getNextTables();
4359
						}
4360
						getNextTables();
4361
					//# sourceURL=dynamicScript-bkup.js
4362
					</script>';
4363
	}
4364
}
4365
4366
function template_backup_xml()
4367
{
4368
	global $upcontext;
4369
4370
	echo '
4371
		<table num="', $upcontext['cur_table_num'], '">', $upcontext['cur_table_name'], '</table>';
4372
}
4373
4374
// Here is the actual "make the changes" template!
4375
function template_database_changes()
4376
{
4377
	global $upcontext, $support_js, $is_debug, $timeLimitThreshold, $txt;
4378
4379
	if (empty($is_debug) && !empty($upcontext['upgrade_status']['debug']))
4380
		$is_debug = true;
4381
4382
	echo '
4383
				<h3>', $txt['upgrade_db_changes'], '</h3>
4384
				<h4><em>', $txt['upgrade_db_patient'], '</em></h4>';
4385
4386
	echo '
4387
				<form action="', $upcontext['form_url'], '&amp;filecount=', $upcontext['file_count'], '" name="upform" id="upform" method="post">
4388
					<input type="hidden" name="database_done" id="database_done" value="0">';
4389
4390
	// No javascript looks rubbish!
4391
	if (!$support_js)
4392
	{
4393
		foreach ($upcontext['actioned_items'] as $num => $item)
4394
		{
4395
			if ($num != 0)
4396
				echo ' Successful!';
4397
			echo '<br>' . $item;
4398
		}
4399
4400
		// Only tell deubbers how much time they wasted waiting for the upgrade because they don't have javascript.
4401
		if (!empty($upcontext['changes_complete']))
4402
		{
4403
			if ($is_debug)
4404
			{
4405
				$active = time() - $upcontext['started'];
4406
				$hours = floor($active / 3600);
4407
				$minutes = intval(($active / 60) % 60);
4408
				$seconds = intval($active % 60);
4409
4410
				echo '', sprintf($txt['upgrade_success_time_db'], $seconds, $minutes, $hours), '<br>';
4411
			}
4412
			else
4413
				echo '', $txt['upgrade_success'], '<br>';
4414
4415
			echo '
4416
					<p id="commess">', $txt['upgrade_db_complete'], '</p>';
4417
		}
4418
	}
4419
	else
4420
	{
4421
		// Tell them how many files we have in total.
4422
		if ($upcontext['file_count'] > 1)
4423
			echo '
4424
					<strong id="info1">', $txt['upgrade_script'], ' <span id="file_done">', $upcontext['cur_file_num'], '</span> of ', $upcontext['file_count'], '.</strong>';
4425
4426
		echo '
4427
					<h3 id="info2">
4428
						<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>
4429
					</h3>
4430
					<p id="commess" class="', !empty($upcontext['changes_complete']) || $upcontext['current_debug_item_num'] == $upcontext['debug_items'] ? 'inline_block' : 'hidden', '">', $txt['upgrade_db_complete2'], '</p>';
4431
4432
		if ($is_debug)
4433
		{
4434
			// Let our debuggers know how much time was spent, but not wasted since JS handled refreshing the page!
4435
			if ($upcontext['current_debug_item_num'] == $upcontext['debug_items'])
4436
			{
4437
				$active = time() - $upcontext['started'];
4438
				$hours = floor($active / 3600);
4439
				$minutes = intval(($active / 60) % 60);
4440
				$seconds = intval($active % 60);
4441
4442
				echo '
4443
					<p id="upgradeCompleted">', sprintf($txt['upgrade_success_time_db'], $seconds, $minutes, $hours), '</p>';
4444
			}
4445
			else
4446
				echo '
4447
					<p id="upgradeCompleted"></p>';
4448
4449
			echo '
4450
					<div id="debug_section">
4451
						<span id="debuginfo"></span>
4452
					</div>';
4453
		}
4454
	}
4455
4456
	// Place for the XML error message.
4457
	echo '
4458
					<div id="error_block" class="errorbox', empty($upcontext['error_message']) ? ' hidden' : '', '">
4459
						<h3>', $txt['upgrade_error'], '</h3>
4460
						<div id="error_message">', isset($upcontext['error_message']) ? $upcontext['error_message'] : $txt['upgrade_unknown_error'], '</div>
4461
					</div>';
4462
4463
	// We want to continue at some point!
4464
	$upcontext['continue'] = $support_js ? 2 : 1;
4465
4466
	// If javascript allows we want to do this using XML.
4467
	if ($support_js)
4468
	{
4469
		echo '
4470
					<script>
4471
						var lastItem = ', $upcontext['current_debug_item_num'], ';
4472
						var sLastString = "', strtr($upcontext['current_debug_item_name'], array('"' => '&quot;')), '";
4473
						var iLastSubStepProgress = -1;
4474
						var curFile = ', $upcontext['cur_file_num'], ';
4475
						var totalItems = 0;
4476
						var prevFile = 0;
4477
						var retryCount = 0;
4478
						var testvar = 0;
4479
						var timeOutID = 0;
4480
						var getData = "";
4481
						var debugItems = ', $upcontext['debug_items'], ';';
4482
4483
		if ($is_debug)
4484
			echo '
4485
						var upgradeStartTime = ' . $upcontext['started'] . ';';
4486
4487
		echo '
4488
						function getNextItem()
4489
						{
4490
							// We want to track this...
4491
							if (timeOutID)
4492
								clearTimeout(timeOutID);
4493
							timeOutID = window.setTimeout("retTimeout()", ', (10 * $timeLimitThreshold), '000);
4494
4495
							getXMLDocument(\'', $upcontext['form_url'], '&xml&filecount=', $upcontext['file_count'], '&substep=\' + lastItem + getData, onItemUpdate);
4496
						}
4497
4498
						// Got an update!
4499
						function onItemUpdate(oXMLDoc)
4500
						{
4501
							var sItemName = "";
4502
							var sDebugName = "";
4503
							var iItemNum = 0;
4504
							var iSubStepProgress = -1;
4505
							var iDebugNum = 0;
4506
							var bIsComplete = 0;
4507
							var bSkipped = 0;
4508
							getData = "";
4509
4510
							// We\'ve got something - so reset the timeout!
4511
							if (timeOutID)
4512
								clearTimeout(timeOutID);
4513
4514
							// Assume no error at this time...
4515
							document.getElementById("error_block").classList.add("hidden");
4516
4517
							// Are we getting some duff info?
4518
							if (!oXMLDoc.getElementsByTagName("item")[0])
4519
							{
4520
								// Too many errors?
4521
								if (retryCount > 15)
4522
								{
4523
									document.getElementById("error_block").classList.remove("hidden");
4524
									setInnerHTML(document.getElementById("error_message"), "Error retrieving information on step: " + (sDebugName == "" ? sLastString : sDebugName));';
4525
4526
		if ($is_debug)
4527
			echo '
4528
									setOuterHTML(document.getElementById(\'debuginfo\'), \'<span class="red">failed<\' + \'/span><span id="debuginfo"><\' + \'/span>\');';
4529
4530
		echo '
4531
								}
4532
								else
4533
								{
4534
									retryCount++;
4535
									getNextItem();
4536
								}
4537
								return false;
4538
							}
4539
4540
							// Never allow loops.
4541
							if (curFile == prevFile)
4542
							{
4543
								retryCount++;
4544
								if (retryCount > 10)
4545
								{
4546
									document.getElementById("error_block").classList.remove("hidden");
4547
									setInnerHTML(document.getElementById("error_message"), "', $txt['upgrade_loop'], '" + sDebugName);';
4548
4549
		if ($is_debug)
4550
			echo '
4551
									setOuterHTML(document.getElementById(\'debuginfo\'), \'<span class="red">failed<\' + \'/span><span id="debuginfo"><\' + \'/span>\');';
4552
4553
		echo '
4554
								}
4555
							}
4556
							retryCount = 0;
4557
4558
							for (var i = 0; i < oXMLDoc.getElementsByTagName("item")[0].childNodes.length; i++)
4559
								sItemName += oXMLDoc.getElementsByTagName("item")[0].childNodes[i].nodeValue;
4560
							for (var i = 0; i < oXMLDoc.getElementsByTagName("debug")[0].childNodes.length; i++)
4561
								sDebugName += oXMLDoc.getElementsByTagName("debug")[0].childNodes[i].nodeValue;
4562
							for (var i = 0; i < oXMLDoc.getElementsByTagName("get").length; i++)
4563
							{
4564
								getData += "&" + oXMLDoc.getElementsByTagName("get")[i].getAttribute("key") + "=";
4565
								for (var j = 0; j < oXMLDoc.getElementsByTagName("get")[i].childNodes.length; j++)
4566
								{
4567
									getData += oXMLDoc.getElementsByTagName("get")[i].childNodes[j].nodeValue;
4568
								}
4569
							}
4570
4571
							iItemNum = oXMLDoc.getElementsByTagName("item")[0].getAttribute("num");
4572
							iDebugNum = parseInt(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("num"));
4573
							bIsComplete = parseInt(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("complete"));
4574
							bSkipped = parseInt(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("skipped"));
4575
							iSubStepProgress = parseFloat(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("percent"));
4576
							sLastString = sDebugName + " (Item: " + iDebugNum + ")";
4577
4578
							curFile = parseInt(oXMLDoc.getElementsByTagName("file")[0].getAttribute("num"));
4579
							debugItems = parseInt(oXMLDoc.getElementsByTagName("file")[0].getAttribute("debug_items"));
4580
							totalItems = parseInt(oXMLDoc.getElementsByTagName("file")[0].getAttribute("items"));
4581
4582
							// If we have an error we haven\'t completed!
4583
							if (oXMLDoc.getElementsByTagName("error")[0] && bIsComplete)
4584
								iDebugNum = lastItem;
4585
4586
							// Do we have the additional progress bar?
4587
							if (iSubStepProgress != -1)
4588
							{
4589
								document.getElementById("substep_bar_div").classList.remove("hidden");
4590
								document.getElementById("substep_progress").style.width = iSubStepProgress + "%";
4591
								setInnerHTML(document.getElementById("substep_text"), iSubStepProgress + "%");
4592
								setInnerHTML(document.getElementById("substep_name"), sDebugName.replace(/\./g, ""));
4593
							}
4594
							else
4595
							{
4596
								document.getElementById("substep_bar_div").classList.add("hidden");
4597
							}
4598
4599
							// Move onto the next item?
4600
							if (bIsComplete)
4601
								lastItem = iDebugNum;
4602
							else
4603
								lastItem = iDebugNum - 1;
4604
4605
							// Are we finished?
4606
							if (bIsComplete && iDebugNum == -1 && curFile >= ', $upcontext['file_count'], ')
4607
							{';
4608
4609
		// Database Changes, tell us how much time we spen to do this.  If this gets updated via JS.
4610
		if ($is_debug)
4611
			echo '
4612
								document.getElementById(\'debug_section\').classList.add("hidden");
4613
4614
								var upgradeFinishedTime = parseInt(oXMLDoc.getElementsByTagName("curtime")[0].childNodes[0].nodeValue);
4615
								var diffTime = upgradeFinishedTime - upgradeStartTime;
4616
								var diffHours = Math.floor(diffTime / 3600);
4617
								var diffMinutes = parseInt((diffTime / 60) % 60);
4618
								var diffSeconds = parseInt(diffTime % 60);
4619
4620
								var completedTxt = "', $txt['upgrade_success_time_db'], '";
4621
console.log(completedTxt, upgradeFinishedTime, diffTime, diffHours, diffMinutes, diffSeconds);
4622
4623
								completedTxt = completedTxt.replace("%1$d", diffSeconds).replace("%2$d", diffMinutes).replace("%3$d", diffHours);
4624
console.log(completedTxt, upgradeFinishedTime, diffTime, diffHours, diffMinutes, diffSeconds);
4625
								setInnerHTML(document.getElementById("upgradeCompleted"), completedTxt);';
4626
4627
		echo '
4628
4629
								document.getElementById(\'commess\').classList.remove("hidden");
4630
								document.getElementById(\'contbutt\').disabled = 0;
4631
								document.getElementById(\'database_done\').value = 1;';
4632
4633
		if ($upcontext['file_count'] > 1)
4634
			echo '
4635
								document.getElementById(\'info1\').classList.add(\'hidden\');';
4636
4637
		echo '
4638
								document.getElementById(\'info2\').classList.add(\'hidden\');
4639
								updateStepProgress(100, 100, ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');
4640
								return true;
4641
							}
4642
							// Was it the last step in the file?
4643
							else if (bIsComplete && iDebugNum == -1)
4644
							{
4645
								lastItem = 0;
4646
								prevFile = curFile;';
4647
4648
		if ($is_debug)
4649
			echo '
4650
								setOuterHTML(document.getElementById(\'debuginfo\'), \'Moving to next script file...done<br><span id="debuginfo"><\' + \'/span>\');';
4651
4652
		echo '
4653
								getNextItem();
4654
								return true;
4655
							}';
4656
4657
		// If debug scroll the screen.
4658
		if ($is_debug)
4659
			echo '
4660
							if (iLastSubStepProgress == -1)
4661
							{
4662
								// Give it consistent dots.
4663
								dots = sDebugName.match(/\./g);
4664
								numDots = dots ? dots.length : 0;
4665
								for (var i = numDots; i < 3; i++)
4666
									sDebugName += ".";
4667
								setOuterHTML(document.getElementById(\'debuginfo\'), sDebugName + \'<span id="debuginfo"><\' + \'/span>\');
4668
							}
4669
							iLastSubStepProgress = iSubStepProgress;
4670
4671
							if (bIsComplete && bSkipped)
4672
								setOuterHTML(document.getElementById(\'debuginfo\'), \'skipped<br><span id="debuginfo"><\' + \'/span>\');
4673
							else if (bIsComplete)
4674
								setOuterHTML(document.getElementById(\'debuginfo\'), \'done<br><span id="debuginfo"><\' + \'/span>\');
4675
							else
4676
								setOuterHTML(document.getElementById(\'debuginfo\'), \'...<span id="debuginfo"><\' + \'/span>\');
4677
4678
							if (document.getElementById(\'debug_section\').scrollHeight)
4679
								document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
4680
4681
		echo '
4682
							// Update the page.
4683
							setInnerHTML(document.getElementById(\'item_num\'), iItemNum);
4684
							setInnerHTML(document.getElementById(\'cur_item_name\'), sItemName);';
4685
4686
		if ($upcontext['file_count'] > 1)
4687
		{
4688
			echo '
4689
							setInnerHTML(document.getElementById(\'file_done\'), curFile);
4690
							setInnerHTML(document.getElementById(\'item_count\'), totalItems);';
4691
		}
4692
4693
		echo '
4694
							// Is there an error?
4695
							if (oXMLDoc.getElementsByTagName("error")[0])
4696
							{
4697
								var sErrorMsg = "";
4698
								for (var i = 0; i < oXMLDoc.getElementsByTagName("error")[0].childNodes.length; i++)
4699
									sErrorMsg += oXMLDoc.getElementsByTagName("error")[0].childNodes[i].nodeValue;
4700
								document.getElementById("error_block").classList.remove("hidden");
4701
								setInnerHTML(document.getElementById("error_message"), sErrorMsg);
4702
								return false;
4703
							}
4704
4705
							// Get the progress bar right.
4706
							barTotal = debugItems * ', $upcontext['file_count'], ';
4707
							barDone = (debugItems * (curFile - 1)) + lastItem;
4708
4709
							updateStepProgress(barDone, barTotal, ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');
4710
4711
							// Finally - update the time here as it shows the server is responding!
4712
							curTime = new Date();
4713
							iElapsed = (curTime.getTime() / 1000 - ', $upcontext['started'], ');
4714
							mins = parseInt(iElapsed / 60);
4715
							secs = parseInt(iElapsed - mins * 60);
4716
							setInnerHTML(document.getElementById("mins_elapsed"), mins);
4717
							setInnerHTML(document.getElementById("secs_elapsed"), secs);
4718
4719
							getNextItem();
4720
							return true;
4721
						}
4722
4723
						// What if we timeout?!
4724
						function retTimeout(attemptAgain)
4725
						{
4726
							// Oh noes...
4727
							if (!attemptAgain)
4728
							{
4729
								document.getElementById("error_block").classList.remove("hidden");
4730
								setInnerHTML(document.getElementById("error_message"), "', sprintf($txt['upgrade_respondtime'], ($timeLimitThreshold * 10)), '" + "<a href=\"#\" onclick=\"retTimeout(true); return false;\">', $txt['upgrade_respondtime_clickhere'], '</a>");
4731
							}
4732
							else
4733
							{
4734
								document.getElementById("error_block").classList.add("hidden");
4735
								getNextItem();
4736
							}
4737
						}';
4738
4739
		// Start things off assuming we've not errored.
4740
		if (empty($upcontext['error_message']))
4741
			echo '
4742
						getNextItem();';
4743
4744
		echo '
4745
					//# sourceURL=dynamicScript-dbch.js
4746
					</script>';
4747
	}
4748
	return;
4749
}
4750
4751
function template_database_xml()
4752
{
4753
	global $is_debug, $upcontext;
4754
4755
	echo '
4756
	<file num="', $upcontext['cur_file_num'], '" items="', $upcontext['total_items'], '" debug_items="', $upcontext['debug_items'], '">', $upcontext['cur_file_name'], '</file>
4757
	<item num="', $upcontext['current_item_num'], '">', $upcontext['current_item_name'], '</item>
4758
	<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>';
4759
4760
	if (!empty($upcontext['error_message']))
4761
		echo '
4762
	<error>', $upcontext['error_message'], '</error>';
4763
4764
	if (!empty($upcontext['error_string']))
4765
		echo '
4766
	<sql>', $upcontext['error_string'], '</sql>';
4767
4768
	if ($is_debug)
4769
		echo '
4770
	<curtime>', time(), '</curtime>';
4771
}
4772
4773
// Template for the UTF-8 conversion step. Basically a copy of the backup stuff with slight modifications....
4774
function template_convert_utf8()
4775
{
4776
	global $upcontext, $support_js, $is_debug, $txt;
4777
4778
	echo '
4779
				<h3>', $txt['upgrade_wait2'], '</h3>
4780
				<form action="', $upcontext['form_url'], '" name="upform" id="upform" method="post">
4781
					<input type="hidden" name="utf8_done" id="utf8_done" value="0">
4782
					<strong>', $txt['upgrade_completed'], ' <span id="tab_done">', $upcontext['cur_table_num'], '</span> ', $txt['upgrade_outof'], ' ', $upcontext['table_count'], ' ', $txt['upgrade_tables'], '</strong>
4783
					<div id="debug_section">
4784
						<span id="debuginfo"></span>
4785
					</div>';
4786
4787
	// Done any tables so far?
4788
	if (!empty($upcontext['previous_tables']))
4789
		foreach ($upcontext['previous_tables'] as $table)
4790
			echo '
4791
					<br>', $txt['upgrade_completed_table'], ' &quot;', $table, '&quot;.';
4792
4793
	echo '
4794
					<h3 id="current_tab">
4795
						', $txt['upgrade_current_table'], ' &quot;<span id="current_table">', $upcontext['cur_table_name'], '</span>&quot;
4796
					</h3>';
4797
4798
	// If we dropped their index, let's let them know
4799
	if ($upcontext['dropping_index'])
4800
		echo '
4801
					<p id="indexmsg" class="', $upcontext['cur_table_num'] == $upcontext['table_count'] ? 'inline_block' : 'hidden', '>', $txt['upgrade_fulltext'], '</p>';
4802
4803
	// Completion notification
4804
	echo '
4805
					<p id="commess" class="', $upcontext['cur_table_num'] == $upcontext['table_count'] ? 'inline_block' : 'hidden', '">', $txt['upgrade_conversion_proceed'], '</p>';
4806
4807
	// Continue please!
4808
	$upcontext['continue'] = $support_js ? 2 : 1;
4809
4810
	// If javascript allows we want to do this using XML.
4811
	if ($support_js)
4812
	{
4813
		echo '
4814
					<script>
4815
						var lastTable = ', $upcontext['cur_table_num'], ';
4816
						function getNextTables()
4817
						{
4818
							getXMLDocument(\'', $upcontext['form_url'], '&xml&substep=\' + lastTable, onConversionUpdate);
4819
						}
4820
4821
						// Got an update!
4822
						function onConversionUpdate(oXMLDoc)
4823
						{
4824
							var sCurrentTableName = "";
4825
							var iTableNum = 0;
4826
							var sCompletedTableName = getInnerHTML(document.getElementById(\'current_table\'));
4827
							for (var i = 0; i < oXMLDoc.getElementsByTagName("table")[0].childNodes.length; i++)
4828
								sCurrentTableName += oXMLDoc.getElementsByTagName("table")[0].childNodes[i].nodeValue;
4829
							iTableNum = oXMLDoc.getElementsByTagName("table")[0].getAttribute("num");
4830
4831
							// Update the page.
4832
							setInnerHTML(document.getElementById(\'tab_done\'), iTableNum);
4833
							setInnerHTML(document.getElementById(\'current_table\'), sCurrentTableName);
4834
							lastTable = iTableNum;
4835
							updateStepProgress(iTableNum, ', $upcontext['table_count'], ', ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');';
4836
4837
		// If debug flood the screen.
4838
		if ($is_debug)
4839
			echo '
4840
						setOuterHTML(document.getElementById(\'debuginfo\'), \'<br>', $txt['upgrade_completed_table'], ' &quot;\' + sCompletedTableName + \'&quot;.<span id="debuginfo"><\' + \'/span>\');
4841
4842
						if (document.getElementById(\'debug_section\').scrollHeight)
4843
							document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
4844
4845
		echo '
4846
						// Get the next update...
4847
						if (iTableNum == ', $upcontext['table_count'], ')
4848
						{
4849
							document.getElementById(\'commess\').classList.remove(\'hidden\');
4850
							if (document.getElementById(\'indexmsg\') != null) {
4851
								document.getElementById(\'indexmsg\').classList.remove(\'hidden\');
4852
							}
4853
							document.getElementById(\'current_tab\').classList.add(\'hidden\');
4854
							document.getElementById(\'contbutt\').disabled = 0;
4855
							document.getElementById(\'utf8_done\').value = 1;
4856
						}
4857
						else
4858
							getNextTables();
4859
					}
4860
					getNextTables();
4861
				//# sourceURL=dynamicScript-conv.js
4862
				</script>';
4863
	}
4864
}
4865
4866
function template_convert_xml()
4867
{
4868
	global $upcontext;
4869
4870
	echo '
4871
	<table num="', $upcontext['cur_table_num'], '">', $upcontext['cur_table_name'], '</table>';
4872
}
4873
4874
// Template for the database backup tool/
4875
function template_serialize_json()
4876
{
4877
	global $upcontext, $support_js, $is_debug, $txt;
4878
4879
	echo '
4880
				<h3>', $txt['upgrade_convert_datajson'], '</h3>
4881
				<form action="', $upcontext['form_url'], '" name="upform" id="upform" method="post">
4882
					<input type="hidden" name="json_done" id="json_done" value="0">
4883
					<strong>', $txt['upgrade_completed'], ' <span id="tab_done">', $upcontext['cur_table_num'], '</span> ', $txt['upgrade_outof'], ' ', $upcontext['table_count'], ' ', $txt['upgrade_tables'], '</strong>
4884
					<div id="debug_section">
4885
						<span id="debuginfo"></span>
4886
					</div>';
4887
4888
	// Dont any tables so far?
4889
	if (!empty($upcontext['previous_tables']))
4890
		foreach ($upcontext['previous_tables'] as $table)
4891
			echo '
4892
					<br>', $txt['upgrade_completed_table'], ' &quot;', $table, '&quot;.';
4893
4894
	echo '
4895
					<h3 id="current_tab">
4896
						', $txt['upgrade_current_table'], ' &quot;<span id="current_table">', $upcontext['cur_table_name'], '</span>&quot;
4897
					</h3>
4898
					<p id="commess" class="', $upcontext['cur_table_num'] == $upcontext['table_count'] ? 'inline_block' : 'hidden', '">', $txt['upgrade_json_completed'], '</p>';
4899
4900
	// Try to make sure substep was reset.
4901
	if ($upcontext['cur_table_num'] == $upcontext['table_count'])
4902
		echo '
4903
					<input type="hidden" name="substep" id="substep" value="0">';
4904
4905
	// Continue please!
4906
	$upcontext['continue'] = $support_js ? 2 : 1;
4907
4908
	// If javascript allows we want to do this using XML.
4909
	if ($support_js)
4910
	{
4911
		echo '
4912
					<script>
4913
						var lastTable = ', $upcontext['cur_table_num'], ';
4914
						function getNextTables()
4915
						{
4916
							getXMLDocument(\'', $upcontext['form_url'], '&xml&substep=\' + lastTable, onBackupUpdate);
4917
						}
4918
4919
						// Got an update!
4920
						function onBackupUpdate(oXMLDoc)
4921
						{
4922
							var sCurrentTableName = "";
4923
							var iTableNum = 0;
4924
							var sCompletedTableName = getInnerHTML(document.getElementById(\'current_table\'));
4925
							for (var i = 0; i < oXMLDoc.getElementsByTagName("table")[0].childNodes.length; i++)
4926
								sCurrentTableName += oXMLDoc.getElementsByTagName("table")[0].childNodes[i].nodeValue;
4927
							iTableNum = oXMLDoc.getElementsByTagName("table")[0].getAttribute("num");
4928
4929
							// Update the page.
4930
							setInnerHTML(document.getElementById(\'tab_done\'), iTableNum);
4931
							setInnerHTML(document.getElementById(\'current_table\'), sCurrentTableName);
4932
							lastTable = iTableNum;
4933
							updateStepProgress(iTableNum, ', $upcontext['table_count'], ', ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');';
4934
4935
		// If debug flood the screen.
4936
		if ($is_debug)
4937
			echo '
4938
							setOuterHTML(document.getElementById(\'debuginfo\'), \'<br>', $txt['upgrade_completed_table'], ' &quot;\' + sCompletedTableName + \'&quot;.<span id="debuginfo"><\' + \'/span>\');
4939
4940
							if (document.getElementById(\'debug_section\').scrollHeight)
4941
								document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
4942
4943
		echo '
4944
							// Get the next update...
4945
							if (iTableNum == ', $upcontext['table_count'], ')
4946
							{
4947
								document.getElementById(\'commess\').classList.remove("hidden");
4948
								document.getElementById(\'current_tab\').classList.add("hidden");
4949
								document.getElementById(\'contbutt\').disabled = 0;
4950
								document.getElementById(\'json_done\').value = 1;
4951
							}
4952
							else
4953
								getNextTables();
4954
						}
4955
						getNextTables();
4956
					//# sourceURL=dynamicScript-json.js
4957
					</script>';
4958
	}
4959
}
4960
4961
function template_serialize_json_xml()
4962
{
4963
	global $upcontext;
4964
4965
	echo '
4966
	<table num="', $upcontext['cur_table_num'], '">', $upcontext['cur_table_name'], '</table>';
4967
}
4968
4969
function template_upgrade_complete()
4970
{
4971
	global $upcontext, $upgradeurl, $settings, $boardurl, $is_debug, $txt;
4972
4973
	echo '
4974
				<h3>', sprintf($txt['upgrade_done'], $boardurl), '</h3>
4975
				<form action="', $boardurl, '/index.php">';
4976
4977
	if (!empty($upcontext['can_delete_script']))
4978
		echo '
4979
					<label>
4980
						<input type="checkbox" id="delete_self" onclick="doTheDelete(this);"> ', $txt['upgrade_delete_now'], '
4981
					</label>
4982
					<em>', $txt['upgrade_delete_server'], '</em>
4983
					<script>
4984
						function doTheDelete(theCheck)
4985
						{
4986
							var theImage = document.getElementById ? document.getElementById("delete_upgrader") : document.all.delete_upgrader;
4987
							theImage.src = "', $upgradeurl, '?delete=1&ts_" + (new Date().getTime());
4988
							theCheck.disabled = true;
4989
						}
4990
					</script>
4991
					<img src="', $settings['default_theme_url'], '/images/blank.png" alt="" id="delete_upgrader"><br>';
4992
4993
	// Show Upgrade time in debug mode when we completed the upgrade process totally
4994
	if ($is_debug)
4995
	{
4996
		$active = time() - $upcontext['started'];
4997
		$hours = floor($active / 3600);
4998
		$minutes = intval(($active / 60) % 60);
4999
		$seconds = intval($active % 60);
5000
5001
		if ($hours > 0)
5002
			echo '', sprintf($txt['upgrade_completed_time_hms'], $seconds, $minutes, $hours), '';
5003
		elseif ($minutes > 0)
5004
			echo '', sprintf($txt['upgrade_completed_time_ms'], $seconds, $minutes), '';
5005
		elseif ($seconds > 0)
5006
			echo '', sprintf($txt['upgrade_completed_time_s'], $seconds), '';
5007
	}
5008
5009
	echo '
5010
					<p>
5011
						', sprintf($txt['upgrade_problems'], 'https://www.simplemachines.org'), '
5012
						<br>
5013
						', $txt['upgrade_luck'], '<br>
5014
						Simple Machines
5015
					</p>';
5016
}
5017
5018
/**
5019
 * Convert MySQL (var)char ip col to binary
5020
 *
5021
 * newCol needs to be a varbinary(16) null able field
5022
 *
5023
 * @param string $targetTable The table to perform the operation on
5024
 * @param string $oldCol The old column to gather data from
5025
 * @param string $newCol The new column to put data in
5026
 * @param int $limit The amount of entries to handle at once.
5027
 * @param int $setSize The amount of entries after which to update the database.
5028
 * @return bool
5029
 */
5030
function MySQLConvertOldIp($targetTable, $oldCol, $newCol, $limit = 50000, $setSize = 100)
5031
{
5032
	global $smcFunc, $step_progress;
5033
5034
	$current_substep = !isset($_GET['substep']) ? 0 : (int) $_GET['substep'];
5035
5036
	if (empty($_GET['a']))
5037
		$_GET['a'] = 0;
5038
	$step_progress['name'] = 'Converting ips';
5039
	$step_progress['current'] = $_GET['a'];
5040
5041
	// Skip this if we don't have the column
5042
	$request = $smcFunc['db_query']('', '
5043
		SHOW FIELDS
5044
		FROM {db_prefix}{raw:table}
5045
		WHERE Field = {string:name}',
5046
		array(
5047
			'table' => $targetTable,
5048
			'name' => $oldCol,
5049
		)
5050
	);
5051
	if ($smcFunc['db_num_rows']($request) !== 1)
5052
	{
5053
		$smcFunc['db_free_result']($request);
5054
		return;
5055
	}
5056
	$smcFunc['db_free_result']($request);
5057
5058
	$is_done = false;
5059
	while (!$is_done)
5060
	{
5061
		// Keep looping at the current step.
5062
		nextSubstep($current_substep);
5063
5064
		// mysql default max length is 1mb https://dev.mysql.com/doc/refman/5.1/en/packet-too-large.html
5065
		$arIp = array();
5066
5067
		$request = $smcFunc['db_query']('', '
5068
			SELECT DISTINCT {raw:old_col}
5069
			FROM {db_prefix}{raw:table_name}
5070
			WHERE {raw:new_col} IS NULL AND
5071
				{raw:old_col} != {string:unknown} AND
5072
				{raw:old_col} != {string:empty}
5073
			LIMIT {int:limit}',
5074
			array(
5075
				'old_col' => $oldCol,
5076
				'new_col' => $newCol,
5077
				'table_name' => $targetTable,
5078
				'empty' => '',
5079
				'limit' => $limit,
5080
				'unknown' => 'unknown',
5081
			)
5082
		);
5083
		while ($row = $smcFunc['db_fetch_assoc']($request))
5084
			$arIp[] = $row[$oldCol];
5085
5086
		$smcFunc['db_free_result']($request);
5087
5088
		// Special case, null ip could keep us in a loop.
5089
		if (!isset($arIp[0]))
5090
			unset($arIp[0]);
5091
5092
		if (empty($arIp))
5093
			$is_done = true;
5094
5095
		$updates = array();
5096
		$cases = array();
5097
		$count = count($arIp);
5098
		for ($i = 0; $i < $count; $i++)
5099
		{
5100
			$arIp[$i] = trim($arIp[$i]);
5101
5102
			if (!filter_var($arIp[$i], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6))
5103
				continue;
5104
5105
			$updates['ip' . $i] = $arIp[$i];
5106
			$cases[$arIp[$i]] = 'WHEN ' . $oldCol . ' = {string:ip' . $i . '} THEN {inet:ip' . $i . '}';
5107
5108
			if ($setSize > 0 && $i % $setSize === 0)
5109
			{
5110
				if (count($updates) == 1)
5111
					continue;
5112
5113
				$updates['whereSet'] = array_values($updates);
5114
				$smcFunc['db_query']('', '
5115
					UPDATE {db_prefix}' . $targetTable . '
5116
					SET ' . $newCol . ' = CASE ' .
5117
					implode('
5118
						', $cases) . '
5119
						ELSE NULL
5120
					END
5121
					WHERE ' . $oldCol . ' IN ({array_string:whereSet})',
5122
					$updates
5123
				);
5124
5125
				$updates = array();
5126
				$cases = array();
5127
			}
5128
		}
5129
5130
		// Incase some extras made it through.
5131
		if (!empty($updates))
5132
		{
5133
			if (count($updates) == 1)
5134
			{
5135
				foreach ($updates as $key => $ip)
5136
				{
5137
					$smcFunc['db_query']('', '
5138
						UPDATE {db_prefix}' . $targetTable . '
5139
						SET ' . $newCol . ' = {inet:ip}
5140
						WHERE ' . $oldCol . ' = {string:ip}',
5141
						array(
5142
							'ip' => $ip
5143
						)
5144
					);
5145
				}
5146
			}
5147
			else
5148
			{
5149
				$updates['whereSet'] = array_values($updates);
5150
				$smcFunc['db_query']('', '
5151
					UPDATE {db_prefix}' . $targetTable . '
5152
					SET ' . $newCol . ' = CASE ' .
5153
					implode('
5154
						', $cases) . '
5155
						ELSE NULL
5156
					END
5157
					WHERE ' . $oldCol . ' IN ({array_string:whereSet})',
5158
					$updates
5159
				);
5160
			}
5161
		}
5162
		else
5163
			$is_done = true;
5164
5165
		$_GET['a'] += $limit;
5166
		$step_progress['current'] = $_GET['a'];
5167
	}
5168
5169
	unset($_GET['a']);
5170
}
5171
5172
/**
5173
 * Get the column info. This is basically the same as smf_db_list_columns but we get 1 column, force detail and other checks.
5174
 *
5175
 * @param string $targetTable The table to perform the operation on
5176
 * @param string $column The column we are looking for.
5177
 *
5178
 * @return array Info on the table.
5179
 */
5180
function upgradeGetColumnInfo($targetTable, $column)
5181
{
5182
	global $smcFunc;
5183
5184
	// This should already be here, but be safe.
5185
	db_extend('packages');
5186
5187
	$columns = $smcFunc['db_list_columns']($targetTable, true);
5188
5189
	if (isset($columns[$column]))
5190
		return $columns[$column];
5191
	else
5192
		return null;
5193
}
5194
5195
?>