Passed
Push — release-2.1 ( 8c6d9c...8593cf )
by John
04:22 queued 10s
created

template_chmod()   C

Complexity

Conditions 17
Paths 19

Size

Total Lines 112
Code Lines 49

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 17
eloc 49
c 0
b 0
f 0
nc 19
nop 0
dl 0
loc 112
rs 5.2166

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

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

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

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

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

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

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

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

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

Loading history...
576
	}
577
578
	// Make sure it exists. If it doesn't, reset it.
579
	if (!isset($_SESSION['upgrader_langfile']) || preg_match('~[^\w.-]~', $_SESSION['upgrader_langfile']) === 1 || !file_exists($lang_dir . '/' . $_SESSION['upgrader_langfile']))
580
	{
581
		// Use the first one...
582
		list ($_SESSION['upgrader_langfile']) = array_keys($detected_languages);
583
584
		// If we have English and some other language, use the other language.
585
		if ($_SESSION['upgrader_langfile'] == 'Install.english.php' && count($detected_languages) > 1)
586
			list (, $_SESSION['upgrader_langfile']) = array_keys($detected_languages);
587
	}
588
589
	// For backup we load English at first, then the second language will overwrite it.
590
	if ($_SESSION['upgrader_langfile'] != 'Install.english.php')
591
		require_once($lang_dir . '/Install.english.php');
592
593
	// And now include the actual language file itself.
594
	require_once($lang_dir . '/' . $_SESSION['upgrader_langfile']);
595
596
	// Remember what we've done
597
	$loaded_langfile = $lang_dir . '/' . $_SESSION['upgrader_langfile'];
598
}
599
600
// Used to direct the user to another location.
601
function redirectLocation($location, $addForm = true)
602
{
603
	global $upgradeurl, $upcontext, $command_line;
604
605
	// Command line users can't be redirected.
606
	if ($command_line)
607
		upgradeExit(true);
608
609
	// Are we providing the core info?
610
	if ($addForm)
611
	{
612
		$upcontext['upgrade_status']['curstep'] = $upcontext['current_step'];
613
		$location = $upgradeurl . '?step=' . $upcontext['current_step'] . '&substep=' . $_GET['substep'] . '&data=' . base64_encode(json_encode($upcontext['upgrade_status'])) . $location;
614
	}
615
616
	while (@ob_end_clean())
617
		header('location: ' . strtr($location, array('&amp;' => '&')));
618
619
	// Exit - saving status as we go.
620
	upgradeExit(true);
621
}
622
623
// Load all essential data and connect to the DB as this is pre SSI.php
624
function loadEssentialData()
625
{
626
	global $db_server, $db_user, $db_passwd, $db_name, $db_connection;
627
	global $db_prefix, $db_character_set, $db_type, $db_port, $db_show_debug;
628
	global $db_mb4, $modSettings, $sourcedir, $smcFunc, $txt, $utf8;
629
630
	// Report all errors if admin wants them or this is a pre-release version.
631
	if (!empty($db_show_debug) || strspn(SMF_VERSION, '1234567890.') !== strlen(SMF_VERSION))
632
		error_reporting(E_ALL);
633
	// Otherwise, report all errors except for deprecation notices.
634
	else
635
		error_reporting(E_ALL & ~E_DEPRECATED);
636
637
638
	define('SMF', 1);
639
	header('X-Frame-Options: SAMEORIGIN');
640
	header('X-XSS-Protection: 1');
641
	header('X-Content-Type-Options: nosniff');
642
643
	// Start the session.
644
	if (@ini_get('session.save_handler') == 'user')
645
		@ini_set('session.save_handler', 'files');
646
	@session_start();
647
648
	if (empty($smcFunc))
649
		$smcFunc = array();
650
651
	require_once($sourcedir . '/Subs.php');
652
653
	$smcFunc['random_int'] = function($min = 0, $max = PHP_INT_MAX)
654
	{
655
		global $sourcedir;
656
657
		// Oh, wouldn't it be great if I *was* crazy? Then the world would be okay.
658
		if (!is_callable('random_int'))
659
			require_once($sourcedir . '/random_compat/random.php');
660
661
		return random_int($min, $max);
662
	};
663
664
	// This is now needed for loadUserSettings()
665
	$smcFunc['random_bytes'] = function($bytes)
666
	{
667
		global $sourcedir;
668
669
		if (!is_callable('random_bytes'))
670
			require_once($sourcedir . '/random_compat/random.php');
671
672
		return random_bytes($bytes);
673
	};
674
675
	// We need this for authentication and some upgrade code
676
	require_once($sourcedir . '/Subs-Auth.php');
677
	require_once($sourcedir . '/Class-Package.php');
678
679
	$smcFunc['strtolower'] = 'smf_strtolower';
680
681
	// Initialize everything...
682
	initialize_inputs();
683
684
	$utf8 = (empty($modSettings['global_character_set']) ? $txt['lang_character_set'] : $modSettings['global_character_set']) === 'UTF-8';
685
686
	// Get the database going!
687
	if (empty($db_type) || $db_type == 'mysqli')
688
	{
689
		$db_type = 'mysql';
690
		// If overriding $db_type, need to set its settings.php entry too
691
		$changes = array();
692
		$changes['db_type'] = 'mysql';
693
		require_once($sourcedir . '/Subs-Admin.php');
694
		updateSettingsFile($changes);
695
	}
696
697
	if (file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php'))
698
	{
699
		require_once($sourcedir . '/Subs-Db-' . $db_type . '.php');
700
701
		// Make the connection...
702
		if (empty($db_connection))
703
		{
704
			$options = array('non_fatal' => true);
705
			// Add in the port if needed
706
			if (!empty($db_port))
707
				$options['port'] = $db_port;
708
709
			if (!empty($db_mb4))
710
				$options['db_mb4'] = $db_mb4;
711
712
			$db_connection = smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, $db_prefix, $options);
713
		}
714
		else
715
			// If we've returned here, ping/reconnect to be safe
716
			$smcFunc['db_ping']($db_connection);
717
718
		// Oh dear god!!
719
		if ($db_connection === null)
720
		{
721
			// Get error info...  Recast just in case we get false or 0...
722
			$error_message = $smcFunc['db_connect_error']();
723
			if (empty($error_message))
724
				$error_message = '';
725
			$error_number = $smcFunc['db_connect_errno']();
726
			if (empty($error_number))
727
				$error_number = '';
728
			$db_error = (!empty($error_number) ? $error_number . ': ' : '') . $error_message;
729
730
			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...
731
		}
732
733
		if ($db_type == 'mysql' && isset($db_character_set) && preg_match('~^\w+$~', $db_character_set) === 1)
734
			$smcFunc['db_query']('', '
735
				SET NAMES {string:db_character_set}',
736
				array(
737
					'db_error_skip' => true,
738
					'db_character_set' => $db_character_set,
739
				)
740
			);
741
742
		// Load the modSettings data...
743
		$request = $smcFunc['db_query']('', '
744
			SELECT variable, value
745
			FROM {db_prefix}settings',
746
			array(
747
				'db_error_skip' => true,
748
			)
749
		);
750
		$modSettings = array();
751
		while ($row = $smcFunc['db_fetch_assoc']($request))
752
			$modSettings[$row['variable']] = $row['value'];
753
		$smcFunc['db_free_result']($request);
754
	}
755
	else
756
		return throw_error(sprintf($txt['error_sourcefile_missing'], 'Subs-Db-' . $db_type . '.php'));
757
758
	// If they don't have the file, they're going to get a warning anyway so we won't need to clean request vars.
759
	if (file_exists($sourcedir . '/QueryString.php') && php_version_check())
760
	{
761
		require_once($sourcedir . '/QueryString.php');
762
		cleanRequest();
763
	}
764
765
	if (!isset($_GET['substep']))
766
		$_GET['substep'] = 0;
767
}
768
769
function initialize_inputs()
770
{
771
	global $start_time, $db_type;
772
773
	$start_time = time();
774
775
	umask(0);
776
777
	ob_start();
778
779
	// Better to upgrade cleanly and fall apart than to screw everything up if things take too long.
780
	ignore_user_abort(true);
781
782
	// This is really quite simple; if ?delete is on the URL, delete the upgrader...
783
	if (isset($_GET['delete']))
784
	{
785
		@unlink(__FILE__);
786
787
		// And the extra little files ;).
788
		@unlink(dirname(__FILE__) . '/upgrade_1-0.sql');
789
		@unlink(dirname(__FILE__) . '/upgrade_1-1.sql');
790
		@unlink(dirname(__FILE__) . '/upgrade_2-0_' . $db_type . '.sql');
791
		@unlink(dirname(__FILE__) . '/upgrade_2-1_' . $db_type . '.sql');
792
		@unlink(dirname(__FILE__) . '/upgrade-helper.php');
793
794
		$dh = opendir(dirname(__FILE__));
795
		while ($file = readdir($dh))
0 ignored issues
show
Bug introduced by
It seems like $dh can also be of type false; however, parameter $dir_handle of readdir() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

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

795
		while ($file = readdir(/** @scrutinizer ignore-type */ $dh))
Loading history...
796
		{
797
			if (preg_match('~upgrade_\d-\d_([A-Za-z])+\.sql~i', $file, $matches) && isset($matches[1]))
798
				@unlink(dirname(__FILE__) . '/' . $file);
799
		}
800
		closedir($dh);
0 ignored issues
show
Bug introduced by
It seems like $dh can also be of type false; however, parameter $dir_handle of closedir() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

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

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

893
	$temp = substr(@implode('', /** @scrutinizer ignore-type */ @file($boarddir . '/index.php')), 0, 4096);
Loading history...
894
	preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $temp, $match);
895
	if (empty($match[1]) || (trim($match[1]) != SMF_VERSION))
896
		return throw_error($txt['error_upgrade_old_files']);
897
898
	// What absolutely needs to be writable?
899
	$writable_files = array(
900
		$boarddir . '/Settings.php',
901
		$boarddir . '/Settings_bak.php',
902
	);
903
904
	// Only check for minified writable files if we have it enabled or not set.
905
	if (!empty($modSettings['minimize_files']) || !isset($modSettings['minimize_files']))
906
		$writable_files += array(
907
			$modSettings['theme_dir'] . '/css/minified.css',
908
			$modSettings['theme_dir'] . '/scripts/minified.js',
909
			$modSettings['theme_dir'] . '/scripts/minified_deferred.js',
910
		);
911
912
	// Do we need to add this setting?
913
	$need_settings_update = empty($modSettings['custom_avatar_dir']);
914
915
	$custom_av_dir = !empty($modSettings['custom_avatar_dir']) ? $modSettings['custom_avatar_dir'] : $GLOBALS['boarddir'] . '/custom_avatar';
916
	$custom_av_url = !empty($modSettings['custom_avatar_url']) ? $modSettings['custom_avatar_url'] : $boardurl . '/custom_avatar';
917
918
	// This little fellow has to cooperate...
919
	quickFileWritable($custom_av_dir);
920
921
	// Are we good now?
922
	if (!is_writable($custom_av_dir))
923
		return throw_error(sprintf($txt['error_dir_not_writable'], $custom_av_dir));
924
	elseif ($need_settings_update)
925
	{
926
		if (!function_exists('cache_put_data'))
927
			require_once($sourcedir . '/Load.php');
928
929
		updateSettings(array('custom_avatar_dir' => $custom_av_dir));
930
		updateSettings(array('custom_avatar_url' => $custom_av_url));
931
	}
932
933
	require_once($sourcedir . '/Security.php');
934
935
	// Check the cache directory.
936
	$cachedir_temp = empty($cachedir) ? $boarddir . '/cache' : $cachedir;
937
	if (!file_exists($cachedir_temp))
938
		@mkdir($cachedir_temp);
939
940
	if (!file_exists($cachedir_temp))
941
		return throw_error($txt['error_cache_not_found']);
942
943
	quickFileWritable($cachedir_temp . '/db_last_error.php');
944
945
	if (!file_exists($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php'))
946
		return throw_error(sprintf($txt['error_lang_index_missing'], $upcontext['language'], $upgradeurl));
947
	elseif (!isset($_GET['skiplang']))
948
	{
949
		$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php')), 0, 4096);
950
		preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
951
952
		if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
953
			return throw_error(sprintf($txt['error_upgrade_old_lang_files'], $upcontext['language'], $upgradeurl));
954
	}
955
956
	if (!makeFilesWritable($writable_files))
957
		return false;
958
959
	// Check agreement.txt. (it may not exist, in which case $boarddir must be writable.)
960
	if (isset($modSettings['agreement']) && (!is_writable($boarddir) || file_exists($boarddir . '/agreement.txt')) && !is_writable($boarddir . '/agreement.txt'))
961
		return throw_error($txt['error_agreement_not_writable']);
962
963
	// Upgrade the agreement.
964
	elseif (isset($modSettings['agreement']))
965
	{
966
		$fp = fopen($boarddir . '/agreement.txt', 'w');
967
		fwrite($fp, $modSettings['agreement']);
0 ignored issues
show
Bug introduced by
It seems like $fp can also be of type false; however, parameter $handle of fwrite() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

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

967
		fwrite(/** @scrutinizer ignore-type */ $fp, $modSettings['agreement']);
Loading history...
968
		fclose($fp);
0 ignored issues
show
Bug introduced by
It seems like $fp can also be of type false; however, parameter $handle of fclose() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

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

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

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

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

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

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

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

2742
	$temp = substr(@implode('', /** @scrutinizer ignore-type */ @file($boarddir . '/index.php')), 0, 4096);
Loading history...
2743
	preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $temp, $match);
2744
	if (empty($match[1]) || (trim($match[1]) != SMF_VERSION))
2745
		print_error('Error: Some files have not yet been updated properly.');
2746
2747
	// Make sure Settings.php is writable.
2748
	quickFileWritable($boarddir . '/Settings.php');
2749
	if (!is_writable($boarddir . '/Settings.php'))
2750
		print_error('Error: Unable to obtain write access to "Settings.php".', true);
2751
2752
	// Make sure Settings_bak.php is writable.
2753
	quickFileWritable($boarddir . '/Settings_bak.php');
2754
	if (!is_writable($boarddir . '/Settings_bak.php'))
2755
		print_error('Error: Unable to obtain write access to "Settings_bak.php".');
2756
2757
	if (isset($modSettings['agreement']) && (!is_writable($boarddir) || file_exists($boarddir . '/agreement.txt')) && !is_writable($boarddir . '/agreement.txt'))
2758
		print_error('Error: Unable to obtain write access to "agreement.txt".');
2759
	elseif (isset($modSettings['agreement']))
2760
	{
2761
		$fp = fopen($boarddir . '/agreement.txt', 'w');
2762
		fwrite($fp, $modSettings['agreement']);
0 ignored issues
show
Bug introduced by
It seems like $fp can also be of type false; however, parameter $handle of fwrite() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

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

2762
		fwrite(/** @scrutinizer ignore-type */ $fp, $modSettings['agreement']);
Loading history...
2763
		fclose($fp);
0 ignored issues
show
Bug introduced by
It seems like $fp can also be of type false; however, parameter $handle of fclose() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

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

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