Passed
Pull Request — release-2.1 (#5986)
by Jeremy
05:17
created

MySQLConvertOldIp()   F

Complexity

Conditions 16
Paths 488

Size

Total Lines 140
Code Lines 78

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 16
eloc 78
nc 488
nop 5
dl 0
loc 140
rs 2.1688
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

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

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

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

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

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

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

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

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

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

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

1118
				$temp = substr(@implode('', /** @scrutinizer ignore-type */ @file($modSettings['theme_dir'] . '/languages/index.' . $user_language . '.php')), 0, 4096);
Loading history...
1119
				preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
1120
1121
				if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
1122
					$upcontext['upgrade_options_warning'] = sprintf($txt['warning_lang_old'], $user_language, $upcontext['language']);
1123
				elseif (!file_exists($modSettings['theme_dir'] . '/languages/Install.' . $user_language . '.php'))
1124
					$upcontext['upgrade_options_warning'] = sprintf($txt['warning_lang_missing'], $user_language, $upcontext['language']);
1125
				else
1126
				{
1127
					// Set this as the new language.
1128
					$upcontext['language'] = $user_language;
1129
					$upcontext['upgrade_status']['lang'] = $upcontext['language'];
1130
1131
					// Include the file.
1132
					load_lang_file();
1133
				}
1134
			}
1135
1136
			// If we're resuming set the step and substep to be correct.
1137
			if (isset($_POST['cont']))
1138
			{
1139
				$upcontext['current_step'] = $upcontext['user']['step'];
1140
				$_GET['substep'] = $upcontext['user']['substep'];
1141
			}
1142
1143
			return true;
1144
		}
1145
	}
1146
1147
	return false;
1148
}
1149
1150
// Step 1: Do the maintenance and backup.
1151
function UpgradeOptions()
1152
{
1153
	global $db_prefix, $command_line, $modSettings, $is_debug, $smcFunc, $packagesdir, $tasksdir, $language, $txt, $db_port;
1154
	global $boarddir, $boardurl, $sourcedir, $maintenance, $cachedir, $upcontext, $db_type, $db_server, $image_proxy_enabled;
1155
1156
	$upcontext['sub_template'] = 'upgrade_options';
1157
	$upcontext['page_title'] = $txt['upgrade_options'];
1158
1159
	db_extend('packages');
1160
	$upcontext['karma_installed'] = array('good' => false, 'bad' => false);
1161
	$member_columns = $smcFunc['db_list_columns']('{db_prefix}members');
1162
1163
	$upcontext['karma_installed']['good'] = in_array('karma_good', $member_columns);
1164
	$upcontext['karma_installed']['bad'] = in_array('karma_bad', $member_columns);
1165
1166
	$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', '<');
1167
1168
	unset($member_columns);
1169
1170
	// If we've not submitted then we're done.
1171
	if (empty($_POST['upcont']))
1172
		return false;
1173
1174
	// Firstly, if they're enabling SM stat collection just do it.
1175
	if (!empty($_POST['stats']) && substr($boardurl, 0, 16) != 'http://localhost' && empty($modSettings['allow_sm_stats']) && empty($modSettings['enable_sm_stats']))
1176
	{
1177
		$upcontext['allow_sm_stats'] = true;
1178
1179
		// Don't register if we still have a key.
1180
		if (empty($modSettings['sm_stats_key']))
1181
		{
1182
			// Attempt to register the site etc.
1183
			$fp = @fsockopen('www.simplemachines.org', 80, $errno, $errstr);
1184
			if ($fp)
0 ignored issues
show
introduced by
$fp is of type false|resource, thus it always evaluated to false.
Loading history...
1185
			{
1186
				$out = 'GET /smf/stats/register_stats.php?site=' . base64_encode($boardurl) . ' HTTP/1.1' . "\r\n";
1187
				$out .= 'Host: www.simplemachines.org' . "\r\n";
1188
				$out .= 'Connection: Close' . "\r\n\r\n";
1189
				fwrite($fp, $out);
1190
1191
				$return_data = '';
1192
				while (!feof($fp))
1193
					$return_data .= fgets($fp, 128);
1194
1195
				fclose($fp);
1196
1197
				// Get the unique site ID.
1198
				preg_match('~SITE-ID:\s(\w{10})~', $return_data, $ID);
1199
1200
				if (!empty($ID[1]))
1201
					$smcFunc['db_insert']('replace',
1202
						$db_prefix . 'settings',
1203
						array('variable' => 'string', 'value' => 'string'),
1204
						array(
1205
							array('sm_stats_key', $ID[1]),
1206
							array('enable_sm_stats', 1),
1207
						),
1208
						array('variable')
1209
					);
1210
			}
1211
		}
1212
		else
1213
		{
1214
			$smcFunc['db_insert']('replace',
1215
				$db_prefix . 'settings',
1216
				array('variable' => 'string', 'value' => 'string'),
1217
				array('enable_sm_stats', 1),
1218
				array('variable')
1219
			);
1220
		}
1221
	}
1222
	// Don't remove stat collection unless we unchecked the box for real, not from the loop.
1223
	elseif (empty($_POST['stats']) && empty($upcontext['allow_sm_stats']))
1224
		$smcFunc['db_query']('', '
1225
			DELETE FROM {db_prefix}settings
1226
			WHERE variable = {string:enable_sm_stats}',
1227
			array(
1228
				'enable_sm_stats' => 'enable_sm_stats',
1229
				'db_error_skip' => true,
1230
			)
1231
		);
1232
1233
	// Deleting old karma stuff?
1234
	if (!empty($_POST['delete_karma']))
1235
	{
1236
		// Delete old settings vars.
1237
		$smcFunc['db_query']('', '
1238
			DELETE FROM {db_prefix}settings
1239
			WHERE variable IN ({array_string:karma_vars})',
1240
			array(
1241
				'karma_vars' => array('karmaMode', 'karmaTimeRestrictAdmins', 'karmaWaitTime', 'karmaMinPosts', 'karmaLabel', 'karmaSmiteLabel', 'karmaApplaudLabel'),
1242
			)
1243
		);
1244
1245
		// Cleaning up old karma member settings.
1246
		if ($upcontext['karma_installed']['good'])
1247
			$smcFunc['db_query']('', '
1248
				ALTER TABLE {db_prefix}members
1249
				DROP karma_good',
1250
				array()
1251
			);
1252
1253
		// Does karma bad was enable?
1254
		if ($upcontext['karma_installed']['bad'])
1255
			$smcFunc['db_query']('', '
1256
				ALTER TABLE {db_prefix}members
1257
				DROP karma_bad',
1258
				array()
1259
			);
1260
1261
		// Cleaning up old karma permissions.
1262
		$smcFunc['db_query']('', '
1263
			DELETE FROM {db_prefix}permissions
1264
			WHERE permission = {string:karma_vars}',
1265
			array(
1266
				'karma_vars' => 'karma_edit',
1267
			)
1268
		);
1269
		// Cleaning up old log_karma table
1270
		$smcFunc['db_query']('', '
1271
			DROP TABLE IF EXISTS {db_prefix}log_karma',
1272
			array()
1273
		);
1274
	}
1275
1276
	// Emptying the error log?
1277
	if (!empty($_POST['empty_error']))
1278
		$smcFunc['db_query']('truncate_table', '
1279
			TRUNCATE {db_prefix}log_errors',
1280
			array(
1281
			)
1282
		);
1283
1284
	$changes = array();
1285
1286
	// Add proxy settings.
1287
	if (!isset($GLOBALS['image_proxy_secret']) || $GLOBALS['image_proxy_secret'] == 'smfisawesome')
1288
		$changes['image_proxy_secret'] = substr(sha1(mt_rand()), 0, 20);
1289
	if (!isset($GLOBALS['image_proxy_maxsize']))
1290
		$changes['image_proxy_maxsize'] = 5190;
1291
	if (!isset($GLOBALS['image_proxy_enabled']))
1292
		$changes['image_proxy_enabled'] = false;
1293
1294
	// If $boardurl reflects https, set force_ssl
1295
	if (!function_exists('cache_put_data'))
1296
		require_once($sourcedir . '/Load.php');
1297
	if (stripos($boardurl, 'https://') !== false)
1298
		updateSettings(array('force_ssl' => '1'));
1299
1300
	// If we're overriding the language follow it through.
1301
	if (isset($upcontext['lang']) && file_exists($modSettings['theme_dir'] . '/languages/index.' . $upcontext['lang'] . '.php'))
1302
		$changes['language'] = $upcontext['lang'];
1303
1304
	if (!empty($_POST['maint']))
1305
	{
1306
		$changes['maintenance'] = 2;
1307
		// Remember what it was...
1308
		$upcontext['user']['main'] = $maintenance;
1309
1310
		if (!empty($_POST['maintitle']))
1311
		{
1312
			$changes['mtitle'] = $_POST['maintitle'];
1313
			$changes['mmessage'] = $_POST['mainmessage'];
1314
		}
1315
		else
1316
		{
1317
			$changes['mtitle'] = $txt['mtitle'];
1318
			$changes['mmessage'] = $txt['mmessage'];
1319
		}
1320
	}
1321
1322
	if ($command_line)
1323
		echo ' * Updating Settings.php...';
1324
1325
	// Fix some old paths.
1326
	if (substr($boarddir, 0, 1) == '.')
1327
		$changes['boarddir'] = fixRelativePath($boarddir);
1328
1329
	if (substr($sourcedir, 0, 1) == '.')
1330
		$changes['sourcedir'] = fixRelativePath($sourcedir);
1331
1332
	if (empty($cachedir) || substr($cachedir, 0, 1) == '.')
1333
		$changes['cachedir'] = fixRelativePath($boarddir) . '/cache';
1334
1335
	// Migrate cache settings.
1336
	// Accelerator setting didn't exist previously; use 'smf' file based caching as default if caching had been enabled.
1337
	if (!isset($GLOBALS['cache_enable']))
1338
		$changes += array(
1339
			'cache_accelerator' => !empty($modSettings['cache_enable']) ? 'smf' : '',
1340
			'cache_enable' => !empty($modSettings['cache_enable']) ? $modSettings['cache_enable'] : 0,
1341
			'cache_memcached' => !empty($modSettings['cache_memcached']) ? $modSettings['cache_memcached'] : '',
1342
		);
1343
1344
	// If they have a "host:port" setup for the host, split that into separate values
1345
	// You should never have a : in the hostname if you're not on MySQL, but better safe than sorry
1346
	if (strpos($db_server, ':') !== false && $db_type == 'mysql')
1347
	{
1348
		list ($db_server, $db_port) = explode(':', $db_server);
1349
1350
		$changes['db_server'] = $db_server;
1351
1352
		// Only set this if we're not using the default port
1353
		if ($db_port != ini_get('mysqli.default_port'))
1354
			$changes['db_port'] = (int) $db_port;
1355
	}
1356
1357
	// If db_port is set and is the same as the default, set it to 0.
1358
	if (!empty($db_port))
1359
	{
1360
		if ($db_type == 'mysql' && $db_port == ini_get('mysqli.default_port'))
1361
			$changes['db_port'] = 0;
1362
		elseif ($db_type == 'postgresql' && $db_port == 5432)
1363
			$changes['db_port'] = 0;
1364
	}
1365
1366
	// Maybe we haven't had this option yet?
1367
	if (empty($packagesdir))
1368
		$changes['packagesdir'] = fixRelativePath($boarddir) . '/Packages';
1369
1370
	// Add support for $tasksdir var.
1371
	if (empty($tasksdir))
1372
		$changes['tasksdir'] = fixRelativePath($sourcedir) . '/tasks';
1373
1374
	// Make sure we fix the language as well.
1375
	if (stristr($language, '-utf8'))
1376
		$changes['language'] = str_ireplace('-utf8', '', $language);
1377
1378
	// @todo Maybe change the cookie name if going to 1.1, too?
1379
1380
	// Ensure this doesn't get lost in translation.
1381
	$changes['upgradeData'] = base64_encode(json_encode($upcontext['user']));
1382
1383
	// Update Settings.php with the new settings, and rebuild if they selected that option.
1384
	require_once($sourcedir . '/Subs.php');
1385
	require_once($sourcedir . '/Subs-Admin.php');
1386
	updateSettingsFile($changes, false, !empty($_POST['migrateSettings']));
1387
1388
	if ($command_line)
1389
		echo ' Successful.' . "\n";
1390
1391
	// Are we doing debug?
1392
	if (isset($_POST['debug']))
1393
	{
1394
		$upcontext['upgrade_status']['debug'] = true;
1395
		$is_debug = true;
1396
	}
1397
1398
	// If we're not backing up then jump one.
1399
	if (empty($_POST['backup']))
1400
		$upcontext['current_step']++;
1401
1402
	// If we've got here then let's proceed to the next step!
1403
	return true;
1404
}
1405
1406
// Backup the database - why not...
1407
function BackupDatabase()
1408
{
1409
	global $upcontext, $db_prefix, $command_line, $support_js, $file_steps, $smcFunc, $txt;
1410
1411
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'backup_xml' : 'backup_database';
1412
	$upcontext['page_title'] = $txt['backup_database'];
1413
1414
	// Done it already - js wise?
1415
	if (!empty($_POST['backup_done']))
1416
		return true;
1417
1418
	// Some useful stuff here.
1419
	db_extend();
1420
1421
	// Might need this as well
1422
	db_extend('packages');
1423
1424
	// Get all the table names.
1425
	$filter = str_replace('_', '\_', preg_match('~^`(.+?)`\.(.+?)$~', $db_prefix, $match) != 0 ? $match[2] : $db_prefix) . '%';
1426
	$db = preg_match('~^`(.+?)`\.(.+?)$~', $db_prefix, $match) != 0 ? strtr($match[1], array('`' => '')) : false;
1427
	$tables = $smcFunc['db_list_tables']($db, $filter);
1428
1429
	$table_names = array();
1430
	foreach ($tables as $table)
1431
		if (substr($table, 0, 7) !== 'backup_')
1432
			$table_names[] = $table;
1433
1434
	$upcontext['table_count'] = count($table_names);
1435
	$upcontext['cur_table_num'] = $_GET['substep'];
1436
	$upcontext['cur_table_name'] = str_replace($db_prefix, '', isset($table_names[$_GET['substep']]) ? $table_names[$_GET['substep']] : $table_names[0]);
1437
	$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
1438
	// For non-java auto submit...
1439
	$file_steps = $upcontext['table_count'];
1440
1441
	// What ones have we already done?
1442
	foreach ($table_names as $id => $table)
1443
		if ($id < $_GET['substep'])
1444
			$upcontext['previous_tables'][] = $table;
1445
1446
	if ($command_line)
1447
		echo 'Backing Up Tables.';
1448
1449
	// If we don't support javascript we backup here.
1450
	if (!$support_js || isset($_GET['xml']))
1451
	{
1452
		// Backup each table!
1453
		for ($substep = $_GET['substep'], $n = count($table_names); $substep < $n; $substep++)
1454
		{
1455
			$upcontext['cur_table_name'] = str_replace($db_prefix, '', (isset($table_names[$substep + 1]) ? $table_names[$substep + 1] : $table_names[$substep]));
1456
			$upcontext['cur_table_num'] = $substep + 1;
1457
1458
			$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
1459
1460
			// Do we need to pause?
1461
			nextSubstep($substep);
1462
1463
			backupTable($table_names[$substep]);
1464
1465
			// If this is XML to keep it nice for the user do one table at a time anyway!
1466
			if (isset($_GET['xml']))
1467
				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...
1468
		}
1469
1470
		if ($command_line)
1471
		{
1472
			echo "\n" . ' Successful.\'' . "\n";
1473
			flush();
1474
		}
1475
		$upcontext['step_progress'] = 100;
1476
1477
		$_GET['substep'] = 0;
1478
		// Make sure we move on!
1479
		return true;
1480
	}
1481
1482
	// Either way next place to post will be database changes!
1483
	$_GET['substep'] = 0;
1484
	return false;
1485
}
1486
1487
// Backup one table...
1488
function backupTable($table)
1489
{
1490
	global $command_line, $db_prefix, $smcFunc;
1491
1492
	if ($command_line)
1493
	{
1494
		echo "\n" . ' +++ Backing up \"' . str_replace($db_prefix, '', $table) . '"...';
1495
		flush();
1496
	}
1497
1498
	$smcFunc['db_backup_table']($table, 'backup_' . $table);
1499
1500
	if ($command_line)
1501
		echo ' done.';
1502
}
1503
1504
// Step 2: Everything.
1505
function DatabaseChanges()
1506
{
1507
	global $db_prefix, $modSettings, $smcFunc, $txt;
1508
	global $upcontext, $support_js, $db_type;
1509
1510
	// Have we just completed this?
1511
	if (!empty($_POST['database_done']))
1512
		return true;
1513
1514
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'database_xml' : 'database_changes';
1515
	$upcontext['page_title'] = $txt['database_changes'];
1516
1517
	// All possible files.
1518
	// Name, < version, insert_on_complete
1519
	// Last entry in array indicates whether to use sql_mode of STRICT or not.
1520
	$files = array(
1521
		array('upgrade_1-0.sql', '1.1', '1.1 RC0', false),
1522
		array('upgrade_1-1.sql', '2.0', '2.0 a', false),
1523
		array('upgrade_2-0_' . $db_type . '.sql', '2.1', '2.1 dev0', false),
1524
		array('upgrade_2-1_' . $db_type . '.sql', '3.0', SMF_VERSION, true),
1525
	);
1526
1527
	// How many files are there in total?
1528
	if (isset($_GET['filecount']))
1529
		$upcontext['file_count'] = (int) $_GET['filecount'];
1530
	else
1531
	{
1532
		$upcontext['file_count'] = 0;
1533
		foreach ($files as $file)
1534
		{
1535
			if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < $file[1])
1536
				$upcontext['file_count']++;
1537
		}
1538
	}
1539
1540
	// Do each file!
1541
	$did_not_do = count($files) - $upcontext['file_count'];
1542
	$upcontext['step_progress'] = 0;
1543
	$upcontext['cur_file_num'] = 0;
1544
	foreach ($files as $file)
1545
	{
1546
		if ($did_not_do)
1547
			$did_not_do--;
1548
		else
1549
		{
1550
			$upcontext['cur_file_num']++;
1551
			$upcontext['cur_file_name'] = $file[0];
1552
			// Do we actually need to do this still?
1553
			if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < $file[1])
1554
			{
1555
				setSqlMode($file[3]);
1556
				$nextFile = parse_sql(dirname(__FILE__) . '/' . $file[0]);
1557
				if ($nextFile)
1558
				{
1559
					// Only update the version of this if complete.
1560
					$smcFunc['db_insert']('replace',
1561
						$db_prefix . 'settings',
1562
						array('variable' => 'string', 'value' => 'string'),
1563
						array('smfVersion', $file[2]),
1564
						array('variable')
1565
					);
1566
1567
					$modSettings['smfVersion'] = $file[2];
1568
				}
1569
1570
				// If this is XML we only do this stuff once.
1571
				if (isset($_GET['xml']))
1572
				{
1573
					// Flag to move on to the next.
1574
					$upcontext['completed_step'] = true;
1575
					// Did we complete the whole file?
1576
					if ($nextFile)
1577
						$upcontext['current_debug_item_num'] = -1;
1578
					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...
1579
				}
1580
				elseif ($support_js)
1581
					break;
1582
			}
1583
			// Set the progress bar to be right as if we had - even if we hadn't...
1584
			$upcontext['step_progress'] = ($upcontext['cur_file_num'] / $upcontext['file_count']) * 100;
1585
		}
1586
	}
1587
1588
	$_GET['substep'] = 0;
1589
	// So the template knows we're done.
1590
	if (!$support_js)
1591
	{
1592
		$upcontext['changes_complete'] = true;
1593
1594
		return true;
1595
	}
1596
	return false;
1597
}
1598
1599
// Different versions of the files use different sql_modes
1600
function setSqlMode($strict = true)
1601
{
1602
	global $db_type, $db_connection;
1603
1604
	if ($db_type != 'mysql')
1605
		return;
1606
1607
	if ($strict)
1608
		$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';
1609
	else
1610
		$mode = '';
1611
1612
	mysqli_query($db_connection, 'SET SESSION sql_mode = \'' . $mode . '\'');
1613
1614
	return;
1615
}
1616
1617
// Delete the damn thing!
1618
function DeleteUpgrade()
1619
{
1620
	global $command_line, $language, $upcontext, $sourcedir;
1621
	global $user_info, $maintenance, $smcFunc, $db_type, $txt, $settings;
1622
1623
	// Now it's nice to have some of the basic SMF source files.
1624
	if (!isset($_GET['ssi']) && !$command_line)
1625
		redirectLocation('&ssi=1');
1626
1627
	$upcontext['sub_template'] = 'upgrade_complete';
1628
	$upcontext['page_title'] = $txt['upgrade_complete'];
1629
1630
	$endl = $command_line ? "\n" : '<br>' . "\n";
1631
1632
	$changes = array(
1633
		'language' => (substr($language, -4) == '.lng' ? substr($language, 0, -4) : $language),
1634
		'db_error_send' => true,
1635
		'upgradeData' => null,
1636
	);
1637
1638
	// Are we in maintenance mode?
1639
	if (isset($upcontext['user']['main']))
1640
	{
1641
		if ($command_line)
1642
			echo ' * ';
1643
		$upcontext['removed_maintenance'] = true;
1644
		$changes['maintenance'] = $upcontext['user']['main'];
1645
	}
1646
	// Otherwise if somehow we are in 2 let's go to 1.
1647
	elseif (!empty($maintenance) && $maintenance == 2)
1648
		$changes['maintenance'] = 1;
1649
1650
	// Wipe this out...
1651
	$upcontext['user'] = array();
1652
1653
	require_once($sourcedir . '/Subs.php');
1654
	require_once($sourcedir . '/Subs-Admin.php');
1655
	updateSettingsFile($changes);
1656
1657
	// Clean any old cache files away.
1658
	upgrade_clean_cache();
1659
1660
	// Can we delete the file?
1661
	$upcontext['can_delete_script'] = is_writable(dirname(__FILE__)) || is_writable(__FILE__);
1662
1663
	// Now is the perfect time to fetch the SM files.
1664
	if ($command_line)
1665
		cli_scheduled_fetchSMfiles();
1666
	else
1667
	{
1668
		require_once($sourcedir . '/ScheduledTasks.php');
1669
		scheduled_fetchSMfiles(); // Now go get those files!
1670
		// This is needed in case someone invokes the upgrader using https when upgrading an http forum
1671
		if (httpsOn())
1672
			$settings['default_theme_url'] = strtr($settings['default_theme_url'], array('http://' => 'https://'));
1673
	}
1674
1675
	// Log what we've done.
1676
	if (empty($user_info['id']))
1677
		$user_info['id'] = !empty($upcontext['user']['id']) ? $upcontext['user']['id'] : 0;
1678
1679
	// Log the action manually, so CLI still works.
1680
	$smcFunc['db_insert']('',
1681
		'{db_prefix}log_actions',
1682
		array(
1683
			'log_time' => 'int', 'id_log' => 'int', 'id_member' => 'int', 'ip' => 'inet', 'action' => 'string',
1684
			'id_board' => 'int', 'id_topic' => 'int', 'id_msg' => 'int', 'extra' => 'string-65534',
1685
		),
1686
		array(
1687
			time(), 3, $user_info['id'], $command_line ? '127.0.0.1' : $user_info['ip'], 'upgrade',
1688
			0, 0, 0, json_encode(array('version' => SMF_FULL_VERSION, 'member' => $user_info['id'])),
1689
		),
1690
		array('id_action')
1691
	);
1692
	$user_info['id'] = 0;
1693
1694
	if ($command_line)
1695
	{
1696
		echo $endl;
1697
		echo 'Upgrade Complete!', $endl;
1698
		echo 'Please delete this file as soon as possible for security reasons.', $endl;
1699
		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...
1700
	}
1701
1702
	// Make sure it says we're done.
1703
	$upcontext['overall_percent'] = 100;
1704
	if (isset($upcontext['step_progress']))
1705
		unset($upcontext['step_progress']);
1706
1707
	$_GET['substep'] = 0;
1708
	return false;
1709
}
1710
1711
// Just like the built in one, but setup for CLI to not use themes.
1712
function cli_scheduled_fetchSMfiles()
1713
{
1714
	global $sourcedir, $language, $modSettings, $smcFunc;
1715
1716
	if (empty($modSettings['time_format']))
1717
		$modSettings['time_format'] = '%B %d, %Y, %I:%M:%S %p';
1718
1719
	// What files do we want to get
1720
	$request = $smcFunc['db_query']('', '
1721
		SELECT id_file, filename, path, parameters
1722
		FROM {db_prefix}admin_info_files',
1723
		array(
1724
		)
1725
	);
1726
1727
	$js_files = array();
1728
	while ($row = $smcFunc['db_fetch_assoc']($request))
1729
	{
1730
		$js_files[$row['id_file']] = array(
1731
			'filename' => $row['filename'],
1732
			'path' => $row['path'],
1733
			'parameters' => sprintf($row['parameters'], $language, urlencode($modSettings['time_format']), urlencode(SMF_FULL_VERSION)),
1734
		);
1735
	}
1736
	$smcFunc['db_free_result']($request);
1737
1738
	// We're gonna need fetch_web_data() to pull this off.
1739
	require_once($sourcedir . '/Subs.php');
1740
1741
	foreach ($js_files as $ID_FILE => $file)
1742
	{
1743
		// Create the url
1744
		$server = empty($file['path']) || substr($file['path'], 0, 7) != 'http://' ? 'https://www.simplemachines.org' : '';
1745
		$url = $server . (!empty($file['path']) ? $file['path'] : $file['path']) . $file['filename'] . (!empty($file['parameters']) ? '?' . $file['parameters'] : '');
1746
1747
		// Get the file
1748
		$file_data = fetch_web_data($url);
1749
1750
		// If we got an error - give up - the site might be down.
1751
		if ($file_data === false)
1752
			return throw_error(sprintf('Could not retrieve the file %1$s.', $url));
1753
1754
		// Save the file to the database.
1755
		$smcFunc['db_query']('substring', '
1756
			UPDATE {db_prefix}admin_info_files
1757
			SET data = SUBSTRING({string:file_data}, 1, 65534)
1758
			WHERE id_file = {int:id_file}',
1759
			array(
1760
				'id_file' => $ID_FILE,
1761
				'file_data' => $file_data,
1762
			)
1763
		);
1764
	}
1765
	return true;
1766
}
1767
1768
function convertSettingsToTheme()
1769
{
1770
	global $db_prefix, $modSettings, $smcFunc;
1771
1772
	$values = array(
1773
		'show_latest_member' => @$GLOBALS['showlatestmember'],
1774
		'show_bbc' => isset($GLOBALS['showyabbcbutt']) ? $GLOBALS['showyabbcbutt'] : @$GLOBALS['showbbcbutt'],
1775
		'show_modify' => @$GLOBALS['showmodify'],
1776
		'show_user_images' => @$GLOBALS['showuserpic'],
1777
		'show_blurb' => @$GLOBALS['showusertext'],
1778
		'show_gender' => @$GLOBALS['showgenderimage'],
1779
		'show_newsfader' => @$GLOBALS['shownewsfader'],
1780
		'display_recent_bar' => @$GLOBALS['Show_RecentBar'],
1781
		'show_member_bar' => @$GLOBALS['Show_MemberBar'],
1782
		'linktree_link' => @$GLOBALS['curposlinks'],
1783
		'show_profile_buttons' => @$GLOBALS['profilebutton'],
1784
		'show_mark_read' => @$GLOBALS['showmarkread'],
1785
		'newsfader_time' => @$GLOBALS['fadertime'],
1786
		'use_image_buttons' => empty($GLOBALS['MenuType']) ? 1 : 0,
1787
		'enable_news' => @$GLOBALS['enable_news'],
1788
		'return_to_post' => @$modSettings['returnToPost'],
1789
	);
1790
1791
	$themeData = array();
1792
	foreach ($values as $variable => $value)
1793
	{
1794
		if (!isset($value) || $value === null)
1795
			$value = 0;
1796
1797
		$themeData[] = array(0, 1, $variable, $value);
1798
	}
1799
	if (!empty($themeData))
1800
	{
1801
		$smcFunc['db_insert']('ignore',
1802
			$db_prefix . 'themes',
1803
			array('id_member' => 'int', 'id_theme' => 'int', 'variable' => 'string', 'value' => 'string'),
1804
			$themeData,
1805
			array('id_member', 'id_theme', 'variable')
1806
		);
1807
	}
1808
}
1809
1810
// This function only works with MySQL but that's fine as it is only used for v1.0.
1811
function convertSettingstoOptions()
1812
{
1813
	global $modSettings, $smcFunc;
1814
1815
	// Format: new_setting -> old_setting_name.
1816
	$values = array(
1817
		'calendar_start_day' => 'cal_startmonday',
1818
		'view_newest_first' => 'viewNewestFirst',
1819
		'view_newest_pm_first' => 'viewNewestFirst',
1820
	);
1821
1822
	foreach ($values as $variable => $value)
1823
	{
1824
		if (empty($modSettings[$value[0]]))
1825
			continue;
1826
1827
		$smcFunc['db_query']('', '
1828
			INSERT IGNORE INTO {db_prefix}themes
1829
				(id_member, id_theme, variable, value)
1830
			SELECT id_member, 1, {string:variable}, {string:value}
1831
			FROM {db_prefix}members',
1832
			array(
1833
				'variable' => $variable,
1834
				'value' => $modSettings[$value[0]],
1835
				'db_error_skip' => true,
1836
			)
1837
		);
1838
1839
		$smcFunc['db_query']('', '
1840
			INSERT IGNORE INTO {db_prefix}themes
1841
				(id_member, id_theme, variable, value)
1842
			VALUES (-1, 1, {string:variable}, {string:value})',
1843
			array(
1844
				'variable' => $variable,
1845
				'value' => $modSettings[$value[0]],
1846
				'db_error_skip' => true,
1847
			)
1848
		);
1849
	}
1850
}
1851
1852
function php_version_check()
1853
{
1854
	return version_compare(PHP_VERSION, $GLOBALS['required_php_version'], '>=');
1855
}
1856
1857
function db_version_check()
1858
{
1859
	global $db_type, $databases;
1860
1861
	$curver = eval($databases[$db_type]['version_check']);
0 ignored issues
show
introduced by
The use of eval() is discouraged.
Loading history...
1862
	$curver = preg_replace('~\-.+?$~', '', $curver);
1863
1864
	return version_compare($databases[$db_type]['version'], $curver, '<=');
1865
}
1866
1867
function fixRelativePath($path)
1868
{
1869
	global $install_path;
1870
1871
	// Fix the . at the start, clear any duplicate slashes, and fix any trailing slash...
1872
	return addslashes(preg_replace(array('~^\.([/\\\]|$)~', '~[/]+~', '~[\\\]+~', '~[/\\\]$~'), array($install_path . '$1', '/', '\\', ''), $path));
1873
}
1874
1875
function parse_sql($filename)
1876
{
1877
	global $db_prefix, $db_collation, $boarddir, $boardurl, $command_line, $file_steps, $step_progress, $custom_warning;
1878
	global $upcontext, $support_js, $is_debug, $db_type, $db_character_set;
1879
1880
/*
1881
	Failure allowed on:
1882
		- INSERT INTO but not INSERT IGNORE INTO.
1883
		- UPDATE IGNORE but not UPDATE.
1884
		- ALTER TABLE and ALTER IGNORE TABLE.
1885
		- DROP TABLE.
1886
	Yes, I realize that this is a bit confusing... maybe it should be done differently?
1887
1888
	If a comment...
1889
		- begins with --- it is to be output, with a break only in debug mode. (and say successful\n\n if there was one before.)
1890
		- begins with ---# it is a debugging statement, no break - only shown at all in debug.
1891
		- is only ---#, it is "done." and then a break - only shown in debug.
1892
		- begins with ---{ it is a code block terminating at ---}.
1893
1894
	Every block of between "--- ..."s is a step.  Every "---#" section represents a substep.
1895
1896
	Replaces the following variables:
1897
		- {$boarddir}
1898
		- {$boardurl}
1899
		- {$db_prefix}
1900
		- {$db_collation}
1901
*/
1902
1903
	// May want to use extended functionality.
1904
	db_extend();
1905
	db_extend('packages');
1906
1907
	// Our custom error handler - does nothing but does stop public errors from XML!
1908
	set_error_handler(
1909
		function($errno, $errstr, $errfile, $errline) use ($support_js)
1910
		{
1911
			if ($support_js)
1912
				return true;
1913
			else
1914
				echo 'Error: ' . $errstr . ' File: ' . $errfile . ' Line: ' . $errline;
1915
		}
1916
	);
1917
1918
	// If we're on MySQL, set {db_collation}; this approach is used throughout upgrade_2-0_mysql.php to set new tables to utf8
1919
	// Note it is expected to be in the format: ENGINE=MyISAM{$db_collation};
1920
	if ($db_type == 'mysql')
1921
		$db_collation = ' DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci';
1922
	else
1923
		$db_collation = '';
1924
1925
	$endl = $command_line ? "\n" : '<br>' . "\n";
1926
1927
	$lines = file($filename);
1928
1929
	$current_type = 'sql';
1930
	$current_data = '';
1931
	$substep = 0;
1932
	$last_step = '';
1933
1934
	// Make sure all newly created tables will have the proper characters set; this approach is used throughout upgrade_2-1_mysql.php
1935
	if (isset($db_character_set) && $db_character_set === 'utf8')
1936
		$lines = str_replace(') ENGINE=MyISAM;', ') ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;', $lines);
1937
1938
	// Count the total number of steps within this file - for progress.
1939
	$file_steps = substr_count(implode('', $lines), '---#');
0 ignored issues
show
Bug introduced by
It seems like $lines can also be of type false; however, parameter $pieces of implode() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

1939
	$file_steps = substr_count(implode('', /** @scrutinizer ignore-type */ $lines), '---#');
Loading history...
1940
	$upcontext['total_items'] = substr_count(implode('', $lines), '--- ');
1941
	$upcontext['debug_items'] = $file_steps;
1942
	$upcontext['current_item_num'] = 0;
1943
	$upcontext['current_item_name'] = '';
1944
	$upcontext['current_debug_item_num'] = 0;
1945
	$upcontext['current_debug_item_name'] = '';
1946
	// This array keeps a record of what we've done in case java is dead...
1947
	$upcontext['actioned_items'] = array();
1948
1949
	$done_something = false;
1950
1951
	foreach ($lines as $line_number => $line)
1952
	{
1953
		$do_current = $substep >= $_GET['substep'];
1954
1955
		// Get rid of any comments in the beginning of the line...
1956
		if (substr(trim($line), 0, 2) === '/*')
1957
			$line = preg_replace('~/\*.+?\*/~', '', $line);
1958
1959
		// Always flush.  Flush, flush, flush.  Flush, flush, flush, flush!  FLUSH!
1960
		if ($is_debug && !$support_js && $command_line)
1961
			flush();
1962
1963
		if (trim($line) === '')
1964
			continue;
1965
1966
		if (trim(substr($line, 0, 3)) === '---')
1967
		{
1968
			$type = substr($line, 3, 1);
1969
1970
			// An error??
1971
			if (trim($current_data) != '' && $type !== '}')
1972
			{
1973
				$upcontext['error_message'] = 'Error in upgrade script - line ' . $line_number . '!' . $endl;
1974
				if ($command_line)
1975
					echo $upcontext['error_message'];
1976
			}
1977
1978
			if ($type == ' ')
1979
			{
1980
				if (!$support_js && $do_current && $_GET['substep'] != 0 && $command_line)
1981
				{
1982
					echo ' Successful.', $endl;
1983
					flush();
1984
				}
1985
1986
				$last_step = htmlspecialchars(rtrim(substr($line, 4)));
1987
				$upcontext['current_item_num']++;
1988
				$upcontext['current_item_name'] = $last_step;
1989
1990
				if ($do_current)
1991
				{
1992
					$upcontext['actioned_items'][] = $last_step;
1993
					if ($command_line)
1994
						echo ' * ';
1995
1996
					// Starting a new main step in our DB changes, so it's time to reset this.
1997
					$upcontext['skip_db_substeps'] = false;
1998
				}
1999
			}
2000
			elseif ($type == '#')
2001
			{
2002
				$upcontext['step_progress'] += (100 / $upcontext['file_count']) / $file_steps;
2003
2004
				$upcontext['current_debug_item_num']++;
2005
				if (trim($line) != '---#')
2006
					$upcontext['current_debug_item_name'] = htmlspecialchars(rtrim(substr($line, 4)));
2007
2008
				// Have we already done something?
2009
				if (isset($_GET['xml']) && $done_something)
2010
				{
2011
					restore_error_handler();
2012
					return $upcontext['current_debug_item_num'] >= $upcontext['debug_items'] ? true : false;
2013
				}
2014
2015
				if ($do_current)
2016
				{
2017
					if (trim($line) == '---#' && $command_line)
2018
						echo ' done.', $endl;
2019
					elseif ($command_line)
2020
						echo ' +++ ', rtrim(substr($line, 4));
2021
					elseif (trim($line) != '---#')
2022
					{
2023
						if ($is_debug)
2024
							$upcontext['actioned_items'][] = $upcontext['current_debug_item_name'];
2025
					}
2026
				}
2027
2028
				if ($substep < $_GET['substep'] && $substep + 1 >= $_GET['substep'])
2029
				{
2030
					if ($command_line)
2031
						echo ' * ';
2032
					else
2033
						$upcontext['actioned_items'][] = $last_step;
2034
				}
2035
2036
				// Small step - only if we're actually doing stuff.
2037
				if ($do_current)
2038
					nextSubstep(++$substep);
2039
				else
2040
					$substep++;
2041
			}
2042
			elseif ($type == '{')
2043
				$current_type = 'code';
2044
			elseif ($type == '}')
2045
			{
2046
				$current_type = 'sql';
2047
2048
				if (!$do_current || !empty($upcontext['skip_db_substeps']))
2049
				{
2050
					$current_data = '';
2051
2052
					// Avoid confusion when skipping something we normally would have done
2053
					if ($do_current)
2054
						$done_something = true;
2055
2056
					continue;
2057
				}
2058
2059
				// @todo Update this to a try/catch for PHP 7+, because eval() now throws an exception for parse errors instead of returning false
2060
				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...
2061
				{
2062
					$upcontext['error_message'] = 'Error in upgrade script ' . basename($filename) . ' on line ' . $line_number . '!' . $endl;
2063
					if ($command_line)
2064
						echo $upcontext['error_message'];
2065
				}
2066
2067
				// Done with code!
2068
				$current_data = '';
2069
				$done_something = true;
2070
			}
2071
2072
			continue;
2073
		}
2074
2075
		$current_data .= $line;
2076
		if (substr(rtrim($current_data), -1) === ';' && $current_type === 'sql')
2077
		{
2078
			if ((!$support_js || isset($_GET['xml'])))
2079
			{
2080
				if (!$do_current || !empty($upcontext['skip_db_substeps']))
2081
				{
2082
					$current_data = '';
2083
2084
					if ($do_current)
2085
						$done_something = true;
2086
2087
					continue;
2088
				}
2089
2090
				$current_data = strtr(substr(rtrim($current_data), 0, -1), array('{$db_prefix}' => $db_prefix, '{$boarddir}' => $boarddir, '{$sboarddir}' => addslashes($boarddir), '{$boardurl}' => $boardurl, '{$db_collation}' => $db_collation));
2091
2092
				upgrade_query($current_data);
2093
2094
				// @todo This will be how it kinda does it once mysql all stripped out - needed for postgre (etc).
2095
				/*
2096
				$result = $smcFunc['db_query']('', $current_data, false, false);
2097
				// Went wrong?
2098
				if (!$result)
2099
				{
2100
					// Bit of a bodge - do we want the error?
2101
					if (!empty($upcontext['return_error']))
2102
					{
2103
						$upcontext['error_message'] = $smcFunc['db_error']($db_connection);
2104
						return false;
2105
					}
2106
				}*/
2107
				$done_something = true;
2108
			}
2109
			$current_data = '';
2110
		}
2111
		// If this is xml based and we're just getting the item name then that's grand.
2112
		elseif ($support_js && !isset($_GET['xml']) && $upcontext['current_debug_item_name'] != '' && $do_current)
2113
		{
2114
			restore_error_handler();
2115
			return false;
2116
		}
2117
2118
		// Clean up by cleaning any step info.
2119
		$step_progress = array();
2120
		$custom_warning = '';
2121
	}
2122
2123
	// Put back the error handler.
2124
	restore_error_handler();
2125
2126
	if ($command_line)
2127
	{
2128
		echo ' Successful.' . "\n";
2129
		flush();
2130
	}
2131
2132
	$_GET['substep'] = 0;
2133
	return true;
2134
}
2135
2136
function upgrade_query($string, $unbuffered = false)
2137
{
2138
	global $db_connection, $db_server, $db_user, $db_passwd, $db_type;
2139
	global $command_line, $upcontext, $upgradeurl, $modSettings;
2140
	global $db_name, $db_unbuffered, $smcFunc, $txt;
2141
2142
	// Get the query result - working around some SMF specific security - just this once!
2143
	$modSettings['disableQueryCheck'] = true;
2144
	$db_unbuffered = $unbuffered;
2145
	$ignore_insert_error = false;
2146
2147
	// If we got an old pg version and use a insert ignore query
2148
	if ($db_type == 'postgresql' && !$smcFunc['db_native_replace']() && strpos($string, 'ON CONFLICT DO NOTHING') !== false)
2149
	{
2150
		$ignore_insert_error = true;
2151
		$string = str_replace('ON CONFLICT DO NOTHING', '', $string);
2152
	}
2153
	$result = $smcFunc['db_query']('', $string, array('security_override' => true, 'db_error_skip' => true));
2154
	$db_unbuffered = false;
2155
2156
	// Failure?!
2157
	if ($result !== false)
2158
		return $result;
2159
2160
	$db_error_message = $smcFunc['db_error']($db_connection);
2161
	// If MySQL we do something more clever.
2162
	if ($db_type == 'mysql')
2163
	{
2164
		$mysqli_errno = mysqli_errno($db_connection);
2165
		$error_query = in_array(substr(trim($string), 0, 11), array('INSERT INTO', 'UPDATE IGNO', 'ALTER TABLE', 'DROP TABLE ', 'ALTER IGNOR', 'INSERT IGNO'));
2166
2167
		// Error numbers:
2168
		//    1016: Can't open file '....MYI'
2169
		//    1050: Table already exists.
2170
		//    1054: Unknown column name.
2171
		//    1060: Duplicate column name.
2172
		//    1061: Duplicate key name.
2173
		//    1062: Duplicate entry for unique key.
2174
		//    1068: Multiple primary keys.
2175
		//    1072: Key column '%s' doesn't exist in table.
2176
		//    1091: Can't drop key, doesn't exist.
2177
		//    1146: Table doesn't exist.
2178
		//    2013: Lost connection to server during query.
2179
2180
		if ($mysqli_errno == 1016)
2181
		{
2182
			if (preg_match('~\'([^\.\']+)~', $db_error_message, $match) != 0 && !empty($match[1]))
2183
			{
2184
				mysqli_query($db_connection, 'REPAIR TABLE `' . $match[1] . '`');
2185
				$result = mysqli_query($db_connection, $string);
2186
				if ($result !== false)
2187
					return $result;
2188
			}
2189
		}
2190
		elseif ($mysqli_errno == 2013)
2191
		{
2192
			$db_connection = mysqli_connect($db_server, $db_user, $db_passwd);
2193
			mysqli_select_db($db_connection, $db_name);
0 ignored issues
show
Bug introduced by
It seems like $db_connection can also be of type false; however, parameter $link of mysqli_select_db() does only seem to accept mysqli, maybe add an additional type check? ( Ignorable by Annotation )

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

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

2656
	$temp = substr(@implode('', /** @scrutinizer ignore-type */ @file($boarddir . '/index.php')), 0, 4096);
Loading history...
2657
	preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $temp, $match);
2658
	if (empty($match[1]) || (trim($match[1]) != SMF_VERSION))
2659
		print_error('Error: Some files have not yet been updated properly.');
2660
2661
	// Make sure Settings.php is writable.
2662
	quickFileWritable($boarddir . '/Settings.php');
2663
	if (!is_writable($boarddir . '/Settings.php'))
2664
		print_error('Error: Unable to obtain write access to "Settings.php".', true);
2665
2666
	// Make sure Settings_bak.php is writable.
2667
	quickFileWritable($boarddir . '/Settings_bak.php');
2668
	if (!is_writable($boarddir . '/Settings_bak.php'))
2669
		print_error('Error: Unable to obtain write access to "Settings_bak.php".');
2670
2671
	if (isset($modSettings['agreement']) && (!is_writable($boarddir) || file_exists($boarddir . '/agreement.txt')) && !is_writable($boarddir . '/agreement.txt'))
2672
		print_error('Error: Unable to obtain write access to "agreement.txt".');
2673
	elseif (isset($modSettings['agreement']))
2674
	{
2675
		$fp = fopen($boarddir . '/agreement.txt', 'w');
2676
		fwrite($fp, $modSettings['agreement']);
0 ignored issues
show
Bug introduced by
It seems like $fp can also be of type false; however, parameter $handle of fwrite() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

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

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

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

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