Passed
Pull Request — release-2.1 (#5986)
by Jeremy
04:34
created

MySQLConvertOldIp()   F

Complexity

Conditions 16
Paths 488

Size

Total Lines 140
Code Lines 78

Duplication

Lines 0
Ratio 0 %

Importance

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

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

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

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

Loading history...
388
		}
389
390
		if (!isset($_GET['xml']))
391
			template_upgrade_above();
392
		else
393
		{
394
			header('content-type: text/xml; charset=UTF-8');
395
			// Sadly we need to retain the $_GET data thanks to the old upgrade scripts.
396
			$upcontext['get_data'] = array();
397
			foreach ($_GET as $k => $v)
398
			{
399
				if (substr($k, 0, 3) != 'amp' && !in_array($k, array('xml', 'substep', 'lang', 'data', 'step', 'filecount')))
400
				{
401
					$upcontext['get_data'][$k] = $v;
402
				}
403
			}
404
			template_xml_above();
405
		}
406
407
		// Call the template.
408
		if (isset($upcontext['sub_template']))
409
		{
410
			$upcontext['upgrade_status']['curstep'] = $upcontext['current_step'];
411
			$upcontext['form_url'] = $upgradeurl . '?step=' . $upcontext['current_step'] . '&amp;substep=' . $_GET['substep'] . '&amp;data=' . base64_encode(json_encode($upcontext['upgrade_status']));
412
413
			// Custom stuff to pass back?
414
			if (!empty($upcontext['query_string']))
415
				$upcontext['form_url'] .= $upcontext['query_string'];
416
417
			// Call the appropriate subtemplate
418
			if (is_callable('template_' . $upcontext['sub_template']))
419
				call_user_func('template_' . $upcontext['sub_template']);
420
			else
421
				die(sprintf($txt['error_invalid_template'], $upcontext['sub_template']));
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

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

Loading history...
422
		}
423
424
		// Was there an error?
425
		if (!empty($upcontext['forced_error_message']))
426
			echo $upcontext['forced_error_message'];
427
428
		// Show the footer.
429
		if (!isset($_GET['xml']))
430
			template_upgrade_below();
431
		else
432
			template_xml_below();
433
	}
434
435
	// Show the upgrade time for CLI when we are completely done, if in debug mode.
436
	if (!empty($command_line) && $is_debug)
437
	{
438
		$active = time() - $upcontext['started'];
439
		$hours = floor($active / 3600);
440
		$minutes = intval(($active / 60) % 60);
441
		$seconds = intval($active % 60);
442
443
		if ($hours > 0)
444
			echo "\n" . '', sprintf($txt['upgrade_completed_time_hms'], $hours, $minutes, $seconds), '' . "\n";
445
		elseif ($minutes > 0)
446
			echo "\n" . '', sprintf($txt['upgrade_completed_time_ms'], $minutes, $seconds), '' . "\n";
447
		elseif ($seconds > 0)
448
			echo "\n" . '', sprintf($txt['upgrade_completed_time_s'], $seconds), '' . "\n";
449
	}
450
451
	// Bang - gone!
452
	die();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

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

Loading history...
453
}
454
455
// Load the list of language files, and the current language file.
456
function load_lang_file()
457
{
458
	global $txt, $upcontext, $language, $modSettings;
459
460
	static $lang_dir = '', $detected_languages = array(), $loaded_langfile = '';
461
462
	// Do we know where to look for the language files, or shall we just guess for now?
463
	$temp = isset($modSettings['theme_dir']) ? $modSettings['theme_dir'] . '/languages' : dirname(__FILE__) . '/Themes/default/languages';
464
465
	if ($lang_dir != $temp)
466
	{
467
		$lang_dir = $temp;
468
		$detected_languages = array();
469
	}
470
471
	// Override the language file?
472
	if (isset($upcontext['language']))
473
		$_SESSION['upgrader_langfile'] = 'Install.' . $upcontext['language'] . '.php';
474
	elseif (isset($upcontext['lang']))
475
		$_SESSION['upgrader_langfile'] = 'Install.' . $upcontext['lang'] . '.php';
476
	elseif (isset($language))
477
		$_SESSION['upgrader_langfile'] = 'Install.' . $language . '.php';
478
479
	// Avoid pointless repetition
480
	if (isset($_SESSION['upgrader_langfile']) && $loaded_langfile == $lang_dir . '/' . $_SESSION['upgrader_langfile'])
481
		return;
482
483
	// Now try to find the language files
484
	if (empty($detected_languages))
485
	{
486
		// Make sure the languages directory actually exists.
487
		if (file_exists($lang_dir))
488
		{
489
			// Find all the "Install" language files in the directory.
490
			$dir = dir($lang_dir);
491
			while ($entry = $dir->read())
492
			{
493
				// Skip any old '-utf8' language files that might be lying around
494
				if (strpos($entry, '-utf8') !== false)
495
					continue;
496
497
				if (substr($entry, 0, 8) == 'Install.' && substr($entry, -4) == '.php')
498
					$detected_languages[$entry] = ucfirst(substr($entry, 8, strlen($entry) - 12));
499
			}
500
			$dir->close();
501
		}
502
		// Our guess was wrong, but that's fine. We'll try again after $modSettings['theme_dir'] is defined.
503
		elseif (!isset($modSettings['theme_dir']))
504
		{
505
			// Define a few essential strings for now.
506
			$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.';
507
			$txt['error_sourcefile_missing'] = 'Unable to find the Sources/%1$s file. Please make sure it was uploaded properly, and then try again.';
508
509
			$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.';
510
			$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.';
511
512
			return;
513
		}
514
	}
515
516
	// Didn't find any, show an error message!
517
	if (empty($detected_languages))
518
	{
519
		$from = explode('/', $_SERVER['PHP_SELF']);
520
		$to = explode('/', $lang_dir);
521
		$relPath = $to;
522
523
		foreach($from as $depth => $dir)
524
		{
525
			if ($dir === $to[$depth])
526
				array_shift($relPath);
527
			else
528
			{
529
				$remaining = count($from) - $depth;
530
				if ($remaining > 1)
531
				{
532
					$padLength = (count($relPath) + $remaining - 1) * -1;
533
					$relPath = array_pad($relPath, $padLength, '..');
534
					break;
535
				}
536
				else
537
					$relPath[0] = './' . $relPath[0];
538
			}
539
		}
540
		$relPath = implode(DIRECTORY_SEPARATOR, $relPath);
541
542
		// Let's not cache this message, eh?
543
		header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
544
		header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
545
		header('Cache-Control: no-cache');
546
547
		echo '<!DOCTYPE html>
548
			<html>
549
				<head>
550
					<title>SMF Upgrader: Error!</title>
551
						<style>
552
							body {
553
								font-family: sans-serif;
554
								max-width: 700px; }
555
556
								h1 {
557
									font-size: 14pt; }
558
559
								.directory {
560
									margin: 0.3em;
561
									font-family: monospace;
562
									font-weight: bold; }
563
						</style>
564
				</head>
565
				<body>
566
					<h1>A critical error has occurred.</h1>
567
						<p>This upgrader was unable to find the upgrader\'s language file or files.  They should be found under:</p>
568
						<div class="directory">', $relPath, '</div>
569
						<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>
570
						<p>If that doesn\'t help, please make sure this upgrade.php file is in the same place as the Themes folder.</p>
571
						<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>
572
				</body>
573
			</html>';
574
		die;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

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

Loading history...
575
	}
576
577
	// Make sure it exists. If it doesn't, reset it.
578
	if (!isset($_SESSION['upgrader_langfile']) || preg_match('~[^\w.-]~', $_SESSION['upgrader_langfile']) === 1 || !file_exists($lang_dir . '/' . $_SESSION['upgrader_langfile']))
579
	{
580
		// Use the first one...
581
		list ($_SESSION['upgrader_langfile']) = array_keys($detected_languages);
582
583
		// If we have English and some other language, use the other language.
584
		if ($_SESSION['upgrader_langfile'] == 'Install.english.php' && count($detected_languages) > 1)
585
			list (, $_SESSION['upgrader_langfile']) = array_keys($detected_languages);
586
	}
587
588
	// For backup we load English at first, then the second language will overwrite it.
589
	if ($_SESSION['upgrader_langfile'] != 'Install.english.php')
590
		require_once($lang_dir . '/Install.english.php');
591
592
	// And now include the actual language file itself.
593
	require_once($lang_dir . '/' . $_SESSION['upgrader_langfile']);
594
595
	// Remember what we've done
596
	$loaded_langfile = $lang_dir . '/' . $_SESSION['upgrader_langfile'];
597
}
598
599
// Used to direct the user to another location.
600
function redirectLocation($location, $addForm = true)
601
{
602
	global $upgradeurl, $upcontext, $command_line;
603
604
	// Command line users can't be redirected.
605
	if ($command_line)
606
		upgradeExit(true);
607
608
	// Are we providing the core info?
609
	if ($addForm)
610
	{
611
		$upcontext['upgrade_status']['curstep'] = $upcontext['current_step'];
612
		$location = $upgradeurl . '?step=' . $upcontext['current_step'] . '&substep=' . $_GET['substep'] . '&data=' . base64_encode(json_encode($upcontext['upgrade_status'])) . $location;
613
	}
614
615
	while (@ob_end_clean())
616
		header('location: ' . strtr($location, array('&amp;' => '&')));
617
618
	// Exit - saving status as we go.
619
	upgradeExit(true);
620
}
621
622
// Load all essential data and connect to the DB as this is pre SSI.php
623
function loadEssentialData()
624
{
625
	global $db_server, $db_user, $db_passwd, $db_name, $db_connection;
626
	global $db_prefix, $db_character_set, $db_type, $db_port;
627
	global $db_mb4, $modSettings, $sourcedir, $smcFunc, $txt, $utf8;
628
629
	error_reporting(E_ALL);
630
	define('SMF', 1);
631
632
	// Start the session.
633
	if (@ini_get('session.save_handler') == 'user')
634
		@ini_set('session.save_handler', 'files');
635
	@session_start();
636
637
	if (empty($smcFunc))
638
		$smcFunc = array();
639
640
	$smcFunc['random_int'] = function($min = 0, $max = PHP_INT_MAX)
641
	{
642
		global $sourcedir;
643
644
		// Oh, wouldn't it be great if I *was* crazy? Then the world would be okay.
645
		if (!is_callable('random_int'))
646
			require_once($sourcedir . '/random_compat/random.php');
647
648
		return random_int($min, $max);
649
	};
650
651
	// We need this for authentication and some upgrade code
652
	require_once($sourcedir . '/Subs-Auth.php');
653
	require_once($sourcedir . '/Class-Package.php');
654
655
	$smcFunc['strtolower'] = 'smf_strtolower';
656
657
	// Initialize everything...
658
	initialize_inputs();
659
660
	$utf8 = (empty($modSettings['global_character_set']) ? $txt['lang_character_set'] : $modSettings['global_character_set']) === 'UTF-8';
661
662
	// Get the database going!
663
	if (empty($db_type) || $db_type == 'mysqli')
664
	{
665
		$db_type = 'mysql';
666
		// If overriding $db_type, need to set its settings.php entry too
667
		$changes = array();
668
		$changes['db_type'] = 'mysql';
669
		require_once($sourcedir . '/Subs-Admin.php');
670
		updateSettingsFile($changes);
671
	}
672
673
	if (file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php'))
674
	{
675
		require_once($sourcedir . '/Subs-Db-' . $db_type . '.php');
676
677
		// Make the connection...
678
		if (empty($db_connection))
679
		{
680
			$options = array('non_fatal' => true);
681
			// Add in the port if needed
682
			if (!empty($db_port))
683
				$options['port'] = $db_port;
684
685
			if (!empty($db_mb4))
686
				$options['db_mb4'] = $db_mb4;
687
688
			$db_connection = smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, $db_prefix, $options);
689
		}
690
		else
691
			// If we've returned here, ping/reconnect to be safe
692
			$smcFunc['db_ping']($db_connection);
693
694
		// Oh dear god!!
695
		if ($db_connection === null)
696
			die($txt['error_db_connect_settings']);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

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

Loading history...
697
698
		if ($db_type == 'mysql' && isset($db_character_set) && preg_match('~^\w+$~', $db_character_set) === 1)
699
			$smcFunc['db_query']('', '
700
				SET NAMES {string:db_character_set}',
701
				array(
702
					'db_error_skip' => true,
703
					'db_character_set' => $db_character_set,
704
				)
705
			);
706
707
		// Load the modSettings data...
708
		$request = $smcFunc['db_query']('', '
709
			SELECT variable, value
710
			FROM {db_prefix}settings',
711
			array(
712
				'db_error_skip' => true,
713
			)
714
		);
715
		$modSettings = array();
716
		while ($row = $smcFunc['db_fetch_assoc']($request))
717
			$modSettings[$row['variable']] = $row['value'];
718
		$smcFunc['db_free_result']($request);
719
	}
720
	else
721
		return throw_error(sprintf($txt['error_sourcefile_missing'], 'Subs-Db-' . $db_type . '.php'));
722
723
	require_once($sourcedir . '/Subs.php');
724
725
	// If they don't have the file, they're going to get a warning anyway so we won't need to clean request vars.
726
	if (file_exists($sourcedir . '/QueryString.php') && php_version_check())
727
	{
728
		require_once($sourcedir . '/QueryString.php');
729
		cleanRequest();
730
	}
731
732
	if (!isset($_GET['substep']))
733
		$_GET['substep'] = 0;
734
}
735
736
function initialize_inputs()
737
{
738
	global $start_time, $db_type;
739
740
	$start_time = time();
741
742
	umask(0);
743
744
	ob_start();
745
746
	// Better to upgrade cleanly and fall apart than to screw everything up if things take too long.
747
	ignore_user_abort(true);
748
749
	// This is really quite simple; if ?delete is on the URL, delete the upgrader...
750
	if (isset($_GET['delete']))
751
	{
752
		@unlink(__FILE__);
753
754
		// And the extra little files ;).
755
		@unlink(dirname(__FILE__) . '/upgrade_1-0.sql');
756
		@unlink(dirname(__FILE__) . '/upgrade_1-1.sql');
757
		@unlink(dirname(__FILE__) . '/upgrade_2-0_' . $db_type . '.sql');
758
		@unlink(dirname(__FILE__) . '/upgrade_2-1_' . $db_type . '.sql');
759
		@unlink(dirname(__FILE__) . '/upgrade-helper.php');
760
761
		$dh = opendir(dirname(__FILE__));
762
		while ($file = readdir($dh))
0 ignored issues
show
Bug introduced by
It seems like $dh can also be of type false; however, parameter $dir_handle of readdir() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

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

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

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

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

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

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

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

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

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

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

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

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

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

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

}

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

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

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

Loading history...
1465
		}
1466
1467
		if ($command_line)
1468
		{
1469
			echo "\n" . ' Successful.\'' . "\n";
1470
			flush();
1471
		}
1472
		$upcontext['step_progress'] = 100;
1473
1474
		$_GET['substep'] = 0;
1475
		// Make sure we move on!
1476
		return true;
1477
	}
1478
1479
	// Either way next place to post will be database changes!
1480
	$_GET['substep'] = 0;
1481
	return false;
1482
}
1483
1484
// Backup one table...
1485
function backupTable($table)
1486
{
1487
	global $command_line, $db_prefix, $smcFunc;
1488
1489
	if ($command_line)
1490
	{
1491
		echo "\n" . ' +++ Backing up \"' . str_replace($db_prefix, '', $table) . '"...';
1492
		flush();
1493
	}
1494
1495
	$smcFunc['db_backup_table']($table, 'backup_' . $table);
1496
1497
	if ($command_line)
1498
		echo ' done.';
1499
}
1500
1501
// Step 2: Everything.
1502
function DatabaseChanges()
1503
{
1504
	global $db_prefix, $modSettings, $smcFunc, $txt;
1505
	global $upcontext, $support_js, $db_type;
1506
1507
	// Have we just completed this?
1508
	if (!empty($_POST['database_done']))
1509
		return true;
1510
1511
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'database_xml' : 'database_changes';
1512
	$upcontext['page_title'] = $txt['database_changes'];
1513
1514
	// All possible files.
1515
	// Name, < version, insert_on_complete
1516
	// Last entry in array indicates whether to use sql_mode of STRICT or not.
1517
	$files = array(
1518
		array('upgrade_1-0.sql', '1.1', '1.1 RC0', false),
1519
		array('upgrade_1-1.sql', '2.0', '2.0 a', false),
1520
		array('upgrade_2-0_' . $db_type . '.sql', '2.1', '2.1 dev0', false),
1521
		array('upgrade_2-1_' . $db_type . '.sql', '3.0', SMF_VERSION, true),
1522
	);
1523
1524
	// How many files are there in total?
1525
	if (isset($_GET['filecount']))
1526
		$upcontext['file_count'] = (int) $_GET['filecount'];
1527
	else
1528
	{
1529
		$upcontext['file_count'] = 0;
1530
		foreach ($files as $file)
1531
		{
1532
			if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < $file[1])
1533
				$upcontext['file_count']++;
1534
		}
1535
	}
1536
1537
	// Do each file!
1538
	$did_not_do = count($files) - $upcontext['file_count'];
1539
	$upcontext['step_progress'] = 0;
1540
	$upcontext['cur_file_num'] = 0;
1541
	foreach ($files as $file)
1542
	{
1543
		if ($did_not_do)
1544
			$did_not_do--;
1545
		else
1546
		{
1547
			$upcontext['cur_file_num']++;
1548
			$upcontext['cur_file_name'] = $file[0];
1549
			// Do we actually need to do this still?
1550
			if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < $file[1])
1551
			{
1552
				setSqlMode($file[3]);
1553
				$nextFile = parse_sql(dirname(__FILE__) . '/' . $file[0]);
1554
				if ($nextFile)
1555
				{
1556
					// Only update the version of this if complete.
1557
					$smcFunc['db_insert']('replace',
1558
						$db_prefix . 'settings',
1559
						array('variable' => 'string', 'value' => 'string'),
1560
						array('smfVersion', $file[2]),
1561
						array('variable')
1562
					);
1563
1564
					$modSettings['smfVersion'] = $file[2];
1565
				}
1566
1567
				// If this is XML we only do this stuff once.
1568
				if (isset($_GET['xml']))
1569
				{
1570
					// Flag to move on to the next.
1571
					$upcontext['completed_step'] = true;
1572
					// Did we complete the whole file?
1573
					if ($nextFile)
1574
						$upcontext['current_debug_item_num'] = -1;
1575
					return upgradeExit();
0 ignored issues
show
Bug introduced by
Are you sure the usage of upgradeExit() is correct as it seems to always return null.

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

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

}

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

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

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

Loading history...
1576
				}
1577
				elseif ($support_js)
1578
					break;
1579
			}
1580
			// Set the progress bar to be right as if we had - even if we hadn't...
1581
			$upcontext['step_progress'] = ($upcontext['cur_file_num'] / $upcontext['file_count']) * 100;
1582
		}
1583
	}
1584
1585
	$_GET['substep'] = 0;
1586
	// So the template knows we're done.
1587
	if (!$support_js)
1588
	{
1589
		$upcontext['changes_complete'] = true;
1590
1591
		return true;
1592
	}
1593
	return false;
1594
}
1595
1596
// Different versions of the files use different sql_modes
1597
function setSqlMode($strict = true)
1598
{
1599
	global $db_type, $db_connection;
1600
1601
	if ($db_type != 'mysql')
1602
		return;
1603
1604
	if ($strict)
1605
		$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';
1606
	else
1607
		$mode = '';
1608
1609
	mysqli_query($db_connection, 'SET SESSION sql_mode = \'' . $mode . '\'');
1610
1611
	return;
1612
}
1613
1614
// Delete the damn thing!
1615
function DeleteUpgrade()
1616
{
1617
	global $command_line, $language, $upcontext, $sourcedir;
1618
	global $user_info, $maintenance, $smcFunc, $db_type, $txt, $settings;
1619
1620
	// Now it's nice to have some of the basic SMF source files.
1621
	if (!isset($_GET['ssi']) && !$command_line)
1622
		redirectLocation('&ssi=1');
1623
1624
	$upcontext['sub_template'] = 'upgrade_complete';
1625
	$upcontext['page_title'] = $txt['upgrade_complete'];
1626
1627
	$endl = $command_line ? "\n" : '<br>' . "\n";
1628
1629
	$changes = array(
1630
		'language' => (substr($language, -4) == '.lng' ? substr($language, 0, -4) : $language),
1631
		'db_error_send' => 1,
1632
		'upgradeData' => null,
1633
	);
1634
1635
	// Are we in maintenance mode?
1636
	if (isset($upcontext['user']['main']))
1637
	{
1638
		if ($command_line)
1639
			echo ' * ';
1640
		$upcontext['removed_maintenance'] = true;
1641
		$changes['maintenance'] = $upcontext['user']['main'];
1642
	}
1643
	// Otherwise if somehow we are in 2 let's go to 1.
1644
	elseif (!empty($maintenance) && $maintenance == 2)
1645
		$changes['maintenance'] = 1;
1646
1647
	// Wipe this out...
1648
	$upcontext['user'] = array();
1649
1650
	require_once($sourcedir . '/Subs-Admin.php');
1651
	updateSettingsFile($changes);
1652
1653
	// Clean any old cache files away.
1654
	upgrade_clean_cache();
1655
1656
	// Can we delete the file?
1657
	$upcontext['can_delete_script'] = is_writable(dirname(__FILE__)) || is_writable(__FILE__);
1658
1659
	// Now is the perfect time to fetch the SM files.
1660
	if ($command_line)
1661
		cli_scheduled_fetchSMfiles();
1662
	else
1663
	{
1664
		require_once($sourcedir . '/ScheduledTasks.php');
1665
		scheduled_fetchSMfiles(); // Now go get those files!
1666
		// This is needed in case someone invokes the upgrader using https when upgrading an http forum
1667
		if (httpsOn())
1668
			$settings['default_theme_url'] = strtr($settings['default_theme_url'], array('http://' => 'https://'));
1669
	}
1670
1671
	// Log what we've done.
1672
	if (empty($user_info['id']))
1673
		$user_info['id'] = !empty($upcontext['user']['id']) ? $upcontext['user']['id'] : 0;
1674
1675
	// Log the action manually, so CLI still works.
1676
	$smcFunc['db_insert']('',
1677
		'{db_prefix}log_actions',
1678
		array(
1679
			'log_time' => 'int', 'id_log' => 'int', 'id_member' => 'int', 'ip' => 'inet', 'action' => 'string',
1680
			'id_board' => 'int', 'id_topic' => 'int', 'id_msg' => 'int', 'extra' => 'string-65534',
1681
		),
1682
		array(
1683
			time(), 3, $user_info['id'], $command_line ? '127.0.0.1' : $user_info['ip'], 'upgrade',
1684
			0, 0, 0, json_encode(array('version' => SMF_FULL_VERSION, 'member' => $user_info['id'])),
1685
		),
1686
		array('id_action')
1687
	);
1688
	$user_info['id'] = 0;
1689
1690
	if ($command_line)
1691
	{
1692
		echo $endl;
1693
		echo 'Upgrade Complete!', $endl;
1694
		echo 'Please delete this file as soon as possible for security reasons.', $endl;
1695
		exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

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

Loading history...
1696
	}
1697
1698
	// Make sure it says we're done.
1699
	$upcontext['overall_percent'] = 100;
1700
	if (isset($upcontext['step_progress']))
1701
		unset($upcontext['step_progress']);
1702
1703
	$_GET['substep'] = 0;
1704
	return false;
1705
}
1706
1707
// Just like the built in one, but setup for CLI to not use themes.
1708
function cli_scheduled_fetchSMfiles()
1709
{
1710
	global $sourcedir, $language, $modSettings, $smcFunc;
1711
1712
	if (empty($modSettings['time_format']))
1713
		$modSettings['time_format'] = '%B %d, %Y, %I:%M:%S %p';
1714
1715
	// What files do we want to get
1716
	$request = $smcFunc['db_query']('', '
1717
		SELECT id_file, filename, path, parameters
1718
		FROM {db_prefix}admin_info_files',
1719
		array(
1720
		)
1721
	);
1722
1723
	$js_files = array();
1724
	while ($row = $smcFunc['db_fetch_assoc']($request))
1725
	{
1726
		$js_files[$row['id_file']] = array(
1727
			'filename' => $row['filename'],
1728
			'path' => $row['path'],
1729
			'parameters' => sprintf($row['parameters'], $language, urlencode($modSettings['time_format']), urlencode(SMF_FULL_VERSION)),
1730
		);
1731
	}
1732
	$smcFunc['db_free_result']($request);
1733
1734
	// We're gonna need fetch_web_data() to pull this off.
1735
	require_once($sourcedir . '/Subs.php');
1736
1737
	foreach ($js_files as $ID_FILE => $file)
1738
	{
1739
		// Create the url
1740
		$server = empty($file['path']) || substr($file['path'], 0, 7) != 'http://' ? 'https://www.simplemachines.org' : '';
1741
		$url = $server . (!empty($file['path']) ? $file['path'] : $file['path']) . $file['filename'] . (!empty($file['parameters']) ? '?' . $file['parameters'] : '');
1742
1743
		// Get the file
1744
		$file_data = fetch_web_data($url);
1745
1746
		// If we got an error - give up - the site might be down.
1747
		if ($file_data === false)
1748
			return throw_error(sprintf('Could not retrieve the file %1$s.', $url));
1749
1750
		// Save the file to the database.
1751
		$smcFunc['db_query']('substring', '
1752
			UPDATE {db_prefix}admin_info_files
1753
			SET data = SUBSTRING({string:file_data}, 1, 65534)
1754
			WHERE id_file = {int:id_file}',
1755
			array(
1756
				'id_file' => $ID_FILE,
1757
				'file_data' => $file_data,
1758
			)
1759
		);
1760
	}
1761
	return true;
1762
}
1763
1764
function convertSettingsToTheme()
1765
{
1766
	global $db_prefix, $modSettings, $smcFunc;
1767
1768
	$values = array(
1769
		'show_latest_member' => @$GLOBALS['showlatestmember'],
1770
		'show_bbc' => isset($GLOBALS['showyabbcbutt']) ? $GLOBALS['showyabbcbutt'] : @$GLOBALS['showbbcbutt'],
1771
		'show_modify' => @$GLOBALS['showmodify'],
1772
		'show_user_images' => @$GLOBALS['showuserpic'],
1773
		'show_blurb' => @$GLOBALS['showusertext'],
1774
		'show_gender' => @$GLOBALS['showgenderimage'],
1775
		'show_newsfader' => @$GLOBALS['shownewsfader'],
1776
		'display_recent_bar' => @$GLOBALS['Show_RecentBar'],
1777
		'show_member_bar' => @$GLOBALS['Show_MemberBar'],
1778
		'linktree_link' => @$GLOBALS['curposlinks'],
1779
		'show_profile_buttons' => @$GLOBALS['profilebutton'],
1780
		'show_mark_read' => @$GLOBALS['showmarkread'],
1781
		'newsfader_time' => @$GLOBALS['fadertime'],
1782
		'use_image_buttons' => empty($GLOBALS['MenuType']) ? 1 : 0,
1783
		'enable_news' => @$GLOBALS['enable_news'],
1784
		'return_to_post' => @$modSettings['returnToPost'],
1785
	);
1786
1787
	$themeData = array();
1788
	foreach ($values as $variable => $value)
1789
	{
1790
		if (!isset($value) || $value === null)
1791
			$value = 0;
1792
1793
		$themeData[] = array(0, 1, $variable, $value);
1794
	}
1795
	if (!empty($themeData))
1796
	{
1797
		$smcFunc['db_insert']('ignore',
1798
			$db_prefix . 'themes',
1799
			array('id_member' => 'int', 'id_theme' => 'int', 'variable' => 'string', 'value' => 'string'),
1800
			$themeData,
1801
			array('id_member', 'id_theme', 'variable')
1802
		);
1803
	}
1804
}
1805
1806
// This function only works with MySQL but that's fine as it is only used for v1.0.
1807
function convertSettingstoOptions()
1808
{
1809
	global $modSettings, $smcFunc;
1810
1811
	// Format: new_setting -> old_setting_name.
1812
	$values = array(
1813
		'calendar_start_day' => 'cal_startmonday',
1814
		'view_newest_first' => 'viewNewestFirst',
1815
		'view_newest_pm_first' => 'viewNewestFirst',
1816
	);
1817
1818
	foreach ($values as $variable => $value)
1819
	{
1820
		if (empty($modSettings[$value[0]]))
1821
			continue;
1822
1823
		$smcFunc['db_query']('', '
1824
			INSERT IGNORE INTO {db_prefix}themes
1825
				(id_member, id_theme, variable, value)
1826
			SELECT id_member, 1, {string:variable}, {string:value}
1827
			FROM {db_prefix}members',
1828
			array(
1829
				'variable' => $variable,
1830
				'value' => $modSettings[$value[0]],
1831
				'db_error_skip' => true,
1832
			)
1833
		);
1834
1835
		$smcFunc['db_query']('', '
1836
			INSERT IGNORE INTO {db_prefix}themes
1837
				(id_member, id_theme, variable, value)
1838
			VALUES (-1, 1, {string:variable}, {string:value})',
1839
			array(
1840
				'variable' => $variable,
1841
				'value' => $modSettings[$value[0]],
1842
				'db_error_skip' => true,
1843
			)
1844
		);
1845
	}
1846
}
1847
1848
function php_version_check()
1849
{
1850
	return version_compare(PHP_VERSION, $GLOBALS['required_php_version'], '>=');
1851
}
1852
1853
function db_version_check()
1854
{
1855
	global $db_type, $databases;
1856
1857
	$curver = eval($databases[$db_type]['version_check']);
0 ignored issues
show
introduced by
The use of eval() is discouraged.
Loading history...
1858
	$curver = preg_replace('~\-.+?$~', '', $curver);
1859
1860
	return version_compare($databases[$db_type]['version'], $curver, '<=');
1861
}
1862
1863
function fixRelativePath($path)
1864
{
1865
	global $install_path;
1866
1867
	// Fix the . at the start, clear any duplicate slashes, and fix any trailing slash...
1868
	return addslashes(preg_replace(array('~^\.([/\\\]|$)~', '~[/]+~', '~[\\\]+~', '~[/\\\]$~'), array($install_path . '$1', '/', '\\', ''), $path));
1869
}
1870
1871
function parse_sql($filename)
1872
{
1873
	global $db_prefix, $db_collation, $boarddir, $boardurl, $command_line, $file_steps, $step_progress, $custom_warning;
1874
	global $upcontext, $support_js, $is_debug, $db_type, $db_character_set;
1875
1876
/*
1877
	Failure allowed on:
1878
		- INSERT INTO but not INSERT IGNORE INTO.
1879
		- UPDATE IGNORE but not UPDATE.
1880
		- ALTER TABLE and ALTER IGNORE TABLE.
1881
		- DROP TABLE.
1882
	Yes, I realize that this is a bit confusing... maybe it should be done differently?
1883
1884
	If a comment...
1885
		- begins with --- it is to be output, with a break only in debug mode. (and say successful\n\n if there was one before.)
1886
		- begins with ---# it is a debugging statement, no break - only shown at all in debug.
1887
		- is only ---#, it is "done." and then a break - only shown in debug.
1888
		- begins with ---{ it is a code block terminating at ---}.
1889
1890
	Every block of between "--- ..."s is a step.  Every "---#" section represents a substep.
1891
1892
	Replaces the following variables:
1893
		- {$boarddir}
1894
		- {$boardurl}
1895
		- {$db_prefix}
1896
		- {$db_collation}
1897
*/
1898
1899
	// May want to use extended functionality.
1900
	db_extend();
1901
	db_extend('packages');
1902
1903
	// Our custom error handler - does nothing but does stop public errors from XML!
1904
	set_error_handler(
1905
		function($errno, $errstr, $errfile, $errline) use ($support_js)
1906
		{
1907
			if ($support_js)
1908
				return true;
1909
			else
1910
				echo 'Error: ' . $errstr . ' File: ' . $errfile . ' Line: ' . $errline;
1911
		}
1912
	);
1913
1914
	// If we're on MySQL, set {db_collation}; this approach is used throughout upgrade_2-0_mysql.php to set new tables to utf8
1915
	// Note it is expected to be in the format: ENGINE=MyISAM{$db_collation};
1916
	if ($db_type == 'mysql')
1917
		$db_collation = ' DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci';
1918
	else
1919
		$db_collation = '';
1920
1921
	$endl = $command_line ? "\n" : '<br>' . "\n";
1922
1923
	$lines = file($filename);
1924
1925
	$current_type = 'sql';
1926
	$current_data = '';
1927
	$substep = 0;
1928
	$last_step = '';
1929
1930
	// Make sure all newly created tables will have the proper characters set; this approach is used throughout upgrade_2-1_mysql.php
1931
	if (isset($db_character_set) && $db_character_set === 'utf8')
1932
		$lines = str_replace(') ENGINE=MyISAM;', ') ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;', $lines);
1933
1934
	// Count the total number of steps within this file - for progress.
1935
	$file_steps = substr_count(implode('', $lines), '---#');
0 ignored issues
show
Bug introduced by
It seems like $lines can also be of type false; however, parameter $pieces of implode() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

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

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

2189
			mysqli_select_db(/** @scrutinizer ignore-type */ $db_connection, $db_name);
Loading history...
2190
			if ($db_connection)
2191
			{
2192
				$result = mysqli_query($db_connection, $string);
2193
				if ($result !== false)
2194
					return $result;
2195
			}
2196
		}
2197
		// Duplicate column name... should be okay ;).
2198
		elseif (in_array($mysqli_errno, array(1060, 1061, 1068, 1091)))
2199
			return false;
2200
		// Duplicate insert... make sure it's the proper type of query ;).
2201
		elseif (in_array($mysqli_errno, array(1054, 1062, 1146)) && $error_query)
2202
			return false;
2203
		// Creating an index on a non-existent column.
2204
		elseif ($mysqli_errno == 1072)
2205
			return false;
2206
		elseif ($mysqli_errno == 1050 && substr(trim($string), 0, 12) == 'RENAME TABLE')
2207
			return false;
2208
		// Testing for legacy tables or columns? Needed for 1.0 & 1.1 scripts.
2209
		elseif (in_array($mysqli_errno, array(1054, 1146)) && in_array(substr(trim($string), 0, 7), array('SELECT ', 'SHOW CO')))
2210
			return false;
2211
	}
2212
	// If a table already exists don't go potty.
2213
	else
2214
	{
2215
		if (in_array(substr(trim($string), 0, 8), array('CREATE T', 'CREATE S', 'DROP TABL', 'ALTER TA', 'CREATE I', 'CREATE U')))
2216
		{
2217
			if (strpos($db_error_message, 'exist') !== false)
2218
				return true;
2219
		}
2220
		elseif (strpos(trim($string), 'INSERT ') !== false)
2221
		{
2222
			if (strpos($db_error_message, 'duplicate') !== false || $ignore_insert_error)
2223
				return true;
2224
		}
2225
	}
2226
2227
	// Get the query string so we pass everything.
2228
	$query_string = '';
2229
	foreach ($_GET as $k => $v)
2230
		$query_string .= ';' . $k . '=' . $v;
2231
	if (strlen($query_string) != 0)
2232
		$query_string = '?' . substr($query_string, 1);
2233
2234
	if ($command_line)
2235
	{
2236
		echo 'Unsuccessful!  Database error message:', "\n", $db_error_message, "\n";
2237
		die;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

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

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

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

Loading history...
2393
	$table_row = $smcFunc['db_fetch_assoc']($request);
2394
	$smcFunc['db_free_result']($request);
2395
2396
	// If something of the current column definition is different, fix it.
2397
	$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']);
2398
2399
	// Columns that previously allowed null, need to be converted first.
2400
	$null_fix = strtolower($table_row['Null']) === 'yes' && !$change['null_allowed'];
2401
2402
	// Get the character set that goes with the collation of the column.
2403
	if ($column_fix && !empty($table_row['Collation']))
2404
	{
2405
		$request = $smcFunc['db_query']('', '
2406
			SHOW COLLATION
2407
			LIKE {string:collation}',
2408
			array(
2409
				'collation' => $table_row['Collation'],
2410
				'db_error_skip' => true,
2411
			)
2412
		);
2413
		// No results? Just forget it all together.
2414
		if ($smcFunc['db_num_rows']($request) === 0)
2415
			unset($table_row['Collation']);
2416
		else
2417
			$collation_info = $smcFunc['db_fetch_assoc']($request);
2418
		$smcFunc['db_free_result']($request);
2419
	}
2420
2421
	if ($column_fix)
2422
	{
2423
		// Make sure there are no NULL's left.
2424
		if ($null_fix)
2425
			$smcFunc['db_query']('', '
2426
				UPDATE {db_prefix}' . $change['table'] . '
2427
				SET ' . $change['column'] . ' = {string:default}
2428
				WHERE ' . $change['column'] . ' IS NULL',
2429
				array(
2430
					'default' => isset($change['default']) ? $change['default'] : '',
2431
					'db_error_skip' => true,
2432
				)
2433
			);
2434
2435
		// Do the actual alteration.
2436
		$smcFunc['db_query']('', '
2437
			ALTER TABLE {db_prefix}' . $change['table'] . '
2438
			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}' : ''),
2439
			array(
2440
				'default' => isset($change['default']) ? $change['default'] : '',
2441
				'db_error_skip' => true,
2442
			)
2443
		);
2444
	}
2445
	nextSubstep($substep);
2446
}
2447
2448
// Check if we need to alter this query.
2449
function checkChange(&$change)
2450
{
2451
	global $smcFunc, $db_type, $databases;
2452
	static $database_version, $where_field_support;
2453
2454
	// Attempt to find a database_version.
2455
	if (empty($database_version))
2456
	{
2457
		$database_version = $databases[$db_type]['version_check'];
2458
		$where_field_support = $db_type == 'mysql' && version_compare('5.0', $database_version, '<=');
2459
	}
2460
2461
	// Not a column we need to check on?
2462
	if (!in_array($change['name'], array('memberGroups', 'passwordSalt')))
2463
		return;
2464
2465
	// Break it up you (six|seven).
2466
	$temp = explode(' ', str_replace('NOT NULL', 'NOT_NULL', $change['text']));
2467
2468
	// Can we support a shortcut method?
2469
	if ($where_field_support)
2470
	{
2471
		// Get the details about this change.
2472
		$request = $smcFunc['db_query']('', '
2473
			SHOW FIELDS
2474
			FROM {db_prefix}{raw:table}
2475
			WHERE Field = {string:old_name} OR Field = {string:new_name}',
2476
			array(
2477
				'table' => $change['table'],
2478
				'old_name' => $temp[1],
2479
				'new_name' => $temp[2],
2480
			)
2481
		);
2482
		// !!! This doesn't technically work because we don't pass request into it, but it hasn't broke anything yet.
2483
		if ($smcFunc['db_num_rows'] != 1)
2484
			return;
2485
2486
		list (, $current_type) = $smcFunc['db_fetch_assoc']($request);
2487
		$smcFunc['db_free_result']($request);
2488
	}
2489
	else
2490
	{
2491
		// Do this the old fashion, sure method way.
2492
		$request = $smcFunc['db_query']('', '
2493
			SHOW FIELDS
2494
			FROM {db_prefix}{raw:table}',
2495
			array(
2496
				'table' => $change['table'],
2497
			)
2498
		);
2499
		// Mayday!
2500
		// !!! This doesn't technically work because we don't pass request into it, but it hasn't broke anything yet.
2501
		if ($smcFunc['db_num_rows'] == 0)
2502
			return;
2503
2504
		// Oh where, oh where has my little field gone. Oh where can it be...
2505
		while ($row = $smcFunc['db_query']($request))
2506
			if ($row['Field'] == $temp[1] || $row['Field'] == $temp[2])
2507
			{
2508
				$current_type = $row['Type'];
2509
				break;
2510
			}
2511
	}
2512
2513
	// If this doesn't match, the column may of been altered for a reason.
2514
	if (trim($current_type) != trim($temp[3]))
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $current_type does not seem to be defined for all execution paths leading up to this point.
Loading history...
2515
		$temp[3] = $current_type;
2516
2517
	// Piece this back together.
2518
	$change['text'] = str_replace('NOT_NULL', 'NOT NULL', implode(' ', $temp));
2519
}
2520
2521
// The next substep.
2522
function nextSubstep($substep)
2523
{
2524
	global $start_time, $timeLimitThreshold, $command_line, $custom_warning;
2525
	global $step_progress, $is_debug, $upcontext;
2526
2527
	if ($_GET['substep'] < $substep)
2528
		$_GET['substep'] = $substep;
2529
2530
	if ($command_line)
2531
	{
2532
		if (time() - $start_time > 1 && empty($is_debug))
2533
		{
2534
			echo '.';
2535
			$start_time = time();
2536
		}
2537
		return;
2538
	}
2539
2540
	@set_time_limit(300);
2541
	if (function_exists('apache_reset_timeout'))
2542
		@apache_reset_timeout();
2543
2544
	if (time() - $start_time <= $timeLimitThreshold)
2545
		return;
2546
2547
	// Do we have some custom step progress stuff?
2548
	if (!empty($step_progress))
2549
	{
2550
		$upcontext['substep_progress'] = 0;
2551
		$upcontext['substep_progress_name'] = $step_progress['name'];
2552
		if ($step_progress['current'] > $step_progress['total'])
2553
			$upcontext['substep_progress'] = 99.9;
2554
		else
2555
			$upcontext['substep_progress'] = ($step_progress['current'] / $step_progress['total']) * 100;
2556
2557
		// Make it nicely rounded.
2558
		$upcontext['substep_progress'] = round($upcontext['substep_progress'], 1);
2559
	}
2560
2561
	// If this is XML we just exit right away!
2562
	if (isset($_GET['xml']))
2563
		return upgradeExit();
0 ignored issues
show
Bug introduced by
Are you sure the usage of upgradeExit() is correct as it seems to always return null.

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

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

}

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

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

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

Loading history...
2564
2565
	// We're going to pause after this!
2566
	$upcontext['pause'] = true;
2567
2568
	$upcontext['query_string'] = '';
2569
	foreach ($_GET as $k => $v)
2570
	{
2571
		if ($k != 'data' && $k != 'substep' && $k != 'step')
2572
			$upcontext['query_string'] .= ';' . $k . '=' . $v;
2573
	}
2574
2575
	// Custom warning?
2576
	if (!empty($custom_warning))
2577
		$upcontext['custom_warning'] = $custom_warning;
2578
2579
	upgradeExit();
2580
}
2581
2582
function cmdStep0()
2583
{
2584
	global $boarddir, $sourcedir, $modSettings, $start_time, $cachedir, $databases, $db_type, $smcFunc, $upcontext;
2585
	global $is_debug;
2586
	$start_time = time();
2587
2588
	ob_end_clean();
2589
	ob_implicit_flush(1);
2590
	@set_time_limit(600);
2591
2592
	if (!isset($_SERVER['argv']))
2593
		$_SERVER['argv'] = array();
2594
	$_GET['maint'] = 1;
2595
2596
	foreach ($_SERVER['argv'] as $i => $arg)
2597
	{
2598
		if (preg_match('~^--language=(.+)$~', $arg, $match) != 0)
2599
			$upcontext['lang'] = $match[1];
2600
		elseif (preg_match('~^--path=(.+)$~', $arg) != 0)
2601
			continue;
2602
		elseif ($arg == '--no-maintenance')
2603
			$_GET['maint'] = 0;
2604
		elseif ($arg == '--debug')
2605
			$is_debug = true;
2606
		elseif ($arg == '--backup')
2607
			$_POST['backup'] = 1;
2608
		elseif ($arg == '--template' && (file_exists($boarddir . '/template.php') || file_exists($boarddir . '/template.html') && !file_exists($modSettings['theme_dir'] . '/converted')))
2609
			$_GET['conv'] = 1;
2610
		elseif ($i != 0)
2611
		{
2612
			echo 'SMF Command-line Upgrader
2613
Usage: /path/to/php -f ' . basename(__FILE__) . ' -- [OPTION]...
2614
2615
	--language=LANG         Reset the forum\'s language to LANG.
2616
	--no-maintenance        Don\'t put the forum into maintenance mode.
2617
	--debug                 Output debugging information.
2618
	--backup                Create backups of tables with "backup_" prefix.';
2619
			echo "\n";
2620
			exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

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

Loading history...
2621
		}
2622
	}
2623
2624
	if (!php_version_check())
2625
		print_error('Error: PHP ' . PHP_VERSION . ' does not match version requirements.', true);
2626
	if (!db_version_check())
2627
		print_error('Error: ' . $databases[$db_type]['name'] . ' ' . $databases[$db_type]['version'] . ' does not match minimum requirements.', true);
2628
2629
	// Do some checks to make sure they have proper privileges
2630
	db_extend('packages');
2631
2632
	// CREATE
2633
	$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');
2634
2635
	// ALTER
2636
	$alter = $smcFunc['db_add_column']('{db_prefix}priv_check', array('name' => 'txt', 'type' => 'varchar', 'size' => 4, 'null' => false, 'default' => ''));
2637
2638
	// DROP
2639
	$drop = $smcFunc['db_drop_table']('{db_prefix}priv_check');
2640
2641
	// Sorry... we need CREATE, ALTER and DROP
2642
	if (!$create || !$alter || !$drop)
2643
		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);
2644
2645
	$check = @file_exists($modSettings['theme_dir'] . '/index.template.php')
2646
		&& @file_exists($sourcedir . '/QueryString.php')
2647
		&& @file_exists($sourcedir . '/ManageBoards.php');
2648
	if (!$check && !isset($modSettings['smfVersion']))
2649
		print_error('Error: Some files are missing or out-of-date.', true);
2650
2651
	// Do a quick version spot check.
2652
	$temp = substr(@implode('', @file($boarddir . '/index.php')), 0, 4096);
0 ignored issues
show
Bug introduced by
It seems like @file($boarddir . '/index.php') can also be of type false; however, parameter $pieces of implode() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

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

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

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

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

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

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

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

}

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

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

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

Loading history...
3183
		}
3184
3185
		$prev_charset = empty($translation_tables[$upcontext['charset_detected']]) ? $charsets[$upcontext['charset_detected']] : $translation_tables[$upcontext['charset_detected']];
3186
3187
		$smcFunc['db_insert']('replace',
3188
			'{db_prefix}settings',
3189
			array('variable' => 'string', 'value' => 'string'),
3190
			array(array('global_character_set', 'UTF-8'), array('previousCharacterSet', $prev_charset)),
3191
			array('variable')
3192
		);
3193
3194
		// Store it in Settings.php too because it's needed before db connection.
3195
		// Hopefully this works...
3196
		require_once($sourcedir . '/Subs-Admin.php');
3197
		updateSettingsFile(array('db_character_set' => 'utf8'));
3198
3199
		// The conversion might have messed up some serialized strings. Fix them!
3200
		$request = $smcFunc['db_query']('', '
3201
			SELECT id_action, extra
3202
			FROM {db_prefix}log_actions
3203
			WHERE action IN ({string:remove}, {string:delete})',
3204
			array(
3205
				'remove' => 'remove',
3206
				'delete' => 'delete',
3207
			)
3208
		);
3209
		while ($row = $smcFunc['db_fetch_assoc']($request))
3210
		{
3211
			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)
3212
				$smcFunc['db_query']('', '
3213
					UPDATE {db_prefix}log_actions
3214
					SET extra = {string:extra}
3215
					WHERE id_action = {int:current_action}',
3216
					array(
3217
						'current_action' => $row['id_action'],
3218
						'extra' => $matches[1] . strlen($matches[3]) . ':"' . $matches[3] . '"' . $matches[4],
3219
					)
3220
				);
3221
		}
3222
		$smcFunc['db_free_result']($request);
3223
3224
		if ($upcontext['dropping_index'] && $command_line)
3225
		{
3226
			echo "\n" . '', $txt['upgrade_fulltext_error'], '';
3227
			flush();
3228
		}
3229
	}
3230
	$_GET['substep'] = 0;
3231
	return false;
3232
}
3233
3234
/**
3235
 * Attempts to repair corrupted serialized data strings
3236
 *
3237
 * @param string $string Serialized data that has been corrupted
3238
 * @return string|bool A working version of the serialized data, or the original if the repair failed
3239
 */
3240
function fix_serialized_data($string)
3241
{
3242
	// If its not broken, don't fix it.
3243
	if (!is_string($string) || !preg_match('/^[bidsa]:/', $string) || @safe_unserialize($string) !== false)
0 ignored issues
show
introduced by
The condition is_string($string) is always true.
Loading history...
3244
		return $string;
3245
3246
	// This bit fixes incorrect string lengths, which can happen if the character encoding was changed (e.g. conversion to UTF-8)
3247
	$new_string = preg_replace_callback('~\bs:(\d+):"(.*?)";(?=$|[bidsa]:|[{}]|N;)~s', function ($matches) {return 's:' . strlen($matches[2]) . ':"' . $matches[2] . '";';}, $string);
3248
3249
	// @todo Add more possible fixes here. For example, fix incorrect array lengths, try to handle truncated strings gracefully, etc.
3250
3251
	// Did it work?
3252
	if (@safe_unserialize($new_string) !== false)
0 ignored issues
show
introduced by
The condition @safe_unserialize($new_string) !== false is always false.
Loading history...
3253
		return $new_string;
3254
	else
3255
		return $string;
3256
}
3257
3258
function serialize_to_json()
3259
{
3260
	global $command_line, $smcFunc, $modSettings, $sourcedir, $upcontext, $support_js, $txt;
3261
3262
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'serialize_json_xml' : 'serialize_json';
3263
	// First thing's first - did we already do this?
3264
	if (!empty($modSettings['json_done']))
3265
	{
3266
		if ($command_line)
3267
			return ConvertUtf8();
3268
		else
3269
			return true;
3270
	}
3271
3272
	// Done it already - js wise?
3273
	if (!empty($_POST['json_done']))
3274
		return true;
3275
3276
	// List of tables affected by this function
3277
	// name => array('key', col1[,col2|true[,col3]])
3278
	// If 3rd item in array is true, it indicates that col1 could be empty...
3279
	$tables = array(
3280
		'background_tasks' => array('id_task', 'task_data'),
3281
		'log_actions' => array('id_action', 'extra'),
3282
		'log_online' => array('session', 'url'),
3283
		'log_packages' => array('id_install', 'db_changes', 'failed_steps', 'credits'),
3284
		'log_spider_hits' => array('id_hit', 'url'),
3285
		'log_subscribed' => array('id_sublog', 'pending_details'),
3286
		'pm_rules' => array('id_rule', 'criteria', 'actions'),
3287
		'qanda' => array('id_question', 'answers'),
3288
		'subscriptions' => array('id_subscribe', 'cost'),
3289
		'user_alerts' => array('id_alert', 'extra', true),
3290
		'user_drafts' => array('id_draft', 'to_list', true),
3291
		// These last two are a bit different - we'll handle those separately
3292
		'settings' => array(),
3293
		'themes' => array()
3294
	);
3295
3296
	// Set up some context stuff...
3297
	// Because we're not using numeric indices, we need this to figure out the current table name...
3298
	$keys = array_keys($tables);
3299
3300
	$upcontext['page_title'] = $txt['converting_json'];
3301
	$upcontext['table_count'] = count($keys);
3302
	$upcontext['cur_table_num'] = $_GET['substep'];
3303
	$upcontext['cur_table_name'] = isset($keys[$_GET['substep']]) ? $keys[$_GET['substep']] : $keys[0];
3304
	$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
3305
3306
	foreach ($keys as $id => $table)
3307
		if ($id < $_GET['substep'])
3308
			$upcontext['previous_tables'][] = $table;
3309
3310
	if ($command_line)
3311
		echo 'Converting data from serialize() to json_encode().';
3312
3313
	if (!$support_js || isset($_GET['xml']))
3314
	{
3315
		// Fix the data in each table
3316
		for ($substep = $_GET['substep']; $substep < $upcontext['table_count']; $substep++)
3317
		{
3318
			$upcontext['cur_table_name'] = isset($keys[$substep + 1]) ? $keys[$substep + 1] : $keys[$substep];
3319
			$upcontext['cur_table_num'] = $substep + 1;
3320
3321
			$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
3322
3323
			// Do we need to pause?
3324
			nextSubstep($substep);
3325
3326
			// Initialize a few things...
3327
			$where = '';
3328
			$vars = array();
3329
			$table = $keys[$substep];
3330
			$info = $tables[$table];
3331
3332
			// Now the fun - build our queries and all that fun stuff
3333
			if ($table == 'settings')
3334
			{
3335
				// Now a few settings...
3336
				$serialized_settings = array(
3337
					'attachment_basedirectories',
3338
					'attachmentUploadDir',
3339
					'cal_today_birthday',
3340
					'cal_today_event',
3341
					'cal_today_holiday',
3342
					'displayFields',
3343
					'last_attachments_directory',
3344
					'memberlist_cache',
3345
					'search_custom_index_config',
3346
					'spider_name_cache'
3347
				);
3348
3349
				// Loop through and fix these...
3350
				$new_settings = array();
3351
				if ($command_line)
3352
					echo "\n" . 'Fixing some settings...';
3353
3354
				foreach ($serialized_settings as $var)
3355
				{
3356
					if (isset($modSettings[$var]))
3357
					{
3358
						// Attempt to unserialize the setting
3359
						$temp = @safe_unserialize($modSettings[$var]);
3360
						// Maybe conversion to UTF-8 corrupted it
3361
						if ($temp === false)
3362
							$temp = @safe_unserialize(fix_serialized_data($modSettings[$var]));
3363
3364
						if (!$temp && $command_line)
3365
							echo "\n - Failed to unserialize the '" . $var . "' setting. Skipping.";
3366
						elseif ($temp !== false)
3367
							$new_settings[$var] = json_encode($temp);
3368
					}
3369
				}
3370
3371
				// Update everything at once
3372
				if (!function_exists('cache_put_data'))
3373
					require_once($sourcedir . '/Load.php');
3374
				updateSettings($new_settings, true);
3375
3376
				if ($command_line)
3377
					echo ' done.';
3378
			}
3379
			elseif ($table == 'themes')
3380
			{
3381
				// Finally, fix the admin prefs. Unfortunately this is stored per theme, but hopefully they only have one theme installed at this point...
3382
				$query = $smcFunc['db_query']('', '
3383
					SELECT id_member, id_theme, value FROM {db_prefix}themes
3384
					WHERE variable = {string:admin_prefs}',
3385
					array(
3386
						'admin_prefs' => 'admin_preferences'
3387
					)
3388
				);
3389
3390
				if ($smcFunc['db_num_rows']($query) != 0)
3391
				{
3392
					while ($row = $smcFunc['db_fetch_assoc']($query))
3393
					{
3394
						$temp = @safe_unserialize($row['value']);
3395
						if ($temp === false)
3396
							$temp = @safe_unserialize(fix_serialized_data($row['value']));
3397
3398
						if ($command_line)
3399
						{
3400
							if ($temp === false)
3401
								echo "\n" . 'Unserialize of admin_preferences for user ' . $row['id_member'] . ' failed. Skipping.';
3402
							else
3403
								echo "\n" . 'Fixing admin preferences...';
3404
						}
3405
3406
						if ($temp !== false)
3407
						{
3408
							$row['value'] = json_encode($temp);
3409
3410
							// Even though we have all values from the table, UPDATE is still faster than REPLACE
3411
							$smcFunc['db_query']('', '
3412
								UPDATE {db_prefix}themes
3413
								SET value = {string:prefs}
3414
								WHERE id_theme = {int:theme}
3415
									AND id_member = {int:member}
3416
									AND variable = {string:admin_prefs}',
3417
								array(
3418
									'prefs' => $row['value'],
3419
									'theme' => $row['id_theme'],
3420
									'member' => $row['id_member'],
3421
									'admin_prefs' => 'admin_preferences'
3422
								)
3423
							);
3424
3425
							if ($command_line)
3426
								echo ' done.';
3427
						}
3428
					}
3429
3430
					$smcFunc['db_free_result']($query);
3431
				}
3432
			}
3433
			else
3434
			{
3435
				// First item is always the key...
3436
				$key = $info[0];
3437
				unset($info[0]);
3438
3439
				// Now we know what columns we have and such...
3440
				if (count($info) == 2 && $info[2] === true)
3441
				{
3442
					$col_select = $info[1];
3443
					$where = ' WHERE ' . $info[1] . ' != {empty}';
3444
				}
3445
				else
3446
				{
3447
					$col_select = implode(', ', $info);
3448
				}
3449
3450
				$query = $smcFunc['db_query']('', '
3451
					SELECT ' . $key . ', ' . $col_select . '
3452
					FROM {db_prefix}' . $table . $where,
3453
					array()
3454
				);
3455
3456
				if ($smcFunc['db_num_rows']($query) != 0)
3457
				{
3458
					if ($command_line)
3459
					{
3460
						echo "\n" . ' +++ Fixing the "' . $table . '" table...';
3461
						flush();
3462
					}
3463
3464
					while ($row = $smcFunc['db_fetch_assoc']($query))
3465
					{
3466
						$update = '';
3467
3468
						// We already know what our key is...
3469
						foreach ($info as $col)
3470
						{
3471
							if ($col !== true && $row[$col] != '')
3472
							{
3473
								$temp = @safe_unserialize($row[$col]);
3474
3475
								// Maybe we can fix the data?
3476
								if ($temp === false)
3477
									$temp = @safe_unserialize(fix_serialized_data($row[$col]));
3478
3479
								// Maybe the data is already JSON?
3480
								if ($temp === false)
3481
									$temp = smf_json_decode($row[$col], true, false);
3482
3483
								// Oh well...
3484
								if ($temp === null)
3485
								{
3486
									$temp = array();
3487
3488
									if ($command_line)
3489
										echo "\nFailed to unserialize " . $row[$col] . ". Setting to empty value.\n";
3490
								}
3491
3492
								$row[$col] = json_encode($temp);
3493
3494
								// Build our SET string and variables array
3495
								$update .= (empty($update) ? '' : ', ') . $col . ' = {string:' . $col . '}';
3496
								$vars[$col] = $row[$col];
3497
							}
3498
						}
3499
3500
						$vars[$key] = $row[$key];
3501
3502
						// In a few cases, we might have empty data, so don't try to update in those situations...
3503
						if (!empty($update))
3504
						{
3505
							$smcFunc['db_query']('', '
3506
								UPDATE {db_prefix}' . $table . '
3507
								SET ' . $update . '
3508
								WHERE ' . $key . ' = {' . ($key == 'session' ? 'string' : 'int') . ':' . $key . '}',
3509
								$vars
3510
							);
3511
						}
3512
					}
3513
3514
					if ($command_line)
3515
						echo ' done.';
3516
3517
					// Free up some memory...
3518
					$smcFunc['db_free_result']($query);
3519
				}
3520
			}
3521
			// If this is XML to keep it nice for the user do one table at a time anyway!
3522
			if (isset($_GET['xml']))
3523
				return upgradeExit();
0 ignored issues
show
Bug introduced by
Are you sure the usage of upgradeExit() is correct as it seems to always return null.

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

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

}

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

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

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

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