Passed
Push — release-2.1 ( 8eeff0...01e7cf )
by Mathias
06:18
created

template_database_xml()   B

Complexity

Conditions 7
Paths 8

Size

Total Lines 20
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

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

5365
		/** @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...
5366
5367
		// Oops. Low disk space, perhaps. Don't mess with Settings.php then.
5368
		// No means no. :P
5369
		if ($written_bytes !== 4)
5370
			return throw_error($txt['error_settings_migration_write_failed']);
5371
	}
5372
5373
	// Protect me from what I want! :P
5374
	clearstatcache();
5375
	if (filemtime($boarddir . '/Settings.php') === $last_settings_change)
5376
	{
5377
		// save the old before we do anything
5378
		$settings_backup_fail = !@is_writable($boarddir . '/Settings_org.php') || !@copy($boarddir . '/Settings.php', $boarddir . '/Settings_org.php');
5379
		$settings_backup_fail = !$settings_backup_fail ? (!file_exists($boarddir . '/Settings_org.php') || filesize($boarddir . '/Settings_org.php') === 0) : $settings_backup_fail;
5380
5381
		// write out the new
5382
		$write_settings = implode('', $settingsArray);
5383
		$written_bytes = file_put_contents($boarddir . '/Settings.php', $write_settings, LOCK_EX);
5384
5385
		// survey says ...
5386
		if ($written_bytes !== strlen($write_settings) && !$settings_backup_fail)
5387
		{
5388
			if (file_exists($boarddir . '/Settings_bak.php'))
5389
				@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

5389
				/** @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...
5390
5391
			return throw_error($txt['error_settings_migration_general']);
5392
		}
5393
	}
5394
5395
	// Even though on normal installations the filemtime should prevent this being used by the installer incorrectly
5396
	// it seems that there are times it might not. So let's MAKE it dump the cache.
5397
	if (function_exists('opcache_invalidate'))
5398
		opcache_invalidate($boarddir . '/Settings.php', true);
5399
}
5400
5401
/**
5402
 * Determine if we should auto select the migrate Settings file.  This is determined by a variety of missing settings.
5403
 * Prior to checking these settings, we look for advanced setups such as integrations or if variables have been moved
5404
 * to another file.  If these are detected, we abort.
5405
 *
5406
 * @param array $config_vars An array of one or more variables to update
5407
 *
5408
 * @return void We either successfully update the Settings file, or throw a error here.
5409
 */
5410
5411
function detectSettingsFileMigrationNeeded()
5412
{
5413
	global $boarddir, $packagesdir, $tasksdir, $db_server, $db_type, $image_proxy_enabled, $db_show_debug;
5414
5415
	// We should not migrate if db_show_debug is in there, some dev stuff going on here.
5416
	if (isset($db_show_debug))
5417
		return false;
5418
5419
	$file_contents = file_get_contents($boarddir . '/Settings.php');
5420
5421
	// Is there a include statement somewhere in there? Some advanced handling of the variables elsewhere?
5422
	// Try our best to stay away from the cachedir match.
5423
	if (preg_match('~\sinclude\((?:(?!\(isset\(\$cachedir))~im', $file_contents))
5424
		return false;
5425
5426
	// If we find a mention of $GLOBALS, there may be a integration going on.
5427
	if (preg_match('~\$GLOBALS\[~im', $file_contents))
5428
		return false;
5429
5430
	// If these are not set, it makes us a candidate to migrate.
5431
	if (!isset($packagesdir, $tasksdir, $db_server, $db_type, $image_proxy_enabled))
5432
		return true;
5433
5434
	return false;
5435
}
5436
5437
?>