Issues (1061)

other/upgrade.php (18 issues)

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

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

784
		while ($file = readdir(/** @scrutinizer ignore-type */ $dh))
Loading history...
785
		{
786
			if (preg_match('~upgrade_\d-\d_([A-Za-z])+\.sql~i', $file, $matches) && isset($matches[1]))
787
				@unlink(dirname(__FILE__) . '/' . $file);
788
		}
789
		closedir($dh);
0 ignored issues
show
It seems like $dh can also be of type false; however, parameter $dir_handle of closedir() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

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

789
		closedir(/** @scrutinizer ignore-type */ $dh);
Loading history...
790
791
		// Legacy files while we're at it. NOTE: We only touch files we KNOW shouldn't be there.
792
		// 1.1 Sources files not in 2.0+
793
		@unlink(dirname(__FILE__) . '/Sources/ModSettings.php');
794
		// 1.1 Templates that don't exist any more (e.g. renamed)
795
		@unlink(dirname(__FILE__) . '/Themes/default/Combat.template.php');
796
		@unlink(dirname(__FILE__) . '/Themes/default/Modlog.template.php');
797
		// 1.1 JS files were stored in the main theme folder, but in 2.0+ are in the scripts/ folder
798
		@unlink(dirname(__FILE__) . '/Themes/default/fader.js');
799
		@unlink(dirname(__FILE__) . '/Themes/default/script.js');
800
		@unlink(dirname(__FILE__) . '/Themes/default/spellcheck.js');
801
		@unlink(dirname(__FILE__) . '/Themes/default/xml_board.js');
802
		@unlink(dirname(__FILE__) . '/Themes/default/xml_topic.js');
803
804
		// 2.0 Sources files not in 2.1+
805
		@unlink(dirname(__FILE__) . '/Sources/DumpDatabase.php');
806
		@unlink(dirname(__FILE__) . '/Sources/LockTopic.php');
807
808
		header('location: http://' . (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : $_SERVER['SERVER_NAME'] . ':' . $_SERVER['SERVER_PORT']) . dirname($_SERVER['PHP_SELF']) . '/Themes/default/images/blank.png');
809
		exit;
810
	}
811
812
	// Something is causing this to happen, and it's annoying.  Stop it.
813
	$temp = 'upgrade_php?step';
814
	while (strlen($temp) > 4)
815
	{
816
		if (isset($_GET[$temp]))
817
			unset($_GET[$temp]);
818
		$temp = substr($temp, 1);
819
	}
820
821
	// Force a step, defaulting to 0.
822
	$_GET['step'] = (int) @$_GET['step'];
823
	$_GET['substep'] = (int) @$_GET['substep'];
824
}
825
826
// Step 0 - Let's welcome them in and ask them to login!
827
function WelcomeLogin()
828
{
829
	global $boarddir, $sourcedir, $modSettings, $cachedir, $upgradeurl, $upcontext;
830
	global $smcFunc, $db_type, $databases, $boardurl;
831
832
	// We global $txt here so that the language files can add to them. This variable is NOT unused.
833
	global $txt;
834
835
	$upcontext['sub_template'] = 'welcome_message';
836
837
	// Check for some key files - one template, one language, and a new and an old source file.
838
	$check = @file_exists($modSettings['theme_dir'] . '/index.template.php')
839
		&& @file_exists($sourcedir . '/QueryString.php')
840
		&& @file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php')
841
		&& @file_exists(dirname(__FILE__) . '/upgrade_2-1_' . $db_type . '.sql');
842
843
	// Need legacy scripts?
844
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 2.1)
845
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_2-0_' . $db_type . '.sql');
846
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 2.0)
847
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_1-1.sql');
848
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 1.1)
849
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_1-0.sql');
850
851
	// We don't need "-utf8" files anymore...
852
	$upcontext['language'] = str_ireplace('-utf8', '', $upcontext['language']);
853
854
	if (!$check)
855
		// 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.
856
		return throw_error($txt['error_upgrade_files_missing']);
857
858
	// Do they meet the install requirements?
859
	if (!php_version_check())
860
		return throw_error($txt['error_php_too_low']);
861
862
	if (!db_version_check())
863
		return throw_error(sprintf($txt['error_db_too_low'], $databases[$db_type]['name']));
864
865
	// Do some checks to make sure they have proper privileges
866
	db_extend('packages');
867
868
	// CREATE
869
	$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');
870
871
	// ALTER
872
	$alter = $smcFunc['db_add_column']('{db_prefix}priv_check', array('name' => 'txt', 'type' => 'varchar', 'size' => 4, 'null' => false, 'default' => ''));
873
874
	// DROP
875
	$drop = $smcFunc['db_drop_table']('{db_prefix}priv_check');
876
877
	// Sorry... we need CREATE, ALTER and DROP
878
	if (!$create || !$alter || !$drop)
879
		return throw_error(sprintf($txt['error_db_privileges'], $databases[$db_type]['name']));
880
881
	// Do a quick version spot check.
882
	$temp = substr(@implode('', @file($boarddir . '/index.php')), 0, 4096);
0 ignored issues
show
It seems like @file($boarddir . '/index.php') can also be of type false; however, parameter $pieces of implode() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

882
	$temp = substr(@implode('', /** @scrutinizer ignore-type */ @file($boarddir . '/index.php')), 0, 4096);
Loading history...
883
	preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $temp, $match);
884
	if (empty($match[1]) || (trim($match[1]) != SMF_VERSION))
885
		return throw_error($txt['error_upgrade_old_files']);
886
887
	// What absolutely needs to be writable?
888
	$writable_files = array(
889
		$boarddir . '/Settings.php',
890
		$boarddir . '/Settings_bak.php',
891
	);
892
893
	// Only check for minified writable files if we have it enabled or not set.
894
	if (!empty($modSettings['minimize_files']) || !isset($modSettings['minimize_files']))
895
		$writable_files += array(
896
			$modSettings['theme_dir'] . '/css/minified.css',
897
			$modSettings['theme_dir'] . '/scripts/minified.js',
898
			$modSettings['theme_dir'] . '/scripts/minified_deferred.js',
899
		);
900
901
	// Do we need to add this setting?
902
	$need_settings_update = empty($modSettings['custom_avatar_dir']);
903
904
	$custom_av_dir = !empty($modSettings['custom_avatar_dir']) ? $modSettings['custom_avatar_dir'] : $GLOBALS['boarddir'] . '/custom_avatar';
905
	$custom_av_url = !empty($modSettings['custom_avatar_url']) ? $modSettings['custom_avatar_url'] : $boardurl . '/custom_avatar';
906
907
	// This little fellow has to cooperate...
908
	quickFileWritable($custom_av_dir);
909
910
	// Are we good now?
911
	if (!is_writable($custom_av_dir))
912
		return throw_error(sprintf($txt['error_dir_not_writable'], $custom_av_dir));
913
	elseif ($need_settings_update)
914
	{
915
		if (!function_exists('cache_put_data'))
916
			require_once($sourcedir . '/Load.php');
917
918
		updateSettings(array('custom_avatar_dir' => $custom_av_dir));
919
		updateSettings(array('custom_avatar_url' => $custom_av_url));
920
	}
921
922
	require_once($sourcedir . '/Security.php');
923
924
	// Check the cache directory.
925
	$cachedir_temp = empty($cachedir) ? $boarddir . '/cache' : $cachedir;
926
	if (!file_exists($cachedir_temp))
927
		@mkdir($cachedir_temp);
928
929
	if (!file_exists($cachedir_temp))
930
		return throw_error($txt['error_cache_not_found']);
931
932
	quickFileWritable($cachedir_temp . '/db_last_error.php');
933
934
	if (!file_exists($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php'))
935
		return throw_error(sprintf($txt['error_lang_index_missing'], $upcontext['language'], $upgradeurl));
936
	elseif (!isset($_GET['skiplang']))
937
	{
938
		$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php')), 0, 4096);
939
		preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
940
941
		if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
942
			return throw_error(sprintf($txt['error_upgrade_old_lang_files'], $upcontext['language'], $upgradeurl));
943
	}
944
945
	if (!makeFilesWritable($writable_files))
946
		return false;
947
948
	// Check agreement.txt. (it may not exist, in which case $boarddir must be writable.)
949
	if (isset($modSettings['agreement']) && (!is_writable($boarddir) || file_exists($boarddir . '/agreement.txt')) && !is_writable($boarddir . '/agreement.txt'))
950
		return throw_error($txt['error_agreement_not_writable']);
951
952
	// Upgrade the agreement.
953
	elseif (isset($modSettings['agreement']))
954
	{
955
		$fp = fopen($boarddir . '/agreement.txt', 'w');
956
		fwrite($fp, $modSettings['agreement']);
0 ignored issues
show
It seems like $fp can also be of type false; however, parameter $handle of fwrite() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

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

956
		fwrite(/** @scrutinizer ignore-type */ $fp, $modSettings['agreement']);
Loading history...
957
		fclose($fp);
0 ignored issues
show
It seems like $fp can also be of type false; however, parameter $handle of fclose() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

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

957
		fclose(/** @scrutinizer ignore-type */ $fp);
Loading history...
958
	}
959
960
	// We're going to check that their board dir setting is right in case they've been moving stuff around.
961
	if (strtr($boarddir, array('/' => '', '\\' => '')) != strtr(dirname(__FILE__), array('/' => '', '\\' => '')))
962
		$upcontext['warning'] = '
963
			' . sprintf($txt['upgrade_boarddir_settings'], $boarddir, dirname(__FILE__)) . '<br>
964
			<ul>
965
				<li>' . $txt['upgrade_boarddir'] . '  ' . $boarddir . '</li>
966
				<li>' . $txt['upgrade_sourcedir'] . '  ' . $boarddir . '</li>
967
				<li>' . $txt['upgrade_cachedir'] . '  ' . $cachedir_temp . '</li>
968
			</ul>
969
			' . $txt['upgrade_incorrect_settings'] . '';
970
971
	// Confirm mbstring is loaded...
972
	if (!extension_loaded('mbstring'))
973
		return throw_error($txt['install_no_mbstring']);
974
975
	// Check for https stream support.
976
	$supported_streams = stream_get_wrappers();
977
	if (!in_array('https', $supported_streams))
978
		$upcontext['custom_warning'] = $txt['install_no_https'];
979
980
	// Either we're logged in or we're going to present the login.
981
	if (checkLogin())
982
		return true;
983
984
	$upcontext += createToken('login');
985
986
	return false;
987
}
988
989
// Step 0.5: Does the login work?
990
function checkLogin()
991
{
992
	global $modSettings, $upcontext, $disable_security;
993
	global $smcFunc, $db_type, $support_js, $sourcedir, $txt;
994
995
	// Are we trying to login?
996
	if (isset($_POST['contbutt']) && (!empty($_POST['user']) || $disable_security))
997
	{
998
		// If we've disabled security pick a suitable name!
999
		if (empty($_POST['user']))
1000
			$_POST['user'] = 'Administrator';
1001
1002
		// Before 2.0 these column names were different!
1003
		$oldDB = false;
1004
		if (empty($db_type) || $db_type == 'mysql')
1005
		{
1006
			$request = $smcFunc['db_query']('', '
1007
				SHOW COLUMNS
1008
				FROM {db_prefix}members
1009
				LIKE {string:member_name}',
1010
				array(
1011
					'member_name' => 'memberName',
1012
					'db_error_skip' => true,
1013
				)
1014
			);
1015
			if ($smcFunc['db_num_rows']($request) != 0)
1016
				$oldDB = true;
1017
			$smcFunc['db_free_result']($request);
1018
		}
1019
1020
		// Get what we believe to be their details.
1021
		if (!$disable_security)
1022
		{
1023
			if ($oldDB)
1024
				$request = $smcFunc['db_query']('', '
1025
					SELECT id_member, memberName AS member_name, passwd, id_group,
1026
						additionalGroups AS additional_groups, lngfile
1027
					FROM {db_prefix}members
1028
					WHERE memberName = {string:member_name}',
1029
					array(
1030
						'member_name' => $_POST['user'],
1031
						'db_error_skip' => true,
1032
					)
1033
				);
1034
			else
1035
				$request = $smcFunc['db_query']('', '
1036
					SELECT id_member, member_name, passwd, id_group, additional_groups, lngfile
1037
					FROM {db_prefix}members
1038
					WHERE member_name = {string:member_name}',
1039
					array(
1040
						'member_name' => $_POST['user'],
1041
						'db_error_skip' => true,
1042
					)
1043
				);
1044
			if ($smcFunc['db_num_rows']($request) != 0)
1045
			{
1046
				list ($id_member, $name, $password, $id_group, $addGroups, $user_language) = $smcFunc['db_fetch_row']($request);
1047
1048
				$groups = explode(',', $addGroups);
1049
				$groups[] = $id_group;
1050
1051
				foreach ($groups as $k => $v)
1052
					$groups[$k] = (int) $v;
1053
1054
				$sha_passwd = sha1(strtolower($name) . un_htmlspecialchars($_REQUEST['passwrd']));
1055
1056
				// We don't use "-utf8" anymore...
1057
				$user_language = str_ireplace('-utf8', '', $user_language);
1058
			}
1059
			else
1060
				$upcontext['username_incorrect'] = true;
1061
1062
			$smcFunc['db_free_result']($request);
1063
		}
1064
		$upcontext['username'] = $_POST['user'];
1065
1066
		// Track whether javascript works!
1067
		if (isset($_POST['js_works']))
1068
		{
1069
			if (!empty($_POST['js_works']))
1070
			{
1071
				$upcontext['upgrade_status']['js'] = 1;
1072
				$support_js = 1;
1073
			}
1074
			else
1075
				$support_js = 0;
1076
		}
1077
1078
		// Note down the version we are coming from.
1079
		if (!empty($modSettings['smfVersion']) && empty($upcontext['user']['version']))
1080
			$upcontext['user']['version'] = $modSettings['smfVersion'];
1081
1082
		// Didn't get anywhere?
1083
		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']))
1084
		{
1085
			// MD5?
1086
			$md5pass = md5_hmac($_REQUEST['passwrd'], strtolower($_POST['user']));
1087
			if ($md5pass != $password)
1088
			{
1089
				$upcontext['password_failed'] = true;
1090
				// Disable the hashing this time.
1091
				$upcontext['disable_login_hashing'] = true;
1092
			}
1093
		}
1094
1095
		if ((empty($upcontext['password_failed']) && !empty($name)) || $disable_security)
1096
		{
1097
			// Set the password.
1098
			if (!$disable_security)
1099
			{
1100
				// Do we actually have permission?
1101
				if (!in_array(1, $groups))
1102
				{
1103
					$request = $smcFunc['db_query']('', '
1104
						SELECT permission
1105
						FROM {db_prefix}permissions
1106
						WHERE id_group IN ({array_int:groups})
1107
							AND permission = {string:admin_forum}',
1108
						array(
1109
							'groups' => $groups,
1110
							'admin_forum' => 'admin_forum',
1111
							'db_error_skip' => true,
1112
						)
1113
					);
1114
					if ($smcFunc['db_num_rows']($request) == 0)
1115
						return throw_error($txt['error_not_admin']);
1116
					$smcFunc['db_free_result']($request);
1117
				}
1118
1119
				$upcontext['user']['id'] = $id_member;
1120
				$upcontext['user']['name'] = $name;
1121
			}
1122
			else
1123
			{
1124
				$upcontext['user']['id'] = 1;
1125
				$upcontext['user']['name'] = 'Administrator';
1126
			}
1127
1128
			if (!is_callable('random_int'))
1129
				require_once('Sources/random_compat/random.php');
1130
1131
			$upcontext['user']['pass'] = random_int(0, 60000);
1132
			// This basically is used to match the GET variables to Settings.php.
1133
			$upcontext['upgrade_status']['pass'] = $upcontext['user']['pass'];
1134
1135
			// Set the language to that of the user?
1136
			if (isset($user_language) && $user_language != $upcontext['language'] && file_exists($modSettings['theme_dir'] . '/languages/index.' . basename($user_language, '.lng') . '.php'))
1137
			{
1138
				$user_language = basename($user_language, '.lng');
1139
				$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $user_language . '.php')), 0, 4096);
0 ignored issues
show
It seems like @file($modSettings['them...user_language . '.php') can also be of type false; however, parameter $pieces of implode() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

1139
				$temp = substr(@implode('', /** @scrutinizer ignore-type */ @file($modSettings['theme_dir'] . '/languages/index.' . $user_language . '.php')), 0, 4096);
Loading history...
1140
				preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
1141
1142
				if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
1143
					$upcontext['upgrade_options_warning'] = sprintf($txt['warning_lang_old'], $user_language, $upcontext['language']);
1144
				elseif (!file_exists($modSettings['theme_dir'] . '/languages/Install.' . $user_language . '.php'))
1145
					$upcontext['upgrade_options_warning'] = sprintf($txt['warning_lang_missing'], $user_language, $upcontext['language']);
1146
				else
1147
				{
1148
					// Set this as the new language.
1149
					$upcontext['language'] = $user_language;
1150
					$upcontext['upgrade_status']['lang'] = $upcontext['language'];
1151
1152
					// Include the file.
1153
					load_lang_file();
1154
				}
1155
			}
1156
1157
			// If we're resuming set the step and substep to be correct.
1158
			if (isset($_POST['cont']))
1159
			{
1160
				$upcontext['current_step'] = $upcontext['user']['step'];
1161
				$_GET['substep'] = $upcontext['user']['substep'];
1162
			}
1163
1164
			return true;
1165
		}
1166
	}
1167
1168
	return false;
1169
}
1170
1171
// Step 1: Do the maintenance and backup.
1172
function UpgradeOptions()
1173
{
1174
	global $db_prefix, $command_line, $modSettings, $is_debug, $smcFunc, $packagesdir, $tasksdir, $language, $txt, $db_port;
1175
	global $boarddir, $boardurl, $sourcedir, $maintenance, $cachedir, $upcontext, $db_type, $db_server, $image_proxy_enabled;
1176
1177
	$upcontext['sub_template'] = 'upgrade_options';
1178
	$upcontext['page_title'] = $txt['upgrade_options'];
1179
1180
	db_extend('packages');
1181
	$upcontext['karma_installed'] = array('good' => false, 'bad' => false);
1182
	$member_columns = $smcFunc['db_list_columns']('{db_prefix}members');
1183
1184
	$upcontext['karma_installed']['good'] = in_array('karma_good', $member_columns);
1185
	$upcontext['karma_installed']['bad'] = in_array('karma_bad', $member_columns);
1186
1187
	$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', '<');
1188
1189
	unset($member_columns);
1190
1191
	// If we've not submitted then we're done.
1192
	if (empty($_POST['upcont']))
1193
		return false;
1194
1195
	// Firstly, if they're enabling SM stat collection just do it.
1196
	if (!empty($_POST['stats']) && substr($boardurl, 0, 16) != 'http://localhost' && empty($modSettings['allow_sm_stats']) && empty($modSettings['enable_sm_stats']))
1197
	{
1198
		$upcontext['allow_sm_stats'] = true;
1199
1200
		// Don't register if we still have a key.
1201
		if (empty($modSettings['sm_stats_key']))
1202
		{
1203
			// Attempt to register the site etc.
1204
			$fp = @fsockopen('www.simplemachines.org', 80, $errno, $errstr);
1205
			if ($fp)
1206
			{
1207
				$out = 'GET /smf/stats/register_stats.php?site=' . base64_encode($boardurl) . ' HTTP/1.1' . "\r\n";
1208
				$out .= 'Host: www.simplemachines.org' . "\r\n";
1209
				$out .= 'Connection: Close' . "\r\n\r\n";
1210
				fwrite($fp, $out);
1211
1212
				$return_data = '';
1213
				while (!feof($fp))
1214
					$return_data .= fgets($fp, 128);
1215
1216
				fclose($fp);
1217
1218
				// Get the unique site ID.
1219
				preg_match('~SITE-ID:\s(\w{10})~', $return_data, $ID);
1220
1221
				if (!empty($ID[1]))
1222
					$smcFunc['db_insert']('replace',
1223
						$db_prefix . 'settings',
1224
						array('variable' => 'string', 'value' => 'string'),
1225
						array(
1226
							array('sm_stats_key', $ID[1]),
1227
							array('enable_sm_stats', 1),
1228
						),
1229
						array('variable')
1230
					);
1231
			}
1232
		}
1233
		else
1234
		{
1235
			$smcFunc['db_insert']('replace',
1236
				$db_prefix . 'settings',
1237
				array('variable' => 'string', 'value' => 'string'),
1238
				array('enable_sm_stats', 1),
1239
				array('variable')
1240
			);
1241
		}
1242
	}
1243
	// Don't remove stat collection unless we unchecked the box for real, not from the loop.
1244
	elseif (empty($_POST['stats']) && empty($upcontext['allow_sm_stats']))
1245
		$smcFunc['db_query']('', '
1246
			DELETE FROM {db_prefix}settings
1247
			WHERE variable = {string:enable_sm_stats}',
1248
			array(
1249
				'enable_sm_stats' => 'enable_sm_stats',
1250
				'db_error_skip' => true,
1251
			)
1252
		);
1253
1254
	// Deleting old karma stuff?
1255
	if (!empty($_POST['delete_karma']))
1256
	{
1257
		// Delete old settings vars.
1258
		$smcFunc['db_query']('', '
1259
			DELETE FROM {db_prefix}settings
1260
			WHERE variable IN ({array_string:karma_vars})',
1261
			array(
1262
				'karma_vars' => array('karmaMode', 'karmaTimeRestrictAdmins', 'karmaWaitTime', 'karmaMinPosts', 'karmaLabel', 'karmaSmiteLabel', 'karmaApplaudLabel'),
1263
			)
1264
		);
1265
1266
		// Cleaning up old karma member settings.
1267
		if ($upcontext['karma_installed']['good'])
1268
			$smcFunc['db_query']('', '
1269
				ALTER TABLE {db_prefix}members
1270
				DROP karma_good',
1271
				array()
1272
			);
1273
1274
		// Does karma bad was enable?
1275
		if ($upcontext['karma_installed']['bad'])
1276
			$smcFunc['db_query']('', '
1277
				ALTER TABLE {db_prefix}members
1278
				DROP karma_bad',
1279
				array()
1280
			);
1281
1282
		// Cleaning up old karma permissions.
1283
		$smcFunc['db_query']('', '
1284
			DELETE FROM {db_prefix}permissions
1285
			WHERE permission = {string:karma_vars}',
1286
			array(
1287
				'karma_vars' => 'karma_edit',
1288
			)
1289
		);
1290
		// Cleaning up old log_karma table
1291
		$smcFunc['db_query']('', '
1292
			DROP TABLE IF EXISTS {db_prefix}log_karma',
1293
			array()
1294
		);
1295
	}
1296
1297
	// Emptying the error log?
1298
	if (!empty($_POST['empty_error']))
1299
		$smcFunc['db_query']('truncate_table', '
1300
			TRUNCATE {db_prefix}log_errors',
1301
			array(
1302
			)
1303
		);
1304
1305
	$changes = array();
1306
1307
	// Add proxy settings.
1308
	if (!isset($GLOBALS['image_proxy_secret']) || $GLOBALS['image_proxy_secret'] == 'smfisawesome')
1309
		$changes['image_proxy_secret'] = substr(sha1(mt_rand()), 0, 20);
1310
	if (!isset($GLOBALS['image_proxy_maxsize']))
1311
		$changes['image_proxy_maxsize'] = 5190;
1312
	if (!isset($GLOBALS['image_proxy_enabled']))
1313
		$changes['image_proxy_enabled'] = false;
1314
1315
	// If $boardurl reflects https, set force_ssl
1316
	if (!function_exists('cache_put_data'))
1317
		require_once($sourcedir . '/Load.php');
1318
	if (stripos($boardurl, 'https://') !== false)
1319
		updateSettings(array('force_ssl' => '1'));
1320
1321
	// If we're overriding the language follow it through.
1322
	if (isset($upcontext['lang']) && file_exists($modSettings['theme_dir'] . '/languages/index.' . $upcontext['lang'] . '.php'))
1323
		$changes['language'] = $upcontext['lang'];
1324
1325
	if (!empty($_POST['maint']))
1326
	{
1327
		$changes['maintenance'] = 2;
1328
		// Remember what it was...
1329
		$upcontext['user']['main'] = $maintenance;
1330
1331
		if (!empty($_POST['maintitle']))
1332
		{
1333
			$changes['mtitle'] = $_POST['maintitle'];
1334
			$changes['mmessage'] = $_POST['mainmessage'];
1335
		}
1336
		else
1337
		{
1338
			$changes['mtitle'] = $txt['mtitle'];
1339
			$changes['mmessage'] = $txt['mmessage'];
1340
		}
1341
	}
1342
1343
	if ($command_line)
1344
		echo ' * Updating Settings.php...';
1345
1346
	// Fix some old paths.
1347
	if (substr($boarddir, 0, 1) == '.')
1348
		$changes['boarddir'] = fixRelativePath($boarddir);
1349
1350
	if (substr($sourcedir, 0, 1) == '.')
1351
		$changes['sourcedir'] = fixRelativePath($sourcedir);
1352
1353
	if (empty($cachedir) || substr($cachedir, 0, 1) == '.')
1354
		$changes['cachedir'] = fixRelativePath($boarddir) . '/cache';
1355
1356
	// Migrate cache settings.
1357
	// Accelerator setting didn't exist previously; use 'smf' file based caching as default if caching had been enabled.
1358
	if (!isset($GLOBALS['cache_enable']))
1359
		$changes += array(
1360
			'cache_accelerator' => !empty($modSettings['cache_enable']) ? 'smf' : '',
1361
			'cache_enable' => !empty($modSettings['cache_enable']) ? $modSettings['cache_enable'] : 0,
1362
			'cache_memcached' => !empty($modSettings['cache_memcached']) ? $modSettings['cache_memcached'] : '',
1363
		);
1364
1365
	// If they have a "host:port" setup for the host, split that into separate values
1366
	// You should never have a : in the hostname if you're not on MySQL, but better safe than sorry
1367
	if (strpos($db_server, ':') !== false && $db_type == 'mysql')
1368
	{
1369
		list ($db_server, $db_port) = explode(':', $db_server);
1370
1371
		$changes['db_server'] = $db_server;
1372
1373
		// Only set this if we're not using the default port
1374
		if ($db_port != ini_get('mysqli.default_port'))
1375
			$changes['db_port'] = (int) $db_port;
1376
	}
1377
1378
	// If db_port is set and is the same as the default, set it to 0.
1379
	if (!empty($db_port))
1380
	{
1381
		if ($db_type == 'mysql' && $db_port == ini_get('mysqli.default_port'))
1382
			$changes['db_port'] = 0;
1383
		elseif ($db_type == 'postgresql' && $db_port == 5432)
1384
			$changes['db_port'] = 0;
1385
	}
1386
1387
	// Maybe we haven't had this option yet?
1388
	if (empty($packagesdir))
1389
		$changes['packagesdir'] = fixRelativePath($boarddir) . '/Packages';
1390
1391
	// Add support for $tasksdir var.
1392
	if (empty($tasksdir))
1393
		$changes['tasksdir'] = fixRelativePath($sourcedir) . '/tasks';
1394
1395
	// Make sure we fix the language as well.
1396
	if (stristr($language, '-utf8'))
1397
		$changes['language'] = str_ireplace('-utf8', '', $language);
1398
1399
	// @todo Maybe change the cookie name if going to 1.1, too?
1400
1401
	// Ensure this doesn't get lost in translation.
1402
	$changes['upgradeData'] = base64_encode(json_encode($upcontext['user']));
1403
1404
	// Update Settings.php with the new settings, and rebuild if they selected that option.
1405
	require_once($sourcedir . '/Subs.php');
1406
	require_once($sourcedir . '/Subs-Admin.php');
1407
	updateSettingsFile($changes, false, !empty($_POST['migrateSettings']));
1408
1409
	if ($command_line)
1410
		echo ' Successful.' . "\n";
1411
1412
	// Are we doing debug?
1413
	if (isset($_POST['debug']))
1414
	{
1415
		$upcontext['upgrade_status']['debug'] = true;
1416
		$is_debug = true;
1417
	}
1418
1419
	// If we're not backing up then jump one.
1420
	if (empty($_POST['backup']))
1421
		$upcontext['current_step']++;
1422
1423
	// If we've got here then let's proceed to the next step!
1424
	return true;
1425
}
1426
1427
// Backup the database - why not...
1428
function BackupDatabase()
1429
{
1430
	global $upcontext, $db_prefix, $command_line, $support_js, $file_steps, $smcFunc, $txt;
1431
1432
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'backup_xml' : 'backup_database';
1433
	$upcontext['page_title'] = $txt['backup_database'];
1434
1435
	// Done it already - js wise?
1436
	if (!empty($_POST['backup_done']))
1437
		return true;
1438
1439
	// Some useful stuff here.
1440
	db_extend();
1441
1442
	// Might need this as well
1443
	db_extend('packages');
1444
1445
	// Get all the table names.
1446
	$filter = str_replace('_', '\_', preg_match('~^`(.+?)`\.(.+?)$~', $db_prefix, $match) != 0 ? $match[2] : $db_prefix) . '%';
1447
	$db = preg_match('~^`(.+?)`\.(.+?)$~', $db_prefix, $match) != 0 ? strtr($match[1], array('`' => '')) : false;
1448
	$tables = $smcFunc['db_list_tables']($db, $filter);
1449
1450
	$table_names = array();
1451
	foreach ($tables as $table)
1452
		if (substr($table, 0, 7) !== 'backup_')
1453
			$table_names[] = $table;
1454
1455
	$upcontext['table_count'] = count($table_names);
1456
	$upcontext['cur_table_num'] = $_GET['substep'];
1457
	$upcontext['cur_table_name'] = str_replace($db_prefix, '', isset($table_names[$_GET['substep']]) ? $table_names[$_GET['substep']] : $table_names[0]);
1458
	$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
1459
	// For non-java auto submit...
1460
	$file_steps = $upcontext['table_count'];
1461
1462
	// What ones have we already done?
1463
	foreach ($table_names as $id => $table)
1464
		if ($id < $_GET['substep'])
1465
			$upcontext['previous_tables'][] = $table;
1466
1467
	if ($command_line)
1468
		echo 'Backing Up Tables.';
1469
1470
	// If we don't support javascript we backup here.
1471
	if (!$support_js || isset($_GET['xml']))
1472
	{
1473
		// Backup each table!
1474
		for ($substep = $_GET['substep'], $n = count($table_names); $substep < $n; $substep++)
1475
		{
1476
			$upcontext['cur_table_name'] = str_replace($db_prefix, '', (isset($table_names[$substep + 1]) ? $table_names[$substep + 1] : $table_names[$substep]));
1477
			$upcontext['cur_table_num'] = $substep + 1;
1478
1479
			$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
1480
1481
			// Do we need to pause?
1482
			nextSubstep($substep);
1483
1484
			backupTable($table_names[$substep]);
1485
1486
			// If this is XML to keep it nice for the user do one table at a time anyway!
1487
			if (isset($_GET['xml']))
1488
				return upgradeExit();
0 ignored issues
show
Are you sure the usage of upgradeExit() is correct as it seems to always return null.

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

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

}

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

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

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

Loading history...
1489
		}
1490
1491
		if ($command_line)
1492
		{
1493
			echo "\n" . ' Successful.\'' . "\n";
1494
			flush();
1495
		}
1496
		$upcontext['step_progress'] = 100;
1497
1498
		$_GET['substep'] = 0;
1499
		// Make sure we move on!
1500
		return true;
1501
	}
1502
1503
	// Either way next place to post will be database changes!
1504
	$_GET['substep'] = 0;
1505
	return false;
1506
}
1507
1508
// Backup one table...
1509
function backupTable($table)
1510
{
1511
	global $command_line, $db_prefix, $smcFunc;
1512
1513
	if ($command_line)
1514
	{
1515
		echo "\n" . ' +++ Backing up \"' . str_replace($db_prefix, '', $table) . '"...';
1516
		flush();
1517
	}
1518
1519
	$smcFunc['db_backup_table']($table, 'backup_' . $table);
1520
1521
	if ($command_line)
1522
		echo ' done.';
1523
}
1524
1525
// Step 2: Everything.
1526
function DatabaseChanges()
1527
{
1528
	global $db_prefix, $modSettings, $smcFunc, $txt;
1529
	global $upcontext, $support_js, $db_type, $boarddir;
1530
1531
	// Have we just completed this?
1532
	if (!empty($_POST['database_done']))
1533
		return true;
1534
1535
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'database_xml' : 'database_changes';
1536
	$upcontext['page_title'] = $txt['database_changes'];
1537
1538
	// All possible files.
1539
	// Name, < version, insert_on_complete
1540
	// Last entry in array indicates whether to use sql_mode of STRICT or not.
1541
	$files = array(
1542
		array('upgrade_1-0.sql', '1.1', '1.1 RC0', false),
1543
		array('upgrade_1-1.sql', '2.0', '2.0 a', false),
1544
		array('upgrade_2-0_' . $db_type . '.sql', '2.1', '2.1 dev0', false),
1545
		array('upgrade_2-1_' . $db_type . '.sql', '3.0', SMF_VERSION, true),
1546
	);
1547
1548
	// How many files are there in total?
1549
	if (isset($_GET['filecount']))
1550
		$upcontext['file_count'] = (int) $_GET['filecount'];
1551
	else
1552
	{
1553
		$upcontext['file_count'] = 0;
1554
		foreach ($files as $file)
1555
		{
1556
			if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < $file[1])
1557
				$upcontext['file_count']++;
1558
		}
1559
	}
1560
1561
	// Do each file!
1562
	$did_not_do = count($files) - $upcontext['file_count'];
1563
	$upcontext['step_progress'] = 0;
1564
	$upcontext['cur_file_num'] = 0;
1565
	foreach ($files as $file)
1566
	{
1567
		if ($did_not_do)
1568
			$did_not_do--;
1569
		else
1570
		{
1571
			$upcontext['cur_file_num']++;
1572
			$upcontext['cur_file_name'] = $file[0];
1573
			// Do we actually need to do this still?
1574
			if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < $file[1])
1575
			{
1576
				// Use STRICT mode on more recent steps
1577
				setSqlMode($file[3]);
1578
1579
				// Reload modSettings to capture any adds/updates made along the way
1580
				$request = $smcFunc['db_query']('', '
1581
					SELECT variable, value
1582
					FROM {db_prefix}settings',
1583
					array(
1584
						'db_error_skip' => true,
1585
					)
1586
				);
1587
1588
				$modSettings = array();
1589
				while ($row = $smcFunc['db_fetch_assoc']($request))
1590
					$modSettings[$row['variable']] = $row['value'];
1591
1592
				$smcFunc['db_free_result']($request);
1593
1594
				// Some theme settings are in $modSettings
1595
				// Note we still might be doing yabbse (no smf ver)
1596
				if (isset($modSettings['smfVersion']))
1597
				{
1598
					$request = $smcFunc['db_query']('', '
1599
						SELECT variable, value
1600
						FROM {db_prefix}themes
1601
						WHERE id_theme = {int:id_theme}
1602
							AND variable IN ({string:theme_url}, {string:theme_dir}, {string:images_url})',
1603
						array(
1604
							'id_theme' => 1,
1605
							'theme_url' => 'theme_url',
1606
							'theme_dir' => 'theme_dir',
1607
							'images_url' => 'images_url',
1608
							'db_error_skip' => true,
1609
						)
1610
					);
1611
1612
					while ($row = $smcFunc['db_fetch_assoc']($request))
1613
						$modSettings[$row['variable']] = $row['value'];
1614
1615
					$smcFunc['db_free_result']($request);
1616
				}
1617
1618
				if (!isset($modSettings['theme_url']))
1619
				{
1620
					$modSettings['theme_dir'] = $boarddir . '/Themes/default';
1621
					$modSettings['theme_url'] = 'Themes/default';
1622
					$modSettings['images_url'] = 'Themes/default/images';
1623
				}
1624
1625
				// Now process the file...
1626
				$nextFile = parse_sql(dirname(__FILE__) . '/' . $file[0]);
1627
				if ($nextFile)
1628
				{
1629
					// Only update the version of this if complete.
1630
					$smcFunc['db_insert']('replace',
1631
						$db_prefix . 'settings',
1632
						array('variable' => 'string', 'value' => 'string'),
1633
						array('smfVersion', $file[2]),
1634
						array('variable')
1635
					);
1636
1637
					$modSettings['smfVersion'] = $file[2];
1638
				}
1639
1640
				// If this is XML we only do this stuff once.
1641
				if (isset($_GET['xml']))
1642
				{
1643
					// Flag to move on to the next.
1644
					$upcontext['completed_step'] = true;
1645
					// Did we complete the whole file?
1646
					if ($nextFile)
1647
						$upcontext['current_debug_item_num'] = -1;
1648
					return upgradeExit();
0 ignored issues
show
Are you sure the usage of upgradeExit() is correct as it seems to always return null.

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

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

}

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

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

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

Loading history...
1649
				}
1650
				elseif ($support_js)
1651
					break;
1652
			}
1653
			// Set the progress bar to be right as if we had - even if we hadn't...
1654
			$upcontext['step_progress'] = ($upcontext['cur_file_num'] / $upcontext['file_count']) * 100;
1655
		}
1656
	}
1657
1658
	$_GET['substep'] = 0;
1659
	// So the template knows we're done.
1660
	if (!$support_js)
1661
	{
1662
		$upcontext['changes_complete'] = true;
1663
1664
		return true;
1665
	}
1666
	return false;
1667
}
1668
1669
// Different versions of the files use different sql_modes
1670
function setSqlMode($strict = true)
1671
{
1672
	global $db_type, $db_connection;
1673
1674
	if ($db_type != 'mysql')
1675
		return;
1676
1677
	if ($strict)
1678
		$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';
1679
	else
1680
		$mode = '';
1681
1682
	mysqli_query($db_connection, 'SET SESSION sql_mode = \'' . $mode . '\'');
1683
1684
	return;
1685
}
1686
1687
// Delete the damn thing!
1688
function DeleteUpgrade()
1689
{
1690
	global $command_line, $language, $upcontext, $sourcedir;
1691
	global $user_info, $maintenance, $smcFunc, $db_type, $txt, $settings;
1692
1693
	// Now it's nice to have some of the basic SMF source files.
1694
	if (!isset($_GET['ssi']) && !$command_line)
1695
		redirectLocation('&ssi=1');
1696
1697
	$upcontext['sub_template'] = 'upgrade_complete';
1698
	$upcontext['page_title'] = $txt['upgrade_complete'];
1699
1700
	$endl = $command_line ? "\n" : '<br>' . "\n";
1701
1702
	$changes = array(
1703
		'language' => (substr($language, -4) == '.lng' ? substr($language, 0, -4) : $language),
1704
		'db_error_send' => true,
1705
		'upgradeData' => null,
1706
	);
1707
1708
	// Are we in maintenance mode?
1709
	if (isset($upcontext['user']['main']))
1710
	{
1711
		if ($command_line)
1712
			echo ' * ';
1713
		$upcontext['removed_maintenance'] = true;
1714
		$changes['maintenance'] = $upcontext['user']['main'];
1715
	}
1716
	// Otherwise if somehow we are in 2 let's go to 1.
1717
	elseif (!empty($maintenance) && $maintenance == 2)
1718
		$changes['maintenance'] = 1;
1719
1720
	// Wipe this out...
1721
	$upcontext['user'] = array();
1722
1723
	require_once($sourcedir . '/Subs.php');
1724
	require_once($sourcedir . '/Subs-Admin.php');
1725
	updateSettingsFile($changes);
1726
1727
	// Clean any old cache files away.
1728
	upgrade_clean_cache();
1729
1730
	// Can we delete the file?
1731
	$upcontext['can_delete_script'] = is_writable(dirname(__FILE__)) || is_writable(__FILE__);
1732
1733
	// Now is the perfect time to fetch the SM files.
1734
	if ($command_line)
1735
		cli_scheduled_fetchSMfiles();
1736
	else
1737
	{
1738
		require_once($sourcedir . '/ScheduledTasks.php');
1739
		scheduled_fetchSMfiles(); // Now go get those files!
1740
		// This is needed in case someone invokes the upgrader using https when upgrading an http forum
1741
		if (httpsOn())
1742
			$settings['default_theme_url'] = strtr($settings['default_theme_url'], array('http://' => 'https://'));
1743
	}
1744
1745
	// Log what we've done.
1746
	if (empty($user_info['id']))
1747
		$user_info['id'] = !empty($upcontext['user']['id']) ? $upcontext['user']['id'] : 0;
1748
1749
	// Log the action manually, so CLI still works.
1750
	$smcFunc['db_insert']('',
1751
		'{db_prefix}log_actions',
1752
		array(
1753
			'log_time' => 'int', 'id_log' => 'int', 'id_member' => 'int', 'ip' => 'inet', 'action' => 'string',
1754
			'id_board' => 'int', 'id_topic' => 'int', 'id_msg' => 'int', 'extra' => 'string-65534',
1755
		),
1756
		array(
1757
			time(), 3, $user_info['id'], $command_line ? '127.0.0.1' : $user_info['ip'], 'upgrade',
1758
			0, 0, 0, json_encode(array('version' => SMF_FULL_VERSION, 'member' => $user_info['id'])),
1759
		),
1760
		array('id_action')
1761
	);
1762
	$user_info['id'] = 0;
1763
1764
	if ($command_line)
1765
	{
1766
		echo $endl;
1767
		echo 'Upgrade Complete!', $endl;
1768
		echo 'Please delete this file as soon as possible for security reasons.', $endl;
1769
		exit;
1770
	}
1771
1772
	// Make sure it says we're done.
1773
	$upcontext['overall_percent'] = 100;
1774
	if (isset($upcontext['step_progress']))
1775
		unset($upcontext['step_progress']);
1776
1777
	$_GET['substep'] = 0;
1778
	return false;
1779
}
1780
1781
// Just like the built in one, but setup for CLI to not use themes.
1782
function cli_scheduled_fetchSMfiles()
1783
{
1784
	global $sourcedir, $language, $modSettings, $smcFunc;
1785
1786
	if (empty($modSettings['time_format']))
1787
		$modSettings['time_format'] = '%B %d, %Y, %I:%M:%S %p';
1788
1789
	// What files do we want to get
1790
	$request = $smcFunc['db_query']('', '
1791
		SELECT id_file, filename, path, parameters
1792
		FROM {db_prefix}admin_info_files',
1793
		array(
1794
		)
1795
	);
1796
1797
	$js_files = array();
1798
	while ($row = $smcFunc['db_fetch_assoc']($request))
1799
	{
1800
		$js_files[$row['id_file']] = array(
1801
			'filename' => $row['filename'],
1802
			'path' => $row['path'],
1803
			'parameters' => sprintf($row['parameters'], $language, urlencode($modSettings['time_format']), urlencode(SMF_FULL_VERSION)),
1804
		);
1805
	}
1806
	$smcFunc['db_free_result']($request);
1807
1808
	// We're gonna need fetch_web_data() to pull this off.
1809
	require_once($sourcedir . '/Subs.php');
1810
1811
	foreach ($js_files as $ID_FILE => $file)
1812
	{
1813
		// Create the url
1814
		$server = empty($file['path']) || substr($file['path'], 0, 7) != 'http://' ? 'https://www.simplemachines.org' : '';
1815
		$url = $server . (!empty($file['path']) ? $file['path'] : $file['path']) . $file['filename'] . (!empty($file['parameters']) ? '?' . $file['parameters'] : '');
1816
1817
		// Get the file
1818
		$file_data = fetch_web_data($url);
1819
1820
		// If we got an error - give up - the site might be down.
1821
		if ($file_data === false)
1822
			return throw_error(sprintf('Could not retrieve the file %1$s.', $url));
1823
1824
		// Save the file to the database.
1825
		$smcFunc['db_query']('substring', '
1826
			UPDATE {db_prefix}admin_info_files
1827
			SET data = SUBSTRING({string:file_data}, 1, 65534)
1828
			WHERE id_file = {int:id_file}',
1829
			array(
1830
				'id_file' => $ID_FILE,
1831
				'file_data' => $file_data,
1832
			)
1833
		);
1834
	}
1835
	return true;
1836
}
1837
1838
function convertSettingsToTheme()
1839
{
1840
	global $db_prefix, $modSettings, $smcFunc;
1841
1842
	$values = array(
1843
		'show_latest_member' => @$GLOBALS['showlatestmember'],
1844
		'show_bbc' => isset($GLOBALS['showyabbcbutt']) ? $GLOBALS['showyabbcbutt'] : @$GLOBALS['showbbcbutt'],
1845
		'show_modify' => @$GLOBALS['showmodify'],
1846
		'show_user_images' => @$GLOBALS['showuserpic'],
1847
		'show_blurb' => @$GLOBALS['showusertext'],
1848
		'show_gender' => @$GLOBALS['showgenderimage'],
1849
		'show_newsfader' => @$GLOBALS['shownewsfader'],
1850
		'display_recent_bar' => @$GLOBALS['Show_RecentBar'],
1851
		'show_member_bar' => @$GLOBALS['Show_MemberBar'],
1852
		'linktree_link' => @$GLOBALS['curposlinks'],
1853
		'show_profile_buttons' => @$GLOBALS['profilebutton'],
1854
		'show_mark_read' => @$GLOBALS['showmarkread'],
1855
		'show_board_desc' => @$GLOBALS['ShowBDescrip'],
1856
		'newsfader_time' => @$GLOBALS['fadertime'],
1857
		'use_image_buttons' => empty($GLOBALS['MenuType']) ? 1 : 0,
1858
		'enable_news' => @$GLOBALS['enable_news'],
1859
		'linktree_inline' => @$modSettings['enableInlineLinks'],
1860
		'return_to_post' => @$modSettings['returnToPost'],
1861
	);
1862
1863
	$themeData = array();
1864
	foreach ($values as $variable => $value)
1865
	{
1866
		if (!isset($value) || $value === null)
1867
			$value = 0;
1868
1869
		$themeData[] = array(0, 1, $variable, $value);
1870
	}
1871
	if (!empty($themeData))
1872
	{
1873
		$smcFunc['db_insert']('ignore',
1874
			$db_prefix . 'themes',
1875
			array('id_member' => 'int', 'id_theme' => 'int', 'variable' => 'string', 'value' => 'string'),
1876
			$themeData,
1877
			array('id_member', 'id_theme', 'variable')
1878
		);
1879
	}
1880
}
1881
1882
// This function only works with MySQL but that's fine as it is only used for v1.0.
1883
function convertSettingstoOptions()
1884
{
1885
	global $modSettings, $smcFunc;
1886
1887
	// Format: new_setting -> old_setting_name.
1888
	$values = array(
1889
		'calendar_start_day' => 'cal_startmonday',
1890
		'view_newest_first' => 'viewNewestFirst',
1891
		'view_newest_pm_first' => 'viewNewestFirst',
1892
	);
1893
1894
	foreach ($values as $variable => $value)
1895
	{
1896
		if (empty($modSettings[$value[0]]))
1897
			continue;
1898
1899
		$smcFunc['db_query']('', '
1900
			INSERT IGNORE INTO {db_prefix}themes
1901
				(id_member, id_theme, variable, value)
1902
			SELECT id_member, 1, {string:variable}, {string:value}
1903
			FROM {db_prefix}members',
1904
			array(
1905
				'variable' => $variable,
1906
				'value' => $modSettings[$value[0]],
1907
				'db_error_skip' => true,
1908
			)
1909
		);
1910
1911
		$smcFunc['db_query']('', '
1912
			INSERT IGNORE INTO {db_prefix}themes
1913
				(id_member, id_theme, variable, value)
1914
			VALUES (-1, 1, {string:variable}, {string:value})',
1915
			array(
1916
				'variable' => $variable,
1917
				'value' => $modSettings[$value[0]],
1918
				'db_error_skip' => true,
1919
			)
1920
		);
1921
	}
1922
}
1923
1924
function php_version_check()
1925
{
1926
	return version_compare(PHP_VERSION, $GLOBALS['required_php_version'], '>=');
1927
}
1928
1929
function db_version_check()
1930
{
1931
	global $db_type, $databases;
1932
1933
	$curver = eval($databases[$db_type]['version_check']);
1934
	$curver = preg_replace('~\-.+?$~', '', $curver);
1935
1936
	return version_compare($databases[$db_type]['version'], $curver, '<=');
1937
}
1938
1939
function fixRelativePath($path)
1940
{
1941
	global $install_path;
1942
1943
	// Fix the . at the start, clear any duplicate slashes, and fix any trailing slash...
1944
	return addslashes(preg_replace(array('~^\.([/\\\]|$)~', '~[/]+~', '~[\\\]+~', '~[/\\\]$~'), array($install_path . '$1', '/', '\\', ''), $path));
1945
}
1946
1947
function parse_sql($filename)
1948
{
1949
	global $db_prefix, $db_collation, $boarddir, $boardurl, $command_line, $file_steps, $step_progress, $custom_warning;
1950
	global $upcontext, $support_js, $is_debug, $db_type, $db_character_set, $smcFunc;
1951
1952
/*
1953
	Failure allowed on:
1954
		- INSERT INTO but not INSERT IGNORE INTO.
1955
		- UPDATE IGNORE but not UPDATE.
1956
		- ALTER TABLE and ALTER IGNORE TABLE.
1957
		- DROP TABLE.
1958
	Yes, I realize that this is a bit confusing... maybe it should be done differently?
1959
1960
	If a comment...
1961
		- begins with --- it is to be output, with a break only in debug mode. (and say successful\n\n if there was one before.)
1962
		- begins with ---# it is a debugging statement, no break - only shown at all in debug.
1963
		- is only ---#, it is "done." and then a break - only shown in debug.
1964
		- begins with ---{ it is a code block terminating at ---}.
1965
1966
	Every block of between "--- ..."s is a step.  Every "---#" section represents a substep.
1967
1968
	Replaces the following variables:
1969
		- {$boarddir}
1970
		- {$boardurl}
1971
		- {$db_prefix}
1972
		- {$db_collation}
1973
*/
1974
1975
	// May want to use extended functionality.
1976
	db_extend();
1977
	db_extend('packages');
1978
1979
	// Our custom error handler - does nothing but does stop public errors from XML!
1980
	// Note that php error suppression - @ - used heavily in the upgrader, calls the error handler
1981
	// but error_reporting() will return 0 as it does so.
1982
	set_error_handler(
1983
		function($errno, $errstr, $errfile, $errline) use ($support_js)
1984
		{
1985
			if ($support_js)
1986
				return true;
1987
			elseif (error_reporting() != 0)
1988
				echo 'Error: ' . $errstr . ' File: ' . $errfile . ' Line: ' . $errline;
1989
		}
1990
	);
1991
1992
	// If we're on MySQL, set {db_collation}; this approach is used throughout upgrade_2-0_mysql.php to set new tables to utf8
1993
	// Note it is expected to be in the format: ENGINE=MyISAM{$db_collation};
1994
	if ($db_type == 'mysql')
1995
		$db_collation = ' DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci';
1996
	else
1997
		$db_collation = '';
1998
1999
	$endl = $command_line ? "\n" : '<br>' . "\n";
2000
2001
	$lines = file($filename);
2002
2003
	$current_type = 'sql';
2004
	$current_data = '';
2005
	$substep = 0;
2006
	$last_step = '';
2007
2008
	// Make sure all newly created tables will have the proper characters set; this approach is used throughout upgrade_2-1_mysql.php
2009
	if (isset($db_character_set) && $db_character_set === 'utf8')
2010
		$lines = str_replace(') ENGINE=MyISAM;', ') ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;', $lines);
2011
2012
	// Count the total number of steps within this file - for progress.
2013
	$file_steps = substr_count(implode('', $lines), '---#');
0 ignored issues
show
It seems like $lines 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

2013
	$file_steps = substr_count(implode('', /** @scrutinizer ignore-type */ $lines), '---#');
Loading history...
2014
	$upcontext['total_items'] = substr_count(implode('', $lines), '--- ');
2015
	$upcontext['debug_items'] = $file_steps;
2016
	$upcontext['current_item_num'] = 0;
2017
	$upcontext['current_item_name'] = '';
2018
	$upcontext['current_debug_item_num'] = 0;
2019
	$upcontext['current_debug_item_name'] = '';
2020
	// This array keeps a record of what we've done in case java is dead...
2021
	$upcontext['actioned_items'] = array();
2022
2023
	$done_something = false;
2024
2025
	foreach ($lines as $line_number => $line)
2026
	{
2027
		$do_current = $substep >= $_GET['substep'];
2028
2029
		// Get rid of any comments in the beginning of the line...
2030
		if (substr(trim($line), 0, 2) === '/*')
2031
			$line = preg_replace('~/\*.+?\*/~', '', $line);
2032
2033
		// Always flush.  Flush, flush, flush.  Flush, flush, flush, flush!  FLUSH!
2034
		if ($is_debug && !$support_js && $command_line)
2035
			flush();
2036
2037
		if (trim($line) === '')
2038
			continue;
2039
2040
		if (trim(substr($line, 0, 3)) === '---')
2041
		{
2042
			$type = substr($line, 3, 1);
2043
2044
			// An error??
2045
			if (trim($current_data) != '' && $type !== '}')
2046
			{
2047
				$upcontext['error_message'] = 'Error in upgrade script - line ' . $line_number . '!' . $endl;
2048
				if ($command_line)
2049
					echo $upcontext['error_message'];
2050
			}
2051
2052
			if ($type == ' ')
2053
			{
2054
				if (!$support_js && $do_current && $_GET['substep'] != 0 && $command_line)
2055
				{
2056
					echo ' Successful.', $endl;
2057
					flush();
2058
				}
2059
2060
				$last_step = htmlspecialchars(rtrim(substr($line, 4)));
2061
				$upcontext['current_item_num']++;
2062
				$upcontext['current_item_name'] = $last_step;
2063
2064
				if ($do_current)
2065
				{
2066
					$upcontext['actioned_items'][] = $last_step;
2067
					if ($command_line)
2068
						echo ' * ';
2069
2070
					// Starting a new main step in our DB changes, so it's time to reset this.
2071
					$upcontext['skip_db_substeps'] = false;
2072
				}
2073
			}
2074
			elseif ($type == '#')
2075
			{
2076
				$upcontext['step_progress'] += (100 / $upcontext['file_count']) / $file_steps;
2077
2078
				$upcontext['current_debug_item_num']++;
2079
				if (trim($line) != '---#')
2080
					$upcontext['current_debug_item_name'] = htmlspecialchars(rtrim(substr($line, 4)));
2081
2082
				// Have we already done something?
2083
				if (isset($_GET['xml']) && $done_something)
2084
				{
2085
					restore_error_handler();
2086
					return $upcontext['current_debug_item_num'] >= $upcontext['debug_items'] ? true : false;
2087
				}
2088
2089
				if ($do_current)
2090
				{
2091
					if (trim($line) == '---#' && $command_line)
2092
						echo ' done.', $endl;
2093
					elseif ($command_line)
2094
						echo ' +++ ', rtrim(substr($line, 4));
2095
					elseif (trim($line) != '---#')
2096
					{
2097
						if ($is_debug)
2098
							$upcontext['actioned_items'][] = $upcontext['current_debug_item_name'];
2099
					}
2100
				}
2101
2102
				if ($substep < $_GET['substep'] && $substep + 1 >= $_GET['substep'])
2103
				{
2104
					if ($command_line)
2105
						echo ' * ';
2106
					else
2107
						$upcontext['actioned_items'][] = $last_step;
2108
				}
2109
2110
				// Small step - only if we're actually doing stuff.
2111
				if ($do_current)
2112
					nextSubstep(++$substep);
2113
				else
2114
					$substep++;
2115
			}
2116
			elseif ($type == '{')
2117
				$current_type = 'code';
2118
			elseif ($type == '}')
2119
			{
2120
				$current_type = 'sql';
2121
2122
				if (!$do_current || !empty($upcontext['skip_db_substeps']))
2123
				{
2124
					$current_data = '';
2125
2126
					// Avoid confusion when skipping something we normally would have done
2127
					if ($do_current)
2128
						$done_something = true;
2129
2130
					continue;
2131
				}
2132
2133
				// @todo Update this to a try/catch for PHP 7+, because eval() now throws an exception for parse errors instead of returning false
2134
				if (eval('global $db_prefix, $modSettings, $smcFunc, $txt, $upcontext, $db_name; ' . $current_data) === false)
2135
				{
2136
					$upcontext['error_message'] = 'Error in upgrade script ' . basename($filename) . ' on line ' . $line_number . '!' . $endl;
2137
					if ($command_line)
2138
						echo $upcontext['error_message'];
2139
				}
2140
2141
				// Done with code!
2142
				$current_data = '';
2143
				$done_something = true;
2144
			}
2145
2146
			continue;
2147
		}
2148
2149
		$current_data .= $line;
2150
		if (substr(rtrim($current_data), -1) === ';' && $current_type === 'sql')
2151
		{
2152
			if ((!$support_js || isset($_GET['xml'])))
2153
			{
2154
				if (!$do_current || !empty($upcontext['skip_db_substeps']))
2155
				{
2156
					$current_data = '';
2157
2158
					if ($do_current)
2159
						$done_something = true;
2160
2161
					continue;
2162
				}
2163
2164
				// {$sboarddir} is deprecated, but blah blah backward compatibility blah...
2165
				$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));
2166
2167
				upgrade_query($current_data);
2168
2169
				// @todo This will be how it kinda does it once mysql all stripped out - needed for postgre (etc).
2170
				/*
2171
				$result = $smcFunc['db_query']('', $current_data, false, false);
2172
				// Went wrong?
2173
				if (!$result)
2174
				{
2175
					// Bit of a bodge - do we want the error?
2176
					if (!empty($upcontext['return_error']))
2177
					{
2178
						$upcontext['error_message'] = $smcFunc['db_error']($db_connection);
2179
						return false;
2180
					}
2181
				}*/
2182
				$done_something = true;
2183
			}
2184
			$current_data = '';
2185
		}
2186
		// If this is xml based and we're just getting the item name then that's grand.
2187
		elseif ($support_js && !isset($_GET['xml']) && $upcontext['current_debug_item_name'] != '' && $do_current)
2188
		{
2189
			restore_error_handler();
2190
			return false;
2191
		}
2192
2193
		// Clean up by cleaning any step info.
2194
		$step_progress = array();
2195
		$custom_warning = '';
2196
	}
2197
2198
	// Put back the error handler.
2199
	restore_error_handler();
2200
2201
	if ($command_line)
2202
	{
2203
		echo ' Successful.' . "\n";
2204
		flush();
2205
	}
2206
2207
	$_GET['substep'] = 0;
2208
	return true;
2209
}
2210
2211
function upgrade_query($string, $unbuffered = false)
2212
{
2213
	global $db_connection, $db_server, $db_user, $db_passwd, $db_type;
2214
	global $command_line, $upcontext, $upgradeurl, $modSettings;
2215
	global $db_name, $db_unbuffered, $smcFunc, $txt;
2216
2217
	// Get the query result - working around some SMF specific security - just this once!
2218
	$modSettings['disableQueryCheck'] = true;
2219
	$db_unbuffered = $unbuffered;
2220
	$ignore_insert_error = false;
2221
2222
	// If we got an old pg version and use a insert ignore query
2223
	if ($db_type == 'postgresql' && !$smcFunc['db_native_replace']() && strpos($string, 'ON CONFLICT DO NOTHING') !== false)
2224
	{
2225
		$ignore_insert_error = true;
2226
		$string = str_replace('ON CONFLICT DO NOTHING', '', $string);
2227
	}
2228
	$result = $smcFunc['db_query']('', $string, array('security_override' => true, 'db_error_skip' => true));
2229
	$db_unbuffered = false;
2230
2231
	// Failure?!
2232
	if ($result !== false)
2233
		return $result;
2234
2235
	$db_error_message = $smcFunc['db_error']($db_connection);
2236
	// If MySQL we do something more clever.
2237
	if ($db_type == 'mysql')
2238
	{
2239
		$mysqli_errno = mysqli_errno($db_connection);
2240
		$error_query = in_array(substr(trim($string), 0, 11), array('INSERT INTO', 'UPDATE IGNO', 'ALTER TABLE', 'DROP TABLE ', 'ALTER IGNOR', 'INSERT IGNO'));
2241
2242
		// Error numbers:
2243
		//    1016: Can't open file '....MYI'
2244
		//    1050: Table already exists.
2245
		//    1054: Unknown column name.
2246
		//    1060: Duplicate column name.
2247
		//    1061: Duplicate key name.
2248
		//    1062: Duplicate entry for unique key.
2249
		//    1068: Multiple primary keys.
2250
		//    1072: Key column '%s' doesn't exist in table.
2251
		//    1091: Can't drop key, doesn't exist.
2252
		//    1146: Table doesn't exist.
2253
		//    2013: Lost connection to server during query.
2254
2255
		if ($mysqli_errno == 1016)
2256
		{
2257
			if (preg_match('~\'([^\.\']+)~', $db_error_message, $match) != 0 && !empty($match[1]))
2258
			{
2259
				mysqli_query($db_connection, 'REPAIR TABLE `' . $match[1] . '`');
2260
				$result = mysqli_query($db_connection, $string);
2261
				if ($result !== false)
2262
					return $result;
2263
			}
2264
		}
2265
		elseif ($mysqli_errno == 2013)
2266
		{
2267
			$db_connection = mysqli_connect($db_server, $db_user, $db_passwd);
2268
			mysqli_select_db($db_connection, $db_name);
0 ignored issues
show
It seems like $db_connection can also be of type false; however, parameter $link of mysqli_select_db() does only seem to accept mysqli, 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

2268
			mysqli_select_db(/** @scrutinizer ignore-type */ $db_connection, $db_name);
Loading history...
2269
			if ($db_connection)
2270
			{
2271
				$result = mysqli_query($db_connection, $string);
2272
				if ($result !== false)
2273
					return $result;
2274
			}
2275
		}
2276
		// Duplicate column name... should be okay ;).
2277
		elseif (in_array($mysqli_errno, array(1060, 1061, 1068, 1091)))
2278
			return false;
2279
		// Duplicate insert... make sure it's the proper type of query ;).
2280
		elseif (in_array($mysqli_errno, array(1054, 1062, 1146)) && $error_query)
2281
			return false;
2282
		// Creating an index on a non-existent column.
2283
		elseif ($mysqli_errno == 1072)
2284
			return false;
2285
		elseif ($mysqli_errno == 1050 && substr(trim($string), 0, 12) == 'RENAME TABLE')
2286
			return false;
2287
		// Testing for legacy tables or columns? Needed for 1.0 & 1.1 scripts.
2288
		elseif (in_array($mysqli_errno, array(1054, 1146)) && in_array(substr(trim($string), 0, 7), array('SELECT ', 'SHOW CO')))
2289
			return false;
2290
	}
2291
	// If a table already exists don't go potty.
2292
	else
2293
	{
2294
		if (in_array(substr(trim($string), 0, 8), array('CREATE T', 'CREATE S', 'DROP TABL', 'ALTER TA', 'CREATE I', 'CREATE U')))
2295
		{
2296
			if (strpos($db_error_message, 'exist') !== false)
2297
				return true;
2298
		}
2299
		elseif (strpos(trim($string), 'INSERT ') !== false)
2300
		{
2301
			if (strpos($db_error_message, 'duplicate') !== false || $ignore_insert_error)
2302
				return true;
2303
		}
2304
	}
2305
2306
	// Get the query string so we pass everything.
2307
	$query_string = '';
2308
	foreach ($_GET as $k => $v)
2309
		$query_string .= ';' . $k . '=' . $v;
2310
	if (strlen($query_string) != 0)
2311
		$query_string = '?' . substr($query_string, 1);
2312
2313
	if ($command_line)
2314
	{
2315
		echo 'Unsuccessful!  Database error message:', "\n", $db_error_message, "\n";
2316
		die;
2317
	}
2318
2319
	// Bit of a bodge - do we want the error?
2320
	if (!empty($upcontext['return_error']))
2321
	{
2322
		$upcontext['error_message'] = $db_error_message;
2323
		$upcontext['error_string'] = $string;
2324
		return false;
2325
	}
2326
2327
	// Otherwise we have to display this somewhere appropriate if possible.
2328
	$upcontext['forced_error_message'] = '
2329
			<strong>' . $txt['upgrade_unsuccessful'] . '</strong><br>
2330
2331
			<div style="margin: 2ex;">
2332
				' . $txt['upgrade_thisquery'] . '
2333
				<blockquote><pre>' . nl2br(htmlspecialchars(trim($string))) . ';</pre></blockquote>
2334
2335
				' . $txt['upgrade_causerror'] . '
2336
				<blockquote>' . nl2br(htmlspecialchars($db_error_message)) . '</blockquote>
2337
			</div>
2338
2339
			<form action="' . $upgradeurl . $query_string . '" method="post">
2340
				<input type="submit" value="' . $txt['upgrade_respondtime_clickhere'] . '" class="button">
2341
			</form>
2342
		</div>';
2343
2344
	upgradeExit();
2345
}
2346
2347
// This performs a table alter, but does it unbuffered so the script can time out professionally.
2348
function protected_alter($change, $substep, $is_test = false)
2349
{
2350
	global $db_prefix, $smcFunc;
2351
2352
	db_extend('packages');
2353
2354
	// Firstly, check whether the current index/column exists.
2355
	$found = false;
2356
	if ($change['type'] === 'column')
2357
	{
2358
		$columns = $smcFunc['db_list_columns']('{db_prefix}' . $change['table'], true);
2359
		foreach ($columns as $column)
2360
		{
2361
			// Found it?
2362
			if ($column['name'] === $change['name'])
2363
			{
2364
				$found |= true;
2365
				// Do some checks on the data if we have it set.
2366
				if (isset($change['col_type']))
2367
					$found &= $change['col_type'] === $column['type'];
2368
				if (isset($change['null_allowed']))
2369
					$found &= $column['null'] == $change['null_allowed'];
2370
				if (isset($change['default']))
2371
					$found &= $change['default'] === $column['default'];
2372
			}
2373
		}
2374
	}
2375
	elseif ($change['type'] === 'index')
2376
	{
2377
		$request = upgrade_query('
2378
			SHOW INDEX
2379
			FROM ' . $db_prefix . $change['table']);
2380
		if ($request !== false)
2381
		{
2382
			$cur_index = array();
2383
2384
			while ($row = $smcFunc['db_fetch_assoc']($request))
2385
				if ($row['Key_name'] === $change['name'])
2386
					$cur_index[(int) $row['Seq_in_index']] = $row['Column_name'];
2387
2388
			ksort($cur_index, SORT_NUMERIC);
2389
			$found = array_values($cur_index) === $change['target_columns'];
2390
2391
			$smcFunc['db_free_result']($request);
2392
		}
2393
	}
2394
2395
	// If we're trying to add and it's added, we're done.
2396
	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...
2397
		return true;
2398
	// Otherwise if we're removing and it wasn't found we're also done.
2399
	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...
2400
		return true;
2401
	// Otherwise is it just a test?
2402
	elseif ($is_test)
2403
		return false;
2404
2405
	// Not found it yet? Bummer! How about we see if we're currently doing it?
2406
	$running = false;
2407
	$found = false;
2408
	while (1 == 1)
2409
	{
2410
		$request = upgrade_query('
2411
			SHOW FULL PROCESSLIST');
2412
		while ($row = $smcFunc['db_fetch_assoc']($request))
2413
		{
2414
			if (strpos($row['Info'], 'ALTER TABLE ' . $db_prefix . $change['table']) !== false && strpos($row['Info'], $change['text']) !== false)
2415
				$found = true;
2416
		}
2417
2418
		// Can't find it? Then we need to run it fools!
2419
		if (!$found && !$running)
2420
		{
2421
			$smcFunc['db_free_result']($request);
2422
2423
			$success = upgrade_query('
2424
				ALTER TABLE ' . $db_prefix . $change['table'] . '
2425
				' . $change['text'], true) !== false;
2426
2427
			if (!$success)
2428
				return false;
2429
2430
			// Return
2431
			$running = true;
2432
		}
2433
		// What if we've not found it, but we'd ran it already? Must of completed.
2434
		elseif (!$found)
2435
		{
2436
			$smcFunc['db_free_result']($request);
2437
			return true;
2438
		}
2439
2440
		// Pause execution for a sec or three.
2441
		sleep(3);
2442
2443
		// Can never be too well protected.
2444
		nextSubstep($substep);
2445
	}
2446
2447
	// Protect it.
2448
	nextSubstep($substep);
2449
}
2450
2451
/**
2452
 * Alter a text column definition preserving its character set.
2453
 *
2454
 * @param array $change
2455
 * @param int $substep
2456
 */
2457
function textfield_alter($change, $substep)
2458
{
2459
	global $db_prefix, $smcFunc;
2460
2461
	$request = $smcFunc['db_query']('', '
2462
		SHOW FULL COLUMNS
2463
		FROM {db_prefix}' . $change['table'] . '
2464
		LIKE {string:column}',
2465
		array(
2466
			'column' => $change['column'],
2467
			'db_error_skip' => true,
2468
		)
2469
	);
2470
	if ($smcFunc['db_num_rows']($request) === 0)
2471
		die('Unable to find column ' . $change['column'] . ' inside table ' . $db_prefix . $change['table']);
2472
	$table_row = $smcFunc['db_fetch_assoc']($request);
2473
	$smcFunc['db_free_result']($request);
2474
2475
	// If something of the current column definition is different, fix it.
2476
	$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']);
2477
2478
	// Columns that previously allowed null, need to be converted first.
2479
	$null_fix = strtolower($table_row['Null']) === 'yes' && !$change['null_allowed'];
2480
2481
	// Get the character set that goes with the collation of the column.
2482
	if ($column_fix && !empty($table_row['Collation']))
2483
	{
2484
		$request = $smcFunc['db_query']('', '
2485
			SHOW COLLATION
2486
			LIKE {string:collation}',
2487
			array(
2488
				'collation' => $table_row['Collation'],
2489
				'db_error_skip' => true,
2490
			)
2491
		);
2492
		// No results? Just forget it all together.
2493
		if ($smcFunc['db_num_rows']($request) === 0)
2494
			unset($table_row['Collation']);
2495
		else
2496
			$collation_info = $smcFunc['db_fetch_assoc']($request);
2497
		$smcFunc['db_free_result']($request);
2498
	}
2499
2500
	if ($column_fix)
2501
	{
2502
		// Make sure there are no NULL's left.
2503
		if ($null_fix)
2504
			$smcFunc['db_query']('', '
2505
				UPDATE {db_prefix}' . $change['table'] . '
2506
				SET ' . $change['column'] . ' = {string:default}
2507
				WHERE ' . $change['column'] . ' IS NULL',
2508
				array(
2509
					'default' => isset($change['default']) ? $change['default'] : '',
2510
					'db_error_skip' => true,
2511
				)
2512
			);
2513
2514
		// Do the actual alteration.
2515
		$smcFunc['db_query']('', '
2516
			ALTER TABLE {db_prefix}' . $change['table'] . '
2517
			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}' : ''),
2518
			array(
2519
				'default' => isset($change['default']) ? $change['default'] : '',
2520
				'db_error_skip' => true,
2521
			)
2522
		);
2523
	}
2524
	nextSubstep($substep);
2525
}
2526
2527
// Check if we need to alter this query.
2528
function checkChange(&$change)
2529
{
2530
	global $smcFunc, $db_type, $databases;
2531
	static $database_version, $where_field_support;
2532
2533
	// Attempt to find a database_version.
2534
	if (empty($database_version))
2535
	{
2536
		$database_version = $databases[$db_type]['version_check'];
2537
		$where_field_support = $db_type == 'mysql' && version_compare('5.0', $database_version, '<=');
2538
	}
2539
2540
	// Not a column we need to check on?
2541
	if (!in_array($change['name'], array('memberGroups', 'passwordSalt')))
2542
		return;
2543
2544
	// Break it up you (six|seven).
2545
	$temp = explode(' ', str_replace('NOT NULL', 'NOT_NULL', $change['text']));
2546
2547
	// Can we support a shortcut method?
2548
	if ($where_field_support)
2549
	{
2550
		// Get the details about this change.
2551
		$request = $smcFunc['db_query']('', '
2552
			SHOW FIELDS
2553
			FROM {db_prefix}{raw:table}
2554
			WHERE Field = {string:old_name} OR Field = {string:new_name}',
2555
			array(
2556
				'table' => $change['table'],
2557
				'old_name' => $temp[1],
2558
				'new_name' => $temp[2],
2559
			)
2560
		);
2561
		// !!! This doesn't technically work because we don't pass request into it, but it hasn't broke anything yet.
2562
		if ($smcFunc['db_num_rows'] != 1)
2563
			return;
2564
2565
		list (, $current_type) = $smcFunc['db_fetch_assoc']($request);
2566
		$smcFunc['db_free_result']($request);
2567
	}
2568
	else
2569
	{
2570
		// Do this the old fashion, sure method way.
2571
		$request = $smcFunc['db_query']('', '
2572
			SHOW FIELDS
2573
			FROM {db_prefix}{raw:table}',
2574
			array(
2575
				'table' => $change['table'],
2576
			)
2577
		);
2578
		// Mayday!
2579
		// !!! This doesn't technically work because we don't pass request into it, but it hasn't broke anything yet.
2580
		if ($smcFunc['db_num_rows'] == 0)
2581
			return;
2582
2583
		// Oh where, oh where has my little field gone. Oh where can it be...
2584
		while ($row = $smcFunc['db_query']($request))
2585
			if ($row['Field'] == $temp[1] || $row['Field'] == $temp[2])
2586
			{
2587
				$current_type = $row['Type'];
2588
				break;
2589
			}
2590
	}
2591
2592
	// If this doesn't match, the column may of been altered for a reason.
2593
	if (trim($current_type) != trim($temp[3]))
2594
		$temp[3] = $current_type;
2595
2596
	// Piece this back together.
2597
	$change['text'] = str_replace('NOT_NULL', 'NOT NULL', implode(' ', $temp));
2598
}
2599
2600
// The next substep.
2601
function nextSubstep($substep)
2602
{
2603
	global $start_time, $timeLimitThreshold, $command_line, $custom_warning;
2604
	global $step_progress, $is_debug, $upcontext;
2605
2606
	if ($_GET['substep'] < $substep)
2607
		$_GET['substep'] = $substep;
2608
2609
	if ($command_line)
2610
	{
2611
		if (time() - $start_time > 1 && empty($is_debug))
2612
		{
2613
			echo '.';
2614
			$start_time = time();
2615
		}
2616
		return;
2617
	}
2618
2619
	@set_time_limit(300);
2620
	if (function_exists('apache_reset_timeout'))
2621
		@apache_reset_timeout();
2622
2623
	if (time() - $start_time <= $timeLimitThreshold)
2624
		return;
2625
2626
	// Do we have some custom step progress stuff?
2627
	if (!empty($step_progress))
2628
	{
2629
		$upcontext['substep_progress'] = 0;
2630
		$upcontext['substep_progress_name'] = $step_progress['name'];
2631
		if ($step_progress['current'] > $step_progress['total'])
2632
			$upcontext['substep_progress'] = 99.9;
2633
		else
2634
			$upcontext['substep_progress'] = ($step_progress['current'] / $step_progress['total']) * 100;
2635
2636
		// Make it nicely rounded.
2637
		$upcontext['substep_progress'] = round($upcontext['substep_progress'], 1);
2638
	}
2639
2640
	// If this is XML we just exit right away!
2641
	if (isset($_GET['xml']))
2642
		return upgradeExit();
0 ignored issues
show
Are you sure the usage of upgradeExit() is correct as it seems to always return null.

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

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

}

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

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

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

Loading history...
2643
2644
	// We're going to pause after this!
2645
	$upcontext['pause'] = true;
2646
2647
	$upcontext['query_string'] = '';
2648
	foreach ($_GET as $k => $v)
2649
	{
2650
		if ($k != 'data' && $k != 'substep' && $k != 'step')
2651
			$upcontext['query_string'] .= ';' . $k . '=' . $v;
2652
	}
2653
2654
	// Custom warning?
2655
	if (!empty($custom_warning))
2656
		$upcontext['custom_warning'] = $custom_warning;
2657
2658
	upgradeExit();
2659
}
2660
2661
function cmdStep0()
2662
{
2663
	global $boarddir, $sourcedir, $modSettings, $start_time, $cachedir, $databases, $db_type, $smcFunc, $upcontext;
2664
	global $is_debug, $boardurl, $txt;
2665
	$start_time = time();
2666
2667
	ob_end_clean();
2668
	ob_implicit_flush(1);
2669
	@set_time_limit(600);
2670
2671
	if (!isset($_SERVER['argv']))
2672
		$_SERVER['argv'] = array();
2673
	$_GET['maint'] = 1;
2674
2675
	foreach ($_SERVER['argv'] as $i => $arg)
2676
	{
2677
		if (preg_match('~^--language=(.+)$~', $arg, $match) != 0)
2678
			$upcontext['lang'] = $match[1];
2679
		elseif (preg_match('~^--path=(.+)$~', $arg) != 0)
2680
			continue;
2681
		elseif ($arg == '--no-maintenance')
2682
			$_GET['maint'] = 0;
2683
		elseif ($arg == '--debug')
2684
			$is_debug = true;
2685
		elseif ($arg == '--backup')
2686
			$_POST['backup'] = 1;
2687
		elseif ($arg == '--template' && (file_exists($boarddir . '/template.php') || file_exists($boarddir . '/template.html') && !file_exists($modSettings['theme_dir'] . '/converted')))
2688
			$_GET['conv'] = 1;
2689
		elseif ($i != 0)
2690
		{
2691
			echo 'SMF Command-line Upgrader
2692
Usage: /path/to/php -f ' . basename(__FILE__) . ' -- [OPTION]...
2693
2694
	--language=LANG         Reset the forum\'s language to LANG.
2695
	--no-maintenance        Don\'t put the forum into maintenance mode.
2696
	--debug                 Output debugging information.
2697
	--backup                Create backups of tables with "backup_" prefix.';
2698
			echo "\n";
2699
			exit;
2700
		}
2701
	}
2702
2703
	if (!php_version_check())
2704
		print_error('Error: PHP ' . PHP_VERSION . ' does not match version requirements.', true);
2705
	if (!db_version_check())
2706
		print_error('Error: ' . $databases[$db_type]['name'] . ' ' . $databases[$db_type]['version'] . ' does not match minimum requirements.', true);
2707
2708
	// Do some checks to make sure they have proper privileges
2709
	db_extend('packages');
2710
2711
	// CREATE
2712
	$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');
2713
2714
	// ALTER
2715
	$alter = $smcFunc['db_add_column']('{db_prefix}priv_check', array('name' => 'txt', 'type' => 'varchar', 'size' => 4, 'null' => false, 'default' => ''));
2716
2717
	// DROP
2718
	$drop = $smcFunc['db_drop_table']('{db_prefix}priv_check');
2719
2720
	// Sorry... we need CREATE, ALTER and DROP
2721
	if (!$create || !$alter || !$drop)
2722
		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);
2723
2724
	$check = @file_exists($modSettings['theme_dir'] . '/index.template.php')
2725
		&& @file_exists($sourcedir . '/QueryString.php')
2726
		&& @file_exists($sourcedir . '/ManageBoards.php');
2727
	if (!$check && !isset($modSettings['smfVersion']))
2728
		print_error('Error: Some files are missing or out-of-date.', true);
2729
2730
	// Do a quick version spot check.
2731
	$temp = substr(@implode('', @file($boarddir . '/index.php')), 0, 4096);
0 ignored issues
show
It seems like @file($boarddir . '/index.php') can also be of type false; however, parameter $pieces of implode() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

2731
	$temp = substr(@implode('', /** @scrutinizer ignore-type */ @file($boarddir . '/index.php')), 0, 4096);
Loading history...
2732
	preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $temp, $match);
2733
	if (empty($match[1]) || (trim($match[1]) != SMF_VERSION))
2734
		print_error('Error: Some files have not yet been updated properly.');
2735
2736
	// Make sure Settings.php is writable.
2737
	quickFileWritable($boarddir . '/Settings.php');
2738
	if (!is_writable($boarddir . '/Settings.php'))
2739
		print_error('Error: Unable to obtain write access to "Settings.php".', true);
2740
2741
	// Make sure Settings_bak.php is writable.
2742
	quickFileWritable($boarddir . '/Settings_bak.php');
2743
	if (!is_writable($boarddir . '/Settings_bak.php'))
2744
		print_error('Error: Unable to obtain write access to "Settings_bak.php".');
2745
2746
	if (isset($modSettings['agreement']) && (!is_writable($boarddir) || file_exists($boarddir . '/agreement.txt')) && !is_writable($boarddir . '/agreement.txt'))
2747
		print_error('Error: Unable to obtain write access to "agreement.txt".');
2748
	elseif (isset($modSettings['agreement']))
2749
	{
2750
		$fp = fopen($boarddir . '/agreement.txt', 'w');
2751
		fwrite($fp, $modSettings['agreement']);
0 ignored issues
show
It seems like $fp can also be of type false; however, parameter $handle of fwrite() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

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

2751
		fwrite(/** @scrutinizer ignore-type */ $fp, $modSettings['agreement']);
Loading history...
2752
		fclose($fp);
0 ignored issues
show
It seems like $fp can also be of type false; however, parameter $handle of fclose() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

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

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

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

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

}

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

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

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

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

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

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

}

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

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

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

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