Passed
Push — release-2.1 ( 2e48b8...01120d )
by Mathias
06:33
created

fix_serialized_data()   A

Complexity

Conditions 5
Paths 3

Size

Total Lines 16
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

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

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

345
		/** @scrutinizer ignore-call */ 
346
  updateDbLastError(0);

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

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

Loading history...
346
	}
347
348
	// Handle the progress of the step, if any.
349
	if (!empty($upcontext['step_progress']) && isset($upcontext['steps'][$upcontext['current_step']]))
350
	{
351
		$upcontext['step_progress'] = round($upcontext['step_progress'], 1);
352
		$upcontext['overall_percent'] += $upcontext['step_progress'] * ($upcontext['steps'][$upcontext['current_step']][3] / 100);
353
	}
354
	$upcontext['overall_percent'] = (int) $upcontext['overall_percent'];
355
356
	// We usually dump our templates out.
357
	if (!$fallThrough)
358
	{
359
		// This should not happen my dear... HELP ME DEVELOPERS!!
360
		if (!empty($command_line))
361
		{
362
			if (function_exists('debug_print_backtrace'))
363
				debug_print_backtrace();
364
365
			printf($txt['error_unexpected_template_call'], isset($upcontext['sub_template']) ? $upcontext['sub_template'] : '');
366
			flush();
367
			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...
368
		}
369
370
		if (!isset($_GET['xml']))
371
			template_upgrade_above();
372
		else
373
		{
374
			header('content-type: text/xml; charset=UTF-8');
375
			// Sadly we need to retain the $_GET data thanks to the old upgrade scripts.
376
			$upcontext['get_data'] = array();
377
			foreach ($_GET as $k => $v)
378
			{
379
				if (substr($k, 0, 3) != 'amp' && !in_array($k, array('xml', 'substep', 'lang', 'data', 'step', 'filecount')))
380
				{
381
					$upcontext['get_data'][$k] = $v;
382
				}
383
			}
384
			template_xml_above();
385
		}
386
387
		// Call the template.
388
		if (isset($upcontext['sub_template']))
389
		{
390
			$upcontext['upgrade_status']['curstep'] = $upcontext['current_step'];
391
			$upcontext['form_url'] = $upgradeurl . '?step=' . $upcontext['current_step'] . '&amp;substep=' . $_GET['substep'] . '&amp;data=' . base64_encode(json_encode($upcontext['upgrade_status']));
392
393
			// Custom stuff to pass back?
394
			if (!empty($upcontext['query_string']))
395
				$upcontext['form_url'] .= $upcontext['query_string'];
396
397
			// Call the appropriate subtemplate
398
			if (is_callable('template_' . $upcontext['sub_template']))
399
				call_user_func('template_' . $upcontext['sub_template']);
400
			else
401
				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...
402
		}
403
404
		// Was there an error?
405
		if (!empty($upcontext['forced_error_message']))
406
			echo $upcontext['forced_error_message'];
407
408
		// Show the footer.
409
		if (!isset($_GET['xml']))
410
			template_upgrade_below();
411
		else
412
			template_xml_below();
413
	}
414
415
	// Show the upgrade time for CLI when we are completely done, if in debug mode.
416
	if (!empty($command_line) && $is_debug)
417
	{
418
		$active = time() - $upcontext['started'];
419
		$hours = floor($active / 3600);
420
		$minutes = intval(($active / 60) % 60);
421
		$seconds = intval($active % 60);
422
423
		$totalTime = '';
0 ignored issues
show
Unused Code introduced by
The assignment to $totalTime is dead and can be removed.
Loading history...
424
		if ($hours > 0)
425
			echo "\n" . '', sprintf($txt['upgrade_completed_time_hms'], $hours, $minutes, $seconds), '' . "\n";
426
		elseif ($minutes > 0)
427
			echo "\n" . '', sprintf($txt['upgrade_completed_time_ms'], $minutes, $seconds), '' . "\n";
428
		elseif ($seconds > 0)
429
			echo "\n" . '', sprintf($txt['upgrade_completed_time_s'], $seconds), '' . "\n";
430
	}
431
432
	// Bang - gone!
433
	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...
434
}
435
436
// Load the list of language files, and the current language file.
437
function load_lang_file()
438
{
439
	global $txt, $upcontext, $language;
440
441
	static $lang_dir = '', $detected_languages = array(), $loaded_langfile = '';
442
443
	// Do we know where to look for the language files, or shall we just guess for now?
444
	$temp = isset($modSettings['theme_dir']) ? $modSettings['theme_dir'] . '/languages' : dirname(__FILE__) . '/Themes/default/languages';
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $modSettings seems to never exist and therefore isset should always be false.
Loading history...
445
446
	if ($lang_dir != $temp)
447
	{
448
		$lang_dir = $temp;
449
		$detected_languages = array();
450
	}
451
452
	// Override the language file?
453
	if (isset($upcontext['language']))
454
		$_SESSION['upgrader_langfile'] = 'Install.' . $upcontext['language'] . '.php';
455
	elseif (isset($upcontext['lang']))
456
		$_SESSION['upgrader_langfile'] = 'Install.' . $upcontext['lang'] . '.php';
457
	elseif (isset($language))
458
		$_SESSION['upgrader_langfile'] = 'Install.' . $language . '.php';
459
460
	// Avoid pointless repetition
461
	if (isset($_SESSION['upgrader_langfile']) && $loaded_langfile == $lang_dir . '/' . $_SESSION['upgrader_langfile'])
462
		return;
463
464
	// Now try to find the language files
465
	if (empty($detected_languages))
466
	{
467
		// Make sure the languages directory actually exists.
468
		if (file_exists($lang_dir))
469
		{
470
			// Find all the "Install" language files in the directory.
471
			$dir = dir($lang_dir);
472
			while ($entry = $dir->read())
473
			{
474
				// Skip any old '-utf8' language files that might be lying around
475
				if (strpos($entry, '-utf8') !== false)
476
					continue;
477
478
				if (substr($entry, 0, 8) == 'Install.' && substr($entry, -4) == '.php')
479
					$detected_languages[$entry] = ucfirst(substr($entry, 8, strlen($entry) - 12));
480
			}
481
			$dir->close();
482
		}
483
		// Our guess was wrong, but that's fine. We'll try again after $modSettings['theme_dir'] is defined.
484
		elseif (!isset($modSettings['theme_dir']))
485
		{
486
			// Define a few essential strings for now.
487
			$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.';
488
			$txt['error_sourcefile_missing'] = 'Unable to find the Sources/%1$s file. Please make sure it was uploaded properly, and then try again.';
489
490
			$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.';
491
			$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.';
492
493
			return;
494
		}
495
	}
496
497
	// Didn't find any, show an error message!
498
	if (empty($detected_languages))
499
	{
500
		$from = explode('/', $_SERVER['PHP_SELF']);
501
		$to = explode('/', $lang_dir);
502
		$relPath = $to;
503
504
		foreach($from as $depth => $dir)
505
		{
506
			if ($dir === $to[$depth])
507
				array_shift($relPath);
508
			else
509
			{
510
				$remaining = count($from) - $depth;
511
				if ($remaining > 1)
512
				{
513
					$padLength = (count($relPath) + $remaining - 1) * -1;
514
					$relPath = array_pad($relPath, $padLength, '..');
515
					break;
516
				}
517
				else
518
					$relPath[0] = './' . $relPath[0];
519
			}
520
		}
521
		$relPath = implode(DIRECTORY_SEPARATOR, $relPath);
522
523
		// Let's not cache this message, eh?
524
		header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
525
		header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
526
		header('Cache-Control: no-cache');
527
528
		echo '<!DOCTYPE html>
529
			<html>
530
				<head>
531
					<title>SMF Upgrader: Error!</title>
532
						<style>
533
							body {
534
								font-family: sans-serif;
535
								max-width: 700px; }
536
537
								h1 {
538
									font-size: 14pt; }
539
540
								.directory {
541
									margin: 0.3em;
542
									font-family: monospace;
543
									font-weight: bold; }
544
						</style>
545
				</head>
546
				<body>
547
					<h1>A critical error has occurred.</h1>
548
						<p>This upgrader was unable to find the upgrader\'s language file or files.  They should be found under:</p>
549
						<div class="directory">', $relPath, '</div>
550
						<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>
551
						<p>If that doesn\'t help, please make sure this upgrade.php file is in the same place as the Themes folder.</p>
552
						<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>
553
				</body>
554
			</html>';
555
		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...
556
	}
557
558
	// Make sure it exists. If it doesn't, reset it.
559
	if (!isset($_SESSION['upgrader_langfile']) || preg_match('~[^\w.-]~', $_SESSION['upgrader_langfile']) === 1 || !file_exists($lang_dir . '/' . $_SESSION['upgrader_langfile']))
560
	{
561
		// Use the first one...
562
		list ($_SESSION['upgrader_langfile']) = array_keys($detected_languages);
563
564
		// If we have English and some other language, use the other language.
565
		if ($_SESSION['upgrader_langfile'] == 'Install.english.php' && count($detected_languages) > 1)
566
			list (, $_SESSION['upgrader_langfile']) = array_keys($detected_languages);
567
	}
568
569
	// For backup we load English at first, then the second language will overwrite it.
570
	if ($_SESSION['upgrader_langfile'] != 'Install.english.php')
571
		require_once($lang_dir . '/Install.english.php');
572
573
	// And now include the actual language file itself.
574
	require_once($lang_dir . '/' . $_SESSION['upgrader_langfile']);
575
576
	// Remember what we've done
577
	$loaded_langfile = $lang_dir . '/' . $_SESSION['upgrader_langfile'];
578
}
579
580
// Used to direct the user to another location.
581
function redirectLocation($location, $addForm = true)
582
{
583
	global $upgradeurl, $upcontext, $command_line;
584
585
	// Command line users can't be redirected.
586
	if ($command_line)
587
		upgradeExit(true);
588
589
	// Are we providing the core info?
590
	if ($addForm)
591
	{
592
		$upcontext['upgrade_status']['curstep'] = $upcontext['current_step'];
593
		$location = $upgradeurl . '?step=' . $upcontext['current_step'] . '&substep=' . $_GET['substep'] . '&data=' . base64_encode(json_encode($upcontext['upgrade_status'])) . $location;
594
	}
595
596
	while (@ob_end_clean())
597
		header('location: ' . strtr($location, array('&amp;' => '&')));
598
599
	// Exit - saving status as we go.
600
	upgradeExit(true);
601
}
602
603
// Load all essential data and connect to the DB as this is pre SSI.php
604
function loadEssentialData()
605
{
606
	global $db_server, $db_user, $db_passwd, $db_name, $db_connection;
607
	global $db_prefix, $db_character_set, $db_type, $db_port;
608
	global $db_mb4, $modSettings, $sourcedir, $smcFunc, $txt;
609
610
	error_reporting(E_ALL);
611
	define('SMF', 1);
612
613
	// Start the session.
614
	if (@ini_get('session.save_handler') == 'user')
615
		@ini_set('session.save_handler', 'files');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for ini_set(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

615
		/** @scrutinizer ignore-unhandled */ @ini_set('session.save_handler', 'files');

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
616
	@session_start();
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for session_start(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

616
	/** @scrutinizer ignore-unhandled */ @session_start();

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
617
618
	if (empty($smcFunc))
619
		$smcFunc = array();
620
621
	$smcFunc['random_int'] = function($min = 0, $max = PHP_INT_MAX)
622
	{
623
		global $sourcedir;
624
625
		// Oh, wouldn't it be great if I *was* crazy? Then the world would be okay.
626
		if (!is_callable('random_int'))
627
			require_once($sourcedir . '/random_compat/random.php');
628
629
		return random_int($min, $max);
630
	};
631
632
	// We need this for authentication and some upgrade code
633
	require_once($sourcedir . '/Subs-Auth.php');
634
	require_once($sourcedir . '/Class-Package.php');
635
636
	$smcFunc['strtolower'] = 'smf_strtolower';
637
638
	// Initialize everything...
639
	initialize_inputs();
640
641
	// Get the database going!
642
	if (empty($db_type) || $db_type == 'mysqli')
643
	{
644
		$db_type = 'mysql';
645
		// If overriding $db_type, need to set its settings.php entry too
646
		$changes = array();
647
		$changes['db_type'] = '\'mysql\'';
648
		require_once($sourcedir . '/Subs-Admin.php');
649
		updateSettingsFile($changes);
650
	}
651
652
	if (file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php'))
653
	{
654
		require_once($sourcedir . '/Subs-Db-' . $db_type . '.php');
655
656
		// Make the connection...
657
		if (empty($db_connection))
658
		{
659
			$options = array('non_fatal' => true);
660
			// Add in the port if needed
661
			if (!empty($db_port))
662
				$options['port'] = $db_port;
663
664
			if (!empty($db_mb4))
665
				$options['db_mb4'] = $db_mb4;
666
667
			$db_connection = smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, $db_prefix, $options);
668
		}
669
		else
670
			// If we've returned here, ping/reconnect to be safe
671
			$smcFunc['db_ping']($db_connection);
672
673
		// Oh dear god!!
674
		if ($db_connection === null)
675
			die($txt['error_db_connect_settings']);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

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

Loading history...
676
677
		if ($db_type == 'mysql' && isset($db_character_set) && preg_match('~^\w+$~', $db_character_set) === 1)
678
			$smcFunc['db_query']('', '
679
				SET NAMES {string:db_character_set}',
680
				array(
681
					'db_error_skip' => true,
682
					'db_character_set' => $db_character_set,
683
				)
684
			);
685
686
		// Load the modSettings data...
687
		$request = $smcFunc['db_query']('', '
688
			SELECT variable, value
689
			FROM {db_prefix}settings',
690
			array(
691
				'db_error_skip' => true,
692
			)
693
		);
694
		$modSettings = array();
695
		while ($row = $smcFunc['db_fetch_assoc']($request))
696
			$modSettings[$row['variable']] = $row['value'];
697
		$smcFunc['db_free_result']($request);
698
	}
699
	else
700
		return throw_error(sprintf($txt['error_sourcefile_missing'], 'Subs-Db-' . $db_type . '.php'));
701
702
	require_once($sourcedir . '/Subs.php');
703
704
	// If they don't have the file, they're going to get a warning anyway so we won't need to clean request vars.
705
	if (file_exists($sourcedir . '/QueryString.php') && php_version_check())
706
	{
707
		require_once($sourcedir . '/QueryString.php');
708
		cleanRequest();
709
	}
710
711
	if (!isset($_GET['substep']))
712
		$_GET['substep'] = 0;
713
}
714
715
function initialize_inputs()
716
{
717
	global $start_time, $db_type;
718
719
	$start_time = time();
720
721
	umask(0);
722
723
	ob_start();
724
725
	// Better to upgrade cleanly and fall apart than to screw everything up if things take too long.
726
	ignore_user_abort(true);
727
728
	// This is really quite simple; if ?delete is on the URL, delete the upgrader...
729
	if (isset($_GET['delete']))
730
	{
731
		@unlink(__FILE__);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

731
		/** @scrutinizer ignore-unhandled */ @unlink(__FILE__);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
732
733
		// And the extra little files ;).
734
		@unlink(dirname(__FILE__) . '/upgrade_1-0.sql');
735
		@unlink(dirname(__FILE__) . '/upgrade_1-1.sql');
736
		@unlink(dirname(__FILE__) . '/upgrade_2-0_' . $db_type . '.sql');
737
		@unlink(dirname(__FILE__) . '/upgrade_2-1_' . $db_type . '.sql');
738
		@unlink(dirname(__FILE__) . '/upgrade-helper.php');
739
740
		$dh = opendir(dirname(__FILE__));
741
		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

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

746
		closedir(/** @scrutinizer ignore-type */ $dh);
Loading history...
747
748
		// Legacy files while we're at it. NOTE: We only touch files we KNOW shouldn't be there.
749
		// 1.1 Sources files not in 2.0+
750
		@unlink(dirname(__FILE__) . '/Sources/ModSettings.php');
751
		// 1.1 Templates that don't exist any more (e.g. renamed)
752
		@unlink(dirname(__FILE__) . '/Themes/default/Combat.template.php');
753
		@unlink(dirname(__FILE__) . '/Themes/default/Modlog.template.php');
754
		// 1.1 JS files were stored in the main theme folder, but in 2.0+ are in the scripts/ folder
755
		@unlink(dirname(__FILE__) . '/Themes/default/fader.js');
756
		@unlink(dirname(__FILE__) . '/Themes/default/script.js');
757
		@unlink(dirname(__FILE__) . '/Themes/default/spellcheck.js');
758
		@unlink(dirname(__FILE__) . '/Themes/default/xml_board.js');
759
		@unlink(dirname(__FILE__) . '/Themes/default/xml_topic.js');
760
761
		// 2.0 Sources files not in 2.1+
762
		@unlink(dirname(__FILE__) . '/Sources/DumpDatabase.php');
763
		@unlink(dirname(__FILE__) . '/Sources/LockTopic.php');
764
765
		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');
766
		exit;
767
	}
768
769
	// Something is causing this to happen, and it's annoying.  Stop it.
770
	$temp = 'upgrade_php?step';
771
	while (strlen($temp) > 4)
772
	{
773
		if (isset($_GET[$temp]))
774
			unset($_GET[$temp]);
775
		$temp = substr($temp, 1);
776
	}
777
778
	// Force a step, defaulting to 0.
779
	$_GET['step'] = (int) @$_GET['step'];
780
	$_GET['substep'] = (int) @$_GET['substep'];
781
}
782
783
// Step 0 - Let's welcome them in and ask them to login!
784
function WelcomeLogin()
785
{
786
	global $boarddir, $sourcedir, $modSettings, $cachedir, $upgradeurl, $upcontext;
787
	global $smcFunc, $db_type, $databases, $boardurl;
788
789
	// We global $txt here so that the language files can add to them. This variable is NOT unused.
790
	global $txt;
791
792
	$upcontext['sub_template'] = 'welcome_message';
793
794
	// Check for some key files - one template, one language, and a new and an old source file.
795
	$check = @file_exists($modSettings['theme_dir'] . '/index.template.php')
796
		&& @file_exists($sourcedir . '/QueryString.php')
797
		&& @file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php')
798
		&& @file_exists(dirname(__FILE__) . '/upgrade_2-1_' . $db_type . '.sql');
799
800
	// Need legacy scripts?
801
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 2.1)
802
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_2-0_' . $db_type . '.sql');
803
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 2.0)
804
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_1-1.sql');
805
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 1.1)
806
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_1-0.sql');
807
808
	// We don't need "-utf8" files anymore...
809
	$upcontext['language'] = str_ireplace('-utf8', '', $upcontext['language']);
810
811
	if (!$check)
812
		// 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.
813
		return throw_error($txt['error_upgrade_files_missing']);
814
815
	// Do they meet the install requirements?
816
	if (!php_version_check())
817
		return throw_error($txt['error_php_too_low']);
818
819
	if (!db_version_check())
820
		return throw_error(sprintf($txt['error_db_too_low'], $databases[$db_type]['name']));
821
822
	// Do some checks to make sure they have proper privileges
823
	db_extend('packages');
824
825
	// CREATE
826
	$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');
827
828
	// ALTER
829
	$alter = $smcFunc['db_add_column']('{db_prefix}priv_check', array('name' => 'txt', 'type' => 'varchar', 'size' => 4, 'null' => false, 'default' => ''));
830
831
	// DROP
832
	$drop = $smcFunc['db_drop_table']('{db_prefix}priv_check');
833
834
	// Sorry... we need CREATE, ALTER and DROP
835
	if (!$create || !$alter || !$drop)
836
		return throw_error(sprintf($txt['error_db_privileges'], $databases[$db_type]['name']));
837
838
	// Do a quick version spot check.
839
	$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

839
	$temp = substr(@implode('', /** @scrutinizer ignore-type */ @file($boarddir . '/index.php')), 0, 4096);
Loading history...
840
	preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $temp, $match);
841
	if (empty($match[1]) || (trim($match[1]) != SMF_VERSION))
842
		return throw_error($txt['error_upgrade_old_files']);
843
844
	// What absolutely needs to be writable?
845
	$writable_files = array(
846
		$boarddir . '/Settings.php',
847
		$boarddir . '/Settings_bak.php',
848
	);
849
850
	// Only check for minified writable files if we have it enabled or not set.
851
	if (!empty($modSettings['minimize_files']) || !isset($modSettings['minimize_files']))
852
		$writable_files += array(
853
			$modSettings['theme_dir'] . '/css/minified.css',
854
			$modSettings['theme_dir'] . '/scripts/minified.js',
855
			$modSettings['theme_dir'] . '/scripts/minified_deferred.js',
856
		);
857
858
	// Do we need to add this setting?
859
	$need_settings_update = empty($modSettings['custom_avatar_dir']);
860
861
	$custom_av_dir = !empty($modSettings['custom_avatar_dir']) ? $modSettings['custom_avatar_dir'] : $GLOBALS['boarddir'] . '/custom_avatar';
862
	$custom_av_url = !empty($modSettings['custom_avatar_url']) ? $modSettings['custom_avatar_url'] : $boardurl . '/custom_avatar';
863
864
	// This little fellow has to cooperate...
865
	quickFileWritable($custom_av_dir);
866
867
	// Are we good now?
868
	if (!is_writable($custom_av_dir))
869
		return throw_error(sprintf($txt['error_dir_not_writable'], $custom_av_dir));
870
	elseif ($need_settings_update)
871
	{
872
		if (!function_exists('cache_put_data'))
873
			require_once($sourcedir . '/Load.php');
874
875
		updateSettings(array('custom_avatar_dir' => $custom_av_dir));
876
		updateSettings(array('custom_avatar_url' => $custom_av_url));
877
	}
878
879
	require_once($sourcedir . '/Security.php');
880
881
	// Check the cache directory.
882
	$cachedir_temp = empty($cachedir) ? $boarddir . '/cache' : $cachedir;
883
	if (!file_exists($cachedir_temp))
884
		@mkdir($cachedir_temp);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for mkdir(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

884
		/** @scrutinizer ignore-unhandled */ @mkdir($cachedir_temp);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
885
886
	if (!file_exists($cachedir_temp))
887
		return throw_error($txt['error_cache_not_found']);
888
889
	if (!file_exists($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php'))
890
		return throw_error(sprintf($txt['error_lang_index_missing'], $upcontext['language'], $upgradeurl));
891
	elseif (!isset($_GET['skiplang']))
892
	{
893
		$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php')), 0, 4096);
894
		preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
895
896
		if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
897
			return throw_error(sprintf($txt['error_upgrade_old_lang_files'], $upcontext['language'], $upgradeurl));
898
	}
899
900
	if (!makeFilesWritable($writable_files))
901
		return false;
902
903
	// Check agreement.txt. (it may not exist, in which case $boarddir must be writable.)
904
	if (isset($modSettings['agreement']) && (!is_writable($boarddir) || file_exists($boarddir . '/agreement.txt')) && !is_writable($boarddir . '/agreement.txt'))
905
		return throw_error($txt['error_agreement_not_writable']);
906
907
	// Upgrade the agreement.
908
	elseif (isset($modSettings['agreement']))
909
	{
910
		$fp = fopen($boarddir . '/agreement.txt', 'w');
911
		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

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

912
		fclose(/** @scrutinizer ignore-type */ $fp);
Loading history...
913
	}
914
915
	// We're going to check that their board dir setting is right in case they've been moving stuff around.
916
	if (strtr($boarddir, array('/' => '', '\\' => '')) != strtr(dirname(__FILE__), array('/' => '', '\\' => '')))
917
		$upcontext['warning'] = '
918
			' . sprintf($txt['upgrade_boarddir_settings'], $boarddir, dirname(__FILE__)) . '<br>
919
			<ul>
920
				<li>' . $txt['upgrade_boarddir'] . '  ' . $boarddir . '</li>
921
				<li>' . $txt['upgrade_sourcedir'] . '  ' . $boarddir . '</li>
922
				<li>' . $txt['upgrade_cachedir'] . '  ' . $cachedir_temp . '</li>
923
			</ul>
924
			' . $txt['upgrade_incorrect_settings'] . '';
925
926
	// Confirm mbstring is loaded...
927
	if (!extension_loaded('mbstring'))
928
		return throw_error($txt['install_no_mbstring']);
929
930
	// Check for https stream support.
931
	$supported_streams = stream_get_wrappers();
932
	if (!in_array('https', $supported_streams))
933
		$upcontext['custom_warning'] = $txt['install_no_https'];
934
935
	// Either we're logged in or we're going to present the login.
936
	if (checkLogin())
937
		return true;
938
939
	$upcontext += createToken('login');
940
941
	return false;
942
}
943
944
// Step 0.5: Does the login work?
945
function checkLogin()
946
{
947
	global $modSettings, $upcontext, $disable_security;
948
	global $smcFunc, $db_type, $support_js, $sourcedir;
949
950
	// Don't bother if the security is disabled.
951
	if ($disable_security)
952
		return true;
953
954
	// Are we trying to login?
955
	if (isset($_POST['contbutt']) && (!empty($_POST['user'])))
956
	{
957
		// If we've disabled security pick a suitable name!
958
		if (empty($_POST['user']))
959
			$_POST['user'] = 'Administrator';
960
961
		// Before 2.0 these column names were different!
962
		$oldDB = false;
963
		if (empty($db_type) || $db_type == 'mysql')
964
		{
965
			$request = $smcFunc['db_query']('', '
966
				SHOW COLUMNS
967
				FROM {db_prefix}members
968
				LIKE {string:member_name}',
969
				array(
970
					'member_name' => 'memberName',
971
					'db_error_skip' => true,
972
				)
973
			);
974
			if ($smcFunc['db_num_rows']($request) != 0)
975
				$oldDB = true;
976
			$smcFunc['db_free_result']($request);
977
		}
978
979
		// Get what we believe to be their details.
980
		if (!$disable_security)
981
		{
982
			if ($oldDB)
983
				$request = $smcFunc['db_query']('', '
984
					SELECT id_member, memberName AS member_name, passwd, id_group,
985
						additionalGroups AS additional_groups, lngfile
986
					FROM {db_prefix}members
987
					WHERE memberName = {string:member_name}',
988
					array(
989
						'member_name' => $_POST['user'],
990
						'db_error_skip' => true,
991
					)
992
				);
993
			else
994
				$request = $smcFunc['db_query']('', '
995
					SELECT id_member, member_name, passwd, id_group, additional_groups, lngfile
996
					FROM {db_prefix}members
997
					WHERE member_name = {string:member_name}',
998
					array(
999
						'member_name' => $_POST['user'],
1000
						'db_error_skip' => true,
1001
					)
1002
				);
1003
			if ($smcFunc['db_num_rows']($request) != 0)
1004
			{
1005
				list ($id_member, $name, $password, $id_group, $addGroups, $user_language) = $smcFunc['db_fetch_row']($request);
1006
1007
				$groups = explode(',', $addGroups);
1008
				$groups[] = $id_group;
1009
1010
				foreach ($groups as $k => $v)
1011
					$groups[$k] = (int) $v;
1012
1013
				$sha_passwd = sha1(strtolower($name) . un_htmlspecialchars($_REQUEST['passwrd']));
1014
1015
				// We don't use "-utf8" anymore...
1016
				$user_language = str_ireplace('-utf8', '', $user_language);
1017
			}
1018
			else
1019
				$upcontext['username_incorrect'] = true;
1020
1021
			$smcFunc['db_free_result']($request);
1022
		}
1023
		$upcontext['username'] = $_POST['user'];
1024
1025
		// Track whether javascript works!
1026
		if (!empty($_POST['js_works']))
1027
		{
1028
			$upcontext['upgrade_status']['js'] = 1;
1029
			$support_js = 1;
1030
		}
1031
		else
1032
			$support_js = 0;
1033
1034
		// Note down the version we are coming from.
1035
		if (!empty($modSettings['smfVersion']) && empty($upcontext['user']['version']))
1036
			$upcontext['user']['version'] = $modSettings['smfVersion'];
1037
1038
		// Didn't get anywhere?
1039
		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']))
1040
		{
1041
			// MD5?
1042
			$md5pass = md5_hmac($_REQUEST['passwrd'], strtolower($_POST['user']));
1043
			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...
1044
			{
1045
				$upcontext['password_failed'] = true;
1046
				// Disable the hashing this time.
1047
				$upcontext['disable_login_hashing'] = true;
1048
			}
1049
		}
1050
1051
		if ((empty($upcontext['password_failed']) && !empty($name)) || $disable_security)
1052
		{
1053
			// Set the password.
1054
			if (!$disable_security)
1055
			{
1056
				// Do we actually have permission?
1057
				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...
1058
				{
1059
					$request = $smcFunc['db_query']('', '
1060
						SELECT permission
1061
						FROM {db_prefix}permissions
1062
						WHERE id_group IN ({array_int:groups})
1063
							AND permission = {string:admin_forum}',
1064
						array(
1065
							'groups' => $groups,
1066
							'admin_forum' => 'admin_forum',
1067
							'db_error_skip' => true,
1068
						)
1069
					);
1070
					if ($smcFunc['db_num_rows']($request) == 0)
1071
						return throw_error($txt['error_not_admin']);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $txt seems to be never defined.
Loading history...
1072
					$smcFunc['db_free_result']($request);
1073
				}
1074
1075
				$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...
1076
				$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...
1077
			}
1078
			else
1079
			{
1080
				$upcontext['user']['id'] = 1;
1081
				$upcontext['user']['name'] = 'Administrator';
1082
			}
1083
1084
			if (!is_callable('random_int'))
1085
				require_once('Sources/random_compat/random.php');
1086
1087
			$upcontext['user']['pass'] = random_int(0, 60000);
1088
			// This basically is used to match the GET variables to Settings.php.
1089
			$upcontext['upgrade_status']['pass'] = $upcontext['user']['pass'];
1090
1091
			// Set the language to that of the user?
1092
			if (isset($user_language) && $user_language != $upcontext['language'] && file_exists($modSettings['theme_dir'] . '/languages/index.' . basename($user_language, '.lng') . '.php'))
1093
			{
1094
				$user_language = basename($user_language, '.lng');
1095
				$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

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

1899
	$file_steps = substr_count(implode('', /** @scrutinizer ignore-type */ $lines), '---#');
Loading history...
1900
	$upcontext['total_items'] = substr_count(implode('', $lines), '--- ');
1901
	$upcontext['debug_items'] = $file_steps;
1902
	$upcontext['current_item_num'] = 0;
1903
	$upcontext['current_item_name'] = '';
1904
	$upcontext['current_debug_item_num'] = 0;
1905
	$upcontext['current_debug_item_name'] = '';
1906
	// This array keeps a record of what we've done in case java is dead...
1907
	$upcontext['actioned_items'] = array();
1908
1909
	$done_something = false;
1910
1911
	foreach ($lines as $line_number => $line)
1912
	{
1913
		$do_current = $substep >= $_GET['substep'];
1914
1915
		// Get rid of any comments in the beginning of the line...
1916
		if (substr(trim($line), 0, 2) === '/*')
1917
			$line = preg_replace('~/\*.+?\*/~', '', $line);
1918
1919
		// Always flush.  Flush, flush, flush.  Flush, flush, flush, flush!  FLUSH!
1920
		if ($is_debug && !$support_js && $command_line)
1921
			flush();
1922
1923
		if (trim($line) === '')
1924
			continue;
1925
1926
		if (trim(substr($line, 0, 3)) === '---')
1927
		{
1928
			$type = substr($line, 3, 1);
1929
1930
			// An error??
1931
			if (trim($current_data) != '' && $type !== '}')
1932
			{
1933
				$upcontext['error_message'] = 'Error in upgrade script - line ' . $line_number . '!' . $endl;
1934
				if ($command_line)
1935
					echo $upcontext['error_message'];
1936
			}
1937
1938
			if ($type == ' ')
1939
			{
1940
				if (!$support_js && $do_current && $_GET['substep'] != 0 && $command_line)
1941
				{
1942
					echo ' Successful.', $endl;
1943
					flush();
1944
				}
1945
1946
				$last_step = htmlspecialchars(rtrim(substr($line, 4)));
1947
				$upcontext['current_item_num']++;
1948
				$upcontext['current_item_name'] = $last_step;
1949
1950
				if ($do_current)
1951
				{
1952
					$upcontext['actioned_items'][] = $last_step;
1953
					if ($command_line)
1954
						echo ' * ';
1955
1956
					// Starting a new main step in our DB changes, so it's time to reset this.
1957
					$upcontext['skip_db_substeps'] = false;
1958
				}
1959
			}
1960
			elseif ($type == '#')
1961
			{
1962
				$upcontext['step_progress'] += (100 / $upcontext['file_count']) / $file_steps;
1963
1964
				$upcontext['current_debug_item_num']++;
1965
				if (trim($line) != '---#')
1966
					$upcontext['current_debug_item_name'] = htmlspecialchars(rtrim(substr($line, 4)));
1967
1968
				// Have we already done something?
1969
				if (isset($_GET['xml']) && $done_something)
1970
				{
1971
					restore_error_handler();
1972
					return $upcontext['current_debug_item_num'] >= $upcontext['debug_items'] ? true : false;
1973
				}
1974
1975
				if ($do_current)
1976
				{
1977
					if (trim($line) == '---#' && $command_line)
1978
						echo ' done.', $endl;
1979
					elseif ($command_line)
1980
						echo ' +++ ', rtrim(substr($line, 4));
1981
					elseif (trim($line) != '---#')
1982
					{
1983
						if ($is_debug)
1984
							$upcontext['actioned_items'][] = $upcontext['current_debug_item_name'];
1985
					}
1986
				}
1987
1988
				if ($substep < $_GET['substep'] && $substep + 1 >= $_GET['substep'])
1989
				{
1990
					if ($command_line)
1991
						echo ' * ';
1992
					else
1993
						$upcontext['actioned_items'][] = $last_step;
1994
				}
1995
1996
				// Small step - only if we're actually doing stuff.
1997
				if ($do_current)
1998
					nextSubstep(++$substep);
1999
				else
2000
					$substep++;
2001
			}
2002
			elseif ($type == '{')
2003
				$current_type = 'code';
2004
			elseif ($type == '}')
2005
			{
2006
				$current_type = 'sql';
2007
2008
				if (!$do_current || !empty($upcontext['skip_db_substeps']))
2009
				{
2010
					$current_data = '';
2011
2012
					// Avoid confusion when skipping something we normally would have done
2013
					if ($do_current)
2014
						$done_something = true;
2015
2016
					continue;
2017
				}
2018
2019
				// @todo Update this to a try/catch for PHP 7+, because eval() now throws an exception for parse errors instead of returning false
2020
				if (eval('global $db_prefix, $modSettings, $smcFunc, $txt, $upcontext; ' . $current_data) === false)
0 ignored issues
show
introduced by
The use of eval() is discouraged.
Loading history...
2021
				{
2022
					$upcontext['error_message'] = 'Error in upgrade script ' . basename($filename) . ' on line ' . $line_number . '!' . $endl;
2023
					if ($command_line)
2024
						echo $upcontext['error_message'];
2025
				}
2026
2027
				// Done with code!
2028
				$current_data = '';
2029
				$done_something = true;
2030
			}
2031
2032
			continue;
2033
		}
2034
2035
		$current_data .= $line;
2036
		if (substr(rtrim($current_data), -1) === ';' && $current_type === 'sql')
2037
		{
2038
			if ((!$support_js || isset($_GET['xml'])))
2039
			{
2040
				if (!$do_current || !empty($upcontext['skip_db_substeps']))
2041
				{
2042
					$current_data = '';
2043
2044
					if ($do_current)
2045
						$done_something = true;
2046
2047
					continue;
2048
				}
2049
2050
				$current_data = strtr(substr(rtrim($current_data), 0, -1), array('{$db_prefix}' => $db_prefix, '{$boarddir}' => $boarddir, '{$sboarddir}' => addslashes($boarddir), '{$boardurl}' => $boardurl, '{$db_collation}' => $db_collation));
2051
2052
				upgrade_query($current_data);
2053
2054
				// @todo This will be how it kinda does it once mysql all stripped out - needed for postgre (etc).
2055
				/*
2056
				$result = $smcFunc['db_query']('', $current_data, false, false);
2057
				// Went wrong?
2058
				if (!$result)
2059
				{
2060
					// Bit of a bodge - do we want the error?
2061
					if (!empty($upcontext['return_error']))
2062
					{
2063
						$upcontext['error_message'] = $smcFunc['db_error']($db_connection);
2064
						return false;
2065
					}
2066
				}*/
2067
				$done_something = true;
2068
			}
2069
			$current_data = '';
2070
		}
2071
		// If this is xml based and we're just getting the item name then that's grand.
2072
		elseif ($support_js && !isset($_GET['xml']) && $upcontext['current_debug_item_name'] != '' && $do_current)
2073
		{
2074
			restore_error_handler();
2075
			return false;
2076
		}
2077
2078
		// Clean up by cleaning any step info.
2079
		$step_progress = array();
2080
		$custom_warning = '';
2081
	}
2082
2083
	// Put back the error handler.
2084
	restore_error_handler();
2085
2086
	if ($command_line)
2087
	{
2088
		echo ' Successful.' . "\n";
2089
		flush();
2090
	}
2091
2092
	$_GET['substep'] = 0;
2093
	return true;
2094
}
2095
2096
function upgrade_query($string, $unbuffered = false)
2097
{
2098
	global $db_connection, $db_server, $db_user, $db_passwd, $db_type;
2099
	global $command_line, $upcontext, $upgradeurl, $modSettings;
2100
	global $db_name, $db_unbuffered, $smcFunc, $txt;
2101
2102
	// Get the query result - working around some SMF specific security - just this once!
2103
	$modSettings['disableQueryCheck'] = true;
2104
	$db_unbuffered = $unbuffered;
2105
	$ignore_insert_error = false;
2106
2107
	// If we got an old pg version and use a insert ignore query
2108
	if ($db_type == 'postgresql' && !$smcFunc['db_native_replace']() && strpos($string, 'ON CONFLICT DO NOTHING') !== false)
2109
	{
2110
		$ignore_insert_error = true;
2111
		$string = str_replace('ON CONFLICT DO NOTHING', '', $string);
2112
	}
2113
	$result = $smcFunc['db_query']('', $string, array('security_override' => true, 'db_error_skip' => true));
2114
	$db_unbuffered = false;
2115
2116
	// Failure?!
2117
	if ($result !== false)
2118
		return $result;
2119
2120
	$db_error_message = $smcFunc['db_error']($db_connection);
2121
	// If MySQL we do something more clever.
2122
	if ($db_type == 'mysql')
2123
	{
2124
		$mysqli_errno = mysqli_errno($db_connection);
2125
		$error_query = in_array(substr(trim($string), 0, 11), array('INSERT INTO', 'UPDATE IGNO', 'ALTER TABLE', 'DROP TABLE ', 'ALTER IGNOR'));
2126
2127
		// Error numbers:
2128
		//    1016: Can't open file '....MYI'
2129
		//    1050: Table already exists.
2130
		//    1054: Unknown column name.
2131
		//    1060: Duplicate column name.
2132
		//    1061: Duplicate key name.
2133
		//    1062: Duplicate entry for unique key.
2134
		//    1068: Multiple primary keys.
2135
		//    1072: Key column '%s' doesn't exist in table.
2136
		//    1091: Can't drop key, doesn't exist.
2137
		//    1146: Table doesn't exist.
2138
		//    2013: Lost connection to server during query.
2139
2140
		if ($mysqli_errno == 1016)
2141
		{
2142
			if (preg_match('~\'([^\.\']+)~', $db_error_message, $match) != 0 && !empty($match[1]))
2143
			{
2144
				mysqli_query($db_connection, 'REPAIR TABLE `' . $match[1] . '`');
2145
				$result = mysqli_query($db_connection, $string);
2146
				if ($result !== false)
2147
					return $result;
2148
			}
2149
		}
2150
		elseif ($mysqli_errno == 2013)
2151
		{
2152
			$db_connection = mysqli_connect($db_server, $db_user, $db_passwd);
2153
			mysqli_select_db($db_connection, $db_name);
2154
			if ($db_connection)
0 ignored issues
show
introduced by
$db_connection is of type mysqli, thus it always evaluated to true.
Loading history...
2155
			{
2156
				$result = mysqli_query($db_connection, $string);
2157
				if ($result !== false)
2158
					return $result;
2159
			}
2160
		}
2161
		// Duplicate column name... should be okay ;).
2162
		elseif (in_array($mysqli_errno, array(1060, 1061, 1068, 1091)))
2163
			return false;
2164
		// Duplicate insert... make sure it's the proper type of query ;).
2165
		elseif (in_array($mysqli_errno, array(1054, 1062, 1146)) && $error_query)
2166
			return false;
2167
		// Creating an index on a non-existent column.
2168
		elseif ($mysqli_errno == 1072)
2169
			return false;
2170
		elseif ($mysqli_errno == 1050 && substr(trim($string), 0, 12) == 'RENAME TABLE')
2171
			return false;
2172
	}
2173
	// If a table already exists don't go potty.
2174
	else
2175
	{
2176
		if (in_array(substr(trim($string), 0, 8), array('CREATE T', 'CREATE S', 'DROP TABL', 'ALTER TA', 'CREATE I', 'CREATE U')))
2177
		{
2178
			if (strpos($db_error_message, 'exist') !== false)
2179
				return true;
2180
		}
2181
		elseif (strpos(trim($string), 'INSERT ') !== false)
2182
		{
2183
			if (strpos($db_error_message, 'duplicate') !== false || $ignore_insert_error)
2184
				return true;
2185
		}
2186
	}
2187
2188
	// Get the query string so we pass everything.
2189
	$query_string = '';
2190
	foreach ($_GET as $k => $v)
2191
		$query_string .= ';' . $k . '=' . $v;
2192
	if (strlen($query_string) != 0)
2193
		$query_string = '?' . substr($query_string, 1);
2194
2195
	if ($command_line)
2196
	{
2197
		echo 'Unsuccessful!  Database error message:', "\n", $db_error_message, "\n";
2198
		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...
2199
	}
2200
2201
	// Bit of a bodge - do we want the error?
2202
	if (!empty($upcontext['return_error']))
2203
	{
2204
		$upcontext['error_message'] = $db_error_message;
2205
		$upcontext['error_string'] = $string;
2206
		return false;
2207
	}
2208
2209
	// Otherwise we have to display this somewhere appropriate if possible.
2210
	$upcontext['forced_error_message'] = '
2211
			<strong>' . $txt['upgrade_unsuccessful'] . '</strong><br>
2212
2213
			<div style="margin: 2ex;">
2214
				' . $txt['upgrade_thisquery'] . '
2215
				<blockquote><pre>' . nl2br(htmlspecialchars(trim($string))) . ';</pre></blockquote>
2216
2217
				' . $txt['upgrade_causerror'] . '
2218
				<blockquote>' . nl2br(htmlspecialchars($db_error_message)) . '</blockquote>
2219
			</div>
2220
2221
			<form action="' . $upgradeurl . $query_string . '" method="post">
2222
				<input type="submit" value="' . $txt['upgrade_respondtime_clickhere'] . '" class="button">
2223
			</form>
2224
		</div>';
2225
2226
	upgradeExit();
2227
}
2228
2229
// This performs a table alter, but does it unbuffered so the script can time out professionally.
2230
function protected_alter($change, $substep, $is_test = false)
2231
{
2232
	global $db_prefix, $smcFunc;
2233
2234
	db_extend('packages');
2235
2236
	// Firstly, check whether the current index/column exists.
2237
	$found = false;
2238
	if ($change['type'] === 'column')
2239
	{
2240
		$columns = $smcFunc['db_list_columns']('{db_prefix}' . $change['table'], true);
2241
		foreach ($columns as $column)
2242
		{
2243
			// Found it?
2244
			if ($column['name'] === $change['name'])
2245
			{
2246
				$found |= 1;
2247
				// Do some checks on the data if we have it set.
2248
				if (isset($change['col_type']))
2249
					$found &= $change['col_type'] === $column['type'];
2250
				if (isset($change['null_allowed']))
2251
					$found &= $column['null'] == $change['null_allowed'];
2252
				if (isset($change['default']))
2253
					$found &= $change['default'] === $column['default'];
2254
			}
2255
		}
2256
	}
2257
	elseif ($change['type'] === 'index')
2258
	{
2259
		$request = upgrade_query('
2260
			SHOW INDEX
2261
			FROM ' . $db_prefix . $change['table']);
2262
		if ($request !== false)
2263
		{
2264
			$cur_index = array();
2265
2266
			while ($row = $smcFunc['db_fetch_assoc']($request))
2267
				if ($row['Key_name'] === $change['name'])
2268
					$cur_index[(int) $row['Seq_in_index']] = $row['Column_name'];
2269
2270
			ksort($cur_index, SORT_NUMERIC);
2271
			$found = array_values($cur_index) === $change['target_columns'];
2272
2273
			$smcFunc['db_free_result']($request);
2274
		}
2275
	}
2276
2277
	// If we're trying to add and it's added, we're done.
2278
	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...
2279
		return true;
2280
	// Otherwise if we're removing and it wasn't found we're also done.
2281
	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...
2282
		return true;
2283
	// Otherwise is it just a test?
2284
	elseif ($is_test)
2285
		return false;
2286
2287
	// Not found it yet? Bummer! How about we see if we're currently doing it?
2288
	$running = false;
2289
	$found = false;
2290
	while (1 == 1)
2291
	{
2292
		$request = upgrade_query('
2293
			SHOW FULL PROCESSLIST');
2294
		while ($row = $smcFunc['db_fetch_assoc']($request))
2295
		{
2296
			if (strpos($row['Info'], 'ALTER TABLE ' . $db_prefix . $change['table']) !== false && strpos($row['Info'], $change['text']) !== false)
2297
				$found = true;
2298
		}
2299
2300
		// Can't find it? Then we need to run it fools!
2301
		if (!$found && !$running)
2302
		{
2303
			$smcFunc['db_free_result']($request);
2304
2305
			$success = upgrade_query('
2306
				ALTER TABLE ' . $db_prefix . $change['table'] . '
2307
				' . $change['text'], true) !== false;
2308
2309
			if (!$success)
2310
				return false;
2311
2312
			// Return
2313
			$running = true;
2314
		}
2315
		// What if we've not found it, but we'd ran it already? Must of completed.
2316
		elseif (!$found)
2317
		{
2318
			$smcFunc['db_free_result']($request);
2319
			return true;
2320
		}
2321
2322
		// Pause execution for a sec or three.
2323
		sleep(3);
2324
2325
		// Can never be too well protected.
2326
		nextSubstep($substep);
2327
	}
2328
2329
	// Protect it.
2330
	nextSubstep($substep);
2331
}
2332
2333
/**
2334
 * Alter a text column definition preserving its character set.
2335
 *
2336
 * @param array $change
2337
 * @param int $substep
2338
 */
2339
function textfield_alter($change, $substep)
2340
{
2341
	global $db_prefix, $smcFunc;
2342
2343
	$request = $smcFunc['db_query']('', '
2344
		SHOW FULL COLUMNS
2345
		FROM {db_prefix}' . $change['table'] . '
2346
		LIKE {string:column}',
2347
		array(
2348
			'column' => $change['column'],
2349
			'db_error_skip' => true,
2350
		)
2351
	);
2352
	if ($smcFunc['db_num_rows']($request) === 0)
2353
		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...
2354
	$table_row = $smcFunc['db_fetch_assoc']($request);
2355
	$smcFunc['db_free_result']($request);
2356
2357
	// If something of the current column definition is different, fix it.
2358
	$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']);
2359
2360
	// Columns that previously allowed null, need to be converted first.
2361
	$null_fix = strtolower($table_row['Null']) === 'yes' && !$change['null_allowed'];
2362
2363
	// Get the character set that goes with the collation of the column.
2364
	if ($column_fix && !empty($table_row['Collation']))
2365
	{
2366
		$request = $smcFunc['db_query']('', '
2367
			SHOW COLLATION
2368
			LIKE {string:collation}',
2369
			array(
2370
				'collation' => $table_row['Collation'],
2371
				'db_error_skip' => true,
2372
			)
2373
		);
2374
		// No results? Just forget it all together.
2375
		if ($smcFunc['db_num_rows']($request) === 0)
2376
			unset($table_row['Collation']);
2377
		else
2378
			$collation_info = $smcFunc['db_fetch_assoc']($request);
2379
		$smcFunc['db_free_result']($request);
2380
	}
2381
2382
	if ($column_fix)
2383
	{
2384
		// Make sure there are no NULL's left.
2385
		if ($null_fix)
2386
			$smcFunc['db_query']('', '
2387
				UPDATE {db_prefix}' . $change['table'] . '
2388
				SET ' . $change['column'] . ' = {string:default}
2389
				WHERE ' . $change['column'] . ' IS NULL',
2390
				array(
2391
					'default' => isset($change['default']) ? $change['default'] : '',
2392
					'db_error_skip' => true,
2393
				)
2394
			);
2395
2396
		// Do the actual alteration.
2397
		$smcFunc['db_query']('', '
2398
			ALTER TABLE {db_prefix}' . $change['table'] . '
2399
			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}' : ''),
2400
			array(
2401
				'default' => isset($change['default']) ? $change['default'] : '',
2402
				'db_error_skip' => true,
2403
			)
2404
		);
2405
	}
2406
	nextSubstep($substep);
2407
}
2408
2409
// Check if we need to alter this query.
2410
function checkChange(&$change)
2411
{
2412
	global $smcFunc, $db_type, $databases;
2413
	static $database_version, $where_field_support;
2414
2415
	// Attempt to find a database_version.
2416
	if (empty($database_version))
2417
	{
2418
		$database_version = $databases[$db_type]['version_check'];
2419
		$where_field_support = $db_type == 'mysql' && version_compare('5.0', $database_version, '<=');
2420
	}
2421
2422
	// Not a column we need to check on?
2423
	if (!in_array($change['name'], array('memberGroups', 'passwordSalt')))
2424
		return;
2425
2426
	// Break it up you (six|seven).
2427
	$temp = explode(' ', str_replace('NOT NULL', 'NOT_NULL', $change['text']));
2428
2429
	// Can we support a shortcut method?
2430
	if ($where_field_support)
2431
	{
2432
		// Get the details about this change.
2433
		$request = $smcFunc['db_query']('', '
2434
			SHOW FIELDS
2435
			FROM {db_prefix}{raw:table}
2436
			WHERE Field = {string:old_name} OR Field = {string:new_name}',
2437
			array(
2438
				'table' => $change['table'],
2439
				'old_name' => $temp[1],
2440
				'new_name' => $temp[2],
2441
			)
2442
		);
2443
		// !!! This doesn't technically work because we don't pass request into it, but it hasn't broke anything yet.
2444
		if ($smcFunc['db_num_rows'] != 1)
2445
			return;
2446
2447
		list (, $current_type) = $smcFunc['db_fetch_assoc']($request);
2448
		$smcFunc['db_free_result']($request);
2449
	}
2450
	else
2451
	{
2452
		// Do this the old fashion, sure method way.
2453
		$request = $smcFunc['db_query']('', '
2454
			SHOW FIELDS
2455
			FROM {db_prefix}{raw:table}',
2456
			array(
2457
				'table' => $change['table'],
2458
			)
2459
		);
2460
		// Mayday!
2461
		// !!! This doesn't technically work because we don't pass request into it, but it hasn't broke anything yet.
2462
		if ($smcFunc['db_num_rows'] == 0)
2463
			return;
2464
2465
		// Oh where, oh where has my little field gone. Oh where can it be...
2466
		while ($row = $smcFunc['db_query']($request))
2467
			if ($row['Field'] == $temp[1] || $row['Field'] == $temp[2])
2468
			{
2469
				$current_type = $row['Type'];
2470
				break;
2471
			}
2472
	}
2473
2474
	// If this doesn't match, the column may of been altered for a reason.
2475
	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...
2476
		$temp[3] = $current_type;
2477
2478
	// Piece this back together.
2479
	$change['text'] = str_replace('NOT_NULL', 'NOT NULL', implode(' ', $temp));
2480
}
2481
2482
// The next substep.
2483
function nextSubstep($substep)
2484
{
2485
	global $start_time, $timeLimitThreshold, $command_line, $custom_warning;
2486
	global $step_progress, $is_debug, $upcontext;
2487
2488
	if ($_GET['substep'] < $substep)
2489
		$_GET['substep'] = $substep;
2490
2491
	if ($command_line)
2492
	{
2493
		if (time() - $start_time > 1 && empty($is_debug))
2494
		{
2495
			echo '.';
2496
			$start_time = time();
2497
		}
2498
		return;
2499
	}
2500
2501
	@set_time_limit(300);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for set_time_limit(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

2501
	/** @scrutinizer ignore-unhandled */ @set_time_limit(300);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
2502
	if (function_exists('apache_reset_timeout'))
2503
		@apache_reset_timeout();
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for apache_reset_timeout(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

2503
		/** @scrutinizer ignore-unhandled */ @apache_reset_timeout();

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
2504
2505
	if (time() - $start_time <= $timeLimitThreshold)
2506
		return;
2507
2508
	// Do we have some custom step progress stuff?
2509
	if (!empty($step_progress))
2510
	{
2511
		$upcontext['substep_progress'] = 0;
2512
		$upcontext['substep_progress_name'] = $step_progress['name'];
2513
		if ($step_progress['current'] > $step_progress['total'])
2514
			$upcontext['substep_progress'] = 99.9;
2515
		else
2516
			$upcontext['substep_progress'] = ($step_progress['current'] / $step_progress['total']) * 100;
2517
2518
		// Make it nicely rounded.
2519
		$upcontext['substep_progress'] = round($upcontext['substep_progress'], 1);
2520
	}
2521
2522
	// If this is XML we just exit right away!
2523
	if (isset($_GET['xml']))
2524
		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...
2525
2526
	// We're going to pause after this!
2527
	$upcontext['pause'] = true;
2528
2529
	$upcontext['query_string'] = '';
2530
	foreach ($_GET as $k => $v)
2531
	{
2532
		if ($k != 'data' && $k != 'substep' && $k != 'step')
2533
			$upcontext['query_string'] .= ';' . $k . '=' . $v;
2534
	}
2535
2536
	// Custom warning?
2537
	if (!empty($custom_warning))
2538
		$upcontext['custom_warning'] = $custom_warning;
2539
2540
	upgradeExit();
2541
}
2542
2543
function cmdStep0()
2544
{
2545
	global $boarddir, $sourcedir, $modSettings, $start_time, $cachedir, $databases, $db_type, $smcFunc, $upcontext;
2546
	global $is_debug;
2547
	$start_time = time();
2548
2549
	ob_end_clean();
2550
	ob_implicit_flush(true);
0 ignored issues
show
Bug introduced by
true of type true is incompatible with the type integer expected by parameter $flag of ob_implicit_flush(). ( Ignorable by Annotation )

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

2550
	ob_implicit_flush(/** @scrutinizer ignore-type */ true);
Loading history...
2551
	@set_time_limit(600);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for set_time_limit(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

2551
	/** @scrutinizer ignore-unhandled */ @set_time_limit(600);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
2552
2553
	if (!isset($_SERVER['argv']))
2554
		$_SERVER['argv'] = array();
2555
	$_GET['maint'] = 1;
2556
2557
	foreach ($_SERVER['argv'] as $i => $arg)
2558
	{
2559
		if (preg_match('~^--language=(.+)$~', $arg, $match) != 0)
2560
			$upcontext['lang'] = $match[1];
2561
		elseif (preg_match('~^--path=(.+)$~', $arg) != 0)
2562
			continue;
2563
		elseif ($arg == '--no-maintenance')
2564
			$_GET['maint'] = 0;
2565
		elseif ($arg == '--debug')
2566
			$is_debug = true;
2567
		elseif ($arg == '--backup')
2568
			$_POST['backup'] = 1;
2569
		elseif ($arg == '--template' && (file_exists($boarddir . '/template.php') || file_exists($boarddir . '/template.html') && !file_exists($modSettings['theme_dir'] . '/converted')))
2570
			$_GET['conv'] = 1;
2571
		elseif ($i != 0)
2572
		{
2573
			echo 'SMF Command-line Upgrader
2574
Usage: /path/to/php -f ' . basename(__FILE__) . ' -- [OPTION]...
2575
2576
	--language=LANG         Reset the forum\'s language to LANG.
2577
	--no-maintenance        Don\'t put the forum into maintenance mode.
2578
	--debug                 Output debugging information.
2579
	--backup                Create backups of tables with "backup_" prefix.';
2580
			echo "\n";
2581
			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...
2582
		}
2583
	}
2584
2585
	if (!php_version_check())
2586
		print_error('Error: PHP ' . PHP_VERSION . ' does not match version requirements.', true);
2587
	if (!db_version_check())
2588
		print_error('Error: ' . $databases[$db_type]['name'] . ' ' . $databases[$db_type]['version'] . ' does not match minimum requirements.', true);
2589
2590
	// Do some checks to make sure they have proper privileges
2591
	db_extend('packages');
2592
2593
	// CREATE
2594
	$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');
2595
2596
	// ALTER
2597
	$alter = $smcFunc['db_add_column']('{db_prefix}priv_check', array('name' => 'txt', 'type' => 'varchar', 'size' => 4, 'null' => false, 'default' => ''));
2598
2599
	// DROP
2600
	$drop = $smcFunc['db_drop_table']('{db_prefix}priv_check');
2601
2602
	// Sorry... we need CREATE, ALTER and DROP
2603
	if (!$create || !$alter || !$drop)
2604
		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);
2605
2606
	$check = @file_exists($modSettings['theme_dir'] . '/index.template.php')
2607
		&& @file_exists($sourcedir . '/QueryString.php')
2608
		&& @file_exists($sourcedir . '/ManageBoards.php');
2609
	if (!$check && !isset($modSettings['smfVersion']))
2610
		print_error('Error: Some files are missing or out-of-date.', true);
2611
2612
	// Do a quick version spot check.
2613
	$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

2613
	$temp = substr(@implode('', /** @scrutinizer ignore-type */ @file($boarddir . '/index.php')), 0, 4096);
Loading history...
2614
	preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $temp, $match);
2615
	if (empty($match[1]) || (trim($match[1]) != SMF_VERSION))
2616
		print_error('Error: Some files have not yet been updated properly.');
2617
2618
	// Make sure Settings.php is writable.
2619
	quickFileWritable($boarddir . '/Settings.php');
2620
	if (!is_writable($boarddir . '/Settings.php'))
2621
		print_error('Error: Unable to obtain write access to "Settings.php".', true);
2622
2623
	// Make sure Settings_bak.php is writable.
2624
	quickFileWritable($boarddir . '/Settings_bak.php');
2625
	if (!is_writable($boarddir . '/Settings_bak.php'))
2626
		print_error('Error: Unable to obtain write access to "Settings_bak.php".');
2627
2628
	if (isset($modSettings['agreement']) && (!is_writable($boarddir) || file_exists($boarddir . '/agreement.txt')) && !is_writable($boarddir . '/agreement.txt'))
2629
		print_error('Error: Unable to obtain write access to "agreement.txt".');
2630
	elseif (isset($modSettings['agreement']))
2631
	{
2632
		$fp = fopen($boarddir . '/agreement.txt', 'w');
2633
		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

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

2634
		fclose(/** @scrutinizer ignore-type */ $fp);
Loading history...
2635
	}
2636
2637
	// Make sure Themes is writable.
2638
	quickFileWritable($modSettings['theme_dir']);
2639
2640
	if (!is_writable($modSettings['theme_dir']) && !isset($modSettings['smfVersion']))
2641
		print_error('Error: Unable to obtain write access to "Themes".');
2642
2643
	// Make sure cache directory exists and is writable!
2644
	$cachedir_temp = empty($cachedir) ? $boarddir . '/cache' : $cachedir;
2645
	if (!file_exists($cachedir_temp))
2646
		@mkdir($cachedir_temp);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for mkdir(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

2646
		/** @scrutinizer ignore-unhandled */ @mkdir($cachedir_temp);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
2647
2648
	// Make sure the cache temp dir is writable.
2649
	quickFileWritable($cachedir_temp);
2650
2651
	if (!is_writable($cachedir_temp))
2652
		print_error('Error: Unable to obtain write access to "cache".', true);
2653
2654
	// Make sure db_last_error.php is writable.
2655
	quickFileWritable($cachedir_temp . '/db_last_error.php');
2656
	if (!is_writable($cachedir_temp . '/db_last_error.php'))
2657
		print_error('Error: Unable to obtain write access to "db_last_error.php".');
2658
2659
	if (!file_exists($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php'))
2660
		print_error('Error: Unable to find language files!', true);
2661
	else
2662
	{
2663
		$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php')), 0, 4096);
2664
		preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
2665
2666
		if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
2667
			print_error('Error: Language files out of date.', true);
2668
		if (!file_exists($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php'))
2669
			print_error('Error: Install language is missing for selected language.', true);
2670
2671
		// Otherwise include it!
2672
		require_once($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php');
2673
	}
2674
2675
	// Make sure we skip the HTML for login.
2676
	$_POST['upcont'] = true;
2677
	$upcontext['current_step'] = 1;
2678
}
2679
2680
/**
2681
 * Handles converting your database to UTF-8
2682
 */
2683
function ConvertUtf8()
2684
{
2685
	global $upcontext, $db_character_set, $sourcedir, $smcFunc, $modSettings, $language;
2686
	global $db_prefix, $db_type, $command_line, $support_js, $txt;
2687
2688
	// Done it already?
2689
	if (!empty($_POST['utf8_done']))
2690
		return true;
2691
2692
	// First make sure they aren't already on UTF-8 before we go anywhere...
2693
	if ($db_type == 'postgresql' || ($db_character_set === 'utf8' && !empty($modSettings['global_character_set']) && $modSettings['global_character_set'] === 'UTF-8'))
2694
	{
2695
		$smcFunc['db_insert']('replace',
2696
			'{db_prefix}settings',
2697
			array('variable' => 'string', 'value' => 'string'),
2698
			array(array('global_character_set', 'UTF-8')),
2699
			array('variable')
2700
		);
2701
2702
		return true;
2703
	}
2704
	else
2705
	{
2706
		$upcontext['page_title'] = $txt['converting_utf8'];
2707
		$upcontext['sub_template'] = isset($_GET['xml']) ? 'convert_xml' : 'convert_utf8';
2708
2709
		// The character sets used in SMF's language files with their db equivalent.
2710
		$charsets = array(
2711
			// Armenian
2712
			'armscii8' => 'armscii8',
2713
			// Chinese-traditional.
2714
			'big5' => 'big5',
2715
			// Chinese-simplified.
2716
			'gbk' => 'gbk',
2717
			// West European.
2718
			'ISO-8859-1' => 'latin1',
2719
			// Romanian.
2720
			'ISO-8859-2' => 'latin2',
2721
			// Turkish.
2722
			'ISO-8859-9' => 'latin5',
2723
			// Latvian
2724
			'ISO-8859-13' => 'latin7',
2725
			// West European with Euro sign.
2726
			'ISO-8859-15' => 'latin9',
2727
			// Thai.
2728
			'tis-620' => 'tis620',
2729
			// Persian, Chinese, etc.
2730
			'UTF-8' => 'utf8',
2731
			// Russian.
2732
			'windows-1251' => 'cp1251',
2733
			// Greek.
2734
			'windows-1253' => 'utf8',
2735
			// Hebrew.
2736
			'windows-1255' => 'utf8',
2737
			// Arabic.
2738
			'windows-1256' => 'cp1256',
2739
		);
2740
2741
		// Get a list of character sets supported by your MySQL server.
2742
		$request = $smcFunc['db_query']('', '
2743
			SHOW CHARACTER SET',
2744
			array(
2745
			)
2746
		);
2747
		$db_charsets = array();
2748
		while ($row = $smcFunc['db_fetch_assoc']($request))
2749
			$db_charsets[] = $row['Charset'];
2750
2751
		$smcFunc['db_free_result']($request);
2752
2753
		// Character sets supported by both MySQL and SMF's language files.
2754
		$charsets = array_intersect($charsets, $db_charsets);
2755
2756
		// Use the messages.body column as indicator for the database charset.
2757
		$request = $smcFunc['db_query']('', '
2758
			SHOW FULL COLUMNS
2759
			FROM {db_prefix}messages
2760
			LIKE {string:body_like}',
2761
			array(
2762
				'body_like' => 'body',
2763
			)
2764
		);
2765
		$column_info = $smcFunc['db_fetch_assoc']($request);
2766
		$smcFunc['db_free_result']($request);
2767
2768
		// A collation looks like latin1_swedish. We only need the character set.
2769
		list($upcontext['database_charset']) = explode('_', $column_info['Collation']);
2770
		$upcontext['database_charset'] = in_array($upcontext['database_charset'], $charsets) ? array_search($upcontext['database_charset'], $charsets) : $upcontext['database_charset'];
2771
2772
		// Detect whether a fulltext index is set.
2773
		$request = $smcFunc['db_query']('', '
2774
			SHOW INDEX
2775
			FROM {db_prefix}messages',
2776
			array(
2777
			)
2778
		);
2779
2780
		$upcontext['dropping_index'] = false;
2781
2782
		// If there's a fulltext index, we need to drop it first...
2783
		if ($request !== false || $smcFunc['db_num_rows']($request) != 0)
2784
		{
2785
			while ($row = $smcFunc['db_fetch_assoc']($request))
2786
				if ($row['Column_name'] == 'body' && (isset($row['Index_type']) && $row['Index_type'] == 'FULLTEXT' || isset($row['Comment']) && $row['Comment'] == 'FULLTEXT'))
2787
					$upcontext['fulltext_index'][] = $row['Key_name'];
2788
			$smcFunc['db_free_result']($request);
2789
2790
			if (isset($upcontext['fulltext_index']))
2791
				$upcontext['fulltext_index'] = array_unique($upcontext['fulltext_index']);
2792
		}
2793
2794
		// Drop it and make a note...
2795
		if (!empty($upcontext['fulltext_index']))
2796
		{
2797
			$upcontext['dropping_index'] = true;
2798
2799
			$smcFunc['db_query']('', '
2800
				ALTER TABLE {db_prefix}messages
2801
				DROP INDEX ' . implode(',
2802
				DROP INDEX ', $upcontext['fulltext_index']),
2803
				array(
2804
					'db_error_skip' => true,
2805
				)
2806
			);
2807
2808
			// Update the settings table
2809
			$smcFunc['db_insert']('replace',
2810
				'{db_prefix}settings',
2811
				array('variable' => 'string', 'value' => 'string'),
2812
				array('db_search_index', ''),
2813
				array('variable')
2814
			);
2815
		}
2816
2817
		// Figure out what charset we should be converting from...
2818
		$lang_charsets = array(
2819
			'arabic' => 'windows-1256',
2820
			'armenian_east' => 'armscii-8',
2821
			'armenian_west' => 'armscii-8',
2822
			'azerbaijani_latin' => 'ISO-8859-9',
2823
			'bangla' => 'UTF-8',
2824
			'belarusian' => 'ISO-8859-5',
2825
			'bulgarian' => 'windows-1251',
2826
			'cambodian' => 'UTF-8',
2827
			'chinese_simplified' => 'gbk',
2828
			'chinese_traditional' => 'big5',
2829
			'croation' => 'ISO-8859-2',
2830
			'czech' => 'ISO-8859-2',
2831
			'czech_informal' => 'ISO-8859-2',
2832
			'english_pirate' => 'UTF-8',
2833
			'esperanto' => 'ISO-8859-3',
2834
			'estonian' => 'ISO-8859-15',
2835
			'filipino_tagalog' => 'UTF-8',
2836
			'filipino_vasayan' => 'UTF-8',
2837
			'georgian' => 'UTF-8',
2838
			'greek' => 'ISO-8859-3',
2839
			'hebrew' => 'windows-1255',
2840
			'hungarian' => 'ISO-8859-2',
2841
			'irish' => 'UTF-8',
2842
			'japanese' => 'UTF-8',
2843
			'khmer' => 'UTF-8',
2844
			'korean' => 'UTF-8',
2845
			'kurdish_kurmanji' => 'ISO-8859-9',
2846
			'kurdish_sorani' => 'windows-1256',
2847
			'lao' => 'tis-620',
2848
			'latvian' => 'ISO-8859-13',
2849
			'lithuanian' => 'ISO-8859-4',
2850
			'macedonian' => 'UTF-8',
2851
			'malayalam' => 'UTF-8',
2852
			'mongolian' => 'UTF-8',
2853
			'nepali' => 'UTF-8',
2854
			'persian' => 'UTF-8',
2855
			'polish' => 'ISO-8859-2',
2856
			'romanian' => 'ISO-8859-2',
2857
			'russian' => 'windows-1252',
2858
			'sakha' => 'UTF-8',
2859
			'serbian_cyrillic' => 'ISO-8859-5',
2860
			'serbian_latin' => 'ISO-8859-2',
2861
			'sinhala' => 'UTF-8',
2862
			'slovak' => 'ISO-8859-2',
2863
			'slovenian' => 'ISO-8859-2',
2864
			'telugu' => 'UTF-8',
2865
			'thai' => 'tis-620',
2866
			'turkish' => 'ISO-8859-9',
2867
			'turkmen' => 'ISO-8859-9',
2868
			'ukranian' => 'windows-1251',
2869
			'urdu' => 'UTF-8',
2870
			'uzbek_cyrillic' => 'ISO-8859-5',
2871
			'uzbek_latin' => 'ISO-8859-5',
2872
			'vietnamese' => 'UTF-8',
2873
			'yoruba' => 'UTF-8'
2874
		);
2875
2876
		// Default to ISO-8859-1 unless we detected another supported charset
2877
		$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';
2878
2879
		$upcontext['charset_list'] = array_keys($charsets);
2880
2881
		// Translation table for the character sets not native for MySQL.
2882
		$translation_tables = array(
2883
			'windows-1255' => array(
2884
				'0x81' => '\'\'',		'0x8A' => '\'\'',		'0x8C' => '\'\'',
2885
				'0x8D' => '\'\'',		'0x8E' => '\'\'',		'0x8F' => '\'\'',
2886
				'0x90' => '\'\'',		'0x9A' => '\'\'',		'0x9C' => '\'\'',
2887
				'0x9D' => '\'\'',		'0x9E' => '\'\'',		'0x9F' => '\'\'',
2888
				'0xCA' => '\'\'',		'0xD9' => '\'\'',		'0xDA' => '\'\'',
2889
				'0xDB' => '\'\'',		'0xDC' => '\'\'',		'0xDD' => '\'\'',
2890
				'0xDE' => '\'\'',		'0xDF' => '\'\'',		'0xFB' => '0xD792',
2891
				'0xFC' => '0xE282AC',		'0xFF' => '0xD6B2',		'0xC2' => '0xFF',
2892
				'0x80' => '0xFC',		'0xE2' => '0xFB',		'0xA0' => '0xC2A0',
2893
				'0xA1' => '0xC2A1',		'0xA2' => '0xC2A2',		'0xA3' => '0xC2A3',
2894
				'0xA5' => '0xC2A5',		'0xA6' => '0xC2A6',		'0xA7' => '0xC2A7',
2895
				'0xA8' => '0xC2A8',		'0xA9' => '0xC2A9',		'0xAB' => '0xC2AB',
2896
				'0xAC' => '0xC2AC',		'0xAD' => '0xC2AD',		'0xAE' => '0xC2AE',
2897
				'0xAF' => '0xC2AF',		'0xB0' => '0xC2B0',		'0xB1' => '0xC2B1',
2898
				'0xB2' => '0xC2B2',		'0xB3' => '0xC2B3',		'0xB4' => '0xC2B4',
2899
				'0xB5' => '0xC2B5',		'0xB6' => '0xC2B6',		'0xB7' => '0xC2B7',
2900
				'0xB8' => '0xC2B8',		'0xB9' => '0xC2B9',		'0xBB' => '0xC2BB',
2901
				'0xBC' => '0xC2BC',		'0xBD' => '0xC2BD',		'0xBE' => '0xC2BE',
2902
				'0xBF' => '0xC2BF',		'0xD7' => '0xD7B3',		'0xD1' => '0xD781',
2903
				'0xD4' => '0xD7B0',		'0xD5' => '0xD7B1',		'0xD6' => '0xD7B2',
2904
				'0xE0' => '0xD790',		'0xEA' => '0xD79A',		'0xEC' => '0xD79C',
2905
				'0xED' => '0xD79D',		'0xEE' => '0xD79E',		'0xEF' => '0xD79F',
2906
				'0xF0' => '0xD7A0',		'0xF1' => '0xD7A1',		'0xF2' => '0xD7A2',
2907
				'0xF3' => '0xD7A3',		'0xF5' => '0xD7A5',		'0xF6' => '0xD7A6',
2908
				'0xF7' => '0xD7A7',		'0xF8' => '0xD7A8',		'0xF9' => '0xD7A9',
2909
				'0x82' => '0xE2809A',	'0x84' => '0xE2809E',	'0x85' => '0xE280A6',
2910
				'0x86' => '0xE280A0',	'0x87' => '0xE280A1',	'0x89' => '0xE280B0',
2911
				'0x8B' => '0xE280B9',	'0x93' => '0xE2809C',	'0x94' => '0xE2809D',
2912
				'0x95' => '0xE280A2',	'0x97' => '0xE28094',	'0x99' => '0xE284A2',
2913
				'0xC0' => '0xD6B0',		'0xC1' => '0xD6B1',		'0xC3' => '0xD6B3',
2914
				'0xC4' => '0xD6B4',		'0xC5' => '0xD6B5',		'0xC6' => '0xD6B6',
2915
				'0xC7' => '0xD6B7',		'0xC8' => '0xD6B8',		'0xC9' => '0xD6B9',
2916
				'0xCB' => '0xD6BB',		'0xCC' => '0xD6BC',		'0xCD' => '0xD6BD',
2917
				'0xCE' => '0xD6BE',		'0xCF' => '0xD6BF',		'0xD0' => '0xD780',
2918
				'0xD2' => '0xD782',		'0xE3' => '0xD793',		'0xE4' => '0xD794',
2919
				'0xE5' => '0xD795',		'0xE7' => '0xD797',		'0xE9' => '0xD799',
2920
				'0xFD' => '0xE2808E',	'0xFE' => '0xE2808F',	'0x92' => '0xE28099',
2921
				'0x83' => '0xC692',		'0xD3' => '0xD783',		'0x88' => '0xCB86',
2922
				'0x98' => '0xCB9C',		'0x91' => '0xE28098',	'0x96' => '0xE28093',
2923
				'0xBA' => '0xC3B7',		'0x9B' => '0xE280BA',	'0xAA' => '0xC397',
2924
				'0xA4' => '0xE282AA',	'0xE1' => '0xD791',		'0xE6' => '0xD796',
2925
				'0xE8' => '0xD798',		'0xEB' => '0xD79B',		'0xF4' => '0xD7A4',
2926
				'0xFA' => '0xD7AA',
2927
			),
2928
			'windows-1253' => array(
2929
				'0x81' => '\'\'',			'0x88' => '\'\'',			'0x8A' => '\'\'',
2930
				'0x8C' => '\'\'',			'0x8D' => '\'\'',			'0x8E' => '\'\'',
2931
				'0x8F' => '\'\'',			'0x90' => '\'\'',			'0x98' => '\'\'',
2932
				'0x9A' => '\'\'',			'0x9C' => '\'\'',			'0x9D' => '\'\'',
2933
				'0x9E' => '\'\'',			'0x9F' => '\'\'',			'0xAA' => '\'\'',
2934
				'0xD2' => '0xE282AC',			'0xFF' => '0xCE92',			'0xCE' => '0xCE9E',
2935
				'0xB8' => '0xCE88',		'0xBA' => '0xCE8A',		'0xBC' => '0xCE8C',
2936
				'0xBE' => '0xCE8E',		'0xBF' => '0xCE8F',		'0xC0' => '0xCE90',
2937
				'0xC8' => '0xCE98',		'0xCA' => '0xCE9A',		'0xCC' => '0xCE9C',
2938
				'0xCD' => '0xCE9D',		'0xCF' => '0xCE9F',		'0xDA' => '0xCEAA',
2939
				'0xE8' => '0xCEB8',		'0xEA' => '0xCEBA',		'0xEC' => '0xCEBC',
2940
				'0xEE' => '0xCEBE',		'0xEF' => '0xCEBF',		'0xC2' => '0xFF',
2941
				'0xBD' => '0xC2BD',		'0xED' => '0xCEBD',		'0xB2' => '0xC2B2',
2942
				'0xA0' => '0xC2A0',		'0xA3' => '0xC2A3',		'0xA4' => '0xC2A4',
2943
				'0xA5' => '0xC2A5',		'0xA6' => '0xC2A6',		'0xA7' => '0xC2A7',
2944
				'0xA8' => '0xC2A8',		'0xA9' => '0xC2A9',		'0xAB' => '0xC2AB',
2945
				'0xAC' => '0xC2AC',		'0xAD' => '0xC2AD',		'0xAE' => '0xC2AE',
2946
				'0xB0' => '0xC2B0',		'0xB1' => '0xC2B1',		'0xB3' => '0xC2B3',
2947
				'0xB5' => '0xC2B5',		'0xB6' => '0xC2B6',		'0xB7' => '0xC2B7',
2948
				'0xBB' => '0xC2BB',		'0xE2' => '0xCEB2',		'0x80' => '0xD2',
2949
				'0x82' => '0xE2809A',	'0x84' => '0xE2809E',	'0x85' => '0xE280A6',
2950
				'0x86' => '0xE280A0',	'0xA1' => '0xCE85',		'0xA2' => '0xCE86',
2951
				'0x87' => '0xE280A1',	'0x89' => '0xE280B0',	'0xB9' => '0xCE89',
2952
				'0x8B' => '0xE280B9',	'0x91' => '0xE28098',	'0x99' => '0xE284A2',
2953
				'0x92' => '0xE28099',	'0x93' => '0xE2809C',	'0x94' => '0xE2809D',
2954
				'0x95' => '0xE280A2',	'0x96' => '0xE28093',	'0x97' => '0xE28094',
2955
				'0x9B' => '0xE280BA',	'0xAF' => '0xE28095',	'0xB4' => '0xCE84',
2956
				'0xC1' => '0xCE91',		'0xC3' => '0xCE93',		'0xC4' => '0xCE94',
2957
				'0xC5' => '0xCE95',		'0xC6' => '0xCE96',		'0x83' => '0xC692',
2958
				'0xC7' => '0xCE97',		'0xC9' => '0xCE99',		'0xCB' => '0xCE9B',
2959
				'0xD0' => '0xCEA0',		'0xD1' => '0xCEA1',		'0xD3' => '0xCEA3',
2960
				'0xD4' => '0xCEA4',		'0xD5' => '0xCEA5',		'0xD6' => '0xCEA6',
2961
				'0xD7' => '0xCEA7',		'0xD8' => '0xCEA8',		'0xD9' => '0xCEA9',
2962
				'0xDB' => '0xCEAB',		'0xDC' => '0xCEAC',		'0xDD' => '0xCEAD',
2963
				'0xDE' => '0xCEAE',		'0xDF' => '0xCEAF',		'0xE0' => '0xCEB0',
2964
				'0xE1' => '0xCEB1',		'0xE3' => '0xCEB3',		'0xE4' => '0xCEB4',
2965
				'0xE5' => '0xCEB5',		'0xE6' => '0xCEB6',		'0xE7' => '0xCEB7',
2966
				'0xE9' => '0xCEB9',		'0xEB' => '0xCEBB',		'0xF0' => '0xCF80',
2967
				'0xF1' => '0xCF81',		'0xF2' => '0xCF82',		'0xF3' => '0xCF83',
2968
				'0xF4' => '0xCF84',		'0xF5' => '0xCF85',		'0xF6' => '0xCF86',
2969
				'0xF7' => '0xCF87',		'0xF8' => '0xCF88',		'0xF9' => '0xCF89',
2970
				'0xFA' => '0xCF8A',		'0xFB' => '0xCF8B',		'0xFC' => '0xCF8C',
2971
				'0xFD' => '0xCF8D',		'0xFE' => '0xCF8E',
2972
			),
2973
		);
2974
2975
		// Make some preparations.
2976
		if (isset($translation_tables[$upcontext['charset_detected']]))
2977
		{
2978
			$replace = '%field%';
2979
2980
			// Build a huge REPLACE statement...
2981
			foreach ($translation_tables[$upcontext['charset_detected']] as $from => $to)
2982
				$replace = 'REPLACE(' . $replace . ', ' . $from . ', ' . $to . ')';
2983
		}
2984
2985
		// Get a list of table names ahead of time... This makes it easier to set our substep and such
2986
		db_extend();
2987
		$queryTables = $smcFunc['db_list_tables'](false, $db_prefix . '%');
2988
2989
		$upcontext['table_count'] = count($queryTables);
2990
2991
		// What ones have we already done?
2992
		foreach ($queryTables as $id => $table)
2993
			if ($id < $_GET['substep'])
2994
				$upcontext['previous_tables'][] = $table;
2995
2996
		$upcontext['cur_table_num'] = $_GET['substep'];
2997
		$upcontext['cur_table_name'] = str_replace($db_prefix, '', $queryTables[$_GET['substep']]);
2998
		$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
2999
3000
		// Make sure we're ready & have painted the template before proceeding
3001
		if ($support_js && !isset($_GET['xml']))
3002
		{
3003
			$_GET['substep'] = 0;
3004
			return false;
3005
		}
3006
3007
		// We want to start at the first table.
3008
		for ($substep = $_GET['substep'], $n = count($queryTables); $substep < $n; $substep++)
3009
		{
3010
			$table = $queryTables[$substep];
3011
3012
			$getTableStatus = $smcFunc['db_query']('', '
3013
				SHOW TABLE STATUS
3014
				LIKE {string:table_name}',
3015
				array(
3016
					'table_name' => str_replace('_', '\_', $table)
3017
				)
3018
			);
3019
3020
			// Only one row so we can just fetch_assoc and free the result...
3021
			$table_info = $smcFunc['db_fetch_assoc']($getTableStatus);
3022
			$smcFunc['db_free_result']($getTableStatus);
3023
3024
			$upcontext['cur_table_name'] = str_replace($db_prefix, '', (isset($queryTables[$substep + 1]) ? $queryTables[$substep + 1] : $queryTables[$substep]));
3025
			$upcontext['cur_table_num'] = $substep + 1;
3026
			$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
3027
3028
			// Do we need to pause?
3029
			nextSubstep($substep);
3030
3031
			// Just to make sure it doesn't time out.
3032
			if (function_exists('apache_reset_timeout'))
3033
				@apache_reset_timeout();
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for apache_reset_timeout(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

3033
				/** @scrutinizer ignore-unhandled */ @apache_reset_timeout();

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
3034
3035
			$table_charsets = array();
3036
3037
			// Loop through each column.
3038
			$queryColumns = $smcFunc['db_query']('', '
3039
				SHOW FULL COLUMNS
3040
				FROM ' . $table_info['Name'],
3041
				array(
3042
				)
3043
			);
3044
			while ($column_info = $smcFunc['db_fetch_assoc']($queryColumns))
3045
			{
3046
				// Only text'ish columns have a character set and need converting.
3047
				if (strpos($column_info['Type'], 'text') !== false || strpos($column_info['Type'], 'char') !== false)
3048
				{
3049
					$collation = empty($column_info['Collation']) || $column_info['Collation'] === 'NULL' ? $table_info['Collation'] : $column_info['Collation'];
3050
					if (!empty($collation) && $collation !== 'NULL')
3051
					{
3052
						list($charset) = explode('_', $collation);
3053
3054
						// Build structure of columns to operate on organized by charset; only operate on columns not yet utf8
3055
						if ($charset != 'utf8')
3056
						{
3057
							if (!isset($table_charsets[$charset]))
3058
								$table_charsets[$charset] = array();
3059
3060
							$table_charsets[$charset][] = $column_info;
3061
						}
3062
					}
3063
				}
3064
			}
3065
			$smcFunc['db_free_result']($queryColumns);
3066
3067
			// Only change the non-utf8 columns identified above
3068
			if (count($table_charsets) > 0)
3069
			{
3070
				$updates_blob = '';
3071
				$updates_text = '';
3072
				foreach ($table_charsets as $charset => $columns)
3073
				{
3074
					if ($charset !== $charsets[$upcontext['charset_detected']])
3075
					{
3076
						foreach ($columns as $column)
3077
						{
3078
							$updates_blob .= '
3079
								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'] . '\'') . ',';
3080
							$updates_text .= '
3081
								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'] . '\'') . ',';
3082
						}
3083
					}
3084
				}
3085
3086
				// Change the columns to binary form.
3087
				$smcFunc['db_query']('', '
3088
					ALTER TABLE {raw:table_name}{raw:updates_blob}',
3089
					array(
3090
						'table_name' => $table_info['Name'],
3091
						'updates_blob' => substr($updates_blob, 0, -1),
3092
					)
3093
				);
3094
3095
				// Convert the character set if MySQL has no native support for it.
3096
				if (isset($translation_tables[$upcontext['charset_detected']]))
3097
				{
3098
					$update = '';
3099
					foreach ($table_charsets as $charset => $columns)
3100
						foreach ($columns as $column)
3101
							$update .= '
3102
								' . $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...
3103
3104
					$smcFunc['db_query']('', '
3105
						UPDATE {raw:table_name}
3106
						SET {raw:updates}',
3107
						array(
3108
							'table_name' => $table_info['Name'],
3109
							'updates' => substr($update, 0, -1),
3110
						)
3111
					);
3112
				}
3113
3114
				// Change the columns back, but with the proper character set.
3115
				$smcFunc['db_query']('', '
3116
					ALTER TABLE {raw:table_name}{raw:updates_text}',
3117
					array(
3118
						'table_name' => $table_info['Name'],
3119
						'updates_text' => substr($updates_text, 0, -1),
3120
					)
3121
				);
3122
			}
3123
3124
			// Now do the actual conversion (if still needed).
3125
			if ($charsets[$upcontext['charset_detected']] !== 'utf8')
3126
			{
3127
				if ($command_line)
3128
					echo 'Converting table ' . $table_info['Name'] . ' to UTF-8...';
3129
3130
				$smcFunc['db_query']('', '
3131
					ALTER TABLE {raw:table_name}
3132
					CONVERT TO CHARACTER SET utf8',
3133
					array(
3134
						'table_name' => $table_info['Name'],
3135
					)
3136
				);
3137
3138
				if ($command_line)
3139
					echo " done.\n";
3140
			}
3141
			// If this is XML to keep it nice for the user do one table at a time anyway!
3142
			if (isset($_GET['xml']) && $upcontext['cur_table_num'] < $upcontext['table_count'])
3143
				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...
3144
		}
3145
3146
		$prev_charset = empty($translation_tables[$upcontext['charset_detected']]) ? $charsets[$upcontext['charset_detected']] : $translation_tables[$upcontext['charset_detected']];
3147
3148
		$smcFunc['db_insert']('replace',
3149
			'{db_prefix}settings',
3150
			array('variable' => 'string', 'value' => 'string'),
3151
			array(array('global_character_set', 'UTF-8'), array('previousCharacterSet', $prev_charset)),
3152
			array('variable')
3153
		);
3154
3155
		// Store it in Settings.php too because it's needed before db connection.
3156
		// Hopefully this works...
3157
		require_once($sourcedir . '/Subs-Admin.php');
3158
		updateSettingsFile(array('db_character_set' => '\'utf8\''));
3159
3160
		// The conversion might have messed up some serialized strings. Fix them!
3161
		$request = $smcFunc['db_query']('', '
3162
			SELECT id_action, extra
3163
			FROM {db_prefix}log_actions
3164
			WHERE action IN ({string:remove}, {string:delete})',
3165
			array(
3166
				'remove' => 'remove',
3167
				'delete' => 'delete',
3168
			)
3169
		);
3170
		while ($row = $smcFunc['db_fetch_assoc']($request))
3171
		{
3172
			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)
3173
				$smcFunc['db_query']('', '
3174
					UPDATE {db_prefix}log_actions
3175
					SET extra = {string:extra}
3176
					WHERE id_action = {int:current_action}',
3177
					array(
3178
						'current_action' => $row['id_action'],
3179
						'extra' => $matches[1] . strlen($matches[3]) . ':"' . $matches[3] . '"' . $matches[4],
3180
					)
3181
				);
3182
		}
3183
		$smcFunc['db_free_result']($request);
3184
3185
		if ($upcontext['dropping_index'] && $command_line)
3186
		{
3187
			echo "\n" . '', $txt['upgrade_fulltext_error'], '';
3188
			flush();
3189
		}
3190
	}
3191
	$_GET['substep'] = 0;
3192
	return false;
3193
}
3194
3195
/**
3196
 * Attempts to repair corrupted serialized data strings
3197
 *
3198
 * @param string $string Serialized data that has been corrupted
3199
 * @return string|bool A working version of the serialized data, or the original if the repair failed
3200
 */
3201
function fix_serialized_data($string)
3202
{
3203
	// If its not broken, don't fix it.
3204
	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...
3205
		return $string;
3206
3207
	// This bit fixes incorrect string lengths, which can happen if the character encoding was changed (e.g. conversion to UTF-8)
3208
	$new_string = preg_replace_callback('~\bs:(\d+):"(.*?)";(?=$|[bidsa]:|[{}]|N;)~s', function ($matches) {return 's:' . strlen($matches[2]) . ':"' . $matches[2] . '";';}, $string);
3209
3210
	// @todo Add more possible fixes here. For example, fix incorrect array lengths, try to handle truncated strings gracefully, etc.
3211
3212
	// Did it work?
3213
	if (@safe_unserialize($new_string) !== false)
0 ignored issues
show
introduced by
The condition @safe_unserialize($new_string) !== false is always false.
Loading history...
3214
		return $new_string;
3215
	else
3216
		return $string;
3217
}
3218
3219
function serialize_to_json()
3220
{
3221
	global $command_line, $smcFunc, $modSettings, $sourcedir, $upcontext, $support_js, $txt;
3222
3223
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'serialize_json_xml' : 'serialize_json';
3224
	// First thing's first - did we already do this?
3225
	if (!empty($modSettings['json_done']))
3226
	{
3227
		if ($command_line)
3228
			return ConvertUtf8();
3229
		else
3230
			return true;
3231
	}
3232
3233
	// Done it already - js wise?
3234
	if (!empty($_POST['json_done']))
3235
		return true;
3236
3237
	// List of tables affected by this function
3238
	// name => array('key', col1[,col2|true[,col3]])
3239
	// If 3rd item in array is true, it indicates that col1 could be empty...
3240
	$tables = array(
3241
		'background_tasks' => array('id_task', 'task_data'),
3242
		'log_actions' => array('id_action', 'extra'),
3243
		'log_online' => array('session', 'url'),
3244
		'log_packages' => array('id_install', 'db_changes', 'failed_steps', 'credits'),
3245
		'log_spider_hits' => array('id_hit', 'url'),
3246
		'log_subscribed' => array('id_sublog', 'pending_details'),
3247
		'pm_rules' => array('id_rule', 'criteria', 'actions'),
3248
		'qanda' => array('id_question', 'answers'),
3249
		'subscriptions' => array('id_subscribe', 'cost'),
3250
		'user_alerts' => array('id_alert', 'extra', true),
3251
		'user_drafts' => array('id_draft', 'to_list', true),
3252
		// These last two are a bit different - we'll handle those separately
3253
		'settings' => array(),
3254
		'themes' => array()
3255
	);
3256
3257
	// Set up some context stuff...
3258
	// Because we're not using numeric indices, we need this to figure out the current table name...
3259
	$keys = array_keys($tables);
3260
3261
	$upcontext['page_title'] = $txt['converting_json'];
3262
	$upcontext['table_count'] = count($keys);
3263
	$upcontext['cur_table_num'] = $_GET['substep'];
3264
	$upcontext['cur_table_name'] = isset($keys[$_GET['substep']]) ? $keys[$_GET['substep']] : $keys[0];
3265
	$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
3266
3267
	foreach ($keys as $id => $table)
3268
		if ($id < $_GET['substep'])
3269
			$upcontext['previous_tables'][] = $table;
3270
3271
	if ($command_line)
3272
		echo 'Converting data from serialize() to json_encode().';
3273
3274
	if (!$support_js || isset($_GET['xml']))
3275
	{
3276
		// Fix the data in each table
3277
		for ($substep = $_GET['substep']; $substep < $upcontext['table_count']; $substep++)
3278
		{
3279
			$upcontext['cur_table_name'] = isset($keys[$substep + 1]) ? $keys[$substep + 1] : $keys[$substep];
3280
			$upcontext['cur_table_num'] = $substep + 1;
3281
3282
			$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
3283
3284
			// Do we need to pause?
3285
			nextSubstep($substep);
3286
3287
			// Initialize a few things...
3288
			$where = '';
3289
			$vars = array();
3290
			$table = $keys[$substep];
3291
			$info = $tables[$table];
3292
3293
			// Now the fun - build our queries and all that fun stuff
3294
			if ($table == 'settings')
3295
			{
3296
				// Now a few settings...
3297
				$serialized_settings = array(
3298
					'attachment_basedirectories',
3299
					'attachmentUploadDir',
3300
					'cal_today_birthday',
3301
					'cal_today_event',
3302
					'cal_today_holiday',
3303
					'displayFields',
3304
					'last_attachments_directory',
3305
					'memberlist_cache',
3306
					'search_custom_index_config',
3307
					'spider_name_cache'
3308
				);
3309
3310
				// Loop through and fix these...
3311
				$new_settings = array();
3312
				if ($command_line)
3313
					echo "\n" . 'Fixing some settings...';
3314
3315
				foreach ($serialized_settings as $var)
3316
				{
3317
					if (isset($modSettings[$var]))
3318
					{
3319
						// Attempt to unserialize the setting
3320
						$temp = @safe_unserialize($modSettings[$var]);
3321
						// Maybe conversion to UTF-8 corrupted it
3322
						if ($temp === false)
3323
							$temp = @safe_unserialize(fix_serialized_data($modSettings[$var]));
3324
3325
						if (!$temp && $command_line)
3326
							echo "\n - Failed to unserialize the '" . $var . "' setting. Skipping.";
3327
						elseif ($temp !== false)
3328
							$new_settings[$var] = json_encode($temp);
3329
					}
3330
				}
3331
3332
				// Update everything at once
3333
				if (!function_exists('cache_put_data'))
3334
					require_once($sourcedir . '/Load.php');
3335
				updateSettings($new_settings, true);
3336
3337
				if ($command_line)
3338
					echo ' done.';
3339
			}
3340
			elseif ($table == 'themes')
3341
			{
3342
				// Finally, fix the admin prefs. Unfortunately this is stored per theme, but hopefully they only have one theme installed at this point...
3343
				$query = $smcFunc['db_query']('', '
3344
					SELECT id_member, id_theme, value FROM {db_prefix}themes
3345
					WHERE variable = {string:admin_prefs}',
3346
					array(
3347
						'admin_prefs' => 'admin_preferences'
3348
					)
3349
				);
3350
3351
				if ($smcFunc['db_num_rows']($query) != 0)
3352
				{
3353
					while ($row = $smcFunc['db_fetch_assoc']($query))
3354
					{
3355
						$temp = @safe_unserialize($row['value']);
3356
						if ($temp === false)
3357
							$temp = @safe_unserialize(fix_serialized_data($row['value']));
3358
3359
						if ($command_line)
3360
						{
3361
							if ($temp === false)
3362
								echo "\n" . 'Unserialize of admin_preferences for user ' . $row['id_member'] . ' failed. Skipping.';
3363
							else
3364
								echo "\n" . 'Fixing admin preferences...';
3365
						}
3366
3367
						if ($temp !== false)
3368
						{
3369
							$row['value'] = json_encode($temp);
3370
3371
							// Even though we have all values from the table, UPDATE is still faster than REPLACE
3372
							$smcFunc['db_query']('', '
3373
								UPDATE {db_prefix}themes
3374
								SET value = {string:prefs}
3375
								WHERE id_theme = {int:theme}
3376
									AND id_member = {int:member}
3377
									AND variable = {string:admin_prefs}',
3378
								array(
3379
									'prefs' => $row['value'],
3380
									'theme' => $row['id_theme'],
3381
									'member' => $row['id_member'],
3382
									'admin_prefs' => 'admin_preferences'
3383
								)
3384
							);
3385
3386
							if ($command_line)
3387
								echo ' done.';
3388
						}
3389
					}
3390
3391
					$smcFunc['db_free_result']($query);
3392
				}
3393
			}
3394
			else
3395
			{
3396
				// First item is always the key...
3397
				$key = $info[0];
3398
				unset($info[0]);
3399
3400
				// Now we know what columns we have and such...
3401
				if (count($info) == 2 && $info[2] === true)
3402
				{
3403
					$col_select = $info[1];
3404
					$where = ' WHERE ' . $info[1] . ' != {empty}';
3405
				}
3406
				else
3407
				{
3408
					$col_select = implode(', ', $info);
3409
				}
3410
3411
				$query = $smcFunc['db_query']('', '
3412
					SELECT ' . $key . ', ' . $col_select . '
3413
					FROM {db_prefix}' . $table . $where,
3414
					array()
3415
				);
3416
3417
				if ($smcFunc['db_num_rows']($query) != 0)
3418
				{
3419
					if ($command_line)
3420
					{
3421
						echo "\n" . ' +++ Fixing the "' . $table . '" table...';
3422
						flush();
3423
					}
3424
3425
					while ($row = $smcFunc['db_fetch_assoc']($query))
3426
					{
3427
						$update = '';
3428
3429
						// We already know what our key is...
3430
						foreach ($info as $col)
3431
						{
3432
							if ($col !== true && $row[$col] != '')
3433
							{
3434
								$temp = @safe_unserialize($row[$col]);
3435
								if ($temp === false)
3436
									$temp = @safe_unserialize(fix_serialized_data($row[$col]));
3437
3438
								if ($temp === false && $command_line)
3439
								{
3440
									echo "\nFailed to unserialize " . $row[$col] . "... Skipping\n";
3441
								}
3442
								// If unserialize failed, it's better to leave the data alone than to overwrite it with an empty JSON value
3443
								elseif ($temp !== false)
3444
								{
3445
									$row[$col] = json_encode($temp);
3446
3447
									// Build our SET string and variables array
3448
									$update .= (empty($update) ? '' : ', ') . $col . ' = {string:' . $col . '}';
3449
									$vars[$col] = $row[$col];
3450
								}
3451
							}
3452
						}
3453
3454
						$vars[$key] = $row[$key];
3455
3456
						// In a few cases, we might have empty data, so don't try to update in those situations...
3457
						if (!empty($update))
3458
						{
3459
							$smcFunc['db_query']('', '
3460
								UPDATE {db_prefix}' . $table . '
3461
								SET ' . $update . '
3462
								WHERE ' . $key . ' = {' . ($key == 'session' ? 'string' : 'int') . ':' . $key . '}',
3463
								$vars
3464
							);
3465
						}
3466
					}
3467
3468
					if ($command_line)
3469
						echo ' done.';
3470
3471
					// Free up some memory...
3472
					$smcFunc['db_free_result']($query);
3473
				}
3474
			}
3475
			// If this is XML to keep it nice for the user do one table at a time anyway!
3476
			if (isset($_GET['xml']))
3477
				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...
3478
		}
3479
3480
		if ($command_line)
3481
		{
3482
			echo "\n" . 'Successful.' . "\n";
3483
			flush();
3484
		}
3485
		$upcontext['step_progress'] = 100;
3486
3487
		// Last but not least, insert a dummy setting so we don't have to do this again in the future...
3488
		updateSettings(array('json_done' => true));
3489
3490
		$_GET['substep'] = 0;
3491
		// Make sure we move on!
3492
		if ($command_line)
3493
			return ConvertUtf8();
3494
3495
		return true;
3496
	}
3497
3498
	// If this fails we just move on to deleting the upgrade anyway...
3499
	$_GET['substep'] = 0;
3500
	return false;
3501
}
3502
3503
/**
3504
 * As of 2.1, we want to store db_last_error.php in the cache
3505
 * To make that happen, Settings.php needs to ensure the $cachedir path is correct before trying to write to db_last_error.php
3506
 */
3507
function move_db_last_error_to_cachedir()
3508
{
3509
	$settings = file_get_contents(dirname(__FILE__) . '/Settings.php');
3510
3511
	$regex = <<<'EOT'
3512
(\s*#\s*Make\s+sure\s+the\s+paths\s+are\s+correct\.\.\.\s+at\s+least\s+try\s+to\s+fix\s+them\.\s+)?if\s*\(\!file_exists\(\$boarddir\)\s+&&\s+file_exists\(dirname\(__FILE__\)\s+\.\s+'/agreement\.txt'\)\)\s+\$boarddir\s*\=\s*dirname\(__FILE__\);\s+if\s*\(\!file_exists\(\$sourcedir\)\s+&&\s+file_exists\(\$boarddir\s*\.\s*'/Sources'\)\)\s+\$sourcedir\s*\=\s*\$boarddir\s*\.\s*'/Sources';\s+if\s*\(\!file_exists\(\$cachedir\)\s+&&\s+file_exists\(\$boarddir\s*\.\s*'/cache'\)\)\s+\$cachedir\s*\=\s*\$boarddir\s*\.\s*'/cache';
3513
EOT;
3514
3515
	$replacement = <<<'EOT'
3516
# Make sure the paths are correct... at least try to fix them.
3517
if (!file_exists($boarddir) && file_exists(dirname(__FILE__) . '/agreement.txt'))
3518
	$boarddir = dirname(__FILE__);
3519
if (!file_exists($sourcedir) && file_exists($boarddir . '/Sources'))
3520
	$sourcedir = $boarddir . '/Sources';
3521
if (!file_exists($cachedir) && file_exists($boarddir . '/cache'))
3522
	$cachedir = $boarddir . '/cache';
3523
3524
3525
EOT;
3526
3527
	if (preg_match('~' . $regex . '~', $settings) && preg_match('~(#+\s*Error-Catching\s*#+)~', $settings))
3528
	{
3529
		$settings = preg_replace('~' . $regex . '~', '', $settings);
3530
		$settings = preg_replace('~(#+\s*Error-Catching\s*#+)~', $replacement . '$1', $settings);
3531
		$settings = preg_replace('~dirname(__FILE__) . \'/db_last_error.php\'~', '(isset($cachedir) ? $cachedir : dirname(__FILE__)) . \'/db_last_error.php\'', $settings);
3532
3533
		// Blank out the file - done to fix a oddity with some servers.
3534
		file_put_contents(dirname(__FILE__) . '/Settings.php', '');
3535
3536
		file_put_contents(dirname(__FILE__) . '/Settings.php', $settings);
3537
	}
3538
}
3539
3540
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3541
						Templates are below this point
3542
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
3543
3544
// This is what is displayed if there's any chmod to be done. If not it returns nothing...
3545
function template_chmod()
3546
{
3547
	global $upcontext, $txt, $settings;
3548
3549
	// Don't call me twice!
3550
	if (!empty($upcontext['chmod_called']))
3551
		return;
3552
3553
	$upcontext['chmod_called'] = true;
3554
3555
	// Nothing?
3556
	if (empty($upcontext['chmod']['files']) && empty($upcontext['chmod']['ftp_error']))
3557
		return;
3558
3559
	// Was it a problem with Windows?
3560
	if (!empty($upcontext['chmod']['ftp_error']) && $upcontext['chmod']['ftp_error'] == 'total_mess')
3561
	{
3562
		echo '
3563
		<div class="error">
3564
			<p>', $txt['upgrade_writable_files'], '</p>
3565
			<ul class="error_content">
3566
				<li>' . implode('</li>
3567
				<li>', $upcontext['chmod']['files']) . '</li>
3568
			</ul>
3569
		</div>';
3570
3571
		return false;
3572
	}
3573
3574
	echo '
3575
		<div class="panel">
3576
			<h2>', $txt['upgrade_ftp_login'], '</h2>
3577
			<h3>', $txt['upgrade_ftp_perms'], '</h3>
3578
			<script>
3579
				function warning_popup()
3580
				{
3581
					popup = window.open(\'\',\'popup\',\'height=150,width=400,scrollbars=yes\');
3582
					var content = popup.document;
3583
					content.write(\'<!DOCTYPE html>\n\');
3584
					content.write(\'<html', $txt['lang_rtl'] == true ? ' dir="rtl"' : '', '>\n\t<head>\n\t\t<meta name="robots" content="noindex">\n\t\t\');
3585
					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\');
3586
					content.write(\'<div class="windowbg description">\n\t\t\t<h4>', $txt['upgrade_ftp_files'], '</h4>\n\t\t\t\');
3587
					content.write(\'<p>', implode('<br>\n\t\t\t', $upcontext['chmod']['files']), '</p>\n\t\t\t\');';
3588
3589
	if (isset($upcontext['systemos']) && $upcontext['systemos'] == 'linux')
3590
		echo '
3591
					content.write(\'<hr>\n\t\t\t\');
3592
					content.write(\'<p>', $txt['upgrade_ftp_shell'], '</p>\n\t\t\t\');
3593
					content.write(\'<tt># chmod a+w ', implode(' ', $upcontext['chmod']['files']), '</tt>\n\t\t\t\');';
3594
3595
	echo '
3596
					content.write(\'<a href="javascript:self.close();">close</a>\n\t\t</div>\n\t</body>\n</html>\');
3597
					content.close();
3598
				}
3599
			</script>';
3600
3601
	if (!empty($upcontext['chmod']['ftp_error']))
3602
		echo '
3603
			<div class="error_message red">
3604
				<p>', $txt['upgrade_ftp_error'], '<p>
3605
				<code>', $upcontext['chmod']['ftp_error'], '</code>
3606
			</div>';
3607
3608
	if (empty($upcontext['chmod_in_form']))
3609
		echo '
3610
			<form action="', $upcontext['form_url'], '" method="post">';
3611
3612
	echo '
3613
				<dl class="settings">
3614
					<dt>
3615
						<label for="ftp_server">', $txt['ftp_server'], ':</label>
3616
					</dt>
3617
					<dd>
3618
						<div class="floatright">
3619
							<label for="ftp_port" class="textbox"><strong>', $txt['ftp_port'], ':</strong></label>
3620
							<input type="text" size="3" name="ftp_port" id="ftp_port" value="', isset($upcontext['chmod']['port']) ? $upcontext['chmod']['port'] : '21', '">
3621
						</div>
3622
						<input type="text" size="30" name="ftp_server" id="ftp_server" value="', isset($upcontext['chmod']['server']) ? $upcontext['chmod']['server'] : 'localhost', '">
3623
						<div class="smalltext">', $txt['ftp_server_info'], '</div>
3624
					</dd>
3625
					<dt>
3626
						<label for="ftp_username">', $txt['ftp_username'], ':</label>
3627
					</dt>
3628
					<dd>
3629
						<input type="text" size="30" name="ftp_username" id="ftp_username" value="', isset($upcontext['chmod']['username']) ? $upcontext['chmod']['username'] : '', '">
3630
						<div class="smalltext">', $txt['ftp_username_info'], '</div>
3631
					</dd>
3632
					<dt>
3633
						<label for="ftp_password">', $txt['ftp_password'], ':</label>
3634
					</dt>
3635
					<dd>
3636
						<input type="password" size="30" name="ftp_password" id="ftp_password">
3637
						<div class="smalltext">', $txt['ftp_password_info'], '</div>
3638
					</dd>
3639
					<dt>
3640
						<label for="ftp_path">', $txt['ftp_path'], ':</label>
3641
					</dt>
3642
					<dd>
3643
						<input type="text" size="30" name="ftp_path" id="ftp_path" value="', isset($upcontext['chmod']['path']) ? $upcontext['chmod']['path'] : '', '">
3644
						<div class="smalltext">', !empty($upcontext['chmod']['path']) ? $txt['ftp_path_found_info'] : $txt['ftp_path_info'], '</div>
3645
					</dd>
3646
				</dl>
3647
3648
				<div class="righttext buttons">
3649
					<input type="submit" value="', $txt['ftp_connect'], '" class="button">
3650
				</div>';
3651
3652
	if (empty($upcontext['chmod_in_form']))
3653
		echo '
3654
			</form>';
3655
3656
	echo '
3657
		</div><!-- .panel -->';
3658
}
3659
3660
function template_upgrade_above()
3661
{
3662
	global $modSettings, $txt, $settings, $upcontext, $upgradeurl;
3663
3664
	echo '<!DOCTYPE html>
3665
<html', $txt['lang_rtl'] == true ? ' dir="rtl"' : '', '>
3666
<head>
3667
	<meta charset="', isset($txt['lang_character_set']) ? $txt['lang_character_set'] : 'UTF-8', '">
3668
	<meta name="robots" content="noindex">
3669
	<title>', $txt['upgrade_upgrade_utility'], '</title>
3670
	<link rel="stylesheet" href="', $settings['default_theme_url'], '/css/index.css">
3671
	<link rel="stylesheet" href="', $settings['default_theme_url'], '/css/install.css">
3672
	', $txt['lang_rtl'] == true ? '<link rel="stylesheet" href="' . $settings['default_theme_url'] . '/css/rtl.css">' : '', '
3673
	<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
3674
	<script src="', $settings['default_theme_url'], '/scripts/script.js"></script>
3675
	<script>
3676
		var smf_scripturl = \'', $upgradeurl, '\';
3677
		var smf_charset = \'', (empty($modSettings['global_character_set']) ? (empty($txt['lang_character_set']) ? 'UTF-8' : $txt['lang_character_set']) : $modSettings['global_character_set']), '\';
3678
		var startPercent = ', $upcontext['overall_percent'], ';
3679
3680
		// This function dynamically updates the step progress bar - and overall one as required.
3681
		function updateStepProgress(current, max, overall_weight)
3682
		{
3683
			// What out the actual percent.
3684
			var width = parseInt((current / max) * 100);
3685
			if (document.getElementById(\'step_progress\'))
3686
			{
3687
				document.getElementById(\'step_progress\').style.width = width + "%";
3688
				setInnerHTML(document.getElementById(\'step_text\'), width + "%");
3689
			}
3690
			if (overall_weight && document.getElementById(\'overall_progress\'))
3691
			{
3692
				overall_width = parseInt(startPercent + width * (overall_weight / 100));
3693
				document.getElementById(\'overall_progress\').style.width = overall_width + "%";
3694
				setInnerHTML(document.getElementById(\'overall_text\'), overall_width + "%");
3695
			}
3696
		}
3697
	</script>
3698
</head>
3699
<body>
3700
	<div id="footerfix">
3701
	<div id="header">
3702
		<h1 class="forumtitle">', $txt['upgrade_upgrade_utility'], '</h1>
3703
		<img id="smflogo" src="', $settings['default_theme_url'], '/images/smflogo.svg" alt="Simple Machines Forum" title="Simple Machines Forum">
3704
	</div>
3705
	<div id="wrapper">
3706
		<div id="upper_section">
3707
			<div id="inner_section">
3708
				<div id="inner_wrap">
3709
				</div>
3710
			</div>
3711
		</div>
3712
		<div id="content_section">
3713
			<div id="main_content_section">
3714
				<div id="main_steps">
3715
					<h2>', $txt['upgrade_progress'], '</h2>
3716
					<ul>';
3717
3718
	foreach ($upcontext['steps'] as $num => $step)
3719
		echo '
3720
						<li class="', $num < $upcontext['current_step'] ? 'stepdone' : ($num == $upcontext['current_step'] ? 'stepcurrent' : 'stepwaiting'), '">', $txt['upgrade_step'], ' ', $step[0], ': ', $txt[$step[1]], '</li>';
3721
3722
	echo '
3723
					</ul>
3724
				</div><!-- #main_steps -->
3725
3726
				<div id="install_progress">
3727
					<div id="progress_bar" class="progress_bar progress_green">
3728
						<h3>', $txt['upgrade_overall_progress'], '</h3>
3729
						<div id="overall_progress" class="bar" style="width: ', $upcontext['overall_percent'], '%;"></div>
3730
						<span id="overall_text">', $upcontext['overall_percent'], '%</span>
3731
					</div>';
3732
3733
	if (isset($upcontext['step_progress']))
3734
		echo '
3735
					<div id="progress_bar_step" class="progress_bar progress_yellow">
3736
						<h3>', $txt['upgrade_step_progress'], '</h3>
3737
						<div id="step_progress" class="bar" style="width: ', $upcontext['step_progress'], '%;"></div>
3738
						<span id="step_text">', $upcontext['step_progress'], '%</span>
3739
					</div>';
3740
3741
	echo '
3742
					<div id="substep_bar_div" class="progress_bar ', isset($upcontext['substep_progress']) ? '' : 'hidden', '">
3743
						<h3 id="substep_name">', isset($upcontext['substep_progress_name']) ? trim(strtr($upcontext['substep_progress_name'], array('.' => ''))) : '', '</h3>
3744
						<div id="substep_progress" class="bar" style="width: ', isset($upcontext['substep_progress']) ? $upcontext['substep_progress'] : 0, '%;"></div>
3745
						<span id="substep_text">', isset($upcontext['substep_progress']) ? $upcontext['substep_progress'] : 0, '%</span>
3746
					</div>';
3747
3748
	// How long have we been running this?
3749
	$elapsed = time() - $upcontext['started'];
3750
	$mins = (int) ($elapsed / 60);
3751
	$seconds = $elapsed - $mins * 60;
3752
	echo '
3753
					<div class="smalltext time_elapsed">
3754
						', $txt['upgrade_time_elapsed'], ':
3755
						<span id="mins_elapsed">', $mins, '</span> ', $txt['upgrade_time_mins'], ', <span id="secs_elapsed">', $seconds, '</span> ', $txt['upgrade_time_secs'], '.
3756
					</div>';
3757
	echo '
3758
				</div><!-- #install_progress -->
3759
			</div><!-- #main_content_section -->
3760
		</div><!-- #content_section -->
3761
		<div id="main_screen" class="clear">
3762
			<h2>', $upcontext['page_title'], '</h2>
3763
			<div class="panel">';
3764
}
3765
3766
function template_upgrade_below()
3767
{
3768
	global $upcontext, $txt;
3769
3770
	if (!empty($upcontext['pause']))
3771
		echo '
3772
					<em>', $txt['upgrade_incomplete'], '.</em><br>
3773
3774
					<h2 style="margin-top: 2ex;">', $txt['upgrade_not_quite_done'], '</h2>
3775
					<h3>
3776
						', $txt['upgrade_paused_overload'], '
3777
					</h3>';
3778
3779
	if (!empty($upcontext['custom_warning']))
3780
		echo '
3781
					<div class="errorbox">
3782
						<h3>', $txt['upgrade_note'], '</h3>
3783
						', $upcontext['custom_warning'], '
3784
					</div>';
3785
3786
	echo '
3787
					<div class="righttext" style="margin: 1ex;">';
3788
3789
	if (!empty($upcontext['continue']))
3790
		echo '
3791
						<input type="submit" id="contbutt" name="contbutt" value="', $txt['upgrade_continue'], '"', $upcontext['continue'] == 2 ? ' disabled' : '', ' class="button">';
3792
	if (!empty($upcontext['skip']))
3793
		echo '
3794
						<input type="submit" id="skip" name="skip" value="', $txt['upgrade_skip'], '" onclick="dontSubmit = true; document.getElementById(\'contbutt\').disabled = \'disabled\'; return true;" class="button">';
3795
3796
	echo '
3797
					</div>
3798
				</form>
3799
			</div><!-- .panel -->
3800
		</div><!-- #main_screen -->
3801
	</div><!-- #wrapper -->
3802
	</div><!-- #footerfix -->
3803
	<div id="footer">
3804
		<ul>
3805
			<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>
3806
		</ul>
3807
	</div>';
3808
3809
	// Are we on a pause?
3810
	if (!empty($upcontext['pause']))
3811
	{
3812
		echo '
3813
	<script>
3814
		window.onload = doAutoSubmit;
3815
		var countdown = 3;
3816
		var dontSubmit = false;
3817
3818
		function doAutoSubmit()
3819
		{
3820
			if (countdown == 0 && !dontSubmit)
3821
				document.upform.submit();
3822
			else if (countdown == -1)
3823
				return;
3824
3825
			document.getElementById(\'contbutt\').value = "', $txt['upgrade_continue'], ' (" + countdown + ")";
3826
			countdown--;
3827
3828
			setTimeout("doAutoSubmit();", 1000);
3829
		}
3830
	</script>';
3831
	}
3832
3833
	echo '
3834
</body>
3835
</html>';
3836
}
3837
3838
function template_xml_above()
3839
{
3840
	global $upcontext;
3841
3842
	echo '<', '?xml version="1.0" encoding="UTF-8"?', '>
3843
	<smf>';
3844
3845
	if (!empty($upcontext['get_data']))
3846
		foreach ($upcontext['get_data'] as $k => $v)
3847
			echo '
3848
		<get key="', $k, '">', $v, '</get>';
3849
}
3850
3851
function template_xml_below()
3852
{
3853
	echo '
3854
	</smf>';
3855
}
3856
3857
function template_error_message()
3858
{
3859
	global $upcontext;
3860
3861
	echo '
3862
	<div class="error_message red">
3863
		', $upcontext['error_msg'], '
3864
		<br>
3865
		<a href="', $_SERVER['PHP_SELF'], '">Click here to try again.</a>
3866
	</div>';
3867
}
3868
3869
function template_welcome_message()
3870
{
3871
	global $upcontext, $disable_security, $settings, $txt;
3872
3873
	echo '
3874
				<script src="https://www.simplemachines.org/smf/current-version.js?version=' . SMF_VERSION . '"></script>
3875
3876
				<h3>', sprintf($txt['upgrade_ready_proceed'], SMF_VERSION), '</h3>
3877
				<form action="', $upcontext['form_url'], '" method="post" name="upform" id="upform">
3878
					<input type="hidden" name="', $upcontext['login_token_var'], '" value="', $upcontext['login_token'], '">
3879
3880
					<div id="version_warning" class="noticebox hidden">
3881
						<h3>', $txt['upgrade_warning'], '</h3>
3882
						', sprintf($txt['upgrade_warning_out_of_date'], SMF_VERSION, 'https://www.simplemachines.org'), '
3883
					</div>';
3884
3885
	$upcontext['chmod_in_form'] = true;
3886
	template_chmod();
3887
3888
	// For large, pre 1.1 RC2 forums give them a warning about the possible impact of this upgrade!
3889
	if ($upcontext['is_large_forum'])
3890
		echo '
3891
					<div class="errorbox">
3892
						<h3>', $txt['upgrade_warning'], '</h3>
3893
						', $txt['upgrade_warning_lots_data'], '
3894
					</div>';
3895
3896
	// A warning message?
3897
	if (!empty($upcontext['warning']))
3898
		echo '
3899
					<div class="errorbox">
3900
						<h3>', $txt['upgrade_warning'], '</h3>
3901
						', $upcontext['warning'], '
3902
					</div>';
3903
3904
	// Paths are incorrect?
3905
	echo '
3906
					<div class="errorbox', (file_exists($settings['default_theme_dir'] . '/scripts/script.js') ? ' hidden' : ''), '" id="js_script_missing_error">
3907
						<h3>', $txt['upgrade_critical_error'], '</h3>
3908
						', sprintf($txt['upgrade_error_script_js'], 'https://www.simplemachines.org'), '
3909
					</div>';
3910
3911
	// Is there someone already doing this?
3912
	if (!empty($upcontext['user']['id']) && (time() - $upcontext['started'] < 72600 || time() - $upcontext['updated'] < 3600))
3913
	{
3914
		$ago = time() - $upcontext['started'];
3915
		$ago_hours = floor($ago / 3600);
3916
		$ago_minutes = intval(($ago / 60) % 60);
3917
		$ago_seconds = intval($ago % 60);
3918
		$agoTxt = $ago < 60 ? 'upgrade_time_ago_s' : ($ago < 3600 ? 'upgrade_time_ago_ms' : 'upgrade_time_ago_hms');
3919
3920
		$updated = time() - $upcontext['updated'];
3921
		$updated_hours = floor($updated / 3600);
3922
		$updated_minutes = intval(($updated / 60) % 60);
3923
		$updated_seconds = intval($updated % 60);
3924
		$updatedTxt = $updated < 60 ? 'upgrade_time_updated_s' : ($updated < 3600 ? 'upgrade_time_updated_hm' : 'upgrade_time_updated_hms');
3925
3926
		echo '
3927
					<div class="errorbox">
3928
						<h3>', $txt['upgrade_warning'], '</h3>
3929
						<p>', sprintf($txt['upgrade_time_user'], $upcontext['user']['name']), '</p>
3930
						<p>', sprintf($txt[$agoTxt], $ago_seconds, $ago_minutes, $ago_hours), '</p>
3931
						<p>', sprintf($txt[$updatedTxt], $updated_seconds, $updated_minutes, $updated_hours), '</p>';
3932
3933
		if ($updated < 600)
3934
			echo '
3935
						<p>', $txt['upgrade_run_script'], ' ', $upcontext['user']['name'], ' ', $txt['upgrade_run_script2'], '</p>';
3936
3937
		if ($updated > $upcontext['inactive_timeout'])
3938
			echo '
3939
						<p>', $txt['upgrade_run'], '</p>';
3940
		else
3941
			echo '
3942
						<p>', $txt['upgrade_script_timeout'], ' ', $upcontext['user']['name'], ' ', $txt['upgrade_script_timeout2'], ' ', ($upcontext['inactive_timeout'] > 120 ? round($upcontext['inactive_timeout'] / 60, 1) . ' minutes!' : $upcontext['inactive_timeout'] . ' seconds!'), '</p>';
3943
3944
		echo '
3945
					</div>';
3946
	}
3947
3948
	echo '
3949
					<strong>', $txt['upgrade_admin_login'], ' ', $disable_security ? '(DISABLED)' : '', '</strong>
3950
					<h3>', $txt['upgrade_sec_login'], '</h3>
3951
					<dl class="settings adminlogin">
3952
						<dt>
3953
							<label for="user"', $disable_security ? ' disabled' : '', '>', $txt['upgrade_username'], '</label>
3954
						</dt>
3955
						<dd>
3956
							<input type="text" name="user" value="', !empty($upcontext['username']) ? $upcontext['username'] : '', '"', $disable_security ? ' disabled' : '', '>';
3957
3958
	if (!empty($upcontext['username_incorrect']))
3959
		echo '
3960
							<div class="smalltext red">', $txt['upgrade_wrong_username'], '</div>';
3961
3962
	echo '
3963
						</dd>
3964
						<dt>
3965
							<label for="passwrd"', $disable_security ? ' disabled' : '', '>', $txt['upgrade_password'], '</label>
3966
						</dt>
3967
						<dd>
3968
							<input type="password" name="passwrd" value=""', $disable_security ? ' disabled' : '', '>
3969
							<input type="hidden" name="hash_passwrd" value="">';
3970
3971
	if (!empty($upcontext['password_failed']))
3972
		echo '
3973
							<div class="smalltext red">', $txt['upgrade_wrong_password'], '</div>';
3974
3975
	echo '
3976
						</dd>';
3977
3978
	// Can they continue?
3979
	if (!empty($upcontext['user']['id']) && time() - $upcontext['user']['updated'] >= $upcontext['inactive_timeout'] && $upcontext['user']['step'] > 1)
3980
	{
3981
		echo '
3982
						<dd>
3983
							<label for="cont"><input type="checkbox" id="cont" name="cont" checked>', $txt['upgrade_continue_step'], '</label>
3984
						</dd>';
3985
	}
3986
3987
	echo '
3988
					</dl>
3989
					<span class="smalltext">
3990
						', $txt['upgrade_bypass'], '
3991
					</span>
3992
					<input type="hidden" name="login_attempt" id="login_attempt" value="1">
3993
					<input type="hidden" name="js_works" id="js_works" value="0">';
3994
3995
	// Say we want the continue button!
3996
	$upcontext['continue'] = !empty($upcontext['user']['id']) && time() - $upcontext['user']['updated'] < $upcontext['inactive_timeout'] ? 2 : 1;
3997
3998
	// This defines whether javascript is going to work elsewhere :D
3999
	echo '
4000
					<script>
4001
						if (\'XMLHttpRequest\' in window && document.getElementById(\'js_works\'))
4002
							document.getElementById(\'js_works\').value = 1;
4003
4004
						// Latest version?
4005
						function smfCurrentVersion()
4006
						{
4007
							var smfVer, yourVer;
4008
4009
							if (!(\'smfVersion\' in window))
4010
								return;
4011
4012
							window.smfVersion = window.smfVersion.replace(/SMF\s?/g, \'\');
4013
4014
							smfVer = document.getElementById(\'smfVersion\');
4015
							yourVer = document.getElementById(\'yourVersion\');
4016
4017
							setInnerHTML(smfVer, window.smfVersion);
4018
4019
							var currentVersion = getInnerHTML(yourVer);
4020
							if (currentVersion < window.smfVersion)
4021
								document.getElementById(\'version_warning\').classList.remove(\'hidden\');
4022
						}
4023
						addLoadEvent(smfCurrentVersion);
4024
4025
						// This checks that the script file even exists!
4026
						if (typeof(smfSelectText) == \'undefined\')
4027
							document.getElementById(\'js_script_missing_error\').classList.remove(\'hidden\');
4028
4029
					</script>';
4030
}
4031
4032
function template_upgrade_options()
4033
{
4034
	global $upcontext, $modSettings, $db_prefix, $mmessage, $mtitle, $txt;
4035
4036
	echo '
4037
				<h3>', $txt['upgrade_areyouready'], '</h3>
4038
				<form action="', $upcontext['form_url'], '" method="post" name="upform" id="upform">';
4039
4040
	// Warning message?
4041
	if (!empty($upcontext['upgrade_options_warning']))
4042
		echo '
4043
				<div class="errorbox">
4044
					<h3>', $txt['upgrade_warning'], '</h3>
4045
					', $upcontext['upgrade_options_warning'], '
4046
				</div>';
4047
4048
	echo '
4049
				<ul class="upgrade_settings">
4050
					<li>
4051
						<input type="checkbox" name="backup" id="backup" value="1">
4052
						<label for="backup">', $txt['upgrade_backup_table'], ' &quot;backup_' . $db_prefix . '&quot;.</label>
4053
						(', $txt['upgrade_recommended'], ')
4054
					</li>
4055
					<li>
4056
						<input type="checkbox" name="maint" id="maint" value="1" checked>
4057
						<label for="maint">', $txt['upgrade_maintenance'], '</label>
4058
						<span class="smalltext">(<a href="javascript:void(0)" onclick="document.getElementById(\'mainmess\').classList.toggle(\'hidden\')">', $txt['upgrade_customize'], '</a>)</span>
4059
						<div id="mainmess" class="hidden">
4060
							<strong class="smalltext">', $txt['upgrade_maintenance_title'], ' </strong><br>
4061
							<input type="text" name="maintitle" size="30" value="', htmlspecialchars($mtitle), '"><br>
4062
							<strong class="smalltext">', $txt['upgrade_maintenance_message'], ' </strong><br>
4063
							<textarea name="mainmessage" rows="3" cols="50">', htmlspecialchars($mmessage), '</textarea>
4064
						</div>
4065
					</li>
4066
					<li>
4067
						<input type="checkbox" name="debug" id="debug" value="1">
4068
						<label for="debug">'.$txt['upgrade_debug_info'], '</label>
4069
					</li>
4070
					<li>
4071
						<input type="checkbox" name="empty_error" id="empty_error" value="1">
4072
						<label for="empty_error">', $txt['upgrade_empty_errorlog'], '</label>
4073
					</li>';
4074
4075
	if (!empty($upcontext['karma_installed']['good']) || !empty($upcontext['karma_installed']['bad']))
4076
		echo '
4077
					<li>
4078
						<input type="checkbox" name="delete_karma" id="delete_karma" value="1">
4079
						<label for="delete_karma">', $txt['upgrade_delete_karma'], '</label>
4080
					</li>';
4081
4082
	echo '
4083
					<li>
4084
						<input type="checkbox" name="stats" id="stats" value="1"', empty($modSettings['allow_sm_stats']) && empty($modSettings['enable_sm_stats']) ? '' : ' checked="checked"', '>
4085
						<label for="stat">
4086
							', $txt['upgrade_stats_collection'], '<br>
4087
							<span class="smalltext">', sprintf($txt['upgrade_stats_info'], 'https://www.simplemachines.org/about/stats.php'), '</a></span>
4088
						</label>
4089
					</li>
4090
					<li>
4091
						<input type="checkbox" name="migrateSettings" id="migrateSettings" value="1"', empty($upcontext['migrateSettingsNeeded']) ? '' : ' checked="checked"', '>
4092
						<label for="migrateSettings">
4093
							', $txt['upgrade_migrate_settings_file'], '
4094
						</label>
4095
					</li>
4096
				</ul>
4097
				<input type="hidden" name="upcont" value="1">';
4098
4099
	// We need a normal continue button here!
4100
	$upcontext['continue'] = 1;
4101
}
4102
4103
// Template for the database backup tool/
4104
function template_backup_database()
4105
{
4106
	global $upcontext, $support_js, $is_debug, $txt;
4107
4108
	echo '
4109
				<h3>', $txt['upgrade_wait'], '</h3>';
4110
4111
	echo '
4112
				<form action="', $upcontext['form_url'], '" name="upform" id="upform" method="post">
4113
					<input type="hidden" name="backup_done" id="backup_done" value="0">
4114
					<strong>', sprintf($txt['upgrade_completedtables_outof'], $upcontext['cur_table_num'], $upcontext['table_count']), '</strong>
4115
					<div id="debug_section">
4116
						<span id="debuginfo"></span>
4117
					</div>';
4118
4119
	// Dont any tables so far?
4120
	if (!empty($upcontext['previous_tables']))
4121
		foreach ($upcontext['previous_tables'] as $table)
4122
			echo '
4123
					<br>', $txt['upgrade_completed_table'], ' &quot;', $table, '&quot;.';
4124
4125
	echo '
4126
					<h3 id="current_tab">
4127
						', $txt['upgrade_current_table'], ' &quot;<span id="current_table">', $upcontext['cur_table_name'], '</span>&quot;
4128
					</h3>
4129
					<p id="commess" class="', $upcontext['cur_table_num'] == $upcontext['table_count'] ? 'inline_block' : 'hidden', '">Backup Complete! Click Continue to Proceed.</p>';
4130
4131
	// Continue please!
4132
	$upcontext['continue'] = $support_js ? 2 : 1;
4133
4134
	// If javascript allows we want to do this using XML.
4135
	if ($support_js)
4136
	{
4137
		echo '
4138
					<script>
4139
						var lastTable = ', $upcontext['cur_table_num'], ';
4140
						function getNextTables()
4141
						{
4142
							getXMLDocument(\'', $upcontext['form_url'], '&xml&substep=\' + lastTable, onBackupUpdate);
4143
						}
4144
4145
						// Got an update!
4146
						function onBackupUpdate(oXMLDoc)
4147
						{
4148
							var sCurrentTableName = "";
4149
							var iTableNum = 0;
4150
							var sCompletedTableName = getInnerHTML(document.getElementById(\'current_table\'));
4151
							for (var i = 0; i < oXMLDoc.getElementsByTagName("table")[0].childNodes.length; i++)
4152
								sCurrentTableName += oXMLDoc.getElementsByTagName("table")[0].childNodes[i].nodeValue;
4153
							iTableNum = oXMLDoc.getElementsByTagName("table")[0].getAttribute("num");
4154
4155
							// Update the page.
4156
							setInnerHTML(document.getElementById(\'tab_done\'), iTableNum);
4157
							setInnerHTML(document.getElementById(\'current_table\'), sCurrentTableName);
4158
							lastTable = iTableNum;
4159
							updateStepProgress(iTableNum, ', $upcontext['table_count'], ', ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');';
4160
4161
		// If debug flood the screen.
4162
		if ($is_debug)
4163
			echo '
4164
							setOuterHTML(document.getElementById(\'debuginfo\'), \'<br>Completed Table: &quot;\' + sCompletedTableName + \'&quot;.<span id="debuginfo"><\' + \'/span>\');
4165
4166
							if (document.getElementById(\'debug_section\').scrollHeight)
4167
								document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
4168
4169
		echo '
4170
							// Get the next update...
4171
							if (iTableNum == ', $upcontext['table_count'], ')
4172
							{
4173
								document.getElementById(\'commess\').classList.remove("hidden");
4174
								document.getElementById(\'current_tab\').classList.add("hidden");
4175
								document.getElementById(\'contbutt\').disabled = 0;
4176
								document.getElementById(\'backup_done\').value = 1;
4177
							}
4178
							else
4179
								getNextTables();
4180
						}
4181
						getNextTables();
4182
					//# sourceURL=dynamicScript-bkup.js
4183
					</script>';
4184
	}
4185
}
4186
4187
function template_backup_xml()
4188
{
4189
	global $upcontext;
4190
4191
	echo '
4192
		<table num="', $upcontext['cur_table_num'], '">', $upcontext['cur_table_name'], '</table>';
4193
}
4194
4195
// Here is the actual "make the changes" template!
4196
function template_database_changes()
4197
{
4198
	global $upcontext, $support_js, $is_debug, $timeLimitThreshold, $txt;
4199
4200
	if (empty($is_debug) && !empty($upcontext['upgrade_status']['debug']))
4201
		$is_debug = true;
4202
4203
	echo '
4204
				<h3>', $txt['upgrade_db_changes'], '</h3>
4205
				<h4><em>', $txt['upgrade_db_patient'], '</em></h4>';
4206
4207
	echo '
4208
				<form action="', $upcontext['form_url'], '&amp;filecount=', $upcontext['file_count'], '" name="upform" id="upform" method="post">
4209
					<input type="hidden" name="database_done" id="database_done" value="0">';
4210
4211
	// No javascript looks rubbish!
4212
	if (!$support_js)
4213
	{
4214
		foreach ($upcontext['actioned_items'] as $num => $item)
4215
		{
4216
			if ($num != 0)
4217
				echo ' Successful!';
4218
			echo '<br>' . $item;
4219
		}
4220
4221
		// Only tell deubbers how much time they wasted waiting for the upgrade because they don't have javascript.
4222
		if (!empty($upcontext['changes_complete']))
4223
		{
4224
			if ($is_debug)
4225
			{
4226
				$active = time() - $upcontext['started'];
4227
				$hours = floor($active / 3600);
4228
				$minutes = intval(($active / 60) % 60);
4229
				$seconds = intval($active % 60);
4230
4231
				echo '', sprintf($txt['upgrade_success_time_db'], $seconds, $minutes, $hours), '<br>';
4232
			}
4233
			else
4234
				echo '', $txt['upgrade_success'], '<br>';
4235
4236
			echo '
4237
					<p id="commess">', $txt['upgrade_db_complete'], '</p>';
4238
		}
4239
	}
4240
	else
4241
	{
4242
		// Tell them how many files we have in total.
4243
		if ($upcontext['file_count'] > 1)
4244
			echo '
4245
					<strong id="info1">', $txt['upgrade_script'], ' <span id="file_done">', $upcontext['cur_file_num'], '</span> of ', $upcontext['file_count'], '.</strong>';
4246
4247
		echo '
4248
					<h3 id="info2">
4249
						<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>
4250
					</h3>
4251
					<p id="commess" class="', !empty($upcontext['changes_complete']) || $upcontext['current_debug_item_num'] == $upcontext['debug_items'] ? 'inline_block' : 'hidden', '">', $txt['upgrade_db_complete2'], '</p>';
4252
4253
		if ($is_debug)
4254
		{
4255
			// Let our debuggers know how much time was spent, but not wasted since JS handled refreshing the page!
4256
			if ($upcontext['current_debug_item_num'] == $upcontext['debug_items'])
4257
			{
4258
				$active = time() - $upcontext['started'];
4259
				$hours = floor($active / 3600);
4260
				$minutes = intval(($active / 60) % 60);
4261
				$seconds = intval($active % 60);
4262
4263
				echo '
4264
					<p id="upgradeCompleted">', sprintf($txt['upgrade_success_time_db'], $seconds, $minutes, $hours), '</p>';
4265
			}
4266
			else
4267
				echo '
4268
					<p id="upgradeCompleted"></p>';
4269
4270
			echo '
4271
					<div id="debug_section">
4272
						<span id="debuginfo"></span>
4273
					</div>';
4274
		}
4275
	}
4276
4277
	// Place for the XML error message.
4278
	echo '
4279
					<div id="error_block" class="errorbox', empty($upcontext['error_message']) ? ' hidden' : '', '">
4280
						<h3>', $txt['upgrade_error'], '</h3>
4281
						<div id="error_message">', isset($upcontext['error_message']) ? $upcontext['error_message'] : $txt['upgrade_unknown_error'], '</div>
4282
					</div>';
4283
4284
	// We want to continue at some point!
4285
	$upcontext['continue'] = $support_js ? 2 : 1;
4286
4287
	// If javascript allows we want to do this using XML.
4288
	if ($support_js)
4289
	{
4290
		echo '
4291
					<script>
4292
						var lastItem = ', $upcontext['current_debug_item_num'], ';
4293
						var sLastString = "', strtr($upcontext['current_debug_item_name'], array('"' => '&quot;')), '";
4294
						var iLastSubStepProgress = -1;
4295
						var curFile = ', $upcontext['cur_file_num'], ';
4296
						var totalItems = 0;
4297
						var prevFile = 0;
4298
						var retryCount = 0;
4299
						var testvar = 0;
4300
						var timeOutID = 0;
4301
						var getData = "";
4302
						var debugItems = ', $upcontext['debug_items'], ';';
4303
4304
		if ($is_debug)
4305
			echo '
4306
						var upgradeStartTime = ' . $upcontext['started'] . ';';
4307
4308
		echo '
4309
						function getNextItem()
4310
						{
4311
							// We want to track this...
4312
							if (timeOutID)
4313
								clearTimeout(timeOutID);
4314
							timeOutID = window.setTimeout("retTimeout()", ', (10 * $timeLimitThreshold), '000);
4315
4316
							getXMLDocument(\'', $upcontext['form_url'], '&xml&filecount=', $upcontext['file_count'], '&substep=\' + lastItem + getData, onItemUpdate);
4317
						}
4318
4319
						// Got an update!
4320
						function onItemUpdate(oXMLDoc)
4321
						{
4322
							var sItemName = "";
4323
							var sDebugName = "";
4324
							var iItemNum = 0;
4325
							var iSubStepProgress = -1;
4326
							var iDebugNum = 0;
4327
							var bIsComplete = 0;
4328
							var bSkipped = 0;
4329
							getData = "";
4330
4331
							// We\'ve got something - so reset the timeout!
4332
							if (timeOutID)
4333
								clearTimeout(timeOutID);
4334
4335
							// Assume no error at this time...
4336
							document.getElementById("error_block").classList.add("hidden");
4337
4338
							// Are we getting some duff info?
4339
							if (!oXMLDoc.getElementsByTagName("item")[0])
4340
							{
4341
								// Too many errors?
4342
								if (retryCount > 15)
4343
								{
4344
									document.getElementById("error_block").classList.remove("hidden");
4345
									setInnerHTML(document.getElementById("error_message"), "Error retrieving information on step: " + (sDebugName == "" ? sLastString : sDebugName));';
4346
4347
		if ($is_debug)
4348
			echo '
4349
									setOuterHTML(document.getElementById(\'debuginfo\'), \'<span class="red">failed<\' + \'/span><span id="debuginfo"><\' + \'/span>\');';
4350
4351
		echo '
4352
								}
4353
								else
4354
								{
4355
									retryCount++;
4356
									getNextItem();
4357
								}
4358
								return false;
4359
							}
4360
4361
							// Never allow loops.
4362
							if (curFile == prevFile)
4363
							{
4364
								retryCount++;
4365
								if (retryCount > 10)
4366
								{
4367
									document.getElementById("error_block").classList.remove("hidden");
4368
									setInnerHTML(document.getElementById("error_message"), "', $txt['upgrade_loop'], '" + sDebugName);';
4369
4370
		if ($is_debug)
4371
			echo '
4372
									setOuterHTML(document.getElementById(\'debuginfo\'), \'<span class="red">failed<\' + \'/span><span id="debuginfo"><\' + \'/span>\');';
4373
4374
		echo '
4375
								}
4376
							}
4377
							retryCount = 0;
4378
4379
							for (var i = 0; i < oXMLDoc.getElementsByTagName("item")[0].childNodes.length; i++)
4380
								sItemName += oXMLDoc.getElementsByTagName("item")[0].childNodes[i].nodeValue;
4381
							for (var i = 0; i < oXMLDoc.getElementsByTagName("debug")[0].childNodes.length; i++)
4382
								sDebugName += oXMLDoc.getElementsByTagName("debug")[0].childNodes[i].nodeValue;
4383
							for (var i = 0; i < oXMLDoc.getElementsByTagName("get").length; i++)
4384
							{
4385
								getData += "&" + oXMLDoc.getElementsByTagName("get")[i].getAttribute("key") + "=";
4386
								for (var j = 0; j < oXMLDoc.getElementsByTagName("get")[i].childNodes.length; j++)
4387
								{
4388
									getData += oXMLDoc.getElementsByTagName("get")[i].childNodes[j].nodeValue;
4389
								}
4390
							}
4391
4392
							iItemNum = oXMLDoc.getElementsByTagName("item")[0].getAttribute("num");
4393
							iDebugNum = parseInt(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("num"));
4394
							bIsComplete = parseInt(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("complete"));
4395
							bSkipped = parseInt(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("skipped"));
4396
							iSubStepProgress = parseFloat(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("percent"));
4397
							sLastString = sDebugName + " (Item: " + iDebugNum + ")";
4398
4399
							curFile = parseInt(oXMLDoc.getElementsByTagName("file")[0].getAttribute("num"));
4400
							debugItems = parseInt(oXMLDoc.getElementsByTagName("file")[0].getAttribute("debug_items"));
4401
							totalItems = parseInt(oXMLDoc.getElementsByTagName("file")[0].getAttribute("items"));
4402
4403
							// If we have an error we haven\'t completed!
4404
							if (oXMLDoc.getElementsByTagName("error")[0] && bIsComplete)
4405
								iDebugNum = lastItem;
4406
4407
							// Do we have the additional progress bar?
4408
							if (iSubStepProgress != -1)
4409
							{
4410
								document.getElementById("substep_bar_div").classList.remove("hidden");
4411
								document.getElementById("substep_progress").style.width = iSubStepProgress + "%";
4412
								setInnerHTML(document.getElementById("substep_text"), iSubStepProgress + "%");
4413
								setInnerHTML(document.getElementById("substep_name"), sDebugName.replace(/\./g, ""));
4414
							}
4415
							else
4416
							{
4417
								document.getElementById("substep_bar_div").classList.add("hidden");
4418
							}
4419
4420
							// Move onto the next item?
4421
							if (bIsComplete)
4422
								lastItem = iDebugNum;
4423
							else
4424
								lastItem = iDebugNum - 1;
4425
4426
							// Are we finished?
4427
							if (bIsComplete && iDebugNum == -1 && curFile >= ', $upcontext['file_count'], ')
4428
							{';
4429
4430
		// Database Changes, tell us how much time we spen to do this.  If this gets updated via JS.
4431
		if ($is_debug)
4432
			echo '
4433
								document.getElementById(\'debug_section\').classList.add("hidden");
4434
4435
								var upgradeFinishedTime = parseInt(oXMLDoc.getElementsByTagName("curtime")[0].childNodes[0].nodeValue);
4436
								var diffTime = upgradeFinishedTime - upgradeStartTime;
4437
								var diffHours = Math.floor(diffTime / 3600);
4438
								var diffMinutes = parseInt((diffTime / 60) % 60);
4439
								var diffSeconds = parseInt(diffTime % 60);
4440
4441
								var completedTxt = "', $txt['upgrade_success_time_db'], '";
4442
console.log(completedTxt, upgradeFinishedTime, diffTime, diffHours, diffMinutes, diffSeconds);
4443
4444
								completedTxt = completedTxt.replace("%1$d", diffSeconds).replace("%2$d", diffMinutes).replace("%3$d", diffHours);
4445
console.log(completedTxt, upgradeFinishedTime, diffTime, diffHours, diffMinutes, diffSeconds);
4446
								setInnerHTML(document.getElementById("upgradeCompleted"), completedTxt);';
4447
4448
		echo '
4449
4450
								document.getElementById(\'commess\').classList.remove("hidden");
4451
								document.getElementById(\'contbutt\').disabled = 0;
4452
								document.getElementById(\'database_done\').value = 1;';
4453
4454
		if ($upcontext['file_count'] > 1)
4455
			echo '
4456
								document.getElementById(\'info1\').classList.add(\'hidden\');';
4457
4458
		echo '
4459
								document.getElementById(\'info2\').classList.add(\'hidden\');
4460
								updateStepProgress(100, 100, ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');
4461
								return true;
4462
							}
4463
							// Was it the last step in the file?
4464
							else if (bIsComplete && iDebugNum == -1)
4465
							{
4466
								lastItem = 0;
4467
								prevFile = curFile;';
4468
4469
		if ($is_debug)
4470
			echo '
4471
								setOuterHTML(document.getElementById(\'debuginfo\'), \'Moving to next script file...done<br><span id="debuginfo"><\' + \'/span>\');';
4472
4473
		echo '
4474
								getNextItem();
4475
								return true;
4476
							}';
4477
4478
		// If debug scroll the screen.
4479
		if ($is_debug)
4480
			echo '
4481
							if (iLastSubStepProgress == -1)
4482
							{
4483
								// Give it consistent dots.
4484
								dots = sDebugName.match(/\./g);
4485
								numDots = dots ? dots.length : 0;
4486
								for (var i = numDots; i < 3; i++)
4487
									sDebugName += ".";
4488
								setOuterHTML(document.getElementById(\'debuginfo\'), sDebugName + \'<span id="debuginfo"><\' + \'/span>\');
4489
							}
4490
							iLastSubStepProgress = iSubStepProgress;
4491
4492
							if (bIsComplete && bSkipped)
4493
								setOuterHTML(document.getElementById(\'debuginfo\'), \'skipped<br><span id="debuginfo"><\' + \'/span>\');
4494
							else if (bIsComplete)
4495
								setOuterHTML(document.getElementById(\'debuginfo\'), \'done<br><span id="debuginfo"><\' + \'/span>\');
4496
							else
4497
								setOuterHTML(document.getElementById(\'debuginfo\'), \'...<span id="debuginfo"><\' + \'/span>\');
4498
4499
							if (document.getElementById(\'debug_section\').scrollHeight)
4500
								document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
4501
4502
		echo '
4503
							// Update the page.
4504
							setInnerHTML(document.getElementById(\'item_num\'), iItemNum);
4505
							setInnerHTML(document.getElementById(\'cur_item_name\'), sItemName);';
4506
4507
		if ($upcontext['file_count'] > 1)
4508
		{
4509
			echo '
4510
							setInnerHTML(document.getElementById(\'file_done\'), curFile);
4511
							setInnerHTML(document.getElementById(\'item_count\'), totalItems);';
4512
		}
4513
4514
		echo '
4515
							// Is there an error?
4516
							if (oXMLDoc.getElementsByTagName("error")[0])
4517
							{
4518
								var sErrorMsg = "";
4519
								for (var i = 0; i < oXMLDoc.getElementsByTagName("error")[0].childNodes.length; i++)
4520
									sErrorMsg += oXMLDoc.getElementsByTagName("error")[0].childNodes[i].nodeValue;
4521
								document.getElementById("error_block").classList.remove("hidden");
4522
								setInnerHTML(document.getElementById("error_message"), sErrorMsg);
4523
								return false;
4524
							}
4525
4526
							// Get the progress bar right.
4527
							barTotal = debugItems * ', $upcontext['file_count'], ';
4528
							barDone = (debugItems * (curFile - 1)) + lastItem;
4529
4530
							updateStepProgress(barDone, barTotal, ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');
4531
4532
							// Finally - update the time here as it shows the server is responding!
4533
							curTime = new Date();
4534
							iElapsed = (curTime.getTime() / 1000 - ', $upcontext['started'], ');
4535
							mins = parseInt(iElapsed / 60);
4536
							secs = parseInt(iElapsed - mins * 60);
4537
							setInnerHTML(document.getElementById("mins_elapsed"), mins);
4538
							setInnerHTML(document.getElementById("secs_elapsed"), secs);
4539
4540
							getNextItem();
4541
							return true;
4542
						}
4543
4544
						// What if we timeout?!
4545
						function retTimeout(attemptAgain)
4546
						{
4547
							// Oh noes...
4548
							if (!attemptAgain)
4549
							{
4550
								document.getElementById("error_block").classList.remove("hidden");
4551
								setInnerHTML(document.getElementById("error_message"), "', sprintf($txt['upgrade_repondtime'], ($timeLimitThreshold * 10)), '" + "<a href=\"#\" onclick=\"retTimeout(true); return false;\">', $txt['upgrade_respondtime_clickhere'], '</a>");
4552
							}
4553
							else
4554
							{
4555
								document.getElementById("error_block").classList.add("hidden");
4556
								getNextItem();
4557
							}
4558
						}';
4559
4560
		// Start things off assuming we've not errored.
4561
		if (empty($upcontext['error_message']))
4562
			echo '
4563
						getNextItem();';
4564
4565
		echo '
4566
					//# sourceURL=dynamicScript-dbch.js
4567
					</script>';
4568
	}
4569
	return;
4570
}
4571
4572
function template_database_xml()
4573
{
4574
	global $is_debug, $upcontext;
4575
4576
	echo '
4577
	<file num="', $upcontext['cur_file_num'], '" items="', $upcontext['total_items'], '" debug_items="', $upcontext['debug_items'], '">', $upcontext['cur_file_name'], '</file>
4578
	<item num="', $upcontext['current_item_num'], '">', $upcontext['current_item_name'], '</item>
4579
	<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>';
4580
4581
	if (!empty($upcontext['error_message']))
4582
		echo '
4583
	<error>', $upcontext['error_message'], '</error>';
4584
4585
	if (!empty($upcontext['error_string']))
4586
		echo '
4587
	<sql>', $upcontext['error_string'], '</sql>';
4588
4589
	if ($is_debug)
4590
		echo '
4591
	<curtime>', time(), '</curtime>';
4592
}
4593
4594
// Template for the UTF-8 conversion step. Basically a copy of the backup stuff with slight modifications....
4595
function template_convert_utf8()
4596
{
4597
	global $upcontext, $support_js, $is_debug, $txt;
4598
4599
	echo '
4600
				<h3>', $txt['upgrade_wait2'], '</h3>
4601
				<form action="', $upcontext['form_url'], '" name="upform" id="upform" method="post">
4602
					<input type="hidden" name="utf8_done" id="utf8_done" value="0">
4603
					<strong>', $txt['upgrade_completed'], ' <span id="tab_done">', $upcontext['cur_table_num'], '</span> ', $txt['upgrade_outof'], ' ', $upcontext['table_count'], ' ', $txt['upgrade_tables'], '</strong>
4604
					<div id="debug_section">
4605
						<span id="debuginfo"></span>
4606
					</div>';
4607
4608
	// Done any tables so far?
4609
	if (!empty($upcontext['previous_tables']))
4610
		foreach ($upcontext['previous_tables'] as $table)
4611
			echo '
4612
					<br>', $txt['upgrade_completed_table'], ' &quot;', $table, '&quot;.';
4613
4614
	echo '
4615
					<h3 id="current_tab">
4616
						', $txt['upgrade_current_table'], ' &quot;<span id="current_table">', $upcontext['cur_table_name'], '</span>&quot;
4617
					</h3>';
4618
4619
	// If we dropped their index, let's let them know
4620
	if ($upcontext['dropping_index'])
4621
		echo '
4622
					<p id="indexmsg" class="', $upcontext['cur_table_num'] == $upcontext['table_count'] ? 'inline_block' : 'hidden', ' style="font-weight: bold; font-style: italic">', $txt['upgrade_fulltext'], '</p>';
4623
4624
	// Completion notification
4625
	echo '
4626
					<p id="commess" class="', $upcontext['cur_table_num'] == $upcontext['table_count'] ? 'inline_block' : 'hidden', '">', $txt['upgrade_conversion_proceed'], '</p>';
4627
4628
	// Continue please!
4629
	$upcontext['continue'] = $support_js ? 2 : 1;
4630
4631
	// If javascript allows we want to do this using XML.
4632
	if ($support_js)
4633
	{
4634
		echo '
4635
					<script>
4636
						var lastTable = ', $upcontext['cur_table_num'], ';
4637
						function getNextTables()
4638
						{
4639
							getXMLDocument(\'', $upcontext['form_url'], '&xml&substep=\' + lastTable, onConversionUpdate);
4640
						}
4641
4642
						// Got an update!
4643
						function onConversionUpdate(oXMLDoc)
4644
						{
4645
							var sCurrentTableName = "";
4646
							var iTableNum = 0;
4647
							var sCompletedTableName = getInnerHTML(document.getElementById(\'current_table\'));
4648
							for (var i = 0; i < oXMLDoc.getElementsByTagName("table")[0].childNodes.length; i++)
4649
								sCurrentTableName += oXMLDoc.getElementsByTagName("table")[0].childNodes[i].nodeValue;
4650
							iTableNum = oXMLDoc.getElementsByTagName("table")[0].getAttribute("num");
4651
4652
							// Update the page.
4653
							setInnerHTML(document.getElementById(\'tab_done\'), iTableNum);
4654
							setInnerHTML(document.getElementById(\'current_table\'), sCurrentTableName);
4655
							lastTable = iTableNum;
4656
							updateStepProgress(iTableNum, ', $upcontext['table_count'], ', ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');';
4657
4658
		// If debug flood the screen.
4659
		if ($is_debug)
4660
			echo '
4661
						setOuterHTML(document.getElementById(\'debuginfo\'), \'<br>Completed Table: &quot;\' + sCompletedTableName + \'&quot;.<span id="debuginfo"><\' + \'/span>\');
4662
4663
						if (document.getElementById(\'debug_section\').scrollHeight)
4664
							document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
4665
4666
		echo '
4667
						// Get the next update...
4668
						if (iTableNum == ', $upcontext['table_count'], ')
4669
						{
4670
							document.getElementById(\'commess\').classList.remove(\'hidden\');
4671
							if (document.getElementById(\'indexmsg\') != null) {
4672
								document.getElementById(\'indexmsg\').classList.remove(\'hidden\');
4673
							}
4674
							document.getElementById(\'current_tab\').classList.add(\'hidden\');
4675
							document.getElementById(\'contbutt\').disabled = 0;
4676
							document.getElementById(\'utf8_done\').value = 1;
4677
						}
4678
						else
4679
							getNextTables();
4680
					}
4681
					getNextTables();
4682
				//# sourceURL=dynamicScript-conv.js
4683
				</script>';
4684
	}
4685
}
4686
4687
function template_convert_xml()
4688
{
4689
	global $upcontext;
4690
4691
	echo '
4692
	<table num="', $upcontext['cur_table_num'], '">', $upcontext['cur_table_name'], '</table>';
4693
}
4694
4695
// Template for the database backup tool/
4696
function template_serialize_json()
4697
{
4698
	global $upcontext, $support_js, $is_debug, $txt;
4699
4700
	echo '
4701
				<h3>', $txt['upgrade_convert_datajson'], '</h3>
4702
				<form action="', $upcontext['form_url'], '" name="upform" id="upform" method="post">
4703
					<input type="hidden" name="json_done" id="json_done" value="0">
4704
					<strong>', $txt['upgrade_completed'], ' <span id="tab_done">', $upcontext['cur_table_num'], '</span> ', $txt['upgrade_outof'], ' ', $upcontext['table_count'], ' ', $txt['upgrade_tables'], '</strong>
4705
					<div id="debug_section">
4706
						<span id="debuginfo"></span>
4707
					</div>';
4708
4709
	// Dont any tables so far?
4710
	if (!empty($upcontext['previous_tables']))
4711
		foreach ($upcontext['previous_tables'] as $table)
4712
			echo '
4713
					<br>', $txt['upgrade_completed_table'], ' &quot;', $table, '&quot;.';
4714
4715
	echo '
4716
					<h3 id="current_tab">
4717
						', $txt['upgrade_current_table'], ' &quot;<span id="current_table">', $upcontext['cur_table_name'], '</span>&quot;
4718
					</h3>
4719
					<p id="commess" class="', $upcontext['cur_table_num'] == $upcontext['table_count'] ? 'inline_block' : 'hidden', '">', $txt['upgrade_json_completed'], '</p>';
4720
4721
	// Try to make sure substep was reset.
4722
	if ($upcontext['cur_table_num'] == $upcontext['table_count'])
4723
		echo '
4724
					<input type="hidden" name="substep" id="substep" value="0">';
4725
4726
	// Continue please!
4727
	$upcontext['continue'] = $support_js ? 2 : 1;
4728
4729
	// If javascript allows we want to do this using XML.
4730
	if ($support_js)
4731
	{
4732
		echo '
4733
					<script>
4734
						var lastTable = ', $upcontext['cur_table_num'], ';
4735
						function getNextTables()
4736
						{
4737
							getXMLDocument(\'', $upcontext['form_url'], '&xml&substep=\' + lastTable, onBackupUpdate);
4738
						}
4739
4740
						// Got an update!
4741
						function onBackupUpdate(oXMLDoc)
4742
						{
4743
							var sCurrentTableName = "";
4744
							var iTableNum = 0;
4745
							var sCompletedTableName = getInnerHTML(document.getElementById(\'current_table\'));
4746
							for (var i = 0; i < oXMLDoc.getElementsByTagName("table")[0].childNodes.length; i++)
4747
								sCurrentTableName += oXMLDoc.getElementsByTagName("table")[0].childNodes[i].nodeValue;
4748
							iTableNum = oXMLDoc.getElementsByTagName("table")[0].getAttribute("num");
4749
4750
							// Update the page.
4751
							setInnerHTML(document.getElementById(\'tab_done\'), iTableNum);
4752
							setInnerHTML(document.getElementById(\'current_table\'), sCurrentTableName);
4753
							lastTable = iTableNum;
4754
							updateStepProgress(iTableNum, ', $upcontext['table_count'], ', ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');';
4755
4756
		// If debug flood the screen.
4757
		if ($is_debug)
4758
			echo '
4759
							setOuterHTML(document.getElementById(\'debuginfo\'), \'<br>', $txt['upgrade_completed_table'], ' &quot;\' + sCompletedTableName + \'&quot;.<span id="debuginfo"><\' + \'/span>\');
4760
4761
							if (document.getElementById(\'debug_section\').scrollHeight)
4762
								document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
4763
4764
		echo '
4765
							// Get the next update...
4766
							if (iTableNum == ', $upcontext['table_count'], ')
4767
							{
4768
								document.getElementById(\'commess\').classList.remove("hidden");
4769
								document.getElementById(\'current_tab\').classList.add("hidden");
4770
								document.getElementById(\'contbutt\').disabled = 0;
4771
								document.getElementById(\'json_done\').value = 1;
4772
							}
4773
							else
4774
								getNextTables();
4775
						}
4776
						getNextTables();
4777
					//# sourceURL=dynamicScript-json.js
4778
					</script>';
4779
	}
4780
}
4781
4782
function template_serialize_json_xml()
4783
{
4784
	global $upcontext;
4785
4786
	echo '
4787
	<table num="', $upcontext['cur_table_num'], '">', $upcontext['cur_table_name'], '</table>';
4788
}
4789
4790
function template_upgrade_complete()
4791
{
4792
	global $upcontext, $upgradeurl, $settings, $boardurl, $is_debug, $txt;
4793
4794
	echo '
4795
				<h3>', $txt['upgrade_done'], ' <a href="', $boardurl, '/index.php">', $txt['upgrade_done2'], '</a>.  ', $txt['upgrade_done3'], '</h3>
4796
				<form action="', $boardurl, '/index.php">';
4797
4798
	if (!empty($upcontext['can_delete_script']))
4799
		echo '
4800
					<label>
4801
						<input type="checkbox" id="delete_self" onclick="doTheDelete(this);"> ', $txt['upgrade_delete_now'], '
4802
					</label>
4803
					<em>', $txt['upgrade_delete_server'], '</em>
4804
					<script>
4805
						function doTheDelete(theCheck)
4806
						{
4807
							var theImage = document.getElementById ? document.getElementById("delete_upgrader") : document.all.delete_upgrader;
4808
							theImage.src = "', $upgradeurl, '?delete=1&ts_" + (new Date().getTime());
4809
							theCheck.disabled = true;
4810
						}
4811
					</script>
4812
					<img src="', $settings['default_theme_url'], '/images/blank.png" alt="" id="delete_upgrader"><br>';
4813
4814
	// Show Upgrade time in debug mode when we completed the upgrade process totally
4815
	if ($is_debug)
4816
	{
4817
		$active = time() - $upcontext['started'];
4818
		$hours = floor($active / 3600);
4819
		$minutes = intval(($active / 60) % 60);
4820
		$seconds = intval($active % 60);
4821
4822
		if ($hours > 0)
4823
			echo '', sprintf($txt['upgrade_completed_time_hms'], $seconds, $minutes, $hours), '';
4824
		elseif ($minutes > 0)
4825
			echo '', sprintf($txt['upgrade_completed_time_ms'], $seconds, $minutes), '';
4826
		elseif ($seconds > 0)
4827
			echo '', sprintf($txt['upgrade_completed_time_s'], $seconds), '';
4828
	}
4829
4830
	echo '
4831
					<p>
4832
						', sprintf($txt['upgrade_problems'], 'http://simplemachines.org'), '
4833
						<br>
4834
						', $txt['upgrade_luck'], '<br>
4835
						Simple Machines
4836
					</p>';
4837
}
4838
4839
/**
4840
 * Convert MySQL (var)char ip col to binary
4841
 *
4842
 * @param string $targetTable The table to perform the operation on
4843
 * @param string $oldCol The old column to gather data from
4844
 * @param string $newCol The new column to put data in
4845
 * @param int $limit The amount of entries to handle at once.
4846
 * @param int $setSize The amount of entries after which to update the database.
4847
 *
4848
 * newCol needs to be a varbinary(16) null able field
4849
 * @return bool
4850
 */
4851
function MySQLConvertOldIp($targetTable, $oldCol, $newCol, $limit = 50000, $setSize = 100)
4852
{
4853
	global $smcFunc, $step_progress;
4854
4855
	$current_substep = $_GET['substep'];
4856
4857
	if (empty($_GET['a']))
4858
		$_GET['a'] = 0;
4859
	$step_progress['name'] = 'Converting ips';
4860
	$step_progress['current'] = $_GET['a'];
4861
4862
	// Skip this if we don't have the column
4863
	$request = $smcFunc['db_query']('', '
4864
		SHOW FIELDS
4865
		FROM {db_prefix}{raw:table}
4866
		WHERE Field = {string:name}',
4867
		array(
4868
			'table' => $targetTable,
4869
			'name' => $oldCol,
4870
		)
4871
	);
4872
	if ($smcFunc['db_num_rows']($request) !== 1)
4873
	{
4874
		$smcFunc['db_free_result']($request);
4875
		return;
4876
	}
4877
	$smcFunc['db_free_result']($request);
4878
4879
	//mysql default max length is 1mb https://dev.mysql.com/doc/refman/5.1/en/packet-too-large.html
4880
	$arIp = array();
0 ignored issues
show
Unused Code introduced by
The assignment to $arIp is dead and can be removed.
Loading history...
4881
4882
	$is_done = false;
4883
	while (!$is_done)
4884
	{
4885
		// Keep looping at the current step.
4886
		nextSubstep($current_substep);
4887
4888
		$arIp = array();
4889
4890
		$request = $smcFunc['db_query']('', '
4891
			SELECT DISTINCT {raw:old_col}
4892
			FROM {db_prefix}{raw:table_name}
4893
			WHERE {raw:new_col} IS NULL
4894
			LIMIT {int:limit}',
4895
			array(
4896
				'old_col' => $oldCol,
4897
				'new_col' => $newCol,
4898
				'table_name' => $targetTable,
4899
				'empty' => '',
4900
				'limit' => $limit,
4901
			)
4902
		);
4903
		while ($row = $smcFunc['db_fetch_assoc']($request))
4904
			$arIp[] = $row[$oldCol];
4905
4906
		$smcFunc['db_free_result']($request);
4907
4908
		// Special case, null ip could keep us in a loop.
4909
		if (is_null($arIp[0]))
4910
			unset($arIp[0]);
4911
4912
		if (empty($arIp))
4913
			$is_done = true;
4914
4915
		$updates = array();
4916
		$cases = array();
4917
		$count = count($arIp);
4918
		for ($i = 0; $i < $count; $i++)
4919
		{
4920
			$arIp[$i] = trim($arIp[$i]);
4921
4922
			if (empty($arIp[$i]))
4923
				continue;
4924
4925
			$updates['ip' . $i] = $arIp[$i];
4926
			$cases[$arIp[$i]] = 'WHEN ' . $oldCol . ' = {string:ip' . $i . '} THEN {inet:ip' . $i . '}';
4927
4928
			if ($setSize > 0 && $i % $setSize === 0)
4929
			{
4930
				if (count($updates) == 1)
4931
					continue;
4932
4933
				$updates['whereSet'] = array_values($updates);
4934
				$smcFunc['db_query']('', '
4935
					UPDATE {db_prefix}' . $targetTable . '
4936
					SET ' . $newCol . ' = CASE ' .
4937
					implode('
4938
						', $cases) . '
4939
						ELSE NULL
4940
					END
4941
					WHERE ' . $oldCol . ' IN ({array_string:whereSet})',
4942
					$updates
4943
				);
4944
4945
				$updates = array();
4946
				$cases = array();
4947
			}
4948
		}
4949
4950
		// Incase some extras made it through.
4951
		if (!empty($updates))
4952
		{
4953
			if (count($updates) == 1)
4954
			{
4955
				foreach ($updates as $key => $ip)
4956
				{
4957
					$smcFunc['db_query']('', '
4958
						UPDATE {db_prefix}' . $targetTable . '
4959
						SET ' . $newCol . ' = {inet:ip}
4960
						WHERE ' . $oldCol . ' = {string:ip}',
4961
						array(
4962
							'ip' => $ip
4963
						)
4964
					);
4965
				}
4966
			}
4967
			else
4968
			{
4969
				$updates['whereSet'] = array_values($updates);
4970
				$smcFunc['db_query']('', '
4971
					UPDATE {db_prefix}' . $targetTable . '
4972
					SET ' . $newCol . ' = CASE ' .
4973
					implode('
4974
						', $cases) . '
4975
						ELSE NULL
4976
					END
4977
					WHERE ' . $oldCol . ' IN ({array_string:whereSet})',
4978
					$updates
4979
				);
4980
			}
4981
		}
4982
		else
4983
			$is_done = true;
4984
4985
		$_GET['a'] += $limit;
4986
		$step_progress['current'] = $_GET['a'];
4987
	}
4988
4989
	unset($_GET['a']);
4990
}
4991
4992
/**
4993
 * Get the column info. This is basically the same as smf_db_list_columns but we get 1 column, force detail and other checks.
4994
 *
4995
 * @param string $targetTable The table to perform the operation on
4996
 * @param string $column The column we are looking for.
4997
 *
4998
 * @return array Info on the table.
4999
 */
5000
function upgradeGetColumnInfo($targetTable, $column)
5001
{
5002
	global $smcFunc;
5003
5004
	// This should already be here, but be safe.
5005
	db_extend('packages');
5006
5007
	$columns = $smcFunc['db_list_columns']($targetTable, true);
5008
5009
	if (isset($columns[$column]))
5010
		return $columns[$column];
5011
	else
5012
		return null;
5013
}
5014
5015
/**
5016
 * Takes the changes to be made during the upgradeOptions step, grabs all known Settings data from Settings.php, then runs
5017
 * through a process to rebuild onto a brand new Settings template.  This should only be done if detection believes the
5018
 * settings file isn't using any advanced configuration setups in the Settings.php file.  A copy is made as Settings_org.php
5019
 * to preserve all changes prior to migration.
5020
 *
5021
 * @param array $config_vars An array of one or more variables to update
5022
 *
5023
 * @return void We either successfully update the Settings file, or throw a error here.
5024
 */
5025
function migrateSettingsFile($changes)
5026
{
5027
	global $boarddir, $cachedir;
5028
5029
	// Try to find all of these settings.
5030
	$settingsVars = array(
5031
		'maintenance' => 'int',
5032
		'mtitle' => 'string',
5033
		'mmessage' => 'string',
5034
		'mbname' => 'string',
5035
		'language' => 'string',
5036
		'boardurl' => 'string',
5037
		'webmaster_email' => 'string',
5038
		'cookiename' => 'string',
5039
		'db_type' => 'string',
5040
		'db_server' => 'string_fatal',
5041
		'db_name' => 'string_fatal',
5042
		'db_user' => 'string_fatal',
5043
		'db_passwd' => 'string_fatal',
5044
		'ssi_db_user' => 'string',
5045
		'ssi_db_passwd' => 'string',
5046
		'db_prefix' => 'string_fatal',
5047
		'db_persist' => 'int',
5048
		'db_error_send' => 'int',
5049
		'db_mb4' => 'null',
5050
		'cache_accelerator' => 'string',
5051
		'cache_enable' => 'int',
5052
		'cache_memcached' => 'string',
5053
		'cachedir' => 'string',
5054
		'image_proxy_enabled' => 'bool',
5055
		'image_proxy_secret' => 'string',
5056
		'image_proxy_maxsize' => 'int',
5057
		'boarddir' => 'string',
5058
		'sourcedir' => 'string',
5059
		'packagesdir' => 'string',
5060
		'tasksdir' => 'string',
5061
		'db_character_set' => 'string',
5062
	);
5063
5064
	// The Settings file, in an array as if it was handled by updateSettingsFile
5065
	$settingsArray = array(
5066
		'<' . '?' . 'php',
5067
		'',
5068
		'/**',
5069
		' * The settings file contains all of the basic settings that need to be present when a database/cache is not available.',
5070
		' *',
5071
		' * Simple Machines Forum (SMF)',
5072
		' *',
5073
		' * @package SMF',
5074
		' * @author Simple Machines http://www.simplemachines.org',
5075
		' * @copyright ' . date('Y', time()) . ' Simple Machines and individual contributors',
5076
		' * @license http://www.simplemachines.org/about/smf/license.php BSD',
5077
		' *',
5078
		' * @version ' . SMF_VERSION,
5079
		' */',
5080
		'',
5081
		'########## Maintenance ##########',
5082
		'/**',
5083
		' * The maintenance "mode"',
5084
		' * Set to 1 to enable Maintenance Mode, 2 to make the forum untouchable. (you\'ll have to make it 0 again manually!)',
5085
		' * 0 is default and disables maintenance mode.',
5086
		' * @var int 0, 1, 2',
5087
		' * @global int $maintenance',
5088
		' */',
5089
		'$maintenance = 0;',
5090
		'/**',
5091
		' * Title for the Maintenance Mode message.',
5092
		' * @var string',
5093
		' * @global int $mtitle',
5094
		' */',
5095
		'$mtitle = \'Maintenance Mode\';',
5096
		'/**',
5097
		' * Description of why the forum is in maintenance mode.',
5098
		' * @var string',
5099
		' * @global string $mmessage',
5100
		' */',
5101
		'$mmessage = \'Okay faithful users...we\\\'re attempting to restore an older backup of the database...news will be posted once we\\\'re back!\';',
5102
		'',
5103
		'########## Forum Info ##########',
5104
		'/**',
5105
		' * The name of your forum.',
5106
		' * @var string',
5107
		' */',
5108
		'$mbname = \'My Community\';',
5109
		'/**',
5110
		' * The default language file set for the forum.',
5111
		' * @var string',
5112
		' */',
5113
		'$language = \'english\';',
5114
		'/**',
5115
		' * URL to your forum\'s folder. (without the trailing /!)',
5116
		' * @var string',
5117
		' */',
5118
		'$boardurl = \'http://127.0.0.1/smf\';',
5119
		'/**',
5120
		' * Email address to send emails from. (like [email protected].)',
5121
		' * @var string',
5122
		' */',
5123
		'$webmaster_email = \'[email protected]\';',
5124
		'/**',
5125
		' * Name of the cookie to set for authentication.',
5126
		' * @var string',
5127
		' */',
5128
		'$cookiename = \'SMFCookie21\';',
5129
		'',
5130
		'########## Database Info ##########',
5131
		'/**',
5132
		' * The database type',
5133
		' * Default options: mysql, postgresql',
5134
		' * @var string',
5135
		' */',
5136
		'$db_type = \'mysql\';',
5137
		'/**',
5138
		' * The server to connect to (or a Unix socket)',
5139
		' * @var string',
5140
		' */',
5141
		'$db_server = \'localhost\';',
5142
		'/**',
5143
		' * The database name',
5144
		' * @var string',
5145
		' */',
5146
		'$db_name = \'smf\';',
5147
		'/**',
5148
		' * Database username',
5149
		' * @var string',
5150
		' */',
5151
		'$db_user = \'root\';',
5152
		'/**',
5153
		' * Database password',
5154
		' * @var string',
5155
		' */',
5156
		'$db_passwd = \'\';',
5157
		'/**',
5158
		' * Database user for when connecting with SSI',
5159
		' * @var string',
5160
		' */',
5161
		'$ssi_db_user = \'\';',
5162
		'/**',
5163
		' * Database password for when connecting with SSI',
5164
		' * @var string',
5165
		' */',
5166
		'$ssi_db_passwd = \'\';',
5167
		'/**',
5168
		' * A prefix to put in front of your table names.',
5169
		' * This helps to prevent conflicts',
5170
		' * @var string',
5171
		' */',
5172
		'$db_prefix = \'smf_\';',
5173
		'/**',
5174
		' * Use a persistent database connection',
5175
		' * @var int|bool',
5176
		' */',
5177
		'$db_persist = 0;',
5178
		'/**',
5179
		' *',
5180
		' * @var int|bool',
5181
		' */',
5182
		'$db_error_send = 0;',
5183
		'/**',
5184
		' * Override the default behavior of the database layer for mb4 handling',
5185
		' * null keep the default behavior untouched',
5186
		' * @var null|bool',
5187
		' */',
5188
		'$db_mb4 = null;',
5189
		'',
5190
		'########## Cache Info ##########',
5191
		'/**',
5192
		' * Select a cache system. You want to leave this up to the cache area of the admin panel for',
5193
		' * proper detection of apc, memcached, output_cache, smf, or xcache',
5194
		' * (you can add more with a mod).',
5195
		' * @var string',
5196
		' */',
5197
		'$cache_accelerator = \'\';',
5198
		'/**',
5199
		' * The level at which you would like to cache. Between 0 (off) through 3 (cache a lot).',
5200
		' * @var int',
5201
		' */',
5202
		'$cache_enable = 0;',
5203
		'/**',
5204
		' * This is only used for memcache / memcached. Should be a string of \'server:port,server:port\'',
5205
		' * @var array',
5206
		' */',
5207
		'$cache_memcached = \'\';',
5208
		'/**',
5209
		' * This is only for the \'smf\' file cache system. It is the path to the cache directory.',
5210
		' * It is also recommended that you place this in /tmp/ if you are going to use this.',
5211
		' * @var string',
5212
		' */',
5213
		'$cachedir = dirname(__FILE__) . \'/cache\';',
5214
		'',
5215
		'########## Image Proxy ##########',
5216
		'# This is done entirely in Settings.php to avoid loading the DB while serving the images',
5217
		'/**',
5218
		' * Whether the proxy is enabled or not',
5219
		' * @var bool',
5220
		' */',
5221
		'$image_proxy_enabled = true;',
5222
		'',
5223
		'/**',
5224
		' * Secret key to be used by the proxy',
5225
		' * @var string',
5226
		' */',
5227
		'$image_proxy_secret = \'smfisawesome\';',
5228
		'',
5229
		'/**',
5230
		' * Maximum file size (in KB) for individual files',
5231
		' * @var int',
5232
		' */',
5233
		'$image_proxy_maxsize = 5192;',
5234
		'',
5235
		'########## Directories/Files ##########',
5236
		'# Note: These directories do not have to be changed unless you move things.',
5237
		'/**',
5238
		' * The absolute path to the forum\'s folder. (not just \'.\'!)',
5239
		' * @var string',
5240
		' */',
5241
		'$boarddir = dirname(__FILE__);',
5242
		'/**',
5243
		' * Path to the Sources directory.',
5244
		' * @var string',
5245
		' */',
5246
		'$sourcedir = dirname(__FILE__) . \'/Sources\';',
5247
		'/**',
5248
		' * Path to the Packages directory.',
5249
		' * @var string',
5250
		' */',
5251
		'$packagesdir = dirname(__FILE__) . \'/Packages\';',
5252
		'/**',
5253
		' * Path to the tasks directory.',
5254
		' * @var string',
5255
		' */',
5256
		'$tasksdir = $sourcedir . \'/tasks\';',
5257
		'',
5258
		'# Make sure the paths are correct... at least try to fix them.',
5259
		'if (!file_exists($boarddir) && file_exists(dirname(__FILE__) . \'/agreement.txt\'))',
5260
		'	$boarddir = dirname(__FILE__);',
5261
		'if (!file_exists($sourcedir) && file_exists($boarddir . \'/Sources\'))',
5262
		'	$sourcedir = $boarddir . \'/Sources\';',
5263
		'if (!file_exists($cachedir) && file_exists($boarddir . \'/cache\'))',
5264
		'	$cachedir = $boarddir . \'/cache\';',
5265
		'',
5266
		'######### Legacy Settings #########',
5267
		'# UTF-8 is now the only character set supported in 2.1.',
5268
		'$db_character_set = \'utf8\';',
5269
		'',
5270
		'########## Error-Catching ##########',
5271
		'# Note: You shouldn\'t touch these settings.',
5272
		'if (file_exists((isset($cachedir) ? $cachedir : dirname(__FILE__)) . \'/db_last_error.php\'))',
5273
		'	include((isset($cachedir) ? $cachedir : dirname(__FILE__)) . \'/db_last_error.php\');',
5274
		'',
5275
		'if (!isset($db_last_error))',
5276
		'{',
5277
		'	// File does not exist so lets try to create it',
5278
		'	file_put_contents((isset($cachedir) ? $cachedir : dirname(__FILE__)) . \'/db_last_error.php\', \'<\' . \'?\' . "php\n" . \'$db_last_error = 0;\' . "\n" . \'?\' . \'>\');',
5279
		'	$db_last_error = 0;',
5280
		'}',
5281
		'',
5282
		'?' . '>',
5283
	);
5284
5285
	// Now, find all of the original settings.  Mark those for the "change".
5286
	$original = array();
5287
	foreach ($settingsVars as $setVar => $setType)
5288
	{
5289
		global $$setVar;
5290
5291
		// Find the setting.
5292
		if ($setType == 'string' || $setType == 'string_fatal')
5293
			$original[$setVar] = isset($$setVar) ? '\'' . addslashes($$setVar) . '\'' : (strpos('fatal', $setType) ? null : '\'\'');
5294
		elseif ($setType == 'int' || $setType == 'int_fatal')
5295
			$original[$setVar] = isset($$setVar) ? (int) $$setVar : (strpos('fatal', $setType) ? null : 0);
5296
		elseif ($setType == 'bool' || $setType == 'bool_fatal')
5297
			$original[$setVar] = isset($$setVar) && in_array($$setVar, array(1, true)) ? 'true' : (strpos('fatal', $setType) ? null : 'false');
5298
		elseif ($setType == 'null' || $setType == 'null_fatal')
5299
			$original[$setVar] = isset($$setVar) && in_array($$setVar, array(1, true)) ? 'true' : (strpos('fatal', $setType) ? null : 'null');
5300
5301
		// Well this isn't good.  Do we fix it or bail?
5302
		if (is_null($original) && $setType != 'null' && strpos('fatal', $setType) > -1)
5303
			return throw_error(sprintf($txt['error_settings_migration_no_var'], $setVar));
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $txt seems to be never defined.
Loading history...
5304
	}
5305
5306
	// Finally, merge the changes with the new ones.
5307
	$config_vars = $original;
5308
	foreach ($changes as $setVar => $value)
5309
	{
5310
		// Nothing needed here.
5311
		if ($setVar != 'upgradeData' && $config_vars[$setVar] == $changes[$setVar])
5312
			continue;
5313
5314
		$config_vars[$setVar] = $value;
5315
	}
5316
5317
	/*
5318
		It would be nice to call updateSettingsFile and be done with this. However the function doesn't support passing in the entire file. We also want to backup with a different name, just incase.
5319
	*/
5320
5321
	// When was Settings.php last changed?
5322
	$last_settings_change = filemtime($boarddir . '/Settings.php');
5323
5324
	// remove any /r's that made there way in here
5325
	foreach ($settingsArray as $k => $dummy)
5326
		$settingsArray[$k] = strtr($dummy, array("\r" => '')) . "\n";
5327
5328
	// go line by line and see whats changing
5329
	for ($i = 0, $n = count($settingsArray); $i < $n; $i++)
5330
	{
5331
		// Don't trim or bother with it if it's not a variable.
5332
		if (substr($settingsArray[$i], 0, 1) != '$')
5333
			continue;
5334
5335
		$settingsArray[$i] = trim($settingsArray[$i]) . "\n";
5336
5337
		// Look through the variables to set....
5338
		foreach ($config_vars as $var => $val)
5339
		{
5340
			// be sure someone is not updating db_last_error this with a group
5341
			if ($var === 'db_last_error')
5342
				unset($config_vars[$var]);
5343
			elseif (strncasecmp($settingsArray[$i], '$' . $var, 1 + strlen($var)) == 0)
5344
			{
5345
				$comment = strstr(substr($settingsArray[$i], strpos($settingsArray[$i], ';')), '#');
5346
				$settingsArray[$i] = '$' . $var . ' = ' . $val . ';' . ($comment == '' ? '' : "\t\t" . rtrim($comment)) . "\n";
5347
5348
				// This one's been 'used', so to speak.
5349
				unset($config_vars[$var]);
5350
			}
5351
		}
5352
5353
		// End of the file ... maybe
5354
		if (substr(trim($settingsArray[$i]), 0, 2) == '?' . '>')
5355
			$end = $i;
5356
	}
5357
5358
	// This should never happen, but apparently it is happening.
5359
	if (empty($end) || $end < 10)
5360
		$end = count($settingsArray) - 1;
5361
5362
	// Still more variables to go?  Then lets add them at the end.
5363
	if (!empty($config_vars))
5364
	{
5365
		if (trim($settingsArray[$end]) == '?' . '>')
5366
			$settingsArray[$end++] = '';
5367
		else
5368
			$end++;
5369
5370
		// Add in any newly defined vars that were passed
5371
		foreach ($config_vars as $var => $val)
5372
			$settingsArray[$end++] = '$' . $var . ' = ' . $val . ';' . "\n";
5373
5374
		$settingsArray[$end] = '?' . '>';
5375
	}
5376
	else
5377
		$settingsArray[$end] = trim($settingsArray[$end]);
5378
5379
	// Sanity error checking: the file needs to be at least 12 lines.
5380
	if (count($settingsArray) < 12)
5381
		return throw_error($txt['error_settings_migration_too_short']);
5382
5383
	// Try to avoid a few pitfalls:
5384
	//  - like a possible race condition,
5385
	//  - or a failure to write at low diskspace
5386
	//
5387
	// Check before you act: if cache is enabled, we can do a simple write test
5388
	// to validate that we even write things on this filesystem.
5389
	if ((empty($cachedir) || !file_exists($cachedir)) && file_exists($boarddir . '/cache'))
5390
		$cachedir = $boarddir . '/cache';
5391
5392
	$test_fp = @fopen($cachedir . '/settings_update.tmp', "w+");
5393
	if ($test_fp)
0 ignored issues
show
introduced by
$test_fp is of type false|resource, thus it always evaluated to false.
Loading history...
5394
	{
5395
		fclose($test_fp);
5396
		$written_bytes = file_put_contents($cachedir . '/settings_update.tmp', 'test', LOCK_EX);
5397
		@unlink($cachedir . '/settings_update.tmp');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

5397
		/** @scrutinizer ignore-unhandled */ @unlink($cachedir . '/settings_update.tmp');

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
5398
5399
		// Oops. Low disk space, perhaps. Don't mess with Settings.php then.
5400
		// No means no. :P
5401
		if ($written_bytes !== 4)
5402
			return throw_error($txt['error_settings_migration_write_failed']);
5403
	}
5404
5405
	// Protect me from what I want! :P
5406
	clearstatcache();
5407
	if (filemtime($boarddir . '/Settings.php') === $last_settings_change)
5408
	{
5409
		// save the old before we do anything
5410
		$settings_backup_fail = !@is_writable($boarddir . '/Settings_org.php') || !@copy($boarddir . '/Settings.php', $boarddir . '/Settings_org.php');
5411
		$settings_backup_fail = !$settings_backup_fail ? (!file_exists($boarddir . '/Settings_org.php') || filesize($boarddir . '/Settings_org.php') === 0) : $settings_backup_fail;
5412
5413
		// write out the new
5414
		$write_settings = implode('', $settingsArray);
5415
		$written_bytes = file_put_contents($boarddir . '/Settings.php', $write_settings, LOCK_EX);
5416
5417
		// survey says ...
5418
		if ($written_bytes !== strlen($write_settings) && !$settings_backup_fail)
5419
		{
5420
			if (file_exists($boarddir . '/Settings_bak.php'))
5421
				@copy($boarddir . '/Settings_bak.php', $boarddir . '/Settings.php');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for copy(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

5421
				/** @scrutinizer ignore-unhandled */ @copy($boarddir . '/Settings_bak.php', $boarddir . '/Settings.php');

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
5422
5423
			return throw_error($txt['error_settings_migration_general']);
5424
		}
5425
	}
5426
5427
	// Even though on normal installations the filemtime should prevent this being used by the installer incorrectly
5428
	// it seems that there are times it might not. So let's MAKE it dump the cache.
5429
	if (function_exists('opcache_invalidate'))
5430
		opcache_invalidate($boarddir . '/Settings.php', true);
5431
}
5432
5433
/**
5434
 * Determine if we should auto select the migrate Settings file.  This is determined by a variety of missing settings.
5435
 * Prior to checking these settings, we look for advanced setups such as integrations or if variables have been moved
5436
 * to another file.  If these are detected, we abort.
5437
 *
5438
 * @param array $config_vars An array of one or more variables to update
5439
 *
5440
 * @return void We either successfully update the Settings file, or throw a error here.
5441
 */
5442
5443
function detectSettingsFileMigrationNeeded()
5444
{
5445
	global $boarddir, $packagesdir, $tasksdir, $db_server, $db_type, $image_proxy_enabled, $db_show_debug;
5446
5447
	// We should not migrate if db_show_debug is in there, some dev stuff going on here.
5448
	if (isset($db_show_debug))
5449
		return false;
5450
5451
	$file_contents = file_get_contents($boarddir . '/Settings.php');
5452
5453
	// Is there a include statement somewhere in there? Some advanced handling of the variables elsewhere?
5454
	// Try our best to stay away from the cachedir match.
5455
	if (preg_match('~\sinclude\((?:(?!\(isset\(\$cachedir))~im', $file_contents))
5456
		return false;
5457
5458
	// If we find a mention of $GLOBALS, there may be a integration going on.
5459
	if (preg_match('~\$GLOBALS\[~im', $file_contents))
5460
		return false;
5461
5462
	// If these are not set, it makes us a candidate to migrate.
5463
	if (!isset($packagesdir, $tasksdir, $db_server, $db_type, $image_proxy_enabled))
5464
		return true;
5465
5466
	return false;
5467
}
5468
5469
?>