Passed
Push — release-2.1 ( 8f939c...c09c8c )
by Mathias
06:25
created

DatabaseChanges()   F

Complexity

Conditions 20
Paths 217

Size

Total Lines 141
Code Lines 72

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 20
eloc 72
nc 217
nop 0
dl 0
loc 141
rs 3.1208
c 3
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

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

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

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

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

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

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

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

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

Loading history...
576
	}
577
578
	// Make sure it exists. If it doesn't, reset it.
579
	if (!isset($_SESSION['upgrader_langfile']) || preg_match('~[^\w.-]~', $_SESSION['upgrader_langfile']) === 1 || !file_exists($lang_dir . '/' . $_SESSION['upgrader_langfile']))
580
	{
581
		// Use the first one...
582
		list ($_SESSION['upgrader_langfile']) = array_keys($detected_languages);
583
584
		// If we have English and some other language, use the other language.
585
		if ($_SESSION['upgrader_langfile'] == 'Install.english.php' && count($detected_languages) > 1)
586
			list (, $_SESSION['upgrader_langfile']) = array_keys($detected_languages);
587
	}
588
589
	// For backup we load English at first, then the second language will overwrite it.
590
	if ($_SESSION['upgrader_langfile'] != 'Install.english.php')
591
		require_once($lang_dir . '/Install.english.php');
592
593
	// And now include the actual language file itself.
594
	require_once($lang_dir . '/' . $_SESSION['upgrader_langfile']);
595
596
	// Remember what we've done
597
	$loaded_langfile = $lang_dir . '/' . $_SESSION['upgrader_langfile'];
598
}
599
600
// Used to direct the user to another location.
601
function redirectLocation($location, $addForm = true)
602
{
603
	global $upgradeurl, $upcontext, $command_line;
604
605
	// Command line users can't be redirected.
606
	if ($command_line)
607
		upgradeExit(true);
608
609
	// Are we providing the core info?
610
	if ($addForm)
611
	{
612
		$upcontext['upgrade_status']['curstep'] = $upcontext['current_step'];
613
		$location = $upgradeurl . '?step=' . $upcontext['current_step'] . '&substep=' . $_GET['substep'] . '&data=' . base64_encode(json_encode($upcontext['upgrade_status'])) . $location;
614
	}
615
616
	while (@ob_end_clean())
617
		header('location: ' . strtr($location, array('&amp;' => '&')));
618
619
	// Exit - saving status as we go.
620
	upgradeExit(true);
621
}
622
623
// Load all essential data and connect to the DB as this is pre SSI.php
624
function loadEssentialData()
625
{
626
	global $db_server, $db_user, $db_passwd, $db_name, $db_connection;
627
	global $db_prefix, $db_character_set, $db_type, $db_port, $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;
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
				$current_data = strtr(substr(rtrim($current_data), 0, -1), array('{$db_prefix}' => $db_prefix, '{$boarddir}' => $boarddir, '{$sboarddir}' => addslashes($boarddir), '{$boardurl}' => $boardurl, '{$db_collation}' => $db_collation));
2165
2166
				upgrade_query($current_data);
2167
2168
				// @todo This will be how it kinda does it once mysql all stripped out - needed for postgre (etc).
2169
				/*
2170
				$result = $smcFunc['db_query']('', $current_data, false, false);
2171
				// Went wrong?
2172
				if (!$result)
2173
				{
2174
					// Bit of a bodge - do we want the error?
2175
					if (!empty($upcontext['return_error']))
2176
					{
2177
						$upcontext['error_message'] = $smcFunc['db_error']($db_connection);
2178
						return false;
2179
					}
2180
				}*/
2181
				$done_something = true;
2182
			}
2183
			$current_data = '';
2184
		}
2185
		// If this is xml based and we're just getting the item name then that's grand.
2186
		elseif ($support_js && !isset($_GET['xml']) && $upcontext['current_debug_item_name'] != '' && $do_current)
2187
		{
2188
			restore_error_handler();
2189
			return false;
2190
		}
2191
2192
		// Clean up by cleaning any step info.
2193
		$step_progress = array();
2194
		$custom_warning = '';
2195
	}
2196
2197
	// Put back the error handler.
2198
	restore_error_handler();
2199
2200
	if ($command_line)
2201
	{
2202
		echo ' Successful.' . "\n";
2203
		flush();
2204
	}
2205
2206
	$_GET['substep'] = 0;
2207
	return true;
2208
}
2209
2210
function upgrade_query($string, $unbuffered = false)
2211
{
2212
	global $db_connection, $db_server, $db_user, $db_passwd, $db_type;
2213
	global $command_line, $upcontext, $upgradeurl, $modSettings;
2214
	global $db_name, $db_unbuffered, $smcFunc, $txt;
2215
2216
	// Get the query result - working around some SMF specific security - just this once!
2217
	$modSettings['disableQueryCheck'] = true;
2218
	$db_unbuffered = $unbuffered;
2219
	$ignore_insert_error = false;
2220
2221
	// If we got an old pg version and use a insert ignore query
2222
	if ($db_type == 'postgresql' && !$smcFunc['db_native_replace']() && strpos($string, 'ON CONFLICT DO NOTHING') !== false)
2223
	{
2224
		$ignore_insert_error = true;
2225
		$string = str_replace('ON CONFLICT DO NOTHING', '', $string);
2226
	}
2227
	$result = $smcFunc['db_query']('', $string, array('security_override' => true, 'db_error_skip' => true));
2228
	$db_unbuffered = false;
2229
2230
	// Failure?!
2231
	if ($result !== false)
2232
		return $result;
2233
2234
	$db_error_message = $smcFunc['db_error']($db_connection);
2235
	// If MySQL we do something more clever.
2236
	if ($db_type == 'mysql')
2237
	{
2238
		$mysqli_errno = mysqli_errno($db_connection);
2239
		$error_query = in_array(substr(trim($string), 0, 11), array('INSERT INTO', 'UPDATE IGNO', 'ALTER TABLE', 'DROP TABLE ', 'ALTER IGNOR', 'INSERT IGNO'));
2240
2241
		// Error numbers:
2242
		//    1016: Can't open file '....MYI'
2243
		//    1050: Table already exists.
2244
		//    1054: Unknown column name.
2245
		//    1060: Duplicate column name.
2246
		//    1061: Duplicate key name.
2247
		//    1062: Duplicate entry for unique key.
2248
		//    1068: Multiple primary keys.
2249
		//    1072: Key column '%s' doesn't exist in table.
2250
		//    1091: Can't drop key, doesn't exist.
2251
		//    1146: Table doesn't exist.
2252
		//    2013: Lost connection to server during query.
2253
2254
		if ($mysqli_errno == 1016)
2255
		{
2256
			if (preg_match('~\'([^\.\']+)~', $db_error_message, $match) != 0 && !empty($match[1]))
2257
			{
2258
				mysqli_query($db_connection, 'REPAIR TABLE `' . $match[1] . '`');
2259
				$result = mysqli_query($db_connection, $string);
2260
				if ($result !== false)
2261
					return $result;
2262
			}
2263
		}
2264
		elseif ($mysqli_errno == 2013)
2265
		{
2266
			$db_connection = mysqli_connect($db_server, $db_user, $db_passwd);
2267
			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

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

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

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

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