Failed Conditions
Push — release-2.1 ( 8d1977...8da17b )
by Rick
06:19
created

other/upgrade.php (11 issues)

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

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

Loading history...
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
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
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
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, $db_show_debug;
628
	global $db_mb4, $modSettings, $sourcedir, $smcFunc, $txt, $utf8;
629
630
	// Report all errors if admin wants them or this is a pre-release version.
631
	if (!empty($db_show_debug) || strspn(SMF_VERSION, '1234567890.') !== strlen(SMF_VERSION))
632
		error_reporting(E_ALL);
633
	// Otherwise, report all errors except for deprecation notices.
634
	else
635
		error_reporting(E_ALL & ~E_DEPRECATED);
636
637
638
	define('SMF', 1);
639
	header('X-Frame-Options: SAMEORIGIN');
640
	header('X-XSS-Protection: 1');
641
	header('X-Content-Type-Options: nosniff');
642
643
	// Start the session.
644
	if (@ini_get('session.save_handler') == 'user')
645
		@ini_set('session.save_handler', 'files');
646
	@session_start();
647
648
	if (empty($smcFunc))
649
		$smcFunc = array();
650
651
	require_once($sourcedir . '/Subs.php');
652
653
	$smcFunc['random_int'] = function($min = 0, $max = PHP_INT_MAX)
654
	{
655
		global $sourcedir;
656
657
		// Oh, wouldn't it be great if I *was* crazy? Then the world would be okay.
658
		if (!is_callable('random_int'))
659
			require_once($sourcedir . '/random_compat/random.php');
660
661
		return random_int($min, $max);
662
	};
663
664
	// We need this for authentication and some upgrade code
665
	require_once($sourcedir . '/Subs-Auth.php');
666
	require_once($sourcedir . '/Class-Package.php');
667
668
	$smcFunc['strtolower'] = 'smf_strtolower';
669
670
	// Initialize everything...
671
	initialize_inputs();
672
673
	$utf8 = (empty($modSettings['global_character_set']) ? $txt['lang_character_set'] : $modSettings['global_character_set']) === 'UTF-8';
674
675
	// Get the database going!
676
	if (empty($db_type) || $db_type == 'mysqli')
677
	{
678
		$db_type = 'mysql';
679
		// If overriding $db_type, need to set its settings.php entry too
680
		$changes = array();
681
		$changes['db_type'] = 'mysql';
682
		require_once($sourcedir . '/Subs-Admin.php');
683
		updateSettingsFile($changes);
684
	}
685
686
	if (file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php'))
687
	{
688
		require_once($sourcedir . '/Subs-Db-' . $db_type . '.php');
689
690
		// Make the connection...
691
		if (empty($db_connection))
692
		{
693
			$options = array('non_fatal' => true);
694
			// Add in the port if needed
695
			if (!empty($db_port))
696
				$options['port'] = $db_port;
697
698
			if (!empty($db_mb4))
699
				$options['db_mb4'] = $db_mb4;
700
701
			$db_connection = smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, $db_prefix, $options);
702
		}
703
		else
704
			// If we've returned here, ping/reconnect to be safe
705
			$smcFunc['db_ping']($db_connection);
706
707
		// Oh dear god!!
708
		if ($db_connection === null)
709
			die($txt['error_db_connect_settings']);
0 ignored issues
show
Using exit here is not recommended.

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

Loading history...
710
711
		if ($db_type == 'mysql' && isset($db_character_set) && preg_match('~^\w+$~', $db_character_set) === 1)
712
			$smcFunc['db_query']('', '
713
				SET NAMES {string:db_character_set}',
714
				array(
715
					'db_error_skip' => true,
716
					'db_character_set' => $db_character_set,
717
				)
718
			);
719
720
		// Load the modSettings data...
721
		$request = $smcFunc['db_query']('', '
722
			SELECT variable, value
723
			FROM {db_prefix}settings',
724
			array(
725
				'db_error_skip' => true,
726
			)
727
		);
728
		$modSettings = array();
729
		while ($row = $smcFunc['db_fetch_assoc']($request))
730
			$modSettings[$row['variable']] = $row['value'];
731
		$smcFunc['db_free_result']($request);
732
	}
733
	else
734
		return throw_error(sprintf($txt['error_sourcefile_missing'], 'Subs-Db-' . $db_type . '.php'));
735
736
	// If they don't have the file, they're going to get a warning anyway so we won't need to clean request vars.
737
	if (file_exists($sourcedir . '/QueryString.php') && php_version_check())
738
	{
739
		require_once($sourcedir . '/QueryString.php');
740
		cleanRequest();
741
	}
742
743
	if (!isset($_GET['substep']))
744
		$_GET['substep'] = 0;
745
}
746
747
function initialize_inputs()
748
{
749
	global $start_time, $db_type;
750
751
	$start_time = time();
752
753
	umask(0);
754
755
	ob_start();
756
757
	// Better to upgrade cleanly and fall apart than to screw everything up if things take too long.
758
	ignore_user_abort(true);
759
760
	// This is really quite simple; if ?delete is on the URL, delete the upgrader...
761
	if (isset($_GET['delete']))
762
	{
763
		@unlink(__FILE__);
764
765
		// And the extra little files ;).
766
		@unlink(dirname(__FILE__) . '/upgrade_1-0.sql');
767
		@unlink(dirname(__FILE__) . '/upgrade_1-1.sql');
768
		@unlink(dirname(__FILE__) . '/upgrade_2-0_' . $db_type . '.sql');
769
		@unlink(dirname(__FILE__) . '/upgrade_2-1_' . $db_type . '.sql');
770
		@unlink(dirname(__FILE__) . '/upgrade-helper.php');
771
772
		$dh = opendir(dirname(__FILE__));
773
		while ($file = readdir($dh))
774
		{
775
			if (preg_match('~upgrade_\d-\d_([A-Za-z])+\.sql~i', $file, $matches) && isset($matches[1]))
776
				@unlink(dirname(__FILE__) . '/' . $file);
777
		}
778
		closedir($dh);
779
780
		// Legacy files while we're at it. NOTE: We only touch files we KNOW shouldn't be there.
781
		// 1.1 Sources files not in 2.0+
782
		@unlink(dirname(__FILE__) . '/Sources/ModSettings.php');
783
		// 1.1 Templates that don't exist any more (e.g. renamed)
784
		@unlink(dirname(__FILE__) . '/Themes/default/Combat.template.php');
785
		@unlink(dirname(__FILE__) . '/Themes/default/Modlog.template.php');
786
		// 1.1 JS files were stored in the main theme folder, but in 2.0+ are in the scripts/ folder
787
		@unlink(dirname(__FILE__) . '/Themes/default/fader.js');
788
		@unlink(dirname(__FILE__) . '/Themes/default/script.js');
789
		@unlink(dirname(__FILE__) . '/Themes/default/spellcheck.js');
790
		@unlink(dirname(__FILE__) . '/Themes/default/xml_board.js');
791
		@unlink(dirname(__FILE__) . '/Themes/default/xml_topic.js');
792
793
		// 2.0 Sources files not in 2.1+
794
		@unlink(dirname(__FILE__) . '/Sources/DumpDatabase.php');
795
		@unlink(dirname(__FILE__) . '/Sources/LockTopic.php');
796
797
		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');
798
		exit;
799
	}
800
801
	// Something is causing this to happen, and it's annoying.  Stop it.
802
	$temp = 'upgrade_php?step';
803
	while (strlen($temp) > 4)
804
	{
805
		if (isset($_GET[$temp]))
806
			unset($_GET[$temp]);
807
		$temp = substr($temp, 1);
808
	}
809
810
	// Force a step, defaulting to 0.
811
	$_GET['step'] = (int) @$_GET['step'];
812
	$_GET['substep'] = (int) @$_GET['substep'];
813
}
814
815
// Step 0 - Let's welcome them in and ask them to login!
816
function WelcomeLogin()
817
{
818
	global $boarddir, $sourcedir, $modSettings, $cachedir, $upgradeurl, $upcontext;
819
	global $smcFunc, $db_type, $databases, $boardurl;
820
821
	// We global $txt here so that the language files can add to them. This variable is NOT unused.
822
	global $txt;
823
824
	$upcontext['sub_template'] = 'welcome_message';
825
826
	// Check for some key files - one template, one language, and a new and an old source file.
827
	$check = @file_exists($modSettings['theme_dir'] . '/index.template.php')
828
		&& @file_exists($sourcedir . '/QueryString.php')
829
		&& @file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php')
830
		&& @file_exists(dirname(__FILE__) . '/upgrade_2-1_' . $db_type . '.sql');
831
832
	// Need legacy scripts?
833
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 2.1)
834
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_2-0_' . $db_type . '.sql');
835
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 2.0)
836
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_1-1.sql');
837
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 1.1)
838
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_1-0.sql');
839
840
	// We don't need "-utf8" files anymore...
841
	$upcontext['language'] = str_ireplace('-utf8', '', $upcontext['language']);
842
843
	if (!$check)
844
		// 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.
845
		return throw_error($txt['error_upgrade_files_missing']);
846
847
	// Do they meet the install requirements?
848
	if (!php_version_check())
849
		return throw_error($txt['error_php_too_low']);
850
851
	if (!db_version_check())
852
		return throw_error(sprintf($txt['error_db_too_low'], $databases[$db_type]['name']));
853
854
	// Do some checks to make sure they have proper privileges
855
	db_extend('packages');
856
857
	// CREATE
858
	$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');
859
860
	// ALTER
861
	$alter = $smcFunc['db_add_column']('{db_prefix}priv_check', array('name' => 'txt', 'type' => 'varchar', 'size' => 4, 'null' => false, 'default' => ''));
862
863
	// DROP
864
	$drop = $smcFunc['db_drop_table']('{db_prefix}priv_check');
865
866
	// Sorry... we need CREATE, ALTER and DROP
867
	if (!$create || !$alter || !$drop)
868
		return throw_error(sprintf($txt['error_db_privileges'], $databases[$db_type]['name']));
869
870
	// Do a quick version spot check.
871
	$temp = substr(@implode('', @file($boarddir . '/index.php')), 0, 4096);
872
	preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $temp, $match);
873
	if (empty($match[1]) || (trim($match[1]) != SMF_VERSION))
874
		return throw_error($txt['error_upgrade_old_files']);
875
876
	// What absolutely needs to be writable?
877
	$writable_files = array(
878
		$boarddir . '/Settings.php',
879
		$boarddir . '/Settings_bak.php',
880
	);
881
882
	// Only check for minified writable files if we have it enabled or not set.
883
	if (!empty($modSettings['minimize_files']) || !isset($modSettings['minimize_files']))
884
		$writable_files += array(
885
			$modSettings['theme_dir'] . '/css/minified.css',
886
			$modSettings['theme_dir'] . '/scripts/minified.js',
887
			$modSettings['theme_dir'] . '/scripts/minified_deferred.js',
888
		);
889
890
	// Do we need to add this setting?
891
	$need_settings_update = empty($modSettings['custom_avatar_dir']);
892
893
	$custom_av_dir = !empty($modSettings['custom_avatar_dir']) ? $modSettings['custom_avatar_dir'] : $GLOBALS['boarddir'] . '/custom_avatar';
894
	$custom_av_url = !empty($modSettings['custom_avatar_url']) ? $modSettings['custom_avatar_url'] : $boardurl . '/custom_avatar';
895
896
	// This little fellow has to cooperate...
897
	quickFileWritable($custom_av_dir);
898
899
	// Are we good now?
900
	if (!is_writable($custom_av_dir))
901
		return throw_error(sprintf($txt['error_dir_not_writable'], $custom_av_dir));
902
	elseif ($need_settings_update)
903
	{
904
		if (!function_exists('cache_put_data'))
905
			require_once($sourcedir . '/Load.php');
906
907
		updateSettings(array('custom_avatar_dir' => $custom_av_dir));
908
		updateSettings(array('custom_avatar_url' => $custom_av_url));
909
	}
910
911
	require_once($sourcedir . '/Security.php');
912
913
	// Check the cache directory.
914
	$cachedir_temp = empty($cachedir) ? $boarddir . '/cache' : $cachedir;
915
	if (!file_exists($cachedir_temp))
916
		@mkdir($cachedir_temp);
917
918
	if (!file_exists($cachedir_temp))
919
		return throw_error($txt['error_cache_not_found']);
920
921
	quickFileWritable($cachedir_temp . '/db_last_error.php');
922
923
	if (!file_exists($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php'))
924
		return throw_error(sprintf($txt['error_lang_index_missing'], $upcontext['language'], $upgradeurl));
925
	elseif (!isset($_GET['skiplang']))
926
	{
927
		$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php')), 0, 4096);
928
		preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
929
930
		if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
931
			return throw_error(sprintf($txt['error_upgrade_old_lang_files'], $upcontext['language'], $upgradeurl));
932
	}
933
934
	if (!makeFilesWritable($writable_files))
935
		return false;
936
937
	// Check agreement.txt. (it may not exist, in which case $boarddir must be writable.)
938
	if (isset($modSettings['agreement']) && (!is_writable($boarddir) || file_exists($boarddir . '/agreement.txt')) && !is_writable($boarddir . '/agreement.txt'))
939
		return throw_error($txt['error_agreement_not_writable']);
940
941
	// Upgrade the agreement.
942
	elseif (isset($modSettings['agreement']))
943
	{
944
		$fp = fopen($boarddir . '/agreement.txt', 'w');
945
		fwrite($fp, $modSettings['agreement']);
946
		fclose($fp);
947
	}
948
949
	// We're going to check that their board dir setting is right in case they've been moving stuff around.
950
	if (strtr($boarddir, array('/' => '', '\\' => '')) != strtr(dirname(__FILE__), array('/' => '', '\\' => '')))
951
		$upcontext['warning'] = '
952
			' . sprintf($txt['upgrade_boarddir_settings'], $boarddir, dirname(__FILE__)) . '<br>
953
			<ul>
954
				<li>' . $txt['upgrade_boarddir'] . '  ' . $boarddir . '</li>
955
				<li>' . $txt['upgrade_sourcedir'] . '  ' . $boarddir . '</li>
956
				<li>' . $txt['upgrade_cachedir'] . '  ' . $cachedir_temp . '</li>
957
			</ul>
958
			' . $txt['upgrade_incorrect_settings'] . '';
959
960
	// Confirm mbstring is loaded...
961
	if (!extension_loaded('mbstring'))
962
		return throw_error($txt['install_no_mbstring']);
963
964
	// Check for https stream support.
965
	$supported_streams = stream_get_wrappers();
966
	if (!in_array('https', $supported_streams))
967
		$upcontext['custom_warning'] = $txt['install_no_https'];
968
969
	// Either we're logged in or we're going to present the login.
970
	if (checkLogin())
971
		return true;
972
973
	$upcontext += createToken('login');
974
975
	return false;
976
}
977
978
// Step 0.5: Does the login work?
979
function checkLogin()
980
{
981
	global $modSettings, $upcontext, $disable_security;
982
	global $smcFunc, $db_type, $support_js, $sourcedir, $txt;
983
984
	// Are we trying to login?
985
	if (isset($_POST['contbutt']) && (!empty($_POST['user']) || $disable_security))
986
	{
987
		// If we've disabled security pick a suitable name!
988
		if (empty($_POST['user']))
989
			$_POST['user'] = 'Administrator';
990
991
		// Before 2.0 these column names were different!
992
		$oldDB = false;
993
		if (empty($db_type) || $db_type == 'mysql')
994
		{
995
			$request = $smcFunc['db_query']('', '
996
				SHOW COLUMNS
997
				FROM {db_prefix}members
998
				LIKE {string:member_name}',
999
				array(
1000
					'member_name' => 'memberName',
1001
					'db_error_skip' => true,
1002
				)
1003
			);
1004
			if ($smcFunc['db_num_rows']($request) != 0)
1005
				$oldDB = true;
1006
			$smcFunc['db_free_result']($request);
1007
		}
1008
1009
		// Get what we believe to be their details.
1010
		if (!$disable_security)
1011
		{
1012
			if ($oldDB)
1013
				$request = $smcFunc['db_query']('', '
1014
					SELECT id_member, memberName AS member_name, passwd, id_group,
1015
						additionalGroups AS additional_groups, lngfile
1016
					FROM {db_prefix}members
1017
					WHERE memberName = {string:member_name}',
1018
					array(
1019
						'member_name' => $_POST['user'],
1020
						'db_error_skip' => true,
1021
					)
1022
				);
1023
			else
1024
				$request = $smcFunc['db_query']('', '
1025
					SELECT id_member, member_name, passwd, id_group, additional_groups, lngfile
1026
					FROM {db_prefix}members
1027
					WHERE member_name = {string:member_name}',
1028
					array(
1029
						'member_name' => $_POST['user'],
1030
						'db_error_skip' => true,
1031
					)
1032
				);
1033
			if ($smcFunc['db_num_rows']($request) != 0)
1034
			{
1035
				list ($id_member, $name, $password, $id_group, $addGroups, $user_language) = $smcFunc['db_fetch_row']($request);
1036
1037
				$groups = explode(',', $addGroups);
1038
				$groups[] = $id_group;
1039
1040
				foreach ($groups as $k => $v)
1041
					$groups[$k] = (int) $v;
1042
1043
				$sha_passwd = sha1(strtolower($name) . un_htmlspecialchars($_REQUEST['passwrd']));
1044
1045
				// We don't use "-utf8" anymore...
1046
				$user_language = str_ireplace('-utf8', '', $user_language);
1047
			}
1048
			else
1049
				$upcontext['username_incorrect'] = true;
1050
1051
			$smcFunc['db_free_result']($request);
1052
		}
1053
		$upcontext['username'] = $_POST['user'];
1054
1055
		// Track whether javascript works!
1056
		if (isset($_POST['js_works']))
1057
		{
1058
			if (!empty($_POST['js_works']))
1059
			{
1060
				$upcontext['upgrade_status']['js'] = 1;
1061
				$support_js = 1;
1062
			}
1063
			else
1064
				$support_js = 0;
1065
		}
1066
1067
		// Note down the version we are coming from.
1068
		if (!empty($modSettings['smfVersion']) && empty($upcontext['user']['version']))
1069
			$upcontext['user']['version'] = $modSettings['smfVersion'];
1070
1071
		// Didn't get anywhere?
1072
		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']))
1073
		{
1074
			// MD5?
1075
			$md5pass = md5_hmac($_REQUEST['passwrd'], strtolower($_POST['user']));
1076
			if ($md5pass != $password)
1077
			{
1078
				$upcontext['password_failed'] = true;
1079
				// Disable the hashing this time.
1080
				$upcontext['disable_login_hashing'] = true;
1081
			}
1082
		}
1083
1084
		if ((empty($upcontext['password_failed']) && !empty($name)) || $disable_security)
1085
		{
1086
			// Set the password.
1087
			if (!$disable_security)
1088
			{
1089
				// Do we actually have permission?
1090
				if (!in_array(1, $groups))
1091
				{
1092
					$request = $smcFunc['db_query']('', '
1093
						SELECT permission
1094
						FROM {db_prefix}permissions
1095
						WHERE id_group IN ({array_int:groups})
1096
							AND permission = {string:admin_forum}',
1097
						array(
1098
							'groups' => $groups,
1099
							'admin_forum' => 'admin_forum',
1100
							'db_error_skip' => true,
1101
						)
1102
					);
1103
					if ($smcFunc['db_num_rows']($request) == 0)
1104
						return throw_error($txt['error_not_admin']);
1105
					$smcFunc['db_free_result']($request);
1106
				}
1107
1108
				$upcontext['user']['id'] = $id_member;
1109
				$upcontext['user']['name'] = $name;
1110
			}
1111
			else
1112
			{
1113
				$upcontext['user']['id'] = 1;
1114
				$upcontext['user']['name'] = 'Administrator';
1115
			}
1116
1117
			if (!is_callable('random_int'))
1118
				require_once('Sources/random_compat/random.php');
1119
1120
			$upcontext['user']['pass'] = random_int(0, 60000);
1121
			// This basically is used to match the GET variables to Settings.php.
1122
			$upcontext['upgrade_status']['pass'] = $upcontext['user']['pass'];
1123
1124
			// Set the language to that of the user?
1125
			if (isset($user_language) && $user_language != $upcontext['language'] && file_exists($modSettings['theme_dir'] . '/languages/index.' . basename($user_language, '.lng') . '.php'))
1126
			{
1127
				$user_language = basename($user_language, '.lng');
1128
				$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $user_language . '.php')), 0, 4096);
1129
				preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
1130
1131
				if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
1132
					$upcontext['upgrade_options_warning'] = sprintf($txt['warning_lang_old'], $user_language, $upcontext['language']);
1133
				elseif (!file_exists($modSettings['theme_dir'] . '/languages/Install.' . $user_language . '.php'))
1134
					$upcontext['upgrade_options_warning'] = sprintf($txt['warning_lang_missing'], $user_language, $upcontext['language']);
1135
				else
1136
				{
1137
					// Set this as the new language.
1138
					$upcontext['language'] = $user_language;
1139
					$upcontext['upgrade_status']['lang'] = $upcontext['language'];
1140
1141
					// Include the file.
1142
					load_lang_file();
1143
				}
1144
			}
1145
1146
			// If we're resuming set the step and substep to be correct.
1147
			if (isset($_POST['cont']))
1148
			{
1149
				$upcontext['current_step'] = $upcontext['user']['step'];
1150
				$_GET['substep'] = $upcontext['user']['substep'];
1151
			}
1152
1153
			return true;
1154
		}
1155
	}
1156
1157
	return false;
1158
}
1159
1160
// Step 1: Do the maintenance and backup.
1161
function UpgradeOptions()
1162
{
1163
	global $db_prefix, $command_line, $modSettings, $is_debug, $smcFunc, $packagesdir, $tasksdir, $language, $txt, $db_port;
1164
	global $boarddir, $boardurl, $sourcedir, $maintenance, $cachedir, $upcontext, $db_type, $db_server, $image_proxy_enabled;
1165
1166
	$upcontext['sub_template'] = 'upgrade_options';
1167
	$upcontext['page_title'] = $txt['upgrade_options'];
1168
1169
	db_extend('packages');
1170
	$upcontext['karma_installed'] = array('good' => false, 'bad' => false);
1171
	$member_columns = $smcFunc['db_list_columns']('{db_prefix}members');
1172
1173
	$upcontext['karma_installed']['good'] = in_array('karma_good', $member_columns);
1174
	$upcontext['karma_installed']['bad'] = in_array('karma_bad', $member_columns);
1175
1176
	$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', '<');
1177
1178
	unset($member_columns);
1179
1180
	// If we've not submitted then we're done.
1181
	if (empty($_POST['upcont']))
1182
		return false;
1183
1184
	// Firstly, if they're enabling SM stat collection just do it.
1185
	if (!empty($_POST['stats']) && substr($boardurl, 0, 16) != 'http://localhost' && empty($modSettings['allow_sm_stats']) && empty($modSettings['enable_sm_stats']))
1186
	{
1187
		$upcontext['allow_sm_stats'] = true;
1188
1189
		// Don't register if we still have a key.
1190
		if (empty($modSettings['sm_stats_key']))
1191
		{
1192
			// Attempt to register the site etc.
1193
			$fp = @fsockopen('www.simplemachines.org', 80, $errno, $errstr);
1194
			if ($fp)
1195
			{
1196
				$out = 'GET /smf/stats/register_stats.php?site=' . base64_encode($boardurl) . ' HTTP/1.1' . "\r\n";
1197
				$out .= 'Host: www.simplemachines.org' . "\r\n";
1198
				$out .= 'Connection: Close' . "\r\n\r\n";
1199
				fwrite($fp, $out);
1200
1201
				$return_data = '';
1202
				while (!feof($fp))
1203
					$return_data .= fgets($fp, 128);
1204
1205
				fclose($fp);
1206
1207
				// Get the unique site ID.
1208
				preg_match('~SITE-ID:\s(\w{10})~', $return_data, $ID);
1209
1210
				if (!empty($ID[1]))
1211
					$smcFunc['db_insert']('replace',
1212
						$db_prefix . 'settings',
1213
						array('variable' => 'string', 'value' => 'string'),
1214
						array(
1215
							array('sm_stats_key', $ID[1]),
1216
							array('enable_sm_stats', 1),
1217
						),
1218
						array('variable')
1219
					);
1220
			}
1221
		}
1222
		else
1223
		{
1224
			$smcFunc['db_insert']('replace',
1225
				$db_prefix . 'settings',
1226
				array('variable' => 'string', 'value' => 'string'),
1227
				array('enable_sm_stats', 1),
1228
				array('variable')
1229
			);
1230
		}
1231
	}
1232
	// Don't remove stat collection unless we unchecked the box for real, not from the loop.
1233
	elseif (empty($_POST['stats']) && empty($upcontext['allow_sm_stats']))
1234
		$smcFunc['db_query']('', '
1235
			DELETE FROM {db_prefix}settings
1236
			WHERE variable = {string:enable_sm_stats}',
1237
			array(
1238
				'enable_sm_stats' => 'enable_sm_stats',
1239
				'db_error_skip' => true,
1240
			)
1241
		);
1242
1243
	// Deleting old karma stuff?
1244
	if (!empty($_POST['delete_karma']))
1245
	{
1246
		// Delete old settings vars.
1247
		$smcFunc['db_query']('', '
1248
			DELETE FROM {db_prefix}settings
1249
			WHERE variable IN ({array_string:karma_vars})',
1250
			array(
1251
				'karma_vars' => array('karmaMode', 'karmaTimeRestrictAdmins', 'karmaWaitTime', 'karmaMinPosts', 'karmaLabel', 'karmaSmiteLabel', 'karmaApplaudLabel'),
1252
			)
1253
		);
1254
1255
		// Cleaning up old karma member settings.
1256
		if ($upcontext['karma_installed']['good'])
1257
			$smcFunc['db_query']('', '
1258
				ALTER TABLE {db_prefix}members
1259
				DROP karma_good',
1260
				array()
1261
			);
1262
1263
		// Does karma bad was enable?
1264
		if ($upcontext['karma_installed']['bad'])
1265
			$smcFunc['db_query']('', '
1266
				ALTER TABLE {db_prefix}members
1267
				DROP karma_bad',
1268
				array()
1269
			);
1270
1271
		// Cleaning up old karma permissions.
1272
		$smcFunc['db_query']('', '
1273
			DELETE FROM {db_prefix}permissions
1274
			WHERE permission = {string:karma_vars}',
1275
			array(
1276
				'karma_vars' => 'karma_edit',
1277
			)
1278
		);
1279
		// Cleaning up old log_karma table
1280
		$smcFunc['db_query']('', '
1281
			DROP TABLE IF EXISTS {db_prefix}log_karma',
1282
			array()
1283
		);
1284
	}
1285
1286
	// Emptying the error log?
1287
	if (!empty($_POST['empty_error']))
1288
		$smcFunc['db_query']('truncate_table', '
1289
			TRUNCATE {db_prefix}log_errors',
1290
			array(
1291
			)
1292
		);
1293
1294
	$changes = array();
1295
1296
	// Add proxy settings.
1297
	if (!isset($GLOBALS['image_proxy_secret']) || $GLOBALS['image_proxy_secret'] == 'smfisawesome')
1298
		$changes['image_proxy_secret'] = substr(sha1(mt_rand()), 0, 20);
1299
	if (!isset($GLOBALS['image_proxy_maxsize']))
1300
		$changes['image_proxy_maxsize'] = 5190;
1301
	if (!isset($GLOBALS['image_proxy_enabled']))
1302
		$changes['image_proxy_enabled'] = false;
1303
1304
	// If $boardurl reflects https, set force_ssl
1305
	if (!function_exists('cache_put_data'))
1306
		require_once($sourcedir . '/Load.php');
1307
	if (stripos($boardurl, 'https://') !== false)
1308
		updateSettings(array('force_ssl' => '1'));
1309
1310
	// If we're overriding the language follow it through.
1311
	if (isset($upcontext['lang']) && file_exists($modSettings['theme_dir'] . '/languages/index.' . $upcontext['lang'] . '.php'))
1312
		$changes['language'] = $upcontext['lang'];
1313
1314
	if (!empty($_POST['maint']))
1315
	{
1316
		$changes['maintenance'] = 2;
1317
		// Remember what it was...
1318
		$upcontext['user']['main'] = $maintenance;
1319
1320
		if (!empty($_POST['maintitle']))
1321
		{
1322
			$changes['mtitle'] = $_POST['maintitle'];
1323
			$changes['mmessage'] = $_POST['mainmessage'];
1324
		}
1325
		else
1326
		{
1327
			$changes['mtitle'] = $txt['mtitle'];
1328
			$changes['mmessage'] = $txt['mmessage'];
1329
		}
1330
	}
1331
1332
	if ($command_line)
1333
		echo ' * Updating Settings.php...';
1334
1335
	// Fix some old paths.
1336
	if (substr($boarddir, 0, 1) == '.')
1337
		$changes['boarddir'] = fixRelativePath($boarddir);
1338
1339
	if (substr($sourcedir, 0, 1) == '.')
1340
		$changes['sourcedir'] = fixRelativePath($sourcedir);
1341
1342
	if (empty($cachedir) || substr($cachedir, 0, 1) == '.')
1343
		$changes['cachedir'] = fixRelativePath($boarddir) . '/cache';
1344
1345
	// Migrate cache settings.
1346
	// Accelerator setting didn't exist previously; use 'smf' file based caching as default if caching had been enabled.
1347
	if (!isset($GLOBALS['cache_enable']))
1348
		$changes += array(
1349
			'cache_accelerator' => !empty($modSettings['cache_enable']) ? 'smf' : '',
1350
			'cache_enable' => !empty($modSettings['cache_enable']) ? $modSettings['cache_enable'] : 0,
1351
			'cache_memcached' => !empty($modSettings['cache_memcached']) ? $modSettings['cache_memcached'] : '',
1352
		);
1353
1354
	// If they have a "host:port" setup for the host, split that into separate values
1355
	// You should never have a : in the hostname if you're not on MySQL, but better safe than sorry
1356
	if (strpos($db_server, ':') !== false && $db_type == 'mysql')
1357
	{
1358
		list ($db_server, $db_port) = explode(':', $db_server);
1359
1360
		$changes['db_server'] = $db_server;
1361
1362
		// Only set this if we're not using the default port
1363
		if ($db_port != ini_get('mysqli.default_port'))
1364
			$changes['db_port'] = (int) $db_port;
1365
	}
1366
1367
	// If db_port is set and is the same as the default, set it to 0.
1368
	if (!empty($db_port))
1369
	{
1370
		if ($db_type == 'mysql' && $db_port == ini_get('mysqli.default_port'))
1371
			$changes['db_port'] = 0;
1372
		elseif ($db_type == 'postgresql' && $db_port == 5432)
1373
			$changes['db_port'] = 0;
1374
	}
1375
1376
	// Maybe we haven't had this option yet?
1377
	if (empty($packagesdir))
1378
		$changes['packagesdir'] = fixRelativePath($boarddir) . '/Packages';
1379
1380
	// Add support for $tasksdir var.
1381
	if (empty($tasksdir))
1382
		$changes['tasksdir'] = fixRelativePath($sourcedir) . '/tasks';
1383
1384
	// Make sure we fix the language as well.
1385
	if (stristr($language, '-utf8'))
1386
		$changes['language'] = str_ireplace('-utf8', '', $language);
1387
1388
	// @todo Maybe change the cookie name if going to 1.1, too?
1389
1390
	// Ensure this doesn't get lost in translation.
1391
	$changes['upgradeData'] = base64_encode(json_encode($upcontext['user']));
1392
1393
	// Update Settings.php with the new settings, and rebuild if they selected that option.
1394
	require_once($sourcedir . '/Subs.php');
1395
	require_once($sourcedir . '/Subs-Admin.php');
1396
	updateSettingsFile($changes, false, !empty($_POST['migrateSettings']));
1397
1398
	if ($command_line)
1399
		echo ' Successful.' . "\n";
1400
1401
	// Are we doing debug?
1402
	if (isset($_POST['debug']))
1403
	{
1404
		$upcontext['upgrade_status']['debug'] = true;
1405
		$is_debug = true;
1406
	}
1407
1408
	// If we're not backing up then jump one.
1409
	if (empty($_POST['backup']))
1410
		$upcontext['current_step']++;
1411
1412
	// If we've got here then let's proceed to the next step!
1413
	return true;
1414
}
1415
1416
// Backup the database - why not...
1417
function BackupDatabase()
1418
{
1419
	global $upcontext, $db_prefix, $command_line, $support_js, $file_steps, $smcFunc, $txt;
1420
1421
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'backup_xml' : 'backup_database';
1422
	$upcontext['page_title'] = $txt['backup_database'];
1423
1424
	// Done it already - js wise?
1425
	if (!empty($_POST['backup_done']))
1426
		return true;
1427
1428
	// Some useful stuff here.
1429
	db_extend();
1430
1431
	// Might need this as well
1432
	db_extend('packages');
1433
1434
	// Get all the table names.
1435
	$filter = str_replace('_', '\_', preg_match('~^`(.+?)`\.(.+?)$~', $db_prefix, $match) != 0 ? $match[2] : $db_prefix) . '%';
1436
	$db = preg_match('~^`(.+?)`\.(.+?)$~', $db_prefix, $match) != 0 ? strtr($match[1], array('`' => '')) : false;
1437
	$tables = $smcFunc['db_list_tables']($db, $filter);
1438
1439
	$table_names = array();
1440
	foreach ($tables as $table)
1441
		if (substr($table, 0, 7) !== 'backup_')
1442
			$table_names[] = $table;
1443
1444
	$upcontext['table_count'] = count($table_names);
1445
	$upcontext['cur_table_num'] = $_GET['substep'];
1446
	$upcontext['cur_table_name'] = str_replace($db_prefix, '', isset($table_names[$_GET['substep']]) ? $table_names[$_GET['substep']] : $table_names[0]);
1447
	$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
1448
	// For non-java auto submit...
1449
	$file_steps = $upcontext['table_count'];
1450
1451
	// What ones have we already done?
1452
	foreach ($table_names as $id => $table)
1453
		if ($id < $_GET['substep'])
1454
			$upcontext['previous_tables'][] = $table;
1455
1456
	if ($command_line)
1457
		echo 'Backing Up Tables.';
1458
1459
	// If we don't support javascript we backup here.
1460
	if (!$support_js || isset($_GET['xml']))
1461
	{
1462
		// Backup each table!
1463
		for ($substep = $_GET['substep'], $n = count($table_names); $substep < $n; $substep++)
1464
		{
1465
			$upcontext['cur_table_name'] = str_replace($db_prefix, '', (isset($table_names[$substep + 1]) ? $table_names[$substep + 1] : $table_names[$substep]));
1466
			$upcontext['cur_table_num'] = $substep + 1;
1467
1468
			$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
1469
1470
			// Do we need to pause?
1471
			nextSubstep($substep);
1472
1473
			backupTable($table_names[$substep]);
1474
1475
			// If this is XML to keep it nice for the user do one table at a time anyway!
1476
			if (isset($_GET['xml']))
1477
				return upgradeExit();
1478
		}
1479
1480
		if ($command_line)
1481
		{
1482
			echo "\n" . ' Successful.\'' . "\n";
1483
			flush();
1484
		}
1485
		$upcontext['step_progress'] = 100;
1486
1487
		$_GET['substep'] = 0;
1488
		// Make sure we move on!
1489
		return true;
1490
	}
1491
1492
	// Either way next place to post will be database changes!
1493
	$_GET['substep'] = 0;
1494
	return false;
1495
}
1496
1497
// Backup one table...
1498
function backupTable($table)
1499
{
1500
	global $command_line, $db_prefix, $smcFunc;
1501
1502
	if ($command_line)
1503
	{
1504
		echo "\n" . ' +++ Backing up \"' . str_replace($db_prefix, '', $table) . '"...';
1505
		flush();
1506
	}
1507
1508
	$smcFunc['db_backup_table']($table, 'backup_' . $table);
1509
1510
	if ($command_line)
1511
		echo ' done.';
1512
}
1513
1514
// Step 2: Everything.
1515
function DatabaseChanges()
1516
{
1517
	global $db_prefix, $modSettings, $smcFunc, $txt;
1518
	global $upcontext, $support_js, $db_type;
1519
1520
	// Have we just completed this?
1521
	if (!empty($_POST['database_done']))
1522
		return true;
1523
1524
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'database_xml' : 'database_changes';
1525
	$upcontext['page_title'] = $txt['database_changes'];
1526
1527
	// All possible files.
1528
	// Name, < version, insert_on_complete
1529
	// Last entry in array indicates whether to use sql_mode of STRICT or not.
1530
	$files = array(
1531
		array('upgrade_1-0.sql', '1.1', '1.1 RC0', false),
1532
		array('upgrade_1-1.sql', '2.0', '2.0 a', false),
1533
		array('upgrade_2-0_' . $db_type . '.sql', '2.1', '2.1 dev0', false),
1534
		array('upgrade_2-1_' . $db_type . '.sql', '3.0', SMF_VERSION, true),
1535
	);
1536
1537
	// How many files are there in total?
1538
	if (isset($_GET['filecount']))
1539
		$upcontext['file_count'] = (int) $_GET['filecount'];
1540
	else
1541
	{
1542
		$upcontext['file_count'] = 0;
1543
		foreach ($files as $file)
1544
		{
1545
			if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < $file[1])
1546
				$upcontext['file_count']++;
1547
		}
1548
	}
1549
1550
	// Do each file!
1551
	$did_not_do = count($files) - $upcontext['file_count'];
1552
	$upcontext['step_progress'] = 0;
1553
	$upcontext['cur_file_num'] = 0;
1554
	foreach ($files as $file)
1555
	{
1556
		if ($did_not_do)
1557
			$did_not_do--;
1558
		else
1559
		{
1560
			$upcontext['cur_file_num']++;
1561
			$upcontext['cur_file_name'] = $file[0];
1562
			// Do we actually need to do this still?
1563
			if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < $file[1])
1564
			{
1565
				setSqlMode($file[3]);
1566
				$nextFile = parse_sql(dirname(__FILE__) . '/' . $file[0]);
1567
				if ($nextFile)
1568
				{
1569
					// Only update the version of this if complete.
1570
					$smcFunc['db_insert']('replace',
1571
						$db_prefix . 'settings',
1572
						array('variable' => 'string', 'value' => 'string'),
1573
						array('smfVersion', $file[2]),
1574
						array('variable')
1575
					);
1576
1577
					$modSettings['smfVersion'] = $file[2];
1578
				}
1579
1580
				// If this is XML we only do this stuff once.
1581
				if (isset($_GET['xml']))
1582
				{
1583
					// Flag to move on to the next.
1584
					$upcontext['completed_step'] = true;
1585
					// Did we complete the whole file?
1586
					if ($nextFile)
1587
						$upcontext['current_debug_item_num'] = -1;
1588
					return upgradeExit();
1589
				}
1590
				elseif ($support_js)
1591
					break;
1592
			}
1593
			// Set the progress bar to be right as if we had - even if we hadn't...
1594
			$upcontext['step_progress'] = ($upcontext['cur_file_num'] / $upcontext['file_count']) * 100;
1595
		}
1596
	}
1597
1598
	$_GET['substep'] = 0;
1599
	// So the template knows we're done.
1600
	if (!$support_js)
1601
	{
1602
		$upcontext['changes_complete'] = true;
1603
1604
		return true;
1605
	}
1606
	return false;
1607
}
1608
1609
// Different versions of the files use different sql_modes
1610
function setSqlMode($strict = true)
1611
{
1612
	global $db_type, $db_connection;
1613
1614
	if ($db_type != 'mysql')
1615
		return;
1616
1617
	if ($strict)
1618
		$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';
1619
	else
1620
		$mode = '';
1621
1622
	mysqli_query($db_connection, 'SET SESSION sql_mode = \'' . $mode . '\'');
1623
1624
	return;
1625
}
1626
1627
// Delete the damn thing!
1628
function DeleteUpgrade()
1629
{
1630
	global $command_line, $language, $upcontext, $sourcedir;
1631
	global $user_info, $maintenance, $smcFunc, $db_type, $txt, $settings;
1632
1633
	// Now it's nice to have some of the basic SMF source files.
1634
	if (!isset($_GET['ssi']) && !$command_line)
1635
		redirectLocation('&ssi=1');
1636
1637
	$upcontext['sub_template'] = 'upgrade_complete';
1638
	$upcontext['page_title'] = $txt['upgrade_complete'];
1639
1640
	$endl = $command_line ? "\n" : '<br>' . "\n";
1641
1642
	$changes = array(
1643
		'language' => (substr($language, -4) == '.lng' ? substr($language, 0, -4) : $language),
1644
		'db_error_send' => true,
1645
		'upgradeData' => null,
1646
	);
1647
1648
	// Are we in maintenance mode?
1649
	if (isset($upcontext['user']['main']))
1650
	{
1651
		if ($command_line)
1652
			echo ' * ';
1653
		$upcontext['removed_maintenance'] = true;
1654
		$changes['maintenance'] = $upcontext['user']['main'];
1655
	}
1656
	// Otherwise if somehow we are in 2 let's go to 1.
1657
	elseif (!empty($maintenance) && $maintenance == 2)
1658
		$changes['maintenance'] = 1;
1659
1660
	// Wipe this out...
1661
	$upcontext['user'] = array();
1662
1663
	require_once($sourcedir . '/Subs.php');
1664
	require_once($sourcedir . '/Subs-Admin.php');
1665
	updateSettingsFile($changes);
1666
1667
	// Clean any old cache files away.
1668
	upgrade_clean_cache();
1669
1670
	// Can we delete the file?
1671
	$upcontext['can_delete_script'] = is_writable(dirname(__FILE__)) || is_writable(__FILE__);
1672
1673
	// Now is the perfect time to fetch the SM files.
1674
	if ($command_line)
1675
		cli_scheduled_fetchSMfiles();
1676
	else
1677
	{
1678
		require_once($sourcedir . '/ScheduledTasks.php');
1679
		scheduled_fetchSMfiles(); // Now go get those files!
1680
		// This is needed in case someone invokes the upgrader using https when upgrading an http forum
1681
		if (httpsOn())
1682
			$settings['default_theme_url'] = strtr($settings['default_theme_url'], array('http://' => 'https://'));
1683
	}
1684
1685
	// Log what we've done.
1686
	if (empty($user_info['id']))
1687
		$user_info['id'] = !empty($upcontext['user']['id']) ? $upcontext['user']['id'] : 0;
1688
1689
	// Log the action manually, so CLI still works.
1690
	$smcFunc['db_insert']('',
1691
		'{db_prefix}log_actions',
1692
		array(
1693
			'log_time' => 'int', 'id_log' => 'int', 'id_member' => 'int', 'ip' => 'inet', 'action' => 'string',
1694
			'id_board' => 'int', 'id_topic' => 'int', 'id_msg' => 'int', 'extra' => 'string-65534',
1695
		),
1696
		array(
1697
			time(), 3, $user_info['id'], $command_line ? '127.0.0.1' : $user_info['ip'], 'upgrade',
1698
			0, 0, 0, json_encode(array('version' => SMF_FULL_VERSION, 'member' => $user_info['id'])),
1699
		),
1700
		array('id_action')
1701
	);
1702
	$user_info['id'] = 0;
1703
1704
	if ($command_line)
1705
	{
1706
		echo $endl;
1707
		echo 'Upgrade Complete!', $endl;
1708
		echo 'Please delete this file as soon as possible for security reasons.', $endl;
1709
		exit;
0 ignored issues
show
Using exit here is not recommended.

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

Loading history...
1710
	}
1711
1712
	// Make sure it says we're done.
1713
	$upcontext['overall_percent'] = 100;
1714
	if (isset($upcontext['step_progress']))
1715
		unset($upcontext['step_progress']);
1716
1717
	$_GET['substep'] = 0;
1718
	return false;
1719
}
1720
1721
// Just like the built in one, but setup for CLI to not use themes.
1722
function cli_scheduled_fetchSMfiles()
1723
{
1724
	global $sourcedir, $language, $modSettings, $smcFunc;
1725
1726
	if (empty($modSettings['time_format']))
1727
		$modSettings['time_format'] = '%B %d, %Y, %I:%M:%S %p';
1728
1729
	// What files do we want to get
1730
	$request = $smcFunc['db_query']('', '
1731
		SELECT id_file, filename, path, parameters
1732
		FROM {db_prefix}admin_info_files',
1733
		array(
1734
		)
1735
	);
1736
1737
	$js_files = array();
1738
	while ($row = $smcFunc['db_fetch_assoc']($request))
1739
	{
1740
		$js_files[$row['id_file']] = array(
1741
			'filename' => $row['filename'],
1742
			'path' => $row['path'],
1743
			'parameters' => sprintf($row['parameters'], $language, urlencode($modSettings['time_format']), urlencode(SMF_FULL_VERSION)),
1744
		);
1745
	}
1746
	$smcFunc['db_free_result']($request);
1747
1748
	// We're gonna need fetch_web_data() to pull this off.
1749
	require_once($sourcedir . '/Subs.php');
1750
1751
	foreach ($js_files as $ID_FILE => $file)
1752
	{
1753
		// Create the url
1754
		$server = empty($file['path']) || substr($file['path'], 0, 7) != 'http://' ? 'https://www.simplemachines.org' : '';
1755
		$url = $server . (!empty($file['path']) ? $file['path'] : $file['path']) . $file['filename'] . (!empty($file['parameters']) ? '?' . $file['parameters'] : '');
1756
1757
		// Get the file
1758
		$file_data = fetch_web_data($url);
1759
1760
		// If we got an error - give up - the site might be down.
1761
		if ($file_data === false)
1762
			return throw_error(sprintf('Could not retrieve the file %1$s.', $url));
1763
1764
		// Save the file to the database.
1765
		$smcFunc['db_query']('substring', '
1766
			UPDATE {db_prefix}admin_info_files
1767
			SET data = SUBSTRING({string:file_data}, 1, 65534)
1768
			WHERE id_file = {int:id_file}',
1769
			array(
1770
				'id_file' => $ID_FILE,
1771
				'file_data' => $file_data,
1772
			)
1773
		);
1774
	}
1775
	return true;
1776
}
1777
1778
function convertSettingsToTheme()
1779
{
1780
	global $db_prefix, $modSettings, $smcFunc;
1781
1782
	$values = array(
1783
		'show_latest_member' => @$GLOBALS['showlatestmember'],
1784
		'show_bbc' => isset($GLOBALS['showyabbcbutt']) ? $GLOBALS['showyabbcbutt'] : @$GLOBALS['showbbcbutt'],
1785
		'show_modify' => @$GLOBALS['showmodify'],
1786
		'show_user_images' => @$GLOBALS['showuserpic'],
1787
		'show_blurb' => @$GLOBALS['showusertext'],
1788
		'show_gender' => @$GLOBALS['showgenderimage'],
1789
		'show_newsfader' => @$GLOBALS['shownewsfader'],
1790
		'display_recent_bar' => @$GLOBALS['Show_RecentBar'],
1791
		'show_member_bar' => @$GLOBALS['Show_MemberBar'],
1792
		'linktree_link' => @$GLOBALS['curposlinks'],
1793
		'show_profile_buttons' => @$GLOBALS['profilebutton'],
1794
		'show_mark_read' => @$GLOBALS['showmarkread'],
1795
		'newsfader_time' => @$GLOBALS['fadertime'],
1796
		'use_image_buttons' => empty($GLOBALS['MenuType']) ? 1 : 0,
1797
		'enable_news' => @$GLOBALS['enable_news'],
1798
		'return_to_post' => @$modSettings['returnToPost'],
1799
	);
1800
1801
	$themeData = array();
1802
	foreach ($values as $variable => $value)
1803
	{
1804
		if (!isset($value) || $value === null)
1805
			$value = 0;
1806
1807
		$themeData[] = array(0, 1, $variable, $value);
1808
	}
1809
	if (!empty($themeData))
1810
	{
1811
		$smcFunc['db_insert']('ignore',
1812
			$db_prefix . 'themes',
1813
			array('id_member' => 'int', 'id_theme' => 'int', 'variable' => 'string', 'value' => 'string'),
1814
			$themeData,
1815
			array('id_member', 'id_theme', 'variable')
1816
		);
1817
	}
1818
}
1819
1820
// This function only works with MySQL but that's fine as it is only used for v1.0.
1821
function convertSettingstoOptions()
1822
{
1823
	global $modSettings, $smcFunc;
1824
1825
	// Format: new_setting -> old_setting_name.
1826
	$values = array(
1827
		'calendar_start_day' => 'cal_startmonday',
1828
		'view_newest_first' => 'viewNewestFirst',
1829
		'view_newest_pm_first' => 'viewNewestFirst',
1830
	);
1831
1832
	foreach ($values as $variable => $value)
1833
	{
1834
		if (empty($modSettings[$value[0]]))
1835
			continue;
1836
1837
		$smcFunc['db_query']('', '
1838
			INSERT IGNORE INTO {db_prefix}themes
1839
				(id_member, id_theme, variable, value)
1840
			SELECT id_member, 1, {string:variable}, {string:value}
1841
			FROM {db_prefix}members',
1842
			array(
1843
				'variable' => $variable,
1844
				'value' => $modSettings[$value[0]],
1845
				'db_error_skip' => true,
1846
			)
1847
		);
1848
1849
		$smcFunc['db_query']('', '
1850
			INSERT IGNORE INTO {db_prefix}themes
1851
				(id_member, id_theme, variable, value)
1852
			VALUES (-1, 1, {string:variable}, {string:value})',
1853
			array(
1854
				'variable' => $variable,
1855
				'value' => $modSettings[$value[0]],
1856
				'db_error_skip' => true,
1857
			)
1858
		);
1859
	}
1860
}
1861
1862
function php_version_check()
1863
{
1864
	return version_compare(PHP_VERSION, $GLOBALS['required_php_version'], '>=');
1865
}
1866
1867
function db_version_check()
1868
{
1869
	global $db_type, $databases;
1870
1871
	$curver = eval($databases[$db_type]['version_check']);
1872
	$curver = preg_replace('~\-.+?$~', '', $curver);
1873
1874
	return version_compare($databases[$db_type]['version'], $curver, '<=');
1875
}
1876
1877
function fixRelativePath($path)
1878
{
1879
	global $install_path;
1880
1881
	// Fix the . at the start, clear any duplicate slashes, and fix any trailing slash...
1882
	return addslashes(preg_replace(array('~^\.([/\\\]|$)~', '~[/]+~', '~[\\\]+~', '~[/\\\]$~'), array($install_path . '$1', '/', '\\', ''), $path));
1883
}
1884
1885
function parse_sql($filename)
1886
{
1887
	global $db_prefix, $db_collation, $boarddir, $boardurl, $command_line, $file_steps, $step_progress, $custom_warning;
1888
	global $upcontext, $support_js, $is_debug, $db_type, $db_character_set;
1889
1890
/*
1891
	Failure allowed on:
1892
		- INSERT INTO but not INSERT IGNORE INTO.
1893
		- UPDATE IGNORE but not UPDATE.
1894
		- ALTER TABLE and ALTER IGNORE TABLE.
1895
		- DROP TABLE.
1896
	Yes, I realize that this is a bit confusing... maybe it should be done differently?
1897
1898
	If a comment...
1899
		- begins with --- it is to be output, with a break only in debug mode. (and say successful\n\n if there was one before.)
1900
		- begins with ---# it is a debugging statement, no break - only shown at all in debug.
1901
		- is only ---#, it is "done." and then a break - only shown in debug.
1902
		- begins with ---{ it is a code block terminating at ---}.
1903
1904
	Every block of between "--- ..."s is a step.  Every "---#" section represents a substep.
1905
1906
	Replaces the following variables:
1907
		- {$boarddir}
1908
		- {$boardurl}
1909
		- {$db_prefix}
1910
		- {$db_collation}
1911
*/
1912
1913
	// May want to use extended functionality.
1914
	db_extend();
1915
	db_extend('packages');
1916
1917
	// Our custom error handler - does nothing but does stop public errors from XML!
1918
	set_error_handler(
1919
		function($errno, $errstr, $errfile, $errline) use ($support_js)
1920
		{
1921
			if ($support_js)
1922
				return true;
1923
			else
1924
				echo 'Error: ' . $errstr . ' File: ' . $errfile . ' Line: ' . $errline;
1925
		}
1926
	);
1927
1928
	// If we're on MySQL, set {db_collation}; this approach is used throughout upgrade_2-0_mysql.php to set new tables to utf8
1929
	// Note it is expected to be in the format: ENGINE=MyISAM{$db_collation};
1930
	if ($db_type == 'mysql')
1931
		$db_collation = ' DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci';
1932
	else
1933
		$db_collation = '';
1934
1935
	$endl = $command_line ? "\n" : '<br>' . "\n";
1936
1937
	$lines = file($filename);
1938
1939
	$current_type = 'sql';
1940
	$current_data = '';
1941
	$substep = 0;
1942
	$last_step = '';
1943
1944
	// Make sure all newly created tables will have the proper characters set; this approach is used throughout upgrade_2-1_mysql.php
1945
	if (isset($db_character_set) && $db_character_set === 'utf8')
1946
		$lines = str_replace(') ENGINE=MyISAM;', ') ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;', $lines);
1947
1948
	// Count the total number of steps within this file - for progress.
1949
	$file_steps = substr_count(implode('', $lines), '---#');
1950
	$upcontext['total_items'] = substr_count(implode('', $lines), '--- ');
1951
	$upcontext['debug_items'] = $file_steps;
1952
	$upcontext['current_item_num'] = 0;
1953
	$upcontext['current_item_name'] = '';
1954
	$upcontext['current_debug_item_num'] = 0;
1955
	$upcontext['current_debug_item_name'] = '';
1956
	// This array keeps a record of what we've done in case java is dead...
1957
	$upcontext['actioned_items'] = array();
1958
1959
	$done_something = false;
1960
1961
	foreach ($lines as $line_number => $line)
1962
	{
1963
		$do_current = $substep >= $_GET['substep'];
1964
1965
		// Get rid of any comments in the beginning of the line...
1966
		if (substr(trim($line), 0, 2) === '/*')
1967
			$line = preg_replace('~/\*.+?\*/~', '', $line);
1968
1969
		// Always flush.  Flush, flush, flush.  Flush, flush, flush, flush!  FLUSH!
1970
		if ($is_debug && !$support_js && $command_line)
1971
			flush();
1972
1973
		if (trim($line) === '')
1974
			continue;
1975
1976
		if (trim(substr($line, 0, 3)) === '---')
1977
		{
1978
			$type = substr($line, 3, 1);
1979
1980
			// An error??
1981
			if (trim($current_data) != '' && $type !== '}')
1982
			{
1983
				$upcontext['error_message'] = 'Error in upgrade script - line ' . $line_number . '!' . $endl;
1984
				if ($command_line)
1985
					echo $upcontext['error_message'];
1986
			}
1987
1988
			if ($type == ' ')
1989
			{
1990
				if (!$support_js && $do_current && $_GET['substep'] != 0 && $command_line)
1991
				{
1992
					echo ' Successful.', $endl;
1993
					flush();
1994
				}
1995
1996
				$last_step = htmlspecialchars(rtrim(substr($line, 4)));
1997
				$upcontext['current_item_num']++;
1998
				$upcontext['current_item_name'] = $last_step;
1999
2000
				if ($do_current)
2001
				{
2002
					$upcontext['actioned_items'][] = $last_step;
2003
					if ($command_line)
2004
						echo ' * ';
2005
2006
					// Starting a new main step in our DB changes, so it's time to reset this.
2007
					$upcontext['skip_db_substeps'] = false;
2008
				}
2009
			}
2010
			elseif ($type == '#')
2011
			{
2012
				$upcontext['step_progress'] += (100 / $upcontext['file_count']) / $file_steps;
2013
2014
				$upcontext['current_debug_item_num']++;
2015
				if (trim($line) != '---#')
2016
					$upcontext['current_debug_item_name'] = htmlspecialchars(rtrim(substr($line, 4)));
2017
2018
				// Have we already done something?
2019
				if (isset($_GET['xml']) && $done_something)
2020
				{
2021
					restore_error_handler();
2022
					return $upcontext['current_debug_item_num'] >= $upcontext['debug_items'] ? true : false;
2023
				}
2024
2025
				if ($do_current)
2026
				{
2027
					if (trim($line) == '---#' && $command_line)
2028
						echo ' done.', $endl;
2029
					elseif ($command_line)
2030
						echo ' +++ ', rtrim(substr($line, 4));
2031
					elseif (trim($line) != '---#')
2032
					{
2033
						if ($is_debug)
2034
							$upcontext['actioned_items'][] = $upcontext['current_debug_item_name'];
2035
					}
2036
				}
2037
2038
				if ($substep < $_GET['substep'] && $substep + 1 >= $_GET['substep'])
2039
				{
2040
					if ($command_line)
2041
						echo ' * ';
2042
					else
2043
						$upcontext['actioned_items'][] = $last_step;
2044
				}
2045
2046
				// Small step - only if we're actually doing stuff.
2047
				if ($do_current)
2048
					nextSubstep(++$substep);
2049
				else
2050
					$substep++;
2051
			}
2052
			elseif ($type == '{')
2053
				$current_type = 'code';
2054
			elseif ($type == '}')
2055
			{
2056
				$current_type = 'sql';
2057
2058
				if (!$do_current || !empty($upcontext['skip_db_substeps']))
2059
				{
2060
					$current_data = '';
2061
2062
					// Avoid confusion when skipping something we normally would have done
2063
					if ($do_current)
2064
						$done_something = true;
2065
2066
					continue;
2067
				}
2068
2069
				// @todo Update this to a try/catch for PHP 7+, because eval() now throws an exception for parse errors instead of returning false
2070
				if (eval('global $db_prefix, $modSettings, $smcFunc, $txt, $upcontext, $db_name; ' . $current_data) === false)
2071
				{
2072
					$upcontext['error_message'] = 'Error in upgrade script ' . basename($filename) . ' on line ' . $line_number . '!' . $endl;
2073
					if ($command_line)
2074
						echo $upcontext['error_message'];
2075
				}
2076
2077
				// Done with code!
2078
				$current_data = '';
2079
				$done_something = true;
2080
			}
2081
2082
			continue;
2083
		}
2084
2085
		$current_data .= $line;
2086
		if (substr(rtrim($current_data), -1) === ';' && $current_type === 'sql')
2087
		{
2088
			if ((!$support_js || isset($_GET['xml'])))
2089
			{
2090
				if (!$do_current || !empty($upcontext['skip_db_substeps']))
2091
				{
2092
					$current_data = '';
2093
2094
					if ($do_current)
2095
						$done_something = true;
2096
2097
					continue;
2098
				}
2099
2100
				$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));
2101
2102
				upgrade_query($current_data);
2103
2104
				// @todo This will be how it kinda does it once mysql all stripped out - needed for postgre (etc).
2105
				/*
2106
				$result = $smcFunc['db_query']('', $current_data, false, false);
2107
				// Went wrong?
2108
				if (!$result)
2109
				{
2110
					// Bit of a bodge - do we want the error?
2111
					if (!empty($upcontext['return_error']))
2112
					{
2113
						$upcontext['error_message'] = $smcFunc['db_error']($db_connection);
2114
						return false;
2115
					}
2116
				}*/
2117
				$done_something = true;
2118
			}
2119
			$current_data = '';
2120
		}
2121
		// If this is xml based and we're just getting the item name then that's grand.
2122
		elseif ($support_js && !isset($_GET['xml']) && $upcontext['current_debug_item_name'] != '' && $do_current)
2123
		{
2124
			restore_error_handler();
2125
			return false;
2126
		}
2127
2128
		// Clean up by cleaning any step info.
2129
		$step_progress = array();
2130
		$custom_warning = '';
2131
	}
2132
2133
	// Put back the error handler.
2134
	restore_error_handler();
2135
2136
	if ($command_line)
2137
	{
2138
		echo ' Successful.' . "\n";
2139
		flush();
2140
	}
2141
2142
	$_GET['substep'] = 0;
2143
	return true;
2144
}
2145
2146
function upgrade_query($string, $unbuffered = false)
2147
{
2148
	global $db_connection, $db_server, $db_user, $db_passwd, $db_type;
2149
	global $command_line, $upcontext, $upgradeurl, $modSettings;
2150
	global $db_name, $db_unbuffered, $smcFunc, $txt;
2151
2152
	// Get the query result - working around some SMF specific security - just this once!
2153
	$modSettings['disableQueryCheck'] = true;
2154
	$db_unbuffered = $unbuffered;
2155
	$ignore_insert_error = false;
2156
2157
	// If we got an old pg version and use a insert ignore query
2158
	if ($db_type == 'postgresql' && !$smcFunc['db_native_replace']() && strpos($string, 'ON CONFLICT DO NOTHING') !== false)
2159
	{
2160
		$ignore_insert_error = true;
2161
		$string = str_replace('ON CONFLICT DO NOTHING', '', $string);
2162
	}
2163
	$result = $smcFunc['db_query']('', $string, array('security_override' => true, 'db_error_skip' => true));
2164
	$db_unbuffered = false;
2165
2166
	// Failure?!
2167
	if ($result !== false)
2168
		return $result;
2169
2170
	$db_error_message = $smcFunc['db_error']($db_connection);
2171
	// If MySQL we do something more clever.
2172
	if ($db_type == 'mysql')
2173
	{
2174
		$mysqli_errno = mysqli_errno($db_connection);
2175
		$error_query = in_array(substr(trim($string), 0, 11), array('INSERT INTO', 'UPDATE IGNO', 'ALTER TABLE', 'DROP TABLE ', 'ALTER IGNOR', 'INSERT IGNO'));
2176
2177
		// Error numbers:
2178
		//    1016: Can't open file '....MYI'
2179
		//    1050: Table already exists.
2180
		//    1054: Unknown column name.
2181
		//    1060: Duplicate column name.
2182
		//    1061: Duplicate key name.
2183
		//    1062: Duplicate entry for unique key.
2184
		//    1068: Multiple primary keys.
2185
		//    1072: Key column '%s' doesn't exist in table.
2186
		//    1091: Can't drop key, doesn't exist.
2187
		//    1146: Table doesn't exist.
2188
		//    2013: Lost connection to server during query.
2189
2190
		if ($mysqli_errno == 1016)
2191
		{
2192
			if (preg_match('~\'([^\.\']+)~', $db_error_message, $match) != 0 && !empty($match[1]))
2193
			{
2194
				mysqli_query($db_connection, 'REPAIR TABLE `' . $match[1] . '`');
2195
				$result = mysqli_query($db_connection, $string);
2196
				if ($result !== false)
2197
					return $result;
2198
			}
2199
		}
2200
		elseif ($mysqli_errno == 2013)
2201
		{
2202
			$db_connection = mysqli_connect($db_server, $db_user, $db_passwd);
2203
			mysqli_select_db($db_connection, $db_name);
2204
			if ($db_connection)
2205
			{
2206
				$result = mysqli_query($db_connection, $string);
2207
				if ($result !== false)
2208
					return $result;
2209
			}
2210
		}
2211
		// Duplicate column name... should be okay ;).
2212
		elseif (in_array($mysqli_errno, array(1060, 1061, 1068, 1091)))
2213
			return false;
2214
		// Duplicate insert... make sure it's the proper type of query ;).
2215
		elseif (in_array($mysqli_errno, array(1054, 1062, 1146)) && $error_query)
2216
			return false;
2217
		// Creating an index on a non-existent column.
2218
		elseif ($mysqli_errno == 1072)
2219
			return false;
2220
		elseif ($mysqli_errno == 1050 && substr(trim($string), 0, 12) == 'RENAME TABLE')
2221
			return false;
2222
		// Testing for legacy tables or columns? Needed for 1.0 & 1.1 scripts.
2223
		elseif (in_array($mysqli_errno, array(1054, 1146)) && in_array(substr(trim($string), 0, 7), array('SELECT ', 'SHOW CO')))
2224
			return false;
2225
	}
2226
	// If a table already exists don't go potty.
2227
	else
2228
	{
2229
		if (in_array(substr(trim($string), 0, 8), array('CREATE T', 'CREATE S', 'DROP TABL', 'ALTER TA', 'CREATE I', 'CREATE U')))
2230
		{
2231
			if (strpos($db_error_message, 'exist') !== false)
2232
				return true;
2233
		}
2234
		elseif (strpos(trim($string), 'INSERT ') !== false)
2235
		{
2236
			if (strpos($db_error_message, 'duplicate') !== false || $ignore_insert_error)
2237
				return true;
2238
		}
2239
	}
2240
2241
	// Get the query string so we pass everything.
2242
	$query_string = '';
2243
	foreach ($_GET as $k => $v)
2244
		$query_string .= ';' . $k . '=' . $v;
2245
	if (strlen($query_string) != 0)
2246
		$query_string = '?' . substr($query_string, 1);
2247
2248
	if ($command_line)
2249
	{
2250
		echo 'Unsuccessful!  Database error message:', "\n", $db_error_message, "\n";
2251
		die;
0 ignored issues
show
Using exit here is not recommended.

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

Loading history...
2252
	}
2253
2254
	// Bit of a bodge - do we want the error?
2255
	if (!empty($upcontext['return_error']))
2256
	{
2257
		$upcontext['error_message'] = $db_error_message;
2258
		$upcontext['error_string'] = $string;
2259
		return false;
2260
	}
2261
2262
	// Otherwise we have to display this somewhere appropriate if possible.
2263
	$upcontext['forced_error_message'] = '
2264
			<strong>' . $txt['upgrade_unsuccessful'] . '</strong><br>
2265
2266
			<div style="margin: 2ex;">
2267
				' . $txt['upgrade_thisquery'] . '
2268
				<blockquote><pre>' . nl2br(htmlspecialchars(trim($string))) . ';</pre></blockquote>
2269
2270
				' . $txt['upgrade_causerror'] . '
2271
				<blockquote>' . nl2br(htmlspecialchars($db_error_message)) . '</blockquote>
2272
			</div>
2273
2274
			<form action="' . $upgradeurl . $query_string . '" method="post">
2275
				<input type="submit" value="' . $txt['upgrade_respondtime_clickhere'] . '" class="button">
2276
			</form>
2277
		</div>';
2278
2279
	upgradeExit();
2280
}
2281
2282
// This performs a table alter, but does it unbuffered so the script can time out professionally.
2283
function protected_alter($change, $substep, $is_test = false)
2284
{
2285
	global $db_prefix, $smcFunc;
2286
2287
	db_extend('packages');
2288
2289
	// Firstly, check whether the current index/column exists.
2290
	$found = false;
2291
	if ($change['type'] === 'column')
2292
	{
2293
		$columns = $smcFunc['db_list_columns']('{db_prefix}' . $change['table'], true);
2294
		foreach ($columns as $column)
2295
		{
2296
			// Found it?
2297
			if ($column['name'] === $change['name'])
2298
			{
2299
				$found |= true;
2300
				// Do some checks on the data if we have it set.
2301
				if (isset($change['col_type']))
2302
					$found &= $change['col_type'] === $column['type'];
2303
				if (isset($change['null_allowed']))
2304
					$found &= $column['null'] == $change['null_allowed'];
2305
				if (isset($change['default']))
2306
					$found &= $change['default'] === $column['default'];
2307
			}
2308
		}
2309
	}
2310
	elseif ($change['type'] === 'index')
2311
	{
2312
		$request = upgrade_query('
2313
			SHOW INDEX
2314
			FROM ' . $db_prefix . $change['table']);
2315
		if ($request !== false)
2316
		{
2317
			$cur_index = array();
2318
2319
			while ($row = $smcFunc['db_fetch_assoc']($request))
2320
				if ($row['Key_name'] === $change['name'])
2321
					$cur_index[(int) $row['Seq_in_index']] = $row['Column_name'];
2322
2323
			ksort($cur_index, SORT_NUMERIC);
2324
			$found = array_values($cur_index) === $change['target_columns'];
2325
2326
			$smcFunc['db_free_result']($request);
2327
		}
2328
	}
2329
2330
	// If we're trying to add and it's added, we're done.
2331
	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...
2332
		return true;
2333
	// Otherwise if we're removing and it wasn't found we're also done.
2334
	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...
2335
		return true;
2336
	// Otherwise is it just a test?
2337
	elseif ($is_test)
2338
		return false;
2339
2340
	// Not found it yet? Bummer! How about we see if we're currently doing it?
2341
	$running = false;
2342
	$found = false;
2343
	while (1 == 1)
2344
	{
2345
		$request = upgrade_query('
2346
			SHOW FULL PROCESSLIST');
2347
		while ($row = $smcFunc['db_fetch_assoc']($request))
2348
		{
2349
			if (strpos($row['Info'], 'ALTER TABLE ' . $db_prefix . $change['table']) !== false && strpos($row['Info'], $change['text']) !== false)
2350
				$found = true;
2351
		}
2352
2353
		// Can't find it? Then we need to run it fools!
2354
		if (!$found && !$running)
2355
		{
2356
			$smcFunc['db_free_result']($request);
2357
2358
			$success = upgrade_query('
2359
				ALTER TABLE ' . $db_prefix . $change['table'] . '
2360
				' . $change['text'], true) !== false;
2361
2362
			if (!$success)
2363
				return false;
2364
2365
			// Return
2366
			$running = true;
2367
		}
2368
		// What if we've not found it, but we'd ran it already? Must of completed.
2369
		elseif (!$found)
2370
		{
2371
			$smcFunc['db_free_result']($request);
2372
			return true;
2373
		}
2374
2375
		// Pause execution for a sec or three.
2376
		sleep(3);
2377
2378
		// Can never be too well protected.
2379
		nextSubstep($substep);
2380
	}
2381
2382
	// Protect it.
2383
	nextSubstep($substep);
2384
}
2385
2386
/**
2387
 * Alter a text column definition preserving its character set.
2388
 *
2389
 * @param array $change
2390
 * @param int $substep
2391
 */
2392
function textfield_alter($change, $substep)
2393
{
2394
	global $db_prefix, $smcFunc;
2395
2396
	$request = $smcFunc['db_query']('', '
2397
		SHOW FULL COLUMNS
2398
		FROM {db_prefix}' . $change['table'] . '
2399
		LIKE {string:column}',
2400
		array(
2401
			'column' => $change['column'],
2402
			'db_error_skip' => true,
2403
		)
2404
	);
2405
	if ($smcFunc['db_num_rows']($request) === 0)
2406
		die('Unable to find column ' . $change['column'] . ' inside table ' . $db_prefix . $change['table']);
0 ignored issues
show
Using exit here is not recommended.

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

Loading history...
2407
	$table_row = $smcFunc['db_fetch_assoc']($request);
2408
	$smcFunc['db_free_result']($request);
2409
2410
	// If something of the current column definition is different, fix it.
2411
	$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']);
2412
2413
	// Columns that previously allowed null, need to be converted first.
2414
	$null_fix = strtolower($table_row['Null']) === 'yes' && !$change['null_allowed'];
2415
2416
	// Get the character set that goes with the collation of the column.
2417
	if ($column_fix && !empty($table_row['Collation']))
2418
	{
2419
		$request = $smcFunc['db_query']('', '
2420
			SHOW COLLATION
2421
			LIKE {string:collation}',
2422
			array(
2423
				'collation' => $table_row['Collation'],
2424
				'db_error_skip' => true,
2425
			)
2426
		);
2427
		// No results? Just forget it all together.
2428
		if ($smcFunc['db_num_rows']($request) === 0)
2429
			unset($table_row['Collation']);
2430
		else
2431
			$collation_info = $smcFunc['db_fetch_assoc']($request);
2432
		$smcFunc['db_free_result']($request);
2433
	}
2434
2435
	if ($column_fix)
2436
	{
2437
		// Make sure there are no NULL's left.
2438
		if ($null_fix)
2439
			$smcFunc['db_query']('', '
2440
				UPDATE {db_prefix}' . $change['table'] . '
2441
				SET ' . $change['column'] . ' = {string:default}
2442
				WHERE ' . $change['column'] . ' IS NULL',
2443
				array(
2444
					'default' => isset($change['default']) ? $change['default'] : '',
2445
					'db_error_skip' => true,
2446
				)
2447
			);
2448
2449
		// Do the actual alteration.
2450
		$smcFunc['db_query']('', '
2451
			ALTER TABLE {db_prefix}' . $change['table'] . '
2452
			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}' : ''),
2453
			array(
2454
				'default' => isset($change['default']) ? $change['default'] : '',
2455
				'db_error_skip' => true,
2456
			)
2457
		);
2458
	}
2459
	nextSubstep($substep);
2460
}
2461
2462
// Check if we need to alter this query.
2463
function checkChange(&$change)
2464
{
2465
	global $smcFunc, $db_type, $databases;
2466
	static $database_version, $where_field_support;
2467
2468
	// Attempt to find a database_version.
2469
	if (empty($database_version))
2470
	{
2471
		$database_version = $databases[$db_type]['version_check'];
2472
		$where_field_support = $db_type == 'mysql' && version_compare('5.0', $database_version, '<=');
2473
	}
2474
2475
	// Not a column we need to check on?
2476
	if (!in_array($change['name'], array('memberGroups', 'passwordSalt')))
2477
		return;
2478
2479
	// Break it up you (six|seven).
2480
	$temp = explode(' ', str_replace('NOT NULL', 'NOT_NULL', $change['text']));
2481
2482
	// Can we support a shortcut method?
2483
	if ($where_field_support)
2484
	{
2485
		// Get the details about this change.
2486
		$request = $smcFunc['db_query']('', '
2487
			SHOW FIELDS
2488
			FROM {db_prefix}{raw:table}
2489
			WHERE Field = {string:old_name} OR Field = {string:new_name}',
2490
			array(
2491
				'table' => $change['table'],
2492
				'old_name' => $temp[1],
2493
				'new_name' => $temp[2],
2494
			)
2495
		);
2496
		// !!! This doesn't technically work because we don't pass request into it, but it hasn't broke anything yet.
2497
		if ($smcFunc['db_num_rows'] != 1)
2498
			return;
2499
2500
		list (, $current_type) = $smcFunc['db_fetch_assoc']($request);
2501
		$smcFunc['db_free_result']($request);
2502
	}
2503
	else
2504
	{
2505
		// Do this the old fashion, sure method way.
2506
		$request = $smcFunc['db_query']('', '
2507
			SHOW FIELDS
2508
			FROM {db_prefix}{raw:table}',
2509
			array(
2510
				'table' => $change['table'],
2511
			)
2512
		);
2513
		// Mayday!
2514
		// !!! This doesn't technically work because we don't pass request into it, but it hasn't broke anything yet.
2515
		if ($smcFunc['db_num_rows'] == 0)
2516
			return;
2517
2518
		// Oh where, oh where has my little field gone. Oh where can it be...
2519
		while ($row = $smcFunc['db_query']($request))
2520
			if ($row['Field'] == $temp[1] || $row['Field'] == $temp[2])
2521
			{
2522
				$current_type = $row['Type'];
2523
				break;
2524
			}
2525
	}
2526
2527
	// If this doesn't match, the column may of been altered for a reason.
2528
	if (trim($current_type) != trim($temp[3]))
2529
		$temp[3] = $current_type;
2530
2531
	// Piece this back together.
2532
	$change['text'] = str_replace('NOT_NULL', 'NOT NULL', implode(' ', $temp));
2533
}
2534
2535
// The next substep.
2536
function nextSubstep($substep)
2537
{
2538
	global $start_time, $timeLimitThreshold, $command_line, $custom_warning;
2539
	global $step_progress, $is_debug, $upcontext;
2540
2541
	if ($_GET['substep'] < $substep)
2542
		$_GET['substep'] = $substep;
2543
2544
	if ($command_line)
2545
	{
2546
		if (time() - $start_time > 1 && empty($is_debug))
2547
		{
2548
			echo '.';
2549
			$start_time = time();
2550
		}
2551
		return;
2552
	}
2553
2554
	@set_time_limit(300);
2555
	if (function_exists('apache_reset_timeout'))
2556
		@apache_reset_timeout();
2557
2558
	if (time() - $start_time <= $timeLimitThreshold)
2559
		return;
2560
2561
	// Do we have some custom step progress stuff?
2562
	if (!empty($step_progress))
2563
	{
2564
		$upcontext['substep_progress'] = 0;
2565
		$upcontext['substep_progress_name'] = $step_progress['name'];
2566
		if ($step_progress['current'] > $step_progress['total'])
2567
			$upcontext['substep_progress'] = 99.9;
2568
		else
2569
			$upcontext['substep_progress'] = ($step_progress['current'] / $step_progress['total']) * 100;
2570
2571
		// Make it nicely rounded.
2572
		$upcontext['substep_progress'] = round($upcontext['substep_progress'], 1);
2573
	}
2574
2575
	// If this is XML we just exit right away!
2576
	if (isset($_GET['xml']))
2577
		return upgradeExit();
2578
2579
	// We're going to pause after this!
2580
	$upcontext['pause'] = true;
2581
2582
	$upcontext['query_string'] = '';
2583
	foreach ($_GET as $k => $v)
2584
	{
2585
		if ($k != 'data' && $k != 'substep' && $k != 'step')
2586
			$upcontext['query_string'] .= ';' . $k . '=' . $v;
2587
	}
2588
2589
	// Custom warning?
2590
	if (!empty($custom_warning))
2591
		$upcontext['custom_warning'] = $custom_warning;
2592
2593
	upgradeExit();
2594
}
2595
2596
function cmdStep0()
2597
{
2598
	global $boarddir, $sourcedir, $modSettings, $start_time, $cachedir, $databases, $db_type, $smcFunc, $upcontext;
2599
	global $is_debug;
2600
	$start_time = time();
2601
2602
	ob_end_clean();
2603
	ob_implicit_flush(1);
2604
	@set_time_limit(600);
2605
2606
	if (!isset($_SERVER['argv']))
2607
		$_SERVER['argv'] = array();
2608
	$_GET['maint'] = 1;
2609
2610
	foreach ($_SERVER['argv'] as $i => $arg)
2611
	{
2612
		if (preg_match('~^--language=(.+)$~', $arg, $match) != 0)
2613
			$upcontext['lang'] = $match[1];
2614
		elseif (preg_match('~^--path=(.+)$~', $arg) != 0)
2615
			continue;
2616
		elseif ($arg == '--no-maintenance')
2617
			$_GET['maint'] = 0;
2618
		elseif ($arg == '--debug')
2619
			$is_debug = true;
2620
		elseif ($arg == '--backup')
2621
			$_POST['backup'] = 1;
2622
		elseif ($arg == '--template' && (file_exists($boarddir . '/template.php') || file_exists($boarddir . '/template.html') && !file_exists($modSettings['theme_dir'] . '/converted')))
2623
			$_GET['conv'] = 1;
2624
		elseif ($i != 0)
2625
		{
2626
			echo 'SMF Command-line Upgrader
2627
Usage: /path/to/php -f ' . basename(__FILE__) . ' -- [OPTION]...
2628
2629
	--language=LANG         Reset the forum\'s language to LANG.
2630
	--no-maintenance        Don\'t put the forum into maintenance mode.
2631
	--debug                 Output debugging information.
2632
	--backup                Create backups of tables with "backup_" prefix.';
2633
			echo "\n";
2634
			exit;
0 ignored issues
show
Using exit here is not recommended.

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

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