Passed
Push — release-2.1 ( 0c2197...207d2d )
by Jeremy
05:47
created

parse_sql()   F

Complexity

Conditions 53
Paths 16880

Size

Total Lines 246
Code Lines 116

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 53
eloc 116
c 0
b 0
f 0
nop 1
dl 0
loc 246
rs 0
nc 16880

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

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

327
		/** @scrutinizer ignore-call */ 
328
  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...
328
	}
329
330
	// Handle the progress of the step, if any.
331
	if (!empty($upcontext['step_progress']) && isset($upcontext['steps'][$upcontext['current_step']]))
332
	{
333
		$upcontext['step_progress'] = round($upcontext['step_progress'], 1);
334
		$upcontext['overall_percent'] += $upcontext['step_progress'] * ($upcontext['steps'][$upcontext['current_step']][3] / 100);
335
	}
336
	$upcontext['overall_percent'] = (int) $upcontext['overall_percent'];
337
338
	// We usually dump our templates out.
339
	if (!$fallThrough)
340
	{
341
		// This should not happen my dear... HELP ME DEVELOPERS!!
342
		if (!empty($command_line))
343
		{
344
			if (function_exists('debug_print_backtrace'))
345
				debug_print_backtrace();
346
347
			echo "\n" . 'Error: Unexpected call to use the ' . (isset($upcontext['sub_template']) ? $upcontext['sub_template'] : '') . ' template. Please copy and paste all the text above and visit the SMF support forum to tell the Developers that they\'ve made a boo boo; they\'ll get you up and running again.';
348
			flush();
349
			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...
350
		}
351
352
		if (!isset($_GET['xml']))
353
			template_upgrade_above();
354
		else
355
		{
356
			header('content-type: text/xml; charset=UTF-8');
357
			// Sadly we need to retain the $_GET data thanks to the old upgrade scripts.
358
			$upcontext['get_data'] = array();
359
			foreach ($_GET as $k => $v)
360
			{
361
				if (substr($k, 0, 3) != 'amp' && !in_array($k, array('xml', 'substep', 'lang', 'data', 'step', 'filecount')))
362
				{
363
					$upcontext['get_data'][$k] = $v;
364
				}
365
			}
366
			template_xml_above();
367
		}
368
369
		// Call the template.
370
		if (isset($upcontext['sub_template']))
371
		{
372
			$upcontext['upgrade_status']['curstep'] = $upcontext['current_step'];
373
			$upcontext['form_url'] = $upgradeurl . '?step=' . $upcontext['current_step'] . '&amp;substep=' . $_GET['substep'] . '&amp;data=' . base64_encode(json_encode($upcontext['upgrade_status']));
374
375
			// Custom stuff to pass back?
376
			if (!empty($upcontext['query_string']))
377
				$upcontext['form_url'] .= $upcontext['query_string'];
378
379
			// Call the appropriate subtemplate
380
			if (is_callable('template_' . $upcontext['sub_template']))
381
				call_user_func('template_' . $upcontext['sub_template']);
382
			else
383
				die('Upgrade aborted!  Invalid template: 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...
384
		}
385
386
		// Was there an error?
387
		if (!empty($upcontext['forced_error_message']))
388
			echo $upcontext['forced_error_message'];
389
390
		// Show the footer.
391
		if (!isset($_GET['xml']))
392
			template_upgrade_below();
393
		else
394
			template_xml_below();
395
	}
396
397
	// Show the upgrade time for CLI when we are completely done, if in debug mode.
398
	if (!empty($command_line) && $is_debug)
399
	{
400
		$active = time() - $upcontext['started'];
401
		$hours = floor($active / 3600);
402
		$minutes = intval(($active / 60) % 60);
403
		$seconds = intval($active % 60);
404
405
		$totalTime = '';
0 ignored issues
show
Unused Code introduced by
The assignment to $totalTime is dead and can be removed.
Loading history...
406
		if ($hours > 0)
407
			echo "\n" . '', sprintf($txt['upgrade_completed_time_hms'], $hours, $minutes, $seconds), '' . "\n";
408
		elseif ($minutes > 0)
409
			echo "\n" . '', sprintf($txt['upgrade_completed_time_ms'], $minutes, $seconds), '' . "\n";
410
		elseif ($seconds > 0)
411
			echo "\n" . '', sprintf($txt['upgrade_completed_time_s'], $seconds), '' . "\n";
412
	}
413
414
	// Bang - gone!
415
	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...
416
}
417
418
// Load the list of language files, and the current language file.
419
function load_lang_file()
420
{
421
	global $txt, $incontext, $user_info;
422
423
	$incontext['detected_languages'] = array();
424
425
	// Make sure the languages directory actually exists.
426
	if (file_exists(dirname(__FILE__) . '/Themes/default/languages'))
427
	{
428
		// Find all the "Install" language files in the directory.
429
		$dir = dir(dirname(__FILE__) . '/Themes/default/languages');
430
		while ($entry = $dir->read())
431
		{
432
			if (substr($entry, 0, 8) == 'Install.' && substr($entry, -4) == '.php')
433
				$incontext['detected_languages'][$entry] = ucfirst(substr($entry, 8, strlen($entry) - 12));
434
		}
435
		$dir->close();
436
	}
437
438
	// Didn't find any, show an error message!
439
	if (empty($incontext['detected_languages']))
440
	{
441
		// Let's not cache this message, eh?
442
		header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
443
		header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
444
		header('Cache-Control: no-cache');
445
446
		echo '<!DOCTYPE html>
447
			<html>
448
				<head>
449
					<title>SMF Upgrader: Error!</title>
450
						<style>
451
							body {
452
								font-family: sans-serif;
453
								max-width: 700px; }
454
455
								h1 {
456
									font-size: 14pt; }
457
458
								.directory {
459
									margin: 0.3em;
460
									font-family: monospace;
461
									font-weight: bold; }
462
						</style>
463
				</head>
464
				<body>
465
					<h1>A critical error has occurred.</h1>
466
						<p>This upgrader was unable to find the upgrader\'s language file or files.  They should be found under:</p>
467
						<div class="directory">', dirname($_SERVER['PHP_SELF']) != '/' ? dirname($_SERVER['PHP_SELF']) : '', '/Themes/default/languages</div>
468
						<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>
469
						<p>If that doesn\'t help, please make sure this install.php file is in the same place as the Themes folder.</p>
470
						<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>
471
				</body>
472
			</html>';
473
		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...
474
	}
475
476
	// Override the language file?
477
	if (isset($_GET['lang_file']))
478
		$_SESSION['installer_temp_lang'] = $_GET['lang_file'];
479
	elseif (isset($GLOBALS['HTTP_GET_VARS']['lang_file']))
480
		$_SESSION['installer_temp_lang'] = $GLOBALS['HTTP_GET_VARS']['lang_file'];
481
482
	// Make sure it exists, if it doesn't reset it.
483
	if (!isset($_SESSION['installer_temp_lang']) || preg_match('~[^\\w_\\-.]~', $_SESSION['installer_temp_lang']) === 1 || !file_exists(dirname(__FILE__) . '/Themes/default/languages/' . $_SESSION['installer_temp_lang']))
484
	{
485
		// Use the first one...
486
		list ($_SESSION['installer_temp_lang']) = array_keys($incontext['detected_languages']);
487
488
		// If we have english and some other language, use the other language.  We Americans hate english :P.
489
		if ($_SESSION['installer_temp_lang'] == 'Install.english.php' && count($incontext['detected_languages']) > 1)
490
			list (, $_SESSION['installer_temp_lang']) = array_keys($incontext['detected_languages']);
491
492
		// For backup we load the english at first -> second language overwrite the english one
493
		if (count($incontext['detected_languages']) > 1)
494
			require_once(dirname(__FILE__) . '/Themes/default/languages/Install.english.php');
495
	}
496
497
	// And now include the actual language file itself.
498
	require_once(dirname(__FILE__) . '/Themes/default/languages/' . $_SESSION['installer_temp_lang']);
499
500
	// Which language did we load? Assume that he likes his language.
501
	preg_match('~^Install\.(.+[^-utf8])\.php$~', $_SESSION['installer_temp_lang'], $matches);
502
	if (empty($matches[1]))
503
		$matches = [
504
			0 => 'nothing',
505
			1 => 'english',
506
		];
507
	$user_info['language'] = $matches[1];
508
}
509
510
// Used to direct the user to another location.
511
function redirectLocation($location, $addForm = true)
512
{
513
	global $upgradeurl, $upcontext, $command_line;
514
515
	// Command line users can't be redirected.
516
	if ($command_line)
517
		upgradeExit(true);
518
519
	// Are we providing the core info?
520
	if ($addForm)
521
	{
522
		$upcontext['upgrade_status']['curstep'] = $upcontext['current_step'];
523
		$location = $upgradeurl . '?step=' . $upcontext['current_step'] . '&substep=' . $_GET['substep'] . '&data=' . base64_encode(json_encode($upcontext['upgrade_status'])) . $location;
524
	}
525
526
	while (@ob_end_clean());
527
	header('location: ' . strtr($location, array('&amp;' => '&')));
528
529
	// Exit - saving status as we go.
530
	upgradeExit(true);
531
}
532
533
// Load all essential data and connect to the DB as this is pre SSI.php
534
function loadEssentialData()
535
{
536
	global $db_server, $db_user, $db_passwd, $db_name, $db_connection, $db_prefix, $db_character_set, $db_type, $db_port;
537
	global $db_mb4, $modSettings, $sourcedir, $smcFunc;
538
539
	error_reporting(E_ALL);
540
	define('SMF', 1);
541
542
	// Start the session.
543
	if (@ini_get('session.save_handler') == 'user')
544
		@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

544
		/** @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...
545
	@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

545
	/** @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...
546
547
	if (empty($smcFunc))
548
		$smcFunc = array();
549
550
	// We need this for authentication and some upgrade code
551
	require_once($sourcedir . '/Subs-Auth.php');
552
	require_once($sourcedir . '/Class-Package.php');
553
554
	$smcFunc['strtolower'] = 'smf_strtolower';
555
556
	// Initialize everything...
557
	initialize_inputs();
558
559
	// Get the database going!
560
	if (empty($db_type) || $db_type == 'mysqli')
561
	{
562
		$db_type = 'mysql';
563
		// If overriding $db_type, need to set its settings.php entry too
564
		$changes = array();
565
		$changes['db_type'] = '\'mysql\'';
566
		require_once($sourcedir . '/Subs-Admin.php');
567
		updateSettingsFile($changes);
568
	}
569
570
	if (file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php'))
571
	{
572
		require_once($sourcedir . '/Subs-Db-' . $db_type . '.php');
573
574
		// Make the connection...
575
		if (empty($db_connection))
576
		{
577
			$options = array('non_fatal' => true);
578
			// Add in the port if needed
579
			if (!empty($db_port))
580
				$options['port'] = $db_port;
581
			
582
			if (!empty($db_mb4))
583
				$options['db_mb4'] = $db_mb4;
584
			
585
			$db_connection = smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, $db_prefix, $options);
586
		}
587
		else
588
			// If we've returned here, ping/reconnect to be safe
589
			$smcFunc['db_ping']($db_connection);
590
591
		// Oh dear god!!
592
		if ($db_connection === null)
593
			die('Unable to connect to database - please check username and password are correct in Settings.php');
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...
594
595
		if ($db_type == 'mysql' && isset($db_character_set) && preg_match('~^\w+$~', $db_character_set) === 1)
596
			$smcFunc['db_query']('', '
597
			SET NAMES {string:db_character_set}',
598
			array(
599
				'db_error_skip' => true,
600
				'db_character_set' => $db_character_set,
601
			)
602
		);
603
604
		// Load the modSettings data...
605
		$request = $smcFunc['db_query']('', '
606
			SELECT variable, value
607
			FROM {db_prefix}settings',
608
			array(
609
				'db_error_skip' => true,
610
			)
611
		);
612
		$modSettings = array();
613
		while ($row = $smcFunc['db_fetch_assoc']($request))
614
			$modSettings[$row['variable']] = $row['value'];
615
		$smcFunc['db_free_result']($request);
616
	}
617
	else
618
	{
619
		return throw_error('Cannot find ' . $sourcedir . '/Subs-Db-' . $db_type . '.php' . '. Please check you have uploaded all source files and have the correct paths set.');
620
	}
621
622
	require_once($sourcedir . '/Subs.php');
623
624
	// If they don't have the file, they're going to get a warning anyway so we won't need to clean request vars.
625
	if (file_exists($sourcedir . '/QueryString.php') && php_version_check())
626
	{
627
		require_once($sourcedir . '/QueryString.php');
628
		cleanRequest();
629
	}
630
631
	if (!isset($_GET['substep']))
632
		$_GET['substep'] = 0;
633
}
634
635
function initialize_inputs()
636
{
637
	global $start_time, $db_type;
638
639
	$start_time = time();
640
641
	umask(0);
642
643
	ob_start();
644
645
	// Better to upgrade cleanly and fall apart than to screw everything up if things take too long.
646
	ignore_user_abort(true);
647
648
	// This is really quite simple; if ?delete is on the URL, delete the upgrader...
649
	if (isset($_GET['delete']))
650
	{
651
		@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

651
		/** @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...
652
653
		// And the extra little files ;).
654
		@unlink(dirname(__FILE__) . '/upgrade_1-0.sql');
655
		@unlink(dirname(__FILE__) . '/upgrade_1-1.sql');
656
		@unlink(dirname(__FILE__) . '/upgrade_2-0_' . $db_type . '.sql');
657
		@unlink(dirname(__FILE__) . '/upgrade_2-1_' . $db_type . '.sql');
658
		@unlink(dirname(__FILE__) . '/upgrade-helper.php');
659
660
		$dh = opendir(dirname(__FILE__));
661
		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

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

666
		closedir(/** @scrutinizer ignore-type */ $dh);
Loading history...
667
668
		// Legacy files while we're at it. NOTE: We only touch files we KNOW shouldn't be there.
669
		// 1.1 Sources files not in 2.0+
670
		@unlink(dirname(__FILE__) . '/Sources/ModSettings.php');
671
		// 1.1 Templates that don't exist any more (e.g. renamed)
672
		@unlink(dirname(__FILE__) . '/Themes/default/Combat.template.php');
673
		@unlink(dirname(__FILE__) . '/Themes/default/Modlog.template.php');
674
		// 1.1 JS files were stored in the main theme folder, but in 2.0+ are in the scripts/ folder
675
		@unlink(dirname(__FILE__) . '/Themes/default/fader.js');
676
		@unlink(dirname(__FILE__) . '/Themes/default/script.js');
677
		@unlink(dirname(__FILE__) . '/Themes/default/spellcheck.js');
678
		@unlink(dirname(__FILE__) . '/Themes/default/xml_board.js');
679
		@unlink(dirname(__FILE__) . '/Themes/default/xml_topic.js');
680
681
		// 2.0 Sources files not in 2.1+
682
		@unlink(dirname(__FILE__) . '/Sources/DumpDatabase.php');
683
		@unlink(dirname(__FILE__) . '/Sources/LockTopic.php');
684
685
		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');
686
		exit;
687
	}
688
689
	// Something is causing this to happen, and it's annoying.  Stop it.
690
	$temp = 'upgrade_php?step';
691
	while (strlen($temp) > 4)
692
	{
693
		if (isset($_GET[$temp]))
694
			unset($_GET[$temp]);
695
		$temp = substr($temp, 1);
696
	}
697
698
	// Force a step, defaulting to 0.
699
	$_GET['step'] = (int) @$_GET['step'];
700
	$_GET['substep'] = (int) @$_GET['substep'];
701
}
702
703
// Step 0 - Let's welcome them in and ask them to login!
704
function WelcomeLogin()
705
{
706
	global $boarddir, $sourcedir, $modSettings, $cachedir, $upgradeurl, $upcontext;
707
	global $smcFunc, $db_type, $databases, $boardurl;
708
709
	// We global $txt here so that the language files can add to them. This variable is NOT unused.
710
	global $txt;
711
712
	$upcontext['sub_template'] = 'welcome_message';
713
714
	// Check for some key files - one template, one language, and a new and an old source file.
715
	$check = @file_exists($modSettings['theme_dir'] . '/index.template.php')
716
		&& @file_exists($sourcedir . '/QueryString.php')
717
		&& @file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php')
718
		&& @file_exists(dirname(__FILE__) . '/upgrade_2-1_' . $db_type . '.sql');
719
720
	// Need legacy scripts?
721
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 2.1)
722
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_2-0_' . $db_type . '.sql');
723
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 2.0)
724
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_1-1.sql');
725
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 1.1)
726
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_1-0.sql');
727
728
	// We don't need "-utf8" files anymore...
729
	$upcontext['language'] = str_ireplace('-utf8', '', $upcontext['language']);
730
731
	// This needs to exist!
732
	if (!file_exists($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php'))
733
		return throw_error('The upgrader could not find the &quot;Install&quot; language file for the forum default language, ' . $upcontext['language'] . '.<br><br>Please make certain you uploaded all the files included in the package, even the theme and language files for the default theme.<br>&nbsp;&nbsp;&nbsp;[<a href="' . $upgradeurl . '?lang=english">Try English</a>]');
734
	else
735
		require_once($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php');
736
737
	if (!$check)
738
		// 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.
739
		return throw_error('The upgrader was unable to find some crucial files.<br><br>Please make sure you uploaded all of the files included in the package, including the Themes, Sources, and other directories.');
740
741
	// Do they meet the install requirements?
742
	if (!php_version_check())
743
		return throw_error('Warning!  You do not appear to have a version of PHP installed on your webserver that meets SMF\'s minimum installations requirements.<br><br>Please ask your host to upgrade.');
744
745
	if (!db_version_check())
746
		return throw_error('Your ' . $databases[$db_type]['name'] . ' version does not meet the minimum requirements of SMF.<br><br>Please ask your host to upgrade.');
747
748
	// Do some checks to make sure they have proper privileges
749
	db_extend('packages');
750
751
	// CREATE
752
	$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');
753
754
	// ALTER
755
	$alter = $smcFunc['db_add_column']('{db_prefix}priv_check', array('name' => 'txt', 'type' => 'varchar', 'size' => 4, 'null' => false, 'default' => ''));
756
757
	// DROP
758
	$drop = $smcFunc['db_drop_table']('{db_prefix}priv_check');
759
760
	// Sorry... we need CREATE, ALTER and DROP
761
	if (!$create || !$alter || !$drop)
762
		return throw_error('The ' . $databases[$db_type]['name'] . ' user you have set in Settings.php does not have proper privileges.<br><br>Please ask your host to give this user the ALTER, CREATE, and DROP privileges.');
763
764
	// Do a quick version spot check.
765
	$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

765
	$temp = substr(@implode('', /** @scrutinizer ignore-type */ @file($boarddir . '/index.php')), 0, 4096);
Loading history...
766
	preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $temp, $match);
767
	if (empty($match[1]) || (trim($match[1]) != SMF_VERSION))
768
		return throw_error('The upgrader found some old or outdated files.<br><br>Please make certain you uploaded the new versions of all the files included in the package.');
769
770
	// What absolutely needs to be writable?
771
	$writable_files = array(
772
		$boarddir . '/Settings.php',
773
		$boarddir . '/Settings_bak.php',
774
	);
775
776
	// Only check for minified writable files if we have it enabled or not set.
777
	if (!empty($modSettings['minimize_files']) || !isset($modSettings['minimize_files']))
778
		$writable_files += array(
779
			$modSettings['theme_dir'] . '/css/minified.css',
780
			$modSettings['theme_dir'] . '/scripts/minified.js',
781
			$modSettings['theme_dir'] . '/scripts/minified_deferred.js',
782
		);
783
784
	// Do we need to add this setting?
785
	$need_settings_update = empty($modSettings['custom_avatar_dir']);
786
787
	$custom_av_dir = !empty($modSettings['custom_avatar_dir']) ? $modSettings['custom_avatar_dir'] : $GLOBALS['boarddir'] . '/custom_avatar';
788
	$custom_av_url = !empty($modSettings['custom_avatar_url']) ? $modSettings['custom_avatar_url'] : $boardurl . '/custom_avatar';
789
790
	// This little fellow has to cooperate...
791
	quickFileWritable($custom_av_dir);
792
793
	// Are we good now?
794
	if (!is_writable($custom_av_dir))
795
		return throw_error(sprintf('The directory: %1$s has to be writable to continue the upgrade. Please make sure permissions are correctly set to allow this.', $custom_av_dir));
796
	elseif ($need_settings_update)
797
	{
798
		if (!function_exists('cache_put_data'))
799
			require_once($sourcedir . '/Load.php');
800
801
		updateSettings(array('custom_avatar_dir' => $custom_av_dir));
802
		updateSettings(array('custom_avatar_url' => $custom_av_url));
803
	}
804
805
	require_once($sourcedir . '/Security.php');
806
807
	// Check the cache directory.
808
	$cachedir_temp = empty($cachedir) ? $boarddir . '/cache' : $cachedir;
809
	if (!file_exists($cachedir_temp))
810
		@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

810
		/** @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...
811
812
	if (!file_exists($cachedir_temp))
813
		return throw_error('The cache directory could not be found.<br><br>Please make sure you have a directory called &quot;cache&quot; in your forum directory before continuing.');
814
815
	if (!file_exists($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php') && !isset($modSettings['smfVersion']) && !isset($_GET['lang']))
816
		return throw_error('The upgrader was unable to find language files for the language specified in Settings.php.<br>SMF will not work without the primary language files installed.<br><br>Please either install them, or <a href="' . $upgradeurl . '?step=0;lang=english">use english instead</a>.');
817
	elseif (!isset($_GET['skiplang']))
818
	{
819
		$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php')), 0, 4096);
820
		preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
821
822
		if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
823
			return throw_error('The upgrader found some old or outdated language files, for the forum default language, ' . $upcontext['language'] . '.<br><br>Please make certain you uploaded the new versions of all the files included in the package, even the theme and language files for the default theme.<br>&nbsp;&nbsp;&nbsp;[<a href="' . $upgradeurl . '?skiplang">SKIP</a>] [<a href="' . $upgradeurl . '?lang=english">Try English</a>]');
824
	}
825
826
	if (!makeFilesWritable($writable_files))
827
		return false;
828
829
	// Check agreement.txt. (it may not exist, in which case $boarddir must be writable.)
830
	if (isset($modSettings['agreement']) && (!is_writable($boarddir) || file_exists($boarddir . '/agreement.txt')) && !is_writable($boarddir . '/agreement.txt'))
831
		return throw_error('The upgrader was unable to obtain write access to agreement.txt.<br><br>If you are using a linux or unix based server, please ensure that the file is chmod\'d to 777, or if it does not exist that the directory this upgrader is in is 777.<br>If your server is running Windows, please ensure that the internet guest account has the proper permissions on it or its folder.');
832
833
	// Upgrade the agreement.
834
	elseif (isset($modSettings['agreement']))
835
	{
836
		$fp = fopen($boarddir . '/agreement.txt', 'w');
837
		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

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

838
		fclose(/** @scrutinizer ignore-type */ $fp);
Loading history...
839
	}
840
841
	// We're going to check that their board dir setting is right in case they've been moving stuff around.
842
	if (strtr($boarddir, array('/' => '', '\\' => '')) != strtr(dirname(__FILE__), array('/' => '', '\\' => '')))
843
		$upcontext['warning'] = '
844
			'. sprintf($txt['upgrade_boarddir_settings'], $boarddir, dirname(__FILE__)) .'<br>
845
			<ul>
846
				<li>'. $txt['upgrade_boarddir'] .'  ' . $boarddir . '</li>
847
				<li>'. $txt['upgrade_sourcedir'] .'  ' . $boarddir . '</li>
848
				<li>'. $txt['upgrade_cachedir'] .'  ' . $cachedir_temp . '</li>
849
			</ul>
850
			'. $txt['upgrade_incorrect_settings'] .'';
851
852
	// Confirm mbstring is loaded...
853
	if (!extension_loaded('mbstring'))
854
		return throw_error($txt['install_no_mbstring']);
855
856
	// Check for https stream support.
857
	$supported_streams = stream_get_wrappers();
858
	if (!in_array('https', $supported_streams))
859
		$upcontext['custom_warning'] = $txt['install_no_https'];
860
861
	// Either we're logged in or we're going to present the login.
862
	if (checkLogin())
863
		return true;
864
865
	$upcontext += createToken('login');
866
867
	return false;
868
}
869
870
// Step 0.5: Does the login work?
871
function checkLogin()
872
{
873
	global $modSettings, $upcontext, $disable_security;
874
	global $smcFunc, $db_type, $support_js;
875
876
	// Don't bother if the security is disabled.
877
	if ($disable_security)
878
		return true;
879
880
	// Are we trying to login?
881
	if (isset($_POST['contbutt']) && (!empty($_POST['user'])))
882
	{
883
		// If we've disabled security pick a suitable name!
884
		if (empty($_POST['user']))
885
			$_POST['user'] = 'Administrator';
886
887
		// Before 2.0 these column names were different!
888
		$oldDB = false;
889
		if (empty($db_type) || $db_type == 'mysql')
890
		{
891
			$request = $smcFunc['db_query']('', '
892
				SHOW COLUMNS
893
				FROM {db_prefix}members
894
				LIKE {string:member_name}',
895
				array(
896
					'member_name' => 'memberName',
897
					'db_error_skip' => true,
898
				)
899
			);
900
			if ($smcFunc['db_num_rows']($request) != 0)
901
				$oldDB = true;
902
			$smcFunc['db_free_result']($request);
903
		}
904
905
		// Get what we believe to be their details.
906
		if (!$disable_security)
907
		{
908
			if ($oldDB)
909
				$request = $smcFunc['db_query']('', '
910
					SELECT id_member, memberName AS member_name, passwd, id_group,
911
					additionalGroups AS additional_groups, lngfile
912
					FROM {db_prefix}members
913
					WHERE memberName = {string:member_name}',
914
					array(
915
						'member_name' => $_POST['user'],
916
						'db_error_skip' => true,
917
					)
918
				);
919
			else
920
				$request = $smcFunc['db_query']('', '
921
					SELECT id_member, member_name, passwd, id_group, additional_groups, lngfile
922
					FROM {db_prefix}members
923
					WHERE member_name = {string:member_name}',
924
					array(
925
						'member_name' => $_POST['user'],
926
						'db_error_skip' => true,
927
					)
928
				);
929
			if ($smcFunc['db_num_rows']($request) != 0)
930
			{
931
				list ($id_member, $name, $password, $id_group, $addGroups, $user_language) = $smcFunc['db_fetch_row']($request);
932
933
				$groups = explode(',', $addGroups);
934
				$groups[] = $id_group;
935
936
				foreach ($groups as $k => $v)
937
					$groups[$k] = (int) $v;
938
939
				$sha_passwd = sha1(strtolower($name) . un_htmlspecialchars($_REQUEST['passwrd']));
940
941
				// We don't use "-utf8" anymore...
942
				$user_language = str_ireplace('-utf8', '', $user_language);
943
			}
944
			else
945
				$upcontext['username_incorrect'] = true;
946
947
			$smcFunc['db_free_result']($request);
948
		}
949
		$upcontext['username'] = $_POST['user'];
950
951
		// Track whether javascript works!
952
		if (!empty($_POST['js_works']))
953
		{
954
			$upcontext['upgrade_status']['js'] = 1;
955
			$support_js = 1;
956
		}
957
		else
958
			$support_js = 0;
959
960
		// Note down the version we are coming from.
961
		if (!empty($modSettings['smfVersion']) && empty($upcontext['user']['version']))
962
			$upcontext['user']['version'] = $modSettings['smfVersion'];
963
964
		// Didn't get anywhere?
965
		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']))
966
		{
967
			// MD5?
968
			$md5pass = md5_hmac($_REQUEST['passwrd'], strtolower($_POST['user']));
969
			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...
970
			{
971
				$upcontext['password_failed'] = true;
972
				// Disable the hashing this time.
973
				$upcontext['disable_login_hashing'] = true;
974
			}
975
		}
976
977
		if ((empty($upcontext['password_failed']) && !empty($name)) || $disable_security)
978
		{
979
			// Set the password.
980
			if (!$disable_security)
981
			{
982
				// Do we actually have permission?
983
				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...
984
				{
985
					$request = $smcFunc['db_query']('', '
986
						SELECT permission
987
						FROM {db_prefix}permissions
988
						WHERE id_group IN ({array_int:groups})
989
							AND permission = {string:admin_forum}',
990
						array(
991
							'groups' => $groups,
992
							'admin_forum' => 'admin_forum',
993
							'db_error_skip' => true,
994
						)
995
					);
996
					if ($smcFunc['db_num_rows']($request) == 0)
997
						return throw_error('You need to be an admin to perform an upgrade!');
998
					$smcFunc['db_free_result']($request);
999
				}
1000
1001
				$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...
1002
				$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...
1003
			}
1004
			else
1005
			{
1006
				$upcontext['user']['id'] = 1;
1007
				$upcontext['user']['name'] = 'Administrator';
1008
			}
1009
			$upcontext['user']['pass'] = mt_rand(0, 60000);
1010
			// This basically is used to match the GET variables to Settings.php.
1011
			$upcontext['upgrade_status']['pass'] = $upcontext['user']['pass'];
1012
1013
			// Set the language to that of the user?
1014
			if (isset($user_language) && $user_language != $upcontext['language'] && file_exists($modSettings['theme_dir'] . '/languages/index.' . basename($user_language, '.lng') . '.php'))
1015
			{
1016
				$user_language = basename($user_language, '.lng');
1017
				$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

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

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

2414
	/** @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...
2415
	if (function_exists('apache_reset_timeout'))
2416
		@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

2416
		/** @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...
2417
2418
	if (time() - $start_time <= $timeLimitThreshold)
2419
		return;
2420
2421
	// Do we have some custom step progress stuff?
2422
	if (!empty($step_progress))
2423
	{
2424
		$upcontext['substep_progress'] = 0;
2425
		$upcontext['substep_progress_name'] = $step_progress['name'];
2426
		if ($step_progress['current'] > $step_progress['total'])
2427
			$upcontext['substep_progress'] = 99.9;
2428
		else
2429
			$upcontext['substep_progress'] = ($step_progress['current'] / $step_progress['total']) * 100;
2430
2431
		// Make it nicely rounded.
2432
		$upcontext['substep_progress'] = round($upcontext['substep_progress'], 1);
2433
	}
2434
2435
	// If this is XML we just exit right away!
2436
	if (isset($_GET['xml']))
2437
		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...
2438
2439
	// We're going to pause after this!
2440
	$upcontext['pause'] = true;
2441
2442
	$upcontext['query_string'] = '';
2443
	foreach ($_GET as $k => $v)
2444
	{
2445
		if ($k != 'data' && $k != 'substep' && $k != 'step')
2446
			$upcontext['query_string'] .= ';' . $k . '=' . $v;
2447
	}
2448
2449
	// Custom warning?
2450
	if (!empty($custom_warning))
2451
		$upcontext['custom_warning'] = $custom_warning;
2452
2453
	upgradeExit();
2454
}
2455
2456
function cmdStep0()
2457
{
2458
	global $boarddir, $sourcedir, $modSettings, $start_time, $cachedir, $databases, $db_type, $smcFunc, $upcontext;
2459
	global $is_debug;
2460
	$start_time = time();
2461
2462
	ob_end_clean();
2463
	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

2463
	ob_implicit_flush(/** @scrutinizer ignore-type */ true);
Loading history...
2464
	@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

2464
	/** @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...
2465
2466
	if (!isset($_SERVER['argv']))
2467
		$_SERVER['argv'] = array();
2468
	$_GET['maint'] = 1;
2469
2470
	foreach ($_SERVER['argv'] as $i => $arg)
2471
	{
2472
		if (preg_match('~^--language=(.+)$~', $arg, $match) != 0)
2473
			$_GET['lang'] = $match[1];
2474
		elseif (preg_match('~^--path=(.+)$~', $arg) != 0)
2475
			continue;
2476
		elseif ($arg == '--no-maintenance')
2477
			$_GET['maint'] = 0;
2478
		elseif ($arg == '--debug')
2479
			$is_debug = true;
2480
		elseif ($arg == '--backup')
2481
			$_POST['backup'] = 1;
2482
		elseif ($arg == '--template' && (file_exists($boarddir . '/template.php') || file_exists($boarddir . '/template.html') && !file_exists($modSettings['theme_dir'] . '/converted')))
2483
			$_GET['conv'] = 1;
2484
		elseif ($i != 0)
2485
		{
2486
			echo 'SMF Command-line Upgrader
2487
Usage: /path/to/php -f ' . basename(__FILE__) . ' -- [OPTION]...
2488
2489
    --language=LANG         Reset the forum\'s language to LANG.
2490
    --no-maintenance        Don\'t put the forum into maintenance mode.
2491
    --debug                 Output debugging information.
2492
    --backup                Create backups of tables with "backup_" prefix.';
2493
			echo "\n";
2494
			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...
2495
		}
2496
	}
2497
2498
	if (!php_version_check())
2499
		print_error('Error: PHP ' . PHP_VERSION . ' does not match version requirements.', true);
2500
	if (!db_version_check())
2501
		print_error('Error: ' . $databases[$db_type]['name'] . ' ' . $databases[$db_type]['version'] . ' does not match minimum requirements.', true);
2502
2503
	// Do some checks to make sure they have proper privileges
2504
	db_extend('packages');
2505
2506
	// CREATE
2507
	$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');
2508
2509
	// ALTER
2510
	$alter = $smcFunc['db_add_column']('{db_prefix}priv_check', array('name' => 'txt', 'type' => 'varchar', 'size' => 4, 'null' => false, 'default' => ''));
2511
2512
	// DROP
2513
	$drop = $smcFunc['db_drop_table']('{db_prefix}priv_check');
2514
2515
	// Sorry... we need CREATE, ALTER and DROP
2516
	if (!$create || !$alter || !$drop)
2517
		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);
2518
2519
	$check = @file_exists($modSettings['theme_dir'] . '/index.template.php')
2520
		&& @file_exists($sourcedir . '/QueryString.php')
2521
		&& @file_exists($sourcedir . '/ManageBoards.php');
2522
	if (!$check && !isset($modSettings['smfVersion']))
2523
		print_error('Error: Some files are missing or out-of-date.', true);
2524
2525
	// Do a quick version spot check.
2526
	$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

2526
	$temp = substr(@implode('', /** @scrutinizer ignore-type */ @file($boarddir . '/index.php')), 0, 4096);
Loading history...
2527
	preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $temp, $match);
2528
	if (empty($match[1]) || (trim($match[1]) != SMF_VERSION))
2529
		print_error('Error: Some files have not yet been updated properly.');
2530
2531
	// Make sure Settings.php is writable.
2532
	quickFileWritable($boarddir . '/Settings.php');
2533
	if (!is_writable($boarddir . '/Settings.php'))
2534
		print_error('Error: Unable to obtain write access to "Settings.php".', true);
2535
2536
	// Make sure Settings_bak.php is writable.
2537
	quickFileWritable($boarddir . '/Settings_bak.php');
2538
	if (!is_writable($boarddir . '/Settings_bak.php'))
2539
		print_error('Error: Unable to obtain write access to "Settings_bak.php".');
2540
2541
	if (isset($modSettings['agreement']) && (!is_writable($boarddir) || file_exists($boarddir . '/agreement.txt')) && !is_writable($boarddir . '/agreement.txt'))
2542
		print_error('Error: Unable to obtain write access to "agreement.txt".');
2543
	elseif (isset($modSettings['agreement']))
2544
	{
2545
		$fp = fopen($boarddir . '/agreement.txt', 'w');
2546
		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

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

2547
		fclose(/** @scrutinizer ignore-type */ $fp);
Loading history...
2548
	}
2549
2550
	// Make sure Themes is writable.
2551
	quickFileWritable($modSettings['theme_dir']);
2552
2553
	if (!is_writable($modSettings['theme_dir']) && !isset($modSettings['smfVersion']))
2554
		print_error('Error: Unable to obtain write access to "Themes".');
2555
2556
	// Make sure cache directory exists and is writable!
2557
	$cachedir_temp = empty($cachedir) ? $boarddir . '/cache' : $cachedir;
2558
	if (!file_exists($cachedir_temp))
2559
		@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

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

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

5267
		/** @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...
5268
5269
		// Oops. Low disk space, perhaps. Don't mess with Settings.php then.
5270
		// No means no. :P
5271
		if ($written_bytes !== 4)
5272
			return throw_error('The upgrader could not write a test file, perhaps not enough storage?  Unable to migrate your Settings file to a new version.');
5273
	}
5274
5275
	// Protect me from what I want! :P
5276
	clearstatcache();
5277
	if (filemtime($boarddir . '/Settings.php') === $last_settings_change)
5278
	{
5279
		// save the old before we do anything
5280
		$settings_backup_fail = !@is_writable($boarddir . '/Settings_org.php') || !@copy($boarddir . '/Settings.php', $boarddir . '/Settings_org.php');
5281
		$settings_backup_fail = !$settings_backup_fail ? (!file_exists($boarddir . '/Settings_org.php') || filesize($boarddir . '/Settings_org.php') === 0) : $settings_backup_fail;
5282
5283
		// write out the new
5284
		$write_settings = implode('', $settingsArray);
5285
		$written_bytes = file_put_contents($boarddir . '/Settings.php', $write_settings, LOCK_EX);
5286
5287
		// survey says ...
5288
		if ($written_bytes !== strlen($write_settings) && !$settings_backup_fail)
5289
		{
5290
			if (file_exists($boarddir . '/Settings_bak.php'))
5291
				@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

5291
				/** @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...
5292
5293
			return throw_error('The upgrader detected a bad Settings file and reverted the changes.  Unable to migrate your Settings file to a new version.');
5294
		}
5295
	}
5296
5297
	// Even though on normal installations the filemtime should prevent this being used by the installer incorrectly
5298
	// it seems that there are times it might not. So let's MAKE it dump the cache.
5299
	if (function_exists('opcache_invalidate'))
5300
		opcache_invalidate($boarddir . '/Settings.php', true);
5301
}
5302
5303
/**
5304
 * Determine if we should auto select the migrate Settings file.  This is determined by a variety of missing settings.
5305
 * Prior to checking these settings, we look for advanced setups such as integrations or if variables have been moved
5306
 * to another file.  If these are detected, we abort.
5307
 *
5308
 * @param array $config_vars An array of one or more variables to update
5309
 *
5310
 * @return void We either succesfully update the Settings file, or throw a error here.
5311
 */
5312
5313
function detectSettingsFileMigrationNeeded()
5314
{
5315
	global $boarddir, $packagesdir, $tasksdir, $db_server, $db_type, $image_proxy_enabled, $db_show_debug;
5316
5317
	// We should not migrate if db_show_debug is in there, some dev stuff going on here.
5318
	if (isset($db_show_debug))
5319
		return false;
5320
5321
	$file_contents = file_get_contents($boarddir . '/Settings.php');
5322
5323
	// Is there a include statement somewhere in there? Some advanced handling of the variables elsewhere?
5324
	// Try our best to stay away from the cachedir match.
5325
	if (preg_match('~\sinclude\((?:(?!\(isset\(\$cachedir))~im', $file_contents))
5326
		return false;
5327
5328
	// If we find a mention of $GLOBALS, there may be a integration going on.
5329
	if (preg_match('~\$GLOBALS\[~im', $file_contents))
5330
		return false;
5331
5332
	// If these are not set, it makes us a canidate to migrate.
5333
	if (!isset($packagesdir, $tasksdir, $db_server, $db_type, $image_proxy_enabled))
5334
		return true;
5335
5336
	return false;
5337
}
5338
5339
?>