Passed
Pull Request — release-2.1 (#7077)
by Jeremy
11:09
created

template_convert_xml()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

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

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

48
			return mysqli_fetch_row(/** @scrutinizer ignore-type */ mysqli_query($db_connection, 'SELECT VERSION();'))[0];
Loading history...
49
		},
50
		'alter_support' => true,
51
	),
52
	'postgresql' => array(
53
		'name' => 'PostgreSQL',
54
		'version' => '9.6',
55
		'version_check' => function() {
56
			if (!function_exists('pg_version'))
57
				return false;
58
			$version = pg_version();
59
			return $version['client'];
60
		},
61
		'always_has_db' => true,
62
	),
63
);
64
65
/**
66
 * The maximum time a single substep may take, in seconds.
67
 *
68
 * @var int
69
 */
70
$timeLimitThreshold = 3;
71
72
/**
73
 * The current path to the upgrade.php file.
74
 *
75
 * @var string
76
 */
77
$upgrade_path = dirname(__FILE__);
78
79
/**
80
 * The URL of the current page.
81
 *
82
 * @var string
83
 */
84
$upgradeurl = $_SERVER['PHP_SELF'];
85
86
/**
87
 * Flag to disable the required administrator login.
88
 *
89
 * @var bool
90
 */
91
$disable_security = false;
92
93
/**
94
 * The amount of seconds allowed between logins.
95
 * If the first user to login is inactive for this amount of seconds, a second login is allowed.
96
 *
97
 * @var int
98
 */
99
$upcontext['inactive_timeout'] = 10;
100
101
global $txt;
102
103
// All the steps in detail.
104
// Number,Name,Function,Progress Weight.
105
$upcontext['steps'] = array(
106
	0 => array(1, 'upgrade_step_login', 'WelcomeLogin', 2),
107
	1 => array(2, 'upgrade_step_options', 'UpgradeOptions', 2),
108
	2 => array(3, 'upgrade_step_backup', 'BackupDatabase', 10),
109
	3 => array(4, 'upgrade_step_database', 'DatabaseChanges', 50),
110
	4 => array(5, 'upgrade_step_convertjson', 'serialize_to_json', 10),
111
	5 => array(6, 'upgrade_step_convertutf', 'ConvertUtf8', 20),
112
	6 => array(7, 'upgrade_step_delete', 'DeleteUpgrade', 1),
113
);
114
// Just to remember which one has files in it.
115
$upcontext['database_step'] = 3;
116
@set_time_limit(600);
117
if (!ini_get('safe_mode'))
118
{
119
	ini_set('mysql.connect_timeout', -1);
120
	ini_set('default_socket_timeout', 900);
121
}
122
// Clean the upgrade path if this is from the client.
123
if (!empty($_SERVER['argv']) && php_sapi_name() == 'cli' && empty($_SERVER['REMOTE_ADDR']))
124
	for ($i = 1; $i < $_SERVER['argc']; $i++)
125
	{
126
		// Provide the help without possible errors if the enviornment isn't sane.
127
		if (in_array($_SERVER['argv'][$i], array('-h', '--help')))
128
		{
129
			cmdStep0();
130
			exit;
131
		}
132
133
		if (preg_match('~^--path=(.+)$~', $_SERVER['argv'][$i], $match) != 0)
134
			$upgrade_path = realpath(substr($match[1], -1) == '/' ? substr($match[1], 0, -1) : $match[1]);
135
136
		// Cases where we do php other/upgrade.php --path=./
137
		if ($upgrade_path == './' && isset($_SERVER['PWD']))
138
			$upgrade_path = realpath($_SERVER['PWD']);
139
		// Cases where we do php upgrade.php --path=../
140
		elseif ($upgrade_path == '../' && isset($_SERVER['PWD']))
141
			$upgrade_path = dirname(realpath($_SERVER['PWD']));
142
	}
143
144
// Are we from the client?
145
if (php_sapi_name() == 'cli' && empty($_SERVER['REMOTE_ADDR']))
146
{
147
	$command_line = true;
148
	$disable_security = true;
149
}
150
else
151
	$command_line = false;
152
153
// We can't do anything without these files.
154
foreach (array(
155
	dirname(__FILE__) . '/upgrade-helper.php',
156
	$upgrade_path . '/Settings.php'
157
) as $required_file)
158
{
159
	if (!file_exists($required_file))
160
		die(basename($required_file) . ' was not found where it was expected: ' . $required_file . '! Make sure you have uploaded ALL files from the upgrade package to your forum\'s root directory. The upgrader cannot continue.');
161
162
	require_once($required_file);
163
}
164
165
// We don't use "-utf8" anymore...  Tweak the entry that may have been loaded by Settings.php
166
if (isset($language))
167
	$language = str_ireplace('-utf8', '', basename($language, '.lng'));
168
169
// Figure out a valid language request (if any)
170
// Can't use $_GET until it's been cleaned, so do this manually and VERY restrictively! This even strips off those '-utf8' bits that we don't want.
171
if (isset($_SERVER['QUERY_STRING']) && preg_match('~\blang=(\w+)~', $_SERVER['QUERY_STRING'], $matches))
172
	$upcontext['lang'] = $matches[1];
173
174
// Are we logged in?
175
if (isset($upgradeData))
176
{
177
	$upcontext['user'] = json_decode(base64_decode($upgradeData), true);
178
179
	// Check for sensible values.
180
	if (empty($upcontext['user']['started']) || $upcontext['user']['started'] < time() - 86400)
181
		$upcontext['user']['started'] = time();
182
	if (empty($upcontext['user']['updated']) || $upcontext['user']['updated'] < time() - 86400)
183
		$upcontext['user']['updated'] = 0;
184
185
	$upcontext['started'] = $upcontext['user']['started'];
186
	$upcontext['updated'] = $upcontext['user']['updated'];
187
188
	$is_debug = !empty($upcontext['user']['debug']) ? true : false;
189
190
	$upcontext['skip_db_substeps'] = !empty($upcontext['user']['skip_db_substeps']);
191
}
192
193
// Nothing sensible?
194
if (empty($upcontext['updated']))
195
{
196
	$upcontext['started'] = time();
197
	$upcontext['updated'] = 0;
198
	$upcontext['skip_db_substeps'] = false;
199
	$upcontext['user'] = array(
200
		'id' => 0,
201
		'name' => 'Guest',
202
		'pass' => 0,
203
		'started' => $upcontext['started'],
204
		'updated' => $upcontext['updated'],
205
	);
206
}
207
208
// Try to load the language file... or at least define a few necessary strings for now.
209
load_lang_file();
210
211
// Load up some essential data...
212
loadEssentialData();
213
214
// Are we going to be mimic'ing SSI at this point?
215
if (isset($_GET['ssi']))
216
{
217
	require_once($sourcedir . '/Errors.php');
218
	require_once($sourcedir . '/Logging.php');
219
	require_once($sourcedir . '/Load.php');
220
	require_once($sourcedir . '/Security.php');
221
	require_once($sourcedir . '/Subs-Package.php');
222
223
	// SMF isn't started up properly, but loadUserSettings calls our cookies.
224
	if (!isset($smcFunc['json_encode']))
225
	{
226
		$smcFunc['json_encode'] = 'json_encode';
227
		$smcFunc['json_decode'] = 'smf_json_decode';
228
	}
229
230
	loadUserSettings();
231
	loadPermissions();
232
	reloadSettings();
233
}
234
235
// Include our helper functions.
236
require_once($sourcedir . '/Subs.php');
237
require_once($sourcedir . '/LogInOut.php');
238
require_once($sourcedir . '/Subs-Editor.php');
239
240
// Don't do security check if on Yabbse
241
if (!isset($modSettings['smfVersion']))
242
	$disable_security = true;
243
244
// This only exists if we're on SMF ;)
245
if (isset($modSettings['smfVersion']))
246
{
247
	$request = $smcFunc['db_query']('', '
248
		SELECT variable, value
249
		FROM {db_prefix}themes
250
		WHERE id_theme = {int:id_theme}
251
			AND variable IN ({string:theme_url}, {string:theme_dir}, {string:images_url})',
252
		array(
253
			'id_theme' => 1,
254
			'theme_url' => 'theme_url',
255
			'theme_dir' => 'theme_dir',
256
			'images_url' => 'images_url',
257
			'db_error_skip' => true,
258
		)
259
	);
260
	while ($row = $smcFunc['db_fetch_assoc']($request))
261
		$modSettings[$row['variable']] = $row['value'];
262
	$smcFunc['db_free_result']($request);
263
}
264
265
if (!isset($modSettings['theme_url']))
266
{
267
	$modSettings['theme_dir'] = $boarddir . '/Themes/default';
268
	$modSettings['theme_url'] = 'Themes/default';
269
	$modSettings['images_url'] = 'Themes/default/images';
270
}
271
if (!isset($settings['default_theme_url']))
272
	$settings['default_theme_url'] = $modSettings['theme_url'];
273
if (!isset($settings['default_theme_dir']))
274
	$settings['default_theme_dir'] = $modSettings['theme_dir'];
275
276
// Old DBs won't have this
277
if (!isset($modSettings['rand_seed']))
278
{
279
	if (!function_exists('cache_put_data'))
280
		require_once($sourcedir . '/Load.php');
281
	smf_seed_generator();
282
}
283
284
// This is needed in case someone invokes the upgrader using https when upgrading an http forum
285
if (httpsOn())
286
	$settings['default_theme_url'] = strtr($settings['default_theme_url'], array('http://' => 'https://'));
287
288
$upcontext['is_large_forum'] = (empty($modSettings['smfVersion']) || $modSettings['smfVersion'] <= '1.1 RC1') && !empty($modSettings['totalMessages']) && $modSettings['totalMessages'] > 75000;
289
290
// Have we got tracking data - if so use it (It will be clean!)
291
if (isset($_GET['data']))
292
{
293
	global $is_debug;
294
295
	$upcontext['upgrade_status'] = json_decode(base64_decode($_GET['data']), true);
296
	$upcontext['current_step'] = $upcontext['upgrade_status']['curstep'];
297
	$upcontext['language'] = $upcontext['upgrade_status']['lang'];
298
	$upcontext['rid'] = $upcontext['upgrade_status']['rid'];
299
	$support_js = $upcontext['upgrade_status']['js'];
300
301
	// Only set this if the upgrader status says so.
302
	if (empty($is_debug))
303
		$is_debug = $upcontext['upgrade_status']['debug'];
304
}
305
// Set the defaults.
306
else
307
{
308
	$upcontext['current_step'] = 0;
309
	$upcontext['rid'] = mt_rand(0, 5000);
310
	$upcontext['upgrade_status'] = array(
311
		'curstep' => 0,
312
		'lang' => isset($upcontext['lang']) ? $upcontext['lang'] : basename($language, '.lng'),
313
		'rid' => $upcontext['rid'],
314
		'pass' => 0,
315
		'debug' => 0,
316
		'js' => 0,
317
	);
318
	$upcontext['language'] = $upcontext['upgrade_status']['lang'];
319
}
320
321
// Now that we have the necessary info, make sure we loaded the right language file.
322
load_lang_file();
323
324
// Default title...
325
$upcontext['page_title'] = $txt['updating_smf_installation'];
326
327
// If this isn't the first stage see whether they are logging in and resuming.
328
if ($upcontext['current_step'] != 0 || !empty($upcontext['user']['step']))
329
	checkLogin();
330
331
if ($command_line)
332
	cmdStep0();
333
334
// Don't error if we're using xml.
335
if (isset($_GET['xml']))
336
	$upcontext['return_error'] = true;
337
338
// Loop through all the steps doing each one as required.
339
$upcontext['overall_percent'] = 0;
340
foreach ($upcontext['steps'] as $num => $step)
341
{
342
	if ($num >= $upcontext['current_step'])
343
	{
344
		// The current weight of this step in terms of overall progress.
345
		$upcontext['step_weight'] = $step[3];
346
		// Make sure we reset the skip button.
347
		$upcontext['skip'] = false;
348
349
		// We cannot proceed if we're not logged in.
350
		if ($num != 0 && !$disable_security && $upcontext['user']['pass'] != $upcontext['upgrade_status']['pass'])
351
		{
352
			$upcontext['steps'][0][2]();
353
			break;
354
		}
355
356
		// Call the step and if it returns false that means pause!
357
		if (function_exists($step[2]) && $step[2]() === false)
358
			break;
359
		elseif (function_exists($step[2]))
360
		{
361
			//Start each new step with this unset, so the 'normal' template is called first
362
			unset($_GET['xml']);
363
			//Clear out warnings at the start of each step
364
			unset($upcontext['custom_warning']);
365
			$_GET['substep'] = 0;
366
			$upcontext['current_step']++;
367
		}
368
	}
369
	$upcontext['overall_percent'] += $step[3];
370
}
371
372
upgradeExit();
373
374
// Exit the upgrade script.
375
function upgradeExit($fallThrough = false)
376
{
377
	global $upcontext, $upgradeurl, $sourcedir, $command_line, $is_debug, $txt;
378
379
	// Save where we are...
380
	if (!empty($upcontext['current_step']) && !empty($upcontext['user']['id']))
381
	{
382
		$upcontext['user']['step'] = $upcontext['current_step'];
383
		$upcontext['user']['substep'] = $_GET['substep'];
384
		$upcontext['user']['updated'] = time();
385
		$upcontext['user']['skip_db_substeps'] = !empty($upcontext['skip_db_substeps']);
386
		$upcontext['debug'] = $is_debug;
387
		$upgradeData = base64_encode(json_encode($upcontext['user']));
388
		require_once($sourcedir . '/Subs.php');
389
		require_once($sourcedir . '/Subs-Admin.php');
390
		updateSettingsFile(array('upgradeData' => $upgradeData));
391
		updateDbLastError(0);
392
	}
393
394
	// Handle the progress of the step, if any.
395
	if (!empty($upcontext['step_progress']) && isset($upcontext['steps'][$upcontext['current_step']]))
396
	{
397
		$upcontext['step_progress'] = round($upcontext['step_progress'], 1);
398
		$upcontext['overall_percent'] += $upcontext['step_progress'] * ($upcontext['steps'][$upcontext['current_step']][3] / 100);
399
	}
400
	$upcontext['overall_percent'] = (int) $upcontext['overall_percent'];
401
402
	// We usually dump our templates out.
403
	if (!$fallThrough)
404
	{
405
		// This should not happen my dear... HELP ME DEVELOPERS!!
406
		if (!empty($command_line))
407
		{
408
			if (function_exists('debug_print_backtrace'))
409
				debug_print_backtrace();
410
411
			printf("\n" . $txt['error_unexpected_template_call'], isset($upcontext['sub_template']) ? $upcontext['sub_template'] : '');
412
			flush();
413
			die();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

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

Loading history...
414
		}
415
416
		if (!isset($_GET['xml']))
417
			template_upgrade_above();
418
		else
419
		{
420
			header('content-type: text/xml; charset=UTF-8');
421
			// Sadly we need to retain the $_GET data thanks to the old upgrade scripts.
422
			$upcontext['get_data'] = array();
423
			foreach ($_GET as $k => $v)
424
			{
425
				if (substr($k, 0, 3) != 'amp' && !in_array($k, array('xml', 'substep', 'lang', 'data', 'step', 'filecount')))
426
				{
427
					$upcontext['get_data'][$k] = $v;
428
				}
429
			}
430
			template_xml_above();
431
		}
432
433
		// Call the template.
434
		if (isset($upcontext['sub_template']))
435
		{
436
			$upcontext['upgrade_status']['curstep'] = $upcontext['current_step'];
437
			$upcontext['form_url'] = $upgradeurl . '?step=' . $upcontext['current_step'] . '&amp;substep=' . $_GET['substep'] . '&amp;data=' . base64_encode(json_encode($upcontext['upgrade_status']));
438
439
			// Custom stuff to pass back?
440
			if (!empty($upcontext['query_string']))
441
				$upcontext['form_url'] .= $upcontext['query_string'];
442
443
			// Call the appropriate subtemplate
444
			if (is_callable('template_' . $upcontext['sub_template']))
445
				call_user_func('template_' . $upcontext['sub_template']);
446
			else
447
				die(sprintf($txt['error_invalid_template'], $upcontext['sub_template']));
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

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

Loading history...
448
		}
449
450
		// Was there an error?
451
		if (!empty($upcontext['forced_error_message']))
452
			echo $upcontext['forced_error_message'];
453
454
		// Show the footer.
455
		if (!isset($_GET['xml']))
456
			template_upgrade_below();
457
		else
458
			template_xml_below();
459
	}
460
461
	// Show the upgrade time for CLI when we are completely done, if in debug mode.
462
	if (!empty($command_line) && $is_debug)
463
	{
464
		$active = time() - $upcontext['started'];
465
		$hours = floor($active / 3600);
466
		$minutes = intval(($active / 60) % 60);
467
		$seconds = intval($active % 60);
468
469
		if ($hours > 0)
470
			echo "\n" . '', sprintf($txt['upgrade_completed_time_hms'], $hours, $minutes, $seconds), '' . "\n";
471
		elseif ($minutes > 0)
472
			echo "\n" . '', sprintf($txt['upgrade_completed_time_ms'], $minutes, $seconds), '' . "\n";
473
		elseif ($seconds > 0)
474
			echo "\n" . '', sprintf($txt['upgrade_completed_time_s'], $seconds), '' . "\n";
475
	}
476
477
	// Bang - gone!
478
	die();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

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

Loading history...
479
}
480
481
// Load the list of language files, and the current language file.
482
function load_lang_file()
483
{
484
	global $txt, $upcontext, $language, $modSettings, $upgrade_path, $command_line;
485
486
	static $lang_dir = '', $detected_languages = array(), $loaded_langfile = '';
487
488
	// Do we know where to look for the language files, or shall we just guess for now?
489
	$temp = isset($modSettings['theme_dir']) ? $modSettings['theme_dir'] . '/languages' : $upgrade_path . '/Themes/default/languages';
490
491
	if ($lang_dir != $temp)
492
	{
493
		$lang_dir = $temp;
494
		$detected_languages = array();
495
	}
496
497
	// Override the language file?
498
	if (isset($upcontext['language']))
499
		$_SESSION['upgrader_langfile'] = 'Install.' . $upcontext['language'] . '.php';
500
	elseif (isset($upcontext['lang']))
501
		$_SESSION['upgrader_langfile'] = 'Install.' . $upcontext['lang'] . '.php';
502
	elseif (isset($language))
503
		$_SESSION['upgrader_langfile'] = 'Install.' . $language . '.php';
504
505
	// Avoid pointless repetition
506
	if (isset($_SESSION['upgrader_langfile']) && $loaded_langfile == $lang_dir . '/' . $_SESSION['upgrader_langfile'])
507
		return;
508
509
	// Now try to find the language files
510
	if (empty($detected_languages))
511
	{
512
		// Make sure the languages directory actually exists.
513
		if (file_exists($lang_dir))
514
		{
515
			// Find all the "Install" language files in the directory.
516
			$dir = dir($lang_dir);
517
			while ($entry = $dir->read())
518
			{
519
				// Skip any old '-utf8' language files that might be lying around
520
				if (strpos($entry, '-utf8') !== false)
521
					continue;
522
523
				if (substr($entry, 0, 8) == 'Install.' && substr($entry, -4) == '.php')
524
					$detected_languages[$entry] = ucfirst(substr($entry, 8, strlen($entry) - 12));
525
			}
526
			$dir->close();
527
		}
528
		// Our guess was wrong, but that's fine. We'll try again after $modSettings['theme_dir'] is defined.
529
		elseif (!isset($modSettings['theme_dir']))
530
		{
531
			// Define a few essential strings for now.
532
			$txt['error_db_connect_settings'] = 'Cannot connect to the database server.<br><br>Please check that the database info variables are correct in Settings.php.';
533
			$txt['error_sourcefile_missing'] = 'Unable to find the Sources/%1$s file. Please make sure it was uploaded properly, and then try again.';
534
535
			$txt['warning_lang_old'] = 'The language files for your selected language, %1$s, have not been updated to the latest version. Upgrade will continue with the forum default, %2$s.';
536
			$txt['warning_lang_missing'] = 'The upgrader could not find the &quot;Install&quot; language file for your selected language, %1$s. Upgrade will continue with the forum default, %2$s.';
537
538
			return;
539
		}
540
	}
541
542
	// Didn't find any, show an error message!
543
	if (empty($detected_languages))
544
	{
545
		$from = explode('/', $command_line ? $upgrade_path : $_SERVER['PHP_SELF']);
546
		$to = explode('/', $lang_dir);
547
		$relPath = $to;
548
549
		foreach($from as $depth => $dir)
550
		{
551
			if ($dir === $to[$depth])
552
				array_shift($relPath);
553
			else
554
			{
555
				$remaining = count($from) - $depth;
556
				if ($remaining > 1)
557
				{
558
					$padLength = (count($relPath) + $remaining - 1) * -1;
559
					$relPath = array_pad($relPath, $padLength, '..');
560
					break;
561
				}
562
				else
563
					$relPath[0] = './' . $relPath[0];
564
			}
565
		}
566
		$relPath = implode(DIRECTORY_SEPARATOR, $relPath);
567
568
		// Command line?
569
		if ($command_line)
570
		{
571
			echo 'This upgrader was unable to find the upgrader\'s language file or files.  They should be found under:', "\n",
572
				$relPath, "\n",
573
				'In some cases, FTP clients do not properly upload files with this many folders. Please double check to make sure you have uploaded all the files in the distribution', "\n",
574
				'If that doesn\'t help, please make sure this upgrade.php file is in the same place as the Themes folder.', "\n";
575
			die;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

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

Loading history...
576
		}
577
578
		// Let's not cache this message, eh?
579
		header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
580
		header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
581
		header('Cache-Control: no-cache');
582
583
		echo '<!DOCTYPE html>
584
			<html>
585
				<head>
586
					<title>SMF Upgrader: Error!</title>
587
						<style>
588
							body {
589
								font-family: sans-serif;
590
								max-width: 700px; }
591
592
								h1 {
593
									font-size: 14pt; }
594
595
								.directory {
596
									margin: 0.3em;
597
									font-family: monospace;
598
									font-weight: bold; }
599
						</style>
600
				</head>
601
				<body>
602
					<h1>A critical error has occurred.</h1>
603
						<p>This upgrader was unable to find the upgrader\'s language file or files.  They should be found under:</p>
604
						<div class="directory">', $relPath, '</div>
605
						<p>In some cases, FTP clients do not properly upload files with this many folders. Please double check to make sure you <strong>have uploaded all the files in the distribution</strong>.</p>
606
						<p>If that doesn\'t help, please make sure this upgrade.php file is in the same place as the Themes folder.</p>
607
						<p>If you continue to get this error message, feel free to <a href="https://support.simplemachines.org/">look to us for support</a>.</p>
608
				</body>
609
			</html>';
610
		die;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

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

Loading history...
611
	}
612
613
	// Make sure it exists. If it doesn't, reset it.
614
	if (!isset($_SESSION['upgrader_langfile']) || preg_match('~[^\w.-]~', $_SESSION['upgrader_langfile']) === 1 || !file_exists($lang_dir . '/' . $_SESSION['upgrader_langfile']))
615
	{
616
		// Use the first one...
617
		list ($_SESSION['upgrader_langfile']) = array_keys($detected_languages);
618
619
		// If we have English and some other language, use the other language.
620
		if ($_SESSION['upgrader_langfile'] == 'Install.english.php' && count($detected_languages) > 1)
621
			list (, $_SESSION['upgrader_langfile']) = array_keys($detected_languages);
622
	}
623
624
	// For backup we load English at first, then the second language will overwrite it.
625
	if ($_SESSION['upgrader_langfile'] != 'Install.english.php')
626
	{
627
		require_once($lang_dir . '/index.english.php');
628
		require_once($lang_dir . '/Install.english.php');
629
	}
630
631
	// And now include the actual language file itself.
632
	require_once($lang_dir . '/' . str_replace('Install.', 'index.', $_SESSION['upgrader_langfile']));
633
	require_once($lang_dir . '/' . $_SESSION['upgrader_langfile']);
634
635
	// Remember what we've done
636
	$loaded_langfile = $lang_dir . '/' . $_SESSION['upgrader_langfile'];
637
}
638
639
// Used to direct the user to another location.
640
function redirectLocation($location, $addForm = true)
641
{
642
	global $upgradeurl, $upcontext, $command_line;
643
644
	// Command line users can't be redirected.
645
	if ($command_line)
646
		upgradeExit(true);
647
648
	// Are we providing the core info?
649
	if ($addForm)
650
	{
651
		$upcontext['upgrade_status']['curstep'] = $upcontext['current_step'];
652
		$location = $upgradeurl . '?step=' . $upcontext['current_step'] . '&substep=' . $_GET['substep'] . '&data=' . base64_encode(json_encode($upcontext['upgrade_status'])) . $location;
653
	}
654
655
	while (@ob_end_clean())
656
		header('location: ' . strtr($location, array('&amp;' => '&')));
657
658
	// Exit - saving status as we go.
659
	upgradeExit(true);
660
}
661
662
// Load all essential data and connect to the DB as this is pre SSI.php
663
function loadEssentialData()
664
{
665
	global $db_server, $db_user, $db_passwd, $db_name, $db_connection;
666
	global $db_prefix, $db_character_set, $db_type, $db_port, $db_show_debug;
667
	global $db_mb4, $modSettings, $sourcedir, $smcFunc, $txt, $utf8;
668
669
	// Report all errors if admin wants them or this is a pre-release version.
670
	if (!empty($db_show_debug) || strspn(SMF_VERSION, '1234567890.') !== strlen(SMF_VERSION))
671
		error_reporting(E_ALL);
672
	// Otherwise, report all errors except for deprecation notices.
673
	else
674
		error_reporting(E_ALL & ~E_DEPRECATED);
675
676
	define('SMF', 1);
677
	header('X-Frame-Options: SAMEORIGIN');
678
	header('X-XSS-Protection: 1');
679
	header('X-Content-Type-Options: nosniff');
680
681
	// Start the session.
682
	if (@ini_get('session.save_handler') == 'user')
683
		@ini_set('session.save_handler', 'files');
684
	@session_start();
685
686
	if (empty($smcFunc))
687
		$smcFunc = array();
688
689
	require_once($sourcedir . '/Subs.php');
690
691
	$smcFunc['random_int'] = function($min = 0, $max = PHP_INT_MAX)
692
	{
693
		global $sourcedir;
694
695
		// Oh, wouldn't it be great if I *was* crazy? Then the world would be okay.
696
		if (!is_callable('random_int'))
697
			require_once($sourcedir . '/random_compat/random.php');
698
699
		return random_int($min, $max);
700
	};
701
702
	// This is now needed for loadUserSettings()
703
	$smcFunc['random_bytes'] = function($bytes)
704
	{
705
		global $sourcedir;
706
707
		if (!is_callable('random_bytes'))
708
			require_once($sourcedir . '/random_compat/random.php');
709
710
		return random_bytes($bytes);
711
	};
712
713
	// We need this for authentication and some upgrade code
714
	require_once($sourcedir . '/Subs-Auth.php');
715
	require_once($sourcedir . '/Class-Package.php');
716
717
	$smcFunc['strtolower'] = 'smf_strtolower';
718
719
	// Initialize everything...
720
	initialize_inputs();
721
722
	$utf8 = (empty($modSettings['global_character_set']) ? $txt['lang_character_set'] : $modSettings['global_character_set']) === 'UTF-8';
723
724
	// Get the database going!
725
	if (empty($db_type) || $db_type == 'mysqli')
726
	{
727
		$db_type = 'mysql';
728
		// If overriding $db_type, need to set its settings.php entry too
729
		$changes = array();
730
		$changes['db_type'] = 'mysql';
731
		require_once($sourcedir . '/Subs-Admin.php');
732
		updateSettingsFile($changes);
733
	}
734
735
	if (file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php'))
736
	{
737
		require_once($sourcedir . '/Subs-Db-' . $db_type . '.php');
738
739
		// Make the connection...
740
		if (empty($db_connection))
741
		{
742
			$options = array('non_fatal' => true);
743
			// Add in the port if needed
744
			if (!empty($db_port))
745
				$options['port'] = $db_port;
746
747
			if (!empty($db_mb4))
748
				$options['db_mb4'] = $db_mb4;
749
750
			$db_connection = smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, $db_prefix, $options);
751
		}
752
		else
753
			// If we've returned here, ping/reconnect to be safe
754
			$smcFunc['db_ping']($db_connection);
755
756
		// Oh dear god!!
757
		if ($db_connection === null)
758
		{
759
			// Get error info...  Recast just in case we get false or 0...
760
			$error_message = $smcFunc['db_connect_error']();
761
			if (empty($error_message))
762
				$error_message = '';
763
			$error_number = $smcFunc['db_connect_errno']();
764
			if (empty($error_number))
765
				$error_number = '';
766
			$db_error = (!empty($error_number) ? $error_number . ': ' : '') . $error_message;
767
768
			die($txt['error_db_connect_settings'] . '<br><br>' . $db_error);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

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

Loading history...
769
		}
770
771
		if ($db_type == 'mysql' && isset($db_character_set) && preg_match('~^\w+$~', $db_character_set) === 1)
772
			$smcFunc['db_query']('', '
773
				SET NAMES {string:db_character_set}',
774
				array(
775
					'db_error_skip' => true,
776
					'db_character_set' => $db_character_set,
777
				)
778
			);
779
780
		// Load the modSettings data...
781
		$request = $smcFunc['db_query']('', '
782
			SELECT variable, value
783
			FROM {db_prefix}settings',
784
			array(
785
				'db_error_skip' => true,
786
			)
787
		);
788
		$modSettings = array();
789
		while ($row = $smcFunc['db_fetch_assoc']($request))
790
			$modSettings[$row['variable']] = $row['value'];
791
		$smcFunc['db_free_result']($request);
792
	}
793
	else
794
		return throw_error(sprintf($txt['error_sourcefile_missing'], 'Subs-Db-' . $db_type . '.php'));
0 ignored issues
show
Bug introduced by
The function throw_error was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

794
		return /** @scrutinizer ignore-call */ throw_error(sprintf($txt['error_sourcefile_missing'], 'Subs-Db-' . $db_type . '.php'));
Loading history...
795
796
	// If they don't have the file, they're going to get a warning anyway so we won't need to clean request vars.
797
	if (file_exists($sourcedir . '/QueryString.php') && php_version_check())
798
	{
799
		require_once($sourcedir . '/QueryString.php');
800
		cleanRequest();
801
	}
802
803
	if (!isset($_GET['substep']))
804
		$_GET['substep'] = 0;
805
}
806
807
function initialize_inputs()
808
{
809
	global $start_time, $db_type, $upgrade_path;
810
811
	$start_time = time();
812
813
	umask(0);
814
815
	ob_start();
816
817
	// Better to upgrade cleanly and fall apart than to screw everything up if things take too long.
818
	ignore_user_abort(true);
819
820
	// This is really quite simple; if ?delete is on the URL, delete the upgrader...
821
	if (isset($_GET['delete']))
822
	{
823
		deleteFile(__FILE__);
0 ignored issues
show
Bug introduced by
The function deleteFile was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

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

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

905
		return /** @scrutinizer ignore-call */ throw_error($txt['error_upgrade_files_missing']);
Loading history...
906
907
	// Do they meet the install requirements?
908
	if (!php_version_check())
909
		return throw_error($txt['error_php_too_low']);
910
911
	if (!db_version_check())
912
		return throw_error(sprintf($txt['error_db_too_low'], $databases[$db_type]['name']));
913
914
	// Do some checks to make sure they have proper privileges
915
	db_extend('packages');
916
917
	// CREATE
918
	$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');
919
920
	// ALTER
921
	$alter = $smcFunc['db_add_column']('{db_prefix}priv_check', array('name' => 'txt', 'type' => 'varchar', 'size' => 4, 'null' => false, 'default' => ''));
922
923
	// DROP
924
	$drop = $smcFunc['db_drop_table']('{db_prefix}priv_check');
925
926
	// Sorry... we need CREATE, ALTER and DROP
927
	if (!$create || !$alter || !$drop)
928
		return throw_error(sprintf($txt['error_db_privileges'], $databases[$db_type]['name']));
929
930
	// Do a quick version spot check.
931
	$temp = substr(@implode('', @file($boarddir . '/index.php')), 0, 4096);
0 ignored issues
show
Bug introduced by
It seems like @file($boarddir . '/index.php') can also be of type false; however, parameter $pieces of implode() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

931
	$temp = substr(@implode('', /** @scrutinizer ignore-type */ @file($boarddir . '/index.php')), 0, 4096);
Loading history...
932
	preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $temp, $match);
933
	if (empty($match[1]) || (trim($match[1]) != SMF_VERSION))
934
		return throw_error($txt['error_upgrade_old_files']);
935
936
	// What absolutely needs to be writable?
937
	$writable_files = array(
938
		$boarddir . '/Settings.php',
939
		$boarddir . '/Settings_bak.php',
940
	);
941
942
	// Only check for minified writable files if we have it enabled or not set.
943
	if (!empty($modSettings['minimize_files']) || !isset($modSettings['minimize_files']))
944
		$writable_files += array(
945
			$modSettings['theme_dir'] . '/css/minified.css',
946
			$modSettings['theme_dir'] . '/scripts/minified.js',
947
			$modSettings['theme_dir'] . '/scripts/minified_deferred.js',
948
		);
949
950
	// Do we need to add this setting?
951
	$need_settings_update = empty($modSettings['custom_avatar_dir']);
952
953
	$custom_av_dir = !empty($modSettings['custom_avatar_dir']) ? $modSettings['custom_avatar_dir'] : $GLOBALS['boarddir'] . '/custom_avatar';
954
	$custom_av_url = !empty($modSettings['custom_avatar_url']) ? $modSettings['custom_avatar_url'] : $boardurl . '/custom_avatar';
955
956
	// This little fellow has to cooperate...
957
	quickFileWritable($custom_av_dir);
0 ignored issues
show
Bug introduced by
The function quickFileWritable was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

957
	/** @scrutinizer ignore-call */ 
958
 quickFileWritable($custom_av_dir);
Loading history...
958
959
	// Are we good now?
960
	if (!is_writable($custom_av_dir))
961
		return throw_error(sprintf($txt['error_dir_not_writable'], $custom_av_dir));
962
	elseif ($need_settings_update)
963
	{
964
		if (!function_exists('cache_put_data'))
965
			require_once($sourcedir . '/Load.php');
966
967
		updateSettings(array('custom_avatar_dir' => $custom_av_dir));
968
		updateSettings(array('custom_avatar_url' => $custom_av_url));
969
	}
970
971
	require_once($sourcedir . '/Security.php');
972
973
	// Check the cache directory.
974
	$cachedir_temp = empty($cachedir) ? $boarddir . '/cache' : $cachedir;
975
	if (!file_exists($cachedir_temp))
976
		@mkdir($cachedir_temp);
977
978
	if (!file_exists($cachedir_temp))
979
		return throw_error($txt['error_cache_not_found']);
980
981
	quickFileWritable($cachedir_temp . '/db_last_error.php');
982
983
	if (!file_exists($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php'))
984
		return throw_error(sprintf($txt['error_lang_index_missing'], $upcontext['language'], $upgradeurl));
985
	elseif (!isset($_GET['skiplang']))
986
	{
987
		$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php')), 0, 4096);
988
		preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
989
990
		if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
991
			return throw_error(sprintf($txt['error_upgrade_old_lang_files'], $upcontext['language'], $upgradeurl));
992
	}
993
994
	if (!makeFilesWritable($writable_files))
0 ignored issues
show
Bug introduced by
The function makeFilesWritable was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

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

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

1164
						return /** @scrutinizer ignore-call */ throw_error($txt['error_not_admin']);
Loading history...
1165
					$smcFunc['db_free_result']($request);
1166
				}
1167
1168
				$upcontext['user']['id'] = $id_member;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $id_member does not seem to be defined for all execution paths leading up to this point.
Loading history...
1169
				$upcontext['user']['name'] = $name;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $name does not seem to be defined for all execution paths leading up to this point.
Loading history...
1170
			}
1171
			else
1172
			{
1173
				$upcontext['user']['id'] = 1;
1174
				$upcontext['user']['name'] = 'Administrator';
1175
			}
1176
1177
			if (!is_callable('random_int'))
1178
				require_once('Sources/random_compat/random.php');
1179
1180
			$upcontext['user']['pass'] = random_int(0, 60000);
1181
			// This basically is used to match the GET variables to Settings.php.
1182
			$upcontext['upgrade_status']['pass'] = $upcontext['user']['pass'];
1183
1184
			// Set the language to that of the user?
1185
			if (isset($user_language) && $user_language != $upcontext['language'] && file_exists($modSettings['theme_dir'] . '/languages/index.' . basename($user_language, '.lng') . '.php'))
1186
			{
1187
				$user_language = basename($user_language, '.lng');
1188
				$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $user_language . '.php')), 0, 4096);
0 ignored issues
show
Bug introduced by
It seems like @file($modSettings['them...user_language . '.php') can also be of type false; however, parameter $pieces of implode() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

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

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

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

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

Loading history...
1426
	}
1427
1428
	// Are we doing debug?
1429
	if (isset($_POST['debug']))
1430
	{
1431
		$upcontext['upgrade_status']['debug'] = true;
1432
		$is_debug = true;
1433
	}
1434
1435
	// If we're not backing up then jump one.
1436
	if (empty($_POST['backup']))
1437
		$upcontext['current_step']++;
1438
1439
	// If we've got here then let's proceed to the next step!
1440
	return true;
1441
}
1442
1443
// Backup the database - why not...
1444
function BackupDatabase()
1445
{
1446
	global $upcontext, $db_prefix, $command_line, $support_js, $file_steps, $smcFunc, $txt;
1447
1448
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'backup_xml' : 'backup_database';
1449
	$upcontext['page_title'] = $txt['backup_database'];
1450
1451
	// Done it already - js wise?
1452
	if (!empty($_POST['backup_done']))
1453
		return true;
1454
1455
	// We cannot execute this step in strict mode - strict mode data fixes are not applied yet
1456
	setSqlMode(false);
1457
1458
	// Some useful stuff here.
1459
	db_extend();
1460
1461
	// Might need this as well
1462
	db_extend('packages');
1463
1464
	// Get all the table names.
1465
	$filter = str_replace('_', '\_', preg_match('~^`(.+?)`\.(.+?)$~', $db_prefix, $match) != 0 ? $match[2] : $db_prefix) . '%';
1466
	$db = preg_match('~^`(.+?)`\.(.+?)$~', $db_prefix, $match) != 0 ? strtr($match[1], array('`' => '')) : false;
1467
	$tables = $smcFunc['db_list_tables']($db, $filter);
1468
1469
	$table_names = array();
1470
	foreach ($tables as $table)
1471
		if (substr($table, 0, 7) !== 'backup_')
1472
			$table_names[] = $table;
1473
1474
	$upcontext['table_count'] = count($table_names);
1475
	$upcontext['cur_table_num'] = $_GET['substep'];
1476
	$upcontext['cur_table_name'] = str_replace($db_prefix, '', isset($table_names[$_GET['substep']]) ? $table_names[$_GET['substep']] : $table_names[0]);
1477
	$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
1478
	// For non-java auto submit...
1479
	$file_steps = $upcontext['table_count'];
1480
1481
	// What ones have we already done?
1482
	foreach ($table_names as $id => $table)
1483
		if ($id < $_GET['substep'])
1484
			$upcontext['previous_tables'][] = $table;
1485
1486
	if ($command_line)
1487
		echo 'Backing Up Tables.';
1488
1489
	// If we don't support javascript we backup here.
1490
	if (!$support_js || isset($_GET['xml']))
1491
	{
1492
		// Backup each table!
1493
		for ($substep = $_GET['substep'], $n = count($table_names); $substep < $n; $substep++)
1494
		{
1495
			$upcontext['cur_table_name'] = str_replace($db_prefix, '', (isset($table_names[$substep + 1]) ? $table_names[$substep + 1] : $table_names[$substep]));
1496
			$upcontext['cur_table_num'] = $substep + 1;
1497
1498
			$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
1499
1500
			// Do we need to pause?
1501
			nextSubstep($substep);
1502
1503
			backupTable($table_names[$substep]);
1504
1505
			// If this is XML to keep it nice for the user do one table at a time anyway!
1506
			if (isset($_GET['xml']))
1507
				return upgradeExit();
0 ignored issues
show
Bug introduced by
Are you sure the usage of upgradeExit() is correct as it seems to always return null.

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

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

}

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

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

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

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

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

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

}

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

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

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

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

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

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

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

Loading history...
1792
	}
1793
1794
	// Make sure it says we're done.
1795
	$upcontext['overall_percent'] = 100;
1796
	if (isset($upcontext['step_progress']))
1797
		unset($upcontext['step_progress']);
1798
1799
	$_GET['substep'] = 0;
1800
	return false;
1801
}
1802
1803
// Just like the built in one, but setup for CLI to not use themes.
1804
function cli_scheduled_fetchSMfiles()
1805
{
1806
	global $sourcedir, $language, $modSettings, $smcFunc;
1807
1808
	if (empty($modSettings['time_format']))
1809
		$modSettings['time_format'] = '%B %d, %Y, %I:%M:%S %p';
1810
1811
	// What files do we want to get
1812
	$request = $smcFunc['db_query']('', '
1813
		SELECT id_file, filename, path, parameters
1814
		FROM {db_prefix}admin_info_files',
1815
		array(
1816
		)
1817
	);
1818
1819
	$js_files = array();
1820
	while ($row = $smcFunc['db_fetch_assoc']($request))
1821
	{
1822
		$js_files[$row['id_file']] = array(
1823
			'filename' => $row['filename'],
1824
			'path' => $row['path'],
1825
			'parameters' => sprintf($row['parameters'], $language, urlencode($modSettings['time_format']), urlencode(SMF_FULL_VERSION)),
1826
		);
1827
	}
1828
	$smcFunc['db_free_result']($request);
1829
1830
	// We're gonna need fetch_web_data() to pull this off.
1831
	require_once($sourcedir . '/Subs.php');
1832
1833
	foreach ($js_files as $ID_FILE => $file)
1834
	{
1835
		// Create the url
1836
		$server = empty($file['path']) || substr($file['path'], 0, 7) != 'http://' ? 'https://www.simplemachines.org' : '';
1837
		$url = $server . (!empty($file['path']) ? $file['path'] : $file['path']) . $file['filename'] . (!empty($file['parameters']) ? '?' . $file['parameters'] : '');
1838
1839
		// Get the file
1840
		$file_data = fetch_web_data($url);
1841
1842
		// If we got an error - give up - the site might be down.
1843
		if ($file_data === false)
1844
			return throw_error(sprintf('Could not retrieve the file %1$s.', $url));
0 ignored issues
show
Bug introduced by
The function throw_error was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

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

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

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

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

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

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

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

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

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

Loading history...
2487
	$table_row = $smcFunc['db_fetch_assoc']($request);
2488
	$smcFunc['db_free_result']($request);
2489
2490
	// If something of the current column definition is different, fix it.
2491
	$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']);
2492
2493
	// Columns that previously allowed null, need to be converted first.
2494
	$null_fix = strtolower($table_row['Null']) === 'yes' && !$change['null_allowed'];
2495
2496
	// Get the character set that goes with the collation of the column.
2497
	if ($column_fix && !empty($table_row['Collation']))
2498
	{
2499
		$request = $smcFunc['db_query']('', '
2500
			SHOW COLLATION
2501
			LIKE {string:collation}',
2502
			array(
2503
				'collation' => $table_row['Collation'],
2504
				'db_error_skip' => true,
2505
			)
2506
		);
2507
		// No results? Just forget it all together.
2508
		if ($smcFunc['db_num_rows']($request) === 0)
2509
			unset($table_row['Collation']);
2510
		else
2511
			$collation_info = $smcFunc['db_fetch_assoc']($request);
2512
		$smcFunc['db_free_result']($request);
2513
	}
2514
2515
	if ($column_fix)
2516
	{
2517
		// Make sure there are no NULL's left.
2518
		if ($null_fix)
2519
			$smcFunc['db_query']('', '
2520
				UPDATE {db_prefix}' . $change['table'] . '
2521
				SET ' . $change['column'] . ' = {string:default}
2522
				WHERE ' . $change['column'] . ' IS NULL',
2523
				array(
2524
					'default' => isset($change['default']) ? $change['default'] : '',
2525
					'db_error_skip' => true,
2526
				)
2527
			);
2528
2529
		// Do the actual alteration.
2530
		$smcFunc['db_query']('', '
2531
			ALTER TABLE {db_prefix}' . $change['table'] . '
2532
			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}' : ''),
2533
			array(
2534
				'default' => isset($change['default']) ? $change['default'] : '',
2535
				'db_error_skip' => true,
2536
			)
2537
		);
2538
	}
2539
	nextSubstep($substep);
2540
}
2541
2542
// Check if we need to alter this query.
2543
function checkChange(&$change)
2544
{
2545
	global $smcFunc, $db_type, $databases;
2546
	static $database_version, $where_field_support;
2547
2548
	// Attempt to find a database_version.
2549
	if (empty($database_version))
2550
	{
2551
		$database_version = $databases[$db_type]['version_check']();
2552
		$where_field_support = $db_type == 'mysql' && version_compare('5.0', $database_version, '<=');
2553
	}
2554
2555
	// Not a column we need to check on?
2556
	if (!in_array($change['name'], array('memberGroups', 'passwordSalt')))
2557
		return;
2558
2559
	// Break it up you (six|seven).
2560
	$temp = explode(' ', str_replace('NOT NULL', 'NOT_NULL', $change['text']));
2561
2562
	// Can we support a shortcut method?
2563
	if ($where_field_support)
2564
	{
2565
		// Get the details about this change.
2566
		$request = $smcFunc['db_query']('', '
2567
			SHOW FIELDS
2568
			FROM {db_prefix}{raw:table}
2569
			WHERE Field = {string:old_name} OR Field = {string:new_name}',
2570
			array(
2571
				'table' => $change['table'],
2572
				'old_name' => $temp[1],
2573
				'new_name' => $temp[2],
2574
			)
2575
		);
2576
		// !!! This doesn't technically work because we don't pass request into it, but it hasn't broke anything yet.
2577
		if ($smcFunc['db_num_rows'] != 1)
2578
			return;
2579
2580
		list (, $current_type) = $smcFunc['db_fetch_assoc']($request);
2581
		$smcFunc['db_free_result']($request);
2582
	}
2583
	else
2584
	{
2585
		// Do this the old fashion, sure method way.
2586
		$request = $smcFunc['db_query']('', '
2587
			SHOW FIELDS
2588
			FROM {db_prefix}{raw:table}',
2589
			array(
2590
				'table' => $change['table'],
2591
			)
2592
		);
2593
		// Mayday!
2594
		// !!! This doesn't technically work because we don't pass request into it, but it hasn't broke anything yet.
2595
		if ($smcFunc['db_num_rows'] == 0)
2596
			return;
2597
2598
		// Oh where, oh where has my little field gone. Oh where can it be...
2599
		while ($row = $smcFunc['db_query']($request))
2600
			if ($row['Field'] == $temp[1] || $row['Field'] == $temp[2])
2601
			{
2602
				$current_type = $row['Type'];
2603
				break;
2604
			}
2605
	}
2606
2607
	// If this doesn't match, the column may of been altered for a reason.
2608
	if (trim($current_type) != trim($temp[3]))
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $current_type does not seem to be defined for all execution paths leading up to this point.
Loading history...
2609
		$temp[3] = $current_type;
2610
2611
	// Piece this back together.
2612
	$change['text'] = str_replace('NOT_NULL', 'NOT NULL', implode(' ', $temp));
2613
}
2614
2615
// The next substep.
2616
function nextSubstep($substep)
2617
{
2618
	global $start_time, $timeLimitThreshold, $command_line, $custom_warning;
2619
	global $step_progress, $is_debug, $upcontext;
2620
2621
	if ($_GET['substep'] < $substep)
2622
		$_GET['substep'] = $substep;
2623
2624
	if ($command_line)
2625
	{
2626
		if (time() - $start_time > 1 && empty($is_debug))
2627
		{
2628
			echo '.';
2629
			$start_time = time();
2630
		}
2631
		return;
2632
	}
2633
2634
	@set_time_limit(300);
2635
	if (function_exists('apache_reset_timeout'))
2636
		@apache_reset_timeout();
2637
2638
	if (time() - $start_time <= $timeLimitThreshold)
2639
		return;
2640
2641
	// Do we have some custom step progress stuff?
2642
	if (!empty($step_progress))
2643
	{
2644
		$upcontext['substep_progress'] = 0;
2645
		$upcontext['substep_progress_name'] = $step_progress['name'];
2646
		if ($step_progress['current'] > $step_progress['total'])
2647
			$upcontext['substep_progress'] = 99.9;
2648
		else
2649
			$upcontext['substep_progress'] = ($step_progress['current'] / $step_progress['total']) * 100;
2650
2651
		// Make it nicely rounded.
2652
		$upcontext['substep_progress'] = round($upcontext['substep_progress'], 1);
2653
	}
2654
2655
	// If this is XML we just exit right away!
2656
	if (isset($_GET['xml']))
2657
		return upgradeExit();
0 ignored issues
show
Bug introduced by
Are you sure the usage of upgradeExit() is correct as it seems to always return null.

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

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

}

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

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

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

Loading history...
2658
2659
	// We're going to pause after this!
2660
	$upcontext['pause'] = true;
2661
2662
	$upcontext['query_string'] = '';
2663
	foreach ($_GET as $k => $v)
2664
	{
2665
		if ($k != 'data' && $k != 'substep' && $k != 'step')
2666
			$upcontext['query_string'] .= ';' . $k . '=' . $v;
2667
	}
2668
2669
	// Custom warning?
2670
	if (!empty($custom_warning))
2671
		$upcontext['custom_warning'] = $custom_warning;
2672
2673
	upgradeExit();
2674
}
2675
2676
function cmdStep0()
2677
{
2678
	global $boarddir, $sourcedir, $modSettings, $start_time, $cachedir, $databases, $db_type, $smcFunc, $upcontext;
2679
	global $is_debug, $boardurl, $txt;
2680
	$start_time = time();
2681
2682
	while (ob_get_level() > 0)
2683
		ob_end_clean();
2684
	ob_implicit_flush(1);
2685
	@set_time_limit(600);
2686
2687
	if (!isset($_SERVER['argv']))
2688
		$_SERVER['argv'] = array();
2689
	$_GET['maint'] = 1;
2690
2691
	foreach ($_SERVER['argv'] as $i => $arg)
2692
	{
2693
		if (preg_match('~^--language=(.+)$~', $arg, $match) != 0)
2694
			$upcontext['lang'] = $match[1];
2695
		elseif (preg_match('~^--path=(.+)$~', $arg) != 0)
2696
			continue;
2697
		elseif ($arg == '--no-maintenance')
2698
			$_GET['maint'] = 0;
2699
		elseif ($arg == '--debug')
2700
			$is_debug = true;
2701
		elseif ($arg == '--backup')
2702
			$_POST['backup'] = 1;
2703
		elseif ($arg == '--rebuild-settings')
2704
			$_POST['migrateSettings'] = 1;
2705
		elseif ($arg == '--allow-stats')
2706
			$_POST['stats'] = 1;
2707
		elseif ($arg == '--template' && (file_exists($boarddir . '/template.php') || file_exists($boarddir . '/template.html') && !file_exists($modSettings['theme_dir'] . '/converted')))
2708
			$_GET['conv'] = 1;
2709
		elseif ($i != 0)
2710
		{
2711
			echo 'SMF Command-line Upgrader
2712
Usage: /path/to/php -f ' . basename(__FILE__) . ' -- [OPTION]...
2713
2714
	--path=/path/to/SMF     Specify custom SMF root directory.
2715
	--language=LANG         Reset the forum\'s language to LANG.
2716
	--no-maintenance        Don\'t put the forum into maintenance mode.
2717
	--debug                 Output debugging information.
2718
	--backup                Create backups of tables with "backup_" prefix.
2719
	--allow-stats           Allow Simple Machines stat collection
2720
	--rebuild-settings      Rebuild the Settings.php file';
2721
			echo "\n";
2722
			exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

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

Loading history...
2723
		}
2724
	}
2725
2726
	if (!php_version_check())
2727
		print_error('Error: PHP ' . PHP_VERSION . ' does not match version requirements.', true);
0 ignored issues
show
Bug introduced by
The function print_error was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

2727
		/** @scrutinizer ignore-call */ 
2728
  print_error('Error: PHP ' . PHP_VERSION . ' does not match version requirements.', true);
Loading history...
2728
	if (!db_version_check())
2729
		print_error('Error: ' . $databases[$db_type]['name'] . ' ' . $databases[$db_type]['version'] . ' does not match minimum requirements.', true);
2730
2731
	// Do some checks to make sure they have proper privileges
2732
	db_extend('packages');
2733
2734
	// CREATE
2735
	$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');
2736
2737
	// ALTER
2738
	$alter = $smcFunc['db_add_column']('{db_prefix}priv_check', array('name' => 'txt', 'type' => 'varchar', 'size' => 4, 'null' => false, 'default' => ''));
2739
2740
	// DROP
2741
	$drop = $smcFunc['db_drop_table']('{db_prefix}priv_check');
2742
2743
	// Sorry... we need CREATE, ALTER and DROP
2744
	if (!$create || !$alter || !$drop)
2745
		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);
2746
2747
	$check = @file_exists($modSettings['theme_dir'] . '/index.template.php')
2748
		&& @file_exists($sourcedir . '/QueryString.php')
2749
		&& @file_exists($sourcedir . '/ManageBoards.php');
2750
	if (!$check && !isset($modSettings['smfVersion']))
2751
		print_error('Error: Some files are missing or out-of-date.', true);
2752
2753
	// Do a quick version spot check.
2754
	$temp = substr(@implode('', @file($boarddir . '/index.php')), 0, 4096);
0 ignored issues
show
Bug introduced by
It seems like @file($boarddir . '/index.php') can also be of type false; however, parameter $pieces of implode() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

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

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

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

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

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

}

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

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

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

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

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

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

}

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

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

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

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