Passed
Push — release-2.1 ( 450280...54b854 )
by Mathias
07:46 queued 12s
created

MySQLConvertOldIp()   C

Complexity

Conditions 12
Paths 86

Size

Total Lines 120
Code Lines 67

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 12
eloc 67
c 0
b 0
f 0
nc 86
nop 5
dl 0
loc 120
rs 6.2933

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 2022 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', '2022');
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
117
// Secure some resources
118
@set_time_limit(600);
119
@ini_set('mysql.connect_timeout', -1);
120
@ini_set('default_socket_timeout', 900);
121
@ini_set('memory_limit', '512M');
122
123
// Clean the upgrade path if this is from the client.
124
if (!empty($_SERVER['argv']) && php_sapi_name() == 'cli' && empty($_SERVER['REMOTE_ADDR']))
125
	for ($i = 1; $i < $_SERVER['argc']; $i++)
126
	{
127
		// Provide the help without possible errors if the enviornment isn't sane.
128
		if (in_array($_SERVER['argv'][$i], array('-h', '--help')))
129
		{
130
			cmdStep0();
131
			exit;
132
		}
133
134
		if (preg_match('~^--path=(.+)$~', $_SERVER['argv'][$i], $match) != 0)
135
			$upgrade_path = realpath(substr($match[1], -1) == '/' ? substr($match[1], 0, -1) : $match[1]);
136
137
		// Cases where we do php other/upgrade.php --path=./
138
		if ($upgrade_path == './' && isset($_SERVER['PWD']))
139
			$upgrade_path = realpath($_SERVER['PWD']);
140
		// Cases where we do php upgrade.php --path=../
141
		elseif ($upgrade_path == '../' && isset($_SERVER['PWD']))
142
			$upgrade_path = dirname(realpath($_SERVER['PWD']));
143
	}
144
145
// Are we from the client?
146
if (php_sapi_name() == 'cli' && empty($_SERVER['REMOTE_ADDR']))
147
{
148
	$command_line = true;
149
	$disable_security = true;
150
}
151
else
152
	$command_line = false;
153
154
// We can't do anything without these files.
155
foreach (array(
156
	dirname(__FILE__) . '/upgrade-helper.php',
157
	$upgrade_path . '/Settings.php'
158
) as $required_file)
159
{
160
	if (!file_exists($required_file))
161
		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.');
162
163
	require_once($required_file);
164
}
165
166
// We don't use "-utf8" anymore...  Tweak the entry that may have been loaded by Settings.php
167
if (isset($language))
168
	$language = str_ireplace('-utf8', '', basename($language, '.lng'));
169
170
// Figure out a valid language request (if any)
171
// 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.
172
if (isset($_SERVER['QUERY_STRING']) && preg_match('~\blang=(\w+)~', $_SERVER['QUERY_STRING'], $matches))
173
	$upcontext['lang'] = $matches[1];
174
175
// Are we logged in?
176
if (isset($upgradeData))
177
{
178
	$upcontext['user'] = json_decode(base64_decode($upgradeData), true);
179
180
	// Check for sensible values.
181
	if (empty($upcontext['user']['started']) || $upcontext['user']['started'] < time() - 86400)
182
		$upcontext['user']['started'] = time();
183
	if (empty($upcontext['user']['updated']) || $upcontext['user']['updated'] < time() - 86400)
184
		$upcontext['user']['updated'] = 0;
185
186
	$upcontext['started'] = $upcontext['user']['started'];
187
	$upcontext['updated'] = $upcontext['user']['updated'];
188
189
	$is_debug = !empty($upcontext['user']['debug']) ? true : false;
190
191
	$upcontext['skip_db_substeps'] = !empty($upcontext['user']['skip_db_substeps']);
192
}
193
194
// Nothing sensible?
195
if (empty($upcontext['updated']))
196
{
197
	$upcontext['started'] = time();
198
	$upcontext['updated'] = 0;
199
	$upcontext['skip_db_substeps'] = false;
200
	$upcontext['user'] = array(
201
		'id' => 0,
202
		'name' => 'Guest',
203
		'pass' => 0,
204
		'started' => $upcontext['started'],
205
		'updated' => $upcontext['updated'],
206
	);
207
}
208
209
// Try to load the language file... or at least define a few necessary strings for now.
210
load_lang_file();
211
212
// Load up some essential data...
213
loadEssentialData();
214
215
// Are we going to be mimic'ing SSI at this point?
216
if (isset($_GET['ssi']))
217
{
218
	require_once($sourcedir . '/Errors.php');
219
	require_once($sourcedir . '/Logging.php');
220
	require_once($sourcedir . '/Load.php');
221
	require_once($sourcedir . '/Security.php');
222
	require_once($sourcedir . '/Subs-Package.php');
223
224
	// SMF isn't started up properly, but loadUserSettings calls our cookies.
225
	if (!isset($smcFunc['json_encode']))
226
	{
227
		$smcFunc['json_encode'] = 'json_encode';
228
		$smcFunc['json_decode'] = 'smf_json_decode';
229
	}
230
231
	loadUserSettings();
232
	loadPermissions();
233
	reloadSettings();
234
}
235
236
// Include our helper functions.
237
require_once($sourcedir . '/Subs.php');
238
require_once($sourcedir . '/LogInOut.php');
239
require_once($sourcedir . '/Subs-Editor.php');
240
241
// Don't do security check if on Yabbse
242
if (!isset($modSettings['smfVersion']))
243
	$disable_security = true;
244
245
// This only exists if we're on SMF ;)
246
if (isset($modSettings['smfVersion']))
247
{
248
	$request = $smcFunc['db_query']('', '
249
		SELECT variable, value
250
		FROM {db_prefix}themes
251
		WHERE id_theme = {int:id_theme}
252
			AND variable IN ({string:theme_url}, {string:theme_dir}, {string:images_url})',
253
		array(
254
			'id_theme' => 1,
255
			'theme_url' => 'theme_url',
256
			'theme_dir' => 'theme_dir',
257
			'images_url' => 'images_url',
258
			'db_error_skip' => true,
259
		)
260
	);
261
	while ($row = $smcFunc['db_fetch_assoc']($request))
262
		$modSettings[$row['variable']] = $row['value'];
263
	$smcFunc['db_free_result']($request);
264
}
265
266
if (!isset($modSettings['theme_url']))
267
{
268
	$modSettings['theme_dir'] = $boarddir . '/Themes/default';
269
	$modSettings['theme_url'] = 'Themes/default';
270
	$modSettings['images_url'] = 'Themes/default/images';
271
}
272
if (!isset($settings['default_theme_url']))
273
	$settings['default_theme_url'] = $modSettings['theme_url'];
274
if (!isset($settings['default_theme_dir']))
275
	$settings['default_theme_dir'] = $modSettings['theme_dir'];
276
277
// Old DBs won't have this
278
if (!isset($modSettings['rand_seed']))
279
{
280
	if (!function_exists('cache_put_data'))
281
		require_once($sourcedir . '/Load.php');
282
	smf_seed_generator();
283
}
284
285
// This is needed in case someone invokes the upgrader using https when upgrading an http forum
286
if (httpsOn())
287
	$settings['default_theme_url'] = strtr($settings['default_theme_url'], array('http://' => 'https://'));
288
289
$upcontext['is_large_forum'] = (empty($modSettings['smfVersion']) || $modSettings['smfVersion'] <= '1.1 RC1') && !empty($modSettings['totalMessages']) && $modSettings['totalMessages'] > 75000;
290
291
// Have we got tracking data - if so use it (It will be clean!)
292
if (isset($_GET['data']))
293
{
294
	global $is_debug;
295
296
	$upcontext['upgrade_status'] = json_decode(base64_decode($_GET['data']), true);
297
	$upcontext['current_step'] = $upcontext['upgrade_status']['curstep'];
298
	$upcontext['language'] = $upcontext['upgrade_status']['lang'];
299
	$upcontext['rid'] = $upcontext['upgrade_status']['rid'];
300
	$support_js = $upcontext['upgrade_status']['js'];
301
302
	// Only set this if the upgrader status says so.
303
	if (empty($is_debug))
304
		$is_debug = $upcontext['upgrade_status']['debug'];
305
}
306
// Set the defaults.
307
else
308
{
309
	$upcontext['current_step'] = 0;
310
	$upcontext['rid'] = mt_rand(0, 5000);
311
	$upcontext['upgrade_status'] = array(
312
		'curstep' => 0,
313
		'lang' => isset($upcontext['lang']) ? $upcontext['lang'] : basename($language, '.lng'),
314
		'rid' => $upcontext['rid'],
315
		'pass' => 0,
316
		'debug' => 0,
317
		'js' => 0,
318
	);
319
	$upcontext['language'] = $upcontext['upgrade_status']['lang'];
320
}
321
322
// Now that we have the necessary info, make sure we loaded the right language file.
323
load_lang_file();
324
325
// Default title...
326
$upcontext['page_title'] = $txt['updating_smf_installation'];
327
328
// If this isn't the first stage see whether they are logging in and resuming.
329
if ($upcontext['current_step'] != 0 || !empty($upcontext['user']['step']))
330
	checkLogin();
331
332
if ($command_line)
333
	cmdStep0();
334
335
// Don't error if we're using xml.
336
if (isset($_GET['xml']))
337
	$upcontext['return_error'] = true;
338
339
// Loop through all the steps doing each one as required.
340
$upcontext['overall_percent'] = 0;
341
foreach ($upcontext['steps'] as $num => $step)
342
{
343
	if ($num >= $upcontext['current_step'])
344
	{
345
		// The current weight of this step in terms of overall progress.
346
		$upcontext['step_weight'] = $step[3];
347
		// Make sure we reset the skip button.
348
		$upcontext['skip'] = false;
349
350
		// We cannot proceed if we're not logged in.
351
		if ($num != 0 && !$disable_security && $upcontext['user']['pass'] != $upcontext['upgrade_status']['pass'])
352
		{
353
			$upcontext['steps'][0][2]();
354
			break;
355
		}
356
357
		// Call the step and if it returns false that means pause!
358
		if (function_exists($step[2]) && $step[2]() === false)
359
			break;
360
		elseif (function_exists($step[2]))
361
		{
362
			//Start each new step with this unset, so the 'normal' template is called first
363
			unset($_GET['xml']);
364
			//Clear out warnings at the start of each step
365
			unset($upcontext['custom_warning']);
366
			$_GET['substep'] = 0;
367
			$upcontext['current_step']++;
368
		}
369
	}
370
	$upcontext['overall_percent'] += $step[3];
371
}
372
373
upgradeExit();
374
375
// Exit the upgrade script.
376
function upgradeExit($fallThrough = false)
377
{
378
	global $upcontext, $upgradeurl, $sourcedir, $command_line, $is_debug, $txt;
379
380
	// Save where we are...
381
	if (!empty($upcontext['current_step']) && !empty($upcontext['user']['id']))
382
	{
383
		$upcontext['user']['step'] = $upcontext['current_step'];
384
		$upcontext['user']['substep'] = $_GET['substep'];
385
		$upcontext['user']['updated'] = time();
386
		$upcontext['user']['skip_db_substeps'] = !empty($upcontext['skip_db_substeps']);
387
		$upcontext['debug'] = $is_debug;
388
		$upgradeData = base64_encode(json_encode($upcontext['user']));
389
		require_once($sourcedir . '/Subs.php');
390
		require_once($sourcedir . '/Subs-Admin.php');
391
		updateSettingsFile(array('upgradeData' => $upgradeData));
392
		updateDbLastError(0);
393
	}
394
395
	// Handle the progress of the step, if any.
396
	if (!empty($upcontext['step_progress']) && isset($upcontext['steps'][$upcontext['current_step']]))
397
	{
398
		$upcontext['step_progress'] = round($upcontext['step_progress'], 1);
399
		$upcontext['overall_percent'] += $upcontext['step_progress'] * ($upcontext['steps'][$upcontext['current_step']][3] / 100);
400
	}
401
	$upcontext['overall_percent'] = (int) $upcontext['overall_percent'];
402
403
	// We usually dump our templates out.
404
	if (!$fallThrough)
405
	{
406
		// This should not happen my dear... HELP ME DEVELOPERS!!
407
		if (!empty($command_line))
408
		{
409
			if (function_exists('debug_print_backtrace'))
410
				debug_print_backtrace();
411
412
			printf("\n" . $txt['error_unexpected_template_call'], isset($upcontext['sub_template']) ? $upcontext['sub_template'] : '');
413
			flush();
414
			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...
415
		}
416
417
		if (!isset($_GET['xml']))
418
			template_upgrade_above();
419
		else
420
		{
421
			header('content-type: text/xml; charset=UTF-8');
422
			// Sadly we need to retain the $_GET data thanks to the old upgrade scripts.
423
			$upcontext['get_data'] = array();
424
			foreach ($_GET as $k => $v)
425
			{
426
				if (substr($k, 0, 3) != 'amp' && !in_array($k, array('xml', 'substep', 'lang', 'data', 'step', 'filecount')))
427
				{
428
					$upcontext['get_data'][$k] = $v;
429
				}
430
			}
431
			template_xml_above();
432
		}
433
434
		// Call the template.
435
		if (isset($upcontext['sub_template']))
436
		{
437
			$upcontext['upgrade_status']['curstep'] = $upcontext['current_step'];
438
			$upcontext['form_url'] = $upgradeurl . '?step=' . $upcontext['current_step'] . '&amp;substep=' . $_GET['substep'] . '&amp;data=' . base64_encode(json_encode($upcontext['upgrade_status']));
439
440
			// Custom stuff to pass back?
441
			if (!empty($upcontext['query_string']))
442
				$upcontext['form_url'] .= $upcontext['query_string'];
443
444
			// Call the appropriate subtemplate
445
			if (is_callable('template_' . $upcontext['sub_template']))
446
				call_user_func('template_' . $upcontext['sub_template']);
447
			else
448
				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...
449
		}
450
451
		// Was there an error?
452
		if (!empty($upcontext['forced_error_message']))
453
			echo $upcontext['forced_error_message'];
454
455
		// Show the footer.
456
		if (!isset($_GET['xml']))
457
			template_upgrade_below();
458
		else
459
			template_xml_below();
460
	}
461
462
	// Show the upgrade time for CLI when we are completely done, if in debug mode.
463
	if (!empty($command_line) && $is_debug)
464
	{
465
		$active = time() - $upcontext['started'];
466
		$hours = floor($active / 3600);
467
		$minutes = intval(($active / 60) % 60);
468
		$seconds = intval($active % 60);
469
470
		if ($hours > 0)
471
			echo "\n" . '', sprintf($txt['upgrade_completed_time_hms'], $hours, $minutes, $seconds), '' . "\n";
472
		elseif ($minutes > 0)
473
			echo "\n" . '', sprintf($txt['upgrade_completed_time_ms'], $minutes, $seconds), '' . "\n";
474
		elseif ($seconds > 0)
475
			echo "\n" . '', sprintf($txt['upgrade_completed_time_s'], $seconds), '' . "\n";
476
	}
477
478
	// Bang - gone!
479
	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...
480
}
481
482
// Load the list of language files, and the current language file.
483
function load_lang_file()
484
{
485
	global $txt, $upcontext, $language, $modSettings, $upgrade_path, $command_line;
486
487
	static $lang_dir = '', $detected_languages = array(), $loaded_langfile = '';
488
489
	// Do we know where to look for the language files, or shall we just guess for now?
490
	$temp = isset($modSettings['theme_dir']) ? $modSettings['theme_dir'] . '/languages' : $upgrade_path . '/Themes/default/languages';
491
492
	if ($lang_dir != $temp)
493
	{
494
		$lang_dir = $temp;
495
		$detected_languages = array();
496
	}
497
498
	// Override the language file?
499
	if (isset($upcontext['language']))
500
		$_SESSION['upgrader_langfile'] = 'Install.' . $upcontext['language'] . '.php';
501
	elseif (isset($upcontext['lang']))
502
		$_SESSION['upgrader_langfile'] = 'Install.' . $upcontext['lang'] . '.php';
503
	elseif (isset($language))
504
		$_SESSION['upgrader_langfile'] = 'Install.' . $language . '.php';
505
506
	// Avoid pointless repetition
507
	if (isset($_SESSION['upgrader_langfile']) && $loaded_langfile == $lang_dir . '/' . $_SESSION['upgrader_langfile'])
508
		return;
509
510
	// Now try to find the language files
511
	if (empty($detected_languages))
512
	{
513
		// Make sure the languages directory actually exists.
514
		if (file_exists($lang_dir))
515
		{
516
			// Find all the "Install" language files in the directory.
517
			$dir = dir($lang_dir);
518
			while ($entry = $dir->read())
519
			{
520
				// Skip any old '-utf8' language files that might be lying around
521
				if (strpos($entry, '-utf8') !== false)
522
					continue;
523
524
				if (substr($entry, 0, 8) == 'Install.' && substr($entry, -4) == '.php')
525
					$detected_languages[$entry] = ucfirst(substr($entry, 8, strlen($entry) - 12));
526
			}
527
			$dir->close();
528
		}
529
		// Our guess was wrong, but that's fine. We'll try again after $modSettings['theme_dir'] is defined.
530
		elseif (!isset($modSettings['theme_dir']))
531
		{
532
			// Define a few essential strings for now.
533
			$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.';
534
			$txt['error_sourcefile_missing'] = 'Unable to find the Sources/%1$s file. Please make sure it was uploaded properly, and then try again.';
535
536
			$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.';
537
			$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.';
538
539
			return;
540
		}
541
	}
542
543
	// Didn't find any, show an error message!
544
	if (empty($detected_languages))
545
	{
546
		$from = explode('/', $command_line ? $upgrade_path : $_SERVER['PHP_SELF']);
547
		$to = explode('/', $lang_dir);
548
		$relPath = $to;
549
550
		foreach($from as $depth => $dir)
551
		{
552
			if ($dir === $to[$depth])
553
				array_shift($relPath);
554
			else
555
			{
556
				$remaining = count($from) - $depth;
557
				if ($remaining > 1)
558
				{
559
					$padLength = (count($relPath) + $remaining - 1) * -1;
560
					$relPath = array_pad($relPath, $padLength, '..');
561
					break;
562
				}
563
				else
564
					$relPath[0] = './' . $relPath[0];
565
			}
566
		}
567
		$relPath = implode(DIRECTORY_SEPARATOR, $relPath);
568
569
		// Command line?
570
		if ($command_line)
571
		{
572
			echo 'This upgrader was unable to find the upgrader\'s language file or files.  They should be found under:', "\n",
573
				$relPath, "\n",
574
				'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",
575
				'If that doesn\'t help, please make sure this upgrade.php file is in the same place as the Themes folder.', "\n";
576
			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...
577
		}
578
579
		// Let's not cache this message, eh?
580
		header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
581
		header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
582
		header('Cache-Control: no-cache');
583
584
		echo '<!DOCTYPE html>
585
			<html>
586
				<head>
587
					<title>SMF Upgrader: Error!</title>
588
						<style>
589
							body {
590
								font-family: sans-serif;
591
								max-width: 700px; }
592
593
								h1 {
594
									font-size: 14pt; }
595
596
								.directory {
597
									margin: 0.3em;
598
									font-family: monospace;
599
									font-weight: bold; }
600
						</style>
601
				</head>
602
				<body>
603
					<h1>A critical error has occurred.</h1>
604
						<p>This upgrader was unable to find the upgrader\'s language file or files.  They should be found under:</p>
605
						<div class="directory">', $relPath, '</div>
606
						<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>
607
						<p>If that doesn\'t help, please make sure this upgrade.php file is in the same place as the Themes folder.</p>
608
						<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>
609
				</body>
610
			</html>';
611
		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...
612
	}
613
614
	// Make sure it exists. If it doesn't, reset it.
615
	if (!isset($_SESSION['upgrader_langfile']) || preg_match('~[^\w.-]~', $_SESSION['upgrader_langfile']) === 1 || !file_exists($lang_dir . '/' . $_SESSION['upgrader_langfile']))
616
	{
617
		// Use the first one...
618
		list ($_SESSION['upgrader_langfile']) = array_keys($detected_languages);
619
620
		// If we have English and some other language, use the other language.
621
		if ($_SESSION['upgrader_langfile'] == 'Install.english.php' && count($detected_languages) > 1)
622
			list (, $_SESSION['upgrader_langfile']) = array_keys($detected_languages);
623
	}
624
625
	// For backup we load English at first, then the second language will overwrite it.
626
	if ($_SESSION['upgrader_langfile'] != 'Install.english.php')
627
	{
628
		require_once($lang_dir . '/index.english.php');
629
		require_once($lang_dir . '/Install.english.php');
630
	}
631
632
	// And now include the actual language file itself.
633
	require_once($lang_dir . '/' . str_replace('Install.', 'index.', $_SESSION['upgrader_langfile']));
634
	require_once($lang_dir . '/' . $_SESSION['upgrader_langfile']);
635
636
	// Remember what we've done
637
	$loaded_langfile = $lang_dir . '/' . $_SESSION['upgrader_langfile'];
638
}
639
640
// Used to direct the user to another location.
641
function redirectLocation($location, $addForm = true)
642
{
643
	global $upgradeurl, $upcontext, $command_line;
644
645
	// Command line users can't be redirected.
646
	if ($command_line)
647
		upgradeExit(true);
648
649
	// Are we providing the core info?
650
	if ($addForm)
651
	{
652
		$upcontext['upgrade_status']['curstep'] = $upcontext['current_step'];
653
		$location = $upgradeurl . '?step=' . $upcontext['current_step'] . '&substep=' . $_GET['substep'] . '&data=' . base64_encode(json_encode($upcontext['upgrade_status'])) . $location;
654
	}
655
656
	while (@ob_end_clean())
657
		header('location: ' . strtr($location, array('&amp;' => '&')));
658
659
	// Exit - saving status as we go.
660
	upgradeExit(true);
661
}
662
663
// Load all essential data and connect to the DB as this is pre SSI.php
664
function loadEssentialData()
665
{
666
	global $db_server, $db_user, $db_passwd, $db_name, $db_connection;
667
	global $db_prefix, $db_character_set, $db_type, $db_port, $db_show_debug;
668
	global $db_mb4, $modSettings, $sourcedir, $smcFunc, $txt, $utf8;
669
670
	// Report all errors if admin wants them or this is a pre-release version.
671
	if (!empty($db_show_debug) || strspn(SMF_VERSION, '1234567890.') !== strlen(SMF_VERSION))
672
		error_reporting(E_ALL);
673
	// Otherwise, report all errors except for deprecation notices.
674
	else
675
		error_reporting(E_ALL & ~E_DEPRECATED);
676
677
	define('SMF', 1);
678
	header('X-Frame-Options: SAMEORIGIN');
679
	header('X-XSS-Protection: 1');
680
	header('X-Content-Type-Options: nosniff');
681
682
	// Start the session.
683
	if (@ini_get('session.save_handler') == 'user')
684
		@ini_set('session.save_handler', 'files');
685
	@session_start();
686
687
	if (empty($smcFunc))
688
		$smcFunc = array();
689
690
	require_once($sourcedir . '/Subs.php');
691
692
	$smcFunc['random_int'] = function($min = 0, $max = PHP_INT_MAX)
693
	{
694
		global $sourcedir;
695
696
		// Oh, wouldn't it be great if I *was* crazy? Then the world would be okay.
697
		if (!is_callable('random_int'))
698
			require_once($sourcedir . '/random_compat/random.php');
699
700
		return random_int($min, $max);
701
	};
702
703
	// This is now needed for loadUserSettings()
704
	$smcFunc['random_bytes'] = function($bytes)
705
	{
706
		global $sourcedir;
707
708
		if (!is_callable('random_bytes'))
709
			require_once($sourcedir . '/random_compat/random.php');
710
711
		return random_bytes($bytes);
712
	};
713
714
	// We need this for authentication and some upgrade code
715
	require_once($sourcedir . '/Subs-Auth.php');
716
	require_once($sourcedir . '/Class-Package.php');
717
718
	$smcFunc['strtolower'] = 'smf_strtolower';
719
720
	// Initialize everything...
721
	initialize_inputs();
722
723
	$utf8 = (empty($modSettings['global_character_set']) ? $txt['lang_character_set'] : $modSettings['global_character_set']) === 'UTF-8';
724
725
	$smcFunc['normalize'] = function($string, $form = 'c') use ($utf8)
726
	{
727
		global $sourcedir;
728
729
		if (!$utf8)
730
			return $string;
731
732
		require_once($sourcedir . '/Subs-Charset.php');
733
734
		$normalize_func = 'utf8_normalize_' . strtolower((string) $form);
735
736
		if (!function_exists($normalize_func))
737
			return false;
738
739
		return $normalize_func($string);
740
	};
741
742
	// Get the database going!
743
	if (empty($db_type) || $db_type == 'mysqli')
744
	{
745
		$db_type = 'mysql';
746
		// If overriding $db_type, need to set its settings.php entry too
747
		$changes = array();
748
		$changes['db_type'] = 'mysql';
749
		require_once($sourcedir . '/Subs-Admin.php');
750
		updateSettingsFile($changes);
751
	}
752
753
	if (file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php'))
754
	{
755
		require_once($sourcedir . '/Subs-Db-' . $db_type . '.php');
756
757
		// Make the connection...
758
		if (empty($db_connection))
759
		{
760
			$options = array('non_fatal' => true);
761
			// Add in the port if needed
762
			if (!empty($db_port))
763
				$options['port'] = $db_port;
764
765
			if (!empty($db_mb4))
766
				$options['db_mb4'] = $db_mb4;
767
768
			$db_connection = smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, $db_prefix, $options);
769
		}
770
		else
771
			// If we've returned here, ping/reconnect to be safe
772
			$smcFunc['db_ping']($db_connection);
773
774
		// Oh dear god!!
775
		if ($db_connection === null)
776
		{
777
			// Get error info...  Recast just in case we get false or 0...
778
			$error_message = $smcFunc['db_connect_error']();
779
			if (empty($error_message))
780
				$error_message = '';
781
			$error_number = $smcFunc['db_connect_errno']();
782
			if (empty($error_number))
783
				$error_number = '';
784
			$db_error = (!empty($error_number) ? $error_number . ': ' : '') . $error_message;
785
786
			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...
787
		}
788
789
		if ($db_type == 'mysql' && isset($db_character_set) && preg_match('~^\w+$~', $db_character_set) === 1)
790
			$smcFunc['db_query']('', '
791
				SET NAMES {string:db_character_set}',
792
				array(
793
					'db_error_skip' => true,
794
					'db_character_set' => $db_character_set,
795
				)
796
			);
797
798
		// Load the modSettings data...
799
		$request = $smcFunc['db_query']('', '
800
			SELECT variable, value
801
			FROM {db_prefix}settings',
802
			array(
803
				'db_error_skip' => true,
804
			)
805
		);
806
		$modSettings = array();
807
		while ($row = $smcFunc['db_fetch_assoc']($request))
808
			$modSettings[$row['variable']] = $row['value'];
809
		$smcFunc['db_free_result']($request);
810
	}
811
	else
812
		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

812
		return /** @scrutinizer ignore-call */ throw_error(sprintf($txt['error_sourcefile_missing'], 'Subs-Db-' . $db_type . '.php'));
Loading history...
813
814
	// If they don't have the file, they're going to get a warning anyway so we won't need to clean request vars.
815
	if (file_exists($sourcedir . '/QueryString.php') && php_version_check())
816
	{
817
		require_once($sourcedir . '/QueryString.php');
818
		cleanRequest();
819
	}
820
821
	if (!isset($_GET['substep']))
822
		$_GET['substep'] = 0;
823
}
824
825
function initialize_inputs()
826
{
827
	global $start_time, $db_type, $upgrade_path;
828
829
	$start_time = time();
830
831
	umask(0);
832
833
	ob_start();
834
835
	// Better to upgrade cleanly and fall apart than to screw everything up if things take too long.
836
	ignore_user_abort(true);
837
838
	// This is really quite simple; if ?delete is on the URL, delete the upgrader...
839
	if (isset($_GET['delete']))
840
	{
841
		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

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

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

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

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

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

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

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

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

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

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

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

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

2706
	$temp = substr(@implode('', /** @scrutinizer ignore-type */ @file($boarddir . '/index.php')), 0, 4096);
Loading history...
2707
	preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $temp, $match);
2708
	if (empty($match[1]) || (trim($match[1]) != SMF_VERSION))
2709
		print_error('Error: Some files have not yet been updated properly.');
2710
2711
	// Make sure Settings.php is writable.
2712
	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

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