Passed
Push — release-2.1 ( ab1855...12d126 )
by Mathias
07:51 queued 13s
created

checkChange()   B

Complexity

Conditions 11
Paths 33

Size

Total Lines 70
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 11
eloc 31
nop 1
dl 0
loc 70
rs 7.3166
c 0
b 0
f 0
nc 33

1 Method

Rating   Name   Duplication   Size   Complexity  
C nextSubstep() 0 58 15

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * Simple Machines Forum (SMF)
5
 *
6
 * @package SMF
7
 * @author Simple Machines https://www.simplemachines.org
8
 * @copyright 2021 Simple Machines and individual contributors
9
 * @license https://www.simplemachines.org/about/smf/license.php BSD
10
 *
11
 * @version 2.1 RC4
12
 */
13
14
// Version information...
15
define('SMF_VERSION', '2.1 RC4');
16
define('SMF_FULL_VERSION', 'SMF ' . SMF_VERSION);
17
define('SMF_SOFTWARE_YEAR', '2021');
18
define('SMF_LANG_VERSION', '2.1 RC4');
19
define('SMF_INSTALLING', 1);
20
21
define('JQUERY_VERSION', '3.6.0');
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'] = '7.0.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.6.0',
44
		'version_check' => function() {
45
			global $db_connection;
46
			if (!function_exists('mysqli_fetch_row'))
47
				return false;
48
			return mysqli_fetch_row(mysqli_query($db_connection, 'SELECT VERSION();'))[0];
0 ignored issues
show
Bug introduced by
It seems like mysqli_query($db_connection, 'SELECT VERSION();') can also be of type true; however, parameter $result of mysqli_fetch_row() does only seem to accept mysqli_result, 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

48
			return mysqli_fetch_row(/** @scrutinizer ignore-type */ mysqli_query($db_connection, 'SELECT VERSION();'))[0];
Loading history...
49
		},
50
		'alter_support' => true,
51
	),
52
	'postgresql' => array(
53
		'name' => 'PostgreSQL',
54
		'version' => '9.6',
55
		'version_check' => function() {
56
			if (!function_exists('pg_version'))
57
				return false;
58
			$version = pg_version();
59
			return $version['client'];
60
		},
61
		'always_has_db' => true,
62
	),
63
);
64
65
/**
66
 * The maximum time a single substep may take, in seconds.
67
 *
68
 * @var int
69
 */
70
$timeLimitThreshold = 3;
71
72
/**
73
 * The current path to the upgrade.php file.
74
 *
75
 * @var string
76
 */
77
$upgrade_path = dirname(__FILE__);
78
79
/**
80
 * The URL of the current page.
81
 *
82
 * @var string
83
 */
84
$upgradeurl = $_SERVER['PHP_SELF'];
85
86
/**
87
 * Flag to disable the required administrator login.
88
 *
89
 * @var bool
90
 */
91
$disable_security = false;
92
93
/**
94
 * The amount of seconds allowed between logins.
95
 * If the first user to login is inactive for this amount of seconds, a second login is allowed.
96
 *
97
 * @var int
98
 */
99
$upcontext['inactive_timeout'] = 10;
100
101
global $txt;
102
103
// All the steps in detail.
104
// Number,Name,Function,Progress Weight.
105
$upcontext['steps'] = array(
106
	0 => array(1, 'upgrade_step_login', 'WelcomeLogin', 2),
107
	1 => array(2, 'upgrade_step_options', 'UpgradeOptions', 2),
108
	2 => array(3, 'upgrade_step_backup', 'BackupDatabase', 10),
109
	3 => array(4, 'upgrade_step_database', 'DatabaseChanges', 50),
110
	4 => array(5, 'upgrade_step_convertjson', 'serialize_to_json', 10),
111
	5 => array(6, 'upgrade_step_convertutf', 'ConvertUtf8', 20),
112
	6 => array(7, 'upgrade_step_delete', 'DeleteUpgrade', 1),
113
);
114
// Just to remember which one has files in it.
115
$upcontext['database_step'] = 3;
116
@set_time_limit(600);
117
if (!ini_get('safe_mode'))
118
{
119
	ini_set('mysql.connect_timeout', -1);
120
	ini_set('default_socket_timeout', 900);
121
}
122
// Clean the upgrade path if this is from the client.
123
if (!empty($_SERVER['argv']) && php_sapi_name() == 'cli' && empty($_SERVER['REMOTE_ADDR']))
124
	for ($i = 1; $i < $_SERVER['argc']; $i++)
125
	{
126
		// Provide the help without possible errors if the enviornment isn't sane.
127
		if (in_array($_SERVER['argv'][$i], array('-h', '--help')))
128
		{
129
			cmdStep0();
130
			exit;
131
		}
132
133
		if (preg_match('~^--path=(.+)$~', $_SERVER['argv'][$i], $match) != 0)
134
			$upgrade_path = realpath(substr($match[1], -1) == '/' ? substr($match[1], 0, -1) : $match[1]);
135
136
		// Cases where we do php other/upgrade.php --path=./
137
		if ($upgrade_path == './' && isset($_SERVER['PWD']))
138
			$upgrade_path = realpath($_SERVER['PWD']);
139
		// Cases where we do php upgrade.php --path=../
140
		elseif ($upgrade_path == '../' && isset($_SERVER['PWD']))
141
			$upgrade_path = dirname(realpath($_SERVER['PWD']));
142
	}
143
144
// Are we from the client?
145
if (php_sapi_name() == 'cli' && empty($_SERVER['REMOTE_ADDR']))
146
{
147
	$command_line = true;
148
	$disable_security = true;
149
}
150
else
151
	$command_line = false;
152
153
// We can't do anything without these files.
154
foreach (array(
155
	dirname(__FILE__) . '/upgrade-helper.php',
156
	$upgrade_path . '/Settings.php'
157
) as $required_file)
158
{
159
	if (!file_exists($required_file))
160
		die(basename($required_file) . ' was not found where it was expected: ' . $required_file . '! Make sure you have uploaded ALL files from the upgrade package to your forum\'s root directory. The upgrader cannot continue.');
161
162
	require_once($required_file);
163
}
164
165
// We don't use "-utf8" anymore...  Tweak the entry that may have been loaded by Settings.php
166
if (isset($language))
167
	$language = str_ireplace('-utf8', '', basename($language, '.lng'));
168
169
// Figure out a valid language request (if any)
170
// 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.
171
if (isset($_SERVER['QUERY_STRING']) && preg_match('~\blang=(\w+)~', $_SERVER['QUERY_STRING'], $matches))
172
	$upcontext['lang'] = $matches[1];
173
174
// Are we logged in?
175
if (isset($upgradeData))
176
{
177
	$upcontext['user'] = json_decode(base64_decode($upgradeData), true);
178
179
	// Check for sensible values.
180
	if (empty($upcontext['user']['started']) || $upcontext['user']['started'] < time() - 86400)
181
		$upcontext['user']['started'] = time();
182
	if (empty($upcontext['user']['updated']) || $upcontext['user']['updated'] < time() - 86400)
183
		$upcontext['user']['updated'] = 0;
184
185
	$upcontext['started'] = $upcontext['user']['started'];
186
	$upcontext['updated'] = $upcontext['user']['updated'];
187
188
	$is_debug = !empty($upcontext['user']['debug']) ? true : false;
189
190
	$upcontext['skip_db_substeps'] = !empty($upcontext['user']['skip_db_substeps']);
191
}
192
193
// Nothing sensible?
194
if (empty($upcontext['updated']))
195
{
196
	$upcontext['started'] = time();
197
	$upcontext['updated'] = 0;
198
	$upcontext['skip_db_substeps'] = false;
199
	$upcontext['user'] = array(
200
		'id' => 0,
201
		'name' => 'Guest',
202
		'pass' => 0,
203
		'started' => $upcontext['started'],
204
		'updated' => $upcontext['updated'],
205
	);
206
}
207
208
// Try to load the language file... or at least define a few necessary strings for now.
209
load_lang_file();
210
211
// Load up some essential data...
212
loadEssentialData();
213
214
// Are we going to be mimic'ing SSI at this point?
215
if (isset($_GET['ssi']))
216
{
217
	require_once($sourcedir . '/Errors.php');
218
	require_once($sourcedir . '/Logging.php');
219
	require_once($sourcedir . '/Load.php');
220
	require_once($sourcedir . '/Security.php');
221
	require_once($sourcedir . '/Subs-Package.php');
222
223
	// SMF isn't started up properly, but loadUserSettings calls our cookies.
224
	if (!isset($smcFunc['json_encode']))
225
	{
226
		$smcFunc['json_encode'] = 'json_encode';
227
		$smcFunc['json_decode'] = 'smf_json_decode';
228
	}
229
230
	loadUserSettings();
231
	loadPermissions();
232
	reloadSettings();
233
}
234
235
// Include our helper functions.
236
require_once($sourcedir . '/Subs.php');
237
require_once($sourcedir . '/LogInOut.php');
238
require_once($sourcedir . '/Subs-Editor.php');
239
240
// Don't do security check if on Yabbse
241
if (!isset($modSettings['smfVersion']))
242
	$disable_security = true;
243
244
// This only exists if we're on SMF ;)
245
if (isset($modSettings['smfVersion']))
246
{
247
	$request = $smcFunc['db_query']('', '
248
		SELECT variable, value
249
		FROM {db_prefix}themes
250
		WHERE id_theme = {int:id_theme}
251
			AND variable IN ({string:theme_url}, {string:theme_dir}, {string:images_url})',
252
		array(
253
			'id_theme' => 1,
254
			'theme_url' => 'theme_url',
255
			'theme_dir' => 'theme_dir',
256
			'images_url' => 'images_url',
257
			'db_error_skip' => true,
258
		)
259
	);
260
	while ($row = $smcFunc['db_fetch_assoc']($request))
261
		$modSettings[$row['variable']] = $row['value'];
262
	$smcFunc['db_free_result']($request);
263
}
264
265
if (!isset($modSettings['theme_url']))
266
{
267
	$modSettings['theme_dir'] = $boarddir . '/Themes/default';
268
	$modSettings['theme_url'] = 'Themes/default';
269
	$modSettings['images_url'] = 'Themes/default/images';
270
}
271
if (!isset($settings['default_theme_url']))
272
	$settings['default_theme_url'] = $modSettings['theme_url'];
273
if (!isset($settings['default_theme_dir']))
274
	$settings['default_theme_dir'] = $modSettings['theme_dir'];
275
276
// Old DBs won't have this
277
if (!isset($modSettings['rand_seed']))
278
{
279
	if (!function_exists('cache_put_data'))
280
		require_once($sourcedir . '/Load.php');
281
	smf_seed_generator();
282
}
283
284
// This is needed in case someone invokes the upgrader using https when upgrading an http forum
285
if (httpsOn())
286
	$settings['default_theme_url'] = strtr($settings['default_theme_url'], array('http://' => 'https://'));
287
288
$upcontext['is_large_forum'] = (empty($modSettings['smfVersion']) || $modSettings['smfVersion'] <= '1.1 RC1') && !empty($modSettings['totalMessages']) && $modSettings['totalMessages'] > 75000;
289
290
// Have we got tracking data - if so use it (It will be clean!)
291
if (isset($_GET['data']))
292
{
293
	global $is_debug;
294
295
	$upcontext['upgrade_status'] = json_decode(base64_decode($_GET['data']), true);
296
	$upcontext['current_step'] = $upcontext['upgrade_status']['curstep'];
297
	$upcontext['language'] = $upcontext['upgrade_status']['lang'];
298
	$upcontext['rid'] = $upcontext['upgrade_status']['rid'];
299
	$support_js = $upcontext['upgrade_status']['js'];
300
301
	// Only set this if the upgrader status says so.
302
	if (empty($is_debug))
303
		$is_debug = $upcontext['upgrade_status']['debug'];
304
}
305
// Set the defaults.
306
else
307
{
308
	$upcontext['current_step'] = 0;
309
	$upcontext['rid'] = mt_rand(0, 5000);
310
	$upcontext['upgrade_status'] = array(
311
		'curstep' => 0,
312
		'lang' => isset($upcontext['lang']) ? $upcontext['lang'] : basename($language, '.lng'),
313
		'rid' => $upcontext['rid'],
314
		'pass' => 0,
315
		'debug' => 0,
316
		'js' => 0,
317
	);
318
	$upcontext['language'] = $upcontext['upgrade_status']['lang'];
319
}
320
321
// Now that we have the necessary info, make sure we loaded the right language file.
322
load_lang_file();
323
324
// Default title...
325
$upcontext['page_title'] = $txt['updating_smf_installation'];
326
327
// If this isn't the first stage see whether they are logging in and resuming.
328
if ($upcontext['current_step'] != 0 || !empty($upcontext['user']['step']))
329
	checkLogin();
330
331
if ($command_line)
332
	cmdStep0();
333
334
// Don't error if we're using xml.
335
if (isset($_GET['xml']))
336
	$upcontext['return_error'] = true;
337
338
// Loop through all the steps doing each one as required.
339
$upcontext['overall_percent'] = 0;
340
foreach ($upcontext['steps'] as $num => $step)
341
{
342
	if ($num >= $upcontext['current_step'])
343
	{
344
		// The current weight of this step in terms of overall progress.
345
		$upcontext['step_weight'] = $step[3];
346
		// Make sure we reset the skip button.
347
		$upcontext['skip'] = false;
348
349
		// We cannot proceed if we're not logged in.
350
		if ($num != 0 && !$disable_security && $upcontext['user']['pass'] != $upcontext['upgrade_status']['pass'])
351
		{
352
			$upcontext['steps'][0][2]();
353
			break;
354
		}
355
356
		// Call the step and if it returns false that means pause!
357
		if (function_exists($step[2]) && $step[2]() === false)
358
			break;
359
		elseif (function_exists($step[2]))
360
		{
361
			//Start each new step with this unset, so the 'normal' template is called first
362
			unset($_GET['xml']);
363
			//Clear out warnings at the start of each step
364
			unset($upcontext['custom_warning']);
365
			$_GET['substep'] = 0;
366
			$upcontext['current_step']++;
367
		}
368
	}
369
	$upcontext['overall_percent'] += $step[3];
370
}
371
372
upgradeExit();
373
374
// Exit the upgrade script.
375
function upgradeExit($fallThrough = false)
376
{
377
	global $upcontext, $upgradeurl, $sourcedir, $command_line, $is_debug, $txt;
378
379
	// Save where we are...
380
	if (!empty($upcontext['current_step']) && !empty($upcontext['user']['id']))
381
	{
382
		$upcontext['user']['step'] = $upcontext['current_step'];
383
		$upcontext['user']['substep'] = $_GET['substep'];
384
		$upcontext['user']['updated'] = time();
385
		$upcontext['user']['skip_db_substeps'] = !empty($upcontext['skip_db_substeps']);
386
		$upcontext['debug'] = $is_debug;
387
		$upgradeData = base64_encode(json_encode($upcontext['user']));
388
		require_once($sourcedir . '/Subs.php');
389
		require_once($sourcedir . '/Subs-Admin.php');
390
		updateSettingsFile(array('upgradeData' => $upgradeData));
391
		updateDbLastError(0);
392
	}
393
394
	// Handle the progress of the step, if any.
395
	if (!empty($upcontext['step_progress']) && isset($upcontext['steps'][$upcontext['current_step']]))
396
	{
397
		$upcontext['step_progress'] = round($upcontext['step_progress'], 1);
398
		$upcontext['overall_percent'] += $upcontext['step_progress'] * ($upcontext['steps'][$upcontext['current_step']][3] / 100);
399
	}
400
	$upcontext['overall_percent'] = (int) $upcontext['overall_percent'];
401
402
	// We usually dump our templates out.
403
	if (!$fallThrough)
404
	{
405
		// This should not happen my dear... HELP ME DEVELOPERS!!
406
		if (!empty($command_line))
407
		{
408
			if (function_exists('debug_print_backtrace'))
409
				debug_print_backtrace();
410
411
			printf("\n" . $txt['error_unexpected_template_call'], isset($upcontext['sub_template']) ? $upcontext['sub_template'] : '');
412
			flush();
413
			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...
414
		}
415
416
		if (!isset($_GET['xml']))
417
			template_upgrade_above();
418
		else
419
		{
420
			header('content-type: text/xml; charset=UTF-8');
421
			// Sadly we need to retain the $_GET data thanks to the old upgrade scripts.
422
			$upcontext['get_data'] = array();
423
			foreach ($_GET as $k => $v)
424
			{
425
				if (substr($k, 0, 3) != 'amp' && !in_array($k, array('xml', 'substep', 'lang', 'data', 'step', 'filecount')))
426
				{
427
					$upcontext['get_data'][$k] = $v;
428
				}
429
			}
430
			template_xml_above();
431
		}
432
433
		// Call the template.
434
		if (isset($upcontext['sub_template']))
435
		{
436
			$upcontext['upgrade_status']['curstep'] = $upcontext['current_step'];
437
			$upcontext['form_url'] = $upgradeurl . '?step=' . $upcontext['current_step'] . '&amp;substep=' . $_GET['substep'] . '&amp;data=' . base64_encode(json_encode($upcontext['upgrade_status']));
438
439
			// Custom stuff to pass back?
440
			if (!empty($upcontext['query_string']))
441
				$upcontext['form_url'] .= $upcontext['query_string'];
442
443
			// Call the appropriate subtemplate
444
			if (is_callable('template_' . $upcontext['sub_template']))
445
				call_user_func('template_' . $upcontext['sub_template']);
446
			else
447
				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...
448
		}
449
450
		// Was there an error?
451
		if (!empty($upcontext['forced_error_message']))
452
			echo $upcontext['forced_error_message'];
453
454
		// Show the footer.
455
		if (!isset($_GET['xml']))
456
			template_upgrade_below();
457
		else
458
			template_xml_below();
459
	}
460
461
	// Show the upgrade time for CLI when we are completely done, if in debug mode.
462
	if (!empty($command_line) && $is_debug)
463
	{
464
		$active = time() - $upcontext['started'];
465
		$hours = floor($active / 3600);
466
		$minutes = intval(($active / 60) % 60);
467
		$seconds = intval($active % 60);
468
469
		if ($hours > 0)
470
			echo "\n" . '', sprintf($txt['upgrade_completed_time_hms'], $hours, $minutes, $seconds), '' . "\n";
471
		elseif ($minutes > 0)
472
			echo "\n" . '', sprintf($txt['upgrade_completed_time_ms'], $minutes, $seconds), '' . "\n";
473
		elseif ($seconds > 0)
474
			echo "\n" . '', sprintf($txt['upgrade_completed_time_s'], $seconds), '' . "\n";
475
	}
476
477
	// Bang - gone!
478
	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...
479
}
480
481
// Load the list of language files, and the current language file.
482
function load_lang_file()
483
{
484
	global $txt, $upcontext, $language, $modSettings, $upgrade_path, $command_line;
485
486
	static $lang_dir = '', $detected_languages = array(), $loaded_langfile = '';
487
488
	// Do we know where to look for the language files, or shall we just guess for now?
489
	$temp = isset($modSettings['theme_dir']) ? $modSettings['theme_dir'] . '/languages' : $upgrade_path . '/Themes/default/languages';
490
491
	if ($lang_dir != $temp)
492
	{
493
		$lang_dir = $temp;
494
		$detected_languages = array();
495
	}
496
497
	// Override the language file?
498
	if (isset($upcontext['language']))
499
		$_SESSION['upgrader_langfile'] = 'Install.' . $upcontext['language'] . '.php';
500
	elseif (isset($upcontext['lang']))
501
		$_SESSION['upgrader_langfile'] = 'Install.' . $upcontext['lang'] . '.php';
502
	elseif (isset($language))
503
		$_SESSION['upgrader_langfile'] = 'Install.' . $language . '.php';
504
505
	// Avoid pointless repetition
506
	if (isset($_SESSION['upgrader_langfile']) && $loaded_langfile == $lang_dir . '/' . $_SESSION['upgrader_langfile'])
507
		return;
508
509
	// Now try to find the language files
510
	if (empty($detected_languages))
511
	{
512
		// Make sure the languages directory actually exists.
513
		if (file_exists($lang_dir))
514
		{
515
			// Find all the "Install" language files in the directory.
516
			$dir = dir($lang_dir);
517
			while ($entry = $dir->read())
518
			{
519
				// Skip any old '-utf8' language files that might be lying around
520
				if (strpos($entry, '-utf8') !== false)
521
					continue;
522
523
				if (substr($entry, 0, 8) == 'Install.' && substr($entry, -4) == '.php')
524
					$detected_languages[$entry] = ucfirst(substr($entry, 8, strlen($entry) - 12));
525
			}
526
			$dir->close();
527
		}
528
		// Our guess was wrong, but that's fine. We'll try again after $modSettings['theme_dir'] is defined.
529
		elseif (!isset($modSettings['theme_dir']))
530
		{
531
			// Define a few essential strings for now.
532
			$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.';
533
			$txt['error_sourcefile_missing'] = 'Unable to find the Sources/%1$s file. Please make sure it was uploaded properly, and then try again.';
534
535
			$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.';
536
			$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.';
537
538
			return;
539
		}
540
	}
541
542
	// Didn't find any, show an error message!
543
	if (empty($detected_languages))
544
	{
545
		$from = explode('/', $command_line ? $upgrade_path : $_SERVER['PHP_SELF']);
546
		$to = explode('/', $lang_dir);
547
		$relPath = $to;
548
549
		foreach($from as $depth => $dir)
550
		{
551
			if ($dir === $to[$depth])
552
				array_shift($relPath);
553
			else
554
			{
555
				$remaining = count($from) - $depth;
556
				if ($remaining > 1)
557
				{
558
					$padLength = (count($relPath) + $remaining - 1) * -1;
559
					$relPath = array_pad($relPath, $padLength, '..');
560
					break;
561
				}
562
				else
563
					$relPath[0] = './' . $relPath[0];
564
			}
565
		}
566
		$relPath = implode(DIRECTORY_SEPARATOR, $relPath);
567
568
		// Command line?
569
		if ($command_line)
570
		{
571
			echo 'This upgrader was unable to find the upgrader\'s language file or files.  They should be found under:', "\n",
572
				$relPath, "\n",
573
				'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",
574
				'If that doesn\'t help, please make sure this upgrade.php file is in the same place as the Themes folder.', "\n";
575
			die;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

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

Loading history...
576
		}
577
578
		// Let's not cache this message, eh?
579
		header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
580
		header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
581
		header('Cache-Control: no-cache');
582
583
		echo '<!DOCTYPE html>
584
			<html>
585
				<head>
586
					<title>SMF Upgrader: Error!</title>
587
						<style>
588
							body {
589
								font-family: sans-serif;
590
								max-width: 700px; }
591
592
								h1 {
593
									font-size: 14pt; }
594
595
								.directory {
596
									margin: 0.3em;
597
									font-family: monospace;
598
									font-weight: bold; }
599
						</style>
600
				</head>
601
				<body>
602
					<h1>A critical error has occurred.</h1>
603
						<p>This upgrader was unable to find the upgrader\'s language file or files.  They should be found under:</p>
604
						<div class="directory">', $relPath, '</div>
605
						<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>
606
						<p>If that doesn\'t help, please make sure this upgrade.php file is in the same place as the Themes folder.</p>
607
						<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>
608
				</body>
609
			</html>';
610
		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...
611
	}
612
613
	// Make sure it exists. If it doesn't, reset it.
614
	if (!isset($_SESSION['upgrader_langfile']) || preg_match('~[^\w.-]~', $_SESSION['upgrader_langfile']) === 1 || !file_exists($lang_dir . '/' . $_SESSION['upgrader_langfile']))
615
	{
616
		// Use the first one...
617
		list ($_SESSION['upgrader_langfile']) = array_keys($detected_languages);
618
619
		// If we have English and some other language, use the other language.
620
		if ($_SESSION['upgrader_langfile'] == 'Install.english.php' && count($detected_languages) > 1)
621
			list (, $_SESSION['upgrader_langfile']) = array_keys($detected_languages);
622
	}
623
624
	// For backup we load English at first, then the second language will overwrite it.
625
	if ($_SESSION['upgrader_langfile'] != 'Install.english.php')
626
	{
627
		require_once($lang_dir . '/index.english.php');
628
		require_once($lang_dir . '/Install.english.php');
629
	}
630
631
	// And now include the actual language file itself.
632
	require_once($lang_dir . '/' . str_replace('Install.', 'index.', $_SESSION['upgrader_langfile']));
633
	require_once($lang_dir . '/' . $_SESSION['upgrader_langfile']);
634
635
	// Remember what we've done
636
	$loaded_langfile = $lang_dir . '/' . $_SESSION['upgrader_langfile'];
637
}
638
639
// Used to direct the user to another location.
640
function redirectLocation($location, $addForm = true)
641
{
642
	global $upgradeurl, $upcontext, $command_line;
643
644
	// Command line users can't be redirected.
645
	if ($command_line)
646
		upgradeExit(true);
647
648
	// Are we providing the core info?
649
	if ($addForm)
650
	{
651
		$upcontext['upgrade_status']['curstep'] = $upcontext['current_step'];
652
		$location = $upgradeurl . '?step=' . $upcontext['current_step'] . '&substep=' . $_GET['substep'] . '&data=' . base64_encode(json_encode($upcontext['upgrade_status'])) . $location;
653
	}
654
655
	while (@ob_end_clean())
656
		header('location: ' . strtr($location, array('&amp;' => '&')));
657
658
	// Exit - saving status as we go.
659
	upgradeExit(true);
660
}
661
662
// Load all essential data and connect to the DB as this is pre SSI.php
663
function loadEssentialData()
664
{
665
	global $db_server, $db_user, $db_passwd, $db_name, $db_connection;
666
	global $db_prefix, $db_character_set, $db_type, $db_port, $db_show_debug;
667
	global $db_mb4, $modSettings, $sourcedir, $smcFunc, $txt, $utf8;
668
669
	// Report all errors if admin wants them or this is a pre-release version.
670
	if (!empty($db_show_debug) || strspn(SMF_VERSION, '1234567890.') !== strlen(SMF_VERSION))
671
		error_reporting(E_ALL);
672
	// Otherwise, report all errors except for deprecation notices.
673
	else
674
		error_reporting(E_ALL & ~E_DEPRECATED);
675
676
	define('SMF', 1);
677
	header('X-Frame-Options: SAMEORIGIN');
678
	header('X-XSS-Protection: 1');
679
	header('X-Content-Type-Options: nosniff');
680
681
	// Start the session.
682
	if (@ini_get('session.save_handler') == 'user')
683
		@ini_set('session.save_handler', 'files');
684
	@session_start();
685
686
	if (empty($smcFunc))
687
		$smcFunc = array();
688
689
	require_once($sourcedir . '/Subs.php');
690
691
	$smcFunc['random_int'] = function($min = 0, $max = PHP_INT_MAX)
692
	{
693
		global $sourcedir;
694
695
		// Oh, wouldn't it be great if I *was* crazy? Then the world would be okay.
696
		if (!is_callable('random_int'))
697
			require_once($sourcedir . '/random_compat/random.php');
698
699
		return random_int($min, $max);
700
	};
701
702
	// This is now needed for loadUserSettings()
703
	$smcFunc['random_bytes'] = function($bytes)
704
	{
705
		global $sourcedir;
706
707
		if (!is_callable('random_bytes'))
708
			require_once($sourcedir . '/random_compat/random.php');
709
710
		return random_bytes($bytes);
711
	};
712
713
	// We need this for authentication and some upgrade code
714
	require_once($sourcedir . '/Subs-Auth.php');
715
	require_once($sourcedir . '/Class-Package.php');
716
717
	$smcFunc['strtolower'] = 'smf_strtolower';
718
719
	// Initialize everything...
720
	initialize_inputs();
721
722
	$utf8 = (empty($modSettings['global_character_set']) ? $txt['lang_character_set'] : $modSettings['global_character_set']) === 'UTF-8';
723
724
	$smcFunc['normalize'] = function($string, $form = 'c') use ($utf8)
725
	{
726
		global $sourcedir;
727
728
		if (!$utf8)
729
			return $string;
730
731
		require_once($sourcedir . '/Subs-Charset.php');
732
733
		$normalize_func = 'utf8_normalize_' . strtolower((string) $form);
734
735
		if (!function_exists($normalize_func))
736
			return false;
737
738
		return $normalize_func($string);
739
	};
740
741
	// Get the database going!
742
	if (empty($db_type) || $db_type == 'mysqli')
743
	{
744
		$db_type = 'mysql';
745
		// If overriding $db_type, need to set its settings.php entry too
746
		$changes = array();
747
		$changes['db_type'] = 'mysql';
748
		require_once($sourcedir . '/Subs-Admin.php');
749
		updateSettingsFile($changes);
750
	}
751
752
	if (file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php'))
753
	{
754
		require_once($sourcedir . '/Subs-Db-' . $db_type . '.php');
755
756
		// Make the connection...
757
		if (empty($db_connection))
758
		{
759
			$options = array('non_fatal' => true);
760
			// Add in the port if needed
761
			if (!empty($db_port))
762
				$options['port'] = $db_port;
763
764
			if (!empty($db_mb4))
765
				$options['db_mb4'] = $db_mb4;
766
767
			$db_connection = smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, $db_prefix, $options);
768
		}
769
		else
770
			// If we've returned here, ping/reconnect to be safe
771
			$smcFunc['db_ping']($db_connection);
772
773
		// Oh dear god!!
774
		if ($db_connection === null)
775
		{
776
			// Get error info...  Recast just in case we get false or 0...
777
			$error_message = $smcFunc['db_connect_error']();
778
			if (empty($error_message))
779
				$error_message = '';
780
			$error_number = $smcFunc['db_connect_errno']();
781
			if (empty($error_number))
782
				$error_number = '';
783
			$db_error = (!empty($error_number) ? $error_number . ': ' : '') . $error_message;
784
785
			die($txt['error_db_connect_settings'] . '<br><br>' . $db_error);
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...
786
		}
787
788
		if ($db_type == 'mysql' && isset($db_character_set) && preg_match('~^\w+$~', $db_character_set) === 1)
789
			$smcFunc['db_query']('', '
790
				SET NAMES {string:db_character_set}',
791
				array(
792
					'db_error_skip' => true,
793
					'db_character_set' => $db_character_set,
794
				)
795
			);
796
797
		// Load the modSettings data...
798
		$request = $smcFunc['db_query']('', '
799
			SELECT variable, value
800
			FROM {db_prefix}settings',
801
			array(
802
				'db_error_skip' => true,
803
			)
804
		);
805
		$modSettings = array();
806
		while ($row = $smcFunc['db_fetch_assoc']($request))
807
			$modSettings[$row['variable']] = $row['value'];
808
		$smcFunc['db_free_result']($request);
809
	}
810
	else
811
		return throw_error(sprintf($txt['error_sourcefile_missing'], 'Subs-Db-' . $db_type . '.php'));
0 ignored issues
show
Bug introduced by
The function throw_error was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

811
		return /** @scrutinizer ignore-call */ throw_error(sprintf($txt['error_sourcefile_missing'], 'Subs-Db-' . $db_type . '.php'));
Loading history...
812
813
	// If they don't have the file, they're going to get a warning anyway so we won't need to clean request vars.
814
	if (file_exists($sourcedir . '/QueryString.php') && php_version_check())
815
	{
816
		require_once($sourcedir . '/QueryString.php');
817
		cleanRequest();
818
	}
819
820
	if (!isset($_GET['substep']))
821
		$_GET['substep'] = 0;
822
}
823
824
function initialize_inputs()
825
{
826
	global $start_time, $db_type, $upgrade_path;
827
828
	$start_time = time();
829
830
	umask(0);
831
832
	ob_start();
833
834
	// Better to upgrade cleanly and fall apart than to screw everything up if things take too long.
835
	ignore_user_abort(true);
836
837
	// This is really quite simple; if ?delete is on the URL, delete the upgrader...
838
	if (isset($_GET['delete']))
839
	{
840
		deleteFile(__FILE__);
0 ignored issues
show
Bug introduced by
The function deleteFile was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

840
		/** @scrutinizer ignore-call */ 
841
  deleteFile(__FILE__);
Loading history...
841
842
		// And the extra little files ;).
843
		deleteFile(dirname(__FILE__) . '/upgrade_1-0.sql');
844
		deleteFile(dirname(__FILE__) . '/upgrade_1-1.sql');
845
		deleteFile(dirname(__FILE__) . '/upgrade_2-0_' . $db_type . '.sql');
846
		deleteFile(dirname(__FILE__) . '/upgrade_2-1_' . $db_type . '.sql');
847
		deleteFile(dirname(__FILE__) . '/upgrade-helper.php');
848
849
		$dh = opendir(dirname(__FILE__));
850
		while ($file = readdir($dh))
851
		{
852
			if (preg_match('~upgrade_\d-\d_([A-Za-z])+\.sql~i', $file, $matches) && isset($matches[1]))
853
				deleteFile(dirname(__FILE__) . '/' . $file);
854
		}
855
		closedir($dh);
856
857
		// Legacy files while we're at it. NOTE: We only touch files we KNOW shouldn't be there.
858
		// 1.1 Sources files not in 2.0+
859
		deleteFile($upgrade_path . '/Sources/ModSettings.php');
860
		// 1.1 Templates that don't exist any more (e.g. renamed)
861
		deleteFile($upgrade_path . '/Themes/default/Combat.template.php');
862
		deleteFile($upgrade_path . '/Themes/default/Modlog.template.php');
863
		// 1.1 JS files were stored in the main theme folder, but in 2.0+ are in the scripts/ folder
864
		deleteFile($upgrade_path . '/Themes/default/fader.js');
865
		deleteFile($upgrade_path . '/Themes/default/script.js');
866
		deleteFile($upgrade_path . '/Themes/default/spellcheck.js');
867
		deleteFile($upgrade_path . '/Themes/default/xml_board.js');
868
		deleteFile($upgrade_path . '/Themes/default/xml_topic.js');
869
870
		// 2.0 Sources files not in 2.1+
871
		deleteFile($upgrade_path . '/Sources/DumpDatabase.php');
872
		deleteFile($upgrade_path . '/Sources/LockTopic.php');
873
874
		header('location: http' . (httpsOn() ? 's' : '') . '://' . (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : $_SERVER['SERVER_NAME'] . ':' . $_SERVER['SERVER_PORT']) . dirname($_SERVER['PHP_SELF']) . '/Themes/default/images/blank.png');
875
		exit;
876
	}
877
878
	// Something is causing this to happen, and it's annoying.  Stop it.
879
	$temp = 'upgrade_php?step';
880
	while (strlen($temp) > 4)
881
	{
882
		if (isset($_GET[$temp]))
883
			unset($_GET[$temp]);
884
		$temp = substr($temp, 1);
885
	}
886
887
	// Force a step, defaulting to 0.
888
	$_GET['step'] = (int) @$_GET['step'];
889
	$_GET['substep'] = (int) @$_GET['substep'];
890
}
891
892
// Step 0 - Let's welcome them in and ask them to login!
893
function WelcomeLogin()
894
{
895
	global $boarddir, $sourcedir, $modSettings, $cachedir, $upgradeurl, $upcontext;
896
	global $smcFunc, $db_type, $databases, $boardurl, $upgrade_path;
897
898
	// We global $txt here so that the language files can add to them. This variable is NOT unused.
899
	global $txt;
900
901
	$upcontext['sub_template'] = 'welcome_message';
902
903
	// Check for some key files - one template, one language, and a new and an old source file.
904
	$check = @file_exists($modSettings['theme_dir'] . '/index.template.php')
905
		&& @file_exists($sourcedir . '/QueryString.php')
906
		&& @file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php')
907
		&& @file_exists(dirname(__FILE__) . '/upgrade_2-1_' . $db_type . '.sql');
908
909
	// Need legacy scripts?
910
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 2.1)
911
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_2-0_' . $db_type . '.sql');
912
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 2.0)
913
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_1-1.sql');
914
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 1.1)
915
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_1-0.sql');
916
917
	// We don't need "-utf8" files anymore...
918
	$upcontext['language'] = str_ireplace('-utf8', '', $upcontext['language']);
919
920
	if (!$check)
921
		// 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.
922
		return throw_error($txt['error_upgrade_files_missing']);
0 ignored issues
show
Bug introduced by
The function throw_error was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

922
		return /** @scrutinizer ignore-call */ throw_error($txt['error_upgrade_files_missing']);
Loading history...
923
924
	// Do they meet the install requirements?
925
	if (!php_version_check())
926
		return throw_error($txt['error_php_too_low']);
927
928
	if (!db_version_check())
929
		return throw_error(sprintf($txt['error_db_too_low'], $databases[$db_type]['name']));
930
931
	// Do some checks to make sure they have proper privileges
932
	db_extend('packages');
933
934
	// CREATE
935
	$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');
936
937
	// ALTER
938
	$alter = $smcFunc['db_add_column']('{db_prefix}priv_check', array('name' => 'txt', 'type' => 'varchar', 'size' => 4, 'null' => false, 'default' => ''));
939
940
	// DROP
941
	$drop = $smcFunc['db_drop_table']('{db_prefix}priv_check');
942
943
	// Sorry... we need CREATE, ALTER and DROP
944
	if (!$create || !$alter || !$drop)
945
		return throw_error(sprintf($txt['error_db_privileges'], $databases[$db_type]['name']));
946
947
	// Do a quick version spot check.
948
	$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

948
	$temp = substr(@implode('', /** @scrutinizer ignore-type */ @file($boarddir . '/index.php')), 0, 4096);
Loading history...
949
	preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $temp, $match);
950
	if (empty($match[1]) || (trim($match[1]) != SMF_VERSION))
951
		return throw_error($txt['error_upgrade_old_files']);
952
953
	// What absolutely needs to be writable?
954
	$writable_files = array(
955
		$boarddir . '/Settings.php',
956
		$boarddir . '/Settings_bak.php',
957
	);
958
959
	// Only check for minified writable files if we have it enabled or not set.
960
	if (!empty($modSettings['minimize_files']) || !isset($modSettings['minimize_files']))
961
		$writable_files += array(
962
			$modSettings['theme_dir'] . '/css/minified.css',
963
			$modSettings['theme_dir'] . '/scripts/minified.js',
964
			$modSettings['theme_dir'] . '/scripts/minified_deferred.js',
965
		);
966
967
	// Do we need to add this setting?
968
	$need_settings_update = empty($modSettings['custom_avatar_dir']);
969
970
	$custom_av_dir = !empty($modSettings['custom_avatar_dir']) ? $modSettings['custom_avatar_dir'] : $GLOBALS['boarddir'] . '/custom_avatar';
971
	$custom_av_url = !empty($modSettings['custom_avatar_url']) ? $modSettings['custom_avatar_url'] : $boardurl . '/custom_avatar';
972
973
	// This little fellow has to cooperate...
974
	quickFileWritable($custom_av_dir);
0 ignored issues
show
Bug introduced by
The function quickFileWritable was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

974
	/** @scrutinizer ignore-call */ 
975
 quickFileWritable($custom_av_dir);
Loading history...
975
976
	// Are we good now?
977
	if (!is_writable($custom_av_dir))
978
		return throw_error(sprintf($txt['error_dir_not_writable'], $custom_av_dir));
979
	elseif ($need_settings_update)
980
	{
981
		if (!function_exists('cache_put_data'))
982
			require_once($sourcedir . '/Load.php');
983
984
		updateSettings(array('custom_avatar_dir' => $custom_av_dir));
985
		updateSettings(array('custom_avatar_url' => $custom_av_url));
986
	}
987
988
	require_once($sourcedir . '/Security.php');
989
990
	// Check the cache directory.
991
	$cachedir_temp = empty($cachedir) ? $boarddir . '/cache' : $cachedir;
992
	if (!file_exists($cachedir_temp))
993
		@mkdir($cachedir_temp);
994
995
	if (!file_exists($cachedir_temp))
996
		return throw_error($txt['error_cache_not_found']);
997
998
	quickFileWritable($cachedir_temp . '/db_last_error.php');
999
1000
	if (!file_exists($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php'))
1001
		return throw_error(sprintf($txt['error_lang_index_missing'], $upcontext['language'], $upgradeurl));
1002
	elseif (!isset($_GET['skiplang']))
1003
	{
1004
		$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php')), 0, 4096);
1005
		preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
1006
1007
		if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
1008
			return throw_error(sprintf($txt['error_upgrade_old_lang_files'], $upcontext['language'], $upgradeurl));
1009
	}
1010
1011
	if (!makeFilesWritable($writable_files))
0 ignored issues
show
Bug introduced by
The function makeFilesWritable was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

1011
	if (!/** @scrutinizer ignore-call */ makeFilesWritable($writable_files))
Loading history...
1012
		return false;
1013
1014
	// Check agreement.txt. (it may not exist, in which case $boarddir must be writable.)
1015
	if (isset($modSettings['agreement']) && (!is_writable($boarddir) || file_exists($boarddir . '/agreement.txt')) && !is_writable($boarddir . '/agreement.txt'))
1016
		return throw_error($txt['error_agreement_not_writable']);
1017
1018
	// Upgrade the agreement.
1019
	elseif (isset($modSettings['agreement']))
1020
	{
1021
		$fp = fopen($boarddir . '/agreement.txt', 'w');
1022
		fwrite($fp, $modSettings['agreement']);
1023
		fclose($fp);
1024
	}
1025
1026
	// We're going to check that their board dir setting is right in case they've been moving stuff around.
1027
	if (strtr($boarddir, array('/' => '', '\\' => '')) != strtr($upgrade_path, array('/' => '', '\\' => '')))
1028
		$upcontext['warning'] = '
1029
			' . sprintf($txt['upgrade_forumdir_settings'], $boarddir, $upgrade_path) . '<br>
1030
			<ul>
1031
				<li>' . $txt['upgrade_forumdir'] . '  ' . $boarddir . '</li>
1032
				<li>' . $txt['upgrade_sourcedir'] . '  ' . $boarddir . '</li>
1033
				<li>' . $txt['upgrade_cachedir'] . '  ' . $cachedir_temp . '</li>
1034
			</ul>
1035
			' . $txt['upgrade_incorrect_settings'] . '';
1036
1037
	// Confirm mbstring is loaded...
1038
	if (!extension_loaded('mbstring'))
1039
		return throw_error($txt['install_no_mbstring']);
1040
1041
	// Check for https stream support.
1042
	$supported_streams = stream_get_wrappers();
1043
	if (!in_array('https', $supported_streams))
1044
		$upcontext['custom_warning'] = $txt['install_no_https'];
1045
1046
	// Either we're logged in or we're going to present the login.
1047
	if (checkLogin())
1048
		return true;
1049
1050
	$upcontext += createToken('login');
1051
1052
	return false;
1053
}
1054
1055
// Step 0.5: Does the login work?
1056
function checkLogin()
1057
{
1058
	global $modSettings, $upcontext, $disable_security;
1059
	global $smcFunc, $db_type, $support_js, $sourcedir, $txt;
1060
1061
	// Are we trying to login?
1062
	if (isset($_POST['contbutt']) && (!empty($_POST['user']) || $disable_security))
1063
	{
1064
		// If we've disabled security pick a suitable name!
1065
		if (empty($_POST['user']))
1066
			$_POST['user'] = 'Administrator';
1067
1068
		// Before 2.0 these column names were different!
1069
		$oldDB = false;
1070
		if (empty($db_type) || $db_type == 'mysql')
1071
		{
1072
			$request = $smcFunc['db_query']('', '
1073
				SHOW COLUMNS
1074
				FROM {db_prefix}members
1075
				LIKE {string:member_name}',
1076
				array(
1077
					'member_name' => 'memberName',
1078
					'db_error_skip' => true,
1079
				)
1080
			);
1081
			if ($smcFunc['db_num_rows']($request) != 0)
1082
				$oldDB = true;
1083
			$smcFunc['db_free_result']($request);
1084
		}
1085
1086
		// Get what we believe to be their details.
1087
		if (!$disable_security)
1088
		{
1089
			if ($oldDB)
1090
				$request = $smcFunc['db_query']('', '
1091
					SELECT id_member, memberName AS member_name, passwd, id_group,
1092
						additionalGroups AS additional_groups, lngfile
1093
					FROM {db_prefix}members
1094
					WHERE memberName = {string:member_name}',
1095
					array(
1096
						'member_name' => $_POST['user'],
1097
						'db_error_skip' => true,
1098
					)
1099
				);
1100
			else
1101
				$request = $smcFunc['db_query']('', '
1102
					SELECT id_member, member_name, passwd, id_group, additional_groups, lngfile
1103
					FROM {db_prefix}members
1104
					WHERE member_name = {string:member_name}',
1105
					array(
1106
						'member_name' => $_POST['user'],
1107
						'db_error_skip' => true,
1108
					)
1109
				);
1110
			if ($smcFunc['db_num_rows']($request) != 0)
1111
			{
1112
				list ($id_member, $name, $password, $id_group, $addGroups, $user_language) = $smcFunc['db_fetch_row']($request);
1113
1114
				$groups = explode(',', $addGroups);
1115
				$groups[] = $id_group;
1116
1117
				foreach ($groups as $k => $v)
1118
					$groups[$k] = (int) $v;
1119
1120
				$sha_passwd = sha1(strtolower($name) . $_REQUEST['passwrd']);
1121
1122
				// We don't use "-utf8" anymore...
1123
				$user_language = str_ireplace('-utf8', '', $user_language);
1124
			}
1125
			else
1126
				$upcontext['username_incorrect'] = true;
1127
1128
			$smcFunc['db_free_result']($request);
1129
		}
1130
		$upcontext['username'] = $_POST['user'];
1131
1132
		// Track whether javascript works!
1133
		if (isset($_POST['js_works']))
1134
		{
1135
			if (!empty($_POST['js_works']))
1136
			{
1137
				$upcontext['upgrade_status']['js'] = 1;
1138
				$support_js = 1;
1139
			}
1140
			else
1141
				$support_js = 0;
1142
		}
1143
1144
		// Note down the version we are coming from.
1145
		if (!empty($modSettings['smfVersion']) && empty($upcontext['user']['version']))
1146
			$upcontext['user']['version'] = $modSettings['smfVersion'];
1147
1148
		// Didn't get anywhere?
1149
		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']))
1150
		{
1151
			// MD5?
1152
			$md5pass = md5_hmac($_REQUEST['passwrd'], strtolower($_POST['user']));
1153
			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...
1154
			{
1155
				$upcontext['password_failed'] = true;
1156
				// Disable the hashing this time.
1157
				$upcontext['disable_login_hashing'] = true;
1158
			}
1159
		}
1160
1161
		if ((empty($upcontext['password_failed']) && !empty($name)) || $disable_security)
1162
		{
1163
			// Set the password.
1164
			if (!$disable_security)
1165
			{
1166
				// Do we actually have permission?
1167
				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...
1168
				{
1169
					$request = $smcFunc['db_query']('', '
1170
						SELECT permission
1171
						FROM {db_prefix}permissions
1172
						WHERE id_group IN ({array_int:groups})
1173
							AND permission = {string:admin_forum}',
1174
						array(
1175
							'groups' => $groups,
1176
							'admin_forum' => 'admin_forum',
1177
							'db_error_skip' => true,
1178
						)
1179
					);
1180
					if ($smcFunc['db_num_rows']($request) == 0)
1181
						return throw_error($txt['error_not_admin']);
0 ignored issues
show
Bug introduced by
The function throw_error was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

1181
						return /** @scrutinizer ignore-call */ throw_error($txt['error_not_admin']);
Loading history...
1182
					$smcFunc['db_free_result']($request);
1183
				}
1184
1185
				$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...
1186
				$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...
1187
			}
1188
			else
1189
			{
1190
				$upcontext['user']['id'] = 1;
1191
				$upcontext['user']['name'] = 'Administrator';
1192
			}
1193
1194
			if (!is_callable('random_int'))
1195
				require_once('Sources/random_compat/random.php');
1196
1197
			$upcontext['user']['pass'] = random_int(0, 60000);
1198
			// This basically is used to match the GET variables to Settings.php.
1199
			$upcontext['upgrade_status']['pass'] = $upcontext['user']['pass'];
1200
1201
			// Set the language to that of the user?
1202
			if (isset($user_language) && $user_language != $upcontext['language'] && file_exists($modSettings['theme_dir'] . '/languages/index.' . basename($user_language, '.lng') . '.php'))
1203
			{
1204
				$user_language = basename($user_language, '.lng');
1205
				$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

1205
				$temp = substr(@implode('', /** @scrutinizer ignore-type */ @file($modSettings['theme_dir'] . '/languages/index.' . $user_language . '.php')), 0, 4096);
Loading history...
1206
				preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
1207
1208
				if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
1209
					$upcontext['upgrade_options_warning'] = sprintf($txt['warning_lang_old'], $user_language, $upcontext['language']);
1210
				elseif (!file_exists($modSettings['theme_dir'] . '/languages/Install.' . $user_language . '.php'))
1211
					$upcontext['upgrade_options_warning'] = sprintf($txt['warning_lang_missing'], $user_language, $upcontext['language']);
1212
				else
1213
				{
1214
					// Set this as the new language.
1215
					$upcontext['language'] = $user_language;
1216
					$upcontext['upgrade_status']['lang'] = $upcontext['language'];
1217
1218
					// Include the file.
1219
					load_lang_file();
1220
				}
1221
			}
1222
1223
			// If we're resuming set the step and substep to be correct.
1224
			if (isset($_POST['cont']))
1225
			{
1226
				$upcontext['current_step'] = $upcontext['user']['step'];
1227
				$_GET['substep'] = $upcontext['user']['substep'];
1228
			}
1229
1230
			return true;
1231
		}
1232
	}
1233
1234
	return false;
1235
}
1236
1237
// Step 1: Do the maintenance and backup.
1238
function UpgradeOptions()
1239
{
1240
	global $db_prefix, $command_line, $modSettings, $is_debug, $smcFunc, $packagesdir, $tasksdir, $language, $txt, $db_port;
1241
	global $boarddir, $boardurl, $sourcedir, $maintenance, $cachedir, $upcontext, $db_type, $db_server, $image_proxy_enabled;
1242
	global $auth_secret;
1243
1244
	$upcontext['sub_template'] = 'upgrade_options';
1245
	$upcontext['page_title'] = $txt['upgrade_options'];
1246
1247
	db_extend('packages');
1248
	$upcontext['karma_installed'] = array('good' => false, 'bad' => false);
1249
	$member_columns = $smcFunc['db_list_columns']('{db_prefix}members');
1250
1251
	$upcontext['karma_installed']['good'] = in_array('karma_good', $member_columns);
1252
	$upcontext['karma_installed']['bad'] = in_array('karma_bad', $member_columns);
1253
1254
	$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', '<');
1255
1256
	unset($member_columns);
1257
1258
	// If we've not submitted then we're done.
1259
	if (empty($_POST['upcont']))
1260
		return false;
1261
1262
	// We cannot execute this step in strict mode - strict mode data fixes are not applied yet
1263
	setSqlMode(false);
1264
1265
	// Firstly, if they're enabling SM stat collection just do it.
1266
	if (!empty($_POST['stats']) && substr($boardurl, 0, 16) != 'http://localhost' && empty($modSettings['allow_sm_stats']) && empty($modSettings['enable_sm_stats']))
1267
	{
1268
		$upcontext['allow_sm_stats'] = true;
1269
1270
		// Don't register if we still have a key.
1271
		if (empty($modSettings['sm_stats_key']))
1272
		{
1273
			// Attempt to register the site etc.
1274
			$fp = @fsockopen('www.simplemachines.org', 443, $errno, $errstr);
1275
			if (!$fp)
0 ignored issues
show
introduced by
$fp is of type false|resource, thus it always evaluated to false.
Loading history...
1276
				$fp = @fsockopen('www.simplemachines.org', 80, $errno, $errstr);
1277
			if ($fp)
0 ignored issues
show
introduced by
$fp is of type false|resource, thus it always evaluated to false.
Loading history...
1278
			{
1279
				$out = 'GET /smf/stats/register_stats.php?site=' . base64_encode($boardurl) . ' HTTP/1.1' . "\r\n";
1280
				$out .= 'Host: www.simplemachines.org' . "\r\n";
1281
				$out .= 'Connection: Close' . "\r\n\r\n";
1282
				fwrite($fp, $out);
1283
1284
				$return_data = '';
1285
				while (!feof($fp))
1286
					$return_data .= fgets($fp, 128);
1287
1288
				fclose($fp);
1289
1290
				// Get the unique site ID.
1291
				preg_match('~SITE-ID:\s(\w{10})~', $return_data, $ID);
1292
1293
				if (!empty($ID[1]))
1294
					$smcFunc['db_insert']('replace',
1295
						$db_prefix . 'settings',
1296
						array('variable' => 'string', 'value' => 'string'),
1297
						array(
1298
							array('sm_stats_key', $ID[1]),
1299
							array('enable_sm_stats', 1),
1300
						),
1301
						array('variable')
1302
					);
1303
			}
1304
		}
1305
		else
1306
		{
1307
			$smcFunc['db_insert']('replace',
1308
				$db_prefix . 'settings',
1309
				array('variable' => 'string', 'value' => 'string'),
1310
				array('enable_sm_stats', 1),
1311
				array('variable')
1312
			);
1313
		}
1314
	}
1315
	// Don't remove stat collection unless we unchecked the box for real, not from the loop.
1316
	elseif (empty($_POST['stats']) && empty($upcontext['allow_sm_stats']))
1317
		$smcFunc['db_query']('', '
1318
			DELETE FROM {db_prefix}settings
1319
			WHERE variable = {string:enable_sm_stats}',
1320
			array(
1321
				'enable_sm_stats' => 'enable_sm_stats',
1322
				'db_error_skip' => true,
1323
			)
1324
		);
1325
1326
	// Deleting old karma stuff?
1327
	$_SESSION['delete_karma'] = !empty($_POST['delete_karma']);
1328
1329
	// Emptying the error log?
1330
	$_SESSION['empty_error'] = !empty($_POST['empty_error']);
1331
1332
	$changes = array();
1333
1334
	// Add proxy settings.
1335
	if (!isset($GLOBALS['image_proxy_secret']) || $GLOBALS['image_proxy_secret'] == 'smfisawesome')
1336
		$changes['image_proxy_secret'] = substr(sha1(mt_rand()), 0, 20);
1337
	if (!isset($GLOBALS['image_proxy_maxsize']))
1338
		$changes['image_proxy_maxsize'] = 5190;
1339
	if (!isset($GLOBALS['image_proxy_enabled']))
1340
		$changes['image_proxy_enabled'] = false;
1341
1342
	// If $boardurl reflects https, set force_ssl
1343
	if (!function_exists('cache_put_data'))
1344
		require_once($sourcedir . '/Load.php');
1345
	if (stripos($boardurl, 'https://') !== false && !isset($modSettings['force_ssl']))
1346
		updateSettings(array('force_ssl' => '1'));
1347
1348
	// If we're overriding the language follow it through.
1349
	if (isset($upcontext['lang']) && file_exists($modSettings['theme_dir'] . '/languages/index.' . $upcontext['lang'] . '.php'))
1350
		$changes['language'] = $upcontext['lang'];
1351
1352
	if (!empty($_POST['maint']))
1353
	{
1354
		$changes['maintenance'] = 2;
1355
		// Remember what it was...
1356
		$upcontext['user']['main'] = $maintenance;
1357
1358
		if (!empty($_POST['maintitle']))
1359
		{
1360
			$changes['mtitle'] = $_POST['maintitle'];
1361
			$changes['mmessage'] = $_POST['mainmessage'];
1362
		}
1363
		else
1364
		{
1365
			$changes['mtitle'] = $txt['mtitle'];
1366
			$changes['mmessage'] = $txt['mmessage'];
1367
		}
1368
	}
1369
1370
	if ($command_line)
1371
		echo ' * Updating Settings.php...';
1372
1373
	// Fix some old paths.
1374
	if (substr($boarddir, 0, 1) == '.')
1375
		$changes['boarddir'] = fixRelativePath($boarddir);
1376
1377
	if (substr($sourcedir, 0, 1) == '.')
1378
		$changes['sourcedir'] = fixRelativePath($sourcedir);
1379
1380
	if (empty($cachedir) || substr($cachedir, 0, 1) == '.')
1381
		$changes['cachedir'] = fixRelativePath($boarddir) . '/cache';
1382
1383
	// Migrate cache settings.
1384
	// Accelerator setting didn't exist previously; use 'smf' file based caching as default if caching had been enabled.
1385
	if (!isset($GLOBALS['cache_enable']))
1386
		$changes += array(
1387
			'cache_accelerator' => upgradeCacheSettings(),
0 ignored issues
show
Bug introduced by
The function upgradeCacheSettings was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

1387
			'cache_accelerator' => /** @scrutinizer ignore-call */ upgradeCacheSettings(),
Loading history...
1388
			'cache_enable' => !empty($modSettings['cache_enable']) ? $modSettings['cache_enable'] : 0,
1389
			'cache_memcached' => !empty($modSettings['cache_memcached']) ? $modSettings['cache_memcached'] : '',
1390
		);
1391
1392
	// If they have a "host:port" setup for the host, split that into separate values
1393
	// You should never have a : in the hostname if you're not on MySQL, but better safe than sorry
1394
	if (strpos($db_server, ':') !== false && $db_type == 'mysql')
1395
	{
1396
		list ($db_server, $db_port) = explode(':', $db_server);
1397
1398
		$changes['db_server'] = $db_server;
1399
1400
		// Only set this if we're not using the default port
1401
		if ($db_port != ini_get('mysqli.default_port'))
1402
			$changes['db_port'] = (int) $db_port;
1403
	}
1404
1405
	// If db_port is set and is the same as the default, set it to 0.
1406
	if (!empty($db_port))
1407
	{
1408
		if ($db_type == 'mysql' && $db_port == ini_get('mysqli.default_port'))
1409
			$changes['db_port'] = 0;
1410
1411
		elseif ($db_type == 'postgresql' && $db_port == 5432)
1412
			$changes['db_port'] = 0;
1413
	}
1414
1415
	// Maybe we haven't had this option yet?
1416
	if (empty($packagesdir))
1417
		$changes['packagesdir'] = fixRelativePath($boarddir) . '/Packages';
1418
1419
	// Add support for $tasksdir var.
1420
	if (empty($tasksdir))
1421
		$changes['tasksdir'] = fixRelativePath($sourcedir) . '/tasks';
1422
1423
	// Make sure we fix the language as well.
1424
	if (stristr($language, '-utf8'))
1425
		$changes['language'] = str_ireplace('-utf8', '', $language);
1426
1427
	// @todo Maybe change the cookie name if going to 1.1, too?
1428
1429
	// Ensure this doesn't get lost in translation.
1430
	$changes['upgradeData'] = base64_encode(json_encode($upcontext['user']));
1431
1432
	// Update Settings.php with the new settings, and rebuild if they selected that option.
1433
	require_once($sourcedir . '/Subs.php');
1434
	require_once($sourcedir . '/Subs-Admin.php');
1435
	$res = updateSettingsFile($changes, false, !empty($_POST['migrateSettings']));
1436
1437
	if ($command_line && $res)
1438
		echo ' Successful.' . "\n";
1439
	elseif ($command_line && !$res)
1440
	{
1441
		echo ' FAILURE.' . "\n";
1442
		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...
1443
	}
1444
1445
	// Are we doing debug?
1446
	if (isset($_POST['debug']))
1447
	{
1448
		$upcontext['upgrade_status']['debug'] = true;
1449
		$is_debug = true;
1450
	}
1451
1452
	// If we're not backing up then jump one.
1453
	if (empty($_POST['backup']))
1454
		$upcontext['current_step']++;
1455
1456
	// If we've got here then let's proceed to the next step!
1457
	return true;
1458
}
1459
1460
// Backup the database - why not...
1461
function BackupDatabase()
1462
{
1463
	global $upcontext, $db_prefix, $command_line, $support_js, $file_steps, $smcFunc, $txt;
1464
1465
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'backup_xml' : 'backup_database';
1466
	$upcontext['page_title'] = $txt['backup_database'];
1467
1468
	// Done it already - js wise?
1469
	if (!empty($_POST['backup_done']))
1470
		return true;
1471
1472
	// We cannot execute this step in strict mode - strict mode data fixes are not applied yet
1473
	setSqlMode(false);
1474
1475
	// Some useful stuff here.
1476
	db_extend();
1477
1478
	// Might need this as well
1479
	db_extend('packages');
1480
1481
	// Get all the table names.
1482
	$filter = str_replace('_', '\_', preg_match('~^`(.+?)`\.(.+?)$~', $db_prefix, $match) != 0 ? $match[2] : $db_prefix) . '%';
1483
	$db = preg_match('~^`(.+?)`\.(.+?)$~', $db_prefix, $match) != 0 ? strtr($match[1], array('`' => '')) : false;
1484
	$tables = $smcFunc['db_list_tables']($db, $filter);
1485
1486
	$table_names = array();
1487
	foreach ($tables as $table)
1488
		if (substr($table, 0, 7) !== 'backup_')
1489
			$table_names[] = $table;
1490
1491
	$upcontext['table_count'] = count($table_names);
1492
	$upcontext['cur_table_num'] = $_GET['substep'];
1493
	$upcontext['cur_table_name'] = str_replace($db_prefix, '', isset($table_names[$_GET['substep']]) ? $table_names[$_GET['substep']] : $table_names[0]);
1494
	$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
1495
	// For non-java auto submit...
1496
	$file_steps = $upcontext['table_count'];
1497
1498
	// What ones have we already done?
1499
	foreach ($table_names as $id => $table)
1500
		if ($id < $_GET['substep'])
1501
			$upcontext['previous_tables'][] = $table;
1502
1503
	if ($command_line)
1504
		echo 'Backing Up Tables.';
1505
1506
	// If we don't support javascript we backup here.
1507
	if (!$support_js || isset($_GET['xml']))
1508
	{
1509
		// Backup each table!
1510
		for ($substep = $_GET['substep'], $n = count($table_names); $substep < $n; $substep++)
1511
		{
1512
			$upcontext['cur_table_name'] = str_replace($db_prefix, '', (isset($table_names[$substep + 1]) ? $table_names[$substep + 1] : $table_names[$substep]));
1513
			$upcontext['cur_table_num'] = $substep + 1;
1514
1515
			$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
1516
1517
			// Do we need to pause?
1518
			nextSubstep($substep);
1519
1520
			backupTable($table_names[$substep]);
1521
1522
			// If this is XML to keep it nice for the user do one table at a time anyway!
1523
			if (isset($_GET['xml']))
1524
				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...
1525
		}
1526
1527
		if ($command_line)
1528
		{
1529
			echo "\n" . ' Successful.\'' . "\n";
1530
			flush();
1531
		}
1532
		$upcontext['step_progress'] = 100;
1533
1534
		$_GET['substep'] = 0;
1535
		// Make sure we move on!
1536
		return true;
1537
	}
1538
1539
	// Either way next place to post will be database changes!
1540
	$_GET['substep'] = 0;
1541
	return false;
1542
}
1543
1544
// Backup one table...
1545
function backupTable($table)
1546
{
1547
	global $command_line, $db_prefix, $smcFunc;
1548
1549
	if ($command_line)
1550
	{
1551
		echo "\n" . ' +++ Backing up \"' . str_replace($db_prefix, '', $table) . '"...';
1552
		flush();
1553
	}
1554
1555
	$smcFunc['db_backup_table']($table, 'backup_' . $table);
1556
1557
	if ($command_line)
1558
		echo ' done.';
1559
}
1560
1561
// Step 2: Everything.
1562
function DatabaseChanges()
1563
{
1564
	global $db_prefix, $modSettings, $smcFunc, $txt;
1565
	global $upcontext, $support_js, $db_type, $boarddir;
1566
1567
	// Have we just completed this?
1568
	if (!empty($_POST['database_done']))
1569
		return true;
1570
1571
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'database_xml' : 'database_changes';
1572
	$upcontext['page_title'] = $txt['database_changes'];
1573
1574
	$upcontext['delete_karma'] = !empty($_SESSION['delete_karma']);
1575
	$upcontext['empty_error'] = !empty($_SESSION['empty_error']);
1576
1577
	// All possible files.
1578
	// Name, < version, insert_on_complete
1579
	// Last entry in array indicates whether to use sql_mode of STRICT or not.
1580
	$files = array(
1581
		array('upgrade_1-0.sql', '1.1', '1.1 RC0', false),
1582
		array('upgrade_1-1.sql', '2.0', '2.0 a', false),
1583
		array('upgrade_2-0_' . $db_type . '.sql', '2.1', '2.1 dev0', false),
1584
		array('upgrade_2-1_' . $db_type . '.sql', '3.0', SMF_VERSION, true),
1585
	);
1586
1587
	// How many files are there in total?
1588
	if (isset($_GET['filecount']))
1589
		$upcontext['file_count'] = (int) $_GET['filecount'];
1590
	else
1591
	{
1592
		$upcontext['file_count'] = 0;
1593
		foreach ($files as $file)
1594
		{
1595
			if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < $file[1])
1596
				$upcontext['file_count']++;
1597
		}
1598
	}
1599
1600
	// Do each file!
1601
	$did_not_do = count($files) - $upcontext['file_count'];
1602
	$upcontext['step_progress'] = 0;
1603
	$upcontext['cur_file_num'] = 0;
1604
	foreach ($files as $file)
1605
	{
1606
		if ($did_not_do)
1607
			$did_not_do--;
1608
		else
1609
		{
1610
			$upcontext['cur_file_num']++;
1611
			$upcontext['cur_file_name'] = $file[0];
1612
			// Do we actually need to do this still?
1613
			if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < $file[1])
1614
			{
1615
				// Use STRICT mode on more recent steps
1616
				setSqlMode($file[3]);
1617
1618
				// Reload modSettings to capture any adds/updates made along the way
1619
				$request = $smcFunc['db_query']('', '
1620
					SELECT variable, value
1621
					FROM {db_prefix}settings',
1622
					array(
1623
						'db_error_skip' => true,
1624
					)
1625
				);
1626
1627
				$modSettings = array();
1628
				while ($row = $smcFunc['db_fetch_assoc']($request))
1629
					$modSettings[$row['variable']] = $row['value'];
1630
1631
				$smcFunc['db_free_result']($request);
1632
1633
				// Some theme settings are in $modSettings
1634
				// Note we still might be doing yabbse (no smf ver)
1635
				if (isset($modSettings['smfVersion']))
1636
				{
1637
					$request = $smcFunc['db_query']('', '
1638
						SELECT variable, value
1639
						FROM {db_prefix}themes
1640
						WHERE id_theme = {int:id_theme}
1641
							AND variable IN ({string:theme_url}, {string:theme_dir}, {string:images_url})',
1642
						array(
1643
							'id_theme' => 1,
1644
							'theme_url' => 'theme_url',
1645
							'theme_dir' => 'theme_dir',
1646
							'images_url' => 'images_url',
1647
							'db_error_skip' => true,
1648
						)
1649
					);
1650
1651
					while ($row = $smcFunc['db_fetch_assoc']($request))
1652
						$modSettings[$row['variable']] = $row['value'];
1653
1654
					$smcFunc['db_free_result']($request);
1655
				}
1656
1657
				if (!isset($modSettings['theme_url']))
1658
				{
1659
					$modSettings['theme_dir'] = $boarddir . '/Themes/default';
1660
					$modSettings['theme_url'] = 'Themes/default';
1661
					$modSettings['images_url'] = 'Themes/default/images';
1662
				}
1663
1664
				// Now process the file...
1665
				$nextFile = parse_sql(dirname(__FILE__) . '/' . $file[0]);
1666
				if ($nextFile)
1667
				{
1668
					// Only update the version of this if complete.
1669
					$smcFunc['db_insert']('replace',
1670
						$db_prefix . 'settings',
1671
						array('variable' => 'string', 'value' => 'string'),
1672
						array('smfVersion', $file[2]),
1673
						array('variable')
1674
					);
1675
1676
					$modSettings['smfVersion'] = $file[2];
1677
				}
1678
1679
				// If this is XML we only do this stuff once.
1680
				if (isset($_GET['xml']))
1681
				{
1682
					// Flag to move on to the next.
1683
					$upcontext['completed_step'] = true;
1684
					// Did we complete the whole file?
1685
					if ($nextFile)
1686
						$upcontext['current_debug_item_num'] = -1;
1687
					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...
1688
				}
1689
				elseif ($support_js)
1690
					break;
1691
			}
1692
			// Set the progress bar to be right as if we had - even if we hadn't...
1693
			$upcontext['step_progress'] = ($upcontext['cur_file_num'] / $upcontext['file_count']) * 100;
1694
		}
1695
	}
1696
1697
	$_GET['substep'] = 0;
1698
	// So the template knows we're done.
1699
	if (!$support_js)
1700
	{
1701
		$upcontext['changes_complete'] = true;
1702
1703
		return true;
1704
	}
1705
	return false;
1706
}
1707
1708
// Different versions of the files use different sql_modes
1709
function setSqlMode($strict = true)
1710
{
1711
	global $db_type, $db_connection;
1712
1713
	if ($db_type != 'mysql')
1714
		return;
1715
1716
	if ($strict)
1717
		$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,PIPES_AS_CONCAT';
1718
	else
1719
		$mode = '';
1720
1721
	mysqli_query($db_connection, 'SET SESSION sql_mode = \'' . $mode . '\'');
1722
1723
	return;
1724
}
1725
1726
// Delete the damn thing!
1727
function DeleteUpgrade()
1728
{
1729
	global $command_line, $language, $upcontext, $sourcedir;
1730
	global $user_info, $maintenance, $smcFunc, $db_type, $txt, $settings;
1731
1732
	// Now it's nice to have some of the basic SMF source files.
1733
	if (!isset($_GET['ssi']) && !$command_line)
1734
		redirectLocation('&ssi=1');
1735
1736
	$upcontext['sub_template'] = 'upgrade_complete';
1737
	$upcontext['page_title'] = $txt['upgrade_complete'];
1738
1739
	$endl = $command_line ? "\n" : '<br>' . "\n";
1740
1741
	$changes = array(
1742
		'language' => (substr($language, -4) == '.lng' ? substr($language, 0, -4) : $language),
1743
		'db_error_send' => true,
1744
		'upgradeData' => null,
1745
	);
1746
1747
	// Are we in maintenance mode?
1748
	if (isset($upcontext['user']['main']))
1749
	{
1750
		if ($command_line)
1751
			echo ' * ';
1752
		$upcontext['removed_maintenance'] = true;
1753
		$changes['maintenance'] = $upcontext['user']['main'];
1754
	}
1755
	// Otherwise if somehow we are in 2 let's go to 1.
1756
	elseif (!empty($maintenance) && $maintenance == 2)
1757
		$changes['maintenance'] = 1;
1758
1759
	// Wipe this out...
1760
	$upcontext['user'] = array();
1761
1762
	require_once($sourcedir . '/Subs.php');
1763
	require_once($sourcedir . '/Subs-Admin.php');
1764
	updateSettingsFile($changes);
1765
1766
	// Clean any old cache files away.
1767
	upgrade_clean_cache();
0 ignored issues
show
Bug introduced by
The function upgrade_clean_cache was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

1767
	/** @scrutinizer ignore-call */ 
1768
 upgrade_clean_cache();
Loading history...
1768
1769
	// Can we delete the file?
1770
	$upcontext['can_delete_script'] = is_writable(dirname(__FILE__)) || is_writable(__FILE__);
1771
1772
	// Now is the perfect time to fetch the SM files.
1773
	if ($command_line)
1774
		cli_scheduled_fetchSMfiles();
1775
	else
1776
	{
1777
		require_once($sourcedir . '/ScheduledTasks.php');
1778
		scheduled_fetchSMfiles(); // Now go get those files!
1779
		// This is needed in case someone invokes the upgrader using https when upgrading an http forum
1780
		if (httpsOn())
1781
			$settings['default_theme_url'] = strtr($settings['default_theme_url'], array('http://' => 'https://'));
1782
	}
1783
1784
	// Log what we've done.
1785
	if (empty($user_info['id']))
1786
		$user_info['id'] = !empty($upcontext['user']['id']) ? $upcontext['user']['id'] : 0;
1787
1788
	// Log the action manually, so CLI still works.
1789
	$smcFunc['db_insert']('',
1790
		'{db_prefix}log_actions',
1791
		array(
1792
			'log_time' => 'int', 'id_log' => 'int', 'id_member' => 'int', 'ip' => 'inet', 'action' => 'string',
1793
			'id_board' => 'int', 'id_topic' => 'int', 'id_msg' => 'int', 'extra' => 'string-65534',
1794
		),
1795
		array(
1796
			time(), 3, $user_info['id'], $command_line ? '127.0.0.1' : $user_info['ip'], 'upgrade',
1797
			0, 0, 0, json_encode(array('version' => SMF_FULL_VERSION, 'member' => $user_info['id'])),
1798
		),
1799
		array('id_action')
1800
	);
1801
	$user_info['id'] = 0;
1802
1803
	if ($command_line)
1804
	{
1805
		echo $endl;
1806
		echo 'Upgrade Complete!', $endl;
1807
		echo 'Please delete this file as soon as possible for security reasons.', $endl;
1808
		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...
1809
	}
1810
1811
	// Make sure it says we're done.
1812
	$upcontext['overall_percent'] = 100;
1813
	if (isset($upcontext['step_progress']))
1814
		unset($upcontext['step_progress']);
1815
1816
	$_GET['substep'] = 0;
1817
	return false;
1818
}
1819
1820
// Just like the built in one, but setup for CLI to not use themes.
1821
function cli_scheduled_fetchSMfiles()
1822
{
1823
	global $sourcedir, $language, $modSettings, $smcFunc;
1824
1825
	if (empty($modSettings['time_format']))
1826
		$modSettings['time_format'] = '%B %d, %Y, %I:%M:%S %p';
1827
1828
	// What files do we want to get
1829
	$request = $smcFunc['db_query']('', '
1830
		SELECT id_file, filename, path, parameters
1831
		FROM {db_prefix}admin_info_files',
1832
		array(
1833
		)
1834
	);
1835
1836
	$js_files = array();
1837
	while ($row = $smcFunc['db_fetch_assoc']($request))
1838
	{
1839
		$js_files[$row['id_file']] = array(
1840
			'filename' => $row['filename'],
1841
			'path' => $row['path'],
1842
			'parameters' => sprintf($row['parameters'], $language, urlencode($modSettings['time_format']), urlencode(SMF_FULL_VERSION)),
1843
		);
1844
	}
1845
	$smcFunc['db_free_result']($request);
1846
1847
	// We're gonna need fetch_web_data() to pull this off.
1848
	require_once($sourcedir . '/Subs.php');
1849
1850
	foreach ($js_files as $ID_FILE => $file)
1851
	{
1852
		// Create the url
1853
		$server = empty($file['path']) || substr($file['path'], 0, 7) != 'http://' ? 'https://www.simplemachines.org' : '';
1854
		$url = $server . (!empty($file['path']) ? $file['path'] : $file['path']) . $file['filename'] . (!empty($file['parameters']) ? '?' . $file['parameters'] : '');
1855
1856
		// Get the file
1857
		$file_data = fetch_web_data($url);
1858
1859
		// If we got an error - give up - the site might be down.
1860
		if ($file_data === false)
1861
			return throw_error(sprintf('Could not retrieve the file %1$s.', $url));
0 ignored issues
show
Bug introduced by
The function throw_error was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

1861
			return /** @scrutinizer ignore-call */ throw_error(sprintf('Could not retrieve the file %1$s.', $url));
Loading history...
1862
1863
		// Save the file to the database.
1864
		$smcFunc['db_query']('substring', '
1865
			UPDATE {db_prefix}admin_info_files
1866
			SET data = SUBSTRING({string:file_data}, 1, 65534)
1867
			WHERE id_file = {int:id_file}',
1868
			array(
1869
				'id_file' => $ID_FILE,
1870
				'file_data' => $file_data,
1871
			)
1872
		);
1873
	}
1874
	return true;
1875
}
1876
1877
function convertSettingsToTheme()
1878
{
1879
	global $db_prefix, $modSettings, $smcFunc;
1880
1881
	$values = array(
1882
		'show_latest_member' => @$GLOBALS['showlatestmember'],
1883
		'show_bbc' => isset($GLOBALS['showyabbcbutt']) ? $GLOBALS['showyabbcbutt'] : @$GLOBALS['showbbcbutt'],
1884
		'show_modify' => @$GLOBALS['showmodify'],
1885
		'show_user_images' => @$GLOBALS['showuserpic'],
1886
		'show_blurb' => @$GLOBALS['showusertext'],
1887
		'show_gender' => @$GLOBALS['showgenderimage'],
1888
		'show_newsfader' => @$GLOBALS['shownewsfader'],
1889
		'display_recent_bar' => @$GLOBALS['Show_RecentBar'],
1890
		'show_member_bar' => @$GLOBALS['Show_MemberBar'],
1891
		'linktree_link' => @$GLOBALS['curposlinks'],
1892
		'show_profile_buttons' => @$GLOBALS['profilebutton'],
1893
		'show_mark_read' => @$GLOBALS['showmarkread'],
1894
		'show_board_desc' => @$GLOBALS['ShowBDescrip'],
1895
		'newsfader_time' => @$GLOBALS['fadertime'],
1896
		'use_image_buttons' => empty($GLOBALS['MenuType']) ? 1 : 0,
1897
		'enable_news' => @$GLOBALS['enable_news'],
1898
		'linktree_inline' => @$modSettings['enableInlineLinks'],
1899
		'return_to_post' => @$modSettings['returnToPost'],
1900
	);
1901
1902
	$themeData = array();
1903
	foreach ($values as $variable => $value)
1904
	{
1905
		if (!isset($value) || $value === null)
1906
			$value = 0;
1907
1908
		$themeData[] = array(0, 1, $variable, $value);
1909
	}
1910
	if (!empty($themeData))
1911
	{
1912
		$smcFunc['db_insert']('ignore',
1913
			$db_prefix . 'themes',
1914
			array('id_member' => 'int', 'id_theme' => 'int', 'variable' => 'string', 'value' => 'string'),
1915
			$themeData,
1916
			array('id_member', 'id_theme', 'variable')
1917
		);
1918
	}
1919
}
1920
1921
// This function only works with MySQL but that's fine as it is only used for v1.0.
1922
function convertSettingstoOptions()
1923
{
1924
	global $modSettings, $smcFunc;
1925
1926
	// Format: new_setting -> old_setting_name.
1927
	$values = array(
1928
		'calendar_start_day' => 'cal_startmonday',
1929
		'view_newest_first' => 'viewNewestFirst',
1930
		'view_newest_pm_first' => 'viewNewestFirst',
1931
	);
1932
1933
	foreach ($values as $variable => $value)
1934
	{
1935
		if (empty($modSettings[$value[0]]))
1936
			continue;
1937
1938
		$smcFunc['db_query']('', '
1939
			INSERT IGNORE INTO {db_prefix}themes
1940
				(id_member, id_theme, variable, value)
1941
			SELECT id_member, 1, {string:variable}, {string:value}
1942
			FROM {db_prefix}members',
1943
			array(
1944
				'variable' => $variable,
1945
				'value' => $modSettings[$value[0]],
1946
				'db_error_skip' => true,
1947
			)
1948
		);
1949
1950
		$smcFunc['db_query']('', '
1951
			INSERT IGNORE INTO {db_prefix}themes
1952
				(id_member, id_theme, variable, value)
1953
			VALUES (-1, 1, {string:variable}, {string:value})',
1954
			array(
1955
				'variable' => $variable,
1956
				'value' => $modSettings[$value[0]],
1957
				'db_error_skip' => true,
1958
			)
1959
		);
1960
	}
1961
}
1962
1963
function php_version_check()
1964
{
1965
	return version_compare(PHP_VERSION, $GLOBALS['required_php_version'], '>=');
1966
}
1967
1968
function db_version_check()
1969
{
1970
	global $db_type, $databases;
1971
1972
	$curver = $databases[$db_type]['version_check']();
1973
	$curver = preg_replace('~\-.+?$~', '', $curver);
1974
1975
	return version_compare($databases[$db_type]['version'], $curver, '<=');
1976
}
1977
1978
function fixRelativePath($path)
1979
{
1980
	global $install_path;
1981
1982
	// Fix the . at the start, clear any duplicate slashes, and fix any trailing slash...
1983
	return addslashes(preg_replace(array('~^\.([/\\\]|$)~', '~[/]+~', '~[\\\]+~', '~[/\\\]$~'), array($install_path . '$1', '/', '\\', ''), $path));
1984
}
1985
1986
function parse_sql($filename)
1987
{
1988
	global $db_prefix, $db_collation, $boarddir, $boardurl, $command_line, $file_steps, $step_progress, $custom_warning;
1989
	global $upcontext, $support_js, $is_debug, $db_type, $db_character_set, $smcFunc;
1990
1991
/*
1992
	Failure allowed on:
1993
		- INSERT INTO but not INSERT IGNORE INTO.
1994
		- UPDATE IGNORE but not UPDATE.
1995
		- ALTER TABLE and ALTER IGNORE TABLE.
1996
		- DROP TABLE.
1997
	Yes, I realize that this is a bit confusing... maybe it should be done differently?
1998
1999
	If a comment...
2000
		- begins with --- it is to be output, with a break only in debug mode. (and say successful\n\n if there was one before.)
2001
		- begins with ---# it is a debugging statement, no break - only shown at all in debug.
2002
		- is only ---#, it is "done." and then a break - only shown in debug.
2003
		- begins with ---{ it is a code block terminating at ---}.
2004
2005
	Every block of between "--- ..."s is a step.  Every "---#" section represents a substep.
2006
2007
	Replaces the following variables:
2008
		- {$boarddir}
2009
		- {$boardurl}
2010
		- {$db_prefix}
2011
		- {$db_collation}
2012
*/
2013
2014
	// May want to use extended functionality.
2015
	db_extend();
2016
	db_extend('packages');
2017
2018
	// Our custom error handler - does nothing but does stop public errors from XML!
2019
	// Note that php error suppression - @ - used heavily in the upgrader, calls the error handler
2020
	// but error_reporting() will return 0 as it does so (pre php8).
2021
	// Note error handling in php8+ no longer fails silently on many errors, but error_reporting()
2022
	// will return 4437 (E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR | E_RECOVERABLE_ERROR | E_PARSE)
2023
	// as it does so.
2024
	set_error_handler(
2025
		function($errno, $errstr, $errfile, $errline) use ($support_js)
2026
		{
2027
			if ($support_js)
2028
				return true;
2029
			elseif ((error_reporting() != 0) && (error_reporting() != (E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR | E_RECOVERABLE_ERROR | E_PARSE)))
2030
				echo 'Error: ' . $errstr . ' File: ' . $errfile . ' Line: ' . $errline;
2031
		}
2032
	);
2033
2034
	// If we're on MySQL, set {db_collation}; this approach is used throughout upgrade_2-0_mysql.php to set new tables to utf8
2035
	// Note it is expected to be in the format: ENGINE=MyISAM{$db_collation};
2036
	if ($db_type == 'mysql')
2037
		$db_collation = ' DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci';
2038
	else
2039
		$db_collation = '';
2040
2041
	$endl = $command_line ? "\n" : '<br>' . "\n";
2042
2043
	$lines = file($filename);
2044
2045
	$current_type = 'sql';
2046
	$current_data = '';
2047
	$substep = 0;
2048
	$last_step = '';
2049
2050
	// Make sure all newly created tables will have the proper characters set; this approach is used throughout upgrade_2-1_mysql.php
2051
	$lines = str_replace(') ENGINE=MyISAM;', ') ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;', $lines);
2052
2053
	// Count the total number of steps within this file - for progress.
2054
	$file_steps = substr_count(implode('', $lines), '---#');
2055
	$upcontext['total_items'] = substr_count(implode('', $lines), '--- ');
2056
	$upcontext['debug_items'] = $file_steps;
2057
	$upcontext['current_item_num'] = 0;
2058
	$upcontext['current_item_name'] = '';
2059
	$upcontext['current_debug_item_num'] = 0;
2060
	$upcontext['current_debug_item_name'] = '';
2061
	// This array keeps a record of what we've done in case java is dead...
2062
	$upcontext['actioned_items'] = array();
2063
2064
	$done_something = false;
2065
2066
	foreach ($lines as $line_number => $line)
2067
	{
2068
		$do_current = $substep >= $_GET['substep'];
2069
2070
		// Get rid of any comments in the beginning of the line...
2071
		if (substr(trim($line), 0, 2) === '/*')
2072
			$line = preg_replace('~/\*.+?\*/~', '', $line);
2073
2074
		// Always flush.  Flush, flush, flush.  Flush, flush, flush, flush!  FLUSH!
2075
		if ($is_debug && !$support_js && $command_line)
2076
			flush();
2077
2078
		if (trim($line) === '')
2079
			continue;
2080
2081
		if (trim(substr($line, 0, 3)) === '---')
2082
		{
2083
			$type = substr($line, 3, 1);
2084
2085
			// An error??
2086
			if (trim($current_data) != '' && $type !== '}')
2087
			{
2088
				$upcontext['error_message'] = 'Error in upgrade script - line ' . $line_number . '!' . $endl;
2089
				if ($command_line)
2090
					echo $upcontext['error_message'];
2091
			}
2092
2093
			if ($type == ' ')
2094
			{
2095
				if (!$support_js && $do_current && $_GET['substep'] != 0 && $command_line)
2096
				{
2097
					echo ' Successful.', $endl;
2098
					flush();
2099
				}
2100
2101
				$last_step = htmlspecialchars(rtrim(substr($line, 4)));
2102
				$upcontext['current_item_num']++;
2103
				$upcontext['current_item_name'] = $last_step;
2104
2105
				if ($do_current)
2106
				{
2107
					$upcontext['actioned_items'][] = $last_step;
2108
					if ($command_line)
2109
						echo ' * ';
2110
2111
					// Starting a new main step in our DB changes, so it's time to reset this.
2112
					$upcontext['skip_db_substeps'] = false;
2113
				}
2114
			}
2115
			elseif ($type == '#')
2116
			{
2117
				$upcontext['step_progress'] += (100 / $upcontext['file_count']) / $file_steps;
2118
2119
				$upcontext['current_debug_item_num']++;
2120
				if (trim($line) != '---#')
2121
					$upcontext['current_debug_item_name'] = htmlspecialchars(rtrim(substr($line, 4)));
2122
2123
				// Have we already done something?
2124
				if (isset($_GET['xml']) && $done_something)
2125
				{
2126
					restore_error_handler();
2127
					return $upcontext['current_debug_item_num'] >= $upcontext['debug_items'] ? true : false;
2128
				}
2129
2130
				if ($do_current)
2131
				{
2132
					if (trim($line) == '---#' && $command_line)
2133
						echo ' done.', $endl;
2134
					elseif ($command_line)
2135
						echo ' +++ ', rtrim(substr($line, 4));
2136
					elseif (trim($line) != '---#')
2137
					{
2138
						if ($is_debug)
2139
							$upcontext['actioned_items'][] = $upcontext['current_debug_item_name'];
2140
					}
2141
				}
2142
2143
				if ($substep < $_GET['substep'] && $substep + 1 >= $_GET['substep'])
2144
				{
2145
					if ($command_line)
2146
						echo ' * ';
2147
					else
2148
						$upcontext['actioned_items'][] = $last_step;
2149
				}
2150
2151
				// Small step - only if we're actually doing stuff.
2152
				if ($do_current)
2153
					nextSubstep(++$substep);
2154
				else
2155
					$substep++;
2156
			}
2157
			elseif ($type == '{')
2158
				$current_type = 'code';
2159
			elseif ($type == '}')
2160
			{
2161
				$current_type = 'sql';
2162
2163
				if (!$do_current || !empty($upcontext['skip_db_substeps']))
2164
				{
2165
					$current_data = '';
2166
2167
					// Avoid confusion when skipping something we normally would have done
2168
					if ($do_current)
2169
						$done_something = true;
2170
2171
					continue;
2172
				}
2173
2174
				// @todo Update this to a try/catch for PHP 7+, because eval() now throws an exception for parse errors instead of returning false
2175
				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...
2176
				{
2177
					$upcontext['error_message'] = 'Error in upgrade script ' . basename($filename) . ' on line ' . $line_number . '!' . $endl;
2178
					if ($command_line)
2179
						echo $upcontext['error_message'];
2180
				}
2181
2182
				// Done with code!
2183
				$current_data = '';
2184
				$done_something = true;
2185
			}
2186
2187
			continue;
2188
		}
2189
2190
		$current_data .= $line;
2191
		if (substr(rtrim($current_data), -1) === ';' && $current_type === 'sql')
2192
		{
2193
			if ((!$support_js || isset($_GET['xml'])))
2194
			{
2195
				if (!$do_current || !empty($upcontext['skip_db_substeps']))
2196
				{
2197
					$current_data = '';
2198
2199
					if ($do_current)
2200
						$done_something = true;
2201
2202
					continue;
2203
				}
2204
2205
				// {$sboarddir} is deprecated, but blah blah backward compatibility blah...
2206
				$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));
2207
2208
				upgrade_query($current_data);
2209
2210
				// @todo This will be how it kinda does it once mysql all stripped out - needed for postgre (etc).
2211
				/*
2212
				$result = $smcFunc['db_query']('', $current_data, false, false);
2213
				// Went wrong?
2214
				if (!$result)
2215
				{
2216
					// Bit of a bodge - do we want the error?
2217
					if (!empty($upcontext['return_error']))
2218
					{
2219
						$upcontext['error_message'] = $smcFunc['db_error']($db_connection);
2220
						return false;
2221
					}
2222
				}*/
2223
				$done_something = true;
2224
			}
2225
			$current_data = '';
2226
		}
2227
		// If this is xml based and we're just getting the item name then that's grand.
2228
		elseif ($support_js && !isset($_GET['xml']) && $upcontext['current_debug_item_name'] != '' && $do_current)
2229
		{
2230
			restore_error_handler();
2231
			return false;
2232
		}
2233
2234
		// Clean up by cleaning any step info.
2235
		$step_progress = array();
2236
		$custom_warning = '';
2237
	}
2238
2239
	// Put back the error handler.
2240
	restore_error_handler();
2241
2242
	if ($command_line)
2243
	{
2244
		echo ' Successful.' . "\n";
2245
		flush();
2246
	}
2247
2248
	$_GET['substep'] = 0;
2249
	return true;
2250
}
2251
2252
function upgrade_query($string, $unbuffered = false)
2253
{
2254
	global $db_connection, $db_server, $db_user, $db_passwd, $db_type;
2255
	global $command_line, $upcontext, $upgradeurl, $modSettings;
2256
	global $db_name, $db_unbuffered, $smcFunc, $txt;
2257
2258
	// Get the query result - working around some SMF specific security - just this once!
2259
	$modSettings['disableQueryCheck'] = true;
2260
	$db_unbuffered = $unbuffered;
2261
	$ignore_insert_error = false;
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);
0 ignored issues
show
Bug introduced by
The call to mysqli_connect() has too few arguments starting with database. ( Ignorable by Annotation )

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

2302
			$db_connection = /** @scrutinizer ignore-call */ mysqli_connect($db_server, $db_user, $db_passwd);

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
2303
			mysqli_select_db($db_connection, $db_name);
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
// The next substep.
2563
function nextSubstep($substep)
2564
{
2565
	global $start_time, $timeLimitThreshold, $command_line, $custom_warning;
2566
	global $step_progress, $is_debug, $upcontext;
2567
2568
	if ($_GET['substep'] < $substep)
2569
		$_GET['substep'] = $substep;
2570
2571
	if ($command_line)
2572
	{
2573
		if (time() - $start_time > 1 && empty($is_debug))
2574
		{
2575
			echo '.';
2576
			$start_time = time();
2577
		}
2578
		return;
2579
	}
2580
2581
	@set_time_limit(300);
2582
	if (function_exists('apache_reset_timeout'))
2583
		@apache_reset_timeout();
2584
2585
	if (time() - $start_time <= $timeLimitThreshold)
2586
		return;
2587
2588
	// Do we have some custom step progress stuff?
2589
	if (!empty($step_progress))
2590
	{
2591
		$upcontext['substep_progress'] = 0;
2592
		$upcontext['substep_progress_name'] = $step_progress['name'];
2593
		if ($step_progress['current'] > $step_progress['total'])
2594
			$upcontext['substep_progress'] = 99.9;
2595
		else
2596
			$upcontext['substep_progress'] = ($step_progress['current'] / $step_progress['total']) * 100;
2597
2598
		// Make it nicely rounded.
2599
		$upcontext['substep_progress'] = round($upcontext['substep_progress'], 1);
2600
	}
2601
2602
	// If this is XML we just exit right away!
2603
	if (isset($_GET['xml']))
2604
		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...
2605
2606
	// We're going to pause after this!
2607
	$upcontext['pause'] = true;
2608
2609
	$upcontext['query_string'] = '';
2610
	foreach ($_GET as $k => $v)
2611
	{
2612
		if ($k != 'data' && $k != 'substep' && $k != 'step')
2613
			$upcontext['query_string'] .= ';' . $k . '=' . $v;
2614
	}
2615
2616
	// Custom warning?
2617
	if (!empty($custom_warning))
2618
		$upcontext['custom_warning'] = $custom_warning;
2619
2620
	upgradeExit();
2621
}
2622
2623
function cmdStep0()
2624
{
2625
	global $boarddir, $sourcedir, $modSettings, $start_time, $cachedir, $databases, $db_type, $smcFunc, $upcontext;
2626
	global $is_debug, $boardurl, $txt;
2627
	$start_time = time();
2628
2629
	while (ob_get_level() > 0)
2630
		ob_end_clean();
2631
	ob_implicit_flush(1);
2632
	@set_time_limit(600);
2633
2634
	if (!isset($_SERVER['argv']))
2635
		$_SERVER['argv'] = array();
2636
	$_GET['maint'] = 1;
2637
2638
	foreach ($_SERVER['argv'] as $i => $arg)
2639
	{
2640
		if (preg_match('~^--language=(.+)$~', $arg, $match) != 0)
2641
			$upcontext['lang'] = $match[1];
2642
		elseif (preg_match('~^--path=(.+)$~', $arg) != 0)
2643
			continue;
2644
		elseif ($arg == '--no-maintenance')
2645
			$_GET['maint'] = 0;
2646
		elseif ($arg == '--debug')
2647
			$is_debug = true;
2648
		elseif ($arg == '--backup')
2649
			$_POST['backup'] = 1;
2650
		elseif ($arg == '--rebuild-settings')
2651
			$_POST['migrateSettings'] = 1;
2652
		elseif ($arg == '--allow-stats')
2653
			$_POST['stats'] = 1;
2654
		elseif ($arg == '--template' && (file_exists($boarddir . '/template.php') || file_exists($boarddir . '/template.html') && !file_exists($modSettings['theme_dir'] . '/converted')))
2655
			$_GET['conv'] = 1;
2656
		elseif ($i != 0)
2657
		{
2658
			echo 'SMF Command-line Upgrader
2659
Usage: /path/to/php -f ' . basename(__FILE__) . ' -- [OPTION]...
2660
2661
	--path=/path/to/SMF     Specify custom SMF root directory.
2662
	--language=LANG         Reset the forum\'s language to LANG.
2663
	--no-maintenance        Don\'t put the forum into maintenance mode.
2664
	--debug                 Output debugging information.
2665
	--backup                Create backups of tables with "backup_" prefix.
2666
	--allow-stats           Allow Simple Machines stat collection
2667
	--rebuild-settings      Rebuild the Settings.php file';
2668
			echo "\n";
2669
			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...
2670
		}
2671
	}
2672
2673
	if (!php_version_check())
2674
		print_error('Error: PHP ' . PHP_VERSION . ' does not match version requirements.', true);
0 ignored issues
show
Bug introduced by
The function print_error was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

2674
		/** @scrutinizer ignore-call */ 
2675
  print_error('Error: PHP ' . PHP_VERSION . ' does not match version requirements.', true);
Loading history...
2675
	if (!db_version_check())
2676
		print_error('Error: ' . $databases[$db_type]['name'] . ' ' . $databases[$db_type]['version'] . ' does not match minimum requirements.', true);
2677
2678
	// Do some checks to make sure they have proper privileges
2679
	db_extend('packages');
2680
2681
	// CREATE
2682
	$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');
2683
2684
	// ALTER
2685
	$alter = $smcFunc['db_add_column']('{db_prefix}priv_check', array('name' => 'txt', 'type' => 'varchar', 'size' => 4, 'null' => false, 'default' => ''));
2686
2687
	// DROP
2688
	$drop = $smcFunc['db_drop_table']('{db_prefix}priv_check');
2689
2690
	// Sorry... we need CREATE, ALTER and DROP
2691
	if (!$create || !$alter || !$drop)
2692
		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);
2693
2694
	$check = @file_exists($modSettings['theme_dir'] . '/index.template.php')
2695
		&& @file_exists($sourcedir . '/QueryString.php')
2696
		&& @file_exists($sourcedir . '/ManageBoards.php');
2697
	if (!$check && !isset($modSettings['smfVersion']))
2698
		print_error('Error: Some files are missing or out-of-date.', true);
2699
2700
	// Do a quick version spot check.
2701
	$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

2701
	$temp = substr(@implode('', /** @scrutinizer ignore-type */ @file($boarddir . '/index.php')), 0, 4096);
Loading history...
2702
	preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $temp, $match);
2703
	if (empty($match[1]) || (trim($match[1]) != SMF_VERSION))
2704
		print_error('Error: Some files have not yet been updated properly.');
2705
2706
	// Make sure Settings.php is writable.
2707
	quickFileWritable($boarddir . '/Settings.php');
0 ignored issues
show
Bug introduced by
The function quickFileWritable was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

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