Passed
Pull Request — release-2.1 (#6262)
by Jeremy
03:45
created

backupTable()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 14
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 7
c 0
b 0
f 0
nop 1
dl 0
loc 14
rs 10
nc 4
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 RC3
12
 */
13
14
// Version information...
15
define('SMF_VERSION', '2.1 RC3');
16
define('SMF_FULL_VERSION', 'SMF ' . SMF_VERSION);
17
define('SMF_SOFTWARE_YEAR', '2020');
18
define('SMF_LANG_VERSION', '2.1 RC3');
19
define('SMF_INSTALLING', 1);
20
21
define('JQUERY_VERSION', '3.5.1');
22
define('POSTGRE_TITLE', 'PostgreSQL');
23
define('MYSQL_TITLE', 'MySQL');
24
define('SMF_USER_AGENT', 'Mozilla/5.0 (' . php_uname('s') . ' ' . php_uname('m') . ') AppleWebKit/605.1.15 (KHTML, like Gecko)  SMF/' . strtr(SMF_VERSION, ' ', '.'));
25
if (!defined('TIME_START'))
26
	define('TIME_START', microtime(true));
27
28
/**
29
 * The minimum required PHP version.
30
 *
31
 * @var string
32
 */
33
$GLOBALS['required_php_version'] = '5.4.0';
34
35
/**
36
 * A list of supported database systems.
37
 *
38
 * @var array
39
 */
40
$databases = array(
41
	'mysql' => array(
42
		'name' => 'MySQL',
43
		'version' => '5.0.22',
44
		'version_check' => 'global $db_connection; return min(mysqli_get_server_info($db_connection), mysqli_get_client_info());',
45
		'utf8_support' => true,
46
		'utf8_version' => '5.0.22',
47
		'utf8_version_check' => 'global $db_connection; return mysqli_get_server_info($db_connection);',
48
		'alter_support' => true,
49
	),
50
	'postgresql' => array(
51
		'name' => 'PostgreSQL',
52
		'version' => '9.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("\n" . $txt['error_unexpected_template_call'], isset($upcontext['sub_template']) ? $upcontext['sub_template'] : '');
387
			flush();
388
			die();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

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

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

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

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

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

Loading history...
454
}
455
456
// Load the list of language files, and the current language file.
457
function load_lang_file()
458
{
459
	global $txt, $upcontext, $language, $modSettings, $upgrade_path, $command_line;
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' : $upgrade_path . '/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
	{
475
		$_SESSION['upgrader_langfile'] = 'Install.' . $upcontext['language'] . '.php';
476
		$_SESSION['upgrader_langfile_index'] = 'index.' . $upcontext['language'] . '.php';
477
	}
478
	elseif (isset($upcontext['lang']))
479
	{
480
		$_SESSION['upgrader_langfile'] = 'Install.' . $upcontext['lang'] . '.php';
481
		$_SESSION['upgrader_langfile_index'] = 'index.' . $upcontext['lang'] . '.php';
482
	}
483
	elseif (isset($language))
484
	{
485
		$_SESSION['upgrader_langfile'] = 'Install.' . $language . '.php';
486
		$_SESSION['upgrader_langfile_index'] = 'index.' . $language . '.php';
487
	}
488
489
	// Avoid pointless repetition
490
	if (isset($_SESSION['upgrader_langfile']) && $loaded_langfile == $lang_dir . '/' . $_SESSION['upgrader_langfile'])
491
		return;
492
493
	// Now try to find the language files
494
	if (empty($detected_languages))
495
	{
496
		// Make sure the languages directory actually exists.
497
		if (file_exists($lang_dir))
498
		{
499
			// Find all the "Install" language files in the directory.
500
			$dir = dir($lang_dir);
501
			while ($entry = $dir->read())
502
			{
503
				// Skip any old '-utf8' language files that might be lying around
504
				if (strpos($entry, '-utf8') !== false)
505
					continue;
506
507
				if (substr($entry, 0, 8) == 'Install.' && substr($entry, -4) == '.php')
508
					$detected_languages[$entry] = ucfirst(substr($entry, 8, strlen($entry) - 12));
509
			}
510
			$dir->close();
511
		}
512
		// Our guess was wrong, but that's fine. We'll try again after $modSettings['theme_dir'] is defined.
513
		elseif (!isset($modSettings['theme_dir']))
514
		{
515
			// Define a few essential strings for now.
516
			$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.';
517
			$txt['error_sourcefile_missing'] = 'Unable to find the Sources/%1$s file. Please make sure it was uploaded properly, and then try again.';
518
519
			$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.';
520
			$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.';
521
522
			return;
523
		}
524
	}
525
526
	// Didn't find any, show an error message!
527
	if (empty($detected_languages))
528
	{
529
		$from = explode('/', $command_line ? $upgrade_path : $_SERVER['PHP_SELF']);
530
		$to = explode('/', $lang_dir);
531
		$relPath = $to;
532
533
		foreach($from as $depth => $dir)
534
		{
535
			if ($dir === $to[$depth])
536
				array_shift($relPath);
537
			else
538
			{
539
				$remaining = count($from) - $depth;
540
				if ($remaining > 1)
541
				{
542
					$padLength = (count($relPath) + $remaining - 1) * -1;
543
					$relPath = array_pad($relPath, $padLength, '..');
544
					break;
545
				}
546
				else
547
					$relPath[0] = './' . $relPath[0];
548
			}
549
		}
550
		$relPath = implode(DIRECTORY_SEPARATOR, $relPath);
551
552
		// Command line?
553
		if ($command_line)
554
		{
555
			echo 'This upgrader was unable to find the upgrader\'s language file or files.  They should be found under:', "\n",
556
				$relPath, "\n",
557
				'In some cases, FTP clients do not properly upload files with this many folders. Please double check to make sure you have uploaded all the files in the distribution', "\n",
558
				'If that doesn\'t help, please make sure this upgrade.php file is in the same place as the Themes folder.', "\n";
559
			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...
560
		}
561
562
		// Let's not cache this message, eh?
563
		header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
564
		header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
565
		header('Cache-Control: no-cache');
566
567
		echo '<!DOCTYPE html>
568
			<html>
569
				<head>
570
					<title>SMF Upgrader: Error!</title>
571
						<style>
572
							body {
573
								font-family: sans-serif;
574
								max-width: 700px; }
575
576
								h1 {
577
									font-size: 14pt; }
578
579
								.directory {
580
									margin: 0.3em;
581
									font-family: monospace;
582
									font-weight: bold; }
583
						</style>
584
				</head>
585
				<body>
586
					<h1>A critical error has occurred.</h1>
587
						<p>This upgrader was unable to find the upgrader\'s language file or files.  They should be found under:</p>
588
						<div class="directory">', $relPath, '</div>
589
						<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>
590
						<p>If that doesn\'t help, please make sure this upgrade.php file is in the same place as the Themes folder.</p>
591
						<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>
592
				</body>
593
			</html>';
594
		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...
595
	}
596
597
	// Make sure it exists. If it doesn't, reset it.
598
	if (!isset($_SESSION['upgrader_langfile']) || preg_match('~[^\w.-]~', $_SESSION['upgrader_langfile']) === 1 || !file_exists($lang_dir . '/' . $_SESSION['upgrader_langfile']))
599
	{
600
		// Use the first one...
601
		list ($_SESSION['upgrader_langfile']) = array_keys($detected_languages);
602
603
		// If we have English and some other language, use the other language.
604
		if ($_SESSION['upgrader_langfile'] == 'Install.english.php' && count($detected_languages) > 1)
605
			list (, $_SESSION['upgrader_langfile']) = array_keys($detected_languages);
606
	}
607
608
	// For backup we load English at first, then the second language will overwrite it.
609
	if ($_SESSION['upgrader_langfile'] != 'Install.english.php')
610
	{
611
		require_once($lang_dir . '/index.english.php');
612
		require_once($lang_dir . '/Install.english.php');
613
	}
614
615
	// And now include the actual language file itself.
616
	require_once($lang_dir . '/' . $_SESSION['upgrader_langfile_index']);
617
	require_once($lang_dir . '/' . $_SESSION['upgrader_langfile']);
618
619
	// Remember what we've done
620
	$loaded_langfile = $lang_dir . '/' . $_SESSION['upgrader_langfile'];
621
}
622
623
// Used to direct the user to another location.
624
function redirectLocation($location, $addForm = true)
625
{
626
	global $upgradeurl, $upcontext, $command_line;
627
628
	// Command line users can't be redirected.
629
	if ($command_line)
630
		upgradeExit(true);
631
632
	// Are we providing the core info?
633
	if ($addForm)
634
	{
635
		$upcontext['upgrade_status']['curstep'] = $upcontext['current_step'];
636
		$location = $upgradeurl . '?step=' . $upcontext['current_step'] . '&substep=' . $_GET['substep'] . '&data=' . base64_encode(json_encode($upcontext['upgrade_status'])) . $location;
637
	}
638
639
	while (@ob_end_clean())
640
		header('location: ' . strtr($location, array('&amp;' => '&')));
641
642
	// Exit - saving status as we go.
643
	upgradeExit(true);
644
}
645
646
// Load all essential data and connect to the DB as this is pre SSI.php
647
function loadEssentialData()
648
{
649
	global $db_server, $db_user, $db_passwd, $db_name, $db_connection;
650
	global $db_prefix, $db_character_set, $db_type, $db_port, $db_show_debug;
651
	global $db_mb4, $modSettings, $sourcedir, $smcFunc, $txt, $utf8;
652
653
	// Report all errors if admin wants them or this is a pre-release version.
654
	if (!empty($db_show_debug) || strspn(SMF_VERSION, '1234567890.') !== strlen(SMF_VERSION))
655
		error_reporting(E_ALL);
656
	// Otherwise, report all errors except for deprecation notices.
657
	else
658
		error_reporting(E_ALL & ~E_DEPRECATED);
659
660
661
	define('SMF', 1);
662
	header('X-Frame-Options: SAMEORIGIN');
663
	header('X-XSS-Protection: 1');
664
	header('X-Content-Type-Options: nosniff');
665
666
	// Start the session.
667
	if (@ini_get('session.save_handler') == 'user')
668
		@ini_set('session.save_handler', 'files');
669
	@session_start();
670
671
	if (empty($smcFunc))
672
		$smcFunc = array();
673
674
	require_once($sourcedir . '/Subs.php');
675
676
	$smcFunc['random_int'] = function($min = 0, $max = PHP_INT_MAX)
677
	{
678
		global $sourcedir;
679
680
		// Oh, wouldn't it be great if I *was* crazy? Then the world would be okay.
681
		if (!is_callable('random_int'))
682
			require_once($sourcedir . '/random_compat/random.php');
683
684
		return random_int($min, $max);
685
	};
686
687
	// This is now needed for loadUserSettings()
688
	$smcFunc['random_bytes'] = function($bytes)
689
	{
690
		global $sourcedir;
691
692
		if (!is_callable('random_bytes'))
693
			require_once($sourcedir . '/random_compat/random.php');
694
695
		return random_bytes($bytes);
696
	};
697
698
	// We need this for authentication and some upgrade code
699
	require_once($sourcedir . '/Subs-Auth.php');
700
	require_once($sourcedir . '/Class-Package.php');
701
702
	$smcFunc['strtolower'] = 'smf_strtolower';
703
704
	// Initialize everything...
705
	initialize_inputs();
706
707
	$utf8 = (empty($modSettings['global_character_set']) ? $txt['lang_character_set'] : $modSettings['global_character_set']) === 'UTF-8';
708
709
	// Get the database going!
710
	if (empty($db_type) || $db_type == 'mysqli')
711
	{
712
		$db_type = 'mysql';
713
		// If overriding $db_type, need to set its settings.php entry too
714
		$changes = array();
715
		$changes['db_type'] = 'mysql';
716
		require_once($sourcedir . '/Subs-Admin.php');
717
		updateSettingsFile($changes);
718
	}
719
720
	if (file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php'))
721
	{
722
		require_once($sourcedir . '/Subs-Db-' . $db_type . '.php');
723
724
		// Make the connection...
725
		if (empty($db_connection))
726
		{
727
			$options = array('non_fatal' => true);
728
			// Add in the port if needed
729
			if (!empty($db_port))
730
				$options['port'] = $db_port;
731
732
			if (!empty($db_mb4))
733
				$options['db_mb4'] = $db_mb4;
734
735
			$db_connection = smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, $db_prefix, $options);
736
		}
737
		else
738
			// If we've returned here, ping/reconnect to be safe
739
			$smcFunc['db_ping']($db_connection);
740
741
		// Oh dear god!!
742
		if ($db_connection === null)
743
			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...
744
745
		if ($db_type == 'mysql' && isset($db_character_set) && preg_match('~^\w+$~', $db_character_set) === 1)
746
			$smcFunc['db_query']('', '
747
				SET NAMES {string:db_character_set}',
748
				array(
749
					'db_error_skip' => true,
750
					'db_character_set' => $db_character_set,
751
				)
752
			);
753
754
		// Load the modSettings data...
755
		$request = $smcFunc['db_query']('', '
756
			SELECT variable, value
757
			FROM {db_prefix}settings',
758
			array(
759
				'db_error_skip' => true,
760
			)
761
		);
762
		$modSettings = array();
763
		while ($row = $smcFunc['db_fetch_assoc']($request))
764
			$modSettings[$row['variable']] = $row['value'];
765
		$smcFunc['db_free_result']($request);
766
	}
767
	else
768
		return throw_error(sprintf($txt['error_sourcefile_missing'], 'Subs-Db-' . $db_type . '.php'));
769
770
	// If they don't have the file, they're going to get a warning anyway so we won't need to clean request vars.
771
	if (file_exists($sourcedir . '/QueryString.php') && php_version_check())
772
	{
773
		require_once($sourcedir . '/QueryString.php');
774
		cleanRequest();
775
	}
776
777
	if (!isset($_GET['substep']))
778
		$_GET['substep'] = 0;
779
}
780
781
function initialize_inputs()
782
{
783
	global $start_time, $db_type, $upgrade_path;
784
785
	$start_time = time();
786
787
	umask(0);
788
789
	ob_start();
790
791
	// Better to upgrade cleanly and fall apart than to screw everything up if things take too long.
792
	ignore_user_abort(true);
793
794
	// This is really quite simple; if ?delete is on the URL, delete the upgrader...
795
	if (isset($_GET['delete']))
796
	{
797
		@unlink(__FILE__);
798
799
		// And the extra little files ;).
800
		@unlink(dirname(__FILE__) . '/upgrade_1-0.sql');
801
		@unlink(dirname(__FILE__) . '/upgrade_1-1.sql');
802
		@unlink(dirname(__FILE__) . '/upgrade_2-0_' . $db_type . '.sql');
803
		@unlink(dirname(__FILE__) . '/upgrade_2-1_' . $db_type . '.sql');
804
		@unlink(dirname(__FILE__) . '/upgrade-helper.php');
805
806
		$dh = opendir(dirname(__FILE__));
807
		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

807
		while ($file = readdir(/** @scrutinizer ignore-type */ $dh))
Loading history...
808
		{
809
			if (preg_match('~upgrade_\d-\d_([A-Za-z])+\.sql~i', $file, $matches) && isset($matches[1]))
810
				@unlink(dirname(__FILE__) . '/' . $file);
811
		}
812
		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

812
		closedir(/** @scrutinizer ignore-type */ $dh);
Loading history...
813
814
		// Legacy files while we're at it. NOTE: We only touch files we KNOW shouldn't be there.
815
		// 1.1 Sources files not in 2.0+
816
		@unlink($upgrade_path . '/Sources/ModSettings.php');
817
		// 1.1 Templates that don't exist any more (e.g. renamed)
818
		@unlink($upgrade_path . '/Themes/default/Combat.template.php');
819
		@unlink($upgrade_path . '/Themes/default/Modlog.template.php');
820
		// 1.1 JS files were stored in the main theme folder, but in 2.0+ are in the scripts/ folder
821
		@unlink($upgrade_path . '/Themes/default/fader.js');
822
		@unlink($upgrade_path . '/Themes/default/script.js');
823
		@unlink($upgrade_path . '/Themes/default/spellcheck.js');
824
		@unlink($upgrade_path . '/Themes/default/xml_board.js');
825
		@unlink($upgrade_path . '/Themes/default/xml_topic.js');
826
827
		// 2.0 Sources files not in 2.1+
828
		@unlink($upgrade_path . '/Sources/DumpDatabase.php');
829
		@unlink($upgrade_path . '/Sources/LockTopic.php');
830
831
		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');
832
		exit;
833
	}
834
835
	// Something is causing this to happen, and it's annoying.  Stop it.
836
	$temp = 'upgrade_php?step';
837
	while (strlen($temp) > 4)
838
	{
839
		if (isset($_GET[$temp]))
840
			unset($_GET[$temp]);
841
		$temp = substr($temp, 1);
842
	}
843
844
	// Force a step, defaulting to 0.
845
	$_GET['step'] = (int) @$_GET['step'];
846
	$_GET['substep'] = (int) @$_GET['substep'];
847
}
848
849
// Step 0 - Let's welcome them in and ask them to login!
850
function WelcomeLogin()
851
{
852
	global $boarddir, $sourcedir, $modSettings, $cachedir, $upgradeurl, $upcontext;
853
	global $smcFunc, $db_type, $databases, $boardurl, $upgrade_path;
854
855
	// We global $txt here so that the language files can add to them. This variable is NOT unused.
856
	global $txt;
857
858
	$upcontext['sub_template'] = 'welcome_message';
859
860
	// Check for some key files - one template, one language, and a new and an old source file.
861
	$check = @file_exists($modSettings['theme_dir'] . '/index.template.php')
862
		&& @file_exists($sourcedir . '/QueryString.php')
863
		&& @file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php')
864
		&& @file_exists(dirname(__FILE__) . '/upgrade_2-1_' . $db_type . '.sql');
865
866
	// Need legacy scripts?
867
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 2.1)
868
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_2-0_' . $db_type . '.sql');
869
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 2.0)
870
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_1-1.sql');
871
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 1.1)
872
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_1-0.sql');
873
874
	// We don't need "-utf8" files anymore...
875
	$upcontext['language'] = str_ireplace('-utf8', '', $upcontext['language']);
876
877
	if (!$check)
878
		// 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.
879
		return throw_error($txt['error_upgrade_files_missing']);
880
881
	// Do they meet the install requirements?
882
	if (!php_version_check())
883
		return throw_error($txt['error_php_too_low']);
884
885
	if (!db_version_check())
886
		return throw_error(sprintf($txt['error_db_too_low'], $databases[$db_type]['name']));
887
888
	// Do some checks to make sure they have proper privileges
889
	db_extend('packages');
890
891
	// CREATE
892
	$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');
893
894
	// ALTER
895
	$alter = $smcFunc['db_add_column']('{db_prefix}priv_check', array('name' => 'txt', 'type' => 'varchar', 'size' => 4, 'null' => false, 'default' => ''));
896
897
	// DROP
898
	$drop = $smcFunc['db_drop_table']('{db_prefix}priv_check');
899
900
	// Sorry... we need CREATE, ALTER and DROP
901
	if (!$create || !$alter || !$drop)
902
		return throw_error(sprintf($txt['error_db_privileges'], $databases[$db_type]['name']));
903
904
	// Do a quick version spot check.
905
	$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

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

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

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

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

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

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

2776
	$temp = substr(@implode('', /** @scrutinizer ignore-type */ @file($boarddir . '/index.php')), 0, 4096);
Loading history...
2777
	preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $temp, $match);
2778
	if (empty($match[1]) || (trim($match[1]) != SMF_VERSION))
2779
		print_error('Error: Some files have not yet been updated properly.');
2780
2781
	// Make sure Settings.php is writable.
2782
	quickFileWritable($boarddir . '/Settings.php');
2783
	if (!is_writable($boarddir . '/Settings.php'))
2784
		print_error('Error: Unable to obtain write access to "Settings.php".', true);
2785
2786
	// Make sure Settings_bak.php is writable.
2787
	quickFileWritable($boarddir . '/Settings_bak.php');
2788
	if (!is_writable($boarddir . '/Settings_bak.php'))
2789
		print_error('Error: Unable to obtain write access to "Settings_bak.php".');
2790
2791
	if (isset($modSettings['agreement']) && (!is_writable($boarddir) || file_exists($boarddir . '/agreement.txt')) && !is_writable($boarddir . '/agreement.txt'))
2792
		print_error('Error: Unable to obtain write access to "agreement.txt".');
2793
	elseif (isset($modSettings['agreement']))
2794
	{
2795
		$fp = fopen($boarddir . '/agreement.txt', 'w');
2796
		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

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

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