Passed
Pull Request — release-2.1 (#6171)
by Mathias
04:49
created

upgrade_query()   D

Complexity

Conditions 27
Paths 106

Size

Total Lines 128
Code Lines 65

Duplication

Lines 0
Ratio 0 %

Importance

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

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

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

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

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

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

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

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

Loading history...
576
	}
577
578
	// Make sure it exists. If it doesn't, reset it.
579
	if (!isset($_SESSION['upgrader_langfile']) || preg_match('~[^\w.-]~', $_SESSION['upgrader_langfile']) === 1 || !file_exists($lang_dir . '/' . $_SESSION['upgrader_langfile']))
580
	{
581
		// Use the first one...
582
		list ($_SESSION['upgrader_langfile']) = array_keys($detected_languages);
583
584
		// If we have English and some other language, use the other language.
585
		if ($_SESSION['upgrader_langfile'] == 'Install.english.php' && count($detected_languages) > 1)
586
			list (, $_SESSION['upgrader_langfile']) = array_keys($detected_languages);
587
	}
588
589
	// For backup we load English at first, then the second language will overwrite it.
590
	if ($_SESSION['upgrader_langfile'] != 'Install.english.php')
591
		require_once($lang_dir . '/Install.english.php');
592
593
	// And now include the actual language file itself.
594
	require_once($lang_dir . '/' . $_SESSION['upgrader_langfile']);
595
596
	// Remember what we've done
597
	$loaded_langfile = $lang_dir . '/' . $_SESSION['upgrader_langfile'];
598
}
599
600
// Used to direct the user to another location.
601
function redirectLocation($location, $addForm = true)
602
{
603
	global $upgradeurl, $upcontext, $command_line;
604
605
	// Command line users can't be redirected.
606
	if ($command_line)
607
		upgradeExit(true);
608
609
	// Are we providing the core info?
610
	if ($addForm)
611
	{
612
		$upcontext['upgrade_status']['curstep'] = $upcontext['current_step'];
613
		$location = $upgradeurl . '?step=' . $upcontext['current_step'] . '&substep=' . $_GET['substep'] . '&data=' . base64_encode(json_encode($upcontext['upgrade_status'])) . $location;
614
	}
615
616
	while (@ob_end_clean())
617
		header('location: ' . strtr($location, array('&amp;' => '&')));
618
619
	// Exit - saving status as we go.
620
	upgradeExit(true);
621
}
622
623
// Load all essential data and connect to the DB as this is pre SSI.php
624
function loadEssentialData()
625
{
626
	global $db_server, $db_user, $db_passwd, $db_name, $db_connection;
627
	global $db_prefix, $db_character_set, $db_type, $db_port, $db_show_debug;
628
	global $db_mb4, $modSettings, $sourcedir, $smcFunc, $txt, $utf8;
629
630
	// Report all errors if admin wants them or this is a pre-release version.
631
	if (!empty($db_show_debug) || strspn(SMF_VERSION, '1234567890.') !== strlen(SMF_VERSION))
632
		error_reporting(E_ALL);
633
	// Otherwise, report all errors except for deprecation notices.
634
	else
635
		error_reporting(E_ALL & ~E_DEPRECATED);
636
637
638
	define('SMF', 1);
639
	header('X-Frame-Options: SAMEORIGIN');
640
	header('X-XSS-Protection: 1');
641
	header('X-Content-Type-Options: nosniff');
642
643
	// Start the session.
644
	if (@ini_get('session.save_handler') == 'user')
645
		@ini_set('session.save_handler', 'files');
646
	@session_start();
647
648
	if (empty($smcFunc))
649
		$smcFunc = array();
650
651
	require_once($sourcedir . '/Subs.php');
652
653
	$smcFunc['random_int'] = function($min = 0, $max = PHP_INT_MAX)
654
	{
655
		global $sourcedir;
656
657
		// Oh, wouldn't it be great if I *was* crazy? Then the world would be okay.
658
		if (!is_callable('random_int'))
659
			require_once($sourcedir . '/random_compat/random.php');
660
661
		return random_int($min, $max);
662
	};
663
664
	// This is now needed for loadUserSettings()
665
	$smcFunc['random_bytes'] = function($bytes)
666
	{
667
		global $sourcedir;
668
669
		if (!is_callable('random_bytes'))
670
			require_once($sourcedir . '/random_compat/random.php');
671
672
		return random_bytes($bytes);
673
	};
674
675
	// We need this for authentication and some upgrade code
676
	require_once($sourcedir . '/Subs-Auth.php');
677
	require_once($sourcedir . '/Class-Package.php');
678
679
	$smcFunc['strtolower'] = 'smf_strtolower';
680
681
	// Initialize everything...
682
	initialize_inputs();
683
684
	$utf8 = (empty($modSettings['global_character_set']) ? $txt['lang_character_set'] : $modSettings['global_character_set']) === 'UTF-8';
685
686
	// Get the database going!
687
	if (empty($db_type) || $db_type == 'mysqli')
688
	{
689
		$db_type = 'mysql';
690
		// If overriding $db_type, need to set its settings.php entry too
691
		$changes = array();
692
		$changes['db_type'] = 'mysql';
693
		require_once($sourcedir . '/Subs-Admin.php');
694
		updateSettingsFile($changes);
695
	}
696
697
	if (file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php'))
698
	{
699
		require_once($sourcedir . '/Subs-Db-' . $db_type . '.php');
700
701
		// Make the connection...
702
		if (empty($db_connection))
703
		{
704
			$options = array('non_fatal' => true);
705
			// Add in the port if needed
706
			if (!empty($db_port))
707
				$options['port'] = $db_port;
708
709
			if (!empty($db_mb4))
710
				$options['db_mb4'] = $db_mb4;
711
712
			$db_connection = smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, $db_prefix, $options);
713
		}
714
		else
715
			// If we've returned here, ping/reconnect to be safe
716
			$smcFunc['db_ping']($db_connection);
717
718
		// Oh dear god!!
719
		if ($db_connection === null)
720
			die($txt['error_db_connect_settings']);
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...
721
722
		if ($db_type == 'mysql' && isset($db_character_set) && preg_match('~^\w+$~', $db_character_set) === 1)
723
			$smcFunc['db_query']('', '
724
				SET NAMES {string:db_character_set}',
725
				array(
726
					'db_error_skip' => true,
727
					'db_character_set' => $db_character_set,
728
				)
729
			);
730
731
		// Load the modSettings data...
732
		$request = $smcFunc['db_query']('', '
733
			SELECT variable, value
734
			FROM {db_prefix}settings',
735
			array(
736
				'db_error_skip' => true,
737
			)
738
		);
739
		$modSettings = array();
740
		while ($row = $smcFunc['db_fetch_assoc']($request))
741
			$modSettings[$row['variable']] = $row['value'];
742
		$smcFunc['db_free_result']($request);
743
	}
744
	else
745
		return throw_error(sprintf($txt['error_sourcefile_missing'], 'Subs-Db-' . $db_type . '.php'));
746
747
	// If they don't have the file, they're going to get a warning anyway so we won't need to clean request vars.
748
	if (file_exists($sourcedir . '/QueryString.php') && php_version_check())
749
	{
750
		require_once($sourcedir . '/QueryString.php');
751
		cleanRequest();
752
	}
753
754
	if (!isset($_GET['substep']))
755
		$_GET['substep'] = 0;
756
}
757
758
function initialize_inputs()
759
{
760
	global $start_time, $db_type;
761
762
	$start_time = time();
763
764
	umask(0);
765
766
	ob_start();
767
768
	// Better to upgrade cleanly and fall apart than to screw everything up if things take too long.
769
	ignore_user_abort(true);
770
771
	// This is really quite simple; if ?delete is on the URL, delete the upgrader...
772
	if (isset($_GET['delete']))
773
	{
774
		@unlink(__FILE__);
775
776
		// And the extra little files ;).
777
		@unlink(dirname(__FILE__) . '/upgrade_1-0.sql');
778
		@unlink(dirname(__FILE__) . '/upgrade_1-1.sql');
779
		@unlink(dirname(__FILE__) . '/upgrade_2-0_' . $db_type . '.sql');
780
		@unlink(dirname(__FILE__) . '/upgrade_2-1_' . $db_type . '.sql');
781
		@unlink(dirname(__FILE__) . '/upgrade-helper.php');
782
783
		$dh = opendir(dirname(__FILE__));
784
		while ($file = readdir($dh))
0 ignored issues
show
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

784
		while ($file = readdir(/** @scrutinizer ignore-type */ $dh))
Loading history...
785
		{
786
			if (preg_match('~upgrade_\d-\d_([A-Za-z])+\.sql~i', $file, $matches) && isset($matches[1]))
787
				@unlink(dirname(__FILE__) . '/' . $file);
788
		}
789
		closedir($dh);
0 ignored issues
show
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

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

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

956
		fwrite(/** @scrutinizer ignore-type */ $fp, $modSettings['agreement']);
Loading history...
957
		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

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

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

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

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

2725
	$temp = substr(@implode('', /** @scrutinizer ignore-type */ @file($boarddir . '/index.php')), 0, 4096);
Loading history...
2726
	preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $temp, $match);
2727
	if (empty($match[1]) || (trim($match[1]) != SMF_VERSION))
2728
		print_error('Error: Some files have not yet been updated properly.');
2729
2730
	// Make sure Settings.php is writable.
2731
	quickFileWritable($boarddir . '/Settings.php');
2732
	if (!is_writable($boarddir . '/Settings.php'))
2733
		print_error('Error: Unable to obtain write access to "Settings.php".', true);
2734
2735
	// Make sure Settings_bak.php is writable.
2736
	quickFileWritable($boarddir . '/Settings_bak.php');
2737
	if (!is_writable($boarddir . '/Settings_bak.php'))
2738
		print_error('Error: Unable to obtain write access to "Settings_bak.php".');
2739
2740
	if (isset($modSettings['agreement']) && (!is_writable($boarddir) || file_exists($boarddir . '/agreement.txt')) && !is_writable($boarddir . '/agreement.txt'))
2741
		print_error('Error: Unable to obtain write access to "agreement.txt".');
2742
	elseif (isset($modSettings['agreement']))
2743
	{
2744
		$fp = fopen($boarddir . '/agreement.txt', 'w');
2745
		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

2745
		fwrite(/** @scrutinizer ignore-type */ $fp, $modSettings['agreement']);
Loading history...
2746
		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

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