Passed
Push — release-2.1 ( 2dff61...5a5f48 )
by
unknown
10:33 queued 06:18
created

upgrade_query()   D

Complexity

Conditions 27
Paths 106

Size

Total Lines 128
Code Lines 65

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 27
eloc 65
nc 106
nop 2
dl 0
loc 128
rs 4.1166
c 1
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

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

787
		return /** @scrutinizer ignore-call */ throw_error(sprintf($txt['error_sourcefile_missing'], 'Subs-Db-' . $db_type . '.php'));
Loading history...
788
789
	// If they don't have the file, they're going to get a warning anyway so we won't need to clean request vars.
790
	if (file_exists($sourcedir . '/QueryString.php') && php_version_check())
791
	{
792
		require_once($sourcedir . '/QueryString.php');
793
		cleanRequest();
794
	}
795
796
	if (!isset($_GET['substep']))
797
		$_GET['substep'] = 0;
798
}
799
800
function initialize_inputs()
801
{
802
	global $start_time, $db_type, $upgrade_path;
803
804
	$start_time = time();
805
806
	umask(0);
807
808
	ob_start();
809
810
	// Better to upgrade cleanly and fall apart than to screw everything up if things take too long.
811
	ignore_user_abort(true);
812
813
	// This is really quite simple; if ?delete is on the URL, delete the upgrader...
814
	if (isset($_GET['delete']))
815
	{
816
		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

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

898
		return /** @scrutinizer ignore-call */ throw_error($txt['error_upgrade_files_missing']);
Loading history...
899
900
	// Do they meet the install requirements?
901
	if (!php_version_check())
902
		return throw_error($txt['error_php_too_low']);
903
904
	if (!db_version_check())
905
		return throw_error(sprintf($txt['error_db_too_low'], $databases[$db_type]['name']));
906
907
	// Do some checks to make sure they have proper privileges
908
	db_extend('packages');
909
910
	// CREATE
911
	$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');
912
913
	// ALTER
914
	$alter = $smcFunc['db_add_column']('{db_prefix}priv_check', array('name' => 'txt', 'type' => 'varchar', 'size' => 4, 'null' => false, 'default' => ''));
915
916
	// DROP
917
	$drop = $smcFunc['db_drop_table']('{db_prefix}priv_check');
918
919
	// Sorry... we need CREATE, ALTER and DROP
920
	if (!$create || !$alter || !$drop)
921
		return throw_error(sprintf($txt['error_db_privileges'], $databases[$db_type]['name']));
922
923
	// Do a quick version spot check.
924
	$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

924
	$temp = substr(@implode('', /** @scrutinizer ignore-type */ @file($boarddir . '/index.php')), 0, 4096);
Loading history...
925
	preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $temp, $match);
926
	if (empty($match[1]) || (trim($match[1]) != SMF_VERSION))
927
		return throw_error($txt['error_upgrade_old_files']);
928
929
	// What absolutely needs to be writable?
930
	$writable_files = array(
931
		$boarddir . '/Settings.php',
932
		$boarddir . '/Settings_bak.php',
933
	);
934
935
	// Only check for minified writable files if we have it enabled or not set.
936
	if (!empty($modSettings['minimize_files']) || !isset($modSettings['minimize_files']))
937
		$writable_files += array(
938
			$modSettings['theme_dir'] . '/css/minified.css',
939
			$modSettings['theme_dir'] . '/scripts/minified.js',
940
			$modSettings['theme_dir'] . '/scripts/minified_deferred.js',
941
		);
942
943
	// Do we need to add this setting?
944
	$need_settings_update = empty($modSettings['custom_avatar_dir']);
945
946
	$custom_av_dir = !empty($modSettings['custom_avatar_dir']) ? $modSettings['custom_avatar_dir'] : $GLOBALS['boarddir'] . '/custom_avatar';
947
	$custom_av_url = !empty($modSettings['custom_avatar_url']) ? $modSettings['custom_avatar_url'] : $boardurl . '/custom_avatar';
948
949
	// This little fellow has to cooperate...
950
	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

950
	/** @scrutinizer ignore-call */ 
951
 quickFileWritable($custom_av_dir);
Loading history...
951
952
	// Are we good now?
953
	if (!is_writable($custom_av_dir))
954
		return throw_error(sprintf($txt['error_dir_not_writable'], $custom_av_dir));
955
	elseif ($need_settings_update)
956
	{
957
		if (!function_exists('cache_put_data'))
958
			require_once($sourcedir . '/Load.php');
959
960
		updateSettings(array('custom_avatar_dir' => $custom_av_dir));
961
		updateSettings(array('custom_avatar_url' => $custom_av_url));
962
	}
963
964
	require_once($sourcedir . '/Security.php');
965
966
	// Check the cache directory.
967
	$cachedir_temp = empty($cachedir) ? $boarddir . '/cache' : $cachedir;
968
	if (!file_exists($cachedir_temp))
969
		@mkdir($cachedir_temp);
970
971
	if (!file_exists($cachedir_temp))
972
		return throw_error($txt['error_cache_not_found']);
973
974
	quickFileWritable($cachedir_temp . '/db_last_error.php');
975
976
	if (!file_exists($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php'))
977
		return throw_error(sprintf($txt['error_lang_index_missing'], $upcontext['language'], $upgradeurl));
978
	elseif (!isset($_GET['skiplang']))
979
	{
980
		$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php')), 0, 4096);
981
		preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
982
983
		if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
984
			return throw_error(sprintf($txt['error_upgrade_old_lang_files'], $upcontext['language'], $upgradeurl));
985
	}
986
987
	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

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

1157
						return /** @scrutinizer ignore-call */ throw_error($txt['error_not_admin']);
Loading history...
1158
					$smcFunc['db_free_result']($request);
1159
				}
1160
1161
				$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...
1162
				$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...
1163
			}
1164
			else
1165
			{
1166
				$upcontext['user']['id'] = 1;
1167
				$upcontext['user']['name'] = 'Administrator';
1168
			}
1169
1170
			if (!is_callable('random_int'))
1171
				require_once('Sources/random_compat/random.php');
1172
1173
			$upcontext['user']['pass'] = random_int(0, 60000);
1174
			// This basically is used to match the GET variables to Settings.php.
1175
			$upcontext['upgrade_status']['pass'] = $upcontext['user']['pass'];
1176
1177
			// Set the language to that of the user?
1178
			if (isset($user_language) && $user_language != $upcontext['language'] && file_exists($modSettings['theme_dir'] . '/languages/index.' . basename($user_language, '.lng') . '.php'))
1179
			{
1180
				$user_language = basename($user_language, '.lng');
1181
				$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

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

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

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

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

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

2718
		/** @scrutinizer ignore-call */ 
2719
  print_error('Error: PHP ' . PHP_VERSION . ' does not match version requirements.', true);
Loading history...
2719
	if (!db_version_check())
2720
		print_error('Error: ' . $databases[$db_type]['name'] . ' ' . $databases[$db_type]['version'] . ' does not match minimum requirements.', true);
2721
2722
	// Do some checks to make sure they have proper privileges
2723
	db_extend('packages');
2724
2725
	// CREATE
2726
	$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');
2727
2728
	// ALTER
2729
	$alter = $smcFunc['db_add_column']('{db_prefix}priv_check', array('name' => 'txt', 'type' => 'varchar', 'size' => 4, 'null' => false, 'default' => ''));
2730
2731
	// DROP
2732
	$drop = $smcFunc['db_drop_table']('{db_prefix}priv_check');
2733
2734
	// Sorry... we need CREATE, ALTER and DROP
2735
	if (!$create || !$alter || !$drop)
2736
		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);
2737
2738
	$check = @file_exists($modSettings['theme_dir'] . '/index.template.php')
2739
		&& @file_exists($sourcedir . '/QueryString.php')
2740
		&& @file_exists($sourcedir . '/ManageBoards.php');
2741
	if (!$check && !isset($modSettings['smfVersion']))
2742
		print_error('Error: Some files are missing or out-of-date.', true);
2743
2744
	// Do a quick version spot check.
2745
	$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

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

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